.
This commit is contained in:
+114
-64
@@ -22,9 +22,11 @@ import string
|
||||
from anatomy.models import Modality
|
||||
|
||||
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.exceptions import InvalidImageFormatError
|
||||
|
||||
image_storage = FileSystemStorage(
|
||||
# Physical file location ROOT
|
||||
@@ -33,21 +35,23 @@ image_storage = FileSystemStorage(
|
||||
base_url=u"{0}longs/".format(settings.MEDIA_URL),
|
||||
)
|
||||
|
||||
|
||||
def image_directory_path(instance, filename):
|
||||
return u"picture/{0}".format(filename)
|
||||
|
||||
|
||||
def findMiddle(input_list):
|
||||
middle = float(len(input_list))/2
|
||||
middle = float(len(input_list)) / 2
|
||||
if middle % 2 != 0:
|
||||
return input_list[int(middle - .5)]
|
||||
return input_list[int(middle - 0.5)]
|
||||
else:
|
||||
return (input_list[int(middle)])
|
||||
return (input_list[int(middle)], input_list[int(middle-1)])
|
||||
return input_list[int(middle)]
|
||||
return (input_list[int(middle)], input_list[int(middle - 1)])
|
||||
|
||||
|
||||
class Long(models.Model):
|
||||
#author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
|
||||
#image = models.ImageField()
|
||||
# author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
|
||||
# image = models.ImageField()
|
||||
history = 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(
|
||||
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 \"...\""
|
||||
help_text='Associated condition. Will allow searching / filtering and tips / hints to be displayed. Conditions with spaces must be enclosed in quotes "..."',
|
||||
)
|
||||
|
||||
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_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_management = models.TextField(null=True, blank=True)
|
||||
|
||||
|
||||
DEFAULT_SITE_ID = 1
|
||||
#site = models.ManyToManyField(
|
||||
# site = models.ManyToManyField(
|
||||
# Site,
|
||||
# blank=True,
|
||||
# default=DEFAULT_SITE_ID,
|
||||
@@ -80,22 +83,23 @@ class Long(models.Model):
|
||||
verified = models.BooleanField(default=False)
|
||||
created_date = models.DateTimeField(default=timezone.now)
|
||||
published_date = models.DateTimeField(blank=True, null=True)
|
||||
author = models.ManyToManyField(settings.AUTH_USER_MODEL,
|
||||
blank=True,
|
||||
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
|
||||
author = models.ManyToManyField(
|
||||
settings.AUTH_USER_MODEL,
|
||||
blank=True,
|
||||
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 a question should be freely available to browse", default=True
|
||||
)
|
||||
|
||||
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):
|
||||
"""Returns a comma seperated text list of authors"""
|
||||
@@ -116,7 +120,6 @@ class Long(models.Model):
|
||||
exams = ", ".join(e)
|
||||
return exams
|
||||
|
||||
|
||||
def GetImages(self, feedback=False):
|
||||
qs = self.images.all()
|
||||
|
||||
@@ -128,28 +131,64 @@ class Long(models.Model):
|
||||
return images
|
||||
|
||||
def GetImageUrls(self):
|
||||
return ",".join(["http://penracourses.org.uk{}".format(i.url) for i in self.GetImages()])
|
||||
#def GetNonFeedbackQuestionImages(self):
|
||||
#return self.GetImages()
|
||||
return ",".join(
|
||||
["http://penracourses.org.uk{}".format(i.url) for i in self.GetImages()]
|
||||
)
|
||||
|
||||
# def GetNonFeedbackQuestionImages(self):
|
||||
# return self.GetImages()
|
||||
|
||||
|
||||
class LongSeriesImage(models.Model):
|
||||
image = models.ImageField(upload_to=image_directory_path, storage=image_storage)
|
||||
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):
|
||||
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, help_text="Name of the examination", 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")
|
||||
Examination,
|
||||
help_text="Name of the examination",
|
||||
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):
|
||||
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):
|
||||
"""Returns a comma seperated text list of authors"""
|
||||
if self.author:
|
||||
return self.author.all()
|
||||
else:
|
||||
return ["None"]
|
||||
if self.long:
|
||||
authors = [i for i in self.long.author.all()]
|
||||
else:
|
||||
@@ -157,15 +196,21 @@ class LongSeries(models.Model):
|
||||
|
||||
return authors
|
||||
|
||||
def get_author_display(self):
|
||||
return ", ".join(self.get_author_objects())
|
||||
|
||||
def get_examination(self):
|
||||
"""Returns a comma seperated text list of regions"""
|
||||
return str(self.examination)
|
||||
|
||||
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):
|
||||
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)
|
||||
|
||||
@@ -173,21 +218,25 @@ class LongSeries(models.Model):
|
||||
images = self.images.all()
|
||||
|
||||
if len(images) < 1:
|
||||
return 'No images', 0
|
||||
return "No images", 0
|
||||
|
||||
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"]
|
||||
return '<img src="/media/{}" />'.format(thumbnail), len(images)
|
||||
|
||||
|
||||
|
||||
def get_block(self):
|
||||
examination = self.get_examination()
|
||||
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,
|
||||
# related_name="images",
|
||||
# on_delete=models.CASCADE)
|
||||
@@ -207,47 +256,43 @@ class LongSeries(models.Model):
|
||||
|
||||
|
||||
class Note(models.Model):
|
||||
long = models.ForeignKey(Long,
|
||||
related_name="long_notes",
|
||||
on_delete=models.CASCADE,
|
||||
null=True)
|
||||
#author = models.ForeignKey(User,
|
||||
author = models.ForeignKey( settings.AUTH_USER_MODEL,
|
||||
related_name="long_notes",
|
||||
on_delete=models.CASCADE)
|
||||
long = models.ForeignKey(
|
||||
Long, related_name="long_notes", on_delete=models.CASCADE, null=True
|
||||
)
|
||||
# author = models.ForeignKey(User,
|
||||
author = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL, related_name="long_notes", on_delete=models.CASCADE
|
||||
)
|
||||
note = models.TextField()
|
||||
created_on = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
def get_absolute_url(self):
|
||||
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):
|
||||
return "{} [{}] {}".format(self.long, self.author, self.created_on)
|
||||
|
||||
|
||||
class LongCreationDefault(models.Model):
|
||||
#author = models.OneToOneField(User,
|
||||
# author = models.OneToOneField(User,
|
||||
author = models.OneToOneField(
|
||||
settings.AUTH_USER_MODEL,
|
||||
related_name="long_default",
|
||||
on_delete=models.CASCADE)
|
||||
settings.AUTH_USER_MODEL, related_name="long_default", on_delete=models.CASCADE
|
||||
)
|
||||
|
||||
#site = models.ManyToManyField(
|
||||
# site = models.ManyToManyField(
|
||||
# Site,
|
||||
# blank=True,
|
||||
# default=1,
|
||||
# help_text="Default site to use when creating a new long")
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('longs:long_create')
|
||||
return reverse("longs:long_create")
|
||||
|
||||
|
||||
class Exam(models.Model):
|
||||
name = models.CharField(max_length=200)
|
||||
exam_questions = SortedManyToManyField(
|
||||
Long, related_name="exams", blank="true"
|
||||
)
|
||||
exam_questions = SortedManyToManyField(Long, related_name="exams", blank="true")
|
||||
|
||||
active = models.BooleanField(
|
||||
help_text="If an exam should be available", default=True
|
||||
@@ -262,7 +307,8 @@ class Exam(models.Model):
|
||||
)
|
||||
|
||||
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):
|
||||
@@ -280,6 +326,7 @@ class Exam(models.Model):
|
||||
def get_question_index(self, question):
|
||||
return list(self.exam_questions.all()).index(question)
|
||||
|
||||
|
||||
class CidUserAnswer(models.Model):
|
||||
"""User answers by candidate"""
|
||||
|
||||
@@ -287,7 +334,6 @@ class CidUserAnswer(models.Model):
|
||||
Long, related_name="cid_user_answers", on_delete=models.CASCADE
|
||||
)
|
||||
|
||||
|
||||
# Answers
|
||||
answer_observations = 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_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
|
||||
exam = models.ForeignKey(
|
||||
@@ -323,7 +373,7 @@ class CidUserAnswer(models.Model):
|
||||
self.answer = self.answer.strip()
|
||||
|
||||
s = self.answer.lower()
|
||||
s = s.translate(str.maketrans('', '', string.punctuation))
|
||||
s = s.translate(str.maketrans("", "", string.punctuation))
|
||||
|
||||
self.answer_compare = s
|
||||
|
||||
|
||||
Reference in New Issue
Block a user