mirror of
https://github.com/mealie-recipes/mealie.git
synced 2026-01-05 08:31:25 -05:00
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:
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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="")
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user