Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial template library implementation #3

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion arho_feature_template/core/feature_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,12 @@ class FeatureTemplate:
name: str
description: str
feature_layer: QgsVectorLayer
feature_attributes: Sequence[FeatureAttribute]
feature_attributes: Sequence[FeatureAttribute] | None = None
display_icon: QIcon | None = None

def add_feature_attributes(self, feature_attribute_groups: Sequence[dict]):
for _group in feature_attribute_groups:
# Should we add FeatureAttributeGroup as a class? Or can group name be
# only one class attribute of FeatureAttribute?
# TODO
pass
101 changes: 87 additions & 14 deletions arho_feature_template/core/feature_template_library.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,109 @@
from __future__ import annotations

import json
from typing import TYPE_CHECKING, Sequence

from qgis.core import QgsMapLayer, QgsProject, QgsVectorLayer
from qgis.utils import iface

from arho_feature_template.core.feature_template import FeatureTemplate
from arho_feature_template.core.json_config import JsonKeys
from arho_feature_template.core.utils import read_json

if TYPE_CHECKING:
from os import PathLike

from arho_feature_template.core.feature_template import FeatureTemplate


class FeatureTemplateLibrary:
"""Class for storing FeatureTemplates and loading them from a JSON (or other conf. file)."""

def __init__(self, json_path: str | PathLike):
self.templates = []
library_dict = self.read_json(json_path)
library_dict = read_json(json_path)
self.name = library_dict.get(JsonKeys.NAME)
self.version = library_dict.get(JsonKeys.VERSION)
self.build_templates(library_dict)

def read_json(self, json_path: str | PathLike) -> dict:
self.source_json = json_path
with open(json_path) as f:
return json.load(f)

def build_templates(self, library_dict: dict):
"""Build feature templates from input `library_dict` and update `templates` attribute."""
all_templates_count = 0
invalid_templates_count = 0
templates = []

_templates_raw = library_dict["templates"]
# for template_raw in templates_raw:
## ... build FeatureTemplate from dict here, in FeatureTemplate class or elsewhere?
# template = FeatureTemplate()
# templates.append(template)
templates_raw = library_dict.get(JsonKeys.TEMPLATES)
if not templates_raw:
iface.messageBar().pushWarning(
"Error: ", f"Could not find any templates in the template library JSON with key {JsonKeys.TEMPLATES}."
)
return

# Go through each template data in the dictionary, save info about invalid templates?
for template_raw in templates_raw:
template = self.build_feature_template(template_raw)
if not template:
invalid_templates_count += 1
else:
templates.append(template)
all_templates_count += 1

self.templates = templates

if invalid_templates_count > 0:
iface.messageBar().pushWarning(
"Warning: ",
f"Failed to create {invalid_templates_count} out of {all_templates_count} \
feature templates from the template library JSON."
)

def build_feature_template(self, template_dict: dict) -> FeatureTemplate | None:
"""
Attempt to build a `FeatureTemplate` from given `template_dict`.

Args:
template_dict: Information about a feature template as a dictionary.

Returns:
`FeatureTemplate` if building was successful, otherwise None.
"""
name: str = template_dict.get(JsonKeys.NAME)
description: str = template_dict.get(JsonKeys.DESCRIPTION, "No description")
feature: dict = template_dict.get(JsonKeys.FEATURE, {})
feature_layer_name: str = feature.get(JsonKeys.LAYER, "")
layers = QgsProject.instance().mapLayersByName(feature_layer_name)
if not layers:
feature_layer = None
else:
feature_layer: QgsMapLayer = layers[0]
# Is this needed?
if len(layers) > 1:
iface.messageBar().pushWarning(
"Warning: ",
f"Multiple layers with the specified feature layer name {feature_layer_name} found \
for feature template {name}, using the first match."
)

if not isinstance(feature_layer, QgsVectorLayer):
iface.messageBar().pushWarning(
"Error: ",
f"Feature layer found but has invalid type {type(feature_layer)} for feature template {name}."
)
feature_layer = None

# All information is needed for FeatureTemplate except for description?
if not name or not feature or not feature_layer_name or not feature_layer:
return None

# TODO: fields of feature layer
# feature_fields = feature.get(JsonKeys.FIELDS, [])

feature_template = FeatureTemplate(
name=name,
description=description,
feature_layer=feature_layer,
)
feature_attribute_groups = feature.get(JsonKeys.CHILDREN, [])
feature_template.add_feature_attributes(feature_attribute_groups)

return feature_template

def get_templates(self) -> Sequence[FeatureTemplate]:
return self.templates
21 changes: 21 additions & 0 deletions arho_feature_template/core/json_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from dataclasses import dataclass


@dataclass
class JsonKeys:
VERSION = "version"
NAME = "name"
TEMPLATES = "templates"

DESCRIPTION = "description"
FEATURE = "feature"

LAYER = "layer"
FIELDS = "fields"
CHILDREN = "children"

FIELD = "field"
VALUE = "value"
MODIFIABLE = "allow_user_input"
HIDDEN = "hidden"
REQUIRED = "required"
11 changes: 11 additions & 0 deletions arho_feature_template/core/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import json

from typing import TYPE_CHECKING

if TYPE_CHECKING:
from os import PathLike


def read_json(json_path: str | PathLike) -> dict:
with open(json_path) as f:
return json.load(f)
4 changes: 2 additions & 2 deletions ruff_defaults.toml
Original file line number Diff line number Diff line change
Expand Up @@ -440,8 +440,8 @@ select = [
"TCH003",
"TCH004",
"TCH005",
"TD004",
"TD005",
# "TD004",
# "TD005",
"TD006",
"TD007",
"TID251",
Expand Down
Loading