More members overhaul

Relates to #44
This commit is contained in:
Jan Tuomi
2017-05-12 21:19:01 +03:00
parent 3a468b9c39
commit 74746fc0de
23 changed files with 258 additions and 484 deletions
+8 -1
View File
@@ -1,6 +1,6 @@
from django import forms
from members.models import Member
from members.models import Member, Payment
class MemberForm(forms.ModelForm):
@@ -8,3 +8,10 @@ class MemberForm(forms.ModelForm):
class Meta:
model = Member
fields = ['first_name', 'last_name', 'email', 'AYY', 'jas', 'POR']
class PaymentForm(forms.ModelForm):
class Meta:
model = Payment
fields = ['date', 'source', 'member']
+9 -3
View File
@@ -30,8 +30,12 @@ class Payment(models.Model):
'''
Payment model representing one payment event
'''
date = models.DateTimeField(default=datetime.fromtimestamp(0))
source = models.CharField(max_length=255)
date = models.DateTimeField(default=datetime.now())
source = models.CharField(choices=[
('AYY', _('AYY')),
('cash', _('Cash')),
('bank_transfer', _('Bank transfer')),
], max_length=255)
member = models.ForeignKey('Member', on_delete=models.SET_NULL, blank=True, null=True)
@@ -41,4 +45,6 @@ class Member(BaseMember):
Member model represets one member on the registry.
'''
created = models.DateTimeField(default=timezone.now)
paid = models.DateTimeField(default=timezone.now) #this needs to be assigned as Payment.date
def __str__(self):
return "{} {}, {}".format(self.last_name, self.first_name, self.email)
@@ -1,3 +1,7 @@
html, body {
font-size: 14px;
}
div {
padding: 0.5rem;
}
@@ -6,6 +10,35 @@ input {
padding: 0.5rem;
}
/* fixes for the sidebar layout */
#sidebar-wrapper {
background: #202020;
top: 0;
padding: 0;
}
.sidebar-nav {
width: initial;
left: 0;
width: 100%;
padding-top: 0.5rem;
}
.sidebar-nav li ul {
padding-left: 0px;
}
.sidebar-nav li span {
user-select: none;
cursor: default;
}
#settings-button {
position: absolute;
bottom: 0;
}
#download-csv {
margin-left: 20px;
}
@@ -81,4 +114,4 @@ input {
.inline-title {
display: inline;
}
}
View File
View File
@@ -1,63 +0,0 @@
//app
app = angular.module('applicationApp', ['vcRecaptcha']);
//tokens
app.config(['$httpProvider', function ($httpProvider) {
$httpProvider.defaults.xsrfCookieName = 'csrftoken';
$httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
}]);
//helpers
function notyfication(type,timeout){
return function(msg){
noty({
'text': msg,
'layout': "bottomRight",
'type': type,
'timeout': timeout
});
};
}
var notyError = notyfication('error',2500);
var notySuccess = notyfication('success',2500);
//controllers
app.controller("applicationController", function($scope, $http, $location, $window, vcRecaptchaService) {
$scope.member = {};
$scope.response = null;
$scope.widgetId = null;
$scope.model = {
key: "6LevHAcUAAAAAA45B7c-7qja-2aSwHztr9xb4K2Z"
};
$scope.setResponse = function(response) {
$scope.response = response;
};
$scope.setWidgetId = function(widgetId) {
$scope.widgetId = widgetId;
};
$scope.cbExpiration = function() {
vcRecaptchaService.reload($scope.widgetId);
$scope.response = null;
};
$scope.send = function() {
var valid;
//server side validation
$scope.member.reCaptchaResponse = vcRecaptchaService.getResponse()
console.log($scope.member.reCaptchaResponse);
if($scope.member.reCaptchaResponse === "") {
notyError("Ole hyvä ja täytä kuvavarmennus");
} else {
$http.post("/members/api/request", $scope.member).then(function(data){
notySuccess("Hakemus lähetetty!");
$window.location.href = "/application/success";
}, function(data){
notyError("Jokin meni vikaan. Yritä uudelleen.");
vcRecaptchaService.reload($scope.widgetId);
});
}
}
});
-279
View File
@@ -1,279 +0,0 @@
/* Controllers for member register views */
/* Generator function to create a "noty" notification function */
function notyfication(type, timeout) {
return function(msg) {
noty({
'text': msg,
'layout': "bottomRight",
'type': type,
'timeout': timeout
});
};
}
/* Create functions to show error and success notifications in the bottom
* right corner of the viewport
*
* These functions take a single message string as a parameter */
var notyError = notyfication('error', 2500);
var notySuccess = notyfication('success', 2500);
function memberDataEditor(returnPath) {
return function($scope, $http, $window, $location) {
var id = memberId;
console.log("id: " + id);
$http.get("/members/api/member/" + id).then(function(response) {
$scope.member = response.data;
});
$scope.send = function() {
$http.put("/members/api/member/" + id, $scope.member).then(function(response){
notySuccess("Jäsentiedot tallennettu");
$window.location = returnPath;
});
}
$scope.cancel = function() { //user canceled. return to list
$window.location = returnPath;
}
}
}
app.directive('ngConfirmClick', [ function() { return {
link: function (scope, element, attr) {
var clickAction = attr.confirmedClick;
element.bind('click', function (event) {
noty( {
text: 'Oletko aivan varma? T. Lasse Lehtinen',
layout: 'bottomRight',
buttons: [ {
addClass: 'btn btn-danger', text: 'Kyllä', onClick: function($noty) {
$noty.close();
scope.$eval(clickAction);
}
},
{
addClass: 'btn btn-primary', text: 'Ei', onClick: function($noty) {
$noty.close();
}
} ]
} );
});
}
}}]);
// controllers
app.controller("getController", function($scope, $document, $http){
/* List of all members that are fetched from the database */
$scope.members = [];
/* Fetch all members from the database and show all members in the table */
$scope.updateMembers = function() {
$http.get("/members/api/members").then(function(response){
$scope.members = response.data;
// map trues and falses to more user-friendly format
_.each($scope.members, function(m){
m.jas = m.jas ? "Kyllä" : "Ei";
m.AYY = m.AYY ? "Kyllä" : "Ei";
});
$scope.shown_members = $scope.members;
});
};
/* Fetch a single member from the database by id and update its row */
$scope.updateMember = function(id) {
$http.get("/members/api/member/" + id).then(function(response) {
for (var i = 0; i < $scope.shown_members.length; i++) {
var member = $scope.shown_members[i];
if (String(member.id) == String(id)) {
member = response.data;
member.jas = member.jas ? "Kyllä" : "Ei";
member.AYY = member.AYY ? "Kyllä" : "Ei";
$scope.shown_members[i] = member;
}
}
});
};
/* Update the payment date of a single member to the current time and send
* the member to the database */
$scope.updatePayment= function(id){
$http.put("/members/api/member/"+id, { paid: moment().format("YYYY-MM-DD kk:mm:ss") }).then(function(response) {
$scope.updateMember(id);
notySuccess("Maksupäivämäärä päivitetty.");
});
};
/* Redirect the browser to the CSV dump download endpoint */
$scope.loadCSV = function() {
window.location = "/members/api/getCSV";
};
/* Delete a single member by id */
$scope.deleteMember = function(id) {
$http.delete("/members/api/member/" + id).then(
function(response) {
notySuccess("Poistaminen onnistui")
$scope.updateMembers();
},
function(response) {
notyError("Epäonnistui. Yritä uudelleen.");
$scope.updateMembers();
}
);
};
$scope.filterByDateComparison = function(members, datePicker, comparison) {
if (datePicker == null) {
return members;
}
var result = [];
for (var i = 0; i < members.length; i++) {
if (comparison(members[i], datePicker)) {
result.push(members[i]);
}
}
return result;
};
/* Do a lazy search on the first name, last name and email fields
* If at least one of the aforementioned fields contains any of the search terms
* the search will be positive */
$scope.filterBySearch = function(members) {
if ($scope.searchFilter == null) {
return members;
}
var filterSearch = $scope.searchFilter.trim();
if (filterSearch.length == 0) {
return members;
}
var names = filterSearch.split(" ");
var result = [];
for (var i = 0; i < members.length; i++) {
var member = members[i];
for (var j = 0; j < names.length; j++) {
var name = names[j].trim().toLowerCase();
if (name.length == 0) continue;
if (member.first_name.toLowerCase().includes(name)
|| member.last_name.toLowerCase().includes(name)
|| member.email.toLowerCase().includes(name)) {
result.push(member);
break;
}
}
}
return result;
}
/* Run all filters on the members list */
$scope.doFilter = function() {
var result = $scope.members;
result = $scope.filterByDateComparison(result, $scope.addedBeforeDatePicker, function(member, date) {
return moment(member.created) < date });
result = $scope.filterByDateComparison(result, $scope.addedAfterDatePicker, function(member, date) {
return moment(member.created) >= date });
result = $scope.filterByDateComparison(result, $scope.paidBeforeDatePicker, function(member, date) {
return moment(member.paid) < date });
result = $scope.filterByDateComparison(result, $scope.paidAfterDatePicker, function(member, date) {
return moment(member.paid) >= date });
result = $scope.filterBySearch(result);
$scope.shown_members = result;
}
/* Clear all filter fields and reset the table view */
$scope.clearFilter = function() {
$scope.paidBeforeDatePicker = null;
$scope.paidAfterDatePicker = null;
$scope.addedBeforeDatePicker = null;
$scope.addedAfterDatePicker = null;
$scope.searchFilter = null;
$scope.updateMembers();
};
/* Run filters on enter keypress in search bar */
$scope.pressKeyOnSearch = function(keyEvent) {
/* 13 is the id of the enter key */
if (keyEvent.which === 13) {
$scope.doFilter();
}
};
/* Start by resetting the whole thing */
$scope.clearFilter();
});
/* Controller for adding a member */
app.controller("postController", function($scope, $http, $location, $window) {
$scope.member = {};
$scope.send = function() {
$http.post("/members/api/member/", $scope.member).then(function(response){
notySuccess("Jäsen lisätty!");
$window.location = "/members/list";
});
}
});
/* Controller for application page */
app.controller("applController", function($scope, $http){
$scope.applUpdateAll = function() {
$http.get("/members/api/requests").then(function(response){
$scope.applications = response.data;
_.each($scope.applications, function(a){
a.member.jas = a.member.jas ? "Kyllä" : "Ei";
a.member.AYY = a.member.AYY ? "Kyllä" : "Ei";
});
});
};
$scope.applUpdateAll();
$scope.sendAppl = function(id) {
$http.post("/members/api/request/" + id).then(
function(response) {
notySuccess("Hakemus hyväksytty");
$scope.applUpdateAll();
},
function(response) {
notyError("Hakemuksen hyväksyminen epäonnistui");
$scope.applUpdateAll();
}
);
};
$scope.deleteAppl = function(id) {
$http.delete("/members/api/request/" + id).then(
function(response) {
notySuccess("Hakemus hylätty!");
$scope.applUpdateAll();
},
function(response) {
notyError("Hakemuksen hylkäys epäonnistui");
$scope.applUpdateAll();
}
);
};
});
app.controller("editController", memberDataEditor("/members/list"));
app.controller("applEditController", memberDataEditor("/members/applications"));
app.controller("addManyController", function($scope, $http, $window) {
$scope.memberData = '';
$scope.sendCSV = function() {
$http.post("/members/api/csvimport", {'csv': $scope.memberData}).then(
function(response) {
notySuccess("Lähetys onnistui");
$window.location.reload();
},
function(response) {
notyError("Lähetys epäonnistui");
}
);
};
});
@@ -14,24 +14,13 @@
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.css" rel = "stylesheet">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.6/angular.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular-route.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<!-- DatePicker -->
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/angular_material/1.0.0/angular-material.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/angular_material/1.0.0/angular-material.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular-messages.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular-animate.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular-aria.min.js"></script>
<!-- reCaptcha -->
<script src="/static/js/angular-recaptcha.js"></script>
<script src="https://www.google.com/recaptcha/api.js?onload=vcRecaptchaApiLoaded&render=explicit" async defer></script>
<script src="/static/js/jquery.noty.packaged.js"></script>
<script src="/static/js/appconfig.js"></script>
<script src="/static/js/application_controllers.js"></script>
<script src="/static/js/moment.js"></script>
<link href="/static/css/simple-sidebar.css" rel="stylesheet">
+1 -1
View File
@@ -1,4 +1,4 @@
{% extends "application_base.html" %}
{% extends "application_form_base.html" %}
{% load static %}
{% block content %}
+44
View File
@@ -0,0 +1,44 @@
{% extends "members_base.html" %}
{% load i18n %}
{% block content %}
<div>
<h3>{% trans "Member applications" %}</h3>
<div>
<table class="table table-bordered table-hover">
<thead>
<tr class="ui-widget-header">
<th>{% trans "Last name" %}</th>
<th>{% trans "First name" %}</th>
<th>{% trans "Email" %}</th>
<th>{% trans "AYY member" %}</th>
<th>{% trans "JAS recipient" %}</th>
<th>{% trans "Residence" %}</th>
<th>{% trans "Submitted" %}</th>
<th class="table-button-column">{% trans "Application count" %}: {{ application_count }}</th>
</tr>
</thead>
<tbody>
{% for application in application_list %}
<tr>
<td>{{ application.last_name }}</td>
<td>{{ application.first_name }}</td>
<td>{{ application.email }}</td>
<td>{{ application.AYY }}</td>
<td>{{ application.jas }}</td>
<td>{{ application.POR }}</td>
<td>{{ application.submitted }}</td>
<td class="table-button-column">
<a href="/members/accept_application/{{ application.id }}" class="btn btn-success">{% trans "Accept" %}</a>
<a href="/members/decline_application/{{ application.id }}" class="table-button btn btn-danger">{% trans "Decline" %}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock content %}
+1 -1
View File
@@ -1,4 +1,4 @@
{% extends "application_base.html" %}
{% extends "application_form_base.html" %}
{% block content %}
<h1>Hienoa! Jäsenhakemuksesi on nyt lähetetty.</h1>
-46
View File
@@ -1,46 +0,0 @@
{% extends "members_base.html" %}
{% load i18n %}
{% block content %}
<div>
<h3>{% trans "Member applications" %}</h3>
<div>
{% for application in applications %}
<div class="panel panel-primary">
<div class="panel-heading">{{ application.first_name }} {{ application.last_name }}</div>
<div class="panel-body">
<div class="row">
<div class="col-sm-4">Sähköposti: {{ application.email }}</div>
</div>
<div class="row">
<div class="col-sm-4">AYY-jäsen: {{ application.AYY }}</div>
</div>
<div class="row">
<div class="col-sm-4">JAS-listaan: {{ application.jas }}</div>
</div>
<div class="row">
<div class="col-sm-4">Asuinpaikka: {{ application.POR }}</div>
</div>
<div class="row">
<div class="col-sm-4">Lisätty: {{ application.created }}</div>
</div>
<div class="row">
<div class="col-sm-4">
<input type="button" value="Hyväksy" class="btn btn-success">
<input type="button" value="Hylkää" class="btn btn-danger"/>
<a href="/members/edit_application/{{ application.id }}"><input type="button" value="Edit" class="btn btn-info"></a>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
{% if application_count == 0 %}
<div>
<h4>Ei jäsenhakemuksia :(</h4>
</div>
{% endif %}
</div>
{% endblock content %}
+18 -14
View File
@@ -3,7 +3,6 @@
{% load static %}
{% load i18n %}
{% block content %}
<link rel="stylesheet" href="{% static "css/jasenlista.css" %}">
<div>
<a id="filter-collapser" href="#collapse-filters" data-toggle="collapse" class="btn btn-info">
{% trans "Show filters" %}
@@ -50,7 +49,7 @@
<input type="button" value="{% trans "Download CSV" %}" id="download-csv" class="btn btn-info" ng-click="loadCSV()"/>
</div>
</div>
<table id="choose-address-table" class="table table-striped">
<table class="table table-bordered table-hover">
<thead>
<tr class="ui-widget-header">
<th>{% trans "Last name" %}</th>
@@ -65,22 +64,27 @@
</tr>
</thead>
<tbody>
{% for member in member_list %}
{% for member_data in member_list %}
<tr>
<td>{{ member.last_name }}</td>
<td>{{ member.first_name }}</td>
<td>{{ member.email }}</td>
<td>{{ member.AYY }}</td>
<td>{{ member.jas }}</td>
<td>{{ member.POR }}</td>
<td>{{ member.created }}</td>
<td>{{ member.paid }}</td>
<td>{{ member_data.member.last_name }}</td>
<td>{{ member_data.member.first_name }}</td>
<td>{{ member_data.member.email }}</td>
<td>{{ member_data.member.AYY }}</td>
<td>{{ member_data.member.jas }}</td>
<td>{{ member_data.member.POR }}</td>
<td>{{ member_data.member.created }}</td>
<td>
{% if member_data.payment %}
{{ member_data.payment.date }}
{% else %}
{% trans "No payment" %}
{% endif %}
</td>
<td class="table-button-column">
<input type="button" value="{% trans "Update payment" %}" class="table-button btn btn-success" />
<a href="/members/edit/{{ member.id }}">
<a href="/members/edit/{{ member_data.member.id }}">
<input type="button" value="{% trans "Edit" %}" class="table-button btn btn-info" value="{% trans "Edit" %}" />
</a>
<a href="/members/delete_member_confirm/{{ member.id }}">
<a href="/members/delete_member_confirm/{{ member_data.member.id }}">
<input type="button" value="{% trans "Delete" %}" class="table-button btn btn-danger" />
</a>
</td>
+26 -8
View File
@@ -4,7 +4,7 @@
{% load static %}
{% load i18n %}
<html lang="en" ng-app="memberApp">
<html lang="en">
<head>
<base href="/">
<meta charset="utf-8">
@@ -24,12 +24,10 @@
<script src="/static/js/jquery.noty.packaged.js"></script>
<!--
<script src="/static/js/members_controllers.js"></script>
-->
<script src="/static/js/moment.js"></script>
<link href="/static/css/simple-sidebar.css" rel="stylesheet">
<link rel="stylesheet" href="{% static "css/base.css" %}">
<link rel="stylesheet" href="{% static "css/members.css" %}">
</head>
<body>
@@ -38,11 +36,30 @@
<!-- Sidebar -->
<div id="sidebar-wrapper">
<ul class="sidebar-nav">
<li><a href="/members/list">{% trans "Member list" %}</a></li>
<li><a href="/members/add">{% trans "Add member" %}</a></li>
<li><a href="/members/add_many">{% trans "Add multiple" %}</a></li>
<li><a href="/members/applications">{% trans "Member application" %}</a></li>
<li>
<span class="text-primary">{% trans "Members" %}</span>
<ul>
<li><a href="/members/list">{% trans "List members" %}</a></li>
<li><a href="/members/add">{% trans "Add member" %}</a></li>
<li><a href="/members/add_many">{% trans "Add multiple" %}</a></li>
</ul>
</li>
<li>
<span class="text-primary">{% trans "Payments" %}</span>
<ul>
<a href="/members/payments">{% trans "List payments" %}</a></li>
<a href="/members/payment_add">{% trans "Add payment" %}</a></li>
</ul>
</li>
<li>
<span class="text-primary">{% trans "Applications" %}</span>
<ul>
<a href="/members/applications">{% trans "List applications" %}</a></li>
<a href="/members/application">{% trans "Application form" %}</a></li>
</ul>
</li>
</ul>
</div>
<!-- /#sidebar-wrapper -->
@@ -65,5 +82,6 @@
{% endblock content %}
</div>
{% include "footer.html" %}
</div>
</body>
</html>
+21
View File
@@ -0,0 +1,21 @@
{% extends "members_base.html" %}
{% load bootstrap3 %}
{% load i18n %}
{% block content %}
<div>
<h3>{% trans "Add payment" %}</h3>
<div id="input_form">
<form name="paymentForm" action="/members/submit_payment" method="post" class="form">{% csrf_token %}
{% bootstrap_form form %}
{% buttons %}
<button type="submit" class="btn btn-primary">
{% trans "Save" %}
</button>
{% endbuttons %}
</form>
</div>
</div>
{% endblock content %}
+1 -4
View File
@@ -3,7 +3,6 @@
{% load static %}
{% load i18n %}
{% block content %}
<link rel="stylesheet" href="{% static "css/jasenlista.css" %}">
<div>
<a id="filter-collapser" href="#collapse-filters" data-toggle="collapse" class="btn btn-info">
{% trans "Show filters" %}
@@ -50,7 +49,7 @@
<input type="button" value="{% trans "Download CSV" %}" id="download-csv" class="btn btn-info" ng-click="loadCSV()"/>
</div>
</div>
<table id="choose-address-table" class="table table-striped">
<table class="table table-bordered table-hover">
<thead>
<tr class="ui-widget-header">
<th>{% trans "Date" %}</th>
@@ -71,8 +70,6 @@
{% trans "No member data found" %}
{% endif %}
</td>
<td>{{ payment.last_name }}</td>
<td>{{ payment.email }}</td>
<td class="table-button-column">
<a href="/members/edit/{{ payment.id }}">
<input type="button" value="{% trans "Edit" %}" class="table-button btn btn-info" value="{% trans "Edit" %}" />
+29 -25
View File
@@ -2,21 +2,21 @@ from django.conf.urls import url
from django.views.generic.base import RedirectView
# members
from members.views import member_list
from members.views import add_member
from members.views import add_many_members
from members.views import edit_member
from members.views import submit_member
from members.views import update_member
from members.views import delete_member_confirm
from members.views import delete_member
from members.views import list_payments
from members.views import member_list, payment_add, payment_submit
from members.views import member_add
from members.views import member_add_many
from members.views import member_edit
from members.views import member_submit
from members.views import member_update
from members.views import member_delete_confirm
from members.views import member_delete
from members.views import payment_list
#application
from members.views import application_index
from members.views import list_applications
from members.views import edit_application
from members.views import application_success_index
from members.views import application_form
from members.views import application_list
from members.views import application_edit
from members.views import application_form_success
favicon_view = RedirectView.as_view(url='static/img/favicon.ico', permanent=True)
@@ -27,36 +27,40 @@ urlpatterns = [
url(r'^list$', member_list),
# add member form view
url(r'^add$', add_member),
url(r'^add$', member_add),
# add many members view
url(r'^add_many$', add_many_members),
url(r'^add_many$', member_add_many),
# edit member information view
url(r'^edit/(?P<index>\d+)$', edit_member),
url(r'^edit/(?P<index>\d+)$', member_edit),
# delete confirmation view
url(r'^delete_member_confirm/(?P<index>\d+)$', delete_member_confirm),
url(r'^delete_member_confirm/(?P<index>\d+)$', member_delete_confirm),
# list all member applications
url(r'^applications$', list_applications),
url(r'^applications$', application_list),
# edit member application
url(r'^edit_application/(?P<index>\d+)$', edit_application),
url(r'^edit_application/(?P<index>\d+)$', application_edit),
# post request targets
url(r'^submit_member$', submit_member),
url(r'^update_member$', update_member),
url(r'^delete_member$', delete_member),
url(r'^submit_member$', member_submit),
url(r'^update_member$', member_update),
url(r'^delete_member$', member_delete),
url(r'^submit_payment$', payment_submit),
# the actual member application form
url(r'^application/$', application_index),
url(r'^application/$', application_form),
# success page for the application
url(r'^application/success$', application_success_index),
url(r'^application/success$', application_form_success),
# list all payment events
url(r'^payments', list_payments),
url(r'^payments', payment_list),
# add payment event
url(r'^payment_add', payment_add),
# favourite icon
url(r'^favicon\.ico$', favicon_view),
+66 -27
View File
@@ -5,23 +5,25 @@ from django.views.decorators.csrf import ensure_csrf_cookie
from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseRedirect
from django.core.mail import send_mail
from django.conf import settings
from django.db.models import Max
import json
import requests
import logging
from members.models import Member, Request, Payment
from members.forms import MemberForm
from members.forms import MemberForm, PaymentForm
# Logger function, you can use the same idea when implementing other loggers to other apps
memberlogger = logging.getLogger(__name__)
logging.basicConfig(format='[%(levelname)s]%(asctime)s %(message)s', level=settings.LOGGERLEVEL, filename=settings.LOGPATH)
'''
Recaptcha is used in member applications
'''
def validate_recaptcha(response):
'''
Recaptcha is used in member applications
'''
values = {
'secret': settings.GOOGLE_RECAPTCHA_SECRET_KEY,
'response': response,
@@ -51,45 +53,61 @@ def send_mail_wrapper(subject, message):
@permission_required('members.change_member', login_url='/login')
def member_list(request, *args, **kwargs):
members = Member.objects.all()
member_list = []
for member in members:
obj = {'member': member, 'payment': None}
member_payments = Payment.objects.filter(member=member.id)
if len(member_payments) > 0:
last_payment = member_payments.aggregate(Max('date'))
print(last_payment)
obj['payment'] = last_payment['date__max']
member_list.append(obj)
context = {
'member_list': members,
'member_count': len(members)
'member_list': member_list,
'member_count': len(member_list)
}
return render(request, 'member_list.html', context)
@ensure_csrf_cookie
@require_http_methods(["GET"])
@permission_required('members.change_member', login_url='/login')
def add_member(request, *args, **kwargs):
def member_add(request, *args, **kwargs):
form = MemberForm()
return render(request, 'add_member.html', {'form': form})
return render(request, 'member_add.html', {'form': form})
@ensure_csrf_cookie
@require_http_methods(["GET"])
@permission_required('members.change_member', login_url='/login')
def delete_member_confirm(request, *args, **kwargs):
def member_delete_confirm(request, *args, **kwargs):
i = kwargs.pop('index', None)
if i is None:
return HttpResponse(status=500, error="{'error': 'No member id specified'}")
else:
member = Member.objects.get(id=i)
form = MemberForm(instance=member)
return render(request, 'delete_member_confirm.html', {'member_id': i, 'form': form})
return render(request, 'member_delete_confirm.html', {'member_id': i, 'form': form})
@ensure_csrf_cookie
@require_http_methods(["GET"])
@permission_required('members.change_member', login_url='/login')
def add_many_members(request, *args, **kwargs):
return render(request, 'add_many_members.html', {})
def member_add_many(request, *args, **kwargs):
return render(request, 'member_add_many.html', {})
@ensure_csrf_cookie
@require_http_methods(["POST"])
@permission_required('members.change_member', login_url='/login')
def submit_member(request, *args, **kwargs):
def member_submit(request, *args, **kwargs):
form = MemberForm(request.POST)
if form.is_valid():
form.save()
logging.info("Saved new member to member register with the following info: {}".format(form))
memberlogger.info("Saved new member to member register with the following info: {}".format(form))
return HttpResponseRedirect('/members')
else:
print(form.errors)
@@ -99,7 +117,7 @@ def submit_member(request, *args, **kwargs):
@ensure_csrf_cookie
@require_http_methods(["POST"])
@permission_required('members.change_member', login_url='/login')
def update_member(request, *args, **kwargs):
def member_update(request, *args, **kwargs):
form = MemberForm(request.POST)
if form.is_valid():
id = request.POST['id']
@@ -107,7 +125,7 @@ def update_member(request, *args, **kwargs):
form = MemberForm(request.POST, instance=member)
form.save()
logging.info("Updated member in member register with the following info: {}".format(form))
memberlogger.info("Updated member in member register with the following info: {}".format(form))
return HttpResponseRedirect('/members')
else:
print(form.errors)
@@ -117,7 +135,7 @@ def update_member(request, *args, **kwargs):
@ensure_csrf_cookie
@require_http_methods(["POST"])
@permission_required('members.change_member', login_url='/login')
def delete_member(request, *args, **kwargs):
def member_delete(request, *args, **kwargs):
try:
id = request.POST['id']
except KeyError:
@@ -136,54 +154,54 @@ def delete_member(request, *args, **kwargs):
@ensure_csrf_cookie
@require_http_methods(["GET"])
@permission_required('members.change_member', login_url='/login')
def list_applications(request, *args, **kwargs):
def application_list(request, *args, **kwargs):
applications = Request.objects.all()
application_count = len(applications)
context = {
'applications': applications,
'application_list': applications,
'application_count': application_count
}
return render(request, 'list_applications.html', context)
return render(request, 'application_list.html', context)
@ensure_csrf_cookie
@require_http_methods(["GET"])
@permission_required('members.change_member', login_url='/login')
def edit_member(request, *args, **kwargs):
def member_edit(request, *args, **kwargs):
i = kwargs.pop('index', None)
if i is None:
return HttpResponse(status=500, error="{'error': 'No member id specified'}")
else:
member = Member.objects.get(id=i)
form = MemberForm(instance=member)
return render(request, 'edit_member.html', {'member_id': i, 'form': form})
return render(request, 'member_edit.html', {'member_id': i, 'form': form})
@ensure_csrf_cookie
@require_http_methods(["GET"])
@permission_required('members.change_member', login_url='/login')
def edit_application(request, *args, **kwargs):
def application_edit(request, *args, **kwargs):
i = kwargs.pop('index', None)
if i is None:
return HttpResponse(status=500, error="{'error': 'No member id specified'}")
else:
return render(request, 'edit_application.html', {'member_id' : i})
return render(request, 'application_edit.html', {'member_id' : i})
@ensure_csrf_cookie
def application_index(request, *args, **kwargs):
def application_form(request, *args, **kwargs):
return render(request, 'application_index.html', {})
@ensure_csrf_cookie
def application_success_index(request, *args, **kwargs):
def application_form_success(request, *args, **kwargs):
return render(request, 'application_success.html', {})
@ensure_csrf_cookie
@require_http_methods(["GET"])
@permission_required('members.change_member', login_url='/login')
def list_payments(request, *args, **kwargs):
def payment_list(request, *args, **kwargs):
payments = Payment.objects.all()
context = {
'payment_list': payments,
@@ -192,6 +210,27 @@ def list_payments(request, *args, **kwargs):
return render(request, 'payment_list.html', context)
@ensure_csrf_cookie
@require_http_methods(["GET"])
@permission_required('members.change_member', login_url='/login')
def payment_add(request, *args, **kwargs):
form = PaymentForm()
return render(request, 'payment_add.html', {'form': form})
@ensure_csrf_cookie
@require_http_methods(["POST"])
@permission_required('members.change_member', login_url='/login')
def payment_submit(request, *args, **kwargs):
form = PaymentForm(request.POST)
if form.is_valid():
form.save()
memberlogger.info("Saved new payment to member register with the following info: {}".format(form))
return HttpResponseRedirect('/members/payments')
else:
print(form.errors)
return HttpResponse('oh shit')
@ensure_csrf_cookie
@require_http_methods(["POST"])
@permission_required('members.change_member', login_url='/login')