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)
|
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":
|
if request.method == "GET":
|
||||||
|
logger.debug(f"exam_collection_bulk_add_groups GET request by user={request.user.username}")
|
||||||
form = ExamCollectionBulkAddGroupsForm(user=request.user)
|
form = ExamCollectionBulkAddGroupsForm(user=request.user)
|
||||||
return render(request, "generic/partials/examcollection_bulk_add_groups_form.html", {"form": form, "collection": collection})
|
return render(request, "generic/partials/examcollection_bulk_add_groups_form.html", {"form": form, "collection": collection})
|
||||||
|
|
||||||
# POST
|
# POST
|
||||||
form = ExamCollectionBulkAddGroupsForm(request.POST, user=request.user)
|
form = ExamCollectionBulkAddGroupsForm(request.POST, user=request.user)
|
||||||
if not form.is_valid():
|
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})
|
return render(request, "generic/partials/examcollection_bulk_add_groups_form.html", {"form": form, "collection": collection})
|
||||||
|
|
||||||
cid_groups = form.cleaned_data.get("cid_user_groups")
|
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
|
Returns a small HTML fragment listing up to 10 matching users. Each
|
||||||
result will call `addUserToField(field, id, text)` when clicked.
|
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")
|
field = request.GET.get("field") or request.GET.get("row")
|
||||||
if not q:
|
if not q_raw:
|
||||||
return HttpResponse("")
|
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(
|
users_qs = User.objects.filter(
|
||||||
Q(first_name__icontains=q)
|
Q(first_name__icontains=q)
|
||||||
| Q(last_name__icontains=q)
|
| Q(last_name__icontains=q)
|
||||||
@@ -5949,19 +6005,28 @@ def user_search_widget(request):
|
|||||||
| Q(username__icontains=q)
|
| Q(username__icontains=q)
|
||||||
)
|
)
|
||||||
|
|
||||||
# optional grade filter (UserProfile.grade FK)
|
# optional grade filter (UserProfile.grade FK) still applies and will narrow results
|
||||||
grade = request.GET.get('grade')
|
grade = request.GET.get("grade")
|
||||||
if grade:
|
if grade:
|
||||||
try:
|
try:
|
||||||
users_qs = users_qs.filter(userprofile__grade_id=int(grade))
|
users_qs = users_qs.filter(userprofile__grade_id=int(grade))
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
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
|
@login_required
|
||||||
|
|||||||
Reference in New Issue
Block a user