WIP
This commit is contained in:
parent
d7d552c5c5
commit
4714162c0b
@ -36,22 +36,21 @@ def search_recipes(request, queryset, params):
|
|||||||
if search_last_viewed > 0:
|
if search_last_viewed > 0:
|
||||||
last_viewed_recipes = ViewLog.objects.filter(
|
last_viewed_recipes = ViewLog.objects.filter(
|
||||||
created_by=request.user, space=request.space,
|
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)
|
).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
|
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 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)))
|
# 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')
|
||||||
orderby += ['-recent']
|
|
||||||
|
|
||||||
# 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 create options for live sorting
|
||||||
# TODO make days of new recipe a setting
|
|
||||||
if search_new == 'true':
|
if search_new == 'true':
|
||||||
queryset = (
|
queryset = (
|
||||||
queryset.annotate(new_recipe=Case(
|
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']
|
orderby += ['-new_recipe']
|
||||||
|
|
||||||
@ -195,13 +194,12 @@ def get_facet(qs, params, space):
|
|||||||
kw_a = annotated_qs(keywords, root=True, fill=True)
|
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 using an OR search, will annotate all keywords, otherwise, just those that appear in results
|
||||||
if search_keywords_or:
|
if search_foods_or:
|
||||||
foods = Food.objects.filter(ingredient__step__recipe__in=list(qs.values_list('id', flat=True),space=space)).annotate(recipe_count=Count('ingredient'))
|
foods = Food.objects.filter(space=space).annotate(recipe_count=Count('ingredient'))
|
||||||
else:
|
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)
|
food_a = annotated_qs(foods, root=True, fill=True)
|
||||||
|
|
||||||
|
|
||||||
# TODO add rating facet
|
# TODO add rating facet
|
||||||
facets['Ratings'] = []
|
facets['Ratings'] = []
|
||||||
facets['Keywords'] = fill_annotated_parents(kw_a, keyword_list)
|
facets['Keywords'] = fill_annotated_parents(kw_a, keyword_list)
|
||||||
|
@ -749,7 +749,8 @@ class CookLog(ExportModelOperationsMixin('cook_log'), models.Model, PermissionMo
|
|||||||
return self.recipe.name
|
return self.recipe.name
|
||||||
|
|
||||||
class Meta():
|
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):
|
class ViewLog(ExportModelOperationsMixin('view_log'), models.Model, PermissionModelMixin):
|
||||||
@ -764,7 +765,8 @@ class ViewLog(ExportModelOperationsMixin('view_log'), models.Model, PermissionMo
|
|||||||
return self.recipe.name
|
return self.recipe.name
|
||||||
|
|
||||||
class Meta():
|
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):
|
class ImportLog(models.Model, PermissionModelMixin):
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
import json
|
|
||||||
import random
|
import random
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
from django.contrib.auth.models import User
|
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 django.urls import reverse
|
||||||
from drf_writable_nested import (UniqueFieldsMixin,
|
from drf_writable_nested import (UniqueFieldsMixin,
|
||||||
WritableNestedModelSerializer)
|
WritableNestedModelSerializer)
|
||||||
@ -58,24 +57,6 @@ class SpaceFilterSerializer(serializers.ListSerializer):
|
|||||||
return super().to_representation(data)
|
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):
|
class SpacedModelSerializer(serializers.ModelSerializer):
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
validated_data['space'] = self.context['request'].space
|
validated_data['space'] = self.context['request'].space
|
||||||
@ -319,8 +300,6 @@ class RecipeSimpleSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
|
|
||||||
class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer):
|
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')
|
image = serializers.SerializerMethodField('get_image')
|
||||||
numrecipe = serializers.SerializerMethodField('count_recipes')
|
numrecipe = serializers.SerializerMethodField('count_recipes')
|
||||||
|
|
||||||
@ -460,19 +439,21 @@ class RecipeBaseSerializer(WritableNestedModelSerializer):
|
|||||||
pass
|
pass
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# TODO make days of new recipe a setting
|
def get_recipe_last_viewed(self, obj):
|
||||||
def is_recipe_new(self, obj):
|
try:
|
||||||
if obj.created_at > (timezone.now() - timedelta(days=7)):
|
last = obj.viewlog_set.filter(created_by=self.context['request'].user).last()
|
||||||
return True
|
if last:
|
||||||
else:
|
return last.created_at
|
||||||
return False
|
except TypeError:
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class RecipeOverviewSerializer(RecipeBaseSerializer):
|
class RecipeOverviewSerializer(RecipeBaseSerializer):
|
||||||
keywords = KeywordLabelSerializer(many=True)
|
keywords = KeywordLabelSerializer(many=True)
|
||||||
rating = serializers.SerializerMethodField('get_recipe_rating')
|
rating = serializers.SerializerMethodField('get_recipe_rating')
|
||||||
last_cooked = serializers.SerializerMethodField('get_recipe_last_cooked')
|
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):
|
def create(self, validated_data):
|
||||||
pass
|
pass
|
||||||
@ -485,7 +466,7 @@ class RecipeOverviewSerializer(RecipeBaseSerializer):
|
|||||||
fields = (
|
fields = (
|
||||||
'id', 'name', 'description', 'image', 'keywords', 'working_time',
|
'id', 'name', 'description', 'image', 'keywords', 'working_time',
|
||||||
'waiting_time', 'created_by', 'created_at', 'updated_at',
|
'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']
|
read_only_fields = ['image', 'created_by', 'created_at']
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ from django.contrib.auth.models import User
|
|||||||
from django.contrib.postgres.search import TrigramSimilarity
|
from django.contrib.postgres.search import TrigramSimilarity
|
||||||
from django.core.exceptions import FieldError, ValidationError
|
from django.core.exceptions import FieldError, ValidationError
|
||||||
from django.core.files import File
|
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.db.models.fields.related import ForeignObjectRel
|
||||||
from django.http import FileResponse, HttpResponse, JsonResponse
|
from django.http import FileResponse, HttpResponse, JsonResponse
|
||||||
from django_scopes import scopes_disabled
|
from django_scopes import scopes_disabled
|
||||||
@ -114,8 +114,11 @@ class FuzzyFilterMixin(ViewSetMixin):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# TODO have this check unaccent search settings or other search preferences?
|
# 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 = self.queryset.filter(name__istartswith=query) | self.queryset.filter(name__icontains=query)
|
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)
|
updated_at = self.request.query_params.get('updated_at', None)
|
||||||
if updated_at is not None:
|
if updated_at is not None:
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
v-b-tooltip.hover :title="$t('Advanced Settings')"
|
v-b-tooltip.hover :title="$t('Advanced Settings')"
|
||||||
v-bind:variant="!isAdvancedSettingsSet() ? 'primary' : 'danger'"
|
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-down" v-if="!settings.advanced_search_visible"></i>
|
||||||
<i class="fas fa-caret-up" v-if="settings.advanced_search_visible"></i>
|
<i class="fas fa-caret-up" v-if="settings.advanced_search_visible"></i>
|
||||||
</b-button>
|
</b-button>
|
||||||
@ -270,7 +270,7 @@ Vue.use(VueCookies)
|
|||||||
|
|
||||||
import {ResolveUrlMixin} from "@/utils/utils";
|
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 {ApiApiFactory} from "@/utils/openapi/api.ts";
|
||||||
import RecipeCard from "@/components/RecipeCard";
|
import RecipeCard from "@/components/RecipeCard";
|
||||||
@ -320,7 +320,20 @@ export default {
|
|||||||
mounted() {
|
mounted() {
|
||||||
this.$nextTick(function () {
|
this.$nextTick(function () {
|
||||||
if (this.$cookies.isKey(SETTINGS_COOKIE_NAME)) {
|
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);
|
let urlParams = new URLSearchParams(window.location.search);
|
||||||
|
Loading…
Reference in New Issue
Block a user