Skip to content

Commit b3c12f3

Browse files
committed
refactor and extend use of optimized feature requests
- use LANGUAGE constant, defined in misc_utils for now - add AdditionalInformationTypeLayer and define get_additional_information_name for it - add get_attribute_value_by_another_attribute_value for AbstractLayer
1 parent d6756f4 commit b3c12f3

11 files changed

+70
-90
lines changed

arho_feature_template/core/lambda_service.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
from qgis.PyQt.QtNetwork import QNetworkAccessManager, QNetworkProxy, QNetworkReply, QNetworkRequest
1010
from qgis.PyQt.QtWidgets import QMessageBox
1111

12-
from arho_feature_template.utils.misc_utils import get_active_plan_id, get_plan_name, get_settings
12+
from arho_feature_template.project.layers.plan_layers import PlanLayer
13+
from arho_feature_template.utils.misc_utils import get_active_plan_id, get_settings
1314

1415

1516
class LambdaService(QObject):
@@ -113,7 +114,7 @@ def _process_json_reply(self, response_json: dict):
113114
if plan_json:
114115
geographical_area = plan_json.get("geographicalArea")
115116
if geographical_area:
116-
outline_name = get_plan_name(plan_id, language="fin")
117+
outline_name = PlanLayer.get_plan_name(plan_id)
117118
outline_json = {
118119
"type": "Feature",
119120
"properties": {"name": outline_name},

arho_feature_template/core/models.py

+5-8
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,9 @@
1111

1212
from arho_feature_template.exceptions import ConfigSyntaxError
1313
from arho_feature_template.qgis_plugin_tools.tools.resources import resources_path
14-
from arho_feature_template.utils.misc_utils import get_layer_by_name, iface
14+
from arho_feature_template.utils.misc_utils import LANGUAGE, get_layer_by_name, iface
1515

1616
if TYPE_CHECKING:
17-
from typing import Literal
18-
1917
from qgis.core import QgsFeature, QgsGeometry
2018

2119

@@ -149,7 +147,6 @@ def initialize(
149147
cls,
150148
config_path: Path = DEFAULT_PLAN_REGULATIONS_CONFIG_PATH,
151149
type_of_plan_regulations_layer_name="Kaavamääräyslaji",
152-
language: Literal["fin", "eng", "swe"] = "fin",
153150
) -> RegulationLibrary:
154151
# Initialize RegulationLibrary and RegulationConfigs from QGIS layer and config file
155152

@@ -165,7 +162,7 @@ def initialize(
165162

166163
# 3. Initialize regulation configs from layer. Storing them by their ID is handy for adding childs later
167164
id_to_regulation_map: dict[str, RegulationConfig] = {
168-
feature["id"]: RegulationConfig.from_feature(feature, language) for feature in layer.getFeatures()
165+
feature["id"]: RegulationConfig.from_feature(feature) for feature in layer.getFeatures()
169166
}
170167

171168
# 4. Add information from config file (value, unit, category only) and link child regulations
@@ -226,7 +223,7 @@ class RegulationConfig:
226223

227224
# NOTE: Perhaps this ("model_from_feature") should be method of PlanTypeLayer class?
228225
@classmethod
229-
def from_feature(cls, feature: QgsFeature, language: str = "fin") -> RegulationConfig:
226+
def from_feature(cls, feature: QgsFeature) -> RegulationConfig:
230227
"""
231228
Initialize PlanRegulationConfig from QgsFeature.
232229
@@ -235,8 +232,8 @@ def from_feature(cls, feature: QgsFeature, language: str = "fin") -> RegulationC
235232
return cls(
236233
id=feature["id"],
237234
regulation_code=feature["value"],
238-
name=feature["name"][language],
239-
description=feature["description"][language],
235+
name=feature["name"][LANGUAGE],
236+
description=feature["description"][LANGUAGE],
240237
status=feature["status"],
241238
level=feature["level"],
242239
parent_id=feature["parent_id"],

arho_feature_template/core/plan_manager.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
from arho_feature_template.resources.libraries.regulation_groups import regulation_group_library_config_files
4444
from arho_feature_template.utils.db_utils import get_existing_database_connection_names
4545
from arho_feature_template.utils.misc_utils import (
46+
LANGUAGE,
4647
check_layer_changes,
4748
get_active_plan_id,
4849
handle_unsaved_changes,
@@ -346,9 +347,9 @@ def create_new_regulation_group(self):
346347

347348
def regulation_group_library_from_active_plan() -> RegulationGroupLibrary:
348349
category_features = list(PlanRegulationGroupTypeLayer.get_features())
349-
category_id_to_name: dict[str, str] = {category["id"]: category["name"]["fin"] for category in category_features}
350+
category_id_to_name: dict[str, str] = {category["id"]: category["name"][LANGUAGE] for category in category_features}
350351
category_regulation_group_map: dict[str, list[QgsFeature]] = {
351-
feature["name"]["fin"]: [] for feature in category_features
352+
feature["name"][LANGUAGE]: [] for feature in category_features
352353
}
353354

354355
for feat in RegulationGroupLayer.get_features():

arho_feature_template/gui/components/code_combobox.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from qgis.PyQt.QtWidgets import QComboBox, QTreeWidget, QTreeWidgetItem
88

99
from arho_feature_template.exceptions import LayerNotFoundError
10+
from arho_feature_template.utils.misc_utils import LANGUAGE
1011

1112
if TYPE_CHECKING:
1213
from arho_feature_template.project.layers.code_layers import (
@@ -33,7 +34,7 @@ def populate_from_code_layer(self, layer_type: type[AbstractCodeLayer]) -> None:
3334
return
3435

3536
for i, feature in enumerate(layer.getFeatures(), start=1):
36-
self.addItem(feature["name"]["fin"])
37+
self.addItem(feature["name"][LANGUAGE])
3738
self.setItemData(i, feature["id"])
3839

3940
def value(self) -> str:
@@ -83,9 +84,9 @@ def populate_from_code_layer(self, layer_type: type[AbstractCodeLayer]) -> None:
8384
item = QTreeWidgetItem()
8485
items[code_feature["id"]] = item
8586

86-
text = code_feature["name"]["fin"]
87+
text = code_feature["name"][LANGUAGE]
8788
item.setText(0, text)
88-
description = code_feature["description"]["fin"]
89+
description = code_feature["description"][LANGUAGE]
8990
item.setToolTip(0, description)
9091
item.setData(0, Qt.UserRole, code_feature["id"])
9192

arho_feature_template/gui/components/plan_proposition_widget.py

-3
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,6 @@
1919
ui_path = resources.files(__package__) / "plan_proposition_widget.ui"
2020
FormClass, _ = uic.loadUiType(ui_path)
2121

22-
# TO BE REPLACED
23-
LANGUAGE = "fin"
24-
2522

2623
class PropositionWidget(QWidget, FormClass): # type: ignore
2724
"""A widget representation of a plan proposition."""

arho_feature_template/gui/components/plan_regulation_widget.py

+3-5
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,15 @@
2424
MultilineTextInputWidget,
2525
SinglelineTextInputWidget,
2626
)
27-
from arho_feature_template.utils.misc_utils import get_additional_information_name, get_layer_by_name, iface
27+
from arho_feature_template.project.layers.code_layers import AdditionalInformationTypeLayer
28+
from arho_feature_template.utils.misc_utils import LANGUAGE, get_layer_by_name, iface
2829

2930
if TYPE_CHECKING:
3031
from qgis.PyQt.QtWidgets import QPushButton
3132

3233
ui_path = resources.files(__package__) / "plan_regulation_widget.ui"
3334
FormClass, _ = uic.loadUiType(ui_path)
3435

35-
# TO BE REPLACED
36-
LANGUAGE = "fin"
37-
3836

3937
class RegulationWidget(QWidget, FormClass): # type: ignore
4038
"""A widget representation of a plan regulation."""
@@ -218,7 +216,7 @@ def _add_additional_info(self, info_type: str, default_value: str | float | None
218216

219217
# NOTE: Now info type is the name / readable version when this is triggered by user
220218
# Might need to refactor this later..
221-
name = get_additional_information_name(info_type)
219+
name = AdditionalInformationTypeLayer.get_additional_information_name(info_type)
222220
self._add_widgets(QLabel("Lisätiedonlaji"), SinglelineTextInputWidget(name, False))
223221

224222
# NOTE: Does not support multiple instances of same additional information kind,

arho_feature_template/gui/tools/inspect_plan_features_tool.py

-3
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,6 @@ def create_menu(self, click_pos: QPoint, nearby_features: dict[QgsVectorLayer, l
9090
for layer, features in nearby_features.items():
9191
for feature in features:
9292
menu_text = layer.name()
93-
# feat_name = feature["name"]["fin"]
94-
# if feat_name:
95-
# menu_text += f" — {feat_name}"
9693
action = menu.addAction(menu_text)
9794
action.triggered.connect(partial(self.edit_feature_requested.emit, feature, layer.name()))
9895
action.hovered.connect(partial(self.highlight_feature, feature, layer))

arho_feature_template/project/layers/__init__.py

+7
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,10 @@ def get_attribute_values_by_another_attribute_value(
5959
request.setFlags(QgsFeatureRequest.NoGeometry)
6060
for feature in layer.getFeatures(request):
6161
yield feature[target_attribute]
62+
63+
@classmethod
64+
def get_attribute_value_by_another_attribute_value(
65+
cls, target_attribute: str, filter_attribute: str, filter_value: str
66+
) -> Any | None:
67+
gen = cls.get_attribute_values_by_another_attribute_value(target_attribute, filter_attribute, filter_value)
68+
return next(gen, None)

arho_feature_template/project/layers/code_layers.py

+18-15
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
from __future__ import annotations
22

3-
from typing import ClassVar
4-
5-
from qgis.core import QgsFeatureRequest
3+
from typing import ClassVar, cast
64

5+
from arho_feature_template.exceptions import LayerNameNotFoundError
76
from arho_feature_template.project.layers import AbstractLayer
7+
from arho_feature_template.utils.misc_utils import LANGUAGE
88

99

1010
class AbstractCodeLayer(AbstractLayer): ...
@@ -30,6 +30,15 @@ class PlanThemeLayer(AbstractCodeLayer):
3030
name = "Kaavoitusteemat"
3131

3232

33+
class AdditionalInformationTypeLayer(AbstractCodeLayer):
34+
name = "Lisätiedonlaji"
35+
36+
@classmethod
37+
def get_additional_information_name(cls, info_type: str) -> str | None:
38+
attribute_value = cls.get_attribute_value_by_another_attribute_value("name", "value", info_type)
39+
return cast(str, attribute_value[LANGUAGE]) if attribute_value else None
40+
41+
3342
class PlanRegulationGroupTypeLayer(AbstractCodeLayer):
3443
name = "Kaavamääräysryhmän tyyppi"
3544

@@ -45,25 +54,19 @@ class PlanRegulationGroupTypeLayer(AbstractCodeLayer):
4554
@classmethod
4655
def get_id_by_feature_layer_name(cls, layer_name: str) -> str | None:
4756
regulation_group_type = cls.LAYER_NAME_TO_REGULATION_GROUP_TYPE_MAP.get(layer_name)
48-
49-
request = QgsFeatureRequest().setFilterExpression(f"\"value\"='{regulation_group_type}'")
50-
feature = next(cls.get_from_project().getFeatures(request), None)
51-
if feature:
52-
return feature["id"]
53-
54-
return None
57+
if not regulation_group_type:
58+
raise LayerNameNotFoundError(layer_name)
59+
attribute_value = cls.get_attribute_value_by_another_attribute_value("id", "value", regulation_group_type)
60+
return cast(str, attribute_value) if attribute_value else attribute_value
5561

5662

5763
class PlanRegulationTypeLayer(AbstractCodeLayer):
5864
name = "Kaavamääräyslaji"
5965

6066
@classmethod
6167
def get_regulation_type_by_id(cls, _id: str) -> str | None:
62-
for feature in cls.get_from_project().getFeatures():
63-
if feature["id"] == _id:
64-
return feature["value"]
65-
66-
return None
68+
attribute_value = cls.get_attribute_value_by_another_attribute_value("value", "id", _id)
69+
return cast(str, attribute_value) if attribute_value else attribute_value
6770

6871

6972
code_layers = AbstractCodeLayer.__subclasses__()

arho_feature_template/project/layers/plan_layers.py

+25-19
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from arho_feature_template.exceptions import FeatureNotFoundError, LayerEditableError, LayerNotFoundError
2222
from arho_feature_template.project.layers import AbstractLayer
2323
from arho_feature_template.project.layers.code_layers import PlanRegulationTypeLayer
24+
from arho_feature_template.utils.misc_utils import LANGUAGE
2425

2526
logger = logging.getLogger(__name__)
2627

@@ -75,8 +76,8 @@ def feature_from_model(cls, model: Plan) -> QgsFeature:
7576
message = "Plan must have a geometry to be added to the layer"
7677
raise ValueError(message)
7778

78-
feature["name"] = {"fin": model.name}
79-
feature["description"] = {"fin": model.description}
79+
feature["name"] = {LANGUAGE: model.name}
80+
feature["description"] = {LANGUAGE: model.description}
8081
feature["permanent_plan_identifier"] = model.permanent_plan_identifier
8182
feature["record_number"] = model.record_number
8283
feature["producers_plan_identifier"] = model.producers_plan_identifier
@@ -95,8 +96,8 @@ def model_from_feature(cls, feature: QgsFeature) -> Plan:
9596
]
9697
return Plan(
9798
geom=feature.geometry(),
98-
name=feature["name"]["fin"],
99-
description=feature["description"]["fin"],
99+
name=feature["name"][LANGUAGE],
100+
description=feature["description"][LANGUAGE],
100101
permanent_plan_identifier=feature["permanent_plan_identifier"],
101102
record_number=feature["record_number"],
102103
producers_plan_identifier=feature["producers_plan_identifier"],
@@ -112,6 +113,11 @@ def model_from_feature(cls, feature: QgsFeature) -> Plan:
112113
id_=feature["id"],
113114
)
114115

116+
@classmethod
117+
def get_plan_name(cls, plan_id: str) -> str:
118+
attribute_value = cls.get_attribute_value_by_another_attribute_value("name", "id", plan_id)
119+
return attribute_value[LANGUAGE] if attribute_value else "Nimetön"
120+
115121

116122
class PlanFeatureLayer(AbstractPlanLayer):
117123
@classmethod
@@ -122,9 +128,9 @@ def feature_from_model(cls, model: PlanFeature, plan_id: str | None = None) -> Q
122128

123129
feature = cls.initialize_feature_from_model(model)
124130
feature.setGeometry(model.geom)
125-
feature["name"] = {"fin": model.name if model.name else ""}
131+
feature["name"] = {LANGUAGE: model.name if model.name else ""}
126132
feature["type_of_underground_id"] = model.type_of_underground_id
127-
feature["description"] = {"fin": model.description if model.description else ""}
133+
feature["description"] = {LANGUAGE: model.description if model.description else ""}
128134
feature["plan_id"] = (
129135
plan_id
130136
if plan_id
@@ -143,8 +149,8 @@ def model_from_feature(cls, feature: QgsFeature) -> PlanFeature:
143149
geom=feature.geometry(),
144150
type_of_underground_id=feature["type_of_underground_id"],
145151
layer_name=cls.get_from_project().name(),
146-
name=feature["name"]["fin"],
147-
description=feature["description"]["fin"],
152+
name=feature["name"][LANGUAGE],
153+
description=feature["description"][LANGUAGE],
148154
regulation_groups=[
149155
RegulationGroupLayer.model_from_feature(feat) for feat in regulation_group_features if feat is not None
150156
],
@@ -187,7 +193,7 @@ def feature_from_model(cls, model: RegulationGroup, plan_id: str | None = None)
187193
feature = cls.initialize_feature_from_model(model)
188194

189195
feature["short_name"] = model.short_name if model.short_name else None
190-
feature["name"] = {"fin": model.name}
196+
feature["name"] = {LANGUAGE: model.name}
191197
feature["type_of_plan_regulation_group_id"] = model.type_code_id
192198
feature["plan_id"] = (
193199
plan_id
@@ -201,7 +207,7 @@ def feature_from_model(cls, model: RegulationGroup, plan_id: str | None = None)
201207
def model_from_feature(cls, feature: QgsFeature) -> RegulationGroup:
202208
return RegulationGroup(
203209
type_code_id=feature["type_of_plan_regulation_group_id"],
204-
name=feature["name"]["fin"],
210+
name=feature["name"][LANGUAGE],
205211
short_name=feature["short_name"],
206212
color_code=None,
207213
group_number=None,
@@ -309,9 +315,9 @@ def feature_from_model(cls, model: Regulation) -> QgsFeature:
309315
feature["plan_regulation_group_id"] = model.regulation_group_id_
310316
feature["type_of_plan_regulation_id"] = model.config.id
311317
feature["unit"] = model.config.unit
312-
feature["text_value"] = {"fin": model.value if isinstance(model.value, str) else ""}
318+
feature["text_value"] = {LANGUAGE: model.value if isinstance(model.value, str) else ""}
313319
feature["numeric_value"] = model.value if isinstance(model.value, Number) else NULL
314-
feature["name"] = {"fin": model.topic_tag if model.topic_tag else ""}
320+
feature["name"] = {LANGUAGE: model.topic_tag if model.topic_tag else ""}
315321
feature["id"] = model.id_ if model.id_ else feature["id"]
316322
# feature["plan_theme_id"]
317323
# feature["type_of_verbal_plan_regulation_id"]
@@ -331,7 +337,7 @@ def model_from_feature(cls, feature: QgsFeature) -> Regulation:
331337
return Regulation(
332338
config=config,
333339
# Assuming only either text_value or numeric_value is defined
334-
value=feature["text_value"]["fin"] if feature["text_value"]["fin"] else feature["numeric_value"],
340+
value=feature["text_value"][LANGUAGE] if feature["text_value"][LANGUAGE] else feature["numeric_value"],
335341
additional_information=None,
336342
regulation_number=None,
337343
files=[],
@@ -365,8 +371,8 @@ class PlanPropositionLayer(AbstractPlanLayer):
365371
def feature_from_model(cls, model: Proposition) -> QgsFeature:
366372
feature = cls.initialize_feature_from_model(model)
367373

368-
feature["name"] = {"fin": model.name}
369-
feature["text_value"] = {"fin": model.value}
374+
feature["name"] = {LANGUAGE: model.name}
375+
feature["text_value"] = {LANGUAGE: model.value}
370376
feature["plan_regulation_group_id"] = model.regulation_group_id_
371377
feature["ordering"] = model.proposition_number
372378
feature["plan_theme_id"] = model.theme_id
@@ -377,17 +383,17 @@ def feature_from_model(cls, model: Proposition) -> QgsFeature:
377383
@classmethod
378384
def model_from_feature(cls, feature: QgsFeature) -> Proposition:
379385
return Proposition(
380-
name=feature["name"]["fin"],
381-
value=feature["text_value"]["fin"],
386+
name=feature["name"][LANGUAGE],
387+
value=feature["text_value"][LANGUAGE],
382388
regulation_group_id_=feature["plan_regulation_group_id"],
383389
proposition_number=feature["ordering"],
384390
theme_id=feature["plan_theme_id"],
385391
id_=feature["id"],
386392
)
387393

388394
@classmethod
389-
def propositions_with_group_id(cls, group_id: str) -> list[QgsFeature]:
390-
return [feat for feat in cls.get_features() if feat["plan_regulation_group_id"] == group_id]
395+
def propositions_with_group_id(cls, group_id: str) -> Generator[QgsFeature]:
396+
return cls.get_features_by_attribute_value("plan_regulation_group_id", group_id)
391397

392398

393399
class DocumentLayer(AbstractPlanLayer):

0 commit comments

Comments
 (0)