mirror of
				https://github.com/mealie-recipes/mealie.git
				synced 2025-10-30 17:53:31 -04: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:
		| @@ -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", |                 "note": "1 cup unsalted butter, cut into cubes", | ||||||
|                 "unit": None, |                 "unit": None, | ||||||
|                 "food": None, |                 "food": None, | ||||||
|                 "disableAmount": True, |  | ||||||
|                 "quantity": 1, |                 "quantity": 1, | ||||||
|                 "originalText": None, |                 "originalText": None, | ||||||
|                 "referenceId": "ea3b6702-9532-4fbc-a40b-f99917831c26", |                 "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", |                 "note": "1 cup light brown sugar", | ||||||
|                 "unit": None, |                 "unit": None, | ||||||
|                 "food": None, |                 "food": None, | ||||||
|                 "disableAmount": True, |  | ||||||
|                 "quantity": 1, |                 "quantity": 1, | ||||||
|                 "originalText": None, |                 "originalText": None, | ||||||
|                 "referenceId": "c5bbfefb-1e23-4ffd-af88-c0363a0fae82", |                 "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", |                 "note": "1/2 cup granulated white sugar", | ||||||
|                 "unit": None, |                 "unit": None, | ||||||
|                 "food": None, |                 "food": None, | ||||||
|                 "disableAmount": True, |  | ||||||
|                 "quantity": 1, |                 "quantity": 1, | ||||||
|                 "originalText": None, |                 "originalText": None, | ||||||
|                 "referenceId": "034f481b-c426-4a17-b983-5aea9be4974b", |                 "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", |                 "note": "2 large eggs", | ||||||
|                 "unit": None, |                 "unit": None, | ||||||
|                 "food": None, |                 "food": None, | ||||||
|                 "disableAmount": True, |  | ||||||
|                 "quantity": 1, |                 "quantity": 1, | ||||||
|                 "originalText": None, |                 "originalText": None, | ||||||
|                 "referenceId": "37c1f796-3bdb-4856-859f-dbec90bc27e4", |                 "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", |                 "note": "2 tsp vanilla extract", | ||||||
|                 "unit": None, |                 "unit": None, | ||||||
|                 "food": None, |                 "food": None, | ||||||
|                 "disableAmount": True, |  | ||||||
|                 "quantity": 1, |                 "quantity": 1, | ||||||
|                 "originalText": None, |                 "originalText": None, | ||||||
|                 "referenceId": "85561ace-f249-401d-834c-e600a2f6280e", |                 "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", |                 "note": "1/2 cup creamy peanut butter", | ||||||
|                 "unit": None, |                 "unit": None, | ||||||
|                 "food": None, |                 "food": None, | ||||||
|                 "disableAmount": True, |  | ||||||
|                 "quantity": 1, |                 "quantity": 1, | ||||||
|                 "originalText": None, |                 "originalText": None, | ||||||
|                 "referenceId": "ac91bda0-e8a8-491a-976a-ae4e72418cfd", |                 "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", |                 "note": "1 tsp cornstarch", | ||||||
|                 "unit": None, |                 "unit": None, | ||||||
|                 "food": None, |                 "food": None, | ||||||
|                 "disableAmount": True, |  | ||||||
|                 "quantity": 1, |                 "quantity": 1, | ||||||
|                 "originalText": None, |                 "originalText": None, | ||||||
|                 "referenceId": "4d1256b3-115e-4475-83cd-464fbc304cb0", |                 "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", |                 "note": "1 tsp baking soda", | ||||||
|                 "unit": None, |                 "unit": None, | ||||||
|                 "food": None, |                 "food": None, | ||||||
|                 "disableAmount": True, |  | ||||||
|                 "quantity": 1, |                 "quantity": 1, | ||||||
|                 "originalText": None, |                 "originalText": None, | ||||||
|                 "referenceId": "64627441-39f9-4ee3-8494-bafe36451d12", |                 "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", |                 "note": "1/2 tsp salt", | ||||||
|                 "unit": None, |                 "unit": None, | ||||||
|                 "food": None, |                 "food": None, | ||||||
|                 "disableAmount": True, |  | ||||||
|                 "quantity": 1, |                 "quantity": 1, | ||||||
|                 "originalText": None, |                 "originalText": None, | ||||||
|                 "referenceId": "7ae212d0-3cd1-44b0-899e-ec5bd91fd384", |                 "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", |                 "note": "1 cup cake flour", | ||||||
|                 "unit": None, |                 "unit": None, | ||||||
|                 "food": None, |                 "food": None, | ||||||
|                 "disableAmount": True, |  | ||||||
|                 "quantity": 1, |                 "quantity": 1, | ||||||
|                 "originalText": None, |                 "originalText": None, | ||||||
|                 "referenceId": "06967994-8548-4952-a8cc-16e8db228ebd", |                 "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", |                 "note": "2 cups all-purpose flour", | ||||||
|                 "unit": None, |                 "unit": None, | ||||||
|                 "food": None, |                 "food": None, | ||||||
|                 "disableAmount": True, |  | ||||||
|                 "quantity": 1, |                 "quantity": 1, | ||||||
|                 "originalText": None, |                 "originalText": None, | ||||||
|                 "referenceId": "bdb33b23-c767-4465-acf8-3b8e79eb5691", |                 "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", |                 "note": "2 cups peanut butter chips", | ||||||
|                 "unit": None, |                 "unit": None, | ||||||
|                 "food": None, |                 "food": None, | ||||||
|                 "disableAmount": True, |  | ||||||
|                 "quantity": 1, |                 "quantity": 1, | ||||||
|                 "originalText": None, |                 "originalText": None, | ||||||
|                 "referenceId": "12ba0af8-affd-4fb2-9cca-6f1b3e8d3aef", |                 "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", |                 "note": "1½ cups Reese's Pieces candies", | ||||||
|                 "unit": None, |                 "unit": None, | ||||||
|                 "food": None, |                 "food": None, | ||||||
|                 "disableAmount": True, |  | ||||||
|                 "quantity": 1, |                 "quantity": 1, | ||||||
|                 "originalText": None, |                 "originalText": None, | ||||||
|                 "referenceId": "4bdc0598-a3eb-41ee-8af0-4da9348fbfe2", |                 "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, |             "showAssets": False, | ||||||
|             "landscapeView": False, |             "landscapeView": False, | ||||||
|             "disableComments": False, |             "disableComments": False, | ||||||
|             "disableAmount": True, |  | ||||||
|             "locked": False, |             "locked": False, | ||||||
|         }, |         }, | ||||||
|         "assets": [], |         "assets": [], | ||||||
|   | |||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -79,11 +79,6 @@ const recipePreferences: Preference[] = [ | |||||||
|     label: i18n.t("group.disable-users-from-commenting-on-recipes"), |     label: i18n.t("group.disable-users-from-commenting-on-recipes"), | ||||||
|     description: i18n.t("group.disable-users-from-commenting-on-recipes-description"), |     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 = [ | const allDays = [ | ||||||
|   | |||||||
| @@ -130,20 +130,23 @@ | |||||||
|                       .ingredients[i] |                       .ingredients[i] | ||||||
|                       .checked" |                       .checked" | ||||||
|                 > |                 > | ||||||
|  |                   <v-container class="pa-0 ma-0"> | ||||||
|  |                     <v-row no-gutters> | ||||||
|                       <v-checkbox |                       <v-checkbox | ||||||
|                         hide-details |                         hide-details | ||||||
|                         :model-value="ingredientData.checked" |                         :model-value="ingredientData.checked" | ||||||
|                     class="pt-0 my-auto py-auto" |                         class="pt-0 my-auto py-auto mr-2" | ||||||
|                         color="secondary" |                         color="secondary" | ||||||
|                         density="compact" |                         density="compact" | ||||||
|                       /> |                       /> | ||||||
|                   <div :key="`${ingredientData.ingredient.quantity || 'no-qty'}-${i}`"> |                       <div :key="`${ingredientData.ingredient.quantity || 'no-qty'}-${i}`" class="pa-auto my-auto"> | ||||||
|                         <RecipeIngredientListItem |                         <RecipeIngredientListItem | ||||||
|                           :ingredient="ingredientData.ingredient" |                           :ingredient="ingredientData.ingredient" | ||||||
|                       :disable-amount="ingredientData.disableAmount" |  | ||||||
|                           :scale="recipeSection.recipeScale" |                           :scale="recipeSection.recipeScale" | ||||||
|                         /> |                         /> | ||||||
|                       </div> |                       </div> | ||||||
|  |                     </v-row> | ||||||
|  |                   </v-container> | ||||||
|                 </v-list-item> |                 </v-list-item> | ||||||
|               </div> |               </div> | ||||||
|             </div> |             </div> | ||||||
| @@ -188,7 +191,6 @@ export interface RecipeWithScale extends Recipe { | |||||||
| export interface ShoppingListIngredient { | export interface ShoppingListIngredient { | ||||||
|   checked: boolean; |   checked: boolean; | ||||||
|   ingredient: RecipeIngredient; |   ingredient: RecipeIngredient; | ||||||
|   disableAmount: boolean; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| export interface ShoppingListIngredientSection { | export interface ShoppingListIngredientSection { | ||||||
| @@ -290,7 +292,6 @@ async function consolidateRecipesIntoSections(recipes: RecipeWithScale[]) { | |||||||
|       return { |       return { | ||||||
|         checked: !householdsWithFood.includes(userHousehold.value), |         checked: !householdsWithFood.includes(userHousehold.value), | ||||||
|         ingredient: ing, |         ingredient: ing, | ||||||
|         disableAmount: recipe.settings?.disableAmount || false, |  | ||||||
|       }; |       }; | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -17,7 +17,6 @@ | |||||||
|       class="d-flex flex-wrap my-1" |       class="d-flex flex-wrap my-1" | ||||||
|     > |     > | ||||||
|       <v-col |       <v-col | ||||||
|         v-if="!disableAmount" |  | ||||||
|         sm="12" |         sm="12" | ||||||
|         md="2" |         md="2" | ||||||
|         cols="12" |         cols="12" | ||||||
| @@ -42,7 +41,6 @@ | |||||||
|         </v-text-field> |         </v-text-field> | ||||||
|       </v-col> |       </v-col> | ||||||
|       <v-col |       <v-col | ||||||
|         v-if="!disableAmount" |  | ||||||
|         sm="12" |         sm="12" | ||||||
|         md="3" |         md="3" | ||||||
|         cols="12" |         cols="12" | ||||||
| @@ -98,7 +96,6 @@ | |||||||
|  |  | ||||||
|       <!-- Foods Input --> |       <!-- Foods Input --> | ||||||
|       <v-col |       <v-col | ||||||
|         v-if="!disableAmount" |  | ||||||
|         m="12" |         m="12" | ||||||
|         md="3" |         md="3" | ||||||
|         cols="12" |         cols="12" | ||||||
| @@ -166,16 +163,7 @@ | |||||||
|             :placeholder="$t('recipe.notes')" |             :placeholder="$t('recipe.notes')" | ||||||
|             class="mb-auto" |             class="mb-auto" | ||||||
|             @click="$emit('clickIngredientField', 'note')" |             @click="$emit('clickIngredientField', 'note')" | ||||||
|           > |           /> | ||||||
|             <template #prepend> |  | ||||||
|               <v-icon |  | ||||||
|                 v-if="disableAmount && $attrs && $attrs.delete" |  | ||||||
|                 class="mr-n1 handle" |  | ||||||
|               > |  | ||||||
|                 {{ $globals.icons.arrowUpDown }} |  | ||||||
|               </v-icon> |  | ||||||
|             </template> |  | ||||||
|           </v-text-field> |  | ||||||
|           <BaseButtonGroup |           <BaseButtonGroup | ||||||
|             hover |             hover | ||||||
|             :large="false" |             :large="false" | ||||||
| @@ -216,10 +204,6 @@ import type { RecipeIngredient } from "~/lib/api/types/recipe"; | |||||||
| const model = defineModel<RecipeIngredient>({ required: true }); | const model = defineModel<RecipeIngredient>({ required: true }); | ||||||
|  |  | ||||||
| defineProps({ | defineProps({ | ||||||
|   disableAmount: { |  | ||||||
|     type: Boolean, |  | ||||||
|     default: false, |  | ||||||
|   }, |  | ||||||
|   unitError: { |   unitError: { | ||||||
|     type: Boolean, |     type: Boolean, | ||||||
|     default: false, |     default: false, | ||||||
|   | |||||||
| @@ -34,16 +34,14 @@ import { useParsedIngredientText } from "~/composables/recipes"; | |||||||
|  |  | ||||||
| interface Props { | interface Props { | ||||||
|   ingredient: RecipeIngredient; |   ingredient: RecipeIngredient; | ||||||
|   disableAmount?: boolean; |  | ||||||
|   scale?: number; |   scale?: number; | ||||||
| } | } | ||||||
| const props = withDefaults(defineProps<Props>(), { | const props = withDefaults(defineProps<Props>(), { | ||||||
|   disableAmount: false, |  | ||||||
|   scale: 1, |   scale: 1, | ||||||
| }); | }); | ||||||
|  |  | ||||||
| const parsedIng = computed(() => { | const parsedIng = computed(() => { | ||||||
|   return useParsedIngredientText(props.ingredient, props.disableAmount, props.scale); |   return useParsedIngredientText(props.ingredient, props.scale); | ||||||
| }); | }); | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -43,7 +43,6 @@ | |||||||
|           <v-list-item-title> |           <v-list-item-title> | ||||||
|             <RecipeIngredientListItem |             <RecipeIngredientListItem | ||||||
|               :ingredient="ingredient" |               :ingredient="ingredient" | ||||||
|               :disable-amount="disableAmount" |  | ||||||
|               :scale="scale" |               :scale="scale" | ||||||
|             /> |             /> | ||||||
|           </v-list-item-title> |           </v-list-item-title> | ||||||
| @@ -60,13 +59,11 @@ import type { RecipeIngredient } from "~/lib/api/types/recipe"; | |||||||
|  |  | ||||||
| interface Props { | interface Props { | ||||||
|   value?: RecipeIngredient[]; |   value?: RecipeIngredient[]; | ||||||
|   disableAmount?: boolean; |  | ||||||
|   scale?: number; |   scale?: number; | ||||||
|   isCookMode?: boolean; |   isCookMode?: boolean; | ||||||
| } | } | ||||||
| const props = withDefaults(defineProps<Props>(), { | const props = withDefaults(defineProps<Props>(), { | ||||||
|   value: () => [], |   value: () => [], | ||||||
|   disableAmount: false, |  | ||||||
|   scale: 1, |   scale: 1, | ||||||
|   isCookMode: false, |   isCookMode: false, | ||||||
| }); | }); | ||||||
| @@ -89,7 +86,7 @@ const ingredientCopyText = computed(() => { | |||||||
|       components.push(`[${ingredient.title}]`); |       components.push(`[${ingredient.title}]`); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     components.push(parseIngredientText(ingredient, props.disableAmount, props.scale, false)); |     components.push(parseIngredientText(ingredient, props.scale, false)); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   return components.join("\n"); |   return components.join("\n"); | ||||||
|   | |||||||
| @@ -141,7 +141,6 @@ | |||||||
|           <RecipeIngredients |           <RecipeIngredients | ||||||
|             :value="notLinkedIngredients" |             :value="notLinkedIngredients" | ||||||
|             :scale="scale" |             :scale="scale" | ||||||
|             :disable-amount="recipe.settings.disableAmount" |  | ||||||
|             :is-cook-mode="isCookMode" |             :is-cook-mode="isCookMode" | ||||||
|           /> |           /> | ||||||
|         </v-card> |         </v-card> | ||||||
|   | |||||||
| @@ -1,9 +1,14 @@ | |||||||
| <!-- eslint-disable vue/no-mutating-props --> | <!-- eslint-disable vue/no-mutating-props --> | ||||||
| <template> | <template> | ||||||
|   <div> |   <div> | ||||||
|  |     <div class="mb-4"> | ||||||
|       <h2 class="mb-4 text-h5 font-weight-medium opacity-80"> |       <h2 class="mb-4 text-h5 font-weight-medium opacity-80"> | ||||||
|         {{ $t("recipe.ingredients") }} |         {{ $t("recipe.ingredients") }} | ||||||
|       </h2> |       </h2> | ||||||
|  |       <BannerWarning v-if="!hasFoodOrUnit"> | ||||||
|  |         {{ $t("recipe.ingredients-not-parsed-description", { parse: $t('recipe.parse') }) }} | ||||||
|  |       </BannerWarning> | ||||||
|  |     </div> | ||||||
|     <VueDraggable |     <VueDraggable | ||||||
|       v-if="recipe.recipeIngredient.length > 0" |       v-if="recipe.recipeIngredient.length > 0" | ||||||
|       v-model="recipe.recipeIngredient" |       v-model="recipe.recipeIngredient" | ||||||
| @@ -27,7 +32,6 @@ | |||||||
|           :key="ingredient.referenceId" |           :key="ingredient.referenceId" | ||||||
|           v-model="recipe.recipeIngredient[index]" |           v-model="recipe.recipeIngredient[index]" | ||||||
|           class="list-group-item" |           class="list-group-item" | ||||||
|           :disable-amount="recipe.settings.disableAmount" |  | ||||||
|           @delete="recipe.recipeIngredient.splice(index, 1)" |           @delete="recipe.recipeIngredient.splice(index, 1)" | ||||||
|           @insert-above="insertNewIngredient(index)" |           @insert-above="insertNewIngredient(index)" | ||||||
|           @insert-below="insertNewIngredient(index + 1)" |           @insert-below="insertNewIngredient(index + 1)" | ||||||
| @@ -49,7 +53,7 @@ | |||||||
|           <span> |           <span> | ||||||
|             <BaseButton |             <BaseButton | ||||||
|               class="mb-1" |               class="mb-1" | ||||||
|               :disabled="recipe.settings.disableAmount || hasFoodOrUnit" |               :disabled="hasFoodOrUnit" | ||||||
|               color="accent" |               color="accent" | ||||||
|               :to="`/g/${groupSlug}/r/${recipe.slug}/ingredient-parser`" |               :to="`/g/${groupSlug}/r/${recipe.slug}/ingredient-parser`" | ||||||
|               v-bind="props" |               v-bind="props" | ||||||
| @@ -109,10 +113,7 @@ const hasFoodOrUnit = computed(() => { | |||||||
| }); | }); | ||||||
|  |  | ||||||
| const parserToolTip = computed(() => { | const parserToolTip = computed(() => { | ||||||
|   if (recipe.value.settings.disableAmount) { |   if (hasFoodOrUnit.value) { | ||||||
|     return i18n.t("recipe.enable-ingredient-amounts-to-use-this-feature"); |  | ||||||
|   } |  | ||||||
|   else if (hasFoodOrUnit.value) { |  | ||||||
|     return i18n.t("recipe.recipes-with-units-or-foods-defined-cannot-be-parsed"); |     return i18n.t("recipe.recipes-with-units-or-foods-defined-cannot-be-parsed"); | ||||||
|   } |   } | ||||||
|   return i18n.t("recipe.parse-ingredients"); |   return i18n.t("recipe.parse-ingredients"); | ||||||
| @@ -127,7 +128,6 @@ function addIngredient(ingredients: Array<string> | null = null) { | |||||||
|         note: x, |         note: x, | ||||||
|         unit: undefined, |         unit: undefined, | ||||||
|         food: undefined, |         food: undefined, | ||||||
|         disableAmount: true, |  | ||||||
|         quantity: 1, |         quantity: 1, | ||||||
|       }; |       }; | ||||||
|     }); |     }); | ||||||
| @@ -146,7 +146,6 @@ function addIngredient(ingredients: Array<string> | null = null) { | |||||||
|       unit: undefined, |       unit: undefined, | ||||||
|       // @ts-expect-error - prop can be null-type by NoUndefinedField type forces it to be set |       // @ts-expect-error - prop can be null-type by NoUndefinedField type forces it to be set | ||||||
|       food: undefined, |       food: undefined, | ||||||
|       disableAmount: true, |  | ||||||
|       quantity: 1, |       quantity: 1, | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
| @@ -161,7 +160,6 @@ function insertNewIngredient(dest: number) { | |||||||
|     unit: undefined, |     unit: undefined, | ||||||
|     // @ts-expect-error - prop can be null-type by NoUndefinedField type forces it to be set |     // @ts-expect-error - prop can be null-type by NoUndefinedField type forces it to be set | ||||||
|     food: undefined, |     food: undefined, | ||||||
|     disableAmount: true, |  | ||||||
|     quantity: 1, |     quantity: 1, | ||||||
|   }); |   }); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -3,7 +3,6 @@ | |||||||
|     <RecipeIngredients |     <RecipeIngredients | ||||||
|       :value="recipe.recipeIngredient" |       :value="recipe.recipeIngredient" | ||||||
|       :scale="scale" |       :scale="scale" | ||||||
|       :disable-amount="recipe.settings.disableAmount" |  | ||||||
|       :is-cook-mode="isCookMode" |       :is-cook-mode="isCookMode" | ||||||
|     /> |     /> | ||||||
|     <div v-if="!isEditMode && recipe.tools && recipe.tools.length > 0"> |     <div v-if="!isEditMode && recipe.tools && recipe.tools.length > 0"> | ||||||
|   | |||||||
| @@ -36,7 +36,7 @@ | |||||||
|             :value="ing.referenceId" |             :value="ing.referenceId" | ||||||
|           > |           > | ||||||
|             <template #label> |             <template #label> | ||||||
|               <RecipeIngredientHtml :markup="parseIngredientText(ing, recipe.settings.disableAmount)" /> |               <RecipeIngredientHtml :markup="parseIngredientText(ing)" /> | ||||||
|             </template> |             </template> | ||||||
|           </v-checkbox-btn> |           </v-checkbox-btn> | ||||||
|  |  | ||||||
| @@ -51,7 +51,7 @@ | |||||||
|               :value="ing.referenceId" |               :value="ing.referenceId" | ||||||
|             > |             > | ||||||
|               <template #label> |               <template #label> | ||||||
|                 <RecipeIngredientHtml :markup="parseIngredientText(ing, recipe.settings.disableAmount)" /> |                 <RecipeIngredientHtml :markup="parseIngredientText(ing)" /> | ||||||
|               </template> |               </template> | ||||||
|             </v-checkbox-btn> |             </v-checkbox-btn> | ||||||
|           </template> |           </template> | ||||||
| @@ -323,7 +323,6 @@ | |||||||
|                               return step.ingredientReferences.map((ref) => ref.referenceId).includes(ing.referenceId || '') |                               return step.ingredientReferences.map((ref) => ref.referenceId).includes(ing.referenceId || '') | ||||||
|                             })" |                             })" | ||||||
|                             :scale="scale" |                             :scale="scale" | ||||||
|                             :disable-amount="recipe.settings.disableAmount" |  | ||||||
|                             :is-cook-mode="isCookMode" |                             :is-cook-mode="isCookMode" | ||||||
|                           /> |                           /> | ||||||
|                         </div> |                         </div> | ||||||
| @@ -552,7 +551,6 @@ function autoSetReferences() { | |||||||
|     props.recipe.recipeIngredient, |     props.recipe.recipeIngredient, | ||||||
|     activeRefs.value, |     activeRefs.value, | ||||||
|     activeText.value, |     activeText.value, | ||||||
|     props.recipe.settings.disableAmount, |  | ||||||
|   ).forEach((ingredient: string) => activeRefs.value.push(ingredient)); |   ).forEach((ingredient: string) => activeRefs.value.push(ingredient)); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -574,7 +572,7 @@ function getIngredientByRefId(refId: string | undefined) { | |||||||
|  |  | ||||||
|   const ing = ingredientLookup.value[refId]; |   const ing = ingredientLookup.value[refId]; | ||||||
|   if (!ing) return ""; |   if (!ing) return ""; | ||||||
|   return parseIngredientText(ing, props.recipe.settings.disableAmount, props.scale); |   return parseIngredientText(ing, props.scale); | ||||||
| } | } | ||||||
|  |  | ||||||
| // =============================================================== | // =============================================================== | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ | |||||||
|       v-if="!isEditMode" |       v-if="!isEditMode" | ||||||
|       v-model.number="scale" |       v-model.number="scale" | ||||||
|       :recipe-servings="recipeServings" |       :recipe-servings="recipeServings" | ||||||
|       :edit-scale="!recipe.settings.disableAmount && !isEditMode" |       :edit-scale="hasFoodOrUnit && !isEditMode" | ||||||
|     /> |     /> | ||||||
|   </div> |   </div> | ||||||
| </template> | </template> | ||||||
| @@ -24,4 +24,15 @@ const { isEditMode } = usePageState(props.recipe.slug); | |||||||
| const recipeServings = computed<number>(() => { | const recipeServings = computed<number>(() => { | ||||||
|   return props.recipe.recipeServings || props.recipe.recipeYieldQuantity || 1; |   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; | ||||||
|  | }); | ||||||
| </script> | </script> | ||||||
|   | |||||||
| @@ -321,7 +321,7 @@ const hasNotes = computed(() => { | |||||||
| }); | }); | ||||||
|  |  | ||||||
| function parseText(ingredient: RecipeIngredient) { | function parseText(ingredient: RecipeIngredient) { | ||||||
|   return parseIngredientText(ingredient, props.recipe.settings?.disableAmount || false, props.scale); |   return parseIngredientText(ingredient, props.scale); | ||||||
| } | } | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -31,7 +31,6 @@ const labels: Record<keyof RecipeSettings, string> = { | |||||||
|   showAssets: i18n.t("asset.show-assets"), |   showAssets: i18n.t("asset.show-assets"), | ||||||
|   landscapeView: i18n.t("recipe.landscape-view-coming-soon"), |   landscapeView: i18n.t("recipe.landscape-view-coming-soon"), | ||||||
|   disableComments: i18n.t("recipe.disable-comments"), |   disableComments: i18n.t("recipe.disable-comments"), | ||||||
|   disableAmount: i18n.t("recipe.disable-amount"), |  | ||||||
|   locked: i18n.t("recipe.locked"), |   locked: i18n.t("recipe.locked"), | ||||||
| }; | }; | ||||||
| </script> | </script> | ||||||
|   | |||||||
| @@ -22,10 +22,7 @@ | |||||||
|             :class="listItem.checked ? 'strike-through' : ''" |             :class="listItem.checked ? 'strike-through' : ''" | ||||||
|             style="min-width: 0;" |             style="min-width: 0;" | ||||||
|           > |           > | ||||||
|             <RecipeIngredientListItem |             <RecipeIngredientListItem :ingredient="listItem" /> | ||||||
|               :ingredient="listItem" |  | ||||||
|               :disable-amount="!(listItem.isFood || listItem.quantity !== 1)" |  | ||||||
|             /> |  | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|       </v-col> |       </v-col> | ||||||
| @@ -172,7 +169,6 @@ | |||||||
|       @save="save" |       @save="save" | ||||||
|       @cancel="toggleEdit(false)" |       @cancel="toggleEdit(false)" | ||||||
|       @delete="$emit('delete')" |       @delete="$emit('delete')" | ||||||
|       @toggle-foods="localListItem.isFood = !localListItem.isFood" |  | ||||||
|     /> |     /> | ||||||
|   </div> |   </div> | ||||||
| </template> | </template> | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ | |||||||
|   <div> |   <div> | ||||||
|     <v-card variant="outlined"> |     <v-card variant="outlined"> | ||||||
|       <v-card-text class="pb-3 pt-1"> |       <v-card-text class="pb-3 pt-1"> | ||||||
|         <div v-if="listItem.isFood" class="d-md-flex align-center mb-2" style="gap: 20px"> |         <div class="d-md-flex align-center mb-2" style="gap: 20px"> | ||||||
|           <div> |           <div> | ||||||
|             <InputQuantity v-model="listItem.quantity" /> |             <InputQuantity v-model="listItem.quantity" /> | ||||||
|           </div> |           </div> | ||||||
| @@ -26,9 +26,6 @@ | |||||||
|           /> |           /> | ||||||
|         </div> |         </div> | ||||||
|         <div class="d-md-flex align-center" style="gap: 20px"> |         <div class="d-md-flex align-center" style="gap: 20px"> | ||||||
|           <div v-if="!listItem.isFood"> |  | ||||||
|             <InputQuantity v-model="listItem.quantity" /> |  | ||||||
|           </div> |  | ||||||
|           <v-textarea |           <v-textarea | ||||||
|             v-model="listItem.note" |             v-model="listItem.note" | ||||||
|             hide-details |             hide-details | ||||||
| @@ -99,11 +96,6 @@ | |||||||
|               text: $t('general.cancel'), |               text: $t('general.cancel'), | ||||||
|               event: 'cancel', |               event: 'cancel', | ||||||
|             }, |             }, | ||||||
|             { |  | ||||||
|               icon: $globals.icons.foods, |  | ||||||
|               text: $t('shopping-list.toggle-food'), |  | ||||||
|               event: 'toggle-foods', |  | ||||||
|             }, |  | ||||||
|             { |             { | ||||||
|               icon: $globals.icons.save, |               icon: $globals.icons.save, | ||||||
|               text: $t('general.save'), |               text: $t('general.save'), | ||||||
| @@ -113,7 +105,6 @@ | |||||||
|           @save="$emit('save')" |           @save="$emit('save')" | ||||||
|           @cancel="$emit('cancel')" |           @cancel="$emit('cancel')" | ||||||
|           @delete="$emit('delete')" |           @delete="$emit('delete')" | ||||||
|           @toggle-foods="listItem.isFood = !listItem.isFood" |  | ||||||
|         /> |         /> | ||||||
|       </v-card-actions> |       </v-card-actions> | ||||||
|     </v-card> |     </v-card> | ||||||
|   | |||||||
| @@ -18,8 +18,8 @@ function removeStartingPunctuation(word: string): string { | |||||||
|   return word.replace(punctuationAtBeginning, ""); |   return word.replace(punctuationAtBeginning, ""); | ||||||
| } | } | ||||||
|  |  | ||||||
| function ingredientMatchesWord(ingredient: RecipeIngredient, word: string, recipeIngredientAmountsDisabled: boolean) { | function ingredientMatchesWord(ingredient: RecipeIngredient, word: string) { | ||||||
|   const searchText = parseIngredientText(ingredient, recipeIngredientAmountsDisabled); |   const searchText = parseIngredientText(ingredient); | ||||||
|   return searchText.toLowerCase().includes(word.toLowerCase()); |   return searchText.toLowerCase().includes(word.toLowerCase()); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -39,7 +39,7 @@ function isBlackListedWord(word: string) { | |||||||
|   return blackListedText.includes(word) || word.match(blackListedRegexMatch); |   return blackListedText.includes(word) || word.match(blackListedRegexMatch); | ||||||
| } | } | ||||||
|  |  | ||||||
| export function useExtractIngredientReferences(recipeIngredients: RecipeIngredient[], activeRefs: string[], text: string, recipeIngredientAmountsDisabled: boolean): Set<string> { | export function useExtractIngredientReferences(recipeIngredients: RecipeIngredient[], activeRefs: string[], text: string): Set<string> { | ||||||
|   const availableIngredients = recipeIngredients |   const availableIngredients = recipeIngredients | ||||||
|     .filter(ingredient => ingredient.referenceId !== undefined) |     .filter(ingredient => ingredient.referenceId !== undefined) | ||||||
|     .filter(ingredient => !activeRefs.includes(ingredient.referenceId as string)); |     .filter(ingredient => !activeRefs.includes(ingredient.referenceId as string)); | ||||||
| @@ -50,7 +50,7 @@ export function useExtractIngredientReferences(recipeIngredients: RecipeIngredie | |||||||
|     .map(normalize) |     .map(normalize) | ||||||
|     .filter(word => word.length > 2) |     .filter(word => word.length > 2) | ||||||
|     .filter(word => !isBlackListedWord(word)) |     .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); |     .map(ingredient => ingredient.referenceId as string); | ||||||
|   //  deduplicate |   //  deduplicate | ||||||
|  |  | ||||||
|   | |||||||
| @@ -16,33 +16,27 @@ describe(parseIngredientText.name, () => { | |||||||
|     ...overrides, |     ...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", () => { |   test("adds note section if note present", () => { | ||||||
|     const ingredient = createRecipeIngredient({ note: "custom note" }); |     const ingredient = createRecipeIngredient({ note: "custom note" }); | ||||||
|  |  | ||||||
|     expect(parseIngredientText(ingredient, false)).toContain("custom note"); |     expect(parseIngredientText(ingredient)).toContain("custom note"); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   test("ingredient text with fraction", () => { |   test("ingredient text with fraction", () => { | ||||||
|     const ingredient = createRecipeIngredient({ quantity: 1.5, unit: { fraction: true, id: "1", name: "cup" } }); |     const ingredient = createRecipeIngredient({ quantity: 1.5, unit: { fraction: true, id: "1", name: "cup" } }); | ||||||
|  |  | ||||||
|     expect(parseIngredientText(ingredient, false, 1, true)).contain("1<sup>1</sup>").and.to.contain("<sub>2</sub>"); |     expect(parseIngredientText(ingredient, 1, true)).contain("1<sup>1</sup>").and.to.contain("<sub>2</sub>"); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   test("ingredient text with fraction when unit is null", () => { |   test("ingredient text with fraction when unit is null", () => { | ||||||
|     const ingredient = createRecipeIngredient({ quantity: 1.5, unit: undefined }); |     const ingredient = createRecipeIngredient({ quantity: 1.5, unit: undefined }); | ||||||
|  |  | ||||||
|     expect(parseIngredientText(ingredient, false, 1, true)).contain("1<sup>1</sup>").and.to.contain("<sub>2</sub>"); |     expect(parseIngredientText(ingredient, 1, true)).contain("1<sup>1</sup>").and.to.contain("<sub>2</sub>"); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   test("ingredient text with fraction no formatting", () => { |   test("ingredient text with fraction no formatting", () => { | ||||||
|     const ingredient = createRecipeIngredient({ quantity: 1.5, unit: { fraction: true, id: "1", name: "cup" } }); |     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("<"); | ||||||
|     expect(result).not.contain(">"); |     expect(result).not.contain(">"); | ||||||
| @@ -52,7 +46,7 @@ describe(parseIngredientText.name, () => { | |||||||
|   test("sanitizes html", () => { |   test("sanitizes html", () => { | ||||||
|     const ingredient = createRecipeIngredient({ note: "<script>alert('foo')</script>" }); |     const ingredient = createRecipeIngredient({ note: "<script>alert('foo')</script>" }); | ||||||
|  |  | ||||||
|     expect(parseIngredientText(ingredient, false)).not.toContain("<script>"); |     expect(parseIngredientText(ingredient)).not.toContain("<script>"); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   test("plural test : plural qty : use abbreviation", () => { |   test("plural test : plural qty : use abbreviation", () => { | ||||||
| @@ -62,7 +56,7 @@ describe(parseIngredientText.name, () => { | |||||||
|       food: { id: "1", name: "diced onion", pluralName: "diced onions" }, |       food: { id: "1", name: "diced onion", pluralName: "diced onions" }, | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     expect(parseIngredientText(ingredient, false)).toEqual("2 tbsps diced onions"); |     expect(parseIngredientText(ingredient)).toEqual("2 tbsps diced onions"); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   test("plural test : plural qty : not abbreviation", () => { |   test("plural test : plural qty : not abbreviation", () => { | ||||||
| @@ -72,7 +66,7 @@ describe(parseIngredientText.name, () => { | |||||||
|       food: { id: "1", name: "diced onion", pluralName: "diced onions" }, |       food: { id: "1", name: "diced onion", pluralName: "diced onions" }, | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     expect(parseIngredientText(ingredient, false)).toEqual("2 tablespoons diced onions"); |     expect(parseIngredientText(ingredient)).toEqual("2 tablespoons diced onions"); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   test("plural test : single qty : use abbreviation", () => { |   test("plural test : single qty : use abbreviation", () => { | ||||||
| @@ -82,7 +76,7 @@ describe(parseIngredientText.name, () => { | |||||||
|       food: { id: "1", name: "diced onion", pluralName: "diced onions" }, |       food: { id: "1", name: "diced onion", pluralName: "diced onions" }, | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     expect(parseIngredientText(ingredient, false)).toEqual("1 tbsp diced onion"); |     expect(parseIngredientText(ingredient)).toEqual("1 tbsp diced onion"); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   test("plural test : single qty : not abbreviation", () => { |   test("plural test : single qty : not abbreviation", () => { | ||||||
| @@ -92,7 +86,7 @@ describe(parseIngredientText.name, () => { | |||||||
|       food: { id: "1", name: "diced onion", pluralName: "diced onions" }, |       food: { id: "1", name: "diced onion", pluralName: "diced onions" }, | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     expect(parseIngredientText(ingredient, false)).toEqual("1 tablespoon diced onion"); |     expect(parseIngredientText(ingredient)).toEqual("1 tablespoon diced onion"); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   test("plural test : small qty : use abbreviation", () => { |   test("plural test : small qty : use abbreviation", () => { | ||||||
| @@ -102,7 +96,7 @@ describe(parseIngredientText.name, () => { | |||||||
|       food: { id: "1", name: "diced onion", pluralName: "diced onions" }, |       food: { id: "1", name: "diced onion", pluralName: "diced onions" }, | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     expect(parseIngredientText(ingredient, false)).toEqual("0.5 tbsp diced onion"); |     expect(parseIngredientText(ingredient)).toEqual("0.5 tbsp diced onion"); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   test("plural test : small qty : not abbreviation", () => { |   test("plural test : small qty : not abbreviation", () => { | ||||||
| @@ -112,7 +106,7 @@ describe(parseIngredientText.name, () => { | |||||||
|       food: { id: "1", name: "diced onion", pluralName: "diced onions" }, |       food: { id: "1", name: "diced onion", pluralName: "diced onions" }, | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     expect(parseIngredientText(ingredient, false)).toEqual("0.5 tablespoon diced onion"); |     expect(parseIngredientText(ingredient)).toEqual("0.5 tablespoon diced onion"); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   test("plural test : zero qty", () => { |   test("plural test : zero qty", () => { | ||||||
| @@ -122,7 +116,7 @@ describe(parseIngredientText.name, () => { | |||||||
|       food: { id: "1", name: "diced onion", pluralName: "diced onions" }, |       food: { id: "1", name: "diced onion", pluralName: "diced onions" }, | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     expect(parseIngredientText(ingredient, false)).toEqual("diced onions"); |     expect(parseIngredientText(ingredient)).toEqual("diced onions"); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   test("plural test : single qty, scaled", () => { |   test("plural test : single qty, scaled", () => { | ||||||
| @@ -132,6 +126,6 @@ describe(parseIngredientText.name, () => { | |||||||
|       food: { id: "1", name: "diced onion", pluralName: "diced onions" }, |       food: { id: "1", name: "diced onion", pluralName: "diced onions" }, | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     expect(parseIngredientText(ingredient, false, 2)).toEqual("2 tablespoons diced onions"); |     expect(parseIngredientText(ingredient, 2)).toEqual("2 tablespoons diced onions"); | ||||||
|   }); |   }); | ||||||
| }); | }); | ||||||
|   | |||||||
| @@ -36,16 +36,7 @@ function useUnitName(unit: CreateIngredientUnit | IngredientUnit | undefined, us | |||||||
|   return returnVal; |   return returnVal; | ||||||
| } | } | ||||||
|  |  | ||||||
| export function useParsedIngredientText(ingredient: RecipeIngredient, disableAmount: boolean, scale = 1, includeFormating = true) { | export function useParsedIngredientText(ingredient: RecipeIngredient, scale = 1, includeFormating = true) { | ||||||
|   if (disableAmount) { |  | ||||||
|     return { |  | ||||||
|       name: ingredient.note ? sanitizeIngredientHTML(ingredient.note) : undefined, |  | ||||||
|       quantity: undefined, |  | ||||||
|       unit: undefined, |  | ||||||
|       note: undefined, |  | ||||||
|     }; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   const { quantity, food, unit, note } = ingredient; |   const { quantity, food, unit, note } = ingredient; | ||||||
|   const usePluralUnit = quantity !== undefined && ((quantity || 0) * scale > 1 || (quantity || 0) * scale === 0); |   const usePluralUnit = quantity !== undefined && ((quantity || 0) * scale > 1 || (quantity || 0) * scale === 0); | ||||||
|   const usePluralFood = (!quantity) || quantity * scale > 1; |   const usePluralFood = (!quantity) || quantity * scale > 1; | ||||||
| @@ -82,8 +73,8 @@ export function useParsedIngredientText(ingredient: RecipeIngredient, disableAmo | |||||||
|   }; |   }; | ||||||
| } | } | ||||||
|  |  | ||||||
| export function parseIngredientText(ingredient: RecipeIngredient, disableAmount: boolean, scale = 1, includeFormating = true): string { | export function parseIngredientText(ingredient: RecipeIngredient, scale = 1, includeFormating = true): string { | ||||||
|   const { quantity, unit, name, note } = useParsedIngredientText(ingredient, disableAmount, scale, includeFormating); |   const { quantity, unit, name, note } = useParsedIngredientText(ingredient, scale, includeFormating); | ||||||
|  |  | ||||||
|   const text = `${quantity || ""} ${unit || ""} ${name || ""} ${note || ""}`.replace(/ {2,}/g, " ").trim(); |   const text = `${quantity || ""} ${unit || ""} ${name || ""} ${note || ""}`.replace(/ {2,}/g, " ").trim(); | ||||||
|   return sanitizeIngredientHTML(text); |   return sanitizeIngredientHTML(text); | ||||||
|   | |||||||
| @@ -587,6 +587,7 @@ | |||||||
|     "api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.", |     "api-extras-description": "Recipes extras are a key feature of the Mealie API. They allow you to create custom JSON key/value pairs within a recipe, to reference from 3rd party applications. You can use these keys to provide information, for example to trigger automations or custom messages to relay to your desired device.", | ||||||
|     "message-key": "Message Key", |     "message-key": "Message Key", | ||||||
|     "parse": "Parse", |     "parse": "Parse", | ||||||
|  |     "ingredients-not-parsed-description": "It looks like your ingredients aren't parsed yet. Click the \"{parse}\" button below to parse your ingredients into structured foods.", | ||||||
|     "attach-images-hint": "Attach images by dragging & dropping them into the editor", |     "attach-images-hint": "Attach images by dragging & dropping them into the editor", | ||||||
|     "drop-image": "Drop image", |     "drop-image": "Drop image", | ||||||
|     "enable-ingredient-amounts-to-use-this-feature": "Enable ingredient amounts to use this feature", |     "enable-ingredient-amounts-to-use-this-feature": "Enable ingredient amounts to use this feature", | ||||||
|   | |||||||
| @@ -22,7 +22,6 @@ export interface CreateHouseholdPreferences { | |||||||
|   recipeShowAssets?: boolean; |   recipeShowAssets?: boolean; | ||||||
|   recipeLandscapeView?: boolean; |   recipeLandscapeView?: boolean; | ||||||
|   recipeDisableComments?: boolean; |   recipeDisableComments?: boolean; | ||||||
|   recipeDisableAmount?: boolean; |  | ||||||
| } | } | ||||||
| export interface CreateInviteToken { | export interface CreateInviteToken { | ||||||
|   uses: number; |   uses: number; | ||||||
| @@ -191,7 +190,6 @@ export interface ReadHouseholdPreferences { | |||||||
|   recipeShowAssets?: boolean; |   recipeShowAssets?: boolean; | ||||||
|   recipeLandscapeView?: boolean; |   recipeLandscapeView?: boolean; | ||||||
|   recipeDisableComments?: boolean; |   recipeDisableComments?: boolean; | ||||||
|   recipeDisableAmount?: boolean; |  | ||||||
|   id: string; |   id: string; | ||||||
| } | } | ||||||
| export interface HouseholdUserSummary { | export interface HouseholdUserSummary { | ||||||
| @@ -269,7 +267,6 @@ export interface SaveHouseholdPreferences { | |||||||
|   recipeShowAssets?: boolean; |   recipeShowAssets?: boolean; | ||||||
|   recipeLandscapeView?: boolean; |   recipeLandscapeView?: boolean; | ||||||
|   recipeDisableComments?: boolean; |   recipeDisableComments?: boolean; | ||||||
|   recipeDisableAmount?: boolean; |  | ||||||
|   householdId: string; |   householdId: string; | ||||||
| } | } | ||||||
| export interface SaveInviteToken { | export interface SaveInviteToken { | ||||||
| @@ -303,8 +300,6 @@ export interface RecipeIngredient { | |||||||
|   unit?: IngredientUnit | CreateIngredientUnit | null; |   unit?: IngredientUnit | CreateIngredientUnit | null; | ||||||
|   food?: IngredientFood | CreateIngredientFood | null; |   food?: IngredientFood | CreateIngredientFood | null; | ||||||
|   note?: string | null; |   note?: string | null; | ||||||
|   isFood?: boolean | null; |  | ||||||
|   disableAmount?: boolean; |  | ||||||
|   display?: string; |   display?: string; | ||||||
|   title?: string | null; |   title?: string | null; | ||||||
|   originalText?: string | null; |   originalText?: string | null; | ||||||
| @@ -409,8 +404,6 @@ export interface ShoppingListItemBase { | |||||||
|   unit?: IngredientUnit | CreateIngredientUnit | null; |   unit?: IngredientUnit | CreateIngredientUnit | null; | ||||||
|   food?: IngredientFood | CreateIngredientFood | null; |   food?: IngredientFood | CreateIngredientFood | null; | ||||||
|   note?: string | null; |   note?: string | null; | ||||||
|   isFood?: boolean; |  | ||||||
|   disableAmount?: boolean | null; |  | ||||||
|   display?: string; |   display?: string; | ||||||
|   shoppingListId: string; |   shoppingListId: string; | ||||||
|   checked?: boolean; |   checked?: boolean; | ||||||
| @@ -427,8 +420,6 @@ export interface ShoppingListItemCreate { | |||||||
|   unit?: IngredientUnit | CreateIngredientUnit | null; |   unit?: IngredientUnit | CreateIngredientUnit | null; | ||||||
|   food?: IngredientFood | CreateIngredientFood | null; |   food?: IngredientFood | CreateIngredientFood | null; | ||||||
|   note?: string | null; |   note?: string | null; | ||||||
|   isFood?: boolean; |  | ||||||
|   disableAmount?: boolean | null; |  | ||||||
|   display?: string; |   display?: string; | ||||||
|   shoppingListId: string; |   shoppingListId: string; | ||||||
|   checked?: boolean; |   checked?: boolean; | ||||||
| @@ -453,8 +444,6 @@ export interface ShoppingListItemOut { | |||||||
|   unit?: IngredientUnit | null; |   unit?: IngredientUnit | null; | ||||||
|   food?: IngredientFood | null; |   food?: IngredientFood | null; | ||||||
|   note?: string | null; |   note?: string | null; | ||||||
|   isFood?: boolean; |  | ||||||
|   disableAmount?: boolean | null; |  | ||||||
|   display?: string; |   display?: string; | ||||||
|   shoppingListId: string; |   shoppingListId: string; | ||||||
|   checked?: boolean; |   checked?: boolean; | ||||||
| @@ -494,8 +483,6 @@ export interface ShoppingListItemUpdate { | |||||||
|   unit?: IngredientUnit | CreateIngredientUnit | null; |   unit?: IngredientUnit | CreateIngredientUnit | null; | ||||||
|   food?: IngredientFood | CreateIngredientFood | null; |   food?: IngredientFood | CreateIngredientFood | null; | ||||||
|   note?: string | null; |   note?: string | null; | ||||||
|   isFood?: boolean; |  | ||||||
|   disableAmount?: boolean | null; |  | ||||||
|   display?: string; |   display?: string; | ||||||
|   shoppingListId: string; |   shoppingListId: string; | ||||||
|   checked?: boolean; |   checked?: boolean; | ||||||
| @@ -513,8 +500,6 @@ export interface ShoppingListItemUpdateBulk { | |||||||
|   unit?: IngredientUnit | CreateIngredientUnit | null; |   unit?: IngredientUnit | CreateIngredientUnit | null; | ||||||
|   food?: IngredientFood | CreateIngredientFood | null; |   food?: IngredientFood | CreateIngredientFood | null; | ||||||
|   note?: string | null; |   note?: string | null; | ||||||
|   isFood?: boolean; |  | ||||||
|   disableAmount?: boolean | null; |  | ||||||
|   display?: string; |   display?: string; | ||||||
|   shoppingListId: string; |   shoppingListId: string; | ||||||
|   checked?: boolean; |   checked?: boolean; | ||||||
| @@ -679,14 +664,11 @@ export interface UpdateHouseholdPreferences { | |||||||
|   recipeShowAssets?: boolean; |   recipeShowAssets?: boolean; | ||||||
|   recipeLandscapeView?: boolean; |   recipeLandscapeView?: boolean; | ||||||
|   recipeDisableComments?: boolean; |   recipeDisableComments?: boolean; | ||||||
|   recipeDisableAmount?: boolean; |  | ||||||
| } | } | ||||||
| export interface RecipeIngredientBase { | export interface RecipeIngredientBase { | ||||||
|   quantity?: number | null; |   quantity?: number | null; | ||||||
|   unit?: IngredientUnit | CreateIngredientUnit | null; |   unit?: IngredientUnit | CreateIngredientUnit | null; | ||||||
|   food?: IngredientFood | CreateIngredientFood | null; |   food?: IngredientFood | CreateIngredientFood | null; | ||||||
|   note?: string | null; |   note?: string | null; | ||||||
|   isFood?: boolean | null; |  | ||||||
|   disableAmount?: boolean | null; |  | ||||||
|   display?: string; |   display?: string; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -31,7 +31,6 @@ export interface RecipeSettings { | |||||||
|   showAssets?: boolean; |   showAssets?: boolean; | ||||||
|   landscapeView?: boolean; |   landscapeView?: boolean; | ||||||
|   disableComments?: boolean; |   disableComments?: boolean; | ||||||
|   disableAmount?: boolean; |  | ||||||
|   locked?: boolean; |   locked?: boolean; | ||||||
| } | } | ||||||
| export interface AssignTags { | export interface AssignTags { | ||||||
| @@ -212,8 +211,6 @@ export interface RecipeIngredient { | |||||||
|   unit?: IngredientUnit | CreateIngredientUnit | null; |   unit?: IngredientUnit | CreateIngredientUnit | null; | ||||||
|   food?: IngredientFood | CreateIngredientFood | null; |   food?: IngredientFood | CreateIngredientFood | null; | ||||||
|   note?: string | null; |   note?: string | null; | ||||||
|   isFood?: boolean | null; |  | ||||||
|   disableAmount?: boolean; |  | ||||||
|   display?: string; |   display?: string; | ||||||
|   title?: string | null; |   title?: string | null; | ||||||
|   originalText?: string | null; |   originalText?: string | null; | ||||||
| @@ -347,8 +344,6 @@ export interface RecipeIngredientBase { | |||||||
|   unit?: IngredientUnit | CreateIngredientUnit | null; |   unit?: IngredientUnit | CreateIngredientUnit | null; | ||||||
|   food?: IngredientFood | CreateIngredientFood | null; |   food?: IngredientFood | CreateIngredientFood | null; | ||||||
|   note?: string | null; |   note?: string | null; | ||||||
|   isFood?: boolean | null; |  | ||||||
|   disableAmount?: boolean | null; |  | ||||||
|   display?: string; |   display?: string; | ||||||
| } | } | ||||||
| export interface RecipeLastMade { | export interface RecipeLastMade { | ||||||
|   | |||||||
| @@ -364,7 +364,6 @@ export default defineNuxtComponent({ | |||||||
|         confidence: {}, |         confidence: {}, | ||||||
|         ingredient: { |         ingredient: { | ||||||
|           quantity: 1.0, |           quantity: 1.0, | ||||||
|           disableAmount: false, |  | ||||||
|           referenceId: uuid4(), |           referenceId: uuid4(), | ||||||
|         }, |         }, | ||||||
|       } as ParsedIngredient; |       } as ParsedIngredient; | ||||||
| @@ -409,10 +408,6 @@ export default defineNuxtComponent({ | |||||||
|       } |       } | ||||||
|  |  | ||||||
|       recipe.value.recipeIngredient = ingredients; |       recipe.value.recipeIngredient = ingredients; | ||||||
|       if (recipe.value.settings) { |  | ||||||
|         recipe.value.settings.disableAmount = false; |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       const { response } = await api.recipes.updateOne(recipe.value.slug, recipe.value); |       const { response } = await api.recipes.updateOne(recipe.value.slug, recipe.value); | ||||||
|  |  | ||||||
|       if (response?.status === 200) { |       if (response?.status === 200) { | ||||||
|   | |||||||
| @@ -415,7 +415,6 @@ export default defineNuxtComponent({ | |||||||
|       showAssets: false, |       showAssets: false, | ||||||
|       landscapeView: false, |       landscapeView: false, | ||||||
|       disableComments: false, |       disableComments: false, | ||||||
|       disableAmount: false, |  | ||||||
|       locked: false, |       locked: false, | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -95,12 +95,6 @@ export default defineNuxtComponent({ | |||||||
| 		      label: i18n.t("group.disable-users-from-commenting-on-recipes"), | 		      label: i18n.t("group.disable-users-from-commenting-on-recipes"), | ||||||
| 		      description: i18n.t("group.disable-users-from-commenting-on-recipes-description"), | 		      description: i18n.t("group.disable-users-from-commenting-on-recipes-description"), | ||||||
| 		    } as Preference, | 		    } as Preference, | ||||||
| 		    { |  | ||||||
| 		      key: "recipeDisableAmount", |  | ||||||
| 		      value: household.value.preferences.recipeDisableAmount || false, |  | ||||||
| 		      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"), |  | ||||||
| 		    } as Preference, |  | ||||||
| 		  ]; | 		  ]; | ||||||
| 		}); | 		}); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -762,31 +762,12 @@ export default defineNuxtComponent({ | |||||||
|  |  | ||||||
|     const contextActions = { |     const contextActions = { | ||||||
|       delete: "delete", |       delete: "delete", | ||||||
|       setIngredient: "setIngredient", |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     const contextMenu = [ |     const contextMenu = [ | ||||||
|       { title: i18n.t("general.delete"), action: contextActions.delete }, |       { title: i18n.t("general.delete"), action: contextActions.delete }, | ||||||
|       { title: i18n.t("recipe.ingredient"), action: contextActions.setIngredient }, |  | ||||||
|     ]; |     ]; | ||||||
|  |  | ||||||
|     function contextMenuAction(action: string, item: ShoppingListItemOut, idx: number) { |  | ||||||
|       if (!shoppingList.value?.listItems) { |  | ||||||
|         return; |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       switch (action) { |  | ||||||
|         case contextActions.delete: |  | ||||||
|           shoppingList.value.listItems = shoppingList.value?.listItems.filter(itm => itm.id !== item.id); |  | ||||||
|           break; |  | ||||||
|         case contextActions.setIngredient: |  | ||||||
|           shoppingList.value.listItems[idx].isFood = !shoppingList.value.listItems[idx].isFood; |  | ||||||
|           break; |  | ||||||
|         default: |  | ||||||
|           break; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // ===================================== |     // ===================================== | ||||||
|     // Labels, Units, Foods |     // Labels, Units, Foods | ||||||
|     // TODO: Extract to Composable |     // TODO: Extract to Composable | ||||||
| @@ -901,7 +882,7 @@ export default defineNuxtComponent({ | |||||||
|       shoppingList.value.listItems.forEach((item) => { |       shoppingList.value.listItems.forEach((item) => { | ||||||
|         const key = item.checked |         const key = item.checked | ||||||
|           ? checkedItemKey |           ? checkedItemKey | ||||||
|           : item.isFood && item.food?.name |           : item.food?.name | ||||||
|             ? item.food.name |             ? item.food.name | ||||||
|             : item.note || ""; |             : item.note || ""; | ||||||
|  |  | ||||||
| @@ -1087,13 +1068,12 @@ export default defineNuxtComponent({ | |||||||
|     const createEditorOpen = ref(false); |     const createEditorOpen = ref(false); | ||||||
|     const createListItemData = ref<ShoppingListItemOut>(listItemFactory()); |     const createListItemData = ref<ShoppingListItemOut>(listItemFactory()); | ||||||
|  |  | ||||||
|     function listItemFactory(isFood = false): ShoppingListItemOut { |     function listItemFactory(): ShoppingListItemOut { | ||||||
|       return { |       return { | ||||||
|         id: uuid4(), |         id: uuid4(), | ||||||
|         shoppingListId: id, |         shoppingListId: id, | ||||||
|         checked: false, |         checked: false, | ||||||
|         position: shoppingList.value?.listItems?.length || 1, |         position: shoppingList.value?.listItems?.length || 1, | ||||||
|         isFood, |  | ||||||
|         quantity: 0, |         quantity: 0, | ||||||
|         note: "", |         note: "", | ||||||
|         labelId: undefined, |         labelId: undefined, | ||||||
| @@ -1144,7 +1124,7 @@ export default defineNuxtComponent({ | |||||||
|         shoppingList.value.listItems.push(createListItemData.value); |         shoppingList.value.listItems.push(createListItemData.value); | ||||||
|         updateListItemOrder(); |         updateListItemOrder(); | ||||||
|       } |       } | ||||||
|       createListItemData.value = listItemFactory(createListItemData.value.isFood || false); |       createListItemData.value = listItemFactory(); | ||||||
|       refresh(); |       refresh(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -1217,7 +1197,6 @@ export default defineNuxtComponent({ | |||||||
|       addRecipeReferenceToList, |       addRecipeReferenceToList, | ||||||
|       allLabels, |       allLabels, | ||||||
|       contextMenu, |       contextMenu, | ||||||
|       contextMenuAction, |  | ||||||
|       copyListItems, |       copyListItems, | ||||||
|       createEditorOpen, |       createEditorOpen, | ||||||
|       createListItem, |       createListItem, | ||||||
|   | |||||||
| @@ -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_show_assets: Mapped[bool | None] = mapped_column(sa.Boolean, default=False) | ||||||
|     recipe_landscape_view: 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) |     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) |     recipe_disable_amount: Mapped[bool | None] = mapped_column(sa.Boolean, default=True) | ||||||
|  |  | ||||||
|     @auto_init() |     @auto_init() | ||||||
|   | |||||||
| @@ -65,7 +65,6 @@ class ShoppingListItem(SqlAlchemyBase, BaseMixins): | |||||||
|     quantity: Mapped[float | None] = mapped_column(Float, default=1) |     quantity: Mapped[float | None] = mapped_column(Float, default=1) | ||||||
|     note: Mapped[str | None] = mapped_column(String) |     note: Mapped[str | None] = mapped_column(String) | ||||||
|  |  | ||||||
|     is_food: Mapped[bool | None] = mapped_column(Boolean, default=False) |  | ||||||
|     extras: Mapped[list[ShoppingListItemExtras]] = orm.relationship( |     extras: Mapped[list[ShoppingListItemExtras]] = orm.relationship( | ||||||
|         "ShoppingListItemExtras", cascade="all, delete-orphan" |         "ShoppingListItemExtras", cascade="all, delete-orphan" | ||||||
|     ) |     ) | ||||||
| @@ -88,6 +87,9 @@ class ShoppingListItem(SqlAlchemyBase, BaseMixins): | |||||||
|     ) |     ) | ||||||
|     model_config = ConfigDict(exclude={"label", "food", "unit"}) |     model_config = ConfigDict(exclude={"label", "food", "unit"}) | ||||||
|  |  | ||||||
|  |     # Deprecated | ||||||
|  |     is_food: Mapped[bool | None] = mapped_column(Boolean, default=False) | ||||||
|  |  | ||||||
|     @api_extras |     @api_extras | ||||||
|     @auto_init() |     @auto_init() | ||||||
|     def __init__(self, **_) -> None: |     def __init__(self, **_) -> None: | ||||||
|   | |||||||
| @@ -13,10 +13,12 @@ class RecipeSettings(SqlAlchemyBase): | |||||||
|     show_nutrition: Mapped[bool | None] = mapped_column(sa.Boolean) |     show_nutrition: Mapped[bool | None] = mapped_column(sa.Boolean) | ||||||
|     show_assets: 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) |     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) |     disable_comments: Mapped[bool | None] = mapped_column(sa.Boolean, default=False) | ||||||
|     locked: 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__( |     def __init__( | ||||||
|         self, |         self, | ||||||
|         public=True, |         public=True, | ||||||
|   | |||||||
| @@ -440,7 +440,6 @@ class RepositoryRecipes(HouseholdRepositoryGeneric[Recipe, RecipeModel]): | |||||||
|             ) |             ) | ||||||
|             q = ( |             q = ( | ||||||
|                 q.join(settings_alias, self.model.settings) |                 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(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) |                 .outerjoin(total_user_foods_query, self.model.id == total_user_foods_query.c.recipe_id) | ||||||
|                 .filter( |                 .filter( | ||||||
|   | |||||||
| @@ -101,10 +101,6 @@ 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" |         image_url = "https://raw.githubusercontent.com/mealie-recipes/mealie/9571816ac4eed5beacfc0abf6c03eff1427fd0eb/frontend/static/icons/android-chrome-512x512.png" | ||||||
|  |  | ||||||
|     ingredients: list[str] = [] |     ingredients: list[str] = [] | ||||||
|     if recipe.settings.disable_amount:  # type: ignore |  | ||||||
|         ingredients = [escape(i.note) for i in recipe.recipe_ingredient if i.note] |  | ||||||
|  |  | ||||||
|     else: |  | ||||||
|     for ing in recipe.recipe_ingredient: |     for ing in recipe.recipe_ingredient: | ||||||
|         s = "" |         s = "" | ||||||
|         if ing.quantity: |         if ing.quantity: | ||||||
|   | |||||||
| @@ -66,7 +66,6 @@ class ShoppingListItemBase(RecipeIngredientBase): | |||||||
|     label_id: UUID4 | None = None |     label_id: UUID4 | None = None | ||||||
|     unit_id: UUID4 | None = None |     unit_id: UUID4 | None = None | ||||||
|  |  | ||||||
|     is_food: bool = False |  | ||||||
|     extras: dict | None = {} |     extras: dict | None = {} | ||||||
|  |  | ||||||
|     @field_validator("extras", mode="before") |     @field_validator("extras", mode="before") | ||||||
|   | |||||||
| @@ -18,7 +18,6 @@ class UpdateHouseholdPreferences(MealieModel): | |||||||
|     recipe_show_assets: bool = False |     recipe_show_assets: bool = False | ||||||
|     recipe_landscape_view: bool = False |     recipe_landscape_view: bool = False | ||||||
|     recipe_disable_comments: bool = False |     recipe_disable_comments: bool = False | ||||||
|     recipe_disable_amount: bool = True |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class CreateHouseholdPreferences(UpdateHouseholdPreferences): ... | class CreateHouseholdPreferences(UpdateHouseholdPreferences): ... | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ from pathlib import Path | |||||||
| from typing import Annotated, Any, ClassVar | from typing import Annotated, Any, ClassVar | ||||||
| from uuid import uuid4 | 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 pydantic_core.core_schema import ValidationInfo | ||||||
| from slugify import slugify | from slugify import slugify | ||||||
| from sqlalchemy import Select, desc, func, or_, select, text | from sqlalchemy import Select, desc, func, or_, select, text | ||||||
| @@ -228,18 +228,6 @@ class Recipe(RecipeSummary): | |||||||
|  |  | ||||||
|     model_config = ConfigDict(from_attributes=True) |     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") |     @field_validator("slug", mode="before") | ||||||
|     def validate_slug(slug: str, info: ValidationInfo): |     def validate_slug(slug: str, info: ValidationInfo): | ||||||
|         if not info.data.get("name"): |         if not info.data.get("name"): | ||||||
|   | |||||||
| @@ -152,13 +152,11 @@ class IngredientUnit(CreateIngredientUnit): | |||||||
|  |  | ||||||
|  |  | ||||||
| class RecipeIngredientBase(MealieModel): | class RecipeIngredientBase(MealieModel): | ||||||
|     quantity: NoneFloat = 1 |     quantity: NoneFloat = 0 | ||||||
|     unit: IngredientUnit | CreateIngredientUnit | None = None |     unit: IngredientUnit | CreateIngredientUnit | None = None | ||||||
|     food: IngredientFood | CreateIngredientFood | None = None |     food: IngredientFood | CreateIngredientFood | None = None | ||||||
|     note: str | None = "" |     note: str | None = "" | ||||||
|  |  | ||||||
|     is_food: bool | None = None |  | ||||||
|     disable_amount: bool | None = None |  | ||||||
|     display: str = "" |     display: str = "" | ||||||
|     """ |     """ | ||||||
|     How the ingredient should be displayed |     How the ingredient should be displayed | ||||||
| @@ -166,20 +164,6 @@ class RecipeIngredientBase(MealieModel): | |||||||
|     Automatically calculated after the object is created, unless overwritten |     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") |     @model_validator(mode="after") | ||||||
|     def format_display(self): |     def format_display(self): | ||||||
|         if not self.display: |         if not self.display: | ||||||
| @@ -266,20 +250,9 @@ class RecipeIngredientBase(MealieModel): | |||||||
|     def _format_display(self) -> str: |     def _format_display(self) -> str: | ||||||
|         components = [] |         components = [] | ||||||
|  |  | ||||||
|         use_food = True |         if self.quantity: | ||||||
|         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): |  | ||||||
|             components.append(self._format_quantity_for_display()) |             components.append(self._format_quantity_for_display()) | ||||||
|  |  | ||||||
|         if not use_food: |  | ||||||
|             components.append(self.note or "") |  | ||||||
|         else: |  | ||||||
|         if self.quantity and self.unit: |         if self.quantity and self.unit: | ||||||
|             components.append(self._format_unit_for_display()) |             components.append(self._format_unit_for_display()) | ||||||
|  |  | ||||||
| @@ -299,7 +272,6 @@ class IngredientUnitPagination(PaginationBase): | |||||||
| class RecipeIngredient(RecipeIngredientBase): | class RecipeIngredient(RecipeIngredientBase): | ||||||
|     title: str | None = None |     title: str | None = None | ||||||
|     original_text: 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 |     # 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 |     # 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 |     show_assets: bool = False | ||||||
|     landscape_view: bool = False |     landscape_view: bool = False | ||||||
|     disable_comments: bool = True |     disable_comments: bool = True | ||||||
|     disable_amount: bool = True |  | ||||||
|     locked: bool = False |     locked: bool = False | ||||||
|     model_config = ConfigDict(from_attributes=True) |     model_config = ConfigDict(from_attributes=True) | ||||||
|   | |||||||
| @@ -312,12 +312,10 @@ class ShoppingListService: | |||||||
|         list_items: list[ShoppingListItemCreate] = [] |         list_items: list[ShoppingListItemCreate] = [] | ||||||
|         for ingredient in recipe_ingredients: |         for ingredient in recipe_ingredients: | ||||||
|             if isinstance(ingredient.food, IngredientFood): |             if isinstance(ingredient.food, IngredientFood): | ||||||
|                 is_food = True |  | ||||||
|                 food_id = ingredient.food.id |                 food_id = ingredient.food.id | ||||||
|                 label_id = ingredient.food.label_id |                 label_id = ingredient.food.label_id | ||||||
|  |  | ||||||
|             else: |             else: | ||||||
|                 is_food = False |  | ||||||
|                 food_id = None |                 food_id = None | ||||||
|                 label_id = None |                 label_id = None | ||||||
|  |  | ||||||
| @@ -329,7 +327,6 @@ class ShoppingListService: | |||||||
|  |  | ||||||
|             new_item = ShoppingListItemCreate( |             new_item = ShoppingListItemCreate( | ||||||
|                 shopping_list_id=list_id, |                 shopping_list_id=list_id, | ||||||
|                 is_food=is_food, |  | ||||||
|                 note=ingredient.note, |                 note=ingredient.note, | ||||||
|                 quantity=ingredient.quantity * scale if ingredient.quantity else 0, |                 quantity=ingredient.quantity * scale if ingredient.quantity else 0, | ||||||
|                 food_id=food_id, |                 food_id=food_id, | ||||||
|   | |||||||
| @@ -177,7 +177,6 @@ class BaseMigrator(BaseService): | |||||||
|             show_assets=self.household.preferences.recipe_show_assets, |             show_assets=self.household.preferences.recipe_show_assets, | ||||||
|             landscape_view=self.household.preferences.recipe_landscape_view, |             landscape_view=self.household.preferences.recipe_landscape_view, | ||||||
|             disable_comments=self.household.preferences.recipe_disable_comments, |             disable_comments=self.household.preferences.recipe_disable_comments, | ||||||
|             disable_amount=self.household.preferences.recipe_disable_amount, |  | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|         for recipe in validated_recipes: |         for recipe in validated_recipes: | ||||||
|   | |||||||
| @@ -36,7 +36,6 @@ class BruteForceParser(ABCIngredientParser): | |||||||
|             ingredient=RecipeIngredient( |             ingredient=RecipeIngredient( | ||||||
|                 unit=CreateIngredientUnit(name=bfi.unit), |                 unit=CreateIngredientUnit(name=bfi.unit), | ||||||
|                 food=CreateIngredientFood(name=bfi.food), |                 food=CreateIngredientFood(name=bfi.food), | ||||||
|                 disable_amount=False, |  | ||||||
|                 quantity=bfi.amount, |                 quantity=bfi.amount, | ||||||
|                 note=bfi.note, |                 note=bfi.note, | ||||||
|             ), |             ), | ||||||
| @@ -151,7 +150,6 @@ class NLPParser(ABCIngredientParser): | |||||||
|                 quantity=qty, |                 quantity=qty, | ||||||
|                 unit=CreateIngredientUnit(name=unit) if unit else None, |                 unit=CreateIngredientUnit(name=unit) if unit else None, | ||||||
|                 food=CreateIngredientFood(name=food) if food else None, |                 food=CreateIngredientFood(name=food) if food else None, | ||||||
|                 disable_amount=False, |  | ||||||
|                 note=note, |                 note=note, | ||||||
|             ), |             ), | ||||||
|         ) |         ) | ||||||
|   | |||||||
| @@ -173,7 +173,6 @@ class RecipeService(RecipeServiceBase): | |||||||
|                     show_assets=self.household.preferences.recipe_show_assets, |                     show_assets=self.household.preferences.recipe_show_assets, | ||||||
|                     landscape_view=self.household.preferences.recipe_landscape_view, |                     landscape_view=self.household.preferences.recipe_landscape_view, | ||||||
|                     disable_comments=self.household.preferences.recipe_disable_comments, |                     disable_comments=self.household.preferences.recipe_disable_comments, | ||||||
|                     disable_amount=self.household.preferences.recipe_disable_amount, |  | ||||||
|                 ) |                 ) | ||||||
|             else: |             else: | ||||||
|                 data.settings = RecipeSettings() |                 data.settings = RecipeSettings() | ||||||
|   | |||||||
| @@ -74,7 +74,6 @@ class RegistrationService: | |||||||
|             recipe_show_assets=self.registration.advanced, |             recipe_show_assets=self.registration.advanced, | ||||||
|             recipe_landscape_view=False, |             recipe_landscape_view=False, | ||||||
|             recipe_disable_comments=self.registration.advanced, |             recipe_disable_comments=self.registration.advanced, | ||||||
|             recipe_disable_amount=self.registration.advanced, |  | ||||||
|         ) |         ) | ||||||
|         return HouseholdService.create_household(group_repos, household_data, household_preferences) |         return HouseholdService.create_household(group_repos, household_data, household_preferences) | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										12
									
								
								tests/fixtures/fixture_recipe.py
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								tests/fixtures/fixture_recipe.py
									
									
									
									
										vendored
									
									
								
							| @@ -37,12 +37,12 @@ def recipe_ingredient_only(unique_user: TestUser): | |||||||
|         group_id=unique_user.group_id, |         group_id=unique_user.group_id, | ||||||
|         name=random_string(10), |         name=random_string(10), | ||||||
|         recipe_ingredient=[ |         recipe_ingredient=[ | ||||||
|             RecipeIngredient(note="Ingredient 1"), |             RecipeIngredient(quantity=1, note="Ingredient 1"), | ||||||
|             RecipeIngredient(note="Ingredient 2"), |             RecipeIngredient(quantity=1, note="Ingredient 2"), | ||||||
|             RecipeIngredient(note="Ingredient 3"), |             RecipeIngredient(quantity=1, note="Ingredient 3"), | ||||||
|             RecipeIngredient(note="Ingredient 4"), |             RecipeIngredient(quantity=1, note="Ingredient 4"), | ||||||
|             RecipeIngredient(note="Ingredient 5"), |             RecipeIngredient(quantity=1, note="Ingredient 5"), | ||||||
|             RecipeIngredient(note="Ingredient 6"), |             RecipeIngredient(quantity=1, note="Ingredient 6"), | ||||||
|         ], |         ], | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								tests/fixtures/fixture_shopping_lists.py
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								tests/fixtures/fixture_shopping_lists.py
									
									
									
									
										vendored
									
									
								
							| @@ -12,7 +12,6 @@ def create_item(list_id: UUID4) -> dict: | |||||||
|         "shopping_list_id": str(list_id), |         "shopping_list_id": str(list_id), | ||||||
|         "checked": False, |         "checked": False, | ||||||
|         "position": 0, |         "position": 0, | ||||||
|         "is_food": False, |  | ||||||
|         "note": random_string(10), |         "note": random_string(10), | ||||||
|         "quantity": 1, |         "quantity": 1, | ||||||
|         "unit_id": None, |         "unit_id": None, | ||||||
|   | |||||||
| @@ -49,7 +49,6 @@ def test_admin_update_household(api_client: TestClient, admin_user: TestUser, un | |||||||
|             "recipeShowAssets": random_bool(), |             "recipeShowAssets": random_bool(), | ||||||
|             "recipeLandscapeView": random_bool(), |             "recipeLandscapeView": random_bool(), | ||||||
|             "recipeDisableComments": random_bool(), |             "recipeDisableComments": random_bool(), | ||||||
|             "recipeDisableAmount": random_bool(), |  | ||||||
|         }, |         }, | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -377,7 +377,6 @@ def test_get_suggested_recipes( | |||||||
|         SaveIngredientFood(id=uuid4(), name=random_string(), group_id=unique_user.group_id) |         SaveIngredientFood(id=uuid4(), name=random_string(), group_id=unique_user.group_id) | ||||||
|     ) |     ) | ||||||
|     random_recipe.recipe_ingredient = [RecipeIngredient(food_id=known_food.id, food=known_food)] |     random_recipe.recipe_ingredient = [RecipeIngredient(food_id=known_food.id, food=known_food)] | ||||||
|     random_recipe.settings.disable_amount = False |  | ||||||
|     database.recipes.update(random_recipe.slug, random_recipe) |     database.recipes.update(random_recipe.slug, random_recipe) | ||||||
|  |  | ||||||
|     ## Try to find suggested recipes |     ## Try to find suggested recipes | ||||||
|   | |||||||
| @@ -145,6 +145,11 @@ from mealie.schema.recipe.recipe_ingredient import ( | |||||||
| @pytest.mark.parametrize( | @pytest.mark.parametrize( | ||||||
|     ["food", "expected_food_singular_string", "expected_food_plural_string"], |     ["food", "expected_food_singular_string", "expected_food_plural_string"], | ||||||
|     [ |     [ | ||||||
|  |         [ | ||||||
|  |             None, | ||||||
|  |             "", | ||||||
|  |             "", | ||||||
|  |         ], | ||||||
|         [ |         [ | ||||||
|             IngredientFood(id=uuid4(), name="chopped onion", plural_name=None), |             IngredientFood(id=uuid4(), name="chopped onion", plural_name=None), | ||||||
|             "chopped onion", |             "chopped onion", | ||||||
| @@ -157,16 +162,14 @@ from mealie.schema.recipe.recipe_ingredient import ( | |||||||
|         ], |         ], | ||||||
|     ], |     ], | ||||||
| ) | ) | ||||||
| @pytest.mark.parametrize("note", ["very thin", ""]) | @pytest.mark.parametrize("note", ["very thin", "", None]) | ||||||
| @pytest.mark.parametrize("use_food", [True, False]) |  | ||||||
| def test_ingredient_display( | def test_ingredient_display( | ||||||
|     quantity: float | None, |     quantity: float | None, | ||||||
|     quantity_display_decimal: str, |     quantity_display_decimal: str, | ||||||
|     quantity_display_fraction: str, |     quantity_display_fraction: str, | ||||||
|     unit: IngredientUnit | None, |     unit: IngredientUnit | None, | ||||||
|     food: IngredientFood, |     food: IngredientFood | None, | ||||||
|     note: str, |     note: str | None, | ||||||
|     use_food: bool, |  | ||||||
|     expect_display_fraction: bool, |     expect_display_fraction: bool, | ||||||
|     expect_plural_unit: bool, |     expect_plural_unit: bool, | ||||||
|     expect_plural_food: bool, |     expect_plural_food: bool, | ||||||
| @@ -176,7 +179,6 @@ def test_ingredient_display( | |||||||
|     expected_food_plural_string: str, |     expected_food_plural_string: str, | ||||||
| ): | ): | ||||||
|     expected_components = [] |     expected_components = [] | ||||||
|     if use_food: |  | ||||||
|     if expect_display_fraction: |     if expect_display_fraction: | ||||||
|         expected_components.append(quantity_display_fraction) |         expected_components.append(quantity_display_fraction) | ||||||
|     else: |     else: | ||||||
| @@ -188,24 +190,14 @@ def test_ingredient_display( | |||||||
|         else: |         else: | ||||||
|             expected_components.append(expected_unit_singular_string) |             expected_components.append(expected_unit_singular_string) | ||||||
|  |  | ||||||
|  |     if food: | ||||||
|         if expect_plural_food: |         if expect_plural_food: | ||||||
|             expected_components.append(expected_food_plural_string) |             expected_components.append(expected_food_plural_string) | ||||||
|         else: |         else: | ||||||
|             expected_components.append(expected_food_singular_string) |             expected_components.append(expected_food_singular_string) | ||||||
|  |  | ||||||
|         expected_components.append(note) |     expected_components.append(note or "") | ||||||
|  |  | ||||||
|     else: |  | ||||||
|         if quantity != 0 and quantity != 1: |  | ||||||
|             if expect_display_fraction: |  | ||||||
|                 expected_components.append(quantity_display_fraction) |  | ||||||
|             else: |  | ||||||
|                 expected_components.append(quantity_display_decimal) |  | ||||||
|  |  | ||||||
|         expected_components.append(note) |  | ||||||
|  |  | ||||||
|     expected_display_value = " ".join(c for c in expected_components if c) |     expected_display_value = " ".join(c for c in expected_components if c) | ||||||
|     ingredient = RecipeIngredient( |     ingredient = RecipeIngredient(quantity=quantity, unit=unit, food=food, note=note) | ||||||
|         quantity=quantity, unit=unit, food=food, note=note, use_food=use_food, disable_amount=not use_food |  | ||||||
|     ) |  | ||||||
|     assert ingredient.display == expected_display_value |     assert ingredient.display == expected_display_value | ||||||
|   | |||||||
| @@ -48,7 +48,6 @@ def create_recipe( | |||||||
|     *, |     *, | ||||||
|     foods: list[IngredientFood] | None = None, |     foods: list[IngredientFood] | None = None, | ||||||
|     tools: list[RecipeToolOut] | None = None, |     tools: list[RecipeToolOut] | None = None, | ||||||
|     disable_amount: bool = False, |  | ||||||
|     **kwargs, |     **kwargs, | ||||||
| ): | ): | ||||||
|     if foods: |     if foods: | ||||||
| @@ -63,7 +62,7 @@ def create_recipe( | |||||||
|             name=kwargs.pop("name", random_string()), |             name=kwargs.pop("name", random_string()), | ||||||
|             recipe_ingredient=ingredients, |             recipe_ingredient=ingredients, | ||||||
|             tools=tools or [], |             tools=tools or [], | ||||||
|             settings=RecipeSettings(disable_amount=disable_amount), |             settings=RecipeSettings(), | ||||||
|             **kwargs, |             **kwargs, | ||||||
|         ) |         ) | ||||||
|     ) |     ) | ||||||
| @@ -338,60 +337,6 @@ def test_include_recipes_with_no_tools(api_client: TestClient, unique_user: Test | |||||||
|             unique_user.repos.recipes.delete(recipe.slug) |             unique_user.repos.recipes.delete(recipe.slug) | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_ignore_recipes_with_ingredient_amounts_disabled_with_foods(api_client: TestClient, unique_user: TestUser): |  | ||||||
|     known_food = create_food(unique_user) |  | ||||||
|     recipe_with_amounts = create_recipe(unique_user, foods=[known_food]) |  | ||||||
|     recipe_without_amounts = create_recipe(unique_user, foods=[known_food], disable_amount=True) |  | ||||||
|  |  | ||||||
|     try: |  | ||||||
|         response = api_client.get( |  | ||||||
|             api_routes.recipes_suggestions, |  | ||||||
|             params={"maxMissingFoods": 0, "maxMissingTools": 0, "foods": [str(known_food.id)]}, |  | ||||||
|             headers=unique_user.token, |  | ||||||
|         ) |  | ||||||
|         response.raise_for_status() |  | ||||||
|  |  | ||||||
|         data = response.json() |  | ||||||
|         assert {item["recipe"]["id"] for item in data["items"]} == {str(recipe_with_amounts.id)} |  | ||||||
|         for item in data["items"]: |  | ||||||
|             assert item["missingFoods"] == [] |  | ||||||
|  |  | ||||||
|     finally: |  | ||||||
|         for recipe in [recipe_with_amounts, recipe_without_amounts]: |  | ||||||
|             unique_user.repos.recipes.delete(recipe.slug) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_include_recipes_with_ingredient_amounts_disabled_without_foods(api_client: TestClient, unique_user: TestUser): |  | ||||||
|     known_tool = create_tool(unique_user) |  | ||||||
|     recipe_with_amounts = create_recipe(unique_user, tools=[known_tool]) |  | ||||||
|     recipe_without_amounts = create_recipe(unique_user, tools=[known_tool], disable_amount=True) |  | ||||||
|  |  | ||||||
|     try: |  | ||||||
|         response = api_client.get( |  | ||||||
|             api_routes.recipes_suggestions, |  | ||||||
|             params={ |  | ||||||
|                 "maxMissingFoods": 0, |  | ||||||
|                 "maxMissingTools": 0, |  | ||||||
|                 "includeFoodsOnHand": False, |  | ||||||
|                 "tools": [str(known_tool.id)], |  | ||||||
|             }, |  | ||||||
|             headers=unique_user.token, |  | ||||||
|         ) |  | ||||||
|         response.raise_for_status() |  | ||||||
|  |  | ||||||
|         data = response.json() |  | ||||||
|         assert {item["recipe"]["id"] for item in data["items"]} == { |  | ||||||
|             str(recipe_with_amounts.id), |  | ||||||
|             str(recipe_without_amounts.id), |  | ||||||
|         } |  | ||||||
|         for item in data["items"]: |  | ||||||
|             assert item["missingFoods"] == [] |  | ||||||
|  |  | ||||||
|     finally: |  | ||||||
|         for recipe in [recipe_with_amounts, recipe_without_amounts]: |  | ||||||
|             unique_user.repos.recipes.delete(recipe.slug) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_exclude_recipes_with_no_user_foods(api_client: TestClient, unique_user: TestUser): | def test_exclude_recipes_with_no_user_foods(api_client: TestClient, unique_user: TestUser): | ||||||
|     known_food = create_food(unique_user) |     known_food = create_food(unique_user) | ||||||
|     food_on_hand = create_food(unique_user, on_hand=True) |     food_on_hand = create_food(unique_user, on_hand=True) | ||||||
|   | |||||||
| @@ -20,8 +20,6 @@ def test_shopping_list_ingredient_validation(): | |||||||
|             "updatedAt": "2024-02-26T18:29:46.190758", |             "updatedAt": "2024-02-26T18:29:46.190758", | ||||||
|         }, |         }, | ||||||
|         "note": "", |         "note": "", | ||||||
|         "isFood": True, |  | ||||||
|         "disableAmount": False, |  | ||||||
|         "shoppingListId": "dc8bce82-2da9-49f0-94e6-6d69d311490e", |         "shoppingListId": "dc8bce82-2da9-49f0-94e6-6d69d311490e", | ||||||
|         "checked": False, |         "checked": False, | ||||||
|         "position": 5, |         "position": 5, | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user