Add functionality to create linked supervisor account when creating a user

This commit is contained in:
Ross
2026-06-15 11:32:21 +01:00
parent 1f0877964b
commit c52cf46d65
5 changed files with 144 additions and 49 deletions
+7 -1
View File
@@ -12,7 +12,7 @@ from django.forms import (
DateField, DateField,
SplitDateTimeField, SplitDateTimeField,
SplitDateTimeWidget, SplitDateTimeWidget,
BooleanField,
) )
from django.forms import inlineformset_factory from django.forms import inlineformset_factory
from atlas.models import CaseCollection from atlas.models import CaseCollection
@@ -812,6 +812,12 @@ class UserUserForm(Form):
first_name = CharField(max_length=255, required=True) first_name = CharField(max_length=255, required=True)
last_name = CharField(max_length=255, required=True) last_name = CharField(max_length=255, required=True)
grade = ModelChoiceField(UserGrades.objects.all(), required=False) grade = ModelChoiceField(UserGrades.objects.all(), required=False)
create_supervisor = BooleanField(
required=False,
label="Create linked supervisor account",
help_text="If checked, a supervisor profile with this user's name and email will be automatically created.",
initial=False,
)
class TraineeForm(Form): class TraineeForm(Form):
@@ -1,30 +1,42 @@
{% extends "generic/base.html" %} {% extends "generic/base.html" %}
<!-- {% load static from static %} -->
{% load crispy_forms_tags %} {% load crispy_forms_tags %}
{% block css %}
{% endblock %}
{% block js %} {% block js %}
<!--<script type="text/javascript" src="/admin/jsi18n/"></script>--> {{ form.media }}
{{form.media}}
<script type="text/javascript">
</script>
<!-- {{ form.media }} -->
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<h2>Create Trainee</h2> <div class="container py-4">
Use this form to create a user. Only existing supervisors can be added (<a href="{% url 'generic:supervisor_create' %}">create them first</a> and refresh this page or add them later if they do not exist). <div class="row justify-content-center">
{% if errors %} <div class="col-md-8">
<div class="alert alert-info" role="alert">{{errors}}</a></div> <div class="card shadow-sm border border-secondary border-opacity-20">
{% endif %} <div class="card-header bg-primary text-white py-3">
<form action="" method="post" enctype="multipart/form-data" id="condition-form"> <h4 class="mb-0"><i class="bi bi-person-plus me-2"></i>Create Trainee</h4>
{% csrf_token %} </div>
<div class="card-body p-4">
<p class="text-muted mb-4">Use this form to create a user. Only existing supervisors can be added (if they don't exist, <a href="{% url 'generic:supervisor_create' %}" class="fw-bold">create them first</a> and refresh this page or add them later).</p>
<table> {% if errors %}
{{ form|crispy }} <div class="alert alert-danger" role="alert">
</table> <i class="bi bi-exclamation-triangle-fill me-2"></i>{{ errors }}
<input type="submit" class="submit-button" value="Submit" name="submit"> </div>
</form> {% endif %}
<form action="" method="post" enctype="multipart/form-data" id="condition-form">
{% csrf_token %}
{{ form|crispy }}
<div class="mt-4 d-flex justify-content-between">
<a href="{% url 'accounts_list' %}" class="btn btn-outline-secondary">
<i class="bi bi-x-lg me-1"></i>Cancel
</a>
<button type="submit" class="btn btn-primary px-4">
<i class="bi bi-check-lg me-1"></i>Create Trainee
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %} {% endblock %}
@@ -1,30 +1,42 @@
{% extends "generic/base.html" %} {% extends "generic/base.html" %}
<!-- {% load static from static %} -->
{% load crispy_forms_tags %} {% load crispy_forms_tags %}
{% block css %}
{% endblock %}
{% block js %} {% block js %}
<!--<script type="text/javascript" src="/admin/jsi18n/"></script>--> {{ form.media }}
{{form.media}}
<script type="text/javascript">
</script>
<!-- {{ form.media }} -->
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<h2>Create User</h2> <div class="container py-4">
Use this form to create a user. Create a trainee <a href="{% url 'create_trainee' %}">here</a>. <div class="row justify-content-center">
{% if errors %} <div class="col-md-8">
<div class="alert alert-info" role="alert">{{errors}}</a></div> <div class="card shadow-sm border border-secondary border-opacity-20">
{% endif %} <div class="card-header bg-primary text-white py-3">
<form action="" method="post" enctype="multipart/form-data" id="condition-form"> <h4 class="mb-0"><i class="bi bi-person-plus me-2"></i>Create User</h4>
{% csrf_token %} </div>
<div class="card-body p-4">
<p class="text-muted mb-4">Use this form to create a new user. To create a trainee instead, click <a href="{% url 'create_trainee' %}" class="fw-bold">here</a>.</p>
<table> {% if errors %}
{{ form|crispy }} <div class="alert alert-danger" role="alert">
</table> <i class="bi bi-exclamation-triangle-fill me-2"></i>{{ errors }}
<input type="submit" class="submit-button" value="Submit" name="submit"> </div>
</form> {% endif %}
<form action="" method="post" enctype="multipart/form-data" id="condition-form">
{% csrf_token %}
{{ form|crispy }}
<div class="mt-4 d-flex justify-content-between">
<a href="{% url 'accounts_list' %}" class="btn btn-outline-secondary">
<i class="bi bi-x-lg me-1"></i>Cancel
</a>
<button type="submit" class="btn btn-primary px-4">
<i class="bi bi-check-lg me-1"></i>Create User
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %} {% endblock %}
+14 -1
View File
@@ -5948,9 +5948,22 @@ def create_user(request, context=None, trainee: bool = False):
if trainee and form.cleaned_data.get("supervisor"): if trainee and form.cleaned_data.get("supervisor"):
supervisor = form.cleaned_data["supervisor"] supervisor = form.cleaned_data["supervisor"]
user_profile.supervisor = supervisor user_profile.supervisor = supervisor
user_profile.save() user_profile.save()
if not trainee and form.cleaned_data.get("create_supervisor"):
from generic.models import Supervisor
supervisor, created = Supervisor.objects.get_or_create(
email=new_user.email,
defaults={
"name": f"{new_user.first_name} {new_user.last_name}".strip(),
"user": new_user,
}
)
if not created and not supervisor.user:
supervisor.user = new_user
supervisor.name = f"{new_user.first_name} {new_user.last_name}".strip()
supervisor.save(update_fields=["user", "name"])
except Exception as error: except Exception as error:
errors = f"Unable to create account profile {error}" errors = f"Unable to create account profile {error}"
return render( return render(
+52
View File
@@ -280,5 +280,57 @@ def test_hide_exams_and_collections(client, create_users):
assert b"Test Physics Exam" in response_unhidden.content assert b"Test Physics Exam" in response_unhidden.content
@pytest.mark.django_db
def test_create_user_with_linked_supervisor(client, create_cid_manager, django_user_model):
from generic.models import Supervisor
client.force_login(create_cid_manager)
# 1. Create a user WITH create_supervisor checked
response = client.post(
reverse("create_user"),
{
"username": "newsupervisor@example.com",
"first_name": "New",
"last_name": "Supervisor",
"grade": "",
"create_supervisor": True,
},
)
assert response.status_code == 302
assert response.url == reverse("accounts_list")
# Verify user created
new_user = django_user_model.objects.get(username="newsupervisor@example.com")
assert new_user.first_name == "New"
assert new_user.last_name == "Supervisor"
# Verify linked supervisor created
supervisor = Supervisor.objects.get(email="newsupervisor@example.com")
assert supervisor.name == "New Supervisor"
assert supervisor.user == new_user
# 2. Create a user WITHOUT create_supervisor checked
response_no_sv = client.post(
reverse("create_user"),
{
"username": "regularuser@example.com",
"first_name": "Regular",
"last_name": "User",
"grade": "",
"create_supervisor": False,
},
)
assert response_no_sv.status_code == 302
# Verify user created
regular_user = django_user_model.objects.get(username="regularuser@example.com")
# Verify no supervisor was created
assert not Supervisor.objects.filter(email="regularuser@example.com").exists()