""" 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 # 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" } ] } 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, }