gwoe-wahlpruefsteine/wahlpruefsteine/aggregator.py
Dotty Dotter f2a12f1238 Initial: GWÖ-Wahlprüfsteine Auswertung Bayern 2026
- Scraper: HTML-Extraktion von ECOnGOOD-Webseite
- Analyzer: LLM-Bewertung (Qwen) nach GWÖ-Matrix 2.0
- Aggregator: Partei-Auswertung + Kandidat:innen-Ranking
- CLI: Reproduzierbarer Workflow (scrape → analyze → aggregate)
- Output: 7 Dokumente inkl. Pressemitteilung und Methodik
- 27 Kandidat:innen, 162 Einzelbewertungen
2026-03-30 23:37:11 +02:00

677 lines
24 KiB
Python
Raw 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.

#!/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())