GWÖ-Antragsprüfer: Automatische Gemeinwohl-Bilanzierung von Parlamentsanträgen nach der GWÖ-Matrix 2.0
Go to file
Dotty Dotter f98e64c734 Add pytest suite + fix two regex bugs uncovered by it (#46)
Erste Tests für die Codebase. 77 Tests, 0.08s Laufzeit, decken die
drei Bug-Klassen aus der April-2026-Adapter-Session ab plus haben
schon zwei weitere Bugs in Production-Code aufgedeckt.

## Setup

- requirements-dev.txt mit pytest + pytest-asyncio
- pytest.ini mit asyncio_mode=auto
- tests/conftest.py stubbt fitz/bs4/openai/pydantic_settings, damit
  die Suite ohne den vollen prod-requirements-Satz läuft (pure unit
  tests, kein PDF-Parsing, kein HTTP)

## Tests

- tests/test_parlamente.py (33 Tests)
  * PortalaAdapter._parse_hit_list_cards: doctype/doctype_full
    NameError-Regression aus 1cb030a, plus Title/Drucksache/Fraktion-
    /Datum/PDF-Extraktion gegen ein BE-Card-Fixture
  * PortalaAdapter._parse_hit_list_dump: gegen ein LSA-Perl-Dump-
    Fixture inkl. Hex-Escape-Decoding (\x{fc} → ü)
  * PortalaAdapter._parse_hit_list_html: Auto-Detection zwischen
    Card- und Dump-Format
  * PortalaAdapter._normalize_fraktion: kanonische Fraktion-Codes
    inkl. F.D.P.-mit-Punkten, BÜNDNIS 90, DIE LINKE, BSW
  * ParLDokAdapter._hit_to_drucksache: JSON-Hit → Drucksache
    Mapping inkl. /navpanes-Stripping, MdL-mit-Partei-in-Klammern,
    Landesregierung-Detection
  * ParLDokAdapter._fulltext_id: bundle.js-mirroring (deferred,
    aber dokumentiert)
  * ADAPTERS-Registry-Sanity

- tests/test_embeddings.py (11 Tests)
  * _chunk_source_label: Programm-Name + Seite (Halluzinations-
    Bug-Regression aus 1b5fd96)
  * format_quotes_for_prompt: jeder Chunk muss Programm-Name
    enthalten, strict-citation-Hinweis muss im Output sein,
    keine NRW-Halluzinationen für MV/BE-Chunk-Sets

- tests/test_wahlprogramme.py (14 Tests)
  * Registry-Struktur (jahr int, seiten int, .pdf-Endung)
  * File-Existenz: jede registrierte PDF muss in
    static/referenzen/ liegen — würde Tippfehler in den 22
    indexierten Programmen sofort fangen
  * embeddings.PROGRAMME-Konsistenz-Cross-Check

- tests/test_bundeslaender.py (15 Tests)
  * Sanity über 16-State-Registry
  * #48-Klassifikations-Regression: TH=ParlDok, HB=StarWeb,
    SN=Eigensystem
  * Wahltermine plausibel (zwischen 2026 und 2035)

- tests/test_analyzer.py (4 Tests)
  * Markdown-Codeblock-Stripping aus dem JSON-Retry-Loop

## Bug-Funde während der Test-Schreibphase

Zwei Production-Bugs in den _normalize_fraktion-Helfern wurden
durch die neuen Tests sofort aufgedeckt und im selben Commit gefixt:

1. PortalaAdapter._normalize_fraktion matched "F.D.P." (mit Punkten,
   wie historische SH/HB-Drucksachen) nicht — Regex \bFDP\b ist zu
   strikt. Fix: \bF\.?\s*D\.?\s*P\.?\b analog zu ParLDokAdapter.

2. ParLDokAdapter._normalize_fraktion (auch PortalaAdapter) matched
   "Ministerium der Finanzen" nicht als Landesregierung, weil
   \bMINISTER\b die Wortgrenze auch nach MINISTER verlangt — bei
   MINISTERIUM steht aber IUM danach, keine Wortgrenze. Fix:
   \bMINISTER ohne abschließendes \b.

Beide Bugs hätten Fraktion-Felder bei Drucksachen der Bremischen
Bürgerschaft (FDP-Listen) und bei Landesregierungs-Drucksachen
in MV/LSA fälschlich leer gelassen — exakt der "fraktionen=[]"-
Befund aus dem MV-Smoke-Test in #4.

Phase 0 aus Roadmap-Issue #49.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 23:26:06 +02:00
app Add pytest suite + fix two regex bugs uncovered by it (#46) 2026-04-08 23:26:06 +02:00
tests Add pytest suite + fix two regex bugs uncovered by it (#46) 2026-04-08 23:26:06 +02:00
.dockerignore Initial commit: GWÖ-Antragsprüfer v1.0 2026-03-28 22:30:24 +01:00
.env.example Initial commit: GWÖ-Antragsprüfer v1.0 2026-03-28 22:30:24 +01:00
.gitignore Initial commit: GWÖ-Antragsprüfer v1.0 2026-03-28 22:30:24 +01:00
.tarignore Initial commit: GWÖ-Antragsprüfer v1.0 2026-03-28 22:30:24 +01:00
docker-compose.yml Initial commit: GWÖ-Antragsprüfer v1.0 2026-03-28 22:30:24 +01:00
Dockerfile Initial commit: GWÖ-Antragsprüfer v1.0 2026-03-28 22:30:24 +01:00
LICENSE Initial commit: GWÖ-Antragsprüfer v1.0 2026-03-28 22:30:24 +01:00
pytest.ini Add pytest suite + fix two regex bugs uncovered by it (#46) 2026-04-08 23:26:06 +02:00
README.md Initial commit: GWÖ-Antragsprüfer v1.0 2026-03-28 22:30:24 +01:00
requirements-dev.txt Add pytest suite + fix two regex bugs uncovered by it (#46) 2026-04-08 23:26:06 +02:00
requirements.txt Initial commit: GWÖ-Antragsprüfer v1.0 2026-03-28 22:30:24 +01:00

GWÖ-Antragsprüfer

Automatische Gemeinwohl-Bilanzierung von Parlamentsanträgen nach der GWÖ-Matrix 2.0 für Gemeinden

Python FastAPI License

🎯 Was ist das?

Der GWÖ-Antragsprüfer analysiert Anträge aus Landesparlamenten (aktuell NRW) und bewertet sie nach den Kriterien der Gemeinwohl-Ökonomie (GWÖ):

  • GWÖ-Score (0-10): Wie gut entspricht der Antrag den GWÖ-Werten?
  • Matrix-Zuordnung: Welche Felder der GWÖ-Matrix werden adressiert?
  • Programmtreue: Passt der Antrag zu Wahl- und Parteiprogrammen?
  • Verbesserungsvorschläge: Konkrete Textänderungen mit GWÖ-Begründung

Features

  • 🔍 Landtag-Suche: Direkte Anbindung an OPAL (NRW Parlamentsdokumentation)
  • 📊 GWÖ-Matrix-Visualisierung: 5×5-Tabelle mit Bewertungssymbolen
  • 🏷️ Tag-Wolke: Filter nach Themen mit Multi-Select
  • 🎯 Partei-Filter: Durchschnittswerte pro Fraktion
  • 📄 PDF-Export: Professionelle Berichte im GWÖ-Design
  • 🔒 Security: CSP, CORS, Rate Limiting

🚀 Schnellstart

Voraussetzungen

  • Python 3.12+
  • Docker & Docker Compose
  • DashScope API-Key (Qwen LLM)

Installation

# Repository klonen
git clone https://github.com/tobiasroedel/gwoe-antragspruefer.git
cd gwoe-antragspruefer

# Environment-Variablen
cp .env.example .env
# DASHSCOPE_API_KEY eintragen

# Mit Docker starten
docker compose up -d

# Oder lokal entwickeln
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
uvicorn app.main:app --reload

Die App läuft auf http://localhost:8000

📁 Projektstruktur

webapp/
├── app/
│   ├── main.py           # FastAPI-Endpoints
│   ├── analyzer.py       # LLM-Analyse-Logik
│   ├── database.py       # SQLite-Persistenz
│   ├── models.py         # Pydantic-Modelle
│   ├── parlamente.py     # Landtag-Adapter (OPAL)
│   ├── report.py         # PDF-Generierung
│   ├── config.py         # Settings
│   ├── kontext/          # GWÖ-Matrix, Wahlprogramme
│   ├── templates/        # Jinja2-HTML
│   └── static/           # CSS, JS, Assets
├── data/                 # SQLite-DBs (Volume)
├── reports/              # Generierte PDFs (Volume)
├── docker-compose.yml
├── Dockerfile
└── requirements.txt

🔧 Konfiguration

Environment-Variablen

Variable Beschreibung Default
DASHSCOPE_API_KEY Alibaba DashScope API-Key (required)
LLM_MODEL_DEFAULT Standard-Modell qwen-plus-latest
LLM_MODEL_PREMIUM Premium-Modell qwen-max

Unterstützte Bundesländer

Code Name Status
NRW Nordrhein-Westfalen Aktiv
BY Bayern 🔜 Geplant
BW Baden-Württemberg 🔜 Geplant

📊 API-Endpoints

Methode Pfad Beschreibung
GET / Web-UI
GET /api/assessments Alle Bewertungen
GET /api/assessment?drucksache=18/12345 Einzelne Bewertung
POST /api/analyze-drucksache Neue Analyse starten
GET /api/search?q=Klima Interne Suche
GET /api/search-landtag?q=Klima Landtag-Suche
GET /api/assessment/pdf?drucksache=18/12345 PDF-Download

🧠 GWÖ-Prompt (v5)

Der Analyse-Prompt basiert auf:

  • GWÖ-Matrix 2.0 für Gemeinden (Arbeitsbuch)
  • ECOnGOOD Corporate Design Manual 2024
  • Wahlprogramme der NRW-Landtagsparteien 2022

Ausgabe-Format:

  • GWÖ-Score mit Matrix-Feldern und Symbolen (++/+/○//)
  • Wahlprogramm- und Parteiprogrammtreue
  • Verbesserungsvorschläge im Redline-Format (Original → Vorschlag → Begründung)
  • Themen-Tags für Kategorisierung

🛠️ Entwicklung

# Tests ausführen
pytest

# Linting
ruff check app/

# Type-Checking
mypy app/

📝 Lizenz

MIT License - siehe LICENSE

🙏 Credits


Entwickelt von Tobias Rödel · tobiasroedel.de