Files
Klotzkette 8985200e16 feat(v50.10.0): Akten-ZIP-Download zusaetzlich zum Gesamt-PDF in allen Testakten-READMEs (#159)
User-Wunsch: In jedem Testakten-README soll oben sowohl das Gesamt-PDF als auch
die Akten-ZIP mit allen Einzeldateien direkt verlinkt sein.

- scripts/inject-gesamt-pdf-section.py: Autogen-Sektion erweitert. Zwei Zeilen:
  Gesamt-PDF (lokal eingecheckt) und Akten-ZIP (stabile URL auf
  releases/latest/download/testakte-<slug>.zip). Idempotent.
- Alle 63 Testakten-READMEs neu generiert.
- CI-Workflow baut die testakte-<slug>.zip schon seit erstem Release; die URLs
  werden ab v50.10.0 nun auch in den READMEs angezeigt.
- Marketplace 50.9.1 -> 50.10.0; Generator laeuft. 108 Plugins / 2655 Skills.
2026-05-30 15:53:28 +02:00

144 lines
5.2 KiB
Python

#!/usr/bin/env python3
"""Fuegt in jede testakten/<name>/README.md prominent ganz oben eine
Akte-komplett-Sektion ein mit zwei Downloads:
1. Gesamt-PDF (im Repo unter gesamt-pdf/<slug>_gesamt.pdf eingecheckt)
2. Akten-ZIP mit allen Einzeldateien (aus dem GitHub-Release,
stabile URL releases/latest/download/testakte-<slug>.zip).
Idempotent ueber HTML-Marker. Position: direkt nach dem H1, vor allen
weiteren Sektionen (insbesondere vor dem Direkt-Download-Block).
"""
from __future__ import annotations
import re
import sys
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent.parent
TESTAKTEN_DIR = REPO_ROOT / "testakten"
# Hinweis: Die Marker heißen weiterhin "gesamt-pdf-section", damit bestehende
# READMEs idempotent aktualisiert werden. Der Inhalt der Sektion umfasst aber
# inzwischen sowohl das Gesamt-PDF als auch die Akten-ZIP.
MARKER_BEGIN = "<!-- BEGIN gesamt-pdf-section (autogen) -->"
MARKER_END = "<!-- END gesamt-pdf-section (autogen) -->"
RELEASE_BASE = (
"https://github.com/Klotzkette/claude-fuer-deutsches-recht/releases/latest/download"
)
def section_block(slug: str, pdf_rel: str | None, size_kb: int | None) -> str:
zip_url = f"{RELEASE_BASE}/testakte-{slug}.zip"
if pdf_rel is not None and size_kb is not None:
rows = (
f"| Gesamt-PDF (alles in einer Datei, {size_kb} KB) | PDF | [`{pdf_rel}`]({pdf_rel}) |\n"
f"| Akten-ZIP (alle Einzeldateien) | ZIP | [testakte-{slug}.zip]({zip_url}) |"
)
intro = (
"Diese Arbeitsakte gibt es in zwei Formaten zum Direkt-Download. Das Gesamt-PDF eignet sich zum Lesen, Ausdrucken und für schnelle Durchsichten. Das Akten-ZIP enthält sämtliche Originaldateien (Markdown-Aktenstücke, Tabellen, E-Mails, Fotos, PDFs, DOCX, XLSX) im Originalordnerlayout für eigene Auswertungen."
)
trailer = "Die ZIP-URL ist stabil und zeigt immer auf die aktuelle Version. Im Akten-ZIP ist das Gesamt-PDF mit enthalten."
else:
rows = f"| Akten-ZIP (alle Einzeldateien) | ZIP | [testakte-{slug}.zip]({zip_url}) |"
intro = (
"Diese Arbeitsakte gibt es als Akten-ZIP zum Direkt-Download. Es enthält sämtliche Originaldateien (Markdown-Aktenstücke, Tabellen, E-Mails, Fotos, PDFs, DOCX, XLSX) im Originalordnerlayout für eigene Auswertungen."
)
trailer = "Die ZIP-URL ist stabil und zeigt immer auf die aktuelle Version."
return f"""{MARKER_BEGIN}
## Akte komplett herunterladen
{intro}
| Was | Format | Quelle |
| --- | --- | --- |
{rows}
{trailer}
{MARKER_END}
"""
H1_RE = re.compile(r"^# .+$", re.MULTILINE)
def inject(readme: Path, slug: str) -> str:
pdf = readme.parent / "gesamt-pdf" / f"{slug}_gesamt.pdf"
if pdf.exists():
size_kb = max(1, round(pdf.stat().st_size / 1024))
pdf_rel = f"gesamt-pdf/{slug}_gesamt.pdf"
else:
size_kb = None
pdf_rel = None
new_section = section_block(slug, pdf_rel, size_kb)
text = readme.read_text(encoding="utf-8")
# Falls bereits eingefuegt: ersetzen
pat = re.compile(
re.escape(MARKER_BEGIN) + r".*?" + re.escape(MARKER_END) + r"\n?",
re.DOTALL,
)
if pat.search(text):
new_text = pat.sub(new_section, text, count=1)
if new_text == text:
return "unchanged"
readme.write_text(new_text, encoding="utf-8")
return "updated"
# Erstmaliges Einfuegen nach dem ersten H1
m = H1_RE.search(text)
if not m:
# Kein H1 - oben einfuegen
new_text = new_section + "\n" + text
else:
end = m.end()
# Falls nach H1 noch eine Leerzeile, dahinter setzen
rest = text[end:]
# konsumiere genau eine Leerzeile, falls vorhanden
if rest.startswith("\n\n"):
insert_at = end + 2
elif rest.startswith("\n"):
insert_at = end + 1
else:
insert_at = end
new_text = text[:insert_at] + "\n" + new_section + "\n" + text[insert_at:]
readme.write_text(new_text, encoding="utf-8")
return "inserted"
def main() -> int:
if not TESTAKTEN_DIR.exists():
print(f"Testakten-Verzeichnis nicht gefunden: {TESTAKTEN_DIR}", file=sys.stderr)
return 1
stats = {"inserted": 0, "updated": 0, "unchanged": 0, "skip": 0}
for sub in sorted(TESTAKTEN_DIR.iterdir()):
if not sub.is_dir():
continue
readme = sub / "README.md"
if not readme.exists():
# Fallback: erstes 00_*.md oder aktenuebersicht*.md
candidates = sorted(sub.glob("00_*.md")) + sorted(sub.glob("aktenuebersicht*.md"))
if candidates:
readme = candidates[0]
else:
print(f" SKIP {sub.name}: keine README.md / 00_*.md")
stats["skip"] += 1
continue
result = inject(readme, sub.name)
key = result.split()[0] if result.startswith("skip") else result
if key not in stats:
key = "skip"
stats[key] += 1
print(f" {result.upper():<9} {sub.name}")
print(
f"\nFertig: {stats['inserted']} neu, {stats['updated']} aktualisiert, "
f"{stats['unchanged']} unveraendert, {stats['skip']} uebersprungen"
)
return 0
if __name__ == "__main__":
sys.exit(main())