"""
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"
")
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"
{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("")
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("