def get_context_data(self): # We need to display the name in some language that is relevant to the # recipient(s) instead of using the reviewer's. addon.default_locale # should work. if self.addon.name.locale != self.addon.default_locale: lang = to_language(self.addon.default_locale) with translation.override(lang): app = Webapp.objects.get(id=self.addon.id) else: app = self.addon return { 'name': app.name, 'reviewer': self.request.user.get_profile().name, 'detail_url': absolutify(app.get_url_path(add_prefix=False)), 'review_url': absolutify( reverse('reviewers.apps.review', args=[app.app_slug], add_prefix=False)), 'status_url': absolutify(app.get_dev_url('versions')), 'comments': self.data['comments'], 'MKT_SUPPORT_EMAIL': settings.MKT_SUPPORT_EMAIL, 'SITE_URL': settings.SITE_URL }
def category_featured(self, category=None, by_locale=False): """ Filter for all featured addons for a category. Specify by_locale to have it filtered by the current locale. """ qs = Q(addoncategory__feature=True) if by_locale: locales = [] for locale in by_locale(): prefix = 'addoncategory__feature_locales' if locale: locale = to_language(locale) prefix += '__contains' else: # Sadly looks like theres '' and NULL in the column, # hopefully the new admin tools will clean this out. locales.append(Q(**{prefix: ''})) locales.append(Q(**{prefix: locale})) qs = (qs & Q(addoncategory__category=category) & reduce(operator.or_, locales)) return self.filter(qs).order_by('-addoncategory__feature_locales') if category: qs = qs & Q(addoncategory__category=category) return self.filter(qs)
def new_payments_region_email(ids, region_slug, **kw): region_name = dict(REGIONS_CHOICES_SLUG)[region_slug].name log.info('[%s@%s] Emailing paid-app devs about new region: %s.' % (len(ids), new_payments_region_email.rate_limit, unicode(region_name))) for id_ in ids: log.info('[Webapp:%s] Emailing paid-app devs about new region: %s.' % (id_, unicode(region_name))) app = Webapp.objects.get(id=id_) for author in app.authors.all(): to = [author.email] with author.activate_lang(): if author.lang is not None: lang = to_language(author.lang) with translation.override(lang): app = Webapp.objects.get(id=id_) context = {'app': app.name, 'region': region_name, 'payments_url': app.get_dev_url('payments')} subject = _(u'{app}: {region} region added to the Firefox ' u'Marketplace').format(**context) send_mail_jinja(subject, 'developers/emails/new_payments_region.html', context, recipient_list=to, perm_setting='app_regions')
def new_payments_region_email(ids, region_slug, **kw): region_name = dict(REGIONS_CHOICES_SLUG)[region_slug].name log.info( '[%s@%s] Emailing paid-app devs about new region: %s.' % (len(ids), new_payments_region_email.rate_limit, unicode(region_name))) for id_ in ids: log.info('[Webapp:%s] Emailing paid-app devs about new region: %s.' % (id_, unicode(region_name))) app = Webapp.objects.get(id=id_) for author in app.authors.all(): to = [author.email] with author.activate_lang(): if author.lang is not None: lang = to_language(author.lang) with translation.override(lang): app = Webapp.objects.get(id=id_) context = { 'app': app.name, 'region': region_name, 'payments_url': app.get_dev_url('payments') } subject = _(u'{app}: {region} region added to the Firefox ' u'Marketplace').format(**context) send_mail_jinja(subject, 'developers/emails/new_payments_region.html', context, recipient_list=to, perm_setting='app_regions')
def field_to_native(self, obj, field_name): field = getattr(obj, field_name) if not self.return_all_translations: return unicode(field) else: translations = field.__class__.objects.filter(id=field.id, localized_string__isnull=False) return dict((to_language(trans.locale), unicode(trans)) for trans in translations)
def field_to_native(self, obj, field_name): field = getattr(obj, field_name) if field is None: return None if not self.return_all_translations: return unicode(field) else: translations = field.__class__.objects.filter( id=field.id, localized_string__isnull=False) return dict((to_language(trans.locale), unicode(trans)) for trans in translations)
def from_upload(cls, upload, platforms): from files.utils import parse_addon data = parse_addon(upload.path) fields = cls._meta.get_all_field_names() addon = Addon(**dict((k, v) for k, v in data.items() if k in fields)) addon.status = amo.STATUS_NULL addon.default_locale = to_language(translation.get_language()) addon.save() Version.from_upload(upload, addon, platforms) amo.log(amo.LOG.CREATE_ADDON, addon) log.debug('New addon %r from %r' % (addon, upload)) return addon
def test_extract(self): obj, doc = self._get_doc() eq_(doc['id'], obj.id) eq_(doc['app_slug'], obj.app_slug) eq_(doc['category'], []) 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': to_language(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': to_language(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) eq_(doc['latest_version']['has_editor_comment'], False) eq_(doc['latest_version']['has_info_request'], False)
def format_translation_es(obj, field): """ Returns a denormalized format of a localized field for ES to be deserialized by ESTranslationSerializerField. """ from amo.utils import to_language extend_with_me = {} extend_with_me['%s_translations' % field] = [ {'lang': to_language(lang), 'string': string} for lang, string in obj.translations[getattr(obj, '%s_id' % field)] if string ] return extend_with_me
def featured(self, app, by_locale=False): """ Filter for all featured add-ons for an application. Specify by_locale to have it filtered by the current locale. """ qs = Q(feature__application=app.id) if by_locale: locales = [] for locale in by_locale(): locale = to_language(locale) if locale else locale locales.append(Q(feature__locale=locale)) qs = qs & Q(feature__isnull=False) & reduce(operator.or_, locales) return (self.valid().filter(qs).order_by('-feature__locale')) return self.valid().filter(qs)
def field_to_native(self, obj, field_name): source = self.source or field_name value = obj for component in source.split("."): value = fields.get_component(value, component) if value is None: break field = value if field is None: return None if not self.return_all_translations: return unicode(field) else: translations = field.__class__.objects.filter(id=field.id, localized_string__isnull=False) return dict((to_language(trans.locale), unicode(trans)) for trans in translations)
def get_context_data(self): # We need to display the name in some language that is relevant to the # recipient(s) instead of using the reviewer's. addon.default_locale # should work. if self.addon.name.locale != self.addon.default_locale: lang = to_language(self.addon.default_locale) with translation.override(lang): app = Webapp.objects.get(id=self.addon.id) else: app = self.addon return { "name": app.name, "reviewer": self.request.user.get_profile().name, "detail_url": absolutify(app.get_url_path(add_prefix=False)), "review_url": absolutify(reverse("reviewers.apps.review", args=[app.app_slug], add_prefix=False)), "status_url": absolutify(app.get_dev_url("versions")), "comments": self.data["comments"], "MKT_SUPPORT_EMAIL": settings.MKT_SUPPORT_EMAIL, "SITE_URL": settings.SITE_URL, }
def get_context_data(self): # We need to display the name in some language that is relevant to the # recipient(s) instead of using the reviewer's. addon.default_locale # should work. if self.addon.name.locale != self.addon.default_locale: lang = to_language(self.addon.default_locale) with translation.override(lang): app = Webapp.objects.get(id=self.addon.id) else: app = self.addon return {'name': app.name, 'reviewer': self.request.user.name, 'detail_url': absolutify( app.get_url_path(add_prefix=False)), 'review_url': absolutify(reverse('reviewers.apps.review', args=[app.app_slug], add_prefix=False)), 'status_url': absolutify(app.get_dev_url('versions')), 'comments': self.data['comments'], 'MKT_SUPPORT_EMAIL': settings.MKT_SUPPORT_EMAIL, 'SITE_URL': settings.SITE_URL}
def get_mail_context(note): """ Get context data for comm emails, specifically for review action emails. """ app = note.thread.addon if app.name.locale != app.default_locale: # We need to display the name in some language that is relevant to the # recipient(s) instead of using the reviewer's. addon.default_locale # should work. lang = to_language(app.default_locale) with translation.override(lang): app = Webapp.objects.get(id=app.id) return { 'amo': amo, 'app': app, 'comm': comm, 'note': note, 'review_url': absolutify(reverse('reviewers.apps.review', args=[app.app_slug], add_prefix=False)), 'settings': settings }
def trans_locale(self, locale): # TODO(Kumar) translate all possible locale differences. # 'en' might be the only one in need of translating. if locale == 'en': locale = 'en-us' return to_language(locale)
def trans_locale(self, locale): return to_language(settings.SHORTER_LANGUAGES.get(locale, locale))
def field_to_native(self, obj, field_name): field = getattr(obj, field_name) translations = field.__class__.objects.filter(id=field.id, localized_string__isnull=False) return dict((to_language(trans.locale), unicode(trans)) for trans in translations)
def fetch_all_translations(self, obj, source, field): translations = field.__class__.objects.filter( id=field.id, localized_string__isnull=False) return dict((to_language(trans.locale), unicode(trans)) for trans in translations) if translations else None
class Collection(amo.models.ModelBase): collection_type = models.IntegerField(choices=COLLECTION_TYPES) description = PurifiedField() name = PurifiedField() is_public = models.BooleanField(default=False) # FIXME: add better / composite indexes that matches the query we are # going to make. category = models.ForeignKey(Category, null=True, blank=True) region = models.PositiveIntegerField( default=None, null=True, blank=True, choices=mkt.regions.REGIONS_CHOICES_ID, db_index=True) carrier = models.IntegerField(default=None, null=True, blank=True, choices=mkt.carriers.CARRIER_CHOICES, db_index=True) author = models.CharField(max_length=255, default='', blank=True) slug = models.SlugField(blank=True, max_length=30, help_text='Used in collection URLs.') default_language = models.CharField( max_length=10, choices=((to_language(lang), desc) for lang, desc in settings.LANGUAGES.items()), default=to_language(settings.LANGUAGE_CODE)) background_color = ColorField(null=True) text_color = ColorField(null=True) objects = amo.models.ManagerBase() public = PublicCollectionsManager() class Meta: db_table = 'app_collections' ordering = ('-id', ) # This will change soon since we'll need to be # able to order collections themselves, but this # helps tests for now. def __unicode__(self): return self.name.localized_string_clean def save(self, **kw): self.clean_slug() return super(Collection, self).save(**kw) @use_master def clean_slug(self): clean_slug(self, 'slug') @classmethod def get_fallback(cls): return cls._meta.get_field('default_language') def apps(self): """ Return a list containing all apps in this collection. """ return [a.app for a in self.collectionmembership_set.all()] def add_app(self, app, order=None): """ Add an app to this collection. If specified, the app will be created with the specified `order`. If not, it will be added to the end of the collection. """ if not order: qs = CollectionMembership.objects.filter(collection=self) aggregate = qs.aggregate(models.Max('order'))['order__max'] order = aggregate + 1 if aggregate is not None else 0 return CollectionMembership.objects.create(collection=self, app=app, order=order) def remove_app(self, app): """ Remove the passed app from this collection, returning a boolean indicating whether a successful deletion took place. """ try: membership = self.collectionmembership_set.get(app=app) except CollectionMembership.DoesNotExist: return False else: membership.delete() return True def reorder(self, new_order): """ Passed a list of app IDs, e.g. [18, 24, 9] will change the order of each item in the collection to match the passed order. A ValueError will be raised if each app in the collection is not included in the ditionary. """ if set(a.pk for a in self.apps()) != set(new_order): raise ValueError('Not all apps included') for order, pk in enumerate(new_order): CollectionMembership.objects.get(collection=self, app_id=pk).update(order=order)
def extract_document(cls, pk, obj=None): """Extracts the ElasticSearch index document for this instance.""" from mkt.webapps.models import AppFeatures, Geodata, Installed, Webapp if obj is None: obj = cls.get_model().objects.no_cache().get(pk=pk) latest_version = obj.latest_version version = obj.current_version geodata = obj.geodata features = version.features.to_dict() if version else AppFeatures().to_dict() is_escalated = obj.escalationqueue_set.exists() try: status = latest_version.statuses[0][1] if latest_version else None except IndexError: status = None installed_ids = list(Installed.objects.filter(addon=obj).values_list("id", flat=True)) attrs = ( "app_slug", "bayesian_rating", "created", "id", "is_disabled", "last_updated", "modified", "premium_type", "status", "type", "uses_flash", "weekly_downloads", ) d = dict(zip(attrs, attrgetter(*attrs)(obj))) d["app_type"] = obj.app_type_id d["author"] = obj.developer_name d["banner_regions"] = geodata.banner_regions_slugs() d["category"] = list(obj.categories.values_list("slug", flat=True)) if obj.is_public: d["collection"] = [ {"id": cms.collection_id, "order": cms.order} for cms in obj.collectionmembership_set.all() ] else: d["collection"] = [] d["content_ratings"] = obj.get_content_ratings_by_body(es=True) or None d["content_descriptors"] = obj.get_descriptors_slugs() d["current_version"] = version.version if version else None d["default_locale"] = obj.default_locale d["description"] = list(set(string for _, string in obj.translations[obj.description_id])) d["device"] = getattr(obj, "device_ids", []) d["features"] = features d["has_public_stats"] = obj.public_stats d["icon_hash"] = obj.icon_hash d["interactive_elements"] = obj.get_interactives_slugs() d["is_escalated"] = is_escalated d["is_offline"] = getattr(obj, "is_offline", False) if latest_version: d["latest_version"] = { "status": status, "is_privileged": latest_version.is_privileged, "has_editor_comment": latest_version.has_editor_comment, "has_info_request": latest_version.has_info_request, } else: d["latest_version"] = { "status": None, "is_privileged": None, "has_editor_comment": None, "has_info_request": None, } d["manifest_url"] = obj.get_manifest_url() d["package_path"] = obj.get_package_path() d["name"] = list(set(string for _, string in obj.translations[obj.name_id])) d["name_sort"] = unicode(obj.name).lower() d["owners"] = [au.user.id for au in obj.addonuser_set.filter(role=amo.AUTHOR_ROLE_OWNER)] d["popularity"] = d["_boost"] = len(installed_ids) d["previews"] = [{"filetype": p.filetype, "modified": p.modified, "id": p.id} for p in obj.previews.all()] try: p = obj.addonpremium.price d["price_tier"] = p.name except AddonPremium.DoesNotExist: d["price_tier"] = None d["ratings"] = {"average": obj.average_rating, "count": obj.total_reviews} d["region_exclusions"] = obj.get_excluded_region_ids() d["reviewed"] = obj.versions.filter(deleted=False).aggregate(Min("reviewed")).get("reviewed__min") if version: d["supported_locales"] = filter(None, version.supported_locales.split(",")) else: d["supported_locales"] = [] d["tags"] = getattr(obj, "tag_list", []) if obj.upsell and obj.upsell.premium.is_public(): upsell_obj = obj.upsell.premium d["upsell"] = { "id": upsell_obj.id, "app_slug": upsell_obj.app_slug, "icon_url": upsell_obj.get_icon_url(128), # TODO: Store all localizations of upsell.name. "name": unicode(upsell_obj.name), "region_exclusions": upsell_obj.get_excluded_region_ids(), } d["versions"] = [dict(version=v.version, resource_uri=reverse_version(v)) for v in obj.versions.all()] # Calculate weight. It's similar to popularity, except that we can # expose the number - it's relative to the max weekly downloads for # the whole database. max_downloads = float(Webapp.objects.aggregate(Max("weekly_downloads")).values()[0] or 0) if max_downloads: d["weight"] = math.ceil(d["weekly_downloads"] / max_downloads * 5) else: d["weight"] = 1 # Handle our localized fields. for field in ("description", "homepage", "name", "support_email", "support_url"): d["%s_translations" % field] = [ {"lang": to_language(lang), "string": string} for lang, string in obj.translations[getattr(obj, "%s_id" % field)] if string ] if version: amo.utils.attach_trans_dict(Version, [version]) d["release_notes_translations"] = [ {"lang": to_language(lang), "string": string} for lang, string in version.translations[version.releasenotes_id] ] else: d["release_notes_translations"] = None amo.utils.attach_trans_dict(Geodata, [geodata]) d["banner_message_translations"] = [ {"lang": to_language(lang), "string": string} for lang, string in geodata.translations[geodata.banner_message_id] ] for region in mkt.regions.ALL_REGION_IDS: d["popularity_%s" % region] = d["popularity"] # Bump the boost if the add-on is public. if obj.status == amo.STATUS_PUBLIC: d["_boost"] = max(d["_boost"], 1) * 4 # If the app is compatible with Firefox OS, push suggestion data in the # index - This will be used by RocketbarView API, which is specific to # Firefox OS. if DEVICE_GAIA.id in d["device"] and obj.is_public(): d["name_suggest"] = { "input": d["name"], "output": unicode(obj.id), # We only care about the payload. "weight": d["_boost"], "payload": { "default_locale": d["default_locale"], "icon_hash": d["icon_hash"], "id": d["id"], "manifest_url": d["manifest_url"], "modified": d["modified"], "name_translations": d["name_translations"], "slug": d["app_slug"], }, } # Indices for each language. languages is a list of locales we want to # index with analyzer if the string's locale matches. for analyzer, languages in amo.SEARCH_ANALYZER_MAP.iteritems(): if not settings.ES_USE_PLUGINS and analyzer in amo.SEARCH_ANALYZER_PLUGINS: continue d["name_" + analyzer] = list( set(string for locale, string in obj.translations[obj.name_id] if locale.lower() in languages) ) d["description_" + analyzer] = list( set(string for locale, string in obj.translations[obj.description_id] if locale.lower() in languages) ) return d
def fetch_all_translations(self, obj, source, field): translations = field.__class__.objects.filter(id=field.id, localized_string__isnull=False) return dict((to_language(trans.locale), unicode(trans)) for trans in translations) if translations else None
def check(a, b): eq_(to_language(a), b)
class Collection(amo.models.ModelBase): collection_type = models.IntegerField(choices=COLLECTION_TYPES) description = PurifiedField() name = PurifiedField() is_public = models.BooleanField(default=False) # FIXME: add better / composite indexes that matches the query we are # going to make. category = models.ForeignKey(Category, null=True, blank=True) region = models.PositiveIntegerField( default=None, null=True, blank=True, choices=mkt.regions.REGIONS_CHOICES_ID, db_index=True) carrier = models.IntegerField(default=None, null=True, blank=True, choices=mkt.carriers.CARRIER_CHOICES, db_index=True) author = models.CharField(max_length=255, default='', blank=True) slug = SlugField(blank=True, max_length=30, help_text='Used in collection URLs.') default_language = models.CharField( max_length=10, choices=((to_language(lang), desc) for lang, desc in settings.LANGUAGES.items()), default=to_language(settings.LANGUAGE_CODE)) curators = models.ManyToManyField('users.UserProfile') background_color = ColorField(null=True) text_color = ColorField(null=True) has_image = models.BooleanField(default=False) can_be_hero = models.BooleanField( default=False, help_text= ('Indicates whether an operator shelf collection can be displayed with' 'a hero graphic')) _apps = models.ManyToManyField(Webapp, through='CollectionMembership', related_name='app_collections') objects = amo.models.ManagerBase() public = PublicCollectionsManager() class Meta: db_table = 'app_collections' ordering = ('-id', ) # This will change soon since we'll need to be # able to order collections themselves, but this # helps tests for now. def __unicode__(self): return self.name.localized_string_clean def save(self, **kw): self.clean_slug() return super(Collection, self).save(**kw) @use_master def clean_slug(self): clean_slug(self, 'slug') @classmethod def get_fallback(cls): return cls._meta.get_field('default_language') def image_path(self): return os.path.join(settings.COLLECTIONS_ICON_PATH, str(self.pk / 1000), 'app_collection_%s.png' % (self.pk, )) def apps(self): """ Public apps on the collection, ordered by their position in the CollectionMembership model. Use this method everytime you want to display apps for a collection to an user. """ return self._apps.filter( disabled_by_user=False, status=amo.STATUS_PUBLIC).order_by('collectionmembership') def add_app(self, app, order=None): """ Add an app to this collection. If specified, the app will be created with the specified `order`. If not, it will be added to the end of the collection. """ qs = CollectionMembership.objects.filter(collection=self) if order is None: aggregate = qs.aggregate(models.Max('order'))['order__max'] order = aggregate + 1 if aggregate is not None else 0 rval = CollectionMembership.objects.create(collection=self, app=app, order=order) # Help django-cache-machine: it doesn't like many 2 many relations, # the cache is never invalidated properly when adding a new object. CollectionMembership.objects.invalidate(*qs) index_webapps.delay([app.pk]) return rval def remove_app(self, app): """ Remove the passed app from this collection, returning a boolean indicating whether a successful deletion took place. """ try: membership = self.collectionmembership_set.get(app=app) except CollectionMembership.DoesNotExist: return False else: membership.delete() index_webapps.delay([app.pk]) return True def reorder(self, new_order): """ Passed a list of app IDs, e.g. [18, 24, 9] will change the order of each item in the collection to match the passed order. A ValueError will be raised if each app in the collection is not included in the ditionary. """ existing_pks = self.apps().no_cache().values_list('pk', flat=True) if set(existing_pks) != set(new_order): raise ValueError('Not all apps included') for order, pk in enumerate(new_order): CollectionMembership.objects.get(collection=self, app_id=pk).update(order=order) index_webapps.delay(new_order) def has_curator(self, userprofile): """ Returns boolean indicating whether the passed user profile is a curator on this collection. ID comparison used instead of directly checking objects to ensure that RequestUser or UserProfile objects could be passed. """ return userprofile.id in self.curators.values_list('id', flat=True) def add_curator(self, userprofile): ret = self.curators.add(userprofile) Collection.objects.invalidate(*self.curators.all()) return ret def remove_curator(self, userprofile): ret = self.curators.remove(userprofile) Collection.objects.invalidate(*self.curators.all()) return ret
def extract_document(cls, pk=None, obj=None): """Extracts the ElasticSearch index document for this instance.""" from mkt.webapps.models import (AppFeatures, attach_devices, attach_prices, attach_tags, attach_translations, Geodata, Installed, RatingDescriptors, RatingInteractives, Webapp) if obj is None: obj = cls.get_model().objects.no_cache().get(pk=pk) # Attach everything we need to index apps. for transform in (attach_devices, attach_prices, attach_tags, attach_translations): transform([obj]) latest_version = obj.latest_version version = obj.current_version geodata = obj.geodata features = (version.features.to_dict() if version else AppFeatures().to_dict()) is_escalated = obj.escalationqueue_set.exists() try: status = latest_version.statuses[0][1] if latest_version else None except IndexError: status = None installed_ids = list(Installed.objects.filter(addon=obj) .values_list('id', flat=True)) attrs = ('app_slug', 'bayesian_rating', 'created', 'id', 'is_disabled', 'last_updated', 'modified', 'premium_type', 'status', 'type', 'uses_flash', 'weekly_downloads') d = dict(zip(attrs, attrgetter(*attrs)(obj))) d['boost'] = len(installed_ids) or 1 d['app_type'] = obj.app_type_id d['author'] = obj.developer_name d['banner_regions'] = geodata.banner_regions_slugs() d['category'] = obj.categories if obj.categories else [] if obj.is_published: d['collection'] = [{'id': cms.collection_id, 'order': cms.order} for cms in obj.collectionmembership_set.all()] else: d['collection'] = [] d['content_ratings'] = (obj.get_content_ratings_by_body(es=True) or None) try: d['content_descriptors'] = obj.rating_descriptors.to_keys() except RatingDescriptors.DoesNotExist: d['content_descriptors'] = [] d['current_version'] = version.version if version else None d['default_locale'] = obj.default_locale d['description'] = list( set(string for _, string in obj.translations[obj.description_id])) d['device'] = getattr(obj, 'device_ids', []) d['features'] = features d['has_public_stats'] = obj.public_stats d['icon_hash'] = obj.icon_hash try: d['interactive_elements'] = obj.rating_interactives.to_keys() except RatingInteractives.DoesNotExist: d['interactive_elements'] = [] d['is_escalated'] = is_escalated d['is_offline'] = getattr(obj, 'is_offline', False) if latest_version: d['latest_version'] = { 'status': status, 'is_privileged': latest_version.is_privileged, 'has_editor_comment': latest_version.has_editor_comment, 'has_info_request': latest_version.has_info_request, } else: d['latest_version'] = { 'status': None, 'is_privileged': None, 'has_editor_comment': None, 'has_info_request': None, } d['manifest_url'] = obj.get_manifest_url() d['package_path'] = obj.get_package_path() d['name'] = list( set(string for _, string in obj.translations[obj.name_id])) d['name_sort'] = unicode(obj.name).lower() d['owners'] = [au.user.id for au in obj.addonuser_set.filter(role=amo.AUTHOR_ROLE_OWNER)] d['popularity'] = len(installed_ids) d['previews'] = [{'filetype': p.filetype, 'modified': p.modified, 'id': p.id, 'sizes': p.sizes} for p in obj.previews.all()] try: p = obj.addonpremium.price d['price_tier'] = p.name except AddonPremium.DoesNotExist: d['price_tier'] = None d['ratings'] = { 'average': obj.average_rating, 'count': obj.total_reviews, } d['region_exclusions'] = obj.get_excluded_region_ids() d['reviewed'] = obj.versions.filter( deleted=False).aggregate(Min('reviewed')).get('reviewed__min') if version: d['supported_locales'] = filter( None, version.supported_locales.split(',')) else: d['supported_locales'] = [] d['tags'] = getattr(obj, 'tag_list', []) if obj.upsell and obj.upsell.premium.is_published(): upsell_obj = obj.upsell.premium d['upsell'] = { 'id': upsell_obj.id, 'app_slug': upsell_obj.app_slug, 'icon_url': upsell_obj.get_icon_url(128), # TODO: Store all localizations of upsell.name. 'name': unicode(upsell_obj.name), 'region_exclusions': upsell_obj.get_excluded_region_ids() } d['versions'] = [dict(version=v.version, resource_uri=reverse_version(v)) for v in obj.versions.all()] # Calculate weight. It's similar to popularity, except that we can # expose the number - it's relative to the max weekly downloads for # the whole database. max_downloads = float( Webapp.objects.aggregate(Max('weekly_downloads')).values()[0] or 0) if max_downloads: d['weight'] = math.ceil(d['weekly_downloads'] / max_downloads * 5) else: d['weight'] = 1 # Handle our localized fields. for field in ('description', 'homepage', 'name', 'support_email', 'support_url'): d['%s_translations' % field] = [ {'lang': to_language(lang), 'string': string} for lang, string in obj.translations[getattr(obj, '%s_id' % field)] if string] if version: amo.utils.attach_trans_dict(Version, [version]) d['release_notes_translations'] = [ {'lang': to_language(lang), 'string': string} for lang, string in version.translations[version.releasenotes_id]] else: d['release_notes_translations'] = None amo.utils.attach_trans_dict(Geodata, [geodata]) d['banner_message_translations'] = [ {'lang': to_language(lang), 'string': string} for lang, string in geodata.translations[geodata.banner_message_id]] for region in mkt.regions.ALL_REGION_IDS: d['popularity_%s' % region] = d['popularity'] # Bump the boost if the add-on is public. if obj.status == amo.STATUS_PUBLIC: d['boost'] = max(d['boost'], 1) * 4 # If the app is compatible with Firefox OS, push suggestion data in the # index - This will be used by RocketbarView API, which is specific to # Firefox OS. if DEVICE_GAIA.id in d['device'] and obj.is_published(): d['name_suggest'] = { 'input': d['name'], 'output': unicode(obj.id), # We only care about the payload. 'weight': d['boost'], 'payload': { 'default_locale': d['default_locale'], 'icon_hash': d['icon_hash'], 'id': d['id'], 'manifest_url': d['manifest_url'], 'modified': d['modified'], 'name_translations': d['name_translations'], 'slug': d['app_slug'], } } # Indices for each language. languages is a list of locales we want to # index with analyzer if the string's locale matches. for analyzer, languages in amo.SEARCH_ANALYZER_MAP.iteritems(): if (not settings.ES_USE_PLUGINS and analyzer in amo.SEARCH_ANALYZER_PLUGINS): continue d['name_' + analyzer] = list( set(string for locale, string in obj.translations[obj.name_id] if locale.lower() in languages)) d['description_' + analyzer] = list( set(string for locale, string in obj.translations[obj.description_id] if locale.lower() in languages)) return d