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 {} lookups = (meals_lookup, recipes_lookup, ingredients_lookup) _move_refs_to_lookups(items, *lookups) await _ensure_lookups_populated(conn, items, *lookups) return lookups def _move_refs_to_lookups(items: List[ShoppingListItem], meals_lookup: Dict[int, Any], recipes_lookup: Dict[int, Any], ingredients_lookup: Dict[int, Any]): for item in items: if item.meal and not item.meal.id in meals_lookup: meals_lookup[item.meal.id] = item.meal item.meal = None if item.ingredient and not item.ingredient.id in ingredients_lookup: ingredients_lookup[item.ingredient.id] = item.ingredient item.ingredient = None if item.recipe and not item.recipe.id in recipes_lookup: recipes_lookup[item.recipe.id] = item.recipe item.recipe = None 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) -> AsyncIterator[ShoppingListItem]: async for item in _find_items_by_list_id(conn, None): if item.person_id == person_id and item.ingredient_id is not None: yield item def flatten_items(items: Iterator[ShoppingListItem]) -> Iterator[ShoppingListItem]: for item in items: if item.meal: for mealRecipe in item.meal.recipes: for ingredient in mealRecipe.recipe.ingredients: yield ShoppingListItem(ingredient=ingredient, meal=item.meal, recipe=mealRecipe.recipe, person_id=item.person_id, created_date=item.created_date) for ingredient in item.meal.extra_ingredients: yield ShoppingListItem(ingredient=ingredient, meal=item.meal, 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]]: 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 and r.meal is not None] meals = {r.meal_id: r.meal for r in meal_requests} purchased_ingredients = {(r.ingredient_id, r.meal_id, r.recipe_id): r async for r in _get_purchased_ingredients(conn, list(meals.keys()))} outstanding_items = [] purchased_items = [] flattened = flatten_items(current_requests) 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