shorts might now be functional?

This commit is contained in:
Ross
2025-04-28 12:15:31 +01:00
parent 405b17776f
commit 06e66ad52c
18 changed files with 901 additions and 234 deletions
+101 -9
View File
@@ -30,6 +30,7 @@ from django.utils.html import mark_safe
from rapids.models import Abnormality, Examination, Region
from helpers.images import get_image_hash, image_as_base64
from django.utils.html import format_html
def image_directory_path(instance, filename):
@@ -63,6 +64,12 @@ class Question(QuestionBase):
related_name="shorts_authored_questions",
)
def __str__(self):
return "{}: {}".format(
self.id,
self.history,
)
def get_app_name(self):
return "shorts"
@@ -116,6 +123,52 @@ class Question(QuestionBase):
def get_best_sample_answer(self):
return self.sample_answers.filter(score__gt=0).order_by("-score").first()
def get_exams(self):
e = self.exams.all().values_list("name", flat=True)
exams = ", ".join(e)
return exams
def get_unmarked_user_answer_string(self, exam_pk: int = None):
unmarked_answers = self.get_unmarked_user_answers(exam_pk)
if not unmarked_answers:
return "No answers to mark"
return format_html(
"<span class='warn'>{} answer unmarked:</span> {}".format(
len(unmarked_answers), ", ".join(unmarked_answers)
)
)
def get_unmarked_user_answers(self, exam_pk: int | None = None, marker=None):
if exam_pk is None:
user_answer_queryset = self.cid_user_answers.all()
else:
user_answer_queryset = self.cid_user_answers.filter(exam__id=exam_pk)
unmarked_answers = user_answer_queryset.filter(score__isnull=True)
return unmarked_answers
def get_unmarked_user_answer_count(self, exam_pk=None, marker=None):
if exam_pk is None:
return self.cid_user_answers.all().count()
return len(self.get_unmarked_user_answers(exam_pk, marker=marker))
def get_user_answers(self, exam_pk=None, include_normal=True):
if exam_pk is None:
queryset = self.cid_user_answers.all()
else:
queryset = self.cid_user_answers.filter(exam__id=exam_pk)
if not include_normal:
queryset = queryset.filter(normal=False)
user_answers = set([i.answer for i in queryset])
return user_answers
class QuestionImage(models.Model):
@@ -230,6 +283,10 @@ class Exam(ExamBase):
related_name="shorts_exam_markers",
)
double_mark = models.BooleanField(
default=False, help_text="Defines if an exam is expected to be double marked"
)
valid_cid_users = models.ManyToManyField(
CidUser, blank=True, related_name="shorts_exams"
)
@@ -271,20 +328,16 @@ class Exam(ExamBase):
return "shorts"
def get_exam_json(self, based=True):
# TODO
return {}
questions = self.get_questions()
exam_questions = defaultdict(dict)
exam_order = []
q: Rapid
q: Question
for q in questions:
exam_order.append(q.id)
# Loop through rapidimage associations
images = []
feedback_images = []
image_titles = []
@@ -311,7 +364,7 @@ class Exam(ExamBase):
"images": images,
# "feedback_image": [],
# "annotations": [str(q.image_annotations)],
"type": "rapid",
"type": "short",
}
if not self.exam_mode:
@@ -331,9 +384,9 @@ class Exam(ExamBase):
# exam_questions[q.id]["feedback_image"] = feedback_images
exam_json = {
"eid": "rapid/{}".format(self.id),
"eid": "short/{}".format(self.id),
"cached": False,
"exam_type": "rapid",
"exam_type": "short",
"exam_name": self.name,
"exam_mode": self.exam_mode,
"exam_order": exam_order,
@@ -343,6 +396,7 @@ class Exam(ExamBase):
if self.time_limit:
exam_json["exam_time"] = self.time_limit
print(exam_json)
return exam_json
def get_absolute_url(self):
@@ -373,8 +427,13 @@ class UserAnswer(UserAnswerBase):
Exam, related_name="cid_user_answers", on_delete=models.CASCADE, null=True
)
# If score is null then the answer is unmarked
score = models.IntegerField(
default=0, blank=True, validators=[MinValueValidator(0), MaxValueValidator(5)]
default=0, blank=True, validators=[MinValueValidator(0), MaxValueValidator(5)], null=True
)
candidate_feedback = models.TextField(
null=True, blank=True, help_text="Feedback for the candidate"
)
class CallStateOptions(models.TextChoices):
@@ -426,6 +485,15 @@ class UserAnswer(UserAnswerBase):
def get_answer_score(self, cached=True):
return self.score
def get_answer_string(self):
return self.answer
def is_marked(self):
if self.score is None:
return False
else:
return True
class QuestionFinding(FindingBase):
question = models.ForeignKey(
@@ -439,3 +507,27 @@ class QuestionFinding(FindingBase):
def __str__(self) -> str:
findings = self.findings.all().values_list("name")
return f"Findings: {self.question.id}/{findings}/{self.description}"
class AnswerMarks(models.Model):
# Same as in UserAnswer except null is not allowed
score = models.IntegerField(
default=0, blank=True, validators=[MinValueValidator(0), MaxValueValidator(5)]
)
mark_reason = models.TextField(
null=True,
blank=True,
help_text="Reason for the given mark - not visible to candidates",
)
marker = models.ForeignKey(
settings.AUTH_USER_MODEL, related_name="shorts_answers", on_delete=models.CASCADE
)
user_answer = models.ForeignKey(
UserAnswer,
related_name="mark",
on_delete=models.SET_NULL,
null=True,
blank=True,
)