feat(backend): implement unit conversion for cost calculation

- Add UnitConverter (normalization, within-family, density tables)
- Update cost.py to convert recipe qty to grocery price unit
- Update generate.py _load_match_index to fetch ingredient name + unit
- Fix orchestrator email/shopping-list cost loops to use conversion
- Fix missing Ingredient import in generate.py
- Add 19 unit tests
This commit is contained in:
2026-05-24 13:46:50 -07:00
parent 3885d7d0dc
commit fd8ba3c4d2
6 changed files with 429 additions and 20 deletions
+6 -2
View File
@@ -12,6 +12,7 @@ from app.models import (
FamilyProfile,
GroceryItem,
HomePantry,
Ingredient,
IngredientGroceryMatch,
MealPlan,
MealPlanItem,
@@ -32,13 +33,14 @@ from app.services.planner.types import GenerationResult
def _load_match_index(db: Session) -> Dict[UUID, List[dict]]:
"""ingredient_id → list of match dicts ordered by confidence DESC."""
rows = (
db.query(IngredientGroceryMatch, GroceryItem)
db.query(IngredientGroceryMatch, GroceryItem, Ingredient)
.join(GroceryItem, GroceryItem.id == IngredientGroceryMatch.grocery_item_id)
.join(Ingredient, Ingredient.id == IngredientGroceryMatch.ingredient_id)
.order_by(IngredientGroceryMatch.confidence.desc())
.all()
)
index: Dict[UUID, List[dict]] = {}
for match, grocery in rows:
for match, grocery, ingredient in rows:
index.setdefault(match.ingredient_id, []).append(
{
"grocery_item_id": grocery.id,
@@ -47,6 +49,8 @@ def _load_match_index(db: Session) -> Dict[UUID, List[dict]]:
"regular_price": grocery.regular_price,
"is_on_sale": bool(grocery.is_on_sale),
"confidence": match.confidence,
"ingredient_name": ingredient.name.lower(),
"grocery_unit": grocery.unit,
}
)
return index