Refactor self-review display logic and enhance user answer feedback in collection templates; improve form serialization for timeout handling

This commit is contained in:
Ross
2025-10-13 11:48:13 +01:00
parent 32d8430f94
commit ed4bd95955
3 changed files with 72 additions and 30 deletions
@@ -221,25 +221,28 @@
{{form}}
</fieldset>
</div>
<div>
{% if collection.self_review %}
<p>
<a target="_blank" title="Add self review, this will open in a new page / tab" href="{% url 'atlas:add_self_review' cid_user_exam.id case.id %}"><button type="button">Add self review</button></a>
</p>
{% if self_review %}
<h4>Self Feedback</h4>
{% for review in self_review %}
{{review.get_display_block}}
{% if question_completed %}
<div>
{% if collection.self_review %}
<p>
<a target="_blank" title="Add self review, this will open in a new page / tab" href="{% url 'atlas:add_self_review' cid_user_exam.id case.id %}"><button type="button">Add self review</button></a>
</p>
{% if self_review %}
<h4>Self Feedback</h4>
{% endfor %}
{% for review in self_review %}
{{review.get_display_block}}
{% endfor %}
{% endif %}
{% else %}
<h4>Answer score: {{answer.score}}</h4>
Answer feedback: {{answer.feedback|safe}}
<br/>
{% endif %}
{% else %}
<h4>Answer score: {{answer.score}}</h4>
Answer feedback: {{answer.feedback|safe}}
<br/>
{% endif %}
</div>
</div>
{% endif %}
{% endif %}
{% if previous %}
@@ -458,14 +461,54 @@
};
htmx.on('htmx:afterRequest', onAfter);
htmx.ajax('POST', window.location.href, {
values: { timed_out: '1' },
swap: 'none',
headers: {
'X-CSRFToken': document.querySelector('input[name="csrfmiddlewaretoken"]').value,
},
target: "#timer-htmx-target",
});
// Serialize the entire form so the server receives the user's answers
// along with the timed_out flag. This ensures the form.save() path
// can validate and persist the submitted answers on timeout.
(function(){
var formValues = {};
try {
$.each($form.serializeArray(), function(i, field) {
if (formValues[field.name] !== undefined) {
if (!Array.isArray(formValues[field.name])) {
formValues[field.name] = [formValues[field.name]];
}
formValues[field.name].push(field.value);
} else {
formValues[field.name] = field.value;
}
});
} catch (e) {
console.debug('Failed to serialize form with jQuery, falling back to manual collection', e);
// Fallback: try to collect inputs manually
var inputs = $form.find('input, textarea, select').not(':disabled');
inputs.each(function () {
var $el = $(this);
var name = $el.attr('name');
if (!name) return;
var val = $el.val();
if (formValues[name] !== undefined) {
if (!Array.isArray(formValues[name])) {
formValues[name] = [formValues[name]];
}
formValues[name].push(val);
} else {
formValues[name] = val;
}
});
}
// Ensure the timed_out flag is included
formValues['timed_out'] = '1';
htmx.ajax('POST', window.location.href, {
values: formValues,
swap: 'none',
headers: {
'X-CSRFToken': document.querySelector('input[name="csrfmiddlewaretoken"]').value,
},
target: "#timer-htmx-target",
});
})();
} else {
$timer.text(formatTime(remaining));
updateProgress();
@@ -10,21 +10,20 @@
<li class="case">
<h4>{{forloop.counter}} / Case: {{casedetail.case.title}}</h4>
{{user_answer.started_at}} - {{user_answer.submitted_at}}
Question started: {{user_answer.started_at}} - Answer submitted: {{user_answer.submitted_at}}
{% if request.user.is_superuser and user_answer %}
&nbsp;(<a href="{% url 'admin:atlas_userreportanswer_change' user_answer.pk %}" target="_blank">Edit in admin</a>)
{% endif %}
<br/>
<br/>
{% if not user_answer %}
<span class="case-not-answered">Case not answered.</span>
{% else %}
<div class="answer-block">
User answer:
<div class="user-answer">
{% if user_answer.answer %}
{{user_answer.answer}}
{% else %}
{{user_answer.json_answer}}
{% for value, user_answer, correct_answer, answer_is_correct, automark in user_answer.get_correct_json_answers %}
@@ -57,6 +56,7 @@
{% endfor %}
{% endif %}
</div>
</div>
{% endif %}
-1
View File
@@ -2833,7 +2833,6 @@ def collection_case_view_take(
or getattr(request, 'htmx', False)
)
if is_ajax:
from django.http import JsonResponse
return JsonResponse(
{
'status': 'ok',