more fixes
This commit is contained in:
@@ -156,7 +156,6 @@ AnswerFormSet = inlineformset_factory(
|
|||||||
can_delete=True,
|
can_delete=True,
|
||||||
extra=1,
|
extra=1,
|
||||||
max_num=10,
|
max_num=10,
|
||||||
field_classes="testing",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
AnswerUpdateFormSet = inlineformset_factory(
|
AnswerUpdateFormSet = inlineformset_factory(
|
||||||
@@ -172,7 +171,6 @@ AnswerUpdateFormSet = inlineformset_factory(
|
|||||||
can_delete=True,
|
can_delete=True,
|
||||||
extra=0,
|
extra=0,
|
||||||
max_num=10,
|
max_num=10,
|
||||||
field_classes="testing",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -23,7 +23,7 @@ with open('/etc/secret_key') as f:
|
|||||||
SECRET_KEY = f.read().strip()
|
SECRET_KEY = f.read().strip()
|
||||||
|
|
||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
# 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"]
|
ALLOWED_HOSTS = ["localhost", "127.0.0.1", "161.35.163.87", "penracourses.org.uk"]
|
||||||
|
|
||||||
|
|||||||
+14
-1
@@ -1,5 +1,6 @@
|
|||||||
from django.contrib import admin
|
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
|
#import tagulous.admin
|
||||||
|
|
||||||
from django.forms import ModelForm
|
from django.forms import ModelForm
|
||||||
@@ -10,10 +11,16 @@ from django.forms.widgets import RadioSelect
|
|||||||
# Register your models here.
|
# Register your models here.
|
||||||
admin.site.register(Examination)
|
admin.site.register(Examination)
|
||||||
admin.site.register(Site)
|
admin.site.register(Site)
|
||||||
|
admin.site.register(Exam)
|
||||||
admin.site.register(Abnormality)
|
admin.site.register(Abnormality)
|
||||||
admin.site.register(Region)
|
admin.site.register(Region)
|
||||||
admin.site.register(Note)
|
admin.site.register(Note)
|
||||||
admin.site.register(RapidCreationDefault)
|
admin.site.register(RapidCreationDefault)
|
||||||
|
admin.site.register(Answer)
|
||||||
|
|
||||||
|
class RapidAnswersInline(admin.TabularInline):
|
||||||
|
model = Answer
|
||||||
|
extra = 1
|
||||||
|
|
||||||
|
|
||||||
class RapidImageInline(admin.TabularInline):
|
class RapidImageInline(admin.TabularInline):
|
||||||
@@ -23,6 +30,10 @@ class RapidImageInline(admin.TabularInline):
|
|||||||
"image_tag",
|
"image_tag",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
class ExamInline(admin.TabularInline):
|
||||||
|
model = Rapid.exams.through
|
||||||
|
extra = 1
|
||||||
|
|
||||||
|
|
||||||
class RapidAdminForm(ModelForm):
|
class RapidAdminForm(ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
@@ -52,6 +63,8 @@ class RapidAdmin(VersionAdmin):
|
|||||||
|
|
||||||
inlines = [
|
inlines = [
|
||||||
RapidImageInline,
|
RapidImageInline,
|
||||||
|
RapidAnswersInline,
|
||||||
|
ExamInline,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+32
-2
@@ -1,10 +1,10 @@
|
|||||||
from django.forms import ModelForm, ModelMultipleChoiceField, ModelChoiceField, ChoiceField
|
from django.forms import ModelForm, ModelMultipleChoiceField, ModelChoiceField, ChoiceField
|
||||||
from django.forms import inlineformset_factory
|
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.contrib.admin.widgets import FilteredSelectMultiple
|
||||||
from django.forms.widgets import RadioSelect
|
from django.forms.widgets import RadioSelect, TextInput, Textarea
|
||||||
|
|
||||||
|
|
||||||
class ExaminationForm(ModelForm):
|
class ExaminationForm(ModelForm):
|
||||||
@@ -100,3 +100,33 @@ ImageFormSet = inlineformset_factory(Rapid,
|
|||||||
extra=4,
|
extra=4,
|
||||||
max_num=10,
|
max_num=10,
|
||||||
field_classes="testing")
|
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,
|
||||||
|
)
|
||||||
+111
-1
@@ -129,7 +129,7 @@ class Rapid(models.Model):
|
|||||||
abnormality = models.ManyToManyField(
|
abnormality = models.ManyToManyField(
|
||||||
Abnormality,
|
Abnormality,
|
||||||
blank=True,
|
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 = models.ManyToManyField(
|
||||||
Region,
|
Region,
|
||||||
@@ -310,3 +310,113 @@ class RapidCreationDefault(models.Model):
|
|||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('rapids:rapid_create')
|
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
|
||||||
Reference in New Issue
Block a user