fix: Fehlerzaehlung, Upload-Fehler und ocr.timeout scharf (v0.4.0)

- [ocr].timeout wird als tesseract_timeout (pro Seite) an ocrmypdf
  durchgereicht; Default 1800 -> 300, 0 = ocrmypdf-Default
- Exceptions nach dem OCR zaehlen als Fehler, Datei wird nach error/ gerettet
- Fehlgeschlagene Uploads zaehlen als Fehler und loesen Fehler-Mail aus
- name_mode wird im Preflight geprueft, nicht erst pro Datei
- Fehlende [paths]-Sektion -> ConfigError mit klarer Meldung statt KeyError
- Stabilitaets-Timeout zaehlt als Fehler (--once liefert Exit 1)
- upload_folder nutzt shutil.copyfile statt read_bytes/write_bytes
- OcrConfig.pdfa_level Default "2" -> "" (Ghostscript-Bug, Issue #3)
- 35 neue Tests (92 gesamt), pytest.ini
- AI_AGENT_BRIEFING.md auf Stand 0.4.0 gebracht

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-22 21:01:38 +02:00
parent cbdc9d6664
commit 578472872e
17 changed files with 890 additions and 76 deletions
+58
View File
@@ -1,5 +1,63 @@
# Changelog
## [0.4.0] - 2026-09-22
### Added
- `[ocr].timeout` ist jetzt wirksam: der Wert wird als `tesseract_timeout`
(Sekunden pro Seite) an ocrmypdf durchgereicht. Bisher war der Key zwar
dokumentiert, wurde aber nirgends gelesen.
- `check_output_config()` validiert zusätzlich `[output].name_mode`. Ein Tippfehler
führt jetzt beim Start zum Abbruch mit Exit-Code 2, statt erst pro Datei
zuzuschlagen — und zwar bisher **nach** dem Verschieben nach `working/`,
wo die Datei dann liegen blieb.
- Neue Exception `ConfigError` in `pdf_ocr_hotfolder.config` — fehlende
`[paths]`-Sektion oder ein fehlender Pfad-Eintrag liefern eine deutsche
Fehlermeldung mit Datei- und Key-Nennung statt eines nackten `KeyError`-Tracebacks.
Die CLI bricht damit sauber mit Exit-Code 2 ab.
- `pytest.ini` mit `testpaths = tests`, damit `pytest` aus dem Repo-Root läuft.
- 35 neue Tests: Fehlerzählung (Exception, Upload, Stabilitäts-Timeout),
Config-Fehlermeldungen, `tesseract_timeout`-Durchreichung (ocrmypdf gemockt)
und `upload_folder()`.
### Changed
- **`[ocr].timeout` hat eine neue Bedeutung — für bestehende Installationen relevant!**
Der Wert ist kein (nie implementiertes) Gesamt-Timeout pro PDF mehr, sondern
das Limit **pro Seite** für Tesseract. Der Default sinkt entsprechend von
`1800` auf `300`. Wer den alten Wert `1800` in seiner `config.toml` stehen hat,
gibt Tesseract damit 30 Minuten **je Seite** — bitte auf einen Seiten-Wert
anpassen (Richtwert 300).
`0` bedeutet "kein eigenes Limit": der Wert wird dann gar nicht erst
durchgereicht, weil ocrmypdf `tesseract_timeout=0` als "OCR komplett
überspringen" interpretiert.
- `_dispatch_uploads()` liefert jetzt die Namen der fehlgeschlagenen Upload-Ziele
zurück; die doppelte `enabled`-Prüfung (Service + Uploader) ist entfallen —
die Uploader prüfen das selbst.
- `upload_folder()` kopiert mit `shutil.copyfile()` statt
`read_bytes()`/`write_bytes()` — große PDFs landen nicht mehr komplett im
Speicher. Die Selbst-Ziel-Erkennung bleibt unverändert.
### Fixed
- `OcrConfig.pdfa_level` hatte im Code noch den Default `"2"`, obwohl
`config.example.toml` seit 0.2.2 bewusst `""` setzt (Ghostscript-Bug, Issue #3).
Eine Config ohne `[ocr]`-Sektion bzw. ohne den Key lief damit ungewollt in
PDF/A. Default im Code jetzt ebenfalls `""`.
- Eine Exception **nach** dem OCR (z.B. ein fehlgeschlagener
`shutil.move()` nach `outgoing/`) wurde nur im Worker-Callback geloggt.
`error_count` blieb 0 und `--once` lieferte trotz Fehlschlag Exit-Code 0.
Jede Exception aus `process_pdf()` zählt jetzt als Fehler, wird geloggt,
löst eine Fehler-Mail aus und die Datei wandert — soweit noch auffindbar
(`incoming/` oder `working/`) — nach `error/`.
- Fehlgeschlagene Uploads waren folgenlos: die Rückgabewerte der Uploader wurden
verworfen, es ging sogar eine Erfolgs-Mail raus. Jetzt zählt mindestens ein
fehlgeschlagenes Ziel als Fehler und die E-Mail geht als **FEHLER** raus, mit
Nennung der betroffenen Ziele. Das OCR-PDF bleibt bewusst in `outgoing/`
liegen (das OCR selbst war ja erfolgreich) — das steht so auch im Log.
- Lief der Stabilitäts-Check einer Datei in den 60-Sekunden-Timeout, gab es nur
ein `log.warning`; `--once` meldete Exit-Code 0. Jetzt `log.error` +
`error_count`. Die Datei bleibt bewusst in `incoming/` liegen und wird beim
nächsten Lauf erneut versucht. Eine zwischenzeitlich *verschwundene* Datei
wird davon unterschieden und zählt weiterhin nicht als Fehler.
## [0.3.1] - 2026-04-10
### Fixed