Public Access
docs: phase 9 complete - planner algorithm shipped
This commit is contained in:
+9
-6
@@ -29,6 +29,7 @@ The project's reason to exist — the meal-planner generation algorithm — is *
|
||||
- Email backend (`backend/app/services/email.py`): Protocol + `ConsoleEmailBackend` (writes JSONL to `backend/var/email_outbox.jsonl`) + `SendGridEmailBackend` stub that raises `NotImplementedError`. Selected via `EMAIL_BACKEND` env (default `console`).
|
||||
- 401 from Swiftly raises `SwiftlyAuthError` carrying the verbatim message `"SWIFTLY_BEARER_TOKEN expired — request a fresh token from the user (capture from luckysupermarkets.com network tab on a /search/api/v1 request)"`. The bg runner catches it and writes `ScrapeLog.error_message` so it surfaces via the admin logs endpoint.
|
||||
- Thin Phase 4: ingredient + recipe CRUD endpoints with admin gating; NeverSuggest CRUD (covers both ingredient blocklist and recipe blocklist via the existing schema); ingredient↔grocery_item match layer (rapidfuzz top-3 ranking with confidence threshold 0.75, manual override via /api/admin/ingredients/{id}/matches and /api/admin/ingredient-matches/{id}); 50 canonical ingredients seeded with aliases enriching pre-existing rows from migration 0002; 30 starter recipes spanning chicken/beef/turkey/pork/fish/vegetarian with varied cuisines, all under 45 min for 28/30. Match job runs after each successful scrape; matcher failures don't flip the scrape to FAILED.
|
||||
- Phase 9: meal-plan generation. POST /api/admin/meal-plans/generate runs the full filter→score→set-select pipeline against seeded recipes and produces a persisted MealPlan with up to 3 MealPlanItem dinners. Regenerate endpoint accepts relaxed constraint overrides (`relax_time_max_minutes`, `relax_calorie_pct`, `relax_max_meal_cost`) and deletes any prior plan for the same `(family, week_start_date)` before re-running. Per-meal cost matched against ingredient_grocery_match using the top-confidence grocery row. 88 tests green.
|
||||
|
||||
### Database
|
||||
- Postgres 15. Seven migrations: `0001_initial_migration`, `0002_seed_data`, `0003_grocery_item_description`, `0004_family_profile_calorie_target`, `0005_grocery_item_external_id`, `0006_thin_phase4` (ingredient.aliases, recipe.calories_per_serving, ingredient_grocery_match), `0007_seed_canonical_ingredients` (50 ingredients + 30 recipes).
|
||||
@@ -71,12 +72,6 @@ The project's reason to exist — the meal-planner generation algorithm — is *
|
||||
- `feedback` table exists with `rating`, `denial_reason`, free-text. No frontend page reads or writes it. No `/api/feedback` router (folded into `meals.py`?).
|
||||
- The "learn from feedback" loop into Phase 9 is unscoped.
|
||||
|
||||
### Phase 9 — **Meal-planner generation algorithm (not started)**
|
||||
- This is the core of the project.
|
||||
- Inputs: `family_profile` (size, budget, calorie target), `family_member` preferences (mushroom etc.), `never_suggest` filter, `home_pantry` items, `grocery_item` sales for the week, `recipe` library, prior `feedback` (downweight low-rated, never-suggest-flagged).
|
||||
- Output: 7 `MealPlanItem` rows (or 21 if 3 meals/day) with chosen `recipe_id`, day, meal_type.
|
||||
- Constraints from spec: variety (max 2 same protein/sauce per week), respect mushroom rule (3 of 4 don't like → recipes containing mushrooms heavily downweighted unless flagged "kid-only"), prefer pantry items, prefer sale items, hit budget per meal.
|
||||
|
||||
### Phase 10 — Images (not started)
|
||||
- Recipe images: scrape from source sites first, AI fallback (`AI_IMAGE_ENABLED=false` flag exists, no implementation).
|
||||
|
||||
@@ -110,6 +105,14 @@ The project's reason to exist — the meal-planner generation algorithm — is *
|
||||
|
||||
10. **Frontend doesn't have a login UI.** Until you build one, the family-facing flows can't actually be exercised by a real user — only by tests. The Dashboard/Pantry/etc. pages assume the cookie is already set.
|
||||
|
||||
11. `regenerate.exclude_recipe_ids` accepted by the API for forward compat but not yet honored by the orchestrator — only NeverSuggest blocklist applies. ~30-line follow-up.
|
||||
|
||||
12. `GET /api/meal-plans/{id}` returns persisted items but with `score=0`, `components={}`, and zeroed debug — those are only available in the immediate `generate` response. Acceptable for the email-approval flow which uses the generate response directly. To persist them, add columns to MealPlanItem.
|
||||
|
||||
13. `family_profile.calorie_target` is treated as per-serving by the planner filter (matches spec §2.1 wording). The family-setup UI/API should clarify per-serving vs per-day to avoid confusion. Test families use ~500 cal/serving for a 4-person household.
|
||||
|
||||
14. Cost estimation treats `qty` as dimensionless (no unit conversion). Produces a biased-but-monotonic ranking signal; sufficient for current use, revisit if real-dollar accuracy is needed (`docs/specs/2026-05-05-meal-planner-algorithm-design.md` §7).
|
||||
|
||||
---
|
||||
|
||||
## Verification commands
|
||||
|
||||
Reference in New Issue
Block a user