Update meal

This commit is contained in:
jableader 2024-05-02 21:20:52 +10:00
parent 3ee7511aea
commit 9f04dcaac3
5 changed files with 135 additions and 15 deletions

View file

@ -1,4 +1,4 @@
from ingredients.db import Ingredient, find_ingredients_by_meal_id, find_ingredients_by_recipe_id, insert_ingredient from ingredients.db import Ingredient, find_ingredients_by_meal_id, find_ingredients_by_recipe_id, insert_ingredient, delete_ingredients_by_meal_id
import units import units
from products import Product, find_product_by_tag from products import Product, find_product_by_tag

View file

@ -74,3 +74,9 @@ async def find_ingredients_by_meal_id(conn, meal_id: int) -> List[Ingredient]:
product_keys = {k:v for k,v in zip(Product.KEYS, row[len(Ingredient.KEYS):])} 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 product = Product(**product_keys) if product_keys['id'] else None
yield Ingredient(**{k:v for k,v in zip(Ingredient.KEYS, row[:len(Ingredient.KEYS)])}, product=product) yield Ingredient(**{k:v for k,v in zip(Ingredient.KEYS, row[:len(Ingredient.KEYS)])}, product=product)
async def delete_ingredients_by_meal_id(conn, meal_id: int):
await conn.execute('''
DELETE FROM Ingredient
WHERE meal_id = ?
''', (meal_id,))

14
main.py
View file

@ -160,6 +160,20 @@ async def create_meal(meal: meals.Meal, conn: sqlite3.Connection = Depends(get_d
await conn.commit() await conn.commit()
return meal return meal
@app.put("/meals/{meal_id}")
async def update_meal(meal_id: int, meal: meals.Meal, conn: sqlite3.Connection = Depends(get_db)) -> meals.Meal:
if meal.id != meal_id:
return JSONResponse(status_code=400, content={'message': 'Meal ID in URL does not match meal ID in body'})
existing = await meals.find_meal_by_id(conn, meal_id)
if not existing:
return JSONResponse(status_code=404, content={'message': 'Meal not found'})
await meals.update_meal(conn, meal)
await conn.commit()
return await get_meal(meal_id, conn)
@app.delete("/meals/{meal_id}") @app.delete("/meals/{meal_id}")
async def delete_meal(meal_id: int, conn: sqlite3.Connection = Depends(get_db)) -> meals.Meal: async def delete_meal(meal_id: int, conn: sqlite3.Connection = Depends(get_db)) -> meals.Meal:
meal = await meals.find_meal_by_id(conn, meal_id) meal = await meals.find_meal_by_id(conn, meal_id)

View file

@ -1,6 +1,6 @@
from typing import List, ClassVar, Optional from typing import List, ClassVar, Optional
from pydantic import BaseModel from pydantic import BaseModel
from ingredients import Ingredient, insert_ingredient, find_ingredients_by_meal_id from ingredients import Ingredient, insert_ingredient, find_ingredients_by_meal_id, delete_ingredients_by_meal_id
from recipes import Recipe, row_to_recipe, load_recipe_ingredients from recipes import Recipe, row_to_recipe, load_recipe_ingredients
@ -50,6 +50,15 @@ async def insert_meal_participant(conn, meal_id: int, person_id: int, role: str)
VALUES (?, ?, ?) VALUES (?, ?, ?)
''', (meal_id, person_id, role)) ''', (meal_id, person_id, role))
async def sync_meal_participants(conn, meal_id: int, participants: List[Person], role: str):
await conn.execute('''
DELETE FROM MealParticipant
WHERE meal_id = ? AND role = ?
''', (meal_id, role))
for person in participants:
await insert_meal_participant(conn, meal_id, person.id, role)
async def insert_meal_recipe(conn, meal_id: int, recipe_id: int): async def insert_meal_recipe(conn, meal_id: int, recipe_id: int):
if not recipe_id: if not recipe_id:
raise ValueError('Recipe must be inserted before meal') raise ValueError('Recipe must be inserted before meal')
@ -66,21 +75,14 @@ async def insert_meal(conn, meal: Meal):
''', (meal.meal_date,)) as cursor: ''', (meal.meal_date,)) as cursor:
meal.id = cursor.lastrowid meal.id = cursor.lastrowid
for person in meal.chefs: await sync_meal_participants(conn, meal.id, meal.chefs, 'chef')
await insert_meal_participant(conn, meal.id, person.id, 'chef') await sync_meal_participants(conn, meal.id, meal.cleanup, 'cleanup')
await sync_meal_participants(conn, meal.id, meal.consumers, 'consumer')
for person in meal.cleanup:
await insert_meal_participant(conn, meal.id, person.id, 'cleanup')
for person in meal.consumers:
await insert_meal_participant(conn, meal.id, person.id, 'consumer')
for recipe in meal.recipes: for recipe in meal.recipes:
await insert_meal_recipe(conn, meal.id, recipe.id) await insert_meal_recipe(conn, meal.id, recipe.id)
for ingredient in meal.extra_ingredients: await sync_extra_ingredients(conn, meal.id, meal.extra_ingredients)
ingredient.meal_id = meal.id
await insert_ingredient(conn, ingredient)
async def find_meal_by_id(conn, meal_id: int) -> Meal: async def find_meal_by_id(conn, meal_id: int) -> Meal:
async with conn.execute(f''' async with conn.execute(f'''
@ -162,3 +164,35 @@ async def delete_meal(conn, meal_id: int) -> None:
DELETE FROM Meal DELETE FROM Meal
WHERE id = ? WHERE id = ?
''', (meal_id,)) ''', (meal_id,))
async def sync_extra_ingredients(conn, meal_id: int, ingredients: List[Ingredient]) -> None:
await delete_ingredients_by_meal_id(conn, meal_id)
for ingredient in ingredients:
ingredient.meal_id = meal_id
ingredient.recipe_id = None
await insert_ingredient(conn, ingredient)
async def sync_recipes(conn, meal_id: int, recipes: List[Recipe]) -> None:
await conn.execute('''
DELETE FROM MealRecipe
WHERE meal_id = ?
''', (meal_id,))
for recipe in recipes:
await insert_meal_recipe(conn, meal_id, recipe.id)
async def update_meal(conn, meal: Meal) -> None:
await conn.execute('''
UPDATE Meal
SET meal_date = ?
WHERE id = ?
''', (meal.meal_date, meal.id))
await sync_meal_participants(conn, meal.id, meal.chefs, 'chef')
await sync_meal_participants(conn, meal.id, meal.cleanup, 'cleanup')
await sync_meal_participants(conn, meal.id, meal.consumers, 'consumer')
await sync_extra_ingredients(conn, meal.id, meal.extra_ingredients)
await sync_recipes(conn, meal.id, meal.recipes)

View file

@ -1,5 +1,6 @@
import unittest import unittest
import test_data import test_data
import datetime
import pathlib import pathlib
from db import connect, create from db import connect, create
@ -7,6 +8,14 @@ from db import connect, create
import products.db as products_db import products.db as products_db
import recipes.db as recipes_db import recipes.db as recipes_db
def unique(lst: list, key: callable):
seen = set()
for item in lst:
k = key(item)
if k not in seen:
seen.add(k)
yield item
class TestProducts(unittest.IsolatedAsyncioTestCase): class TestProducts(unittest.IsolatedAsyncioTestCase):
async def asyncSetUp(self): async def asyncSetUp(self):
self.conn = await connect('./testdb.db') self.conn = await connect('./testdb.db')
@ -162,5 +171,62 @@ class TestMeals(unittest.IsolatedAsyncioTestCase):
self.assertEqual(meals_by_date_range[0].recipes[0].id, recipe.id) self.assertEqual(meals_by_date_range[0].recipes[0].id, recipe.id)
self.assertEqual(meals_by_date_range[0].recipes[0].name, recipe.name) self.assertEqual(meals_by_date_range[0].recipes[0].name, recipe.name)
async def testUpdate(self) -> None:
meal = test_data.Meals.broccoli_soup_for_jacob
person = test_data.Persons.jacob
recipes = meal.recipes + [test_data.Recipes.how_to_steam_green_beans]
ingredients = meal.extra_ingredients + [ingredient for recipe in recipes for ingredient in recipe.ingredients] + [test_data.Ingredients.butter]
products = [ingredient.product for ingredient in ingredients]
for product in unique(products, lambda p: p.name):
await products_db.insert_product(self.conn, product, {})
for recipe in recipes:
created_recipe = await main.create_recipe(recipe, self.conn, person)
recipe.id = created_recipe.id
create_response = await main.create_meal(meal, self.conn)
self.assertIsInstance(create_response, meals.Meal, msg=create_response.body if hasattr(create_response, 'body') else create_response)
saved_meal = await main.get_meal(meal.id, self.conn)
self.assertIsNotNone(saved_meal)
self.assertIsInstance(saved_meal, meals.Meal, msg=saved_meal.body if hasattr(saved_meal, 'body') else saved_meal)
saved_meal.meal_date = datetime.datetime.fromisoformat('2021-01-01T12:00:00')
saved_meal.recipes = [test_data.Recipes.how_to_steam_green_beans]
saved_meal.extra_ingredients.append(test_data.Ingredients.butter)
saved_meal.chefs.append(test_data.Persons.ryan)
saved_meal.cleanup = [test_data.Persons.chris]
saved_meal.consumers = [test_data.Persons.ellie, test_data.Persons.jacob, test_data.Persons.ryan]
updated_meal = await main.update_meal(saved_meal.id, saved_meal, self.conn)
self.assertIsNotNone(updated_meal)
self.assertIsInstance(updated_meal, meals.Meal, msg=updated_meal.body if hasattr(updated_meal, 'body') else updated_meal)
self.assertEqual(updated_meal.id, saved_meal.id)
self.assertEqual(updated_meal.meal_date, saved_meal.meal_date)
self.assertPersons(updated_meal.chefs, saved_meal.chefs)
self.assertPersons(updated_meal.cleanup, saved_meal.cleanup)
self.assertPersons(updated_meal.consumers, saved_meal.consumers)
self.assertEqual(len(updated_meal.recipes), 1)
self.assertEqual(updated_meal.recipes[0].id, test_data.Recipes.how_to_steam_green_beans.id)
self.assertEqual(updated_meal.recipes[0].name, test_data.Recipes.how_to_steam_green_beans.name)
self.assertEqual(updated_meal.recipes[0].link, test_data.Recipes.how_to_steam_green_beans.link)
self.assertEqual(updated_meal.recipes[0].image_urls, test_data.Recipes.how_to_steam_green_beans.image_urls)
expected = [i.name for i in test_data.Recipes.how_to_steam_green_beans.ingredients]
actual = [i.name for i in updated_meal.recipes[0].ingredients]
self.assertEqual(sorted(expected), sorted(actual))
expected = [i.name for i in saved_meal.extra_ingredients]
actual = [i.name for i in updated_meal.extra_ingredients]
self.assertEqual(sorted(expected), sorted(actual))
def assertPersons(self, expected, actual):
expected_names = [p.name for p in expected]
actual_names = [p.name for p in actual]
self.assertEqual(sorted(expected_names), sorted(actual_names))
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()