improving physics / sba exams
This commit is contained in:
@@ -581,3 +581,49 @@ function delete_multiple(url, csrf_token) {
|
|||||||
}
|
}
|
||||||
window.delete_multiple = delete_multiple
|
window.delete_multiple = delete_multiple
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function getTimeRemaining(endtime) {
|
||||||
|
const total = Date.parse(endtime) - Date.parse(new Date());
|
||||||
|
const seconds = Math.floor((total / 1000) % 60);
|
||||||
|
const minutes = Math.floor((total / 1000 / 60) % 60);
|
||||||
|
const hours = Math.floor((total / (1000 * 60 * 60)) % 24);
|
||||||
|
//const days = Math.floor(total / (1000 * 60 * 60 * 24));
|
||||||
|
|
||||||
|
return {
|
||||||
|
total,
|
||||||
|
//days,
|
||||||
|
hours,
|
||||||
|
minutes,
|
||||||
|
seconds
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function initializeClock(id, endtime) {
|
||||||
|
const clock = document.getElementById(id);
|
||||||
|
clock.style.display = "block"
|
||||||
|
const daysSpan = clock.querySelector('.days');
|
||||||
|
const hoursSpan = clock.querySelector('.hours');
|
||||||
|
const minutesSpan = clock.querySelector('.minutes');
|
||||||
|
const secondsSpan = clock.querySelector('.seconds');
|
||||||
|
|
||||||
|
function updateClock() {
|
||||||
|
const t = getTimeRemaining(endtime);
|
||||||
|
|
||||||
|
//daysSpan.innerHTML = t.days;
|
||||||
|
hoursSpan.innerHTML = ('0' + t.hours).slice(-2);
|
||||||
|
minutesSpan.innerHTML = ('0' + t.minutes).slice(-2);
|
||||||
|
secondsSpan.innerHTML = ('0' + t.seconds).slice(-2);
|
||||||
|
|
||||||
|
if (t.total <= 0) {
|
||||||
|
clearInterval(timeinterval);
|
||||||
|
timeup.style.display = "block"
|
||||||
|
clock.style.display = "none"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeinterval = setInterval(updateClock, 1000);
|
||||||
|
updateClock();
|
||||||
|
}
|
||||||
|
|
||||||
|
window.initializeClock = initializeClock
|
||||||
+18
-1
@@ -1,4 +1,5 @@
|
|||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
import datetime
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
from typing import Self, Tuple
|
from typing import Self, Tuple
|
||||||
@@ -712,6 +713,20 @@ class ExamBase(ExamCollectionGenericBase):
|
|||||||
"{}:exam_json_cid".format(self.app_name), args=(self.pk, cid, passcode)
|
"{}:exam_json_cid".format(self.app_name), args=(self.pk, cid, passcode)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_time_limit(self):
|
||||||
|
"""Returns a human readable time limit"""
|
||||||
|
h, m, s = str(datetime.timedelta(seconds=self.time_limit)).split(":")
|
||||||
|
|
||||||
|
time_limit = ""
|
||||||
|
if s != "0":
|
||||||
|
time_limit = f" {s} second(s)"
|
||||||
|
if m != "0":
|
||||||
|
time_limit = f"{m} minute(s) {time_limit}"
|
||||||
|
if h != "0":
|
||||||
|
time_limit = f"{h} hour(s) {time_limit}"
|
||||||
|
|
||||||
|
return time_limit.strip()
|
||||||
|
|
||||||
def get_question_index(self, question):
|
def get_question_index(self, question):
|
||||||
return list(self.exam_questions.all()).index(question)
|
return list(self.exam_questions.all()).index(question)
|
||||||
|
|
||||||
@@ -955,10 +970,12 @@ class CidUser(models.Model):
|
|||||||
def check_passcode(self, passcode) -> bool:
|
def check_passcode(self, passcode) -> bool:
|
||||||
return self.passcode == passcode
|
return self.passcode == passcode
|
||||||
|
|
||||||
def get_cid_exams(self):
|
def get_cid_exams(self, include_case_collections=True):
|
||||||
available_exams = []
|
available_exams = []
|
||||||
|
|
||||||
for n, t in EXAM_TYPES:
|
for n, t in EXAM_TYPES:
|
||||||
|
if not include_case_collections and n == "casecollection":
|
||||||
|
continue
|
||||||
exam_rel = getattr(self, t)
|
exam_rel = getattr(self, t)
|
||||||
if exam_rel.exists():
|
if exam_rel.exists():
|
||||||
exams = exam_rel.filter(exam_mode=True, archive=False).order_by("name")
|
exams = exam_rel.filter(exam_mode=True, archive=False).order_by("name")
|
||||||
|
|||||||
@@ -11,6 +11,10 @@
|
|||||||
<p><input id="cid-box" type="text" value="Candidate ID"></p>
|
<p><input id="cid-box" type="text" value="Candidate ID"></p>
|
||||||
<p><input id="passcode-box" type="text" value="Passcode"></p>
|
<p><input id="passcode-box" type="text" value="Passcode"></p>
|
||||||
|
|
||||||
|
{% if exam.time_limit %}
|
||||||
|
This exam has a time limit of {{ exam.get_time_limit }}. The time will start when you click the Start Exam button below.<br/>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<button>Start exam</button>
|
<button>Start exam</button>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|||||||
@@ -2,34 +2,13 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<span id="user-id">
|
<span id="user-id">
|
||||||
{% if request.user.is_authenticated %}
|
{% if request.user.is_authenticated %}
|
||||||
User: {{request.user}}
|
User: {{request.user}}
|
||||||
{% else %}
|
{% else %}
|
||||||
CID: {{cid}}
|
CID: {{cid}}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</span>
|
</span>
|
||||||
<div id="timeup">
|
{% include "exam_clock.html" %}
|
||||||
Allocated time over.
|
|
||||||
</div>
|
|
||||||
<div id="clockdiv">
|
|
||||||
Time remaining:
|
|
||||||
{% comment %} <div>
|
|
||||||
<span class="days"></span>
|
|
||||||
<div class="smalltext">Days</div>
|
|
||||||
</div> {% endcomment %}
|
|
||||||
<div>
|
|
||||||
<span class="hours num"></span>
|
|
||||||
<span class="smalltext">Hours</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span class="minutes num"></span>
|
|
||||||
<span class="smalltext">Minutes</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span class="seconds num"></span>
|
|
||||||
<span class="smalltext">Seconds</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="no-select">
|
<div class="no-select">
|
||||||
<h2>{{exam.name}}: Question [<span id="question-number">{{pos|add:1}}</span>/<span id="exam-length">{{exam_length}}</span>]</h2>
|
<h2>{{exam.name}}: Question [<span id="question-number">{{pos|add:1}}</span>/<span id="exam-length">{{exam_length}}</span>]</h2>
|
||||||
{% if exam.publish_results %}
|
{% if exam.publish_results %}
|
||||||
@@ -54,7 +33,11 @@
|
|||||||
{{ form.a.errors }}
|
{{ form.a.errors }}
|
||||||
<label for="{{ form.a.id_for_label }}" class="flex-8 question-text">{{question.a|safe}}</label>
|
<label for="{{ form.a.id_for_label }}" class="flex-8 question-text">{{question.a|safe}}</label>
|
||||||
<span class="flex-1">{{ form.a }}<span class="postinput"></span></span>
|
<span class="flex-1">{{ form.a }}<span class="postinput"></span></span>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
{% if exam.publish_results and question.a_feedback %}
|
||||||
|
<span class="feedback">Feedback: {{question.a_feedback}}</span>
|
||||||
|
{% endif %}
|
||||||
</li>
|
</li>
|
||||||
<li data-ans="b">
|
<li data-ans="b">
|
||||||
<div class="fieldWrapper flex-container">
|
<div class="fieldWrapper flex-container">
|
||||||
@@ -88,22 +71,18 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if previous > -1 %}
|
{% if previous > -1 %}
|
||||||
<button type="submit" name="previous" class="save btn btn-default">Previous</button>
|
<button type="submit" name="previous" class="save btn btn-default" title="Click to save your answer(s) and go to the previous question">Previous</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if next %}
|
{% if next %}
|
||||||
<button type="submit" name="next" class="save btn btn-default">Next</button>
|
<button type="submit" name="next" class="save btn btn-default" title="Click to save your answer(s) and go to the next quesiton">Next</button>
|
||||||
{% else %}
|
{% else %}
|
||||||
{% if not exam.publish_results %}
|
{% if not exam.publish_results %}
|
||||||
<button type="submit" name="save" class="save btn btn-default">Save</button>
|
<button type="submit" name="save" class="save btn btn-default" title="Click to save your current answer(s)">Save</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% comment %} {% if exam.publish_results and question.feedback %}
|
|
||||||
<h3>Feedback</h3>
|
|
||||||
<p>{{question.feedback|safe}}</p>
|
|
||||||
{% endif %} {% endcomment %}
|
|
||||||
<br />
|
<br />
|
||||||
<button type="submit" name="finish" class="save btn btn-default">Overview</button>
|
<button type="submit" name="finish" class="save btn btn-default" title="Click to go to the overview page (your answers will be saved).">Overview</button>
|
||||||
<button type="submit" id="goto-button" value="test" name="goto" class="hide">goto</button>
|
<button type="submit" id="goto-button" value="test" name="goto" class="hide">goto</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -113,6 +92,11 @@
|
|||||||
<div id="menu-list">
|
<div id="menu-list">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
<details><summary>Help</summary>
|
||||||
|
<p>Each question contains a list of 5 different statements. These are either True or False</p>
|
||||||
|
<p>Click on each statement to toggle between True and False. The current state can be seen to the right of the statement. </p>
|
||||||
|
<p>Your answers are saved when navigating between questions (or if the save button is clicked on the final question). An overview of all the questions can be seen by clicking on the overview button (or by clicking <a target="_blank" href="{% url 'physics:exam_scores_cid_user' pk=exam.pk cid=cid passcode=passcode %}">here</a>).</p>
|
||||||
|
</details>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block js %}
|
{% block js %}
|
||||||
@@ -151,8 +135,10 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
for (let i = 0; i < {{ exam_length }}; i++) {
|
for (let i = 0; i < {{ exam_length }}; i++) {
|
||||||
$("#menu-list").append($(
|
qbutton = $(`<button class="question-menu-item" name="goto-${i}" data-qn="${i}">${i+1}</button>`)
|
||||||
`<button class="question-menu-item" name="goto-${i}" data-qn="${i}">${i+1}</button>`));
|
|
||||||
|
if (i == {{pos}}) {qbutton.addClass("current-question")}
|
||||||
|
$("#menu-list").append(qbutton);
|
||||||
|
|
||||||
}
|
}
|
||||||
$("button.question-menu-item").on("click", (e) => {
|
$("button.question-menu-item").on("click", (e) => {
|
||||||
@@ -164,48 +150,6 @@
|
|||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
function getTimeRemaining(endtime) {
|
|
||||||
const total = Date.parse(endtime) - Date.parse(new Date());
|
|
||||||
const seconds = Math.floor((total / 1000) % 60);
|
|
||||||
const minutes = Math.floor((total / 1000 / 60) % 60);
|
|
||||||
const hours = Math.floor((total / (1000 * 60 * 60)) % 24);
|
|
||||||
//const days = Math.floor(total / (1000 * 60 * 60 * 24));
|
|
||||||
|
|
||||||
return {
|
|
||||||
total,
|
|
||||||
//days,
|
|
||||||
hours,
|
|
||||||
minutes,
|
|
||||||
seconds
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function initializeClock(id, endtime) {
|
|
||||||
const clock = document.getElementById(id);
|
|
||||||
clock.style.display = "block"
|
|
||||||
const daysSpan = clock.querySelector('.days');
|
|
||||||
const hoursSpan = clock.querySelector('.hours');
|
|
||||||
const minutesSpan = clock.querySelector('.minutes');
|
|
||||||
const secondsSpan = clock.querySelector('.seconds');
|
|
||||||
|
|
||||||
function updateClock() {
|
|
||||||
const t = getTimeRemaining(endtime);
|
|
||||||
|
|
||||||
//daysSpan.innerHTML = t.days;
|
|
||||||
hoursSpan.innerHTML = ('0' + t.hours).slice(-2);
|
|
||||||
minutesSpan.innerHTML = ('0' + t.minutes).slice(-2);
|
|
||||||
secondsSpan.innerHTML = ('0' + t.seconds).slice(-2);
|
|
||||||
|
|
||||||
if (t.total <= 0) {
|
|
||||||
clearInterval(timeinterval);
|
|
||||||
timeup.style.display = "block"
|
|
||||||
clock.style.display = "none"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const timeinterval = setInterval(updateClock, 1000);
|
|
||||||
updateClock();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -232,14 +176,18 @@
|
|||||||
border: 1px solid purple;
|
border: 1px solid purple;
|
||||||
}
|
}
|
||||||
|
|
||||||
.answer-true::after {
|
.answer-true::before {
|
||||||
content: "[Correct answer: True]";
|
content: "[Correct answer: True]";
|
||||||
color: darkgray;
|
color: darkgray;
|
||||||
|
float: right;
|
||||||
|
opacity: 50%;
|
||||||
|
|
||||||
}
|
}
|
||||||
.answer-false::after {
|
.answer-false::before {
|
||||||
content: "[Correct answer: False]";
|
content: "[Correct answer: False]";
|
||||||
color: darkgray;
|
color: darkgray;
|
||||||
|
float: right;
|
||||||
|
opacity: 50%;
|
||||||
|
|
||||||
}
|
}
|
||||||
.answer-correct {
|
.answer-correct {
|
||||||
@@ -249,6 +197,11 @@
|
|||||||
border: 1px dashed darkred;
|
border: 1px dashed darkred;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.physics-answer-list li {
|
||||||
|
margin-top: 5px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
.physics-answer-list input:checked+.postinput::after {
|
.physics-answer-list input:checked+.postinput::after {
|
||||||
margin-left: 20px;
|
margin-left: 20px;
|
||||||
content: "True"
|
content: "True"
|
||||||
@@ -258,35 +211,10 @@
|
|||||||
content: "False"
|
content: "False"
|
||||||
}
|
}
|
||||||
|
|
||||||
#clockdiv{
|
.feedback {
|
||||||
font-family: sans-serif;
|
padding: 20px;
|
||||||
display: inline-block;
|
display: block;
|
||||||
font-weight: 100;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 12px;
|
|
||||||
display: none;
|
|
||||||
color: darkgray;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#clockdiv > div{
|
|
||||||
padding: 1px;
|
|
||||||
border-radius: 3px;
|
|
||||||
{% comment %} background: purple; {% endcomment %}
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
#clockdiv div > span.num{
|
|
||||||
color: white;
|
|
||||||
padding: 1px;
|
|
||||||
border-radius: 3px;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
#timeup {
|
|
||||||
display: none;
|
|
||||||
color: red;
|
|
||||||
font-weight: 100;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
<span id="user-id">
|
||||||
{% if request.user.is_authenticated %}
|
{% if request.user.is_authenticated %}
|
||||||
User: {{request.user}}
|
User: {{request.user}}
|
||||||
{% else %}
|
{% else %}
|
||||||
CID: {{cid}}
|
CID: {{cid}}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</span>
|
||||||
|
{% include "exam_clock.html" %}
|
||||||
|
|
||||||
<h2>Exam: {{exam.name}}</h2>
|
<h2>Exam: {{exam.name}}</h2>
|
||||||
|
|
||||||
@@ -20,27 +23,44 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if not exam.publish_results and answer_count != exam_length %}
|
||||||
|
<div id="unanswered-questions-alert" class="alert alert-warning" role="alert">
|
||||||
|
You have unanswered questions.
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
<div><p>Questions</p></div>
|
<div class="overview-text">{{answer_count}} out of {{exam_length}} questions answered. Unanswered questions are shown in red. <p>Click to go to a question.</p></div>
|
||||||
<div class="overview-text">{{answer_count}} out of {{exam_length}} questions answered. Click to go to question.</div>
|
|
||||||
<div class="physics-finish-list">
|
<div class="physics-finish-list">
|
||||||
{% for question, answer in question_answer_tuples %}
|
{% for question, answer in question_answer_tuples %}
|
||||||
{% if request.user.is_authenticated %}
|
{% if request.user.is_authenticated %}
|
||||||
<a href="{% url 'physics:exam_take_user' pk=exam.id sk=forloop.counter0 %}"><button {% if not answer %}class="unanswered"{% endif %}>{{forloop.counter}}: {{answer.answer}}</button></a>
|
<a href="{% url 'physics:exam_take_user' pk=exam.id sk=forloop.counter0 %}"><button {% if not answer %}class="unanswered" title="You have not answered this question"{% endif %}>{{forloop.counter}}: {{answer.answer}}</button></a>
|
||||||
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="{% url 'physics:exam_take' pk=exam.id sk=forloop.counter0 cid=cid passcode=passcode %}"><button {% if not answer %}class="unanswered"{% endif %}>{{forloop.counter}}: {{answer.answer}}</button></a>
|
<a href="{% url 'physics:exam_take' pk=exam.id sk=forloop.counter0 cid=cid passcode=passcode %}"><button {% if not answer %}class="unanswered" title="You have not answered this question"{% endif %}>{{forloop.counter}}: {{answer.answer}}</button></a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
<div id="time-details">
|
||||||
Start time: {{cid_user_exam.start_time}}<br/>
|
Start time: {{cid_user_exam.start_time}}<br/>
|
||||||
Last change time: {{cid_user_exam.end_time}}
|
Last change time: {{cid_user_exam.end_time}}
|
||||||
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block js %}
|
{% block js %}
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(() => {
|
$(document).ready(() => {
|
||||||
|
{% if not exam.publish_results %}
|
||||||
|
|
||||||
|
let time_limit = '{{exam.time_limit}}'
|
||||||
|
if (time_limit != "None") {
|
||||||
|
let end_time = new Date({{cid_user_exam.start_time|date:"U"}}*1000+parseInt('{{exam.time_limit}}')*1000);
|
||||||
|
initializeClock("clockdiv", end_time);
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -73,7 +73,8 @@ a:link {
|
|||||||
font-size: 20;
|
font-size: 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hide, .hidden {
|
.hide,
|
||||||
|
.hidden {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +84,12 @@ a:link {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.unanswered {
|
.unanswered {
|
||||||
color: gray;
|
color: darkred;
|
||||||
|
border-color: darkred;
|
||||||
|
}
|
||||||
|
|
||||||
|
.unanswered:hover {
|
||||||
|
background-color: rgb(166, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#question-list {
|
#question-list {
|
||||||
@@ -1057,7 +1063,50 @@ table .peninsula-trainee::before {
|
|||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-control, .form-control:focus, .form-control option {
|
.form-control,
|
||||||
|
.form-control:focus,
|
||||||
|
.form-control option {
|
||||||
background-color: unset;
|
background-color: unset;
|
||||||
color: unset
|
color: unset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#clockdiv {
|
||||||
|
font-family: sans-serif;
|
||||||
|
display: inline-block;
|
||||||
|
font-weight: 100;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 12px;
|
||||||
|
display: none;
|
||||||
|
color: darkgray;
|
||||||
|
}
|
||||||
|
|
||||||
|
#clockdiv>div {
|
||||||
|
padding: 1px;
|
||||||
|
border-radius: 3px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#clockdiv div>span.num {
|
||||||
|
color: white;
|
||||||
|
padding: 1px;
|
||||||
|
border-radius: 3px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#timeup {
|
||||||
|
display: none;
|
||||||
|
color: red;
|
||||||
|
font-weight: 100;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#time-details {
|
||||||
|
opacity: 50%;
|
||||||
|
font-size: small;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current-question {
|
||||||
|
background-color: #52057b;
|
||||||
|
color: grey;
|
||||||
|
}
|
||||||
+1
-1
@@ -245,7 +245,7 @@ def cid_scores(request, cid, passcode):
|
|||||||
|
|
||||||
case_collections = cid_user.casecollection_exams.all()
|
case_collections = cid_user.casecollection_exams.all()
|
||||||
|
|
||||||
available_exams = cid_user.get_cid_exams()
|
available_exams = cid_user.get_cid_exams(include_case_collections=False)
|
||||||
|
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
|
|||||||
@@ -10,6 +10,10 @@
|
|||||||
Enter your CID and passcode in the below boxes.<br />
|
Enter your CID and passcode in the below boxes.<br />
|
||||||
<p><input id="cid-box" type="text" value="Candidate ID"></p>
|
<p><input id="cid-box" type="text" value="Candidate ID"></p>
|
||||||
<p><input id="passcode-box" type="text" value="Passcode"></p>
|
<p><input id="passcode-box" type="text" value="Passcode"></p>
|
||||||
|
|
||||||
|
{% if exam.time_limit %}
|
||||||
|
This exam has a time limit of {{ exam.get_time_limit }}. The time will start when you click the Start Exam button below.<br/>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<button>Start exam</button>
|
<button>Start exam</button>
|
||||||
|
|
||||||
|
|||||||
@@ -2,34 +2,13 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<span id="user-id">
|
<span id="user-id">
|
||||||
{% if request.user.is_authenticated %}
|
{% if request.user.is_authenticated %}
|
||||||
User: {{request.user}}
|
User: {{request.user}}
|
||||||
{% else %}
|
{% else %}
|
||||||
CID: {{cid}}
|
CID: {{cid}}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</span>
|
</span>
|
||||||
<div id="timeup">
|
{% include "exam_clock.html" %}
|
||||||
Allocated time over.
|
|
||||||
</div>
|
|
||||||
<div id="clockdiv">
|
|
||||||
Time remaining:
|
|
||||||
{% comment %} <div>
|
|
||||||
<span class="days"></span>
|
|
||||||
<div class="smalltext">Days</div>
|
|
||||||
</div> {% endcomment %}
|
|
||||||
<div>
|
|
||||||
<span class="hours num"></span>
|
|
||||||
<span class="smalltext">Hours</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span class="minutes num"></span>
|
|
||||||
<span class="smalltext">Minutes</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span class="seconds num"></span>
|
|
||||||
<span class="smalltext">Seconds</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="no-select">
|
<div class="no-select">
|
||||||
<h2>{{exam.name}}: Question [<span id="question-number">{{pos|add:1}}</span>/<span id="exam-length">{{exam_length}}</span>]</h2>
|
<h2>{{exam.name}}: Question [<span id="question-number">{{pos|add:1}}</span>/<span id="exam-length">{{exam_length}}</span>]</h2>
|
||||||
{% if exam.publish_results %}
|
{% if exam.publish_results %}
|
||||||
@@ -58,13 +37,13 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if previous > -1 %}
|
{% if previous > -1 %}
|
||||||
<button type="submit" name="previous" class="save btn btn-default">Previous</button>
|
<button type="submit" name="previous" class="save btn btn-default" title="Click to save your answer(s) and go to the previous question">Previous</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if next %}
|
{% if next %}
|
||||||
<button type="submit" name="next" class="save btn btn-default">Next</button>
|
<button type="submit" name="next" class="save btn btn-default" title="Click to save your answer(s) and go to the next quesiton">Next</button>
|
||||||
{% else %}
|
{% else %}
|
||||||
{% if not exam.publish_results %}
|
{% if not exam.publish_results %}
|
||||||
<button type="submit" name="save" class="save btn btn-default">Save</button>
|
<button type="submit" name="save" class="save btn btn-default" title="Click to save your current answer(s)">Save</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
@@ -73,7 +52,7 @@
|
|||||||
<p>{{question.feedback|safe}}</p>
|
<p>{{question.feedback|safe}}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<br />
|
<br />
|
||||||
<button type="submit" name="finish" class="save btn btn-default">Overview</button>
|
<button type="submit" name="finish" class="save btn btn-default" title="Click to go to the overview page (your answers will be saved).">Overview</button>
|
||||||
<button type="submit" id="goto-button" value="test" name="goto" class="hide">goto</button>
|
<button type="submit" id="goto-button" value="test" name="goto" class="hide">goto</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -83,6 +62,11 @@
|
|||||||
<div id="menu-list">
|
<div id="menu-list">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
<details><summary>Help</summary>
|
||||||
|
<p>Each question contains a list of 5 different statements. One of these is the single BEST answer.</p>
|
||||||
|
<p>Click on the correct statement to select it. Once selected it will be highlighted. </p>
|
||||||
|
<p>Your answers are saved when navigating between questions (or if the save button is clicked on the final question). An overview of all the questions can be seen by clicking on the overview button (or by clicking <a target="_blank" href="{% url 'sbas:exam_scores_cid_user' pk=exam.pk cid=cid passcode=passcode %}">here</a>).</p>
|
||||||
|
</details>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block js %}
|
{% block js %}
|
||||||
@@ -113,8 +97,10 @@
|
|||||||
$("ul.sba-answer-list li[data-ans='{{saved_answer}}']").addClass("selected"); {% endif %}
|
$("ul.sba-answer-list li[data-ans='{{saved_answer}}']").addClass("selected"); {% endif %}
|
||||||
|
|
||||||
for (let i = 0; i < {{ exam_length }}; i++) {
|
for (let i = 0; i < {{ exam_length }}; i++) {
|
||||||
$("#menu-list").append($(
|
qbutton = $(`<button class="question-menu-item" name="goto-${i}" data-qn="${i}">${i+1}</button>`)
|
||||||
`<button class="question-menu-item" name="goto-${i}" data-qn="${i}">${i+1}</button>`));
|
|
||||||
|
if (i == {{pos}}) {qbutton.addClass("current-question")}
|
||||||
|
$("#menu-list").append(qbutton);
|
||||||
|
|
||||||
}
|
}
|
||||||
$("button.question-menu-item").on("click", (e) => {
|
$("button.question-menu-item").on("click", (e) => {
|
||||||
@@ -124,50 +110,6 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
function getTimeRemaining(endtime) {
|
|
||||||
const total = Date.parse(endtime) - Date.parse(new Date());
|
|
||||||
const seconds = Math.floor((total / 1000) % 60);
|
|
||||||
const minutes = Math.floor((total / 1000 / 60) % 60);
|
|
||||||
const hours = Math.floor((total / (1000 * 60 * 60)) % 24);
|
|
||||||
//const days = Math.floor(total / (1000 * 60 * 60 * 24));
|
|
||||||
|
|
||||||
return {
|
|
||||||
total,
|
|
||||||
//days,
|
|
||||||
hours,
|
|
||||||
minutes,
|
|
||||||
seconds
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function initializeClock(id, endtime) {
|
|
||||||
console.log("Start clock", endtime)
|
|
||||||
const clock = document.getElementById(id);
|
|
||||||
clock.style.display = "block"
|
|
||||||
const daysSpan = clock.querySelector('.days');
|
|
||||||
const hoursSpan = clock.querySelector('.hours');
|
|
||||||
const minutesSpan = clock.querySelector('.minutes');
|
|
||||||
const secondsSpan = clock.querySelector('.seconds');
|
|
||||||
|
|
||||||
function updateClock() {
|
|
||||||
const t = getTimeRemaining(endtime);
|
|
||||||
|
|
||||||
//daysSpan.innerHTML = t.days;
|
|
||||||
hoursSpan.innerHTML = ('0' + t.hours).slice(-2);
|
|
||||||
minutesSpan.innerHTML = ('0' + t.minutes).slice(-2);
|
|
||||||
secondsSpan.innerHTML = ('0' + t.seconds).slice(-2);
|
|
||||||
|
|
||||||
if (t.total <= 0) {
|
|
||||||
clearInterval(timeinterval);
|
|
||||||
timeup.style.display = "block"
|
|
||||||
clock.style.display = "none"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const timeinterval = setInterval(updateClock, 1000);
|
|
||||||
updateClock();
|
|
||||||
}
|
|
||||||
/* beautify ignore:end */
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -180,36 +122,15 @@
|
|||||||
.selected {
|
.selected {
|
||||||
border: 1px solid purple;
|
border: 1px solid purple;
|
||||||
}
|
}
|
||||||
|
li:hover::before {
|
||||||
#clockdiv{
|
content: "Click to select";
|
||||||
font-family: sans-serif;
|
float: right
|
||||||
display: inline-block;
|
|
||||||
font-weight: 100;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 12px;
|
|
||||||
display: none;
|
|
||||||
color: darkgray;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#clockdiv > div{
|
li:hover.selected::before {
|
||||||
padding: 1px;
|
content: "Chosen answer";
|
||||||
border-radius: 3px;
|
float: right
|
||||||
{% comment %} background: purple; {% endcomment %}
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
#clockdiv div > span.num{
|
|
||||||
color: white;
|
|
||||||
padding: 1px;
|
|
||||||
border-radius: 3px;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#timeup {
|
|
||||||
display: none;
|
|
||||||
color: red;
|
|
||||||
font-weight: 100;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -1,12 +1,15 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
<span id="user-id">
|
||||||
{% if request.user.is_authenticated %}
|
{% if request.user.is_authenticated %}
|
||||||
User: {{request.user}}
|
User: {{request.user}}
|
||||||
{% else %}
|
{% else %}
|
||||||
CID: {{cid}}
|
CID: {{cid}}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{% include "exam_clock.html" %}
|
||||||
<h2>Exam: {{exam.name}}</h2>
|
<h2>Exam: {{exam.name}}</h2>
|
||||||
|
|
||||||
{% if exam.publish_results %}
|
{% if exam.publish_results %}
|
||||||
@@ -21,26 +24,43 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div><p>Questions</p></div>
|
{% if not exam.publish_results and answer_count != exam_length %}
|
||||||
<div class="overview-text">{{answer_count}} out of {{exam_length}} questions answered. Click to go to question.</div>
|
<div id="unanswered-questions-alert" class="alert alert-warning" role="alert">
|
||||||
|
You have unanswered questions.
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
<div class="overview-text">{{answer_count}} out of {{exam_length}} questions answered. Unanswered questions are shown in red. <p>Click to go to a question.</p></div>
|
||||||
<div class="sba-finish-list">
|
<div class="sba-finish-list">
|
||||||
{% for question, answer in question_answer_tuples %}
|
{% for question, answer in question_answer_tuples %}
|
||||||
{% if request.user.is_authenticated %}
|
{% if request.user.is_authenticated %}
|
||||||
<a href="{% url 'sbas:exam_take_user' pk=exam.id sk=forloop.counter0 %}"><button {% if not answer %}class="unanswered"{% endif %}>{{forloop.counter}}: {{answer.answer}}</button></a>
|
<a href="{% url 'sbas:exam_take_user' pk=exam.id sk=forloop.counter0 %}"><button {% if not answer %}class="unanswered" title="You have not answered this question"{% endif %}>{{forloop.counter}}: {{answer.answer}}</button></a>
|
||||||
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="{% url 'sbas:exam_take' pk=exam.id sk=forloop.counter0 cid=cid passcode=passcode %}"><button {% if not answer %}class="unanswered"{% endif %}>{{forloop.counter}}: {{answer.answer}}</button></a>
|
<a href="{% url 'sbas:exam_take' pk=exam.id sk=forloop.counter0 cid=cid passcode=passcode %}"><button {% if not answer %}class="unanswered" title="You have not answered this question"{% endif %}>{{forloop.counter}}: {{answer.answer}}</button></a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
<div id="time-details">
|
||||||
Start time: {{cid_user_exam.start_time}}<br/>
|
Start time: {{cid_user_exam.start_time}}<br/>
|
||||||
Last change time: {{cid_user_exam.end_time}}
|
Last change time: {{cid_user_exam.end_time}}
|
||||||
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block js %}
|
{% block js %}
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(() => {
|
$(document).ready(() => {
|
||||||
|
{% if not exam.publish_results %}
|
||||||
|
|
||||||
|
let time_limit = '{{exam.time_limit}}'
|
||||||
|
if (time_limit != "None") {
|
||||||
|
let end_time = new Date({{cid_user_exam.start_time|date:"U"}}*1000+parseInt('{{exam.time_limit}}')*1000);
|
||||||
|
|
||||||
|
initializeClock("clockdiv", end_time);
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -4,8 +4,10 @@
|
|||||||
<div class="">
|
<div class="">
|
||||||
<h2>Exam / Results checker</h2>
|
<h2>Exam / Results checker</h2>
|
||||||
<p>Please enter your CID (Candidate ID) and passcode</p>
|
<p>Please enter your CID (Candidate ID) and passcode</p>
|
||||||
<p>CID: <input type="text" name="cid" id="cid-box" autofocus></p>
|
<table>
|
||||||
<p>Passcode: <input id="passcode-box" type="text"></p>
|
<tr><td>CID:</td><td><input type="text" name="cid" id="cid-box" autofocus placeholder="Please enter your CID number"></td></tr>
|
||||||
|
<tr><td>Passcode:</td><td><input id="passcode-box" type="text" placeholder="Please enter your passcode"></td></tr>
|
||||||
|
</table>
|
||||||
<button id="cid-selector-go-button">Login</button>
|
<button id="cid-selector-go-button">Login</button>
|
||||||
</div>
|
</div>
|
||||||
<details>
|
<details>
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<div id="timeup">
|
||||||
|
Allocated time over.
|
||||||
|
</div>
|
||||||
|
<div id="clockdiv">
|
||||||
|
Time remaining:
|
||||||
|
<div>
|
||||||
|
<span class="hours num"></span>
|
||||||
|
<span class="smalltext">Hours</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span class="minutes num"></span>
|
||||||
|
<span class="smalltext">Minutes</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span class="seconds num"></span>
|
||||||
|
<span class="smalltext">Seconds</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
+23
-3
@@ -15,19 +15,38 @@
|
|||||||
<a href="{% url 'longs:index'%}">Longs</a>
|
<a href="{% url 'longs:index'%}">Longs</a>
|
||||||
<a href="{% url 'sbas:index'%}">SBAs</a> {% endcomment %}
|
<a href="{% url 'sbas:index'%}">SBAs</a> {% endcomment %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if request.user.is_staff %}<a href="{% url 'admin:index'%}">Admin</a>{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
</p>
|
</p>
|
||||||
{% if request.user.is_authenticated %}
|
{% if request.user.is_authenticated %}
|
||||||
|
|
||||||
|
|
||||||
|
{% if request.user.userprofile.peninsula_trainee %}
|
||||||
|
<div id="user-details">
|
||||||
|
Please check your details are correct.<br/>
|
||||||
|
Name: {{request.user.first_name}} {{request.user.last_name}}<br/>
|
||||||
|
Email: {{request.user.email}}<br/>
|
||||||
|
Grade: {{request.user.userprofile.grade}}<br/>
|
||||||
|
Supervisor: {{request.user.userprofile.supervisor}}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<a href="{% url 'user_scores'%}">Your Exams</a>
|
<a href="{% url 'user_scores'%}"><button>Exams</button></a>
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
<p><a href="http://www.penracourses.org.uk/rts">RTS is available here</a></p>
|
<p><a href="http://www.penracourses.org.uk/rts">RTS is available here</a></p>
|
||||||
<p><a href="{% url 'cid_selector' %}">CID users can log in here</a></p>
|
|
||||||
|
|
||||||
|
{% if not request.user.is_authenticated %}
|
||||||
|
<p><a href="{% url 'cid_selector' %}">CID users can log in here</a></p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
{% if request.user.is_staff %}
|
{% if request.user.is_staff %}
|
||||||
Manage users <a href="{% url 'accounts_list'%}">here</a> and candidates <a href="{% url 'generic:manage_cids'%}">here</a>
|
Manage users <a href="{% url 'accounts_list'%}">here</a> and candidates <a href="{% url 'generic:manage_cids'%}">here</a>
|
||||||
@@ -35,6 +54,7 @@
|
|||||||
<p>Manage <a href="{% url 'generic:examination_view' %}">Examinations</a>
|
<p>Manage <a href="{% url 'generic:examination_view' %}">Examinations</a>
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
|
<a href="{% url 'admin:index'%}">Admin</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
Reference in New Issue
Block a user