From 8b682c33f3425135bb3bf30faabf72134e03780c Mon Sep 17 00:00:00 2001 From: smilerz Date: Thu, 28 Oct 2021 20:40:56 -0500 Subject: [PATCH] related recipes included when adding mealplan to shopping list --- cookbook/forms.py | 4 +- cookbook/helper/shopping_helper.py | 80 +++- cookbook/models.py | 154 ------- cookbook/serializer.py | 5 +- cookbook/signals.py | 54 ++- cookbook/templates/settings.html | 4 +- cookbook/views/api.py | 4 +- vue/src/apps/RecipeView/RecipeView.vue | 15 +- .../ContextMenu/ContextMenuSubmenu.vue | 34 -- .../ContextMenu/RecipeContextMenu.vue | 330 ++++++++------- .../components/Modals/MealPlanEditModal.vue | 33 +- vue/src/locales/en.json | 4 +- vue/src/utils/openapi/api.ts | 392 +++++++++--------- 13 files changed, 519 insertions(+), 594 deletions(-) delete mode 100644 vue/src/components/ContextMenu/ContextMenuSubmenu.vue diff --git a/cookbook/forms.py b/cookbook/forms.py index da86d6ecd..4d977dc3b 100644 --- a/cookbook/forms.py +++ b/cookbook/forms.py @@ -489,8 +489,8 @@ class ShoppingPreferenceForm(forms.ModelForm): 'of mobile data. If lower than instance limit it is reset when saving.' # noqa: E501 ), 'mealplan_autoadd_shopping': _('Automatically add meal plan ingredients to shopping list.'), - 'mealplan_autoexclude_onhand': _('When automatically adding a meal plan to the shopping list, exclude ingredients that are on hand.'), - 'mealplan_autoinclude_related': _('When automatically adding a meal plan to the shopping list, include all related recipes.'), + 'mealplan_autoinclude_related': _('When adding a meal plan to the shopping list (manually or automatically), include all related recipes.'), + 'mealplan_autoexclude_onhand': _('When adding a meal plan to the shopping list (manually or automatically), exclude ingredients that are on hand.'), 'default_delay': _('Default number of hours to delay a shopping list entry.'), 'filter_to_supermarket': _('Filter shopping list to only include supermarket categories.'), } diff --git a/cookbook/helper/shopping_helper.py b/cookbook/helper/shopping_helper.py index 9ea8a6113..17f9915b2 100644 --- a/cookbook/helper/shopping_helper.py +++ b/cookbook/helper/shopping_helper.py @@ -1,11 +1,15 @@ from datetime import timedelta +from decimal import Decimal from django.contrib.postgres.aggregates import ArrayAgg from django.db.models import F, OuterRef, Q, Subquery, Value from django.db.models.functions import Coalesce from django.utils import timezone -from cookbook.models import UserPreference +from cookbook.helper.HelperFunctions import Round, str2bool +from cookbook.models import (Ingredient, ShoppingListEntry, ShoppingListRecipe, + SupermarketCategoryRelation) +from recipes import settings def shopping_helper(qs, request): @@ -35,3 +39,77 @@ def shopping_helper(qs, request): supermarket_order = ['checked'] + supermarket_order return qs.order_by(*supermarket_order).select_related('unit', 'food', 'ingredient', 'created_by', 'list_recipe', 'list_recipe__mealplan', 'list_recipe__recipe') + + +def list_from_recipe(list_recipe=None, recipe=None, mealplan=None, servings=None, ingredients=None, created_by=None, space=None, append=False): + """ + Creates ShoppingListRecipe and associated ShoppingListEntrys from a recipe or a meal plan with a recipe + :param list_recipe: Modify an existing ShoppingListRecipe + :param recipe: Recipe to use as list of ingredients. One of [recipe, mealplan] are required + :param mealplan: alternatively use a mealplan recipe as source of ingredients + :param servings: Optional: Number of servings to use to scale shoppinglist. If servings = 0 an existing recipe list will be deleted + :param ingredients: Ingredients, list of ingredient IDs to include on the shopping list. When not provided all ingredients will be used + :param append: If False will remove any entries not included with ingredients, when True will append ingredients to the shopping list + """ + # TODO cascade to related recipes + r = recipe or getattr(mealplan, 'recipe', None) or getattr(list_recipe, 'recipe', None) + if not r: + raise ValueError(_("You must supply a recipe or mealplan")) + + created_by = created_by or getattr(mealplan, 'created_by', None) or getattr(list_recipe, 'created_by', None) + if not created_by: + raise ValueError(_("You must supply a created_by")) + + if type(servings) not in [int, float]: + servings = getattr(mealplan, 'servings', 1.0) + + shared_users = list(created_by.get_shopping_share()) + shared_users.append(created_by) + if list_recipe: + created = False + else: + list_recipe = ShoppingListRecipe.objects.create(recipe=r, mealplan=mealplan, servings=servings) + created = True + + if servings == 0 and not created: + list_recipe.delete() + return [] + elif ingredients: + ingredients = Ingredient.objects.filter(pk__in=ingredients, space=space) + else: + ingredients = Ingredient.objects.filter(step__recipe=r, space=space) + + add_ingredients = ingredients.values_list('id', flat=True) + if not append: + existing_list = ShoppingListEntry.objects.filter(list_recipe=list_recipe) + # delete shopping list entries not included in ingredients + existing_list.exclude(ingredient__in=ingredients).delete() + # add shopping list entries that did not previously exist + add_ingredients = set(add_ingredients) - set(existing_list.values_list('ingredient__id', flat=True)) + add_ingredients = Ingredient.objects.filter(id__in=add_ingredients, space=space) + + # if servings have changed, update the ShoppingListRecipe and existing Entrys + if servings <= 0: + servings = 1 + servings_factor = servings / r.servings + if not created and list_recipe.servings != servings: + update_ingredients = set(ingredients.values_list('id', flat=True)) - set(add_ingredients.values_list('id', flat=True)) + for sle in ShoppingListEntry.objects.filter(list_recipe=list_recipe, ingredient__id__in=update_ingredients): + sle.amount = sle.ingredient.amount * Decimal(servings_factor) + sle.save() + + # add any missing Entrys + for i in [x for x in add_ingredients if not x.food.ignore_shopping]: + + ShoppingListEntry.objects.create( + list_recipe=list_recipe, + food=i.food, + unit=i.unit, + ingredient=i, + amount=i.amount * Decimal(servings_factor), + created_by=created_by, + space=space, + ) + + # return all shopping list items + return list_recipe diff --git a/cookbook/models.py b/cookbook/models.py index 198185975..7fbff948e 100644 --- a/cookbook/models.py +++ b/cookbook/models.py @@ -783,17 +783,6 @@ class MealPlan(ExportModelOperationsMixin('meal_plan'), models.Model, Permission space = models.ForeignKey(Space, on_delete=models.CASCADE) objects = ScopedManager(space='space') - # TODO override create method to check if recipes are always added - # @classmethod - # def generate_shoppinglist(self, ingredients=None): - # recipe_list = ShoppingListRecipe.objects.create() - # if not ingredients: - # ingredients = Ingredient.objects.filter(step__recipe=self.recipe) - # for i in ingredients: - # ShoppingListEntry.objects.create( - - # ) - def get_label(self): if self.title: return self.title @@ -847,149 +836,6 @@ class ShoppingListEntry(ExportModelOperationsMixin('shopping_list_entry'), model space = models.ForeignKey(Space, on_delete=models.CASCADE) objects = ScopedManager(space='space') - @classmethod - @atomic - def list_from_recipe(self, list_recipe=None, recipe=None, mealplan=None, servings=None, ingredients=None, created_by=None, space=None): - """ - Creates ShoppingListRecipe and associated ShoppingListEntrys from a recipe or a meal plan with a recipe - :param list_recipe: Modify an existing ShoppingListRecipe - :param recipe: Recipe to use as list of ingredients. One of [recipe, mealplan] are required - :param mealplan: alternatively use a mealplan recipe as source of ingredients - :param servings: Optional: Number of servings to use to scale shoppinglist. If servings = 0 an existing recipe list will be deleted - :param ingredients: Ingredients, list of ingredient IDs to include on the shopping list. When not provided all ingredients will be used - """ - # TODO cascade to related recipes - r = recipe or getattr(mealplan, 'recipe', None) or getattr(list_recipe, 'recipe', None) - if not r: - raise ValueError(_("You must supply a recipe or mealplan")) - - created_by = created_by or getattr(mealplan, 'created_by', None) or getattr(list_recipe, 'created_by', None) - if not created_by: - raise ValueError(_("You must supply a created_by")) - - if type(servings) not in [int, float]: - servings = getattr(mealplan, 'servings', 1.0) - - shared_users = list(created_by.get_shopping_share()) - shared_users.append(created_by) - if list_recipe: - created = False - else: - list_recipe = ShoppingListRecipe.objects.create(recipe=r, mealplan=mealplan, servings=servings) - created = True - - if servings == 0 and not created: - list_recipe.delete() - return [] - elif ingredients: - ingredients = Ingredient.objects.filter(pk__in=ingredients, space=space) - else: - ingredients = Ingredient.objects.filter(step__recipe=r, space=space) - existing_list = ShoppingListEntry.objects.filter(list_recipe=list_recipe) - # delete shopping list entries not included in ingredients - existing_list.exclude(ingredient__in=ingredients).delete() - # add shopping list entries that did not previously exist - add_ingredients = set(ingredients.values_list('id', flat=True)) - set(existing_list.values_list('ingredient__id', flat=True)) - add_ingredients = Ingredient.objects.filter(id__in=add_ingredients, space=space) - - # if servings have changed, update the ShoppingListRecipe and existing Entrys - if servings <= 0: - servings = 1 - servings_factor = servings / r.servings - if not created and list_recipe.servings != servings: - update_ingredients = set(ingredients.values_list('id', flat=True)) - set(add_ingredients.values_list('id', flat=True)) - for sle in ShoppingListEntry.objects.filter(list_recipe=list_recipe, ingredient__id__in=update_ingredients): - sle.amount = sle.ingredient.amount * Decimal(servings_factor) - sle.save() - - # add any missing Entrys - shoppinglist = [ - ShoppingListEntry( - list_recipe=list_recipe, - food=i.food, - unit=i.unit, - ingredient=i, - amount=i.amount * Decimal(servings), - created_by=created_by, - space=space - ) - for i in ingredients - ] - return ShoppingListEntry.objects.bulk_create(shoppinglist) - - @classmethod - @atomic - def list_from_recipe(self, list_recipe=None, recipe=None, mealplan=None, servings=None, ingredients=None, created_by=None, space=None): - """ - Creates ShoppingListRecipe and associated ShoppingListEntrys from a recipe or a meal plan with a recipe - :param list_recipe: Modify an existing ShoppingListRecipe - :param recipe: Recipe to use as list of ingredients. One of [recipe, mealplan] are required - :param mealplan: alternatively use a mealplan recipe as source of ingredients - :param servings: Optional: Number of servings to use to scale shoppinglist. If servings = 0 an existing recipe list will be deleted - :param ingredients: Ingredients, list of ingredient IDs to include on the shopping list. When not provided all ingredients will be used - """ - # TODO cascade to related recipes - r = recipe or getattr(mealplan, 'recipe', None) or getattr(list_recipe, 'recipe', None) - if not r: - raise ValueError(_("You must supply a recipe or mealplan")) - - created_by = created_by or getattr(mealplan, 'created_by', None) or getattr(list_recipe, 'created_by', None) - if not created_by: - raise ValueError(_("You must supply a created_by")) - - if type(servings) not in [int, float]: - servings = getattr(mealplan, 'servings', 1.0) - - shared_users = list(created_by.get_shopping_share()) - shared_users.append(created_by) - if list_recipe: - created = False - else: - list_recipe = ShoppingListRecipe.objects.create(recipe=r, mealplan=mealplan, servings=servings) - created = True - - if servings == 0 and not created: - list_recipe.delete() - return [] - elif ingredients: - ingredients = Ingredient.objects.filter(pk__in=ingredients, space=space) - else: - ingredients = Ingredient.objects.filter(step__recipe=r, space=space) - existing_list = ShoppingListEntry.objects.filter(list_recipe=list_recipe) - # delete shopping list entries not included in ingredients - existing_list.exclude(ingredient__in=ingredients).delete() - # add shopping list entries that did not previously exist - add_ingredients = set(ingredients.values_list('id', flat=True)) - set(existing_list.values_list('ingredient__id', flat=True)) - add_ingredients = Ingredient.objects.filter(id__in=add_ingredients, space=space) - - # if servings have changed, update the ShoppingListRecipe and existing Entrys - if servings <= 0: - servings = 1 - servings_factor = servings / r.servings - if not created and list_recipe.servings != servings: - update_ingredients = set(ingredients.values_list('id', flat=True)) - set(add_ingredients.values_list('id', flat=True)) - for sle in ShoppingListEntry.objects.filter(list_recipe=list_recipe, ingredient__id__in=update_ingredients): - sle.amount = sle.ingredient.amount * Decimal(servings_factor) - sle.save() - - # add any missing Entrys - shoppinglist = [ - ShoppingListEntry( - list_recipe=list_recipe, - food=i.food, - unit=i.unit, - ingredient=i, - amount=i.amount * Decimal(servings_factor), - created_by=created_by, - space=space - ) - for i in [x for x in add_ingredients if not x.food.ignore_shopping] - ] - ShoppingListEntry.objects.bulk_create(shoppinglist) - # return all shopping list items - print('end of servings') - return ShoppingListEntry.objects.filter(list_recipe=list_recipe) - @ staticmethod def get_space_key(): return 'shoppinglist', 'space' diff --git a/cookbook/serializer.py b/cookbook/serializer.py index d8ad1464b..9d6f49f9a 100644 --- a/cookbook/serializer.py +++ b/cookbook/serializer.py @@ -12,6 +12,7 @@ from drf_writable_nested import UniqueFieldsMixin, WritableNestedModelSerializer from rest_framework import serializers from rest_framework.exceptions import NotFound, ValidationError +from cookbook.helper.shopping_helper import list_from_recipe from cookbook.models import (Automation, BookmarkletImport, Comment, CookLog, Food, FoodInheritField, ImportLog, Ingredient, Keyword, MealPlan, MealType, NutritionInformation, Recipe, RecipeBook, RecipeBookEntry, @@ -616,7 +617,7 @@ class MealPlanSerializer(SpacedModelSerializer, WritableNestedModelSerializer): validated_data['created_by'] = self.context['request'].user mealplan = super().create(validated_data) if self.context['request'].data.get('addshopping', False): - ShoppingListEntry.list_from_recipe(mealplan=mealplan, space=validated_data['space'], created_by=validated_data['created_by']) + list_from_recipe(mealplan=mealplan, space=validated_data['space'], created_by=validated_data['created_by']) return mealplan class Meta: @@ -648,7 +649,7 @@ class ShoppingListRecipeSerializer(serializers.ModelSerializer): def update(self, instance, validated_data): if 'servings' in validated_data: - ShoppingListEntry.list_from_recipe( + list_from_recipe( list_recipe=instance, servings=validated_data['servings'], created_by=self.context['request'].user, diff --git a/cookbook/signals.py b/cookbook/signals.py index 10ca5262c..b6ddb67bc 100644 --- a/cookbook/signals.py +++ b/cookbook/signals.py @@ -1,3 +1,4 @@ +from decimal import Decimal from functools import wraps from django.contrib.postgres.search import SearchVector @@ -5,8 +6,10 @@ from django.db.models.signals import post_save from django.dispatch import receiver from django.utils import translation +from cookbook.helper.shopping_helper import list_from_recipe from cookbook.managers import DICTIONARY -from cookbook.models import Food, FoodInheritField, Recipe, Step +from cookbook.models import (Food, FoodInheritField, Ingredient, MealPlan, Recipe, + ShoppingListEntry, Step) # wraps a signal with the ability to set 'skip_signal' to avoid creating recursive signals @@ -78,3 +81,52 @@ def update_food_inheritance(sender, instance=None, created=False, **kwargs): # don't cascade empty supermarket category if instance.supermarket_category: instance.get_children().filter(inherit=True).exclude(ignore_inherit__field='supermarket_category').update(supermarket_category=instance.supermarket_category) + + +@receiver(post_save, sender=MealPlan) +def auto_add_shopping(sender, instance=None, created=False, weak=False, **kwargs): + user = instance.get_owner() + if not created or not user.userpreference.mealplan_autoadd_shopping: + return + + # if creating a mealplan - perform shopping list activities + space = instance.space + if user.userpreference.mealplan_autoadd_shopping: + kwargs = { + 'mealplan': instance, + 'space': space, + 'created_by': user, + 'servings': instance.servings + } + recipe_ingredients = Ingredient.objects.filter(step__recipe=instance.recipe, space=space) + if exclude_onhand := user.userpreference.mealplan_autoexclude_onhand: + recipe_ingredients = recipe_ingredients.exclude(food__on_hand=True) + if related := user.userpreference.mealplan_autoinclude_related: + # TODO: add levels of related recipes to use when auto-adding mealplans + related_recipes = instance.recipe.get_related_recipes() + # dont' add recipes that are going to have their recipes added to the shopping list + kwargs['ingredients'] = recipe_ingredients.exclude(food__recipe__in=related_recipes).values_list('id', flat=True) + else: + kwargs['ingredients'] = recipe_ingredients.values_list('id', flat=True) + + list_recipe = list_from_recipe(**kwargs) + if related: + servings_factor = Decimal(instance.servings / instance.recipe.servings) + kwargs['list_recipe'] = list_recipe + food_recipes = recipe_ingredients.filter(food__recipe__in=related_recipes).values('food__recipe', 'amount') + + for recipe in related_recipes: + kwargs['ingredients'] = [] + if exclude_onhand: + kwargs['ingredients'] = Ingredient.objects.filter(step__recipe=recipe, food__on_hand=False, space=space).values_list('id', flat=True) + kwargs['recipe'] = recipe + + # assume related recipes are intended to be 'full sized' to parent recipe + # Recipe1 (servings:4) includes StepRecipe2(servings:2) a Meal Plan serving size of 8 would assume 4 servings of StepRecipe2 + if recipe.id in [x['food__recipe'] for x in food_recipes if x['food__recipe'] == recipe.id]: + kwargs['servings'] = Decimal(recipe.servings) * sum([x['amount'] for x in food_recipes if x['food__recipe'] == recipe.id]) * servings_factor + else: + # TODO: When modifying step recipes to allow serving size - will need to update this + kwargs['servings'] = Decimal(recipe.servings) * servings_factor + + list_from_recipe(**kwargs, append=True) diff --git a/cookbook/templates/settings.html b/cookbook/templates/settings.html index e33a3563c..2d9d15834 100644 --- a/cookbook/templates/settings.html +++ b/cookbook/templates/settings.html @@ -243,7 +243,7 @@ window.location.hash = e.target.hash; }) // listen for events - $(document).ready(function(){ + {% comment %} $(document).ready(function(){ hideShow() // call hideShow when the user clicks on the mealplan_autoadd checkbox $("#id_shopping-mealplan_autoadd_shopping").click(function(event){ @@ -261,7 +261,7 @@ { $('#div_id_shopping-mealplan_autoexclude_onhand').hide(); $('#div_id_shopping-mealplan_autoinclude_related').hide(); - } + } {% endcomment %} } {% endblock %} diff --git a/cookbook/views/api.py b/cookbook/views/api.py index 21815e60b..e171ea209 100644 --- a/cookbook/views/api.py +++ b/cookbook/views/api.py @@ -38,7 +38,7 @@ from cookbook.helper.permission_helper import (CustomIsAdmin, CustomIsGuest, Cus from cookbook.helper.recipe_html_import import get_recipe_from_source from cookbook.helper.recipe_search import get_facet, old_search, search_recipes from cookbook.helper.recipe_url_import import get_from_scraper -from cookbook.helper.shopping_helper import shopping_helper +from cookbook.helper.shopping_helper import list_from_recipe, shopping_helper from cookbook.models import (Automation, BookmarkletImport, CookLog, Food, FoodInheritField, ImportLog, Ingredient, Keyword, MealPlan, MealType, Recipe, RecipeBook, RecipeBookEntry, ShareLink, ShoppingList, ShoppingListEntry, @@ -658,7 +658,7 @@ class RecipeViewSet(viewsets.ModelViewSet): list_recipe = request.data.get('list_recipe', None) content = {'msg': _(f'{obj.name} was added to the shopping list.')} # TODO: Consider if this should be a Recipe method - ShoppingListEntry.list_from_recipe(list_recipe=list_recipe, recipe=obj, ingredients=ingredients, servings=servings, space=request.space, created_by=request.user) + list_from_recipe(list_recipe=list_recipe, recipe=obj, ingredients=ingredients, servings=servings, space=request.space, created_by=request.user) return Response(content, status=status.HTTP_204_NO_CONTENT) diff --git a/vue/src/apps/RecipeView/RecipeView.vue b/vue/src/apps/RecipeView/RecipeView.vue index de938729b..61a2127ce 100644 --- a/vue/src/apps/RecipeView/RecipeView.vue +++ b/vue/src/apps/RecipeView/RecipeView.vue @@ -81,18 +81,11 @@
-
- +
+
-
+
@@ -149,11 +142,11 @@ import { apiLoadRecipe } from "@/utils/api" import Step from "@/components/Step" import RecipeContextMenu from "@/components/ContextMenu/RecipeContextMenu" import { ResolveUrlMixin, ToastMixin } from "@/utils/utils" -import IngredientsCard from "@/components/IngredientsCard" import PdfViewer from "@/components/PdfViewer" import ImageViewer from "@/components/ImageViewer" import Nutrition from "@/components/Nutrition" +import IngredientsCard from "@/components/IngredientsCard" import moment from "moment" import Keywords from "@/components/Keywords" diff --git a/vue/src/components/ContextMenu/ContextMenuSubmenu.vue b/vue/src/components/ContextMenu/ContextMenuSubmenu.vue deleted file mode 100644 index 4ff781f4b..000000000 --- a/vue/src/components/ContextMenu/ContextMenuSubmenu.vue +++ /dev/null @@ -1,34 +0,0 @@ - - - - - diff --git a/vue/src/components/ContextMenu/RecipeContextMenu.vue b/vue/src/components/ContextMenu/RecipeContextMenu.vue index f4b3067e7..c2df13043 100644 --- a/vue/src/components/ContextMenu/RecipeContextMenu.vue +++ b/vue/src/components/ContextMenu/RecipeContextMenu.vue @@ -1,193 +1,189 @@ diff --git a/vue/src/components/Modals/MealPlanEditModal.vue b/vue/src/components/Modals/MealPlanEditModal.vue index ab775fdd1..78a391d5b 100644 --- a/vue/src/components/Modals/MealPlanEditModal.vue +++ b/vue/src/components/Modals/MealPlanEditModal.vue @@ -12,15 +12,11 @@ @change="missing_recipe = false" > - + {{ $t("Title_or_Recipe_Required") }} - {{ - $t("Title") - }} + {{ $t("Title") }}
@@ -58,24 +54,13 @@ @new="createMealType" > {{ $t("Meal_Type_Required") }} - {{ - $t("Meal_Type") - }} + {{ $t("Meal_Type") }} - + - + {{ $t("Servings") }} @@ -90,9 +75,7 @@
- {{ $t("Delete") }} - + {{ $t("Delete") }} {{ $t("Save") }}
@@ -119,6 +102,7 @@ export default { entry: Object, entryEditing_initial_recipe: Array, entryEditing_initial_meal_type: Array, + entryEditing_inital_servings: Number, modal_title: String, modal_id: { type: String, @@ -145,6 +129,9 @@ export default { entry: { handler() { this.entryEditing = Object.assign({}, this.entry) + if (this.entryEditing_inital_servings) { + this.entryEditing.servings = this.entryEditing_inital_servings + } }, deep: true, }, diff --git a/vue/src/locales/en.json b/vue/src/locales/en.json index 3a220d518..194fd85d4 100644 --- a/vue/src/locales/en.json +++ b/vue/src/locales/en.json @@ -233,8 +233,8 @@ "shopping_share_desc": "Users will see all items you add to your shopping list. They must add you to see items on their list.", "shopping_auto_sync_desc": "Setting to 0 will disable auto sync. When viewing a shopping list the list is updated every set seconds to sync changes someone else might have made. Useful when shopping with multiple people but will use mobile data.", "mealplan_autoadd_shopping_desc": "Automatically add meal plan ingredients to shopping list.", - "mealplan_autoexclude_onhand_desc": "When automatically adding a meal plan to the shopping list, exclude ingredients that are on hand.", - "mealplan_autoinclude_related_desc": "When automatically adding a meal plan to the shopping list, include all related recipes.", + "mealplan_autoexclude_onhand_desc": "When adding a meal plan to the shopping list (manually or automatically), exclude ingredients that are on hand.", + "mealplan_autoinclude_related_desc": "When adding a meal plan to the shopping list (manually or automatically), include all related recipes.", "default_delay_desc": "Default number of hours to delay a shopping list entry.", "filter_to_supermarket": "Filter to Supermarket", "filter_to_supermarket_desc": "Filter shopping list to only include supermarket categories.", diff --git a/vue/src/utils/openapi/api.ts b/vue/src/utils/openapi/api.ts index ecec2b9de..060f02eb6 100644 --- a/vue/src/utils/openapi/api.ts +++ b/vue/src/utils/openapi/api.ts @@ -229,10 +229,10 @@ export interface Food { parent?: string; /** * - * @type {boolean} + * @type {number} * @memberof Food */ - on_hand?: boolean; + numchild?: number; /** * * @type {boolean} @@ -269,13 +269,13 @@ export interface FoodIgnoreInherit { * @type {string} * @memberof FoodIgnoreInherit */ - name?: string; + name: string; /** * * @type {string} * @memberof FoodIgnoreInherit */ - field?: string; + field: string; } /** * @@ -294,13 +294,13 @@ export interface FoodInheritField { * @type {string} * @memberof FoodInheritField */ - name?: string; + name: string; /** * * @type {string} * @memberof FoodInheritField */ - field?: string; + field: string; } /** * @@ -958,37 +958,6 @@ export interface InlineResponse2009 { */ results?: Array; } -/** - * - * @export - * @interface InlineResponse2009 - */ -export interface InlineResponse2009 { - /** - * - * @type {number} - * @memberof InlineResponse2009 - */ - count?: number; - /** - * - * @type {string} - * @memberof InlineResponse2009 - */ - next?: string | null; - /** - * - * @type {string} - * @memberof InlineResponse2009 - */ - previous?: string | null; - /** - * - * @type {Array} - * @memberof InlineResponse2009 - */ - results?: Array; -} /** * * @export @@ -1006,7 +975,13 @@ export interface Keyword { * @type {string} * @memberof Keyword */ - parent?: string; + name: string; + /** + * + * @type {string} + * @memberof Keyword + */ + icon?: string | null; /** * * @type {string} @@ -1018,13 +993,31 @@ export interface Keyword { * @type {string} * @memberof Keyword */ - created_at?: string; + description?: string; + /** + * + * @type {string} + * @memberof Keyword + */ + parent?: string; /** * * @type {number} * @memberof Keyword */ numchild?: number; + /** + * + * @type {string} + * @memberof Keyword + */ + created_at?: string; + /** + * + * @type {string} + * @memberof Keyword + */ + updated_at?: string; } /** * @@ -1632,61 +1625,6 @@ export interface RecipeIngredients { */ no_amount?: boolean; } -/** - * - * @export - * @interface RecipeKeywords - */ -export interface RecipeIngredients { - /** - * - * @type {number} - * @memberof RecipeIngredients - */ - id?: number; - /** - * - * @type {IngredientFood} - * @memberof RecipeIngredients - */ - food: IngredientFood | null; - /** - * - * @type {FoodSupermarketCategory} - * @memberof RecipeIngredients - */ - unit: FoodSupermarketCategory | null; - /** - * - * @type {string} - * @memberof RecipeIngredients - */ - amount: string; - /** - * - * @type {string} - * @memberof RecipeIngredients - */ - note?: string | null; - /** - * - * @type {number} - * @memberof RecipeIngredients - */ - order?: number; - /** - * - * @type {boolean} - * @memberof RecipeIngredients - */ - is_header?: boolean; - /** - * - * @type {boolean} - * @memberof RecipeIngredients - */ - no_amount?: boolean; -} /** * * @export @@ -1704,7 +1642,13 @@ export interface RecipeKeywords { * @type {string} * @memberof RecipeKeywords */ - parent?: string; + name: string; + /** + * + * @type {string} + * @memberof RecipeKeywords + */ + icon?: string | null; /** * * @type {string} @@ -1716,13 +1660,31 @@ export interface RecipeKeywords { * @type {string} * @memberof RecipeKeywords */ - created_at?: string; + description?: string; + /** + * + * @type {string} + * @memberof RecipeKeywords + */ + parent?: string; /** * * @type {number} * @memberof RecipeKeywords */ numchild?: number; + /** + * + * @type {string} + * @memberof RecipeKeywords + */ + created_at?: string; + /** + * + * @type {string} + * @memberof RecipeKeywords + */ + updated_at?: string; } /** * @@ -2129,10 +2091,10 @@ export interface ShoppingListEntries { id?: number; /** * - * @type {string} + * @type {number} * @memberof ShoppingListEntries */ - list_recipe?: string; + list_recipe?: number | null; /** * * @type {IngredientFood} @@ -2186,7 +2148,7 @@ export interface ShoppingListEntries { * @type {ShoppingListCreatedBy} * @memberof ShoppingListEntries */ - created_by: ShoppingListCreatedBy; + created_by?: ShoppingListCreatedBy; /** * * @type {string} @@ -2198,7 +2160,13 @@ export interface ShoppingListEntries { * @type {string} * @memberof ShoppingListEntries */ - completed_at?: string | null; + completed_at: string | null; + /** + * + * @type {string} + * @memberof ShoppingListEntries + */ + delay_until?: string | null; } /** * @@ -2214,10 +2182,10 @@ export interface ShoppingListEntry { id?: number; /** * - * @type {string} + * @type {number} * @memberof ShoppingListEntry */ - list_recipe?: string; + list_recipe?: number | null; /** * * @type {IngredientFood} @@ -2283,7 +2251,13 @@ export interface ShoppingListEntry { * @type {string} * @memberof ShoppingListEntry */ - completed_at?: string; + completed_at: string | null; + /** + * + * @type {string} + * @memberof ShoppingListEntry + */ + delay_until?: string | null; } /** * @@ -2297,6 +2271,12 @@ export interface ShoppingListRecipe { * @memberof ShoppingListRecipe */ id?: number; + /** + * + * @type {string} + * @memberof ShoppingListRecipe + */ + recipe_name?: string; /** * * @type {string} @@ -2312,7 +2292,7 @@ export interface ShoppingListRecipe { /** * * @type {number} - * @memberof ShoppingListEntry + * @memberof ShoppingListRecipe */ ingredient?: number | null; /** @@ -2333,79 +2313,6 @@ export interface ShoppingListRecipe { * @memberof ShoppingListRecipe */ servings: string; - /** - * - * @type {string} - * @memberof ShoppingListRecipe - */ - checked?: boolean; - /** - * - * @type {ShoppingListRecipeMealplan} - * @memberof ShoppingListEntry - */ - recipe_mealplan?: ShoppingListRecipeMealplan; - /** - * - * @type {ShoppingListCreatedBy} - * @memberof ShoppingListEntry - */ - created_by: ShoppingListCreatedBy; - /** - * - * @type {string} - * @memberof ShoppingListEntry - */ - created_at?: string; - /** - * - * @type {string} - * @memberof ShoppingListEntry - */ - completed_at?: string | null; -} -/** - * - * @export - * @interface ShoppingListRecipeMealplan - */ -export interface ShoppingListRecipeMealplan { - /** - * - * @type {number} - * @memberof ShoppingListRecipeMealplan - */ - id?: number; - /** - * - * @type {string} - * @memberof ShoppingListRecipeMealplan - */ - name?: string; - /** - * - * @type {number} - * @memberof ShoppingListRecipeMealplan - */ - recipe?: number | null; - /** - * - * @type {number} - * @memberof ShoppingListRecipe - */ - mealplan?: number | null; - /** - * - * @type {string} - * @memberof ShoppingListRecipe - */ - mealplan?: number | null; - /** - * - * @type {string} - * @memberof ShoppingListRecipeMealplan - */ - servings: string; /** * * @type {string} @@ -2428,7 +2335,13 @@ export interface ShoppingListRecipeMealplan { /** * * @type {string} - * @memberof ShoppingListRecipes + * @memberof ShoppingListRecipeMealplan + */ + recipe_name?: string; + /** + * + * @type {string} + * @memberof ShoppingListRecipeMealplan */ name?: string; /** @@ -2437,6 +2350,61 @@ export interface ShoppingListRecipeMealplan { * @memberof ShoppingListRecipeMealplan */ recipe?: number | null; + /** + * + * @type {number} + * @memberof ShoppingListRecipe + */ + mealplan?: number | null; + /** + * + * @type {string} + * @memberof ShoppingListRecipe + */ + mealplan?: number | null; + /** + * + * @type {string} + * @memberof ShoppingListRecipeMealplan + */ + servings: string; + /** + * + * @type {string} + * @memberof ShoppingListRecipeMealplan + */ + mealplan_note?: string; +} +/** + * + * @export + * @interface ShoppingListRecipes + */ +export interface ShoppingListRecipes { + /** + * + * @type {number} + * @memberof ShoppingListRecipes + */ + id?: number; + /** + * + * @type {string} + * @memberof ShoppingListRecipes + */ + recipe_name?: string; + /** + * + * @type {string} + * @memberof ShoppingListRecipes + */ + name?: string; + /** + * + * @type {number} + * @memberof ShoppingListRecipes + */ + recipe?: number | null; /** * * @type {number} @@ -2452,13 +2420,13 @@ export interface ShoppingListRecipeMealplan { /** * * @type {string} - * @memberof ShoppingListRecipeMealplan + * @memberof ShoppingListRecipes */ servings: string; /** * * @type {string} - * @memberof ShoppingListRecipeMealplan + * @memberof ShoppingListRecipes */ mealplan_note?: string; } @@ -3038,6 +3006,36 @@ export interface UserPreference { * @memberof UserPreference */ mealplan_autoadd_shopping?: boolean; + /** + * + * @type {string} + * @memberof UserPreference + */ + food_ignore_default?: string; + /** + * + * @type {number} + * @memberof UserPreference + */ + default_delay?: number; + /** + * + * @type {boolean} + * @memberof UserPreference + */ + mealplan_autoinclude_related?: boolean; + /** + * + * @type {boolean} + * @memberof UserPreference + */ + mealplan_autoexclude_onhand?: boolean; + /** + * + * @type {Array} + * @memberof UserPreference + */ + shopping_share?: Array; } /** @@ -5359,12 +5357,13 @@ export const ApiApiAxiosParamCreator = function (configuration?: Configuration) }, /** * - * @param {string} [checked] Filter shopping list entries on checked. Valid values are true, false, both and <b>false+</b>.<br> - false+ includes unchecked items and recently completed items. * @param {number} [id] Returns the shopping list entry with a primary key of id. Multiple values allowed. + * @param {string} [checked] Filter shopping list entries on checked. [true, false, both, <b>recent</b>]<br> - recent includes unchecked items and recently completed items. + * @param {number} [supermarket] Returns the shopping list entries sorted by supermarket category order. * @param {*} [options] Override http request option. * @throws {RequiredError} */ - listShoppingListEntrys: async (checked?: string, id?: number, options: any = {}): Promise => { + listShoppingListEntrys: async (id?: number, checked?: string, supermarket?: number, options: any = {}): Promise => { const localVarPath = `/api/shopping-list-entry/`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); @@ -5377,12 +5376,16 @@ export const ApiApiAxiosParamCreator = function (configuration?: Configuration) const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; + if (id !== undefined) { + localVarQueryParameter['id'] = id; + } + if (checked !== undefined) { localVarQueryParameter['checked'] = checked; } - if (id !== undefined) { - localVarQueryParameter['id'] = id; + if (supermarket !== undefined) { + localVarQueryParameter['supermarket'] = supermarket; } @@ -9663,13 +9666,14 @@ export const ApiApiFp = function(configuration?: Configuration) { }, /** * - * @param {string} [checked] Filter shopping list entries on checked. Valid values are true, false, both and <b>false+</b>.<br> - false+ includes unchecked items and recently completed items. * @param {number} [id] Returns the shopping list entry with a primary key of id. Multiple values allowed. + * @param {string} [checked] Filter shopping list entries on checked. [true, false, both, <b>recent</b>]<br> - recent includes unchecked items and recently completed items. + * @param {number} [supermarket] Returns the shopping list entries sorted by supermarket category order. * @param {*} [options] Override http request option. * @throws {RequiredError} */ - async listShoppingListEntrys(checked?: string, id?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { - const localVarAxiosArgs = await localVarAxiosParamCreator.listShoppingListEntrys(checked, id, options); + async listShoppingListEntrys(id?: number, checked?: string, supermarket?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { + const localVarAxiosArgs = await localVarAxiosParamCreator.listShoppingListEntrys(id, checked, supermarket, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** @@ -11345,13 +11349,14 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?: }, /** * - * @param {string} [checked] Filter shopping list entries on checked. Valid values are true, false, both and <b>false+</b>.<br> - false+ includes unchecked items and recently completed items. * @param {number} [id] Returns the shopping list entry with a primary key of id. Multiple values allowed. + * @param {string} [checked] Filter shopping list entries on checked. [true, false, both, <b>recent</b>]<br> - recent includes unchecked items and recently completed items. + * @param {number} [supermarket] Returns the shopping list entries sorted by supermarket category order. * @param {*} [options] Override http request option. * @throws {RequiredError} */ - listShoppingListEntrys(checked?: string, id?: number, options?: any): AxiosPromise> { - return localVarFp.listShoppingListEntrys(checked, id, options).then((request) => request(axios, basePath)); + listShoppingListEntrys(id?: number, checked?: string, supermarket?: number, options?: any): AxiosPromise> { + return localVarFp.listShoppingListEntrys(id, checked, supermarket, options).then((request) => request(axios, basePath)); }, /** * @@ -13054,14 +13059,15 @@ export class ApiApi extends BaseAPI { /** * - * @param {string} [checked] Filter shopping list entries on checked. Valid values are true, false, both and <b>false+</b>.<br> - false+ includes unchecked items and recently completed items. * @param {number} [id] Returns the shopping list entry with a primary key of id. Multiple values allowed. + * @param {string} [checked] Filter shopping list entries on checked. [true, false, both, <b>recent</b>]<br> - recent includes unchecked items and recently completed items. + * @param {number} [supermarket] Returns the shopping list entries sorted by supermarket category order. * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof ApiApi */ - public listShoppingListEntrys(checked?: string, id?: number, options?: any) { - return ApiApiFp(this.configuration).listShoppingListEntrys(checked, id, options).then((request) => request(this.axios, this.basePath)); + public listShoppingListEntrys(id?: number, checked?: string, supermarket?: number, options?: any) { + return ApiApiFp(this.configuration).listShoppingListEntrys(id, checked, supermarket, options).then((request) => request(this.axios, this.basePath)); } /**