Phase-2-Erweiterungen des Stimmverhalten-Tabs:
**1. Empfehlungs-Konsistenz (#167):**
Pro Fraktion: Anteil der Anträge mit GWÖ-Empfehlung
"Uneingeschränkt unterstützen" oder "Unterstützen mit Änderungen",
bei denen die Fraktion trotzdem NEIN gestimmt hat. Orthogonal zur
Heuchelei-Quote — prüft NICHT gegen Wahlprogramm-Treue, sondern gegen
die GWÖ-Empfehlung des Systems.
- `aggregate_empfehlungs_konsistenz()` in app/auswertungen.py
- `GET /api/auswertungen/empfehlungs-konsistenz`
- 5. Chart-Sub-Section im Stimmverhalten-Tab (rote Bar Chart, 0..100%)
**2. CSV-Export (Phase-1-Querschnitts-TODO):**
Long-Format-CSV mit Spalten: drucksache, bundesland, wahlperiode, datum,
gwoe_score, empfehlung, partei, vote, ist_antragsteller. Macht alle
Stimmverhalten-Aussagen wissenschaftlich auswertbar (R/pandas/Excel).
- `export_stimmverhalten_csv()` in app/auswertungen.py
- `GET /api/auswertungen/stimmverhalten.csv` mit
Filter-Parametern bundesland/wahlperiode/exclude_antragsteller
- "CSV-Export"-Button im Stimmverhalten-Tab neben dem Toggle
**Tests:** 27 Stimmverhalten-Tests (war 18, +4 Empfehlungs-Konsistenz,
+5 CSV-Export). Fixture um `empfehlung`-Spalte erweitert.
Suite: 989 Tests grün (war 980).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Neue Auswertungs-Sicht: Welche Fraktionen stimmen häufiger gemeinwohl-
orientierten Anträgen zu? Verschneidet GWÖ-Bewertung pro Antrag mit
dem tatsächlichen Plenum-Stimmverhalten der Fraktionen.
Vier Aussagen, alle hinter dem neuen Tab "Stimmverhalten":
1. **Gemeinwohl-Stimm-Index** pro Fraktion: Ø-GWÖ-Score der JA-Anträge
minus Ø-GWÖ-Score der NEIN-Anträge. Domain −10..+10. Positiv = stimmt
eher Gemeinwohl-affinen Anträgen zu.
2. **Heuchelei-Quote** pro Fraktion: Anteil der Anträge mit
wahlprogramm_score ≥ 7 (passt zum eigenen Wahlprogramm), bei denen
die Fraktion trotzdem NEIN gestimmt hat.
3. **Stimm-Index pro GWÖ-Wert** als Heatmap: 5 Spalten (Würde,
Solidarität, Nachhaltigkeit, Gerechtigkeit, Demokratie) aus den
gwoe_matrix-Suffix-Spalten. Domain −5..+5 pro Zelle.
4. **Cross-BL-Vergleich** als Grouped Bar: gleiche Fraktion in
mehreren Ländern. Nur Fraktionen in ≥2 BL mit ausreichender
Datenbasis.
Querschnitt:
- `exclude_antragsteller=True` per Default (Toggle-Checkbox in UI),
weil Antragsteller-Fraktionen quasi immer JA stimmen → würde Index
verzerren. Toggle macht den Effekt sichtbar.
- `min_n=5` pro Fraktion fuer Stimm-Index, n=3 fuer Heatmaps.
Fraktionen unter dem Cutoff werden als "Nicht aussagekräftig" separat
gelistet.
- Caveat-Banner mit `n_assessments_matched` über jedem Chart.
Implementation:
- `app/auswertungen.py`: `_load_assessments_with_votes()` JOIN-Helper
+ 4 Aggregat-Funktionen analog zu `aggregate_matrix`-Pattern.
Reuse: `normalize_partei` für Aliasing (BÜNDNIS 90/DIE GRÜNEN →
GRÜNE), `wahlperiode_for` für WP-Filter.
- `app/main.py`: 4 neue read-only GET-Endpoints unter
`/api/auswertungen/stimm-index|heuchelei|stimm-index-pro-wert|
stimm-index-cross-bl`.
- `app/templates/v2/screens/auswertungen.html`: 4. Tab "Stimmverhalten"
mit 4 Sub-Sektionen, Chart.js Bars + HTML-Heatmap-Tabelle.
- `tests/test_auswertungen_stimmverhalten.py`: 18 neue Tests
(Fixture-DB mit 13 Assessments + 13 Vote-Results, Edge-Cases:
GRÜNE-positiver-Index, AfD-negativer-Index, exclude_antragsteller-
Effekt, min_n-Cutoff, leere DB).
Sparse-Data-Realität: aktuell 35 Assessments im prod, dünne Datenbasis
fuer einige Fraktionen. Feature wächst mit Issue #44 Batch-Bewertung.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>