allow completion of sbas / physics exams

This commit is contained in:
Ross
2024-12-30 11:12:36 +00:00
parent 13713d870f
commit 7493d55bc2
17 changed files with 362 additions and 240 deletions
+10 -1
View File
@@ -1518,6 +1518,12 @@ class CidUserExam(models.Model):
else:
return "CID" + str(self.cid_user.cid)
def complete_exam(self):
# TODO add examuserstatus?
self.end_time = timezone.now()
self.completed = True
self.save()
CID_GROUP_EXAMS = (
("SBAs", "sba_cid_user_groups"),
@@ -1729,7 +1735,10 @@ class ExamCollection(models.Model, AuthorMixin):
)
def __str__(self):
return f"{self.name} [{self.date}]"
if self.date is not None and self.date is not "":
return f"{self.name} [{self.date}]"
else:
return f"{self.name}"
def get_absolute_url(self):
return reverse("generic:examcollection_detail", kwargs={"pk": self.pk})
@@ -2,7 +2,12 @@
{% block content %}
<h1>{{ object.name }} [{{object.date}}] </h1>
<h1>
{{ object.name }}
{% if object.date %}
[{{object.date}}]
{% endif %}
</h1>
<p>
<a href="{% url 'generic:examcollection_edit' object.pk %}">Edit</a>
@@ -17,55 +22,55 @@
{% if object.anatomy_exams.all %}
<h2>Anatomy Exams:</h2>
<ul>
{% for exam in object.anatomy_exams.all %}
<li><a href="{% url 'anatomy:exam_overview' exam.pk %}">{{ exam }}</a> - <a href="{% url 'anatomy:exam_update' exam.pk %}">(Edit)</a></li>
{% endfor %}
</ul>
<a href="{% url 'anatomy:exam_list_collection' object.pk %}">View exam list</a>
{% endif %}
{% if object.anatomy_exams.all %}
<h2>Anatomy Exams:</h2>
<ul>
{% for exam in object.anatomy_exams.all %}
<li><a href="{% url 'anatomy:exam_overview' exam.pk %}">{{ exam }}</a> - <a href="{% url 'anatomy:exam_update' exam.pk %}">(Edit)</a></li>
{% endfor %}
</ul>
<a href="{% url 'anatomy:exam_list_collection' object.pk %}">View exam list</a>
{% endif %}
{% if object.longs_exams.all %}
<h2>Longs Exams:</h2>
<ul>
{% for exam in object.longs_exams.all %}
<li><a href="{% url 'longs:exam_overview' exam.pk %}">{{ exam }}</a> - <a href="{% url 'longs:exam_update' exam.pk %}">(Edit)</a></li>
{% endfor %}
</ul>
<a href="{% url 'longs:exam_list_collection' object.pk %}">View exam list</a>
{% endif %}
{% if object.longs_exams.all %}
<h2>Longs Exams:</h2>
<ul>
{% for exam in object.longs_exams.all %}
<li><a href="{% url 'longs:exam_overview' exam.pk %}">{{ exam }}</a> - <a href="{% url 'longs:exam_update' exam.pk %}">(Edit)</a></li>
{% endfor %}
</ul>
<a href="{% url 'longs:exam_list_collection' object.pk %}">View exam list</a>
{% endif %}
{% if object.rapids_exams.all %}
<h2>Rapids Exams:</h2>
<ul>
{% for exam in object.rapids_exams.all %}
<li><a href="{% url 'rapids:exam_overview' exam.pk %}">{{ exam }}</a> - <a href="{% url 'rapids:exam_update' exam.pk %}">(Edit)</a></li>
{% endfor %}
</ul>
<a href="{% url 'rapids:exam_list_collection' object.pk %}">View exam list</a>
{% endif %}
{% if object.rapids_exams.all %}
<h2>Rapids Exams:</h2>
<ul>
{% for exam in object.rapids_exams.all %}
<li><a href="{% url 'rapids:exam_overview' exam.pk %}">{{ exam }}</a> - <a href="{% url 'rapids:exam_update' exam.pk %}">(Edit)</a></li>
{% endfor %}
</ul>
<a href="{% url 'rapids:exam_list_collection' object.pk %}">View exam list</a>
{% endif %}
{% if object.physics_exams.all %}
<h2>Physics Exams:</h2>
<ul>
{% for exam in object.physics_exams.all %}
<li><a href="{% url 'physics:exam_overview' exam.pk %}">{{ exam }}</a> - <a href="{% url 'physics:exam_update' exam.pk %}">(Edit)</a></li>
{% endfor %}
</ul>
<a href="{% url 'physics:exam_list_collection' object.pk %}">View exam list</a>
{% endif %}
{% if object.physics_exams.all %}
<h2>Physics Exams:</h2>
<ul>
{% for exam in object.physics_exams.all %}
<li><a href="{% url 'physics:exam_overview' exam.pk %}">{{ exam }}</a> - <a href="{% url 'physics:exam_update' exam.pk %}">(Edit)</a></li>
{% endfor %}
</ul>
<a href="{% url 'physics:exam_list_collection' object.pk %}">View exam list</a>
{% endif %}
{% if object.sbas_exams.all %}
<h2>SBAs Exams:</h2>
<ul>
{% for exam in object.sbas_exams.all %}
<li><a href="{% url 'sbas:exam_overview' exam.pk %}">{{ exam }}</a> - <a href="{% url 'sbas:exam_update' exam.pk %}">(Edit)</a></li>
{% endfor %}
</ul>
<a href="{% url 'sbas:exam_list_collection' object.pk %}">View exam list</a>
{% endif %}
{% if object.sbas_exams.all %}
<h2>SBAs Exams:</h2>
<ul>
{% for exam in object.sbas_exams.all %}
<li><a href="{% url 'sbas:exam_overview' exam.pk %}">{{ exam }}</a> - <a href="{% url 'sbas:exam_update' exam.pk %}">(Edit)</a></li>
{% endfor %}
</ul>
<a href="{% url 'sbas:exam_list_collection' object.pk %}">View exam list</a>
{% endif %}
+4 -2
View File
@@ -9,13 +9,14 @@
{% endif %}
{% if request.user.is_authenticated and valid_user %}
<a href="{% url 'physics:exam_take_user' pk=exam.pk sk=0 %}">Start Exam</a>
<a href="{% url 'physics:exam_take_user' pk=exam.pk sk=0 %}" title="Click to start the exam"><button class="btn btn-primary">Start Exam</button></a>
{% else %}
Enter your CID and passcode in the below boxes.<br />
<p><input id="cid-box" type="text" value="Candidate ID"></p>
<p><input id="passcode-box" type="text" value="Passcode"></p>
<button>Start exam</button>
<button type="button" class="btn btn-primary">Start exam</button>
<script type="text/javascript">
$(document).ready(function () {
@@ -53,4 +54,5 @@
});
</script>
{% endif %}
{% include "physics/exam_take_help.html" %}
{% endblock %}
+99 -104
View File
@@ -15,6 +15,10 @@
<div class="alert alert-primary review-mode-alert" role="alert">
Exam is in review mode. Add question feedback <a href="#" onclick="return window.create_popup_window('{% url 'feedback_create' question_type='physics' pk=question.pk %}')">here</a>.
</div>
{% elif cid_user_exam.completed %}
<div class="alert alert-primary review-mode-alert" role="alert">
Exam completed.
</div>
{% endif %}
<div>
@@ -92,135 +96,126 @@
<div id="menu-list">
</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="
{% if cid %}
{% url 'physics:exam_scores_cid_user' pk=exam.pk cid=cid passcode=passcode %}
{% else %}
{% url 'physics:exam_scores_user' pk=exam.pk %}
{% endif %}
">here</a>).</p>
</details>
{% endblock %}
{% include "physics/exam_take_help.html" %}
{% block js %}
<script>
$(document).ready(() => {
{% endblock %}
{% block js %}
<script>
$(document).ready(() => {
/* beautify ignore:start */
{% 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);
}
{% else %}
$(`ol.physics-answer-list input`).each((n, el) => { el.disabled = true; });
{{question.get_answers_js}}.forEach((el, n) => {
console.log(el)
li_el = $(`ol.physics-answer-list li:eq(${n})`)//.addClass(`answer-${el}`)
if (el) {
li_el.addClass("answer-true");
} else {
li_el.addClass("answer-false");
}
if (el == li_el.find("input").get(0).checked) {
li_el.addClass("answer-correct");
} else {
li_el.addClass("answer-incorrect");
{% if not exam.publish_results and not cid_user_exam.completed %}
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);
}
});
{% elif exam.publish_result %}
$(`ol.physics-answer-list input`).each((n, el) => { el.disabled = true; });
{{question.get_answers_js}}.forEach((el, n) => {
console.log(el)
li_el = $(`ol.physics-answer-list li:eq(${n})`)//.addClass(`answer-${el}`)
if (el) {
li_el.addClass("answer-true");
} else {
li_el.addClass("answer-false");
}
if (el == li_el.find("input").get(0).checked) {
li_el.addClass("answer-correct");
} else {
li_el.addClass("answer-incorrect");
}
});
//$("ul.physics-answer-list li[data-ans='{{question.best_answer}}']").addClass("correct");
{% endif %}
{% endif %}
for (let i = 0; i < {{ exam_length }}; i++) {
qbutton = $(`<button class="question-menu-item" name="goto-${i}" data-qn="${i}">${i+1}</button>`)
for (let i = 0; i < {{ exam_length }}; i++) {
qbutton = $(`<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);
if (i == {{pos}}) {qbutton.addClass("current-question")}
$("#menu-list").append(qbutton);
}
$("button.question-menu-item").on("click", (e) => {
console.log(e.currentTarget.dataset.qn);
document.getElementById("goto-button").value = e.currentTarget.dataset.qn;
$("#goto-button").click();
});
}
$("button.question-menu-item").on("click", (e) => {
console.log(e.currentTarget.dataset.qn);
document.getElementById("goto-button").value = e.currentTarget.dataset.qn;
$("#goto-button").click();
});
/* beautify ignore:end */
})
})
</script>
{% endblock %}
{% block css %}
<style type="text/css">
</script>
{% endblock %}
{% block css %}
<style type="text/css">
/* .form-contents {
display: none;
} */
.flex-container {
display: flex;
}
.flex-container {
display: flex;
}
.flex-1 {
flex: 1;
}
.flex-1 {
flex: 1;
}
.flex-8 {
flex: 8;
}
.flex-8 {
flex: 8;
}
.selected {
border: 1px solid purple;
}
.selected {
border: 1px solid purple;
}
.answer-true::before {
content: "[Correct answer: True]";
color: darkgray;
float: right;
opacity: 50%;
.answer-true::before {
content: "[Correct answer: True]";
color: darkgray;
float: right;
opacity: 50%;
}
.answer-false::before {
content: "[Correct answer: False]";
color: darkgray;
float: right;
opacity: 50%;
}
.answer-false::before {
content: "[Correct answer: False]";
color: darkgray;
float: right;
opacity: 50%;
}
.answer-correct {
border: 1px dotted darkgreen;
}
.answer-incorrect {
border: 1px dashed darkred;
}
}
.answer-correct {
border: 1px dotted darkgreen;
}
.answer-incorrect {
border: 1px dashed darkred;
}
.physics-answer-list li {
margin-top: 5px;
margin-bottom: 5px;
}
.physics-answer-list li {
margin-top: 5px;
margin-bottom: 5px;
}
.physics-answer-list input:checked+.postinput::after {
margin-left: 20px;
content: "True"
}
.physics-answer-list input:not(:checked)+.postinput::after {
margin-left: 20px;
content: "False"
}
.physics-answer-list input:checked+.postinput::after {
margin-left: 20px;
content: "True"
}
.physics-answer-list input:not(:checked)+.postinput::after {
margin-left: 20px;
content: "False"
}
.feedback {
padding: 20px;
display: block;
}
.feedback {
padding: 20px;
display: block;
}
</style>
{% endblock %}
</style>
{% endblock %}
@@ -0,0 +1,14 @@
<details class="help-text pt-3">
<summary><i class="bi bi-info-circle"></i> 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.</p>
<p>Results are also available on the results page
<a target="_blank" href="
{% if cid %}
{% url 'physics:exam_scores_cid_user' pk=exam.pk cid=cid passcode=passcode %}
{% else %}
{% url 'physics:exam_scores_user' pk=exam.pk %}
{% endif %}
">here</a>.</p>
</details>
@@ -2,7 +2,7 @@
{% block content %}
<span id="user-id">
{% if request.user.is_authenticated %}
{% if not cid %}
User: {{request.user}}
{% else %}
CID: {{cid}}
@@ -22,9 +22,16 @@
<a href="{% url 'physics:exam_scores_cid_user' pk=exam.id cid=cid passcode=passcode %}">here</a>
{% endif %}
</div>
{% endif %}
{% if not exam.publish_results and answer_count != exam_length %}
{% elif cid_user_exam.completed %}
<div class="alert alert-primary review-mode-alert" role="alert">
Exam completed. Answers are available
{% if request.user.is_authenticated %}
<a href="{% url 'physics:exam_scores_user' pk=exam.id %}">here</a>
{% else %}
<a href="{% url 'physics:exam_scores_cid_user' pk=exam.id cid=cid passcode=passcode %}">here</a>
{% endif %}
{% elif answer_count != exam_length %}
<div id="unanswered-questions-alert" class="alert alert-warning" role="alert">
You have unanswered questions.
</div>
@@ -34,7 +41,7 @@
<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="physics-finish-list">
{% for question, answer in question_answer_tuples %}
{% if request.user.is_authenticated %}
{% if not cid %}
<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 %}
@@ -44,15 +51,26 @@
</div>
<div id="time-details">
Start time: {{cid_user_exam.start_time}}<br/>
Last change time: {{cid_user_exam.end_time}}
Last change time: {{cid_user_exam.end_time}}<br/>
Completed: {{cid_user_exam.completed}}
</div>
{% if not cid_user_exam.completed %}
{% if not cid %}
<button hx-get="{% url 'physics:exam_complete_user' pk=exam.id %}" hx-swap="outerHTML" hx-confirm="Finish exam?">Finish Exam</button>
{% else %}
<button hx-get="{% url 'physics:exam_complete' pk=exam.id cid=cid passcode=passcode %}" hx-swap="outerHTML" hx-confirm="Finish exam?">Finish Exam</button>
{% endif %}
{% endif %}
{% include "physics/exam_take_help.html" %}
{% endblock %}
{% block js %}
<script>
$(document).ready(() => {
{% if not exam.publish_results %}
{% if not exam.publish_results and not cid_user_exam.completed %}
let time_limit = '{{exam.time_limit}}'
if (time_limit != "None") {
+10
View File
@@ -33,6 +33,16 @@ urlpatterns.extend(
views.exam_take_overview,
name="exam_take_overview_user",
),
path(
"exam/<int:pk>/<str:cid>/<str:passcode>/complete",
views.exam_complete,
name="exam_complete",
),
path(
"exam/<int:pk>/complete",
views.exam_complete,
name="exam_complete_user",
),
path("exam/<int:pk>/markers", views.ExamMarkersUpdate.as_view(), name="exam_markers"),
path("exam/<int:pk>/groups", views.ExamGroupsUpdate.as_view(), name="exam_groups_edit"),
path(
+12 -1
View File
@@ -136,6 +136,17 @@ def exam_start(request, pk):
},
)
def exam_complete(request, pk, cid=None, passcode=None):
exam = get_object_or_404(Exam, pk=pk)
exam.check_user_can_take(cid, passcode, request.user)
cid_user_exam = exam.get_or_create_cid_user_exam(cid=cid, user_user=request.user)
cid_user_exam.complete_exam()
return HttpResponse("<div role='alert' class='alert alert-info'>Exam completed</div>")
def exam_take_overview(request, pk, cid=None, passcode=None):
exam = get_object_or_404(Exam, pk=pk)
@@ -213,7 +224,7 @@ def exam_take(request, pk: int, sk: int, cid: str| None = None, passcode: str| N
form = UserAnswerForm(request.POST, instance=answer)
else:
form = UserAnswerForm(request.POST)
if form.is_valid() and not exam.publish_results:
if form.is_valid() and not exam.publish_results and not cid_user_exam.completed:
answer = form.save(commit=False)
if cid is not None:
+12 -7
View File
@@ -3,13 +3,6 @@ body {
background-color: black;
}
a, a:link {
color: #3282b8;
}
.navbar .nav-link a {
color: #3282b8;
}
.warn {
color: red;
@@ -1323,3 +1316,15 @@ details.help-text > summary {
padding-bottom: 1px;
}
a, a:link {
color: #3282b8;
}
.navbar .nav-link {
color: #3282b8;
}
span#user-id {
opacity: 50%;
float: right;
}
+5 -2
View File
@@ -9,13 +9,13 @@
{% endif %}
{% if request.user.is_authenticated and valid_user %}
<a href="{% url 'sbas:exam_take_user' pk=exam.pk sk=0 %}">Start Exam</a>
<a href="{% url 'sbas:exam_take_user' pk=exam.pk sk=0 %}" title="Click to start the exam"><button class="btn btn-primary">Start Exam</button></a>
{% else %}
Enter your CID and passcode in the below boxes.<br />
<p><input id="cid-box" type="text" value="Candidate ID"></p>
<p><input id="passcode-box" type="text" value="Passcode"></p>
<button>Start exam</button>
<button type="button" class="btn btn-primary">Start exam</button>
<script type="text/javascript">
$(document).ready(function () {
@@ -52,4 +52,7 @@
});
</script>
{% endif %}
{% include "sbas/exam_take_help.html" %}
{% endblock %}
+9 -17
View File
@@ -15,6 +15,10 @@
<div class="alert alert-primary review-mode-alert" role="alert">
Exam is in review mode. Add question feedback <a href="#" onclick="return window.create_popup_window('{% url 'feedback_create' question_type='sbas' pk=question.pk %}')">here</a>.
</div>
{% elif cid_user_exam.completed %}
<div class="alert alert-primary review-mode-alert" role="alert">
Exam completed.
</div>
{% endif %}
<div>
@@ -62,26 +66,14 @@
<div id="menu-list">
</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="
{% if cid %}
{% url 'sbas:exam_scores_cid_user' pk=exam.pk cid=cid passcode=passcode %}
{% else %}
{% url 'sbas:exam_scores_user' pk=exam.pk %}
{% endif %}
"
>here</a>).</p>
</details>
{% include "sbas/exam_take_help.html" %}
{% endblock %}
{% block js %}
<script>
$(document).ready(() => {
/* beautify ignore:start */
{% if not exam.publish_results %}
{% if not exam.publish_results and not cid_user_exam.completed %}
let time_limit = '{{exam.time_limit}}'
if (time_limit != "None") {
@@ -97,7 +89,7 @@
$(el).addClass("selected")
})
})
{% else %}
{% elif exam.publish_results %}
$("ul.sba-answer-list li[data-ans='{{question.best_answer}}']").addClass("correct");
{% endif %}
@@ -130,12 +122,12 @@
.selected {
border: 1px solid purple;
}
li:hover::before {
.sba-answer-list li:hover::before {
content: "Click to select";
float: right
}
li:hover.selected::before {
.sba-answer-list li:hover.selected::before {
content: "Chosen answer";
float: right
}
+15
View File
@@ -0,0 +1,15 @@
<details class="help-text pt-3">
<summary><i class="bi bi-info-circle"></i> 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.</p>
<p>Results are also available on the results page
<a target="_blank" href="
{% if cid %}
{% url 'sbas:exam_scores_cid_user' pk=exam.pk cid=cid passcode=passcode %}
{% else %}
{% url 'sbas:exam_scores_user' pk=exam.pk %}
{% endif %}
">here</a>.</p>
</details>
+26 -7
View File
@@ -2,7 +2,7 @@
{% block content %}
<span id="user-id">
{% if request.user.is_authenticated %}
{% if not cid %}
User: {{request.user}}
{% else %}
CID: {{cid}}
@@ -15,16 +15,22 @@
{% if exam.publish_results %}
<div class="alert alert-primary review-mode-alert" role="alert">
Exam is in review mode. Score are available
{% if request.user.is_authenticated %}
{% if not cid %}
<a href="{% url 'sbas:exam_scores_user' pk=exam.id %}">here</a>
{% else %}
<a href="{% url 'sbas:exam_scores_cid_user' pk=exam.id cid=cid passcode=passcode %}">here</a>
{% endif %}
</div>
{% endif %}
{% if not exam.publish_results and answer_count != exam_length %}
{% elif cid_user_exam.completed %}
<div class="alert alert-primary review-mode-alert" role="alert">
Exam completed. Answers are available
{% if not cid %}
<a href="{% url 'sbas:exam_scores_user' pk=exam.id %}">here</a>
{% else %}
<a href="{% url 'sbas:exam_scores_cid_user' pk=exam.id cid=cid passcode=passcode %}">here</a>
{% endif %}
</div>
{% elif answer_count != exam_length %}
<div id="unanswered-questions-alert" class="alert alert-warning" role="alert">
You have unanswered questions.
</div>
@@ -44,9 +50,22 @@
</div>
<div id="time-details">
Start time: {{cid_user_exam.start_time}}<br/>
Last change time: {{cid_user_exam.end_time}}
Last change time: {{cid_user_exam.end_time}}<br/>
Completed: {{cid_user_exam.completed}}
</div>
{% if not cid_user_exam.completed %}
{% if not cid %}
<button hx-get="{% url 'sbas:exam_complete_user' pk=exam.id %}" hx-swap="outerHTML" hx-confirm="Finish exam?">Finish Exam</button>
{% else %}
<button hx-get="{% url 'sbas:exam_complete' pk=exam.id cid=cid passcode=passcode %}" hx-swap="outerHTML" hx-confirm="Finish exam?">Finish Exam</button>
{% endif %}
{% endif %}
{% include "sbas/exam_take_help.html" %}
{% endblock %}
{% block js %}
+10
View File
@@ -45,6 +45,16 @@ urlpatterns = [
views.exam_take_overview,
name="exam_take_overview_user",
),
path(
"exam/<int:pk>/<str:cid>/<str:passcode>/complete",
views.exam_complete,
name="exam_complete",
),
path(
"exam/<int:pk>/complete",
views.exam_complete,
name="exam_complete_user",
),
path("exam/<int:pk>/markers", views.ExamMarkersUpdate.as_view(), name="exam_markers"),
path("exam/<int:pk>/groups", views.ExamGroupsUpdate.as_view(), name="exam_groups_edit"),
+12 -1
View File
@@ -116,6 +116,17 @@ def exam_start(request, pk):
{"exam": exam, "valid_user": exam.check_logged_in_user(request)},
)
def exam_complete(request, pk, cid=None, passcode=None):
exam = get_object_or_404(Exam, pk=pk)
exam.check_user_can_take(cid, passcode, request.user)
cid_user_exam = exam.get_or_create_cid_user_exam(cid=cid, user_user=request.user)
cid_user_exam.complete_exam()
return HttpResponse("<div role='alert' class='alert alert-info'>Exam completed</div>")
def exam_take_overview(request, pk, cid=None, passcode=None):
exam = get_object_or_404(Exam, pk=pk)
@@ -195,7 +206,7 @@ def exam_take(request, pk: int, sk: int, cid: int = None, passcode: str = None):
form = UserAnswerForm(request.POST, instance=answer)
else:
form = UserAnswerForm(request.POST)
if form.is_valid():
if form.is_valid() and not exam.publish_results and not cid_user_exam.completed:
answer = form.save(commit=False)
if cid is not None:
+16 -11
View File
@@ -2,19 +2,19 @@
{% block content %}
<h1>Examinations</h1>
{% if collection %}
<h2>Collection: {{collection.name}}</h2>
<h2>Collection: {{collection.name}}</h2>
{% endif %}
<details class="help-text">
<summary><i class="bi bi-info-circle"></i> Help</summary>
{% if collection %}
<p>This page shows the examinations within the collection "{{collection.name}}".</p>
<p>This page shows all the examinations within the collection "{{collection.name}}".</p>
{% else %}
<p>This page shows the currently active examinations (that you have access to manage).</p>
{% endif %}
<p>Exams that are active will be available for candidates to take.</p>
@@ -43,8 +43,8 @@
<input type="checkbox" id="published-{{exam.pk}}" class="exam-publish-results-switch" data-posturl="{% url app_name|add:':exam_toggle_results_published' pk=exam.pk %}" {% if exam.publish_results %}checked{% endif %}>
<label for="published-{{exam.pk}}" class="flex-col icon-container published-icon" title="Click to toggle published state">Results Published</label>
<a href="{% url app_name|add:':exam_update' pk=exam.pk %}" class="flex-shrink exam-list-edit-button"><i class="bi bi-pencil-square"></i></a>
<input type="checkbox" id="archived-{{exam.pk}}" class="exam-archived-switch" data-posturl="{% url app_name|add:':exam_toggle_archived' pk=exam.pk %}" {% if exam.archive %}checked{% endif %}>
<label for="archived-{{exam.pk}}" class="flex-shrink icon-archive archived-icon" title="Click to toggle archived state"><i class="bi bi-archive"></i></label>
<input type="checkbox" id="archived-{{exam.pk}}" class="exam-archived-switch" data-posturl="{% url app_name|add:':exam_toggle_archived' pk=exam.pk %}" {% if exam.archive %}checked{% endif %}>
<label for="archived-{{exam.pk}}" class="flex-shrink icon-archive archived-icon" title="Click to toggle archived state"><i class="bi bi-archive"></i></label>
</li>
{% endif %}
{% endfor %}
@@ -56,12 +56,12 @@
{% if not exam.active %}
<li class="exam-item">
<span class="flex-col-2">
<a href="{% url app_name|add:':exam_overview' pk=exam.pk %}" class="exam-name">{{exam}}</a>
<a href="{% url app_name|add:':exam_overview' pk=exam.pk %}" class="exam-name">{{exam}}</a>
{% if exam.examcollection %}
<a href='{% url "generic:examcollection_detail" exam.examcollection_id %}' title="This exam is part of the collection {{exam.examcollection.name}}"><i class="bi bi-collection"></i></a>
{% endif %}
</span>
{% if marking %}<a href="{% url app_name|add:':mark_overview' pk=exam.pk %}" class="flex-col-half" title="Click to mark exam">Mark</a>{% endif %}
<span class="flex-col">
<a href="{% url app_name|add:':exam_cids' exam_id=exam.pk %}" title="Click to manage candidates">Candidates</a>
@@ -87,10 +87,15 @@
</ul>
{% if view_all %}
<a href="{% url app_name|add:':exam_list' %}" >Hide archived</a>
{% if collection %}
{% else %}
<a href="{% url app_name|add:':exam_list_all' %}" title="Show all exams (including archived)">View all</a>
{% if view_all %}
<a href="{% url app_name|add:':exam_list' %}" >Hide archived</a>
{% else %}
<a href="{% url app_name|add:':exam_list_all' %}" title="Show all exams (including archived)">View all</a>
{% endif %}
{% endif %}
</div>
{% include 'exam_overview_js.html' %}
+32 -34
View File
@@ -1,39 +1,37 @@
{% extends 'base.html' %}
{% load auth_extras %}
{% block content %}
<p>
<div class="anatomy">
<h1>PENRA Courses</h1>
{% if not request.user.is_authenticated %}
<a href="{% url 'login'%}">Log in</a><br/>
<a href="{% url 'password_reset'%}">Reset password</a>
{% else %}
<h1>PENRA Courses</h1>
{% if not request.user.is_authenticated %}
<a href="{% url 'login'%}">Log in</a><br/>
<a href="{% url 'password_reset'%}">Reset password</a>
{% else %}
{% comment %} <a href="{% url 'atlas:index'%}">Atlas</a>
<a href="{% url 'anatomy:index'%}">Anatomy</a>
<a href="{% url 'physics:index'%}">Physics</a>
<a href="{% url 'rapids:index'%}">Rapids</a>
<a href="{% url 'longs:index'%}">Longs</a>
<a href="{% url 'sbas:index'%}">SBAs</a> {% endcomment %}
{% endif %}
</div>
</p>
{% endif %}
{% 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 id="user-details card">
<div class="card-body">
<h5 class="card-title">Please check your details are correct.</h5>
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>
</div>
{% endif %}
{% if request.user.supervisor %}
<a href='{% url "generic:supervisor_overview" request.user.supervisor.pk %}'>Supervisor overview</a>
<a href='{% url "generic:supervisor_overview" request.user.supervisor.pk %}'>Supervisor overview</a>
{% endif %}
<p>
@@ -42,26 +40,26 @@
</p>
{% endif %}
{% if collections %}
<h2>Exam collections</h2>
<ul>
{% for collection in collections %}
<li><a href="{% url 'generic:examcollection_detail' collection.pk %}">{{collection.name}}</a></li>
{% endfor %}
</ul>
<h2>Exam collections</h2>
<ul>
{% for collection in collections %}
<li><a href="{% url 'generic:examcollection_detail' collection.pk %}">{{collection.name}}</a></li>
{% endfor %}
</ul>
{% endif %}
<p><a href="http://www.penracourses.org.uk/rts">RTS is available here</a></p>
{% if rcr_assessor %}
<div>
{% if rcr_assessor %}
<div>
<h3><a href="{% url 'rcr:radiology_index_view'%}">RCR platform migration</a></h3>
</div>
{% endif %}
</div>
{% endif %}
{% if not request.user.is_authenticated %}
<p><a href="{% url 'cid_selector' %}">CID users can log in here</a></p>
@@ -83,10 +81,10 @@
</div>
{% if request.user.is_superuser %}
<a href="{% url 'django_psutil_dash:dashboard' %}">psutil</a>
<a href="{% url 'django_psutil_dash:dashboard' %}">psutil</a>
{% endif %}
{% endblock %}