From a7d66fa850626e948743f86f4ba97c59c48e0a14 Mon Sep 17 00:00:00 2001 From: smilerz Date: Tue, 22 Feb 2022 15:18:39 -0600 Subject: [PATCH 1/3] export recipes from saved filter --- cookbook/forms.py | 1 + cookbook/helper/recipe_search.py | 10 +- cookbook/views/import_export.py | 4 + vue/src/apps/ExportView/ExportView.vue | 280 ++++----- .../RecipeSearchView/RecipeSearchView.vue | 551 ++++++------------ 5 files changed, 333 insertions(+), 513 deletions(-) diff --git a/cookbook/forms.py b/cookbook/forms.py index 60237ac7e..0ac423a42 100644 --- a/cookbook/forms.py +++ b/cookbook/forms.py @@ -179,6 +179,7 @@ class ImportForm(ImportExportBase): class ExportForm(ImportExportBase): recipes = forms.ModelMultipleChoiceField(widget=MultiSelectWidget, queryset=Recipe.objects.none(), required=False) all = forms.BooleanField(required=False) + filter = forms.IntegerField(required=False) def __init__(self, *args, **kwargs): space = kwargs.pop('space') diff --git a/cookbook/helper/recipe_search.py b/cookbook/helper/recipe_search.py index 38518712d..49845b75d 100644 --- a/cookbook/helper/recipe_search.py +++ b/cookbook/helper/recipe_search.py @@ -13,8 +13,8 @@ from cookbook.filters import RecipeFilter from cookbook.helper.HelperFunctions import Round, str2bool from cookbook.helper.permission_helper import has_group_permission from cookbook.managers import DICTIONARY -from cookbook.models import (CookLog, CustomFilter, Food, Keyword, Recipe, SearchFields, - SearchPreference, ViewLog, RecipeBook) +from cookbook.models import (CookLog, CustomFilter, Food, Keyword, Recipe, RecipeBook, SearchFields, + SearchPreference, ViewLog) from recipes import settings @@ -28,7 +28,7 @@ class RecipeSearch(): self._queryset = None if f := params.get('filter', None): custom_filter = CustomFilter.objects.filter(id=f, space=self._request.space).filter(Q(created_by=self._request.user) | - Q(shared=self._request.user) | Q(recipebook__shared=self._request.user)).first() + Q(shared=self._request.user) | Q(recipebook__shared=self._request.user)).first() if custom_filter: self._params = {**json.loads(custom_filter.search)} self._original_params = {**(params or {})} @@ -40,7 +40,7 @@ class RecipeSearch(): self._search_prefs = request.user.searchpreference else: self._search_prefs = SearchPreference() - self._string = params.get('query').strip() if params.get('query', None) else None + self._string = self._params.get('query').strip() if self._params.get('query', None) else None self._rating = self._params.get('rating', None) self._keywords = { 'or': self._params.get('keywords_or', None) or self._params.get('keywords', None), @@ -205,7 +205,7 @@ class RecipeSearch(): else: self._queryset = self._queryset.annotate(simularity=Coalesce(Subquery(simularity), 0.0)) if self._sort_includes('score') and self._fulltext_include and self._fuzzy_match is not None: - self._queryset = self._queryset.annotate(score=Sum(F('rank')+F('simularity'))) + self._queryset = self._queryset.annotate(score=F('rank')+F('simularity')) else: query_filter = Q() for f in [x + '__unaccent__iexact' if x in self._unaccent_include else x + '__iexact' for x in SearchFields.objects.all().values_list('field', flat=True)]: diff --git a/cookbook/views/import_export.py b/cookbook/views/import_export.py index f4aea7c0f..19e1a53bf 100644 --- a/cookbook/views/import_export.py +++ b/cookbook/views/import_export.py @@ -11,6 +11,7 @@ from django.utils.translation import gettext as _ from cookbook.forms import ExportForm, ImportExportBase, ImportForm from cookbook.helper.permission_helper import group_required +from cookbook.helper.recipe_search import RecipeSearch from cookbook.integration.cheftap import ChefTap from cookbook.integration.chowdown import Chowdown from cookbook.integration.cookbookapp import CookBookApp @@ -123,6 +124,9 @@ def export_recipe(request): recipes = form.cleaned_data['recipes'] if form.cleaned_data['all']: recipes = Recipe.objects.filter(space=request.space, internal=True).all() + elif filter := form.cleaned_data['filter']: + search = RecipeSearch(request, filter=filter) + recipes = search.get_queryset(Recipe.objects.filter(space=request.space, internal=True)) integration = get_integration(request, form.cleaned_data['type']) diff --git a/vue/src/apps/ExportView/ExportView.vue b/vue/src/apps/ExportView/ExportView.vue index ca55b7206..5a472ef94 100644 --- a/vue/src/apps/ExportView/ExportView.vue +++ b/vue/src/apps/ExportView/ExportView.vue @@ -1,174 +1,174 @@ - + diff --git a/vue/src/apps/RecipeSearchView/RecipeSearchView.vue b/vue/src/apps/RecipeSearchView/RecipeSearchView.vue index 4e7d35901..467569152 100644 --- a/vue/src/apps/RecipeSearchView/RecipeSearchView.vue +++ b/vue/src/apps/RecipeSearchView/RecipeSearchView.vue @@ -1,6 +1,6 @@ @@ -742,48 +573,40 @@ and don't contain - anyall of the following books: + anyall of the following books: {{ k.items.flatMap((x) => x.name).join(", ") }} -
+
- and you can make right now (based on the on hand flag)
+ and you can make right now (based on the on hand flag)
- and contain anyall of the following units: + and contain anyall of the following units: {{ search.search_units.flatMap((x) => x.name).join(", ") }}
+ >
- and have a rating or - equal to {{ search.search_rating }}
+ and have a rating or + equal to {{ search.search_rating }}
- and have been last cooked + and have been last cooked {{ search.lastcooked }}
+ >
- and have been cooked or - equal to{{ search.timescooked }} times
+ and have been cooked or + equal to{{ search.timescooked }} times
order by {{ search.sort_order.flatMap((x) => x.text).join(", ") }} -
+
@@ -797,14 +620,13 @@
- +
{{ o.text }} @@ -812,35 +634,34 @@
- - {{ $t("Page") }} {{ search.pagination_page }}/{{ - Math.ceil(pagination_count / ui.page_size) - }} - {{ $t("Reset") }} - + + {{ $t("Page") }} {{ search.pagination_page }}/{{ Math.ceil(pagination_count / ui.page_size) }} + {{ $t("Reset") }} +
-
+
- +
- +
@@ -848,9 +669,8 @@
-

{{ $t('search_no_recipes') }}

+

{{ $t("search_no_recipes") }}

-
@@ -858,47 +678,41 @@ - {{ $t('search_import_help_text') }} + {{ $t("search_import_help_text") }} - {{ $t('Import') }} - + {{ $t("Import") }} - {{ $t('search_create_help_text') }} + {{ $t("search_create_help_text") }} - {{ $t('Create') }} - + {{ $t("Create") }}
- -
- + From 081edfd2d63b66ef49abd01cf13e161cd741ca43 Mon Sep 17 00:00:00 2001 From: smilerz Date: Tue, 22 Feb 2022 16:33:09 -0600 Subject: [PATCH 3/3] fix errors on Tandoor recipe import --- cookbook/serializer.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/cookbook/serializer.py b/cookbook/serializer.py index b1b72d3e2..11967d435 100644 --- a/cookbook/serializer.py +++ b/cookbook/serializer.py @@ -421,9 +421,11 @@ class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer, ExtendedR space = validated_data.pop('space', self.context['request'].space) # supermarket category needs to be handled manually as food.get or create does not create nested serializers unlike a super.create of serializer if 'supermarket_category' in validated_data and validated_data['supermarket_category']: + sm_category = validated_data['supermarket_category'] + sc_name = sm_category.pop('name', None) validated_data['supermarket_category'], sc_created = SupermarketCategory.objects.get_or_create( - name__iexact=validated_data.pop('supermarket_category')['name'], - space=self.context['request'].space) + name=name, + space=space, defaults=sm_category) onhand = validated_data.pop('food_onhand', None) # assuming if on hand for user also onhand for shopping_share users @@ -678,7 +680,7 @@ class RecipeBookEntrySerializer(serializers.ModelSerializer): def create(self, validated_data): book = validated_data['book'] recipe = validated_data['recipe'] - if not book.get_owner() == self.context['request'].user and not self.context['request'].user in book.get_shared(): + if not book.get_owner() == self.context['request'].user and not self.context['request'].user in book.get_shared(): raise NotFound(detail=None, code=None) obj, created = RecipeBookEntry.objects.get_or_create(book=book, recipe=recipe) return obj @@ -733,11 +735,11 @@ class ShoppingListRecipeSerializer(serializers.ModelSerializer): value = Decimal(value) value = value.quantize(Decimal(1)) if value == value.to_integral() else value.normalize() # strips trailing zero return ( - obj.name - or getattr(obj.mealplan, 'title', None) - or (d := getattr(obj.mealplan, 'date', None)) and ': '.join([obj.mealplan.recipe.name, str(d)]) - or obj.recipe.name - ) + f' ({value:.2g})' + obj.name + or getattr(obj.mealplan, 'title', None) + or (d := getattr(obj.mealplan, 'date', None)) and ': '.join([obj.mealplan.recipe.name, str(d)]) + or obj.recipe.name + ) + f' ({value:.2g})' def update(self, instance, validated_data): # TODO remove once old shopping list