start working torwards stack modificaiton
This commit is contained in:
@@ -183,7 +183,6 @@ def series_remove_duplicate_images(request, series_id: int):
|
|||||||
|
|
||||||
@router.get("/get_cases_user", auth=django_auth, response=List[CaseSchema])
|
@router.get("/get_cases_user", auth=django_auth, response=List[CaseSchema])
|
||||||
def get_cases_user(request):
|
def get_cases_user(request):
|
||||||
print("HELLO")
|
|
||||||
return Case.objects.filter(author=request.user)
|
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(")"):
|
if dicom_tag.startswith("(") and dicom_tag.endswith(")"):
|
||||||
dicom_tag = tuple([hex(int(i.strip(),16)) for i in dicom_tag[1:-1].split(",")])
|
dicom_tag = tuple([hex(int(i.strip(),16)) for i in dicom_tag[1:-1].split(",")])
|
||||||
|
|
||||||
print("1", dicom_tag)
|
|
||||||
|
|
||||||
image_map = defaultdict(list)
|
image_map = defaultdict(list)
|
||||||
|
|
||||||
# Split teh associated images
|
# Split teh associated images
|
||||||
|
|||||||
+55
-16
@@ -32,7 +32,12 @@ from sortedm2m.fields import SortedManyToManyField
|
|||||||
|
|
||||||
import string
|
import string
|
||||||
from collections import defaultdict
|
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 (
|
from generic.models import (
|
||||||
@@ -143,16 +148,42 @@ class Finding(SynMixin, models.Model):
|
|||||||
class Condition(SynMixin, models.Model):
|
class Condition(SynMixin, models.Model):
|
||||||
name = models.CharField(max_length=255, unique=True)
|
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'.")
|
synonym = models.ManyToManyField(
|
||||||
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'.")
|
"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):
|
def get_absolute_url(self):
|
||||||
return reverse("atlas:condition_detail", kwargs={"pk": self.pk})
|
return reverse("atlas:condition_detail", kwargs={"pk": self.pk})
|
||||||
@@ -163,10 +194,10 @@ class Condition(SynMixin, models.Model):
|
|||||||
def get_descendents(self):
|
def get_descendents(self):
|
||||||
"""TODO"""
|
"""TODO"""
|
||||||
|
|
||||||
|
|
||||||
def get_parents(self):
|
def get_parents(self):
|
||||||
return self.parent.all()
|
return self.parent.all()
|
||||||
|
|
||||||
|
|
||||||
class ConditionRelationship(models.Model):
|
class ConditionRelationship(models.Model):
|
||||||
child = models.ForeignKey(Condition, on_delete=models.CASCADE)
|
child = models.ForeignKey(Condition, on_delete=models.CASCADE)
|
||||||
parent = models.ForeignKey(Condition, on_delete=models.CASCADE, related_name="+")
|
parent = models.ForeignKey(Condition, on_delete=models.CASCADE, related_name="+")
|
||||||
@@ -263,7 +294,6 @@ class Case(models.Model):
|
|||||||
ALMOST_CERTAIN = 3
|
ALMOST_CERTAIN = 3
|
||||||
CERTAIN = 4
|
CERTAIN = 4
|
||||||
|
|
||||||
|
|
||||||
title = models.CharField(max_length=255, help_text="Title of the case", default="")
|
title = models.CharField(max_length=255, help_text="Title of the case", default="")
|
||||||
description = models.TextField(
|
description = models.TextField(
|
||||||
blank=True,
|
blank=True,
|
||||||
@@ -293,7 +323,9 @@ class Case(models.Model):
|
|||||||
Condition, through=Differential, related_name="casedifferential"
|
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)
|
verified = models.BooleanField(default=False)
|
||||||
created_date = models.DateTimeField(default=timezone.now)
|
created_date = models.DateTimeField(default=timezone.now)
|
||||||
@@ -348,7 +380,7 @@ class Case(models.Model):
|
|||||||
return authors
|
return authors
|
||||||
|
|
||||||
def get_link(self):
|
def get_link(self):
|
||||||
#return f"{self.pk}: {self.title}"
|
# return f"{self.pk}: {self.title}"
|
||||||
return format_html("<a href='{}'>{}</a>", self.get_absolute_url(), str(self))
|
return format_html("<a href='{}'>{}</a>", self.get_absolute_url(), str(self))
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
@@ -439,8 +471,14 @@ class SeriesImage(SeriesImageBase):
|
|||||||
"Series", related_name="images", on_delete=models.CASCADE, null=True
|
"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):
|
def get_dicom_data(self):
|
||||||
print(self.image)
|
|
||||||
try:
|
try:
|
||||||
with pydicom.dcmread(self.image) as d:
|
with pydicom.dcmread(self.image) as d:
|
||||||
return d
|
return d
|
||||||
@@ -844,7 +882,11 @@ class UncategorisedDicom(models.Model):
|
|||||||
)
|
)
|
||||||
|
|
||||||
series = models.ForeignKey(
|
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)
|
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():
|
if obj := SeriesImage.objects.filter(image_md5_hash=image_hash).first():
|
||||||
duplicate = obj
|
duplicate = obj
|
||||||
|
|
||||||
|
|
||||||
return duplicate
|
return duplicate
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
"""Override save method to add image hash"""
|
"""Override save method to add image hash"""
|
||||||
if self.image:
|
if self.image:
|
||||||
|
|
||||||
ds = self.get_dicom_info()
|
ds = self.get_dicom_info()
|
||||||
|
|
||||||
self.series_instance_uid = ds["SeriesInstanceUID"].value
|
self.series_instance_uid = ds["SeriesInstanceUID"].value
|
||||||
@@ -886,7 +926,6 @@ class UncategorisedDicom(models.Model):
|
|||||||
if duplicate is not None:
|
if duplicate is not None:
|
||||||
raise DuplicateDicom
|
raise DuplicateDicom
|
||||||
|
|
||||||
|
|
||||||
# Hack for tests
|
# Hack for tests
|
||||||
if image_hash != "1234":
|
if image_hash != "1234":
|
||||||
super().save(*args, **kwargs) # Call the "real" save() method.
|
super().save(*args, **kwargs) # Call the "real" save() method.
|
||||||
|
|||||||
@@ -398,6 +398,8 @@ def user_uploads(request, case_id: int | None = None):
|
|||||||
series_list.append((series, len(data[series]), tags, date))
|
series_list.append((series, len(data[series]), tags, date))
|
||||||
print(series_list)
|
print(series_list)
|
||||||
|
|
||||||
|
series_list.sort(key=lambda l: l[-1])
|
||||||
|
|
||||||
case = False
|
case = False
|
||||||
if case_id is not None:
|
if case_id is not None:
|
||||||
case = get_object_or_404(Case, pk=case_id)
|
case = get_object_or_404(Case, pk=case_id)
|
||||||
|
|||||||
@@ -183,6 +183,11 @@ class QuestionBase(models.Model):
|
|||||||
|
|
||||||
|
|
||||||
class SeriesImageBase(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)
|
position = models.IntegerField(default=0)
|
||||||
upload_filename = models.CharField(max_length=255, blank=True)
|
upload_filename = models.CharField(max_length=255, blank=True)
|
||||||
series = models.ForeignKey(
|
series = models.ForeignKey(
|
||||||
|
|||||||
Reference in New Issue
Block a user