Merge branch 'develop' into 'master'

Search bar implementation

See merge request !50
This commit is contained in:
Jan Tuomi
2017-09-25 22:49:13 +03:00
25 changed files with 1237 additions and 923 deletions
+29 -2
View File
@@ -1,3 +1,30 @@
from django.test import TestCase
from django.test import TestCase, Client
from django.conf import settings
# Create your tests here.
from coffee_scale.mqtt import on_message
HOST = settings.MQTT_SETTINGS['HOST']
PORT = settings.MQTT_SETTINGS['PORT']
TOPICS = settings.MQTT_SETTINGS['TOPICS']
class MQTTTestCase(TestCase):
"""Tests MQTT functionality"""
class MockMessage:
def __init__(self, payload, topic):
self.payload = payload
self.topic = topic
def setUp(self):
payload = '10'.encode('utf-8')
topic = TOPICS['CUPS']
msg = MQTTTestCase.MockMessage(payload, topic)
on_message(None, None, msg)
self.c = Client()
def test_receive_cups(self):
response = self.c.get('/coffee/cups')
payload = response.json()
self.assertEquals(payload['cups'], 10)
+3 -2
View File
@@ -1,7 +1,8 @@
from django.shortcuts import render
from django.http import JsonResponse
import datetime
from django.utils import timezone
from .mqtt import get_latest
import coffee_scale.mqtt # somehow this is needed
@@ -15,7 +16,7 @@ def coffee_view(request):
def cups_view(request):
now = datetime.datetime.now()
now = timezone.now()
latest = get_latest()
data = {
'date': now,
+5 -4
View File
@@ -3,7 +3,8 @@
import urllib.request
import json
import logging
from datetime import datetime, timedelta
from datetime import timedelta, datetime
from django.utils import timezone
from django.conf import settings
@@ -20,7 +21,7 @@ class HSLFetcher:
def fetch_if_needed(self):
"""Check if new fetch from HSL API is needed."""
if (datetime.now() - HSLFetcher.last_fetched >
if (timezone.now() - HSLFetcher.last_fetched >
timedelta(minutes=HSLFetcher.INTERVAL)):
self.fetch()
@@ -38,7 +39,7 @@ class HSLFetcher:
arr = []
time = (datetime.now() +
time = (timezone.now() +
timedelta(minutes=settings.HSL_DEPARTURE_THRESHOLD))
time = "{0:02d}{0:02d}".format(time.hour, time.minute)
for element in data:
@@ -65,7 +66,7 @@ class HSLFetcher:
obj = model_arr[count - 1]
obj.data = json_dump
obj.save()
now = datetime.now()
now = timezone.now()
HSLFetcher.last_fetched = now
logging.info(
Binary file not shown.
+184 -139
View File
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-06-07 18:22+0300\n"
"POT-Creation-Date: 2017-09-25 21:32+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,35 +17,35 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: infoscreen/models.py:77
#: infoscreen/models.py:97
msgid "ABB jobs"
msgstr "ABB jobs"
#: infoscreen/models.py:88
#: infoscreen/models.py:112
msgid "APY Item"
msgstr "ÄPY Item"
#: infoscreen/models.py:99
#: infoscreen/models.py:127
msgid "External website"
msgstr "External website"
#: infoscreen/models.py:147
#: infoscreen/models.py:184
msgid "Sössö articles"
msgstr "Sössö articles"
#: infoscreen/models.py:158
#: infoscreen/models.py:199
msgid "Events"
msgstr "Events"
#: infoscreen/models.py:169
#: infoscreen/models.py:214
msgid "Image"
msgstr "Image"
#: infoscreen/models.py:204
#: infoscreen/models.py:260
msgid "HSL timetables"
msgstr "HSL timetables"
#: infoscreen/models.py:215
#: infoscreen/models.py:275
msgid "External image"
msgstr "External image"
@@ -169,7 +169,8 @@ msgstr "Select rotation to edit"
msgid "id"
msgstr "id"
#: infoscreen/templates/infoscreen_admin.html:141 webapp/models.py:46
#: infoscreen/templates/infoscreen_admin.html:141 webapp/models.py:60
#: webapp/models.py:94 webapp/models.py:107
msgid "Name"
msgstr "Name"
@@ -183,14 +184,12 @@ msgid "Language"
msgstr "Language"
#: infoscreen/templates/infoscreen_admin.html:161
#: members/templates/settings.html:20 sikweb/settings-sample.py:179
#: sikweb/settings.py:178
#: members/templates/settings.html:20 sikweb/base.py:216
msgid "Finnish"
msgstr "Finnish"
#: infoscreen/templates/infoscreen_admin.html:162
#: members/templates/settings.html:21 sikweb/settings-sample.py:178
#: sikweb/settings.py:177
#: members/templates/settings.html:21 sikweb/base.py:217
msgid "English"
msgstr "English"
@@ -201,31 +200,32 @@ msgstr "English"
msgid "Submit"
msgstr "Submitted"
#: members/forms.py:20 members/tables.py:24
#: members/forms.py:103 members/tables.py:32
msgid "Member"
msgstr "Member"
#: members/models.py:16
#: members/models.py:13
msgid "First name"
msgstr "First name"
#: members/models.py:17
#: members/models.py:14
msgid "Last name"
msgstr "Last name"
#: members/models.py:18
#: members/models.py:15 webapp/models.py:95 webapp/models.py:108
msgid "Email"
msgstr "Email"
#: members/models.py:19
#: members/models.py:16
msgid "Place of residence"
msgstr "Place of residence"
#: members/models.py:20 members/models.py:83
#: members/models.py:18 members/models.py:83
#: members/templates/member_add_many.html:35
msgid "AYY"
msgstr "AYY"
#: members/models.py:21
#: members/models.py:19
msgid "JAS"
msgstr "JAS"
@@ -245,7 +245,7 @@ msgstr "Source"
msgid "Cash"
msgstr "Cash"
#: members/models.py:85
#: members/models.py:85 members/templates/member_add_many.html:36
msgid "Bank transfer"
msgstr "Bank transfer"
@@ -253,15 +253,15 @@ msgstr "Bank transfer"
msgid "Created"
msgstr "Created"
#: members/tables.py:9
#: members/tables.py:13
msgid "Last paid"
msgstr "Last paid"
#: members/tables.py:13 members/tables.py:28 members/tables.py:41
#: members/tables.py:18 members/tables.py:37 members/tables.py:54
msgid "Edit"
msgstr "Edit"
#: members/tables.py:15 members/tables.py:30 members/tables.py:43
#: members/tables.py:20 members/tables.py:39 members/tables.py:56
msgid "Options"
msgstr "Options"
@@ -294,10 +294,16 @@ msgid "Add member"
msgstr "Add member"
#: members/templates/member_add.html:15 members/templates/member_edit.html:18
#: members/templates/payment_add.html:15 members/templates/payment_edit.html:18
#: members/templates/payment_add.html:20 members/templates/payment_edit.html:18
msgid "Save"
msgstr "Save"
#: members/templates/member_add_many.html:8
#, fuzzy
#| msgid "Add member"
msgid "Add many members"
msgstr "Add member"
#: members/templates/member_add_many.html:13
msgid ""
"\n"
@@ -326,10 +332,41 @@ msgstr ""
msgid "Syntax"
msgstr "Syntax"
#: members/templates/member_add_many.html:32
#: members/templates/member_add_many.html:29
msgid "Data"
msgstr ""
#: members/templates/member_add_many.html:33
#, fuzzy
#| msgid "Payments"
msgid "Payment source"
msgstr "Payments"
#: members/templates/member_add_many.html:37
#, fuzzy
#| msgid "List payments"
msgid "Cash payment"
msgstr "List payments"
#: members/templates/member_add_many.html:41
#: members/templates/member_add_many_confirm.html:22
msgid "Send"
msgstr "Send"
#: members/templates/member_add_many_confirm.html:8
msgid "Confirm adding these entries?"
msgstr ""
#: members/templates/member_add_many_confirm.html:12
#: members/templates/members_base.html:52 webapp/templates/main_index.html:7
msgid "Members"
msgstr "Members"
#: members/templates/member_add_many_confirm.html:16
#: members/templates/members_base.html:60
msgid "Payments"
msgstr "Payments"
#: members/templates/member_delete_confirm.html:9
msgid "Are you sure you want to delete this member?"
msgstr "Are you sure you want to delete this member?"
@@ -339,31 +376,6 @@ msgstr "Are you sure you want to delete this member?"
msgid "Yes, I'm sure"
msgstr "Yes, I'm sure"
#: members/templates/member_duplicates.html:9
msgid "Conflicting member entries"
msgstr ""
#: members/templates/member_duplicates.html:13
msgid ""
"Found conflicting member entries. Choose how to handle the problematic data."
msgstr ""
#: members/templates/member_duplicates.html:29
msgid "Which one has the correct information for this member?"
msgstr ""
#: members/templates/member_duplicates.html:31
msgid "Accept first and remove second"
msgstr ""
#: members/templates/member_duplicates.html:32
msgid "Accept second and remove first"
msgstr ""
#: members/templates/member_duplicates.html:33
msgid "Accept both as two members"
msgstr ""
#: members/templates/member_edit.html:9
msgid "Edit member"
msgstr "Edit member"
@@ -372,19 +384,21 @@ msgstr "Edit member"
msgid "Member register"
msgstr "Member register"
#: members/templates/member_list.html:16
msgid ""
"There are duplicate member entries in the register.\n"
" Please visit <a href=\"/members/duplicates\">duplicate resolver</a>."
msgstr ""
#: members/templates/member_list.html:28
#: members/templates/member_list.html:21
#, fuzzy
#| msgid "Member register"
msgid "Members in register:"
msgstr "Member register"
#: members/templates/member_list.html:34
#: members/templates/member_list.html:28 members/templates/payment_list.html:25
msgid "Search"
msgstr ""
#: members/templates/member_list.html:36 members/templates/payment_list.html:33
msgid "Showing results for"
msgstr ""
#: members/templates/member_list.html:44
msgid "Download CSV"
msgstr "Download CSV"
@@ -393,10 +407,6 @@ msgstr "Download CSV"
msgid "Member register of SIK ry"
msgstr "Member register of SIK ry"
#: members/templates/members_base.html:52 webapp/templates/main_index.html:7
msgid "Members"
msgstr "Members"
#: members/templates/members_base.html:54
msgid "List members"
msgstr "List members"
@@ -405,15 +415,11 @@ msgstr "List members"
msgid "Add multiple"
msgstr "Add multiple"
#: members/templates/members_base.html:60
msgid "Payments"
msgstr "Payments"
#: members/templates/members_base.html:62
msgid "List payments"
msgstr "List payments"
#: members/templates/members_base.html:63 members/templates/payment_add.html:8
#: members/templates/members_base.html:63 members/templates/payment_add.html:13
msgid "Add payment"
msgstr "Add payment"
@@ -441,121 +447,136 @@ msgstr "Edit payment"
msgid "Payment events"
msgstr "Payment events"
#: members/views.py:129 members/views.py:186 members/views.py:205
msgid "No member id specified"
msgstr "No member id specified"
#: members/templates/payment_list.html:18
#, fuzzy
#| msgid "Member register"
msgid "Payments in register:"
msgstr "Member register"
#: members/views.py:151
msgid "Successfully added member"
msgstr "Successfully added member"
#: members/views.py:172
msgid "Successfully updated member"
msgstr "Successfully updated member"
#: members/views.py:176
msgid "Could not update member object"
msgstr "Could not update member object"
#: members/views.py:190
msgid "Successfully deleted member"
msgstr "Successfully deleted member"
#: members/views.py:196
msgid "Could not delete member object"
msgstr "Could not delete member object"
#: members/views.py:239 members/views.py:273 members/views.py:291
#: members/views/applications.py:49 members/views/applications.py:96
#: members/views/applications.py:124
msgid "No application id specified"
msgstr "No application id specified"
#: members/views.py:260
#: members/views/applications.py:77
msgid "Successfully accepted application"
msgstr "Successfully accepted application"
#: members/views.py:263
#: members/views/applications.py:84
msgid "Could not accept application object"
msgstr "Could not accept application object"
#: members/views.py:277
#: members/views/applications.py:100
msgid "Successfully deleted application"
msgstr "Successfully deleted application"
#: members/views.py:282
#: members/views/applications.py:112
msgid "Could not delete application object"
msgstr "Could not delete application object"
#: members/views.py:346
msgid "Successfully added payment for member"
msgstr "Successfully added payment for member"
#: members/views/members.py:70 members/views/members.py:163
#: members/views/members.py:189
msgid "No member id specified"
msgstr "No member id specified"
#: members/views.py:359 members/views.py:372 members/views.py:386
msgid "No payment id specified"
msgstr "No payment id specified"
#: members/views.py:390
msgid "Successfully deleted payment"
msgstr "Successfully deleted payment"
#: members/views.py:395
msgid "Could not delete payment object"
msgstr "Could not delete payment object"
#: members/views.py:410
msgid "Successfully updated payment"
msgstr "Successfully updated payment"
#: members/views.py:413
msgid "Could not update payment object"
msgstr "Could not update payment object"
#: members/views.py:430
msgid "Missing \"textfield\" POST request field"
msgstr "Missing \"textfield\" POST request field"
#: members/views.py:435
msgid "Successfully imported multiple members"
msgstr "Successfully imported multiple members"
#: members/views.py:438
#: members/views/members.py:105
msgid "Failed to import members"
msgstr "Failed to import members"
#: members/views.py:504
#, fuzzy
#| msgid "Successfully deleted member"
msgid "Successfully resolved all member conflicts."
#: members/views/members.py:118
msgid "Successfully added member"
msgstr "Successfully added member"
#: members/views/members.py:143
msgid "Successfully updated member"
msgstr "Successfully updated member"
#: members/views/members.py:151
msgid "Could not update member object"
msgstr "Could not update member object"
#: members/views/members.py:167
msgid "Successfully deleted member"
msgstr "Successfully deleted member"
#: members/views/members.py:178
msgid "Could not delete member object"
msgstr "Could not delete member object"
#: members/views/payments.py:69
msgid "Successfully added payment for member"
msgstr "Successfully added payment for member"
#: members/views/payments.py:87 members/views/payments.py:105
#: members/views/payments.py:124
msgid "No payment id specified"
msgstr "No payment id specified"
#: members/views/payments.py:129
msgid "Successfully deleted payment"
msgstr "Successfully deleted payment"
#: members/views/payments.py:139
msgid "Could not delete payment object"
msgstr "Could not delete payment object"
#: members/views/payments.py:158
msgid "Successfully updated payment"
msgstr "Successfully updated payment"
#: members/views/payments.py:165
msgid "Could not update payment object"
msgstr "Could not update payment object"
#: members/views/utils.py:110
msgid "Missing \"textfield\" POST request field"
msgstr "Missing \"textfield\" POST request field"
#: templates/footer.html:7
msgid "Copyright Aalto-yliopiston Sähköinsinöörikilta ry"
msgstr "Copyright Aalto-yliopiston Sähköinsinöörikilta ry"
#: webapp/models.py:47
#: webapp/models.py:61
#, fuzzy
#| msgid "Add member"
msgid "Board member"
msgstr "Add member"
#: webapp/models.py:54
#: webapp/models.py:67
#, fuzzy
#| msgid "Duration"
msgid "Description"
msgstr "Duration"
#: webapp/models.py:55
#: webapp/models.py:68
msgid "Summary"
msgstr ""
#: webapp/models.py:70
#: webapp/models.py:96
msgid "Message"
msgstr ""
#: webapp/models.py:109
msgid "Year"
msgstr ""
#: webapp/models.py:123
msgid "Role"
msgstr ""
#: webapp/models.py:125
msgid "Start date"
msgstr ""
#: webapp/models.py:71
#: webapp/models.py:126
msgid "End date"
msgstr ""
#: webapp/models.py:79
#: webapp/models.py:136
msgid "Official"
msgstr ""
#: webapp/models.py:138
msgid "Phone number"
msgstr ""
@@ -603,5 +624,29 @@ msgstr "Sössö"
msgid "Contact"
msgstr "Contact"
#: webapp/templates/ohlhafv.html:8
msgid "Ohlhafv"
msgstr ""
#: webapp/templates/ohlhafv.html:15
msgid "Challenge"
msgstr ""
#: webapp/templates/ohlhafv_list.html:11
msgid "All challenges"
msgstr ""
#: webapp/templates/ohlhafv_list.html:15
msgid "Total challenges:"
msgstr ""
#~ msgid "Successfully imported multiple members"
#~ msgstr "Successfully imported multiple members"
#, fuzzy
#~| msgid "Successfully deleted member"
#~ msgid "Successfully resolved all member conflicts."
#~ msgstr "Successfully deleted member"
#~ msgid "Select"
#~ msgstr "Select"
Binary file not shown.
+169 -142
View File
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-06-07 18:22+0300\n"
"POT-Creation-Date: 2017-09-25 21:32+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -18,35 +18,35 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: infoscreen/models.py:77
#: infoscreen/models.py:97
msgid "ABB jobs"
msgstr "ABB-työpaikat"
#: infoscreen/models.py:88
#: infoscreen/models.py:112
msgid "APY Item"
msgstr "ÄPY-tilastot"
#: infoscreen/models.py:99
#: infoscreen/models.py:127
msgid "External website"
msgstr "Ulkoinen verkkosivu"
#: infoscreen/models.py:147
#: infoscreen/models.py:184
msgid "Sössö articles"
msgstr "Sössön artikkelit"
#: infoscreen/models.py:158
#: infoscreen/models.py:199
msgid "Events"
msgstr "Tapahtumat"
#: infoscreen/models.py:169
#: infoscreen/models.py:214
msgid "Image"
msgstr "Kuva"
#: infoscreen/models.py:204
#: infoscreen/models.py:260
msgid "HSL timetables"
msgstr "HSL-aikataulut"
#: infoscreen/models.py:215
#: infoscreen/models.py:275
msgid "External image"
msgstr "Ulkoinen kuva"
@@ -168,7 +168,8 @@ msgstr "Valitse muokattava rotaatio"
msgid "id"
msgstr "id"
#: infoscreen/templates/infoscreen_admin.html:141 webapp/models.py:46
#: infoscreen/templates/infoscreen_admin.html:141 webapp/models.py:60
#: webapp/models.py:94 webapp/models.py:107
msgid "Name"
msgstr "Nimi"
@@ -182,14 +183,12 @@ msgid "Language"
msgstr "Kieli"
#: infoscreen/templates/infoscreen_admin.html:161
#: members/templates/settings.html:20 sikweb/settings-sample.py:179
#: sikweb/settings.py:178
#: members/templates/settings.html:20 sikweb/base.py:216
msgid "Finnish"
msgstr "suomi"
#: infoscreen/templates/infoscreen_admin.html:162
#: members/templates/settings.html:21 sikweb/settings-sample.py:178
#: sikweb/settings.py:177
#: members/templates/settings.html:21 sikweb/base.py:217
msgid "English"
msgstr "englanti"
@@ -198,31 +197,32 @@ msgstr "englanti"
msgid "Submit"
msgstr "Lisää"
#: members/forms.py:20 members/tables.py:24
#: members/forms.py:103 members/tables.py:32
msgid "Member"
msgstr "Jäsen"
#: members/models.py:16
#: members/models.py:13
msgid "First name"
msgstr "Etunimi"
#: members/models.py:17
#: members/models.py:14
msgid "Last name"
msgstr "Sukunimi"
#: members/models.py:18
#: members/models.py:15 webapp/models.py:95 webapp/models.py:108
msgid "Email"
msgstr "Sähköposti"
#: members/models.py:19
#: members/models.py:16
msgid "Place of residence"
msgstr "Asuinpaikka"
#: members/models.py:20 members/models.py:83
#: members/models.py:18 members/models.py:83
#: members/templates/member_add_many.html:35
msgid "AYY"
msgstr "AYY"
#: members/models.py:21
#: members/models.py:19
msgid "JAS"
msgstr "JAS"
@@ -242,7 +242,7 @@ msgstr "Lähde"
msgid "Cash"
msgstr "Käteinen"
#: members/models.py:85
#: members/models.py:85 members/templates/member_add_many.html:36
msgid "Bank transfer"
msgstr "Tilisiirto"
@@ -250,15 +250,15 @@ msgstr "Tilisiirto"
msgid "Created"
msgstr "Lisätty"
#: members/tables.py:9
#: members/tables.py:13
msgid "Last paid"
msgstr "Viimeksi maksettu"
#: members/tables.py:13 members/tables.py:28 members/tables.py:41
#: members/tables.py:18 members/tables.py:37 members/tables.py:54
msgid "Edit"
msgstr "Muokkaa"
#: members/tables.py:15 members/tables.py:30 members/tables.py:43
#: members/tables.py:20 members/tables.py:39 members/tables.py:56
msgid "Options"
msgstr "Asetukset"
@@ -291,10 +291,14 @@ msgid "Add member"
msgstr "Lisää jäsen"
#: members/templates/member_add.html:15 members/templates/member_edit.html:18
#: members/templates/payment_add.html:15 members/templates/payment_edit.html:18
#: members/templates/payment_add.html:20 members/templates/payment_edit.html:18
msgid "Save"
msgstr "Tallenna"
#: members/templates/member_add_many.html:8
msgid "Add many members"
msgstr "Lisää useita"
#: members/templates/member_add_many.html:13
msgid ""
"\n"
@@ -326,10 +330,37 @@ msgstr ""
msgid "Syntax"
msgstr "Syntaksi"
#: members/templates/member_add_many.html:32
#: members/templates/member_add_many.html:29
msgid "Data"
msgstr "Data"
#: members/templates/member_add_many.html:33
msgid "Payment source"
msgstr "Maksutapa"
#: members/templates/member_add_many.html:37
msgid "Cash payment"
msgstr "Käteismaksu"
#: members/templates/member_add_many.html:41
#: members/templates/member_add_many_confirm.html:22
msgid "Send"
msgstr "Lähetä"
#: members/templates/member_add_many_confirm.html:8
msgid "Confirm adding these entries?"
msgstr "Vahvista muutokset?"
#: members/templates/member_add_many_confirm.html:12
#: members/templates/members_base.html:52 webapp/templates/main_index.html:7
msgid "Members"
msgstr "Jäsenet"
#: members/templates/member_add_many_confirm.html:16
#: members/templates/members_base.html:60
msgid "Payments"
msgstr "Maksutapahtumat"
#: members/templates/member_delete_confirm.html:9
msgid "Are you sure you want to delete this member?"
msgstr "Oletko varma, että haluat poistaa tämän jäsenen?"
@@ -339,32 +370,6 @@ msgstr "Oletko varma, että haluat poistaa tämän jäsenen?"
msgid "Yes, I'm sure"
msgstr "Kyllä, olen varma"
#: members/templates/member_duplicates.html:9
msgid "Conflicting member entries"
msgstr "Ongelmalliset jäsentiedot"
#: members/templates/member_duplicates.html:13
msgid ""
"Found conflicting member entries. Choose how to handle the problematic data."
msgstr ""
"Ongelmallista jäsendataa havaittu. Valitse, miten ongelmat ratkaistaan."
#: members/templates/member_duplicates.html:29
msgid "Which one has the correct information for this member?"
msgstr "Kummassa on jäsenen oikeat tiedot?"
#: members/templates/member_duplicates.html:31
msgid "Accept first and remove second"
msgstr "Hyväksy ensimmäinen ja poista toinen"
#: members/templates/member_duplicates.html:32
msgid "Accept second and remove first"
msgstr "Hyväksy toinen ja poista ensimmäinen"
#: members/templates/member_duplicates.html:33
msgid "Accept both as two members"
msgstr "Hyväksy molemmat kahtena jäsenenä"
#: members/templates/member_edit.html:9
msgid "Edit member"
msgstr "Muokkaa jäsentä"
@@ -373,20 +378,19 @@ msgstr "Muokkaa jäsentä"
msgid "Member register"
msgstr "Jäsenrekisteri"
#: members/templates/member_list.html:16
msgid ""
"There are duplicate member entries in the register.\n"
" Please visit <a href=\"/members/duplicates\">duplicate resolver</a>."
msgstr ""
"Jäsenrekisterissä on duplikaattijäseniä.\n"
" Käytä ongelman ratkaisuun <a href=\"/members/duplicates"
"\">duplikaattityökalua</a>."
#: members/templates/member_list.html:28
#: members/templates/member_list.html:21
msgid "Members in register:"
msgstr "Jäseniä:"
#: members/templates/member_list.html:34
#: members/templates/member_list.html:28 members/templates/payment_list.html:25
msgid "Search"
msgstr "Hae"
#: members/templates/member_list.html:36 members/templates/payment_list.html:33
msgid "Showing results for"
msgstr "Näytetään tulokset haulle"
#: members/templates/member_list.html:44
msgid "Download CSV"
msgstr "Lataa CSV"
@@ -395,10 +399,6 @@ msgstr "Lataa CSV"
msgid "Member register of SIK ry"
msgstr "Aalto-yliopiston Sähköinsinöörikilta ry:n jäsenrekisteri"
#: members/templates/members_base.html:52 webapp/templates/main_index.html:7
msgid "Members"
msgstr "Jäsenet"
#: members/templates/members_base.html:54
msgid "List members"
msgstr "Jäsenlistaus"
@@ -407,15 +407,11 @@ msgstr "Jäsenlistaus"
msgid "Add multiple"
msgstr "Lisää useita"
#: members/templates/members_base.html:60
msgid "Payments"
msgstr "Maksutapahtumat"
#: members/templates/members_base.html:62
msgid "List payments"
msgstr "Maksulistaus"
#: members/templates/members_base.html:63 members/templates/payment_add.html:8
#: members/templates/members_base.html:63 members/templates/payment_add.html:13
msgid "Add payment"
msgstr "Lisää maksu"
@@ -443,115 +439,130 @@ msgstr "Muokkaa maksua"
msgid "Payment events"
msgstr "Maksutapahtumat"
#: members/views.py:129 members/views.py:186 members/views.py:205
msgid "No member id specified"
msgstr "Jäsenen ID ei määritelty"
#: members/templates/payment_list.html:18
msgid "Payments in register:"
msgstr "Maksutapahtumia:"
#: members/views.py:151
msgid "Successfully added member"
msgstr "Onnistuneesti lisättiin jäsen"
#: members/views.py:172
msgid "Successfully updated member"
msgstr "Onnistuneesti päivitettiin jäsen"
#: members/views.py:176
msgid "Could not update member object"
msgstr "Jäsenobjektia ei voitu päivittää"
#: members/views.py:190
msgid "Successfully deleted member"
msgstr "Onnistuneesti poistettiin jäsen"
#: members/views.py:196
msgid "Could not delete member object"
msgstr "Jäsenobjektia ei voitu poistaa"
#: members/views.py:239 members/views.py:273 members/views.py:291
#: members/views/applications.py:49 members/views/applications.py:96
#: members/views/applications.py:124
msgid "No application id specified"
msgstr "Hakemuksen ID ei määritelty"
#: members/views.py:260
#: members/views/applications.py:77
msgid "Successfully accepted application"
msgstr "Onnistuneesti hyväksyttiin hakemus"
#: members/views.py:263
#: members/views/applications.py:84
msgid "Could not accept application object"
msgstr "Hakemusobjektia ei voitu hyväksyä"
#: members/views.py:277
#: members/views/applications.py:100
msgid "Successfully deleted application"
msgstr "Onnistuneesti poistettiin hakemus"
#: members/views.py:282
#: members/views/applications.py:112
msgid "Could not delete application object"
msgstr "Hakemusobjektia ei voitu poistaa"
#: members/views.py:346
msgid "Successfully added payment for member"
msgstr "Onnistuneesti lisättiin maksutapahtuma jäsenelle"
#: members/views/members.py:70 members/views/members.py:163
#: members/views/members.py:189
msgid "No member id specified"
msgstr "Jäsenen ID ei määritelty"
#: members/views.py:359 members/views.py:372 members/views.py:386
msgid "No payment id specified"
msgstr "Maksutapahtuman ID ei määritelty"
#: members/views.py:390
msgid "Successfully deleted payment"
msgstr "Onnistuneesti poistettiin maksutapahtuma"
#: members/views.py:395
msgid "Could not delete payment object"
msgstr "Maksutapahtumaobjektia ei voitu poistaa"
#: members/views.py:410
msgid "Successfully updated payment"
msgstr "Onnistuneesti päivitettiin maksutapahtuma"
#: members/views.py:413
msgid "Could not update payment object"
msgstr "Maksutapahtumaobjektia ei voitu päivittää"
#: members/views.py:430
msgid "Missing \"textfield\" POST request field"
msgstr "Puuttuva \"textfield\" POST-kenttä"
#: members/views.py:435
msgid "Successfully imported multiple members"
msgstr "Onnistuneesti tuotu useita jäseniä"
#: members/views.py:438
#: members/views/members.py:105
msgid "Failed to import members"
msgstr "Jäsenten tuonti epäonnistui"
#: members/views.py:504
msgid "Successfully resolved all member conflicts."
msgstr "Kaikki jäsenkonfliktit ratkaistu onnistuneesti."
#: members/views/members.py:118
msgid "Successfully added member"
msgstr "Onnistuneesti lisättiin jäsen"
#: members/views/members.py:143
msgid "Successfully updated member"
msgstr "Onnistuneesti päivitettiin jäsen"
#: members/views/members.py:151
msgid "Could not update member object"
msgstr "Jäsenobjektia ei voitu päivittää"
#: members/views/members.py:167
msgid "Successfully deleted member"
msgstr "Onnistuneesti poistettiin jäsen"
#: members/views/members.py:178
msgid "Could not delete member object"
msgstr "Jäsenobjektia ei voitu poistaa"
#: members/views/payments.py:69
msgid "Successfully added payment for member"
msgstr "Onnistuneesti lisättiin maksutapahtuma jäsenelle"
#: members/views/payments.py:87 members/views/payments.py:105
#: members/views/payments.py:124
msgid "No payment id specified"
msgstr "Maksutapahtuman ID ei määritelty"
#: members/views/payments.py:129
msgid "Successfully deleted payment"
msgstr "Onnistuneesti poistettiin maksutapahtuma"
#: members/views/payments.py:139
msgid "Could not delete payment object"
msgstr "Maksutapahtumaobjektia ei voitu poistaa"
#: members/views/payments.py:158
msgid "Successfully updated payment"
msgstr "Onnistuneesti päivitettiin maksutapahtuma"
#: members/views/payments.py:165
msgid "Could not update payment object"
msgstr "Maksutapahtumaobjektia ei voitu päivittää"
#: members/views/utils.py:110
msgid "Missing \"textfield\" POST request field"
msgstr "Puuttuva \"textfield\" POST-kenttä"
#: templates/footer.html:7
msgid "Copyright Aalto-yliopiston Sähköinsinöörikilta ry"
msgstr "Copyright Aalto-yliopiston Sähköinsinöörikilta ry"
#: webapp/models.py:47
#: webapp/models.py:61
msgid "Board member"
msgstr "Hallituksen jäsen"
#: webapp/models.py:54
#: webapp/models.py:67
msgid "Description"
msgstr "Kuvaus"
#: webapp/models.py:55
#: webapp/models.py:68
msgid "Summary"
msgstr "Tiivistelmä"
#: webapp/models.py:70
#: webapp/models.py:96
msgid "Message"
msgstr "Viesti"
#: webapp/models.py:109
msgid "Year"
msgstr "Vuosi"
#: webapp/models.py:123
msgid "Role"
msgstr "Rooli"
#: webapp/models.py:125
msgid "Start date"
msgstr "Alkupäivämäärä"
#: webapp/models.py:71
#: webapp/models.py:126
msgid "End date"
msgstr "Loppupäivämäärä"
#: webapp/models.py:79
#: webapp/models.py:136
msgid "Official"
msgstr "Toimihenkilö"
#: webapp/models.py:138
msgid "Phone number"
msgstr "Puhelinnumero"
@@ -598,3 +609,19 @@ msgstr "Sössö"
#: webapp/templates/navigation.html:32
msgid "Contact"
msgstr "Yhteystiedot"
#: webapp/templates/ohlhafv.html:8
msgid "Ohlhafv"
msgstr "Øhlhäfv"
#: webapp/templates/ohlhafv.html:15
msgid "Challenge"
msgstr "Haaste"
#: webapp/templates/ohlhafv_list.html:11
msgid "All challenges"
msgstr "Kaikki haasteet"
#: webapp/templates/ohlhafv_list.html:15
msgid "Total challenges:"
msgstr "Haasteita yhteensä:"
+1 -2
View File
@@ -1,12 +1,11 @@
"""Admin site registers for Members app."""
from django.contrib import admin
from members.models import Member, Request, Payment, MemberConflict
from members.models import Member, Request, Payment
# Register your models here.
admin.site.register(Member)
admin.site.register(Request)
admin.site.register(Payment)
admin.site.register(MemberConflict)
admin.site.site_header = 'SIK Admin'
+17 -4
View File
@@ -1,13 +1,14 @@
"""File containing member forms."""
from django import forms
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from members.models import Member, Payment, Request
import csv
import datetime
import logging
from dal import autocomplete
class CSVValidationError(Exception):
@@ -37,11 +38,18 @@ class MemberForm(forms.ModelForm):
return email
def _clean_boolean_field(self, key):
value = self.data.get(key, None)
if value in ['1', '0']:
return bool(int(value))
else:
return value == 'on'
def clean_jas(self):
return bool(int(self.data['jas']))
return self._clean_boolean_field('jas')
def clean_AYY(self):
return bool(int(self.data['AYY']))
return self._clean_boolean_field('AYY')
@staticmethod
def csv_to_models(data, payment_source='AYY'):
@@ -73,7 +81,7 @@ class MemberForm(forms.ModelForm):
payment_data = {
'source': payment_source,
'member': member.id,
'date': datetime.datetime.now(),
'date': timezone.now(),
}
form = PaymentForm(payment_data)
if not form.is_valid():
@@ -88,6 +96,11 @@ class MemberForm(forms.ModelForm):
class PaymentForm(forms.ModelForm):
"""Payment model form."""
member = forms.ModelChoiceField(
queryset=Member.objects.all(),
widget=autocomplete.ModelSelect2(url='member-autocomplete')
)
class Meta:
"""Meta for Payment model form."""
@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2017-09-25 16:17
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('members', '0014_auto_20170920_1457'),
]
operations = [
migrations.RemoveField(
model_name='memberconflict',
name='first_member',
),
migrations.RemoveField(
model_name='memberconflict',
name='second_member',
),
migrations.DeleteModel(
name='MemberConflict',
),
]
@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2017-09-25 16:24
from __future__ import unicode_literals
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('members', '0015_auto_20170925_1917'),
]
operations = [
migrations.AlterField(
model_name='member',
name='created',
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='Created'),
),
migrations.AlterField(
model_name='payment',
name='date',
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='Date'),
),
]
+2 -22
View File
@@ -4,7 +4,6 @@ from django.db import models
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from datetime import datetime
import csv
@@ -79,7 +78,7 @@ class Request(BaseMember):
class Payment(models.Model):
"""Payment model representing one payment event."""
date = models.DateTimeField(_('Date'), default=datetime.now)
date = models.DateTimeField(_('Date'), default=timezone.now)
source = models.CharField(_('Source'), choices=[
('AYY', _('AYY')),
('cash', _('Cash')),
@@ -100,7 +99,7 @@ class Payment(models.Model):
class Member(BaseMember):
"""Member model represets one member on the registry."""
created = models.DateTimeField(_('Created'), default=datetime.now)
created = models.DateTimeField(_('Created'), default=timezone.now)
def last_paid(self):
"""Return member's last payment."""
@@ -128,24 +127,5 @@ class Member(BaseMember):
)
class MemberConflict(models.Model):
"""Model representing member conflict situation."""
first_member = models.ForeignKey(
'Member', related_name='%(class)s_first_member')
second_member = models.ForeignKey(
'Member', related_name='%(class)s_second_member')
@property
def first_member_form(self):
"""Get first member form."""
return MemberForm(instance=self.first_member)
@property
def second_member_form(self):
"""Get second member form."""
return MemberForm(instance=self.second_member)
# To avoid problems with a cyclical import, this is at the bottom of the file
from members.forms import MemberForm # nopep8
+17 -7
View File
@@ -11,13 +11,6 @@
<h2>{% trans "Member register" %}</h2>
</div>
{% if is_member_conflict %}
<div class="alert alert-warning">
{% blocktrans %}There are duplicate member entries in the register.
Please visit <a href="/members/duplicates">duplicate resolver</a>.{% endblocktrans %}
</div>
{% endif %}
{% if notification %}
<div class="alert alert-success">
{{ notification }}
@@ -28,6 +21,23 @@
<span>{% trans "Members in register:" %} {{ member_count }}</span>
</div>
<div>
<form class="input-group" method="GET" action="/members/list">
<input class="form-control" type="text" name="q" placeholder="Teemu Teekkari" />
<span class="input-group-btn">
<input type="submit" class="btn" value="{% trans "Search" %}" />
</span>
</form>
</div>
{% if request.GET.q %}
<div>
<div class="alert alert-info" role="alert">
{% trans "Showing results for" %} "{{ request.GET.q }}"
</div>
</div>
{% endif %}
{{ table|safe }}
<div>
+8
View File
@@ -4,6 +4,11 @@
{% load i18n %}
{% block content %}
<script
src="https://code.jquery.com/jquery-3.2.1.min.js"
integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
crossorigin="anonymous"></script>
<div>
<h3>{% trans "Add payment" %}</h3>
@@ -18,4 +23,7 @@
</form>
</div>
</div>
{{ form.media }}
{% endblock content %}
+21
View File
@@ -14,6 +14,27 @@
</div>
{% endif %}
<div class="member_count">
<span>{% trans "Payments in register:" %} {{ payment_count }}</span>
</div>
<div>
<form class="input-group" method="GET" action="/members/payments">
<input class="form-control" type="text" name="q" placeholder="Teemu Teekkari" />
<span class="input-group-btn">
<input type="submit" class="btn" value="{% trans "Search" %}" />
</span>
</form>
</div>
{% if request.GET.q %}
<div>
<div class="alert alert-info" role="alert">
{% trans "Showing results for" %} "{{ request.GET.q }}"
</div>
</div>
{% endif %}
{{ table|safe }}
</div>
{% endblock content %}
+21 -5
View File
@@ -12,9 +12,11 @@ class MemberRegisterTestCase(TestCase):
def setUp(self):
"""Setup testing environment by creating member and admin."""
memb = Member.objects.create(first_name="Tidus", last_name="Tester")
username, password = 'test_admin', 'password123'
test_admin = User.objects.create_superuser(
'test_admin', 'myemail@test.com', 'password123')
username, 'myemail@test.com', password)
self.c = Client()
self.c.login(username=username, password=password)
def test_member_created(self):
"""Test member creation."""
@@ -24,13 +26,27 @@ class MemberRegisterTestCase(TestCase):
def test_import_csv_single_line(self):
"""Test csv import only with single line in csv file."""
data = 'Teppo, Tulppu, teppo@tulppu.fi, Ankkalinna, 0, 0'
response = self.c.post('/members/import_csv', {'textarea': data})
self.assertIn(response.status_code, [200, 302])
response = self.c.post('/members/import_csv', {'textarea': data}, follow=True)
self.assertEqual(response.status_code, 200)
def test_import_csv_multi_line(self):
"""Test csv import with multilined csv."""
data = ('Teppo, Tulppu, teppo@tulppu.fi, Ankkalinna, 0, 0\n'
'Reiska, Remontti, remontti@reiska.fi, Värisilmä, 1, 1')
response = self.c.post('/members/import_csv', {'textarea': data})
self.assertIn(response.status_code, [200, 302])
response = self.c.post('/members/import_csv', {'textarea': data}, follow=True)
self.assertEqual(response.status_code, 200)
def test_autocomplete_search_found(self):
"""Test member autocomplete search"""
search_terms = 'Tidus'
response = self.c.get('/members/member-autocomplete?q={}'.format(search_terms), follow=True)
results = response.json()['results']
self.assertEqual(len(results), 1)
def test_autocomplete_search_not_found(self):
"""Test member autocomplete search"""
search_terms = 'Notfound'
response = self.c.get('/members/member-autocomplete?q={}'.format(search_terms), follow=True)
results = response.json()['results']
self.assertEqual(len(results), 0)
+15
View File
@@ -1,6 +1,7 @@
"""File containing Member application URLs."""
from django.conf.urls import url
from django.contrib.auth.decorators import login_required, permission_required
from django.views.generic.base import RedirectView
# members
@@ -20,6 +21,9 @@ from members.views import member_delete
from members.views import payment_list
from members.views import add_many_confirm
# autocomplete view
from members.views import MemberAutoComplete
# rest api
from members.views import MemberDetail
@@ -36,6 +40,10 @@ from members.views import application_form_success
favicon_view = RedirectView.as_view(
url='static/img/favicon.ico', permanent=True)
member_autocomplete_view = login_required(
permission_required('members.change_member', login_url='/login')(MemberAutoComplete.as_view())
)
urlpatterns = [
# landing page
@@ -110,4 +118,11 @@ urlpatterns = [
# rest api url
url(r'^api/members/(?P<pk>\d+)$', MemberDetail.as_view()),
# member select autocomplete view
url(
r'^member-autocomplete/$',
member_autocomplete_view,
name='member-autocomplete',
),
]
+1 -594
View File
@@ -17,12 +17,6 @@ from django.utils.http import urlsafe_base64_encode
from django.utils.encoding import force_bytes
from django.core.mail import send_mail
# REST framework
from members.serializers import MemberSerializer
from rest_framework import generics
from rest_framework import permissions
from rest_framework.throttling import UserRateThrottle, AnonRateThrottle
import json
import requests
import logging
@@ -31,588 +25,11 @@ import csv
import pickle
from smtplib import SMTPAuthenticationError
from members.models import Member, Request, Payment, MemberConflict
from members.models import Member, Request, Payment
from members.forms import MemberForm, PaymentForm, ApplicationForm, CSVValidationError
from members.tables import MemberTable, PaymentTable, RequestTable
def error_view(request, message):
return render(request, 'error.html', {'error': str(message)})
def validate_recaptcha(response):
"""
Recaptcha is used in member applications.
:param response:
:return: Boolean, success or not
"""
values = {
'secret': settings.GOOGLE_RECAPTCHA_SECRET_KEY,
'response': response,
}
url = "https://www.google.com/recaptcha/api/siteverify"
headers = {'Content-type': 'application/x-www-form-urlencoded'}
resp = requests.post(url, values, headers=headers)
try:
result = json.loads(resp.text)
logging.info('Recaptcha response: {}'.format(result))
return result["success"]
except:
return False
def send_mail_wrapper(subject, message):
"""Call send_mail function."""
send_mail(subject,
message,
'no-reply@sahkoinsinoorikilta.fi',
['viestintamestari@sahkoinsinoorikilta.fi'],
fail_silently=False)
def convert_table_to_html(table, request):
"""
Convert table to html.
This is a horrible hack for converting a table object to raw html.
Even with extensive research I wasn't able to find a way to add a path
prefix "e.g. /members/list" to the query strings "e.g. ?sort=foo", so I
did it manually with string.replace.
Note: When adding the html to a page, you need to run it through
the "safe" filter. E.g. "{{ table|safe }}"
:param table: Table object from members.tables
:param request: HttpRequest
:return: Raw html string
"""
table_as_html = table.as_html(request)
path = request.path
fixed = table_as_html.replace(r'href="?', r'href="{}?'.format(path))
return fixed
@ensure_csrf_cookie
@require_http_methods(["GET"])
@permission_required('members.change_member', login_url='/login')
def member_list(request, *args, **kwargs):
"""Render members list."""
members = Member.objects.all()
table = MemberTable(members,
request=request,
exclude=['id'],
attrs={'class': 'table table-bordered table-hover'})
table.paginate(page=request.GET.get('page', 1), per_page=25)
table_html = convert_table_to_html(table, request)
context = {
'table': table_html,
'member_count': len(members),
'notification': request.GET.get('notification', None),
'is_member_conflict': MemberConflict.objects.exists()
}
return render(request, 'member_list.html', context)
@ensure_csrf_cookie
@require_http_methods(["GET"])
@permission_required('members.change_member', login_url='/login')
def member_add(request, *args, **kwargs):
"""Render add member page."""
form = MemberForm()
return render(request, 'member_add.html', {'form': form})
@ensure_csrf_cookie
@require_http_methods(["GET"])
@permission_required('members.change_member', login_url='/login')
def member_delete_confirm(request, *args, **kwargs):
"""Render member deletion confirmation page."""
i = kwargs.pop('index', None)
if i is None:
return render(request, 'error.html',
{'error': _('No member id specified')})
else:
member = Member.objects.get(id=i)
form = MemberForm(instance=member)
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 member_add_many(request, *args, **kwargs):
"""Render add multiple members page."""
return render(request, 'member_add_many.html', {})
@ensure_csrf_cookie
@require_http_methods(["POST"])
@permission_required('members.change_member', login_url='/login')
def member_submit(request, *args, **kwargs):
"""Add member based on data gained from member form."""
form = MemberForm(request.POST)
if form.is_valid():
form.save()
logging.info("Saved new member to member register"
"with the following info: {}".format(form))
notification = "{} {} {}.".format(_("Successfully added member"),
form.cleaned_data['last_name'],
form.cleaned_data['first_name'])
return HttpResponseRedirect(
'/members/list?notification={}'.format(html.escape(notification)))
else:
return render(request, 'error.html', {'error': form.errors})
@ensure_csrf_cookie
@require_http_methods(["POST"])
@permission_required('members.change_member', login_url='/login')
def member_update(request, *args, **kwargs):
"""Update member information."""
form = MemberForm(request.POST)
if form.is_valid():
id = request.POST['id']
member = Member.objects.get(id=id)
form = MemberForm(request.POST, instance=member)
form.save()
logging.info(
"Updated member in member register with the following info: {}"
.format(form))
notification = "{} {} {}.".format(_("Successfully updated member"),
member.last_name, member.first_name)
return HttpResponseRedirect(
'/members/list?notification={}'.format(html.escape(notification)))
else:
return render(
request,
'error.html',
{'error': _('Could not update member object')})
@ensure_csrf_cookie
@require_http_methods(["POST"])
@permission_required('members.change_member', login_url='/login')
def member_delete(request, *args, **kwargs):
"""Delete member."""
try:
id = request.POST['id']
except KeyError:
return render(request,
'error.html', {'error': _('No member id specified')})
try:
member = Member.objects.get(id=id)
notification = "{} {} {}.".format(_("Successfully deleted member"),
member.last_name, member.first_name)
member.delete()
logging.info(
"Delete member in member register with the following id: {}"
.format(id))
return HttpResponseRedirect(
'/members/list?notification={}'.format(html.escape(notification)))
except:
return render(request,
'error.html',
{'error': _('Could not delete member object')})
@ensure_csrf_cookie
@require_http_methods(["GET"])
@permission_required('members.change_member', login_url='/login')
def member_edit(request, *args, **kwargs):
"""Edit member information."""
i = kwargs.pop('index', None)
if i is None:
return render(
request, 'error.html', {'error': _('No member id specified')})
else:
member = Member.objects.get(id=i)
form = MemberForm(instance=member)
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 application_list(request, *args, **kwargs):
"""List member applications not yet processed."""
applications = Request.objects.all()
application_count = len(applications)
table = RequestTable(applications,
request=request,
exclude=['id'],
attrs={'class': 'table table-bordered table-hover'})
table.paginate(page=request.GET.get('page', 1), per_page=25)
table_html = convert_table_to_html(table, request)
context = {
'table': table_html,
'application_count': application_count,
'notification': request.GET.get('notification', None)
}
return render(request, 'application_list.html', context)
@ensure_csrf_cookie
@require_http_methods(["GET"])
@permission_required('members.change_member', login_url='/login')
def application_edit(request, *args, **kwargs):
"""Edit member request information."""
i = kwargs.pop('index', None)
if i is None:
return render(
request, 'error.html', {'error': _('No application id specified')})
else:
application = Request.objects.get(id=i)
form = ApplicationForm(instance=application)
return render(
request,
'application_edit.html',
{'application_id': i, 'form': form})
@ensure_csrf_cookie
@require_http_methods(["POST"])
@permission_required('members.change_member', login_url='/login')
def application_accept(request, *args, **kwargs):
"""Accept application."""
form = ApplicationForm(request.POST)
if form.is_valid():
id = request.POST['id']
application = Request.objects.get(id=id)
member = application.to_member()
member.save()
application.delete()
logging.info(
"Accepted application in member "
"register with the following info: {}"
.format(form))
notification = "{} {}.".format(_("Successfully accepted application"),
str(application))
return HttpResponseRedirect(
'/members/list?notification={}'.format(html.escape(notification)))
else:
return render(request,
'error.html',
{'error': _('Could not accept application object')})
@ensure_csrf_cookie
@require_http_methods(["POST"])
@permission_required('members.change_member', login_url='/login')
def application_delete(request, *args, **kwargs):
"""Delete member application."""
try:
id = request.POST['id']
except KeyError:
return render(
request, 'error.html', {'error': _('No application id specified')})
try:
application = Request.objects.get(id=id)
notification = "{} {}.".format(_("Successfully deleted application"),
str(application))
application.delete()
logging.info(
"Delete application in member register with the following id: {}"
.format(id))
return HttpResponseRedirect(
'/members/applications?notification={}'
.format(html.escape(notification)))
except:
return render(request,
'error.html',
{'error': _('Could not delete application object')})
@ensure_csrf_cookie
@require_http_methods(["GET"])
@permission_required('members.change_member', login_url='/login')
def application_delete_confirm(request, *args, **kwargs):
"""Confirm application deletion."""
i = kwargs.pop('index', None)
if i is None:
return render(request,
'error.html',
{'error': _('No application id specified')})
else:
application = Request.objects.get(id=i)
form = ApplicationForm(instance=application)
return render(request,
'application_delete_confirm.html',
{'application_id': i, 'form': form})
@ensure_csrf_cookie
def application_form(request, *args, **kwargs):
"""Render member application form."""
return render(request, 'application_index.html', {})
@ensure_csrf_cookie
def application_form_success(request, *args, **kwargs):
"""Render application Successfully sent page."""
return render(request, 'application_success.html', {})
@ensure_csrf_cookie
@require_http_methods(["GET"])
@permission_required('members.change_member', login_url='/login')
def payment_list(request, *args, **kwargs):
"""Render list of payments."""
payments = Payment.objects.all()
table = PaymentTable(payments,
request=request,
exclude=['id'],
attrs={'class': 'table table-bordered table-hover'})
table.paginate(page=request.GET.get('page', 1), per_page=25)
table_html = convert_table_to_html(table, request)
context = {
'table': table_html,
'payment_count': len(payments),
'notification': request.GET.get('notification', None)
}
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):
"""Render add payment form."""
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):
"""Submit payment."""
form = PaymentForm(request.POST)
if form.is_valid():
form.save()
logging.info(
"Saved new payment to member register with the following info: {}"
.format(form))
notification = "{} {}.".format(
_("Successfully added payment for member"),
form.cleaned_data['member'])
return HttpResponseRedirect(
'/members/payments?notification={}'
.format(html.escape(notification)))
else:
return render(request, 'error.html', {'error': form.errors})
@ensure_csrf_cookie
@require_http_methods(["GET"])
@permission_required('members.change_member', login_url='/login')
def payment_edit(request, *args, **kwargs):
"""Edit payment."""
i = kwargs.pop('index', None)
if i is None:
return render(request,
'error.html',
{'error': _('No payment id specified')})
else:
payment = Payment.objects.get(id=i)
form = PaymentForm(instance=payment)
return render(request,
'payment_edit.html',
{'payment_id': i, 'form': form})
@ensure_csrf_cookie
@require_http_methods(["GET"])
@permission_required('members.change_member', login_url='/login')
def payment_delete_confirm(request, *args, **kwargs):
"""Render payment delete confirmation page."""
i = kwargs.pop('index', None)
if i is None:
return render(request,
'error.html',
{'error': _('No payment id specified')})
else:
payment = Payment.objects.get(id=i)
form = PaymentForm(instance=payment)
return render(request,
'payment_delete_confirm.html',
{'payment_id': i, 'form': form})
@ensure_csrf_cookie
@require_http_methods(["POST"])
@permission_required('members.change_member', login_url='/login')
def payment_delete(request, *args, **kwargs):
"""Delete payment."""
try:
id = request.POST['id']
except KeyError:
return render(request,
'error.html',
{'error': _('No payment id specified')})
try:
payment = Payment.objects.get(id=id)
notification = "{} {}.".format(
_("Successfully deleted payment"), str(payment))
payment.delete()
logging.info(
"Delete payment '{}' in member register".format(str(payment)))
return HttpResponseRedirect(
'/members/payments?notification={}'
.format(html.escape(notification)))
except:
return render(request,
'error.html',
{'error': _('Could not delete payment object')})
@ensure_csrf_cookie
@require_http_methods(["POST"])
@permission_required('members.change_member', login_url='/login')
def payment_update(request, *args, **kwargs):
"""Update payment information."""
form = PaymentForm(request.POST)
if form.is_valid():
id = request.POST['id']
payment = Payment.objects.get(id=id)
form = PaymentForm(request.POST, instance=payment)
form.save()
logging.info(
"Updated member in member register with the following info: {}"
.format(form))
notification = "{} {}.".format(
_("Successfully updated payment"), str(payment))
return HttpResponseRedirect(
'/members/payments?notification={}'
.format(html.escape(notification)))
else:
return render(request,
'error.html',
{'error': _('Could not update payment object')})
@ensure_csrf_cookie
@require_http_methods(["GET"])
@permission_required('members.change_member', login_url='/login')
def settings_page(request, *args, **kwargs):
"""Render member app settings page."""
return render(request, 'settings.html', {})
@ensure_csrf_cookie
@require_http_methods(["POST"])
@permission_required('members.change_member', login_url='/login')
def import_csv(request, *args, **kwargs):
"""Get csv data imported to page and create members based on that."""
try:
data = request.POST['textfield']
payment_source = request.POST['payment_source']
except:
return render(request,
'error.html',
{'error': _('Missing "textfield" POST request field')})
try:
result = MemberForm.csv_to_models(data, payment_source=payment_source)
except CSVValidationError as ex:
logging.exception('Model validation error')
return error_view(request, ex.form_errors)
except Exception as ex:
logging.exception('Other error in CSV import')
return error_view(request, ex)
member_table = MemberTable(result.members,
request=request,
exclude=['id', 'options'],
attrs={'class': 'table table-bordered table-hover'})
member_table_html = convert_table_to_html(member_table, request)
payment_table = PaymentTable(result.payments,
request=request,
exclude=['id', 'options'],
attrs={'class': 'table table-bordered table-hover'})
payment_table_html = convert_table_to_html(payment_table, request)
request.session['models'] = result
context = {
'members': member_table_html,
'payments': payment_table_html
}
return render(request, 'member_add_many_confirm.html', context)
@ensure_csrf_cookie
@require_http_methods(["POST"])
@permission_required('members.change_member', login_url='/login')
def add_many_confirm(request, *args, **kwargs):
models = request.session['models']
try:
members, payments = models.members, models.payments
for member in members:
member.save()
for payment in payments:
payment.save()
msg = "Successfully imported {} members and {} payments."
notification = _(msg).format(len(members), len(payments))
return HttpResponseRedirect('/members/list?notification={}'.format(html.escape(notification)))
except Exception as ex:
logging.exception('Failed to save models after "add many."')
return error_view(request, _('Failed to import members'))
@ensure_csrf_cookie
@require_http_methods(["GET"])
@permission_required('members.change_member', login_url='/login')
def export_csv(request, *args, **kwargs):
"""Export members as csv."""
response = HttpResponse()
response['Content-type'] = 'text/csv'
response['Accept'] = 'text/csv'
response['Content-Disposition'] = 'filename; filename=members.csv'
writer = csv.writer(response, csv.excel)
# BOM (optional...Excel needs it to open UTF-8 file properly)
response.write(u'\ufeff'.encode('utf8'))
for obj in Member.objects.all():
data = obj.as_array()
field_list = map(lambda d: str(d), data)
writer.writerow(field_list)
return response
def send_mail_wrapper(subject, message, email_to):
"""Send mail to default email."""
send_mail(subject,
message,
settings.DEFAULT_EMAIL_FROM,
[email_to],
fail_silently=False)
@receiver(post_save, sender=Request)
def email_on_request(sender, instance, created, **kwargs):
"""Send email validation."""
@@ -641,13 +58,3 @@ def email_on_accept(sender, instance, created, **kwargs):
send_mail_wrapper(subject, message, instance.email)
except SMTPAuthenticationError:
logging.error('Failed to send email to accepted member!')
# Can be used to retrieve single member information via REST API
class MemberDetail(generics.RetrieveAPIView):
"""Member detail rest API view."""
queryset = Member.objects.all()
serializer_class = MemberSerializer
permission_classes = (permissions.IsAdminUser, )
throttle_classes = (UserRateThrottle, AnonRateThrottle, )
+4
View File
@@ -0,0 +1,4 @@
from members.views.members import *
from members.views.applications import *
from members.views.payments import *
from members.views.utils import *
+142
View File
@@ -0,0 +1,142 @@
from django.shortcuts import render
from django.contrib.auth.decorators import permission_required
from django.views.decorators.http import require_http_methods
from django.views.decorators.csrf import ensure_csrf_cookie
from django.http import HttpResponse, HttpResponseRedirect
from django.core.mail import send_mail
from django.conf import settings
from django.utils.translation import ugettext as _
from django.forms.models import model_to_dict
import logging
import html
from members.views.utils import *
from members.tables import RequestTable
from members.forms import ApplicationForm
@ensure_csrf_cookie
@require_http_methods(["GET"])
@permission_required('members.change_member', login_url='/login')
def application_list(request, *args, **kwargs):
"""List member applications not yet processed."""
applications = Request.objects.all()
application_count = len(applications)
table = RequestTable(applications,
request=request,
exclude=['id'],
attrs={'class': 'table table-bordered table-hover'})
table.paginate(page=request.GET.get('page', 1), per_page=25)
table_html = convert_table_to_html(table, request)
context = {
'table': table_html,
'application_count': application_count,
'notification': request.GET.get('notification', None)
}
return render(request, 'application_list.html', context)
@ensure_csrf_cookie
@require_http_methods(["GET"])
@permission_required('members.change_member', login_url='/login')
def application_edit(request, *args, **kwargs):
"""Edit member request information."""
i = kwargs.pop('index', None)
if i is None:
return render(
request, 'error.html', {'error': _('No application id specified')})
else:
application = Request.objects.get(id=i)
form = ApplicationForm(instance=application)
return render(
request,
'application_edit.html',
{'application_id': i, 'form': form})
@ensure_csrf_cookie
@require_http_methods(["POST"])
@permission_required('members.change_member', login_url='/login')
def application_accept(request, *args, **kwargs):
"""Accept application."""
form = ApplicationForm(request.POST)
if form.is_valid():
id = request.POST['id']
application = Request.objects.get(id=id)
member = application.to_member()
member.save()
application.delete()
logging.info(
"Accepted application in member "
"register with the following info: {}"
.format(form))
notification = "{} {}.".format(_("Successfully accepted application"),
str(application))
return HttpResponseRedirect(
'/members/list?notification={}'.format(html.escape(notification)))
else:
return render(request,
'error.html',
{'error': _('Could not accept application object')})
@ensure_csrf_cookie
@require_http_methods(["POST"])
@permission_required('members.change_member', login_url='/login')
def application_delete(request, *args, **kwargs):
"""Delete member application."""
try:
id = request.POST['id']
except KeyError:
return render(
request, 'error.html', {'error': _('No application id specified')})
try:
application = Request.objects.get(id=id)
notification = "{} {}.".format(_("Successfully deleted application"),
str(application))
application.delete()
logging.info(
"Delete application in member register with the following id: {}"
.format(id))
return HttpResponseRedirect(
'/members/applications?notification={}'
.format(html.escape(notification)))
except:
return render(request,
'error.html',
{'error': _('Could not delete application object')})
@ensure_csrf_cookie
@require_http_methods(["GET"])
@permission_required('members.change_member', login_url='/login')
def application_delete_confirm(request, *args, **kwargs):
"""Confirm application deletion."""
i = kwargs.pop('index', None)
if i is None:
return render(request,
'error.html',
{'error': _('No application id specified')})
else:
application = Request.objects.get(id=i)
form = ApplicationForm(instance=application)
return render(request,
'application_delete_confirm.html',
{'application_id': i, 'form': form})
@ensure_csrf_cookie
def application_form(request, *args, **kwargs):
"""Render member application form."""
return render(request, 'application_index.html', {})
@ensure_csrf_cookie
def application_form_success(request, *args, **kwargs):
"""Render application Successfully sent page."""
return render(request, 'application_success.html', {})
+205
View File
@@ -0,0 +1,205 @@
from django.shortcuts import render
from django.contrib.auth.decorators import permission_required
from django.utils.decorators import method_decorator
from django.views.decorators.http import require_http_methods
from django.views.decorators.csrf import ensure_csrf_cookie
from django.http import HttpResponse, HttpResponseRedirect
from django.core.mail import send_mail
from django.conf import settings
from django.utils.translation import ugettext as _
from django.forms.models import model_to_dict
from dal import autocomplete
import logging
import html
from members.models import Member, Request, Payment
from members.forms import MemberForm, CSVValidationError
from members.tables import MemberTable
from members.views.utils import *
@ensure_csrf_cookie
@require_http_methods(["GET"])
@permission_required('members.change_member', login_url='/login')
def member_list(request, *args, **kwargs):
"""Render members list."""
search = request.GET.get('q', None)
if search:
firsts = Member.objects.filter(first_name__istartswith=search)
lasts = Member.objects.filter(last_name__istartswith=search)
members = firsts | lasts
else:
members = Member.objects.all()
table = MemberTable(members,
request=request,
exclude=['id'],
attrs={'class': 'table table-bordered table-hover'})
table.paginate(page=request.GET.get('page', 1), per_page=25)
table_html = convert_table_to_html(table, request)
context = {
'table': table_html,
'member_count': len(members),
'notification': request.GET.get('notification', None),
}
return render(request, 'member_list.html', context)
@ensure_csrf_cookie
@require_http_methods(["GET"])
@permission_required('members.change_member', login_url='/login')
def member_add(request, *args, **kwargs):
"""Render add member page."""
form = MemberForm()
return render(request, 'member_add.html', {'form': form})
@ensure_csrf_cookie
@require_http_methods(["GET"])
@permission_required('members.change_member', login_url='/login')
def member_delete_confirm(request, *args, **kwargs):
"""Render member deletion confirmation page."""
i = kwargs.pop('index', None)
if i is None:
return render(request, 'error.html',
{'error': _('No member id specified')})
else:
member = Member.objects.get(id=i)
form = MemberForm(instance=member)
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 member_add_many(request, *args, **kwargs):
"""Render add multiple members page."""
return render(request, 'member_add_many.html', {})
@ensure_csrf_cookie
@require_http_methods(["POST"])
@permission_required('members.change_member', login_url='/login')
def add_many_confirm(request, *args, **kwargs):
models = request.session['models']
try:
members, payments = models.members, models.payments
for member in members:
member.save()
for payment in payments:
payment.save()
msg = "Successfully imported {} members and {} payments."
notification = _(msg).format(len(members), len(payments))
return HttpResponseRedirect('/members/list?notification={}'.format(html.escape(notification)))
except Exception as ex:
logging.exception('Failed to save models after "add many."')
return error_view(request, _('Failed to import members'))
@ensure_csrf_cookie
@require_http_methods(["POST"])
@permission_required('members.change_member', login_url='/login')
def member_submit(request, *args, **kwargs):
"""Add member based on data gained from member form."""
form = MemberForm(request.POST)
if form.is_valid():
form.save()
logging.info("Saved new member to member register"
"with the following info: {}".format(form.cleaned_data))
notification = "{} {} {}.".format(_("Successfully added member"),
form.cleaned_data['last_name'],
form.cleaned_data['first_name'])
return HttpResponseRedirect(
'/members/list?notification={}'.format(html.escape(notification)))
else:
return render(request, 'error.html', {'error': form.errors})
@ensure_csrf_cookie
@require_http_methods(["POST"])
@permission_required('members.change_member', login_url='/login')
def member_update(request, *args, **kwargs):
"""Update member information."""
form = MemberForm(request.POST)
if form.is_valid():
id = request.POST['id']
member = Member.objects.get(id=id)
form = MemberForm(request.POST, instance=member)
form.save()
logging.info(
"Updated member in member register with the following info: {}"
.format(form))
notification = "{} {} {}.".format(_("Successfully updated member"),
member.last_name, member.first_name)
return HttpResponseRedirect(
'/members/list?notification={}'.format(html.escape(notification)))
else:
return render(
request,
'error.html',
{'error': _('Could not update member object')})
@ensure_csrf_cookie
@require_http_methods(["POST"])
@permission_required('members.change_member', login_url='/login')
def member_delete(request, *args, **kwargs):
"""Delete member."""
try:
id = request.POST['id']
except KeyError:
return render(request,
'error.html', {'error': _('No member id specified')})
try:
member = Member.objects.get(id=id)
notification = "{} {} {}.".format(_("Successfully deleted member"),
member.last_name, member.first_name)
member.delete()
logging.info(
"Delete member in member register with the following id: {}"
.format(id))
return HttpResponseRedirect(
'/members/list?notification={}'.format(html.escape(notification)))
except:
return render(request,
'error.html',
{'error': _('Could not delete member object')})
@ensure_csrf_cookie
@require_http_methods(["GET"])
@permission_required('members.change_member', login_url='/login')
def member_edit(request, *args, **kwargs):
"""Edit member information."""
i = kwargs.pop('index', None)
if i is None:
return render(
request, 'error.html', {'error': _('No member id specified')})
else:
member = Member.objects.get(id=i)
form = MemberForm(instance=member)
return render(
request, 'member_edit.html', {'member_id': i, 'form': form})
class MemberAutoComplete(autocomplete.Select2QuerySetView):
def get_queryset(self):
qs = Member.objects.all()
if self.q:
firsts = qs.filter(first_name__istartswith=self.q)
lasts = qs.filter(last_name__istartswith=self.q)
qs = firsts | lasts
return qs
+165
View File
@@ -0,0 +1,165 @@
from django.shortcuts import render
from django.contrib.auth.decorators import permission_required
from django.views.decorators.http import require_http_methods
from django.views.decorators.csrf import ensure_csrf_cookie
from django.http import HttpResponse, HttpResponseRedirect
from django.core.mail import send_mail
from django.conf import settings
from django.utils.translation import ugettext as _
from django.forms.models import model_to_dict
import logging
import html
from members.views.utils import *
from members.tables import PaymentTable
from members.forms import PaymentForm
@ensure_csrf_cookie
@require_http_methods(["GET"])
@permission_required('members.change_member', login_url='/login')
def payment_list(request, *args, **kwargs):
"""Render list of payments."""
search = request.GET.get('q', None)
if search:
firsts = Payment.objects.filter(member__first_name__istartswith=search)
lasts = Payment.objects.filter(member__last_name__istartswith=search)
payments = firsts | lasts
else:
payments = Payment.objects.all()
table = PaymentTable(payments,
request=request,
exclude=['id'],
attrs={'class': 'table table-bordered table-hover'})
table.paginate(page=request.GET.get('page', 1), per_page=25)
table_html = convert_table_to_html(table, request)
context = {
'table': table_html,
'payment_count': len(payments),
'notification': request.GET.get('notification', None)
}
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):
"""Render add payment form."""
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):
"""Submit payment."""
form = PaymentForm(request.POST)
if form.is_valid():
form.save()
logging.info(
"Saved new payment to member register with the following info: {}"
.format(form.cleaned_data))
notification = "{} {}.".format(
_("Successfully added payment for member"),
form.cleaned_data['member'])
return HttpResponseRedirect(
'/members/payments?notification={}'
.format(html.escape(notification)))
else:
return render(request, 'error.html', {'error': form.errors})
@ensure_csrf_cookie
@require_http_methods(["GET"])
@permission_required('members.change_member', login_url='/login')
def payment_edit(request, *args, **kwargs):
"""Edit payment."""
i = kwargs.pop('index', None)
if i is None:
return render(request,
'error.html',
{'error': _('No payment id specified')})
else:
payment = Payment.objects.get(id=i)
form = PaymentForm(instance=payment)
return render(request,
'payment_edit.html',
{'payment_id': i, 'form': form})
@ensure_csrf_cookie
@require_http_methods(["GET"])
@permission_required('members.change_member', login_url='/login')
def payment_delete_confirm(request, *args, **kwargs):
"""Render payment delete confirmation page."""
i = kwargs.pop('index', None)
if i is None:
return render(request,
'error.html',
{'error': _('No payment id specified')})
else:
payment = Payment.objects.get(id=i)
form = PaymentForm(instance=payment)
return render(request,
'payment_delete_confirm.html',
{'payment_id': i, 'form': form})
@ensure_csrf_cookie
@require_http_methods(["POST"])
@permission_required('members.change_member', login_url='/login')
def payment_delete(request, *args, **kwargs):
"""Delete payment."""
try:
id = request.POST['id']
except KeyError:
return render(request,
'error.html',
{'error': _('No payment id specified')})
try:
payment = Payment.objects.get(id=id)
notification = "{} {}.".format(
_("Successfully deleted payment"), str(payment))
payment.delete()
logging.info(
"Delete payment '{}' in member register".format(str(payment)))
return HttpResponseRedirect(
'/members/payments?notification={}'
.format(html.escape(notification)))
except:
return render(request,
'error.html',
{'error': _('Could not delete payment object')})
@ensure_csrf_cookie
@require_http_methods(["POST"])
@permission_required('members.change_member', login_url='/login')
def payment_update(request, *args, **kwargs):
"""Update payment information."""
form = PaymentForm(request.POST)
if form.is_valid():
id = request.POST['id']
payment = Payment.objects.get(id=id)
form = PaymentForm(request.POST, instance=payment)
form.save()
logging.info(
"Updated member in member register with the following info: {}"
.format(form))
notification = "{} {}.".format(
_("Successfully updated payment"), str(payment))
return HttpResponseRedirect(
'/members/payments?notification={}'
.format(html.escape(notification)))
else:
return render(request,
'error.html',
{'error': _('Could not update payment object')})
+172
View File
@@ -0,0 +1,172 @@
from django.shortcuts import render
from django.contrib.auth.decorators import permission_required
from django.views.decorators.http import require_http_methods
from django.views.decorators.csrf import ensure_csrf_cookie
from django.http import HttpResponse, HttpResponseRedirect
from django.core.mail import send_mail
from django.conf import settings
from django.utils.translation import ugettext as _
from django.forms.models import model_to_dict
import logging
import csv
# REST framework
from members.serializers import MemberSerializer
from rest_framework import generics
from rest_framework import permissions
from rest_framework.throttling import UserRateThrottle, AnonRateThrottle
from members.models import Member, Request, Payment
from members.forms import MemberForm, PaymentForm, ApplicationForm, CSVValidationError
from members.tables import MemberTable, PaymentTable, RequestTable
# Can be used to retrieve single member information via REST API
class MemberDetail(generics.RetrieveAPIView):
"""Member detail rest API view."""
queryset = Member.objects.all()
serializer_class = MemberSerializer
permission_classes = (permissions.IsAdminUser, )
throttle_classes = (UserRateThrottle, AnonRateThrottle, )
def error_view(request, message):
return render(request, 'error.html', {'error': str(message)})
def validate_recaptcha(response):
"""
Recaptcha is used in member applications.
:param response:
:return: Boolean, success or not
"""
values = {
'secret': settings.GOOGLE_RECAPTCHA_SECRET_KEY,
'response': response,
}
url = "https://www.google.com/recaptcha/api/siteverify"
headers = {'Content-type': 'application/x-www-form-urlencoded'}
resp = requests.post(url, values, headers=headers)
try:
result = json.loads(resp.text)
logging.info('Recaptcha response: {}'.format(result))
return result["success"]
except:
return False
def send_mail_wrapper(subject, message):
"""Call send_mail function."""
send_mail(subject,
message,
'no-reply@sahkoinsinoorikilta.fi',
['viestintamestari@sahkoinsinoorikilta.fi'],
fail_silently=False)
def convert_table_to_html(table, request):
"""
Convert table to html.
This is a horrible hack for converting a table object to raw html.
Even with extensive research I wasn't able to find a way to add a path
prefix "e.g. /members/list" to the query strings "e.g. ?sort=foo", so I
did it manually with string.replace.
Note: When adding the html to a page, you need to run it through
the "safe" filter. E.g. "{{ table|safe }}"
:param table: Table object from members.tables
:param request: HttpRequest
:return: Raw html string
"""
table_as_html = table.as_html(request)
path = request.path
fixed = table_as_html.replace(r'href="?', r'href="{}?'.format(path))
return fixed
@ensure_csrf_cookie
@require_http_methods(["GET"])
@permission_required('members.change_member', login_url='/login')
def settings_page(request, *args, **kwargs):
"""Render member app settings page."""
return render(request, 'settings.html', {})
@ensure_csrf_cookie
@require_http_methods(["POST"])
@permission_required('members.change_member', login_url='/login')
def import_csv(request, *args, **kwargs):
"""Get csv data imported to page and create members based on that."""
try:
data = request.POST['textfield']
payment_source = request.POST['payment_source']
except:
return render(request,
'error.html',
{'error': _('Missing "textfield" POST request field')})
try:
result = MemberForm.csv_to_models(data, payment_source=payment_source)
except CSVValidationError as ex:
logging.exception('Model validation error')
return error_view(request, ex.form_errors)
except Exception as ex:
logging.exception('Other error in CSV import')
return error_view(request, ex)
member_table = MemberTable(result.members,
request=request,
exclude=['id', 'options'],
attrs={'class': 'table table-bordered table-hover'})
member_table_html = convert_table_to_html(member_table, request)
payment_table = PaymentTable(result.payments,
request=request,
exclude=['id', 'options'],
attrs={'class': 'table table-bordered table-hover'})
payment_table_html = convert_table_to_html(payment_table, request)
request.session['models'] = result
context = {
'members': member_table_html,
'payments': payment_table_html
}
return render(request, 'member_add_many_confirm.html', context)
@ensure_csrf_cookie
@require_http_methods(["GET"])
@permission_required('members.change_member', login_url='/login')
def export_csv(request, *args, **kwargs):
"""Export members as csv."""
response = HttpResponse()
response['Content-type'] = 'text/csv'
response['Accept'] = 'text/csv'
response['Content-Disposition'] = 'filename; filename=members.csv'
writer = csv.writer(response, csv.excel)
# BOM (optional...Excel needs it to open UTF-8 file properly)
response.write(u'\ufeff'.encode('utf8'))
for obj in Member.objects.all():
data = obj.as_array()
field_list = map(lambda d: str(d), data)
writer.writerow(field_list)
return response
def send_mail_wrapper(subject, message, email_to):
"""Send mail to default email."""
send_mail(subject,
message,
settings.DEFAULT_EMAIL_FROM,
[email_to],
fail_silently=False)
+2
View File
@@ -26,3 +26,5 @@ django-modeltranslation==0.12.1
django-auditlog==0.4.3
django-phonenumber-field==1.3.0
paho-mqtt==1.3.0
django-autocomplete-light==3.2.10
six==1.10.0
+2
View File
@@ -64,6 +64,8 @@ LOGGING = {
INSTALLED_APPS = [
'modeltranslation', # has to be before admin for translation admin to work
'dal',
'dal_select2',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',