diff --git a/physics/templates/physics/exam_take.html b/physics/templates/physics/exam_take.html
index 93e2a42c..e1ef7f25 100755
--- a/physics/templates/physics/exam_take.html
+++ b/physics/templates/physics/exam_take.html
@@ -3,17 +3,24 @@
{% block content %}
{% if request.user.is_authenticated %}
- User: {{request.user}}
+ User: {{ request.user }}
{% else %}
- CID: {{cid}}
+ CID: {{ cid }}
{% endif %}
+
{% include "exam_clock.html" %}
+
-
{{exam}}: Question [{{pos|add:1}}/{{exam_length}}]
+
+ {{ exam }}: Question
+ [{{ pos|add:1 }}/{{ exam_length }}]
+
+
{% if exam.publish_results %}
- Exam is in review mode. Add question feedback
here.
+ Exam is in review mode. Add question feedback
+
here.
{% elif cid_user_exam.completed %}
@@ -22,63 +29,68 @@
{% endif %}
-
{{question.stem|safe}}
+
{{ question.stem|safe }}
+
+
Questions
-
{% include "physics/exam_take_help.html" %}
- {% endblock %}
+{% endblock %}
- {% block js %}
-
+{% endblock %}
- })
+{% block css %}
+
- {% endblock %}
+ /* Question menu (jump buttons) */
+ #menu-list { margin-top: 1rem; display:flex; flex-wrap:wrap; gap:0.4rem; }
+ button.question-menu-item {
+ padding: 0.35rem 0.6rem;
+ border-radius: 0.35rem;
+ border: 1px solid var(--card-border);
+ background: var(--card-bg);
+ color: var(--text);
+ cursor: pointer;
+ font-size: 0.9rem;
+ }
+ button.question-menu-item.current-question {
+ background: var(--primary); color: #fff; border-color: var(--primary);
+ box-shadow: 0 1px 3px rgba(13,110,253,0.18);
+ }
+
+ /* Control buttons spacing */
+ .mt-2 .save { margin-right: 0.5rem; }
+ .mt-2 .save.btn.btn-default { background: var(--feedback-bg); border: 1px solid var(--card-border); color: var(--text); }
+
+ /* Small helper: make labels wrap nicely on small screens */
+ @media (max-width: 576px) {
+ .flex-container { flex-direction: column; align-items: stretch; }
+ .postinput { margin-top: 0.4rem; }
+ .physics-answer-list li { padding: 0.6rem; }
+ }
+
+{% endblock %}
diff --git a/physics/views.py b/physics/views.py
index e08e45ac..d8149859 100644
--- a/physics/views.py
+++ b/physics/views.py
@@ -188,7 +188,16 @@ def exam_take_overview(request, pk, cid=None, passcode=None):
)
-def exam_take(request, pk: int, sk: int, cid: str| None = None, passcode: str| None = None):
+def exam_take(request, pk: int, sk: int, cid: str | None = None, passcode: str | None = None):
+ """Display and handle answers for a single question in a physics exam.
+
+ Behavior and invariants:
+ - Uses a single canonical list of questions (from `exam.get_questions()`) for
+ selection, navigation and sizing so indexes cannot drift.
+ - Validates the incoming question index and raises 404 for invalid values.
+ - Supports candidate (cid) and logged-in user answers.
+ """
+
exam = get_object_or_404(Exam, pk=pk)
if not exam.active:
@@ -198,25 +207,23 @@ def exam_take(request, pk: int, sk: int, cid: str| None = None, passcode: str| N
cid_user_exam = exam.get_or_create_cid_user_exam(cid=cid, user_user=request.user)
- # Use a single, canonical list of questions for all indexing/navigation
- # operations to avoid mismatches between different querysets or orderings.
+ # Canonical questions list for consistent indexing/navigation
questions = list(exam.get_questions())
- # Validate sk and select the current question
+ # Normalize and validate provided index
try:
- sk = int(sk)
+ index = int(sk)
except Exception:
raise Http404("Invalid question index")
- if sk < 0 or sk >= len(questions):
+ if index < 0 or index >= len(questions):
raise Http404("Question not found in exam")
- question = questions[sk]
-
+ question = questions[index]
exam_length = len(questions)
- # Since we're using the canonical list, the positional index is sk.
- pos = sk
+ # local numeric position equals the validated index
+ pos = index
if cid is not None:
answer = question.cid_user_answers.filter(cid=cid, exam=exam).first()