This commit is contained in:
Ross
2021-02-16 17:09:02 +00:00
parent a0b9e92aba
commit 4f60fccb06
10 changed files with 188 additions and 180 deletions
+11
View File
@@ -458,4 +458,15 @@ td.user-answer-score-2::after {
.temp-thumb {
float:right;
}
.long-answer pre {
white-space: pre-wrap;
font: inherit;
color: inherit;
}
.long-answer .answer-heading {
font-style: italic;
font-weight: bold;
}
+4 -5
View File
@@ -34,11 +34,10 @@ class LongAnswerForm(ModelForm):
fields = ("answer_observations","answer_interpretation","answer_principle_diagnosis","answer_differential_diagnosis","answer_management",)
class MarkLongQuestionForm(Form):
# correct = forms.CharField(required=False)
# half_correct = forms.CharField(required=False)
# incorrect = forms.CharField(required=False)
marked_answers = CharField(required=False)
class MarkLongQuestionForm(ModelForm):
class Meta:
model = CidUserAnswer
fields = ["score"]
class ExaminationForm(ModelForm):
@@ -0,0 +1,18 @@
# Generated by Django 3.1.3 on 2021-02-16 13:15
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('longs', '0022_long_mark_scheme'),
]
operations = [
migrations.AlterField(
model_name='ciduseranswer',
name='score',
field=models.CharField(blank=True, choices=[('', 'Unmarked'), ('4', '4'), ('4.5', '4.5'), ('5', '5'), ('5.5', '5.5'), ('6', '6'), ('6.5', '6.5'), ('7', '7'), ('7.5', '7.5'), ('8', '8')], default='', max_length=3),
),
]
+4
View File
@@ -151,6 +151,7 @@ class Long(models.Model):
answers = self.cid_user_answers.all()
return [ans for ans in answers if not ans.is_marked()]
# def GetNonFeedbackQuestionImages(self):
# return self.GetImages()
@@ -422,6 +423,9 @@ class CidUserAnswer(models.Model):
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['cid']
def __str__(self):
try:
exam = self.exam
@@ -32,6 +32,8 @@
<p class="pre-whitespace"><b>Principle Diagnosis:</b> {{ question.model_principle_diagnosis }}</p>
<p class="pre-whitespace"><b>Differential Diagnosis:</b> {{ question.model_differential_diagnosis }}</p>
<p class="pre-whitespace"><b>Managment:</b> {{ question.model_management }}</p>
<p class="pre-whitespace"><b>Mark Scheme:</b> {{ question.mark_scheme }}</p>
</p>
<p><b>Exam(s):</b> {{question.GetExams}}</p>
</div>
+33 -57
View File
@@ -1,67 +1,43 @@
{% extends 'longs/exams.html' %}
{% block content %}
<h2>Marking question {{question_details.current}} of {{question_details.total}}</h2>
<a href="{% url 'longs:exam_question_detail' exam.id question_details.current %}" title="View the Question">View</a>
<a href="{% url 'longs:long_update' question.id %}" title="Edit the Question">Edit</a> <a
href="{% url 'admin:longs_long_change' question.id %}" title="Edit the Question using the admin interface">Admin
Edit</a>
{% if question.normal %}
<h3>This question is normal</h3>
Answers will be automatically marked.
{% else %}
<h3>This question is abnormal</h3>
Answers marked as normal will be automatically marked.
{% endif %}
<div id="single-dicom-viewer" class="marking-dicom" data-images="{{question.GetImageUrls}}"
data-annotations='{{question.image_annotations}}'>
</div>
<div class="marking">
<form method="POST" class="post-form">{% csrf_token %}
{% if not question.normal %}
Click each answer to toggle through marks awarded (as per colour)
<div class="marking-list">
Unmarked:
<ul id="new-answer-list" class="answer-list">
{% for answer in user_answers %}
<li>
<pre><span class="answer not-marked">{{ answer }}</span></pre>
</li>
{% endfor %}
</ul>
Marked:
<ul id="marked-answer-list" class="answer-list">
{% for answer in correct_answers %}
<li>
<pre><span class="answer correct">{{ answer }}</span></pre>
</li>
{% endfor %}
{% for answer in half_mark_answers %}
<li>
<pre><span class="answer half-correct">{{ answer }}</span></pre>
</li>
{% endfor %}
{% for answer in incorrect_answers %}
<li>
<pre><span class="answer incorrect">{{ answer }}</span></pre>
</li>
{% endfor %}
</ul>
<div class="answer-list key">Key: <span class="correct">2 Marks</span>, <span class="half-correct">1
Mark</span>, <span class="incorrect">0 Marks</span></div>
</div>
{% endif %}
{% if question_details.current > 1 %}
<button type="submit" name="previous" class="save btn btn-default">Previous</button>
{% endif %}
<button type="submit" name="save" class="save btn btn-default">Save</button>
{% if question_details.current >= question_details.total %}
<h2>Marking question {{question_details.current}} of {{question_details.total}}</h2>
<div>
{% if not user_answers %}
No answers to mark.
{% else %}
Answers:
<ul>
{% for answer in user_answers %}
<li>
<a href="{% url 'longs:mark_answer' exam.id question_details.current|add:'-1' answer.cid %}"
title="Click to mark"> {{answer.cid}}</a>:
{% if answer.is_marked %}
Score {{answer.get_answer_score}}
{% else %}
<button type="submit" name="next" class="save btn btn-default">Next</button>
<button type="submit" name="skip" class="save btn btn-default">Skip</button>
Unmarked
{% endif %}
<span class=hide>
{{ form.as_p }}
</span>
</form>
</li>
{% endfor %}
</ul>
{% endif %}
<div>
{% if question_details.current > 1 %}
<a href="{% url 'longs:mark' exam.id question_details.current|add:'-2' %}"
title="Previous question">Previous</a>
{% endif %}
{% if question_details.current >= question_details.total %}
{% else %}
<a href="{% url 'longs:mark' exam.id question_details.current %}" title="Next question">Next</a>
{% endif %}
</div>
</div>
{% endblock %}
+45
View File
@@ -0,0 +1,45 @@
{% extends 'longs/exams.html' %}
{% block content %}
<a href="{% url 'longs:exam_question_detail' exam.id question_details.current %}" title="View the Question">View</a>
<a href="{% url 'longs:long_update' question.id %}" title="Edit the Question">Edit</a> <a
href="{% url 'admin:longs_long_change' question.id %}" title="Edit the Question using the admin interface">Admin
Edit</a>
<h2>Marking question <a href="{% url 'longs:mark' exam.id question_details.current|add:'-1' %}"
title="View question answers">{{question_details.current}}</a> of {{question_details.total}}</h2>
<div>
<div class="long-answer">
<span class="answer-heading">User observations:</span>
<pre>{{answer.answer_observations}}</pre>
</div>
<div class="long-answer">
<span class="answer-heading">User interpretation:</span>
<pre>{{answer.answer_interpretation}}</pre>
</div>
<div class="long-answer">
<span class="answer-heading">User principle diagnosis:</span>
<pre>{{answer.answer_principle_diagnosis}}</pre>
</div>
<div class="long-answer">
<span class="answer-heading">User differential diagnosis:</span>
<pre>{{answer.answer_differential_diagnosis}}</pre>
</div>
<div class="long-answer">
<span class="answer-heading">User management:</span>
<pre>{{answer.answer_management}}</pre>
</div>
</div>
<div class="marking">
<form method="POST" class="post-form">{% csrf_token %}
{{ form.as_p }}
<button type="submit" name="save" class="save btn btn-default">Save</button>
{% if next_unmarked_id %}
<button type="submit" name="next" class="save btn btn-default">Next</button>
<!-- <button type="submit" name="skip" class="save btn btn-default">Skip</button> -->
{% else %}
(no more answers for this question to mark)
{% endif %}
</form>
</div>
{% endblock %}
+1
View File
@@ -19,6 +19,7 @@ urlpatterns = [
# path("verified/", views.verified, name="verified"),
# path("all_questions/", views.all_questions, name="all_questions"),
path("question/<int:pk>/scrap", views.long_scrap, name="long_scrap"),
path("exam/<int:pk>/<int:sk>/<int:tk>/mark", views.mark_answer, name="mark_answer"),
path("exam/<int:pk>/<int:sk>/mark", views.mark, name="mark"),
path("exam/<int:pk>/mark", views.LongExamViews.mark_overview, name="mark_overview"),
# path("exam/<int:pk>/<int:sk>/", views.exam_take, name="exam_take"),
+70 -118
View File
@@ -5,7 +5,7 @@ from django import forms
# from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required, user_passes_test
from django.contrib.auth.models import User
from django.core.exceptions import PermissionDenied
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied, ViewDoesNotExist
from django.contrib.auth.mixins import LoginRequiredMixin
@@ -91,7 +91,7 @@ def long_detail(request, pk):
# logging.debug(long.long_notes.first())
# logging.debug(long.subspecialty.first().name.all())
return render(request, "longs/long_detail.html", {"question": long})
return render(request, "longs/question_detail.html", {"question": long})
@login_required
def long_series_detail(request, pk):
@@ -139,7 +139,7 @@ def long_split(request, pk):
# logging.debug(long.long_notes.first())
# logging.debug(long.subspecialty.first().name.all())
return render(request, "longs/long_detail.html", {"question": long})
return render(request, "longs/question_detail.html", {"question": long})
@login_required
@@ -484,7 +484,7 @@ def long_scrap(request, pk):
long.scrapped = not long.scrapped
long.save()
return HttpResponseRedirect(reverse("longs:long_detail", args=(pk,)))
return HttpResponseRedirect(reverse("longs:question_detail", args=(pk,)))
# @login_required
@@ -656,6 +656,70 @@ def loadJsonAnswer(answer):
return True
@login_required
def mark_answer(request, pk, sk, tk):
exam = get_object_or_404(Exam, pk=pk)
questions = exam.exam_questions.all()
n = sk
question_details = {
"total": len(questions),
"current": n + 1,
}
try:
question = questions[sk]
except IndexError:
raise Http404("Exam question does not exist")
try:
answer = question.cid_user_answers.get(cid=tk)
except ObjectDoesNotExist:
raise Http404("User answer does not exist")
try:
unmarked = question.GetUnmarkedAnswers()
next_unmarked_id = unmarked[0].cid
except IndexError:
next_unmarked_id = False
if request.method == "POST":
form = MarkLongQuestionForm(request.POST)
if form.is_valid():
# If skip button is pressed skip the question without marking
# Skip is problematic if we skip to the next unmarked answer
#if "skip" in request.POST:
# return redirect("longs:mark_answer", pk=pk, sk=sk, tk=next_unmarked_id)
# Extract score from form and save it to the object
answer.score = form.cleaned_data["score"]
answer.save()
if "next" in request.POST:
return redirect("longs:mark_answer", pk=pk, sk=sk, tk=next_unmarked_id)
#elif "previous" in request.POST:
# return redirect("longs:mark", pk=pk, sk=n - 1)
else:
form = MarkLongQuestionForm(initial={'score': answer.score})
return render(
request,
"longs/mark_answer.html",
{
"exam": exam,
"form": form,
"answer": answer,
"question": question,
"question_details": question_details,
"next_unmarked_id": next_unmarked_id,
},
)
# @user_passes_test(user_is_admin, login_url="/accounts/login")
@login_required
@@ -676,129 +740,17 @@ def mark(request, pk, sk):
except IndexError:
raise Http404("Exam question does not exist")
answers_dict = {}
for ans in question.answers.all():
answers_dict[ans.answer_compare] = ans
marked_answers_set = set(answers_dict.keys())
# correct_answers = [i.answer.lower() for i in question.answers.all()]
# half_correct_answers = [
# i.answer.lower() for i in question.half_mark_answers.all()
# ]
# incorrect_answers = [
# i.answer.lower() for i in question.incorrect_answers.all()
# ]
unmarked_user_answers = question.GetUnmarkedAnswers()
if request.method == "POST":
form = MarkLongQuestionForm(request.POST)
# *******************************
# TODO: convert to JSON request
# *******************************
if form.is_valid():
# If skip button is pressed skip the question without marking
if "skip" in request.POST:
return redirect("longs:mark", pk=pk, sk=n + 1)
cd = form.cleaned_data
# correct = cd.get("correct")
# half_correct = cd.get("half_correct")
# incorrect = cd.get("incorrect")
marked_answers = json.loads(cd.get("marked_answers"))
## This is mildly dangerous as we delete all answers
# question.answers.all().delete()
# question.half_mark_answers.all().delete()
# question.incorrect_answers.all().delete()
# This should probably use json (or something else...)
# ajax.....
for ans in marked_answers["correct"]:
ans = ans.strip()
if ans == "":
continue
a = Answer.objects.filter(
answer__iexact=ans, question_id=question.pk
).first()
if a is None:
a = Answer()
a.question_id = question.pk
a.answer = ans
a.status = Answer.MarkOptions.CORRECT
a.save()
# for ans in half_correct.split("--//--"):
for ans in marked_answers["half-correct"]:
ans = ans.strip()
if ans == "":
continue
a = Answer.objects.filter(
answer__iexact=ans, question_id=question.pk
).first()
if a is None:
a = Answer()
a.question_id = question.pk
a.answer = ans
a.status = Answer.MarkOptions.HALF_MARK
a.save()
for ans in marked_answers["incorrect"]:
ans = ans.strip()
if ans == "":
continue
a = Answer.objects.filter(
answer__iexact=ans, question_id=question.pk
).first()
if a is None:
a = Answer()
a.question_id = question.pk
a.answer = ans
a.status = Answer.MarkOptions.INCORRECT
a.save()
# answer = form.save(commit=False)
# answer.user = request.user
# answer.question = question
# answer.published_date = timezone.now()
# answer.save()
if "next" in request.POST:
return redirect("longs:mark", pk=pk, sk=n + 1)
elif "previous" in request.POST:
return redirect("longs:mark", pk=pk, sk=n - 1)
# Reset user answers (relies on the functional javascript)
unmarked_user_answers = set()
else:
form = MarkLongQuestionForm()
correct_answers = question.answers.filter(status=Answer.MarkOptions.CORRECT)
half_mark_answers = question.answers.filter(status=Answer.MarkOptions.HALF_MARK)
incorrect_answers = question.answers.filter(status=Answer.MarkOptions.INCORRECT)
user_answers = question.cid_user_answers.all()
return render(
request,
"longs/mark.html",
{
"exam": exam,
"form": form,
"question": question,
"question_details": question_details,
"user_answers": unmarked_user_answers,
"correct_answers": correct_answers,
"half_mark_answers": half_mark_answers,
"incorrect_answers": incorrect_answers,
"user_answers": user_answers,
},
)