allow adding users to exams by group

This commit is contained in:
Ross
2024-09-02 12:16:37 +01:00
parent cf9b514597
commit 4f60440ea1
4 changed files with 129 additions and 57 deletions
+13 -9
View File
@@ -68,6 +68,7 @@ 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
class SplitDateTimeFieldDefaultTime(SplitDateTimeField): class SplitDateTimeFieldDefaultTime(SplitDateTimeField):
def compress(self, data_list): def compress(self, data_list):
if data_list: if data_list:
@@ -111,13 +112,12 @@ 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)
@@ -199,6 +199,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"]
@@ -227,8 +228,12 @@ class ExamGroupsFormMixin(ModelForm):
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
)
user_user_group_queryset = UserUserGroup.objects.none(
archive=False, open_access=True
)
# 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()
@@ -751,4 +756,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%;
+10
View File
@@ -120,6 +120,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(),
+35
View File
@@ -133,6 +133,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:
@@ -2709,6 +2720,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):