This commit is contained in:
Ross
2022-05-31 23:40:50 +01:00
parent 4969723de9
commit 6d3cdedb95
8 changed files with 228 additions and 60 deletions
+1 -1
View File
@@ -10,7 +10,7 @@
<input type="submit" />
</form>
</details>
<h4>{{filter.qs|length}} exams found.</h4>
<h4 class="exam-number-title">{{filter.qs|length}} exams found.</h4>
{% for exam in filter.qs %}
<div class="">
<h1><a href="{% url app_name|add:':exam_overview' pk=exam.pk %}">Exam: {{ exam.name }} </a></h1>
+37 -4
View File
@@ -318,14 +318,15 @@ class ExamViews(View, LoginRequiredMixin):
# },
# },
# }
def __init__(self, data=None, *args, **kwargs):
# if filterset is bound, use initial values as defaults
if data is not None:
# get a mutable copy of the QueryDict
data = data.copy()
#for name, f in self.base_filters.items():
# filter param is either missing or empty, use initial as default
# for name, f in self.base_filters.items():
# filter param is either missing or empty, use initial as default
if not data.get("archive"):
data["archive"] = False
@@ -796,6 +797,8 @@ class ExamViews(View, LoginRequiredMixin):
app_exam_map = {}
app_exam_map["rapids"] = cid_user.rapid_exams
app_exam_map["anatomy"] = cid_user.anatomy_exams
app_exam_map["longs"] = cid_user.long_exams
app_exam_map["atlas"] = cid_user.casecollection_exams
if add:
@@ -815,6 +818,8 @@ class ExamViews(View, LoginRequiredMixin):
app_exam_map = {}
app_exam_map["rapids"] = user_user.user_rapid_exams
app_exam_map["anatomy"] = user_user.user_anatomy_exams
app_exam_map["longs"] = user_user.user_longs_exams
# app_exam_map["atlas"] = user_user.casecollection_exams
if add:
@@ -967,7 +972,8 @@ class ExamViews(View, LoginRequiredMixin):
# self.check_user_access(request.user, exam.pk)
if (
(
request.user.is_superuser
or (
self.app_name == "rapids"
and request.user.groups.filter(name="rapid_checker").exists()
)
@@ -986,7 +992,11 @@ class ExamViews(View, LoginRequiredMixin):
filter = self.ExamFilter(request.GET, queryset=exams)
return render(request, "generic/exam_index.html", {"filter": filter, "app_name": self.app_name})
return render(
request,
"generic/exam_index.html",
{"filter": filter, "app_name": self.app_name},
)
@method_decorator(login_required)
def exam_question_detail(self, request, pk, sk):
@@ -2064,3 +2074,26 @@ class UserUserGroupDelete(RevisionMixin, CidManagerRequiredMixin, DeleteView):
model = UserUserGroup
template_name = "confirm_delete.html"
success_url = reverse_lazy("generic:user_group_view")
class ExamCreateBase(RevisionMixin, LoginRequiredMixin, CreateView):
template_name = "exam_create_form.html"
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 ExamUpdateBase(RevisionMixin, LoginRequiredMixin, UpdateView):
template_name = "exam_update_form.html"
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)
+9 -9
View File
@@ -1,4 +1,4 @@
{% extends "rapids/exams.html" %}
{% extends "rapids/base.html" %}
{% block js %}
{{ form.media }}
@@ -6,12 +6,12 @@
{% block content %}
<h2>Add Exam</h2>
<form action="" method="post">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<input type="submit" value="Submit">
</form>
<h2>Add Exam</h2>
<form action="" method="post">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<input type="submit" value="Submit">
</form>
{% endblock %}
+109 -11
View File
@@ -1,24 +1,122 @@
import json
import pytest
#from django.contrib.auth.models import User
# from django.contrib.auth.models import User
from django.urls import reverse
from rich.pretty import pprint
from bs4 import BeautifulSoup
from rapids.views import GenericExamViews as RapidExamViews
from sbas.views import GenericExamViews as SbaExamViews
from longs.views import GenericExamViews as LongExamViews
from physics.views import GenericExamViews as PhysicExamViews
from anatomy.views import GenericExamViews as AnatomyExamViews
APP_NAMES = ("rapids", "anatomy", "longs", "physics", "sbas")
JSON_APPS = ("rapids", "anatomy", "longs")
EXAM_VIEWS = (
RapidExamViews,
SbaExamViews,
LongExamViews,
PhysicExamViews,
AnatomyExamViews,
)
@pytest.fixture
def create_superuser(db, django_user_model):
return django_user_model.objects.create_superuser("admin", "ross@xkjq.uk", "adminpassword")
return django_user_model.objects.create_superuser(
"admin", "ross@xkjq.uk", "adminpassword"
)
#@pytest.fixture
#def create_exam(db):
# @pytest.fixture
# def create_exam(db):
# return Exam.objects.create(name="test exam", exam_mode=True)
# Very basic tests, make sure we can hit key urls without error
@pytest.mark.parametrize("app_name", APP_NAMES)
def test_index(client, create_superuser, app_name):
@pytest.mark.parametrize("exam_views", EXAM_VIEWS)
def test_index(client, create_superuser, exam_views, django_user_model):
basic_user = django_user_model.objects.create_user(
"user1", "user@user.net", "pass2"
)
e1 = exam_views.Exam.objects.create(name="test exam", exam_mode=True)
e = exam_views.Exam.objects.create(name="test exam 2", exam_mode=True)
e.author.add(basic_user)
client.login(username="admin", password="adminpassword")
response = client.get(reverse(f'{app_name}:index'))
print(reverse(f'{app_name}:index'))
print(response.status_code)
print(response.content)
assert response.status_code == 200
response = client.get(reverse(f"{exam_views.app_name}:index"))
assert response.status_code == 200
assert (
BeautifulSoup(response.content, "html.parser")
.find("h4", {"class": "exam-number-title"})
.getText()
== "2 exams found."
)
if exam_views.app_name in JSON_APPS:
response = client.get(reverse(f"{exam_views.app_name}:active_exams"))
json_response = json.loads(response.content)["exams"]
assert len(json_response) == 2
for json_exam in json_response:
eid_app_type, eid = json_exam["eid"].split("/")
# hack for long / longs mismathc
assert (
eid_app_type == exam_views.app_name
or eid_app_type == exam_views.app_name[:-1]
)
assert exam_views.Exam.objects.get(pk=int(eid)).name == json_exam["name"]
add_user_response = client.post(
reverse(f"{exam_views.app_name}:exam_json_edit", args=(e1.pk,)),
{"edit_user_user": basic_user.pk, "add": "true"},
HTTP_X_REQUESTED_WITH="XMLHttpRequest",
)
print(f"{e1.valid_user_users.all()=}")
assert json.loads(add_user_response.content)["status"] == "success"
client.logout()
client.force_login(basic_user)
response = client.get(reverse(f"{exam_views.app_name}:index"))
assert response.status_code == 200
assert (
BeautifulSoup(response.content, "html.parser")
.find("h4", {"class": "exam-number-title"})
.getText()
== "1 exams found."
)
if exam_views.app_name in JSON_APPS:
response = client.get(reverse(f"{exam_views.app_name}:active_exams"))
json_response = json.loads(response.content)["exams"]
assert len(json_response) == 1
e1.active = True
e1.save()
response = client.get(reverse(f"{exam_views.app_name}:active_exams"))
json_response = json.loads(response.content)["exams"]
print(json_response)
assert len(json_response) == 2
+31 -34
View File
@@ -42,7 +42,14 @@ from .models import (
CidUserAnswer,
)
from generic.views import AuthorRequiredMixin, ExamCloneMixin, ExamViews, GenericViewBase
from generic.views import (
AuthorRequiredMixin,
ExamCloneMixin,
ExamCreateBase,
ExamUpdateBase,
ExamViews,
GenericViewBase,
)
from reversion.views import RevisionMixin
@@ -350,8 +357,8 @@ class RapidUpdate(
def form_valid(self, form):
# save exam orders (there must be a better way to do this)
#exam_orders = {}
#for exam in self.object.exams.all():
# exam_orders = {}
# for exam in self.object.exams.all():
# exam_orders[exam] = list(exam.exam_questions.all())
self.object = form.save(commit=False)
@@ -371,8 +378,8 @@ class RapidUpdate(
answer_formset.save()
# restore exam orders
#wanted_exams = form["exams"].data
#for exam in self.object.exams.all():
# wanted_exams = form["exams"].data
# for exam in self.object.exams.all():
# if exam in exam_orders and self.object in exam_orders[exam]:
# print(exam_orders[exam])
# exam.exam_questions.set(exam_orders[exam])
@@ -677,7 +684,6 @@ def mark(request, exam_pk, sk, unmarked_exam_answers_only=True, review=False):
)
def exam_scores_cid_user(request, pk, cid, passcode):
exam = get_object_or_404(Exam, pk=pk)
@@ -686,13 +692,15 @@ def exam_scores_cid_user(request, pk, cid, passcode):
# TODO:Need some kind of test for cid
questions = exam.exam_questions.all()#.prefetch_related("cid_user_answers", "answers")
questions = (
exam.exam_questions.all()
) # .prefetch_related("cid_user_answers", "answers")
#cid_user_answers = list(CidUserAnswer.objects.filter(cid=cid, exam__id=pk).prefetch_related("question"))
# cid_user_answers = list(CidUserAnswer.objects.filter(cid=cid, exam__id=pk).prefetch_related("question"))
#cid_user_answers_q_map = {}
# cid_user_answers_q_map = {}
#for ans in cid_user_answers:
# for ans in cid_user_answers:
# cid_user_answers_q_map[ans.question] = ans
answers_and_marks = []
@@ -706,7 +714,7 @@ def exam_scores_cid_user(request, pk, cid, passcode):
for q in questions:
# Get user answer
user_answer = q.cid_user_answers.filter(cid=cid, exam__id=pk).first()
#user_answer = cid_user_answers_q_map[q]
# user_answer = cid_user_answers_q_map[q]
if not user_answer or user_answer is None:
# skip if no answer
@@ -766,49 +774,36 @@ class QuestionDelete(AuthorOrCheckerRequiredMixin, DeleteView):
success_url = reverse_lazy("rapids:rapid_view")
GenericExamViews = ExamViews(
Exam, Rapid, Answer, CidUserAnswer, "rapids", "rapid"
)
GenericExamViews = ExamViews(Exam, Rapid, Answer, CidUserAnswer, "rapids", "rapid")
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 ExamUpdate(
RevisionMixin, LoginRequiredMixin, AuthorOrCheckerRequiredMixin, UpdateView
class ExamAuthorUpdate(
RevisionMixin, LoginRequiredMixin, AuthorRequiredMixin, 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("rapids:index")
class UserAnswerView(LoginRequiredMixin, DetailView):
model = CidUserAnswer
class UserAnswerTableView(LoginRequiredMixin, SingleTableMixin, FilterView):
model = CidUserAnswer
table_class = RapidUserAnswerTable
@@ -925,6 +920,7 @@ class RapidLateralityViewSet(
GenericViews = GenericViewBase("rapids", Rapid, CidUserAnswer, Exam)
@user_is_author_or_rapid_checker
def question_anonymise_dicom(request, pk):
question = get_object_or_404(Rapid, pk=pk)
@@ -933,6 +929,7 @@ def question_anonymise_dicom(request, pk):
return redirect("rapids:question_detail", pk=pk)
@user_is_author_or_rapid_checker
def question_save_annotation(request, pk):
if request.is_ajax() and request.method == "POST":
+3 -1
View File
@@ -26,4 +26,6 @@ django-querysetsequence
dicognito
django_unused_media
pytest-django
gunicorn
gunicorn
rich
bs4
+20
View File
@@ -0,0 +1,20 @@
{% extends "base.html" %}
{% comment %} {% extends exam.app_name|add:"/base.html" %} {% endcomment %}
{% block js %}
{{ form.media }}
{% endblock %}
{% block content %}
{{form.instance}}
<h2>Add Exam</h2>
<form action="" method="post">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<input type="submit" value="Submit">
</form>
{% endblock %}
+18
View File
@@ -0,0 +1,18 @@
{% extends exam.app_name|add:"/exams.html" %}
{% block js %}
{{ form.media }}
{% endblock %}
{% block content %}
<h2>Update Exam</h2>
<form action="" method="post">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<input type="submit" value="Submit">
</form>
{% endblock %}