""" Classroom API - Context Core Routes Teacher context, onboarding endpoints (Phase 8). """ from typing import Dict, Any from datetime import datetime import logging from fastapi import APIRouter, HTTPException, Query, Depends from classroom_engine import ( FEDERAL_STATES, SCHOOL_TYPES, MacroPhaseEnum, ) from ..models import ( TeacherContextResponse, SchoolInfo, SchoolYearInfo, MacroPhaseInfo, CoreCounts, ContextFlags, UpdateContextRequest, ) from ..services.persistence import ( init_db_if_needed, DB_ENABLED, SessionLocal, ) logger = logging.getLogger(__name__) router = APIRouter(tags=["Context"]) def get_db(): """Database session dependency.""" if DB_ENABLED and SessionLocal: db = SessionLocal() try: yield db finally: db.close() else: yield None def _get_macro_phase_label(phase) -> str: """Gibt den Anzeigenamen einer Makro-Phase zurueck.""" labels = { "onboarding": "Einrichtung", "schuljahresstart": "Schuljahresstart", "unterrichtsaufbau": "Unterrichtsaufbau", "leistungsphase_1": "Leistungsphase 1", "halbjahresabschluss": "Halbjahresabschluss", "leistungsphase_2": "Leistungsphase 2", "jahresabschluss": "Jahresabschluss", } phase_value = phase.value if hasattr(phase, 'value') else str(phase) return labels.get(phase_value, phase_value) # === Context Endpoints === @router.get("/v1/context", response_model=TeacherContextResponse) async def get_teacher_context( teacher_id: str = Query(..., description="Teacher ID"), db=Depends(get_db) ): """ Liefert den aktuellen Makro-Kontext eines Lehrers. Der Kontext beinhaltet: - Schul-Informationen (Bundesland, Schulart) - Schuljahr-Daten (aktuelles Jahr, Woche) - Makro-Phase (ONBOARDING bis JAHRESABSCHLUSS) - Zaehler (Klassen, geplante Klausuren, etc.) - Status-Flags (Onboarding abgeschlossen, etc.) """ if DB_ENABLED and db: try: from classroom_engine.repository import TeacherContextRepository, SchoolyearEventRepository repo = TeacherContextRepository(db) context = repo.get_or_create(teacher_id) # Zaehler berechnen event_repo = SchoolyearEventRepository(db) upcoming_exams = event_repo.get_upcoming(teacher_id, days=30) exams_count = len([e for e in upcoming_exams if e.event_type.value == "exam"]) return TeacherContextResponse( schema_version="1.0", teacher_id=teacher_id, school=SchoolInfo( federal_state=context.federal_state or "BY", federal_state_name=FEDERAL_STATES.get(context.federal_state, ""), school_type=context.school_type or "gymnasium", school_type_name=SCHOOL_TYPES.get(context.school_type, ""), ), school_year=SchoolYearInfo( id=context.schoolyear or "2024-2025", start=context.schoolyear_start.isoformat() if context.schoolyear_start else None, current_week=context.current_week or 1, ), macro_phase=MacroPhaseInfo( id=context.macro_phase.value, label=_get_macro_phase_label(context.macro_phase), confidence=1.0, ), core_counts=CoreCounts( classes=1 if context.has_classes else 0, exams_scheduled=exams_count, corrections_pending=0, ), flags=ContextFlags( onboarding_completed=context.onboarding_completed, has_classes=context.has_classes, has_schedule=context.has_schedule, is_exam_period=context.is_exam_period, is_before_holidays=context.is_before_holidays, ), ) except Exception as e: logger.error(f"Failed to get teacher context: {e}") raise HTTPException(status_code=500, detail=f"Fehler beim Laden des Kontexts: {e}") # Fallback ohne DB return TeacherContextResponse( schema_version="1.0", teacher_id=teacher_id, school=SchoolInfo( federal_state="BY", federal_state_name="Bayern", school_type="gymnasium", school_type_name="Gymnasium", ), school_year=SchoolYearInfo( id="2024-2025", start=None, current_week=1, ), macro_phase=MacroPhaseInfo( id="onboarding", label="Einrichtung", confidence=1.0, ), core_counts=CoreCounts(), flags=ContextFlags(), ) @router.put("/v1/context", response_model=TeacherContextResponse) async def update_teacher_context( teacher_id: str, request: UpdateContextRequest, db=Depends(get_db) ): """ Aktualisiert den Kontext eines Lehrers. """ if not DB_ENABLED or not db: raise HTTPException(status_code=503, detail="Datenbank nicht verfuegbar") try: from classroom_engine.repository import TeacherContextRepository repo = TeacherContextRepository(db) # Validierung if request.federal_state and request.federal_state not in FEDERAL_STATES: raise HTTPException(status_code=400, detail=f"Ungueltiges Bundesland: {request.federal_state}") if request.school_type and request.school_type not in SCHOOL_TYPES: raise HTTPException(status_code=400, detail=f"Ungueltige Schulart: {request.school_type}") # Parse datetime if provided schoolyear_start = None if request.schoolyear_start: schoolyear_start = datetime.fromisoformat(request.schoolyear_start.replace('Z', '+00:00')) repo.update_context( teacher_id=teacher_id, federal_state=request.federal_state, school_type=request.school_type, schoolyear=request.schoolyear, schoolyear_start=schoolyear_start, macro_phase=request.macro_phase, current_week=request.current_week, ) return await get_teacher_context(teacher_id, db) except HTTPException: raise except Exception as e: logger.error(f"Failed to update teacher context: {e}") raise HTTPException(status_code=500, detail=f"Fehler beim Aktualisieren: {e}") @router.post("/v1/context/complete-onboarding") async def complete_onboarding( teacher_id: str = Query(...), db=Depends(get_db) ): """Markiert das Onboarding als abgeschlossen.""" if not DB_ENABLED or not db: return {"success": True, "macro_phase": "schuljahresstart", "note": "DB not available"} try: from classroom_engine.repository import TeacherContextRepository repo = TeacherContextRepository(db) context = repo.complete_onboarding(teacher_id) return { "success": True, "macro_phase": context.macro_phase.value, "teacher_id": teacher_id, } except Exception as e: logger.error(f"Failed to complete onboarding: {e}") raise HTTPException(status_code=500, detail=f"Fehler: {e}") @router.post("/v1/context/reset-onboarding") async def reset_onboarding( teacher_id: str = Query(...), db=Depends(get_db) ): """Setzt das Onboarding zurueck (fuer Tests).""" if not DB_ENABLED or not db: return {"success": True, "macro_phase": "onboarding", "note": "DB not available"} try: from classroom_engine.repository import TeacherContextRepository repo = TeacherContextRepository(db) context = repo.get_or_create(teacher_id) context.onboarding_completed = False context.macro_phase = MacroPhaseEnum.ONBOARDING db.commit() db.refresh(context) return { "success": True, "macro_phase": "onboarding", "teacher_id": teacher_id, } except Exception as e: logger.error(f"Failed to reset onboarding: {e}") raise HTTPException(status_code=500, detail=f"Fehler: {e}")