From 6c149bb2ae719fbc0f165bb053522d99b22aff18 Mon Sep 17 00:00:00 2001 From: Ross Date: Tue, 11 Nov 2025 13:56:11 +0000 Subject: [PATCH] Add mark2 overview page and update navigation for improved marking workflow --- anatomy/templates/anatomy/exams.html | 1 + anatomy/templates/anatomy/mark2_overview.html | 20 +++++++ anatomy/urls.py | 1 + anatomy/views.py | 60 +++++++++++++++++++ .../generic/partials/_exam_list.html | 5 +- templates/exam_list.html | 5 +- 6 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 anatomy/templates/anatomy/mark2_overview.html diff --git a/anatomy/templates/anatomy/exams.html b/anatomy/templates/anatomy/exams.html index c34a76a3..aa1c536e 100644 --- a/anatomy/templates/anatomy/exams.html +++ b/anatomy/templates/anatomy/exams.html @@ -7,6 +7,7 @@ Overview / {% if exam.exam_mode %} Mark / + Mark2 / Scores / Candidates / Stats / diff --git a/anatomy/templates/anatomy/mark2_overview.html b/anatomy/templates/anatomy/mark2_overview.html new file mode 100644 index 00000000..ebfa1baf --- /dev/null +++ b/anatomy/templates/anatomy/mark2_overview.html @@ -0,0 +1,20 @@ +{% extends 'anatomy/exams.html' %} + +{% block content %} +
+

Mark2: Marking exam: {{ exam }}

+ Use the improved HTMX marking UI by clicking into a question below. + +
+ +
+ + + +
+{% endblock %} diff --git a/anatomy/urls.py b/anatomy/urls.py index 9e42e44c..793ff6d5 100644 --- a/anatomy/urls.py +++ b/anatomy/urls.py @@ -109,6 +109,7 @@ urlpatterns = [ ), # New improved marking UI (mark2) and small toggle endpoint used by JS/HTMX path("exam///mark2", views.mark2, name="mark2"), + path("exam//mark2", views.mark2_overview, name="mark2_overview"), 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"), diff --git a/anatomy/views.py b/anatomy/views.py index 3bc96761..ec88af5f 100644 --- a/anatomy/views.py +++ b/anatomy/views.py @@ -15,6 +15,7 @@ from django.views.generic.edit import CreateView, UpdateView, DeleteView from django.views.generic import ListView from django.db.models.functions import Lower +from django.db.models import Prefetch from django.core.cache import cache @@ -780,6 +781,65 @@ def mark2(request, exam_pk, sk, unmarked_exam_answers_only=True, review=False): return render(request, "anatomy/mark2.html", context) +@login_required +def mark2_overview(request, pk): + """Overview page for mark2 workflow: lists questions and unmarked counts and links into mark2.""" + exam = get_object_or_404(Exam, pk=pk) + + if not exam.exam_mode: + raise Http404("Packet not in exam mode") + + # Only allow authors or designated markers + if request.user not in exam.author.all(): + if not GenericExamViews.check_user_marker_access(request.user, pk): + raise PermissionDenied + + # For anatomy prefer prefetch similar to generic.mark_overview + questions = ( + exam.exam_questions.select_related( + "modality", + "structure", + "body_part", + "region", + "examination", + "question_type", + ) + .all() + .prefetch_related( + Prefetch( + "answers", + queryset=Answer.objects.filter(proposed=False, status=Answer.MarkOptions.CORRECT), + to_attr="prefetched_primary_answer", + ), + ) + .order_by("examquestiondetail__sort_order") + ) + + # Handle double marking which needs per-marker counts + try: + if exam.double_mark: + question_unmarked_map = [] + for q in questions: + question_unmarked_map.append( + ( + q, + int(q.get_unmarked_user_answer_count(exam_pk=exam.pk)), + int(q.get_unmarked_user_answer_count(exam_pk=exam.pk, marker=request.user)), + ) + ) + + return render(request, "anatomy/mark2_overview.html", {"exam": exam, "question_unmarked_map": question_unmarked_map}) + except AttributeError: + pass + + question_unmarked_map = [] + for q in questions: + count = int(q.get_unmarked_user_answer_count(exam_pk=exam.pk)) + question_unmarked_map.append((q, count, count)) + + return render(request, "anatomy/mark2_overview.html", {"exam": exam, "question_unmarked_map": question_unmarked_map}) + + @login_required def mark2_toggle_answer(request): """Endpoint to toggle/set a single answer mark quickly. diff --git a/generic/templates/generic/partials/_exam_list.html b/generic/templates/generic/partials/_exam_list.html index 65893ba6..a64544a5 100644 --- a/generic/templates/generic/partials/_exam_list.html +++ b/generic/templates/generic/partials/_exam_list.html @@ -21,7 +21,10 @@ Authors: {{ exam.get_authors }} {% if exam.exam_mode %} {% if app_name in "rapids longs anatomy" %} - {% if request.user.is_staff %}Mark answers{% endif %} + {% if request.user.is_staff %} + Mark answers + {% if app_name == 'anatomy' %} / Mark (mark2){% endif %} + {% endif %} {% endif %} {% if request.user.is_staff %}Scores{% endif %} {% endif %} diff --git a/templates/exam_list.html b/templates/exam_list.html index d6964448..cc38e3b3 100644 --- a/templates/exam_list.html +++ b/templates/exam_list.html @@ -12,7 +12,10 @@ {% endif %} - {% if marking %}Mark{% endif %} + {% if marking %} + Mark + {% if app_name == 'anatomy' %}Mark2{% endif %} + {% endif %} Candidates [ {{exam.valid_cid_users.count}}