diff --git a/generic/filters.py b/generic/filters.py
index fd31154b..38999cf9 100644
--- a/generic/filters.py
+++ b/generic/filters.py
@@ -50,6 +50,7 @@ class UserUserFilter(django_filters.FilterSet):
#"userprofile__grade": ["exact"],
#"userprofile__supervisor": ["exact"],
"email": ["contains"],
+ "is_active": ["exact"]
}
@property
@@ -57,11 +58,16 @@ class UserUserFilter(django_filters.FilterSet):
parent = super().qs
## filter_active = getattr(self.request, 'active', True)
- #if "active" not in self.request.GET:
- # return parent.filter(active=True)
+ if "is_active" not in self.request.GET:
+ return parent.filter(is_active=True)
return parent
+ def __init__(self, *args, **kwargs):
+ super(UserUserFilter, self).__init__(*args, **kwargs)
+ self.form.initial['is_active'] = True
+
+
class ExaminationFilter(django_filters.FilterSet):
class Meta:
model = Examination
diff --git a/generic/templates/generic/user_view.html b/generic/templates/generic/user_view.html
index fe985ea5..08d2b99c 100644
--- a/generic/templates/generic/user_view.html
+++ b/generic/templates/generic/user_view.html
@@ -18,16 +18,7 @@
-
-
- {% render_table table %}
-
-
-
- Bulk create users
- Create single user
-
-
+
Bulk edit
@@ -39,19 +30,47 @@
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"
+ _='on click put "" into #htmx-options'
>Delete supervisors
+
+
+
+
+ {% render_table table %}
+
+
+
+ Bulk create users
+ Create single user
+
+
{% endblock %}
diff --git a/generic/views.py b/generic/views.py
index 5d9974dd..31108a84 100644
--- a/generic/views.py
+++ b/generic/views.py
@@ -208,7 +208,6 @@ def examination_merge(request, pk):
if examination.merge_into(examination_to_merge_into):
success = True
-
else:
examination_to_merge_into = False
return render(
@@ -218,7 +217,7 @@ def examination_merge(request, pk):
"form": form,
"examination": examination,
"examination_to_merge_into": examination_to_merge_into,
- "success": success
+ "success": success,
},
)
@@ -2474,83 +2473,131 @@ def users_bulk_delete_supervisors(request):
return HttpResponse("No users selected", content_type="text/plain")
+class NoUsersSelected(Exception):
+ pass
+
+
+def get_user_selection_from_request(request):
+ selected_users = request.POST.getlist("selection")
+
+ if not selected_users:
+ raise NoUsersSelected
+ user_models = User.objects.filter(id__in=selected_users)
+ return user_models
+
+
@user_is_cid_user_manager
def users_bulk_edit(request):
print(request.htmx.trigger)
print(request.htmx.trigger_name)
- match request.htmx.trigger:
- case r if r is None:
- return HttpResponse("None", content_type="text/html")
- case "bulk-edit-grade-button":
- user_grades = UserGrades.objects.all()
+ try:
+ match request.htmx.trigger:
+ case r if r is None:
+ html = "None"
+ case "bulk-deactivate-user-button":
+ user_models = get_user_selection_from_request(request)
- html = "".join(
- [
- f""""""
- for grade in user_grades
- ]
- )
- # TODO: work out how to actually use hyperscript
- html = (
- html
- + ""
- )
+ # u: User
+ for u in user_models:
+ u.is_active = False
+ u.save()
- return HttpResponse(html, content_type="text/html")
- case r if r.startswith("add-grade"):
- selected_users = request.POST.getlist("selection")
+ html = "Users deactivated"
- if not selected_users:
- return HttpResponse("No users selected", content_type="text/html")
- user_models = User.objects.filter(id__in=selected_users)
+ case "bulk-edit-grade-button":
+ user_grades = UserGrades.objects.all()
- # u: User
- for u in user_models:
- u.userprofile.grade_id = r.split("--")[-1]
- u.save()
- return HttpResponse("Grades update", content_type="text/html")
- case "bulk-add-group-button":
- user_groups = UserUserGroup.objects.all()
+ html = "".join(
+ [
+ f""""""
+ for grade in user_grades
+ ]
+ )
+ # TODO: work out how to actually use hyperscript
+ html = (
+ html
+ + ""
+ )
- html = "".join(
- [
- f""""""
- for group in user_groups
- ]
- )
- # TODO: work out how to actually use hyperscript
- html = (
- html
- + ""
- )
+ case r if r.startswith("add-grade"):
+ user_models = get_user_selection_from_request(request)
- return HttpResponse(html, content_type="text/html")
- case r if r.startswith("add-group"):
- selected_users = request.POST.getlist("selection")
+ # u: User
+ for u in user_models:
+ u.userprofile.grade_id = r.split("--")[-1]
+ u.save()
+ html = "Grades updated"
+ case "bulk-add-group-button":
+ user_groups = UserUserGroup.objects.all()
- if not selected_users:
- return HttpResponse("No users selected", content_type="text/html")
- user_models = User.objects.filter(id__in=selected_users)
+ html = "".join(
+ [
+ f""""""
+ for group in user_groups
+ ]
+ )
+ # TODO: work out how to actually use hyperscript
+ html = (
+ html
+ + ""
+ )
- # u: User
- for u in user_models:
- pass
- u.user_groups.add(r.split("--")[-1])
- u.save()
- return HttpResponse("Group added", content_type="text/html")
- case _:
- return HttpResponse("Error", content_type="text/html")
+ case "bulk-remove-group-button":
+ user_groups = UserUserGroup.objects.all()
+
+ html = "".join(
+ [
+ f""""""
+ for group in user_groups
+ ]
+ )
+ # TODO: work out how to actually use hyperscript
+ html = (
+ html
+ + ""
+ )
+
+ case r if r.startswith("add-group"):
+ user_models = get_user_selection_from_request(request)
+
+ # u: User
+ for u in user_models:
+ pass
+ u.user_groups.add(r.split("--")[-1])
+ u.save()
+ html = "Group added"
+ case r if r.startswith("remove-group"):
+ user_models = get_user_selection_from_request(request)
+
+ # u: User
+ for u in user_models:
+ group_id = r.split("--")[-1]
+ u.user_groups.remove(group_id)
+ html = "Group removed"
+ case _:
+ html = "None"
+ return HttpResponse(html, content_type="text/html")
+ except NoUsersSelected:
+ return HttpResponse("No users selected", content_type="text/html")
@user_is_cid_user_manager
diff --git a/rad/static/css/anatomy.css b/rad/static/css/anatomy.css
index 87ab5e6b..de7e4f96 100644
--- a/rad/static/css/anatomy.css
+++ b/rad/static/css/anatomy.css
@@ -588,6 +588,7 @@ td.user-answer-score-2.rapid-ans::after {
.series-block-popup-link {
font-size: smaller;
}
+
.series-block-popup-link a {
color: rgb(0, 153, 255);
opacity: 50%;
@@ -758,7 +759,7 @@ input {
.sba-finish-list a {
text-decoration: none;
color: unset;
-
+
}
.sba-finish-list span {
@@ -915,7 +916,10 @@ summary h5 {
padding: 0px;
}
-button a, button a:link, button a:visited, button a:hover {
+button a,
+button a:link,
+button a:visited,
+button a:hover {
text-decoration: none;
color: inherit;
}
@@ -929,17 +933,17 @@ details.filter {
/* Generic default styling for forms */
/* Highlight form errors */
-tr:has(.errorlist){
- border: 1px dashed red;
+tr:has(.errorlist) {
+ border: 1px dashed red;
}
-.errorlist + input {
- background-color: red;
- color: darkblue;
+.errorlist+input {
+ background-color: red;
+ color: darkblue;
}
.alert-error {
- border: 1px dashed red;
+ border: 1px dashed red;
}
@@ -954,7 +958,7 @@ tr:has(.errorlist){
opacity: 25%;
}
-.control-group:hover .help-block{
+.control-group:hover .help-block {
opacity: 100%;
}
@@ -971,7 +975,12 @@ form .submit-button {
margin-bottom: 10px;
}
-h1, h2, h3, h4, h5, h6 {
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
margin-top: 10px;
}
@@ -986,24 +995,23 @@ h1, h2, h3, h4, h5, h6 {
}
-.cid-candidate-list div {
-}
+.cid-candidate-list div {}
.stamp-white {
- display: inline-block;
- z-index:1;
- font-family:Arial,sans-serif;
- transform: rotate(-15deg);
- font-size:20px;
- color:white;
- border:solid 2px white;
- padding:5px;
- border-radius:5px;
- zoom:1;
- filter:alpha(opacity=20);
- opacity:0.9;
- text-shadow: 0 0 2px white;
- box-shadow: 0 0 2px white;
+ display: inline-block;
+ z-index: 1;
+ font-family: Arial, sans-serif;
+ transform: rotate(-15deg);
+ font-size: 20px;
+ color: white;
+ border: solid 2px white;
+ padding: 5px;
+ border-radius: 5px;
+ zoom: 1;
+ filter: alpha(opacity=20);
+ opacity: 0.9;
+ text-shadow: 0 0 2px white;
+ box-shadow: 0 0 2px white;
}
.self-feedback-block {
@@ -1018,4 +1026,17 @@ h1, h2, h3, h4, h5, h6 {
.collection-name-blend {
opacity: 70%;
font-size: small;
+}
+
+.cancel-button {
+ color: darkblue;
+ border-color: darkblue;
+}
+
+.bulk-edit {
+ position: sticky;
+ top: 0px;
+ background-color: black;
+ width: 100%;
+
}
\ No newline at end of file
diff --git a/rad/views.py b/rad/views.py
index 86713d1e..a207b188 100644
--- a/rad/views.py
+++ b/rad/views.py
@@ -588,7 +588,7 @@ class DeleteUserView(CidManagerRequiredMixin, DeleteView):
class UpdateUserView(CidManagerRequiredMixin, UpdateView):
model = User
- fields = ["first_name", "last_name", "email"] # Keep listing whatever fields
+ fields = ["first_name", "last_name", "email", "is_active"] # Keep listing whatever fields
# the combined UserProfile and User exposes.
template_name = "user_update.html"
slug_field = "username"