mirror of
https://github.com/mealie-recipes/mealie.git
synced 2026-02-07 00:13:12 -05:00
feat: Remove "Is Food" and "Disable Amounts" Flags (#5684)
Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
"""empty migration to fix food flag data
|
||||
|
||||
Revision ID: d7b3ce6fa31a
|
||||
Revises: 7cf3054cbbcc
|
||||
Create Date: 2025-07-11 20:17:10.543280
|
||||
|
||||
"""
|
||||
|
||||
from textwrap import dedent
|
||||
|
||||
from alembic import op
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "d7b3ce6fa31a"
|
||||
down_revision: str | None = "7cf3054cbbcc"
|
||||
branch_labels: str | tuple[str, ...] | None = None
|
||||
depends_on: str | tuple[str, ...] | None = None
|
||||
|
||||
|
||||
def is_postgres():
|
||||
return op.get_context().dialect.name == "postgresql"
|
||||
|
||||
|
||||
def upgrade():
|
||||
# Update recipes with disable_amount=True: set ingredient quantities of 1 to 0
|
||||
op.execute(
|
||||
dedent(
|
||||
f"""
|
||||
UPDATE recipes_ingredients
|
||||
SET quantity = 0
|
||||
WHERE quantity = 1
|
||||
AND recipe_id IN (
|
||||
SELECT r.id
|
||||
FROM recipes r
|
||||
JOIN recipe_settings rs ON r.id = rs.recipe_id
|
||||
WHERE rs.disable_amount = {"true" if is_postgres() else "1"}
|
||||
)
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def downgrade():
|
||||
pass
|
||||
@@ -31,6 +31,8 @@ class HouseholdPreferencesModel(SqlAlchemyBase, BaseMixins):
|
||||
recipe_show_assets: Mapped[bool | None] = mapped_column(sa.Boolean, default=False)
|
||||
recipe_landscape_view: Mapped[bool | None] = mapped_column(sa.Boolean, default=False)
|
||||
recipe_disable_comments: Mapped[bool | None] = mapped_column(sa.Boolean, default=False)
|
||||
|
||||
# Deprecated
|
||||
recipe_disable_amount: Mapped[bool | None] = mapped_column(sa.Boolean, default=True)
|
||||
|
||||
@auto_init()
|
||||
|
||||
@@ -65,7 +65,6 @@ class ShoppingListItem(SqlAlchemyBase, BaseMixins):
|
||||
quantity: Mapped[float | None] = mapped_column(Float, default=1)
|
||||
note: Mapped[str | None] = mapped_column(String)
|
||||
|
||||
is_food: Mapped[bool | None] = mapped_column(Boolean, default=False)
|
||||
extras: Mapped[list[ShoppingListItemExtras]] = orm.relationship(
|
||||
"ShoppingListItemExtras", cascade="all, delete-orphan"
|
||||
)
|
||||
@@ -88,6 +87,9 @@ class ShoppingListItem(SqlAlchemyBase, BaseMixins):
|
||||
)
|
||||
model_config = ConfigDict(exclude={"label", "food", "unit"})
|
||||
|
||||
# Deprecated
|
||||
is_food: Mapped[bool | None] = mapped_column(Boolean, default=False)
|
||||
|
||||
@api_extras
|
||||
@auto_init()
|
||||
def __init__(self, **_) -> None:
|
||||
|
||||
@@ -13,10 +13,12 @@ class RecipeSettings(SqlAlchemyBase):
|
||||
show_nutrition: Mapped[bool | None] = mapped_column(sa.Boolean)
|
||||
show_assets: Mapped[bool | None] = mapped_column(sa.Boolean)
|
||||
landscape_view: Mapped[bool | None] = mapped_column(sa.Boolean)
|
||||
disable_amount: Mapped[bool | None] = mapped_column(sa.Boolean, default=True)
|
||||
disable_comments: Mapped[bool | None] = mapped_column(sa.Boolean, default=False)
|
||||
locked: Mapped[bool | None] = mapped_column(sa.Boolean, default=False)
|
||||
|
||||
# Deprecated
|
||||
disable_amount: Mapped[bool | None] = mapped_column(sa.Boolean, default=True)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
public=True,
|
||||
|
||||
@@ -440,7 +440,6 @@ class RepositoryRecipes(HouseholdRepositoryGeneric[Recipe, RecipeModel]):
|
||||
)
|
||||
q = (
|
||||
q.join(settings_alias, self.model.settings)
|
||||
.filter(settings_alias.disable_amount == False) # noqa: E712 - required for SQLAlchemy comparison
|
||||
.outerjoin(unmatched_foods_query, self.model.id == unmatched_foods_query.c.recipe_id)
|
||||
.outerjoin(total_user_foods_query, self.model.id == total_user_foods_query.c.recipe_id)
|
||||
.filter(
|
||||
|
||||
@@ -101,22 +101,18 @@ def content_with_meta(group_slug: str, recipe: Recipe) -> str:
|
||||
image_url = "https://raw.githubusercontent.com/mealie-recipes/mealie/9571816ac4eed5beacfc0abf6c03eff1427fd0eb/frontend/static/icons/android-chrome-512x512.png"
|
||||
|
||||
ingredients: list[str] = []
|
||||
if recipe.settings.disable_amount: # type: ignore
|
||||
ingredients = [escape(i.note) for i in recipe.recipe_ingredient if i.note]
|
||||
for ing in recipe.recipe_ingredient:
|
||||
s = ""
|
||||
if ing.quantity:
|
||||
s += f"{ing.quantity} "
|
||||
if ing.unit:
|
||||
s += f"{ing.unit.name} "
|
||||
if ing.food:
|
||||
s += f"{ing.food.name} "
|
||||
if ing.note:
|
||||
s += f"{ing.note}"
|
||||
|
||||
else:
|
||||
for ing in recipe.recipe_ingredient:
|
||||
s = ""
|
||||
if ing.quantity:
|
||||
s += f"{ing.quantity} "
|
||||
if ing.unit:
|
||||
s += f"{ing.unit.name} "
|
||||
if ing.food:
|
||||
s += f"{ing.food.name} "
|
||||
if ing.note:
|
||||
s += f"{ing.note}"
|
||||
|
||||
ingredients.append(escape(s))
|
||||
ingredients.append(escape(s))
|
||||
|
||||
nutrition: dict[str, str | None] = recipe.nutrition.model_dump(by_alias=True) if recipe.nutrition else {}
|
||||
for k, v in nutrition.items():
|
||||
|
||||
@@ -66,7 +66,6 @@ class ShoppingListItemBase(RecipeIngredientBase):
|
||||
label_id: UUID4 | None = None
|
||||
unit_id: UUID4 | None = None
|
||||
|
||||
is_food: bool = False
|
||||
extras: dict | None = {}
|
||||
|
||||
@field_validator("extras", mode="before")
|
||||
|
||||
@@ -18,7 +18,6 @@ class UpdateHouseholdPreferences(MealieModel):
|
||||
recipe_show_assets: bool = False
|
||||
recipe_landscape_view: bool = False
|
||||
recipe_disable_comments: bool = False
|
||||
recipe_disable_amount: bool = True
|
||||
|
||||
|
||||
class CreateHouseholdPreferences(UpdateHouseholdPreferences): ...
|
||||
|
||||
@@ -6,7 +6,7 @@ from pathlib import Path
|
||||
from typing import Annotated, Any, ClassVar
|
||||
from uuid import uuid4
|
||||
|
||||
from pydantic import UUID4, BaseModel, ConfigDict, Field, field_validator, model_validator
|
||||
from pydantic import UUID4, BaseModel, ConfigDict, Field, field_validator
|
||||
from pydantic_core.core_schema import ValidationInfo
|
||||
from slugify import slugify
|
||||
from sqlalchemy import Select, desc, func, or_, select, text
|
||||
@@ -228,18 +228,6 @@ class Recipe(RecipeSummary):
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
@model_validator(mode="after")
|
||||
def calculate_missing_food_flags_and_format_display(self):
|
||||
disable_amount = self.settings.disable_amount if self.settings else True
|
||||
for ingredient in self.recipe_ingredient:
|
||||
ingredient.disable_amount = disable_amount
|
||||
ingredient.is_food = not ingredient.disable_amount
|
||||
|
||||
# recalculate the display property, since it depends on the disable_amount flag
|
||||
ingredient.display = ingredient._format_display()
|
||||
|
||||
return self
|
||||
|
||||
@field_validator("slug", mode="before")
|
||||
def validate_slug(slug: str, info: ValidationInfo):
|
||||
if not info.data.get("name"):
|
||||
|
||||
@@ -152,13 +152,11 @@ class IngredientUnit(CreateIngredientUnit):
|
||||
|
||||
|
||||
class RecipeIngredientBase(MealieModel):
|
||||
quantity: NoneFloat = 1
|
||||
quantity: NoneFloat = 0
|
||||
unit: IngredientUnit | CreateIngredientUnit | None = None
|
||||
food: IngredientFood | CreateIngredientFood | None = None
|
||||
note: str | None = ""
|
||||
|
||||
is_food: bool | None = None
|
||||
disable_amount: bool | None = None
|
||||
display: str = ""
|
||||
"""
|
||||
How the ingredient should be displayed
|
||||
@@ -166,20 +164,6 @@ class RecipeIngredientBase(MealieModel):
|
||||
Automatically calculated after the object is created, unless overwritten
|
||||
"""
|
||||
|
||||
@model_validator(mode="after")
|
||||
def calculate_missing_food_flags(self):
|
||||
# calculate missing is_food and disable_amount values
|
||||
# we can't do this in a validator since they depend on each other
|
||||
if self.is_food is None and self.disable_amount is not None:
|
||||
self.is_food = not self.disable_amount
|
||||
elif self.disable_amount is None and self.is_food is not None:
|
||||
self.disable_amount = not self.is_food
|
||||
elif self.is_food is None and self.disable_amount is None:
|
||||
self.is_food = bool(self.food)
|
||||
self.disable_amount = not self.is_food
|
||||
|
||||
return self
|
||||
|
||||
@model_validator(mode="after")
|
||||
def format_display(self):
|
||||
if not self.display:
|
||||
@@ -266,28 +250,17 @@ class RecipeIngredientBase(MealieModel):
|
||||
def _format_display(self) -> str:
|
||||
components = []
|
||||
|
||||
use_food = True
|
||||
if self.is_food is False:
|
||||
use_food = False
|
||||
elif self.disable_amount is True:
|
||||
use_food = False
|
||||
|
||||
# ingredients with no food come across with a qty of 1, which looks weird
|
||||
# e.g. "1 2 tbsp of olive oil"
|
||||
if self.quantity and (use_food or self.quantity != 1):
|
||||
if self.quantity:
|
||||
components.append(self._format_quantity_for_display())
|
||||
|
||||
if not use_food:
|
||||
components.append(self.note or "")
|
||||
else:
|
||||
if self.quantity and self.unit:
|
||||
components.append(self._format_unit_for_display())
|
||||
if self.quantity and self.unit:
|
||||
components.append(self._format_unit_for_display())
|
||||
|
||||
if self.food:
|
||||
components.append(self._format_food_for_display())
|
||||
if self.food:
|
||||
components.append(self._format_food_for_display())
|
||||
|
||||
if self.note:
|
||||
components.append(self.note)
|
||||
if self.note:
|
||||
components.append(self.note)
|
||||
|
||||
return " ".join(components).strip()
|
||||
|
||||
@@ -299,7 +272,6 @@ class IngredientUnitPagination(PaginationBase):
|
||||
class RecipeIngredient(RecipeIngredientBase):
|
||||
title: str | None = None
|
||||
original_text: str | None = None
|
||||
disable_amount: bool = True
|
||||
|
||||
# Ref is used as a way to distinguish between an individual ingredient on the frontend
|
||||
# It is required for the reorder and section titles to function properly because of how
|
||||
|
||||
@@ -9,6 +9,5 @@ class RecipeSettings(MealieModel):
|
||||
show_assets: bool = False
|
||||
landscape_view: bool = False
|
||||
disable_comments: bool = True
|
||||
disable_amount: bool = True
|
||||
locked: bool = False
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
@@ -312,12 +312,10 @@ class ShoppingListService:
|
||||
list_items: list[ShoppingListItemCreate] = []
|
||||
for ingredient in recipe_ingredients:
|
||||
if isinstance(ingredient.food, IngredientFood):
|
||||
is_food = True
|
||||
food_id = ingredient.food.id
|
||||
label_id = ingredient.food.label_id
|
||||
|
||||
else:
|
||||
is_food = False
|
||||
food_id = None
|
||||
label_id = None
|
||||
|
||||
@@ -329,7 +327,6 @@ class ShoppingListService:
|
||||
|
||||
new_item = ShoppingListItemCreate(
|
||||
shopping_list_id=list_id,
|
||||
is_food=is_food,
|
||||
note=ingredient.note,
|
||||
quantity=ingredient.quantity * scale if ingredient.quantity else 0,
|
||||
food_id=food_id,
|
||||
|
||||
@@ -177,7 +177,6 @@ class BaseMigrator(BaseService):
|
||||
show_assets=self.household.preferences.recipe_show_assets,
|
||||
landscape_view=self.household.preferences.recipe_landscape_view,
|
||||
disable_comments=self.household.preferences.recipe_disable_comments,
|
||||
disable_amount=self.household.preferences.recipe_disable_amount,
|
||||
)
|
||||
|
||||
for recipe in validated_recipes:
|
||||
|
||||
@@ -36,7 +36,6 @@ class BruteForceParser(ABCIngredientParser):
|
||||
ingredient=RecipeIngredient(
|
||||
unit=CreateIngredientUnit(name=bfi.unit),
|
||||
food=CreateIngredientFood(name=bfi.food),
|
||||
disable_amount=False,
|
||||
quantity=bfi.amount,
|
||||
note=bfi.note,
|
||||
),
|
||||
@@ -151,7 +150,6 @@ class NLPParser(ABCIngredientParser):
|
||||
quantity=qty,
|
||||
unit=CreateIngredientUnit(name=unit) if unit else None,
|
||||
food=CreateIngredientFood(name=food) if food else None,
|
||||
disable_amount=False,
|
||||
note=note,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -173,7 +173,6 @@ class RecipeService(RecipeServiceBase):
|
||||
show_assets=self.household.preferences.recipe_show_assets,
|
||||
landscape_view=self.household.preferences.recipe_landscape_view,
|
||||
disable_comments=self.household.preferences.recipe_disable_comments,
|
||||
disable_amount=self.household.preferences.recipe_disable_amount,
|
||||
)
|
||||
else:
|
||||
data.settings = RecipeSettings()
|
||||
|
||||
@@ -74,7 +74,6 @@ class RegistrationService:
|
||||
recipe_show_assets=self.registration.advanced,
|
||||
recipe_landscape_view=False,
|
||||
recipe_disable_comments=self.registration.advanced,
|
||||
recipe_disable_amount=self.registration.advanced,
|
||||
)
|
||||
return HouseholdService.create_household(group_repos, household_data, household_preferences)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user