""" AI Processing - Mindmap Generator. Generiert kindgerechte Lernposter-Mindmaps aus Arbeitsblatt-Analysen. """ from pathlib import Path import math import json import os import requests import logging from .core import get_openai_api_key, BEREINIGT_DIR logger = logging.getLogger(__name__) def generate_mindmap_data(analysis_path: Path) -> dict: """ Extrahiert Fachbegriffe aus der Analyse und gruppiert sie für eine Mindmap. Args: analysis_path: Pfad zur *_analyse.json Datei Returns: Dictionary mit Mindmap-Struktur: { "topic": "Hauptthema", "subject": "Fach", "categories": [ { "name": "Kategorie", "color": "#hexcolor", "emoji": "🔬", "terms": [ {"term": "Begriff", "explanation": "Kurze Erklärung"} ] } ] } """ if not analysis_path.exists(): raise FileNotFoundError(f"Analysedatei nicht gefunden: {analysis_path}") try: data = json.loads(analysis_path.read_text(encoding="utf-8")) except json.JSONDecodeError as e: raise RuntimeError(f"Analyse-Datei enthält kein gültiges JSON: {analysis_path}\n{e}") from e title = data.get("title") or "Arbeitsblatt" subject = data.get("subject") or "" canonical_text = data.get("canonical_text") or "" tasks = data.get("tasks", []) or [] # Sammle allen Text für die Analyse all_text = canonical_text for task in tasks: if task.get("description"): all_text += "\n" + task.get("description") if task.get("text_with_gaps"): all_text += "\n" + task.get("text_with_gaps") if not all_text.strip(): return { "topic": title, "subject": subject, "categories": [] } # KI-basierte Extraktion der Fachbegriffe api_key = get_openai_api_key() prompt = f"""Analysiere diesen Schultext und extrahiere alle Fachbegriffe für eine kindgerechte Lern-Mindmap. THEMA: {title} FACH: {subject} TEXT: {all_text[:3000]} AUFGABE: 1. Identifiziere das Hauptthema (ein einzelnes Wort oder kurzer Begriff) 2. Finde ALLE Fachbegriffe und gruppiere sie in 3-6 sinnvolle Kategorien 3. Gib für jeden Begriff eine kurze, kindgerechte Erklärung (max 10 Wörter) 4. Wähle für jede Kategorie ein passendes Emoji und eine Farbe Antworte NUR mit diesem JSON-Format: {{ "topic": "Hauptthema (z.B. 'Das Auge')", "categories": [ {{ "name": "Kategoriename", "emoji": "passendes Emoji", "color": "#Hexfarbe (bunt, kindgerecht)", "terms": [ {{"term": "Fachbegriff", "explanation": "Kurze Erklärung"}} ] }} ] }} WICHTIG: - Verwende kindgerechte, einfache Sprache - Bunte, fröhliche Farben: #FF6B6B, #4ECDC4, #45B7D1, #96CEB4, #FFEAA7, #DDA0DD, #98D8C8 - Passende Emojis für jede Kategorie - Mindestens 3 Begriffe pro Kategorie wenn möglich - Maximal 6 Kategorien""" try: # Versuche Claude claude_key = os.environ.get("ANTHROPIC_API_KEY") if claude_key: import anthropic client = anthropic.Anthropic(api_key=claude_key) response = client.messages.create( model="claude-3-5-sonnet-20241022", max_tokens=2000, messages=[{"role": "user", "content": prompt}] ) result_text = response.content[0].text else: # Fallback zu OpenAI logger.info("Claude Mindmap-Generierung fehlgeschlagen, nutze OpenAI: ANTHROPIC_API_KEY ist nicht gesetzt.") url = "https://api.openai.com/v1/chat/completions" headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"} payload = { "model": "gpt-4o-mini", "messages": [ {"role": "system", "content": "Du bist ein Experte für kindgerechte Lernmaterialien."}, {"role": "user", "content": prompt} ], "max_tokens": 2000, "temperature": 0.7 } resp = requests.post(url, headers=headers, json=payload, timeout=60) resp.raise_for_status() result_text = resp.json()["choices"][0]["message"]["content"] # JSON extrahieren result_text = result_text.strip() if result_text.startswith("```"): result_text = result_text.split("```")[1] if result_text.startswith("json"): result_text = result_text[4:] result_text = result_text.strip() mindmap_data = json.loads(result_text) mindmap_data["subject"] = subject return mindmap_data except Exception as e: logger.error(f"Mindmap-Generierung fehlgeschlagen: {e}") # Fallback: Einfache Struktur zurückgeben return { "topic": title, "subject": subject, "categories": [] } def generate_mindmap_html(mindmap_data: dict, format: str = "a3") -> str: """ Generiert ein kindgerechtes HTML/SVG Mindmap-Poster. Args: mindmap_data: Dictionary aus generate_mindmap_data() format: "a3" für A3-Poster (Standard) oder "a4" für A4-Ansicht Returns: HTML-String mit SVG-Mindmap """ topic = mindmap_data.get("topic", "Thema") subject = mindmap_data.get("subject", "") categories = mindmap_data.get("categories", []) # Format-spezifische Einstellungen if format.lower() == "a4": page_size = "A4 landscape" svg_width = 1100 svg_height = 780 radius = 250 else: # a3 (Standard) page_size = "A3 landscape" svg_width = 1400 svg_height = 990 radius = 320 # Wenn keine Kategorien, zeige Platzhalter if not categories: return f"""
Noch keine Daten vorhanden. Bitte zuerst das Arbeitsblatt analysieren.
""" # Farben für Verbindungslinien num_categories = len(categories) # SVG-Dimensionen wurden oben basierend auf format gesetzt center_x = svg_width // 2 center_y = svg_height // 2 # Berechne Positionen der Kategorien im Kreis category_positions = [] for i, cat in enumerate(categories): angle = (2 * math.pi * i / num_categories) - (math.pi / 2) # Start oben x = center_x + radius * math.cos(angle) y = center_y + radius * math.sin(angle) category_positions.append({ "x": x, "y": y, "angle": angle, "data": cat }) html = f"""