.
This commit is contained in:
+8
-6
@@ -1,5 +1,5 @@
|
||||
from django.contrib import admin
|
||||
from .models import Case, Series, SeriesImage
|
||||
from .models import Case, Series, SeriesImage, Condition
|
||||
|
||||
import tagulous.admin
|
||||
|
||||
@@ -11,13 +11,13 @@ from django.forms.widgets import RadioSelect
|
||||
from tinymce.widgets import TinyMCE
|
||||
|
||||
# from generic.models import Examination, Sign, Site, Condition
|
||||
from generic.models import Examination, Sign, Condition
|
||||
from generic.models import Examination, Sign
|
||||
|
||||
# Register your models here.
|
||||
# admin.site.register(Examination)
|
||||
# admin.site.register(Site)
|
||||
admin.site.register(SeriesImage)
|
||||
|
||||
admin.site.register(Condition)
|
||||
|
||||
|
||||
class AtlasAdminForm(ModelForm):
|
||||
@@ -37,10 +37,11 @@ class AtlasAdminForm(ModelForm):
|
||||
# "normal": RadioSelect(
|
||||
# choices=[(True, 'Yes'),
|
||||
# (False, 'No')])
|
||||
"findings" : TinyMCE(attrs={'cols': 80, 'rows': 30}),
|
||||
"mark_scheme" : TinyMCE(attrs={'cols': 80, 'rows': 30}),
|
||||
"findings": TinyMCE(attrs={"cols": 80, "rows": 30}),
|
||||
"mark_scheme": TinyMCE(attrs={"cols": 80, "rows": 30}),
|
||||
}
|
||||
|
||||
|
||||
class AtlasAdmin(VersionAdmin):
|
||||
form = AtlasAdminForm
|
||||
|
||||
@@ -55,4 +56,5 @@ admin.site.register(Case, AtlasAdmin)
|
||||
class SeriesAdmin(VersionAdmin):
|
||||
pass
|
||||
|
||||
admin.site.register(Series, SeriesAdmin)
|
||||
|
||||
admin.site.register(Series, SeriesAdmin)
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
# Generated by Django 3.2.8 on 2021-11-25 22:16
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('atlas', '0003_rename_subpecialty_case_subspecialty'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Condition',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=255)),
|
||||
('synonym', models.ManyToManyField(blank=True, related_name='_atlas_condition_synonym_+', to='atlas.Condition')),
|
||||
],
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='case',
|
||||
name='condition',
|
||||
field=models.ManyToManyField(blank=True, to='atlas.Condition'),
|
||||
),
|
||||
]
|
||||
+53
-34
@@ -28,7 +28,15 @@ from helpers.images import image_as_base64, pretty_print_dicom
|
||||
|
||||
from anatomy.models import Modality
|
||||
|
||||
from generic.models import Examination, Condition, Sign, ExamBase, Plane, Contrast, QuestionNote
|
||||
from generic.models import (
|
||||
Examination,
|
||||
Condition,
|
||||
Sign,
|
||||
ExamBase,
|
||||
Plane,
|
||||
Contrast,
|
||||
QuestionNote,
|
||||
)
|
||||
|
||||
# from generic.models import Examination, Site, Condition, Sign
|
||||
|
||||
@@ -66,20 +74,26 @@ def findMiddle(input_list):
|
||||
return (input_list[int(middle)], input_list[int(middle - 1)])
|
||||
|
||||
|
||||
class Condition(models.Model):
|
||||
name = models.CharField(max_length=255)
|
||||
|
||||
synonym = models.ManyToManyField("self", blank=True)
|
||||
|
||||
|
||||
@reversion.register
|
||||
class Case(models.Model):
|
||||
class SubspecialtyChoices(models.TextChoices):
|
||||
BREAST = 'BR', _('Breast')
|
||||
CARDIAC = 'CA', _('Cardiac')
|
||||
GASTRO = 'GI', _('Gastrointestinal and hepatobiliary')
|
||||
HEADNECK = 'HN', _('Head and Neck')
|
||||
MSK = 'MS', _('Musculoskeletal')
|
||||
NEURO = 'NE', _('Neuroradiology')
|
||||
OBSGYN = 'OG', _('Obstectric and Gynaecological')
|
||||
PAED = 'PA', _('Paediatric')
|
||||
URO = 'UR', _('Uroradiology')
|
||||
VASC = 'VA', _('Vascular')
|
||||
HAEMONC = 'HA', _('Haemotology and Oncology')
|
||||
BREAST = "BR", _("Breast")
|
||||
CARDIAC = "CA", _("Cardiac")
|
||||
GASTRO = "GI", _("Gastrointestinal and hepatobiliary")
|
||||
HEADNECK = "HN", _("Head and Neck")
|
||||
MSK = "MS", _("Musculoskeletal")
|
||||
NEURO = "NE", _("Neuroradiology")
|
||||
OBSGYN = "OG", _("Obstectric and Gynaecological")
|
||||
PAED = "PA", _("Paediatric")
|
||||
URO = "UR", _("Uroradiology")
|
||||
VASC = "VA", _("Vascular")
|
||||
HAEMONC = "HA", _("Haemotology and Oncology")
|
||||
|
||||
title = models.CharField(max_length=255, help_text="Title of the case", default="")
|
||||
# author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
|
||||
@@ -95,11 +109,7 @@ class Case(models.Model):
|
||||
|
||||
subspecialty = models.CharField(max_length=2, choices=SubspecialtyChoices.choices)
|
||||
|
||||
condition = tagulous.models.TagField(
|
||||
to=Condition,
|
||||
blank=True,
|
||||
help_text='Associated condition. Will allow searching / filtering and tips / hints to be displayed. Conditions with spaces must be enclosed in quotes "..."',
|
||||
)
|
||||
condition = models.ManyToManyField(Condition, blank=True)
|
||||
|
||||
sign = tagulous.models.TagField(
|
||||
to=Sign, blank=True, help_text="Radiological signs in the question"
|
||||
@@ -127,7 +137,7 @@ class Case(models.Model):
|
||||
|
||||
notes = GenericRelation(QuestionNote)
|
||||
|
||||
#question_file = models.FileField(upload_to=question_file_directory_path, blank=True, null=True)
|
||||
# question_file = models.FileField(upload_to=question_file_directory_path, blank=True, null=True)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse("atlas:case_detail", kwargs={"pk": self.pk})
|
||||
@@ -158,11 +168,19 @@ class Case(models.Model):
|
||||
|
||||
def get_image_urls(self):
|
||||
return ",".join(
|
||||
["https://www.penracourses.org.uk{}".format(i.url) for i in self.get_images()]
|
||||
[
|
||||
"https://www.penracourses.org.uk{}".format(i.url)
|
||||
for i in self.get_images()
|
||||
]
|
||||
)
|
||||
|
||||
def get_image_url_array(self):
|
||||
return json.dumps(["https://www.penracourses.org.uk{}".format(i.url) for i in self.get_images()])
|
||||
return json.dumps(
|
||||
[
|
||||
"https://www.penracourses.org.uk{}".format(i.url)
|
||||
for i in self.get_images()
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class SeriesImage(models.Model):
|
||||
@@ -174,20 +192,23 @@ class SeriesImage(models.Model):
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ['position']
|
||||
ordering = ["position"]
|
||||
|
||||
def get_dicom_info(self):
|
||||
try:
|
||||
info = pretty_print_dicom(pydicom.read_file(self.image))
|
||||
except pydicom.errors.InvalidDicomError:
|
||||
info = "File is not a dicom."
|
||||
return(info)
|
||||
return info
|
||||
|
||||
|
||||
@reversion.register
|
||||
class Series(models.Model):
|
||||
modality = models.ForeignKey(
|
||||
Modality, related_name="atlas_series_modality", on_delete=models.SET_NULL, null=True
|
||||
Modality,
|
||||
related_name="atlas_series_modality",
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
)
|
||||
examination = models.ForeignKey(
|
||||
Examination,
|
||||
@@ -227,15 +248,14 @@ class Series(models.Model):
|
||||
help_text="If a question should be freely available to browse", default=True
|
||||
)
|
||||
|
||||
|
||||
def __str__(self):
|
||||
if self.case:
|
||||
case_id = ", ".format([case.pk for case in self.case.all()])
|
||||
#case_id = self.case.pk
|
||||
# case_id = self.case.pk
|
||||
else:
|
||||
case_id = "None"
|
||||
return "{}/{} : {} [{}]".format(self.pk,
|
||||
self.get_examination(), self.description, case_id
|
||||
return "{}/{} : {} [{}]".format(
|
||||
self.pk, self.get_examination(), self.description, case_id
|
||||
)
|
||||
|
||||
def get_author_objects(self):
|
||||
@@ -295,7 +315,9 @@ class Series(models.Model):
|
||||
thumbnailer = get_thumbnailer(img)
|
||||
thumbnail = thumbnailer["exam-list"]
|
||||
except InvalidImageFormatError:
|
||||
return format_html('<span title="{}">Invalid image url<span>', img), len(images)
|
||||
return format_html('<span title="{}">Invalid image url<span>', img), len(
|
||||
images
|
||||
)
|
||||
return format_html('<img src="/media/{}" />', thumbnail), len(images)
|
||||
|
||||
def get_block(self):
|
||||
@@ -323,8 +345,6 @@ class Series(models.Model):
|
||||
i.position = n
|
||||
i.save()
|
||||
n = n + 1
|
||||
|
||||
|
||||
|
||||
def order_by_dicom(self, field="SliceLocation"):
|
||||
images = self.images.all()
|
||||
@@ -334,7 +354,7 @@ class Series(models.Model):
|
||||
for i in images:
|
||||
files.append((i, pydicom.dcmread(i.image.path)))
|
||||
|
||||
#print("file count: {}".format(len(files)))
|
||||
# print("file count: {}".format(len(files)))
|
||||
|
||||
# skip files with no SliceLocation (eg scout views)
|
||||
slices = []
|
||||
@@ -343,7 +363,7 @@ class Series(models.Model):
|
||||
for i, f in files:
|
||||
if hasattr(f, field):
|
||||
slices.append(f)
|
||||
#map[f.SliceLocation] = i
|
||||
# map[f.SliceLocation] = i
|
||||
map[f[field].value] = i
|
||||
else:
|
||||
skipcount = skipcount + 1
|
||||
@@ -353,8 +373,7 @@ class Series(models.Model):
|
||||
# ensure they are in the correct order
|
||||
slices = sorted(slices, key=lambda s: s[field].value)
|
||||
|
||||
|
||||
#print(slices)
|
||||
# print(slices)
|
||||
n = 1
|
||||
for f in slices:
|
||||
i = map[f[field].value]
|
||||
|
||||
Reference in New Issue
Block a user