Merge branch 'feature-payment' into develop
This commit is contained in:
@@ -13,3 +13,5 @@ logs/
|
|||||||
/media/
|
/media/
|
||||||
node_modules/
|
node_modules/
|
||||||
/.coverage
|
/.coverage
|
||||||
|
db.sqlite3
|
||||||
|
requirements_henu.txt
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from members.models import Member, MemberRequest
|
from members.models import Member, Request
|
||||||
|
|
||||||
# Register your models here.
|
# Register your models here.
|
||||||
admin.site.register(Member)
|
admin.site.register(Member)
|
||||||
admin.site.register(MemberRequest)
|
admin.site.register(Request)
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9 on 2017-03-29 16:28
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import django.utils.timezone
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('members', '0002_auto_20170329_1857'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Payment',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('date', models.DateTimeField(default=datetime.datetime(1970, 1, 1, 2, 0))),
|
||||||
|
('source', models.CharField(max_length=255)),
|
||||||
|
('first_name', models.CharField(max_length=255)),
|
||||||
|
('last_name', models.CharField(max_length=255)),
|
||||||
|
('email', models.EmailField(max_length=255)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Request',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('first_name', models.CharField(max_length=127)),
|
||||||
|
('last_name', models.CharField(max_length=127)),
|
||||||
|
('email', models.EmailField(max_length=254)),
|
||||||
|
('POR', models.CharField(default='ei_tiedossa', max_length=255)),
|
||||||
|
('AYY', models.BooleanField(default=False)),
|
||||||
|
('jas', models.BooleanField(default=False)),
|
||||||
|
('submitted', models.DateTimeField(default=django.utils.timezone.now)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='memberrequest',
|
||||||
|
name='member',
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='member',
|
||||||
|
name='POR',
|
||||||
|
field=models.CharField(default='ei_tiedossa', max_length=255),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='member',
|
||||||
|
name='paid',
|
||||||
|
field=models.DateTimeField(default=django.utils.timezone.now),
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='MemberRequest',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='payment',
|
||||||
|
name='member',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='members.Member'),
|
||||||
|
),
|
||||||
|
]
|
||||||
+32
-93
@@ -4,115 +4,54 @@ from datetime import datetime
|
|||||||
from io import StringIO
|
from io import StringIO
|
||||||
import csv
|
import csv
|
||||||
|
|
||||||
|
class BaseMember(models.Model):
|
||||||
class Member(models.Model):
|
|
||||||
'''
|
'''
|
||||||
Member model represets one member on the registry
|
Base model for member.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
first_name = models.CharField(max_length=127)
|
first_name = models.CharField(max_length=127)
|
||||||
last_name = models.CharField(max_length=127)
|
last_name = models.CharField(max_length=127)
|
||||||
email = models.EmailField()
|
email = models.EmailField()
|
||||||
POR = models.CharField(max_length=255) # place of residence
|
POR = models.CharField(default="ei_tiedossa", max_length=255) # place of residence
|
||||||
AYY = models.BooleanField(default=False)
|
AYY = models.BooleanField(default=False)
|
||||||
jas = models.BooleanField(default=False)
|
jas = models.BooleanField(default=False)
|
||||||
created = models.DateTimeField(default=timezone.now)
|
|
||||||
paid = models.DateTimeField(default=datetime.fromtimestamp(0))
|
|
||||||
|
|
||||||
def get_dict(self):
|
class Meta:
|
||||||
return {
|
abstract = True
|
||||||
'id': self.id,
|
|
||||||
'first_name': self.first_name,
|
|
||||||
'last_name': self.last_name,
|
|
||||||
'email': self.email,
|
|
||||||
'POR': self.POR,
|
|
||||||
'AYY': self.AYY,
|
|
||||||
'jas': self.jas,
|
|
||||||
'created': date2str(self.created),
|
|
||||||
'paid': date2str(self.paid),
|
|
||||||
}
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def create_from_dict(cls, d):
|
|
||||||
mbr = cls()
|
|
||||||
mbr.update_from_dict(d)
|
|
||||||
return mbr
|
|
||||||
|
|
||||||
def update_from_dict(self, d):
|
|
||||||
dmap = {
|
|
||||||
'first_name': reverse_map('first_name'),
|
|
||||||
'last_name': reverse_map('last_name'),
|
|
||||||
'email': reverse_map('email'),
|
|
||||||
'POR': reverse_map('POR'),
|
|
||||||
'AYY': reverse_map('AYY'),
|
|
||||||
'jas': reverse_map('jas'),
|
|
||||||
'paid': reverse_map('paid', str2date),
|
|
||||||
}
|
|
||||||
for k, v in d.items():
|
|
||||||
try:
|
|
||||||
self.__setattr__(dmap[k].key, dmap[k].parser(v))
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
self.save()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def import_csv(cls, csv_string):
|
|
||||||
reader = csv.reader(StringIO(csv_string.strip()))
|
|
||||||
response = {"status": "success", "errors": []}
|
|
||||||
try:
|
|
||||||
data = list(reader)
|
|
||||||
except ValueError:
|
|
||||||
return {"status": "failure", "errors": ["could not parse csv file"]}
|
|
||||||
|
|
||||||
affirmative_answers = ['yes', 'y', '1', 'true', "kyllä", "khyl"]
|
|
||||||
for row in data:
|
|
||||||
|
|
||||||
try:
|
|
||||||
obj = cls.objects.create(
|
|
||||||
first_name=row[0].strip(),
|
|
||||||
last_name=row[1].strip(),
|
|
||||||
email=row[2].strip(),
|
|
||||||
POR=row[3].strip(),
|
|
||||||
AYY=row[4].lower().strip() in affirmative_answers,
|
|
||||||
jas=row[5].lower().strip() in affirmative_answers,
|
|
||||||
paid=timezone.now()
|
|
||||||
)
|
|
||||||
print("added obj {}".format(obj))
|
|
||||||
|
|
||||||
except:
|
|
||||||
response["status"] = "failure"
|
|
||||||
response["errors"].append("failure adding item {}".format(", ".join(row)))
|
|
||||||
|
|
||||||
return response
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return "{} {}".format(self.first_name, self.last_name)
|
|
||||||
|
|
||||||
|
|
||||||
class MemberRequest(models.Model):
|
class Request(BaseMember):
|
||||||
member = models.ForeignKey(Member)
|
'''
|
||||||
|
Member request model represents one member request.
|
||||||
|
'''
|
||||||
|
submitted = models.DateTimeField(default=timezone.now)
|
||||||
|
|
||||||
|
|
||||||
|
class Payment(models.Model):
|
||||||
|
'''
|
||||||
|
Payment model representing one payment event
|
||||||
|
'''
|
||||||
|
date = models.DateTimeField(default=datetime.fromtimestamp(0))
|
||||||
|
source = models.CharField(max_length=255)
|
||||||
|
first_name = models.CharField(max_length=255)
|
||||||
|
last_name = models.CharField(max_length=255)
|
||||||
|
email = models.EmailField(max_length=255)
|
||||||
|
member = models.ForeignKey('Member', on_delete=models.SET_NULL, blank=True, null=True)
|
||||||
|
|
||||||
def get_dict(self):
|
def get_dict(self):
|
||||||
return {
|
return {
|
||||||
'id': self.id,
|
'id': self.id,
|
||||||
'member': self.member.get_dict(),
|
'date': date2str(self.date),
|
||||||
|
'source': self.source,
|
||||||
|
'first_name': self.first_name,
|
||||||
|
'last_name': self.last_name,
|
||||||
|
'email': self.email,
|
||||||
|
'member': self.member.get_dict() if self.member else {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def date2str(date):
|
class Member(BaseMember):
|
||||||
'''
|
'''
|
||||||
Convert date to a standard date string
|
Member model represets one member on the registry.
|
||||||
'''
|
'''
|
||||||
if date is None:
|
created = models.DateTimeField(default=timezone.now)
|
||||||
return None
|
paid = models.DateTimeField(default=timezone.now) #this needs to be assigned as Payment.date
|
||||||
return date.strftime("%Y-%m-%d %H:%M:%S")
|
|
||||||
|
|
||||||
|
|
||||||
def str2date(s):
|
|
||||||
return datetime.strptime(s,"%Y-%m-%d %H:%M:%S")
|
|
||||||
|
|
||||||
|
|
||||||
class reverse_map:
|
|
||||||
def __init__(self, key, parser=lambda x: x):
|
|
||||||
self.key = key
|
|
||||||
self.parser = parser
|
|
||||||
|
|||||||
+28
-6
@@ -1,7 +1,7 @@
|
|||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from members.models import Member, MemberRequest
|
from members.models import Member, Request
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
||||||
@@ -37,10 +37,32 @@ class MemberSerializer(serializers.Serializer):
|
|||||||
instance.save()
|
instance.save()
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
class MemberRequestSerializer(serializers.ModelSerializer):
|
class MemberRequestSerializer(serializers.Serializer):
|
||||||
id = serializers.IntegerField(read_only=True)
|
id = serializers.IntegerField(read_only=True)
|
||||||
member = MemberSerializer()
|
submitted = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S")
|
||||||
|
first_name = serializers.CharField(required=True, max_length=127)
|
||||||
|
last_name = serializers.CharField(required=True, max_length=127)
|
||||||
|
email = serializers.EmailField(min_length=None, max_length=None, required=True)
|
||||||
|
POR = serializers.CharField(max_length=255)
|
||||||
|
AYY = serializers.BooleanField(default=False)
|
||||||
|
jas = serializers.BooleanField(default=False)
|
||||||
|
|
||||||
class Meta:
|
def created(self, validated_data):
|
||||||
model = MemberRequest
|
'''
|
||||||
fields = ('id', 'member')
|
Create and return a new MemberRequest instance, given the validated data.
|
||||||
|
'''
|
||||||
|
return MemberRequest.objects.create(**validated_data)
|
||||||
|
|
||||||
|
def update(self, instance, validated_data):
|
||||||
|
'''
|
||||||
|
Update and return an existing Member request instance given the validated data.
|
||||||
|
'''
|
||||||
|
instance.submitted = validated_data.get('submitted', instance.submitted)
|
||||||
|
instance.first_name = validated_data.get('first_name', instance.first_name)
|
||||||
|
instance.last_name = validated_data.get('last_name', instance.last_name)
|
||||||
|
instance.email = validated_data.get('email', instance.email)
|
||||||
|
instance.POR = validated_data.get('POR', instance.POR)
|
||||||
|
instance.AYY = validated_data.get('AYY', instance.AYY)
|
||||||
|
instance.jas = validated_data.get('jas', instance.jas)
|
||||||
|
instance.save()
|
||||||
|
return instance
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
{% extends "members_base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div id="wrapper">
|
|
||||||
|
|
||||||
<!-- Sidebar -->
|
|
||||||
<div id="sidebar-wrapper">
|
|
||||||
<ul class="sidebar-nav">
|
|
||||||
<li><a href="#/list">Listaa jäsenet</a></li>
|
|
||||||
<li><a href="#/add">Lisää jäsen</a></li>
|
|
||||||
<li><a href="#/addmany">Lisää jäseniä</a></li>
|
|
||||||
<li><a href="#/applications">Jäsenhakemukset</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<!-- /#sidebar-wrapper -->
|
|
||||||
|
|
||||||
<!-- Page Content -->
|
|
||||||
<div class="col-xs-12 col-sm-12 col-lg-12">
|
|
||||||
<div id="header" class="row">
|
|
||||||
<div class="col-xs-11 col-sm-11 col-lg-11">
|
|
||||||
<h1 style="text-align:center; margin-top:10px; margin-bottom: 10px;">Aalto-yliopiston Sähköinsinöörikilta RY:n jäsenrekisteri</h1>
|
|
||||||
</div>
|
|
||||||
<div class="col-xs-1 col-sm-1 col-lg-1">
|
|
||||||
<form action="/logout" method="post"> {% csrf_token %}
|
|
||||||
<input type="Submit" value="Logout" name="Logout" class="btn btn-danger" style="margin-top:12px;"/>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div ng-view></div>
|
|
||||||
</div>
|
|
||||||
{% endblock content %}
|
|
||||||
+3
-4
@@ -4,7 +4,7 @@ from django.views.decorators.http import require_http_methods
|
|||||||
from django.views.decorators.csrf import ensure_csrf_cookie
|
from django.views.decorators.csrf import ensure_csrf_cookie
|
||||||
from django.http import HttpResponse, HttpResponseBadRequest
|
from django.http import HttpResponse, HttpResponseBadRequest
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from members.models import Member, MemberRequest
|
from members.models import Member, Request
|
||||||
import json
|
import json
|
||||||
from django.core.mail import send_mail
|
from django.core.mail import send_mail
|
||||||
import requests
|
import requests
|
||||||
@@ -32,7 +32,6 @@ class MembersList(generics.ListCreateAPIView):
|
|||||||
permission_classes = (HasRights, permissions.IsAuthenticated, )
|
permission_classes = (HasRights, permissions.IsAuthenticated, )
|
||||||
throttle_classes = (BurstRateThrottle, SustainedRateThrottle, )
|
throttle_classes = (BurstRateThrottle, SustainedRateThrottle, )
|
||||||
|
|
||||||
|
|
||||||
class MemberDetails(generics.RetrieveUpdateDestroyAPIView):
|
class MemberDetails(generics.RetrieveUpdateDestroyAPIView):
|
||||||
queryset = Member.objects.all()
|
queryset = Member.objects.all()
|
||||||
serializer_class = MemberSerializer
|
serializer_class = MemberSerializer
|
||||||
@@ -40,13 +39,13 @@ class MemberDetails(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
throttle_classes = (BurstRateThrottle, SustainedRateThrottle, )
|
throttle_classes = (BurstRateThrottle, SustainedRateThrottle, )
|
||||||
|
|
||||||
class MemberRequestList(generics.ListCreateAPIView):
|
class MemberRequestList(generics.ListCreateAPIView):
|
||||||
queryset = MemberRequest.objects.all()
|
queryset = Request.objects.all()
|
||||||
serializer_class = MemberRequestSerializer
|
serializer_class = MemberRequestSerializer
|
||||||
permission_classes = (HasRights, permissions.IsAuthenticated, )
|
permission_classes = (HasRights, permissions.IsAuthenticated, )
|
||||||
throttle_classes = (BurstRateThrottle, SustainedRateThrottle, )
|
throttle_classes = (BurstRateThrottle, SustainedRateThrottle, )
|
||||||
|
|
||||||
class MemberRequestDetail(generics.RetrieveUpdateDestroyAPIView):
|
class MemberRequestDetail(generics.RetrieveUpdateDestroyAPIView):
|
||||||
queryset = MemberRequest.objects.all()
|
queryset = Request.objects.all()
|
||||||
serializer_class = MemberRequestSerializer
|
serializer_class = MemberRequestSerializer
|
||||||
permission_classes = (HasRights, permissions.IsAuthenticated, )
|
permission_classes = (HasRights, permissions.IsAuthenticated, )
|
||||||
throttle_classes = (BurstRateThrottle, SustainedRateThrottle, )
|
throttle_classes = (BurstRateThrottle, SustainedRateThrottle, )
|
||||||
|
|||||||
@@ -9,8 +9,7 @@ from django.conf import settings
|
|||||||
|
|
||||||
django.setup()
|
django.setup()
|
||||||
#django related stuff should be imported below this
|
#django related stuff should be imported below this
|
||||||
from members.models import Member, MemberRequest
|
from members.models import Member, Request
|
||||||
from infoscreen.models import Rotation, ExternalImageInfoItem, InfoInstance
|
|
||||||
from misc.namegenerator import generate_names
|
from misc.namegenerator import generate_names
|
||||||
MEMBERAMOUNT = 30
|
MEMBERAMOUNT = 30
|
||||||
MEMBERREQUESTAMOUNT = 3
|
MEMBERREQUESTAMOUNT = 3
|
||||||
@@ -47,3 +46,21 @@ inst = InfoInstance.objects.create(
|
|||||||
item=i_item,
|
item=i_item,
|
||||||
duration=20.0
|
duration=20.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# for m in list(Member.objects.all())[:5]:
|
||||||
|
# MemberRequest.objects.create(member=m)
|
||||||
|
names = generate_names(MEMBERREQUESTAMOUNT)
|
||||||
|
maildomains = ["example.coms",'ggmail.om',"notmail.dom"] #intentionally wrong
|
||||||
|
places = ["Helsinki", "Espoo", "Korso","Kerava", "Kouvostoliitto"]
|
||||||
|
for i in range(MEMBERREQUESTAMOUNT):
|
||||||
|
f,l = names[i]
|
||||||
|
mail = "{}.{}@{}".format(f.lower(),l.lower(),random.choice(maildomains))
|
||||||
|
por = random.choice(places)
|
||||||
|
ayy = random.randint(0,1)
|
||||||
|
jas = random.randint(0,1)
|
||||||
|
Request.objects.create(first_name=f,
|
||||||
|
last_name=l,
|
||||||
|
email=mail,
|
||||||
|
POR=por,
|
||||||
|
AYY=ayy,
|
||||||
|
jas=jas)
|
||||||
|
|||||||
Reference in New Issue
Block a user