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}}
|
{{form}}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</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 %}
|
{% if question_completed %}
|
||||||
{{review.get_display_block}}
|
<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 %}
|
{% endif %}
|
||||||
{% else %}
|
</div>
|
||||||
<h4>Answer score: {{answer.score}}</h4>
|
{% endif %}
|
||||||
Answer feedback: {{answer.feedback|safe}}
|
|
||||||
<br/>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if previous %}
|
{% if previous %}
|
||||||
@@ -458,14 +461,54 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
htmx.on('htmx:afterRequest', onAfter);
|
htmx.on('htmx:afterRequest', onAfter);
|
||||||
htmx.ajax('POST', window.location.href, {
|
// Serialize the entire form so the server receives the user's answers
|
||||||
values: { timed_out: '1' },
|
// along with the timed_out flag. This ensures the form.save() path
|
||||||
swap: 'none',
|
// can validate and persist the submitted answers on timeout.
|
||||||
headers: {
|
(function(){
|
||||||
'X-CSRFToken': document.querySelector('input[name="csrfmiddlewaretoken"]').value,
|
var formValues = {};
|
||||||
},
|
try {
|
||||||
target: "#timer-htmx-target",
|
$.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 {
|
} else {
|
||||||
$timer.text(formatTime(remaining));
|
$timer.text(formatTime(remaining));
|
||||||
updateProgress();
|
updateProgress();
|
||||||
|
|||||||
@@ -10,21 +10,20 @@
|
|||||||
<li class="case">
|
<li class="case">
|
||||||
|
|
||||||
<h4>{{forloop.counter}} / Case: {{casedetail.case.title}}</h4>
|
<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 %}
|
{% if request.user.is_superuser and user_answer %}
|
||||||
(<a href="{% url 'admin:atlas_userreportanswer_change' user_answer.pk %}" target="_blank">Edit in admin</a>)
|
(<a href="{% url 'admin:atlas_userreportanswer_change' user_answer.pk %}" target="_blank">Edit in admin</a>)
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<br/>
|
<br/>
|
||||||
|
<br/>
|
||||||
{% if not user_answer %}
|
{% if not user_answer %}
|
||||||
<span class="case-not-answered">Case not answered.</span>
|
<span class="case-not-answered">Case not answered.</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="answer-block">
|
<div class="answer-block">
|
||||||
|
User answer:
|
||||||
|
<div class="user-answer">
|
||||||
{% if user_answer.answer %}
|
{% if user_answer.answer %}
|
||||||
{{user_answer.answer}}
|
{{user_answer.answer}}
|
||||||
|
|
||||||
{% else %}
|
{% else %}
|
||||||
{{user_answer.json_answer}}
|
{{user_answer.json_answer}}
|
||||||
{% for value, user_answer, correct_answer, answer_is_correct, automark in user_answer.get_correct_json_answers %}
|
{% for value, user_answer, correct_answer, answer_is_correct, automark in user_answer.get_correct_json_answers %}
|
||||||
@@ -57,6 +56,7 @@
|
|||||||
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|||||||
@@ -2833,7 +2833,6 @@ def collection_case_view_take(
|
|||||||
or getattr(request, 'htmx', False)
|
or getattr(request, 'htmx', False)
|
||||||
)
|
)
|
||||||
if is_ajax:
|
if is_ajax:
|
||||||
from django.http import JsonResponse
|
|
||||||
return JsonResponse(
|
return JsonResponse(
|
||||||
{
|
{
|
||||||
'status': 'ok',
|
'status': 'ok',
|
||||||
|
|||||||
Reference in New Issue
Block a user