numerous updates

This commit is contained in:
Ross
2022-05-20 20:26:02 +01:00
parent 0373b22e78
commit 77ccdbc92a
246 changed files with 22615 additions and 2409 deletions
+1
View File
@@ -46,6 +46,7 @@ class RapidUserAnswerFilter(django_filters.FilterSet):
model = CidUserAnswer
fields = (
"cid",
"user",
"normal",
"answer",
#"answer_compare",
+1
View File
@@ -243,6 +243,7 @@ class ExamForm(ExamFormMixin, ModelForm):
"publish_results",
"archive",
"cid_user_groups",
"user_user_groups",
#"author",
]
@@ -0,0 +1,18 @@
# Generated by Django 3.2.13 on 2022-05-20 10:45
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('rapids', '0053_ciduseranswer_user'),
]
operations = [
migrations.RenameField(
model_name='exam',
old_name='valid_users',
new_name='valid_cid_users',
),
]
@@ -0,0 +1,20 @@
# Generated by Django 3.2.13 on 2022-05-20 10:59
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('rapids', '0054_rename_valid_users_exam_valid_cid_users'),
]
operations = [
migrations.AddField(
model_name='exam',
name='valid_user_users',
field=models.ManyToManyField(blank=True, related_name='user_rapid_exams', to=settings.AUTH_USER_MODEL),
),
]
@@ -0,0 +1,19 @@
# Generated by Django 3.2.13 on 2022-05-20 17:44
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('generic', '0029_userusergroup'),
('rapids', '0055_exam_valid_user_users'),
]
operations = [
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.', to='generic.UserUserGroup'),
),
]
+48 -17
View File
@@ -26,7 +26,16 @@ from helpers.images import image_as_base64
from django.contrib.contenttypes.fields import GenericRelation
from generic.models import CidUser, CidUserGroup, Site, Condition, Sign, ExamBase, QuestionNote
from generic.models import (
CidUser,
CidUserGroup,
Site,
Condition,
Sign,
ExamBase,
QuestionNote,
UserUserGroup,
)
import reversion
@@ -245,7 +254,10 @@ class Rapid(models.Model):
if self.normal:
return "Normal"
else:
if hasattr(self, "prefetched_primary_answer") and self.prefetched_primary_answer:
if (
hasattr(self, "prefetched_primary_answer")
and self.prefetched_primary_answer
):
ans = self.prefetched_primary_answer[0]
else:
ans = self.answers.filter(
@@ -257,7 +269,7 @@ class Rapid(models.Model):
else:
return ans.answer
#def get_primary_answer(self):
# def get_primary_answer(self):
# if self.normal:
# return "Normal"
# elif (
@@ -302,7 +314,6 @@ class Rapid(models.Model):
marked_answers = self.get_marked_answers()
unmarked_answers = set(
[
i
@@ -449,7 +460,7 @@ class Rapid(models.Model):
feedback_images.append(
"{}/{}".format(settings.REMOTE_URL, i.image.url)
)
#feedback_images.append("{}/{}".format(settings.REMOTE_URL, i.image.url))
# feedback_images.append("{}/{}".format(settings.REMOTE_URL, i.image.url))
else:
if based:
images.append(image_as_base64(i.image))
@@ -471,7 +482,6 @@ class Rapid(models.Model):
if feedback:
json["feedback"] = self.feedback
return json
def anonymise_images(self):
@@ -487,6 +497,7 @@ class Rapid(models.Model):
except pydicom.errors.InvalidDicomError:
pass
# reversion.register(Rapid, follow=["images"])
# @reversion.register
class RapidImage(models.Model):
@@ -501,7 +512,12 @@ class RapidImage(models.Model):
feedback_image = models.BooleanField(default=False)
filename = models.CharField(max_length=255, null=True, blank=True, help_text="Name of the original file when uploaded")
filename = models.CharField(
max_length=255,
null=True,
blank=True,
help_text="Name of the original file when uploaded",
)
description = models.CharField(max_length=255, null=True, blank=True)
@@ -591,11 +607,25 @@ class Exam(ExamBase):
related_name="rapid_exam_author",
)
valid_users = models.ManyToManyField(
valid_cid_users = models.ManyToManyField(
CidUser, blank=True, related_name="rapid_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.")
valid_user_users = models.ManyToManyField(
settings.AUTH_USER_MODEL, blank=True, related_name="user_rapid_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.",
)
user_user_groups = models.ManyToManyField(
UserUserGroup,
blank=True,
help_text="These groups define which candidates are able to be added to the exams/collection.",
)
def get_normal_abnormal_breakdown(self):
# Inefficient but more extendible
@@ -638,7 +668,7 @@ class Exam(ExamBase):
images.append(image_as_base64(i.image))
else:
images.append("{}/{}".format(settings.REMOTE_URL, i.image.url))
if i.description:
image_titles.append(i.description)
else:
@@ -664,8 +694,6 @@ class Exam(ExamBase):
if any(image_titles):
exam_questions[q.id]["image_titles"] = image_titles
# if feedback_images:
# exam_questions[q.id]["feedback_image"] = feedback_images
@@ -709,10 +737,12 @@ class CidUserAnswer(models.Model):
)
user = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE, blank=True, null=True
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
blank=True,
null=True, # , related_name="user_rapid_user_answers"
)
# Each user answer is associated with a particular exam
exam = models.ForeignKey(
Exam, related_name="cid_user_answers", on_delete=models.CASCADE, null=True
@@ -752,7 +782,9 @@ class CidUserAnswer(models.Model):
self.answer = self.answer.strip()
s = self.answer.lower()
s = s.translate(str.maketrans("", "", string.punctuation.replace("#", ""))).strip()
s = s.translate(
str.maketrans("", "", string.punctuation.replace("#", ""))
).strip()
self.answer_compare = s
else:
@@ -805,7 +837,6 @@ class CidUserAnswer(models.Model):
self.save()
return 0
# TODO: this should be cleaned up as we don't want duplicates...
# try:
# marked_ans = q.answers.get(answer_compare__iexact=ans)
@@ -825,4 +856,4 @@ class CidUserAnswer(models.Model):
elif marked_ans.status == Answer.MarkOptions.INCORRECT:
mark = 0
return mark
return mark
+1 -1
View File
@@ -24,7 +24,7 @@ class UserAnswerSerializer(serializers.HyperlinkedModelSerializer):
)
class Meta:
model = CidUserAnswer
fields = ('id', 'question', 'normal', "answer", "answer_compare", "cid", "exam", "created", "updated")
fields = ('id', 'question', 'normal', "answer", "answer_compare", "cid", "user", "exam", "created", "updated")
class RapidSerializer(serializers.ModelSerializer):
class Meta:
+1
View File
@@ -80,6 +80,7 @@ class RapidUserAnswerTable(tables.Table):
template_name = "django_tables2/bootstrap4.html"
fields = (
"cid",
"user",
"question",
"normal",
"answer",
@@ -3,6 +3,7 @@
{% block content %}
CID: {{ciduseranswer.cid}}<br/>
User: {{ciduseranswer.user}}<br/>
Exam: {{ciduseranswer.exam}}<br/>
{{ciduseranswer.question}}<br/>
Normal: {{ciduseranswer.normal}}<br/>
@@ -33,7 +33,11 @@
</tr>
{% for cid in cids %}
<tr>
{% if cid|slice:":1" == "u" %}
<td>{{cid}}</td>
{% else %}
<td><a href="{% url 'cid_scores_admin' cid %}">{{cid}}</a></td>
{% endif %}
<td>{{user_scores|get_item:cid}}</td>
<td>{{user_scores_normalised|get_item:cid}}</td>
</tr>
+1
View File
@@ -113,6 +113,7 @@ urlpatterns = [
path("exam/<int:exam_id>/clone", views.ExamClone.as_view(), name="exam_clone"),
path("exam/<int:exam_id>/cids", views.GenericExamViews.exam_cids, name="exam_cids"),
path("exam/<int:exam_id>/cids/edit", views.GenericExamViews.exam_cids_edit, name="exam_cids_edit"),
path("exam/<int:exam_id>/users/edit", views.GenericExamViews.exam_users_edit, name="exam_users_edit"),
path("exam/<int:pk>/update", views.ExamUpdate.as_view(), name="exam_update"),
path("exam/<int:pk>/delete", views.ExamDelete.as_view(), name="exam_delete"),
path("exam/json/", views.GenericExamViews.active_exams, name="active_exams"),
+49 -18
View File
@@ -509,26 +509,48 @@ class RapidView(LoginRequiredMixin, SingleTableMixin, FilterView):
filterset_class = RapidFilter
def loadJsonAnswer(answer):
def loadJsonAnswer(answer, user=None):
eid = int(answer["eid"].split("/")[1])
cid = int(answer["cid"])
uid = False
# 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"}
)
try:
cid = int(answer["cid"])
except ValueError:
if answer["cid"].startswith("u-"):
cid = False
uid = int(answer["cid"][2:])
# 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"})
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)
if not exam.check_cid_user(cid, answer["passcode"]):
if not exam.check_cid_user(cid, answer["passcode"], user_id=uid):
return False, JsonResponse(
{"success": False, "error": "invalid cid / passcode"}
)
@@ -538,9 +560,14 @@ def loadJsonAnswer(answer):
# {"success": False, "error": "No active exam: {}".format(eid)}
# )
exiting_answers = CidUserAnswer.objects.filter(
question__id=answer["qid"], exam__id=eid, cid=cid
)
if uid:
exiting_answers = CidUserAnswer.objects.filter(
question__id=answer["qid"], exam__id=eid, user__id=uid
)
else:
exiting_answers = CidUserAnswer.objects.filter(
question__id=answer["qid"], exam__id=eid, cid=cid
)
posted_answer = answer["ans"]
@@ -554,7 +581,11 @@ def loadJsonAnswer(answer):
normal = True
if not exiting_answers:
ans = CidUserAnswer(answer=posted_answer, normal=normal, cid=cid)
if uid:
ans = CidUserAnswer(answer=posted_answer, normal=normal, user=User.objects.get(id=uid))
else:
ans = CidUserAnswer(answer=posted_answer, normal=normal, cid=cid)
ans.question_id = answer["qid"]
ans.exam_id = eid