CI / test-and-package (push) Successful in 36s
- docker-compose.yml: meal-cron service loops send_reminders_cron.py every 60s - send_reminders_cron.py: checks each household's local time, sends ntfy reminders at 10:00 (lunch) and 16:00 (dinner), guarded against duplicates - models.py: timezone, last_lunch_reminder, last_dinner_reminder on households - meals.py: /admin/timezone route for admins to set household timezone - dashboard: timezone selector (20 common zones) in Account Settings - i18n: 5 new translation keys for timezone strings - Dockerfile: copy send_reminders_cron.py - tests: 4 new timezone tests (admin set, non-admin denied, default UTC, auth) - docs: updated AGENTS.md
4.6 KiB
4.6 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.py |
Standalone script run every 60s by the meal-cron container. Checks per-household local time against 10:00/16:00 triggers. |
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.