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>
190 lines
5.7 KiB
Python
190 lines
5.7 KiB
Python
"""
|
|
Phasenspezifische Content-Vorschlaege (Feature f18 erweitert).
|
|
|
|
Generiert Aktivitaets-Vorschlaege basierend auf der aktuellen Unterrichtsphase
|
|
und optional dem Fach.
|
|
"""
|
|
|
|
from typing import List, Dict, Any, Optional
|
|
|
|
from .models import LessonPhase, LessonSession, PhaseSuggestion
|
|
from .suggestion_data import (
|
|
SUPPORTED_SUBJECTS,
|
|
SUBJECT_SUGGESTIONS,
|
|
PHASE_SUGGESTIONS,
|
|
)
|
|
|
|
|
|
class SuggestionEngine:
|
|
"""
|
|
Engine zur Generierung von phasenspezifischen Vorschlaegen (Feature f18 erweitert).
|
|
|
|
Liefert Aktivitaets-Vorschlaege basierend auf:
|
|
- Aktueller Phase
|
|
- Fach (priorisiert fachspezifische Vorschlaege)
|
|
- Optional: Klassenstufe, bisherige Nutzung
|
|
"""
|
|
|
|
def _normalize_subject(self, subject: str) -> Optional[str]:
|
|
"""Normalisiert Fachnamen fuer die Suche."""
|
|
if not subject:
|
|
return None
|
|
normalized = subject.lower().strip()
|
|
# Mapping zu Hauptkategorien
|
|
mappings = {
|
|
"mathe": "mathematik",
|
|
"math": "mathematik",
|
|
"bio": "biologie",
|
|
"english": "englisch",
|
|
"erdkunde": "geografie",
|
|
}
|
|
return mappings.get(normalized, normalized)
|
|
|
|
def _get_subject_suggestions(
|
|
self,
|
|
subject: str,
|
|
phase: LessonPhase
|
|
) -> List[Dict[str, Any]]:
|
|
"""Holt fachspezifische Vorschlaege (Feature f18)."""
|
|
normalized = self._normalize_subject(subject)
|
|
if not normalized:
|
|
return []
|
|
|
|
subject_data = SUBJECT_SUGGESTIONS.get(normalized, {})
|
|
return subject_data.get(phase, [])
|
|
|
|
def get_suggestions(
|
|
self,
|
|
session: LessonSession,
|
|
limit: int = 3
|
|
) -> List[PhaseSuggestion]:
|
|
"""
|
|
Gibt Vorschlaege fuer die aktuelle Phase zurueck.
|
|
|
|
Priorisiert fachspezifische Vorschlaege (Feature f18),
|
|
ergaenzt mit allgemeinen Vorschlaegen.
|
|
|
|
Args:
|
|
session: Die aktuelle Session
|
|
limit: Maximale Anzahl Vorschlaege
|
|
|
|
Returns:
|
|
Liste von PhaseSuggestion Objekten
|
|
"""
|
|
# Keine Vorschlaege fuer inaktive Phasen
|
|
if session.current_phase in [LessonPhase.NOT_STARTED, LessonPhase.ENDED]:
|
|
return []
|
|
|
|
suggestions = []
|
|
seen_ids = set()
|
|
|
|
# 1. Fachspezifische Vorschlaege zuerst (Feature f18)
|
|
if session.subject:
|
|
subject_suggestions = self._get_subject_suggestions(
|
|
session.subject,
|
|
session.current_phase
|
|
)
|
|
for s in subject_suggestions:
|
|
if s["id"] not in seen_ids:
|
|
suggestions.append(PhaseSuggestion(**s))
|
|
seen_ids.add(s["id"])
|
|
if len(suggestions) >= limit:
|
|
return suggestions
|
|
|
|
# 2. Allgemeine Vorschlaege ergaenzen
|
|
phase_suggestions = PHASE_SUGGESTIONS.get(session.current_phase, [])
|
|
for s in phase_suggestions:
|
|
if s["id"] not in seen_ids:
|
|
suggestions.append(PhaseSuggestion(**s))
|
|
seen_ids.add(s["id"])
|
|
if len(suggestions) >= limit:
|
|
break
|
|
|
|
return suggestions
|
|
|
|
def get_all_suggestions(self, session: LessonSession) -> List[PhaseSuggestion]:
|
|
"""
|
|
Gibt alle Vorschlaege fuer die aktuelle Phase zurueck.
|
|
|
|
Inkludiert fachspezifische und allgemeine Vorschlaege (Feature f18).
|
|
|
|
Args:
|
|
session: Die aktuelle Session
|
|
|
|
Returns:
|
|
Alle Vorschlaege fuer die Phase
|
|
"""
|
|
return self.get_suggestions(session, limit=100)
|
|
|
|
def get_suggestion_by_id(
|
|
self,
|
|
session: LessonSession,
|
|
suggestion_id: str
|
|
) -> Optional[PhaseSuggestion]:
|
|
"""
|
|
Gibt einen spezifischen Vorschlag zurueck.
|
|
|
|
Args:
|
|
session: Die aktuelle Session
|
|
suggestion_id: ID des Vorschlags
|
|
|
|
Returns:
|
|
Der Vorschlag oder None
|
|
"""
|
|
all_suggestions = self.get_all_suggestions(session)
|
|
for s in all_suggestions:
|
|
if s.id == suggestion_id:
|
|
return s
|
|
return None
|
|
|
|
def get_suggestions_by_type(
|
|
self,
|
|
session: LessonSession,
|
|
activity_type: str
|
|
) -> List[PhaseSuggestion]:
|
|
"""
|
|
Gibt Vorschlaege eines bestimmten Typs zurueck.
|
|
|
|
Args:
|
|
session: Die aktuelle Session
|
|
activity_type: Der Aktivitaetstyp (z.B. "warmup", "group_work")
|
|
|
|
Returns:
|
|
Gefilterte Vorschlaege
|
|
"""
|
|
all_suggestions = self.get_all_suggestions(session)
|
|
return [s for s in all_suggestions if s.activity_type == activity_type]
|
|
|
|
def get_suggestions_response(
|
|
self,
|
|
session: LessonSession,
|
|
limit: int = 3
|
|
) -> Dict[str, Any]:
|
|
"""
|
|
Gibt die Vorschlaege als API-Response-Format zurueck (Feature f18 erweitert).
|
|
|
|
Args:
|
|
session: Die aktuelle Session
|
|
limit: Maximale Anzahl Vorschlaege
|
|
|
|
Returns:
|
|
Dictionary fuer API-Response
|
|
"""
|
|
suggestions = self.get_suggestions(session, limit)
|
|
|
|
# Zaehle fachspezifische und allgemeine Vorschlaege
|
|
general_count = len(PHASE_SUGGESTIONS.get(session.current_phase, []))
|
|
subject_count = len(self._get_subject_suggestions(
|
|
session.subject,
|
|
session.current_phase
|
|
)) if session.subject else 0
|
|
|
|
return {
|
|
"suggestions": [s.to_dict() for s in suggestions],
|
|
"current_phase": session.current_phase.value,
|
|
"phase_display_name": session.get_phase_display_name(),
|
|
"total_available": general_count + subject_count,
|
|
"subject_specific_available": subject_count,
|
|
"subject": session.subject,
|
|
}
|