diff --git a/ComSemApp/administrator/views.py b/ComSemApp/administrator/views.py
index 8fa6f9f8..4542ba1d 100644
--- a/ComSemApp/administrator/views.py
+++ b/ComSemApp/administrator/views.py
@@ -8,7 +8,9 @@
from django.views.generic import ListView, CreateView, UpdateView, FormView, DeleteView
from django.views import View
from django.core.mail import send_mail
+from django.core.validators import validate_email
from django.contrib import messages
+import re
from django.contrib.auth.models import User
from ComSemApp.models import *
@@ -17,7 +19,6 @@
from ComSemApp.libs.mixins import RoleViewMixin
-
class AdminViewMixin(RoleViewMixin):
role_class = Admin
@@ -66,6 +67,104 @@ def _send_email(self, user, password):
fail_silently=False,
)
+ def db_create_user(self, **kwargs):
+ user = User.objects.create(**kwargs)
+ password = User.objects.make_random_password()
+ user.set_password(password)
+ user.save()
+ self._send_email(user, password)
+ return user
+
+ def db_create_student(self, **kwargs):
+ institution = self.insititution
+ user = self.db_create_user(**kwargs)
+ return Student.objects.create(user=user, institution=institution)
+
+ #handle CSV upload
+ def post(self, request, *args, **kwargs):
+ if (len(request.FILES) > 0): #check to make sure file was uploaded
+ csv_file = request.FILES['file']
+ file_data = csv_file.read().decode("utf-8")
+ lines = file_data.split("\n")
+ message_content = [""]
+ linecount = 0
+ rejectcount = 0
+ for line in lines:
+ if len(line): #make sure line isnt empty
+ fields = line.split(",")
+ okToCreate = True
+ rejected = False
+ linecount += 1
+ if (fields[0] == "" or fields[0] == ""):
+ #end of file
+ break
+ if (len(fields) < 4):
+ message = "!!! Missing columns, please make sure you have columns as follows: firstname,lastname,email,username"
+ message_content.append(message)
+ rejected = True
+ rejectcount += 1
+ break
+ if (fields[0].isalpha() == False or fields[1].isalpha() == False):
+ message = (str(linecount) + " " + fields[0] + " " + fields[1] + " " + fields[2] + " " + fields[3] + " Invalid First or Last Name \n")
+ message_content.append(message)
+ rejectcount += 1
+ rejected = True
+ okToCreate = False
+ for user in Student.objects.filter(institution=self.institution):
+ if(user.user.email== fields[2]):
+ okToCreate = False
+ if (rejected == False): ##if rejected is false, we need to increment the number of rejects, if its already false, dont increment it but still log error
+ rejectcount += 1
+ rejected = True
+ message = (str(linecount) + " " + fields[0] + " " + fields[1] + " " + fields[2] + " " + fields[3] + " Duplicate Email Address \n")
+ message_content.append(message)
+
+ if(user.user.username== fields[3]):
+ okToCreate = False
+ if (rejected == False): ##if rejected is false, we need to increment the number of rejects, if its already false, dont increment it but still log error
+ rejectcount += 1
+ rejected = True
+ message = (str(linecount) + " " + fields[0] + " " + fields[1] + " " + fields[2] + " " + fields[3] + " Duplicate Username \n")
+ message_content.append(message)
+ if(okToCreate == False):
+ break
+
+ # Check if a valid email address
+ match = re.match('^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,4})$', fields[2].lower())
+
+ if (match == None):
+ if(rejected == False):
+ rejectcount += 1
+ rejected = True
+ okToCreate = False
+ message = (str(linecount) + " " + fields[0] + " " + fields[1] + " " + fields[2] + " " + fields[3] + " Invalid Email Address \n")
+ message_content.append(message)
+
+ # Check for valid username
+ usernameCheck = re.match('^[\w.@+-]+$', fields[3])
+ if (usernameCheck == None):
+ if(rejected == False):
+ rejectcount += 1
+ rejected = True
+ okToCreate = False
+ message = (str(linecount) + " " + fields[0] + " " + fields[1] + " " + fields[2] + " " + fields[3] + " Invalid Username \n")
+ message_content.append(message)
+
+ if (okToCreate == True):
+ user = {
+ "first_name": fields[0],
+ "last_name": fields[1],
+ "email": fields[2],
+ "username": fields[3]
+ }
+ self.db_create_student(**user)
+ print("student made")
+ message_content.insert(0, ("" + str((linecount - rejectcount)) + "/" + str(linecount)+ " Accounts created sucessfully\n" + "The below users were not added, Their line numbers are listed to the left,\nLines with multiple errors will be listed multiple times \n \n"))
+ message_disp = "".join(message_content)
+ messages.add_message(request, messages.ERROR, message_disp)
+ request.FILES.pop('file', None) #delete the csv file from memory
+ return HttpResponseRedirect(self.success_url)
+
@atomic
def _create_student(self, **kwargs):
user = User.objects.create_user(**kwargs)
@@ -134,11 +233,9 @@ def post(self, request):
messages.add_message(request, messages.ERROR, message)
return HttpResponseRedirect(self.success_url)
-
- def get_queryset(self):
+ def get_queryset(self):
return Student.objects.filter(institution=self.institution)
-
class CourseListView(AdminViewMixin, ListView):
model = Course
template_name = 'ComSemApp/admin/course_list.html'
diff --git a/ComSemApp/static/ComSemApp/DKNotus-Tour-master/DKNotusTour.png b/ComSemApp/static/ComSemApp/DKNotus-Tour-master/DKNotusTour.png
new file mode 100755
index 00000000..126344fe
Binary files /dev/null and b/ComSemApp/static/ComSemApp/DKNotus-Tour-master/DKNotusTour.png differ
diff --git a/ComSemApp/static/ComSemApp/DKNotus-Tour-master/LICENSE b/ComSemApp/static/ComSemApp/DKNotus-Tour-master/LICENSE
new file mode 100755
index 00000000..5b132a5e
--- /dev/null
+++ b/ComSemApp/static/ComSemApp/DKNotus-Tour-master/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 DK Notus IT Team
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/ComSemApp/static/ComSemApp/DKNotus-Tour-master/README.md b/ComSemApp/static/ComSemApp/DKNotus-Tour-master/README.md
new file mode 100755
index 00000000..630e86e7
--- /dev/null
+++ b/ComSemApp/static/ComSemApp/DKNotus-Tour-master/README.md
@@ -0,0 +1,235 @@
+# DK Notus Tour
+
+#### This compact solution for guided tours has 27 languages support. It requires only two very common dependencies: **jQuery** and **Bootstrap**. Also has useful features like auto scroll and "spotlight". We hope you enjoy it.
+
+
+
+We tried to keep all data regarding usage as short as possible.
+## 1. Features
+
+Features that we considerble important:
+
+ - **small requirements** - only jQuery and Bootstrap;
+ - **simple usage** - one function for common usage - yes, it's that simple;
+ - **events** - for advanced programmers usage;
+ - **scroll** - and some more useful features;
+ - **multi elements selection** - you can point more then one element for one tour step;
+ - **translations** - 27 languages support.
+
+## 2. Simple use case
+
+Lets start with two step tour for elements below:
+
+First of all we need to include two common libraries jQuery and Bootstrap. You ca use some CDN for that.
+
+
+
+
+Then it's time tour library `dknotus-tour.js` or `dknotus-tour.min.js`.
+
+
+
+Finally we can define our own tour and run it with Tour.run(). Yes, it's that simple.
+
+```javascript
+ $(function(){
+ $('#simpleBtn').click(function(){
+ Tour.run([
+ {
+ element: $('#btn1'),
+ content: 'first btn'
+ },
+ {
+ element: $('#btn2'),
+ content: 'and the second one description might be HTML'
+ },
+ ]);
+ });
+ });
+```
+
+## 3. Different tour positions
+```javascript
+ $(function(){
+ $('#positionsBtn').click(function(){
+ Tour.run([
+ {
+ element: $('#posBtn'),
+ content: 'by default tour is on the right'
+ },
+ {
+ element: $('#posBtn'),
+ content: 'but it can be on top',
+ position: 'top'
+ },
+ {
+ element: $('#posBtn'),
+ content: 'bottom',
+ position: 'bottom'
+ },
+ {
+ element: $('#posBtn'),
+ content: 'and finally on the left',
+ position: 'left'
+ }
+ ]);
+ });
+ });
+```
+
+## 4. Global and local parameters
+
+Tour may be run with two parameters: tour description (mandatory) and global options (optional) Tour.run(tourDescription, options). If for some tour hint some parameter is not set, then if it's possible it's taken from options.
+
+Possible parameters for hints descriptions and for global options:
+
+Parameter | Default value | Description
+--------- | ------------- | -----------
+element | *none* | jQuery element (might be more then one), if it's not set then hint is skipped.
+content | *empty string* | It's for contents of particular hints.
+close | true | Defines if close button should be shown.
+language | en | Defines interface language. Available languages:
+|| en | English (default)
+|| pl | Polish
+|| be | Belarusian
+|| ca | Catalan
+|| cs | Czech
+|| da | Danish
+|| de | German
+|| el | Greek
+|| es | Spanish
+|| et | Estonian
+|| fi | Finnish
+|| fr | French
+|| hu | Hungarian
+|| it | Italian
+|| lt | Lithuanian
+|| lv | Latvian
+|| mk | Macedonian
+|| nl | Dutch
+|| no | Norwegian
+|| pt | Portuguese
+|| ru | Russian
+|| sk | Slovak
+|| sl | Slovenian
+|| sq | Albanian
+|| sv | Swedish
+|| tr | Turkish
+|| uk | Ukrainian
+padding | 5 | Extra space around tour exposed elements. (Has only sense when spotlight option is true).
+position | right | Determines where hint should be shown relatively to element it describes.
+||| Possible values: right, left, top and bottom.
+scroll | true | If true then scrolls window so selected element and hint would be as close as possible to the view center.
+spotlight | true | If true then covers everything except selected element and hint with shadow.
+forceCorrectionLeft | 0 | Useful if for some reason left offset needs to be modified.
+forceCorrectionTop | 0 | Useful if for some reason top offset needs to be modified.
+forceCorrectionWidth | 0 | Useful if for some reason width needs to be modified.
+forceCorrectionHeight | 0 | Useful if for some reason height needs to be modified.
+
+All above options can be used for both: single hint description and for global options. With global options previous example can be written like:
+
+```javascript
+ $(function(){
+ $('#positionsShorterBtn').click(function(){
+ var globalOptions = {
+ element: $('#posBtn')
+ };
+
+ var tourDescription = [
+ {
+ content: 'by default tour is on the right'
+ },
+ {
+ content: 'but it can be on top',
+ position: 'top'
+ },
+ {
+ content: 'bottom',
+ position: 'bottom'
+ },
+ {
+ content: 'and finally on the left',
+ position: 'left'
+ }
+ ];
+
+ Tour.run(tourDescription, globalOptions);
+ });
+ });
+```
+
+## 5. Events example
+
+There are four events that can be used by developers:
+
+ - **onstart()** - Triggered when new tour starts ( `Tour.run()` );
+ - **onfinish()** - Triggered when Finish button is clicked;
+ - **onclose()** - Triggered when Close button is pressed ( `Tour.close()` );
+ - **onstep( currentStep )** - Triggered on every step shown ( `Tour.next()` or `Tour.prev()` );
+ - **onresize()** - By default this one is set.
+
+```javascript
+ $(function(){
+ $('#eventsBtn').click(function(){
+ Tour.onstart = function(){
+ console.log('We started!');
+ };
+
+ Tour.onfinish = function(){
+ console.log('The End');
+ };
+
+ Tour.onclose = function(){
+ console.log('Tour interupted');
+ };
+
+ Tour.onstep = function(currentStep){
+ console.log('"That\'s one small step for a man ..."');
+ console.log(currentStep);
+ };
+
+ Tour.run([
+ {
+ element: $('#eventBtn1').add('#eventBtn3'),
+ content: 'You prefer photos?',
+ position: 'top'
+ },
+ {
+ element: $('#eventBtn3').add('#eventBtn4'),
+ content: 'or videos?',
+ onstep: function(currentStep) {
+ console.log('Events defined in step, overwrites global definition');
+ }
+ }
+ ]);
+ });
+ });
+```
+
+## 6. Tour interface
+
+#### Methods
+
+Method | Description
+------ | -----------
+**Tour.run( tourDescription, globlOptions )** | Function for running Tour.
+**Tour.next()** | Goes to next tour step.
+**Tour.prev()** | Goes to previous tour step.
+**Tour.close()** | Interrupts tour and closes it.
+**Tour.current()** | Returns current step description.
+
+#### Events
+
+By default all except `onresize` are set to null.
+
+Event | Description
+----- | -----------
+**Tour.onstart()** | Triggered when new tour starts ( Tour.run() ).
+**Tour.onfinish()** | Triggered when Finish button is clicked.
+**Tour.onclose()** | Triggered when Close button is pressed ( Tour.close() ).
+**Tour.onstep( currentEvent )** | Triggered on every step shown ( Tour.next() or Tour.prev() ).
+**Tour.onresize()** | By default this one is set.
+
+## 7. Contact
+
+Jan Doleczek
diff --git a/ComSemApp/static/ComSemApp/DKNotus-Tour-master/dknotus-tour.js b/ComSemApp/static/ComSemApp/DKNotus-Tour-master/dknotus-tour.js
new file mode 100755
index 00000000..d1d25fd5
--- /dev/null
+++ b/ComSemApp/static/ComSemApp/DKNotus-Tour-master/dknotus-tour.js
@@ -0,0 +1,391 @@
+/*!
+ * DK Notus Tour JavaScript Library v1.2
+ * https://github.com/DKNotusIT/DKNotus-Tour/
+ *
+ * Copyright DK Notus and other contributors
+ * Released under the MIT license
+ * https://github.com/DKNotusIT/DKNotus-Tour/blob/master/LICENSE
+ *
+ * Date: 2018-03-17
+ */
+
+var Tour = (function() {
+ var t = [],
+ o, cur
+ T = {
+ step: {
+ pl: "krok",
+ en: "step",
+ be: "крок",
+ ca: "pas",
+ cs: "krok",
+ da: "trin",
+ de: "Schritt",
+ el: "βήμα",
+ es: "paso",
+ et: "samm",
+ fi: "vaihe",
+ fr: "étape",
+ hu: "lépés",
+ it: "passo",
+ lt: "žingsnis",
+ lv: "solis",
+ mk: "чекор",
+ nl: "stap",
+ no: "trinn",
+ pt: "passo",
+ ru: "шаг",
+ sk: "krok",
+ sl: "korak",
+ sq: "hapi",
+ sv: "steg",
+ tr: "adım",
+ uk: "крок"
+ },
+ Next: {
+ pl: "Następny",
+ en: "Next",
+ be: "Далей",
+ ca: "Següent",
+ cs: "Další",
+ da: "Næste",
+ de: "Weiter",
+ el: "Την επόμενη",
+ es: "Siguiente",
+ et: "Järgmine",
+ fi: "Seuraava",
+ fr: "Prochaine",
+ hu: "Következő",
+ it: "Accanto",
+ lt: "Kitas",
+ lv: "Nākamā",
+ mk: "Следна",
+ nl: "Volgende",
+ no: "Neste",
+ pt: "Próximo",
+ ru: "Далее",
+ sk: "Ďalej",
+ sl: "Naprej",
+ sq: "Tjetër",
+ sv: "Nästa",
+ tr: "Gelecek",
+ uk: "Далі"
+ },
+ Previous: {
+ pl: "Poprzedni",
+ en: "Previous",
+ be: "Папярэдні",
+ ca: "Anteriors",
+ cs: "Předchozí",
+ da: "Tidligere",
+ de: "Vorherige",
+ el: "Προηγούμενο",
+ es: "Anterior",
+ et: "Eelmine",
+ fi: "Edellinen",
+ fr: "Précédente",
+ hu: "Előző",
+ it: "Precedente",
+ lt: "Ankstesnis",
+ lv: "Iepriekšējā",
+ mk: "Претходна",
+ nl: "Vorige",
+ no: "Tidligere",
+ pt: "Anterior",
+ ru: "Предыдущий",
+ sk: "Predchádzajúce",
+ sl: "Prejšnji",
+ sq: "E mëparshme",
+ sv: "Föregående",
+ tr: "Önceki",
+ uk: "Попередній"
+ },
+ Finish: {
+ pl: "Zakończ",
+ en: "Finish",
+ be: "Аздабленне",
+ ca: "Acabat",
+ cs: "Dokončit",
+ da: "Finish",
+ de: "Finish",
+ el: "Τελειώνει",
+ es: "Acabado",
+ et: "Lõpeta",
+ fi: "Loppuun",
+ fr: "Finition",
+ hu: "Befejezés",
+ it: "Finitura",
+ lt: "Apdaila",
+ lv: "Apdare",
+ mk: "Заврши",
+ nl: "Afwerking",
+ no: "Ferdig",
+ pt: "Acabamento",
+ ru: "Отделка",
+ sk: "Povrch",
+ sl: "Zaključek",
+ sq: "Finish",
+ sv: "Avsluta",
+ tr: "Bitir",
+ uk: "Оздоблення"
+ }
+ };
+
+ function _t(s) {
+ return T[s][t[cur].language] || T[s]['en'];
+ }
+
+ function step(n) {
+ cur = n;
+ $('.tourStep, .tourBg').remove();
+
+ if (!t[n]) {
+ return;
+ }
+
+ $('body').append([
+ '
+ This compact solution for guided tours has
+ 27 languages support.
+ It requires only two very common dependencies:
+ jQuery and Bootstrap.
+ Also has useful features like auto scroll
+ and "spotlight".
+ We hope you enjoy it.
+
+
+
+
+
+
+
+ We tried to keep all data regarding usage as short as possible.
+
+
+
1. Features
+
+
+ Features that we considerble important:
+
+
+
+
small requirements
+
only jQuery and Bootstrap;
+
+
simple usage
+
one function for common usage - yes, it's that simple;
+
+
events
+
for advanced programmers usage;
+
+
scroll
+
and some more useful features;
+
+
multi elem. selection
+
you can point more then one element for one tour step;
+
+
translations
+
languages support.
+
+
+
2. Simple use case
+
+
Lets start with two step tour for elements below:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ First of all we need to include two common libraries
+ jQuery and Bootstrap.
+ You ca use some CDN for that.
+
+ $(function(){
+ $('#positionsBtn').click(function(){
+ Tour.run([
+ {
+ element: $('#posBtn'),
+ content: 'by default tour is on the right'
+ },
+ {
+ element: $('#posBtn'),
+ content: 'but it can be on top',
+ position: 'top'
+ },
+ {
+ element: $('#posBtn'),
+ content: 'bottom',
+ position: 'bottom'
+ },
+ {
+ element: $('#posBtn'),
+ content: 'and finally on the left',
+ position: 'left'
+ }
+ ]);
+ });
+ });
+
+
+
4. Global and local parameters
+
+
+ Tour may be run with two parameters: tour description (mandatory)
+ and global options (optional) Tour.run(tourDescription,
+ options). If for some tour hint some parameter is not set,
+ then if it's possible it's taken from options.
+
+
+
+ Possible parameters for hints descriptions and for global options
+
+
+
+
+
+
Parameter
+
Default value
+
Description
+
+
+
+
+
element
+
none
+
+ jQuery element (might be more then one),
+ if it's not set then hint is skipped.
+
+
+
+
content
+
empty string
+
It's for contents of particular hints.
+
+
+
close
+
true
+
Defines if close button should be shown.
+
+
+
language
+
en
+
+ Defines interface language. Available languages:
+
+
en
+
English (default)
+
+
pl
+
Polish
+
+
be
+
Belarusian
+
+
ca
+
Catalan
+
+
cs
+
Czech
+
+
da
+
Danish
+
+
de
+
German
+
+
el
+
Greek
+
+
es
+
Spanish
+
+
et
+
Estonian
+
+
fi
+
Finnish
+
+
fr
+
French
+
+
hu
+
Hungarian
+
+
it
+
Italian
+
+
lt
+
Lithuanian
+
+
lv
+
Latvian
+
+
mk
+
Macedonian
+
+
nl
+
Dutch
+
+
no
+
Norwegian
+
+
pt
+
Portuguese
+
+
ru
+
Russian
+
+
sk
+
Slovak
+
+
sl
+
Slovenian
+
+
sq
+
Albanian
+
+
sv
+
Swedish
+
+
tr
+
Turkish
+
+
uk
+
Ukrainian
+
+
+
+
+
padding
+
5
+
+ Extra space around tour exposed elements.
+ (Has only sense when spotlight option is true).
+
+
+
+
position
+
right
+
+ Determines where hint should be shown relativly to element
+ it describes.
+ Possible values: right, left, top and bottom.
+
+
+
+
scroll
+
true
+
+ If true then scrolls window so selected element
+ and hint would be as close as possible to the view center.
+
+
+
+
spotlight
+
true
+
+ If true then covers everything except selected element
+ and hint with shadow.
+
+
+
+
forceCorrectionLeft
+
0
+
+ Useful if for some reason left offset needs to be modified.
+
+
+
+
forceCorrectionTop
+
0
+
+ Useful if for some reason top offset needs to be modified.
+
+
+
+
forceCorrectionWidth
+
0
+
+ Useful if for some reason width needs to be modified.
+
+
+
+
forceCorrectionHeight
+
0
+
+ Useful if for some reason height needs to be modified.
+
+
+
+
+
+
+ All above options can be used for both: single hint description
+ and for global options. With global options previous example
+ can be writen like:
+
+
+
+
+ $(function(){
+ $('#positionsShorterBtn').click(function(){
+ var globalOptions = {
+ element: $('#posBtn')
+ };
+
+ var tourDescription = [
+ {
+ content: 'by default tour is on the right'
+ },
+ {
+ content: 'but it can be on top',
+ position: 'top'
+ },
+ {
+ content: 'bottom',
+ position: 'bottom'
+ },
+ {
+ content: 'and finally on the left',
+ position: 'left'
+ }
+ ];
+
+ Tour.run(tourDescription, globalOptions);
+ });
+ });
+
+
+
+
+
+
+
+
+
5. Events example
+
+
+ There are four events that can be used by developers:
+
+
+
+
onstart()
+
Triggered when new tour starts ( Tour.run() ).
+
+
onfinish()
+
Triggered when Finish button is clicked.
+
+
onclose()
+
+ Triggered when Close button is pressed
+ ( Tour.close() ).
+
+
+
onstep( currentStep )
+
+ Triggered on every step shown
+ ( Tour.next() or Tour.prev() ).
+
+ Please order students in the following way: firstname, lastname, email, username.
+ Below is an example of how the first line would look, a comma indicates a new column. Names must contain letters only.
+
+
+ John,Doe,johndoe@gmail.com,johnsusername
+
+
+
+
+ {% if messages %}
+ {% for message in messages %}
+