- .claude/agents/planer.md - .claude/agents/quorum-stimme.md - .claude/skills/bookstack-upload/SKILL.md - .claude/skills/bookstack-upload/scripts/bookstack/archiv.py - .claude/skills/bookstack-upload/scripts/bookstack/auswahl.py - .claude/skills/bookstack-upload/scripts/bookstack/beschreibung.py - .claude/skills/bookstack-upload/scripts/bookstack/bilder.py - .claude/skills/bookstack-upload/scripts/bookstack/ergebnis.py - .claude/skills/bookstack-upload/scripts/bookstack/hochladen.py - .claude/skills/bookstack-upload/scripts/bookstack/konvertierung.py - .claude/skills/bookstack-upload/scripts/bookstack/struktur.py - .claude/skills/bookstack-upload/scripts/bookstack/verweise.py - … und 33 weitere Erzeugt vom PostToolUse-Hook .vault-tools/auto-push/auto_push.py. Co-Authored-By: Claude Opus 5 <[email protected]>
77 lines
2.3 KiB
Python
77 lines
2.3 KiB
Python
"""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"(?<![\w#-])#review-done(?![\w-])")
|
|
OFFEN = re.compile(r"(?<![\w#-])#review(?![\w-])")
|
|
UEBERSCHRIFT = re.compile(r"^#\s+(.+)$", re.MULTILINE)
|
|
BESCHREIBUNGSDATEIEN: dict[str, str] = {
|
|
"_Buchbeschreibung.md": "buch",
|
|
"_Kapitelbeschreibung.md": "kapitel",
|
|
}
|
|
|
|
|
|
def ist_freigegeben(notiz: Notiz) -> 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]
|