This commit is contained in:
smilerz 2021-08-25 15:22:47 -05:00
parent d7d552c5c5
commit 4714162c0b
5 changed files with 46 additions and 49 deletions

View File

@ -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)

View File

@ -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):

View File

@ -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']

View File

@ -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:

View File

@ -19,7 +19,7 @@
v-b-tooltip.hover :title="$t('Advanced Settings')"
v-bind:variant="!isAdvancedSettingsSet() ? 'primary' : 'danger'"
>
<!-- consider changing this icon to a filter -->
<!-- TODO consider changing this icon to a filter -->
<i class="fas fa-caret-down" v-if="!settings.advanced_search_visible"></i>
<i class="fas fa-caret-up" v-if="settings.advanced_search_visible"></i>
</b-button>
@ -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);