Files
penracourses/generic/views.py
T
Ross d8a4942382 .
2021-04-29 19:33:31 +01:00

508 lines
18 KiB
Python

from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.auth.decorators import login_required, user_passes_test
from django.views.decorators.csrf import csrf_exempt
from django.core.exceptions import PermissionDenied
from django.http import Http404, JsonResponse
from django.http import HttpResponseRedirect, HttpResponse
from django.contrib.auth.mixins import LoginRequiredMixin
from django.utils.decorators import method_decorator
from django.core.cache import cache
import json
import urllib
from django.views import View
from .forms import (
ExaminationForm,
)
from .models import Examination
from rapids.models import Rapid as RapidQuestion
from rapids.models import Exam as RapidExam
from longs.models import Long as LongQuestion
from longs.models import Exam as LongExam
from anatomy.models import AnatomyQuestion as AnatomyQuestion
from anatomy.models import Exam as AnatomyExam
from django.db.models import Case, When
from django.conf import settings
from datetime import datetime
import os
# Create your views here.
@login_required
def create_examination(request):
form = ExaminationForm(request.POST or None)
if form.is_valid():
instance = form.save()
return HttpResponse(
'<script>opener.closePopup(window, "%s", "%s", "#id_examination");</script>'
% (instance.pk, instance)
)
return render(
request, "rapids/create_simple.html", {"form": form, "name": "Examination"}
)
@csrf_exempt
def get_examination_id(request):
if request.is_ajax():
examination_name = request.GET["examination_name"]
examination_id = Examination.objects.get(name=examination_name).id
data = {
"examination_id": examination_id,
}
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):
if request.is_ajax() and request.method == "POST":
question_type = request.POST.get("type")
if question_type == "rapid":
Exam = RapidExam
Question = RapidQuestion
elif question_type == "long":
Exam = LongExam
Question = LongQuestion
elif question_type == "anatomy":
Exam = AnatomyExam
Question = AnatomyQuestion
else:
data = {"status": "error"}
return JsonResponse(data, status=400)
exam = get_object_or_404(Exam, pk=request.POST.get("exam_id"))
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)
for i in question_ids:
exam.exam_questions.add(i)
#exam.exam_questions.add(question_objects)
exam.save()
data = {"status": "success"}
return JsonResponse(data, status=200)
if "set_open_access" in request.POST:
if request.POST.get("set_open_access") == "true":
state = True
else:
state = False
questions_changed = []
for q in exam.exam_questions.all():
q.open_access = state
q.save()
questions_changed.append(q.pk)
data = {"status": "success", "questions": questions_changed, "state": state}
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)])
objects = Question.objects.filter(pk__in=new_order).order_by(preserved)
if len(objects) != exam.exam_questions.count():
data = {"status": "error, count does not match"}
return JsonResponse(data, status=400)
exam.exam_questions.set(objects)
exam.save()
data = {"status": "success"}
return JsonResponse(data, status=200)
data = {"status": "error, unknown request"}
return JsonResponse(data, status=400)
else:
data = {"status": "error"}
return JsonResponse(data, status=400)
class ExamViews(View, LoginRequiredMixin):
def __init__(self, exam, question, app, question_type, loadJsonAnswer):
self.Exam = exam
self.Question = question
self.app_name = app
self.question_type = question_type
self.loadJsonAnswer = loadJsonAnswer
@method_decorator(login_required)
def exam_toggle_results_published(self, request, pk):
if request.is_ajax() and request.method == "POST":
exam = get_object_or_404(self.Exam, pk=pk)
exam.publish_results = (
True if request.POST.get("publish_results") == "true" else False
)
exam.save()
data = {"status": "success", "publish_results": exam.publish_results}
return JsonResponse(data, status=200)
else:
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":
exam = get_object_or_404(self.Exam, pk=pk)
exam.active = True if request.POST.get("active") == "true" else False
exam.save()
data = {"status": "success", "active": exam.active}
return JsonResponse(data, status=200)
else:
data = {"status": "error"}
return JsonResponse(data, status=400)
@method_decorator(login_required)
def exam_json_recreate(self, request, pk):
exam = get_object_or_404(self.Exam, pk=pk)
exam.recreate_json = True
exam.save()
exam.get_exam_json()
return redirect("{}:exam_overview".format(self.app_name), pk=pk)
@method_decorator(login_required)
def question_json_recreate(self, request, pk):
question = get_object_or_404(self.Question, pk=pk)
question.recreate_json = True
question.save()
return redirect("{}:question_detail".format(self.app_name), pk=pk)
@method_decorator(login_required)
def exam_list(self, request):
exams = self.Exam.objects.filter(exam_mode=True)
if self.app_name == "rapids" and not request.user.groups.filter(name='rapid_checker').exists():
raise PermissionDenied
if self.app_name == "anatomy" and not request.user.groups.filter(name='anatomy_checker').exists():
raise PermissionDenied
if self.app_name == "longs" and not request.user.groups.filter(name='long_checker').exists():
raise PermissionDenied
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))
exam = get_object_or_404(self.Exam, pk=pk)
if request.user not in exam.author.all():
if self.app_name == "rapids" and not request.user.groups.filter(name='rapid_checker').exists():
raise PermissionDenied
if self.app_name == "anatomy" and not request.user.groups.filter(name='anatomy_checker').exists():
raise PermissionDenied
if self.app_name == "longs" and not request.user.groups.filter(name='long_checker').exists():
raise PermissionDenied
questions = exam.exam_questions.all()
question_number = len(questions)
return render(
request,
"{}/exam_overview.html".format(self.app_name),
{"exam": exam, "questions": questions, "question_number": question_number},
)
@method_decorator(login_required)
def exam_json_edit(self, request, pk):
if request.is_ajax() and request.method == "POST":
exam = get_object_or_404(self.Exam, pk=pk)
if "add_exam_questions" in request.POST:
question_ids = json.loads(request.POST.get("add_exam_questions"))
question_objects = self.Question.objects.filter(pk__in=question_ids)
exam.exam_questions.add(question_objects)
exam.save()
data = {"status": "success"}
return JsonResponse(data, status=200)
if "set_open_access" in request.POST:
if request.POST.get("set_open_access") == "true":
state = True
else:
state = False
questions_changed = []
for q in exam.exam_questions.all():
q.open_access = state
q.save()
questions_changed.append(q.pk)
data = {"status": "success", "questions": questions_changed, "state": state}
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)])
objects = self.Question.objects.filter(pk__in=new_order).order_by(preserved)
if len(objects) != exam.exam_questions.count():
data = {"status": "error, count does not match"}
return JsonResponse(data, status=400)
exam.exam_questions.set(objects)
exam.save()
data = {"status": "success"}
return JsonResponse(data, status=200)
data = {"status": "error, unknown request"}
return JsonResponse(data, status=400)
else:
data = {"status": "error"}
return JsonResponse(data, status=400)
@method_decorator(login_required)
def mark_overview(self, request, pk):
exam = get_object_or_404(self.Exam, pk=pk)
if request.user not in exam.author.all():
if self.app_name == "rapids" and not request.user.groups.filter(name='rapid_checker').exists():
raise PermissionDenied
if self.app_name == "anatomy" and not request.user.groups.filter(name='anatomy_checker').exists():
raise PermissionDenied
if self.app_name == "longs" and not request.user.groups.filter(name='long_checker').exists():
raise PermissionDenied
questions = exam.exam_questions.all()
return render(
request, "{}/mark_overview.html".format(self.app_name), {"exam": exam, "questions": questions}
)
@method_decorator(login_required)
def index(self, request):
if request.user.groups.filter(name='rapid_checker').exists():
exams = self.Exam.objects.all()
else:
exams = self.Exam.objects.filter(author__id=request.user.id)
return render(request, "{}/index.html".format(self.app_name), {"exams": exams})
@method_decorator(login_required)
def exam_question_detail(self, request, pk, sk):
exam = get_object_or_404(self.Exam, pk=pk)
if request.user not in exam.author.all():
if self.app_name == "rapids" and not request.user.groups.filter(name='rapid_checker').exists():
raise PermissionDenied
if self.app_name == "anatomy" and not request.user.groups.filter(name='anatomy_checker').exists():
raise PermissionDenied
if self.app_name == "longs" and not request.user.groups.filter(name='long_checker').exists():
raise PermissionDenied
question = exam.exam_questions.all()[sk]
exam_length = len(exam.exam_questions.all())
pos = exam.get_question_index(question) + 1
previous = -1
if sk > 0:
previous = sk - 1
next = sk + 1
if sk == exam_length - 1:
next = False
return render(
request,
"{}/question_detail.html".format(self.app_name),
{
"question": question,
"exam": exam,
"next": next,
"previous": previous,
"exam_length": exam_length,
"pos": pos,
},
)
def active_exams(self, request, json=True, based=True):
exams = self.Exam.objects.all()
active_exams = {"exams": []}
for exam in exams:
if exam.active:
if exam.json_creation_time:
creation_time = exam.json_creation_time.isoformat()
else:
creation_time = "None"
url = request.build_absolute_uri(exam.get_json_url())
# hacky
if not based:
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
}
if self.question_type == "long":
h = {}
for question in exam.exam_questions.all():
h[question.pk] = question.json_creation_time.isoformat()
obj["multi_question_json"] = h
active_exams["exams"].append( obj )
if json == False:
return active_exams["exams"]
return JsonResponse(active_exams)
@method_decorator(csrf_exempt)
def postExamAnswers(self, request):
if request.is_ajax and request.method == "POST":
n = 0
for answer in json.loads(request.POST.get("answers")):
ret = self.loadJsonAnswer(answer)
#if ret is not True:
# return ret
if ret:
n = n + 1
# print(UserAnswer.objects.filter(exam__id=q["eid"]))
# print(request.urlencode())
return JsonResponse({"success": True, "question_count": n})
return JsonResponse({"success": False, "error": "Invalid data"})
def exam_json_unbased(self, request, pk):
"""
No (file based) caching is enabled for unbased exams
"""
exam = get_object_or_404(self.Exam, pk=pk)
if not exam.active:
raise Http404("No available exam")
exam_json = exam.get_exam_json(based=False)
return JsonResponse(exam_json)
def exam_json(self, request, pk):
exam = get_object_or_404(self.Exam, pk=pk)
if not exam.active:
raise Http404("No available exam")
#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)
if os.path.isfile(path) and not exam.recreate_json:
#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)
return redirect(url)
return JsonResponse(exam_json_cache)
time = datetime.now()
with open(path, "w+") as f:
exam_json = exam.get_exam_json()
exam_json["generated"] = time.isoformat()
f.write(json.dumps(exam_json))
exam.recreate_json = False
exam.json_creation_time = time
exam.save()
# 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)
#cache.set("{}_exam_json_{}".format(self.app_name, pk), exam_json, 3600)
return JsonResponse(exam_json)
def exam_question_json(self, request, pk, sk):
question = get_object_or_404(self.Question, pk=sk)
exam = get_object_or_404(self.Exam, pk=pk)
if not exam.active:
raise Http404("No available exam")
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)
return redirect("{}:question_json".format(self.app_name), pk=sk)
def exam_question_json_unbased(self, request, pk, sk):
question = get_object_or_404(self.Question, pk=sk)
exam = get_object_or_404(self.Exam, pk=pk)
if not exam.active:
raise Http404("No available exam")
return redirect("{}:question_json_unbased".format(self.app_name), pk=sk)
redirect()
question_json_cache = cache.get("{}_question_json_{}".format(self.app_name, sk))
if question_json_cache is not None and not question.recreate_json:
question_json_cache["cached"] = True
return JsonResponse(question_json_cache)
question_json = exam.get_exam_question_json(sk)
cache.set("{}_question_json_{}".format(self.app_name, sk), question_json, 3600)
return JsonResponse(question_json)