Esempio n. 1
0
class QualityRulesGeneralResultsPanelWidget(QgsPanelWidget, WIDGET_UI):
    def __init__(self, controller, mode, parent=None):
        QgsPanelWidget.__init__(self, None)
        self.setupUi(self)
        self.__controller = controller
        self.__mode = mode
        self.parent = parent
        self.logger = Logger()

        self.__selected_item = None  # qr_key
        self.__icon_names = [
            'schema.png', 'points.png', 'lines.png', 'polygons.png',
            'relationships.svg'
        ]
        self.__block_control_updates = False

        self.txt_search.addAction(
            QIcon(":/Asistente-LADM-COL/resources/images/search.png"),
            QLineEdit.LeadingPosition)

        self.setDockMode(True)
        self.setPanelTitle(
            QCoreApplication.translate("QualityRulesGeneralResultsPanelWidget",
                                       "Validation results"))
        self.trw_qrs.header().setStretchLastSection(False)
        self.trw_qrs.setColumnWidth(RESULT_COLUMN, 50)
        self.trw_qrs.header().setSectionResizeMode(0, QHeaderView.Stretch)
        self.pbr_total_progress.setValue(0)

        self.txt_search.textChanged.connect(self.__search_text_changed)
        self.btn_open_report.clicked.connect(self.__controller.open_report)
        self.btn_view_error_results.clicked.connect(
            self.__view_error_results_clicked)
        self.btn_help.clicked.connect(self.__show_help)
        self.trw_qrs.itemSelectionChanged.connect(self.__selection_changed)
        self.panelAccepted.connect(self.__reset)

        # To keep track of the connections to 'partial' slots, because we need to delete them when the panel is closed
        self.__partial_connections = list()

        # Load available rules for current role and current db models
        self.__load_available_rules()

        self.__enable_panel_controls(
            False
        )  # Panel controls should be enabled after all rules have validation results

    def __reset(self, panel=None):
        # Reset connections to "partial" slots
        for pair in self.__partial_connections:
            pair[0].disconnect(pair[1])

        self.__partial_connections = list()

        self.logger.clear_message_bar()

    def __load_available_rules(self):
        self.__controller.load_general_results_tree_data()
        self.__update_available_rules()

    def __update_available_rules(self):
        self.trw_qrs.setUpdatesEnabled(False)  # Don't render until we're ready

        # Grab some context data
        top_level_items_expanded_info = dict()
        for i in range(self.trw_qrs.topLevelItemCount()):
            top_level_items_expanded_info[self.trw_qrs.topLevelItem(i).text(
                QR_COLUMN)] = self.trw_qrs.topLevelItem(i).isExpanded()

        # Save selection before clearing tree to restate it later (if needed)
        self.__update_selected_item()

        # Iterate qr types adding children
        self.trw_qrs.blockSignals(
            True)  # We don't want to get itemSelectionChanged here
        self.trw_qrs.clear()
        self.trw_qrs.blockSignals(False)

        bold_font = QFont()
        bold_font.setBold(True)

        sorted_types = sorted(
            self.__controller.get_general_results_tree_data().keys())
        for type_enum in sorted_types:
            children = []
            type_item = QTreeWidgetItem(
                [self.__controller.get_tr_string(type_enum)])

            # Filter by search text
            list_qrs = self.__filter_by_search_text(type_enum,
                                                    self.txt_search.text())

            for qr in list_qrs:
                qr_item = QTreeWidgetItem([qr.name(), ''])
                qr_item.setData(QR_COLUMN, Qt.UserRole, qr.id())
                qr_item.setData(QR_COLUMN, Qt.ToolTipRole,
                                "{}\n{}".format(qr.name(), qr.id()))

                # Let's listen some QR's relevant signals to update our view when needed
                self.__partial_connections.append([
                    qr,
                    qr.progress_changed.connect(
                        partial(self.__set_qr_progress, qr.id()))
                ])
                self.__partial_connections.append([
                    qr,
                    qr.validation_finished.connect(
                        partial(self.__set_qr_validation_result, qr))
                ])

                children.append(qr_item)

            if children:
                icon_name = self.__icon_names[type_enum.value]
                icon = QIcon(":/Asistente-LADM-COL/resources/images/{}".format(
                    icon_name))
                type_item.setData(0, Qt.DecorationRole, icon)
                type_item.setData(0, Qt.FontRole, bold_font)
                type_item.addChildren(children)
                self.trw_qrs.addTopLevelItem(type_item)

                # After we've set the children, we can set custom item widgets
                self.__set_children_custom_widget(type_enum, type_item,
                                                  list_qrs)
            else:
                type_item = None

        # Set selection
        self.trw_qrs.blockSignals(
            True)  # We don't want to get itemSelectionChanged here
        item = self.__get_item_by_qr_key(self.__selected_item)
        if item:
            item.setSelected(True)
        self.trw_qrs.blockSignals(False)

        # Make type items non selectable
        # Set expand taking previous states into account
        for i in range(self.trw_qrs.topLevelItemCount()):
            self.trw_qrs.topLevelItem(i).setFlags(
                Qt.ItemIsEnabled)  # Not selectable
            self.trw_qrs.topLevelItem(i).setExpanded(
                top_level_items_expanded_info.get(
                    self.trw_qrs.topLevelItem(i).text(QR_COLUMN), True))

        self.trw_qrs.setUpdatesEnabled(True)  # Now render!

    def __set_children_custom_widget(self, type_enum, type_item, filtered_qrs):
        dict_filtered_qrs = {qr.id(): qr for qr in filtered_qrs}
        for i in range(type_item.childCount()):
            item = type_item.child(i)
            qr_key = item.data(QR_COLUMN, Qt.UserRole)
            qr_result = self.__controller.get_general_results_tree_data(
            )[type_enum][dict_filtered_qrs[qr_key]]
            self.__set_style_for_validation_result(item, qr_result)

    def __filter_by_search_text(self, type, search_text):
        res = list(self.__controller.get_general_results_tree_data()
                   [type].keys())  # qr_objs

        search_text = search_text.lower().strip()
        if len(search_text) > 1:
            to_delete = list()
            for qr_obj in res:
                # First check in QR id
                found = search_text in qr_obj.id().lower()

                if not found:
                    # Now search in QR tags
                    for tag in qr_obj.tags():
                        if search_text in tag:
                            found = True
                            continue

                    if not found:
                        # Finally, if we search for 'errores', we should find failing rules
                        if search_text in 'errores':
                            qr_res = self.__controller.get_general_results_tree_data(
                            )[type][qr_obj]
                            if qr_res and qr_res.level == EnumQualityRuleResult.ERRORS:
                                found = True

                        if not found:
                            to_delete.append(qr_obj)

            for qr_obj in to_delete:
                res.remove(qr_obj)

        return res

    def __get_item_by_qr_key(self, qr_key):
        iterator = QTreeWidgetItemIterator(self.trw_qrs,
                                           QTreeWidgetItemIterator.Selectable)
        while iterator.value():
            item = iterator.value()
            if item.data(QR_COLUMN, Qt.UserRole) == qr_key:
                return item

            iterator += 1

        return None

    def __update_selected_item(self):
        self.__selected_item = None
        iterator = QTreeWidgetItemIterator(self.trw_qrs,
                                           QTreeWidgetItemIterator.Selectable)
        while iterator.value():
            item = iterator.value()
            qr_key = item.data(QR_COLUMN, Qt.UserRole)

            if item.isSelected():
                self.__selected_item = qr_key
                break

            iterator += 1

    def __search_text_changed(self, text):
        self.__update_available_rules()

    def __view_error_results_clicked(self):
        self.__save_settings()

        if self.trw_qrs.selectedItems():
            qr_key = self.__get_selected_qr_key()
            res = self.__controller.set_selected_qr(qr_key)
            if res:
                self.show_error_results_panel()
            else:
                self.logger.warning_msg(
                    __name__,
                    QCoreApplication.translate(
                        "QualityRulesGeneralResultsPanelWidget",
                        "The quality rule '{}' couldn't be found! We cannot continue exploring errors."
                    ).format(qr_key))

    def __get_selected_qr_key(self):
        # Only call it if you know that bool(self.trw_qrs.selectedItems()) is True.
        # Since only 1 selected item is allowed, we can do this:
        return self.trw_qrs.selectedItems()[0].data(QR_COLUMN, Qt.UserRole)

    def __save_settings(self):
        pass

    def __restore_settings(self):
        pass

    def __selection_changed(self):
        # Custom slot to refresh labels and button state
        self.__update_view_error_button_state()

    def __enable_panel_controls(self, enable):
        # Enable/disable panel controls
        self.blockSignals(not enable)
        self.__block_control_updates = not enable

        self.txt_search.setEnabled(enable)
        self.btn_open_report.setEnabled(enable)

        # Enable/disable view error results button, which depends on a selection as well
        self.__update_view_error_button_state()

    def __update_view_error_button_state(self):
        enable = False
        if not self.__block_control_updates:
            if self.trw_qrs.selectedItems():
                # Only enable the next panel for QRs that have errors
                qr_key = self.__get_selected_qr_key()
                qr_result = self.__controller.get_qr_result(qr_key)
                if qr_result:
                    enable = qr_result.level == EnumQualityRuleResult.ERRORS

        self.btn_view_error_results.setEnabled(enable)

    def __set_qr_progress(self, qr_key, value):
        item = self.__get_item_by_qr_key(qr_key)
        if item:
            self.trw_qrs.itemWidget(item,
                                    RESULT_COLUMN).setText("{}%".format(value))
            QCoreApplication.processEvents()

    def __set_qr_validation_result(self, qr, qr_result):
        self.__controller.set_qr_validation_result(qr, qr_result)
        item = self.__get_item_by_qr_key(qr.id())
        if item:
            self.__set_style_for_validation_result(item, qr_result)

    def __set_style_for_validation_result(self, item, qr_result):
        # Note this method considers when qr_result hasn't been set
        if qr_result:
            text = qr_result.msg
            if qr_result.record_count:
                text = "{} records generated in the error database.\n({})".format(
                    qr_result.record_count, text)
            item.setData(RESULT_COLUMN, Qt.ToolTipRole, text)

        self.trw_qrs.setItemWidget(
            item, RESULT_COLUMN,
            self.__get_custom_widget_item_for_result(qr_result))

    def __get_custom_widget_item_for_result(self, qr_result):
        label = QLabel()

        if not qr_result:
            style = WIDGET_STYLE_QUALITY_RULE_INITIAL_STATE
            label.setText("0%")
        elif qr_result.level == EnumQualityRuleResult.SUCCESS:
            style = WIDGET_STYLE_QUALITY_RULE_SUCCESS
            icon = QIcon(
                ":/Asistente-LADM-COL/resources/images/qr_validation.svg")
            label.setPixmap(icon.pixmap(QSize(16, 16)))
        elif qr_result.level == EnumQualityRuleResult.ERRORS:
            style = WIDGET_STYLE_QUALITY_RULE_ERRORS
            label.setText(str(qr_result.record_count))
        elif qr_result.level == EnumQualityRuleResult.UNDEFINED:
            style = WIDGET_STYLE_QUALITY_RULE_UNDEFINED
        else:  # EnumQualityRuleResult.CRITICAL
            style = WIDGET_STYLE_QUALITY_RULE_CRITICAL

        label.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        label.setStyleSheet("QLabel{}".format(style))

        return label

    def update_total_progress(self, value):
        self.pbr_total_progress.setValue(value)
        if value == 100:
            self.__enable_panel_controls(True)

    def unblock_panel(self):
        """
        Since the panel is blocked while getting QR results,
        we need a way to let external classes call "unblock_panel",
        because getting QR results might trigger errors and then they
        would need to go back and close/accept this panel.
        """
        self.blockSignals(False)
        self.__block_control_updates = False

    def show_error_results_panel(self):
        """
        Slot called to show the task panel based on a selected task.

        :param task_id: Id of the task that will be used to show the task panel.
        """
        self.parent.show_error_results_panel()

    def __show_help(self):
        show_plugin_help("quality_rules")
Esempio n. 2
0
class ChangeDetectionSettingsDialog(QDialog, DIALOG_UI):
    CHANGE_DETECTIONS_MODE_SUPPLIES_MODEL = "CHANGE_DETECTIONS_MODE_SUPPLIES_MODEL"
    CHANGE_DETECTIONS_MODES = {CHANGE_DETECTIONS_MODE_SUPPLIES_MODEL: QCoreApplication.translate("ChangeDetectionSettingsDialog", "Change detection supplies model")}

    def __init__(self, parent=None, qgis_utils=None, conn_manager=None):
        QDialog.__init__(self, parent)
        self.setupUi(self)
        self.logger = Logger()
        self.help_strings = HelpStrings()
        self.txt_help_page.setHtml(self.help_strings.CHANGE_DETECTION_SETTING_DIALOG_HELP)

        self.conn_manager = conn_manager
        self.qgis_utils = qgis_utils

        # we will use a unique instance of setting dialog
        self.settings_dialog = SettingsDialog(qgis_utils=self.qgis_utils, conn_manager=self.conn_manager)
        # The database configuration is saved if it becomes necessary
        # to restore the configuration when the user rejects the dialog
        self.init_db_collected = None
        self.init_db_supplies = None
        self.set_init_db_config()  # Always call after the settings_dialog variable is set

        self._db_collected = self.conn_manager.get_db_connector_from_source()
        self._db_supplies = self.conn_manager.get_db_connector_from_source(SUPPLIES_DB_SOURCE)

        # There may be 1 case where we need to emit a db_connection_changed from the change detection settings dialog:
        #   1) Connection Settings was opened and the DB conn was changed.
        self._db_collected_was_changed = False  # To postpone calling refresh gui until we close this dialog instead of settings
        self._db_supplies_was_changed = False

        # Similarly, we could call a refresh on layers and relations cache in 1 case:
        #   1) If the change detection settings dialog was called for the COLLECTED source: opening Connection Settings
        #      and changing the DB connection.
        self._schedule_layers_and_relations_refresh = False

        for mode, label_mode in self.CHANGE_DETECTIONS_MODES.items():
            self.cbo_change_detection_modes.addItem(label_mode, mode)

        self.radio_button_other_db.setChecked(True)  # Default option
        self.radio_button_same_db.setEnabled(True)
        if not self._db_collected.supplies_model_exists():
            self.radio_button_same_db.setEnabled(False)

        self.radio_button_same_db.toggled.connect(self.update_supplies_db_options)
        self.update_supplies_db_options()

        self.btn_collected_db.clicked.connect(self.show_settings_collected_db)
        self.btn_supplies_db.clicked.connect(self.show_settings_supplies_db)

        # Default color error labels
        self.lbl_msg_collected.setStyleSheet('color: orange')
        self.lbl_msg_supplies.setStyleSheet('color: orange')

        # Set connections
        self.buttonBox.accepted.disconnect()
        self.buttonBox.accepted.connect(self.accepted)
        self.buttonBox.helpRequested.connect(self.show_help)

        self.bar = QgsMessageBar()
        self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop)

        self.update_connection_info()

        self.logger.clear_message_bar()  # Close any existing message in QGIS message bar

    def set_init_db_config(self):
        """
         A copy of the initial connections to the database is made,
         User can change the initial connections and then cancel the changes.
         Initial connections need to be re-established
        """
        self.init_db_collected = self.conn_manager.get_db_connector_from_source(COLLECTED_DB_SOURCE)
        self.init_db_supplies = self.conn_manager.get_db_connector_from_source(SUPPLIES_DB_SOURCE)

    def update_supplies_db_options(self):
        if self.radio_button_same_db.isChecked():
            self.btn_supplies_db.setEnabled(False)
        else:
            self.btn_supplies_db.setEnabled(True)
        self.update_connection_info()

    def show_settings_collected_db(self):
        self.settings_dialog.set_db_source(COLLECTED_DB_SOURCE)
        self.settings_dialog.set_tab_pages_list([SETTINGS_CONNECTION_TAB_INDEX])
        self.settings_dialog.set_required_models([LADMNames.OPERATION_MODEL_PREFIX])
        self.settings_dialog.db_connection_changed.connect(self.db_connection_changed)

        if self.settings_dialog.exec_():
            self._db_collected = self.settings_dialog.get_db_connection()
            self.update_connection_info()
        self.settings_dialog.db_connection_changed.disconnect(self.db_connection_changed)

    def show_settings_supplies_db(self):
        self.settings_dialog.set_db_source(SUPPLIES_DB_SOURCE)
        self.settings_dialog.set_tab_pages_list([SETTINGS_CONNECTION_TAB_INDEX])
        self.settings_dialog.set_required_models([LADMNames.SUPPLIES_MODEL_PREFIX])
        self.settings_dialog.db_connection_changed.connect(self.db_connection_changed)

        if self.settings_dialog.exec_():
            self._db_supplies = self.settings_dialog.get_db_connection()
            self.update_connection_info()
        self.settings_dialog.db_connection_changed.disconnect(self.db_connection_changed)

    def db_connection_changed(self, db, ladm_col_db, db_source):
        # We dismiss parameters here, after all, we already have the db, and the ladm_col_db
        # may change from this moment until we close the import schema dialog
        if db_source == COLLECTED_DB_SOURCE:
            self._db_collected_was_changed = True
            self._schedule_layers_and_relations_refresh = True
        else:
            self._db_supplies_was_changed = True

    def update_connection_info(self):
        # Validate db connections
        self.lbl_msg_collected.setText("")
        self.lbl_msg_supplies.setText("")
        self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(False)

        # First, update status of same_db button according to collected db connection
        res_collected, code_collected, msg_collected = self._db_collected.test_connection(required_models=[LADMNames.OPERATION_MODEL_PREFIX])
        res_supplies, code_supplies, msg_supplies = self._db_collected.test_connection(required_models=[LADMNames.SUPPLIES_MODEL_PREFIX])

        if res_supplies:
            self.radio_button_same_db.setEnabled(True)
        else:
            self.radio_button_same_db.setChecked(False)  # signal update the label

        if not self.radio_button_same_db.isChecked():
            res_supplies, code_supplies, msg_supplies = self._db_supplies.test_connection(required_models=[LADMNames.SUPPLIES_MODEL_PREFIX])

        # Update collected db connection label
        db_description = self._db_collected.get_description_conn_string()
        if db_description:
            self.db_collected_connect_label.setText(db_description)
            self.db_collected_connect_label.setToolTip(self._db_collected.get_display_conn_string())
        else:
            self.db_collected_connect_label.setText(QCoreApplication.translate("ChangeDetectionSettingsDialog", "The database is not defined!"))
            self.db_collected_connect_label.setToolTip('')

        # Update supplies db connection label
        if self.radio_button_same_db.isChecked():
            self.db_supplies_connect_label.setText(self.db_collected_connect_label.text())
            self.db_supplies_connect_label.setToolTip(self.db_collected_connect_label.toolTip())
        else:
            db_description = self._db_supplies.get_description_conn_string()
            if db_description:
                self.db_supplies_connect_label.setText(db_description)
                self.db_supplies_connect_label.setToolTip(self._db_supplies.get_display_conn_string())
            else:
                self.db_supplies_connect_label.setText(QCoreApplication.translate("ChangeDetectionSettingsDialog", "The database is not defined!"))
                self.db_supplies_connect_label.setToolTip('')

        # Update error message labels
        if not res_collected:
            self.lbl_msg_collected.setText(QCoreApplication.translate("ChangeDetectionSettingsDialog", "Warning: DB connection is not valid"))
            self.lbl_msg_collected.setToolTip(msg_collected)

        if not res_supplies:
            self.lbl_msg_supplies.setText(QCoreApplication.translate("ChangeDetectionSettingsDialog", "Warning: DB connection is not valid"))
            self.lbl_msg_supplies.setToolTip(msg_supplies)

        if res_collected and res_supplies:
            self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(True)

    def accepted(self):
        """
        Confirm changes in db connections.
        If user select collected db as supplies db we update supplies db connection with collected db connection.

        If there are layers loaded in canvas from a previous connection that changed, we ask users
        if they want to clean the canvas or preserve the layers.

        If none of the connections changed, the dialog is closed without asking anything to users, and an info message
        is displayed.
        """
        if self.radio_button_same_db.isChecked():
            # Set supplies db connector from collected db connector
            self.conn_manager.save_parameters_conn(self._db_collected, SUPPLIES_DB_SOURCE)
            self._db_supplies = self._db_collected
            self.conn_manager.db_connection_changed.emit(self._db_supplies, self._db_supplies.test_connection()[0], SUPPLIES_DB_SOURCE)

        # Show messages when closing dialog
        if self._db_collected_was_changed or self._db_supplies_was_changed:
            if list(QgsProject.instance().mapLayers().values()):
                message = ""
                if self._db_collected_was_changed and self._db_supplies_was_changed:
                    message = "The connection of the collected and supplies databases has changed,"
                elif self._db_collected_was_changed:
                    message = "The collected database connection has changed,"
                elif self._db_supplies_was_changed:
                    message = "The supplies database connection has changed,"

                message += " do you want to remove the layers that are currently loaded in QGIS?"
                self.show_message_clean_layers_panel(message)
                self.show_message_change_detection_settings_status()  # Show information message indicating if setting is OK
            else:
                self.close_dialog(QDialog.Accepted)
        else:
            # Connections have not changed
            self.close_dialog(QDialog.Accepted)

    def show_message_change_detection_settings_status(self):
        if not self.collected_db_is_valid() and not self.supplies_db_is_valid():
            message = QCoreApplication.translate("ChangeDetectionSettingsDialog", "Neither Collected nor Supplies database connections is valid, you should first configure them before proceeding to detect changes.")
            self.logger.warning_msg(__name__, message, 5)
        elif not self.collected_db_is_valid() or not self.supplies_db_is_valid():

            if not self.collected_db_is_valid():
                message = QCoreApplication.translate("ChangeDetectionSettingsDialog", "Collected database connection is not valid, you should first configure it before proceeding to detect changes.")
                self.logger.warning_msg(__name__, message, 5)

            if not self.supplies_db_is_valid():
                message = QCoreApplication.translate("ChangeDetectionSettingsDialog", "Supplies database connection is not valid, you should first configure it before proceeding to detect changes.")
                self.logger.warning_msg(__name__, message, 5)
        else:
            message = QCoreApplication.translate("ChangeDetectionSettingsDialog", "Both Collected and Supplies database connections are valid, now you can proceed to detect changes.")
            self.logger.message_with_buttons_change_detection_all_and_per_parcel_emitted.emit(message)

    def show_message_clean_layers_panel(self, message):
        msg = QMessageBox(self)
        msg.setIcon(QMessageBox.Question)
        msg.setText(message)
        msg.setWindowTitle(QCoreApplication.translate("ChangeDetectionSettingsDialog", "Remove layers?"))
        msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)
        msg.button(QMessageBox.Yes).setText(QCoreApplication.translate("ChangeDetectionSettingsDialog", "Yes, remove layers"))
        msg.button(QMessageBox.No).setText(QCoreApplication.translate("ChangeDetectionSettingsDialog", "No, don't remove"))
        reply = msg.exec_()

        if reply == QMessageBox.Yes:
            QgsProject.instance().layerTreeRoot().removeAllChildren()
            self.close_dialog(QDialog.Accepted)
        elif reply == QMessageBox.No:
            self.close_dialog(QDialog.Accepted)
        elif reply == QMessageBox.Cancel:
            pass  # Continue config db connections

    def collected_db_is_valid(self):
        res, foo, bar = self._db_collected.test_connection(required_models=[LADMNames.OPERATION_MODEL_PREFIX])
        return res

    def supplies_db_is_valid(self):
        res, foo, bar = self._db_supplies.test_connection(required_models=[LADMNames.SUPPLIES_MODEL_PREFIX])
        return res

    def reject(self):
        self.close_dialog(QDialog.Rejected)

    def close_dialog(self, result):
        """
        We use this slot to be safe when emitting the db_connection_changed (should be done at the end), otherwise we
        could trigger slots that unload the plugin, destroying dialogs and thus, leading to crashes.
        """
        if result == QDialog.Accepted:
            if self._schedule_layers_and_relations_refresh:
                self.conn_manager.db_connection_changed.connect(self.qgis_utils.cache_layers_and_relations)

            if self._db_collected_was_changed:
                self.conn_manager.db_connection_changed.emit(self._db_collected,
                                                             self._db_collected.test_connection()[0],
                                                             COLLECTED_DB_SOURCE)

            if self._db_supplies_was_changed:
                self.conn_manager.db_connection_changed.emit(self._db_supplies,
                                                             self._db_supplies.test_connection()[0],
                                                             SUPPLIES_DB_SOURCE)

            if self._schedule_layers_and_relations_refresh:
                self.conn_manager.db_connection_changed.disconnect(self.qgis_utils.cache_layers_and_relations)

        elif result == QDialog.Rejected:
            # Go back to initial connections and don't emit db_connection_changed
            if self._db_collected_was_changed:
                self.conn_manager.save_parameters_conn(self.init_db_collected, COLLECTED_DB_SOURCE)

            if self._db_supplies_was_changed:
                self.conn_manager.save_parameters_conn(self.init_db_supplies, SUPPLIES_DB_SOURCE)

        self.show_message_change_detection_settings_status()  # Show information message indicating whether setting is OK
        self.logger.info(__name__, "Dialog closed.")
        self.done(result)

    def show_help(self):
        self.qgis_utils.show_help("change_detection_settings")
class BaseAllocateParcelsInitialPanelWidget(QgsPanelWidget, WIDGET_UI):
    allocate_parcels_to_receiver_panel_requested = pyqtSignal(dict)  # {parcel_fid: parcel_number}
    configure_receivers_panel_requested = pyqtSignal()
    split_data_for_receivers_panel_requested = pyqtSignal()

    STATUS_COL = 1

    def __init__(self, parent, controller):
        QgsPanelWidget.__init__(self, parent)
        self.setupUi(self)
        self.parent = parent
        self._controller = controller
        self.logger = Logger()
        self.app = AppInterface()

        self.setDockMode(True)
        self.setPanelTitle(QCoreApplication.translate("AllocateParcelsFieldDataCapturePanelWidget", "Allocate parcels"))
        self.parent.setWindowTitle(QCoreApplication.translate("AllocateParcelsFieldDataCapturePanelWidget", "Allocate parcels"))

        self.tbl_parcels.resizeColumnsToContents()

        self.txt_search.valueChanged.connect(self.search_value_changed)
        self.tbl_parcels.itemSelectionChanged.connect(self.selection_changed)
        self.btn_allocate.clicked.connect(self.call_allocate_parcels_to_receiver_panel)
        self.btn_configure_receivers.clicked.connect(self.configure_receivers_panel_requested)
        self.btn_show_summary.clicked.connect(self.split_data_for_receivers_panel_requested)
        self.chk_show_only_not_allocated.stateChanged.connect(self.chk_check_state_changed)
        self.btn_reallocate.clicked.connect(self.reallocate_clicked)

        self.connect_to_plot_selection(True)

        self.__parcel_data = dict()  # {parcel_fid: (parcel_number, surveyor_name)}
        self.__selected_items = dict()  # {parcel_fid: parcel_number}

    def _parcel_data(self, refresh_parcel_data=False):
        if not self.__parcel_data or refresh_parcel_data:
            self.__parcel_data = self._controller.get_parcel_receiver_data()

        return self.__parcel_data

    def fill_data(self, refresh_parcel_data=False):
        self.update_selected_items()  # Save selection

        self.tbl_parcels.blockSignals(True)  # We don't want to get itemSelectionChanged here
        self.tbl_parcels.clearContents()
        self.tbl_parcels.blockSignals(False)  # We don't want to get itemSelectionChanged here

        # Build the parcel_data dict taking configuration (search string, chk filter) into account
        parcel_data = self._parcel_data(refresh_parcel_data).copy()
        if self.chk_show_only_not_allocated.isChecked():
            parcel_data = {k:v for k,v in parcel_data.items() if not v[1]}  # v: (parcel_number, surveyor)
        parcel_data = self.filter_data_by_search_string(parcel_data)

        self.tbl_parcels.setRowCount(len(parcel_data))
        self.tbl_parcels.setSortingEnabled(False)

        self.tbl_parcels.blockSignals(True)  # We don't want to get itemSelectionChanged here
        for row, data in enumerate(parcel_data.items()):
            parcel_number, receiver = data[1]
            self.fill_row(data[0], parcel_number, receiver, row)
        self.tbl_parcels.blockSignals(False)  # We don't want to get itemSelectionChanged here

        self.tbl_parcels.setSortingEnabled(True)
        self.tbl_parcels.resizeColumnsToContents()

    def fill_row(self, parcel_fid, parcel_number, receiver, row):
        item = QTableWidgetItem(parcel_number)
        item.setData(Qt.UserRole, parcel_fid)
        self.tbl_parcels.setItem(row, 0, item)

        item2 = QTableWidgetItem(receiver or '')
        if not receiver:
            item2.setBackground(QBrush(NOT_ALLOCATED_PARCEL_COLOR))
        self.tbl_parcels.setItem(row, self.STATUS_COL, item2)

        if parcel_fid in self.__selected_items:
            item.setSelected(True)
            item2.setSelected(True)

    def filter_data_by_search_string(self, parcel_data):
        value = self.txt_search.value().strip()
        if value and len(value) > 1:
            parcel_data = {k:v for k,v in parcel_data.items() if value in v[0]}

        return parcel_data

    def search_value_changed(self, value):
        self.fill_data()

    def chk_check_state_changed(self, state):
        self.fill_data()

    def update_selected_items(self):
        """Update the internal selected_items dict"""
        selected_gui_items = [item.data(Qt.UserRole) for item in self.tbl_parcels.selectedItems()]
        for row in range(self.tbl_parcels.rowCount()):
            item = self.tbl_parcels.item(row, 0)
            fid = item.data(Qt.UserRole)
            if fid in selected_gui_items:
                self.__selected_items[fid] = item.text()
            else:
                if fid in self.__selected_items:
                    # It was selected before, but not anymore
                    del self.__selected_items[fid]

    def selection_changed(self):
        """React upon manual selection in the table widget"""
        self.update_selected_items()

        self.connect_to_plot_selection(False)  # This plot selection should not trigger a table view selection refresh
        self._controller.update_plot_selection(list(self.__selected_items.keys()))
        self.connect_to_plot_selection(True)

    def update_parcel_selection(self, selected, deselected, clear_and_select):
        """React upon a plot selection"""
        self.tbl_parcels.blockSignals(True)  # We don't want to get itemSelectionChanged here
        self.tbl_parcels.clearSelection()  # Reset GUI selection
        self.__selected_items = dict()  # Reset internal selection dict
        parcel_ids = self._controller.get_parcel_numbers_from_selected_plots()

        for parcel_id in parcel_ids:
            if parcel_id in self._parcel_data():
                parcel_number = self._parcel_data()[parcel_id][0]
                items = self.tbl_parcels.findItems(parcel_number, Qt.MatchExactly)
                if items:
                    items[0].setSelected(True)  # Select item in column 0
                    self.tbl_parcels.item(items[0].row(), self.STATUS_COL).setSelected(True)  # Select item in column 1
                else:  # parcel is not currently shown, so select it in internal dict
                    if parcel_id in self._parcel_data():
                        self.__selected_items[parcel_id] = parcel_number

        self.tbl_parcels.blockSignals(False)
        self.update_selected_items()  # Update the internal selection dict

    def connect_to_plot_selection(self, connect):
        if connect:
            self._controller.plot_layer().selectionChanged.connect(self.update_parcel_selection)
        else:
            try:
                self._controller.plot_layer().selectionChanged.disconnect(self.update_parcel_selection)
            except (TypeError, RuntimeError):  # Layer in C++ could be already deleted...
                pass

    def close_panel(self):
        # Disconnect signals
        self.connect_to_plot_selection(False)

    def panel_accepted_clear_message_bar(self):
        self.logger.clear_message_bar()

    def panel_accepted_refresh_parcel_data(self):
        """Slot for refreshing parcel data when it has changed in other panels"""
        self.panel_accepted_clear_message_bar()
        self.fill_data(True)

    def panel_accepted_refresh_and_clear_selection(self):
        self.panel_accepted_refresh_parcel_data()  # Refresh data in table widget, as it might be out of sync with newly added layers
        self.tbl_parcels.clearSelection()  # Selection might be remembered from the status before converting to offline

    def call_allocate_parcels_to_receiver_panel(self):
        # Make sure that all selected items are not yet allocated, otherwise, allow users to deallocate selected
        already_allocated = list()  # [parcel_fid1, ...]
        for parcel_fid, parcel_number in self.__selected_items.items():
            if parcel_fid in self._parcel_data():
                if self._parcel_data()[parcel_fid][1]:  # surveyor_name
                    already_allocated.append(parcel_fid)

        if already_allocated:
            msg = QMessageBox(self)
            msg.setIcon(QMessageBox.Question)
            msg.setText(QCoreApplication.translate("AllocateParcelsFieldDataCapturePanelWidget",
                                                   "Some selected parcels are already allocated!\n\nWhat would you like to do with selected parcels that are already allocated?"))
            msg.setWindowTitle(QCoreApplication.translate("AllocateParcelsFieldDataCapturePanelWidget", "Warning"))
            msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)
            msg.button(QMessageBox.Yes).setText(
                QCoreApplication.translate("AllocateParcelsFieldDataCapturePanelWidget", "Deselect them and continue"))
            msg.button(QMessageBox.No).setText(
                QCoreApplication.translate("AllocateParcelsFieldDataCapturePanelWidget", "Reallocate and continue"))
            reply = msg.exec_()

            if reply == QMessageBox.Yes:  # Ignore
                # Remove selection of allocated parcels, reload table widget data and continue
                for allocated_parcel_id in already_allocated:
                    if allocated_parcel_id in self._parcel_data():
                        items = self.tbl_parcels.findItems(self._parcel_data()[allocated_parcel_id][0], Qt.MatchExactly)
                        if items:  # Item is currently shown, so deselect it in GUI
                            items[0].setSelected(False)  # Deselect item in column 0
                            self.tbl_parcels.item(items[0].row(), self.STATUS_COL).setSelected(False)  # Deselect item in column 1
                        else:  # Item is not currently shown, deselected in internal selection dict
                            if allocated_parcel_id in self.__selected_items:
                                del self.__selected_items[allocated_parcel_id]

                self.fill_data()

                if not self.__selected_items:
                    self.logger.warning_msg(__name__, QCoreApplication.translate("AllocateParcelsFieldDataCapturePanelWidget",
                                                                       "Ignoring selected parcels, there are none to be allocated! First select some!"), 10)
                    return

            elif reply == QMessageBox.No:  # Reallocate
                # Preserve the selected_items dict, but remove allocation before continuing
                if not self.discard_parcel_allocation(already_allocated):
                    return
            else:  # QMessageBox.Cancel
                return

        if self.__selected_items:
            self.allocate_parcels_to_receiver_panel_requested.emit(self.__selected_items)
        else:
            self.logger.warning_msg(__name__, QCoreApplication.translate("AllocateParcelsFieldDataCapturePanelWidget", "First select some parcels to be allocated."), 5)

    def discard_parcel_allocation(self, parcel_fids):
        res = self._controller.discard_parcel_allocation(parcel_fids)
        if res:
            self.fill_data(True)  # Refresh parcel data
            self.logger.success_msg(__name__, QCoreApplication.translate("AllocateParcelsFieldDataCapturePanelWidget",
                                                                         "Selected parcels are now not allocated!"))
        else:
            self.logger.warning_msg(__name__, QCoreApplication.translate("AllocateParcelsFieldDataCapturePanelWidget",
                                                                         "There were troubles reallocating parcels!"))
        return res

    def reallocate_clicked(self):
        if not self.__selected_items:
            self.logger.warning_msg(__name__, QCoreApplication.translate("AllocateParcelsFieldDataCapturePanelWidget",
                                                                         "First select some parcels."), 5)
            return

        # Get selected parcels that are already allocated
        already_allocated = list()  # [parcel_fid1, ...]
        for parcel_fid, parcel_number in self.__selected_items.items():
            if parcel_fid in self._parcel_data():
                if self._parcel_data()[parcel_fid][1]:  # surveyor_name
                    already_allocated.append(parcel_fid)

        if already_allocated:
            # Ask for confirmation
            reply = QMessageBox.question(self,
                                         QCoreApplication.translate("AllocateParcelsFieldDataCapturePanelWidget", "Do you confirm?"),
                                         QCoreApplication.translate("AllocateParcelsFieldDataCapturePanelWidget",
                                                                    "Are you sure you want to remove the allocation of selected parcels?"),
                                         QMessageBox.Yes, QMessageBox.No)
            if reply == QMessageBox.Yes:
                self.discard_parcel_allocation(already_allocated)
        else:
            self.logger.info_msg(__name__, QCoreApplication.translate("AllocateParcelsFieldDataCapturePanelWidget", "Selected parcels are not yet allocated, so we cannot reallocate them."))
Esempio n. 4
0
class ReportGenerator(QObject):
    LOG_TAB = 'LADM-COL Reports'

    enable_action_requested = pyqtSignal(str, bool)

    def __init__(self, ladm_data):
        QObject.__init__(self)
        self.ladm_data = ladm_data
        self.logger = Logger()
        self.app = AppInterface()
        self.java_dependency = JavaDependency()
        self.java_dependency.download_dependency_completed.connect(
            self.download_java_complete)

        self.report_dependency = ReportDependency()
        self.report_dependency.download_dependency_completed.connect(
            self.download_report_complete)

        self.encoding = locale.getlocale()[1]
        # This might be unset
        if not self.encoding:
            self.encoding = 'UTF8'

        self._downloading = False

    def stderr_ready(self, proc):
        text = bytes(proc.readAllStandardError()).decode(self.encoding)
        self.logger.critical(__name__, text, tab=self.LOG_TAB)

    def stdout_ready(self, proc):
        text = bytes(proc.readAllStandardOutput()).decode(self.encoding)
        self.logger.info(__name__, text, tab=self.LOG_TAB)

    def update_yaml_config(self, db, config_path):
        text = ''
        qgs_uri = QgsDataSourceUri(db.uri)

        with open(os.path.join(config_path, 'config_template.yaml')) as f:
            text = f.read()
            text = text.format('{}',
                               DB_USER=qgs_uri.username(),
                               DB_PASSWORD=qgs_uri.password(),
                               DB_HOST=qgs_uri.host(),
                               DB_PORT=qgs_uri.port(),
                               DB_NAME=qgs_uri.database())
        new_file_path = os.path.join(
            config_path, self.get_tmp_filename('yaml_config', 'yaml'))

        with open(new_file_path, 'w') as new_yaml:
            new_yaml.write(text)

        return new_file_path

    def get_layer_geojson(self, db, layer_name, plot_id, report_type):
        if report_type == ANNEX_17_REPORT:
            if layer_name == 'terreno':
                return db.get_annex17_plot_data(plot_id, 'only_id')
            elif layer_name == 'terrenos':
                return db.get_annex17_plot_data(plot_id, 'all_but_id')
            elif layer_name == 'terrenos_all':
                return db.get_annex17_plot_data(plot_id, 'all')
            elif layer_name == 'construcciones':
                return db.get_annex17_building_data()
            else:
                return db.get_annex17_point_data(plot_id)
        else:  #report_type == ANT_MAP_REPORT:
            if layer_name == 'terreno':
                return db.get_ant_map_plot_data(plot_id, 'only_id')
            elif layer_name == 'terrenos':
                return db.get_ant_map_plot_data(plot_id, 'all_but_id')
            elif layer_name == 'terrenos_all':
                return db.get_annex17_plot_data(plot_id, 'all')
            elif layer_name == 'construcciones':
                return db.get_annex17_building_data()
            elif layer_name == 'puntoLindero':
                return db.get_annex17_point_data(plot_id)
            else:  #layer_name == 'cambio_colindancia':
                return db.get_ant_map_neighbouring_change_data(plot_id)

    def update_json_data(self, db, json_spec_file, plot_id, tmp_dir,
                         report_type):
        json_data = dict()
        with open(json_spec_file) as f:
            json_data = json.load(f)

        json_data['attributes']['id'] = plot_id
        json_data['attributes']['datasetName'] = db.schema
        layers = json_data['attributes']['map']['layers']
        for layer in layers:
            layer['geoJson'] = self.get_layer_geojson(db, layer['name'],
                                                      plot_id, report_type)

        overview_layers = json_data['attributes']['overviewMap']['layers']
        for layer in overview_layers:
            layer['geoJson'] = self.get_layer_geojson(db, layer['name'],
                                                      plot_id, report_type)

        new_json_file_path = os.path.join(
            tmp_dir,
            self.get_tmp_filename('json_data_{}'.format(plot_id), 'json'))
        with open(new_json_file_path, 'w') as new_json:
            new_json.write(json.dumps(json_data))

        return new_json_file_path

    def get_tmp_dir(self, create_random=True):
        if create_random:
            return tempfile.mkdtemp()

        return tempfile.gettempdir()

    def get_tmp_filename(self, basename, extension='gpkg'):
        return "{}_{}.{}".format(basename,
                                 str(time.time()).replace(".", ""), extension)

    def generate_report(self, db, report_type):
        # Check if mapfish and Jasper are installed, otherwise show where to
        # download them from and return
        if not self.report_dependency.check_if_dependency_is_valid():
            self.report_dependency.download_dependency(URL_REPORTS_LIBRARIES)
            return

        java_home_set = self.java_dependency.set_java_home()
        if not java_home_set:
            self.java_dependency.get_java_on_demand()
            self.logger.info_msg(
                __name__,
                QCoreApplication.translate(
                    "ReportGenerator",
                    "Java is a prerequisite. Since it was not found, it is being configured..."
                ))
            return

        plot_layer = self.app.core.get_layer(db, db.names.LC_PLOT_T, load=True)
        if not plot_layer:
            return

        selected_plots = plot_layer.selectedFeatures()
        if not selected_plots:
            self.logger.warning_msg(
                __name__,
                QCoreApplication.translate(
                    "ReportGenerator",
                    "To generate reports, first select at least a plot!"))
            return

        # Where to store the reports?
        previous_folder = QSettings().value(
            "Asistente-LADM-COL/reports/save_into_dir", ".")
        save_into_folder = QFileDialog.getExistingDirectory(
            None,
            QCoreApplication.translate(
                "ReportGenerator",
                "Select a folder to save the reports to be generated"),
            previous_folder)
        if not save_into_folder:
            self.logger.warning_msg(
                __name__,
                QCoreApplication.translate(
                    "ReportGenerator",
                    "You need to select a folder where to save the reports before continuing."
                ))
            return
        QSettings().setValue("Asistente-LADM-COL/reports/save_into_dir",
                             save_into_folder)

        config_path = os.path.join(DEPENDENCY_REPORTS_DIR_NAME, report_type)
        json_spec_file = os.path.join(config_path, 'spec_json_file.json')

        script_name = ''
        if os.name == 'posix':
            script_name = 'print'
        elif os.name == 'nt':
            script_name = 'print.bat'

        script_path = os.path.join(DEPENDENCY_REPORTS_DIR_NAME, 'bin',
                                   script_name)
        if not os.path.isfile(script_path):
            self.logger.warning(
                __name__,
                "Script file for reports wasn't found! {}".format(script_path))
            return

        self.enable_action_requested.emit(report_type, False)

        # Update config file
        yaml_config_path = self.update_yaml_config(db, config_path)
        self.logger.debug(
            __name__, "Config file for reports: {}".format(yaml_config_path))

        total = len(selected_plots)
        step = 0
        count = 0
        tmp_dir = self.get_tmp_dir()

        # Progress bar setup
        progress = QProgressBar()
        if total == 1:
            progress.setRange(0, 0)
        else:
            progress.setRange(0, 100)
        progress.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
        self.app.gui.create_progress_message_bar(
            QCoreApplication.translate("ReportGenerator",
                                       "Generating {} report{}...").format(
                                           total, '' if total == 1 else 's'),
            progress)

        polygons_with_holes = []
        multi_polygons = []

        for selected_plot in selected_plots:
            plot_id = selected_plot[db.names.T_ID_F]

            geometry = selected_plot.geometry()
            abstract_geometry = geometry.get()
            if abstract_geometry.ringCount() > 1:
                polygons_with_holes.append(str(plot_id))
                self.logger.warning(
                    __name__,
                    QCoreApplication.translate(
                        "ReportGenerator",
                        "Skipping Annex 17 for plot with {}={} because it has holes. The reporter module does not support such polygons."
                    ).format(db.names.T_ID_F, plot_id))
                continue
            if abstract_geometry.numGeometries() > 1:
                multi_polygons.append(str(plot_id))
                self.logger.warning(
                    __name__,
                    QCoreApplication.translate(
                        "ReportGenerator",
                        "Skipping Annex 17 for plot with {}={} because it is a multi-polygon. The reporter module does not support such polygons."
                    ).format(db.names.T_ID_F, plot_id))
                continue

            # Generate data file
            json_file = self.update_json_data(db, json_spec_file, plot_id,
                                              tmp_dir, report_type)
            self.logger.debug(__name__,
                              "JSON file for reports: {}".format(json_file))

            # Run sh/bat passing config and data files
            proc = QProcess()
            proc.readyReadStandardError.connect(
                functools.partial(self.stderr_ready, proc=proc))
            proc.readyReadStandardOutput.connect(
                functools.partial(self.stdout_ready, proc=proc))

            parcel_number = self.ladm_data.get_parcels_related_to_plots(
                db, [plot_id], db.names.LC_PARCEL_T_PARCEL_NUMBER_F) or ['']
            file_name = '{}_{}_{}.pdf'.format(report_type, plot_id,
                                              parcel_number[0])

            current_report_path = os.path.join(save_into_folder, file_name)
            proc.start(script_path, [
                '-config', yaml_config_path, '-spec', json_file, '-output',
                current_report_path
            ])

            if not proc.waitForStarted():
                # Grant execution permissions
                os.chmod(
                    script_path, stat.S_IXOTH | stat.S_IXGRP | stat.S_IXUSR
                    | stat.S_IRUSR | stat.S_IRGRP)
                proc.start(script_path, [
                    '-config', yaml_config_path, '-spec', json_file, '-output',
                    current_report_path
                ])

            if not proc.waitForStarted():
                proc = None
                self.logger.warning(
                    __name__, "Couldn't execute script to generate report...")
            else:
                loop = QEventLoop()
                proc.finished.connect(loop.exit)
                loop.exec()

                self.logger.debug(__name__,
                                  "{}:{}".format(plot_id, proc.exitCode()))
                if proc.exitCode() == 0:
                    count += 1

                step += 1
                progress.setValue(step * 100 / total)

        os.remove(yaml_config_path)

        self.enable_action_requested.emit(report_type, True)
        self.logger.clear_message_bar()

        if total == count:
            if total == 1:
                msg = QCoreApplication.translate(
                    "ReportGenerator",
                    "The report <a href='file:///{}'>{}</a> was successfully generated!"
                ).format(normalize_local_url(save_into_folder), file_name)
            else:
                msg = QCoreApplication.translate(
                    "ReportGenerator",
                    "All reports were successfully generated in folder <a href='file:///{path}'>{path}</a>!"
                ).format(path=normalize_local_url(save_into_folder))

            self.logger.success_msg(__name__, msg)
        else:
            details_msg = ''
            if polygons_with_holes:
                details_msg += QCoreApplication.translate(
                    "ReportGenerator",
                    " The following polygons were skipped because they have holes and are not supported: {}."
                ).format(", ".join(polygons_with_holes))
            if multi_polygons:
                details_msg += QCoreApplication.translate(
                    "ReportGenerator",
                    " The following polygons were skipped because they are multi-polygons and are not supported: {}."
                ).format(", ".join(multi_polygons))

            if total == 1:
                msg = QCoreApplication.translate(
                    "ReportGenerator",
                    "The report for plot {} couldn't be generated!{} See QGIS log (tab '{}') for details."
                ).format(plot_id, details_msg, self.LOG_TAB)
            else:
                if count == 0:
                    msg = QCoreApplication.translate(
                        "ReportGenerator",
                        "No report could be generated!{} See QGIS log (tab '{}') for details."
                    ).format(details_msg, self.LOG_TAB)
                else:
                    msg = QCoreApplication.translate(
                        "ReportGenerator",
                        "At least one report couldn't be generated!{details_msg} See QGIS log (tab '{log_tab}') for details. Go to <a href='file:///{path}'>{path}</a> to see the reports that were generated."
                    ).format(details_msg=details_msg,
                             path=normalize_local_url(save_into_folder),
                             log_tab=self.LOG_TAB)

            self.logger.warning_msg(__name__, msg)

    def download_java_complete(self):
        if self.java_dependency.fetcher_task and not self.java_dependency.fetcher_task.isCanceled(
        ):
            if self.java_dependency.check_if_dependency_is_valid():
                self.logger.info_msg(
                    __name__,
                    QCoreApplication.translate(
                        "ReportGenerator",
                        "Java was successfully configured!"), 5)
        else:
            self.logger.warning_msg(
                __name__,
                QCoreApplication.translate(
                    "ReportGenerator",
                    "You have just canceled the Java dependency download."), 5)

    def download_report_complete(self):
        if self.report_dependency.fetcher_task and not self.report_dependency.fetcher_task.isCanceled(
        ):
            if self.report_dependency.check_if_dependency_is_valid():
                self.logger.info_msg(
                    __name__,
                    QCoreApplication.translate(
                        "ReportGenerator",
                        "Report dependency was successfully configured!"), 5)
        else:
            self.logger.warning_msg(
                __name__,
                QCoreApplication.translate(
                    "ReportGenerator",
                    "You have just canceled the report dependency download."),
                5)
Esempio n. 5
0
class BaseDockWidgetFieldDataCapture(QgsDockWidget, DOCKWIDGET_UI):
    def __init__(self, iface, db, ladm_data, allocate_mode=True):
        super(BaseDockWidgetFieldDataCapture, self).__init__(None)
        self.setupUi(self)
        self.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)

        self.logger = Logger()
        self.logger.clear_message_bar()  # Clear QGIS message bar

        self._controller = self._get_controller(iface, db, ladm_data)
        self._controller.field_data_capture_layer_removed.connect(self.layer_removed)

        # Configure panels
        self.configure_receivers_panel = None
        self.lst_configure_receivers_panel = list()

        self.allocate_parcels_to_receiver_panel = None
        self.lst_allocate_parcels_to_receiver_panel = list()

        self.split_data_for_receivers_panel = None
        self.lst_split_data_for_receivers_panel = list()

        self.allocate_panel = None

        if allocate_mode:
            self._initialize_allocate_initial_panel()
        else:  # Synchronize mode
            # self.synchronize_panel = ChangesPerParcelPanelWidget(self, self.utils)
            # self.widget.setMainPanel(self.synchronize_panel)
            # self.lst_parcel_panels.append(self.synchronize_panel)
            self._initialize_synchronize_initial_panel()

    def _get_controller(self, iface, db, ladm_data):
        raise NotImplementedError

    def _initialize_allocate_initial_panel(self):
        raise NotImplementedError

    def _initialize_synchronize_initial_panel(self):
        raise NotImplementedError

    def show_configure_receivers_panel(self):
        with OverrideCursor(Qt.WaitCursor):
            self.__reset_receivers_panel_vars()

            self.configure_receivers_panel = self._get_receivers_panel()
            self.configure_receivers_panel.clear_message_bar_requested.connect(
                self.allocate_panel.panel_accepted_clear_message_bar)
            self.widget.showPanel(self.configure_receivers_panel)
            self.lst_configure_receivers_panel.append(self.configure_receivers_panel)

    def __reset_receivers_panel_vars(self):
        if self.lst_configure_receivers_panel:
            for panel in self.lst_configure_receivers_panel:
                try:
                    self.widget.closePanel(panel)
                except RuntimeError as e:  # Panel in C++ could be already closed...
                    pass

            self.lst_configure_receivers_panel = list()
            self.configure_receivers_panel = None

    def _get_receivers_panel(self):
        raise NotImplementedError

    def show_allocate_parcels_to_receiver_panel(self, selected_parcels):
        with OverrideCursor(Qt.WaitCursor):
            self._reset_allocate_parcels_to_receiver_panel_vars()

            self.allocate_parcels_to_receiver_panel = self._get_allocate_to_receiver_panel(selected_parcels)
            self.allocate_parcels_to_receiver_panel.refresh_parcel_data_requested.connect(
                self.allocate_panel.panel_accepted_refresh_parcel_data)
            self.widget.showPanel(self.allocate_parcels_to_receiver_panel)
            self.lst_allocate_parcels_to_receiver_panel.append(self.allocate_parcels_to_receiver_panel)

    def _reset_allocate_parcels_to_receiver_panel_vars(self):
        if self.lst_allocate_parcels_to_receiver_panel:
            for panel in self.lst_allocate_parcels_to_receiver_panel:
                try:
                    self.widget.closePanel(panel)
                except RuntimeError as e:  # Panel in C++ could be already closed...
                    pass

            self.lst_allocate_parcels_to_receiver_panel = list()
            self.allocate_parcels_to_receiver_panel = None

    def _get_allocate_to_receiver_panel(self, selected_parcels):
        raise NotImplementedError

    def show_split_data_for_receivers_panel(self):
        with OverrideCursor(Qt.WaitCursor):
            self._reset_split_data_for_receivers_panel_vars()

            self.split_data_for_receivers_panel = self._get_split_data_for_receivers_panel()
            self.split_data_for_receivers_panel.refresh_parcel_data_clear_selection_requested.connect(
                self.allocate_panel.panel_accepted_refresh_and_clear_selection)
            self.widget.showPanel(self.split_data_for_receivers_panel)
            self.lst_split_data_for_receivers_panel.append(self.split_data_for_receivers_panel)

    def _get_split_data_for_receivers_panel(self):
        raise NotImplementedError

    def _reset_split_data_for_receivers_panel_vars(self):
        if self.lst_split_data_for_receivers_panel:
            for panel in self.lst_split_data_for_receivers_panel:
                try:
                    self.widget.closePanel(panel)
                except RuntimeError as e:  # Panel in C++ could be already closed...
                    pass

            self.lst_split_data_for_receivers_panel = list()
            self.split_data_for_receivers_panel = None

    def closeEvent(self, event):
        # Close here open signals in other panels (if needed)
        if self.allocate_panel:
            self.allocate_panel.close_panel()

        self.close_dock_widget()

    def add_layers(self):
        self._controller.add_layers()

    def layer_removed(self):
        self.logger.info_msg(__name__, QCoreApplication.translate("DockWidgetFieldDataCapture",
                                                                  "'Field data capture' has been closed because you just removed a required layer."))
        self.close_dock_widget()

    def update_db_connection(self, db, ladm_col_db, db_source):
        self.close_dock_widget()  # New DB: the user needs to use the menus again, which will start FDC from scratch

    def close_dock_widget(self):
        try:
            self._controller.field_data_capture_layer_removed.disconnect()  # disconnect layer signals
        except:
            pass

        self.close()  # The user needs to use the menus again, which will start everything from scratch

    def initialize_layers(self):
        self._controller.initialize_layers()
class DockWidgetQualityRules(QgsDockWidget, DOCKWIDGET_UI):
    """
    Main UI for the Quality Rules module. It holds other panels.
    """
    trigger_action_emitted = pyqtSignal(str)  # action tag

    def __init__(self, controller, parent):
        super(DockWidgetQualityRules, self).__init__(parent)
        self.setupUi(self)
        self.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)

        self.__controller = controller
        self.__controller.quality_rule_layer_removed.connect(self.__layer_removed)

        self.logger = Logger()

        # Configure panels
        self.__general_results_panel = None
        self.__error_results_panel = None

        self.__main_panel = QualityRulesInitialPanelWidget(controller, self)
        self.widget.setMainPanel(self.__main_panel)

    def __layer_removed(self):
        self.logger.info_msg(__name__, QCoreApplication.translate("DockWidgetQualityRules",
                                                                  "'Quality rules panel' has been initialized because you just removed a required layer."))

        # Go back to initial panel when the user removes QR DB layers
        # Note this closes open signals on panels, reset controller variables
        # and even removes the whole QR DB group!
        self.widget.acceptAllPanels()

    def closeEvent(self, event):
        # Note this closes open signals on panels, reset controller variables
        # and even removes the whole QR DB group!
        self.widget.acceptAllPanels()

        self.close_dock_widget()

    def update_db_connection(self, db, ladm_col_db, db_source):
        self.close_dock_widget()  # The user needs to use the menus again, which will start everything from scratch

    def close_dock_widget(self):
        self.close()  # The user needs to use the menus again, which will start everything from scratch

    def show_general_results_panel(self, mode):
        """
        :params mode: EnumQualityRulePanelMode
        """
        with OverrideCursor(Qt.WaitCursor):
            self.__delete_general_result_panel()

            self.__general_results_panel = QualityRulesGeneralResultsPanelWidget(self.__controller, mode, self)
            self.__controller.total_progress_changed.connect(self.__general_results_panel.update_total_progress)
            self.__general_results_panel.panelAccepted.connect(self.__controller.reset_vars_for_general_results_panel)
            self.widget.showPanel(self.__general_results_panel)

            if mode == EnumQualityRulePanelMode.VALIDATE:
                self.logger.clear_message_bar()
                res, msg, db_qr = self.__controller.validate_qrs()
                if not res:
                    self.__general_results_panel.unblock_panel()
                    self.widget.acceptAllPanels()  # Go back to initial panel
                    self.logger.warning_msg(__name__, msg)

    def __delete_general_result_panel(self):
        if self.__general_results_panel is not None:
            try:
                self.widget.closePanel(self.__general_results_panel)
            except RuntimeError as e:  # Panel in C++ could be already closed...
                pass

            self.__general_results_panel = None

    def show_error_results_panel(self):
        with OverrideCursor(Qt.WaitCursor):
            if self.__error_results_panel is not None:
                try:
                    self.widget.closePanel(self.__error_results_panel)
                except RuntimeError as e:  # Panel in C++ could be already closed...
                    pass

                self.__error_results_panel = None

            self.__error_results_panel = QualityRulesErrorResultsPanelWidget(self.__controller, self)
            self.__error_results_panel.panelAccepted.connect(self.__controller.reset_vars_for_error_results_panel)
            self.widget.showPanel(self.__error_results_panel)
Esempio n. 7
0
class Dependency(QObject):
    download_dependency_completed = pyqtSignal()
    download_dependency_progress_changed = pyqtSignal(int)  # progress

    def __init__(self):
        QObject.__init__(self)
        self.logger = Logger()
        self._downloading = False
        self._show_cursor = True
        self.dependency_name = ""
        self.fetcher_task = None

    def download_dependency(self, uri):
        if not uri:
            self.logger.warning_msg(
                __name__,
                QCoreApplication.translate(
                    "Dependency", "Invalid URL to download dependency."))

        self.logger.clear_message_bar()
        is_valid = self.check_if_dependency_is_valid()

        if is_valid:
            self.logger.debug(
                __name__,
                QCoreApplication.translate(
                    "Dependency",
                    "The {} dependency is already valid, so it won't be downloaded! (Dev, why did you asked to download it :P?)"
                    .format(self.dependency_name)))
            return

        if not self._downloading:  # Already downloading dependency?
            if is_connected(TEST_SERVER):
                self._downloading = True
                self.logger.clear_message_bar()
                self.logger.info_msg(
                    __name__,
                    QCoreApplication.translate(
                        "Dependency",
                        "A {} dependency will be downloaded...".format(
                            self.dependency_name)))
                self.fetcher_task = QgsNetworkContentFetcherTask(QUrl(uri))
                self.fetcher_task.begun.connect(self._task_begun)
                self.fetcher_task.progressChanged.connect(
                    self._task_progress_changed)
                self.fetcher_task.fetched.connect(
                    partial(self._save_dependency_file, self.fetcher_task))
                self.fetcher_task.taskCompleted.connect(self._task_completed)
                QgsApplication.taskManager().addTask(self.fetcher_task)
            else:
                self.logger.clear_message_bar()
                self.logger.warning_msg(
                    __name__,
                    QCoreApplication.translate(
                        "Dependency",
                        "There was a problem connecting to Internet."))
                self._downloading = False

    def check_if_dependency_is_valid(self):
        raise NotImplementedError

    def _task_begun(self):
        if self._show_cursor:
            QApplication.setOverrideCursor(Qt.WaitCursor)

    def _task_progress_changed(self, progress):
        self.download_dependency_progress_changed.emit(progress)

    def _save_dependency_file(self, fetcher_task):
        raise NotImplementedError

    def _task_completed(self):
        if self._show_cursor:
            QApplication.restoreOverrideCursor()
        self.download_dependency_completed.emit()
Esempio n. 8
0
class BaseSplitDataForReceiversPanelWidget(QgsPanelWidget, WIDGET_UI):
    refresh_parcel_data_clear_selection_requested = pyqtSignal()

    def __init__(self, parent, controller):
        QgsPanelWidget.__init__(self, parent)
        self.setupUi(self)
        self.parent = parent
        self._controller = controller

        self.logger = Logger()
        self.app = AppInterface()

        self.setDockMode(True)
        self.setPanelTitle(QCoreApplication.translate("BaseSplitDataForReceiversPanelWidget", "Convert to offline"))
        self.parent.setWindowTitle(QCoreApplication.translate("BaseSplitDataForReceiversPanelWidget", "Allocate parcels"))

        self.mQgsFileWidget.lineEdit().setPlaceholderText(QCoreApplication.translate("BaseSplitDataForReceiversPanelWidget", "Choose the output folder..."))
        self.mQgsFileWidget.setDefaultRoot(self.app.settings.export_dir_field_data)

        self.panelAccepted.connect(self.panel_accepted)
        self._controller.export_field_data_progress.connect(self.update_progress)
        self.btn_split_data.clicked.connect(self.export_field_data)

        self.fill_data()

    def panel_accepted(self):
        self.refresh_parcel_data_clear_selection_requested.emit()

    def fill_data(self):
        summary_data = self._controller.get_summary_data()
        row = 1
        for row_data in summary_data:
            receiver_name, parcel_count = row_data
            self.fill_row(receiver_name, parcel_count, row)
            row += 2  # v_spacer + label

        # After the real data, we add a new spacer that expands itself to shrink content upwards
        v_spacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
        self.grb_summary.layout().addItem(v_spacer, row, 0)

        # Show/hide warning depending on if there are not allocated parcels
        not_allocated_parcels = self._controller.get_count_of_not_allocated_parcels()
        self.lbl_warning.setVisible(not_allocated_parcels)
        self.lbl_not_allocated_parcels.setVisible(not_allocated_parcels)
        self.lbl_not_allocated_parcels.setText(QCoreApplication.translate("BaseSplitDataForReceiversPanelWidget",
                                               "{} parcels have not been yet allocated!").format(not_allocated_parcels))

    def fill_row(self, receiver_name, parcel_count, row):
        # First add a spacer between previoud data row (could be the title) and the next row data
        v_spacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Preferred)
        self.grb_summary.layout().addItem(v_spacer, row, 0)

        # Now, let's add a row of data
        w = QLabel(receiver_name)
        self.grb_summary.layout().addWidget(w, row+1, 0)
        w = QLabel(str(parcel_count))
        w.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        self.grb_summary.layout().addWidget(w, row+1, 2)

    def export_field_data(self):
        self.logger.clear_message_bar()
        export_dir = self.mQgsFileWidget.filePath()

        if export_dir and os.path.isdir(export_dir):
            self.app.settings.export_dir_field_data = export_dir
            self.prb_export_field_data.setRange(0, 100)
            self.prb_export_field_data.setValue(0)

            res, msg = self._controller.export_field_data(export_dir)

            self.logger.success_warning(__name__, res, msg, EnumLogHandler.MESSAGE_BAR)
        else:
            self.logger.warning_msg(__name__, QCoreApplication.translate("BaseSplitDataForReceiversPanelWidget", "The output folder is invalid. Choose a valid folder."))

    def update_progress(self, progress):
        self.prb_export_field_data.setValue(progress)
class JavaUtils(QObject):
    download_java_completed = pyqtSignal()
    download_java_progress_changed = pyqtSignal(int)  # progress

    JAVA_NAME = 'java.exe' if platform.system() == 'Windows' else 'java'

    def __init__(self):
        QObject.__init__(self)
        self.logger = Logger()
        self._downloading = False
        self._show_cursor = True

    def get_java_on_demand(self):
        # Create required directories
        Path(DEPENDENCIES_BASE_PATH).mkdir(parents=True, exist_ok=True)

        # Remove previous Java if any
        custom_java_dir_path = os.path.join(
            DEPENDENCIES_BASE_PATH, DICT_JAVA_DIR_NAME[KEY_JAVA_OS_VERSION])
        if os.path.exists(custom_java_dir_path):
            shutil.rmtree(custom_java_dir_path, ignore_errors=True)

        url_java = DICT_JAVA_DOWNLOAD_URL[KEY_JAVA_OS_VERSION]
        self.download_java_dependency(url_java)

    def download_java_dependency(self, uri):
        self.logger.clear_message_bar()

        if not self._downloading:  # Already downloading report dependency?
            if QGISUtils.is_connected(TEST_SERVER):
                self._downloading = True
                fetcher_task = QgsNetworkContentFetcherTask(QUrl(uri))
                fetcher_task.begun.connect(self.task_begun)
                fetcher_task.progressChanged.connect(
                    self.task_progress_changed)
                fetcher_task.fetched.connect(
                    functools.partial(self.save_java_dependency_file,
                                      fetcher_task))
                fetcher_task.taskCompleted.connect(self.task_completed)
                QgsApplication.taskManager().addTask(fetcher_task)
            else:
                self.logger.warning_msg(
                    __name__,
                    QCoreApplication.translate(
                        "JavaUtils",
                        "There was a problem connecting to Internet."))
                self._downloading = False

    def task_begun(self):
        if self._show_cursor:
            QApplication.setOverrideCursor(Qt.WaitCursor)

    def task_progress_changed(self, progress):
        self.download_java_progress_changed.emit(progress)

    def task_completed(self):
        if self._show_cursor:
            QApplication.restoreOverrideCursor()
        self.download_java_completed.emit()

    def save_java_dependency_file(self, fetcher_task):
        if fetcher_task.reply() is not None:
            # Write response to tmp file
            tmp_file = tempfile.mktemp()
            out_file = QFile(tmp_file)
            out_file.open(QIODevice.WriteOnly)
            out_file.write(fetcher_task.reply().readAll())
            out_file.close()

            if not os.path.exists(DEPENDENCIES_BASE_PATH):
                os.makedirs(DEPENDENCIES_BASE_PATH)

            if md5sum(tmp_file) == DICT_JAVA_MD5SUM[KEY_JAVA_OS_VERSION]:

                try:
                    tar = tarfile.open(tmp_file)
                    tar.extractall(DEPENDENCIES_BASE_PATH)
                    tar.close()
                except tarfile.ReadError as e:
                    self.logger.warning_msg(
                        __name__,
                        QCoreApplication.translate(
                            "JavaUtils",
                            "There was an error with the download. The downloaded file is invalid."
                        ))
                except PermissionError as e:
                    self.logger.warning_msg(
                        __name__,
                        QCoreApplication.translate(
                            "JavaUtils",
                            "Java couldn't be installed. Check if it is possible to write into this folder: <a href='file:///{path}'>{path}</a>"
                        ).format(path=normalize_local_url(
                            os.path.join(DEPENDENCIES_BASE_PATH),
                            DICT_JAVA_DIR_NAME[KEY_JAVA_OS_VERSION])))

            else:
                self.logger.warning_msg(
                    __name__,
                    QCoreApplication.translate(
                        "JavaUtils",
                        "There was an error with the download. The downloaded file is invalid."
                    ))
            try:
                os.remove(tmp_file)
            except:
                pass

        self._downloading = False

    def set_java_home(self):
        """
        Attempt to set a valid JAVA_HOME only for the current session, which is used by reports (MapFish).
        First try with the system JAVA_HOME, if not present, try with the Java configured in Model Baker.
        If not, return False.
        :return: Whether a proper Java could be set or not in the current session's JAVA_HOME
        """
        java_name = 'java.exe' if sys.platform == 'win32' else 'java'
        pattern_java = "bin{}java".format(os.sep)

        # Get JAVA_HOME environment variable
        if 'JAVA_HOME' in os.environ:
            java_home = os.environ['JAVA_HOME']

            java_exe = os.path.join(java_home, 'bin', java_name)
            is_valid, java_message = JavaUtils.java_path_is_valid(java_exe)

            if not is_valid:
                # Another try: does JAVA_HOME include bin dir?
                java_exe = os.path.join(java_home, java_name)
                is_valid, java_message = JavaUtils.java_path_is_valid(java_exe)

                if is_valid:
                    os.environ['JAVA_HOME'] = java_exe.split(pattern_java)[0]
                    return True
            else:
                os.environ['JAVA_HOME'] = java_exe.split(pattern_java)[0]
                # JAVA_HOME is valid, we'll use it as it is!
                return True
        else:
            java_exe = which(java_name)
            if java_exe:
                # verifies that the java configured in the system is valid.
                is_valid, java_message = JavaUtils.java_path_is_valid(java_exe)
                if is_valid:
                    os.environ['JAVA_HOME'] = java_exe.split(pattern_java)[0]
                    return True

        # If JAVA_HOME environment variable doesn't exist
        # We use the value defined in QgisModelBaker
        java_exe = JavaUtils.get_java_path_from_qgis_model_baker()

        if java_exe:
            is_valid, java_message = JavaUtils.java_path_is_valid(java_exe)
            if is_valid:
                os.environ['JAVA_HOME'] = java_exe.split(pattern_java)[0]
                return True

        # If JAVA_HOME environment variable doesn't exist
        # If JAVA_HOME is no defined in QgisModelBaker

        # Check if Java was installed previously by this plugin
        full_path_java = os.path.join(DEPENDENCIES_BASE_PATH,
                                      DICT_JAVA_DIR_NAME[KEY_JAVA_OS_VERSION],
                                      'bin', JavaUtils.JAVA_NAME)
        is_valid, java_message = JavaUtils.java_path_is_valid(full_path_java)

        if is_valid:
            # Java was downloaded previously, so use it!
            os.environ['JAVA_HOME'] = full_path_java.split(pattern_java)[0]
            return True

        return False

    @staticmethod
    def get_full_java_exe_path():
        java_name = 'java.exe' if sys.platform == 'win32' else 'java'
        full_java_exe_path = ""

        # Get JAVA_HOME environment variable
        if 'JAVA_HOME' in os.environ:
            java_home = os.environ['JAVA_HOME']

            full_java_exe_path = os.path.join(java_home, 'bin', java_name)
            is_valid, java_message = JavaUtils.java_path_is_valid(
                full_java_exe_path)

            if not is_valid:
                # Another try: does JAVA_HOME include bin dir?
                full_java_exe_path = os.path.join(java_home, java_name)
                is_valid, java_message = JavaUtils.java_path_is_valid(
                    full_java_exe_path)
                if not is_valid:
                    full_java_exe_path = ""
        else:
            full_java_exe_path = which(java_name)
            # verifies that the java configured in the system is valid.
            is_valid, java_message = JavaUtils.java_path_is_valid(
                full_java_exe_path)
            if not is_valid:
                full_java_exe_path = ""

        return full_java_exe_path

    @staticmethod
    def java_path_is_valid(java_path):
        """
        Check if java path exists
        :param java_path: (str) java path to validate
        :return: (bool, str)  True if java Path is valid, False otherwise
        """
        try:
            if os.name == 'nt':
                java_path = JavaUtils.normalize_java_path(java_path)

            procs_message = subprocess.check_output(
                [java_path, '-version'],
                stderr=subprocess.STDOUT).decode('utf8').lower()
            types_java = ['jre', 'java', 'jdk']

            if procs_message:
                if any(type_java in procs_message for type_java in types_java):
                    pattern = '\"(\d+\.\d+).*\"'
                    java_version = re.search(pattern,
                                             procs_message).groups()[0]

                    if java_version:
                        if float(java_version) == JAVA_REQUIRED_VERSION:
                            return (
                                True,
                                QCoreApplication.translate(
                                    "JavaPath",
                                    "Java path has been configured correctly.")
                            )
                        else:
                            return (
                                False,
                                QCoreApplication.translate(
                                    "JavaPath",
                                    "Java version is not valid. Current version is {}, but must be {}."
                                ).format(java_version, JAVA_REQUIRED_VERSION))

                    return (
                        False,
                        QCoreApplication.translate(
                            "JavaPath",
                            "Java exists but it is not possible to know and validate its version."
                        ))
                else:
                    return (
                        False,
                        QCoreApplication.translate(
                            "JavaPath",
                            "Java path is not valid, please select a valid path..."
                        ))
            else:
                return (
                    False,
                    QCoreApplication.translate(
                        "JavaPath",
                        "Java path is not valid, please select a valid path..."
                    ))
        except Exception as e:
            return (
                False,
                QCoreApplication.translate(
                    "JavaPath",
                    "Java path is not valid, please select a valid path..."))

    @staticmethod
    def normalize_java_path(java_path):
        escape_characters = [('\a', '\\a'), ('\b', '\\b'), ('\f', '\\f'),
                             ('\n', '\\n'), ('\r', '\\r'), ('\t', '\\t'),
                             ('\v', '\\v')]
        for escape_character in escape_characters:
            java_path = java_path.replace(escape_character[0],
                                          escape_character[1])
        return java_path

    @staticmethod
    def get_java_path_from_qgis_model_baker():
        return QSettings().value('QgisModelBaker/ili2db/JavaPath', '', str)