.
This commit is contained in:
@@ -0,0 +1,24 @@
|
|||||||
|
# Generated by Django 3.1.3 on 2021-02-06 09:05
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('anatomy', '0027_anatomyquestion_examination'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='anatomyquestion',
|
||||||
|
name='open_access',
|
||||||
|
field=models.BooleanField(default=True, help_text='If a question should be freely available to browse'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='anatomyquestion',
|
||||||
|
name='question_type',
|
||||||
|
field=models.ForeignKey(default=1, null=True, on_delete=django.db.models.deletion.SET_NULL, to='anatomy.questiontype'),
|
||||||
|
),
|
||||||
|
]
|
||||||
+2
-2
@@ -68,7 +68,7 @@ class AnatomyQuestion(models.Model):
|
|||||||
# image = models.ImageField()
|
# image = models.ImageField()
|
||||||
# feedback = models.TextField(null=True)
|
# feedback = models.TextField(null=True)
|
||||||
question_type = models.ForeignKey(
|
question_type = models.ForeignKey(
|
||||||
QuestionType, on_delete=models.SET_NULL, null=True
|
QuestionType, on_delete=models.SET_NULL, null=True, default=1
|
||||||
)
|
)
|
||||||
|
|
||||||
image = models.ImageField(upload_to=image_directory_path, storage=image_storage)
|
image = models.ImageField(upload_to=image_directory_path, storage=image_storage)
|
||||||
@@ -108,7 +108,7 @@ class AnatomyQuestion(models.Model):
|
|||||||
created_date = models.DateTimeField(default=timezone.now)
|
created_date = models.DateTimeField(default=timezone.now)
|
||||||
|
|
||||||
open_access = models.BooleanField(
|
open_access = models.BooleanField(
|
||||||
help_text="If an question should be freely available to browse", default=True
|
help_text="If a question should be freely available to browse", default=True
|
||||||
)
|
)
|
||||||
|
|
||||||
author = models.ManyToManyField(
|
author = models.ManyToManyField(
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# Generated by Django 3.1.3 on 2021-02-06 09:05
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('longs', '0009_auto_20210205_1146'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='longseries',
|
||||||
|
name='author',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='long_series', to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='long',
|
||||||
|
name='open_access',
|
||||||
|
field=models.BooleanField(default=True, help_text='If a question should be freely available to browse'),
|
||||||
|
),
|
||||||
|
]
|
||||||
+114
-64
@@ -22,9 +22,11 @@ import string
|
|||||||
from anatomy.models import Modality
|
from anatomy.models import Modality
|
||||||
|
|
||||||
from generic.models import Examination, Condition, Sign
|
from generic.models import Examination, Condition, Sign
|
||||||
#from generic.models import Examination, Site, Condition, Sign
|
|
||||||
|
# from generic.models import Examination, Site, Condition, Sign
|
||||||
|
|
||||||
from easy_thumbnails.files import get_thumbnailer
|
from easy_thumbnails.files import get_thumbnailer
|
||||||
|
from easy_thumbnails.exceptions import InvalidImageFormatError
|
||||||
|
|
||||||
image_storage = FileSystemStorage(
|
image_storage = FileSystemStorage(
|
||||||
# Physical file location ROOT
|
# Physical file location ROOT
|
||||||
@@ -33,21 +35,23 @@ image_storage = FileSystemStorage(
|
|||||||
base_url=u"{0}longs/".format(settings.MEDIA_URL),
|
base_url=u"{0}longs/".format(settings.MEDIA_URL),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def image_directory_path(instance, filename):
|
def image_directory_path(instance, filename):
|
||||||
return u"picture/{0}".format(filename)
|
return u"picture/{0}".format(filename)
|
||||||
|
|
||||||
|
|
||||||
def findMiddle(input_list):
|
def findMiddle(input_list):
|
||||||
middle = float(len(input_list))/2
|
middle = float(len(input_list)) / 2
|
||||||
if middle % 2 != 0:
|
if middle % 2 != 0:
|
||||||
return input_list[int(middle - .5)]
|
return input_list[int(middle - 0.5)]
|
||||||
else:
|
else:
|
||||||
return (input_list[int(middle)])
|
return input_list[int(middle)]
|
||||||
return (input_list[int(middle)], input_list[int(middle-1)])
|
return (input_list[int(middle)], input_list[int(middle - 1)])
|
||||||
|
|
||||||
|
|
||||||
class Long(models.Model):
|
class Long(models.Model):
|
||||||
#author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
|
# author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
|
||||||
#image = models.ImageField()
|
# image = models.ImageField()
|
||||||
history = models.TextField(null=True, blank=True)
|
history = models.TextField(null=True, blank=True)
|
||||||
|
|
||||||
feedback = models.TextField(null=True, blank=True)
|
feedback = models.TextField(null=True, blank=True)
|
||||||
@@ -55,12 +59,12 @@ class Long(models.Model):
|
|||||||
condition = tagulous.models.TagField(
|
condition = tagulous.models.TagField(
|
||||||
to=Condition,
|
to=Condition,
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text=
|
help_text='Associated condition. Will allow searching / filtering and tips / hints to be displayed. Conditions with spaces must be enclosed in quotes "..."',
|
||||||
"Associated condition. Will allow searching / filtering and tips / hints to be displayed. Conditions with spaces must be enclosed in quotes \"...\""
|
|
||||||
)
|
)
|
||||||
|
|
||||||
sign = tagulous.models.TagField(
|
sign = tagulous.models.TagField(
|
||||||
to=Sign, blank=True, help_text='Radiological signs in the question')
|
to=Sign, blank=True, help_text="Radiological signs in the question"
|
||||||
|
)
|
||||||
|
|
||||||
# Model answers
|
# Model answers
|
||||||
model_observations = models.TextField(null=True, blank=True)
|
model_observations = models.TextField(null=True, blank=True)
|
||||||
@@ -69,9 +73,8 @@ class Long(models.Model):
|
|||||||
model_differential_diagnosis = models.TextField(null=True, blank=True)
|
model_differential_diagnosis = models.TextField(null=True, blank=True)
|
||||||
model_management = models.TextField(null=True, blank=True)
|
model_management = models.TextField(null=True, blank=True)
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_SITE_ID = 1
|
DEFAULT_SITE_ID = 1
|
||||||
#site = models.ManyToManyField(
|
# site = models.ManyToManyField(
|
||||||
# Site,
|
# Site,
|
||||||
# blank=True,
|
# blank=True,
|
||||||
# default=DEFAULT_SITE_ID,
|
# default=DEFAULT_SITE_ID,
|
||||||
@@ -80,22 +83,23 @@ class Long(models.Model):
|
|||||||
verified = models.BooleanField(default=False)
|
verified = models.BooleanField(default=False)
|
||||||
created_date = models.DateTimeField(default=timezone.now)
|
created_date = models.DateTimeField(default=timezone.now)
|
||||||
published_date = models.DateTimeField(blank=True, null=True)
|
published_date = models.DateTimeField(blank=True, null=True)
|
||||||
author = models.ManyToManyField(settings.AUTH_USER_MODEL,
|
author = models.ManyToManyField(
|
||||||
blank=True,
|
settings.AUTH_USER_MODEL,
|
||||||
help_text='Author of question',
|
blank=True,
|
||||||
related_name="long_authored_questions")
|
help_text="Author of question",
|
||||||
|
related_name="long_authored_questions",
|
||||||
scrapped = models.BooleanField(
|
|
||||||
default=False,
|
|
||||||
help_text='Question has been scrapped and will not be shown')
|
|
||||||
|
|
||||||
open_access = models.BooleanField(
|
|
||||||
help_text="If an question should be freely available to browse", default=True
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
scrapped = models.BooleanField(
|
||||||
|
default=False, help_text="Question has been scrapped and will not be shown"
|
||||||
|
)
|
||||||
|
|
||||||
|
open_access = models.BooleanField(
|
||||||
|
help_text="If a question should be freely available to browse", default=True
|
||||||
|
)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('longs:long_detail', kwargs={'pk': self.pk})
|
return reverse("longs:long_detail", kwargs={"pk": self.pk})
|
||||||
|
|
||||||
def get_authors(self):
|
def get_authors(self):
|
||||||
"""Returns a comma seperated text list of authors"""
|
"""Returns a comma seperated text list of authors"""
|
||||||
@@ -116,7 +120,6 @@ class Long(models.Model):
|
|||||||
exams = ", ".join(e)
|
exams = ", ".join(e)
|
||||||
return exams
|
return exams
|
||||||
|
|
||||||
|
|
||||||
def GetImages(self, feedback=False):
|
def GetImages(self, feedback=False):
|
||||||
qs = self.images.all()
|
qs = self.images.all()
|
||||||
|
|
||||||
@@ -128,28 +131,64 @@ class Long(models.Model):
|
|||||||
return images
|
return images
|
||||||
|
|
||||||
def GetImageUrls(self):
|
def GetImageUrls(self):
|
||||||
return ",".join(["http://penracourses.org.uk{}".format(i.url) for i in self.GetImages()])
|
return ",".join(
|
||||||
#def GetNonFeedbackQuestionImages(self):
|
["http://penracourses.org.uk{}".format(i.url) for i in self.GetImages()]
|
||||||
#return self.GetImages()
|
)
|
||||||
|
|
||||||
|
# def GetNonFeedbackQuestionImages(self):
|
||||||
|
# return self.GetImages()
|
||||||
|
|
||||||
|
|
||||||
class LongSeriesImage(models.Model):
|
class LongSeriesImage(models.Model):
|
||||||
image = models.ImageField(upload_to=image_directory_path, storage=image_storage)
|
image = models.ImageField(upload_to=image_directory_path, storage=image_storage)
|
||||||
position = models.IntegerField(default=0)
|
position = models.IntegerField(default=0)
|
||||||
series = models.ForeignKey("LongSeries", related_name="images", on_delete=models.SET_NULL, null=True)
|
series = models.ForeignKey(
|
||||||
|
"LongSeries", related_name="images", on_delete=models.SET_NULL, null=True
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class LongSeries(models.Model):
|
class LongSeries(models.Model):
|
||||||
modality = models.ForeignKey(Modality, related_name="series_modality", on_delete=models.SET_NULL, null=True)
|
modality = models.ForeignKey(
|
||||||
|
Modality, related_name="series_modality", on_delete=models.SET_NULL, null=True
|
||||||
|
)
|
||||||
examination = models.ForeignKey(
|
examination = models.ForeignKey(
|
||||||
Examination, help_text="Name of the examination", related_name="series_examination", on_delete=models.SET_NULL, null=True)
|
Examination,
|
||||||
long = models.ForeignKey("Long", help_text="The question this series should be associated with", related_name="series", on_delete=models.SET_NULL, null=True)
|
help_text="Name of the examination",
|
||||||
description = models.TextField(blank=True, help_text="Description of stack, for admin organisation, will not be visible when taking")
|
related_name="series_examination",
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
null=True,
|
||||||
|
)
|
||||||
|
long = models.ForeignKey(
|
||||||
|
"Long",
|
||||||
|
help_text="The question this series should be associated with",
|
||||||
|
related_name="series",
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
null=True,
|
||||||
|
)
|
||||||
|
description = models.TextField(
|
||||||
|
blank=True,
|
||||||
|
help_text="Description of stack, for admin organisation, will not be visible when taking",
|
||||||
|
)
|
||||||
|
|
||||||
|
author = models.ForeignKey(
|
||||||
|
settings.AUTH_USER_MODEL,
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
related_name="long_series",
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "{} : {} [{}]".format(self.get_examination(), self.description, self.long.pk)
|
return "{} : {} [{}]".format(
|
||||||
|
self.get_examination(), self.description, self.long.pk
|
||||||
|
)
|
||||||
|
|
||||||
def get_author_objects(self):
|
def get_author_objects(self):
|
||||||
"""Returns a comma seperated text list of authors"""
|
"""Returns a comma seperated text list of authors"""
|
||||||
|
if self.author:
|
||||||
|
return self.author.all()
|
||||||
|
else:
|
||||||
|
return ["None"]
|
||||||
if self.long:
|
if self.long:
|
||||||
authors = [i for i in self.long.author.all()]
|
authors = [i for i in self.long.author.all()]
|
||||||
else:
|
else:
|
||||||
@@ -157,15 +196,21 @@ class LongSeries(models.Model):
|
|||||||
|
|
||||||
return authors
|
return authors
|
||||||
|
|
||||||
|
def get_author_display(self):
|
||||||
|
return ", ".join(self.get_author_objects())
|
||||||
|
|
||||||
def get_examination(self):
|
def get_examination(self):
|
||||||
"""Returns a comma seperated text list of regions"""
|
"""Returns a comma seperated text list of regions"""
|
||||||
return str(self.examination)
|
return str(self.examination)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('longs:long_series_detail', kwargs={'pk': self.pk})
|
return reverse("longs:long_series_detail", kwargs={"pk": self.pk})
|
||||||
|
|
||||||
def get_image_urls(self, feedback=False):
|
def get_image_urls(self, feedback=False):
|
||||||
images = ["http://penracourses.org.uk{}".format(i.image.url) for i in self.images.all()]
|
images = [
|
||||||
|
"http://penracourses.org.uk{}".format(i.image.url)
|
||||||
|
for i in self.images.all()
|
||||||
|
]
|
||||||
|
|
||||||
return ",".join(images)
|
return ",".join(images)
|
||||||
|
|
||||||
@@ -173,21 +218,25 @@ class LongSeries(models.Model):
|
|||||||
images = self.images.all()
|
images = self.images.all()
|
||||||
|
|
||||||
if len(images) < 1:
|
if len(images) < 1:
|
||||||
return 'No images', 0
|
return "No images", 0
|
||||||
|
|
||||||
img = findMiddle(images).image
|
img = findMiddle(images).image
|
||||||
thumbnailer = get_thumbnailer(img)
|
try:
|
||||||
|
thumbnailer = get_thumbnailer(img)
|
||||||
|
except InvalidImageFormatError:
|
||||||
|
return format_html('<span title="{}">Invalid image url<span>', image_object)
|
||||||
thumbnail = thumbnailer["exam-list"]
|
thumbnail = thumbnailer["exam-list"]
|
||||||
return '<img src="/media/{}" />'.format(thumbnail), len(images)
|
return '<img src="/media/{}" />'.format(thumbnail), len(images)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_block(self):
|
def get_block(self):
|
||||||
examination = self.get_examination()
|
examination = self.get_examination()
|
||||||
thumb, image_number = self.get_thumbnail()
|
thumb, image_number = self.get_thumbnail()
|
||||||
return format_html('<div>{}<br/>{}<br/>Images: {}</div>', examination, thumb, image_number )
|
return format_html(
|
||||||
|
"<div>{}<br/>{}<br/>Images: {}</div>", examination, thumb, image_number
|
||||||
|
)
|
||||||
|
|
||||||
#class LongImage(models.Model):
|
|
||||||
|
# class LongImage(models.Model):
|
||||||
# long = models.ForeignKey(Long,
|
# long = models.ForeignKey(Long,
|
||||||
# related_name="images",
|
# related_name="images",
|
||||||
# on_delete=models.CASCADE)
|
# on_delete=models.CASCADE)
|
||||||
@@ -207,47 +256,43 @@ class LongSeries(models.Model):
|
|||||||
|
|
||||||
|
|
||||||
class Note(models.Model):
|
class Note(models.Model):
|
||||||
long = models.ForeignKey(Long,
|
long = models.ForeignKey(
|
||||||
related_name="long_notes",
|
Long, related_name="long_notes", on_delete=models.CASCADE, null=True
|
||||||
on_delete=models.CASCADE,
|
)
|
||||||
null=True)
|
# author = models.ForeignKey(User,
|
||||||
#author = models.ForeignKey(User,
|
author = models.ForeignKey(
|
||||||
author = models.ForeignKey( settings.AUTH_USER_MODEL,
|
settings.AUTH_USER_MODEL, related_name="long_notes", on_delete=models.CASCADE
|
||||||
related_name="long_notes",
|
)
|
||||||
on_delete=models.CASCADE)
|
|
||||||
note = models.TextField()
|
note = models.TextField()
|
||||||
created_on = models.DateTimeField(auto_now_add=True)
|
created_on = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
self.pk = self.long_id
|
self.pk = self.long_id
|
||||||
return reverse('longs:long_detail', kwargs={'pk': self.pk})
|
return reverse("longs:long_detail", kwargs={"pk": self.pk})
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "{} [{}] {}".format(self.long, self.author, self.created_on)
|
return "{} [{}] {}".format(self.long, self.author, self.created_on)
|
||||||
|
|
||||||
|
|
||||||
class LongCreationDefault(models.Model):
|
class LongCreationDefault(models.Model):
|
||||||
#author = models.OneToOneField(User,
|
# author = models.OneToOneField(User,
|
||||||
author = models.OneToOneField(
|
author = models.OneToOneField(
|
||||||
settings.AUTH_USER_MODEL,
|
settings.AUTH_USER_MODEL, related_name="long_default", on_delete=models.CASCADE
|
||||||
related_name="long_default",
|
)
|
||||||
on_delete=models.CASCADE)
|
|
||||||
|
|
||||||
#site = models.ManyToManyField(
|
# site = models.ManyToManyField(
|
||||||
# Site,
|
# Site,
|
||||||
# blank=True,
|
# blank=True,
|
||||||
# default=1,
|
# default=1,
|
||||||
# help_text="Default site to use when creating a new long")
|
# help_text="Default site to use when creating a new long")
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('longs:long_create')
|
return reverse("longs:long_create")
|
||||||
|
|
||||||
|
|
||||||
class Exam(models.Model):
|
class Exam(models.Model):
|
||||||
name = models.CharField(max_length=200)
|
name = models.CharField(max_length=200)
|
||||||
exam_questions = SortedManyToManyField(
|
exam_questions = SortedManyToManyField(Long, related_name="exams", blank="true")
|
||||||
Long, related_name="exams", blank="true"
|
|
||||||
)
|
|
||||||
|
|
||||||
active = models.BooleanField(
|
active = models.BooleanField(
|
||||||
help_text="If an exam should be available", default=True
|
help_text="If an exam should be available", default=True
|
||||||
@@ -262,7 +307,8 @@ class Exam(models.Model):
|
|||||||
)
|
)
|
||||||
|
|
||||||
time_limit = models.IntegerField(
|
time_limit = models.IntegerField(
|
||||||
help_text="Exam time limit (in seconds). Default is 2100 secondse (35 minutes)", default=2100
|
help_text="Exam time limit (in seconds). Default is 2100 secondse (35 minutes)",
|
||||||
|
default=2100,
|
||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
@@ -280,6 +326,7 @@ class Exam(models.Model):
|
|||||||
def get_question_index(self, question):
|
def get_question_index(self, question):
|
||||||
return list(self.exam_questions.all()).index(question)
|
return list(self.exam_questions.all()).index(question)
|
||||||
|
|
||||||
|
|
||||||
class CidUserAnswer(models.Model):
|
class CidUserAnswer(models.Model):
|
||||||
"""User answers by candidate"""
|
"""User answers by candidate"""
|
||||||
|
|
||||||
@@ -287,7 +334,6 @@ class CidUserAnswer(models.Model):
|
|||||||
Long, related_name="cid_user_answers", on_delete=models.CASCADE
|
Long, related_name="cid_user_answers", on_delete=models.CASCADE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# Answers
|
# Answers
|
||||||
answer_observations = models.TextField(null=True, blank=True)
|
answer_observations = models.TextField(null=True, blank=True)
|
||||||
answer_interpretation = models.TextField(null=True, blank=True)
|
answer_interpretation = models.TextField(null=True, blank=True)
|
||||||
@@ -295,7 +341,11 @@ class CidUserAnswer(models.Model):
|
|||||||
answer_differential_diagnosis = models.TextField(null=True, blank=True)
|
answer_differential_diagnosis = models.TextField(null=True, blank=True)
|
||||||
answer_management = models.TextField(null=True, blank=True)
|
answer_management = models.TextField(null=True, blank=True)
|
||||||
|
|
||||||
cid = models.BigIntegerField(blank=True, null=True, help_text="Candidate ID (limitied by BigIntegerField size)")
|
cid = models.BigIntegerField(
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
help_text="Candidate ID (limitied by BigIntegerField size)",
|
||||||
|
)
|
||||||
|
|
||||||
# Each user answer is associated with a particular exam
|
# Each user answer is associated with a particular exam
|
||||||
exam = models.ForeignKey(
|
exam = models.ForeignKey(
|
||||||
@@ -323,7 +373,7 @@ class CidUserAnswer(models.Model):
|
|||||||
self.answer = self.answer.strip()
|
self.answer = self.answer.strip()
|
||||||
|
|
||||||
s = self.answer.lower()
|
s = self.answer.lower()
|
||||||
s = s.translate(str.maketrans('', '', string.punctuation))
|
s = s.translate(str.maketrans("", "", string.punctuation))
|
||||||
|
|
||||||
self.answer_compare = s
|
self.answer_compare = s
|
||||||
|
|
||||||
|
|||||||
+10
-8
@@ -17,12 +17,14 @@ class ImageColumn(tables.Column):
|
|||||||
if obj is None:
|
if obj is None:
|
||||||
return format_html('<span>No image<span>')
|
return format_html('<span>No image<span>')
|
||||||
|
|
||||||
image_object = obj.image
|
return obj.get_thumbnail()[0]
|
||||||
try:
|
|
||||||
thumbnailer = get_thumbnailer(image_object)
|
#image_object = obj.image
|
||||||
return format_html('<img src="/media/{}" />', thumbnailer["exam-list"])
|
#try:
|
||||||
except InvalidImageFormatError:
|
# thumbnailer = get_thumbnailer(image_object)
|
||||||
return format_html('<span title="{}">Invalid image url<span>', image_object)
|
# return format_html('<img src="/media/{}" />', thumbnailer["exam-list"])
|
||||||
|
#except InvalidImageFormatError:
|
||||||
|
# return format_html('<span title="{}">Invalid image url<span>', image_object)
|
||||||
|
|
||||||
|
|
||||||
class LongTable(tables.Table):
|
class LongTable(tables.Table):
|
||||||
@@ -38,11 +40,11 @@ class LongTable(tables.Table):
|
|||||||
text='Clone',
|
text='Clone',
|
||||||
args=[A('pk')],
|
args=[A('pk')],
|
||||||
orderable=False)
|
orderable=False)
|
||||||
images = ImageColumn("images", orderable=False)
|
series = ImageColumn("images", orderable=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Long
|
model = Long
|
||||||
template_name = "django_tables2/bootstrap4.html"
|
template_name = "django_tables2/bootstrap4.html"
|
||||||
fields = ("normal", "abnormality", "region", "examination",
|
fields = ("normal", "abnormality", "region", "examination",
|
||||||
"laterality", "site", "created_date", "author")
|
"laterality", "site", "created_date", "author")
|
||||||
sequence = ("view", "images")
|
sequence = ("view", "series")
|
||||||
@@ -19,7 +19,7 @@ Longs:
|
|||||||
{% if request.user.is_authenticated %}
|
{% if request.user.is_authenticated %}
|
||||||
<a href="{% url 'longs:exam_list' %}">Exams</a> /
|
<a href="{% url 'longs:exam_list' %}">Exams</a> /
|
||||||
<a href="{% url 'longs:long_view' %}">Questions</a> /
|
<a href="{% url 'longs:long_view' %}">Questions</a> /
|
||||||
<a href="{% url 'longs:long_create' %}" title="Create a new question">Create Question</a>
|
<a href="{% url 'longs:long_create' %}" title="Create a new long case">Create Case</a> /
|
||||||
<a href="{% url 'longs:long_series_create' %}" title="Create a new image series">Create Series</a>
|
<a href="{% url 'longs:long_series_create' %}" title="Create a new image series">Create Series</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% comment %} </br>
|
{% comment %} </br>
|
||||||
|
|||||||
@@ -18,7 +18,8 @@
|
|||||||
>Popup</a>
|
>Popup</a>
|
||||||
</span>
|
</span>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<a href="{% url 'longs:long_series_id_create' pk=question.pk %}">Add series</a>
|
<span><a href="{% url 'longs:long_series_id_create' pk=question.pk %}">Add new series</a><br/>
|
||||||
|
<a href="{% url 'longs:long_series_id_create' pk=question.pk %}">Add existing series</a></span>
|
||||||
</div>
|
</div>
|
||||||
<p class="pre-whitespace"><b>Feedback:</b> {{ question.feedback }}</p>
|
<p class="pre-whitespace"><b>Feedback:</b> {{ question.feedback }}</p>
|
||||||
<p><b>Author(s):</b> {% for author in question.author.all %} <a
|
<p><b>Author(s):</b> {% for author in question.author.all %} <a
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h2>Submit Long Case</h2>
|
<h2>Submit Long Case</h2>
|
||||||
Use this form to create a long case. Once the case has been created it can be associated with image sets (series)
|
Use this form to create a long case. Associated image sets can be added using this form however the images will have to be uploaded once the case has been created.
|
||||||
<form action="" method="post" enctype="multipart/form-data" id="long-form">
|
<form action="" method="post" enctype="multipart/form-data" id="long-form">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
|
||||||
|
|||||||
@@ -2,3 +2,5 @@
|
|||||||
<div>{{ series.modality}}, {{ series.examination }}</div>
|
<div>{{ series.modality}}, {{ series.examination }}</div>
|
||||||
|
|
||||||
<div id="single-dicom-viewer" class="dicom-viewer" data-images="{{ series.get_image_urls }}" data-annotations=''></div>
|
<div id="single-dicom-viewer" class="dicom-viewer" data-images="{{ series.get_image_urls }}" data-annotations=''></div>
|
||||||
|
|
||||||
|
<div>Author: {{ series.get_author_display }}</div>
|
||||||
+3
-1
@@ -294,7 +294,7 @@ class LongSeriesCreate(LoginRequiredMixin, CreateView):
|
|||||||
self.object = form.save(commit=False)
|
self.object = form.save(commit=False)
|
||||||
self.object.save()
|
self.object.save()
|
||||||
|
|
||||||
#form.instance.author.add(self.request.user.id)
|
form.instance.author.add(self.request.user.id)
|
||||||
|
|
||||||
context = self.get_context_data(form=form)
|
context = self.get_context_data(form=form)
|
||||||
series_formset = context["image_formset"]
|
series_formset = context["image_formset"]
|
||||||
@@ -336,6 +336,8 @@ class LongSeriesUpdate(LoginRequiredMixin, AuthorOrCheckerRequiredMixin, UpdateV
|
|||||||
#self.object.exams.clear()
|
#self.object.exams.clear()
|
||||||
#self.object.exams.add(*new_exams)
|
#self.object.exams.add(*new_exams)
|
||||||
|
|
||||||
|
form.instance.author.add(self.request.user.id)
|
||||||
|
|
||||||
context = self.get_context_data(form=form)
|
context = self.get_context_data(form=form)
|
||||||
image_formset = context["image_formset"]
|
image_formset = context["image_formset"]
|
||||||
# logger.debug(formset.is_valid())
|
# logger.debug(formset.is_valid())
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 3.1.3 on 2021-02-06 09:05
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('rapids', '0011_auto_20210205_1056'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='rapid',
|
||||||
|
name='open_access',
|
||||||
|
field=models.BooleanField(default=True, help_text='If a question should be freely available to browse'),
|
||||||
|
),
|
||||||
|
]
|
||||||
+1
-1
@@ -168,7 +168,7 @@ class Rapid(models.Model):
|
|||||||
help_text='Question has been scrapped and will not be shown')
|
help_text='Question has been scrapped and will not be shown')
|
||||||
|
|
||||||
open_access = models.BooleanField(
|
open_access = models.BooleanField(
|
||||||
help_text="If an question should be freely available to browse", default=True
|
help_text="If a question should be freely available to browse", default=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user