diff --git a/arho_feature_template/core/models.py b/arho_feature_template/core/models.py index ad49087..416fb40 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"], @@ -284,7 +286,6 @@ class Regulation: @dataclass class Proposition: - name: str value: str theme_id: str | None = None proposition_number: int | None = None @@ -370,5 +371,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..11596ed 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, @@ -496,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, @@ -507,13 +510,22 @@ 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 = plan_id + save_document(document) + return feature @@ -660,3 +672,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..a9d4941 --- /dev/null +++ b/arho_feature_template/gui/components/plan_document_widget.py @@ -0,0 +1,187 @@ +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.""" + + document_edited = pyqtSignal() + 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) + + 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), + (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 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)) + 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/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_attribute_form.py b/arho_feature_template/gui/dialogs/plan_attribute_form.py index 2cae050..e746ccd 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) @@ -109,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: @@ -161,6 +175,25 @@ 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()) + 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() + # --- def into_model(self) -> Plan: @@ -176,6 +209,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/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/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..96a3a42 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"], ) @@ -376,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 @@ -388,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"], @@ -405,6 +408,53 @@ 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"], + ) + + @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"