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
|
- USE_DOCKER=yes
|
||||||
- DJANGO_SETTINGS_MODULE=config.settings.test
|
- DJANGO_SETTINGS_MODULE=config.settings.test
|
||||||
commands:
|
commands:
|
||||||
- python manage.py test mhackspace.subscriptions --verbosity 2
|
- cp -n env.example .env
|
||||||
|
- python manage.py test mhackspace --verbosity 2
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#volumes:
|
#volumes:
|
||||||
# postgres_data_dev: {}
|
# 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
|
COPY . /app
|
||||||
RUN chown -R django /app
|
RUN chown -R django /app
|
||||||
|
RUN mkdir -p /data/sockets
|
||||||
|
|
||||||
COPY ./compose/django/gunicorn.sh /gunicorn.sh
|
COPY ./compose/django/gunicorn.sh /gunicorn.sh
|
||||||
COPY ./compose/django/entrypoint.sh /entrypoint.sh
|
COPY ./compose/django/entrypoint.sh /entrypoint.sh
|
||||||
|
@ -19,7 +20,8 @@ RUN sed -i 's/\r//' /entrypoint.sh \
|
||||||
&& chmod +x /entrypoint.sh \
|
&& chmod +x /entrypoint.sh \
|
||||||
&& chown django /entrypoint.sh \
|
&& chown django /entrypoint.sh \
|
||||||
&& chmod +x /gunicorn.sh \
|
&& chmod +x /gunicorn.sh \
|
||||||
&& chown django /gunicorn.sh
|
&& chown django /gunicorn.sh \
|
||||||
|
&& chown django /data/sockets
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
|
|
@ -33,5 +33,4 @@ until postgres_ready; do
|
||||||
sleep 1
|
sleep 1
|
||||||
done
|
done
|
||||||
|
|
||||||
>&2 echo "Postgres is up - continuing..."
|
|
||||||
exec $cmd
|
exec $cmd
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
python /app/manage.py collectstatic --noinput
|
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 start.sh /start.sh
|
||||||
ADD nginx-secure.conf /etc/nginx/nginx-secure.conf
|
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
|
CMD /start.sh
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,8 @@ http {
|
||||||
#gzip on;
|
#gzip on;
|
||||||
|
|
||||||
upstream app {
|
upstream app {
|
||||||
server django:5000;
|
#server django:5000;
|
||||||
|
server unix:/data/sockets/gunicorn.sock;
|
||||||
}
|
}
|
||||||
|
|
||||||
server {
|
server {
|
||||||
|
@ -36,12 +37,12 @@ http {
|
||||||
|
|
||||||
server_name ___my.example.com___ ;
|
server_name ___my.example.com___ ;
|
||||||
|
|
||||||
location /.well-known/acme-challenge {
|
# location /.well-known/acme-challenge {
|
||||||
proxy_pass http://certbot:80;
|
# proxy_pass http://certbot:80;
|
||||||
proxy_set_header Host $host;
|
# proxy_set_header Host $host;
|
||||||
proxy_set_header X-Forwarded-For $remote_addr;
|
# proxy_set_header X-Forwarded-For $remote_addr;
|
||||||
proxy_set_header X-Forwarded-Proto https;
|
# proxy_set_header X-Forwarded-Proto https;
|
||||||
}
|
# }
|
||||||
|
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
|
|
|
@ -16,7 +16,7 @@ ROOT_DIR = environ.Path(__file__) - 3 # (mhackspace/config/settings/common.py -
|
||||||
APPS_DIR = ROOT_DIR.path('mhackspace')
|
APPS_DIR = ROOT_DIR.path('mhackspace')
|
||||||
|
|
||||||
env = environ.Env()
|
env = environ.Env()
|
||||||
env.read_env()
|
env.read_env('%s/.env' % ROOT_DIR)
|
||||||
|
|
||||||
# APP CONFIGURATION
|
# APP CONFIGURATION
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
@ -48,6 +48,7 @@ LOCAL_APPS = (
|
||||||
# custom users app
|
# custom users app
|
||||||
# Your stuff: custom apps go here
|
# Your stuff: custom apps go here
|
||||||
'mhackspace.users.apps.UsersConfig',
|
'mhackspace.users.apps.UsersConfig',
|
||||||
|
'mhackspace.base',
|
||||||
'mhackspace.subscriptions',
|
'mhackspace.subscriptions',
|
||||||
'mhackspace.feeds',
|
'mhackspace.feeds',
|
||||||
'mhackspace.contact',
|
'mhackspace.contact',
|
||||||
|
@ -189,6 +190,7 @@ STATICFILES_DIRS = (
|
||||||
STATICFILES_FINDERS = (
|
STATICFILES_FINDERS = (
|
||||||
'django.contrib.staticfiles.finders.FileSystemFinder',
|
'django.contrib.staticfiles.finders.FileSystemFinder',
|
||||||
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
|
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
|
||||||
|
'sass_processor.finders.CssFinder',
|
||||||
)
|
)
|
||||||
|
|
||||||
# MEDIA CONFIGURATION
|
# MEDIA CONFIGURATION
|
||||||
|
@ -252,7 +254,7 @@ LOGIN_URL = 'account_login'
|
||||||
AUTOSLUG_SLUGIFY_FUNCTION = 'slugify.slugify'
|
AUTOSLUG_SLUGIFY_FUNCTION = 'slugify.slugify'
|
||||||
# django-compressor
|
# django-compressor
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
INSTALLED_APPS += ("compressor", )
|
INSTALLED_APPS += ("compressor", 'sass_processor',)
|
||||||
STATICFILES_FINDERS += ("compressor.finders.CompressorFinder", )
|
STATICFILES_FINDERS += ("compressor.finders.CompressorFinder", )
|
||||||
|
|
||||||
# Location of root django.contrib.admin URL, use {% url 'admin:index' %}
|
# Location of root django.contrib.admin URL, use {% url 'admin:index' %}
|
||||||
|
@ -262,7 +264,7 @@ ADMIN_URL = '^admin/'
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
payment_providers = {
|
PAYMENT_PROVIDERS = {
|
||||||
'braintree': {
|
'braintree': {
|
||||||
'mode': 'sandbox',
|
'mode': 'sandbox',
|
||||||
'credentials': {
|
'credentials': {
|
||||||
|
@ -275,7 +277,7 @@ payment_providers = {
|
||||||
"mode": "sandbox", # sandbox or live
|
"mode": "sandbox", # sandbox or live
|
||||||
'credentials': {
|
'credentials': {
|
||||||
"mode": "sandbox", # sandbox or live
|
"mode": "sandbox", # sandbox or live
|
||||||
"client_id": end('PAYPAL_CLIENT_ID'),
|
"client_id": env('PAYPAL_CLIENT_ID'),
|
||||||
"client_secret": env('PAYPAL_CLIENT_SECRET')}
|
"client_secret": env('PAYPAL_CLIENT_SECRET')}
|
||||||
},
|
},
|
||||||
'gocardless':{
|
'gocardless':{
|
||||||
|
|
|
@ -56,6 +56,9 @@ X_FRAME_OPTIONS = 'DENY'
|
||||||
# Hosts/domain names that are valid for this site
|
# Hosts/domain names that are valid for this site
|
||||||
# See https://docs.djangoproject.com/en/1.6/ref/settings/#allowed-hosts
|
# 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 = env.list('DJANGO_ALLOWED_HOSTS', default=['maidstone-hackspace.org.uk'])
|
||||||
|
ALLOWED_HOSTS.append('172.*')
|
||||||
|
ALLOWED_HOSTS.append('172.18.0.5')
|
||||||
|
|
||||||
# END SITE CONFIGURATION
|
# END SITE CONFIGURATION
|
||||||
|
|
||||||
INSTALLED_APPS += ('gunicorn', )
|
INSTALLED_APPS += ('gunicorn', )
|
||||||
|
|
|
@ -1,8 +1,20 @@
|
||||||
version: '2'
|
version: '2'
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
postgres_data: {}
|
gunicorn_socket:
|
||||||
postgres_backup: {}
|
driver: local
|
||||||
|
postgres_data:
|
||||||
|
driver: local
|
||||||
|
postgres_backup:
|
||||||
|
driver: local
|
||||||
|
|
||||||
|
|
||||||
|
# volumes:
|
||||||
|
# sockets:
|
||||||
|
# driver: local
|
||||||
|
# data:
|
||||||
|
# driver: local
|
||||||
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
|
@ -22,37 +34,41 @@ services:
|
||||||
- redis
|
- redis
|
||||||
command: /gunicorn.sh
|
command: /gunicorn.sh
|
||||||
env_file: .env
|
env_file: .env
|
||||||
|
volumes:
|
||||||
|
- .:/app
|
||||||
|
- gunicorn_socket:/data/sockets
|
||||||
|
|
||||||
nginx:
|
nginx:
|
||||||
build: ./compose/nginx
|
build: ./compose/nginx
|
||||||
|
env_file: .env
|
||||||
depends_on:
|
depends_on:
|
||||||
- django
|
- django
|
||||||
- certbot
|
# - certbot
|
||||||
|
|
||||||
ports:
|
|
||||||
- "0.0.0.0:80:80"
|
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
- MY_DOMAIN_NAME=maidstone-hackspace.org.uk
|
- MY_DOMAIN_NAME=maidstone-hackspace.org.uk
|
||||||
ports:
|
ports:
|
||||||
- "0.0.0.0:80:80"
|
- "0.0.0.0:80:80"
|
||||||
- "0.0.0.0:443:443"
|
|
||||||
volumes:
|
volumes:
|
||||||
- /etc/letsencrypt:/etc/letsencrypt
|
- .:/app
|
||||||
- /var/lib/letsencrypt:/var/lib/letsencrypt
|
- gunicorn_socket:/data/sockets
|
||||||
|
# - "0.0.0.0:443:443"
|
||||||
|
# volumes:
|
||||||
|
# - /etc/letsencrypt:/etc/letsencrypt
|
||||||
|
# - /var/lib/letsencrypt:/var/lib/letsencrypt
|
||||||
|
|
||||||
certbot:
|
# certbot:
|
||||||
image: quay.io/letsencrypt/letsencrypt
|
# 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"
|
# 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: ""
|
# entrypoint: ""
|
||||||
volumes:
|
# volumes:
|
||||||
- /etc/letsencrypt:/etc/letsencrypt
|
# - /etc/letsencrypt:/etc/letsencrypt
|
||||||
- /var/lib/letsencrypt:/var/lib/letsencrypt
|
# - /var/lib/letsencrypt:/var/lib/letsencrypt
|
||||||
ports:
|
# ports:
|
||||||
- "80"
|
# - "80"
|
||||||
- "443"
|
# - "443"
|
||||||
environment:
|
# environment:
|
||||||
- TERM=xterm
|
# - TERM=xterm
|
||||||
|
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
|
|
20
env.example
20
env.example
|
@ -29,16 +29,16 @@ DJANGO_ACCOUNT_ALLOW_REGISTRATION=True
|
||||||
COMPRESS_ENABLED=
|
COMPRESS_ENABLED=
|
||||||
|
|
||||||
|
|
||||||
PAYMENT_ENVIRONMENT = 'sandbox'
|
PAYMENT_ENVIRONMENT=sandbox
|
||||||
|
|
||||||
BRAINTREE_MERCHANT_ID = ''
|
BRAINTREE_MERCHANT_ID=demo
|
||||||
BRAINTREE_PUBLIC_KEY = ''
|
BRAINTREE_PUBLIC_KEY=demo
|
||||||
BRAINTREE_PRIVATE_KEY = ''
|
BRAINTREE_PRIVATE_KEY=demo
|
||||||
|
|
||||||
PAYPAL_CLIENT_ID = ""
|
PAYPAL_CLIENT_ID=demo
|
||||||
PAYPAL_CLIENT_SECRET = ""
|
PAYPAL_CLIENT_SECRET=demo
|
||||||
|
|
||||||
GOCARDLESS_APP_ID = ''
|
GOCARDLESS_APP_ID=demo
|
||||||
GOCARDLESS_APP_SECRET = ''
|
GOCARDLESS_APP_SECRET=demo
|
||||||
GOCARDLESS_ACCESS_TOKEN = ''
|
GOCARDLESS_ACCESS_TOKEN=demo
|
||||||
GOCARDLESS_MERCHANT_ID = ''
|
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 -*-
|
# -*- 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 __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import migrations, models
|
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):
|
class Migration(migrations.Migration):
|
||||||
|
@ -13,14 +17,35 @@ class Migration(migrations.Migration):
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
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(
|
migrations.CreateModel(
|
||||||
name='Feed',
|
name='Feed',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
('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)),
|
('author', models.CharField(max_length=255)),
|
||||||
('tags', models.CharField(max_length=255)),
|
('tags', models.CharField(blank=True, max_length=255)),
|
||||||
('image', models.ImageField(upload_to='')),
|
('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):
|
class MemberListView(LoginRequiredMixin, ListView):
|
||||||
template_name = 'pages/members.html'
|
template_name = 'pages/members.html'
|
||||||
queryset = User.objects.prefetch_related('users', 'groups')
|
queryset = User.objects.prefetch_related('user', 'groups')
|
||||||
paginate_by = 10
|
paginate_by = 10
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(MemberListView, self).get_context_data(**kwargs)
|
context = super(MemberListView, self).get_context_data(**kwargs)
|
||||||
context['members'] = self.get_queryset()
|
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
|
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():
|
for sub in provider.fetch_subscriptions():
|
||||||
self.stdout.write(
|
self.stdout.write(
|
||||||
self.style.SUCCESS(
|
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 gocardless
|
||||||
import braintree
|
import braintree
|
||||||
|
|
||||||
from django.conf.settings import payment_providers
|
from django.conf import settings
|
||||||
|
payment_providers = settings.PAYMENT_PROVIDERS
|
||||||
|
|
||||||
# import gocardless_pro
|
# import gocardless_pro
|
||||||
# import paypalrestsdk as paypal
|
# import paypalrestsdk as paypal
|
||||||
|
@ -45,18 +46,48 @@ class gocardless_provider:
|
||||||
return {
|
return {
|
||||||
'amount': subscription.amount,
|
'amount': subscription.amount,
|
||||||
'start_date': subscription.created_at,
|
'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):
|
def fetch_subscriptions(self):
|
||||||
for paying_member in self.client.subscriptions():
|
for paying_member in self.client.subscriptions():
|
||||||
user=paying_member.user()
|
user=paying_member.user()
|
||||||
# for bill in paying_member.bills():
|
|
||||||
# print('test')
|
#gocardless does not have a reference so we use the id instead
|
||||||
# print(dir(bill))
|
|
||||||
# print(bill.created_at)
|
|
||||||
# print(dir(paying_member))
|
|
||||||
# print(paying_member.reference_fields)
|
|
||||||
yield {
|
yield {
|
||||||
'status': paying_member.status,
|
'status': paying_member.status,
|
||||||
'email': user.email,
|
'email': user.email,
|
||||||
|
@ -70,6 +101,16 @@ class gocardless_provider:
|
||||||
def get_token(self):
|
def get_token(self):
|
||||||
return 'N/A'
|
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'):
|
def create_subscription(self, amount, name, redirect_success, redirect_failure, interval_unit='month', interval_length='1'):
|
||||||
return gocardless.client.new_subscription_url(
|
return gocardless.client.new_subscription_url(
|
||||||
amount=float(amount),
|
amount=float(amount),
|
||||||
|
@ -199,40 +240,6 @@ class payment:
|
||||||
'reference': paying_member.id,
|
'reference': paying_member.id,
|
||||||
'amount': paying_member.amount}
|
'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):
|
def subscribe_confirm(self, args):
|
||||||
if self.provider == 'gocardless':
|
if self.provider == 'gocardless':
|
||||||
|
@ -362,9 +369,7 @@ class payment:
|
||||||
confirm_details['successfull'] = False
|
confirm_details['successfull'] = False
|
||||||
print('---------------------')
|
print('---------------------')
|
||||||
print(args)
|
print(args)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
from pprint import pprint
|
from pprint import pprint
|
||||||
if self.provider == 'paypal':
|
if self.provider == 'paypal':
|
||||||
print(args.get('paymentId'))
|
print(args.get('paymentId'))
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from test_plus.test import TestCase
|
from test_plus.test import TestCase
|
||||||
# import unittest
|
from unittest import skip
|
||||||
from mock import patch, Mock
|
from mock import patch, Mock
|
||||||
|
|
||||||
from mhackspace.subscriptions.payments import payment, gocardless_provider, braintree_provider
|
from mhackspace.subscriptions.payments import payment, gocardless_provider, braintree_provider
|
||||||
|
@ -26,31 +26,73 @@ class TestPaymentGatewaysGocardless(TestCase):
|
||||||
self.provider = gocardless_provider()
|
self.provider = gocardless_provider()
|
||||||
return self.provider #self.provider
|
return self.provider #self.provider
|
||||||
|
|
||||||
def test_confirm_subscription_callback(self):
|
@skip("Need to implement")
|
||||||
with patch('gocardless.client.confirm_resources') as mock_subscription:
|
@patch('mhackspace.subscriptions.payments.gocardless.client.subscription', autospec=True)
|
||||||
self.provider = gocardless_provider()
|
def test_unsubscribe(self, mock_subscription):
|
||||||
|
mock_subscription.return_value = Mock(success='success')
|
||||||
def test_fetch_subscription_gocardless(self):
|
mock_subscription.cancel.return_value = Mock(
|
||||||
items = [Mock(
|
|
||||||
id='01',
|
id='01',
|
||||||
status='active',
|
status='active',
|
||||||
amount=20.00,
|
amount=20.00,
|
||||||
reference='ref01',
|
|
||||||
created_at='date'
|
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 = 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():
|
for item in self.provider.fetch_subscriptions():
|
||||||
self.assertEqual(item.get('status'), 'active')
|
self.assertEqual(item.get('status'), 'active')
|
||||||
self.assertEqual(item.get('email'), 'test@test.com')
|
self.assertEqual(item.get('email'), 'test@test.com')
|
||||||
self.assertEqual(item.get('reference'), 'ref01')
|
self.assertEqual(item.get('reference'), '01')
|
||||||
self.assertEqual(item.get('start_date'), 'date')
|
self.assertEqual(item.get('start_date'), 'date')
|
||||||
self.assertEqual(item.get('amount'), 20.00)
|
self.assertEqual(item.get('amount'), 20.00)
|
||||||
|
|
||||||
|
|
||||||
class TestPaymentGatewaysBraintree(TestCase):
|
class DisabledestPaymentGatewaysBraintree(TestCase):
|
||||||
@patch('mhackspace.subscriptions.payments.braintree.Configuration.configure')
|
@patch('mhackspace.subscriptions.payments.braintree.Configuration.configure')
|
||||||
def auth_braintree(self, mock_request):
|
def auth_braintree(self, mock_request):
|
||||||
# mock braintree initalisation request
|
# mock braintree initalisation request
|
||||||
|
|
|
@ -91,8 +91,25 @@
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</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 %}
|
{% if messages %}
|
||||||
{% for message in messages %}
|
{% for message in messages %}
|
||||||
<div class="alert {% if message.tags %}alert-{{ message.tags }}{% endif %}">{{ message }}</div>
|
<div class="alert {% if message.tags %}alert-{{ message.tags }}{% endif %}">{{ message }}</div>
|
||||||
|
|
|
@ -4,5 +4,7 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h2>Introduction</h2>
|
<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!
|
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 %}
|
{% show_feeds %}
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
Members feed
|
Members feed
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="">Users {{ paginator.count }}</div>
|
<div class="col">Users {{ paginator.count }}</div>
|
||||||
<div class="">Members {{ total }}</div>
|
<div class="col">Members {{ total }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-deck-wrapper">
|
<div class="card-deck-wrapper">
|
||||||
|
@ -17,13 +17,14 @@ Members feed
|
||||||
<h4 class="card-title">{{ member.name }}</h4>
|
<h4 class="card-title">{{ member.name }}</h4>
|
||||||
<p class="card-text">{{ member.users.description }}</p>
|
<p class="card-text">{{ member.users.description }}</p>
|
||||||
<p class="card-text">{{ member.users.skills }}</p>
|
<p class="card-text">{{ member.users.skills }}</p>
|
||||||
|
<p class="card-text">{{ member.status }}</p>
|
||||||
|
|
||||||
{% for group in member.groups.all %}
|
{% for group in member.groups.all %}
|
||||||
{{ group.name }}
|
{{ group.name }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
<div class="card-block">
|
<div class="card-block">
|
||||||
<p class="card-text">{{ member.userblurb }}</p>
|
<p class="card-text">{{ member.blurb }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-block">
|
<div class="card-block">
|
||||||
<a href="{{ feed.url }}" class="card-link">View Original</a>
|
<a href="{{ feed.url }}" class="card-link">View Original</a>
|
||||||
|
|
|
@ -5,12 +5,25 @@
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container">
|
<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>
|
<h2>Profile</h2>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<img src="{{ user.image.profile.url }}" alt="Profile image"/>
|
<img src="{{ user.image.profile.url }}" alt="Profile image"/>
|
||||||
|
|
||||||
<p>{{ user.username }}</p>
|
<p>{{ user.username }}</p>
|
||||||
<p>{{ user.name }}</p>
|
<p>{{ user.name }}</p>
|
||||||
<p>{{ user.email }}</p>
|
<p>{{ user.email }}</p>
|
||||||
|
@ -18,13 +31,18 @@
|
||||||
<p>Member since</p>
|
<p>Member since</p>
|
||||||
<p>Description: {{ blurb.description }}</p>
|
<p>Description: {{ blurb.description }}</p>
|
||||||
<p>Skills: {{ 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>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div id="membercard" class="registered">
|
<div id="membercard" class="registered">
|
||||||
<div class="date">Joined </div>
|
<div class="date">Joined {{membership.date}}</div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="middle">
|
<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>
|
<a href="/profile/membership/cancel">Cancel Membership</a>
|
||||||
</div>
|
</div>
|
||||||
</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.db import models
|
||||||
from django.forms import ModelForm
|
from django.forms import ModelForm
|
||||||
|
|
||||||
from .models import UserBlurb
|
from .models import Blurb
|
||||||
|
|
||||||
class UserBlurbForm(ModelForm):
|
class BlurbForm(ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = UserBlurb
|
model = Blurb
|
||||||
exclude = ['user']
|
exclude = ['user']
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- 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 __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
import django.contrib.auth.models
|
import django.contrib.auth.models
|
||||||
import django.contrib.auth.validators
|
import django.contrib.auth.validators
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
import django.utils.timezone
|
import django.utils.timezone
|
||||||
|
import stdimage.models
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
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')),
|
('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')),
|
('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')),
|
('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')),
|
('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')),
|
('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={
|
options={
|
||||||
'verbose_name_plural': 'users',
|
|
||||||
'verbose_name': 'user',
|
'verbose_name': 'user',
|
||||||
'abstract': False,
|
'abstract': False,
|
||||||
|
'verbose_name_plural': 'users',
|
||||||
},
|
},
|
||||||
managers=[
|
managers=[
|
||||||
('objects', django.contrib.auth.models.UserManager()),
|
('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})
|
return reverse('users:detail', kwargs={'username': self.username})
|
||||||
|
|
||||||
|
|
||||||
class UserBlurb(models.Model):
|
class Blurb(models.Model):
|
||||||
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='users')
|
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='+')
|
||||||
skills = models.CharField(max_length=255)
|
skills = models.CharField(max_length=255)
|
||||||
description = models.TextField()
|
description = models.TextField()
|
||||||
|
|
||||||
|
|
||||||
class UserMembership(models.Model):
|
MEMBERSHIP_STRING = {
|
||||||
user = models.ForeignKey(settings.AUTH_USER_MODEL)
|
0: 'Guest user',
|
||||||
payment = models.DecimalField(max_digits=6, decimal_places=2)
|
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()
|
date = models.DateTimeField()
|
||||||
reference = models.CharField(max_length=255)
|
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 django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
|
||||||
from .models import User
|
from .models import User
|
||||||
from .models import UserBlurb
|
from .models import Blurb
|
||||||
from .models import UserMembership
|
from .models import Membership
|
||||||
|
|
||||||
from .forms import UserBlurbForm
|
from .forms import BlurbForm
|
||||||
|
|
||||||
class UserDetailView(LoginRequiredMixin, DetailView):
|
class UserDetailView(LoginRequiredMixin, DetailView):
|
||||||
model = User
|
model = User
|
||||||
|
@ -21,8 +21,8 @@ class UserDetailView(LoginRequiredMixin, DetailView):
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
# xxx will be available in the template as the related objects
|
# xxx will be available in the template as the related objects
|
||||||
context = super(UserDetailView, self).get_context_data(**kwargs)
|
context = super(UserDetailView, self).get_context_data(**kwargs)
|
||||||
context['blurb'] = UserBlurb.objects.filter(user=self.get_object()).first()
|
context['blurb'] = Blurb.objects.filter(user=self.get_object()).first()
|
||||||
context['membership'] = UserMembership.objects.filter(user=self.get_object()).first()
|
context['membership'] = Membership.objects.filter(user=self.get_object()).first()
|
||||||
return context
|
return context
|
||||||
|
|
||||||
class UserRedirectView(LoginRequiredMixin, RedirectView):
|
class UserRedirectView(LoginRequiredMixin, RedirectView):
|
||||||
|
@ -45,8 +45,8 @@ class UserUpdateView(LoginRequiredMixin, UpdateView):
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(UserUpdateView, self).get_context_data(**kwargs)
|
context = super(UserUpdateView, self).get_context_data(**kwargs)
|
||||||
profile = UserBlurb.objects.filter(user=self.get_object()).first()
|
profile = Blurb.objects.filter(user=self.get_object()).first()
|
||||||
context['form_blurb'] = UserBlurbForm(instance=profile)
|
context['form_blurb'] = BlurbForm(instance=profile)
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
|
@ -54,8 +54,8 @@ class UserUpdateView(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):
|
||||||
profile = UserBlurb.objects.filter(user=self.get_object()).first()
|
profile = Blurb.objects.filter(user=self.get_object()).first()
|
||||||
form_blurb = UserBlurbForm(self.request.POST, instance=profile)
|
form_blurb = BlurbForm(self.request.POST, instance=profile)
|
||||||
if form_blurb.is_valid():
|
if form_blurb.is_valid():
|
||||||
blurb_model = form_blurb.save(commit=False)
|
blurb_model = form_blurb.save(commit=False)
|
||||||
blurb_model.user = self.request.user
|
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
|
redis>=2.10.5
|
||||||
|
|
||||||
|
|
||||||
|
rcssmin==1.0.6
|
||||||
|
|
||||||
rcssmin==1.0.6
|
|
||||||
django-compressor==2.1
|
django-compressor==2.1
|
||||||
|
django-sass-processor==0.5.3
|
||||||
|
|
||||||
lxml==3.7.2
|
lxml==3.7.2
|
||||||
|
|
||||||
|
|
||||||
|
# Your custom requirements go here
|
||||||
mock==2.0.0
|
mock==2.0.0
|
||||||
|
|
||||||
gocardless
|
gocardless
|
||||||
braintree
|
braintree==3.34.0
|
||||||
|
|
||||||
# Your custom requirements go here
|
django-autofixture==0.12.1
|
||||||
-e git+https://github.com/olymk2/scaffold.git#egg=scaffold
|
|
||||||
|
git+https://github.com/olymk2/scaffold.git
|
||||||
|
|
Loading…
Reference in New Issue