From d25702b71741e133375a26d910c72193f872e824 Mon Sep 17 00:00:00 2001 From: vabene1111 Date: Thu, 21 Aug 2025 15:50:13 +0200 Subject: [PATCH] added recipe batch editing dialog --- Dockerfile | 2 +- cookbook/serializer.py | 16 ++ cookbook/views/api.py | 56 ++++- .../dialogs/BatchEditRecipeDialog.vue | 141 ++++++++--- .../components/model_editors/RecipeEditor.vue | 4 +- vue3/src/locales/ar.json | 11 + vue3/src/locales/bg.json | 11 + vue3/src/locales/ca.json | 10 + vue3/src/locales/cs.json | 10 + vue3/src/locales/da.json | 10 + vue3/src/locales/de.json | 12 +- vue3/src/locales/el.json | 10 + vue3/src/locales/en.json | 12 +- vue3/src/locales/es.json | 10 + vue3/src/locales/fi.json | 10 + vue3/src/locales/fr.json | 10 + vue3/src/locales/he.json | 10 + vue3/src/locales/hr.json | 10 + vue3/src/locales/hu.json | 10 + vue3/src/locales/hy.json | 11 + vue3/src/locales/id.json | 10 + vue3/src/locales/is.json | 10 + vue3/src/locales/it.json | 10 + vue3/src/locales/lt.json | 10 + vue3/src/locales/lv.json | 10 + vue3/src/locales/nb_NO.json | 10 + vue3/src/locales/nl.json | 10 + vue3/src/locales/pl.json | 10 + vue3/src/locales/pt.json | 10 + vue3/src/locales/pt_BR.json | 10 + vue3/src/locales/ro.json | 10 + vue3/src/locales/ru.json | 10 + vue3/src/locales/sl.json | 10 + vue3/src/locales/sv.json | 10 + vue3/src/locales/tr.json | 10 + vue3/src/locales/uk.json | 10 + vue3/src/locales/zh_Hans.json | 10 + vue3/src/locales/zh_Hant.json | 10 + vue3/src/openapi/.openapi-generator/FILES | 7 + vue3/src/openapi/apis/ApiApi.ts | 220 +++++++++++++++--- vue3/src/openapi/models/BaseUnitEnum.ts | 72 +++--- vue3/src/openapi/models/OpenDataFood.ts | 7 +- .../src/openapi/models/PatchedOpenDataFood.ts | 4 +- vue3/src/openapi/models/RecipeBatchUpdate.ts | 108 +++++++++ vue3/src/openapi/models/index.ts | 7 + vue3/src/pages/SearchPage.vue | 34 ++- 46 files changed, 909 insertions(+), 106 deletions(-) diff --git a/Dockerfile b/Dockerfile index ebfb117e5..1184d9a87 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ FROM python:3.13-alpine3.22 #Install all dependencies. -RUN apk add --no-cache postgresql-libs postgresql-client gettext zlib libjpeg libwebp libxml2-dev libxslt-dev openldap git libgcc libstdc++ nginx tini envsubst +RUN apk add --no-cache postgresql-libs postgresql-client gettext zlib libjpeg libwebp libxml2-dev libxslt-dev openldap git libgcc libstdc++ nginx tini envsubst node #Print all logs without buffering it. ENV PYTHONUNBUFFERED=1 \ diff --git a/cookbook/serializer.py b/cookbook/serializer.py index b57b12223..a05fdab18 100644 --- a/cookbook/serializer.py +++ b/cookbook/serializer.py @@ -1116,6 +1116,22 @@ class RecipeBatchUpdateSerializer(serializers.Serializer): recipes = serializers.ListField(child=serializers.IntegerField()) keywords_add = serializers.ListField(child=serializers.IntegerField()) keywords_remove = serializers.ListField(child=serializers.IntegerField()) + keywords_set = serializers.ListField(child=serializers.IntegerField()) + keywords_remove_all = serializers.BooleanField(default=False) + + working_time = serializers.IntegerField(required=False, allow_null=True) + waiting_time = serializers.IntegerField(required=False, allow_null=True) + servings = serializers.IntegerField(required=False, allow_null=True) + servings_text = serializers.CharField(required=False, allow_null=True, allow_blank=True) + + private = serializers.BooleanField(required=False, allow_null=True) + shared_add = serializers.ListField(child=serializers.IntegerField()) + shared_remove = serializers.ListField(child=serializers.IntegerField()) + shared_set = serializers.ListField(child=serializers.IntegerField()) + shared_remove_all = serializers.BooleanField(default=False) + + show_ingredient_overview = serializers.BooleanField(required=False, allow_null=True) + clear_description = serializers.BooleanField(required=False, allow_null=True) class CustomFilterSerializer(SpacedModelSerializer, WritableNestedModelSerializer): diff --git a/cookbook/views/api.py b/cookbook/views/api.py index f9fa4a453..689256e1b 100644 --- a/cookbook/views/api.py +++ b/cookbook/views/api.py @@ -1376,7 +1376,61 @@ class RecipeViewSet(LoggingMixin, viewsets.ModelViewSet): if 'keywords_remove' in serializer.validated_data: for k in serializer.validated_data['keywords_remove']: - Recipe.keywords.through.objects.filter(recipe_id__in=safe_recipe_ids,keyword_id=k).delete() + Recipe.keywords.through.objects.filter(recipe_id__in=safe_recipe_ids, keyword_id=k).delete() + + if 'keywords_set' in serializer.validated_data and len(serializer.validated_data['keywords_set']) > 0: + keyword_relations = [] + Recipe.keywords.through.objects.filter(recipe_id__in=safe_recipe_ids).delete() + for r in recipes: + for k in serializer.validated_data['keywords_set']: + keyword_relations.append(Recipe.keywords.through(recipe_id=r.pk, keyword_id=k)) + Recipe.keywords.through.objects.bulk_create(keyword_relations, ignore_conflicts=True, unique_fields=('recipe_id', 'keyword_id',)) + + if 'keywords_remove_all' in serializer.validated_data and serializer.validated_data['keywords_remove_all']: + Recipe.keywords.through.objects.filter(recipe_id__in=safe_recipe_ids).delete() + + if 'working_time' in serializer.validated_data: + recipes.update(working_time=serializer.validated_data['working_time']) + + if 'waiting_time' in serializer.validated_data: + recipes.update(waiting_time=serializer.validated_data['waiting_time']) + + if 'servings' in serializer.validated_data: + recipes.update(servings=serializer.validated_data['servings']) + + if 'servings_text' in serializer.validated_data: + recipes.update(servings_text=serializer.validated_data['servings_text']) + + if 'private' in serializer.validated_data and serializer.validated_data['private'] is not None: + recipes.update(private=serializer.validated_data['private']) + + if 'shared_add' in serializer.validated_data: + shared_relation = [] + for r in recipes: + for u in serializer.validated_data['shared_add']: + shared_relation.append(Recipe.shared.through(recipe_id=r.pk, user_id=u)) + Recipe.shared.through.objects.bulk_create(shared_relation, ignore_conflicts=True, unique_fields=('recipe_id', 'user_id',)) + + if 'shared_remove' in serializer.validated_data: + for s in serializer.validated_data['shared_remove']: + Recipe.shared.through.objects.filter(recipe_id__in=safe_recipe_ids, user_id=s).delete() + + if 'shared_set' in serializer.validated_data and len(serializer.validated_data['shared_set']) > 0: + shared_relation = [] + Recipe.shared.through.objects.filter(recipe_id__in=safe_recipe_ids).delete() + for r in recipes: + for u in serializer.validated_data['shared_set']: + shared_relation.append(Recipe.shared.through(recipe_id=r.pk, user_id=u)) + Recipe.shared.through.objects.bulk_create(shared_relation, ignore_conflicts=True, unique_fields=('recipe_id', 'user_id',)) + + if 'shared_remove_all' in serializer.validated_data and serializer.validated_data['shared_remove_all']: + Recipe.shared.through.objects.filter(recipe_id__in=safe_recipe_ids).delete() + + if 'clear_description' in serializer.validated_data and serializer.validated_data['clear_description']: + recipes.update(description='') + + if 'show_ingredient_overview' in serializer.validated_data and serializer.validated_data['show_ingredient_overview'] is not None: + recipes.update(show_ingredient_overview=serializer.validated_data['show_ingredient_overview']) return Response({}, 200) diff --git a/vue3/src/components/dialogs/BatchEditRecipeDialog.vue b/vue3/src/components/dialogs/BatchEditRecipeDialog.vue index ad8c8c691..455b3261e 100644 --- a/vue3/src/components/dialogs/BatchEditRecipeDialog.vue +++ b/vue3/src/components/dialogs/BatchEditRecipeDialog.vue @@ -1,20 +1,94 @@