fix: Restore all files lost during destructive rebase
A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.
This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).
Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
489
backend/api/classroom/models.py
Normal file
489
backend/api/classroom/models.py
Normal file
@@ -0,0 +1,489 @@
|
||||
"""
|
||||
Classroom API - Pydantic Models.
|
||||
|
||||
Alle Request/Response Models fuer die Classroom API.
|
||||
"""
|
||||
|
||||
from typing import Dict, List, Optional, Any
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
# === Request 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)")
|
||||
|
||||
|
||||
# === Response Models ===
|
||||
|
||||
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 (Feature f17) ===
|
||||
|
||||
class SessionHistoryItem(BaseModel):
|
||||
"""Ein Eintrag in der Session-Historie."""
|
||||
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: int
|
||||
phases_completed: int
|
||||
notes: str
|
||||
homework: str
|
||||
|
||||
|
||||
class SessionHistoryResponse(BaseModel):
|
||||
"""Response fuer Session-Historie."""
|
||||
sessions: List[SessionHistoryItem]
|
||||
total_count: int
|
||||
page: int
|
||||
page_size: int
|
||||
|
||||
|
||||
# === Template Models ===
|
||||
|
||||
class TemplatePhaseConfig(BaseModel):
|
||||
"""Konfiguration einer Phase im Template."""
|
||||
phase: str
|
||||
duration_minutes: int
|
||||
activities: List[str] = Field(default_factory=list)
|
||||
notes: str = ""
|
||||
|
||||
|
||||
class CreateTemplateRequest(BaseModel):
|
||||
"""Request zum Erstellen eines Templates."""
|
||||
name: str = Field(..., min_length=1, max_length=100)
|
||||
description: str = Field("", max_length=500)
|
||||
subject: str = Field(..., min_length=1)
|
||||
grade_level: Optional[str] = None
|
||||
phase_configs: Optional[List[TemplatePhaseConfig]] = None
|
||||
tags: List[str] = Field(default_factory=list)
|
||||
is_public: bool = False
|
||||
|
||||
|
||||
class UpdateTemplateRequest(BaseModel):
|
||||
"""Request zum Aktualisieren eines Templates."""
|
||||
name: Optional[str] = Field(None, min_length=1, max_length=100)
|
||||
description: Optional[str] = Field(None, max_length=500)
|
||||
subject: Optional[str] = None
|
||||
grade_level: Optional[str] = None
|
||||
phase_configs: Optional[List[TemplatePhaseConfig]] = None
|
||||
tags: Optional[List[str]] = None
|
||||
is_public: Optional[bool] = None
|
||||
|
||||
|
||||
class TemplateResponse(BaseModel):
|
||||
"""Response fuer ein einzelnes Template."""
|
||||
template_id: str
|
||||
name: str
|
||||
description: str
|
||||
subject: str
|
||||
grade_level: Optional[str]
|
||||
phase_configs: List[TemplatePhaseConfig]
|
||||
tags: List[str]
|
||||
is_public: bool
|
||||
is_system: bool
|
||||
created_by: str
|
||||
created_at: str
|
||||
updated_at: Optional[str]
|
||||
usage_count: int
|
||||
|
||||
|
||||
class TemplateListResponse(BaseModel):
|
||||
"""Response fuer Template-Liste."""
|
||||
templates: List[TemplateResponse]
|
||||
total_count: int
|
||||
|
||||
|
||||
class CreateFromTemplateRequest(BaseModel):
|
||||
"""Request zum Erstellen einer Session aus Template."""
|
||||
template_id: str
|
||||
class_id: str
|
||||
topic: Optional[str] = None
|
||||
phase_duration_overrides: Optional[Dict[str, int]] = None
|
||||
|
||||
|
||||
# === Homework Models ===
|
||||
|
||||
class CreateHomeworkRequest(BaseModel):
|
||||
"""Request zum Erstellen einer Hausaufgabe."""
|
||||
session_id: Optional[str] = None
|
||||
teacher_id: str
|
||||
class_id: str
|
||||
subject: str
|
||||
title: str = Field(..., min_length=1, max_length=200)
|
||||
description: str = Field("", max_length=2000)
|
||||
due_date: Optional[str] = None
|
||||
estimated_minutes: Optional[int] = Field(None, ge=5, le=180)
|
||||
materials: List[str] = Field(default_factory=list)
|
||||
tags: List[str] = Field(default_factory=list)
|
||||
|
||||
|
||||
class UpdateHomeworkRequest(BaseModel):
|
||||
"""Request zum Aktualisieren einer Hausaufgabe."""
|
||||
title: Optional[str] = Field(None, min_length=1, max_length=200)
|
||||
description: Optional[str] = Field(None, max_length=2000)
|
||||
due_date: Optional[str] = None
|
||||
estimated_minutes: Optional[int] = Field(None, ge=5, le=180)
|
||||
status: Optional[str] = None
|
||||
materials: Optional[List[str]] = None
|
||||
tags: Optional[List[str]] = None
|
||||
|
||||
|
||||
class HomeworkResponse(BaseModel):
|
||||
"""Response fuer eine Hausaufgabe."""
|
||||
homework_id: str
|
||||
session_id: Optional[str]
|
||||
teacher_id: str
|
||||
class_id: str
|
||||
subject: str
|
||||
title: str
|
||||
description: str
|
||||
due_date: Optional[str]
|
||||
estimated_minutes: Optional[int]
|
||||
status: str
|
||||
materials: List[str]
|
||||
tags: List[str]
|
||||
created_at: str
|
||||
updated_at: Optional[str]
|
||||
|
||||
|
||||
class HomeworkListResponse(BaseModel):
|
||||
"""Response fuer Hausaufgaben-Liste."""
|
||||
homework: List[HomeworkResponse]
|
||||
total_count: int
|
||||
|
||||
|
||||
# === Material Models ===
|
||||
|
||||
class CreateMaterialRequest(BaseModel):
|
||||
"""Request zum Erstellen eines Materials."""
|
||||
teacher_id: str
|
||||
title: str = Field(..., min_length=1, max_length=200)
|
||||
description: str = Field("", max_length=1000)
|
||||
material_type: str = Field(..., description="Type: link, document, video, interactive, image")
|
||||
content_url: Optional[str] = None
|
||||
content_data: Optional[Dict[str, Any]] = None
|
||||
phase: Optional[str] = None
|
||||
subject: Optional[str] = None
|
||||
tags: List[str] = Field(default_factory=list)
|
||||
is_public: bool = False
|
||||
|
||||
|
||||
class UpdateMaterialRequest(BaseModel):
|
||||
"""Request zum Aktualisieren eines Materials."""
|
||||
title: Optional[str] = Field(None, min_length=1, max_length=200)
|
||||
description: Optional[str] = Field(None, max_length=1000)
|
||||
material_type: Optional[str] = None
|
||||
content_url: Optional[str] = None
|
||||
content_data: Optional[Dict[str, Any]] = None
|
||||
phase: Optional[str] = None
|
||||
subject: 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
|
||||
description: str
|
||||
material_type: str
|
||||
content_url: Optional[str]
|
||||
content_data: Optional[Dict[str, Any]]
|
||||
phase: Optional[str]
|
||||
subject: Optional[str]
|
||||
tags: List[str]
|
||||
is_public: bool
|
||||
usage_count: int
|
||||
created_at: str
|
||||
updated_at: Optional[str]
|
||||
|
||||
|
||||
class MaterialListResponse(BaseModel):
|
||||
"""Response fuer Material-Liste."""
|
||||
materials: List[MaterialResponse]
|
||||
total_count: int
|
||||
|
||||
|
||||
# === Feedback Models ===
|
||||
|
||||
class CreateFeedbackRequest(BaseModel):
|
||||
"""Request zum Erstellen von Feedback."""
|
||||
teacher_id: str
|
||||
session_id: Optional[str] = None
|
||||
category: str = Field(..., description="bug, feature, usability, content, other")
|
||||
title: str = Field(..., min_length=1, max_length=200)
|
||||
description: str = Field(..., min_length=10, max_length=5000)
|
||||
priority: str = Field("medium", description="low, medium, high, critical")
|
||||
context_data: Optional[Dict[str, Any]] = None
|
||||
|
||||
|
||||
class FeedbackResponse(BaseModel):
|
||||
"""Response fuer ein Feedback."""
|
||||
feedback_id: str
|
||||
teacher_id: str
|
||||
session_id: Optional[str]
|
||||
category: str
|
||||
title: str
|
||||
description: str
|
||||
priority: str
|
||||
status: str
|
||||
context_data: Optional[Dict[str, Any]]
|
||||
admin_notes: Optional[str]
|
||||
created_at: str
|
||||
updated_at: Optional[str]
|
||||
|
||||
|
||||
class FeedbackListResponse(BaseModel):
|
||||
"""Response fuer Feedback-Liste."""
|
||||
feedback: List[FeedbackResponse]
|
||||
total_count: int
|
||||
|
||||
|
||||
class FeedbackStatsResponse(BaseModel):
|
||||
"""Response fuer Feedback-Statistiken."""
|
||||
total: int
|
||||
by_category: Dict[str, int]
|
||||
by_status: Dict[str, int]
|
||||
by_priority: Dict[str, int]
|
||||
|
||||
|
||||
# === Settings Models ===
|
||||
|
||||
class PhaseDurationsUpdate(BaseModel):
|
||||
"""Update fuer Phasendauern."""
|
||||
einstieg: Optional[int] = Field(None, ge=1, le=30)
|
||||
erarbeitung: Optional[int] = Field(None, ge=5, le=45)
|
||||
sicherung: Optional[int] = Field(None, ge=3, le=20)
|
||||
transfer: Optional[int] = Field(None, ge=3, le=20)
|
||||
reflexion: Optional[int] = Field(None, ge=2, le=15)
|
||||
|
||||
|
||||
class PreferencesUpdate(BaseModel):
|
||||
"""Update fuer Lehrer-Praeferenzen."""
|
||||
auto_advance: Optional[bool] = None
|
||||
sound_enabled: Optional[bool] = None
|
||||
notification_enabled: Optional[bool] = None
|
||||
theme: Optional[str] = None
|
||||
language: Optional[str] = None
|
||||
|
||||
|
||||
class TeacherSettingsResponse(BaseModel):
|
||||
"""Response fuer Lehrer-Einstellungen."""
|
||||
teacher_id: str
|
||||
phase_durations: Dict[str, int]
|
||||
preferences: Dict[str, Any]
|
||||
created_at: str
|
||||
updated_at: Optional[str]
|
||||
|
||||
|
||||
# === Analytics Models ===
|
||||
|
||||
class ReflectionRequest(BaseModel):
|
||||
"""Request zum Erstellen/Aktualisieren einer Reflexion."""
|
||||
session_id: str
|
||||
teacher_id: str
|
||||
overall_rating: int = Field(..., ge=1, le=5)
|
||||
time_management_rating: int = Field(..., ge=1, le=5)
|
||||
student_engagement_rating: int = Field(..., ge=1, le=5)
|
||||
goals_achieved_rating: int = Field(..., ge=1, le=5)
|
||||
what_worked_well: str = Field("", max_length=2000)
|
||||
what_to_improve: str = Field("", max_length=2000)
|
||||
notes_for_next_time: str = Field("", max_length=2000)
|
||||
tags: List[str] = Field(default_factory=list)
|
||||
|
||||
|
||||
class ReflectionResponse(BaseModel):
|
||||
"""Response fuer eine Reflexion."""
|
||||
reflection_id: str
|
||||
session_id: str
|
||||
teacher_id: str
|
||||
overall_rating: int
|
||||
time_management_rating: int
|
||||
student_engagement_rating: int
|
||||
goals_achieved_rating: int
|
||||
what_worked_well: str
|
||||
what_to_improve: str
|
||||
notes_for_next_time: str
|
||||
tags: List[str]
|
||||
created_at: str
|
||||
updated_at: Optional[str]
|
||||
|
||||
|
||||
# === Teacher Context Models (v1 API) ===
|
||||
|
||||
class TeacherContextResponse(BaseModel):
|
||||
"""Response fuer Teacher Context."""
|
||||
teacher_id: str
|
||||
federal_state: Optional[str]
|
||||
school_type: Optional[str]
|
||||
subjects: List[str]
|
||||
class_levels: List[str]
|
||||
current_macro_phase: Optional[str]
|
||||
onboarding_completed: bool
|
||||
preferences: Dict[str, Any]
|
||||
created_at: str
|
||||
updated_at: Optional[str]
|
||||
|
||||
|
||||
class UpdateTeacherContextRequest(BaseModel):
|
||||
"""Request zum Aktualisieren des Teacher Context."""
|
||||
federal_state: Optional[str] = None
|
||||
school_type: Optional[str] = None
|
||||
subjects: Optional[List[str]] = None
|
||||
class_levels: Optional[List[str]] = None
|
||||
current_macro_phase: Optional[str] = None
|
||||
preferences: Optional[Dict[str, Any]] = None
|
||||
|
||||
|
||||
class EventResponse(BaseModel):
|
||||
"""Response fuer ein Schuljahres-Event."""
|
||||
event_id: str
|
||||
teacher_id: str
|
||||
title: str
|
||||
event_type: str
|
||||
start_date: str
|
||||
end_date: Optional[str]
|
||||
description: Optional[str]
|
||||
status: str
|
||||
metadata: Optional[Dict[str, Any]]
|
||||
created_at: str
|
||||
|
||||
|
||||
class CreateEventRequest(BaseModel):
|
||||
"""Request zum Erstellen eines Events."""
|
||||
title: str = Field(..., min_length=1, max_length=200)
|
||||
event_type: str
|
||||
start_date: str
|
||||
end_date: Optional[str] = None
|
||||
description: Optional[str] = Field(None, max_length=1000)
|
||||
metadata: Optional[Dict[str, Any]] = None
|
||||
|
||||
|
||||
class RoutineResponse(BaseModel):
|
||||
"""Response fuer eine wiederkehrende Routine."""
|
||||
routine_id: str
|
||||
teacher_id: str
|
||||
title: str
|
||||
routine_type: str
|
||||
recurrence_pattern: str
|
||||
day_of_week: Optional[int]
|
||||
time_of_day: Optional[str]
|
||||
description: Optional[str]
|
||||
is_active: bool
|
||||
metadata: Optional[Dict[str, Any]]
|
||||
created_at: str
|
||||
|
||||
|
||||
class CreateRoutineRequest(BaseModel):
|
||||
"""Request zum Erstellen einer Routine."""
|
||||
title: str = Field(..., min_length=1, max_length=200)
|
||||
routine_type: str
|
||||
recurrence_pattern: str
|
||||
day_of_week: Optional[int] = Field(None, ge=0, le=6)
|
||||
time_of_day: Optional[str] = None
|
||||
description: Optional[str] = Field(None, max_length=500)
|
||||
metadata: Optional[Dict[str, Any]] = None
|
||||
Reference in New Issue
Block a user