Compare commits

...
4 Commits
Author SHA1 Message Date
Ross 80412dcbe0 improve trainee creation 2024-09-02 14:14:57 +01:00
Ross a024d7fca7 start cleaning up trainee management 2024-09-02 12:58:05 +01:00
Ross 33b49c9430 change user scores html base 2024-09-02 12:34:32 +01:00
Ross 4f60440ea1 allow adding users to exams by group 2024-09-02 12:16:37 +01:00
15 changed files with 269 additions and 81 deletions
@@ -1,4 +1,4 @@
{% extends 'anatomy/base.html' %} {% extends 'base.html' %}
{% block content %} {% block content %}
<div class="question-display-block"> <div class="question-display-block">
+66 -50
View File
@@ -68,6 +68,9 @@ from django.forms.utils import from_current_timezone, to_current_timezone
from crispy_forms.helper import FormHelper from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit from crispy_forms.layout import Submit
from dal import autocomplete
class SplitDateTimeFieldDefaultTime(SplitDateTimeField): class SplitDateTimeFieldDefaultTime(SplitDateTimeField):
def compress(self, data_list): def compress(self, data_list):
if data_list: if data_list:
@@ -79,9 +82,9 @@ class SplitDateTimeFieldDefaultTime(SplitDateTimeField):
) )
if data_list[1] in self.empty_values: if data_list[1] in self.empty_values:
data_list[1] = datetime.time(0, 1) data_list[1] = datetime.time(0, 1)
#raise ValidationError( # raise ValidationError(
# self.error_messages["invalid_time"], code="invalid_time" # self.error_messages["invalid_time"], code="invalid_time"
#) # )
result = datetime.datetime.combine(*data_list) result = datetime.datetime.combine(*data_list)
return from_current_timezone(result) return from_current_timezone(result)
return None return None
@@ -98,9 +101,9 @@ class SplitDateTimeFieldDefaultTimeEnd(SplitDateTimeField):
) )
if data_list[1] in self.empty_values: if data_list[1] in self.empty_values:
data_list[1] = datetime.time(23, 59) data_list[1] = datetime.time(23, 59)
#raise ValidationError( # raise ValidationError(
# self.error_messages["invalid_time"], code="invalid_time" # self.error_messages["invalid_time"], code="invalid_time"
#) # )
result = datetime.datetime.combine(*data_list) result = datetime.datetime.combine(*data_list)
return from_current_timezone(result) return from_current_timezone(result)
return None return None
@@ -111,29 +114,28 @@ class ExamFormMixin:
super(ModelForm, self).__init__(*args, **kwargs) super(ModelForm, self).__init__(*args, **kwargs)
self.helper = FormHelper() self.helper = FormHelper()
self.helper.form_id = 'id-exam-form' self.helper.form_id = "id-exam-form"
self.helper.form_class = 'exam-form' self.helper.form_class = "exam-form"
self.helper.form_method = 'post' self.helper.form_method = "post"
self.helper.form_action = 'submit' self.helper.form_action = "submit"
self.helper.add_input(Submit("submit", "Submit"))
self.helper.add_input(Submit('submit', 'Submit')) # if user.is_superuser or user.groups.filter(name="cid_user_manager").exists():
#if user.is_superuser or user.groups.filter(name="cid_user_manager").exists():
# cid_user_group_queryset = CidUserGroup.objects.filter(archive=False) # cid_user_group_queryset = CidUserGroup.objects.filter(archive=False)
# user_user_group_queryset = UserUserGroup.objects.filter(archive=False) # user_user_group_queryset = UserUserGroup.objects.filter(archive=False)
#else: # else:
# cid_user_group_queryset = CidUserGroup.objects.none() # cid_user_group_queryset = CidUserGroup.objects.none()
# user_user_group_queryset = UserUserGroup.objects.none() # user_user_group_queryset = UserUserGroup.objects.none()
#self.fields["cid_user_groups"] = ModelMultipleChoiceField( # self.fields["cid_user_groups"] = ModelMultipleChoiceField(
# required=False, # required=False,
# queryset=cid_user_group_queryset, # queryset=cid_user_group_queryset,
#) # )
#self.fields["user_user_groups"] = ModelMultipleChoiceField( # self.fields["user_user_groups"] = ModelMultipleChoiceField(
# required=False, # required=False,
# queryset=user_user_group_queryset, # queryset=user_user_group_queryset,
#) # )
self.fields["start_date"] = SplitDateTimeFieldDefaultTime( self.fields["start_date"] = SplitDateTimeFieldDefaultTime(
widget=SplitDateTimeWidget( widget=SplitDateTimeWidget(
date_attrs={"type": "date", "class": "datepicker"}, date_attrs={"type": "date", "class": "datepicker"},
@@ -169,8 +171,8 @@ class ExamFormMixin:
"active", "active",
"publish_results", "publish_results",
"archive", "archive",
#"cid_user_groups", # "cid_user_groups",
#"user_user_groups", # "user_user_groups",
# "author", # "author",
] ]
@@ -199,6 +201,7 @@ class ExamAuthorFormMixin(ModelForm):
widget=FilteredSelectMultiple(verbose_name="Authors", is_stacked=False), widget=FilteredSelectMultiple(verbose_name="Authors", is_stacked=False),
) )
class ExamMarkerFormMixin(ModelForm): class ExamMarkerFormMixin(ModelForm):
class Meta: class Meta:
fields = ["markers"] fields = ["markers"]
@@ -213,37 +216,41 @@ class ExamMarkerFormMixin(ModelForm):
class ExamGroupsFormMixin(ModelForm): class ExamGroupsFormMixin(ModelForm):
class Meta: class Meta:
#model = ExamCollection # model = ExamCollection
fields = ("cid_user_groups", "user_user_groups") fields = ("cid_user_groups", "user_user_groups")
def __init__(self, *args, user, **kwargs) -> None: def __init__(self, *args, user, **kwargs) -> None:
super(ModelForm, self).__init__(*args, **kwargs) super(ModelForm, self).__init__(*args, **kwargs)
# TODO: decide how we should handle managing user groups # TODO: decide how we should handle managing user groups
# i.e who should be able to access a list of all users? # i.e who should be able to access a list of all users?
if user.is_superuser or user.groups.filter(name="cid_user_manager").exists(): if user.is_superuser or user.groups.filter(name="cid_user_manager").exists():
cid_user_group_queryset = CidUserGroup.objects.filter(archive=False) cid_user_group_queryset = CidUserGroup.objects.filter(archive=False)
user_user_group_queryset = UserUserGroup.objects.filter(archive=False) user_user_group_queryset = UserUserGroup.objects.filter(archive=False)
else: else:
cid_user_group_queryset = CidUserGroup.objects.filter(archive=False, open_access=True) cid_user_group_queryset = CidUserGroup.objects.filter(
user_user_group_queryset = UserUserGroup.objects.none(archive=False, open_access=True) archive=False, open_access=True
#cid_user_group_queryset = CidUserGroup.objects.none()
#user_user_group_queryset = UserUserGroup.objects.none()
self.fields["cid_user_groups"] = ModelMultipleChoiceField(
required=False,
queryset=cid_user_group_queryset,
label="CID User Groups",
) )
self.fields["user_user_groups"] = ModelMultipleChoiceField( user_user_group_queryset = UserUserGroup.objects.none(
required=False, archive=False, open_access=True
queryset=user_user_group_queryset,
label="User Groups",
) )
self.fields["cid_user_groups"].widget.attrs["size"] = 8 # cid_user_group_queryset = CidUserGroup.objects.none()
self.fields["user_user_groups"].widget.attrs["size"] = 8 # user_user_group_queryset = UserUserGroup.objects.none()
self.fields["cid_user_groups"] = ModelMultipleChoiceField(
required=False,
queryset=cid_user_group_queryset,
label="CID User Groups",
)
self.fields["user_user_groups"] = ModelMultipleChoiceField(
required=False,
queryset=user_user_group_queryset,
label="User Groups",
)
self.fields["cid_user_groups"].widget.attrs["size"] = 8
self.fields["user_user_groups"].widget.attrs["size"] = 8
class ExaminationForm(ModelForm): class ExaminationForm(ModelForm):
@@ -667,13 +674,23 @@ 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)
class TraineeForm(Form):
username = EmailField(
required=True,
help_text="Username / email should be the same. Ideally this should be an nhs email.",
)
first_name = CharField(max_length=255, required=True)
last_name = CharField(max_length=255, required=True)
grade = ModelChoiceField(UserGrades.objects.all(), required=False)
supervisor = ModelChoiceField( supervisor = ModelChoiceField(
Supervisor.objects.all(), required=False Supervisor.objects.all(), required=False, widget=autocomplete.ModelSelect2(url='generic:supervisor-autocomplete')
) # Needs to be a user/object ref ) # Needs to be a user/object ref
#
# class Meta: #class Meta:
# # widgets = {
# fields = ("username", "first_name", "last_name") # "supervisor" : autocomplete.ModelSelect2(url='generic:supervisor-autocomplete')
# }
class SupervisorForm(ModelForm): class SupervisorForm(ModelForm):
@@ -751,4 +768,3 @@ class ExamCollectionCloneForm(ModelForm):
class Meta: class Meta:
model = ExamCollection model = ExamCollection
fields = ("name", "date") fields = ("name", "date")
@@ -39,44 +39,67 @@
{% endif %} {% endif %}
{% endif %} {% endif %}
<h3>Exams</h3> <h3>Exams</h3>
The group is currently associated with the following exams. The group is currently associated with the following exams. This allows the users within the group to be easily added to the exam. It will not automatically give members of the group access to the exam.
{% if group_type == "cid" %} {% if group_type == "cid" %}
Add more <a href='{% url "generic:cid_group_exams" group.pk %}'>here</a>. Add more <a href='{% url "generic:cid_group_exams" group.pk %}'>here</a>.
{% else %} {% else %}
Add more <a href='{% url "generic:user_group_exams" group.pk %}'>here</a>. Add more <a href='{% url "generic:user_group_exams" group.pk %}'>here</a>.
{% endif %} {% endif %}
<p>It is possible to quickly add or remove all group candidates from an exam by clicking the buttons below. This will only affect the <b>{{users|length}}</b> candidates in this group. You will need to refresh the page to see updated candidate counts.</p>
{% with group.GetGroupExams as exam_map %} {% with group.GetGroupExams as exam_map %}
{% for key, value in exam_map.items %} {% for key, value in exam_map.items %}
<h4>{{key}}</h4> <h4>{{key}}</h4>
<ul> <div>
{% for exam in value %} {% for exam in value %}
<li> <div class="d-flex">
<a href="{{exam.get_absolute_url}}">{{exam}}</a> <a href="{{exam.get_absolute_url}}" class="px-2">{{exam}}</a>
{% if group_type == "cid" %} {% if group_type == "cid" %}
<a href="{{exam.get_cid_edit_url}}" class="edit-link">Edit candidates</a> <a href="{{exam.get_cid_edit_url}}" class="edit-link" px-2>Edit candidates</a>
{% comment %} <button>Add group candidates</button>
<button>Remove group candidates</button> {% endcomment %}
{% else %} {% else %}
<a href="{{exam.get_user_edit_url}}" class="edit-link">Edit candidates</a> <a href="{{exam.get_user_edit_url}}" class="edit-link px-2">Edit candidates</a>
<form>
<input type="hidden" name="exam_id" value="{{exam.pk}}">
<input type="hidden" name="exam_type" value="{{exam.app_name}}">
<button
class="btn btn-sm px-2"
hx-post="{% url 'generic:user_group_add_candidates_to_exams' group.pk %}"
title="Add all group candidates to this exam"
name="action"
value="add"
>Add group candidates</button>
<button
hx-post="{% url 'generic:user_group_add_candidates_to_exams' group.pk %}"
title="Remove all group candidates to this exam"
class="btn btn-sm px-2"
name="action"
value="remove"
>Remove group candidates</button>
</form>
{% endif %} {% endif %}
<span class="cid-number">
<div class="px-2 ms-auto ">
{% if group_type == "cid" %} {% if group_type == "cid" %}
(Cid candidate count: {{exam.valid_cid_users.count}}) (Cid candidate count: {{exam.valid_cid_users.count}})
{% else %} {% else %}
(User candidate count: {{exam.valid_user_users.count}}) (User candidate count: {{exam.valid_user_users.count}})
{% endif %} {% endif %}
</span> </div>
</li> </div>
{% endfor %} {% endfor %}
</ul> </div>
{% endfor %} {% endfor %}
{% endwith %} {% endwith %}
{% endblock %} {% endblock %}
{% block js %} {% block js %}
<style> <style>
td, th { padding-left: 10px } td, th { padding-left: 10px }
.cid-number {float: right}
.edit-link { .edit-link {
font-size: smaller; font-size: smaller;
opacity: 60%; opacity: 60%;
+30
View File
@@ -0,0 +1,30 @@
{% extends "generic/base.html" %}
<!-- {% load static from static %} -->
{% load crispy_forms_tags %}
{% block css %}
{% endblock %}
{% block js %}
<!--<script type="text/javascript" src="/admin/jsi18n/"></script>-->
{{form.media}}
<script type="text/javascript">
</script>
<!-- {{ form.media }} -->
{% endblock %}
{% block content %}
<h2>Create Trainee</h2>
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).
{% if errors %}
<div class="alert alert-info" role="alert">{{errors}}</a></div>
{% endif %}
<form action="" method="post" enctype="multipart/form-data" id="condition-form">
{% csrf_token %}
<table>
{{ form|crispy }}
</table>
<input type="submit" class="submit-button" value="Submit" name="submit">
</form>
{% endblock %}
+7 -1
View File
@@ -9,6 +9,7 @@
{% for i in "123456"|make_list %} {% for i in "123456"|make_list %}
<a href='{% url "trainees_grade" "ST"|add:i %}'>ST{{i}}</a>{% if not forloop.last %}, {% endif %} <a href='{% url "trainees_grade" "ST"|add:i %}'>ST{{i}}</a>{% if not forloop.last %}, {% endif %}
{% endfor %} {% endfor %}
(<a href="{% url 'create_trainee' %}" title="Click to add a trainee to the platform, creating an account for them.">Add trainee</a>)
<h2> <h2>
{% if grade %} {% if grade %}
@@ -38,7 +39,12 @@
<a href="{% url 'account_profile_update' trainee.user.username %}" class="add-supervisor">add</a> <a href="{% url 'account_profile_update' trainee.user.username %}" class="add-supervisor">add</a>
{% endif %} {% endif %}
</td> </td>
<td><a href="{% url 'account_update' trainee.user.username %}">User</a>/<a href="{% url 'account_profile_update' trainee.user.username %}">Profile</a></td> <td><a href="{% url 'account_update' trainee.user.username %}">User</a>/<a href="{% url 'account_profile_update' trainee.user.username %}">Profile</a>
<span class="hover-highlight"><i class="bi bi-x-circle" title="Click to remove trainee status"
hx-get = "{% url 'generic:user_not_trainee' trainee.user.pk %}"
></i></span>
</td>
</tr> </tr>
{% endfor %} {% endfor %}
@@ -1,5 +1,6 @@
{% extends "generic/base.html" %} {% extends "generic/base.html" %}
<!-- {% load static from static %} --> <!-- {% load static from static %} -->
{% load crispy_forms_tags %}
{% block css %} {% block css %}
{% endblock %} {% endblock %}
@@ -13,8 +14,8 @@
<!-- {{ form.media }} --> <!-- {{ form.media }} -->
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<h2>Edit User / {{ciduser.cid}}</h2> <h2>Create User</h2>
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). Use this form to create a user. Create a trainee <a href="{% url 'create_trainee' %}">here</a>.
{% if errors %} {% if errors %}
<div class="alert alert-info" role="alert">{{errors}}</a></div> <div class="alert alert-info" role="alert">{{errors}}</a></div>
{% endif %} {% endif %}
@@ -22,7 +23,7 @@ Use this form to create a user. Only existing supervisors can be added (<a href=
{% csrf_token %} {% csrf_token %}
<table> <table>
{{ form.as_table }} {{ form|crispy }}
</table> </table>
<input type="submit" class="submit-button" value="Submit" name="submit"> <input type="submit" class="submit-button" value="Submit" name="submit">
</form> </form>
+23 -1
View File
@@ -1,6 +1,6 @@
from django.urls import path, include from django.urls import path, include
from generic.models import Examination from generic.models import Examination, Supervisor
from . import views from . import views
from generic.views import ExamViews as GenericExamViews, GenericViewBase from generic.views import ExamViews as GenericExamViews, GenericViewBase
@@ -42,6 +42,13 @@ urlpatterns = [
), ),
name="examination-autocomplete", name="examination-autocomplete",
), ),
path(
"supervisor-autocomplete",
views.SupervisorAutocomplete.as_view(
model=Supervisor
),
name="supervisor-autocomplete",
),
path( path(
"cids/manage/<int:pk>/update", views.CidUserUpdate.as_view(), name="update_cid" "cids/manage/<int:pk>/update", views.CidUserUpdate.as_view(), name="update_cid"
), ),
@@ -113,6 +120,11 @@ urlpatterns = [
views.users_bulk_edit, views.users_bulk_edit,
name="users_bulk_edit", name="users_bulk_edit",
), ),
path(
"user/<int:user_id>/not_trainee",
views.user_not_trainee,
name="user_not_trainee",
),
path("user/group/", views.user_group_view, name="user_group_view"), path("user/group/", views.user_group_view, name="user_group_view"),
path("user/group/all", views.user_group_view_all, name="user_group_view_all"), path("user/group/all", views.user_group_view_all, name="user_group_view_all"),
path( path(
@@ -120,6 +132,16 @@ urlpatterns = [
views.user_group_view_detail, views.user_group_view_detail,
name="user_group_detail", name="user_group_detail",
), ),
path(
"user/group/<int:group_id>/add",
views.user_group_add_candidates_to_exams,
name="user_group_add_candidates_to_exams",
),
path(
"user/group/<int:group_id>/",
views.user_group_view_detail,
name="user_group_detail",
),
path( path(
"user/group/<int:pk>/exams", "user/group/<int:pk>/exams",
views.UserGroupExamUpdate.as_view(), views.UserGroupExamUpdate.as_view(),
+85 -9
View File
@@ -53,7 +53,7 @@ from generic.mixins import CheckCanEditMixin, SuperuserRequiredMixin
import zipfile import zipfile
from django.core.files.base import ContentFile from django.core.files.base import ContentFile
from django.db.models import Q
from .forms import ( from .forms import (
CidGroupExamForm, CidGroupExamForm,
@@ -65,6 +65,7 @@ from .forms import (
CidUserGroupForm, CidUserGroupForm,
ExaminationMergeForm, ExaminationMergeForm,
SupervisorForm, SupervisorForm,
TraineeForm,
UserGroupExamForm, UserGroupExamForm,
CidGroupExamForm, CidGroupExamForm,
UserUserForm, UserUserForm,
@@ -133,6 +134,17 @@ class CidManagerRequiredMixin(UserPassesTestMixin):
# return obj # return obj
# raise PermissionDenied() # or Http404 # raise PermissionDenied() # or Http404
def get_exam_model_from_app_name(app_name: str) -> ExamBase:
EXAM_MAP = {
"physics": PhysicsExam,
"anatomy": AnatomyExam,
"rapids": RapidsExam,
"longs": LongsExam,
"sbas": SbasExam,
}
return EXAM_MAP[app_name]
def normaliseRapidsScore(score): def normaliseRapidsScore(score):
if score == 49: if score == 49:
@@ -2597,6 +2609,23 @@ class ExaminationAutocomplete(autocomplete.Select2QuerySetView):
return Examination.objects.none() return Examination.objects.none()
return qs return qs
class SupervisorAutocomplete(autocomplete.Select2QuerySetView):
def get_queryset(self):
# TODO: we should probably filter this to only
# allow access by trainees / other suprevisors
if not self.request.user.is_authenticated:
return Supervisor.objects.none()
qs = Supervisor.objects.all()
if self.q:
# This raises a fielderror which breaks creating a new item if not caught
try:
qs = qs.filter(Q(email__icontains=self.q) | Q(name__icontains=self.q))
except FieldError:
return Supervisor.objects.none()
return qs
class CidUserExamView(CidManagerRequiredMixin, SingleTableMixin, FilterView): class CidUserExamView(CidManagerRequiredMixin, SingleTableMixin, FilterView):
@@ -2709,6 +2738,30 @@ def cid_group_view_all(request):
{"groups": groups, "view_all": True}, {"groups": groups, "view_all": True},
) )
@user_is_cid_user_manager
def user_group_add_candidates_to_exams(request, group_id):
if request.htmx:
group = get_object_or_404(UserUserGroup, pk=group_id)
exam_id = request.POST.get("exam_id")
exam_type = request.POST.get("exam_type")
ExamModel = get_exam_model_from_app_name(exam_type)
exam = get_object_or_404(ExamModel, pk=exam_id)
if request.POST.get("action") == "remove":
exam.valid_user_users.remove(*group.users.all())
return HttpResponse(f"Candidates removed")
else:
exam.valid_user_users.add(*group.users.all())
return HttpResponse(f"Candidates added")
#exam.save()
raise PermissionDenied() # or Http404
pass
@user_is_cid_user_manager @user_is_cid_user_manager
def user_group_view_detail(request, group_id): def user_group_view_detail(request, group_id):
@@ -2756,6 +2809,14 @@ def get_user_selection_from_request(request):
user_models = User.objects.filter(id__in=selected_users) user_models = User.objects.filter(id__in=selected_users)
return user_models return user_models
@user_is_cid_user_manager
def user_not_trainee(request, user_id):
user = get_object_or_404(User, pk=user_id)
user.userprofile.peninsula_trainee = False
user.userprofile.save()
return HttpResponse(f"{user.username} is no longer a trainee", content_type="text/plain")
@user_is_cid_user_manager @user_is_cid_user_manager
def users_bulk_edit(request): def users_bulk_edit(request):
@@ -3321,12 +3382,24 @@ def trainees(request, grade: None|str=None):
return render(request, "generic/trainees.html", context) return render(request, "generic/trainees.html", context)
def create_user(request, context=None): def create_trainee(request, context=None):
return create_user(request, context, trainee=True)
@user_is_cid_user_manager
def create_user(request, context=None, trainee: bool=False):
if trainee:
form_template = "generic/trainee_creation_form.html"
else:
form_template = "generic/user_creation_form.html"
# if this is a POST request we need to process the form data # if this is a POST request we need to process the form data
if request.method == "POST": if request.method == "POST":
# create a form instance and populate it with data from the request: # create a form instance and populate it with data from the request:
form = UserUserForm(request.POST) if trainee:
form = TraineeForm(request.POST)
else:
form = UserUserForm(request.POST)
# check whether it's valid: # check whether it's valid:
if form.is_valid(): if form.is_valid():
try: try:
user_dict = { user_dict = {
@@ -3342,19 +3415,19 @@ def create_user(request, context=None):
errors = f"Unable to create account: {error}" errors = f"Unable to create account: {error}"
return render( return render(
request, request,
"generic/user_creation_form.html", form_template,
{"form": form, "errors": errors}, {"form": form, "errors": errors},
) )
try: try:
user_profile = UserProfile.objects.get(user=new_user) user_profile = UserProfile.objects.get(user=new_user)
user_profile.peninsula_trainee = True user_profile.peninsula_trainee = trainee
if request.POST["grade"]: if request.POST["grade"]:
grade = UserGrades.objects.get(pk=request.POST["grade"]) grade = UserGrades.objects.get(pk=request.POST["grade"])
user_profile.grade = grade user_profile.grade = grade
if request.POST["supervisor"]: if "supervisor" in request.POST and request.POST["supervisor"]:
supervisor = Supervisor.objects.get(pk=request.POST["supervisor"]) supervisor = Supervisor.objects.get(pk=request.POST["supervisor"])
user_profile.supervisor = supervisor user_profile.supervisor = supervisor
@@ -3364,7 +3437,7 @@ def create_user(request, context=None):
errors = f"Unable to create account profile {error}" errors = f"Unable to create account profile {error}"
return render( return render(
request, request,
"generic/user_creation_form.html", form_template,
{"form": form, "errors": errors}, {"form": form, "errors": errors},
) )
@@ -3373,9 +3446,12 @@ def create_user(request, context=None):
# if a GET (or any other method) we'll create a blank form # if a GET (or any other method) we'll create a blank form
else: else:
form = UserUserForm() if trainee:
form = TraineeForm()
else:
form = UserUserForm()
return render(request, "generic/user_creation_form.html", {"form": form}) return render(request, form_template, {"form": form})
class SupervisorDetail(CidManagerRequiredMixin, DetailView): class SupervisorDetail(CidManagerRequiredMixin, DetailView):
+1 -1
View File
@@ -1,4 +1,4 @@
{% extends 'longs/base.html' %} {% extends 'base.html' %}
{% block content %} {% block content %}
<div class="question-display-block"> <div class="question-display-block">
@@ -1,4 +1,4 @@
{% extends 'physics/base.html' %} {% extends 'base.html' %}
{% block content %} {% block content %}
<div class="physics"> <div class="physics">
+8
View File
@@ -1275,3 +1275,11 @@ tr:has(> td > a) {
color: lightgray; color: lightgray;
overflow: auto; overflow: auto;
} }
.hover-highlight {
opacity: 50%;
}
.hover-highlight:hover {
opacity: 100%;
color:purple;
}
+3
View File
@@ -75,6 +75,9 @@ urlpatterns = [
path( path(
"accounts/create/", generic_views.create_user, name="create_user" "accounts/create/", generic_views.create_user, name="create_user"
), ),
path(
"accounts/trainees/create/", generic_views.create_trainee, name="create_trainee"
),
path( path(
"accounts/trainees/", generic_views.trainees, name="trainees" "accounts/trainees/", generic_views.trainees, name="trainees"
), ),
@@ -1,4 +1,4 @@
{% extends 'rapids/base.html' %} {% extends 'base.html' %}
{% block content %} {% block content %}
<div class="question-display-block"> <div class="question-display-block">
+1 -1
View File
@@ -1,4 +1,4 @@
{% extends 'sbas/base.html' %} {% extends 'base.html' %}
{% block content %} {% block content %}
<div class="sbas"> <div class="sbas">
+3
View File
@@ -7,7 +7,10 @@
</div> </div>
{% endif %} {% endif %}
<div> <div>
<br/>
{% if cid %} {% if cid %}
<a href="{% url 'cid_scores' cid passcode %}">Other exams</a> <a href="{% url 'cid_scores' cid passcode %}">Other exams</a>
{% else %}
<a href="{% url 'user_scores' %}">Other exams</a>
{% endif %} {% endif %}
</div> </div>