27a68292428d94cac1f6e7a6f165a9a1caee6c64
CI / test-and-package (push) Successful in 28s
Add account deletion, admin user removal, household deletion, grayed-out other-user chips (47 tests)
Add account deletion, admin user removal, household deletion, grayed-out other-user chips (47 tests)
Meal Tracker
A lightweight Flask web application for tracking household meal attendance. Members respond yes or no to lunch and dinner each day, providing an at-a-glance dashboard of who will be eating at home.
Features
- User accounts — register, log in, log out with password hashing (Werkzeug)
- Households — multi-tenant; users join an existing household or create a new one on registration
- Daily meal tracking — lunch and dinner periods auto-created for each day
- Dashboard — see all household members and their responses for a given date
- History — browse past days; responses to past dates are read-only
- Admin controls — household admin can remove members or delete the entire household
- Self-service — users can delete their own account (password confirmation required)
- SQLite — zero-dependency storage, database file persisted via Docker volume
Technology Stack
| Layer | Choice |
|---|---|
| Runtime | Python 3.13 |
| Framework | Flask 3.x |
| Database | SQLite (via Python sqlite3) |
| Passwords | Werkzeug generate_password_hash / check_password_hash |
| Frontend | Jinja2 templates, vanilla CSS |
| Deployment | Docker + docker-compose, Caddy reverse proxy network |
Project Structure
.
├── app.py # Flask application entry point
├── auth.py # Authentication blueprint (login, register, logout)
├── meals.py # Meals blueprint (dashboard, responses, history, admin)
├── models.py # Database models and helpers
├── requirements.txt # Python dependencies
├── Dockerfile # Container image definition
├── docker-compose.yml # Container orchestration
├── watch-for-updates.sh # Auto-deploy poller script
├── .gitea/ # Gitea CI/CD workflows
├── templates/ # Jinja2 HTML templates
│ ├── base.html
│ ├── login.html
│ ├── register.html
│ └── dashboard.html
├── static/
│ └── style.css
├── tests/ # Pytest test suite
│ ├── conftest.py
│ ├── test_auth.py
│ ├── test_meals.py
│ └── test_routes.py
└── doc/ # Documentation
└── index.md
Getting Started
Prerequisites
- Docker and docker-compose
- A Caddy reverse proxy network named
caddy(already present on CozyTren infrastructure)
Quick Start
# Clone the repository
git clone https://gitea.ct.cozytren.ch/romane/agentbox-test
cd agentbox-test
# Set your secret key (or it defaults to a dev value)
export SECRET_KEY="your-secret-here"
# Build and run
docker compose up -d --build
The app will be available at http://meal-tracker:5000 (reachable through Caddy if configured).
Auto-Deploy
The watch-for-updates.sh script polls the Gitea repository every 20 seconds and automatically rebuilds and redeploys the containers on new commits:
# Source credentials and start the watcher
source .env
./watch-for-updates.sh
Configuration
| Environment Variable | Default | Description |
|---|---|---|
SECRET_KEY |
change-me-in-production |
Flask session signing secret |
DB_PATH |
/data/meals.db in Docker |
Path to the SQLite database file |
GITEA_PASSWORD |
(required for auto-deploy) | Gitea password for agentbox user |
Usage
Registration
- Navigate to the app
- Click Register
- Choose a username and password
- Either join an existing household or create a new one
- The first user in a new household becomes the admin
Responding to Meals
- On the dashboard, click I'll be there (green) or I won't be there (red) for lunch and dinner
- Your response appears alongside other household members
- A timestamp records the last change between yes ↔ no
Admin Actions
- Remove a user: click the × button next to a member's name
- Delete household: use the dangerous action section (requires password confirmation)
- Delete your own account: available to all users, also requires password confirmation
API / Routes
Authentication
| Method | Path | Description |
|---|---|---|
| GET/POST | /login |
Login page |
| GET/POST | /register |
Registration page |
| GET | /logout |
Logout (clears session) |
Meals
| Method | Path | Description |
|---|---|---|
| GET | / |
Redirects to dashboard or login |
| GET | /dashboard |
Main dashboard (?date=YYYY-MM-DD to view other dates) |
| POST | /respond |
Submit a meal response (meal_type, status, date) |
| GET | /history |
History view (?date=YYYY-MM-DD) |
Account / Admin
| Method | Path | Description |
|---|---|---|
| POST | /delete-account |
Delete own account (needs password) |
| POST | /admin/remove-user/<id> |
Admin removes a household member |
| POST | /admin/delete-household |
Admin deletes the entire household |
Database Schema
households
| Column | Type | Notes |
|---|---|---|
| id | INTEGER | PRIMARY KEY AUTOINCREMENT |
| name | TEXT | UNIQUE NOT NULL |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP |
users
| Column | Type | Notes |
|---|---|---|
| id | INTEGER | PRIMARY KEY AUTOINCREMENT |
| username | TEXT | UNIQUE NOT NULL |
| password_hash | TEXT | NOT NULL |
| is_admin | INTEGER | DEFAULT 0 |
| household_id | INTEGER | REFERENCES households(id) |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP |
meal_periods
| Column | Type | Notes |
|---|---|---|
| id | INTEGER | PRIMARY KEY AUTOINCREMENT |
| date | TEXT | NOT NULL (ISO format YYYY-MM-DD) |
| meal_type | TEXT | NOT NULL, CHECK(lunch OR dinner) |
| UNIQUE(date, meal_type) |
responses
| Column | Type | Notes |
|---|---|---|
| id | INTEGER | PRIMARY KEY AUTOINCREMENT |
| user_id | INTEGER | NOT NULL, REFERENCES users(id) |
| period_id | INTEGER | NOT NULL, REFERENCES meal_periods(id) |
| status | TEXT | DEFAULT 'not_answered', CHECK(yes, no, not_answered) |
| changed_at | TIMESTAMP | Set when flipping between yes↔no |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP |
| UNIQUE(user_id, period_id) |
Running Tests
pip install -r requirements.txt
pytest tests/ -v
Tests use a temporary in-memory SQLite database (configured per test via conftest.py).
Description
A lightweight Flask meal attendance tracker with household scoping, ntfy push notifications, and i18n (en/fr/de). 100% vibecoded.
400 KiB
Languages
Python
68.4%
HTML
12.5%
CSS
7.1%
Java
7%
Shell
4.7%
Other
0.3%