Files
penracourses/rapids/models.py
T
2021-01-18 13:40:28 +00:00

313 lines
9.4 KiB
Python

from django.db import models
from django.utils import timezone
from django.core.files.storage import FileSystemStorage
from django.conf import settings
from django.utils.html import format_html
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
from sortedm2m.fields import SortedManyToManyField
import string
image_storage = FileSystemStorage(
# Physical file location ROOT
location=u"{0}/rapids/".format(settings.MEDIA_ROOT),
# Url for file
base_url=u"{0}rapids/".format(settings.MEDIA_URL),
)
def image_directory_path(instance, filename):
return u"picture/{0}".format(filename)
class Abnormality(models.Model):
name = models.CharField(max_length=200,
unique=True,
help_text="Primary abnormality on the film(s)")
def __str__(self):
return self.name
class Meta:
ordering = ('name', )
class Region(models.Model):
name = models.CharField(
max_length=200,
unique=True,
help_text="Region of the abnormality (not including lateralitiy)",
blank=True)
def __str__(self):
return self.name
class Meta:
ordering = ('name', )
class Examination(models.Model):
examination = models.CharField(max_length=200)
def __str__(self):
return self.examination
class Meta:
ordering = ('examination', )
class Site(models.Model):
site = models.CharField(max_length=200)
initials = models.CharField(max_length=200)
def __str__(self):
return self.site
#class Condition(tagulous.models.TagModel):
# pass
class Answer(models.Model):
question = models.ForeignKey(
"Rapid", related_name="answers", on_delete=models.CASCADE
)
answer = models.TextField(max_length=500)
answer_compare = models.TextField(max_length=500)
class MarkOptions(models.TextChoices):
UNMARKED = "", _("Unmarked")
INCORRECT = "0", _("Incorrect")
HALF_MARK = "1", _("Half mark")
CORRECT = "2", _("Correct")
status = models.CharField(
max_length=1, choices=MarkOptions.choices, default=MarkOptions.UNMARKED
)
def __str__(self):
return self.answer
def save(self, *args, **kwargs):
self.clean()
return super(Answer, self).save(*args, **kwargs)
def clean(self):
if self.answer:
self.answer = self.answer.strip()
s = self.answer.lower()
s = s.translate(str.maketrans('', '', string.punctuation))
self.answer_compare = s
class Rapid(models.Model):
#author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
#image = models.ImageField()
question = models.TextField(null=True, blank=True)
feedback = models.TextField(null=True, blank=True)
normal = models.BooleanField(default=False, help_text="Tick if true")
NONE = "NONE"
LEFT = "LEFT"
RIGHT = "RIGHT"
BILATERAL = "BILAT"
LATERALITY_CHOICES = (
(NONE, "None"),
(LEFT, "Left"),
(RIGHT, "Right"),
(BILATERAL, "Bilateral"),
)
abnormality = models.ManyToManyField(
Abnormality,
blank=True,
help_text="The abnormality (laterality and region independent)",
)
region = models.ManyToManyField(
Region,
blank=True,
help_text="Region of the abnormality (laterality independent)")
examination = models.ManyToManyField(
Examination, help_text="Name of the (primary) examination")
laterality = models.CharField(
max_length=20,
choices=LATERALITY_CHOICES,
default=NONE,
help_text="Applies to the answer, not the examination")
# 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 \"...\""
# )
#
# sign = tagulous.models.TagField(
# to=Sign, blank=True, help_text='Radiological signs in the question')
DEFAULT_SITE_ID = 1
site = models.ManyToManyField(
Site,
blank=True,
default=DEFAULT_SITE_ID,
help_text=
"If we know the source of the image (should possibly be removed?)")
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="rapid_authored_questions")
scrapped = models.BooleanField(
default=False,
help_text='Question has been scrapped and will not be shown')
def get_absolute_url(self):
return reverse('rapids:rapid_detail', kwargs={'pk': self.pk})
def get_authors(self):
"""Returns a comma seperated text list of authors"""
authors = ", ".join([i.username for i in self.author.all()])
return authors
def get_regions(self):
"""Returns a comma seperated text list of regions"""
regions = ", ".join([i.name for i in self.region.all()])
return regions
def get_abnormalities(self):
"""Returns a comma seperated text list of regions"""
abnormalities = ", ".join([i.name for i in self.abnormality.all()])
return abnormalities
def __str__(self):
n = "Normal"
if not self.normal:
#n = self.answers.first()
n = "{} / {}".format(self.abnormality.first(), self.region.first())
exams = ", ".join([str(i) for i in self.examination.all()])
lat = ""
if self.laterality != "NONE":
lat = self.laterality
return "{} / {} {}".format(exams, lat, n)
def GetPrimaryAnswer(self):
if len(self.answers.all()) > 0:
return self.answers.all().first().answer
else:
return "None yet..."
def GetExams(self):
e = self.exams.all().values_list("name", flat=True)
exams = ", ".join(e)
return exams
def GetUnmarkedAnswersString(self):
unmarked_answers = self.GetUnmarkedAnswers()
if not unmarked_answers:
return "No answers to mark"
return format_html(
"<span class='warn'>{} answer unmarked:</span> {}".format(
len(unmarked_answers), ", ".join(unmarked_answers)
)
)
def GetUnmarkedAnswers(self):
user_answers = set(
[i.answer_compare for i in self.cid_user_answers.all()]
)
unmarked_answers = user_answers - self.GetMarkedAnswers()
return unmarked_answers
def GetUnmarkedAnswerCount(self):
return len(self.GetUnmarkedAnswers())
def GetMarkedAnswers(self):
return set(
[
i.answer_compare
for i in self.answers.all()
if i.status != i.MarkOptions.UNMARKED
]
)
correct_answers = set(
[i.answer.get_compare_string() for i in self.answers.all()]
)
half_mark_answers = set(
[i.answer.get_compare_string() for i in self.half_mark_answers.all()]
)
incorrect_answers = set(
[i.answer.get_compare_string() for i in self.incorrect_answers.all()]
)
marked_answers = correct_answers | half_mark_answers | incorrect_answers
return marked_answers
class RapidImage(models.Model):
rapid = models.ForeignKey(Rapid,
related_name="images",
on_delete=models.CASCADE)
image = models.ImageField(upload_to=image_directory_path, storage=image_storage)
feedback_image = models.BooleanField(default=False)
def image_tag(self):
if self.image:
return mark_safe(
'<img src="{}{}" class="admin-rapid-image" /><span class="admin-rapid-image-info">Click and hold to zoom<span>'
.format(settings.MEDIA_URL, self.image))
else:
return ""
image_tag.short_description = 'Image'
class Note(models.Model):
rapid = models.ForeignKey(Rapid,
related_name="rapid_notes",
on_delete=models.CASCADE,
null=True)
#author = models.ForeignKey(User,
author = models.ForeignKey( settings.AUTH_USER_MODEL,
related_name="rapid_notes",
on_delete=models.CASCADE)
note = models.TextField()
created_on = models.DateTimeField(auto_now_add=True)
def get_absolute_url(self):
self.pk = self.rapid_id
return reverse('rapids:rapid_detail', kwargs={'pk': self.pk})
def __str__(self):
return "{} [{}] {}".format(self.rapid, self.author, self.created_on)
class RapidCreationDefault(models.Model):
#author = models.OneToOneField(User,
author = models.OneToOneField(
settings.AUTH_USER_MODEL,
related_name="rapid_default",
on_delete=models.CASCADE)
site = models.ManyToManyField(
Site,
blank=True,
default=1,
help_text="Default site to use when creating a new rapid")
def get_absolute_url(self):
return reverse('rapids:rapid_create')