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:
Hayden
2022-01-08 22:24:34 -09:00
committed by GitHub
parent 86c99b10a2
commit 6db1357064
66 changed files with 3455 additions and 1311 deletions

View File

@@ -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))

View File

@@ -1,5 +1,6 @@
from .event import *
from .group import *
from .labels import *
from .recipe.recipe import *
from .server import *
from .sign_up import *

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View 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

View File

@@ -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

View File

@@ -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]