129 lines
3.9 KiB
Python
129 lines
3.9 KiB
Python
|
|
from __future__ import annotations
|
||
|
|
"""API routes for Fraktions-Dashboard."""
|
||
|
|
|
||
|
|
from typing import Optional, List
|
||
|
|
|
||
|
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
||
|
|
|
||
|
|
from tracker.core.kategorien import BEWERTUNGSKATEGORIEN
|
||
|
|
from tracker.db.session import get_connection
|
||
|
|
|
||
|
|
router = APIRouter(prefix="/fraktionen", tags=["Fraktionen"])
|
||
|
|
|
||
|
|
|
||
|
|
def _db():
|
||
|
|
conn = get_connection()
|
||
|
|
try:
|
||
|
|
yield conn
|
||
|
|
finally:
|
||
|
|
conn.close()
|
||
|
|
|
||
|
|
|
||
|
|
@router.get("/kategorien")
|
||
|
|
def get_kategorien():
|
||
|
|
"""Return category definitions for frontend tooltips."""
|
||
|
|
return {
|
||
|
|
key: {
|
||
|
|
"label": val["label"],
|
||
|
|
"farbe": val["farbe"],
|
||
|
|
"icon": val["icon"],
|
||
|
|
"beschreibung": val["beschreibung"],
|
||
|
|
"beispiel": val["beispiel"],
|
||
|
|
"score_range": val["score_range"],
|
||
|
|
}
|
||
|
|
for key, val in BEWERTUNGSKATEGORIEN.items()
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
@router.get("")
|
||
|
|
def list_fraktionen(conn=Depends(_db)):
|
||
|
|
"""List all parties with Antrag counts."""
|
||
|
|
rows = conn.execute("""
|
||
|
|
SELECT p.id, p.kuerzel, p.name, p.farbe, COUNT(a.vorlage_id) as anzahl
|
||
|
|
FROM parteien p
|
||
|
|
LEFT JOIN antragsteller a ON p.id = a.partei_id
|
||
|
|
GROUP BY p.id
|
||
|
|
HAVING anzahl > 0
|
||
|
|
ORDER BY anzahl DESC
|
||
|
|
""").fetchall()
|
||
|
|
return [dict(r) for r in rows]
|
||
|
|
|
||
|
|
|
||
|
|
@router.get("/{kuerzel}/dashboard")
|
||
|
|
def fraktion_dashboard(
|
||
|
|
kuerzel: str,
|
||
|
|
jahr: Optional[int] = None,
|
||
|
|
conn=Depends(_db),
|
||
|
|
):
|
||
|
|
"""Dashboard for a single Fraktion with Umsetzungsanalyse."""
|
||
|
|
# Find party
|
||
|
|
partei = conn.execute(
|
||
|
|
"SELECT id, kuerzel, name, farbe FROM parteien WHERE kuerzel = ?", (kuerzel,)
|
||
|
|
).fetchone()
|
||
|
|
if not partei:
|
||
|
|
raise HTTPException(404, f"Fraktion '{kuerzel}' nicht gefunden")
|
||
|
|
|
||
|
|
# Base query: all Vorlagen by this party
|
||
|
|
jahr_filter = ""
|
||
|
|
params = [partei["id"]]
|
||
|
|
if jahr:
|
||
|
|
jahr_filter = "AND strftime('%Y', v.datum_eingang) = ?"
|
||
|
|
params.append(str(jahr))
|
||
|
|
|
||
|
|
# Total Anträge
|
||
|
|
total = conn.execute(f"""
|
||
|
|
SELECT COUNT(DISTINCT v.id) as c
|
||
|
|
FROM vorlagen v
|
||
|
|
JOIN antragsteller a ON a.vorlage_id = v.id
|
||
|
|
WHERE a.partei_id = ? {jahr_filter}
|
||
|
|
""", params).fetchone()["c"]
|
||
|
|
|
||
|
|
# With Ketten-Match results
|
||
|
|
umsetzung_rows = conn.execute(f"""
|
||
|
|
SELECT
|
||
|
|
json_extract(kb.anmerkungen, '$.bewertung') as bewertung,
|
||
|
|
COUNT(*) as anzahl,
|
||
|
|
ROUND(AVG(kb.score), 2) as avg_score
|
||
|
|
FROM vorlagen v
|
||
|
|
JOIN antragsteller a ON a.vorlage_id = v.id
|
||
|
|
JOIN ki_bewertungen kb ON kb.vorlage_id = v.id AND kb.typ = 'umsetzung_match'
|
||
|
|
WHERE a.partei_id = ? {jahr_filter}
|
||
|
|
GROUP BY bewertung
|
||
|
|
ORDER BY anzahl DESC
|
||
|
|
""", params).fetchall()
|
||
|
|
|
||
|
|
umsetzung = [dict(r) for r in umsetzung_rows]
|
||
|
|
bewertet = sum(r["anzahl"] for r in umsetzung_rows)
|
||
|
|
|
||
|
|
# Top Anträge with scores
|
||
|
|
antraege = conn.execute(f"""
|
||
|
|
SELECT v.id, v.aktenzeichen, v.betreff, v.typ, v.datum_eingang,
|
||
|
|
kb.score as umsetzung_score,
|
||
|
|
json_extract(kb.anmerkungen, '$.bewertung') as umsetzung_bewertung,
|
||
|
|
kb.begruendung as umsetzung_begruendung
|
||
|
|
FROM vorlagen v
|
||
|
|
JOIN antragsteller a ON a.vorlage_id = v.id
|
||
|
|
LEFT JOIN ki_bewertungen kb ON kb.vorlage_id = v.id AND kb.typ = 'umsetzung_match'
|
||
|
|
WHERE a.partei_id = ? {jahr_filter}
|
||
|
|
ORDER BY v.datum_eingang DESC
|
||
|
|
LIMIT 200
|
||
|
|
""", params).fetchall()
|
||
|
|
|
||
|
|
# Jahre für Filter
|
||
|
|
jahre = conn.execute("""
|
||
|
|
SELECT DISTINCT strftime('%Y', v.datum_eingang) as j
|
||
|
|
FROM vorlagen v
|
||
|
|
JOIN antragsteller a ON a.vorlage_id = v.id
|
||
|
|
WHERE a.partei_id = ? AND v.datum_eingang IS NOT NULL
|
||
|
|
ORDER BY j DESC
|
||
|
|
""", [partei["id"]]).fetchall()
|
||
|
|
|
||
|
|
return {
|
||
|
|
"partei": dict(partei),
|
||
|
|
"total_antraege": total,
|
||
|
|
"bewertet": bewertet,
|
||
|
|
"umsetzung": umsetzung,
|
||
|
|
"antraege": [dict(r) for r in antraege],
|
||
|
|
"jahre": [r["j"] for r in jahre],
|
||
|
|
}
|