Skip to content

Commit 4f1427d

Browse files
committed
(WIP) refactor plan regulations and plan regulations groups
1 parent 31b68d5 commit 4f1427d

8 files changed

+406
-218
lines changed

arho_feature_template/core/models.py

+72-9
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,91 @@
11
from __future__ import annotations
22

33
from dataclasses import dataclass, field
4+
from enum import Enum
45
from typing import TYPE_CHECKING
56

67
if TYPE_CHECKING:
7-
from qgis.core import QgsGeometry
8+
from numbers import Number
9+
from qgis.core import QgsGeometry, QgsFeature
10+
11+
12+
class ValueType(Enum):
13+
DECIMAL = "desimaali"
14+
POSITIVE_DECIMAL = "positiivinen desimaali"
15+
POSITIVE_INTEGER = "positiivinen kokonaisluku"
16+
POSITIVE_INTEGER_RANGE = "positiivinen kokonaisluku arvoväli"
17+
VERSIONED_TEXT = "kieliversioitu teksti"
18+
19+
20+
@dataclass
21+
class RegulationConfig:
22+
"""
23+
Describes plan regulation type.
24+
25+
Initialized from DB/QGIS layer and extended with data from a config file.
26+
"""
27+
id: str
28+
regulation_code: str
29+
name: str
30+
description: str
31+
status: str
32+
level: int
33+
parent_id: str | None
34+
child_regulations: list[RegulationConfig] = field(default_factory=list)
35+
36+
# Data from config file
37+
category_only: bool = False
38+
value_type: ValueType | None = None
39+
unit: str | None = None
40+
41+
# NOTE: Perhaps this ("model_from_feature") should be method of PlanTypeLayer class?
42+
@classmethod
43+
def from_feature(cls, feature: QgsFeature, language: str = "fin") -> RegulationConfig:
44+
"""
45+
Initialize PlanRegulationConfig from QgsFeature.
46+
47+
Child regulations, value type ,category only and unit need to be set separately.
48+
"""
49+
return cls(
50+
id=feature["id"],
51+
regulation_code=feature["value"],
52+
name=feature["name"][language],
53+
description=feature["description"][language],
54+
status=feature["status"],
55+
level=feature["level"],
56+
parent_id=feature["parent_id"],
57+
)
58+
59+
def add_to_dictionary(self, dictionary: dict[str, RegulationConfig]):
60+
"""Add child regulations to dictionary too."""
61+
dictionary[self.regulation_code] = self
62+
for regulation in self.child_regulations:
63+
regulation.add_to_dictionary(dictionary)
864

965

1066
@dataclass
1167
class Regulation:
12-
type_code: str
13-
value_string: str | None
14-
value_int: int | None
15-
value_float: float | None
16-
unit: str | None
68+
config: RegulationConfig # includes regulation_code and unit among other needed data for saving feature
69+
value: str | Number | tuple[int, int] | None = None
70+
regulation_number: int | None
71+
additional_information: dict[str, str | Number | None]
72+
files: list[str] = []
73+
theme: str | None = None
74+
topic_tag: str | None = None
1775
id_: int | None = None
76+
# value_string: str | None
77+
# value_number: Number | None
78+
# value_number_pair: tuple[Number, Number] | None
1879

1980

2081
@dataclass
2182
class RegulationGroup:
22-
name: str
23-
short_name: str | None
2483
type_code: str
25-
regulations: list[Regulation]
84+
name: str | None
85+
short_name: str | None
86+
color_code: str | None
87+
letter_code: str | None
88+
regulations: list[Regulation] | list[RegulationConfig]
2689
id_: int | None = None
2790

2891

arho_feature_template/core/plan_regulation_config.py

+21-21
Original file line numberDiff line numberDiff line change
@@ -199,24 +199,24 @@ def add_to_dictionary(self, dictionary: dict[str, PlanRegulationConfig]):
199199
regulation.add_to_dictionary(dictionary)
200200

201201

202-
@dataclass
203-
class PlanRegulationDefinition:
204-
"""Associates a PlanRegulationConfig with an optional default value and additional data."""
205-
206-
regulation_config: PlanRegulationConfig
207-
default_value: str | Number | list[int] | None
208-
additional_information: list[
209-
dict[str, str | Number | None]
210-
] # NOTE: Correct typing for additional information values?
211-
regulation_number: int | None
212-
attached_files: list[Path]
213-
214-
@classmethod
215-
def from_dict(cls, data: dict) -> PlanRegulationDefinition:
216-
return cls(
217-
regulation_config=data["config"],
218-
default_value=data.get("default_value"),
219-
additional_information=data.get("additional_information", []),
220-
regulation_number=data.get("regulation_number"),
221-
attached_files=data.get("attached_files", []),
222-
)
202+
# @dataclass
203+
# class PlanRegulationDefinition:
204+
# """Associates a PlanRegulationConfig with an optional default value and additional data."""
205+
206+
# regulation_config: PlanRegulationConfig
207+
# default_value: str | Number | list[int] | None
208+
# additional_information: list[
209+
# dict[str, str | Number | None]
210+
# ] # NOTE: Correct typing for additional information values?
211+
# regulation_number: int | None
212+
# attached_files: list[Path]
213+
214+
# @classmethod
215+
# def from_dict(cls, data: dict) -> PlanRegulationDefinition:
216+
# return cls(
217+
# regulation_config=data["config"],
218+
# default_value=data.get("default_value"),
219+
# additional_information=data.get("additional_information", []),
220+
# regulation_number=data.get("regulation_number"),
221+
# attached_files=data.get("attached_files", []),
222+
# )

arho_feature_template/gui/plan_regulation_group_widget.py

+46-23
Original file line numberDiff line numberDiff line change
@@ -8,51 +8,74 @@
88
from qgis.PyQt.QtCore import pyqtSignal
99
from qgis.PyQt.QtWidgets import QWidget
1010

11-
from arho_feature_template.gui.plan_regulation_widget import PlanRegulationWidget
11+
from arho_feature_template.gui.plan_regulation_widget import RegulationWidget
12+
from arho_feature_template.core.models import Regulation, RegulationGroup
13+
from arho_feature_template.project.layers.plan_layers import RegulationGroupLayer
1214

1315
if TYPE_CHECKING:
1416
from qgis.PyQt.QtWidgets import QFrame, QLineEdit, QPushButton
1517

16-
from arho_feature_template.core.plan_regulation_config import PlanRegulationDefinition
17-
from arho_feature_template.core.plan_regulation_group_config import PlanRegulationGroupDefinition
18-
1918
ui_path = resources.files(__package__) / "plan_regulation_group_widget.ui"
2019
FormClass, _ = uic.loadUiType(ui_path)
2120

2221

23-
class PlanRegulationGroupWidget(QWidget, FormClass): # type: ignore
22+
class RegulationGroupWidget(QWidget, FormClass): # type: ignore
2423
"""A widget representation of a plan regulation group."""
2524

2625
delete_signal = pyqtSignal(QWidget)
2726

28-
def __init__(self, group_definition: PlanRegulationGroupDefinition):
27+
def __init__(self, regulation_group_data: RegulationGroup):
2928
super().__init__()
3029
self.setupUi(self)
3130

3231
# TYPES
3332
self.frame: QFrame
34-
self.heading: QLineEdit
33+
self.name: QLineEdit # "heading"
3534
self.del_btn: QPushButton
3635

3736
# INIT
38-
self.group_definition = group_definition
39-
self.layer = "plan_regulation_group"
40-
41-
self.heading.setText(self.group_definition.name)
42-
self.init_buttons()
43-
for plan_regulation_definition in self.group_definition.plan_regulations:
44-
_ = self.add_plan_regulation_widget(plan_regulation_definition)
45-
46-
def init_buttons(self):
37+
self.regulation_group_data = regulation_group_data
38+
self.regulation_widgets: list[RegulationWidget] = [
39+
self.add_regulation_widget(regulation) for regulation in self.regulation_group_data.regulations
40+
]
41+
self.name.setText(self.regulation_group_data.name)
4742
self.del_btn.setIcon(QgsApplication.getThemeIcon("mActionDeleteSelected.svg"))
4843
self.del_btn.clicked.connect(lambda: self.delete_signal.emit(self))
49-
50-
def add_plan_regulation_widget(self, definition: PlanRegulationDefinition) -> PlanRegulationWidget:
51-
widget = PlanRegulationWidget.from_definition(definition=definition, parent=self.frame)
52-
widget.delete_signal.connect(self.delete_plan_regulation_widget)
44+
45+
def add_regulation_widget(self, regulation: Regulation) -> RegulationWidget:
46+
widget = RegulationWidget(regulation_data=regulation, parent=self.frame)
47+
widget.delete_signal.connect(self.delete_regulation_widget)
5348
self.frame.layout().addWidget(widget)
5449
return widget
5550

56-
def delete_plan_regulation_widget(self, plan_regulation_widget: PlanRegulationWidget):
57-
self.frame.layout().removeWidget(plan_regulation_widget)
58-
plan_regulation_widget.deleteLater()
51+
def delete_regulation_widget(self, regulation_widget: RegulationWidget):
52+
self.frame.layout().removeWidget(regulation_widget)
53+
self.regulation_widgets.remove(regulation_widget)
54+
regulation_widget.deleteLater()
55+
56+
def into_model(self, id: str) -> RegulationGroup:
57+
return RegulationGroup(
58+
type_code=self.regulation_group_data.type_code,
59+
name=self.name.text(),
60+
short_name=self.regulation_group_data.short_name,
61+
color_code=self.regulation_group_data.color_code,
62+
letter_code=self.regulation_group_data.letter_code,
63+
plan_regulations=[widget.save_data(id) for widget in self.regulation_widgets],
64+
id_=id
65+
)
66+
67+
def save_data(self):
68+
if not self.regulation_group_data.id_:
69+
new_feature = True
70+
id_ = "new_id_here" # TODO
71+
else:
72+
new_feature = False
73+
id_ = self.regulation_group_data.id_
74+
75+
model = self.into_model(id_)
76+
regulation_feature = RegulationGroupLayer.feature_from_model(model)
77+
layer = RegulationGroupLayer.get_from_project()
78+
if new_feature:
79+
layer.addFeature(regulation_feature)
80+
else:
81+
layer.updateFeature(regulation_feature)

arho_feature_template/gui/plan_regulation_group_widget.ui

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
</widget>
5252
</item>
5353
<item>
54-
<widget class="QLineEdit" name="heading">
54+
<widget class="QLineEdit" name="name">
5555
<property name="sizePolicy">
5656
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
5757
<horstretch>0</horstretch>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
from qgis.PyQt.QtWidgets import QWidget, QLineEdit, QTextEdit, QLineEdit, QSizePolicy
2+
from qgis.gui import QgsSpinBox, QgsDoubleSpinBox
3+
4+
from numbers import Number
5+
6+
7+
def initialize_numeric_input_widget(
8+
widget: QgsSpinBox | QgsDoubleSpinBox, default_value: Number | None, unit: str | None, positive: bool
9+
):
10+
if unit:
11+
widget.setSuffix(f" {unit}")
12+
13+
if positive:
14+
widget.setMinimum(0)
15+
else:
16+
widget.setMinimum(-9999)
17+
18+
if default_value:
19+
widget.setValue(default_value)
20+
21+
widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
22+
23+
24+
def initialize_text_input_widget(widget: QTextEdit | QLineEdit, default_value: str | None, editable: bool):
25+
if default_value:
26+
widget.setText(str(default_value))
27+
28+
if not editable:
29+
widget.setReadOnly(True)
30+
31+
32+
class DecimalInputWidget(QgsSpinBox):
33+
def __init__(self, default_value: float | None = None, unit: str | None = None, positive: bool = False):
34+
super().__init__()
35+
self.unit = unit
36+
initialize_numeric_input_widget(self, default_value, unit, positive)
37+
38+
def get_value(self) -> float:
39+
return self.value()
40+
41+
42+
class IntegerInputWidget(QgsDoubleSpinBox):
43+
def __init__(self, default_value: int | None = None, unit: str | None = None, positive: bool = False):
44+
super().__init__()
45+
self.unit = unit
46+
initialize_numeric_input_widget(self, default_value, unit, positive)
47+
48+
def get_value(self) -> int:
49+
return self.value()
50+
51+
52+
class IntegerRangeInputWidget(QWidget):
53+
def __init__(
54+
self, default_value: tuple[int, int] | None = None, unit: str | None = None, positive: bool = False
55+
):
56+
super().__init__()
57+
self.min_widget = IntegerInputWidget(default_value[0], unit, positive)
58+
self.max_widget = IntegerInputWidget(default_value[1], unit, positive)
59+
60+
def get_value(self) -> tuple[int, int]:
61+
return (self.min_widget.get_value(), self.max_widget.get_value())
62+
63+
64+
class SinglelineTextInputWidget(QLineEdit):
65+
def __init__(self, default_value: str | None = None, editable: bool = False):
66+
super().__init__()
67+
initialize_text_input_widget(self, default_value, editable)
68+
69+
def get_value(self) -> str | None:
70+
return self.text() if self.text() else None
71+
72+
73+
class MultilineTextInputWidget(QTextEdit):
74+
def __init__(self, default_value: str | None = None, editable: bool = True):
75+
super().__init__()
76+
initialize_text_input_widget(self, default_value, editable)
77+
78+
def get_value(self) -> str | None:
79+
return self.toPlainText() if self.toPlainText() else None

0 commit comments

Comments
 (0)