[split-required] Split final 43 files (500-668 LOC) to complete refactoring

klausur-service (11 files):
- cv_gutter_repair, ocr_pipeline_regression, upload_api
- ocr_pipeline_sessions, smart_spell, nru_worksheet_generator
- ocr_pipeline_overlays, mail/aggregator, zeugnis_api
- cv_syllable_detect, self_rag

backend-lehrer (17 files):
- classroom_engine/suggestions, generators/quiz_generator
- worksheets_api, llm_gateway/comparison, state_engine_api
- classroom/models (→ 4 submodules), services/file_processor
- alerts_agent/api/wizard+digests+routes, content_generators/pdf
- classroom/routes/sessions, llm_gateway/inference
- classroom_engine/analytics, auth/keycloak_auth
- alerts_agent/processing/rule_engine, ai_processor/print_versions

agent-core (5 files):
- brain/memory_store, brain/knowledge_graph, brain/context_manager
- orchestrator/supervisor, sessions/session_manager

admin-lehrer (5 components):
- GridOverlay, StepGridReview, DevOpsPipelineSidebar
- DataFlowDiagram, sbom/wizard/page

website (2 files):
- DependencyMap, lehrer/abitur-archiv

Other: nibis_ingestion, grid_detection_service, export-doclayout-onnx

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-04-25 09:41:42 +02:00
parent 451365a312
commit bd4b956e3c
113 changed files with 13790 additions and 14148 deletions

View File

@@ -32,7 +32,8 @@ from .models import (
)
from .fsm import LessonStateMachine
from .timer import PhaseTimer
from .suggestions import SuggestionEngine, PHASE_SUGGESTIONS, SUBJECT_SUGGESTIONS
from .suggestions import SuggestionEngine
from .suggestion_data import PHASE_SUGGESTIONS, SUBJECT_SUGGESTIONS
from .context_models import (
MacroPhaseEnum,
EventTypeEnum,

View File

@@ -11,256 +11,28 @@ WICHTIG: Keine wertenden Metriken (z.B. "Sie haben 70% geredet").
Fokus auf neutrale, hilfreiche Statistiken.
"""
from dataclasses import dataclass, field
from datetime import datetime, timedelta
from datetime import datetime
from typing import Optional, List, Dict, Any
from enum import Enum
from .analytics_models import (
PhaseStatistics,
SessionSummary,
TeacherAnalytics,
LessonReflection,
)
# ==================== Analytics Models ====================
# Re-export models for backward compatibility
__all__ = [
"PhaseStatistics",
"SessionSummary",
"TeacherAnalytics",
"LessonReflection",
"AnalyticsCalculator",
]
@dataclass
class PhaseStatistics:
"""Statistik fuer eine einzelne Phase."""
phase: str
display_name: str
# Dauer-Metriken
planned_duration_seconds: int
actual_duration_seconds: int
difference_seconds: int # positiv = laenger als geplant
# Overtime
had_overtime: bool
overtime_seconds: int = 0
# Erweiterungen
was_extended: bool = False
extension_minutes: int = 0
# Pausen
pause_count: int = 0
total_pause_seconds: int = 0
def to_dict(self) -> Dict[str, Any]:
return {
"phase": self.phase,
"display_name": self.display_name,
"planned_duration_seconds": self.planned_duration_seconds,
"actual_duration_seconds": self.actual_duration_seconds,
"difference_seconds": self.difference_seconds,
"difference_formatted": self._format_difference(),
"had_overtime": self.had_overtime,
"overtime_seconds": self.overtime_seconds,
"overtime_formatted": self._format_seconds(self.overtime_seconds),
"was_extended": self.was_extended,
"extension_minutes": self.extension_minutes,
"pause_count": self.pause_count,
"total_pause_seconds": self.total_pause_seconds,
}
def _format_difference(self) -> str:
"""Formatiert die Differenz als +/-MM:SS."""
prefix = "+" if self.difference_seconds >= 0 else ""
return f"{prefix}{self._format_seconds(abs(self.difference_seconds))}"
def _format_seconds(self, seconds: int) -> str:
"""Formatiert Sekunden als MM:SS."""
mins = seconds // 60
secs = seconds % 60
return f"{mins:02d}:{secs:02d}"
@dataclass
class SessionSummary:
"""
Zusammenfassung einer Unterrichtsstunde.
Wird nach Stundenende generiert und fuer das Lehrer-Dashboard verwendet.
"""
session_id: str
teacher_id: str
class_id: str
subject: str
topic: Optional[str]
date: datetime
# Dauer
total_duration_seconds: int
planned_duration_seconds: int
# Phasen-Statistiken
phases_completed: int
total_phases: int = 5
phase_statistics: List[PhaseStatistics] = field(default_factory=list)
# Overtime-Zusammenfassung
total_overtime_seconds: int = 0
phases_with_overtime: int = 0
# Pausen-Zusammenfassung
total_pause_count: int = 0
total_pause_seconds: int = 0
# Post-Lesson Reflection
reflection_notes: str = ""
reflection_rating: Optional[int] = None # 1-5 Sterne (optional)
key_learnings: List[str] = field(default_factory=list)
def to_dict(self) -> Dict[str, Any]:
return {
"session_id": self.session_id,
"teacher_id": self.teacher_id,
"class_id": self.class_id,
"subject": self.subject,
"topic": self.topic,
"date": self.date.isoformat() if self.date else None,
"date_formatted": self._format_date(),
"total_duration_seconds": self.total_duration_seconds,
"total_duration_formatted": self._format_seconds(self.total_duration_seconds),
"planned_duration_seconds": self.planned_duration_seconds,
"planned_duration_formatted": self._format_seconds(self.planned_duration_seconds),
"phases_completed": self.phases_completed,
"total_phases": self.total_phases,
"completion_percentage": round(self.phases_completed / self.total_phases * 100),
"phase_statistics": [p.to_dict() for p in self.phase_statistics],
"total_overtime_seconds": self.total_overtime_seconds,
"total_overtime_formatted": self._format_seconds(self.total_overtime_seconds),
"phases_with_overtime": self.phases_with_overtime,
"total_pause_count": self.total_pause_count,
"total_pause_seconds": self.total_pause_seconds,
"reflection_notes": self.reflection_notes,
"reflection_rating": self.reflection_rating,
"key_learnings": self.key_learnings,
}
def _format_seconds(self, seconds: int) -> str:
mins = seconds // 60
secs = seconds % 60
return f"{mins:02d}:{secs:02d}"
def _format_date(self) -> str:
if not self.date:
return ""
return self.date.strftime("%d.%m.%Y %H:%M")
@dataclass
class TeacherAnalytics:
"""
Aggregierte Statistiken fuer einen Lehrer.
Zeigt Trends und Muster ueber mehrere Stunden.
"""
teacher_id: str
period_start: datetime
period_end: datetime
# Stunden-Uebersicht
total_sessions: int = 0
completed_sessions: int = 0
total_teaching_minutes: int = 0
# Durchschnittliche Phasendauern
avg_phase_durations: Dict[str, float] = field(default_factory=dict)
# Overtime-Trends
sessions_with_overtime: int = 0
avg_overtime_seconds: float = 0
most_overtime_phase: Optional[str] = None
# Pausen-Statistik
avg_pause_count: float = 0
avg_pause_duration_seconds: float = 0
# Faecher-Verteilung
subjects_taught: Dict[str, int] = field(default_factory=dict)
# Klassen-Verteilung
classes_taught: Dict[str, int] = field(default_factory=dict)
def to_dict(self) -> Dict[str, Any]:
return {
"teacher_id": self.teacher_id,
"period_start": self.period_start.isoformat() if self.period_start else None,
"period_end": self.period_end.isoformat() if self.period_end else None,
"total_sessions": self.total_sessions,
"completed_sessions": self.completed_sessions,
"total_teaching_minutes": self.total_teaching_minutes,
"total_teaching_hours": round(self.total_teaching_minutes / 60, 1),
"avg_phase_durations": self.avg_phase_durations,
"sessions_with_overtime": self.sessions_with_overtime,
"overtime_percentage": round(self.sessions_with_overtime / max(self.total_sessions, 1) * 100),
"avg_overtime_seconds": round(self.avg_overtime_seconds),
"avg_overtime_formatted": self._format_seconds(int(self.avg_overtime_seconds)),
"most_overtime_phase": self.most_overtime_phase,
"avg_pause_count": round(self.avg_pause_count, 1),
"avg_pause_duration_seconds": round(self.avg_pause_duration_seconds),
"subjects_taught": self.subjects_taught,
"classes_taught": self.classes_taught,
}
def _format_seconds(self, seconds: int) -> str:
mins = seconds // 60
secs = seconds % 60
return f"{mins:02d}:{secs:02d}"
# ==================== Reflection Model ====================
@dataclass
class LessonReflection:
"""
Post-Lesson Reflection (Feature).
Ermoeglicht Lehrern, nach der Stunde Notizen zu machen.
Keine Bewertung, nur Reflexion.
"""
reflection_id: str
session_id: str
teacher_id: str
# Reflexionsnotizen
notes: str = ""
# Optional: Sterne-Bewertung (selbst-eingeschaetzt)
overall_rating: Optional[int] = None # 1-5
# Was hat gut funktioniert?
what_worked: List[str] = field(default_factory=list)
# Was wuerde ich naechstes Mal anders machen?
improvements: List[str] = field(default_factory=list)
# Notizen fuer naechste Stunde
notes_for_next_lesson: str = ""
created_at: Optional[datetime] = None
updated_at: Optional[datetime] = None
def to_dict(self) -> Dict[str, Any]:
return {
"reflection_id": self.reflection_id,
"session_id": self.session_id,
"teacher_id": self.teacher_id,
"notes": self.notes,
"overall_rating": self.overall_rating,
"what_worked": self.what_worked,
"improvements": self.improvements,
"notes_for_next_lesson": self.notes_for_next_lesson,
"created_at": self.created_at.isoformat() if self.created_at else None,
"updated_at": self.updated_at.isoformat() if self.updated_at else None,
}
# ==================== Analytics Calculator ====================
class AnalyticsCalculator:
"""
Berechnet Analytics aus Session-Daten.
Verwendet In-Memory-Daten oder DB-Daten.
"""
"""Berechnet Analytics aus Session-Daten."""
PHASE_DISPLAY_NAMES = {
"einstieg": "Einstieg",
@@ -276,24 +48,13 @@ class AnalyticsCalculator:
session_data: Dict[str, Any],
phase_history: List[Dict[str, Any]]
) -> SessionSummary:
"""
Berechnet die Zusammenfassung einer Session.
Args:
session_data: Session-Dictionary (aus LessonSession.to_dict())
phase_history: Liste der Phasen-History-Eintraege
Returns:
SessionSummary mit allen berechneten Statistiken
"""
# Basis-Daten
"""Berechnet die Zusammenfassung einer Session."""
session_id = session_data.get("session_id", "")
teacher_id = session_data.get("teacher_id", "")
class_id = session_data.get("class_id", "")
subject = session_data.get("subject", "")
topic = session_data.get("topic")
# Timestamps
lesson_started = session_data.get("lesson_started_at")
lesson_ended = session_data.get("lesson_ended_at")
@@ -302,16 +63,13 @@ class AnalyticsCalculator:
if isinstance(lesson_ended, str):
lesson_ended = datetime.fromisoformat(lesson_ended.replace("Z", "+00:00"))
# Dauer berechnen
total_duration = 0
if lesson_started and lesson_ended:
total_duration = int((lesson_ended - lesson_started).total_seconds())
# Geplante Dauer
phase_durations = session_data.get("phase_durations", {})
planned_duration = sum(phase_durations.values()) * 60 # Minuten zu Sekunden
planned_duration = sum(phase_durations.values()) * 60
# Phasen-Statistiken berechnen
phase_stats = []
total_overtime = 0
phases_with_overtime = 0
@@ -324,18 +82,10 @@ class AnalyticsCalculator:
if phase in ["not_started", "ended"]:
continue
# Geplante Dauer fuer diese Phase
planned_seconds = phase_durations.get(phase, 0) * 60
# Tatsaechliche Dauer
actual_seconds = entry.get("duration_seconds", 0)
if actual_seconds is None:
actual_seconds = 0
# Differenz
actual_seconds = entry.get("duration_seconds", 0) or 0
difference = actual_seconds - planned_seconds
# Overtime (nur positive Differenz zaehlt)
had_overtime = difference > 0
overtime_seconds = max(0, difference)
@@ -343,13 +93,11 @@ class AnalyticsCalculator:
total_overtime += overtime_seconds
phases_with_overtime += 1
# Pausen
pause_count = entry.get("pause_count", 0) or 0
pause_seconds = entry.get("total_pause_seconds", 0) or 0
total_pause_count += pause_count
total_pause_seconds += pause_seconds
# Phase als abgeschlossen zaehlen
if entry.get("ended_at"):
phases_completed += 1
@@ -368,16 +116,12 @@ class AnalyticsCalculator:
))
return SessionSummary(
session_id=session_id,
teacher_id=teacher_id,
class_id=class_id,
subject=subject,
topic=topic,
session_id=session_id, teacher_id=teacher_id,
class_id=class_id, subject=subject, topic=topic,
date=lesson_started or datetime.now(),
total_duration_seconds=total_duration,
planned_duration_seconds=planned_duration,
phases_completed=phases_completed,
total_phases=5,
phases_completed=phases_completed, total_phases=5,
phase_statistics=phase_stats,
total_overtime_seconds=total_overtime,
phases_with_overtime=phases_with_overtime,
@@ -392,31 +136,15 @@ class AnalyticsCalculator:
period_start: datetime,
period_end: datetime
) -> TeacherAnalytics:
"""
Berechnet aggregierte Statistiken fuer einen Lehrer.
Args:
sessions: Liste von Session-Dictionaries
period_start: Beginn des Zeitraums
period_end: Ende des Zeitraums
Returns:
TeacherAnalytics mit aggregierten Statistiken
"""
"""Berechnet aggregierte Statistiken fuer einen Lehrer."""
if not sessions:
return TeacherAnalytics(
teacher_id="",
period_start=period_start,
period_end=period_end,
)
return TeacherAnalytics(teacher_id="", period_start=period_start, period_end=period_end)
teacher_id = sessions[0].get("teacher_id", "")
# Basis-Zaehler
total_sessions = len(sessions)
completed_sessions = sum(1 for s in sessions if s.get("lesson_ended_at"))
# Gesamtdauer berechnen
total_minutes = 0
for session in sessions:
started = session.get("lesson_started_at")
@@ -428,41 +156,29 @@ class AnalyticsCalculator:
ended = datetime.fromisoformat(ended.replace("Z", "+00:00"))
total_minutes += (ended - started).total_seconds() / 60
# Durchschnittliche Phasendauern
phase_durations_sum: Dict[str, List[int]] = {
"einstieg": [],
"erarbeitung": [],
"sicherung": [],
"transfer": [],
"reflexion": [],
"einstieg": [], "erarbeitung": [], "sicherung": [],
"transfer": [], "reflexion": [],
}
# Overtime-Tracking
overtime_count = 0
overtime_seconds_total = 0
phase_overtime: Dict[str, int] = {}
# Pausen-Tracking
pause_counts = []
pause_durations = []
# Faecher und Klassen
subjects: Dict[str, int] = {}
classes: Dict[str, int] = {}
for session in sessions:
# Fach und Klasse zaehlen
subject = session.get("subject", "")
class_id = session.get("class_id", "")
subjects[subject] = subjects.get(subject, 0) + 1
classes[class_id] = classes.get(class_id, 0) + 1
# Phase History analysieren
history = session.get("phase_history", [])
session_has_overtime = False
session_pause_count = 0
session_pause_duration = 0
phase_durations_dict = session.get("phase_durations", {})
for entry in history:
@@ -471,7 +187,6 @@ class AnalyticsCalculator:
duration = entry.get("duration_seconds", 0) or 0
phase_durations_sum[phase].append(duration)
# Overtime berechnen
planned = phase_durations_dict.get(phase, 0) * 60
if duration > planned:
overtime = duration - planned
@@ -479,35 +194,25 @@ class AnalyticsCalculator:
session_has_overtime = True
phase_overtime[phase] = phase_overtime.get(phase, 0) + overtime
# Pausen zaehlen
session_pause_count += entry.get("pause_count", 0) or 0
session_pause_duration += entry.get("total_pause_seconds", 0) or 0
if session_has_overtime:
overtime_count += 1
pause_counts.append(session_pause_count)
pause_durations.append(session_pause_duration)
# Durchschnitte berechnen
avg_durations = {}
for phase, durations in phase_durations_sum.items():
if durations:
avg_durations[phase] = round(sum(durations) / len(durations))
else:
avg_durations[phase] = 0
avg_durations[phase] = round(sum(durations) / len(durations)) if durations else 0
# Phase mit meistem Overtime finden
most_overtime_phase = None
if phase_overtime:
most_overtime_phase = max(phase_overtime, key=phase_overtime.get)
return TeacherAnalytics(
teacher_id=teacher_id,
period_start=period_start,
period_end=period_end,
total_sessions=total_sessions,
completed_sessions=completed_sessions,
teacher_id=teacher_id, period_start=period_start, period_end=period_end,
total_sessions=total_sessions, completed_sessions=completed_sessions,
total_teaching_minutes=int(total_minutes),
avg_phase_durations=avg_durations,
sessions_with_overtime=overtime_count,
@@ -515,6 +220,5 @@ class AnalyticsCalculator:
most_overtime_phase=most_overtime_phase,
avg_pause_count=sum(pause_counts) / max(len(pause_counts), 1),
avg_pause_duration_seconds=sum(pause_durations) / max(len(pause_durations), 1),
subjects_taught=subjects,
classes_taught=classes,
subjects_taught=subjects, classes_taught=classes,
)

View File

@@ -0,0 +1,205 @@
"""
Analytics Models - Datenstrukturen fuer Classroom Analytics.
Enthaelt PhaseStatistics, SessionSummary, TeacherAnalytics, LessonReflection.
"""
from dataclasses import dataclass, field
from datetime import datetime
from typing import Optional, List, Dict, Any
@dataclass
class PhaseStatistics:
"""Statistik fuer eine einzelne Phase."""
phase: str
display_name: str
# Dauer-Metriken
planned_duration_seconds: int
actual_duration_seconds: int
difference_seconds: int # positiv = laenger als geplant
# Overtime
had_overtime: bool
overtime_seconds: int = 0
# Erweiterungen
was_extended: bool = False
extension_minutes: int = 0
# Pausen
pause_count: int = 0
total_pause_seconds: int = 0
def to_dict(self) -> Dict[str, Any]:
return {
"phase": self.phase,
"display_name": self.display_name,
"planned_duration_seconds": self.planned_duration_seconds,
"actual_duration_seconds": self.actual_duration_seconds,
"difference_seconds": self.difference_seconds,
"difference_formatted": self._format_difference(),
"had_overtime": self.had_overtime,
"overtime_seconds": self.overtime_seconds,
"overtime_formatted": self._format_seconds(self.overtime_seconds),
"was_extended": self.was_extended,
"extension_minutes": self.extension_minutes,
"pause_count": self.pause_count,
"total_pause_seconds": self.total_pause_seconds,
}
def _format_difference(self) -> str:
prefix = "+" if self.difference_seconds >= 0 else ""
return f"{prefix}{self._format_seconds(abs(self.difference_seconds))}"
def _format_seconds(self, seconds: int) -> str:
mins = seconds // 60
secs = seconds % 60
return f"{mins:02d}:{secs:02d}"
@dataclass
class SessionSummary:
"""Zusammenfassung einer Unterrichtsstunde."""
session_id: str
teacher_id: str
class_id: str
subject: str
topic: Optional[str]
date: datetime
total_duration_seconds: int
planned_duration_seconds: int
phases_completed: int
total_phases: int = 5
phase_statistics: List[PhaseStatistics] = field(default_factory=list)
total_overtime_seconds: int = 0
phases_with_overtime: int = 0
total_pause_count: int = 0
total_pause_seconds: int = 0
reflection_notes: str = ""
reflection_rating: Optional[int] = None
key_learnings: List[str] = field(default_factory=list)
def to_dict(self) -> Dict[str, Any]:
return {
"session_id": self.session_id,
"teacher_id": self.teacher_id,
"class_id": self.class_id,
"subject": self.subject,
"topic": self.topic,
"date": self.date.isoformat() if self.date else None,
"date_formatted": self._format_date(),
"total_duration_seconds": self.total_duration_seconds,
"total_duration_formatted": self._format_seconds(self.total_duration_seconds),
"planned_duration_seconds": self.planned_duration_seconds,
"planned_duration_formatted": self._format_seconds(self.planned_duration_seconds),
"phases_completed": self.phases_completed,
"total_phases": self.total_phases,
"completion_percentage": round(self.phases_completed / self.total_phases * 100),
"phase_statistics": [p.to_dict() for p in self.phase_statistics],
"total_overtime_seconds": self.total_overtime_seconds,
"total_overtime_formatted": self._format_seconds(self.total_overtime_seconds),
"phases_with_overtime": self.phases_with_overtime,
"total_pause_count": self.total_pause_count,
"total_pause_seconds": self.total_pause_seconds,
"reflection_notes": self.reflection_notes,
"reflection_rating": self.reflection_rating,
"key_learnings": self.key_learnings,
}
def _format_seconds(self, seconds: int) -> str:
mins = seconds // 60
secs = seconds % 60
return f"{mins:02d}:{secs:02d}"
def _format_date(self) -> str:
if not self.date:
return ""
return self.date.strftime("%d.%m.%Y %H:%M")
@dataclass
class TeacherAnalytics:
"""Aggregierte Statistiken fuer einen Lehrer."""
teacher_id: str
period_start: datetime
period_end: datetime
total_sessions: int = 0
completed_sessions: int = 0
total_teaching_minutes: int = 0
avg_phase_durations: Dict[str, float] = field(default_factory=dict)
sessions_with_overtime: int = 0
avg_overtime_seconds: float = 0
most_overtime_phase: Optional[str] = None
avg_pause_count: float = 0
avg_pause_duration_seconds: float = 0
subjects_taught: Dict[str, int] = field(default_factory=dict)
classes_taught: Dict[str, int] = field(default_factory=dict)
def to_dict(self) -> Dict[str, Any]:
return {
"teacher_id": self.teacher_id,
"period_start": self.period_start.isoformat() if self.period_start else None,
"period_end": self.period_end.isoformat() if self.period_end else None,
"total_sessions": self.total_sessions,
"completed_sessions": self.completed_sessions,
"total_teaching_minutes": self.total_teaching_minutes,
"total_teaching_hours": round(self.total_teaching_minutes / 60, 1),
"avg_phase_durations": self.avg_phase_durations,
"sessions_with_overtime": self.sessions_with_overtime,
"overtime_percentage": round(self.sessions_with_overtime / max(self.total_sessions, 1) * 100),
"avg_overtime_seconds": round(self.avg_overtime_seconds),
"avg_overtime_formatted": self._format_seconds(int(self.avg_overtime_seconds)),
"most_overtime_phase": self.most_overtime_phase,
"avg_pause_count": round(self.avg_pause_count, 1),
"avg_pause_duration_seconds": round(self.avg_pause_duration_seconds),
"subjects_taught": self.subjects_taught,
"classes_taught": self.classes_taught,
}
def _format_seconds(self, seconds: int) -> str:
mins = seconds // 60
secs = seconds % 60
return f"{mins:02d}:{secs:02d}"
@dataclass
class LessonReflection:
"""Post-Lesson Reflection (Feature)."""
reflection_id: str
session_id: str
teacher_id: str
notes: str = ""
overall_rating: Optional[int] = None
what_worked: List[str] = field(default_factory=list)
improvements: List[str] = field(default_factory=list)
notes_for_next_lesson: str = ""
created_at: Optional[datetime] = None
updated_at: Optional[datetime] = None
def to_dict(self) -> Dict[str, Any]:
return {
"reflection_id": self.reflection_id,
"session_id": self.session_id,
"teacher_id": self.teacher_id,
"notes": self.notes,
"overall_rating": self.overall_rating,
"what_worked": self.what_worked,
"improvements": self.improvements,
"notes_for_next_lesson": self.notes_for_next_lesson,
"created_at": self.created_at.isoformat() if self.created_at else None,
"updated_at": self.updated_at.isoformat() if self.updated_at else None,
}

View File

@@ -0,0 +1,494 @@
"""
Phasenspezifische und fachspezifische Vorschlags-Daten (Feature f18).
Enthaelt die vordefinierten Vorschlaege fuer allgemeine Phasen
und fachspezifische Aktivitaeten.
"""
from typing import List, Dict, Any
from .models import LessonPhase
# Unterstuetzte Faecher fuer fachspezifische Vorschlaege
SUPPORTED_SUBJECTS = [
"mathematik", "mathe", "math",
"deutsch",
"englisch", "english",
"biologie", "bio",
"physik",
"chemie",
"geschichte",
"geografie", "erdkunde",
"kunst",
"musik",
"sport",
"informatik",
]
# Fachspezifische Vorschlaege (Feature f18)
SUBJECT_SUGGESTIONS: Dict[str, Dict[LessonPhase, List[Dict[str, Any]]]] = {
"mathematik": {
LessonPhase.EINSTIEG: [
{
"id": "math_warm_up",
"title": "Kopfrechnen-Challenge",
"description": "5 schnelle Kopfrechenaufgaben zum Aufwaermen",
"activity_type": "warmup",
"estimated_minutes": 3,
"icon": "calculate",
"subjects": ["mathematik", "mathe"],
},
{
"id": "math_puzzle",
"title": "Mathematisches Raetsel",
"description": "Ein kniffliges Zahlenraetsel als Einstieg",
"activity_type": "motivation",
"estimated_minutes": 5,
"icon": "extension",
"subjects": ["mathematik", "mathe"],
},
],
LessonPhase.ERARBEITUNG: [
{
"id": "math_geogebra",
"title": "GeoGebra-Exploration",
"description": "Interaktive Visualisierung mit GeoGebra",
"activity_type": "individual_work",
"estimated_minutes": 15,
"icon": "functions",
"subjects": ["mathematik", "mathe"],
},
{
"id": "math_peer_explain",
"title": "Rechenweg erklaeren",
"description": "Schueler erklaeren sich gegenseitig ihre Loesungswege",
"activity_type": "partner_work",
"estimated_minutes": 10,
"icon": "groups",
"subjects": ["mathematik", "mathe"],
},
],
LessonPhase.SICHERUNG: [
{
"id": "math_formula_card",
"title": "Formelkarte erstellen",
"description": "Wichtigste Formeln auf einer Karte festhalten",
"activity_type": "documentation",
"estimated_minutes": 5,
"icon": "note_alt",
"subjects": ["mathematik", "mathe"],
},
],
},
"deutsch": {
LessonPhase.EINSTIEG: [
{
"id": "deutsch_wordle",
"title": "Wordle-Variante",
"description": "Wort des Tages erraten",
"activity_type": "warmup",
"estimated_minutes": 4,
"icon": "abc",
"subjects": ["deutsch"],
},
{
"id": "deutsch_zitat",
"title": "Zitat-Interpretation",
"description": "Ein literarisches Zitat gemeinsam deuten",
"activity_type": "motivation",
"estimated_minutes": 5,
"icon": "format_quote",
"subjects": ["deutsch"],
},
],
LessonPhase.ERARBEITUNG: [
{
"id": "deutsch_textarbeit",
"title": "Textanalyse in Gruppen",
"description": "Gruppenarbeit zu verschiedenen Textabschnitten",
"activity_type": "group_work",
"estimated_minutes": 15,
"icon": "menu_book",
"subjects": ["deutsch"],
},
{
"id": "deutsch_schreibworkshop",
"title": "Schreibwerkstatt",
"description": "Kreatives Schreiben mit Peer-Feedback",
"activity_type": "individual_work",
"estimated_minutes": 20,
"icon": "edit_note",
"subjects": ["deutsch"],
},
],
LessonPhase.SICHERUNG: [
{
"id": "deutsch_zusammenfassung",
"title": "Text-Zusammenfassung",
"description": "Die wichtigsten Punkte in 3 Saetzen formulieren",
"activity_type": "summary",
"estimated_minutes": 5,
"icon": "summarize",
"subjects": ["deutsch"],
},
],
},
"englisch": {
LessonPhase.EINSTIEG: [
{
"id": "english_smalltalk",
"title": "Small Talk Warm-Up",
"description": "2-Minuten Gespraeche zu einem Alltagsthema",
"activity_type": "warmup",
"estimated_minutes": 4,
"icon": "chat",
"subjects": ["englisch", "english"],
},
{
"id": "english_video",
"title": "Authentic Video Clip",
"description": "Kurzer Clip aus einer englischen Serie oder Nachricht",
"activity_type": "motivation",
"estimated_minutes": 5,
"icon": "movie",
"subjects": ["englisch", "english"],
},
],
LessonPhase.ERARBEITUNG: [
{
"id": "english_role_play",
"title": "Role Play Activity",
"description": "Dialoguebung in authentischen Situationen",
"activity_type": "partner_work",
"estimated_minutes": 12,
"icon": "theater_comedy",
"subjects": ["englisch", "english"],
},
{
"id": "english_reading_circle",
"title": "Reading Circle",
"description": "Gemeinsames Lesen mit verteilten Rollen",
"activity_type": "group_work",
"estimated_minutes": 15,
"icon": "auto_stories",
"subjects": ["englisch", "english"],
},
],
},
"biologie": {
LessonPhase.EINSTIEG: [
{
"id": "bio_nature_question",
"title": "Naturfrage",
"description": "Eine spannende Frage aus der Natur diskutieren",
"activity_type": "motivation",
"estimated_minutes": 5,
"icon": "eco",
"subjects": ["biologie", "bio"],
},
],
LessonPhase.ERARBEITUNG: [
{
"id": "bio_experiment",
"title": "Mini-Experiment",
"description": "Einfaches Experiment zum Thema durchfuehren",
"activity_type": "group_work",
"estimated_minutes": 20,
"icon": "science",
"subjects": ["biologie", "bio"],
},
{
"id": "bio_diagram",
"title": "Biologische Zeichnung",
"description": "Beschriftete Zeichnung eines Organismus",
"activity_type": "individual_work",
"estimated_minutes": 15,
"icon": "draw",
"subjects": ["biologie", "bio"],
},
],
},
"physik": {
LessonPhase.EINSTIEG: [
{
"id": "physik_demo",
"title": "Phaenomen-Demo",
"description": "Ein physikalisches Phaenomen vorfuehren",
"activity_type": "motivation",
"estimated_minutes": 5,
"icon": "bolt",
"subjects": ["physik"],
},
],
LessonPhase.ERARBEITUNG: [
{
"id": "physik_simulation",
"title": "PhET-Simulation",
"description": "Interaktive Simulation von phet.colorado.edu",
"activity_type": "individual_work",
"estimated_minutes": 15,
"icon": "smart_toy",
"subjects": ["physik"],
},
{
"id": "physik_rechnung",
"title": "Physikalische Rechnung",
"description": "Rechenaufgabe mit physikalischem Kontext",
"activity_type": "partner_work",
"estimated_minutes": 12,
"icon": "calculate",
"subjects": ["physik"],
},
],
},
"informatik": {
LessonPhase.EINSTIEG: [
{
"id": "info_code_puzzle",
"title": "Code-Puzzle",
"description": "Kurzen Code-Schnipsel analysieren - was macht er?",
"activity_type": "warmup",
"estimated_minutes": 4,
"icon": "code",
"subjects": ["informatik"],
},
],
LessonPhase.ERARBEITUNG: [
{
"id": "info_live_coding",
"title": "Live Coding",
"description": "Gemeinsam Code entwickeln mit Erklaerungen",
"activity_type": "instruction",
"estimated_minutes": 15,
"icon": "terminal",
"subjects": ["informatik"],
},
{
"id": "info_pair_programming",
"title": "Pair Programming",
"description": "Zu zweit programmieren - Driver und Navigator",
"activity_type": "partner_work",
"estimated_minutes": 20,
"icon": "computer",
"subjects": ["informatik"],
},
],
},
}
# Vordefinierte allgemeine Vorschlaege pro Phase
PHASE_SUGGESTIONS: Dict[LessonPhase, List[Dict[str, Any]]] = {
LessonPhase.EINSTIEG: [
{
"id": "warmup_quiz",
"title": "Kurzes Quiz zum Einstieg",
"description": "Aktivieren Sie das Vorwissen der Schueler mit 3-5 Fragen zum Thema",
"activity_type": "warmup",
"estimated_minutes": 3,
"icon": "quiz"
},
{
"id": "problem_story",
"title": "Problemgeschichte erzaehlen",
"description": "Stellen Sie ein alltagsnahes Problem vor, das zum Thema fuehrt",
"activity_type": "motivation",
"estimated_minutes": 5,
"icon": "auto_stories"
},
{
"id": "video_intro",
"title": "Kurzes Erklaervideo",
"description": "Zeigen Sie ein 2-3 Minuten Video zur Einfuehrung ins Thema",
"activity_type": "motivation",
"estimated_minutes": 4,
"icon": "play_circle"
},
{
"id": "brainstorming",
"title": "Brainstorming",
"description": "Sammeln Sie Ideen und Vorkenntnisse der Schueler an der Tafel",
"activity_type": "warmup",
"estimated_minutes": 5,
"icon": "psychology"
},
{
"id": "daily_challenge",
"title": "Tagesaufgabe vorstellen",
"description": "Praesentieren Sie die zentrale Frage oder Aufgabe der Stunde",
"activity_type": "problem_introduction",
"estimated_minutes": 3,
"icon": "flag"
}
],
LessonPhase.ERARBEITUNG: [
{
"id": "think_pair_share",
"title": "Think-Pair-Share",
"description": "Schueler denken erst einzeln nach, tauschen sich dann zu zweit aus und praesentieren im Plenum",
"activity_type": "partner_work",
"estimated_minutes": 10,
"icon": "groups"
},
{
"id": "worksheet_digital",
"title": "Digitales Arbeitsblatt",
"description": "Schueler bearbeiten ein interaktives Arbeitsblatt am Tablet oder Computer",
"activity_type": "individual_work",
"estimated_minutes": 15,
"icon": "description"
},
{
"id": "station_learning",
"title": "Stationenlernen",
"description": "Verschiedene Stationen mit unterschiedlichen Aufgaben und Materialien",
"activity_type": "group_work",
"estimated_minutes": 20,
"icon": "hub"
},
{
"id": "expert_puzzle",
"title": "Expertenrunde (Jigsaw)",
"description": "Schueler werden Experten fuer ein Teilthema und lehren es anderen",
"activity_type": "group_work",
"estimated_minutes": 15,
"icon": "extension"
},
{
"id": "guided_instruction",
"title": "Geleitete Instruktion",
"description": "Schrittweise Erklaerung mit Uebungsphasen zwischendurch",
"activity_type": "instruction",
"estimated_minutes": 12,
"icon": "school"
},
{
"id": "pair_programming",
"title": "Partnerarbeit",
"description": "Zwei Schueler loesen gemeinsam eine Aufgabe",
"activity_type": "partner_work",
"estimated_minutes": 10,
"icon": "people"
}
],
LessonPhase.SICHERUNG: [
{
"id": "mindmap_class",
"title": "Gemeinsame Mindmap",
"description": "Ergebnisse als Mindmap an der Tafel oder digital sammeln und strukturieren",
"activity_type": "visualization",
"estimated_minutes": 8,
"icon": "account_tree"
},
{
"id": "exit_ticket",
"title": "Exit Ticket",
"description": "Schueler notieren 3 Dinge die sie gelernt haben und 1 offene Frage",
"activity_type": "summary",
"estimated_minutes": 5,
"icon": "sticky_note_2"
},
{
"id": "gallery_walk",
"title": "Galerie-Rundgang",
"description": "Schueler praesentieren ihre Ergebnisse und geben sich Feedback",
"activity_type": "presentation",
"estimated_minutes": 10,
"icon": "photo_library"
},
{
"id": "key_points",
"title": "Kernpunkte zusammenfassen",
"description": "Gemeinsam die wichtigsten Erkenntnisse der Stunde formulieren",
"activity_type": "summary",
"estimated_minutes": 5,
"icon": "format_list_bulleted"
},
{
"id": "quick_check",
"title": "Schneller Wissenscheck",
"description": "5 kurze Fragen zur Ueberpruefung des Verstaendnisses",
"activity_type": "documentation",
"estimated_minutes": 5,
"icon": "fact_check"
}
],
LessonPhase.TRANSFER: [
{
"id": "real_world_example",
"title": "Alltagsbeispiele finden",
"description": "Schueler suchen Beispiele aus ihrem Alltag, wo das Gelernte vorkommt",
"activity_type": "application",
"estimated_minutes": 5,
"icon": "public"
},
{
"id": "challenge_task",
"title": "Knobelaufgabe",
"description": "Eine anspruchsvollere Aufgabe fuer schnelle Schueler oder als Bonus",
"activity_type": "differentiation",
"estimated_minutes": 7,
"icon": "psychology"
},
{
"id": "creative_application",
"title": "Kreative Anwendung",
"description": "Schueler wenden das Gelernte in einem kreativen Projekt an",
"activity_type": "application",
"estimated_minutes": 10,
"icon": "palette"
},
{
"id": "peer_teaching",
"title": "Peer-Teaching",
"description": "Schueler erklaeren sich gegenseitig das Gelernte",
"activity_type": "real_world_connection",
"estimated_minutes": 5,
"icon": "supervisor_account"
}
],
LessonPhase.REFLEXION: [
{
"id": "thumbs_feedback",
"title": "Daumen-Feedback",
"description": "Schnelle Stimmungsabfrage: Daumen hoch/mitte/runter",
"activity_type": "feedback",
"estimated_minutes": 2,
"icon": "thumb_up"
},
{
"id": "homework_assign",
"title": "Hausaufgabe vergeben",
"description": "Passende Hausaufgabe zur Vertiefung des Gelernten",
"activity_type": "homework",
"estimated_minutes": 3,
"icon": "home_work"
},
{
"id": "one_word",
"title": "Ein-Wort-Reflexion",
"description": "Jeder Schueler nennt ein Wort, das die Stunde beschreibt",
"activity_type": "feedback",
"estimated_minutes": 3,
"icon": "chat"
},
{
"id": "preview_next",
"title": "Ausblick naechste Stunde",
"description": "Kurzer Ausblick auf das Thema der naechsten Stunde",
"activity_type": "preview",
"estimated_minutes": 2,
"icon": "event"
},
{
"id": "learning_log",
"title": "Lerntagebuch",
"description": "Schueler notieren ihre wichtigsten Erkenntnisse im Lerntagebuch",
"activity_type": "feedback",
"estimated_minutes": 4,
"icon": "menu_book"
}
]
}

View File

@@ -8,490 +8,11 @@ und optional dem Fach.
from typing import List, Dict, Any, Optional
from .models import LessonPhase, LessonSession, PhaseSuggestion
# Unterstuetzte Faecher fuer fachspezifische Vorschlaege
SUPPORTED_SUBJECTS = [
"mathematik", "mathe", "math",
"deutsch",
"englisch", "english",
"biologie", "bio",
"physik",
"chemie",
"geschichte",
"geografie", "erdkunde",
"kunst",
"musik",
"sport",
"informatik",
]
# Fachspezifische Vorschlaege (Feature f18)
SUBJECT_SUGGESTIONS: Dict[str, Dict[LessonPhase, List[Dict[str, Any]]]] = {
"mathematik": {
LessonPhase.EINSTIEG: [
{
"id": "math_warm_up",
"title": "Kopfrechnen-Challenge",
"description": "5 schnelle Kopfrechenaufgaben zum Aufwaermen",
"activity_type": "warmup",
"estimated_minutes": 3,
"icon": "calculate",
"subjects": ["mathematik", "mathe"],
},
{
"id": "math_puzzle",
"title": "Mathematisches Raetsel",
"description": "Ein kniffliges Zahlenraetsel als Einstieg",
"activity_type": "motivation",
"estimated_minutes": 5,
"icon": "extension",
"subjects": ["mathematik", "mathe"],
},
],
LessonPhase.ERARBEITUNG: [
{
"id": "math_geogebra",
"title": "GeoGebra-Exploration",
"description": "Interaktive Visualisierung mit GeoGebra",
"activity_type": "individual_work",
"estimated_minutes": 15,
"icon": "functions",
"subjects": ["mathematik", "mathe"],
},
{
"id": "math_peer_explain",
"title": "Rechenweg erklaeren",
"description": "Schueler erklaeren sich gegenseitig ihre Loesungswege",
"activity_type": "partner_work",
"estimated_minutes": 10,
"icon": "groups",
"subjects": ["mathematik", "mathe"],
},
],
LessonPhase.SICHERUNG: [
{
"id": "math_formula_card",
"title": "Formelkarte erstellen",
"description": "Wichtigste Formeln auf einer Karte festhalten",
"activity_type": "documentation",
"estimated_minutes": 5,
"icon": "note_alt",
"subjects": ["mathematik", "mathe"],
},
],
},
"deutsch": {
LessonPhase.EINSTIEG: [
{
"id": "deutsch_wordle",
"title": "Wordle-Variante",
"description": "Wort des Tages erraten",
"activity_type": "warmup",
"estimated_minutes": 4,
"icon": "abc",
"subjects": ["deutsch"],
},
{
"id": "deutsch_zitat",
"title": "Zitat-Interpretation",
"description": "Ein literarisches Zitat gemeinsam deuten",
"activity_type": "motivation",
"estimated_minutes": 5,
"icon": "format_quote",
"subjects": ["deutsch"],
},
],
LessonPhase.ERARBEITUNG: [
{
"id": "deutsch_textarbeit",
"title": "Textanalyse in Gruppen",
"description": "Gruppenarbeit zu verschiedenen Textabschnitten",
"activity_type": "group_work",
"estimated_minutes": 15,
"icon": "menu_book",
"subjects": ["deutsch"],
},
{
"id": "deutsch_schreibworkshop",
"title": "Schreibwerkstatt",
"description": "Kreatives Schreiben mit Peer-Feedback",
"activity_type": "individual_work",
"estimated_minutes": 20,
"icon": "edit_note",
"subjects": ["deutsch"],
},
],
LessonPhase.SICHERUNG: [
{
"id": "deutsch_zusammenfassung",
"title": "Text-Zusammenfassung",
"description": "Die wichtigsten Punkte in 3 Saetzen formulieren",
"activity_type": "summary",
"estimated_minutes": 5,
"icon": "summarize",
"subjects": ["deutsch"],
},
],
},
"englisch": {
LessonPhase.EINSTIEG: [
{
"id": "english_smalltalk",
"title": "Small Talk Warm-Up",
"description": "2-Minuten Gespraeche zu einem Alltagsthema",
"activity_type": "warmup",
"estimated_minutes": 4,
"icon": "chat",
"subjects": ["englisch", "english"],
},
{
"id": "english_video",
"title": "Authentic Video Clip",
"description": "Kurzer Clip aus einer englischen Serie oder Nachricht",
"activity_type": "motivation",
"estimated_minutes": 5,
"icon": "movie",
"subjects": ["englisch", "english"],
},
],
LessonPhase.ERARBEITUNG: [
{
"id": "english_role_play",
"title": "Role Play Activity",
"description": "Dialoguebung in authentischen Situationen",
"activity_type": "partner_work",
"estimated_minutes": 12,
"icon": "theater_comedy",
"subjects": ["englisch", "english"],
},
{
"id": "english_reading_circle",
"title": "Reading Circle",
"description": "Gemeinsames Lesen mit verteilten Rollen",
"activity_type": "group_work",
"estimated_minutes": 15,
"icon": "auto_stories",
"subjects": ["englisch", "english"],
},
],
},
"biologie": {
LessonPhase.EINSTIEG: [
{
"id": "bio_nature_question",
"title": "Naturfrage",
"description": "Eine spannende Frage aus der Natur diskutieren",
"activity_type": "motivation",
"estimated_minutes": 5,
"icon": "eco",
"subjects": ["biologie", "bio"],
},
],
LessonPhase.ERARBEITUNG: [
{
"id": "bio_experiment",
"title": "Mini-Experiment",
"description": "Einfaches Experiment zum Thema durchfuehren",
"activity_type": "group_work",
"estimated_minutes": 20,
"icon": "science",
"subjects": ["biologie", "bio"],
},
{
"id": "bio_diagram",
"title": "Biologische Zeichnung",
"description": "Beschriftete Zeichnung eines Organismus",
"activity_type": "individual_work",
"estimated_minutes": 15,
"icon": "draw",
"subjects": ["biologie", "bio"],
},
],
},
"physik": {
LessonPhase.EINSTIEG: [
{
"id": "physik_demo",
"title": "Phaenomen-Demo",
"description": "Ein physikalisches Phaenomen vorfuehren",
"activity_type": "motivation",
"estimated_minutes": 5,
"icon": "bolt",
"subjects": ["physik"],
},
],
LessonPhase.ERARBEITUNG: [
{
"id": "physik_simulation",
"title": "PhET-Simulation",
"description": "Interaktive Simulation von phet.colorado.edu",
"activity_type": "individual_work",
"estimated_minutes": 15,
"icon": "smart_toy",
"subjects": ["physik"],
},
{
"id": "physik_rechnung",
"title": "Physikalische Rechnung",
"description": "Rechenaufgabe mit physikalischem Kontext",
"activity_type": "partner_work",
"estimated_minutes": 12,
"icon": "calculate",
"subjects": ["physik"],
},
],
},
"informatik": {
LessonPhase.EINSTIEG: [
{
"id": "info_code_puzzle",
"title": "Code-Puzzle",
"description": "Kurzen Code-Schnipsel analysieren - was macht er?",
"activity_type": "warmup",
"estimated_minutes": 4,
"icon": "code",
"subjects": ["informatik"],
},
],
LessonPhase.ERARBEITUNG: [
{
"id": "info_live_coding",
"title": "Live Coding",
"description": "Gemeinsam Code entwickeln mit Erklaerungen",
"activity_type": "instruction",
"estimated_minutes": 15,
"icon": "terminal",
"subjects": ["informatik"],
},
{
"id": "info_pair_programming",
"title": "Pair Programming",
"description": "Zu zweit programmieren - Driver und Navigator",
"activity_type": "partner_work",
"estimated_minutes": 20,
"icon": "computer",
"subjects": ["informatik"],
},
],
},
}
# Vordefinierte allgemeine Vorschlaege pro Phase
PHASE_SUGGESTIONS: Dict[LessonPhase, List[Dict[str, Any]]] = {
LessonPhase.EINSTIEG: [
{
"id": "warmup_quiz",
"title": "Kurzes Quiz zum Einstieg",
"description": "Aktivieren Sie das Vorwissen der Schueler mit 3-5 Fragen zum Thema",
"activity_type": "warmup",
"estimated_minutes": 3,
"icon": "quiz"
},
{
"id": "problem_story",
"title": "Problemgeschichte erzaehlen",
"description": "Stellen Sie ein alltagsnahes Problem vor, das zum Thema fuehrt",
"activity_type": "motivation",
"estimated_minutes": 5,
"icon": "auto_stories"
},
{
"id": "video_intro",
"title": "Kurzes Erklaervideo",
"description": "Zeigen Sie ein 2-3 Minuten Video zur Einfuehrung ins Thema",
"activity_type": "motivation",
"estimated_minutes": 4,
"icon": "play_circle"
},
{
"id": "brainstorming",
"title": "Brainstorming",
"description": "Sammeln Sie Ideen und Vorkenntnisse der Schueler an der Tafel",
"activity_type": "warmup",
"estimated_minutes": 5,
"icon": "psychology"
},
{
"id": "daily_challenge",
"title": "Tagesaufgabe vorstellen",
"description": "Praesentieren Sie die zentrale Frage oder Aufgabe der Stunde",
"activity_type": "problem_introduction",
"estimated_minutes": 3,
"icon": "flag"
}
],
LessonPhase.ERARBEITUNG: [
{
"id": "think_pair_share",
"title": "Think-Pair-Share",
"description": "Schueler denken erst einzeln nach, tauschen sich dann zu zweit aus und praesentieren im Plenum",
"activity_type": "partner_work",
"estimated_minutes": 10,
"icon": "groups"
},
{
"id": "worksheet_digital",
"title": "Digitales Arbeitsblatt",
"description": "Schueler bearbeiten ein interaktives Arbeitsblatt am Tablet oder Computer",
"activity_type": "individual_work",
"estimated_minutes": 15,
"icon": "description"
},
{
"id": "station_learning",
"title": "Stationenlernen",
"description": "Verschiedene Stationen mit unterschiedlichen Aufgaben und Materialien",
"activity_type": "group_work",
"estimated_minutes": 20,
"icon": "hub"
},
{
"id": "expert_puzzle",
"title": "Expertenrunde (Jigsaw)",
"description": "Schueler werden Experten fuer ein Teilthema und lehren es anderen",
"activity_type": "group_work",
"estimated_minutes": 15,
"icon": "extension"
},
{
"id": "guided_instruction",
"title": "Geleitete Instruktion",
"description": "Schrittweise Erklaerung mit Uebungsphasen zwischendurch",
"activity_type": "instruction",
"estimated_minutes": 12,
"icon": "school"
},
{
"id": "pair_programming",
"title": "Partnerarbeit",
"description": "Zwei Schueler loesen gemeinsam eine Aufgabe",
"activity_type": "partner_work",
"estimated_minutes": 10,
"icon": "people"
}
],
LessonPhase.SICHERUNG: [
{
"id": "mindmap_class",
"title": "Gemeinsame Mindmap",
"description": "Ergebnisse als Mindmap an der Tafel oder digital sammeln und strukturieren",
"activity_type": "visualization",
"estimated_minutes": 8,
"icon": "account_tree"
},
{
"id": "exit_ticket",
"title": "Exit Ticket",
"description": "Schueler notieren 3 Dinge die sie gelernt haben und 1 offene Frage",
"activity_type": "summary",
"estimated_minutes": 5,
"icon": "sticky_note_2"
},
{
"id": "gallery_walk",
"title": "Galerie-Rundgang",
"description": "Schueler praesentieren ihre Ergebnisse und geben sich Feedback",
"activity_type": "presentation",
"estimated_minutes": 10,
"icon": "photo_library"
},
{
"id": "key_points",
"title": "Kernpunkte zusammenfassen",
"description": "Gemeinsam die wichtigsten Erkenntnisse der Stunde formulieren",
"activity_type": "summary",
"estimated_minutes": 5,
"icon": "format_list_bulleted"
},
{
"id": "quick_check",
"title": "Schneller Wissenscheck",
"description": "5 kurze Fragen zur Ueberpruefung des Verstaendnisses",
"activity_type": "documentation",
"estimated_minutes": 5,
"icon": "fact_check"
}
],
LessonPhase.TRANSFER: [
{
"id": "real_world_example",
"title": "Alltagsbeispiele finden",
"description": "Schueler suchen Beispiele aus ihrem Alltag, wo das Gelernte vorkommt",
"activity_type": "application",
"estimated_minutes": 5,
"icon": "public"
},
{
"id": "challenge_task",
"title": "Knobelaufgabe",
"description": "Eine anspruchsvollere Aufgabe fuer schnelle Schueler oder als Bonus",
"activity_type": "differentiation",
"estimated_minutes": 7,
"icon": "psychology"
},
{
"id": "creative_application",
"title": "Kreative Anwendung",
"description": "Schueler wenden das Gelernte in einem kreativen Projekt an",
"activity_type": "application",
"estimated_minutes": 10,
"icon": "palette"
},
{
"id": "peer_teaching",
"title": "Peer-Teaching",
"description": "Schueler erklaeren sich gegenseitig das Gelernte",
"activity_type": "real_world_connection",
"estimated_minutes": 5,
"icon": "supervisor_account"
}
],
LessonPhase.REFLEXION: [
{
"id": "thumbs_feedback",
"title": "Daumen-Feedback",
"description": "Schnelle Stimmungsabfrage: Daumen hoch/mitte/runter",
"activity_type": "feedback",
"estimated_minutes": 2,
"icon": "thumb_up"
},
{
"id": "homework_assign",
"title": "Hausaufgabe vergeben",
"description": "Passende Hausaufgabe zur Vertiefung des Gelernten",
"activity_type": "homework",
"estimated_minutes": 3,
"icon": "home_work"
},
{
"id": "one_word",
"title": "Ein-Wort-Reflexion",
"description": "Jeder Schueler nennt ein Wort, das die Stunde beschreibt",
"activity_type": "feedback",
"estimated_minutes": 3,
"icon": "chat"
},
{
"id": "preview_next",
"title": "Ausblick naechste Stunde",
"description": "Kurzer Ausblick auf das Thema der naechsten Stunde",
"activity_type": "preview",
"estimated_minutes": 2,
"icon": "event"
},
{
"id": "learning_log",
"title": "Lerntagebuch",
"description": "Schueler notieren ihre wichtigsten Erkenntnisse im Lerntagebuch",
"activity_type": "feedback",
"estimated_minutes": 4,
"icon": "menu_book"
}
]
}
from .suggestion_data import (
SUPPORTED_SUBJECTS,
SUBJECT_SUGGESTIONS,
PHASE_SUGGESTIONS,
)
class SuggestionEngine: