def alter_list_data_to_serialize(self, request, data): if waffle.switch_is_active('rocketfuel'): types = ( ('collections', COLLECTIONS_TYPE_BASIC), ('featured', COLLECTIONS_TYPE_FEATURED), ('operator', COLLECTIONS_TYPE_OPERATOR), ) self.filter_fallbacks = {} for name, col_type in types: data[name], fallback = self.collections(request, collection_type=col_type) if fallback: self.filter_fallbacks[name] = fallback else: form_data = self.get_search_data(request) region = getattr(request, 'REGION', mkt.regions.WORLDWIDE) cat_slug = form_data.get('cat') if cat_slug: cat_slug = [cat_slug] # Filter by device feature profile. profile = self.get_feature_profile(request) qs = Webapp.featured(cat=cat_slug, region=region, profile=profile) bundles = (self.build_bundle(obj=obj, request=request) for obj in qs) data['featured'] = [AppResource().full_dehydrate(bundle) for bundle in bundles] # Alter the _view_name so that statsd logs seperately from search. request._view_name = 'featured' return data
def dump_app(id, **kw): # Because @robhudson told me to. from mkt.api.resources import AppResource # Note: not using storage because all these operations should be local. target_dir = os.path.join(settings.DUMPED_APPS_PATH, 'apps', str(id / 1000)) target_file = os.path.join(target_dir, str(id) + '.json') try: obj = Webapp.objects.get(pk=id) except Webapp.DoesNotExist: task_log.info(u'Webapp does not exist: {0}'.format(id)) return req = RequestFactory().get('/') req.user = AnonymousUser() req.REGION = WORLDWIDE if not os.path.exists(target_dir): os.makedirs(target_dir) task_log.info('Dumping app {0} to {1}'.format(id, target_file)) res = AppResource().dehydrate_objects([obj], request=req) json.dump(res[0], open(target_file, 'w'), cls=JSONEncoder) return target_file
def test_to_native(self): resource = AppResource().full_dehydrate(Bundle(obj=self.app)) native = self.field.to_native(self.membership) for key, value in native.iteritems(): if key == 'resource_uri': eq_(value, self.app.get_api_url(pk=self.app.pk)) else: eq_(value, resource.data[key])
def dehydrate(self, bundle): obj = bundle.obj amo_user = getattr(bundle.request, 'amo_user', None) uses_es = waffle.switch_is_active('search-api-es') if uses_es: bundle.data.update(es_app_to_dict( obj, region=bundle.request.REGION.id, profile=amo_user)) else: bundle = AppResource().dehydrate(bundle) bundle.data['absolute_url'] = absolutify( bundle.obj.get_detail_url()) # Add extra data for reviewers. Used in reviewer tool search. bundle = update_with_reviewer_data(bundle) return bundle
def alter_list_data_to_serialize(self, request, data): form_data = self.get_search_data(request) region = getattr(request, 'REGION', mkt.regions.WORLDWIDE) cat_slug = form_data.get('cat') if cat_slug: cat_slug = [cat_slug] # Filter by device feature profile. profile = self.get_feature_profile(request) qs = Webapp.featured(cat=cat_slug, region=region, profile=profile) bundles = [self.build_bundle(obj=obj, request=request) for obj in qs] data['featured'] = [ AppResource().full_dehydrate(bundle) for bundle in bundles ] # Alter the _view_name so that statsd logs seperately from search. request._view_name = 'featured' return data
def get_list(self, request, **kwargs): form = Featured(request.GET, region=getattr(request, 'REGION', regions.WORLDWIDE).slug) if not form.is_valid(): raise self.form_errors(form) data = form.as_featured() # By default the home page has no category. data['cat'] = None # Regardless of the limit, we will override that. data['limit'] = 9 if data['mobile'] else 12 cat = CategoryResource() featured = Webapp.featured(**data) return self.create_response( request, { 'categories': cat.dehydrate_objects(cat.obj_get_list()), 'featured': AppResource().dehydrate_objects(featured) })
def dehydrate(self, bundle): obj = bundle.obj amo_user = getattr(bundle.request, 'amo_user', None) uses_es = waffle.switch_is_active('search-api-es') if uses_es: bundle.data.update( es_app_to_dict(obj, currency=bundle.request.REGION.default_currency, profile=amo_user)) else: bundle = AppResource().dehydrate(bundle) bundle.data['absolute_url'] = absolutify( bundle.obj.get_detail_url()) # Add extra data for reviewers. Used in reviewer tool search. # TODO: Reviewer flags in ES (bug 848446) if acl.action_allowed(bundle.request, 'Apps', 'Review'): addon_id = bundle.obj._id if uses_es else bundle.obj.id version = Version.objects.filter(addon_id=addon_id).latest() escalated = EscalationQueue.objects.filter( addon_id=addon_id).exists() if uses_es: bundle.data['latest_version_status'] = ( obj.latest_version_status) else: try: file_ = version and version.files.latest() bundle.data['latest_version_status'] = (file_.status if file_ else None) except ObjectDoesNotExist: bundle.data['latest_version_status'] = None bundle.data['reviewer_flags'] = { 'has_comment': version.has_editor_comment, 'has_info_request': version.has_info_request, 'is_escalated': escalated, } return bundle
def alter_list_data_to_serialize(self, request, data): form_data = self.search_form(request) region = getattr(request, 'REGION', mkt.regions.WORLDWIDE) if form_data['cat']: category = Category.objects.get(pk=form_data['cat']) else: category = None # Filter by device feature profile. profile = None if request.GET.get('dev') in ('firefoxos', 'android'): sig = request.GET.get('pro') if sig: profile = FeatureProfile.from_signature(sig) qs = Webapp.featured(cat=category, region=region, profile=profile) bundles = [self.build_bundle(obj=obj, request=request) for obj in qs] data['featured'] = [ AppResource().full_dehydrate(bundle) for bundle in bundles ] return data
def dehydrate(self, bundle): obj = bundle.obj amo_user = getattr(bundle.request, 'amo_user', None) uses_es = waffle.switch_is_active('search-api-es') if uses_es: bundle.data.update(es_app_to_dict( obj, currency=bundle.request.REGION.default_currency, profile=amo_user)) else: bundle = AppResource().dehydrate(bundle) bundle.data['absolute_url'] = absolutify( bundle.obj.get_detail_url()) # Add extra data for reviewers. Used in reviewer tool search. # TODO: Reviewer flags in ES (bug 848446) if acl.action_allowed(bundle.request, 'Apps', 'Review'): addon_id = bundle.obj._id if uses_es else bundle.obj.id version = Version.objects.filter(addon_id=addon_id).latest() escalated = EscalationQueue.objects.filter( addon_id=addon_id).exists() if uses_es: bundle.data['latest_version_status'] = ( obj.latest_version_status) else: try: file_ = version and version.files.latest() bundle.data['latest_version_status'] = ( file_.status if file_ else None) except ObjectDoesNotExist: bundle.data['latest_version_status'] = None bundle.data['reviewer_flags'] = { 'has_comment': version.has_editor_comment, 'has_info_request': version.has_info_request, 'is_escalated': escalated, } return bundle
def to_native(self, value): bundle = Bundle(obj=value.app) return AppResource().full_dehydrate(bundle).data
from django.conf import settings from django.conf.urls import include, patterns, url from tastypie.api import Api from tastypie_services.services import (ErrorResource, SettingsResource) from mkt.api.base import handle_500 from mkt.api.resources import (AppResource, CategoryResource, PreviewResource, StatusResource, ValidationResource) from mkt.ratings.resources import RatingResource from mkt.search.api import SearchResource, WithCreaturedResource api = Api(api_name='apps') api.register(ValidationResource()) api.register(AppResource()) api.register(CategoryResource()) api.register(PreviewResource()) api.register(WithCreaturedResource()) api.register(SearchResource()) api.register(StatusResource()) api.register(RatingResource()) urls = [url(r'^', include(api.urls)),] if settings.ALLOW_TASTYPIE_SERVICES: services = Api(api_name='services') services.register(ErrorResource(set_handler=handle_500)) if getattr(settings, 'CLEANSED_SETTINGS_ACCESS', False): services.register(SettingsResource()) urls.append(url(r'^', include(services.urls)))
def get_resource_uri(self, bundle): # Link to the AppResource URI. return AppResource().get_resource_uri(bundle.obj)
def app_to_dict(app, currency=None, profile=None): """Return app data as dict for API.""" # Sad circular import issues. from mkt.api.resources import AppResource, PreviewResource from mkt.developers.api import AccountResource from mkt.developers.models import AddonPaymentAccount cv = app.current_version version_data = { 'version': getattr(cv, 'version', None), 'release_notes': getattr(cv, 'releasenotes', None) } supported_locales = getattr(app.current_version, 'supported_locales', '') data = { 'app_type': app.app_type, 'categories': list(app.categories.values_list('pk', flat=True)), 'content_ratings': dict([(cr.get_body().name, { 'name': cr.get_rating().name, 'description': unicode(cr.get_rating().description), }) for cr in app.content_ratings.all()]) or None, 'current_version': version_data, 'default_locale': app.default_locale, 'image_assets': dict([(ia.slug, (ia.image_url, ia.hue)) for ia in app.image_assets.all()]), 'icons': dict([(icon_size, app.get_icon_url(icon_size)) for icon_size in (16, 48, 64, 128)]), 'is_packaged': app.is_packaged, 'listed_authors': [{ 'name': author.name } for author in app.listed_authors], 'manifest_url': app.get_manifest_url(), 'previews': PreviewResource().dehydrate_objects(app.previews.all()), 'premium_type': amo.ADDON_PREMIUM_API[app.premium_type], 'public_stats': app.public_stats, 'price': None, 'price_locale': None, 'ratings': { 'average': app.average_rating, 'count': app.total_reviews }, 'regions': RegionResource().dehydrate_objects(app.get_regions()), 'slug': app.app_slug, 'supported_locales': (supported_locales.split(',') if supported_locales else []) } data['upsell'] = False if app.upsell: upsell = app.upsell.premium data['upsell'] = { 'id': upsell.id, 'app_slug': upsell.app_slug, 'icon_url': upsell.get_icon_url(128), 'name': unicode(upsell.name), 'resource_uri': AppResource().get_resource_uri(upsell), } if app.premium: q = AddonPaymentAccount.objects.filter(addon=app) if len(q) > 0 and q[0].payment_account: data['payment_account'] = AccountResource().get_resource_uri( q[0].payment_account) try: data['price'] = app.get_price(currency) data['price_locale'] = app.get_price_locale(currency) except AttributeError: # Until bug 864569 gets fixed. log.info('Missing price data for premium app: %s' % app.pk) with no_translation(): data['device_types'] = [n.api_name for n in app.device_types] if profile: data['user'] = { 'developed': app.has_author(profile, [amo.AUTHOR_ROLE_OWNER]), 'installed': app.has_installed(profile), 'purchased': app.pk in profile.purchase_ids(), } return data
def es_app_to_dict(obj, region=None, profile=None, request=None): """ Return app data as dict for API where `app` is the elasticsearch result. """ # Circular import. from mkt.api.base import GenericObject from mkt.api.resources import AppResource, PrivacyPolicyResource from mkt.developers.api import AccountResource from mkt.developers.models import AddonPaymentAccount from mkt.webapps.models import Installed, Webapp src = obj._source # The following doesn't perform a database query, but gives us useful # methods like `get_detail_url`. If you use `obj` make sure the calls # don't query the database. is_packaged = src.get('app_type') != amo.ADDON_WEBAPP_HOSTED app = Webapp(app_slug=obj.app_slug, is_packaged=is_packaged) attrs = ('content_ratings', 'created', 'current_version', 'default_locale', 'homepage', 'manifest_url', 'previews', 'ratings', 'status', 'support_email', 'support_url', 'weekly_downloads') data = dict((a, getattr(obj, a, None)) for a in attrs) data.update({ 'absolute_url': absolutify(app.get_detail_url()), 'app_type': app.app_type, 'author': src.get('author', ''), 'categories': [c for c in obj.category], 'description': get_attr_lang(src, 'description', obj.default_locale), 'device_types': [DEVICE_TYPES[d].api_name for d in src.get('device')], 'icons': dict((i['size'], i['url']) for i in src.get('icons')), 'id': str(obj._id), 'is_packaged': is_packaged, 'name': get_attr_lang(src, 'name', obj.default_locale), 'payment_required': False, 'premium_type': amo.ADDON_PREMIUM_API[src.get('premium_type')], 'privacy_policy': PrivacyPolicyResource().get_resource_uri(GenericObject({'pk': obj._id})), 'public_stats': obj.has_public_stats, 'supported_locales': src.get('supported_locales', ''), 'slug': obj.app_slug, # TODO: Remove the type check once this code rolls out and our indexes # aren't between mapping changes. 'versions': dict((v.get('version'), v.get('resource_uri')) for v in src.get('versions') if type(v) == dict), }) if not data['public_stats']: data['weekly_downloads'] = None data['regions'] = RegionResource().dehydrate_objects( map(REGIONS_CHOICES_ID_DICT.get, app.get_region_ids(worldwide=True, excluded=obj.region_exclusions))) if src.get('premium_type') in amo.ADDON_PREMIUMS: acct = list(AddonPaymentAccount.objects.filter(addon=app)) if acct and acct.payment_account: data['payment_account'] = AccountResource().get_resource_uri( acct.payment_account) else: data['payment_account'] = None data['upsell'] = False if hasattr(obj, 'upsell'): exclusions = obj.upsell.get('region_exclusions') if exclusions is not None and region not in exclusions: data['upsell'] = obj.upsell data['upsell']['resource_uri'] = AppResource().get_resource_uri( Webapp(id=obj.upsell['id'])) data['price'] = data['price_locale'] = None try: price_tier = src.get('price_tier') if price_tier: price = Price.objects.get(name=price_tier) if (data['upsell'] or payments_enabled(request)): price_currency = price.get_price_currency(region=region) if price_currency and price_currency.paid: data['price'] = price.get_price(region=region) data['price_locale'] = price.get_price_locale( region=region) data['payment_required'] = bool(price.price) except Price.DoesNotExist: log.warning('Issue with price tier on app: {0}'.format(obj._id)) data['payment_required'] = True # TODO: Let's get rid of these from the API to avoid db hits. if profile and isinstance(profile, UserProfile): data['user'] = { 'developed': AddonUser.objects.filter(addon=obj.id, user=profile, role=amo.AUTHOR_ROLE_OWNER).exists(), 'installed': Installed.objects.filter(user=profile, addon_id=obj.id).exists(), 'purchased': obj.id in profile.purchase_ids(), } return data
def get_list(self, request=None, **kwargs): form_data = self.search_form(request) is_admin = acl.action_allowed(request, 'Admin', '%') is_reviewer = acl.action_allowed(request, 'Apps', 'Review') uses_es = waffle.switch_is_active('search-api-es') # Pluck out status and addon type first since it forms part of the base # query, but only for privileged users. status = form_data['status'] addon_type = form_data['type'] base_filters = { 'type': addon_type, } # Allow reviewers and admins to search by statuses other than PUBLIC. if status and (status == 'any' or status != amo.STATUS_PUBLIC): if is_admin or is_reviewer: base_filters['status'] = status else: return http.HttpUnauthorized(content=json.dumps( {'reason': _('Unauthorized to filter by status.')})) # Filter by device feature profile. profile = None # TODO: Remove uses_es conditional with 'search-api-es' waffle. if uses_es and request.GET.get('dev') in ('firefoxos', 'android'): sig = request.GET.get('pro') if sig: profile = FeatureProfile.from_signature(sig) # Filter by region. region = getattr(request, 'REGION', mkt.regions.WORLDWIDE) qs = _get_query(region, gaia=request.GAIA, mobile=request.MOBILE, tablet=request.TABLET, filters=base_filters, new_idx=True) qs = _filter_search(request, qs, form_data, region=region, profile=profile) paginator = self._meta.paginator_class( request.GET, qs, resource_uri=self.get_resource_list_uri(), limit=self._meta.limit) page = paginator.page() # Rehydrate the results as per tastypie. objs = [] for obj in page['objects']: obj.pk = obj.id objs.append(self.build_bundle(obj=obj, request=request)) if uses_es: page['objects'] = [self.full_dehydrate(bundle) for bundle in objs] else: page['objects'] = [ AppResource().full_dehydrate(bundle) for bundle in objs ] # This isn't as quite a full as a full TastyPie meta object, # but at least it's namespaced that way and ready to expand. to_be_serialized = self.alter_list_data_to_serialize(request, page) return self.create_response(request, to_be_serialized)
def app_to_dict(app, region=None, profile=None, request=None): """Return app data as dict for API.""" # Sad circular import issues. from mkt.api.resources import AppResource from mkt.developers.api import AccountResource from mkt.developers.models import AddonPaymentAccount from mkt.submit.api import PreviewResource from mkt.webapps.models import reverse_version supported_locales = getattr(app.current_version, 'supported_locales', '') data = { 'app_type': app.app_type, 'author': app.developer_name, 'categories': list(app.categories.values_list('slug', flat=True)), 'content_ratings': dict([(cr.get_body().name, { 'name': cr.get_rating().name, 'description': unicode(cr.get_rating().description), }) for cr in app.content_ratings.all()]) or None, 'created': app.created, 'current_version': (app.current_version.version if getattr( app, 'current_version') else None), 'default_locale': app.default_locale, 'icons': dict([(icon_size, app.get_icon_url(icon_size)) for icon_size in (16, 48, 64, 128)]), 'is_packaged': app.is_packaged, 'manifest_url': app.get_manifest_url(), 'payment_required': False, 'previews': PreviewResource().dehydrate_objects(app.previews.all()), 'premium_type': amo.ADDON_PREMIUM_API[app.premium_type], 'public_stats': app.public_stats, 'price': None, 'price_locale': None, 'ratings': { 'average': app.average_rating, 'count': app.total_reviews }, 'regions': RegionResource().dehydrate_objects(app.get_regions()), 'slug': app.app_slug, 'supported_locales': (supported_locales.split(',') if supported_locales else []), 'weekly_downloads': app.weekly_downloads if app.public_stats else None, 'versions': dict((v.version, reverse_version(v)) for v in app.versions.all()) } data['upsell'] = False if app.upsell and region in app.upsell.premium.get_price_region_ids(): upsell = app.upsell.premium data['upsell'] = { 'id': upsell.id, 'app_slug': upsell.app_slug, 'icon_url': upsell.get_icon_url(128), 'name': unicode(upsell.name), 'resource_uri': AppResource().get_resource_uri(upsell), } if app.premium: q = AddonPaymentAccount.objects.filter(addon=app) if len(q) > 0 and q[0].payment_account: data['payment_account'] = AccountResource().get_resource_uri( q[0].payment_account) if (region in app.get_price_region_ids() or payments_enabled(request)): data['price'] = app.get_price(region=region) data['price_locale'] = app.get_price_locale(region=region) data['payment_required'] = (bool(app.get_tier().price) if app.get_tier() else False) with no_translation(): data['device_types'] = [n.api_name for n in app.device_types] if profile: data['user'] = { 'developed': app.addonuser_set.filter(user=profile, role=amo.AUTHOR_ROLE_OWNER).exists(), 'installed': app.has_installed(profile), 'purchased': app.pk in profile.purchase_ids(), } return data
def es_app_to_dict(obj, currency=None, profile=None): """ Return app data as dict for API where `app` is the elasticsearch result. """ # Circular import. from mkt.api.base import GenericObject from mkt.api.resources import AppResource, PrivacyPolicyResource from mkt.developers.api import AccountResource from mkt.developers.models import AddonPaymentAccount from mkt.webapps.models import Installed, Webapp src = obj._source # The following doesn't perform a database query, but gives us useful # methods like `get_detail_url`. If you use `obj` make sure the calls # don't query the database. is_packaged = src['app_type'] == amo.ADDON_WEBAPP_PACKAGED app = Webapp(app_slug=obj.app_slug, is_packaged=is_packaged) attrs = ('content_ratings', 'current_version', 'default_locale', 'homepage', 'manifest_url', 'previews', 'ratings', 'status', 'support_email', 'support_url') data = dict(zip(attrs, attrgetter(*attrs)(obj))) data.update({ 'absolute_url': absolutify(app.get_detail_url()), 'app_type': app.app_type, 'categories': [c for c in obj.category], 'description': get_attr_lang(src, 'description', obj.default_locale), 'device_types': [DEVICE_TYPES[d].api_name for d in src['device']], 'icons': dict((i['size'], i['url']) for i in src['icons']), 'id': str(obj._id), 'is_packaged': is_packaged, 'listed_authors': [{ 'name': name } for name in src['authors']], 'name': get_attr_lang(src, 'name', obj.default_locale), 'premium_type': amo.ADDON_PREMIUM_API[src['premium_type']], 'privacy_policy': PrivacyPolicyResource().get_resource_uri(GenericObject({'pk': obj._id})), 'public_stats': obj.has_public_stats, 'summary': get_attr_lang(src, 'summary', obj.default_locale), 'supported_locales': src.get('supported_locales', ''), 'slug': obj.app_slug, }) data['regions'] = RegionResource().dehydrate_objects( map(REGIONS_CHOICES_ID_DICT.get, app.get_region_ids(worldwide=True, excluded=obj.region_exclusions))) if src['premium_type'] in amo.ADDON_PREMIUMS: acct = list(AddonPaymentAccount.objects.filter(addon=app)) if acct and acct.payment_account: data['payment_account'] = AccountResource().get_resource_uri( acct.payment_account) else: data['payment_account'] = None data['price'] = data['price_locale'] = None try: if src['price_tier']: price = Price.objects.get(name=src['price_tier']) data['price'] = price.get_price(currency=currency) data['price_locale'] = price.get_price_locale(currency=currency) except Price.DoesNotExist: pass data['upsell'] = False if hasattr(obj, 'upsell'): data['upsell'] = obj.upsell data['upsell']['resource_uri'] = AppResource().get_resource_uri( Webapp(id=obj.upsell['id'])) # TODO: Let's get rid of these from the API to avoid db hits. if profile and isinstance(profile, UserProfile): data['user'] = { 'developed': AddonUser.objects.filter(user=profile, role=amo.AUTHOR_ROLE_OWNER).exists(), 'installed': Installed.objects.filter(user=profile, addon_id=obj.id).exists(), 'purchased': obj.id in profile.purchase_ids(), } return data