improve suggest answers (and fix some other issues)

This commit is contained in:
Ross
2025-01-20 09:54:56 +00:00
parent 9cb83b62d2
commit 0ca17c23b5
10 changed files with 114 additions and 21 deletions
+6 -1
View File
@@ -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"]
+3
View File
@@ -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))
+21
View File
@@ -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>
+6
View File
@@ -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"
+41
View File
@@ -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):
-18
View File
@@ -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:
+19
View File
@@ -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()
+1
View File
@@ -1525,6 +1525,7 @@ class ExamViews(View, LoginRequiredMixin):
# #queryset=self.Answer.objects.filter(),
# ),
)
.order_by("examquestiondetail__sort_order")
)
else:
questions = exam.get_questions()