1+ import json
2+ from collections .abc import Mapping
3+ from typing import Sequence
4+
15from django .contrib import admin , messages
26from django .db .models import Max
37from django .urls import reverse
48from django .utils .safestring import mark_safe
59
610from NEMO .mixins import BillableItemMixin
7- from NEMO .models import Area , Configuration , Interlock , InterlockCard , Tool , User
11+ from NEMO .models import Area , Configuration , Interlock , InterlockCard , Tool , ToolUsageQuestions , User
812from NEMO .typing import QuerySetType
9- from NEMO .utilities import export_format_datetime , new_model_copy
13+ from NEMO .utilities import export_format_datetime , get_django_default_perm , new_model_copy
1014from NEMO .views .access_requests import access_csv_export
1115from NEMO .views .adjustment_requests import adjustments_csv_export
1216
1317
14- @admin .action (description = "Disable selected cards" )
18+ @admin .action (description = "Disable selected cards" , permissions = [ "change" ] )
1519def disable_selected_cards (model_admin , request , queryset : QuerySetType [InterlockCard ]):
1620 for interlock_card in queryset :
1721 interlock_card .enabled = False
1822 interlock_card .save (update_fields = ["enabled" ])
1923
2024
21- @admin .action (description = "Enable selected cards" )
25+ @admin .action (description = "Enable selected cards" , permissions = [ "change" ] )
2226def enable_selected_cards (model_admin , request , queryset : QuerySetType [InterlockCard ]):
2327 for interlock_card in queryset :
2428 interlock_card .enabled = True
2529 interlock_card .save (update_fields = ["enabled" ])
2630
2731
28- @admin .action (description = "Lock selected interlocks" )
32+ @admin .action (description = "Lock selected interlocks" , permissions = [ "change" ] )
2933def lock_selected_interlocks (model_admin , request , queryset ):
3034 for interlock in queryset :
3135 try :
@@ -38,7 +42,7 @@ def lock_selected_interlocks(model_admin, request, queryset):
3842 messages .error (request , f"{ interlock } could not be locked due to the following error: { str (error )} " )
3943
4044
41- @admin .action (description = "Unlock selected interlocks" )
45+ @admin .action (description = "Unlock selected interlocks" , permissions = [ "change" ] )
4246def unlock_selected_interlocks (model_admin , request , queryset ):
4347 for interlock in queryset :
4448 try :
@@ -51,7 +55,7 @@ def unlock_selected_interlocks(model_admin, request, queryset):
5155 messages .error (request , f"{ interlock } could not be unlocked due to the following error: { str (error )} " )
5256
5357
54- @admin .action (description = "Synchronize selected interlocks with tool usage" )
58+ @admin .action (description = "Synchronize selected interlocks with tool usage" , permissions = [ "change" ] )
5559def synchronize_with_tool_usage (model_admin , request , queryset ):
5660 for interlock in queryset :
5761 # Ignore interlocks with no tool assigned, and ignore interlocks connected to doors
@@ -65,6 +69,8 @@ def synchronize_with_tool_usage(model_admin, request, queryset):
6569
6670@admin .action (description = "Create next interlock" )
6771def create_next_interlock (model_admin , request , queryset ):
72+ if not has_perm (request , queryset , "add" ) or not has_perm (request , queryset , "change" ):
73+ model_admin .message_user (request , "You do not have permission to run this action." , level = messages .ERROR )
6874 for interlock in queryset :
6975 new_interlock = Interlock ()
7076 new_interlock .card = interlock .card
@@ -74,7 +80,7 @@ def create_next_interlock(model_admin, request, queryset):
7480 new_interlock .save ()
7581
7682
77- @admin .action (description = "Generate CSV status report for selected interlocks" )
83+ @admin .action (description = "Generate CSV status report for selected interlocks" , permissions = [ "view" ] )
7884def csv_interlock_status_report (model_admin , request , queryset : QuerySetType [Interlock ]):
7985 from NEMO .interlocks import get_interlock_report
8086
@@ -85,6 +91,8 @@ def csv_interlock_status_report(model_admin, request, queryset: QuerySetType[Int
8591
8692@admin .action (description = "Duplicate selected tool configuration" )
8793def duplicate_tool_configuration (model_admin , request , queryset ):
94+ if not has_perm (request , queryset , "add" ) or not has_perm (request , queryset , "change" ):
95+ model_admin .message_user (request , "You do not have permission to run this action." , level = messages .ERROR )
8896 for tool in queryset :
8997 original_name = tool .name
9098 new_name = "Copy of " + tool .name
@@ -135,28 +143,30 @@ def duplicate_tool_configuration(model_admin, request, queryset):
135143 )
136144
137145
138- @admin .action (description = "Rebuild area tree" )
146+ @admin .action (description = "Rebuild area tree" , permissions = [ "change" ] )
139147def rebuild_area_tree (model_admin , request , queryset ):
140148 Area .objects .rebuild ()
141149
142150
143- @admin .action (description = "Export selected adjustment requests in CSV" )
151+ @admin .action (description = "Export selected adjustment requests in CSV" , permissions = [ "view" ] )
144152def adjustment_requests_export_csv (modeladmin , request , queryset ):
145153 return adjustments_csv_export (queryset .all ())
146154
147155
148- @admin .action (description = "Mark selected adjustment requests as applied" )
156+ @admin .action (description = "Mark selected adjustment requests as applied" , permissions = [ "change" ] )
149157def adjustment_requests_mark_as_applied (modeladmin , request , queryset ):
150158 return queryset .update (applied = True , applied_by = request .user )
151159
152160
153- @admin .action (description = "Export selected access requests in CSV" )
161+ @admin .action (description = "Export selected access requests in CSV" , permissions = [ "view" ] )
154162def access_requests_export_csv (modeladmin , request , queryset ):
155163 return access_csv_export (queryset .all ())
156164
157165
158166@admin .action (description = "Duplicate selected configuration" )
159167def duplicate_configuration (model_admin , request , queryset : QuerySetType [Configuration ]):
168+ if not has_perm (request , queryset , "add" ) or not has_perm (request , queryset , "change" ):
169+ model_admin .message_user (request , "You do not have permission to run this action." , level = messages .ERROR )
160170 for configuration in queryset :
161171 original_name = configuration .name
162172 new_name = "Copy of " + configuration .name
@@ -188,8 +198,58 @@ def duplicate_configuration(model_admin, request, queryset: QuerySetType[Configu
188198 )
189199
190200
191- @admin .action (description = "Waive selected charges" )
201+ @admin .action (description = "Duplicate selected tool usage questions" )
202+ def duplicate_tool_usage_questions (model_admin , request , queryset : QuerySetType [ToolUsageQuestions ]):
203+ if not has_perm (request , queryset , "add" ) or not has_perm (request , queryset , "change" ):
204+ model_admin .message_user (request , "You do not have permission to run this action." , level = messages .ERROR )
205+ for tool_usage_question in queryset :
206+ try :
207+ old_tools = tool_usage_question .only_for_tools .all ()
208+ old_projects = tool_usage_question .only_for_projects .all ()
209+ old_users = tool_usage_question .only_for_users .all ()
210+ old_groups = tool_usage_question .only_for_groups .all ()
211+ new_tool_usage_question = new_model_copy (tool_usage_question )
212+ new_tool_usage_question .display_order = tool_usage_question .display_order + 1
213+
214+ def walk (x ):
215+ if isinstance (x , Mapping ):
216+ x = dict (x )
217+ if "name" in x and isinstance (x ["name" ], str ):
218+ x ["name" ] += f"_{ new_tool_usage_question .display_order } "
219+ for k , v in x .items ():
220+ x [k ] = walk (v )
221+ return x
222+ elif isinstance (x , Sequence ) and not isinstance (x , str ):
223+ return [walk (i ) for i in x ]
224+ return x
225+
226+ new_tool_usage_question .questions = json .dumps (walk (json .loads (tool_usage_question .questions )), indent = 4 )
227+
228+ new_tool_usage_question .full_clean ()
229+ new_tool_usage_question .save ()
230+ new_tool_usage_question .only_for_tools .set (old_tools )
231+ new_tool_usage_question .only_for_projects .set (old_projects )
232+ new_tool_usage_question .only_for_users .set (old_users )
233+ new_tool_usage_question .only_for_groups .set (old_groups )
234+ messages .success (
235+ request ,
236+ mark_safe (
237+ f'A duplicate of { str (tool_usage_question )} has been made as <a href="{ reverse ("admin:NEMO_toolusagequestions_change" , args = [new_tool_usage_question .id ])} ">{ str (new_tool_usage_question )} </a>'
238+ ),
239+ )
240+ except Exception as error :
241+ messages .error (
242+ request ,
243+ f"{ str (tool_usage_question )} could not be duplicated because of the following error: { str (error )} " ,
244+ )
245+
246+
247+ @admin .action (description = "Waive selected charges" , permissions = ["change" ])
192248def waive_selected_charges (model_admin , request , queryset : QuerySetType [BillableItemMixin ]):
193249 for charge in queryset :
194250 charge .waive (request .user )
195251 messages .success (request , f"{ model_admin .model .__name__ } #{ charge .id } has been successfully waived" )
252+
253+
254+ def has_perm (request , qs , action ) -> bool :
255+ return request .user .has_perm (get_django_default_perm (qs .model , action ))
0 commit comments