"""API routes for Ketten (chains).""" from fastapi import APIRouter, Depends, HTTPException, Query from tracker.api.models import ( KetteDetail, KetteKurz, KettenGliedOut, PaginatedKetten, ParteiOut, VorlageKurz, ) from tracker.core.graph import get_kette_graph from tracker.db.session import get_connection router = APIRouter(prefix="/ketten", tags=["Ketten"]) def _db(): conn = get_connection() try: yield conn finally: conn.close() @router.get("", response_model=PaginatedKetten) def list_ketten( page: int = Query(1, ge=1), page_size: int = Query(50, ge=1, le=200), status: str | None = None, typ: str | None = None, suche: str | None = None, conn=Depends(_db), ): """List Ketten with optional filters.""" where_clauses = [] params: list = [] if status: where_clauses.append("k.status = ?") params.append(status) if typ: where_clauses.append("k.typ = ?") params.append(typ) if suche: where_clauses.append("k.thema LIKE ?") params.append(f"%{suche}%") where_sql = ("WHERE " + " AND ".join(where_clauses)) if where_clauses else "" total = conn.execute( f"SELECT COUNT(*) as cnt FROM ketten k {where_sql}", params ).fetchone()["cnt"] offset = (page - 1) * page_size rows = conn.execute( f"""SELECT k.id, k.typ, k.thema, k.status, k.status_seit, k.letzte_aktivitaet, k.vertagungen_count, k.ursprung_id, v.aktenzeichen, v.typ as v_typ, v.betreff, v.datum_eingang, v.ist_verwaltungsvorlage, (SELECT COUNT(*) FROM ketten_glieder kg WHERE kg.kette_id = k.id) as glieder_count FROM ketten k LEFT JOIN vorlagen v ON k.ursprung_id = v.id {where_sql} ORDER BY k.letzte_aktivitaet DESC NULLS LAST, k.id DESC LIMIT ? OFFSET ?""", params + [page_size, offset], ).fetchall() items = [ KetteKurz( id=r["id"], ursprung=VorlageKurz( id=r["ursprung_id"], aktenzeichen=r["aktenzeichen"], typ=r["v_typ"], betreff=r["betreff"], datum_eingang=r["datum_eingang"], ist_verwaltungsvorlage=bool(r["ist_verwaltungsvorlage"]), ) if r["ursprung_id"] else None, typ=r["typ"], thema=r["thema"], status=r["status"], status_seit=r["status_seit"], letzte_aktivitaet=r["letzte_aktivitaet"], vertagungen_count=r["vertagungen_count"], glieder_count=r["glieder_count"], ) for r in rows ] return PaginatedKetten(items=items, total=total, page=page, page_size=page_size) @router.get("/{kette_id}", response_model=KetteDetail) def get_kette(kette_id: int, conn=Depends(_db)): """Get a single Kette with all Glieder.""" row = conn.execute( """SELECT k.id, k.typ, k.thema, k.status, k.status_seit, k.letzte_aktivitaet, k.vertagungen_count, k.ursprung_id, v.aktenzeichen, v.typ as v_typ, v.betreff, v.datum_eingang, v.ist_verwaltungsvorlage FROM ketten k LEFT JOIN vorlagen v ON k.ursprung_id = v.id WHERE k.id = ?""", (kette_id,), ).fetchone() if not row: raise HTTPException(status_code=404, detail="Kette nicht gefunden") # Get Glieder glieder_rows = conn.execute( """SELECT kg.position, kg.rolle, v.id, v.aktenzeichen, v.typ, v.betreff, v.datum_eingang, v.ist_verwaltungsvorlage FROM ketten_glieder kg JOIN vorlagen v ON kg.vorlage_id = v.id WHERE kg.kette_id = ? ORDER BY kg.position""", (kette_id,), ).fetchall() glieder = [ KettenGliedOut( vorlage=VorlageKurz( id=g["id"], aktenzeichen=g["aktenzeichen"], typ=g["typ"], betreff=g["betreff"], datum_eingang=g["datum_eingang"], ist_verwaltungsvorlage=bool(g["ist_verwaltungsvorlage"]), ), position=g["position"], rolle=g["rolle"], ) for g in glieder_rows ] # Antragsteller des Ursprungs antragsteller = [] if row["ursprung_id"]: antragsteller_rows = conn.execute(""" SELECT p.id, p.kuerzel, p.name, p.farbe FROM antragsteller a JOIN parteien p ON a.partei_id = p.id WHERE a.vorlage_id = ? """, (row["ursprung_id"],)).fetchall() antragsteller = [ParteiOut(**dict(a)) for a in antragsteller_rows] # Graph/Perlenschnur data graph = get_kette_graph(conn, kette_id) return KetteDetail( id=row["id"], ursprung=VorlageKurz( id=row["ursprung_id"], aktenzeichen=row["aktenzeichen"], typ=row["v_typ"], betreff=row["betreff"], datum_eingang=row["datum_eingang"], ist_verwaltungsvorlage=bool(row["ist_verwaltungsvorlage"]), ) if row["ursprung_id"] else None, typ=row["typ"], thema=row["thema"], status=row["status"], status_seit=row["status_seit"], letzte_aktivitaet=row["letzte_aktivitaet"], vertagungen_count=row["vertagungen_count"], glieder=glieder, antragsteller=antragsteller, graph=graph, )