12 KiB
MealPlanner - Agent Handoff Document
Project: MealPlanner - Self-hosted meal planning system Last Updated: 2026-05-04 Last Agent: OpenCode (MealPlanner session)
Project Overview
A self-hosted meal planning system for a family of 4 (2 adults, 2 children) that integrates with Lucky California grocery store. Key problem: replacing meal kit services (Blue Apron → EveryPlate → HungryRoot → Sunbasket) that suffer from 3x ingredient markup, repetitive meals, and no pantry integration.
Core Workflow
- System generates 7-day meal plan based on Lucky California sales, family dietary constraints, budget, and home pantry items
- Email sent to adults with meal proposals and approval links
- Adults click email link → confirmation page → POST vote (Approve/Deny)
- Per-voter tokens (single-use, 72h TTL)
- Majority approve → meal confirmed; any deny → swap
- Shopping list generated after approval
Family Constraints
- 3 of 4 family members do NOT like mushrooms
- No allergies
- Budget-conscious but wants tasty food
Architecture
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 via nginx)
- 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, Playwright, BeautifulSoup
- Frontend: React 18, TypeScript, Tailwind CSS, React Query, Vite
- Database: PostgreSQL 15 with ENUMs and CITEXT
- Email: SendGrid
- Hosting: Docker Compose, nginx
Current Git State
git log --oneline -10
9458edf docs: update ORIENTATION.md with Phase 3/7 progress and current git state
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
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 (meal proposal emails) | 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 |
Key Files and Locations
Backend Structure
backend/
├── alembic/
│ ├── env.py # Alembic configuration
│ └── versions/
│ ├── 0001_initial_migration.py # Full schema (enums, tables, indexes, constraints)
│ └── 0002_seed_data.py # Basic ingredients (70+), family profile, family members
├── app/
│ ├── api/
│ │ ├── admin.py # /api/admin/* (scrape trigger, logs, stats)
│ │ ├── meals.py # /api/meals/* (meal plans, voting, approval tokens)
│ │ ├── pantry.py # /api/pantry/* (CRUD for home pantry)
│ │ ├── profile.py # /api/profile/* (family profile, members)
│ │ ├── recipes.py # /api/recipes/* (CRUD, ingredients, filtering)
│ │ └── shopping_list.py # /api/shopping-list/* (aggregation, print HTML)
│ ├── config.py # Pydantic Settings (reads from .env)
│ ├── database.py # SQLAlchemy engine, SessionLocal, get_db
│ ├── models/
│ │ └── __init__.py # All SQLAlchemy models (FamilyProfile, Recipe, MealPlan, etc.)
│ ├── schemas/
│ │ └── __init__.py # All Pydantic schemas for API request/response
│ ├── scraper/
│ │ ├── base.py # BaseScraper with rate limiting, retries, session management
│ │ ├── lucky_ca_scraper.py # LuckyCaliforniaScraper with BeautifulSoup + Playwright
│ │ └── __init__.py
│ └── services/
│ └── scraper_service.py # ScraperService to save scraped items to grocery_item table
├── alembic.ini
├── Dockerfile
└── requirements.txt
Frontend Structure
frontend/
├── src/
│ ├── api/
│ │ └── index.ts # mealPlannerApi wrapper for all endpoints
│ ├── pages/
│ │ ├── Dashboard.tsx # Weekly meal plan grid view
│ │ ├── MealDetail.tsx # Recipe display with ingredients/instructions
│ │ ├── Pantry.tsx # Add/remove pantry items
│ │ └── ShoppingList.tsx # Grouped by aisle with sale highlighting
│ ├── types/
│ │ └── index.ts # TypeScript interfaces for all models
│ ├── App.tsx # React Router with /, /meals/:id, /pantry, /shopping-list
│ └── vite-env.d.ts # Vite env types
├── Dockerfile
├── package.json
└── vite.config.ts
Documentation
docs/
├── ARCHITECTURE.md # System architecture diagram
├── database-schema.md # Complete PostgreSQL schema reference
├── implementation-plan.md # Detailed phase-by-phase plan
├── ORIENTATION.md # First-stop guide for new agents (READ THIS)
├── RUNNING.md # Deployment guide
└── SPEC.md # Project specification
Database Schema Highlights
Core Tables
family_profile- Household with CHECK(household_size > 0)family_member- Individual members with email, role, mushroom preferencerecipe- 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 enummeal_plan_vote- Per-member votes (one vote per member per meal)approval_token- Single-use tokens with TTL and status trackinghome_pantry- Family's on-hand ingredientsfeedback- Ratings, denial reasons, never-suggest flagsgrocery_item- Scraped Lucky CA items with FK to ingredientscrape_log/email_log- Operation history
Key Enums
meal_plan_status_enum: draft, pending_approval, approved, lockedmeal_plan_item_status_enum: pending, approved, denied, swappedapproval_token_status_enum: active, used, expireddenial_reason_enum: too_expensive, boring, disliked_ingredient, cultural, other
Approval Flow (REDESIGNED)
- Email contains unique token per family member (not shared)
- Token expires after 72 hours
- Token can only be used once (marked USED after voting)
- Email link → GET confirmation page (NOT auto-approve)
- Vote submitted via POST
Configuration
Environment Variables (.env)
DATABASE_URL=postgresql://mealplanner:password@db:5432/mealplanner
SENDGRID_API_KEY=SG.xxx
FAMILY_EMAIL_1=you@example.com
FAMILY_EMAIL_2=spouse@example.com
LUCKY_CA_URL=https://luckysupermarkets.com
AI_IMAGE_ENABLED=false
LOG_LEVEL=INFO
SECRET_KEY=change-me-to-a-random-secret-key
Key URLs
- Lucky California: https://luckysupermarkets.com (verified scrapeable)
- robots.txt: Allows all
Verification Commands
# Docker build verification
docker compose config
docker compose build backend
docker compose build frontend
# Backend import test
docker compose run --rm backend python -c "from app.main import app; print(app.title)"
# Alembic migration test (requires running db)
docker compose run --rm backend alembic upgrade head
# Frontend build test
docker compose run --rm frontend npm run build
Known Issues / Open Questions
-
Lucky California scraping: Site uses dynamic content (JS rendering). The scraper uses Playwright for browser automation, but actual scraping hasn't been tested with a live database yet. May need adjustment based on actual page structure.
-
AI image generation: Config flag
AI_IMAGE_ENABLED=false. Not implemented - just a placeholder for future. -
WhatsApp integration: Planned for future via Twilio. Out of scope for MVP.
-
Planned scheduler: APScheduler with
--workers 1to avoid duplicate fires. Not yet implemented. -
Meal planner algorithm: Not implemented. Need to create algorithm that:
- Filters out never_suggest ingredients/recipes
- Prioritizes home_pantry items
- Incorporates sale items from grocery_item
- Ensures variety (max 2 same protein/sauce per week)
- Respects mushroom preference (3 of 4 don't like)
-
SendGrid email integration: Not implemented. Need:
- Email templates for meal proposals
- Approval email with per-voter tokens
- Reminder emails before deadline
- Confirmation emails after approval
Next Steps (Priority Order)
1. Implement SendGrid Email Integration (Phase 6)
- Create
backend/app/services/email_service.py - Implement meal proposal email template
- Connect to meal plan creation flow
- Use approval tokens for email links
2. Implement Meal Planner Generation Algorithm (Phase 9)
- Create
backend/app/services/meal_planner_service.py - Load constraints (family profile, dietary, budget)
- Filter never_suggest ingredients/recipes
- Prioritize home_pantry items
- Incorporate sale items
- Ensure variety requirements
3. Implement Recipe Engine (Phase 4)
- Recipe CRUD already exists but needs testing
- Tag-based filtering implemented in API
- Search functionality needed
4. Implement Web UI - Feedback (Phase 8)
- Feedback Portal page
- Rating submission (1-5 stars)
- "Never suggest this" flag
- Learning integration
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
API Design
- RESTful endpoints with proper HTTP methods
- Pydantic schemas for request/response validation
- Email approval links use GET → confirmation page → POST
- Per-voter tokens, not shared household tokens
Database
- UUIDs for primary keys
- Timestamps with timezone (TIMESTAMPTZ)
- ENUMs for status fields (not loose VARCHAR)
- CITEXT or lowercase-on-write for name matching
Contacts
- 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)
File: ORIENTATION.md
IMPORTANT: Read docs/ORIENTATION.md first before doing anything else. It contains the authoritative project state, phase table, and next steps.