change subscription mechanism to use new gocardless api
This commit is contained in:
parent
b3d580d49c
commit
54e1bce190
|
@ -0,0 +1,3 @@
|
||||||
|
* cache folder
|
||||||
|
|
||||||
|
Store things like pip cache here so wee dont have to download every time wee build.
|
|
@ -0,0 +1,3 @@
|
||||||
|
* Logs folder
|
||||||
|
|
||||||
|
Store logs generated by containers you want to keep in this folder, usefull if your container fails on startup
|
|
@ -10,15 +10,52 @@ https://docs.djangoproject.com/en/dev/ref/settings/
|
||||||
"""
|
"""
|
||||||
from __future__ import absolute_import, unicode_literals
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
|
||||||
|
import os
|
||||||
import time
|
import time
|
||||||
import environ
|
import environ
|
||||||
|
|
||||||
|
|
||||||
|
# from spirit.settings import *
|
||||||
ROOT_DIR = environ.Path(__file__) - 3 # (mhackspace/config/settings/common.py - 3 = mhackspace/)
|
ROOT_DIR = environ.Path(__file__) - 3 # (mhackspace/config/settings/common.py - 3 = mhackspace/)
|
||||||
APPS_DIR = ROOT_DIR.path('mhackspace')
|
APPS_DIR = ROOT_DIR.path('mhackspace')
|
||||||
|
|
||||||
env = environ.Env()
|
env = environ.Env()
|
||||||
env.read_env('%s/.env' % ROOT_DIR)
|
env.read_env('%s/.env' % ROOT_DIR)
|
||||||
|
|
||||||
|
ST_TOPIC_PRIVATE_CATEGORY_PK = 1
|
||||||
|
ST_RATELIMIT_ENABLE = True
|
||||||
|
ST_RATELIMIT_CACHE_PREFIX = 'srl'
|
||||||
|
ST_RATELIMIT_CACHE = 'default'
|
||||||
|
ST_RATELIMIT_SKIP_TIMEOUT_CHECK = False
|
||||||
|
ST_NOTIFICATIONS_PER_PAGE = 20
|
||||||
|
ST_COMMENT_MAX_LEN = 3000
|
||||||
|
ST_MENTIONS_PER_COMMENT = 30
|
||||||
|
ST_DOUBLE_POST_THRESHOLD_MINUTES = 30
|
||||||
|
ST_YT_PAGINATOR_PAGE_RANGE = 3
|
||||||
|
ST_SEARCH_QUERY_MIN_LEN = 3
|
||||||
|
ST_USER_LAST_SEEN_THRESHOLD_MINUTES = 1
|
||||||
|
ST_PRIVATE_FORUM = False
|
||||||
|
ST_ALLOWED_UPLOAD_IMAGE_FORMAT = ('jpeg', 'png', 'gif')
|
||||||
|
ST_ALLOWED_URL_PROTOCOLS = {
|
||||||
|
'http', 'https', 'mailto', 'ftp', 'ftps',
|
||||||
|
'git', 'svn', 'magnet', 'irc', 'ircs'}
|
||||||
|
|
||||||
|
ST_UNICODE_SLUGS = True
|
||||||
|
ST_UNIQUE_EMAILS = True
|
||||||
|
ST_CASE_INSENSITIVE_EMAILS = True
|
||||||
|
|
||||||
|
# Tests helpers
|
||||||
|
ST_TESTS_RATELIMIT_NEVER_EXPIRE = False
|
||||||
|
ST_BASE_DIR = os.path.dirname(__file__)
|
||||||
|
|
||||||
|
|
||||||
|
HAYSTACK_CONNECTIONS = {
|
||||||
|
'default': {
|
||||||
|
'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
|
||||||
|
'PATH': os.path.join(os.path.dirname(__file__), 'search', 'whoosh_index'),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
# APP CONFIGURATION
|
# APP CONFIGURATION
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
DJANGO_APPS = (
|
DJANGO_APPS = (
|
||||||
|
@ -40,11 +77,41 @@ THIRD_PARTY_APPS = (
|
||||||
'allauth.socialaccount', # registration
|
'allauth.socialaccount', # registration
|
||||||
'allauth.socialaccount.providers.google', # registration
|
'allauth.socialaccount.providers.google', # registration
|
||||||
'allauth.socialaccount.providers.github', # registration
|
'allauth.socialaccount.providers.github', # registration
|
||||||
'allauth.socialaccount.providers.facebook', # registration
|
# 'allauth.socialaccount.providers.facebook', # registration
|
||||||
'whitenoise.runserver_nostatic',
|
'whitenoise.runserver_nostatic',
|
||||||
'stdimage',
|
'stdimage',
|
||||||
'rest_framework',
|
'rest_framework',
|
||||||
'draceditor',
|
'draceditor',
|
||||||
|
|
||||||
|
'haystack',
|
||||||
|
'djconfig',
|
||||||
|
'spirit.core',
|
||||||
|
'spirit.admin',
|
||||||
|
'spirit.search',
|
||||||
|
|
||||||
|
'spirit.user',
|
||||||
|
'spirit.user.admin',
|
||||||
|
'spirit.user.auth',
|
||||||
|
|
||||||
|
'spirit.category',
|
||||||
|
'spirit.category.admin',
|
||||||
|
|
||||||
|
'spirit.topic',
|
||||||
|
'spirit.topic.admin',
|
||||||
|
'spirit.topic.favorite',
|
||||||
|
'spirit.topic.moderate',
|
||||||
|
'spirit.topic.notification',
|
||||||
|
'spirit.topic.poll', # todo: remove in Spirit v0.6
|
||||||
|
'spirit.topic.private',
|
||||||
|
'spirit.topic.unread',
|
||||||
|
|
||||||
|
'spirit.comment',
|
||||||
|
'spirit.comment.bookmark',
|
||||||
|
'spirit.comment.flag',
|
||||||
|
'spirit.comment.flag.admin',
|
||||||
|
'spirit.comment.history',
|
||||||
|
'spirit.comment.like',
|
||||||
|
'spirit.comment.poll',
|
||||||
)
|
)
|
||||||
|
|
||||||
# Apps specific for this project go here.
|
# Apps specific for this project go here.
|
||||||
|
@ -74,6 +141,16 @@ MIDDLEWARE = (
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
|
|
||||||
|
#fix for ip logging behind a proxy
|
||||||
|
'x_forwarded_for.middleware.XForwardedForMiddleware',
|
||||||
|
'djconfig.middleware.DjConfigMiddleware',
|
||||||
|
|
||||||
|
'spirit.user.middleware.TimezoneMiddleware',
|
||||||
|
'spirit.user.middleware.LastIPMiddleware',
|
||||||
|
'spirit.user.middleware.LastSeenMiddleware',
|
||||||
|
'spirit.user.middleware.ActiveUserMiddleware',
|
||||||
|
'spirit.core.middleware.PrivateForumMiddleware',
|
||||||
)
|
)
|
||||||
|
|
||||||
# MIGRATIONS CONFIGURATION
|
# MIGRATIONS CONFIGURATION
|
||||||
|
@ -171,6 +248,7 @@ TEMPLATES = [
|
||||||
'django.template.context_processors.tz',
|
'django.template.context_processors.tz',
|
||||||
'django.contrib.messages.context_processors.messages',
|
'django.contrib.messages.context_processors.messages',
|
||||||
# Your stuff: custom template context processors go here
|
# Your stuff: custom template context processors go here
|
||||||
|
'djconfig.context_processors.config',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -47,14 +47,14 @@ CACHES = {
|
||||||
'IGNORE_EXCEPTIONS': True, # mimics memcache behavior.
|
'IGNORE_EXCEPTIONS': True, # mimics memcache behavior.
|
||||||
# http://niwinz.github.io/django-redis/latest/#_memcached_exceptions_behavior
|
# http://niwinz.github.io/django-redis/latest/#_memcached_exceptions_behavior
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
'st_rate_limit': {
|
||||||
|
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
||||||
|
'LOCATION': 'spirit_rl_cache',
|
||||||
|
'TIMEOUT': None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# CACHES = {
|
|
||||||
# 'default': {
|
|
||||||
# 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
|
||||||
# 'LOCATION': ''
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
|
|
||||||
# django-debug-toolbar
|
# django-debug-toolbar
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
@ -92,3 +92,5 @@ CAPTCHA = {
|
||||||
|
|
||||||
WHITENOISE_AUTOREFRESH = True
|
WHITENOISE_AUTOREFRESH = True
|
||||||
WHITENOISE_USE_FINDERS = True
|
WHITENOISE_USE_FINDERS = True
|
||||||
|
|
||||||
|
PAYMENT_PROVIDERS['gocardless']['redirect_url'] = 'http://127.0.0.1:8180'
|
||||||
|
|
|
@ -151,6 +151,11 @@ CACHES = {
|
||||||
'IGNORE_EXCEPTIONS': True, # mimics memcache behavior.
|
'IGNORE_EXCEPTIONS': True, # mimics memcache behavior.
|
||||||
# http://niwinz.github.io/django-redis/latest/#_memcached_exceptions_behavior
|
# http://niwinz.github.io/django-redis/latest/#_memcached_exceptions_behavior
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
'st_rate_limit': {
|
||||||
|
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
||||||
|
'LOCATION': 'spirit_rl_cache',
|
||||||
|
'TIMEOUT': None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -152,6 +152,11 @@ CACHES = {
|
||||||
'IGNORE_EXCEPTIONS': True, # mimics memcache behavior.
|
'IGNORE_EXCEPTIONS': True, # mimics memcache behavior.
|
||||||
# http://niwinz.github.io/django-redis/latest/#_memcached_exceptions_behavior
|
# http://niwinz.github.io/django-redis/latest/#_memcached_exceptions_behavior
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
'st_rate_limit': {
|
||||||
|
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
||||||
|
'LOCATION': 'spirit_rl_cache',
|
||||||
|
'TIMEOUT': None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,11 @@ CACHES = {
|
||||||
'default': {
|
'default': {
|
||||||
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
||||||
'LOCATION': ''
|
'LOCATION': ''
|
||||||
|
},
|
||||||
|
'st_rate_limit': {
|
||||||
|
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
||||||
|
'LOCATION': 'spirit_rl_cache',
|
||||||
|
'TIMEOUT': None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.conf.urls import include, url
|
from django.conf.urls import include, url
|
||||||
from django.conf.urls.static import static
|
from django.conf.urls.static import static
|
||||||
|
@ -21,6 +20,7 @@ from mhackspace.blog.views import PostViewSet, CategoryViewSet, BlogPost, PostLi
|
||||||
from mhackspace.blog.sitemaps import PostSitemap, CategorySitemap
|
from mhackspace.blog.sitemaps import PostSitemap, CategorySitemap
|
||||||
from mhackspace.feeds.views import FeedViewSet, ArticleViewSet
|
from mhackspace.feeds.views import FeedViewSet, ArticleViewSet
|
||||||
|
|
||||||
|
# import spirit.urls
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register(r'posts', PostViewSet)
|
router.register(r'posts', PostViewSet)
|
||||||
router.register(r'categories', CategoryViewSet)
|
router.register(r'categories', CategoryViewSet)
|
||||||
|
@ -39,6 +39,7 @@ urlpatterns = [
|
||||||
url(r'^mailing-list/$', TemplateView.as_view(template_name='pages/mailing-list.html'), name='group'),
|
url(r'^mailing-list/$', TemplateView.as_view(template_name='pages/mailing-list.html'), name='group'),
|
||||||
url(r'^contact/$', contact, name='contact'),
|
url(r'^contact/$', contact, name='contact'),
|
||||||
|
|
||||||
|
url(r'^discuss/', include('spirit.urls')),
|
||||||
url(r'^api/v1/', include(router.urls, namespace='v1')),
|
url(r'^api/v1/', include(router.urls, namespace='v1')),
|
||||||
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
|
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
|
||||||
url(r'^draceditor/', include('draceditor.urls')),
|
url(r'^draceditor/', include('draceditor.urls')),
|
||||||
|
@ -79,6 +80,7 @@ urlpatterns = [
|
||||||
url(r'^reset/done/$', auth_views.password_reset_complete, name='password_reset_complete'),
|
url(r'^reset/done/$', auth_views.password_reset_complete, name='password_reset_complete'),
|
||||||
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||||
|
|
||||||
|
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
# This allows the error pages to be debugged during development, just visit
|
# This allows the error pages to be debugged during development, just visit
|
||||||
# these url in browser to see how these error pages look like.
|
# these url in browser to see how these error pages look like.
|
||||||
|
|
7
dev.yml
7
dev.yml
|
@ -20,9 +20,10 @@ services:
|
||||||
command: /start-dev.sh
|
command: /start-dev.sh
|
||||||
depends_on:
|
depends_on:
|
||||||
- postgres
|
- postgres
|
||||||
environment:
|
env_file: .env
|
||||||
- POSTGRES_USER=mhackspace
|
# environment:
|
||||||
- USE_DOCKER=yes
|
# - POSTGRES_USER=mhackspace
|
||||||
|
# - USE_DOCKER=yes
|
||||||
volumes:
|
volumes:
|
||||||
- .:/app
|
- .:/app
|
||||||
ports:
|
ports:
|
||||||
|
|
1
live.yml
1
live.yml
|
@ -28,6 +28,7 @@ services:
|
||||||
env_file: .env
|
env_file: .env
|
||||||
volumes:
|
volumes:
|
||||||
- .:/app
|
- .:/app
|
||||||
|
- ./compose/data/logs:/var/log/gunicorn
|
||||||
- sockets:/data/sockets
|
- sockets:/data/sockets
|
||||||
|
|
||||||
node:
|
node:
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import random
|
||||||
from autofixture import AutoFixture
|
from autofixture import AutoFixture
|
||||||
from autofixture.generators import ImageGenerator
|
from autofixture.generators import ImageGenerator
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
|
@ -7,10 +8,12 @@ from mhackspace.feeds.models import Article, Feed
|
||||||
from mhackspace.users.models import User
|
from mhackspace.users.models import User
|
||||||
from mhackspace.blog.models import Category, Post
|
from mhackspace.blog.models import Category, Post
|
||||||
|
|
||||||
|
|
||||||
class ImageFixture(AutoFixture):
|
class ImageFixture(AutoFixture):
|
||||||
class Values:
|
class Values:
|
||||||
scaled_image = ImageGenerator(width=800, height=300, sizes=((1280, 300),))
|
scaled_image = ImageGenerator(width=800, height=300, sizes=((1280, 300),))
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
help = 'Build test data for development environment'
|
help = 'Build test data for development environment'
|
||||||
|
|
||||||
|
@ -30,10 +33,11 @@ class Command(BaseCommand):
|
||||||
call_command('loaddata', 'mhackspace/users/fixtures/groups.json', verbose=0)
|
call_command('loaddata', 'mhackspace/users/fixtures/groups.json', verbose=0)
|
||||||
|
|
||||||
# random data
|
# random data
|
||||||
users = AutoFixture(User)
|
users = AutoFixture(User, field_values={
|
||||||
|
'title': random.choicee(('Mr', 'Mrs', 'Emperor', 'Captain'))
|
||||||
|
})
|
||||||
users.create(10)
|
users.create(10)
|
||||||
|
|
||||||
|
|
||||||
banners = ImageFixture(BannerImage)
|
banners = ImageFixture(BannerImage)
|
||||||
banners.create(10)
|
banners.create(10)
|
||||||
self.stdout.write(
|
self.stdout.write(
|
||||||
|
|
|
@ -6,3 +6,11 @@
|
||||||
transform: rotate(360deg);
|
transform: rotate(360deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-brand {
|
||||||
|
width:100%;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals, absolute_import
|
||||||
|
from django.contrib import messages
|
||||||
|
from django.contrib.auth.models import Group
|
||||||
|
from mhackspace.users.models import Membership
|
||||||
|
|
||||||
|
|
||||||
|
def create_or_update_membership(user, signup_details, complete=False):
|
||||||
|
try:
|
||||||
|
member = Membership.objects.get(user=user)
|
||||||
|
except Membership.DoesNotExist:
|
||||||
|
member = Membership()
|
||||||
|
member.user = user
|
||||||
|
|
||||||
|
if complete is True:
|
||||||
|
member.status = Membership.lookup_status(name=signup_details.get('status'))
|
||||||
|
member.email = signup_details.get('email')
|
||||||
|
member.reference = signup_details.get('reference')
|
||||||
|
member.payment = signup_details.get('amount')
|
||||||
|
member.date = signup_details.get('start_date')
|
||||||
|
|
||||||
|
member.save()
|
||||||
|
|
||||||
|
if complete is False:
|
||||||
|
return False # sign up not completed
|
||||||
|
|
||||||
|
# add user to group on success
|
||||||
|
group = Group.objects.get(name='members')
|
||||||
|
user.groups.add(group)
|
||||||
|
return True # Sign up finished
|
|
@ -22,20 +22,16 @@ class Command(BaseCommand):
|
||||||
|
|
||||||
payment_objects = []
|
payment_objects = []
|
||||||
for customer in provider.fetch_customers():
|
for customer in provider.fetch_customers():
|
||||||
# self.stdout.write(str(dir(customer)))
|
user = User.objects.get(email=customer.get('email'))
|
||||||
# self.stdout.write(str(customer))
|
|
||||||
|
|
||||||
payment_objects.append(Payments(
|
payment_objects.append(Payments(
|
||||||
user=None,
|
user=user,
|
||||||
user_reference=customer.get('user_id'),
|
user_reference=customer.get('user_reference'),
|
||||||
user_email=customer.get('email'),
|
user_email=customer.get('email'),
|
||||||
reference=customer.get('payment_id'),
|
reference=customer.get('payment_id'),
|
||||||
amount=customer.get('amount'),
|
amount=customer.get('amount'),
|
||||||
type=Payments.lookup_payment_type(customer.get('payment_type')),
|
type=Payments.lookup_payment_type(customer.get('payment_type')),
|
||||||
date=customer.get('payment_date')
|
date=customer.get('payment_date')
|
||||||
))
|
))
|
||||||
# self.stdout.write(str(customer.email))
|
|
||||||
# self.stdout.write(str(dir(customer['email']())))
|
|
||||||
self.stdout.write(
|
self.stdout.write(
|
||||||
self.style.SUCCESS(
|
self.style.SUCCESS(
|
||||||
'\t{reference} - {amount} - {type} - {user_email}'.format(**model_to_dict(payment_objects[-1]))))
|
'\t{reference} - {amount} - {type} - {user_email}'.format(**model_to_dict(payment_objects[-1]))))
|
||||||
|
|
|
@ -5,7 +5,7 @@ from django.forms.models import model_to_dict
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from mhackspace.subscriptions.payments import select_provider
|
from mhackspace.subscriptions.payments import select_provider
|
||||||
from mhackspace.users.models import Membership, User
|
from mhackspace.users.models import Membership, User
|
||||||
|
from mhackspace.subscriptions.helper import create_or_update_membership
|
||||||
|
|
||||||
|
|
||||||
def update_subscriptions(provider_name):
|
def update_subscriptions(provider_name):
|
||||||
|
@ -54,28 +54,44 @@ class Command(BaseCommand):
|
||||||
group = Group.objects.get(name='members')
|
group = Group.objects.get(name='members')
|
||||||
|
|
||||||
for sub in provider.fetch_subscriptions():
|
for sub in provider.fetch_subscriptions():
|
||||||
|
sub['amount'] = sub['amount'] * 0.01
|
||||||
try:
|
try:
|
||||||
user_model = User.objects.get(email=sub.get('email'))
|
user_model = User.objects.get(email=sub.get('email'))
|
||||||
if sub.get('status') == 'active':
|
if sub.get('status') == 'active':
|
||||||
user_model.groups.add(group)
|
user_model.groups.add(group)
|
||||||
except User.DoesNotExist:
|
except User.DoesNotExist:
|
||||||
user_model = None
|
user_model = None
|
||||||
|
self.style.NOTICE(
|
||||||
|
'\tMissing User {reference} - {payment} - {status} - {email}'.format(**{
|
||||||
|
'reference': sub.get('reference'),
|
||||||
|
'payment': sub.get('amount'),
|
||||||
|
'status': sub.get('status'),
|
||||||
|
'email': sub.get('email')
|
||||||
|
}))
|
||||||
|
continue
|
||||||
|
|
||||||
|
create_or_update_membership(user=user_model,
|
||||||
|
signup_details=sub,
|
||||||
|
complete=True)
|
||||||
subscriptions.append(
|
subscriptions.append(
|
||||||
Membership(
|
Membership(
|
||||||
user=user_model,
|
user=user_model,
|
||||||
email=sub.get('email'),
|
email=sub.get('email'),
|
||||||
reference=sub.get('reference'),
|
reference=sub.get('reference'),
|
||||||
payment=10.00,
|
payment=sub.get('amount'),
|
||||||
date=sub.get('start_date'),
|
date=sub.get('start_date'),
|
||||||
# date=timezone.now(),
|
|
||||||
status=Membership.lookup_status(name=sub.get('status'))
|
status=Membership.lookup_status(name=sub.get('status'))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
self.stdout.write(
|
self.stdout.write(
|
||||||
self.style.SUCCESS(
|
self.style.SUCCESS(
|
||||||
'\t{reference} - {payment} - {status} - {email}'.format(**model_to_dict(subscriptions[-1]))))
|
'\t{reference} - {payment} - {status} - {email}'.format(**{
|
||||||
|
'reference': sub.get('reference'),
|
||||||
|
'payment': sub.get('amount'),
|
||||||
|
'status': sub.get('status'),
|
||||||
|
'email': sub.get('email')
|
||||||
|
})))
|
||||||
|
|
||||||
Membership.objects.bulk_create(subscriptions)
|
Membership.objects.bulk_create(subscriptions)
|
||||||
|
|
|
@ -1,19 +1,14 @@
|
||||||
from pprint import pprint
|
from pprint import pprint
|
||||||
import pytz
|
import pytz
|
||||||
import gocardless
|
import gocardless_pro as gocardless
|
||||||
import braintree
|
import braintree
|
||||||
|
import logging
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
payment_providers = settings.PAYMENT_PROVIDERS
|
payment_providers = settings.PAYMENT_PROVIDERS
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
# import gocardless_pro
|
|
||||||
# import paypalrestsdk as paypal
|
# import paypalrestsdk as paypal
|
||||||
|
|
||||||
# from website.config import settings
|
|
||||||
# from website.config.import app_domain
|
|
||||||
|
|
||||||
# from website.config.logger import log
|
|
||||||
|
|
||||||
PROVIDER_ID = {'gocardless':1, 'braintree': 2}
|
PROVIDER_ID = {'gocardless':1, 'braintree': 2}
|
||||||
PROVIDER_NAME = {1: 'gocardless', 2: 'braintree'}
|
PROVIDER_NAME = {1: 'gocardless', 2: 'braintree'}
|
||||||
|
|
||||||
|
@ -26,19 +21,17 @@ def select_provider(type):
|
||||||
assert 0, "No Provider for " + type
|
assert 0, "No Provider for " + type
|
||||||
|
|
||||||
class gocardless_provider:
|
class gocardless_provider:
|
||||||
|
"""
|
||||||
|
gocardless test account details 20-00-00, 55779911
|
||||||
|
"""
|
||||||
form_remote = True
|
form_remote = True
|
||||||
client = None
|
client = None
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
# gocardless are changing there api, not sure if we can switch yet
|
# gocardless are changing there api, not sure if we can switch yet
|
||||||
# self.client = gocardless_pro.Client(
|
self.client = gocardless.Client(
|
||||||
# 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'])
|
||||||
|
|
||||||
print(payment_providers.keys)
|
|
||||||
gocardless.environment = payment_providers['gocardless']['environment']
|
|
||||||
gocardless.set_details(**payment_providers['gocardless']['credentials'])
|
|
||||||
self.client = gocardless.client.merchant()
|
|
||||||
|
|
||||||
def subscribe_confirm(self, args):
|
def subscribe_confirm(self, args):
|
||||||
response = gocardless.client.confirm_resource(args)
|
response = gocardless.client.confirm_resource(args)
|
||||||
|
@ -50,49 +43,33 @@ class gocardless_provider:
|
||||||
'success': response.success
|
'success': response.success
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def fetch_customers(self):
|
def fetch_customers(self):
|
||||||
merchant = gocardless.client.merchant()
|
"""Fetch list of customers payments"""
|
||||||
for customer in merchant.bills():
|
for customer in self.client.customers.list().records:
|
||||||
user = customer.user()
|
for payment in self.client.payments.list(params={"customer": customer.id}).records:
|
||||||
# print(dir(customer))
|
yield {
|
||||||
# print(dir(customer.reference_fields))
|
'user_reference': customer.id,
|
||||||
# print(customer.reference_fields)
|
'email': customer.email,
|
||||||
# print(customer.payout_id)
|
'status': payment.status,
|
||||||
# print(customer.reference_fields.payout_id)
|
'payment_id': payment.links.subscription,
|
||||||
result = {
|
'payment_type': 'subscription' if payment.links.subscription else 'payment',
|
||||||
'user_id': user.id,
|
'payment_date': payment.created_at,
|
||||||
'email': user.email,
|
'amount': payment.amount
|
||||||
'status': customer.status,
|
|
||||||
'payment_id': customer.id,
|
|
||||||
'payment_type': customer.source_type,
|
|
||||||
'payment_date': customer.created_at,
|
|
||||||
'amount': customer.amount
|
|
||||||
}
|
}
|
||||||
yield result #customer
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# for customer in self.client.users():
|
|
||||||
# result = {
|
|
||||||
# 'email': customer.email,
|
|
||||||
# 'created_date': customer.created_at,
|
|
||||||
# 'first_name': customer.first_name,
|
|
||||||
# 'last_name': customer.last_name
|
|
||||||
# }
|
|
||||||
# yield customer
|
|
||||||
|
|
||||||
def fetch_subscriptions(self):
|
def fetch_subscriptions(self):
|
||||||
for paying_member in self.client.subscriptions():
|
# for paying_member in self.client.mandates.list().records:
|
||||||
user=paying_member.user()
|
for paying_member in self.client.subscriptions.list().records:
|
||||||
|
mandate=self.client.mandates.get(paying_member.links.mandate)
|
||||||
|
user=self.client.customers.get(mandate.links.customer)
|
||||||
|
|
||||||
# gocardless does not have a reference so we use the id instead
|
# gocardless does not have a reference so we use the id instead
|
||||||
yield {
|
yield {
|
||||||
'status': paying_member.status,
|
'status': paying_member.status,
|
||||||
'email': user.email,
|
'email': user.email,
|
||||||
'start_date': paying_member.created_at,
|
'start_date': paying_member.created_at,
|
||||||
'reference': paying_member.id,
|
'reference': mandate.id,
|
||||||
'amount': paying_member.amount}
|
'amount': paying_member.amount}
|
||||||
|
|
||||||
def get_redirect_url(self):
|
def get_redirect_url(self):
|
||||||
|
@ -116,25 +93,61 @@ class gocardless_provider:
|
||||||
'success': response.get('success', False)
|
'success': response.get('success', False)
|
||||||
}
|
}
|
||||||
|
|
||||||
def create_subscription(self, amount, name, redirect_success, redirect_failure, interval_unit='month', interval_length='1'):
|
def create_subscription(self, user, session, amount,
|
||||||
return gocardless.client.new_subscription_url(
|
name, redirect_success, redirect_failure,
|
||||||
amount=float(amount),
|
interval_unit='monthly', interval_length='1'):
|
||||||
interval_length=interval_length,
|
return self.client.redirect_flows.create(params={
|
||||||
interval_unit=interval_unit,
|
"description": name,
|
||||||
name=name,
|
"session_token": session,
|
||||||
redirect_uri=redirect_success)
|
"success_redirect_url": redirect_success,
|
||||||
|
"prefilled_customer": {
|
||||||
def confirm_subscription(self, provider_response):
|
"given_name": user.first_name,
|
||||||
response = gocardless.client.confirm_resource(provider_response)
|
"family_name": user.last_name,
|
||||||
subscription = gocardless.client.subscription(provider_response.get('resource_id'))
|
"email": user.email
|
||||||
user = subscription.user()
|
|
||||||
return {
|
|
||||||
'amount': subscription.amount,
|
|
||||||
'email': user.email,
|
|
||||||
'start_date': subscription.created_at,
|
|
||||||
'reference': subscription.id,
|
|
||||||
'success': response.get('success')
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
def confirm_subscription(self, membership, session, provider_response,
|
||||||
|
name, interval_unit='monthly', interval_length='1'):
|
||||||
|
r = provider_response.get('redirect_flow_id')
|
||||||
|
|
||||||
|
# response = self.client.redirect_flows.complete(r, params={
|
||||||
|
# "session_token": session
|
||||||
|
# })
|
||||||
|
response = self.client.redirect_flows.get(r)
|
||||||
|
# response = self.client.redirect_flows.get(provider_response.get('redirect_flow_id'))
|
||||||
|
|
||||||
|
# response = gocardless.client.confirm_resource(provider_response)
|
||||||
|
# subscription = gocardless.client.subscription(provider_response.get('resource_id'))
|
||||||
|
user_id = response.links.customer
|
||||||
|
mandate_id = response.links.mandate
|
||||||
|
# user = subscription.user()
|
||||||
|
user = self.client.customers.get(response.links.customer)
|
||||||
|
mandate = self.client.mandates.get(response.links.mandate)
|
||||||
|
logging.debug(user)
|
||||||
|
logging.debug(mandate)
|
||||||
|
|
||||||
|
# for some reason go cardless is in pence, so 20.00 needs to be sent as 2000
|
||||||
|
# what genious decided that was a good idea, now looks like i am charging £2000 :p
|
||||||
|
# the return is the same so you need to convert on send and receive
|
||||||
|
subscription_response = self.client.subscriptions.create(
|
||||||
|
params={
|
||||||
|
'amount': str(membership.payment).replace('.', ''),
|
||||||
|
'currency': 'GBP',
|
||||||
|
'interval_unit': interval_unit,
|
||||||
|
'name': name,
|
||||||
|
# 'metadata': {'reference': },
|
||||||
|
'links': {'mandate': mandate_id}
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
'amount': membership.payment,
|
||||||
|
'email': user.email,
|
||||||
|
'start_date': subscription_response.created_at,
|
||||||
|
'reference': mandate_id,
|
||||||
|
'success': subscription_response.api_response.status_code
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class braintree_provider:
|
class braintree_provider:
|
||||||
form_remote = False
|
form_remote = False
|
||||||
|
@ -189,6 +202,7 @@ class braintree_provider:
|
||||||
|
|
||||||
class payment:
|
class payment:
|
||||||
"""
|
"""
|
||||||
|
https://developer.gocardless.com/api-reference/#redirect-flows-create-a-redirect-flow
|
||||||
paypal reference = https://github.com/paypal/PayPal-Python-SDK
|
paypal reference = https://github.com/paypal/PayPal-Python-SDK
|
||||||
gocardless reference = https://github.com/paypal/PayPal-Python-SDK
|
gocardless reference = https://github.com/paypal/PayPal-Python-SDK
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import unicode_literals, absolute_import
|
from __future__ import unicode_literals, absolute_import
|
||||||
|
|
||||||
|
from django.utils import timezone
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.views.generic import UpdateView, RedirectView
|
from django.views.generic import UpdateView, RedirectView
|
||||||
|
@ -13,6 +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
|
||||||
|
|
||||||
|
|
||||||
class MembershipCancelView(LoginRequiredMixin, RedirectView):
|
class MembershipCancelView(LoginRequiredMixin, RedirectView):
|
||||||
|
@ -54,7 +56,6 @@ class MembershipJoinView(LoginRequiredMixin, UpdateView):
|
||||||
return User.objects.get(username=self.request.user.username)
|
return User.objects.get(username=self.request.user.username)
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
app_domain = 'http://test.maidstone-hackspace.org.uk'
|
|
||||||
payment_provider = 'gocardless'
|
payment_provider = 'gocardless'
|
||||||
provider = select_provider(payment_provider)
|
provider = select_provider(payment_provider)
|
||||||
app_domain = provider.get_redirect_url()
|
app_domain = provider.get_redirect_url()
|
||||||
|
@ -64,16 +65,31 @@ class MembershipJoinView(LoginRequiredMixin, UpdateView):
|
||||||
form_subscription = MembershipJoinForm(data=self.request.POST)
|
form_subscription = MembershipJoinForm(data=self.request.POST)
|
||||||
form_subscription.is_valid()
|
form_subscription.is_valid()
|
||||||
|
|
||||||
|
result = {
|
||||||
|
'email': self.request.user.email,
|
||||||
|
'reference': user_code,
|
||||||
|
'amount': form_subscription.cleaned_data.get('amount', 20.00) * 0.01,
|
||||||
|
'start_date': timezone.now()
|
||||||
|
}
|
||||||
|
|
||||||
|
create_or_update_membership(
|
||||||
|
user=self.request.user,
|
||||||
|
signup_details=result,
|
||||||
|
complete=False
|
||||||
|
)
|
||||||
|
|
||||||
success_url = '%s/membership/%s/success' % (app_domain, payment_provider)
|
success_url = '%s/membership/%s/success' % (app_domain, payment_provider)
|
||||||
failure_url = '%s/membership/%s/failure' % (app_domain, payment_provider)
|
failure_url = '%s/membership/%s/failure' % (app_domain, payment_provider)
|
||||||
url = provider.create_subscription(
|
url = provider.create_subscription(
|
||||||
|
user=self.request.user,
|
||||||
|
session=self.request.session.session_key,
|
||||||
amount=form_subscription.cleaned_data.get('amount', 20.00),
|
amount=form_subscription.cleaned_data.get('amount', 20.00),
|
||||||
name="Membership your membership id is MH%s" % user_code,
|
name="Membership your membership id is MH%s" % user_code,
|
||||||
redirect_success=success_url,
|
redirect_success=success_url,
|
||||||
redirect_failure=failure_url
|
redirect_failure=failure_url
|
||||||
)
|
)
|
||||||
|
|
||||||
return redirect(url)
|
return redirect(url.redirect_url)
|
||||||
|
|
||||||
|
|
||||||
class MembershipJoinSuccessView(LoginRequiredMixin, RedirectView):
|
class MembershipJoinSuccessView(LoginRequiredMixin, RedirectView):
|
||||||
|
@ -83,8 +99,14 @@ class MembershipJoinSuccessView(LoginRequiredMixin, RedirectView):
|
||||||
def get_redirect_url(self, *args, **kwargs):
|
def get_redirect_url(self, *args, **kwargs):
|
||||||
payment_provider = 'gocardless'
|
payment_provider = 'gocardless'
|
||||||
provider = select_provider(payment_provider)
|
provider = select_provider(payment_provider)
|
||||||
|
membership = Membership.objects.get(user=self.request.user)
|
||||||
|
|
||||||
|
name="Membership your membership id is MH%s" % membership.reference
|
||||||
result = provider.confirm_subscription(
|
result = provider.confirm_subscription(
|
||||||
provider_response=self.request.GET
|
membership=membership,
|
||||||
|
session=self.request.session.session_key,
|
||||||
|
provider_response=self.request.GET,
|
||||||
|
name=name
|
||||||
)
|
)
|
||||||
|
|
||||||
# if something went wrong return to profile with an error
|
# if something went wrong return to profile with an error
|
||||||
|
@ -96,27 +118,15 @@ class MembershipJoinSuccessView(LoginRequiredMixin, RedirectView):
|
||||||
return super(MembershipJoinSuccessView, self).get_redirect_url(*args, **kwargs)
|
return super(MembershipJoinSuccessView, self).get_redirect_url(*args, **kwargs)
|
||||||
|
|
||||||
del(kwargs['provider'])
|
del(kwargs['provider'])
|
||||||
try:
|
|
||||||
member = Membership.objects.get(user=self.request.user)
|
|
||||||
except Membership.DoesNotExist:
|
|
||||||
member = Membership()
|
|
||||||
|
|
||||||
member.user = self.request.user
|
if create_or_update_membership(user=self.request.user,
|
||||||
member.email = result.get('email')
|
signup_details=result,
|
||||||
member.reference = result.get('reference')
|
complete=True) is True:
|
||||||
member.payment = result.get('amount')
|
|
||||||
member.date = result.get('start_date')
|
|
||||||
member.status = Membership.lookup_status(name=result.get('status'))
|
|
||||||
member.save()
|
|
||||||
kwargs['username'] = self.request.user.get_username()
|
|
||||||
|
|
||||||
# add user to group on success
|
|
||||||
group = Group.objects.get(name='members')
|
|
||||||
self.request.user.groups.add(group)
|
|
||||||
messages.add_message(
|
messages.add_message(
|
||||||
self.request,
|
self.request,
|
||||||
messages.SUCCESS,
|
messages.SUCCESS,
|
||||||
'Success your membership should now be active')
|
'Success your membership should now be active')
|
||||||
|
kwargs['username'] = self.request.user.get_username()
|
||||||
return super(MembershipJoinSuccessView, self).get_redirect_url(*args, **kwargs)
|
return super(MembershipJoinSuccessView, self).get_redirect_url(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -27,11 +27,12 @@
|
||||||
<meta name="msapplication-config" content="{% static 'browserconfig.xml' %}">
|
<meta name="msapplication-config" content="{% static 'browserconfig.xml' %}">
|
||||||
<meta name="theme-color" content="#008080">
|
<meta name="theme-color" content="#008080">
|
||||||
|
|
||||||
|
<link href="{% sass_src 'sass/project.scss' %}" rel="stylesheet">
|
||||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous">
|
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous">
|
||||||
{% block css %}
|
{% block css %}
|
||||||
<link href="{% sass_src 'sass/project.scss' %}" rel="stylesheet">
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block head-extra %}{% endblock head-extra %}
|
||||||
<script type="application/ld+json">
|
<script type="application/ld+json">
|
||||||
{
|
{
|
||||||
"@context": "http://schema.org",
|
"@context": "http://schema.org",
|
||||||
|
@ -45,7 +46,7 @@
|
||||||
"@type": "PostalAddress",
|
"@type": "PostalAddress",
|
||||||
"streetAddress": "Maidstone Hackspace, Maidstone Community Support Centre",
|
"streetAddress": "Maidstone Hackspace, Maidstone Community Support Centre",
|
||||||
"addressLocality": "Maidstone",
|
"addressLocality": "Maidstone",
|
||||||
"addressRegion": "FLKent",
|
"addressRegion": "Kent",
|
||||||
"postalCode": "ME14 1HH",
|
"postalCode": "ME14 1HH",
|
||||||
"addressCountry": "UK"
|
"addressCountry": "UK"
|
||||||
},
|
},
|
||||||
|
@ -66,6 +67,7 @@
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<nav class="navbar navbar-toggleable-md navbar-inverse bg-inverse mb-4">
|
<nav class="navbar navbar-toggleable-md navbar-inverse bg-inverse mb-4">
|
||||||
<button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse"
|
<button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse"
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% load sass_tags %}
|
||||||
|
{% load static i18n %}
|
||||||
|
{% block title %}Members{% endblock %}
|
||||||
|
|
||||||
|
{% block css %}
|
||||||
|
<link rel="stylesheet" href="{% static 'spirit/stylesheets/styles.all.min.css' %}">
|
||||||
|
<link href="{% sass_src 'sass/project.scss' %}" rel="stylesheet">
|
||||||
|
{{ super.block }}
|
||||||
|
{% endblock css %}
|
||||||
|
|
||||||
|
{% block head-extra %}
|
||||||
|
|
||||||
|
<script src="{% static "spirit/scripts/all.min.js" %}"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$( document ).ready(function() {
|
||||||
|
$.tab();
|
||||||
|
$( 'a.js-post' ).postify( {
|
||||||
|
csrfToken: "{{ csrf_token }}",
|
||||||
|
} );
|
||||||
|
$('.js-messages').messages();
|
||||||
|
{% if user.is_authenticated %}
|
||||||
|
$.notification( {
|
||||||
|
notificationUrl: "{% url "spirit:topic:notification:index-ajax" %}",
|
||||||
|
notificationListUrl: "{% url "spirit:topic:notification:index-unread" %}",
|
||||||
|
mentionTxt: "{% trans "{user} has mention you on {topic}" %}",
|
||||||
|
commentTxt: "{% trans "{user} has commented on {topic}" %}",
|
||||||
|
showAll: "{% trans "Show all" %}",
|
||||||
|
empty: "{% trans "No new notifications, yet" %}",
|
||||||
|
unread: "{% trans "unread" %}",
|
||||||
|
} );
|
||||||
|
{% endif %}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock head-extra %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container">
|
||||||
|
<h2>Users</h2>
|
||||||
|
|
||||||
|
<div class="list-group">
|
||||||
|
{% for user in user_list %}
|
||||||
|
<a href="{% url 'users:detail' user.username %}" class="list-group-item">
|
||||||
|
<h4 class="list-group-item-heading">{{ user.username }}</h4>
|
||||||
|
</a>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock content %}
|
|
@ -2,6 +2,7 @@
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load crispy_forms_tags %}
|
{% load crispy_forms_tags %}
|
||||||
|
{% load socialaccount %}
|
||||||
|
|
||||||
{% block title %}User: {{ object.username }}{% endblock %}
|
{% block title %}User: {{ object.username }}{% endblock %}
|
||||||
|
|
||||||
|
@ -15,14 +16,22 @@
|
||||||
<p>{{ user.name }}</p>
|
<p>{{ user.name }}</p>
|
||||||
<p>{{ user.email }}</p>
|
<p>{{ user.email }}</p>
|
||||||
<p>Last login {{ user.last_login}}</p>
|
<p>Last login {{ user.last_login}}</p>
|
||||||
<p>Member since</p>
|
{% if blurb.description %}
|
||||||
<p>Description: {{ blurb.description }}</p>
|
<p>Description: {{ blurb.description }}</p>
|
||||||
<p>Skills: {{ blurb.description }}</p>
|
{% endif %}
|
||||||
|
{% if blurb.skills %}
|
||||||
|
<p>Skills: {{ blurb.skills }}</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if membership %}
|
||||||
<h3>Membership status</h3>
|
<h3>Membership status</h3>
|
||||||
|
<p>Member since {{membership.date}}</p>
|
||||||
<p>Membership Status: {{ membership.get_status }}</p>
|
<p>Membership Status: {{ membership.get_status }}</p>
|
||||||
<p>Last Payment: {{membership.date}}</p>
|
<p>Last Payment: {{membership.date}}</p>
|
||||||
<p>Amount: £{{membership.payment}}</p>
|
<p>Amount: £{{membership.payment}}</p>
|
||||||
|
{% else %}
|
||||||
|
You are not currently a member consider signing up.
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
{% if membership.get_status %}
|
{% if membership.get_status %}
|
||||||
|
@ -63,11 +72,27 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
|
<p>
|
||||||
<a class="btn btn-primary" href="{% url 'users:update' %}" role="button">My Info</a>
|
<a class="btn btn-primary" href="{% url 'users:update' %}" role="button">My Info</a>
|
||||||
<a class="btn btn-primary" href="{% url 'account_email' %}" role="button">E-Mail</a>
|
<a class="btn btn-primary" href="{% url 'account_email' %}" role="button">E-Mail</a>
|
||||||
|
</p>
|
||||||
<!-- Your Stuff: Custom user template urls -->
|
<!-- Your Stuff: Custom user template urls -->
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<p>
|
||||||
|
<a class="btn btn-primary" href="{% provider_login_url "google" process="connect" %}">Link your Google account</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<p>
|
||||||
|
<a class="btn btn-primary" href="{% provider_login_url "github" process="connect" %}">Link your Github account</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- End Action buttons -->
|
<!-- End Action buttons -->
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -11,7 +11,7 @@ from django.urls import reverse
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
from .models import User, Membership, MEMBERSHIP_STATUS_CHOICES
|
from .models import User, Membership, MEMBERSHIP_STATUS_CHOICES
|
||||||
|
|
||||||
from mhackspace.subscriptions.management.commands.refresh_subscriptions import update_subscriptions
|
from mhackspace.subscriptions.management.commands.update_membership_status import update_subscriptions
|
||||||
|
|
||||||
|
|
||||||
class MyUserChangeForm(UserChangeForm):
|
class MyUserChangeForm(UserChangeForm):
|
||||||
|
@ -41,7 +41,7 @@ class MyUserAdmin(AuthUserAdmin):
|
||||||
form = MyUserChangeForm
|
form = MyUserChangeForm
|
||||||
add_form = MyUserCreationForm
|
add_form = MyUserCreationForm
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
('User Profile', {'fields': ('name', 'image')}),
|
('User Profile', {'fields': ('name', '_image')}),
|
||||||
) + AuthUserAdmin.fieldsets
|
) + AuthUserAdmin.fieldsets
|
||||||
list_display = ('username', 'name', 'is_superuser')
|
list_display = ('username', 'name', 'is_superuser')
|
||||||
search_fields = ['name']
|
search_fields = ['name']
|
||||||
|
|
|
@ -11,17 +11,28 @@ from stdimage.models import StdImageField
|
||||||
|
|
||||||
class User(AbstractUser):
|
class User(AbstractUser):
|
||||||
name = models.CharField(_('Name of User'), blank=True, max_length=255)
|
name = models.CharField(_('Name of User'), blank=True, max_length=255)
|
||||||
public = models.BooleanField(default=False, help_text='If the users email is public on post feeds')
|
public = models.BooleanField(
|
||||||
image = StdImageField(
|
default=False, help_text='If the users email is public on post feeds')
|
||||||
|
_image = StdImageField(
|
||||||
upload_to='avatars/',
|
upload_to='avatars/',
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
|
db_column='image',
|
||||||
variations={
|
variations={
|
||||||
'profile': {
|
'profile': {
|
||||||
"width": 256,
|
"width": 256,
|
||||||
"height": 256,
|
"height": 256,
|
||||||
"crop": True}})
|
"crop": True}})
|
||||||
|
|
||||||
|
# https://github.com/pennersr/django-allauth/issues/520
|
||||||
|
@property
|
||||||
|
def image(self):
|
||||||
|
return self._image
|
||||||
|
|
||||||
|
@image.setter
|
||||||
|
def image(self, value):
|
||||||
|
self._image = value
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.username
|
return self.username
|
||||||
|
|
||||||
|
@ -50,6 +61,7 @@ MEMBERSHIP_STRING = {
|
||||||
}
|
}
|
||||||
|
|
||||||
MEMBERSHIP_STATUS = {
|
MEMBERSHIP_STATUS = {
|
||||||
|
'signup': 0, # This means the user has not completed signup
|
||||||
'active': 1,
|
'active': 1,
|
||||||
'cancelled': 2
|
'cancelled': 2
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ class UserDetailView(LoginRequiredMixin, DetailView):
|
||||||
context['membership_form'] = MembershipJoinForm(initial={'amount': 20.00})
|
context['membership_form'] = MembershipJoinForm(initial={'amount': 20.00})
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class UserRedirectView(LoginRequiredMixin, RedirectView):
|
class UserRedirectView(LoginRequiredMixin, RedirectView):
|
||||||
permanent = False
|
permanent = False
|
||||||
|
|
||||||
|
@ -35,7 +36,7 @@ class UserRedirectView(LoginRequiredMixin, RedirectView):
|
||||||
|
|
||||||
|
|
||||||
class UserUpdateView(LoginRequiredMixin, UpdateView):
|
class UserUpdateView(LoginRequiredMixin, UpdateView):
|
||||||
fields = ['name', 'image', ]
|
fields = ['name', '_image', ]
|
||||||
model = User
|
model = User
|
||||||
|
|
||||||
# send the user back to their own page after a successful update
|
# send the user back to their own page after a successful update
|
||||||
|
|
|
@ -52,7 +52,7 @@ lxml==3.7.3
|
||||||
# Your custom requirements go here
|
# Your custom requirements go here
|
||||||
mock==2.0.0
|
mock==2.0.0
|
||||||
|
|
||||||
gocardless
|
gocardless_pro
|
||||||
braintree==3.37.2
|
braintree==3.37.2
|
||||||
|
|
||||||
django-autofixture==0.12.1
|
django-autofixture==0.12.1
|
||||||
|
@ -62,5 +62,10 @@ git+https://github.com/olymk2/scaffold.git
|
||||||
djangorestframework==3.6.3
|
djangorestframework==3.6.3
|
||||||
django-filter==1.0.2
|
django-filter==1.0.2
|
||||||
|
|
||||||
#git+https://github.com/olymk2/dracos-markdown-editor.git
|
|
||||||
draceditor==1.1.8
|
draceditor==1.1.8
|
||||||
|
# django-spirit
|
||||||
|
django-djconfig
|
||||||
|
django-haystack
|
||||||
|
git+https://github.com/nitely/Spirit.git
|
||||||
|
# git+https://github.com/olymk2/django-xforwardedfor-middleware.git
|
||||||
|
django-xforwardedfor-middleware==2.0
|
||||||
|
|
|
@ -11,3 +11,10 @@ factory-boy==2.8.1
|
||||||
# pytest
|
# pytest
|
||||||
pytest-django==3.1.2
|
pytest-django==3.1.2
|
||||||
pytest-sugar==0.8.0
|
pytest-sugar==0.8.0
|
||||||
|
|
||||||
|
django-test-plus==1.0.18
|
||||||
|
# improved REPL
|
||||||
|
ipdb==0.10.3
|
||||||
|
|
||||||
|
pytest-django==3.1.2
|
||||||
|
pytest-sugar==0.8.0
|
||||||
|
|
Loading…
Reference in New Issue