Exemple #1
0
class AppSerializer(serializers.ModelSerializer):
    app_type = serializers.ChoiceField(
        choices=mkt.ADDON_WEBAPP_TYPES_LOOKUP.items(), read_only=True)
    author = serializers.CharField(source='developer_name', read_only=True)
    categories = ListField(serializers.ChoiceField(choices=CATEGORY_CHOICES),
                           required=True)
    content_ratings = serializers.SerializerMethodField('get_content_ratings')
    created = serializers.DateField(read_only=True)
    current_version = serializers.CharField(source='current_version.version',
                                            read_only=True)
    default_locale = serializers.CharField(read_only=True)
    device_types = SemiSerializerMethodField('get_device_types')
    description = TranslationSerializerField(required=False)
    homepage = TranslationSerializerField(required=False)
    feature_compatibility = serializers.SerializerMethodField(
        'get_feature_compatibility')
    file_size = serializers.IntegerField(source='file_size', read_only=True)
    icons = serializers.SerializerMethodField('get_icons')
    id = serializers.IntegerField(source='pk', required=False)
    is_disabled = serializers.BooleanField(read_only=True, default=False)
    is_homescreen = serializers.SerializerMethodField('get_is_homescreen')
    is_offline = serializers.BooleanField(read_only=True)
    is_packaged = serializers.BooleanField(read_only=True)
    last_updated = serializers.DateField(read_only=True)
    manifest_url = serializers.CharField(source='get_manifest_url',
                                         read_only=True)
    modified = serializers.DateField(read_only=True)
    name = TranslationSerializerField(required=False)
    package_path = serializers.CharField(source='get_package_path',
                                         read_only=True)
    payment_account = serializers.SerializerMethodField('get_payment_account')
    payment_required = serializers.SerializerMethodField(
        'get_payment_required')
    premium_type = ReverseChoiceField(choices_dict=mkt.ADDON_PREMIUM_API,
                                      required=False)
    previews = PreviewSerializer(many=True,
                                 required=False,
                                 source='all_previews')
    price = SemiSerializerMethodField('get_price')
    price_locale = serializers.SerializerMethodField('get_price_locale')
    privacy_policy = LargeTextField(view_name='app-privacy-policy-detail',
                                    required=False)
    promo_imgs = serializers.SerializerMethodField('get_promo_imgs')
    public_stats = serializers.BooleanField(read_only=True)
    ratings = serializers.SerializerMethodField('get_ratings_aggregates')
    regions = RegionSerializer(read_only=True, source='get_regions', many=True)
    release_notes = TranslationSerializerField(
        read_only=True, source='current_version.releasenotes')
    resource_uri = serializers.HyperlinkedIdentityField(view_name='app-detail')
    slug = serializers.CharField(source='app_slug', required=False)
    status = serializers.IntegerField(read_only=True)
    support_email = TranslationSerializerField(required=False)
    support_url = TranslationSerializerField(required=False)
    supported_locales = serializers.SerializerMethodField(
        'get_supported_locales')
    tags = serializers.SerializerMethodField('get_tags')
    upsell = serializers.SerializerMethodField('get_upsell')
    upsold = serializers.HyperlinkedRelatedField(view_name='app-detail',
                                                 source='upsold.free',
                                                 required=False,
                                                 queryset=Webapp.objects.all())
    user = serializers.SerializerMethodField('get_user_info')
    versions = serializers.SerializerMethodField('get_versions')

    class Meta:
        model = Webapp
        fields = [
            'app_type', 'author', 'categories', 'content_ratings', 'created',
            'current_version', 'default_locale', 'description', 'device_types',
            'feature_compatibility', 'file_size', 'homepage', 'hosted_url',
            'icons', 'id', 'is_disabled', 'is_homescreen', 'is_offline',
            'is_packaged', 'last_updated', 'manifest_url', 'name',
            'package_path', 'payment_account', 'payment_required',
            'premium_type', 'previews', 'price', 'price_locale',
            'privacy_policy', 'promo_imgs', 'public_stats', 'release_notes',
            'ratings', 'regions', 'resource_uri', 'slug', 'status',
            'support_email', 'support_url', 'supported_locales', 'tags',
            'upsell', 'upsold', 'user', 'versions'
        ]

    def _get_region_id(self):
        request = self.context.get('request')
        REGION = getattr(request, 'REGION', None)
        return REGION.id if REGION else None

    def _get_region_slug(self):
        request = self.context.get('request')
        REGION = getattr(request, 'REGION', None)
        return REGION.slug if REGION else None

    def get_content_ratings(self, app):
        body = mkt.regions.REGION_TO_RATINGS_BODY().get(
            self._get_region_slug(), 'generic')

        return {
            'body':
            body,
            'rating':
            app.get_content_ratings_by_body().get(body, None),
            'descriptors': (app.rating_descriptors.to_keys_by_body(body)
                            if hasattr(app, 'rating_descriptors') else []),
            'descriptors_text': ([
                mkt.iarc_mappings.REVERSE_DESCS[key]
                for key in app.rating_descriptors.to_keys_by_body(body)
            ] if hasattr(app, 'rating_descriptors') else []),
            'interactives': (app.rating_interactives.to_keys() if hasattr(
                app, 'rating_interactives') else []),
            'interactives_text': ([
                mkt.iarc_mappings.REVERSE_INTERACTIVES[key]
                for key in app.rating_interactives.to_keys()
            ] if hasattr(app, 'rating_interactives') else []),
        }

    def get_icons(self, app):
        return dict([(icon_size, app.get_icon_url(icon_size))
                     for icon_size in mkt.CONTENT_ICON_SIZES])

    def get_feature_compatibility(self, app):
        request = self.context['request']
        if not hasattr(request, 'feature_profile'):
            load_feature_profile(request)
        if request.feature_profile is None:
            # No profile information sent, we can't return compatibility,
            # return null.
            return None
        app_features = app.current_version.features.to_list()
        return request.feature_profile.has_features(app_features)

    def get_payment_account(self, app):
        # Avoid a query for payment_account if the app is not premium.
        if not app.is_premium():
            return None

        try:
            # This is a soon to be deprecated API property that only
            # returns the Bango account for historic compatibility.
            app_acct = app.payment_account(PROVIDER_BANGO)
            return reverse('payment-account-detail',
                           args=[app_acct.payment_account.pk])
        except app.PayAccountDoesNotExist:
            return None

    def get_payment_required(self, app):
        if app.has_premium():
            tier = app.get_tier()
            return bool(tier and tier.price)
        return False

    def get_price(self, app):
        if app.has_premium():
            return app.get_price(region=self._get_region_id())
        return None

    def get_price_locale(self, app):
        if app.has_premium():
            return app.get_price_locale(region=self._get_region_id())
        return None

    def get_promo_imgs(self, obj):
        return dict([(promo_img_size, obj.get_promo_img_url(promo_img_size))
                     for promo_img_size in mkt.PROMO_IMG_SIZES])

    def get_ratings_aggregates(self, app):
        return {'average': app.average_rating, 'count': app.total_reviews}

    def get_supported_locales(self, app):
        locs = getattr(app.current_version, 'supported_locales', '')
        if locs:
            return locs.split(',') if isinstance(locs, basestring) else locs
        else:
            return []

    def get_tags(self, app):
        if not hasattr(app, 'tags_list'):
            attach_tags([app])
        return getattr(app, 'tags_list', [])

    def get_upsell(self, app):
        upsell = False
        if app.upsell:
            upsell = app.upsell.premium
        # Only return the upsell app if it's public and we are not in an
        # excluded region.
        if (upsell and upsell.is_public() and self._get_region_id()
                not in upsell.get_excluded_region_ids()):
            return {
                'id': upsell.id,
                'app_slug': upsell.app_slug,
                'icon_url': upsell.get_icon_url(128),
                'name': unicode(upsell.name),
                'resource_uri': reverse('app-detail', kwargs={'pk': upsell.pk})
            }
        else:
            return False

    def get_user_info(self, app):
        request = self.context.get('request')
        if request and request.user.is_authenticated():
            user = request.user
            return {
                'developed':
                app.addonuser_set.filter(user=user,
                                         role=mkt.AUTHOR_ROLE_OWNER).exists(),
                'installed':
                app.has_installed(user),
                'purchased':
                app.pk in user.purchase_ids(),
            }

    def get_is_homescreen(self, app):
        return app.is_homescreen()

    def get_versions(self, app):
        # Disable transforms, we only need two fields: version and pk.
        # Unfortunately, cache-machine gets in the way so we can't use .only()
        # (.no_transforms() is ignored, defeating the purpose), and we can't
        # use .values() / .values_list() because those aren't cached :(
        return dict((v.version, reverse('version-detail', kwargs={'pk': v.pk}))
                    for v in app.versions.all().no_transforms())

    def validate_categories(self, attrs, source):
        if not attrs.get('categories'):
            raise serializers.ValidationError('This field is required.')
        set_categories = set(attrs[source])
        total = len(set_categories)
        max_cat = mkt.MAX_CATEGORIES

        if total > max_cat:
            # L10n: {0} is the number of categories.
            raise serializers.ValidationError(
                ngettext('You can have only {0} category.',
                         'You can have only {0} categories.',
                         max_cat).format(max_cat))

        return attrs

    def get_device_types(self, app):
        with no_translation():
            return [n.api_name for n in app.device_types]

    def save_device_types(self, obj, new_types):
        new_types = [mkt.DEVICE_LOOKUP[d].id for d in new_types]
        old_types = [x.id for x in obj.device_types]

        added_devices = set(new_types) - set(old_types)
        removed_devices = set(old_types) - set(new_types)

        for d in added_devices:
            obj.addondevicetype_set.create(device_type=d)
        for d in removed_devices:
            obj.addondevicetype_set.filter(device_type=d).delete()

        # Send app to re-review queue if public and new devices are added.
        if added_devices and obj.status in mkt.WEBAPPS_APPROVED_STATUSES:
            mark_for_rereview(obj, added_devices, removed_devices)

    def save_upsold(self, obj, upsold):
        current_upsell = obj.upsold
        if upsold and upsold != obj.upsold.free:
            if not current_upsell:
                log.debug('[1@%s] Creating app upsell' % obj.pk)
                current_upsell = AddonUpsell(premium=obj)
            current_upsell.free = upsold
            current_upsell.save()

        elif current_upsell:
            # We're deleting the upsell.
            log.debug('[1@%s] Deleting the app upsell' % obj.pk)
            current_upsell.delete()

    def save_price(self, obj, price):
        premium = obj.premium
        if not premium:
            premium = AddonPremium()
            premium.addon = obj
        premium.price = Price.objects.active().get(price=price)
        premium.save()

    def validate_device_types(self, attrs, source):
        if attrs.get('device_types') is None:
            raise serializers.ValidationError('This field is required.')
        for v in attrs['device_types']:
            if v not in mkt.DEVICE_LOOKUP.keys():
                raise serializers.ValidationError(
                    str(v) + ' is not one of the available choices.')
        return attrs

    def validate_price(self, attrs, source):
        if attrs.get('premium_type',
                     None) not in (mkt.ADDON_FREE, mkt.ADDON_FREE_INAPP):
            valid_prices = Price.objects.exclude(price='0.00').values_list(
                'price', flat=True)
            price = attrs.get('price')
            if not (price and Decimal(price) in valid_prices):
                raise serializers.ValidationError(
                    'Premium app specified without a valid price. Price can be'
                    ' one of %s.' % (', '.join('"%s"' % str(p)
                                               for p in valid_prices), ))
        return attrs

    def restore_object(self, attrs, instance=None):
        # restore_object creates or updates a model instance, during
        # input validation.
        extras = []
        # Upsell bits are handled here because we need to remove it
        # from the attrs dict before deserializing.
        upsold = attrs.pop('upsold.free', None)
        if upsold is not None:
            extras.append((self.save_upsold, upsold))
        price = attrs.pop('price', None)
        if price is not None:
            extras.append((self.save_price, price))
        device_types = attrs['device_types']
        if device_types:
            extras.append((self.save_device_types, device_types))
            del attrs['device_types']
        instance = super(AppSerializer, self).restore_object(attrs,
                                                             instance=instance)
        for f, v in extras:
            f(instance, v)
        return instance
class BaseAppSerializer(serializers.ModelSerializer):
    # REST Framework 3.x doesn't allow meta.fields to omit fields declared in
    # the class body, but it does allow omitting ones in superclasses. All the
    # serializers are subsets of the full field collection, hence this
    # superclass.
    app_type = serializers.ChoiceField(
        choices=mkt.ADDON_WEBAPP_TYPES_LOOKUP.items(), read_only=True)
    author = serializers.CharField(source='developer_name', read_only=True)
    categories = serializers.ListField(child=serializers.ChoiceField(
        choices=CATEGORY_CHOICES, read_only=False),
                                       read_only=False,
                                       required=True)
    content_ratings = serializers.SerializerMethodField()
    created = serializers.DateTimeField(read_only=True, format=None)
    current_version = serializers.CharField(source='current_version.version',
                                            read_only=True)
    default_locale = serializers.CharField(read_only=True)
    device_types = SemiSerializerMethodField()
    description = TranslationSerializerField(required=False)
    homepage = TranslationSerializerField(required=False)
    feature_compatibility = serializers.SerializerMethodField()
    file_size = serializers.IntegerField(read_only=True)
    icons = serializers.SerializerMethodField()
    id = serializers.IntegerField(source='pk', required=False)
    is_disabled = serializers.BooleanField(read_only=True)
    is_homescreen = serializers.SerializerMethodField()
    is_offline = serializers.BooleanField(read_only=True)
    is_packaged = serializers.BooleanField(read_only=True)
    last_updated = serializers.DateTimeField(read_only=True, format=None)
    manifest_url = serializers.CharField(source='get_manifest_url',
                                         read_only=True)
    modified = serializers.DateTimeField(read_only=True, format=None)
    name = TranslationSerializerField(required=False)
    package_path = serializers.CharField(source='get_package_path',
                                         read_only=True)
    payment_account = serializers.SerializerMethodField()
    payment_required = serializers.SerializerMethodField()
    premium_type = ReverseChoiceField(choices_dict=mkt.ADDON_PREMIUM_API,
                                      required=False)
    previews = PreviewSerializer(many=True,
                                 required=False,
                                 source='all_previews')
    price = SemiSerializerMethodField(source='*', required=False)
    price_locale = serializers.SerializerMethodField()
    privacy_policy = LargeTextField(view_name='app-privacy-policy-detail',
                                    queryset=Webapp.objects,
                                    required=False)
    promo_imgs = serializers.SerializerMethodField()
    public_stats = serializers.BooleanField(read_only=True)
    ratings = serializers.SerializerMethodField('get_ratings_aggregates')
    regions = RegionSerializer(read_only=True, source='get_regions', many=True)
    release_notes = TranslationSerializerField(
        read_only=True, source='current_version.releasenotes')
    resource_uri = serializers.HyperlinkedIdentityField(view_name='app-detail')
    slug = serializers.CharField(source='app_slug', required=False)
    status = serializers.IntegerField(read_only=True)
    support_email = TranslationSerializerField(required=False)
    support_url = TranslationSerializerField(required=False)
    supported_locales = serializers.SerializerMethodField()
    tags = serializers.SerializerMethodField()
    upsell = serializers.SerializerMethodField()
    upsold = serializers.HyperlinkedRelatedField(view_name='app-detail',
                                                 source='upsold.free',
                                                 required=False,
                                                 queryset=Webapp.objects.all())
    user = serializers.SerializerMethodField('get_user_info')
    versions = serializers.SerializerMethodField()