@@ -48,3 +62,56 @@
{% endif %}
{% endblock %}
+
+{% block extra_scripts %}
+
+{% endblock %}
diff --git a/generic/urls.py b/generic/urls.py
index 4b0c3aed..6d4fb9cc 100755
--- a/generic/urls.py
+++ b/generic/urls.py
@@ -57,6 +57,7 @@ urlpatterns = [
views.SupervisorAutocomplete.as_view(model=Supervisor),
name="supervisor-autocomplete",
),
+ path("user-search/", views.user_search, name="user_search"),
path(
"cids/manage//update", views.CidUserUpdate.as_view(), name="update_cid"
),
diff --git a/generic/views.py b/generic/views.py
index eb584762..e3512665 100644
--- a/generic/views.py
+++ b/generic/views.py
@@ -3067,13 +3067,12 @@ class GenericViewBase:
Accepts POST or GET parameters:
- category: integer primary key of category (optional)
- - status: one of the status codes (AC, OD, ER, RJ, IP), or 'UNREVIEWED' or 'ANY'
- If not supplied, the default behavior is to return only unreviewed questions.
+ - status: one of the status codes (AC, OD, ER, RJ, IP), or 'UNREVIEWED' or 'ANY'
"""
# Read filters
category = request.POST.get("category") or request.GET.get("category")
- status = request.POST.get("status") or request.GET.get("status") or "UNREVIEWED"
+ status = request.POST.get("status") or request.GET.get("status") or "ANY"
# How many matching questions to skip (useful for Skip button). Expected integer >= 0.
try:
skip = int(request.POST.get("skip") or request.GET.get("skip") or 0)
@@ -4277,7 +4276,7 @@ def trainees_bulk_update_from_spreadsheet(request):
current_grade = None
- for line in lines:
+ for idx, line in enumerate(lines):
if not line:
continue
@@ -4340,6 +4339,7 @@ def trainees_bulk_update_from_spreadsheet(request):
preview.append(
{
"raw": line,
+ "row_index": idx,
"name": name_part,
"norm_name": norm_name,
"first": first,
@@ -4355,7 +4355,21 @@ def trainees_bulk_update_from_spreadsheet(request):
updated = 0
skipped = 0
for row in preview:
- user = row.get("matched_user")
+ # Check for a manual override from the submitted form
+ manual_key = f"manual_user_{row.get('row_index')}"
+ manual_val = request.POST.get(manual_key)
+
+ user = None
+ if manual_val:
+ try:
+ user = User.objects.get(pk=int(manual_val))
+ except Exception:
+ user = None
+
+ # Fall back to automatic match
+ if not user:
+ user = row.get("matched_user")
+
if not user:
skipped += 1
continue
@@ -4385,6 +4399,22 @@ def trainees_bulk_update_from_spreadsheet(request):
)
+@user_is_cid_user_manager
+def user_search(request):
+ """Simple JSON endpoint to search users by name/email for manual matching."""
+ q = request.GET.get("q", "").strip()
+ results = []
+ if q:
+ qs = User.objects.filter(
+ Q(first_name__icontains=q) | Q(last_name__icontains=q) | Q(email__icontains=q)
+ ).order_by("last_name", "first_name")[:50]
+
+ for u in qs:
+ results.append({"id": u.pk, "text": f"{u.first_name} {u.last_name} ({u.email})"})
+
+ return JsonResponse({"results": results})
+
+
def create_trainee(request, context=None):
return create_user(request, context, trainee=True)