class FeedShelfESSerializer(BaseFeedCollectionESSerializer, FeedShelfSerializer): """A serializer for the FeedShelf class for ES representation.""" apps = AppESField(source='_app_ids', many=True) description = ESTranslationSerializerField(required=False) name = ESTranslationSerializerField(required=False) class Meta(FeedShelfSerializer.Meta): fields = filter(lambda field: field != 'is_published', FeedShelfSerializer.Meta.fields) def fake_object(self, data): shelf = self._attach_fields(FeedShelf(), data, ( 'id', 'carrier', 'image_hash', 'image_landing_hash', 'region', 'slug' )) shelf = self._attach_translations(shelf, data, ( 'description', 'name' )) shelf._app_ids = data.get('apps') # Attach groups. self.context['group_apps'] = data.get('group_apps') self.context['group_names'] = data.get('group_names') return shelf
class RocketbarESAppSerializer(serializers.Serializer): """Used by Firefox OS's Rocketbar apps viewer.""" name = ESTranslationSerializerField() @property def data(self): if self._data is None: self._data = [self.to_native(o['payload']) for o in self.object] return self._data def to_native(self, obj): # fake_app is a fake instance because we need to access a couple # properties and methods on Webapp. It should never hit the database. self.fake_app = Webapp(id=obj['id'], icon_type='image/png', default_locale=obj.get('default_locale', settings.LANGUAGE_CODE), icon_hash=obj.get('icon_hash'), modified=es_to_datetime(obj['modified'])) ESTranslationSerializerField.attach_translations( self.fake_app, obj, 'name') return { 'name': self.fields['name'].field_to_native(self.fake_app, 'name'), 'icon': self.fake_app.get_icon_url(64), 'slug': obj['slug'], 'manifest_url': obj['manifest_url'], }
def get_fields(self): """ Return all fields as normal, with one exception: replace every instance of TranslationSerializerField with ESTranslationSerializerField. """ fields = super(BaseESSerializer, self).get_fields() for key, field in fields.items(): if isinstance(field, TranslationSerializerField): fields[key] = ESTranslationSerializerField(source=field.source) return fields
class FeedAppESSerializer(FeedAppSerializer, BaseESSerializer): """ A serializer for the FeedApp class that serializes ES representation. """ app = AppESField(source='_app_id') background_image = FeedImageField(allow_null=True) description = ESTranslationSerializerField(required=False) preview = FeedPreviewESSerializer(source='_preview') pullquote_text = ESTranslationSerializerField(required=False) def fake_object(self, data): feed_app = self._attach_fields( FeedApp(), data, ('id', 'background_color', 'color', 'image_hash', 'pullquote_attribution', 'pullquote_rating', 'slug', 'type')) feed_app._preview = data.get('preview') feed_app = self._attach_translations(feed_app, data, ('description', 'pullquote_text')) feed_app._app_id = data.get('app') return feed_app
class FeedCollectionESSerializer(BaseFeedCollectionESSerializer, FeedCollectionSerializer): """ A serializer for the FeedCollection class for ES representation. """ apps = AppESField(source='_app_ids', many=True) description = ESTranslationSerializerField(required=False) name = ESTranslationSerializerField(required=False) def fake_object(self, data): collection = self._attach_fields( FeedCollection(), data, ('id', 'background_color', 'image_hash', 'slug', 'type')) collection = self._attach_translations(collection, data, ('name', 'description')) collection._app_ids = data.get('apps') # Attach groups. self.context['group_apps'] = data.get('group_apps') self.context['group_names'] = data.get('group_names') return collection
class ESAppSerializer(BaseESSerializer, AppSerializer): # Fields specific to search. absolute_url = serializers.SerializerMethodField('get_absolute_url') reviewed = serializers.DateField() # Override previews, because we don't need the full PreviewSerializer. previews = SimplePreviewSerializer(many=True, source='all_previews') # Override those, because we want a different source. Also, related fields # will call self.queryset early if they are not read_only, so force that. file_size = serializers.SerializerMethodField('get_file_size') is_disabled = serializers.BooleanField(source='_is_disabled', default=False) manifest_url = serializers.CharField(source='manifest_url') package_path = serializers.SerializerMethodField('get_package_path') # Feed collection. group = ESTranslationSerializerField(required=False) # The fields we want converted to Python date/datetimes. datetime_fields = ('created', 'last_updated', 'modified', 'reviewed') class Meta(AppSerializer.Meta): fields = AppSerializer.Meta.fields + [ 'absolute_url', 'group', 'reviewed' ] def __init__(self, *args, **kwargs): super(ESAppSerializer, self).__init__(*args, **kwargs) # Remove fields that we don't have in ES at the moment. self.fields.pop('upsold', None) def fake_object(self, data): """Create a fake instance of Webapp and related models from ES data.""" is_packaged = data['app_type'] != mkt.ADDON_WEBAPP_HOSTED is_privileged = data['app_type'] == mkt.ADDON_WEBAPP_PRIVILEGED obj = Webapp(id=data['id'], app_slug=data['app_slug'], is_packaged=is_packaged, icon_type='image/png') # Set relations and attributes we need on those relations. # The properties set on latest_version and current_version differ # because we are only setting what the serializer is going to need. # In particular, latest_version.is_privileged needs to be set because # it's used by obj.app_type_id. obj.listed_authors = [] obj._current_version = Version() obj._current_version.addon = obj obj._current_version._developer_name = data['author'] obj._current_version.supported_locales = data['supported_locales'] obj._current_version.version = data['current_version'] obj._latest_version = Version() obj._latest_version.is_privileged = is_privileged obj._geodata = Geodata() obj.all_previews = [ Preview(id=p['id'], modified=self.to_datetime(p['modified']), filetype=p['filetype'], sizes=p.get('sizes', {})) for p in data['previews'] ] obj.categories = data['category'] obj.tags_list = data['tags'] obj._device_types = [DEVICE_TYPES[d] for d in data['device']] obj._is_disabled = data['is_disabled'] # Set base attributes on the "fake" app using the data from ES. self._attach_fields( obj, data, ('created', 'default_locale', 'guid', 'icon_hash', 'is_escalated', 'is_offline', 'last_updated', 'hosted_url', 'manifest_url', 'modified', 'premium_type', 'promo_img_hash', 'regions', 'reviewed', 'status')) # Attach translations for all translated attributes. self._attach_translations(obj, data, ('name', 'description', 'homepage', 'support_email', 'support_url')) if data.get('group_translations'): self._attach_translations(obj, data, ('group', )) # Feed group. else: obj.group_translations = None # Release notes target and source name differ (ES stores it as # release_notes but the db field we are emulating is called # releasenotes without the "_"). ESTranslationSerializerField.attach_translations( obj._current_version, data, 'release_notes', target_name='releasenotes') # Set attributes that have a different name in ES. obj.public_stats = data['has_public_stats'] # Override obj.get_excluded_region_ids() to just return the list of # regions stored in ES instead of making SQL queries. obj.get_excluded_region_ids = lambda: data['region_exclusions'] # Set up payments stuff to avoid extra queries later (we'll still make # some, because price info is not in ES). if obj.is_premium(): Webapp.attach_premiums([obj]) # Some methods below will need the raw data from ES, put it on obj. obj.es_data = data return obj def get_content_ratings(self, obj): body = (mkt.regions.REGION_TO_RATINGS_BODY().get( self._get_region_slug(), 'generic')) prefix = 'has_%s' % body # Backwards incompat with old index. for i, desc in enumerate(obj.es_data.get('content_descriptors', [])): if desc.isupper(): obj.es_data['content_descriptors'][i] = 'has_' + desc.lower() for i, inter in enumerate(obj.es_data.get('interactive_elements', [])): if inter.isupper(): obj.es_data['interactive_elements'][i] = 'has_' + inter.lower() return { 'body': body, 'rating': dehydrate_content_rating( (obj.es_data.get('content_ratings') or {}).get(body)) or None, 'descriptors': [ key for key in obj.es_data.get('content_descriptors', []) if prefix in key ], 'descriptors_text': [ mkt.iarc_mappings.REVERSE_DESCS[key] for key in obj.es_data.get('content_descriptors') if prefix in key ], 'interactives': obj.es_data.get('interactive_elements', []), 'interactives_text': [ mkt.iarc_mappings.REVERSE_INTERACTIVES[key] for key in obj.es_data.get('interactive_elements') ] } def get_feature_compatibility(self, app): # We're supposed to be filtering out incompatible apps anyway, so don't # bother calculating feature compatibility: if an app is there, it's # either compatible or the client overrode this by asking to see apps # for a different platform. return None def get_versions(self, obj): return dict( (v['version'], v['resource_uri']) for v in obj.es_data['versions']) def get_ratings_aggregates(self, obj): return obj.es_data.get('ratings', {}) def get_upsell(self, obj): upsell = obj.es_data.get('upsell', False) if upsell: region_id = self.context['request'].REGION.id exclusions = upsell.get('region_exclusions') if exclusions is not None and region_id not in exclusions: upsell['resource_uri'] = reverse('app-detail', kwargs={'pk': upsell['id']}) else: upsell = False return upsell def get_absolute_url(self, obj): return absolutify(obj.get_absolute_url()) def get_package_path(self, obj): return obj.es_data.get('package_path') def get_file_size(self, obj): return obj.es_data.get('file_size') def get_is_homescreen(self, obj): return obj.es_data.get('is_homescreen')
class ESAppSerializer(AppSerializer): # Fields specific to search. absolute_url = serializers.SerializerMethodField('get_absolute_url') reviewed = serializers.DateField() # Override previews, because we don't need the full PreviewSerializer. previews = SimplePreviewSerializer(many=True, source='all_previews') # Override those, because we want a different source. Also, related fields # will call self.queryset early if they are not read_only, so force that. categories = serializers.SlugRelatedField(read_only=True, many=True, slug_field='slug', source='all_categories') manifest_url = serializers.CharField(source='manifest_url') # Override translations, because we want a different field. banner_message = ESTranslationSerializerField( source='geodata.banner_message') description = ESTranslationSerializerField() homepage = ESTranslationSerializerField() name = ESTranslationSerializerField() release_notes = ESTranslationSerializerField( source='current_version.releasenotes') support_email = ESTranslationSerializerField() support_url = ESTranslationSerializerField() class Meta(AppSerializer.Meta): fields = AppSerializer.Meta.fields + ['absolute_url', 'reviewed'] def __init__(self, *args, **kwargs): super(ESAppSerializer, self).__init__(*args, **kwargs) # Remove fields that we don't have in ES at the moment. self.fields.pop('upsold', None) # Set all fields as read_only just in case. for field_name in self.fields: self.fields[field_name].read_only = True @property def data(self): """ Returns the serialized data on the serializer. """ if self._data is None: if self.many: self._data = [self.to_native(item) for item in self.object] else: self._data = self.to_native(self.object) return self._data def field_to_native(self, obj, field_name): # DRF's field_to_native calls .all(), which we want to avoid, so we # provide a simplified version that doesn't and just iterates on the # object list. return [self.to_native(item) for item in obj.object_list] def to_native(self, obj): app = self.create_fake_app(obj._source) return super(ESAppSerializer, self).to_native(app) def create_fake_app(self, data): """Create a fake instance of Webapp and related models from ES data.""" is_packaged = data['app_type'] != amo.ADDON_WEBAPP_HOSTED is_privileged = data['app_type'] == amo.ADDON_WEBAPP_PRIVILEGED obj = Webapp(id=data['id'], app_slug=data['app_slug'], is_packaged=is_packaged, type=amo.ADDON_WEBAPP, icon_type='image/png') # Set relations and attributes we need on those relations. # The properties set on latest_version and current_version differ # because we are only setting what the serializer is going to need. # In particular, latest_version.is_privileged needs to be set because # it's used by obj.app_type_id. obj.listed_authors = [] obj._current_version = Version() obj._current_version.addon = obj obj._current_version._developer_name = data['author'] obj._current_version.supported_locales = data['supported_locales'] obj._current_version.version = data['current_version'] obj._latest_version = Version() obj._latest_version.is_privileged = is_privileged obj._geodata = Geodata() obj.all_categories = [Category(slug=cat) for cat in data['category']] obj.all_previews = [ Preview(id=p['id'], modified=p['modified'], filetype=p['filetype']) for p in data['previews'] ] obj._device_types = [DEVICE_TYPES[d] for d in data['device']] # Set base attributes on the "fake" app using the data from ES. # It doesn't mean they'll get exposed in the serializer output, that # depends on what the fields/exclude attributes in Meta. for field_name in ('created', 'modified', 'default_locale', 'icon_hash', 'is_escalated', 'is_offline', 'manifest_url', 'premium_type', 'regions', 'reviewed', 'status', 'weekly_downloads'): setattr(obj, field_name, data.get(field_name)) # Attach translations for all translated attributes. for field_name in ('name', 'description', 'homepage', 'support_email', 'support_url'): ESTranslationSerializerField.attach_translations( obj, data, field_name) ESTranslationSerializerField.attach_translations( obj._geodata, data, 'banner_message') ESTranslationSerializerField.attach_translations( obj._current_version, data, 'release_notes', target_name='releasenotes') # Set attributes that have a different name in ES. obj.public_stats = data['has_public_stats'] # Override obj.get_region() with a static list of regions generated # from the region_exclusions stored in ES. obj.get_regions = obj.get_regions( obj.get_region_ids(restofworld=True, excluded=data['region_exclusions'])) # Some methods below will need the raw data from ES, put it on obj. obj.es_data = data return obj def get_content_ratings(self, obj): body = (mkt.regions.REGION_TO_RATINGS_BODY().get( self.context['request'].REGION.slug, 'generic')) return { 'body': body, 'rating': dehydrate_content_rating( (obj.es_data.get('content_ratings') or {}).get(body)) or None, 'descriptors': dehydrate_descriptors(obj.es_data.get('content_descriptors', {})).get(body, []), 'interactives': dehydrate_interactives(obj.es_data.get('interactive_elements', [])), } def get_versions(self, obj): return dict( (v['version'], v['resource_uri']) for v in obj.es_data['versions']) def get_ratings_aggregates(self, obj): return obj.es_data.get('ratings', {}) def get_upsell(self, obj): upsell = obj.es_data.get('upsell', False) if upsell: region_id = self.context['request'].REGION.id exclusions = upsell.get('region_exclusions') if exclusions is not None and region_id not in exclusions: upsell['resource_uri'] = reverse('app-detail', kwargs={'pk': upsell['id']}) else: upsell = False return upsell def get_absolute_url(self, obj): return absolutify(obj.get_absolute_url()) def get_tags(self, obj): return obj.es_data['tags']