Add mark2 overview page and update navigation for improved marking workflow

This commit is contained in:
Ross
2025-11-11 13:56:11 +00:00
parent 1232c3af32
commit 6c149bb2ae
6 changed files with 90 additions and 2 deletions
+1
View File
@@ -7,6 +7,7 @@
<a href="{% url 'anatomy:exam_overview' pk=exam.pk %}">Overview</a> /
{% if exam.exam_mode %}
<a href="{% url 'anatomy:mark_overview' pk=exam.pk %}">Mark</a> /
<a href="{% url 'anatomy:mark2_overview' pk=exam.pk %}">Mark2</a> /
<a href="{% url 'anatomy:exam_scores_all' pk=exam.pk %}">Scores</a> /
<a href="{% url 'anatomy:exam_cids' exam_id=exam.pk %}">Candidates</a> /
<a href="{% url 'anatomy:exam_stats' exam_id=exam.pk %}">Stats</a> /
@@ -0,0 +1,20 @@
{% extends 'anatomy/exams.html' %}
{% block content %}
<div class="anatomy">
<h2>Mark2: Marking exam: {{ exam }}</h2>
Use the improved HTMX marking UI by clicking into a question below.
<div>
<button class="show-all-button">Show all</button><button class="show-unmarked-button">Show unmarked</button>
</div>
<div id="stark-marking-button"><a href="{% url 'anatomy:mark2' exam_pk=exam.pk sk=0 %}"><button>Click here to start mark2</button></a></div>
<ul id="question-mark-list">
{% for question, unmarked_count, unmarked_count2 in question_unmarked_map %}
<li data-markcount={{unmarked_count}} {% if unmarked_count %}class="unmarked" {% endif %}><a href="{% url 'anatomy:mark2' exam_pk=exam.pk sk=forloop.counter0 %}">Question {{forloop.counter }}:
{{ question }}</a><br />Unmarked answers: {{ unmarked_count }}</li>
{% endfor %}
</ul>
</div>
{% endblock %}
+1
View File
@@ -109,6 +109,7 @@ urlpatterns = [
),
# New improved marking UI (mark2) and small toggle endpoint used by JS/HTMX
path("exam/<int:exam_pk>/<int:sk>/mark2", views.mark2, name="mark2"),
path("exam/<int:pk>/mark2", views.mark2_overview, name="mark2_overview"),
path("exam/mark2/toggle", views.mark2_toggle_answer, name="mark2_toggle_answer"),
path("exam/<int:exam_pk>/<int:sk>/mark2/exam_marked", views.mark2_exam_marked, name="mark2_exam_marked"),
path("question/<int:pk>/mark2/question_marked", views.mark2_question_marked, name="mark2_question_marked"),
+60
View File
@@ -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.
@@ -21,7 +21,10 @@
<small class="text-muted">Authors: {{ exam.get_authors }}</small>
{% if exam.exam_mode %}
{% if app_name in "rapids longs anatomy" %}
{% if request.user.is_staff %}<a href="{% url app_name|add:':mark_overview' pk=exam.pk %}">Mark answers</a>{% endif %}
{% if request.user.is_staff %}
<a href="{% url app_name|add:':mark_overview' pk=exam.pk %}">Mark answers</a>
{% if app_name == 'anatomy' %} / <a href="{% url 'anatomy:mark2_overview' pk=exam.pk %}">Mark (mark2)</a>{% endif %}
{% endif %}
{% endif %}
{% if request.user.is_staff %}<a href="{% url app_name|add:':exam_scores_all' pk=exam.pk %}">Scores</a>{% endif %}
{% endif %}
+4 -1
View File
@@ -12,7 +12,10 @@
<a href='{% url "generic:examcollection_detail" exam.examcollection_id %}' title="This exam is part of the collection {{exam.examcollection.name}}"><i class="bi bi-collection"></i></a>
{% endif %}
</span>
{% if marking %}<a href="{% url app_name|add:':mark_overview' pk=exam.pk %}" class="flex-col-half">Mark</a>{% endif %}
{% if marking %}
<a href="{% url app_name|add:':mark_overview' pk=exam.pk %}" class="flex-col-half">Mark</a>
{% if app_name == 'anatomy' %}<a href="{% url 'anatomy:mark2_overview' pk=exam.pk %}" class="flex-col-half">Mark2</a>{% endif %}
{% endif %}
<span class="flex-col">
<a href="{% url app_name|add:':exam_cids' exam_id=exam.pk %}">Candidates</a> <span class="candidate-counts">[<span title="Number of active CID users">
{{exam.valid_cid_users.count}}