Fixed purchased item matching
This commit is contained in:
parent
4fa88335d1
commit
410b097d5c
5 changed files with 52 additions and 11 deletions
|
|
@ -1,4 +1,4 @@
|
|||
from ingredients.db import Ingredient, find_ingredients_by_meal_id, find_ingredients_by_recipe_id, insert_ingredient, delete_ingredients_by_meal_id
|
||||
from ingredients.db import Ingredient, find_ingredient_by_id, find_ingredients_by_meal_id, find_ingredients_by_recipe_id, insert_ingredient, delete_ingredients_by_meal_id
|
||||
|
||||
import units
|
||||
from products import Product, find_product_by_tag, get_or_create, add_missing_tags
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ async def insert_ingredient(conn, ingredient: Ingredient):
|
|||
if ingredient.product:
|
||||
ingredient.product_id = ingredient.product.id
|
||||
|
||||
if ingredient.product_id < 0:
|
||||
if ingredient.product_id is None or ingredient.product_id < 0:
|
||||
ingredient.product_id = None
|
||||
|
||||
async with conn.execute('''
|
||||
|
|
@ -46,6 +46,21 @@ async def insert_ingredient(conn, ingredient: Ingredient):
|
|||
''', (ingredient.name, ingredient.line, ingredient.preparation, ingredient.unit, ingredient.quantity, ingredient.product_id, ingredient.recipe_id, ingredient.meal_id)) as cursor:
|
||||
ingredient.id = cursor.lastrowid
|
||||
|
||||
async def find_ingredient_by_id(conn, ingredient_id: int) -> Optional[Ingredient]:
|
||||
ingredient_keys = [f'ingredient.{key}' for key in Ingredient.KEYS]
|
||||
product_keys = [f'product.{key}' for key in Product.KEYS]
|
||||
|
||||
async with conn.execute(f'''
|
||||
SELECT {','.join(ingredient_keys + product_keys)} FROM Ingredient
|
||||
LEFT JOIN Product ON Ingredient.product_id = Product.id
|
||||
WHERE id = ?
|
||||
''', (ingredient_id,)) as cursor:
|
||||
async for row in cursor:
|
||||
product_keys = {k:v for k,v in zip(Product.KEYS, row[len(Ingredient.KEYS):])}
|
||||
product = Product(**product_keys) if product_keys['id'] else None
|
||||
return Ingredient(**{k:v for k,v in zip(Ingredient.KEYS, row[:len(Ingredient.KEYS)])}, product=product)
|
||||
return None
|
||||
|
||||
async def find_ingredients_by_recipe_id(conn, recipe_id: int) -> AsyncIterator[Ingredient]:
|
||||
ingredient_keys = [f'ingredient.{key}' for key in Ingredient.KEYS]
|
||||
product_keys = [f'product.{key}' for key in Product.KEYS]
|
||||
|
|
|
|||
6
main.py
6
main.py
|
|
@ -270,7 +270,7 @@ async def get_current_shopping_list(conn: sqlite3.Connection = Depends(get_db))
|
|||
|
||||
# Reduce the data structure to items and lookups
|
||||
items = meal_requests + outstanding_requests + purchased_requests + [item for sl in shopping_list_lookup.values() for item in sl.items]
|
||||
meals_lookup, recipes_lookup, ingredients_lookup = shopping.remove_references(items)
|
||||
meals_lookup, recipes_lookup, ingredients_lookup = await shopping.to_lookups(conn, items)
|
||||
|
||||
return CurrentShoppingList(
|
||||
outstanding_items=outstanding_requests,
|
||||
|
|
@ -291,7 +291,7 @@ class PurchasedShoppingList(BaseModel):
|
|||
@app.get("/api/shopping/{list_id}")
|
||||
async def get_shopping_list(list_id: int, conn: sqlite3.Connection = Depends(get_db)) -> PurchasedShoppingList:
|
||||
shopping_list = await shopping.load_shopping_list(conn, list_id)
|
||||
meals_lookup, recipes_lookup, ingredients_lookup = shopping.remove_references(shopping_list.items)
|
||||
meals_lookup, recipes_lookup, ingredients_lookup = await shopping.to_lookups(conn, shopping_list.items)
|
||||
return PurchasedShoppingList(list=shopping_list, meals_lookup=meals_lookup, recipes_lookup=recipes_lookup, ingredients_lookup=ingredients_lookup)
|
||||
|
||||
@app.post("/api/shopping/")
|
||||
|
|
@ -302,7 +302,7 @@ async def purchase_ingredients(shopping_list: shopping.ShoppingList, conn: sqlit
|
|||
await conn.commit()
|
||||
|
||||
result = PurchasedShoppingList(list=shopping_list)
|
||||
shopping.remove_references(shopping_list.items, result.meals_lookup, result.recipes_lookup, result.ingredients_lookup)
|
||||
await shopping.to_lookups(conn, shopping_list.items, result.meals_lookup, result.recipes_lookup, result.ingredients_lookup)
|
||||
return result
|
||||
|
||||
@app.get("/api/shopping/current/me/ingredients")
|
||||
|
|
|
|||
|
|
@ -3,11 +3,20 @@ from shopping.db import ShoppingList, ShoppingListItem, load_shopping_list, purc
|
|||
|
||||
from shopping.db import find_items_by_list_id as _find_items_by_list_id, get_purchased_ingredients as _get_purchased_ingredients
|
||||
|
||||
def remove_references(items: List[ShoppingListItem], meals_lookup = None, recipes_lookup = None, ingredients_lookup = None) -> Tuple[Dict[int, Any], Dict[int, Any], Dict[int, Any]]:
|
||||
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
|
||||
|
|
@ -21,7 +30,15 @@ def remove_references(items: List[ShoppingListItem], meals_lookup = None, recipe
|
|||
recipes_lookup[item.recipe.id] = item.recipe
|
||||
item.recipe = None
|
||||
|
||||
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) -> AsyncIterator[ShoppingListItem]:
|
||||
async for item in _find_items_by_list_id(conn, None):
|
||||
|
|
@ -44,10 +61,19 @@ async def get_outstanding_requests(conn) -> Tuple[List[ShoppingListItem], List[S
|
|||
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 async for r in _get_purchased_ingredients(conn, list(meals.keys()))}
|
||||
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)
|
||||
outstanding_items = [r for r in flattened if r.ingredient_id not in purchased_ingredients]
|
||||
purchased_items = [r for r in flattened if r.ingredient_id in purchased_ingredients]
|
||||
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
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ from typing import AsyncIterator, List, ClassVar, Optional
|
|||
from datetime import datetime
|
||||
|
||||
class ShoppingListItem(BaseLinkedModel):
|
||||
KEYS: ClassVar[List[str]] = ['id', 'ingredient_id', 'list_id', 'person_id', 'meal_id', 'created_date']
|
||||
KEYS: ClassVar[List[str]] = ['id', 'ingredient_id', 'list_id', 'person_id', 'meal_id', 'recipe_id', 'created_date']
|
||||
id: int = -1
|
||||
list_id: Optional[int] = None
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue