further improve anatomy questions
This commit is contained in:
@@ -329,6 +329,12 @@ class Answer(models.Model):
|
||||
self.answer = self.answer.strip()
|
||||
self.answer_compare = get_answer_compare(self.answer)
|
||||
|
||||
def can_edit(self, user):
|
||||
"""Check if user can edit the answer
|
||||
|
||||
This just mirrors the result from the attached question"""
|
||||
return self.question.can_edit(user)
|
||||
|
||||
# def get_compare_string(self):
|
||||
# s = self.answer.lower().strip()
|
||||
# s = s.translate(str.maketrans('', '', string.punctuation))
|
||||
|
||||
@@ -20,25 +20,40 @@
|
||||
</div>
|
||||
<h2>Question type: {{question.question_type}}</h2>
|
||||
<h3>Primary answer: {{ question.get_primary_answer }}</h3>
|
||||
<details>
|
||||
<summary>
|
||||
Answers:
|
||||
</summary>
|
||||
<table>
|
||||
<tr><th>Answer</th><th>Score</th></tr>
|
||||
{% for answer in question.answers.all|dictsortreversed:"status" %}
|
||||
<tr>
|
||||
<td>
|
||||
<span {% if answer.proposed %}class="proposed-answer" data-aid="{{answer.pk}}" data-question-type="anatomy"
|
||||
{% endif %}>
|
||||
{{ answer }}
|
||||
</span>
|
||||
<td>
|
||||
<td>{{answer.status}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</details>
|
||||
<div>
|
||||
<details>
|
||||
<summary title="Click to view the question answers">
|
||||
Answers:
|
||||
</summary>
|
||||
<table>
|
||||
<tr><th>Answer</th><th>Score</th></tr>
|
||||
{% for answer in question.answers.all|dictsortreversed:"status" %}
|
||||
<tr>
|
||||
<td {% if answer.proposed %}class="proposed-answer" data-aid="{{answer.pk}}" data-question-type="anatomy"
|
||||
{% endif %}>
|
||||
<span >
|
||||
{{ answer }}
|
||||
</span>
|
||||
<td>
|
||||
<td>{{answer.status}}</td>
|
||||
{% if answer.proposed %}
|
||||
<td>
|
||||
<button class="btn btn-sm accept-button" data-aid={{answer.id}}
|
||||
hx-get="{% url 'anatomy:confirm_answer' answer.id %}"
|
||||
title="Click to accept the proposed answer">Accept</button>
|
||||
<button class="btn btn-sm delete-button" data-aid={{answer.id}}
|
||||
hx-get="{% url 'anatomy:delete_answer' answer.id %}"
|
||||
title="Click to delete the proposed answer"
|
||||
>Delete</button>
|
||||
</td>
|
||||
{% endif %}
|
||||
|
||||
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</details>
|
||||
</div>
|
||||
<div>
|
||||
Answer help: {{ question.answer_help|safe }}
|
||||
</div>
|
||||
@@ -52,6 +67,10 @@
|
||||
Exams: {% for exam in question.exams.all %}
|
||||
<a href="{% url 'anatomy:exam_overview' pk=exam.pk %}">{{ exam }}</a>
|
||||
{% endfor %}
|
||||
<button class="btn btn-sm" hx-get="{% url 'anatomy:question_add_exam' question_id=question.pk %}"
|
||||
hx-target="#exam-list"
|
||||
hx-swap="innerHTML">Edit exam(s)</button>
|
||||
<span id="exam-list"></span>
|
||||
</div>
|
||||
<div>
|
||||
Modality: {{ question.modality }}
|
||||
@@ -148,4 +167,17 @@
|
||||
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
<style>
|
||||
.proposed-answer::before {
|
||||
content: "\F505";
|
||||
font-family: bootstrap-icons;
|
||||
}
|
||||
|
||||
.question div {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
{% endblock css %}
|
||||
@@ -47,6 +47,13 @@ urlpatterns = [
|
||||
views.QuestionDelete.as_view(),
|
||||
name="question_delete",
|
||||
),
|
||||
path(
|
||||
"question/<int:question_id>/add_exam",
|
||||
views.question_add_exam,
|
||||
name="question_add_exam",
|
||||
),
|
||||
path("answer/<int:answer_id>/confirm", views.confirm_answer, name="confirm_answer"),
|
||||
path("answer/<int:answer_id>/delete", views.delete_answer, name="delete_answer"),
|
||||
path("exam/<int:exam_pk>/<int:sk>/mark", views.mark, name="mark"),
|
||||
path("exam/<int:exam_pk>/<int:sk>/mark/all", views.mark_all, name="mark_all"),
|
||||
path(
|
||||
|
||||
@@ -27,6 +27,8 @@ from django.http import HttpResponseRedirect, HttpResponse
|
||||
from dal import autocomplete
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils.html import format_html_join
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
|
||||
from .forms import (
|
||||
@@ -851,3 +853,81 @@ GenericViews = GenericViewBase("anatomy", AnatomyQuestion, UserAnswer, Exam)
|
||||
class ExamClone(ExamCloneMixin, ExamCreate):
|
||||
"""Clone exam view"""
|
||||
|
||||
|
||||
|
||||
def confirm_answer(request, answer_id: int):
|
||||
if request.htmx:
|
||||
answer = get_object_or_404(Answer, pk=answer_id)
|
||||
|
||||
# Check if the user has permission to confirm the answer
|
||||
if not answer.can_edit(request.user):
|
||||
return HttpResponse("Invalid permissions", status=403)
|
||||
|
||||
answer.clean()
|
||||
answer.proposed = False
|
||||
answer.save()
|
||||
return HttpResponse("Answer accepted")
|
||||
|
||||
raise PermissionDenied()
|
||||
|
||||
def delete_answer(request, answer_id: int):
|
||||
if request.htmx:
|
||||
answer = get_object_or_404(Answer, pk=answer_id)
|
||||
|
||||
# Check if the user has permission to confirm the answer
|
||||
if not answer.can_edit(request.user):
|
||||
return HttpResponse("Invalid permissions", status=403)
|
||||
|
||||
answer.delete()
|
||||
return HttpResponse("Answer deleted")
|
||||
raise PermissionDenied()
|
||||
|
||||
def question_add_exam(request, question_id: int):
|
||||
if request.htmx:
|
||||
question = get_object_or_404(AnatomyQuestion, pk=question_id)
|
||||
|
||||
if not question.can_edit(request.user):
|
||||
return HttpResponse("Invalid permissions", status=403)
|
||||
|
||||
|
||||
if request.POST:
|
||||
exam_id = request.POST.get("exam_id", None)
|
||||
|
||||
if exam_id is None:
|
||||
return HttpResponse("No exam id provided", status=400)
|
||||
|
||||
exam = get_object_or_404(Exam, pk=exam_id)
|
||||
|
||||
if request.POST.get("remove", False) == "true":
|
||||
question.exams.remove(exam)
|
||||
return HttpResponse("Question removed from exam.")
|
||||
else:
|
||||
question.exams.add(exam)
|
||||
|
||||
return HttpResponse("Question added to exam.")
|
||||
else:
|
||||
# Return a list of exams that we can add to the question
|
||||
|
||||
exams = Exam.objects.filter(author=request.user) | Exam.objects.filter(open_access=True)
|
||||
exams = exams.difference(question.exams.all())
|
||||
|
||||
if not exams:
|
||||
return HttpResponse("No exams available to add")
|
||||
|
||||
html = "Exams to add to question: <br>"
|
||||
for exam in exams:
|
||||
html = html + (f"<span id='htmx-exam-list'><form><input name='exam_id' value='{exam.id}' type='hidden'><button hx-post=\"{request.path}\""
|
||||
f">{exam.name}: {exam.id}</button></form>"
|
||||
)
|
||||
html = html + "<br>Exams to remove from question: <br>"
|
||||
for exam in question.exams.all():
|
||||
html = html + (f"<span id='htmx-exam-list'><form><input name='exam_id' value='{exam.id}' type='hidden'><input name='remove' value='true' type='hidden'><button hx-post=\"{request.path}\""
|
||||
f"hx-confirm='Are you sure you want to remove this exam from the question?'"
|
||||
f">{exam.name}: {exam.id}</button></form>"
|
||||
)
|
||||
html = html + "<button _='on click remove #htmx-exam-list'>Cancel</button>"
|
||||
|
||||
return HttpResponse(mark_safe(html))
|
||||
|
||||
|
||||
raise PermissionDenied()
|
||||
Reference in New Issue
Block a user