Exemple #1
0
 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
     }
Exemple #2
0
    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)
Exemple #3
0
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')
Exemple #4
0
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')
Exemple #5
0
 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)
Exemple #6
0
 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)
Exemple #7
0
 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
Exemple #8
0
 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)
Exemple #9
0
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
Exemple #10
0
    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)
Exemple #11
0
    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)
Exemple #12
0
 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,
     }
Exemple #13
0
 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}
Exemple #14
0
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
    }
Exemple #15
0
 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)
Exemple #16
0
 def trans_locale(self, locale):
     return to_language(settings.SHORTER_LANGUAGES.get(locale, locale))
Exemple #17
0
 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)
Exemple #18
0
 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
Exemple #19
0
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)
Exemple #20
0
    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
Exemple #21
0
 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
Exemple #22
0
 def check(a, b):
     eq_(to_language(a), b)
Exemple #23
0
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
Exemple #24
0
 def trans_locale(self, locale):
     return to_language(settings.SHORTER_LANGUAGES.get(locale, locale))
Exemple #25
0
 def check(a, b):
     eq_(to_language(a), b)
Exemple #26
0
    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