class SuppliesETLWizard(QWizard, WIZARD_UI):
    on_result = pyqtSignal(
        bool)  # whether the tool was run successfully or not

    def __init__(self, db, conn_manager, parent=None):
        QWizard.__init__(self, parent)
        self.setupUi(self)
        self._db = db
        self.conn_manager = conn_manager
        self.parent = parent

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

        self.names = self._db.names
        self.help_strings = HelpStrings()
        self._data_source_widget = None
        self.db_source = SUPPLIES_DB_SOURCE
        self.tool_name = ""
        self._running_tool = False
        self._db_was_changed = False  # To postpone calling refresh gui until we close this dialog instead of settings
        self.progress_configuration(0, 1)  # start from: 0, number of steps: 1

        self.wizardPage2.setButtonText(
            QWizard.CustomButton1,
            QCoreApplication.translate("SuppliesETLWizard", "Run ETL"))
        self.wizardPage1.setButtonText(
            QWizard.CancelButton,
            QCoreApplication.translate("SuppliesETLWizard", "Close"))
        self.wizardPage2.setButtonText(
            QWizard.CancelButton,
            QCoreApplication.translate("SuppliesETLWizard", "Close"))

        # Auxiliary data to set nonlinear next pages
        self.pages = [self.wizardPage1, self.wizardPage2, self.wizardPage3]
        self.dict_pages_ids = {
            self.pages[idx]: pid
            for idx, pid in enumerate(self.pageIds())
        }

        # Set connections
        self.rad_snc_data.toggled.connect(self.etl_option_changed)
        self.etl_option_changed()  # Initialize it
        self.button(QWizard.CustomButton1).clicked.connect(
            self.import_button_clicked)
        self.button(QWizard.HelpButton).clicked.connect(self.show_help)
        self.currentIdChanged.connect(self.current_page_changed)
        self.finished.connect(self.finished_slot)
        self.btn_browse_connection.clicked.connect(self.show_settings)

        # Initialize
        self.current_page_changed(1)
        self.update_connection_info()
        self.restore_settings()
        self.initialize_feedback()

        # Set MessageBar for QWizard
        self.bar = QgsMessageBar()
        self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        self.setLayout(QGridLayout())
        self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop)

    def current_page_changed(self, id):
        """
        Reset the Next button. Needed because Next might have been disabled by a
        condition in a another SLOT.
        """
        #enable_next_wizard(self)
        button_list = [
            QWizard.HelpButton, QWizard.Stretch, QWizard.BackButton,
            QWizard.CustomButton1, QWizard.NextButton, QWizard.FinishButton,
            QWizard.CancelButton
        ]
        not_visible = []

        if id == self.dict_pages_ids[self.wizardPage1]:
            self.setWindowTitle(
                QCoreApplication.translate("SuppliesETLWizard",
                                           "Run supplies ETL"))
            button_list.remove(QWizard.BackButton)
            button_list.remove(QWizard.CustomButton1)
            button_list.remove(QWizard.FinishButton)
        elif id == self.dict_pages_ids[self.wizardPage2]:
            button_list.remove(QWizard.FinishButton)
            not_visible.append(self.NextButton)
            self.load_data_source_controls()
            if self.rad_snc_data.isChecked():
                self.setWindowTitle(
                    QCoreApplication.translate("SuppliesETLWizard",
                                               "ETL: SNC to Supplies model"))
            else:
                self.setWindowTitle(
                    QCoreApplication.translate("SuppliesETLWizard",
                                               "ETL: Cobol to Supplies model"))
        elif id == self.dict_pages_ids[self.wizardPage3]:
            self.bar.clearWidgets()
            button_list.remove(QWizard.CustomButton1)
            button_list.remove(QWizard.NextButton)
            button_list.remove(QWizard.BackButton)
            self.wizardPage3.setFinalPage(True)

        self.setButtonLayout(button_list)
        for button in not_visible:
            self.button(button).setVisible(False)

    def etl_option_changed(self):
        """
        Adjust help, names and titles according to the selected option
        """
        if self.rad_snc_data.isChecked():
            self.tool_name = QCoreApplication.translate(
                "SuppliesETLWizard", "ETL-SNC")
            self.txt_help_page_2.setHtml(
                self.help_strings.WIZ_SUPPLIES_ETL_PAGE_2.format("del SNC"))
        elif self.rad_cobol_data.isChecked():  # self.rad_cobol_data is checked
            self.tool_name = QCoreApplication.translate(
                "SuppliesETLWizard", "ETL-Cobol")
            self.txt_help_page_2.setHtml(
                self.help_strings.WIZ_SUPPLIES_ETL_PAGE_2.format("de Cobol"))

    def load_data_source_controls(self):
        self.clear_data_source_widget()
        if self.rad_snc_data.isChecked():
            self._data_source_widget = SNCDataSourceWidget()
        else:  # Cobol
            self._data_source_widget = CobolDataSourceWidget()

        self._data_source_widget.input_data_changed.connect(
            self.set_import_button_enabled)
        self._data_source_widget.emit_input_data_changed(
        )  # Initialize input validation
        self.data_source_layout.addWidget(self._data_source_widget)
        self._data_source_widget.setVisible(True)

    def clear_data_source_widget(self):
        while self.data_source_layout.count():
            child = self.data_source_layout.takeAt(0)
            if child.widget():
                child.widget().setVisible(False)

    def initialize_feedback(self):
        self.progress.setValue(0)
        self.progress.setVisible(False)
        self.custom_feedback = CustomFeedback()
        self.custom_feedback.progressChanged.connect(self.progress_changed)
        self.set_gui_controls_enabled(True)

    def progress_configuration(self, base, num_process):
        """
        :param base: Where to start counting from
        :param num_process: Number of steps
        """
        self.progress_base = base
        self.progress_maximum = 100 * num_process
        self.progress.setMaximum(self.progress_maximum)

    def progress_changed(self):
        QCoreApplication.processEvents()  # Listen to cancel from the user
        self.progress.setValue(self.progress_base +
                               self.custom_feedback.progress())

    def set_gui_controls_enabled(self, enable):
        self.gbx_data_source.setEnabled(enable)
        self.target_data.setEnabled(enable)
        self.set_import_button_enabled(enable)

    def set_import_button_enabled(self, enable):
        self.button(QWizard.CustomButton1).setEnabled(enable)

    def import_button_clicked(self):
        self.bar.clearWidgets()
        self.save_settings()
        etl_result = False

        if self.rad_snc_data.isChecked():
            etl = ETLSNC(self.names, self._data_source_widget)
        else:  # Cobol
            etl = ETLCobol(self.names, self._data_source_widget)

        if self._db.test_connection()[0]:
            reply = QMessageBox.question(
                self, QCoreApplication.translate("SuppliesETLWizard",
                                                 "Warning"),
                QCoreApplication.translate(
                    "SuppliesETLWizard",
                    "The database <i>{}</i> already has a valid LADM-COL structure.<br/><br/>If such database has any data, loading data into it might cause invalid data.<br/><br/>Do you still want to continue?"
                ).format(self._db.get_description_conn_string()),
                QMessageBox.Yes, QMessageBox.No)

            if reply == QMessageBox.Yes:
                self.set_gui_controls_enabled(False)
                self.button(self.BackButton).setEnabled(False)
                self.button(self.CustomButton1).setEnabled(False)
                self.button(self.CancelButton).setText(
                    QCoreApplication.translate("SuppliesETLWizard", "Cancel"))

                with OverrideCursor(Qt.WaitCursor):
                    res_alpha, msg_alpha = etl.load_alphanumeric_layers()

                    if res_alpha:
                        res_spatial, msg_spatial = etl.load_spatial_layers()

                        if res_spatial:
                            res_model, msg_model = self.load_model_layers(
                                etl.layers)

                            if res_model:
                                layers_feature_count_before = {
                                    name: layer.featureCount()
                                    for name, layer in etl.layers.items()
                                }
                                self._running_tool = True
                                self.progress.setVisible(True)
                                res_etl_model = etl.run_etl_model(
                                    self.custom_feedback)
                                if not self.custom_feedback.isCanceled(
                                ) and res_etl_model:
                                    self.progress.setValue(100)

                                    self.button(
                                        self.NextButton).setVisible(True)
                                    self.button(
                                        self.CustomButton1).setVisible(False)
                                    self.button(self.CancelButton).setText(
                                        QCoreApplication.translate(
                                            "SuppliesETLWizard", "Close"))
                                    self.show_message(
                                        QCoreApplication.translate(
                                            "SuppliesETLWizard",
                                            "The {} has finished successfully!"
                                        ).format(self.tool_name), Qgis.Success,
                                        0)

                                    self.logger.clear_status()
                                    self.fill_summary(
                                        layers_feature_count_before,
                                        etl.layers)
                                    etl_result = True
                                else:
                                    self.initialize_feedback(
                                    )  # Get ready for an eventual new execution
                                    self.logger.clear_status()
                                self._running_tool = False
                            else:
                                self.show_message(msg_model, Qgis.Warning)
                        else:
                            self.show_message(msg_spatial, Qgis.Warning)
                    else:
                        self.show_message(msg_alpha, Qgis.Warning)
        else:
            with OverrideCursor(Qt.WaitCursor):
                # TODO: if an empty schema was selected, do the magic under the hood
                # self.create_model_into_database()
                # Now execute "accepted()"
                msg = QCoreApplication.translate(
                    "SuppliesETLWizard",
                    "To run the ETL, the database (schema) should have the Supplies LADM-COL structure. Choose a proper database (schema) and try again."
                )
                self.show_message(msg, Qgis.Warning)
                self.logger.warning(__name__, msg)

        self.on_result.emit(
            etl_result)  # Inform other classes if the execution was successful

    def reject(self):
        if self._running_tool:
            reply = QMessageBox.question(
                self, QCoreApplication.translate("SuppliesETLWizard",
                                                 "Warning"),
                QCoreApplication.translate(
                    "SuppliesETLWizard",
                    "The '{}' tool is still running. Do you want to cancel it? If you cancel, the data might be incomplete in the target database."
                ).format(self.tool_name), QMessageBox.Yes, QMessageBox.No)

            if reply == QMessageBox.Yes:
                self.custom_feedback.cancel()
                self._running_tool = False
                msg = QCoreApplication.translate(
                    "SuppliesETLWizard",
                    "The '{}' tool was cancelled.").format(self.tool_name)
                self.logger.info(__name__, msg)
                self.show_message(msg, Qgis.Info)
        else:
            if self._db_was_changed:
                self.conn_manager.db_connection_changed.emit(
                    self._db,
                    self._db.test_connection()[0], self.db_source)
            self.logger.info(__name__, "Dialog closed.")
            self.app.settings.set_setting(
                self.app.settings.COBOL_FILES_DIR_KEY, '')
            self.app.settings.set_setting(self.app.settings.SNC_FILES_DIR_KEY,
                                          '')
            self.done(1)

    def finished_slot(self, result):
        self.bar.clearWidgets()

    def show_message(self, message, level, duration=10):
        self.bar.clearWidgets(
        )  # Remove previous messages before showing a new one
        self.bar.pushMessage(message, level, duration)

    def fill_summary(self, layers_feature_count_before, etl_layers):
        layers_feature_count_after = {
            name: layer.featureCount()
            for name, layer in etl_layers.items()
        }
        summary = """<html><head/><body><p>"""
        summary += QCoreApplication.translate(
            "SuppliesETLWizard", "<h4>{} report</h4>").format(self.tool_name)
        summary += QCoreApplication.translate(
            "SuppliesETLWizard",
            "Number of features loaded to the LADM-COL cadastral supplies model:<br/>"
        )

        for name, before_count in layers_feature_count_before.items():
            summary += QCoreApplication.translate(
                "SuppliesETLWizard", '<br/><b>{}</b> : {}'.format(
                    name, layers_feature_count_after[name] - before_count))

        summary += """<hr>"""
        summary += """</body></html>"""
        self.txt_log.setText(summary)

    def save_settings(self):
        settings = QSettings()
        etl_source = "snc"
        if self.rad_snc_data.isChecked():
            etl_source = "snc"
        elif self.rad_cobol_data.isChecked():
            etl_source = "cobol"

        settings.setValue('Asistente-LADM-COL/supplies/etl_source', etl_source)
        self._data_source_widget.save_settings()

        # In the main page (source-target configuration), save if splitter is closed
        self.app.settings.etl_splitter_collapsed = self.splitter_2.sizes(
        )[1] == 0

    def restore_settings(self):
        settings = QSettings()
        etl_source = settings.value(
            'Asistente-LADM-COL/supplies/etl_source') or 'snc'
        if etl_source == 'snc':
            self.rad_snc_data.setChecked(True)
        elif etl_source == 'cobol':
            self.rad_cobol_data.setChecked(True)

        # If splitter in the main page was closed before, set it as closed again
        if self.app.settings.etl_splitter_collapsed:
            sizes = self.splitter_2.sizes()
            self.splitter_2.setSizes([sizes[0], 0])

    def show_help(self):
        show_plugin_help('supplies')

    def show_settings(self):
        dlg = SettingsDialog(self.conn_manager, parent=self)
        dlg.setWindowTitle(
            QCoreApplication.translate("SuppliesETLWizard",
                                       "Target DB Connection Settings"))
        dlg.show_tip(
            QCoreApplication.translate(
                "SuppliesETLWizard",
                "Configure where do you want the data to be imported."))
        dlg.set_db_source(self.db_source)

        dlg.set_required_models([LADMNames.SUPPLIES_MODEL_KEY])
        dlg.set_tab_pages_list(
            [SETTINGS_CONNECTION_TAB_INDEX, SETTINGS_MODELS_TAB_INDEX])
        dlg.set_action_type(EnumDbActionType.IMPORT_FROM_ETL)

        dlg.db_connection_changed.connect(self.db_connection_changed)
        if self.db_source == COLLECTED_DB_SOURCE:
            dlg.db_connection_changed.connect(
                self.app.core.cache_layers_and_relations)

        if dlg.exec_():
            self._db = dlg.get_db_connection()
            self.update_connection_info()

    def update_connection_info(self):
        db_description = self._db.get_description_conn_string()
        if db_description:
            self.db_connect_label.setText(db_description)
            self.db_connect_label.setToolTip(
                self._db.get_display_conn_string())
        else:
            self.db_connect_label.setText(
                QCoreApplication.translate("SuppliesETLWizard",
                                           "The database is not defined!"))
            self.db_connect_label.setToolTip('')

    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 supplies dialog (e.g., we might run an import schema before under the hood)
        self._db_was_changed = True

    def load_model_layers(self, layers):
        self.app.core.get_layers(self._db, layers, load=True)
        if not layers:
            return False, QCoreApplication.translate(
                "SuppliesETLWizard",
                "There was a problem loading layers from the 'Supplies' model!"
            )

        return True, ''
Example #2
0
class Ili2DB(QObject):
    """
    Execute ili2db operations via Model Baker API
    """
    stdout = pyqtSignal(str)
    stderr = pyqtSignal(str)
    process_started = pyqtSignal(str)
    process_finished = pyqtSignal(int, int)

    def __init__(self):
        QObject.__init__(self)

        self.logger = Logger()

        self._java_path = ''
        self._java_dependency = JavaDependency()

        self.dbs_supported = ConfigDBsSupported()

        self._base_configuration = None
        self._ilicache = None
        self._log = ''

    def get_full_java_exe_path(self):
        if not self._java_path:
            self._java_path = JavaDependency.get_full_java_exe_path()

        return self._java_path

    def configure_java(self):
        if not self._java_dependency.set_java_home():
            message_java = QCoreApplication.translate(
                "Ili2DB",
                """Configuring Java {}...""").format(JAVA_REQUIRED_VERSION)
            self.logger.status(message_java)
            self._java_dependency.get_java_on_demand(asynchronous=False)

        res = True
        msg = 'Success!'

        java_path = self.get_full_java_exe_path()
        if not java_path:
            res = False
            msg = QCoreApplication.translate(
                "Ili2DB",
                "Java {} could not be confiured for you. You can configure the JAVA_HOME environment variable manually, restart QGIS and try again."
            ).format(JAVA_REQUIRED_VERSION)

        return res, msg

    def _get_base_configuration(self):
        """
        :return: BaseConfiguration object. If it's already configured, it returns the existing object, so that it can
                 be shared among chained operations (e.g., export DB1-->schema import DB2-->import DB2).
        """
        if not self._base_configuration:
            self._base_configuration = BaseConfiguration()
            self._ilicache = IliCache(self._base_configuration)

            self._base_configuration.java_path = self.get_full_java_exe_path(
            )  # It is well configured at this point!

            # Check custom model directories
            if QSettings().value(
                    'Asistente-LADM-COL/models/custom_model_directories_is_checked',
                    DEFAULT_USE_CUSTOM_MODELS,
                    type=bool):
                custom_model_directories = QSettings().value(
                    'Asistente-LADM-COL/models/custom_models',
                    DEFAULT_MODELS_DIR)
                if not custom_model_directories:
                    self._base_configuration.custom_model_directories_enabled = False
                else:
                    self._base_configuration.custom_model_directories = custom_model_directories
                    self._base_configuration.custom_model_directories_enabled = True

            # Debug mode
            self._base_configuration.debugging_enabled = \
                QSettings().value('Asistente-LADM-COL/models/debug', DEFAULT_ILI2DB_DEBUG_MODE, type=bool)

            self._base_configuration.logfile_path = QSettings().value(
                'Asistente-LADM-COL/models/log_file_path', '')

            self._ilicache.refresh(
            )  # Always call it after setting custom_model_directories

        return self._base_configuration

    def _get_ili_models(self, db):
        ili_models = list()
        model_names = db.get_models()
        if model_names:
            for model in LADMColModelRegistry().supported_models():
                if not model.hidden() and model.full_name() in model_names:
                    ili_models.append(model.full_name())

        return ili_models

    def get_import_schema_configuration(self,
                                        db,
                                        ili_models=list(),
                                        create_basket_col=False):
        db_factory = self.dbs_supported.get_db_factory(db.engine)

        configuration = SchemaImportConfiguration()
        db_factory.set_ili2db_configuration_params(db.dict_conn_params,
                                                   configuration)
        configuration.inheritance = ILI2DBNames.DEFAULT_INHERITANCE
        configuration.create_basket_col = create_basket_col
        configuration.create_import_tid = ILI2DBNames.CREATE_IMPORT_TID
        configuration.stroke_arcs = ILI2DBNames.STROKE_ARCS
        configuration.tomlfile = TOML_FILE_DIR

        configuration.base_configuration = self._get_base_configuration()
        configuration.ilimodels = ';'.join(ili_models)

        if db.engine == 'gpkg':
            # EPSG:9377 support for GPKG (Ugly, I know) We need to send known parameters, we'll fix this in the post_script
            configuration.srs_auth = 'EPSG'
            configuration.srs_code = 3116
            configuration.post_script = CTM12_GPKG_SCRIPT_PATH
        elif db.engine == 'pg':
            configuration.srs_auth = 'EPSG'
            configuration.srs_code = 9377
            configuration.pre_script = CTM12_PG_SCRIPT_PATH

        return configuration

    def get_import_data_configuration(self,
                                      db,
                                      xtf_path,
                                      dataset='',
                                      baskets=list(),
                                      disable_validation=False):
        db_factory = self.dbs_supported.get_db_factory(db.engine)
        configuration = ImportDataConfiguration()
        db_factory.set_ili2db_configuration_params(db.dict_conn_params,
                                                   configuration)
        configuration.with_importtid = True
        configuration.xtffile = xtf_path
        configuration.disable_validation = disable_validation
        configuration.dataset = dataset
        configuration.baskets = baskets  # list with basket UUIDs

        configuration.base_configuration = self._get_base_configuration()
        ili_models = self._get_ili_models(db)
        if ili_models:
            configuration.ilimodels = ';'.join(ili_models)

        return configuration

    def get_export_configuration(self,
                                 db,
                                 xtf_path,
                                 dataset='',
                                 baskets=list(),
                                 disable_validation=False):
        db_factory = self.dbs_supported.get_db_factory(db.engine)
        configuration = ExportConfiguration()
        db_factory.set_ili2db_configuration_params(db.dict_conn_params,
                                                   configuration)
        configuration.with_exporttid = True
        configuration.xtffile = xtf_path
        configuration.disable_validation = disable_validation
        configuration.dataset = dataset
        configuration.baskets = baskets  # List with basket UUIDs

        configuration.base_configuration = self._get_base_configuration()
        ili_models = self._get_ili_models(db)
        if ili_models:
            configuration.ilimodels = ';'.join(ili_models)

        return configuration

    def get_update_configuration(self, db_factory, db, xtf_path, dataset_name):
        configuration = UpdateDataConfiguration()
        db_factory.set_ili2db_configuration_params(db.dict_conn_params,
                                                   configuration)

        configuration.base_configuration = self._get_base_configuration()
        ili_models = self._get_ili_models(db)
        if ili_models:
            configuration.ilimodels = ';'.join(ili_models)

        configuration.dataset = dataset_name
        configuration.with_importbid = True
        configuration.with_importtid = True
        configuration.xtffile = xtf_path

        return configuration

    def get_validate_configuration(self, db_factory, db, model_names,
                                   xtflog_path, configfile_path):
        configuration = ValidateDataConfiguration()
        db_factory.set_ili2db_configuration_params(db.dict_conn_params,
                                                   configuration)

        configuration.base_configuration = self._get_base_configuration()
        # Since BaseConfiguration can be shared, avoid a --trace in --validate operation (not supported by ili2db)
        configuration.base_configuration.debugging_enabled = False

        if model_names:
            configuration.ilimodels = ';'.join(model_names)
        if xtflog_path:
            configuration.xtflogfile = xtflog_path
        if configfile_path:
            configuration.configfile = configfile_path

        return configuration

    @with_override_cursor
    def import_schema(self, db, configuration: SchemaImportConfiguration):
        # Check prerequisite
        if not self.get_full_java_exe_path():
            res_java, msg_java = self.configure_java()
            if not res_java:
                return res_java, msg_java

        # Configure command parameters
        db_factory = self.dbs_supported.get_db_factory(db.engine)

        # Configure run
        importer = iliimporter.Importer()
        importer.tool = db_factory.get_model_baker_db_ili_mode()
        importer.configuration = configuration

        self._connect_ili_executable_signals(importer)

        # Run!
        res = True
        msg = QCoreApplication.translate("Ili2DB",
                                         "Schema import ran successfully!")
        self._log = ''
        self.logger.status(
            QCoreApplication.translate(
                "Ili2Db", "Creating LADM-COL structure into {}...").format(
                    db.engine.upper()))
        try:
            if importer.run() != iliimporter.Importer.SUCCESS:
                msg = QCoreApplication.translate(
                    "Ili2DB",
                    "An error occurred when importing a schema into a DB (check the QGIS log panel)."
                )
                res = False
                QgsMessageLog.logMessage(
                    self._log,
                    QCoreApplication.translate("Ili2DB", "DB Schema Import"),
                    Qgis.Critical)

        except JavaNotFoundError:
            msg = QCoreApplication.translate(
                "Ili2DB",
                "Java {} could not be found. You can configure the JAVA_HOME environment variable manually, restart QGIS and try again."
            ).format(JAVA_REQUIRED_VERSION)
            res = False

        self._disconnect_ili_executable_signals(importer)
        self.logger.clear_status()
        return res, msg

    @with_override_cursor
    def import_data(self, db, configuration: ImportDataConfiguration):
        # Check prerequisite
        if not self.get_full_java_exe_path():
            res_java, msg_java = self.configure_java()
            if not res_java:
                return res_java, msg_java

        # Configure command parameters
        db_factory = self.dbs_supported.get_db_factory(db.engine)

        # Configure run
        importer = iliimporter.Importer(dataImport=True)
        importer.tool = db_factory.get_model_baker_db_ili_mode()
        importer.configuration = configuration

        self._connect_ili_executable_signals(importer)

        # Run!
        res = True
        msg = QCoreApplication.translate(
            "Ili2DB",
            "XTF '{}' imported successfully!").format(configuration.xtffile)
        self._log = ''
        self.logger.status(
            QCoreApplication.translate("Ili2Db",
                                       "Importing XTF into {}...").format(
                                           db.engine.upper()))
        try:
            if importer.run() != iliimporter.Importer.SUCCESS:
                msg = QCoreApplication.translate(
                    "Ili2DB",
                    "An error occurred when importing from XTF (check the QGIS log panel)."
                )
                res = False
                QgsMessageLog.logMessage(
                    self._log,
                    QCoreApplication.translate("Ili2DB", "Import from XTF"),
                    Qgis.Critical)

        except JavaNotFoundError:
            msg = QCoreApplication.translate(
                "Ili2DB",
                "Java {} could not be found. You can configure the JAVA_HOME environment variable manually, restart QGIS and try again."
            ).format(JAVA_REQUIRED_VERSION)
            res = False

        self._disconnect_ili_executable_signals(importer)
        self.logger.clear_status()
        return res, msg

    @with_override_cursor
    def export(self, db, configuration: ExportConfiguration):
        # Check prerequisite
        if not self.get_full_java_exe_path():
            res_java, msg_java = self.configure_java()
            if not res_java:
                return res_java, msg_java

        # Configure command parameters
        db_factory = self.dbs_supported.get_db_factory(db.engine)

        # Configure run
        exporter = iliexporter.Exporter()
        exporter.tool = db_factory.get_model_baker_db_ili_mode()
        exporter.configuration = configuration

        self._connect_ili_executable_signals(exporter)

        # Run!
        res = True
        msg = QCoreApplication.translate(
            "Ili2DB",
            "XTF '{}' exported successfully!").format(configuration.xtffile)
        self._log = ''
        self.logger.status(
            QCoreApplication.translate("Ili2Db",
                                       "Exporting from {} to XTF...").format(
                                           db.engine.upper()))
        try:
            if exporter.run() != iliexporter.Exporter.SUCCESS:
                msg = QCoreApplication.translate(
                    "Ili2DB",
                    "An error occurred when exporting data to XTF (check the QGIS log panel)."
                )
                res = False
                QgsMessageLog.logMessage(
                    self._log,
                    QCoreApplication.translate("Ili2DB", "Export to XTF"),
                    Qgis.Critical)
            else:
                self.logger.info(__name__, msg)

        except JavaNotFoundError:
            msg = QCoreApplication.translate(
                "Ili2DB",
                "Java {} could not be found. You can configure the JAVA_HOME environment variable manually, restart QGIS and try again."
            ).format(JAVA_REQUIRED_VERSION)
            res = False

        self._disconnect_ili_executable_signals(exporter)
        self.logger.clear_status()
        return res, msg

    @with_override_cursor
    def update(self, db, xtf_path, dataset_name):
        # Check prerequisite
        if not self.get_full_java_exe_path():
            res_java, msg_java = self.configure_java()
            if not res_java:
                return res_java, msg_java

        # Configure command parameters
        db_factory = self.dbs_supported.get_db_factory(db.engine)
        configuration = self.get_update_configuration(db_factory, db, xtf_path,
                                                      dataset_name)

        # Configure run
        updater = iliupdater.Updater()
        updater.tool = db_factory.get_model_baker_db_ili_mode()
        updater.configuration = configuration

        self._connect_ili_executable_signals(updater)

        # Run!
        res = True
        msg = QCoreApplication.translate(
            "Ili2DB",
            "DB updated successfully from XTF file '{}'!").format(xtf_path)
        self._log = ''
        self.logger.status(
            QCoreApplication.translate(
                "Ili2Db", "Updating {} DB from XTF '{}'...").format(
                    db.engine.upper(), xtf_path))
        try:
            if updater.run() != iliupdater.Updater.SUCCESS:
                msg = QCoreApplication.translate(
                    "Ili2DB",
                    "An error occurred when updating the DB from an XTF (check the QGIS log panel)."
                )
                res = False
                QgsMessageLog.logMessage(
                    self._log,
                    QCoreApplication.translate("Ili2DB", "Update DB from XTF"),
                    Qgis.Critical)
            else:
                self.logger.info(__name__, msg)

        except JavaNotFoundError:
            msg = QCoreApplication.translate(
                "Ili2DB",
                "Java {} could not be found. You can configure the JAVA_HOME environment variable manually, restart QGIS and try again."
            ).format(JAVA_REQUIRED_VERSION)
            res = False

        self._disconnect_ili_executable_signals(updater)
        self.logger.clear_status()
        return res, msg

    @with_override_cursor
    def validate(self,
                 db,
                 model_names=list(),
                 xtflog_path='',
                 configfile_path=''):
        # Check prerequisite
        if not self.get_full_java_exe_path():
            res_java, msg_java = self.configure_java()
            if not res_java:
                return res_java, msg_java

        # Configure command parameters
        db_factory = self.dbs_supported.get_db_factory(db.engine)
        configuration = self.get_validate_configuration(
            db_factory, db, model_names, xtflog_path, configfile_path)

        # Configure run
        validator = ili2dbvalidator.Ili2DBValidator()
        validator.tool = db_factory.get_model_baker_db_ili_mode()
        validator.process_started.connect(self.on_process_started)
        validator.stderr.connect(self.on_stderr)
        validator.configuration = configuration

        # Run!
        res = True
        msg = QCoreApplication.translate(
            "Ili2DB", "Data successfully validated from DB '{}'!").format(
                db.get_description_conn_string())
        self._log = ''
        self.logger.status(
            QCoreApplication.translate(
                "Ili2Db", "Validating data from '{}' DB...").format(
                    db.get_description_conn_string()))
        try:
            res_validation = validator.run()
            if (res_validation != ili2dbvalidator.Ili2DBValidator.SUCCESS
                    and res_validation != ili2dbvalidator.Ili2DBValidator.
                    SUCCESS_WITH_VALIDATION_ERRORS):
                msg = QCoreApplication.translate(
                    "Ili2DB",
                    "An error occurred when validating data from a DB (check the QGIS log panel)."
                )
                res = False
                QgsMessageLog.logMessage(
                    self._log,
                    QCoreApplication.translate("Ili2DB",
                                               "Validate data from DB"),
                    Qgis.Critical)
            else:
                self.logger.info(__name__, msg)
        except JavaNotFoundError:
            msg = QCoreApplication.translate(
                "Ili2DB",
                "Java {} could not be found. You can configure the JAVA_HOME environment variable manually, restart QGIS and try again."
            ).format(JAVA_REQUIRED_VERSION)
            res = False

        self.logger.clear_status()
        return res, msg

    def on_process_started(self, command):
        self._log += command + '\n'

    def on_stderr(self, text):
        self._log += text

    def _connect_ili_executable_signals(self, ili_executable: IliExecutable):
        ili_executable.process_started.connect(self.process_started)
        ili_executable.stderr.connect(self.stderr)
        ili_executable.stdout.connect(self.stdout)
        ili_executable.process_finished.connect(self.process_finished)

        ili_executable.process_started.connect(self.on_process_started)
        ili_executable.stderr.connect(self.on_stderr)

    def _disconnect_ili_executable_signals(self,
                                           ili_executable: IliExecutable):
        ili_executable.process_started.disconnect(self.process_started)
        ili_executable.stderr.disconnect(self.stderr)
        ili_executable.stdout.disconnect(self.stdout)
        ili_executable.process_finished.disconnect(self.process_finished)

        ili_executable.process_started.disconnect(self.on_process_started)
        ili_executable.stderr.disconnect(self.on_stderr)