add bulk edit grades
This commit is contained in:
@@ -1,11 +1,16 @@
|
|||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
|
|
||||||
|
|
||||||
def user_is_cid_user_manager(function):
|
def user_is_cid_user_manager(function):
|
||||||
def wrap(request, *args, **kwargs):
|
def wrap(request, *args, **kwargs):
|
||||||
if request.user.groups.filter(name='cid_user_manager').exists() or request.user.is_superuser:
|
if (
|
||||||
|
request.user.groups.filter(name="cid_user_manager").exists()
|
||||||
|
or request.user.is_superuser
|
||||||
|
):
|
||||||
return function(request, *args, **kwargs)
|
return function(request, *args, **kwargs)
|
||||||
else:
|
else:
|
||||||
raise PermissionDenied
|
raise PermissionDenied
|
||||||
|
|
||||||
wrap.__doc__ = function.__doc__
|
wrap.__doc__ = function.__doc__
|
||||||
wrap.__name__ = function.__name__
|
wrap.__name__ = function.__name__
|
||||||
return wrap
|
return wrap
|
||||||
|
|||||||
@@ -7,18 +7,18 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<div id="view-filter-options">
|
<div id="view-filter-options">
|
||||||
<details class="filter">
|
<details class="filter">
|
||||||
<summary>
|
<summary>
|
||||||
<h3>Filter Users </h3>
|
<h3>Filter Users </h3>
|
||||||
</summary>
|
</summary>
|
||||||
<form action="" method="get">
|
<form action="" method="get">
|
||||||
{{ filter.form }}
|
{{ filter.form }}
|
||||||
<input class="btn btn-primary btn-sm mt-1 mb-1" type="submit" />
|
<input class="btn btn-primary btn-sm mt-1 mb-1" type="submit" />
|
||||||
</form>
|
</form>
|
||||||
</details>
|
</details>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div id="table-div">
|
||||||
<span id="manage-span">
|
<span id="manage-span">
|
||||||
{% render_table table %}
|
{% render_table table %}
|
||||||
</span>
|
</span>
|
||||||
@@ -31,7 +31,21 @@
|
|||||||
<summary>
|
<summary>
|
||||||
Bulk edit
|
Bulk edit
|
||||||
</summary>
|
</summary>
|
||||||
<button>Delete supervisors</button>
|
<div id="htmx-info"></div>
|
||||||
|
<div id="htmx-options"></div>
|
||||||
|
<button id="bulk-delete-supervisors-button"
|
||||||
|
title="Deletes the supervisors of all selected users"
|
||||||
|
hx-post="{% url 'generic:users_bulk_delete_supervisors' %}"
|
||||||
|
hx-include="[name='selection']"
|
||||||
|
hx-confirm="This will delete supervisors from all selected users, are you sure you wish to continue?"
|
||||||
|
hx-target="#htmx-info"
|
||||||
|
>Delete supervisors</button>
|
||||||
|
<button id="bulk-edit-grade-button"
|
||||||
|
hx-post="{% url 'generic:users_bulk_edit_grade' %}"
|
||||||
|
hx-include="[name='selection']"
|
||||||
|
hx-target="#htmx-options"
|
||||||
|
|
||||||
|
>Edit grade</button>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
+14
-1
@@ -77,6 +77,8 @@ urlpatterns = [
|
|||||||
path(
|
path(
|
||||||
"cids/group/create", views.CidUserGroupCreate.as_view(), name="cid_group_create"
|
"cids/group/create", views.CidUserGroupCreate.as_view(), name="cid_group_create"
|
||||||
),
|
),
|
||||||
|
path("user/bulk_delete_supervisors/", views.users_bulk_delete_supervisors, name="users_bulk_delete_supervisors"),
|
||||||
|
path("user/bulk_edit_grade/", views.users_bulk_edit_grade, name="users_bulk_edit_grade"),
|
||||||
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("user/group/<int:group_id>/", views.user_group_view_detail, name="user_group_detail"),
|
path("user/group/<int:group_id>/", views.user_group_view_detail, name="user_group_detail"),
|
||||||
@@ -246,16 +248,27 @@ def generic_exam_urls(generic_exam_view: GenericExamViews):
|
|||||||
generic_exam_view.exam_json_cid_unbased,
|
generic_exam_view.exam_json_cid_unbased,
|
||||||
name="exam_json_cid_unbased",
|
name="exam_json_cid_unbased",
|
||||||
),
|
),
|
||||||
|
# These are a little more ambiguous than ideal
|
||||||
path(
|
path(
|
||||||
"exam/json/<int:pk>/<int:sk>",
|
"exam/json/<int:pk>/<int:sk>",
|
||||||
generic_exam_view.exam_question_json,
|
generic_exam_view.exam_question_json,
|
||||||
name="exam_question_json_unbased",
|
name="exam_question_json",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"exam/json/<int:pk>/<int:sk>/unbased",
|
"exam/json/<int:pk>/<int:sk>/unbased",
|
||||||
generic_exam_view.exam_question_json_unbased,
|
generic_exam_view.exam_question_json_unbased,
|
||||||
name="exam_question_json_unbased",
|
name="exam_question_json_unbased",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"exam/json/<int:pk>/<int:cid>/<str:passcode>/<int:sk>",
|
||||||
|
generic_exam_view.exam_question_json_cid,
|
||||||
|
name="exam_question_json_cid",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"exam/json/<int:pk>/<int:cid>/<str:passcode>/<int:sk>/unbased",
|
||||||
|
generic_exam_view.exam_question_json_unbased_cid,
|
||||||
|
name="exam_question_json_unbased_cid",
|
||||||
|
),
|
||||||
path(
|
path(
|
||||||
"exam/json/<int:pk>/recreate",
|
"exam/json/<int:pk>/recreate",
|
||||||
generic_exam_view.exam_json_recreate,
|
generic_exam_view.exam_json_recreate,
|
||||||
|
|||||||
+188
-39
@@ -58,7 +58,19 @@ from .forms import (
|
|||||||
UserUserGroupForm,
|
UserUserGroupForm,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .models import CidUser, CidUserGroup, ExamBase, ExamUserStatus, Examination, QuestionNote, Supervisor, UserGrades, UserProfile, UserUserGroup, get_next_cid
|
from .models import (
|
||||||
|
CidUser,
|
||||||
|
CidUserGroup,
|
||||||
|
ExamBase,
|
||||||
|
ExamUserStatus,
|
||||||
|
Examination,
|
||||||
|
QuestionNote,
|
||||||
|
Supervisor,
|
||||||
|
UserGrades,
|
||||||
|
UserProfile,
|
||||||
|
UserUserGroup,
|
||||||
|
get_next_cid,
|
||||||
|
)
|
||||||
|
|
||||||
from rapids.models import Rapid as RapidQuestion
|
from rapids.models import Rapid as RapidQuestion
|
||||||
from rapids.models import Exam as RapidExam
|
from rapids.models import Exam as RapidExam
|
||||||
@@ -355,6 +367,7 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
|
|
||||||
self.ExamFilter = ExamFilter
|
self.ExamFilter = ExamFilter
|
||||||
|
|
||||||
|
# TODO: these may be better implemented as decorators
|
||||||
def check_user_access(self, user: User, exam_id: int = None):
|
def check_user_access(self, user: User, exam_id: int = None):
|
||||||
"""Check if a user should be able to access a view
|
"""Check if a user should be able to access a view
|
||||||
|
|
||||||
@@ -479,9 +492,7 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
|
|
||||||
exam = get_object_or_404(self.Exam, pk=pk)
|
exam = get_object_or_404(self.Exam, pk=pk)
|
||||||
|
|
||||||
exam.archive = (
|
exam.archive = True if request.POST.get("archive") == "true" else False
|
||||||
True if request.POST.get("archive") == "true" else False
|
|
||||||
)
|
|
||||||
print(f"{exam.archive=}")
|
print(f"{exam.archive=}")
|
||||||
exam.save()
|
exam.save()
|
||||||
data = {
|
data = {
|
||||||
@@ -842,7 +853,7 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
"can_edit": can_edit,
|
"can_edit": can_edit,
|
||||||
"notes": notes,
|
"notes": notes,
|
||||||
"app_name": self.app_name,
|
"app_name": self.app_name,
|
||||||
"candidate_count": (cid_candidates, users_candidates)
|
"candidate_count": (cid_candidates, users_candidates),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -885,7 +896,7 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
_type_: _description_
|
_type_: _description_
|
||||||
"""
|
"""
|
||||||
exam = get_object_or_404(self.Exam, pk=exam_id)
|
exam = get_object_or_404(self.Exam, pk=exam_id)
|
||||||
|
|
||||||
# if not request.user.groups.filter(name="cid_user_manager").exists():
|
# if not request.user.groups.filter(name="cid_user_manager").exists():
|
||||||
@@ -1020,25 +1031,26 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
else:
|
else:
|
||||||
statuses = exam.exam_user_status.all()
|
statuses = exam.exam_user_status.all()
|
||||||
|
|
||||||
return render(request, "exam_user_status.html", {"exam": exam, "statuses": statuses})
|
return render(
|
||||||
|
request, "exam_user_status.html", {"exam": exam, "statuses": statuses}
|
||||||
|
)
|
||||||
|
|
||||||
#if not self.check_user_access(request.user, pk):
|
# if not self.check_user_access(request.user, pk):
|
||||||
# raise PermissionDenied
|
# raise PermissionDenied
|
||||||
|
|
||||||
#q, content_type = get_question_and_content_type(self.question_type)
|
# q, content_type = get_question_and_content_type(self.question_type)
|
||||||
|
|
||||||
## Get active notes for type
|
## Get active notes for type
|
||||||
#statuses = ExamUserStatus.objects.filter(
|
# statuses = ExamUserStatus.objects.filter(
|
||||||
# content_type=content_type, object_id__in=pk
|
# content_type=content_type, object_id__in=pk
|
||||||
#)
|
# )
|
||||||
|
|
||||||
#return statuses
|
# return statuses
|
||||||
|
|
||||||
@method_decorator(login_required)
|
@method_decorator(login_required)
|
||||||
def exam_json_edit(self, request, pk):
|
def exam_json_edit(self, request, pk):
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
|
|
||||||
|
|
||||||
if request.user.groups.filter(name="cid_user_manager").exists():
|
if request.user.groups.filter(name="cid_user_manager").exists():
|
||||||
# This may give more permissions than we actually want
|
# This may give more permissions than we actually want
|
||||||
pass
|
pass
|
||||||
@@ -1306,7 +1318,14 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
def active_exams(self, request: HttpRequest, json: bool=True, based: bool=True, cid: int=None, passcode: str=None):
|
def active_exams(
|
||||||
|
self,
|
||||||
|
request: HttpRequest,
|
||||||
|
json: bool = True,
|
||||||
|
based: bool = True,
|
||||||
|
cid: int = None,
|
||||||
|
passcode: str = None,
|
||||||
|
):
|
||||||
exams = self.Exam.objects.filter(archive=False)
|
exams = self.Exam.objects.filter(archive=False)
|
||||||
|
|
||||||
active_exams = {"exams": []}
|
active_exams = {"exams": []}
|
||||||
@@ -1321,7 +1340,9 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
exam: ExamBase
|
exam: ExamBase
|
||||||
for exam in exams:
|
for exam in exams:
|
||||||
if exam.active or self.check_user_access(request.user, exam.pk):
|
if exam.active or self.check_user_access(request.user, exam.pk):
|
||||||
print(f"{exam.app_name=}, {exam.name=}, {cid=}, {passcode=}, {request.user=}")
|
print(
|
||||||
|
f"{exam.app_name=}, {exam.name=}, {cid=}, {passcode=}, {request.user=}"
|
||||||
|
)
|
||||||
if exam.exam_mode and not exam.check_cid_user(
|
if exam.exam_mode and not exam.check_cid_user(
|
||||||
cid, passcode, request=request, user_id=request.user.pk
|
cid, passcode, request=request, user_id=request.user.pk
|
||||||
):
|
):
|
||||||
@@ -1333,7 +1354,9 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
else:
|
else:
|
||||||
creation_time = "None"
|
creation_time = "None"
|
||||||
|
|
||||||
url = request.build_absolute_uri(exam.get_json_url(cid=cid, passcode=passcode))
|
url = request.build_absolute_uri(
|
||||||
|
exam.get_json_url(cid=cid, passcode=passcode)
|
||||||
|
)
|
||||||
# hacky
|
# hacky
|
||||||
if not based:
|
if not based:
|
||||||
url = url + "/unbased"
|
url = url + "/unbased"
|
||||||
@@ -1544,7 +1567,6 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
return JsonResponse({"success": True, "question_count": n})
|
return JsonResponse({"success": True, "question_count": n})
|
||||||
return JsonResponse({"success": False, "error": "Invalid data"})
|
return JsonResponse({"success": False, "error": "Invalid data"})
|
||||||
|
|
||||||
|
|
||||||
def exam_json_cid(self, request, pk, cid, passcode):
|
def exam_json_cid(self, request, pk, cid, passcode):
|
||||||
return self.exam_json(request, pk, cid, passcode)
|
return self.exam_json(request, pk, cid, passcode)
|
||||||
|
|
||||||
@@ -1578,7 +1600,9 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
cid_user = CidUser.objects.get(cid=cid)
|
cid_user = CidUser.objects.get(cid=cid)
|
||||||
else:
|
else:
|
||||||
cid_user = None
|
cid_user = None
|
||||||
exam.exam_user_status.create(cid_user=cid_user, user_user=user, status="downloaded")
|
exam.exam_user_status.create(
|
||||||
|
cid_user=cid_user, user_user=user, status="downloaded"
|
||||||
|
)
|
||||||
|
|
||||||
path = "{0}{1}/exam/{2}.json".format(settings.MEDIA_ROOT, self.app_name, pk)
|
path = "{0}{1}/exam/{2}.json".format(settings.MEDIA_ROOT, self.app_name, pk)
|
||||||
url = "{0}{1}/exam/{2}.json".format(settings.MEDIA_URL, self.app_name, pk)
|
url = "{0}{1}/exam/{2}.json".format(settings.MEDIA_URL, self.app_name, pk)
|
||||||
@@ -1629,6 +1653,14 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
if not exam.active and not self.check_user_access(request.user, pk):
|
if not exam.active and not self.check_user_access(request.user, pk):
|
||||||
raise Http404("No available exam")
|
raise Http404("No available exam")
|
||||||
|
|
||||||
|
# Check access for users
|
||||||
|
if request.user.is_anonymous:
|
||||||
|
user_id = None
|
||||||
|
else:
|
||||||
|
user_id = request.user.pk
|
||||||
|
if not exam.check_cid_user(cid, passcode, request, user_id):
|
||||||
|
raise Http404("No available exam")
|
||||||
|
|
||||||
time = datetime.now()
|
time = datetime.now()
|
||||||
|
|
||||||
exam_json = exam.get_exam_json(based=False)
|
exam_json = exam.get_exam_json(based=False)
|
||||||
@@ -1642,17 +1674,30 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
cid_user = CidUser.objects.get(cid=cid)
|
cid_user = CidUser.objects.get(cid=cid)
|
||||||
else:
|
else:
|
||||||
cid_user = None
|
cid_user = None
|
||||||
exam.exam_user_status.create(cid_user=cid, user_user=user, status="downloaded", extra="unbased")
|
exam.exam_user_status.create(
|
||||||
|
cid_user=cid, user_user=user, status="downloaded", extra="unbased"
|
||||||
|
)
|
||||||
|
|
||||||
return JsonResponse(exam_json)
|
return JsonResponse(exam_json)
|
||||||
|
|
||||||
def exam_question_json(self, request, pk, sk):
|
def exam_question_json_cid(self, request, pk, sk, cid, passcode):
|
||||||
|
return self.exam_question_json(request, pk, sk, cid, passcode)
|
||||||
|
|
||||||
|
def exam_question_json(self, request, pk, sk, cid=None, passcode=None):
|
||||||
question = get_object_or_404(self.Question, pk=sk)
|
question = get_object_or_404(self.Question, pk=sk)
|
||||||
exam = get_object_or_404(self.Exam, pk=pk)
|
exam = get_object_or_404(self.Exam, pk=pk)
|
||||||
|
|
||||||
if not exam.active and not self.check_user_access(request.user, pk):
|
if not exam.active and not self.check_user_access(request.user, pk):
|
||||||
raise Http404("No available exam")
|
raise Http404("No available exam")
|
||||||
|
|
||||||
|
# Check access for users
|
||||||
|
if request.user.is_anonymous:
|
||||||
|
user_id = None
|
||||||
|
else:
|
||||||
|
user_id = request.user.pk
|
||||||
|
if not exam.check_cid_user(cid, passcode, request, user_id):
|
||||||
|
raise Http404("No available exam")
|
||||||
|
|
||||||
if request.GET.get("_"):
|
if request.GET.get("_"):
|
||||||
base_url = redirect("{}:question_json".format(self.app_name), pk=sk)
|
base_url = redirect("{}:question_json".format(self.app_name), pk=sk)
|
||||||
query_string = urllib.parse.urlencode({"_": request.GET.get("_")})
|
query_string = urllib.parse.urlencode({"_": request.GET.get("_")})
|
||||||
@@ -1661,6 +1706,9 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
|
|
||||||
return redirect("{}:question_json".format(self.app_name), pk=sk)
|
return redirect("{}:question_json".format(self.app_name), pk=sk)
|
||||||
|
|
||||||
|
def exam_question_json_unbased_cid(self, request, pk, sk, cid, passcode):
|
||||||
|
return self.exam_question_json_unbased(request, pk, sk, cid, passcode)
|
||||||
|
|
||||||
def exam_question_json_unbased(self, request, pk, sk):
|
def exam_question_json_unbased(self, request, pk, sk):
|
||||||
question = get_object_or_404(self.Question, pk=sk)
|
question = get_object_or_404(self.Question, pk=sk)
|
||||||
exam = get_object_or_404(self.Exam, pk=pk)
|
exam = get_object_or_404(self.Exam, pk=pk)
|
||||||
@@ -1815,7 +1863,6 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
|
|
||||||
cached_scores = True
|
cached_scores = True
|
||||||
|
|
||||||
|
|
||||||
valid_cid_users = set(exam.valid_cid_users.all().values_list("cid", flat=True))
|
valid_cid_users = set(exam.valid_cid_users.all().values_list("cid", flat=True))
|
||||||
valid_user_users = set(exam.valid_user_users.all().values_list("pk", flat=True))
|
valid_user_users = set(exam.valid_user_users.all().values_list("pk", flat=True))
|
||||||
|
|
||||||
@@ -2242,7 +2289,6 @@ class CidUserView(CidManagerRequiredMixin, SingleTableMixin, FilterView):
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@user_is_cid_user_manager
|
@user_is_cid_user_manager
|
||||||
def cid_group_view(request):
|
def cid_group_view(request):
|
||||||
groups = CidUserGroup.objects.filter(archive=False)
|
groups = CidUserGroup.objects.filter(archive=False)
|
||||||
@@ -2253,6 +2299,7 @@ def cid_group_view(request):
|
|||||||
{"groups": groups},
|
{"groups": groups},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@user_is_cid_user_manager
|
@user_is_cid_user_manager
|
||||||
def cid_group_view_detail(request, group_id):
|
def cid_group_view_detail(request, group_id):
|
||||||
group = get_object_or_404(CidUserGroup, pk=group_id)
|
group = get_object_or_404(CidUserGroup, pk=group_id)
|
||||||
@@ -2265,6 +2312,7 @@ def cid_group_view_detail(request, group_id):
|
|||||||
{"group": group, "users": users, "group_type": "cid"},
|
{"group": group, "users": users, "group_type": "cid"},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@user_is_cid_user_manager
|
@user_is_cid_user_manager
|
||||||
def cid_group_view_all(request):
|
def cid_group_view_all(request):
|
||||||
groups = CidUserGroup.objects.filter()
|
groups = CidUserGroup.objects.filter()
|
||||||
@@ -2275,11 +2323,12 @@ def cid_group_view_all(request):
|
|||||||
{"groups": groups, "view_all": True},
|
{"groups": groups, "view_all": True},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@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):
|
||||||
group = get_object_or_404(UserUserGroup, pk=group_id)
|
group = get_object_or_404(UserUserGroup, pk=group_id)
|
||||||
|
|
||||||
users = group.users.filter() # Filter inactive users?
|
users = group.users.filter() # Filter inactive users?
|
||||||
|
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
@@ -2287,6 +2336,89 @@ def user_group_view_detail(request, group_id):
|
|||||||
{"group": group, "users": users, "group_type": "user"},
|
{"group": group, "users": users, "group_type": "user"},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@user_is_cid_user_manager
|
||||||
|
def users_bulk_delete_supervisors(request):
|
||||||
|
if "selection" in request.POST:
|
||||||
|
selected_users = request.POST.getlist("selection")
|
||||||
|
user_models = User.objects.filter(id__in=selected_users)
|
||||||
|
|
||||||
|
for u in user_models:
|
||||||
|
u.userprofile.supervisor = None
|
||||||
|
u.save()
|
||||||
|
|
||||||
|
modified_users = ",".join([user.username for user in user_models])
|
||||||
|
|
||||||
|
return HttpResponse(
|
||||||
|
f"Supervisors removed from {modified_users}. Refresh page to see update",
|
||||||
|
content_type="text/plain",
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
return HttpResponse("No users selected", content_type="text/plain")
|
||||||
|
|
||||||
|
|
||||||
|
@user_is_cid_user_manager
|
||||||
|
def users_bulk_edit_grade(request):
|
||||||
|
|
||||||
|
if request.htmx.trigger == "bulk-edit-grade-button":
|
||||||
|
user_grades = UserGrades.objects.all()
|
||||||
|
|
||||||
|
html = "".join(
|
||||||
|
[
|
||||||
|
f"""<button class='change-grade-button' name='add-grade--{grade.id}'
|
||||||
|
hx-post="{reverse('generic:users_bulk_edit_grade')}"
|
||||||
|
hx-include="[name='selection']"
|
||||||
|
hx-confirm="This will modify grades of all selected users, are you sure you wish to continue?"
|
||||||
|
hx-target="#htmx-info"
|
||||||
|
>{grade.name}</button>"""
|
||||||
|
for grade in user_grades
|
||||||
|
]
|
||||||
|
)
|
||||||
|
# TODO: work out how to actually use hyperscript
|
||||||
|
html = (
|
||||||
|
html
|
||||||
|
+ "<button _='on click for el in .change-grade-button remove el end then remove me'>Cancel</button>"
|
||||||
|
)
|
||||||
|
|
||||||
|
return HttpResponse(html, content_type="text/html")
|
||||||
|
elif request.htmx.trigger_name.startswith("add-grade"):
|
||||||
|
selected_users = request.POST.getlist("selection")
|
||||||
|
|
||||||
|
if not selected_users:
|
||||||
|
return HttpResponse("No users selected", content_type="text/html")
|
||||||
|
user_models = User.objects.filter(id__in=selected_users)
|
||||||
|
|
||||||
|
# u: User
|
||||||
|
for u in user_models:
|
||||||
|
u.userprofile.grade_id = request.htmx.trigger_name.split("--")[-1]
|
||||||
|
u.save()
|
||||||
|
return HttpResponse("Grades update", content_type="text/html")
|
||||||
|
else:
|
||||||
|
return HttpResponse("Error", content_type="text/html")
|
||||||
|
|
||||||
|
|
||||||
|
@user_is_cid_user_manager
|
||||||
|
def users_bulk_delete_supervisors(request):
|
||||||
|
if "selection" in request.POST:
|
||||||
|
selected_users = request.POST.getlist("selection")
|
||||||
|
user_models = User.objects.filter(id__in=selected_users)
|
||||||
|
|
||||||
|
for u in user_models:
|
||||||
|
u.userprofile.supervisor = None
|
||||||
|
u.save()
|
||||||
|
|
||||||
|
modified_users = ",".join([user.username for user in user_models])
|
||||||
|
|
||||||
|
return HttpResponse(
|
||||||
|
f"Supervisors removed from {modified_users}. Refresh page to see update",
|
||||||
|
content_type="text/plain",
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
return HttpResponse("No users selected", content_type="text/plain")
|
||||||
|
|
||||||
|
|
||||||
@user_is_cid_user_manager
|
@user_is_cid_user_manager
|
||||||
def user_group_view(request):
|
def user_group_view(request):
|
||||||
groups = UserUserGroup.objects.filter(archive=False)
|
groups = UserUserGroup.objects.filter(archive=False)
|
||||||
@@ -2391,10 +2523,12 @@ def candidate_email_results(request, cid, resend=False):
|
|||||||
def candidate_email_results_resend(request, cid, resend=True):
|
def candidate_email_results_resend(request, cid, resend=True):
|
||||||
return candidate_email_results(request, cid, resend=True)
|
return candidate_email_results(request, cid, resend=True)
|
||||||
|
|
||||||
|
|
||||||
@user_is_cid_user_manager
|
@user_is_cid_user_manager
|
||||||
def create_cid_email(request):
|
def create_cid_email(request):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@user_is_cid_user_manager
|
@user_is_cid_user_manager
|
||||||
def manage_cid_users(request):
|
def manage_cid_users(request):
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
@@ -2410,7 +2544,6 @@ def manage_cid_users(request):
|
|||||||
|
|
||||||
emails = json.loads(request.POST.get("emails"))
|
emails = json.loads(request.POST.get("emails"))
|
||||||
|
|
||||||
|
|
||||||
number_to_create = int(request.POST.get("number_to_create"))
|
number_to_create = int(request.POST.get("number_to_create"))
|
||||||
|
|
||||||
add_group = request.POST.get("add_group")
|
add_group = request.POST.get("add_group")
|
||||||
@@ -2513,13 +2646,15 @@ def manage_cid_users(request):
|
|||||||
validate_email(email)
|
validate_email(email)
|
||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
invalid_emails.append(f"Invalid email: {email}")
|
invalid_emails.append(f"Invalid email: {email}")
|
||||||
|
|
||||||
print(f"Email list {email_list}")
|
print(f"Email list {email_list}")
|
||||||
if not email_list:
|
if not email_list:
|
||||||
return JsonResponse({"status": "fail", "details": "No valid emails"})
|
return JsonResponse({"status": "fail", "details": "No valid emails"})
|
||||||
if invalid_emails or not email_list:
|
if invalid_emails or not email_list:
|
||||||
e = "<br/>".join(invalid_emails)
|
e = "<br/>".join(invalid_emails)
|
||||||
return JsonResponse({"status": "fail", "details": f"Unable to create users<hr>{e}"})
|
return JsonResponse(
|
||||||
|
{"status": "fail", "details": f"Unable to create users<hr>{e}"}
|
||||||
|
)
|
||||||
|
|
||||||
for e in email_list:
|
for e in email_list:
|
||||||
passcode = "".join(random.choices(string.ascii_uppercase, k=4))
|
passcode = "".join(random.choices(string.ascii_uppercase, k=4))
|
||||||
@@ -2578,16 +2713,18 @@ class CidUserUpdate(CidManagerRequiredMixin, UpdateView):
|
|||||||
model = CidUser
|
model = CidUser
|
||||||
form_class = CidUserForm
|
form_class = CidUserForm
|
||||||
|
|
||||||
|
|
||||||
class CidUserExamUpdate(CidManagerRequiredMixin, UpdateView):
|
class CidUserExamUpdate(CidManagerRequiredMixin, UpdateView):
|
||||||
model = CidUser
|
model = CidUser
|
||||||
form_class = CidUserExamForm
|
form_class = CidUserExamForm
|
||||||
template_name = "generic/ciduserexam_form.html"
|
template_name = "generic/ciduserexam_form.html"
|
||||||
|
|
||||||
#def get_context_data(self, *args, **kwargs):
|
# def get_context_data(self, *args, **kwargs):
|
||||||
# context = super().get_context_data(**kwargs)
|
# context = super().get_context_data(**kwargs)
|
||||||
# context["test"] = "HELLO"
|
# context["test"] = "HELLO"
|
||||||
# return context
|
# return context
|
||||||
|
|
||||||
|
|
||||||
class CidUserGroupDelete(RevisionMixin, CidManagerRequiredMixin, DeleteView):
|
class CidUserGroupDelete(RevisionMixin, CidManagerRequiredMixin, DeleteView):
|
||||||
model = CidUserGroup
|
model = CidUserGroup
|
||||||
template_name = "confirm_delete.html"
|
template_name = "confirm_delete.html"
|
||||||
@@ -2672,16 +2809,18 @@ class ExamDeleteBase(RevisionMixin, DeleteView):
|
|||||||
def exam_inactive(request, context):
|
def exam_inactive(request, context):
|
||||||
return render(request, "exam_inactive.html", context)
|
return render(request, "exam_inactive.html", context)
|
||||||
|
|
||||||
#class UserUserCreate(CidManagerRequiredMixin, CreateView):
|
|
||||||
|
# class UserUserCreate(CidManagerRequiredMixin, CreateView):
|
||||||
# model = User
|
# model = User
|
||||||
# form_class = UserUserForm
|
# form_class = UserUserForm
|
||||||
# template_name: str = "generic/user_creation_form.html"
|
# template_name: str = "generic/user_creation_form.html"
|
||||||
#
|
#
|
||||||
# success_url = reverse_lazy("accounts_list")
|
# success_url = reverse_lazy("accounts_list")
|
||||||
|
|
||||||
|
|
||||||
def create_user(request, context=None):
|
def create_user(request, context=None):
|
||||||
# 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)
|
form = UserUserForm(request.POST)
|
||||||
# check whether it's valid:
|
# check whether it's valid:
|
||||||
@@ -2692,20 +2831,22 @@ def create_user(request, context=None):
|
|||||||
"first_name": request.POST["first_name"],
|
"first_name": request.POST["first_name"],
|
||||||
"last_name": request.POST["last_name"],
|
"last_name": request.POST["last_name"],
|
||||||
"email": request.POST["username"],
|
"email": request.POST["username"],
|
||||||
"password": secrets.token_hex(nbytes=16)
|
"password": secrets.token_hex(nbytes=16),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
new_user = User.objects.create_user(**user_dict)
|
new_user = User.objects.create_user(**user_dict)
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
errors = f"Unable to create account: {error}"
|
errors = f"Unable to create account: {error}"
|
||||||
return render(request, 'generic/user_creation_form.html', {'form': form, "errors": errors})
|
return render(
|
||||||
|
request,
|
||||||
|
"generic/user_creation_form.html",
|
||||||
|
{"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 = True
|
||||||
|
|
||||||
|
|
||||||
if request.POST["grade"]:
|
if request.POST["grade"]:
|
||||||
grade = UserGrades.objects.get(pk=request.POST["grade"])
|
grade = UserGrades.objects.get(pk=request.POST["grade"])
|
||||||
@@ -2714,12 +2855,16 @@ def create_user(request, context=None):
|
|||||||
if request.POST["supervisor"]:
|
if 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
|
||||||
|
|
||||||
user_profile.save()
|
user_profile.save()
|
||||||
|
|
||||||
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(request, 'generic/user_creation_form.html', {'form': form, "errors": errors})
|
return render(
|
||||||
|
request,
|
||||||
|
"generic/user_creation_form.html",
|
||||||
|
{"form": form, "errors": errors},
|
||||||
|
)
|
||||||
|
|
||||||
return HttpResponseRedirect(reverse_lazy("accounts_list"))
|
return HttpResponseRedirect(reverse_lazy("accounts_list"))
|
||||||
return reverse_lazy("accounts_list")
|
return reverse_lazy("accounts_list")
|
||||||
@@ -2728,7 +2873,8 @@ def create_user(request, context=None):
|
|||||||
else:
|
else:
|
||||||
form = UserUserForm()
|
form = UserUserForm()
|
||||||
|
|
||||||
return render(request, 'generic/user_creation_form.html', {'form': form})
|
return render(request, "generic/user_creation_form.html", {"form": form})
|
||||||
|
|
||||||
|
|
||||||
class SupervisorDetail(CidManagerRequiredMixin, DetailView):
|
class SupervisorDetail(CidManagerRequiredMixin, DetailView):
|
||||||
model = Supervisor
|
model = Supervisor
|
||||||
@@ -2739,15 +2885,18 @@ class SupervisorDelete(CidManagerRequiredMixin, DeleteView):
|
|||||||
template_name: str = "confirm_delete.html"
|
template_name: str = "confirm_delete.html"
|
||||||
success_url = reverse_lazy("generic:supervisor")
|
success_url = reverse_lazy("generic:supervisor")
|
||||||
|
|
||||||
|
|
||||||
class SupervisorUpdate(CidManagerRequiredMixin, UpdateView):
|
class SupervisorUpdate(CidManagerRequiredMixin, UpdateView):
|
||||||
model = Supervisor
|
model = Supervisor
|
||||||
form_class = SupervisorForm
|
form_class = SupervisorForm
|
||||||
#success_url = reverse_lazy("generic:supervisor_detail", kwargs={'pk': self.pk})
|
# success_url = reverse_lazy("generic:supervisor_detail", kwargs={'pk': self.pk})
|
||||||
|
|
||||||
|
|
||||||
class SupervisorCreate(CidManagerRequiredMixin, CreateView):
|
class SupervisorCreate(CidManagerRequiredMixin, CreateView):
|
||||||
model = Supervisor
|
model = Supervisor
|
||||||
form_class = SupervisorForm
|
form_class = SupervisorForm
|
||||||
#success_url = reverse_lazy("generic:supervisor_detail")
|
# success_url = reverse_lazy("generic:supervisor_detail")
|
||||||
|
|
||||||
|
|
||||||
class SupervisorList(CidManagerRequiredMixin, ListView):
|
class SupervisorList(CidManagerRequiredMixin, ListView):
|
||||||
model = Supervisor
|
model = Supervisor
|
||||||
|
|||||||
Reference in New Issue
Block a user