Merge branch 'master' into pyup-pin-mock-2.0.0
This commit is contained in:
commit
cb312b34e5
|
@ -6,7 +6,12 @@ pipeline:
|
|||
- USE_DOCKER=yes
|
||||
- DJANGO_SETTINGS_MODULE=config.settings.test
|
||||
commands:
|
||||
- python manage.py test mhackspace.subscriptions --verbosity 2
|
||||
- cp -n env.example .env
|
||||
- python manage.py test mhackspace --verbosity 2
|
||||
|
||||
deploy:
|
||||
|
||||
|
||||
|
||||
#volumes:
|
||||
# postgres_data_dev: {}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
sudo: true
|
||||
before_install:
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -qq build-essential gettext python-dev zlib1g-dev libpq-dev xvfb
|
||||
- sudo apt-get install -qq libtiff4-dev libjpeg8-dev libfreetype6-dev liblcms1-dev libwebp-dev
|
||||
- sudo apt-get install -qq graphviz-dev python-setuptools python3-dev python-virtualenv python-pip
|
||||
- sudo apt-get install -qq firefox automake libtool libreadline6 libreadline6-dev libreadline-dev
|
||||
- sudo apt-get install -qq libsqlite3-dev libxml2 libxml2-dev libssl-dev libbz2-dev wget curl llvm
|
||||
language: python
|
||||
python:
|
||||
- "3.5"
|
|
@ -0,0 +1,6 @@
|
|||
machine:
|
||||
python:
|
||||
version: 3.5.0
|
||||
environment:
|
||||
DJANGO_SETTINGS_MODULE: config.settings.test
|
||||
DATABASE_URL: postgres://ubuntu:@127.0.0.1:5432/circle_test
|
|
@ -11,6 +11,7 @@ RUN pip install -r /requirements/production.txt \
|
|||
|
||||
COPY . /app
|
||||
RUN chown -R django /app
|
||||
RUN mkdir -p /data/sockets
|
||||
|
||||
COPY ./compose/django/gunicorn.sh /gunicorn.sh
|
||||
COPY ./compose/django/entrypoint.sh /entrypoint.sh
|
||||
|
@ -19,7 +20,8 @@ RUN sed -i 's/\r//' /entrypoint.sh \
|
|||
&& chmod +x /entrypoint.sh \
|
||||
&& chown django /entrypoint.sh \
|
||||
&& chmod +x /gunicorn.sh \
|
||||
&& chown django /gunicorn.sh
|
||||
&& chown django /gunicorn.sh \
|
||||
&& chown django /data/sockets
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
|
|
@ -33,5 +33,4 @@ until postgres_ready; do
|
|||
sleep 1
|
||||
done
|
||||
|
||||
>&2 echo "Postgres is up - continuing..."
|
||||
exec $cmd
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
#!/bin/sh
|
||||
python /app/manage.py collectstatic --noinput
|
||||
/usr/local/bin/gunicorn config.wsgi -w 4 -b 0.0.0.0:5000 --chdir=/app
|
||||
chmod 777 -R /data/sockets/
|
||||
touch /data/sockets/gunicron.sock
|
||||
ls -la /data/sockets/
|
||||
/usr/local/bin/gunicorn config.wsgi -w 4 -b unix:/data/sockets/gunicorn.sock --chdir=/app
|
||||
|
|
|
@ -4,6 +4,6 @@ ADD nginx.conf /etc/nginx/nginx.conf
|
|||
|
||||
ADD start.sh /start.sh
|
||||
ADD nginx-secure.conf /etc/nginx/nginx-secure.conf
|
||||
ADD dhparams.pem /etc/ssl/private/dhparams.pem
|
||||
#ADD dhparams.pem /etc/ssl/private/dhparams.pem
|
||||
CMD /start.sh
|
||||
|
||||
|
|
|
@ -26,7 +26,8 @@ http {
|
|||
#gzip on;
|
||||
|
||||
upstream app {
|
||||
server django:5000;
|
||||
#server django:5000;
|
||||
server unix:/data/sockets/gunicorn.sock;
|
||||
}
|
||||
|
||||
server {
|
||||
|
@ -36,12 +37,12 @@ http {
|
|||
|
||||
server_name ___my.example.com___ ;
|
||||
|
||||
location /.well-known/acme-challenge {
|
||||
proxy_pass http://certbot:80;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
}
|
||||
# location /.well-known/acme-challenge {
|
||||
# proxy_pass http://certbot:80;
|
||||
# proxy_set_header Host $host;
|
||||
# proxy_set_header X-Forwarded-For $remote_addr;
|
||||
# proxy_set_header X-Forwarded-Proto https;
|
||||
# }
|
||||
|
||||
|
||||
location / {
|
||||
|
|
|
@ -16,7 +16,7 @@ ROOT_DIR = environ.Path(__file__) - 3 # (mhackspace/config/settings/common.py -
|
|||
APPS_DIR = ROOT_DIR.path('mhackspace')
|
||||
|
||||
env = environ.Env()
|
||||
env.read_env()
|
||||
env.read_env('%s/.env' % ROOT_DIR)
|
||||
|
||||
# APP CONFIGURATION
|
||||
# ------------------------------------------------------------------------------
|
||||
|
@ -48,6 +48,7 @@ LOCAL_APPS = (
|
|||
# custom users app
|
||||
# Your stuff: custom apps go here
|
||||
'mhackspace.users.apps.UsersConfig',
|
||||
'mhackspace.base',
|
||||
'mhackspace.subscriptions',
|
||||
'mhackspace.feeds',
|
||||
'mhackspace.contact',
|
||||
|
@ -189,6 +190,7 @@ STATICFILES_DIRS = (
|
|||
STATICFILES_FINDERS = (
|
||||
'django.contrib.staticfiles.finders.FileSystemFinder',
|
||||
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
|
||||
'sass_processor.finders.CssFinder',
|
||||
)
|
||||
|
||||
# MEDIA CONFIGURATION
|
||||
|
@ -252,7 +254,7 @@ LOGIN_URL = 'account_login'
|
|||
AUTOSLUG_SLUGIFY_FUNCTION = 'slugify.slugify'
|
||||
# django-compressor
|
||||
# ------------------------------------------------------------------------------
|
||||
INSTALLED_APPS += ("compressor", )
|
||||
INSTALLED_APPS += ("compressor", 'sass_processor',)
|
||||
STATICFILES_FINDERS += ("compressor.finders.CompressorFinder", )
|
||||
|
||||
# Location of root django.contrib.admin URL, use {% url 'admin:index' %}
|
||||
|
@ -262,7 +264,7 @@ ADMIN_URL = '^admin/'
|
|||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
payment_providers = {
|
||||
PAYMENT_PROVIDERS = {
|
||||
'braintree': {
|
||||
'mode': 'sandbox',
|
||||
'credentials': {
|
||||
|
@ -275,7 +277,7 @@ payment_providers = {
|
|||
"mode": "sandbox", # sandbox or live
|
||||
'credentials': {
|
||||
"mode": "sandbox", # sandbox or live
|
||||
"client_id": end('PAYPAL_CLIENT_ID'),
|
||||
"client_id": env('PAYPAL_CLIENT_ID'),
|
||||
"client_secret": env('PAYPAL_CLIENT_SECRET')}
|
||||
},
|
||||
'gocardless':{
|
||||
|
|
|
@ -56,6 +56,9 @@ X_FRAME_OPTIONS = 'DENY'
|
|||
# Hosts/domain names that are valid for this site
|
||||
# See https://docs.djangoproject.com/en/1.6/ref/settings/#allowed-hosts
|
||||
ALLOWED_HOSTS = env.list('DJANGO_ALLOWED_HOSTS', default=['maidstone-hackspace.org.uk'])
|
||||
ALLOWED_HOSTS.append('172.*')
|
||||
ALLOWED_HOSTS.append('172.18.0.5')
|
||||
|
||||
# END SITE CONFIGURATION
|
||||
|
||||
INSTALLED_APPS += ('gunicorn', )
|
||||
|
|
|
@ -1,8 +1,20 @@
|
|||
version: '2'
|
||||
|
||||
volumes:
|
||||
postgres_data: {}
|
||||
postgres_backup: {}
|
||||
gunicorn_socket:
|
||||
driver: local
|
||||
postgres_data:
|
||||
driver: local
|
||||
postgres_backup:
|
||||
driver: local
|
||||
|
||||
|
||||
# volumes:
|
||||
# sockets:
|
||||
# driver: local
|
||||
# data:
|
||||
# driver: local
|
||||
|
||||
|
||||
services:
|
||||
postgres:
|
||||
|
@ -22,37 +34,41 @@ services:
|
|||
- redis
|
||||
command: /gunicorn.sh
|
||||
env_file: .env
|
||||
volumes:
|
||||
- .:/app
|
||||
- gunicorn_socket:/data/sockets
|
||||
|
||||
nginx:
|
||||
build: ./compose/nginx
|
||||
env_file: .env
|
||||
depends_on:
|
||||
- django
|
||||
- certbot
|
||||
|
||||
ports:
|
||||
- "0.0.0.0:80:80"
|
||||
# - certbot
|
||||
|
||||
environment:
|
||||
- MY_DOMAIN_NAME=maidstone-hackspace.org.uk
|
||||
ports:
|
||||
- "0.0.0.0:80:80"
|
||||
- "0.0.0.0:443:443"
|
||||
volumes:
|
||||
- /etc/letsencrypt:/etc/letsencrypt
|
||||
- /var/lib/letsencrypt:/var/lib/letsencrypt
|
||||
- .:/app
|
||||
- gunicorn_socket:/data/sockets
|
||||
# - "0.0.0.0:443:443"
|
||||
# volumes:
|
||||
# - /etc/letsencrypt:/etc/letsencrypt
|
||||
# - /var/lib/letsencrypt:/var/lib/letsencrypt
|
||||
|
||||
certbot:
|
||||
image: quay.io/letsencrypt/letsencrypt
|
||||
command: bash -c "sleep 6 && certbot certonly -n --standalone -d maidstone-hackspace.org.uk --text --agree-tos --email support@maidstone-hackspace.org.uk --server https://acme-v01.api.letsencrypt.org/directory --rsa-key-size 4096 --verbose --keep-until-expiring --standalone-supported-challenges http-01"
|
||||
entrypoint: ""
|
||||
volumes:
|
||||
- /etc/letsencrypt:/etc/letsencrypt
|
||||
- /var/lib/letsencrypt:/var/lib/letsencrypt
|
||||
ports:
|
||||
- "80"
|
||||
- "443"
|
||||
environment:
|
||||
- TERM=xterm
|
||||
# certbot:
|
||||
# image: quay.io/letsencrypt/letsencrypt
|
||||
# command: bash -c "sleep 6 && certbot certonly -n --standalone -d maidstone-hackspace.org.uk --text --agree-tos --email support@maidstone-hackspace.org.uk --server https://acme-v01.api.letsencrypt.org/directory --rsa-key-size 4096 --verbose --keep-until-expiring --standalone-supported-challenges http-01"
|
||||
# entrypoint: ""
|
||||
# volumes:
|
||||
# - /etc/letsencrypt:/etc/letsencrypt
|
||||
# - /var/lib/letsencrypt:/var/lib/letsencrypt
|
||||
# ports:
|
||||
# - "80"
|
||||
# - "443"
|
||||
# environment:
|
||||
# - TERM=xterm
|
||||
|
||||
|
||||
redis:
|
||||
|
|
20
env.example
20
env.example
|
@ -29,16 +29,16 @@ DJANGO_ACCOUNT_ALLOW_REGISTRATION=True
|
|||
COMPRESS_ENABLED=
|
||||
|
||||
|
||||
PAYMENT_ENVIRONMENT = 'sandbox'
|
||||
PAYMENT_ENVIRONMENT=sandbox
|
||||
|
||||
BRAINTREE_MERCHANT_ID = ''
|
||||
BRAINTREE_PUBLIC_KEY = ''
|
||||
BRAINTREE_PRIVATE_KEY = ''
|
||||
BRAINTREE_MERCHANT_ID=demo
|
||||
BRAINTREE_PUBLIC_KEY=demo
|
||||
BRAINTREE_PRIVATE_KEY=demo
|
||||
|
||||
PAYPAL_CLIENT_ID = ""
|
||||
PAYPAL_CLIENT_SECRET = ""
|
||||
PAYPAL_CLIENT_ID=demo
|
||||
PAYPAL_CLIENT_SECRET=demo
|
||||
|
||||
GOCARDLESS_APP_ID = ''
|
||||
GOCARDLESS_APP_SECRET = ''
|
||||
GOCARDLESS_ACCESS_TOKEN = ''
|
||||
GOCARDLESS_MERCHANT_ID = ''
|
||||
GOCARDLESS_APP_ID=demo
|
||||
GOCARDLESS_APP_SECRET=demo
|
||||
GOCARDLESS_ACCESS_TOKEN=demo
|
||||
GOCARDLESS_MERCHANT_ID=demo
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
version: '2'
|
||||
|
||||
volumes:
|
||||
gunicorn_socket: {}
|
||||
postgres_data_dev: {}
|
||||
postgres_backup_dev: {}
|
||||
|
||||
services:
|
||||
postgres:
|
||||
build: ./compose/postgres
|
||||
volumes:
|
||||
- postgres_data_dev:/var/lib/postgresql/data
|
||||
- postgres_backup_dev:/backups
|
||||
# environment:
|
||||
# - POSTGRES_USER=${POSTGRES_USER}
|
||||
env_file: .env
|
||||
|
||||
django:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./compose/django/Dockerfile
|
||||
command: /start-dev.sh
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
- POSTGRES_USER=${POSTGRES_USER}
|
||||
- USE_DOCKER=yes
|
||||
volumes:
|
||||
- .:/app
|
||||
- gunicorn_socket:/var/run/gunicorn/
|
||||
ports:
|
||||
- "8180:8000"
|
||||
links:
|
||||
- postgres
|
||||
- mailhog
|
||||
|
||||
nginx:
|
||||
build: ./compose/nginx
|
||||
depends_on:
|
||||
- django
|
||||
environment:
|
||||
- MY_DOMAIN_NAME=maidstone-hackspace.org.uk
|
||||
ports:
|
||||
- "0.0.0.0:80:80"
|
||||
# - "0.0.0.0:443:443"
|
||||
volumes:
|
||||
- gunicorn_socket:/var/run/gunicorn/
|
||||
|
||||
mailhog:
|
||||
image: mailhog/mailhog
|
||||
ports:
|
||||
- "8125:8025"
|
||||
|
||||
redis:
|
||||
image: redis:latest
|
|
@ -0,0 +1,19 @@
|
|||
from autofixture import AutoFixture
|
||||
from django.core.management.base import BaseCommand
|
||||
from mhackspace.feeds.models import Article
|
||||
from mhackspace.users.models import User
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Imports the RSS feeds from active blogs'
|
||||
|
||||
def handle(self, *args, **options):
|
||||
users = AutoFixture(User)
|
||||
users.create(10)
|
||||
|
||||
feeds = AutoFixture(User)
|
||||
feeds.create(10)
|
||||
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS(
|
||||
'Finished creating test data'))
|
|
@ -1,8 +1,12 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-01-04 14:04
|
||||
# Generated by Django 1.10.5 on 2017-01-28 18:38
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
import stdimage.models
|
||||
import stdimage.utils
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
@ -13,14 +17,35 @@ class Migration(migrations.Migration):
|
|||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Article',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('url', models.URLField()),
|
||||
('title', models.CharField(max_length=255)),
|
||||
('original_image', models.URLField(blank=True, max_length=255, null=True)),
|
||||
('image', stdimage.models.StdImageField(blank=True, null=True, upload_to=stdimage.utils.UploadToAutoSlugClassNameDir('title'))),
|
||||
('description', models.TextField()),
|
||||
('displayed', models.BooleanField(default=True)),
|
||||
('date', models.DateTimeField(default=django.utils.timezone.now)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Feed',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('url', models.CharField(max_length=255)),
|
||||
('home_url', models.URLField(verbose_name='Site Home Page')),
|
||||
('feed_url', models.URLField(verbose_name='RSS Feed URL')),
|
||||
('title', models.CharField(max_length=255)),
|
||||
('author', models.CharField(max_length=255)),
|
||||
('tags', models.CharField(max_length=255)),
|
||||
('image', models.ImageField(upload_to='')),
|
||||
('tags', models.CharField(blank=True, max_length=255)),
|
||||
('image', stdimage.models.StdImageField(blank=True, null=True, upload_to=stdimage.utils.UploadToAutoSlugClassNameDir('title'))),
|
||||
('enabled', models.BooleanField(default=True)),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='article',
|
||||
name='feed',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='feeds.Feed'),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-01-04 20:33
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('feeds', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='feed',
|
||||
name='id',
|
||||
field=models.IntegerField(primary_key=True, serialize=False),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='feed',
|
||||
name='image',
|
||||
field=models.ImageField(blank=True, upload_to=''),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='feed',
|
||||
name='tags',
|
||||
field=models.CharField(blank=True, max_length=255),
|
||||
),
|
||||
]
|
|
@ -1,20 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-01-04 20:35
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('feeds', '0002_auto_20170104_2033'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='feed',
|
||||
name='id',
|
||||
field=models.AutoField(primary_key=True, serialize=False),
|
||||
),
|
||||
]
|
|
@ -1,20 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-01-04 21:39
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('feeds', '0003_auto_20170104_2035'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='feed',
|
||||
name='enabled',
|
||||
field=models.BooleanField(default=True),
|
||||
),
|
||||
]
|
|
@ -1,69 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-01-08 03:42
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
import stdimage.models
|
||||
import stdimage.utils
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('feeds', '0004_feed_enabled'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Article',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('url', models.URLField()),
|
||||
('title', models.CharField(max_length=255)),
|
||||
('original_image', models.URLField(blank=True, max_length=255, null=True)),
|
||||
('image', stdimage.models.StdImageField(blank=True, null=True, upload_to=stdimage.utils.UploadToAutoSlugClassNameDir('title'))),
|
||||
('description', models.TextField()),
|
||||
('displayed', models.BooleanField(default=True)),
|
||||
('date', models.DateTimeField(default=django.utils.timezone.now)),
|
||||
],
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='feed',
|
||||
name='url',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='feed',
|
||||
name='feed_url',
|
||||
field=models.URLField(default='http://thearduinoguy.org/?feed=rss2', verbose_name='RSS Feed URL'),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='feed',
|
||||
name='home_url',
|
||||
field=models.URLField(default='http://thearduinoguy.org/', verbose_name='Site Home Page'),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='feed',
|
||||
name='title',
|
||||
field=models.CharField(default='The Arduino Guy', max_length=255),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='feed',
|
||||
name='id',
|
||||
field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='feed',
|
||||
name='image',
|
||||
field=stdimage.models.StdImageField(blank=True, null=True, upload_to=stdimage.utils.UploadToAutoSlugClassNameDir('title')),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='article',
|
||||
name='feed',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='feeds.Feed'),
|
||||
),
|
||||
]
|
|
@ -10,11 +10,11 @@ from mhackspace.users.models import User
|
|||
|
||||
class MemberListView(LoginRequiredMixin, ListView):
|
||||
template_name = 'pages/members.html'
|
||||
queryset = User.objects.prefetch_related('users', 'groups')
|
||||
queryset = User.objects.prefetch_related('user', 'groups')
|
||||
paginate_by = 10
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(MemberListView, self).get_context_data(**kwargs)
|
||||
context['members'] = self.get_queryset()
|
||||
context['total'] = self.get_queryset().filter(groups__name='member').count()
|
||||
context['total'] = self.get_queryset().filter(groups__name='members').count()
|
||||
return context
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
from datetime import datetime
|
||||
from django.utils import timezone
|
||||
from django.contrib.auth.models import Group
|
||||
from django.forms.models import model_to_dict
|
||||
from django.core.management.base import BaseCommand
|
||||
from mhackspace.subscriptions.payments import select_provider
|
||||
from mhackspace.users.models import Membership, User
|
||||
from mhackspace.subscriptions.models import Payments
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Update user subscriptions'
|
||||
|
||||
def handle(self, *args, **options):
|
||||
provider = select_provider('gocardless')
|
||||
|
||||
self.stdout.write(
|
||||
self.style.NOTICE(
|
||||
'== Gocardless customer payments =='))
|
||||
|
||||
Payments.objects.all().delete()
|
||||
|
||||
payment_objects = []
|
||||
for customer in provider.fetch_customers():
|
||||
# self.stdout.write(str(dir(customer)))
|
||||
# self.stdout.write(str(customer))
|
||||
|
||||
payment_objects.append(Payments(
|
||||
user=None,
|
||||
user_reference=customer.get('user_id'),
|
||||
user_email=customer.get('email'),
|
||||
reference=customer.get('payment_id'),
|
||||
amount=customer.get('amount'),
|
||||
type=Payments.lookup_payment_type(customer.get('payment_type')),
|
||||
date=customer.get('payment_date')
|
||||
))
|
||||
# self.stdout.write(str(customer.email))
|
||||
# self.stdout.write(str(dir(customer['email']())))
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS(
|
||||
'\t{reference} - {amount} - {type} - {user_email}'.format(**model_to_dict(payment_objects[-1]))))
|
||||
|
||||
|
||||
|
||||
Payments.objects.bulk_create(payment_objects)
|
|
@ -24,4 +24,4 @@ class Command(BaseCommand):
|
|||
for sub in provider.fetch_subscriptions():
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS(
|
||||
'\t{reference} - {amount} - {status} - {email}'.format(**sub)))
|
||||
'\t{start_date} {reference} - {amount} - {status} - {email}'.format(**sub)))
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
from datetime import datetime
|
||||
from django.utils import timezone
|
||||
from django.contrib.auth.models import Group
|
||||
from django.forms.models import model_to_dict
|
||||
from django.core.management.base import BaseCommand
|
||||
from mhackspace.subscriptions.payments import select_provider
|
||||
from mhackspace.users.models import Membership, User
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Update user subscriptions'
|
||||
|
||||
def handle(self, *args, **options):
|
||||
provider = select_provider('gocardless')
|
||||
|
||||
self.stdout.write(
|
||||
self.style.NOTICE(
|
||||
'== Gocardless subscriptions =='))
|
||||
|
||||
Membership.objects.all().delete()
|
||||
subscriptions = []
|
||||
|
||||
group = Group.objects.get(name='members')
|
||||
|
||||
for sub in provider.fetch_subscriptions():
|
||||
try:
|
||||
user_model = User.objects.get(email=sub.get('email'))
|
||||
if sub.get('status') == 'active':
|
||||
user_model.groups.add(group)
|
||||
except User.DoesNotExist:
|
||||
user_model = None
|
||||
|
||||
self.stdout.write(sub.get('status'))
|
||||
subscriptions.append(
|
||||
Membership(
|
||||
user=user_model,
|
||||
email=sub.get('email'),
|
||||
reference=sub.get('reference'),
|
||||
payment=10.00,
|
||||
date= sub.get('start_date'),
|
||||
# date=timezone.now(),
|
||||
status=Membership.lookup_status(name=sub.get('status'))
|
||||
)
|
||||
)
|
||||
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS(
|
||||
'\t{reference} - {payment} - {status} - {email}'.format(**model_to_dict(subscriptions[-1]))))
|
||||
|
||||
Membership.objects.bulk_create(subscriptions)
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.5 on 2017-01-29 21:55
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Payments',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('user_reference', models.CharField(max_length=255)),
|
||||
('user_email', models.CharField(max_length=255)),
|
||||
('reference', models.CharField(max_length=255, unique=True)),
|
||||
('amount', models.DecimalField(decimal_places=2, default=0.0, max_digits=6)),
|
||||
('type', models.PositiveSmallIntegerField(default=0)),
|
||||
('date', models.DateTimeField()),
|
||||
('user', models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='from_user', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
]
|
|
@ -0,0 +1,42 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.db import models
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from stdimage.models import StdImageField
|
||||
|
||||
|
||||
PAYMENT_TYPES = {
|
||||
'unknown': 0,
|
||||
'subscription': 1,
|
||||
'payment': 2
|
||||
}
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Payments(models.Model):
|
||||
user = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
null=True, blank=True,
|
||||
default=None,
|
||||
related_name='from_user'
|
||||
)
|
||||
user_reference = models.CharField(max_length=255)
|
||||
user_email = models.CharField(max_length=255)
|
||||
|
||||
reference = models.CharField(max_length=255, unique=True)
|
||||
amount = models.DecimalField(max_digits=6, decimal_places=2, default=0.0)
|
||||
type = models.PositiveSmallIntegerField(default=0)
|
||||
date = models.DateTimeField()
|
||||
|
||||
def lookup_payment_type(name):
|
||||
return PAYMENT_TYPES.get(name, 0)
|
||||
|
||||
def get_payment_type(self):
|
||||
return self.type
|
||||
|
||||
def __str__(self):
|
||||
return self.reference
|
|
@ -3,7 +3,8 @@ import pytz
|
|||
import gocardless
|
||||
import braintree
|
||||
|
||||
from django.conf.settings import payment_providers
|
||||
from django.conf import settings
|
||||
payment_providers = settings.PAYMENT_PROVIDERS
|
||||
|
||||
# import gocardless_pro
|
||||
# import paypalrestsdk as paypal
|
||||
|
@ -45,18 +46,48 @@ class gocardless_provider:
|
|||
return {
|
||||
'amount': subscription.amount,
|
||||
'start_date': subscription.created_at,
|
||||
'reference': subscription.id
|
||||
'reference': subscription.id,
|
||||
'success': response.success
|
||||
}
|
||||
|
||||
|
||||
|
||||
def fetch_customers(self):
|
||||
merchant = gocardless.client.merchant()
|
||||
for customer in merchant.bills():
|
||||
user = customer.user()
|
||||
# print(dir(customer))
|
||||
# print(dir(customer.reference_fields))
|
||||
# print(customer.reference_fields)
|
||||
# print(customer.payout_id)
|
||||
# print(customer.reference_fields.payout_id)
|
||||
result = {
|
||||
'user_id': user.id,
|
||||
'email': user.email,
|
||||
'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):
|
||||
for paying_member in self.client.subscriptions():
|
||||
user=paying_member.user()
|
||||
# for bill in paying_member.bills():
|
||||
# print('test')
|
||||
# print(dir(bill))
|
||||
# print(bill.created_at)
|
||||
# print(dir(paying_member))
|
||||
# print(paying_member.reference_fields)
|
||||
|
||||
#gocardless does not have a reference so we use the id instead
|
||||
yield {
|
||||
'status': paying_member.status,
|
||||
'email': user.email,
|
||||
|
@ -70,6 +101,16 @@ class gocardless_provider:
|
|||
def get_token(self):
|
||||
return 'N/A'
|
||||
|
||||
def cancel_subscribe(self, reference):
|
||||
subscription = gocardless.client.subscription(reference)
|
||||
response = subscription.cancel()
|
||||
return {
|
||||
'amount': subscription.amount,
|
||||
'start_date': subscription.created_at,
|
||||
'reference': subscription.id,
|
||||
'success': response.success
|
||||
}
|
||||
|
||||
def create_subscription(self, amount, name, redirect_success, redirect_failure, interval_unit='month', interval_length='1'):
|
||||
return gocardless.client.new_subscription_url(
|
||||
amount=float(amount),
|
||||
|
@ -199,40 +240,6 @@ class payment:
|
|||
'reference': paying_member.id,
|
||||
'amount': paying_member.amount}
|
||||
|
||||
if self.provider == 'paypal':
|
||||
#~ I-S39170DK26AF
|
||||
#~ start_date, end_date = "2014-07-01", "2014-07-20"
|
||||
billing_agreement = paypal.BillingAgreement.find('')
|
||||
print(billing_agreement)
|
||||
print(dir(billing_agreement))
|
||||
#~ print billing_agreement.search_transactions(start_date, end_date)
|
||||
#~ transactions = billing_agreement.search_transactions(start_date, end_date)
|
||||
payment_history = paypal.Payment.all({"count": 2})
|
||||
|
||||
# List Payments
|
||||
print("List Payment:")
|
||||
print(payment_history)
|
||||
for payment in payment_history.payments:
|
||||
print(" -> Payment[%s]" % (payment.id))
|
||||
#~ print paypal.BillingAgreement.all()
|
||||
history = paypal.BillingPlan.all(
|
||||
{"status": "CREATED", "page_size": 5, "page": 1, "total_required": "yes"})
|
||||
print(history)
|
||||
|
||||
print("List BillingPlan:")
|
||||
for plan in history.plans:
|
||||
print(dir(plan))
|
||||
print(plan.to_dict())
|
||||
print(" -> BillingPlan[%s]" % (plan.id))
|
||||
|
||||
#~ merchant = gocardless.client.merchant()
|
||||
#~ for paying_member in merchant.subscriptions():
|
||||
#~ user=paying_member.user()
|
||||
#~ yield {
|
||||
#~ 'email': user.email,
|
||||
#~ 'start_date': paying_member.created_at,
|
||||
#~ 'reference': paying_member.id,
|
||||
#~ 'amount': paying_member.amount}
|
||||
|
||||
def subscribe_confirm(self, args):
|
||||
if self.provider == 'gocardless':
|
||||
|
@ -362,9 +369,7 @@ class payment:
|
|||
confirm_details['successfull'] = False
|
||||
print('---------------------')
|
||||
print(args)
|
||||
|
||||
|
||||
|
||||
from pprint import pprint
|
||||
if self.provider == 'paypal':
|
||||
print(args.get('paymentId'))
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
from test_plus.test import TestCase
|
||||
# import unittest
|
||||
from unittest import skip
|
||||
from mock import patch, Mock
|
||||
|
||||
from mhackspace.subscriptions.payments import payment, gocardless_provider, braintree_provider
|
||||
|
@ -26,31 +26,73 @@ class TestPaymentGatewaysGocardless(TestCase):
|
|||
self.provider = gocardless_provider()
|
||||
return self.provider #self.provider
|
||||
|
||||
def test_confirm_subscription_callback(self):
|
||||
with patch('gocardless.client.confirm_resources') as mock_subscription:
|
||||
self.provider = gocardless_provider()
|
||||
|
||||
def test_fetch_subscription_gocardless(self):
|
||||
items = [Mock(
|
||||
@skip("Need to implement")
|
||||
@patch('mhackspace.subscriptions.payments.gocardless.client.subscription', autospec=True)
|
||||
def test_unsubscribe(self, mock_subscription):
|
||||
mock_subscription.return_value = Mock(success='success')
|
||||
mock_subscription.cancel.return_value = Mock(
|
||||
id='01',
|
||||
status='active',
|
||||
amount=20.00,
|
||||
reference='ref01',
|
||||
created_at='date'
|
||||
)]
|
||||
items[-1].user.return_value = Mock(email='test@test.com')
|
||||
)
|
||||
result = self.provider.cancel_subscription(reference='M01')
|
||||
|
||||
self.assertEqual(result.get('amount'), 20.00)
|
||||
self.assertEqual(result.get('start_date'), 'date')
|
||||
self.assertEqual(result.get('reference'), '01')
|
||||
self.assertEqual(result.get('success'), 'success')
|
||||
|
||||
# @patch('mhackspace.subscriptions.payments.gocardless.request.requests.get', autospec=True)
|
||||
@patch('mhackspace.subscriptions.payments.gocardless.client.subscription', autospec=True)
|
||||
@patch('mhackspace.subscriptions.payments.gocardless.client.confirm_resource', autospec=True)
|
||||
def test_confirm_subscription_callback(self, mock_confirm, mock_subscription):
|
||||
mock_confirm.return_value = Mock(success='success')
|
||||
mock_subscription.return_value = Mock(
|
||||
id='01',
|
||||
status='active',
|
||||
amount=20.00,
|
||||
created_at='date'
|
||||
)
|
||||
|
||||
request_params = {
|
||||
'resource_uri': 'http://gocardless/resource/url/01',
|
||||
'resource_id': '01',
|
||||
'resource_type': 'subscription',
|
||||
'signature': 'sig',
|
||||
'state': 'inactive'
|
||||
}
|
||||
|
||||
result = self.provider.subscribe_confirm(request_params)
|
||||
|
||||
self.assertEqual(result.get('amount'), 20.00)
|
||||
self.assertEqual(result.get('start_date'), 'date')
|
||||
self.assertEqual(result.get('reference'), '01')
|
||||
self.assertEqual(result.get('success'), 'success')
|
||||
|
||||
|
||||
def test_fetch_subscription_gocardless(self):
|
||||
item = Mock(
|
||||
id='01',
|
||||
status='active',
|
||||
amount=20.00,
|
||||
created_at='date'
|
||||
)
|
||||
item.user.return_value = Mock(email='test@test.com')
|
||||
|
||||
self.provider.client = Mock()
|
||||
self.provider.client.subscriptions = Mock(return_value=items)
|
||||
self.provider.client.subscriptions = Mock(return_value=[item])
|
||||
|
||||
# mock out gocardless subscriptions method, and return our own values
|
||||
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'), 'ref01')
|
||||
self.assertEqual(item.get('reference'), '01')
|
||||
self.assertEqual(item.get('start_date'), 'date')
|
||||
self.assertEqual(item.get('amount'), 20.00)
|
||||
|
||||
|
||||
class TestPaymentGatewaysBraintree(TestCase):
|
||||
class DisabledestPaymentGatewaysBraintree(TestCase):
|
||||
@patch('mhackspace.subscriptions.payments.braintree.Configuration.configure')
|
||||
def auth_braintree(self, mock_request):
|
||||
# mock braintree initalisation request
|
||||
|
|
|
@ -91,8 +91,25 @@
|
|||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<ul class="nav flex-column navbar-left" style="position:absolute;top:80px;background-color:#bbbbbb;">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="#">Profile</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/members">Members</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">Mailing List</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link disabled" href="/equipment">Equipment</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link disabled" href="/accounts/signout">Sign out</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="container">
|
||||
{% if messages %}
|
||||
{% for message in messages %}
|
||||
<div class="alert {% if message.tags %}alert-{{ message.tags }}{% endif %}">{{ message }}</div>
|
||||
|
|
|
@ -4,5 +4,7 @@
|
|||
{% block content %}
|
||||
<h2>Introduction</h2>
|
||||
Hackspaces are a shared space where artists, designers, makers, hackers, programmers, tinkerers, professionals and hobbyists can work on their projects, share knowledge and collaborate.We are in the process of developing Maidstone Hackspace. We're previous members of (ICMP) and looking to form a new space in the future. At the moment, communication is via google groups, email, and the website. If you're at all intrested please join our mailing list and make yourself known!
|
||||
|
||||
Click here to chat with us https://hangouts.google.com/group/oDcAL0nDfQYfO3qq1
|
||||
{% show_feeds %}
|
||||
{% endblock content %}
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
Members feed
|
||||
|
||||
<div class="row">
|
||||
<div class="">Users {{ paginator.count }}</div>
|
||||
<div class="">Members {{ total }}</div>
|
||||
<div class="col">Users {{ paginator.count }}</div>
|
||||
<div class="col">Members {{ total }}</div>
|
||||
</div>
|
||||
|
||||
<div class="card-deck-wrapper">
|
||||
|
@ -17,13 +17,14 @@ Members feed
|
|||
<h4 class="card-title">{{ member.name }}</h4>
|
||||
<p class="card-text">{{ member.users.description }}</p>
|
||||
<p class="card-text">{{ member.users.skills }}</p>
|
||||
<p class="card-text">{{ member.status }}</p>
|
||||
|
||||
{% for group in member.groups.all %}
|
||||
{{ group.name }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="card-block">
|
||||
<p class="card-text">{{ member.userblurb }}</p>
|
||||
<p class="card-text">{{ member.blurb }}</p>
|
||||
</div>
|
||||
<div class="card-block">
|
||||
<a href="{{ feed.url }}" class="card-link">View Original</a>
|
||||
|
|
|
@ -5,12 +5,25 @@
|
|||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
|
||||
<style>
|
||||
#membercard {
|
||||
color: #000;
|
||||
background-color: #8C50A5;
|
||||
border-radius: 14px;
|
||||
box-shadow: 0 2px 5px 0 rgba(0,0,0,0.5);
|
||||
background-image: url('/static/images/membership_card_background.png');
|
||||
width: 430px;
|
||||
height: 240px;
|
||||
margin-left: 20px;
|
||||
text-shadow: 1px 1px #FFF;
|
||||
position: relative;
|
||||
float: right;
|
||||
}
|
||||
</style>
|
||||
<h2>Profile</h2>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<img src="{{ user.image.profile.url }}" alt="Profile image"/>
|
||||
|
||||
<p>{{ user.username }}</p>
|
||||
<p>{{ user.name }}</p>
|
||||
<p>{{ user.email }}</p>
|
||||
|
@ -18,13 +31,18 @@
|
|||
<p>Member since</p>
|
||||
<p>Description: {{ blurb.description }}</p>
|
||||
<p>Skills: {{ blurb.description }}</p>
|
||||
|
||||
<h3>Membership status</h3>
|
||||
<p>Membership Status: {{ membership.get_status }}</p>
|
||||
<p>Last Payment: {{membership.date}}</p>
|
||||
<p>Amount: £{{membership.payment}}</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div id="membercard" class="registered">
|
||||
<div class="date">Joined </div>
|
||||
<div class="date">Joined {{membership.date}}</div>
|
||||
<div class="container">
|
||||
<div class="middle">
|
||||
<p>MHS{{ user.id|stringformat:"05d" }}</p><p>Change me</p>
|
||||
<p>MHS{{ user.id|stringformat:"05d" }}</p><p>{{user.name}}{{user.last_name}}</p>
|
||||
<a href="/profile/membership/cancel">Cancel Membership</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
[{"model": "auth.group", "pk": 1, "fields": {"name": "members", "permissions": []}}]
|
|
@ -2,9 +2,9 @@
|
|||
from django.db import models
|
||||
from django.forms import ModelForm
|
||||
|
||||
from .models import UserBlurb
|
||||
from .models import Blurb
|
||||
|
||||
class UserBlurbForm(ModelForm):
|
||||
class BlurbForm(ModelForm):
|
||||
class Meta:
|
||||
model = UserBlurb
|
||||
model = Blurb
|
||||
exclude = ['user']
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2016-09-23 04:36
|
||||
# Generated by Django 1.10.5 on 2017-01-28 19:46
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
import django.contrib.auth.models
|
||||
import django.contrib.auth.validators
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
import stdimage.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
@ -32,16 +35,38 @@ class Migration(migrations.Migration):
|
|||
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
|
||||
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
|
||||
('name', models.CharField(blank=True, max_length=255, verbose_name='Name of User')),
|
||||
('image', stdimage.models.StdImageField(blank=True, null=True, upload_to='avatars/')),
|
||||
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
|
||||
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
|
||||
],
|
||||
options={
|
||||
'verbose_name_plural': 'users',
|
||||
'verbose_name': 'user',
|
||||
'abstract': False,
|
||||
'verbose_name_plural': 'users',
|
||||
},
|
||||
managers=[
|
||||
('objects', django.contrib.auth.models.UserManager()),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Blurb',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('skills', models.CharField(max_length=255)),
|
||||
('description', models.TextField()),
|
||||
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Membership',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('payment', models.DecimalField(decimal_places=2, default=0.0, max_digits=6)),
|
||||
('date', models.DateTimeField()),
|
||||
('reference', models.CharField(max_length=255)),
|
||||
('status', models.PositiveSmallIntegerField(default=0)),
|
||||
('email', models.CharField(max_length=255)),
|
||||
('user', models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='user', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-01-06 08:53
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('users', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Membership',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('payment', models.DecimalField(decimal_places=2, max_digits=6)),
|
||||
('date', models.DateTimeField()),
|
||||
('reference', models.CharField(max_length=255)),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='UserBlurb',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('skills', models.CharField(max_length=255)),
|
||||
('description', models.TextField()),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
]
|
|
@ -1,20 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-01-06 19:03
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('users', '0002_membership_userblurb'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='image',
|
||||
field=models.ImageField(blank=True, null=True, upload_to='/upload/avatars'),
|
||||
),
|
||||
]
|
|
@ -1,24 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-01-06 20:30
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('users', '0003_user_image'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameModel(
|
||||
old_name='Membership',
|
||||
new_name='UserMembership',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='user',
|
||||
name='image',
|
||||
field=models.ImageField(blank=True, null=True, upload_to='avatars/'),
|
||||
),
|
||||
]
|
|
@ -1,28 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.4 on 2017-01-08 00:50
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import stdimage.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('users', '0004_auto_20170106_2030'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='user',
|
||||
name='image',
|
||||
field=stdimage.models.StdImageField(blank=True, null=True, upload_to='avatars/'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='userblurb',
|
||||
name='user',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='users', to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
]
|
|
@ -29,14 +29,44 @@ class User(AbstractUser):
|
|||
return reverse('users:detail', kwargs={'username': self.username})
|
||||
|
||||
|
||||
class UserBlurb(models.Model):
|
||||
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='users')
|
||||
class Blurb(models.Model):
|
||||
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='+')
|
||||
skills = models.CharField(max_length=255)
|
||||
description = models.TextField()
|
||||
|
||||
|
||||
class UserMembership(models.Model):
|
||||
user = models.ForeignKey(settings.AUTH_USER_MODEL)
|
||||
payment = models.DecimalField(max_digits=6, decimal_places=2)
|
||||
MEMBERSHIP_STRING = {
|
||||
0: 'Guest user',
|
||||
1: 'Active membership',
|
||||
3: 'Membership Expired'
|
||||
}
|
||||
|
||||
MEMBERSHIP_STATUS = {
|
||||
'active': 1,
|
||||
'cancelled': 2
|
||||
}
|
||||
|
||||
class Membership(models.Model):
|
||||
user = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
null=True, blank=True,
|
||||
default=None,
|
||||
related_name='user'
|
||||
)
|
||||
payment = models.DecimalField(max_digits=6, decimal_places=2, default=0.0)
|
||||
date = models.DateTimeField()
|
||||
reference = models.CharField(max_length=255)
|
||||
status = models.PositiveSmallIntegerField(default=0)
|
||||
email = models.CharField(max_length=255)
|
||||
|
||||
@property
|
||||
def get_status(self):
|
||||
return MEMBERSHIP_STRING[self.status]
|
||||
|
||||
def lookup_status(name):
|
||||
if not name:
|
||||
return 0
|
||||
return MEMBERSHIP_STATUS.get(name.lower(), 0)
|
||||
|
||||
def __str__(self):
|
||||
return self.reference
|
||||
|
|
|
@ -7,10 +7,10 @@ from django.views.generic import DetailView, ListView, RedirectView, UpdateView
|
|||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
|
||||
from .models import User
|
||||
from .models import UserBlurb
|
||||
from .models import UserMembership
|
||||
from .models import Blurb
|
||||
from .models import Membership
|
||||
|
||||
from .forms import UserBlurbForm
|
||||
from .forms import BlurbForm
|
||||
|
||||
class UserDetailView(LoginRequiredMixin, DetailView):
|
||||
model = User
|
||||
|
@ -21,8 +21,8 @@ class UserDetailView(LoginRequiredMixin, DetailView):
|
|||
def get_context_data(self, **kwargs):
|
||||
# xxx will be available in the template as the related objects
|
||||
context = super(UserDetailView, self).get_context_data(**kwargs)
|
||||
context['blurb'] = UserBlurb.objects.filter(user=self.get_object()).first()
|
||||
context['membership'] = UserMembership.objects.filter(user=self.get_object()).first()
|
||||
context['blurb'] = Blurb.objects.filter(user=self.get_object()).first()
|
||||
context['membership'] = Membership.objects.filter(user=self.get_object()).first()
|
||||
return context
|
||||
|
||||
class UserRedirectView(LoginRequiredMixin, RedirectView):
|
||||
|
@ -45,8 +45,8 @@ class UserUpdateView(LoginRequiredMixin, UpdateView):
|
|||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(UserUpdateView, self).get_context_data(**kwargs)
|
||||
profile = UserBlurb.objects.filter(user=self.get_object()).first()
|
||||
context['form_blurb'] = UserBlurbForm(instance=profile)
|
||||
profile = Blurb.objects.filter(user=self.get_object()).first()
|
||||
context['form_blurb'] = BlurbForm(instance=profile)
|
||||
return context
|
||||
|
||||
def get_object(self):
|
||||
|
@ -54,8 +54,8 @@ class UserUpdateView(LoginRequiredMixin, UpdateView):
|
|||
return User.objects.get(username=self.request.user.username)
|
||||
|
||||
def form_valid(self, form):
|
||||
profile = UserBlurb.objects.filter(user=self.get_object()).first()
|
||||
form_blurb = UserBlurbForm(self.request.POST, instance=profile)
|
||||
profile = Blurb.objects.filter(user=self.get_object()).first()
|
||||
form_blurb = BlurbForm(self.request.POST, instance=profile)
|
||||
if form_blurb.is_valid():
|
||||
blurb_model = form_blurb.save(commit=False)
|
||||
blurb_model.user = self.request.user
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
# This file is here because many Platforms as a Service look for
|
||||
# requirements.txt in the root directory of a project.
|
||||
-r requirements/production.txt
|
||||
|
|
@ -42,16 +42,19 @@ django-redis==4.7.0
|
|||
redis>=2.10.5
|
||||
|
||||
|
||||
|
||||
|
||||
rcssmin==1.0.6
|
||||
rcssmin==1.0.6
|
||||
django-compressor==2.1
|
||||
django-sass-processor==0.5.3
|
||||
|
||||
lxml==3.7.2
|
||||
|
||||
|
||||
# Your custom requirements go here
|
||||
mock==2.0.0
|
||||
|
||||
gocardless
|
||||
braintree
|
||||
braintree==3.34.0
|
||||
|
||||
# Your custom requirements go here
|
||||
-e git+https://github.com/olymk2/scaffold.git#egg=scaffold
|
||||
django-autofixture==0.12.1
|
||||
|
||||
git+https://github.com/olymk2/scaffold.git
|
||||
|
|
Loading…
Reference in New Issue