.
This commit is contained in:
@@ -6045,6 +6045,113 @@ def user_search_widget(request):
|
||||
)
|
||||
|
||||
|
||||
@login_required
|
||||
def exam_search_widget(request):
|
||||
"""HTMX/JS endpoint to search exams for the exam-selection widget.
|
||||
|
||||
GET params:
|
||||
- q: query string
|
||||
- field: the form field name to return with the results (so the
|
||||
template can call addExamToField(field, id, text)).
|
||||
- model: optional model label ("app_label.ModelName") to restrict
|
||||
search to a specific exam model.
|
||||
|
||||
Returns a small HTML fragment listing matching exams. Each result will
|
||||
include a button with class `.exam-add-btn` and `data-exam-id`/`data-exam-text`.
|
||||
"""
|
||||
import re
|
||||
from django.apps import apps
|
||||
|
||||
q_raw = (request.GET.get("q") or "").strip()
|
||||
field = request.GET.get("field") or request.GET.get("row")
|
||||
model_label = request.GET.get("model")
|
||||
if not q_raw:
|
||||
return HttpResponse("")
|
||||
|
||||
# Support wildcard queries
|
||||
q = q_raw
|
||||
|
||||
model = None
|
||||
if model_label:
|
||||
try:
|
||||
# model_label may be 'app_label.ModelName' or the model label
|
||||
model = apps.get_model(model_label)
|
||||
except Exception:
|
||||
try:
|
||||
# try splitting
|
||||
app_label, model_name = model_label.split(".")
|
||||
model = apps.get_model(app_label, model_name)
|
||||
except Exception:
|
||||
model = None
|
||||
|
||||
# Build queryset
|
||||
qs = None
|
||||
if model is not None:
|
||||
# Try common name-like fields for exams
|
||||
from django.db.models import Q
|
||||
|
||||
field_lookups = ["name__icontains", "title__icontains", "examination__icontains", "modality__icontains"]
|
||||
for lookup in field_lookups:
|
||||
try:
|
||||
qs = model.objects.filter(**{lookup: q})
|
||||
if qs.exists():
|
||||
break
|
||||
except Exception:
|
||||
qs = None
|
||||
# fallback: try icontains on str() via name if present
|
||||
if qs is None or not qs.exists():
|
||||
try:
|
||||
qs = model.objects.filter(Q(name__icontains=q) | Q(title__icontains=q))
|
||||
except Exception:
|
||||
try:
|
||||
qs = model.objects.all()
|
||||
except Exception:
|
||||
qs = model.objects.none()
|
||||
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 = []
|
||||
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:
|
||||
rqs = m.objects.filter(title__icontains=q)[:5]
|
||||
except Exception:
|
||||
rqs = m.objects.none()
|
||||
for o in rqs:
|
||||
results.append(o)
|
||||
# Render combined results
|
||||
rendered_results = []
|
||||
for o in results[:50]:
|
||||
rendered_results.append({"id": getattr(o, "pk", None), "text": str(o)})
|
||||
return render(request, "generic/partials/exam_search_widget_results.html", {"results": rendered_results, "field": field, "q": q_raw})
|
||||
|
||||
# Limit results
|
||||
limit = 50 if q in ("*", "%") else 10
|
||||
try:
|
||||
qs = qs.order_by("name")[:limit]
|
||||
except Exception:
|
||||
qs = qs[:limit]
|
||||
|
||||
results = []
|
||||
for e in qs:
|
||||
results.append({"id": e.pk, "text": str(e)})
|
||||
|
||||
return render(request, "generic/partials/exam_search_widget_results.html", {"results": results, "field": field, "q": q_raw})
|
||||
|
||||
|
||||
@login_required
|
||||
def supervisor_search(request):
|
||||
"""HTMX endpoint to search supervisors for the trainees bulk-update UI.
|
||||
|
||||
Reference in New Issue
Block a user