diff --git a/generic/templates/generic/exam_cids.html b/generic/templates/generic/exam_cids.html index e12ae5f7..c0dcac02 100644 --- a/generic/templates/generic/exam_cids.html +++ b/generic/templates/generic/exam_cids.html @@ -19,37 +19,31 @@
- {% if cid_users %} +
- {% else %} -

This exam has no CID candidates. Add some with the edit link above.

- {% endif %} +
@@ -61,49 +55,37 @@
- {% if user_users %} +
- {% else %} -

This exam has no User candidates. Add some with the edit link above.

- {% endif %} +
- {% if user_exam_data %} -
-
-
-
-
Submitted candidates
-

The following candidate submissions have been received. Candidates with submissions are highlighted above.

-
    - {% for user_data in user_exam_data %} -
  • CID: {{user_data.cid_user}} / User: {{user_data.user_user}} / Start: {{user_data.start_time}} / End: {{user_data.end_time}}
  • - {% endfor %} + {% if has_submissions %} +
    +
    +
    +
    +
    +

    +
      +
    • +
    @@ -135,23 +117,9 @@ {% block js %} {% endblock %} diff --git a/generic/templates/generic/partials/exam_cids_cid_list.html b/generic/templates/generic/partials/exam_cids_cid_list.html new file mode 100644 index 00000000..0e23d7c4 --- /dev/null +++ b/generic/templates/generic/partials/exam_cids_cid_list.html @@ -0,0 +1,31 @@ +{% comment %} Partial: list of CID candidates for HTMX swap {% endcomment %} +{% if cid_users %} +
      + {% for cid in cid_users %} +
    • +
      +
      +
      + + {{ cid.cid }} + [{{ cid.passcode }}] + +
      +
      {{ cid.name }}
      +
      Email: {{ cid.email }}
      + {% if cid.notes %} +
      Notes: {{ cid.notes|truncatechars:120 }}
      + {% endif %} +
      +
      +
    • + {% endfor %} +
    +{% else %} +

    This exam has no CID candidates.

    +{% endif %} diff --git a/generic/templates/generic/partials/exam_cids_submitted_list.html b/generic/templates/generic/partials/exam_cids_submitted_list.html new file mode 100644 index 00000000..2d676f0f --- /dev/null +++ b/generic/templates/generic/partials/exam_cids_submitted_list.html @@ -0,0 +1,16 @@ +{% comment %} Partial: submitted candidates fragment for HTMX swap {% endcomment %} +{% if user_exam_data %} +
    +
    +
    Submitted candidates
    +

    The following candidate submissions have been received. Candidates with submissions are highlighted above.

    +
      + {% for user_data in user_exam_data %} +
    • CID: {{user_data.cid_user}} / User: {{user_data.user_user}} / Start: {{user_data.start_time}} / End: {{user_data.end_time}}
    • + {% endfor %} +
    +
    +
    +{% else %} +

    No submissions yet.

    +{% endif %} diff --git a/generic/templates/generic/partials/exam_cids_user_list.html b/generic/templates/generic/partials/exam_cids_user_list.html new file mode 100644 index 00000000..c754a397 --- /dev/null +++ b/generic/templates/generic/partials/exam_cids_user_list.html @@ -0,0 +1,27 @@ +{% comment %} Partial: list of User candidates for HTMX swap {% endcomment %} +{% if user_users %} +
      + {% for user in user_users %} +
    • +
      +
      + + {% if request.user.is_superuser %} + + {% endif %} + {{ user.username }} +
      +
      Email: {{ user.email }}
      +
      +
       
      +
    • + {% endfor %} +
    +{% else %} +

    This exam has no User candidates.

    +{% endif %} diff --git a/generic/urls.py b/generic/urls.py index df7b7ac4..dbc83b56 100755 --- a/generic/urls.py +++ b/generic/urls.py @@ -523,6 +523,9 @@ def generic_exam_urls(generic_exam_view: GenericExamViews): name="order_questions", ), path("exam//cids", generic_exam_view.exam_cids, name="exam_cids"), + path("exam//cids/cid_list", generic_exam_view.exam_cids_cid_list, name="exam_cids_cid_list"), + path("exam//cids/user_list", generic_exam_view.exam_cids_user_list, name="exam_cids_user_list"), + path("exam//cids/submitted_list", generic_exam_view.exam_cids_submitted_list, name="exam_cids_submitted_list"), # path("exam//groups", generic_exam_view.exam_groups_edit, name="exam_groups_edit"), path( "exam//reset_answers", diff --git a/generic/views.py b/generic/views.py index d997e93e..d6d3cef7 100644 --- a/generic/views.py +++ b/generic/views.py @@ -2183,23 +2183,20 @@ class ExamViews(View, LoginRequiredMixin): if not request.user.is_superuser: raise PermissionDenied - cid_users = ( - exam.valid_cid_users.all() - ) # .order_by("cid")#.prefetch_related("*") - cid_user_count = len(cid_users) + # Avoid loading large querysets here — the detailed lists are + # rendered by HTMX partials which will fetch the data separately. + cid_user_count = exam.valid_cid_users.count() + user_user_count = exam.valid_user_users.count() - user_users = exam.valid_user_users.all() # .order_by("username") - user_user_count = len(user_users) - - user_exam_data = exam.cid_users.all().prefetch_related("cid_user", "user_user") + # Provide a lightweight indicator if there are any submissions so the + # template can decide whether to load the submitted-list partial. + has_submissions = exam.cid_users.exists() context = { "exam": exam, - "cid_users": cid_users, "cid_user_count": cid_user_count, - "user_exam_data": user_exam_data, - "user_users": user_users, "user_user_count": user_user_count, + "has_submissions": has_submissions, "app_name": self.app_name, } @@ -2208,6 +2205,36 @@ class ExamViews(View, LoginRequiredMixin): return render(request, "exam_cids.html", context) + def exam_cids_cid_list(self, request, exam_id): + """HTMX partial returning the CID candidates list for an exam.""" + exam = get_object_or_404(self.Exam, pk=exam_id) + if request.user not in exam.author.all() and not request.user.is_superuser: + raise PermissionDenied + + cid_users = exam.valid_cid_users.all() + + return render(request, "generic/partials/exam_cids_cid_list.html", {"cid_users": cid_users, "exam": exam}) + + def exam_cids_user_list(self, request, exam_id): + """HTMX partial returning the User candidates list for an exam.""" + exam = get_object_or_404(self.Exam, pk=exam_id) + if request.user not in exam.author.all() and not request.user.is_superuser: + raise PermissionDenied + + user_users = exam.valid_user_users.all() + + return render(request, "generic/partials/exam_cids_user_list.html", {"user_users": user_users, "exam": exam}) + + def exam_cids_submitted_list(self, request, exam_id): + """HTMX partial returning the submitted candidates summary.""" + exam = get_object_or_404(self.Exam, pk=exam_id) + if request.user not in exam.author.all() and not request.user.is_superuser: + raise PermissionDenied + + user_exam_data = exam.cid_users.all().prefetch_related("cid_user", "user_user") + + return render(request, "generic/partials/exam_cids_submitted_list.html", {"user_exam_data": user_exam_data, "exam": exam}) + # def exam_groups_edit(self, request, exam_id): # exam = get_object_or_404(self.Exam, pk=exam_id)