Files
breakpilot-lehrer/backend-lehrer/state_engine_models.py
Benjamin Admin bd4b956e3c [split-required] Split final 43 files (500-668 LOC) to complete refactoring
klausur-service (11 files):
- cv_gutter_repair, ocr_pipeline_regression, upload_api
- ocr_pipeline_sessions, smart_spell, nru_worksheet_generator
- ocr_pipeline_overlays, mail/aggregator, zeugnis_api
- cv_syllable_detect, self_rag

backend-lehrer (17 files):
- classroom_engine/suggestions, generators/quiz_generator
- worksheets_api, llm_gateway/comparison, state_engine_api
- classroom/models (→ 4 submodules), services/file_processor
- alerts_agent/api/wizard+digests+routes, content_generators/pdf
- classroom/routes/sessions, llm_gateway/inference
- classroom_engine/analytics, auth/keycloak_auth
- alerts_agent/processing/rule_engine, ai_processor/print_versions

agent-core (5 files):
- brain/memory_store, brain/knowledge_graph, brain/context_manager
- orchestrator/supervisor, sessions/session_manager

admin-lehrer (5 components):
- GridOverlay, StepGridReview, DevOpsPipelineSidebar
- DataFlowDiagram, sbom/wizard/page

website (2 files):
- DependencyMap, lehrer/abitur-archiv

Other: nibis_ingestion, grid_detection_service, export-doclayout-onnx

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

144 lines
4.3 KiB
Python

"""
State Engine API - Pydantic Models und Helper Functions.
"""
import uuid
from datetime import datetime, timedelta
from typing import Dict, Any, List, Optional
from pydantic import BaseModel, Field
from state_engine import (
SchoolYearPhase,
ClassSummary,
Event,
TeacherContext,
TeacherStats,
get_phase_info,
)
# ============================================================================
# In-Memory Storage (später durch DB ersetzen)
# ============================================================================
_teacher_contexts: Dict[str, TeacherContext] = {}
_milestones: Dict[str, List[str]] = {} # teacher_id -> milestones
# ============================================================================
# Pydantic Models
# ============================================================================
class MilestoneRequest(BaseModel):
"""Request zum Abschließen eines Meilensteins."""
milestone: str = Field(..., description="Name des Meilensteins")
class TransitionRequest(BaseModel):
"""Request für Phasen-Übergang."""
target_phase: str = Field(..., description="Zielphase")
class ContextResponse(BaseModel):
"""Response mit TeacherContext."""
context: Dict[str, Any]
phase_info: Dict[str, Any]
class SuggestionsResponse(BaseModel):
"""Response mit Vorschlägen."""
suggestions: List[Dict[str, Any]]
current_phase: str
phase_display_name: str
priority_counts: Dict[str, int]
class DashboardResponse(BaseModel):
"""Response mit Dashboard-Daten."""
context: Dict[str, Any]
suggestions: List[Dict[str, Any]]
stats: Dict[str, Any]
upcoming_events: List[Dict[str, Any]]
progress: Dict[str, Any]
phases: List[Dict[str, Any]]
# ============================================================================
# Helper Functions
# ============================================================================
def get_or_create_context(teacher_id: str) -> TeacherContext:
"""
Holt oder erstellt TeacherContext.
In Produktion würde dies aus der Datenbank geladen.
"""
if teacher_id not in _teacher_contexts:
now = datetime.now()
school_year_start = datetime(now.year if now.month >= 8 else now.year - 1, 8, 1)
weeks_since_start = (now - school_year_start).days // 7
month = now.month
if month in [8, 9]:
phase = SchoolYearPhase.SCHOOL_YEAR_START
elif month in [10, 11]:
phase = SchoolYearPhase.TEACHING_SETUP
elif month == 12:
phase = SchoolYearPhase.PERFORMANCE_1
elif month in [1, 2]:
phase = SchoolYearPhase.SEMESTER_END
elif month in [3, 4]:
phase = SchoolYearPhase.TEACHING_2
elif month in [5, 6]:
phase = SchoolYearPhase.PERFORMANCE_2
else:
phase = SchoolYearPhase.YEAR_END
_teacher_contexts[teacher_id] = TeacherContext(
teacher_id=teacher_id,
school_id=str(uuid.uuid4()),
school_year_id=str(uuid.uuid4()),
federal_state="niedersachsen",
school_type="gymnasium",
school_year_start=school_year_start,
current_phase=phase,
phase_entered_at=now - timedelta(days=7),
weeks_since_start=weeks_since_start,
days_in_phase=7,
classes=[],
total_students=0,
upcoming_events=[],
completed_milestones=_milestones.get(teacher_id, []),
pending_milestones=[],
stats=TeacherStats(),
)
return _teacher_contexts[teacher_id]
def update_context_from_services(ctx: TeacherContext) -> TeacherContext:
"""
Aktualisiert Kontext mit Daten aus anderen Services.
In Produktion würde dies von school-service, gradebook etc. laden.
"""
ctx.days_in_phase = (datetime.now() - ctx.phase_entered_at).days
ctx.completed_milestones = _milestones.get(ctx.teacher_id, [])
phase_info = get_phase_info(ctx.current_phase)
ctx.pending_milestones = [
m for m in phase_info.required_actions
if m not in ctx.completed_milestones
]
return ctx
def get_phase_display_name(phase: str) -> str:
"""Gibt Display-Name für Phase zurück."""
try:
return get_phase_info(SchoolYearPhase(phase)).display_name
except (ValueError, KeyError):
return phase