allow adding users to exams by group
This commit is contained in:
+49
-45
@@ -68,6 +68,7 @@ from django.forms.utils import from_current_timezone, to_current_timezone
|
||||
from crispy_forms.helper import FormHelper
|
||||
from crispy_forms.layout import Submit
|
||||
|
||||
|
||||
class SplitDateTimeFieldDefaultTime(SplitDateTimeField):
|
||||
def compress(self, data_list):
|
||||
if data_list:
|
||||
@@ -79,9 +80,9 @@ class SplitDateTimeFieldDefaultTime(SplitDateTimeField):
|
||||
)
|
||||
if data_list[1] in self.empty_values:
|
||||
data_list[1] = datetime.time(0, 1)
|
||||
#raise ValidationError(
|
||||
# raise ValidationError(
|
||||
# self.error_messages["invalid_time"], code="invalid_time"
|
||||
#)
|
||||
# )
|
||||
result = datetime.datetime.combine(*data_list)
|
||||
return from_current_timezone(result)
|
||||
return None
|
||||
@@ -98,9 +99,9 @@ class SplitDateTimeFieldDefaultTimeEnd(SplitDateTimeField):
|
||||
)
|
||||
if data_list[1] in self.empty_values:
|
||||
data_list[1] = datetime.time(23, 59)
|
||||
#raise ValidationError(
|
||||
# raise ValidationError(
|
||||
# self.error_messages["invalid_time"], code="invalid_time"
|
||||
#)
|
||||
# )
|
||||
result = datetime.datetime.combine(*data_list)
|
||||
return from_current_timezone(result)
|
||||
return None
|
||||
@@ -111,29 +112,28 @@ class ExamFormMixin:
|
||||
super(ModelForm, self).__init__(*args, **kwargs)
|
||||
|
||||
self.helper = FormHelper()
|
||||
self.helper.form_id = 'id-exam-form'
|
||||
self.helper.form_class = 'exam-form'
|
||||
self.helper.form_method = 'post'
|
||||
self.helper.form_action = 'submit'
|
||||
self.helper.form_id = "id-exam-form"
|
||||
self.helper.form_class = "exam-form"
|
||||
self.helper.form_method = "post"
|
||||
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)
|
||||
# user_user_group_queryset = UserUserGroup.objects.filter(archive=False)
|
||||
#else:
|
||||
# else:
|
||||
# cid_user_group_queryset = CidUserGroup.objects.none()
|
||||
# user_user_group_queryset = UserUserGroup.objects.none()
|
||||
|
||||
#self.fields["cid_user_groups"] = ModelMultipleChoiceField(
|
||||
# self.fields["cid_user_groups"] = ModelMultipleChoiceField(
|
||||
# required=False,
|
||||
# queryset=cid_user_group_queryset,
|
||||
#)
|
||||
#self.fields["user_user_groups"] = ModelMultipleChoiceField(
|
||||
# )
|
||||
# self.fields["user_user_groups"] = ModelMultipleChoiceField(
|
||||
# required=False,
|
||||
# queryset=user_user_group_queryset,
|
||||
#)
|
||||
# )
|
||||
self.fields["start_date"] = SplitDateTimeFieldDefaultTime(
|
||||
widget=SplitDateTimeWidget(
|
||||
date_attrs={"type": "date", "class": "datepicker"},
|
||||
@@ -169,8 +169,8 @@ class ExamFormMixin:
|
||||
"active",
|
||||
"publish_results",
|
||||
"archive",
|
||||
#"cid_user_groups",
|
||||
#"user_user_groups",
|
||||
# "cid_user_groups",
|
||||
# "user_user_groups",
|
||||
# "author",
|
||||
]
|
||||
|
||||
@@ -199,6 +199,7 @@ class ExamAuthorFormMixin(ModelForm):
|
||||
widget=FilteredSelectMultiple(verbose_name="Authors", is_stacked=False),
|
||||
)
|
||||
|
||||
|
||||
class ExamMarkerFormMixin(ModelForm):
|
||||
class Meta:
|
||||
fields = ["markers"]
|
||||
@@ -213,37 +214,41 @@ class ExamMarkerFormMixin(ModelForm):
|
||||
|
||||
|
||||
class ExamGroupsFormMixin(ModelForm):
|
||||
class Meta:
|
||||
#model = ExamCollection
|
||||
fields = ("cid_user_groups", "user_user_groups")
|
||||
class Meta:
|
||||
# model = ExamCollection
|
||||
fields = ("cid_user_groups", "user_user_groups")
|
||||
|
||||
def __init__(self, *args, user, **kwargs) -> None:
|
||||
super(ModelForm, self).__init__(*args, **kwargs)
|
||||
def __init__(self, *args, user, **kwargs) -> None:
|
||||
super(ModelForm, self).__init__(*args, **kwargs)
|
||||
|
||||
# TODO: decide how we should handle managing user groups
|
||||
# i.e who should be able to access a list of all users?
|
||||
# TODO: decide how we should handle managing user groups
|
||||
# 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():
|
||||
cid_user_group_queryset = CidUserGroup.objects.filter(archive=False)
|
||||
user_user_group_queryset = UserUserGroup.objects.filter(archive=False)
|
||||
else:
|
||||
cid_user_group_queryset = CidUserGroup.objects.filter(archive=False, open_access=True)
|
||||
user_user_group_queryset = UserUserGroup.objects.none(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",
|
||||
if user.is_superuser or user.groups.filter(name="cid_user_manager").exists():
|
||||
cid_user_group_queryset = CidUserGroup.objects.filter(archive=False)
|
||||
user_user_group_queryset = UserUserGroup.objects.filter(archive=False)
|
||||
else:
|
||||
cid_user_group_queryset = CidUserGroup.objects.filter(
|
||||
archive=False, open_access=True
|
||||
)
|
||||
self.fields["user_user_groups"] = ModelMultipleChoiceField(
|
||||
required=False,
|
||||
queryset=user_user_group_queryset,
|
||||
label="User Groups",
|
||||
user_user_group_queryset = UserUserGroup.objects.none(
|
||||
archive=False, open_access=True
|
||||
)
|
||||
self.fields["cid_user_groups"].widget.attrs["size"] = 8
|
||||
self.fields["user_user_groups"].widget.attrs["size"] = 8
|
||||
# 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(
|
||||
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):
|
||||
@@ -751,4 +756,3 @@ class ExamCollectionCloneForm(ModelForm):
|
||||
class Meta:
|
||||
model = ExamCollection
|
||||
fields = ("name", "date")
|
||||
|
||||
|
||||
@@ -39,44 +39,67 @@
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<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" %}
|
||||
Add more <a href='{% url "generic:cid_group_exams" group.pk %}'>here</a>.
|
||||
{% else %}
|
||||
Add more <a href='{% url "generic:user_group_exams" group.pk %}'>here</a>.
|
||||
{% 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 %}
|
||||
{% for key, value in exam_map.items %}
|
||||
<h4>{{key}}</h4>
|
||||
<ul>
|
||||
<div>
|
||||
{% for exam in value %}
|
||||
<li>
|
||||
<a href="{{exam.get_absolute_url}}">{{exam}}</a>
|
||||
<div class="d-flex">
|
||||
<a href="{{exam.get_absolute_url}}" class="px-2">{{exam}}</a>
|
||||
{% 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 %}
|
||||
<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 %}
|
||||
<span class="cid-number">
|
||||
|
||||
|
||||
<div class="px-2 ms-auto ">
|
||||
{% if group_type == "cid" %}
|
||||
(Cid candidate count: {{exam.valid_cid_users.count}})
|
||||
{% else %}
|
||||
(User candidate count: {{exam.valid_user_users.count}})
|
||||
{% endif %}
|
||||
</span>
|
||||
</li>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endwith %}
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
<style>
|
||||
td, th { padding-left: 10px }
|
||||
|
||||
.cid-number {float: right}
|
||||
|
||||
.edit-link {
|
||||
font-size: smaller;
|
||||
opacity: 60%;
|
||||
|
||||
@@ -120,6 +120,16 @@ urlpatterns = [
|
||||
views.user_group_view_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(
|
||||
"user/group/<int:pk>/exams",
|
||||
views.UserGroupExamUpdate.as_view(),
|
||||
|
||||
@@ -133,6 +133,17 @@ class CidManagerRequiredMixin(UserPassesTestMixin):
|
||||
# return obj
|
||||
# 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):
|
||||
if score == 49:
|
||||
@@ -2709,6 +2720,30 @@ def cid_group_view_all(request):
|
||||
{"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
|
||||
def user_group_view_detail(request, group_id):
|
||||
|
||||
Reference in New Issue
Block a user