Skip to content

Commit

Permalink
[back] feat: add a dedicated route for random reco.
Browse files Browse the repository at this point in the history
  • Loading branch information
GresilleSiffle committed Jan 11, 2024
1 parent ff39851 commit 3660b3d
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 0 deletions.
6 changes: 6 additions & 0 deletions backend/tournesol/serializers/poll.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,9 @@ class RecommendationsFilterSerializer(serializers.Serializer):
help_text="If true and a user is authenticated, then entities compared by the"
" user will be removed from the response",
)


class RecommendationsRandomFilterSerializer(serializers.Serializer):
random = serializers.IntegerField(default=None)
date_lte = serializers.DateTimeField(default=None)
date_gte = serializers.DateTimeField(default=None)
6 changes: 6 additions & 0 deletions backend/tournesol/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
PollsRecommendationsView,
PollsView,
)
from .views.polls_reco_random import RandomRecommendationList
from .views.previews import (
DynamicWebsitePreviewComparison,
DynamicWebsitePreviewDefault,
Expand Down Expand Up @@ -196,6 +197,11 @@
PollsRecommendationsView.as_view(),
name="polls_recommendations",
),
path(
"polls/<str:name>/recommendations/random/",
RandomRecommendationList.as_view(),
name="polls_recommendations_random",
),
path(
"polls/<str:name>/entities/<str:uid>",
PollsEntityView.as_view(),
Expand Down
1 change: 1 addition & 0 deletions backend/tournesol/utils/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@
COMPARISON_MAX = 10.0

# Default weight for a criteria in the recommendations
# FIXME: the default weight used by the front end is 50, not 10
CRITERIA_DEFAULT_WEIGHT = 10
7 changes: 7 additions & 0 deletions backend/tournesol/views/polls.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,13 @@ def annotate_and_prefetch_scores(self, queryset, request, poll: Poll):
criteria_weight = self._build_criteria_weight_condition(
request, poll, when="all_criteria_scores__criteria"
)

# FIXME: we can significantly improve the performance of the queryset
# by filtering the criteria on their names, to remove those that are
# not present in the request.
#
# ex:
# all_criteria_scores__criteria__in=[...]
queryset = queryset.filter(
all_criteria_scores__poll=poll,
all_criteria_scores__score_mode=score_mode,
Expand Down
81 changes: 81 additions & 0 deletions backend/tournesol/views/polls_reco_random.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
from django.utils.decorators import method_decorator
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import (
OpenApiExample,
OpenApiParameter,
extend_schema,
extend_schema_view,
)

from tournesol.models import Entity
from tournesol.serializers.entity import EntityNoExtraFieldSerializer
from tournesol.serializers.poll import RecommendationsRandomFilterSerializer
from tournesol.utils.cache import cache_page_no_i18n
from tournesol.views import PollRecommendationsBaseAPIView


class RandomRecommendationBaseAPIView(PollRecommendationsBaseAPIView):
def get_queryset(self):
"""
Return a queryset of random recommended entities.
This queryset is designed to be more performant than the queryset
of the regular recommendations view. For this reason, it doesn't allow
to:
- filter entities by text
- filter entities by weighted criteria score
- or anything involving a SQL JOIN on EntityCriteriaScore
"""
poll = self.poll_from_url
queryset = Entity.objects.all()
queryset, _ = self.filter_by_parameters(self.request, queryset, poll)
queryset = queryset.with_prefetched_poll_ratings(poll_name=poll.name)
queryset = queryset.filter_safe_for_poll(poll)
queryset = queryset.order_by("?")
return queryset


@extend_schema_view(
get=extend_schema(
parameters=[
RecommendationsRandomFilterSerializer,
OpenApiParameter(
"metadata",
OpenApiTypes.OBJECT,
style="deepObject",
description="Filter by one or more metadata.",
examples=[
OpenApiExample(
name="No metadata filter",
),
OpenApiExample(
name="Videos - some examples",
value={"language": ["en", "pt"], "uploader": "Kurzgesagt – In a Nutshell"},
),
OpenApiExample(
name="Videos - videos of 8 minutes or less (480 sec)",
value={"duration:lte:int": "480"},
),
OpenApiExample(
name="Candidates - some examples",
value={
"name": "A candidate full name",
"youtube_channel_id": "channel ID",
},
),
],
),
],
)
)
class RandomRecommendationList(RandomRecommendationBaseAPIView):
"""
Return a random list of recommended entities.
"""
permission_classes = []
serializer_class = EntityNoExtraFieldSerializer
poll_parameter = "name"

@method_decorator(cache_page_no_i18n(60 * 10)) # 10 minutes cache
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)

0 comments on commit 3660b3d

Please sign in to comment.