Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update to match original repository #1

Open
wants to merge 23 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
95390dd
add simple quiz list
simennj Jan 3, 2018
e002e02
add simple timer to quiz
simennj Jan 4, 2018
f8ca004
slugify name when creating quiz
simennj Jan 4, 2018
77cfa07
quiz duration in results page
simennj Jan 4, 2018
671ca06
register attempts on questions in database
simennj Jan 4, 2018
1b35c5c
make answer in question attempts nullable
simennj Jan 4, 2018
09b6213
show time used for all questions attempts in result page
simennj Jan 4, 2018
08d98b6
fix bug with answer not always being highlighted on result page
simennj Jan 4, 2018
3177d45
add filter by max answered count in question search
simennj Jan 5, 2018
51b21f4
order by answered count ascending in question search
simennj Jan 5, 2018
465b87c
remove deprecated answered field in answer
simennj Jan 5, 2018
601f8c2
make quiz string representation more human readable
simennj Jan 5, 2018
8cb0a72
add button to select first n questions
simennj Jan 5, 2018
589b265
fix autofill of filter from query
simennj Jan 5, 2018
355e939
autofill max_answered field from query
simennj Jan 5, 2018
2d52431
add shortcut to quiz list in navigation bar
simennj Jan 5, 2018
7f94086
fix migration issue
simennj Jan 11, 2018
366eafd
quick and dirty simulation of old quiz system
simennj Jan 18, 2018
eeb6031
even more quick and dirty fs_east quiz type
simennj Jan 19, 2018
673e81e
30s -> 90s penalty for wrong answers in quiz_east
simennj Jan 19, 2018
51c0c12
add numbering to questions in east type quiz
simennj Jan 19, 2018
a690a3b
improve visual feedback in east type quiz results
simennj Jan 19, 2018
84826c7
randomize order of questions in normal quiz
simennj Jan 21, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions fs_quiz/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,16 @@
from django.contrib import admin
from django.urls import path

from quiz.views import SearchView, QuizCreateView, QuizView, QuizResultView, QuestionCreateView
from quiz.views import SearchView, QuizCreateView, QuizListView, QuizView, QuizResultView, QuestionCreateView

urlpatterns = [
path('', SearchView.as_view()),
path('create_quiz', QuizCreateView.as_view(), name='create_quiz'),
path('create_question', QuestionCreateView.as_view(), name='create_question'),
path('quiz/<slug:pk>', QuizView.as_view(), name='quiz'),
path('quiz', QuizListView.as_view(), name='quiz_list'),
path('quiz/<slug:pk>', QuizView.as_view(template_name='quiz.html'), name='quiz'),
path('quiz_old/<slug:pk>', QuizView.as_view(template_name='quiz_old.html'), name='quiz_old'),
path('quiz_east/<slug:pk>', QuizView.as_view(template_name='quiz_east.html'), name='quiz_east'),
path('quiz_result/<slug:pk>', QuizResultView.as_view(), name='quiz_result'),
path('admin/', admin.site.urls),
]
22 changes: 22 additions & 0 deletions quiz/migrations/0006_questionattempt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Generated by Django 2.0 on 2018-01-04 12:44

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
('quiz', '0005_question_timestamp'),
]

operations = [
migrations.CreateModel(
name='QuestionAttempt',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('duration', models.FloatField()),
('answer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='quiz.Answer')),
('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='quiz.Question')),
],
),
]
18 changes: 18 additions & 0 deletions quiz/migrations/0007_questionattempt_nullable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 2.0 on 2018-01-04 13:27

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
('quiz', '0006_questionattempt'),
]

operations = [
migrations.AlterField(
model_name='questionattempt',
name='answer',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='quiz.Answer'),
),
]
16 changes: 16 additions & 0 deletions quiz/migrations/0008_remove_answer_answered.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Generated by Django 2.0 on 2018-01-05 15:17

from django.db import migrations


class Migration(migrations.Migration):
dependencies = [
('quiz', '0007_questionattempt_nullable'),
]

operations = [
migrations.RemoveField(
model_name='answer',
name='answered',
),
]
20 changes: 12 additions & 8 deletions quiz/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,6 @@ class Question(models.Model):
tags = models.ManyToManyField(Tag)
timestamp = models.DateTimeField(auto_now=True)

def get_answer_count(self):
return self.answer_set.aggregate(models.Sum('answered'))['answered__sum']

def get_wrong_answer_count(self):
return self.answer_set.filter(correct=False).aggregate(
models.Sum('answered'))['answered__sum']

def shuffled_answers(self):
return self.answer_set.order_by('?')

Expand All @@ -30,7 +23,6 @@ def __str__(self):
class Answer(models.Model):
text = models.TextField()
correct = models.BooleanField()
answered = models.IntegerField(default=0)
question = models.ForeignKey(to=Question, on_delete=models.CASCADE)

def __str__(self):
Expand All @@ -40,3 +32,15 @@ def __str__(self):
class Quiz(models.Model):
name = models.SlugField(max_length=16, primary_key=True)
questions = models.ManyToManyField(Question)

def shuffled_questions(self):
return self.questions.order_by('?')

def __str__(self):
return self.name


class QuestionAttempt(models.Model):
question = models.ForeignKey(to=Question, on_delete=models.CASCADE)
answer = models.ForeignKey(to=Answer, null=True, on_delete=models.CASCADE)
duration = models.FloatField()
3 changes: 3 additions & 0 deletions quiz/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
<a class="item" href="{% url 'create_question' %}">
Create Question
</a>
<a class="item" href="{% url 'quiz_list' %}">
Quiz list
</a>
</div>
{% block content %}
<h1>Hello, world!</h1>
Expand Down
64 changes: 48 additions & 16 deletions quiz/templates/quiz.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,46 +7,79 @@
<h1 class="ui header">{{ quiz.name }}</h1>
<form action="{% url 'quiz_result' quiz.pk %}" method="post">
{% csrf_token %}
{% for question in quiz.questions.all %}
<div class="ui question tab segment"
<div class="ui question active tob attached tab segment" data-tab="0">
<a class="ui huge primary next button" tabindex="0"
onclick="nextTab()">Start</a>
</div>
{% for question in quiz.shuffled_questions %}
<div class="ui question left aligned top attached tab segment"
data-tab="{{ forloop.counter }}">
<h2>{{ question.text }}</h2>
<div class="ui divider"></div>
<div class="ui massive form">
<div class="grouped {{ forloop.counter }} fields">
{% for answer in question.shuffled_answers %}
<div class="field">
<input id="{{ answer.pk }}" name="{{ question.pk }}" value="{{ answer.pk }}"
tabindex="0"
type="radio">
<label for="{{ answer.pk }}">{{ answer.text }}</label>
<input id="{{ answer.pk }}" name="{{ question.pk }}" value="{{ answer.pk }}"
tabindex="0"
type="radio">
<label for="{{ answer.pk }}">{{ answer.text }}</label>
</div>
{% endfor %}
</div>
</div>
{% if forloop.last %}
<button class="ui huge primary {{ question.pk }} button"
tabindex="1">Submit<i class="right arrow icon"></i></button>
tabindex="1">Submit
</button>
{% else %}
<a class="ui huge primary next button" tabindex="1"
onclick="nextTab()">Next<i class="right arrow icon"></i></a>
{% endif %}
<div class="ui disabled input" style="float: right;">
<input id="timer{{ forloop.counter }}" title="timer{{ forloop.counter }}"
name="timer{{ question.pk }}" type="text" style="width: 4.6em; text-align: center">
</div>
</div>
{% endfor %}
<div class="ui clearing bottom attached segment">
<div class="ui disabled input" style="float: right">
<input id="timer0" title="timer0" name="timer" type="text"
style="width: 4.65em; text-align: center">
</div>
<span id="pagenumber" style="float: left; padding: 0.678571em"></span>
</div>
</form>
<div id="pagenumber"></div>
</div>
<script>
var tab = 0;
var tabs = {{ quiz.questions.count }}
$('.ui.radio.checkbox')
.checkbox()
;
$
.tab()
;
var tabs = {{ quiz.questions.count }};
var startTimes = [];
var mainTimer = $('#timer0');

$('.ui.radio.checkbox').checkbox();

$.tab();

function getTimeSince(startTimeIndex) {
return ((Date.now() - startTimes[startTimeIndex]) / 1000).toFixed(1)
}

function startTimer() {
setInterval(
function () {
mainTimer.val(getTimeSince(0));
$('#timer' + tab).val(getTimeSince(tab - 1));
},
100
)
}

function nextTab() {
startTimes.push(Date.now());
if (startTimes.length === 1) {
startTimer();
}
if (++tab > tabs) {
$('form').submit();
}
Expand All @@ -63,7 +96,6 @@ <h2>{{ question.text }}</h2>
e.preventDefault()
}
});
nextTab();
</script>
{% endblock %}
{% block head %}
Expand Down
133 changes: 133 additions & 0 deletions quiz/templates/quiz_east.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
{% extends 'base.html' %}
{% block title %}
{{ quiz.name }}
{% endblock %}
{% block content %}
<div class="ui center aligned container">
<h1 class="ui header">{{ quiz.name }}</h1>
<div class="ui start segment">
<a class="ui huge primary next button" tabindex="0"
onclick="start()">Start</a>
</div>
{% for question in quiz.questions.all %}
<div class="ui question left aligned segment">
<h2>{{ forloop.counter }}: {{ question.text }}</h2>
<div class="ui divider"></div>
<div class="ui massive form">
<div class="grouped {{ forloop.counter }} fields">
{% for answer in question.shuffled_answers %}
<div class="field">
<input id="{{ answer.pk }}" name="{{ question.pk }}" value="{{ answer.pk }}"
class="answer {% if answer.correct %}correct{% endif %}" tabindex="0"
type="radio">
<label for="{{ answer.pk }}">{{ answer.text }}</label>
</div>
{% endfor %}
</div>
</div>
</div>
{% endfor %}
<div class="ui clearing bottom segment">
<a class="ui left floated massive primary attempt button" tabindex="1" onclick="submit()">Submit</a>
<span id="results" style="display: inline-block; font-size: 1.71428571em; padding-top: 0.785714em"></span>
<div class="ui disabled massive input" style="float: right">
<input id="timer" title="timer" name="timer" type="text"
style="width: 4.65em; text-align: center">
</div>
</div>
</div>
<script>
var questions = {{ quiz.questions.count }};
var timerText = $('#timer');
var timer;
var startTime;
var button = $('.attempt.button');


$('.ui.radio.checkbox').checkbox();

$('.ui.question.segment').hide();
$('.ui.bottom.segment').hide();

function startTimer() {
startTime = Date.now();
timer = setInterval(
function () {
timerText.val(((Date.now() - startTime) / 1000).toFixed(1));
},
100
)
}

function start() {
startTimer();
$('.ui.segment').show();
$('.ui.start.segment').hide();
}

function submit() {
$('.answer:checked').addClass('answered');
$('.answer:not(:checked)').addClass('not-answered');
var correctCount = $('.correct:checked').length;
clearInterval(timer);
$('#results').html(correctCount + '/' + questions);
button.addClass('disabled');
button.html((Date.now() - startTime) / 1000 + 90 * (questions - correctCount))
}
</script>
{% endblock %}
{% block head %}
<style>

.ui.form .field > label {
font-weight: normal;
line-height: 1.1em;
cursor: pointer;
}

.ui.form .field input:checked + label {
font-weight: bold;
}

.ui.form .field label:before {
font-family: Icons;
content: "\f10c";
padding-right: 1em;
}

.ui.form .field .answer:checked + label:before {
content: "\f05d";
}

.ui.form .field .answered:checked + label:before {
content: "\f05c";
}

.ui.form .field .answered.correct:checked + label:before {
content: "\f05d";
}

.ui.form .field input {
opacity: 0;
position: absolute;
}

.answered + label {
color: red !important;
}

.not-answered + label {
color: grey !important;
}

.correct.not-answered + label {
text-decoration: underline;
color: black !important;
}

.answered.correct + label {
color: green !important;
}

</style>
{% endblock %}
16 changes: 16 additions & 0 deletions quiz/templates/quiz_list.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{% extends 'base.html' %}
{% block content %}
<div class="ui center aligned container">
<div class="ui massive list">
{% for quiz in quiz_list %}
<div class="item">
<div class="content">
<a href="{% url 'quiz' quiz.name %}">
{{ quiz.name }}
</a>
</div>
</div>
{% endfor %}
</div>
</div>
{% endblock content %}
Loading