munch-ease-backend/meals/db.py

164 lines
5.2 KiB
Python
Raw Normal View History

2024-04-25 04:57:39 +00:00
from typing import List, ClassVar, Optional
2024-01-13 07:18:25 +00:00
from pydantic import BaseModel
from ingredients import Ingredient, insert_ingredient, find_ingredients_by_meal_id
2024-04-25 04:57:39 +00:00
from recipes import Recipe, row_to_recipe, load_recipe_ingredients
import persons
from persons import Person
2024-01-13 07:18:25 +00:00
import datetime
class Meal(BaseModel):
2024-04-25 04:57:39 +00:00
KEYS: ClassVar[List[str]] = ['id', 'meal_date']
2024-01-13 07:18:25 +00:00
id: int
2024-04-25 04:57:39 +00:00
meal_date: datetime.datetime
2024-01-17 11:43:16 +00:00
chefs: List[Person] = []
2024-01-13 07:18:25 +00:00
cleanup: List[Person] = []
consumers: List[Person] = []
2024-01-17 11:09:55 +00:00
recipes: List[Recipe] = []
2024-01-17 07:21:16 +00:00
extra_ingredients: List[Ingredient] = []
2024-01-13 07:18:25 +00:00
async def create(conn):
await conn.execute('''
CREATE TABLE IF NOT EXISTS Meal (
id INTEGER PRIMARY KEY,
2024-04-25 04:57:39 +00:00
meal_date TEXT
2024-01-13 07:18:25 +00:00
);''')
await conn.execute('''
CREATE TABLE IF NOT EXISTS MealParticipant (
meal_id INTEGER,
person_id INTEGER,
role TEXT,
FOREIGN KEY(meal_id) REFERENCES Meal(id),
FOREIGN KEY(person_id) REFERENCES Person(id)
);''')
await conn.execute('''
CREATE TABLE IF NOT EXISTS MealRecipe (
meal_id INTEGER,
recipe_id INTEGER,
FOREIGN KEY(meal_id) REFERENCES Meal(id),
FOREIGN KEY(recipe_id) REFERENCES Recipe(id)
);''')
2024-01-13 07:42:23 +00:00
async def insert_meal_participant(conn, meal_id: int, person_id: int, role: str):
await conn.execute('''
INSERT INTO MealParticipant (meal_id, person_id, role)
VALUES (?, ?, ?)
''', (meal_id, person_id, role))
2024-01-17 10:39:48 +00:00
async def insert_meal_recipe(conn, meal_id: int, recipe_id: int):
2024-04-25 04:57:39 +00:00
if not recipe_id:
raise ValueError('Recipe must be inserted before meal')
2024-01-17 10:39:48 +00:00
await conn.execute('''
INSERT INTO MealRecipe (meal_id, recipe_id)
VALUES (?, ?)
''', (meal_id, recipe_id))
2024-01-13 07:18:25 +00:00
async def insert_meal(conn, meal: Meal):
async with conn.execute('''
2024-04-25 04:57:39 +00:00
INSERT INTO Meal (meal_date)
2024-01-13 07:18:25 +00:00
VALUES (?)
2024-04-25 04:57:39 +00:00
''', (meal.meal_date,)) as cursor:
2024-01-13 07:42:23 +00:00
meal.id = cursor.lastrowid
2024-01-17 11:43:16 +00:00
for person in meal.chefs:
2024-01-13 07:42:23 +00:00
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')
2024-01-13 07:18:25 +00:00
2024-01-17 10:39:48 +00:00
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)
2024-01-13 07:18:25 +00:00
async def find_meal_by_id(conn, meal_id: int) -> Meal:
async with conn.execute(f'''
SELECT {','.join(Meal.KEYS)} FROM Meal
WHERE id = ?
LIMIT 1
''', (meal_id,)) as cursor:
async for row in cursor:
return Meal(**{k:v for k,v in zip(Meal.KEYS, row)})
async def find_meal_by_date(conn, date: datetime) -> Meal:
async with conn.execute(f'''
SELECT {','.join(Meal.KEYS)} FROM Meal
2024-04-28 02:15:19 +00:00
WHERE meal_date = ?
2024-01-13 07:18:25 +00:00
LIMIT 1
''', (date,)) as cursor:
async for row in cursor:
2024-01-13 07:42:23 +00:00
return Meal(**{k:v for k,v in zip(Meal.KEYS, row)})
async def find_meals_by_date_range(conn, start: datetime, end: datetime) -> List[Meal]:
async with conn.execute(f'''
SELECT {','.join(Meal.KEYS)} FROM Meal
2024-04-28 02:15:19 +00:00
WHERE meal_date >= ? AND meal_date <= ?
2024-01-13 07:42:23 +00:00
''', (start, end)) as cursor:
result = []
async for row in cursor:
result.append(Meal(**{k:v for k,v in zip(Meal.KEYS, row)}))
return result
async def load_participants(conn, meal: Meal) -> None:
async with conn.execute(f'''
SELECT person_id, role FROM MealParticipant
WHERE meal_id = ?
''', (meal.id,)) as cursor:
async for row in cursor:
2024-04-25 04:57:39 +00:00
person = await persons.get_by_id(conn, row[0])
2024-01-13 07:42:23 +00:00
if row[1] == 'chef':
2024-01-17 11:43:16 +00:00
meal.chefs.append(person)
2024-01-13 07:42:23 +00:00
elif row[1] == 'cleanup':
meal.cleanup.append(person)
elif row[1] == 'consumer':
meal.consumers.append(person)
else:
2024-01-17 10:39:48 +00:00
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:
2024-04-25 04:57:39 +00:00
recipe = row_to_recipe(zip(Recipe.KEYS, row))
await load_recipe_ingredients(conn, recipe)
meal.recipes.append(recipe)
2024-01-17 10:39:48 +00:00
async def load_extra_ingredients(conn, meal: Meal) -> None:
async for ingredient in find_ingredients_by_meal_id(conn, meal.id):
meal.extra_ingredients.append(ingredient)
2024-01-17 11:43:16 +00:00
async def delete_meal(conn, meal_id: int) -> None:
await conn.execute('''
DELETE FROM MealParticipant
WHERE meal_id = ?
''', (meal_id,))
await conn.execute('''
DELETE FROM MealRecipe
WHERE meal_id = ?
''', (meal_id,))
await conn.execute('''
DELETE FROM Ingredient
WHERE meal_id = ?
''', (meal_id,))
await conn.execute('''
DELETE FROM Meal
WHERE id = ?
''', (meal_id,))