Updated code to use new index translations fields (bug 947291)
This commit is contained in:
Родитель
d12026a77c
Коммит
a8a2cf957d
|
@ -10,9 +10,10 @@ from mkt.regions.utils import parse_region
|
|||
from mkt.reviewers.forms import ApiReviewersSearchForm, ApproveRegionForm
|
||||
from mkt.reviewers.serializers import ReviewingSerializer
|
||||
from mkt.reviewers.utils import AppsReviewing
|
||||
from mkt.search.api import SearchResultSerializer, SearchView
|
||||
from mkt.search.api import SearchView
|
||||
from mkt.search.utils import S
|
||||
from mkt.webapps.models import Webapp, WebappIndexer
|
||||
from mkt.webapps.utils import get_translations
|
||||
|
||||
|
||||
class ReviewingView(ListAPIView):
|
||||
|
@ -25,8 +26,8 @@ class ReviewingView(ListAPIView):
|
|||
return [row['app'] for row in AppsReviewing(self.request).get_apps()]
|
||||
|
||||
SEARCH_FIELDS = [u'device_types', u'id', u'is_escalated', u'is_packaged',
|
||||
u'latest_version', u'name', u'premium_type', u'price', u'slug',
|
||||
u'status']
|
||||
u'latest_version', u'name', u'premium_type', u'price',
|
||||
u'slug', u'status']
|
||||
|
||||
|
||||
class ReviewersSearchView(SearchView):
|
||||
|
@ -51,11 +52,16 @@ class ReviewersSearchView(SearchView):
|
|||
data = {}
|
||||
for k in SEARCH_FIELDS:
|
||||
data[k] = full_data.get(k)
|
||||
# For translated fields, just return the default locale.
|
||||
data['name'] = get_translations(full_data, 'name',
|
||||
full_data['default_locale'],
|
||||
full_data['default_locale'])
|
||||
# Add reviewer-specific stuff that's not in the standard dehydrate.
|
||||
data['latest_version'] = app.latest_version
|
||||
data['is_escalated'] = app.is_escalated
|
||||
return data
|
||||
|
||||
|
||||
def apply_reviewer_filters(request, qs, data=None):
|
||||
for k in ('has_info_request', 'has_editor_comment'):
|
||||
if data.get(k, None) is not None:
|
||||
|
|
|
@ -60,8 +60,8 @@ class SearchView(CORSMixin, MarketplaceView, GenericAPIView):
|
|||
attempts to do this without authentication and one of the
|
||||
'Regions:BypassFilters' permission or curator-level access to a
|
||||
collection, return a 403.
|
||||
2. If the GET param `region` is set and not empty, attempt to return the
|
||||
region with the specified slug.
|
||||
2. If the GET param `region` is set and not empty, attempt to return
|
||||
the region with the specified slug.
|
||||
3. If request.REGION is set, return it. (If the GET param `region` is
|
||||
either not set or set and empty, RegionMiddleware will attempt to
|
||||
determine the region via IP address).
|
||||
|
@ -92,7 +92,7 @@ class SearchView(CORSMixin, MarketplaceView, GenericAPIView):
|
|||
base_filters = {'type': form_data['type']}
|
||||
|
||||
qs = self.get_query(request, base_filters=base_filters,
|
||||
region=self.get_region(request))
|
||||
region=self.get_region(request))
|
||||
profile = get_feature_profile(request)
|
||||
qs = self.apply_filters(request, qs, data=form_data,
|
||||
profile=profile)
|
||||
|
@ -120,7 +120,6 @@ class SearchView(CORSMixin, MarketplaceView, GenericAPIView):
|
|||
return _filter_search(request, qs, data, region=region,
|
||||
profile=profile)
|
||||
|
||||
|
||||
def collections(self, request, collection_type=None, limit=1):
|
||||
filters = request.GET.dict()
|
||||
filters.setdefault('region', self.get_region(request).slug)
|
||||
|
@ -135,12 +134,12 @@ class SearchView(CORSMixin, MarketplaceView, GenericAPIView):
|
|||
return serializer.data, getattr(qs, 'filter_fallback', None)
|
||||
|
||||
|
||||
|
||||
class FeaturedSearchView(SearchView):
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
serializer = self.search(request)
|
||||
data, filter_fallbacks = self.add_featured_etc(request, serializer.data)
|
||||
data, filter_fallbacks = self.add_featured_etc(request,
|
||||
serializer.data)
|
||||
response = Response(data)
|
||||
for name, value in filter_fallbacks.items():
|
||||
response['API-Fallback-%s' % name] = ','.join(value)
|
||||
|
@ -154,7 +153,8 @@ class FeaturedSearchView(SearchView):
|
|||
)
|
||||
filter_fallbacks = {}
|
||||
for name, col_type in types:
|
||||
data[name], fallback = self.collections(request, collection_type=col_type)
|
||||
data[name], fallback = self.collections(request,
|
||||
collection_type=col_type)
|
||||
if fallback:
|
||||
filter_fallbacks[name] = fallback
|
||||
|
||||
|
@ -181,7 +181,7 @@ class SuggestionsView(SearchView):
|
|||
base_filters = {'type': form_data['type']}
|
||||
|
||||
qs = self.get_query(request, base_filters=base_filters,
|
||||
region=self.get_region(request))
|
||||
region=self.get_region(request))
|
||||
profile = get_feature_profile(request)
|
||||
qs = self.apply_filters(request, qs, data=form_data, profile=profile)
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@ from rest_framework.exceptions import ParseError, PermissionDenied
|
|||
|
||||
import amo
|
||||
import mkt
|
||||
import mkt.regions
|
||||
from access.middleware import ACLMiddleware
|
||||
from addons.models import AddonCategory, AddonDeviceType, AddonUpsell, Category
|
||||
from amo.helpers import absolutify
|
||||
|
@ -24,6 +23,7 @@ from tags.models import Tag
|
|||
from translations.helpers import truncate
|
||||
from users.models import UserProfile
|
||||
|
||||
import mkt.regions
|
||||
from mkt.api.tests.test_oauth import RestOAuth, RestOAuthClient
|
||||
from mkt.collections.constants import (COLLECTIONS_TYPE_BASIC,
|
||||
COLLECTIONS_TYPE_FEATURED,
|
||||
|
@ -150,7 +150,7 @@ class TestApi(RestOAuth, ESTestCase):
|
|||
|
||||
def test_wrong_weight(self):
|
||||
self.category.update(weight=-1)
|
||||
res = self.client.get(self.url, data={'cat': self.category.slug })
|
||||
res = self.client.get(self.url, data={'cat': self.category.slug})
|
||||
eq_(res.status_code, 200)
|
||||
eq_(len(res.json['objects']), 0)
|
||||
|
||||
|
@ -228,7 +228,8 @@ class TestApi(RestOAuth, ESTestCase):
|
|||
{'descriptors': [], 'interactive_elements': [],
|
||||
'ratings': None})
|
||||
eq_(obj['current_version'], u'1.0')
|
||||
eq_(obj['description'], unicode(self.webapp.description))
|
||||
eq_(obj['description'],
|
||||
{'en-US': self.webapp.description.localized_string})
|
||||
eq_(obj['icons']['128'], self.webapp.get_icon_url(128))
|
||||
eq_(obj['id'], str(self.webapp.id))
|
||||
eq_(obj['manifest_url'], self.webapp.get_manifest_url())
|
||||
|
@ -237,7 +238,8 @@ class TestApi(RestOAuth, ESTestCase):
|
|||
'/apps/app/337141/privacy/')
|
||||
eq_(obj['public_stats'], self.webapp.public_stats)
|
||||
eq_(obj['ratings'], {'average': 0.0, 'count': 0})
|
||||
self.assertApiUrlEqual(obj['resource_uri'], '/apps/app/337141/')
|
||||
self.assertApiUrlEqual(obj['resource_uri'],
|
||||
'/apps/app/337141/')
|
||||
eq_(obj['slug'], self.webapp.app_slug)
|
||||
eq_(obj['supported_locales'], ['en-US', 'es', 'pt-BR'])
|
||||
ok_('1.0' in obj['versions'])
|
||||
|
@ -382,6 +384,15 @@ class TestApi(RestOAuth, ESTestCase):
|
|||
eq_(obj['slug'], self.webapp.app_slug)
|
||||
|
||||
def test_name_localized(self):
|
||||
# First test no ?lang parameter returns all localizations.
|
||||
res = self.client.get(self.url, data={'q': 'something'})
|
||||
eq_(res.status_code, 200)
|
||||
obj = res.json['objects'][0]
|
||||
eq_(obj['slug'], self.webapp.app_slug)
|
||||
eq_(obj['name'], {u'en-US': u'Something Something Steamcube!',
|
||||
u'es': u'Algo Algo Steamcube!'})
|
||||
|
||||
# Second test that adding ?lang returns only that localization.
|
||||
res = self.client.get(self.url,
|
||||
data={'q': 'something', 'lang': 'es'})
|
||||
eq_(res.status_code, 200)
|
||||
|
@ -389,6 +400,23 @@ class TestApi(RestOAuth, ESTestCase):
|
|||
eq_(obj['slug'], self.webapp.app_slug)
|
||||
eq_(obj['name'], u'Algo Algo Steamcube!')
|
||||
|
||||
def test_other_localized(self):
|
||||
# Test fields that should be localized.
|
||||
translations = {'en-US': u'Test in English',
|
||||
'es': u'Test in Español'}
|
||||
self.webapp.homepage = translations
|
||||
self.webapp.support_email = translations
|
||||
self.webapp.support_url = translations
|
||||
self.webapp.save()
|
||||
self.refresh('webapp')
|
||||
|
||||
res = self.client.get(self.url, data={'q': 'something'})
|
||||
eq_(res.status_code, 200)
|
||||
obj = res.json['objects'][0]
|
||||
eq_(obj['homepage'], translations)
|
||||
eq_(obj['support_email'], translations)
|
||||
eq_(obj['support_url'], translations)
|
||||
|
||||
def test_name_localized_to_default_locale(self):
|
||||
self.webapp.update(default_locale='es')
|
||||
self.refresh('webapp')
|
||||
|
@ -677,8 +705,8 @@ class BaseFeaturedTests(RestOAuth, ESTestCase):
|
|||
super(BaseFeaturedTests, self).setUp()
|
||||
self.cat = Category.objects.create(type=amo.ADDON_WEBAPP, slug='shiny')
|
||||
self.app = Webapp.objects.get(pk=337141)
|
||||
AddonDeviceType.objects.create(addon=self.app,
|
||||
device_type=DEVICE_CHOICES_IDS['firefoxos'])
|
||||
AddonDeviceType.objects.create(
|
||||
addon=self.app, device_type=DEVICE_CHOICES_IDS['firefoxos'])
|
||||
AddonCategory.objects.get_or_create(addon=self.app, category=self.cat)
|
||||
self.profile = FeatureProfile(apps=True, audio=True, fullscreen=True,
|
||||
geolocation=True, indexeddb=True,
|
||||
|
@ -696,12 +724,12 @@ class TestFeaturedCollections(BaseFeaturedTests):
|
|||
|
||||
def setUp(self):
|
||||
super(TestFeaturedCollections, self).setUp()
|
||||
self.col = Collection.objects.create(name='Hi', description='Mom',
|
||||
collection_type=self.col_type, category=self.cat, is_public=True,
|
||||
region=mkt.regions.US.id)
|
||||
self.col = Collection.objects.create(
|
||||
name='Hi', description='Mom', collection_type=self.col_type,
|
||||
category=self.cat, is_public=True, region=mkt.regions.US.id)
|
||||
self.qs['region'] = mkt.regions.US.slug
|
||||
self.create_switch('collections-use-es-for-apps')
|
||||
# FIXME: mock the search part, we don't care about it
|
||||
# FIXME: mock the search part, we don't care about it.
|
||||
|
||||
def make_request(self):
|
||||
res = self.client.get(self.list_url, self.qs)
|
||||
|
@ -734,8 +762,9 @@ class TestFeaturedCollections(BaseFeaturedTests):
|
|||
eq_(len(json[self.prop_name][0]['apps']), 0)
|
||||
|
||||
def test_only_public(self):
|
||||
self.col2 = Collection.objects.create(name='Col', description='Hidden',
|
||||
collection_type=self.col_type, category=self.cat, is_public=False)
|
||||
self.col2 = Collection.objects.create(
|
||||
name='Col', description='Hidden', collection_type=self.col_type,
|
||||
category=self.cat, is_public=False)
|
||||
res, json = self.test_added_to_results()
|
||||
|
||||
header = 'API-Fallback-%s' % self.prop_name
|
||||
|
@ -748,8 +777,9 @@ class TestFeaturedCollections(BaseFeaturedTests):
|
|||
"""
|
||||
different_type = (COLLECTIONS_TYPE_FEATURED if self.col_type ==
|
||||
COLLECTIONS_TYPE_BASIC else COLLECTIONS_TYPE_BASIC)
|
||||
self.col2 = Collection.objects.create(name='Bye', description='Dad',
|
||||
collection_type=different_type, category=self.cat, is_public=True)
|
||||
self.col2 = Collection.objects.create(
|
||||
name='Bye', description='Dad', collection_type=different_type,
|
||||
category=self.cat, is_public=True)
|
||||
res, json = self.test_added_to_results()
|
||||
|
||||
header = 'API-Fallback-%s' % self.prop_name
|
||||
|
@ -764,9 +794,9 @@ class TestFeaturedCollections(BaseFeaturedTests):
|
|||
"""
|
||||
mock_field_to_native.return_value = None
|
||||
self.col.add_app(self.app)
|
||||
self.col = Collection.objects.create(name='Me', description='Hello',
|
||||
collection_type=self.col_type, category=self.cat, is_public=True,
|
||||
region=mkt.regions.US.id)
|
||||
self.col = Collection.objects.create(
|
||||
name='Me', description='Hello', collection_type=self.col_type,
|
||||
category=self.cat, is_public=True, region=mkt.regions.US.id)
|
||||
self.col.add_app(self.app)
|
||||
# Call standard test method.
|
||||
self.test_added_to_results()
|
||||
|
@ -832,8 +862,9 @@ class TestSuggestionsApi(ESTestCase):
|
|||
self.client = RestOAuthClient(None)
|
||||
self.app1 = Webapp.objects.get(pk=337141)
|
||||
self.app1.save()
|
||||
self.app2 = app_factory(name=u"Second âpp", description=u"Second dèsc" * 25,
|
||||
created=self.days_ago(3))
|
||||
self.app2 = app_factory(name=u'Second âpp',
|
||||
description=u'Second dèsc' * 25,
|
||||
created=self.days_ago(3))
|
||||
self.refresh('webapp')
|
||||
|
||||
def tearDown(self):
|
||||
|
@ -843,19 +874,26 @@ class TestSuggestionsApi(ESTestCase):
|
|||
self.app2.delete()
|
||||
|
||||
def test_suggestions(self):
|
||||
response = self.client.get(self.url)
|
||||
response = self.client.get(self.url, data={'lang': 'en-US'})
|
||||
parsed = json.loads(response.content)
|
||||
eq_(parsed[0], '')
|
||||
self.assertSetEqual(parsed[1], [unicode(self.app1.name),
|
||||
unicode(self.app2.name)])
|
||||
self.assertSetEqual(parsed[2], [unicode(self.app1.description),
|
||||
unicode(truncate(self.app2.description))])
|
||||
self.assertSetEqual(parsed[3], [absolutify(self.app1.get_detail_url()),
|
||||
absolutify(self.app2.get_detail_url())])
|
||||
self.assertSetEqual(parsed[4], [self.app1.get_icon_url(64),
|
||||
self.app2.get_icon_url(64)])
|
||||
self.assertSetEqual(
|
||||
parsed[1],
|
||||
[unicode(self.app1.name), unicode(self.app2.name)])
|
||||
self.assertSetEqual(
|
||||
parsed[2],
|
||||
[unicode(self.app1.description),
|
||||
unicode(truncate(self.app2.description))])
|
||||
self.assertSetEqual(
|
||||
parsed[3],
|
||||
[absolutify(self.app1.get_detail_url()),
|
||||
absolutify(self.app2.get_detail_url())])
|
||||
self.assertSetEqual(
|
||||
parsed[4],
|
||||
[self.app1.get_icon_url(64), self.app2.get_icon_url(64)])
|
||||
|
||||
def test_suggestions_filtered(self):
|
||||
response = self.client.get(self.url, data={'q': 'Second'})
|
||||
response = self.client.get(self.url, data={'q': 'Second',
|
||||
'lang': 'en-US'})
|
||||
parsed = json.loads(response.content)
|
||||
eq_(parsed[1], [unicode(self.app2.name)])
|
||||
|
|
|
@ -206,7 +206,7 @@ class Webapp(Addon):
|
|||
|
||||
ids = set(app.id for app in apps)
|
||||
versions = (Version.objects.no_cache().filter(addon__in=ids)
|
||||
.select_related('addon'))
|
||||
.select_related('addon'))
|
||||
vids = [v.id for v in versions]
|
||||
files = (File.objects.no_cache().filter(version__in=vids)
|
||||
.select_related('version'))
|
||||
|
@ -585,8 +585,8 @@ class Webapp(Addon):
|
|||
:param optional provider: an int for the provider. Defaults to bango.
|
||||
"""
|
||||
if self.has_premium() and self.premium.price:
|
||||
return self.premium.price.get_price(carrier=carrier,
|
||||
region=region, provider=provider)
|
||||
return self.premium.price.get_price(carrier=carrier, region=region,
|
||||
provider=provider)
|
||||
|
||||
def get_price_locale(self, carrier=None, region=None, provider=None):
|
||||
"""
|
||||
|
@ -598,8 +598,8 @@ class Webapp(Addon):
|
|||
:param optional provider: an int for the provider. Defaults to bango.
|
||||
"""
|
||||
if self.has_premium() and self.premium.price:
|
||||
return self.premium.price.get_price_locale(carrier=carrier,
|
||||
region=region, provider=provider)
|
||||
return self.premium.price.get_price_locale(
|
||||
carrier=carrier, region=region, provider=provider)
|
||||
|
||||
def get_tier(self):
|
||||
"""
|
||||
|
@ -748,8 +748,8 @@ class Webapp(Addon):
|
|||
|
||||
srch = S(WebappIndexer).filter(**filters)
|
||||
|
||||
if (region and not
|
||||
waffle.flag_is_active(request, 'override-region-exclusion')):
|
||||
if (region and
|
||||
not waffle.flag_is_active(request, 'override-region-exclusion')):
|
||||
srch = srch.filter(~F(region_exclusions=region.id))
|
||||
|
||||
if mobile or gaia:
|
||||
|
@ -1365,7 +1365,6 @@ class WebappIndexer(MappingType, Indexable):
|
|||
for f in APP_FEATURES)
|
||||
},
|
||||
'has_public_stats': {'type': 'boolean'},
|
||||
'homepage': {'type': 'string', 'index': 'not_analyzed'},
|
||||
'icons': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
|
@ -1406,8 +1405,6 @@ class WebappIndexer(MappingType, Indexable):
|
|||
},
|
||||
'price_tier': {'type': 'string',
|
||||
'index': 'not_analyzed'},
|
||||
'release_notes': {'type': 'string',
|
||||
'index': 'not_analyzed'},
|
||||
'ratings': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
|
@ -1418,10 +1415,6 @@ class WebappIndexer(MappingType, Indexable):
|
|||
'region_exclusions': {'type': 'short'},
|
||||
'reviewed': {'format': 'dateOptionalTime', 'type': 'date'},
|
||||
'status': {'type': 'byte'},
|
||||
'support_email': {'type': 'string',
|
||||
'index': 'not_analyzed'},
|
||||
'support_url': {'type': 'string',
|
||||
'index': 'not_analyzed'},
|
||||
'supported_locales': {'type': 'string',
|
||||
'index': 'not_analyzed'},
|
||||
'tags': {'type': 'string', 'analyzer': 'simple'},
|
||||
|
@ -1487,8 +1480,6 @@ class WebappIndexer(MappingType, Indexable):
|
|||
_locale_field_mapping('name', analyzer))
|
||||
mapping[doc_type]['properties'].update(
|
||||
_locale_field_mapping('description', analyzer))
|
||||
mapping[doc_type]['properties'].update(
|
||||
_locale_field_mapping('release_notes', analyzer))
|
||||
|
||||
# TODO: reviewer flags (bug 848446)
|
||||
|
||||
|
@ -1538,8 +1529,6 @@ class WebappIndexer(MappingType, Indexable):
|
|||
d['device'] = getattr(obj, 'device_ids', [])
|
||||
d['features'] = features
|
||||
d['has_public_stats'] = obj.public_stats
|
||||
# TODO: Store all localizations of homepage.
|
||||
d['homepage'] = unicode(obj.homepage) if obj.homepage else ''
|
||||
d['icons'] = [{'size': icon_size, 'url': obj.get_icon_url(icon_size)}
|
||||
for icon_size in (16, 48, 64, 128)]
|
||||
d['interactive_elements'] = obj.get_interactives(es=True)
|
||||
|
@ -1583,16 +1572,6 @@ class WebappIndexer(MappingType, Indexable):
|
|||
d['region_exclusions'] = obj.get_excluded_region_ids()
|
||||
d['reviewed'] = obj.versions.filter(
|
||||
deleted=False).aggregate(Min('reviewed')).get('reviewed__min')
|
||||
if version:
|
||||
amo.utils.attach_trans_dict(Version, [version])
|
||||
d['release_notes'] = list(set(string for _, string
|
||||
in version.translations[version.releasenotes_id]))
|
||||
else:
|
||||
d['release_notes'] = None
|
||||
d['support_email'] = (unicode(obj.support_email)
|
||||
if obj.support_email else None)
|
||||
d['support_url'] = (unicode(obj.support_url)
|
||||
if obj.support_url else None)
|
||||
if version:
|
||||
d['supported_locales'] = filter(
|
||||
None, version.supported_locales.split(','))
|
||||
|
@ -1664,11 +1643,6 @@ class WebappIndexer(MappingType, Indexable):
|
|||
set(string for locale, string
|
||||
in obj.translations[obj.description_id]
|
||||
if locale.lower() in languages))
|
||||
if version:
|
||||
d['release_notes_' + analyzer] = list(
|
||||
set(string for locale, string
|
||||
in version.translations[version.releasenotes_id]
|
||||
if locale.lower() in languages))
|
||||
|
||||
return d
|
||||
|
||||
|
@ -2018,8 +1992,8 @@ class Geodata(amo.models.ModelBase):
|
|||
db_table = 'webapps_geodata'
|
||||
|
||||
def __unicode__(self):
|
||||
return u'%s (%s): <Webapp %s>' % (self.id,
|
||||
'restricted' if self.restricted else 'unrestricted',
|
||||
return u'%s (%s): <Webapp %s>' % (
|
||||
self.id, 'restricted' if self.restricted else 'unrestricted',
|
||||
self.addon.id)
|
||||
|
||||
def get_status(self, region):
|
||||
|
@ -2102,7 +2076,8 @@ class Geodata(amo.models.ModelBase):
|
|||
for region in mkt.regions.SPECIAL_REGIONS:
|
||||
help_text = _('{region} approval status').format(region=region.name)
|
||||
field = models.PositiveIntegerField(help_text=help_text,
|
||||
choices=amo.STATUS_CHOICES.items(), db_index=True, default=0)
|
||||
choices=amo.STATUS_CHOICES.items(),
|
||||
db_index=True, default=0)
|
||||
field.contribute_to_class(Geodata, 'region_%s_status' % region.slug)
|
||||
|
||||
help_text = _('{region} nomination date').format(region=region.name)
|
||||
|
|
|
@ -1548,9 +1548,15 @@ class TestWebappIndexer(amo.tests.TestCase):
|
|||
eq_(doc['default_locale'], obj.default_locale)
|
||||
eq_(doc['description'], list(
|
||||
set(s for _, s in obj.translations[obj.description_id])))
|
||||
eq_(doc['description_translations'],
|
||||
[{'lang': l, 'string': s}
|
||||
for l, s in obj.translations[obj.description_id]])
|
||||
eq_(doc['device'], [])
|
||||
eq_(doc['name'], list(
|
||||
set(s for _, s in obj.translations[obj.name_id])))
|
||||
eq_(doc['name_translations'],
|
||||
[{'lang': l, 'string': s}
|
||||
for l, s in obj.translations[obj.name_id]])
|
||||
eq_(doc['status'], obj.status)
|
||||
eq_(doc['is_escalated'], False)
|
||||
eq_(doc['latest_version']['status'], amo.STATUS_PUBLIC)
|
||||
|
@ -1665,9 +1671,10 @@ class TestWebappIndexer(amo.tests.TestCase):
|
|||
version.releasenotes = release_notes
|
||||
version.save()
|
||||
obj, doc = self._get_doc()
|
||||
self.assertSetEqual(doc['release_notes'], release_notes.values())
|
||||
eq_(doc['release_notes_french'], [release_notes['fr']])
|
||||
eq_(doc['release_notes_english'], [release_notes['en-US']])
|
||||
eq_(doc['release_notes'][0],
|
||||
{'lang': 'en-US', 'string': release_notes['en-US']})
|
||||
eq_(doc['release_notes'][1],
|
||||
{'lang': 'fr', 'string': release_notes['fr']})
|
||||
|
||||
|
||||
class TestRatingDescriptors(DynamicBoolFieldsTestMixin, amo.tests.TestCase):
|
||||
|
|
|
@ -281,13 +281,15 @@ class TestESAppToDict(amo.tests.ESTestCase):
|
|||
'created': self.app.created,
|
||||
'current_version': '1.0',
|
||||
'default_locale': u'en-US',
|
||||
'description': u'Something Something Steamcube description!',
|
||||
'homepage': '',
|
||||
'description': {
|
||||
u'en-US': u'Something Something Steamcube description!'},
|
||||
'homepage': None,
|
||||
'id': '337141',
|
||||
'is_offline': False,
|
||||
'is_packaged': False,
|
||||
'manifest_url': 'http://micropipes.com/temp/steamcube.webapp',
|
||||
'name': 'Something Something Steamcube!',
|
||||
'name': {u'en-US': u'Something Something Steamcube!',
|
||||
u'es': u'Algo Algo Steamcube!'},
|
||||
'premium_type': 'free',
|
||||
'public_stats': False,
|
||||
'ratings': {
|
||||
|
@ -319,6 +321,26 @@ class TestESAppToDict(amo.tests.ESTestCase):
|
|||
u'Expected value "%s" for field "%s", got "%s"' %
|
||||
(v, k, res[k]))
|
||||
|
||||
def test_basic_with_lang(self):
|
||||
# Check that when ?lang is passed, we get the right language and we get
|
||||
# empty stings instead of None if the strings don't exist.
|
||||
request = RequestFactory().get('/?lang=es')
|
||||
res = es_app_to_dict(self.get_obj(), profile=self.profile,
|
||||
request=request)
|
||||
expected = {
|
||||
'id': '337141',
|
||||
'description': u'Something Something Steamcube description!',
|
||||
'homepage': u'',
|
||||
'name': u'Algo Algo Steamcube!',
|
||||
'support_email': u'',
|
||||
'support_url': u'',
|
||||
}
|
||||
|
||||
for k, v in expected.items():
|
||||
eq_(res[k], v,
|
||||
u'Expected value "%s" for field "%s", got "%s"' %
|
||||
(v, k, res[k]))
|
||||
|
||||
def test_content_ratings(self):
|
||||
self.create_switch('iarc')
|
||||
self.app.set_content_ratings({
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from django.conf import settings
|
||||
from django.utils import translation
|
||||
|
||||
import commonware.log
|
||||
import waffle
|
||||
|
@ -7,8 +7,8 @@ import waffle
|
|||
import amo
|
||||
from addons.models import AddonUser
|
||||
from amo.helpers import absolutify
|
||||
from amo.utils import find_language
|
||||
from amo.urlresolvers import reverse
|
||||
from amo.utils import find_language
|
||||
from constants.applications import DEVICE_TYPES
|
||||
from market.models import Price
|
||||
from users.models import UserProfile
|
||||
|
@ -16,6 +16,7 @@ from users.models import UserProfile
|
|||
import mkt
|
||||
from mkt.regions import REGIONS_CHOICES_ID_DICT
|
||||
|
||||
|
||||
log = commonware.log.getLogger('z.webapps')
|
||||
|
||||
|
||||
|
@ -48,24 +49,28 @@ def get_supported_locales(manifest):
|
|||
manifest.get('locales', {}).keys()))))
|
||||
|
||||
|
||||
def get_attr_lang(src, attr, default_locale):
|
||||
def get_translations(src, attr, default_locale, lang):
|
||||
"""
|
||||
Our index stores localized strings in elasticsearch as, e.g.,
|
||||
"name_spanish": [u'Nombre']. This takes the current language in the
|
||||
threadlocal and gets the localized value, defaulting to
|
||||
settings.LANGUAGE_CODE.
|
||||
"""
|
||||
req_lang = amo.SEARCH_LANGUAGE_TO_ANALYZER.get(
|
||||
translation.get_language().lower())
|
||||
def_lang = amo.SEARCH_LANGUAGE_TO_ANALYZER.get(
|
||||
default_locale.lower())
|
||||
svr_lang = amo.SEARCH_LANGUAGE_TO_ANALYZER.get(
|
||||
settings.LANGUAGE_CODE.lower())
|
||||
Return dict of localized strings for attr or string if lang provided.
|
||||
|
||||
value = (src.get('%s_%s' % (attr, req_lang)) or
|
||||
src.get('%s_%s' % (attr, def_lang)) or
|
||||
src.get('%s_%s' % (attr, svr_lang)))
|
||||
return value[0] if value else u''
|
||||
If lang is provided, try to get the attr localized in lang, falling back to
|
||||
the app's default_locale and server language.
|
||||
|
||||
If lang is not provided, we return all localized strings in the form::
|
||||
|
||||
{"en": "English", "es": "Español"}
|
||||
|
||||
"""
|
||||
translations = src.get(attr, {})
|
||||
requested_language = find_language(lang)
|
||||
|
||||
# If a language was requested, return only that translation.
|
||||
if requested_language:
|
||||
return (translations.get(requested_language) or
|
||||
translations.get(default_locale) or
|
||||
translations.get(settings.LANGUAGE_CODE) or u'')
|
||||
else:
|
||||
return translations or None
|
||||
|
||||
|
||||
def es_app_to_dict(obj, region=None, profile=None, request=None):
|
||||
|
@ -76,6 +81,12 @@ def es_app_to_dict(obj, region=None, profile=None, request=None):
|
|||
from mkt.developers.models import AddonPaymentAccount
|
||||
from mkt.webapps.models import Installed, Webapp
|
||||
|
||||
translation_fields = ('description', 'homepage', 'name', 'release_notes',
|
||||
'support_email', 'support_url')
|
||||
lang = None
|
||||
if request and request.method == 'GET' and 'lang' in request.GET:
|
||||
lang = request.GET.get('lang', '').lower()
|
||||
|
||||
src = obj._source
|
||||
# The following doesn't perform a database query, but gives us useful
|
||||
# methods like `get_detail_url`. If you use `obj` make sure the calls
|
||||
|
@ -83,11 +94,20 @@ def es_app_to_dict(obj, region=None, profile=None, request=None):
|
|||
is_packaged = src.get('app_type') != amo.ADDON_WEBAPP_HOSTED
|
||||
app = Webapp(app_slug=obj.app_slug, is_packaged=is_packaged)
|
||||
|
||||
attrs = ('created', 'current_version', 'default_locale', 'homepage',
|
||||
'is_offline', 'manifest_url', 'previews', 'reviewed', 'ratings',
|
||||
'status', 'support_email', 'support_url', 'weekly_downloads')
|
||||
attrs = ('created', 'current_version', 'default_locale', 'is_offline',
|
||||
'manifest_url', 'previews', 'reviewed', 'ratings', 'status',
|
||||
'weekly_downloads')
|
||||
data = dict((a, getattr(obj, a, None)) for a in attrs)
|
||||
|
||||
# Flatten the localized fields from {'lang': ..., 'string': ...}
|
||||
# to {lang: string}.
|
||||
for field in translation_fields:
|
||||
src_field = '%s_translations' % field
|
||||
src[src_field] = dict((v.get('lang', ''), v.get('string', ''))
|
||||
for v in src.get(src_field))
|
||||
data[field] = get_translations(src, src_field, obj.default_locale,
|
||||
lang)
|
||||
|
||||
if getattr(obj, 'content_ratings', None):
|
||||
for region_key in obj.content_ratings:
|
||||
obj.content_ratings[region_key] = dehydrate_content_rating(
|
||||
|
@ -105,19 +125,15 @@ def es_app_to_dict(obj, region=None, profile=None, request=None):
|
|||
'interactive_elements': dehydrate_interactives(
|
||||
getattr(obj, 'interactive_elements', [])),
|
||||
},
|
||||
'description': get_attr_lang(src, 'description', obj.default_locale),
|
||||
'device_types': [DEVICE_TYPES[d].api_name for d in src.get('device')],
|
||||
'icons': dict((i['size'], i['url']) for i in src.get('icons')),
|
||||
'id': str(obj._id),
|
||||
'is_packaged': is_packaged,
|
||||
'name': get_attr_lang(src, 'name', obj.default_locale),
|
||||
'payment_required': False,
|
||||
'premium_type': amo.ADDON_PREMIUM_API[src.get('premium_type')],
|
||||
'privacy_policy': reverse('app-privacy-policy-detail',
|
||||
kwargs={'pk': obj._id}),
|
||||
'public_stats': obj.has_public_stats,
|
||||
'release_notes': get_attr_lang(src, 'release_notes',
|
||||
obj.default_locale),
|
||||
'supported_locales': src.get('supported_locales', ''),
|
||||
'slug': obj.app_slug,
|
||||
# TODO: Remove the type check once this code rolls out and our indexes
|
||||
|
@ -141,7 +157,8 @@ def es_app_to_dict(obj, region=None, profile=None, request=None):
|
|||
if src.get('premium_type') in amo.ADDON_PREMIUMS:
|
||||
acct = list(AddonPaymentAccount.objects.filter(addon=app))
|
||||
if acct and acct.payment_account:
|
||||
data['payment_account'] = reverse('payment-account-detail',
|
||||
data['payment_account'] = reverse(
|
||||
'payment-account-detail',
|
||||
kwargs={'pk': acct.payment_account.pk})
|
||||
else:
|
||||
data['payment_account'] = None
|
||||
|
@ -173,8 +190,9 @@ def es_app_to_dict(obj, region=None, profile=None, request=None):
|
|||
# TODO: Let's get rid of these from the API to avoid db hits.
|
||||
if profile and isinstance(profile, UserProfile):
|
||||
data['user'] = {
|
||||
'developed': AddonUser.objects.filter(addon=obj.id,
|
||||
user=profile, role=amo.AUTHOR_ROLE_OWNER).exists(),
|
||||
'developed': AddonUser.objects.filter(
|
||||
addon=obj.id, user=profile,
|
||||
role=amo.AUTHOR_ROLE_OWNER).exists(),
|
||||
'installed': Installed.objects.filter(
|
||||
user=profile, addon_id=obj.id).exists(),
|
||||
'purchased': obj.id in profile.purchase_ids(),
|
||||
|
|
Загрузка…
Ссылка в новой задаче