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
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_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'
TEMPLATE_DEBUG = False

View File

@ -67,5 +67,8 @@ TEMPLATES[0]['OPTIONS']['loaders'] = [
]
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)
article.save()
except:
logger.exception(result)
logger.exception(result[0])
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.utils.dateparse import parse_datetime
from mhackspace.users.models import Membership
from mhackspace.users.models import MEMBERSHIP_CANCELLED
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')
user.groups.add(group)
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'],
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):
"""Fetch list of customers payments"""
for customer in self.client.customers.list().records:
@ -80,11 +70,18 @@ class gocardless_provider:
def get_token(self):
return 'N/A'
def cancel_subscription(self, reference):
def cancel_subscription(self, user, reference):
try:
subscription = gocardless_pro.client.subscription(reference)
response = subscription.cancel()
except gocardless_pro.exceptions.ClientError:
subscription = self.client.subscriptions.get(reference)
response = self.client.subscriptions.cancel(reference)
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 {
'success': False
}
@ -92,7 +89,7 @@ class gocardless_provider:
'amount': subscription.amount,
'start_date': subscription.created_at,
'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,

View File

@ -35,14 +35,15 @@ class gocardlessMocks(TestCase):
return self.provider
def mock_success_responses(self):
mock_list = MagicMock()
mock_list_records = MagicMock(side_effect=[Mock(
id='01',
subscription_properties = Mock(
id='02',
status='active',
amount=20.00,
created_at='date'
)])
)
mock_list = MagicMock()
mock_list_records = MagicMock(side_effect=[subscription_properties])
mock_list.records.return_value = mock_list_records
self.provider.client.subscriptions.list = mock_list
@ -54,3 +55,10 @@ class gocardlessMocks(TestCase):
created_at=self.date_now,
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):
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()
@skip("Need to implement")
@patch('mhackspace.subscriptions.payments.gocardless_pro.Client.subscription', autospec=True)
def test_unsubscribe(self, mock_subscription):
def test_unsubscribe(self):
self.mock_success_responses()
# self.auth_gocardless()
mock_subscription.return_value = Mock(success='success')
mock_subscription.cancel.return_value = Mock(
id='01',
status='active',
amount=20.00,
created_at='date'
)
result = self.provider.cancel_subscription(reference='M01')
result = self.provider.cancel_subscription(user=self.user, reference='M01')
self.assertEqual(result.get('amount'), 20.00)
self.assertEqual(result.get('reference'), '02')
self.assertEqual(result.get('success'), 'success')
self.assertEqual(result.get('success'), True)
def test_confirm_subscription_callback(self):
self.mock_success_responses()
membership = self.create_membership_record()
# self.auth_gocardless()
# mock_confirm.return_value = Mock(success='success')
request_params = {
'resource_uri': 'http://gocardless/resource/url/01',
@ -73,6 +53,6 @@ class TestPaymentGatewaysGocardless(gocardlessMocks):
for item in self.provider.fetch_subscriptions():
self.assertEqual(item.get('status'), 'active')
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)

View File

@ -14,7 +14,7 @@ from mhackspace.users.models import User, Membership
from mhackspace.users.models import MEMBERSHIP_CANCELLED
from mhackspace.users.forms import MembershipJoinForm
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):
@ -28,21 +28,16 @@ class MembershipCancelView(LoginRequiredMixin, RedirectView):
member = Membership.objects.filter(user=self.request.user).first()
result = provider.cancel_subscription(
user=self.request.user,
reference=member.reference
)
if result.get('success') is True:
# set membership to cancelled on success
member.status = MEMBERSHIP_CANCELLED
member.save()
# remove user from group on success
group = Group.objects.get(name='members')
self.request.user.groups.remove(group)
messages.add_message(
self.request,
messages.SUCCESS,
'Your membership has now been cancelled')
# if result.get('success') is True:
cancel_membership(user=self.request.user)
messages.add_message(
self.request,
messages.SUCCESS,
'Your membership has now been cancelled')
kwargs['username'] = self.request.user.get_username()
return super(MembershipCancelView, self).get_redirect_url(*args, **kwargs)

View File

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

View File

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