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

@ -24,4 +24,4 @@ class UserAdmin(admin.ModelAdmin):
admin.site.register(Question, QuestionAdmin)
admin.site.register(Setting),
admin.site.register(User, UserAdmin)
admin.site.register(User, UserAdmin)

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_access_denied = 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)
active = models.BooleanField(unique=True, default=False)
def __str__(self):
def __unicode__(self):
return self.title
@ -26,16 +28,16 @@ class Question(models.Model):
text = models.TextField(null=True)
explanation = models.TextField(null=True)
def __str__(self):
def __unicode__(self):
return self.title
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)
question = models.ForeignKey(Question, on_delete=models.CASCADE, null=False)
def __str__(self):
def __unicode__(self):
return self.text
@ -49,7 +51,7 @@ class User(models.Model):
name = models.CharField(max_length=100, null=False)
last_seen = models.DateTimeField(null=True)
def __str__(self):
def __unicode__(self):
return self.name
@ -58,8 +60,8 @@ class UserAnswer(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE, null=False)
correct = models.NullBooleanField(null=True)
def __str__(self):
return str(self.user) + " - " + str(self.question)
def __unicode__(self):
return str(self.user) + " - " + str(self.question.id)
class Meta:
unique_together = (('user', 'question'),)
@ -71,3 +73,6 @@ class UserAnswerOptions(models.Model):
class Meta:
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;
}
.correct {
outline: 4px solid #00a353;
}
.failed {
outline: 4px solid #a60000;
.margin75 {
margin-top: 75px;
}
.mainLogo {
@ -54,4 +50,8 @@ p[id^="explanation_"] {
.table_headline {
font-weight: bold;
}
.footer {
color: #b3b3b3
}

View file

@ -59,8 +59,8 @@ function toggle_solution(id)
function show_solution(id)
{
var element = document.getElementById("explanation_" + id);
if (element.className == "hide") {
element.className = "show";
if (element.className.indexOf("hide") > -1) {
element.className = element.className.replace("hide", "show");
}
}
@ -76,9 +76,10 @@ function get_answer(id) {
if (answerRequest.readyState==4){
if (answerRequest.status==200 || window.location.href.indexOf("http")==-1){
parseResponse(id, answerRequest.responseText);
setButtonToNext(id);
}
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)
}
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) {
var answers = [];
@ -107,32 +137,45 @@ function getCheckboxAnswers(id) {
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 holders = document.getElementsByName("checkbox_div_" + id);
for(var i = 0; i < elements.length; i++) {
var element = elements[i];
element.className="";
for(var i = 0; i < elements.length; i++) {
var element = elements[i];
var holder = holders[i];
var correct = isCorrectOption(element.value);
if(correct) {
element.className="correct";
} else {
if(element.checked) {
element.className="failed";
var correct = isCorrectOption(element.value);
if(correct) {
holder.className = holder.className + " list-group-item-success";
} else {
if(element.checked) {
holder.className = holder.className + " list-group-item-danger";;
}
}
}
}
element.disabled = true;
}
function isCorrectOption(option) {
for(var i = 0; i < correctOptions.length; i++) {
if(correctOptions[i] == option) {
return true;
function isCorrectOption(option) {
for(var i = 0; i < correctOptions.length; i++) {
if(correctOptions[i] == option) {
return true;
}
}
return false;
}
return false;
}
}
function setProgress(progress) {
progressbar = document.getElementById("progressbar");
progressbar.style.width = progress + "%";
}
function ajaxRequest() {

View file

@ -7,7 +7,15 @@
{% load staticfiles %}
<link rel="stylesheet" href="{% static 'css/style.css' %}">
<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>
<!-- 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>
<body>

View file

@ -7,19 +7,33 @@
{% load staticfiles %}
<link rel="stylesheet" href="{% static 'css/style.css' %}">
<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>
<!-- 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>
<body>
<div class="container">
<img src="{{ settings.logo }}" class="mainLogo">
<h1>{{ settings.title }}</h1>
<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/>
<div class="margin50" ></div>
{% for question, options in questions_options.items %}
<div class="container question" name="question" id="question_{{ question.id }}">
<h2>{{ question.id }}: {{ question.title }}</h2>
<p>{{ question.text }}</p>
@ -27,19 +41,28 @@
{{ settings.message_already_answered }}
</p>
<form action="#">
{% for option in options %}
<ul class="list-group ">
{% for option in options %}
<li name="checkbox_div_{{ question.id }}" class="list-group-item">
<label>
<input name="checkbox_{{ question.id }}" type="checkbox" value="{{ option.id }}">
{{ option.text }}
</label>
{% endfor %}
<button class="margin10" type="button" id="showSolution_{{ question.id }}" onmousedown="get_answer({{ question.id }})">{{ settings.button_solution }}</button>
</form>
</li>
{% endfor %}
</ul>
<p id="explanation_{{ question.id }}" class="hide">{{ question.explanation }}</p>
<div class="margin25"></div>
<div class="container">
<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 %}
<div class="margin50" ></div>

View file

@ -7,7 +7,15 @@
{% load staticfiles %}
<link rel="stylesheet" href="{% static 'css/style.css' %}">
<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>
<!-- 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>
<body>
@ -20,7 +28,7 @@
<div class="margin50" ></div>
<h2>Users</h2>
<table>
<table class="table">
<tr class="table_headline">
<td>name</td>
<td>questions answered</td>
@ -38,7 +46,7 @@
<div class="margin50" ></div>
<h2>Questions</h2>
<table>
<table class="table">
<tr class="table_headline">
<td>title</td>
<td>answers</td>
@ -57,7 +65,10 @@
<div class="margin50" ></div>
<hr/>
{{ settings.footer }}
<div class="container footer">
{{ settings.footer }}
</div>
</div>

View file

@ -1,8 +1,9 @@
import datetime
import json
from __future__ import division
from django.core.exceptions import ObjectDoesNotExist
from django.http import HttpResponse
from django.shortcuts import render_to_response
import datetime
import json
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
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):
token = request.GET.get('token')
@ -42,7 +50,9 @@ def start(request):
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:
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.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):
@ -124,6 +137,8 @@ def statistic(request):
for question in questions:
question.answers = UserAnswer.objects.filter(question=question.id).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})