An unticked checkbox is absent from a form submission entirely, so
"rotate unticked" and "rotate never mentioned" arrived at the parser
looking identical - and rotate defaults to on, so it snapped straight
back every time. Add a hidden marker the options form always sends, so
absent-but-submitted can be read as unticked.
Same bug silently reset the field list when every field was unticked;
that is fixed by the same marker. rotate_default lets a saved site-wide
preference apply on a fresh arrival without overriding an explicit
untick on the page.
LIM configured no logging at all, so the instruction fetcher had no trail to follow. Adds a rotating file handler on the root logger plus two owner-only admin pages.
- Admin > Logs: filter by level, source and text; tracebacks kept with their error; auto-refresh, raw download, rotated-file browsing and rotate-now
- Admin > Logs > Brickset API calls: every outbound call recorded with method, params, caller, duration, result count and error, plus daily quota accounting (only getSets counts) and a comparison against getKeyUsageStats
- New owner concept (services/ownership.py): a single account, distinct from is_admin, since these pages expose other admins activity. Defaults to the first admin; flask owner show/set to change
- Services now log through module loggers; retention setting and pruning for call records
- New CLI: flask owner show/set, flask logs path/prune
- New table brickset_api_calls, created automatically at boot; LOG_* env vars, /data/logs in Docker
Downloads official instruction PDFs from LEGO, located via the Brickset API, one set per interval (10 minutes by default) so neither API gets hammered. Official sets only; MOCs are excluded throughout.
- New admin page at /admin/instruction-fetch: queue builder, pacing controls, live countdown and per-set job history
- Database-backed queue (instruction_fetch_jobs) and last-run timestamp, so pacing survives restarts
- Background worker thread, plus 'flask instructions queue/run/status' for Task Scheduler or cron
- SHA-256 dedupe (instructions.sha256), PDF header check, size cap and streaming downloads via temp files
- brickset_api: getInstructions2 by set number and getInstructions by setID (the old code sent a set number to the setID method); getSets params now proper JSON; getKeyUsageStats added
- Schema top-ups applied automatically at boot: new table plus instructions.sha256/source
- Docker: fetcher env vars passed through compose, no worker thread in the entrypoint bootstrap