From 558bd69ee5da761573867034ab92fadc709544d7 Mon Sep 17 00:00:00 2001 From: Ross Date: Mon, 18 Jan 2021 14:00:37 +0000 Subject: [PATCH] more fixes --- anatomy/forms.py | 2 - rad/settings.py | 2 +- rapids/admin.py | 15 ++++++- rapids/forms.py | 34 +++++++++++++- rapids/models.py | 112 ++++++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 158 insertions(+), 7 deletions(-) diff --git a/anatomy/forms.py b/anatomy/forms.py index f57e961a..35672a64 100644 --- a/anatomy/forms.py +++ b/anatomy/forms.py @@ -156,7 +156,6 @@ AnswerFormSet = inlineformset_factory( can_delete=True, extra=1, max_num=10, - field_classes="testing", ) AnswerUpdateFormSet = inlineformset_factory( @@ -172,7 +171,6 @@ AnswerUpdateFormSet = inlineformset_factory( can_delete=True, extra=0, max_num=10, - field_classes="testing", ) diff --git a/rad/settings.py b/rad/settings.py index bb1647df..591ffbbe 100644 --- a/rad/settings.py +++ b/rad/settings.py @@ -23,7 +23,7 @@ with open('/etc/secret_key') as f: SECRET_KEY = f.read().strip() # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = False +DEBUG = True ALLOWED_HOSTS = ["localhost", "127.0.0.1", "161.35.163.87", "penracourses.org.uk"] diff --git a/rapids/admin.py b/rapids/admin.py index a00dba5e..0a0f6d14 100755 --- a/rapids/admin.py +++ b/rapids/admin.py @@ -1,5 +1,6 @@ from django.contrib import admin -from .models import Rapid, RapidImage, Examination, Site, Abnormality, Region, Note, RapidCreationDefault +from .models import Rapid, RapidImage, Examination, Site, Abnormality, Region, Note, RapidCreationDefault, Answer, Exam + #import tagulous.admin from django.forms import ModelForm @@ -10,10 +11,16 @@ from django.forms.widgets import RadioSelect # Register your models here. admin.site.register(Examination) admin.site.register(Site) +admin.site.register(Exam) admin.site.register(Abnormality) admin.site.register(Region) admin.site.register(Note) admin.site.register(RapidCreationDefault) +admin.site.register(Answer) + +class RapidAnswersInline(admin.TabularInline): + model = Answer + extra = 1 class RapidImageInline(admin.TabularInline): @@ -23,6 +30,10 @@ class RapidImageInline(admin.TabularInline): "image_tag", ] +class ExamInline(admin.TabularInline): + model = Rapid.exams.through + extra = 1 + class RapidAdminForm(ModelForm): class Meta: @@ -52,6 +63,8 @@ class RapidAdmin(VersionAdmin): inlines = [ RapidImageInline, + RapidAnswersInline, + ExamInline, ] diff --git a/rapids/forms.py b/rapids/forms.py index afcc2863..73f15940 100755 --- a/rapids/forms.py +++ b/rapids/forms.py @@ -1,10 +1,10 @@ from django.forms import ModelForm, ModelMultipleChoiceField, ModelChoiceField, ChoiceField from django.forms import inlineformset_factory -from rapids.models import Abnormality, Examination, Note, Rapid, RapidCreationDefault, RapidImage, Region +from rapids.models import Abnormality, Examination, Note, Rapid, RapidCreationDefault, RapidImage, Region, Answer from django.contrib.admin.widgets import FilteredSelectMultiple -from django.forms.widgets import RadioSelect +from django.forms.widgets import RadioSelect, TextInput, Textarea class ExaminationForm(ModelForm): @@ -100,3 +100,33 @@ ImageFormSet = inlineformset_factory(Rapid, extra=4, max_num=10, field_classes="testing") + +AnswerFormSet = inlineformset_factory( + Rapid, + Answer, + fields=["answer", "status"], + widgets={ + "answer": Textarea( + attrs={"required": "true", "minlength": 2, "maxlength": 500, "rows": 3} + ) + }, + exclude=[], + can_delete=True, + extra=1, + max_num=10, +) + +AnswerUpdateFormSet = inlineformset_factory( + Rapid, + Answer, + fields=["answer", "status"], + widgets={ + "answer": Textarea( + attrs={"required": "true", "minlength": 2, "maxlength": 500, "rows": 3} + ) + }, + exclude=[], + can_delete=True, + extra=0, + max_num=10, +) \ No newline at end of file diff --git a/rapids/models.py b/rapids/models.py index 0a07d1da..3559b98d 100644 --- a/rapids/models.py +++ b/rapids/models.py @@ -129,7 +129,7 @@ class Rapid(models.Model): abnormality = models.ManyToManyField( Abnormality, blank=True, - help_text="The abnormality (laterality and region independent)", + help_text="The abnormality (laterality and region independent). Used for categorisation but does not affect the answer", ) region = models.ManyToManyField( Region, @@ -310,3 +310,113 @@ class RapidCreationDefault(models.Model): def get_absolute_url(self): return reverse('rapids:rapid_create') + +class Exam(models.Model): + name = models.CharField(max_length=200) + exam_questions = SortedManyToManyField( + Rapid, related_name="exams", blank="true" + ) + + active = models.BooleanField( + help_text="If an exam should be available", default=True + ) + + publish_results = models.BooleanField( + help_text="If an exams results should be available", default=True + ) + + recreate_json = models.BooleanField( + help_text="If the json cache needs updating", default=False + ) + + time_limit = models.IntegerField( + help_text="Exam time limit (in seconds)", default=5400 + ) + + def __str__(self): + return self.name + + def get_absolute_url(self): + return reverse("rapids:exam_overview", kwargs={"pk": self.pk}) + + def get_exam_name(self): + return self.name + + def get_json_url(self): + return reverse("rapids:exam_json", args=(self.pk,)) + + def get_question_index(self, question): + return list(self.exam_questions.all()).index(question) + +class CidUserAnswer(models.Model): + """User answers by candidate""" + + question = models.ForeignKey( + Rapid, related_name="cid_user_answers", on_delete=models.CASCADE + ) + answer = models.TextField(max_length=500, blank=True) + + answer_compare = models.TextField(max_length=500, blank=True) + + 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( + Exam, related_name="cid_user_answers", on_delete=models.CASCADE, null=True + ) + + created = models.DateTimeField(auto_now_add=True) + updated = models.DateTimeField(auto_now=True) + + def __str__(self): + try: + exam = self.exam + except (Exam.DoesNotExist, KeyError) as e: + exam = "None" + return "{}/{}/{}: {}".format( + exam, self.cid, self.question.GetPrimaryAnswer(), self.answer + ) + + def save(self, *args, **kwargs): + self.clean() + return super(CidUserAnswer, 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 + + # def get_compare_string(self): + # # strip here should be unneccasry (providing clean is now working) + # s = self.answer.lower().strip() + # s = s.translate(str.maketrans('', '', string.punctuation)) + # return s + + def get_answer_score(self): + q = self.question + ans = self.answer_compare + if ( + q.answers.filter( + answer_compare__iexact=ans, status=Answer.MarkOptions.CORRECT + ).first() + is not None + ): + mark = 2 + elif ( + q.answers.filter( + answer_compare__iexact=ans, status=Answer.MarkOptions.HALF_MARK + ).first() + is not None + ): + mark = 1 + elif q.answers.filter( + answer_compare__iexact=ans, status=Answer.MarkOptions.INCORRECT + ).first(): + mark = 0 + else: + mark = "unmarked" + return mark \ No newline at end of file