From 5f32cce58ddba476e8a367bb3fc1d50191aee5a2 Mon Sep 17 00:00:00 2001 From: Ross Date: Mon, 20 Dec 2021 10:22:39 +0000 Subject: [PATCH] . --- anatomy/templates/anatomy/exam_cids.html | 21 -------- anatomy/templates/anatomy/exams.html | 2 +- anatomy/views.py | 2 +- generic/forms.py | 7 ++- generic/models.py | 3 ++ generic/tables.py | 5 +- generic/templates/generic/base.html | 4 +- generic/templates/generic/ciduser_form.html | 26 +++++++++ generic/urls.py | 1 + generic/views.py | 36 +++++++++++-- longs/templates/longs/exams.html | 12 +++-- longs/urls.py | 1 + physics/urls.py | 1 + rapids/templates/rapids/exams.html | 1 + rapids/urls.py | 1 + sbas/templates/sbas/exams.html | 11 ++-- sbas/urls.py | 2 + sbas/views.py | 60 ++++++++++++++++++++- templates/exam_cids.html | 27 ++++++++++ templates/exam_list.html | 12 ++++- 20 files changed, 192 insertions(+), 43 deletions(-) delete mode 100644 anatomy/templates/anatomy/exam_cids.html create mode 100755 generic/templates/generic/ciduser_form.html create mode 100644 templates/exam_cids.html diff --git a/anatomy/templates/anatomy/exam_cids.html b/anatomy/templates/anatomy/exam_cids.html deleted file mode 100644 index fea07897..00000000 --- a/anatomy/templates/anatomy/exam_cids.html +++ /dev/null @@ -1,21 +0,0 @@ -{% extends 'anatomy/exams.html' %} - -{% block content %} - - {% load thumbnail %} -
-

Exam: {{ exam.name }}

- -

{{cid_user_count}} users.

- - -
    - {% for cid in cid_users %} -
  1. {{cid.cid}} [{{cid.passcode}}]
  2. - - {% endfor %} -
- - -
-{% endblock %} diff --git a/anatomy/templates/anatomy/exams.html b/anatomy/templates/anatomy/exams.html index e053338b..0b659555 100644 --- a/anatomy/templates/anatomy/exams.html +++ b/anatomy/templates/anatomy/exams.html @@ -8,7 +8,7 @@ {% if exam.exam_mode %} Mark / Scores / - CIDs / + Candidates / {% endif %} Add New Question {% endblock %} diff --git a/anatomy/views.py b/anatomy/views.py index 816831e3..3ac27502 100644 --- a/anatomy/views.py +++ b/anatomy/views.py @@ -226,7 +226,7 @@ def loadJsonAnswer(answer): # in the frontend this is appended with the type # e.g. anatomy/1 - # TODO Update to only allow valid CIDs + # TODO Update to only allow valid Candidates eid = int(answer["eid"].split("/")[1]) # As access is not restricted make sure the data appears valid if (not isinstance(answer["cid"], int)) or ( diff --git a/generic/forms.py b/generic/forms.py index 44ece2a5..20bd50ec 100755 --- a/generic/forms.py +++ b/generic/forms.py @@ -10,7 +10,7 @@ from django.forms import ( from django.forms import inlineformset_factory from generic.models import ( - Examination, QuestionNote + CidUser, Examination, QuestionNote ) from django.contrib.admin.widgets import FilteredSelectMultiple @@ -66,3 +66,8 @@ class QuestionNoteForm(ModelForm): ##initial["content_id"] = pk #self.fields["content_type"].initial = content_type #self.fields["object_id"].initial = pk + +class CidUserForm(ModelForm): + class Meta: + model = CidUser + fields = ["cid", "passcode", "active", "internal_candidate", "name", "email", "supervisor_email"] \ No newline at end of file diff --git a/generic/models.py b/generic/models.py index a54fdd03..2da0d104 100644 --- a/generic/models.py +++ b/generic/models.py @@ -1,8 +1,11 @@ +from django.contrib.auth.mixins import LoginRequiredMixin from django.db import models from django.http import HttpRequest from smtplib import SMTPException from django.urls import reverse +from django.views.generic.edit import CreateView +from reversion.views import RevisionMixin import tagulous import tagulous.models diff --git a/generic/tables.py b/generic/tables.py index 80dd95af..df3c097b 100644 --- a/generic/tables.py +++ b/generic/tables.py @@ -23,7 +23,10 @@ class CidUserTable(tables.Table): # edit = tables.LinkColumn( # "anatomy:anatomy_question_update", text="Edit", args=[A("pk")], orderable=False # ) - cid = tables.LinkColumn("cid_scores", args=[A("cid"), A("passcode")], orderable=True) + cid = tables.LinkColumn( + "cid_scores", args=[A("cid"), A("passcode")], orderable=True + ) + edit = tables.LinkColumn("generic:update_cid", text="Edit", args=[A("pk")], orderable=True) # image = ImageColumn("image", orderable=False) # clone = tables.LinkColumn('anatomy:anatomy_question_clone', # text='Clone', diff --git a/generic/templates/generic/base.html b/generic/templates/generic/base.html index 550e2ab1..bdbb5448 100755 --- a/generic/templates/generic/base.html +++ b/generic/templates/generic/base.html @@ -1,7 +1,7 @@ {% extends 'base.html' %} {% block title %} -CIDs +Candidates {% endblock %} {% block css %} @@ -15,7 +15,7 @@ CIDs {% block content %} {% endblock %} {% block navigation %} -CIDs: +Candidates: {% if request.user.is_authenticated %} Cids / Groups / diff --git a/generic/templates/generic/ciduser_form.html b/generic/templates/generic/ciduser_form.html new file mode 100755 index 00000000..d13dae67 --- /dev/null +++ b/generic/templates/generic/ciduser_form.html @@ -0,0 +1,26 @@ +{% extends "atlas/base.html" %} + + +{% block css %} +{% endblock %} +{% block js %} + +{{form.media}} + + + + +{% endblock %} +{% block content %} +

Edit CID User

+Use this form to create or edit a CID user. +
+ {% csrf_token %} + + + {{ form.as_table }} +
+ +
+{% endblock %} \ No newline at end of file diff --git a/generic/urls.py b/generic/urls.py index 3aff1b23..161bd44c 100755 --- a/generic/urls.py +++ b/generic/urls.py @@ -26,6 +26,7 @@ urlpatterns = [ ), name="examination-autocomplete", ), + path("cids/manage//update", views.CidUserUpdate.as_view(), name="update_cid"), path("cids/manage/", views.CidUserView.as_view(), name="manage_cids"), path("cids/group/", views.group_view, name="group_view"), path("cids/group//email", views.group_email, name="group_email"), diff --git a/generic/views.py b/generic/views.py index c1a7b146..58b0eb1f 100644 --- a/generic/views.py +++ b/generic/views.py @@ -25,14 +25,17 @@ import json import urllib from django.views import View +from django.views.generic.edit import CreateView, UpdateView from django_filters.views import FilterView from django_tables2.views import SingleTableMixin +from reversion.views import RevisionMixin from generic.decorators import user_is_cid_user_manager from generic.filters import CidUserFilter from generic.tables import CidUserTable from .forms import ( + CidUserForm, ExaminationForm, ) @@ -414,10 +417,20 @@ class ExamViews(View, LoginRequiredMixin): if not self.check_user_access(request.user): raise PermissionDenied + marking = False + + if self.app_name in ("rapids", "anatomy", "longs"): + marking = True + return render( request, "exam_list.html".format(self.app_name), - {"exams": exams, "app_name": self.app_name, "view_all": all}, + { + "exams": exams, + "app_name": self.app_name, + "view_all": all, + "marking": marking, + }, ) @method_decorator(login_required) @@ -526,8 +539,13 @@ class ExamViews(View, LoginRequiredMixin): return render( request, - "{}/exam_cids.html".format(self.app_name), - {"exam": exam, "cid_users": cid_users, "cid_user_count": cid_user_count}, + "exam_cids.html", + { + "exam": exam, + "cid_users": cid_users, + "cid_user_count": cid_user_count, + "app_name": self.app_name, + }, ) # @method_decorator(login_required) @@ -1287,7 +1305,7 @@ def group_email(request, pk, resend=False): if resend: users = group.ciduser_set.filter() else: - users = group.ciduser_set.filter(login_email_sent=False) + users = gGroup.ciduser_set.filter(login_email_sent=False) sent = [] not_sent = [] @@ -1449,3 +1467,13 @@ def manage_cid_users(request): return JsonResponse({"status": "success"}) return JsonResponse({"status": "fail"}) + + +class CidUserCreate(RevisionMixin, LoginRequiredMixin, CreateView): + model = CidUser + form_class = CidUserForm + + +class CidUserUpdate(RevisionMixin, LoginRequiredMixin, UpdateView): + model = CidUser + form_class = CidUserForm diff --git a/longs/templates/longs/exams.html b/longs/templates/longs/exams.html index 4ac3011b..a265c7cd 100644 --- a/longs/templates/longs/exams.html +++ b/longs/templates/longs/exams.html @@ -1,7 +1,11 @@ {% extends 'longs/base.html' %} {% block navigation %} -{{block.super}} -
-Exams: {{exam.name}}-> Overview / Mark / Scores -{% endblock %} \ No newline at end of file + {{block.super}} +
+ Exams: {{exam.name}}-> + Overview / + Mark / + Scores + Candidates / +{% endblock %} diff --git a/longs/urls.py b/longs/urls.py index 29dcce98..ff349a6b 100755 --- a/longs/urls.py +++ b/longs/urls.py @@ -125,6 +125,7 @@ urlpatterns = [ path("exam/", views.GenericExamViews.exam_list, name="exam_list"), path("exam/all", views.GenericExamViews.exam_list_all, name="exam_list_all"), path("exam/create", views.ExamCreate.as_view(), name="exam_create"), + path("exam//cids", views.GenericExamViews.exam_cids, name="exam_cids"), path("exam//clone", views.ExamClone.as_view(), name="exam_clone"), path("exam//update", views.ExamUpdate.as_view(), name="exam_update"), path("exam//delete", views.ExamDelete.as_view(), name="exam_delete"), diff --git a/physics/urls.py b/physics/urls.py index e170e4c5..30115f69 100644 --- a/physics/urls.py +++ b/physics/urls.py @@ -43,6 +43,7 @@ urlpatterns = [ name="exam_toggle_results_published", ), path("exam//clone", views.ExamClone.as_view(), name="exam_clone"), + path("exam//cids", views.GenericExamViews.exam_cids, name="exam_cids"), path("exam//update", views.ExamUpdate.as_view(), name="exam_update"), path("exam//delete", views.ExamDelete.as_view(), name="exam_delete"), path( diff --git a/rapids/templates/rapids/exams.html b/rapids/templates/rapids/exams.html index ab8c5f66..1d257c34 100644 --- a/rapids/templates/rapids/exams.html +++ b/rapids/templates/rapids/exams.html @@ -8,6 +8,7 @@ {% if exam.exam_mode %} Mark / Scores / + Candidates / {% endif %} Add New Question {% endblock %} diff --git a/rapids/urls.py b/rapids/urls.py index faabb137..b480777e 100755 --- a/rapids/urls.py +++ b/rapids/urls.py @@ -105,6 +105,7 @@ urlpatterns = [ path("exam/all", views.GenericExamViews.exam_list_all, name="exam_list_all"), path("exam/create", views.ExamCreate.as_view(), name="exam_create"), path("exam//clone", views.ExamClone.as_view(), name="exam_clone"), + path("exam//cids", views.GenericExamViews.exam_cids, name="exam_cids"), path("exam//update", views.ExamUpdate.as_view(), name="exam_update"), path("exam//delete", views.ExamDelete.as_view(), name="exam_delete"), path("exam/json/", views.GenericExamViews.active_exams, name="active_exams"), diff --git a/sbas/templates/sbas/exams.html b/sbas/templates/sbas/exams.html index 8512644a..7e74a3a8 100644 --- a/sbas/templates/sbas/exams.html +++ b/sbas/templates/sbas/exams.html @@ -1,7 +1,10 @@ {% extends 'sbas/base.html' %} {% block navigation %} -{{block.super}} -
-Exams: {{exam.name}}-> Overview / Scores -{% endblock %} \ No newline at end of file + {{block.super}} +
+ Exams: {{exam.name}}-> + Overview / + Scores + Candidates / +{% endblock %} diff --git a/sbas/urls.py b/sbas/urls.py index 0e015375..555eed3c 100644 --- a/sbas/urls.py +++ b/sbas/urls.py @@ -48,6 +48,8 @@ urlpatterns = [ ), # path("exam/submit", views.GenericExamViews.postExamAnswers, name="exam_answers_submit"), path("exam/", views.GenericExamViews.exam_list, name="exam_list"), + path("exam//clone", views.ExamClone.as_view(), name="exam_clone"), + path("exam//cids", views.GenericExamViews.exam_cids, name="exam_cids"), path("exam/available", views.active_exams, name="active_exams"), # path("exam/json/", views.exam_json, name="exam_json"), # path("exam/json//recreate", views.exam_json_recreate, name="exam_json_recreate"), diff --git a/sbas/views.py b/sbas/views.py index be3f74c8..8f2e1693 100644 --- a/sbas/views.py +++ b/sbas/views.py @@ -1,6 +1,7 @@ +from reversion.views import RevisionMixin from generic.mixins import SuperuserRequiredMixin from django.views.generic.detail import DetailView -from sbas.forms import CidUserAnswerForm +from sbas.forms import CidUserAnswerForm, ExamForm from django.shortcuts import render, get_object_or_404, redirect from django.views.decorators.csrf import csrf_exempt from django import forms @@ -37,7 +38,7 @@ import json import statistics import plotly.express as px -from generic.views import ExamViews, GenericViewBase +from generic.views import ExamCloneMixin, ExamViews, GenericViewBase from rest_framework import viewsets, permissions from rest_framework.pagination import PageNumberPagination @@ -319,3 +320,58 @@ class UserAnswerDelete(SuperuserRequiredMixin, DeleteView): GenericViews = GenericViewBase("sbas", Question, CidUserAnswer, Exam) + +class QuestionDelete(AuthorOrCheckerRequiredMixin, DeleteView): + model = Question + success_url = reverse_lazy("sbas:question_list") + + +class ExamCreate(RevisionMixin, LoginRequiredMixin, CreateView): + model = Exam + form_class = ExamForm + + def form_valid(self, form): + self.object = form.save(commit=False) + self.object.save() + + form.instance.author.add(self.request.user.id) + return super().form_valid(form) + + +class ExamUpdate( + RevisionMixin, LoginRequiredMixin, AuthorOrCheckerRequiredMixin, UpdateView +): + model = Exam + form_class = ExamForm + + def form_valid(self, form): + self.object = form.save(commit=False) + self.object.save() + + form.instance.author.add(self.request.user.id) + return super().form_valid(form) + + +#class ExamViewSet(RevisionMixin, viewsets.ModelViewSet): +# # queryset = Exam.objects.all().order_by('name') +# serializer_class = ExamSerializer +# permission_classes = [permissions.IsAuthenticated] +# +# def get_queryset(self): +# """ +# This view should return a list exams available to a user. +# """ +# user = self.request.user +# if user.groups.filter(name="sbas_checker").exists(): +# return Exam.objects.all() +# +# return Exam.objects.filter(author__id=user.id) + + +class ExamDelete(RevisionMixin, AuthorOrCheckerRequiredMixin, DeleteView): + model = Exam + template_name = "exam_confirm_delete.html" + success_url = reverse_lazy("sbas:index") + +class ExamClone(ExamCloneMixin, ExamCreate): + """Clone exam view""" diff --git a/templates/exam_cids.html b/templates/exam_cids.html new file mode 100644 index 00000000..ef0ca8f9 --- /dev/null +++ b/templates/exam_cids.html @@ -0,0 +1,27 @@ +{% extends app_name|add:'/exams.html' %} + +{% block content %} + + {% load thumbnail %} +
+

Exam: {{ exam.name }}

+ +

{{cid_user_count}} candidates.

+ + +
    + {% for cid in cid_users %} + +
  1. {{cid.cid}} [{{cid.passcode}}] {{cid.name}} / + {{cid.email}} / + {{cid.supervisor_email}} / + {% if cid.login_email_sent %}Login email sent{% endif %} / + {% if cid.results_email_sent %}Results email sent{% endif %} / +
  2. + + {% endfor %} +
+ + +
+{% endblock %} diff --git a/templates/exam_list.html b/templates/exam_list.html index cbcc9d9e..37418305 100644 --- a/templates/exam_list.html +++ b/templates/exam_list.html @@ -8,7 +8,11 @@ {% for exam in exams %} {% if exam.active %}
  • - {{exam.name}} MarkScores Results Published + {{exam.name}} + {% if marking %}Mark{% endif %} + Candidates + Scores + Results Published
  • {% endif %} {% endfor %} @@ -19,7 +23,11 @@ {% for exam in exams %} {% if not exam.active %}
  • - {{exam.name}} MarkScores Results Published + {{exam.name}} + {% if marking %}Mark{% endif %} + Candidates + Scores + Results Published
  • {% endif %} {% endfor %}