feat: Implement supervisor trainee management features, including search, add, and remove functionalities

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
Ross
2026-04-29 22:27:09 +01:00
co-authored by Copilot
parent f63f3b3687
commit 89c8cd565f
10 changed files with 500 additions and 82 deletions
+56
View File
@@ -45,6 +45,7 @@ from django.urls import reverse_lazy, reverse
from django.http import Http404, JsonResponse
from django.http import HttpResponseRedirect, HttpResponse
from django.views.decorators.http import require_POST
from physics.models import UserAnswer as PhysicsUserAnswer
from physics.models import Exam as PhysicsExam
@@ -120,6 +121,12 @@ def people(request):
@login_required
def profile(request):
"""Render the current user's profile page with group management context.
Scope: authenticated user's own profile view.
Functionality: provides user details, available groups, and supervisor
account state for template actions.
"""
user = request.user
available_groups = UserUserGroup.objects.filter(archive=False).exclude(
pk__in=user.user_groups.values_list("pk", flat=True)
@@ -128,6 +135,7 @@ def profile(request):
pk__in=user.groups.values_list("pk", flat=True)
)
is_cid_user_manager = request.user.groups.filter(name="cid_user_manager").exists()
supervisor_account = Supervisor.objects.filter(user=user).first()
return render(
request,
"profile.html",
@@ -136,6 +144,8 @@ def profile(request):
"available_groups": available_groups,
"available_auth_groups": available_auth_groups,
"is_cid_user_manager": is_cid_user_manager,
"supervisor_account": supervisor_account,
"supervisor_action": request.GET.get("supervisor_action", ""),
},
)
@@ -160,6 +170,12 @@ def index(request):
@user_is_cid_user_manager
def account_profile(request, slug):
"""Render an account profile page for administrators.
Scope: cid_user_manager and superusers viewing another user's profile.
Functionality: exposes profile details, group controls, and supervisor
account creation/link status for admin actions.
"""
user = get_object_or_404(User, username=slug)
available_groups = UserUserGroup.objects.filter(archive=False).exclude(
pk__in=user.user_groups.values_list("pk", flat=True)
@@ -168,6 +184,7 @@ def account_profile(request, slug):
pk__in=user.groups.values_list("pk", flat=True)
)
is_cid_user_manager = request.user.groups.filter(name="cid_user_manager").exists()
supervisor_account = Supervisor.objects.filter(user=user).first()
return render(
request,
"profile.html",
@@ -176,10 +193,49 @@ def account_profile(request, slug):
"available_groups": available_groups,
"available_auth_groups": available_auth_groups,
"is_cid_user_manager": is_cid_user_manager,
"supervisor_account": supervisor_account,
"supervisor_action": request.GET.get("supervisor_action", ""),
},
)
@require_POST
@user_is_cid_user_manager
def account_set_supervisor(request, slug):
"""Create or link a Supervisor record for an existing user.
Scope: cid_user_manager and superusers only.
Functionality: one-click action from profile pages to assign a user as a
supervisor account, reusing an existing supervisor by email when safe.
"""
user = get_object_or_404(User, username=slug)
next_url = request.POST.get("next") or reverse("account_profile", kwargs={"slug": user.username})
email = (user.email or "").strip().lower()
if not email:
return redirect(f"{next_url}?supervisor_action=no_email")
existing_for_user = Supervisor.objects.filter(user=user).first()
if existing_for_user:
return redirect(f"{next_url}?supervisor_action=already_linked")
supervisor = Supervisor.objects.filter(email__iexact=email).first()
if supervisor:
if supervisor.user and supervisor.user != user:
return redirect(f"{next_url}?supervisor_action=email_in_use")
supervisor.user = user
if not supervisor.name:
supervisor.name = user.get_full_name().strip() or user.username
supervisor.save()
return redirect(f"{next_url}?supervisor_action=linked")
name = user.get_full_name().strip() or user.username
Supervisor.objects.create(email=email, name=name, user=user)
return redirect(f"{next_url}?supervisor_action=created")
def cid_selector(request):
return render(
request,