This commit is contained in:
Ross
2022-06-07 21:32:49 +01:00
parent 349654a22b
commit 45100f27b0
11 changed files with 136 additions and 141 deletions
+6 -21
View File
@@ -50,7 +50,7 @@ from .models import (
# IncorrectAnswers,
)
from generic.models import CidUser, Examination
from generic.views import AuthorRequiredMixin, ExamCloneMixin, ExamViews, GenericViewBase
from generic.views import AuthorRequiredMixin, ExamCloneMixin, ExamCreateBase, ExamDeleteBase, ExamUpdateBase, ExamViews, GenericViewBase
from reversion.views import RevisionMixin
from .decorators import user_is_author_or_anatomy_checker
@@ -963,30 +963,19 @@ class QuestionDelete(AuthorOrCheckerRequiredMixin, DeleteView):
success_url = reverse_lazy("anatomy:question_list")
class ExamCreate(RevisionMixin, LoginRequiredMixin, CreateView):
class ExamCreate(ExamCreateBase):
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
):
class ExamUpdate(ExamUpdateBase, AuthorOrCheckerRequiredMixin):
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 ExamDelete(AuthorOrCheckerRequiredMixin, ExamDeleteBase):
model = Exam
class ExamViewSet(RevisionMixin, viewsets.ModelViewSet):
@@ -1009,10 +998,6 @@ class ExamAuthorUpdate(RevisionMixin, LoginRequiredMixin, AuthorRequiredMixin, U
model = Exam
form_class = ExamAuthorForm
class ExamDelete(RevisionMixin, AuthorOrCheckerRequiredMixin, DeleteView):
model = Exam
template_name = "exam_confirm_delete.html"
success_url = reverse_lazy("anatomy:index")
GenericViews = GenericViewBase("anatomy", AnatomyQuestion, CidUserAnswer, Exam)
+30 -7
View File
@@ -32,7 +32,7 @@ import urllib
from django.views import View
from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django_filters.views import FilterView
from django_filters import FilterSet
from django_filters import FilterSet, OrderingFilter
from django_tables2.views import SingleTableMixin
from reversion.views import RevisionMixin
from atlas.models import CaseCollection
@@ -294,6 +294,8 @@ class ExamViews(View, LoginRequiredMixin):
# exam_group = group_map[self.app_name]
class ExamFilter(FilterSet):
sort_order = OrderingFilter(fields=(("name", "name"), ("exam_mode", "exam_mode")))
class Meta:
model = exam
fields = {
@@ -329,6 +331,8 @@ class ExamViews(View, LoginRequiredMixin):
# filter param is either missing or empty, use initial as default
if not data.get("archive"):
data["archive"] = False
if not data.get("sort_order"):
data["sort_order"] = "name"
super().__init__(data, *args, **kwargs)
@@ -523,15 +527,18 @@ class ExamViews(View, LoginRequiredMixin):
@method_decorator(login_required)
def exam_list(self, request, all=False):
if all:
exams = self.Exam.objects.all().order_by("name")
if not self.check_user_access(request.user):
#raise PermissionDenied
exam_list = self.Exam.objects.filter(author__id=request.user.id).order_by("name")
else:
exams = self.Exam.objects.filter(exam_mode=True, archive=False).order_by(
exam_list = self.Exam.objects.all().order_by("name")
if not all:
exams = exam_list.filter(exam_mode=True, archive=False).order_by(
"name"
)
if not self.check_user_access(request.user):
raise PermissionDenied
marking = False
@@ -1021,7 +1028,7 @@ class ExamViews(View, LoginRequiredMixin):
view_feedback = False
if (
#request.user.groups.filter(name=self.checker_group).exists() or
# request.user.groups.filter(name=self.checker_group).exists() or
request.user.groups.filter(name="feedback_checker").exists()
or request.user in question.author.all()
):
@@ -2095,6 +2102,14 @@ class ExamCreateBase(RevisionMixin, LoginRequiredMixin, CreateView):
form.instance.author.add(self.request.user.id)
return super().form_valid(form)
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super().get_context_data(**kwargs)
print(dir(context["view"]))
print(context["view"].template_name_suffix)
print(context["view"].model.app_name)
return context
class ExamUpdateBase(RevisionMixin, LoginRequiredMixin, UpdateView):
template_name = "exam_update_form.html"
@@ -2106,3 +2121,11 @@ class ExamUpdateBase(RevisionMixin, LoginRequiredMixin, UpdateView):
form.instance.author.add(self.request.user.id)
return super().form_valid(form)
class ExamDeleteBase(RevisionMixin, DeleteView):
template_name = "exam_confirm_delete.html"
def get_success_url(self) -> str:
# return super().get_success_url()(self):
return reverse_lazy(f"{self.model.app_name}:index")
+8 -27
View File
@@ -75,7 +75,7 @@ from django.forms.models import model_to_dict
from longs.forms import LongCreationDefaultForm
from longs.models import LongCreationDefault
from generic.views import AuthorRequiredMixin, ExamCloneMixin, ExamViews
from generic.views import AuthorRequiredMixin, ExamCloneMixin, ExamCreateBase, ExamDeleteBase, ExamUpdateBase, ExamViews
from reversion.views import RevisionMixin
import reversion
@@ -1112,47 +1112,28 @@ def long_series_order_upload_filename(request, pk):
GenericExamViews = ExamViews(Exam, Long, None, CidUserAnswer, "longs", "long")
class ExamCreate(RevisionMixin, LoginRequiredMixin, CreateView):
class ExamCreate(ExamCreateBase):
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(ExamUpdateBase, AuthorOrCheckerRequiredMixin):
model = Exam
form_class = ExamForm
class ExamDelete(AuthorOrCheckerRequiredMixin, ExamDeleteBase):
model = Exam
class ExamClone(ExamCloneMixin, ExamCreate):
"""Clone exam view"""
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 ExamAuthorUpdate(RevisionMixin, LoginRequiredMixin, AuthorRequiredMixin, UpdateView):
model = Exam
form_class = ExamAuthorForm
class ExamDelete(RevisionMixin, AuthorOrCheckerRequiredMixin, DeleteView):
model = Exam
template_name = "exam_confirm_delete.html"
success_url = reverse_lazy("longs:index")
class ExamViewSet(RevisionMixin, viewsets.ModelViewSet):
queryset = Exam.objects.all().order_by("name")
serializer_class = ExamSerializer
+12 -25
View File
@@ -45,6 +45,9 @@ import plotly.express as px
from generic.views import (
AuthorRequiredMixin,
ExamCloneMixin,
ExamCreateBase,
ExamDeleteBase,
ExamUpdateBase,
ExamViews,
GenericViewBase,
)
@@ -377,17 +380,19 @@ GenericExamViews = ExamViews(Exam, Question, None, CidUserAnswer, "physics", "ph
GenericViews = GenericViewBase("physics", Question, CidUserAnswer, Exam)
class ExamCreate(RevisionMixin, LoginRequiredMixin, CreateView):
class ExamCreate(ExamCreateBase):
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(ExamUpdateBase, AuthorOrCheckerRequiredMixin):
model = Exam
form_class = ExamForm
class ExamDelete(AuthorOrCheckerRequiredMixin, ExamDeleteBase):
model = Exam
class ExamClone(AuthorOrCheckerRequiredMixin, ExamCloneMixin, ExamCreate):
@@ -401,24 +406,6 @@ class ExamAuthorUpdate(
form_class = ExamAuthorForm
class ExamDelete(RevisionMixin, AuthorOrCheckerRequiredMixin, DeleteView):
model = Exam
template_name = "exam_confirm_delete.html"
success_url = reverse_lazy("physics:index")
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 QuestionView(
+5 -1
View File
@@ -1,12 +1,16 @@
from cProfile import label
import django_filters
from generic.models import UserGrades
from django.contrib.auth.models import User
class UserListFilter(django_filters.FilterSet):
userprofile__grade = django_filters.CharFilter(label="Grade")
userprofile__grade = django_filters.ModelChoiceFilter(label="Grade", queryset=UserGrades.objects.all())
#test2 = django_filters.CharFilter()
class Meta:
model= User
@@ -36,6 +36,7 @@
<button id="add-to-exam" data-exam_json_edit_url="{% url 'generic:generic_exam_json_edit' %}"
data-exam_list_url="{% url 'rapid-exam-list' %}" data-type="rapid" data-csrf="{{ csrf_token}}"
data-qid="{{question.pk}}">Add to exam</button>
<button id="cancel-add-to-exam" style="display:none">Cancel</button>
<span id="exam-options"></span>
</div>
<p class="pre-whitespace"><b>Open Access:</b> {{ question.open_access }}</p>
@@ -156,8 +157,15 @@
})
})
$("#cancel-add-to-exam").click(function (evt) {
$("#add-to-exam").toggle();
$("#cancel-add-to-exam").toggle();
$("#exam-options").empty();
})
$("#add-to-exam").click(function (evt) {
$("#add-to-exam").toggle();
$("#cancel-add-to-exam").toggle();
var jqxhr = $.get(evt.target.dataset.exam_list_url, function (data) {
console.log(data);
@@ -380,11 +388,18 @@
<style>
.toggle-button {
font-size: x-small;
opacity: 10%;
opacity: 30%;
}
.toggle-button:hover {
font-size: x-small;
opacity: 100%;
}
#cancel-add-to-exam {
border-style: dashed;
}
#cancel-add-to-exam:hover {
background-color: purple;
}
</style>
+42 -1
View File
@@ -33,6 +33,18 @@ def create_superuser(db, django_user_model):
"admin", "ross@xkjq.uk", "adminpassword"
)
@pytest.fixture(params=EXAM_VIEWS)
def set_up_exams(request, django_user_model, create_superuser):
basic_user = django_user_model.objects.create_user(
"user1", "user@user.net", "pass2"
)
e1 = request.param.Exam.objects.create(name="test exam", exam_mode=True)
e = request.param.Exam.objects.create(name="test exam 2", exam_mode=True)
e.author.add(basic_user)
return request.param
# @pytest.fixture
# def create_exam(db):
@@ -40,6 +52,18 @@ def create_superuser(db, django_user_model):
# Very basic tests, make sure we can hit key urls without error
def test_index2(client, set_up_exams):
client.login(username="admin", password="adminpassword")
response = client.get(reverse(f"{set_up_exams.app_name}:index"))
assert response.status_code == 200
assert (
BeautifulSoup(response.content, "html.parser")
.find("h4", {"class": "exam-number-title"})
.getText()
== "2 exams found."
)
@pytest.mark.parametrize("exam_views", EXAM_VIEWS)
def test_index(client, create_superuser, exam_views, django_user_model):
@@ -63,6 +87,15 @@ def test_index(client, create_superuser, exam_views, django_user_model):
== "2 exams found."
)
response = client.get(reverse(f"{exam_views.app_name}:exam_list"))
assert response.status_code == 200
assert (
len(BeautifulSoup(response.content, "html.parser")
.select("a.exam-name"))
== 2
)
if exam_views.app_name in JSON_APPS:
response = client.get(reverse(f"{exam_views.app_name}:active_exams"))
@@ -86,7 +119,6 @@ def test_index(client, create_superuser, exam_views, django_user_model):
HTTP_X_REQUESTED_WITH="XMLHttpRequest",
)
print(f"{e1.valid_user_users.all()=}")
assert json.loads(add_user_response.content)["status"] == "success"
@@ -103,6 +135,15 @@ def test_index(client, create_superuser, exam_views, django_user_model):
== "1 exams found."
)
response = client.get(reverse(f"{exam_views.app_name}:exam_list"))
assert (
len(BeautifulSoup(response.content, "html.parser")
.select("a.exam-name"))
== 1
)
if exam_views.app_name in JSON_APPS:
response = client.get(reverse(f"{exam_views.app_name}:active_exams"))
+6 -7
View File
@@ -46,6 +46,7 @@ from generic.views import (
AuthorRequiredMixin,
ExamCloneMixin,
ExamCreateBase,
ExamDeleteBase,
ExamUpdateBase,
ExamViews,
GenericViewBase,
@@ -787,6 +788,9 @@ class ExamUpdate(ExamUpdateBase, AuthorOrCheckerRequiredMixin):
form_class = ExamForm
class ExamDelete(AuthorOrCheckerRequiredMixin, ExamDeleteBase):
model = Exam
class ExamAuthorUpdate(
RevisionMixin, LoginRequiredMixin, AuthorRequiredMixin, UpdateView
):
@@ -794,11 +798,6 @@ class ExamAuthorUpdate(
form_class = ExamAuthorForm
class ExamDelete(RevisionMixin, AuthorOrCheckerRequiredMixin, DeleteView):
model = Exam
template_name = "exam_confirm_delete.html"
success_url = reverse_lazy("rapids:index")
class UserAnswerView(LoginRequiredMixin, DetailView):
model = CidUserAnswer
@@ -840,9 +839,9 @@ class ExamViewSet(RevisionMixin, viewsets.ModelViewSet):
"""
user = self.request.user
if user.groups.filter(name="rapid_checker").exists():
return Exam.objects.all()
return Exam.objects.filter(archive=False).order_by('name')
return Exam.objects.filter(author__id=user.id)
return Exam.objects.filter(author__id=user.id, archive=False).order_by('name')
class QuestionAnswerViewSet(viewsets.ModelViewSet):
+6 -44
View File
@@ -40,7 +40,7 @@ import json
import statistics
import plotly.express as px
from generic.views import AuthorRequiredMixin, ExamCloneMixin, ExamViews, GenericViewBase
from generic.views import AuthorRequiredMixin, ExamCloneMixin, ExamCreateBase, ExamDeleteBase, ExamUpdateBase, ExamViews, GenericViewBase
from rest_framework import viewsets, permissions
from rest_framework.pagination import PageNumberPagination
@@ -342,31 +342,17 @@ class QuestionDelete(AuthorOrCheckerRequiredMixin, DeleteView):
success_url = reverse_lazy("sbas:question_list")
class ExamCreate(RevisionMixin, LoginRequiredMixin, CreateView):
class ExamCreate(ExamCreateBase):
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
):
class ExamUpdate(ExamUpdateBase, AuthorOrCheckerRequiredMixin):
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 ExamDelete(AuthorOrCheckerRequiredMixin, ExamDeleteBase):
model = Exam
# class ExamViewSet(RevisionMixin, viewsets.ModelViewSet):
# # queryset = Exam.objects.all().order_by('name')
@@ -388,30 +374,6 @@ class ExamAuthorUpdate(RevisionMixin, LoginRequiredMixin, AuthorRequiredMixin, U
form_class = ExamAuthorForm
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"""
class ExamDelete(RevisionMixin, AuthorOrCheckerRequiredMixin, DeleteView):
model = Exam
template_name = "exam_confirm_delete.html"
success_url = reverse_lazy("sbas:index")
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)
"""Clone exam view"""
+1 -3
View File
@@ -1,12 +1,10 @@
{% extends "base.html" %}
{% comment %} {% extends exam.app_name|add:"/base.html" %} {% endcomment %}
{% extends view.model.app_name|add:"/base.html" %}
{% block js %}
{{ form.media }}
{% endblock %}
{% block content %}
{{form.instance}}
<h2>Add Exam</h2>
<form action="" method="post">
+4 -4
View File
@@ -4,11 +4,11 @@
<h1>Examinations</h1>
<div class="{{app_name}}">
Active exams:<br/>
<ul class="exam-list">
<ul class="exam-list exam-list-active">
{% for exam in exams %}
{% if exam.active %}
<li class="exam-item">
<a href="{% url app_name|add:':exam_overview' pk=exam.pk %}" class="flex-col-2">{{exam.name}}</a>
<a href="{% url app_name|add:':exam_overview' pk=exam.pk %}" class="flex-col-2 exam-name">{{exam.name}}</a>
{% if marking %}<a href="{% url app_name|add:':mark_overview' pk=exam.pk %}" class="flex-col">Mark</a>{% endif %}
<a href="{% url app_name|add:':exam_cids' exam_id=exam.pk %}" class="flex-col">Candidates</a>
<a href="{% url app_name|add:':exam_scores_cid' pk=exam.pk %}" class="flex-col">Scores</a>
@@ -22,11 +22,11 @@
</ul>
Inactive exams:<br/>
<ul class="exam-list">
<ul class="exam-list exam-list-inactive">
{% for exam in exams %}
{% if not exam.active %}
<li class="exam-item">
<a href="{% url app_name|add:':exam_overview' pk=exam.pk %}" class="flex-col-2">{{exam.name}}</a>
<a href="{% url app_name|add:':exam_overview' pk=exam.pk %}" class="flex-col-2 exam-name">{{exam.name}}</a>
{% if marking %}<a href="{% url app_name|add:':mark_overview' pk=exam.pk %}" class="flex-col">Mark</a>{% endif %}
<a href="{% url app_name|add:':exam_cids' exam_id=exam.pk %}" class="flex-col">Candidates</a>
<a href="{% url app_name|add:':exam_scores_cid' pk=exam.pk %}" class="flex-col">Scores</a>