educommon 3.13.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 +4 -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.0.dist-info → educommon-3.13.2.dist-info}/METADATA +26 -14
- educommon-3.13.2.dist-info/RECORD +354 -0
- educommon/utils/patches.py +0 -27
- educommon/version.conf +0 -11
- educommon-3.13.0.dist-info/RECORD +0 -357
- educommon-3.13.0.dist-info/dependency_links.txt +0 -1
- {educommon-3.13.0.dist-info → educommon-3.13.2.dist-info}/WHEEL +0 -0
- {educommon-3.13.0.dist-info → educommon-3.13.2.dist-info}/top_level.txt +0 -0
educommon/__init__.py
CHANGED
educommon/about/ui/actions.py
CHANGED
@@ -48,21 +48,13 @@ class Package(VirtualModel):
|
|
48
48
|
|
49
49
|
@classmethod
|
50
50
|
def _get_ids(cls):
|
51
|
-
packages = sorted(
|
52
|
-
tuple(get_installed_distributions()),
|
53
|
-
key=lambda p: p.project_name
|
54
|
-
)
|
51
|
+
packages = sorted(tuple(get_installed_distributions()), key=lambda p: p.project_name)
|
55
52
|
|
56
53
|
for row_id, package in enumerate(packages, 1):
|
57
|
-
yield dict(
|
58
|
-
id=row_id,
|
59
|
-
name=package.project_name,
|
60
|
-
version=package.version
|
61
|
-
)
|
54
|
+
yield dict(id=row_id, name=package.project_name, version=package.version)
|
62
55
|
|
63
56
|
|
64
57
|
class PackagesPack(ObjectPack):
|
65
|
-
|
66
58
|
"""Пак грида установленных в системе пакетов."""
|
67
59
|
|
68
60
|
model = Package
|
@@ -85,11 +77,12 @@ class PackagesPack(ObjectPack):
|
|
85
77
|
header='Версия',
|
86
78
|
),
|
87
79
|
)
|
80
|
+
|
81
|
+
|
88
82
|
# -----------------------------------------------------------------------------
|
89
83
|
|
90
84
|
|
91
85
|
class PostgreSQLExtension(VirtualModel):
|
92
|
-
|
93
86
|
"""Виртуальная модель 'Расширение PostgreSQL'."""
|
94
87
|
|
95
88
|
def __init__(self, data):
|
@@ -116,7 +109,6 @@ class PostgreSQLExtension(VirtualModel):
|
|
116
109
|
|
117
110
|
|
118
111
|
class PostgreSQLExtensionsPack(ObjectPack):
|
119
|
-
|
120
112
|
"""Пак грида расширений БД."""
|
121
113
|
|
122
114
|
model = PostgreSQLExtension
|
@@ -141,11 +133,12 @@ class PostgreSQLExtensionsPack(ObjectPack):
|
|
141
133
|
width=1,
|
142
134
|
),
|
143
135
|
)
|
136
|
+
|
137
|
+
|
144
138
|
# -----------------------------------------------------------------------------
|
145
139
|
|
146
140
|
|
147
141
|
class AboutPack(BasePack, metaclass=ABCMeta):
|
148
|
-
|
149
142
|
"""Пак окна 'Информация о системе'."""
|
150
143
|
|
151
144
|
title = 'О системе'
|
@@ -165,10 +158,12 @@ class AboutPack(BasePack, metaclass=ABCMeta):
|
|
165
158
|
|
166
159
|
self.packages_pack = PackagesPack()
|
167
160
|
self.postgresql_extensions_pack = PostgreSQLExtensionsPack()
|
168
|
-
self.subpacks.extend(
|
169
|
-
|
170
|
-
|
171
|
-
|
161
|
+
self.subpacks.extend(
|
162
|
+
(
|
163
|
+
self.packages_pack,
|
164
|
+
self.postgresql_extensions_pack,
|
165
|
+
)
|
166
|
+
)
|
172
167
|
|
173
168
|
@abstractmethod
|
174
169
|
def get_version_config_path(self):
|
@@ -183,15 +178,10 @@ class AboutPack(BasePack, metaclass=ABCMeta):
|
|
183
178
|
- настройки состава вкладок;
|
184
179
|
- интеграции с системой прав доступа проекта.
|
185
180
|
"""
|
186
|
-
return dict(
|
187
|
-
can_view_common_tab=True,
|
188
|
-
can_view_packages_tab=True,
|
189
|
-
can_view_postgresql_ext_tab=True
|
190
|
-
)
|
181
|
+
return dict(can_view_common_tab=True, can_view_packages_tab=True, can_view_postgresql_ext_tab=True)
|
191
182
|
|
192
183
|
|
193
184
|
class AboutWindowAction(BaseWindowAction):
|
194
|
-
|
195
185
|
"""Экшен окна 'Информация о системе'."""
|
196
186
|
|
197
187
|
def create_window(self):
|
@@ -199,17 +189,13 @@ class AboutWindowAction(BaseWindowAction):
|
|
199
189
|
|
200
190
|
def set_window_params(self):
|
201
191
|
self.win_params['data'] = dict(
|
202
|
-
version=get_build_info(self.parent.get_version_config_path()),
|
203
|
-
project_title=self.parent.project_title
|
192
|
+
version=get_build_info(self.parent.get_version_config_path()), project_title=self.parent.project_title
|
204
193
|
)
|
205
194
|
self.win_params['version_info'] = (
|
206
195
|
(1, 'OC', get_os_version()),
|
207
196
|
(2, 'Python', python_version()),
|
208
|
-
(3, 'PostgreSQL', '.'.join(
|
209
|
-
map(str, get_postgresql_version(connection))
|
210
|
-
)),
|
197
|
+
(3, 'PostgreSQL', '.'.join(map(str, get_postgresql_version(connection)))),
|
211
198
|
)
|
212
|
-
tab_permissions = self.parent.get_tab_permissions(
|
213
|
-
self.request, self.context)
|
199
|
+
tab_permissions = self.parent.get_tab_permissions(self.request, self.context)
|
214
200
|
|
215
201
|
self.win_params.update(tab_permissions)
|
educommon/about/ui/ui.py
CHANGED
@@ -24,7 +24,6 @@ from educommon.utils.ui import (
|
|
24
24
|
|
25
25
|
|
26
26
|
class CommonTab(WindowTab):
|
27
|
-
|
28
27
|
"""Вкладка с общей информацией о системе."""
|
29
28
|
|
30
29
|
title = 'Общие сведения'
|
@@ -48,7 +47,6 @@ class CommonTab(WindowTab):
|
|
48
47
|
|
49
48
|
|
50
49
|
class PackagesTab(ObjectGridTab):
|
51
|
-
|
52
50
|
"""Вкладка со списком пакетов, установленных в системе."""
|
53
51
|
|
54
52
|
title = 'Версии ПО'
|
@@ -108,7 +106,6 @@ class PackagesTab(ObjectGridTab):
|
|
108
106
|
|
109
107
|
|
110
108
|
class PostgreSQLExtensionsTab(ObjectGridTab):
|
111
|
-
|
112
109
|
"""Вкладка со списком расширений PostgreSQL."""
|
113
110
|
|
114
111
|
title = 'Расширения БД'
|
@@ -127,7 +124,6 @@ class PostgreSQLExtensionsTab(ObjectGridTab):
|
|
127
124
|
|
128
125
|
|
129
126
|
class AboutWindow(TabbedWindow):
|
130
|
-
|
131
127
|
"""Окно 'Информация о системе'."""
|
132
128
|
|
133
129
|
tabs = (
|
@@ -151,14 +147,10 @@ class AboutWindow(TabbedWindow):
|
|
151
147
|
available_tab_titles.append(tab.title)
|
152
148
|
|
153
149
|
# удаление вкладок
|
154
|
-
self.tabs[:] = (
|
155
|
-
tab for tab in self.tabs
|
156
|
-
if tab.title in available_tab_titles
|
157
|
-
)
|
150
|
+
self.tabs[:] = (tab for tab in self.tabs if tab.title in available_tab_titles)
|
158
151
|
# удаление контейнеров вкладок
|
159
152
|
self._tab_container.items[:] = (
|
160
|
-
item for item in self._tab_container.items
|
161
|
-
if item.title in available_tab_titles
|
153
|
+
item for item in self._tab_container.items if item.title in available_tab_titles
|
162
154
|
)
|
163
155
|
|
164
156
|
def set_params(self, params):
|
@@ -176,8 +168,7 @@ class AboutWindow(TabbedWindow):
|
|
176
168
|
if len(self._tab_container.items) > 1:
|
177
169
|
for tab in self._tab_container.items:
|
178
170
|
if hasattr(tab, 'grid'):
|
179
|
-
self.lazy_grids.setdefault(
|
180
|
-
tab.client_id, []).append(tab.grid.client_id)
|
171
|
+
self.lazy_grids.setdefault(tab.client_id, []).append(tab.grid.client_id)
|
181
172
|
tab.grid.store.auto_load = False
|
182
173
|
|
183
174
|
self.template_globals = local_template('about-window.js')
|
educommon/about/utils.py
CHANGED
@@ -14,12 +14,13 @@ def get_installed_distributions():
|
|
14
14
|
|
15
15
|
:rtype: list of :class:`~pkg_resources.Distribution`.
|
16
16
|
"""
|
17
|
-
stdlib_pkgs = (
|
17
|
+
stdlib_pkgs = (
|
18
|
+
'python',
|
19
|
+
'wsgiref',
|
20
|
+
'argparse',
|
21
|
+
)
|
18
22
|
|
19
23
|
# pylint: disable=not-an-iterable
|
20
24
|
for dist in working_set:
|
21
|
-
if (
|
22
|
-
dist.key not in stdlib_pkgs and
|
23
|
-
normcase(realpath(dist.location)).startswith(realpath(sys.prefix))
|
24
|
-
):
|
25
|
+
if dist.key not in stdlib_pkgs and normcase(realpath(dist.location)).startswith(realpath(sys.prefix)):
|
25
26
|
yield dist
|
educommon/async_task/__init__.py
CHANGED
educommon/async_task/actions.py
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
from functools import (
|
2
2
|
partial,
|
3
3
|
)
|
4
|
+
|
4
5
|
from typing import (
|
5
6
|
Any,
|
6
7
|
Dict,
|
7
8
|
List,
|
8
9
|
Optional,
|
10
|
+
TYPE_CHECKING,
|
9
11
|
)
|
10
12
|
|
11
13
|
from django.core.exceptions import (
|
@@ -44,6 +46,11 @@ from educommon.utils.ui import (
|
|
44
46
|
ChoicesFilter,
|
45
47
|
)
|
46
48
|
|
49
|
+
if TYPE_CHECKING:
|
50
|
+
from django.db.models.query import (
|
51
|
+
QuerySet,
|
52
|
+
)
|
53
|
+
|
47
54
|
|
48
55
|
class AsyncTaskPack(ObjectPack):
|
49
56
|
"""Пак асинхронных задач."""
|
@@ -79,7 +86,7 @@ class AsyncTaskPack(ObjectPack):
|
|
79
86
|
'header': 'Тип задачи',
|
80
87
|
'width': 5,
|
81
88
|
'sortable': True,
|
82
|
-
'sort_fields': ('task_type_id',
|
89
|
+
'sort_fields': ('task_type_id',),
|
83
90
|
'filter': ChoicesFilter(
|
84
91
|
choices=[(value.key, value.title) for value in AsyncTaskType.get_model_enum_values()],
|
85
92
|
parser=str,
|
@@ -103,7 +110,7 @@ class AsyncTaskPack(ObjectPack):
|
|
103
110
|
'header': 'Статус задачи',
|
104
111
|
'width': 5,
|
105
112
|
'sortable': True,
|
106
|
-
'sort_fields': ('status_id',
|
113
|
+
'sort_fields': ('status_id',),
|
107
114
|
'filter': ChoicesFilter(
|
108
115
|
choices=[(value.key, value.title) for value in AsyncTaskStatus.get_model_enum_values()],
|
109
116
|
parser=str,
|
@@ -117,10 +124,12 @@ class AsyncTaskPack(ObjectPack):
|
|
117
124
|
|
118
125
|
self.view_result_action = ViewResultWindowAction()
|
119
126
|
self.revoke_task_action = RevokeAsyncTaskAction()
|
120
|
-
self.actions.extend(
|
121
|
-
|
122
|
-
|
123
|
-
|
127
|
+
self.actions.extend(
|
128
|
+
(
|
129
|
+
self.view_result_action,
|
130
|
+
self.revoke_task_action,
|
131
|
+
)
|
132
|
+
)
|
124
133
|
|
125
134
|
def declare_context(self, action):
|
126
135
|
"""Декларация контекста."""
|
@@ -192,23 +201,19 @@ class RevokeAsyncTaskAction(BaseAction):
|
|
192
201
|
"""Выполнение экшена."""
|
193
202
|
running_tasks = self._get_running_tasks(context.async_task_ids.split(','))
|
194
203
|
task_id_to_status = {task.id: task.status for task in running_tasks.only('id', 'status')}
|
195
|
-
all_tasks_are_cancellable = all(
|
196
|
-
AsyncTaskStatus.is_cancellable(status) for status in task_id_to_status.values()
|
197
|
-
)
|
204
|
+
all_tasks_are_cancellable = all(AsyncTaskStatus.is_cancellable(status) for status in task_id_to_status.values())
|
198
205
|
if not all_tasks_are_cancellable:
|
199
206
|
return OperationResult(success=False, message='Необходимо выбрать только те задачи, которые запущены!')
|
200
207
|
|
201
208
|
to_revoke_task_ids = [
|
202
|
-
str(task_id)
|
203
|
-
for task_id, status in task_id_to_status.items()
|
204
|
-
if AsyncTaskStatus.is_cancellable(status)
|
209
|
+
str(task_id) for task_id, status in task_id_to_status.items() if AsyncTaskStatus.is_cancellable(status)
|
205
210
|
]
|
206
211
|
|
207
212
|
revoke_async_tasks(to_revoke_task_ids)
|
208
213
|
|
209
214
|
return OperationResult()
|
210
215
|
|
211
|
-
def _get_running_tasks(self, ids: List[str]) -> 'QuerySet':
|
216
|
+
def _get_running_tasks(self, ids: List[str]) -> 'QuerySet[RunningTask]':
|
212
217
|
"""Возвращает кварисет асинхронных задач."""
|
213
218
|
try:
|
214
219
|
running_task_qs = self.parent.model.objects.filter(id__in=ids)
|
educommon/async_task/apps.py
CHANGED
educommon/async_task/locker.py
CHANGED
@@ -54,17 +54,14 @@ class TaskLocker:
|
|
54
54
|
def lock_id(self) -> str:
|
55
55
|
"""Ключ в кэше."""
|
56
56
|
return self.lock_id_format.format(
|
57
|
-
task_name=self.task_name,
|
58
|
-
params='&'.join(f'{k}={v}' for k, v in self.params.items())
|
57
|
+
task_name=self.task_name, params='&'.join(f'{k}={v}' for k, v in self.params.items())
|
59
58
|
)
|
60
59
|
|
61
60
|
def acquire_lock(self) -> str:
|
62
61
|
"""Установка блокировки."""
|
63
62
|
value = self.task_id or self.DEFAULT_LOCK_VALUE
|
64
63
|
cache.set(self.lock_id, value, self.expire_on)
|
65
|
-
self.debug(
|
66
|
-
f'Lock acquired for Task {self.task_name} ({self.params}) with value: {value}'
|
67
|
-
)
|
64
|
+
self.debug(f'Lock acquired for Task {self.task_name} ({self.params}) with value: {value}')
|
68
65
|
|
69
66
|
return self.lock_id
|
70
67
|
|
@@ -9,7 +9,6 @@ from django.db import (
|
|
9
9
|
|
10
10
|
|
11
11
|
class Migration(migrations.Migration):
|
12
|
-
|
13
12
|
initial = True
|
14
13
|
|
15
14
|
dependencies = [
|
@@ -21,7 +20,12 @@ class Migration(migrations.Migration):
|
|
21
20
|
name='AsyncTaskStatus',
|
22
21
|
fields=[
|
23
22
|
('title', models.TextField(verbose_name='расшифровка значения')),
|
24
|
-
(
|
23
|
+
(
|
24
|
+
'key',
|
25
|
+
models.CharField(
|
26
|
+
db_index=True, max_length=512, primary_key=True, serialize=False, verbose_name='ключ'
|
27
|
+
),
|
28
|
+
),
|
25
29
|
('order_number', models.PositiveIntegerField(default=100000, verbose_name='Порядковый номер')),
|
26
30
|
],
|
27
31
|
options={
|
@@ -32,7 +36,12 @@ class Migration(migrations.Migration):
|
|
32
36
|
name='AsyncTaskType',
|
33
37
|
fields=[
|
34
38
|
('title', models.TextField(verbose_name='расшифровка значения')),
|
35
|
-
(
|
39
|
+
(
|
40
|
+
'key',
|
41
|
+
models.CharField(
|
42
|
+
db_index=True, max_length=512, primary_key=True, serialize=False, verbose_name='ключ'
|
43
|
+
),
|
44
|
+
),
|
36
45
|
('order_number', models.PositiveIntegerField(default=100000, verbose_name='Порядковый номер')),
|
37
46
|
],
|
38
47
|
options={
|
@@ -47,13 +56,50 @@ class Migration(migrations.Migration):
|
|
47
56
|
('name', models.CharField(blank=True, max_length=512, verbose_name='Наименование задачи')),
|
48
57
|
('profile_id', models.PositiveIntegerField(blank=True, null=True)),
|
49
58
|
('description', models.CharField(blank=True, max_length=512, verbose_name='Описание задачи')),
|
50
|
-
(
|
51
|
-
|
59
|
+
(
|
60
|
+
'options',
|
61
|
+
django.contrib.postgres.fields.jsonb.JSONField(
|
62
|
+
blank=True, null=True, verbose_name='Дополнительные опции задачи'
|
63
|
+
),
|
64
|
+
),
|
65
|
+
(
|
66
|
+
'queued_at',
|
67
|
+
models.DateTimeField(
|
68
|
+
blank=True, db_index=True, null=True, verbose_name='Дата и время помещения в очередь'
|
69
|
+
),
|
70
|
+
),
|
52
71
|
('started_at', models.DateTimeField(blank=True, null=True, verbose_name='Дата и время запуска задачи')),
|
53
|
-
(
|
54
|
-
|
55
|
-
|
56
|
-
|
72
|
+
(
|
73
|
+
'finished_at',
|
74
|
+
models.DateTimeField(blank=True, null=True, verbose_name='Дата и время завершения задачи'),
|
75
|
+
),
|
76
|
+
(
|
77
|
+
'profile_type',
|
78
|
+
models.ForeignKey(
|
79
|
+
blank=True,
|
80
|
+
null=True,
|
81
|
+
on_delete=django.db.models.deletion.SET_NULL,
|
82
|
+
to='contenttypes.ContentType',
|
83
|
+
),
|
84
|
+
),
|
85
|
+
(
|
86
|
+
'status',
|
87
|
+
models.ForeignKey(
|
88
|
+
default='PENDING',
|
89
|
+
on_delete=django.db.models.deletion.PROTECT,
|
90
|
+
to='async_task.AsyncTaskStatus',
|
91
|
+
verbose_name='Состояние задачи',
|
92
|
+
),
|
93
|
+
),
|
94
|
+
(
|
95
|
+
'task_type',
|
96
|
+
models.ForeignKey(
|
97
|
+
default='UNKNOWN',
|
98
|
+
on_delete=django.db.models.deletion.PROTECT,
|
99
|
+
to='async_task.AsyncTaskType',
|
100
|
+
verbose_name='Тип задачи',
|
101
|
+
),
|
102
|
+
),
|
57
103
|
],
|
58
104
|
options={
|
59
105
|
'verbose_name': 'Асинхронная задача',
|
@@ -8,104 +8,109 @@ def init_task_types(apps, schema_editor):
|
|
8
8
|
"""Заполнение первоначальными данными AsyncTaskType."""
|
9
9
|
AsyncTaskType = apps.get_model('async_task', 'AsyncTaskType') # noqa: N806
|
10
10
|
|
11
|
-
AsyncTaskType.objects.bulk_create(
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
11
|
+
AsyncTaskType.objects.bulk_create(
|
12
|
+
[
|
13
|
+
AsyncTaskType(
|
14
|
+
key='UNKNOWN',
|
15
|
+
title='Неизвестно',
|
16
|
+
),
|
17
|
+
AsyncTaskType(
|
18
|
+
key='SYSTEM',
|
19
|
+
title='Системная',
|
20
|
+
),
|
21
|
+
AsyncTaskType(
|
22
|
+
key='REPORT',
|
23
|
+
title='Отчет',
|
24
|
+
),
|
25
|
+
AsyncTaskType(
|
26
|
+
key='IMPORT',
|
27
|
+
title='Импорт данных',
|
28
|
+
),
|
29
|
+
AsyncTaskType(
|
30
|
+
key='SCHEDULE_FORMING',
|
31
|
+
title='Формирование расписания',
|
32
|
+
),
|
33
|
+
AsyncTaskType(
|
34
|
+
key='SCHEDULE_CLEANER',
|
35
|
+
title='Очищение расписания',
|
36
|
+
),
|
37
|
+
AsyncTaskType(
|
38
|
+
key='SCHEDULE_PATTERN_COPY',
|
39
|
+
title='Копирование шаблона расписания',
|
40
|
+
),
|
41
|
+
AsyncTaskType(
|
42
|
+
key='ST_PRINTING',
|
43
|
+
title='Печать учебных планов',
|
44
|
+
),
|
45
|
+
AsyncTaskType(
|
46
|
+
key='ASYNC_REQUEST',
|
47
|
+
title='Отправка асинхронных запросов',
|
48
|
+
),
|
49
|
+
AsyncTaskType(
|
50
|
+
key='MASS_EXPULSION',
|
51
|
+
title='Массовый выпуск',
|
52
|
+
),
|
53
|
+
AsyncTaskType(
|
54
|
+
key='AIO_CLIENT',
|
55
|
+
title='AIO клиент',
|
56
|
+
),
|
57
|
+
],
|
58
|
+
ignore_conflicts=True,
|
59
|
+
)
|
57
60
|
|
58
61
|
|
59
62
|
def init_task_statuses(apps, schema_editor):
|
60
63
|
"""Заполнение первоначальными данными AsyncTaskStatus."""
|
61
64
|
AsyncTaskStatus = apps.get_model('async_task', 'AsyncTaskStatus') # noqa: N806
|
62
65
|
|
63
|
-
AsyncTaskStatus.objects.bulk_create(
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
66
|
+
AsyncTaskStatus.objects.bulk_create(
|
67
|
+
[
|
68
|
+
AsyncTaskStatus(
|
69
|
+
key='PENDING',
|
70
|
+
title='В ожидании',
|
71
|
+
),
|
72
|
+
AsyncTaskStatus(
|
73
|
+
key='RECEIVED',
|
74
|
+
title='В очереди',
|
75
|
+
),
|
76
|
+
AsyncTaskStatus(
|
77
|
+
key='STARTED',
|
78
|
+
title='Выполняется',
|
79
|
+
),
|
80
|
+
AsyncTaskStatus(
|
81
|
+
key='SUCCESS',
|
82
|
+
title='Успешно выполнена',
|
83
|
+
),
|
84
|
+
AsyncTaskStatus(
|
85
|
+
key='REVOKED',
|
86
|
+
title='Остановлена',
|
87
|
+
),
|
88
|
+
AsyncTaskStatus(
|
89
|
+
key='FAILURE',
|
90
|
+
title='Ошибка',
|
91
|
+
),
|
92
|
+
AsyncTaskStatus(
|
93
|
+
key='RETRY',
|
94
|
+
title='Перезапуск',
|
95
|
+
),
|
96
|
+
AsyncTaskStatus(
|
97
|
+
key='IGNORED',
|
98
|
+
title='Игнорирована',
|
99
|
+
),
|
100
|
+
AsyncTaskStatus(
|
101
|
+
key='REJECTED',
|
102
|
+
title='Отменена',
|
103
|
+
),
|
104
|
+
AsyncTaskStatus(
|
105
|
+
key='UNKNOWN',
|
106
|
+
title='Неизвестно',
|
107
|
+
),
|
108
|
+
],
|
109
|
+
ignore_conflicts=True,
|
110
|
+
)
|
105
111
|
|
106
112
|
|
107
113
|
class Migration(migrations.Migration):
|
108
|
-
|
109
114
|
dependencies = [
|
110
115
|
('async_task', '0001_initial'),
|
111
116
|
]
|
educommon/async_task/models.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
"""Модели для асинхронных задач Celery."""
|
2
|
+
|
2
3
|
from typing import (
|
3
4
|
Optional,
|
4
5
|
)
|
@@ -130,9 +131,13 @@ class AnnotatedRunningTaskManager(Manager):
|
|
130
131
|
|
131
132
|
def get_queryset(self):
|
132
133
|
"""Возвращает кварисет с аннотированием."""
|
133
|
-
return
|
134
|
-
|
135
|
-
|
134
|
+
return (
|
135
|
+
super()
|
136
|
+
.get_queryset()
|
137
|
+
.annotate(
|
138
|
+
task_type_str=F('task_type__title'),
|
139
|
+
status_str=F('status__title'),
|
140
|
+
)
|
136
141
|
)
|
137
142
|
|
138
143
|
|
@@ -221,8 +226,6 @@ class RunningTask(ModelValidationMixin, BaseObjectModel):
|
|
221
226
|
return getattr(self.user_profile, 'fullname', str(self.user_profile))
|
222
227
|
|
223
228
|
class Meta:
|
224
|
-
indexes = (
|
225
|
-
Index(fields=['profile_type', 'profile_id']),
|
226
|
-
)
|
229
|
+
indexes = (Index(fields=['profile_type', 'profile_id']),)
|
227
230
|
verbose_name = 'Асинхронная задача'
|
228
231
|
verbose_name_plural = 'Асинхронные задачи'
|