Load meal by ID

This commit is contained in:
jableader 2024-01-17 21:39:48 +11:00
parent 33169c69ef
commit a137199400
4 changed files with 71 additions and 10 deletions

15
main.py
View file

@ -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 = []

View file

@ -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)

View file

@ -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

View file

@ -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)}))