Files
breakpilot-lehrer/backend-lehrer/classroom/routes/context_core.py
Benjamin Admin 34da9f4cda [split-required] Split 700-870 LOC files across all services
backend-lehrer (11 files):
- llm_gateway/routes/schools.py (867 → 5), recording_api.py (848 → 6)
- messenger_api.py (840 → 5), print_generator.py (824 → 5)
- unit_analytics_api.py (751 → 5), classroom/routes/context.py (726 → 4)
- llm_gateway/routes/edu_search_seeds.py (710 → 4)

klausur-service (12 files):
- ocr_labeling_api.py (845 → 4), metrics_db.py (833 → 4)
- legal_corpus_api.py (790 → 4), page_crop.py (758 → 3)
- mail/ai_service.py (747 → 4), github_crawler.py (767 → 3)
- trocr_service.py (730 → 4), full_compliance_pipeline.py (723 → 4)
- dsfa_rag_api.py (715 → 4), ocr_pipeline_auto.py (705 → 4)

website (6 pages):
- audit-checklist (867 → 8), content (806 → 6)
- screen-flow (790 → 4), scraper (789 → 5)
- zeugnisse (776 → 5), modules (745 → 4)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-25 08:01:18 +02:00

248 lines
8.2 KiB
Python

"""
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}")