| """Per-serving macro estimator — ingredient lookup, no extra model call needed.""" |
| from __future__ import annotations |
|
|
| |
| _MACROS: dict[str, tuple[float, float, float, float, float]] = { |
| |
| "chicken": (165, 31, 0, 3.6, 0), |
| "beef": (250, 26, 0, 16, 0), |
| "pork": (242, 27, 0, 14, 0), |
| "fish": (130, 20, 0, 5, 0), |
| "salmon": (208, 20, 0, 13, 0), |
| "tuna": (130, 29, 0, 0.5, 0), |
| "shrimp": (99, 24, 0, 0.3, 0), |
| "egg": (155, 13, 1.1, 11, 0), |
| "eggs": (155, 13, 1.1, 11, 0), |
| "tofu": (76, 8, 1.9, 4.8, 0.3), |
| |
| "milk": (61, 3.2, 4.8, 3.3, 0), |
| "cheese": (402, 25, 1.3, 33, 0), |
| "butter": (717, 0.9, 0.1, 81, 0), |
| "yogurt": (59, 3.5, 4.7, 3.3, 0), |
| "cream": (340, 2.1, 2.8, 36, 0), |
| |
| "rice": (130, 2.7, 28, 0.3, 0.4), |
| "pasta": (158, 5.8, 31, 0.9, 1.8), |
| "bread": (265, 9, 49, 3.2, 2.7), |
| "potato": (77, 2, 17, 0.1, 2.2), |
| "potatoes": (77, 2, 17, 0.1, 2.2), |
| "flour": (364, 10, 76, 1, 2.7), |
| "oats": (389, 17, 66, 7, 10.6), |
| "quinoa": (120, 4.1, 21, 1.9, 2.8), |
| "lentils": (116, 9, 20, 0.4, 7.9), |
| "beans": (347, 21, 60, 1.2, 15), |
| "chickpeas": (164, 8.9, 27, 2.6, 7.6), |
| |
| "tomato": (18, 0.9, 3.9, 0.2, 1.2), |
| "tomatoes": (18, 0.9, 3.9, 0.2, 1.2), |
| "onion": (40, 1.1, 9.3, 0.1, 1.7), |
| "onions": (40, 1.1, 9.3, 0.1, 1.7), |
| "garlic": (149, 6.4, 33, 0.5, 2.1), |
| "carrot": (41, 0.9, 10, 0.2, 2.8), |
| "carrots": (41, 0.9, 10, 0.2, 2.8), |
| "broccoli": (34, 2.8, 7, 0.4, 2.6), |
| "spinach": (23, 2.9, 3.6, 0.4, 2.2), |
| "pepper": (31, 1, 6, 0.3, 2.1), |
| "peppers": (31, 1, 6, 0.3, 2.1), |
| "mushroom": (22, 3.1, 3.3, 0.3, 1), |
| "mushrooms": (22, 3.1, 3.3, 0.3, 1), |
| "zucchini": (17, 1.2, 3.1, 0.3, 1), |
| "corn": (86, 3.3, 19, 1.4, 2.7), |
| "lettuce": (15, 1.4, 2.9, 0.2, 1.3), |
| "cucumber": (16, 0.7, 3.6, 0.1, 0.5), |
| "eggplant": (25, 1, 5.9, 0.2, 3), |
| "cabbage": (25, 1.3, 5.8, 0.1, 2.5), |
| "celery": (16, 0.7, 3, 0.2, 1.6), |
| "leek": (61, 1.5, 14, 0.3, 1.8), |
| |
| "apple": (52, 0.3, 14, 0.2, 2.4), |
| "banana": (89, 1.1, 23, 0.3, 2.6), |
| "lemon": (29, 1.1, 9.3, 0.3, 2.8), |
| "lime": (30, 0.7, 10.5, 0.2, 2.8), |
| "orange": (47, 0.9, 12, 0.1, 2.4), |
| |
| "olive oil": (884, 0, 0, 100, 0), |
| "oil": (884, 0, 0, 100, 0), |
| "soy sauce": (53, 8.1, 4.9, 0.1, 0.8), |
| "honey": (304, 0.3, 82, 0, 0.2), |
| "sugar": (387, 0, 100, 0, 0), |
| "salt": (0, 0, 0, 0, 0), |
| "vinegar": (18, 0, 0.9, 0, 0), |
| } |
|
|
| |
| _GRAMS: dict[str, int] = { |
| "egg": 50, "eggs": 100, |
| "butter": 15, |
| "olive oil": 14, "oil": 14, |
| "soy sauce": 15, |
| "salt": 3, |
| "garlic": 10, |
| "honey": 21, |
| "sugar": 12, |
| "lemon": 30, "lime": 30, |
| } |
| _DEFAULT_GRAMS = 80 |
|
|
|
|
| def compute_nutrition(ingredients: list[str], servings: int = 2) -> dict[str, float]: |
| """Return per-serving macro estimates keyed to the NutritionGrid format.""" |
| cal = prot = carb = fat = fib = 0.0 |
| for ing in ingredients: |
| key = ing.lower().strip() |
| row = _MACROS.get(key) or _MACROS.get(key.split()[0]) if key else None |
| if row is None: |
| continue |
| grams = _GRAMS.get(key, _DEFAULT_GRAMS) |
| f = grams / 100 |
| c, p, cb, ft, fb = row |
| cal += c * f |
| prot += p * f |
| carb += cb * f |
| fat += ft * f |
| fib += fb * f |
|
|
| sv = max(servings, 1) |
| return { |
| "calories": round(cal / sv), |
| "protein_g": round(prot / sv, 1), |
| "carbs_g": round(carb / sv, 1), |
| "fat_g": round(fat / sv, 1), |
| "fiber_g": round(fib / sv, 1), |
| } |
|
|