add (correct) support for logged in users to sba and physics exams

This commit is contained in:
Ross
2023-06-26 11:52:23 +01:00
parent 09951286e2
commit 656f1658f8
10 changed files with 288 additions and 187 deletions
+76 -46
View File
@@ -24,7 +24,7 @@ from django.core.cache import cache
from django.urls import reverse_lazy, reverse
from django.http import Http404, JsonResponse
from django.http import Http404, HttpResponseBadRequest, JsonResponse
from django.http import HttpResponseRedirect, HttpResponse
@@ -41,7 +41,16 @@ import json
import statistics
import plotly.express as px
from generic.views import AuthorRequiredMixin, ExamCloneMixin, ExamCreateBase, ExamDeleteBase, ExamUpdateBase, ExamViews, GenericViewBase, exam_inactive
from generic.views import (
AuthorRequiredMixin,
ExamCloneMixin,
ExamCreateBase,
ExamDeleteBase,
ExamUpdateBase,
ExamViews,
GenericViewBase,
exam_inactive,
)
from rest_framework import viewsets, permissions
from rest_framework.pagination import PageNumberPagination
@@ -156,29 +165,29 @@ def exam_start(request, pk):
if not exam.active:
return exam_inactive(request, context={"exam": exam})
raise Http404("Exam not found")
return render(
request,
"sbas/exam_start.html",
{
"exam": exam,
},
{"exam": exam, "valid_user": exam.check_logged_in_user(request)},
)
def exam_take_overview(request, pk, cid, passcode):
def exam_take_overview(request, pk, cid=None, passcode=None):
exam = get_object_or_404(Exam, pk=pk)
if not exam.active:
return exam_inactive(request, context={"exam": exam})
if not exam.check_cid_user(cid, passcode):
if not exam.check_cid_user(cid, passcode, request):
raise Http404("Error accessing exam")
questions = exam.exam_questions.all()
answers = UserAnswer.objects.filter(cid=cid, exam=exam)
if cid is not None:
answers = UserAnswer.objects.filter(cid=cid, exam=exam)
else:
answers = UserAnswer.objects.filter(user=request.user, exam=exam)
answer_question_map = {}
for ans in answers:
@@ -193,8 +202,11 @@ def exam_take_overview(request, pk, cid, passcode):
else:
question_answer_tuples.append((q, None))
c = CidUser.objects.filter(cid=cid).first()
cid_user_exam = exam.get_or_create_cid_user_exam(cid_user=c)
if cid is not None:
c = CidUser.objects.filter(cid=cid).first()
cid_user_exam = exam.get_or_create_cid_user_exam(cid_user=c)
else:
cid_user_exam = exam.get_or_create_cid_user_exam(user_user=request.user)
return render(
request,
@@ -211,18 +223,20 @@ def exam_take_overview(request, pk, cid, passcode):
)
def exam_take(request, pk, sk, cid, passcode):
def exam_take(request, pk: int, sk: int, cid: str = None, passcode: str = None):
exam = get_object_or_404(Exam, pk=pk)
if not exam.active:
raise Http404("Exam not found")
if not exam.check_cid_user(cid, passcode):
if not exam.check_cid_user(cid, passcode, request):
raise Http404("Error accessing exam")
c = CidUser.objects.filter(cid=cid).first()
cid_user_exam = exam.get_or_create_cid_user_exam(cid_user=c)
if cid is not None:
c = CidUser.objects.filter(cid=cid).first()
cid_user_exam = exam.get_or_create_cid_user_exam(cid_user=c)
else:
cid_user_exam = exam.get_or_create_cid_user_exam(user_user=request.user)
question = exam.exam_questions.all()[sk]
@@ -230,7 +244,17 @@ def exam_take(request, pk, sk, cid, passcode):
pos = exam.get_question_index(question)
answer = question.cid_user_answers.filter(cid=cid, exam=exam).first()
if cid is not None:
answer = question.cid_user_answers.filter(cid=cid, exam=exam).first()
else:
answer = question.cid_user_answers.filter(user=request.user, exam=exam).first()
previous = -1
if sk > 0:
previous = sk - 1
next = sk + 1
if sk == exam_length - 1:
next = False
if request.method == "POST":
if answer:
@@ -239,7 +263,11 @@ def exam_take(request, pk, sk, cid, passcode):
form = UserAnswerForm(request.POST)
if form.is_valid():
answer = form.save(commit=False)
answer.cid = cid
if cid is not None:
answer.cid = cid
else:
answer.user = request.user
answer.question = question
answer.exam = exam
# answer.published_date = timezone.now()
@@ -248,28 +276,28 @@ def exam_take(request, pk, sk, cid, passcode):
cid_user_exam.end_time = timezone.now()
cid_user_exam.save()
kwargs = {"pk": pk, "cid": cid, "passcode": passcode}
if cid is not None:
kwargs = {"pk": pk, "cid": cid, "passcode": passcode}
take_url = "sbas:exam_take"
finish_url = "sbas:exam_take_overview"
else:
kwargs = {"pk": pk}
take_url = "sbas:exam_take_user"
finish_url = "sbas:exam_take_overview_user"
if "next" in request.POST:
return redirect("sbas:exam_take", sk=pos + 1, **kwargs)
if not next:
return HttpResponseBadRequest("Invalid request")
return redirect(take_url, sk=pos + 1, **kwargs)
elif "previous" in request.POST:
return redirect("sbas:exam_take", sk=pos - 1, **kwargs)
return redirect(take_url, sk=pos - 1, **kwargs)
elif "finish" in request.POST:
return redirect("sbas:exam_take_overview", **kwargs)
return redirect(finish_url, **kwargs)
elif "goto" in request.POST:
return redirect(
"sbas:exam_take", sk=int(request.POST.get("goto")), **kwargs
)
return redirect(take_url, sk=int(request.POST.get("goto")), **kwargs)
else:
form = UserAnswerForm(instance=answer)
previous = -1
if sk > 0:
previous = sk - 1
next = sk + 1
if sk == exam_length - 1:
next = False
saved_answer = False
if answer is not None:
saved_answer = answer.answer
@@ -293,9 +321,7 @@ def exam_take(request, pk, sk, cid, passcode):
)
GenericExamViews = ExamViews(
Exam, Question, None, UserAnswer, "sbas", "sbas"
)
GenericExamViews = ExamViews(Exam, Question, None, UserAnswer, "sbas", "sbas")
class QuestionView(LoginRequiredMixin, SingleTableMixin, FilterView):
@@ -310,6 +336,7 @@ class QuestionView(LoginRequiredMixin, SingleTableMixin, FilterView):
context["app_name"] = "sbas"
return context
class QuestionCreateBase(RevisionMixin, LoginRequiredMixin, CreateView):
model = Question
form_class = QuestionForm
@@ -321,22 +348,20 @@ class QuestionCreateBase(RevisionMixin, LoginRequiredMixin, CreateView):
def get_context_data(self, **kwargs):
context = super(QuestionCreateBase, self).get_context_data(**kwargs)
#if self.request.POST:
# if self.request.POST:
# context["answer_formset"] = AnswerFormSet(
# self.request.POST, self.request.FILES
# )
#else:
# else:
# context["answer_formset"] = AnswerFormSet()
return context
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.save()
form.instance.author.add(self.request.user.id)
response = super().form_valid(form)
# If the normal submit button is pressed we save as normal
if "submit" in self.request.POST:
@@ -347,7 +372,6 @@ class QuestionCreateBase(RevisionMixin, LoginRequiredMixin, CreateView):
class QuestionCreate(QuestionCreateBase):
# initial = {'laterality': AnatomyQuestion.NONE}
def get_initial(self):
@@ -359,6 +383,7 @@ class QuestionCreate(QuestionCreateBase):
return initial
class QuestionUpdate(RevisionMixin, AuthorOrCheckerRequiredMixin, UpdateView):
model = Question
form_class = QuestionForm
@@ -373,10 +398,9 @@ class QuestionUpdate(RevisionMixin, AuthorOrCheckerRequiredMixin, UpdateView):
return context
def form_valid(self, form):
# save exam orders (there must be a better way to do this)
#exam_orders = {}
#for exam in self.object.exams.all():
# exam_orders = {}
# for exam in self.object.exams.all():
# exam_orders[exam] = list(exam.exam_questions.all())
# print(exam_orders[exam])
@@ -389,6 +413,7 @@ class QuestionUpdate(RevisionMixin, AuthorOrCheckerRequiredMixin, UpdateView):
response = super().form_valid(form)
return response
class QuestionClone(QuestionCreateBase):
"""Clones a existing question"""
@@ -404,6 +429,7 @@ class QuestionClone(QuestionCreateBase):
return initial_data
class UserAnswerView(LoginRequiredMixin, DetailView):
model = UserAnswer
@@ -450,9 +476,11 @@ class ExamUpdate(ExamUpdateBase, AuthorOrCheckerRequiredMixin):
model = Exam
form_class = ExamForm
class ExamDelete(AuthorOrCheckerRequiredMixin, ExamDeleteBase):
model = Exam
# class ExamViewSet(RevisionMixin, viewsets.ModelViewSet):
# # queryset = Exam.objects.all().order_by('name')
# serializer_class = ExamSerializer
@@ -468,11 +496,13 @@ class ExamDelete(AuthorOrCheckerRequiredMixin, ExamDeleteBase):
#
# return Exam.objects.filter(author__id=user.id)
class ExamAuthorUpdate(RevisionMixin, LoginRequiredMixin, AuthorRequiredMixin, UpdateView):
class ExamAuthorUpdate(
RevisionMixin, LoginRequiredMixin, AuthorRequiredMixin, UpdateView
):
model = Exam
form_class = ExamAuthorForm
class ExamClone(ExamCloneMixin, ExamCreate):
"""Clone exam view"""
"""Clone exam view"""