From dfc626a268100310325faf43db1a9b24a3528e18 Mon Sep 17 00:00:00 2001 From: signedav Date: Wed, 5 Apr 2023 13:18:37 +0200 Subject: [PATCH 1/6] user interface to select --exportModels in export page of the wizard and in the validation panel --- .../gui/panel/export_models_panel.py | 76 +++++++++++++++++++ QgisModelBaker/gui/validate.py | 25 ++++++ .../export_data_configuration_page.py | 5 ++ .../gui/workflow_wizard/workflow_wizard.py | 13 ++++ QgisModelBaker/ui/export_models_panel.ui | 67 ++++++++++++++++ QgisModelBaker/ui/filter_data_panel.ui | 2 +- QgisModelBaker/ui/validator.ui | 75 +++++++++--------- .../export_data_configuration.ui | 31 +++++--- 8 files changed, 247 insertions(+), 47 deletions(-) create mode 100644 QgisModelBaker/gui/panel/export_models_panel.py create mode 100644 QgisModelBaker/ui/export_models_panel.ui diff --git a/QgisModelBaker/gui/panel/export_models_panel.py b/QgisModelBaker/gui/panel/export_models_panel.py new file mode 100644 index 000000000..24540fdd9 --- /dev/null +++ b/QgisModelBaker/gui/panel/export_models_panel.py @@ -0,0 +1,76 @@ +""" +/*************************************************************************** + begin : 22.11.2021 + git sha : :%H$ + copyright : (C) 2021 by Dave Signer + email : david at opengis ch + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +""" + +from qgis.PyQt.QtWidgets import QWidget + +import QgisModelBaker.utils.gui_utils as gui_utils +from QgisModelBaker.utils import gui_utils + +WIDGET_UI = gui_utils.get_ui_class("export_models_panel.ui") + + +# could be renamed since it's not only model - it's dataset and basket as well +class ExportModelsPanel(QWidget, WIDGET_UI): + def __init__(self, parent=None): + QWidget.__init__(self, parent) + self.setupUi(self) + self.parent = parent + + def setup_dialog(self, validation=False): + self._generate_texts(validation) + self.export_models_checkbox.setChecked(False) + self.items_view.setVisible(False) + if self.parent: + self.items_view.setModel(self.parent.current_export_models_model) + self.items_view.clicked.connect(self.items_view.model().check) + self.items_view.space_pressed.connect(self.items_view.model().check) + + self.export_models_checkbox.stateChanged.connect(self._active_state_changed) + self.export_models_checkbox.setChecked( + self.parent.current_export_models_active + ) + self._active_state_changed(self.parent.current_export_models_active) + + def _generate_texts(self, validation): + self.export_models_checkbox.setText( + self.tr( + "{verb} the data in a model other than the one where it is stored" + ).format(verb="Validate" if validation else "Export") + ) + self.export_models_checkbox.setToolTip( + self.tr( + """ + +

If your data is in the format of the cantonal model, but you want to {verb} it in the format of the national model.

+

The data that cannot be {pastverb} in the selected model is {pastverb} in the model it is stored.

+

Usually, this is one single model. However, it is also possible to pass multiple models, which makes sense if there are multiple base models in the schema you want to {verb}.

+

This is the value passed to --exportModels

+ + """ + ).format( + verb="validate" if validation else "export", + pastverb="validated" if validation else "exported", + ) + ) + + def _active_state_changed(self, checked): + self.items_view.setVisible(checked) + if not checked: + # on uncheck disable all + self.items_view.model().check_all([]) + self.parent.current_export_models_active = checked diff --git a/QgisModelBaker/gui/validate.py b/QgisModelBaker/gui/validate.py index b6447983f..45f73d1b9 100644 --- a/QgisModelBaker/gui/validate.py +++ b/QgisModelBaker/gui/validate.py @@ -32,6 +32,7 @@ from qgis.PyQt.QtWidgets import QAction, QDockWidget, QFileDialog, QHeaderView, QMenu import QgisModelBaker.libs.modelbaker.utils.db_utils as db_utils +from QgisModelBaker.gui.panel.export_models_panel import ExportModelsPanel from QgisModelBaker.gui.panel.filter_data_panel import FilterDataPanel from QgisModelBaker.libs.modelbaker.db_factory.db_simple_factory import DbSimpleFactory from QgisModelBaker.libs.modelbaker.iliwrapper import ilivalidator @@ -105,6 +106,7 @@ def __init__(self): self.models_model = SchemaModelsModel() self.datasets_model = SchemaDatasetsModel() self.baskets_model = SchemaBasketsModel() + self.export_models_model = SchemaModelsModel() self.result_model = None def __init__(self, base_config, iface): @@ -125,10 +127,16 @@ def __init__(self, base_config, iface): self.current_datasets_model = SchemaDatasetsModel() self.current_baskets_model = SchemaBasketsModel() self.current_filter_mode = SchemaDataFilterMode.NO_FILTER + self.current_export_models_model = SchemaModelsModel() + self.current_export_models_active = False self.filter_data_panel = FilterDataPanel(self) self.filter_data_panel.setMaximumHeight(self.fontMetrics().lineSpacing() * 10) self.filter_layout.addWidget(self.filter_data_panel) + + self.export_models_panel = ExportModelsPanel(self) + self.export_models_panel.setMaximumHeight(self.fontMetrics().lineSpacing() * 10) + self.export_models_layout.addWidget(self.export_models_panel) self._reset_gui() self.run_button.clicked.connect(self._run) @@ -161,6 +169,8 @@ def _reset_current_values(self): self.current_datasets_model = SchemaDatasetsModel() self.current_baskets_model = SchemaBasketsModel() self.current_filter_mode = SchemaDataFilterMode.NO_FILTER + self.current_export_models_model = SchemaModelsModel() + self.current_export_models_active = False def _reset_gui(self): self._reset_current_values() @@ -253,7 +263,12 @@ def set_current_layer(self, layer): self.current_baskets_model = self.schema_validations[ self.current_schema_identificator ].baskets_model + self.current_export_models_model = self.schema_validations[ + self.current_schema_identificator + ].export_models_model + self.filter_data_panel.setup_dialog(self._basket_handling()) + self.export_models_panel.setup_dialog(True) self.setDisabled(False) def _visibility_changed(self, visible): @@ -271,6 +286,9 @@ def _refresh_schemadata_models(self): self.schema_validations[ self.current_schema_identificator ].baskets_model.refresh_model(db_connector) + self.schema_validations[ + self.current_schema_identificator + ].export_models_model.refresh_model([db_connector]) return def _basket_handling(self): @@ -317,6 +335,13 @@ def _run(self, edited_command=None): self.current_models_model.stringList() ) + if self.current_export_models_active: + validator.configuration.iliexportmodels = ";".join( + self.current_export_models_model.checked_entries() + ) + else: + validator.configuration.iliexportmodels = "" + validator.configuration.skip_geometry_errors = ( self.skip_geometry_errors_check_box.isChecked() ) diff --git a/QgisModelBaker/gui/workflow_wizard/export_data_configuration_page.py b/QgisModelBaker/gui/workflow_wizard/export_data_configuration_page.py index 4bec9256d..ffb3eca6c 100644 --- a/QgisModelBaker/gui/workflow_wizard/export_data_configuration_page.py +++ b/QgisModelBaker/gui/workflow_wizard/export_data_configuration_page.py @@ -22,6 +22,7 @@ from qgis.PyQt.QtWidgets import QWizardPage import QgisModelBaker.utils.gui_utils as gui_utils +from QgisModelBaker.gui.panel.export_models_panel import ExportModelsPanel from QgisModelBaker.gui.panel.filter_data_panel import FilterDataPanel from QgisModelBaker.libs.modelbaker.utils.qt_utils import ( FileValidator, @@ -45,6 +46,9 @@ def __init__(self, parent, title): self.filter_data_panel = FilterDataPanel(self.workflow_wizard) self.filter_layout.addWidget(self.filter_data_panel) + self.export_models_panel = ExportModelsPanel(self.workflow_wizard) + self.export_models_layout.addWidget(self.export_models_panel) + self.is_complete = False self.xtf_file_browse_button.clicked.connect( @@ -84,6 +88,7 @@ def nextId(self): def setup_dialog(self, basket_handling): self.filter_data_panel.setup_dialog(basket_handling) + self.export_models_panel.setup_dialog() def _set_current_export_target(self, text): self.setComplete( diff --git a/QgisModelBaker/gui/workflow_wizard/workflow_wizard.py b/QgisModelBaker/gui/workflow_wizard/workflow_wizard.py index e978f833b..52a8f3306 100644 --- a/QgisModelBaker/gui/workflow_wizard/workflow_wizard.py +++ b/QgisModelBaker/gui/workflow_wizard/workflow_wizard.py @@ -126,6 +126,10 @@ def __init__(self, iface, base_config, parent): self.current_export_target = "" self.current_filter_mode = SchemaDataFilterMode.NO_FILTER + # the current_export_models_model keeps every single model found in the current database and keeps the selected models + self.current_export_models_model = SchemaModelsModel() + self.current_export_models_active = False + # pages setup self.intro_page = IntroPage(self, self._current_page_title(PageIds.Intro)) self.source_selection_page = ImportSourceSelectionPage( @@ -345,6 +349,7 @@ def id_changed(self, new_id): models = [] datasets = [] baskets = [] + export_models = [] if self.current_filter_mode == SchemaDataFilterMode.MODEL: models = self.current_models_model.checked_entries() elif self.current_filter_mode == SchemaDataFilterMode.DATASET: @@ -355,6 +360,13 @@ def id_changed(self, new_id): # no filter - export all models models = self.current_models_model.stringList() + if self.current_export_models_active: + export_models = self.current_export_models_model.checked_entries() + self.export_data_configuration.iliexportmodels = ";".join(export_models) + # or (needs to be checked): sessions[self.current_export_target]["export_models"] = export_models + else: + self.export_data_configuration.iliexportmodels = "" + sessions[self.current_export_target]["models"] = models sessions[self.current_export_target]["datasets"] = datasets sessions[self.current_export_target]["baskets"] = baskets @@ -413,6 +425,7 @@ def refresh_export_models(self): self.current_models_model.refresh_model([db_connector]) self.current_datasets_model.refresh_model(db_connector) self.current_baskets_model.refresh_model(db_connector) + self.current_export_models_model.refresh_model([db_connector]) return def refresh_import_models(self, silent=False): diff --git a/QgisModelBaker/ui/export_models_panel.ui b/QgisModelBaker/ui/export_models_panel.ui new file mode 100644 index 000000000..dde6b72ac --- /dev/null +++ b/QgisModelBaker/ui/export_models_panel.ui @@ -0,0 +1,67 @@ + + + Form + + + + 0 + 0 + 603 + 176 + + + + + 0 + 0 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + <html><head/><body><p>If your data is in the format of the cantonal model, but you want to export it in the format of the national model.</p><p>Usually, this is one single model. However, it is also possible to pass multiple models, which makes sense if there are multiple base models in the schema you want to export.</p><p>This is the value passed to <span style=" font-family:'monospace';">--exportModels</span></p></body></html> + + + Export the data in a model other than the one where it is stored + + + + + + + + 0 + 0 + + + + + + + + + SpaceCheckListView + +
QgisModelBaker.utils.gui_utils
+ 1 +
+
+ + +
diff --git a/QgisModelBaker/ui/filter_data_panel.ui b/QgisModelBaker/ui/filter_data_panel.ui index ef296bf81..f8e08e502 100644 --- a/QgisModelBaker/ui/filter_data_panel.ui +++ b/QgisModelBaker/ui/filter_data_panel.ui @@ -36,7 +36,7 @@ - + 0 0 diff --git a/QgisModelBaker/ui/validator.ui b/QgisModelBaker/ui/validator.ui index 8a8733f0d..18fe95b79 100644 --- a/QgisModelBaker/ui/validator.ui +++ b/QgisModelBaker/ui/validator.ui @@ -6,8 +6,8 @@ 0 0 - 800 - 600 + 834 + 804 @@ -15,28 +15,7 @@ - - - - - - ili2db option --validConfig - - - Validator config file (--validConfig) - - - - - - - ... - - - - - - + @@ -115,7 +94,21 @@ - + + + + 0 + + + + + + + 0 + + + + Ignores geometry errors (--skipGeometryErrors) and AREA topology validation (--disableAreaValidation) @@ -125,7 +118,7 @@ - + @@ -164,7 +157,28 @@ - + + + + + + ili2db option --validConfig + + + Validator config file (--validConfig) + + + + + + + ... + + + + + + @@ -174,13 +188,6 @@ - - - - 0 - - - diff --git a/QgisModelBaker/ui/workflow_wizard/export_data_configuration.ui b/QgisModelBaker/ui/workflow_wizard/export_data_configuration.ui index 10d4d9a28..50181d373 100644 --- a/QgisModelBaker/ui/workflow_wizard/export_data_configuration.ui +++ b/QgisModelBaker/ui/workflow_wizard/export_data_configuration.ui @@ -54,16 +54,6 @@ QFrame::Raised - - - - 0 - - - - - - @@ -71,7 +61,10 @@ - + + + + Browse XTF files @@ -81,7 +74,21 @@ - + + + + 0 + + + + + + + 0 + + + + Qt::Vertical From 7f5c38110cf9b1a8f3367c73c5f36db123bf073e Mon Sep 17 00:00:00 2001 From: signedav Date: Wed, 5 Apr 2023 15:17:01 +0200 Subject: [PATCH 2/6] return parentmodels in model - but not yet used by the gui --- QgisModelBaker/utils/gui_utils.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/QgisModelBaker/utils/gui_utils.py b/QgisModelBaker/utils/gui_utils.py index 6885f89ae..d8b02f8a9 100644 --- a/QgisModelBaker/utils/gui_utils.py +++ b/QgisModelBaker/utils/gui_utils.py @@ -864,8 +864,27 @@ class SchemaModelsModel(CheckEntriesModel): Multiple db_connectors can be passed to scan multiple sources. """ + class Roles(Enum): + PARENT_MODELS = Qt.UserRole + 1 + + def __int__(self): + return self.value + def __init__(self): super().__init__() + self._parent_models = {} + + def data(self, index, role): + if role == int(SchemaModelsModel.Roles.PARENT_MODELS): + return self._parent_models[self.data(index, Qt.DisplayRole)] + else: + return CheckEntriesModel.data(self, index, role) + + def setData(self, index, role, data): + if role == int(SchemaModelsModel.Roles.PARENT_MODELS): + self._parent_models[self.data(index, Qt.DisplayRole)] = data + else: + CheckEntriesModel.setData(self, index, role, data) def refresh_model(self, db_connectors=[]): modelnames = [] @@ -887,6 +906,7 @@ def refresh_model(self, db_connectors=[]): and name not in modelnames ): modelnames.append(name) + self._parent_models[name] = db_model["parents"] self.setStringList(modelnames) From c6902470af4f4155f12abd09716aa72857f031ec Mon Sep 17 00:00:00 2001 From: signedav Date: Wed, 5 Apr 2023 17:16:55 +0200 Subject: [PATCH 3/6] tooltip --- QgisModelBaker/utils/gui_utils.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/QgisModelBaker/utils/gui_utils.py b/QgisModelBaker/utils/gui_utils.py index d8b02f8a9..8c1276d86 100644 --- a/QgisModelBaker/utils/gui_utils.py +++ b/QgisModelBaker/utils/gui_utils.py @@ -875,6 +875,16 @@ def __init__(self): self._parent_models = {} def data(self, index, role): + if role == Qt.ToolTipRole: + model_name = self.data(index, Qt.DisplayRole) + if self._parent_models[model_name]: + return self.tr( + """ + +

{} is an extension of {}

+ + """ + ).format(model_name, ", ".join(self._parent_models[model_name])) if role == int(SchemaModelsModel.Roles.PARENT_MODELS): return self._parent_models[self.data(index, Qt.DisplayRole)] else: From 67678167d8da0a6d76568eb07216ffdd1ec2e01a Mon Sep 17 00:00:00 2001 From: signedav Date: Wed, 5 Apr 2023 17:53:25 +0200 Subject: [PATCH 4/6] Improve session panel outputs --- QgisModelBaker/gui/panel/session_panel.py | 65 ++++++++++++++++--- .../gui/workflow_wizard/execution_page.py | 8 +++ .../gui/workflow_wizard/workflow_wizard.py | 5 +- QgisModelBaker/utils/gui_utils.py | 8 +-- 4 files changed, 68 insertions(+), 18 deletions(-) diff --git a/QgisModelBaker/gui/panel/session_panel.py b/QgisModelBaker/gui/panel/session_panel.py index 31069ffba..630551a9a 100644 --- a/QgisModelBaker/gui/panel/session_panel.py +++ b/QgisModelBaker/gui/panel/session_panel.py @@ -56,6 +56,7 @@ def __init__( models, datasets, baskets, + export_models, db_action_type, parent=None, ): @@ -68,6 +69,7 @@ def __init__( self.models = models self.datasets = datasets self.baskets = baskets + self.export_models = export_models # set up the gui self.create_text = self.tr("Run") @@ -109,27 +111,70 @@ def __init__( if os.path.isfile(self.file): self.configuration.ilifile = self.file self.configuration.ilimodels = ";".join(self.models) - self.info_label.setText(self.tr("Import {}").format(", ".join(self.models))) + self.info_label.setText( + self.tr( + """ + +

Import {models}

+ + """ + ).format(models=", ".join(self.models)) + ) elif self.db_action_type == DbActionType.IMPORT_DATA: self.configuration.xtffile = self.file self.configuration.ilimodels = ";".join(self.models) self.configuration.with_importtid = self._get_tid_handling() - self.info_label.setText( - self.tr("Import {} of {}").format(", ".join(self.models), self.file) - ) + if self.datasets: + + self.info_label.setText( + self.tr( + """ + +

Update the data in dataset {dataset}

+

with the data from {file}

+ + """ + ).format(dataset=self.datasets[0], file=self.file) + ) + else: + self.info_label.setText( + self.tr( + """ + +

Import the data from {file}

+ + """ + ).format(file=self.file) + ) + self.configuration.dataset = self.datasets[0] if self.datasets else None elif self.db_action_type == DbActionType.EXPORT: self.configuration.xtffile = self.file - self.configuration.ilimodels = ";".join(self.models) self.configuration.with_exporttid = self._get_tid_handling() + self.configuration.iliexportmodels = ";".join(self.export_models) self.info_label.setText( - self.tr('Export of "{}" \nto {}').format( - '", "'.join(self.models) - or '", "'.join(self.datasets) - or '", "'.join(self.baskets), - self.file, + self.tr( + """ + +

Export the data of {filtered_data}

+ {export_model_part} +

to file {file}

+ + """ + ).format( + filtered_data=", ".join(self.models) + or ", ".join(self.datasets) + or ", ".join(self.baskets), + export_model_part=self.tr( + "

in the format of {export_models}

" + ).format(export_models=", ".join(self.export_models)) + if self.export_models + else "", + file=self.file, ) ) + + self.configuration.ilimodels = ";".join(self.models) self.configuration.dataset = ";".join(self.datasets) self.configuration.baskets = self.baskets diff --git a/QgisModelBaker/gui/workflow_wizard/execution_page.py b/QgisModelBaker/gui/workflow_wizard/execution_page.py index 36d4e121c..f184f92ac 100644 --- a/QgisModelBaker/gui/workflow_wizard/execution_page.py +++ b/QgisModelBaker/gui/workflow_wizard/execution_page.py @@ -97,12 +97,19 @@ def setup_sessions(self, configuration, sessions): ) baskets = sessions[key]["baskets"] if "baskets" in sessions[key] else None + export_models = ( + sessions[key]["export_models"] + if "export_models" in sessions[key] + else None + ) + skipped_session_widget = self._find_skipped_session_widget( ( key, models, datasets, baskets, + export_models, db_utils.get_schema_identificator_from_configuration(configuration), ) ) @@ -115,6 +122,7 @@ def setup_sessions(self, configuration, sessions): models, datasets, baskets, + export_models, self.db_action_type, ) session.on_done_or_skipped.connect(self._on_done_or_skipped_received) diff --git a/QgisModelBaker/gui/workflow_wizard/workflow_wizard.py b/QgisModelBaker/gui/workflow_wizard/workflow_wizard.py index 52a8f3306..9b1757b05 100644 --- a/QgisModelBaker/gui/workflow_wizard/workflow_wizard.py +++ b/QgisModelBaker/gui/workflow_wizard/workflow_wizard.py @@ -362,14 +362,11 @@ def id_changed(self, new_id): if self.current_export_models_active: export_models = self.current_export_models_model.checked_entries() - self.export_data_configuration.iliexportmodels = ";".join(export_models) - # or (needs to be checked): sessions[self.current_export_target]["export_models"] = export_models - else: - self.export_data_configuration.iliexportmodels = "" sessions[self.current_export_target]["models"] = models sessions[self.current_export_target]["datasets"] = datasets sessions[self.current_export_target]["baskets"] = baskets + sessions[self.current_export_target]["export_models"] = export_models self.export_data_execution_page.setup_sessions( self.export_data_configuration, sessions diff --git a/QgisModelBaker/utils/gui_utils.py b/QgisModelBaker/utils/gui_utils.py index 8c1276d86..4ab1c117c 100644 --- a/QgisModelBaker/utils/gui_utils.py +++ b/QgisModelBaker/utils/gui_utils.py @@ -880,10 +880,10 @@ def data(self, index, role): if self._parent_models[model_name]: return self.tr( """ - -

{} is an extension of {}

- - """ + +

{} is an extension of {}

+ + """ ).format(model_name, ", ".join(self._parent_models[model_name])) if role == int(SchemaModelsModel.Roles.PARENT_MODELS): return self._parent_models[self.data(index, Qt.DisplayRole)] From 5f9071da274eb56cd74aeb924c461f581addadc3 Mon Sep 17 00:00:00 2001 From: signedav Date: Thu, 6 Apr 2023 09:37:58 +0200 Subject: [PATCH 5/6] bump to modelbaker 143 --- scripts/package_pip_packages.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/package_pip_packages.sh b/scripts/package_pip_packages.sh index 55f545a38..5b84bce80 100755 --- a/scripts/package_pip_packages.sh +++ b/scripts/package_pip_packages.sh @@ -1,7 +1,7 @@ #!/bin/bash LIBS_DIR="QgisModelBaker/libs" -MODELBAKER_LIBRARY=("modelbaker" "1.4.2") +MODELBAKER_LIBRARY=("modelbaker" "1.4.3") PACKAGING=("packaging" "21.3") PACKAGES=( From c9fbb7d6a178f12558333ad98ed246c640b6ccbb Mon Sep 17 00:00:00 2001 From: signedav Date: Thu, 6 Apr 2023 15:07:34 +0200 Subject: [PATCH 6/6] avoid multiple trigger the signal --- QgisModelBaker/gui/panel/export_models_panel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QgisModelBaker/gui/panel/export_models_panel.py b/QgisModelBaker/gui/panel/export_models_panel.py index 24540fdd9..be40a71cd 100644 --- a/QgisModelBaker/gui/panel/export_models_panel.py +++ b/QgisModelBaker/gui/panel/export_models_panel.py @@ -40,10 +40,10 @@ def setup_dialog(self, validation=False): self.items_view.clicked.connect(self.items_view.model().check) self.items_view.space_pressed.connect(self.items_view.model().check) - self.export_models_checkbox.stateChanged.connect(self._active_state_changed) self.export_models_checkbox.setChecked( self.parent.current_export_models_active ) + self.export_models_checkbox.stateChanged.connect(self._active_state_changed) self._active_state_changed(self.parent.current_export_models_active) def _generate_texts(self, validation):