Esempio n. 1
0
    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
Esempio n. 2
0
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
Esempio n. 3
0
 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])
Esempio n. 4
0
    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
Esempio n. 5
0
File: api.py Progetto: at13/zamboni
    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
Esempio n. 6
0
    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)
            })
Esempio n. 7
0
    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
Esempio n. 8
0
    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
Esempio n. 9
0
    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
Esempio n. 10
0
 def to_native(self, value):
     bundle = Bundle(obj=value.app)
     return AppResource().full_dehydrate(bundle).data
Esempio n. 11
0
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)))
Esempio n. 12
0
 def get_resource_uri(self, bundle):
     # Link to the AppResource URI.
     return AppResource().get_resource_uri(bundle.obj)
Esempio n. 13
0
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
Esempio n. 14
0
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
Esempio n. 15
0
    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)
Esempio n. 16
0
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
Esempio n. 17
0
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