Add HTMX support for exam selection and update related templates and views
This commit is contained in:
@@ -6,27 +6,31 @@
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div id="view-filter-options">
|
||||
<h3>Filter Anatomy Questions</h3>
|
||||
<form action="" method="get">
|
||||
{{ filter.form }}
|
||||
<input class="btn btn-primary btn-sm mt-1 mb-1" type="submit" />
|
||||
</form>
|
||||
</div>
|
||||
{% render_table table %}
|
||||
<div id="view-filter-options">
|
||||
<h3>Filter Anatomy Questions</h3>
|
||||
<form action="" method="get">
|
||||
{{ filter.form }}
|
||||
<input class="btn btn-primary btn-sm mt-1 mb-1" type="submit" />
|
||||
</form>
|
||||
</div>
|
||||
{% render_table table %}
|
||||
|
||||
<button id="button-select-add-exam"
|
||||
data-exam_json_edit_url="{% url 'generic:generic_exam_json_edit' %}"
|
||||
data-exam_list_url="{% url 'api-1:anatomy_user_exams' %}"
|
||||
data-type="anatomy"
|
||||
data-csrf="{{ csrf_token}}">Add selected questions to
|
||||
exam</button>
|
||||
<div id="exam-options"></div>
|
||||
<button id="button-select-add-exam"
|
||||
hx-get="{% url 'generic:generic_exam_htmx' %}?type=anatomy"
|
||||
hx-target="#action-result"
|
||||
hx-swap="innerHTML"
|
||||
data-exam_json_edit_url="{% url 'generic:generic_exam_json_edit' %}"
|
||||
data-exam_list_url="{% url 'api-1:anatomy_user_exams' %}"
|
||||
data-type="anatomy"
|
||||
data-csrf="{{ csrf_token }}">
|
||||
Add selected questions to exam
|
||||
</button>
|
||||
<div id="exam-options"></div>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
|
||||
|
||||
});
|
||||
</script>
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -34,6 +34,11 @@ urlpatterns = [
|
||||
views.generic_exam_json_edit,
|
||||
name="generic_exam_json_edit",
|
||||
),
|
||||
path(
|
||||
"generic_exam_htmx",
|
||||
views.generic_exam_htmx,
|
||||
name="generic_exam_htmx",
|
||||
),
|
||||
path("bulk_delete_questions/", views.bulk_delete_questions, name="bulk_delete_questions"),
|
||||
path(
|
||||
"examination-autocomplete",
|
||||
|
||||
@@ -567,6 +567,63 @@ def generic_exam_json_edit(request):
|
||||
return JsonResponse(data, status=400)
|
||||
|
||||
|
||||
@login_required
|
||||
def generic_exam_htmx(request):
|
||||
"""HTMX-specific endpoint for selecting an exam and adding selected questions.
|
||||
|
||||
GET: returns the exam selection fragment (same as earlier partial)
|
||||
POST: expects 'type' and checkbox list 'selection' (or selection[]) and 'exam_id'
|
||||
adds selected question ids to the chosen exam and returns a small HTML fragment
|
||||
or JSON suitable for HTMX swapping.
|
||||
"""
|
||||
if request.method == "GET":
|
||||
question_type = request.GET.get("type")
|
||||
if not question_type:
|
||||
return HttpResponse("Missing type", status=400)
|
||||
try:
|
||||
Exam = get_exam_model_from_app_name(question_type)
|
||||
except Exception:
|
||||
return HttpResponse("Unknown type", status=400)
|
||||
|
||||
exams = Exam.objects.filter(archive=False, exam_mode=True).order_by("name")[:200]
|
||||
return render(request, "generic/partials/exam_select_fragment.html", {"exams": exams, "type": question_type})
|
||||
|
||||
if request.method == "POST":
|
||||
question_type = request.POST.get("type")
|
||||
if not question_type:
|
||||
return JsonResponse({"status": "error", "message": "missing type"}, status=400)
|
||||
|
||||
try:
|
||||
Exam = get_exam_model_from_app_name(question_type)
|
||||
except Exception:
|
||||
return JsonResponse({"status": "error", "message": "unknown type"}, status=400)
|
||||
|
||||
exam = get_object_or_404(Exam, pk=request.POST.get("exam_id"))
|
||||
|
||||
# read checkbox selections
|
||||
sel = request.POST.getlist("selection") or request.POST.getlist("selection[]")
|
||||
question_ids = []
|
||||
for s in sel:
|
||||
try:
|
||||
question_ids.append(int(s))
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
add_count = 0
|
||||
for i in question_ids:
|
||||
if not exam.exam_questions.filter(pk=i).exists():
|
||||
add_count += 1
|
||||
exam.exam_questions.add(i)
|
||||
|
||||
if add_count > 0:
|
||||
exam.save()
|
||||
|
||||
# return a small HTMX-friendly fragment summarising the result
|
||||
return render(request, "generic/partials/exam_select_result.html", {"added": add_count, "exam": exam})
|
||||
|
||||
return HttpResponse(status=405)
|
||||
|
||||
|
||||
class ExamViews(View, LoginRequiredMixin):
|
||||
def __init__(
|
||||
self,
|
||||
|
||||
@@ -15,10 +15,12 @@
|
||||
</div>
|
||||
{% render_table table %}
|
||||
|
||||
<button id="button-select-add-exam"
|
||||
data-exam_json_edit_url="{% url 'generic:generic_exam_json_edit' %}"
|
||||
data-exam_list_url="{% url 'api-1:rapid_user_exams' %}"
|
||||
data-type="rapid" data-csrf="{{ csrf_token}}">Add selected questions to
|
||||
<button id="button-select-add-exam"
|
||||
hx-get="{% url 'generic:generic_exam_htmx' %}?type=rapid"
|
||||
hx-target="#action-result"
|
||||
hx-swap="innerHTML"
|
||||
data-exam_list_url="{% url 'api-1:rapid_user_exams' %}"
|
||||
data-type="rapid" data-csrf="{{ csrf_token}}">Add selected questions to
|
||||
exam</button>
|
||||
<div id="exam-options"></div>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form hx-post="{% url 'generic:generic_exam_json_edit' %}" hx-target="#action-result" hx-swap="innerHTML">
|
||||
<form hx-post="{% url 'generic:generic_exam_htmx' %}" hx-target="#action-result" hx-swap="innerHTML">
|
||||
<input type="hidden" name="type" value="{{ type }}" />
|
||||
<div class="mb-2">
|
||||
<label for="id_exam_id">Choose exam</label>
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<div class="alert alert-success alert-sm" role="alert">
|
||||
{% if added %}
|
||||
Added {{ added }} question{{ added|pluralize }} to "{{ exam.name }}".
|
||||
{% else %}
|
||||
No new questions were added to "{{ exam.name }}".
|
||||
{% endif %}
|
||||
<div class="mt-2">
|
||||
<button class="btn btn-sm btn-secondary" type="button" onclick="document.querySelector('#action-result').innerHTML='';">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -21,7 +21,7 @@
|
||||
</div>
|
||||
<div class="btn-group" role="group">
|
||||
<button id="button-select-add-exam" class="btn btn-outline-primary btn-sm"
|
||||
hx-get="{% url 'generic:generic_exam_json_edit' %}?type={{ app_name }}"
|
||||
hx-get="{% url 'generic:generic_exam_htmx' %}?type={{ app_name }}"
|
||||
hx-target="#action-result"
|
||||
hx-swap="innerHTML">Add to exam</button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user