""" PDF Worksheet Generator for Breakpilot Units ============================================ Generates printable PDF worksheets from unit definitions. Structure: 1. Title + Learning Objectives 2. Vocabulary Table 3. Key Concepts Summary 4. Practice Exercises (Basic) 5. Challenge Exercises (Advanced) 6. Reflection Questions """ from typing import Optional, Union from .worksheet_models import Worksheet, WorksheetSection class PDFGenerator: """Generates PDF worksheets from unit definitions""" def __init__(self, locale: str = "de-DE"): self.locale = locale def generate_from_unit(self, unit: dict) -> Worksheet: """Generate a worksheet from a unit definition.""" unit_id = unit.get("unit_id", "unknown") title = self._get_localized(unit.get("title"), "Arbeitsblatt") objectives = unit.get("learning_objectives", []) stops = unit.get("stops", []) sections = [] if objectives: sections.append(WorksheetSection( title="Lernziele", content_type="objectives", content=objectives )) vocab_section = self._create_vocabulary_section(stops) if vocab_section: sections.append(vocab_section) concepts_section = self._create_concepts_section(stops) if concepts_section: sections.append(concepts_section) basic_exercises = self._create_basic_exercises(stops) if basic_exercises: sections.append(WorksheetSection( title="Ubungen - Basis", content_type="exercises", content=basic_exercises, difficulty=1 )) challenge_exercises = self._create_challenge_exercises(stops, unit) if challenge_exercises: sections.append(WorksheetSection( title="Ubungen - Herausforderung", content_type="exercises", content=challenge_exercises, difficulty=3 )) sections.append(WorksheetSection( title="Reflexion", content_type="reflection", content={"prompt": "Erklaere in eigenen Worten, was du heute gelernt hast:"} )) return Worksheet( title=title, subtitle=f"Arbeitsblatt zur Unit: {unit_id}", unit_id=unit_id, locale=self.locale, sections=sections, footer="Generiert mit Breakpilot | www.breakpilot.de" ) def _get_localized(self, obj: Union[dict, str, None], default: str = "") -> str: """Get localized string from object""" if not obj: return default if isinstance(obj, str): return obj if isinstance(obj, dict): if self.locale in obj: return obj[self.locale] alt_locale = self.locale.replace("-", "_") if alt_locale in obj: return obj[alt_locale] if obj: return next(iter(obj.values())) return default def _create_vocabulary_section(self, stops: list) -> Optional[WorksheetSection]: """Create vocabulary table from stops""" rows = [] for stop in stops: vocab = stop.get("vocab", []) for v in vocab: term = self._get_localized(v.get("term")) hint = self._get_localized(v.get("hint")) if term: rows.append([term, hint or ""]) if not rows: return None return WorksheetSection( title="Wichtige Begriffe", content_type="table", content={"headers": ["Begriff", "Erklarung"], "rows": rows} ) def _create_concepts_section(self, stops: list) -> Optional[WorksheetSection]: """Create concepts summary from stops""" rows = [] for stop in stops: label = self._get_localized(stop.get("label")) concept = stop.get("concept", {}) why = self._get_localized(concept.get("why")) if label and why: rows.append([label, why]) if not rows: return None return WorksheetSection( title="Zusammenfassung", content_type="table", content={"headers": ["Station", "Was hast du gelernt?"], "rows": rows} ) def _create_basic_exercises(self, stops: list) -> list[dict]: """Create basic difficulty exercises""" exercises = [] vocab_items = [] for stop in stops: for v in stop.get("vocab", []): term = self._get_localized(v.get("term")) hint = self._get_localized(v.get("hint")) if term and hint: vocab_items.append({"term": term, "hint": hint}) if len(vocab_items) >= 3: exercises.append({ "type": "matching", "question": "Ordne die Begriffe den richtigen Erklarungen zu:", "left": [v["term"] for v in vocab_items[:5]], "right": [v["hint"] for v in vocab_items[:5]], "difficulty": 1 }) for stop in stops[:3]: concept = stop.get("concept", {}) why = self._get_localized(concept.get("why")) if why: exercises.append({ "type": "multiple_choice", "question": f"Richtig oder Falsch? {why}", "options": ["Richtig", "Falsch"], "difficulty": 1 }) break if len(stops) >= 4: labels = [self._get_localized(s.get("label")) for s in stops[:6] if self._get_localized(s.get("label"))] if len(labels) >= 4: import random shuffled = labels.copy() random.shuffle(shuffled) exercises.append({ "type": "sequence", "question": "Bringe die Stationen in die richtige Reihenfolge:", "items": shuffled, "difficulty": 2 }) return exercises def _create_challenge_exercises(self, stops: list, unit: dict) -> list[dict]: """Create challenging exercises""" exercises = [] for stop in stops: concept = stop.get("concept", {}) misconception = self._get_localized(concept.get("common_misconception")) why = self._get_localized(concept.get("why")) label = self._get_localized(stop.get("label")) if misconception and why: exercises.append({ "type": "multiple_choice", "question": f"Welche Aussage uber {label} ist RICHTIG?", "options": [why, misconception], "difficulty": 3 }) if len(exercises) >= 2: break exercises.append({ "type": "text", "question": "Erklaere einem Freund in 2-3 Satzen, was du gelernt hast:", "difficulty": 3 }) exercises.append({ "type": "text", "question": "Was moechtest du noch mehr uber dieses Thema erfahren?", "difficulty": 4 }) return exercises def generate_worksheet_html(unit_definition: dict, locale: str = "de-DE") -> str: """Generate HTML worksheet from unit definition.""" generator = PDFGenerator(locale=locale) worksheet = generator.generate_from_unit(unit_definition) return worksheet.to_html() def generate_worksheet_pdf(unit_definition: dict, locale: str = "de-DE") -> bytes: """Generate PDF worksheet from unit definition.""" try: from weasyprint import HTML except ImportError: raise ImportError("weasyprint is required for PDF generation. Install with: pip install weasyprint") html = generate_worksheet_html(unit_definition, locale) pdf_bytes = HTML(string=html).write_pdf() return pdf_bytes