From 245ca5fe3b94d697e6ce54e1549da821c2c30639 Mon Sep 17 00:00:00 2001 From: Michael Genson <71845777+michael-genson@users.noreply.github.com> Date: Thu, 31 Jul 2025 10:36:24 -0500 Subject: [PATCH] feat: Remove "Is Food" and "Disable Amounts" Flags (#5684) Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com> --- dev/scripts/all_recipes_stress_test.py | 14 ----- docs/docs/overrides/api.html | 2 +- .../Household/HouseholdPreferencesEditor.vue | 5 -- .../Recipe/RecipeDialogAddToShoppingList.vue | 33 +++++------ .../Domain/Recipe/RecipeIngredientEditor.vue | 18 +----- .../Recipe/RecipeIngredientListItem.vue | 4 +- .../Domain/Recipe/RecipeIngredients.vue | 5 +- .../Domain/Recipe/RecipePage/RecipePage.vue | 1 - .../RecipePageIngredientEditor.vue | 22 ++++--- .../RecipePageIngredientToolsView.vue | 1 - .../RecipePageInstructions.vue | 8 +-- .../RecipePageParts/RecipePageScale.vue | 13 ++++- .../Domain/Recipe/RecipePrintView.vue | 2 +- .../Domain/Recipe/RecipeSettingsSwitches.vue | 1 - .../Domain/ShoppingList/ShoppingListItem.vue | 6 +- .../ShoppingList/ShoppingListItemEditor.vue | 11 +--- .../use-extract-ingredient-references.ts | 8 +-- .../recipes/use-recipe-ingredients.test.ts | 32 +++++------ .../recipes/use-recipe-ingredients.ts | 15 +---- frontend/lang/messages/en-US.json | 1 + frontend/lib/api/types/household.ts | 18 ------ frontend/lib/api/types/recipe.ts | 5 -- .../r/[slug]/ingredient-parser.vue | 5 -- frontend/pages/group/data/recipes.vue | 1 - frontend/pages/household/index.vue | 6 -- frontend/pages/shopping-lists/[id].vue | 27 +-------- ...a_empty_migration_to_fix_food_flag_data.py | 45 +++++++++++++++ mealie/db/models/household/preferences.py | 2 + mealie/db/models/household/shopping_list.py | 4 +- mealie/db/models/recipe/settings.py | 4 +- mealie/repos/repository_recipes.py | 1 - mealie/routes/spa/__init__.py | 26 ++++----- .../schema/household/group_shopping_list.py | 1 - .../schema/household/household_preferences.py | 1 - mealie/schema/recipe/recipe.py | 14 +---- mealie/schema/recipe/recipe_ingredient.py | 44 +++----------- mealie/schema/recipe/recipe_settings.py | 1 - .../household_services/shopping_lists.py | 3 - mealie/services/migrations/_migration_base.py | 1 - .../parser_services/ingredient_parser.py | 2 - mealie/services/recipe/recipe_service.py | 1 - .../user_services/registration_service.py | 1 - tests/fixtures/fixture_recipe.py | 12 ++-- tests/fixtures/fixture_shopping_lists.py | 1 - .../test_admin_household_actions.py | 1 - .../test_public_recipes.py | 1 - .../test_recipe_ingredients.py | 48 +++++++--------- .../test_recipe_suggestions.py | 57 +------------------ .../test_shopping_list_ingredient.py | 2 - 49 files changed, 173 insertions(+), 364 deletions(-) create mode 100644 mealie/alembic/versions/2025-07-11-20.17.10_d7b3ce6fa31a_empty_migration_to_fix_food_flag_data.py diff --git a/dev/scripts/all_recipes_stress_test.py b/dev/scripts/all_recipes_stress_test.py index 0ce27cb13..ffdded3a0 100644 --- a/dev/scripts/all_recipes_stress_test.py +++ b/dev/scripts/all_recipes_stress_test.py @@ -44,7 +44,6 @@ def recipe_data(name: str, slug: str, id: str, userId: str, groupId: str) -> dic "note": "1 cup unsalted butter, cut into cubes", "unit": None, "food": None, - "disableAmount": True, "quantity": 1, "originalText": None, "referenceId": "ea3b6702-9532-4fbc-a40b-f99917831c26", @@ -54,7 +53,6 @@ def recipe_data(name: str, slug: str, id: str, userId: str, groupId: str) -> dic "note": "1 cup light brown sugar", "unit": None, "food": None, - "disableAmount": True, "quantity": 1, "originalText": None, "referenceId": "c5bbfefb-1e23-4ffd-af88-c0363a0fae82", @@ -64,7 +62,6 @@ def recipe_data(name: str, slug: str, id: str, userId: str, groupId: str) -> dic "note": "1/2 cup granulated white sugar", "unit": None, "food": None, - "disableAmount": True, "quantity": 1, "originalText": None, "referenceId": "034f481b-c426-4a17-b983-5aea9be4974b", @@ -74,7 +71,6 @@ def recipe_data(name: str, slug: str, id: str, userId: str, groupId: str) -> dic "note": "2 large eggs", "unit": None, "food": None, - "disableAmount": True, "quantity": 1, "originalText": None, "referenceId": "37c1f796-3bdb-4856-859f-dbec90bc27e4", @@ -84,7 +80,6 @@ def recipe_data(name: str, slug: str, id: str, userId: str, groupId: str) -> dic "note": "2 tsp vanilla extract", "unit": None, "food": None, - "disableAmount": True, "quantity": 1, "originalText": None, "referenceId": "85561ace-f249-401d-834c-e600a2f6280e", @@ -94,7 +89,6 @@ def recipe_data(name: str, slug: str, id: str, userId: str, groupId: str) -> dic "note": "1/2 cup creamy peanut butter", "unit": None, "food": None, - "disableAmount": True, "quantity": 1, "originalText": None, "referenceId": "ac91bda0-e8a8-491a-976a-ae4e72418cfd", @@ -104,7 +98,6 @@ def recipe_data(name: str, slug: str, id: str, userId: str, groupId: str) -> dic "note": "1 tsp cornstarch", "unit": None, "food": None, - "disableAmount": True, "quantity": 1, "originalText": None, "referenceId": "4d1256b3-115e-4475-83cd-464fbc304cb0", @@ -114,7 +107,6 @@ def recipe_data(name: str, slug: str, id: str, userId: str, groupId: str) -> dic "note": "1 tsp baking soda", "unit": None, "food": None, - "disableAmount": True, "quantity": 1, "originalText": None, "referenceId": "64627441-39f9-4ee3-8494-bafe36451d12", @@ -124,7 +116,6 @@ def recipe_data(name: str, slug: str, id: str, userId: str, groupId: str) -> dic "note": "1/2 tsp salt", "unit": None, "food": None, - "disableAmount": True, "quantity": 1, "originalText": None, "referenceId": "7ae212d0-3cd1-44b0-899e-ec5bd91fd384", @@ -134,7 +125,6 @@ def recipe_data(name: str, slug: str, id: str, userId: str, groupId: str) -> dic "note": "1 cup cake flour", "unit": None, "food": None, - "disableAmount": True, "quantity": 1, "originalText": None, "referenceId": "06967994-8548-4952-a8cc-16e8db228ebd", @@ -144,7 +134,6 @@ def recipe_data(name: str, slug: str, id: str, userId: str, groupId: str) -> dic "note": "2 cups all-purpose flour", "unit": None, "food": None, - "disableAmount": True, "quantity": 1, "originalText": None, "referenceId": "bdb33b23-c767-4465-acf8-3b8e79eb5691", @@ -154,7 +143,6 @@ def recipe_data(name: str, slug: str, id: str, userId: str, groupId: str) -> dic "note": "2 cups peanut butter chips", "unit": None, "food": None, - "disableAmount": True, "quantity": 1, "originalText": None, "referenceId": "12ba0af8-affd-4fb2-9cca-6f1b3e8d3aef", @@ -164,7 +152,6 @@ def recipe_data(name: str, slug: str, id: str, userId: str, groupId: str) -> dic "note": "1½ cups Reese's Pieces candies", "unit": None, "food": None, - "disableAmount": True, "quantity": 1, "originalText": None, "referenceId": "4bdc0598-a3eb-41ee-8af0-4da9348fbfe2", @@ -221,7 +208,6 @@ def recipe_data(name: str, slug: str, id: str, userId: str, groupId: str) -> dic "showAssets": False, "landscapeView": False, "disableComments": False, - "disableAmount": True, "locked": False, }, "assets": [], diff --git a/docs/docs/overrides/api.html b/docs/docs/overrides/api.html index cc74f1312..64a6e5cf4 100644 --- a/docs/docs/overrides/api.html +++ b/docs/docs/overrides/api.html @@ -14,7 +14,7 @@
diff --git a/frontend/components/Domain/Household/HouseholdPreferencesEditor.vue b/frontend/components/Domain/Household/HouseholdPreferencesEditor.vue index 32e433b31..31fe28f0e 100644 --- a/frontend/components/Domain/Household/HouseholdPreferencesEditor.vue +++ b/frontend/components/Domain/Household/HouseholdPreferencesEditor.vue @@ -79,11 +79,6 @@ const recipePreferences: Preference[] = [ label: i18n.t("group.disable-users-from-commenting-on-recipes"), description: i18n.t("group.disable-users-from-commenting-on-recipes-description"), }, - { - key: "recipeDisableAmount", - label: i18n.t("group.disable-organizing-recipe-ingredients-by-units-and-food"), - description: i18n.t("group.disable-organizing-recipe-ingredients-by-units-and-food-description"), - }, ]; const allDays = [ diff --git a/frontend/components/Domain/Recipe/RecipeDialogAddToShoppingList.vue b/frontend/components/Domain/Recipe/RecipeDialogAddToShoppingList.vue index 4f4738e9d..a55fd14c1 100644 --- a/frontend/components/Domain/Recipe/RecipeDialogAddToShoppingList.vue +++ b/frontend/components/Domain/Recipe/RecipeDialogAddToShoppingList.vue @@ -130,20 +130,23 @@ .ingredients[i] .checked" > - -
- -
+ + + +
+ +
+
+
@@ -188,7 +191,6 @@ export interface RecipeWithScale extends Recipe { export interface ShoppingListIngredient { checked: boolean; ingredient: RecipeIngredient; - disableAmount: boolean; } export interface ShoppingListIngredientSection { @@ -290,7 +292,6 @@ async function consolidateRecipesIntoSections(recipes: RecipeWithScale[]) { return { checked: !householdsWithFood.includes(userHousehold.value), ingredient: ing, - disableAmount: recipe.settings?.disableAmount || false, }; }); diff --git a/frontend/components/Domain/Recipe/RecipeIngredientEditor.vue b/frontend/components/Domain/Recipe/RecipeIngredientEditor.vue index 01e1f4084..920641f95 100644 --- a/frontend/components/Domain/Recipe/RecipeIngredientEditor.vue +++ b/frontend/components/Domain/Recipe/RecipeIngredientEditor.vue @@ -17,7 +17,6 @@ class="d-flex flex-wrap my-1" > - - + /> ({ required: true }); defineProps({ - disableAmount: { - type: Boolean, - default: false, - }, unitError: { type: Boolean, default: false, diff --git a/frontend/components/Domain/Recipe/RecipeIngredientListItem.vue b/frontend/components/Domain/Recipe/RecipeIngredientListItem.vue index bdf2604b6..f4a50e6cb 100644 --- a/frontend/components/Domain/Recipe/RecipeIngredientListItem.vue +++ b/frontend/components/Domain/Recipe/RecipeIngredientListItem.vue @@ -34,16 +34,14 @@ import { useParsedIngredientText } from "~/composables/recipes"; interface Props { ingredient: RecipeIngredient; - disableAmount?: boolean; scale?: number; } const props = withDefaults(defineProps(), { - disableAmount: false, scale: 1, }); const parsedIng = computed(() => { - return useParsedIngredientText(props.ingredient, props.disableAmount, props.scale); + return useParsedIngredientText(props.ingredient, props.scale); }); diff --git a/frontend/components/Domain/Recipe/RecipeIngredients.vue b/frontend/components/Domain/Recipe/RecipeIngredients.vue index 345a6a1de..06ac087ca 100644 --- a/frontend/components/Domain/Recipe/RecipeIngredients.vue +++ b/frontend/components/Domain/Recipe/RecipeIngredients.vue @@ -43,7 +43,6 @@ @@ -60,13 +59,11 @@ import type { RecipeIngredient } from "~/lib/api/types/recipe"; interface Props { value?: RecipeIngredient[]; - disableAmount?: boolean; scale?: number; isCookMode?: boolean; } const props = withDefaults(defineProps(), { value: () => [], - disableAmount: false, scale: 1, isCookMode: false, }); @@ -89,7 +86,7 @@ const ingredientCopyText = computed(() => { components.push(`[${ingredient.title}]`); } - components.push(parseIngredientText(ingredient, props.disableAmount, props.scale, false)); + components.push(parseIngredientText(ingredient, props.scale, false)); }); return components.join("\n"); diff --git a/frontend/components/Domain/Recipe/RecipePage/RecipePage.vue b/frontend/components/Domain/Recipe/RecipePage/RecipePage.vue index ae59941d5..b55487ed0 100644 --- a/frontend/components/Domain/Recipe/RecipePage/RecipePage.vue +++ b/frontend/components/Domain/Recipe/RecipePage/RecipePage.vue @@ -141,7 +141,6 @@ diff --git a/frontend/components/Domain/Recipe/RecipePage/RecipePageParts/RecipePageIngredientEditor.vue b/frontend/components/Domain/Recipe/RecipePage/RecipePageParts/RecipePageIngredientEditor.vue index 010b1d941..302b16c9d 100644 --- a/frontend/components/Domain/Recipe/RecipePage/RecipePageParts/RecipePageIngredientEditor.vue +++ b/frontend/components/Domain/Recipe/RecipePage/RecipePageParts/RecipePageIngredientEditor.vue @@ -1,9 +1,14 @@ @@ -323,7 +323,6 @@ return step.ingredientReferences.map((ref) => ref.referenceId).includes(ing.referenceId || '') })" :scale="scale" - :disable-amount="recipe.settings.disableAmount" :is-cook-mode="isCookMode" /> @@ -552,7 +551,6 @@ function autoSetReferences() { props.recipe.recipeIngredient, activeRefs.value, activeText.value, - props.recipe.settings.disableAmount, ).forEach((ingredient: string) => activeRefs.value.push(ingredient)); } @@ -574,7 +572,7 @@ function getIngredientByRefId(refId: string | undefined) { const ing = ingredientLookup.value[refId]; if (!ing) return ""; - return parseIngredientText(ing, props.recipe.settings.disableAmount, props.scale); + return parseIngredientText(ing, props.scale); } // =============================================================== diff --git a/frontend/components/Domain/Recipe/RecipePage/RecipePageParts/RecipePageScale.vue b/frontend/components/Domain/Recipe/RecipePage/RecipePageParts/RecipePageScale.vue index f886effa2..52e4af3c1 100644 --- a/frontend/components/Domain/Recipe/RecipePage/RecipePageParts/RecipePageScale.vue +++ b/frontend/components/Domain/Recipe/RecipePage/RecipePageParts/RecipePageScale.vue @@ -4,7 +4,7 @@ v-if="!isEditMode" v-model.number="scale" :recipe-servings="recipeServings" - :edit-scale="!recipe.settings.disableAmount && !isEditMode" + :edit-scale="hasFoodOrUnit && !isEditMode" /> @@ -24,4 +24,15 @@ const { isEditMode } = usePageState(props.recipe.slug); const recipeServings = computed(() => { return props.recipe.recipeServings || props.recipe.recipeYieldQuantity || 1; }); + +const hasFoodOrUnit = computed(() => { + if (props.recipe.recipeIngredient) { + for (const ingredient of props.recipe.recipeIngredient) { + if (ingredient.food || ingredient.unit) { + return true; + } + } + } + return false; +}); diff --git a/frontend/components/Domain/Recipe/RecipePrintView.vue b/frontend/components/Domain/Recipe/RecipePrintView.vue index 964256afb..68168dcaa 100644 --- a/frontend/components/Domain/Recipe/RecipePrintView.vue +++ b/frontend/components/Domain/Recipe/RecipePrintView.vue @@ -321,7 +321,7 @@ const hasNotes = computed(() => { }); function parseText(ingredient: RecipeIngredient) { - return parseIngredientText(ingredient, props.recipe.settings?.disableAmount || false, props.scale); + return parseIngredientText(ingredient, props.scale); } diff --git a/frontend/components/Domain/Recipe/RecipeSettingsSwitches.vue b/frontend/components/Domain/Recipe/RecipeSettingsSwitches.vue index 8bfd49852..a2dc3f55a 100644 --- a/frontend/components/Domain/Recipe/RecipeSettingsSwitches.vue +++ b/frontend/components/Domain/Recipe/RecipeSettingsSwitches.vue @@ -31,7 +31,6 @@ const labels: Record = { showAssets: i18n.t("asset.show-assets"), landscapeView: i18n.t("recipe.landscape-view-coming-soon"), disableComments: i18n.t("recipe.disable-comments"), - disableAmount: i18n.t("recipe.disable-amount"), locked: i18n.t("recipe.locked"), }; diff --git a/frontend/components/Domain/ShoppingList/ShoppingListItem.vue b/frontend/components/Domain/ShoppingList/ShoppingListItem.vue index f1cd028c9..fe9d8c52a 100644 --- a/frontend/components/Domain/ShoppingList/ShoppingListItem.vue +++ b/frontend/components/Domain/ShoppingList/ShoppingListItem.vue @@ -22,10 +22,7 @@ :class="listItem.checked ? 'strike-through' : ''" style="min-width: 0;" > - + @@ -172,7 +169,6 @@ @save="save" @cancel="toggleEdit(false)" @delete="$emit('delete')" - @toggle-foods="localListItem.isFood = !localListItem.isFood" /> diff --git a/frontend/components/Domain/ShoppingList/ShoppingListItemEditor.vue b/frontend/components/Domain/ShoppingList/ShoppingListItemEditor.vue index 8ed581310..e10a70332 100644 --- a/frontend/components/Domain/ShoppingList/ShoppingListItemEditor.vue +++ b/frontend/components/Domain/ShoppingList/ShoppingListItemEditor.vue @@ -2,7 +2,7 @@
-
+
@@ -26,9 +26,6 @@ />
-
- -
diff --git a/frontend/composables/recipe-page/use-extract-ingredient-references.ts b/frontend/composables/recipe-page/use-extract-ingredient-references.ts index 4504bf612..3fa16a674 100644 --- a/frontend/composables/recipe-page/use-extract-ingredient-references.ts +++ b/frontend/composables/recipe-page/use-extract-ingredient-references.ts @@ -18,8 +18,8 @@ function removeStartingPunctuation(word: string): string { return word.replace(punctuationAtBeginning, ""); } -function ingredientMatchesWord(ingredient: RecipeIngredient, word: string, recipeIngredientAmountsDisabled: boolean) { - const searchText = parseIngredientText(ingredient, recipeIngredientAmountsDisabled); +function ingredientMatchesWord(ingredient: RecipeIngredient, word: string) { + const searchText = parseIngredientText(ingredient); return searchText.toLowerCase().includes(word.toLowerCase()); } @@ -39,7 +39,7 @@ function isBlackListedWord(word: string) { return blackListedText.includes(word) || word.match(blackListedRegexMatch); } -export function useExtractIngredientReferences(recipeIngredients: RecipeIngredient[], activeRefs: string[], text: string, recipeIngredientAmountsDisabled: boolean): Set { +export function useExtractIngredientReferences(recipeIngredients: RecipeIngredient[], activeRefs: string[], text: string): Set { const availableIngredients = recipeIngredients .filter(ingredient => ingredient.referenceId !== undefined) .filter(ingredient => !activeRefs.includes(ingredient.referenceId as string)); @@ -50,7 +50,7 @@ export function useExtractIngredientReferences(recipeIngredients: RecipeIngredie .map(normalize) .filter(word => word.length > 2) .filter(word => !isBlackListedWord(word)) - .flatMap(word => availableIngredients.filter(ingredient => ingredientMatchesWord(ingredient, word, recipeIngredientAmountsDisabled))) + .flatMap(word => availableIngredients.filter(ingredient => ingredientMatchesWord(ingredient, word))) .map(ingredient => ingredient.referenceId as string); // deduplicate diff --git a/frontend/composables/recipes/use-recipe-ingredients.test.ts b/frontend/composables/recipes/use-recipe-ingredients.test.ts index 52eaa3904..398c42da3 100644 --- a/frontend/composables/recipes/use-recipe-ingredients.test.ts +++ b/frontend/composables/recipes/use-recipe-ingredients.test.ts @@ -16,33 +16,27 @@ describe(parseIngredientText.name, () => { ...overrides, }); - test("uses ingredient note if disableAmount: true", () => { - const ingredient = createRecipeIngredient({ note: "foo" }); - - expect(parseIngredientText(ingredient, true)).toEqual("foo"); - }); - test("adds note section if note present", () => { const ingredient = createRecipeIngredient({ note: "custom note" }); - expect(parseIngredientText(ingredient, false)).toContain("custom note"); + expect(parseIngredientText(ingredient)).toContain("custom note"); }); test("ingredient text with fraction", () => { const ingredient = createRecipeIngredient({ quantity: 1.5, unit: { fraction: true, id: "1", name: "cup" } }); - expect(parseIngredientText(ingredient, false, 1, true)).contain("11").and.to.contain("2"); + expect(parseIngredientText(ingredient, 1, true)).contain("11").and.to.contain("2"); }); test("ingredient text with fraction when unit is null", () => { const ingredient = createRecipeIngredient({ quantity: 1.5, unit: undefined }); - expect(parseIngredientText(ingredient, false, 1, true)).contain("11").and.to.contain("2"); + expect(parseIngredientText(ingredient, 1, true)).contain("11").and.to.contain("2"); }); test("ingredient text with fraction no formatting", () => { const ingredient = createRecipeIngredient({ quantity: 1.5, unit: { fraction: true, id: "1", name: "cup" } }); - const result = parseIngredientText(ingredient, false, 1, false); + const result = parseIngredientText(ingredient, 1, false); expect(result).not.contain("<"); expect(result).not.contain(">"); @@ -52,7 +46,7 @@ describe(parseIngredientText.name, () => { test("sanitizes html", () => { const ingredient = createRecipeIngredient({ note: "" }); - expect(parseIngredientText(ingredient, false)).not.toContain("