""" API Routes fuer Alert Digests (Wochenzusammenfassungen). Endpoints: - GET /digests - Liste aller Digests fuer den User - GET /digests/{id} - Digest-Details - GET /digests/{id}/pdf - PDF-Download - POST /digests/generate - Digest manuell generieren - POST /digests/{id}/send-email - Digest per E-Mail versenden """ import io from datetime import datetime, timedelta from fastapi import APIRouter, Depends, HTTPException, Query from fastapi.responses import StreamingResponse from sqlalchemy.orm import Session as DBSession from ..db.database import get_db from ..db.models import ( AlertDigestDB, UserAlertSubscriptionDB, DigestStatusEnum ) from ..processing.digest_generator import DigestGenerator from .digests_models import ( DigestDetail, DigestListResponse, GenerateDigestRequest, GenerateDigestResponse, SendEmailRequest, SendEmailResponse, digest_to_list_item, digest_to_detail, ) from .digests_email import generate_pdf_from_html, send_digest_by_email router = APIRouter(prefix="/digests", tags=["digests"]) def get_user_id_from_request() -> str: """Extrahiert User-ID aus Request. TODO: JWT-Token auswerten.""" return "demo-user" # ============================================================================ # Endpoints # ============================================================================ @router.get("", response_model=DigestListResponse) async def list_digests( limit: int = Query(10, ge=1, le=50), offset: int = Query(0, ge=0), db: DBSession = Depends(get_db) ): """Liste alle Digests des aktuellen Users.""" user_id = get_user_id_from_request() query = db.query(AlertDigestDB).filter( AlertDigestDB.user_id == user_id ).order_by(AlertDigestDB.created_at.desc()) total = query.count() digests = query.offset(offset).limit(limit).all() return DigestListResponse( digests=[digest_to_list_item(d) for d in digests], total=total ) @router.get("/latest", response_model=DigestDetail) async def get_latest_digest(db: DBSession = Depends(get_db)): """Hole den neuesten Digest des Users.""" user_id = get_user_id_from_request() digest = db.query(AlertDigestDB).filter( AlertDigestDB.user_id == user_id ).order_by(AlertDigestDB.created_at.desc()).first() if not digest: raise HTTPException(status_code=404, detail="Kein Digest vorhanden") return digest_to_detail(digest) @router.get("/{digest_id}", response_model=DigestDetail) async def get_digest(digest_id: str, db: DBSession = Depends(get_db)): """Hole Details eines spezifischen Digests.""" user_id = get_user_id_from_request() digest = db.query(AlertDigestDB).filter( AlertDigestDB.id == digest_id, AlertDigestDB.user_id == user_id ).first() if not digest: raise HTTPException(status_code=404, detail="Digest nicht gefunden") return digest_to_detail(digest) @router.get("/{digest_id}/pdf") async def get_digest_pdf(digest_id: str, db: DBSession = Depends(get_db)): """Generiere und lade PDF-Version des Digests herunter.""" user_id = get_user_id_from_request() digest = db.query(AlertDigestDB).filter( AlertDigestDB.id == digest_id, AlertDigestDB.user_id == user_id ).first() if not digest: raise HTTPException(status_code=404, detail="Digest nicht gefunden") if not digest.summary_html: raise HTTPException(status_code=400, detail="Digest hat keinen Inhalt") try: pdf_bytes = await generate_pdf_from_html(digest.summary_html) except Exception as e: raise HTTPException(status_code=500, detail=f"PDF-Generierung fehlgeschlagen: {str(e)}") filename = f"wochenbericht_{digest.period_start.strftime('%Y%m%d')}_{digest.period_end.strftime('%Y%m%d')}.pdf" return StreamingResponse( io.BytesIO(pdf_bytes), media_type="application/pdf", headers={"Content-Disposition": f"attachment; filename={filename}"} ) @router.get("/latest/pdf") async def get_latest_digest_pdf(db: DBSession = Depends(get_db)): """PDF des neuesten Digests herunterladen.""" user_id = get_user_id_from_request() digest = db.query(AlertDigestDB).filter( AlertDigestDB.user_id == user_id ).order_by(AlertDigestDB.created_at.desc()).first() if not digest: raise HTTPException(status_code=404, detail="Kein Digest vorhanden") if not digest.summary_html: raise HTTPException(status_code=400, detail="Digest hat keinen Inhalt") try: pdf_bytes = await generate_pdf_from_html(digest.summary_html) except Exception as e: raise HTTPException(status_code=500, detail=f"PDF-Generierung fehlgeschlagen: {str(e)}") filename = f"wochenbericht_{digest.period_start.strftime('%Y%m%d')}_{digest.period_end.strftime('%Y%m%d')}.pdf" return StreamingResponse( io.BytesIO(pdf_bytes), media_type="application/pdf", headers={"Content-Disposition": f"attachment; filename={filename}"} ) @router.post("/generate", response_model=GenerateDigestResponse) async def generate_digest( request: GenerateDigestRequest = None, db: DBSession = Depends(get_db) ): """Generiere einen neuen Digest manuell.""" user_id = get_user_id_from_request() weeks_back = request.weeks_back if request else 1 now = datetime.utcnow() period_end = now - timedelta(days=now.weekday()) period_start = period_end - timedelta(weeks=weeks_back) existing = db.query(AlertDigestDB).filter( AlertDigestDB.user_id == user_id, AlertDigestDB.period_start >= period_start - timedelta(days=1), AlertDigestDB.period_end <= period_end + timedelta(days=1) ).first() if existing and not (request and request.force_regenerate): return GenerateDigestResponse( status="exists", digest_id=existing.id, message="Digest fuer diesen Zeitraum existiert bereits" ) generator = DigestGenerator(db) try: digest = await generator.generate_weekly_digest(user_id, weeks_back) if digest: return GenerateDigestResponse( status="success", digest_id=digest.id, message="Digest erfolgreich generiert" ) else: return GenerateDigestResponse( status="empty", digest_id=None, message="Keine Alerts fuer diesen Zeitraum vorhanden" ) except Exception as e: raise HTTPException(status_code=500, detail=f"Fehler bei Digest-Generierung: {str(e)}") @router.post("/{digest_id}/send-email", response_model=SendEmailResponse) async def send_digest_email( digest_id: str, request: SendEmailRequest = None, db: DBSession = Depends(get_db) ): """Versende Digest per E-Mail.""" user_id = get_user_id_from_request() digest = db.query(AlertDigestDB).filter( AlertDigestDB.id == digest_id, AlertDigestDB.user_id == user_id ).first() if not digest: raise HTTPException(status_code=404, detail="Digest nicht gefunden") email = None if request and request.email: email = request.email else: subscription = db.query(UserAlertSubscriptionDB).filter( UserAlertSubscriptionDB.id == digest.subscription_id ).first() if subscription: email = subscription.notification_email if not email: raise HTTPException(status_code=400, detail="Keine E-Mail-Adresse angegeben") try: await send_digest_by_email(digest, email) digest.status = DigestStatusEnum.SENT digest.sent_at = datetime.utcnow() db.commit() return SendEmailResponse( status="success", sent_to=email, message="E-Mail erfolgreich versendet" ) except Exception as e: digest.status = DigestStatusEnum.FAILED db.commit() raise HTTPException(status_code=500, detail=f"E-Mail-Versand fehlgeschlagen: {str(e)}")