Update meal
This commit is contained in:
parent
3ee7511aea
commit
9f04dcaac3
5 changed files with 135 additions and 15 deletions
|
|
@ -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
|
||||
from products import Product, find_product_by_tag
|
||||
|
|
|
|||
|
|
@ -73,4 +73,10 @@ async def find_ingredients_by_meal_id(conn, meal_id: int) -> List[Ingredient]:
|
|||
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
|
||||
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
14
main.py
|
|
@ -160,6 +160,20 @@ async def create_meal(meal: meals.Meal, conn: sqlite3.Connection = Depends(get_d
|
|||
await conn.commit()
|
||||
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}")
|
||||
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)
|
||||
|
|
|
|||
60
meals/db.py
60
meals/db.py
|
|
@ -1,6 +1,6 @@
|
|||
from typing import List, ClassVar, Optional
|
||||
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
|
||||
|
||||
|
|
@ -50,6 +50,15 @@ async def insert_meal_participant(conn, meal_id: int, person_id: int, role: str)
|
|||
VALUES (?, ?, ?)
|
||||
''', (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):
|
||||
if not recipe_id:
|
||||
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.id = cursor.lastrowid
|
||||
|
||||
for person in meal.chefs:
|
||||
await insert_meal_participant(conn, meal.id, person.id, 'chef')
|
||||
|
||||
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')
|
||||
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')
|
||||
|
||||
for recipe in meal.recipes:
|
||||
await insert_meal_recipe(conn, meal.id, recipe.id)
|
||||
|
||||
for ingredient in meal.extra_ingredients:
|
||||
ingredient.meal_id = meal.id
|
||||
await insert_ingredient(conn, ingredient)
|
||||
await sync_extra_ingredients(conn, meal.id, meal.extra_ingredients)
|
||||
|
||||
async def find_meal_by_id(conn, meal_id: int) -> Meal:
|
||||
async with conn.execute(f'''
|
||||
|
|
@ -161,4 +163,36 @@ async def delete_meal(conn, meal_id: int) -> None:
|
|||
await conn.execute('''
|
||||
DELETE FROM Meal
|
||||
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)
|
||||
|
|
|
|||
66
tests.py
66
tests.py
|
|
@ -1,5 +1,6 @@
|
|||
import unittest
|
||||
import test_data
|
||||
import datetime
|
||||
|
||||
import pathlib
|
||||
from db import connect, create
|
||||
|
|
@ -7,6 +8,14 @@ from db import connect, create
|
|||
import products.db as products_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):
|
||||
async def asyncSetUp(self):
|
||||
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].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__':
|
||||
unittest.main()
|
||||
Loading…
Reference in a new issue