Files
Meal-Planner/docs/ARCHITECTURE.md
T
admin 0c5b0aa5ed docs: add complete project documentation
- SPEC.md: project specification and goals
- ARCHITECTURE.md: system design and component descriptions
- database-schema.md: PostgreSQL schema with all tables
- implementation-plan.md: 12-phase implementation guide
- RUNNING.md: deployment and troubleshooting guide
- ORIENTATION.md: context compaction recovery guide
- README.md: project overview and quick start

Family profile: 2 adults, 2 children. Mushroom avoidance for 3/4.
Approval workflow: email proposals, one denial swaps meal.
Tech stack: FastAPI, PostgreSQL, React, Playwright, SendGrid.
2026-05-04 19:27:22 -07:00

405 lines
18 KiB
Markdown

# Meal Planner - Architecture
## 1. System Overview
```
┌─────────────────────────────────────────────────────────────────────────┐
│ USER FACING │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────────┐ │
│ │ Email │ │ Web UI │ │ Future: WhatsApp │ │
│ │ (SendGrid) │ │ (React) │ │ (Twilio) │ │
│ └──────┬───────┘ └──────┬───────┘ └───────────┬──────────────┘ │
└─────────┼────────────────────┼───────────────────────┼─────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────────────┐
│ BACKEND (FastAPI) │
│ │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────────────┐ │
│ │ Scraper │ │ Recipe │ │ Meal │ │ Notification │ │
│ │ Service │ │ Engine │ │ Planner │ │ Service │ │
│ └─────┬──────┘ └─────┬──────┘ └─────┬──────┘ └─────────┬──────────┘ │
│ │ │ │ │ │
│ ┌─────┴───────────────┴───────────────┴────────────────────┴────────┐ │
│ │ SERVICE LAYER │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌───────────┐ │ │
│ │ │ Grocery │ │ Recipe │ │ MealPlan │ │ Feedback │ │ │
│ │ │ Service │ │ Service │ │ Service │ │ Service │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ └───────────┘ │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ DATA LAYER (PostgreSQL) │ │
│ └────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ EXTERNAL SERVICES │
│ │
│ ┌───────────────┐ ┌───────────────┐ ┌────────────────────────────┐ │
│ │ Lucky │ │ Recipe Sites │ │ AI Image Service │ │
│ │ California │ │ (scraping) │ │ (fallback only) │ │
│ │ (scrape) │ │ │ │ │ │
│ └───────────────┘ └───────────────┘ └────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## 2. Component Descriptions
### 2.1 Scraper Service
**Responsibility**: Fetch and parse data from Lucky California and public recipe sites
**Modules**:
- `LuckyCaliforniaScraper`: Scrapes weekly ad, product catalog, sales
- `RecipeSiteScraper`: Scrapes recipe images and metadata from public sites
**Technology**: Playwright + BeautifulSoup
**Data Flow**:
```
Lucky California website → Scraper → Parse → Store in PostgreSQL
Recipe sites → Scraper → Parse → Store as recipe.image_source
```
**Error Handling**:
- Retry with exponential backoff (3 attempts)
- Log failures for admin review
- Graceful degradation to manual input
---
### 2.2 Recipe Engine
**Responsibility**: Store, tag, and retrieve recipes
**Capabilities**:
- CRUD operations for recipes
- Tag-based filtering (cuisine, protein, dietary, season)
- Ingredient matching against home pantry
- Image URL storage (scraped primary, AI fallback)
**Schema**:
- Recipe: id, name, description, ingredients[], instructions, image_url, cuisine_tags[], dietary_tags[], protein_type, prep_time, cook_time, servings, created_at
- Ingredient: id, name, aisle, typical_price_range, season_months[]
---
### 2.3 Meal Planner Service
**Responsibility**: Generate weekly meal plans based on constraints
**Algorithm Inputs**:
- Family profile (size, dietary constraints, preferences)
- Scraped grocery data (sales, in-season)
- Home pantry items
- Feedback history (avoid denied meals, prioritize liked ingredients)
- Variety constraints (max 2 meals with same sauce/protein)
**Algorithm Output**:
- 7-day meal plan with one meal per day
- Each meal includes: recipe, adjusted ingredients (substituting sale items), estimated cost per serving
**Learning Mechanism**:
- Weight denied meals at 0 (never re-suggest)
- Weight high-rated meals higher for future weeks
- Track ingredient frequency to enforce variety
---
### 2.4 Notification Service
**Responsibility**: Send emails and manage approval workflow
**Email Triggers**:
- Weekly meal proposal (to both adults)
- Reminder email (2 days before shipping deadline)
- Final meal plan confirmation
- Shopping list ready notification
**Approval Flow**:
```
Generate plan → Send proposal email
→ Wait for responses (48h window)
→ If deny → Swap meal with alternative
→ If approve/no response → Confirm meal
→ After all confirmations → Generate shopping list
```
**Email Template Data**:
- Meal name and day
- Meal image (URL)
- Ingredient list with pricing
- Approve/Deny links (tokenized URLs)
- Estimated total cost
---
### 2.5 Feedback Service
**Responsibility**: Collect and process family feedback
**Feedback Types**:
- **Rating**: 1-5 stars per meal
- **Denial Reason**: too_expensive, boring, disliked_ingredient, cultural, other
- **Never-Suggest Flag**: per ingredient or per recipe
- **Home Pantry Update**: items currently available
**Learning Integration**:
- Feedback stored with timestamps
- Aggregated weekly to adjust meal planner weights
- "Never-suggest" ingredients filtered from all future proposals
- Denial reasons used to improve substitutions
---
### 2.6 Web UI (React + Tailwind)
**Responsibility**: Interactive interface for family members
**Pages**:
1. **Dashboard**: Current week's meal plan, approval status, shopping list summary
2. **Meal Detail**: Full recipe view, ingredients, cooking instructions, print button
3. **Approval Center**: Pending approvals with Approve/Deny actions
4. **Pantry Manager**: Add/remove home pantry items
5. **Feedback Portal**: Rate completed meals, flag ingredients
6. **Admin/Settings**: Family profile, scraper status, email history
**Technology**:
- React 18+ with TypeScript
- Tailwind CSS for styling
- React Query for data fetching
- React Router for navigation
---
## 3. Data Architecture
### 3.1 PostgreSQL Schema Overview
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ family_ │ │ recipe │ │ meal_plan │
│ profile │ │ │ │ │
├─────────────────┤ ├─────────────────┤ ├─────────────────┤
│ id │ │ id │ │ id │
│ name │◄────│ family_profile │ │ week_start_date│
│ household_size │ │ name │◄─┐ │ status │
│ dietary_notes │ │ description │ │ │ created_at │
│ preferences │ │ image_url │ │ └────────────────┘
│ created_at │ │ prep_time │ │ │
└─────────────────┘ │ cook_time │ │ │
│ │ servings │ │ │
│ │ cuisine_tags[] │ │ │
│ │ dietary_tags[] │ │ │
│ │ protein_type │ │ │
│ │ created_at │ │ │
│ └─────────────────┘ │ │
│ │ │ │
│ ▼ │ │
│ ┌─────────────────┐ │ ┌─────────────────────┐
│ │ recipe │ │ │ meal_plan_item │
│ │ _ingredient │◄──┘ ├─────────────────────┤
│ ├─────────────────┤ │ id │
│ │ recipe_id │ │ meal_plan_id │
│ │ ingredient_id │ │ recipe_id ────┘
└──────────────►│ quantity │ │ day_of_week │
│ unit │ │ approval_status │
│ is_optional │ │ approval_token │
└─────────────────┘ │ denial_reason │
└─────────────────────┘
┌─────────────────┐ ┌─────────────────┐
│ grocery │ │ home_ │
│ item │ │ pantry │
├─────────────────┤ ├─────────────────┤
│ id │ │ id │
│ name │ │ family_profile │
│ aisle │ │ ingredient_id │
│ current_price │ │ quantity │
│ is_on_sale │ │ added_at │
│ sale_end_date │ │ expires_at │
│ season_months[] │ └─────────────────┘
│ scraped_at │
└─────────────────┘
┌─────────────────┐ ┌─────────────────┐
│ feedback │ │ ingredient │
├─────────────────┤ ├─────────────────┤
│ id │ │ id │
│ meal_plan_item │ │ name │
│ rating │ │ aisle │
│ never_suggest │ │ typical_price │
│ denial_reason │ │ season_months[] │
│ feedback_text │ │ created_at │
│ created_at │ └─────────────────┘
└─────────────────┘
```
### 3.2 Key Relationships
- `family_profile` 1:N `meal_plan`
- `family_profile` 1:N `home_pantry`
- `recipe` N:N `ingredient` (via `recipe_ingredient`)
- `recipe` 1:N `meal_plan_item`
- `meal_plan` 1:N `meal_plan_item`
- `meal_plan_item` 1:1 `feedback`
---
## 4. API Design
### 4.1 Core Endpoints
| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/profile` | Get family profile |
| PUT | `/api/profile` | Update family profile |
| GET | `/api/meals/planned` | Get current week's meal plan |
| GET | `/api/meals/{id}` | Get meal details with recipe |
| POST | `/api/meals/{id}/approve` | Approve a meal |
| POST | `/api/meals/{id}/deny` | Deny a meal with reason |
| GET | `/api/shopping-list` | Get current week's shopping list |
| GET | `/api/shopping-list/print` | Get printable shopping list |
| GET | `/api/pantry` | Get home pantry items |
| POST | `/api/pantry` | Add item to pantry |
| DELETE | `/api/pantry/{id}` | Remove item from pantry |
| POST | `/api/feedback` | Submit meal feedback |
| GET | `/api/recipes` | Search recipes |
| POST | `/api/admin/scrape` | Trigger grocery scrape (admin) |
| GET | `/api/admin/logs` | Get scraper logs |
### 4.2 Email Approval Links
- `GET /api/approve/{token}` → Mark meal approved, redirect to confirmation page
- `GET /api/deny/{token}` → Show denial reason form
- `POST /api/deny/{token}` → Process denial with reason
---
## 5. Deployment Architecture
### 5.1 Docker Compose Services
```yaml
services:
backend:
build: ./backend
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/mealplanner
- SENDGRID_API_KEY=${SENDGRID_API_KEY}
depends_on:
- db
restart: unless-stopped
frontend:
build: ./frontend
ports:
- "3000:80" # nginx serves built React app
depends_on:
- backend
restart: unless-stopped
db:
image: postgres:15-alpine
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
- POSTGRES_USER=mealplanner
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=mealplanner
restart: unless-stopped
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./ssl:/etc/nginx/ssl
depends_on:
- frontend
- backend
restart: unless-stopped
volumes:
postgres_data:
```
### 5.2 Reverse Proxy (Nginx)
Nginx handles:
- SSL termination for remote access
- Routing `/api/*` to backend
- Routing `/*` to frontend
- Rate limiting on approval endpoints
### 5.3 Remote Access Security
- Caddy or Nginx with Let's Encrypt
- VPN option for additional security
- IP whitelist capability
---
## 6. Image Strategy
### 6.1 Primary: Scraped Images
- Recipe images scraped from public recipe sites
- Lucky California product images where available
- Stored as URLs (not downloaded), hotlink protection handled gracefully
### 6.2 Fallback: AI Generation
- Triggered only when scraped image unavailable
- Uses configured AI service (DALL-E, Anthropic, etc.)
- Generated images cached in database
- Config flag to enable/disable
### 6.3 Email Image Handling
- Images embedded via CDN or inline base64
- Alt text provided for email clients that block images
- Link to web UI for full image gallery
---
## 7. Error Handling Strategy
### 7.1 Scraper Failures
1. Log error with full context
2. Retry 3 times with exponential backoff
3. If all fail, flag for manual review
4. Send admin notification after 3 failures
5. Graceful degradation: allow manual sale input
### 7.2 Email Failures
1. Log send attempt
2. Retry via SendGrid's built-in retry
3. If permanently failed, mark meal as "pending email confirmation"
4. Web UI remains as fallback for approval
### 7.3 Database Failures
1. Connection pooling with automatic reconnect
2. Read operations can fall back to cache if available
3. Write operations queued for retry
---
## 8. Logging & Monitoring
### 8.1 Log Categories
- `scraper`: All scraping operations and results
- `email`: SendGrid API calls, delivery status
- `meal_planner`: Plan generation inputs/outputs
- `api`: All HTTP requests
- `feedback`: Feedback submissions
### 8.2 Log Storage
- JSON logs to stdout (Docker log driver)
- Centralized logging optional (Papertrail, Datadog)
- Log rotation: 7 days local
### 8.3 Monitoring (Future)
- Uptime monitoring
- Scraping success rate
- Email delivery rate
- Approval response rate