Files
Plugin_SN_Verfahrensgebiet/tests/test_eigentuemer_suche.py
T
DanielandClaude Opus 5.5 b105894241 PER_RECHTSPERSON als Rohwert in der Trefferliste zeigen (#56)
PER_RECHTSPERSON kennt die Werte 0, 1, 5 und 9 und ist kein Ja/Nein-Feld.
Die Trefferliste bekommt dafür eine eigene Spalte mit dem unveränderten
Wert; abweichende Werte einer Person werden gesammelt. Die Entschlüsselung
folgt später. „verstorben“ (PER_VERSTORBEN 0/1) bleibt als Hinweis.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 13:13:50 +02:00

198 lines
9.2 KiB
Python
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Tests für die Eigentümersuche im Serienbriefexport (#56).
Alle Namen und Anschriften sind erfunden.
"""
import contextlib
import io
import types
import unittest
from unittest.mock import patch
from sn_verfahrensgebiet.functions import eigentuemer_suche as es
from sn_verfahrensgebiet.functions.eigentuemer_suche import Eigentuemer
KOPF = [
"EIG_KONTO", "PER_ID", "PER_VERSTORBEN", "PER_NR", "Anrede", "Titel", "Vorname", "Nachname",
"OT", "STRASSE", "PLZ", "WOHNORT", "Email", "PER_KONTOINHABER", "PER_RECHTSPERSON",
"EIG_GEMARKUNG", "EIG_BLATT", "LISTE_ZGBBL", "ANZAHL_PERSONEN", "EIG_ID",
]
class _Null:
"""Nachbildung von QGIS-NULL."""
def isNull(self):
return True
class _Feature(dict):
def __init__(self, fid, werte):
super().__init__(werte)
self._fid = fid
def id(self):
return self._fid
class _Layer:
def __init__(self, zeilen, kopf=KOPF, name="Serienbrief", layer_id="csv1"):
self._kopf = list(kopf)
self._name = name
self._id = layer_id
self._features = [
_Feature(i + 1, {spalte: zeile.get(spalte.lstrip(""), "") for spalte in self._kopf})
for i, zeile in enumerate(zeilen)
]
def name(self):
return self._name
def id(self):
return self._id
def fields(self):
return types.SimpleNamespace(names=lambda: list(self._kopf))
def getFeatures(self):
return iter(self._features)
def _zeile(nachname, vorname, strasse="Hauptstraße 1", plz="01234", ort="Musterstadt", **weitere):
zeile = {"Nachname": nachname, "Vorname": vorname, "STRASSE": strasse, "PLZ": plz, "WOHNORT": ort}
zeile.update(weitere)
return zeile
class TestLayererkennung(unittest.TestCase):
def test_layer_mit_name_und_vorname_ist_eigentuemerlayer(self):
"""Prüft, dass ein Layer mit den Spalten Nachname und Vorname als Eigentümerdatei erkannt wird, auch mit BOM und anderer Schreibweise."""
self.assertTrue(es.ist_eigentuemerlayer(_Layer([])))
self.assertTrue(es.ist_eigentuemerlayer(_Layer([], kopf=["NACHNAME", "vorname"])))
self.assertFalse(es.ist_eigentuemerlayer(_Layer([], kopf=["flurstueckskennzeichen"])))
self.assertFalse(es.ist_eigentuemerlayer(object()))
def test_eigentuemerlayer_im_projekt(self):
"""Prüft, dass aus dem Projekt nur passende Layer kommen, nach Namen sortiert."""
b = _Layer([], name="b-Export", layer_id="b")
a = _Layer([], name="A-Export", layer_id="a")
flst = _Layer([], kopf=["flurstueckskennzeichen"], name="Flurstücke", layer_id="f")
projekt = types.SimpleNamespace(mapLayers=lambda: {"b": b, "f": flst, "a": a})
with patch.object(es, "QgsProject", types.SimpleNamespace(instance=lambda: projekt)):
self.assertEqual(es.eigentuemerlayer_im_projekt(), [a, b])
class TestSuche(unittest.TestCase):
def test_ohne_eingabe_fehler(self):
"""Prüft, dass ohne Name und Vorname nicht die ganze Datei gelistet, sondern um eine Eingabe gebeten wird."""
with self.assertRaisesRegex(es.EigentuemerSucheFehler, "Namen"):
es.suche_eigentuemer(_Layer([_zeile("Beispiel", "Anna")]), " ", "")
def test_layer_ohne_pflichtspalten(self):
"""Prüft, dass ein Layer ohne Nachname/Vorname mit Hinweis auf den Serienbriefexport abgelehnt wird."""
with self.assertRaisesRegex(es.EigentuemerSucheFehler, "Serienbriefexport"):
es.suche_eigentuemer(_Layer([], kopf=["flurstueckskennzeichen"]), "Beispiel", "")
def test_teilstring_ohne_gross_kleinschreibung(self):
"""Prüft, dass Name und Vorname als Teilstring ohne Rücksicht auf Groß-/Kleinschreibung gesucht werden."""
layer = _Layer([
_zeile("Beispiel", "Anna"),
_zeile("Beispiel-Muster", "Bernd"),
_zeile("Anders", "Anna"),
])
self.assertEqual([e.nachname for e in es.suche_eigentuemer(layer, "beispiel", "")],
["Beispiel", "Beispiel-Muster"])
self.assertEqual([e.nachname for e in es.suche_eigentuemer(layer, "", "ANNA")], ["Anders", "Beispiel"])
self.assertEqual([e.vorname for e in es.suche_eigentuemer(layer, "beisp", "an")], ["Anna"])
def test_umlaute_und_eszett(self):
"""Prüft, dass „Müller“ auch „Mueller“ findet und „Strauss“ auch „Strauß“."""
layer = _Layer([_zeile("Mueller", "Jörg"), _zeile("Strauß", "Erika", ort="Anderswo")])
self.assertEqual(len(es.suche_eigentuemer(layer, "Müller", "Joerg")), 1)
self.assertEqual(len(es.suche_eigentuemer(layer, "Strauss", "")), 1)
def test_gleiche_person_gleiche_anschrift_zusammengefasst(self):
"""Prüft, dass Zeilen mit gleichem Namen und gleicher Anschrift ein Treffer werden und Konten, Personen-IDs und E-Mails gesammelt werden."""
layer = _Layer([
_zeile("Beispiel", "Anna", EIG_KONTO="100", PER_ID="7", Email="anna@example.org"),
_zeile("beispiel ", " Anna", strasse="Hauptstraße 1", EIG_KONTO="200", PER_ID="7",
Titel="Dr.", Email="anna@example.org"),
_zeile("Beispiel", "Anna", EIG_KONTO="300", PER_ID="8", Email="a.b@example.org"),
])
treffer = es.suche_eigentuemer(layer, "Beispiel", "Anna")
self.assertEqual(len(treffer), 1)
e = treffer[0]
self.assertEqual(e.konten, ("100", "200", "300"))
self.assertEqual(e.per_ids, ("7", "8"))
self.assertEqual(e.emails, ("anna@example.org", "a.b@example.org"))
self.assertEqual(e.feature_ids, (1, 2, 3))
self.assertEqual(e.titel, "Dr.")
def test_andere_anschrift_eigener_treffer(self):
"""Prüft, dass dieselbe Person mit anderer Anschrift ein eigener Treffer bleibt, sortiert nach Wohnort."""
layer = _Layer([
_zeile("Beispiel", "Anna", ort="Zettlitz"),
_zeile("Beispiel", "Anna", ort="Altdorf", plz="09999"),
])
treffer = es.suche_eigentuemer(layer, "Beispiel", "Anna")
self.assertEqual([e.wohnort for e in treffer], ["Altdorf", "Zettlitz"])
def test_kennzeichen_verstorben(self):
"""Prüft, dass „verstorben“ beim Exportwert 1 (auch als Zahl gelesen) gesetzt ist und bei 0, Leerem und NULL nicht."""
ja = ["1", 1]
nein = ["0", 0, "", None, _Null()]
zeilen = [_zeile(f"Ja{i}", "Test", PER_VERSTORBEN=w) for i, w in enumerate(ja)]
zeilen += [_zeile(f"Nein{i}", "Test", PER_VERSTORBEN=w) for i, w in enumerate(nein)]
for e in es.suche_eigentuemer(_Layer(zeilen), "", "Test"):
erwartet = e.nachname.startswith("Ja")
with self.subTest(name=e.nachname):
self.assertEqual(e.verstorben, erwartet)
self.assertEqual(e.hinweise, "verstorben" if erwartet else "")
def test_rechtsperson_als_rohwert(self):
"""Prüft, dass PER_RECHTSPERSON unverändert angezeigt wird (0, 1, 5, 9; noch nicht entschlüsselt) und abweichende Werte einer Person gesammelt werden."""
layer = _Layer([
_zeile("Beispiel", "Anna", PER_RECHTSPERSON="0"),
_zeile("Gemeinde Musterstadt", "", strasse="Markt 1", PER_RECHTSPERSON=5),
_zeile("Muster", "Bernd", PER_RECHTSPERSON="1"),
_zeile("Muster", "Bernd", PER_RECHTSPERSON="9"),
_zeile("Ohnewert", "Clara", PER_RECHTSPERSON=_Null()),
])
werte = {e.nachname: e.rechtsperson_text for e in es.suche_eigentuemer(layer, "e", "")}
self.assertEqual(werte, {"Beispiel": "0", "Gemeinde Musterstadt": "5", "Muster": "1, 9", "Ohnewert": ""})
def test_plz_mit_fuehrender_null(self):
"""Prüft, dass eine von QGIS als Zahl gelesene PLZ wieder fünfstellig mit führender Null erscheint und Zeilen dadurch zusammenfinden."""
layer = _Layer([
_zeile("Beispiel", "Anna", plz=1067),
_zeile("Beispiel", "Anna", plz="01067"),
_zeile("Muster", "Bernd", plz="1067.0"),
])
treffer = es.suche_eigentuemer(layer, "e", "")
self.assertEqual([(e.nachname, e.plz) for e in treffer], [("Beispiel", "01067"), ("Muster", "01067")])
def test_suche_gibt_nichts_aus(self):
"""Prüft, dass die Suche keine Personendaten auf die Konsole schreibt (Datenschutz)."""
ausgabe = io.StringIO()
with contextlib.redirect_stdout(ausgabe), contextlib.redirect_stderr(ausgabe):
es.suche_eigentuemer(_Layer([_zeile("Beispiel", "Anna")]), "Beispiel", "")
self.assertEqual(ausgabe.getvalue(), "")
class TestAnzeigeformen(unittest.TestCase):
def test_name_und_anschrift(self):
"""Prüft, dass Name mit Titel und einzeilige Anschrift mit Ortsteil lesbar zusammengesetzt werden."""
e = Eigentuemer(titel="Dr.", vorname="Anna", nachname="Beispiel", strasse="Hauptstraße 1",
plz="01234", wohnort="Musterstadt", ortsteil="Unterdorf")
self.assertEqual(e.name, "Beispiel, Dr. Anna")
self.assertEqual(e.anschrift, "Hauptstraße 1, 01234 Musterstadt, OT Unterdorf")
self.assertEqual(Eigentuemer(nachname="Beispiel", ortsteil="OT Unterdorf").anschrift, "OT Unterdorf")
self.assertEqual(Eigentuemer(nachname="Gemeinde Musterstadt").name, "Gemeinde Musterstadt")
if __name__ == "__main__":
unittest.main()