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>
318 lines
10 KiB
Python
318 lines
10 KiB
Python
"""
|
|
State Engine Models - Datenstrukturen für das Phasen-Management.
|
|
|
|
Definiert:
|
|
- SchoolYearPhase: Die 9 Phasen des Schuljahres
|
|
- TeacherContext: Aggregierter Kontext für Antizipation
|
|
- Event, Milestone, Stats: Unterstützende Modelle
|
|
"""
|
|
|
|
from dataclasses import dataclass, field
|
|
from datetime import datetime
|
|
from enum import Enum
|
|
from typing import List, Optional, Dict, Any
|
|
import uuid
|
|
|
|
|
|
class SchoolYearPhase(str, Enum):
|
|
"""Die 9 Phasen eines Schuljahres."""
|
|
|
|
# Phase 1: Schuljahresbeginn (Aug/Sep)
|
|
ONBOARDING = "onboarding"
|
|
# Neue Lehrer, Schulsuche, Grundkonfiguration
|
|
|
|
# Phase 2: Schuljahresstart (Sep/Okt)
|
|
SCHOOL_YEAR_START = "school_year_start"
|
|
# Klassen anlegen, Stundenplan, erste Einheiten
|
|
|
|
# Phase 3: Unterrichtsaufbau (Okt/Nov)
|
|
TEACHING_SETUP = "teaching_setup"
|
|
# Lerneinheiten, Materialien, Elternkommunikation
|
|
|
|
# Phase 4: Leistungsphase 1 (Nov/Dez)
|
|
PERFORMANCE_1 = "performance_1"
|
|
# Klausuren, Korrektur, erste Noten
|
|
|
|
# Phase 5: Halbjahresabschluss (Jan/Feb)
|
|
SEMESTER_END = "semester_end"
|
|
# Halbjahreszeugnisse, Konferenzen, Elterngespräche
|
|
|
|
# Phase 6: 2. Halbjahr Unterricht (Feb/Apr)
|
|
TEACHING_2 = "teaching_2"
|
|
# Wiederholung von Phase 3
|
|
|
|
# Phase 7: Leistungsphase 2 (Apr/Jun)
|
|
PERFORMANCE_2 = "performance_2"
|
|
# Klausuren, Korrektur, finale Noten
|
|
|
|
# Phase 8: Jahresabschluss (Jun/Jul)
|
|
YEAR_END = "year_end"
|
|
# Abschlusszeugnisse, Versetzung, Archivierung
|
|
|
|
# Phase 9: Archiviert
|
|
ARCHIVED = "archived"
|
|
# Schuljahr abgeschlossen
|
|
|
|
|
|
@dataclass
|
|
class PhaseInfo:
|
|
"""Metadaten zu einer Phase."""
|
|
phase: SchoolYearPhase
|
|
display_name: str
|
|
description: str
|
|
typical_months: List[int] # 1-12
|
|
expected_duration_weeks: int
|
|
required_actions: List[str]
|
|
optional_actions: List[str]
|
|
|
|
|
|
# Phasen-Definitionen mit Metadaten
|
|
PHASE_INFO: Dict[SchoolYearPhase, PhaseInfo] = {
|
|
SchoolYearPhase.ONBOARDING: PhaseInfo(
|
|
phase=SchoolYearPhase.ONBOARDING,
|
|
display_name="Onboarding",
|
|
description="Willkommen bei BreakPilot! Richte dein Schuljahr ein.",
|
|
typical_months=[8, 9],
|
|
expected_duration_weeks=2,
|
|
required_actions=["school_select", "consent_accept", "profile_complete"],
|
|
optional_actions=["import_previous_year"],
|
|
),
|
|
SchoolYearPhase.SCHOOL_YEAR_START: PhaseInfo(
|
|
phase=SchoolYearPhase.SCHOOL_YEAR_START,
|
|
display_name="Schuljahresstart",
|
|
description="Lege deine Klassen und den Stundenplan an.",
|
|
typical_months=[9, 10],
|
|
expected_duration_weeks=3,
|
|
required_actions=["create_classes", "add_students", "create_timetable"],
|
|
optional_actions=["import_students_csv", "invite_parents"],
|
|
),
|
|
SchoolYearPhase.TEACHING_SETUP: PhaseInfo(
|
|
phase=SchoolYearPhase.TEACHING_SETUP,
|
|
display_name="Unterrichtsaufbau",
|
|
description="Erstelle Lerneinheiten und Materialien.",
|
|
typical_months=[10, 11],
|
|
expected_duration_weeks=4,
|
|
required_actions=["create_learning_units"],
|
|
optional_actions=["generate_worksheets", "prepare_parent_meeting"],
|
|
),
|
|
SchoolYearPhase.PERFORMANCE_1: PhaseInfo(
|
|
phase=SchoolYearPhase.PERFORMANCE_1,
|
|
display_name="Leistungsphase 1",
|
|
description="Erste Klausuren und Bewertungen.",
|
|
typical_months=[11, 12],
|
|
expected_duration_weeks=6,
|
|
required_actions=["schedule_exams", "enter_grades"],
|
|
optional_actions=["use_correction_module", "generate_feedback"],
|
|
),
|
|
SchoolYearPhase.SEMESTER_END: PhaseInfo(
|
|
phase=SchoolYearPhase.SEMESTER_END,
|
|
display_name="Halbjahresabschluss",
|
|
description="Halbjahreszeugnisse und Konferenzen.",
|
|
typical_months=[1, 2],
|
|
expected_duration_weeks=3,
|
|
required_actions=["complete_grades", "generate_certificates"],
|
|
optional_actions=["parent_conferences", "archive_semester"],
|
|
),
|
|
SchoolYearPhase.TEACHING_2: PhaseInfo(
|
|
phase=SchoolYearPhase.TEACHING_2,
|
|
display_name="2. Halbjahr",
|
|
description="Weiterführender Unterricht im 2. Halbjahr.",
|
|
typical_months=[2, 3, 4],
|
|
expected_duration_weeks=8,
|
|
required_actions=["update_learning_units"],
|
|
optional_actions=["generate_worksheets"],
|
|
),
|
|
SchoolYearPhase.PERFORMANCE_2: PhaseInfo(
|
|
phase=SchoolYearPhase.PERFORMANCE_2,
|
|
display_name="Leistungsphase 2",
|
|
description="Finale Klausuren und Bewertungen.",
|
|
typical_months=[4, 5, 6],
|
|
expected_duration_weeks=8,
|
|
required_actions=["schedule_exams", "enter_final_grades"],
|
|
optional_actions=["use_correction_module"],
|
|
),
|
|
SchoolYearPhase.YEAR_END: PhaseInfo(
|
|
phase=SchoolYearPhase.YEAR_END,
|
|
display_name="Jahresabschluss",
|
|
description="Abschlusszeugnisse und Versetzung.",
|
|
typical_months=[6, 7],
|
|
expected_duration_weeks=3,
|
|
required_actions=["complete_all_grades", "generate_final_certificates"],
|
|
optional_actions=["archive_year", "export_data"],
|
|
),
|
|
SchoolYearPhase.ARCHIVED: PhaseInfo(
|
|
phase=SchoolYearPhase.ARCHIVED,
|
|
display_name="Archiviert",
|
|
description="Das Schuljahr ist abgeschlossen.",
|
|
typical_months=[7, 8],
|
|
expected_duration_weeks=0,
|
|
required_actions=[],
|
|
optional_actions=["view_archive"],
|
|
),
|
|
}
|
|
|
|
|
|
def get_phase_info(phase: SchoolYearPhase) -> PhaseInfo:
|
|
"""Gibt Metadaten für eine Phase zurück."""
|
|
return PHASE_INFO.get(phase, PHASE_INFO[SchoolYearPhase.ONBOARDING])
|
|
|
|
|
|
@dataclass
|
|
class ClassSummary:
|
|
"""Zusammenfassung einer Klasse."""
|
|
class_id: str
|
|
name: str
|
|
grade_level: int
|
|
student_count: int
|
|
subject: str
|
|
|
|
|
|
@dataclass
|
|
class Event:
|
|
"""Ein anstehendes Ereignis."""
|
|
type: str # "exam", "parent_meeting", "deadline"
|
|
title: str
|
|
date: datetime
|
|
in_days: int
|
|
class_id: Optional[str] = None
|
|
priority: str = "medium" # "high", "medium", "low"
|
|
|
|
|
|
@dataclass
|
|
class Milestone:
|
|
"""Ein erreichter Meilenstein."""
|
|
milestone: str
|
|
completed_at: datetime
|
|
|
|
|
|
@dataclass
|
|
class TeacherStats:
|
|
"""Statistiken eines Lehrers."""
|
|
learning_units_created: int = 0
|
|
exams_scheduled: int = 0
|
|
exams_graded: int = 0
|
|
grades_entered: int = 0
|
|
parent_messages_count: int = 0
|
|
avg_response_time_hours: float = 0.0
|
|
unanswered_messages: int = 0
|
|
|
|
|
|
@dataclass
|
|
class TeacherContext:
|
|
"""
|
|
Aggregierter Kontext für einen Lehrer.
|
|
|
|
Enthält alle relevanten Informationen für die Antizipations-Engine:
|
|
- Identifikation
|
|
- Schulkontext
|
|
- Zeitlicher Kontext
|
|
- Klassen und Schüler
|
|
- Termine und Events
|
|
- Fortschritt
|
|
- Statistiken
|
|
"""
|
|
# Identifikation
|
|
teacher_id: str
|
|
school_id: str
|
|
school_year_id: str
|
|
|
|
# Schulkontext
|
|
federal_state: str = "niedersachsen" # Bundesland
|
|
school_type: str = "gymnasium" # Schulform
|
|
|
|
# Zeitlicher Kontext
|
|
school_year_start: datetime = field(default_factory=datetime.now)
|
|
current_phase: SchoolYearPhase = SchoolYearPhase.ONBOARDING
|
|
phase_entered_at: datetime = field(default_factory=datetime.now)
|
|
weeks_since_start: int = 0
|
|
days_in_phase: int = 0
|
|
|
|
# Klassen und Schüler
|
|
classes: List[ClassSummary] = field(default_factory=list)
|
|
total_students: int = 0
|
|
|
|
# Termine und Events
|
|
upcoming_events: List[Event] = field(default_factory=list)
|
|
overdue_actions: List[Dict[str, Any]] = field(default_factory=list)
|
|
|
|
# Fortschritt
|
|
completed_milestones: List[str] = field(default_factory=list)
|
|
pending_milestones: List[str] = field(default_factory=list)
|
|
|
|
# Statistiken
|
|
stats: TeacherStats = field(default_factory=TeacherStats)
|
|
|
|
def has_completed_milestone(self, milestone: str) -> bool:
|
|
"""Prüft ob ein Meilenstein erreicht wurde."""
|
|
return milestone in self.completed_milestones
|
|
|
|
def has_learning_units(self) -> bool:
|
|
"""Prüft ob Lerneinheiten erstellt wurden."""
|
|
return self.stats.learning_units_created > 0
|
|
|
|
def is_in_month(self, month: int) -> bool:
|
|
"""Prüft ob aktueller Monat übereinstimmt."""
|
|
return datetime.now().month == month
|
|
|
|
def get_next_deadline(self) -> Optional[Event]:
|
|
"""Gibt die nächste Deadline zurück."""
|
|
for e in self.upcoming_events:
|
|
if e.type == "deadline":
|
|
return e
|
|
return None
|
|
|
|
def get_next_exam(self) -> Optional[Event]:
|
|
"""Gibt die nächste Klausur zurück."""
|
|
for e in self.upcoming_events:
|
|
if e.type == "exam" and e.in_days > 0:
|
|
return e
|
|
return None
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
"""Konvertiert zu Dictionary."""
|
|
return {
|
|
"teacher_id": self.teacher_id,
|
|
"school_id": self.school_id,
|
|
"school_year_id": self.school_year_id,
|
|
"federal_state": self.federal_state,
|
|
"school_type": self.school_type,
|
|
"school_year_start": self.school_year_start.isoformat(),
|
|
"current_phase": self.current_phase.value,
|
|
"phase_entered_at": self.phase_entered_at.isoformat(),
|
|
"weeks_since_start": self.weeks_since_start,
|
|
"days_in_phase": self.days_in_phase,
|
|
"classes": [
|
|
{
|
|
"class_id": c.class_id,
|
|
"name": c.name,
|
|
"grade_level": c.grade_level,
|
|
"student_count": c.student_count,
|
|
"subject": c.subject,
|
|
}
|
|
for c in self.classes
|
|
],
|
|
"total_students": self.total_students,
|
|
"upcoming_events": [
|
|
{
|
|
"type": e.type,
|
|
"title": e.title,
|
|
"date": e.date.isoformat(),
|
|
"in_days": e.in_days,
|
|
"class_id": e.class_id,
|
|
"priority": e.priority,
|
|
}
|
|
for e in self.upcoming_events
|
|
],
|
|
"completed_milestones": self.completed_milestones,
|
|
"pending_milestones": self.pending_milestones,
|
|
"stats": {
|
|
"learning_units_created": self.stats.learning_units_created,
|
|
"exams_scheduled": self.stats.exams_scheduled,
|
|
"exams_graded": self.stats.exams_graded,
|
|
"grades_entered": self.stats.grades_entered,
|
|
"parent_messages_count": self.stats.parent_messages_count,
|
|
"avg_response_time_hours": self.stats.avg_response_time_hours,
|
|
"unanswered_messages": self.stats.unanswered_messages,
|
|
},
|
|
}
|