educommon 3.12.0__py3-none-any.whl → 3.13.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- educommon/__init__.py +0 -1
- educommon/about/ui/actions.py +16 -30
- educommon/about/ui/ui.py +3 -12
- educommon/about/utils.py +6 -5
- educommon/async_task/__init__.py +0 -1
- educommon/async_task/actions.py +18 -13
- educommon/async_task/apps.py +4 -0
- educommon/async_task/locker.py +2 -5
- educommon/async_task/migrations/0001_initial.py +55 -9
- educommon/async_task/migrations/0002_task_type_and_status_data.py +94 -89
- educommon/async_task/migrations/0003_alter_runningtask_options.py +0 -1
- educommon/async_task/models.py +9 -6
- educommon/async_task/tasks.py +11 -7
- educommon/async_task/ui.py +16 -35
- educommon/async_tasks/__init__.py +0 -1
- educommon/async_tasks/apps.py +4 -0
- educommon/async_tasks/locks.py +11 -21
- educommon/async_tasks/migrations/0001_initial.py +68 -8
- educommon/async_tasks/migrations/0002_load_initial_data.py +0 -1
- educommon/async_tasks/models.py +9 -29
- educommon/async_tasks/tasks.py +25 -54
- educommon/audit_log/__init__.py +1 -0
- educommon/audit_log/actions.py +27 -36
- educommon/audit_log/app_meta.py +7 -4
- educommon/audit_log/apps.py +44 -29
- educommon/audit_log/constants.py +7 -4
- educommon/audit_log/error_log/actions.py +1 -3
- educommon/audit_log/helpers.py +2 -4
- educommon/audit_log/management/commands/reinstall_audit_log.py +11 -7
- educommon/audit_log/migrations/0001_initial.py +91 -16
- educommon/audit_log/migrations/0002_install_audit_log.py +13 -13
- educommon/audit_log/migrations/0003_logproxy.py +1 -3
- educommon/audit_log/migrations/0004_reinstall_audit_log.py +1 -4
- educommon/audit_log/migrations/0005_postgresql_error.py +4 -2
- educommon/audit_log/migrations/0006_auto_20200806_1707.py +3 -4
- educommon/audit_log/migrations/0007_create_selective_tables_function.py +8 -5
- educommon/audit_log/migrations/0008_table_logged.py +0 -1
- educommon/audit_log/migrations/0009_reinstall_audit_log.py +0 -1
- educommon/audit_log/models.py +36 -42
- educommon/audit_log/permissions.py +11 -9
- educommon/audit_log/proxies.py +12 -23
- educommon/audit_log/ui.py +18 -15
- educommon/audit_log/utils/__init__.py +28 -60
- educommon/audit_log/utils/operations.py +16 -2
- educommon/auth/__init__.py +0 -3
- educommon/auth/rbac/__init__.py +2 -4
- educommon/auth/rbac/actions.py +148 -145
- educommon/auth/rbac/app_meta.py +9 -6
- educommon/auth/rbac/backends/base.py +2 -8
- educommon/auth/rbac/backends/caching.py +27 -37
- educommon/auth/rbac/backends/simple.py +1 -4
- educommon/auth/rbac/checker.py +1 -3
- educommon/auth/rbac/management/commands/rbac.py +6 -11
- educommon/auth/rbac/manager.py +18 -47
- educommon/auth/rbac/migrations/0001_initial.py +73 -12
- educommon/auth/rbac/migrations/0002_model_modifier_metaclass_fix.py +7 -6
- educommon/auth/rbac/migrations/0003_permission_hidden.py +1 -5
- educommon/auth/rbac/migrations/0004_auto_20171024_1245.py +26 -19
- educommon/auth/rbac/models.py +63 -68
- educommon/auth/rbac/permissions.py +6 -7
- educommon/auth/rbac/ui.py +83 -84
- educommon/auth/rbac/utils.py +10 -11
- educommon/auth/rbac/validators.py +4 -5
- educommon/auth/simple_auth/__init__.py +1 -5
- educommon/auth/simple_auth/actions.py +79 -92
- educommon/auth/simple_auth/app_meta.py +2 -9
- educommon/auth/simple_auth/checkers.py +3 -3
- educommon/auth/simple_auth/migrations/0001_initial.py +23 -4
- educommon/auth/simple_auth/validators.py +0 -1
- educommon/contingent/actions.py +7 -7
- educommon/contingent/app_meta.py +1 -4
- educommon/contingent/base.py +10 -15
- educommon/contingent/catalogs.py +424 -540
- educommon/contingent/contingent_plugin/actions.py +4 -15
- educommon/contingent/contingent_plugin/apps.py +10 -4
- educommon/contingent/contingent_plugin/migrations/0001_initial.py +5 -6
- educommon/contingent/contingent_plugin/migrations/0002_add_contingent_model_deleted.py +6 -11
- educommon/contingent/contingent_plugin/model_views.py +2 -12
- educommon/contingent/contingent_plugin/models.py +2 -7
- educommon/contingent/contingent_plugin/observer.py +14 -13
- educommon/contingent/contingent_plugin/plugin_meta.py +1 -3
- educommon/contingent/contingent_plugin/storage.py +8 -7
- educommon/contingent/contingent_plugin/utils.py +6 -6
- educommon/django/db/fields.py +72 -86
- educommon/django/db/migration/__init__.py +3 -7
- educommon/django/db/migration/operations.py +29 -51
- educommon/django/db/mixins/__init__.py +16 -10
- educommon/django/db/mixins/date_interval.py +47 -75
- educommon/django/db/mixins/validation.py +26 -26
- educommon/django/db/model_view/__init__.py +18 -22
- educommon/django/db/models.py +9 -8
- educommon/django/db/observer.py +9 -27
- educommon/django/db/partitioning/__init__.py +66 -92
- educommon/django/db/partitioning/management/commands/apply_partitioning.py +3 -13
- educommon/django/db/partitioning/management/commands/clear_table.py +18 -14
- educommon/django/db/partitioning/management/commands/split_table.py +18 -13
- educommon/django/db/routers.py +6 -15
- educommon/django/db/signals.py +149 -2
- educommon/django/db/utils.py +14 -19
- educommon/django/db/validators/__init__.py +1 -0
- educommon/django/db/validators/simple.py +72 -100
- educommon/django/storages/atcfs/api.py +39 -53
- educommon/django/storages/atcfs/app_meta.py +1 -1
- educommon/django/storages/atcfs/management/commands/atcfs_migrate.py +42 -55
- educommon/django/storages/atcfs/models.py +0 -3
- educommon/django/storages/atcfs/monkey_patching.py +18 -12
- educommon/django/storages/atcfs/storage.py +14 -23
- educommon/extjs/fields/input_params.py +15 -45
- educommon/importer/XLSReader.py +143 -241
- educommon/importer/__init__.py +86 -4
- educommon/importer/api.py +53 -84
- educommon/importer/constants.py +4 -14
- educommon/importer/loggers.py +16 -26
- educommon/importer/proxy.py +131 -176
- educommon/importer/proxy_import.py +11 -12
- educommon/importer/report.py +4 -6
- educommon/importer/ui.py +32 -26
- educommon/importer/validators.py +4 -7
- educommon/integration_entities/helpers.py +14 -18
- educommon/ioc/__init__.py +3 -6
- educommon/logger/loggers.py +10 -14
- educommon/m3/__init__.py +20 -38
- educommon/m3/extensions/__init__.py +1 -0
- educommon/m3/extensions/listeners/__init__.py +22 -38
- educommon/m3/extensions/listeners/delete_check/listeners.py +31 -41
- educommon/m3/extensions/listeners/delete_check/mixins.py +20 -25
- educommon/m3/extensions/listeners/delete_check/signals.py +2 -2
- educommon/m3/extensions/listeners/delete_check/ui.py +15 -14
- educommon/m3/extensions/listeners/delete_check/utils.py +9 -11
- educommon/m3/extensions/ui.py +15 -33
- educommon/m3/transaction_context.py +17 -19
- educommon/objectpack/actions.py +70 -88
- educommon/objectpack/apps.py +5 -0
- educommon/objectpack/filters.py +9 -15
- educommon/objectpack/ui.py +59 -77
- educommon/report/__init__.py +9 -5
- educommon/report/actions.py +29 -32
- educommon/report/constructor/__init__.py +5 -8
- educommon/report/constructor/app_meta.py +1 -3
- educommon/report/constructor/apps.py +1 -0
- educommon/report/constructor/base.py +33 -80
- educommon/report/constructor/builders/excel/_base.py +138 -286
- educommon/report/constructor/builders/excel/_header.py +2 -9
- educommon/report/constructor/builders/excel/product.py +13 -34
- educommon/report/constructor/builders/excel/with_merged_cells.py +18 -14
- educommon/report/constructor/config.py +2 -0
- educommon/report/constructor/editor/actions.py +101 -215
- educommon/report/constructor/editor/ui.py +71 -93
- educommon/report/constructor/exceptions.py +6 -12
- educommon/report/constructor/migrations/0001_initial.py +36 -44
- educommon/report/constructor/migrations/0002_report_filters.py +86 -72
- educommon/report/constructor/migrations/0003_reportfilter_exclude.py +5 -5
- educommon/report/constructor/migrations/0004_reportfilter_fields.py +22 -18
- educommon/report/constructor/migrations/0005_reportcolumn_visible.py +5 -4
- educommon/report/constructor/migrations/0006_reportsorting.py +21 -17
- educommon/report/constructor/migrations/0007_include_available_units.py +14 -14
- educommon/report/constructor/migrations/0008_auto_20170407_1318.py +4 -5
- educommon/report/constructor/migrations/0009_auto_20180405_0642.py +1 -4
- educommon/report/constructor/migrations/0010_add_aggregate_fields.py +7 -8
- educommon/report/constructor/mixins.py +14 -15
- educommon/report/constructor/models.py +76 -124
- educommon/report/constructor/utils.py +3 -8
- educommon/report/constructor/validators.py +1 -3
- educommon/report/reporter.py +25 -43
- educommon/report/utils.py +14 -40
- educommon/rest/actions.py +7 -11
- educommon/rest/context.py +6 -16
- educommon/rest/controllers.py +10 -10
- educommon/rest/mixins.py +29 -27
- educommon/secure_media/app_meta.py +9 -9
- educommon/utils/__init__.py +3 -2
- educommon/utils/caching.py +1 -3
- educommon/utils/conversion.py +1 -3
- educommon/utils/crypto.py +1 -2
- educommon/utils/date.py +13 -26
- educommon/utils/db/__init__.py +17 -26
- educommon/utils/db/postgresql.py +1 -4
- educommon/utils/fonts/__init__.py +3 -4
- educommon/utils/licence/__init__.py +5 -16
- educommon/utils/misc.py +9 -18
- educommon/utils/object_grid.py +55 -62
- educommon/utils/phone_number/modelfields.py +1 -3
- educommon/utils/phone_number/phone_number.py +5 -8
- educommon/utils/phone_number/validators.py +8 -23
- educommon/utils/plugins.py +15 -28
- educommon/utils/registry.py +2 -1
- educommon/utils/seqtools.py +1 -3
- educommon/utils/serializer.py +9 -16
- educommon/utils/storage.py +3 -2
- educommon/utils/system.py +1 -3
- educommon/utils/system_app/management/commands/delete_objects.py +17 -34
- educommon/utils/ui.py +87 -84
- educommon/utils/xml/__init__.py +2 -7
- educommon/utils/xml/resolver.py +1 -0
- educommon/ws_log/actions.py +31 -76
- educommon/ws_log/base.py +6 -20
- educommon/ws_log/migrations/0001_initial.py +25 -8
- educommon/ws_log/migrations/0002_auto_20160628_1334.py +0 -1
- educommon/ws_log/migrations/0003_add_fields_to_smev_logs.py +20 -4
- educommon/ws_log/migrations/0004_auto_20160727_1600.py +7 -6
- educommon/ws_log/migrations/0005_auto_20161130_1615.py +14 -4
- educommon/ws_log/migrations/0006_auto_20170327_1027.py +3 -2
- educommon/ws_log/migrations/0007_auto_20180607_1040.py +8 -9
- educommon/ws_log/migrations/0008_auto_20180713_1445.py +23 -10
- educommon/ws_log/migrations/0009_auto_20201130_1553.py +7 -2
- educommon/ws_log/models.py +21 -35
- educommon/ws_log/provider.py +2 -1
- educommon/ws_log/report.py +8 -13
- educommon/ws_log/smev/applications.py +12 -27
- educommon/ws_log/smev/exceptions.py +2 -3
- educommon/ws_log/ui.py +32 -32
- educommon/ws_log/utils.py +1 -3
- educommon-3.13.2.dist-info/METADATA +57 -0
- educommon-3.13.2.dist-info/RECORD +354 -0
- {educommon-3.12.0.dist-info → educommon-3.13.2.dist-info}/WHEEL +1 -1
- educommon/utils/patches.py +0 -27
- educommon/version.conf +0 -11
- educommon-3.12.0.dist-info/METADATA +0 -47
- educommon-3.12.0.dist-info/RECORD +0 -357
- educommon-3.12.0.dist-info/dependency_links.txt +0 -1
- {educommon-3.12.0.dist-info → educommon-3.13.2.dist-info}/top_level.txt +0 -0
educommon/auth/rbac/actions.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
"""Паки и экшены для окна реестра "Роли"."""
|
2
|
+
|
2
3
|
import json
|
3
4
|
from collections import (
|
4
5
|
defaultdict,
|
@@ -101,12 +102,11 @@ from educommon.m3 import (
|
|
101
102
|
|
102
103
|
|
103
104
|
def _get_role(role_id):
|
105
|
+
"""Возвращает объект роли по ID или выбрасывает исключение."""
|
104
106
|
try:
|
105
107
|
return Role.objects.get(pk=role_id)
|
106
108
|
except Role.DoesNotExist:
|
107
|
-
raise ApplicationLogicException(
|
108
|
-
'Роль ID:{} не существует'.format(role_id)
|
109
|
-
)
|
109
|
+
raise ApplicationLogicException('Роль ID:{} не существует'.format(role_id))
|
110
110
|
|
111
111
|
|
112
112
|
class RolesTreeRowsAction(ObjectRowsAction):
|
@@ -143,14 +143,15 @@ class RolesTreeRowsAction(ObjectRowsAction):
|
|
143
143
|
|
144
144
|
def prepare_object(self, obj):
|
145
145
|
"""Сохранение данных роли в словарь перед сериализацией в JSON."""
|
146
|
-
data = super(
|
146
|
+
data = super().prepare_object(obj)
|
147
147
|
|
148
148
|
data['leaf'] = self.is_leaf(obj)
|
149
149
|
|
150
150
|
return data
|
151
151
|
|
152
152
|
def run(self, *args, **kwargs):
|
153
|
-
|
153
|
+
"""Тело Action, вызывается при обработке запроса к серверу."""
|
154
|
+
result = super().run(*args, **kwargs)
|
154
155
|
|
155
156
|
data = result.data.get('rows', [])
|
156
157
|
|
@@ -161,10 +162,15 @@ class AddRoleToRoleWindowAction(ObjectSelectWindowAction):
|
|
161
162
|
"""Отображение окна добавления одной роли в другую."""
|
162
163
|
|
163
164
|
def create_window(self):
|
165
|
+
"""Метод инстанцирует окно."""
|
164
166
|
self.win = ui.RoleSelectWindow()
|
165
167
|
|
166
168
|
def set_window_params(self):
|
167
|
-
|
169
|
+
"""Метод заполняет словарь self.win_params, который будет передан в окно.
|
170
|
+
|
171
|
+
Этот словарь выступает как шина передачи данных от Actions/Packs к окну.
|
172
|
+
"""
|
173
|
+
super().set_window_params()
|
168
174
|
|
169
175
|
self.win_params['pack'] = self.parent
|
170
176
|
|
@@ -178,6 +184,7 @@ class AddRoleToRoleAction(BaseAction):
|
|
178
184
|
|
179
185
|
@convert_validation_error_to(ApplicationLogicException)
|
180
186
|
def run(self, request, context):
|
187
|
+
"""Тело Action, вызывается при обработке запроса к серверу."""
|
181
188
|
role_parent = RoleParent(
|
182
189
|
role=_get_role(getattr(context, self.parent.id_param_name)),
|
183
190
|
parent=_get_role(context.parent_id),
|
@@ -192,15 +199,13 @@ class DeleteRoleFromRoleAction(BaseAction):
|
|
192
199
|
"""Удаление одной роли из другой."""
|
193
200
|
|
194
201
|
def run(self, request, context):
|
202
|
+
"""Тело Action, вызывается при обработке запроса к серверу."""
|
195
203
|
try:
|
196
204
|
role_parent = RoleParent.objects.get(
|
197
|
-
role=_get_role(getattr(context, self.parent.id_param_name)),
|
198
|
-
parent_id=context.parent_id
|
205
|
+
role=_get_role(getattr(context, self.parent.id_param_name)), parent_id=context.parent_id
|
199
206
|
)
|
200
207
|
except RoleParent.DoesNotExist:
|
201
|
-
raise ApplicationLogicException(
|
202
|
-
'Выбранная роль должна являться вложенной ролью.'
|
203
|
-
)
|
208
|
+
raise ApplicationLogicException('Выбранная роль должна являться вложенной ролью.')
|
204
209
|
|
205
210
|
role_parent.delete()
|
206
211
|
|
@@ -228,7 +233,7 @@ class Pack(TreeObjectPack):
|
|
228
233
|
column_name_on_select = 'name'
|
229
234
|
|
230
235
|
def __init__(self):
|
231
|
-
super(
|
236
|
+
super().__init__()
|
232
237
|
|
233
238
|
self.replace_action('rows_action', RolesTreeRowsAction())
|
234
239
|
|
@@ -236,30 +241,36 @@ class Pack(TreeObjectPack):
|
|
236
241
|
self.add_role_to_role_action = AddRoleToRoleAction()
|
237
242
|
self.delete_role_from_role_action = DeleteRoleFromRoleAction()
|
238
243
|
|
239
|
-
self.actions.extend(
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
+
self.actions.extend(
|
245
|
+
(
|
246
|
+
self.add_role_to_role_window_action,
|
247
|
+
self.add_role_to_role_action,
|
248
|
+
self.delete_role_from_role_action,
|
249
|
+
)
|
250
|
+
)
|
244
251
|
# ---------------------------------------------------------------------
|
245
252
|
# Настройка разрешений для экшенов пака.
|
246
253
|
self.need_check_permission = True
|
247
254
|
self.perm_code = PERM_GROUP__ROLE
|
248
255
|
|
249
|
-
for action in (
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
256
|
+
for action in (
|
257
|
+
self.autocomplete_action,
|
258
|
+
self.list_window_action,
|
259
|
+
self.multi_select_window_action,
|
260
|
+
self.rows_action,
|
261
|
+
self.select_window_action,
|
262
|
+
self.edit_window_action,
|
263
|
+
):
|
255
264
|
action.perm_code = 'view'
|
256
265
|
|
257
|
-
for action in (
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
266
|
+
for action in (
|
267
|
+
self.new_window_action,
|
268
|
+
self.save_action,
|
269
|
+
self.delete_action,
|
270
|
+
self.add_role_to_role_window_action,
|
271
|
+
self.add_role_to_role_action,
|
272
|
+
self.delete_role_from_role_action,
|
273
|
+
):
|
263
274
|
action.perm_code = 'edit'
|
264
275
|
# ---------------------------------------------------------------------
|
265
276
|
|
@@ -270,11 +281,10 @@ class Pack(TreeObjectPack):
|
|
270
281
|
)
|
271
282
|
|
272
283
|
def declare_context(self, action):
|
273
|
-
|
284
|
+
"""Декларирует контекст для экшна."""
|
285
|
+
result = super().declare_context(action)
|
274
286
|
|
275
|
-
if action in (self.rows_action,
|
276
|
-
self.add_role_to_role_window_action,
|
277
|
-
self.add_role_to_role_action):
|
287
|
+
if action in (self.rows_action, self.add_role_to_role_window_action, self.add_role_to_role_action):
|
278
288
|
result[self.id_param_name] = dict(type='int')
|
279
289
|
|
280
290
|
if action is self.rows_action:
|
@@ -297,27 +307,22 @@ class Pack(TreeObjectPack):
|
|
297
307
|
return result
|
298
308
|
|
299
309
|
def get_rows_query(self, request, context):
|
310
|
+
"""Возвращает выборку из БД для получения списка данных."""
|
300
311
|
request_params = get_request_params(request)
|
301
312
|
if request_params.get('filter'):
|
302
|
-
return super(
|
313
|
+
return super().get_rows_query(request, context)
|
303
314
|
|
304
315
|
current_role_id = getattr(context, self.id_param_name)
|
305
316
|
|
306
317
|
if request_params.get('filter', False):
|
307
|
-
result = super(
|
308
|
-
request, context
|
309
|
-
)
|
318
|
+
result = super().get_rows_query(request, context)
|
310
319
|
elif current_role_id < 0:
|
311
320
|
# Вывод корневых ролей
|
312
|
-
result = self.model.objects.exclude(
|
313
|
-
pk__in=RoleParent.objects.values('role').distinct()
|
314
|
-
)
|
321
|
+
result = self.model.objects.exclude(pk__in=RoleParent.objects.values('role').distinct())
|
315
322
|
else:
|
316
323
|
# Вывод ролей, вложенных в указанную роль
|
317
324
|
result = self.model.objects.filter(
|
318
|
-
pk__in=RoleParent.objects.filter(
|
319
|
-
parent=current_role_id
|
320
|
-
).values('role').distinct()
|
325
|
+
pk__in=RoleParent.objects.filter(parent=current_role_id).values('role').distinct()
|
321
326
|
)
|
322
327
|
|
323
328
|
if context.select_mode and context.role_id is not None:
|
@@ -328,20 +333,15 @@ class Pack(TreeObjectPack):
|
|
328
333
|
try:
|
329
334
|
role = Role.objects.get(pk=context.role_id)
|
330
335
|
except Role.DoesNotExist:
|
331
|
-
raise ApplicationLogicException(
|
332
|
-
'Роль ID:{} не найдена'.format(context.role_id)
|
333
|
-
)
|
336
|
+
raise ApplicationLogicException('Роль ID:{} не найдена'.format(context.role_id))
|
334
337
|
|
335
|
-
result = result.exclude(
|
336
|
-
pk__in=set([role.id]) | set(r.id for r in role.subroles)
|
337
|
-
)
|
338
|
+
result = result.exclude(pk__in=set([role.id]) | set(r.id for r in role.subroles))
|
338
339
|
|
339
340
|
return result
|
340
341
|
|
341
342
|
def get_list_window_params(self, params, request, context):
|
342
|
-
|
343
|
-
|
344
|
-
)
|
343
|
+
"""Возвращает словарь параметров, которые будут переданы окну списка."""
|
344
|
+
params = super().get_list_window_params(params, request, context)
|
345
345
|
|
346
346
|
if not params['is_select_mode']:
|
347
347
|
params['width'] = 700
|
@@ -352,9 +352,8 @@ class Pack(TreeObjectPack):
|
|
352
352
|
return params
|
353
353
|
|
354
354
|
def get_edit_window_params(self, params, request, context):
|
355
|
-
|
356
|
-
|
357
|
-
)
|
355
|
+
"""Возвращает словарь параметров, которые будут переданы окну редактирования."""
|
356
|
+
params = super().get_edit_window_params(params, request, context)
|
358
357
|
|
359
358
|
params['roles_pack'] = self
|
360
359
|
params['partitions_pack'] = get_pack(PartitionsPack)
|
@@ -368,21 +367,13 @@ class Pack(TreeObjectPack):
|
|
368
367
|
params['show_user_types'] = True
|
369
368
|
params['user_types'] = tuple(
|
370
369
|
(u_type.id, u_type.name)
|
371
|
-
for u_type in (
|
372
|
-
ContentType.objects.get_for_models(
|
373
|
-
*rbac_config.user_types
|
374
|
-
).values()
|
375
|
-
)
|
370
|
+
for u_type in (ContentType.objects.get_for_models(*rbac_config.user_types).values())
|
376
371
|
)
|
377
372
|
if not params['create_new']:
|
378
|
-
params['user_type_ids'] = tuple(
|
379
|
-
params['object'].user_types.values_list('pk', flat=True)
|
380
|
-
)
|
373
|
+
params['user_type_ids'] = tuple(params['object'].user_types.values_list('pk', flat=True))
|
381
374
|
|
382
375
|
if not params['create_new']:
|
383
|
-
params['permission_ids'] = list(
|
384
|
-
params['object'].permissions.values_list('pk', flat=True)
|
385
|
-
)
|
376
|
+
params['permission_ids'] = list(params['object'].permissions.values_list('pk', flat=True))
|
386
377
|
|
387
378
|
params['can_edit'] = rbac.has_access(self.save_action, request)
|
388
379
|
if not params['can_edit']:
|
@@ -413,33 +404,25 @@ class Pack(TreeObjectPack):
|
|
413
404
|
)
|
414
405
|
# Проверка, есть ли пользователи с удаляемыми типами
|
415
406
|
if types_to_delete and user_roles_to_delete.exists():
|
416
|
-
related_content_types = ContentType.objects.filter(
|
417
|
-
pk__in=types_to_delete
|
418
|
-
)
|
407
|
+
related_content_types = ContentType.objects.filter(pk__in=types_to_delete)
|
419
408
|
raise ApplicationLogicException(
|
420
409
|
'Невозможно отменить назначение роли для пользователей '
|
421
410
|
'типа {}, т.к. данная роль уже назначена {} '
|
422
411
|
'пользователей.'.format(
|
423
|
-
', '.join(
|
424
|
-
|
425
|
-
),
|
426
|
-
'этому типу'
|
427
|
-
if len(types_to_delete) == 1 else 'этим типам',
|
412
|
+
', '.join('"{}"'.format(ct.name) for ct in related_content_types),
|
413
|
+
'этому типу' if len(types_to_delete) == 1 else 'этим типам',
|
428
414
|
)
|
429
415
|
)
|
430
416
|
# Удаление лишних типов пользователей
|
431
|
-
RoleUserType.objects.filter(
|
432
|
-
role=role, user_type__in=types_to_delete
|
433
|
-
).delete()
|
417
|
+
RoleUserType.objects.filter(role=role, user_type__in=types_to_delete).delete()
|
434
418
|
# Добавление новых типов пользователей
|
435
419
|
for user_type_id in new_user_types - old_user_types:
|
436
|
-
RoleUserType.objects.create(
|
437
|
-
role=role, user_type_id=user_type_id
|
438
|
-
)
|
420
|
+
RoleUserType.objects.create(role=role, user_type_id=user_type_id)
|
439
421
|
|
440
422
|
@convert_validation_error_to(ValidationError)
|
441
423
|
@atomic
|
442
424
|
def save_row(self, obj, create_new, request, context):
|
425
|
+
"""Сохраняет объект."""
|
443
426
|
# При отключении флага удаляются все связи с типами пользователей
|
444
427
|
if not (create_new or obj.can_be_assigned):
|
445
428
|
RoleUserType.objects.filter(role=obj).delete()
|
@@ -454,9 +437,7 @@ class Pack(TreeObjectPack):
|
|
454
437
|
try:
|
455
438
|
parent = Role.objects.get(pk=context.parent_id)
|
456
439
|
except Role.DoesNotExist:
|
457
|
-
raise ApplicationLogicException(
|
458
|
-
'Роль ID:{} не существует.'.format(context.parent_id)
|
459
|
-
)
|
440
|
+
raise ApplicationLogicException('Роль ID:{} не существует.'.format(context.parent_id))
|
460
441
|
|
461
442
|
RoleParent.objects.create(role=obj, parent=parent)
|
462
443
|
else:
|
@@ -472,9 +453,7 @@ class Pack(TreeObjectPack):
|
|
472
453
|
|
473
454
|
# Добавление новых прав в роль
|
474
455
|
for permission_id in new_permissions - old_permissions:
|
475
|
-
RolePermission.objects.get_or_create(
|
476
|
-
role=obj, permission_id=permission_id
|
477
|
-
)
|
456
|
+
RolePermission.objects.get_or_create(role=obj, permission_id=permission_id)
|
478
457
|
|
479
458
|
|
480
459
|
class Partition(VirtualModel):
|
@@ -488,6 +467,12 @@ class Partition(VirtualModel):
|
|
488
467
|
|
489
468
|
@classmethod
|
490
469
|
def _get_ids(cls):
|
470
|
+
"""Метод получения ID и названий разделов системы.
|
471
|
+
|
472
|
+
Метод возвращает iterable, или callable, возвращаюший iterable,
|
473
|
+
для каждого элемента которого (iterable) будет инстанцирован объект класса
|
474
|
+
(каждый эл-т итератора передаётся в конструктор).
|
475
|
+
"""
|
491
476
|
if not hasattr(cls, 'data'):
|
492
477
|
cls.data = []
|
493
478
|
for i, title in enumerate(sorted(rbac.partitions)):
|
@@ -514,7 +499,7 @@ class PartitionsPack(ObjectPack):
|
|
514
499
|
allow_paging = False
|
515
500
|
|
516
501
|
def __init__(self):
|
517
|
-
super(
|
502
|
+
super().__init__()
|
518
503
|
# ---------------------------------------------------------------------
|
519
504
|
|
520
505
|
self.need_check_permission = True
|
@@ -552,18 +537,17 @@ class PermissionsPack(ObjectPack):
|
|
552
537
|
allow_paging = False
|
553
538
|
|
554
539
|
def __init__(self):
|
555
|
-
super(
|
556
|
-
# ---------------------------------------------------------------------
|
540
|
+
super().__init__()
|
557
541
|
|
558
542
|
self.need_check_permission = True
|
559
543
|
self.perm_code = PERM_GROUP__ROLE
|
560
544
|
|
561
545
|
for action in self.actions:
|
562
546
|
action.perm_code = 'view'
|
563
|
-
# ---------------------------------------------------------------------
|
564
547
|
|
565
548
|
def declare_context(self, action):
|
566
|
-
|
549
|
+
"""Декларирует контекст для экшна"""
|
550
|
+
result = super().declare_context(action)
|
567
551
|
|
568
552
|
if action is self.rows_action:
|
569
553
|
result['partition_id'] = dict(type='int')
|
@@ -571,70 +555,71 @@ class PermissionsPack(ObjectPack):
|
|
571
555
|
return result
|
572
556
|
|
573
557
|
def prepare_row(self, obj, request, context):
|
574
|
-
|
575
|
-
|
576
|
-
)
|
558
|
+
"""Установка дополнительных атрибутов объекта."""
|
559
|
+
result = super().prepare_row(obj, request, context)
|
577
560
|
|
578
|
-
permission_names = (
|
579
|
-
|
561
|
+
permission_names = rbac.get_dependent_permissions(obj.name) - rbac.hidden_permissions
|
562
|
+
result.dependencies = json.dumps(
|
563
|
+
sorted(get_permission_full_title(dependency) for dependency in permission_names)
|
580
564
|
)
|
581
|
-
result.dependencies = json.dumps(sorted(
|
582
|
-
get_permission_full_title(dependency)
|
583
|
-
for dependency in permission_names
|
584
|
-
))
|
585
565
|
|
586
566
|
return result
|
587
567
|
|
588
568
|
def get_rows_query(self, request, context):
|
569
|
+
"""Возвращает выборку из БД для получения списка данных."""
|
589
570
|
# Определение название раздела по его id.
|
590
571
|
try:
|
591
|
-
partition = PartitionsPack.model.objects.get(
|
592
|
-
id=context.partition_id
|
593
|
-
).title
|
572
|
+
partition = PartitionsPack.model.objects.get(id=context.partition_id).title
|
594
573
|
except PartitionsPack.model.DoesNotExists:
|
595
|
-
raise ApplicationLogicException(
|
596
|
-
|
574
|
+
raise ApplicationLogicException('Раздел {} не существует'.format(context.partition_id))
|
575
|
+
|
576
|
+
query = (
|
577
|
+
super()
|
578
|
+
.get_rows_query(request, context)
|
579
|
+
.filter(
|
580
|
+
# Условия для выборки разрешений только из раздела partition.
|
581
|
+
reduce(or_, (Q(name__startswith=code + '/') for code in rbac.partitions[partition])),
|
582
|
+
hidden=False,
|
597
583
|
)
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
When(
|
613
|
-
name__startswith=group + '/',
|
614
|
-
then=Concat(
|
615
|
-
Value(title + ' - ' if title else ''),
|
616
|
-
F('title'),
|
617
|
-
)
|
584
|
+
.annotate(
|
585
|
+
title_with_group=Case(
|
586
|
+
# Добавление названия группы к названию разрешения.
|
587
|
+
output_field=CharField(),
|
588
|
+
*(
|
589
|
+
When(
|
590
|
+
name__startswith=group + '/',
|
591
|
+
then=Concat(
|
592
|
+
Value(title + ' - ' if title else ''),
|
593
|
+
F('title'),
|
594
|
+
),
|
595
|
+
)
|
596
|
+
for group, title in rbac.groups.items()
|
597
|
+
),
|
618
598
|
)
|
619
|
-
for group, title in rbac.groups.items()
|
620
599
|
)
|
621
|
-
)
|
600
|
+
)
|
622
601
|
|
623
602
|
return query
|
603
|
+
|
604
|
+
|
624
605
|
# -----------------------------------------------------------------------------
|
625
606
|
|
607
|
+
|
626
608
|
def _get_group_name(perm_name):
|
627
609
|
"""Возвращает имя группы разрешения."""
|
628
610
|
return perm_name.split('/')[0]
|
629
611
|
|
612
|
+
|
630
613
|
def _get_group_title(perm_name):
|
631
614
|
"""Возвращает название группы разрешений."""
|
632
615
|
group_name = _get_group_name(perm_name)
|
633
616
|
group_title = rbac.groups[group_name]
|
617
|
+
|
634
618
|
return group_title
|
635
619
|
|
636
620
|
|
637
621
|
def _get_partition_title(perm_name):
|
622
|
+
"""Возвращает название раздела, к которому относится разрешение."""
|
638
623
|
group_name = perm_name.split('/')[0]
|
639
624
|
|
640
625
|
for title, names in rbac.partitions.items():
|
@@ -648,6 +633,10 @@ class ResultPermissionsAction(BaseAction):
|
|
648
633
|
"""Возвращает данные для грида "Итоговые разрешения"."""
|
649
634
|
|
650
635
|
def _get_nested_roles(self, role_id):
|
636
|
+
"""Возвращает все вложенные роли для заданной роли.
|
637
|
+
|
638
|
+
Строит рекурсивное множество дочерних ролей, включая вложенные на любой глубине.
|
639
|
+
"""
|
651
640
|
role_children = defaultdict(set)
|
652
641
|
query = RoleParent.objects.values_list('parent', 'role')
|
653
642
|
|
@@ -667,10 +656,9 @@ class ResultPermissionsAction(BaseAction):
|
|
667
656
|
return get_nested_roles(role_id)
|
668
657
|
|
669
658
|
def _get_nested_roles_permissions(self, role_id):
|
659
|
+
"""Возвращает разрешения, назначенные вложенным ролям заданной роли."""
|
670
660
|
query = Permission.objects.filter(
|
671
|
-
pk__in=RolePermission.objects.filter(
|
672
|
-
role_id__in=self._get_nested_roles(role_id)
|
673
|
-
).values('permission'),
|
661
|
+
pk__in=RolePermission.objects.filter(role_id__in=self._get_nested_roles(role_id)).values('permission'),
|
674
662
|
hidden=False,
|
675
663
|
).values_list('name', 'title', 'description')
|
676
664
|
|
@@ -685,17 +673,19 @@ class ResultPermissionsAction(BaseAction):
|
|
685
673
|
)
|
686
674
|
|
687
675
|
def _get_dependent_permissions(self, permission_ids):
|
676
|
+
"""Возвращает разрешения, от которых зависят указанные разрешения.
|
677
|
+
|
678
|
+
Исключает скрытые разрешения и формирует структуру для отображения
|
679
|
+
в интерфейсе.
|
680
|
+
"""
|
688
681
|
permissions_by_id = {
|
689
682
|
pk: (name, title, description)
|
690
683
|
for pk, name, title, description in Permission.objects.filter(
|
691
684
|
hidden=False,
|
692
|
-
).values_list(
|
693
|
-
'pk', 'name', 'title', 'description'
|
694
|
-
)
|
685
|
+
).values_list('pk', 'name', 'title', 'description')
|
695
686
|
}
|
696
687
|
permissions_by_name = {
|
697
|
-
name: (pk, title, description)
|
698
|
-
for pk, (name, title, description) in permissions_by_id.items()
|
688
|
+
name: (pk, title, description) for pk, (name, title, description) in permissions_by_id.items()
|
699
689
|
}
|
700
690
|
|
701
691
|
for perm_id in permission_ids:
|
@@ -719,6 +709,7 @@ class ResultPermissionsAction(BaseAction):
|
|
719
709
|
)
|
720
710
|
|
721
711
|
def _get_role_permissions(self, permission_ids):
|
712
|
+
"""Возвращает основные разрешения, явно назначенные роли."""
|
722
713
|
query = Permission.objects.filter(
|
723
714
|
pk__in=permission_ids,
|
724
715
|
hidden=False,
|
@@ -736,6 +727,15 @@ class ResultPermissionsAction(BaseAction):
|
|
736
727
|
)
|
737
728
|
|
738
729
|
def run(self, request, context):
|
730
|
+
"""Обрабатывает запрос на получение итоговых разрешений роли.
|
731
|
+
|
732
|
+
Комбинирует разрешения:
|
733
|
+
- явно назначенные роли;
|
734
|
+
- зависимые разрешения;
|
735
|
+
- разрешения вложенных ролей.
|
736
|
+
|
737
|
+
Возвращает данные, сгруппированные по разделам и группам.
|
738
|
+
"""
|
739
739
|
perm_names = set()
|
740
740
|
|
741
741
|
data = defaultdict(lambda: defaultdict(list))
|
@@ -751,11 +751,13 @@ class ResultPermissionsAction(BaseAction):
|
|
751
751
|
partition_title = perm_data['partition']
|
752
752
|
group_title = perm_data['group']
|
753
753
|
|
754
|
-
data[partition_title][group_title].append(
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
754
|
+
data[partition_title][group_title].append(
|
755
|
+
dict(
|
756
|
+
title=perm_data['title'],
|
757
|
+
description=perm_data['description'],
|
758
|
+
source=perm_data['source'],
|
759
|
+
)
|
760
|
+
)
|
759
761
|
|
760
762
|
return PreJsonResult(data)
|
761
763
|
|
@@ -764,14 +766,12 @@ class ResultPermissionsPack(BasePack):
|
|
764
766
|
"""Набор действий для грида "Итоговые разрешения" окна редактирования."""
|
765
767
|
|
766
768
|
def __init__(self):
|
767
|
-
super(
|
769
|
+
super().__init__()
|
768
770
|
# ---------------------------------------------------------------------
|
769
771
|
|
770
772
|
self.result_permissions_action = ResultPermissionsAction()
|
771
773
|
|
772
|
-
self.actions.extend((
|
773
|
-
self.result_permissions_action,
|
774
|
-
))
|
774
|
+
self.actions.extend((self.result_permissions_action,))
|
775
775
|
# ---------------------------------------------------------------------
|
776
776
|
|
777
777
|
self.need_check_permission = True
|
@@ -782,11 +782,14 @@ class ResultPermissionsPack(BasePack):
|
|
782
782
|
# ---------------------------------------------------------------------
|
783
783
|
|
784
784
|
def declare_context(self, action):
|
785
|
-
|
785
|
+
"""Декларация контекста для экшна."""
|
786
|
+
result = super().declare_context(action)
|
786
787
|
|
787
788
|
if action is self.result_permissions_action:
|
788
789
|
result[get_pack_id(Pack)] = dict(type='int_or_none', default=None)
|
789
790
|
result['role_permissions'] = dict(type='int_list', default=())
|
790
791
|
|
791
792
|
return result
|
793
|
+
|
794
|
+
|
792
795
|
# -----------------------------------------------------------------------------
|
educommon/auth/rbac/app_meta.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
"""Инициализация приложения в M3."""
|
2
|
+
|
2
3
|
from educommon import (
|
3
4
|
ioc,
|
4
5
|
)
|
@@ -9,9 +10,11 @@ from educommon.auth.rbac import (
|
|
9
10
|
|
10
11
|
def register_actions():
|
11
12
|
"""Регистрация паков приложения в контроллере."""
|
12
|
-
ioc.get('auth_controller').extend_packs(
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
13
|
+
ioc.get('auth_controller').extend_packs(
|
14
|
+
(
|
15
|
+
actions.Pack(),
|
16
|
+
actions.PartitionsPack(),
|
17
|
+
actions.PermissionsPack(),
|
18
|
+
actions.ResultPermissionsPack(),
|
19
|
+
)
|
20
|
+
)
|
@@ -19,10 +19,7 @@ class BackendBase(metaclass=ABCMeta):
|
|
19
19
|
|
20
20
|
:rtype: bool
|
21
21
|
"""
|
22
|
-
return
|
23
|
-
action.parent.need_check_permission or
|
24
|
-
action.need_check_permission
|
25
|
-
)
|
22
|
+
return action.parent.need_check_permission or action.need_check_permission
|
26
23
|
|
27
24
|
def _get_current_user(self, request):
|
28
25
|
"""Возвращает текущего пользователя.
|
@@ -37,10 +34,7 @@ class BackendBase(metaclass=ABCMeta):
|
|
37
34
|
:rtype: tuple
|
38
35
|
"""
|
39
36
|
if action.sub_permissions:
|
40
|
-
result = tuple(
|
41
|
-
action.get_perm_code(sub_perm)
|
42
|
-
for sub_perm in action.sub_permissions
|
43
|
-
)
|
37
|
+
result = tuple(action.get_perm_code(sub_perm) for sub_perm in action.sub_permissions)
|
44
38
|
else:
|
45
39
|
result = (action.get_perm_code(),)
|
46
40
|
|