This repository has been archived on 2026-02-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
BreakPilot Dev 19855efacc
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
feat: BreakPilot PWA - Full codebase (clean push without large binaries)
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.
2026-02-11 13:25:58 +01:00

430 lines
13 KiB
Python

"""
SQLAlchemy Database Models fuer Classroom Engine (Feature f13).
Persistiert Unterrichtsstunden und deren Verlauf in PostgreSQL.
"""
from datetime import datetime
from sqlalchemy import (
Column, String, Integer, Float, DateTime, JSON,
Boolean, Text, Enum as SQLEnum, ForeignKey
)
from sqlalchemy.orm import relationship
import enum
import uuid
from .database import Base
# Import Phase 8 Context Models
from .context_models import (
MacroPhaseEnum,
EventTypeEnum,
EventStatusEnum,
RoutineTypeEnum,
RecurrencePatternEnum,
TeacherContextDB,
SchoolyearEventDB,
RecurringRoutineDB,
FEDERAL_STATES,
SCHOOL_TYPES,
)
# Exports
__all__ = [
"LessonPhaseEnum",
"LessonSessionDB",
"PhaseHistoryDB",
"LessonTemplateDB",
"TeacherSettingsDB",
"HomeworkStatusEnum",
"HomeworkDB",
"MaterialTypeEnum",
"PhaseMaterialDB",
"LessonReflectionDB",
"FeedbackTypeEnum",
"FeedbackStatusEnum",
"FeedbackPriorityEnum",
"TeacherFeedbackDB",
# Phase 8: Schuljahres-Kontext
"MacroPhaseEnum",
"EventTypeEnum",
"EventStatusEnum",
"RoutineTypeEnum",
"RecurrencePatternEnum",
"TeacherContextDB",
"SchoolyearEventDB",
"RecurringRoutineDB",
"FEDERAL_STATES",
"SCHOOL_TYPES",
]
class LessonPhaseEnum(str, enum.Enum):
"""Unterrichtsphasen als DB-Enum."""
NOT_STARTED = "not_started"
EINSTIEG = "einstieg"
ERARBEITUNG = "erarbeitung"
SICHERUNG = "sicherung"
TRANSFER = "transfer"
REFLEXION = "reflexion"
ENDED = "ended"
class LessonSessionDB(Base):
"""
Persistierte Unterrichtsstunde.
Speichert alle Session-Daten inklusive Timer-Status und History.
"""
__tablename__ = 'lesson_sessions'
# Primary Key
id = Column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
# Beziehungen
teacher_id = Column(String(100), nullable=False, index=True)
class_id = Column(String(50), nullable=False, index=True)
# Session Metadaten
subject = Column(String(100), nullable=False)
topic = Column(String(500))
# Status
current_phase = Column(
SQLEnum(LessonPhaseEnum),
default=LessonPhaseEnum.NOT_STARTED,
nullable=False
)
is_paused = Column(Boolean, default=False)
# Timestamps
created_at = Column(DateTime, default=datetime.utcnow)
lesson_started_at = Column(DateTime)
lesson_ended_at = Column(DateTime)
phase_started_at = Column(DateTime)
pause_started_at = Column(DateTime)
# Timer-Daten
total_paused_seconds = Column(Integer, default=0)
phase_durations = Column(JSON, default=dict) # {"einstieg": 8, ...}
# History & Notizen
phase_history = Column(JSON, default=list) # [{phase, started_at, ended_at, duration}]
notes = Column(Text, default="")
homework = Column(Text, default="")
# Relationship zu PhaseHistory (optional fuer detaillierte Abfragen)
history_entries = relationship("PhaseHistoryDB", back_populates="session", cascade="all, delete-orphan")
def __repr__(self):
return f"<LessonSession {self.id}: {self.subject} ({self.current_phase.value})>"
class PhaseHistoryDB(Base):
"""
Einzelner Phasen-Verlaufseintrag.
Ermoeglicht detaillierte Statistiken ueber Phasendauern.
"""
__tablename__ = 'lesson_phase_history'
id = Column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
session_id = Column(String(36), ForeignKey('lesson_sessions.id'), nullable=False, index=True)
phase = Column(SQLEnum(LessonPhaseEnum), nullable=False)
started_at = Column(DateTime, nullable=False)
ended_at = Column(DateTime)
duration_seconds = Column(Integer)
# Zusaetzliche Metriken
was_extended = Column(Boolean, default=False)
extension_minutes = Column(Integer, default=0)
pause_count = Column(Integer, default=0)
total_pause_seconds = Column(Integer, default=0)
# Relationship
session = relationship("LessonSessionDB", back_populates="history_entries")
def __repr__(self):
return f"<PhaseHistory {self.phase.value} ({self.duration_seconds}s)>"
class LessonTemplateDB(Base):
"""
Stunden-Vorlage (Feature f37).
Ermoeglicht Speicherung von wiederverwendbaren Stundenkonfigurationen.
"""
__tablename__ = 'lesson_templates'
id = Column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
teacher_id = Column(String(100), nullable=False, index=True)
# Basis-Infos
name = Column(String(200), nullable=False)
description = Column(Text, default="")
subject = Column(String(100), default="")
grade_level = Column(String(50), default="")
# Phasenkonfiguration
phase_durations = Column(JSON, default=dict)
# Vorbelegungen
default_topic = Column(String(500), default="")
default_notes = Column(Text, default="")
# Metadaten
is_public = Column(Boolean, default=False)
usage_count = Column(Integer, default=0)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
def __repr__(self):
return f"<LessonTemplate {self.name} ({self.teacher_id})>"
class TeacherSettingsDB(Base):
"""
Lehrer-spezifische Einstellungen (Feature f16).
Speichert individuelle Praeferenzen wie Phasendauern.
"""
__tablename__ = 'teacher_settings'
id = Column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
teacher_id = Column(String(100), unique=True, nullable=False, index=True)
# Individuelle Phasendauern
default_phase_durations = Column(JSON, default=dict)
# UI Praeferenzen
audio_enabled = Column(Boolean, default=True)
high_contrast = Column(Boolean, default=False)
# Statistik-Einstellungen
show_statistics = Column(Boolean, default=True)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
def __repr__(self):
return f"<TeacherSettings {self.teacher_id}>"
class HomeworkStatusEnum(str, enum.Enum):
"""Status-Enum fuer Hausaufgaben."""
ASSIGNED = "assigned"
IN_PROGRESS = "in_progress"
COMPLETED = "completed"
OVERDUE = "overdue"
class HomeworkDB(Base):
"""
Hausaufgaben-Tracking (Feature f20).
Ermoeglicht die Verfolgung von Hausaufgaben ueber Sessions hinweg.
"""
__tablename__ = 'homework_assignments'
id = Column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
teacher_id = Column(String(100), nullable=False, index=True)
class_id = Column(String(50), nullable=False, index=True)
subject = Column(String(100), nullable=False)
# Aufgaben-Details
title = Column(String(300), nullable=False)
description = Column(Text, default="")
# Verknuepfung zur Session (optional)
session_id = Column(String(36), ForeignKey('lesson_sessions.id'), nullable=True, index=True)
# Faelligkeit und Status
due_date = Column(DateTime, nullable=True, index=True)
status = Column(
SQLEnum(HomeworkStatusEnum),
default=HomeworkStatusEnum.ASSIGNED,
nullable=False
)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
def __repr__(self):
return f"<Homework {self.title} ({self.status.value})>"
class MaterialTypeEnum(str, enum.Enum):
"""Typ-Enum fuer Materialien."""
DOCUMENT = "document"
LINK = "link"
VIDEO = "video"
IMAGE = "image"
WORKSHEET = "worksheet"
PRESENTATION = "presentation"
OTHER = "other"
class PhaseMaterialDB(Base):
"""
Phasen-Materialien (Feature f19).
Ermoeglicht das Anhaengen von Dokumenten und Links an Unterrichtsphasen.
"""
__tablename__ = 'phase_materials'
id = Column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
teacher_id = Column(String(100), nullable=False, index=True)
# Material-Details
title = Column(String(300), nullable=False)
material_type = Column(
SQLEnum(MaterialTypeEnum),
default=MaterialTypeEnum.DOCUMENT,
nullable=False
)
url = Column(String(2000), nullable=True) # URL oder Dateipfad
description = Column(Text, default="")
# Phasen-Zuordnung
phase = Column(String(50), nullable=True, index=True) # einstieg, erarbeitung, etc.
subject = Column(String(100), default="")
grade_level = Column(String(50), default="")
# Tags als JSON Array
tags = Column(JSON, default=list)
# Sharing und Nutzung
is_public = Column(Boolean, default=False)
usage_count = Column(Integer, default=0)
# Optionale Session-Verknuepfung
session_id = Column(String(36), ForeignKey('lesson_sessions.id'), nullable=True, index=True)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
def __repr__(self):
return f"<PhaseMaterial {self.title} ({self.material_type.value})>"
class LessonReflectionDB(Base):
"""
Post-Lesson Reflection (Phase 5: Analytics).
Ermoeglicht Lehrern, nach der Stunde Reflexionsnotizen zu speichern.
"""
__tablename__ = 'lesson_reflections'
id = Column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
session_id = Column(String(36), ForeignKey('lesson_sessions.id'), nullable=False, unique=True, index=True)
teacher_id = Column(String(100), nullable=False, index=True)
# Reflexions-Inhalt
notes = Column(Text, default="")
# Optionale Selbst-Bewertung (1-5)
overall_rating = Column(Integer, nullable=True)
# Was hat gut funktioniert? (JSON Array)
what_worked = Column(JSON, default=list)
# Was wuerde ich anders machen? (JSON Array)
improvements = Column(JSON, default=list)
# Notizen fuer naechste Stunde
notes_for_next_lesson = Column(Text, default="")
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
def __repr__(self):
return f"<LessonReflection session={self.session_id}>"
class FeedbackTypeEnum(str, enum.Enum):
"""Feedback-Typen fuer Lehrer-Rueckmeldungen."""
BUG = "bug"
FEATURE_REQUEST = "feature_request"
IMPROVEMENT = "improvement"
PRAISE = "praise"
QUESTION = "question"
class FeedbackStatusEnum(str, enum.Enum):
"""Status eines Feedbacks."""
NEW = "new"
ACKNOWLEDGED = "acknowledged"
PLANNED = "planned"
IMPLEMENTED = "implemented"
DECLINED = "declined"
class FeedbackPriorityEnum(str, enum.Enum):
"""Prioritaet eines Feedbacks."""
CRITICAL = "critical"
HIGH = "high"
MEDIUM = "medium"
LOW = "low"
class TeacherFeedbackDB(Base):
"""
Lehrer-Feedback zum Companion-Modul (Phase 7).
Ermoeglicht Lehrern, Bug-Reports, Feature-Requests und Verbesserungen
direkt aus dem Lehrer-Frontend zu senden.
"""
__tablename__ = 'teacher_feedback'
id = Column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
# Wer hat das Feedback gegeben?
teacher_id = Column(String(100), nullable=False, index=True)
teacher_name = Column(String(200), default="")
teacher_email = Column(String(200), default="")
# Feedback-Inhalt
title = Column(String(500), nullable=False)
description = Column(Text, nullable=False)
# Kategorisierung
feedback_type = Column(
SQLEnum(FeedbackTypeEnum),
default=FeedbackTypeEnum.IMPROVEMENT,
nullable=False
)
priority = Column(
SQLEnum(FeedbackPriorityEnum),
default=FeedbackPriorityEnum.MEDIUM,
nullable=False
)
status = Column(
SQLEnum(FeedbackStatusEnum),
default=FeedbackStatusEnum.NEW,
nullable=False,
index=True
)
# Optionale Verknuepfung zu Feature
related_feature = Column(String(50), nullable=True)
# Kontext: Wo war der User als er Feedback gab?
context_url = Column(String(500), default="")
context_phase = Column(String(50), default="") # z.B. "einstieg"
context_session_id = Column(String(36), nullable=True)
# Browser/Device Info
user_agent = Column(String(500), default="")
# Entwickler-Antwort
response = Column(Text, default="")
responded_at = Column(DateTime, nullable=True)
responded_by = Column(String(100), nullable=True)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
def __repr__(self):
return f"<TeacherFeedback {self.id[:8]} - {self.title[:30]}>"