improve suggest answers (and fix some other issues)
This commit is contained in:
+6
-1
@@ -252,4 +252,9 @@ class ExamMarkerForm(ExamMarkerFormMixin):
|
||||
|
||||
class ExamGroupsForm(ExamGroupsFormMixin):
|
||||
class Meta(ExamGroupsFormMixin.Meta):
|
||||
model = Exam
|
||||
model = Exam
|
||||
|
||||
class SuggestIncorrectAnswerForm(ModelForm):
|
||||
class Meta:
|
||||
model = AnatomyQuestion
|
||||
fields = ["answer_suggest_incorrect"]
|
||||
@@ -340,6 +340,9 @@ class Answer(models.Model):
|
||||
def get_answer_url_safe(self):
|
||||
return urllib.parse.quote(self.answer_compare)
|
||||
|
||||
def get_constituent_words(self):
|
||||
return self.answer_compare.split()
|
||||
|
||||
# def get_compare_string(self):
|
||||
# s = self.answer.lower().strip()
|
||||
# s = s.translate(str.maketrans('', '', string.punctuation))
|
||||
|
||||
@@ -15,7 +15,28 @@
|
||||
{% if question.answer_help %}
|
||||
<details>
|
||||
<summary>Marking help:</summary>
|
||||
<div class="border border-secondary">
|
||||
{{question.answer_help|safe}}
|
||||
</div>
|
||||
</details>
|
||||
<details>
|
||||
<summary>Suggest incorrect answers:</summary>
|
||||
|
||||
{% for word in words_suggest_incorrect %}
|
||||
<span>{{word}}<span class="user-select-none" _="on click if #id_answer_suggest_incorrect == null then
|
||||
set btn to #answer-suggest-incorrect-button
|
||||
js(btn) btn.click() end
|
||||
repeat forever
|
||||
if #id_answer_suggest_incorrect != null then break end
|
||||
wait 0.1s
|
||||
end
|
||||
end
|
||||
put (#id_answer_suggest_incorrect's value + ',' + my @data-word) into #id_answer_suggest_incorrect's value then remove closest parent <span/>" data-word='{{word}}'>(+)</span>,</span>
|
||||
{% endfor %}
|
||||
|
||||
<div class="border border-secondary">
|
||||
{% include "anatomy/question_detail.html#suggest-incorrect-form" %}
|
||||
</div>
|
||||
</details>
|
||||
|
||||
{% endif %}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{% extends 'anatomy/base.html' %}
|
||||
|
||||
{% load partials %}
|
||||
{% block content %}
|
||||
{% load static %}
|
||||
{% include 'anatomy/question_link_header.html' %}
|
||||
@@ -58,7 +58,14 @@
|
||||
Answer help: {{ question.answer_help|safe }}
|
||||
</div>
|
||||
<div>
|
||||
Answer suggest incorrect: {{ question.answer_suggest_incorrect }}
|
||||
{% partialdef suggest-incorrect-form inline %}
|
||||
<span id="incorrect-answers">
|
||||
Answer suggest incorrect: {{ question.answer_suggest_incorrect }}
|
||||
<button id="answer-suggest-incorrect-button" class="btn btn-sm" hx-get="{% url 'anatomy:question_suggest_incorrect_answers' question.pk %}"
|
||||
hx-target="#incorrect-answers"
|
||||
hx-swap="innerHTML">Edit suggestions</button>
|
||||
</span>
|
||||
{% endpartialdef %}
|
||||
</div>
|
||||
<div>
|
||||
Description: {{ question.description }}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{% load crispy_forms_tags %}
|
||||
<form method="POST" class="post-form">{% csrf_token %}
|
||||
{{ form | crispy}}
|
||||
<button type="button" class="btn btn-sm" hx-post="{% url 'anatomy:question_suggest_incorrect_answers' question.id %}"
|
||||
hx-target="#incorrect-answers"
|
||||
hx-swap="innerHTML">Save</button>
|
||||
<button type="button" _="on click remove closest <form/>" >Cancel</button>
|
||||
</form>
|
||||
@@ -33,6 +33,12 @@ urlpatterns = [
|
||||
views.question_save_annotation,
|
||||
name="question_save_annotation",
|
||||
),
|
||||
path(
|
||||
"question/<int:pk>/suggest_incorrect_answers",
|
||||
views.question_suggest_incorrect_answers,
|
||||
name="question_suggest_incorrect_answers",
|
||||
),
|
||||
|
||||
path("question/<int:pk>/answer/", views.answer_question, name="answer_question"),
|
||||
path(
|
||||
"question/<int:pk>/clone", views.QuestionClone.as_view(), name="question_clone"
|
||||
|
||||
@@ -44,6 +44,7 @@ from .forms import (
|
||||
MarkAnatomyQuestionForm,
|
||||
AnatomyQuestionForm,
|
||||
ExamForm,
|
||||
SuggestIncorrectAnswerForm,
|
||||
)
|
||||
from .models import (
|
||||
AnatomyQuestion,
|
||||
@@ -345,6 +346,10 @@ def mark(request, exam_pk, sk, unmarked_exam_answers_only=True, review=False):
|
||||
incorrect_answers = question.answers.filter(status=Answer.MarkOptions.INCORRECT)
|
||||
|
||||
answer_suggest_incorrect: list = []
|
||||
|
||||
words_present: set = set()
|
||||
words_present_in_correct: set = set()
|
||||
|
||||
if review:
|
||||
for ans in unmarked_user_answers:
|
||||
# duplicated from user answer model....
|
||||
@@ -367,6 +372,22 @@ def mark(request, exam_pk, sk, unmarked_exam_answers_only=True, review=False):
|
||||
answer_suggest_incorrect.append(ans)
|
||||
break
|
||||
|
||||
for ans in correct_answers:
|
||||
answer_words = set(ans.get_constituent_words())
|
||||
words_present.update(answer_words)
|
||||
words_present_in_correct.update(answer_words)
|
||||
|
||||
for ans in half_mark_answers:
|
||||
answer_words = set(ans.get_constituent_words())
|
||||
words_present.update(answer_words)
|
||||
words_present_in_correct.update(answer_words)
|
||||
|
||||
for ans in incorrect_answers:
|
||||
answer_words = set(ans.get_constituent_words())
|
||||
words_present.update(answer_words)
|
||||
|
||||
words_suggest_incorrect = words_present - words_present_in_correct - set(question.answer_suggest_incorrect)
|
||||
|
||||
return render(
|
||||
request,
|
||||
"anatomy/mark.html",
|
||||
@@ -385,6 +406,7 @@ def mark(request, exam_pk, sk, unmarked_exam_answers_only=True, review=False):
|
||||
"incorrect_answers": incorrect_answers,
|
||||
"unmarked_exam_answers_only": unmarked_exam_answers_only,
|
||||
"answer_suggest_incorrect": answer_suggest_incorrect,
|
||||
"words_suggest_incorrect": words_suggest_incorrect,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -748,6 +770,25 @@ class AnatomyQuestionView(LoginRequiredMixin, SingleTableMixin, FilterView):
|
||||
|
||||
# return qs
|
||||
|
||||
@user_is_author_or_anatomy_checker
|
||||
def question_suggest_incorrect_answers(request, pk):
|
||||
question = get_object_or_404(AnatomyQuestion, pk=pk)
|
||||
if request.method == "POST":
|
||||
form = SuggestIncorrectAnswerForm(request.POST, instance=question)
|
||||
|
||||
if form.is_valid():
|
||||
obj = form.save()
|
||||
obj.save()
|
||||
|
||||
return render(request, "anatomy/question_detail.html#suggest-incorrect-form", {"question": question})
|
||||
|
||||
return HttpResponse("Invalid")
|
||||
else:
|
||||
form = SuggestIncorrectAnswerForm(instance=question)
|
||||
|
||||
return render(request, "anatomy/suggest_incorrect.html", {"form": form, "question": question})
|
||||
|
||||
return HttpResponse("Error", status=400)
|
||||
|
||||
@user_is_author_or_anatomy_checker
|
||||
def question_save_annotation(request, pk):
|
||||
|
||||
@@ -568,24 +568,6 @@ class SeriesImage(SeriesImageBase):
|
||||
help_text="Reference to the object that has replaced this one.",
|
||||
)
|
||||
|
||||
def get_file_size(self):
|
||||
try:
|
||||
return self.image.size
|
||||
|
||||
# We catch this for when the image does not exist
|
||||
except FileNotFoundError:
|
||||
return 0
|
||||
|
||||
def get_dicom_data(self):
|
||||
try:
|
||||
with pydicom.dcmread(self.image) as d:
|
||||
return d
|
||||
except InvalidDicomError:
|
||||
return {}
|
||||
|
||||
def get_series(self):
|
||||
return self.series
|
||||
|
||||
# def get_image_dicom_json(self, image_index):
|
||||
# try:
|
||||
# with pydicom.dcmread(self.image) as ds:
|
||||
|
||||
@@ -37,6 +37,7 @@ from easy_thumbnails.exceptions import InvalidImageFormatError
|
||||
|
||||
|
||||
import pydicom
|
||||
import pydicom.errors
|
||||
import dicognito.anonymizer
|
||||
from django.contrib.auth.models import User
|
||||
from django.forms.models import model_to_dict
|
||||
@@ -240,6 +241,24 @@ class SeriesImageBase(models.Model):
|
||||
ordering = ["position"]
|
||||
abstract = True
|
||||
|
||||
def get_file_size(self):
|
||||
try:
|
||||
return self.image.size
|
||||
|
||||
# We catch this for when the image does not exist
|
||||
except FileNotFoundError:
|
||||
return 0
|
||||
|
||||
def get_dicom_data(self):
|
||||
try:
|
||||
with pydicom.dcmread(self.image) as d:
|
||||
return d
|
||||
except pydicom.errors.InvalidDicomError:
|
||||
return {}
|
||||
|
||||
def get_series(self):
|
||||
return self.series
|
||||
|
||||
def get_dicom_json(self):
|
||||
try:
|
||||
json = pydicom.read_file(self.image).to_json()
|
||||
|
||||
@@ -1525,6 +1525,7 @@ class ExamViews(View, LoginRequiredMixin):
|
||||
# #queryset=self.Answer.objects.filter(),
|
||||
# ),
|
||||
)
|
||||
.order_by("examquestiondetail__sort_order")
|
||||
)
|
||||
else:
|
||||
questions = exam.get_questions()
|
||||
|
||||
Reference in New Issue
Block a user