start testing

This commit is contained in:
Ross
2022-05-21 11:20:21 +01:00
parent 0ca980ccb6
commit 160bb13b64
18 changed files with 203 additions and 61 deletions
+1 -1
View File
@@ -51,7 +51,7 @@ class AnatomyUserAnswerFilter(django_filters.FilterSet):
model= CidUserAnswer model= CidUserAnswer
fields = ( fields = (
"cid", "cid",
#"user", "user",
"question", "question",
"answer", "answer",
#"answer_compare", #"answer_compare",
@@ -0,0 +1,21 @@
# Generated by Django 3.2.13 on 2022-05-20 19:51
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('anatomy', '0062_exam_valid_user_users'),
]
operations = [
migrations.AddField(
model_name='ciduseranswer',
name='user',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='user_anatomy_user_answers', to=settings.AUTH_USER_MODEL),
),
]
@@ -0,0 +1,24 @@
# Generated by Django 3.2.13 on 2022-05-21 09:20
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('generic', '0032_userprofile_registration_number'),
('anatomy', '0063_ciduseranswer_user'),
]
operations = [
migrations.AddField(
model_name='exam',
name='cid_user_groups',
field=models.ManyToManyField(blank=True, help_text='These groups define which candidates are able to be added to the exams/collection.', related_name='anatomy_cid_user_groups', to='generic.CidUserGroup'),
),
migrations.AddField(
model_name='exam',
name='user_user_groups',
field=models.ManyToManyField(blank=True, help_text='These groups define which candidates are able to be added to the exams/collection.', related_name='anatomy_user_user_groups', to='generic.UserUserGroup'),
),
]
+18 -4
View File
@@ -15,7 +15,7 @@ from sortedm2m.fields import SortedManyToManyField
import string import string
from generic.models import CidUser, Examination, ExamBase, QuestionNote from generic.models import CidUser, CidUserGroup, Examination, ExamBase, QuestionNote, UserUserGroup
from collections import defaultdict from collections import defaultdict
from helpers.images import image_as_base64 from helpers.images import image_as_base64
@@ -391,6 +391,20 @@ class Exam(ExamBase):
settings.AUTH_USER_MODEL, blank=True, related_name="user_anatomy_exams" settings.AUTH_USER_MODEL, blank=True, related_name="user_anatomy_exams"
) )
cid_user_groups = models.ManyToManyField(
CidUserGroup,
blank=True,
help_text="These groups define which candidates are able to be added to the exams/collection.",
related_name="anatomy_cid_user_groups"
)
user_user_groups = models.ManyToManyField(
UserUserGroup,
blank=True,
help_text="These groups define which candidates are able to be added to the exams/collection.",
related_name="anatomy_user_user_groups"
)
def get_exam_json(self, based=True): def get_exam_json(self, based=True):
questions = self.exam_questions.all() questions = self.exam_questions.all()
@@ -452,9 +466,9 @@ class CidUserAnswer(models.Model):
help_text="Candidate ID (limitied by BigIntegerField size)", help_text="Candidate ID (limitied by BigIntegerField size)",
) )
#user = models.ForeignKey( user = models.ForeignKey(
# settings.AUTH_USER_MODEL, on_delete=models.CASCADE, blank=True, null=True#, related_name="user_anatomy_user_answers" settings.AUTH_USER_MODEL, on_delete=models.CASCADE, blank=True, null=True, related_name="user_anatomy_user_answers"
#) )
# Each user answer is associated with a particular exam # Each user answer is associated with a particular exam
exam = models.ForeignKey( exam = models.ForeignKey(
+1 -1
View File
@@ -69,7 +69,7 @@ class AnatomyUserAnswerTable(tables.Table):
template_name = "django_tables2/bootstrap4.html" template_name = "django_tables2/bootstrap4.html"
fields = ( fields = (
"cid", "cid",
#"user", "user",
"question", "question",
"answer", "answer",
#"answer_compare", #"answer_compare",
-5
View File
@@ -250,11 +250,6 @@ def loadJsonAnswer(answer):
{"success": False, "error": "invalid cid / passcode"} {"success": False, "error": "invalid cid / passcode"}
) )
# if not exam.active:
# return False, JsonResponse(
# {"success": False, "error": "No active exam: {}".format(eid)}
# )
exiting_answers = CidUserAnswer.objects.filter( exiting_answers = CidUserAnswer.objects.filter(
question__id=answer["qid"], exam__id=eid, cid=cid question__id=answer["qid"], exam__id=eid, cid=cid
) )
+32 -1
View File
@@ -1039,7 +1039,38 @@ class ExamViews(View, LoginRequiredMixin):
n = 0 n = 0
for answer in json.loads(request.POST.get("answers")): for answer in json.loads(request.POST.get("answers")):
ret_status, ret = self.loadJsonAnswer(answer, user=request.user)
eid = int(answer["eid"].split("/")[1])
uid = False
try:
cid = int(answer["cid"])
except ValueError:
if answer["cid"].startswith("u-"):
cid = False
uid = int(answer["cid"][2:])
if not uid == request.user.id:
return JsonResponse(
{"success": False, "error": "user / uid mismatch"}
)
else:
return JsonResponse(
{"success": False, "error": "cid or eid or answers not defined"}
)
if not uid:
# As access is not restricted make sure the data appears valid
if (not isinstance(cid, int)) or (
not isinstance(eid, int) or (not isinstance(answer["ans"], str))
):
return JsonResponse(
{"success": False, "error": "cid or eid or answers not defined"}
)
ret_status, ret = self.loadJsonAnswer(answer, cid=cid, eid=eid, uid=uid)
# try: # try:
# ret_status, ret = self.loadJsonAnswer(answer) # ret_status, ret = self.loadJsonAnswer(answer)
# except: # except:
+3
View File
@@ -0,0 +1,3 @@
[pytest]
DJANGO_SETTINGS_MODULE=rad.settings
python_files = tests.py test_*.py *_tests.py
@@ -0,0 +1,21 @@
# Generated by Django 3.2.13 on 2022-05-20 19:51
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('rapids', '0056_exam_user_user_groups'),
]
operations = [
migrations.AlterField(
model_name='ciduseranswer',
name='user',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='user_rapid_user_answers', to=settings.AUTH_USER_MODEL),
),
]
@@ -0,0 +1,24 @@
# Generated by Django 3.2.13 on 2022-05-21 09:20
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('generic', '0032_userprofile_registration_number'),
('rapids', '0057_alter_ciduseranswer_user'),
]
operations = [
migrations.AlterField(
model_name='exam',
name='cid_user_groups',
field=models.ManyToManyField(blank=True, help_text='These groups define which candidates are able to be added to the exams/collection.', related_name='rapid_cid_user_groups', to='generic.CidUserGroup'),
),
migrations.AlterField(
model_name='exam',
name='user_user_groups',
field=models.ManyToManyField(blank=True, help_text='These groups define which candidates are able to be added to the exams/collection.', related_name='rapid_user_user_groups', to='generic.UserUserGroup'),
),
]
+7 -2
View File
@@ -1,4 +1,5 @@
import os import os
import re
import dicognito import dicognito
from rad.settings import REMOTE_URL from rad.settings import REMOTE_URL
from django.db import models from django.db import models
@@ -597,7 +598,7 @@ class Exam(ExamBase):
time_limit = models.IntegerField( time_limit = models.IntegerField(
help_text="Exam time limit (in seconds). Default is 2100 secondse (35 minutes)", help_text="Exam time limit (in seconds). Default is 2100 secondse (35 minutes)",
default=2100, default=35 * 60,
) )
author = models.ManyToManyField( author = models.ManyToManyField(
@@ -619,12 +620,15 @@ class Exam(ExamBase):
CidUserGroup, CidUserGroup,
blank=True, blank=True,
help_text="These groups define which candidates are able to be added to the exams/collection.", help_text="These groups define which candidates are able to be added to the exams/collection.",
related_name="rapid_cid_user_groups"
) )
user_user_groups = models.ManyToManyField( user_user_groups = models.ManyToManyField(
UserUserGroup, UserUserGroup,
blank=True, blank=True,
help_text="These groups define which candidates are able to be added to the exams/collection.", help_text="These groups define which candidates are able to be added to the exams/collection.",
related_name="rapid_user_user_groups"
) )
def get_normal_abnormal_breakdown(self): def get_normal_abnormal_breakdown(self):
@@ -740,7 +744,8 @@ class CidUserAnswer(models.Model):
settings.AUTH_USER_MODEL, settings.AUTH_USER_MODEL,
on_delete=models.CASCADE, on_delete=models.CASCADE,
blank=True, blank=True,
null=True, # , related_name="user_rapid_user_answers" null=True,
related_name="user_rapid_user_answers",
) )
# Each user answer is associated with a particular exam # Each user answer is associated with a particular exam
-3
View File
@@ -1,3 +0,0 @@
from django.test import TestCase
# Create your tests here.
+20
View File
@@ -0,0 +1,20 @@
import pytest
from rapids.models import Abnormality, Exam
#@pytest.mark.django_db
def test_create_abnormality(db):
abnormality = Abnormality.objects.create(name="Abnorm")
assert abnormality.name == "Abnorm"
@pytest.fixture
def create_exam(db):
return Exam.objects.create(name="test exam")
def test_exam_creation(create_exam):
exams = Exam.objects.filter(name="test exam")
assert exams.exists()
exam = exams.first()
assert exam.time_limit == 35 * 60
+12
View File
@@ -0,0 +1,12 @@
import pytest
#from django.contrib.auth.models import User
from django.urls import reverse
@pytest.fixture
def create_superuser(db, django_user_model):
return django_user_model.objects.create_superuser("admin", "ross@xkjq.uk", "adminpassword")
def test_index(client, create_superuser):
client.login(username="admin", password="adminpassword")
response = client.get(reverse('rapids:index'))
assert response.status_code == 200
+16
View File
@@ -0,0 +1,16 @@
from django.test import TestCase, Client
from django.urls import reverse
from django.contrib.auth.models import User
# Create your tests here.
#class TestUrls(TestCase):
# def setUp(self):
# User.objects.create_superuser("admin", "ross@xkjq.uk", "adminpassword")
#
# def test_index(self):
# self.client.login(username="admin", password="adminpassword")
# response = self.client.get(reverse('rapids:index'))
# self.assertEqual(response.status_code, 200)
+1 -43
View File
@@ -509,44 +509,7 @@ class RapidView(LoginRequiredMixin, SingleTableMixin, FilterView):
filterset_class = RapidFilter filterset_class = RapidFilter
def loadJsonAnswer(answer, user=None): def loadJsonAnswer(answer, cid, eid, uid):
eid = int(answer["eid"].split("/")[1])
uid = False
try:
cid = int(answer["cid"])
except ValueError:
if answer["cid"].startswith("u-"):
cid = False
uid = int(answer["cid"][2:])
print(f"{uid=}")
print(f"{uid=}")
if not uid == user.id:
return False, JsonResponse(
{"success": False, "error": "user / uid mismatch"}
)
else:
return False, JsonResponse(
{"success": False, "error": "cid or eid or answers not defined"}
)
#uid = int(answer["uid"])
if not uid:
# As access is not restricted make sure the data appears valid
if (not isinstance(cid, int)) or (
not isinstance(eid, int) or (not isinstance(answer["ans"], str))
):
return False, JsonResponse(
{"success": False, "error": "cid or eid or answers not defined"}
)
## The model should catch invalid data but this should be less intensive
#max_int = 999999999999999999999
#if cid > max_int or eid > max_int:
# return False, JsonResponse({"success": False, "error": "invalid cid"})
exam = get_object_or_404(Exam, pk=eid) exam = get_object_or_404(Exam, pk=eid)
@@ -555,11 +518,6 @@ def loadJsonAnswer(answer, user=None):
{"success": False, "error": "invalid cid / passcode"} {"success": False, "error": "invalid cid / passcode"}
) )
# if not exam.active:
# return False, JsonResponse(
# {"success": False, "error": "No active exam: {}".format(eid)}
# )
if uid: if uid:
exiting_answers = CidUserAnswer.objects.filter( exiting_answers = CidUserAnswer.objects.filter(
question__id=answer["qid"], exam__id=eid, user__id=uid question__id=answer["qid"], exam__id=eid, user__id=uid
+1
View File
@@ -25,4 +25,5 @@ django-autocomplete-light
django-querysetsequence django-querysetsequence
dicognito dicognito
django_unused_media django_unused_media
pytest-django
gunicorn gunicorn
+1 -1
View File
@@ -15,7 +15,7 @@
</div> </div>
<div> <div>
Supervisor: {{ user.userprofile.supervisor_name }}<br/> Supervisor: {{ user.userprofile.supervisor_name }}<br/>
Email: {{ user.userprofile.supervisor_email }}<br/> Supervisor Email: {{ user.userprofile.supervisor_email }}<br/>
</div> </div>
<div> <div>
Registration number: {{ user.userprofile.registration_number }} Registration number: {{ user.userprofile.registration_number }}