From 2e7051c27f71b2ca933ef9959b851721ec31309b Mon Sep 17 00:00:00 2001 From: Ross Date: Sat, 11 Dec 2021 23:22:35 +0000 Subject: [PATCH] . --- generic/views.py | 304 ++++++++++++++++++++++++++++++----------------- 1 file changed, 194 insertions(+), 110 deletions(-) diff --git a/generic/views.py b/generic/views.py index 8cab58c4..4acbc6d3 100644 --- a/generic/views.py +++ b/generic/views.py @@ -54,7 +54,8 @@ import os import string import random -#from rad.views import get_question_and_content_type +# from rad.views import get_question_and_content_type + def get_question_and_content_type(question_type): if question_type == "rapid": @@ -74,9 +75,10 @@ def get_question_and_content_type(question_type): content_type = ContentType.objects.get(app_label="physics", model="question") else: raise PermissionError() - + return question, content_type + # Create your views here. @login_required def create_examination(request): @@ -91,6 +93,7 @@ def create_examination(request): request, "rapids/create_simple.html", {"form": form, "name": "Examination"} ) + @csrf_exempt def get_examination_id(request): if request.is_ajax(): @@ -102,6 +105,7 @@ def get_examination_id(request): return HttpResponse(json.dumps(data), content_type="application/json") return HttpResponse("/") + # Generic view to dispatch to indivuals app views @login_required def generic_exam_json_edit(request): @@ -109,28 +113,27 @@ def generic_exam_json_edit(request): question_type = request.POST.get("type") if question_type == "rapid": - Exam = RapidExam + Exam = RapidExam Question = RapidQuestion elif question_type == "long": - Exam = LongExam + Exam = LongExam Question = LongQuestion elif question_type == "anatomy": - Exam = AnatomyExam + Exam = AnatomyExam Question = AnatomyQuestion elif question_type == "physics": - Exam = PhysicsExam + Exam = PhysicsExam Question = PhysicsQuestion elif question_type == "sbas": - Exam = SbasExam + Exam = SbasExam Question = SbasQuestion else: data = {"status": "error"} return JsonResponse(data, status=400) - exam = get_object_or_404(Exam, pk=request.POST.get("exam_id")) - #if "exam_toggle_active" in request.POST: + # if "exam_toggle_active" in request.POST: # active = json.loads(request.POST.get("active")) # data = {"status": "success"} @@ -138,7 +141,7 @@ def generic_exam_json_edit(request): if "add_exam_questions" in request.POST: question_ids = json.loads(request.POST.get("add_exam_questions")) - #question_objects = Question.objects.filter(pk__in=question_ids) + # question_objects = Question.objects.filter(pk__in=question_ids) add_count = 0 for i in question_ids: @@ -146,15 +149,17 @@ def generic_exam_json_edit(request): if not exam.exam_questions.filter(pk=i).exists(): add_count += 1 exam.exam_questions.add(i) - + exam.save() - data = {"status": "success", "questions added" : add_count} + data = {"status": "success", "questions added": add_count} return JsonResponse(data, status=200) if "set_exam_order" in request.POST: new_order = json.loads(request.POST.get("set_exam_order")) - preserved = Case(*[When(pk=pk, then=pos) for pos, pk in enumerate(new_order)]) + preserved = Case( + *[When(pk=pk, then=pos) for pos, pk in enumerate(new_order)] + ) objects = Question.objects.filter(pk__in=new_order).order_by(preserved) if len(objects) != exam.exam_questions.count(): @@ -167,13 +172,22 @@ def generic_exam_json_edit(request): return JsonResponse(data, status=200) # Only checkers can do the following - if question_type == "rapid" and not request.user.groups.filter(name='rapid_checker').exists(): + if ( + question_type == "rapid" + and not request.user.groups.filter(name="rapid_checker").exists() + ): data = {"status": "error, permission denied"} return JsonResponse(data, status=400) - if question_type == "anatomy" and not request.user.groups.filter(name='anatomy_checker').exists(): + if ( + question_type == "anatomy" + and not request.user.groups.filter(name="anatomy_checker").exists() + ): data = {"status": "error, permission denied"} return JsonResponse(data, status=400) - if question_type == "long" and not request.user.groups.filter(name='long_checker').exists(): + if ( + question_type == "long" + and not request.user.groups.filter(name="long_checker").exists() + ): data = {"status": "error, permission denied"} return JsonResponse(data, status=400) @@ -192,8 +206,6 @@ def generic_exam_json_edit(request): data = {"status": "success", "questions": questions_changed, "state": state} return JsonResponse(data, status=200) - - data = {"status": "error, unknown request"} return JsonResponse(data, status=400) else: @@ -201,7 +213,6 @@ def generic_exam_json_edit(request): return JsonResponse(data, status=400) - class ExamViews(View, LoginRequiredMixin): def __init__(self, exam, question, app, question_type, loadJsonAnswer): self.Exam = exam @@ -211,8 +222,8 @@ class ExamViews(View, LoginRequiredMixin): self.loadJsonAnswer = loadJsonAnswer # THis may be better than check_user_access below - #group_map = {"rapids" : "rapid_checker", "anatomy" : "anatomy_checker", "longs":"long_checker"} - #exam_group = group_map[self.app_name] + # group_map = {"rapids" : "rapid_checker", "anatomy" : "anatomy_checker", "longs":"long_checker"} + # exam_group = group_map[self.app_name] def check_user_access(self, user, exam_id=None): """Check if a user should be able to access a view @@ -222,21 +233,38 @@ class ExamViews(View, LoginRequiredMixin): Returns: [boolean]: True if the user has access - """ + """ if exam_id is not None: exam = get_object_or_404(self.Exam, pk=exam_id) if exam.open_access or user in exam.get_author_objects(): return True - if self.app_name == "rapids" and not user.groups.filter(name='rapid_checker').exists(): + if ( + self.app_name == "rapids" + and not user.groups.filter(name="rapid_checker").exists() + ): return False - if self.app_name == "anatomy" and not user.groups.filter(name='anatomy_checker').exists(): + if ( + self.app_name == "anatomy" + and not user.groups.filter(name="anatomy_checker").exists() + ): return False - if self.app_name == "longs" and not user.groups.filter(name__in=('long_checker', 'long_marker')).exists(): + if ( + self.app_name == "longs" + and not user.groups.filter( + name__in=("long_checker", "long_marker") + ).exists() + ): return False - if self.app_name == "physics" and not user.groups.filter(name='physics_checker').exists(): + if ( + self.app_name == "physics" + and not user.groups.filter(name="physics_checker").exists() + ): return False - if self.app_name == "sbas" and not user.groups.filter(name='sba_checker').exists(): + if ( + self.app_name == "sbas" + and not user.groups.filter(name="sba_checker").exists() + ): return False return True @@ -248,27 +276,40 @@ class ExamViews(View, LoginRequiredMixin): Returns: [boolean]: True if the user has access - """ + """ # If a user is an exam author they should have acccess if exam_id is not None: exam = get_object_or_404(self.Exam, pk=exam_id) if exam.open_access or user in exam.get_author_objects(): return True - if self.app_name == "rapids" and not user.groups.filter(name='rapid_checker').exists(): + if ( + self.app_name == "rapids" + and not user.groups.filter(name="rapid_checker").exists() + ): return False - if self.app_name == "anatomy" and not user.groups.filter(name='anatomy_checker').exists(): + if ( + self.app_name == "anatomy" + and not user.groups.filter(name="anatomy_checker").exists() + ): return False - if self.app_name == "longs" and not user.groups.filter(name__in=('long_checker')).exists(): + if ( + self.app_name == "longs" + and not user.groups.filter(name__in=("long_checker")).exists() + ): return False - if self.app_name == "physics" and not user.groups.filter(name='physics_checker').exists(): + if ( + self.app_name == "physics" + and not user.groups.filter(name="physics_checker").exists() + ): return False - if self.app_name == "sbas" and not user.groups.filter(name='sba_checker').exists(): + if ( + self.app_name == "sbas" + and not user.groups.filter(name="sba_checker").exists() + ): return False return True - - @method_decorator(login_required) def exam_toggle_results_published(self, request, pk): if request.is_ajax() and request.method == "POST": @@ -285,7 +326,6 @@ class ExamViews(View, LoginRequiredMixin): data = {"status": "error"} return JsonResponse(data, status=400) - @method_decorator(login_required) def exam_toggle_active(self, request, pk): if request.is_ajax() and request.method == "POST": @@ -329,26 +369,31 @@ class ExamViews(View, LoginRequiredMixin): if all: exams = self.Exam.objects.all().order_by("name") else: - exams = self.Exam.objects.filter(exam_mode=True, archive=False).order_by("name") + exams = self.Exam.objects.filter(exam_mode=True, archive=False).order_by( + "name" + ) if not self.check_user_access(request.user): raise PermissionDenied - return render(request, "{}/exam_list.html".format(self.app_name), {"exams": exams}) - + return render( + request, "{}/exam_list.html".format(self.app_name), {"exams": exams} + ) @method_decorator(login_required) def exam_overview(self, request, pk): - #print(Exam.objects.all()) - #exams = Exam.objects.all() - #print("test", Exam.objects.all().get(id=pk)) + # print(Exam.objects.all()) + # exams = Exam.objects.all() + # print("test", Exam.objects.all().get(id=pk)) exam = get_object_or_404(self.Exam, pk=pk) if request.user not in exam.author.all(): if not self.check_user_access(request.user, pk): raise PermissionDenied - - can_edit = self.check_user_edit_access(request.user, pk) | request.user.is_superuser + + can_edit = ( + self.check_user_edit_access(request.user, pk) | request.user.is_superuser + ) notes = [] if can_edit: @@ -361,10 +406,16 @@ class ExamViews(View, LoginRequiredMixin): return render( request, "{}/exam_overview.html".format(self.app_name), - {"exam": exam, "questions": questions, "question_number": question_number, "can_edit": can_edit, "notes": notes}, + { + "exam": exam, + "questions": questions, + "question_number": question_number, + "can_edit": can_edit, + "notes": notes, + }, ) - #@method_decorator(login_required) + # @method_decorator(login_required) def exam_notes(self, request, pk): exam = get_object_or_404(self.Exam, pk=pk) @@ -376,11 +427,12 @@ class ExamViews(View, LoginRequiredMixin): question_pks = exam.exam_questions.values_list("pk", flat=True) - # Get active notes for type - notes = QuestionNote.objects.filter(content_type=content_type, object_id__in=question_pks, complete=False) + # Get active notes for type + notes = QuestionNote.objects.filter( + content_type=content_type, object_id__in=question_pks, complete=False + ) return notes - @method_decorator(login_required) def exam_json_edit(self, request, pk): @@ -404,11 +456,15 @@ class ExamViews(View, LoginRequiredMixin): if "set_exam_order" in request.POST: new_order = json.loads(request.POST.get("set_exam_order")) - preserved = Case(*[When(pk=pk, then=pos) for pos, pk in enumerate(new_order)]) - objects = self.Question.objects.filter(pk__in=new_order).order_by(preserved) + preserved = Case( + *[When(pk=pk, then=pos) for pos, pk in enumerate(new_order)] + ) + objects = self.Question.objects.filter(pk__in=new_order).order_by( + preserved + ) # We now allow deleting exam questions - #if len(objects) != exam.exam_questions.count(): + # if len(objects) != exam.exam_questions.count(): # data = {"status": "error, count does not match"} # return JsonResponse(data, status=400) @@ -417,7 +473,6 @@ class ExamViews(View, LoginRequiredMixin): data = {"status": "success"} return JsonResponse(data, status=200) - if not self.check_user_access(request.user, pk): raise PermissionDenied @@ -433,7 +488,11 @@ class ExamViews(View, LoginRequiredMixin): q.save() questions_changed.append(q.pk) - data = {"status": "success", "questions": questions_changed, "state": state} + data = { + "status": "success", + "questions": questions_changed, + "state": state, + } return JsonResponse(data, status=200) data = {"status": "error, unknown request"} @@ -453,40 +512,63 @@ class ExamViews(View, LoginRequiredMixin): if not self.check_user_access(request.user, pk): raise PermissionDenied - questions = exam.exam_questions.all() - # Handle exams that require double marking try: if exam.double_mark: question_unmarked_map = [] for q in questions: - question_unmarked_map.append((q, int(q.get_unmarked_user_answer_count(exam_pk=exam.pk)), int(q.get_unmarked_user_answer_count(exam_pk=exam.pk, marker=request.user)) )) + question_unmarked_map.append( + ( + q, + int(q.get_unmarked_user_answer_count(exam_pk=exam.pk)), + int( + q.get_unmarked_user_answer_count( + exam_pk=exam.pk, marker=request.user + ) + ), + ) + ) return render( - request, "{}/mark_overview.html".format(self.app_name), {"exam": exam, "question_unmarked_map": question_unmarked_map} + request, + "{}/mark_overview.html".format(self.app_name), + {"exam": exam, "question_unmarked_map": question_unmarked_map}, ) except AttributeError: pass question_unmarked_map = [] for q in questions: - question_unmarked_map.append((q, int(q.get_unmarked_user_answer_count(exam_pk=exam.pk)))) + question_unmarked_map.append( + (q, int(q.get_unmarked_user_answer_count(exam_pk=exam.pk))) + ) return render( - request, "{}/mark_overview.html".format(self.app_name), {"exam": exam, "question_unmarked_map": question_unmarked_map} + request, + "{}/mark_overview.html".format(self.app_name), + {"exam": exam, "question_unmarked_map": question_unmarked_map}, ) @method_decorator(login_required) def index(self, request): exams = self.Exam.objects.filter(author__id=request.user.id) - if self.app_name == "rapids" and request.user.groups.filter(name='rapid_checker').exists(): + if ( + self.app_name == "rapids" + and request.user.groups.filter(name="rapid_checker").exists() + ): exams = self.Exam.objects.all() - if self.app_name == "anatomy" and request.user.groups.filter(name='anatomy_checker').exists(): + if ( + self.app_name == "anatomy" + and request.user.groups.filter(name="anatomy_checker").exists() + ): exams = self.Exam.objects.all() - if self.app_name == "longs" and request.user.groups.filter(name='long_checker').exists(): + if ( + self.app_name == "longs" + and request.user.groups.filter(name="long_checker").exists() + ): exams = self.Exam.objects.all() return render(request, "{}/index.html".format(self.app_name), {"exams": exams}) @@ -499,7 +581,6 @@ class ExamViews(View, LoginRequiredMixin): if not self.check_user_access(request.user, pk): raise PermissionDenied - question = exam.exam_questions.all()[sk] exam_length = len(exam.exam_questions.all()) @@ -544,14 +625,14 @@ class ExamViews(View, LoginRequiredMixin): url = url + "/unbased" obj = { - "name": exam.get_exam_name(), - "url": url, - "type": self.question_type, - "eid": "{}/{}".format(self.question_type, exam.pk), - "json_creation_time": creation_time, - "exam_json_id": exam.exam_json_id, - "exam_active": exam.active - } + "name": exam.get_exam_name(), + "url": url, + "type": self.question_type, + "eid": "{}/{}".format(self.question_type, exam.pk), + "json_creation_time": creation_time, + "exam_json_id": exam.exam_json_id, + "exam_active": exam.active, + } if self.question_type == "long": h = {} @@ -561,8 +642,8 @@ class ExamViews(View, LoginRequiredMixin): question.get_question_json() h[question.pk] = question.question_json_id obj["multi_question_json"] = h - active_exams["exams"].append( obj ) - + active_exams["exams"].append(obj) + if json == False: return active_exams["exams"] @@ -573,7 +654,7 @@ class ExamViews(View, LoginRequiredMixin): if request.is_ajax and request.method == "POST": n = 0 - + for answer in json.loads(request.POST.get("answers")): try: ret_status, ret = self.loadJsonAnswer(answer) @@ -581,8 +662,7 @@ class ExamViews(View, LoginRequiredMixin): # We catch the error here so users get an error (rather than 500) return JsonResponse({"success": False, "error": "Invalid data"}) - - #if ret is not True: + # if ret is not True: # return ret if ret_status: @@ -606,7 +686,7 @@ class ExamViews(View, LoginRequiredMixin): raise Http404("No available exam") exam_json = exam.get_exam_json(based=False) - #exam_json["exam_active"] = False + # exam_json["exam_active"] = False return JsonResponse(exam_json) def exam_json(self, request, pk): @@ -616,19 +696,18 @@ class ExamViews(View, LoginRequiredMixin): if not exam.active and not self.check_user_access(request.user, pk): raise Http404("No available exam") + # exam_json_cache = cache.get("{}_exam_json_{}".format(self.app_name, pk)) - #exam_json_cache = cache.get("{}_exam_json_{}".format(self.app_name, pk)) - - path= "{0}{1}/exam/{2}.json".format(settings.MEDIA_ROOT, self.app_name, pk) - url= "{0}{1}/exam/{2}.json".format(settings.MEDIA_URL, self.app_name, pk) + path = "{0}{1}/exam/{2}.json".format(settings.MEDIA_ROOT, self.app_name, pk) + url = "{0}{1}/exam/{2}.json".format(settings.MEDIA_URL, self.app_name, pk) if os.path.isfile(path) and not exam.recreate_json: - #exam_json_cache["cached"] = True + # exam_json_cache["cached"] = True # Make sure we pass on an argument if we are forcing a reload - if request.GET.get('_'): - query_string = urllib.parse.urlencode({'_': request.GET.get('_')}) - url = '{}?{}'.format(url, query_string) - return redirect(url) + if request.GET.get("_"): + query_string = urllib.parse.urlencode({"_": request.GET.get("_")}) + url = "{}?{}".format(url, query_string) + return redirect(url) return redirect(url) return JsonResponse(exam_json_cache) @@ -649,11 +728,11 @@ class ExamViews(View, LoginRequiredMixin): # We also try to clear the cache of any associated questions (longs) # see exam_question_json - #n = exam.exam_questions.count() - #question_keys = ["{}_exam_json_{}_{}".format(self.app_name, pk, i) for i in range(1, n)] - #cache.delete_many(question_keys) + # n = exam.exam_questions.count() + # question_keys = ["{}_exam_json_{}_{}".format(self.app_name, pk, i) for i in range(1, n)] + # cache.delete_many(question_keys) - #cache.set("{}_exam_json_{}".format(self.app_name, pk), exam_json, 3600) + # cache.set("{}_exam_json_{}".format(self.app_name, pk), exam_json, 3600) return JsonResponse(exam_json) @@ -664,12 +743,11 @@ class ExamViews(View, LoginRequiredMixin): if not exam.active and not self.check_user_access(request.user, pk): raise Http404("No available exam") - - if request.GET.get('_'): + if request.GET.get("_"): base_url = redirect("{}:question_json".format(self.app_name), pk=sk) - query_string = urllib.parse.urlencode({'_': request.GET.get('_')}) - url = '{}?{}'.format(base_url, query_string) - return redirect(url) + query_string = urllib.parse.urlencode({"_": request.GET.get("_")}) + url = "{}?{}".format(base_url, query_string) + return redirect(url) return redirect("{}:question_json".format(self.app_name), pk=sk) @@ -695,7 +773,7 @@ class ExamViews(View, LoginRequiredMixin): return JsonResponse(question_json) - + class GenericViewBase: def __init__(self, app_name, QuestionObject, CidUserAnswerObject, ExamObject): self.question_object = QuestionObject @@ -704,11 +782,11 @@ class GenericViewBase: self.app_name = app_name g = { - "rapids" : "rapid_checker", - "anatomy" : "anatomy_checker", - "longs" : "longs_checker", - "sbas" : "sbas_checker", - "physics" : "physics_checker", + "rapids": "rapid_checker", + "anatomy": "anatomy_checker", + "longs": "longs_checker", + "sbas": "sbas_checker", + "physics": "physics_checker", } self.checker_group = g[app_name] @@ -793,18 +871,21 @@ class GenericViewBase: questions = self.question_object.objects.filter(author=pk) return render( - request, f"{self.app_name}/category_detail.html", {"category": author, "questions": questions} + request, + f"{self.app_name}/category_detail.html", + {"category": author, "questions": questions}, ) - @method_decorator(login_required) def author_list(self, request): authors = User.objects.all() - return render(request, f"{self.app_name}/author_list.html", {"authors": authors}) + return render( + request, f"{self.app_name}/author_list.html", {"authors": authors} + ) -class ExamCloneMixin(): +class ExamCloneMixin: def get_initial(self): old_object = get_object_or_404(self.model, pk=self.kwargs["exam_id"]) initial_data = model_to_dict(old_object, exclude=["id"]) @@ -849,19 +930,22 @@ class CidUserView(LoginRequiredMixin, SingleTableMixin, FilterView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - #user = self.request.user + # user = self.request.user - physics_exams = [(i.name, i.pk) for i in PhysicsExam.objects.filter(archived=False)] + physics_exams = [ + (i.name, i.pk) for i in PhysicsExam.objects.filter(archive=False) + ] context["physics_exams"] = physics_exams return context + @login_required def create_cid_users(request): if request.is_ajax() and request.method == "POST": number = int(request.POST.get("number")) - new_cid = max(CidUser.objects.all().order_by("-cid")[0].cid+1, 50000) + new_cid = max(CidUser.objects.all().order_by("-cid")[0].cid + 1, 50000) for n in range(number): passcode = "".join(random.choices(string.ascii_uppercase, k=4)) @@ -870,4 +954,4 @@ def create_cid_users(request): new_cid = new_cid + 1 return JsonResponse({"status": "success"}) - return JsonResponse({"status": "fail"}) \ No newline at end of file + return JsonResponse({"status": "fail"})