Added consumed functionality
This commit is contained in:
parent
d6b667463b
commit
dc83b0da9b
4 changed files with 81 additions and 12 deletions
22
main.py
22
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, Union
|
||||
from typing import List, Annotated, Optional, 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/")
|
||||
async def get_meals(date_from: Annotated[datetime.datetime, Query(alias='from')], to: datetime.datetime, conn: sqlite3.Connection = Depends(get_db)) -> List[meals.Meal]:
|
||||
@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]:
|
||||
result = []
|
||||
async for meal in meals.find_meals_by_date_range(conn, date_from, to):
|
||||
async for meal in meals.find_upcoming_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,6 +229,20 @@ async def update_meal(meal_id: int, meal: meals.Meal, conn: sqlite3.Connection =
|
|||
|
||||
return await get_meal(meal_id, conn)
|
||||
|
||||
class ConsumedWrapper(BaseModel):
|
||||
meal_id: int
|
||||
consumed_date: Optional[datetime.datetime] = None
|
||||
|
||||
@app.post("/meals/{meal_id}/consumed")
|
||||
async def mark_consumed(body: ConsumedWrapper, conn: sqlite3.Connection = Depends(get_db), person: persons.Person = Depends(cookie_person)) -> meals.Meal:
|
||||
meal = await meals.find_meal_by_id(conn, body.meal_id)
|
||||
if not meal:
|
||||
return JSONResponse(status_code=404, content={'message': 'Meal not found'})
|
||||
|
||||
await meals.mark_consumed(conn, meal, body.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)
|
||||
|
|
|
|||
23
meals/db.py
23
meals/db.py
|
|
@ -10,10 +10,10 @@ from persons import Person
|
|||
import datetime
|
||||
|
||||
class Meal(BaseModel):
|
||||
KEYS: ClassVar[List[str]] = ['id', 'suggested_date']
|
||||
KEYS: ClassVar[List[str]] = ['id', 'suggested_date', 'consumed_date']
|
||||
id: int = -1
|
||||
suggested_date: datetime.datetime
|
||||
purchase_date: Optional[datetime.datetime] = None
|
||||
consumed_date: Optional[datetime.datetime] = None
|
||||
|
||||
chefs: List[Person] = []
|
||||
cleanup: List[Person] = []
|
||||
|
|
@ -21,11 +21,15 @@ 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
|
||||
suggested_date TEXT,
|
||||
consumed_date TEXT
|
||||
);''')
|
||||
|
||||
await conn.execute('''
|
||||
|
|
@ -99,10 +103,10 @@ async def find_meal_by_id(conn, meal_id: int) -> Meal:
|
|||
await load_extra_ingredients(conn, meal)
|
||||
return meal
|
||||
|
||||
async def find_meals_by_date_range(conn, start: datetime, end: datetime) -> AsyncIterator[Meal]:
|
||||
async def find_upcoming_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 <= ?
|
||||
WHERE suggested_date >= ? AND suggested_date <= ? AND consumed_date IS NULL
|
||||
''', (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)}))
|
||||
|
|
@ -192,6 +196,15 @@ 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_meals_by_date_range, find_meal_by_id
|
||||
from meals import Meal, find_upcoming_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_meals_by_date_range(conn, start, end):
|
||||
async for meal in find_upcoming_meals_by_date_range(conn, start, end):
|
||||
if meal.purchase_date is None:
|
||||
yield meal
|
||||
|
||||
|
|
|
|||
|
|
@ -214,7 +214,7 @@ 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_meals(meal.suggested_date, meal.suggested_date, self.conn)
|
||||
meals_by_date_range = await main.get_upcoming_meals(meal.suggested_date, meal.suggested_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)
|
||||
|
|
@ -230,6 +230,48 @@ 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(main.ConsumedWrapper(meal_id=meal.id), 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
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue