import secrets from typing import Optional from django.conf import settings from django_tables2 import SingleTableMixin from atlas.models import CaseCollection, CidReportAnswer from generic.decorators import user_is_cid_user_manager from generic.filters import UserUserFilter from generic.tables import UserUserTable from generic.views import CidManagerRequiredMixin, get_question_and_content_type from django.core.exceptions import PermissionDenied, ObjectDoesNotExist from django.shortcuts import render, get_object_or_404, redirect from django.views.decorators.csrf import csrf_exempt from django import forms from django.core.exceptions import ValidationError from django.core.validators import validate_email from django.utils.html import format_html # from django.contrib.auth.models import User from django.contrib.auth.decorators import login_required, user_passes_test from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType from django.contrib.auth.mixins import LoginRequiredMixin from django.views.generic.edit import CreateView, UpdateView, DeleteView from django.views.generic import ListView, TemplateView from django.db.models.functions import Lower from django.core.cache import cache from django.urls import reverse_lazy, reverse from django.http import Http404, JsonResponse from django.http import HttpResponseRedirect, HttpResponse from physics.models import UserAnswer as PhysicsUserAnswer, Question from physics.models import Exam as PhysicsExam from physics.models import Question as SbasQuestion from anatomy.models import UserAnswer as AnatomyUserAnswer from anatomy.models import Exam as AnatomyExam from anatomy.models import AnatomyQuestion from anatomy.models import Answer as AnatomyAnswer from rad.filters import UserListFilter from rapids.models import UserAnswer as RapidsUserAnswer from rapids.models import Exam as RapidsExam from rapids.models import Rapid from rapids.models import Answer as RapidAnswer from longs.models import UserAnswer as LongsUserAnswer from longs.models import Exam as LongsExam from longs.models import Long from sbas.models import UserAnswer as SbasUserAnswer from sbas.models import Exam as SbasExam from sbas.models import Question as SbasQuestion from anatomy.views import GenericExamViews as AnatomyExamViews from rapids.views import GenericExamViews as RapidsExamsViews from longs.views import GenericExamViews as LongsExamViews from generic.forms import QuestionNoteForm from generic.models import CidUser, QuestionNote, Supervisor, UserGrades, UserProfile from django_filters.views import FilterView import json from django.template import RequestContext def feedback_checker(user): return user.groups.filter(name="feedback_checker").exists() @user_is_cid_user_manager def people(request): return render(request, "people.html", {}) @login_required def profile(request): user = request.user return render(request, "profile.html", {"user": user}) @user_is_cid_user_manager def account_profile(request, slug): user = get_object_or_404(User, username=slug) return render(request, "profile.html", {"user": user}) def cid_selector(request): return render( request, "cid_selector.html", ) @login_required @user_passes_test(lambda u: u.is_superuser) def cid_scores_admin(request, cid): cid_user = CidUser.objects.filter(cid=cid).first() if not cid_user: raise Http404("CID not found") return cid_scores(request, cid_user.cid, cid_user.passcode) @login_required # @user_passes_test(lambda u: u.is_superuser) @user_is_cid_user_manager def cid_results(request, cid): cid_user = CidUser.objects.filter(cid=cid).first() if not cid_user: raise Http404("CID not found") report = cid_user.generate_exam_report() return render( request, "cid_results.html", { "report": report, }, ) @user_passes_test(lambda u: u.is_superuser) def user_scores_admin(request, user_id): try: user = User.objects.get(id=user_id) except User.DoesNotExist: raise Http404("Cid not found") return user_scores(request, user=user) @login_required def user_scores(request, user=None): # exam = get_object_or_404(Exam, pk=pk) # cid = pk # cid_user = CidUser.objects.filter(cid=cid).first() # print(cid_user) # if not cid_user or cid_user.passcode != passcode: # raise Http404("CID / Passcode combination not found") # print(cid_user.passcode) admin = True if user is None: user = request.user admin = False # questions = exam.exam_questions.all() EXAM_ANSWER_MAP = { "physics": (PhysicsUserAnswer, PhysicsExam), "anatomy": (AnatomyUserAnswer, AnatomyExam), "rapids": (RapidsUserAnswer, RapidsExam), "longs": (LongsUserAnswer, LongsExam), "sbas": (SbasUserAnswer, SbasExam), } kwargs = {"exam_mode": True, "archive": False} exams = [] for exam_type in EXAM_ANSWER_MAP: UserAnswer, Exam = EXAM_ANSWER_MAP[exam_type] exam_answers = UserAnswer.objects.filter(user=user) exam_ids = exam_answers.values_list("exam").distinct() exams_to_add = Exam.objects.filter(id__in=exam_ids, **kwargs) if exams_to_add: exams.append((exam_type, exams_to_add)) # case_collections = request.user.casecollection_exams.all() USER_EXAM_TYPES = ( ("Physics", "user_physics_exams"), ("Rapids", "user_rapid_exams"), ("SBAs", "user_sba_exams"), ("Anatomy", "user_anatomy_exams"), ("Longs", "user_longs_exams"), # ("CaseCollection", "user_casecollection_exams"), ) available_exams = [] for n, t in USER_EXAM_TYPES: exam_rel = getattr(user, t) if exam_rel.exists(): temp_exams = exam_rel.filter(exam_mode=True, archive=False).order_by("name") available_exams.append((n, temp_exams)) # available_exams = request.user.get_cid_exams() return render( request, "user_scores.html", { # "physics_exams": physics_exams, # "anatomy_exams": anatomy_exams, # "rapid_exams": rapid_exams, # "longs_exams": longs_exams, # "sba_exams": sba_exams, "all_exams": exams, # "passcode": passcode, "cid_user": user, "available_exams": available_exams, "case_collections": [], # case_collections, "admin": admin, }, ) def cid_scores(request, cid, passcode): # exam = get_object_or_404(Exam, pk=pk) cid_user = CidUser.objects.filter(cid=cid).first() print(cid_user) if not cid_user or cid_user.passcode != passcode: raise Http404("CID / Passcode combination not found") print(cid_user.passcode) # questions = exam.exam_questions.all() EXAM_ANSWER_MAP = { "physics": (PhysicsUserAnswer, PhysicsExam), "anatomy": (AnatomyUserAnswer, AnatomyExam), "rapids": (RapidsUserAnswer, RapidsExam), "longs": (LongsUserAnswer, LongsExam), "sbas": (SbasUserAnswer, SbasExam), } kwargs = {"exam_mode": True, "archive": False} exams = [] for exam_type in EXAM_ANSWER_MAP: UserAnswer, Exam = EXAM_ANSWER_MAP[exam_type] exam_answers = UserAnswer.objects.filter(cid=cid) exam_ids = exam_answers.values_list("exam").distinct() exams_to_add = Exam.objects.filter(id__in=exam_ids, **kwargs) if exams_to_add: exams.append((exam_type, exams_to_add)) case_collections = cid_user.casecollection_exams.all() available_exams = cid_user.get_cid_exams() return render( request, "cid_scores.html", { # "physics_exams": physics_exams, # "anatomy_exams": anatomy_exams, # "rapid_exams": rapid_exams, # "longs_exams": longs_exams, # "sba_exams": sba_exams, "all_exams": exams, "cid": cid, "passcode": passcode, "cid_user": cid_user, "available_exams": available_exams, "case_collections": case_collections, }, ) def active_exams(request, cid=None, passcode=None, based=True): active_exams = {} if cid is not None: active_exams["cid"] = cid cid_user = CidUser.objects.filter(cid=cid).first() if not cid_user or cid_user.passcode != passcode: return JsonResponse({"status": "invalid"}, status=401) anatomy_exams = AnatomyExamViews.active_exams( request, False, cid=cid, passcode=passcode ) rapid_exams = RapidsExamsViews.active_exams( request, False, cid=cid, passcode=passcode ) long_exams = LongsExamViews.active_exams(request, False, cid=cid, passcode=passcode) exams = [] exams.extend(anatomy_exams) exams.extend(rapid_exams) exams.extend(long_exams) active_exams["exams"] = exams if request.user: active_exams["user"] = request.user.username active_exams["user_id"] = request.user.pk else: active_exams["user"] = False return JsonResponse(active_exams) def active_exams_unbased(request, cid=None, passcode=None): return active_exams(request, cid, passcode, based=False) @csrf_exempt def exam_submit(request): print(request) if request.method == "POST": print(request.POST) exam_type, exam_id = request.POST.get("eid").split("/") try: if exam_type == "anatomy": return AnatomyExamViews.post_exam_answers(request) elif exam_type == "rapid": return RapidsExamsViews.post_exam_answers(request) elif exam_type == "long": return LongsExamViews.post_exam_answers(request) return JsonResponse({"success": False, "error": "Invalid exam type"}) except ObjectDoesNotExist: return JsonResponse({"success": False, "error": "Invalid data"}) # post_exam_answers return JsonResponse({"success": False, "error": "Invalid data"}) def feedback_create(request, question_type, pk): if question_type == "anatomy": question = AnatomyQuestion elif question_type == "rapid": question = Rapid elif question_type == "long": question = Long return JsonResponse({"success": False, "error": "Invalid exam type"}) class AddQuestionNote(CreateView): model = QuestionNote form_class = QuestionNoteForm # fields = ("author", "note_type", "note") def get_form_kwargs(self): kwargs = super(AddQuestionNote, self).get_form_kwargs() # update the kwargs for the form init method with yours kwargs.update(self.kwargs) # self.kwargs contains all url conf params kwargs.update({"user": self.request.user}) return kwargs def get_initial(self): pk = self.kwargs["pk"] self.question_type = self.kwargs["question_type"] question, content_type = get_question_and_content_type(self.question_type) self.question = get_object_or_404(question, pk=pk) return {"content_type": content_type, "object_id": pk} def get_context_data(self, **kwargs): # This can also be accessed via view.**** in the template context = super(AddQuestionNote, self).get_context_data(**kwargs) context["question"] = self.question context["question_type"] = self.question_type return context def form_valid(self, form): model = form.save(commit=False) if self.request.user.is_authenticated: model.user_author = self.request.user model.save() response = super().form_valid(form) return response @user_passes_test(lambda u: u.is_superuser) def feedback_note_delete(request, pk): q = get_object_or_404(QuestionNote, pk=pk) q.delete() return redirect(q.get_object_url()) @login_required def feedback_mark_complete(request, pk): q = get_object_or_404(QuestionNote, pk=pk) if ( request.user not in q.question.author.all() and not request.user.groups.filter(name="feedback_checker").exists() ): raise PermissionDenied() q.complete = True q.save() return redirect(q.get_object_url()) @csrf_exempt def answer_suggestion_submit(request): if request.method == "POST": post_data = json.loads(request.body) question_type, qid = post_data["qid"].split("/") answer_string = post_data["answer"] status = post_data["status"] if str(status) not in " 012": return JsonResponse({"success": False, "error": "Invalid status"}) if question_type == "anatomy": AnswerModel = AnatomyAnswer question = AnatomyQuestion elif question_type == "rapid": AnswerModel = RapidAnswer question = Rapid else: return JsonResponse({"success": False, "error": "Invalid question type"}) q = get_object_or_404(question, pk=qid) if AnswerModel.objects.filter(answer=answer_string, question=q): return JsonResponse({"success": False, "error": "Answer already exists"}) a = AnswerModel(question=q, answer=answer_string, status=status, proposed=True) a.save() return JsonResponse({"success": True, "error": "Answer submited"}) # post_exam_answers return JsonResponse({"success": False, "error": "Invalid data"}) @login_required def answer_submit(request): if request.method == "POST": qid = request.POST.get("qid") question_type = request.POST.get("question_type") answer_string = request.POST.get("answer") status = request.POST.get("status") if str(status) not in "012": return JsonResponse({"success": False, "error": "Invalid status"}) if question_type == "anatomy": AnswerModel = AnatomyAnswer question = AnatomyQuestion elif question_type == "rapid": AnswerModel = RapidAnswer question = Rapid else: return JsonResponse({"success": False, "error": "Invalid question type"}) q = get_object_or_404(question, pk=qid) if AnswerModel.objects.filter(answer=answer_string, question=q): return JsonResponse({"success": False, "error": "Answer already exists"}) a = AnswerModel(question=q, answer=answer_string, status=status) a.save() return JsonResponse({"success": True, "error": "Answer added"}) return JsonResponse({"success": False, "error": "Invalid data"}) @login_required def answer_suggestion_confirm(request): if request.method == "POST": question_type = request.POST.get("question_type") aid = request.POST.get("aid") status = request.POST.get("status") if question_type == "anatomy": AnswerModel = AnatomyAnswer question = AnatomyQuestion elif question_type == "rapid": AnswerModel = RapidAnswer question = Rapid else: return JsonResponse({"success": False, "error": "Invalid question type"}) answer = get_object_or_404(AnswerModel, pk=aid) answer.proposed = False answer.status = status answer.save() return JsonResponse({"success": True, "error": "Answer changed"}) @login_required @user_passes_test(feedback_checker) def view_feedback(request): # exam = get_object_or_404(Exam, pk=pk) feedback_notes = QuestionNote.objects.filter(complete=False) return render( request, "view_feedback.html", { "notes": feedback_notes, }, ) def privacy_view(request): return render(request, "privacy.html") # HTTP Error 400 def page_not_found(request, exception): response = render(request, "404.html") response.status_code = 404 return response def page_forbidden(request, exception): response = render(request, "403.html") response.status_code = 403 return response def server_error(request): response = render(request, "500.html") response.status_code = 500 return response class UserListView(CidManagerRequiredMixin, FilterView): model = User template_name = "user_list_view.html" filterset_class = UserListFilter class UserListTableView(CidManagerRequiredMixin, SingleTableMixin, FilterView): model = User table_class = UserUserTable template_name = "generic/user_view.html" filterset_class = UserUserFilter #table_pagination = {"per_page": 5} def get_paginate_by(self, table_data) -> int | None: return 100 #return super().get_paginate_by(table_data) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) # user = self.request.user # filters = {"archive": False, "exam_mode": True} # physics_exams = [(i.name, i.pk) for i in PhysicsExam.objects.filter(**filters)] # rapid_exams = [(i.name, i.pk) for i in RapidExam.objects.filter(**filters)] # sba_exams = [(i.name, i.pk) for i in SbasExam.objects.filter(**filters)] # longs_exams = [(i.name, i.pk) for i in LongExam.objects.filter(**filters)] # anatomy_exams = [(i.name, i.pk) for i in AnatomyExam.objects.filter(**filters)] # casecollection_exams = [ # (i.name, i.pk) # for i in CaseCollection.objects.filter(archive=False, collection_type__gt=0) # ] # context["physics_exams"] = physics_exams # context["rapid_exams"] = rapid_exams # context["sba_exams"] = sba_exams # context["longs_exams"] = longs_exams # context["anatomy_exams"] = anatomy_exams # context["casecollection_exams"] = casecollection_exams # cid_user_groups = [ # (i.name, i.pk) for i in CidUserGroup.objects.filter(archive=False) # ] # context["cid_user_groups"] = cid_user_groups return context class DeleteUserView(CidManagerRequiredMixin, DeleteView): model = User template_name: str = "confirm_delete.html" success_url = reverse_lazy("accounts_list") class UpdateUserView(CidManagerRequiredMixin, UpdateView): model = User fields = ["first_name", "last_name", "email", "is_active"] # Keep listing whatever fields # the combined UserProfile and User exposes. template_name = "user_update.html" slug_field = "username" slug_url_kwarg = "slug" def get_success_url(self): view_name = "account_profile" # No need for reverse_lazy here, because it's called inside the method return reverse(view_name, kwargs={"slug": self.object.username}) class UpdateUserProfileAdminView(CidManagerRequiredMixin, UpdateView): model = UserProfile fields = [ "supervisor", "grade", "registration_number", "peninsula_trainee", ] # Keep listing whatever fields template_name = "user_update_profile.html" slug_field = "user__username" slug_url_kwarg = "slug" def get_success_url(self): view_name = "account_profile" # No need for reverse_lazy here, because it's called inside the method return reverse(view_name, kwargs={"slug": self.object.username}) class UpdateUserProfileView(UpdateView): model = UserProfile fields = [ "supervisor", "grade", "registration_number", #"peninsula_trainee", ] # Keep listing whatever fields template_name = "user_update_profile.html" slug_field = "user__username" slug_url_kwarg = "slug" def dispatch(self, request, *args, **kwargs): if request.user.is_superuser or request.user.groups.filter(name="cid_user_manager").exists(): self.fields = ["supervisor", "grade", "registration_number", "peninsula_trainee"] # Check permissions for the request.user here elif request.user != self.get_object().user: raise Http404 elif request.user.userprofile.peninsula_trainee: self.fields = ["supervisor", "grade", "registration_number"] else: self.fields = ["grade", "registration_number"] return super().dispatch(request, *args, **kwargs) def get_success_url(self): if self.request.user.is_superuser or self.request.user.groups.filter(name="cid_user_manager").exists(): view_name = "account_profile" # No need for reverse_lazy here, because it's called inside the method return reverse(view_name, kwargs={"slug": self.object.username}) else: return reverse("profile") # class UpdateUser(TemplateView): # # user_form_class = UserForm # comment_form_class = UserProfileForm # template_name = 'user_update.html' # slug_field = 'username' # slug_url_kwarg = 'slug' # # def post(self, request): # post_data = request.POST or None # user_form = self.user_form_class(post_data, prefix='user') # profile_form = self.profile_form_class(post_data, prefix='userprofile') # # context = self.get_context_data(post_form=user_form, # profile_form=profile_form) # # if user_form.is_valid(): # self.form_save(user_form) # if profile_form.is_valid(): # self.form_save(profile_form) # # return self.render_to_response(context) # # def form_save(self, form): # obj = form.save() # #messages.success(self.request, "{} saved successfully".format(obj)) # return obj # # def get(self, request, *args, **kwargs): # return self.post(request, *args, **kwargs) @user_is_cid_user_manager def accounts_check_users(request): if request.method == "POST": users = json.loads(request.POST.get("user_list")) existing_users = [] for email in users: if User.objects.filter(username=email).exists(): existing_users.append(email) return JsonResponse({"users": existing_users}) return @user_is_cid_user_manager def accounts_bulk_create(request): if request.method == "POST": context = {} users = json.loads(request.POST.get("usersjson")) print(users) created_users = [] existing_users = [] error_text = "" try: users = json.loads(request.POST.get("usersjson")) print(users) for user in users: if not User.objects.filter(username=user["email"]).exists(): # Start by checking the email try: validate_email(user["email"]) except ValidationError: error_text = ( error_text + f"
{user['email']} does not appear to be a valid email (account for {user['first_name']} {user['last_name']} not created).
" ) continue # Try to create the user try: print(user) user_dict = { "username": user["email"], "first_name": user["email"], "last_name": user["last_name"], "email": user["email"], "password": secrets.token_hex(nbytes=16), } new_user = User.objects.create_user(**user_dict) except Exception as error: error_text = ( error_text + f"Error creating user: { user['email'] }
{error}
Error creating user profile: { user['email'] }
{error} (user not created)
Error creating users.
{error}