styled site with bootstrap, did some minor ui improvements and fixes

This commit is contained in:
tkupek 2016-02-21 22:41:24 +01:00
parent 7f04d1e502
commit 1c0927ca1c
12 changed files with 233 additions and 52 deletions

Binary file not shown.

View file

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.2 on 2016-02-21 17:05
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('elearning', '0017_auto_20160214_1534'),
]
operations = [
migrations.AddField(
model_name='useranswer',
name='correct',
field=models.NullBooleanField(),
),
migrations.AlterField(
model_name='setting',
name='logo',
field=models.CharField(default='test', max_length=256),
preserve_default=False,
),
]

View file

@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.2 on 2016-02-21 17:54
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('elearning', '0018_auto_20160221_1705'),
]
operations = [
migrations.RenameField(
model_name='setting',
old_name='button_solution',
new_name='text_solution',
),
migrations.AddField(
model_name='setting',
name='text_answer',
field=models.CharField(max_length=100, null=True),
),
migrations.AlterField(
model_name='option',
name='text',
field=models.CharField(max_length=256, null=True),
),
]

View file

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.2 on 2016-02-21 20:50
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('elearning', '0019_auto_20160221_1754'),
]
operations = [
migrations.AddField(
model_name='setting',
name='text_next',
field=models.CharField(max_length=100, null=True),
),
]

View file

@ -12,11 +12,13 @@ class Setting(models.Model):
message_welcome_user = models.TextField(null=True) message_welcome_user = models.TextField(null=True)
message_access_denied = models.TextField(null=True) message_access_denied = models.TextField(null=True)
message_already_answered = models.TextField(null=True) message_already_answered = models.TextField(null=True)
button_solution = models.CharField(max_length=100, null=True) text_answer = models.CharField(max_length=100, null=True)
text_solution = models.CharField(max_length=100, null=True)
text_next = models.CharField(max_length=100, null=True)
logo = models.CharField(max_length=256, null=False) logo = models.CharField(max_length=256, null=False)
active = models.BooleanField(unique=True, default=False) active = models.BooleanField(unique=True, default=False)
def __str__(self): def __unicode__(self):
return self.title return self.title
@ -26,16 +28,16 @@ class Question(models.Model):
text = models.TextField(null=True) text = models.TextField(null=True)
explanation = models.TextField(null=True) explanation = models.TextField(null=True)
def __str__(self): def __unicode__(self):
return self.title return self.title
class Option(models.Model): class Option(models.Model):
text = models.CharField(max_length=100, null=True) text = models.CharField(max_length=256, null=True)
correct = models.BooleanField(null=False, default=False) correct = models.BooleanField(null=False, default=False)
question = models.ForeignKey(Question, on_delete=models.CASCADE, null=False) question = models.ForeignKey(Question, on_delete=models.CASCADE, null=False)
def __str__(self): def __unicode__(self):
return self.text return self.text
@ -49,7 +51,7 @@ class User(models.Model):
name = models.CharField(max_length=100, null=False) name = models.CharField(max_length=100, null=False)
last_seen = models.DateTimeField(null=True) last_seen = models.DateTimeField(null=True)
def __str__(self): def __unicode__(self):
return self.name return self.name
@ -58,8 +60,8 @@ class UserAnswer(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE, null=False) question = models.ForeignKey(Question, on_delete=models.CASCADE, null=False)
correct = models.NullBooleanField(null=True) correct = models.NullBooleanField(null=True)
def __str__(self): def __unicode__(self):
return str(self.user) + " - " + str(self.question) return str(self.user) + " - " + str(self.question.id)
class Meta: class Meta:
unique_together = (('user', 'question'),) unique_together = (('user', 'question'),)
@ -71,3 +73,6 @@ class UserAnswerOptions(models.Model):
class Meta: class Meta:
unique_together = (('user_answer', 'option'),) unique_together = (('user_answer', 'option'),)
def __unicode__(self):
return str(self.user_answer) + " - " + str(self.option)

View file

@ -34,12 +34,8 @@ p[id^="explanation_"] {
margin-top: 50px; margin-top: 50px;
} }
.correct { .margin75 {
outline: 4px solid #00a353; margin-top: 75px;
}
.failed {
outline: 4px solid #a60000;
} }
.mainLogo { .mainLogo {
@ -55,3 +51,7 @@ p[id^="explanation_"] {
.table_headline { .table_headline {
font-weight: bold; font-weight: bold;
} }
.footer {
color: #b3b3b3
}

View file

@ -59,8 +59,8 @@ function toggle_solution(id)
function show_solution(id) function show_solution(id)
{ {
var element = document.getElementById("explanation_" + id); var element = document.getElementById("explanation_" + id);
if (element.className == "hide") { if (element.className.indexOf("hide") > -1) {
element.className = "show"; element.className = element.className.replace("hide", "show");
} }
} }
@ -76,9 +76,10 @@ function get_answer(id) {
if (answerRequest.readyState==4){ if (answerRequest.readyState==4){
if (answerRequest.status==200 || window.location.href.indexOf("http")==-1){ if (answerRequest.status==200 || window.location.href.indexOf("http")==-1){
parseResponse(id, answerRequest.responseText); parseResponse(id, answerRequest.responseText);
setButtonToNext(id);
} }
else{ else{
alert("An error has occured making the request"); alert("Sorry, sending your answer was not successful!");
} }
} }
} }
@ -91,6 +92,35 @@ function get_answer(id) {
answerRequest.send(null) answerRequest.send(null)
} }
function setButtonToNext(id) {
button = document.getElementById('showSolution_' + id);
button.className = button.className.replace("show", "hide");
if(getNextQuestion(id)) {
button = document.getElementById('next_' + id);
button.className = button.className.replace("hide", "show");
}
}
function scrollToNextQuestion(id) {
var nextQuestion = getNextQuestion(id);
if(nextQuestion) {
location.href = "#" + nextQuestion.id;
}
}
function getNextQuestion(id) {
var questions = document.getElementsByName("question");
for(var i = 0; i < questions.length; i++) {
if(questions[i].id == "question_" + id) {
return questions[i+1];
}
}
return null;
}
function getCheckboxAnswers(id) { function getCheckboxAnswers(id) {
var answers = []; var answers = [];
@ -107,22 +137,30 @@ function getCheckboxAnswers(id) {
function parseResponse(id, responseText) { function parseResponse(id, responseText) {
var correctOptions = JSON.parse(responseText); var correctOptions = JSON.parse(responseText).options_id;
var progress = JSON.parse(responseText).progress;
setCorrectOptions(correctOptions, id)
setProgress(progress)
}
function setCorrectOptions(correctOptions, id) {
var elements = document.getElementsByName("checkbox_" + id); var elements = document.getElementsByName("checkbox_" + id);
var holders = document.getElementsByName("checkbox_div_" + id);
for(var i = 0; i < elements.length; i++) { for(var i = 0; i < elements.length; i++) {
var element = elements[i]; var element = elements[i];
element.className=""; var holder = holders[i];
var correct = isCorrectOption(element.value); var correct = isCorrectOption(element.value);
if(correct) { if(correct) {
element.className="correct"; holder.className = holder.className + " list-group-item-success";
} else { } else {
if(element.checked) { if(element.checked) {
element.className="failed"; holder.className = holder.className + " list-group-item-danger";;
} }
} }
element.disabled = true;
} }
function isCorrectOption(option) { function isCorrectOption(option) {
@ -135,6 +173,11 @@ function parseResponse(id, responseText) {
} }
} }
function setProgress(progress) {
progressbar = document.getElementById("progressbar");
progressbar.style.width = progress + "%";
}
function ajaxRequest() { function ajaxRequest() {
var activexmodes=["Msxml2.XMLHTTP", "Microsoft.XMLHTTP"] //activeX versions to check for in IE var activexmodes=["Msxml2.XMLHTTP", "Microsoft.XMLHTTP"] //activeX versions to check for in IE

View file

@ -7,7 +7,15 @@
{% load staticfiles %} {% load staticfiles %}
<link rel="stylesheet" href="{% static 'css/style.css' %}"> <link rel="stylesheet" href="{% static 'css/style.css' %}">
<link href="{% static 'images/favicon.ico' %}" rel="shortcut icon"> <link href="{% static 'images/favicon.ico' %}" rel="shortcut icon">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>{{ settings.title }}</title> <title>{{ settings.title }}</title>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" integrity="sha384-fLW2N01lMqjakBkx3l/M9EahuwpSfeNvV63J5ezn3uZzapT0u7EYsXMjQV+0En5r" crossorigin="anonymous">
</head> </head>
<body> <body>

View file

@ -7,19 +7,33 @@
{% load staticfiles %} {% load staticfiles %}
<link rel="stylesheet" href="{% static 'css/style.css' %}"> <link rel="stylesheet" href="{% static 'css/style.css' %}">
<link href="{% static 'images/favicon.ico' %}" rel="shortcut icon"> <link href="{% static 'images/favicon.ico' %}" rel="shortcut icon">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>{{ settings.title }}</title> <title>{{ settings.title }}</title>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" integrity="sha384-fLW2N01lMqjakBkx3l/M9EahuwpSfeNvV63J5ezn3uZzapT0u7EYsXMjQV+0En5r" crossorigin="anonymous">
</head> </head>
<body> <body>
<div class="container"> <div class="container">
<img src="{{ settings.logo }}" class="mainLogo"> <img src="{{ settings.logo }}" class="mainLogo">
<h1>{{ settings.title }}</h1> <h1>{{ settings.title }}</h1>
<p>{{ settings.message_welcome_user }}</p> <p>{{ settings.message_welcome_user }}</p>
<div class="progress">
<div id="progressbar" class="progress-bar progress-bar-striped" role="progressbar" style="width: {{ progress }}%">
</div>
</div>
<hr/> <hr/>
<div class="margin50" ></div> <div class="margin50" ></div>
{% for question, options in questions_options.items %} {% for question, options in questions_options.items %}
<div class="container question" name="question" id="question_{{ question.id }}">
<h2>{{ question.id }}: {{ question.title }}</h2> <h2>{{ question.id }}: {{ question.title }}</h2>
<p>{{ question.text }}</p> <p>{{ question.text }}</p>
@ -27,19 +41,28 @@
{{ settings.message_already_answered }} {{ settings.message_already_answered }}
</p> </p>
<form action="#"> <ul class="list-group ">
{% for option in options %} {% for option in options %}
<li name="checkbox_div_{{ question.id }}" class="list-group-item">
<label> <label>
<input name="checkbox_{{ question.id }}" type="checkbox" value="{{ option.id }}"> <input name="checkbox_{{ question.id }}" type="checkbox" value="{{ option.id }}">
{{ option.text }} {{ option.text }}
</label> </label>
</li>
{% endfor %} {% endfor %}
<button class="margin10" type="button" id="showSolution_{{ question.id }}" onmousedown="get_answer({{ question.id }})">{{ settings.button_solution }}</button> </ul>
</form>
<p id="explanation_{{ question.id }}" class="hide">{{ question.explanation }}</p> <div class="container">
<div class="margin25"></div> <button class="btn btn-default show" type="button" id="showSolution_{{ question.id }}" onmouseup="get_answer({{ question.id }})">{{ settings.text_solution }}</button>
<button class="btn btn-default hide" type="button" id="next_{{ question.id }}" onmouseup="scrollToNextQuestion({{ question.id }})">{{ settings.text_next }}</button>
</div>
<div class="margin10 well well-sm hide" id="explanation_{{ question.id }}">
<h3>{{ settings.text_answer }}</h3>
<p>{{ question.explanation }}</p>
</div>
<div class="margin75"></div>
</div>
{% endfor %} {% endfor %}
<div class="margin50" ></div> <div class="margin50" ></div>

View file

@ -7,7 +7,15 @@
{% load staticfiles %} {% load staticfiles %}
<link rel="stylesheet" href="{% static 'css/style.css' %}"> <link rel="stylesheet" href="{% static 'css/style.css' %}">
<link href="{% static 'images/favicon.ico' %}" rel="shortcut icon"> <link href="{% static 'images/favicon.ico' %}" rel="shortcut icon">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>{{ settings.title }}</title> <title>{{ settings.title }}</title>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" integrity="sha384-fLW2N01lMqjakBkx3l/M9EahuwpSfeNvV63J5ezn3uZzapT0u7EYsXMjQV+0En5r" crossorigin="anonymous">
</head> </head>
<body> <body>
@ -20,7 +28,7 @@
<div class="margin50" ></div> <div class="margin50" ></div>
<h2>Users</h2> <h2>Users</h2>
<table> <table class="table">
<tr class="table_headline"> <tr class="table_headline">
<td>name</td> <td>name</td>
<td>questions answered</td> <td>questions answered</td>
@ -38,7 +46,7 @@
<div class="margin50" ></div> <div class="margin50" ></div>
<h2>Questions</h2> <h2>Questions</h2>
<table> <table class="table">
<tr class="table_headline"> <tr class="table_headline">
<td>title</td> <td>title</td>
<td>answers</td> <td>answers</td>
@ -57,7 +65,10 @@
<div class="margin50" ></div> <div class="margin50" ></div>
<hr/> <hr/>
<div class="container footer">
{{ settings.footer }} {{ settings.footer }}
</div>
</div> </div>

View file

@ -1,8 +1,9 @@
import datetime from __future__ import division
import json
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.http import HttpResponse from django.http import HttpResponse
from django.shortcuts import render_to_response from django.shortcuts import render_to_response
import datetime
import json
from tkupek_elearning.elearning.models import Setting, Question, Option, UserAnswer, User, UserAnswerOptions from tkupek_elearning.elearning.models import Setting, Question, Option, UserAnswer, User, UserAnswerOptions
@ -10,6 +11,13 @@ from tkupek_elearning.elearning.models import Setting, Question, Option, UserAns
import pdb import pdb
def get_progress(user):
progress_max = Question.objects.all().count()
progress_current = UserAnswer.objects.filter(user=user.id).count()
progress = str(int(progress_current / progress_max * 100))
return progress
def start(request): def start(request):
token = request.GET.get('token') token = request.GET.get('token')
@ -42,7 +50,9 @@ def start(request):
questions_options[question] = options questions_options[question] = options
questions_options[question] = options questions_options[question] = options
return render_to_response('elearning.html', {'settings': settings, 'questions_options': questions_options}) progress = get_progress(user)
return render_to_response('elearning.html', {'settings': settings, 'questions_options': questions_options, 'progress': progress})
else: else:
return render_to_response('access_denied.html', {'settings': settings}) return render_to_response('access_denied.html', {'settings': settings})
@ -95,7 +105,10 @@ def get_answer(request):
user_answer_options.option = Option.objects.get(id=option) user_answer_options.option = Option.objects.get(id=option)
user_answer_options.save() user_answer_options.save()
return HttpResponse(options_id) holder = {'options_id': options_id, 'progress': get_progress(user)}
holder = json.dumps(holder)
return HttpResponse(holder)
def get_user_answer(question, user): def get_user_answer(question, user):
@ -124,6 +137,8 @@ def statistic(request):
for question in questions: for question in questions:
question.answers = UserAnswer.objects.filter(question=question.id).count() question.answers = UserAnswer.objects.filter(question=question.id).count()
question.correct_answers = UserAnswer.objects.filter(question=question.id, correct=True).count() question.correct_answers = UserAnswer.objects.filter(question=question.id, correct=True).count()
question.correct_answers_percentage = question.correct_answers / question.answers if question.answers:
question.correct_answers_percentage = str(int(question.correct_answers / question.answers * 100))
question.correct_answers_percentage += str(' %')
return render_to_response('statistic.html', {'settings': settings, 'users': users, 'questions': questions}) return render_to_response('statistic.html', {'settings': settings, 'users': users, 'questions': questions})