Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ecb1e49d8d | ||
|
|
ad6c7ea3e1 | ||
|
|
1bb425e419 | ||
|
|
b8f36bab7a | ||
|
|
aacba1e5eb | ||
|
|
9efeff5aff |
@@ -1,4 +1,8 @@
|
||||
---
|
||||
Version 26.3.3-unstable:
|
||||
---
|
||||
Version 26.3.2-unstable:
|
||||
---
|
||||
Version 26.3.1-testing:
|
||||
- unstable als testing veröffentlicht
|
||||
---
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
[general]
|
||||
version=26.3.1-testing
|
||||
version=26.3.3-unstable
|
||||
name=LNO Sachsen | Plan41
|
||||
description=Plugin zum Erzeugen der Pläne nach §38 und §41
|
||||
author=Daniel Helbig, Michael Otto
|
||||
homepage=https://entwicklung.flurneuordnung-sachsen.de/AG_QGIS/Plugin_SN_Plan41
|
||||
tracker=https://entwicklung.flurneuordnung-sachsen.de/AG_QGIS/Plugin_SN_Plan41/issues
|
||||
repository=https://entwicklung.flurneuordnung-sachsen.de/AG_QGIS/Plugin_Test_Action/src/branch/testing/
|
||||
repository=https://entwicklung.flurneuordnung-sachsen.de/AG_QGIS/Plugin_Test_Action/src/branch/unstable/
|
||||
|
||||
qgisMinimumVersion=3.40
|
||||
experimental=true
|
||||
|
||||
@@ -94,59 +94,6 @@ class TestTabALogic(unittest.TestCase):
|
||||
# -----------------------------------------------------
|
||||
# 6. Layer-ID speichern
|
||||
# -----------------------------------------------------
|
||||
def test_verfahrensgebiet_layer_id_storage(self):
|
||||
logic = TabALogic()
|
||||
|
||||
class MockLayer:
|
||||
def id(self):
|
||||
return "layer-123"
|
||||
|
||||
logic.save_verfahrensgebiet_layer(MockLayer())
|
||||
|
||||
stored = get_variable("verfahrensgebiet_layer", scope="project")
|
||||
self.assertEqual(stored, "layer-123")
|
||||
|
||||
# -----------------------------------------------------
|
||||
# 7. Ungültiger Layer wird ignoriert
|
||||
# -----------------------------------------------------
|
||||
def test_invalid_layer_is_rejected(self):
|
||||
logic = TabALogic()
|
||||
|
||||
class InvalidLayer:
|
||||
pass
|
||||
|
||||
logic.save_verfahrensgebiet_layer(InvalidLayer())
|
||||
|
||||
stored = get_variable("verfahrensgebiet_layer", scope="project")
|
||||
self.assertEqual(stored, "")
|
||||
#-----------------------------------------------------
|
||||
# 8. Layer-ID wirft Exception
|
||||
#----------------------------------------------------
|
||||
def test_layer_id_raises_exception(self):
|
||||
logic = TabALogic()
|
||||
|
||||
class BadLayer:
|
||||
def id(self):
|
||||
raise RuntimeError("boom")
|
||||
|
||||
logic.save_verfahrensgebiet_layer(BadLayer())
|
||||
|
||||
stored = get_variable("verfahrensgebiet_layer", scope="project")
|
||||
self.assertEqual(stored, "")
|
||||
# -----------------------------------------------------
|
||||
# 11. Layer ID wird leer zurückgegeben
|
||||
# -----------------------------------------------------
|
||||
def test_layer_id_returns_empty(self):
|
||||
logic = TabALogic()
|
||||
|
||||
class EmptyLayer:
|
||||
def id(self):
|
||||
return ""
|
||||
|
||||
logic.save_verfahrensgebiet_layer(EmptyLayer())
|
||||
|
||||
stored = get_variable("verfahrensgebiet_layer", scope="project")
|
||||
self.assertEqual(stored, "")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
from typing import Any, cast
|
||||
|
||||
from sn_plan41.ui.tab_a_logic import TabALogic
|
||||
|
||||
|
||||
class _DummyPruefmanager:
|
||||
def __init__(self) -> None:
|
||||
self.hinweise: list[tuple[str, str]] = []
|
||||
|
||||
def verarbeite(self, ergebnis):
|
||||
return ergebnis
|
||||
|
||||
def zeige_hinweis(self, titel: str, meldung: str) -> None:
|
||||
self.hinweise.append((titel, meldung))
|
||||
|
||||
|
||||
class _DummyDateiErgebnis:
|
||||
def __init__(self, ok: bool = True, kontext: str = "C:/tmp/v_db.gpkg") -> None:
|
||||
self.ok = ok
|
||||
self.kontext = kontext
|
||||
|
||||
|
||||
class _DummyProgress:
|
||||
def __init__(self) -> None:
|
||||
self.closed = False
|
||||
|
||||
def set_total(self, _value: int) -> None:
|
||||
pass
|
||||
|
||||
def set_value(self, _value: int) -> None:
|
||||
pass
|
||||
|
||||
def set_label(self, _value: str) -> None:
|
||||
pass
|
||||
|
||||
def close(self) -> None:
|
||||
self.closed = True
|
||||
|
||||
|
||||
class _DummyProject:
|
||||
def mapLayer(self, _layer_id: str):
|
||||
return None
|
||||
|
||||
|
||||
class _DummyGrabber:
|
||||
pass
|
||||
|
||||
|
||||
class TestTabAPreconditions(unittest.TestCase):
|
||||
|
||||
def _make_logic(self) -> tuple[TabALogic, _DummyPruefmanager]:
|
||||
pruefmanager = _DummyPruefmanager()
|
||||
logic = TabALogic(
|
||||
pruefmanager=cast(Any, pruefmanager),
|
||||
link_pruefer=cast(Any, object()),
|
||||
stil_pruefer=cast(Any, object()),
|
||||
)
|
||||
return logic, pruefmanager
|
||||
|
||||
def test_get_verfahrensgebiet_layer_shows_hint_when_layer_id_missing(self):
|
||||
logic, pruefmanager = self._make_logic()
|
||||
|
||||
with patch.object(logic, "load_verfahrensgebiet_layer_id", return_value=None):
|
||||
layer = logic._get_verfahrensgebiet_layer()
|
||||
|
||||
self.assertIsNone(layer)
|
||||
self.assertEqual(len(pruefmanager.hinweise), 1)
|
||||
self.assertIn("Verfahrensgebiet", pruefmanager.hinweise[0][0])
|
||||
|
||||
@patch("sn_plan41.ui.tab_a_logic.QgsProject.instance", return_value=_DummyProject())
|
||||
def test_get_verfahrensgebiet_layer_shows_hint_when_layer_not_in_project(self, _mock_project_instance):
|
||||
logic, pruefmanager = self._make_logic()
|
||||
|
||||
with patch.object(logic, "load_verfahrensgebiet_layer_id", return_value="layer_404"):
|
||||
layer = logic._get_verfahrensgebiet_layer()
|
||||
|
||||
self.assertIsNone(layer)
|
||||
self.assertEqual(len(pruefmanager.hinweise), 1)
|
||||
self.assertIn("Verfahrensgebiet", pruefmanager.hinweise[0][0])
|
||||
|
||||
@patch("sn_plan41.ui.tab_a_logic.Dateipruefer")
|
||||
def test_pipeline_aborts_and_shows_hint_when_verfahrensgebiet_missing(
|
||||
self,
|
||||
mock_dateipruefer,
|
||||
):
|
||||
logic, pruefmanager = self._make_logic()
|
||||
logic.data_grabber = cast(Any, _DummyGrabber())
|
||||
|
||||
mock_dateipruefer.return_value.pruefe.return_value = _DummyDateiErgebnis(ok=True, kontext="C:/tmp/v_db.gpkg")
|
||||
|
||||
progress = _DummyProgress()
|
||||
|
||||
with patch("sn_plan41.ui.tab_a_logic.create_progress_dialog", return_value=progress):
|
||||
with patch.object(logic, "_resolve_linkliste", return_value="C:/tmp/linkliste.xlsx"):
|
||||
with patch.object(logic, "load_verfahrensgebiet_layer_id", return_value=None):
|
||||
result = logic._on_run_pipeline(
|
||||
source="C:/tmp/v_db.gpkg",
|
||||
linkliste=None,
|
||||
raumfilter="Verfahrensgebiet",
|
||||
)
|
||||
|
||||
self.assertIsNone(result)
|
||||
self.assertEqual(len(pruefmanager.hinweise), 1)
|
||||
self.assertIn("Verfahrensgebiet", pruefmanager.hinweise[0][0])
|
||||
self.assertTrue(progress.closed)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
+28
-6
@@ -66,6 +66,28 @@ class TabALogic:
|
||||
def _log(self, msg: str) -> None:
|
||||
print(f"[TabALogic] {msg}")
|
||||
|
||||
def _zeige_verfahrensgebiet_hinweis(self) -> None:
|
||||
if not self.pruefmanager:
|
||||
return
|
||||
show_hint = getattr(self.pruefmanager, "zeige_hinweis", None)
|
||||
if callable(show_hint):
|
||||
show_hint(
|
||||
"Verfahrensgebiet fehlt",
|
||||
"Kein gueltiger Verfahrensgebiet-Layer vorhanden. "
|
||||
"Bitte zuerst in sn_verfahrensgebiet den Layer ueber 'Aus ALKIS laden' laden "
|
||||
"oder im Dropdown einen gueltigen Layer auswaehlen.",
|
||||
)
|
||||
|
||||
def _close_progress_dialog(self, progress: Optional[Any]) -> None:
|
||||
if progress is None:
|
||||
return
|
||||
close = getattr(progress, "close", None)
|
||||
if callable(close):
|
||||
try:
|
||||
close()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# -------------------------------
|
||||
# Verfahrens-Datenbank (Pfad-Management)
|
||||
# -------------------------------
|
||||
@@ -155,12 +177,6 @@ class TabALogic:
|
||||
# -------------------------------
|
||||
# Verfahrensgebiet-Layer
|
||||
# -------------------------------
|
||||
|
||||
def save_verfahrensgebiet_layer(self, layer: QgsVectorLayer) -> None:
|
||||
"""Speichert die Verfahrensgebiet-Layer-ID, unter Annahme, dass der Layer prevalidiert ist."""
|
||||
layer_id = layer.id() if layer is not None else ""
|
||||
set_variable("verfahrensgebiet_layer", layer_id or "", scope="project")
|
||||
|
||||
def load_verfahrensgebiet_layer_id(self) -> Optional[str]:
|
||||
value = get_variable("verfahrensgebiet_layer", scope="project")
|
||||
return value or None
|
||||
@@ -217,6 +233,7 @@ class TabALogic:
|
||||
linkliste_final = self._resolve_linkliste(linkliste)
|
||||
if linkliste_final is None:
|
||||
self._log("Linkliste kann nicht aufgelöst werden")
|
||||
self._close_progress_dialog(progress)
|
||||
return None
|
||||
else:
|
||||
self._log(f"Linkliste final: '{linkliste_final}'")
|
||||
@@ -224,6 +241,7 @@ class TabALogic:
|
||||
raumfilter_layer = self._resolve_raumfilter(raumfilter, final_pfad)
|
||||
if raumfilter in ("Verfahrensgebiet", "Pufferlayer") and raumfilter_layer is None:
|
||||
self._log(f"Raumfilter '{raumfilter}' nicht verfügbar")
|
||||
self._close_progress_dialog(progress)
|
||||
return None
|
||||
|
||||
# 4) Lade-Status initialisieren (funktioniert ab Bestätigung überschreiben/anhängen)
|
||||
@@ -249,6 +267,7 @@ class TabALogic:
|
||||
if not source_dict.get("rows"):
|
||||
self._log("Keine validen Linkliste-Einträge für Datenabruf")
|
||||
print("[TabALogic] ... STOP: rows:", len(source_dict.get("rows", [])))
|
||||
self._close_progress_dialog(progress)
|
||||
return None
|
||||
|
||||
total_rows = len(source_dict.get("rows", []))
|
||||
@@ -875,15 +894,18 @@ class TabALogic:
|
||||
|
||||
if not layer_id:
|
||||
self._log("❌ Keine Layer-ID gespeichert")
|
||||
self._zeige_verfahrensgebiet_hinweis()
|
||||
return None
|
||||
|
||||
layer = QgsProject.instance().mapLayer(layer_id)
|
||||
if not layer:
|
||||
self._log("❌ Layer-ID existiert nicht im Projekt")
|
||||
self._zeige_verfahrensgebiet_hinweis()
|
||||
return None
|
||||
|
||||
if not self.is_valid_verfahrensgebiet_layer(layer):
|
||||
self._log("❌ Layer ist kein gültiger Vektorlayer")
|
||||
self._zeige_verfahrensgebiet_hinweis()
|
||||
return None
|
||||
|
||||
self._log(f"Verfahrensgebiet-Layer OK: '{layer.name()}'")
|
||||
|
||||
+1
-45
@@ -166,16 +166,7 @@ class TabA(QWidget):
|
||||
self.optional_content.setVisible(False)
|
||||
main_layout.addWidget(self.optional_content)
|
||||
|
||||
# === LAYER-AUSWAHL + RAUMFILTER ===
|
||||
layer_label = QLabel("Verfahrensgebiet-Layer auswählen")
|
||||
layer_label.setStyleSheet("font-weight: bold; margin-top: 6px;")
|
||||
main_layout.addWidget(layer_label)
|
||||
|
||||
self.layer_combo = QgsMapLayerComboBox()
|
||||
self.layer_combo.setFilters(QgsMapLayerProxyModel.VectorLayer)
|
||||
self.layer_combo.layerChanged.connect(self._on_layer_changed)
|
||||
main_layout.addWidget(self.layer_combo)
|
||||
|
||||
# === RAUMFILTER ===
|
||||
main_layout.addWidget(QLabel("Raumfilter"))
|
||||
self._raumfilter_combo = QComboBox(self)
|
||||
self._raumfilter_combo.setToolTip("Wählt die räumliche Bezugsfläche für die Datenextraktion.")
|
||||
@@ -213,18 +204,6 @@ class TabA(QWidget):
|
||||
self.lokale_linkliste = link
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Layer
|
||||
try:
|
||||
layer_id = get_variable("tab_a_layer_id", scope="project")
|
||||
if layer_id:
|
||||
layer = QgsProject.instance().mapLayer(layer_id)
|
||||
if layer and self.layer_combo:
|
||||
self.layer_combo.setLayer(layer)
|
||||
self._pufferlayer = layer
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Raumfilter (schon im _build_ui behandelt)
|
||||
|
||||
# === UI CALLBACKS ===
|
||||
@@ -249,20 +228,6 @@ class TabA(QWidget):
|
||||
self.lokale_linkliste = path
|
||||
set_variable("tab_a_linkliste", path, scope="project")
|
||||
|
||||
def _on_layer_changed(self, layer) -> None:
|
||||
"""Persistiert Layer-Auswahl und registriert Verfahrensgebiet."""
|
||||
self._pufferlayer = layer
|
||||
|
||||
if not layer:
|
||||
return
|
||||
|
||||
# UI-State speichern
|
||||
set_variable("tab_a_layer_id", layer.id(), scope="project")
|
||||
|
||||
# 🔹 NEU: Verfahrensgebiet explizit registrieren
|
||||
if self.logic:
|
||||
self.logic.save_verfahrensgebiet_layer(layer)
|
||||
|
||||
|
||||
def _on_raumfilter_changed(self, value: str) -> None:
|
||||
"""Persistieret Raumfilter-Auswahl."""
|
||||
@@ -296,15 +261,6 @@ class TabA(QWidget):
|
||||
source = self.file_widget.filePath()
|
||||
raumfilter = self._raumfilter_combo.currentText()
|
||||
linkliste = self.linkliste_widget.filePath()
|
||||
|
||||
if self.logic and self.layer_combo:
|
||||
layer = self.layer_combo.currentLayer()
|
||||
else:
|
||||
layer = None
|
||||
|
||||
if layer and layer.name() == "Verfahrensgebiet":
|
||||
self.logic.save_verfahrensgebiet_layer(layer)
|
||||
|
||||
if self.logic:
|
||||
try:
|
||||
self.logic._on_run_pipeline(source, linkliste, raumfilter)
|
||||
|
||||
Reference in New Issue
Block a user