diff --git a/cookbook/helper/recipe_search.py b/cookbook/helper/recipe_search.py
index 80ad18d62..8bd3c0417 100644
--- a/cookbook/helper/recipe_search.py
+++ b/cookbook/helper/recipe_search.py
@@ -36,22 +36,21 @@ def search_recipes(request, queryset, params):
if search_last_viewed > 0:
last_viewed_recipes = ViewLog.objects.filter(
created_by=request.user, space=request.space,
- created_at__gte=datetime.now() - timedelta(days=14) # TODO make recent days a setting
+ created_at__gte=datetime.now() - timedelta(days=14)
).order_by('-pk').values_list('recipe__pk', flat=True)
last_viewed_recipes = list(dict.fromkeys(last_viewed_recipes))[:search_last_viewed] # removes duplicates from list prior to slicing
- # return queryset.annotate(last_view=Max('viewlog__pk')).annotate(new=Case(When(pk__in=last_viewed_recipes, then=('last_view')), default=Value(0))).filter(new__gt=0).order_by('-new')
+ return queryset.annotate(last_view=Max('viewlog__pk')).annotate(new=Case(When(pk__in=last_viewed_recipes, then=('last_view')), default=Value(0))).filter(new__gt=0).order_by('-new')
# queryset that only annotates most recent view (higher pk = lastest view)
- queryset = queryset.annotate(last_view=Max('viewlog__pk')).annotate(recent=Case(When(pk__in=last_viewed_recipes, then=('last_view')), default=Value(0)))
- orderby += ['-recent']
+ # TODO queryset.annotate(last_view=Max('viewlog__pk')).annotate(new=Case(When(pk__in=last_viewed_recipes, then=Value(100)), default=Value(0))).order_by('-new')
- # TODO create setting for default ordering - most cooked, rating,
+ orderby = []
+ # TODO create setting for default ordering - most cooked, rating,
# TODO create options for live sorting
- # TODO make days of new recipe a setting
if search_new == 'true':
queryset = (
queryset.annotate(new_recipe=Case(
- When(created_at__gte=(datetime.now() - timedelta(days=7)), then=('pk')), default=Value(0),))
+ When(created_at__gte=(datetime.now() - timedelta(days=81)), then=('pk')), default=Value(0),))
)
orderby += ['-new_recipe']
@@ -195,13 +194,12 @@ def get_facet(qs, params, space):
kw_a = annotated_qs(keywords, root=True, fill=True)
# if using an OR search, will annotate all keywords, otherwise, just those that appear in results
- if search_keywords_or:
- foods = Food.objects.filter(ingredient__step__recipe__in=list(qs.values_list('id', flat=True),space=space)).annotate(recipe_count=Count('ingredient'))
+ if search_foods_or:
+ foods = Food.objects.filter(space=space).annotate(recipe_count=Count('ingredient'))
else:
- foods = Food.objects.filter(ingredient__step__recipe__in=list(qs.values_list('id', flat=True))).annotate(recipe_count=Count('ingredient'))
+ foods = Food.objects.filter(ingredient__step__recipe__in=list(qs.values_list('id', flat=True)), space=space).annotate(recipe_count=Count('ingredient'))
food_a = annotated_qs(foods, root=True, fill=True)
-
# TODO add rating facet
facets['Ratings'] = []
facets['Keywords'] = fill_annotated_parents(kw_a, keyword_list)
diff --git a/cookbook/models.py b/cookbook/models.py
index 1c237302d..6f7ea1475 100644
--- a/cookbook/models.py
+++ b/cookbook/models.py
@@ -749,7 +749,8 @@ class CookLog(ExportModelOperationsMixin('cook_log'), models.Model, PermissionMo
return self.recipe.name
class Meta():
- indexes = (Index(fields=['id', 'recipe', '-created_at', 'rating', 'created_by']),)
+ # TODO add created_by
+ indexes = (Index(fields=['id', 'recipe', '-created_at', 'rating']),)
class ViewLog(ExportModelOperationsMixin('view_log'), models.Model, PermissionModelMixin):
@@ -764,7 +765,8 @@ class ViewLog(ExportModelOperationsMixin('view_log'), models.Model, PermissionMo
return self.recipe.name
class Meta():
- indexes = (Index(fields=['recipe', '-created_at', 'created_by']),)
+ # TODO add created_by
+ indexes = (Index(fields=['recipe', '-created_at']),)
class ImportLog(models.Model, PermissionModelMixin):
diff --git a/cookbook/serializer.py b/cookbook/serializer.py
index b5ef844d2..6bc9ce94b 100644
--- a/cookbook/serializer.py
+++ b/cookbook/serializer.py
@@ -1,10 +1,9 @@
-import json
import random
from datetime import timedelta
from decimal import Decimal
from gettext import gettext as _
from django.contrib.auth.models import User
-from django.db.models import Avg, Manager, QuerySet, Sum
+from django.db.models import Avg, QuerySet, Sum
from django.urls import reverse
from drf_writable_nested import (UniqueFieldsMixin,
WritableNestedModelSerializer)
@@ -58,24 +57,6 @@ class SpaceFilterSerializer(serializers.ListSerializer):
return super().to_representation(data)
-# custom related field, sends details on read, accepts primary key on write
-# class RelatedFieldAlternative(serializers.PrimaryKeyRelatedField):
-# def __init__(self, **kwargs):
-# self.serializer = kwargs.pop('serializer', None)
-# if self.serializer is not None and not issubclass(self.serializer, serializers.Serializer):
-# raise TypeError('"serializer" is not a valid serializer class')
-
-# super().__init__(**kwargs)
-
-# def use_pk_only_optimization(self):
-# return False if self.serializer else True
-
-# def to_representation(self, instance):
-# if self.serializer:
-# return self.serializer(instance, context=self.context).data
-# return super().to_representation(instance)
-
-
class SpacedModelSerializer(serializers.ModelSerializer):
def create(self, validated_data):
validated_data['space'] = self.context['request'].space
@@ -319,8 +300,6 @@ class RecipeSimpleSerializer(serializers.ModelSerializer):
class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer):
- # RelatedFieldAlternative adds details of related object on read, accepts PK on write
- # this approach prevents adding *new* objects when updating Food, SupermarketCategory must be created elsewhere
image = serializers.SerializerMethodField('get_image')
numrecipe = serializers.SerializerMethodField('count_recipes')
@@ -460,19 +439,21 @@ class RecipeBaseSerializer(WritableNestedModelSerializer):
pass
return None
- # TODO make days of new recipe a setting
- def is_recipe_new(self, obj):
- if obj.created_at > (timezone.now() - timedelta(days=7)):
- return True
- else:
- return False
+ def get_recipe_last_viewed(self, obj):
+ try:
+ last = obj.viewlog_set.filter(created_by=self.context['request'].user).last()
+ if last:
+ return last.created_at
+ except TypeError:
+ pass
+ return None
class RecipeOverviewSerializer(RecipeBaseSerializer):
keywords = KeywordLabelSerializer(many=True)
rating = serializers.SerializerMethodField('get_recipe_rating')
last_cooked = serializers.SerializerMethodField('get_recipe_last_cooked')
- new = serializers.SerializerMethodField('is_recipe_new')
+ last_viewed = serializers.SerializerMethodField('get_recipe_last_viewed')
def create(self, validated_data):
pass
@@ -485,7 +466,7 @@ class RecipeOverviewSerializer(RecipeBaseSerializer):
fields = (
'id', 'name', 'description', 'image', 'keywords', 'working_time',
'waiting_time', 'created_by', 'created_at', 'updated_at',
- 'internal', 'servings', 'servings_text', 'rating', 'last_cooked', 'new'
+ 'internal', 'servings', 'servings_text', 'rating', 'last_cooked', 'last_viewed',
)
read_only_fields = ['image', 'created_by', 'created_at']
diff --git a/cookbook/views/api.py b/cookbook/views/api.py
index b68b17dee..bacd99523 100644
--- a/cookbook/views/api.py
+++ b/cookbook/views/api.py
@@ -12,7 +12,7 @@ from django.contrib.auth.models import User
from django.contrib.postgres.search import TrigramSimilarity
from django.core.exceptions import FieldError, ValidationError
from django.core.files import File
-from django.db.models import Q
+from django.db.models import Q, Case, When, Value
from django.db.models.fields.related import ForeignObjectRel
from django.http import FileResponse, HttpResponse, JsonResponse
from django_scopes import scopes_disabled
@@ -114,8 +114,11 @@ class FuzzyFilterMixin(ViewSetMixin):
)
else:
# TODO have this check unaccent search settings or other search preferences?
- # TODO for some querysets exact matches are sorted beyond pagesize, need to find better solution
- self.queryset = self.queryset.filter(name__istartswith=query) | self.queryset.filter(name__icontains=query)
+ self.queryset = (
+ self.queryset
+ .annotate(exact=Case(When(name__iexact=query, then=(Value(100))), default=Value(0))) # put exact matches at the top of the result set
+ .filter(name__icontains=query).order_by('-exact')
+ )
updated_at = self.request.query_params.get('updated_at', None)
if updated_at is not None:
diff --git a/vue/src/apps/RecipeSearchView/RecipeSearchView.vue b/vue/src/apps/RecipeSearchView/RecipeSearchView.vue
index 327d14034..d7220aaf9 100644
--- a/vue/src/apps/RecipeSearchView/RecipeSearchView.vue
+++ b/vue/src/apps/RecipeSearchView/RecipeSearchView.vue
@@ -19,7 +19,7 @@
v-b-tooltip.hover :title="$t('Advanced Settings')"
v-bind:variant="!isAdvancedSettingsSet() ? 'primary' : 'danger'"
>
-
+
@@ -270,7 +270,7 @@ Vue.use(VueCookies)
import {ResolveUrlMixin} from "@/utils/utils";
-import LoadingSpinner from "@/components/LoadingSpinner";
+import LoadingSpinner from "@/components/LoadingSpinner"; // is this deprecated?
import {ApiApiFactory} from "@/utils/openapi/api.ts";
import RecipeCard from "@/components/RecipeCard";
@@ -320,7 +320,20 @@ export default {
mounted() {
this.$nextTick(function () {
if (this.$cookies.isKey(SETTINGS_COOKIE_NAME)) {
- this.settings = Object.assign({}, this.settings, this.$cookies.get(SETTINGS_COOKIE_NAME))
+ let cookie_val = this.$cookies.get(SETTINGS_COOKIE_NAME)
+ for (let i of Object.keys(cookie_val)) {
+ this.$set(this.settings, i, cookie_val[i])
+ }
+ // @vabene - I think you need Vue.set but am not sure what you were observing to validate
+ //TODO i have no idea why the above code does not suffice to update the
+ //TODO pagination UI element as $set should update all values reactively but it does not
+ setTimeout(function () {
+ this.$set(this.settings, 'pagination_page', 0)
+ }.bind(this), 50)
+ setTimeout(function () {
+ this.$set(this.settings, 'pagination_page', cookie_val['pagination_page'])
+ }.bind(this), 51)
+
}
let urlParams = new URLSearchParams(window.location.search);