consolidated recently viewed into FTS queryset

This commit is contained in:
smilerz
2021-08-25 21:08:35 -05:00
parent aa2d0eafb1
commit 2a1e1953ee
7 changed files with 73 additions and 96 deletions

View File

@@ -8,7 +8,7 @@ from django.db.models import Count, Max, Q, Subquery, Case, When, Value
from django.utils import translation
from cookbook.managers import DICTIONARY
from cookbook.models import Food, Keyword, ViewLog
from cookbook.models import Food, Keyword, Recipe, ViewLog
# TODO create extensive tests to make sure ORs ANDs and various filters, sorting, etc work as expected
@@ -30,26 +30,28 @@ def search_recipes(request, queryset, params):
search_random = params.get('random', False)
search_new = params.get('new', False)
search_last_viewed = int(params.get('last_viewed', 0))
orderby = []
# TODO update this to concat with full search queryset qs1 | qs2
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)
created_at__gte=datetime.now() - timedelta(days=14) # TODO make recent days a setting
).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)
# 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')
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']
orderby = []
# TODO create setting for default ordering - most cooked, rating,
# 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=81)), then=('pk')), default=Value(0),))
When(created_at__gte=(datetime.now() - timedelta(days=7)), then=('pk')), default=Value(0),))
)
orderby += ['-new_recipe']
@@ -167,27 +169,27 @@ def search_recipes(request, queryset, params):
queryset = queryset.order_by("?")
else:
# TODO add order by user settings
orderby += ['name']
# orderby += ['name']
queryset = queryset.order_by(*orderby)
return queryset
def get_facet(qs, params, space):
def get_facet(qs, request):
# NOTE facet counts for tree models include self AND descendants
facets = {}
ratings = params.getlist('ratings', [])
keyword_list = params.getlist('keywords', [])
food_list = params.getlist('foods', [])
book_list = params.getlist('book', [])
search_keywords_or = params.get('keywords_or', True)
search_foods_or = params.get('foods_or', True)
search_books_or = params.get('books_or', True)
ratings = request.query_params.getlist('ratings', [])
keyword_list = request.query_params.getlist('keywords', [])
food_list = request.query_params.getlist('foods', [])
book_list = request.query_params.getlist('book', [])
search_keywords_or = request.query_params.get('keywords_or', True)
search_foods_or = request.query_params.get('foods_or', True)
search_books_or = request.query_params.get('books_or', True)
# if using an OR search, will annotate all keywords, otherwise, just those that appear in results
if search_keywords_or:
keywords = Keyword.objects.filter(space=space).annotate(recipe_count=Count('recipe'))
keywords = Keyword.objects.filter(space=request.space).annotate(recipe_count=Count('recipe'))
else:
keywords = Keyword.objects.filter(recipe__in=qs, space=space).annotate(recipe_count=Count('recipe'))
keywords = Keyword.objects.filter(recipe__in=qs, space=request.space).annotate(recipe_count=Count('recipe'))
# custom django-tree function annotates a queryset to make building a tree easier.
# see https://django-treebeard.readthedocs.io/en/latest/api.html#treebeard.models.Node.get_annotated_list_qs for details
kw_a = annotated_qs(keywords, root=True, fill=True)
@@ -199,7 +201,10 @@ def get_facet(qs, params, space):
facets['Foods'] = []
# TODO add book facet
facets['Books'] = []
facets['Recent'] = ViewLog.objects.filter(
created_by=request.user, space=request.space,
created_at__gte=datetime.now() - timedelta(days=14) # TODO make days of recent recipe a setting
).values_list('recipe__pk', flat=True)
return facets

View File

@@ -745,7 +745,7 @@ class CookLog(ExportModelOperationsMixin('cook_log'), models.Model, PermissionMo
return self.recipe.name
class Meta():
indexes = (Index(fields=['id', 'recipe', '-created_at', 'rating']),)
indexes = (Index(fields=['id', 'recipe', '-created_at', 'rating', 'created_by']),)
class ViewLog(ExportModelOperationsMixin('view_log'), models.Model, PermissionModelMixin):
@@ -760,7 +760,7 @@ class ViewLog(ExportModelOperationsMixin('view_log'), models.Model, PermissionMo
return self.recipe.name
class Meta():
indexes = (Index(fields=['recipe', '-created_at']),)
indexes = (Index(fields=['recipe', '-created_at', 'created_by']),)
class ImportLog(models.Model, PermissionModelMixin):

View File

@@ -1,14 +1,14 @@
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 QuerySet, Sum, Avg
from django.utils import timezone
from drf_writable_nested import (UniqueFieldsMixin,
WritableNestedModelSerializer)
from rest_framework import serializers
from rest_framework.exceptions import ValidationError, NotFound
from treebeard.mp_tree import MP_NodeQuerySet
from cookbook.models import (Comment, CookLog, Food, Ingredient, Keyword,
MealPlan, MealType, NutritionInformation, Recipe,
@@ -387,11 +387,19 @@ 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
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')
def create(self, validated_data):
pass
@@ -404,7 +412,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',
'internal', 'servings', 'servings_text', 'rating', 'last_cooked', 'new'
)
read_only_fields = ['image', 'created_by', 'created_at']

File diff suppressed because one or more lines are too long

View File

@@ -475,7 +475,7 @@ class RecipePagination(PageNumberPagination):
max_page_size = 100
def paginate_queryset(self, queryset, request, view=None):
self.facets = get_facet(queryset, request.query_params, request.space)
self.facets = get_facet(queryset, request)
return super().paginate_queryset(queryset, request, view)
def get_paginated_response(self, data):