Add HTMX endpoint to set open access for exam questions and corresponding result template
This commit is contained in:
@@ -39,6 +39,11 @@ urlpatterns = [
|
|||||||
views.generic_exam_htmx,
|
views.generic_exam_htmx,
|
||||||
name="generic_exam_htmx",
|
name="generic_exam_htmx",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"generic_exam_set_open_access",
|
||||||
|
views.generic_exam_set_open_access,
|
||||||
|
name="generic_exam_set_open_access",
|
||||||
|
),
|
||||||
path("bulk_delete_questions/", views.bulk_delete_questions, name="bulk_delete_questions"),
|
path("bulk_delete_questions/", views.bulk_delete_questions, name="bulk_delete_questions"),
|
||||||
path(
|
path(
|
||||||
"examination-autocomplete",
|
"examination-autocomplete",
|
||||||
|
|||||||
@@ -635,6 +635,71 @@ def generic_exam_htmx(request):
|
|||||||
return HttpResponse(status=405)
|
return HttpResponse(status=405)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def generic_exam_set_open_access(request):
|
||||||
|
"""HTMX endpoint to set open_access on questions in an exam.
|
||||||
|
|
||||||
|
POST params:
|
||||||
|
- type: app name (sbas, rapids, anatomy, physics, shorts, longs)
|
||||||
|
- exam_id: id of the exam
|
||||||
|
- set_open_access: 'true' or 'false'
|
||||||
|
- selection / selection[]: optional list of question ids to apply to (if omitted apply to all)
|
||||||
|
|
||||||
|
Only questions that request.user can edit (question.can_edit(user)) or superusers will be modified.
|
||||||
|
Returns a small HTML fragment summarising the count of changed questions.
|
||||||
|
"""
|
||||||
|
if request.method != "POST":
|
||||||
|
return HttpResponse(status=405)
|
||||||
|
|
||||||
|
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"))
|
||||||
|
|
||||||
|
# permission to edit exam required
|
||||||
|
# Many exam edits are limited to exam authors / checkers; reuse existing check where possible
|
||||||
|
# For now require the user to have edit access (ExamViews.check_user_edit_access equivalent)
|
||||||
|
# We'll do a lightweight check: if user is superuser OR user in exam.get_author_objects()
|
||||||
|
if not (request.user.is_superuser or request.user in exam.get_author_objects()):
|
||||||
|
return HttpResponse(format_html('<div class="alert alert-danger">Permission denied</div>'))
|
||||||
|
|
||||||
|
set_val = True if request.POST.get("set_open_access") == "true" else False
|
||||||
|
|
||||||
|
sel = request.POST.getlist("selection") or request.POST.getlist("selection[]")
|
||||||
|
if sel:
|
||||||
|
try:
|
||||||
|
ids = [int(s) for s in sel]
|
||||||
|
except Exception:
|
||||||
|
ids = []
|
||||||
|
qs = exam.exam_questions.filter(pk__in=ids)
|
||||||
|
else:
|
||||||
|
qs = exam.exam_questions.all()
|
||||||
|
|
||||||
|
changed = 0
|
||||||
|
for q in qs:
|
||||||
|
try:
|
||||||
|
# prefer per-question permission check if available
|
||||||
|
can_edit = request.user.is_superuser or getattr(q, 'can_edit', lambda u: False)(request.user)
|
||||||
|
except Exception:
|
||||||
|
can_edit = request.user.is_superuser
|
||||||
|
|
||||||
|
if not can_edit:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if q.open_access != set_val:
|
||||||
|
q.open_access = set_val
|
||||||
|
q.save()
|
||||||
|
changed += 1
|
||||||
|
|
||||||
|
return render(request, "generic/partials/set_open_access_result.html", {"changed": changed, "exam": exam, "state": set_val})
|
||||||
|
|
||||||
|
|
||||||
class ExamViews(View, LoginRequiredMixin):
|
class ExamViews(View, LoginRequiredMixin):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<div class="alert alert-info">
|
||||||
|
{% if changed %}
|
||||||
|
Updated {{ changed }} question{{ changed|pluralize }} in "{{ exam.name }}". Open access set to {{ state|yesno:"true,false" }}.
|
||||||
|
{% else %}
|
||||||
|
No questions were updated.
|
||||||
|
{% endif %}
|
||||||
|
<div class="mt-2">
|
||||||
|
<button class="btn btn-sm btn-secondary" type="button" onclick="document.querySelector('#action-result').innerHTML='';">Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
Reference in New Issue
Block a user