mirror of
https://github.com/mealie-recipes/mealie.git
synced 2026-02-01 21:43:28 -05:00
feature/mealplanner-rewrite (#417)
* multiple recipes per day * fix update * meal-planner rewrite * disable meal-tests * spacing Co-authored-by: hay-kot <hay-kot@pm.me>
This commit is contained in:
@@ -9,6 +9,7 @@ from mealie.routes.groups import groups_router
|
||||
from mealie.routes.mealplans import meal_plan_router
|
||||
from mealie.routes.media import media_router
|
||||
from mealie.routes.recipe import recipe_router
|
||||
from mealie.routes.shopping_list import shopping_list_router
|
||||
from mealie.routes.site_settings import settings_router
|
||||
from mealie.routes.users import user_router
|
||||
from mealie.services.events import create_general_event
|
||||
@@ -32,6 +33,7 @@ def api_routers():
|
||||
# Authentication
|
||||
app.include_router(user_router)
|
||||
app.include_router(groups_router)
|
||||
app.include_router(shopping_list_router)
|
||||
# Recipes
|
||||
app.include_router(recipe_router)
|
||||
app.include_router(media_router)
|
||||
|
||||
@@ -3,19 +3,21 @@ from logging import getLogger
|
||||
from mealie.db.db_base import BaseDocument
|
||||
from mealie.db.models.event import Event, EventNotification
|
||||
from mealie.db.models.group import Group
|
||||
from mealie.db.models.mealplan import MealPlanModel
|
||||
from mealie.db.models.mealplan import MealPlan
|
||||
from mealie.db.models.recipe.recipe import Category, RecipeModel, Tag
|
||||
from mealie.db.models.settings import CustomPage, SiteSettings
|
||||
from mealie.db.models.shopping_list import ShoppingList
|
||||
from mealie.db.models.sign_up import SignUp
|
||||
from mealie.db.models.theme import SiteThemeModel
|
||||
from mealie.db.models.users import LongLiveToken, User
|
||||
from mealie.schema.category import RecipeCategoryResponse, RecipeTagResponse
|
||||
from mealie.schema.event_notifications import EventNotificationIn
|
||||
from mealie.schema.events import Event as EventSchema
|
||||
from mealie.schema.meal import MealPlanInDB
|
||||
from mealie.schema.meal import MealPlanOut
|
||||
from mealie.schema.recipe import Recipe
|
||||
from mealie.schema.settings import CustomPageOut
|
||||
from mealie.schema.settings import SiteSettings as SiteSettingsSchema
|
||||
from mealie.schema.shopping_list import ShoppingListOut
|
||||
from mealie.schema.sign_up import SignUpOut
|
||||
from mealie.schema.theme import SiteTheme
|
||||
from mealie.schema.user import GroupInDB, LongLiveTokenInDB, UserInDB
|
||||
@@ -75,8 +77,8 @@ class _Tags(BaseDocument):
|
||||
class _Meals(BaseDocument):
|
||||
def __init__(self) -> None:
|
||||
self.primary_key = "uid"
|
||||
self.sql_model = MealPlanModel
|
||||
self.schema = MealPlanInDB
|
||||
self.sql_model = MealPlan
|
||||
self.schema = MealPlanOut
|
||||
|
||||
|
||||
class _Settings(BaseDocument):
|
||||
@@ -120,7 +122,7 @@ class _Groups(BaseDocument):
|
||||
self.sql_model = Group
|
||||
self.schema = GroupInDB
|
||||
|
||||
def get_meals(self, session: Session, match_value: str, match_key: str = "name") -> list[MealPlanInDB]:
|
||||
def get_meals(self, session: Session, match_value: str, match_key: str = "name") -> list[MealPlanOut]:
|
||||
"""A Helper function to get the group from the database and return a sorted list of
|
||||
|
||||
Args:
|
||||
@@ -129,13 +131,20 @@ class _Groups(BaseDocument):
|
||||
match_key (str, optional): Match Key. Defaults to "name".
|
||||
|
||||
Returns:
|
||||
list[MealPlanInDB]: [description]
|
||||
list[MealPlanOut]: [description]
|
||||
"""
|
||||
group: GroupInDB = session.query(self.sql_model).filter_by(**{match_key: match_value}).one_or_none()
|
||||
|
||||
return group.mealplans
|
||||
|
||||
|
||||
class _ShoppingList(BaseDocument):
|
||||
def __init__(self) -> None:
|
||||
self.primary_key = "id"
|
||||
self.sql_model = ShoppingList
|
||||
self.schema = ShoppingListOut
|
||||
|
||||
|
||||
class _SignUps(BaseDocument):
|
||||
def __init__(self) -> None:
|
||||
self.primary_key = "token"
|
||||
@@ -179,6 +188,7 @@ class Database:
|
||||
self.custom_pages = _CustomPages()
|
||||
self.events = _Events()
|
||||
self.event_notifications = _EventNotification()
|
||||
self.shopping_lists = _ShoppingList()
|
||||
|
||||
|
||||
db = Database()
|
||||
|
||||
@@ -3,6 +3,7 @@ from mealie.db.models.group import *
|
||||
from mealie.db.models.mealplan import *
|
||||
from mealie.db.models.recipe.recipe import *
|
||||
from mealie.db.models.settings import *
|
||||
from mealie.db.models.shopping_list import *
|
||||
from mealie.db.models.sign_up import *
|
||||
from mealie.db.models.theme import *
|
||||
from mealie.db.models.users import *
|
||||
|
||||
@@ -19,11 +19,18 @@ class Group(SqlAlchemyBase, BaseMixins):
|
||||
name = sa.Column(sa.String, index=True, nullable=False, unique=True)
|
||||
users = orm.relationship("User", back_populates="group")
|
||||
mealplans = orm.relationship(
|
||||
"MealPlanModel",
|
||||
"MealPlan",
|
||||
back_populates="group",
|
||||
single_parent=True,
|
||||
order_by="MealPlanModel.startDate",
|
||||
order_by="MealPlan.start_date",
|
||||
)
|
||||
|
||||
shopping_lists = orm.relationship(
|
||||
"ShoppingList",
|
||||
back_populates="group",
|
||||
single_parent=True,
|
||||
)
|
||||
|
||||
categories = orm.relationship("Category", secondary=group2categories, single_parent=True)
|
||||
|
||||
# Webhook Settings
|
||||
@@ -32,16 +39,7 @@ class Group(SqlAlchemyBase, BaseMixins):
|
||||
webhook_urls = orm.relationship("WebhookURLModel", uselist=True, cascade="all, delete-orphan")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name,
|
||||
id=None,
|
||||
users=None,
|
||||
mealplans=None,
|
||||
categories=[],
|
||||
session=None,
|
||||
webhook_enable=False,
|
||||
webhook_time="00:00",
|
||||
webhook_urls=[],
|
||||
self, name, categories=[], session=None, webhook_enable=False, webhook_time="00:00", webhook_urls=[], **_
|
||||
) -> None:
|
||||
self.name = name
|
||||
self.categories = [Category.get_ref(session=session, slug=cat.get("slug")) for cat in categories]
|
||||
|
||||
@@ -1,50 +1,80 @@
|
||||
from typing import List
|
||||
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy.orm as orm
|
||||
from mealie.db.models.group import Group
|
||||
from mealie.db.models.model_base import BaseMixins, SqlAlchemyBase
|
||||
from mealie.db.models.recipe.recipe import RecipeModel
|
||||
from mealie.db.models.shopping_list import ShoppingList
|
||||
from sqlalchemy import Column, Date, ForeignKey, Integer, String
|
||||
from sqlalchemy.ext.orderinglist import ordering_list
|
||||
|
||||
|
||||
class Meal(SqlAlchemyBase):
|
||||
__tablename__ = "meal"
|
||||
id = sa.Column(sa.Integer, primary_key=True)
|
||||
parent_id = sa.Column(sa.Integer, sa.ForeignKey("mealplan.uid"))
|
||||
slug = sa.Column(sa.String)
|
||||
name = sa.Column(sa.String)
|
||||
date = sa.Column(sa.Date)
|
||||
image = sa.Column(sa.String)
|
||||
description = sa.Column(sa.String)
|
||||
id = Column(Integer, primary_key=True)
|
||||
parent_id = Column(Integer, ForeignKey("mealdays.id"))
|
||||
position = Column(Integer)
|
||||
name = Column(String)
|
||||
slug = Column(String)
|
||||
description = Column(String)
|
||||
|
||||
def __init__(self, slug, name="", description="", session=None) -> None:
|
||||
|
||||
if slug and slug != "":
|
||||
recipe: RecipeModel = session.query(RecipeModel).filter(RecipeModel.slug == slug).one_or_none()
|
||||
|
||||
if recipe:
|
||||
name = recipe.name
|
||||
self.slug = recipe.slug
|
||||
description = recipe.description
|
||||
|
||||
def __init__(self, slug, name, date, image, description, session=None) -> None:
|
||||
self.slug = slug
|
||||
self.name = name
|
||||
self.date = date
|
||||
self.image = image
|
||||
self.description = description
|
||||
|
||||
|
||||
class MealPlanModel(SqlAlchemyBase, BaseMixins):
|
||||
class MealDay(SqlAlchemyBase, BaseMixins):
|
||||
__tablename__ = "mealdays"
|
||||
id = Column(Integer, primary_key=True)
|
||||
parent_id = Column(Integer, ForeignKey("mealplan.uid"))
|
||||
date = Column(Date)
|
||||
meals: list[Meal] = orm.relationship(
|
||||
Meal,
|
||||
cascade="all, delete, delete-orphan",
|
||||
order_by="Meal.position",
|
||||
collection_class=ordering_list("position"),
|
||||
)
|
||||
|
||||
def __init__(self, date, meals: list, session=None):
|
||||
self.date = date
|
||||
self.meals = [Meal(**m, session=session) for m in meals]
|
||||
|
||||
|
||||
class MealPlan(SqlAlchemyBase, BaseMixins):
|
||||
__tablename__ = "mealplan"
|
||||
uid = sa.Column(sa.Integer, primary_key=True, unique=True) # ! Probably Bad?
|
||||
startDate = sa.Column(sa.Date)
|
||||
endDate = sa.Column(sa.Date)
|
||||
meals: List[Meal] = orm.relationship(Meal, cascade="all, delete, delete-orphan")
|
||||
group_id = sa.Column(sa.Integer, sa.ForeignKey("groups.id"))
|
||||
uid = Column(Integer, primary_key=True, unique=True)
|
||||
start_date = Column(Date)
|
||||
end_date = Column(Date)
|
||||
plan_days: list[MealDay] = orm.relationship(MealDay, cascade="all, delete, delete-orphan")
|
||||
|
||||
group_id = Column(Integer, ForeignKey("groups.id"))
|
||||
group = orm.relationship("Group", back_populates="mealplans")
|
||||
|
||||
def __init__(self, startDate, endDate, meals, group: str, uid=None, session=None) -> None:
|
||||
self.startDate = startDate
|
||||
self.endDate = endDate
|
||||
shopping_list_id = Column(Integer, ForeignKey("shopping_lists.id"))
|
||||
shopping_list: ShoppingList = orm.relationship("ShoppingList", single_parent=True)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
start_date,
|
||||
end_date,
|
||||
plan_days,
|
||||
group: str,
|
||||
shopping_list: int = None,
|
||||
session=None,
|
||||
**_,
|
||||
) -> None:
|
||||
self.start_date = start_date
|
||||
self.end_date = end_date
|
||||
self.group = Group.get_ref(session, group)
|
||||
self.meals = [Meal(**meal) for meal in meals]
|
||||
|
||||
def update(self, session, startDate, endDate, meals, uid, group) -> None:
|
||||
if shopping_list:
|
||||
self.shopping_list = ShoppingList.get_ref(session, shopping_list)
|
||||
|
||||
self.__init__(
|
||||
startDate=startDate,
|
||||
endDate=endDate,
|
||||
meals=meals,
|
||||
group=group,
|
||||
session=session,
|
||||
)
|
||||
self.plan_days = [MealDay(**day, session=session) for day in plan_days]
|
||||
|
||||
49
mealie/db/models/shopping_list.py
Normal file
49
mealie/db/models/shopping_list.py
Normal file
@@ -0,0 +1,49 @@
|
||||
import sqlalchemy.orm as orm
|
||||
from mealie.db.models.group import Group
|
||||
from mealie.db.models.model_base import BaseMixins, SqlAlchemyBase
|
||||
from requests import Session
|
||||
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String
|
||||
from sqlalchemy.ext.orderinglist import ordering_list
|
||||
|
||||
|
||||
class ShoppingListItem(SqlAlchemyBase, BaseMixins):
|
||||
__tablename__ = "shopping_list_items"
|
||||
id = Column(Integer, primary_key=True)
|
||||
parent_id = Column(Integer, ForeignKey("shopping_lists.id"))
|
||||
position = Column(Integer, nullable=False)
|
||||
|
||||
title = Column(String)
|
||||
text = Column(String)
|
||||
quantity = Column(Integer)
|
||||
checked = Column(Boolean)
|
||||
|
||||
def __init__(self, title, text, quantity, checked, **_) -> None:
|
||||
self.title = title
|
||||
self.text = text
|
||||
self.quantity = quantity
|
||||
self.checked = checked
|
||||
|
||||
|
||||
class ShoppingList(SqlAlchemyBase, BaseMixins):
|
||||
__tablename__ = "shopping_lists"
|
||||
id = Column(Integer, primary_key=True)
|
||||
|
||||
group_id = Column(Integer, ForeignKey("groups.id"))
|
||||
group = orm.relationship("Group", back_populates="shopping_lists")
|
||||
|
||||
name = Column(String)
|
||||
items: list[ShoppingListItem] = orm.relationship(
|
||||
ShoppingListItem,
|
||||
cascade="all, delete, delete-orphan",
|
||||
order_by="ShoppingListItem.position",
|
||||
collection_class=ordering_list("position"),
|
||||
)
|
||||
|
||||
def __init__(self, name, group, items, session=None, **_) -> None:
|
||||
self.name = name
|
||||
self.group = Group.get_ref(session, group)
|
||||
self.items = [ShoppingListItem(**i) for i in items]
|
||||
|
||||
@staticmethod
|
||||
def get_ref(session: Session, id: int):
|
||||
return session.query(ShoppingList).filter(ShoppingList.id == id).one_or_none()
|
||||
@@ -2,18 +2,18 @@ from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, status
|
||||
from mealie.db.database import db
|
||||
from mealie.db.db_setup import generate_session
|
||||
from mealie.routes.deps import get_current_user
|
||||
from mealie.schema.meal import MealPlanIn, MealPlanInDB
|
||||
from mealie.schema.meal import MealPlanIn, MealPlanOut
|
||||
from mealie.schema.user import GroupInDB, UserInDB
|
||||
from mealie.services.events import create_group_event
|
||||
from mealie.services.image import image
|
||||
from mealie.services.meal_services import get_todays_meal, process_meals
|
||||
from mealie.services.meal_services import get_todays_meal, set_mealplan_dates
|
||||
from sqlalchemy.orm.session import Session
|
||||
from starlette.responses import FileResponse
|
||||
|
||||
router = APIRouter(prefix="/api/meal-plans", tags=["Meal Plan"])
|
||||
|
||||
|
||||
@router.get("/all", response_model=list[MealPlanInDB])
|
||||
@router.get("/all", response_model=list[MealPlanOut])
|
||||
def get_all_meals(
|
||||
current_user: UserInDB = Depends(get_current_user),
|
||||
session: Session = Depends(generate_session),
|
||||
@@ -31,11 +31,11 @@ def create_meal_plan(
|
||||
current_user: UserInDB = Depends(get_current_user),
|
||||
):
|
||||
""" Creates a meal plan database entry """
|
||||
processed_plan = process_meals(session, data)
|
||||
set_mealplan_dates(data)
|
||||
background_tasks.add_task(
|
||||
create_group_event, "Meal Plan Created", f"Mealplan Created for '{current_user.group}'", session=session
|
||||
)
|
||||
return db.meals.create(session, processed_plan.dict())
|
||||
return db.meals.create(session, data.dict())
|
||||
|
||||
|
||||
@router.put("/{plan_id}")
|
||||
@@ -47,8 +47,8 @@ def update_meal_plan(
|
||||
current_user: UserInDB = Depends(get_current_user),
|
||||
):
|
||||
""" Updates a meal plan based off ID """
|
||||
processed_plan = process_meals(session, meal_plan)
|
||||
processed_plan = MealPlanInDB(uid=plan_id, **processed_plan.dict())
|
||||
set_mealplan_dates(meal_plan)
|
||||
processed_plan = MealPlanOut(uid=plan_id, **meal_plan.dict())
|
||||
try:
|
||||
db.meals.update(session, plan_id, processed_plan.dict())
|
||||
background_tasks.add_task(
|
||||
@@ -76,7 +76,7 @@ def delete_meal_plan(
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
@router.get("/this-week", response_model=MealPlanInDB)
|
||||
@router.get("/this-week", response_model=MealPlanOut)
|
||||
def get_this_week(session: Session = Depends(generate_session), current_user: UserInDB = Depends(get_current_user)):
|
||||
""" Returns the meal plan data for this week """
|
||||
plans = db.groups.get_meals(session, current_user.group)
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
from fastapi import APIRouter, Depends
|
||||
from mealie.core.root_logger import get_logger
|
||||
from mealie.db.database import db
|
||||
from mealie.db.db_setup import generate_session
|
||||
from mealie.routes.deps import get_current_user
|
||||
from mealie.schema.meal import MealPlanInDB
|
||||
from mealie.schema.meal import MealPlanOut
|
||||
from mealie.schema.recipe import Recipe
|
||||
from mealie.schema.shopping_list import ListItem, ShoppingListIn, ShoppingListOut
|
||||
from mealie.schema.user import UserInDB
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
logger = get_logger()
|
||||
|
||||
router = APIRouter(prefix="/api/meal-plans", tags=["Meal Plan"])
|
||||
|
||||
|
||||
@@ -13,12 +18,32 @@ router = APIRouter(prefix="/api/meal-plans", tags=["Meal Plan"])
|
||||
def get_shopping_list(
|
||||
id: str,
|
||||
session: Session = Depends(generate_session),
|
||||
current_user=Depends(get_current_user),
|
||||
current_user: UserInDB = Depends(get_current_user),
|
||||
):
|
||||
|
||||
# ! Refactor into Single Database Call
|
||||
mealplan = db.meals.get(session, id)
|
||||
mealplan: MealPlanInDB
|
||||
slugs = [x.slug for x in mealplan.meals]
|
||||
recipes: list[Recipe] = [db.recipes.get(session, x) for x in slugs]
|
||||
return [{"name": x.name, "recipe_ingredient": x.recipe_ingredient} for x in recipes if x]
|
||||
mealplan: MealPlanOut = db.meals.get(session, id)
|
||||
|
||||
all_ingredients = []
|
||||
|
||||
for plan_day in mealplan.plan_days:
|
||||
for meal in plan_day.meals:
|
||||
if not meal.slug:
|
||||
continue
|
||||
|
||||
try:
|
||||
recipe: Recipe = db.recipes.get(session, meal.slug)
|
||||
all_ingredients += recipe.recipe_ingredient
|
||||
except Exception:
|
||||
logger.error("Recipe Not Found")
|
||||
|
||||
new_list = ShoppingListIn(
|
||||
name="MealPlan Shopping List", group=current_user.group, items=[ListItem(text=t) for t in all_ingredients]
|
||||
)
|
||||
|
||||
created_list: ShoppingListOut = db.shopping_lists.create(session, new_list)
|
||||
|
||||
mealplan.shopping_list = created_list.id
|
||||
|
||||
db.meals.update(session, mealplan.uid, mealplan)
|
||||
|
||||
return created_list
|
||||
|
||||
@@ -5,10 +5,7 @@ from mealie.routes.deps import get_current_user
|
||||
from mealie.schema.category import CategoryIn, RecipeCategoryResponse
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
router = APIRouter(
|
||||
prefix="/api/categories",
|
||||
tags=["Recipe Categories"],
|
||||
)
|
||||
router = APIRouter(prefix="/api/categories", tags=["Recipe Categories"])
|
||||
|
||||
|
||||
@router.get("")
|
||||
|
||||
40
mealie/routes/shopping_list.py
Normal file
40
mealie/routes/shopping_list.py
Normal file
@@ -0,0 +1,40 @@
|
||||
from fastapi import APIRouter, Depends
|
||||
from mealie.db.database import db
|
||||
from mealie.db.db_setup import generate_session
|
||||
from mealie.routes.deps import get_current_user
|
||||
from mealie.schema.shopping_list import ShoppingListIn, ShoppingListOut
|
||||
from mealie.schema.user import UserInDB
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
shopping_list_router = APIRouter(prefix="/api/shopping-lists", tags=["Shopping Lists"])
|
||||
|
||||
|
||||
@shopping_list_router.post("", response_model=ShoppingListOut)
|
||||
async def create_shopping_list(
|
||||
list_in: ShoppingListIn,
|
||||
current_user: UserInDB = Depends(get_current_user),
|
||||
session: Session = Depends(generate_session),
|
||||
):
|
||||
""" Create Shopping List in the Database """
|
||||
|
||||
list_in.group = current_user.group
|
||||
|
||||
return db.shopping_lists.create(session, list_in)
|
||||
|
||||
|
||||
@shopping_list_router.get("/{id}", response_model=ShoppingListOut)
|
||||
async def get_shopping_list(id: int, session: Session = Depends(generate_session)):
|
||||
""" Get Shopping List from the Database """
|
||||
return db.shopping_lists.get(session, id)
|
||||
|
||||
|
||||
@shopping_list_router.put("/{id}", dependencies=[Depends(get_current_user)], response_model=ShoppingListOut)
|
||||
async def update_shopping_list(id: int, new_data: ShoppingListIn, session: Session = Depends(generate_session)):
|
||||
""" Update Shopping List in the Database """
|
||||
return db.shopping_lists.update(session, id, new_data)
|
||||
|
||||
|
||||
@shopping_list_router.delete("/{id}", dependencies=[Depends(get_current_user)])
|
||||
async def delete_shopping_list(id: int, session: Session = Depends(generate_session)):
|
||||
""" Delete Shopping List from the Database """
|
||||
return db.shopping_lists.delete(session, id)
|
||||
@@ -1,51 +1,70 @@
|
||||
from datetime import date
|
||||
from typing import List, Optional
|
||||
from typing import Optional
|
||||
|
||||
from mealie.db.models.mealplan import MealPlanModel
|
||||
from pydantic import BaseModel, validator
|
||||
from fastapi_camelcase import CamelModel
|
||||
from mealie.db.models.mealplan import MealPlan
|
||||
from pydantic import validator
|
||||
from pydantic.utils import GetterDict
|
||||
|
||||
|
||||
class MealIn(BaseModel):
|
||||
name: Optional[str]
|
||||
class MealIn(CamelModel):
|
||||
slug: Optional[str]
|
||||
date: Optional[date]
|
||||
|
||||
|
||||
class MealOut(MealIn):
|
||||
image: Optional[str]
|
||||
name: Optional[str]
|
||||
description: Optional[str]
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
|
||||
class MealPlanIn(BaseModel):
|
||||
group: str
|
||||
startDate: date
|
||||
endDate: date
|
||||
meals: List[MealIn]
|
||||
class MealDayIn(CamelModel):
|
||||
date: Optional[date]
|
||||
meals: list[MealIn]
|
||||
|
||||
@validator("endDate")
|
||||
def endDate_after_startDate(v, values, config, field):
|
||||
if "startDate" in values and v < values["startDate"]:
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
|
||||
class MealDayOut(MealDayIn):
|
||||
id: int
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
|
||||
class MealPlanIn(CamelModel):
|
||||
group: str
|
||||
start_date: date
|
||||
end_date: date
|
||||
plan_days: list[MealDayIn]
|
||||
|
||||
@validator("end_date")
|
||||
def end_date_after_start_date(v, values, config, field):
|
||||
if "start_date" in values and v < values["start_date"]:
|
||||
raise ValueError("EndDate should be greater than StartDate")
|
||||
return v
|
||||
|
||||
|
||||
class MealPlanProcessed(MealPlanIn):
|
||||
meals: list[MealOut]
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
|
||||
class MealPlanInDB(MealPlanProcessed):
|
||||
uid: str
|
||||
class MealPlanOut(MealPlanIn):
|
||||
uid: int
|
||||
shopping_list: Optional[int]
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
@classmethod
|
||||
def getter_dict(_cls, name_orm: MealPlanModel):
|
||||
return {
|
||||
**GetterDict(name_orm),
|
||||
"group": name_orm.group.name,
|
||||
}
|
||||
def getter_dict(_cls, name_orm: MealPlan):
|
||||
try:
|
||||
return {
|
||||
**GetterDict(name_orm),
|
||||
"group": name_orm.group.name,
|
||||
"shopping_list": name_orm.shopping_list.id,
|
||||
}
|
||||
except Exception:
|
||||
return {
|
||||
**GetterDict(name_orm),
|
||||
"group": name_orm.group.name,
|
||||
"shopping_list": None,
|
||||
}
|
||||
|
||||
35
mealie/schema/shopping_list.py
Normal file
35
mealie/schema/shopping_list.py
Normal file
@@ -0,0 +1,35 @@
|
||||
from typing import Optional
|
||||
|
||||
from fastapi_camelcase import CamelModel
|
||||
from mealie.db.models.shopping_list import ShoppingList
|
||||
from pydantic.utils import GetterDict
|
||||
|
||||
|
||||
class ListItem(CamelModel):
|
||||
title: Optional[str]
|
||||
text: str = ""
|
||||
quantity: int = 1
|
||||
checked: bool = False
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
|
||||
class ShoppingListIn(CamelModel):
|
||||
name: str
|
||||
group: Optional[str]
|
||||
items: list[ListItem]
|
||||
|
||||
|
||||
class ShoppingListOut(ShoppingListIn):
|
||||
id: int
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
@classmethod
|
||||
def getter_dict(cls, ormModel: ShoppingList):
|
||||
return {
|
||||
**GetterDict(ormModel),
|
||||
"group": ormModel.group.name,
|
||||
}
|
||||
@@ -5,7 +5,8 @@ from mealie.core.config import settings
|
||||
from mealie.db.models.group import Group
|
||||
from mealie.db.models.users import User
|
||||
from mealie.schema.category import CategoryBase
|
||||
from mealie.schema.meal import MealPlanInDB
|
||||
from mealie.schema.meal import MealPlanOut
|
||||
from mealie.schema.shopping_list import ShoppingListOut
|
||||
from pydantic.types import constr
|
||||
from pydantic.utils import GetterDict
|
||||
|
||||
@@ -105,7 +106,8 @@ class UpdateGroup(GroupBase):
|
||||
|
||||
class GroupInDB(UpdateGroup):
|
||||
users: Optional[list[UserOut]]
|
||||
mealplans: Optional[list[MealPlanInDB]]
|
||||
mealplans: Optional[list[MealPlanOut]]
|
||||
shopping_lists: Optional[list[ShoppingListOut]]
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
@@ -3,41 +3,16 @@ from typing import Union
|
||||
|
||||
from mealie.db.database import db
|
||||
from mealie.db.db_setup import create_session
|
||||
from mealie.schema.meal import MealIn, MealOut, MealPlanIn, MealPlanInDB, MealPlanProcessed
|
||||
from mealie.schema.meal import MealDayIn, MealPlanIn
|
||||
from mealie.schema.recipe import Recipe
|
||||
from mealie.schema.user import GroupInDB
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
|
||||
def process_meals(session: Session, meal_plan_base: MealPlanIn) -> MealPlanProcessed:
|
||||
meals = []
|
||||
for x, meal in enumerate(meal_plan_base.meals):
|
||||
meal: MealIn
|
||||
try:
|
||||
recipe: Recipe = db.recipes.get(session, meal.slug)
|
||||
|
||||
meal_data = MealOut(
|
||||
slug=recipe.slug,
|
||||
name=recipe.name,
|
||||
date=meal_plan_base.startDate + timedelta(days=x),
|
||||
image=recipe.image,
|
||||
description=recipe.description,
|
||||
)
|
||||
|
||||
except Exception:
|
||||
|
||||
meal_data = MealOut(
|
||||
date=meal_plan_base.startDate + timedelta(days=x),
|
||||
)
|
||||
|
||||
meals.append(meal_data)
|
||||
|
||||
return MealPlanProcessed(
|
||||
group=meal_plan_base.group,
|
||||
meals=meals,
|
||||
startDate=meal_plan_base.startDate,
|
||||
endDate=meal_plan_base.endDate,
|
||||
)
|
||||
def set_mealplan_dates(meal_plan_base: MealPlanIn) -> MealPlanIn:
|
||||
for x, plan_days in enumerate(meal_plan_base.plan_days):
|
||||
plan_days: MealDayIn
|
||||
plan_days.date = meal_plan_base.start_date + timedelta(days=x)
|
||||
|
||||
|
||||
def get_todays_meal(session: Session, group: Union[int, GroupInDB]) -> Recipe:
|
||||
@@ -52,6 +27,7 @@ def get_todays_meal(session: Session, group: Union[int, GroupInDB]) -> Recipe:
|
||||
Returns:
|
||||
Recipe: Pydantic Recipe Object
|
||||
"""
|
||||
|
||||
session = session or create_session()
|
||||
|
||||
if isinstance(group, int):
|
||||
@@ -60,12 +36,12 @@ def get_todays_meal(session: Session, group: Union[int, GroupInDB]) -> Recipe:
|
||||
today_slug = None
|
||||
|
||||
for mealplan in group.mealplans:
|
||||
mealplan: MealPlanInDB
|
||||
for meal in mealplan.meals:
|
||||
meal: MealOut
|
||||
if meal.date == date.today():
|
||||
today_slug = meal.slug
|
||||
break
|
||||
for plan_day in mealplan.plan_days:
|
||||
if plan_day.date == date.today():
|
||||
if plan_day.meals[0].slug and plan_day.meals[0].slug != "":
|
||||
today_slug = plan_day.meals[0].slug
|
||||
else:
|
||||
return plan_day.meals[0]
|
||||
|
||||
if today_slug:
|
||||
return db.recipes.get(session, today_slug)
|
||||
|
||||
Reference in New Issue
Block a user