Add AddExamForm and update exam_edit_fragment for exam management

This commit is contained in:
Ross
2026-02-16 11:21:51 +00:00
parent fb9952e7aa
commit 0718ea82b2
2 changed files with 65 additions and 32 deletions
@@ -0,0 +1,23 @@
{% load static %}
<div id="exam-list">
<form hx-post="{% url 'anatomy:question_add_exam' question_id=question.pk %}" hx-target="#exam-list" hx-swap="innerHTML">
{% csrf_token %}
<div class="input-group mb-2">
{{ form.exam }}
<button class="btn btn-sm btn-primary" type="submit">Add</button>
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="document.getElementById('exam-list').innerHTML='';">Close</button>
</div>
</form>
<div class="mb-2 small">Current exams:
{% for exam in question.exams.all %}
<form class="d-inline me-1" hx-post="{% url 'anatomy:question_add_exam' question_id=question.pk %}" hx-target="#exam-list" hx-swap="innerHTML">
{% csrf_token %}
<input type="hidden" name="exam_id" value="{{ exam.id }}">
<input type="hidden" name="remove" value="true">
<button class="btn btn-sm btn-outline-danger mb-1" type="submit" hx-confirm="Are you sure you want to remove this exam?">{{ exam }}</button>
</form>
{% empty %}
<span class="text-muted">No exams</span>
{% endfor %}
</div>
</div>
+42 -32
View File
@@ -59,6 +59,21 @@ from .models import (
# HalfMarkAnswers, # HalfMarkAnswers,
# IncorrectAnswers, # IncorrectAnswers,
) )
from generic.widgets import ExamSearchWidget
class AddExamForm(forms.Form):
exam = forms.ModelChoiceField(queryset=Exam.objects.none(), required=True, widget=ExamSearchWidget(exam_model=Exam), label="Exam to add")
def __init__(self, *args, user=None, **kwargs):
super().__init__(*args, **kwargs)
if user is None:
qs = Exam.objects.none()
elif user.groups.filter(name="anatomy_checker").exists():
qs = Exam.objects.all()
else:
qs = Exam.objects.filter(author__id=user.id) | Exam.objects.filter(open_access=True)
self.fields["exam"].queryset = qs.distinct()
from generic.models import CidUser, Examination from generic.models import CidUser, Examination
from generic.views import AuthorRequiredMixin, ExamCloneMixin, ExamCreateBase, ExamDeleteBase, ExamGroupsUpdateBase, ExamUpdateBase, ExamViews, GenericViewBase, UpdateQuestionMixin from generic.views import AuthorRequiredMixin, ExamCloneMixin, ExamCreateBase, ExamDeleteBase, ExamGroupsUpdateBase, ExamUpdateBase, ExamViews, GenericViewBase, UpdateQuestionMixin
from reversion.views import RevisionMixin from reversion.views import RevisionMixin
@@ -1627,44 +1642,39 @@ def question_add_exam(request, question_id: int):
return HttpResponse("Invalid permissions", status=403) return HttpResponse("Invalid permissions", status=403)
if request.POST: # POST: handle add/remove; if exam_id provided use existing behaviour
if request.method == "POST":
exam_id = request.POST.get("exam_id", None) exam_id = request.POST.get("exam_id", None)
if exam_id is None: if exam_id:
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)
else:
question.exams.add(exam)
exam = get_object_or_404(Exam, pk=exam_id) # return updated fragment
form = AddExamForm(user=request.user)
return render(request, "anatomy/partials/exam_edit_fragment.html", {"form": form, "question": question})
if request.POST.get("remove", False) == "true": # else try to process from the search form
question.exams.remove(exam) form = AddExamForm(request.POST, user=request.user)
return HttpResponse("Question removed from exam.") if form.is_valid():
exam = form.cleaned_data.get("exam")
if request.POST.get("remove", False) == "true":
question.exams.remove(exam)
else:
question.exams.add(exam)
form = AddExamForm(user=request.user)
return render(request, "anatomy/partials/exam_edit_fragment.html", {"form": form, "question": question})
else: else:
question.exams.add(exam) # render form with errors
return render(request, "anatomy/partials/exam_edit_fragment.html", {"form": form, "question": question})
return HttpResponse("Question added to exam.") # GET: render the fragment with the add form and current exams
else: form = AddExamForm(user=request.user)
# Return a list of exams that we can add to the question return render(request, "anatomy/partials/exam_edit_fragment.html", {"form": form, "question": question})
exams = Exam.objects.filter(author=request.user)
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() raise PermissionDenied()