.
This commit is contained in:
@@ -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
@@ -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),
|
||||
),
|
||||
]
|
||||
@@ -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>
|
||||
|
||||
@@ -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 %}
|
||||
@@ -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 %}
|
||||
@@ -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
@@ -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,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user