Cleanup, and better handling in the cancellation flow more work to come

This commit is contained in:
Oliver Marks 2017-10-01 22:59:08 +01:00
parent 2e9a428761
commit c1ed938b04
11 changed files with 115 additions and 68 deletions

View File

@ -1,3 +1,3 @@
#!/bin/sh #!/bin/sh
python manage.py migrate python manage.py migrate
python manage.py runserver_plus 0.0.0.0:8000 while true; do python manage.py runserver_plus 0.0.0.0:8000; sleep 2; done

View File

@ -99,5 +99,50 @@ CAPTCHA = {
WHITENOISE_AUTOREFRESH = True WHITENOISE_AUTOREFRESH = True
WHITENOISE_USE_FINDERS = True WHITENOISE_USE_FINDERS = True
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'filters': {
'require_debug_false': {
'()': 'django.utils.log.RequireDebugFalse'
}
},
'formatters': {
'verbose': {
'format': '%(levelname)s %(asctime)s %(module)s '
'%(process)d %(thread)d %(message)s'
},
},
'handlers': {
'mail_admins': {
'level': 'DEBUG',
'filters': ['require_debug_false'],
'class': 'django.utils.log.AdminEmailHandler'
},
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'verbose',
},
'logfile': {
'level':'DEBUG',
'class':'logging.FileHandler',
'filename': "%s/django.log" % ROOT_DIR,
},
},
'loggers': {
'django.request': {
'handlers': ['mail_admins', 'logfile'],
'level': 'ERROR',
'propagate': True
},
'django.security.DisallowedHost': {
'level': 'ERROR',
'handlers': ['logfile', 'console', 'mail_admins'],
'propagate': True
}
}
}
PAYMENT_PROVIDERS['gocardless']['redirect_url'] = 'http://127.0.0.1:8180' PAYMENT_PROVIDERS['gocardless']['redirect_url'] = 'http://127.0.0.1:8180'
TEMPLATE_DEBUG = False TEMPLATE_DEBUG = False

View File

@ -67,5 +67,8 @@ TEMPLATES[0]['OPTIONS']['loaders'] = [
] ]
DATABASES = { DATABASES = {
'default': {'ENGINE': 'django.db.backends.sqlite3'} 'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(str(ROOT_DIR), 'cache/test_database.db'),
}
} }

View File

@ -53,6 +53,8 @@ def download_remote_images():
render_variations(result[0], image_variations, replace=True) render_variations(result[0], image_variations, replace=True)
article.save() article.save()
except: except:
logger.exception(result)
logger.exception(result[0])
logger.exception('Unable to download remote image for %s' % article.original_image) logger.exception('Unable to download remote image for %s' % article.original_image)

View File

@ -3,6 +3,7 @@ from __future__ import unicode_literals, absolute_import
from django.contrib.auth.models import Group from django.contrib.auth.models import Group
from django.utils.dateparse import parse_datetime from django.utils.dateparse import parse_datetime
from mhackspace.users.models import Membership from mhackspace.users.models import Membership
from mhackspace.users.models import MEMBERSHIP_CANCELLED
def create_or_update_membership(user, signup_details, complete=False): def create_or_update_membership(user, signup_details, complete=False):
@ -34,3 +35,13 @@ def create_or_update_membership(user, signup_details, complete=False):
group = Group.objects.get(name='members') group = Group.objects.get(name='members')
user.groups.add(group) user.groups.add(group)
return True # Sign up finished return True # Sign up finished
def cancel_membership(user):
member = Membership.objects.get(user=user)
member.status = MEMBERSHIP_CANCELLED
member.save()
group = Group.objects.get(name='members')
user.groups.remove(group)
return True

View File

@ -35,16 +35,6 @@ class gocardless_provider:
access_token=payment_providers['gocardless']['credentials']['access_token'], access_token=payment_providers['gocardless']['credentials']['access_token'],
environment=payment_providers['gocardless']['environment']) environment=payment_providers['gocardless']['environment'])
# def subscribe_confirm(self, args):
# response = gocardless_pro.client.confirm_resource(args)
# subscription = gocardless_pro.client.subscription(args.get('resource_id'))
# return {
# 'amount': subscription.amount,
# 'start_date': subscription.created_at,
# 'reference': subscription.id,
# 'success': response.success
# }
def fetch_customers(self): def fetch_customers(self):
"""Fetch list of customers payments""" """Fetch list of customers payments"""
for customer in self.client.customers.list().records: for customer in self.client.customers.list().records:
@ -80,11 +70,18 @@ class gocardless_provider:
def get_token(self): def get_token(self):
return 'N/A' return 'N/A'
def cancel_subscription(self, reference): def cancel_subscription(self, user, reference):
try: try:
subscription = gocardless_pro.client.subscription(reference) subscription = self.client.subscriptions.get(reference)
response = subscription.cancel() response = self.client.subscriptions.cancel(reference)
except gocardless_pro.exceptions.ClientError: except gocardless_pro.errors.InvalidApiUsageError as e:
if e.code is 404:
logger.info('Cancel subscription failed user not found %s %s' % (e.code, e))
return {
'success': False
}
except Exception as e:
logger.info('Cancel subscription failed unknown reason code %s %s' % (e.code, e))
return { return {
'success': False 'success': False
} }
@ -92,7 +89,7 @@ class gocardless_provider:
'amount': subscription.amount, 'amount': subscription.amount,
'start_date': subscription.created_at, 'start_date': subscription.created_at,
'reference': subscription.id, 'reference': subscription.id,
'success': response.get('success', False) 'success': True if response.get('status_code') is '200' else False
} }
def create_subscription(self, user, session, amount, def create_subscription(self, user, session, amount,

View File

@ -35,14 +35,15 @@ class gocardlessMocks(TestCase):
return self.provider return self.provider
def mock_success_responses(self): def mock_success_responses(self):
subscription_properties = Mock(
mock_list = MagicMock() id='02',
mock_list_records = MagicMock(side_effect=[Mock(
id='01',
status='active', status='active',
amount=20.00, amount=20.00,
created_at='date' created_at='date'
)]) )
mock_list = MagicMock()
mock_list_records = MagicMock(side_effect=[subscription_properties])
mock_list.records.return_value = mock_list_records mock_list.records.return_value = mock_list_records
self.provider.client.subscriptions.list = mock_list self.provider.client.subscriptions.list = mock_list
@ -54,3 +55,10 @@ class gocardlessMocks(TestCase):
created_at=self.date_now, created_at=self.date_now,
api_response=ApiResponseStatus(status_code='200')) api_response=ApiResponseStatus(status_code='200'))
) )
self.provider.client.subscriptions.get = Mock(
return_value=subscription_properties)
self.provider.client.subscriptions.cancel = PropertyMock(
return_value={'status_code': '200'})

View File

@ -14,39 +14,19 @@ class TestPaymentGatewaysGocardless(gocardlessMocks):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
# self.date_now = django.utils.timezone.now()
# self.user = self.make_user()
# member = Membership()
# member.user = self.user
# member.payment = '20.00'
# member.date = self.date_now
# member.save()
# self.auth_gocardless()
def test_unsubscribe(self):
@skip("Need to implement")
@patch('mhackspace.subscriptions.payments.gocardless_pro.Client.subscription', autospec=True)
def test_unsubscribe(self, mock_subscription):
self.mock_success_responses() self.mock_success_responses()
# self.auth_gocardless()
mock_subscription.return_value = Mock(success='success') result = self.provider.cancel_subscription(user=self.user, reference='M01')
mock_subscription.cancel.return_value = Mock(
id='01',
status='active',
amount=20.00,
created_at='date'
)
result = self.provider.cancel_subscription(reference='M01')
self.assertEqual(result.get('amount'), 20.00) self.assertEqual(result.get('amount'), 20.00)
self.assertEqual(result.get('reference'), '02') self.assertEqual(result.get('reference'), '02')
self.assertEqual(result.get('success'), 'success') self.assertEqual(result.get('success'), True)
def test_confirm_subscription_callback(self): def test_confirm_subscription_callback(self):
self.mock_success_responses() self.mock_success_responses()
membership = self.create_membership_record() membership = self.create_membership_record()
# self.auth_gocardless()
# mock_confirm.return_value = Mock(success='success')
request_params = { request_params = {
'resource_uri': 'http://gocardless/resource/url/01', 'resource_uri': 'http://gocardless/resource/url/01',
@ -73,6 +53,6 @@ class TestPaymentGatewaysGocardless(gocardlessMocks):
for item in self.provider.fetch_subscriptions(): for item in self.provider.fetch_subscriptions():
self.assertEqual(item.get('status'), 'active') self.assertEqual(item.get('status'), 'active')
self.assertEqual(item.get('email'), 'test@test.com') self.assertEqual(item.get('email'), 'test@test.com')
self.assertEqual(item.get('reference'), '01') self.assertEqual(item.get('reference'), '02')
self.assertEqual(item.get('amount'), 20.00) self.assertEqual(item.get('amount'), 20.00)

View File

@ -14,7 +14,7 @@ from mhackspace.users.models import User, Membership
from mhackspace.users.models import MEMBERSHIP_CANCELLED from mhackspace.users.models import MEMBERSHIP_CANCELLED
from mhackspace.users.forms import MembershipJoinForm from mhackspace.users.forms import MembershipJoinForm
from mhackspace.subscriptions.payments import select_provider from mhackspace.subscriptions.payments import select_provider
from mhackspace.subscriptions.helper import create_or_update_membership from mhackspace.subscriptions.helper import create_or_update_membership, cancel_membership
class MembershipCancelView(LoginRequiredMixin, RedirectView): class MembershipCancelView(LoginRequiredMixin, RedirectView):
@ -28,17 +28,12 @@ class MembershipCancelView(LoginRequiredMixin, RedirectView):
member = Membership.objects.filter(user=self.request.user).first() member = Membership.objects.filter(user=self.request.user).first()
result = provider.cancel_subscription( result = provider.cancel_subscription(
user=self.request.user,
reference=member.reference reference=member.reference
) )
if result.get('success') is True:
# set membership to cancelled on success
member.status = MEMBERSHIP_CANCELLED
member.save()
# if result.get('success') is True:
# remove user from group on success cancel_membership(user=self.request.user)
group = Group.objects.get(name='members')
self.request.user.groups.remove(group)
messages.add_message( messages.add_message(
self.request, self.request,
messages.SUCCESS, messages.SUCCESS,

View File

@ -34,7 +34,7 @@
{% endif %} {% endif %}
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
{% if membership.get_status %} {% if membership.is_active %}
<div id="membercard" class="registered"> <div id="membercard" class="registered">
<div class="date">Joined {{membership.date}}</div> <div class="date">Joined {{membership.date}}</div>
<div class="container"> <div class="container">

View File

@ -45,27 +45,28 @@ class Blurb(models.Model):
skills = models.CharField(max_length=255) skills = models.CharField(max_length=255)
description = models.TextField() description = models.TextField()
MEMBERSHIP_CANCELLED = 0 MEMBERSHIP_ACTIVE = 4
MEMBERSHIP_CANCELLED = 4
MEMBERSHIP_STATUS_CHOICES = ( MEMBERSHIP_STATUS_CHOICES = (
(0, 'Guest user'), (0, 'Guest user'),
(1, 'Active membership'), (MEMBERSHIP_ACTIVE, 'Active membership'),
(3, 'Membership Expired'), (3, 'Membership Expired'),
(4, 'Membership Cancelled') (MEMBERSHIP_CANCELLED, 'Membership Cancelled')
) )
MEMBERSHIP_STRING = { MEMBERSHIP_STRING = {
0: 'Guest user', 0: 'Guest user',
1: 'Active membership', MEMBERSHIP_ACTIVE: 'Active membership',
3: 'Membership Expired', 3: 'Membership Expired',
4: 'Membership Cancelled' MEMBERSHIP_CANCELLED: 'Membership Cancelled'
} }
MEMBERSHIP_STATUS = { MEMBERSHIP_STATUS = {
'signup': 0, # This means the user has not completed signup 'signup': 0, # This means the user has not completed signup
'active': 1, 'active': MEMBERSHIP_ACTIVE,
'expired': 3, 'expired': 3,
'cancelled': 4 'cancelled': MEMBERSHIP_CANCELLED
} }
@ -87,6 +88,11 @@ class Membership(models.Model):
def get_status(self): def get_status(self):
return MEMBERSHIP_STRING[self.status] return MEMBERSHIP_STRING[self.status]
def is_active(self):
if self.status is MEMBERSHIP_ACTIVE:
return True
return False
def lookup_status(name): def lookup_status(name):
if not name: if not name:
return 0 return 0