Files
Benjamin Boenisch 5a31f52310 Initial commit: breakpilot-lehrer - Lehrer KI Platform
Services: Admin-Lehrer, Backend-Lehrer, Studio v2, Website,
Klausur-Service, School-Service, Voice-Service, Geo-Service,
BreakPilot Drive, Agent-Core

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 23:47:26 +01:00

569 lines
16 KiB
Python

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