Compare commits
No commits in common. "407d649d993858e031b4a6aa2196832e6b8ed80f" and "7882b16bd412dfbb9df844d6508536a7b420dedc" have entirely different histories.
407d649d99
...
7882b16bd4
6 changed files with 23 additions and 88 deletions
18
main.py
18
main.py
|
|
@ -3,7 +3,7 @@ import products, recipes, db, meals, persons, ingredients, shopping
|
|||
import datetime
|
||||
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Annotated, Optional, Union
|
||||
from typing import List, Annotated, Union
|
||||
from fastapi import FastAPI, Depends, Query, Cookie
|
||||
from fastapi.responses import JSONResponse
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
|
|
@ -146,10 +146,10 @@ async def delete_recipe(recipe_id: int, conn: sqlite3.Connection = Depends(get_d
|
|||
await conn.commit()
|
||||
return recipe
|
||||
|
||||
@app.get("/meals/upcoming")
|
||||
async def get_upcoming_meals(date_from: Annotated[datetime.datetime, Query(alias='from')], to: datetime.datetime, conn: sqlite3.Connection = Depends(get_db)) -> List[meals.Meal]:
|
||||
@app.get("/meals/")
|
||||
async def get_meals(date_from: Annotated[datetime.datetime, Query(alias='from')], to: datetime.datetime, conn: sqlite3.Connection = Depends(get_db)) -> List[meals.Meal]:
|
||||
result = []
|
||||
async for meal in meals.find_upcoming_meals_by_date_range(conn, date_from, to):
|
||||
async for meal in meals.find_meals_by_date_range(conn, date_from, to):
|
||||
await meals.load_recipes(conn, meal)
|
||||
await meals.load_extra_ingredients(conn, meal)
|
||||
await meals.load_participants(conn, meal)
|
||||
|
|
@ -229,16 +229,6 @@ async def update_meal(meal_id: int, meal: meals.Meal, conn: sqlite3.Connection =
|
|||
|
||||
return await get_meal(meal_id, conn)
|
||||
|
||||
@app.post("/meals/{meal_id}/consumed")
|
||||
async def mark_consumed(meal_id: int, consumed_date: Optional[datetime.datetime] = None, conn: sqlite3.Connection = Depends(get_db), person: persons.Person = Depends(cookie_person)) -> 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.mark_consumed(conn, meal, consumed_date or datetime.datetime.now())
|
||||
await conn.commit()
|
||||
return meal
|
||||
|
||||
@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)
|
||||
|
|
|
|||
33
meals/db.py
33
meals/db.py
|
|
@ -10,10 +10,10 @@ from persons import Person
|
|||
import datetime
|
||||
|
||||
class Meal(BaseModel):
|
||||
KEYS: ClassVar[List[str]] = ['id', 'suggested_date', 'consumed_date']
|
||||
KEYS: ClassVar[List[str]] = ['id', 'meal_date']
|
||||
id: int = -1
|
||||
suggested_date: datetime.datetime
|
||||
consumed_date: Optional[datetime.datetime] = None
|
||||
meal_date: datetime.datetime
|
||||
purchase_date: Optional[datetime.datetime] = None
|
||||
|
||||
chefs: List[Person] = []
|
||||
cleanup: List[Person] = []
|
||||
|
|
@ -21,15 +21,11 @@ class Meal(BaseModel):
|
|||
recipes: List[Recipe] = []
|
||||
extra_ingredients: List[Ingredient] = []
|
||||
|
||||
# Set from shopping list
|
||||
purchase_date: Optional[datetime.datetime] = None
|
||||
|
||||
async def create(conn):
|
||||
await conn.execute('''
|
||||
CREATE TABLE IF NOT EXISTS Meal (
|
||||
id INTEGER PRIMARY KEY,
|
||||
suggested_date TEXT,
|
||||
consumed_date TEXT
|
||||
meal_date TEXT
|
||||
);''')
|
||||
|
||||
await conn.execute('''
|
||||
|
|
@ -75,9 +71,9 @@ async def insert_meal_recipe(conn, meal_id: int, recipe_id: int):
|
|||
|
||||
async def insert_meal(conn, meal: Meal):
|
||||
async with conn.execute('''
|
||||
INSERT INTO Meal (suggested_date)
|
||||
INSERT INTO Meal (meal_date)
|
||||
VALUES (?)
|
||||
''', (meal.suggested_date,)) as cursor:
|
||||
''', (meal.meal_date,)) as cursor:
|
||||
meal.id = cursor.lastrowid
|
||||
|
||||
await sync_meal_participants(conn, meal.id, meal.chefs, 'chef')
|
||||
|
|
@ -103,10 +99,10 @@ async def find_meal_by_id(conn, meal_id: int) -> Meal:
|
|||
await load_extra_ingredients(conn, meal)
|
||||
return meal
|
||||
|
||||
async def find_upcoming_meals_by_date_range(conn, start: datetime, end: datetime) -> AsyncIterator[Meal]:
|
||||
async def find_meals_by_date_range(conn, start: datetime, end: datetime) -> AsyncIterator[Meal]:
|
||||
async with conn.execute(f'''
|
||||
SELECT {','.join(Meal.KEYS)} FROM Meal
|
||||
WHERE suggested_date >= ? AND suggested_date <= ? AND consumed_date IS NULL
|
||||
WHERE meal_date >= ? AND meal_date <= ?
|
||||
''', (start, end)) as cursor:
|
||||
async for row in cursor:
|
||||
yield await with_purchase_date(conn, Meal(**{k:v for k,v in zip(Meal.KEYS, row)}))
|
||||
|
|
@ -185,9 +181,9 @@ async def sync_recipes(conn, meal_id: int, recipes: List[Recipe]) -> None:
|
|||
async def update_meal(conn, meal: Meal) -> None:
|
||||
await conn.execute('''
|
||||
UPDATE Meal
|
||||
SET suggested_date = ?
|
||||
SET meal_date = ?
|
||||
WHERE id = ?
|
||||
''', (meal.suggested_date, meal.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')
|
||||
|
|
@ -196,15 +192,6 @@ async def update_meal(conn, meal: Meal) -> None:
|
|||
await sync_extra_ingredients(conn, meal.id, meal.extra_ingredients)
|
||||
await sync_recipes(conn, meal.id, meal.recipes)
|
||||
|
||||
async def mark_consumed(conn, meal: Meal, date: datetime.datetime = datetime.datetime.now()) -> None:
|
||||
meal.consumed_date = date
|
||||
|
||||
await conn.execute('''
|
||||
UPDATE Meal
|
||||
SET consumed_date = ?
|
||||
WHERE id = ?
|
||||
''', (date, meal.id))
|
||||
|
||||
async def with_purchase_date(conn, meal: Meal) -> Meal:
|
||||
async with conn.execute('''
|
||||
SELECT purchased_date FROM ShoppingList
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from meals import Meal, find_upcoming_meals_by_date_range, find_meal_by_id
|
||||
from meals import Meal, find_meals_by_date_range, find_meal_by_id
|
||||
from ingredients import Ingredient, insert_ingredient
|
||||
from persons import Person
|
||||
from products import Product
|
||||
|
|
@ -245,7 +245,7 @@ async def load_shopping_list(conn, id: int) -> ShoppingList:
|
|||
async def _upcoming_meals(conn) -> AsyncIterator[Meal]:
|
||||
start = datetime.now()
|
||||
end = start + timedelta(days=7)
|
||||
async for meal in find_upcoming_meals_by_date_range(conn, start, end):
|
||||
async for meal in find_meals_by_date_range(conn, start, end):
|
||||
if meal.purchase_date is None:
|
||||
yield meal
|
||||
|
||||
|
|
|
|||
|
|
@ -231,7 +231,7 @@ class Meals:
|
|||
id=0,
|
||||
create_date=datetime(2021, 12, 25),
|
||||
created_by=Persons.jacob,
|
||||
suggested_date=datetime(2021, 12, 25),
|
||||
meal_date=datetime(2021, 12, 25),
|
||||
chefs=[Persons.jacob],
|
||||
cleanup=[Persons.ryan],
|
||||
consumers=[Persons.ellie, Persons.chris],
|
||||
|
|
|
|||
|
|
@ -191,7 +191,7 @@ class TestMeals(unittest.IsolatedAsyncioTestCase):
|
|||
|
||||
self.assertIsNotNone(meal_by_id)
|
||||
self.assertEqual(meal_by_id.id, meal.id)
|
||||
self.assertEqual(meal_by_id.suggested_date, meal.suggested_date)
|
||||
self.assertEqual(meal_by_id.meal_date, meal.meal_date)
|
||||
self.assertEqual(len(meal_by_id.chefs), 1)
|
||||
self.assertEqual(meal_by_id.chefs[0].id, meal.chefs[0].id)
|
||||
self.assertEqual(meal_by_id.chefs[0].name, meal.chefs[0].name)
|
||||
|
|
@ -214,12 +214,12 @@ class TestMeals(unittest.IsolatedAsyncioTestCase):
|
|||
self.assertEqual(meal_by_id.extra_ingredients[0].id, extra_ingredient.id)
|
||||
self.assertEqual(meal_by_id.extra_ingredients[0].line, extra_ingredient.line)
|
||||
|
||||
meals_by_date_range = await main.get_upcoming_meals(meal.suggested_date, meal.suggested_date, self.conn)
|
||||
meals_by_date_range = await main.get_meals(meal.meal_date, meal.meal_date, self.conn)
|
||||
self.assertIsNotNone(meals_by_date_range)
|
||||
self.assertIsInstance(meals_by_date_range, list, msg=meals_by_date_range.body if hasattr(meals_by_date_range, 'body') else meals_by_date_range)
|
||||
self.assertEqual(len(meals_by_date_range), 1)
|
||||
self.assertEqual(meals_by_date_range[0].id, meal.id)
|
||||
self.assertEqual(meals_by_date_range[0].suggested_date, meal.suggested_date)
|
||||
self.assertEqual(meals_by_date_range[0].meal_date, meal.meal_date)
|
||||
self.assertEqual(len(meals_by_date_range[0].chefs), 1)
|
||||
self.assertEqual(meals_by_date_range[0].chefs[0].id, meal.chefs[0].id)
|
||||
self.assertEqual(meals_by_date_range[0].chefs[0].name, meal.chefs[0].name)
|
||||
|
|
@ -230,48 +230,6 @@ 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 testMarkConsumed(self) -> None:
|
||||
meal = test_data.Meals.broccoli_soup_for_jacob
|
||||
recipe = meal.recipes[0]
|
||||
recipe_ingredient = recipe.ingredients[0]
|
||||
recipe_product = recipe_ingredient.product
|
||||
extra_ingredient = meal.extra_ingredients[0]
|
||||
extra_product = extra_ingredient.product
|
||||
|
||||
person = test_data.Persons.jacob
|
||||
|
||||
await products_db.insert_product(self.conn, recipe_product, {})
|
||||
await products_db.insert_product(self.conn, extra_product, {})
|
||||
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.assertIsNotNone(create_response)
|
||||
self.assertIsInstance(create_response, meals.Meal, msg=create_response.body if hasattr(create_response, 'body') else create_response)
|
||||
|
||||
meal_by_id = await main.get_meal(meal.id, self.conn)
|
||||
self.assertIsNotNone(meal_by_id)
|
||||
self.assertIsInstance(meal_by_id, meals.Meal, msg=meal_by_id.body if hasattr(meal_by_id, 'body') else meal_by_id)
|
||||
self.assertIsNone(meal_by_id.consumed_date)
|
||||
|
||||
updated_meal = await main.mark_consumed(meal_id=meal.id, conn=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.assertIsNotNone(updated_meal.consumed_date)
|
||||
self.assertLessEqual(datetime.datetime.now() - updated_meal.consumed_date, datetime.timedelta(seconds=1))
|
||||
|
||||
upcoming_meals = await main.get_upcoming_meals(meal.suggested_date, meal.suggested_date, self.conn)
|
||||
self.assertIsNotNone(upcoming_meals)
|
||||
self.assertIsInstance(upcoming_meals, list, msg=upcoming_meals.body if hasattr(upcoming_meals, 'body') else upcoming_meals)
|
||||
self.assertEqual(len(upcoming_meals), 0)
|
||||
|
||||
meal_by_id = await main.get_meal(meal.id, self.conn)
|
||||
self.assertIsNotNone(meal_by_id)
|
||||
self.assertIsInstance(meal_by_id, meals.Meal, msg=meal_by_id.body if hasattr(meal_by_id, 'body') else meal_by_id)
|
||||
self.assertIsNotNone(meal_by_id.consumed_date)
|
||||
self.assertLessEqual(datetime.datetime.now() - meal_by_id.consumed_date, datetime.timedelta(seconds=1))
|
||||
|
||||
|
||||
async def testUpdate(self) -> None:
|
||||
meal = test_data.Meals.broccoli_soup_for_jacob
|
||||
|
||||
|
|
@ -293,7 +251,7 @@ class TestMeals(unittest.IsolatedAsyncioTestCase):
|
|||
self.assertIsNotNone(saved_meal)
|
||||
self.assertIsInstance(saved_meal, meals.Meal, msg=saved_meal.body if hasattr(saved_meal, 'body') else saved_meal)
|
||||
|
||||
saved_meal.suggested_date = datetime.datetime.fromisoformat('2021-01-01T12:00:00')
|
||||
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)
|
||||
|
|
@ -305,7 +263,7 @@ class TestMeals(unittest.IsolatedAsyncioTestCase):
|
|||
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.suggested_date, saved_meal.suggested_date)
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ class TestShopping(unittest.IsolatedAsyncioTestCase):
|
|||
for recipe in meal.recipes:
|
||||
await recipes.insert_recipe(self.conn, recipe)
|
||||
|
||||
meal.suggested_date = datetime.now() + timedelta(days=1)
|
||||
meal.meal_date = datetime.now() + timedelta(days=1)
|
||||
await meals.insert_meal(self.conn, meal)
|
||||
|
||||
shopping_list = await shopping.current_shopping_list(self.conn)
|
||||
|
|
|
|||
Loading…
Reference in a new issue