# quartermaster Household budget tracker. FastAPI + HTMX frontend, SQLite backend. ## Pages * `/` budget configuration. One section per category (Incomes, Fixed Amount Bills, Debt Minimums, Primary Debt Target, Food and Essentials, Subscriptions, Other). Every section accepts name + amount entries and shows a running total. The Primary Debt Target is a pointer to a Debt Minimums row. * `/month/YYYY-MM` monthly view. Snapshots the budget at creation time and tracks an `applied` amount per entry alongside the planned amount. Each row is annotated when its name or planned value has been edited away from the snapshot, or when the row was added after creation. Per-month debt target is independent of the budget's target after snapshot. Navigate between months with the prev / next buttons or the dropdown picker. A "This month" link on `/` jumps to the current `YYYY-MM`; if it has not been created yet, you land on the create flow. ## Requirements * Python 3.12+ * [uv](https://github.com/astral-sh/uv) for dependency management ## Setup ```sh uv sync uv run alembic upgrade head ``` The SQLite file lives at `./quartermaster.db` by default. Override with the `QUARTERMASTER_DB_URL` environment variable (any SQLAlchemy URL). ## Run ```sh uv run uvicorn quartermaster.main:app \ --log-config src/quartermaster/logconfig.json \ --reload ``` Open http://127.0.0.1:8000. ## Logs Logs are JSON on stdout. Each line has `level` and `event` as queryable Loki labels (indexed by Promtail on the deploy host), plus arbitrary extra fields in the JSON body. Example LogQL queries in Grafana Explore (Loki data source): ``` {container="quartermaster"} | json {container="quartermaster", event="http_request", status=~"5.."} {container="quartermaster", event="month_closed"} | json | line_format "{{.path}} {{.message}}" ``` HTTP access logs appear as `event="http_request"` with `method`, `path`, `status`, and `client_ip` extras. Application events (`month_created`, `month_closed`, `template_entry_updated`, `posting_added`, `posting_deleted`) fire at the matching mutation sites. ## Tests ```sh uv run pytest ``` Tests run against an in-memory SQLite database; no migration step needed. ## Backups The SQLite data file is precious and gitignored. Before any schema change or destructive operation, back it up: ```sh ./scripts/backup-db.sh ``` Backups land next to the database in `./backups/` by default (`QUARTERMASTER_BACKUP_DIR=/some/path` to override) as `quartermaster-YYYYMMDD-HHMMSS-{slug}.db` using SQLite's online backup API (safe even while the app is writing). Alembic invokes the script automatically before every migration via `alembic/env.py`; retention is forever for now. To restore, stop the app, copy the chosen backup over `quartermaster.db`, and restart. ## Project Layout ``` src/quartermaster/ main.py FastAPI app factory routes.py Budget configuration HTTP handlers routes_month.py Monthly view HTTP handlers service.py Budget queries, totals, target logic month_service.py Snapshot, deviation, per-month CRUD models.py SQLAlchemy models and Section enum db.py Engine, session, PRAGMA foreign_keys=ON config.py DB URL resolution templates/ Jinja2 templates (base, index, month, partials) static/ CSS alembic/ Migrations tests/ pytest suite ``` ## Deferred A transaction log that rolls up into `applied` on a per-entry per-month basis is deferred. Once implemented it may replace the hand-edited applied field.