Merge branch 'feature-excel-import-export' into 'develop'

Feature excel import export

See merge request vtmk/web2.0!97
This commit is contained in:
Jan Tuomi
2017-11-02 23:41:36 +02:00
23 changed files with 641 additions and 389 deletions
Binary file not shown.
+231 -174
View File
@@ -3,19 +3,19 @@
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-10-30 13:41+0100\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"
"Language: \n"
"POT-Creation-Date: 2017-11-02 21:59+0100\n"
"PO-Revision-Date: 2017-11-02 23:09+0200\n"
"Language: en\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Last-Translator: \n"
"Language-Team: \n"
"X-Generator: Poedit 2.0.4\n"
#: .\infoscreen\models.py:97
msgid "ABB jobs"
@@ -177,11 +177,11 @@ msgstr "Member"
#: .\members\forms.py:123
msgid "I'm a member of AYY"
msgstr ""
msgstr "I'm a member of AYY"
#: .\members\forms.py:124
msgid "I want to receive a weekly newsletter"
msgstr ""
msgstr "I want to receive a weekly newsletter"
#: .\members\models.py:14
msgid "First name"
@@ -201,7 +201,7 @@ msgid "Place of residence"
msgstr "Place of residence"
#: .\members\models.py:19 .\members\models.py:70
#: .\members\templates\member_add_many.html:35
#: .\members\templates\member_add_many.html:39
msgid "AYY"
msgstr "AYY"
@@ -225,7 +225,7 @@ msgstr "Source"
msgid "Cash"
msgstr "Cash"
#: .\members\models.py:72 .\members\templates\member_add_many.html:36
#: .\members\models.py:72 .\members\templates\member_add_many.html:40
msgid "Bank transfer"
msgstr "Bank transfer"
@@ -235,7 +235,6 @@ msgstr "Created"
#: .\members\models.py:104 .\members\templates\member_add_many_confirm.html:12
#: .\members\templates\members_base.html:52
#: .\webapp\templates\main_index.html:7
msgid "Members"
msgstr "Members"
@@ -248,10 +247,8 @@ msgid "Edit"
msgstr "Edit"
#: .\members\templates\application_delete_confirm.html:9
#, fuzzy
#| msgid "Are you sure you want to delete this payment?"
msgid "Are you sure you want to delete this application?"
msgstr "Are you sure you want to delete this payment?"
msgstr "Are you sure you want to delete this application?"
#: .\members\templates\application_delete_confirm.html:19
#: .\members\templates\member_delete_confirm.html:19
@@ -284,8 +281,9 @@ msgid "Muista myös maksaa jäsenmaksusi!"
msgstr "Don't forget to pay your membership fee!"
#: .\members\templates\application_index.html:16
#: .\members\templates\member_add_many.html:48
#: .\members\templates\member_add_many.html:55
#: .\members\templates\member_add_many_confirm.html:22
#: .\templates\password_reset\recovery_form.html:10
#: .\webapp\templates\kaehmy_list.html:48
msgid "Send"
msgstr "Send"
@@ -294,6 +292,12 @@ msgstr "Send"
msgid "Member applications"
msgstr "Member applications"
#: .\members\templates\application_list.html:20
#: .\members\templates\member_list.html:44
#: .\members\templates\payment_list.html:41
msgid "Download Excel"
msgstr "Download Excel"
#: .\members\templates\application_success.html:8
msgid "Hienoa! Jäsenhakemuksesi on nyt lähetetty."
msgstr "Amazing! Your membership application has been sent."
@@ -316,61 +320,75 @@ msgstr "Save"
#: .\members\templates\member_add_many.html:8
msgid "Add many members"
msgstr ""
msgstr "Add many members"
#: .\members\templates\member_add_many.html:13
msgid ""
"\n"
" Enter member information in CSV format, separate members on "
"separate lines.\n"
"separate lines. \n"
" If a new member already exists in the database, a new payment "
"event will be created for that member instead.\n"
" "
msgstr ""
"\n"
" Enter member information in CSV format, separate members on "
"separate lines. \n"
" If a new member already exists in the database, a new payment "
"event will be created for that member instead.\n"
" "
#: .\members\templates\member_add_many.html:18
#: .\members\templates\member_add_many.html:21
msgid "Format the member table like this:"
msgstr "Format the member table like this:"
#: .\members\templates\member_add_many.html:25
msgid ""
"\n"
" first_name, last_name, email_address and place_of_origin should "
"be given string values.\n"
" ayy_member and jas_recipient should be given the value 0 (off) "
"or 1 (on).\n"
" "
"Columns: First name, last name, email address, place of origin, AYY member, "
"JAS recipient"
msgstr ""
"\n"
" first_name, last_name, email_address and place_of_origin should "
"be given string values.\n"
" ayy_member and jas_recipient should be given the value 0 (off) "
"or 1 (on).\n"
" "
"Columns: First name, last name, email address, place of origin, AYY member, "
"JAS recipient"
#: .\members\templates\member_add_many.html:23
msgid "Syntax"
msgstr "Syntax"
#: .\members\templates\member_add_many.html:29
msgid "Data"
msgstr ""
#: .\members\templates\member_add_many.html:28
msgid "Save the file as CSV"
msgstr "Save the file as CSV"
#: .\members\templates\member_add_many.html:33
msgid "Payment source"
msgstr ""
msgid "Upload file"
msgstr "Upload file"
#: .\members\templates\member_add_many.html:37
msgid "Cash payment"
msgstr ""
msgid "Payment source"
msgstr "Payment source"
#: .\members\templates\member_add_many.html:41
msgid "CSV delimiter"
msgstr ""
msgid "Cash payment"
msgstr "Cash payment"
#: .\members\templates\member_add_many.html:44
msgid ""
"This payment source will be used to create any payments for new members that "
"already exist in the database."
msgstr ""
"This payment source will be used to create any payments for new members that "
"already exist in the database."
#: .\members\templates\member_add_many.html:48
msgid "CSV delimiter"
msgstr "CSV delimiter"
#: .\members\templates\member_add_many.html:51
msgid ""
"The symbol that is used to separate items in one line. Defaults to "
"';' (semicolon)."
msgstr ""
"The symbol that is used to separate items in one line. Defaults to "
"';' (semicolon)."
#: .\members\templates\member_add_many_confirm.html:8
msgid "Confirm adding these entries?"
msgstr ""
msgstr "Confirm adding these entries?"
#: .\members\templates\member_add_many_confirm.html:16
#: .\members\templates\members_base.html:60
@@ -392,21 +410,17 @@ msgstr "Member register"
#: .\members\templates\member_list.html:21
msgid "Members in register:"
msgstr ""
msgstr "Members in register:"
#: .\members\templates\member_list.html:28
#: .\members\templates\payment_list.html:25
msgid "Search"
msgstr ""
msgstr "Search"
#: .\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"
msgstr "Showing results for"
#: .\members\templates\members_base.html:33
#: .\members\templates\members_base.html:42
@@ -460,20 +474,18 @@ msgid "Payment events"
msgstr "Payment events"
#: .\members\templates\payment_list.html:18
#, fuzzy
#| msgid "Member register"
msgid "Payments in register:"
msgstr "Member register"
msgstr "Payments in register:"
#: .\members\templates\settings.html:17
msgid "Language"
msgstr "Language"
#: .\members\templates\settings.html:20 .\sikweb\base.py:222
#: .\members\templates\settings.html:20 .\sikweb\base.py:226
msgid "Finnish"
msgstr "Finnish"
#: .\members\templates\settings.html:21 .\sikweb\base.py:223
#: .\members\templates\settings.html:21 .\sikweb\base.py:227
msgid "English"
msgstr "English"
@@ -481,92 +493,98 @@ msgstr "English"
msgid "Submit"
msgstr "Submit"
#: .\members\views\applications.py:51 .\members\views\applications.py:119
#: .\members\views\applications.py:148
#: .\members\views\applications.py:51 .\members\views\applications.py:112
#: .\members\views\applications.py:137
msgid "No application id specified"
msgstr "No application id specified"
#: .\members\views\applications.py:73
#: .\members\views\applications.py:71
msgid "Application missing 'id' field."
msgstr ""
msgstr "Application missing 'id' field."
#: .\members\views\applications.py:83
#: .\members\views\applications.py:80
msgid "Email {} is already in use by a member. Application cannot be accepted."
msgstr ""
"Email {} is already in use by a member. Application cannot be accepted."
#: .\members\views\applications.py:93
#: .\members\views\applications.py:91
msgid "Successfully accepted application"
msgstr "Successfully accepted application"
#: .\members\views\applications.py:123
#: .\members\views\applications.py:116
msgid "Successfully deleted application"
msgstr "Successfully deleted application"
#: .\members\views\applications.py:135
#: .\members\views\applications.py:126
msgid "Could not delete application object"
msgstr "Could not delete application object"
#: .\members\views\members.py:74 .\members\views\members.py:179
#: .\members\views\members.py:206
#: .\members\views\members.py:73 .\members\views\members.py:175
#: .\members\views\members.py:199
msgid "No member id specified"
msgstr "No member id specified"
#: .\members\views\members.py:111
#: .\members\views\members.py:114
msgid "Failed to import members"
msgstr "Failed to import members"
#: .\members\views\members.py:125
#: .\members\views\members.py:128
msgid "Successfully added member"
msgstr "Successfully added member"
#: .\members\views\members.py:148
#: .\members\views\members.py:149
msgid "Member missing 'id' field."
msgstr ""
msgstr "Member missing 'id' field."
#: .\members\views\members.py:158
msgid "Successfully updated member"
msgstr "Successfully updated member"
#: .\members\views\members.py:183
#: .\members\views\members.py:179
msgid "Successfully deleted member"
msgstr "Successfully deleted member"
#: .\members\views\members.py:194
#: .\members\views\members.py:188
msgid "Could not delete member object"
msgstr "Could not delete member object"
#: .\members\views\payments.py:70
#: .\members\views\payments.py:71
msgid "Successfully added payment for member"
msgstr "Successfully added payment for member"
#: .\members\views\payments.py:89 .\members\views\payments.py:108
#: .\members\views\payments.py:128
#: .\members\views\payments.py:88 .\members\views\payments.py:105
#: .\members\views\payments.py:123
msgid "No payment id specified"
msgstr "No payment id specified"
#: .\members\views\payments.py:133
#: .\members\views\payments.py:128
msgid "Successfully deleted payment"
msgstr "Successfully deleted payment"
#: .\members\views\payments.py:143
#: .\members\views\payments.py:136
msgid "Could not delete payment object"
msgstr "Could not delete payment object"
#: .\members\views\payments.py:163
#: .\members\views\payments.py:156
msgid "Successfully updated payment"
msgstr "Successfully updated payment"
#: .\members\views\payments.py:170
#: .\members\views\payments.py:161
msgid "Could not update payment object"
msgstr "Could not update payment object"
#: .\members\views\utils.py:117
msgid "Missing \"textfield\" POST request field"
msgstr "Missing \"textfield\" POST request field"
#: .\members\views\utils.py:119
msgid "Missing CSV file"
msgstr "Missing CSV file"
#: .\templates\admin\base_site.html:43
msgid "Go"
msgstr ""
msgstr "Go"
#: .\templates\base.html:14 .\webapp\templates\kaehmy_base.html:14
#: .\webapp\templates\main_index.html:9
msgid "Aalto-yliopiston Sähköinsinöörikilta ry"
msgstr "Aalto-yliopiston Sähköinsinöörikilta ry"
#: .\templates\error.html:8 .\webapp\templates\kaehmy_error.html:8
msgid "Error"
@@ -576,53 +594,117 @@ msgstr "Error"
msgid "Back"
msgstr "Back"
#: .\templates\footer.html:23
#: .\templates\footer.html:10 .\templates\footer.html:60
#: .\webapp\templates\kaehmy_footer.html:23
msgid "Copyright Aalto-yliopiston Sähköinsinöörikilta ry"
msgstr "Copyright Aalto-yliopiston Sähköinsinöörikilta ry"
#: .\templates\login.html:12 .\templates\login.html:13
msgid "Username"
msgstr "Username"
#: .\templates\login.html:16 .\templates\login.html:17
msgid "Password"
msgstr "Password"
#: .\templates\login.html:20
msgid "Forgot password?"
msgstr "Forgot password?"
#: .\templates\login.html:26
msgid "Log in"
msgstr "Log in"
#: .\templates\password_reset\recovery_done.html:3
msgid "New password set"
msgstr "New password set"
#: .\templates\password_reset\recovery_done.html:6
msgid ""
"Your password has successfully been reset. You can use it right now on the "
"login page."
msgstr ""
"Your password has successfully been reset. You can use it right now on the "
"login page."
#: .\templates\password_reset\recovery_form.html:4
msgid "Password recovery"
msgstr "Password recovery"
#: .\templates\password_reset\reset.html:5
#, python-format
msgid ""
"Sorry, this password reset link is invalid. You can still <a href="
"\"%(recovery_url)s\">request a new one</a>."
msgstr ""
"Sorry, this password reset link is invalid. You can still <a href="
"\"%(recovery_url)s\">request a new one</a>."
#: .\templates\password_reset\reset.html:7
#, python-format
msgid "Hi, <strong>%(username)s</strong>. Please choose your new password."
msgstr "Hi, <strong>%(username)s</strong>. Please choose your new password."
#: .\templates\password_reset\reset.html:11
msgid "Set new password"
msgstr "Set new password"
#: .\templates\password_reset\reset_sent.html:4
msgid "Password recovery sent"
msgstr "Password recovery sent"
#: .\templates\password_reset\reset_sent.html:7
#, python-format
msgid ""
"An email was sent to <strong>%(email)s</strong> %(ago)s ago. Use the link in "
"it to set a new password."
msgstr ""
"An email was sent to <strong>%(email)s</strong> %(ago)s ago. Use the link in "
"it to set a new password."
#: .\webapp\forms.py:38
msgid "Email (not public)"
msgstr ""
msgstr "Email (not public)"
#: .\webapp\forms.py:39
msgid "Phone number (not public)"
msgstr ""
msgstr "Phone number (not public)"
#: .\webapp\forms.py:44
msgid "Custom roles"
msgstr ""
msgstr "Custom roles"
#: .\webapp\forms.py:52 .\webapp\templates\kaehmy.html:41
msgid "Preset roles"
msgstr ""
msgstr "Preset roles"
#: .\webapp\forms.py:76
msgid "Invalid phone number"
msgstr ""
msgstr "Invalid phone number"
#: .\webapp\forms.py:84
msgid "Custom role with the same name already exists."
msgstr ""
msgstr "Custom role with the same name already exists."
#: .\webapp\models.py:17
msgid "Webapp"
msgstr ""
msgstr "Webapp"
#: .\webapp\models.py:28
msgid "Tag"
msgstr ""
msgstr "Tag"
#: .\webapp\models.py:29
msgid "Tags"
msgstr ""
msgstr "Tags"
#: .\webapp\models.py:32
msgid "Tag: {}"
msgstr ""
msgstr "Tag: {}"
#: .\webapp\models.py:52
msgid "Feed: {}"
msgstr ""
msgstr "Feed: {}"
#: .\webapp\models.py:55
msgid "Feed"
@@ -656,13 +738,13 @@ msgstr ""
msgid "Corporate affairs"
msgstr ""
#: .\webapp\models.py:95 .\webapp\templates\freshmen.html:7
#: .\webapp\templates\navigation.html:10
#: .\webapp\models.py:95 .\webapp\templates\freshmen.html:10
#: .\webapp\templates\navigation.html:9
msgid "Freshmen"
msgstr "Freshmen"
#: .\webapp\models.py:96 .\webapp\templates\international.html:7
#: .\webapp\templates\navigation.html:16
#: .\webapp\models.py:96 .\webapp\templates\international.html:10
#: .\webapp\templates\navigation.html:15
msgid "International"
msgstr "International"
@@ -699,66 +781,48 @@ msgid "Studies"
msgstr ""
#: .\webapp\models.py:105
#, fuzzy
#| msgid "Sössö articles"
msgid "Sössö magazine"
msgstr "Sössö articles"
msgstr "Sössö magazine"
#: .\webapp\models.py:106
#, fuzzy
#| msgid "Applications"
msgid "Alumni relations"
msgstr "Applications"
msgstr "Alumni relations"
#: .\webapp\models.py:107
msgid "Others"
msgstr ""
#: .\webapp\models.py:111 .\webapp\models.py:191
#, fuzzy
#| msgid "Add member"
msgid "Board member"
msgstr "Add member"
msgstr "Board member"
#: .\webapp\models.py:112
msgid "Category"
msgstr ""
#: .\webapp\models.py:116
#, fuzzy
#| msgid "Add member"
msgid "board member"
msgstr "Add member"
msgstr "board member"
#: .\webapp\models.py:122
#, fuzzy
#| msgid "Duration"
msgid "Description"
msgstr "Duration"
msgstr "Description"
#: .\webapp\models.py:129
#, fuzzy
#| msgid "Total challenges:"
msgid "Preset kaehmy role"
msgstr "Total challenges:"
msgstr "Preset kaehmy role"
#: .\webapp\models.py:130
#, fuzzy
#| msgid "Total challenges:"
msgid "Preset kaehmy roles"
msgstr "Total challenges:"
msgstr "Preset kaehmy roles"
#: .\webapp\models.py:137
#, fuzzy
#| msgid "Total challenges:"
msgid "Custom kaehmy role"
msgstr "Total challenges:"
msgstr "Custom kaehmy role"
#: .\webapp\models.py:138
#, fuzzy
#| msgid "Total challenges:"
msgid "Custom kaehmy roles"
msgstr "Total challenges:"
msgstr "Custom kaehmy roles"
#: .\webapp\models.py:145
msgid "Timestamp"
@@ -801,10 +865,8 @@ msgid "Custom role name"
msgstr ""
#: .\webapp\models.py:199
#, fuzzy
#| msgid "Member applications"
msgid "Kaehmy application: {}"
msgstr "Member applications"
msgstr "Kaehmy application: {}"
#: .\webapp\models.py:221
msgid "Board: {}"
@@ -850,24 +912,20 @@ msgstr ""
msgid "SIK Admin"
msgstr "SIK Admin"
#: .\webapp\templates\base.html:15
msgid "Aalto-yliopiston Sähköinsinöörikilta ry"
msgstr "Aalto-yliopiston Sähköinsinöörikilta ry"
#: .\webapp\templates\contact.html:7 .\webapp\templates\navigation.html:22
#: .\webapp\templates\contact.html:10 .\webapp\templates\navigation.html:21
msgid "Contact"
msgstr "Contact"
#: .\webapp\templates\event_calendar.html:7
#: .\webapp\templates\navigation.html:13
#: .\webapp\templates\event_calendar.html:10
#: .\webapp\templates\navigation.html:12
msgid "Event calendar"
msgstr "Event calendar"
#: .\webapp\templates\guild.html:7
#: .\webapp\templates\guild.html:10
msgid "Kilta"
msgstr "Guild"
#: .\webapp\templates\jobs.html:7 .\webapp\templates\navigation.html:29
#: .\webapp\templates\jobs.html:10 .\webapp\templates\navigation.html:28
msgid "Jobs"
msgstr "Jobs"
@@ -929,22 +987,16 @@ msgid "Vaalikokous, osa 3 (toimarien valinta)"
msgstr "Election meeting, part 3 (non-board election)"
#: .\webapp\templates\kaehmy_export.html:9
#, fuzzy
#| msgid "Applications"
msgid "All applications"
msgstr "Applications"
msgstr "All applications"
#: .\webapp\templates\kaehmy_export.html:13
#, fuzzy
#| msgid "Applications"
msgid "Board applications"
msgstr "Applications"
msgstr "Board applications"
#: .\webapp\templates\kaehmy_export.html:18
#, fuzzy
#| msgid "Member applications"
msgid "Non-board applications"
msgstr "Member applications"
msgstr "Non-board applications"
#: .\webapp\templates\kaehmy_export.html:23
msgid "Front page"
@@ -964,10 +1016,8 @@ msgid "Comment"
msgstr ""
#: .\webapp\templates\kaehmy_list.html:57
#, fuzzy
#| msgid "List kaehmys"
msgid "Filter kaehmys"
msgstr "List applications"
msgstr "Filter kaehmys"
#: .\webapp\templates\kaehmy_list.html:70
#: .\webapp\templates\kaehmy_statistics.html:18
@@ -999,35 +1049,15 @@ msgstr "New application"
msgid "Statistics"
msgstr ""
#: .\webapp\templates\login.html:25 .\webapp\templates\login.html:27
msgid "Username"
msgstr "Username"
#: .\webapp\templates\login.html:31 .\webapp\templates\login.html:33
msgid "Password"
msgstr "Password"
#: .\webapp\templates\login.html:43
msgid "Log in"
msgstr "Log in"
#: .\webapp\templates\main_index.html:8
msgid "Infoscreen"
msgstr "Infoscreen"
#: .\webapp\templates\main_index.html:9
msgid "Admin tools"
msgstr "Admin tools"
#: .\webapp\templates\navigation.html:7
#: .\webapp\templates\navigation.html:6
msgid "Guild"
msgstr "Guild"
#: .\webapp\templates\navigation.html:19 .\webapp\templates\sosso.html:7
#: .\webapp\templates\navigation.html:18 .\webapp\templates\sosso.html:10
msgid "Sössö"
msgstr "Sössö"
#: .\webapp\templates\navigation.html:26
#: .\webapp\templates\navigation.html:25
msgid "Corporate"
msgstr "Corporate"
@@ -1047,5 +1077,32 @@ msgstr "All challenges"
msgid "Total challenges:"
msgstr "Total challenges:"
#~ msgid "Infoscreen"
#~ msgstr "Infoscreen"
#~ msgid "Admin tools"
#~ msgstr "Admin tools"
#~ msgid ""
#~ "\n"
#~ " first_name, last_name, email_address and place_of_origin "
#~ "should be given string values.\n"
#~ " ayy_member and jas_recipient should be given the value 0 "
#~ "(off) or 1 (on).\n"
#~ " "
#~ msgstr ""
#~ "\n"
#~ " first_name, last_name, email_address and place_of_origin "
#~ "should be given string values.\n"
#~ " ayy_member and jas_recipient should be given the value 0 "
#~ "(off) or 1 (on).\n"
#~ " "
#~ msgid "Syntax"
#~ msgstr "Syntax"
#~ msgid "Missing \"textfield\" POST request field"
#~ msgstr "Missing \"textfield\" POST request field"
#~ msgid "Options"
#~ msgstr "Options"
Binary file not shown.
+180 -97
View File
@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-10-30 13:41+0100\n"
"PO-Revision-Date: 2017-10-30 14:42+0200\n"
"POT-Creation-Date: 2017-11-02 21:59+0100\n"
"PO-Revision-Date: 2017-11-02 23:04+0200\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: fi\n"
@@ -202,7 +202,7 @@ msgid "Place of residence"
msgstr "Asuinpaikka"
#: .\members\models.py:19 .\members\models.py:70
#: .\members\templates\member_add_many.html:35
#: .\members\templates\member_add_many.html:39
msgid "AYY"
msgstr "AYY"
@@ -226,7 +226,7 @@ msgstr "Lähde"
msgid "Cash"
msgstr "Käteinen"
#: .\members\models.py:72 .\members\templates\member_add_many.html:36
#: .\members\models.py:72 .\members\templates\member_add_many.html:40
msgid "Bank transfer"
msgstr "Tilisiirto"
@@ -236,7 +236,6 @@ msgstr "Lisätty"
#: .\members\models.py:104 .\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"
@@ -283,8 +282,9 @@ msgid "Muista myös maksaa jäsenmaksusi!"
msgstr "Muista myös maksaa jäsenmaksusi!"
#: .\members\templates\application_index.html:16
#: .\members\templates\member_add_many.html:48
#: .\members\templates\member_add_many.html:55
#: .\members\templates\member_add_many_confirm.html:22
#: .\templates\password_reset\recovery_form.html:10
#: .\webapp\templates\kaehmy_list.html:48
msgid "Send"
msgstr "Lähetä"
@@ -293,6 +293,12 @@ msgstr "Lähetä"
msgid "Member applications"
msgstr "Jäsenhakemukset"
#: .\members\templates\application_list.html:20
#: .\members\templates\member_list.html:44
#: .\members\templates\payment_list.html:41
msgid "Download Excel"
msgstr "Lataa Excel"
#: .\members\templates\application_success.html:8
msgid "Hienoa! Jäsenhakemuksesi on nyt lähetetty."
msgstr "Hienoa! Jäsenhakemuksesi on nyt lähetetty."
@@ -321,50 +327,58 @@ msgstr "Lisää useita"
msgid ""
"\n"
" Enter member information in CSV format, separate members on "
"separate lines.\n"
"separate lines. \n"
" If a new member already exists in the database, a new payment "
"event will be created for that member instead.\n"
" "
msgstr ""
"\n"
" Syötä jäsentiedot CSV-formaatissa, erilliset jäsenet omilla "
"riveillään.\n"
" "
" Jos jäsenen tiedot ovat jo tietokannassa, tehdään tälle "
"jäsenelle uusi maksutapahtuma."
#: .\members\templates\member_add_many.html:18
#: .\members\templates\member_add_many.html:21
msgid "Format the member table like this:"
msgstr "Jäsentele taulukko seuraavasti:"
#: .\members\templates\member_add_many.html:25
msgid ""
"\n"
" first_name, last_name, email_address and place_of_origin should "
"be given string values.\n"
" ayy_member and jas_recipient should be given the value 0 (off) "
"or 1 (on).\n"
" "
"Columns: First name, last name, email address, place of origin, AYY member, "
"JAS recipient"
msgstr ""
"\n"
" first_name, last_name, email_address ja place_of_origin ovat "
"merkkijonoja.\n"
" ayy_member ja jas_recipient ovat joko 0 (off) tai 1 (on).\n"
" "
"Kolumnit: Etunimi, sukunimi, sähköpostiosoite, asuinpaikka, AYY:n jäsen, "
"jäsenmailin vastaanottaja"
#: .\members\templates\member_add_many.html:23
msgid "Syntax"
msgstr "Syntaksi"
#: .\members\templates\member_add_many.html:29
msgid "Data"
msgstr "Data"
#: .\members\templates\member_add_many.html:28
msgid "Save the file as CSV"
msgstr "Tallenna tiedosto CSV-formaatissa"
#: .\members\templates\member_add_many.html:33
msgid "Upload file"
msgstr "Lataa tiedosto"
#: .\members\templates\member_add_many.html:37
msgid "Payment source"
msgstr "Maksutapa"
#: .\members\templates\member_add_many.html:37
#: .\members\templates\member_add_many.html:41
msgid "Cash payment"
msgstr "Käteismaksu"
#: .\members\templates\member_add_many.html:41
#: .\members\templates\member_add_many.html:44
msgid ""
"This payment source will be used to create any payments for new members that "
"already exist in the database."
msgstr ""
"Tätä maksutapaa käytetään, kun jo tietokannassa oleville jäsenille luodaan "
"maksutapahtuma."
#: .\members\templates\member_add_many.html:48
msgid "CSV delimiter"
msgstr "CSV-erotin"
#: .\members\templates\member_add_many.html:44
#: .\members\templates\member_add_many.html:51
msgid ""
"The symbol that is used to separate items in one line. Defaults to "
"';' (semicolon)."
@@ -407,10 +421,6 @@ msgstr "Hae"
msgid "Showing results for"
msgstr "Näytetään tulokset haulle"
#: .\members\templates\member_list.html:44
msgid "Download CSV"
msgstr "Lataa CSV"
#: .\members\templates\members_base.html:33
#: .\members\templates\members_base.html:42
msgid "Member register of SIK ry"
@@ -470,11 +480,11 @@ msgstr "Maksutapahtumia:"
msgid "Language"
msgstr "Kieli"
#: .\members\templates\settings.html:20 .\sikweb\base.py:222
#: .\members\templates\settings.html:20 .\sikweb\base.py:226
msgid "Finnish"
msgstr "suomi"
#: .\members\templates\settings.html:21 .\sikweb\base.py:223
#: .\members\templates\settings.html:21 .\sikweb\base.py:227
msgid "English"
msgstr "englanti"
@@ -482,45 +492,45 @@ msgstr "englanti"
msgid "Submit"
msgstr "Lisää"
#: .\members\views\applications.py:51 .\members\views\applications.py:119
#: .\members\views\applications.py:148
#: .\members\views\applications.py:51 .\members\views\applications.py:112
#: .\members\views\applications.py:137
msgid "No application id specified"
msgstr "Hakemuksen ID ei määritelty"
#: .\members\views\applications.py:73
#: .\members\views\applications.py:71
msgid "Application missing 'id' field."
msgstr "Hakemuksen ID ei määritelty."
#: .\members\views\applications.py:83
#: .\members\views\applications.py:80
msgid "Email {} is already in use by a member. Application cannot be accepted."
msgstr "Sähköpostiosoite {} on jo käytössä. Hakemusta ei hyväksytty."
#: .\members\views\applications.py:93
#: .\members\views\applications.py:91
msgid "Successfully accepted application"
msgstr "Onnistuneesti hyväksyttiin hakemus"
#: .\members\views\applications.py:123
#: .\members\views\applications.py:116
msgid "Successfully deleted application"
msgstr "Onnistuneesti poistettiin hakemus"
#: .\members\views\applications.py:135
#: .\members\views\applications.py:126
msgid "Could not delete application object"
msgstr "Hakemusobjektia ei voitu poistaa"
#: .\members\views\members.py:74 .\members\views\members.py:179
#: .\members\views\members.py:206
#: .\members\views\members.py:73 .\members\views\members.py:175
#: .\members\views\members.py:199
msgid "No member id specified"
msgstr "Jäsenen ID ei määritelty"
#: .\members\views\members.py:111
#: .\members\views\members.py:114
msgid "Failed to import members"
msgstr "Jäsenten tuonti epäonnistui"
#: .\members\views\members.py:125
#: .\members\views\members.py:128
msgid "Successfully added member"
msgstr "Onnistuneesti lisättiin jäsen"
#: .\members\views\members.py:148
#: .\members\views\members.py:149
msgid "Member missing 'id' field."
msgstr "Jäsenen ID ei määritelty."
@@ -528,47 +538,52 @@ msgstr "Jäsenen ID ei määritelty."
msgid "Successfully updated member"
msgstr "Onnistuneesti päivitettiin jäsen"
#: .\members\views\members.py:183
#: .\members\views\members.py:179
msgid "Successfully deleted member"
msgstr "Onnistuneesti poistettiin jäsen"
#: .\members\views\members.py:194
#: .\members\views\members.py:188
msgid "Could not delete member object"
msgstr "Jäsenobjektia ei voitu poistaa"
#: .\members\views\payments.py:70
#: .\members\views\payments.py:71
msgid "Successfully added payment for member"
msgstr "Onnistuneesti lisättiin maksutapahtuma jäsenelle"
#: .\members\views\payments.py:89 .\members\views\payments.py:108
#: .\members\views\payments.py:128
#: .\members\views\payments.py:88 .\members\views\payments.py:105
#: .\members\views\payments.py:123
msgid "No payment id specified"
msgstr "Maksutapahtuman ID ei määritelty"
#: .\members\views\payments.py:133
#: .\members\views\payments.py:128
msgid "Successfully deleted payment"
msgstr "Onnistuneesti poistettiin maksutapahtuma"
#: .\members\views\payments.py:143
#: .\members\views\payments.py:136
msgid "Could not delete payment object"
msgstr "Maksutapahtumaobjektia ei voitu poistaa"
#: .\members\views\payments.py:163
#: .\members\views\payments.py:156
msgid "Successfully updated payment"
msgstr "Onnistuneesti päivitettiin maksutapahtuma"
#: .\members\views\payments.py:170
#: .\members\views\payments.py:161
msgid "Could not update payment object"
msgstr "Maksutapahtumaobjektia ei voitu päivittää"
#: .\members\views\utils.py:117
msgid "Missing \"textfield\" POST request field"
msgstr "Puuttuva \"textfield\" POST-kenttä"
#: .\members\views\utils.py:119
msgid "Missing CSV file"
msgstr "Puuttuva CSV-tiedosto"
#: .\templates\admin\base_site.html:43
msgid "Go"
msgstr "Vaihda"
#: .\templates\base.html:14 .\webapp\templates\kaehmy_base.html:14
#: .\webapp\templates\main_index.html:9
msgid "Aalto-yliopiston Sähköinsinöörikilta ry"
msgstr "Aalto-yliopiston Sähköinsinöörikilta ry"
#: .\templates\error.html:8 .\webapp\templates\kaehmy_error.html:8
msgid "Error"
msgstr "Virhe"
@@ -577,10 +592,73 @@ msgstr "Virhe"
msgid "Back"
msgstr "Takaisin"
#: .\templates\footer.html:23
#: .\templates\footer.html:10 .\templates\footer.html:60
#: .\webapp\templates\kaehmy_footer.html:23
msgid "Copyright Aalto-yliopiston Sähköinsinöörikilta ry"
msgstr "Copyright Aalto-yliopiston Sähköinsinöörikilta ry"
#: .\templates\login.html:12 .\templates\login.html:13
msgid "Username"
msgstr "Käyttäjänimi"
#: .\templates\login.html:16 .\templates\login.html:17
msgid "Password"
msgstr "Salasana"
#: .\templates\login.html:20
msgid "Forgot password?"
msgstr "Unohditko salasanasi?"
#: .\templates\login.html:26
msgid "Log in"
msgstr "Kirjaudu sisään"
#: .\templates\password_reset\recovery_done.html:3
msgid "New password set"
msgstr "Uusi salasana asetettu"
#: .\templates\password_reset\recovery_done.html:6
msgid ""
"Your password has successfully been reset. You can use it right now on the "
"login page."
msgstr ""
"Salasanasi on asetettu onnistuneesti. Voit käyttää sitä nyt kirjautuessasi."
#: .\templates\password_reset\recovery_form.html:4
msgid "Password recovery"
msgstr "Salasanan palautus"
#: .\templates\password_reset\reset.html:5
#, python-format
msgid ""
"Sorry, this password reset link is invalid. You can still <a href="
"\"%(recovery_url)s\">request a new one</a>."
msgstr ""
"Pahoittelut, tämä salasanan palautuslinkki on epäkelpo. Voit kuitenkin <a "
"href=\"%(recovery_url)s\">hankkia uuden</a>."
#: .\templates\password_reset\reset.html:7
#, python-format
msgid "Hi, <strong>%(username)s</strong>. Please choose your new password."
msgstr "Hei, <strong>%(username)s</strong>. Valitse uusi salasanasi."
#: .\templates\password_reset\reset.html:11
msgid "Set new password"
msgstr "Aseta uusi salasana"
#: .\templates\password_reset\reset_sent.html:4
msgid "Password recovery sent"
msgstr "Salasanan palautusviesti lähetetty"
#: .\templates\password_reset\reset_sent.html:7
#, python-format
msgid ""
"An email was sent to <strong>%(email)s</strong> %(ago)s ago. Use the link in "
"it to set a new password."
msgstr ""
"Sähköposti on lähetetty osoitteeseen <strong>%(email)s</strong> %(ago)s:a "
"sitten. Käytä linkkiä asettaaksesi uuden salasanan."
#: .\webapp\forms.py:38
msgid "Email (not public)"
msgstr "Sähköposti (ei julkinen)"
@@ -657,13 +735,13 @@ msgstr "Ilmoittautumiset"
msgid "Corporate affairs"
msgstr "Yrityssuhteet"
#: .\webapp\models.py:95 .\webapp\templates\freshmen.html:7
#: .\webapp\templates\navigation.html:10
#: .\webapp\models.py:95 .\webapp\templates\freshmen.html:10
#: .\webapp\templates\navigation.html:9
msgid "Freshmen"
msgstr "Fuksit"
#: .\webapp\models.py:96 .\webapp\templates\international.html:7
#: .\webapp\templates\navigation.html:16
#: .\webapp\models.py:96 .\webapp\templates\international.html:10
#: .\webapp\templates\navigation.html:15
msgid "International"
msgstr "International"
@@ -831,24 +909,20 @@ msgstr "Telegram-kanavat"
msgid "SIK Admin"
msgstr "SIK Hallintapaneeli"
#: .\webapp\templates\base.html:15
msgid "Aalto-yliopiston Sähköinsinöörikilta ry"
msgstr "Aalto-yliopiston Sähköinsinöörikilta ry"
#: .\webapp\templates\contact.html:7 .\webapp\templates\navigation.html:22
#: .\webapp\templates\contact.html:10 .\webapp\templates\navigation.html:21
msgid "Contact"
msgstr "Yhteystiedot"
#: .\webapp\templates\event_calendar.html:7
#: .\webapp\templates\navigation.html:13
#: .\webapp\templates\event_calendar.html:10
#: .\webapp\templates\navigation.html:12
msgid "Event calendar"
msgstr "Tapahtumakalenteri"
#: .\webapp\templates\guild.html:7
#: .\webapp\templates\guild.html:10
msgid "Kilta"
msgstr "Kilta"
#: .\webapp\templates\jobs.html:7 .\webapp\templates\navigation.html:29
#: .\webapp\templates\jobs.html:10 .\webapp\templates\navigation.html:28
msgid "Jobs"
msgstr "Työpaikat"
@@ -975,35 +1049,15 @@ msgstr "Uusi kaehmy"
msgid "Statistics"
msgstr "Kaehmytilastot"
#: .\webapp\templates\login.html:25 .\webapp\templates\login.html:27
msgid "Username"
msgstr "Käyttäjänimi"
#: .\webapp\templates\login.html:31 .\webapp\templates\login.html:33
msgid "Password"
msgstr "Salasana"
#: .\webapp\templates\login.html:43
msgid "Log in"
msgstr "Kirjaudu sisään"
#: .\webapp\templates\main_index.html:8
msgid "Infoscreen"
msgstr "Infonäyttö"
#: .\webapp\templates\main_index.html:9
msgid "Admin tools"
msgstr "Hallintatyökalut"
#: .\webapp\templates\navigation.html:7
#: .\webapp\templates\navigation.html:6
msgid "Guild"
msgstr "Kilta"
#: .\webapp\templates\navigation.html:19 .\webapp\templates\sosso.html:7
#: .\webapp\templates\navigation.html:18 .\webapp\templates\sosso.html:10
msgid "Sössö"
msgstr "Sössö"
#: .\webapp\templates\navigation.html:26
#: .\webapp\templates\navigation.html:25
msgid "Corporate"
msgstr "Yritys"
@@ -1023,5 +1077,34 @@ msgstr "Kaikki haasteet"
msgid "Total challenges:"
msgstr "Haasteita yhteensä:"
#~ msgid "Infoscreen"
#~ msgstr "Infonäyttö"
#~ msgid "Admin tools"
#~ msgstr "Hallintatyökalut"
#~ msgid ""
#~ "\n"
#~ " first_name, last_name, email_address and place_of_origin "
#~ "should be given string values.\n"
#~ " ayy_member and jas_recipient should be given the value 0 "
#~ "(off) or 1 (on).\n"
#~ " "
#~ msgstr ""
#~ "\n"
#~ " first_name, last_name, email_address ja place_of_origin ovat "
#~ "merkkijonoja.\n"
#~ " ayy_member ja jas_recipient ovat joko 0 (off) tai 1 (on).\n"
#~ " "
#~ msgid "Syntax"
#~ msgstr "Syntaksi"
#~ msgid "Data"
#~ msgstr "Data"
#~ msgid "Missing \"textfield\" POST request field"
#~ msgstr "Puuttuva \"textfield\" POST-kenttä"
#~ msgid "Applied for board"
#~ msgstr "Hakenut hallitukseen"
+5 -1
View File
@@ -46,7 +46,7 @@ class MemberForm(forms.ModelForm):
@staticmethod
def csv_to_models(data, payment_source='AYY', delimiter=','):
clean_data = data.strip().split('\n')
clean_data = [row.rstrip(',') for row in clean_data]
clean_data = [row.rstrip(',').rstrip('\r').strip() for row in clean_data]
csv_reader = csv.DictReader(clean_data, fieldnames=MemberForm.Meta.fields, delimiter=delimiter, quoting=csv.QUOTE_NONE)
members = []
@@ -122,3 +122,7 @@ class ApplicationForm(forms.ModelForm):
self.fields['AYY'].label = _("I'm a member of AYY")
self.fields['jas'].label = _("I want to receive a weekly newsletter")
class UploadFileForm(forms.Form):
file = forms.FileField()
+26
View File
@@ -0,0 +1,26 @@
from import_export import resources
from .models import Member, Payment, Request
class MemberResource(resources.ModelResource):
class Meta:
model = Member
exclude = ['id', 'created']
class PaymentResource(resources.ModelResource):
member = resources.Field()
class Meta:
model = Payment
exclude = ['id']
def dehydrate_member(self, payment):
return '{} {}'.format(payment.member.first_name, payment.member.last_name)
class ApplicationResource(resources.ModelResource):
class Meta:
model = Request
exclude = ['id']
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

+4
View File
@@ -15,6 +15,10 @@
{% endif %}
{{ table|safe }}
<div>
<a href="/members/export_applications" class="btn btn-info">{% trans "Download Excel" %}</a>
</div>
</div>
{% endblock content %}
+24 -17
View File
@@ -1,7 +1,7 @@
{% extends "members_base.html" %}
{% load i18n %}
{% load static %}
{% block content %}
<div>
<div>
@@ -11,24 +11,28 @@
<div>
<p>
{% blocktrans %}
Enter member information in CSV format, separate members on separate lines.
Enter member information in CSV format, separate members on separate lines.
If a new member already exists in the database, a new payment event will be created for that member instead.
{% endblocktrans %}
</p>
<p>
{% blocktrans %}
first_name, last_name, email_address and place_of_origin should be given string values.
ayy_member and jas_recipient should be given the value 0 (off) or 1 (on).
{% endblocktrans %}
</p>
<h4>{% trans "Syntax" %}</h4>
<pre>first_name, last_name, email_address, place_of_origin, ayy_member, jas_recipient</pre>
</div>
<form name="memberTextForm" action="/members/import_csv" method="POST">{% csrf_token %}
<div class="form-group">
<label>{% trans "Data" %}</label>
<textarea name="textfield" class="form-control large-textarea" placeholder="Teemu, Teekkari, teemu.teekkari@notmail.dom, Otaniemi, 0, 0"></textarea>
<div>
<label>{% trans "Format the member table like this:" %}</label>
<div>
<img src="{% static "img/excel_csv_save_example.png" %}">
</div>
<p>{% blocktrans %}Columns: First name, last name, email address, place of origin, AYY member, JAS recipient{% endblocktrans %}</p>
</div>
<div>
<label>{% trans "Save the file as CSV" %}</label>
<div><img src="{% static "img/excel_csv_save_tutorial.png" %}"></div>
</div>
<form name="memberTextForm" action="/members/import_csv" enctype="multipart/form-data" method="POST">{% csrf_token %}
<h3>{% trans "Upload file" %}</h3>
<input class="form-control-file" type="file" accept=".csv" name="csvFile" />
<div class="form-group">
<label>{% trans "Payment source" %}</label>
<select name="payment_source" class="form-control">
@@ -36,13 +40,16 @@
<option value="bank_transfer">{% trans "Bank transfer" %}</option>
<option value="cash">{% trans "Cash payment" %}</option>
</select>
<small class="form-text text-muted">
{% trans "This payment source will be used to create any payments for new members that already exist in the database." %}
</small>
</div>
<div class="form-group">
<label>{% trans "CSV delimiter" %}</label>
<input type="text" name="delimiter" class="form-control" value=";" />
<p class="form-text text-muted">
<small class="form-text text-muted">
{% blocktrans %}The symbol that is used to separate items in one line. Defaults to ';' (semicolon).{% endblocktrans %}
</p>
</small>
</div>
<div>
<button type="submit" class="btn btn-primary">{% trans "Send" %}</button>
+1 -1
View File
@@ -41,7 +41,7 @@
{{ table|safe }}
<div>
<a href="/members/export_csv" class="btn btn-info">{% trans "Download CSV" %}</a>
<a href="/members/export_members" class="btn btn-info">{% trans "Download Excel" %}</a>
</div>
</div>
{% endblock content %}
+4
View File
@@ -36,5 +36,9 @@
{% endif %}
{{ table|safe }}
<div>
<a href="/members/export_payments" class="btn btn-info">{% trans "Download Excel" %}</a>
</div>
</div>
{% endblock content %}
+10
View File
@@ -0,0 +1,10 @@
{% extends "members_base.html" %}
{% block content %}
<h1>{{ title }}</h1>
<h3>{{ header }}</h3>
<form method="POST" action="/members/import_excel" enctype="multipart/form-data">{% csrf_token %}
{{ form }}
<input type="submit" class="btn btn-primary">
</form>
{% endblock %}
@@ -0,0 +1,3 @@
Testi;Ukkeli;testi@ukkeli.fi;Espoo;1;0
Jäbä;Kakkeli;jaba@kakkeli.fi;Hamina;0;1
Kolmas;Kaveri;kolmas@kaveri.com;Mesta;1;1
1 Testi Ukkeli testi@ukkeli.fi Espoo 1 0
2 Jäbä Kakkeli jaba@kakkeli.fi Hamina 0 1
3 Kolmas Kaveri kolmas@kaveri.com Mesta 1 1
@@ -0,0 +1 @@
Testi;Ukkeli;testi@ukkeli.fi;Espoo;1;0
1 Testi Ukkeli testi@ukkeli.fi Espoo 1 0
+73 -7
View File
@@ -3,10 +3,12 @@
from django.test import TestCase, Client
from django.contrib.auth.models import User
from members.management.commands.createsahkopiikkiuser import Command as SahkopiikkiCommand
from members.models import Member
from members.models import Member, Payment, Request
from rest_framework.authtoken.models import Token
import logging
import os
import pyexcel
class MemberRegisterTestCase(TestCase):
@@ -14,7 +16,13 @@ class MemberRegisterTestCase(TestCase):
def setUp(self):
"""Setup testing environment by creating member and admin."""
memb = Member.objects.create(first_name="Tidus", last_name="Tester")
memb = Member.objects.create(first_name="Tidus", last_name="Tester", email="tidus@tester.fi")
payment = Payment.objects.create(member=memb, source='AYY')
appl = Request.objects.create(
first_name="Liisa", last_name="Mattila",
email="liisa.mattila@pylly.com", POR="Kouvola",
AYY=True, jas=False)
username, password = 'test_admin', 'password123'
test_admin = User.objects.create_superuser(
username, 'myemail@test.com', password)
@@ -31,16 +39,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}, follow=True)
current_dir = os.path.dirname(os.path.abspath(__file__))
with open(os.path.join(current_dir, 'test_resources', 'single_line_import.csv')) as csvFile:
response = self.c.post('/members/import_csv', {
'csvFile': csvFile,
'delimiter': ';',
'payment_source': 'AYY'
}, 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}, follow=True)
current_dir = os.path.dirname(os.path.abspath(__file__))
with open(os.path.join(current_dir, 'test_resources', 'multi_line_import.csv')) as csvFile:
response = self.c.post('/members/import_csv', {
'csvFile': csvFile,
'delimiter': ';',
'payment_source': 'AYY'
}, follow=True)
self.assertEqual(response.status_code, 200)
def test_autocomplete_search_found(self):
@@ -87,3 +106,50 @@ class MemberRegisterTestCase(TestCase):
response = self.c.get('/members/check?email={}'.format(email), follow=True)
self.assertEqual(response.status_code, 401)
def test_export_members_excel(self):
"""Test if the user can download an excel file of the member register"""
resp = self.c.get('/members/export_members')
content_type = 'application/vnd.ms-excel'
self.assertIn(content_type, resp['Content-Type'])
content = resp.content
arrays = pyexcel.get_array(file_content=content, file_type='xlsx')
tidus_array = ['Tidus', 'Tester', 'tidus@tester.fi', '', '0', '0']
self.assertIn(tidus_array, arrays)
def test_export_payments_excel(self):
"""Test if the user can download an excel file of the payment register"""
resp = self.c.get('/members/export_payments')
content_type = 'application/vnd.ms-excel'
self.assertIn(content_type, resp['Content-Type'])
content = resp.content
arrays = pyexcel.get_array(file_content=content, file_type='xlsx')
created = Payment.objects.get(member__email='tidus@tester.fi').date.strftime('%Y-%m-%d %H:%M:%S')
tidus_array = ['Tidus Tester', created, 'AYY']
self.assertIn(tidus_array, arrays)
def test_export_applications_excel(self):
"""Test if the user can download an excel file of the member application register"""
resp = self.c.get('/members/export_applications')
content_type = 'application/vnd.ms-excel'
self.assertIn(content_type, resp['Content-Type'])
content = resp.content
arrays = pyexcel.get_array(file_content=content, file_type='xlsx')
submitted = Request.objects.get(email='liisa.mattila@pylly.com').submitted.strftime('%Y-%m-%d %H:%M:%S')
liisa_array = ['Liisa', 'Mattila', 'liisa.mattila@pylly.com', 'Kouvola', '1', '0', submitted]
self.assertIn(liisa_array, arrays)
def test_submit_member_application(self):
"""Test if submitting a member application works"""
data = {
'first_name': 'Seppo', 'last_name': 'Saastamoinen',
'email': 'seppo@saastamoin.en', 'jas': 'on',
'POR': 'Dipolin viinibaari'
}
resp = self.c.post('/members/submit_application', data=data)
self.assertEqual(resp.status_code, 200)
self.assertTrue(Request.objects.filter(email='seppo@saastamoin.en').exists())
+8 -3
View File
@@ -7,7 +7,7 @@ from django.views.generic.base import RedirectView
# members
from members.views import member_list, payment_add, payment_submit
from members.views import application_delete_confirm, application_delete
from members.views import application_accept, import_csv, export_csv
from members.views import application_accept, import_csv
from members.views import settings_page, payment_edit
from members.views import payment_delete_confirm
from members.views import payment_delete, payment_update
@@ -20,6 +20,9 @@ from members.views import member_delete_confirm
from members.views import member_delete
from members.views import payment_list
from members.views import add_many_confirm
from members.views import export_members_excel
from members.views import export_payments_excel
from members.views import export_applications_excel
# autocomplete view
from members.views import MemberAutoComplete
@@ -108,8 +111,10 @@ urlpatterns = [
# send CSV member data by POST
url(r'^import_csv', import_csv),
# download CSV member data
url(r'^export_csv', export_csv),
# export members as excel file
url(r'export_members', export_members_excel),
url(r'export_payments', export_payments_excel),
url(r'export_applications', export_applications_excel),
# favourite icon
url(r'^favicon\.ico$', favicon_view),
+12 -25
View File
@@ -14,6 +14,7 @@ import html
from members.views.utils import *
from members.tables import RequestTable
from members.forms import ApplicationForm
from members.views import error_view
@ensure_csrf_cookie
@@ -47,8 +48,7 @@ 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')})
return error_view(request, _('No application id specified'))
else:
application = Request.objects.get(id=i)
form = ApplicationForm(instance=application)
@@ -68,9 +68,7 @@ def application_accept(request, *args, **kwargs):
if id is not None:
application = Request.objects.get(id=id)
else:
return render(request,
'error.html',
{'error': _("Application missing 'id' field.")})
return error_view(request, _("Application missing 'id' field."))
form = ApplicationForm(request.POST, instance=application)
if form.is_valid():
@@ -78,9 +76,9 @@ def application_accept(request, *args, **kwargs):
application = form.save()
if Member.objects.filter(email=application.email).exists():
return render(request,
'error.html',
{'error': _('Email {} is already in use by a member. Application cannot be accepted.').format(application.email)})
return error_view(request, _(
'Email {} is already in use by a member. Application cannot be accepted.'
).format(application.email))
member = application.to_member()
member.save()
@@ -96,14 +94,10 @@ def application_accept(request, *args, **kwargs):
'/members/list?notification={}'.format(html.escape(notification)))
except Exception as ex:
logging.exception('Exception while accepting application')
return render(request,
'error.html',
{'error': str(ex)})
return error_view(request, str(ex))
else:
logging.info(form)
return render(request,
'error.html',
{'error': form.errors})
return error_view(request, form.errors)
@ensure_csrf_cookie
@@ -115,8 +109,7 @@ def application_delete(request, *args, **kwargs):
try:
id = request.POST['id']
except KeyError:
return render(
request, 'error.html', {'error': _('No application id specified')})
return error_view(request, _('No application id specified'))
try:
application = Request.objects.get(id=id)
@@ -130,9 +123,7 @@ def application_delete(request, *args, **kwargs):
'/members/applications?notification={}'
.format(html.escape(notification)))
except:
return render(request,
'error.html',
{'error': _('Could not delete application object')})
return error_view(request, _('Could not delete application object'))
@ensure_csrf_cookie
@@ -143,9 +134,7 @@ 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')})
return error_view(request, _('No application id specified'))
else:
application = Request.objects.get(id=i)
form = ApplicationForm(instance=application)
@@ -172,6 +161,4 @@ def application_submit(request, *args, **kwargs):
form.save()
return render(request, 'application_success.html', {})
else:
return render(request,
'error.html',
{'error': form.errors})
return error_view(request, form.errors)
+11 -18
View File
@@ -70,8 +70,7 @@ 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')})
return error_view(request, _('No member id specified'))
else:
member = Member.objects.get(id=i)
form = MemberForm(instance=member)
@@ -94,11 +93,15 @@ def member_add_many(request, *args, **kwargs):
@permission_required('members.add_member', raise_exception=True)
def add_many_confirm(request, *args, **kwargs):
models = request.session['models']
payment_source = request.session['payment_source']
try:
members, payments = models.members, models.payments
for member in members:
member.save()
if not member.payments.exists(): # create payment for new members
payment = Payment.objects.create(member=member, source=payment_source)
for payment in payments:
payment.save()
@@ -129,7 +132,7 @@ def member_submit(request, *args, **kwargs):
return HttpResponseRedirect(
'/members/list?notification={}'.format(html.escape(notification)))
else:
return render(request, 'error.html', {'error': form.errors})
return error_view(request, form.errors)
@ensure_csrf_cookie
@@ -143,10 +146,7 @@ def member_update(request, *args, **kwargs):
if id is not None:
member = Member.objects.get(id=id)
else:
return render(request,
'error.html',
{'error': _("Member missing 'id' field.")})
logging.debug(member)
return error_view(request, _("Member missing 'id' field."))
form = MemberForm(request.POST, instance=member)
if form.is_valid():
@@ -160,10 +160,7 @@ def member_update(request, *args, **kwargs):
return HttpResponseRedirect(
'/members/list?notification={}'.format(html.escape(notification)))
else:
return render(
request,
'error.html',
{'error': form.errors})
return error_view(request, form.errors)
@ensure_csrf_cookie
@@ -175,8 +172,7 @@ def member_delete(request, *args, **kwargs):
try:
id = request.POST['id']
except KeyError:
return render(request,
'error.html', {'error': _('No member id specified')})
return error_view(request, _('No member id specified'))
try:
member = Member.objects.get(id=id)
@@ -189,9 +185,7 @@ def member_delete(request, *args, **kwargs):
return HttpResponseRedirect(
'/members/list?notification={}'.format(html.escape(notification)))
except:
return render(request,
'error.html',
{'error': _('Could not delete member object')})
return error_view(request, _('Could not delete member object'))
@ensure_csrf_cookie
@@ -202,8 +196,7 @@ 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')})
return error_view(request, _('No member id specified'))
else:
member = Member.objects.get(id=i)
form = MemberForm(instance=member)
+7 -16
View File
@@ -14,6 +14,7 @@ import html
from members.views.utils import *
from members.tables import PaymentTable
from members.forms import PaymentForm
from members.views import error_view
@ensure_csrf_cookie
@@ -73,7 +74,7 @@ def payment_submit(request, *args, **kwargs):
'/members/payments?notification={}'
.format(html.escape(notification)))
else:
return render(request, 'error.html', {'error': form.errors})
return error_view(request, form.errors)
@ensure_csrf_cookie
@@ -84,9 +85,7 @@ 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')})
return error_view(request, _('No payment id specified'))
else:
payment = Payment.objects.get(id=i)
form = PaymentForm(instance=payment)
@@ -103,9 +102,7 @@ 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')})
return error_view(request, _('No payment id specified'))
else:
payment = Payment.objects.get(id=i)
form = PaymentForm(instance=payment)
@@ -123,9 +120,7 @@ def payment_delete(request, *args, **kwargs):
try:
id = request.POST['id']
except KeyError:
return render(request,
'error.html',
{'error': _('No payment id specified')})
return error_view(request, _('No payment id specified'))
try:
payment = Payment.objects.get(id=id)
@@ -138,9 +133,7 @@ def payment_delete(request, *args, **kwargs):
'/members/payments?notification={}'
.format(html.escape(notification)))
except:
return render(request,
'error.html',
{'error': _('Could not delete payment object')})
return error_view(request, _('Could not delete payment object'))
@ensure_csrf_cookie
@@ -165,6 +158,4 @@ def payment_update(request, *args, **kwargs):
'/members/payments?notification={}'
.format(html.escape(notification)))
else:
return render(request,
'error.html',
{'error': _('Could not update payment object')})
return error_view(request, _('Could not update payment object'))
+35 -30
View File
@@ -2,7 +2,7 @@ from django.shortcuts import render
from django.contrib.auth.decorators import permission_required, login_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.http import HttpResponse, HttpResponseRedirect, HttpResponseBadRequest
from django.core.mail import send_mail
from django.conf import settings
from django.utils.translation import ugettext as _
@@ -20,8 +20,9 @@ from rest_framework import generics
from rest_framework import permissions
from members.models import Member, Request, Payment
from members.forms import MemberForm, PaymentForm, ApplicationForm, CSVValidationError
from members.forms import MemberForm, PaymentForm, ApplicationForm, CSVValidationError, UploadFileForm
from members.tables import MemberTable, PaymentTable, RequestTable
from members.resources import MemberResource, PaymentResource, ApplicationResource
# Can be used to retrieve single member information via REST API
@@ -34,8 +35,8 @@ class MemberDetail(generics.RetrieveAPIView):
throttle_classes = (BurstRateThrottle, SustainedRateThrottle, )
def error_view(request, message):
return render(request, 'error.html', {'error': str(message)})
def error_view(request, message, status=400):
return render(request, 'error.html', {'error': message}, status=400)
def validate_recaptcha(response):
@@ -108,13 +109,14 @@ def settings_page(request, *args, **kwargs):
def import_csv(request, *args, **kwargs):
"""Get csv data imported to page and create members based on that."""
try:
data = request.POST['textfield']
csv_in_memory_file = request.FILES.get('csvFile')
csv_file = csv_in_memory_file.file
data = csv_file.read().decode('utf-8')
delimiter = request.POST.get('delimiter', ',')
payment_source = request.POST['payment_source']
except:
return render(request,
'error.html',
{'error': _('Missing "textfield" POST request field')})
return error_view(request, _('Missing CSV file'))
try:
result = MemberForm.csv_to_models(data, payment_source=payment_source, delimiter=delimiter)
@@ -123,7 +125,7 @@ def import_csv(request, *args, **kwargs):
return error_view(request, ex.form_errors)
except Exception as ex:
logging.exception('Other error in CSV import')
return error_view(request, ex)
return error_view(request, str(ex))
member_table = MemberTable(result.members,
request=request,
@@ -142,6 +144,7 @@ def import_csv(request, *args, **kwargs):
payment_table_html = convert_table_to_html(payment_table, request)
request.session['models'] = result
request.session['payment_source'] = payment_source
context = {
'members': member_table_html,
'payments': payment_table_html
@@ -149,27 +152,6 @@ def import_csv(request, *args, **kwargs):
return render(request, 'member_add_many_confirm.html', context)
@ensure_csrf_cookie
@require_http_methods(["GET"])
@permission_required('members.read_member', login_url='/login', raise_exception=True)
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 = 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,
@@ -177,3 +159,26 @@ def send_mail_wrapper(subject, message, email_to):
settings.DEFAULT_EMAIL_FROM,
[email_to],
fail_silently=False)
def make_excel_response(Resource):
res = Resource()
dataset = res.export()
response = HttpResponse(dataset.xlsx, content_type='application/vnd.ms-excel; charset=utf-8')
response['Content-Disposition'] = 'attachment; filename="export.xlsx"'
return response
@require_http_methods(['GET'])
def export_members_excel(request, *args, **kwargs):
return make_excel_response(MemberResource)
@require_http_methods(['GET'])
def export_payments_excel(request, *args, **kwargs):
return make_excel_response(PaymentResource)
@require_http_methods(['GET'])
def export_applications_excel(request, *args, **kwargs):
return make_excel_response(ApplicationResource)
+3
View File
@@ -29,4 +29,7 @@ django-autocomplete-light==3.2.10
six==1.10.0
django-suit==0.2.25
telepot==12.3
django-import-export==0.5.1
django-password-reset==1.0
pyexcel==0.5.6
pyexcel-xlsx==0.5.2
+3
View File
@@ -85,9 +85,12 @@ INSTALLED_APPS = [
'django_tables2',
'auditlog',
'phonenumber_field',
'import_export',
'password_reset',
]
IMPORT_EXPORT_USE_TRANSACTIONS = True
TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
NOSE_ARGS = [