further abstraction
This commit is contained in:
+2
-3
@@ -35,6 +35,7 @@ from generic.models import (
|
|||||||
CidUser,
|
CidUser,
|
||||||
CidUserExam,
|
CidUserExam,
|
||||||
CidUserGroup,
|
CidUserGroup,
|
||||||
|
ExamCollectionGenericBase,
|
||||||
Examination,
|
Examination,
|
||||||
# Condition,
|
# Condition,
|
||||||
ExamBase,
|
ExamBase,
|
||||||
@@ -404,7 +405,7 @@ class Series(SeriesBase):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
class CaseCollection(models.Model):
|
class CaseCollection(ExamCollectionGenericBase):
|
||||||
app_name = "atlas"
|
app_name = "atlas"
|
||||||
|
|
||||||
name = models.CharField(max_length=255, unique=True)
|
name = models.CharField(max_length=255, unique=True)
|
||||||
@@ -458,8 +459,6 @@ class CaseCollection(models.Model):
|
|||||||
# related_name="atlas_authored_cases",
|
# related_name="atlas_authored_cases",
|
||||||
)
|
)
|
||||||
|
|
||||||
cid_users = GenericRelation("generic.CidUserExam")
|
|
||||||
|
|
||||||
valid_cid_users = models.ManyToManyField(
|
valid_cid_users = models.ManyToManyField(
|
||||||
CidUser, blank=True, related_name="casecollection_exams"
|
CidUser, blank=True, related_name="casecollection_exams"
|
||||||
)
|
)
|
||||||
|
|||||||
+123
-112
@@ -310,126 +310,18 @@ class SeriesBase(models.Model):
|
|||||||
except pydicom.errors.InvalidDicomError:
|
except pydicom.errors.InvalidDicomError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class ExamBase(models.Model):
|
class ExamCollectionGenericBase(models.Model):
|
||||||
name = models.CharField(max_length=200, help_text="Name of the exam")
|
"""Holds functions that relate to both case and other exams
|
||||||
# exam_questions = SortedManyToManyField(Long, related_name="exams", blank="true")
|
|
||||||
|
|
||||||
active = models.BooleanField(
|
e.g. user management
|
||||||
help_text="If an exam should be available to take", default=False
|
|
||||||
)
|
|
||||||
|
|
||||||
exam_mode = models.BooleanField(
|
|
||||||
help_text="If an exam should be taken in exam mode (users results will be submited to the server for marking)",
|
|
||||||
default=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
include_history = models.BooleanField(
|
|
||||||
help_text="If an exam should include history when taking",
|
|
||||||
default=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
publish_results = models.BooleanField(
|
|
||||||
help_text="If an exams results should be available", default=False
|
|
||||||
)
|
|
||||||
|
|
||||||
recreate_json = models.BooleanField(
|
|
||||||
help_text="If the json cache needs updating", default=False
|
|
||||||
)
|
|
||||||
|
|
||||||
json_creation_time = models.DateTimeField(blank=True, default=None, null=True)
|
|
||||||
exam_json_id = models.IntegerField(
|
|
||||||
default=1, help_text="auto incrementing field when json recreated"
|
|
||||||
)
|
|
||||||
|
|
||||||
archive = models.BooleanField(
|
|
||||||
help_text="Archived exams will remain on the test system but will not be displayed by default",
|
|
||||||
default=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
open_access = models.BooleanField(
|
|
||||||
help_text="If the exam is freely accessible (to view and edit on the test system)",
|
|
||||||
default=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
authors_only = models.BooleanField(
|
|
||||||
help_text="If true only exam authors will be able to view.",
|
|
||||||
default=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
stats_mean = models.FloatField(default=0)
|
|
||||||
stats_mode = models.CharField(default=0, max_length=25)
|
|
||||||
stats_median = models.FloatField(default=0)
|
|
||||||
|
|
||||||
stats_candidates = models.FloatField(default=0)
|
|
||||||
|
|
||||||
stats_min = models.FloatField(default=0)
|
|
||||||
stats_max = models.FloatField(default=0)
|
|
||||||
|
|
||||||
stats_max_possible = models.FloatField(default=0)
|
|
||||||
|
|
||||||
# stats_graph = models.TextField(default=0)
|
|
||||||
|
|
||||||
user_scores = models.JSONField(default=dict)
|
|
||||||
|
|
||||||
|
"""
|
||||||
# Is this actually used?
|
# Is this actually used?
|
||||||
cid_users = GenericRelation("generic.CidUserExam")
|
cid_users = GenericRelation("generic.CidUserExam")
|
||||||
|
|
||||||
exam_results_emailed = models.DateTimeField(default=None, null=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
def save(self, *args, recreate_json=True, **kwargs):
|
|
||||||
self.recreate_json = recreate_json
|
|
||||||
super().save(*args, **kwargs)
|
|
||||||
|
|
||||||
def get_exam_stats(self, name=True):
|
|
||||||
if self.stats_candidates < int(4):
|
|
||||||
return f"- Candidates: {int(self.stats_candidates)} (too few to generate stats)"
|
|
||||||
|
|
||||||
text = f"""- Candidates: {int(self.stats_candidates)}
|
|
||||||
- Mean: {self.stats_mean:.2f} Median: {self.stats_median} Mode: {self.stats_mode} [Min: {self.stats_min} / Max: {self.stats_max}]
|
|
||||||
"""
|
|
||||||
if name:
|
|
||||||
text = f"""{self.name}
|
|
||||||
{"-"*len(self.name)}
|
|
||||||
{text}
|
|
||||||
"""
|
|
||||||
return text
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
def get_absolute_url(self):
|
|
||||||
return reverse("{}:exam_overview".format(self.app_name), kwargs={"pk": self.pk})
|
|
||||||
|
|
||||||
def get_cid_edit_url(self):
|
|
||||||
return reverse(
|
|
||||||
"{}:exam_cids_edit".format(self.app_name), kwargs={"exam_id": self.pk}
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_user_edit_url(self):
|
|
||||||
return reverse(
|
|
||||||
"{}:exam_users_edit".format(self.app_name), kwargs={"exam_id": self.pk}
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_take_url(self):
|
|
||||||
return f"{settings.REMOTE_URL}/rts"
|
|
||||||
|
|
||||||
def get_exam_name(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
def get_json_url(self, cid=None, passcode=None):
|
|
||||||
if cid is None:
|
|
||||||
return reverse("{}:exam_json".format(self.app_name), args=(self.pk,))
|
|
||||||
else:
|
|
||||||
return reverse(
|
|
||||||
"{}:exam_json_cid".format(self.app_name), args=(self.pk, cid, passcode)
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_question_index(self, question):
|
|
||||||
return list(self.exam_questions.all()).index(question)
|
|
||||||
|
|
||||||
def get_authors(self):
|
def get_authors(self):
|
||||||
"""Returns a comma seperated text list of authors"""
|
"""Returns a comma seperated text list of authors"""
|
||||||
authors = ", ".join([i.username for i in self.author.all()])
|
authors = ", ".join([i.username for i in self.author.all()])
|
||||||
@@ -536,6 +428,125 @@ class ExamBase(models.Model):
|
|||||||
content_type=content_type, object_id=self.pk, user_user=user_user
|
content_type=content_type, object_id=self.pk, user_user=user_user
|
||||||
)
|
)
|
||||||
|
|
||||||
|
class ExamBase(ExamCollectionGenericBase):
|
||||||
|
name = models.CharField(max_length=200, help_text="Name of the exam")
|
||||||
|
# exam_questions = SortedManyToManyField(Long, related_name="exams", blank="true")
|
||||||
|
|
||||||
|
active = models.BooleanField(
|
||||||
|
help_text="If an exam should be available to take", default=False
|
||||||
|
)
|
||||||
|
|
||||||
|
exam_mode = models.BooleanField(
|
||||||
|
help_text="If an exam should be taken in exam mode (users results will be submited to the server for marking)",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
include_history = models.BooleanField(
|
||||||
|
help_text="If an exam should include history when taking",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
publish_results = models.BooleanField(
|
||||||
|
help_text="If an exams results should be available", default=False
|
||||||
|
)
|
||||||
|
|
||||||
|
recreate_json = models.BooleanField(
|
||||||
|
help_text="If the json cache needs updating", default=False
|
||||||
|
)
|
||||||
|
|
||||||
|
json_creation_time = models.DateTimeField(blank=True, default=None, null=True)
|
||||||
|
exam_json_id = models.IntegerField(
|
||||||
|
default=1, help_text="auto incrementing field when json recreated"
|
||||||
|
)
|
||||||
|
|
||||||
|
archive = models.BooleanField(
|
||||||
|
help_text="Archived exams will remain on the test system but will not be displayed by default",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
open_access = models.BooleanField(
|
||||||
|
help_text="If the exam is freely accessible (to view and edit on the test system)",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
authors_only = models.BooleanField(
|
||||||
|
help_text="If true only exam authors will be able to view.",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
stats_mean = models.FloatField(default=0)
|
||||||
|
stats_mode = models.CharField(default=0, max_length=25)
|
||||||
|
stats_median = models.FloatField(default=0)
|
||||||
|
|
||||||
|
stats_candidates = models.FloatField(default=0)
|
||||||
|
|
||||||
|
stats_min = models.FloatField(default=0)
|
||||||
|
stats_max = models.FloatField(default=0)
|
||||||
|
|
||||||
|
stats_max_possible = models.FloatField(default=0)
|
||||||
|
|
||||||
|
# stats_graph = models.TextField(default=0)
|
||||||
|
|
||||||
|
user_scores = models.JSONField(default=dict)
|
||||||
|
|
||||||
|
|
||||||
|
exam_results_emailed = models.DateTimeField(default=None, null=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
|
||||||
|
def save(self, *args, recreate_json=True, **kwargs):
|
||||||
|
self.recreate_json = recreate_json
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
def get_exam_stats(self, name=True):
|
||||||
|
if self.stats_candidates < int(4):
|
||||||
|
return f"- Candidates: {int(self.stats_candidates)} (too few to generate stats)"
|
||||||
|
|
||||||
|
text = f"""- Candidates: {int(self.stats_candidates)}
|
||||||
|
- Mean: {self.stats_mean:.2f} Median: {self.stats_median} Mode: {self.stats_mode} [Min: {self.stats_min} / Max: {self.stats_max}]
|
||||||
|
"""
|
||||||
|
if name:
|
||||||
|
text = f"""{self.name}
|
||||||
|
{"-"*len(self.name)}
|
||||||
|
{text}
|
||||||
|
"""
|
||||||
|
return text
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def get_absolute_url(self):
|
||||||
|
return reverse("{}:exam_overview".format(self.app_name), kwargs={"pk": self.pk})
|
||||||
|
|
||||||
|
def get_cid_edit_url(self):
|
||||||
|
return reverse(
|
||||||
|
"{}:exam_cids_edit".format(self.app_name), kwargs={"exam_id": self.pk}
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_user_edit_url(self):
|
||||||
|
return reverse(
|
||||||
|
"{}:exam_users_edit".format(self.app_name), kwargs={"exam_id": self.pk}
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_take_url(self):
|
||||||
|
return f"{settings.REMOTE_URL}/rts"
|
||||||
|
|
||||||
|
def get_exam_name(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def get_json_url(self, cid=None, passcode=None):
|
||||||
|
if cid is None:
|
||||||
|
return reverse("{}:exam_json".format(self.app_name), args=(self.pk,))
|
||||||
|
else:
|
||||||
|
return reverse(
|
||||||
|
"{}:exam_json_cid".format(self.app_name), args=(self.pk, cid, passcode)
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_question_index(self, question):
|
||||||
|
return list(self.exam_questions.all()).index(question)
|
||||||
|
|
||||||
|
|
||||||
def get_cid_user_score(self, cid_user):
|
def get_cid_user_score(self, cid_user):
|
||||||
c = "c/" + str(cid_user)
|
c = "c/" + str(cid_user)
|
||||||
if c in self.user_scores:
|
if c in self.user_scores:
|
||||||
|
|||||||
Reference in New Issue
Block a user