Exemple #1
0
class UserAbuseReportSerializer(serializers.ModelSerializer):
    reporter = BaseUserSerializer(read_only=True)
    user = BaseUserSerializer()

    class Meta:
        model = AbuseReport
        fields = ('reporter', 'user', 'message')
Exemple #2
0
class CollectionSerializer(serializers.ModelSerializer):
    name = TranslationSerializerField()
    description = TranslationSerializerField(required=False)
    url = serializers.SerializerMethodField()
    author = BaseUserSerializer(default=serializers.CurrentUserDefault())
    public = serializers.BooleanField(source='listed', default=True)
    uuid = serializers.UUIDField(format='hex', required=False)

    class Meta:
        model = Collection
        fields = ('id', 'uuid', 'url', 'addon_count', 'author', 'description',
                  'modified', 'name', 'slug', 'public', 'default_locale')
        writeable_fields = ('description', 'name', 'slug', 'public',
                            'default_locale')
        read_only_fields = tuple(set(fields) - set(writeable_fields))
        validators = [
            UniqueTogetherValidator(
                queryset=Collection.objects.all(),
                message=_(u'This custom URL is already in use by another one '
                          u'of your collections.'),
                fields=('slug', 'author')),
            CollectionAkismetSpamValidator(fields=('name', 'description'))
        ]

    def get_url(self, obj):
        return obj.get_abs_url()

    def validate_name(self, value):
        # if we have a localised dict of values validate them all.
        if isinstance(value, dict):
            return {
                locale: self.validate_name(sub_value)
                for locale, sub_value in six.iteritems(value)
            }
        if value.strip() == u'':
            raise serializers.ValidationError(
                ugettext(u'Name cannot be empty.'))
        if DeniedName.blocked(value):
            raise serializers.ValidationError(
                ugettext(u'This name cannot be used.'))
        return value

    def validate_description(self, value):
        if has_links(clean_nl(six.text_type(value))):
            # There's some links, we don't want them.
            raise serializers.ValidationError(
                ugettext(u'No links are allowed.'))
        return value

    def validate_slug(self, value):
        slug_validator(value,
                       lower=False,
                       message=ugettext(
                           u'The custom URL must consist of letters, '
                           u'numbers, underscores or hyphens.'))
        if DeniedName.blocked(value):
            raise serializers.ValidationError(
                ugettext(u'This custom URL cannot be used.'))

        return value
Exemple #3
0
class ActivityLogSerializer(serializers.ModelSerializer):
    action = serializers.SerializerMethodField()
    action_label = serializers.SerializerMethodField()
    comments = serializers.SerializerMethodField()
    date = serializers.DateTimeField(source='created')
    user = BaseUserSerializer()
    highlight = serializers.SerializerMethodField()

    class Meta:
        model = ActivityLog
        fields = ('id', 'action', 'action_label', 'comments', 'user', 'date',
                  'highlight')

    def __init__(self, *args, **kwargs):
        super(ActivityLogSerializer, self).__init__(*args, **kwargs)
        self.to_highlight = kwargs.get('context', []).get('to_highlight', [])

    def get_comments(self, obj):
        comments = obj.details['comments'] if obj.details else ''
        return getattr(obj.log(), 'sanitize', comments)

    def get_action_label(self, obj):
        log = obj.log()
        default = ugettext(u'Review note')
        return default if not hasattr(log, 'short') else log.short

    def get_action(self, obj):
        return self.get_action_label(obj).replace(' ', '-').lower()

    def get_highlight(self, obj):
        return obj in self.to_highlight
Exemple #4
0
class DraftCommentSerializer(serializers.ModelSerializer):
    user = SplitField(
        serializers.PrimaryKeyRelatedField(queryset=UserProfile.objects.all()),
        BaseUserSerializer())
    version_id = serializers.PrimaryKeyRelatedField(
        queryset=Version.unfiltered.all(), source='version')
    canned_response = SplitField(
        serializers.PrimaryKeyRelatedField(
            queryset=CannedResponse.objects.all(),
            required=False),
        CannedResponseSerializer(),
        allow_null=True,
        required=False)

    class Meta:
        model = DraftComment
        fields = (
            'id', 'filename', 'lineno', 'comment',
            'version_id', 'user', 'canned_response'
        )

    def get_or_default(self, key, data, default=''):
        """Return the value of ``key`` in ``data``

        If that key is not present then return the value of ``key`` from
        ``self.instance`, otherwise return the ``default``.

        This method is a helper to simplify validation for partial updates.
        """
        retval = data.get(key)

        if retval is None and self.instance is not None:
            retval = getattr(self.instance, key)

        return retval or default

    def validate(self, data):
        canned_response = self.get_or_default('canned_response', data)
        comment = self.get_or_default('comment', data)

        if comment and canned_response:
            raise serializers.ValidationError(
                {'comment': ugettext(
                    'You can\'t submit a comment if `canned_response` is '
                    'defined.')})

        if not canned_response and not comment:
            raise serializers.ValidationError(
                {'comment': ugettext(
                    'You can\'t submit an empty comment.')})

        lineno = self.get_or_default('lineno', data)
        filename = self.get_or_default('filename', data)

        if lineno and not filename:
            raise serializers.ValidationError(
                {'comment': ugettext(
                    'You can\'t submit a line number without associating '
                    'it to a filename.')})
        return data
class DraftCommentSerializer(serializers.ModelSerializer):
    user = SplitField(
        serializers.PrimaryKeyRelatedField(queryset=UserProfile.objects.all()),
        BaseUserSerializer())
    version = SplitField(
        serializers.PrimaryKeyRelatedField(queryset=Version.unfiltered.all()),
        VersionSerializer())
    canned_response = SplitField(
        serializers.PrimaryKeyRelatedField(
            queryset=CannedResponse.objects.all(), required=False),
        CannedResponseSerializer())

    class Meta:
        model = DraftComment
        fields = ('id', 'filename', 'lineno', 'comment', 'version', 'user',
                  'canned_response')

    def validate(self, data):
        if data.get('comment') and data.get('canned_response'):
            raise serializers.ValidationError({
                'comment':
                ugettext('You can\'t submit a comment if `canned_response` is '
                         'defined.')
            })
        return data
class DraftCommentSerializer(serializers.ModelSerializer):
    user = SplitField(
        serializers.PrimaryKeyRelatedField(queryset=UserProfile.objects.all()),
        BaseUserSerializer())
    version = SplitField(
        serializers.PrimaryKeyRelatedField(queryset=Version.unfiltered.all()),
        AddonBrowseVersionSerializer())
    canned_response = SplitField(
        serializers.PrimaryKeyRelatedField(
            queryset=CannedResponse.objects.all(), required=False),
        CannedResponseSerializer())

    class Meta:
        model = DraftComment
        fields = ('id', 'filename', 'lineno', 'comment', 'version', 'user',
                  'canned_response')

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # Set the instance for `AddonBrowseVersionSerializer` which requires
        # on `instance` being set correctly.
        self.fields['version'].output.instance = self.context['version']

    def validate(self, data):
        if data.get('comment') and data.get('canned_response'):
            raise serializers.ValidationError({
                'comment':
                ugettext('You can\'t submit a comment if `canned_response` is '
                         'defined.')
            })
        return data
class UserAbuseReportSerializer(BaseAbuseReportSerializer):
    user = BaseUserSerializer(required=False)  # We validate it ourselves.
    # Unlike add-on reports, for user reports we don't have a 'reason' field so
    # the message is always required and can't be blank.
    message = serializers.CharField(
        required=True, allow_blank=False, max_length=10000)

    class Meta:
        model = AbuseReport
        fields = BaseAbuseReportSerializer.Meta.fields + (
            'user',
        )

    def to_internal_value(self, data):
        view = self.context.get('view')
        self.validate_target(data, 'user')
        output = {
            'user': view.get_user_object()
        }
        # Pop 'user' before passing it to super(), we already have the
        # output value and did the validation above.
        data.pop('user')
        output.update(
            super(UserAbuseReportSerializer, self).to_internal_value(data)
        )
        return output
 def test_user_report(self):
     user = user_factory()
     report = AbuseReport(user=user, message='bad stuff')
     serialized = self.serialize(report)
     serialized_user = BaseUserSerializer(user).data
     assert serialized == {
         'reporter': None,
         'user': serialized_user,
         'message': 'bad stuff',
     }
Exemple #9
0
class RatingFlagSerializer(serializers.ModelSerializer):
    flag = serializers.CharField()
    note = serializers.CharField(allow_null=True, required=False)
    rating = RatingSerializer(read_only=True)
    user = BaseUserSerializer(read_only=True)

    class Meta:
        model = RatingFlag
        fields = ('flag', 'note', 'rating', 'user')

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.request = kwargs.get('context', {}).get('request')

    def validate_flag(self, flag):
        flags = dict(RatingFlag.FLAGS)
        if flag not in flags:
            raise serializers.ValidationError(
                gettext(
                    'Invalid flag [%s] - must be one of [%s]' % (flag, ','.join(flags))
                )
            )
        return flag

    def validate(self, data):
        data['rating'] = self.context['view'].rating_object
        data['user'] = self.request.user
        if not data['rating'].body:
            raise serializers.ValidationError(
                gettext("This rating can't be flagged because it has no review text.")
            )

        if 'note' in data and data['note'].strip():
            data['flag'] = RatingFlag.OTHER
        elif data.get('flag') == RatingFlag.OTHER:
            raise serializers.ValidationError(
                {
                    'note': gettext(
                        'A short explanation must be provided when selecting '
                        '"Other" as a flag reason.'
                    )
                }
            )
        return data

    def save(self, **kwargs):
        instance = super().save(**kwargs)
        instance.rating.update(editorreview=True)
        return instance
class DraftCommentSerializer(serializers.ModelSerializer):
    user = SplitField(
        serializers.PrimaryKeyRelatedField(queryset=UserProfile.objects.all()),
        BaseUserSerializer())
    version = SplitField(
        serializers.PrimaryKeyRelatedField(
            queryset=Version.unfiltered.all()),
        VersionSerializer())

    class Meta:
        model = DraftComment
        fields = (
            'id', 'filename', 'lineno', 'comment',
            'version', 'user'
        )
Exemple #11
0
class UserAbuseReportSerializer(BaseAbuseReportSerializer):
    user = BaseUserSerializer(required=False)  # We validate it ourselves.

    class Meta:
        model = AbuseReport
        fields = BaseAbuseReportSerializer.Meta.fields + ('user', )

    def to_internal_value(self, data):
        view = self.context.get('view')
        self.validate_target(data, 'user')
        output = {'user': view.get_user_object()}
        # Pop 'user' before passing it to super(), we already have the
        # output value and did the validation above.
        data.pop('user')
        output.update(
            super(UserAbuseReportSerializer, self).to_internal_value(data))
        return output
Exemple #12
0
class AddonAbuseReportSerializer(serializers.ModelSerializer):
    addon = serializers.SerializerMethodField()
    reporter = BaseUserSerializer(read_only=True)

    class Meta:
        model = AbuseReport
        fields = ('reporter', 'addon', 'message')

    def get_addon(self, obj):
        addon = obj.addon
        if not addon and not obj.guid:
            return None
        return {
            'guid': addon.guid if addon else obj.guid,
            'id': addon.id if addon else None,
            'slug': addon.slug if addon else None,
        }
Exemple #13
0
class BaseAbuseReportSerializer(serializers.ModelSerializer):
    reporter = BaseUserSerializer(read_only=True)

    class Meta:
        model = AbuseReport
        fields = ('message', 'reporter')

    def validate_target(self, data, target_name):
        if target_name not in data:
            msg = serializers.Field.default_error_messages['required']
            raise serializers.ValidationError({target_name: [msg]})

    def to_internal_value(self, data):
        output = super(BaseAbuseReportSerializer, self).to_internal_value(data)
        request = self.context['request']
        output['ip_address'] = request.META.get('REMOTE_ADDR')
        if request.user.is_authenticated:
            output['reporter'] = request.user
        return output
class BaseRatingSerializer(serializers.ModelSerializer):
    addon = serializers.SerializerMethodField()
    body = serializers.CharField(allow_null=True, required=False)
    is_latest = serializers.BooleanField(read_only=True)
    previous_count = serializers.IntegerField(read_only=True)
    user = BaseUserSerializer(read_only=True)

    class Meta:
        model = Rating
        fields = ('id', 'addon', 'body', 'created', 'is_latest',
                  'previous_count', 'user')

    def __init__(self, *args, **kwargs):
        super(BaseRatingSerializer, self).__init__(*args, **kwargs)
        self.request = kwargs.get('context', {}).get('request')

    def get_addon(self, obj):
        # We only return the addon id and slug for convenience, so just return
        # them directly to avoid instantiating a full serializer. Also avoid
        # database queries if possible by re-using the addon object from the
        # view if there is one.
        addon = self.context['view'].get_addon_object() or obj.addon

        return {'id': addon.id, 'slug': addon.slug}

    def validate(self, data):
        data = super(BaseRatingSerializer, self).validate(data)
        request = self.context['request']

        data['user_responsible'] = request.user

        # There are a few fields that need to be set at creation time and never
        # modified afterwards:
        if not self.partial:
            # Because we want to avoid extra queries, addon is a
            # SerializerMethodField, which means it needs to be validated
            # manually. Fortunately the view does most of the work for us.
            data['addon'] = self.context['view'].get_addon_object()
            if data['addon'] is None:
                raise serializers.ValidationError(
                    {'addon': ugettext('This field is required.')})

            # Get the user from the request, don't allow clients to pick one
            # themselves.
            data['user'] = request.user

            # Also include the user ip adress.
            data['ip_address'] = request.META.get('REMOTE_ADDR', '')
        else:
            # When editing, you can't change the add-on.
            if self.context['request'].data.get('addon'):
                raise serializers.ValidationError({
                    'addon':
                    ugettext(u'You can\'t change the add-on of a review once'
                             u' it has been created.')
                })

        # Clean up body and automatically flag the review if an URL was in it.
        body = data.get('body', '')
        if body:
            if '<br>' in body:
                data['body'] = re.sub('<br>', '\n', body)
            # Unquote the body when searching for links, in case someone tries
            # 'example%2ecom'.
            if RatingForm.link_pattern.search(unquote(body)) is not None:
                data['flag'] = True
                data['editorreview'] = True

        return data

    def to_representation(self, instance):
        out = super(BaseRatingSerializer, self).to_representation(instance)
        if self.request and is_gate_active(self.request, 'ratings-title-shim'):
            out['title'] = None
        return out
class ESAddonSerializer(BaseESSerializer, AddonSerializer):
    # Override various fields for related objects which we don't want to expose
    # data the same way than the regular serializer does (usually because we
    # some of the data is not indexed in ES).
    authors = BaseUserSerializer(many=True, source='listed_authors')
    current_beta_version = SimpleESVersionSerializer()
    current_version = SimpleESVersionSerializer()
    previews = ESPreviewSerializer(many=True, source='all_previews')

    datetime_fields = ('created', 'last_updated', 'modified')
    translated_fields = ('name', 'description', 'developer_comments',
                         'homepage', 'summary', 'support_email', 'support_url')

    def fake_file_object(self, obj, data):
        file_ = File(
            id=data['id'], created=self.handle_date(data['created']),
            hash=data['hash'], filename=data['filename'],
            is_webextension=data.get('is_webextension'),
            is_mozilla_signed_extension=data.get(
                'is_mozilla_signed_extension'),
            is_restart_required=data.get('is_restart_required', False),
            platform=data['platform'], size=data['size'],
            status=data['status'],
            strict_compatibility=data.get('strict_compatibility', False),
            version=obj)
        file_.webext_permissions_list = data.get('webext_permissions_list', [])
        return file_

    def fake_version_object(self, obj, data, channel):
        if data:
            version = Version(
                addon=obj, id=data['id'],
                reviewed=self.handle_date(data['reviewed']),
                version=data['version'], channel=channel)
            version.all_files = [
                self.fake_file_object(version, file_data)
                for file_data in data.get('files', [])
            ]

            # In ES we store integers for the appversion info, we need to
            # convert it back to strings.
            compatible_apps = {}
            for app_id, compat_dict in data.get('compatible_apps', {}).items():
                app_name = APPS_ALL[int(app_id)]
                compatible_apps[app_name] = ApplicationsVersions(
                    min=AppVersion(version=compat_dict.get('min_human', '')),
                    max=AppVersion(version=compat_dict.get('max_human', '')))
            version._compatible_apps = compatible_apps
        else:
            version = None
        return version

    def fake_object(self, data):
        """Create a fake instance of Addon and related models from ES data."""
        obj = Addon(id=data['id'], slug=data['slug'])

        # Attach base attributes that have the same name/format in ES and in
        # the model.
        self._attach_fields(
            obj, data, (
                'average_daily_users',
                'bayesian_rating',
                'contributions',
                'created',
                'default_locale',
                'guid',
                'has_eula',
                'has_privacy_policy',
                'hotness',
                'icon_hash',
                'icon_type',
                'is_experimental',
                'last_updated',
                'modified',
                'public_stats',
                'requires_payment',
                'slug',
                'status',
                'type',
                'view_source',
                'weekly_downloads'
            )
        )

        # Attach attributes that do not have the same name/format in ES.
        obj.tag_list = data.get('tags', [])
        obj.all_categories = [
            CATEGORIES_BY_ID[cat_id] for cat_id in data.get('category', [])]

        # Not entirely accurate, but enough in the context of the search API.
        obj.disabled_by_user = data.get('is_disabled', False)

        # Attach translations (they require special treatment).
        self._attach_translations(obj, data, self.translated_fields)

        # Attach related models (also faking them). `current_version` is a
        # property we can't write to, so we use the underlying field which
        # begins with an underscore. `current_beta_version` and
        # `latest_unlisted_version` are writeable cached_property so we can
        # directly write to them.
        obj.current_beta_version = self.fake_version_object(
            obj, data.get('current_beta_version'), amo.RELEASE_CHANNEL_LISTED)
        obj._current_version = self.fake_version_object(
            obj, data.get('current_version'), amo.RELEASE_CHANNEL_LISTED)
        obj.latest_unlisted_version = self.fake_version_object(
            obj, data.get('latest_unlisted_version'),
            amo.RELEASE_CHANNEL_UNLISTED)

        data_authors = data.get('listed_authors', [])
        obj.listed_authors = [
            UserProfile(
                id=data_author['id'], display_name=data_author['name'],
                username=data_author['username'],
                is_public=data_author.get('is_public', False))
            for data_author in data_authors
        ]

        # We set obj.all_previews to the raw preview data because
        # ESPreviewSerializer will handle creating the fake Preview object
        # for us when its to_representation() method is called.
        obj.all_previews = data.get('previews', [])

        ratings = data.get('ratings', {})
        obj.average_rating = ratings.get('average')
        obj.total_ratings = ratings.get('count')
        obj.text_ratings_count = ratings.get('text_count')

        obj._is_featured = data.get('is_featured', False)

        if data['type'] == amo.ADDON_PERSONA:
            persona_data = data.get('persona')
            if persona_data:
                obj.persona = Persona(
                    addon=obj,
                    accentcolor=persona_data['accentcolor'],
                    display_username=persona_data['author'],
                    header=persona_data['header'],
                    footer=persona_data['footer'],
                    # "New" Persona do not have a persona_id, it's a relic from
                    # old ones.
                    persona_id=0 if persona_data['is_new'] else 42,
                    textcolor=persona_data['textcolor'],
                    popularity=data.get('average_daily_users'),
                )
            else:
                # Sadly, https://code.djangoproject.com/ticket/14368 prevents
                # us from setting obj.persona = None. This is fixed in
                # Django 1.9, but in the meantime, work around it by creating
                # a Persona instance with a custom attribute indicating that
                # it should not be used.
                obj.persona = Persona()
                obj.persona._broken = True

        return obj
Exemple #16
0
class BaseRatingSerializer(serializers.ModelSerializer):
    addon = RatingAddonSerializer(read_only=True)
    body = serializers.CharField(allow_null=True, required=False)
    is_deleted = serializers.BooleanField(read_only=True, source='deleted')
    is_developer_reply = serializers.SerializerMethodField()
    is_latest = serializers.BooleanField(read_only=True)
    previous_count = serializers.IntegerField(read_only=True)
    user = BaseUserSerializer(read_only=True)
    flags = serializers.SerializerMethodField()

    class Meta:
        model = Rating
        fields = (
            'id',
            'addon',
            'body',
            'created',
            'flags',
            'is_deleted',
            'is_developer_reply',
            'is_latest',
            'previous_count',
            'user',
        )

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.request = kwargs.get('context', {}).get('request')

    def get_is_developer_reply(self, obj):
        return obj.reply_to_id is not None

    def validate(self, data):
        data = super().validate(data)
        request = self.context['request']

        data['user_responsible'] = request.user

        # There are a few fields that need to be set at creation time and never
        # modified afterwards:
        if not self.partial:
            # Because we want to avoid extra queries, addon is a
            # SerializerMethodField, which means it needs to be validated
            # manually. Fortunately the view does most of the work for us.
            data['addon'] = self.context['view'].get_addon_object()
            if data['addon'] is None:
                raise serializers.ValidationError(
                    {'addon': gettext('This field is required.')})

            # Get the user from the request, don't allow clients to pick one
            # themselves.
            data['user'] = request.user

            # Also include the user ip adress.
            data['ip_address'] = request.META.get('REMOTE_ADDR', '')
        else:
            # When editing, you can't change the add-on.
            if self.context['request'].data.get('addon'):
                raise serializers.ValidationError({
                    'addon':
                    gettext("You can't change the add-on of a review once"
                            ' it has been created.')
                })

        # Clean up body and automatically flag the review if an URL was in it.
        body = data.get('body', '')
        if body:
            if '<br>' in body:
                data['body'] = re.sub('<br>', '\n', body)
            # Unquote the body when searching for links, in case someone tries
            # 'example%2ecom'.
            if link_pattern.search(unquote(body)) is not None:
                data['flag'] = True
                data['editorreview'] = True

        return data

    def get_flags(self, obj):
        if self.context['view'].should_include_flags():
            # should be maximum one RatingFlag per rating+user anyway.
            rating_flags = obj.ratingflag_set.filter(
                user=self.context['request'].user)
            return [{
                'flag': flag.flag,
                'note': flag.note or None
            } for flag in rating_flags]
        return None

    def to_representation(self, instance):
        out = super().to_representation(instance)
        if self.request and is_gate_active(self.request, 'ratings-title-shim'):
            out['title'] = None
        if not self.context['view'].should_include_flags():
            out.pop('flags', None)
        return out
class ESAddonSerializer(BaseESSerializer, AddonSerializer):
    # Override various fields for related objects which we don't want to expose
    # data the same way than the regular serializer does (usually because we
    # some of the data is not indexed in ES).
    authors = BaseUserSerializer(many=True, source='listed_authors')
    current_version = ESCurrentVersionSerializer()
    previews = ESPreviewSerializer(many=True, source='current_previews')
    _score = serializers.SerializerMethodField()

    datetime_fields = ('created', 'last_updated', 'modified')
    translated_fields = ('name', 'description', 'developer_comments',
                         'homepage', 'summary', 'support_email', 'support_url')

    class Meta:
        model = Addon
        fields = AddonSerializer.Meta.fields + ('_score', )

    def fake_preview_object(self, obj, data, model_class=Preview):
        # This is what ESPreviewSerializer.fake_object() would do, but we do
        # it here and make that fake_object() method a no-op in order to have
        # access to the right model_class to use - VersionPreview for static
        # themes, Preview for the rest.
        preview = model_class(id=data['id'], sizes=data.get('sizes', {}))
        preview.addon = obj
        preview.version = obj.current_version
        preview_serializer = self.fields['previews'].child
        # Attach base attributes that have the same name/format in ES and in
        # the model.
        preview_serializer._attach_fields(preview, data, ('modified', ))
        # Attach translations.
        preview_serializer._attach_translations(
            preview, data, preview_serializer.translated_fields)
        return preview

    def fake_file_object(self, obj, data):
        file_ = File(id=data['id'],
                     created=self.handle_date(data['created']),
                     hash=data['hash'],
                     filename=data['filename'],
                     is_webextension=data.get('is_webextension'),
                     is_mozilla_signed_extension=data.get(
                         'is_mozilla_signed_extension'),
                     is_restart_required=data.get('is_restart_required',
                                                  False),
                     platform=data['platform'],
                     size=data['size'],
                     status=data['status'],
                     strict_compatibility=data.get('strict_compatibility',
                                                   False),
                     version=obj)
        file_.webext_permissions_list = data.get('webext_permissions_list', [])
        return file_

    def fake_version_object(self, obj, data, channel):
        if data:
            version = Version(addon=obj,
                              id=data['id'],
                              reviewed=self.handle_date(data['reviewed']),
                              version=data['version'],
                              channel=channel)
            version.all_files = [
                self.fake_file_object(version, file_data)
                for file_data in data.get('files', [])
            ]

            # In ES we store integers for the appversion info, we need to
            # convert it back to strings.
            compatible_apps = {}
            for app_id, compat_dict in data.get('compatible_apps', {}).items():
                app_name = APPS_ALL[int(app_id)]
                compatible_apps[app_name] = ApplicationsVersions(
                    min=AppVersion(version=compat_dict.get('min_human', '')),
                    max=AppVersion(version=compat_dict.get('max_human', '')))
            version._compatible_apps = compatible_apps
            version_serializer = self.fields['current_version']
            version_serializer._attach_translations(
                version, data, version_serializer.translated_fields)
            if 'license' in data:
                license_serializer = version_serializer.fields['license']
                version.license = License(id=data['license']['id'])
                license_serializer._attach_fields(version.license,
                                                  data['license'],
                                                  ('builtin', 'url'))
                # Can't use license_serializer._attach_translations() directly
                # because 'name' is a SerializerMethodField, not an
                # ESTranslatedField.
                license_serializer.db_name.attach_translations(
                    version.license, data['license'], 'name')
            else:
                version.license = None
        else:
            version = None
        return version

    def fake_object(self, data):
        """Create a fake instance of Addon and related models from ES data."""
        obj = Addon(id=data['id'], slug=data['slug'])

        # Attach base attributes that have the same name/format in ES and in
        # the model.
        self._attach_fields(
            obj, data,
            ('average_daily_users', 'bayesian_rating', 'contributions',
             'created', 'default_locale', 'guid', 'has_eula',
             'has_privacy_policy', 'hotness', 'icon_hash', 'icon_type',
             'is_experimental', 'is_recommended', 'last_updated', 'modified',
             'public_stats', 'requires_payment', 'slug', 'status', 'type',
             'view_source', 'weekly_downloads'))

        # Attach attributes that do not have the same name/format in ES.
        obj.tag_list = data.get('tags', [])
        obj.all_categories = [
            CATEGORIES_BY_ID[cat_id] for cat_id in data.get('category', [])
        ]

        # Not entirely accurate, but enough in the context of the search API.
        obj.disabled_by_user = data.get('is_disabled', False)

        # Attach translations (they require special treatment).
        self._attach_translations(obj, data, self.translated_fields)

        # Attach related models (also faking them). `current_version` is a
        # property we can't write to, so we use the underlying field which
        # begins with an underscore.
        data_version = data.get('current_version') or {}
        obj._current_version = self.fake_version_object(
            obj, data_version, amo.RELEASE_CHANNEL_LISTED)
        obj._current_version_id = data_version.get('id')

        data_authors = data.get('listed_authors', [])
        obj.listed_authors = [
            UserProfile(id=data_author['id'],
                        display_name=data_author['name'],
                        username=data_author['username'],
                        is_public=data_author.get('is_public', False))
            for data_author in data_authors
        ]

        is_static_theme = data.get('type') == amo.ADDON_STATICTHEME
        preview_model_class = VersionPreview if is_static_theme else Preview
        obj.current_previews = [
            self.fake_preview_object(obj,
                                     preview_data,
                                     model_class=preview_model_class)
            for preview_data in data.get('previews', [])
        ]

        ratings = data.get('ratings', {})
        obj.average_rating = ratings.get('average')
        obj.total_ratings = ratings.get('count')
        obj.text_ratings_count = ratings.get('text_count')

        obj._is_featured = data.get('is_featured', False)

        return obj

    def get__score(self, obj):
        # es_meta is added by BaseESSerializer.to_representation() before DRF's
        # to_representation() is called, so it's present on all objects.
        return obj._es_meta['score']

    def to_representation(self, obj):
        data = super(ESAddonSerializer, self).to_representation(obj)
        request = self.context.get('request')
        if request and '_score' in data and not is_gate_active(
                request, 'addons-search-_score-field'):
            data.pop('_score')
        return data