backend-lehrer (10 files): - game/database.py (785 → 5), correction_api.py (683 → 4) - classroom_engine/antizipation.py (676 → 5) - llm_gateway schools/edu_search already done in prior batch klausur-service (12 files): - orientation_crop_api.py (694 → 5), pdf_export.py (677 → 4) - zeugnis_crawler.py (676 → 5), grid_editor_api.py (671 → 5) - eh_templates.py (658 → 5), mail/api.py (651 → 5) - qdrant_service.py (638 → 5), training_api.py (625 → 4) website (6 pages): - middleware (696 → 8), mail (733 → 6), consent (628 → 8) - compliance/risks (622 → 5), export (502 → 5), brandbook (629 → 7) studio-v2 (3 components): - B2BMigrationWizard (848 → 3), CleanupPanel (765 → 2) - dashboard-experimental (739 → 2) admin-lehrer (4 files): - uebersetzungen (769 → 4), manager (670 → 2) - ChunkBrowserQA (675 → 6), dsfa/page (674 → 5) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
132 lines
4.5 KiB
Python
132 lines
4.5 KiB
Python
"""
|
|
Antizipation Engine - Signal collector.
|
|
|
|
Sammelt Signale aus verschiedenen Quellen:
|
|
- TeacherContext (Makro-Phase, Schuljahr)
|
|
- SchoolyearEvents (Klausuren, Elternabende, etc.)
|
|
- RecurringRoutines (Konferenzen heute)
|
|
- Zeit/Kalender (Wochenende, Ferien)
|
|
"""
|
|
|
|
import logging
|
|
from datetime import datetime, timedelta
|
|
|
|
from .antizipation_models import Signals
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class SignalCollector:
|
|
"""
|
|
Sammelt Signale aus verschiedenen Quellen.
|
|
"""
|
|
|
|
def __init__(self, db_session=None):
|
|
self.db = db_session
|
|
|
|
def collect(self, teacher_id: str) -> Signals:
|
|
"""Sammelt alle Signale fuer einen Lehrer."""
|
|
signals = Signals()
|
|
|
|
# Zeit-Signale
|
|
self._collect_time_signals(signals)
|
|
|
|
if self.db:
|
|
# Kontext-Signale
|
|
self._collect_context_signals(signals, teacher_id)
|
|
# Event-Signale
|
|
self._collect_event_signals(signals, teacher_id)
|
|
# Routine-Signale
|
|
self._collect_routine_signals(signals, teacher_id)
|
|
|
|
return signals
|
|
|
|
def _collect_time_signals(self, signals: Signals):
|
|
"""Sammelt zeitbasierte Signale."""
|
|
now = datetime.utcnow()
|
|
signals.is_weekend = now.weekday() >= 5
|
|
|
|
# TODO: Ferien-Kalender pro Bundesland integrieren
|
|
# Fuer jetzt: Dummy-Werte
|
|
signals.is_before_holidays = False
|
|
signals.days_until_holidays = 999
|
|
|
|
def _collect_context_signals(self, signals: Signals, teacher_id: str):
|
|
"""Sammelt Signale aus dem Teacher-Kontext."""
|
|
from .repository import TeacherContextRepository
|
|
|
|
try:
|
|
repo = TeacherContextRepository(self.db)
|
|
context = repo.get_or_create(teacher_id)
|
|
|
|
signals.macro_phase = context.macro_phase.value
|
|
signals.current_week = context.current_week or 1
|
|
signals.onboarding_completed = context.onboarding_completed
|
|
signals.has_classes = context.has_classes
|
|
signals.has_schedule = context.has_schedule
|
|
signals.classes_count = 1 if context.has_classes else 0
|
|
|
|
# Wochen seit Schuljahresstart berechnen
|
|
if context.schoolyear_start:
|
|
delta = datetime.utcnow() - context.schoolyear_start
|
|
signals.weeks_since_start = max(0, delta.days // 7)
|
|
|
|
signals.is_before_holidays = context.is_before_holidays
|
|
|
|
except Exception as e:
|
|
logger.warning(f"Failed to collect context signals: {e}")
|
|
|
|
def _collect_event_signals(self, signals: Signals, teacher_id: str):
|
|
"""Sammelt Signale aus Events."""
|
|
from .repository import SchoolyearEventRepository
|
|
|
|
try:
|
|
repo = SchoolyearEventRepository(self.db)
|
|
now = datetime.utcnow()
|
|
|
|
# Alle anstehenden Events (30 Tage)
|
|
upcoming = repo.get_upcoming(teacher_id, days=30, limit=20)
|
|
signals.upcoming_events = [repo.to_dict(e) for e in upcoming]
|
|
|
|
# Klausuren in den naechsten 7 Tagen
|
|
seven_days = now + timedelta(days=7)
|
|
signals.exams_in_7_days = [
|
|
repo.to_dict(e) for e in upcoming
|
|
if e.event_type.value == "exam" and e.start_date <= seven_days
|
|
]
|
|
signals.exams_scheduled_count = len([
|
|
e for e in upcoming if e.event_type.value == "exam"
|
|
])
|
|
|
|
# Klassenfahrten in 30 Tagen
|
|
signals.trips_in_30_days = [
|
|
repo.to_dict(e) for e in upcoming
|
|
if e.event_type.value == "trip"
|
|
]
|
|
|
|
# Elternabende bald
|
|
signals.parent_evenings_soon = [
|
|
repo.to_dict(e) for e in upcoming
|
|
if e.event_type.value in ("parent_evening", "parent_consultation")
|
|
]
|
|
|
|
except Exception as e:
|
|
logger.warning(f"Failed to collect event signals: {e}")
|
|
|
|
def _collect_routine_signals(self, signals: Signals, teacher_id: str):
|
|
"""Sammelt Signale aus Routinen."""
|
|
from .repository import RecurringRoutineRepository
|
|
|
|
try:
|
|
repo = RecurringRoutineRepository(self.db)
|
|
today_routines = repo.get_today(teacher_id)
|
|
|
|
signals.routines_today = [repo.to_dict(r) for r in today_routines]
|
|
signals.has_conference_today = any(
|
|
r.routine_type.value in ("teacher_conference", "subject_conference")
|
|
for r in today_routines
|
|
)
|
|
|
|
except Exception as e:
|
|
logger.warning(f"Failed to collect routine signals: {e}")
|