Merge pull request #5939 from diox/add-language-tools-api

Add language tools API
This commit is contained in:
Mathieu Pillard 2017-07-18 18:27:07 +02:00 коммит произвёл GitHub
Родитель 9c83d5ae95 2cf22e7705
Коммит 875c13b3c4
6 изменённых файлов: 154 добавлений и 4 удалений

Просмотреть файл

@ -359,3 +359,35 @@ This endpoint allows you to fetch an add-on EULA and privacy policy.
:>json string|object|null eula: The text of the EULA, if present (See :ref:`translated fields <api-overview-translations>`).
:>json string|object|null privacy_policy: The text of the Privacy Policy, if present (See :ref:`translated fields <api-overview-translations>`).
--------------
Language Tools
--------------
.. _addon-language-tools:
This endpoint allows you to list all public language tools add-ons available
on AMO.
.. http:get:: /api/v3/addons/language-tools/
.. note::
Because this endpoint is intended to be used to feed a page that
displays all available language tools in a single page, it is not
paginated as normal, and instead will return all results without
obeying regular pagination parameters. The ordering is left undefined,
it's up to the clients to re-order results as needed before displaying
the add-ons to the end-users.
:query string app: Mandatory. Filter by :ref:`add-on application <addon-detail-application>` availability.
:query string lang: Activate translations in the specific language for that query. (See :ref:`translated fields <api-overview-translations>`)
:>json array results: An array of language tools.
:>json int results[].id: The add-on id on AMO.
:>json object results[].current_version: Object holding the current :ref:`version <version-detail-object>` of the add-on. For performance reasons the ``release_notes`` field is omitted and the ``license`` field omits the ``text`` property.
:>json string results[].default_locale: The add-on default locale for translations.
:>json string|object|null results[].name: The add-on name (See :ref:`translated fields <api-overview-translations>`).
:>json string results[].locale_disambiguation: Free text field allowing clients to distinguish between multiple dictionaries in the same locale but different spellings. Only present when using the Language Tools endpoint.
:>json string results[].target_locale: For dictionaries and language packs, the locale the add-on is meant for. Only present when using the Language Tools endpoint.
:>json string results[].type: The :ref:`add-on type <addon-detail-type>`.
:>json string results[].url: The (absolute) add-on detail URL.

Просмотреть файл

@ -7,7 +7,7 @@ from olympia.activity.views import VersionReviewNotesViewSet
from .views import (
AddonFeaturedView, AddonSearchView, AddonVersionViewSet, AddonViewSet,
StaticCategoryView)
LanguageToolsView, StaticCategoryView)
addons = SimpleRouter()
@ -27,4 +27,6 @@ urlpatterns = [
url(r'^search/$', AddonSearchView.as_view(), name='addon-search'),
url(r'^featured/$', AddonFeaturedView.as_view(), name='addon-featured'),
url(r'^categories/$', StaticCategoryView.as_view(), name='category-list'),
url(r'^language-tools/$', LanguageToolsView.as_view(),
name='addon-language-tools'),
]

Просмотреть файл

@ -486,3 +486,14 @@ class StaticCategorySerializer(serializers.Serializer):
def get_type(self, obj):
return ADDON_TYPE_CHOICES_API[obj.type]
class LanguageToolsSerializer(AddonSerializer):
target_locale = serializers.CharField()
locale_disambiguation = serializers.CharField()
class Meta:
model = Addon
fields = ('id', 'current_version', 'default_locale',
'locale_disambiguation', 'name', 'target_locale', 'type',
'url', )

Просмотреть файл

@ -15,8 +15,8 @@ from olympia.addons.models import (
Addon, AddonCategory, AddonUser, Category, Persona, Preview)
from olympia.addons.serializers import (
AddonSerializer, AddonSerializerWithUnlistedData, ESAddonSerializer,
ESAddonSerializerWithUnlistedData, SimpleVersionSerializer,
VersionSerializer)
ESAddonSerializerWithUnlistedData, LanguageToolsSerializer,
SimpleVersionSerializer, VersionSerializer)
from olympia.addons.utils import generate_addon_guid
from olympia.bandwagon.models import FeaturedCollection
from olympia.constants.categories import CATEGORIES
@ -688,3 +688,30 @@ class TestSimpleVersionSerializerOutput(TestCase):
assert result['license']['name']['fr'] == u'Mä Licence'
assert result['license']['url'] == 'http://license.example.com/'
assert 'text' not in result['license']
class TestLanguageToolsSerializerOutput(TestCase):
def setUp(self):
self.request = APIRequestFactory().get('/')
def serialize(self):
serializer = LanguageToolsSerializer(context={'request': self.request})
return serializer.to_representation(self.addon)
def test_basic(self):
self.addon = addon_factory(
type=amo.ADDON_DICT, target_locale='fr',
locale_disambiguation=u'lolé')
result = self.serialize()
assert result['id'] == self.addon.pk
assert result['default_locale'] == self.addon.default_locale
assert result['locale_disambiguation'] == (
self.addon.locale_disambiguation)
assert result['name'] == {'en-US': self.addon.name}
assert result['target_locale'] == self.addon.target_locale
assert result['url'] == absolutify(self.addon.get_url_path())
addon_testcase = AddonSerializerOutputTestMixin()
addon_testcase.addon = self.addon
addon_testcase._test_version(
self.addon.current_version, result['current_version'])

Просмотреть файл

@ -2858,3 +2858,55 @@ class TestStaticCategoryView(TestCase):
response = self.client.get(self.url)
assert response.status_code == 200
assert response['cache-control'] == 'max-age=21600'
class TestLanguageToolsView(TestCase):
client_class = APITestClient
def setUp(self):
super(TestLanguageToolsView, self).setUp()
self.url = reverse('addon-language-tools')
def test_wrong_app(self):
response = self.client.get(self.url)
assert response.status_code == 400
response = self.client.get(self.url, {'app': 'foo'})
assert response.status_code == 400
def test_basic(self):
dictionary = addon_factory(type=amo.ADDON_DICT, target_locale='fr')
dictionary_spelling_variant = addon_factory(
type=amo.ADDON_DICT, target_locale='fr',
locale_disambiguation='For spelling reform')
language_pack = addon_factory(type=amo.ADDON_DICT, target_locale='es')
# These add-ons below should be ignored: they are either not public or
# of the wrong type, not supporting the app we care about, or their
# target locale is empty.
addon_factory(
type=amo.ADDON_DICT, target_locale='de',
version_kw={'application': amo.THUNDERBIRD.id})
addon_factory(
type=amo.ADDON_DICT, target_locale='fr',
version_kw={'channel': amo.RELEASE_CHANNEL_UNLISTED})
addon_factory(
type=amo.ADDON_LPAPP, target_locale='es',
file_kw={'status': amo.STATUS_AWAITING_REVIEW},
status=amo.STATUS_NOMINATED)
addon_factory(type=amo.ADDON_DICT, target_locale='')
addon_factory(type=amo.ADDON_LPAPP, target_locale=None)
addon_factory(target_locale='fr')
response = self.client.get(self.url, {'app': 'firefox'})
assert response.status_code == 200
data = json.loads(response.content)
assert len(data['results']) == 3
expected = [dictionary, dictionary_spelling_variant, language_pack]
assert (
set(item['id'] for item in data['results']) ==
set(item.pk for item in expected))
assert 'locale_disambiguation' in data['results'][0]
assert 'target_locale' in data['results'][0]

Просмотреть файл

@ -63,7 +63,7 @@ from .models import Addon, Persona, FrozenAddon, ReplacementAddon
from .serializers import (
AddonEulaPolicySerializer, AddonFeatureCompatibilitySerializer,
AddonSerializer, AddonSerializerWithUnlistedData, ESAddonSerializer,
VersionSerializer, StaticCategorySerializer)
LanguageToolsSerializer, VersionSerializer, StaticCategorySerializer)
from .utils import get_creatured_ids, get_featured_ids
@ -859,3 +859,29 @@ class StaticCategoryView(ListAPIView):
request, response, *args, **kwargs)
patch_cache_control(response, max_age=60 * 60 * 6)
return response
class LanguageToolsView(ListAPIView):
authentication_classes = []
pagination_class = None
permission_classes = []
serializer_class = LanguageToolsSerializer
def get_queryset(self):
try:
application_id = AddonAppFilterParam(self.request).get_value()
except ValueError:
raise ParseError('Invalid app parameter.')
types = (amo.ADDON_DICT, amo.ADDON_LPAPP)
return Addon.objects.public().filter(
appsupport__app=application_id, type__in=types,
target_locale__isnull=False).exclude(target_locale='')
def list(self, request, *args, **kwargs):
# Ignore pagination (return everything) but do wrap the data in a
# 'results' property to mimic what the default implementation of list()
# does in DRF.
queryset = self.filter_queryset(self.get_queryset())
serializer = self.get_serializer(queryset, many=True)
return Response({'results': serializer.data})