""" AI Processing - Print Version Generator. Generiert druckbare HTML-Versionen für verschiedene Arbeitsblatt-Typen. """ from pathlib import Path import json import random import logging from .core import BEREINIGT_DIR logger = logging.getLogger(__name__) def generate_print_version_qa(qa_path: Path, include_answers: bool = False) -> Path: """ Generiert eine druckbare HTML-Version der Frage-Antwort-Paare. Args: qa_path: Pfad zur *_qa.json Datei include_answers: True für Lösungsblatt (für Eltern) Returns: Pfad zur generierten HTML-Datei """ if not qa_path.exists(): raise FileNotFoundError(f"Q&A-Datei nicht gefunden: {qa_path}") qa_data = json.loads(qa_path.read_text(encoding="utf-8")) items = qa_data.get("qa_items", []) metadata = qa_data.get("metadata", {}) title = metadata.get("source_title", "Arbeitsblatt") subject = metadata.get("subject", "") grade = metadata.get("grade_level", "") html_parts = [] html_parts.append(""" """ + title + """ - Fragen """) # Header version_text = "Lösungsblatt" if include_answers else "Fragenblatt" html_parts.append(f"

{title} - {version_text}

") meta_parts = [] if subject: meta_parts.append(f"Fach: {subject}") if grade: meta_parts.append(f"Klasse: {grade}") meta_parts.append(f"Anzahl Fragen: {len(items)}") html_parts.append(f"
{' | '.join(meta_parts)}
") # Fragen for idx, item in enumerate(items, 1): html_parts.append("
") html_parts.append(f"
Frage {idx}
") html_parts.append(f"
{item.get('question', '')}
") if include_answers: # Lösungsblatt: Antwort anzeigen html_parts.append(f"
Antwort: {item.get('answer', '')}
") # Schlüsselbegriffe key_terms = item.get("key_terms", []) if key_terms: terms_html = " ".join([f"{term}" for term in key_terms]) html_parts.append(f"
Wichtige Begriffe: {terms_html}
") else: # Fragenblatt: Antwortlinien html_parts.append("
") for _ in range(3): html_parts.append("
") html_parts.append("
") html_parts.append("
") html_parts.append("") # Speichern suffix = "_qa_solutions.html" if include_answers else "_qa_print.html" out_name = qa_path.stem.replace("_qa", "") + suffix out_path = BEREINIGT_DIR / out_name out_path.write_text("\n".join(html_parts), encoding="utf-8") logger.info(f"Print-Version gespeichert: {out_path.name}") return out_path def generate_print_version_cloze(cloze_path: Path, include_answers: bool = False) -> Path: """ Generiert eine druckbare HTML-Version der Lückentexte. Args: cloze_path: Pfad zur *_cloze.json Datei include_answers: True für Lösungsblatt (für Eltern) Returns: Pfad zur generierten HTML-Datei """ if not cloze_path.exists(): raise FileNotFoundError(f"Cloze-Datei nicht gefunden: {cloze_path}") cloze_data = json.loads(cloze_path.read_text(encoding="utf-8")) items = cloze_data.get("cloze_items", []) metadata = cloze_data.get("metadata", {}) title = metadata.get("source_title", "Arbeitsblatt") subject = metadata.get("subject", "") grade = metadata.get("grade_level", "") total_gaps = metadata.get("total_gaps", 0) html_parts = [] html_parts.append(""" """ + title + """ - Lückentext """) # Header version_text = "Lösungsblatt" if include_answers else "Lückentext" html_parts.append(f"

{title} - {version_text}

") meta_parts = [] if subject: meta_parts.append(f"Fach: {subject}") if grade: meta_parts.append(f"Klasse: {grade}") meta_parts.append(f"Lücken gesamt: {total_gaps}") html_parts.append(f"
{' | '.join(meta_parts)}
") # Sammle alle Lückenwörter für Wortbank all_words = [] # Lückentexte for idx, item in enumerate(items, 1): html_parts.append("
") html_parts.append(f"
{idx}.
") gaps = item.get("gaps", []) sentence = item.get("sentence_with_gaps", "") if include_answers: # Lösungsblatt: Lücken mit Antworten füllen for gap in gaps: word = gap.get("word", "") sentence = sentence.replace("___", f"{word}", 1) else: # Fragenblatt: Lücken als Linien sentence = sentence.replace("___", " ") # Wörter für Wortbank sammeln for gap in gaps: all_words.append(gap.get("word", "")) html_parts.append(f"
{sentence}
") # Übersetzung anzeigen translation = item.get("translation", {}) if translation: lang_name = translation.get("language_name", "Übersetzung") full_sentence = translation.get("full_sentence", "") if full_sentence: html_parts.append("
") html_parts.append(f"
{lang_name}:
") html_parts.append(full_sentence) html_parts.append("
") html_parts.append("
") # Wortbank (nur für Fragenblatt) if not include_answers and all_words: random.shuffle(all_words) # Mische die Wörter html_parts.append("
") html_parts.append("
Wortbank (diese Wörter fehlen):
") for word in all_words: html_parts.append(f"{word}") html_parts.append("
") html_parts.append("") # Speichern suffix = "_cloze_solutions.html" if include_answers else "_cloze_print.html" out_name = cloze_path.stem.replace("_cloze", "") + suffix out_path = BEREINIGT_DIR / out_name out_path.write_text("\n".join(html_parts), encoding="utf-8") logger.info(f"Cloze Print-Version gespeichert: {out_path.name}") return out_path def generate_print_version_mc(mc_path: Path, include_answers: bool = False) -> str: """ Generiert eine druckbare HTML-Version der Multiple-Choice-Fragen. Args: mc_path: Pfad zur *_mc.json Datei include_answers: True für Lösungsblatt mit markierten richtigen Antworten Returns: HTML-String (zum direkten Ausliefern) """ if not mc_path.exists(): raise FileNotFoundError(f"MC-Datei nicht gefunden: {mc_path}") mc_data = json.loads(mc_path.read_text(encoding="utf-8")) questions = mc_data.get("questions", []) metadata = mc_data.get("metadata", {}) title = metadata.get("source_title", "Arbeitsblatt") subject = metadata.get("subject", "") grade = metadata.get("grade_level", "") html_parts = [] html_parts.append(""" """ + title + """ - Multiple Choice """) # Header version_text = "Lösungsblatt" if include_answers else "Multiple Choice Test" html_parts.append(f"

{title}

") html_parts.append(f"
{version_text}") if subject: html_parts.append(f" | Fach: {subject}") if grade: html_parts.append(f" | Klasse: {grade}") html_parts.append(f" | Anzahl Fragen: {len(questions)}
") if not include_answers: html_parts.append("
") html_parts.append("Anleitung: Kreuze bei jeder Frage die richtige Antwort an. ") html_parts.append("Es ist immer nur eine Antwort richtig.") html_parts.append("
") # Fragen for idx, q in enumerate(questions, 1): html_parts.append("
") html_parts.append(f"
Frage {idx}
") html_parts.append(f"
{q.get('question', '')}
") html_parts.append("
") correct_answer = q.get("correct_answer", "") for opt in q.get("options", []): opt_id = opt.get("id", "") is_correct = opt_id == correct_answer opt_class = "option" checkbox_class = "option-checkbox" if include_answers and is_correct: opt_class += " option-correct" checkbox_class += " checked" html_parts.append(f"
") html_parts.append(f"
") html_parts.append(f"{opt_id})") html_parts.append(f"{opt.get('text', '')}") html_parts.append("
") html_parts.append("
") # Erklärung nur bei Lösungsblatt if include_answers and q.get("explanation"): html_parts.append(f"
Erklärung: {q.get('explanation')}
") html_parts.append("
") # Lösungsschlüssel (kompakt) - nur bei Lösungsblatt if include_answers: html_parts.append("
") html_parts.append("
Lösungsschlüssel
") html_parts.append("
") for idx, q in enumerate(questions, 1): html_parts.append("
") html_parts.append(f"{idx}. ") html_parts.append(f"{q.get('correct_answer', '')}") html_parts.append("
") html_parts.append("
") html_parts.append("
") html_parts.append("") return "\n".join(html_parts) def generate_print_version_worksheet(analysis_path: Path) -> str: """ Generiert eine druckoptimierte HTML-Version des Arbeitsblatts. Eigenschaften: - Große, gut lesbare Schrift (16pt) - Schwarz-weiß / Graustufen-tauglich - Klare Struktur für Druck - Keine interaktiven Elemente Args: analysis_path: Pfad zur *_analyse.json Datei Returns: HTML-String zum direkten Ausliefern """ 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 "" grade_level = data.get("grade_level") or "" instructions = data.get("instructions") or "" tasks = data.get("tasks", []) or [] canonical_text = data.get("canonical_text") or "" printed_blocks = data.get("printed_blocks") or [] html_parts = [] html_parts.append(""" """ + title + """ """) # Titel html_parts.append(f"

{title}

") # Meta-Informationen meta_parts = [] if subject: meta_parts.append(f"Fach: {subject}") if grade_level: meta_parts.append(f"Klasse: {grade_level}") if meta_parts: html_parts.append(f"
{''.join(meta_parts)}
") # Arbeitsanweisung if instructions: html_parts.append("
") html_parts.append("
Arbeitsanweisung:
") html_parts.append(f"
{instructions}
") html_parts.append("
") # Haupttext / gedruckte Blöcke if printed_blocks: html_parts.append("
") for block in printed_blocks: role = (block.get("role") or "body").lower() text = (block.get("text") or "").strip() if not text: continue if role == "title": html_parts.append(f"
{text}
") else: html_parts.append(f"
{text}
") html_parts.append("
") elif canonical_text: html_parts.append("
") paragraphs = [ p.strip() for p in canonical_text.replace("\r\n", "\n").split("\n\n") if p.strip() ] for p in paragraphs: html_parts.append(f"
{p}
") html_parts.append("
") # Aufgaben if tasks: html_parts.append("
") html_parts.append("

Aufgaben

") for idx, task in enumerate(tasks, start=1): t_type = task.get("type") or "Aufgabe" desc = task.get("description") or "" text_with_gaps = task.get("text_with_gaps") html_parts.append("
") # Task-Header type_label = { "fill_in_blank": "Lückentext", "multiple_choice": "Multiple Choice", "free_text": "Freitext", "matching": "Zuordnung", "labeling": "Beschriftung", "calculation": "Rechnung", "other": "Aufgabe" }.get(t_type, t_type) html_parts.append(f"
Aufgabe {idx}: {type_label}
") if desc: html_parts.append(f"
{desc}
") if text_with_gaps: rendered = text_with_gaps.replace("___", " ") html_parts.append(f"
{rendered}
") # Antwortlinien für Freitext-Aufgaben if t_type in ["free_text", "other"] or (not text_with_gaps and not desc): html_parts.append("
") for _ in range(3): html_parts.append("
") html_parts.append("
") html_parts.append("
") html_parts.append("
") # Fußzeile html_parts.append("") html_parts.append("") return "\n".join(html_parts)