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>
267 lines
8.7 KiB
Python
267 lines
8.7 KiB
Python
"""
|
|
Classroom API - Events & Routines Routes
|
|
|
|
School year events, recurring routines endpoints (Phase 8).
|
|
"""
|
|
|
|
from typing import Optional
|
|
from datetime import datetime
|
|
import logging
|
|
|
|
from fastapi import APIRouter, HTTPException, Query, Depends
|
|
|
|
from ..models import (
|
|
CreateEventRequest,
|
|
EventResponse,
|
|
CreateRoutineRequest,
|
|
RoutineResponse,
|
|
)
|
|
from ..services.persistence import (
|
|
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
|
|
|
|
|
|
# === Events Endpoints ===
|
|
|
|
@router.get("/v1/events")
|
|
async def get_events(
|
|
teacher_id: str = Query(...),
|
|
status: Optional[str] = None,
|
|
event_type: Optional[str] = None,
|
|
limit: int = 50,
|
|
db=Depends(get_db)
|
|
):
|
|
"""Holt Events eines Lehrers."""
|
|
if not DB_ENABLED or not db:
|
|
return {"events": [], "count": 0}
|
|
|
|
try:
|
|
from classroom_engine.repository import SchoolyearEventRepository
|
|
repo = SchoolyearEventRepository(db)
|
|
events = repo.get_by_teacher(teacher_id, status=status, event_type=event_type, limit=limit)
|
|
return {
|
|
"events": [repo.to_dict(e) for e in events],
|
|
"count": len(events),
|
|
}
|
|
except Exception as e:
|
|
logger.error(f"Failed to get events: {e}")
|
|
raise HTTPException(status_code=500, detail=f"Fehler: {e}")
|
|
|
|
|
|
@router.get("/v1/events/upcoming")
|
|
async def get_upcoming_events(
|
|
teacher_id: str = Query(...),
|
|
days: int = 30,
|
|
limit: int = 10,
|
|
db=Depends(get_db)
|
|
):
|
|
"""Holt anstehende Events der naechsten X Tage."""
|
|
if not DB_ENABLED or not db:
|
|
return {"events": [], "count": 0}
|
|
|
|
try:
|
|
from classroom_engine.repository import SchoolyearEventRepository
|
|
repo = SchoolyearEventRepository(db)
|
|
events = repo.get_upcoming(teacher_id, days=days, limit=limit)
|
|
return {
|
|
"events": [repo.to_dict(e) for e in events],
|
|
"count": len(events),
|
|
}
|
|
except Exception as e:
|
|
logger.error(f"Failed to get upcoming events: {e}")
|
|
raise HTTPException(status_code=500, detail=f"Fehler: {e}")
|
|
|
|
|
|
@router.post("/v1/events", response_model=EventResponse)
|
|
async def create_event(
|
|
teacher_id: str,
|
|
request: CreateEventRequest,
|
|
db=Depends(get_db)
|
|
):
|
|
"""Erstellt ein neues Schuljahr-Event."""
|
|
if not DB_ENABLED or not db:
|
|
raise HTTPException(status_code=503, detail="Datenbank nicht verfuegbar")
|
|
|
|
try:
|
|
from classroom_engine.repository import SchoolyearEventRepository
|
|
repo = SchoolyearEventRepository(db)
|
|
start_date = datetime.fromisoformat(request.start_date.replace('Z', '+00:00'))
|
|
end_date = None
|
|
if request.end_date:
|
|
end_date = datetime.fromisoformat(request.end_date.replace('Z', '+00:00'))
|
|
|
|
event = repo.create(
|
|
teacher_id=teacher_id,
|
|
title=request.title,
|
|
event_type=request.event_type,
|
|
start_date=start_date,
|
|
end_date=end_date,
|
|
class_id=request.class_id,
|
|
subject=request.subject,
|
|
description=request.description,
|
|
needs_preparation=request.needs_preparation,
|
|
reminder_days_before=request.reminder_days_before,
|
|
)
|
|
|
|
return EventResponse(
|
|
id=event.id,
|
|
teacher_id=event.teacher_id,
|
|
event_type=event.event_type.value,
|
|
title=event.title,
|
|
description=event.description,
|
|
start_date=event.start_date.isoformat(),
|
|
end_date=event.end_date.isoformat() if event.end_date else None,
|
|
class_id=event.class_id,
|
|
subject=event.subject,
|
|
status=event.status.value,
|
|
needs_preparation=event.needs_preparation,
|
|
preparation_done=event.preparation_done,
|
|
reminder_days_before=event.reminder_days_before,
|
|
)
|
|
except Exception as e:
|
|
logger.error(f"Failed to create event: {e}")
|
|
raise HTTPException(status_code=500, detail=f"Fehler: {e}")
|
|
|
|
|
|
@router.delete("/v1/events/{event_id}")
|
|
async def delete_event(event_id: str, db=Depends(get_db)):
|
|
"""Loescht ein Event."""
|
|
if not DB_ENABLED or not db:
|
|
raise HTTPException(status_code=503, detail="Datenbank nicht verfuegbar")
|
|
|
|
try:
|
|
from classroom_engine.repository import SchoolyearEventRepository
|
|
repo = SchoolyearEventRepository(db)
|
|
if repo.delete(event_id):
|
|
return {"success": True, "deleted_id": event_id}
|
|
raise HTTPException(status_code=404, detail="Event nicht gefunden")
|
|
except HTTPException:
|
|
raise
|
|
except Exception as e:
|
|
logger.error(f"Failed to delete event: {e}")
|
|
raise HTTPException(status_code=500, detail=f"Fehler: {e}")
|
|
|
|
|
|
# === Routines Endpoints ===
|
|
|
|
@router.get("/v1/routines")
|
|
async def get_routines(
|
|
teacher_id: str = Query(...),
|
|
is_active: bool = True,
|
|
routine_type: Optional[str] = None,
|
|
db=Depends(get_db)
|
|
):
|
|
"""Holt Routinen eines Lehrers."""
|
|
if not DB_ENABLED or not db:
|
|
return {"routines": [], "count": 0}
|
|
|
|
try:
|
|
from classroom_engine.repository import RecurringRoutineRepository
|
|
repo = RecurringRoutineRepository(db)
|
|
routines = repo.get_by_teacher(teacher_id, is_active=is_active, routine_type=routine_type)
|
|
return {
|
|
"routines": [repo.to_dict(r) for r in routines],
|
|
"count": len(routines),
|
|
}
|
|
except Exception as e:
|
|
logger.error(f"Failed to get routines: {e}")
|
|
raise HTTPException(status_code=500, detail=f"Fehler: {e}")
|
|
|
|
|
|
@router.get("/v1/routines/today")
|
|
async def get_today_routines(teacher_id: str = Query(...), db=Depends(get_db)):
|
|
"""Holt Routinen die heute stattfinden."""
|
|
if not DB_ENABLED or not db:
|
|
return {"routines": [], "count": 0}
|
|
|
|
try:
|
|
from classroom_engine.repository import RecurringRoutineRepository
|
|
repo = RecurringRoutineRepository(db)
|
|
routines = repo.get_today(teacher_id)
|
|
return {
|
|
"routines": [repo.to_dict(r) for r in routines],
|
|
"count": len(routines),
|
|
}
|
|
except Exception as e:
|
|
logger.error(f"Failed to get today's routines: {e}")
|
|
raise HTTPException(status_code=500, detail=f"Fehler: {e}")
|
|
|
|
|
|
@router.post("/v1/routines", response_model=RoutineResponse)
|
|
async def create_routine(
|
|
teacher_id: str,
|
|
request: CreateRoutineRequest,
|
|
db=Depends(get_db)
|
|
):
|
|
"""Erstellt eine neue wiederkehrende Routine."""
|
|
if not DB_ENABLED or not db:
|
|
raise HTTPException(status_code=503, detail="Datenbank nicht verfuegbar")
|
|
|
|
try:
|
|
from classroom_engine.repository import RecurringRoutineRepository
|
|
repo = RecurringRoutineRepository(db)
|
|
routine = repo.create(
|
|
teacher_id=teacher_id,
|
|
title=request.title,
|
|
routine_type=request.routine_type,
|
|
recurrence_pattern=request.recurrence_pattern,
|
|
day_of_week=request.day_of_week,
|
|
day_of_month=request.day_of_month,
|
|
time_of_day=request.time_of_day,
|
|
duration_minutes=request.duration_minutes,
|
|
description=request.description,
|
|
)
|
|
|
|
return RoutineResponse(
|
|
id=routine.id,
|
|
teacher_id=routine.teacher_id,
|
|
routine_type=routine.routine_type.value,
|
|
title=routine.title,
|
|
description=routine.description,
|
|
recurrence_pattern=routine.recurrence_pattern.value,
|
|
day_of_week=routine.day_of_week,
|
|
day_of_month=routine.day_of_month,
|
|
time_of_day=routine.time_of_day.isoformat() if routine.time_of_day else None,
|
|
duration_minutes=routine.duration_minutes,
|
|
is_active=routine.is_active,
|
|
)
|
|
except Exception as e:
|
|
logger.error(f"Failed to create routine: {e}")
|
|
raise HTTPException(status_code=500, detail=f"Fehler: {e}")
|
|
|
|
|
|
@router.delete("/v1/routines/{routine_id}")
|
|
async def delete_routine(routine_id: str, db=Depends(get_db)):
|
|
"""Loescht eine Routine."""
|
|
if not DB_ENABLED or not db:
|
|
raise HTTPException(status_code=503, detail="Datenbank nicht verfuegbar")
|
|
|
|
try:
|
|
from classroom_engine.repository import RecurringRoutineRepository
|
|
repo = RecurringRoutineRepository(db)
|
|
if repo.delete(routine_id):
|
|
return {"success": True, "deleted_id": routine_id}
|
|
raise HTTPException(status_code=404, detail="Routine nicht gefunden")
|
|
except HTTPException:
|
|
raise
|
|
except Exception as e:
|
|
logger.error(f"Failed to delete routine: {e}")
|
|
raise HTTPException(status_code=500, detail=f"Fehler: {e}")
|