diff --git a/backend/tournesol/serializers/rating.py b/backend/tournesol/serializers/rating.py index 652a181ca4..936495cbd6 100644 --- a/backend/tournesol/serializers/rating.py +++ b/backend/tournesol/serializers/rating.py @@ -5,6 +5,7 @@ from tournesol.models import ContributorRating from tournesol.serializers.criteria_score import ContributorCriteriaScoreSerializer from tournesol.serializers.entity import EntityNoExtraFieldSerializer, RelatedEntitySerializer +from tournesol.serializers.entity_context import EntityContextSerializer from tournesol.serializers.poll import CollectiveRatingSerializer, IndividualRatingSerializer @@ -20,6 +21,11 @@ class Meta: class ContributorRatingSerializer(ModelSerializer): entity = EntityNoExtraFieldSerializer(read_only=True) + entity_contexts = EntityContextSerializer( + source="entity.single_poll_entity_contexts", + read_only=True, + many=True + ) individual_rating = ExtendedInvididualRatingSerializer(source="*", read_only=True) collective_rating = CollectiveRatingSerializer( source="entity.single_poll_rating", @@ -31,6 +37,7 @@ class Meta: model = ContributorRating fields = [ "entity", + "entity_contexts", "individual_rating", "collective_rating", "is_public", @@ -55,6 +62,7 @@ class Meta: "uid", "is_public", "entity", + "entity_contexts", "individual_rating", "collective_rating", ] diff --git a/backend/tournesol/tests/test_api_ratings.py b/backend/tournesol/tests/test_api_ratings.py index f5a7153020..5c993afe87 100644 --- a/backend/tournesol/tests/test_api_ratings.py +++ b/backend/tournesol/tests/test_api_ratings.py @@ -9,6 +9,7 @@ from core.tests.factories.user import UserFactory from core.utils.time import time_ago, time_ahead from tournesol.models import Comparison, ContributorRating, Poll +from tournesol.models.entity_context import EntityContext, EntityContextLocale from tournesol.tests.factories.comparison import ComparisonFactory from tournesol.tests.factories.entity import VideoFactory from tournesol.tests.factories.entity_poll_rating import EntityPollRatingFactory @@ -102,6 +103,7 @@ def test_authenticated_can_create_with_existing_video(self): ) self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(response.data["entity_contexts"], []) rating = ContributorRating.objects.select_related("poll", "user", "entity").get( poll=self.poll_videos, @@ -240,6 +242,7 @@ def test_authenticated_can_fetch_existing_entity(self): ) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data["entity"]["uid"], video.uid) + self.assertEqual(response.data["entity_contexts"], []) self.assertEqual(response.data["individual_rating"]["is_public"], False) self.assertEqual( response.data["individual_rating"]["criteria_scores"], @@ -268,11 +271,14 @@ def test_authenticated_can_list(self): An authenticated user can list its ratings related to a poll. """ self.client.force_authenticate(user=self.user1) + response = self.client.get(self.ratings_base_url, format="json") + rating = response.data["results"][0] + self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data["count"], 2) - rating = response.data["results"][0] self.assertEqual(rating["entity"]["uid"], self.video2.uid) + self.assertEqual(rating["entity_contexts"], []) self.assertEqual( rating["individual_rating"], { @@ -285,6 +291,46 @@ def test_authenticated_can_list(self): } ) + def test_authenticated_can_list_with_contexts(self): + """ + The list of ratings should include the contexts of each entity. + """ + self.client.force_authenticate(user=self.user1) + + entity_context = EntityContext.objects.create( + name="context_safe", + origin=EntityContext.ASSOCIATION, + predicate={"video_id": self.video1.metadata["video_id"]}, + unsafe=False, + enabled=True, + poll=self.poll_videos, + ) + + entity_context_locale = EntityContextLocale.objects.create( + context=entity_context, + language="en", + text="Hello context", + ) + + EntityPollRatingFactory(poll=self.poll_videos, entity=self.video1, tournesol_score=1) + + response = self.client.get(self.ratings_base_url, format="json") + rating1 = response.data["results"][1] + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(rating1["entity_contexts"]), 1) + self.assertDictEqual( + rating1["entity_contexts"][0], + { + "origin": "ASSOCIATION", + "unsafe": False, + "text": entity_context_locale.text, + "created_at": entity_context.created_at.strftime( + "%Y-%m-%dT%H:%M:%S.%fZ" + ), + } + ) + def test_authenticated_can_list_ordered_by_n_comparisons(self): """ An authenticated user can list its ratings related to a poll by the @@ -630,6 +676,7 @@ def test_authenticated_can_update_public_status(self): ) response_data = response.json() self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response_data["entity_contexts"], []) self.assertEqual(response_data["individual_rating"]["is_public"], True) self.assertEqual( response_data["individual_rating"]["criteria_scores"], diff --git a/frontend/scripts/openapi.yaml b/frontend/scripts/openapi.yaml index e93172c540..da60df4e21 100644 --- a/frontend/scripts/openapi.yaml +++ b/frontend/scripts/openapi.yaml @@ -2850,6 +2850,11 @@ components: allOf: - $ref: '#/components/schemas/EntityNoExtraField' readOnly: true + entity_contexts: + type: array + items: + $ref: '#/components/schemas/EntityContext' + readOnly: true individual_rating: allOf: - $ref: '#/components/schemas/ExtendedInvididualRating' @@ -2862,6 +2867,7 @@ components: required: - collective_rating - entity + - entity_contexts - individual_rating ContributorRatingCreate: type: object @@ -2870,6 +2876,11 @@ components: allOf: - $ref: '#/components/schemas/EntityNoExtraField' readOnly: true + entity_contexts: + type: array + items: + $ref: '#/components/schemas/EntityContext' + readOnly: true individual_rating: allOf: - $ref: '#/components/schemas/ExtendedInvididualRating' @@ -2882,6 +2893,7 @@ components: required: - collective_rating - entity + - entity_contexts - individual_rating ContributorRatingCreateRequest: type: object diff --git a/frontend/src/pages/videos/VideoRatings.tsx b/frontend/src/pages/videos/VideoRatings.tsx index 84526f9715..c59b81710a 100644 --- a/frontend/src/pages/videos/VideoRatings.tsx +++ b/frontend/src/pages/videos/VideoRatings.tsx @@ -115,6 +115,7 @@ const VideoRatingsPage = () => { entities={ratings.results} emptyMessage={} cardProps={{ showRatingControl: true }} + displayContextAlert={true} /> {!isLoading && videoCount > 0 && videoCount > limit && (