CI / test-and-package (push) Successful in 33s
- meal-cron now runs send_reminders_cron.sh (simple wget loop, no DB mount) - New /api/cron-tick endpoint handles all timezone logic server-side - base_url captured from request.host_url when user saves ntfy settings, stored per-user — no APP_BASE_URL env var needed - Cron endpoint protected by CRON_SECRET shared env var - Removed send_reminders_cron.py, DB volume from cron container - tests: 3 new cron-tick + base_url capture tests (69 total)
4.7 KiB
4.7 KiB
AGENTS.md — Meal Tracker
Overview
Flask web app for tracking household meal attendance. Users register into households, then mark yes/no for lunch & dinner each day. A dashboard shows all members' responses. SQLite backend, Docker deployment behind Caddy reverse proxy.
- Repo:
https://gitea.ct.cozytren.ch/romane/agentbox-test - Production:
https://meals.ct.cozytren.ch - Auto-deploy:
watch-for-updates.shpolls for new commits every 20s, rebuilds viadocker compose, redeploys. Push-to-deploy in ~1 min.
Files — What Goes Where
| File | Role |
|---|---|
app.py |
Flask factory (create_app()), registers blueprints and i18n |
auth.py |
Auth blueprint: /login, /register, /logout. Session-based. |
meals.py |
Meals blueprint: /dashboard, /respond, /history, /delete-account, /admin/*, /ntfy-settings, /ntfy-test, /api/ntfy-callback |
models.py |
All DB access. SQLite via sqlite3. 4 tables: households, users, meal_periods, responses. Ntfy helpers in # Ntfy / notifications section. |
i18n.py |
Translation module. t(key) helper for en/fr/de. Detects Accept-Language, session override via ?lang=. All user-visible strings flow through t(). |
ntfy.py |
Ntfy notification helpers. send_meal_reminder() sends push with Home/Out HTTP action buttons. send_test_notification() verifies config. |
templates/ |
Jinja2: base.html (nav, lang footer), login.html, register.html, dashboard.html |
static/style.css |
All CSS (no framework) |
tests/ |
Pytest suite (47 tests): conftest.py sets up in-memory DB per test |
Dockerfile |
Python 3.13-alpine. Must list every .py file in the COPY line. |
docker-compose.yml |
Two services: meal-tracker (web) and meal-cron (sends reminders). Both on external caddy network, shared meal-data volume at /data. |
watch-for-updates.sh |
Polls git remote, rebuilds on change. Uses docker compose with env vars from .env. |
send_reminders_cron.sh |
Simple shell loop run by meal-cron: calls POST /api/cron-tick on meal-tracker:5000 every 60s with X-Cron-Secret header. No DB access — all logic is server-side. |
doc/index.md |
Full project documentation |
README.md |
Symlink → doc/index.md |
Key Conventions
- Blueprints:
auth_bpandmeals_bpregistered inapp.py. Decorator@login_requiredon protected routes. - Session keys:
user_id,username,is_admin,household_id,household_name,lang. - i18n: Every user-visible string in templates uses
{{ t('English text') }}. In Python,flash(t("message"), "category"). Translations live inTRANSLATIONSdict ini18n.py. English string is the key; only non-English translations stored. When adding new user-facing text, add it toTRANSLATIONSfor fr/de. - DB: All access through
get_db()context manager inmodels.py. Returnssqlite3.Rowobjects accessed as dicts. Path viaDB_PATHenv var (defaults to/data/meals.dbin Docker,./meals.dblocally). - CSS: No framework. Variables in
:root. Use existing utility classes (.btn,.chip,.badge, etc.). - Ntfy: Per-user topic URL + Bearer token stored in
userstable. Callback token auto-generated on first ntfy setup./api/ntfy-callbackis unauthenticated (uses callback token). Send reminders viaflask send-reminders [--date] [--meal] [--base-url]. Schedule with cron.
Common Pitfalls
- Dockerfile COPY must include every
.pyfile. If you add a new Python module, add it to the COPY line. Missing files causeModuleNotFoundError→ container crash → 502. meal_typevalues are lowercase (lunch,dinner) in DB/forms. Templates capitalize them for display:period.meal_type|capitalize.- Past dates are read-only. The respond route rejects dates before today. Dashboard shows badges instead of buttons for other users' rows.
changed_atlogic: Set only when flipping betweenyes↔no. First answer andnot_answeredtransitions leave it NULL.- Response status values:
yes,no,not_answered(stored in DB; CSS classes and display strings derived from these).
Running Locally (no Docker)
pip install -r requirements.txt
python app.py # or: flask --app 'app:create_app()' run --debug
pytest tests/ -v
Running with Docker
docker compose up -d --build # needs caddy network
# or standalone:
docker build -t meal-tracker .
docker run -p 5000:5000 meal-tracker
Adding a New Language
- Add the language code and name to
LANGUAGESini18n.py. - Add translations for every key in
TRANSLATIONS(keys are the English strings). - Add the language to the footer loop in
templates/base.html.