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 10f8088..b5c1f75 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()) @@ -163,17 +166,17 @@ 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) 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/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() 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: