Add bulk edit functionality for CID users: implement endpoint and update templates for bulk add/remove actions
This commit is contained in:
@@ -21,6 +21,7 @@ from django.core.exceptions import PermissionDenied, FieldError
|
||||
|
||||
from django.http import Http404, HttpRequest, JsonResponse
|
||||
from django.http import HttpResponseRedirect, HttpResponse
|
||||
from django.template.loader import render_to_string
|
||||
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
|
||||
|
||||
@@ -2378,6 +2379,18 @@ class ExamViews(View, LoginRequiredMixin):
|
||||
else:
|
||||
app_exam_map[self.app_name].remove(pk)
|
||||
|
||||
# If this is an HTMX request, return the rendered li partial
|
||||
if request.headers.get('HX-Request') == 'true':
|
||||
# Render the updated list item and return as HTML so HTMX can swap it
|
||||
html = render_to_string(
|
||||
'generic/partials/cid_user_li.html',
|
||||
{'cid': cid_user, 'exam': exam, 'current': add},
|
||||
request=request,
|
||||
)
|
||||
response = HttpResponse(html, content_type='text/html')
|
||||
response['HX-Trigger'] = json.dumps({'cid_toggled': {'pk': cid_user.pk, 'added': add}})
|
||||
return response
|
||||
|
||||
data = {"status": "success", "added": add}
|
||||
return JsonResponse(data, status=200)
|
||||
|
||||
@@ -2502,6 +2515,95 @@ class ExamViews(View, LoginRequiredMixin):
|
||||
data = {"status": "error"}
|
||||
return JsonResponse(data, status=400)
|
||||
|
||||
@method_decorator(login_required)
|
||||
def exam_cid_bulk_edit(self, request, pk):
|
||||
"""Bulk add/remove CidUsers to/from an exam. Expects POST with 'bulk_pks' (JSON list)
|
||||
and 'add' ('true'/'false'). Returns a rendered list partial for HTMX swaps.
|
||||
"""
|
||||
if request.method != 'POST':
|
||||
return JsonResponse({'status': 'error, invalid method'}, status=400)
|
||||
|
||||
if request.user.groups.filter(name="cid_user_manager").exists():
|
||||
pass
|
||||
elif not self.check_user_edit_access(request.user, exam_id=pk):
|
||||
data = {"status": "invalid permisions"}
|
||||
return JsonResponse(data, status=403)
|
||||
|
||||
exam = get_object_or_404(self.Exam, pk=pk)
|
||||
|
||||
bulk_pks_raw = request.POST.get('bulk_pks') or request.POST.get('bulk_pks[]')
|
||||
items = []
|
||||
if bulk_pks_raw:
|
||||
try:
|
||||
items = json.loads(bulk_pks_raw)
|
||||
except Exception:
|
||||
# attempt to parse comma separated
|
||||
try:
|
||||
items = [int(x) for x in bulk_pks_raw.split(',') if x.strip()]
|
||||
except Exception:
|
||||
items = []
|
||||
else:
|
||||
sel = request.POST.getlist('selection') or request.POST.getlist('selection[]')
|
||||
for s in sel:
|
||||
try:
|
||||
items.append(int(s))
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
# 'add' may be provided for uniform bulk operations; otherwise items may be a list of {pk, add}
|
||||
add_flag = None
|
||||
if 'add' in request.POST:
|
||||
add_flag = request.POST.get('add') == 'true'
|
||||
|
||||
changed = []
|
||||
for entry in items:
|
||||
# entry may be an int PK or a dict {pk, add}
|
||||
if isinstance(entry, dict):
|
||||
uid = entry.get('pk')
|
||||
this_add = bool(entry.get('add'))
|
||||
else:
|
||||
uid = entry
|
||||
this_add = add_flag if add_flag is not None else True
|
||||
|
||||
try:
|
||||
cid_user = CidUser.objects.get(pk=uid)
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
app_exam_map = {}
|
||||
app_exam_map["rapids"] = cid_user.rapid_exams
|
||||
app_exam_map["shorts"] = cid_user.shorts_exams
|
||||
app_exam_map["anatomy"] = cid_user.anatomy_exams
|
||||
app_exam_map["longs"] = cid_user.longs_exams
|
||||
app_exam_map["physics"] = cid_user.physics_exams
|
||||
app_exam_map["sbas"] = cid_user.sba_exams
|
||||
app_exam_map["atlas"] = cid_user.casecollection_exams
|
||||
|
||||
try:
|
||||
if this_add:
|
||||
app_exam_map[self.app_name].add(pk)
|
||||
else:
|
||||
app_exam_map[self.app_name].remove(pk)
|
||||
changed.append(cid_user.pk)
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
# Rebuild lists for rendering
|
||||
current_cid_users = exam.valid_cid_users.all()
|
||||
exam_groups = exam.cid_user_groups.all()
|
||||
available_cid_users = CidUser.objects.filter(group__in=exam_groups).difference(current_cid_users)
|
||||
|
||||
html = render_to_string('generic/partials/cid_user_list.html', {
|
||||
'exam': exam,
|
||||
'current_cid_users': current_cid_users,
|
||||
'available_cid_users': available_cid_users,
|
||||
}, request=request)
|
||||
|
||||
response = HttpResponse(html, content_type='text/html')
|
||||
# add_flag may be None for mixed add/remove operations
|
||||
response['HX-Trigger'] = json.dumps({'cid_bulk_toggled': {'changed': changed, 'add': add_flag, 'mixed': add_flag is None}})
|
||||
return response
|
||||
|
||||
@method_decorator(login_required)
|
||||
def mark_overview(self, request, pk):
|
||||
exam: ExamBase = get_object_or_404(self.Exam, pk=pk)
|
||||
|
||||
Reference in New Issue
Block a user