# Meal Planner - Implementation Plan ## Overview Self-hosted meal planning system that integrates with Lucky California grocery store, sends weekly meal proposals via email to family members, generates shopping lists, and learns from feedback. --- ## Phase 1: Infrastructure & Foundation ### 1.1 Project Setup - [ ] Initialize Git repository with `.gitignore` (Python, Node, Docker) - [ ] Create `docker-compose.yml` with: backend (FastAPI), frontend (React), PostgreSQL, nginx - [ ] Create `backend/` directory structure - [ ] Create `frontend/` directory structure - [ ] Set up Alembic for database migrations - [ ] Create initial database migration with full schema **Verify**: `docker-compose up -d` starts all services; `docker-compose ps` shows all running ### 1.2 Backend Skeleton - [ ] Create FastAPI app with directory structure: ``` backend/app/ __init__.py main.py config.py database.py models/ schemas/ api/ services/ scraper/ ``` - [ ] Create SQLAlchemy models matching database schema - [ ] Create Pydantic schemas for API requests/responses - [ ] Set up database connection with session management - [ ] Create health check endpoint `GET /health` **Verify**: `curl localhost:8000/health` returns `{"status": "ok"}` ### 1.3 Frontend Skeleton - [ ] Create React app with TypeScript (`npx create-react-app` or Vite) - [ ] Install dependencies: Tailwind CSS, React Router, React Query, Axios - [ ] Create basic directory structure: ``` frontend/src/ components/ pages/ hooks/ api/ types/ ``` - [ ] Set up React Query provider and routing - [ ] Create basic layout with navigation **Verify**: Frontend builds without errors; `docker-compose up` serves frontend on port 3000 ### 1.4 Reverse Proxy (Nginx) - [ ] Create `nginx.conf` with routing rules: - `/api/*` → backend:8000 - `/*` → frontend:80 - SSL configuration for remote access - [ ] Create Caddyfile alternative for easier Let's Encrypt setup **Verify**: Nginx starts without config errors; routing works correctly --- ## Phase 2: Database & Models ### 2.1 SQLAlchemy Models - [ ] Create `backend/app/models/__init__.py` with all model imports - [ ] Implement `FamilyProfile` model - [ ] Implement `Ingredient` model - [ ] Implement `Recipe` model with JSONB ingredients column - [ ] Implement `MealPlan` model - [ ] Implement `MealPlanItem` model - [ ] Implement `HomePantry` model - [ ] Implement `Feedback` model - [ ] Implement `NeverSuggest` model - [ ] Implement `GroceryItem` model - [ ] Implement `ScrapeLog` model - [ ] Implement `EmailLog` model ### 2.2 Pydantic Schemas - [ ] Create request/response schemas for each model - [ ] Create nested schemas for related objects (e.g., `MealPlanWithItems`) - [ ] Create pagination schemas for list endpoints ### 2.3 Database Migrations - [ ] Create Alembic configuration - [ ] Create initial migration from SQLAlchemy models - [ ] Add seed data migration (basic ingredients, sample recipes) **Verify**: `alembic upgrade head` runs without errors; tables created in PostgreSQL --- ## Phase 3: API Endpoints ### 3.1 Core CRUD Endpoints - [ ] `GET/PUT /api/profile` - Family profile management - [ ] `GET/POST /api/ingredients` - Ingredient list - [ ] `GET/POST /api/recipes` - Recipe management - [ ] `GET /api/recipes/{id}` - Single recipe with full details - [ ] `GET /api/pantry` - Home pantry items - [ ] `POST/DELETE /api/pantry/{id}` - Add/remove pantry items ### 3.2 Meal Plan Endpoints - [ ] `GET /api/meals/planned` - Current week's meal plan - [ ] `POST /api/meals/{id}/approve` - Approve a meal - [ ] `GET /api/deny/{token}` - Denial form (for email links) - [ ] `POST /api/deny/{token}` - Process denial with reason ### 3.3 Shopping List Endpoints - [ ] `GET /api/shopping-list` - Current week's shopping list - [ ] `GET /api/shopping-list/print` - Printable format (PDF-ready HTML) ### 3.4 Feedback Endpoints - [ ] `POST /api/feedback` - Submit feedback for a meal - [ ] `GET /api/feedback` - View feedback history ### 3.5 Admin Endpoints - [ ] `POST /api/admin/scrape` - Trigger Lucky California scrape - [ ] `GET /api/admin/logs` - View scrape and email logs **Verify**: All endpoints return expected status codes and data shapes --- ## Phase 4: Lucky California Scraper ### 4.1 Scraper Infrastructure - [ ] Create `backend/app/scraper/base.py` with base scraper class - [ ] Create `backend/app/scraper/playwright_setup.py` for browser automation - [ ] Implement retry logic with exponential backoff - [ ] Create logging for scrape operations - [ ] Implement rate limiting (respect Lucky California's limits) ### 4.2 Weekly Ad Scraper - [ ] Scrape Lucky California homepage for weekly ad link - [ ] Parse weekly ad page for sale items - [ ] Extract: product name, sale price, regular price, image URL, product URL - [ ] Map products to existing ingredients or create new ones - [ ] Store in `grocery_item` table ### 4.3 Product Catalog Scraper (Future) - [ ] Scrape product search results for specific ingredients - [ ] Parse product detail pages for pricing and availability - [ ] Implement pagination handling ### 4.4 Scrape Scheduling - [ ] Create scheduler service (APScheduler or similar) - [ ] Schedule weekly scrape (Sunday night before meal planning) - [ ] Schedule ad-hoc scrape via admin endpoint ### 4.5 Error Handling - [ ] Log all scrape failures with full context - [ ] Create alert if scrape fails 3 consecutive times - [ ] Store failed URLs for manual review **Verify**: Scraper successfully pulls 10+ sale items from Lucky California website --- ## Phase 5: Recipe Engine ### 5.1 Recipe Database - [ ] Create recipe CRUD service - [ ] Implement tag-based filtering (cuisine, dietary, protein) - [ ] Create search functionality (name, ingredients) - [ ] Store recipe images (URL reference) ### 5.2 Recipe Importing - [ ] Create basic recipe input form in web UI - [ ] Support manual recipe entry (name, ingredients, instructions) - [ ] Support pasting recipe URL for future scraping ### 5.3 Ingredient Mapping - [x] Create ingredient matching service - [x] Map recipe ingredients to `ingredient` table entries - [x] Handle unit conversions (cups → oz, lb → g, etc.) - Implemented in `app/utils/units.py`: normalization, within-family (weight/volume/count), cross-family via density tables for ~30 canonical ingredients, fallback to dimensionless - Integrated into `planner/cost.py` for accurate cost calculation - Orchestrator email and shopping list use converted quantities **Verify**: Can add a recipe manually and retrieve it via API --- ## Phase 6: Meal Planner Engine ### 6.1 Plan Generation Algorithm - [ ] Create `MealPlannerService` with generation logic - [ ] Load constraints: family profile, dietary restrictions, budget - [ ] Filter out `never_suggest` ingredients and recipes - [ ] Prioritize ingredients from `home_pantry` - [ ] Select meals based on variety (max 2 same protein/sauce per week) - [ ] Incorporate sale items from `grocery_item` - [ ] Generate 7-day plan (dinner only for MVP) ### 6.2 Substitution Logic - [ ] When meal denied, find similar substitute - [ ] Criteria: same protein, similar cuisine, within budget - [ ] Avoid previously denied meals ### 6.3 Cost Calculation - [ ] Calculate per-serving cost using grocery prices - [ ] Sum total weekly meal cost - [ ] Flag meals exceeding budget ### 6.4 Plan Persistence - [ ] Save generated plan as `draft` status - [ ] Generate unique approval tokens per meal - [ ] Set approval deadline (48h from email send) **Verify**: Plan generation produces valid 7-day plan respecting all constraints --- ## Phase 7: SendGrid Email Integration ### 7.1 SendGrid Setup - [ ] Create SendGrid API key (if not already done) - [ ] Create `backend/app/services/email_service.py` - [ ] Implement email sending via SendGrid API - [ ] Create HTML email templates ### 7.2 Meal Proposal Email - [ ] Create email template with: - Week overview - All 7 meals listed with images - Each meal: name, day, image, ingredients list, cost estimate - Approve/Deny links with tokens - Deadline reminder - [ ] Send to both adult email addresses ### 7.3 Reminder Email - [ ] Send reminder 2 days before deadline - [ ] Include current approval status - [ ] Include link to web UI for approval ### 7.4 Confirmation Email - [ ] Send after all meals approved - [ ] Include link to shopping list - [ ] Include link to recipes **Verify**: Emails sent and received; links work correctly --- ## Phase 8: Web UI - Core Features ### 8.1 Dashboard - [ ] Create `Dashboard` page - [ ] Show current week's meal plan - [ ] Show approval status for each meal - [ ] Quick action buttons (approve/deny) - [ ] Show shopping list summary ### 8.2 Meal Detail Page - [ ] Create `MealDetail` page - [ ] Show full recipe with ingredients and instructions - [ ] Display meal image - [ ] Show nutritional hints (calories, protein) - [ ] Print recipe button ### 8.3 Approval Center - [ ] Create `ApprovalCenter` page - [ ] List pending approvals - [ ] Approve/Deny buttons with reason selection - [ ] Show denial history and reasons ### 8.4 Pantry Manager - [ ] Create `PantryManager` page - [ ] Add items with quantity and unit - [ ] Remove items - [ ] Set expiration dates for perishables **Verify**: All pages render correctly; actions update database --- ## Phase 9: Web UI - Feedback & Learning ### 9.1 Feedback Portal - [ ] Create `FeedbackPortal` page - [ ] Rate completed meals (1-5 stars) - [ ] Select "Never suggest this" option - [ ] Free-text feedback field ### 9.2 Learning Integration - [x] Update meal planner weights based on feedback (family-level planner_config JSONB) - [ ] Block denied recipes from future proposals - [ ] Block denied ingredients from future proposals ### 9.3 Recipe Discovery - [ ] Create `RecipeBrowser` page - [ ] Filter by cuisine, protein, dietary tags - [ ] Search by name or ingredient - [ ] "Surprise me" random recipe button **Verify**: Feedback submitted and reflected in future meal plans --- ## Phase 10: Shopping List & Print ### 10.1 Shopping List Generation - [ ] Aggregate all ingredients from week's meals - [ ] Subtract home pantry items - [ ] Group by Lucky California aisle - [ ] Highlight sale items - [ ] Show estimated total cost ### 10.2 Printable Shopping List - [ ] Create print-optimized CSS - [ ] Generate clean HTML for printing - [ ] Include checkbox squares for manual marking - [ ] Show aisle location for each item ### 10.3 Web UI Shopping List View - [ ] Create `ShoppingList` page - [ ] Show items grouped by aisle - [ ] Show sale price vs regular price - [ ] Show total estimated cost - [ ] Print/export button **Verify**: Shopping list accuracy (matches meal plan ingredients minus pantry) --- ## Phase 11: Image Strategy Implementation ### 11.1 Scraped Images - [ ] Store image URLs in `recipe.image_url` - [ ] Implement graceful fallback if image unavailable - [ ] Lazy-load images in web UI ### 11.2 AI Image Generation (Fallback) - [ ] Create AI image service abstraction - [ ] Implement DALL-E or similar API integration - [ ] Generate images only when scraped image unavailable - [ ] Cache generated images in database - [ ] Add config flag to enable/disable AI generation ### 11.3 Email Image Handling - [ ] Embed images via CDN URL or inline base64 - [ ] Provide alt text for blocked images - [ ] Include link to web UI for full image gallery **Verify**: Meals display appropriate images in both web UI and email --- ## Phase 12: Polish & Future Features ### 12.1 Variety Analysis - [ ] Track sauce/ingredient usage per week - [ ] Warn if meals are too similar - [ ] Suggest diverse alternatives ### 12.2 Budget Tracking - [ ] Track actual spending vs estimated - [ ] Show cost per serving over time - [ ] Alert if significantly over budget ### 12.3 WhatsApp Integration (Future) - [ ] Set up Twilio WhatsApp Business API - [ ] Create WhatsApp message templates - [ ] Implement approval/denial via WhatsApp ### 12.4 Recipe Scraping (Future) - [ ] Implement recipe site scraper - [ ] Parse recipe structured data (JSON-LD) - [ ] Auto-import recipes from web --- ## Verification Commands ### Backend Tests ```bash # Health check curl localhost:8000/health # API tests curl localhost:8000/api/profile curl localhost:8000/api/recipes curl localhost:8000/api/shopping-list ``` ### Frontend Tests ```bash # Build test cd frontend && npm run build # Dev server cd frontend && npm run dev ``` ### Full Stack Tests ```bash # Start all services docker-compose up -d # Check all services running docker-compose ps # View logs docker-compose logs -f # Stop all services docker-compose down ``` --- ## File Structure ``` mealplanner/ ├── docker-compose.yml ├── docker-compose.override.yml ├── .env.example ├── .gitignore ├── README.md ├── RUNNING.md ├── docs/ │ ├── SPEC.md │ ├── ARCHITECTURE.md │ ├── database-schema.md │ └── implemenation-plan.md ├── nginx/ │ ├── nginx.conf │ └── ssl/ ├── backend/ │ ├── Dockerfile │ ├── requirements.txt │ ├── alembic.ini │ ├── alembic/ │ │ ├── env.py │ │ └── versions/ │ └── app/ │ ├── __init__.py │ ├── main.py │ ├── config.py │ ├── database.py │ ├── models/ │ │ ├── __init__.py │ │ └── [model files] │ ├── schemas/ │ │ ├── __init__.py │ │ └── [schema files] │ ├── api/ │ │ ├── __init__.py │ │ └── [endpoint files] │ ├── services/ │ │ ├── __init__.py │ │ ├── email_service.py │ │ ├── meal_planner_service.py │ │ └── [other services] │ └── scraper/ │ ├── __init__.py │ ├── base.py │ ├── lucky_ca_scraper.py │ └── [other scrapers] └── frontend/ ├── Dockerfile ├── package.json ├── vite.config.ts ├── tailwind.config.js └── src/ ├── App.tsx ├── main.tsx ├── index.css ├── components/ ├── pages/ ├── hooks/ ├── api/ └── types/ ``` --- ## Dependencies ### Backend (Python 3.11+) - fastapi - uvicorn - sqlalchemy - alembic - psycopg2-binary - pydantic - sendgrid (sendgrid-python) - playwright - beautifulsoup4 - apscheduler - python-dotenv - pytest (dev) ### Frontend (Node 18+) - react - react-dom - react-router-dom - @tanstack/react-query - axios - tailwindcss - postcss - autoprefixer - typescript - vite - @types/react - @types/node --- ## Environment Variables ```bash # Backend DATABASE_URL=postgresql://mealplanner:password@db:5432/mealplanner SENDGRID_API_KEY=SG.xxx RECIPES_EMAIL=you@example.com FAMILY_EMAIL_1=wife@example.com FAMILY_EMAIL_2=you@example.com AI_IMAGE_ENABLED=false AI_IMAGE_PROVIDER=openai # Frontend VITE_API_URL=http://localhost:8000 # Docker POSTGRES_PASSWORD=secure_password_here ``` --- ## Notes - All code commits should follow conventional commit format - Test at each phase before proceeding to next - Document any deviations from this plan - Keep ORIENTATION.md updated as implementation progresses