Feature/restore-recipe-functionality (#810)

* feat(frontend):  add back support for assets

* feat(backend):  add back support for assets

* feat(frontend):  add support for recipe tools

* feat(backend):  add support for recipe tools

* feat(frontend):  add onHand support for recipe toosl

* feat(backend):  add onHand support for backend

* refactor(frontend): ♻️ move items to recipe folder and break apart types

* feat(frontend):  add support for recipe comments

* feat(backend):  Add support for recipe comments

* fix(backend): 💥 disable comments import

* fix(frontend): 🐛 fix rendering issue with titles when moving steps

* add tools to changelog

* fix type errors

Co-authored-by: hay-kot <hay-kot@pm.me>
This commit is contained in:
Hayden
2021-11-22 20:10:48 -09:00
committed by GitHub
parent 912cc6d956
commit 7afdd5b577
43 changed files with 1221 additions and 423 deletions

View File

@@ -13,6 +13,7 @@ from mealie.db.models.recipe.comment import RecipeComment
from mealie.db.models.recipe.ingredient import IngredientFoodModel, IngredientUnitModel
from mealie.db.models.recipe.recipe import RecipeModel
from mealie.db.models.recipe.tag import Tag
from mealie.db.models.recipe.tool import Tool
from mealie.db.models.server.task import ServerTaskModel
from mealie.db.models.sign_up import SignUp
from mealie.db.models.users import LongLiveToken, User
@@ -24,8 +25,9 @@ from mealie.schema.group.group_preferences import ReadGroupPreferences
from mealie.schema.group.invite_token import ReadInviteToken
from mealie.schema.group.webhook import ReadWebhook
from mealie.schema.meal_plan.new_meal import ReadPlanEntry
from mealie.schema.recipe import CommentOut, Recipe, RecipeCategoryResponse, RecipeTagResponse
from mealie.schema.recipe import Recipe, RecipeCategoryResponse, RecipeCommentOut, RecipeTagResponse
from mealie.schema.recipe.recipe_ingredient import IngredientFood, IngredientUnit
from mealie.schema.recipe.recipe_tool import RecipeTool
from mealie.schema.server import ServerTask
from mealie.schema.user import GroupInDB, LongLiveTokenInDB, PrivateUser, SignUpOut
from mealie.schema.user.user_passwords import PrivatePasswordResetToken
@@ -78,8 +80,12 @@ class Database:
return AccessModel(self.session, pk_id, IngredientUnitModel, IngredientUnit)
@cached_property
def comments(self) -> AccessModel[CommentOut, RecipeComment]:
return AccessModel(self.session, pk_id, RecipeComment, CommentOut)
def tools(self) -> AccessModel[RecipeTool, Tool]:
return AccessModel(self.session, pk_id, Tool, RecipeTool)
@cached_property
def comments(self) -> AccessModel[RecipeCommentOut, RecipeComment]:
return AccessModel(self.session, pk_id, RecipeComment, RecipeCommentOut)
@cached_property
def categories(self) -> CategoryDataAccessModel:

View File

@@ -11,6 +11,7 @@ class GUID(TypeDecorator):
"""
impl = CHAR
cache_ok = True
def load_dialect_impl(self, dialect):
if dialect.name == "postgresql":
@@ -31,9 +32,6 @@ class GUID(TypeDecorator):
return "%.32x" % value.int
def process_result_value(self, value, dialect):
if value is None:
return value
else:
if not isinstance(value, uuid.UUID):
value = uuid.UUID(value)
return value
if value is not None and not isinstance(value, uuid.UUID):
value = uuid.UUID(value)
return value

View File

@@ -7,7 +7,7 @@ class ApiExtras(SqlAlchemyBase):
__tablename__ = "api_extras"
id = sa.Column(sa.Integer, primary_key=True)
parent_id = sa.Column(sa.Integer, sa.ForeignKey("recipes.id"))
key_name = sa.Column(sa.String, unique=True)
key_name = sa.Column(sa.String)
value = sa.Column(sa.String)
def __init__(self, key, value) -> None:

View File

@@ -1,37 +1,28 @@
from datetime import datetime
from uuid import uuid4
from sqlalchemy import Column, DateTime, ForeignKey, Integer, String, orm
from sqlalchemy import Column, ForeignKey, Integer, String, orm
from mealie.db.models._model_base import BaseMixins, SqlAlchemyBase
from mealie.db.models.recipe.recipe import RecipeModel
from mealie.db.models.users import User
def generate_uuid():
return str(uuid4())
from mealie.db.models._model_utils import auto_init
from mealie.db.models._model_utils.guid import GUID
class RecipeComment(SqlAlchemyBase, BaseMixins):
__tablename__ = "recipe_comments"
id = Column(Integer, primary_key=True)
uuid = Column(String, unique=True, nullable=False, default=generate_uuid)
parent_id = Column(Integer, ForeignKey("recipes.id"), nullable=False)
recipe = orm.relationship("RecipeModel", back_populates="comments")
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
user = orm.relationship("User", back_populates="comments", single_parent=True, foreign_keys=[user_id])
date_added = Column(DateTime, default=datetime.now)
id = Column(GUID(), primary_key=True, default=uuid4)
text = Column(String)
def __init__(self, recipe_slug, user, text, session, date_added=None, **_) -> None:
self.text = text
self.recipe = RecipeModel.get_ref(session, recipe_slug, "slug")
self.date_added = date_added or datetime.now()
# Recipe Link
recipe_id = Column(Integer, ForeignKey("recipes.id"), nullable=False)
recipe = orm.relationship("RecipeModel", back_populates="comments")
if isinstance(user, dict):
user = user.get("id")
# User Link
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
user = orm.relationship("User", back_populates="comments", single_parent=True, foreign_keys=[user_id])
self.user = User.get_ref(session, user)
@auto_init()
def __init__(self, **_) -> None:
pass
def update(self, text, **_) -> None:
self.text = text

View File

@@ -1,3 +1,5 @@
from uuid import uuid4
from sqlalchemy import Column, ForeignKey, Integer, String, orm
from .._model_base import BaseMixins, SqlAlchemyBase
@@ -17,7 +19,7 @@ class RecipeIngredientRefLink(SqlAlchemyBase, BaseMixins):
class RecipeInstruction(SqlAlchemyBase):
__tablename__ = "recipe_instructions"
id = Column(Integer, primary_key=True)
id = Column(GUID(), primary_key=True, default=uuid4)
parent_id = Column(Integer, ForeignKey("recipes.id"))
position = Column(Integer)
type = Column(String, default="")

View File

@@ -18,7 +18,22 @@ from .note import Note
from .nutrition import Nutrition
from .settings import RecipeSettings
from .tag import Tag, recipes2tags
from .tool import Tool
from .tool import recipes_to_tools
# Decorator function to unpack the extras into a dict
def recipe_extras(func):
def wrapper(*args, **kwargs):
extras = kwargs.pop("extras")
if extras is None:
extras = []
extras = [{"key": key, "value": value} for key, value in extras.items()]
return func(*args, extras=extras, **kwargs)
return wrapper
class RecipeModel(SqlAlchemyBase, BaseMixins):
@@ -52,10 +67,10 @@ class RecipeModel(SqlAlchemyBase, BaseMixins):
recipe_yield = sa.Column(sa.String)
recipeCuisine = sa.Column(sa.String)
tools: list[Tool] = orm.relationship("Tool", cascade="all, delete-orphan")
assets: list[RecipeAsset] = orm.relationship("RecipeAsset", cascade="all, delete-orphan")
assets = orm.relationship("RecipeAsset", cascade="all, delete-orphan")
nutrition: Nutrition = orm.relationship("Nutrition", uselist=False, cascade="all, delete-orphan")
recipe_category: list = orm.relationship("Category", secondary=recipes2categories, back_populates="recipes")
tools = orm.relationship("Tool", secondary=recipes_to_tools, back_populates="recipes")
recipe_ingredient: list[RecipeIngredient] = orm.relationship(
"RecipeIngredient",
@@ -88,45 +103,36 @@ class RecipeModel(SqlAlchemyBase, BaseMixins):
get_attr = "slug"
exclude = {
"assets",
"extras",
"notes",
"nutrition",
"recipe_ingredient",
"settings",
"tools",
}
@validates("name")
def validate_name(self, key, name):
def validate_name(self, _, name):
assert name != ""
return name
@recipe_extras
@auto_init()
def __init__(
self,
session,
assets: list = None,
extras: dict = None,
notes: list[dict] = None,
nutrition: dict = None,
recipe_ingredient: list[str] = None,
settings: dict = None,
tools: list[str] = None,
**_,
) -> None:
self.nutrition = Nutrition(**nutrition) if nutrition else Nutrition()
self.tools = [Tool(tool=x) for x in tools] if tools else []
self.recipe_ingredient = [RecipeIngredient(**ingr, session=session) for ingr in recipe_ingredient]
self.assets = [RecipeAsset(**a) for a in assets]
# self.recipe_instructions = [
# RecipeInstruction(text=instruc.get("text"), title=instruc.get("title"), type=instruc.get("@type", None))
# for instruc in recipe_instructions
# ]
# Mealie Specific
self.settings = RecipeSettings(**settings) if settings else RecipeSettings()
self.notes = [Note(**note) for note in notes]
self.extras = [ApiExtras(key=key, value=value) for key, value in extras.items()]
# Time Stampes
self.date_updated = datetime.datetime.now()

View File

@@ -1,13 +1,23 @@
import sqlalchemy as sa
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, Table, orm
from mealie.db.models._model_base import SqlAlchemyBase
from mealie.db.models._model_base import BaseMixins, SqlAlchemyBase
from mealie.db.models._model_utils import auto_init
recipes_to_tools = Table(
"recipes_to_tools",
SqlAlchemyBase.metadata,
Column("recipe_id", Integer, ForeignKey("recipes.id")),
Column("tool_id", Integer, ForeignKey("tools.id")),
)
class Tool(SqlAlchemyBase):
class Tool(SqlAlchemyBase, BaseMixins):
__tablename__ = "tools"
id = sa.Column(sa.Integer, primary_key=True)
parent_id = sa.Column(sa.Integer, sa.ForeignKey("recipes.id"))
tool = sa.Column(sa.String)
name = Column(String, index=True, unique=True, nullable=False)
on_hand = Column(Boolean, default=False)
recipes = orm.relationship("RecipeModel", secondary=recipes_to_tools, back_populates="tools")
def __init__(self, tool) -> None:
self.tool = tool
@auto_init()
def __init__(self, name, on_hand, **_) -> None:
self.on_hand = on_hand
self.name = name