""" AI Processor - Print Version Generators Generate printable HTML versions for Q&A, Cloze, and Multiple Choice. """ from pathlib import Path import json import logging import random from ..config import BEREINIGT_DIR logger = logging.getLogger(__name__) def generate_print_version_qa(qa_path: Path, include_answers: bool = False) -> Path: """ Generate a printable HTML version of the Q&A pairs. Args: qa_path: Path to *_qa.json file include_answers: True for solution sheet (for parents) Returns: Path to generated HTML file """ 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(_get_qa_html_header(title)) # Header version_text = "Loesungsblatt" 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)}
") # Questions 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: html_parts.append(f"
Antwort: {item.get('answer', '')}
") 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: html_parts.append("
") for _ in range(3): html_parts.append("
") html_parts.append("
") html_parts.append("
") html_parts.append("") # Save 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: """ Generate a printable HTML version of the cloze texts. Args: cloze_path: Path to *_cloze.json file include_answers: True for solution sheet (for parents) Returns: Path to generated HTML file """ 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(_get_cloze_html_header(title)) # Header version_text = "Loesungsblatt" if include_answers else "Lueckentext" 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"Luecken gesamt: {total_gaps}") html_parts.append(f"
{' | '.join(meta_parts)}
") # Collect all gap words for word bank all_words = [] # Cloze texts 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: # Solution sheet: fill gaps with answers for gap in gaps: word = gap.get("word", "") sentence = sentence.replace("___", f"{word}", 1) else: # Question sheet: gaps as lines sentence = sentence.replace("___", " ") for gap in gaps: all_words.append(gap.get("word", "")) html_parts.append(f"
{sentence}
") # Show translation translation = item.get("translation", {}) if translation: lang_name = translation.get("language_name", "Uebersetzung") 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("
") # Word bank (only for question sheet) if not include_answers and all_words: random.shuffle(all_words) html_parts.append("
") html_parts.append("
Wortbank (diese Woerter fehlen):
") for word in all_words: html_parts.append(f"{word}") html_parts.append("
") html_parts.append("") # Save 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: """ Generate a printable HTML version of the multiple choice questions. Args: mc_path: Path to *_mc.json file include_answers: True for solution sheet with marked correct answers Returns: HTML string (for direct delivery) """ 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(_get_mc_html_header(title)) # Header version_text = "Loesungsblatt" 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("
") # Questions 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("
") # Explanation only for solution sheet if include_answers and q.get("explanation"): html_parts.append(f"
Erklaerung: {q.get('explanation')}
") html_parts.append("
") # Answer key (compact) - only for solution sheet if include_answers: html_parts.append("
") html_parts.append("
Loesungsschluessel
") 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 _get_qa_html_header(title: str) -> str: """Get HTML header for Q&A print version.""" return f""" {title} - Fragen """ def _get_cloze_html_header(title: str) -> str: """Get HTML header for cloze print version.""" return f""" {title} - Lueckentext """ def _get_mc_html_header(title: str) -> str: """Get HTML header for MC print version.""" return f""" {title} - Multiple Choice """