Refactor trainees bulk update logic to improve user matching criteria and enhance clarity in code comments
This commit is contained in:
@@ -1,20 +1,20 @@
|
|||||||
{% extends 'generic/base.html' %}
|
{% extends 'generic/base.html' %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h2>Bulk update trainees (paste spreadsheet text)</h2>
|
<h2>Bulk update trainees (paste spreadsheet text)</h2>
|
||||||
|
|
||||||
<p>Paste the spreadsheet rows below. Headings like "Year 1 - Group 1" are used to set the grade (Year N -> STN). Rows should contain "Name <tab or 2+spaces> Supervisor".</p>
|
<p>Paste the spreadsheet rows below. Headings like "Year 1 - Group 1" are used to set the grade (Year N -> STN). Rows should contain "Name <tab or 2+spaces> Supervisor".</p>
|
||||||
|
|
||||||
<form method="post">
|
<form method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<textarea name="data" rows="18" style="width:100%">{% if request.POST.data %}{{ request.POST.data }}{% endif %}</textarea>
|
<textarea name="data" rows="18" style="width:100%">{% if request.POST.data %}{{ request.POST.data }}{% endif %}</textarea>
|
||||||
<div style="margin-top:8px">
|
<div style="margin-top:8px">
|
||||||
<button type="submit" name="action" value="preview" class="btn btn-primary">Preview</button>
|
<button type="submit" name="action" value="preview" class="btn btn-primary">Preview</button>
|
||||||
<button type="submit" name="action" value="apply" class="btn btn-danger" onclick="return confirm('This will apply updates to matched trainee profiles. Are you sure?')">Apply updates</button>
|
<button type="submit" name="action" value="apply" class="btn btn-danger" onclick="return confirm('This will apply updates to matched trainee profiles. Are you sure?')">Apply updates</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{% if preview %}
|
{% if preview %}
|
||||||
<h3>Preview ({{ preview|length }} rows)</h3>
|
<h3>Preview ({{ preview|length }} rows)</h3>
|
||||||
<table class="table table-sm">
|
<table class="table table-sm">
|
||||||
<thead>
|
<thead>
|
||||||
@@ -40,11 +40,11 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if report %}
|
{% if report %}
|
||||||
<h3>Apply report</h3>
|
<h3>Apply report</h3>
|
||||||
<p>Updated: {{ report.updated }}; Skipped (no user matched): {{ report.skipped }}</p>
|
<p>Updated: {{ report.updated }}; Skipped (no user matched): {{ report.skipped }}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
+18
-3
@@ -4262,7 +4262,6 @@ def trainees_bulk_update_from_spreadsheet(request):
|
|||||||
- Preview: parse the pasted text and show rows with any matched user / supervisor
|
- Preview: parse the pasted text and show rows with any matched user / supervisor
|
||||||
- Apply: apply grade and supervisor updates for matched users
|
- Apply: apply grade and supervisor updates for matched users
|
||||||
"""
|
"""
|
||||||
logger.debug("Trainees bulk update called")
|
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
@@ -4309,11 +4308,27 @@ def trainees_bulk_update_from_spreadsheet(request):
|
|||||||
|
|
||||||
matched_user = None
|
matched_user = None
|
||||||
if first and last:
|
if first and last:
|
||||||
|
# Prefer an exact first+last match
|
||||||
qs = User.objects.filter(first_name__iexact=first, last_name__iexact=last)
|
qs = User.objects.filter(first_name__iexact=first, last_name__iexact=last)
|
||||||
if not qs.exists():
|
|
||||||
qs = User.objects.filter(last_name__iexact=last)
|
|
||||||
if qs.exists():
|
if qs.exists():
|
||||||
matched_user = qs.first()
|
matched_user = qs.first()
|
||||||
|
else:
|
||||||
|
# Fallback: only match by last name when the last-name query returns
|
||||||
|
# a single candidate AND the candidate's first name reasonably
|
||||||
|
# matches the provided first token. This avoids ambiguous matches
|
||||||
|
# (e.g. Maria vs Stephanie both 'Bailey'). We accept a match when
|
||||||
|
# the candidate first name startswith the first 3 chars of the
|
||||||
|
# provided first token, or contains it as a substring (case-ins).
|
||||||
|
candidates = User.objects.filter(last_name__iexact=last)
|
||||||
|
if candidates.count() == 1:
|
||||||
|
candidate = candidates.first()
|
||||||
|
cand_first = (candidate.first_name or "").strip()
|
||||||
|
if cand_first:
|
||||||
|
# take at most 3 chars for prefix comparison
|
||||||
|
prefix = first[:3].lower()
|
||||||
|
cand_first_low = cand_first.lower()
|
||||||
|
if cand_first_low.startswith(prefix) or first.lower() in cand_first_low:
|
||||||
|
matched_user = candidate
|
||||||
|
|
||||||
matched_supervisor = None
|
matched_supervisor = None
|
||||||
if supervisor_part:
|
if supervisor_part:
|
||||||
|
|||||||
Reference in New Issue
Block a user