fix(planner): correct set_size to 3 dinners and switch cost filter to per-serving

- set_size 21→3, top_k 20→10: generate 3 weekly dinners not full 3×7 matrix
- All 3 items assigned MealType.DINNER on Mon/Wed/Fri
- RecipeCost gains servings field + cost_per_serving property
- compute_recipe_cost accepts servings param (default 4)
- filter.py gates on cost_per_serving instead of total_cost
- max_meal_cost 500→50 (now a meaningful $/serving threshold)
- Email displays ~$X/serving instead of inflated raw total
- select_set: candidate_pool uses max(top_k, set_size) to prevent
  combinations(n<set_size) returning empty iterator

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-22 07:20:17 -07:00
co-authored by Claude Sonnet 4.6
parent 16069bfa92
commit 35f736a052
7 changed files with 27 additions and 13 deletions
+8 -5
View File
@@ -238,8 +238,8 @@ def step_email(run: "WeeklyRun", db: "Session") -> None:
if instr_rows else ""
)
# Estimated cost: sum top-confidence grocery match prices
est_cost = 0.0
# Estimated cost: sum top-confidence grocery match prices ÷ servings
est_cost_total = 0.0
for ing in ingredients:
ing_name = ingredient_names.get(str(ing.get("ingredient_id", "")), ing.get("name", "")).lower()
match = (
@@ -250,11 +250,14 @@ def step_email(run: "WeeklyRun", db: "Session") -> None:
.first()
)
if match and match.grocery_item and match.grocery_item.current_price:
est_cost += float(match.grocery_item.current_price)
est_cost_total += float(match.grocery_item.current_price)
recipe_servings = (item.recipe.servings or 4) if item.recipe else 4
est_cost_per_serving = est_cost_total / recipe_servings
cost_block = (
f"<p style='font-size:13px;color:#888'>Est. cost: ~${est_cost:.2f}</p>"
if est_cost > 0 else ""
f"<p style='font-size:13px;color:#888'>Est. ~${est_cost_per_serving:.2f}/serving</p>"
if est_cost_total > 0 else ""
)
item_html_parts.append(