Refactor exam review URLs and streamline HTMX request handling in views
This commit is contained in:
+10
-10
@@ -315,16 +315,6 @@ def generic_view_urls(generic_views: GenericViewBase):
|
|||||||
generic_views.question_user_answers,
|
generic_views.question_user_answers,
|
||||||
name="question_user_answers",
|
name="question_user_answers",
|
||||||
),
|
),
|
||||||
path(
|
|
||||||
"exam/<int:pk>/review/start",
|
|
||||||
generic_views.exam_review_start,
|
|
||||||
name="exam_review_start",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"exam/<int:pk>/review/<int:q_index>",
|
|
||||||
generic_views.exam_review_question,
|
|
||||||
name="exam_review_question",
|
|
||||||
),
|
|
||||||
path(
|
path(
|
||||||
"question/<int:pk>/user_answers/<str:answer_compare>/str",
|
"question/<int:pk>/user_answers/<str:answer_compare>/str",
|
||||||
generic_views.question_user_answers_by_compare,
|
generic_views.question_user_answers_by_compare,
|
||||||
@@ -363,6 +353,16 @@ def generic_exam_urls(generic_exam_view: GenericExamViews):
|
|||||||
generic_exam_view.exam_question_detail,
|
generic_exam_view.exam_question_detail,
|
||||||
name="exam_question_detail",
|
name="exam_question_detail",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"exam/<int:pk>/review/start",
|
||||||
|
generic_exam_view.exam_review_start,
|
||||||
|
name="exam_review_start",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"exam/<int:pk>/review/<int:q_index>",
|
||||||
|
generic_exam_view.exam_review_question,
|
||||||
|
name="exam_review_question",
|
||||||
|
),
|
||||||
# path(
|
# path(
|
||||||
# "exam/<int:pk>/question/<int:sk>/answer/<str:user_or_cid>",
|
# "exam/<int:pk>/question/<int:sk>/answer/<str:user_or_cid>",
|
||||||
# generic_exam_view.exam_question_user_answer,
|
# generic_exam_view.exam_question_user_answer,
|
||||||
|
|||||||
+1
-62
@@ -1216,13 +1216,7 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
"can_edit": self.check_user_edit_access(request.user, exam_id=pk),
|
"can_edit": self.check_user_edit_access(request.user, exam_id=pk),
|
||||||
}
|
}
|
||||||
|
|
||||||
# If this is an HTMX request, return only the fragment so the client
|
if request.htmx:
|
||||||
# can swap it into the page. HTMX sets the `HX-Request` header; use
|
|
||||||
# request.headers (case-insensitive) where available and fall back to
|
|
||||||
# the META variant for older environments/proxies.
|
|
||||||
is_htmx = True if request.htmx.prompt is not None else False
|
|
||||||
|
|
||||||
if is_htmx:
|
|
||||||
return render(request, "generic/partials/exam_review_question_fragment.html", context)
|
return render(request, "generic/partials/exam_review_question_fragment.html", context)
|
||||||
|
|
||||||
return render(request, "generic/exam_review_question.html", context)
|
return render(request, "generic/exam_review_question.html", context)
|
||||||
@@ -3284,61 +3278,6 @@ class GenericViewBase:
|
|||||||
|
|
||||||
return render(request, "generic/partials/question_reviews_list.html", {"question": question, "reviews": reviews, "app_name": self.app_name})
|
return render(request, "generic/partials/question_reviews_list.html", {"question": question, "reviews": reviews, "app_name": self.app_name})
|
||||||
|
|
||||||
@method_decorator(login_required)
|
|
||||||
def exam_review_start(self, request, pk):
|
|
||||||
"""Start a per-question review for an exam. Redirects to the first question."""
|
|
||||||
# Ensure the user can edit the exam (authors only)
|
|
||||||
if not self.check_user_edit_access(request.user, exam_id=pk):
|
|
||||||
raise PermissionDenied
|
|
||||||
|
|
||||||
exam = get_object_or_404(self.exam_object, pk=pk)
|
|
||||||
|
|
||||||
# Materialise ordered questions
|
|
||||||
questions = list(exam.get_questions())
|
|
||||||
if not questions:
|
|
||||||
return render(request, "generic/exam_review_complete.html", {"exam": exam, "app_name": self.app_name})
|
|
||||||
|
|
||||||
# Render the first question
|
|
||||||
return self.exam_review_question(request, pk, q_index=0)
|
|
||||||
|
|
||||||
@method_decorator(login_required)
|
|
||||||
def exam_review_question(self, request, pk, q_index=0):
|
|
||||||
"""Render a single question from an exam for review with navigation."""
|
|
||||||
if not self.check_user_edit_access(request.user, exam_id=pk):
|
|
||||||
raise PermissionDenied
|
|
||||||
|
|
||||||
exam = get_object_or_404(self.exam_object, pk=pk)
|
|
||||||
|
|
||||||
questions = list(exam.get_questions())
|
|
||||||
total = len(questions)
|
|
||||||
|
|
||||||
try:
|
|
||||||
q_index = int(q_index)
|
|
||||||
except Exception:
|
|
||||||
q_index = 0
|
|
||||||
|
|
||||||
if q_index < 0 or q_index >= total:
|
|
||||||
# Out of range — render complete
|
|
||||||
return render(request, "generic/exam_review_complete.html", {"exam": exam, "app_name": self.app_name})
|
|
||||||
|
|
||||||
question = questions[q_index]
|
|
||||||
|
|
||||||
prev_index = q_index - 1 if q_index > 0 else None
|
|
||||||
next_index = q_index + 1 if q_index < total - 1 else None
|
|
||||||
|
|
||||||
context = {
|
|
||||||
"exam": exam,
|
|
||||||
"question": question,
|
|
||||||
"q_index": q_index,
|
|
||||||
"total": total,
|
|
||||||
"prev_index": prev_index,
|
|
||||||
"next_index": next_index,
|
|
||||||
"app_name": self.app_name,
|
|
||||||
"can_edit": self.check_user_edit_access(request.user, exam_id=pk),
|
|
||||||
}
|
|
||||||
|
|
||||||
return render(request, "generic/exam_review_question.html", context)
|
|
||||||
|
|
||||||
def question_review_start(self, request):
|
def question_review_start(self, request):
|
||||||
|
|
||||||
# Prepare category choices if the question model exposes a FK named 'category'
|
# Prepare category choices if the question model exposes a FK named 'category'
|
||||||
|
|||||||
Reference in New Issue
Block a user