6bad2a2ad1f16f2ca559829ae81a7b18721ac2d1
Require login for /app/, with a long sliding session so you log in once per device and effectively stay in. - AppLoginRequiredMiddleware gates only /app/; /api/ keeps DRF token auth and /admin/ keeps its own login (a blanket LoginRequired would break token requests, whose user isn't resolved until the view runs). - Login page (styled to the dark palette) via django.contrib.auth.urls; logout control in the nav. - Session: ~1 year cookie, sliding (saved every request), survives browser close. - Dropped every @csrf_exempt now that a real session + CSRF token are in place (HTMX already sends X-CSRFToken). - SECRET_KEY and DEBUG now read from the environment (prod-safe defaults); systemd loads an optional /var/lib/food/.env. - Tests authenticate, plus new coverage: /app/ redirects when logged out, login grants access, /api/ is not caught by the app gate. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Food — Meal Planning System
A custom Django app for pantry tracking, meta-recipes, and meal planning. Built for Tom, operated primarily through Caine (OpenClaw AI assistant) via Matrix chat, with Django admin as the web UI.
Architecture
- Django 5.2 (latest LTS) with SQLite backend
- Django REST Framework for API with token auth
- HTMX frontend planned (Phase 4)
- App name:
kitchen
Core Concept: Meta-Recipes
Instead of rigid recipes, the system uses templates with swappable slots:
"Stir Fry" = protein (chicken OR pork) + carb (noodles OR rice) + veg (any combo) + sauce
This matches how Tom actually cooks — same method, different ingredients depending on what's in the pantry.
Models
| Model | Purpose |
|---|---|
Tag |
Flexible ingredient tags (protein, carb, veg, etc.) |
Ingredient |
Master ingredient list with aliases and shelf life |
PantryItem |
Current kitchen inventory with location and expiry |
MetaRecipe |
Cooking template with swappable slots |
Slot |
A category in a meta-recipe (protein, carb, veg) |
SlotOption |
An ingredient that can fill a slot, with qty/serving |
MetaRecipeBase |
Always-needed ingredients (onion, garlic, oil) |
Recipe |
Traditional fixed-ingredient recipe |
RecipeIngredient |
Ingredient in a fixed recipe |
CookLog |
What was cooked and when |
ShoppingListItem |
Shopping list with reasons and checkboxes |
Setup
cd /var/lib/food
source venv/bin/activate
# Run migrations
python manage.py migrate
# Seed initial data (ingredients, pantry, meta-recipes)
python manage.py seed
# Create superuser (if not already done)
python manage.py createsuperuser
# Run dev server
python manage.py runserver 0.0.0.0:8042
Access
- Admin UI:
http://localhost:8042/admin/ - Browsable API:
http://localhost:8042/api/ - cgit:
https://git.jihakuz.xyz/food/
API
All endpoints require auth. Token auth for Caine, session auth for browser.
Key Endpoints
| Method | Endpoint | Purpose |
|---|---|---|
| GET | /api/what-can-i-cook/?servings=2 |
Match pantry vs recipes |
| POST | /api/log-cook/ |
Log a cooked meal, optionally deduct from pantry |
| GET | /api/pantry/ |
List all pantry items |
| GET | /api/pantry/expiring/ |
Items expiring within 3 days |
| GET | /api/pantry/restock/ |
Staples needing restocking |
| CRUD | /api/ingredients/ |
Ingredient master list |
| CRUD | /api/meta-recipes/ |
Meta-recipes with nested slots |
| CRUD | /api/recipes/ |
Fixed recipes |
| CRUD | /api/shopping-list/ |
Shopping list |
| CRUD | /api/cook-log/ |
Cooking history |
Auth
- Token:
Authorization: Token <token>header - Session: Log in via admin or
/api-auth/login/ - API users:
tom(admin),caine(API-only)
File Structure
/var/lib/food/
├── food_project/ # Django project config
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── kitchen/ # Main app
│ ├── models.py # All 11 models
│ ├── admin.py # Admin config with inlines
│ ├── serializers.py # DRF serializers
│ ├── views.py # ViewSets + what-can-i-cook + log-cook
│ ├── urls.py # API router
│ ├── management/
│ │ └── commands/
│ │ └── seed.py # Initial data seeder
│ └── migrations/
├── venv/ # Python virtual environment (gitignored)
├── db.sqlite3 # SQLite database (gitignored)
├── manage.py
├── requirements.txt
└── README.md
Pantry Business Rules
- Freezer items → no expiry date (frozen = indefinite)
- Fridge items → expiry date set from ingredient's
shelf_life_daysif available - Staples (onions, frozen chips, salt, etc.) → auto-flag for restocking when quantity = 0
- Onions are a permanent staple — always restock
- Chicken skin → remove before cooking (noted in slot option notes)
- Tom prefers pork to chicken — pork mince over loins
- Cooked chicken — 3 day max fridge life
- Defrosted sausages — 2-3 days max
Implementation Phases
- Phase 1: Django project + models + admin + seed data
- Phase 2: DRF API + token auth + "what can I cook?" + log-cook with pantry deduction
- Phase 3: Recipe import (URL scraping + cookbook extraction)
- Phase 4: HTMX frontend (pantry view, recipe browser, shopping list)
- Phase 5: Systemd service + nginx reverse proxy + production hardening
Caine Integration
OpenClaw skill file at ~/.openclaw/workspace/skills/food/SKILL.md teaches Caine the API each session. Natural language interface via Matrix:
- "What can I cook?" → calls
/api/what-can-i-cook/ - "Add 6 eggs to the fridge" → POST to
/api/pantry/ - "Made stir fry with pork and noodles" → POST to
/api/log-cook/with deduction - [Photo of shopping] → identify items, bulk add to pantry
Design Doc
Full design document: Obsidian Vault/Tom Net/Meal Planning System.md
Description
Django + DRF meal-planning system: pantry tracking, meta-recipes and slot-based meal generation.
Readme
277 KiB
Languages
Python
76%
HTML
24%