"""Findet im Quellordner die Notizen und Beschreibungsdateien, die zum Upload freigegeben sind. Ersteller: Pascal Michel Erstellt: 2026-08-05 Doku: docs/code/auswahl.md · Version und Änderungen: CHANGELOG.md """ from __future__ import annotations import re from pathlib import Path from bookstack.notiz import Notiz, lade_notiz MAX_TITEL = 255 FREIGABE = re.compile(r"(? bool: """Freigegeben heißt: #review-done vorhanden und kein offenes #review mehr.""" return bool(FREIGABE.search(notiz.roh)) and not OFFEN.search(notiz.roh) def beschreibungsart(pfad: Path) -> str | None: """'buch', 'kapitel' oder None — exakter Vergleich auf pfad.name.""" return BESCHREIBUNGSDATEIEN.get(pfad.name) def finde_kandidaten(quelle: Path) -> list[Notiz]: if not quelle.is_dir(): return [] gefunden: list[Notiz] = [] for pfad in sorted(quelle.rglob("*.md")): if pfad.name.startswith((".", "_")): continue notiz = lade_notiz(pfad) if ist_freigegeben(notiz): gefunden.append(notiz) return gefunden def finde_beschreibungen(quelle: Path) -> list[Notiz]: """Freigegebene Beschreibungsdateien unter quelle (rglob, Archiv eingeschlossen). Reihenfolge: alle 'buch' vor allen 'kapitel', je Art nach Pfad sortiert.""" if not quelle.is_dir(): return [] buecher: list[Notiz] = [] kapitel: list[Notiz] = [] for pfad in sorted(quelle.rglob("*.md")): art = beschreibungsart(pfad) if art is None: continue notiz = lade_notiz(pfad) if not ist_freigegeben(notiz): continue if art == "buch": buecher.append(notiz) else: kapitel.append(notiz) return buecher + kapitel def titel_von(notiz: Notiz) -> str: """Erste H1, sonst Frontmatter-title, sonst Dateiname.""" treffer = UEBERSCHRIFT.search(notiz.rumpf) if treffer: titel = treffer.group(1).strip() else: titel = notiz.frontmatter.get("title", "").strip() or notiz.pfad.stem return titel[:MAX_TITEL]