""" Recording API - Meeting Minutes Routes. Generate, retrieve, and export KI-based meeting minutes. """ from datetime import datetime from typing import Optional from fastapi import APIRouter, HTTPException, Query from fastapi.responses import PlainTextResponse, HTMLResponse from recording_helpers import ( _recordings_store, _transcriptions_store, _minutes_store, log_audit, ) router = APIRouter(tags=["Recordings"]) # ========================================== # MEETING MINUTES ENDPOINTS # ========================================== @router.post("/{recording_id}/minutes") async def generate_meeting_minutes( recording_id: str, title: Optional[str] = Query(None, description="Meeting-Titel"), model: str = Query("breakpilot-teacher-8b", description="LLM Modell") ): """ Generiert KI-basierte Meeting Minutes aus der Transkription. Nutzt das LLM Gateway (Ollama/vLLM) fuer lokale Verarbeitung. """ from meeting_minutes_generator import get_minutes_generator, MeetingMinutes # Check recording exists recording = _recordings_store.get(recording_id) if not recording: raise HTTPException(status_code=404, detail="Recording not found") # Check transcription exists and is completed transcription = next( (t for t in _transcriptions_store.values() if t["recording_id"] == recording_id), None ) if not transcription: raise HTTPException(status_code=400, detail="No transcription found. Please transcribe first.") if transcription["status"] != "completed": raise HTTPException( status_code=400, detail=f"Transcription not ready. Status: {transcription['status']}" ) # Check if minutes already exist existing = _minutes_store.get(recording_id) if existing and existing.get("status") == "completed": # Return existing minutes return existing # Get transcript text transcript_text = transcription.get("full_text", "") if not transcript_text: raise HTTPException(status_code=400, detail="Transcription has no text content") # Generate meeting minutes generator = get_minutes_generator() try: minutes = await generator.generate( transcript=transcript_text, recording_id=recording_id, transcription_id=transcription["id"], title=title, date=recording.get("recorded_at", "")[:10] if recording.get("recorded_at") else None, duration_minutes=recording.get("duration_seconds", 0) // 60 if recording.get("duration_seconds") else None, participant_count=recording.get("participant_count", 0), model=model ) # Store minutes minutes_dict = minutes.model_dump() minutes_dict["generated_at"] = minutes.generated_at.isoformat() _minutes_store[recording_id] = minutes_dict # Log action log_audit( action="minutes_generated", recording_id=recording_id, metadata={"model": model, "generation_time": minutes.generation_time_seconds} ) return minutes_dict except Exception as e: raise HTTPException(status_code=500, detail=f"Minutes generation failed: {str(e)}") @router.get("/{recording_id}/minutes") async def get_meeting_minutes(recording_id: str): """ Ruft generierte Meeting Minutes ab. """ minutes = _minutes_store.get(recording_id) if not minutes: raise HTTPException(status_code=404, detail="No meeting minutes found. Generate them first with POST.") return minutes def _load_minutes(recording_id: str): """Load and convert stored minutes dict back to MeetingMinutes.""" from meeting_minutes_generator import MeetingMinutes minutes_dict = _minutes_store.get(recording_id) if not minutes_dict: raise HTTPException(status_code=404, detail="No meeting minutes found") minutes_dict_copy = minutes_dict.copy() if isinstance(minutes_dict_copy.get("generated_at"), str): minutes_dict_copy["generated_at"] = datetime.fromisoformat(minutes_dict_copy["generated_at"]) return MeetingMinutes(**minutes_dict_copy) @router.get("/{recording_id}/minutes/markdown") async def get_minutes_markdown(recording_id: str): """ Exportiert Meeting Minutes als Markdown. """ from meeting_minutes_generator import minutes_to_markdown minutes = _load_minutes(recording_id) markdown = minutes_to_markdown(minutes) return PlainTextResponse( content=markdown, media_type="text/markdown", headers={"Content-Disposition": f"attachment; filename=protokoll_{recording_id}.md"} ) @router.get("/{recording_id}/minutes/html") async def get_minutes_html(recording_id: str): """ Exportiert Meeting Minutes als HTML. """ from meeting_minutes_generator import minutes_to_html minutes = _load_minutes(recording_id) html = minutes_to_html(minutes) return HTMLResponse(content=html) @router.get("/{recording_id}/minutes/pdf") async def get_minutes_pdf(recording_id: str): """ Exportiert Meeting Minutes als PDF. Benoetigt WeasyPrint (pip install weasyprint). """ from meeting_minutes_generator import minutes_to_html minutes = _load_minutes(recording_id) html = minutes_to_html(minutes) try: from weasyprint import HTML from fastapi.responses import Response pdf_bytes = HTML(string=html).write_pdf() return Response( content=pdf_bytes, media_type="application/pdf", headers={"Content-Disposition": f"attachment; filename=protokoll_{recording_id}.pdf"} ) except ImportError: raise HTTPException( status_code=501, detail="PDF export not available. Install weasyprint: pip install weasyprint" )