diff --git a/main.py b/main.py index 1fbe973..3d84900 100644 --- a/main.py +++ b/main.py @@ -117,8 +117,21 @@ async def get_meals(start: datetime.datetime, end: datetime.datetime, conn: sqli return result +@app.get("/meals/{meal_id}") +async def get_meal(meal_id: int, request: Request, conn: sqlite3.Connection = Depends(get_db)) -> meals.Meal: + meal = await meals.find_meal_by_id(conn, meal_id) + if not meal: + return JSONResponse(status_code=404, content={'message': 'Meal not found'}) + + await meals.load_participants(conn, meal) + await meals.load_recipes(conn, meal) + await meals.load_extra_ingredients(conn, meal) + + return meal + @app.post("/meals/") async def create_meal(meal: meals.Meal, conn: sqlite3.Connection = Depends(get_db)) -> meals.Meal: + """ if not meal.chef: return JSONResponse(status_code=400, content={'message': 'Meal must have at least one chef'}) @@ -127,6 +140,7 @@ async def create_meal(meal: meals.Meal, conn: sqlite3.Connection = Depends(get_d if not meal.consumers: return JSONResponse(status_code=400, content={'message': 'Meal must have at least one consumer'}) + """ if len(meal.recipes) == 0 and len(meal.extra_ingredients) == 0: return JSONResponse(status_code=400, content={'message': 'Meal must have at least one recipe or ingredient'}) @@ -135,6 +149,7 @@ async def create_meal(meal: meals.Meal, conn: sqlite3.Connection = Depends(get_d await conn.commit() return meal + @app.get("/persons/") async def get_persons(conn: sqlite3.Connection = Depends(get_db)) -> List[meals.Person]: result = [] diff --git a/meals/db.py b/meals/db.py index b63fa70..becd1ce 100644 --- a/meals/db.py +++ b/meals/db.py @@ -1,7 +1,9 @@ from typing import List, ClassVar from pydantic import BaseModel from persons import Person -from ingredients import Ingredient +from ingredients import Ingredient, insert_ingredient +from products import Product +from recipes import Recipe, row_to_recipe import datetime @@ -45,6 +47,12 @@ async def insert_meal_participant(conn, meal_id: int, person_id: int, role: str) VALUES (?, ?, ?) ''', (meal_id, person_id, role)) +async def insert_meal_recipe(conn, meal_id: int, recipe_id: int): + await conn.execute(''' + INSERT INTO MealRecipe (meal_id, recipe_id) + VALUES (?, ?) + ''', (meal_id, recipe_id)) + async def insert_meal(conn, meal: Meal): async with conn.execute(''' INSERT INTO Meal (date) @@ -61,6 +69,13 @@ async def insert_meal(conn, meal: Meal): for person in meal.consumers: await insert_meal_participant(conn, meal.id, person.id, '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) + async def find_meal_by_id(conn, meal_id: int) -> Meal: async with conn.execute(f''' SELECT {','.join(Meal.KEYS)} FROM Meal @@ -103,4 +118,27 @@ async def load_participants(conn, meal: Meal) -> None: elif row[1] == 'consumer': meal.consumers.append(person) else: - raise Exception(f'Unknown role: {row[1]}') \ No newline at end of file + raise Exception(f'Unknown role: {row[1]}') + +async def load_recipes(conn, meal: Meal) -> None: + async with conn.execute(f''' + SELECT {','.join(Recipe.KEYS)} FROM Recipe + JOIN MealRecipe ON MealRecipe.recipe_id = Recipe.id + WHERE MealRecipe.meal_id = ? + ''', (meal.id,)) as cursor: + async for row in cursor: + meal.recipes.append(row_to_recipe(zip(Recipe.KEYS, row))) + +async def load_extra_ingredients(conn, meal: Meal) -> None: + ingredient_cols = [f'Ingredient.{k}' for k in Ingredient.KEYS] + product_cols = [f'Product.{k}' for k in Product.KEYS] + + async with conn.execute(f''' + SELECT {','.join(ingredient_cols + product_cols)} FROM Ingredient + JOIN Product ON Ingredient.product_id = Product.id + WHERE meal_id = ? + ''', (meal.id,)) as cursor: + async for row in cursor: + ingredient = Ingredient(**{k:v for k,v in zip(Ingredient.KEYS, row[:len(Ingredient.KEYS)])}) + ingredient.product = Product(**{k:v for k,v in zip(Product.KEYS, row[len(Ingredient.KEYS):])}) + meal.extra_ingredients.append(ingredient) \ No newline at end of file diff --git a/recipes/__init__.py b/recipes/__init__.py index ba5ee32..246f976 100644 --- a/recipes/__init__.py +++ b/recipes/__init__.py @@ -1,4 +1,4 @@ -from recipes.db import Recipe, insert_recipe, find_recipe_by_id, get_all, find_recipes_by_name +from recipes.db import Recipe, insert_recipe, find_recipe_by_id, get_all, find_recipes_by_name, row_to_recipe from recipes.scraping import scrape_recipe_ldata as _scrape_recipe_ldata from ingredients import parse_ingredient_from_nlp as _parse_ingredient_from_nlp, match_existing_products as _match_existing_products diff --git a/recipes/db.py b/recipes/db.py index f844045..0ab4791 100644 --- a/recipes/db.py +++ b/recipes/db.py @@ -2,8 +2,8 @@ import json from ingredients import Ingredient -from pydantic import BaseModel -from typing import List, ClassVar +from pydantic import BaseModel, Field +from typing import List, ClassVar, Tuple class Recipe(BaseModel): KEYS: ClassVar[List[str]] = ['id', 'name', 'link', 'image_urls', 'raw_data'] @@ -31,8 +31,8 @@ async def insert_recipe(conn, recipe: Recipe): ''', (recipe.name, recipe.link, recipe.raw_data, json.dumps(recipe.image_urls))) as cursor: recipe.id = cursor.lastrowid -def row_to_recipe(row) -> Recipe: - d = {k:v for k,v in zip(Recipe.KEYS, row)} +def row_to_recipe(col_tuples: List[Tuple[str, ...]]) -> Recipe: + d = {k:v for k,v in col_tuples} d['image_urls'] = json.loads(d['image_urls']) return Recipe(**d) @@ -43,7 +43,7 @@ async def find_recipe_by_id(conn, recipe_id: int) -> Recipe: LIMIT 1 ''', (recipe_id,)) as cursor: async for row in cursor: - return row_to_recipe(row) + return row_to_recipe(zip(Recipe.KEYS, row)) async def find_recipes_by_name(conn, name: str) -> List[Recipe]: async with conn.execute(f''' @@ -51,11 +51,19 @@ async def find_recipes_by_name(conn, name: str) -> List[Recipe]: WHERE name LIKE ? ''', (f'%{name}%',)) as cursor: async for row in cursor: - yield row_to_recipe(row) + yield row_to_recipe(zip(Recipe.KEYS, row)) async def get_all(conn) -> List[Recipe]: async with conn.execute(f''' SELECT {','.join(Recipe.KEYS)} FROM Recipe ''') as cursor: async for row in cursor: - yield row_to_recipe(row) \ No newline at end of file + yield row_to_recipe(zip(Recipe.KEYS, row)) + +async def load_ingredients(conn, recipe: Recipe): + async with conn.execute(f''' + SELECT {','.join(Ingredient.KEYS)} FROM Ingredient + WHERE recipe_id = ? + ''', (recipe.id,)) as cursor: + async for row in cursor: + recipe.ingredients.append(Ingredient(**{k:v for k,v in zip(Ingredient.KEYS, row)})) \ No newline at end of file