From 594b2defb70aef1f806d31451027d93bae413822 Mon Sep 17 00:00:00 2001 From: Vasyl Dizhak Date: Thu, 2 Nov 2017 22:25:18 +0100 Subject: [PATCH] ref #1, Implement bonus/reward system and click statistics --- .travis.yml | 6 +- referral_module/admin.py | 6 + referral_module/middleware.py | 28 ++ .../migrations/0004_auto_20171102_1938.py | 37 +++ .../migrations/0005_auto_20171102_1957.py | 24 ++ referral_module/models.py | 70 ++++- referral_module/tests.py | 6 - referral_module/tests/__init__.py | 0 referral_module/tests/factories.py | 28 ++ referral_module/tests/test_models.py | 13 + referral_module/tests/test_utils.py | 18 ++ referral_module/tests/test_views.py | 15 + referral_module/utils.py | 7 + requirements-dev.txt | 4 - requirements.in | 16 ++ requirements.txt | 268 ++++++++++++++++-- tox.ini | 3 +- 17 files changed, 497 insertions(+), 52 deletions(-) create mode 100644 referral_module/migrations/0004_auto_20171102_1938.py create mode 100644 referral_module/migrations/0005_auto_20171102_1957.py delete mode 100644 referral_module/tests.py create mode 100644 referral_module/tests/__init__.py create mode 100644 referral_module/tests/factories.py create mode 100644 referral_module/tests/test_models.py create mode 100644 referral_module/tests/test_utils.py create mode 100644 referral_module/tests/test_views.py create mode 100644 referral_module/utils.py delete mode 100644 requirements-dev.txt create mode 100644 requirements.in diff --git a/.travis.yml b/.travis.yml index dd8db91..90f1e84 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,9 +18,13 @@ after_success: - codecov -e TOXENV,DJANGO services: - - postgresql: "9.6" + - postgresql + +addons: + postgresql: "9.6" before_script: + - psql -c 'SHOW SERVER_VERSION;' -U postgres - psql -c 'create database test_db;' -U postgres notifications: email: false diff --git a/referral_module/admin.py b/referral_module/admin.py index 1f3df3c..030a6bd 100644 --- a/referral_module/admin.py +++ b/referral_module/admin.py @@ -9,6 +9,7 @@ class CampaignAdmin(admin.ModelAdmin): class UserReferrerAdmin(admin.ModelAdmin): + list_display = ('__str__', 'reward', ) raw_id_fields = ('user', 'campaign', ) @@ -17,6 +18,11 @@ class ReferrerAdmin(admin.ModelAdmin): raw_id_fields = ('registered_user', 'user_referrer', ) +class UserReferrerStatsAdmin(admin.ModelAdmin): + raw_id_fields = ('user_referrer', ) + + admin.site.register(models.Campaign, CampaignAdmin) admin.site.register(models.UserReferrer, UserReferrerAdmin) admin.site.register(models.Referrer, ReferrerAdmin) +admin.site.register(models.UserReferrerStats, UserReferrerStatsAdmin) diff --git a/referral_module/middleware.py b/referral_module/middleware.py index 3ed2c8d..af61b19 100644 --- a/referral_module/middleware.py +++ b/referral_module/middleware.py @@ -1,6 +1,11 @@ +from decimal import Decimal +from django.db.models.expressions import F from django.http import HttpResponseBadRequest from . import constants +from .models import ( + UserReferrerStats, UserReferrer +) class ReferrerStoreMiddleware(object): @@ -23,5 +28,28 @@ def __call__(self, request): campaign_key, user_referral_key ) ) + self.update_referrer_data(request, user_referral_key) return response + + def update_referrer_data(self, request, user_referral_key): + # store statistics + meta = request.META + try: + user_referrer = UserReferrer.objects.get( + key=user_referral_key + ) + UserReferrerStats.objects.create(**{ + 'user_referrer': user_referrer, + 'context': { + 'HTTP_ACCEPT_LANGUAGE': meta.get('HTTP_ACCEPT_LANGUAGE'), + 'HTTP_USER_AGENT': meta.get('HTTP_USER_AGENT'), + }, + 'ip': request.META['REMOTE_ADDR'], + }) + + # update user's reward + user_referrer.reward = F('reward') + Decimal(user_referrer.campaign.bonus_policy['click']) + user_referrer.save(update_fields=['reward', ]) + except UserReferrer.DoesNotExist: + pass diff --git a/referral_module/migrations/0004_auto_20171102_1938.py b/referral_module/migrations/0004_auto_20171102_1938.py new file mode 100644 index 0000000..37d58f6 --- /dev/null +++ b/referral_module/migrations/0004_auto_20171102_1938.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.6 on 2017-11-02 19:38 +from __future__ import unicode_literals + +import django.contrib.postgres.fields.jsonb +from django.db import migrations, models +import django.db.models.deletion +import referral_module.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('referral_module', '0003_auto_20171016_1258'), + ] + + operations = [ + migrations.CreateModel( + name='UserReferrerStats', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created', models.DateTimeField(auto_now_add=True)), + ('event_type', models.CharField(choices=[('click', 'click')], db_index=True, default=referral_module.models.StatsType('click'), max_length=50)), + ('ip', models.GenericIPAddressField()), + ('context', django.contrib.postgres.fields.jsonb.JSONField(blank=True, null=True)), + ('user_referrer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='referral_module.UserReferrer', verbose_name='stats')), + ], + options={ + 'abstract': False, + }, + ), + migrations.AddField( + model_name='campaign', + name='bonus_policy', + field=django.contrib.postgres.fields.jsonb.JSONField(default=referral_module.models.default_bonus_policy), + ), + ] diff --git a/referral_module/migrations/0005_auto_20171102_1957.py b/referral_module/migrations/0005_auto_20171102_1957.py new file mode 100644 index 0000000..653b828 --- /dev/null +++ b/referral_module/migrations/0005_auto_20171102_1957.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.6 on 2017-11-02 19:57 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('referral_module', '0004_auto_20171102_1938'), + ] + + operations = [ + migrations.AlterModelOptions( + name='userreferrerstats', + options={'verbose_name': 'User referrer stats', 'verbose_name_plural': 'User referrer stats'}, + ), + migrations.AddField( + model_name='userreferrer', + name='reward', + field=models.DecimalField(decimal_places=3, default=0, max_digits=10), + ), + ] diff --git a/referral_module/models.py b/referral_module/models.py index 60e1260..2346111 100644 --- a/referral_module/models.py +++ b/referral_module/models.py @@ -1,13 +1,19 @@ import logging + +from decimal import Decimal + +from django.db.models import F from random import choice from django.contrib.auth.models import User +from django.contrib.postgres.fields import JSONField from django.db import models, transaction from django.db.utils import IntegrityError from django.dispatch import receiver from django.http import HttpRequest from registration.signals import user_registered +from referral_module.utils import ChoiceEnum from . import constants logger = logging.getLogger('bootcamp.{}'.format(__file__)) @@ -20,6 +26,13 @@ class Meta: abstract = True +def default_bonus_policy(): + return { + 'click': 0, + 'registration': 0, + } + + class Campaign(CreatedDateModel): key = models.CharField( verbose_name='Campaign key', @@ -32,6 +45,9 @@ class Campaign(CreatedDateModel): is_active = models.BooleanField( default=True ) + bonus_policy = JSONField( + default=default_bonus_policy + ) updated = models.DateTimeField(auto_now=True) class Meta: @@ -50,6 +66,10 @@ class UserReferrer(CreatedDateModel): max_length=7, unique=True ) + reward = models.DecimalField( + max_digits=10, decimal_places=3, + default=0 + ) class Meta: verbose_name = 'User referrer' @@ -59,15 +79,18 @@ class Meta: def save(self, *args, **kwargs): attempts = 5 - while not self.key and attempts: - self.key = "".join( - [choice(constants.KEY_ALPHABET) for i in range(7)] - ) - with transaction.atomic(): - try: - super(UserReferrer, self).save(*args, **kwargs) - except IntegrityError as e: - attempts -= 1 + if not self.key: + while not self.key and attempts: + self.key = "".join( + [choice(constants.KEY_ALPHABET) for i in range(7)] + ) + with transaction.atomic(): + try: + super(UserReferrer, self).save(*args, **kwargs) + except IntegrityError as e: + attempts -= 1 + else: + super(UserReferrer, self).save(*args, **kwargs) def __str__(self): return 'Campaign: {} {} -key-> {}'.format( @@ -75,6 +98,31 @@ def __str__(self): ) +class StatsType(ChoiceEnum): + click = 'click' + + +class UserReferrerStats(CreatedDateModel): + user_referrer = models.ForeignKey(UserReferrer, verbose_name='stats') + event_type = models.CharField( + choices=StatsType.choices(), default=StatsType.click, + max_length=50, db_index=True + ) + ip = models.GenericIPAddressField() + context = JSONField( + blank=True, null=True + ) + + class Meta: + verbose_name = 'User referrer stats' + verbose_name_plural = 'User referrer stats' + + def __str__(self): + return "{} event at {}".format( + self.event_type, self.created + ) + + class Referrer(CreatedDateModel): registered_user = models.ForeignKey('auth.User') user_referrer = models.ForeignKey(UserReferrer) @@ -108,6 +156,10 @@ def associate_registered_user_with_referral(sender, **kwargs): registered_user=user, user_referrer=user_referrer ) + + # update user's reward + user_referrer.reward = F('reward') + Decimal(user_referrer.campaign.bonus_policy['registration']) + user_referrer.save(update_fields=['reward', ]) logger.debug( 'New referrer %s', referrer ) diff --git a/referral_module/tests.py b/referral_module/tests.py deleted file mode 100644 index 0c15e8e..0000000 --- a/referral_module/tests.py +++ /dev/null @@ -1,6 +0,0 @@ -from django.test import TestCase - - -class CampaignListView(TestCase): - def test_page_access(self): - pass diff --git a/referral_module/tests/__init__.py b/referral_module/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/referral_module/tests/factories.py b/referral_module/tests/factories.py new file mode 100644 index 0000000..c71a90f --- /dev/null +++ b/referral_module/tests/factories.py @@ -0,0 +1,28 @@ +from django.contrib.auth.models import User +import factory as factory_boy + +from referral_module import models + + +class UserFactory(factory_boy.DjangoModelFactory): + username = factory_boy.Sequence(lambda n: 'user-{}'.format(n)) + email = factory_boy.Sequence(lambda n: 'user-{}@moneypark.ch'.format(n)) + + class Meta: + model = User + + +class CampaignFactory(factory_boy.DjangoModelFactory): + key = factory_boy.Sequence(lambda n: 'key-{}'.format(n)) + is_active = True + + class Meta: + model = models.Campaign + + +class UserReferrerFactory(factory_boy.DjangoModelFactory): + campaign = factory_boy.SubFactory(CampaignFactory) + user = factory_boy.SubFactory(UserFactory) + + class Meta: + model = models.UserReferrer diff --git a/referral_module/tests/test_models.py b/referral_module/tests/test_models.py new file mode 100644 index 0000000..916f4ca --- /dev/null +++ b/referral_module/tests/test_models.py @@ -0,0 +1,13 @@ +from django.test import TestCase + +from .factories import UserReferrerFactory + + +class UserReferrerModelTestCase(TestCase): + def test_key_autogeneration(self): + user_referrer = UserReferrerFactory() + self.assertTrue(user_referrer.key) + + user_referrer.key = None + user_referrer.save() + self.assertTrue(user_referrer.key) diff --git a/referral_module/tests/test_utils.py b/referral_module/tests/test_utils.py new file mode 100644 index 0000000..eaeaf51 --- /dev/null +++ b/referral_module/tests/test_utils.py @@ -0,0 +1,18 @@ +from django.test import TestCase + +from referral_module.utils import ChoiceEnum + + +class ColorChoice(ChoiceEnum): + green = 1 + red = 2 + + +class ChoicesEnumTestCase(TestCase): + + def test_choices_classmethod(self): + assert ColorChoice.choices() == ( + ('green', 1), + ('red', 2) + ) + assert ColorChoice.green.value == 1 diff --git a/referral_module/tests/test_views.py b/referral_module/tests/test_views.py new file mode 100644 index 0000000..b574dd6 --- /dev/null +++ b/referral_module/tests/test_views.py @@ -0,0 +1,15 @@ +from django.test import TestCase +from django.urls import reverse + + +class CampaignListTestCase(TestCase): + def setUp(self): + self.url = reverse('campaign-list') + + def test_page_access(self): + response = self.client.get(self.url) + assert response.status_code == 200 + self.assertTemplateUsed( + response, + 'campaign/list.html' + ) diff --git a/referral_module/utils.py b/referral_module/utils.py new file mode 100644 index 0000000..9ff30c0 --- /dev/null +++ b/referral_module/utils.py @@ -0,0 +1,7 @@ +from enum import Enum + + +class ChoiceEnum(Enum): + @classmethod + def choices(cls): + return tuple((x.name, x.value) for x in cls) diff --git a/requirements-dev.txt b/requirements-dev.txt deleted file mode 100644 index 033b7af..0000000 --- a/requirements-dev.txt +++ /dev/null @@ -1,4 +0,0 @@ -pytest -pytest-django -pytest-cov -codecov diff --git a/requirements.in b/requirements.in new file mode 100644 index 0000000..4f8d1e5 --- /dev/null +++ b/requirements.in @@ -0,0 +1,16 @@ +django +django-debug-toolbar +django-extensions +django-registration +flake8 +ipdb +psycopg2 +pyflakes +werkzeug + +tox +pytest +pytest-cov +pytest-django +codecov +factory_boy diff --git a/requirements.txt b/requirements.txt index 0b523f8..06accc8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,30 +1,238 @@ -appnope==0.1.0 -confusable-homoglyphs==2.0.2 -decorator==4.1.2 -Django==1.11.6 -django-debug-toolbar==1.8 -django-extensions==1.9.1 -django-registration==2.3 -flake8==3.4.1 -hashids==1.2.0 -ipdb==0.10.3 -ipython==6.2.1 -ipython-genutils==0.2.0 -jedi==0.11.0 -mccabe==0.6.1 -parso==0.1.0 -pexpect==4.2.1 -pickleshare==0.7.4 -prompt-toolkit==1.0.15 -psycopg2==2.7.3.1 -ptyprocess==0.5.2 -pycodestyle==2.3.1 -pyflakes==1.5.0 -Pygments==2.2.0 -pytz==2017.2 -simplegeneric==0.8.1 -six==1.11.0 -sqlparse==0.2.4 -traitlets==4.3.2 -wcwidth==0.1.7 -Werkzeug==0.12.2 +# +# This file is autogenerated by pip-compile +# To update, run: +# +# pip-compile --generate-hashes --output-file requirements.txt requirements.in +# +appnope==0.1.0 \ + --hash=sha256:5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0 \ + --hash=sha256:8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71 \ + # via ipython +certifi==2017.7.27.1 \ + --hash=sha256:40523d2efb60523e113b44602298f0960e900388cf3bb6043f645cf57ea9e3f5 \ + --hash=sha256:54a07c09c586b0e4c619f02a5e94e36619da8e2b053e20f594348c0611803704 \ + # via requests +chardet==3.0.4 \ + --hash=sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae \ + --hash=sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691 \ + # via requests +codecov==2.0.9 \ + --hash=sha256:ad82f054837b02081f86ed1eb6c04cddc029fbc734eaf92ff73da1db3a79188b \ + --hash=sha256:db1c182ca896244d8644d8410a33f6f6dd1cc24d80209907a65077445923f00c +confusable-homoglyphs==2.0.2 \ + --hash=sha256:9c042d2c952eb2cfb26c0bfb8ea8c2105fea4817c16ea6eaf55eb01503aeaae1 \ + --hash=sha256:e653f557f6728864e70c24dd4e33750595304f2d193a1db9f37960c5c4b50d6c \ + # via django-registration +coverage==4.4.1 \ + --hash=sha256:0f1883eab9c19aa243f51308751b8a2a547b9b817b721cc0ecf3efb99fafbea7 \ + --hash=sha256:13ef9f799c8fb45c446a239df68034de3a6f3de274881b088bebd7f5661f79f8 \ + --hash=sha256:16db2c69a1acbcb3c13211e9f954e22b22a729909d81f983b6b9badacc466eda \ + --hash=sha256:17709e22e4c9f5412ba90f446fb13b245cc20bf4a60377021bbff6c0f1f63e7c \ + --hash=sha256:2a08e978f402696c6956eee9d1b7e95d3ad042959b71bafe1f3e4557cbd6e0ac \ + --hash=sha256:381be5d31d3f0d912334cf2c159bc7bea6bfe6b0e3df6061a3bf2bf88359b1f6 \ + --hash=sha256:4047dc83773869701bde934fb3c4792648eda7c0e008a77a0aec64157d246801 \ + --hash=sha256:483ed14080c5301048128bb027b77978c632dd9e92e3ecb09b7e28f5b92abfcf \ + --hash=sha256:53770b20ac5b4a12e99229d4bae57af0945be87cc257fce6c6c7571a39f0c5d4 \ + --hash=sha256:57f510bb16efaec0b6f371b64a8000c62e7e3b3e48e8b0a5745ade078d849814 \ + --hash=sha256:5dea90ed140e7fa9bc00463313f9bc4a6e6aff297b4969615e7a688615c4c4d2 \ + --hash=sha256:700040c354f0230287906b1276635552a3def4b646e0145555bc9e2e5da9e365 \ + --hash=sha256:7a9c44400ee0f3b4546066e0710e1250fd75831adc02ab99dda176ad8726f424 \ + --hash=sha256:7f1eacae700c66c3d7362a433b228599c9d94a5a3a52613dddd9474e04deb6bc \ + --hash=sha256:83a477ac4f55a6ef59552683a0544d47b68a85ce6a80fd0ca6b3dc767f6495fb \ + --hash=sha256:83e955b975666b5a07d217135e7797857ce844eb340a99e46cc25525120417c4 \ + --hash=sha256:8801880d32f11b6df11c32a961e186774b4634ae39d7c43235f5a24368a85f07 \ + --hash=sha256:9681efc2d310cfc53863cc6f63e88ebe7a48124550fa822147996cb09390b6ab \ + --hash=sha256:97a7ec51cdde3a386e390b159b20f247ccb478084d925c75f1faa3d26c01335e \ + --hash=sha256:a2f7106d1167825c4115794c2ba57cc3b15feb6183db5328fa66f94c12902d8b \ + --hash=sha256:a5ed27ad3e8420b2d6b625dcbd3e59488c14ccc06030167bcf14ffb0f4189b77 \ + --hash=sha256:b7f7283eb7badd2b8a9c6a9d6eeca200a0a24db6be79baee2c11398f978edcaa \ + --hash=sha256:c1456f66c536010cf9e4633a8853a9153e8fd588393695295afd4d0fc16c1d74 \ + --hash=sha256:c3e2736664a6074fc9bd54fb643f5af0fc60bfedb2963b3d3f98c7450335e34c \ + --hash=sha256:c5a205b4da3c624f5119dc4d84240789b5906bb8468902ec22dcc4aad8aa4638 \ + --hash=sha256:c820a533a943ebc860acc0ce6a00dd36e0fdf2c6f619ff8225755169428c5fa2 \ + --hash=sha256:d7b70b7b4eb14d0753d33253fe4f121ca99102612e2719f0993607deb30c6f33 \ + --hash=sha256:dfb011587e2b7299112f08a2a60d2601706aac9abde37aa1177ea825adaed923 \ + --hash=sha256:dfd35f1979da31bcabbe27bcf78d4284d69870731874af629082590023a77336 \ + --hash=sha256:e00fe141e22ce6e9395aa24d862039eb180c6b7e89df0bbaf9765e9aebe560a9 \ + --hash=sha256:ec596e4401553caa6dd2e3349ce47f9ef82c1f1bcba5d8ac3342724f0df8d6ff \ + --hash=sha256:ef43a06a960b46c73c018704051e023ee6082030f145841ffafc8728039d5a88 \ + --hash=sha256:ef574ab9640bcfa2f3c671831faf03f65788945fdf8efa4d4a1fffc034838e2a \ + --hash=sha256:f9e83b39d29c2815a38e4118d776b482d4082b5bf9c9147fbc99a3f83abe480a \ + # via codecov, pytest-cov +decorator==4.1.2 \ + --hash=sha256:7cb64d38cb8002971710c8899fbdfb859a23a364b7c99dab19d1f719c2ba16b5 \ + --hash=sha256:95a26b17806e284452bfd97fa20aa1e8cb4ee23542bda4dbac5e4562aa1642cd \ + # via ipython, traitlets +django-debug-toolbar==1.8 \ + --hash=sha256:0b4d2b1ac49a8bc5604518e8e20f56c1c08c0c4873336107e7c773c42537876b \ + --hash=sha256:e9f08b94f9423ac76cfc287151182bbaddbe7521ae32bef9f9863e2ac58018d3 +django-extensions==1.9.1 \ + --hash=sha256:34823a9ba7f9e0e5d15e44536f02bb98a75f16bb27fbeafe659cddac4a2948f5 \ + --hash=sha256:b562fd29acbf5d9b18a1c8605f1009719357b739e3a869a4bd4c7be223ace1cc +django-registration==2.3 \ + --hash=sha256:4acd6ccdda4bfe4a568936d22678589529434f2ff702d61d70d3ca1512cbb141 \ + --hash=sha256:694a23336e5c68059834910646fbc6b6fdf0ea68fdaa5f890f329be40bdd9c8b +django==1.11.6 \ + --hash=sha256:7ab6a9c798a5f9f359ee6da3677211f883fb02ef32cebe9b29751eb7a871febf \ + --hash=sha256:c3b42ca1efa1c0a129a9e863134cc3fe705c651dea3a04a7998019e522af0c60 +factory-boy==2.9.2 \ + --hash=sha256:340c602f6fed2d8dd160397f28f2c0219e937f0488460450e8e5bf2add020ed6 \ + --hash=sha256:b8334bcc3c5b10af9a83ab5b8786f98cb322638dc1e6d320cad01c7f2b420e87 +faker==0.8.6 \ + --hash=sha256:3c98dd04bba667ecfddb77fcd0dfd19b376b258d21beeaf5b95578826e275a83 \ + --hash=sha256:f2d7f4b0956bd610645aa7c62ae6a30fc323936b6298e521847410509236f389 \ + # via factory-boy +flake8==3.4.1 \ + --hash=sha256:c20044779ff848f67f89c56a0e4624c04298cd476e25253ac0c36f910a1a11d8 \ + --hash=sha256:f1a9d8886a9cbefb52485f4f4c770832c7fb569c084a9a314fb1eaa37c0c2c86 +idna==2.6 \ + --hash=sha256:2c6a5de3089009e3da7c5dde64a141dbc8551d5b7f6cf4ed7c2568d0cc520a8f \ + --hash=sha256:8c7309c718f94b3a625cb648ace320157ad16ff131ae0af362c9f21b80ef6ec4 \ + # via requests +ipdb==0.10.3 \ + --hash=sha256:9ea256b4280fbe12840fb9dfc3ce498c6c6de03352eca293e4400b0dfbed2b28 +ipython-genutils==0.2.0 \ + --hash=sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8 \ + --hash=sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8 \ + # via traitlets +ipython==6.2.1 \ + --hash=sha256:51c158a6c8b899898d1c91c6b51a34110196815cc905f9be0fa5878e19355608 \ + --hash=sha256:fcc6d46f08c3c4de7b15ae1c426e15be1b7932bcda9d83ce1a4304e8c1129df3 \ + # via ipdb +jedi==0.11.0 \ + --hash=sha256:3af518490ffcd00a3074c135b42511e081575e9abd115c216a34491411ceebb0 \ + --hash=sha256:f6d5973573e76b1fd2ea75f6dcd6445d02d41ff3af5fc61b275b4e323d1dd396 \ + # via ipython +mccabe==0.6.1 \ + --hash=sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42 \ + --hash=sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f \ + # via flake8 +parso==0.1.0 \ + --hash=sha256:b573acb69f66a970197b5fdbbdfad3b8a417a520e383133b2b4e708f104bfc9a \ + --hash=sha256:c5279916bb417aa2bf634648ff895cf35dce371d7319744884827bfad06f8d7b \ + # via jedi +pexpect==4.2.1 \ + --hash=sha256:3d132465a75b57aa818341c6521392a06cc660feb3988d7f1074f39bd23c9a92 \ + --hash=sha256:f853b52afaf3b064d29854771e2db509ef80392509bde2dd7a6ecf2dfc3f0018 \ + # via ipython +pickleshare==0.7.4 \ + --hash=sha256:84a9257227dfdd6fe1b4be1319096c20eb85ff1e82c7932f36efccfe1b09737b \ + --hash=sha256:c9a2541f25aeabc070f12f452e1f2a8eae2abd51e1cd19e8430402bdf4c1d8b5 \ + # via ipython +pluggy==0.5.2 \ + --hash=sha256:bd60171dbb250fdebafad46ed16d97065369da40568ae948ef7117eee8536e94 \ + # via tox +prompt-toolkit==1.0.15 \ + --hash=sha256:1df952620eccb399c53ebb359cc7d9a8d3a9538cb34c5a1344bdbeb29fbcc381 \ + --hash=sha256:3f473ae040ddaa52b52f97f6b4a493cfa9f5920c255a12dc56a7d34397a398a4 \ + --hash=sha256:858588f1983ca497f1cf4ffde01d978a3ea02b01c8a26a8bbc5cd2e66d816917 \ + # via ipython +psycopg2==2.7.3.1 \ + --hash=sha256:04a5b2805c620ddecdff33e015631cc8d7ea8dd01e1fcc930bfe002fdc26b9e0 \ + --hash=sha256:0bd0b1cf81eb6d74a77199570d5ce097fb3c6b8e57acc1edd328cef5c552f98a \ + --hash=sha256:16482d050db68503abbb693795d75e9fca287a00f662376759825404533ca87c \ + --hash=sha256:279dbf220c94c9f73aefac719ea3b9550ca791389bc9184c15e835516bc8428d \ + --hash=sha256:35d1fec112337c2607c8ca17dee3c2f8455e07dd69d140ff8e86ef1240dace3d \ + --hash=sha256:39369e40bc3e062da5da93ce5f1e7f9bed95e9a60cb6f003d316f2a834722e2d \ + --hash=sha256:3ab693b907b2c7a34c1dca198cfc454516cfd75440fd913bb372da6f70d6db55 \ + --hash=sha256:3c99c88216e0fc62113a1177aae9db18a533274370e99a4537b433b87b319360 \ + --hash=sha256:3fbba0dd7f3ac458f355dcfc4d7d9cd082c19748e453bcd81bf9d8bc14260c01 \ + --hash=sha256:471e3140e1cb241ecb53602cdc98fe305b82c854cfa53c3e343d642683dc96c7 \ + --hash=sha256:4cca242df228364b4de6241c54553301856bc253d7f21e15009b11c812eb7b5c \ + --hash=sha256:4d7c872d9c85745964cf8efabba3fb1a6010b345c18d0e2b620509f0444aa729 \ + --hash=sha256:4db0de7d6236acbf7d020926489b6c4b98e845ba98df11057f22d54866e26b20 \ + --hash=sha256:4fbe2c29f6d4c8d9eac5fc3c42c42036e7cc58e225036130b7713afa72489aac \ + --hash=sha256:604c49bf196c654c417ade1dc765bd23fe9bc3392d9a8c7184a7077142d621b3 \ + --hash=sha256:6194e81d8839b636118f5275c53be3c70eb3c3abcf836de675c34b20c06025ea \ + --hash=sha256:891a70235c202158780fc0ab98d7ca2a7ed1625c26725b15119d47b2d852a5c6 \ + --hash=sha256:8d2003d23d8fc59f0f0a2e73c13baa41581006b8227a9d82bbdc1aa233285ba4 \ + --hash=sha256:99a298b9030f8fd36f885c5d0661e4d5a059136a541bb6c4d7d1100e38da3cd1 \ + --hash=sha256:9a0a74a6f20d82c389095c88d4d281e66eb3ffbc09adf543dcec4bec22436569 \ + --hash=sha256:9b7b16e26448b43cf167f785d8b5345007731ebf153a510e12dae826800caa65 \ + --hash=sha256:a4d7134058e8869d785c0be386cd702fe2a4be14b678d7571a51045e1bbad862 \ + --hash=sha256:b7646f7bdb42ba3cf7ef9f500f6514b5e413d25c5b3093d70e6ba52df417a83c \ + --hash=sha256:c939b238cbc18e786909d20277c9f051241696ebb03ca9e3490094f526ce69a7 \ + --hash=sha256:df70e0a387b0145a7a80de67d43d84e1c5a24a33bbfaedb96271876d573e5fb6 \ + --hash=sha256:e26aca5eb272869fdf8e55111f36026517c1c0799eb7ef1ddf67d4412affe1ef \ + --hash=sha256:e80a1ae04218e854a5d71085f9498c8c979033ca855cedeed3afc5d976c348ce \ + --hash=sha256:ebee4f59803eda1ed4035649469a713818dbf5b6de2e12396edd9fa228727c20 \ + --hash=sha256:f07791ee5793621bfaaa844f731529cd72321280f94e8dc3bd4ef524d20f58f2 \ + --hash=sha256:f74e50341f45fb9bcd3598c11b5774c3e4349ee38cf15c342cc7075c73ef1f95 \ + --hash=sha256:face605eea5826fa36600c04447b61674992bbd5eb177f66f86f61a04436dbd9 +ptyprocess==0.5.2 \ + --hash=sha256:e64193f0047ad603b71f202332ab5527c5e52aa7c8b609704fc28c0dc20c4365 \ + --hash=sha256:e8c43b5eee76b2083a9badde89fd1bbce6c8942d1045146e100b7b5e014f4f1a \ + # via pexpect +py==1.4.34 \ + --hash=sha256:0f2d585d22050e90c7d293b6451c83db097df77871974d90efd5a30dc12fcde3 \ + --hash=sha256:2ccb79b01769d99115aa600d7eed99f524bf752bba8f041dc1c184853514655a \ + # via pytest, tox +pycodestyle==2.3.1 \ + --hash=sha256:682256a5b318149ca0d2a9185d365d8864a768a28db66a84a2ea946bcc426766 \ + --hash=sha256:6c4245ade1edfad79c3446fadfc96b0de2759662dc29d07d80a6f27ad1ca6ba9 \ + # via flake8 +pyflakes==1.5.0 \ + --hash=sha256:aa0d4dff45c0cc2214ba158d29280f8fa1129f3e87858ef825930845146337f4 \ + --hash=sha256:cc5eadfb38041f8366128786b4ca12700ed05bbf1403d808e89d57d67a3875a7 +pygments==2.2.0 \ + --hash=sha256:78f3f434bcc5d6ee09020f92ba487f95ba50f1e3ef83ae96b9d5ffa1bab25c5d \ + --hash=sha256:dbae1046def0efb574852fab9e90209b23f556367b5a320c0bcb871c77c3e8cc \ + # via ipython +pytest-cov==2.5.1 \ + --hash=sha256:03aa752cf11db41d281ea1d807d954c4eda35cfa1b21d6971966cc041bbf6e2d \ + --hash=sha256:890fe5565400902b0c78b5357004aab1c814115894f4f21370e2433256a3eeec +pytest-django==3.1.2 \ + --hash=sha256:00995c2999b884a38ae9cd30a8c00ed32b3d38c1041250ea84caf18085589662 \ + --hash=sha256:038ccc5a9daa1b1b0eb739ab7dce54e495811eca5ea3af4815a2a3ac45152309 +pytest==3.2.3 \ + --hash=sha256:27fa6617efc2869d3e969a3e75ec060375bfb28831ade8b5cdd68da3a741dc3c \ + --hash=sha256:81a25f36a97da3313e1125fce9e7bbbba565bc7fec3c5beb14c262ddab238ac1 +python-dateutil==2.6.1 \ + --hash=sha256:891c38b2a02f5bb1be3e4793866c8df49c7d19baabf9c1bad62547e0b4866aca \ + --hash=sha256:95511bae634d69bc7329ba55e646499a842bc4ec342ad54a8cdb65645a0aad3c \ + # via faker +pytz==2017.2 \ + --hash=sha256:d1d6729c85acea5423671382868627129432fba9a89ecbb248d8d1c7a9f01c67 \ + --hash=sha256:f5c056e8f62d45ba8215e5cb8f50dfccb198b4b9fbea8500674f3443e4689589 \ + # via django +requests==2.18.4 \ + --hash=sha256:6a1b267aa90cac58ac3a765d067950e7dbbf75b1da07e895d1f594193a40a38b \ + --hash=sha256:9c443e7324ba5b85070c4a818ade28bfabedf16ea10206da1132edaa6dda237e \ + # via codecov +simplegeneric==0.8.1 \ + --hash=sha256:dc972e06094b9af5b855b3df4a646395e43d1c9d0d39ed345b7393560d0b9173 \ + # via ipython +six==1.11.0 \ + --hash=sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9 \ + --hash=sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb \ + # via django-extensions, faker, prompt-toolkit, python-dateutil, tox, traitlets +sqlparse==0.2.4 \ + --hash=sha256:ce028444cfab83be538752a2ffdb56bc417b7784ff35bb9a3062413717807dec \ + --hash=sha256:d9cf190f51cbb26da0412247dfe4fb5f4098edb73db84e02f9fc21fdca31fed4 \ + # via django-debug-toolbar +text-unidecode==1.0 \ + --hash=sha256:1943a4bfb52f2b5068c8da25cdcfcf28c64c41d891dff70c662927c18ac58052 \ + --hash=sha256:b7a515d2b14d476b35f7701ceac9872737cbb7a8b2cd07431f3cd73837b97a18 \ + # via faker +tox==2.9.1 \ + --hash=sha256:752f5ec561c6c08c5ecb167d3b20f4f4ffc158c0ab78855701a75f5cef05f4b8 \ + --hash=sha256:8af30fd835a11f3ff8e95176ccba5a4e60779df4d96a9dfefa1a1704af263225 +traitlets==4.3.2 \ + --hash=sha256:9c4bd2d267b7153df9152698efb1050a5d84982d3384a37b2c1f7723ba3e7835 \ + --hash=sha256:c6cb5e6f57c5a9bdaa40fa71ce7b4af30298fbab9ece9815b5d995ab6217c7d9 \ + # via ipython +urllib3==1.22 \ + --hash=sha256:06330f386d6e4b195fbfc736b297f58c5a892e4440e54d294d7004e3a9bbea1b \ + --hash=sha256:cc44da8e1145637334317feebd728bd869a35285b93cbb4cca2577da7e62db4f \ + # via requests +virtualenv==15.1.0 \ + --hash=sha256:02f8102c2436bb03b3ee6dede1919d1dac8a427541652e5ec95171ec8adbc93a \ + --hash=sha256:39d88b533b422825d644087a21e78c45cf5af0ef7a99a1fc9fbb7b481e5c85b0 \ + # via tox +wcwidth==0.1.7 \ + --hash=sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e \ + --hash=sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c \ + # via prompt-toolkit +werkzeug==0.12.2 \ + --hash=sha256:903a7b87b74635244548b30d30db4c8947fe64c5198f58899ddcd3a13c23bb26 \ + --hash=sha256:e8549c143af3ce6559699a01e26fa4174f4c591dbee0a499f3cd4c3781cdec3d diff --git a/tox.ini b/tox.ini index 5749039..1080ab4 100644 --- a/tox.ini +++ b/tox.ini @@ -9,7 +9,6 @@ envlist = {py36}-django111 [testenv] -commands = pytest --cov --cov-report=xml +commands = pytest --cov=referral_module --cov-report=xml --cov-report=html deps= -rrequirements.txt - -rrequirements-dev.txt