Operations runbook¶
Monitoring a run¶
monitorTUI —python -m evilflowers_books_digitalizer monitor: live per-faculty progress bars, ok/skip/err, pages, output MB, pages/min, ETA, plus a "working now" panel showing each parallel worker in flight (book, current step, a step progress bar, elapsed). Reads the report files + per-book heartbeats underoutput/.progress/, so run it from anywhere that seesoutput_dir(host,docker exec, synced volume) — it doesn't touch the running batch. Workers refresh their heartbeat at each step boundary and remove it when done; a leftover from a crashed worker is ignored afterprogress.DEFAULT_TTLand wiped at the nextrun-source.stats— one-shot snapshot. Prints the same rich table asmonitor(add asourceto scope it);--jsonfor cron/Slack;--export csv|json|html [--out PATH]to write a handover file (per-book CSV, summary JSON, or a self-contained HTML report with bars). Shows the catalog-import tally too when publish reports exist.- Log — stderr and
output/logs/digitizer.log. Text by default;EVILFLOWERS_LOG_FORMAT=jsongives one JSON object per line withsource,book_id,step,duration_ms,status,entry_idattached (format in deployment.md) — filter by book or step in Loki.EVILFLOWERS_LOG_LEVELsets the level. - JSONL reports —
output/batch_report_<src>.jsonl(digitization) andoutput/publish_report_<src>.jsonl(catalog import), one row per book (re-runs append;stats/monitordedup to the newest per book).
Resuming¶
Re-running run-book/run-source/run-corpus is safe and cheap: process_book
skips any book whose distribution PDF already exists, so completed books return
skipped quickly. Just start the same command again after an interruption or
reboot. Under systemd, Restart=on-failure does this automatically; a k8s Job
just re-runs the same run-book … --publish.
Before a run, evilflowers-digitalizer check (add --catalog when publishing)
catches unreadable mounts, read-only volumes, low disk and missing binaries.
Disk¶
Low-storage mode is on by default: staged frames (symlinks), ScanTailor output
and the pre-OCR PDF are deleted per book; only <slug>.pdf, <slug>.txt and
<slug>.cover.jpg remain. A worker waits (up to disk_wait_minutes) for space
when free disk drops below [orchestration].min_free_gb, then records an error
for that book if it never recovers. Offload output/ to durable storage
periodically (the whole corpus is ~65–70 GB of PDFs).
Concurrency tuning¶
max_parallel_books * ocr_jobs ≤ CPU cores. Start conservative (e.g. 4 × 2 on an
8-core VM) and watch CPU/disk via monitor + top. Change it in
configs/pipeline.toml and restart the run (it resumes).
Common issues¶
| Symptom | Likely cause / fix |
|---|---|
| Most books error | Systematic problem — scantailor-deviant-cli / recode_pdf / tesseract missing, or the mount unreadable. Check output/logs/digitizer.log or a report row's error. |
| All titles are de-slugged directory names | Catalog didn't match — run validate-catalog, check [metadata].columns and key_field. |
| Covers all generated (no real ones) | OPAC/obalkyknih unreachable from the VM, or [cover].source set to generated. Cover errors are non-fatal. |
low disk errors |
Free space below min_free_gb on cache_dir's filesystem; offload output/ or raise disk. |
check fails … writable in a container |
The uid the container runs as cannot write the volume; fix ownership or --user. |
Publish row conflict |
The catalog already holds a different book with that title/ISBN. Fix the title/ISBN or route it via cieľový_katalog, then re-run. |
Publish row error with an entry_id |
The upload failed after the entry was created; just re-run — it resumes into the same entry. |
Catalog & cover iteration¶
python -m evilflowers_books_digitalizer validate-catalog # match/miss across all sources
python -m evilflowers_books_digitalizer preview-cover svf <book_id> --template banner
preview-cover needs only the Excel (not the mount), so you can iterate on
palette/template quickly, then re-deploy.
Catalog import¶
Once books are produced, publish them into the EvilFlowers Catalog (see
catalog_import.md for the full flow and [catalog] config):
export EVILFLOWERS_API_KEY="…"
evilflowers-digitalizer check --catalog # token, ping, target catalogs
evilflowers-digitalizer publish-catalog --dry-run # validate payloads, no HTTP
evilflowers-digitalizer publish-catalog # resumable push
Re-runs are safe: books already recorded published/skipped are skipped, an
interrupted book resumes into the entry it already created, and a 409 is only
adopted when it is provably the same book. Progress shows in stats/monitor.
Handover exports¶
python -m evilflowers_books_digitalizer stats --export csv # per-book spreadsheet
python -m evilflowers_books_digitalizer stats --export html # shareable styled report
python -m evilflowers_books_digitalizer stats svf --export csv # one faculty only
Files land at output/stats[_<source>].<ext> unless --out is given.