def test_no_translation(): """ `no_translation` provides a context where only the default language is active. """ lang = translation.get_language() translation.activate("pt-br") with no_translation(): eq_(translation.get_language(), settings.LANGUAGE_CODE) eq_(translation.get_language(), "pt-br") with no_translation("es"): eq_(translation.get_language(), "es") eq_(translation.get_language(), "pt-br") translation.activate(lang)
def test_no_translation(): """ `no_translation` provides a context where only the default language is active. """ lang = translation.get_language() translation.activate('pt-br') with no_translation(): eq_(translation.get_language(), settings.LANGUAGE_CODE) eq_(translation.get_language(), 'pt-br') with no_translation('es'): eq_(translation.get_language(), 'es') eq_(translation.get_language(), 'pt-br') translation.activate(lang)
def test_no_translation(): """ `no_translation` provides a context where only the default language is active. """ old_lang = translation.get_language() try: translation.activate('pt-br') with no_translation(): assert (translation.get_language().lower() == settings.LANGUAGE_CODE.lower()) assert translation.get_language() == 'pt-br' with no_translation('es'): assert translation.get_language() == 'es' assert translation.get_language() == 'pt-br' finally: translation.activate(old_lang)
def dehydrate(self, bundle): obj = bundle.obj bundle.data["app_slug"] = obj.app_slug bundle.data["premium_type"] = amo.ADDON_PREMIUM_API[obj.premium_type] bundle.data["categories"] = [c.pk for c in obj.categories.all()] with no_translation(): bundle.data["device_types"] = [str(n.name).lower() for n in obj.device_types] return bundle
def app_to_dict(app, user=None): """Return app data as dict for API.""" # Sad circular import issues. from mkt.api.resources import PreviewResource cv = app.current_version version_data = { 'version': getattr(cv, 'version', None), 'release_notes': getattr(cv, 'releasenotes', None) } 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, '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.manifest_url, 'previews': PreviewResource().dehydrate_objects(app.previews.all()), 'premium_type': amo.ADDON_PREMIUM_API[app.premium_type], 'public_stats': app.public_stats, 'price': app.get_price(), 'price_locale': (app.premium.get_price_locale() if app.premium else None), 'ratings': { 'average': app.average_rating, 'count': app.total_reviews }, 'slug': app.app_slug, } with no_translation(): data['device_types'] = [n.api_name for n in app.device_types] if user: data['user'] = {'owns': app.has_author(user, [amo.AUTHOR_ROLE_OWNER])} return data
def dehydrate(self, bundle): obj = bundle.obj bundle.data['slug'] = obj.app_slug bundle.data['premium_type'] = amo.ADDON_PREMIUM_API[obj.premium_type] bundle.data['categories'] = [c.pk for c in obj.categories.all()] with no_translation(): bundle.data['device_types'] = [str(n.name).lower() for n in obj.device_types] return bundle
def dehydrate(self, bundle): obj = bundle.obj bundle.data['app_slug'] = obj.app_slug bundle.data['premium_type'] = amo.ADDON_PREMIUM_API[obj.premium_type] bundle.data['categories'] = [c.pk for c in obj.categories.all()] with no_translation(): bundle.data['device_types'] = [n.api_name for n in obj.device_types] bundle.data['app_type'] = obj.app_type return bundle
def app_to_dict(app, currency=None, user=None): """Return app data as dict for API.""" # Sad circular import issues. from mkt.api.resources import PreviewResource cv = app.current_version version_data = { 'version': getattr(cv, 'version', None), 'release_notes': getattr(cv, 'releasenotes', None) } 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, '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}, 'slug': app.app_slug, } if app.premium: try: data['price'] = app.premium.get_price(currency) data['price_locale'] = app.premium.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 user: data['user'] = {'owns': app.has_author(user, [amo.AUTHOR_ROLE_OWNER])} return data
def devices(self, data): with no_translation(): names = dict([(n.api_name, n.id) for n in DEVICE_TYPES.values()]) filtered = [names.get(n, n) for n in data.get('device_types', [])] return {'device_types': filtered}
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 dehydrate(self, bundle): with no_translation(): bundle.data['name'] = str(bundle.obj.name).lower() return bundle
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 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 []) } 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.has_purchased(profile) } return data
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 devices(self, data): with no_translation(): names = dict([(str(n.name).lower(), n.pk) for n in DeviceType.objects.all()]) filtered = [names.get(n, n) for n in data.get('device_types', [])] return {'device_types': filtered}
def categories(request, locale_code): if not _permission_to_edit_locale(request, locale_code): raise PermissionDenied with no_translation(): cats = list(Category.objects.order_by('application')) strings = dict( Translation.objects.filter(id__in=[c.name_id for c in cats], locale=locale_code).values_list( 'id', 'localized_string')) # Category ID to localized string map for checking for changes. category_names = dict([(c.id, strings.get(c.name_id)) for c in cats]) # Category ID to model object to avoid extra SQL lookups on POST. category_objects = dict([(c.id, c) for c in cats]) # Initial data to pre-populate forms. initial = [ dict(id=c.id, name=strings.get(c.name_id), application=c.application) for c in cats ] # Group categories by application, and sort by name within app groups. categories = [] category_no_app = None for key, group in groupby(cats, lambda c: c.application): sorted_cats = sorted(group, key=lambda c: c.name) if key: categories.append((key, sorted_cats)) else: category_no_app = (key, sorted_cats) if category_no_app: # Put app-less categories at the bottom. categories.append(category_no_app) formset = CategoryFormSet(request.POST or None, initial=initial) # Category ID to form mapping. form_map = dict((form.initial['id'], form) for form in formset.forms) if request.method == 'POST' and formset.is_valid(): for form in formset: pk = form.cleaned_data.get('id') name = form.cleaned_data.get('name') if name != category_names.get(pk): cat = category_objects.get(pk) if not cat: continue # The localized string has changed. # Make sure we don't save an empty string value. if name == '' and category_names.get(pk) == None: # If the form field was left blank and there was no # previous translation, do nothing. continue elif name == '' and category_names.get(pk) != None: # If the name is blank and there was a prior translation, # assume they want to remove this translation. # # TODO(robhudson): Figure out how to delete a single # translation properly. Calling... # # Translation.objects.filter(id=cat.name.id, # locale=locale_code).delete() # # ...results in some crazy db traversal that tries to # delete all kinds of things. pass else: # Otherwise, name is not empty and it had a prior # translation so update it. cat.name = {locale_code: name} cat.save() return redirect( reverse('localizers.categories', kwargs=dict(locale_code=locale_code))) data = { 'locale_code': locale_code, 'userlang': product_details.languages[locale_code], 'categories': categories, 'formset': formset, 'form_map': form_map, 'apps': amo.APP_IDS, 'types': amo.ADDON_TYPE, } return render(request, 'localizers/categories.html', data)
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, "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, "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 settings.PURCHASE_ENABLED_REGIONS: 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 settings.PURCHASE_ENABLED_REGIONS or ( request and waffle.flag_is_active(request, "allow-paid-app-search") ): 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 app_to_dict(app, region=None, profile=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.api import AppFeaturesSerializer cv = app.current_version version_data = { 'version': getattr(cv, 'version', None), 'release_notes': getattr(cv, 'releasenotes', None), 'developer_name': getattr(cv, 'developer_name', None), } features = getattr(cv, 'features', None) if features: features = AppFeaturesSerializer().to_native(features)['required'] version_data['required_features'] = features 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, 'created': app.created, '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 []), 'weekly_downloads': app.weekly_downloads if app.public_stats else None, } 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) data['price'] = app.get_price(region=region) data['price_locale'] = app.get_price_locale(region=region) with no_translation(): data['device_types'] = [n.api_name for n in app.device_types] if profile: data['user'] = { 'developed': AddonUser.objects.filter( user=profile, role=amo.AUTHOR_ROLE_OWNER).exists(), 'installed': app.has_installed(profile), 'purchased': app.pk in profile.purchase_ids(), } return data
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 get_device_types(self, app): with no_translation(): return [n.api_name for n in app.device_types]
def categories(request, locale_code): if not _permission_to_edit_locale(request, locale_code): raise PermissionDenied with no_translation(): cats = list(Category.objects.order_by('application')) strings = dict(Translation.objects.filter( id__in=[c.name_id for c in cats], locale=locale_code) .values_list('id', 'localized_string')) # Category ID to localized string map for checking for changes. category_names = dict([(c.id, strings.get(c.name_id)) for c in cats]) # Category ID to model object to avoid extra SQL lookups on POST. category_objects = dict([(c.id, c) for c in cats]) # Initial data to pre-populate forms. initial = [dict(id=c.id, name=strings.get(c.name_id), application=c.application) for c in cats] # Group categories by application, and sort by name within app groups. categories = [] category_no_app = None for key, group in groupby(cats, lambda c: c.application): sorted_cats = sorted(group, key=lambda c: c.name) if key: categories.append((key, sorted_cats)) else: category_no_app = (key, sorted_cats) if category_no_app: # Put app-less categories at the bottom. categories.append(category_no_app) formset = CategoryFormSet(request.POST or None, initial=initial) # Category ID to form mapping. form_map = dict((form.initial['id'], form) for form in formset.forms) if request.method == 'POST' and formset.is_valid(): for form in formset: pk = form.cleaned_data.get('id') name = form.cleaned_data.get('name') if name != category_names.get(pk): cat = category_objects.get(pk) if not cat: continue # The localized string has changed. # Make sure we don't save an empty string value. if name == '' and category_names.get(pk) == None: # If the form field was left blank and there was no # previous translation, do nothing. continue elif name == '' and category_names.get(pk) != None: # If the name is blank and there was a prior translation, # assume they want to remove this translation. # # TODO(robhudson): Figure out how to delete a single # translation properly. Calling... # # Translation.objects.filter(id=cat.name.id, # locale=locale_code).delete() # # ...results in some crazy db traversal that tries to # delete all kinds of things. pass else: # Otherwise, name is not empty and it had a prior # translation so update it. cat.name = {locale_code: name} cat.save() return redirect(reverse('localizers.categories', kwargs=dict(locale_code=locale_code))) data = { 'locale_code': locale_code, 'userlang': product_details.languages[locale_code], 'categories': categories, 'formset': formset, 'form_map': form_map, 'apps': amo.APP_IDS, 'types': amo.ADDON_TYPE, } return render(request, 'localizers/categories.html', data)