munch-ease-backend/shopping/repository.py

376 lines
12 KiB
Python
Raw Normal View History

Squashed commit of the following: commit fcd005b8624023547f28b7b28e59e6099bcfc7d4 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 20:24:07 2025 +1100 Openapi tightening commit f93bd8f641d561052c7bd075bae321b4ff3b676d Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 19:03:52 2025 +1100 Removed refactor strategy doc commit 0c5a61092f522be0c47cbbe86917c8a7e4e2d339 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 17:48:33 2025 +1100 mypy & ruff checks commit 23d66d6b18984127e17c73c3063f6120385935e9 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 16:49:35 2025 +1100 Final removal of db.py files commit f454aed1ca9783cc558cc203f29a7fe31b62a975 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:42:31 2025 +1100 Finalise restructure, remove db.py files commit 7187f6dd89489521538791c6bdebb426514beb99 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:34:54 2025 +1100 commit 6fea227ae20d32b8eb1e7a4885006a620fcc7bb1 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:32:53 2025 +1100 commit 27415e7e02d89195ad514cb017a9dbbf84d7a5e4 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:31:10 2025 +1100 commit b773428033d855f9ad82005602e049c1a2e3c585 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:28:58 2025 +1100 commit 116592c95278d995f4c516e87f2cea43cf5b7735 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:25:21 2025 +1100 commit 03ec565faea088971968ee2f9bb83e2de16b21f3 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:21:29 2025 +1100 Plan
2025-10-19 09:24:23 +00:00
from typing import Any, AsyncIterator, List, Optional
2025-10-18 03:26:42 +00:00
Squashed commit of the following: commit fcd005b8624023547f28b7b28e59e6099bcfc7d4 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 20:24:07 2025 +1100 Openapi tightening commit f93bd8f641d561052c7bd075bae321b4ff3b676d Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 19:03:52 2025 +1100 Removed refactor strategy doc commit 0c5a61092f522be0c47cbbe86917c8a7e4e2d339 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 17:48:33 2025 +1100 mypy & ruff checks commit 23d66d6b18984127e17c73c3063f6120385935e9 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 16:49:35 2025 +1100 Final removal of db.py files commit f454aed1ca9783cc558cc203f29a7fe31b62a975 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:42:31 2025 +1100 Finalise restructure, remove db.py files commit 7187f6dd89489521538791c6bdebb426514beb99 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:34:54 2025 +1100 commit 6fea227ae20d32b8eb1e7a4885006a620fcc7bb1 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:32:53 2025 +1100 commit 27415e7e02d89195ad514cb017a9dbbf84d7a5e4 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:31:10 2025 +1100 commit b773428033d855f9ad82005602e049c1a2e3c585 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:28:58 2025 +1100 commit 116592c95278d995f4c516e87f2cea43cf5b7735 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:25:21 2025 +1100 commit 03ec565faea088971968ee2f9bb83e2de16b21f3 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:21:29 2025 +1100 Plan
2025-10-19 09:24:23 +00:00
from shopping.models import ShoppingList, ShoppingListItem
2025-10-18 03:26:42 +00:00
2024-05-17 09:09:03 +00:00
async def create(conn):
2025-10-18 03:26:42 +00:00
await conn.execute(
"""
2024-05-17 09:09:03 +00:00
CREATE TABLE IF NOT EXISTS ShoppingList (
id INTEGER PRIMARY KEY,
2024-10-14 06:36:56 +00:00
created_date DATETIME NOT NULL,
store_name TEXT NOT NULL,
purchased_by_id INTEGER,
FOREIGN KEY(purchased_by_id) REFERENCES Person(id)
2025-10-18 03:26:42 +00:00
);"""
)
await conn.execute(
"""
CREATE TABLE IF NOT EXISTS ShoppingListItem (
2024-05-17 09:09:03 +00:00
id INTEGER PRIMARY KEY,
ingredient_id INTEGER,
list_id INTEGER,
person_id INTEGER,
meal_id INTEGER,
recipe_id INTEGER,
2024-10-14 06:36:56 +00:00
created_date DATETIME NOT NULL,
2024-05-17 09:09:03 +00:00
FOREIGN KEY(ingredient_id) REFERENCES Ingredient(id),
FOREIGN KEY(list_id) REFERENCES ShoppingList(id),
2024-05-17 09:09:03 +00:00
FOREIGN KEY(person_id) REFERENCES Person(id),
FOREIGN KEY(meal_id) REFERENCES Meal(id),
FOREIGN KEY(recipe_id) REFERENCES Recipe(id)
2025-10-18 03:26:42 +00:00
);"""
)
# Useful indexes for queries
await conn.execute(
"CREATE INDEX IF NOT EXISTS idx_shopping_item_list_id ON ShoppingListItem(list_id);"
)
await conn.execute(
"CREATE INDEX IF NOT EXISTS idx_shopping_item_meal_id ON ShoppingListItem(meal_id);"
)
Squashed commit of the following: commit fcd005b8624023547f28b7b28e59e6099bcfc7d4 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 20:24:07 2025 +1100 Openapi tightening commit f93bd8f641d561052c7bd075bae321b4ff3b676d Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 19:03:52 2025 +1100 Removed refactor strategy doc commit 0c5a61092f522be0c47cbbe86917c8a7e4e2d339 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 17:48:33 2025 +1100 mypy & ruff checks commit 23d66d6b18984127e17c73c3063f6120385935e9 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 16:49:35 2025 +1100 Final removal of db.py files commit f454aed1ca9783cc558cc203f29a7fe31b62a975 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:42:31 2025 +1100 Finalise restructure, remove db.py files commit 7187f6dd89489521538791c6bdebb426514beb99 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:34:54 2025 +1100 commit 6fea227ae20d32b8eb1e7a4885006a620fcc7bb1 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:32:53 2025 +1100 commit 27415e7e02d89195ad514cb017a9dbbf84d7a5e4 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:31:10 2025 +1100 commit b773428033d855f9ad82005602e049c1a2e3c585 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:28:58 2025 +1100 commit 116592c95278d995f4c516e87f2cea43cf5b7735 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:25:21 2025 +1100 commit 03ec565faea088971968ee2f9bb83e2de16b21f3 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:21:29 2025 +1100 Plan
2025-10-19 09:24:23 +00:00
await conn.execute(
"CREATE INDEX IF NOT EXISTS idx_shopping_item_person_null_list ON ShoppingListItem(person_id, ingredient_id) WHERE list_id IS NULL;"
)
2025-10-18 03:26:42 +00:00
2024-05-17 09:09:03 +00:00
def validate_request(request: ShoppingListItem) -> None:
if request.person_id < 0:
2025-10-18 03:26:42 +00:00
raise ValueError("Requests must have a person")
2024-05-20 10:09:57 +00:00
# A request must have either an ingredient or a meal, but not both
if not request.ingredient_id and not request.meal_id:
2025-10-18 03:26:42 +00:00
raise ValueError("Request must have either an ingredient or a meal")
async def purchase(conn, shopping_list: ShoppingList) -> None:
2025-07-29 07:15:38 +00:00
if shopping_list.purchased_by_id is None or shopping_list.purchased_by_id < 0:
2025-10-18 03:26:42 +00:00
raise ValueError("Shopping list must have a person id")
if shopping_list.items is None or len(shopping_list.items) == 0:
2025-10-18 03:26:42 +00:00
raise ValueError("Shopping list must have items")
Squashed commit of the following: commit fcd005b8624023547f28b7b28e59e6099bcfc7d4 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 20:24:07 2025 +1100 Openapi tightening commit f93bd8f641d561052c7bd075bae321b4ff3b676d Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 19:03:52 2025 +1100 Removed refactor strategy doc commit 0c5a61092f522be0c47cbbe86917c8a7e4e2d339 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 17:48:33 2025 +1100 mypy & ruff checks commit 23d66d6b18984127e17c73c3063f6120385935e9 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 16:49:35 2025 +1100 Final removal of db.py files commit f454aed1ca9783cc558cc203f29a7fe31b62a975 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:42:31 2025 +1100 Finalise restructure, remove db.py files commit 7187f6dd89489521538791c6bdebb426514beb99 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:34:54 2025 +1100 commit 6fea227ae20d32b8eb1e7a4885006a620fcc7bb1 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:32:53 2025 +1100 commit 27415e7e02d89195ad514cb017a9dbbf84d7a5e4 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:31:10 2025 +1100 commit b773428033d855f9ad82005602e049c1a2e3c585 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:28:58 2025 +1100 commit 116592c95278d995f4c516e87f2cea43cf5b7735 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:25:21 2025 +1100 commit 03ec565faea088971968ee2f9bb83e2de16b21f3 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:21:29 2025 +1100 Plan
2025-10-19 09:24:23 +00:00
from datetime import datetime
2024-10-14 05:59:05 +00:00
shopping_list.created_date = datetime.now().astimezone()
2025-10-18 03:26:42 +00:00
async with conn.execute(
"""
INSERT INTO ShoppingList (created_date, store_name, purchased_by_id)
VALUES (?, ?, ?)
2025-10-18 03:26:42 +00:00
""",
(
shopping_list.created_date.isoformat(),
shopping_list.store_name,
shopping_list.purchased_by_id,
),
) as cursor:
2024-05-17 09:09:03 +00:00
shopping_list.id = cursor.lastrowid
for item in shopping_list.items:
item.list_id = shopping_list.id
validate_request(item)
if item.ingredient_id is None or item.ingredient_id < 0:
2025-10-18 03:26:42 +00:00
raise ValueError("Ingredient request must have a valid ingredient id")
isMeal = item.meal_id is not None and item.meal_id >= 0
isPersonRequest = (not isMeal) and item.person_id is not None and item.person_id >= 0
if not isMeal and not isPersonRequest:
2025-10-18 03:26:42 +00:00
raise ValueError("Ingredient request must have either a meal or a person id")
if isPersonRequest:
# Update existing request from its null id, or throw
2025-10-18 03:26:42 +00:00
async with conn.execute(
"""
UPDATE ShoppingListItem
SET list_id = ?
WHERE ingredient_id = ?
AND list_id IS NULL
AND person_id = ?
AND meal_id IS NULL
AND recipe_id IS NULL
2025-10-18 03:26:42 +00:00
""",
(shopping_list.id, item.ingredient_id, item.person_id),
) as cursor:
if cursor.rowcount == 0:
2025-10-18 03:26:42 +00:00
raise ValueError(
"Ingredient request must have a valid person id and ingredient id"
)
elif isMeal:
# Insert new request for meal
if item.meal_id is None or item.meal_id < 0:
2025-10-18 03:26:42 +00:00
raise ValueError("Meal request must have a valid meal id")
async with conn.execute(
"""
INSERT INTO ShoppingListItem (ingredient_id, list_id, person_id, meal_id, recipe_id, created_date)
VALUES (?, ?, ?, ?, ?, ?)
2025-10-18 03:26:42 +00:00
""",
(
item.ingredient_id,
shopping_list.id,
item.person_id,
item.meal_id,
item.recipe_id,
item.created_date.isoformat(),
),
) as cursor:
item.id = cursor.lastrowid
2025-10-18 03:26:42 +00:00
meal_ids = list(
{
item.meal_id
for item in shopping_list.items
if item.meal_id is not None and item.meal_id >= 0
}
)
await update_purchased_meals(conn, meal_ids)
2025-10-18 03:26:42 +00:00
async def update_purchased_meals(conn, meal_ids: List[int]) -> None:
if not meal_ids:
return
2025-10-18 03:26:42 +00:00
purchased_ingredient_ids = {
item.ingredient_id async for item in get_purchased_ingredients(conn, meal_ids)
}
Squashed commit of the following: commit fcd005b8624023547f28b7b28e59e6099bcfc7d4 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 20:24:07 2025 +1100 Openapi tightening commit f93bd8f641d561052c7bd075bae321b4ff3b676d Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 19:03:52 2025 +1100 Removed refactor strategy doc commit 0c5a61092f522be0c47cbbe86917c8a7e4e2d339 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 17:48:33 2025 +1100 mypy & ruff checks commit 23d66d6b18984127e17c73c3063f6120385935e9 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 16:49:35 2025 +1100 Final removal of db.py files commit f454aed1ca9783cc558cc203f29a7fe31b62a975 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:42:31 2025 +1100 Finalise restructure, remove db.py files commit 7187f6dd89489521538791c6bdebb426514beb99 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:34:54 2025 +1100 commit 6fea227ae20d32b8eb1e7a4885006a620fcc7bb1 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:32:53 2025 +1100 commit 27415e7e02d89195ad514cb017a9dbbf84d7a5e4 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:31:10 2025 +1100 commit b773428033d855f9ad82005602e049c1a2e3c585 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:28:58 2025 +1100 commit 116592c95278d995f4c516e87f2cea43cf5b7735 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:25:21 2025 +1100 commit 03ec565faea088971968ee2f9bb83e2de16b21f3 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:21:29 2025 +1100 Plan
2025-10-19 09:24:23 +00:00
from meals.repository import find_meal_by_id, mark_purchased
for meal_id in meal_ids:
meal = await find_meal_by_id(conn, meal_id)
2025-10-18 03:26:42 +00:00
if not meal:
continue
ingredients = {
ingredient.id
for mr in meal.recipes
for ingredient in (mr.recipe.ingredients if mr.recipe else [])
} | {ingredient.id for ingredient in meal.extra_ingredients}
remaining_ingredients = ingredients - purchased_ingredient_ids
if not remaining_ingredients:
await mark_purchased(conn, meal)
2025-10-18 03:26:42 +00:00
await remove_request(conn, person=None, meal=meal)
Squashed commit of the following: commit fcd005b8624023547f28b7b28e59e6099bcfc7d4 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 20:24:07 2025 +1100 Openapi tightening commit f93bd8f641d561052c7bd075bae321b4ff3b676d Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 19:03:52 2025 +1100 Removed refactor strategy doc commit 0c5a61092f522be0c47cbbe86917c8a7e4e2d339 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 17:48:33 2025 +1100 mypy & ruff checks commit 23d66d6b18984127e17c73c3063f6120385935e9 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 16:49:35 2025 +1100 Final removal of db.py files commit f454aed1ca9783cc558cc203f29a7fe31b62a975 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:42:31 2025 +1100 Finalise restructure, remove db.py files commit 7187f6dd89489521538791c6bdebb426514beb99 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:34:54 2025 +1100 commit 6fea227ae20d32b8eb1e7a4885006a620fcc7bb1 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:32:53 2025 +1100 commit 27415e7e02d89195ad514cb017a9dbbf84d7a5e4 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:31:10 2025 +1100 commit b773428033d855f9ad82005602e049c1a2e3c585 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:28:58 2025 +1100 commit 116592c95278d995f4c516e87f2cea43cf5b7735 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:25:21 2025 +1100 commit 03ec565faea088971968ee2f9bb83e2de16b21f3 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:21:29 2025 +1100 Plan
2025-10-19 09:24:23 +00:00
async def is_requested(conn, meal) -> bool:
if meal.id < 0:
return False
2024-05-20 10:09:57 +00:00
2025-10-18 03:26:42 +00:00
async with conn.execute(
"""
SELECT COUNT(*) FROM ShoppingListItem
WHERE meal_id = ? AND list_id IS NULL
2025-10-18 03:26:42 +00:00
""",
(meal.id,),
) as cursor:
row = await cursor.fetchone()
return row[0] > 0
2024-05-20 10:09:57 +00:00
2025-10-18 03:26:42 +00:00
async def request(
Squashed commit of the following: commit fcd005b8624023547f28b7b28e59e6099bcfc7d4 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 20:24:07 2025 +1100 Openapi tightening commit f93bd8f641d561052c7bd075bae321b4ff3b676d Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 19:03:52 2025 +1100 Removed refactor strategy doc commit 0c5a61092f522be0c47cbbe86917c8a7e4e2d339 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 17:48:33 2025 +1100 mypy & ruff checks commit 23d66d6b18984127e17c73c3063f6120385935e9 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 16:49:35 2025 +1100 Final removal of db.py files commit f454aed1ca9783cc558cc203f29a7fe31b62a975 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:42:31 2025 +1100 Finalise restructure, remove db.py files commit 7187f6dd89489521538791c6bdebb426514beb99 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:34:54 2025 +1100 commit 6fea227ae20d32b8eb1e7a4885006a620fcc7bb1 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:32:53 2025 +1100 commit 27415e7e02d89195ad514cb017a9dbbf84d7a5e4 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:31:10 2025 +1100 commit b773428033d855f9ad82005602e049c1a2e3c585 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:28:58 2025 +1100 commit 116592c95278d995f4c516e87f2cea43cf5b7735 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:25:21 2025 +1100 commit 03ec565faea088971968ee2f9bb83e2de16b21f3 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:21:29 2025 +1100 Plan
2025-10-19 09:24:23 +00:00
conn, person, ingredient: Optional[Any] = None, meal: Optional[Any] = None
2025-10-18 03:26:42 +00:00
) -> ShoppingListItem:
Squashed commit of the following: commit fcd005b8624023547f28b7b28e59e6099bcfc7d4 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 20:24:07 2025 +1100 Openapi tightening commit f93bd8f641d561052c7bd075bae321b4ff3b676d Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 19:03:52 2025 +1100 Removed refactor strategy doc commit 0c5a61092f522be0c47cbbe86917c8a7e4e2d339 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 17:48:33 2025 +1100 mypy & ruff checks commit 23d66d6b18984127e17c73c3063f6120385935e9 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 16:49:35 2025 +1100 Final removal of db.py files commit f454aed1ca9783cc558cc203f29a7fe31b62a975 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:42:31 2025 +1100 Finalise restructure, remove db.py files commit 7187f6dd89489521538791c6bdebb426514beb99 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:34:54 2025 +1100 commit 6fea227ae20d32b8eb1e7a4885006a620fcc7bb1 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:32:53 2025 +1100 commit 27415e7e02d89195ad514cb017a9dbbf84d7a5e4 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:31:10 2025 +1100 commit b773428033d855f9ad82005602e049c1a2e3c585 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:28:58 2025 +1100 commit 116592c95278d995f4c516e87f2cea43cf5b7735 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:25:21 2025 +1100 commit 03ec565faea088971968ee2f9bb83e2de16b21f3 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:21:29 2025 +1100 Plan
2025-10-19 09:24:23 +00:00
from ingredients.repository import insert_ingredient
if ingredient is not None and meal is not None:
2025-10-18 03:26:42 +00:00
raise ValueError("Cannot request both an ingredient and a meal")
2024-05-17 09:09:03 +00:00
if ingredient is None and meal is None:
2025-10-18 03:26:42 +00:00
raise ValueError("Must specify either an ingredient or a meal to request")
2024-05-20 10:09:57 +00:00
if meal is not None and meal.id < 0:
2025-10-18 03:26:42 +00:00
raise ValueError("Meal must have a valid id")
2024-05-20 10:09:57 +00:00
if ingredient is not None and ingredient.id < 0:
await insert_ingredient(conn, ingredient)
2024-05-17 09:09:03 +00:00
ingredient_id = ingredient.id if ingredient else None
meal_id = meal.id if meal else None
2025-10-18 03:26:42 +00:00
item = ShoppingListItem(ingredient_id=ingredient_id, person_id=person.id, meal_id=meal_id)
validate_request(item)
if meal is not None and await is_requested(conn, meal):
2025-10-18 03:26:42 +00:00
raise ValueError("Meal is already requested")
2025-10-18 03:26:42 +00:00
async with conn.execute(
"""
INSERT INTO ShoppingListItem (ingredient_id, person_id, meal_id, created_date)
VALUES (?, ?, ?, ?)
2025-10-18 03:26:42 +00:00
""",
(item.ingredient_id, item.person_id, item.meal_id, item.created_date.isoformat()),
) as cursor:
item.id = cursor.lastrowid
return item
2024-05-20 10:09:57 +00:00
2025-10-18 03:26:42 +00:00
async def remove_request(
conn,
Squashed commit of the following: commit fcd005b8624023547f28b7b28e59e6099bcfc7d4 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 20:24:07 2025 +1100 Openapi tightening commit f93bd8f641d561052c7bd075bae321b4ff3b676d Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 19:03:52 2025 +1100 Removed refactor strategy doc commit 0c5a61092f522be0c47cbbe86917c8a7e4e2d339 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 17:48:33 2025 +1100 mypy & ruff checks commit 23d66d6b18984127e17c73c3063f6120385935e9 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 16:49:35 2025 +1100 Final removal of db.py files commit f454aed1ca9783cc558cc203f29a7fe31b62a975 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:42:31 2025 +1100 Finalise restructure, remove db.py files commit 7187f6dd89489521538791c6bdebb426514beb99 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:34:54 2025 +1100 commit 6fea227ae20d32b8eb1e7a4885006a620fcc7bb1 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:32:53 2025 +1100 commit 27415e7e02d89195ad514cb017a9dbbf84d7a5e4 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:31:10 2025 +1100 commit b773428033d855f9ad82005602e049c1a2e3c585 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:28:58 2025 +1100 commit 116592c95278d995f4c516e87f2cea43cf5b7735 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:25:21 2025 +1100 commit 03ec565faea088971968ee2f9bb83e2de16b21f3 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:21:29 2025 +1100 Plan
2025-10-19 09:24:23 +00:00
person: Optional[Any] = None,
meal: Optional[Any] = None,
ingredient: Optional[Any] = None,
2025-10-18 03:26:42 +00:00
) -> bool:
if meal is not None:
2025-10-18 03:26:42 +00:00
async with conn.execute(
"""
DELETE FROM ShoppingListItem
WHERE list_id IS NULL AND meal_id = ?
2025-10-18 03:26:42 +00:00
""",
(meal.id,),
) as cursor:
return cursor.rowcount > 0
elif ingredient is not None:
2025-10-18 03:26:42 +00:00
async with conn.execute(
"""
DELETE FROM ShoppingListItem
WHERE list_id IS NULL AND ingredient_id = ? AND person_id = ?
2025-10-18 03:26:42 +00:00
""",
(ingredient.id, person.id if person else -1),
) as cursor:
return cursor.rowcount > 0
2025-10-18 03:26:42 +00:00
raise ValueError("Must specify either a meal or an ingredient to remove")
async def find_items_by_list_id(conn, list_id: Optional[int]) -> AsyncIterator[ShoppingListItem]:
2025-10-18 03:26:42 +00:00
request_cols = [f"shoppinglistitem.{key}" for key in ShoppingListItem.KEYS]
2024-05-17 09:09:03 +00:00
2025-10-18 03:26:42 +00:00
select = f"""
SELECT {",".join(request_cols)}
FROM ShoppingListItem
2025-10-18 03:26:42 +00:00
"""
where: str
params: tuple[Any, ...]
where, params = (" WHERE list_id IS NULL", ())
if list_id is not None:
2025-10-18 03:26:42 +00:00
where, params = " WHERE list_id = ?", (list_id,)
cursor = await conn.execute(select + where, params)
2025-10-18 03:26:42 +00:00
2024-05-17 09:09:03 +00:00
async for row in cursor:
2025-10-18 03:26:42 +00:00
request_map = {k: v for k, v in zip(ShoppingListItem.KEYS, row)}
request = ShoppingListItem(**request_map)
2024-05-17 09:09:03 +00:00
yield request
2025-10-18 03:26:42 +00:00
async def find_items_by_list_id_scoped(
conn, list_id: Optional[int], household_id: int
) -> AsyncIterator[ShoppingListItem]:
request_cols = [f"shoppinglistitem.{key}" for key in ShoppingListItem.KEYS]
select = f"""
SELECT {",".join(request_cols)}
FROM ShoppingListItem
"""
where: str
params: tuple[Any, ...]
where, params = (" WHERE list_id IS NULL AND household_id = ?", (household_id,))
if list_id is not None:
where, params = (
" WHERE list_id = ? AND household_id = ?",
(list_id, household_id),
)
cursor = await conn.execute(select + where, params)
async for row in cursor:
request_map = {k: v for k, v in zip(ShoppingListItem.KEYS, row)}
request = ShoppingListItem(**request_map)
yield request
2025-10-18 03:26:42 +00:00
async def load_shopping_list(conn, id: int) -> Optional[ShoppingList]:
shopping_list: Optional[ShoppingList] = None
async with conn.execute(
f"""
SELECT {",".join(ShoppingList.KEYS)} FROM ShoppingList
2024-05-17 09:09:03 +00:00
WHERE id = ?
LIMIT 1
2025-10-18 03:26:42 +00:00
""",
(id,),
) as cursor:
2024-05-17 09:09:03 +00:00
async for row in cursor:
2025-10-18 03:26:42 +00:00
shopping_list = ShoppingList(**{k: v for k, v in zip(ShoppingList.KEYS, row)})
2024-05-17 09:09:03 +00:00
break
if shopping_list:
async for item in find_items_by_list_id(conn, shopping_list.id):
shopping_list.items.append(item)
2024-05-17 09:09:03 +00:00
return shopping_list
2025-10-18 03:26:42 +00:00
async def load_shopping_list_scoped(
conn, id: int, household_id: int
) -> Optional[ShoppingList]:
shopping_list: Optional[ShoppingList] = None
async with conn.execute(
f"""
SELECT {",".join(ShoppingList.KEYS)} FROM ShoppingList
WHERE id = ? AND household_id = ?
LIMIT 1
""",
(id, household_id),
) as cursor:
async for row in cursor:
shopping_list = ShoppingList(**{k: v for k, v in zip(ShoppingList.KEYS, row)})
break
if shopping_list:
async for item in find_items_by_list_id_scoped(conn, shopping_list.id, household_id):
shopping_list.items.append(item)
return shopping_list
async def get_purchased_ingredients(conn, meal_ids: List[int]) -> AsyncIterator[ShoppingListItem]:
if not meal_ids:
return
2025-10-18 03:26:42 +00:00
async with conn.execute(
f"""
SELECT {",".join(ShoppingListItem.KEYS)}
FROM ShoppingListItem
WHERE meal_id IN ({",".join(["?"] * len(meal_ids))}) AND list_id IS NOT NULL
2025-10-18 03:26:42 +00:00
""",
meal_ids,
) as cursor:
2024-05-19 03:43:06 +00:00
async for row in cursor:
2025-10-18 03:26:42 +00:00
yield ShoppingListItem(**{k: v for k, v in zip(ShoppingListItem.KEYS, row)})
async def get_purchased_ingredients_scoped(
conn, meal_ids: List[int], household_id: int
) -> AsyncIterator[ShoppingListItem]:
if not meal_ids:
return
async with conn.execute(
f"""
SELECT {",".join(ShoppingListItem.KEYS)}
FROM ShoppingListItem
WHERE meal_id IN ({",".join(["?"] * len(meal_ids))}) AND list_id IS NOT NULL AND household_id = ?
""",
(*meal_ids, household_id),
) as cursor:
async for row in cursor:
yield ShoppingListItem(**{k: v for k, v in zip(ShoppingListItem.KEYS, row)})