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
70 lines
4.6 KiB
Markdown
70 lines
4.6 KiB
Markdown
# 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.sh` polls for new commits every 20s, rebuilds via `docker 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_bp` and `meals_bp` registered in `app.py`. Decorator `@login_required` on 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 in `TRANSLATIONS` dict in `i18n.py`. English string is the key; only non-English translations stored. When adding new user-facing text, add it to `TRANSLATIONS` for fr/de.
|
|
- **DB:** All access through `get_db()` context manager in `models.py`. Returns `sqlite3.Row` objects accessed as dicts. Path via `DB_PATH` env var (defaults to `/data/meals.db` in Docker, `./meals.db` locally).
|
|
- **CSS:** No framework. Variables in `:root`. Use existing utility classes (`.btn`, `.chip`, `.badge`, etc.).
|
|
- **Ntfy:** Per-user topic URL + Bearer token stored in `users` table. Callback token auto-generated on first ntfy setup. `/api/ntfy-callback` is unauthenticated (uses callback token). Send reminders via `flask send-reminders [--date] [--meal] [--base-url]`. Schedule with cron.
|
|
|
|
## Common Pitfalls
|
|
|
|
1. **Dockerfile COPY must include every `.py` file.** If you add a new Python module, add it to the COPY line. Missing files cause `ModuleNotFoundError` → container crash → 502.
|
|
2. **`meal_type` values are lowercase** (`lunch`, `dinner`) in DB/forms. Templates capitalize them for display: `period.meal_type|capitalize`.
|
|
3. **Past dates are read-only.** The respond route rejects dates before today. Dashboard shows badges instead of buttons for other users' rows.
|
|
4. **`changed_at` logic:** Set only when flipping between `yes` ↔ `no`. First answer and `not_answered` transitions leave it NULL.
|
|
5. **Response status values:** `yes`, `no`, `not_answered` (stored in DB; CSS classes and display strings derived from these).
|
|
|
|
## Running Locally (no Docker)
|
|
|
|
```bash
|
|
pip install -r requirements.txt
|
|
python app.py # or: flask --app 'app:create_app()' run --debug
|
|
pytest tests/ -v
|
|
```
|
|
|
|
## Running with Docker
|
|
|
|
```bash
|
|
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
|
|
|
|
1. Add the language code and name to `LANGUAGES` in `i18n.py`.
|
|
2. Add translations for every key in `TRANSLATIONS` (keys are the English strings).
|
|
3. Add the language to the footer loop in `templates/base.html`.
|