Skip to content

Commit

Permalink
[back] feat: the rate-later auto remove is now connected to the user …
Browse files Browse the repository at this point in the history
…settings (#1325)
  • Loading branch information
GresilleSiffle authored Feb 2, 2023
1 parent 397b280 commit c9c3b9d
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 28 deletions.
10 changes: 7 additions & 3 deletions backend/tournesol/models/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from tournesol.entities.base import UID_DELIMITER, EntityType
from tournesol.entities.video import TYPE_VIDEO, YOUTUBE_UID_NAMESPACE
from tournesol.models.entity_score import EntityCriteriaScore, ScoreMode
from tournesol.models.rate_later import RateLater
from tournesol.models.rate_later import RATE_LATER_AUTO_REMOVE_DEFAULT, RateLater
from tournesol.serializers.metadata import VideoMetadata
from tournesol.utils.constants import MEHESTAN_MAX_SCALED_SCORE
from tournesol.utils.video_language import (
Expand Down Expand Up @@ -203,17 +203,21 @@ def update_n_ratings(self):
def auto_remove_from_rate_later(self, poll, user) -> None:
"""
When called, the entity is removed from the user's rate-later list if
it has been compared at least 4 times.
it has been compared enough times according to the user's auto remove
setting.
"""
from .comparisons import Comparison # pylint: disable=import-outside-toplevel

max_threshold = user.settings.get(poll.name, {}).get(
"rate_later__auto_remove", RATE_LATER_AUTO_REMOVE_DEFAULT
)
n_comparisons = Comparison.objects.filter(
poll=poll, user=user
).filter(
Q(entity_1=self) | Q(entity_2=self)
).count()

if n_comparisons >= 4:
if n_comparisons >= max_threshold:
RateLater.objects.filter(poll=poll, user=user, entity=self).delete()

@property
Expand Down
5 changes: 5 additions & 0 deletions backend/tournesol/models/rate_later.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
from core.models import User
from tournesol.models.poll import Poll

# The default minimum number of comparisons required to remove an entity from
# a user's rate-later list. When this constant is imported and used, the
# user's settings should take precedence over this default value.
RATE_LATER_AUTO_REMOVE_DEFAULT = 4


class RateLater(models.Model):
"""An `Entity` a user wants to rate later in a poll."""
Expand Down
49 changes: 30 additions & 19 deletions backend/tournesol/tests/test_api_comparison.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,20 @@ def _remove_optional_fields(self, comparison):

return comparison

def _compare(self, uid_1, uid_2):
self.client.post(
self.comparisons_base_url,
{
"entity_a": {"uid": uid_1},
"entity_b": {"uid": uid_2},
"criteria_scores": [
{"criteria": "largely_recommended", "score": 10, "weight": 10}
],
"duration_ms": 103,
},
format="json",
)

def test_anonymous_cant_create(self):
"""
An anonymous user can't create a comparison.
Expand Down Expand Up @@ -941,37 +955,34 @@ def test_invalid_criteria_in_comparison(self):

def test_comparing_removes_from_rate_later(self):
"""
Comparing a video multiple time removes it from the rate later list
A video compared several times should be removed from the user's
rate-later list.
If the user hasn't configured `rate_later__auto_remove`, the video
should be removed after 4 comparisons.
"""

# [GIVEN] a user with no settings configured.
self.user.settings = {}
self.user.save(update_fields=["settings"])

self.client.force_authenticate(user=self.user)
video_main, *videos = (VideoFactory() for _ in range(5))

def compare(uid_1, uid_2):
response = self.client.post(
self.comparisons_base_url,
{
"entity_a": {"uid": uid_1},
"entity_b": {"uid": uid_2},
"criteria_scores": [
{"criteria": "largely_recommended", "score": 10, "weight": 10}
],
"duration_ms": 103,
},
format="json",
)

data = {"entity": {"uid": video_main.uid}}
self.client.post(
f"/users/me/rate_later/{self.poll_videos.name}/",
data,
format="json",
)
compare(video_main.uid, videos[0].uid)
# Video main should still be in the rate later list

# The main video should be in the rate later list after 1 comparison.
self._compare(video_main.uid, videos[0].uid)
self.assertEqual(RateLater.objects.filter(entity=video_main).count(), 1)

# The main video should not be in the rate later list after 4 comparison.
for video in videos[1:]:
compare(video_main.uid, video.uid)
# Video main should not be in the rate later list after >= 4 comparisons
self._compare(video_main.uid, video.uid)
self.assertEqual(RateLater.objects.filter(entity=video_main).count(), 0)


Expand Down
58 changes: 52 additions & 6 deletions backend/tournesol/tests/test_api_rate_later.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@
from rest_framework import status
from rest_framework.test import APIClient

from core.models import User
from core.tests.factories.user import UserFactory
from tournesol.models import Entity, Poll, RateLater
from tournesol.models import RateLater
from tournesol.tests.factories.comparison import ComparisonFactory
from tournesol.tests.factories.entity import RateLaterFactory, VideoFactory
from tournesol.tests.factories.entity import VideoFactory
from tournesol.tests.factories.poll import PollFactory


Expand Down Expand Up @@ -382,14 +381,19 @@ def test_auto_remove(self) -> None:
"""
Test of the `auto_remove_from_rate_later` method of the Entity model.
After 4 comparisons, calling the tested method must remove the entity
from the user's rate-later list related to the specified poll.
After a defined number of comparisons, calling the tested method must
remove the entity from the user's rate-later list related to the
specified poll.
"""
poll = self.poll
user = self.user
entity = self.entity_in_ratelater

# Initial state.
# [GIVEN] a user without settings.
user.settings = {}
user.save(update_fields=["settings"])

# [GIVEN] a rate-later list with 1 entity.
self.assertEqual(
RateLater.objects.filter(poll=poll, user=user, entity=entity).count(),
1,
Expand Down Expand Up @@ -450,6 +454,48 @@ def test_auto_remove(self) -> None:
0,
)

def test_auto_remove_is_setting_specific(self) -> None:
"""
Test of the `auto_remove_from_rate_later` method of the Entity model.
After number of comparisons defined by the user's settings, calling
the tested method must remove the entity from the user's rate-later
list.
"""
poll = self.poll
user = self.user
entity = self.entity_in_ratelater

# [GIVEN] a user with the setting `auto_remove` set to 2.
user.settings[poll.name] = {"rate_later__auto_remove": 2}
user.save(update_fields=["settings"])

# [GIVEN] a rate-later list with 1 entity.
self.assertEqual(
RateLater.objects.filter(poll=poll, user=user, entity=entity).count(),
1,
)

# [WHEN] the entity is compared 1 time.
ComparisonFactory(poll=poll, user=user, entity_1=entity)
entity.auto_remove_from_rate_later(poll, user)

# [THEN] the entity should be present in the rate-later list.
self.assertEqual(
RateLater.objects.filter(poll=poll, user=user, entity=entity).count(),
1,
)

# [WHEN] the entity is compared 2 tims.
ComparisonFactory(poll=poll, user=user, entity_1=entity)
entity.auto_remove_from_rate_later(poll, user)

# [THEN] the entity should not be present in the rate-later list.
self.assertEqual(
RateLater.objects.filter(poll=poll, user=user, entity=entity).count(),
0,
)

def test_auto_remove_is_user_specific(self) -> None:
"""
Test of the `auto_remove_from_rate_later` method of the Entity model.
Expand Down

0 comments on commit c9c3b9d

Please sign in to comment.