diff --git a/anatomy/static/js/anatomy.js b/anatomy/static/js/anatomy.js
index 0493c7c2..057dea51 100644
--- a/anatomy/static/js/anatomy.js
+++ b/anatomy/static/js/anatomy.js
@@ -581,3 +581,49 @@ function delete_multiple(url, csrf_token) {
}
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
\ No newline at end of file
diff --git a/generic/models.py b/generic/models.py
index a21f73a7..dbf5ccec 100644
--- a/generic/models.py
+++ b/generic/models.py
@@ -1,4 +1,5 @@
from collections import defaultdict
+import datetime
import json
import os
from typing import Self, Tuple
@@ -712,6 +713,20 @@ class ExamBase(ExamCollectionGenericBase):
"{}: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):
return list(self.exam_questions.all()).index(question)
@@ -955,10 +970,12 @@ class CidUser(models.Model):
def check_passcode(self, passcode) -> bool:
return self.passcode == passcode
- def get_cid_exams(self):
+ def get_cid_exams(self, include_case_collections=True):
available_exams = []
for n, t in EXAM_TYPES:
+ if not include_case_collections and n == "casecollection":
+ continue
exam_rel = getattr(self, t)
if exam_rel.exists():
exams = exam_rel.filter(exam_mode=True, archive=False).order_by("name")
diff --git a/physics/templates/physics/exam_start.html b/physics/templates/physics/exam_start.html
index 96be8048..171e6e2c 100755
--- a/physics/templates/physics/exam_start.html
+++ b/physics/templates/physics/exam_start.html
@@ -11,6 +11,10 @@
+ {% 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.
+ {% endif %}
+
Start exam
{% endblock %}
diff --git a/rad/static/css/anatomy.css b/rad/static/css/anatomy.css
index 9d4d8762..d5ec5891 100644
--- a/rad/static/css/anatomy.css
+++ b/rad/static/css/anatomy.css
@@ -73,7 +73,8 @@ a:link {
font-size: 20;
}
-.hide, .hidden {
+.hide,
+.hidden {
display: none;
}
@@ -83,7 +84,12 @@ a:link {
}
.unanswered {
- color: gray;
+ color: darkred;
+ border-color: darkred;
+}
+
+.unanswered:hover {
+ background-color: rgb(166, 0, 0);
}
#question-list {
@@ -1057,7 +1063,50 @@ table .peninsula-trainee::before {
user-select: none;
}
-.form-control, .form-control:focus, .form-control option {
+.form-control,
+.form-control:focus,
+.form-control option {
background-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;
+}
\ No newline at end of file
diff --git a/rad/views.py b/rad/views.py
index 83b6131d..5874e2bf 100644
--- a/rad/views.py
+++ b/rad/views.py
@@ -245,7 +245,7 @@ def cid_scores(request, cid, passcode):
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(
request,
diff --git a/sbas/templates/sbas/exam_start.html b/sbas/templates/sbas/exam_start.html
index 634140cc..3ffd3aaf 100755
--- a/sbas/templates/sbas/exam_start.html
+++ b/sbas/templates/sbas/exam_start.html
@@ -10,6 +10,10 @@
Enter your CID and passcode in the below boxes.
+
+ {% 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.
+ {% endif %}
Start exam
diff --git a/sbas/templates/sbas/exam_take.html b/sbas/templates/sbas/exam_take.html
index b4d40603..89236307 100755
--- a/sbas/templates/sbas/exam_take.html
+++ b/sbas/templates/sbas/exam_take.html
@@ -2,34 +2,13 @@
{% block content %}
- {% if request.user.is_authenticated %}
- User: {{request.user}}
- {% else %}
- CID: {{cid}}
- {% endif %}
+ {% if request.user.is_authenticated %}
+ User: {{request.user}}
+ {% else %}
+ CID: {{cid}}
+ {% endif %}
-
- Allocated time over.
-
-
- Time remaining:
- {% comment %}
{% endcomment %}
-
-
- Hours
-
-
-
- Minutes
-
-
-
- Seconds
-
-
+ {% include "exam_clock.html" %}
{{exam.name}}: Question [{{pos|add:1}} /{{exam_length}} ]
{% if exam.publish_results %}
@@ -58,13 +37,13 @@
{% if previous > -1 %}
- Previous
+ Previous
{% endif %}
{% if next %}
- Next
+ Next
{% else %}
{% if not exam.publish_results %}
- Save
+ Save
{% endif %}
{% endif %}
@@ -73,7 +52,7 @@
{{question.feedback|safe}}
{% endif %}
- Overview
+ Overview
goto
@@ -83,6 +62,11 @@
+ Help
+ Each question contains a list of 5 different statements. One of these is the single BEST answer.
+Click on the correct statement to select it. Once selected it will be highlighted.
+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 here ).
+
{% endblock %}
{% block js %}
@@ -113,8 +97,10 @@
$("ul.sba-answer-list li[data-ans='{{saved_answer}}']").addClass("selected"); {% endif %}
for (let i = 0; i < {{ exam_length }}; i++) {
- $("#menu-list").append($(
- ``));
+ qbutton = $(``)
+
+ if (i == {{pos}}) {qbutton.addClass("current-question")}
+ $("#menu-list").append(qbutton);
}
$("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 */
})
{% endblock %}
@@ -180,36 +122,15 @@
.selected {
border: 1px solid purple;
}
-
- #clockdiv{
- font-family: sans-serif;
- display: inline-block;
- font-weight: 100;
- text-align: center;
- font-size: 12px;
- display: none;
- color: darkgray;
+ li:hover::before {
+ content: "Click to select";
+ float: right
}
- #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;
+ li:hover.selected::before {
+ content: "Chosen answer";
+ float: right
}
- #timeup {
- display: none;
- color: red;
- font-weight: 100;
- text-align: center;
- font-size: 12px;
- }
{% endblock %}
\ No newline at end of file
diff --git a/sbas/templates/sbas/exam_take_overview.html b/sbas/templates/sbas/exam_take_overview.html
index 4836aaa7..227e8d87 100644
--- a/sbas/templates/sbas/exam_take_overview.html
+++ b/sbas/templates/sbas/exam_take_overview.html
@@ -1,12 +1,15 @@
{% extends 'base.html' %}
{% block content %}
+
{% if request.user.is_authenticated %}
User: {{request.user}}
{% else %}
CID: {{cid}}
{% endif %}
+
+ {% include "exam_clock.html" %}
Exam: {{exam.name}}
{% if exam.publish_results %}
@@ -21,26 +24,43 @@
{% endif %}
-
- {{answer_count}} out of {{exam_length}} questions answered. Click to go to question.
+ {% if not exam.publish_results and answer_count != exam_length %}
+
+ You have unanswered questions.
+
+ {% endif %}
+
+
+ {{answer_count}} out of {{exam_length}} questions answered. Unanswered questions are shown in red.
Click to go to a question.
+
Start time: {{cid_user_exam.start_time}}
Last change time: {{cid_user_exam.end_time}}
+
{% endblock %}
{% block js %}
{% endblock %}
diff --git a/templates/cid_selector.html b/templates/cid_selector.html
index 441f7e53..1194c7e3 100644
--- a/templates/cid_selector.html
+++ b/templates/cid_selector.html
@@ -4,8 +4,10 @@
Exam / Results checker
Please enter your CID (Candidate ID) and passcode
-
CID:
-
Passcode:
+
Login
diff --git a/templates/exam_clock.html b/templates/exam_clock.html
new file mode 100644
index 00000000..673030be
--- /dev/null
+++ b/templates/exam_clock.html
@@ -0,0 +1,18 @@
+
+ Allocated time over.
+
+
+ Time remaining:
+
+
+ Hours
+
+
+
+ Minutes
+
+
+
+ Seconds
+
+
\ No newline at end of file
diff --git a/templates/index.html b/templates/index.html
index f9866a42..998f564a 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -15,19 +15,38 @@
Longs
SBAs {% endcomment %}
{% endif %}
- {% if request.user.is_staff %}Admin {% endif %}
{% if request.user.is_authenticated %}
+
+
+ {% if request.user.userprofile.peninsula_trainee %}
+
+ Please check your details are correct.
+ Name: {{request.user.first_name}} {{request.user.last_name}}
+ Email: {{request.user.email}}
+ Grade: {{request.user.userprofile.grade}}
+ Supervisor: {{request.user.userprofile.supervisor}}
+
+
+
+ {% endif %}
+
+
- Your Exams
+ Exams
{% endif %}
RTS is available here
- CID users can log in here
+
+
+ {% if not request.user.is_authenticated %}
+ CID users can log in here
+ {% endif %}
+
{% if request.user.is_staff %}
Manage users
here and candidates
here
@@ -35,6 +54,7 @@
Manage Examinations
+
Admin
{% endif %}
{% endblock %}