From 297f8619b4b009bf074d94f3cdd27ec3fb1d8e31 Mon Sep 17 00:00:00 2001 From: Ross Date: Fri, 8 Sep 2023 18:10:42 +0100 Subject: [PATCH] a number of updates --- generic/filters.py | 10 +- generic/templates/generic/user_view.html | 39 +++-- generic/views.py | 179 ++++++++++++++--------- rad/static/css/anatomy.css | 73 +++++---- rad/views.py | 2 +- 5 files changed, 198 insertions(+), 105 deletions(-) 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"