diff --git a/anatomy/urls.py b/anatomy/urls.py index acc781f6..77d2ffe6 100644 --- a/anatomy/urls.py +++ b/anatomy/urls.py @@ -1,6 +1,7 @@ from django.urls import path, include from anatomy.models import Structure +from generic.urls import generic_exam_urls, generic_view_urls from . import views from django.views.decorators.cache import cache_page @@ -8,15 +9,11 @@ app_name = "anatomy" urlpatterns = [ # path('', views.question_list, name='question_list'), - path("", views.GenericExamViews.index, name="index"), path( "question/", views.AnatomyQuestionView.as_view(), name="question_list", ), - path( - "question//", views.GenericViews.question_detail, name="question_detail" - ), path( "question/create/", views.AnatomyQuestionCreate.as_view(), @@ -46,102 +43,16 @@ urlpatterns = [ views.QuestionDelete.as_view(), name="question_delete", ), - path( - "question//user_answers", - views.GenericViews.question_user_answers, - name="question_user_answers", - ), - path( - "exam//review", - views.GenericViews.question_review, - name="question_review", - ), path("exam///mark", views.mark, name="mark"), path("exam///mark/all", views.mark_all, name="mark_all"), path( "exam///mark/review", views.mark_review, name="mark_review" ), - path( - "exam//mark", views.GenericExamViews.mark_overview, name="mark_overview" - ), - # path("exam///", views.exam_take, name="exam_take"), - path( - "exam//question//", - views.GenericExamViews.exam_question_detail, - 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_unsent", views.GenericExamViews.exam_report_email_unsent, name="exam_report_email_unsent"), - 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", - views.GenericExamViews.exam_json_edit, - name="exam_json_edit", - ), - path( - "exam//scores", - views.GenericExamViews.exam_scores_cid, - # cache_page(60 * 1)(views.exam_scores_cid), - name="exam_scores_cid", - ), - path( - "exam//scores/refresh", - views.GenericExamViews.exam_scores_refresh, - # cache_page(60 * 1)(views.exam_scores_cid), - name="exam_scores_refresh", - ), - path( - "exam//scores///", - views.exam_scores_cid_user, - name="exam_scores_cid_user", - ), - path( - "exam//toggle_active", - views.GenericExamViews.exam_toggle_active, - name="exam_toggle_active", - ), - path( - "exam//toggle_results_published", - views.GenericExamViews.exam_toggle_results_published, - name="exam_toggle_results_published", - ), - path( - "exam/submit", - views.GenericExamViews.postExamAnswers, - name="exam_answers_submit", - ), - path("exam/", views.GenericExamViews.exam_list, name="exam_list"), - path("exam/all", views.GenericExamViews.exam_list_all, name="exam_list_all"), path("exam/create", views.ExamCreate.as_view(), name="exam_create"), path("exam//clone", views.ExamClone.as_view(), name="exam_clone"), - path("exam//cids", views.GenericExamViews.exam_cids, name="exam_cids"), - path("exam//cids/edit", views.GenericExamViews.exam_cids_edit, name="exam_cids_edit"), - path("exam//users/edit", views.GenericExamViews.exam_users_edit, name="exam_users_edit"), path("exam//update", views.ExamUpdate.as_view(), name="exam_update"), path("exam//delete", views.ExamDelete.as_view(), name="exam_delete"), - path("exam/json/", views.GenericExamViews.active_exams, name="active_exams"), - path("exam/json/", views.GenericExamViews.exam_json, name="exam_json"), - path( - "exam/json//unbased", - views.GenericExamViews.exam_json_unbased, - name="exam_json_unbased", - ), - path( - "exam/json///unbased", - views.GenericExamViews.exam_question_json_unbased, - name="exam_question_json_unbased", - ), - path( - "exam/json//recreate", - views.GenericExamViews.exam_json_recreate, - name="exam_json_recreate", - ), - # path("cid/", views.cid_selector, name="cid_selector"), - # path("ajax/exam/flag/", views.flag_question, name="flag_question"), path("body_part/create/", views.create_body_part, name="create_body_part"), path( "body_part/ajax/get_body_part_id", @@ -173,14 +84,12 @@ urlpatterns = [ views.UserAnswerDelete.as_view(), name="user_answer_delete", ), - path( - "user_answers/delete", - views.GenericViews.user_answer_delete_multiple, - name="user_answer_delete_multiple", - ), path( "structure-autocomplete", views.StructureAutocomplete.as_view(model=Structure, create_field="structure"), name="structure-autocomplete", ), ] + +urlpatterns.extend(generic_view_urls(views.GenericViews)) +urlpatterns.extend(generic_exam_urls(views.GenericExamViews)) \ No newline at end of file diff --git a/generic/urls.py b/generic/urls.py index 72d6a23f..ba02e02c 100755 --- a/generic/urls.py +++ b/generic/urls.py @@ -60,10 +60,14 @@ urlpatterns = [ name="group_email_resend", ), path( - "cids/group//update", views.CidUserGroupUpdate.as_view(), name="cid_group_update" + "cids/group//update", + views.CidUserGroupUpdate.as_view(), + name="cid_group_update", ), path( - "cids/group//delete", views.CidUserGroupDelete.as_view(), name="cid_group_delete" + "cids/group//delete", + views.CidUserGroupDelete.as_view(), + name="cid_group_delete", ), path( "cids/group/create", views.CidUserGroupCreate.as_view(), name="cid_group_create" @@ -71,13 +75,163 @@ urlpatterns = [ path("user/group/", views.user_group_view, name="user_group_view"), path("user/group/all", views.user_group_view_all, name="user_group_view_all"), path( - "user/group//update", views.UserUserGroupUpdate.as_view(), name="user_group_update" + "user/group//update", + views.UserUserGroupUpdate.as_view(), + name="user_group_update", ), path( - "user/group//delete", views.UserUserGroupDelete.as_view(), name="user_group_delete" + "user/group//delete", + views.UserUserGroupDelete.as_view(), + name="user_group_delete", ), path( - "user/group/create", views.UserUserGroupCreate.as_view(), name="user_group_create" + "user/group/create", + views.UserUserGroupCreate.as_view(), + name="user_group_create", ), path("cids/create", views.manage_cid_users, name="manage_cid_users"), ] + + +def generic_view_urls(generic_views): + urlpatterns = [ + path( + "user_answers/delete", + generic_views.user_answer_delete_multiple, + name="user_answer_delete_multiple", + ), + path("author//", generic_views.author_detail, name="author_detail"), + path("author/", generic_views.author_list, name="author_list"), + path( + "question//", generic_views.question_detail, name="question_detail" + ), + path( + "question//user_answers", + generic_views.question_user_answers, + name="question_user_answers", + ), + path( + "exam//review", + generic_views.question_review, + name="question_review", + ), + ] + + return urlpatterns + + +def generic_exam_urls(generic_exam_view): + urlpatterns = [ + path("", generic_exam_view.index, name="index"), + path("exam//", generic_exam_view.exam_overview, name="exam_overview"), + path( + "exam//mark", generic_exam_view.mark_overview, name="mark_overview" + ), + path( + "exam//question//", + generic_exam_view.exam_question_detail, + name="exam_question_detail", + ), + path( + "exam//user/", + generic_exam_view.exam_user, + name="exam_user", + ), + path( + "exam//report/email", + generic_exam_view.exam_report_email, + name="exam_report_email", + ), + path( + "exam//report/email_unsent", + generic_exam_view.exam_report_email_unsent, + name="exam_report_email_unsent", + ), + path( + "exam//report/email/status", + generic_exam_view.exam_report_email_status, + name="exam_report_email_status", + ), + path( + "exam//report/", + generic_exam_view.exam_user_report, + name="exam_user_report", + ), + path( + "exam//report//email", + generic_exam_view.exam_user_report_email, + name="exam_user_report_email", + ), + path( + "exam//json_edit", + generic_exam_view.exam_json_edit, + name="exam_json_edit", + ), + path( + "exam//scores", + generic_exam_view.exam_scores_cid, + name="exam_scores_cid", + ), + path( + "exam//scores/refresh", + generic_exam_view.exam_scores_refresh, + # cache_page(60 * 1)(exam_scores_cid), + name="exam_scores_refresh", + ), + path( + "exam//scores///", + generic_exam_view.exam_scores_cid_user, + name="exam_scores_cid_user", + ), + path( + "exam//scores/", + generic_exam_view.exam_scores_user, + name="exam_scores_user", + ), + path( + "exam//toggle_active", + generic_exam_view.exam_toggle_active, + name="exam_toggle_active", + ), + path( + "exam//toggle_results_published", + generic_exam_view.exam_toggle_results_published, + name="exam_toggle_results_published", + ), + path( + "exam/submit", + generic_exam_view.postExamAnswers, + name="exam_answers_submit", + ), + path("exam/", generic_exam_view.exam_list, name="exam_list"), + path("exam/all", generic_exam_view.exam_list_all, name="exam_list_all"), + path("exam//cids", generic_exam_view.exam_cids, name="exam_cids"), + path( + "exam//cids/edit", + generic_exam_view.exam_cids_edit, + name="exam_cids_edit", + ), + path( + "exam//users/edit", + generic_exam_view.exam_users_edit, + name="exam_users_edit", + ), + path("exam/json/", generic_exam_view.active_exams, name="active_exams"), + path("exam/json/", generic_exam_view.exam_json, name="exam_json"), + path( + "exam/json//unbased", + generic_exam_view.exam_json_unbased, + name="exam_json_unbased", + ), + path( + "exam/json///unbased", + generic_exam_view.exam_question_json_unbased, + name="exam_question_json_unbased", + ), + path( + "exam/json//recreate", + generic_exam_view.exam_json_recreate, + name="exam_json_recreate", + ), + ] + return urlpatterns diff --git a/generic/views.py b/generic/views.py index 6dd4383f..7cd98ff2 100644 --- a/generic/views.py +++ b/generic/views.py @@ -284,6 +284,7 @@ class ExamViews(View, LoginRequiredMixin): cid_user_answer, app, question_type, + normalise_score=None, ): self.Exam = exam self.Question = question @@ -291,6 +292,7 @@ class ExamViews(View, LoginRequiredMixin): self.CidUserAnswer = cid_user_answer self.app_name = app self.question_type = question_type + self.normalise_score = normalise_score # THis may be better than check_user_access below # group_map = {"rapids" : "rapid_checker", "anatomy" : "anatomy_checker", "longs":"long_checker"} @@ -562,7 +564,9 @@ class ExamViews(View, LoginRequiredMixin): @method_decorator(login_required) def exam_user(self, request, exam_id, user_id): - exam = get_object_or_404(self.Exam.objects.prefetch_related("author"), pk=exam_id) + exam = get_object_or_404( + self.Exam.objects.prefetch_related("author"), pk=exam_id + ) if request.user not in exam.author.all(): if not self.check_user_access(request.user, exam_id): @@ -573,10 +577,11 @@ class ExamViews(View, LoginRequiredMixin): return JsonResponse(model_to_dict(user_exam)) - @method_decorator(login_required) def exam_report_email_status(self, request, exam_id): - exam = get_object_or_404(self.Exam.objects.prefetch_related("author"), pk=exam_id) + exam = get_object_or_404( + self.Exam.objects.prefetch_related("author"), pk=exam_id + ) if request.user not in exam.author.all(): if not self.check_user_access(request.user, exam_id): @@ -600,20 +605,21 @@ class ExamViews(View, LoginRequiredMixin): }, ) return JsonResponse(status) - + @method_decorator(login_required) def exam_report_email_unsent(self, request, exam_id): return self.exam_report_email(request, exam_id, unsent_only=True) @method_decorator(login_required) def exam_report_email(self, request, exam_id, unsent_only=False, users=None): - exam = get_object_or_404(self.Exam.objects.prefetch_related("author"), pk=exam_id) + exam = get_object_or_404( + self.Exam.objects.prefetch_related("author"), pk=exam_id + ) if request.user not in exam.author.all(): if not self.check_user_access(request.user, exam_id): raise PermissionDenied - additional_email = request.GET.get("additional_email", "") # check addition email is valid @@ -639,9 +645,10 @@ class ExamViews(View, LoginRequiredMixin): if json.loads(user_exam.results_emailed_status)[0] == True: continue - if additional_email: - email_results[user.pk] = exam.email_user_results(user, additional_emails=[additional_email]) + email_results[user.pk] = exam.email_user_results( + user, additional_emails=[additional_email] + ) else: email_results[user.pk] = exam.email_user_results(user) email_results[user.pk].append(f"{time}") @@ -651,7 +658,7 @@ class ExamViews(View, LoginRequiredMixin): exam.exam_results_emailed = time exam.save() - + return JsonResponse(email_results) @method_decorator(login_required) @@ -660,24 +667,26 @@ class ExamViews(View, LoginRequiredMixin): # print(Exam.objects.all()) # exams = Exam.objects.all() # print("test", Exam.objects.all().get(id=pk)) - #exam = get_object_or_404(self.Exam.objects.prefetch_related("author"), pk=exam_id) + # exam = get_object_or_404(self.Exam.objects.prefetch_related("author"), pk=exam_id) - #u = get_object_or_404(User, pk=user_id) + # u = get_object_or_404(User, pk=user_id) - #if request.user not in exam.author.all(): + # if request.user not in exam.author.all(): # if not self.check_user_access(request.user, exam_id): # raise PermissionDenied - #res = exam.email_user_results(u) + # res = exam.email_user_results(u) - #return HttpResponse(res) + # return HttpResponse(res) @method_decorator(login_required) def exam_user_report(self, request, exam_id, user_id): # print(Exam.objects.all()) # exams = Exam.objects.all() # print("test", Exam.objects.all().get(id=pk)) - exam = get_object_or_404(self.Exam.objects.prefetch_related("author"), pk=exam_id) + exam = get_object_or_404( + self.Exam.objects.prefetch_related("author"), pk=exam_id + ) u = get_object_or_404(User, pk=user_id) @@ -690,7 +699,6 @@ class ExamViews(View, LoginRequiredMixin): return HttpResponse(res) - @method_decorator(login_required) def exam_overview(self, request, pk): # print(Exam.objects.all()) @@ -698,7 +706,6 @@ class ExamViews(View, LoginRequiredMixin): # print("test", Exam.objects.all().get(id=pk)) exam = get_object_or_404(self.Exam.objects.prefetch_related("author"), pk=pk) - if request.user not in exam.author.all(): if not self.check_user_access(request.user, pk): raise PermissionDenied @@ -1517,6 +1524,103 @@ class ExamViews(View, LoginRequiredMixin): return JsonResponse(question_json) + @method_decorator(login_required) + def exam_scores_user(self, request, pk): + return self.exam_scores_cid_user(request, pk) + + def exam_scores_cid_user(self, request, pk, cid=None, passcode=""): + exam = get_object_or_404(self.Exam, pk=pk) + + if not exam.exam_mode: + raise Http404("Packet not in exam mode") + + # TODO:Need some kind of test for cid + + questions = ( + exam.exam_questions.all() + ) # .prefetch_related("cid_user_answers", "answers") + + # cid_user_answers = list(CidUserAnswer.objects.filter(cid=cid, exam__id=pk).prefetch_related("question")) + + # cid_user_answers_q_map = {} + + # for ans in cid_user_answers: + # cid_user_answers_q_map[ans.question] = ans + + answers_and_marks = [] + answers_marks = [] + answers = [] + + view_all_results = False + if request.user.groups.filter(name="view_all_results").exists(): + view_all_results = True + + for q in questions: + # Get user answer + if cid is not None: + user_answer = q.cid_user_answers.filter(cid=cid, exam__id=pk).first() + else: + user_answer = q.cid_user_answers.filter( + user=request.user, exam__id=pk + ).first() + # user_answer = cid_user_answers_q_map[q] + + if not user_answer or user_answer is None: + # skip if no answer + answers_marks.append(0) + # answers.append("") + answer_score = 0 + ans = "Not answered" + else: + if user_answer.normal: + ans = "Normal" + else: + ans = user_answer.answer + answer_score = user_answer.get_answer_score() + + correct_answer = q.get_primary_answer() + + if not exam.publish_results and not view_all_results: + correct_answer = "*****" + answer_score = 0 + answers.append(ans) + answers_marks.append(answer_score) + answers_and_marks.append((ans, answer_score, correct_answer)) + + if "unmarked" in answers_marks: + answered = [i for i in answers_marks if type(i) == int] + total_score = sum(answered) + unmarked_number = len(answers_marks) - len(answered) + total_score = "{} ({} unmarked)".format(total_score, unmarked_number) + else: + total_score = sum(answers_marks) + + max_score = len(questions) * 2 + + template_context = { + "exam": exam, + "cid": cid, + "passcode": passcode, + "questions": questions, + "answers": answers, + "answers_marks": answers_marks, + "total_score": total_score, + "max_score": max_score, + "answers_and_marks": answers_and_marks, + "view_all_results": view_all_results, + } + + if self.normalise_score is not None: + + normalised_score = self.normalise_score(total_score) + template_context["normalised_score"] = normalised_score + + return render( + request, + f"{self.app_name}/exam_scores_user.html", + template_context, + ) + def exam_scores_cid(self, request, pk): exam = get_object_or_404(self.Exam, pk=pk) @@ -1563,7 +1667,7 @@ class ExamViews(View, LoginRequiredMixin): cids.add(cid) else: cid = f"u/{cid_user_answer.user.pk}" - #cids_user_id_map[cid] = cid_user_answer.user.username + # cids_user_id_map[cid] = cid_user_answer.user.username name = f"{cid_user_answer.user.first_name} {cid_user_answer.user.last_name}" if not name.strip(): name = cid_user_answer.user.username @@ -1665,11 +1769,11 @@ class ExamViews(View, LoginRequiredMixin): exam.stats_min = min(user_scores_list) exam.stats_max = max(user_scores_list) - #exam.stats_graph = fig_html + # exam.stats_graph = fig_html user_data = {} for u in cids: - #uid = cids_user_id_map[u] + # uid = cids_user_id_map[u] user_data[u] = { "score": user_scores[u], "normalised_score": user_scores_normalised[u], @@ -1680,7 +1784,6 @@ class ExamViews(View, LoginRequiredMixin): user_data[u]["callstates"] = str(counted_callstates) user_answers_callstates_counted[u] = counted_callstates - exam.user_scores = user_data exam.save() @@ -1709,7 +1812,9 @@ class ExamViews(View, LoginRequiredMixin): } if self.app_name == "rapids": - template_variables["user_answers_callstates"] = user_answers_callstates_counted + template_variables[ + "user_answers_callstates" + ] = user_answers_callstates_counted return render( request, diff --git a/longs/urls.py b/longs/urls.py index de101d8d..0c2e90dd 100755 --- a/longs/urls.py +++ b/longs/urls.py @@ -1,11 +1,12 @@ from django.urls import path, include + +from generic.urls import generic_exam_urls, generic_view_urls from . import views app_name = "longs" urlpatterns = [ # path('', views.question_list, name='question_list'), - path("", views.GenericExamViews.index, name="index"), path("author//", views.author_detail, name="author_detail"), path("author/", views.author_list, name="author_list"), path("question/", views.LongView.as_view(), name="long_view"), @@ -81,22 +82,7 @@ urlpatterns = [ views.mark_question_overview, name="mark_question_overview", ), - path( - "exam//mark", views.GenericExamViews.mark_overview, name="mark_overview" - ), - # path("exam///", views.exam_take, name="exam_take"), - path( - "exam//question//", - views.GenericExamViews.exam_question_detail, - name="exam_question_detail", - ), - path("exam//", views.GenericExamViews.exam_overview, name="exam_overview"), path("exam//authors", views.ExamAuthorUpdate.as_view(), name="exam_authors"), - path( - "exam//json_edit", - views.GenericExamViews.exam_json_edit, - name="exam_json_edit", - ), path( "exam//question_json_refresh", views.refresh_exam_question_json, @@ -108,52 +94,10 @@ urlpatterns = [ views.exam_scores_cid_user, name="exam_scores_cid_user", ), - path( - "exam//toggle_active", - views.GenericExamViews.exam_toggle_active, - name="exam_toggle_active", - ), - path( - "exam//toggle_results_published", - views.GenericExamViews.exam_toggle_results_published, - name="exam_toggle_results_published", - ), - path( - "exam/submit", - views.GenericExamViews.postExamAnswers, - name="exam_answers_submit", - ), - path("exam/", views.GenericExamViews.exam_list, name="exam_list"), - path("exam/all", views.GenericExamViews.exam_list_all, name="exam_list_all"), path("exam/create", views.ExamCreate.as_view(), name="exam_create"), - path("exam//cids", views.GenericExamViews.exam_cids, name="exam_cids"), - path("exam//cids/edit", views.GenericExamViews.exam_cids_edit, name="exam_cids_edit"), - path("exam//users/edit", views.GenericExamViews.exam_users_edit, name="exam_users_edit"), path("exam//clone", views.ExamClone.as_view(), name="exam_clone"), path("exam//update", views.ExamUpdate.as_view(), name="exam_update"), path("exam//delete", views.ExamDelete.as_view(), name="exam_delete"), - path("exam/json/", views.GenericExamViews.active_exams, name="active_exams"), - path("exam/json/", views.GenericExamViews.exam_json, name="exam_json"), - path( - "exam/json//unbased", - views.GenericExamViews.exam_json_unbased, - name="exam_json_unbased", - ), - path( - "exam/json//", - views.GenericExamViews.exam_question_json, - name="exam_question_json", - ), - path( - "exam/json//unbased/", - views.GenericExamViews.exam_question_json_unbased, - name="exam_question_json_unbased", - ), - path( - "exam/json//recreate", - views.GenericExamViews.exam_json_recreate, - name="exam_json_recreate", - ), path("create/", views.LongCreate.as_view(), name="long_create"), path("create/series", views.LongSeriesCreate.as_view(), name="long_series_create"), path( @@ -209,3 +153,7 @@ urlpatterns = [ name="user_answer_delete_multiple", ), ] + + +#urlpatterns.extend(generic_view_urls(views.GenericViews)) +urlpatterns.extend(generic_exam_urls(views.GenericExamViews)) \ No newline at end of file diff --git a/physics/urls.py b/physics/urls.py index 2f5642f4..76d6f64f 100644 --- a/physics/urls.py +++ b/physics/urls.py @@ -1,11 +1,11 @@ from django.urls import path, include + +from generic.urls import generic_exam_urls, generic_view_urls from . import views app_name = "physics" urlpatterns = [ - # path('', views.question_list, name='question_list'), - path("", views.GenericExamViews.index, name="index"), # path("question/", views.QuestionView.as_view(), name="question_view"), path("question/", views.QuestionView.as_view(), name="question_view"), path("question//", views.question_detail, name="question_detail"), @@ -25,46 +25,10 @@ urlpatterns = [ views.exam_take_overview, 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_unsent", views.GenericExamViews.exam_report_email_unsent, name="exam_report_email_unsent"), - 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", - views.GenericExamViews.exam_scores_cid, - name="exam_scores_cid", - ), - path( - "exam//scores///", - views.exam_scores_cid_user, - name="exam_scores_cid_user", - ), - path( - "exam//toggle_active", - views.GenericExamViews.exam_toggle_active, - name="exam_toggle_active", - ), - path( - "exam//toggle_results_published", - views.GenericExamViews.exam_toggle_results_published, - name="exam_toggle_results_published", - ), path("exam//clone", views.ExamClone.as_view(), name="exam_clone"), - path("exam//cids", views.GenericExamViews.exam_cids, name="exam_cids"), - path("exam//cids/edit", views.GenericExamViews.exam_cids_edit, name="exam_cids_edit"), - path("exam//users/edit", views.GenericExamViews.exam_users_edit, name="exam_users_edit"), path("exam//update", views.ExamUpdate.as_view(), name="exam_update"), path("exam//delete", views.ExamDelete.as_view(), name="exam_delete"), - path( - "exam/submit", - views.GenericExamViews.postExamAnswers, - name="exam_answers_submit", - ), - path("exam/", views.GenericExamViews.exam_list, name="exam_list"), - path("exam/all", views.GenericExamViews.exam_list_all, name="exam_list_all"), path("exam/available", views.active_exams, name="active_exams"), # path("exam/json/", views.exam_json, name="exam_json"), # path("exam/json//recreate", views.exam_json_recreate, name="exam_json_recreate"), @@ -81,9 +45,7 @@ urlpatterns = [ views.UserAnswerDelete.as_view(), name="user_answer_delete", ), - path( - "user_answers/delete", - views.GenericViews.user_answer_delete_multiple, - name="user_answer_delete_multiple", - ), ] + +urlpatterns.extend(generic_view_urls(views.GenericViews)) +urlpatterns.extend(generic_exam_urls(views.GenericExamViews)) \ No newline at end of file diff --git a/rapids/urls.py b/rapids/urls.py index df6328c2..a8aa8a8a 100755 --- a/rapids/urls.py +++ b/rapids/urls.py @@ -4,23 +4,21 @@ from rapids.models import Answer from . import views from django.views.decorators.cache import cache_page, cache_control - +from generic.urls import generic_exam_urls, generic_view_urls app_name = "rapids" + + urlpatterns = [ # path('', views.question_list, name='question_list'), - path("", views.GenericExamViews.index, name="index"), - path("author//", views.GenericViews.author_detail, name="author_detail"), - path("author/", views.GenericViews.author_list, name="author_list"), + #path("", views.GenericExamViews.index, name="index"), + #path("exam", generic_exam_urls(views.GenericExamViews), name="index"), path("question_viewer", views.question_viewer, name="question_viewer"), path("question/", views.RapidView.as_view(), name="rapid_view"), path("question/hash", views.get_question_by_hash, name="rapid_question_by_hash"), # path("unchecked/", views.unchecked_list, name="unchecked_list"), # path("verified//", views.verified_detail, name="verified_detail"), - path( - "question//", views.GenericViews.question_detail, name="question_detail" - ), path("question//json", views.question_json, name="question_json"), path( "question//json/unbased", @@ -42,110 +40,21 @@ urlpatterns = [ views.QuestionDelete.as_view(), name="question_delete", ), - path( - "question//user_answers", - views.GenericViews.question_user_answers, - name="question_user_answers", - ), path( "question//anon", views.question_anonymise_dicom, name="question_anonymise_dicom", ), - path( - "exam//review", - views.GenericViews.question_review, - name="question_review", - ), path("exam///mark", views.mark, name="mark"), path("exam///mark/all", views.mark_all, name="mark_all"), path( "exam///mark/review", views.mark_review, name="mark_review" ), - path( - "exam//mark", views.GenericExamViews.mark_overview, name="mark_overview" - ), - # path("exam///", views.exam_take, name="exam_take"), - path( - "exam//question//", - views.GenericExamViews.exam_question_detail, - name="exam_question_detail", - ), - path("exam//", views.GenericExamViews.exam_overview, name="exam_overview"), - path("exam//user/", views.GenericExamViews.exam_user, name="exam_user"), - path("exam//report/email", views.GenericExamViews.exam_report_email, name="exam_report_email"), - path("exam//report/email_unsent", views.GenericExamViews.exam_report_email_unsent, name="exam_report_email_unsent"), - 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", - views.GenericExamViews.exam_json_edit, - name="exam_json_edit", - ), - path( - "exam//scores", - views.GenericExamViews.exam_scores_cid, - name="exam_scores_cid", - ), - path( - "exam//scores/refresh", - views.GenericExamViews.exam_scores_refresh, - # cache_page(60 * 1)(views.exam_scores_cid), - name="exam_scores_refresh", - ), - path( - "exam//scores///", - views.exam_scores_cid_user, - name="exam_scores_cid_user", - ), - path( - "exam//scores/", - views.exam_scores_user, - name="exam_scores_user", - ), - path( - "exam//toggle_active", - views.GenericExamViews.exam_toggle_active, - name="exam_toggle_active", - ), - path( - "exam//toggle_results_published", - views.GenericExamViews.exam_toggle_results_published, - name="exam_toggle_results_published", - ), - path( - "exam/submit", - views.GenericExamViews.postExamAnswers, - name="exam_answers_submit", - ), - path("exam/", views.GenericExamViews.exam_list, name="exam_list"), - path("exam/all", views.GenericExamViews.exam_list_all, name="exam_list_all"), path("exam/create", views.ExamCreate.as_view(), name="exam_create"), path("exam//clone", views.ExamClone.as_view(), name="exam_clone"), - path("exam//cids", views.GenericExamViews.exam_cids, name="exam_cids"), - path("exam//cids/edit", views.GenericExamViews.exam_cids_edit, name="exam_cids_edit"), - path("exam//users/edit", views.GenericExamViews.exam_users_edit, name="exam_users_edit"), path("exam//update", views.ExamUpdate.as_view(), name="exam_update"), path("exam//delete", views.ExamDelete.as_view(), name="exam_delete"), - path("exam/json/", views.GenericExamViews.active_exams, name="active_exams"), - path("exam/json/", views.GenericExamViews.exam_json, name="exam_json"), - path( - "exam/json//unbased", - views.GenericExamViews.exam_json_unbased, - name="exam_json_unbased", - ), - path( - "exam/json///unbased", - views.GenericExamViews.exam_question_json_unbased, - name="exam_question_json_unbased", - ), - path( - "exam/json//recreate", - views.GenericExamViews.exam_json_recreate, - name="exam_json_recreate", - ), path("create/", views.RapidCreate.as_view(), name="rapid_create"), path("create/exam/", views.RapidCreate.as_view(), name="rapid_create_exam"), path( @@ -185,11 +94,6 @@ urlpatterns = [ views.UserAnswerDelete.as_view(), name="user_answer_delete", ), - path( - "user_answers/delete", - views.GenericViews.user_answer_delete_multiple, - name="user_answer_delete_multiple", - ), path( "answer-autocomplete", views.AnswerAutocomplete.as_view( @@ -198,3 +102,6 @@ urlpatterns = [ name="answer-autocomplete", ), ] + +urlpatterns.extend(generic_view_urls(views.GenericViews)) +urlpatterns.extend(generic_exam_urls(views.GenericExamViews)) \ No newline at end of file diff --git a/rapids/views.py b/rapids/views.py index 9b6adabd..d31e71d3 100755 --- a/rapids/views.py +++ b/rapids/views.py @@ -686,93 +686,6 @@ def mark(request, exam_pk, sk, unmarked_exam_answers_only=True, review=False): }, ) -@login_required -def exam_scores_user(request, pk): - return exam_scores_cid_user(request, pk) - -def exam_scores_cid_user(request, pk, cid=None, passcode=""): - exam = get_object_or_404(Exam, pk=pk) - - if not exam.exam_mode: - raise Http404("Packet not in exam mode") - - # TODO:Need some kind of test for cid - - questions = ( - exam.exam_questions.all() - ) # .prefetch_related("cid_user_answers", "answers") - - # cid_user_answers = list(CidUserAnswer.objects.filter(cid=cid, exam__id=pk).prefetch_related("question")) - - # cid_user_answers_q_map = {} - - # for ans in cid_user_answers: - # cid_user_answers_q_map[ans.question] = ans - - answers_and_marks = [] - answers_marks = [] - answers = [] - - view_all_results = False - if request.user.groups.filter(name="view_all_results").exists(): - view_all_results = True - - for q in questions: - # Get user answer - user_answer = q.cid_user_answers.filter(cid=cid, exam__id=pk).first() - # user_answer = cid_user_answers_q_map[q] - - if not user_answer or user_answer is None: - # skip if no answer - answers_marks.append(0) - # answers.append("") - answer_score = 0 - ans = "Not answered" - else: - if user_answer.normal: - ans = "Normal" - else: - ans = user_answer.answer - answer_score = user_answer.get_answer_score() - - correct_answer = q.get_primary_answer() - - if not exam.publish_results and not view_all_results: - correct_answer = "*****" - answer_score = 0 - answers.append(ans) - answers_marks.append(answer_score) - answers_and_marks.append((ans, answer_score, correct_answer)) - - if "unmarked" in answers_marks: - answered = [i for i in answers_marks if type(i) == int] - total_score = sum(answered) - unmarked_number = len(answers_marks) - len(answered) - total_score = "{} ({} unmarked)".format(total_score, unmarked_number) - else: - total_score = sum(answers_marks) - - normalised_score = normaliseScore(total_score) - - max_score = len(questions) * 2 - - return render( - request, - "rapids/exam_scores_user.html", - { - "exam": exam, - "cid": cid, - "passcode": passcode, - "questions": questions, - "answers": answers, - "answers_marks": answers_marks, - "total_score": total_score, - "normalised_score": normalised_score, - "max_score": max_score, - "answers_and_marks": answers_and_marks, - "view_all_results": view_all_results, - }, - ) class QuestionDelete(AuthorOrCheckerRequiredMixin, DeleteView): @@ -780,7 +693,7 @@ class QuestionDelete(AuthorOrCheckerRequiredMixin, DeleteView): success_url = reverse_lazy("rapids:rapid_view") -GenericExamViews = ExamViews(Exam, Rapid, Answer, CidUserAnswer, "rapids", "rapid") +GenericExamViews = ExamViews(Exam, Rapid, Answer, CidUserAnswer, "rapids", "rapid", normalise_score=normaliseScore) class ExamCreate(ExamCreateBase): diff --git a/sbas/urls.py b/sbas/urls.py index 6bfa901c..47a751aa 100644 --- a/sbas/urls.py +++ b/sbas/urls.py @@ -1,13 +1,13 @@ from django.urls import path, include +from generic.urls import generic_exam_urls, generic_view_urls + from . import views from django.views.decorators.cache import cache_page app_name = "sbas" urlpatterns = [ - # path('', views.question_list, name='question_list'), - path("", views.GenericExamViews.index, name="index"), path("question/", views.QuestionView.as_view(), name="question_view"), path("question//", views.question_detail, name="question_detail"), # path("question/create/", views.QuestionCreate.as_view(), name="anatomy_question_create"), @@ -25,40 +25,8 @@ urlpatterns = [ views.exam_take_overview, 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_unsent", views.GenericExamViews.exam_report_email_unsent, name="exam_report_email_unsent"), - 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", - views.GenericExamViews.exam_scores_cid, - name="exam_scores_cid", - ), - path( - "exam//scores///", - views.exam_scores_cid_user, - name="exam_scores_cid_user", - ), - path( - "exam//toggle_active", - views.GenericExamViews.exam_toggle_active, - name="exam_toggle_active", - ), - path( - "exam//toggle_results_published", - views.GenericExamViews.exam_toggle_results_published, - name="exam_toggle_results_published", - ), - # path("exam/submit", views.GenericExamViews.postExamAnswers, name="exam_answers_submit"), - path("exam/", views.GenericExamViews.exam_list, name="exam_list"), - path("exam/all", views.GenericExamViews.exam_list_all, name="exam_list_all"), path("exam//clone", views.ExamClone.as_view(), name="exam_clone"), - path("exam//cids", views.GenericExamViews.exam_cids, name="exam_cids"), - path("exam//cids/edit", views.GenericExamViews.exam_cids_edit, name="exam_cids_edit"), - path("exam//users/edit", views.GenericExamViews.exam_users_edit, name="exam_users_edit"), path("exam/available", views.active_exams, name="active_exams"), path("exam//update", views.ExamUpdate.as_view(), name="exam_update"), path("exam//delete", views.ExamDelete.as_view(), name="exam_delete"), @@ -77,9 +45,7 @@ urlpatterns = [ views.UserAnswerDelete.as_view(), name="user_answer_delete", ), - path( - "user_answers/delete", - views.GenericViews.user_answer_delete_multiple, - name="user_answer_delete_multiple", - ), ] + +urlpatterns.extend(generic_view_urls(views.GenericViews)) +urlpatterns.extend(generic_exam_urls(views.GenericExamViews)) \ No newline at end of file