diff --git a/atlas/api.py b/atlas/api.py index 9f7cc9f8..63eb9848 100644 --- a/atlas/api.py +++ b/atlas/api.py @@ -183,7 +183,6 @@ def series_remove_duplicate_images(request, series_id: int): @router.get("/get_cases_user", auth=django_auth, response=List[CaseSchema]) def get_cases_user(request): - print("HELLO") return Case.objects.filter(author=request.user) @@ -250,8 +249,6 @@ def series_split_by_tag(request, series_id: int, dicom_tag:str): if dicom_tag.startswith("(") and dicom_tag.endswith(")"): dicom_tag = tuple([hex(int(i.strip(),16)) for i in dicom_tag[1:-1].split(",")]) - print("1", dicom_tag) - image_map = defaultdict(list) # Split teh associated images diff --git a/atlas/models.py b/atlas/models.py index 8a293e29..8dd6c035 100644 --- a/atlas/models.py +++ b/atlas/models.py @@ -32,7 +32,12 @@ from sortedm2m.fields import SortedManyToManyField import string from collections import defaultdict -from helpers.images import get_image_dicom_hash, get_image_hash, image_as_base64, pretty_print_dicom +from helpers.images import ( + get_image_dicom_hash, + get_image_hash, + image_as_base64, + pretty_print_dicom, +) from generic.models import ( @@ -143,16 +148,42 @@ class Finding(SynMixin, models.Model): class Condition(SynMixin, models.Model): name = models.CharField(max_length=255, unique=True) - synonym = models.ManyToManyField("self", blank=True, help_text="Use if a direct synonym for the condition exists, e.g. 'Wegener granulomatosis' and 'Granulomatosis with Polyangitis'.") - parent = models.ManyToManyField("self", blank=True, related_name="child", symmetrical=False, through="ConditionRelationship", help_text="Use if the condition could be considered a subset of another, e.g. 'Alzheimer disease' and 'Dementia'.") + synonym = models.ManyToManyField( + "self", + blank=True, + help_text="Use if a direct synonym for the condition exists, e.g. 'Wegener granulomatosis' and 'Granulomatosis with Polyangitis'.", + ) + parent = models.ManyToManyField( + "self", + blank=True, + related_name="child", + symmetrical=False, + through="ConditionRelationship", + help_text="Use if the condition could be considered a subset of another, e.g. 'Alzheimer disease' and 'Dementia'.", + ) - primary = models.BooleanField(default="True", help_text="Sets if this should be the canonical item, all other synonyms will then redirect to this by default.") + primary = models.BooleanField( + default="True", + help_text="Sets if this should be the canonical item, all other synonyms will then redirect to this by default.", + ) - subspecialty = models.ManyToManyField("subspecialty", blank=True, help_text="Sets the subspecialty(/ies) that this condition falls under.") + subspecialty = models.ManyToManyField( + "subspecialty", + blank=True, + help_text="Sets the subspecialty(/ies) that this condition falls under.", + ) - rcr_curriculum_map = models.ManyToManyField("self", blank=True, limit_choices_to={"rcr_curriculum": True}, help_text="Specifies the related RCR curriculum condition (if it exists).") + rcr_curriculum_map = models.ManyToManyField( + "self", + blank=True, + limit_choices_to={"rcr_curriculum": True}, + help_text="Specifies the related RCR curriculum condition (if it exists).", + ) - rcr_curriculum = models.BooleanField(default=False, help_text="The condition is from the (non exhaustive) RCR curriculum") + rcr_curriculum = models.BooleanField( + default=False, + help_text="The condition is from the (non exhaustive) RCR curriculum", + ) def get_absolute_url(self): return reverse("atlas:condition_detail", kwargs={"pk": self.pk}) @@ -163,10 +194,10 @@ class Condition(SynMixin, models.Model): def get_descendents(self): """TODO""" - def get_parents(self): return self.parent.all() + class ConditionRelationship(models.Model): child = models.ForeignKey(Condition, on_delete=models.CASCADE) parent = models.ForeignKey(Condition, on_delete=models.CASCADE, related_name="+") @@ -175,7 +206,7 @@ class ConditionRelationship(models.Model): def __str__(self): return f"{self.parent} -> {self.child}" - + class Presentation(models.Model): name = models.CharField(max_length=255) @@ -263,7 +294,6 @@ class Case(models.Model): ALMOST_CERTAIN = 3 CERTAIN = 4 - title = models.CharField(max_length=255, help_text="Title of the case", default="") description = models.TextField( blank=True, @@ -293,7 +323,9 @@ class Case(models.Model): Condition, through=Differential, related_name="casedifferential" ) - diagnostic_certainty = models.IntegerField(choices=CertaintyChoices.choices, default=CertaintyChoices.NONE) + diagnostic_certainty = models.IntegerField( + choices=CertaintyChoices.choices, default=CertaintyChoices.NONE + ) verified = models.BooleanField(default=False) created_date = models.DateTimeField(default=timezone.now) @@ -348,7 +380,7 @@ class Case(models.Model): return authors def get_link(self): - #return f"{self.pk}: {self.title}" + # return f"{self.pk}: {self.title}" return format_html("{}", self.get_absolute_url(), str(self)) def __str__(self): @@ -439,8 +471,14 @@ class SeriesImage(SeriesImageBase): "Series", related_name="images", on_delete=models.CASCADE, null=True ) + replaced = models.ForeignKey( + "self", + null=True, + blank=True, + help_text="Reference to the object that has replaced this one.", + ) + def get_dicom_data(self): - print(self.image) try: with pydicom.dcmread(self.image) as d: return d @@ -844,7 +882,11 @@ class UncategorisedDicom(models.Model): ) series = models.ForeignKey( - "Series", related_name="imported_dicoms", on_delete=models.SET_NULL, null=True, blank=True + "Series", + related_name="imported_dicoms", + on_delete=models.SET_NULL, + null=True, + blank=True, ) series_instance_uid = models.CharField(max_length=255, blank=True, null=True) @@ -864,13 +906,11 @@ class UncategorisedDicom(models.Model): if obj := SeriesImage.objects.filter(image_md5_hash=image_hash).first(): duplicate = obj - return duplicate def save(self, *args, **kwargs): """Override save method to add image hash""" if self.image: - ds = self.get_dicom_info() self.series_instance_uid = ds["SeriesInstanceUID"].value @@ -886,7 +926,6 @@ class UncategorisedDicom(models.Model): if duplicate is not None: raise DuplicateDicom - # Hack for tests if image_hash != "1234": super().save(*args, **kwargs) # Call the "real" save() method. diff --git a/atlas/views.py b/atlas/views.py index 8d43f287..8cd38684 100755 --- a/atlas/views.py +++ b/atlas/views.py @@ -398,6 +398,8 @@ def user_uploads(request, case_id: int | None = None): series_list.append((series, len(data[series]), tags, date)) print(series_list) + series_list.sort(key=lambda l: l[-1]) + case = False if case_id is not None: case = get_object_or_404(Case, pk=case_id) diff --git a/generic/models.py b/generic/models.py index 7e70c986..7aeb9ac5 100644 --- a/generic/models.py +++ b/generic/models.py @@ -183,6 +183,11 @@ class QuestionBase(models.Model): class SeriesImageBase(models.Model): + """ + Series Images may be modified (usually downsampled to save space), in this case the + (larger) original file will be deleted but the model object will be persisted so that + the duplicate check continues to function. + """ position = models.IntegerField(default=0) upload_filename = models.CharField(max_length=255, blank=True) series = models.ForeignKey(