This commit is contained in:
Ross
2021-12-11 23:22:35 +00:00
parent c85ce0962c
commit 2e7051c27f
+141 -57
View File
@@ -56,6 +56,7 @@ 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): def get_question_and_content_type(question_type):
if question_type == "rapid": if question_type == "rapid":
question = RapidQuestion question = RapidQuestion
@@ -77,6 +78,7 @@ def get_question_and_content_type(question_type):
return question, content_type return question, content_type
# Create your views here. # Create your views here.
@login_required @login_required
def create_examination(request): def create_examination(request):
@@ -91,6 +93,7 @@ def create_examination(request):
request, "rapids/create_simple.html", {"form": form, "name": "Examination"} request, "rapids/create_simple.html", {"form": form, "name": "Examination"}
) )
@csrf_exempt @csrf_exempt
def get_examination_id(request): def get_examination_id(request):
if request.is_ajax(): if request.is_ajax():
@@ -102,6 +105,7 @@ def get_examination_id(request):
return HttpResponse(json.dumps(data), content_type="application/json") return HttpResponse(json.dumps(data), content_type="application/json")
return HttpResponse("/") return HttpResponse("/")
# Generic view to dispatch to indivuals app views # Generic view to dispatch to indivuals app views
@login_required @login_required
def generic_exam_json_edit(request): def generic_exam_json_edit(request):
@@ -127,7 +131,6 @@ def generic_exam_json_edit(request):
data = {"status": "error"} data = {"status": "error"}
return JsonResponse(data, status=400) return JsonResponse(data, status=400)
exam = get_object_or_404(Exam, pk=request.POST.get("exam_id")) 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:
@@ -154,7 +157,9 @@ def generic_exam_json_edit(request):
if "set_exam_order" in request.POST: if "set_exam_order" in request.POST:
new_order = json.loads(request.POST.get("set_exam_order")) 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) objects = Question.objects.filter(pk__in=new_order).order_by(preserved)
if len(objects) != exam.exam_questions.count(): if len(objects) != exam.exam_questions.count():
@@ -167,13 +172,22 @@ def generic_exam_json_edit(request):
return JsonResponse(data, status=200) return JsonResponse(data, status=200)
# Only checkers can do the following # 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"} data = {"status": "error, permission denied"}
return JsonResponse(data, status=400) 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"} data = {"status": "error, permission denied"}
return JsonResponse(data, status=400) 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"} data = {"status": "error, permission denied"}
return JsonResponse(data, status=400) return JsonResponse(data, status=400)
@@ -192,8 +206,6 @@ def generic_exam_json_edit(request):
data = {"status": "success", "questions": questions_changed, "state": state} data = {"status": "success", "questions": questions_changed, "state": state}
return JsonResponse(data, status=200) return JsonResponse(data, status=200)
data = {"status": "error, unknown request"} data = {"status": "error, unknown request"}
return JsonResponse(data, status=400) return JsonResponse(data, status=400)
else: else:
@@ -201,7 +213,6 @@ def generic_exam_json_edit(request):
return JsonResponse(data, status=400) return JsonResponse(data, status=400)
class ExamViews(View, LoginRequiredMixin): class ExamViews(View, LoginRequiredMixin):
def __init__(self, exam, question, app, question_type, loadJsonAnswer): def __init__(self, exam, question, app, question_type, loadJsonAnswer):
self.Exam = exam self.Exam = exam
@@ -228,15 +239,32 @@ class ExamViews(View, LoginRequiredMixin):
if exam.open_access or user in exam.get_author_objects(): if exam.open_access or user in exam.get_author_objects():
return True 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 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 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 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 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 False
return True return True
@@ -255,20 +283,33 @@ class ExamViews(View, LoginRequiredMixin):
if exam.open_access or user in exam.get_author_objects(): if exam.open_access or user in exam.get_author_objects():
return True 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 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 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 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 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 False
return True return True
@method_decorator(login_required) @method_decorator(login_required)
def exam_toggle_results_published(self, request, pk): def exam_toggle_results_published(self, request, pk):
if request.is_ajax() and request.method == "POST": if request.is_ajax() and request.method == "POST":
@@ -285,7 +326,6 @@ class ExamViews(View, LoginRequiredMixin):
data = {"status": "error"} data = {"status": "error"}
return JsonResponse(data, status=400) return JsonResponse(data, status=400)
@method_decorator(login_required) @method_decorator(login_required)
def exam_toggle_active(self, request, pk): def exam_toggle_active(self, request, pk):
if request.is_ajax() and request.method == "POST": if request.is_ajax() and request.method == "POST":
@@ -329,13 +369,16 @@ class ExamViews(View, LoginRequiredMixin):
if all: if all:
exams = self.Exam.objects.all().order_by("name") exams = self.Exam.objects.all().order_by("name")
else: 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): if not self.check_user_access(request.user):
raise PermissionDenied 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) @method_decorator(login_required)
def exam_overview(self, request, pk): def exam_overview(self, request, pk):
@@ -348,7 +391,9 @@ class ExamViews(View, LoginRequiredMixin):
if not self.check_user_access(request.user, pk): if not self.check_user_access(request.user, pk):
raise PermissionDenied 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 = [] notes = []
if can_edit: if can_edit:
@@ -361,7 +406,13 @@ class ExamViews(View, LoginRequiredMixin):
return render( return render(
request, request,
"{}/exam_overview.html".format(self.app_name), "{}/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)
@@ -377,11 +428,12 @@ class ExamViews(View, LoginRequiredMixin):
question_pks = exam.exam_questions.values_list("pk", flat=True) question_pks = exam.exam_questions.values_list("pk", flat=True)
# Get active notes for type # Get active notes for type
notes = QuestionNote.objects.filter(content_type=content_type, object_id__in=question_pks, complete=False) notes = QuestionNote.objects.filter(
content_type=content_type, object_id__in=question_pks, complete=False
)
return notes return notes
@method_decorator(login_required) @method_decorator(login_required)
def exam_json_edit(self, request, pk): def exam_json_edit(self, request, pk):
if request.is_ajax() and request.method == "POST": if request.is_ajax() and request.method == "POST":
@@ -404,8 +456,12 @@ class ExamViews(View, LoginRequiredMixin):
if "set_exam_order" in request.POST: if "set_exam_order" in request.POST:
new_order = json.loads(request.POST.get("set_exam_order")) 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(
objects = self.Question.objects.filter(pk__in=new_order).order_by(preserved) *[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 # We now allow deleting exam questions
# if len(objects) != exam.exam_questions.count(): # if len(objects) != exam.exam_questions.count():
@@ -417,7 +473,6 @@ class ExamViews(View, LoginRequiredMixin):
data = {"status": "success"} data = {"status": "success"}
return JsonResponse(data, status=200) return JsonResponse(data, status=200)
if not self.check_user_access(request.user, pk): if not self.check_user_access(request.user, pk):
raise PermissionDenied raise PermissionDenied
@@ -433,7 +488,11 @@ class ExamViews(View, LoginRequiredMixin):
q.save() q.save()
questions_changed.append(q.pk) 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) return JsonResponse(data, status=200)
data = {"status": "error, unknown request"} data = {"status": "error, unknown request"}
@@ -453,40 +512,63 @@ class ExamViews(View, LoginRequiredMixin):
if not self.check_user_access(request.user, pk): if not self.check_user_access(request.user, pk):
raise PermissionDenied raise PermissionDenied
questions = exam.exam_questions.all() questions = exam.exam_questions.all()
# Handle exams that require double marking # Handle exams that require double marking
try: try:
if exam.double_mark: if exam.double_mark:
question_unmarked_map = [] question_unmarked_map = []
for q in questions: 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( 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: except AttributeError:
pass pass
question_unmarked_map = [] question_unmarked_map = []
for q in questions: 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( 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) @method_decorator(login_required)
def index(self, request): def index(self, request):
exams = self.Exam.objects.filter(author__id=request.user.id) 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() 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() 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() exams = self.Exam.objects.all()
return render(request, "{}/index.html".format(self.app_name), {"exams": exams}) 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): if not self.check_user_access(request.user, pk):
raise PermissionDenied raise PermissionDenied
question = exam.exam_questions.all()[sk] question = exam.exam_questions.all()[sk]
exam_length = len(exam.exam_questions.all()) exam_length = len(exam.exam_questions.all())
@@ -550,7 +631,7 @@ class ExamViews(View, LoginRequiredMixin):
"eid": "{}/{}".format(self.question_type, exam.pk), "eid": "{}/{}".format(self.question_type, exam.pk),
"json_creation_time": creation_time, "json_creation_time": creation_time,
"exam_json_id": exam.exam_json_id, "exam_json_id": exam.exam_json_id,
"exam_active": exam.active "exam_active": exam.active,
} }
if self.question_type == "long": if self.question_type == "long":
@@ -581,7 +662,6 @@ class ExamViews(View, LoginRequiredMixin):
# We catch the error here so users get an error (rather than 500) # We catch the error here so users get an error (rather than 500)
return JsonResponse({"success": False, "error": "Invalid data"}) return JsonResponse({"success": False, "error": "Invalid data"})
# if ret is not True: # if ret is not True:
# return ret # return ret
@@ -616,7 +696,6 @@ class ExamViews(View, LoginRequiredMixin):
if not exam.active and not self.check_user_access(request.user, pk): if not exam.active and not self.check_user_access(request.user, pk):
raise Http404("No available exam") 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) path = "{0}{1}/exam/{2}.json".format(settings.MEDIA_ROOT, self.app_name, pk)
@@ -625,9 +704,9 @@ class ExamViews(View, LoginRequiredMixin):
if os.path.isfile(path) and not exam.recreate_json: 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 # Make sure we pass on an argument if we are forcing a reload
if request.GET.get('_'): if request.GET.get("_"):
query_string = urllib.parse.urlencode({'_': request.GET.get('_')}) query_string = urllib.parse.urlencode({"_": request.GET.get("_")})
url = '{}?{}'.format(url, query_string) url = "{}?{}".format(url, query_string)
return redirect(url) return redirect(url)
return redirect(url) return redirect(url)
return JsonResponse(exam_json_cache) return JsonResponse(exam_json_cache)
@@ -664,11 +743,10 @@ class ExamViews(View, LoginRequiredMixin):
if not exam.active and not self.check_user_access(request.user, pk): if not exam.active and not self.check_user_access(request.user, pk):
raise Http404("No available exam") raise Http404("No available exam")
if request.GET.get("_"):
if request.GET.get('_'):
base_url = redirect("{}:question_json".format(self.app_name), pk=sk) base_url = redirect("{}:question_json".format(self.app_name), pk=sk)
query_string = urllib.parse.urlencode({'_': request.GET.get('_')}) query_string = urllib.parse.urlencode({"_": request.GET.get("_")})
url = '{}?{}'.format(base_url, query_string) url = "{}?{}".format(base_url, query_string)
return redirect(url) return redirect(url)
return redirect("{}:question_json".format(self.app_name), pk=sk) return redirect("{}:question_json".format(self.app_name), pk=sk)
@@ -793,18 +871,21 @@ class GenericViewBase:
questions = self.question_object.objects.filter(author=pk) questions = self.question_object.objects.filter(author=pk)
return render( 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) @method_decorator(login_required)
def author_list(self, request): def author_list(self, request):
authors = User.objects.all() 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): def get_initial(self):
old_object = get_object_or_404(self.model, pk=self.kwargs["exam_id"]) old_object = get_object_or_404(self.model, pk=self.kwargs["exam_id"])
initial_data = model_to_dict(old_object, exclude=["id"]) initial_data = model_to_dict(old_object, exclude=["id"])
@@ -851,11 +932,14 @@ class CidUserView(LoginRequiredMixin, SingleTableMixin, FilterView):
context = super().get_context_data(**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 context["physics_exams"] = physics_exams
return context return context
@login_required @login_required
def create_cid_users(request): def create_cid_users(request):
if request.is_ajax() and request.method == "POST": if request.is_ajax() and request.method == "POST":