gwoe-antragspruefer/app/wahlperioden.py
Dotty Dotter 3631e5418c Phase C: Auswertungen-Dashboard #58 + CSV-Export #45 (Roadmap #59)
Drei-dimensionale Aggregations-Sicht über Bundesland × Partei ×
Wahlperiode mit minimalem Frontend.

Backend (`app/auswertungen.py`):

- `aggregate_matrix(filter_wp=None)` — 2D-Matrix Bundesland × Partei mit
  (n, Ø-Score) pro Zelle, optional gefiltert nach Wahlperiode
- `aggregate_zeitreihe(bundesland, partei)` — Score-Verlauf einer
  (BL, Partei)-Kombination über alle bekannten WPs
- `export_long_format()` — Long-Format-CSV-Export für externe Tools
  (deckt #45 vollständig ab)
- Partei-Auflösung läuft strikt durch `normalize_partei()` aus #55 —
  damit wird BB-`FREIE WÄHLER` korrekt als `BVB-FW` aggregiert und
  NICHT mit dem RP-FW zusammengezählt

Wahlperioden-Helper (`app/wahlperioden.py`):

- `wahlperiode_for(datum, bundesland)` mappt ein ISO-Datum + BL auf eine
  Kennung wie `"NRW-WP18"` oder `"MV-WP7"` (Vorgänger-WP). Single Source
  of Truth ist `BUNDESLAENDER[bl].wahlperiode_start`
- `all_wahlperioden()` für UI-Filter-Dropdowns

Endpoints in `app/main.py`:

- `GET /auswertungen` — HTML-Seite (neues Template)
- `GET /api/auswertungen/matrix?wahlperiode=NRW-WP18` — JSON-Matrix
- `GET /api/auswertungen/zeitreihe?bundesland=MV&partei=CDU` — JSON-Verlauf
- `GET /api/auswertungen/export.csv` — CSV-Download

Frontend (`app/templates/auswertungen.html`):

- Statisches Template mit Vanilla-JS, kein Build-Step
- Wahlperioden-Dropdown + Reload-Button + CSV-Export-Button
- Matrix-Tabelle mit Score-Color-Coding (rot ≤ 3, gelb 3-6, grün > 6)
- Sticky-Bundesland-Spalte für horizontales Scrolling

Tests (`tests/test_auswertungen.py`):

- 19 Cases mit in-memory SQLite-Fixture
- Verifiziert WP-Mapping, Matrix-Aggregation, Koalitions-Counting,
  WP-Filter-Korrektheit, BVB-FW-Disambiguierung in der Matrix,
  CSV-Long-Format
- 176 Unit-Tests grün (157 alt + 19 neu)

Refs: #58, #45, #59 (Phase C)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 11:25:57 +02:00

53 lines
1.9 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Wahlperioden-Helper für die Auswertungen.
Maps a Drucksache-Datum + Bundesland auf eine Wahlperioden-Kennung.
Single Source of Truth ist ``BUNDESLAENDER[bl].wahlperiode_start`` —
alles vor diesem Datum gehört zur Vorgänger-WP, alles ab dem Datum zur
aktuellen.
Granularität: pro Bundesland kennen wir genau zwei WP — die laufende und
die direkt davor. Das reicht für die Aggregations-Sicht (#58), denn der
gesamte Antragsbestand stammt aus den letzten 510 Jahren. Sollte später
eine echte Multi-WP-Historie nötig werden, ist der Erweiterungspunkt
``_wp_calendar`` als Liste von ``(start, ende, wp_id)``-Tupeln pro BL.
"""
from __future__ import annotations
from typing import Optional
from .bundeslaender import BUNDESLAENDER
def wahlperiode_for(datum: str, bundesland: str) -> Optional[str]:
"""Liefert die Wahlperioden-Kennung für ein Datum + Bundesland.
Format der Kennung: ``"<BL>-WP<n>"``, z.B. ``"NRW-WP18"``,
``"MV-WP7"`` (Vorgänger-WP). Returns ``None`` wenn das Bundesland
unbekannt oder das Datum nicht parsbar ist.
>>> wahlperiode_for("2026-03-18", "MV")
'MV-WP8'
>>> wahlperiode_for("2020-01-01", "MV")
'MV-WP7'
"""
bl = BUNDESLAENDER.get(bundesland)
if bl is None:
return None
if not datum:
return f"{bundesland}-WP{bl.wahlperiode}" # default: aktuelle WP
# ISO-Datum ist lexikographisch vergleichbar — keine datetime-Parsing
# nötig, solange das Format YYYY-MM-DD eingehalten wird.
if datum >= bl.wahlperiode_start:
return f"{bundesland}-WP{bl.wahlperiode}"
return f"{bundesland}-WP{bl.wahlperiode - 1}"
def all_wahlperioden() -> list[str]:
"""Liste aller bekannten Wahlperioden-Kennungen (aktuelle + Vorgänger
pro Bundesland). Nützlich für UI-Filter-Dropdowns."""
out: list[str] = []
for code, bl in BUNDESLAENDER.items():
out.append(f"{code}-WP{bl.wahlperiode}")
out.append(f"{code}-WP{bl.wahlperiode - 1}")
return out