Files
AndreasTharangandClaude Sonnet 5 ab416fb34c Projektwissen: Git-Vorgaben, Ticketzugriff, Tests und Einstieg
Erster Stand des Repositories AG_QGIS/Projektwissen (bisher Arbeitstitel
"Workspace", ADR 0006). Der lokale Ordner heisst weiter AG_QGIS, weil darin
auch die Plugin-Klone liegen.

Git verbindlich (ADR 0007), aus dem bisherigen Wiki-Konzept entwickelt:
- docs/leitfaden/05_git_arbeitsweise.md mit Kanal-Kette, Branch-Namen und
  Gitea-Branch-Schutz.
- Der Einstiegspunkt eines Fixes richtet sich nach dem Kanal, in dem der
  Fehler sitzt; ein Fix an testing oder stable ist erst mit der
  Rueckfuehrung nach unten abgeschlossen. Das fehlte bisher.
- Der stabile Kanal heisst stable, nicht main: der Release-Workflow in
  Repository:hidden/workflows fuehrt git checkout "$CHANNEL" aus, der
  Branchname muss also dem Kanalnamen entsprechen. Er existiert in keinem
  Plugin-Repo und ist vom Maintainer anzulegen.
- Der Mensch drueckt ab: .claude/settings.json sperrt push, merge, tag.
- scripts/hallo.py und /hallo holen den Stand aller Klone, per
  Fast-Forward wo gefahrlos; nie mergen, stashen oder verwerfen.

Ticketzugriff:
- scripts/gitea.py liest Issues, Kommentare und PRs ueber die oeffentliche
  API, ohne Token, nur GET. Schreibendes nur als Entwurf in entwuerfe/.
- Skill sn-ticket.

Tests (ADR 0009):
- docs/leitfaden/06_tests.md: Testebenen, Testpflicht, und wie eine
  Fachperson beurteilt, ob Tests fachlich sinnvoll sind.
- scripts/testkatalog.py erzeugt docs/tests/testkatalog.md per ast-Parsing,
  ohne Import und ohne QGIS. Befund: nur 100 von 332 Tests haben einen
  Docstring, sn_basis keinen einzigen von 87.
- Vorlage und erste Testkonzept-Seite mit Mermaid. Dabei gefunden und gegen
  origin geprueft: _ermittle_gebietstyp_aus_fbschl hat keinen Test, obwohl
  PR #44/#45 genau diese Funktion tolerant gemacht hat.
- docs/konzept/07_qgis_tests_plan.md: Stufenplan fuer Tests gegen echtes
  QGIS. Dabei aufgefallen: QGIS 3.40 ist seit Januar 2026 End of Life,
  qgis_min steht aber noch darauf.

Werkzeuge (ADR 0008):
- 04_werkzeuge_einrichten.md nur noch Claude Code als CLI und in VS Code.
- .vscode/ entfernt, brauchbare Einstellungen als persoenliche Vorlage.

Ausserdem: README als Einstieg neu aufgebaut, Mermaid-Diagramm in
schnittstellen.md repariert (unmaskierte Klammern, rendert seither nicht).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-04 11:57:26 +02:00

8.9 KiB
Raw Permalink Blame History

Schnittstellen und Verträge

Lebende Referenz der impliziten Verträge zwischen den Komponenten. Wer eine dieser Stellen ändert, ändert eine Schnittstelle und muss alle Nutzer mit anpassen. Stand 02.09.2026.

1. Plugin-Lebenszyklus: sn_basis ↔ Fachplugin

sequenceDiagram
    participant QGIS
    participant B as sn_basis.BasisPlugin
    participant F as Fachplugin (z. B. Verfahrensgebiet)
    QGIS->>B: classFactory(iface) / initGui()
    B->>B: Navigation().init_ui()  (Menü + Toolbar "LNO Sachsen")
    B->>B: setup_verfahrensgebiet_listener()
    loop für jedes Plugin in qgis.utils.plugins mit Namen sn_* (außer sn_basis)
        B->>F: initGui()
        F->>B: basis.ui.add_action(name, run, tooltip, priority)
    end
    B->>B: finalize_menu_and_toolbar()  (sortiert nach priority)
    QGIS->>F: Nutzer klickt Action → run()
    F->>F: DockWidget(...) mit objectName "sn_dock_<name>"
    F->>B: DockManager.show(dock)  (entfernt andere sn_dock_*)
    F->>B: basis.ui.set_active_plugin(action)
    QGIS->>F: unload() → removeDockWidget, basis.ui.remove_action(action)

Vertrag für ein Fachplugin (Muster: Plugin_SN_Widmung/main.py):

  • Paketname beginnt mit sn_ (nur dann ruft sn_basis das initGui() nach).
  • initGui() holt plugins.get("sn_basis"); ohne basis.ui nichts tun (QGIS lädt Plugins in nicht garantierter Reihenfolge; sn_basis ruft später nach).
  • add_action(text, callback, tooltip="", priority=100)QAction (checkable, in exklusiver Gruppe). Vergebene Prioritäten: Verfahrensgebiet 10, Plan41 20, Widmung 30.
  • Dock-Objektname sn_dock_<pluginname klein>; nur ein sn_dock_* ist gleichzeitig sichtbar.
  • unload() muss Dock entfernen und Action abmelden, sonst bleiben Reste in Menü/Toolbar.

2. DockWidget und Tabs

  • Fachplugin-Dock erbt von sn_basis.ui.base_dockwidget.BaseDockWidget; Klassenattribut tabs = [TabKlasse, ...].
  • Tab-Klasse: QWidget-Unterklasse mit Klassenattribut tab_title; Konstruktor Tab(parent). Optional set_services(pruefmanager=..., data_grabber=...) (wird von Fachplugin-Docks aufgerufen) und cleanup() (wird beim Schließen des Docks aufgerufen Signale trennen!).
  • Gemeinsame Tabs aus sn_basis: ui.tabs.print_tab.PrintTab (Druck), ui.tabs.settings_tab.SettingsTab (Projekteigenschaften).
  • dock.action = Referenz auf die Toolbar-Action; BaseDockWidget.closeEvent setzt sie zurück.

3. Projektvariablen (gemeinsamer Zustand im QGIS-Projekt)

Zugriff nur über sn_basis.functions.variable_wrapper.get_variable(key, scope="project") / set_variable(...). Der Wrapper setzt das Präfix sn_ selbst; Schlüssel ohne Präfix übergeben.

Schlüssel (ohne sn_) Gesetzt von Gelesen von Bedeutung
amt, behoerde, landkreis_user, sachgebiet SettingsTab Druck (Schriftfeld) benutzerspezifische Angaben
bezeichnung, name, verfahrensnummer, gemeinden, landkreise_proj, landkreis_kennzahl SettingsTab Verfahrensgebiet (ALKIS-Suche), Druck Verfahrensangaben; verfahrensnummer = VKZ
verfahrensgebiet_layer verfahrensgebiet_manager, WorkingTab alle Fachplugins, Druck Layer-ID des aktuellen Verfahrensgebiets
verfahrens_db, tab_a_verfahrens_db Plan41 Tab A Plan41 Pfad des Verfahrens-GeoPackages
linkliste, tab_a_linkliste Plan41 Tab A Plan41 Pfad der Linkliste (XLSX)
tab_a_layer_id, Raumfilter Plan41 Tab A Plan41 Raumfilter-Einstellung
druckrechteck_xmin/xmax/ymin/ymax PrintTab/PrintLogic Druck gespeicherter Druckbereich
sn_abdeckungslayer PrintTab/PrintLogic Druck (Atlas) Atlas-Abdeckungslayer. Schlüssel enthält bereits sn_ → tatsächliche Variable sn_sn_abdeckungslayer; funktioniert, ist aber inkonsistent
verfahrensgebietslayer print_logic.py print_logic.py Zweite Schreibweise neben verfahrensgebiet_layer vermutlich Altlast, beim Refactoring zusammenführen

Benutzer-/Maschinendaten außerhalb des Projekts: QSettings in sn_plan41 (mail, api_key der VLN-API).

4. Prüf- und Entscheidungsmodell

  • pruef_ergebnis(ok: bool, meldung, aktion: PruefAktion, kontext) ist das einheitliche Rückgabeobjekt aller Prüfer (Dateipruefer, Layerpruefer, Linkpruefer, Stilpruefer, Datenbankpruefer) und vieler Fachfunktionen.
  • PruefAktion ist ein Literal mit Codes wie datei_nicht_gefunden, layer_leer, datei_existiert_ueberschreiben, verfahrensgebiet_ergaenzen. Neue Codes dort ergänzen, nicht als freie Strings verwenden.
  • Pruefmanager(ui_modus="qgis"): verarbeite(ergebnis) entscheidet je Aktion (Dialog, Meldung, Standardwert). Mit ui_modus != "qgis" (Tests) werden Standardantworten ohne Dialog geliefert.
  • Fachplugins erhalten eine Pruefmanager- und eine DataGrabber-Instanz aus main.py (Konstruktor) und reichen sie an Tabs weiter.

5. Datenpipeline Plan41 (Fachdaten aus Linkliste)

flowchart LR
    XLSX[assets/Linkliste.xlsx] --> DG[DataGrabber.run]
    DG -->|Excel| LA[sn_plan41.Listenauswerter.validate_rows]
    LA --> DA[Datenabruf.datenabruf]
    VG[(Verfahrensgebiet / Puffer)] --> DA
    DA --> LL[LayerLoader: create_layer, filter_by_layer/extent, apply_style]
    LL --> DS[Datenschreiber.schreibe_Daten → GeoPackage]
    DS --> P[Datenschreiber.lade_Layer + schreibe_log HTML]
  • Erwartetes Spaltenschema der XLSX: ident, gruppe, kartenebene, inhalt, link, provider, stildatei. provider ∈ WFS, REST (ArcGIS), OGR.
  • Das zentrale Repository Linkliste (linkliste.csv: id;gruppe;ebene;inhalt;url;provider;stil) ist nicht angebunden → zwei Wahrheiten, siehe fallstricke.md.
  • Die einzige Rückwärtsabhängigkeit sn_basis → sn_plan41 ist der Lazy-Import von Listenauswerter in DataGrabber._process_excel_source (seit PR #57 abgesichert, damit sn_verfahrensgebiet ohne sn_plan41 lädt).

6. Verfahrensgebiet-Abläufe (Fachplugin)

  • Zentrale Kennung des Verfahrens: Projektvariable verfahrensnummer (VKZ) und/oder landkreis_kennzahl.
  • ALKIS-Quellen: vereinfachtes ALKIS (.../public_alkis/vereinf/wfs) für Gemarkungen/BROR, ALKIS-SF (.../public_alkis/sf/wfs) für Flurstücke und PunktortTA.
  • Layer-Namenskonventionen (String-Konstanten in den Modulen): BROR_LAYER_NAME, FLST_LAYER_NAME, JOINED_LAYER_NAME, „Verfahrensgebiet“, „Neuordnungsgebiet (NoBIS)“, „Neuvermessungsgebiet (NoBIS)“. Andere Module finden Layer über den Namen Umbenennungen sind Schnittstellenänderungen.
  • Ablaufdokumentation mit Diagrammen: Plugin_SN_Verfahrensgebiet/docs/.

7. VLN-Karten-API (Plan41)

  • Basis-URL https://api.flurneuordnung-sachsen.de/v2; Login per E-Mail/Passwort, danach Header X-API-Key.
  • Laden: Verfahren (VKZ) → Plan41-GeoJSON → Memory-Layer je Geometriefamilie (vln_layer_manager). Upload: alle Teillayer → eine FeatureCollection, PUT ersetzt den Serverbestand der VKZ vollständig.
  • Arbeits-CRS EPSG:25833.

8. Mock-Modus (Tests ohne QGIS)

  • Jeder Wrapper versucht den Import von qgis/PyQt6/PyQt5; schlägt das fehl, definiert er _Mock*-Klassen. Kennzeichen: qgiscore_wrapper.QGIS_AVAILABLE, qt_wrapper.QT_VERSION == 0.
  • Tests importieren Module über den Paketnamen (from sn_basis...), deshalb müssen die Repos unter ihrem Paketnamen erreichbar sein → scripts/run_tests.py erledigt das über ein temporäres Verzeichnis.
  • Tests, die echtes Layer- oder Netzwerkverhalten brauchen, laufen nur in QGIS (tests/test_qgis.bat, Windows).

9. Release-Vertrag

flowchart LR
    T["Tag v26.9.1-u auf Plugin-Repo"] --> W1["Plugin-Workflow release.yaml"]
    W1 -->|"workflow_dispatch + Payload"| W2["Repository, Branch hidden/workflows"]
    W2 --> C["checkout Branch = Kanal<br/>(unstable / testing / stable)"]
    C --> M["metadata.template → metadata.txt:<br/>version, experimental, repository-URL"]
    M --> Z["rsync ohne .git .gitea .gitignore __pycache__ → ZIP_FOLDER.zip"]
    Z --> R["Gitea-Release + Asset"]
    R --> X["plugins-&lt;kanal&gt;.xml aktualisieren"]
  • Tagformat v<JJ.M.N>-u (unstable), -t (testing), ohne Suffix (stable). Version = Jahr.Monat.Laufnummer.
  • Gebaut wird der Kopf des Kanal-Branches, nicht der getaggte Commit. Tag also nur auf dem aktuellen Kopf von unstable bzw. testing setzen.
  • Paketname im ZIP kommt aus der Gitea-Repo-Variable ZIP_FOLDER (z. B. sn_basis).
  • metadata.txt im Repo ist nur ein Abbild des letzten Builds; gepflegt wird .gitea/templates/metadata.template.
  • Release-Body = Block über dem ersten --- in changelog.txt.
  • Neue Workspace-Dateien (AGENTS.md, CLAUDE.md, .agents/, .claude/, docs/ der Repos) landen derzeit im ZIP → Excludes im zentralen Workflow ergänzen (offener Punkt in STATUS.md).

10. Linkliste-Repository

linkliste.csv (Semikolon, Spalten id;gruppe;ebene;inhalt;url;provider;stil) → Gitea Action → linkliste.xml/json/yaml auf main. Die Plan41-Laufzeit nutzt stattdessen die mitgelieferte assets/Linkliste.xlsx mit abweichenden Spaltennamen.