Refactor self-review display logic and enhance user answer feedback in collection templates; improve form serialization for timeout handling
This commit is contained in:
@@ -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 %}
|
||||
(<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 %}
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user