[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>
This commit is contained in:
247
backend-lehrer/classroom/routes/context_core.py
Normal file
247
backend-lehrer/classroom/routes/context_core.py
Normal file
@@ -0,0 +1,247 @@
|
||||
"""
|
||||
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}")
|
||||
Reference in New Issue
Block a user