Load meal by ID
This commit is contained in:
parent
33169c69ef
commit
a137199400
4 changed files with 71 additions and 10 deletions
15
main.py
15
main.py
|
|
@ -117,8 +117,21 @@ async def get_meals(start: datetime.datetime, end: datetime.datetime, conn: sqli
|
||||||
|
|
||||||
return result
|
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/")
|
@app.post("/meals/")
|
||||||
async def create_meal(meal: meals.Meal, conn: sqlite3.Connection = Depends(get_db)) -> meals.Meal:
|
async def create_meal(meal: meals.Meal, conn: sqlite3.Connection = Depends(get_db)) -> meals.Meal:
|
||||||
|
"""
|
||||||
if not meal.chef:
|
if not meal.chef:
|
||||||
return JSONResponse(status_code=400, content={'message': 'Meal must have at least one 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:
|
if not meal.consumers:
|
||||||
return JSONResponse(status_code=400, content={'message': 'Meal must have at least one consumer'})
|
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:
|
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'})
|
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()
|
await conn.commit()
|
||||||
return meal
|
return meal
|
||||||
|
|
||||||
|
|
||||||
@app.get("/persons/")
|
@app.get("/persons/")
|
||||||
async def get_persons(conn: sqlite3.Connection = Depends(get_db)) -> List[meals.Person]:
|
async def get_persons(conn: sqlite3.Connection = Depends(get_db)) -> List[meals.Person]:
|
||||||
result = []
|
result = []
|
||||||
|
|
|
||||||
42
meals/db.py
42
meals/db.py
|
|
@ -1,7 +1,9 @@
|
||||||
from typing import List, ClassVar
|
from typing import List, ClassVar
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from persons import Person
|
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
|
import datetime
|
||||||
|
|
||||||
|
|
@ -45,6 +47,12 @@ 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 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 def insert_meal(conn, meal: Meal):
|
||||||
async with conn.execute('''
|
async with conn.execute('''
|
||||||
INSERT INTO Meal (date)
|
INSERT INTO Meal (date)
|
||||||
|
|
@ -61,6 +69,13 @@ async def insert_meal(conn, meal: Meal):
|
||||||
for person in meal.consumers:
|
for person in meal.consumers:
|
||||||
await insert_meal_participant(conn, meal.id, person.id, 'consumer')
|
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 def find_meal_by_id(conn, meal_id: int) -> Meal:
|
||||||
async with conn.execute(f'''
|
async with conn.execute(f'''
|
||||||
SELECT {','.join(Meal.KEYS)} FROM Meal
|
SELECT {','.join(Meal.KEYS)} FROM Meal
|
||||||
|
|
@ -103,4 +118,27 @@ async def load_participants(conn, meal: Meal) -> None:
|
||||||
elif row[1] == 'consumer':
|
elif row[1] == 'consumer':
|
||||||
meal.consumers.append(person)
|
meal.consumers.append(person)
|
||||||
else:
|
else:
|
||||||
raise Exception(f'Unknown role: {row[1]}')
|
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)
|
||||||
|
|
@ -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 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
|
from ingredients import parse_ingredient_from_nlp as _parse_ingredient_from_nlp, match_existing_products as _match_existing_products
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ import json
|
||||||
|
|
||||||
from ingredients import Ingredient
|
from ingredients import Ingredient
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel, Field
|
||||||
from typing import List, ClassVar
|
from typing import List, ClassVar, Tuple
|
||||||
|
|
||||||
class Recipe(BaseModel):
|
class Recipe(BaseModel):
|
||||||
KEYS: ClassVar[List[str]] = ['id', 'name', 'link', 'image_urls', 'raw_data']
|
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.name, recipe.link, recipe.raw_data, json.dumps(recipe.image_urls))) as cursor:
|
||||||
recipe.id = cursor.lastrowid
|
recipe.id = cursor.lastrowid
|
||||||
|
|
||||||
def row_to_recipe(row) -> Recipe:
|
def row_to_recipe(col_tuples: List[Tuple[str, ...]]) -> Recipe:
|
||||||
d = {k:v for k,v in zip(Recipe.KEYS, row)}
|
d = {k:v for k,v in col_tuples}
|
||||||
d['image_urls'] = json.loads(d['image_urls'])
|
d['image_urls'] = json.loads(d['image_urls'])
|
||||||
return Recipe(**d)
|
return Recipe(**d)
|
||||||
|
|
||||||
|
|
@ -43,7 +43,7 @@ async def find_recipe_by_id(conn, recipe_id: int) -> Recipe:
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
''', (recipe_id,)) as cursor:
|
''', (recipe_id,)) as cursor:
|
||||||
async for row in 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 def find_recipes_by_name(conn, name: str) -> List[Recipe]:
|
||||||
async with conn.execute(f'''
|
async with conn.execute(f'''
|
||||||
|
|
@ -51,11 +51,19 @@ async def find_recipes_by_name(conn, name: str) -> List[Recipe]:
|
||||||
WHERE name LIKE ?
|
WHERE name LIKE ?
|
||||||
''', (f'%{name}%',)) as cursor:
|
''', (f'%{name}%',)) as cursor:
|
||||||
async for row in 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 def get_all(conn) -> List[Recipe]:
|
||||||
async with conn.execute(f'''
|
async with conn.execute(f'''
|
||||||
SELECT {','.join(Recipe.KEYS)} FROM Recipe
|
SELECT {','.join(Recipe.KEYS)} FROM Recipe
|
||||||
''') as cursor:
|
''') as cursor:
|
||||||
async for row in cursor:
|
async for row in cursor:
|
||||||
yield row_to_recipe(row)
|
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)}))
|
||||||
Loading…
Reference in a new issue