munch-ease-backend/shopping/__init__.py
jableader 255ebd4613 Squashed commit of the following:
commit 1c707260a4059cbdebb3e2720d3899b8773a1c37
Author: jableader <jacobdunk@gmail.com>
Date:   Wed Jul 30 22:03:48 2025 +1000

    Sync tests

commit 5a71077121921a2cea9f3be9c1d1f7770691520a
Author: jableader <jacobdunk@gmail.com>
Date:   Wed Jul 30 21:57:00 2025 +1000

    minor refactorings

commit 0bafec1b40bf5cf6f1554f6e5ec6e8f4e043d1cc
Author: jableader <jacobdunk@gmail.com>
Date:   Wed Jul 30 21:39:52 2025 +1000

    Remove ids from shopping list

commit c1ee30d1b591b241dfa152d8a0fa9af1f6aaaed1
Author: jableader <jacobdunk@gmail.com>
Date:   Wed Jul 30 21:04:48 2025 +1000

    Added more cases to shopping tests

commit ad3d598bdb574b1aeaabc354bb825ee428aaaa41
Author: jableader <jacobdunk@gmail.com>
Date:   Wed Jul 30 20:26:36 2025 +1000

    Added 404 response when list not found

commit 336538dc47e12736f7f57d2c80252a700dc1e83b
Author: jableader <jacobdunk@gmail.com>
Date:   Wed Jul 30 20:15:52 2025 +1000

    Tests for main

commit 238c0f89b2671348091a162ee519e5f1bf2cd66c
Author: jableader <jacobdunk@gmail.com>
Date:   Wed Jul 30 20:07:12 2025 +1000

    Meals tests

commit b92f2432ee13f5384b6f1a9b8efe76bec9dbd920
Author: jableader <jacobdunk@gmail.com>
Date:   Wed Jul 30 19:57:45 2025 +1000

    Shopping tests

commit b3c7ae20e6207cf0f9f04bdec5cb6145b04638d9
Author: jableader <jacobdunk@gmail.com>
Date:   Wed Jul 30 19:40:19 2025 +1000

    product tests passing
2025-07-30 22:05:29 +10:00

82 lines
4.4 KiB
Python

from typing import Any, AsyncIterator, Dict, Iterator, List, Tuple
from shopping.db import ShoppingList, ShoppingListItem, load_shopping_list, purchase, remove_request, request
from shopping.db import find_items_by_list_id as _find_items_by_list_id, get_purchased_ingredients as _get_purchased_ingredients
import meals, recipes, ingredients
async def to_lookups(conn, items: List[ShoppingListItem], meals_lookup: Dict[int, Any] = None, recipes_lookup: Dict[int, Any] = None, ingredients_lookup: Dict[int, Any] = None) -> Tuple[Dict[int, Any], Dict[int, Any], Dict[int, Any]]:
meals_lookup = meals_lookup or {}
recipes_lookup = recipes_lookup or {}
ingredients_lookup = ingredients_lookup or {}
await _ensure_lookups_populated(conn, items, meals_lookup, recipes_lookup, ingredients_lookup)
return meals_lookup, recipes_lookup, ingredients_lookup
async def _ensure_lookups_populated(conn, items: List[ShoppingListItem], meals_lookup, recipes_lookup, ingredients_lookup):
for item in items:
# If the any item is not in the lookup, we need to add it
if item.meal_id and item.meal_id not in meals_lookup:
meals_lookup[item.meal_id] = await meals.find_meal_by_id(conn, item.meal_id)
if item.recipe_id and item.recipe_id not in recipes_lookup:
recipes_lookup[item.recipe_id] = await recipes.find_recipe_by_id(conn, item.recipe_id)
if item.ingredient_id and item.ingredient_id not in ingredients_lookup:
ingredients_lookup[item.ingredient_id] = await ingredients.find_ingredient_by_id(conn, item.ingredient_id)
async def get_persons_requests(conn, person_id: int) -> List[ingredients.Ingredient]:
ids = [item.ingredient_id async for item in _find_items_by_list_id(conn, None) if item.person_id == person_id and item.ingredient_id is not None and item.meal_id is None]
return [await ingredients.find_ingredient_by_id(conn, ingredient_id) for ingredient_id in ids]
def flatten_items(items: Iterator[ShoppingListItem], meals_lookup: Dict[int, Any]) -> Iterator[ShoppingListItem]:
for item in items:
if item.meal_id and item.meal_id in meals_lookup:
meal = meals_lookup[item.meal_id]
for mealRecipe in meal.recipes:
for ingredient in mealRecipe.recipe.ingredients:
yield ShoppingListItem(
ingredient_id=ingredient.id,
meal_id=item.meal_id,
recipe_id=mealRecipe.recipe.id,
person_id=item.person_id,
created_date=item.created_date
)
for ingredient in meal.extra_ingredients:
yield ShoppingListItem(
ingredient_id=ingredient.id,
meal_id=item.meal_id,
person_id=item.person_id,
created_date=item.created_date
)
else:
yield item
async def get_outstanding_requests(conn) -> Tuple[List[ShoppingListItem], List[ShoppingListItem], List[ShoppingListItem], Dict[int, Any], Dict[int, Any], Dict[int, Any]]:
current_requests = [r async for r in _find_items_by_list_id(conn, None)]
meal_requests = [r for r in current_requests if r.meal_id is not None and r.meal_id > 0]
# Get lookups for meals to enable flattening
meals_lookup, recipes_lookup, ingredients_lookup = await to_lookups(conn, current_requests)
meal_ids = [r.meal_id for r in meal_requests if r.meal_id]
purchased_ingredients = {(r.ingredient_id, r.meal_id, r.recipe_id): r async for r in _get_purchased_ingredients(conn, meal_ids)}
outstanding_items = []
purchased_items = []
flattened = list(flatten_items(current_requests, meals_lookup))
# Now ensure that all ingredients from the flattened items are in the lookup
await _ensure_lookups_populated(conn, flattened, meals_lookup, recipes_lookup, ingredients_lookup)
for r in flattened:
# Meal ingredients may have already been purchased
if r.meal_id is not None and r.meal_id > 0:
purchased_item = purchased_ingredients.get((r.ingredient_id, r.meal_id, r.recipe_id))
if purchased_item:
purchased_items.append(purchased_item)
continue
outstanding_items.append(r)
return outstanding_items, purchased_items, meal_requests, meals_lookup, recipes_lookup, ingredients_lookup