gwoe-wahlpruefsteine/wahlpruefsteine/aggregator.py

677 lines
24 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
"""
GWÖ-Wahlprüfsteine Aggregator
Erstellt Partei-Aggregationen und Berichte.
"""
import sqlite3
import json
from pathlib import Path
from dataclasses import dataclass
from datetime import datetime
@dataclass
class ParteiStatistik:
partei: str
anzahl_kandidaten: int
anzahl_antworten: int
avg_substanz: float
avg_gwoe: float
avg_wortanzahl: float
ja_quote: float # Anteil Ja-Antworten
top_felder: list[str] # Häufigste Matrix-Felder
beste_zitate: list[dict] # Top 3 Antworten
schwachpunkte: list[str]
def get_partei_statistiken(conn: sqlite3.Connection) -> list[ParteiStatistik]:
"""Holt aggregierte Statistiken pro Partei."""
cursor = conn.cursor()
# Basisstatistiken
cursor.execute("""
SELECT
k.partei_normalisiert AS partei,
COUNT(DISTINCT k.id) AS anzahl_kandidaten,
COUNT(b.id) AS anzahl_antworten,
ROUND(AVG(b.substanz_score), 2) AS avg_substanz,
ROUND(AVG(b.gwoe_score), 2) AS avg_gwoe,
ROUND(AVG(b.wortanzahl), 0) AS avg_wortanzahl
FROM kandidaten k
LEFT JOIN antworten_raw ar ON k.id = ar.kandidat_id
LEFT JOIN bewertungen b ON ar.id = b.antwort_id
WHERE b.id IS NOT NULL
GROUP BY k.partei_normalisiert
ORDER BY avg_gwoe DESC
""")
basis = {row['partei']: dict(row) for row in cursor.fetchall()}
# Ja-Quoten pro Partei
cursor.execute("""
SELECT
k.partei_normalisiert AS partei,
SUM(CASE WHEN ar.antwort_kurz = 'Ja' THEN 1 ELSE 0 END) * 1.0 / COUNT(*) AS ja_quote
FROM kandidaten k
JOIN antworten_raw ar ON k.id = ar.kandidat_id
GROUP BY k.partei_normalisiert
""")
ja_quoten = {row['partei']: row['ja_quote'] for row in cursor.fetchall()}
# Matrix-Felder pro Partei (häufigste)
matrix_felder = {}
for partei in basis.keys():
cursor.execute("""
SELECT b.matrix_felder
FROM bewertungen b
JOIN antworten_raw ar ON b.antwort_id = ar.id
JOIN kandidaten k ON ar.kandidat_id = k.id
WHERE k.partei_normalisiert = ? AND b.matrix_felder IS NOT NULL
""", (partei,))
feld_counts = {}
for row in cursor.fetchall():
try:
felder = json.loads(row['matrix_felder'])
for f in felder:
feld_counts[f] = feld_counts.get(f, 0) + 1
except:
pass
# Top 5 Felder
sorted_felder = sorted(feld_counts.items(), key=lambda x: -x[1])
matrix_felder[partei] = [f[0] for f in sorted_felder[:5]]
# Beste Zitate pro Partei (Top 3 nach GWÖ-Score)
beste_zitate = {}
for partei in basis.keys():
cursor.execute("""
SELECT
k.vorname || ' ' || k.nachname AS name,
k.kommune,
f.kurztext AS frage,
ar.antwort_erlaeuterung AS zitat,
b.gwoe_score,
b.gwoe_begruendung
FROM bewertungen b
JOIN antworten_raw ar ON b.antwort_id = ar.id
JOIN kandidaten k ON ar.kandidat_id = k.id
JOIN fragen f ON ar.frage_id = f.id
WHERE k.partei_normalisiert = ?
AND ar.antwort_erlaeuterung IS NOT NULL
AND LENGTH(ar.antwort_erlaeuterung) > 50
ORDER BY b.gwoe_score DESC
LIMIT 3
""", (partei,))
beste_zitate[partei] = [dict(row) for row in cursor.fetchall()]
# Zusammenbauen
statistiken = []
for partei, stats in basis.items():
statistiken.append(ParteiStatistik(
partei=partei,
anzahl_kandidaten=stats['anzahl_kandidaten'],
anzahl_antworten=stats['anzahl_antworten'],
avg_substanz=stats['avg_substanz'] or 0,
avg_gwoe=stats['avg_gwoe'] or 0,
avg_wortanzahl=stats['avg_wortanzahl'] or 0,
ja_quote=ja_quoten.get(partei, 0),
top_felder=matrix_felder.get(partei, []),
beste_zitate=beste_zitate.get(partei, []),
schwachpunkte=[] # Wird später gefüllt
))
return sorted(statistiken, key=lambda x: -x.avg_gwoe)
def get_fragen_statistiken(conn: sqlite3.Connection) -> list[dict]:
"""Statistiken pro Frage."""
cursor = conn.cursor()
cursor.execute("""
SELECT
f.nummer,
f.kurztext,
f.volltext,
COUNT(b.id) AS anzahl,
ROUND(AVG(b.substanz_score), 2) AS avg_substanz,
ROUND(AVG(b.gwoe_score), 2) AS avg_gwoe,
SUM(CASE WHEN ar.antwort_kurz = 'Ja' THEN 1 ELSE 0 END) AS ja_count,
SUM(CASE WHEN ar.antwort_kurz = 'Nein' THEN 1 ELSE 0 END) AS nein_count
FROM fragen f
LEFT JOIN antworten_raw ar ON f.id = ar.frage_id
LEFT JOIN bewertungen b ON ar.id = b.antwort_id
GROUP BY f.id
ORDER BY f.nummer
""")
return [dict(row) for row in cursor.fetchall()]
def generate_partei_report(statistiken: list[ParteiStatistik], fragen_stats: list[dict], conn: sqlite3.Connection) -> str:
"""Generiert den Markdown-Report für Parteien."""
# Kandidaten für Bandbreiten-Berechnung holen
cursor = conn.cursor()
cursor.execute("""
SELECT
k.partei_normalisiert AS partei,
k.vorname || ' ' || k.nachname AS name,
k.kommune,
ROUND(AVG(b.gwoe_score), 2) AS avg_gwoe
FROM kandidaten k
JOIN antworten_raw ar ON k.id = ar.kandidat_id
JOIN bewertungen b ON ar.id = b.antwort_id
GROUP BY k.id
""")
kandidaten_scores = {}
for row in cursor.fetchall():
if row['partei'] not in kandidaten_scores:
kandidaten_scores[row['partei']] = []
kandidaten_scores[row['partei']].append({
'name': row['name'],
'kommune': row['kommune'],
'avg_gwoe': row['avg_gwoe']
})
lines = [
"# GWÖ-Wahlprüfsteine Bayern 2026 — Partei-Auswertung",
"",
f"*Stand: {datetime.now().strftime('%d.%m.%Y %H:%M')}*",
"",
"---",
"",
"## Zusammenfassung",
"",
"| Partei | Kandidat:innen | Ø GWÖ | Bandbreite | Ø Substanz | Ja-Quote |",
"|--------|---------------|-------|------------|------------|----------|",
]
for s in statistiken:
# Bandbreite berechnen
if s.partei in kandidaten_scores and len(kandidaten_scores[s.partei]) > 1:
scores = [k['avg_gwoe'] for k in kandidaten_scores[s.partei]]
bandbreite = f"{min(scores):.1f}{max(scores):.1f}"
elif s.partei in kandidaten_scores:
bandbreite = f"{kandidaten_scores[s.partei][0]['avg_gwoe']:.1f}"
else:
bandbreite = ""
lines.append(f"| **{s.partei}** | {s.anzahl_kandidaten} | {s.avg_gwoe:.1f} | {bandbreite} | {s.avg_substanz:.1f}/3 | {s.ja_quote*100:.0f}% |")
lines.extend([
"",
"---",
"",
"## Übergreifende Beobachtungen",
"",
"### Das Substanz-Problem",
"",
"Die Analyse zeigt ein strukturelles Muster: **Hohe Zustimmungsquoten, aber wenig konkrete Maßnahmen.**",
"",
"- Die meisten Parteien haben Ja-Quoten von 75100%",
"- Der durchschnittliche Substanz-Score liegt jedoch nur bei 0.31.8 von 3",
"- Typisch: *\"Ja\"* ohne Erläuterung oder mit Floskeln wie *\"Gespräche führen\"*",
"",
"### Bandbreite innerhalb der Parteien",
"",
"Die Unterschiede *innerhalb* einer Partei sind oft größer als *zwischen* Parteien:",
"",
])
for s in statistiken:
if s.partei in kandidaten_scores and len(kandidaten_scores[s.partei]) > 1:
scores = [k['avg_gwoe'] for k in kandidaten_scores[s.partei]]
spanne = max(scores) - min(scores)
if spanne >= 2:
best = max(kandidaten_scores[s.partei], key=lambda x: x['avg_gwoe'])
worst = min(kandidaten_scores[s.partei], key=lambda x: x['avg_gwoe'])
lines.append(f"- **{s.partei}:** {worst['avg_gwoe']:.1f} ({worst['name']}) bis {best['avg_gwoe']:.1f} ({best['name']}) — Δ {spanne:.1f}")
lines.extend([
"",
"**→ Fazit:** Das Parteilabel allein sagt wenig über die GWÖ-Affinität der einzelnen Kandidat:innen aus.",
"",
"---",
"",
"## Detailauswertung nach Parteien",
""
])
for s in statistiken:
lines.extend([
f"### {s.partei}",
"",
f"**Kandidat:innen:** {s.anzahl_kandidaten} | **Antworten:** {s.anzahl_antworten} | **Ø Wortanzahl:** {s.avg_wortanzahl:.0f}",
"",
f"**GWÖ-Score:** {s.avg_gwoe:.1f}/10 | **Substanz:** {s.avg_substanz:.1f}/3 | **Ja-Quote:** {s.ja_quote*100:.0f}%",
"",
])
if s.top_felder:
lines.append(f"**Häufigste Matrix-Felder:** {', '.join(s.top_felder)}")
lines.append("")
if s.beste_zitate:
lines.append("**Beste Antworten:**")
lines.append("")
for z in s.beste_zitate:
zitat = z['zitat'][:300] + "..." if len(z['zitat']) > 300 else z['zitat']
lines.extend([
f"> *\"{zitat}\"*",
f"> — {z['name']} ({z['kommune']}), zu Frage \"{z['frage']}\" (GWÖ: {z['gwoe_score']:.1f})",
""
])
lines.append("---")
lines.append("")
# Fragen-Statistik
lines.extend([
"## Auswertung nach Fragen",
"",
"| Nr | Frage | Ja | Nein | Ø GWÖ | Ø Substanz |",
"|----|-------|-----|------|-------|------------|",
])
for f in fragen_stats:
lines.append(f"| {f['nummer']} | {f['kurztext']} | {f['ja_count']} | {f['nein_count']} | {f['avg_gwoe']:.1f} | {f['avg_substanz']:.1f} |")
return "\n".join(lines)
def generate_landscape_report(statistiken: list[ParteiStatistik]) -> str:
"""Generiert die komprimierte Parteienlandschafts-Analyse."""
# Kategorisieren
vorreiter = [s for s in statistiken if s.avg_gwoe >= 6]
mittelfeld = [s for s in statistiken if 3 <= s.avg_gwoe < 6]
nachzuegler = [s for s in statistiken if s.avg_gwoe < 3]
lines = [
"# GWÖ-Parteienlandschaft Bayern 2026",
"",
"*Kommunalwahlen — komprimierte Analyse*",
"",
"---",
"",
]
if vorreiter:
lines.extend([
"## 🟢 Vorreiter (GWÖ ≥ 6)",
"",
])
for s in vorreiter:
lines.append(f"**{s.partei}** (Ø {s.avg_gwoe:.1f}): Hohe GWÖ-Affinität, {s.ja_quote*100:.0f}% Zustimmung zu allen Fragen.")
lines.append("")
if mittelfeld:
lines.extend([
"## 🟡 Mittelfeld (GWÖ 3-6)",
"",
])
for s in mittelfeld:
lines.append(f"**{s.partei}** (Ø {s.avg_gwoe:.1f}): Partielle Übereinstimmung, erkennbare Offenheit für GWÖ-Themen.")
lines.append("")
if nachzuegler:
lines.extend([
"## 🔴 Zurückhaltend (GWÖ < 3)",
"",
])
for s in nachzuegler:
lines.append(f"**{s.partei}** (Ø {s.avg_gwoe:.1f}): Geringe GWÖ-Resonanz, {(1-s.ja_quote)*100:.0f}% Ablehnung.")
lines.append("")
# Kernaussagen
lines.extend([
"---",
"",
"## Kernaussagen",
"",
])
if statistiken:
best = statistiken[0]
worst = statistiken[-1]
lines.extend([
f"- **Höchste GWÖ-Affinität:** {best.partei} mit Ø {best.avg_gwoe:.1f}",
f"- **Niedrigste GWÖ-Affinität:** {worst.partei} mit Ø {worst.avg_gwoe:.1f}",
f"- **Spannweite:** {best.avg_gwoe - worst.avg_gwoe:.1f} Punkte zwischen Spitze und Ende",
"",
])
return "\n".join(lines)
def get_kandidaten_ranking(conn: sqlite3.Connection) -> list[dict]:
"""Holt Einzelranking aller Kandidat:innen."""
cursor = conn.cursor()
cursor.execute("""
SELECT
k.vorname || ' ' || k.nachname AS name,
k.kommune,
k.partei_normalisiert AS partei,
k.partei_raw,
ROUND(AVG(b.gwoe_score), 2) AS avg_gwoe,
ROUND(AVG(b.substanz_score), 2) AS avg_substanz,
MAX(b.gwoe_score) AS max_gwoe,
MIN(b.gwoe_score) AS min_gwoe,
COUNT(b.id) AS anzahl_antworten
FROM kandidaten k
JOIN antworten_raw ar ON k.id = ar.kandidat_id
JOIN bewertungen b ON ar.id = b.antwort_id
GROUP BY k.id
ORDER BY avg_gwoe DESC, avg_substanz DESC
""")
return [dict(row) for row in cursor.fetchall()]
def generate_kandidaten_report(conn: sqlite3.Connection, statistiken: list[ParteiStatistik]) -> str:
"""Generiert den Kandidat:innen-Report."""
kandidaten = get_kandidaten_ranking(conn)
lines = [
"# GWÖ-Wahlprüfsteine Bayern 2026 — Kandidat:innen-Ranking",
"",
f"*Stand: {datetime.now().strftime('%d.%m.%Y %H:%M')}*",
"",
"---",
"",
"## Gesamtranking",
"",
"| Rang | Name | Kommune | Partei | Ø GWÖ | Ø Substanz | Spanne |",
"|------|------|---------|--------|-------|------------|--------|",
]
for i, k in enumerate(kandidaten, 1):
spanne = f"{k['min_gwoe']:.1f}{k['max_gwoe']:.1f}"
lines.append(f"| {i} | **{k['name']}** | {k['kommune']} | {k['partei']} | {k['avg_gwoe']:.1f} | {k['avg_substanz']:.1f}/3 | {spanne} |")
# Kategorisierung
vorreiter = [k for k in kandidaten if k['avg_gwoe'] >= 5]
solide = [k for k in kandidaten if 3 <= k['avg_gwoe'] < 5]
schwach = [k for k in kandidaten if k['avg_gwoe'] < 3]
lines.extend([
"",
"---",
"",
"## Kategorisierung",
"",
])
if vorreiter:
lines.extend([
"### 🟢 GWÖ-Vorreiter:innen (Ø ≥ 5.0)",
"",
])
for k in vorreiter:
lines.append(f"- **{k['name']}** ({k['kommune']}, {k['partei']}): Ø {k['avg_gwoe']:.1f}, Spanne {k['min_gwoe']:.1f}{k['max_gwoe']:.1f}")
lines.append("")
if solide:
lines.extend([
"### 🟡 Solide Basis (Ø 3.05.0)",
"",
])
for k in solide:
lines.append(f"- **{k['name']}** ({k['kommune']}, {k['partei']}): Ø {k['avg_gwoe']:.1f}")
lines.append("")
if schwach:
lines.extend([
"### 🔴 Wenig GWÖ-Substanz (Ø < 3.0)",
"",
])
for k in schwach:
lines.append(f"- {k['name']} ({k['kommune']}, {k['partei']}): Ø {k['avg_gwoe']:.1f}")
lines.append("")
# Bandbreite innerhalb Parteien
lines.extend([
"---",
"",
"## Bandbreite innerhalb der Parteien",
"",
"Die Durchschnittswerte pro Partei verdecken teils erhebliche Unterschiede zwischen einzelnen Kandidat:innen:",
"",
])
for s in statistiken:
partei_kandidaten = [k for k in kandidaten if k['partei'] == s.partei]
if len(partei_kandidaten) > 1:
scores = [k['avg_gwoe'] for k in partei_kandidaten]
best = max(partei_kandidaten, key=lambda x: x['avg_gwoe'])
worst = min(partei_kandidaten, key=lambda x: x['avg_gwoe'])
spanne = max(scores) - min(scores)
lines.append(f"### {s.partei}")
lines.append(f"- **Partei-Durchschnitt:** {s.avg_gwoe:.1f}")
lines.append(f"- **Bandbreite:** {min(scores):.1f} {max(scores):.1f}{spanne:.1f})")
lines.append(f"- **Beste:r:** {best['name']} ({best['kommune']}) mit Ø {best['avg_gwoe']:.1f}")
lines.append(f"- **Schwächste:r:** {worst['name']} ({worst['kommune']}) mit Ø {worst['avg_gwoe']:.1f}")
lines.append("")
elif len(partei_kandidaten) == 1:
k = partei_kandidaten[0]
lines.append(f"### {s.partei}")
lines.append(f"- Nur 1 Kandidat:in: {k['name']} ({k['kommune']}) mit Ø {k['avg_gwoe']:.1f}")
lines.append("")
return "\n".join(lines)
def generate_recommendation(statistiken: list[ParteiStatistik], conn: sqlite3.Connection) -> str:
"""Generiert die begründete Wahlempfehlung."""
kandidaten = get_kandidaten_ranking(conn)
vorreiter = [k for k in kandidaten if k['avg_gwoe'] >= 5]
# Berechne Gesamtstatistik
alle_gwoe = [k['avg_gwoe'] for k in kandidaten]
alle_substanz = [k['avg_substanz'] for k in kandidaten]
lines = [
"# GWÖ-Wahlempfehlung Bayern 2026",
"",
"*Basierend auf der Analyse der Wahlprüfstein-Antworten*",
"",
"---",
"",
"## Methodik",
"",
"Diese Empfehlung basiert auf:",
"- GWÖ-Score (0-10) nach Matrix 2.0 für Gemeinden",
"- Substanz der Antworten (konkrete Maßnahmen vs. Floskeln)",
"- Zustimmungsquote zu den 6 GWÖ-Kernfragen",
"",
"---",
"",
"## Übergreifende Beobachtungen",
"",
"### Das Substanz-Problem",
"",
f"Von {len(kandidaten)} Kandidat:innen erreichen nur **{len(vorreiter)}** einen GWÖ-Durchschnitt ≥ 5.0.",
"",
"Die Analyse zeigt ein strukturelles Problem: **Viele Ja-Antworten ohne konkrete Maßnahmen.**",
"",
f"- **Ø GWÖ-Score aller Kandidat:innen:** {sum(alle_gwoe)/len(alle_gwoe):.1f}/10",
f"- **Ø Substanz-Score:** {sum(alle_substanz)/len(alle_substanz):.1f}/3",
f"- **Ja-Quote:** hoch (85-100% bei den meisten Parteien)",
f"- **Aber:** Konkrete Umsetzungsideen fehlen häufig",
"",
"Typisches Muster: *\"Ja\"* ohne Erläuterung oder mit Floskeln wie *\"Gespräche führen\"*, *\"Unterstützung anbieten\"*.",
"",
"### Parteilabel ≠ Kandidat:innen-Qualität",
"",
"Die Bandbreite *innerhalb* der Parteien ist oft größer als *zwischen* den Parteien:",
"",
]
# Beispiele für Bandbreite
for s in statistiken:
partei_kandidaten = [k for k in kandidaten if k['partei'] == s.partei]
if len(partei_kandidaten) > 1:
scores = [k['avg_gwoe'] for k in partei_kandidaten]
spanne = max(scores) - min(scores)
if spanne >= 3:
best = max(partei_kandidaten, key=lambda x: x['avg_gwoe'])
worst = min(partei_kandidaten, key=lambda x: x['avg_gwoe'])
lines.append(f"- **{s.partei}:** {worst['name']} ({worst['avg_gwoe']:.1f}) bis {best['name']} ({best['avg_gwoe']:.1f}) — Δ {spanne:.1f} Punkte!")
lines.extend([
"",
"---",
"",
"## Wahlempfehlung",
"",
"### Keine pauschale Parteiempfehlung möglich",
"",
"**Für Bayern können wir keine übergreifende Wahlempfehlung auf Parteiebene geben.**",
"",
"Die Unterschiede zwischen einzelnen Kandidat:innen derselben Partei sind zu groß. ",
"Ein Grünen-Kandidat kann GWÖ-Vorreiter sein, während ein anderer kaum Substanz liefert. ",
"Das gleiche gilt für ÖDP, Freie Wähler und andere.",
"",
"**→ Empfehlung: Prüfen Sie die konkreten Kandidat:innen in Ihrer Kommune!**",
"",
"Siehe dazu: [Kandidat:innen-Ranking](kandidaten-ranking.md)",
"",
])
if vorreiter:
lines.extend([
"### 🟢 GWÖ-Vorreiter:innen (individuell empfehlenswert)",
"",
"Diese Kandidat:innen zeigen überdurchschnittliches GWÖ-Engagement:",
"",
])
for k in vorreiter:
lines.append(f"- **{k['name']}** ({k['kommune']}, {k['partei']}): Ø {k['avg_gwoe']:.1f}")
lines.append("")
# Eingeschränkt
eingeschraenkt = [s for s in statistiken if 3 <= s.avg_gwoe < 5]
if eingeschraenkt:
lines.extend([
"### ⚠️ Parteien mit partieller Übereinstimmung",
"",
"Partei-Durchschnitt im Mittelfeld — individuelle Prüfung empfohlen:",
"",
])
for s in eingeschraenkt:
lines.append(f"- **{s.partei}** (Ø {s.avg_gwoe:.1f})")
lines.append("")
# Nicht empfohlen
nicht_empfohlen = [s for s in statistiken if s.avg_gwoe < 3]
if nicht_empfohlen:
lines.extend([
"### ❌ Geringe GWÖ-Resonanz",
"",
"Diese Parteien zeigen im Durchschnitt wenig GWÖ-Affinität:",
"",
])
for s in nicht_empfohlen:
lines.append(f"- **{s.partei}** (Ø {s.avg_gwoe:.1f})")
lines.append("")
lines.extend([
"---",
"",
"## Fazit",
"",
"Die bayerischen Kommunalwahlen 2026 zeigen: **GWÖ-Unterstützung ist Sache einzelner Personen, nicht ganzer Parteien.**",
"",
"Wer GWÖ-affine Bürgermeister:innen wählen möchte, sollte:",
"1. Das Kandidat:innen-Ranking konsultieren",
"2. Die konkreten Antworten der lokalen Kandidat:innen lesen",
"3. Bei Interesse nachfragen: *\"Welche konkreten Maßnahmen planen Sie?\"*",
"",
"---",
"",
"## Disclaimer",
"",
"Diese Empfehlung bezieht sich ausschließlich auf die Übereinstimmung mit GWÖ-Werten ",
"und ersetzt keine umfassende politische Bewertung. Die Analyse basiert auf den ",
"freiwilligen Antworten der Kandidat:innen auf die ECOnGOOD-Wahlprüfsteine.",
"",
f"*Erstellt: {datetime.now().strftime('%d.%m.%Y')}*"
])
return "\n".join(lines)
def main():
import argparse
parser = argparse.ArgumentParser(description='GWÖ-Wahlprüfsteine Aggregator')
parser.add_argument('--db', type=Path, default=Path(__file__).parent / 'wahlpruefsteine.db',
help='Pfad zur SQLite-Datenbank')
parser.add_argument('--output', type=Path, default=Path(__file__).parent / 'output',
help='Ausgabeverzeichnis')
args = parser.parse_args()
# Output-Verzeichnis erstellen
args.output.mkdir(parents=True, exist_ok=True)
# Datenbank öffnen
conn = sqlite3.connect(args.db)
conn.row_factory = sqlite3.Row
# Statistiken holen
print("Lade Statistiken...")
statistiken = get_partei_statistiken(conn)
fragen_stats = get_fragen_statistiken(conn)
if not statistiken:
print("FEHLER: Keine Bewertungen in der Datenbank. Erst analyzer.py ausführen!")
return 1
print(f"Gefunden: {len(statistiken)} Parteien/Gruppen")
# Reports generieren
print("Generiere Reports...")
# 1. Partei-Report
partei_report = generate_partei_report(statistiken, fragen_stats, conn)
partei_path = args.output / "partei-auswertung.md"
partei_path.write_text(partei_report)
print(f"{partei_path}")
# 2. Parteienlandschaft
landscape_report = generate_landscape_report(statistiken)
landscape_path = args.output / "parteienlandschaft.md"
landscape_path.write_text(landscape_report)
print(f"{landscape_path}")
# 3. Kandidat:innen-Ranking
kandidaten_report = generate_kandidaten_report(conn, statistiken)
kandidaten_path = args.output / "kandidaten-ranking.md"
kandidaten_path.write_text(kandidaten_report)
print(f"{kandidaten_path}")
# 4. Wahlempfehlung
recommendation = generate_recommendation(statistiken, conn)
rec_path = args.output / "wahlempfehlung.md"
rec_path.write_text(recommendation)
print(f"{rec_path}")
conn.close()
print("\nFertig!")
return 0
if __name__ == '__main__':
exit(main())