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>
53 lines
1.9 KiB
Python
53 lines
1.9 KiB
Python
"""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 5–10 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
|