2026-01-08 17:13:43 +01:00
|
|
|
|
"""
|
|
|
|
|
|
sn_plan41/ui/tab_a_logic.py – Fachlogik für Tab A (Daten)
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
2026-02-13 21:38:25 +01:00
|
|
|
|
from __future__ import annotations
|
2026-01-08 17:13:43 +01:00
|
|
|
|
|
2026-02-13 21:38:25 +01:00
|
|
|
|
from typing import Any, Dict, List, Optional, Tuple
|
|
|
|
|
|
from collections.abc import Mapping as _Mapping
|
|
|
|
|
|
|
|
|
|
|
|
from sn_basis.functions.variable_wrapper import ( # type: ignore
|
2026-01-08 17:13:43 +01:00
|
|
|
|
get_variable,
|
|
|
|
|
|
set_variable,
|
|
|
|
|
|
)
|
2026-02-13 21:38:25 +01:00
|
|
|
|
from sn_basis.functions.sys_wrapper import ( # type: ignore
|
2026-01-08 17:13:43 +01:00
|
|
|
|
file_exists,
|
|
|
|
|
|
write_text,
|
|
|
|
|
|
)
|
2026-02-13 21:38:25 +01:00
|
|
|
|
from sn_basis.functions.ly_existence_wrapper import layer_exists # type: ignore
|
|
|
|
|
|
from sn_basis.functions.ly_metadata_wrapper import get_layer_type # type: ignore
|
|
|
|
|
|
|
|
|
|
|
|
# Prüfer-Typen (werden als Instanzen erwartet)
|
|
|
|
|
|
from sn_basis.modules.Pruefmanager import Pruefmanager # type: ignore
|
|
|
|
|
|
from sn_basis.modules.linkpruefer import Linkpruefer # type: ignore
|
|
|
|
|
|
from sn_basis.modules.stilpruefer import Stilpruefer # type: ignore
|
|
|
|
|
|
|
|
|
|
|
|
# Typalias für Klarheit
|
|
|
|
|
|
Row = Dict[str, Any]
|
|
|
|
|
|
DataDict = Dict[str, List[Row]]
|
2026-01-08 17:13:43 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TabALogic:
|
|
|
|
|
|
"""
|
|
|
|
|
|
Kapselt die komplette Logik von Tab A:
|
|
|
|
|
|
- Verfahrens-Datenbank
|
|
|
|
|
|
- optionale Linkliste
|
|
|
|
|
|
- Verfahrensgebiet-Layer
|
2026-02-13 21:38:25 +01:00
|
|
|
|
|
|
|
|
|
|
Diese Klasse erwartet beim Erzeugen Instanzen von Pruefmanager, Linkpruefer
|
|
|
|
|
|
und Stilpruefer. Die validate_and_filter_rows-Methode verwendet diese
|
|
|
|
|
|
Instanzen über self.
|
2026-01-08 17:13:43 +01:00
|
|
|
|
"""
|
|
|
|
|
|
|
2026-02-13 21:38:25 +01:00
|
|
|
|
def __init__(self, pruefmanager: Pruefmanager, link_pruefer: Linkpruefer, stil_pruefer: Stilpruefer) -> None:
|
|
|
|
|
|
"""
|
|
|
|
|
|
:param pruefmanager: Instanz des Pruefmanagers (für verarbeite/report)
|
|
|
|
|
|
:param link_pruefer: Instanz des Linkpruefers (mit methode pruefe)
|
|
|
|
|
|
:param stil_pruefer: Instanz des Stilpruefers (mit methode pruefe)
|
|
|
|
|
|
"""
|
|
|
|
|
|
self.pruefmanager = pruefmanager
|
|
|
|
|
|
self.link_pruefer = link_pruefer
|
|
|
|
|
|
self.stil_pruefer = stil_pruefer
|
|
|
|
|
|
|
2026-01-08 17:13:43 +01:00
|
|
|
|
# -------------------------------
|
|
|
|
|
|
# Verfahrens-Datenbank
|
|
|
|
|
|
# -------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
def load_verfahrens_db(self) -> Optional[str]:
|
|
|
|
|
|
"""
|
|
|
|
|
|
Lädt die gespeicherte Verfahrens-Datenbank.
|
|
|
|
|
|
"""
|
|
|
|
|
|
path = get_variable("verfahrens_db", scope="project")
|
|
|
|
|
|
if path and file_exists(path):
|
|
|
|
|
|
return path
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
def set_verfahrens_db(self, path: Optional[str]) -> None:
|
|
|
|
|
|
"""
|
|
|
|
|
|
Speichert oder löscht die Verfahrens-Datenbank.
|
|
|
|
|
|
"""
|
|
|
|
|
|
if path:
|
|
|
|
|
|
set_variable("verfahrens_db", path, scope="project")
|
|
|
|
|
|
else:
|
|
|
|
|
|
set_variable("verfahrens_db", "", scope="project")
|
|
|
|
|
|
|
|
|
|
|
|
def create_new_verfahrens_db(self, path: str) -> bool:
|
|
|
|
|
|
"""
|
|
|
|
|
|
Legt eine neue leere GPKG-Datei an.
|
|
|
|
|
|
"""
|
|
|
|
|
|
if not path:
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
write_text(path, "")
|
|
|
|
|
|
except Exception:
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
self.set_verfahrens_db(path)
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
# -------------------------------
|
|
|
|
|
|
# Lokale Linkliste
|
|
|
|
|
|
# -------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
def load_linkliste(self) -> Optional[str]:
|
|
|
|
|
|
"""
|
|
|
|
|
|
Lädt die gespeicherte lokale Linkliste.
|
|
|
|
|
|
"""
|
|
|
|
|
|
path = get_variable("linkliste", scope="project")
|
|
|
|
|
|
if path and file_exists(path):
|
|
|
|
|
|
return path
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
def set_linkliste(self, path: Optional[str]) -> None:
|
|
|
|
|
|
"""
|
|
|
|
|
|
Speichert oder löscht die lokale Linkliste.
|
|
|
|
|
|
"""
|
|
|
|
|
|
if path:
|
|
|
|
|
|
set_variable("linkliste", path, scope="project")
|
|
|
|
|
|
else:
|
|
|
|
|
|
set_variable("linkliste", "", scope="project")
|
|
|
|
|
|
|
|
|
|
|
|
# -------------------------------
|
|
|
|
|
|
# Verfahrensgebiet-Layer
|
|
|
|
|
|
# -------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
def save_verfahrensgebiet_layer(self, layer) -> None:
|
|
|
|
|
|
"""
|
|
|
|
|
|
Speichert die ID des Verfahrensgebiet-Layers.
|
|
|
|
|
|
Ungültige Layer werden ignoriert.
|
|
|
|
|
|
"""
|
|
|
|
|
|
if layer is None:
|
|
|
|
|
|
set_variable("verfahrensgebiet_layer", "", scope="project")
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
if not hasattr(layer, "id") or not callable(layer.id):
|
|
|
|
|
|
set_variable("verfahrensgebiet_layer", "", scope="project")
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
layer_id = layer.id()
|
|
|
|
|
|
except Exception:
|
|
|
|
|
|
set_variable("verfahrensgebiet_layer", "", scope="project")
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
if not layer_id:
|
|
|
|
|
|
set_variable("verfahrensgebiet_layer", "", scope="project")
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
set_variable("verfahrensgebiet_layer", layer_id, scope="project")
|
|
|
|
|
|
|
|
|
|
|
|
def load_verfahrensgebiet_layer_id(self) -> Optional[str]:
|
|
|
|
|
|
"""
|
|
|
|
|
|
Lädt die gespeicherte Layer-ID.
|
|
|
|
|
|
"""
|
|
|
|
|
|
value = get_variable("verfahrensgebiet_layer", scope="project")
|
|
|
|
|
|
return value or None
|
|
|
|
|
|
|
|
|
|
|
|
def is_valid_verfahrensgebiet_layer(self, layer) -> bool:
|
|
|
|
|
|
"""
|
|
|
|
|
|
Prüft, ob ein Layer als Verfahrensgebiet geeignet ist.
|
|
|
|
|
|
"""
|
|
|
|
|
|
if not layer_exists(layer):
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
layer_type = get_layer_type(layer)
|
|
|
|
|
|
return layer_type == "vector"
|
2026-02-13 21:38:25 +01:00
|
|
|
|
|
|
|
|
|
|
# -------------------------------
|
|
|
|
|
|
# Validierung und Filterung von data_dict
|
|
|
|
|
|
# -------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
def validate_and_filter_rows(self, data_dict: DataDict) -> Tuple[DataDict, List[Any]]:
|
|
|
|
|
|
"""
|
|
|
|
|
|
Validiert und filtert die Zeilen aus `data_dict`.
|
|
|
|
|
|
|
|
|
|
|
|
Erwartete Struktur von `data_dict`: {'rows': [ {attr}, ... ]}.
|
|
|
|
|
|
|
|
|
|
|
|
Für jede Zeile werden die folgenden Attribute gelesen:
|
|
|
|
|
|
ident = attr['ident'] (Pflicht)
|
|
|
|
|
|
thema = attr['Inhalt'] (optional)
|
|
|
|
|
|
url = attr['Link'] (Pflicht)
|
|
|
|
|
|
stildatei = attr['Stildatei'] (optional)
|
|
|
|
|
|
provider = attr['Provider'] (Pflicht, wird uppercased)
|
|
|
|
|
|
|
|
|
|
|
|
Verhalten
|
|
|
|
|
|
- Pflichtfelder (ident, Link, Provider) müssen vorhanden und nicht-leer sein,
|
|
|
|
|
|
sonst wird die Zeile verworfen.
|
|
|
|
|
|
- Wenn Link nicht leer ist, wird self.link_pruefer.pruefe(url) aufgerufen.
|
|
|
|
|
|
- Ist das Ergebnis ok: Zeile wird behalten.
|
|
|
|
|
|
- Ist das Ergebnis nicht ok: Zeile wird verworfen; das verarbeitete
|
|
|
|
|
|
pruef_ergebnis wird gesammelt.
|
|
|
|
|
|
- Wenn Stildatei nicht leer ist, wird self.stil_pruefer.pruefe(stildatei) aufgerufen.
|
|
|
|
|
|
- Ist das Ergebnis ok: der Wert bleibt erhalten.
|
|
|
|
|
|
- Ist das Ergebnis nicht ok: das Feld `Stildatei` wird in der zurückgegebenen
|
|
|
|
|
|
Zeile auf None gesetzt; das verarbeitete pruef_ergebnis wird gesammelt.
|
|
|
|
|
|
- Alle pruef_ergebnis-Objekte werden an self.pruefmanager.verarbeite(...) übergeben.
|
|
|
|
|
|
Die verarbeiteten Ergebnisse werden in der Rückgabe-Liste gesammelt.
|
|
|
|
|
|
|
|
|
|
|
|
Rückgabe
|
|
|
|
|
|
- (valid_data_dict, processed_results)
|
|
|
|
|
|
valid_data_dict: {'rows': [valid_row1, valid_row2, ...]}
|
|
|
|
|
|
processed_results: Liste der vom Pruefmanager verarbeiteten pruef_ergebnis-Objekte
|
|
|
|
|
|
"""
|
|
|
|
|
|
processed_results: List[Any] = []
|
|
|
|
|
|
valid_rows: List[Row] = []
|
|
|
|
|
|
|
|
|
|
|
|
# Grundstruktur prüfen
|
|
|
|
|
|
if not isinstance(data_dict, dict):
|
|
|
|
|
|
return {"rows": []}, processed_results
|
|
|
|
|
|
|
|
|
|
|
|
rows = data_dict.get("rows", [])
|
|
|
|
|
|
if not isinstance(rows, (list, tuple)):
|
|
|
|
|
|
return {"rows": []}, processed_results
|
|
|
|
|
|
|
|
|
|
|
|
for raw in rows:
|
|
|
|
|
|
# Sicherstellen, dass raw ein Mapping ist
|
|
|
|
|
|
if not isinstance(raw, _Mapping):
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
|
|
ident = raw.get("ident")
|
|
|
|
|
|
inhalt = raw.get("Inhalt")
|
|
|
|
|
|
link = raw.get("Link")
|
|
|
|
|
|
stildatei = raw.get("Stildatei")
|
|
|
|
|
|
provider = raw.get("Provider")
|
|
|
|
|
|
|
|
|
|
|
|
# Pflichtfelder prüfen
|
|
|
|
|
|
if not ident or not link or not provider:
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
|
|
# Provider normalisieren
|
|
|
|
|
|
provider_norm = str(provider).upper()
|
|
|
|
|
|
|
|
|
|
|
|
# Link prüfen
|
|
|
|
|
|
pe_link = self.link_pruefer.pruefe(link)
|
|
|
|
|
|
processed_link = self.pruefmanager.verarbeite(pe_link)
|
|
|
|
|
|
if not getattr(processed_link, "ok", False):
|
|
|
|
|
|
processed_results.append(processed_link)
|
|
|
|
|
|
continue # Zeile verwerfen
|
|
|
|
|
|
|
|
|
|
|
|
# Stil prüfen (falls vorhanden)
|
|
|
|
|
|
if stildatei:
|
|
|
|
|
|
pe_stil = self.stil_pruefer.pruefe(stildatei)
|
|
|
|
|
|
processed_stil = self.pruefmanager.verarbeite(pe_stil)
|
|
|
|
|
|
if not getattr(processed_stil, "ok", False):
|
|
|
|
|
|
processed_results.append(processed_stil)
|
|
|
|
|
|
stildatei_value: Optional[str] = None
|
|
|
|
|
|
else:
|
|
|
|
|
|
stildatei_value = stildatei
|
|
|
|
|
|
else:
|
|
|
|
|
|
stildatei_value = None
|
|
|
|
|
|
|
|
|
|
|
|
# Validierte Zeile zusammenbauen
|
|
|
|
|
|
validated_row: Row = {
|
|
|
|
|
|
"ident": ident,
|
|
|
|
|
|
"Inhalt": inhalt,
|
|
|
|
|
|
"Link": link,
|
|
|
|
|
|
"Stildatei": stildatei_value,
|
|
|
|
|
|
"Provider": provider_norm,
|
|
|
|
|
|
}
|
|
|
|
|
|
valid_rows.append(validated_row)
|
|
|
|
|
|
|
|
|
|
|
|
result_dict: DataDict = {"rows": valid_rows}
|
|
|
|
|
|
return result_dict, processed_results
|