"""Parser für buch/GLIEDERUNG.md (Format siehe prompts/gliederung.md).""" import re KAPITEL_RE = re.compile(r"^\*\*Kapitel (\d+) – (.+?)\*\* \(`([0-9]{2}-[a-z0-9-]+\.md)`\)\s*$") TEIL_RE = re.compile(r"^## (Teil .+?)\s*$") TITEL_RE = re.compile(r"^# Gliederung – (.+?)(?:\. (.+))?$") # Richtwerte je Abschnitt (Wörter); ein Kapitel soll eine Lesesitzung bleiben. RICHTWERT = {"kurz": 800, "mittel": 1300, "lang": 2000} KAPITEL_MAX = 8000 def parsen(text: str) -> dict: """→ {"titel", "untertitel", "kapitel": [{"n","titel","datei","teil","abschnitte":[{"nr","titel","gewicht","richtwert"}]}], "fehler": [...]}""" aus = {"titel": "", "untertitel": "", "kapitel": [], "fehler": []} teil = "" zeilen = text.splitlines() i = 0 while i < len(zeilen): z = zeilen[i].rstrip() m = TITEL_RE.match(z) if m and not aus["titel"]: aus["titel"], aus["untertitel"] = m.group(1).strip(), (m.group(2) or "").strip() elif TEIL_RE.match(z): teil = TEIL_RE.match(z).group(1) elif KAPITEL_RE.match(z): m = KAPITEL_RE.match(z) n = int(m.group(1)) k = {"n": n, "titel": m.group(2).strip(), "datei": m.group(3), "teil": teil, "abschnitte": []} # nächste nicht-leere Zeile: Abschnitte j = i + 1 while j < len(zeilen) and not zeilen[j].strip(): j += 1 if j < len(zeilen): for teilstueck in re.split(r"\s+·\s+", zeilen[j].strip()): am = re.match(r"^(\d+)\.(\d+)\s+(.+?)(?:\s*\((kurz|mittel|lang)\))?$", teilstueck.strip()) if am and int(am.group(1)) == n: gewicht = am.group(4) or "mittel" k["abschnitte"].append({"nr": f"{am.group(1)}.{am.group(2)}", "titel": am.group(3).strip(), "gewicht": gewicht, "richtwert": RICHTWERT[gewicht]}) else: aus["fehler"].append(f"Kapitel {n}: Abschnittszeile unlesbar: „{teilstueck.strip()[:60]}“") i = j if not k["datei"].startswith(f"{n:02d}-"): aus["fehler"].append(f"Kapitel {n}: Dateiname muss mit {n:02d}- beginnen ({k['datei']})") aus["kapitel"].append(k) elif z.startswith("**Kapitel"): aus["fehler"].append(f"Kapitelzeile nicht im Format: „{z[:80]}“") i += 1 for k in aus["kapitel"]: if not 2 <= len(k["abschnitte"]) <= 7: aus["fehler"].append(f"Kapitel {k['n']} hat {len(k['abschnitte'])} Abschnitte (2–7 verlangt; mehr Stoff → Kapitel teilen)") summe = sum(a["richtwert"] for a in k["abschnitte"]) if summe > KAPITEL_MAX: aus["fehler"].append(f"Kapitel {k['n']}: Richtwerte summieren sich auf {summe} Wörter (höchstens {KAPITEL_MAX}) – Kapitel teilen oder Gewichte senken") nummern = [k["n"] for k in aus["kapitel"]] if nummern != list(range(1, len(nummern) + 1)): aus["fehler"].append(f"Kapitelnummern nicht fortlaufend: {nummern}") if not aus["kapitel"]: aus["fehler"].append("Keine Kapitelzeile gefunden") return aus