322 lines
13 KiB
Python
322 lines
13 KiB
Python
import random
|
|
|
|
from django.contrib.auth.decorators import login_required, permission_required
|
|
from django.core.exceptions import PermissionDenied
|
|
from django.shortcuts import get_object_or_404, render, redirect
|
|
from django.utils.decorators import method_decorator
|
|
from django.views.generic import DetailView, ListView, TemplateView, FormView, CreateView, FormView, DeleteView, UpdateView
|
|
from django.contrib import messages
|
|
from django.urls import reverse_lazy
|
|
from django.db import transaction
|
|
from django.forms import inlineformset_factory
|
|
from django.http import HttpResponseRedirect
|
|
|
|
from accounts.decorators import student_required, lecturer_required
|
|
from .models import *
|
|
from .forms import *
|
|
|
|
|
|
@method_decorator([login_required, lecturer_required], name='dispatch')
|
|
class QuizCreateView(CreateView):
|
|
model = Quiz
|
|
form_class = QuizAddForm
|
|
|
|
def get_context_data(self, *args, **kwargs):
|
|
context = super(QuizCreateView, self).get_context_data(**kwargs)
|
|
context['course'] = Course.objects.get(slug=self.kwargs['slug'])
|
|
if self.request.POST:
|
|
context['form'] = QuizAddForm(self.request.POST)
|
|
# context['quiz'] = self.request.POST.get('quiz')
|
|
else:
|
|
context['form'] = QuizAddForm(initial={'course': Course.objects.get(slug=self.kwargs['slug'])})
|
|
return context
|
|
|
|
def form_valid(self, form, **kwargs):
|
|
context = self.get_context_data()
|
|
form = context['form']
|
|
with transaction.atomic():
|
|
self.object = form.save()
|
|
if form.is_valid():
|
|
form.instance = self.object
|
|
form.save()
|
|
return redirect('mc_create', slug=self.kwargs['slug'], quiz_id=form.instance.id)
|
|
return super(QuizCreateView, self).form_invalid(form)
|
|
|
|
|
|
@method_decorator([login_required, lecturer_required], name='dispatch')
|
|
class QuizUpdateView(UpdateView):
|
|
model = Quiz
|
|
form_class = QuizAddForm
|
|
|
|
def get_context_data(self, *args, **kwargs):
|
|
context = super(QuizUpdateView, self).get_context_data(**kwargs)
|
|
context['course'] = Course.objects.get(slug=self.kwargs['slug'])
|
|
quiz = Quiz.objects.get(pk=self.kwargs['pk'])
|
|
if self.request.POST:
|
|
context['form'] = QuizAddForm(self.request.POST, instance=quiz)
|
|
else:
|
|
context['form'] = QuizAddForm(instance=quiz)
|
|
return context
|
|
|
|
def form_valid(self, form, **kwargs):
|
|
context = self.get_context_data()
|
|
course = context['course']
|
|
form = context['form']
|
|
with transaction.atomic():
|
|
self.object = form.save()
|
|
if form.is_valid():
|
|
form.instance = self.object
|
|
form.save()
|
|
return redirect('quiz_index', course.slug)
|
|
return super(QuizUpdateView, self).form_invalid(form)
|
|
|
|
|
|
@login_required
|
|
@lecturer_required
|
|
def quiz_delete(request, slug, pk):
|
|
quiz = Quiz.objects.get(pk=pk)
|
|
course = Course.objects.get(slug=slug)
|
|
quiz.delete()
|
|
messages.success(request, f'successfuly deleted.')
|
|
return redirect('quiz_index', quiz.course.slug)
|
|
|
|
|
|
@method_decorator([login_required, lecturer_required], name='dispatch')
|
|
class MCQuestionCreate(CreateView):
|
|
model = MCQuestion
|
|
form_class = MCQuestionForm
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(MCQuestionCreate, self).get_context_data(**kwargs)
|
|
context['course'] = Course.objects.get(slug=self.kwargs['slug'])
|
|
context['quiz_obj'] = Quiz.objects.get(id=self.kwargs['quiz_id'])
|
|
context['quizQuestions'] = Question.objects.filter(quiz=self.kwargs['quiz_id']).count()
|
|
if self.request.POST:
|
|
context['form'] = MCQuestionForm(self.request.POST)
|
|
context['formset'] = MCQuestionFormSet(self.request.POST)
|
|
else:
|
|
context['form'] = MCQuestionForm(initial={'quiz': self.kwargs['quiz_id']})
|
|
context['formset'] = MCQuestionFormSet()
|
|
|
|
return context
|
|
|
|
def form_valid(self, form):
|
|
context = self.get_context_data()
|
|
formset = context['formset']
|
|
course = context['course']
|
|
with transaction.atomic():
|
|
form.instance.question = self.request.POST.get('content')
|
|
self.object = form.save()
|
|
if formset.is_valid():
|
|
formset.instance = self.object
|
|
formset.save()
|
|
if "another" in self.request.POST:
|
|
return redirect('mc_create', slug=self.kwargs['slug'], quiz_id=self.kwargs['quiz_id'])
|
|
return redirect('quiz_index', course.slug)
|
|
return super(MCQuestionCreate, self).form_invalid(form)
|
|
|
|
|
|
@login_required
|
|
def quiz_list(request, slug):
|
|
quizzes = Quiz.objects.filter(course__slug = slug).order_by('-timestamp')
|
|
course = Course.objects.get(slug = slug)
|
|
return render(request, 'quiz/quiz_list.html', {'quizzes': quizzes, 'course': course})
|
|
# return render(request, 'quiz/quiz_list.html', {'quizzes': quizzes})
|
|
|
|
|
|
@method_decorator([login_required, lecturer_required], name='dispatch')
|
|
class QuizMarkerMixin(object):
|
|
@method_decorator(login_required)
|
|
# @method_decorator(permission_required('quiz.view_sittings'))
|
|
def dispatch(self, *args, **kwargs):
|
|
return super(QuizMarkerMixin, self).dispatch(*args, **kwargs)
|
|
|
|
|
|
# @method_decorator([login_required, lecturer_required], name='get_queryset')
|
|
class SittingFilterTitleMixin(object):
|
|
def get_queryset(self):
|
|
queryset = super(SittingFilterTitleMixin, self).get_queryset()
|
|
quiz_filter = self.request.GET.get('quiz_filter')
|
|
if quiz_filter:
|
|
queryset = queryset.filter(quiz__title__icontains=quiz_filter)
|
|
|
|
return queryset
|
|
|
|
|
|
@method_decorator([login_required], name='dispatch')
|
|
class QuizUserProgressView(TemplateView):
|
|
template_name = 'progress.html'
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
return super(QuizUserProgressView, self).dispatch(request, *args, **kwargs)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(QuizUserProgressView, self).get_context_data(**kwargs)
|
|
progress, c = Progress.objects.get_or_create(user=self.request.user)
|
|
context['cat_scores'] = progress.list_all_cat_scores
|
|
context['exams'] = progress.show_exams()
|
|
context['exams_counter'] = progress.show_exams().count()
|
|
return context
|
|
|
|
from result.models import TakenCourse
|
|
|
|
@method_decorator([login_required, lecturer_required], name='dispatch')
|
|
class QuizMarkingList(QuizMarkerMixin, SittingFilterTitleMixin, ListView):
|
|
model = Sitting
|
|
# def get_context_data(self, **kwargs):
|
|
# context = super(QuizMarkingList, self).get_context_data(**kwargs)
|
|
# context['queryset_counter'] = super(QuizMarkingList, self).get_queryset().filter(complete=True).filter(course__allocated_course__lecturer__pk=self.request.user.id).count()
|
|
# context['marking_list'] = super(QuizMarkingList, self).get_queryset().filter(complete=True).filter(course__allocated_course__lecturer__pk=self.request.user.id)
|
|
# return context
|
|
def get_queryset(self):
|
|
if self.request.user.is_superuser:
|
|
queryset = super(QuizMarkingList, self).get_queryset().filter(complete=True)
|
|
else:
|
|
queryset = super(QuizMarkingList, self).get_queryset().filter(quiz__course__allocated_course__lecturer__pk=self.request.user.id).filter(complete=True)
|
|
|
|
# search by user
|
|
user_filter = self.request.GET.get('user_filter')
|
|
if user_filter:
|
|
queryset = queryset.filter(user__username__icontains=user_filter)
|
|
|
|
return queryset
|
|
|
|
|
|
@method_decorator([login_required, lecturer_required], name='dispatch')
|
|
class QuizMarkingDetail(QuizMarkerMixin, DetailView):
|
|
model = Sitting
|
|
|
|
def post(self, request, *args, **kwargs):
|
|
sitting = self.get_object()
|
|
|
|
q_to_toggle = request.POST.get('qid', None)
|
|
if q_to_toggle:
|
|
q = Question.objects.get_subclass(id=int(q_to_toggle))
|
|
if int(q_to_toggle) in sitting.get_incorrect_questions:
|
|
sitting.remove_incorrect_question(q)
|
|
else:
|
|
sitting.add_incorrect_question(q)
|
|
|
|
return self.get(request)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(QuizMarkingDetail, self).get_context_data(**kwargs)
|
|
context['questions'] = context['sitting'].get_questions(with_answers=True)
|
|
return context
|
|
|
|
|
|
# @method_decorator([login_required, student_required], name='dispatch')
|
|
@method_decorator([login_required], name='dispatch')
|
|
class QuizTake(FormView):
|
|
form_class = QuestionForm
|
|
template_name = 'question.html'
|
|
result_template_name = 'result.html'
|
|
# single_complete_template_name = 'single_complete.html'
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
self.quiz = get_object_or_404(Quiz, slug=self.kwargs['slug'])
|
|
self.course = get_object_or_404(Course, pk=self.kwargs['pk'])
|
|
quizQuestions = Question.objects.filter(quiz=self.quiz).count()
|
|
course = get_object_or_404(Course, pk=self.kwargs['pk'])
|
|
|
|
if quizQuestions <= 0:
|
|
messages.warning(request, f'Question set of the quiz is empty. try later!')
|
|
return redirect('quiz_index', self.course.slug)
|
|
|
|
if self.quiz.draft and not request.user.has_perm('quiz.change_quiz'):
|
|
raise PermissionDenied
|
|
|
|
self.sitting = Sitting.objects.user_sitting(request.user, self.quiz, self.course)
|
|
|
|
if self.sitting is False:
|
|
# return render(request, self.single_complete_template_name)
|
|
messages.info(request, f'You have already sat this exam and only one sitting is permitted')
|
|
return redirect('quiz_index', self.course.slug)
|
|
|
|
return super(QuizTake, self).dispatch(request, *args, **kwargs)
|
|
|
|
def get_form(self, *args, **kwargs):
|
|
self.question = self.sitting.get_first_question()
|
|
self.progress = self.sitting.progress()
|
|
|
|
if self.question.__class__ is Essay_Question:
|
|
form_class = EssayForm
|
|
else:
|
|
form_class = self.form_class
|
|
|
|
return form_class(**self.get_form_kwargs())
|
|
|
|
def get_form_kwargs(self):
|
|
kwargs = super(QuizTake, self).get_form_kwargs()
|
|
|
|
return dict(kwargs, question=self.question)
|
|
|
|
def form_valid(self, form):
|
|
self.form_valid_user(form)
|
|
if self.sitting.get_first_question() is False:
|
|
return self.final_result_user()
|
|
|
|
self.request.POST = {}
|
|
|
|
return super(QuizTake, self).get(self, self.request)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(QuizTake, self).get_context_data(**kwargs)
|
|
context['question'] = self.question
|
|
context['quiz'] = self.quiz
|
|
context['course'] = get_object_or_404(Course, pk=self.kwargs['pk'])
|
|
if hasattr(self, 'previous'):
|
|
context['previous'] = self.previous
|
|
if hasattr(self, 'progress'):
|
|
context['progress'] = self.progress
|
|
return context
|
|
|
|
def form_valid_user(self, form):
|
|
progress, c = Progress.objects.get_or_create(user=self.request.user)
|
|
guess = form.cleaned_data['answers']
|
|
is_correct = self.question.check_if_correct(guess)
|
|
|
|
if is_correct is True:
|
|
self.sitting.add_to_score(1)
|
|
progress.update_score(self.question, 1, 1)
|
|
else:
|
|
self.sitting.add_incorrect_question(self.question)
|
|
progress.update_score(self.question, 0, 1)
|
|
|
|
if self.quiz.answers_at_end is not True:
|
|
self.previous = {
|
|
'previous_answer': guess,
|
|
'previous_outcome': is_correct,
|
|
'previous_question': self.question,
|
|
'answers': self.question.get_choices(),
|
|
'question_type': {self.question.__class__.__name__: True}
|
|
}
|
|
else:
|
|
self.previous = {}
|
|
|
|
self.sitting.add_user_answer(self.question, guess)
|
|
self.sitting.remove_first_question()
|
|
|
|
def final_result_user(self):
|
|
results = {
|
|
'course': get_object_or_404(Course, pk=self.kwargs['pk']),
|
|
'quiz': self.quiz,
|
|
'score': self.sitting.get_current_score,
|
|
'max_score': self.sitting.get_max_score,
|
|
'percent': self.sitting.get_percent_correct,
|
|
'sitting': self.sitting,
|
|
'previous': self.previous,
|
|
'course': get_object_or_404(Course, pk=self.kwargs['pk'])
|
|
}
|
|
|
|
self.sitting.mark_quiz_complete()
|
|
|
|
if self.quiz.answers_at_end:
|
|
results['questions'] = self.sitting.get_questions(with_answers=True)
|
|
results['incorrect_questions'] = self.sitting.get_incorrect_questions
|
|
|
|
if self.quiz.exam_paper is False or self.request.user.is_superuser or self.request.user.is_lecturer :
|
|
self.sitting.delete()
|
|
|
|
return render(self.request, self.result_template_name, results)
|