From d17d9b8b366dbd1f54f1e522d961acab93758337 Mon Sep 17 00:00:00 2001 From: Aarni Date: Mon, 8 Aug 2016 21:15:31 +0300 Subject: [PATCH 1/5] Removed obsolete file --- members/static/html/rekisteri.html | 26 -------------------------- 1 file changed, 26 deletions(-) delete mode 100644 members/static/html/rekisteri.html diff --git a/members/static/html/rekisteri.html b/members/static/html/rekisteri.html deleted file mode 100644 index c8b0e7b..0000000 --- a/members/static/html/rekisteri.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - Haku - - - - - - - - -
-

Jäsenrekisteri

-

Hae jäsen

-

Syötä jäsenen nimi:

-
-
- - -
- -
- - - \ No newline at end of file From 2a1388990ceea46791517943423e44947abb6472 Mon Sep 17 00:00:00 2001 From: Aarni Date: Mon, 15 Aug 2016 19:22:05 +0300 Subject: [PATCH 2/5] JS app moved to appconfig. Noty notifications added --- members/static/js/appconfig.js | 6 + members/static/js/jquery.noty.packaged.js | 1442 +++++++++++++++++++++ members/static/js/members_controllers.js | 18 +- members/static/js/members_routers.js | 8 - members/templates/members_index.html | 12 +- 5 files changed, 1467 insertions(+), 19 deletions(-) create mode 100644 members/static/js/appconfig.js create mode 100644 members/static/js/jquery.noty.packaged.js diff --git a/members/static/js/appconfig.js b/members/static/js/appconfig.js new file mode 100644 index 0000000..878eb93 --- /dev/null +++ b/members/static/js/appconfig.js @@ -0,0 +1,6 @@ +var app = angular.module('memberApp', ['ngRoute', 'ngAnimate', 'ui.bootstrap']); + +app.config(['$httpProvider', function ($httpProvider) { + $httpProvider.defaults.xsrfCookieName = 'csrftoken'; + $httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken'; +}]); \ No newline at end of file diff --git a/members/static/js/jquery.noty.packaged.js b/members/static/js/jquery.noty.packaged.js new file mode 100644 index 0000000..c799814 --- /dev/null +++ b/members/static/js/jquery.noty.packaged.js @@ -0,0 +1,1442 @@ +!function(root, factory) { + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else if (typeof exports === 'object') { + module.exports = factory(require('jquery')); + } else { + factory(root.jQuery); + } +}(this, function($) { + +/*! + @package noty - jQuery Notification Plugin + @version version: 2.3.8 + @contributors https://github.com/needim/noty/graphs/contributors + + @documentation Examples and Documentation - http://needim.github.com/noty/ + + @license Licensed under the MIT licenses: http://www.opensource.org/licenses/mit-license.php + */ + + if(typeof Object.create !== 'function') { + Object.create = function(o) { + function F() { + } + + F.prototype = o; + return new F(); + }; + } + + var NotyObject = { + + init: function(options) { + + // Mix in the passed in options with the default options + this.options = $.extend({}, $.noty.defaults, options); + + this.options.layout = (this.options.custom) ? $.noty.layouts['inline'] : $.noty.layouts[this.options.layout]; + + if($.noty.themes[this.options.theme]) + this.options.theme = $.noty.themes[this.options.theme]; + else + this.options.themeClassName = this.options.theme; + + this.options = $.extend({}, this.options, this.options.layout.options); + this.options.id = 'noty_' + (new Date().getTime() * Math.floor(Math.random() * 1000000)); + + // Build the noty dom initial structure + this._build(); + + // return this so we can chain/use the bridge with less code. + return this; + }, // end init + + _build: function() { + + // Generating noty bar + var $bar = $('
').attr('id', this.options.id); + $bar.append(this.options.template).find('.noty_text').html(this.options.text); + + this.$bar = (this.options.layout.parent.object !== null) ? $(this.options.layout.parent.object).css(this.options.layout.parent.css).append($bar) : $bar; + + if(this.options.themeClassName) + this.$bar.addClass(this.options.themeClassName).addClass('noty_container_type_' + this.options.type); + + // Set buttons if available + if(this.options.buttons) { + + // If we have button disable closeWith & timeout options + this.options.closeWith = []; + this.options.timeout = false; + + var $buttons = $('
').addClass('noty_buttons'); + + (this.options.layout.parent.object !== null) ? this.$bar.find('.noty_bar').append($buttons) : this.$bar.append($buttons); + + var self = this; + + $.each(this.options.buttons, function(i, button) { + var $button = $('
From 82ec5d53fe089fcea507c82919f9fa8de9bf5580 Mon Sep 17 00:00:00 2001 From: Aarni Date: Mon, 15 Aug 2016 19:26:32 +0300 Subject: [PATCH 3/5] Removed unneeded js libraries --- members/static/js/appconfig.js | 2 +- members/templates/members_index.html | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/members/static/js/appconfig.js b/members/static/js/appconfig.js index 878eb93..382521e 100644 --- a/members/static/js/appconfig.js +++ b/members/static/js/appconfig.js @@ -1,4 +1,4 @@ -var app = angular.module('memberApp', ['ngRoute', 'ngAnimate', 'ui.bootstrap']); +var app = angular.module('memberApp', ['ngRoute']); app.config(['$httpProvider', function ($httpProvider) { $httpProvider.defaults.xsrfCookieName = 'csrftoken'; diff --git a/members/templates/members_index.html b/members/templates/members_index.html index 09cb8db..3317a5f 100644 --- a/members/templates/members_index.html +++ b/members/templates/members_index.html @@ -14,13 +14,10 @@ - - {%load staticfiles %} - From a64de3b1772a5e935adfffbfdbf8780127758ba9 Mon Sep 17 00:00:00 2001 From: okalintu Date: Mon, 8 Aug 2016 22:38:40 +0300 Subject: [PATCH 4/5] fixed a bunch of bugs in csvimport backend --- members/models.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/members/models.py b/members/models.py index 436a73d..afa0bfc 100644 --- a/members/models.py +++ b/members/models.py @@ -1,5 +1,7 @@ from django.db import models from django.utils import timezone +from io import StringIO +import csv class Member(models.Model): ''' @@ -45,9 +47,33 @@ class Member(models.Model): except KeyError: pass self.save() + @classmethod + def import_csv(cls, csv_string): + reader = csv.reader(StringIO(csv_string.strip())) + response = {"status": "success", "errors": []}; + try: + data = list(reader) + except ValueError: + return {"status": "failure", "errors": ["could not parse csv file"]} + for row in data: + if True: + obj = cls.objects.create( + first_name=row[0], + last_name=row[1], + email=row[2], + POR=row[3], + AYY=bool(row[4]), + jas=bool(row[5]), + ) + print("added obj {}".format(obj)) + else: + response["status"] = "failure" + response["errors"].append("failure adding item {}".format(", ".join(row))) + + return response def __str__(self): - return "{} {}".format(first_name, last_name) + return "{} {}".format(self.first_name, self.last_name) class MemberRequest(models.Model): member = models.ForeignKey(Member) From b564e98c2e192e79b26a98a95b9c52590b5dc708 Mon Sep 17 00:00:00 2001 From: okalintu Date: Mon, 15 Aug 2016 19:42:25 +0300 Subject: [PATCH 5/5] completed csvimport feature. see examples/members.txt --- examples/members.txt | 12 ++++++++++++ members/models.py | 8 ++++---- members/views.py | 9 +++++++++ sikweb/urls.py | 3 ++- 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/examples/members.txt b/examples/members.txt index 67eb1ee..87969b0 100644 --- a/examples/members.txt +++ b/examples/members.txt @@ -88,3 +88,15 @@ POST /members/api/request/:id //reject member request (== delete request and delete member) DELETE /members/api/request/:id + + +// mass import from csv +POST /members/api/csvimport +//csvformat first_name,last_name,email,POR,AYY,JAS +// example data +Pekka,Pöytä,pekka.p.pouta@mosh.pit,Tuska,1,0 + +// example response on success +{"status": "success", "errors": []} +// example response on failure (code will be 400) +{"status": "failure", "errors": ["failure adding item Pekka, P\u00f6yt\u00e42, pekka.p.pouta@mosh.pit, Tuska, Eip"]} diff --git a/members/models.py b/members/models.py index afa0bfc..97398e0 100644 --- a/members/models.py +++ b/members/models.py @@ -56,17 +56,17 @@ class Member(models.Model): except ValueError: return {"status": "failure", "errors": ["could not parse csv file"]} for row in data: - if True: + try: obj = cls.objects.create( first_name=row[0], last_name=row[1], email=row[2], POR=row[3], - AYY=bool(row[4]), - jas=bool(row[5]), + AYY=row[4].lower() in ['yes','y','1','true',"kyllä", "khyl"], + jas=row[5].lower() in ['yes','y','1','true',"kyllä", "khyl"], ) print("added obj {}".format(obj)) - else: + except: response["status"] = "failure" response["errors"].append("failure adding item {}".format(", ".join(row))) diff --git a/members/views.py b/members/views.py index 994c418..589a913 100644 --- a/members/views.py +++ b/members/views.py @@ -62,6 +62,15 @@ def member_requests(request, *args, **kwargs): reqs = list(map(lambda r: r.get_dict(),MemberRequest.objects.all())) return HttpResponse(json.dumps(reqs)) +@ensure_csrf_cookie +def csv_import(request, *args, **kwargs): + data = request.body.decode("utf-8") + resp_data = Member.import_csv(data) + resp = HttpResponse(json.dumps(resp_data)) + if resp_data['status'] == 'failure': + resp.status_code = 400 + return resp + @ensure_csrf_cookie def new_member_request(request, *args, **kwargs): try: diff --git a/sikweb/urls.py b/sikweb/urls.py index df7ca9f..d3af1d4 100644 --- a/sikweb/urls.py +++ b/sikweb/urls.py @@ -21,6 +21,7 @@ from members.views import index as mindex from members.views import members as mems from members.views import member as mem from members.views import handle_mem_request +from members.views import csv_import as mem_csv_import from members.views import new_member_request from members.views import member_requests @@ -34,7 +35,7 @@ urlpatterns = [ url(r'^members/api/members$', mems), url(r'^members/api/member/(?P\d+)$', mem), url(r'^members/api/member/$', mem), - url(r'^members/api/member/$', mem), + url(r'^members/api/csvimport$', mem_csv_import), url(r'^members/api/requests$', member_requests), url(r'^members/api/request$', new_member_request), url(r'^members/api/request/(?P\d+)$', handle_mem_request),