Enhance user search widget with wildcard support and field-specific queries; improve logging in bulk add groups
This commit is contained in:
+72
-7
@@ -389,13 +389,18 @@ def exam_collection_bulk_add_groups(request, collection_id):
|
||||
"""
|
||||
collection = get_object_or_404(ExamCollection, pk=collection_id)
|
||||
|
||||
logger.debug(f"exam_collection_bulk_add_groups called with method={request.method} by user={request.user.username}")
|
||||
|
||||
|
||||
if request.method == "GET":
|
||||
logger.debug(f"exam_collection_bulk_add_groups GET request by user={request.user.username}")
|
||||
form = ExamCollectionBulkAddGroupsForm(user=request.user)
|
||||
return render(request, "generic/partials/examcollection_bulk_add_groups_form.html", {"form": form, "collection": collection})
|
||||
|
||||
# POST
|
||||
form = ExamCollectionBulkAddGroupsForm(request.POST, user=request.user)
|
||||
if not form.is_valid():
|
||||
logger.debug(f"exam_collection_bulk_add_groups form invalid: {form.errors}")
|
||||
return render(request, "generic/partials/examcollection_bulk_add_groups_form.html", {"form": form, "collection": collection})
|
||||
|
||||
cid_groups = form.cleaned_data.get("cid_user_groups")
|
||||
@@ -5937,11 +5942,62 @@ def user_search_widget(request):
|
||||
Returns a small HTML fragment listing up to 10 matching users. Each
|
||||
result will call `addUserToField(field, id, text)` when clicked.
|
||||
"""
|
||||
q = (request.GET.get("q") or "").strip()
|
||||
import re
|
||||
|
||||
q_raw = (request.GET.get("q") or "").strip()
|
||||
field = request.GET.get("field") or request.GET.get("row")
|
||||
if not q:
|
||||
if not q_raw:
|
||||
return HttpResponse("")
|
||||
|
||||
# Support wildcard '*' (return many results) and field-scoped queries like 'name:smith' or 'email:foo'
|
||||
q = q_raw
|
||||
|
||||
# If user explicitly asks for wildcard, return an unfiltered queryset (limited)
|
||||
if q in ("*", "%"):
|
||||
users_qs = User.objects.all()
|
||||
else:
|
||||
# field-specific search syntax: field:term
|
||||
m = re.match(r"^(?P<field>\w+):(?P<term>.+)$", q)
|
||||
if m:
|
||||
f = m.group("field").lower()
|
||||
term = m.group("term").strip()
|
||||
if term in ("*", "%"):
|
||||
users_qs = User.objects.all()
|
||||
else:
|
||||
if f in ("name", "fullname", "full_name"):
|
||||
users_qs = User.objects.filter(
|
||||
Q(first_name__icontains=term) | Q(last_name__icontains=term)
|
||||
)
|
||||
elif f in ("first", "first_name"):
|
||||
users_qs = User.objects.filter(first_name__icontains=term)
|
||||
elif f in ("last", "last_name"):
|
||||
users_qs = User.objects.filter(last_name__icontains=term)
|
||||
elif f == "email":
|
||||
users_qs = User.objects.filter(email__icontains=term)
|
||||
elif f == "username":
|
||||
users_qs = User.objects.filter(username__icontains=term)
|
||||
elif f in ("id", "pk"):
|
||||
try:
|
||||
users_qs = User.objects.filter(pk=int(term))
|
||||
except Exception:
|
||||
users_qs = User.objects.none()
|
||||
elif f == "grade":
|
||||
# allow grade name or id
|
||||
try:
|
||||
gid = int(term)
|
||||
users_qs = User.objects.filter(userprofile__grade_id=gid)
|
||||
except Exception:
|
||||
users_qs = User.objects.filter(userprofile__grade__name__icontains=term)
|
||||
else:
|
||||
# unknown field: fallback to general search
|
||||
users_qs = User.objects.filter(
|
||||
Q(first_name__icontains=term)
|
||||
| Q(last_name__icontains=term)
|
||||
| Q(email__icontains=term)
|
||||
| Q(username__icontains=term)
|
||||
)
|
||||
else:
|
||||
# default substring search (icontains)
|
||||
users_qs = User.objects.filter(
|
||||
Q(first_name__icontains=q)
|
||||
| Q(last_name__icontains=q)
|
||||
@@ -5949,19 +6005,28 @@ def user_search_widget(request):
|
||||
| Q(username__icontains=q)
|
||||
)
|
||||
|
||||
# optional grade filter (UserProfile.grade FK)
|
||||
grade = request.GET.get('grade')
|
||||
# optional grade filter (UserProfile.grade FK) still applies and will narrow results
|
||||
grade = request.GET.get("grade")
|
||||
if grade:
|
||||
try:
|
||||
users_qs = users_qs.filter(userprofile__grade_id=int(grade))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
users_qs = users_qs.order_by("last_name", "first_name")[:10]
|
||||
# Limit results to avoid returning too many rows; wildcard returns up to 50
|
||||
limit = 50 if q in ("*", "%") or q_raw in ("*", "%") else 10
|
||||
users_qs = users_qs.order_by("last_name", "first_name")[:limit]
|
||||
|
||||
results = [{"id": u.pk, "text": f"{u.get_full_name() or u.username} - {u.email or ''}"} for u in users_qs]
|
||||
results = [
|
||||
{"id": u.pk, "text": f"{u.get_full_name() or u.username} - {u.email or ''}"}
|
||||
for u in users_qs
|
||||
]
|
||||
|
||||
return render(request, "generic/partials/user_search_widget_results.html", {"results": results, "field": field, "q": q})
|
||||
return render(
|
||||
request,
|
||||
"generic/partials/user_search_widget_results.html",
|
||||
{"results": results, "field": field, "q": q_raw},
|
||||
)
|
||||
|
||||
|
||||
@login_required
|
||||
|
||||
Reference in New Issue
Block a user