Skip to content

Commit 1841636

Browse files
committed
add config classes for plan regulation groups, add PlanRegulationDefinition for plan regulations with additional data and defaults
1 parent 12e1db3 commit 1841636

File tree

2 files changed

+171
-8
lines changed

2 files changed

+171
-8
lines changed

arho_feature_template/core/plan_regulation_config.py

+53-8
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import logging
44
import os
5-
from dataclasses import dataclass
5+
from dataclasses import dataclass, field
66
from enum import Enum
77
from pathlib import Path
88
from typing import TYPE_CHECKING, cast
@@ -14,6 +14,7 @@
1414
from arho_feature_template.qgis_plugin_tools.tools.resources import resources_path
1515

1616
if TYPE_CHECKING:
17+
from numbers import Number
1718
from typing import Literal
1819

1920
from qgis.core import QgsMapLayer
@@ -34,10 +35,11 @@ def __init__(self, message: str):
3435

3536
class UninitializedError(Exception):
3637
def __init__(self):
37-
super().__init__("PlanRegulationsSet is not initialized. Call 'load_config' first")
38+
super().__init__("PlanRegulationsSet is not initialized. Call 'initialize' first")
3839

3940

4041
class ValueType(Enum):
42+
DECIMAL = "desimaali"
4143
POSITIVE_DECIMAL = "positiivinen desimaali"
4244
POSITIVE_INTEGER = "positiivinen kokonaisluku"
4345
POSITIVE_INTEGER_RANGE = "positiivinen kokonaisluku arvoväli"
@@ -50,6 +52,8 @@ class Unit(Enum):
5052
EFFICIENCY_RATIO = "k-m2/m2"
5153
PERCENTAGE = "prosentti"
5254
AREA_RATIO = "m2/k-m2"
55+
DEGREES = "°"
56+
DECIBEL = "dB"
5357

5458

5559
# TODO: Same as in PlanManager, should refactor
@@ -75,6 +79,7 @@ class PlanRegulationsSet:
7579

7680
version: str
7781
regulations: list[PlanRegulationConfig]
82+
regulations_dict: dict[str, PlanRegulationConfig] = field(default_factory=dict)
7883

7984
_instance: PlanRegulationsSet | None = None
8085

@@ -87,9 +92,18 @@ def get_instance(cls) -> PlanRegulationsSet:
8792

8893
@classmethod
8994
def get_regulations(cls) -> list[PlanRegulationConfig]:
90-
"""Get the list of regulation configs, if instance is initialized."""
91-
instance = cls.get_instance()
92-
return instance.regulations
95+
"""Get the list of top-level regulation configs, if instance is initialized."""
96+
return cls.get_instance().regulations
97+
98+
@classmethod
99+
def get_regulations_dict(cls) -> dict[str, PlanRegulationConfig]:
100+
"""Get all regulations in a dictionary where keys are regulations codes and values PlanRegulationConfigs."""
101+
return cls.get_instance().regulations_dict
102+
103+
@classmethod
104+
def get_regulation_by_code(cls, regulation_code: str) -> PlanRegulationConfig | None:
105+
"""Get a regulation by it's regulation code (if exists)."""
106+
return cls.get_instance().regulations_dict.get(regulation_code)
93107

94108
@classmethod
95109
def initialize(
@@ -103,12 +117,17 @@ def initialize(
103117
data = yaml.safe_load(f)
104118
cls._instance = cls.from_dict(data)
105119

106-
# Add names from plan regulation layer
120+
regulations = cls.get_regulations()
121+
regulations_dict: dict[str, PlanRegulationConfig] = {}
107122
mapping = get_name_mapping_for_plan_regulations(type_of_plan_regulations_layer_name)
108-
if mapping:
109-
for regulation in cls.get_regulations():
123+
124+
# Add names to dictionary, add names to regulations
125+
for regulation in regulations:
126+
regulation.add_to_dictionary(regulations_dict)
127+
if mapping:
110128
regulation.add_name(mapping, language)
111129

130+
cls._instance.regulations_dict = regulations_dict
112131
logger.info("PlanRegulationsSet initialized successfully.")
113132
return cls._instance
114133

@@ -156,3 +175,29 @@ def add_name(self, code_to_name_mapping: dict[str, dict[str, str]], language: Li
156175
self.name = language_to_name_dict[language] if language_to_name_dict else self.regulation_code
157176
for regulation in self.child_regulations:
158177
regulation.add_name(code_to_name_mapping, language)
178+
179+
def add_to_dictionary(self, dictionary: dict[str, PlanRegulationConfig]):
180+
dictionary[self.regulation_code] = self
181+
for regulation in self.child_regulations:
182+
regulation.add_to_dictionary(dictionary)
183+
184+
185+
@dataclass
186+
class PlanRegulationDefinition:
187+
"""Associates a PlanRegulationConfig with an optional default value and additional data."""
188+
189+
regulation_config: PlanRegulationConfig
190+
default_value: str | Number | None
191+
additional_info: dict[str, str | Number | None] # NOTE: Correct typing for additional information values?
192+
regulation_number: int | None
193+
attached_files: list[Path]
194+
195+
@classmethod
196+
def from_dict(cls, data: dict) -> PlanRegulationDefinition:
197+
return cls(
198+
regulation_config=data["config"],
199+
default_value=data.get("default_value"),
200+
additional_info=data.get("additional_info", {}),
201+
regulation_number=data.get("regulation_number"),
202+
attached_files=data.get("attached_files", []),
203+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
from __future__ import annotations
2+
3+
import logging
4+
from dataclasses import dataclass
5+
from typing import TYPE_CHECKING, cast
6+
7+
import yaml
8+
from qgis.utils import iface
9+
10+
from arho_feature_template.core.plan_regulation_config import PlanRegulationDefinition, PlanRegulationsSet
11+
12+
if TYPE_CHECKING:
13+
from pathlib import Path
14+
15+
from qgis.gui import QgisInterface
16+
17+
iface: QgisInterface = cast("QgisInterface", iface) # type: ignore[no-redef]
18+
19+
logger = logging.getLogger(__name__)
20+
21+
22+
class ConfigSyntaxError(Exception):
23+
def __init__(self, message: str):
24+
super().__init__(f"Invalid config syntax: {message}")
25+
26+
27+
@dataclass
28+
class PlanRegulationGroupLibrary:
29+
"""Describes the configuration of a plan regulation group library"""
30+
31+
meta: PlanRegulationGroupLibraryMeta
32+
plan_regulation_group_categories: list[PlanRegulationGroupCategory]
33+
34+
@classmethod
35+
def from_dict(cls, data: dict) -> PlanRegulationGroupLibrary:
36+
try:
37+
return cls(
38+
meta=PlanRegulationGroupLibraryMeta.from_dict(data["meta"]),
39+
plan_regulation_group_categories=[
40+
PlanRegulationGroupCategory.from_dict(category) for category in data["categories"]
41+
],
42+
)
43+
except KeyError as e:
44+
raise ConfigSyntaxError(str(e)) from e
45+
46+
@classmethod
47+
def new_from_file(cls, fp: Path) -> PlanRegulationGroupLibrary:
48+
with fp.open(encoding="utf-8") as f:
49+
data = yaml.safe_load(f)
50+
return PlanRegulationGroupLibrary.from_dict(data)
51+
52+
53+
@dataclass
54+
class PlanRegulationGroupLibraryMeta:
55+
"""Describes the metadata of a plan regulation group library"""
56+
57+
name: str
58+
version: int | None
59+
group: str | None
60+
sub_group: str | None
61+
description: str | None
62+
63+
@classmethod
64+
def from_dict(cls, data: dict) -> PlanRegulationGroupLibraryMeta:
65+
return cls(
66+
name=data["name"],
67+
version=data.get("version"),
68+
group=data.get("group"),
69+
sub_group=data.get("sub_group"),
70+
description=data.get("description"),
71+
)
72+
73+
74+
@dataclass
75+
class PlanRegulationGroupCategory:
76+
category_code: str
77+
name: str | None
78+
plan_regulation_groups: list[PlanRegulationGroupDefinition]
79+
80+
@classmethod
81+
def from_dict(cls, data: dict) -> PlanRegulationGroupCategory:
82+
return cls(
83+
category_code=data["category_code"],
84+
name=data.get("name"),
85+
plan_regulation_groups=[
86+
PlanRegulationGroupDefinition.from_dict(group) for group in data["plan_regulation_groups"]
87+
],
88+
)
89+
90+
91+
@dataclass
92+
class PlanRegulationGroupDefinition:
93+
"""Describes a plan regulation group"""
94+
95+
name: str
96+
geometry: str
97+
color_code: str | None
98+
letter_code: str | None
99+
plan_regulations: list[PlanRegulationDefinition]
100+
101+
@classmethod
102+
def from_dict(cls, data: dict) -> PlanRegulationGroupDefinition:
103+
regulations = []
104+
for reg_data in data["plan_regulations"]:
105+
reg_code = reg_data["regulation_code"]
106+
config = PlanRegulationsSet.get_regulation_by_code(reg_code)
107+
if config:
108+
reg_data["config"] = config
109+
regulations.append(PlanRegulationDefinition.from_dict(reg_data))
110+
else:
111+
iface.messageBar().pushWarning("", f"Could not find config for {reg_code} plan regulation!")
112+
return cls(
113+
name=data["name"],
114+
geometry=data["geometry"],
115+
color_code=data.get("color_code"),
116+
letter_code=data.get("letter_code"),
117+
plan_regulations=regulations,
118+
)

0 commit comments

Comments
 (0)