Plugin export: Listen als CSV (Excel-DE) und PDF (WeasyPrint)

- CSV über stdlib csv: Semikolon-getrennt, UTF-8 mit BOM, korrektes
  Quoting; PDF über WeasyPrint mit deutscher Kopfzeile (je Seite
  wiederholt), Erstell-Datum und Seitenzahlen (Seite X von Y)
- Drei Listen: Neuheiten, Planung, Archiv (Neuheiten gefiltert auf
  Status „archiviert“); Partner-Tabellen über gemeinsame Metadata,
  ohne Plugin-Importe — fehlende Plugins degradieren sauber
- Rollen: Admin/Redakteur alles, Rezensent nur Planungsliste;
  serverseitige Prüfung an den Download-Endpunkten (StreamingResponse),
  UI-Karten für nicht erlaubte Listen ausgegraut
- Jeder Download best effort im Audit-Log (Aktion „exportiert“)
- WeasyPrint lazy importiert: ohne System-Bibliotheken bleiben App und
  CSV nutzbar; Tests skippen PDF-Fälle nach Installationsversuch
- 20 neue Tests (CSV-Inhalt/BOM/Umlaute/Quoting, PDF-Gültigkeit,
  Rollen, Audit, Degradierung); 161 gesamt grün
- README: Plugin-Dokumentation, Fortschritt export auf 
This commit is contained in:
Flo Hartmann
2026-08-21 20:30:19 +00:00
parent 0f12cd952b
commit d024f42f3c
9 changed files with 1248 additions and 25 deletions

View File

@@ -4,7 +4,7 @@
modularer Plugin-Architektur. Lauffähiger Kern mit Plugin-System,
Authentifizierung/Rollen und Migrationen; vollständig implementiert sind
bisher **Audit-Log**, **Benachrichtigung**, **Neuheiten (BGG-Sync)**,
**Dedup-Prüfung** und **Planungsliste**.
**Dedup-Prüfung**, **Planungsliste** und **Export (CSV/PDF)**.
## Stack
@@ -14,6 +14,7 @@ bisher **Audit-Log**, **Benachrichtigung**, **Neuheiten (BGG-Sync)**,
- **Plugins:** Entry-Points (`importlib.metadata`, Gruppe `spiele_redaktion.plugins`)
plus lokales `plugins/`-Verzeichnis
- **Hintergrund-Jobs:** APScheduler (BGG-Neuheiten-Sync)
- **PDF-Export:** WeasyPrint (System-Bibliotheken: Pango/fontconfig)
- **Dependencies/Tests:** [uv](https://docs.astral.sh/uv/) + pytest
## Setup
@@ -73,8 +74,13 @@ Rollen am Sync-Endpunkt und Scheduler-Lifecycle. Dazu die beiden neuen
Plugins: **dedup** (Titel-Heuristik, Fuzzy-Match, alle vier Prüfungen mit
Fake-BGG-Client, Prüfprotokoll, Prüfseite) und **planung** (beide Eintragswege
mit Stub-gemockter dedup-/Benachrichtigungs-/Audit-API, Verlags-Auswahl-
Dialog, Statuswechsel, Bearbeiten/Löschen, Rechte pro Rolle) plus ein
Integrationstest mit den echten Plugins (In-App-Nachricht + Audit-Eintrag).
Dialog, Statuswechsel, Bearbeiten/Löschen, Rechte pro Rolle), das Plugin
**export** (CSV-Inhalt mit Semikolon/BOM/Umlauten inkl. Quoting, gültige
PDF-Dateien via Kopf-/Struktur-Check, Rollen-Zugriff je Liste und Format,
Audit-Protokollierung der Downloads; PDF-Tests skippen sauber, wenn die
WeasyPrint-System-Bibliotheken fehlen — ein Installationsversuch läuft
zuvor automatisch) plus ein Integrationstest mit den echten Plugins
(In-App-Nachricht + Audit-Eintrag).
## Architektur
@@ -94,7 +100,8 @@ plugins/ ein Ordner pro Funktion, ladbar über den Plugin-Load
├── benachrichtigung/ VOLL IMPLEMENTIERT: E-Mail/Telegram/In-App
├── dedup/ VOLL IMPLEMENTIERT: check_titel-API, Heuristik, Protokoll
├── planung/ VOLL IMPLEMENTIERT: Planungsliste, Verschiebung, Dialoge
├── archiv/ erinnerung/ export/ (Stubs, ladbar)
├── export/ VOLL IMPLEMENTIERT: CSV/PDF-Downloads, Rollen, Audit
├── archiv/ erinnerung/ (Stubs, ladbar)
└── … je __init__.py + templates/<name>/
```
@@ -363,6 +370,40 @@ Rezensenten können **nur ihre eigenen Einträge** bearbeiten (Statuswechsel,
Bearbeiten, Löschen); Admins und Redakteure alle. Jede Änderung wird im
Audit-Log protokolliert (`erstellt`/`verschoben`/`geaendert`/`geloescht`).
## Plugin „export“ (implementiert)
Redaktionslisten als Datei: die drei Listen **Neuheiten**, **Planung** und
**Archiv** sind je als CSV und PDF unter **Export** (`/export`) herunterladbar.
Keine eigene Datentabelle — das Plugin liest die Tabellen der Partner-Plugins
über die gemeinsame SQLAlchemy-Metadata (ohne Plugin-Importe); fehlt ein
Partner, erscheint die Liste als „nicht verfügbar“, statt zu brechen.
### Formate
| Format | Details |
|--------|---------|
| **CSV** | stdlib `csv`, Semikolon-getrennt (Excel mit deutschem Gebietsschema), UTF-8 mit Byte-Order-Mark — Umlaute/ß bleiben erhalten, Felder mit `;` werden korrekt gequotet; Auslieferung als `StreamingResponse` (`text/csv; charset=utf-8`) |
| **PDF** | WeasyPrint aus einem generierten HTML: A4-Tabelle mit deutscher Kopfzeile (wird auf jeder Seite wiederholt), Erstell-Datum und Anzahl Einträge im Kopf, Fußzeile „Seite X von Y“; Auslieferung als `StreamingResponse` (`application/pdf`) |
WeasyPrint wird erst beim PDF-Download importiert: Ohne installierte
System-Bibliotheken (Pango & Co.) bleiben App und CSV-Export voll nutzbar,
der PDF-Download liefert eine verständliche Fehlermeldung.
### Spalten
- **Neuheiten:** Titel · Verlag · Autor · Erscheinungsjahr · Status („Neuheit“, „In Planung“, „Archiviert“) · Aktualisiert am
- **Planung:** Titel · Verlag · Autor · Ausgabe · Rezensent (aufgelöster Name) · Status (Offen / In Bearbeitung / Abgeschlossen) · Notizen · Aktualisiert am
- **Archiv** = Neuheiten-Tabelle gefiltert auf Status „archiviert“ (gefüllt durch den Autopilot des archiv-Plugins)
### Rollen & Protokollierung
Admins und Redakteure exportieren alle Listen; **Rezensenten nur die
Planungsliste**. Die Prüfung passiert serverseitig an jedem Download-Endpunkt
(`403` bei Zugriff ohne Berechtigung, `404` bei unbekannter Liste); auf der
Export-Seite sind nicht erlaubte Karten ausgegraut. Jeder Download wird best
effort ins Audit-Log geschrieben (Aktion „exportiert“, Format und Zeilenzahl
in den Details).
## Deployment (später)
Docker/Podman Compose ist vorgesehen (Henry-Lab, danach Kundenhardware).
@@ -383,7 +424,7 @@ geschrieben.
| 6 | Plugin `planung` (Verschiebung, händischer Eintrag + Prüfungen + Benachrichtigung) | ✅ fertig (141 Tests grün) |
| 7 | Plugin `archiv` (12-Monats-Autopilot) | ⏳ offen |
| 8 | Plugin `erinnerung` (Redaktionsschluss pro Ausgabe, 4-Wochen-Erinnerung) | ⏳ offen |
| 9 | Plugin `export` (CSV + PDF via WeasyPrint) | ⏳ offen |
| 9 | Plugin `export` (CSV + PDF via WeasyPrint) | ✅ fertig (161 Tests grün) |
| 10 | Integrationstests über alle Plugins | ⏳ offen |
| 11 | Deployment auf swen.henry.insight-it.de (Compose + Traefik, Live-Check) | ⏳ offen |
@@ -393,5 +434,4 @@ Legende: ✅ fertig · 🔄 in Arbeit · ⏳ offen · ⚠️ fertig mit offenen
- CSRF-Schutz für Formulare (aktuell SameSite=Lax-Cookie als Basisschutz)
- Hintergrund-Jobs (APScheduler) für Erinnerungen
- WeasyPrint für PDF-Export (Systemabhängigkeiten im Container)
- Tailwind via CDN nur für Dev; für Produktion lokal gehostete Assets