def install_packages():
        """
        installs  base packages via pip, e.g. packaging package
        :return: True if packages installed successfully else False
        """
        logger = QGISLogHandler("ModuleService")

        python = ModuleService.get_python()

        requirements = list("{}>={}".format(key, module_list[key]) for key in module_list.keys())
        cmd = [python, "-m", "pip", "install", "--force-reinstall", "--user", "--upgrade"] + requirements

        try:
            logger.info("Installing packages", ", ".join(requirements))
            logger.info("CMD: {}".format(" ".join(cmd)))

            result = run(cmd, stdout=PIPE, stderr=STDOUT, check=True)

            logger.info("installation result", result.stdout.decode())
            return True

        except CalledProcessError as e:
            logger.error("Package installation failed!")
            logger.error("RETURN-CODE: {} - CMD: {}".format(e.returncode, e.cmd))
            logger.error("OUTPUT: {}".format(e.output))
            return False
    def check_required_modules():
        """
        Check all module requirements
        :return: True is all modules with required versions were found, else false
        """
        logger = QGISLogHandler("ModuleService")

        # don't display logging to QGIS
        safed_iface = logger.qgis_iface
        logger.qgis_iface = None

        logger.info("Checking required modules")

        python = ModuleService.get_python()

        cmd = [python, "-m", "pip", "list", "--format", "json", "--user"]

        try:
            logger.info("Checking package versions")
            logger.info("CMD: {}".format(" ".join(cmd)))

            result = run(cmd, stdout=PIPE, stderr=STDOUT, check=True)
            logger.info("run pip info successful")
            packages = json.loads(result.stdout.decode())

        except CalledProcessError as e:
            # restore QGIS logging
            logger.qgis_iface = safed_iface
            logger.error("pip info request failed!")
            logger.error("RETURN-CODE: {} - CMD: {}".format(e.returncode, e.cmd))
            logger.error("OUTPUT: {}".format(e.output))
            return False

        ModuleService.modules = list()
        for module in module_list:
            module_found = False
            for package in packages:
                if package["name"] != module:
                    continue
                module_found = True

                logger.debug("found module {} [{}]".format(package["name"], package["version"]))

                v1 = version.parse(package["version"])
                v2 = version.parse(module_list[module])
                if v1 < v2:
                    logger.warn("Module version [{}] differs from required version [{}]".format(v1, v2))
                    ModuleService.modules.append("{}=={}".format(module, module_list[module]))

            if not module_found:
                logger.debug("Module not found, adding {}=={} to install list".format(module, module_list[module]))
                ModuleService.modules.append("{}=={}".format(module, module_list[module]))

        if len(ModuleService.modules) > 0:
            # restore QGIS logging
            logger.qgis_iface = safed_iface
            logger.info("Missing packages found: {}".format(ModuleService.modules))
            return False

        logger.info("All packages up to date")

        # restore QGIS logging
        logger.qgis_iface = safed_iface
        return True
Esempio n. 3
0
class DatabaseController(QObject):
    """
    Controller class for database interaction
    """
    def __init__(self, settings: SettingsDialog = None) -> None:
        """
        Constructor
        :param settings: settings dialog
        :return: nothing
        """
        QObject.__init__(self)

        self.logger = QGISLogHandler(DatabaseController.__name__)

        self.__db_service = DatabaseService.get_instance()
        self.__last_db_settings = dict()

        self.__settings = None
        self.__config = ConfigHandler()
        self.settings = settings

        db_type = self.__config.get("General", "db_type")
        if db_type != "" and db_type in ["SQLite", "PostgreSQL"]:
            self.settings.DB_type.setCurrentText(db_type)
        else:
            db_type = "SQLite"
            self.__config.set("General", "db_type", db_type)
            self.settings.DB_type.setCurrentText(db_type)

        self.__on_db_type_changed(db_type)

    #
    # slots
    #

    def __on_db_type_changed(self, db_type: str):
        """
        Slot called, when the database type was changed to show / hide specific elements
        :param db_type: selected database type
        :return: nothing
        :raises ValueError: if type is unknown
        """
        self.logger.debug("Selecting new database type: {}".format(db_type))
        if db_type == "SQLite":
            self.settings.create_DB_button.show()
            self.settings.select_DB_button.show()
            self.settings.password_label.hide()
            self.settings.password.hide()
            self.settings.username_label.hide()
            self.settings.username.hide()
            self.settings.save_password.hide()

            tempdir = "/tmp" if platform.system(
            ) == "Darwin" else tempfile.gettempdir()
            filename = "geology.sqlite"
            self.settings.database_connection.setPlaceholderText(
                os.path.join(tempdir, filename))

        elif db_type == "PostgreSQL":
            self.settings.create_DB_button.hide()
            self.settings.select_DB_button.hide()
            self.settings.password_label.show()
            self.settings.password.show()
            self.settings.username_label.show()
            self.settings.username.show()
            if found_keyring:
                self.settings.save_password.show()
            else:
                self.settings.save_password.hide()

            self.settings.database_connection.setPlaceholderText(
                "localhost:5432/geology")
            self.settings.username.setPlaceholderText("postgres")
            self.settings.password.setPlaceholderText("")
        else:
            self.settings.database_connection.setText("")
            self.settings.username.setText("")
            self.settings.password.setText("")
            self.settings.database_connection.setPlaceholderText("")
            self.settings.username.setPlaceholderText("")
            self.settings.password.setPlaceholderText("")
            raise ValueError("Unknown DB Format: {}".format(db_type))

        if self.__config.has_section(db_type):
            self.settings.database_connection.setText(
                self.__config.get(db_type, "connection"))
            self.settings.username.setText(
                self.__config.get(db_type, "username"))
            self.settings.password.setText("")

        else:
            self.settings.database_connection.setText("")
            self.settings.username.setText("")
            self.settings.password.setText("")

        self.__update_db_service()

    def __on_create_db_clicked(self) -> None:
        """
        slot for creating a new database
        :return: Nothing
        """

        self.__validate()
        # noinspection PyCallByClass,PyArgumentList
        filename = get_file_name(
            QFileDialog.getSaveFileName(
                parent=self.settings,
                caption="Select database file",
                directory="",
                filter="Databases(*.db *.sqlite *.data);;Any File Type (*)"))

        if filename != "":
            # noinspection PyTypeChecker
            if os.path.splitext(filename)[-1].lower().lstrip('.') not in [
                    "db", "data", "sqlite"
            ]:
                filename += ".data"
            self.settings.database_connection.setText(filename)

    def __on_select_db(self) -> None:
        """
        slot for selecting a sqlite database file and set the result to the related lineedit
        :return: Nothing
        """

        self.__validate()
        # noinspection PyCallByClass,PyArgumentList
        filename = get_file_name(
            QFileDialog.getOpenFileName(
                self.settings, "Select database file", "",
                "Databases(*.db *.sqlite *.data);;Any File Type (*)"))

        if filename != "":
            # noinspection PyTypeChecker
            if os.path.splitext(filename)[-1].lower().lstrip('.') not in [
                    "db", "data", "sqlite"
            ]:
                filename += ".data"
            self.settings.database_connection.setText(filename)

    def __on_check_connection(self):
        """
        Check the requested database connection
        :return: if the connection check was successful
        :raises ValueError: if database type is unknown
        """
        result = self.__db_service.check_connection()

        if result == "":
            self.logger.info("Connection test successful")
            return True

        else:
            self.logger.error("connection test failed", result)
            return False

    def __on_save(self):
        self.__last_db_settings = self.__get_db_settings()
        self.__update_db_service()
        if self.__on_check_connection():
            self.__update_config()
            self.settings.accept()
        else:
            self.__restore_db_settings(self.__last_db_settings)

    def __on_cancel(self):
        self.__on_db_type_changed(self.settings.DB_type.currentText())
        self.settings.reject()

    #
    # private functions
    #

    def __update_db_service(self, _: object = None) -> None:
        """
        Update the database service, if a GUI input element changed
        :param _: temporary parameter for QLineEdit update
        :return: Nothing
        """

        self.__db_service.db_type = self.settings.DB_type.currentText()
        self.__db_service.connection = self.settings.database_connection.text()
        self.__db_service.username = self.settings.username.text()
        self.__db_service.password = self.settings.password.text()

        if self.__db_service.connection == "":
            self.__db_service.connection = self.settings.database_connection.placeholderText(
            )
        if self.__db_service.username == "":
            self.__db_service.username = self.settings.username.placeholderText(
            )
        if self.__db_service.password == "" and found_keyring:
            # empty password ? try request from system keystore
            self.__db_service.password = keyring.get_password(
                "Postgres {}".format(self.__db_service.connection),
                self.__db_service.username)

        # self.logger.debug("Connection settings:\ndatabase:\t{}\nconnection:\t{}\nusername:\t{}\npassword:\t{}".format(
        #    self.__db_service.db_type, self.__db_service.connection,
        #    self.__db_service.username, self.__db_service.password))

    def __get_db_settings(self) -> Dict:
        """
        Returns the current database connection settings as dictionary
        :return: current database connection settings
        """
        return {
            "db": self.__db_service.db_type,
            "connection": self.__db_service.connection,
            "username": self.__db_service.username,
            "password": self.__db_service.password
        }

    def __restore_db_settings(self, values: Dict) -> None:
        """
        Restores the database connection settings
        :param values: dictionary with connection settings
        :return: Nothing
        """
        try:
            self.__db_service.db_type = values["db"]
            self.__db_service.connection = values["connection"]
            self.__db_service.username = values["username"]
            self.__db_service.password = values["password"]
        except KeyError as e:
            self.logger.error("Can't restore database settings: {}", str(e))

    def __update_config(self):
        db_type = self.settings.DB_type.currentText()

        if db_type == "PostgreSQL":
            self.__config.set("PostgreSQL", "connection",
                              self.__db_service.connection)
            self.__config.set("PostgreSQL", "username",
                              self.__db_service.username)

            if self.settings.save_password.isChecked() and found_keyring:
                keyring.set_password(
                    "Postgres {}".format(self.__db_service.connection),
                    self.__db_service.username, self.__db_service.password)

        elif db_type == "SQLite":
            self.__config.set("SQLite", "connection",
                              self.__db_service.connection)

        if db_type in ["PostgreSQL", "SQLite"]:
            self.__config.set("General", "db_type", db_type)
            self.__on_db_type_changed(self.__config.get("General", "db_type"))

    def __validate(self):
        """
        Validates, if the service can be executed
        :return: Nothing
        :raises
        """

        if self.settings is None:
            raise AttributeError("No settings dialog is set")

        if self.__config is None:
            raise AttributeError("No config is set")

    #
    # setter and getter
    #

    @property
    def settings(self) -> SettingsDialog:
        """
        Returns the currently active settings dialog
        :return: returns the currently active settings dialog
        """
        return self.__settings

    @settings.setter
    def settings(self, value: SettingsDialog) -> None:
        """
        Sets the currently active settings dialog
        :return: returns the currently active settings dialog
        :raises TypeError: if value is not of type SettingsDialog
        """

        if isinstance(value, SettingsDialog):
            if self.__settings is not None:
                self.__settings.create_DB_button.clicked.disconnect(
                    self.__on_create_db_clicked)
                self.__settings.select_DB_button.clicked.disconnect(
                    self.__on_select_db)
                self.__settings.DB_type.currentIndexChanged[str].disconnect(
                    self.__on_db_type_changed)
                self.__settings.save_button.clicked.disconnect(self.__on_save)
                self.__settings.cancel_button.clicked.disconnect(
                    self.__on_cancel)

            self.__settings = value

            self.__settings.create_DB_button.clicked.connect(
                self.__on_create_db_clicked)
            self.__settings.select_DB_button.clicked.connect(
                self.__on_select_db)
            self.__settings.DB_type.currentIndexChanged[str].connect(
                self.__on_db_type_changed)
            self.__settings.save_button.clicked.connect(self.__on_save)
            self.__settings.cancel_button.clicked.connect(self.__on_cancel)

        else:
            raise TypeError(
                "committed parameter is not of type SettingsDialog")
Esempio n. 4
0
class ImportViewInterface(QObject):
    """
    interface class defining signals and slots for all import views
    """

    def __init__(self, dock_widget: GeologicalDataProcessingDockWidget) -> None:
        """
        Initialize the view
        :param dock_widget: current GeologicalDataProcessingDockWidget instance
        """

        self.logger = QGISLogHandler(self.__class__.__name__)
        self.__combos = dict()
        self._dwg = dock_widget

        # initialize user interface
        self._import_service = ImportService.get_instance()
        self._import_service.reset_import.connect(self.reset_import)
        self._import_service.import_columns_changed.connect(self._on_import_columns_changed)

        self._table_view: QTableView or None = None
        self._only_number_in_table_view: bool = False
        self._table_model: PropertyImportModel = PropertyImportModel()
        self._dwg.start_import_button.clicked.connect(self._on_start_import)
        self._controller_thread: ImportControllersInterface or None = None

        super().__init__()

    def _connect_combo_listener(self):
        """
        connects all combobox elements to the on_selection_changed slot
        :return: Nothing
        """
        [combo.currentIndexChanged.connect(self.on_selection_changed) for combo in self.__combos]

    @property
    def combobox_names(self) -> Dict:
        """
        Returns a dictionary of comboboxes for the current view
        :return: returns a dictionary of comboboxes for the current view
        """
        return self.__combos

    @combobox_names.setter
    def combobox_names(self, combo_dict: Dict) -> None:
        """
        Sets a new dictionary to the combobox list. Disconnects the old ones and connects the new to the
        on_selection_changed slot
        :param combo_dict: dictionary with new combobox elements
        :return: Nothing
        :raises TypeError: if a dictionary value is not an instance of QComboBox or a key is not a str. Sets an empty
        dictionary instead
        """

        self._disconnect_selection_changed()

        for key in combo_dict:
            if not isinstance(key, str):
                self.__combos = dict()
                raise TypeError("{} is not a string".format(str(key)))
            if not isinstance(combo_dict[key], QComboBox):
                self.__combos = dict()
                raise TypeError("{} is not an instance of QComboBox".format(str(key)))

        self.__combos = combo_dict
        self._connect_selection_changed()

    @property
    def table_view(self) -> QTableView or None:
        return self._table_view

    @table_view.setter
    def table_view(self, widget: QTableView) -> None:
        if not isinstance(widget, QTableView):
            raise TypeError("submitted object is not of type QTableView: {}", str(widget))

        self._table_model.clear()
        self._table_view = widget
        self._table_view.setModel(self._table_model)

        if self._only_number_in_table_view:
            self._table_view.setItemDelegate(LogImportDelegate())
        else:
            self._table_view.setItemDelegate(PropertyImportDelegate())
        # self._table_view.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
        # self._table_view.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch)
        self._table_view.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)

    @property
    def dockwidget(self) -> GeologicalDataProcessingDockWidget:
        """
        Returns the current dockwidget
        :return: the current dockwidget
        """
        return self._dwg

    #
    # signals
    #

    selection_changed = pyqtSignal(list)
    """data changed signal which gives the index or name of the changed column and the newly selected text"""

    start_import = pyqtSignal()
    """signal to start the data import through an ImportController Thread"""

    #
    # slots
    #

    def on_selection_changed(self, _=None) -> None:
        """
        Emits the combobox_changed signal with a list of changed text
        :return: Nothing
        """
        selection_list = [self.combobox_names[key].currentText() for key in self.combobox_names]
        self.selection_changed.emit(selection_list)

        if self._table_view is None:
            self.logger.debug("No list widget specified for additional columns")
            return

        if self._only_number_in_table_view:
            cols = diff(self._import_service.number_columns, selection_list)
        else:
            cols = diff(self._import_service.selectable_columns, selection_list)

        self.logger.debug("selectable_columns: " + str(self._import_service.selectable_columns))
        self.logger.debug("selected_cols: " + str(selection_list))
        self.logger.debug("additional cols: " + str(cols))

        if self._table_view is not None:
            self._table_view.show()
            self._table_view.setEnabled(True)
            self._table_view.clearSelection()

        self._table_model.clear()
        for col in cols:
            property_type = PropertyTypes.FLOAT if col in self._import_service.number_columns else PropertyTypes.STRING
            self._table_model.add(PropertyImportData(name=col[0], unit=col[1], property_type=property_type))

    def _on_import_columns_changed(self) -> None:
        """
        change the import columns
        :return: Nothing
        """
        self.logger.debug("(Interface) _on_import_columns_changed")
        self._connect_selection_changed()
        self.on_selection_changed()

    def _on_start_import(self) -> None:
        self.logger.debug("(Interface) _on_start_import")
        self._update_progress_bar(0)
        self._dwg.progress_bar_layout.setVisible(True)

    def _on_import_failed(self, msg: str) -> None:
        self.logger.debug("(Interface) _on_import_failed")
        self.__import_finished()
        self.logger.error("Import failed", msg, to_messagebar=True)

    def _on_import_finished_with_warnings(self, msg: str) -> None:
        self.logger.debug("(Interface) _on_import_finished_with_warnings")
        self.logger.warn("Import finished with warnings", msg, to_messagebar=True)
        self.__import_finished()

    def _on_import_successful(self):
        self.logger.debug("(Interface) _on_import_successful")
        self.logger.info("Import successful", to_messagebar=True)
        self.__import_finished()

    def _on_cancel_import(self):
        self._controller_thread.cancel_import("Import canceled by user")

    def __import_finished(self):
        self._dwg.progress_bar_layout.setVisible(False)
        self._disconnect_thread()

        self._controller_thread.wait(2000)
        self._controller_thread = None

    #
    # public functions
    #

    def combobox_data(self, index: int or str) -> str:
        """
        Returns the currently selected item of the gui element with the given index
        :param index: index of the requested gui element as integer or string
        :return: Returns the data at the given index
        :raises IndexError: if index is not part in the available list
        """

        if isinstance(index, int):
            index = [self.combobox_names.keys()][index]
        else:
            index = str(index)

        if index not in self.combobox_names:
            raise IndexError("{} is not available".format(index))

        return self.combobox_names[index].currentText()

    def get_name(self, index: int) -> str or None:
        """
        Returns the name of the combobox with the given index
        :param index: index of the requested combobox
        :return: Returns the name of the combobox with the given index
        :raises IndexError: if the requested index is not in the list
        :raises ValueError: if the index is not convertible to an integer
        """
        index = int(index)

        if 0 <= index < len(self.combobox_names.keys()):
            return list(self.combobox_names.keys())[0]

    def get_names(self):
        """
        Returns a list of the combobox names
        :return: Returns a list of the combobox names
        """
        return list(self.combobox_names.keys())

    def set_combobox_data(self, index: int or str, values: List[str], default_index: int = 0) -> None:
        """
        Sets the committed values list to the gui combobox elements for the given index
        :param index: index of the requested gui element as integer or string
        :param values: new values for the combo boxes as a list of strings
        :param default_index: default selected index. If no default value is given, or the index is not part of the
                              list, the first entry will be selected by default
        :return: Returns, if the data setting was successful
        :raises IndexError: if index is not part in the available list
        :raises TypeError: if default_index is not an instance of int
        """

        if isinstance(index, int):
            index = [self.combobox_names.keys()][index]
        else:
            index = str(index)

        if index not in self.combobox_names:
            raise IndexError("{} is not available".format(index))

        if not isinstance(default_index, int):
            raise TypeError("default_index({}) is not an instance of int!".format(default_index))

        self.combobox_names[index].clear()
        for item in values:
            self.combobox_names[index].addItem(str(item))

        if not (0 <= default_index <= len(values)):
            default_index = 0
        self.combobox_names[index].setCurrentIndex(default_index)

    def reset_import(self) -> None:
        """
        Clears all import combo boxes, in case of a failure
        :return: Nothing
        """
        [self.set_combobox_data(name, []) for name in self.get_names()]

    def get_property_columns(self) -> List[PropertyImportData]:
        selection = set([x.row() for x in self._table_view.selectedIndexes()])
        self.logger.debug("selected rows indices: {}".format(selection))
        erg = [self._table_model.row(x) for x in selection]
        self.logger.debug("Selection:")
        [self.logger.debug("\t{}".format(x)) for x in erg]
        return erg

    #
    # protected functions
    #

    def _update_progress_bar(self, value):
        """
        slot to set the current progress bar value
        :param value: value in percent
        :return: nothing
        """
        self.logger.debug("Update progressbar with value {} called".format(value))
        if value < 0:
            self.dockwidget.progress_bar.setValue(0)
        elif value > 100:
            self.dockwidget.progress_bar.setValue(100)
        else:
            self.dockwidget.progress_bar.setValue(int(value))

    def _connect_selection_changed(self):
        self.logger.debug("_connect_selection_changed")
        [self.__combos[key].currentTextChanged.connect(self.on_selection_changed) for key in self.__combos]

    def _disconnect_selection_changed(self):
        for key in self.__combos:
            try:
                self.__combos[key].currentTextChanged.disconnect(self.on_selection_changed)
            except TypeError:
                # not connected
                pass

    def _connect_thread(self):
        self._controller_thread.import_finished.connect(self._on_import_successful)
        self._controller_thread.import_failed.connect(self._on_import_failed)
        self._controller_thread.import_finished_with_warnings.connect(self._on_import_finished_with_warnings)
        self._controller_thread.update_progress.connect(self._update_progress_bar)
        self._dwg.cancel_import.clicked.connect(self._on_cancel_import)

    def _disconnect_thread(self):
        self._controller_thread.import_finished.disconnect(self._on_import_successful)
        self._controller_thread.import_failed.disconnect(self._on_import_failed)
        self._controller_thread.import_finished_with_warnings.disconnect(self._on_import_finished_with_warnings)
        self._controller_thread.update_progress.disconnect(self._update_progress_bar)
        self._dwg.cancel_import.clicked.disconnect(self._on_cancel_import)
Esempio n. 5
0
class ImportControllersInterface(QThread):
    """
    Basic interface for all import_tests controller
    """

    def __init__(self, data: Dict, selection: Dict, properties: List[PropertyImportData]) -> None:
        """
        :param data: import data parsed from the file to import
        :param selection: dictionary of selected columns
        """
        super().__init__()

        self._logger = QGISLogHandler(self.__class__.__name__)
        self._data: Dict = data
        self._selection: Dict = selection
        self._properties: List[PropertyImportData] = properties
        self._mutex = QMutex()
        self._cancel = False
        self._message = ""

    def run(self) -> None:
        """
        Thread execution function to import data
        :return: Nothing
        """
        pass

    #
    # signals
    #

    update_progress = pyqtSignal(int)
    """update progress bar signal. Committed value has to be between 0 and 100"""

    import_finished = pyqtSignal()
    """signal emitted, when the import process has finished"""

    import_finished_with_warnings = pyqtSignal(str)
    """signal emitted, when the import process has finished with warnings"""

    import_failed = pyqtSignal(str)
    """signal emitted, when the import process was canceled or failed through a call of the cancel_import slot"""

    #
    # slots
    #

    def cancel_import(self, msg: str = "") -> None:
        """
        slot for canceling the import process. A trigger variable will hint the importer to stop at the next
        possibility. This should ensure the finalization of all started write processes and therefore the integrity of
        all database objects.

        The :func:`~GeologicalDataProcessing.controller.import_controller.ImportControllersInterface.import_cancelled`
        signal will be sent, if the import process was successfully cancelled.
        :return: Nothing
        """
        self._cancel = True
        if msg == "":
            self._message = "Import canceled"
        else:
            self._message = msg

    def _import_done(self, future: Future = None) -> None:
        """
        function called, when import is done or canceled
        :param future: import executing future object
        :return: nothing
        """
        self._logger.debug("import done")

        self._view.dockwidget.progress_bar_layout.setVisible(False)
        self._view.dockwidget.cancel_import.clicked.disconnect(self._stop_import)
        if (future is not None) and future.cancelled():
            self._logger.warn("future run finished, import cancelled!")
        elif future is not None:
            self._logger.info("future run finished, import successful")

        self._logger.info("QThread finished")
        if self.__thread is not None:
            self._logger.debug("waiting for the end...")
            self.__thread.wait()
            self._logger.debug("at the end...")
            self.__thread = None
    def run(self) -> None:
        """Run method that loads and starts the plugin"""

        if not self.pluginIsActive:
            self.pluginIsActive = True

            try:
                # initialize logger
                logger = QGISLogHandler()
                logger.qgis_iface = self.iface
                logger.save_to_file = True

                if packages_found == "NO_PACKAGES" or not ModuleService.check_required_modules():
                    logger.info("installing or updating packages")
                    if not ModuleService.install_packages():
                        logger.error("package installation failed, please restart QGIS to try again.")
                    else:
                        logger.info("package installation successful, please restart QGIS")
                        msg = QMessageBox()
                        msg.setIcon(QMessageBox.Information)
                        msg.setText("packages installation successful")
                        msg.setInformativeText("Please restart QGIS to use the GeologicalDataProcessing extension")

                        msg.setWindowTitle("package update")
                        msg.exec_()
                        return

                    return
                else:
                    logger.debug("all required packages up2date")

                # dockwidget may not exist if:
                #    first run of plugin
                #    removed on close (see self.onClosePlugin method)
                if self.dockwidget is None:
                    # Create the dockwidget (after translation) and keep reference
                    self.dockwidget = GeologicalDataProcessingDockWidget()

                if self.settings_dialog is None:
                    self.settings_dialog = SettingsDialog(parent=self.dockwidget)
                    self.settings_dialog.setModal(True)
                    self.dockwidget.settings_button.clicked.connect(self.settings_dialog.exec)

                # connect to provide cleanup on closing of dockwidget
                self.dockwidget.closingPlugin.connect(self.onClosePlugin)

                # show the dockwidget
                # TODO: fix to allow choice of dock location
                self.iface.addDockWidget(Qt.RightDockWidgetArea, self.dockwidget)
                self.dockwidget.show()

                from GeologicalDataProcessing.controller.database_controller import DatabaseController
                from GeologicalDataProcessing.services.import_service import ImportService
                from GeologicalDataProcessing.views.import_views import LineImportView, PointImportView, \
                    WellImportView, PropertyImportView, WellLogImportView

                ImportService.get_instance(self.dockwidget)

                # initialize the gui and connect signals and slots
                self.dockwidget.import_type.currentChanged.connect(self.on_import_type_changed_event)

                # start tests button
                # -> only visible and active when the debug flag is True
                if config.debug:
                    self.dockwidget.start_tests_button.clicked.connect(self.on_start_tests)
                else:
                    self.dockwidget.start_tests_button.setVisible(False)
                    self.dockwidget.start_tests_separator.setVisible(False)

                self.dockwidget.progress_bar_layout.setVisible(False)

                self.__views["import_points"] = PointImportView(self.dockwidget)
                self.__views["import_lines"] = LineImportView(self.dockwidget)
                self.__views["import_wells"] = WellImportView(self.dockwidget)
                self.__views["import_properties"] = PropertyImportView(self.dockwidget)
                self.__views["import_well_logs"] = WellLogImportView(self.dockwidget)

                self.__db_controller = DatabaseController(self.settings_dialog)

                if config.debug:
                    self.dockwidget.import_file.setText(
                        "/Users/stephan/Library/Application Support/QGIS/QGIS3/profiles/" +
                        "default/python/plugins/GeologicalDataProcessing/tests/test_data/point_data.txt")

            except Exception as e:
                ExceptionHandler(e).log()