+
Answers submitted in this exam
-
+
By count
By score
+
Enable editing
{% if not items %}
@@ -16,18 +24,35 @@
{% for it in items %}
-
-
+
+
+ {# Stored mark badge #}
{% if it.mark == 'correct' %}
- 2
+ 2
{% elif it.mark == 'half-correct' %}
- 1
+ 1
{% elif it.mark == 'incorrect' %}
- 0
+ 0
{% else %}
- -
+ -
{% endif %}
+
+ {# Submission count badge #}
{{ it.count }}
+
+
+
+ Edit
{% endfor %}
@@ -35,3 +60,23 @@
{% endif %}
+
+
diff --git a/anatomy/urls.py b/anatomy/urls.py
index a662c6d3..3b4d2d59 100644
--- a/anatomy/urls.py
+++ b/anatomy/urls.py
@@ -107,6 +107,7 @@ urlpatterns = [
path("exam/mark2/toggle", views.mark2_toggle_answer, name="mark2_toggle_answer"),
path("exam/
//mark2/exam_marked", views.mark2_exam_marked, name="mark2_exam_marked"),
path("question//mark2/question_marked", views.mark2_question_marked, name="mark2_question_marked"),
+ path("exam/mark2/edit_answer", views.mark2_edit_answer, name="mark2_edit_answer"),
]
urlpatterns.extend(generic_view_urls(views.GenericViews))
diff --git a/anatomy/views.py b/anatomy/views.py
index 1f1fbbf9..0861ef29 100644
--- a/anatomy/views.py
+++ b/anatomy/views.py
@@ -818,6 +818,24 @@ def mark2_toggle_answer(request):
"incorrect": Answer.MarkOptions.INCORRECT,
}
+ # Support clearing a stored answer (delete) via 'clear'
+ exam_pk = request.POST.get('exam_pk')
+ sk = request.POST.get('sk')
+
+ if newmark == 'clear':
+ a = Answer.objects.filter(answer_compare__iexact=answer_text, question_id=question.pk).first()
+ if a is not None:
+ # permission: ensure user can edit this answer
+ if not a.can_edit(request.user):
+ return JsonResponse({"status": "error", "message": "forbidden"}, status=403)
+ a.delete()
+ if request.htmx and exam_pk and sk is not None:
+ try:
+ return mark2_exam_marked(request, int(exam_pk), int(sk))
+ except Exception:
+ return JsonResponse({"status": "ok"})
+ return JsonResponse({"status": "ok"})
+
if newmark not in mark_map:
return JsonResponse({"status": "error", "message": "invalid mark"}, status=400)
@@ -833,6 +851,12 @@ def mark2_toggle_answer(request):
a.status = status_val
a.save()
+ if request.htmx and exam_pk and sk is not None:
+ try:
+ return mark2_exam_marked(request, int(exam_pk), int(sk))
+ except Exception:
+ return JsonResponse({"status": "ok"})
+
return JsonResponse({"status": "ok"})
@@ -879,10 +903,71 @@ def mark2_exam_marked(request, exam_pk, sk):
else:
items.sort(key=lambda x: x.get('count', 0), reverse=True)
- context = {'exam': exam, 'question': question, 'items': items}
+ context = {'exam': exam, 'question': question, 'items': items, 'sk': sk}
return render(request, 'anatomy/partials/mark2_exam_marked_fragment.html', context)
+@login_required
+def mark2_edit_answer(request):
+ """HTMX-friendly endpoint to edit/create a stored Answer for a question.
+
+ GET: returns a small form fragment.
+ POST: updates/creates the Answer and, if exam_pk/sk provided, returns the
+ refreshed exam-marked fragment so the UI updates in-place.
+ """
+ if request.method == 'GET' and request.htmx:
+ question_pk = request.GET.get('question_pk')
+ original = request.GET.get('original', '')
+ exam_pk = request.GET.get('exam_pk')
+ sk = request.GET.get('sk')
+ question = get_object_or_404(AnatomyQuestion, pk=question_pk)
+ # Try to find an existing Answer by compare
+ existing = question.answers.filter(answer_compare__iexact=original).first()
+ context = {
+ 'question': question,
+ 'original': original,
+ 'existing': existing,
+ 'exam_pk': exam_pk,
+ 'sk': sk,
+ }
+ return render(request, 'anatomy/partials/mark2_edit_answer_fragment.html', context)
+
+ if request.method == 'POST' and request.htmx:
+ question_pk = request.POST.get('question_pk')
+ original = request.POST.get('original', '')
+ new_text = request.POST.get('new_answer', '').strip()
+ exam_pk = request.POST.get('exam_pk')
+ sk = request.POST.get('sk')
+
+ if not question_pk or not new_text:
+ return HttpResponse('Missing parameters', status=400)
+
+ question = get_object_or_404(AnatomyQuestion, pk=question_pk)
+
+ a = question.answers.filter(answer_compare__iexact=original).first()
+ if a is None:
+ a = Answer()
+ a.question = question
+ a.answer = new_text
+ else:
+ a.answer = new_text
+
+ a.save()
+
+ # If we have exam context, refresh the exam-marked fragment for that question
+ if exam_pk and sk is not None:
+ # delegate to mark2_exam_marked to re-render the card
+ try:
+ return mark2_exam_marked(request, int(exam_pk), int(sk))
+ except Exception:
+ return HttpResponse('OK')
+
+ return HttpResponse('OK')
+
+ # Fallback: forbidden for non-HTMX or other methods
+ raise PermissionDenied()
+
+
@login_required
def mark2_question_marked(request, pk):
"""Return a partial listing of all stored Answer choices for this question (grouped by status)."""