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-03-04 15:31:36 +01:00
|
|
|
|
from typing import Any, Dict, List, Optional
|
2026-02-13 21:38:25 +01:00
|
|
|
|
from collections.abc import Mapping as _Mapping
|
2026-03-04 15:31:36 +01:00
|
|
|
|
import os
|
2026-02-13 21:38:25 +01:00
|
|
|
|
|
2026-03-04 15:31:36 +01:00
|
|
|
|
from sn_basis.functions.qgiscore_wrapper import (
|
|
|
|
|
|
QgsVectorFileWriter,
|
|
|
|
|
|
QgsVectorLayer,
|
|
|
|
|
|
QgsProject,
|
|
|
|
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
from sn_basis.functions.variable_wrapper import (
|
2026-01-08 17:13:43 +01:00
|
|
|
|
get_variable,
|
|
|
|
|
|
set_variable,
|
|
|
|
|
|
)
|
2026-03-04 15:31:36 +01:00
|
|
|
|
from sn_basis.functions.sys_wrapper import file_exists
|
|
|
|
|
|
from sn_basis.functions.ly_existence_wrapper import layer_exists
|
|
|
|
|
|
from sn_basis.functions.ly_metadata_wrapper import get_layer_type
|
2026-03-11 13:27:16 +01:00
|
|
|
|
from sn_basis.functions.qgisui_wrapper import QgsFileWidget
|
2026-02-13 21:38:25 +01:00
|
|
|
|
|
2026-03-04 15:31:36 +01:00
|
|
|
|
# Prüfer-Typen
|
|
|
|
|
|
from sn_basis.modules.Pruefmanager import Pruefmanager
|
|
|
|
|
|
from sn_basis.modules.linkpruefer import Linkpruefer
|
|
|
|
|
|
from sn_basis.modules.stilpruefer import Stilpruefer
|
2026-03-11 13:27:16 +01:00
|
|
|
|
from sn_basis.modules.Dateipruefer import Dateipruefer
|
|
|
|
|
|
from sn_basis.modules.pruef_ergebnis import pruef_ergebnis
|
|
|
|
|
|
from sn_basis.modules.DataGrabber import DataGrabber, SourceType, SourceDict
|
2026-02-13 21:38:25 +01:00
|
|
|
|
|
|
|
|
|
|
Row = Dict[str, Any]
|
|
|
|
|
|
DataDict = Dict[str, List[Row]]
|
2026-01-08 17:13:43 +01:00
|
|
|
|
|
|
|
|
|
|
class TabALogic:
|
|
|
|
|
|
"""
|
2026-03-04 15:31:36 +01:00
|
|
|
|
Kapselt die Fachlogik von Tab A. Verfahrens-DB wird **nicht** bei Pfad-Auswahl,
|
2026-03-11 13:27:16 +01:00
|
|
|
|
sondern erst beim ersten Layer-Schreiben angelegt.
|
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:
|
|
|
|
|
|
self.pruefmanager = pruefmanager
|
|
|
|
|
|
self.link_pruefer = link_pruefer
|
|
|
|
|
|
self.stil_pruefer = stil_pruefer
|
2026-03-11 13:27:16 +01:00
|
|
|
|
self.data_grabber: Optional[DataGrabber] = None
|
2026-02-13 21:38:25 +01:00
|
|
|
|
|
2026-01-08 17:13:43 +01:00
|
|
|
|
# -------------------------------
|
2026-03-04 15:31:36 +01:00
|
|
|
|
# Verfahrens-Datenbank (Pfad-Management)
|
2026-01-08 17:13:43 +01:00
|
|
|
|
# -------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
def load_verfahrens_db(self) -> Optional[str]:
|
2026-03-04 15:31:36 +01:00
|
|
|
|
"""Lädt den gespeicherten Verfahrens-DB-Pfad (Datei muss nicht existieren)."""
|
2026-01-08 17:13:43 +01:00
|
|
|
|
path = get_variable("verfahrens_db", scope="project")
|
2026-03-04 15:31:36 +01:00
|
|
|
|
return path or None
|
2026-01-08 17:13:43 +01:00
|
|
|
|
|
|
|
|
|
|
def set_verfahrens_db(self, path: Optional[str]) -> None:
|
2026-03-04 15:31:36 +01:00
|
|
|
|
"""Speichert den Verfahrens-DB-Pfad (Datei wird später angelegt)."""
|
2026-01-08 17:13:43 +01:00
|
|
|
|
if path:
|
|
|
|
|
|
set_variable("verfahrens_db", path, scope="project")
|
|
|
|
|
|
else:
|
|
|
|
|
|
set_variable("verfahrens_db", "", scope="project")
|
|
|
|
|
|
|
2026-03-04 15:31:36 +01:00
|
|
|
|
# -------------------------------
|
|
|
|
|
|
# Layer → Verfahrens-DB schreiben (alte Logik!)
|
|
|
|
|
|
# -------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
def write_layer_to_verfahrens_db(
|
|
|
|
|
|
self,
|
|
|
|
|
|
source_layer: QgsVectorLayer,
|
|
|
|
|
|
zielpfad: str,
|
|
|
|
|
|
layer_name: str,
|
|
|
|
|
|
) -> bool:
|
|
|
|
|
|
"""
|
|
|
|
|
|
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 zielpfad or not source_layer or not source_layer.isValid():
|
2026-01-08 17:13:43 +01:00
|
|
|
|
return False
|
|
|
|
|
|
|
2026-03-04 15:31:36 +01:00
|
|
|
|
# Optionen wie im alten puffer_setzen
|
|
|
|
|
|
opts = QgsVectorFileWriter.SaveVectorOptions()
|
|
|
|
|
|
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}")
|
2026-01-08 17:13:43 +01:00
|
|
|
|
return False
|
|
|
|
|
|
|
2026-03-04 15:31:36 +01:00
|
|
|
|
# Pfad jetzt auch als "Verfahrens-DB" merken
|
|
|
|
|
|
self.set_verfahrens_db(zielpfad)
|
2026-01-08 17:13:43 +01:00
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
# -------------------------------
|
|
|
|
|
|
# Lokale Linkliste
|
|
|
|
|
|
# -------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
def load_linkliste(self) -> Optional[str]:
|
|
|
|
|
|
path = get_variable("linkliste", scope="project")
|
|
|
|
|
|
if path and file_exists(path):
|
|
|
|
|
|
return path
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
def set_linkliste(self, path: Optional[str]) -> None:
|
|
|
|
|
|
if path:
|
|
|
|
|
|
set_variable("linkliste", path, scope="project")
|
|
|
|
|
|
else:
|
|
|
|
|
|
set_variable("linkliste", "", scope="project")
|
|
|
|
|
|
|
|
|
|
|
|
# -------------------------------
|
|
|
|
|
|
# Verfahrensgebiet-Layer
|
|
|
|
|
|
# -------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
def save_verfahrensgebiet_layer(self, layer) -> None:
|
|
|
|
|
|
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]:
|
|
|
|
|
|
value = get_variable("verfahrensgebiet_layer", scope="project")
|
|
|
|
|
|
return value or None
|
|
|
|
|
|
|
|
|
|
|
|
def is_valid_verfahrensgebiet_layer(self, layer) -> bool:
|
|
|
|
|
|
if not layer_exists(layer):
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
layer_type = get_layer_type(layer)
|
|
|
|
|
|
return layer_type == "vector"
|
2026-03-11 13:27:16 +01:00
|
|
|
|
|
|
|
|
|
|
# === PIPELINE ===
|
|
|
|
|
|
def _on_run_pipeline(self,source: str) -> 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 Version an
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 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")
|