return grouped ratings with addon detail api (#16468)
This commit is contained in:
Родитель
7a3be4cf86
Коммит
dd050597d3
|
@ -136,6 +136,7 @@ This endpoint allows you to fetch a specific add-on by id, slug or guid.
|
|||
:query string app: Used in conjunction with ``appversion`` below to alter ``current_version`` behaviour. Need to be a valid :ref:`add-on application <addon-detail-application>`.
|
||||
:query string appversion: Make ``current_version`` return the latest public version of the add-on compatible with the given application version, if possible, otherwise fall back on the generic implementation. Pass the full version as a string, e.g. ``46.0``. Only valid when the ``app`` parameter is also present. Currently only compatible with language packs through the add-on detail API, ignored for other types of add-ons and APIs.
|
||||
:query string lang: Activate translations in the specific language for that query. (See :ref:`Translated Fields <api-overview-translations>`)
|
||||
:query boolean show_grouped_ratings: Whether or not to show ratings aggregates in the ``ratings`` object (Use "true"/"1" as truthy values, "0"/"false" as falsy ones).
|
||||
:>json int id: The add-on id on AMO.
|
||||
:>json array authors: Array holding information about the authors for the add-on.
|
||||
:>json int authors[].id: The id for an author.
|
||||
|
@ -181,6 +182,12 @@ This endpoint allows you to fetch a specific add-on by id, slug or guid.
|
|||
:>json string ratings_url: The URL to the user ratings list page for the add-on.
|
||||
:>json float ratings.average: The average user rating for the add-on.
|
||||
:>json float ratings.bayesian_average: The bayesian average user rating for the add-on.
|
||||
:>json object ratings.grouped_counts: Object with aggregate counts for ratings. Only included when ``show_grouped_ratings`` is present in the request.
|
||||
:>json int ratings.grouped_counts.1: the count of ratings with a score of 1.
|
||||
:>json int ratings.grouped_counts.2: the count of ratings with a score of 2.
|
||||
:>json int ratings.grouped_counts.3: the count of ratings with a score of 3.
|
||||
:>json int ratings.grouped_counts.4: the count of ratings with a score of 4.
|
||||
:>json int ratings.grouped_counts.5: the count of ratings with a score of 5.
|
||||
:>json boolean requires_payment: Does the add-on require payment, non-free services or software, or additional hardware.
|
||||
:>json string review_url: The URL to the reviewer review page for the add-on.
|
||||
:>json string slug: The add-on slug.
|
||||
|
|
|
@ -410,6 +410,7 @@ These are `v5` specific changes - `v4` changes apply also.
|
|||
* 2021-01-28: made ``description_text`` in discovery endpoint a translated field in the response. (It was always localized, we just didn't return it as such). https://github.com/mozilla/addons-server/issues/8712
|
||||
* 2021-02-04: dropped /shelves/sponsored endpoint https://github.com/mozilla/addons-server/issues/16390
|
||||
* 2021-02-11: removed Stripe webhook endpoint https://github.com/mozilla/addons-server/issues/16391
|
||||
* 2021-02-11: added ``show_grouped_ratings`` to addon detail endpoint. https://github.com/mozilla/addons-server/issues/16459
|
||||
|
||||
.. _`#11380`: https://github.com/mozilla/addons-server/issues/11380/
|
||||
.. _`#11379`: https://github.com/mozilla/addons-server/issues/11379/
|
||||
|
|
|
@ -31,6 +31,7 @@ from olympia.constants.promoted import PROMOTED_GROUPS, RECOMMENDED
|
|||
from olympia.files.models import File
|
||||
from olympia.promoted.models import PromotedAddon
|
||||
from olympia.search.filters import AddonAppVersionQueryParam
|
||||
from olympia.ratings.utils import get_grouped_ratings
|
||||
from olympia.users.models import UserProfile
|
||||
from olympia.versions.models import (
|
||||
ApplicationsVersions,
|
||||
|
@ -505,12 +506,17 @@ class AddonSerializer(serializers.ModelSerializer):
|
|||
return {str(size): absolutify(get_icon(size)) for size in amo.ADDON_ICON_SIZES}
|
||||
|
||||
def get_ratings(self, obj):
|
||||
return {
|
||||
ratings = {
|
||||
'average': obj.average_rating,
|
||||
'bayesian_average': obj.bayesian_rating,
|
||||
'count': obj.total_ratings,
|
||||
'text_count': obj.text_ratings_count,
|
||||
}
|
||||
if (request := self.context.get('request', None)) and (
|
||||
grouped := get_grouped_ratings(request, obj)
|
||||
):
|
||||
ratings['grouped_counts'] = grouped
|
||||
return ratings
|
||||
|
||||
def get_is_source_public(self, obj):
|
||||
return False
|
||||
|
@ -742,6 +748,14 @@ class ESAddonSerializer(BaseESSerializer, AddonSerializer):
|
|||
# to_representation() is called, so it's present on all objects.
|
||||
return obj._es_meta['score']
|
||||
|
||||
def get_ratings(self, obj):
|
||||
return {
|
||||
'average': obj.average_rating,
|
||||
'bayesian_average': obj.bayesian_rating,
|
||||
'count': obj.total_ratings,
|
||||
'text_count': obj.text_ratings_count,
|
||||
}
|
||||
|
||||
def to_representation(self, obj):
|
||||
data = super(ESAddonSerializer, self).to_representation(obj)
|
||||
request = self.context.get('request')
|
||||
|
|
|
@ -49,6 +49,7 @@ from olympia.constants.categories import CATEGORIES
|
|||
from olympia.constants.licenses import LICENSES_BY_BUILTIN
|
||||
from olympia.constants.promoted import RECOMMENDED
|
||||
from olympia.files.models import WebextPermission
|
||||
from olympia.ratings.models import Rating
|
||||
from olympia.versions.models import (
|
||||
ApplicationsVersions,
|
||||
AppVersion,
|
||||
|
@ -735,6 +736,17 @@ class AddonSerializerOutputTestMixin(object):
|
|||
result = self.serialize()
|
||||
assert 'created' not in result
|
||||
|
||||
def test_grouped_ratings(self):
|
||||
self.addon = addon_factory()
|
||||
self.request = self.get_request('/', {'show_grouped_ratings': 1})
|
||||
result = self.serialize()
|
||||
assert result['ratings']['grouped_counts'] == {1: 0, 2: 0, 3: 0, 4: 0, 5: 0}
|
||||
Rating.objects.create(addon=self.addon, rating=2, user=user_factory())
|
||||
Rating.objects.create(addon=self.addon, rating=2, user=user_factory())
|
||||
Rating.objects.create(addon=self.addon, rating=5, user=user_factory())
|
||||
result = self.serialize()
|
||||
assert result['ratings']['grouped_counts'] == {1: 0, 2: 2, 3: 0, 4: 0, 5: 1}
|
||||
|
||||
|
||||
class TestAddonSerializerOutput(AddonSerializerOutputTestMixin, TestCase):
|
||||
serializer_class = AddonSerializer
|
||||
|
@ -1000,6 +1012,13 @@ class TestESAddonSerializerOutput(AddonSerializerOutputTestMixin, ESTestCase):
|
|||
result = self.serialize()
|
||||
assert '_score' not in result
|
||||
|
||||
def test_grouped_ratings(self):
|
||||
# as grouped ratings aren't stored in ES, we don't support this
|
||||
self.addon = addon_factory()
|
||||
self.request = self.get_request('/', {'show_grouped_ratings': 1})
|
||||
result = self.serialize()
|
||||
assert 'grouped_counts' not in result['ratings']
|
||||
|
||||
|
||||
class TestVersionSerializerOutput(TestCase):
|
||||
def setUp(self):
|
||||
|
|
|
@ -644,6 +644,24 @@ class TestAddonViewSetDetail(AddonAndVersionViewSetDetailMixin, TestCase):
|
|||
data = json.loads(force_str(response.content))
|
||||
assert data == {'detail': 'Invalid "app" parameter.'}
|
||||
|
||||
def test_with_grouped_ratings(self):
|
||||
assert 'grouped_counts' not in self.client.get(self.url).json()['ratings']
|
||||
|
||||
response = self.client.get(self.url, {'show_grouped_ratings': 'true'})
|
||||
assert 'grouped_counts' in response.json()['ratings']
|
||||
assert response.json()['ratings']['grouped_counts'] == {
|
||||
'1': 0,
|
||||
'2': 0,
|
||||
'3': 0,
|
||||
'4': 0,
|
||||
'5': 0,
|
||||
}
|
||||
|
||||
response = self.client.get(self.url, {'show_grouped_ratings': '58.0'})
|
||||
assert response.status_code == 400
|
||||
data = json.loads(force_str(response.content))
|
||||
assert data == {'detail': 'show_grouped_ratings parameter should be a boolean'}
|
||||
|
||||
|
||||
class TestVersionViewSetDetail(AddonAndVersionViewSetDetailMixin, TestCase):
|
||||
client_class = APITestClient
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
from rest_framework import serializers
|
||||
from rest_framework.exceptions import ParseError
|
||||
|
||||
from .models import GroupedRating
|
||||
|
||||
|
||||
def get_grouped_ratings(request, addon):
|
||||
if 'show_grouped_ratings' in request.GET:
|
||||
try:
|
||||
show_grouped_ratings = serializers.BooleanField().to_internal_value(
|
||||
request.GET['show_grouped_ratings']
|
||||
)
|
||||
except serializers.ValidationError:
|
||||
raise ParseError('show_grouped_ratings parameter should be a boolean')
|
||||
if show_grouped_ratings and addon:
|
||||
return dict(GroupedRating.get(addon.id))
|
|
@ -26,9 +26,10 @@ from olympia.api.permissions import (
|
|||
from olympia.api.throttling import GranularUserRateThrottle
|
||||
from olympia.api.utils import is_gate_active
|
||||
|
||||
from .models import GroupedRating, Rating, RatingFlag
|
||||
from .models import Rating, RatingFlag
|
||||
from .permissions import CanDeleteRatingPermission
|
||||
from .serializers import RatingFlagSerializer, RatingSerializer, RatingSerializerReply
|
||||
from .utils import get_grouped_ratings
|
||||
|
||||
|
||||
class RatingThrottle(GranularUserRateThrottle):
|
||||
|
@ -226,17 +227,8 @@ class RatingViewSet(AddonChildMixin, ModelViewSet):
|
|||
def get_paginated_response(self, data):
|
||||
request = self.request
|
||||
extra_data = {}
|
||||
if 'show_grouped_ratings' in request.GET:
|
||||
try:
|
||||
show_grouped_ratings = serializers.BooleanField().to_internal_value(
|
||||
request.GET['show_grouped_ratings']
|
||||
)
|
||||
except serializers.ValidationError:
|
||||
raise ParseError('show_grouped_ratings parameter should be a boolean')
|
||||
if show_grouped_ratings and self.get_addon_object():
|
||||
extra_data['grouped_ratings'] = dict(
|
||||
GroupedRating.get(self.addon_object.id)
|
||||
)
|
||||
if grouped_rating := get_grouped_ratings(request, self.get_addon_object()):
|
||||
extra_data['grouped_ratings'] = grouped_rating
|
||||
if 'show_permissions_for' in request.GET and is_gate_active(
|
||||
self.request, 'ratings-can_reply'
|
||||
):
|
||||
|
|
Загрузка…
Ссылка в новой задаче