Public Access
274 lines
9.7 KiB
Markdown
274 lines
9.7 KiB
Markdown
# Meal Planner - Orientation Guide
|
|
|
|
This document is the **first stop** for any agent resuming work on this project after context compaction. Read it before doing anything else.
|
|
|
|
---
|
|
|
|
## Project Overview
|
|
|
|
**MealPlanner** is a self-hosted meal planning system for a family of 4 (2 adults, 2 children) that integrates with Lucky California grocery store to source ingredients from weekly sales, sends meal proposals via email, generates shopping lists, and learns from feedback.
|
|
|
|
### Key Problem Being Solved
|
|
|
|
The family has been using meal kit services (Blue Apron → EveryPlate → HungryRoot → Sunbasket) which suffer from:
|
|
- 3x ingredient markup in cost
|
|
- Repetitive meals and sauces
|
|
- Forcing app login to manage meal selection
|
|
- No home pantry integration
|
|
|
|
### Current Status
|
|
|
|
**Phase**: Phase 2 (Database & Models) IN PROGRESS. Migrations created, schemas implemented, API endpoints implemented.
|
|
|
|
**Adversarial Review Completed**: 2026-05-04
|
|
- All consensus blockers (§1.1 - §1.8) addressed
|
|
- All high-risk gaps (§2.1 - §2.8) addressed
|
|
- Key fixes applied:
|
|
- Recipe-ingredient: JSONB only (removed join table)
|
|
- Family member: Added `family_member` table for per-voter tracking
|
|
- Approval flow: Redesigned with confirmation page + POST + per-voter tokens + TTL
|
|
- Auth: VPN-only for admin endpoints, session-based for family web UI
|
|
- Schema: ENUMs, CHECKs, CITEXT for ingredients, ISO day_of_week (1=Mon)
|
|
- Lucky URL: Fixed to `https://luckysupermarkets.com`
|
|
- Docker: Hardened (no direct port exposure to backend/frontend)
|
|
- Alembic: Configured with migration policy
|
|
|
|
**Phase 2 Progress** (2026-05-04):
|
|
- [x] Initial Alembic migration (0001_initial_migration.py)
|
|
- [x] Seed data migration (0002_seed_data.py)
|
|
- [x] Pydantic schemas for all models
|
|
- [x] /api/profile endpoints (CRUD, family members)
|
|
- [x] /api/recipes endpoints (CRUD, ingredients)
|
|
- [x] /api/meals endpoints (meal plans, voting, approval tokens)
|
|
- [x] /api/pantry endpoints (CRUD)
|
|
- [x] /api/shopping-list endpoints (aggregation, print)
|
|
- [x] /api/admin endpoints (scrape trigger, logs, stats)
|
|
|
|
**Phase 3 Progress** (2026-05-04):
|
|
- [x] Base scraper with rate limiting, retries, session management
|
|
- [x] LuckyCaliforniaScraper with BeautifulSoup + Playwright
|
|
- [x] ScraperService to save scraped items to grocery_item table
|
|
- [x] /api/admin/scrape endpoint connected to ScraperService
|
|
|
|
**Phase 7 Progress** (2026-05-04):
|
|
- [x] Types: FamilyProfile, Recipe, MealPlan, Ingredient, ShoppingList, etc.
|
|
- [x] API client: mealPlannerApi wrapper for all endpoints
|
|
- [x] Dashboard: weekly meal plan grid view with day/meal columns
|
|
- [x] Pantry page: add/remove items with ingredient selection
|
|
- [x] MealDetail page: recipe display with ingredients and instructions
|
|
- [x] ShoppingList page: grouped by aisle with sale highlighting
|
|
|
|
---
|
|
|
|
## Architecture Summary
|
|
|
|
```
|
|
User (email) ──► SendGrid ───────────────────────────────┐
|
|
User (web) ───► React UI ──► nginx ──► FastAPI ──────────┼──► PostgreSQL
|
|
│ │
|
|
└──► Lucky CA scraper ──┘
|
|
(luckysupermarkets.com)
|
|
```
|
|
|
|
### Services (Docker Compose)
|
|
- **backend**: FastAPI Python app (port 8000, internal only)
|
|
- **frontend**: React + Tailwind (port 3000, internal only via nginx)
|
|
- **db**: PostgreSQL 15 (internal only)
|
|
- **nginx**: Reverse proxy with SSL (ports 80/443)
|
|
|
|
### Tech Stack
|
|
- Backend: Python 3.11, FastAPI, SQLAlchemy 2.0, Alembic
|
|
- Frontend: React 18, TypeScript, Tailwind CSS, React Query, Vite
|
|
- Database: PostgreSQL 15 with ENUMs and CITEXT
|
|
- Scraping: Playwright, BeautifulSoup
|
|
- Email: SendGrid
|
|
- Hosting: Docker Compose, nginx
|
|
|
|
---
|
|
|
|
## Family Profile
|
|
|
|
### Household
|
|
- 2 adults, 2 children
|
|
- 3 of 4 members do NOT like mushrooms
|
|
- No allergies
|
|
- Goals: Calorie, budget, and health conscious; tasty but not expensive
|
|
|
|
### Family Members
|
|
| Member | Role | Mushroom Preference |
|
|
|--------|------|-------------------|
|
|
| Adult 1 | Adult | Does NOT like mushrooms |
|
|
| Adult 2 | Adult | Likes mushrooms |
|
|
| Child 1 | Child | Does NOT like mushrooms |
|
|
| Child 2 | Child | OK with mushrooms |
|
|
|
|
### Approval Workflow (REDESIGNED)
|
|
1. System generates 7-day meal plan (Sunday)
|
|
2. Email sent to all adults with meals, images, approval page links
|
|
3. Email link → confirmation page (GET, not auto-approve)
|
|
4. Adult clicks Approve/Deny → POST with reason
|
|
5. Per-member token (single-use, 72h TTL)
|
|
6. Majority approve → meal confirmed; any deny → swap
|
|
7. After approval → shopping list generated
|
|
|
|
---
|
|
|
|
## Database Schema Highlights
|
|
|
|
### Core Tables
|
|
- `family_profile` - Household configuration with household_size CHECK
|
|
- `family_member` - Individual family members with email, role, mushroom preference
|
|
- `recipe` - Recipes with JSONB ingredients (not join table)
|
|
- `ingredient` - Master list with name_lower (CITEXT for case-insensitive matching)
|
|
- `meal_plan` - Weekly plan with ISO day_of_week (1=Mon, 7=Sun)
|
|
- `meal_plan_item` - Individual meal with approval status
|
|
- `meal_plan_vote` - Per-member votes (one vote per member per meal)
|
|
- `approval_token` - Single-use tokens with TTL and status tracking
|
|
- `home_pantry` - Family's on-hand ingredients
|
|
- `feedback` - Ratings, denial reasons, never-suggest flags
|
|
- `grocery_item` - Scraped Lucky California items with FK to ingredient
|
|
- `scrape_log` / `email_log` - Operation history
|
|
|
|
### Key Relationships
|
|
- `family_profile` 1:N `family_member`
|
|
- `family_profile` 1:N `meal_plan`
|
|
- `family_member` 1:N `meal_plan_vote` (per-voter tracking)
|
|
- `recipe` 1:N `meal_plan_item`
|
|
- `meal_plan` 1:N `meal_plan_item`
|
|
- `meal_plan_item` 1:N `meal_plan_vote`
|
|
- `meal_plan_item` 1:N `approval_token`
|
|
- `grocery_item` → `ingredient` (FK)
|
|
|
|
---
|
|
|
|
## Implementation Phases
|
|
|
|
| Phase | Description | Status |
|
|
|-------|-------------|--------|
|
|
| 1 | Infrastructure (Docker, PostgreSQL, FastAPI, React, nginx) | **Complete** |
|
|
| 2 | Database & Models (Alembic migrations, Pydantic schemas, API endpoints) | **Complete** |
|
|
| 3 | Lucky California Scraper (BeautifulSoup + Playwright, ScraperService) | **Complete** |
|
|
| 4 | Recipe Engine (CRUD, tagging, search) | Not Started |
|
|
| 5 | Meal Planner Engine (generation algorithm, substitutions) | Not Started |
|
|
| 6 | SendGrid Email Integration | Not Started |
|
|
| 7 | Web UI - Core (Dashboard, Meal Detail, Pantry, Shopping List) | **Complete** |
|
|
| 8 | Web UI - Feedback (Feedback Portal, Learning) | Not Started |
|
|
| 9 | Meal Planner Generation Algorithm | Not Started |
|
|
| 10 | Image Strategy (scraped + AI fallback) | Not Started |
|
|
| 11 | Polish & Future (variety analysis, budget tracking) | Not Started |
|
|
|
|
---
|
|
|
|
## Environment Variables
|
|
|
|
```bash
|
|
# Database
|
|
DATABASE_URL=postgresql://mealplanner:password@db:5432/mealplanner
|
|
POSTGRES_PASSWORD=secure_password
|
|
|
|
# SendGrid
|
|
SENDGRID_API_KEY=SG.xxx
|
|
|
|
# Family
|
|
FAMILY_EMAIL_1=user@example.com
|
|
FAMILY_EMAIL_2=spouse@example.com
|
|
|
|
# Scraping
|
|
LUCKY_CA_URL=https://luckysupermarkets.com
|
|
|
|
# AI Images (optional)
|
|
AI_IMAGE_ENABLED=false
|
|
|
|
# Auth
|
|
SECRET_KEY=change-me-in-production
|
|
```
|
|
|
|
---
|
|
|
|
## Next Steps
|
|
|
|
### IMMEDIATE: Verify skeleton with verification matrix
|
|
|
|
```bash
|
|
docker compose config
|
|
docker compose build backend
|
|
docker compose build frontend
|
|
docker compose up -d db
|
|
docker compose run --rm backend python -c "from app.main import app; print(app.title)"
|
|
docker compose run --rm backend alembic upgrade head
|
|
docker compose run --rm frontend npm run build
|
|
```
|
|
|
|
If any of these fail, fix before proceeding.
|
|
|
|
### After Verification
|
|
|
|
1. Phase 4: Recipe Engine (CRUD, tagging, search)
|
|
2. Phase 5: Meal Planner Engine (generation algorithm, substitutions)
|
|
3. Phase 6: SendGrid Email Integration (meal proposal emails with approval links)
|
|
|
|
---
|
|
|
|
## Current Git State
|
|
|
|
```bash
|
|
git log --oneline -10
|
|
27bca0e docs: update ORIENTATION.md phase table
|
|
08e196b feat: implement frontend Web UI pages
|
|
933a0cc feat: implement Lucky California scraper with Playwright + BeautifulSoup
|
|
c735d21 feat: implement Phase 2 - Alembic migrations, Pydantic schemas, and real API endpoints
|
|
e8706d3 docs: final ORIENTATION update
|
|
624b516 docs: update ORIENTATION.md for Phase 1 complete
|
|
1328ec3 feat: add Phase 1 infrastructure skeleton
|
|
0c5b0aa docs: add complete project documentation
|
|
```
|
|
|
|
---
|
|
|
|
## Important Conventions
|
|
|
|
### Code Style
|
|
- Python: Black formatter, isort for imports
|
|
- TypeScript: Prettier, ESLint
|
|
- No comments unless explaining non-obvious logic
|
|
|
|
### Git Commits
|
|
- Conventional commits: `feat:`, `fix:`, `docs:`, `refactor:`, `test:`
|
|
- One logical change per commit where possible
|
|
|
|
### API Design
|
|
- RESTful endpoints with proper HTTP methods and status codes
|
|
- Pydantic schemas for request/response validation
|
|
- Email approval links use GET → confirmation page → POST
|
|
- Per-voter tokens, not shared household tokens
|
|
|
|
### Database
|
|
- Always use UUIDs for primary keys
|
|
- Timestamps with timezone (TIMESTAMPTZ)
|
|
- Use ENUMs for status fields (not loose VARCHAR)
|
|
- Use CITEXT or lowercase-on-write for name matching
|
|
|
|
### Testing
|
|
- Write unit tests for services (pytest)
|
|
- Integration tests for API endpoints
|
|
- Frontend: component tests with React Testing Library
|
|
|
|
---
|
|
|
|
## Known Issues / Open Questions
|
|
|
|
1. **Lucky California scraping**: Feasibility not yet spiked. URL is `luckysupermarkets.com`.
|
|
2. **AI image generation**: Config flag to enable/disable. Disabled by default.
|
|
3. **WhatsApp integration**: Planned for future via Twilio. Out of scope for MVP.
|
|
4. **Planned scheduler**: APScheduler with `--workers 1` to avoid duplicate fires.
|
|
|
|
---
|
|
|
|
## Contact / Context
|
|
|
|
- **Primary user**: Peter (tech-savvy, hosts the system)
|
|
- **Wife**: Non-technical, will use web UI and email
|
|
- **Children**: 2, eating habits vary (one OK with mushrooms)
|
|
|
|
Last updated: 2026-05-04 (all adversarial review fixes committed)
|