213 lines
9.4 KiB
HTML
213 lines
9.4 KiB
HTML
{% extends exam.app_name|add:'/exams.html' %}
|
|
|
|
{% block content %}
|
|
<div class="">
|
|
<h2>{{ exam }}</h2>
|
|
{% if cached_scores %}
|
|
User answer scores are cached, if you update or change an answer you will need to <a href="{% url exam.app_name|add:':exam_scores_refresh' exam.pk %}">refresh the scores</a>.
|
|
{% endif %}
|
|
|
|
{% if missing_cids or missing_users %}
|
|
<details>
|
|
<summary>This exam has candidates / users with unsubmitted results</summary>
|
|
{% if missing_cids %}
|
|
<div class="alert alert-warning" role="alert">
|
|
The following CIDs results have not been submitted:
|
|
{% for cid in missing_cids %}
|
|
{{cid}},
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
{% if missing_users %}
|
|
<div class="alert alert-warning" role="alert">
|
|
The following users results have not been submitted:
|
|
{% for user in missing_users %}
|
|
{{user}} ({{user.id}}),
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
</details>
|
|
{% endif %}
|
|
{% if unmarked %}
|
|
<div class="alert alert-warning" role="alert">
|
|
The following questions need marking
|
|
{% for exam_index in unmarked %}
|
|
<a href="{% url exam.app_name|add:':mark' exam.pk exam_index %}">{{ exam_index|add:1 }}</a>
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
</div>
|
|
<div id="stats-plot">{{plot|safe}}</div>
|
|
<div id="stats-block" title="low scores (<20% max) are excluded from statistics" class="mb-2 small">
|
|
<h5 class="mb-1">Stats</h5>
|
|
<div>Candidates: <span class="fw-semibold">{{cids|length}}</span></div>
|
|
<div>Questions: <span id="question-number">{{question_number}}</span></div>
|
|
<div title="Max score">Available marks: {{max_score}}</div>
|
|
<div>Mean: {{mean}}, Median {{median}}, Mode {{mode}}</div>
|
|
<div>Top score: {{exam.stats_max}}</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<div class="table-responsive">
|
|
<table class="table table-dark table-striped table-hover table-sm cid-score-table sortable small">
|
|
<tr>
|
|
<th title="click to sort by candidate / user id">Candidate ID</th>
|
|
<th title="click to sort by score">Score</th>
|
|
{% if exam.app_name == "longs" or exam.app_name == "rapids" %}
|
|
<th>Normalised Score</th>
|
|
{% endif %}
|
|
</tr>
|
|
{% for cid in cids %}
|
|
<tr class="candidate-row" data-answer-count={{user_answer_count|get_item:cid}}>
|
|
{% if cid|slice:":1" == "u" %}
|
|
<td class="cid" >{{cids_user_id_map|get_item:cid}}</td>
|
|
{% else %}
|
|
<td class="cid" data-cid="{{cid|slice:'2:'}}"><a href="{% url 'cid_scores_admin' cid|slice:'2:' %}">{{cid}}</a>
|
|
<span title="Click to show candidate details"
|
|
hx-get="{% url 'generic:cid_details' cid|slice:'2:' %}"
|
|
hx-target="this"
|
|
>
|
|
<i class="bi bi-binoculars search-cid" ></i>
|
|
</span>
|
|
</td>
|
|
{% endif %}
|
|
<td>{{user_scores|get_item:cid}}</td>
|
|
|
|
{% if exam.app_name == "longs" or exam.app_name == "rapids" %}
|
|
<td>{{user_scores_normalised|get_item:cid}}</td>
|
|
{% endif %}
|
|
</tr>
|
|
{% endfor %}
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<div class="d-flex justify-content-between align-items-center mb-1">
|
|
<h5 class="mb-0">Answers as a table</h5>
|
|
<div>
|
|
<button class="btn btn-sm btn-outline-secondary" data-bs-toggle="collapse" data-bs-target="#answers-table" aria-expanded="true">Toggle answers</button>
|
|
</div>
|
|
</div>
|
|
<div class="collapse show" id="answers-table">
|
|
<div class="table-responsive">
|
|
<table class="table table-dark table-striped table-hover table-sm col-sm small">
|
|
<thead class="thead-dark">
|
|
<tr>
|
|
<th>Candidate</th>
|
|
{% for cid in cids %}
|
|
{% comment %} <th><a href="{% url exam.app_name|add:':exam_scores_cid_user' exam.pk cid %}">{{cid}}</a></th> {% endcomment %}
|
|
<th>
|
|
|
|
{% if cid|slice:":1" == "u" %}
|
|
<a href="{% url exam.app_name|add:':exam_scores_user_admin' exam.pk cid|slice:'2:' %}">
|
|
{% else %}
|
|
|
|
<a href="{% url exam.app_name|add:':exam_scores_cid_user_admin' exam.pk cid|slice:'2:' %}">
|
|
|
|
{% endif %}
|
|
|
|
{{cids_user_id_map|get_item:cid}}</a></th>
|
|
{% endfor %}
|
|
|
|
</tr>
|
|
</thead>
|
|
{% block table_answers %}
|
|
{% endblock table_answers %}
|
|
{% comment %} {% for question in questions %}
|
|
<tr>
|
|
<td><a href="{% url exam.app_name|add:':mark' exam_pk=exam.pk sk=forloop.counter0 %}">Question {{forloop.counter}}</a>
|
|
{% if question.normal %}
|
|
[N]
|
|
{% else %}
|
|
[A]
|
|
{% endif %}
|
|
</td>
|
|
{% for cid in cids %}
|
|
{% with by_question|get_item:question|get_item:cid as ans_score %}
|
|
<td class="user-answer-score-{{ans_score.1}}" title="answer score: {{ans_score.1}} [{{ans_score.2}}]">{{ans_score.0}}</td>
|
|
{% endwith %}
|
|
{% endfor %}
|
|
</tr>
|
|
{% endfor %} {% endcomment %}
|
|
<tr>
|
|
<td>Score:</td>
|
|
{% for cid in cids %}
|
|
<td>{{user_scores|get_item:cid}}</td>
|
|
{% endfor %}
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
{% block breakdown %}{% endblock breakdown %}
|
|
|
|
<details
|
|
hx-get="{% url exam.app_name|add:':exam_report_email_status' exam_id=exam.pk %}"
|
|
hx-trigger="revealed"
|
|
hx-target="#user-details">
|
|
<summary>Email results</summary>
|
|
User results emailed: {{exam.exam_results_emailed|default:"Never"}}<br/>
|
|
<button id="email-results-button"
|
|
title="Email results to users and their supervisor"
|
|
hx-get="{% url exam.app_name|add:':exam_report_email' exam_id=exam.pk %}"
|
|
hx-target="#user-details"
|
|
hx-confirm="This will email results, please make sure scores have been refreshed before continuing"
|
|
hx-prompt="Please enter an additional email to send to (user and supervisor will automatically be used if available) if required"
|
|
>Email user results</button>
|
|
<button id="email-unsent-results-button" title="Email results to users and their supervisor"
|
|
hx-get="{% url exam.app_name|add:':exam_report_email_unsent' exam_id=exam.pk %}"
|
|
hx-target="#user-details"
|
|
hx-confirm="This will email results, please make sure scores have been refreshed before continuing"
|
|
hx-prompt="Please enter an additional email to send to (user and supervisor will automatically be used if available) if required"
|
|
>Email unsent user results</button>
|
|
<button id="email-results-check-button" title="Check the status of emailed results"
|
|
hx-get="{% url exam.app_name|add:':exam_report_email_status' exam_id=exam.pk %}"
|
|
hx-trigger="click"
|
|
hx-target="#user-details">Refresh email status</button>
|
|
<p>Note: currently only works with registered users</p>
|
|
<div id="user-details"></div>
|
|
</details>
|
|
|
|
</div>
|
|
{% endblock %}
|
|
|
|
|
|
{% block js %}
|
|
<script>
|
|
$(document).ready(() => {
|
|
let question_number = document.getElementById("question-number").innerHTML;
|
|
console.log(question_number)
|
|
$(".candidate-row").each((index, el) => {
|
|
console.log(index, el)
|
|
if (el.dataset.answerCount != question_number) {
|
|
$(el).addClass("missing-answers")
|
|
}
|
|
});
|
|
|
|
|
|
});
|
|
|
|
</script>
|
|
<style>
|
|
.missing-answers td:first-child:before {
|
|
content: "*";
|
|
color: red;
|
|
}
|
|
|
|
.search-cid {
|
|
display: none;
|
|
}
|
|
|
|
.cid:hover .search-cid {
|
|
display: inline-block;
|
|
color: darkblue;
|
|
}
|
|
/* Reduce vertical spacing for dense score tables */
|
|
.cid-score-table td, .cid-score-table th, table.table-sm td, table.table-sm th {
|
|
padding: .25rem .4rem;
|
|
line-height: 1;
|
|
}
|
|
#stats-block { font-size: .9rem; }
|
|
</style>
|
|
|
|
{% endblock %} |