From a8f41fe3667719c96b9f7bfa2cfe34dbc18c2eaa Mon Sep 17 00:00:00 2001 From: Niko Aarnio Date: Thu, 20 Feb 2025 12:42:03 +0200 Subject: [PATCH 1/2] refactor regulation groups from active plan handling Now PlanManager has a variable for reg. groups from active plan that is updated when needed. Previously all regulation groups were read from DB every time reg. groups from active plan were needed for some form. --- arho_feature_template/core/plan_manager.py | 29 ++++++++++++------- .../gui/docks/regulation_groups_dock.py | 2 +- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/arho_feature_template/core/plan_manager.py b/arho_feature_template/core/plan_manager.py index 10f8088..5c6d66e 100644 --- a/arho_feature_template/core/plan_manager.py +++ b/arho_feature_template/core/plan_manager.py @@ -104,8 +104,7 @@ def __init__(self): self.regulation_groups_dock.new_regulation_group_requested.connect(self.create_new_regulation_group) self.regulation_groups_dock.edit_regulation_group_requested.connect(self.edit_regulation_group) self.regulation_groups_dock.delete_regulation_group_requested.connect(self.delete_regulation_group) - if get_active_plan_id(): - self.regulation_groups_dock.initialize_regulation_groups(regulation_group_library_from_active_plan()) + self.update_active_plan_regulation_group_library() self.regulation_groups_dock.hide() # Initialize digitize tools @@ -156,6 +155,10 @@ def initialize_libraries(self): ] self.new_feature_dock.initialize_feature_template_libraries(self.feature_template_libraries) + def update_active_plan_regulation_group_library(self): + self.active_plan_regulation_group_library = regulation_group_library_from_active_plan() + self.regulation_groups_dock.update_regulation_groups(self.active_plan_regulation_group_library) + def create_new_regulation_group(self): self._open_regulation_group_form(RegulationGroup()) @@ -169,11 +172,11 @@ def _open_regulation_group_form(self, regulation_group: RegulationGroup): save_regulation_group_as_config(regulation_group_form.model) else: save_regulation_group(regulation_group_form.model) - self.regulation_groups_dock.initialize_regulation_groups(regulation_group_library_from_active_plan()) + self.update_active_plan_regulation_group_library() def delete_regulation_group(self, group: RegulationGroup): delete_regulation_group(group) - self.regulation_groups_dock.initialize_regulation_groups(regulation_group_library_from_active_plan()) + self.update_active_plan_regulation_group_library() def toggle_identify_plan_features(self, activate: bool): # noqa: FBT001 if activate: @@ -240,7 +243,7 @@ def edit_plan(self): attribute_form = PlanAttributeForm(plan_model, self.regulation_group_libraries) if attribute_form.exec_(): feature = save_plan(attribute_form.model) - self.regulation_groups_dock.initialize_regulation_groups(regulation_group_library_from_active_plan()) + self.update_active_plan_regulation_group_library() def edit_lifecycles(self): plan_layer = PlanLayer.get_from_project() @@ -311,11 +314,11 @@ def _plan_feature_geom_digitized(self, feature: QgsFeature): plan_feature.geom = feature.geometry() attribute_form = PlanFeatureForm( - plan_feature, title, [*self.regulation_group_libraries, regulation_group_library_from_active_plan()] + plan_feature, title, [*self.regulation_group_libraries, self.active_plan_regulation_group_library] ) if attribute_form.exec_(): save_plan_feature(attribute_form.model) - self.regulation_groups_dock.initialize_regulation_groups(regulation_group_library_from_active_plan()) + self.update_active_plan_regulation_group_library() def edit_plan_feature(self, feature: QgsFeature, layer_name: str): layer_class = FEATURE_LAYER_NAME_TO_CLASS_MAP[layer_name] @@ -323,11 +326,11 @@ def edit_plan_feature(self, feature: QgsFeature, layer_name: str): title = plan_feature.name if plan_feature.name else layer_name attribute_form = PlanFeatureForm( - plan_feature, title, [*self.regulation_group_libraries, regulation_group_library_from_active_plan()] + plan_feature, title, [*self.regulation_group_libraries, self.active_plan_regulation_group_library] ) if attribute_form.exec_(): save_plan_feature(attribute_form.model) - self.regulation_groups_dock.initialize_regulation_groups(regulation_group_library_from_active_plan()) + self.update_active_plan_regulation_group_library() def set_active_plan(self, plan_id: str | None): """Update the project layers based on the selected land use plan. @@ -352,8 +355,7 @@ def set_active_plan(self, plan_id: str | None): if previously_in_edit_mode: plan_layer.startEditing() - # Update regulation group dock - self.regulation_groups_dock.initialize_regulation_groups(regulation_group_library_from_active_plan()) + self.update_active_plan_regulation_group_library() def load_land_use_plan(self): """Load an existing land use plan using a dialog selection.""" @@ -456,6 +458,11 @@ def unload(self): def regulation_group_library_from_active_plan() -> RegulationGroupLibrary: + if not get_active_plan_id(): + return RegulationGroupLibrary( + name="Käytössä olevat kaavamääräysryhmät", version=None, description=None, regulation_group_categories=[] + ) + id_of_general_regulation_group_type = PlanRegulationGroupTypeLayer.get_attribute_value_by_another_attribute_value( "id", "value", "generalRegulations" ) diff --git a/arho_feature_template/gui/docks/regulation_groups_dock.py b/arho_feature_template/gui/docks/regulation_groups_dock.py index 6e525a6..b388dd7 100644 --- a/arho_feature_template/gui/docks/regulation_groups_dock.py +++ b/arho_feature_template/gui/docks/regulation_groups_dock.py @@ -64,7 +64,7 @@ def connect_buttons(self): self.edit_btn.clicked.connect(self.on_edit_btn_clicked) self.delete_btn.clicked.connect(self.on_delete_btn_clicked) - def initialize_regulation_groups(self, regulation_group_library: RegulationGroupLibrary): + def update_regulation_groups(self, regulation_group_library: RegulationGroupLibrary): self.regulation_group_list.clear() for category in regulation_group_library.regulation_group_categories: From ab824b3f3ec11d8e1f8a5a9185ba3d66fd522099 Mon Sep 17 00:00:00 2001 From: Niko Aarnio Date: Fri, 21 Feb 2025 10:35:44 +0200 Subject: [PATCH 2/2] add checks and messages for existing/duplicate regulation group short names --- arho_feature_template/core/models.py | 9 +++ arho_feature_template/core/plan_manager.py | 6 +- .../gui/dialogs/plan_feature_form.py | 58 +++++++++++++++++-- .../gui/dialogs/plan_regulation_group_form.py | 20 ++++++- 4 files changed, 82 insertions(+), 11 deletions(-) diff --git a/arho_feature_template/core/models.py b/arho_feature_template/core/models.py index 8b12e67..e236430 100644 --- a/arho_feature_template/core/models.py +++ b/arho_feature_template/core/models.py @@ -146,6 +146,15 @@ def from_config_file(cls, config_fp: Path) -> RegulationGroupLibrary: ], ) + def get_short_names(self) -> set[str]: + """Returns set of non-empty short names (letter codes) of regulation groups part of the library.""" + return { + regulation_group.short_name + for category in self.regulation_group_categories + for regulation_group in category.regulation_groups + if regulation_group.short_name + } + @dataclass class RegulationLibrary: diff --git a/arho_feature_template/core/plan_manager.py b/arho_feature_template/core/plan_manager.py index 5c6d66e..b5c1f75 100644 --- a/arho_feature_template/core/plan_manager.py +++ b/arho_feature_template/core/plan_manager.py @@ -166,7 +166,7 @@ def edit_regulation_group(self, regulation_group: RegulationGroup): self._open_regulation_group_form(regulation_group) def _open_regulation_group_form(self, regulation_group: RegulationGroup): - regulation_group_form = PlanRegulationGroupForm(regulation_group) + regulation_group_form = PlanRegulationGroupForm(regulation_group, self.active_plan_regulation_group_library) if regulation_group_form.exec_(): if regulation_group_form.save_as_config: save_regulation_group_as_config(regulation_group_form.model) @@ -314,7 +314,7 @@ def _plan_feature_geom_digitized(self, feature: QgsFeature): plan_feature.geom = feature.geometry() attribute_form = PlanFeatureForm( - plan_feature, title, [*self.regulation_group_libraries, self.active_plan_regulation_group_library] + plan_feature, title, self.regulation_group_libraries, self.active_plan_regulation_group_library ) if attribute_form.exec_(): save_plan_feature(attribute_form.model) @@ -326,7 +326,7 @@ def edit_plan_feature(self, feature: QgsFeature, layer_name: str): title = plan_feature.name if plan_feature.name else layer_name attribute_form = PlanFeatureForm( - plan_feature, title, [*self.regulation_group_libraries, self.active_plan_regulation_group_library] + plan_feature, title, self.regulation_group_libraries, self.active_plan_regulation_group_library ) if attribute_form.exec_(): save_plan_feature(attribute_form.model) diff --git a/arho_feature_template/gui/dialogs/plan_feature_form.py b/arho_feature_template/gui/dialogs/plan_feature_form.py index d945fd8..1c1e24d 100644 --- a/arho_feature_template/gui/dialogs/plan_feature_form.py +++ b/arho_feature_template/gui/dialogs/plan_feature_form.py @@ -10,6 +10,7 @@ QDialog, QDialogButtonBox, QLineEdit, + QMessageBox, QScrollArea, QSizePolicy, QSpacerItem, @@ -39,7 +40,11 @@ class PlanFeatureForm(QDialog, FormClass): # type: ignore """Parent class for feature forms for adding and modifying feature attribute data.""" def __init__( - self, plan_feature: PlanFeature, form_title: str, regulation_group_libraries: list[RegulationGroupLibrary] + self, + plan_feature: PlanFeature, + form_title: str, + regulation_group_libraries: list[RegulationGroupLibrary], + active_plan_regulation_groups_library: RegulationGroupLibrary, ): super().__init__() self.setupUi(self) @@ -57,7 +62,9 @@ def __init__( self.regulation_groups_tree_layout: QVBoxLayout # INIT - self.regulation_group_libraries = regulation_group_libraries + self.existing_group_short_names = active_plan_regulation_groups_library.get_short_names() + self.active_plan_regulation_groups_library = active_plan_regulation_groups_library + self.regulation_group_libraries = [*regulation_group_libraries, active_plan_regulation_groups_library] self.plan_regulation_group_libraries_combobox.addItems( library.name for library in self.regulation_group_libraries ) @@ -97,6 +104,44 @@ def _remove_spacer(self): self.plan_regulation_group_scrollarea_contents.layout().removeItem(self.scroll_area_spacer) self.scroll_area_spacer = None + def _check_regulation_group_short_names(self) -> bool: + seen_names = set() + existing_names = set() + duplicate_names = set() + + for reg_group_widget in self.regulation_group_widgets: + short_name = reg_group_widget.short_name.text() + if not short_name: + continue + + if short_name in self.existing_group_short_names: + existing_names.add(short_name) + if short_name in seen_names: + duplicate_names.add(short_name) + seen_names.add(short_name) + + def format_names(names, last_item_conjucation: str = "ja"): + names = list(names) + formatted_names = ", ".join(f"'{name}'" for name in names[:-1]) + formatted_names += f" {last_item_conjucation} " if len(names) > 1 else "" + formatted_names += f"'{names[-1]}'" if names else "" + return formatted_names + + if existing_names: + if len(existing_names) == 1: + msg = f"Kaavamääräysryhmä lyhyellä nimellä {format_names(existing_names)} on jo olemassa." + else: + msg = f"Kaavamääräysryhmät lyhyillä nimillä {format_names(existing_names)} ovat jo olemassa." + QMessageBox.critical(self, "Virhe", msg) + return False + + if duplicate_names: + msg = f"Lomakkeella on useita kaavamääräysryhmiä, joilla on lyhyt nimi {format_names(duplicate_names, 'tai')}." + QMessageBox.critical(self, "Virhe", msg) + return False + + return True + def add_selected_plan_regulation_group(self, item: QTreeWidgetItem, column: int): if not item.parent(): return @@ -113,7 +158,9 @@ def add_plan_regulation_group(self, definition: RegulationGroup): self._add_spacer() def open_plan_regulation_group_form(self, regulation_group_widget: RegulationGroupWidget): - group_as_form = PlanRegulationGroupForm(regulation_group_widget.into_model()) + group_as_form = PlanRegulationGroupForm( + regulation_group_widget.into_model(), self.active_plan_regulation_groups_library + ) if group_as_form.exec_(): regulation_group_widget.from_model(group_as_form.model) @@ -146,5 +193,6 @@ def into_model(self) -> PlanFeature: ) def _on_ok_clicked(self): - self.model = self.into_model() - self.accept() + if self._check_regulation_group_short_names(): + self.model = self.into_model() + self.accept() diff --git a/arho_feature_template/gui/dialogs/plan_regulation_group_form.py b/arho_feature_template/gui/dialogs/plan_regulation_group_form.py index 4053060..a7bf234 100644 --- a/arho_feature_template/gui/dialogs/plan_regulation_group_form.py +++ b/arho_feature_template/gui/dialogs/plan_regulation_group_form.py @@ -12,6 +12,7 @@ QDialogButtonBox, QHBoxLayout, QLabel, + QMessageBox, QSizePolicy, QTextBrowser, QTreeWidgetItem, @@ -23,6 +24,7 @@ Regulation, RegulationConfig, RegulationGroup, + RegulationGroupLibrary, RegulationLibrary, ) from arho_feature_template.gui.components.plan_proposition_widget import PropositionWidget @@ -45,7 +47,9 @@ class PlanRegulationGroupForm(QDialog, FormClass): # type: ignore """Form to create a new plan regulation group.""" - def __init__(self, regulation_group: RegulationGroup): + def __init__( + self, regulation_group: RegulationGroup, active_plan_regulation_groups_library: RegulationGroupLibrary + ): super().__init__() self.setupUi(self) @@ -74,6 +78,7 @@ def __init__(self, regulation_group: RegulationGroup): self.regulation_widgets: list[RegulationWidget] = [] self.proposition_widgets: list[PropositionWidget] = [] self.save_as_config = False + self.existing_group_short_names = active_plan_regulation_groups_library.get_short_names() # Initialize regulation library self.regulations_selection_widget = TreeWithSearchWidget() @@ -137,6 +142,14 @@ def _initalize_regulation_from_config(self, config: RegulationConfig, parent: QT for child_config in config.child_regulations: self._initalize_regulation_from_config(child_config, item) + def _check_short_name(self) -> bool: + short_name = self.short_name.text() + if short_name and short_name in self.existing_group_short_names: + msg = f"Kaavamääräysryhmä lyhyellä nimellä '{short_name}' on jo olemassa." + QMessageBox.critical(self, "Virhe", msg) + return False + return True + def update_selected_regulation(self, item: QTreeWidgetItem, column: int): config: RegulationConfig = item.data(column, Qt.UserRole) # Retrieve the associated config self.regulation_info.setText(config.description) @@ -190,5 +203,6 @@ def into_model(self) -> RegulationGroup: ) def _on_ok_clicked(self): - self.model = self.into_model() - self.accept() + if self._check_short_name(): + self.model = self.into_model() + self.accept()