forked from AG_QGIS/Plugin_SN_Plan41
Compare commits
3 Commits
93b17e154c
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| c0bee6df98 | |||
| fc55735477 | |||
| b5f663d9de |
137
modules/listenauswerter.py
Normal file
137
modules/listenauswerter.py
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
#sn_plan41/modules/listenauswerter.py
|
||||||
|
from typing import Any, Dict, List, Mapping, Optional, Tuple
|
||||||
|
from collections.abc import Mapping as _Mapping
|
||||||
|
# Prüfer-Typen (werden als Instanzen erwartet)
|
||||||
|
from sn_basis.modules.Pruefmanager import Pruefmanager # type: ignore
|
||||||
|
from sn_basis.modules.pruef_ergebnis import pruef_ergebnis
|
||||||
|
from sn_basis.modules.stilpruefer import Stilpruefer # type: ignore
|
||||||
|
|
||||||
|
class Listenauswerter:
|
||||||
|
"""
|
||||||
|
Validiert Zeilen aus einem DataDict, das vom DataGrabber stammt.
|
||||||
|
Erwartet wird die Struktur::
|
||||||
|
|
||||||
|
{"rows": [ {attr}, ... ]}
|
||||||
|
|
||||||
|
Die Linkprüfung entfällt vollständig, da der DataGrabber nur gültige
|
||||||
|
Links liefert. Diese Methode prüft ausschließlich die Konsistenz der
|
||||||
|
Zeilen mit dem erwarteten Datenschema und führt optional eine
|
||||||
|
Stilprüfung durch.
|
||||||
|
"""
|
||||||
|
def __init__(self, pruefmanager, stil_pruefer):
|
||||||
|
""" Parameters
|
||||||
|
----------
|
||||||
|
pruefmanager: Instanz des Pruefmanagers, der pruef_ergebnis verarbeitet.
|
||||||
|
stil_pruefer: Instanz des Stilpruefers, der Stildateien prüft.
|
||||||
|
"""
|
||||||
|
self.pruefmanager = pruefmanager
|
||||||
|
self.stil_pruefer = stil_pruefer
|
||||||
|
|
||||||
|
def validate_rows(
|
||||||
|
self,
|
||||||
|
data_dict: Dict[str, List[Mapping[str, Any]]]
|
||||||
|
) -> Tuple[Dict[str, List[Mapping[str, Any]]], List[Any]]:
|
||||||
|
"""
|
||||||
|
Validiert die Zeilen aus ``data_dict`` anhand des erwarteten Schemas.
|
||||||
|
|
||||||
|
Erwartete Felder pro Zeile
|
||||||
|
--------------------------
|
||||||
|
Pflichtfelder:
|
||||||
|
- ``ident``: eindeutige Kennung
|
||||||
|
- ``Link``: bereits geprüfter Link (vom DataGrabber garantiert gültig)
|
||||||
|
- ``Provider``: Datenquelle (wird in Großbuchstaben normalisiert)
|
||||||
|
|
||||||
|
Optionale Felder:
|
||||||
|
- ``Inhalt``: thematische Beschreibung
|
||||||
|
- ``Stildatei``: Pfad zur Stildatei (falls vorhanden)
|
||||||
|
|
||||||
|
Verhalten
|
||||||
|
---------
|
||||||
|
- Zeilen, denen Pflichtfelder fehlen oder deren Werte leer sind,
|
||||||
|
werden verworfen.
|
||||||
|
- ``Provider`` wird in Großbuchstaben normalisiert.
|
||||||
|
- Wenn ``Stildatei`` vorhanden ist, wird sie durch
|
||||||
|
``self.stil_pruefer.pruefe(...)`` geprüft.
|
||||||
|
- Bei OK bleibt der Wert erhalten.
|
||||||
|
- Bei nicht OK wird ``Stildatei`` auf ``None`` gesetzt und das
|
||||||
|
verarbeitete Prüfergebnis gesammelt.
|
||||||
|
- Alle Prüfergebnisse werden durch ``self.pruefmanager.verarbeite(...)``
|
||||||
|
geleitet und in der Rückgabe gesammelt.
|
||||||
|
|
||||||
|
Rückgabe
|
||||||
|
--------
|
||||||
|
Tuple[Dict[str, List[Mapping[str, Any]]]], List[Any]]
|
||||||
|
- ``valid_data_dict``: enthält nur Zeilen, die dem Schema entsprechen
|
||||||
|
- ``processed_results``: Liste der verarbeiteten Prüfergebnisse
|
||||||
|
|
||||||
|
Hinweise
|
||||||
|
--------
|
||||||
|
- Diese Methode führt **keine Linkprüfung** durch.
|
||||||
|
- Die Verantwortung für die Linkvalidität liegt vollständig beim DataGrabber.
|
||||||
|
- Die Methode verändert die Zeilen nur minimal (Provider‑Normalisierung,
|
||||||
|
Stildatei ggf. auf ``None``).
|
||||||
|
"""
|
||||||
|
|
||||||
|
processed_results: List[Any] = []
|
||||||
|
valid_rows: List[Mapping[str, Any]] = []
|
||||||
|
|
||||||
|
# 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:
|
||||||
|
# Fehler dokumentieren
|
||||||
|
pe = pruef_ergebnis(
|
||||||
|
ok=False,
|
||||||
|
meldung="Pflichtfelder fehlen oder sind leer",
|
||||||
|
aktion="pflichtfelder_fehlen",
|
||||||
|
kontext=raw,
|
||||||
|
)
|
||||||
|
processed_results.append(self.pruefmanager.verarbeite(pe))
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Provider normalisieren
|
||||||
|
provider_norm = str(provider).upper()
|
||||||
|
|
||||||
|
# Stildatei 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 = {
|
||||||
|
"ident": ident,
|
||||||
|
"Inhalt": inhalt,
|
||||||
|
"Link": link,
|
||||||
|
"Stildatei": stildatei_value,
|
||||||
|
"Provider": provider_norm,
|
||||||
|
}
|
||||||
|
|
||||||
|
valid_rows.append(validated_row)
|
||||||
|
|
||||||
|
result_dict = {"rows": valid_rows}
|
||||||
|
return result_dict, processed_results
|
||||||
@@ -1,7 +1,45 @@
|
|||||||
|
#sn_plan41/ui/dockwidget.py
|
||||||
from sn_basis.ui.tabs.settings_tab import SettingsTab
|
from sn_basis.ui.tabs.settings_tab import SettingsTab
|
||||||
from sn_plan41.ui.tab_a_ui import TabA
|
from sn_plan41.ui.tab_a_ui import TabA
|
||||||
#from sn_plan41.ui.tabs.tab_b import TabB
|
#from sn_plan41.ui.tabs.tab_b import TabB
|
||||||
from sn_basis.ui.base_dockwidget import BaseDockWidget
|
from sn_basis.ui.base_dockwidget import BaseDockWidget
|
||||||
|
from sn_basis.functions.qt_wrapper import QTabWidget
|
||||||
|
from sn_basis.functions.message_wrapper import error
|
||||||
|
|
||||||
|
|
||||||
class DockWidget(BaseDockWidget):
|
class DockWidget(BaseDockWidget):
|
||||||
tabs = [TabA, SettingsTab]
|
tabs = [TabA, SettingsTab]
|
||||||
|
|
||||||
|
def __init__(self, parent=None, subtitle="", pruefmanager=None, data_grabber=None):
|
||||||
|
super().__init__(parent, subtitle)
|
||||||
|
|
||||||
|
# Services als Attribute speichern
|
||||||
|
self.pruefmanager = pruefmanager
|
||||||
|
self.data_grabber = data_grabber
|
||||||
|
|
||||||
|
# Tabs NACH Services initialisieren (override der Basis-Logik)
|
||||||
|
self._init_tabs_with_services()
|
||||||
|
|
||||||
|
def _init_tabs_with_services(self):
|
||||||
|
"""Tabs mit pruefmanager/data_grabber initialisieren"""
|
||||||
|
try:
|
||||||
|
# Bestehendes TabWidget löschen
|
||||||
|
self.setWidget(None)
|
||||||
|
|
||||||
|
tab_widget = QTabWidget()
|
||||||
|
for tab_class in self.tabs:
|
||||||
|
tab_instance = tab_class(self) # parent=self.dockwidget
|
||||||
|
tab_title = getattr(tab_class, "tab_title", tab_class.__name__)
|
||||||
|
tab_widget.addTab(tab_instance, tab_title)
|
||||||
|
|
||||||
|
# Services durchreichen
|
||||||
|
if hasattr(tab_instance, 'set_services'):
|
||||||
|
tab_instance.set_services(
|
||||||
|
pruefmanager=self.pruefmanager,
|
||||||
|
data_grabber=self.data_grabber
|
||||||
|
)
|
||||||
|
|
||||||
|
self.setWidget(tab_widget)
|
||||||
|
except Exception as e:
|
||||||
|
error("Services-Tabs konnten nicht initialisiert werden", str(e))
|
||||||
|
|
||||||
|
|||||||
@@ -4,87 +4,112 @@ sn_plan41/ui/tab_a_logic.py – Fachlogik für Tab A (Daten)
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any, Dict, List, Optional, Tuple
|
from typing import Any, Dict, List, Optional
|
||||||
from collections.abc import Mapping as _Mapping
|
from collections.abc import Mapping as _Mapping
|
||||||
|
import os
|
||||||
|
|
||||||
from sn_basis.functions.variable_wrapper import ( # type: ignore
|
from sn_basis.functions.qgiscore_wrapper import (
|
||||||
|
QgsVectorFileWriter,
|
||||||
|
QgsVectorLayer,
|
||||||
|
QgsProject,
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
from sn_basis.functions.variable_wrapper import (
|
||||||
get_variable,
|
get_variable,
|
||||||
set_variable,
|
set_variable,
|
||||||
)
|
)
|
||||||
from sn_basis.functions.sys_wrapper import ( # type: ignore
|
from sn_basis.functions.sys_wrapper import file_exists
|
||||||
file_exists,
|
from sn_basis.functions.ly_existence_wrapper import layer_exists
|
||||||
write_text,
|
from sn_basis.functions.ly_metadata_wrapper import get_layer_type
|
||||||
)
|
|
||||||
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)
|
# Prüfer-Typen
|
||||||
from sn_basis.modules.Pruefmanager import Pruefmanager # type: ignore
|
from sn_basis.modules.Pruefmanager import Pruefmanager
|
||||||
from sn_basis.modules.linkpruefer import Linkpruefer # type: ignore
|
from sn_basis.modules.linkpruefer import Linkpruefer
|
||||||
from sn_basis.modules.stilpruefer import Stilpruefer # type: ignore
|
from sn_basis.modules.stilpruefer import Stilpruefer
|
||||||
|
|
||||||
# Typalias für Klarheit
|
|
||||||
Row = Dict[str, Any]
|
Row = Dict[str, Any]
|
||||||
DataDict = Dict[str, List[Row]]
|
DataDict = Dict[str, List[Row]]
|
||||||
|
|
||||||
|
|
||||||
class TabALogic:
|
class TabALogic:
|
||||||
"""
|
"""
|
||||||
Kapselt die komplette Logik von Tab A:
|
Kapselt die Fachlogik von Tab A. Verfahrens-DB wird **nicht** bei Pfad-Auswahl,
|
||||||
- Verfahrens-Datenbank
|
sondern erst beim ersten Layer-Schreiben angelegt (alte Logik).
|
||||||
- optionale Linkliste
|
|
||||||
- Verfahrensgebiet-Layer
|
|
||||||
|
|
||||||
Diese Klasse erwartet beim Erzeugen Instanzen von Pruefmanager, Linkpruefer
|
|
||||||
und Stilpruefer. Die validate_and_filter_rows-Methode verwendet diese
|
|
||||||
Instanzen über self.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, pruefmanager: Pruefmanager, link_pruefer: Linkpruefer, stil_pruefer: Stilpruefer) -> None:
|
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.pruefmanager = pruefmanager
|
||||||
self.link_pruefer = link_pruefer
|
self.link_pruefer = link_pruefer
|
||||||
self.stil_pruefer = stil_pruefer
|
self.stil_pruefer = stil_pruefer
|
||||||
|
|
||||||
# -------------------------------
|
# -------------------------------
|
||||||
# Verfahrens-Datenbank
|
# Verfahrens-Datenbank (Pfad-Management)
|
||||||
# -------------------------------
|
# -------------------------------
|
||||||
|
|
||||||
def load_verfahrens_db(self) -> Optional[str]:
|
def load_verfahrens_db(self) -> Optional[str]:
|
||||||
"""
|
"""Lädt den gespeicherten Verfahrens-DB-Pfad (Datei muss nicht existieren)."""
|
||||||
Lädt die gespeicherte Verfahrens-Datenbank.
|
|
||||||
"""
|
|
||||||
path = get_variable("verfahrens_db", scope="project")
|
path = get_variable("verfahrens_db", scope="project")
|
||||||
if path and file_exists(path):
|
return path or None
|
||||||
return path
|
|
||||||
return None
|
|
||||||
|
|
||||||
def set_verfahrens_db(self, path: Optional[str]) -> None:
|
def set_verfahrens_db(self, path: Optional[str]) -> None:
|
||||||
"""
|
"""Speichert den Verfahrens-DB-Pfad (Datei wird später angelegt)."""
|
||||||
Speichert oder löscht die Verfahrens-Datenbank.
|
|
||||||
"""
|
|
||||||
if path:
|
if path:
|
||||||
set_variable("verfahrens_db", path, scope="project")
|
set_variable("verfahrens_db", path, scope="project")
|
||||||
else:
|
else:
|
||||||
set_variable("verfahrens_db", "", scope="project")
|
set_variable("verfahrens_db", "", scope="project")
|
||||||
|
|
||||||
def create_new_verfahrens_db(self, path: str) -> bool:
|
# -------------------------------
|
||||||
|
# Layer → Verfahrens-DB schreiben (alte Logik!)
|
||||||
|
# -------------------------------
|
||||||
|
|
||||||
|
def write_layer_to_verfahrens_db(
|
||||||
|
self,
|
||||||
|
source_layer: QgsVectorLayer,
|
||||||
|
zielpfad: str,
|
||||||
|
layer_name: str,
|
||||||
|
) -> bool:
|
||||||
"""
|
"""
|
||||||
Legt eine neue leere GPKG-Datei an.
|
Schreibt einen Layer in die Verfahrens-DB.
|
||||||
|
Legt GPKG **bei Bedarf neu an** (wie puffer_setzen im alten Code).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
source_layer: Layer zum Exportieren (z.B. aus DataGrabber)
|
||||||
|
zielpfad: Vom Dateiprüfer geprüfter Ziel-GPKG-Pfad
|
||||||
|
layer_name: Name des Layers in der GPKG
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True wenn erfolgreich
|
||||||
"""
|
"""
|
||||||
if not path:
|
if not zielpfad or not source_layer or not source_layer.isValid():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
try:
|
# Optionen wie im alten puffer_setzen
|
||||||
write_text(path, "")
|
opts = QgsVectorFileWriter.SaveVectorOptions()
|
||||||
except Exception:
|
opts.driverName = "GPKG"
|
||||||
|
opts.fileEncoding = "UTF-8"
|
||||||
|
opts.layerName = layer_name
|
||||||
|
|
||||||
|
# Alte Logik: bei neuem Pfad komplett neue GPKG, sonst Layer überschreiben
|
||||||
|
if not os.path.exists(zielpfad):
|
||||||
|
opts.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteFile
|
||||||
|
else:
|
||||||
|
opts.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer
|
||||||
|
|
||||||
|
transform_context = QgsProject.instance().transformContext()
|
||||||
|
|
||||||
|
error = QgsVectorFileWriter.writeAsVectorFormatV3(
|
||||||
|
source_layer,
|
||||||
|
zielpfad,
|
||||||
|
transform_context,
|
||||||
|
opts,
|
||||||
|
)
|
||||||
|
|
||||||
|
if error != QgsVectorFileWriter.NoError:
|
||||||
|
print(f"Fehler beim Schreiben nach {zielpfad}: {error}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self.set_verfahrens_db(path)
|
# Pfad jetzt auch als "Verfahrens-DB" merken
|
||||||
|
self.set_verfahrens_db(zielpfad)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# -------------------------------
|
# -------------------------------
|
||||||
@@ -92,18 +117,12 @@ class TabALogic:
|
|||||||
# -------------------------------
|
# -------------------------------
|
||||||
|
|
||||||
def load_linkliste(self) -> Optional[str]:
|
def load_linkliste(self) -> Optional[str]:
|
||||||
"""
|
|
||||||
Lädt die gespeicherte lokale Linkliste.
|
|
||||||
"""
|
|
||||||
path = get_variable("linkliste", scope="project")
|
path = get_variable("linkliste", scope="project")
|
||||||
if path and file_exists(path):
|
if path and file_exists(path):
|
||||||
return path
|
return path
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def set_linkliste(self, path: Optional[str]) -> None:
|
def set_linkliste(self, path: Optional[str]) -> None:
|
||||||
"""
|
|
||||||
Speichert oder löscht die lokale Linkliste.
|
|
||||||
"""
|
|
||||||
if path:
|
if path:
|
||||||
set_variable("linkliste", path, scope="project")
|
set_variable("linkliste", path, scope="project")
|
||||||
else:
|
else:
|
||||||
@@ -114,10 +133,6 @@ class TabALogic:
|
|||||||
# -------------------------------
|
# -------------------------------
|
||||||
|
|
||||||
def save_verfahrensgebiet_layer(self, layer) -> None:
|
def save_verfahrensgebiet_layer(self, layer) -> None:
|
||||||
"""
|
|
||||||
Speichert die ID des Verfahrensgebiet-Layers.
|
|
||||||
Ungültige Layer werden ignoriert.
|
|
||||||
"""
|
|
||||||
if layer is None:
|
if layer is None:
|
||||||
set_variable("verfahrensgebiet_layer", "", scope="project")
|
set_variable("verfahrensgebiet_layer", "", scope="project")
|
||||||
return
|
return
|
||||||
@@ -139,115 +154,12 @@ class TabALogic:
|
|||||||
set_variable("verfahrensgebiet_layer", layer_id, scope="project")
|
set_variable("verfahrensgebiet_layer", layer_id, scope="project")
|
||||||
|
|
||||||
def load_verfahrensgebiet_layer_id(self) -> Optional[str]:
|
def load_verfahrensgebiet_layer_id(self) -> Optional[str]:
|
||||||
"""
|
|
||||||
Lädt die gespeicherte Layer-ID.
|
|
||||||
"""
|
|
||||||
value = get_variable("verfahrensgebiet_layer", scope="project")
|
value = get_variable("verfahrensgebiet_layer", scope="project")
|
||||||
return value or None
|
return value or None
|
||||||
|
|
||||||
def is_valid_verfahrensgebiet_layer(self, layer) -> bool:
|
def is_valid_verfahrensgebiet_layer(self, layer) -> bool:
|
||||||
"""
|
|
||||||
Prüft, ob ein Layer als Verfahrensgebiet geeignet ist.
|
|
||||||
"""
|
|
||||||
if not layer_exists(layer):
|
if not layer_exists(layer):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
layer_type = get_layer_type(layer)
|
layer_type = get_layer_type(layer)
|
||||||
return layer_type == "vector"
|
return layer_type == "vector"
|
||||||
|
|
||||||
# -------------------------------
|
|
||||||
# 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
|
|
||||||
|
|||||||
398
ui/tab_a_ui.py
398
ui/tab_a_ui.py
@@ -1,4 +1,7 @@
|
|||||||
# sn_plan41/ui/tab_a_ui.py – UI für Tab A (Daten)
|
"""
|
||||||
|
sn_plan41/ui/tab_a_ui.py – UI für Tab A (Daten)
|
||||||
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
@@ -16,77 +19,84 @@ from sn_basis.functions.qt_wrapper import (
|
|||||||
ArrowRight,
|
ArrowRight,
|
||||||
SizePolicyPreferred,
|
SizePolicyPreferred,
|
||||||
SizePolicyMaximum,
|
SizePolicyMaximum,
|
||||||
ComboBox,
|
QComboBox,
|
||||||
)
|
)
|
||||||
from sn_basis.functions.qgisui_wrapper import QgsFileWidget, QgsMapLayerComboBox
|
from sn_basis.functions.qgisui_wrapper import QgsFileWidget, QgsMapLayerComboBox
|
||||||
from sn_basis.functions.qgiscore_wrapper import QgsProject, QgsMapLayerProxyModel
|
from sn_basis.functions.qgiscore_wrapper import QgsProject, QgsMapLayerProxyModel
|
||||||
from sn_basis.functions.variable_wrapper import get_variable, set_variable
|
from sn_basis.functions.variable_wrapper import get_variable, set_variable
|
||||||
|
from sn_basis.modules.pruef_ergebnis import pruef_ergebnis
|
||||||
|
|
||||||
from sn_plan41.ui.tab_a_logic import TabALogic
|
# Services (werden von DockWidget injiziert)
|
||||||
|
|
||||||
# Prüf‑Workflow / DataGrabber (werden zur Laufzeit vom Pruefmanager/Pruefern verwendet)
|
|
||||||
from sn_basis.modules.Dateipruefer import Dateipruefer
|
|
||||||
from sn_basis.modules.Pruefmanager import Pruefmanager
|
from sn_basis.modules.Pruefmanager import Pruefmanager
|
||||||
from sn_basis.modules.DataGrabber import DataGrabber
|
from sn_basis.modules.DataGrabber import DataGrabber
|
||||||
from sn_basis.modules.linkpruefer import Linkpruefer
|
from sn_basis.modules.Dateipruefer import Dateipruefer
|
||||||
from sn_basis.modules.stilpruefer import Stilpruefer
|
from sn_plan41.ui.tab_a_logic import TabALogic
|
||||||
# Raumfilter-Optionen
|
|
||||||
|
# Konstanten
|
||||||
RAUMFILTER_VAR = "Raumfilter"
|
RAUMFILTER_VAR = "Raumfilter"
|
||||||
RAUMFILTER_OPTIONS = ("Verfahrensgebiet", "Pufferlayer", "ohne")
|
RAUMFILTER_OPTIONS = ("Verfahrensgebiet", "Pufferlayer", "ohne")
|
||||||
RAUMFILTER_DEFAULT = "Pufferlayer"
|
RAUMFILTER_DEFAULT = "Pufferlayer"
|
||||||
pm = Pruefmanager(ui_modus="qgis")
|
|
||||||
lp = Linkpruefer()
|
|
||||||
sp = Stilpruefer()
|
|
||||||
|
|
||||||
|
|
||||||
class TabA(QWidget):
|
class TabA(QWidget):
|
||||||
"""
|
"""
|
||||||
UI-Klasse für Tab A (Daten).
|
UI-Klasse für Tab A (Daten) des Plan41-Plugins.
|
||||||
|
|
||||||
Diese bereinigte Version enthält ausschließlich UI-Elemente und
|
Zuständig für:
|
||||||
einfache, nicht-validierende Callback-Handler. Alle fachlichen Prüfungen
|
- Anzeige und Auswahl von Verfahrens-DB, Linklisten und Layern
|
||||||
und Fehlerbehandlungen werden zur Laufzeit vom Pruefmanager und den Prüfern
|
- Steuerung der Pipeline über "Fachdaten laden"
|
||||||
übernommen.
|
- Persistierung von UI-States via Projektvariablen
|
||||||
|
|
||||||
|
Services (Pruefmanager, DataGrabber) werden zur Laufzeit vom DockWidget injiziert.
|
||||||
|
Alle fachlichen Prüfungen laufen über den zentralen Pruefmanager.
|
||||||
"""
|
"""
|
||||||
tab_title = "Daten"
|
|
||||||
|
|
||||||
def __init__(self, parent=None, pruefmanager=None, link_pruefer=None, stil_pruefer=None, build_ui=True):
|
tab_title = "Daten" #: Tab-Titel für BaseDockWidget
|
||||||
|
|
||||||
|
def __init__(self, parent: Optional[QWidget] = None):
|
||||||
|
"""
|
||||||
|
Initialisiert die UI-Struktur.
|
||||||
|
|
||||||
|
Services werden später über :meth:`set_services` injiziert.
|
||||||
|
|
||||||
|
:param parent: Parent-Widget (typischerweise DockWidget)
|
||||||
|
"""
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.parent = parent
|
|
||||||
self.tab_title = "Daten"
|
|
||||||
|
|
||||||
# Logik-Adapter (TabALogic verwaltet persistente Projektvariablen)
|
# Services (werden von DockWidget gesetzt)
|
||||||
self.logic = TabALogic(pruefmanager=pruefmanager, link_pruefer=link_pruefer, stil_pruefer=stil_pruefer)
|
self.pruefmanager: Optional[Pruefmanager] = None
|
||||||
|
self.data_grabber: Optional[DataGrabber] = None
|
||||||
# Prüfmanager-Instanz (UI-Modus wird zur Laufzeit vom Pruefmanager gehandhabt)
|
self.logic: Optional[TabALogic] = None
|
||||||
self.pruefmanager = Pruefmanager(ui_modus="qgis")
|
|
||||||
|
|
||||||
# DataGrabber-Instanz (synchroner Aufruf; Prüfungen übernimmt Pruefmanager/Pruefer)
|
|
||||||
self.data_grabber = DataGrabber(pruefmanager=self.pruefmanager)
|
|
||||||
|
|
||||||
# Platzhalter, die vom Plugin oder Nutzer gesetzt werden können
|
|
||||||
self._attributes_list = [] # optionale Attributliste (z. B. Excel-Import)
|
|
||||||
self._pufferlayer = None # optionaler Layer (Verfahrensgebiet)
|
|
||||||
|
|
||||||
|
# UI-State
|
||||||
self.verfahrens_db: Optional[str] = None
|
self.verfahrens_db: Optional[str] = None
|
||||||
self.lokale_linkliste: Optional[str] = None
|
self.lokale_linkliste: Optional[str] = None
|
||||||
|
self._pufferlayer = None
|
||||||
|
self._attributes_list = []
|
||||||
|
|
||||||
# UI-Widget-Referenz für Raumfilter
|
# UI-Referenzen
|
||||||
self._raumfilter_combo: Optional[ComboBox] = None
|
self._raumfilter_combo: Optional[QComboBox] = None
|
||||||
|
|
||||||
if build_ui:
|
|
||||||
self._build_ui()
|
self._build_ui()
|
||||||
self._restore_state()
|
self._restore_state()
|
||||||
|
|
||||||
# ---------------------------------------------------------
|
def set_services(self, pruefmanager: Pruefmanager, data_grabber: DataGrabber) -> None:
|
||||||
# UI-Aufbau
|
"""
|
||||||
# ---------------------------------------------------------
|
Injiziert Services vom übergeordneten DockWidget.
|
||||||
|
|
||||||
|
:param pruefmanager: Zentrale Prüfmanager-Instanz
|
||||||
|
:param data_grabber: DataGrabber für Quellenprüfung/Abruf
|
||||||
|
"""
|
||||||
|
self.pruefmanager = pruefmanager
|
||||||
|
self.data_grabber = data_grabber
|
||||||
|
|
||||||
def _build_ui(self) -> None:
|
def _build_ui(self) -> None:
|
||||||
|
"""Erstellt die komplette UI-Hierarchie mit allen Gruppen."""
|
||||||
main_layout = QVBoxLayout()
|
main_layout = QVBoxLayout()
|
||||||
main_layout.setSpacing(4)
|
main_layout.setSpacing(4)
|
||||||
main_layout.setContentsMargins(4, 4, 4, 4)
|
main_layout.setContentsMargins(4, 4, 4, 4)
|
||||||
|
|
||||||
# Verfahrens-Datenbank Gruppe
|
# === VERFAHRENS-DATENBANK ===
|
||||||
self.group_button = QToolButton()
|
self.group_button = QToolButton()
|
||||||
self.group_button.setText("Verfahrens-Datenbank")
|
self.group_button.setText("Verfahrens-Datenbank")
|
||||||
self.group_button.setCheckable(True)
|
self.group_button.setCheckable(True)
|
||||||
@@ -99,13 +109,11 @@ class TabA(QWidget):
|
|||||||
|
|
||||||
self.group_content = QWidget()
|
self.group_content = QWidget()
|
||||||
self.group_content.setSizePolicy(SizePolicyPreferred, SizePolicyMaximum)
|
self.group_content.setSizePolicy(SizePolicyPreferred, SizePolicyMaximum)
|
||||||
|
|
||||||
group_layout = QVBoxLayout()
|
group_layout = QVBoxLayout()
|
||||||
group_layout.setSpacing(2)
|
group_layout.setSpacing(2)
|
||||||
group_layout.setContentsMargins(10, 4, 4, 4)
|
group_layout.setContentsMargins(10, 4, 4, 4)
|
||||||
|
|
||||||
group_layout.addWidget(QLabel("bestehende Datei auswählen"))
|
group_layout.addWidget(QLabel("bestehende Datei auswählen"))
|
||||||
|
|
||||||
self.file_widget = QgsFileWidget()
|
self.file_widget = QgsFileWidget()
|
||||||
self.file_widget.setStorageMode(QgsFileWidget.GetFile)
|
self.file_widget.setStorageMode(QgsFileWidget.GetFile)
|
||||||
self.file_widget.setFilter("Geopackage (*.gpkg)")
|
self.file_widget.setFilter("Geopackage (*.gpkg)")
|
||||||
@@ -113,7 +121,6 @@ class TabA(QWidget):
|
|||||||
group_layout.addWidget(self.file_widget)
|
group_layout.addWidget(self.file_widget)
|
||||||
|
|
||||||
group_layout.addWidget(QLabel("-oder-"))
|
group_layout.addWidget(QLabel("-oder-"))
|
||||||
|
|
||||||
self.btn_new = QPushButton("Neue Verfahrens-DB anlegen")
|
self.btn_new = QPushButton("Neue Verfahrens-DB anlegen")
|
||||||
self.btn_new.clicked.connect(self._create_new_gpkg)
|
self.btn_new.clicked.connect(self._create_new_gpkg)
|
||||||
group_layout.addWidget(self.btn_new)
|
group_layout.addWidget(self.btn_new)
|
||||||
@@ -121,7 +128,7 @@ class TabA(QWidget):
|
|||||||
self.group_content.setLayout(group_layout)
|
self.group_content.setLayout(group_layout)
|
||||||
main_layout.addWidget(self.group_content)
|
main_layout.addWidget(self.group_content)
|
||||||
|
|
||||||
# Optionale Linkliste
|
# === OPTIONALE LINKLISTE ===
|
||||||
self.optional_button = QToolButton()
|
self.optional_button = QToolButton()
|
||||||
self.optional_button.setText("Optional: Lokale Linkliste")
|
self.optional_button.setText("Optional: Lokale Linkliste")
|
||||||
self.optional_button.setCheckable(True)
|
self.optional_button.setCheckable(True)
|
||||||
@@ -134,13 +141,11 @@ class TabA(QWidget):
|
|||||||
|
|
||||||
self.optional_content = QWidget()
|
self.optional_content = QWidget()
|
||||||
self.optional_content.setSizePolicy(SizePolicyPreferred, SizePolicyMaximum)
|
self.optional_content.setSizePolicy(SizePolicyPreferred, SizePolicyMaximum)
|
||||||
|
|
||||||
optional_layout = QVBoxLayout()
|
optional_layout = QVBoxLayout()
|
||||||
optional_layout.setSpacing(2)
|
optional_layout.setSpacing(2)
|
||||||
optional_layout.setContentsMargins(10, 4, 4, 20)
|
optional_layout.setContentsMargins(10, 4, 4, 20)
|
||||||
|
|
||||||
optional_layout.addWidget(QLabel("(frei lassen für globale Linkliste)"))
|
optional_layout.addWidget(QLabel("(frei lassen für globale Linkliste)"))
|
||||||
|
|
||||||
self.linkliste_widget = QgsFileWidget()
|
self.linkliste_widget = QgsFileWidget()
|
||||||
self.linkliste_widget.setStorageMode(QgsFileWidget.GetFile)
|
self.linkliste_widget.setStorageMode(QgsFileWidget.GetFile)
|
||||||
self.linkliste_widget.setFilter("Excelliste (*.xlsx)")
|
self.linkliste_widget.setFilter("Excelliste (*.xlsx)")
|
||||||
@@ -151,221 +156,200 @@ class TabA(QWidget):
|
|||||||
self.optional_content.setVisible(False)
|
self.optional_content.setVisible(False)
|
||||||
main_layout.addWidget(self.optional_content)
|
main_layout.addWidget(self.optional_content)
|
||||||
|
|
||||||
# Layer-Auswahl
|
# === LAYER-AUSWAHL + RAUMFILTER ===
|
||||||
layer_label = QLabel("Verfahrensgebiet-Layer auswählen")
|
layer_label = QLabel("Verfahrensgebiet-Layer auswählen")
|
||||||
layer_label.setStyleSheet("font-weight: bold; margin-top: 6px;")
|
layer_label.setStyleSheet("font-weight: bold; margin-top: 6px;")
|
||||||
main_layout.addWidget(layer_label)
|
main_layout.addWidget(layer_label)
|
||||||
|
|
||||||
self.layer_combo = QgsMapLayerComboBox()
|
self.layer_combo = QgsMapLayerComboBox()
|
||||||
self.layer_combo.setSizePolicy(SizePolicyPreferred, SizePolicyMaximum)
|
|
||||||
self.layer_combo.setFilters(QgsMapLayerProxyModel.VectorLayer)
|
self.layer_combo.setFilters(QgsMapLayerProxyModel.VectorLayer)
|
||||||
self.layer_combo.layerChanged.connect(self._on_layer_changed)
|
self.layer_combo.layerChanged.connect(self._on_layer_changed)
|
||||||
main_layout.addWidget(self.layer_combo)
|
main_layout.addWidget(self.layer_combo)
|
||||||
|
|
||||||
# Raumfilter-Label + ComboBox (unterhalb der Layer-Auswahl)
|
|
||||||
main_layout.addWidget(QLabel("Raumfilter"))
|
main_layout.addWidget(QLabel("Raumfilter"))
|
||||||
self._raumfilter_combo = ComboBox(self)
|
self._raumfilter_combo = QComboBox(self)
|
||||||
# Fülle Optionen (Wrapper stellt addItems bereit)
|
self._raumfilter_combo.addItems(RAUMFILTER_OPTIONS)
|
||||||
try:
|
|
||||||
self._raumfilter_combo.addItems(list(RAUMFILTER_OPTIONS))
|
|
||||||
except Exception:
|
|
||||||
# fallback: iterativ hinzufügen, falls Wrapper andere API hat
|
|
||||||
for opt in RAUMFILTER_OPTIONS:
|
|
||||||
if hasattr(self._raumfilter_combo, "addItem"):
|
|
||||||
self._raumfilter_combo.addItem(opt)
|
|
||||||
|
|
||||||
# Initialisiere Auswahl aus Projekt-Variable oder Default
|
|
||||||
stored = get_variable(RAUMFILTER_VAR, scope="project")
|
|
||||||
if isinstance(stored, str) and stored in RAUMFILTER_OPTIONS:
|
|
||||||
try:
|
|
||||||
self._raumfilter_combo.setCurrentText(stored)
|
|
||||||
except Exception:
|
|
||||||
try:
|
|
||||||
idx = self._raumfilter_combo.findText(stored)
|
|
||||||
if idx is not None and idx >= 0:
|
|
||||||
self._raumfilter_combo.setCurrentIndex(idx)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
self._raumfilter_combo.setCurrentText(RAUMFILTER_DEFAULT)
|
|
||||||
except Exception:
|
|
||||||
try:
|
|
||||||
idx = self._raumfilter_combo.findText(RAUMFILTER_DEFAULT)
|
|
||||||
if idx is not None and idx >= 0:
|
|
||||||
self._raumfilter_combo.setCurrentIndex(idx)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
# persistiere Default, falls noch kein Wert gesetzt
|
|
||||||
if not stored:
|
|
||||||
set_variable(RAUMFILTER_VAR, RAUMFILTER_DEFAULT, scope="project")
|
|
||||||
|
|
||||||
# Signal: bei Änderung Variable setzen
|
|
||||||
try:
|
|
||||||
self._raumfilter_combo.currentTextChanged.connect(self._on_raumfilter_changed)
|
self._raumfilter_combo.currentTextChanged.connect(self._on_raumfilter_changed)
|
||||||
except Exception:
|
|
||||||
try:
|
|
||||||
self._raumfilter_combo.current_text_changed.connect(self._on_raumfilter_changed)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
main_layout.addWidget(self._raumfilter_combo)
|
main_layout.addWidget(self._raumfilter_combo)
|
||||||
|
|
||||||
# Aktion: Fachdaten laden
|
# === PIPELINE-STEUERUNG ===
|
||||||
self.btn_load = QPushButton("Fachdaten laden")
|
self.btn_pipeline = QPushButton("Fachdaten laden")
|
||||||
self.btn_load.clicked.connect(self._on_load_fachdaten)
|
self.btn_pipeline.setToolTip("Starte Pipeline: Linkliste → DataGrabber → Datenschreiber → Log")
|
||||||
main_layout.addWidget(self.btn_load)
|
self.btn_pipeline.clicked.connect(self._on_run_pipeline)
|
||||||
|
main_layout.addWidget(self.btn_pipeline)
|
||||||
|
|
||||||
|
|
||||||
main_layout.addStretch(1)
|
main_layout.addStretch(1)
|
||||||
self.setLayout(main_layout)
|
self.setLayout(main_layout)
|
||||||
|
|
||||||
# ---------------------------------------------------------
|
|
||||||
# State Restore (UI-Wiederherstellung ohne Prüfungen)
|
|
||||||
# ---------------------------------------------------------
|
|
||||||
def _restore_state(self) -> None:
|
def _restore_state(self) -> None:
|
||||||
db = self.logic.load_verfahrens_db()
|
"""Stellt UI-State aus Projektvariablen/Persistenz wieder her."""
|
||||||
if db:
|
# Verfahrens-DB
|
||||||
self.verfahrens_db = db
|
|
||||||
try:
|
try:
|
||||||
|
db = get_variable("tab_a_verfahrens_db", scope="project")
|
||||||
|
if db and self.file_widget:
|
||||||
self.file_widget.setFilePath(db)
|
self.file_widget.setFilePath(db)
|
||||||
except Exception:
|
self.verfahrens_db = db
|
||||||
pass
|
|
||||||
self._update_group_color()
|
self._update_group_color()
|
||||||
|
|
||||||
link = self.logic.load_linkliste()
|
|
||||||
if link:
|
|
||||||
self.lokale_linkliste = link
|
|
||||||
try:
|
|
||||||
self.linkliste_widget.setFilePath(link)
|
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
layer_id = self.logic.load_verfahrensgebiet_layer_id()
|
# Linkliste
|
||||||
|
try:
|
||||||
|
link = get_variable("tab_a_linkliste", scope="project")
|
||||||
|
if link and self.linkliste_widget:
|
||||||
|
self.linkliste_widget.setFilePath(link)
|
||||||
|
self.lokale_linkliste = link
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Layer
|
||||||
|
try:
|
||||||
|
layer_id = get_variable("tab_a_layer_id", scope="project")
|
||||||
if layer_id:
|
if layer_id:
|
||||||
layer = QgsProject.instance().mapLayer(layer_id)
|
layer = QgsProject.instance().mapLayer(layer_id)
|
||||||
if layer:
|
if layer and self.layer_combo:
|
||||||
self.layer_combo.setLayer(layer)
|
self.layer_combo.setLayer(layer)
|
||||||
|
self._pufferlayer = layer
|
||||||
# Raumfilter aus Variable wiederherstellen (falls Combo existiert)
|
|
||||||
try:
|
|
||||||
stored = get_variable(RAUMFILTER_VAR, scope="project")
|
|
||||||
if stored and self._raumfilter_combo is not None:
|
|
||||||
try:
|
|
||||||
self._raumfilter_combo.setCurrentText(stored)
|
|
||||||
except Exception:
|
|
||||||
idx = self._raumfilter_combo.findText(stored)
|
|
||||||
if idx is not None and idx >= 0:
|
|
||||||
self._raumfilter_combo.setCurrentIndex(idx)
|
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# ---------------------------------------------------------
|
# Raumfilter (schon im _build_ui behandelt)
|
||||||
# UI-Callbacks (ohne Prüfungen / Exceptions)
|
|
||||||
# ---------------------------------------------------------
|
# === UI CALLBACKS ===
|
||||||
def _toggle_group(self, checked: bool) -> None:
|
def _toggle_group(self, checked: bool) -> None:
|
||||||
|
"""Zeigt/verbirgt Verfahrens-DB-Gruppe."""
|
||||||
self.group_button.setArrowType(ArrowDown if checked else ArrowRight)
|
self.group_button.setArrowType(ArrowDown if checked else ArrowRight)
|
||||||
self.group_content.setVisible(checked)
|
self.group_content.setVisible(checked)
|
||||||
|
|
||||||
def _toggle_optional(self, checked: bool) -> None:
|
def _toggle_optional(self, checked: bool) -> None:
|
||||||
|
"""Zeigt/verbirgt optionale Linkliste."""
|
||||||
self.optional_button.setArrowType(ArrowDown if checked else ArrowRight)
|
self.optional_button.setArrowType(ArrowDown if checked else ArrowRight)
|
||||||
self.optional_content.setVisible(checked)
|
self.optional_content.setVisible(checked)
|
||||||
|
|
||||||
def _on_verfahrens_db_changed(self, path: str) -> None:
|
def _on_verfahrens_db_changed(self, path: str) -> None:
|
||||||
|
"""Persistieret Verfahrens-DB-Pfad."""
|
||||||
self.verfahrens_db = path
|
self.verfahrens_db = path
|
||||||
self.logic.set_verfahrens_db(path)
|
set_variable("tab_a_verfahrens_db", path, scope="project")
|
||||||
self._update_group_color()
|
self._update_group_color()
|
||||||
|
|
||||||
def _on_linkliste_changed(self, path: str) -> None:
|
def _on_linkliste_changed(self, path: str) -> None:
|
||||||
|
"""Persistieret lokale Linkliste."""
|
||||||
self.lokale_linkliste = path
|
self.lokale_linkliste = path
|
||||||
self.logic.set_linkliste(path)
|
set_variable("tab_a_linkliste", path, scope="project")
|
||||||
|
|
||||||
def _on_layer_changed(self, layer) -> None:
|
def _on_layer_changed(self, layer) -> None:
|
||||||
self.logic.save_verfahrensgebiet_layer(layer)
|
"""Persistieret Layer-Auswahl."""
|
||||||
self._pufferlayer = layer
|
self._pufferlayer = layer
|
||||||
|
if layer:
|
||||||
|
set_variable("tab_a_layer_id", layer.id(), scope="project")
|
||||||
|
|
||||||
def _create_new_gpkg(self) -> None:
|
|
||||||
file_path, _ = QFileDialog.getSaveFileName(
|
|
||||||
self,
|
|
||||||
"Neue Verfahrens-Datenbank anlegen",
|
|
||||||
"",
|
|
||||||
"Geopackage (*.gpkg)",
|
|
||||||
)
|
|
||||||
if not file_path:
|
|
||||||
return
|
|
||||||
|
|
||||||
if not file_path.lower().endswith(".gpkg"):
|
|
||||||
file_path += ".gpkg"
|
|
||||||
|
|
||||||
# Delegation an TabALogic; TabALogic / Pruefmanager übernehmen Prüfungen
|
|
||||||
self.logic.create_new_verfahrens_db(file_path)
|
|
||||||
self.verfahrens_db = file_path
|
|
||||||
try:
|
|
||||||
self.file_widget.setFilePath(file_path)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
self._update_group_color()
|
|
||||||
|
|
||||||
def _on_load_fachdaten(self) -> None:
|
|
||||||
"""
|
|
||||||
Platzhalter-Handler für 'Fachdaten laden'.
|
|
||||||
|
|
||||||
Keine Prüfungen oder Exception-Handling hier. Die fachliche Prüfung
|
|
||||||
und Fehlerbehandlung erfolgen zur Laufzeit durch den Pruefmanager und
|
|
||||||
die Prüfer, die vom DataGrabber verwendet werden.
|
|
||||||
"""
|
|
||||||
pfad = self.file_widget.filePath()
|
|
||||||
|
|
||||||
# Dateipruefer wird zur Laufzeit verwendet; hier nur der Aufruf
|
|
||||||
pruefer = Dateipruefer(pfad=pfad, temporaer_erlaubt=True)
|
|
||||||
ergebnis = pruefer.pruefe()
|
|
||||||
ergebnis = self.pruefmanager.verarbeite(ergebnis)
|
|
||||||
|
|
||||||
zielpfad = None
|
|
||||||
if ergebnis.kontext is not None:
|
|
||||||
try:
|
|
||||||
zielpfad = str(ergebnis.kontext)
|
|
||||||
except Exception:
|
|
||||||
zielpfad = ergebnis.kontext
|
|
||||||
|
|
||||||
self.data_grabber.run(
|
|
||||||
attributes_list=self._attributes_list,
|
|
||||||
pufferlayer=self._pufferlayer,
|
|
||||||
zielpfad=zielpfad,
|
|
||||||
temporaer=(ergebnis.aktion == "temporaer_erzeugen"),
|
|
||||||
temporaer_erlaubt=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
# ---------------------------------------------------------
|
|
||||||
# Raumfilter Callback
|
|
||||||
# ---------------------------------------------------------
|
|
||||||
def _on_raumfilter_changed(self, value: str) -> None:
|
def _on_raumfilter_changed(self, value: str) -> None:
|
||||||
# Persistiere Auswahl in Projekt-Variable; Prüfungen übernimmt die Laufzeitlogik
|
"""Persistieret Raumfilter-Auswahl."""
|
||||||
set_variable(RAUMFILTER_VAR, value, scope="project")
|
set_variable(RAUMFILTER_VAR, value, scope="project")
|
||||||
|
|
||||||
# ---------------------------------------------------------
|
def _create_new_gpkg(self) -> None:
|
||||||
# UI-Helfer
|
"""Delegiert GPKG-Erstellung (Prüfungen über Services)."""
|
||||||
# ---------------------------------------------------------
|
file_path, _ = QFileDialog.getSaveFileName(
|
||||||
def _prompt_user_to_select_file(self) -> None:
|
self, "Neue Verfahrens-Datenbank", "", "Geopackage (*.gpkg)"
|
||||||
fname, _ = QFileDialog.getOpenFileName(
|
|
||||||
self,
|
|
||||||
"Verfahrens-DB auswählen",
|
|
||||||
"",
|
|
||||||
"Geopackage (*.gpkg)",
|
|
||||||
)
|
)
|
||||||
if fname:
|
if file_path:
|
||||||
try:
|
if not file_path.lower().endswith(".gpkg"):
|
||||||
self.file_widget.setFilePath(fname)
|
file_path += ".gpkg"
|
||||||
except Exception:
|
self.verfahrens_db = file_path
|
||||||
try:
|
self.file_widget.setFilePath(file_path)
|
||||||
self.file_widget.setFileName(fname)
|
set_variable("tab_a_verfahrens_db", file_path, scope="project")
|
||||||
except Exception:
|
|
||||||
self.file_widget.setProperty("filePath", fname)
|
|
||||||
self.verfahrens_db = fname
|
|
||||||
self.logic.set_verfahrens_db(fname)
|
|
||||||
self._update_group_color()
|
self._update_group_color()
|
||||||
|
|
||||||
def _update_group_color(self) -> None:
|
def _update_group_color(self) -> None:
|
||||||
|
"""Visuelles Feedback für Verfahrens-DB-Status."""
|
||||||
if self.verfahrens_db:
|
if self.verfahrens_db:
|
||||||
self.group_button.setStyleSheet("font-weight: bold;")
|
self.group_button.setStyleSheet("font-weight: bold; background-color: #e0f7e0;")
|
||||||
else:
|
else:
|
||||||
self.group_button.setStyleSheet("")
|
self.group_button.setStyleSheet("font-weight: bold;")
|
||||||
|
|
||||||
|
# === PIPELINE ===
|
||||||
|
def _on_run_pipeline(self) -> None:
|
||||||
|
"""DEBUG: Pipeline mit maximaler Ausgabe."""
|
||||||
|
print("\n" + "="*60)
|
||||||
|
print("🚀 _on_run_pipeline GESTARTET")
|
||||||
|
print("="*60)
|
||||||
|
# 🔥 DEBUG QT STATUS
|
||||||
|
from sn_basis.functions import qt_wrapper
|
||||||
|
qt_wrapper.debug_qt_status() # ← Zeigt EXAKT was läuft!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 1. Services prüfen
|
||||||
|
print(f"pruefmanager: {self.pruefmanager is not None}")
|
||||||
|
print(f"data_grabber: {self.data_grabber is not None}")
|
||||||
|
print(f"logic: {hasattr(self, 'logic')}")
|
||||||
|
|
||||||
|
if not self.pruefmanager:
|
||||||
|
print("❌ FEHLER: self.pruefmanager fehlt!")
|
||||||
|
return
|
||||||
|
if not self.data_grabber:
|
||||||
|
print("❌ FEHLER: self.data_grabber fehlt!")
|
||||||
|
return
|
||||||
|
|
||||||
|
print("✅ Services OK")
|
||||||
|
|
||||||
|
# 2. FileWidget
|
||||||
|
source = self.file_widget.filePath()
|
||||||
|
print(f"📁 Eingabe: '{source}' (len={len(source or '')})")
|
||||||
|
|
||||||
|
# 3. Dateipruefer
|
||||||
|
print("🔍 Dateipruefer starte...")
|
||||||
|
try:
|
||||||
|
pruefer = Dateipruefer(
|
||||||
|
source,
|
||||||
|
basis_pfad="",
|
||||||
|
leereingabe_erlaubt=False,
|
||||||
|
standarddatei=None,
|
||||||
|
temporaer_erlaubt=True, # ✅ Explizit True
|
||||||
|
verfahrens_db_modus=True # ✅ Keyword-only
|
||||||
|
)
|
||||||
|
ergebnis1 = pruefer.pruefe()
|
||||||
|
print(f" → ok={ergebnis1.ok}, aktion='{ergebnis1.aktion}', kontext={ergebnis1.kontext}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"💥 Dateipruefer FEHLER: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
return
|
||||||
|
|
||||||
|
# 4. Pruefmanager
|
||||||
|
print("🤖 Pruefmanager starte...")
|
||||||
|
try:
|
||||||
|
ergebnis2 = self.pruefmanager.verarbeite(ergebnis1)
|
||||||
|
|
||||||
|
print(f" → ok={ergebnis2.ok}, aktion='{ergebnis2.aktion}', kontext={ergebnis2.kontext}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"💥 Pruefmanager FEHLER: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
return
|
||||||
|
|
||||||
|
# 5. Entscheidung
|
||||||
|
weiter = ergebnis2.ok
|
||||||
|
print(f"➡️ Weiter? {weiter} (aktion='{ergebnis2.aktion}')")
|
||||||
|
|
||||||
|
if weiter:
|
||||||
|
final_pfad = ergebnis2.kontext if ergebnis2.kontext else source
|
||||||
|
print(f"🚀 DataGrabber mit: '{final_pfad}'")
|
||||||
|
try:
|
||||||
|
self.data_grabber.run(final_pfad)
|
||||||
|
print("✅ DataGrabber aufgerufen!")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"💥 DataGrabber FEHLER: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
else:
|
||||||
|
print("⏹️ Pipeline gestoppt (erwartet bei leerem Pfad)")
|
||||||
|
|
||||||
|
print("="*60 + "\n")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _on_load_fachdaten(self) -> None:
|
||||||
|
"""Kompatibilitäts-Handler → neue Pipeline."""
|
||||||
|
self._on_run_pipeline()
|
||||||
|
|||||||
Reference in New Issue
Block a user