This repository has been archived by the owner on Mar 15, 2018. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add management cmd for paid app regions (bug 900145)
- Loading branch information
1 parent
4b4dcc0
commit 88c0f04
Showing
3 changed files
with
261 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
161 changes: 161 additions & 0 deletions
161
mkt/developers/management/commands/check_paid_app_regions.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
from optparse import make_option | ||
|
||
from django.core.management.base import BaseCommand, CommandError | ||
|
||
import amo | ||
from mkt.developers.forms import RegionForm | ||
from mkt.regions import ALL_REGION_IDS, REGIONS_CHOICES_ID_DICT | ||
from mkt.webapps.models import AddonExcludedRegion as AER, Webapp | ||
|
||
ALL_REGIONS = set(ALL_REGION_IDS) | ||
DIVIDER = '-' * 28 | ||
|
||
|
||
class Command(BaseCommand): | ||
args = '<app_slug>' | ||
option_list = BaseCommand.option_list + ( | ||
make_option('--exclude_region_by_id', action='store', | ||
type='int', dest='exclude_region_id', | ||
help='Adds an exclusion record for a region by id'), | ||
make_option('--include_region_by_id', action='store', | ||
type='int', dest='include_region_id', | ||
help='Removes an exclusion record for a region by id'), | ||
) | ||
help = ('Check regions for a given paid app and flag if they have ' | ||
'incorrect regions.') | ||
|
||
def _region_obj(self, id_): | ||
return REGIONS_CHOICES_ID_DICT.get(id_) | ||
|
||
def _region_name(self, id_): | ||
region_obj_ = self._region_obj(id_) | ||
return unicode(region_obj_.name) | ||
|
||
def write_output(self, value=''): | ||
self.stdout.write(value + '\n') | ||
|
||
def write_error(self, value=''): | ||
self.stderr.write(value + '\n') | ||
|
||
def get_regions(self, app): | ||
region_excludes = (AER.objects.filter(addon=app) | ||
.values_list('region', flat=True)) | ||
return ALL_REGIONS.difference(region_excludes) | ||
|
||
def get_bad_regions(self, app, regions): | ||
# Initialise RegionForm so we can get the disabled region data | ||
# based on our app. | ||
|
||
if app.premium_type == amo.ADDON_FREE_INAPP: | ||
price = 'free' | ||
else: | ||
price = app.premium.price | ||
|
||
region_form = RegionForm(data={'regions': regions}, | ||
product=app, price=price) | ||
|
||
# We manually construct bad_regions so we can make sure we catch | ||
# worldwide regions (Worldwide is not a valid choice in the form). | ||
return regions.intersection(region_form.disabled_regions) | ||
|
||
def exclude_region(self, app, app_slug, exclude_region_id): | ||
aer, created = AER.objects.get_or_create(addon=app, | ||
region=exclude_region_id) | ||
if not created: | ||
self.write_error('Could not create exclusion record for ' | ||
'region_id %s (%s). It already exists' % ( | ||
exclude_region_id, | ||
self._region_name(exclude_region_id))) | ||
else: | ||
self.write_output('') | ||
self.write_output("Excluding from region_id %s (%s) for " | ||
"app '%s'" % (exclude_region_id, | ||
self._region_name(exclude_region_id), | ||
app_slug)) | ||
self.include_exclude_region = True | ||
|
||
def include_region(self, app, app_slug, include_region_id): | ||
self.write_output() | ||
self.write_output("Including from region_id %s (%s) for app " | ||
"'%s'" % (include_region_id, | ||
self._region_name(include_region_id), | ||
app_slug)) | ||
try: | ||
aer = AER.objects.get(addon=app, region=include_region_id) | ||
aer.delete() | ||
self.include_exclude_region = True | ||
except AER.DoesNotExist: | ||
self.write_error('Could not remove exclusion record for ' | ||
'region_id %s (%s)' % (include_region_id, | ||
self._region_name(include_region_id))) | ||
|
||
def output_regions(self, app, app_slug): | ||
regions = self.get_regions(app) | ||
bad_regions = self.get_bad_regions(app, regions) | ||
|
||
self.write_output('App Slug: %s' % app_slug) | ||
self.write_output('App Status: %s' % unicode( | ||
amo.STATUS_CHOICES.get(app.status))) | ||
self.write_output('App Id: %s' % app.pk) | ||
self.write_output(DIVIDER) | ||
self.write_output('id | region.name') | ||
self.write_output(DIVIDER) | ||
|
||
has_bad_region = False | ||
for region_id in regions: | ||
region_name = self._region_name(region_id) | ||
asterisk = '' | ||
if region_id in bad_regions: | ||
has_bad_region = True | ||
asterisk = ' *' | ||
|
||
self.write_output('%s | %s%s' % (str(region_id).ljust(2), | ||
region_name, asterisk)) | ||
|
||
if has_bad_region: | ||
self.write_output('* Inappropriate region') | ||
|
||
def handle(self, *args, **options): | ||
self.include_exclude_region = False | ||
|
||
if not args: | ||
raise CommandError('An app_slug is required.') | ||
|
||
if len(args) > 1: | ||
raise CommandError('Only a single app_slug is accepted.') | ||
|
||
app_slug = args[0] | ||
|
||
# Look up the app by slug. | ||
try: | ||
app = Webapp.objects.get(app_slug=app_slug, | ||
premium_type__in=amo.ADDON_HAS_PAYMENTS) | ||
except Webapp.DoesNotExist: | ||
raise CommandError('Paid app with slug %s not ' | ||
'found.' % app_slug) | ||
|
||
# Bail if the app doesn't have a price. | ||
if (app.premium_type != amo.ADDON_FREE_INAPP and | ||
not app.has_premium() and | ||
not getattr('app.premium', 'price', False)): | ||
raise CommandError("App %s doesn't have a price" % app_slug) | ||
|
||
# Outputs the region info. | ||
self.output_regions(app, app_slug) | ||
|
||
# Handle including a region by deleting an exlusion record for the app. | ||
include_region_id = options.get('include_region_id') | ||
if include_region_id: | ||
self.include_region(app, app_slug, include_region_id) | ||
|
||
# Handle an exclusions record by adding an exclusion record for | ||
# the app. | ||
exclude_region_id = options.get('exclude_region_id') | ||
if exclude_region_id: | ||
self.exclude_region(app, app_slug, exclude_region_id) | ||
|
||
# If we've include/excluded a region show the regions now. | ||
if self.include_exclude_region: | ||
self.write_output() | ||
self.write_output('Regions are now as follows:') | ||
self.output_regions(app, app_slug) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
from StringIO import StringIO | ||
|
||
from django.core.management import call_command | ||
from django.core.management.base import CommandError | ||
|
||
from nose.tools import eq_, ok_, raises | ||
from mock import patch | ||
|
||
import amo | ||
import amo.tests | ||
from market.models import AddonPremium, Price | ||
from mkt.developers.management.commands import ( | ||
check_paid_app_regions) | ||
from mkt.regions import (ALL_REGION_IDS, REGIONS_CHOICES_ID_DICT, | ||
PL, US, WORLDWIDE) | ||
from mkt.site.fixtures import fixture | ||
from mkt.webapps.models import AddonExcludedRegion as AER, Webapp | ||
|
||
|
||
class TestRegionManagmentCommand(amo.tests.TestCase): | ||
fixtures = fixture('webapp_337141', 'prices') | ||
|
||
@raises(CommandError) | ||
def test_unknown_slug(self): | ||
check_paid_app_regions.Command().handle('whatever') | ||
|
||
@patch('mkt.developers.forms.ALL_PAID_REGION_IDS', new=[PL.id, US.id]) | ||
@patch('sys.stdout', new_callable=StringIO) | ||
def test_app_has_bad_regions(self, mock_stdout): | ||
app = Webapp.objects.get(id=337141) | ||
app.update(premium_type=amo.ADDON_PREMIUM) | ||
price = Price.objects.get(id=1) | ||
AddonPremium.objects.create(addon=app, price=price) | ||
call_command('check_paid_app_regions', app.app_slug) | ||
# From the fixture poland is the only ok region | ||
# for the price. | ||
stdout_val = mock_stdout.getvalue() | ||
assert 'Poland *' not in stdout_val | ||
assert '* Inappropriate region' in stdout_val | ||
|
||
for region_id in ALL_REGION_IDS: | ||
region_id = int(region_id) | ||
if region_id in (PL.id, US.id): | ||
continue | ||
region_name = unicode(REGIONS_CHOICES_ID_DICT.get( | ||
region_id).name) | ||
ok_('%s *' % region_name in stdout_val, | ||
'%s not present' % region_name) | ||
|
||
@raises(CommandError) | ||
def test_premium_no_price(self): | ||
app = Webapp.objects.get(id=337141) | ||
app.update(premium_type=amo.ADDON_PREMIUM) | ||
check_paid_app_regions.Command().handle(app.app_slug) | ||
|
||
@patch('sys.stdout', new_callable=StringIO) | ||
def test_include_region_by_id(self, mock_stdout): | ||
app = Webapp.objects.get(id=337141) | ||
app.update(premium_type=amo.ADDON_PREMIUM) | ||
price = Price.objects.get(id=1) | ||
AddonPremium.objects.create(addon=app, | ||
price=price) | ||
AER.objects.create(addon=app, region=WORLDWIDE.id) | ||
eq_(len(AER.objects.all()), 1) | ||
call_command('check_paid_app_regions', app.app_slug, | ||
include_region_id=WORLDWIDE.id) | ||
eq_(AER.objects.all().exists(), False) | ||
|
||
@patch('sys.stdout', new_callable=StringIO) | ||
def test_exclude_region_by_id(self, mock_stdout): | ||
app = Webapp.objects.get(id=337141) | ||
app.update(premium_type=amo.ADDON_PREMIUM) | ||
price = Price.objects.get(id=1) | ||
AddonPremium.objects.create(addon=app, | ||
price=price) | ||
eq_(len(AER.objects.all()), 0) | ||
call_command('check_paid_app_regions', app.app_slug, | ||
exclude_region_id=WORLDWIDE.id) | ||
eq_(AER.objects.get(addon=app).id, WORLDWIDE.id) | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong. |
||
|
||
@patch('sys.stdout', new_callable=StringIO) | ||
@patch('mkt.developers.forms.ALL_PAID_REGION_IDS', new=[PL.id, US.id]) | ||
def test_free_with_inapp(self, mock_stdout): | ||
app = Webapp.objects.get(id=337141) | ||
app.update(premium_type=amo.ADDON_FREE_INAPP) | ||
call_command('check_paid_app_regions', app.app_slug) | ||
# From the fixture poland is the only ok region | ||
# for the price. | ||
stdout_val = mock_stdout.getvalue() | ||
assert 'Poland *' not in stdout_val | ||
assert 'United States *' not in stdout_val | ||
assert '* Inappropriate region' in stdout_val | ||
|
||
@raises(CommandError) | ||
def test_free_app(self): | ||
app = Webapp.objects.get(id=337141) | ||
app.update(premium_type=amo.ADDON_FREE) | ||
eq_(app.premium_type, amo.ADDON_FREE) | ||
check_paid_app_regions.Command().handle(app.app_slug) |
this caused a test failure because you're comparing the primary key
id
not theregion
id. heh, it's funny because locally this passed because theAER
was1
as is the id forWORLDWIDE
, but on jenkins the primary key was different.anyhow, fixed!