mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-12-29 05:25:30 -05:00
feat: ✨ (WIP) base-shoppinglist infra (#911)
* feat: ✨ base-shoppinglist infra (WIP) * add type checker * implement controllers * apply router fixes * add checked section hide/animation * add label support * formatting * fix overflow images * add experimental banner * fix #912 word break issue * remove any type errors * bump dependencies * remove templates * fix build errors * bump node version * fix template literal
This commit is contained in:
@@ -4,16 +4,14 @@ from mealie.db.db_setup import create_session, engine
|
||||
from mealie.db.models._model_base import SqlAlchemyBase
|
||||
from mealie.repos.all_repositories import get_repositories
|
||||
from mealie.repos.repository_factory import AllRepositories
|
||||
from mealie.repos.seed.init_units_foods import default_recipe_unit_init
|
||||
from mealie.repos.seed.init_users import default_user_init
|
||||
from mealie.repos.seed.seeders import IngredientFoodsSeeder, IngredientUnitsSeeder, MultiPurposeLabelSeeder
|
||||
from mealie.schema.user.user import GroupBase
|
||||
from mealie.services.events import create_general_event
|
||||
from mealie.services.group_services.group_utils import create_new_group
|
||||
|
||||
logger = root_logger.get_logger("init_db")
|
||||
|
||||
settings = get_app_settings()
|
||||
|
||||
|
||||
def create_all_models():
|
||||
import mealie.db.models._all_models # noqa: F401
|
||||
@@ -22,12 +20,25 @@ def create_all_models():
|
||||
|
||||
|
||||
def init_db(db: AllRepositories) -> None:
|
||||
# TODO: Port other seed data to use abstract seeder class
|
||||
default_group_init(db)
|
||||
default_user_init(db)
|
||||
default_recipe_unit_init(db)
|
||||
|
||||
group_id = db.groups.get_all()[0].id
|
||||
|
||||
seeders = [
|
||||
MultiPurposeLabelSeeder(db, group_id=group_id),
|
||||
IngredientFoodsSeeder(db, group_id=group_id),
|
||||
IngredientUnitsSeeder(db, group_id=group_id),
|
||||
]
|
||||
|
||||
for seeder in seeders:
|
||||
seeder.seed()
|
||||
|
||||
|
||||
def default_group_init(db: AllRepositories):
|
||||
settings = get_app_settings()
|
||||
|
||||
logger.info("Generating Default Group")
|
||||
create_new_group(db, GroupBase(name=settings.DEFAULT_GROUP))
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from .event import *
|
||||
from .group import *
|
||||
from .labels import *
|
||||
from .recipe.recipe import *
|
||||
from .server import *
|
||||
from .sign_up import *
|
||||
|
||||
@@ -78,6 +78,8 @@ def handle_one_to_many_list(session: Session, get_attr, relation_cls, all_elemen
|
||||
elems_to_create: list[dict] = []
|
||||
updated_elems: list[dict] = []
|
||||
|
||||
cfg = _get_config(relation_cls)
|
||||
|
||||
for elem in all_elements:
|
||||
elem_id = elem.get(get_attr, None) if isinstance(elem, dict) else elem
|
||||
existing_elem = session.query(relation_cls).filter_by(**{get_attr: elem_id}).one_or_none()
|
||||
@@ -88,7 +90,8 @@ def handle_one_to_many_list(session: Session, get_attr, relation_cls, all_elemen
|
||||
|
||||
elif isinstance(elem, dict):
|
||||
for key, value in elem.items():
|
||||
setattr(existing_elem, key, value)
|
||||
if key not in cfg.exclude:
|
||||
setattr(existing_elem, key, value)
|
||||
|
||||
updated_elems.append(existing_elem)
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import sqlalchemy.orm as orm
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from mealie.core.config import get_app_settings
|
||||
from mealie.db.models.labels import MultiPurposeLabel
|
||||
|
||||
from .._model_base import BaseMixins, SqlAlchemyBase
|
||||
from .._model_utils import GUID, auto_init
|
||||
@@ -47,6 +48,8 @@ class Group(SqlAlchemyBase, BaseMixins):
|
||||
"single_parent": True,
|
||||
}
|
||||
|
||||
labels = orm.relationship(MultiPurposeLabel, **common_args)
|
||||
|
||||
mealplans = orm.relationship(GroupMealPlan, order_by="GroupMealPlan.date", **common_args)
|
||||
webhooks = orm.relationship(GroupWebhooksModel, **common_args)
|
||||
cookbooks = orm.relationship(CookBook, **common_args)
|
||||
|
||||
@@ -1,51 +1,64 @@
|
||||
import sqlalchemy.orm as orm
|
||||
from requests import Session
|
||||
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String
|
||||
from sqlalchemy import Boolean, Column, Float, ForeignKey, Integer, String, orm
|
||||
from sqlalchemy.ext.orderinglist import ordering_list
|
||||
|
||||
from mealie.db.models.labels import MultiPurposeLabel
|
||||
|
||||
from .._model_base import BaseMixins, SqlAlchemyBase
|
||||
from .._model_utils.guid import GUID
|
||||
from .group import Group
|
||||
from .._model_utils import GUID, auto_init
|
||||
from ..recipe.ingredient import IngredientFoodModel, IngredientUnitModel
|
||||
|
||||
|
||||
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)
|
||||
# Id's
|
||||
id = Column(GUID, primary_key=True, default=GUID.generate)
|
||||
shopping_list_id = Column(GUID, ForeignKey("shopping_lists.id"))
|
||||
|
||||
def __init__(self, title, text, quantity, checked, **_) -> None:
|
||||
self.title = title
|
||||
self.text = text
|
||||
self.quantity = quantity
|
||||
self.checked = checked
|
||||
# Meta
|
||||
recipe_id = Column(Integer, nullable=True)
|
||||
is_ingredient = Column(Boolean, default=True)
|
||||
position = Column(Integer, nullable=False, default=0)
|
||||
checked = Column(Boolean, default=False)
|
||||
|
||||
quantity = Column(Float, default=1)
|
||||
note = Column(String)
|
||||
|
||||
is_food = Column(Boolean, default=False)
|
||||
|
||||
# Scaling Items
|
||||
unit_id = Column(Integer, ForeignKey("ingredient_units.id"))
|
||||
unit = orm.relationship(IngredientUnitModel, uselist=False)
|
||||
|
||||
food_id = Column(Integer, ForeignKey("ingredient_foods.id"))
|
||||
food = orm.relationship(IngredientFoodModel, uselist=False)
|
||||
|
||||
label_id = Column(GUID, ForeignKey("multi_purpose_labels.id"))
|
||||
label = orm.relationship(MultiPurposeLabel, uselist=False, back_populates="shopping_list_items")
|
||||
|
||||
class Config:
|
||||
exclude = {"id", "label"}
|
||||
|
||||
@auto_init()
|
||||
def __init__(self, **_) -> None:
|
||||
pass
|
||||
|
||||
|
||||
class ShoppingList(SqlAlchemyBase, BaseMixins):
|
||||
__tablename__ = "shopping_lists"
|
||||
id = Column(Integer, primary_key=True)
|
||||
id = Column(GUID, primary_key=True, default=GUID.generate)
|
||||
|
||||
group_id = Column(GUID, ForeignKey("groups.id"))
|
||||
group = orm.relationship("Group", back_populates="shopping_lists")
|
||||
|
||||
name = Column(String)
|
||||
items: list[ShoppingListItem] = orm.relationship(
|
||||
list_items = 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()
|
||||
@auto_init()
|
||||
def __init__(self, **_) -> None:
|
||||
pass
|
||||
|
||||
22
mealie/db/models/labels.py
Normal file
22
mealie/db/models/labels.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from sqlalchemy import Column, ForeignKey, String, orm
|
||||
|
||||
from mealie.db.models._model_base import BaseMixins, SqlAlchemyBase
|
||||
|
||||
from ._model_utils import auto_init
|
||||
from ._model_utils.guid import GUID
|
||||
|
||||
|
||||
class MultiPurposeLabel(SqlAlchemyBase, BaseMixins):
|
||||
__tablename__ = "multi_purpose_labels"
|
||||
id = Column(GUID, default=GUID.generate, primary_key=True)
|
||||
name = Column(String(255), nullable=False)
|
||||
|
||||
group_id = Column(GUID, ForeignKey("groups.id"))
|
||||
group = orm.relationship("Group", back_populates="labels")
|
||||
|
||||
shopping_list_items = orm.relationship("ShoppingListItem", back_populates="label")
|
||||
foods = orm.relationship("IngredientFoodModel", back_populates="label")
|
||||
|
||||
@auto_init()
|
||||
def __init__(self, **_) -> None:
|
||||
pass
|
||||
@@ -1,6 +1,7 @@
|
||||
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, orm
|
||||
|
||||
from mealie.db.models._model_base import BaseMixins, SqlAlchemyBase
|
||||
from mealie.db.models.labels import MultiPurposeLabel
|
||||
|
||||
from .._model_utils import auto_init
|
||||
from .._model_utils.guid import GUID
|
||||
@@ -27,6 +28,9 @@ class IngredientFoodModel(SqlAlchemyBase, BaseMixins):
|
||||
description = Column(String)
|
||||
ingredients = orm.relationship("RecipeIngredient", back_populates="food")
|
||||
|
||||
label_id = Column(GUID, ForeignKey("multi_purpose_labels.id"))
|
||||
label = orm.relationship(MultiPurposeLabel, uselist=False, back_populates="foods")
|
||||
|
||||
@auto_init()
|
||||
def __init__(self, **_) -> None:
|
||||
pass
|
||||
@@ -51,8 +55,6 @@ class RecipeIngredient(SqlAlchemyBase, BaseMixins):
|
||||
|
||||
reference_id = Column(GUID) # Reference Links
|
||||
|
||||
# Extras
|
||||
|
||||
@auto_init()
|
||||
def __init__(self, **_) -> None:
|
||||
pass
|
||||
|
||||
@@ -26,6 +26,12 @@ class RecipeInstruction(SqlAlchemyBase):
|
||||
|
||||
ingredient_references = orm.relationship("RecipeIngredientRefLink", cascade="all, delete-orphan")
|
||||
|
||||
class Config:
|
||||
exclude = {
|
||||
"id",
|
||||
"ingredient_references",
|
||||
}
|
||||
|
||||
@auto_init()
|
||||
def __init__(self, **_) -> None:
|
||||
pass
|
||||
def __init__(self, ingredient_references, **_) -> None:
|
||||
self.ingredient_references = [RecipeIngredientRefLink(**ref) for ref in ingredient_references]
|
||||
|
||||
Reference in New Issue
Block a user