From 33a24f550a0b066c36f267860f5b9450372ddb33 Mon Sep 17 00:00:00 2001 From: Niko Aarnio Date: Tue, 28 Jan 2025 10:36:54 +0200 Subject: [PATCH 1/3] add plan documents --- arho_feature_template/core/models.py | 25 +- arho_feature_template/core/plan_manager.py | 21 ++ .../gui/components/code_combobox.py | 7 +- .../gui/components/plan_document_widget.py | 169 +++++++++++++ .../gui/components/plan_document_widget.ui | 233 ++++++++++++++++++ .../gui/dialogs/plan_attribute_form.py | 33 ++- .../gui/dialogs/plan_attribute_form.ui | 55 +++++ .../project/layers/code_layers.py | 30 +++ .../project/layers/plan_layers.py | 43 ++++ 9 files changed, 612 insertions(+), 4 deletions(-) create mode 100644 arho_feature_template/gui/components/plan_document_widget.py create mode 100644 arho_feature_template/gui/components/plan_document_widget.ui diff --git a/arho_feature_template/core/models.py b/arho_feature_template/core/models.py index ad49087..55643d7 100644 --- a/arho_feature_template/core/models.py +++ b/arho_feature_template/core/models.py @@ -15,6 +15,8 @@ from arho_feature_template.utils.misc_utils import LANGUAGE, get_layer_by_name, iface if TYPE_CHECKING: + from datetime import datetime + from qgis.core import QgsFeature, QgsGeometry @@ -255,7 +257,7 @@ def from_feature(cls, feature: QgsFeature) -> RegulationConfig: id=feature["id"], regulation_code=feature["value"], name=feature["name"][LANGUAGE], - description=feature["description"][LANGUAGE], + description=feature["description"][LANGUAGE] if feature["description"] else "", status=feature["status"], level=feature["level"], parent_id=feature["parent_id"], @@ -370,5 +372,26 @@ class Plan: producers_plan_identifier: str | None = None organisation_id: str | None = None general_regulations: list[RegulationGroup] = field(default_factory=list) + documents: list[Document] = field(default_factory=list) geom: QgsGeometry | None = None id_: int | None = None + + +@dataclass +class Document: + name: str | None = None + url: str | None = None + type_of_document_id: str | None = None + decision: bool | None = None + # permanent_document_identifier: str | None = None + category_of_publicity_id: str | None = None + personal_data_content_id: str | None = None + retention_time_id: str | None = None + language_id: str | None = None + document_date: datetime | None = None + # exported_at: str | None = None + # exported_file_key: + confirmation_date: datetime | None = None + arrival_date: datetime | None = None + plan_id: int | None = None + id_: int | None = None diff --git a/arho_feature_template/core/plan_manager.py b/arho_feature_template/core/plan_manager.py index fc5cf4e..c4bbcf8 100644 --- a/arho_feature_template/core/plan_manager.py +++ b/arho_feature_template/core/plan_manager.py @@ -10,6 +10,7 @@ from arho_feature_template.core.lambda_service import LambdaService from arho_feature_template.core.models import ( + Document, FeatureTemplateLibrary, Plan, PlanFeature, @@ -28,6 +29,7 @@ from arho_feature_template.gui.tools.inspect_plan_features_tool import InspectPlanFeatures from arho_feature_template.project.layers.code_layers import PlanRegulationGroupTypeLayer, code_layers from arho_feature_template.project.layers.plan_layers import ( + DocumentLayer, LandUseAreaLayer, LandUsePointLayer, LineLayer, @@ -514,6 +516,11 @@ def save_plan(plan: Plan) -> QgsFeature: regulation_group_feature = save_regulation_group(regulation_group, plan_id) save_regulation_group_association(regulation_group_feature["id"], PlanLayer.name, plan_id) + # Save documents + for document in plan.documents: + document.plan_id = feature["id"] + save_document(document) + return feature @@ -660,3 +667,17 @@ def delete_proposition(proposition: Proposition): layer = PlanPropositionLayer.get_from_project() _delete_feature(feature, layer, "Kaavasuosituksen poisto") + + +def save_document(document: Document) -> QgsFeature: + feature = DocumentLayer.feature_from_model(document) + layer = DocumentLayer.get_from_project() + + _save_feature( + feature=feature, + layer=layer, + id_=document.id_, + edit_text="Asiakirjan lisäys" if document.id_ is None else "Asiakirjan muokkaus", + ) + + return feature diff --git a/arho_feature_template/gui/components/code_combobox.py b/arho_feature_template/gui/components/code_combobox.py index fc7b1a4..5368aad 100644 --- a/arho_feature_template/gui/components/code_combobox.py +++ b/arho_feature_template/gui/components/code_combobox.py @@ -83,7 +83,12 @@ def populate_from_code_layer(self, layer_type: type[AbstractCodeLayer]) -> None: items[code_feature["id"]] = item item.setText(0, code_feature["name"][LANGUAGE]) - item.setToolTip(0, code_feature["description"][LANGUAGE]) + item.setToolTip( + 0, + code_feature["description"][LANGUAGE] + if code_feature["description"] + else code_feature["name"][LANGUAGE], + ) item.setData(0, Qt.UserRole, code_feature["id"]) if code_feature["value"] in layer_type.category_only_codes: diff --git a/arho_feature_template/gui/components/plan_document_widget.py b/arho_feature_template/gui/components/plan_document_widget.py new file mode 100644 index 0000000..f97c226 --- /dev/null +++ b/arho_feature_template/gui/components/plan_document_widget.py @@ -0,0 +1,169 @@ +from __future__ import annotations + +from importlib import resources +from typing import TYPE_CHECKING + +from qgis.core import QgsApplication +from qgis.gui import QgsDateTimeEdit +from qgis.PyQt import uic +from qgis.PyQt.QtCore import Qt, pyqtSignal +from qgis.PyQt.QtWidgets import QCheckBox, QFormLayout, QLabel, QMenu, QToolButton, QWidget + +from arho_feature_template.core.models import Document +from arho_feature_template.project.layers.code_layers import ( + CategoryOfPublicityLayer, + LanguageLayer, + PersonalDataContentLayer, + RetentionTimeLayer, + TypeOfDocumentLayer, +) + +if TYPE_CHECKING: + from datetime import datetime + + from qgis.PyQt.QtWidgets import QLineEdit, QPushButton + + from arho_feature_template.gui.components.code_combobox import CodeComboBox, HierarchicalCodeComboBox + + +ui_path = resources.files(__package__) / "plan_document_widget.ui" +FormClass, _ = uic.loadUiType(ui_path) + + +class DocumentWidget(QWidget, FormClass): # type: ignore + """A widget representation of a plan document.""" + + delete_signal = pyqtSignal(QWidget) + + def __init__(self, document: Document, parent=None): + super().__init__(parent) + self.setupUi(self) + + # TYPES + self.name: QLineEdit + self.url: QLineEdit + self.url_label: QLabel + self.document_type: CodeComboBox + self.document_type_label: QLabel + self.publicity: CodeComboBox + self.publicity_label: QLabel + self.decision: QCheckBox + self.decision_label: QLabel + self.language: CodeComboBox + self.language_label: QLabel + self.retention_time: HierarchicalCodeComboBox + self.retention_time_label: QLabel + self.personal_data_content: CodeComboBox + self.personal_data_content_label: QLabel + self.document_date: QgsDateTimeEdit + self.document_date_label: QLabel + + self.add_field_btn: QPushButton + self.del_btn: QPushButton + self.form_layout: QFormLayout + self.expand_hide_btn: QToolButton + + # INIT + self.document = document + + self.document_type.populate_from_code_layer(TypeOfDocumentLayer) + self.publicity.populate_from_code_layer(CategoryOfPublicityLayer) + self.language.populate_from_code_layer(LanguageLayer) + self.language.setCurrentIndex(1) + self.retention_time.populate_from_code_layer(RetentionTimeLayer) + self.personal_data_content.populate_from_code_layer(PersonalDataContentLayer) + + # List of widgets for hiding / showing + self.widgets: list[tuple[QLabel, QWidget]] = [ + (self.url_label, self.url), + (self.document_type_label, self.document_type), + (self.publicity_label, self.publicity), + (self.decision_label, self.decision), + (self.language_label, self.language), + (self.retention_time_label, self.retention_time), + (self.personal_data_content_label, self.personal_data_content), + (self.document_date_label, self.document_date), + ] + self.arrival_date_widget: QgsDateTimeEdit | None = None + self.confirmation_date_widget: QgsDateTimeEdit | None = None + + add_field_menu = QMenu(self) + add_field_menu.addAction("Saapumispäivämäärä").triggered.connect(self._add_arrival_date) + add_field_menu.addAction("Vahvistuspäivämäärä").triggered.connect(self._add_confirmation_date) + self.add_field_btn.setMenu(add_field_menu) + self.add_field_btn.setIcon(QgsApplication.getThemeIcon("mActionAdd.svg")) + self.del_btn.setIcon(QgsApplication.getThemeIcon("mActionDeleteSelected.svg")) + self.del_btn.clicked.connect(lambda: self.delete_signal.emit(self)) + + self.expanded = True + self.expand_hide_btn.clicked.connect(self._on_expand_hide_btn_clicked) + + # Set values from input Document model + self.name.setText(document.name) + self.url.setText(document.url) + self.document_type.set_value(document.type_of_document_id) + self.publicity.set_value(document.category_of_publicity_id) + self.decision.setChecked(document.decision is True) + if document.language_id: + self.language.set_value(document.language_id) + self.retention_time.set_value(document.retention_time_id) + self.personal_data_content.set_value(document.personal_data_content_id) + if document.arrival_date: + self._add_arrival_date(document.arrival_date) + if document.confirmation_date: + self._add_confirmation_date(document.confirmation_date) + + def _add_widgets(self, label: QLabel, widget: QWidget): + self.form_layout.addRow(label, widget) + self.widgets.append((label, widget)) + if not self.expanded: + self._on_expand_hide_btn_clicked() + + def _add_arrival_date(self, default_value: datetime | None = None): + if not self.arrival_date_widget: + self.arrival_date_widget = QgsDateTimeEdit() + self.arrival_date_widget.setDisplayFormat("d.M.yyyy") + if default_value: + self.arrival_date_widget.setDateTime(default_value) + self._add_widgets(QLabel("Saapumispäivämäärä"), self.arrival_date_widget) + + def _add_confirmation_date(self, default_value: datetime | None = None): + if not self.confirmation_date_widget: + self.confirmation_date_widget = QgsDateTimeEdit() + self.confirmation_date_widget.setDisplayFormat("d.M.yyyy") + if default_value: + self.confirmation_date_widget.setDateTime(default_value) + self._add_widgets(QLabel("Vahvistuspäivämäärä"), self.confirmation_date_widget) + + def _on_expand_hide_btn_clicked(self): + if self.expanded: + for label, value_widget in self.widgets: + self.form_layout.removeWidget(label) + label.hide() + self.form_layout.removeWidget(value_widget) + value_widget.hide() + self.expand_hide_btn.setArrowType(Qt.ArrowType.DownArrow) + self.expanded = False + else: + for label, value_widget in self.widgets: + self.form_layout.addRow(label, value_widget) + label.show() + value_widget.show() + self.expand_hide_btn.setArrowType(Qt.ArrowType.UpArrow) + self.expanded = True + + def into_model(self) -> Document: + return Document( + name=self.name.text(), + url=self.url.text(), + type_of_document_id=self.document_type.value(), + decision=self.decision.isChecked(), + category_of_publicity_id=self.publicity.value(), + personal_data_content_id=self.personal_data_content.value(), + retention_time_id=self.retention_time.value(), + language_id=self.language.value(), + document_date=self.document_date.date(), + # exported_at=self.document.exported_at, + plan_id=self.document.plan_id, + id_=self.document.id_, + ) diff --git a/arho_feature_template/gui/components/plan_document_widget.ui b/arho_feature_template/gui/components/plan_document_widget.ui new file mode 100644 index 0000000..9b26f0e --- /dev/null +++ b/arho_feature_template/gui/components/plan_document_widget.ui @@ -0,0 +1,233 @@ + + + plan_document + + + + 0 + 0 + 488 + 327 + + + + Form + + + + + + + + + 11 + 75 + true + + + + Asiakirja + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Muu tieto + + + + + + + + + + + 0 + 0 + + + + + 30 + 16777215 + + + + + + + + + + + + + + + Nimi + + + + + + + + + + 0 + 0 + + + + false + + + + + + + Laajenna + + + false + + + Qt::UpArrow + + + + + + + + + URL + + + + + + + + + + Tyyppi + + + + + + + + + + Julkisuusluokka + + + + + + + + + + Kieli + + + + + + + + + + + + + + + + Päätös + + + + + + + + 0 + 0 + + + + + + + + + + + Henkilötietosisältö + + + + + + + Säilytysaika + + + + + + + d.M.yyyy + + + + + + + Asiakirjan päivämäärä + + + + + + + + + + QgsDateTimeEdit + QDateTimeEdit +
qgsdatetimeedit.h
+
+ + CodeComboBox + QComboBox +
arho_feature_template.gui.components.code_combobox
+
+ + HierarchicalCodeComboBox + QComboBox +
arho_feature_template.gui.components.code_combobox
+
+
+ + +
diff --git a/arho_feature_template/gui/dialogs/plan_attribute_form.py b/arho_feature_template/gui/dialogs/plan_attribute_form.py index 2cae050..c2a9bd5 100644 --- a/arho_feature_template/gui/dialogs/plan_attribute_form.py +++ b/arho_feature_template/gui/dialogs/plan_attribute_form.py @@ -3,6 +3,7 @@ from importlib import resources from typing import TYPE_CHECKING +from qgis.core import QgsApplication from qgis.PyQt import uic from qgis.PyQt.QtCore import Qt from qgis.PyQt.QtWidgets import ( @@ -16,7 +17,8 @@ QTreeWidgetItem, ) -from arho_feature_template.core.models import Plan, RegulationGroup, RegulationGroupLibrary +from arho_feature_template.core.models import Document, Plan, RegulationGroup, RegulationGroupLibrary +from arho_feature_template.gui.components.plan_document_widget import DocumentWidget from arho_feature_template.gui.components.plan_regulation_group_widget import RegulationGroupWidget from arho_feature_template.gui.components.tree_with_search_widget import TreeWithSearchWidget from arho_feature_template.gui.dialogs.plan_regulation_group_form import PlanRegulationGroupForm @@ -28,7 +30,7 @@ from arho_feature_template.utils.misc_utils import disconnect_signal if TYPE_CHECKING: - from qgis.PyQt.QtWidgets import QComboBox, QLineEdit, QTextEdit, QVBoxLayout, QWidget + from qgis.PyQt.QtWidgets import QComboBox, QLineEdit, QPushButton, QTextEdit, QVBoxLayout, QWidget from arho_feature_template.gui.components.code_combobox import CodeComboBox, HierarchicalCodeComboBox @@ -51,6 +53,10 @@ class PlanAttributeForm(QDialog, FormClass): # type: ignore plan_regulation_group_libraries_combobox: QComboBox regulation_groups_tree_layout: QVBoxLayout + documents_scroll_contents: QWidget + documents_layout: QVBoxLayout + add_document_btn: QPushButton + button_box: QDialogButtonBox def __init__(self, plan: Plan, regulation_group_libraries: list[RegulationGroupLibrary], parent=None): @@ -97,6 +103,13 @@ def __init__(self, plan: Plan, regulation_group_libraries: list[RegulationGroupL for regulation_group in plan.general_regulations: self.add_plan_regulation_group(regulation_group) + self.document_widgets: list[DocumentWidget] = [] + for document in plan.documents: + self.add_document(document) + + self.add_document_btn.clicked.connect(self.add_new_document) + self.add_document_btn.setIcon(QgsApplication.getThemeIcon("mActionAdd.svg")) + self.button_box.button(QDialogButtonBox.Ok).setEnabled(False) self.button_box.accepted.connect(self._on_ok_clicked) @@ -161,6 +174,21 @@ def init_plan_regulation_group_library(self, library: RegulationGroupLibrary): group_definition.name, group_definition, category_item ) + def add_new_document(self): + self.add_document(Document()) + + def add_document(self, document: Document): + widget = DocumentWidget(document, parent=self.documents_scroll_contents) + widget.delete_signal.connect(self.delete_document) + self.documents_layout.insertWidget(1, widget) + self.document_widgets.append(widget) + + def delete_document(self, document_widget: DocumentWidget): + document_widget.delete_signal.disconnect() + self.documents_layout.removeWidget(document_widget) + self.document_widgets.remove(document_widget) + document_widget.deleteLater() + # --- def into_model(self) -> Plan: @@ -176,6 +204,7 @@ def into_model(self) -> Plan: matter_management_identifier=self.matter_management_identifier_line_edit.text() or None, lifecycle_status_id=self.lifecycle_status_combo_box.value(), general_regulations=[reg_group_widget.into_model() for reg_group_widget in self.regulation_group_widgets], + documents=[document_widget.into_model() for document_widget in self.document_widgets], geom=self.plan.geom, ) diff --git a/arho_feature_template/gui/dialogs/plan_attribute_form.ui b/arho_feature_template/gui/dialogs/plan_attribute_form.ui index 106411a..c31b0eb 100644 --- a/arho_feature_template/gui/dialogs/plan_attribute_form.ui +++ b/arho_feature_template/gui/dialogs/plan_attribute_form.ui @@ -290,6 +290,61 @@ + + + Asiakirjat + + + + + + Qt::ScrollBarAsNeeded + + + true + + + + + 0 + 0 + 831 + 562 + + + + + + + + 0 + 0 + + + + Lisää asiakirja + + + + + + + Qt::Vertical + + + + 20 + 20 + + + + + + + + + + diff --git a/arho_feature_template/project/layers/code_layers.py b/arho_feature_template/project/layers/code_layers.py index e96c706..cd6bce8 100644 --- a/arho_feature_template/project/layers/code_layers.py +++ b/arho_feature_template/project/layers/code_layers.py @@ -91,4 +91,34 @@ class VerbalRegulationType(AbstractCodeLayer): category_only_codes: ClassVar[list[str]] = ["maarayksenTyyppi"] +class CategoryOfPublicityLayer(AbstractCodeLayer): + name = "Julkisuusluokka" + + category_only_codes: ClassVar[list[str]] = [] + + +class TypeOfDocumentLayer(AbstractCodeLayer): + name = "Asiakirjatyyppi" + + category_only_codes: ClassVar[list[str]] = [] + + +class LanguageLayer(AbstractCodeLayer): + name = "Kieli" + + category_only_codes: ClassVar[list[str]] = [] + + +class PersonalDataContentLayer(AbstractCodeLayer): + name = "Henkilötietosisältö" + + category_only_codes: ClassVar[list[str]] = [] + + +class RetentionTimeLayer(AbstractCodeLayer): + name = "Säilytysaika" + + category_only_codes: ClassVar[list[str]] = [] + + code_layers = AbstractCodeLayer.__subclasses__() diff --git a/arho_feature_template/project/layers/plan_layers.py b/arho_feature_template/project/layers/plan_layers.py index 9437718..7c5e740 100644 --- a/arho_feature_template/project/layers/plan_layers.py +++ b/arho_feature_template/project/layers/plan_layers.py @@ -11,6 +11,7 @@ from qgis.utils import iface from arho_feature_template.core.models import ( + Document, Plan, PlanFeature, Proposition, @@ -110,6 +111,10 @@ def model_from_feature(cls, feature: QgsFeature) -> Plan: for feat in general_regulation_features if feat is not None ], + documents=[ + DocumentLayer.model_from_feature(feat) + for feat in DocumentLayer.get_features_by_attribute_value("plan_id", feature["id"]) + ], id_=feature["id"], ) @@ -405,6 +410,44 @@ class DocumentLayer(AbstractPlanLayer): name = "Asiakirjat" filter_template = Template("plan_id = '$plan_id'") + @classmethod + def feature_from_model(cls, model: Document) -> QgsFeature: + feature = cls.initialize_feature_from_model(model) + + feature["name"] = {LANGUAGE: model.name} + feature["url"] = model.url + feature["type_of_document_id"] = model.type_of_document_id + feature["decision"] = model.decision + feature["category_of_publicity_id"] = model.category_of_publicity_id + feature["personal_data_content_id"] = model.personal_data_content_id + feature["retention_time_id"] = model.retention_time_id + feature["language_id"] = model.language_id + feature["document_date"] = model.document_date + feature["arrival_date"] = model.arrival_date + feature["confirmation_date"] = model.confirmation_date + feature["plan_id"] = model.plan_id + feature["id"] = model.id_ if model.id_ else feature["id"] + + return feature + + @classmethod + def model_from_feature(cls, feature: QgsFeature) -> Document: + return Document( + name=feature["name"][LANGUAGE], + url=feature["url"], + type_of_document_id=feature["type_of_document_id"], + decision=feature["decision"], + category_of_publicity_id=feature["category_of_publicity_id"], + personal_data_content_id=feature["personal_data_content_id"], + retention_time_id=feature["retention_time_id"], + language_id=feature["language_id"], + document_date=feature["document_date"], + arrival_date=feature["arrival_date"], + confirmation_date=feature["confirmation_date"], + plan_id=feature["plan_id"], + id_=feature["id"], + ) + class SourceDataLayer(AbstractPlanLayer): name = "Lähtötietoaineistot" From eb397ab8dad480e03c5f75833ba2cd21a5aa912e Mon Sep 17 00:00:00 2001 From: Niko Aarnio Date: Tue, 28 Jan 2025 13:15:26 +0200 Subject: [PATCH 2/3] update plan proposition model (remove 'name' field) --- arho_feature_template/core/models.py | 1 - .../gui/components/plan_proposition_widget.py | 6 +-- .../gui/components/plan_proposition_widget.ui | 48 ++++++------------- .../gui/dialogs/plan_regulation_group_form.py | 2 +- .../project/layers/plan_layers.py | 2 - 5 files changed, 18 insertions(+), 41 deletions(-) diff --git a/arho_feature_template/core/models.py b/arho_feature_template/core/models.py index 55643d7..416fb40 100644 --- a/arho_feature_template/core/models.py +++ b/arho_feature_template/core/models.py @@ -286,7 +286,6 @@ class Regulation: @dataclass class Proposition: - name: str value: str theme_id: str | None = None proposition_number: int | None = None diff --git a/arho_feature_template/gui/components/plan_proposition_widget.py b/arho_feature_template/gui/components/plan_proposition_widget.py index dbff0ad..986df25 100644 --- a/arho_feature_template/gui/components/plan_proposition_widget.py +++ b/arho_feature_template/gui/components/plan_proposition_widget.py @@ -14,7 +14,7 @@ from arho_feature_template.project.layers.code_layers import PlanThemeLayer if TYPE_CHECKING: - from qgis.PyQt.QtWidgets import QLineEdit, QPushButton + from qgis.PyQt.QtWidgets import QPushButton ui_path = resources.files(__package__) / "plan_proposition_widget.ui" FormClass, _ = uic.loadUiType(ui_path) @@ -30,7 +30,6 @@ def __init__(self, proposition: Proposition, parent=None): self.setupUi(self) # TYPES - self.name: QLineEdit self.value_label: QLabel self.text_input: QTextEdit self.add_field_btn: QPushButton @@ -58,7 +57,7 @@ def __init__(self, proposition: Proposition, parent=None): self.expanded = True self.expand_hide_btn.clicked.connect(self._on_expand_hide_btn_clicked) - self.name.setText(proposition.name) + # self.name.setText(proposition.name) self.text_input.setText(proposition.value) if proposition.theme_id: self._add_theme(proposition.theme_id) @@ -103,7 +102,6 @@ def _on_expand_hide_btn_clicked(self): def into_model(self) -> Proposition: return Proposition( - name=self.name.text(), value=self.text_input.toPlainText(), theme_id=self.theme_widget.value() if self.theme_widget else None, proposition_number=self.proposition_number_widget.get_value() if self.proposition_number_widget else None, diff --git a/arho_feature_template/gui/components/plan_proposition_widget.ui b/arho_feature_template/gui/components/plan_proposition_widget.ui index 73bbcb1..c1b9049 100644 --- a/arho_feature_template/gui/components/plan_proposition_widget.ui +++ b/arho_feature_template/gui/components/plan_proposition_widget.ui @@ -7,7 +7,7 @@ 0 0 441 - 207 + 125 @@ -87,49 +87,31 @@ + + + + Laajenna + + + false + + + Qt::UpArrow + + + - - - Otsikko - - - - - - - - - false - - - - - - - Laajenna - - - false - - - Qt::UpArrow - - - - - - Sisältö - + 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 db0c100..cf43229 100644 --- a/arho_feature_template/gui/dialogs/plan_regulation_group_form.py +++ b/arho_feature_template/gui/dialogs/plan_regulation_group_form.py @@ -122,7 +122,7 @@ def delete_regulation(self, regulation_widget: RegulationWidget): regulation_widget.deleteLater() def add_new_proposition(self): - proposition = Proposition(name="", value="") + proposition = Proposition(value="") self.add_proposition(proposition) def add_proposition(self, proposition: Proposition): diff --git a/arho_feature_template/project/layers/plan_layers.py b/arho_feature_template/project/layers/plan_layers.py index 7c5e740..6f9bf95 100644 --- a/arho_feature_template/project/layers/plan_layers.py +++ b/arho_feature_template/project/layers/plan_layers.py @@ -381,7 +381,6 @@ class PlanPropositionLayer(AbstractPlanLayer): def feature_from_model(cls, model: Proposition) -> QgsFeature: feature = cls.initialize_feature_from_model(model) - feature["name"] = {LANGUAGE: model.name} feature["text_value"] = {LANGUAGE: model.value} feature["plan_regulation_group_id"] = model.regulation_group_id_ feature["ordering"] = model.proposition_number @@ -393,7 +392,6 @@ def feature_from_model(cls, model: Proposition) -> QgsFeature: @classmethod def model_from_feature(cls, feature: QgsFeature) -> Proposition: return Proposition( - name=feature["name"][LANGUAGE], value=feature["text_value"][LANGUAGE], regulation_group_id_=feature["plan_regulation_group_id"], proposition_number=feature["ordering"], From 3237eada3be8d66d9ca6eb1c05d806b63aeb252f Mon Sep 17 00:00:00 2001 From: Niko Aarnio Date: Tue, 28 Jan 2025 13:45:08 +0200 Subject: [PATCH 3/3] add deleting and required field checking for plan documents --- arho_feature_template/core/plan_manager.py | 13 +++++++++---- .../gui/components/plan_document_widget.py | 18 ++++++++++++++++++ .../gui/dialogs/plan_attribute_form.py | 5 +++++ .../project/layers/plan_layers.py | 9 +++++++++ 4 files changed, 41 insertions(+), 4 deletions(-) diff --git a/arho_feature_template/core/plan_manager.py b/arho_feature_template/core/plan_manager.py index c4bbcf8..11596ed 100644 --- a/arho_feature_template/core/plan_manager.py +++ b/arho_feature_template/core/plan_manager.py @@ -498,10 +498,11 @@ def save_plan(plan: Plan) -> QgsFeature: edit_text="Kaavan muokkaus" if editing else "Kaavan luominen", ) - # Check for deleted general regulations + plan_id = feature["id"] if editing: + # Check for deleted general regulations for association in RegulationGroupAssociationLayer.get_dangling_associations( - plan.general_regulations, feature["id"], PlanLayer.name + plan.general_regulations, plan_id, PlanLayer.name ): _delete_feature( association, @@ -509,16 +510,20 @@ def save_plan(plan: Plan) -> QgsFeature: "Kaavamääräysryhmän assosiaation poisto", ) + # Check for documents to be deleted + doc_layer = DocumentLayer.get_from_project() + for doc_feature in DocumentLayer.get_documents_to_delete(plan.documents, plan): + _delete_feature(doc_feature, doc_layer, "Asiakirjan poisto") + # Save general regulations if plan.general_regulations: for regulation_group in plan.general_regulations: - plan_id = feature["id"] regulation_group_feature = save_regulation_group(regulation_group, plan_id) save_regulation_group_association(regulation_group_feature["id"], PlanLayer.name, plan_id) # Save documents for document in plan.documents: - document.plan_id = feature["id"] + document.plan_id = plan_id save_document(document) return feature diff --git a/arho_feature_template/gui/components/plan_document_widget.py b/arho_feature_template/gui/components/plan_document_widget.py index f97c226..a9d4941 100644 --- a/arho_feature_template/gui/components/plan_document_widget.py +++ b/arho_feature_template/gui/components/plan_document_widget.py @@ -33,6 +33,7 @@ class DocumentWidget(QWidget, FormClass): # type: ignore """A widget representation of a plan document.""" + document_edited = pyqtSignal() delete_signal = pyqtSignal(QWidget) def __init__(self, document: Document, parent=None): @@ -73,6 +74,13 @@ def __init__(self, document: Document, parent=None): self.retention_time.populate_from_code_layer(RetentionTimeLayer) self.personal_data_content.populate_from_code_layer(PersonalDataContentLayer) + self.name.textChanged.connect(self.document_edited.emit) + self.document_type.currentIndexChanged.connect(self.document_edited.emit) + self.publicity.currentIndexChanged.connect(self.document_edited.emit) + self.language.currentIndexChanged.connect(self.document_edited.emit) + self.retention_time.currentIndexChanged.connect(self.document_edited.emit) + self.personal_data_content.currentIndexChanged.connect(self.document_edited.emit) + # List of widgets for hiding / showing self.widgets: list[tuple[QLabel, QWidget]] = [ (self.url_label, self.url), @@ -113,6 +121,16 @@ def __init__(self, document: Document, parent=None): if document.confirmation_date: self._add_confirmation_date(document.confirmation_date) + def is_ok(self) -> bool: + return ( + self.name.text() != "" + and self.document_type.value() is not None + and self.publicity.value() is not None + and self.language.value() is not None + and self.retention_time.value() is not None + and self.personal_data_content.value() is not None + ) + def _add_widgets(self, label: QLabel, widget: QWidget): self.form_layout.addRow(label, widget) self.widgets.append((label, widget)) diff --git a/arho_feature_template/gui/dialogs/plan_attribute_form.py b/arho_feature_template/gui/dialogs/plan_attribute_form.py index c2a9bd5..e746ccd 100644 --- a/arho_feature_template/gui/dialogs/plan_attribute_form.py +++ b/arho_feature_template/gui/dialogs/plan_attribute_form.py @@ -122,6 +122,7 @@ def _check_required_fields(self) -> None: and self.plan_type_combo_box.value() is not None and self.organisation_combo_box.value() is not None and self.lifecycle_status_combo_box.value() is not None + and all(document_widget.is_ok() for document_widget in self.document_widgets) ): ok_button.setEnabled(True) else: @@ -176,18 +177,22 @@ def init_plan_regulation_group_library(self, library: RegulationGroupLibrary): def add_new_document(self): self.add_document(Document()) + self._check_required_fields() def add_document(self, document: Document): widget = DocumentWidget(document, parent=self.documents_scroll_contents) widget.delete_signal.connect(self.delete_document) + widget.document_edited.connect(self._check_required_fields) self.documents_layout.insertWidget(1, widget) self.document_widgets.append(widget) def delete_document(self, document_widget: DocumentWidget): document_widget.delete_signal.disconnect() + document_widget.document_edited.disconnect() self.documents_layout.removeWidget(document_widget) self.document_widgets.remove(document_widget) document_widget.deleteLater() + self._check_required_fields() # --- diff --git a/arho_feature_template/project/layers/plan_layers.py b/arho_feature_template/project/layers/plan_layers.py index 6f9bf95..96a3a42 100644 --- a/arho_feature_template/project/layers/plan_layers.py +++ b/arho_feature_template/project/layers/plan_layers.py @@ -446,6 +446,15 @@ def model_from_feature(cls, feature: QgsFeature) -> Document: id_=feature["id"], ) + @classmethod + def get_documents_to_delete(cls, documents: list[Document], plan: Plan) -> list[QgsFeature]: + updated_document_ids = [doc.id_ for doc in documents] + return [ + doc + for doc in cls.get_features_by_attribute_value("plan_id", str(plan.id_)) + if doc["id"] not in updated_document_ids + ] + class SourceDataLayer(AbstractPlanLayer): name = "Lähtötietoaineistot"