From e2c8f751f2a18024e108bdd0c3728508773cf4d2 Mon Sep 17 00:00:00 2001 From: Ross Date: Sun, 26 Jun 2022 22:45:52 +0100 Subject: [PATCH] . --- anatomy/templates/anatomy/exam_scores.html | 76 ++----- .../templates/anatomy/exam_scores_new.html | 81 ------- anatomy/urls.py | 4 + anatomy/views.py | 203 ------------------ .../templates/generic/exam_scores_base.html | 33 ++- generic/views.py | 7 +- physics/templates/physics/exam_scores.html | 77 ++----- .../templates/physics/exam_scores_new.html | 73 ------- physics/urls.py | 4 + rapids/templates/rapids/exam_scores.html | 105 +++------ sbas/templates/sbas/exam_scores.html | 76 ++----- sbas/templates/sbas/exam_scores_new.html | 70 ------ sbas/urls.py | 4 + 13 files changed, 108 insertions(+), 705 deletions(-) delete mode 100644 anatomy/templates/anatomy/exam_scores_new.html rename rapids/templates/rapids/exam_scores_new.html => generic/templates/generic/exam_scores_base.html (77%) delete mode 100644 physics/templates/physics/exam_scores_new.html delete mode 100644 sbas/templates/sbas/exam_scores_new.html diff --git a/anatomy/templates/anatomy/exam_scores.html b/anatomy/templates/anatomy/exam_scores.html index 6e0f089d..ac78623e 100644 --- a/anatomy/templates/anatomy/exam_scores.html +++ b/anatomy/templates/anatomy/exam_scores.html @@ -1,71 +1,19 @@ -{% extends 'anatomy/exams.html' %} +{% extends 'generic/exam_scores_base.html' %} -{% block content %} -
-

{{ exam.name }}

- User answer scores are cached, if you update or change an answer you will need to refresh the scores. - - {% if unmarked %} - - - {% endif %} - -
-
-

Stats

- Candidates: {{cids|length}}
- Max score: {{max_score}}
- Mean: {{mean}}, Median {{median}}, Mode {{mode}} - -
{{plot|safe}}
-
-
- - - - - - {% for user, value in user_answers_marks.items %} - - - - - - {% endfor %} -
Candidate IDScore
{{user}}{{user}}{{user_scores|get_item:user}}
-
-
-

Results as a table

- - - - - {% for cid in cids %} - - {% endfor %} - - - +{% block table_answers %} {% for question in questions %} - - {% for ans, score in by_question|get_item:question %} - + + {% comment %} {% for cid in cids %} + + {% endfor %} {% endcomment %} + {% for cid in cids %} + {% with by_question|get_item:question|get_item:cid as ans_score %} + + {% endwith %} {% endfor %} {% endfor %} - - - {% for score in user_scores_list %} - - {% endfor %} - -
Candidate{{cid}}
Question {{forloop.counter}}{{ans}}Question {{forloop.counter}} + {{ans_by_question|get_item:question|get_item:cid}}{{ans_score.0}}
Score:{{score}}
+{% endblock table_answers %} -
-{% endblock %} diff --git a/anatomy/templates/anatomy/exam_scores_new.html b/anatomy/templates/anatomy/exam_scores_new.html deleted file mode 100644 index 5fc34f07..00000000 --- a/anatomy/templates/anatomy/exam_scores_new.html +++ /dev/null @@ -1,81 +0,0 @@ -{% extends 'anatomy/exams.html' %} - -{% block content %} -
{{plot|safe}}
-
-

{{ exam.name }}

- User answer scores are cached, if you update or change an answer you will need to refresh the scores. - - {% if unmarked %} - - - {% endif %} - -
-
-

Stats

- Candidates: {{cids|length}}
- Max score: {{max_score}}
- Mean: {{mean}}, Median {{median}}, Mode {{mode}} - -
-
- - - - - {% comment %} {% endcomment %} - - {% comment %} {% for user, value in user_answers_marks.items %} {% endcomment %} - {% for cid in cids %} - - - - {% comment %} {% endcomment %} - - {% endfor %} -
Candidate IDScoreNormalised Score
{{cid}}{{user_scores|get_item:cid}}{{user_scores_normalised|get_item:user}}
-
-
-

Answers as a table

- - - - - {% for cid in cids %} - - {% comment %} {% endcomment %} - {% endfor %} - - - - {% for question in questions %} - - - {% comment %} {% for cid in cids %} - - {% endfor %} {% endcomment %} - {% for cid in cids %} - {% with by_question|get_item:question|get_item:cid as ans_score %} - - {% endwith %} - {% endfor %} - - {% endfor %} - - - {% for cid in cids %} - - {% endfor %} - -
Candidate{{cid}}{{cid}}
Question {{forloop.counter}} - {{ans_by_question|get_item:question|get_item:cid}}{{ans_score.0}}
Score:{{user_scores|get_item:cid}}
- -
-{% endblock %} - diff --git a/anatomy/urls.py b/anatomy/urls.py index fb2210d9..b560e040 100644 --- a/anatomy/urls.py +++ b/anatomy/urls.py @@ -71,6 +71,10 @@ urlpatterns = [ name="exam_question_detail", ), path("exam//", views.GenericExamViews.exam_overview, name="exam_overview"), + path("exam//report/email", views.GenericExamViews.exam_report_email, name="exam_report_email"), + path("exam//report/email/status", views.GenericExamViews.exam_report_email_status, name="exam_report_email_status"), + path("exam//report/", views.GenericExamViews.exam_user_report, name="exam_user_report"), + path("exam//report//email", views.GenericExamViews.exam_user_report_email, name="exam_user_report_email"), path("exam//authors", views.ExamAuthorUpdate.as_view(), name="exam_authors"), path( "exam//json_edit", diff --git a/anatomy/views.py b/anatomy/views.py index c6d257e8..efe611ef 100644 --- a/anatomy/views.py +++ b/anatomy/views.py @@ -399,126 +399,6 @@ def exam_scores_refresh(request, pk): ) -# @login_required -# def exam_scores_cid(request, pk): -# exam = get_object_or_404(Exam, pk=pk) -# -# if not exam.exam_mode: -# raise Http404("Packet not in exam mode") -# -# questions = exam.exam_questions.all() -# -# cids = ( -# CidUserAnswer.objects.filter(question__in=questions, exam__id=pk) -# .order_by("cid") -# .values_list("cid", flat=True) -# .distinct() -# ) -# -# user_answers_and_marks = defaultdict(list) -# user_answers_marks = defaultdict(list) -# user_answers = defaultdict(list) -# user_names = {} -# -# by_question = defaultdict(list) -# unmarked = set() -# -# # Loop through all candidates -# for cid in cids: -# # Convoluted (probably...) -# user_names[cid] = cid -# for q in questions: -# # Get user answer -# s = q.cid_user_answers.filter(cid=cid, exam__id=pk).first() -# if not s: -# # skip if no answer -# user_answers_marks[cid].append(0) -# user_answers[cid].append("") -# by_question[q].append(("", 0)) -# continue -# -# ans = s.answer -# answer_score = s.get_answer_score() -# if answer_score == "unmarked": -# index = exam.get_question_index(q) -# unmarked.add(index) -# user_answers[cid].append(ans) -# user_answers_marks[cid].append(answer_score) -# user_answers_and_marks[cid].append((ans, answer_score)) -# -# by_question[q].append((ans, answer_score)) -# -# user_scores = {} -# for user in user_answers_marks: -# user_scores[user] = sum( -# [i for i in user_answers_marks[user] if i != "unmarked"] -# ) -# -# user_scores_list = list(user_scores.values()) -# -# if len(user_scores_list) < 1: -# mean = 0 -# median = 0 -# mode = 0 -# fig_html = "" -# else: -# mean = statistics.mean(user_scores_list) -# median = statistics.median(user_scores_list) -# try: -# mode = statistics.mode(user_scores_list) -# except statistics.StatisticsError: -# mode = "No unique mode" -# -# df = user_scores_list -# fig = px.histogram( -# df, -# x=0, -# title="{}: distribution of scores".format(exam), -# labels={"0": "Score"}, -# height=400, -# width=600, -# ) -# fig_html = fig.to_html() -# -# max_score = len(questions) * 2 -# -# exam.stats_mean = mean -# exam.stats_median = median -# exam.stats_mode = mode -# -# exam.stats_candidates = len(user_scores_list) -# exam.stats_max_possible = max_score -# -# exam.stats_min = min(user_scores_list) -# exam.stats_max = max(user_scores_list) -# -# exam.stats_graph = fig_html -# exam.save() -# -# return render( -# request, -# "anatomy/exam_scores.html", -# { -# "cids": cids, -# "exam": exam, -# "unmarked": unmarked, -# "questions": questions, -# "by_question": by_question, -# "user_answers": dict(user_answers), -# "user_answers_marks": dict(user_answers_marks), -# "user_scores": user_scores, -# "user_scores_list": user_scores_list, -# "user_names": user_names, -# "user_answers_and_marks": user_answers_and_marks, -# "max_score": max_score, -# "mean": mean, -# "median": median, -# "mode": mode, -# "plot": fig_html, -# }, -# ) - - def exam_scores_cid_user(request, pk, cid, passcode): exam = get_object_or_404(Exam, pk=pk) @@ -589,89 +469,6 @@ def exam_scores_cid_user(request, pk, cid, passcode): ) -# @login_required -# def exam_scores(request, pk): -# exam = get_object_or_404(Exam, pk=pk) - -# questions = exam.exam_questions.all() - -# users = (CidUserAnswer.objects.filter( -# question__in=questions).values_list("user").distinct()) - -# user_answers_and_marks = defaultdict(list) -# user_answers_marks = defaultdict(list) -# user_answers = defaultdict(list) -# user_names = {} - -# by_question = defaultdict(list) - -# for u in users: -## Convoluted (probably...) -# user_names[u] = User.objects.filter(pk=u[0]).first().get_username() -# for q in questions: -# user_answer = q.user_answers.filter(user__in=u).first() -# if not user_answer: -# user_answers_marks[u].append(0) -# user_answers[u].append("") -# continue - -# ans = user_answer.answer -# answer_score = user_answer.get_answer_score() -# user_answers[u].append(ans) -# user_answers_marks[u].append(answer_score) -# user_answers_and_marks[u].append((ans, answer_score)) - -# by_question[q].append(ans) - -# user_scores = {} -# for user in user_answers_marks: -# user_scores[user] = sum(user_answers_marks[user]) - -# total = len(questions) - -# return render( -# request, -# "anatomy/exam_scores.html", -# { -# "exam": exam, -# "questions": questions, -# "by_question": by_question, -# "user_answers": dict(user_answers), -# "user_answers_marks": dict(user_answers_marks), -# "user_scores": user_scores, -# "user_names": user_names, -# "user_answers_and_marks": user_answers_and_marks, -# }, -# ) - -# def cid_scores(request, pk): -# #exam = get_object_or_404(Exam, pk=pk) -# -# # TODO:Need some kind of test for cid -# cid = pk -# -# #questions = exam.exam_questions.all() -# answers = CidUserAnswer.objects.filter( -# cid=cid) -# -# -# if not answers: -# raise Http404("cid not found") -# -# exam_ids = answers.values_list("exam").distinct() -# -# exams = Exam.objects.filter(id__in=exam_ids) -# -# -# return render( -# request, -# "anatomy/cid_scores.html", -# { -# "exams": exams, -# "cid": cid, -# }, -# ) - class AnatomyQuestionCreateBase(RevisionMixin, LoginRequiredMixin, CreateView): model = AnatomyQuestion diff --git a/rapids/templates/rapids/exam_scores_new.html b/generic/templates/generic/exam_scores_base.html similarity index 77% rename from rapids/templates/rapids/exam_scores_new.html rename to generic/templates/generic/exam_scores_base.html index 5aff081d..932daaa9 100644 --- a/rapids/templates/rapids/exam_scores_new.html +++ b/generic/templates/generic/exam_scores_base.html @@ -1,16 +1,18 @@ -{% extends 'rapids/exams.html' %} +{% extends exam.app_name|add:'/exams.html' %} {% block content %}
{{plot|safe}}
-
+

{{ exam.name }}

- User answer scores are cached, if you update or change an answer you will need to refresh the scores. + {% if cached_scores %} + User answer scores are cached, if you update or change an answer you will need to refresh the scores. + {% endif %} {% if unmarked %} @@ -51,15 +53,17 @@ Candidate {% for cid in cids %} - {% comment %} {{cid}} {% endcomment %} + {% comment %} {{cid}} {% endcomment %} {{cids_user_id_map|get_item:cid}} {% endfor %} - {% for question in questions %} + {% block table_answers %} + {% endblock table_answers %} + {% comment %} {% for question in questions %} - Question {{forloop.counter}} + Question {{forloop.counter}} {% if question.normal %} [N] {% else %} @@ -72,7 +76,7 @@ {% endwith %} {% endfor %} - {% endfor %} + {% endfor %} {% endcomment %} Score: {% for cid in cids %} @@ -81,14 +85,7 @@ - Call breakdown -
    - {% for user, breakdown in user_answers_callstates.items %} -
  • - {{cids_user_id_map|get_item:user}} - {{breakdown}} -
  • - {% endfor %} -
+ {% block breakdown %}{% endblock breakdown %}
Email results @@ -107,7 +104,7 @@ let additional_email = prompt("Please enter an additional email to send to (user and supervisor will automatically be used if available) if required"); - let url = "{% url 'rapids:exam_report_email' exam_id=exam.pk %}"; + let url = "{% url exam.app_name|add:':exam_report_email' exam_id=exam.pk %}"; if ( additional_email != null ) { url = url + "?additional_email="+encodeURI(additional_email) @@ -120,7 +117,7 @@ }) $("#email-results-check-button").click((evt) => { - window.location = "{% url 'rapids:exam_report_email_status' exam_id=exam.pk %}"; + window.location = "{% url exam.app_name|add:':exam_report_email_status' exam_id=exam.pk %}"; }) }) diff --git a/generic/views.py b/generic/views.py index 3e7883fa..885ec813 100644 --- a/generic/views.py +++ b/generic/views.py @@ -1497,11 +1497,14 @@ class ExamViews(View, LoginRequiredMixin): by_question = defaultdict(dict) unmarked = set() + cached_scores = True + if self.app_name in ("rapids"): user_answers_callstates = defaultdict(list) user_answers_callstates_counted = {} if self.app_name in ("physics", "sbas"): + cached_scores = False questions = exam.exam_questions.all() else: questions = exam.exam_questions.all().prefetch_related("answers") @@ -1642,7 +1645,6 @@ class ExamViews(View, LoginRequiredMixin): user_data[u]["callstates"] = str(counted_callstates) user_answers_callstates_counted[u] = counted_callstates - print(user_data) exam.user_scores = user_data @@ -1668,6 +1670,7 @@ class ExamViews(View, LoginRequiredMixin): "median": median, "mode": mode, "plot": fig_html, + "cached_scores": cached_scores, } if self.app_name == "rapids": @@ -1675,7 +1678,7 @@ class ExamViews(View, LoginRequiredMixin): return render( request, - f"{self.app_name}/exam_scores_new.html", + f"{self.app_name}/exam_scores.html", template_variables, ) diff --git a/physics/templates/physics/exam_scores.html b/physics/templates/physics/exam_scores.html index 5b8eb126..8718bee5 100644 --- a/physics/templates/physics/exam_scores.html +++ b/physics/templates/physics/exam_scores.html @@ -1,66 +1,23 @@ -{% extends 'physics/exams.html' %} +{% extends 'generic/exam_scores_base.html' %} -{% block content %} -
-

{{ exam.name }}

- -
-
-

Stats

- Candidates: {{cids|length}}
- Max score: {{max_score}}
- Mean: {{mean}}, Median {{median}}, Mode {{mode}} - -
-

Distribution of results

{{plot|safe}} -
-
-
- +{% block table_answers %} + {% for question in questions %} - - - - {% for user, value in user_answers_marks.items %} - - - - - {% endfor %} -
Candidate IDScore
{{user}}{{user_scores|get_item:user}}
-
-
-

Answers as a table

- - - - - {% for cid in cids %} - - {% endfor %} - - - {% for question in questions %} - - - {% for zipped_answers in by_question|get_item:question %} - + {% for cid in cids %} + {% endfor %} - {% endfor %} - - - {% for score in user_scores_list %} - - {% endfor %} - -
Candidate{{cid}}
Question {{forloop.counter}} -
    - {% for ans, score in zipped_answers %} -
  1. {{ans}}
  2. - {% endfor %} -
+
Question {{forloop.counter}} +
    + {% with by_question|get_item:question|get_item:cid as ans_score %} + {% for ans, score in ans_score %} +
  1. {{ans}}
  2. + {% endfor %} + {% endwith %} +
+
Score:{{score}}
+ {% endfor %} + +{% endblock %} -
-{% endblock %} \ No newline at end of file diff --git a/physics/templates/physics/exam_scores_new.html b/physics/templates/physics/exam_scores_new.html deleted file mode 100644 index 17c77290..00000000 --- a/physics/templates/physics/exam_scores_new.html +++ /dev/null @@ -1,73 +0,0 @@ -{% extends 'physics/exams.html' %} - -{% block content %} -
{{plot|safe}}
-
-

{{ exam.name }}

- -
-
-

Stats

- Candidates: {{cids|length}}
- Max score: {{max_score}}
- Mean: {{mean}}, Median {{median}}, Mode {{mode}} - -
-
- - - - - {% comment %} {% endcomment %} - - {% comment %} {% for user, value in user_answers_marks.items %} {% endcomment %} - {% for cid in cids %} - - - - {% comment %} {% endcomment %} - - {% endfor %} -
Candidate IDScoreNormalised Score
{{cid}}{{user_scores|get_item:cid}}{{user_scores_normalised|get_item:user}}
-
-
-

Answers as a table

- - - - - {% for cid in cids %} - - {% comment %} {% endcomment %} - {% endfor %} - - - - {% for question in questions %} - - - {% for cid in cids %} - - {% endfor %} - - {% endfor %} - - - {% for cid in cids %} - - {% endfor %} - -
Candidate{{cid}}{{cid}}
Question {{forloop.counter}} - -
    - {% with by_question|get_item:question|get_item:cid as ans_score %} - {% for ans, score in ans_score %} -
  1. {{ans}}
  2. - {% endfor %} - {% endwith %} -
-
Score:{{user_scores|get_item:cid}}
- -
-{% endblock %} - diff --git a/physics/urls.py b/physics/urls.py index 3a8fd54f..daef603f 100644 --- a/physics/urls.py +++ b/physics/urls.py @@ -26,6 +26,10 @@ urlpatterns = [ name="exam_take_overview", ), path("exam//", views.GenericExamViews.exam_overview, name="exam_overview"), + path("exam//report/email", views.GenericExamViews.exam_report_email, name="exam_report_email"), + path("exam//report/email/status", views.GenericExamViews.exam_report_email_status, name="exam_report_email_status"), + path("exam//report/", views.GenericExamViews.exam_user_report, name="exam_user_report"), + path("exam//report//email", views.GenericExamViews.exam_user_report_email, name="exam_user_report_email"), path("exam//authors", views.ExamAuthorUpdate.as_view(), name="exam_authors"), path( "exam//scores", diff --git a/rapids/templates/rapids/exam_scores.html b/rapids/templates/rapids/exam_scores.html index 04b43d6f..bff599ad 100644 --- a/rapids/templates/rapids/exam_scores.html +++ b/rapids/templates/rapids/exam_scores.html @@ -1,79 +1,34 @@ -{% extends 'rapids/exams.html' %} +{% extends 'generic/exam_scores_base.html' %} -{% block content %} -
{{plot|safe}}
-
-

{{ exam.name }}

- User answer scores are cached, if you update or change an answer you will need to refresh the scores. - - {% if unmarked %} - - - {% endif %} - -
-
-

Stats

- Candidates: {{cids|length}}
- Max score: {{max_score}}
- Mean: {{mean}}, Median {{median}}, Mode {{mode}} - -
-
- - - - - - - {% for user, value in user_answers_marks.items %} - - - - - +{% block table_answers %} + {% for question in questions %} + + + {% for cid in cids %} + {% with by_question|get_item:question|get_item:cid as ans_score %} + + {% endwith %} {% endfor %} -
Candidate IDScoreNormalised Score
{{user}}{{user_scores|get_item:user}}{{user_scores_normalised|get_item:user}}
Question {{forloop.counter}} + {% if question.normal %} + [N] + {% else %} + [A] + {% endif %} + {{ans_score.0}}
-
-
-

Answers as a table

- - - - - {% for cid in cids %} - - {% endfor %} + + {% endfor %} - - - {% for question in questions %} - - - {% for ans, score in by_question|get_item:question %} - - {% endfor %} - - {% endfor %} - - - {% for score in user_scores_list %} - - {% endfor %} - -
Candidate{{cid}}
Question {{forloop.counter}} - {% if question.normal %} - [N] - {% else %} - [A] - {% endif %} - {{ans}}
Score:{{score}}
- -
-{% endblock %} +{% endblock table_answers %} +{% block breakdown %} +
+ Call breakdown +
    + {% for user, breakdown in user_answers_callstates.items %} +
  • + {{cids_user_id_map|get_item:user}} - {{breakdown}} +
  • + {% endfor %} +
+
+{% endblock breakdown %} \ No newline at end of file diff --git a/sbas/templates/sbas/exam_scores.html b/sbas/templates/sbas/exam_scores.html index 2ab22dc9..3217f19c 100644 --- a/sbas/templates/sbas/exam_scores.html +++ b/sbas/templates/sbas/exam_scores.html @@ -1,63 +1,21 @@ -{% extends 'sbas/exams.html' %} +{% extends 'generic/exam_scores_base.html' %} -{% block content %} -
-

{{ exam.name }}

+{% block table_answers %} -
-
-

Stats

- Candidates: {{cids|length}}
- Max score: {{max_score}}
- Mean: {{mean}}, Median {{median}}, Mode {{mode}} - -
-

Distribution of results

{{plot|safe}} -
-
-
- - - - - - {% for user, value in user_answers_marks.items %} - - - - - {% endfor %} -
Candidate IDScore
{{user}}{{user_scores|get_item:user}}
-
-
-

Results as a table

- - - - - {% for cid in cids %} - - {% endfor %} - - - - {% for question in questions %} - - - {% for ans, score in by_question|get_item:question %} - + {% for question in questions %} + + + {% comment %} {% for cid in cids %} + + {% endfor %} {% endcomment %} + {% for cid in cids %} + {% with by_question|get_item:question|get_item:cid as ans_score %} + + {% endwith %} + {% endfor %} + {% endfor %} - - {% endfor %} - - - {% for score in user_scores_list %} - - {% endfor %} - -
Candidate{{cid}}
Question {{forloop.counter}} - {{ans}} -
Question {{forloop.counter}} + {{ans_by_question|get_item:question|get_item:cid}}{{ans_score.0}}
Score:{{score}}
-
-{% endblock %} \ No newline at end of file +{% endblock %} + diff --git a/sbas/templates/sbas/exam_scores_new.html b/sbas/templates/sbas/exam_scores_new.html deleted file mode 100644 index d0afab9f..00000000 --- a/sbas/templates/sbas/exam_scores_new.html +++ /dev/null @@ -1,70 +0,0 @@ -{% extends 'sbas/exams.html' %} - -{% block content %} -
{{plot|safe}}
-
-

{{ exam.name }}

- -
-
-

Stats

- Candidates: {{cids|length}}
- Max score: {{max_score}}
- Mean: {{mean}}, Median {{median}}, Mode {{mode}} - -
-
- - - - - {% comment %} {% endcomment %} - - {% comment %} {% for user, value in user_answers_marks.items %} {% endcomment %} - {% for cid in cids %} - - - - {% comment %} {% endcomment %} - - {% endfor %} -
Candidate IDScoreNormalised Score
{{cid}}{{user_scores|get_item:cid}}{{user_scores_normalised|get_item:user}}
-
-
-

Answers as a table

- - - - - {% for cid in cids %} - - {% comment %} {% endcomment %} - {% endfor %} - - - - {% for question in questions %} - - - {% comment %} {% for cid in cids %} - - {% endfor %} {% endcomment %} - {% for cid in cids %} - {% with by_question|get_item:question|get_item:cid as ans_score %} - - {% endwith %} - {% endfor %} - - {% endfor %} - - - {% for cid in cids %} - - {% endfor %} - -
Candidate{{cid}}{{cid}}
Question {{forloop.counter}} - {{ans_by_question|get_item:question|get_item:cid}}{{ans_score.0}}
Score:{{user_scores|get_item:cid}}
- -
-{% endblock %} - diff --git a/sbas/urls.py b/sbas/urls.py index 43b500e7..f25dd7be 100644 --- a/sbas/urls.py +++ b/sbas/urls.py @@ -26,6 +26,10 @@ urlpatterns = [ name="exam_take_overview", ), path("exam//", views.GenericExamViews.exam_overview, name="exam_overview"), + path("exam//report/email", views.GenericExamViews.exam_report_email, name="exam_report_email"), + path("exam//report/email/status", views.GenericExamViews.exam_report_email_status, name="exam_report_email_status"), + path("exam//report/", views.GenericExamViews.exam_user_report, name="exam_user_report"), + path("exam//report//email", views.GenericExamViews.exam_user_report_email, name="exam_user_report_email"), path("exam//authors", views.ExamAuthorUpdate.as_view(), name="exam_authors"), path( "exam//scores",