This commit is contained in:
Ross
2025-12-08 11:51:36 +00:00
parent eb06e63f77
commit 9a3a8b932a
3 changed files with 178 additions and 17 deletions
+138 -13
View File
@@ -6084,6 +6084,13 @@ def exam_search_widget(request):
except Exception:
model = None
# Helper to parse optional boolean GET params
def _get_bool_param(name):
v = request.GET.get(name)
if v is None:
return None
return str(v).lower() in ("1", "true", "yes", "on")
# Build queryset
qs = None
if model is not None:
@@ -6118,20 +6125,34 @@ def exam_search_widget(request):
"sbas.Exam",
]
results = []
# gather optional filters from GET params
extra_filters = {}
for pname in ("archive", "open_access", "exam_mode", "candidates_only"):
val = _get_bool_param(pname)
if val is not None:
extra_filters[pname] = val
for ml in possible_models:
try:
m = apps.get_model(*ml.split("."))
except Exception:
continue
try:
rqs = m.objects.filter(name__icontains=q)[:5]
except Exception:
# try common name-like fields and apply extra_filters when possible
try:
rqs = m.objects.filter(title__icontains=q)[:5]
rqs = m.objects.filter(name__icontains=q, **extra_filters)[:5]
except Exception:
rqs = m.objects.none()
try:
rqs = m.objects.filter(title__icontains=q, **extra_filters)[:5]
except Exception:
# fallback to unfiltered small slice
rqs = m.objects.filter(name__icontains=q)[:5]
except Exception:
rqs = m.objects.none()
for o in rqs:
results.append(o)
# Render combined results, but restrict to exams the user can access
def user_can_view_exam(exam, user):
try:
@@ -6160,7 +6181,14 @@ def exam_search_widget(request):
rendered_results = []
for o in results[:50]:
if user_can_view_exam(o, request.user):
rendered_results.append({"id": getattr(o, "pk", None), "text": str(o)})
rendered_results.append({
"id": getattr(o, "pk", None),
"text": str(o),
"archive": getattr(o, "archive", False),
"open_access": getattr(o, "open_access", False),
"exam_mode": getattr(o, "exam_mode", False),
"candidates_only": getattr(o, "candidates_only", False),
})
return render(request, "generic/partials/exam_search_widget_results.html", {"results": rendered_results, "field": field, "q": q_raw})
# Limit results
@@ -6200,10 +6228,36 @@ def exam_search_widget(request):
pass
return False
# Apply optional filters from GET params to the per-model queryset
extra_filters = {}
for pname in ("archive", "open_access", "exam_mode", "candidates_only"):
val = _get_bool_param(pname)
if val is not None:
extra_filters[pname] = val
results = []
for e in qs:
# apply any extra_filters defensively (models may not have fields)
skip = False
for k, v in extra_filters.items():
try:
if getattr(e, k, None) != v:
skip = True
break
except Exception:
# if attribute access fails, don't skip
continue
if skip:
continue
if user_can_view_exam(e, request.user):
results.append({"id": e.pk, "text": str(e)})
results.append({
"id": e.pk,
"text": str(e),
"archive": getattr(e, "archive", False),
"open_access": getattr(e, "open_access", False),
"exam_mode": getattr(e, "exam_mode", False),
"candidates_only": getattr(e, "candidates_only", False),
})
return render(request, "generic/partials/exam_search_widget_results.html", {"results": results, "field": field, "q": q_raw})
@@ -6223,14 +6277,85 @@ def supervisor_search(request):
q = (request.GET.get("q") or "").strip()
row = request.GET.get("row")
if not q:
return HttpResponse("")
else:
# If no model specified, try to search across several known exam models
possible_models = [
"anatomy.Exam",
"longs.Exam",
"rapids.Exam",
"shorts.Exam",
"physics.Exam",
"sbas.Exam",
]
results = []
supervisors_qs = Supervisor.objects.filter(
Q(name__icontains=q) | Q(email__icontains=q)
).order_by("name")[:10]
# gather optional filters from GET params
extra_filters = {}
for pname in ("archive", "open_access", "exam_mode", "candidates_only"):
val = _get_bool_param(pname)
if val is not None:
extra_filters[pname] = val
results = [{"id": s.pk, "text": f"{s.name} - {s.email or ''}"} for s in supervisors_qs]
for ml in possible_models:
try:
m = apps.get_model(*ml.split("."))
except Exception:
continue
try:
# try common name-like fields and apply extra_filters when possible
try:
rqs = m.objects.filter(name__icontains=q, **extra_filters)[:5]
except Exception:
try:
rqs = m.objects.filter(title__icontains=q, **extra_filters)[:5]
except Exception:
# fallback to unfiltered small slice
rqs = m.objects.filter(name__icontains=q)[:5]
except Exception:
rqs = m.objects.none()
for o in rqs:
results.append(o)
return render(request, "generic/partials/supervisor_search_results.html", {"results": results, "row": row, "q": q})
# Render combined results, but restrict to exams the user can access
def user_can_view_exam(exam, user):
try:
if user.is_superuser:
return True
except Exception:
pass
try:
if getattr(exam, "open_access", False) and getattr(exam, "active", False):
return True
except Exception:
pass
try:
authors = exam.get_author_objects()
if user in authors:
return True
except Exception:
pass
try:
if hasattr(exam, "cid_user_exam") and exam.cid_user_exam.filter(user_user=user).exists():
return True
except Exception:
pass
try:
if hasattr(exam, "markers") and user in exam.markers.all():
return True
except Exception:
pass
return False
rendered_results = []
for o in results[:50]:
if user_can_view_exam(o, request.user):
rendered_results.append({
"id": getattr(o, "pk", None),
"text": str(o),
"archive": getattr(o, "archive", False),
"open_access": getattr(o, "open_access", False),
"exam_mode": getattr(o, "exam_mode", False),
"candidates_only": getattr(o, "candidates_only", False),
})
return render(request, "generic/partials/exam_search_widget_results.html", {"results": rendered_results, "field": field, "q": q_raw})