From e0f0c7619ae64529c2d12164acbf2cd5df393a2f Mon Sep 17 00:00:00 2001 From: Ross Date: Mon, 12 Jun 2023 11:35:15 +0100 Subject: [PATCH] further abstraction --- atlas/models.py | 5 +- generic/models.py | 235 ++++++++++++++++++++++++---------------------- 2 files changed, 125 insertions(+), 115 deletions(-) diff --git a/atlas/models.py b/atlas/models.py index 8ca0faae..ce53544c 100644 --- a/atlas/models.py +++ b/atlas/models.py @@ -35,6 +35,7 @@ from generic.models import ( CidUser, CidUserExam, CidUserGroup, + ExamCollectionGenericBase, Examination, # Condition, ExamBase, @@ -404,7 +405,7 @@ class Series(SeriesBase): -class CaseCollection(models.Model): +class CaseCollection(ExamCollectionGenericBase): app_name = "atlas" name = models.CharField(max_length=255, unique=True) @@ -458,8 +459,6 @@ class CaseCollection(models.Model): # related_name="atlas_authored_cases", ) - cid_users = GenericRelation("generic.CidUserExam") - valid_cid_users = models.ManyToManyField( CidUser, blank=True, related_name="casecollection_exams" ) diff --git a/generic/models.py b/generic/models.py index a941aeb3..a1c601ae 100644 --- a/generic/models.py +++ b/generic/models.py @@ -310,126 +310,18 @@ class SeriesBase(models.Model): except pydicom.errors.InvalidDicomError: pass -class ExamBase(models.Model): - name = models.CharField(max_length=200, help_text="Name of the exam") - # exam_questions = SortedManyToManyField(Long, related_name="exams", blank="true") +class ExamCollectionGenericBase(models.Model): + """Holds functions that relate to both case and other exams - 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) + e.g. user management + """ # Is this actually used? cid_users = GenericRelation("generic.CidUserExam") - 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_authors(self): """Returns a comma seperated text list of authors""" 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 ) +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): c = "c/" + str(cid_user) if c in self.user_scores: