Merge branch 'develop' into keyword_todo

This commit is contained in:
vabene1111
2021-09-04 18:30:49 +02:00
committed by GitHub
67 changed files with 4339 additions and 17803 deletions

View File

@@ -4,11 +4,11 @@ from recipes import settings
from django.contrib.postgres.search import (
SearchQuery, SearchRank, TrigramSimilarity
)
from django.db.models import Count, Q, Subquery, Case, When, Value
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,24 +30,30 @@ 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)
).order_by('pk').values_list('recipe__pk', flat=True).distinct()
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.filter(pk__in=last_viewed_recipes[len(last_viewed_recipes) - min(len(last_viewed_recipes), search_last_viewed):])
# 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']
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=Value(100)),
default=Value(0), ))
orderby += ['new_recipe']
else:
queryset = queryset
queryset = (
queryset.annotate(new_recipe=Case(
When(created_at__gte=(datetime.now() - timedelta(days=7)), then=('pk')), default=Value(0),))
)
orderby += ['-new_recipe']
search_type = search_prefs.search or 'plain'
if len(search_string) > 0:
@@ -124,7 +130,7 @@ def search_recipes(request, queryset, params):
queryset = queryset.filter(query_filter).annotate(rank=search_rank)
orderby += ['-rank']
else:
queryset = queryset.filter(query_filter)
queryset = queryset.filter(name__icontains=search_string)
if len(search_keywords) > 0:
if search_keywords_or == 'true':
@@ -163,39 +169,50 @@ 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', [])
ingredient_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)
# if using an OR search, will annotate all keywords, otherwise, just those that appear in results
if search_foods_or:
foods = Food.objects.filter(space=request.space).annotate(recipe_count=Count('ingredient'))
else:
foods = Food.objects.filter(ingredient__step__recipe__in=list(qs.values_list('id', flat=True)), space=request.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)
# TODO add food facet
facets['Foods'] = []
facets['Foods'] = fill_annotated_parents(food_a, food_list)
# 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
@@ -262,6 +279,8 @@ def annotated_qs(qs, root=False, fill=False):
dirty = False
current_node = node_queue[-1]
depth = current_node.get_depth()
# TODO if node is at the wrong depth for some reason this fails
# either create a 'fix node' page, or automatically move the node to the root
parent_id = current_node.parent
if root and depth > 1 and parent_id not in nodes_list:
parent_id = current_node.parent