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-pwa/ai-content-generator/app/services/content_generator.py
Benjamin Admin 21a844cb8a fix: Restore all files lost during destructive rebase
A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.

This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).

Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 09:51:32 +01:00

342 lines
11 KiB
Python

"""
Content Generator
Orchestriert die Generierung aller 8 H5P Content-Typen
"""
from typing import List, Dict, Any, Optional
from datetime import datetime
import json
class ContentGenerator:
"""H5P Content Generator - Orchestrator"""
def __init__(self, claude_service, youtube_service):
self.claude = claude_service
self.youtube = youtube_service
async def generate_all_content_types(
self,
topic: str,
description: Optional[str],
target_grade: str,
materials: List[Dict[str, Any]],
videos: List[Dict[str, Any]]
) -> Dict[str, Any]:
"""
Generate all 8 H5P content types
Returns:
Dictionary with all generated content
"""
result = {
"topic": topic,
"description": description,
"target_grade": target_grade,
"generated_at": datetime.utcnow().isoformat(),
"content_types": {}
}
# 1. Quiz
try:
quiz_data = await self._generate_quiz(topic, description, target_grade, materials)
result["content_types"]["quiz"] = quiz_data
except Exception as e:
result["content_types"]["quiz"] = {"error": str(e)}
# 2. Interactive Video
try:
video_data = await self._generate_interactive_video(topic, description, target_grade, materials, videos)
result["content_types"]["interactive_video"] = video_data
except Exception as e:
result["content_types"]["interactive_video"] = {"error": str(e)}
# 3. Course Presentation
try:
presentation_data = await self._generate_presentation(topic, description, target_grade, materials)
result["content_types"]["course_presentation"] = presentation_data
except Exception as e:
result["content_types"]["course_presentation"] = {"error": str(e)}
# 4. Flashcards
try:
flashcards_data = await self._generate_flashcards(topic, description, target_grade, materials)
result["content_types"]["flashcards"] = flashcards_data
except Exception as e:
result["content_types"]["flashcards"] = {"error": str(e)}
# 5. Timeline
try:
timeline_data = await self._generate_timeline(topic, description, target_grade, materials)
result["content_types"]["timeline"] = timeline_data
except Exception as e:
result["content_types"]["timeline"] = {"error": str(e)}
# 6. Drag and Drop
try:
dragdrop_data = await self._generate_drag_drop(topic, description, target_grade, materials)
result["content_types"]["drag_drop"] = dragdrop_data
except Exception as e:
result["content_types"]["drag_drop"] = {"error": str(e)}
# 7. Fill in the Blanks
try:
fillblanks_data = await self._generate_fill_blanks(topic, description, target_grade, materials)
result["content_types"]["fill_blanks"] = fillblanks_data
except Exception as e:
result["content_types"]["fill_blanks"] = {"error": str(e)}
# 8. Memory Game
try:
memory_data = await self._generate_memory(topic, description, target_grade, materials)
result["content_types"]["memory"] = memory_data
except Exception as e:
result["content_types"]["memory"] = {"error": str(e)}
return result
async def _generate_quiz(
self,
topic: str,
description: Optional[str],
target_grade: str,
materials: List[Dict[str, Any]]
) -> Dict[str, Any]:
"""Generate Quiz content"""
questions = await self.claude.generate_quiz_questions(
topic=topic,
materials=materials,
target_grade=target_grade,
num_questions=10
)
return {
"type": "quiz",
"title": f"Quiz: {topic}",
"description": description or f"Teste dein Wissen über {topic}",
"questions": questions
}
async def _generate_interactive_video(
self,
topic: str,
description: Optional[str],
target_grade: str,
materials: List[Dict[str, Any]],
videos: List[Dict[str, Any]]
) -> Dict[str, Any]:
"""Generate Interactive Video content"""
# Wähle bestes Video (falls vorhanden)
if not videos or len(videos) == 0:
return {
"error": "Keine Videos gefunden",
"note": "Lehrer muss manuell Video-URL eingeben"
}
best_video = videos[0] # Nimm erstes Video
# Hole Transkript
video_id = best_video.get("video_id")
if not video_id or video_id == "EXAMPLE_VIDEO_ID":
# Fallback: Generiere generische Interaktionen
return {
"type": "interactive-video",
"title": f"Interaktives Video: {topic}",
"videoUrl": "https://www.youtube.com/watch?v=EXAMPLE",
"description": description or f"Lerne über {topic} mit diesem interaktiven Video",
"interactions": [
{
"time": "01:00",
"type": "question",
"title": "Verständnisfrage",
"content": f"Was ist das Hauptthema dieses Videos über {topic}?"
},
{
"time": "03:00",
"type": "info",
"title": "Wichtiger Hinweis",
"content": "Achte auf die wichtigsten Konzepte, die jetzt erklärt werden."
}
],
"note": "Generische Interaktionen - Lehrer sollte echte Video-URL eingeben"
}
# Echtes Video mit Transkript
transcript_data = await self.youtube.get_video_transcript(video_id)
if transcript_data:
# Generate interactions using Claude
interactions = await self.youtube.generate_video_interactions_with_claude(
video_id=video_id,
topic=topic,
transcript_data=transcript_data["transcript"],
claude_service=self.claude,
num_interactions=5
)
else:
# Fallback ohne Transkript
interactions = []
return {
"type": "interactive-video",
"title": best_video.get("title", f"Video: {topic}"),
"videoUrl": best_video.get("url"),
"description": description or f"Interaktives Video über {topic}",
"interactions": interactions
}
async def _generate_presentation(
self,
topic: str,
description: Optional[str],
target_grade: str,
materials: List[Dict[str, Any]]
) -> Dict[str, Any]:
"""Generate Course Presentation content"""
slides = await self.claude.generate_presentation_slides(
topic=topic,
materials=materials,
target_grade=target_grade,
num_slides=6
)
# Add IDs to slides
for i, slide in enumerate(slides, 1):
slide["id"] = i
return {
"type": "course-presentation",
"title": f"Präsentation: {topic}",
"description": description or f"Lerne alles über {topic}",
"slides": slides
}
async def _generate_flashcards(
self,
topic: str,
description: Optional[str],
target_grade: str,
materials: List[Dict[str, Any]]
) -> Dict[str, Any]:
"""Generate Flashcards content"""
cards = await self.claude.generate_flashcards(
topic=topic,
materials=materials,
target_grade=target_grade,
num_cards=15
)
# Add IDs to cards
for i, card in enumerate(cards, 1):
card["id"] = i
return {
"type": "flashcards",
"title": f"Lernkarten: {topic}",
"description": description or f"Wiederhole wichtige Begriffe zu {topic}",
"cards": cards
}
async def _generate_timeline(
self,
topic: str,
description: Optional[str],
target_grade: str,
materials: List[Dict[str, Any]]
) -> Dict[str, Any]:
"""Generate Timeline content"""
events = await self.claude.generate_timeline_events(
topic=topic,
materials=materials,
target_grade=target_grade
)
# Add IDs to events
for i, event in enumerate(events, 1):
event["id"] = i
return {
"type": "timeline",
"title": f"Zeitleiste: {topic}",
"description": description or f"Chronologie von {topic}",
"events": events
}
async def _generate_drag_drop(
self,
topic: str,
description: Optional[str],
target_grade: str,
materials: List[Dict[str, Any]]
) -> Dict[str, Any]:
"""Generate Drag and Drop content"""
exercise = await self.claude.generate_drag_drop_exercise(
topic=topic,
materials=materials,
target_grade=target_grade
)
return {
"type": "drag-drop",
"title": exercise.get("title", f"Zuordnung: {topic}"),
"question": exercise.get("question", "Ziehe die Elemente in die richtigen Kategorien."),
"zones": exercise.get("zones", []),
"draggables": exercise.get("draggables", [])
}
async def _generate_fill_blanks(
self,
topic: str,
description: Optional[str],
target_grade: str,
materials: List[Dict[str, Any]]
) -> Dict[str, Any]:
"""Generate Fill in the Blanks content"""
exercise = await self.claude.generate_fill_blanks_text(
topic=topic,
materials=materials,
target_grade=target_grade
)
return {
"type": "fill-blanks",
"title": exercise.get("title", f"Lückentext: {topic}"),
"text": exercise.get("text", ""),
"hints": exercise.get("hints", "")
}
async def _generate_memory(
self,
topic: str,
description: Optional[str],
target_grade: str,
materials: List[Dict[str, Any]]
) -> Dict[str, Any]:
"""Generate Memory Game content"""
pairs = await self.claude.generate_memory_pairs(
topic=topic,
materials=materials,
target_grade=target_grade,
num_pairs=8
)
# Add IDs to pairs
for i, pair in enumerate(pairs, 1):
pair["id"] = i
return {
"type": "memory",
"title": f"Memory: {topic}",
"description": description or f"Finde die passenden Paare zu {topic}",
"pairs": pairs
}