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( '' % (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): if not request.user.groups.filter(name='rapid_checker').exists(): raise PermissionDenied exams = self.Exam.objects.all() 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 self.app_name == "rapids" and request.user not in exam.author.all() and not request.user.groups.filter(name='rapid_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() and not request.user.groups.filter(name='rapid_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() and not request.user.groups.filter(name='rapid_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)