""" Classroom API - Pydantic Models Alle Request- und Response-Models fuer die Classroom API. """ from typing import Dict, List, Optional, Any from pydantic import BaseModel, Field # === Session Models === class CreateSessionRequest(BaseModel): """Request zum Erstellen einer neuen Session.""" teacher_id: str = Field(..., description="ID des Lehrers") class_id: str = Field(..., description="ID der Klasse") subject: str = Field(..., description="Unterrichtsfach") topic: Optional[str] = Field(None, description="Thema der Stunde") phase_durations: Optional[Dict[str, int]] = Field( None, description="Optionale individuelle Phasendauern in Minuten" ) class NotesRequest(BaseModel): """Request zum Aktualisieren von Notizen.""" notes: str = Field("", description="Stundennotizen") homework: str = Field("", description="Hausaufgaben") class ExtendTimeRequest(BaseModel): """Request zum Verlaengern der aktuellen Phase (Feature f28).""" minutes: int = Field(5, ge=1, le=30, description="Zusaetzliche Minuten (1-30)") class PhaseInfo(BaseModel): """Informationen zu einer Phase.""" phase: str display_name: str icon: str duration_minutes: int is_completed: bool is_current: bool is_future: bool class TimerStatus(BaseModel): """Timer-Status einer Phase.""" remaining_seconds: int remaining_formatted: str total_seconds: int total_formatted: str elapsed_seconds: int elapsed_formatted: str percentage_remaining: int percentage_elapsed: int percentage: int = Field(description="Alias fuer percentage_remaining (Visual Timer)") warning: bool overtime: bool overtime_seconds: int overtime_formatted: Optional[str] is_paused: bool = Field(False, description="Ist der Timer pausiert?") class SuggestionItem(BaseModel): """Ein Aktivitaets-Vorschlag.""" id: str title: str description: str activity_type: str estimated_minutes: int icon: str content_url: Optional[str] class SessionResponse(BaseModel): """Vollstaendige Session-Response.""" session_id: str teacher_id: str class_id: str subject: str topic: Optional[str] current_phase: str phase_display_name: str phase_started_at: Optional[str] lesson_started_at: Optional[str] lesson_ended_at: Optional[str] timer: TimerStatus phases: List[PhaseInfo] phase_history: List[Dict[str, Any]] notes: str homework: str is_active: bool is_ended: bool is_paused: bool = Field(False, description="Ist die Stunde pausiert?") class SuggestionsResponse(BaseModel): """Response fuer Vorschlaege.""" suggestions: List[SuggestionItem] current_phase: str phase_display_name: str total_available: int class PhasesListResponse(BaseModel): """Liste aller verfuegbaren Phasen.""" phases: List[Dict[str, Any]] class ActiveSessionsResponse(BaseModel): """Liste aktiver Sessions.""" sessions: List[Dict[str, Any]] count: int # === Session History Models === class SessionHistoryItem(BaseModel): """Einzelner Eintrag in der Session-History.""" session_id: str teacher_id: str class_id: str subject: str topic: Optional[str] lesson_started_at: Optional[str] lesson_ended_at: Optional[str] total_duration_minutes: Optional[int] phases_completed: int notes: str homework: str class SessionHistoryResponse(BaseModel): """Response fuer Session-History.""" sessions: List[SessionHistoryItem] total_count: int limit: int offset: int # === Template Models === class TemplateCreate(BaseModel): """Request zum Erstellen einer Vorlage.""" name: str = Field(..., min_length=1, max_length=200, description="Name der Vorlage") description: str = Field("", max_length=1000, description="Beschreibung") subject: str = Field("", max_length=100, description="Fach") grade_level: str = Field("", max_length=50, description="Klassenstufe (z.B. '7', '10')") phase_durations: Optional[Dict[str, int]] = Field( None, description="Phasendauern in Minuten" ) default_topic: str = Field("", max_length=500, description="Vorausgefuelltes Thema") default_notes: str = Field("", description="Vorausgefuellte Notizen") is_public: bool = Field(False, description="Vorlage fuer alle sichtbar?") class TemplateUpdate(BaseModel): """Request zum Aktualisieren einer Vorlage.""" name: Optional[str] = Field(None, min_length=1, max_length=200) description: Optional[str] = Field(None, max_length=1000) subject: Optional[str] = Field(None, max_length=100) grade_level: Optional[str] = Field(None, max_length=50) phase_durations: Optional[Dict[str, int]] = None default_topic: Optional[str] = Field(None, max_length=500) default_notes: Optional[str] = None is_public: Optional[bool] = None class TemplateResponse(BaseModel): """Response fuer eine einzelne Vorlage.""" template_id: str teacher_id: str name: str description: str subject: str grade_level: str phase_durations: Dict[str, int] default_topic: str default_notes: str is_public: bool usage_count: int total_duration_minutes: int created_at: Optional[str] updated_at: Optional[str] is_system_template: bool = False class TemplateListResponse(BaseModel): """Response fuer Template-Liste.""" templates: List[TemplateResponse] total_count: int # === Homework Models === class CreateHomeworkRequest(BaseModel): """Request zum Erstellen einer Hausaufgabe.""" teacher_id: str class_id: str subject: str title: str = Field(..., max_length=300) description: str = "" session_id: Optional[str] = None due_date: Optional[str] = Field(None, description="ISO-Format Datum") class UpdateHomeworkRequest(BaseModel): """Request zum Aktualisieren einer Hausaufgabe.""" title: Optional[str] = Field(None, max_length=300) description: Optional[str] = None due_date: Optional[str] = Field(None, description="ISO-Format Datum") status: Optional[str] = Field(None, description="assigned, in_progress, completed") class HomeworkResponse(BaseModel): """Response fuer eine Hausaufgabe.""" homework_id: str teacher_id: str class_id: str subject: str title: str description: str session_id: Optional[str] due_date: Optional[str] status: str is_overdue: bool created_at: Optional[str] updated_at: Optional[str] class HomeworkListResponse(BaseModel): """Response fuer Liste von Hausaufgaben.""" homework: List[HomeworkResponse] total: int # === Material Models === class CreateMaterialRequest(BaseModel): """Request zum Erstellen eines Materials.""" teacher_id: str title: str = Field(..., max_length=300) material_type: str = Field("document", description="document, link, video, image, worksheet, presentation, other") url: Optional[str] = Field(None, max_length=2000) description: str = "" phase: Optional[str] = Field(None, description="einstieg, erarbeitung, sicherung, transfer, reflexion") subject: str = "" grade_level: str = "" tags: List[str] = [] is_public: bool = False session_id: Optional[str] = None class UpdateMaterialRequest(BaseModel): """Request zum Aktualisieren eines Materials.""" title: Optional[str] = Field(None, max_length=300) material_type: Optional[str] = None url: Optional[str] = Field(None, max_length=2000) description: Optional[str] = None phase: Optional[str] = None subject: Optional[str] = None grade_level: Optional[str] = None tags: Optional[List[str]] = None is_public: Optional[bool] = None class MaterialResponse(BaseModel): """Response fuer ein Material.""" material_id: str teacher_id: str title: str material_type: str url: Optional[str] description: str phase: Optional[str] subject: str grade_level: str tags: List[str] is_public: bool usage_count: int session_id: Optional[str] created_at: Optional[str] updated_at: Optional[str] class MaterialListResponse(BaseModel): """Response fuer Liste von Materialien.""" materials: List[MaterialResponse] total: int # === Analytics Models === class SessionSummaryResponse(BaseModel): """Response fuer Session-Summary.""" session_id: str teacher_id: str class_id: str subject: str topic: Optional[str] date: Optional[str] date_formatted: str total_duration_seconds: int total_duration_formatted: str planned_duration_seconds: int planned_duration_formatted: str phases_completed: int total_phases: int completion_percentage: int phase_statistics: List[Dict[str, Any]] total_overtime_seconds: int total_overtime_formatted: str phases_with_overtime: int total_pause_count: int total_pause_seconds: int reflection_notes: str = "" reflection_rating: Optional[int] = None key_learnings: List[str] = [] class TeacherAnalyticsResponse(BaseModel): """Response fuer Lehrer-Analytics.""" teacher_id: str period_start: Optional[str] period_end: Optional[str] total_sessions: int completed_sessions: int total_teaching_minutes: int total_teaching_hours: float avg_phase_durations: Dict[str, int] sessions_with_overtime: int overtime_percentage: int avg_overtime_seconds: int avg_overtime_formatted: str most_overtime_phase: Optional[str] avg_pause_count: float avg_pause_duration_seconds: int subjects_taught: Dict[str, int] classes_taught: Dict[str, int] class ReflectionCreate(BaseModel): """Request-Body fuer Reflection-Erstellung.""" session_id: str teacher_id: str notes: str = "" overall_rating: Optional[int] = Field(None, ge=1, le=5) what_worked: List[str] = [] improvements: List[str] = [] notes_for_next_lesson: str = "" class ReflectionUpdate(BaseModel): """Request-Body fuer Reflection-Update.""" notes: Optional[str] = None overall_rating: Optional[int] = Field(None, ge=1, le=5) what_worked: Optional[List[str]] = None improvements: Optional[List[str]] = None notes_for_next_lesson: Optional[str] = None class ReflectionResponse(BaseModel): """Response fuer eine einzelne Reflection.""" reflection_id: str session_id: str teacher_id: str notes: str overall_rating: Optional[int] what_worked: List[str] improvements: List[str] notes_for_next_lesson: str created_at: Optional[str] updated_at: Optional[str] # === Feedback Models === class FeedbackCreate(BaseModel): """Request zum Erstellen von Feedback.""" title: str = Field(..., min_length=3, max_length=500, description="Kurzer Titel") description: str = Field(..., min_length=10, description="Beschreibung") feedback_type: str = Field("improvement", description="bug, feature_request, improvement, praise, question") priority: str = Field("medium", description="critical, high, medium, low") teacher_name: str = Field("", description="Name des Lehrers") teacher_email: str = Field("", description="E-Mail fuer Rueckfragen") context_url: str = Field("", description="URL wo Feedback gegeben wurde") context_phase: str = Field("", description="Aktuelle Phase") context_session_id: Optional[str] = Field(None, description="Session-ID falls aktiv") related_feature: Optional[str] = Field(None, description="Verwandtes Feature") class FeedbackResponse(BaseModel): """Response fuer Feedback.""" id: str teacher_id: str teacher_name: str title: str description: str feedback_type: str priority: str status: str created_at: str response: Optional[str] = None class FeedbackListResponse(BaseModel): """Liste von Feedbacks.""" feedbacks: List[Dict[str, Any]] total: int class FeedbackStatsResponse(BaseModel): """Feedback-Statistiken.""" total: int by_status: Dict[str, int] by_type: Dict[str, int] by_priority: Dict[str, int] # === Settings Models === class TeacherSettingsResponse(BaseModel): """Response fuer Lehrer-Einstellungen.""" teacher_id: str default_phase_durations: Dict[str, int] audio_enabled: bool = True high_contrast: bool = False show_statistics: bool = True class UpdatePhaseDurationsRequest(BaseModel): """Request zum Aktualisieren der Phasen-Dauern.""" durations: Dict[str, int] = Field( ..., description="Phasen-Dauern in Minuten, z.B. {'einstieg': 10, 'erarbeitung': 25}", examples=[{"einstieg": 10, "erarbeitung": 25, "sicherung": 10, "transfer": 8, "reflexion": 5}] ) class UpdatePreferencesRequest(BaseModel): """Request zum Aktualisieren der UI-Praeferenzen.""" audio_enabled: Optional[bool] = None high_contrast: Optional[bool] = None show_statistics: Optional[bool] = None # === Context Models === class SchoolInfo(BaseModel): """Schul-Informationen.""" federal_state: str federal_state_name: str = "" school_type: str school_type_name: str = "" class SchoolYearInfo(BaseModel): """Schuljahr-Informationen.""" id: str start: Optional[str] = None current_week: int = 1 class MacroPhaseInfo(BaseModel): """Makro-Phase Informationen.""" id: str label: str confidence: float = 1.0 class CoreCounts(BaseModel): """Kern-Zaehler fuer den Kontext.""" classes: int = 0 exams_scheduled: int = 0 corrections_pending: int = 0 class ContextFlags(BaseModel): """Status-Flags des Kontexts.""" onboarding_completed: bool = False has_classes: bool = False has_schedule: bool = False is_exam_period: bool = False is_before_holidays: bool = False class TeacherContextResponse(BaseModel): """Response fuer GET /v1/context.""" schema_version: str = "1.0" teacher_id: str school: SchoolInfo school_year: SchoolYearInfo macro_phase: MacroPhaseInfo core_counts: CoreCounts flags: ContextFlags class UpdateContextRequest(BaseModel): """Request zum Aktualisieren des Kontexts.""" federal_state: Optional[str] = None school_type: Optional[str] = None schoolyear: Optional[str] = None schoolyear_start: Optional[str] = None macro_phase: Optional[str] = None current_week: Optional[int] = None # === Event Models === class CreateEventRequest(BaseModel): """Request zum Erstellen eines Events.""" title: str event_type: str = "other" start_date: str end_date: Optional[str] = None class_id: Optional[str] = None subject: Optional[str] = None description: str = "" needs_preparation: bool = True reminder_days_before: int = 7 class EventResponse(BaseModel): """Response fuer ein Event.""" id: str teacher_id: str event_type: str title: str description: str start_date: str end_date: Optional[str] class_id: Optional[str] subject: Optional[str] status: str needs_preparation: bool preparation_done: bool reminder_days_before: int # === Routine Models === class CreateRoutineRequest(BaseModel): """Request zum Erstellen einer Routine.""" title: str routine_type: str = "other" recurrence_pattern: str = "weekly" day_of_week: Optional[int] = None day_of_month: Optional[int] = None time_of_day: Optional[str] = None duration_minutes: int = 60 description: str = "" class RoutineResponse(BaseModel): """Response fuer eine Routine.""" id: str teacher_id: str routine_type: str title: str description: str recurrence_pattern: str day_of_week: Optional[int] day_of_month: Optional[int] time_of_day: Optional[str] duration_minutes: int is_active: bool