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.
Files changed (220) hide show
  1. educommon/__init__.py +0 -1
  2. educommon/about/ui/actions.py +16 -30
  3. educommon/about/ui/ui.py +3 -12
  4. educommon/about/utils.py +6 -5
  5. educommon/async_task/__init__.py +0 -1
  6. educommon/async_task/actions.py +18 -13
  7. educommon/async_task/apps.py +4 -0
  8. educommon/async_task/locker.py +2 -5
  9. educommon/async_task/migrations/0001_initial.py +55 -9
  10. educommon/async_task/migrations/0002_task_type_and_status_data.py +94 -89
  11. educommon/async_task/migrations/0003_alter_runningtask_options.py +0 -1
  12. educommon/async_task/models.py +9 -6
  13. educommon/async_task/tasks.py +11 -7
  14. educommon/async_task/ui.py +16 -35
  15. educommon/async_tasks/__init__.py +0 -1
  16. educommon/async_tasks/apps.py +4 -0
  17. educommon/async_tasks/locks.py +11 -21
  18. educommon/async_tasks/migrations/0001_initial.py +68 -8
  19. educommon/async_tasks/migrations/0002_load_initial_data.py +0 -1
  20. educommon/async_tasks/models.py +9 -29
  21. educommon/async_tasks/tasks.py +25 -54
  22. educommon/audit_log/__init__.py +1 -0
  23. educommon/audit_log/actions.py +27 -36
  24. educommon/audit_log/app_meta.py +7 -4
  25. educommon/audit_log/apps.py +44 -29
  26. educommon/audit_log/constants.py +7 -4
  27. educommon/audit_log/error_log/actions.py +1 -3
  28. educommon/audit_log/helpers.py +2 -4
  29. educommon/audit_log/management/commands/reinstall_audit_log.py +11 -7
  30. educommon/audit_log/migrations/0001_initial.py +91 -16
  31. educommon/audit_log/migrations/0002_install_audit_log.py +13 -13
  32. educommon/audit_log/migrations/0003_logproxy.py +1 -3
  33. educommon/audit_log/migrations/0004_reinstall_audit_log.py +1 -4
  34. educommon/audit_log/migrations/0005_postgresql_error.py +4 -2
  35. educommon/audit_log/migrations/0006_auto_20200806_1707.py +3 -4
  36. educommon/audit_log/migrations/0007_create_selective_tables_function.py +8 -5
  37. educommon/audit_log/migrations/0008_table_logged.py +0 -1
  38. educommon/audit_log/migrations/0009_reinstall_audit_log.py +0 -1
  39. educommon/audit_log/models.py +36 -42
  40. educommon/audit_log/permissions.py +11 -9
  41. educommon/audit_log/proxies.py +12 -23
  42. educommon/audit_log/ui.py +18 -15
  43. educommon/audit_log/utils/__init__.py +28 -60
  44. educommon/audit_log/utils/operations.py +16 -2
  45. educommon/auth/__init__.py +0 -3
  46. educommon/auth/rbac/__init__.py +2 -4
  47. educommon/auth/rbac/actions.py +148 -145
  48. educommon/auth/rbac/app_meta.py +9 -6
  49. educommon/auth/rbac/backends/base.py +2 -8
  50. educommon/auth/rbac/backends/caching.py +27 -37
  51. educommon/auth/rbac/backends/simple.py +1 -4
  52. educommon/auth/rbac/checker.py +1 -3
  53. educommon/auth/rbac/management/commands/rbac.py +6 -11
  54. educommon/auth/rbac/manager.py +18 -47
  55. educommon/auth/rbac/migrations/0001_initial.py +73 -12
  56. educommon/auth/rbac/migrations/0002_model_modifier_metaclass_fix.py +7 -6
  57. educommon/auth/rbac/migrations/0003_permission_hidden.py +1 -5
  58. educommon/auth/rbac/migrations/0004_auto_20171024_1245.py +26 -19
  59. educommon/auth/rbac/models.py +63 -68
  60. educommon/auth/rbac/permissions.py +6 -7
  61. educommon/auth/rbac/ui.py +83 -84
  62. educommon/auth/rbac/utils.py +10 -11
  63. educommon/auth/rbac/validators.py +4 -5
  64. educommon/auth/simple_auth/__init__.py +1 -5
  65. educommon/auth/simple_auth/actions.py +79 -92
  66. educommon/auth/simple_auth/app_meta.py +2 -9
  67. educommon/auth/simple_auth/checkers.py +3 -3
  68. educommon/auth/simple_auth/migrations/0001_initial.py +23 -4
  69. educommon/auth/simple_auth/validators.py +0 -1
  70. educommon/contingent/actions.py +7 -7
  71. educommon/contingent/app_meta.py +1 -4
  72. educommon/contingent/base.py +10 -15
  73. educommon/contingent/catalogs.py +424 -540
  74. educommon/contingent/contingent_plugin/actions.py +4 -15
  75. educommon/contingent/contingent_plugin/apps.py +10 -4
  76. educommon/contingent/contingent_plugin/migrations/0001_initial.py +5 -6
  77. educommon/contingent/contingent_plugin/migrations/0002_add_contingent_model_deleted.py +6 -11
  78. educommon/contingent/contingent_plugin/model_views.py +2 -12
  79. educommon/contingent/contingent_plugin/models.py +2 -7
  80. educommon/contingent/contingent_plugin/observer.py +14 -13
  81. educommon/contingent/contingent_plugin/plugin_meta.py +1 -3
  82. educommon/contingent/contingent_plugin/storage.py +8 -7
  83. educommon/contingent/contingent_plugin/utils.py +6 -6
  84. educommon/django/db/fields.py +72 -86
  85. educommon/django/db/migration/__init__.py +3 -7
  86. educommon/django/db/migration/operations.py +29 -51
  87. educommon/django/db/mixins/__init__.py +16 -10
  88. educommon/django/db/mixins/date_interval.py +47 -75
  89. educommon/django/db/mixins/validation.py +26 -26
  90. educommon/django/db/model_view/__init__.py +18 -22
  91. educommon/django/db/models.py +9 -8
  92. educommon/django/db/observer.py +9 -27
  93. educommon/django/db/partitioning/__init__.py +66 -92
  94. educommon/django/db/partitioning/management/commands/apply_partitioning.py +3 -13
  95. educommon/django/db/partitioning/management/commands/clear_table.py +18 -14
  96. educommon/django/db/partitioning/management/commands/split_table.py +18 -13
  97. educommon/django/db/routers.py +6 -15
  98. educommon/django/db/signals.py +4 -2
  99. educommon/django/db/utils.py +14 -19
  100. educommon/django/db/validators/__init__.py +1 -0
  101. educommon/django/db/validators/simple.py +72 -100
  102. educommon/django/storages/atcfs/api.py +39 -53
  103. educommon/django/storages/atcfs/app_meta.py +1 -1
  104. educommon/django/storages/atcfs/management/commands/atcfs_migrate.py +42 -55
  105. educommon/django/storages/atcfs/models.py +0 -3
  106. educommon/django/storages/atcfs/monkey_patching.py +18 -12
  107. educommon/django/storages/atcfs/storage.py +14 -23
  108. educommon/extjs/fields/input_params.py +15 -45
  109. educommon/importer/XLSReader.py +143 -241
  110. educommon/importer/__init__.py +86 -4
  111. educommon/importer/api.py +53 -84
  112. educommon/importer/constants.py +4 -14
  113. educommon/importer/loggers.py +16 -26
  114. educommon/importer/proxy.py +131 -176
  115. educommon/importer/proxy_import.py +11 -12
  116. educommon/importer/report.py +4 -6
  117. educommon/importer/ui.py +32 -26
  118. educommon/importer/validators.py +4 -7
  119. educommon/integration_entities/helpers.py +14 -18
  120. educommon/ioc/__init__.py +3 -6
  121. educommon/logger/loggers.py +10 -14
  122. educommon/m3/__init__.py +20 -38
  123. educommon/m3/extensions/__init__.py +1 -0
  124. educommon/m3/extensions/listeners/__init__.py +22 -38
  125. educommon/m3/extensions/listeners/delete_check/listeners.py +31 -41
  126. educommon/m3/extensions/listeners/delete_check/mixins.py +20 -25
  127. educommon/m3/extensions/listeners/delete_check/signals.py +2 -2
  128. educommon/m3/extensions/listeners/delete_check/ui.py +15 -14
  129. educommon/m3/extensions/listeners/delete_check/utils.py +9 -11
  130. educommon/m3/extensions/ui.py +15 -33
  131. educommon/m3/transaction_context.py +17 -19
  132. educommon/objectpack/actions.py +70 -88
  133. educommon/objectpack/apps.py +5 -0
  134. educommon/objectpack/filters.py +9 -15
  135. educommon/objectpack/ui.py +59 -77
  136. educommon/report/__init__.py +9 -5
  137. educommon/report/actions.py +29 -32
  138. educommon/report/constructor/__init__.py +5 -8
  139. educommon/report/constructor/app_meta.py +1 -3
  140. educommon/report/constructor/apps.py +1 -0
  141. educommon/report/constructor/base.py +33 -80
  142. educommon/report/constructor/builders/excel/_base.py +138 -286
  143. educommon/report/constructor/builders/excel/_header.py +2 -9
  144. educommon/report/constructor/builders/excel/product.py +13 -34
  145. educommon/report/constructor/builders/excel/with_merged_cells.py +18 -14
  146. educommon/report/constructor/config.py +2 -0
  147. educommon/report/constructor/editor/actions.py +101 -215
  148. educommon/report/constructor/editor/ui.py +71 -93
  149. educommon/report/constructor/exceptions.py +6 -12
  150. educommon/report/constructor/migrations/0001_initial.py +36 -44
  151. educommon/report/constructor/migrations/0002_report_filters.py +86 -72
  152. educommon/report/constructor/migrations/0003_reportfilter_exclude.py +5 -5
  153. educommon/report/constructor/migrations/0004_reportfilter_fields.py +22 -18
  154. educommon/report/constructor/migrations/0005_reportcolumn_visible.py +5 -4
  155. educommon/report/constructor/migrations/0006_reportsorting.py +21 -17
  156. educommon/report/constructor/migrations/0007_include_available_units.py +14 -14
  157. educommon/report/constructor/migrations/0008_auto_20170407_1318.py +4 -5
  158. educommon/report/constructor/migrations/0009_auto_20180405_0642.py +1 -4
  159. educommon/report/constructor/migrations/0010_add_aggregate_fields.py +7 -8
  160. educommon/report/constructor/mixins.py +14 -15
  161. educommon/report/constructor/models.py +76 -124
  162. educommon/report/constructor/utils.py +3 -8
  163. educommon/report/constructor/validators.py +1 -3
  164. educommon/report/reporter.py +25 -43
  165. educommon/report/utils.py +14 -40
  166. educommon/rest/actions.py +7 -11
  167. educommon/rest/context.py +6 -16
  168. educommon/rest/controllers.py +10 -10
  169. educommon/rest/mixins.py +29 -27
  170. educommon/secure_media/app_meta.py +9 -9
  171. educommon/utils/__init__.py +3 -2
  172. educommon/utils/caching.py +1 -3
  173. educommon/utils/conversion.py +1 -3
  174. educommon/utils/crypto.py +1 -2
  175. educommon/utils/date.py +13 -26
  176. educommon/utils/db/__init__.py +17 -26
  177. educommon/utils/db/postgresql.py +1 -4
  178. educommon/utils/fonts/__init__.py +3 -4
  179. educommon/utils/licence/__init__.py +5 -16
  180. educommon/utils/misc.py +9 -18
  181. educommon/utils/object_grid.py +55 -62
  182. educommon/utils/phone_number/modelfields.py +1 -3
  183. educommon/utils/phone_number/phone_number.py +5 -8
  184. educommon/utils/phone_number/validators.py +8 -23
  185. educommon/utils/plugins.py +15 -28
  186. educommon/utils/registry.py +2 -1
  187. educommon/utils/seqtools.py +1 -3
  188. educommon/utils/serializer.py +9 -16
  189. educommon/utils/storage.py +3 -2
  190. educommon/utils/system.py +1 -3
  191. educommon/utils/system_app/management/commands/delete_objects.py +17 -34
  192. educommon/utils/ui.py +87 -84
  193. educommon/utils/xml/__init__.py +2 -7
  194. educommon/utils/xml/resolver.py +1 -0
  195. educommon/ws_log/actions.py +31 -76
  196. educommon/ws_log/base.py +6 -20
  197. educommon/ws_log/migrations/0001_initial.py +25 -8
  198. educommon/ws_log/migrations/0002_auto_20160628_1334.py +0 -1
  199. educommon/ws_log/migrations/0003_add_fields_to_smev_logs.py +20 -4
  200. educommon/ws_log/migrations/0004_auto_20160727_1600.py +7 -6
  201. educommon/ws_log/migrations/0005_auto_20161130_1615.py +14 -4
  202. educommon/ws_log/migrations/0006_auto_20170327_1027.py +3 -2
  203. educommon/ws_log/migrations/0007_auto_20180607_1040.py +8 -9
  204. educommon/ws_log/migrations/0008_auto_20180713_1445.py +23 -10
  205. educommon/ws_log/migrations/0009_auto_20201130_1553.py +7 -2
  206. educommon/ws_log/models.py +21 -35
  207. educommon/ws_log/provider.py +2 -1
  208. educommon/ws_log/report.py +8 -13
  209. educommon/ws_log/smev/applications.py +12 -27
  210. educommon/ws_log/smev/exceptions.py +2 -3
  211. educommon/ws_log/ui.py +32 -32
  212. educommon/ws_log/utils.py +1 -3
  213. {educommon-3.13.0.dist-info → educommon-3.13.2.dist-info}/METADATA +26 -14
  214. educommon-3.13.2.dist-info/RECORD +354 -0
  215. educommon/utils/patches.py +0 -27
  216. educommon/version.conf +0 -11
  217. educommon-3.13.0.dist-info/RECORD +0 -357
  218. educommon-3.13.0.dist-info/dependency_links.txt +0 -1
  219. {educommon-3.13.0.dist-info → educommon-3.13.2.dist-info}/WHEEL +0 -0
  220. {educommon-3.13.0.dist-info → educommon-3.13.2.dist-info}/top_level.txt +0 -0
educommon/__init__.py CHANGED
@@ -1,6 +1,5 @@
1
1
  """Общая кодовая база для проектов БЦ Образование."""
2
2
 
3
-
4
3
  #: Неопределенное значение.
5
4
  #:
6
5
  #: Может использоваться в т.ч. как значение аргументов по умолчанию, когда есть
@@ -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
- self.packages_pack,
170
- self.postgresql_extensions_pack,
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 = ('python', 'wsgiref', 'argparse',)
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
@@ -1,4 +1,3 @@
1
1
  """Реестр "Асинхронные задачи"."""
2
2
 
3
-
4
3
  default_app_config = __name__ + '.apps.AppConfig'
@@ -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
- self.view_result_action,
122
- self.revoke_task_action,
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)
@@ -4,6 +4,10 @@ from django.apps import (
4
4
 
5
5
 
6
6
  class AppConfig(AppConfigBase):
7
+ """Конфигурация реестра "Асинхронные задачи".
8
+
9
+ Устанавливает имя и метку приложения для регистрации в системе Django.
10
+ """
7
11
 
8
12
  name = __package__
9
13
  label = 'async_task'
@@ -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
- ('key', models.CharField(db_index=True, max_length=512, primary_key=True, serialize=False, verbose_name='ключ')),
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
- ('key', models.CharField(db_index=True, max_length=512, primary_key=True, serialize=False, verbose_name='ключ')),
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
- ('options', django.contrib.postgres.fields.jsonb.JSONField(blank=True, null=True, verbose_name='Дополнительные опции задачи')),
51
- ('queued_at', models.DateTimeField(blank=True, db_index=True, null=True, verbose_name='Дата и время помещения в очередь')),
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
- ('finished_at', models.DateTimeField(blank=True, null=True, verbose_name='Дата и время завершения задачи')),
54
- ('profile_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='contenttypes.ContentType')),
55
- ('status', models.ForeignKey(default='PENDING', on_delete=django.db.models.deletion.PROTECT, to='async_task.AsyncTaskStatus', verbose_name='Состояние задачи')),
56
- ('task_type', models.ForeignKey(default='UNKNOWN', on_delete=django.db.models.deletion.PROTECT, to='async_task.AsyncTaskType', verbose_name='Тип задачи')),
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
- AsyncTaskType(
13
- key='UNKNOWN',
14
- title='Неизвестно',
15
- ),
16
- AsyncTaskType(
17
- key='SYSTEM',
18
- title='Системная',
19
- ),
20
- AsyncTaskType(
21
- key='REPORT',
22
- title='Отчет',
23
- ),
24
- AsyncTaskType(
25
- key='IMPORT',
26
- title='Импорт данных',
27
- ),
28
- AsyncTaskType(
29
- key='SCHEDULE_FORMING',
30
- title='Формирование расписания',
31
- ),
32
- AsyncTaskType(
33
- key='SCHEDULE_CLEANER',
34
- title='Очищение расписания',
35
- ),
36
- AsyncTaskType(
37
- key='SCHEDULE_PATTERN_COPY',
38
- title='Копирование шаблона расписания',
39
- ),
40
- AsyncTaskType(
41
- key='ST_PRINTING',
42
- title='Печать учебных планов',
43
- ),
44
- AsyncTaskType(
45
- key='ASYNC_REQUEST',
46
- title='Отправка асинхронных запросов',
47
- ),
48
- AsyncTaskType(
49
- key='MASS_EXPULSION',
50
- title='Массовый выпуск',
51
- ),
52
- AsyncTaskType(
53
- key='AIO_CLIENT',
54
- title='AIO клиент',
55
- ),
56
- ], ignore_conflicts=True)
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
- AsyncTaskStatus(
65
- key='PENDING',
66
- title='В ожидании',
67
- ),
68
- AsyncTaskStatus(
69
- key='RECEIVED',
70
- title='В очереди',
71
- ),
72
- AsyncTaskStatus(
73
- key='STARTED',
74
- title='Выполняется',
75
- ),
76
- AsyncTaskStatus(
77
- key='SUCCESS',
78
- title='Успешно выполнена',
79
- ),
80
- AsyncTaskStatus(
81
- key='REVOKED',
82
- title='Остановлена',
83
- ),
84
- AsyncTaskStatus(
85
- key='FAILURE',
86
- title='Ошибка',
87
- ),
88
- AsyncTaskStatus(
89
- key='RETRY',
90
- title='Перезапуск',
91
- ),
92
- AsyncTaskStatus(
93
- key='IGNORED',
94
- title='Игнорирована',
95
- ),
96
- AsyncTaskStatus(
97
- key='REJECTED',
98
- title='Отменена',
99
- ),
100
- AsyncTaskStatus(
101
- key='UNKNOWN',
102
- title='Неизвестно',
103
- ),
104
- ], ignore_conflicts=True)
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
  ]
@@ -4,7 +4,6 @@ from django.db import migrations, models
4
4
 
5
5
 
6
6
  class Migration(migrations.Migration):
7
-
8
7
  dependencies = [
9
8
  ('async_task', '0002_task_type_and_status_data'),
10
9
  ]
@@ -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 super().get_queryset().annotate(
134
- task_type_str=F('task_type__title'),
135
- status_str=F('status__title'),
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 = 'Асинхронные задачи'