Some checks failed
Tests / Go Tests (push) Has been cancelled
Tests / Python Tests (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / Go Lint (push) Has been cancelled
Tests / Python Lint (push) Has been cancelled
Tests / Security Scan (push) Has been cancelled
Tests / All Checks Passed (push) Has been cancelled
Security Scanning / Secret Scanning (push) Has been cancelled
Security Scanning / Dependency Vulnerability Scan (push) Has been cancelled
Security Scanning / Go Security Scan (push) Has been cancelled
Security Scanning / Python Security Scan (push) Has been cancelled
Security Scanning / Node.js Security Scan (push) Has been cancelled
Security Scanning / Docker Image Security (push) Has been cancelled
Security Scanning / Security Summary (push) Has been cancelled
CI/CD Pipeline / Go Tests (push) Has been cancelled
CI/CD Pipeline / Python Tests (push) Has been cancelled
CI/CD Pipeline / Website Tests (push) Has been cancelled
CI/CD Pipeline / Linting (push) Has been cancelled
CI/CD Pipeline / Security Scan (push) Has been cancelled
CI/CD Pipeline / Docker Build & Push (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / CI Summary (push) Has been cancelled
ci/woodpecker/manual/build-ci-image Pipeline was successful
ci/woodpecker/manual/main Pipeline failed
All services: admin-v2, studio-v2, website, ai-compliance-sdk, consent-service, klausur-service, voice-service, and infrastructure. Large PDFs and compiled binaries excluded via .gitignore.
569 lines
16 KiB
Python
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
|