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
@@ -1,4 +1,5 @@
1
1
  """Базовые классы для асинхронных задач."""
2
+
2
3
  import time
3
4
  from datetime import (
4
5
  datetime,
@@ -166,8 +167,14 @@ class AsyncTask(Task):
166
167
  kwargs['is_replica'] = True
167
168
 
168
169
  async_result = super().apply_async(
169
- args=args, kwargs=kwargs, task_id=task_id, producer=producer,
170
- link=link, link_error=link_error, shadow=shadow, **options
170
+ args=args,
171
+ kwargs=kwargs,
172
+ task_id=task_id,
173
+ producer=producer,
174
+ link=link,
175
+ link_error=link_error,
176
+ shadow=shadow,
177
+ **options,
171
178
  )
172
179
 
173
180
  self.debug(f'Task {self.__name__} (id = {task_id}) added')
@@ -190,10 +197,7 @@ class AsyncTask(Task):
190
197
  'exc_type': '',
191
198
  'exc_message': '',
192
199
  }
193
- self.update_state(
194
- state=AsyncTaskStatus.to_state(AsyncTaskStatus.STARTED),
195
- meta=self.state
196
- )
200
+ self.update_state(state=AsyncTaskStatus.to_state(AsyncTaskStatus.STARTED), meta=self.state)
197
201
  self.set_read_from_replica(kwargs.get('is_replica', False))
198
202
 
199
203
  self.debug(f'Task {self.__name__} (id = {self.request.id}) started')
@@ -358,7 +362,7 @@ class UniquePeriodicAsyncTask(PeriodicAsyncTask):
358
362
 
359
363
  @property
360
364
  def locker_config(self) -> dict:
361
- """Настройки для механизма блокировок. """
365
+ """Настройки для механизма блокировок."""
362
366
  return {
363
367
  'lock_params': {'task_name': self.name},
364
368
  'lock_message': f'Task [{self.__name__}] is running',
@@ -83,34 +83,19 @@ class AsyncTaskResultViewWindow(BaseWindow):
83
83
  """Инициализация элементов окна."""
84
84
  super()._init_components()
85
85
 
86
- self.top_region = ExtContainer(
87
- region='north', layout='form', height=80, style={'padding': '5px'}
88
- )
86
+ self.top_region = ExtContainer(region='north', layout='form', height=80, style={'padding': '5px'})
89
87
  self.center_region = ExtContainer(region='center', layout='fit', style={'padding': '5px'})
90
- self.bottom_region = ExtContainer(
91
- region='south',
92
- layout='form',
93
- height=150,
94
- style={'padding': '5px'}
95
- )
96
- self.task_type_fld = ExtStringField(
97
- anchor='100%', label='Тип', read_only=True)
98
- self.description_fld = ExtStringField(
99
- anchor='100%', label='Описание', read_only=True)
100
- self.progress_fld = ExtStringField(
101
- anchor='100%', label='Прогресс', read_only=True)
102
- self.error_fld = ExtTextArea(
103
- anchor='100%', label='Сообщение об ошибке', read_only=True)
104
- self.state_fld = ExtStringField(
105
- anchor='100%', label='Состояние задачи', read_only=True)
88
+ self.bottom_region = ExtContainer(region='south', layout='form', height=150, style={'padding': '5px'})
89
+ self.task_type_fld = ExtStringField(anchor='100%', label='Тип', read_only=True)
90
+ self.description_fld = ExtStringField(anchor='100%', label='Описание', read_only=True)
91
+ self.progress_fld = ExtStringField(anchor='100%', label='Прогресс', read_only=True)
92
+ self.error_fld = ExtTextArea(anchor='100%', label='Сообщение об ошибке', read_only=True)
93
+ self.state_fld = ExtStringField(anchor='100%', label='Состояние задачи', read_only=True)
106
94
  self.results_grid = ExtObjectGrid(
107
95
  title='Результаты',
108
96
  layout='fit',
109
97
  )
110
- self.close_btn = ExtButton(
111
- text='Закрыть',
112
- handler='function() {win.close()}'
113
- )
98
+ self.close_btn = ExtButton(text='Закрыть', handler='function() {win.close()}')
114
99
 
115
100
  # Кнопка "Отмена" не блокируется в режиме "только для чтения"
116
101
  self._mro_exclude_list.append(self.close_btn)
@@ -120,19 +105,15 @@ class AsyncTaskResultViewWindow(BaseWindow):
120
105
  super()._do_layout()
121
106
 
122
107
  self.layout = 'border'
123
- self.items[:] = [
124
- self.top_region,
125
- self.center_region,
126
- self.bottom_region
127
- ]
108
+ self.items[:] = [self.top_region, self.center_region, self.bottom_region]
128
109
  self.center_region.items.append(self.results_grid)
129
- self.bottom_region.items.extend([
130
- self.progress_fld, self.state_fld, self.error_fld])
131
- self.top_region.items.extend([
132
- self.task_type_fld, self.description_fld])
133
- self.buttons.extend([
134
- self.close_btn,
135
- ])
110
+ self.bottom_region.items.extend([self.progress_fld, self.state_fld, self.error_fld])
111
+ self.top_region.items.extend([self.task_type_fld, self.description_fld])
112
+ self.buttons.extend(
113
+ [
114
+ self.close_btn,
115
+ ]
116
+ )
136
117
 
137
118
  def _configure_results_grid(self, data: dict):
138
119
  """Конфигурирование грида с результатами задачи."""
@@ -1,4 +1,3 @@
1
1
  """Реестр "Асинхронные задачи"."""
2
2
 
3
-
4
3
  default_app_config = __name__ + '.apps.AppConfig'
@@ -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'
@@ -32,12 +32,13 @@ class TaskLocker:
32
32
  task_id = None
33
33
  expire_on = DEFAULT_LOCK_EXPIRE
34
34
 
35
- def __init__(self,
36
- task_name='',
37
- params={},
38
- task_id=None,
39
- expire_on=DEFAULT_LOCK_EXPIRE,
40
- ):
35
+ def __init__(
36
+ self,
37
+ task_name='',
38
+ params={},
39
+ task_id=None,
40
+ expire_on=DEFAULT_LOCK_EXPIRE,
41
+ ):
41
42
  self.task_name = task_name
42
43
  self.params = params
43
44
  self.task_id = task_id
@@ -50,21 +51,14 @@ class TaskLocker:
50
51
  @cached_property
51
52
  def lock_id(self) -> str:
52
53
  """Ключ в кэше."""
53
- str_params = ['%s=%s' % (k, v) for k, v in self.params.items()]
54
- return self.lock_id_format.format(
55
- task_name=self.task_name,
56
- params='&'.join(str_params)
57
- )
54
+ str_params = [f'{k}={v}' for k, v in self.params.items()]
55
+ return self.lock_id_format.format(task_name=self.task_name, params='&'.join(str_params))
58
56
 
59
57
  def acquire_lock(self) -> None:
60
58
  """Установка блокировки."""
61
59
  value = self.task_id or 'true'
62
60
  cache.set(self.lock_id, value, self.expire_on)
63
- self.debug(
64
- 'Lock acquired for Task %s (%s) with value: %s' % (
65
- self.task_name, self.params, value
66
- )
67
- )
61
+ self.debug(f'Lock acquired for Task {self.task_name} ({self.params}) with value: {value}')
68
62
 
69
63
  def delete_lock(self) -> None:
70
64
  """Удаление блокировки."""
@@ -104,9 +98,5 @@ class TaskLocker:
104
98
  :raises: educommon.async_task.exceptions.TaskUniqueException
105
99
  """
106
100
  if self.is_locked():
107
- self.debug(
108
- 'Add failed. Task %s currently locked (%s)' % (
109
- self.task_name, self.params
110
- )
111
- )
101
+ self.debug(f'Add failed. Task {self.task_name} currently locked ({self.params})')
112
102
  raise TaskUniqueException(message or self.DEFAULT_LOCK_MSG)
@@ -5,7 +5,6 @@ from django.db import (
5
5
 
6
6
 
7
7
  class Migration(migrations.Migration):
8
-
9
8
  dependencies = [
10
9
  ('contenttypes', '0001_initial'),
11
10
  ]
@@ -15,8 +14,21 @@ class Migration(migrations.Migration):
15
14
  name='AsyncTaskMeta',
16
15
  fields=[
17
16
  ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
18
- ('description', models.CharField(max_length=400, null=True, verbose_name='\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438', blank=True)),
19
- ('location', models.CharField(max_length=400, verbose_name='\u041f\u0443\u0442\u044c \u043a\u043b\u0430\u0441\u0441\u0430')),
17
+ (
18
+ 'description',
19
+ models.CharField(
20
+ max_length=400,
21
+ null=True,
22
+ verbose_name='\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438',
23
+ blank=True,
24
+ ),
25
+ ),
26
+ (
27
+ 'location',
28
+ models.CharField(
29
+ max_length=400, verbose_name='\u041f\u0443\u0442\u044c \u043a\u043b\u0430\u0441\u0441\u0430'
30
+ ),
31
+ ),
20
32
  ],
21
33
  options={
22
34
  'verbose_name': '\u0414\u0430\u043d\u043d\u044b\u0435 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438',
@@ -42,11 +54,59 @@ class Migration(migrations.Migration):
42
54
  ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
43
55
  ('object_id', models.PositiveIntegerField(null=True, blank=True)),
44
56
  ('task_id', models.CharField(max_length=36, verbose_name='ID \u0437\u0430\u0434\u0430\u0447\u0438')),
45
- ('status', models.SmallIntegerField(default=1, db_index=True, verbose_name='\u0421\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438', choices=[(1, '\u041d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u043e'), (2, '\u0412 \u043e\u0447\u0435\u0440\u0435\u0434\u0438'), (3, '\u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f'), (4, '\u0423\u0441\u043f\u0435\u0448\u043d\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430'), (5, '\u041e\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0430'), (6, '\u041e\u0448\u0438\u0431\u043a\u0430'), (7, '\u041f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a'), (8, '\u0418\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0430'), (9, '\u041e\u0442\u043c\u0435\u043d\u0435\u043d\u0430')])),
46
- ('queued_on', models.DateTimeField(null=True, verbose_name='\u0421\u0442\u0430\u0440\u0442 \u0437\u0430\u0434\u0430\u0447\u0438', db_index=True)),
47
- ('content_type', models.ForeignKey(blank=True, to='contenttypes.ContentType', null=True, on_delete=models.SET_NULL)),
48
- ('task_meta', models.ForeignKey(verbose_name='\u0414\u0430\u043d\u043d\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438', blank=True, to='async.AsyncTaskMeta', null=True, on_delete=models.SET_NULL)),
49
- ('task_type', models.ForeignKey(default=1, verbose_name='\u0422\u0438\u043f \u0437\u0430\u0434\u0430\u0447\u0438', to='async.AsyncTaskType', on_delete=models.CASCADE)),
57
+ (
58
+ 'status',
59
+ models.SmallIntegerField(
60
+ default=1,
61
+ db_index=True,
62
+ verbose_name='\u0421\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0438',
63
+ choices=[
64
+ (1, '\u041d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u043e'),
65
+ (2, '\u0412 \u043e\u0447\u0435\u0440\u0435\u0434\u0438'),
66
+ (3, '\u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f'),
67
+ (
68
+ 4,
69
+ '\u0423\u0441\u043f\u0435\u0448\u043d\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430',
70
+ ),
71
+ (5, '\u041e\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0430'),
72
+ (6, '\u041e\u0448\u0438\u0431\u043a\u0430'),
73
+ (7, '\u041f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a'),
74
+ (8, '\u0418\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0430'),
75
+ (9, '\u041e\u0442\u043c\u0435\u043d\u0435\u043d\u0430'),
76
+ ],
77
+ ),
78
+ ),
79
+ (
80
+ 'queued_on',
81
+ models.DateTimeField(
82
+ null=True,
83
+ verbose_name='\u0421\u0442\u0430\u0440\u0442 \u0437\u0430\u0434\u0430\u0447\u0438',
84
+ db_index=True,
85
+ ),
86
+ ),
87
+ (
88
+ 'content_type',
89
+ models.ForeignKey(blank=True, to='contenttypes.ContentType', null=True, on_delete=models.SET_NULL),
90
+ ),
91
+ (
92
+ 'task_meta',
93
+ models.ForeignKey(
94
+ verbose_name='\u0414\u0430\u043d\u043d\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438',
95
+ blank=True,
96
+ to='async.AsyncTaskMeta',
97
+ null=True,
98
+ on_delete=models.SET_NULL,
99
+ ),
100
+ ),
101
+ (
102
+ 'task_type',
103
+ models.ForeignKey(
104
+ default=1,
105
+ verbose_name='\u0422\u0438\u043f \u0437\u0430\u0434\u0430\u0447\u0438',
106
+ to='async.AsyncTaskType',
107
+ on_delete=models.CASCADE,
108
+ ),
109
+ ),
50
110
  ],
51
111
  options={
52
112
  'verbose_name': '\u0410\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430',
@@ -14,7 +14,6 @@ APP_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
14
14
 
15
15
 
16
16
  class Migration(migrations.Migration):
17
-
18
17
  dependencies = [
19
18
  ('async', '0001_initial'),
20
19
  ]
@@ -1,4 +1,5 @@
1
1
  """Модели для асинхронных задач Celery."""
2
+
2
3
  from django.contrib.contenttypes.models import (
3
4
  ContentType,
4
5
  )
@@ -40,19 +41,11 @@ class AsyncTaskType(ModelValidationMixin, BaseObjectModel):
40
41
  class AsyncTaskMeta(ModelValidationMixin, BaseObjectModel):
41
42
  """Модель данных асинхронных задач."""
42
43
 
43
- description = models.CharField(
44
- max_length=400,
45
- verbose_name='Описание задачи',
46
- null=True,
47
- blank=True
48
- )
44
+ description = models.CharField(max_length=400, verbose_name='Описание задачи', null=True, blank=True)
49
45
 
50
46
  # путь класса от корня проекта, содержит имя модуля и название класса
51
47
  # пример для ЭДО: 'extedu.person.merge.tasks.MergeTask'
52
- location = models.CharField(
53
- max_length=400,
54
- verbose_name='Путь класса'
55
- )
48
+ location = models.CharField(max_length=400, verbose_name='Путь класса')
56
49
 
57
50
  class Meta:
58
51
  verbose_name = 'Данные асинхронной задачи'
@@ -67,12 +60,9 @@ class RunningTask(ModelValidationMixin, BaseObjectModel):
67
60
  # отображение пользователя, если пользователь не задан
68
61
  DEFAULT_USER = 'Система'
69
62
 
70
- MSG_TASK_NOT_FOUND = (
71
- 'Информации не найдено! Возможно задача была удалена!')
63
+ MSG_TASK_NOT_FOUND = 'Информации не найдено! Возможно задача была удалена!'
72
64
 
73
- content_type = models.ForeignKey(
74
- ContentType, null=True, blank=True, on_delete=models.SET_NULL
75
- )
65
+ content_type = models.ForeignKey(ContentType, null=True, blank=True, on_delete=models.SET_NULL)
76
66
  object_id = models.PositiveIntegerField(null=True, blank=True)
77
67
  user = GenericForeignKey()
78
68
  task_id = models.CharField(
@@ -80,28 +70,18 @@ class RunningTask(ModelValidationMixin, BaseObjectModel):
80
70
  verbose_name='ID задачи',
81
71
  )
82
72
  task_type = models.ForeignKey(
83
- AsyncTaskType,
84
- default=AsyncTaskType.TASK_UNKNOWN,
85
- verbose_name='Тип задачи',
86
- on_delete=models.CASCADE
73
+ AsyncTaskType, default=AsyncTaskType.TASK_UNKNOWN, verbose_name='Тип задачи', on_delete=models.CASCADE
87
74
  )
88
75
  task_meta = models.ForeignKey(
89
- AsyncTaskMeta,
90
- verbose_name='Данные задачи',
91
- null=True,
92
- blank=True,
93
- on_delete=models.SET_NULL
76
+ AsyncTaskMeta, verbose_name='Данные задачи', null=True, blank=True, on_delete=models.SET_NULL
94
77
  )
95
78
  status = models.SmallIntegerField(
96
- choices=statuses.STATUS_CHOICES,
97
- default=statuses.STATUS_PENDING,
98
- verbose_name='Состояние задачи',
99
- db_index=True
79
+ choices=statuses.STATUS_CHOICES, default=statuses.STATUS_PENDING, verbose_name='Состояние задачи', db_index=True
100
80
  )
101
81
  queued_on = models.DateTimeField(
102
82
  verbose_name='Старт задачи',
103
83
  db_index=True,
104
- null=True # null - если задача ещё не в работе
84
+ null=True, # null - если задача ещё не в работе
105
85
  )
106
86
 
107
87
  class Meta:
@@ -1,4 +1,5 @@
1
1
  """Базовые классы для асинхронных задач."""
2
+
2
3
  import logging
3
4
  from collections import (
4
5
  OrderedDict,
@@ -39,8 +40,7 @@ class AsyncTask(Task):
39
40
  logger = logging.getLogger('educommon.async')
40
41
  logger.debug(*args, **kwargs)
41
42
 
42
- def apply_async(self, args=None, kwargs=None, task_id=None, producer=None,
43
- link=None, link_error=None, **options):
43
+ def apply_async(self, args=None, kwargs=None, task_id=None, producer=None, link=None, link_error=None, **options):
44
44
  """Постановка задачи в очередь.
45
45
 
46
46
  автор задачи задаётся снаружи через 2 поля в словаре kwargs:
@@ -49,35 +49,27 @@ class AsyncTask(Task):
49
49
  if kwargs is None:
50
50
  kwargs = {}
51
51
 
52
- async_result = super(AsyncTask, self).apply_async(
53
- args=args, kwargs=kwargs, task_id=task_id, producer=producer,
54
- link=link, link_error=link_error, **options
52
+ async_result = super().apply_async(
53
+ args=args, kwargs=kwargs, task_id=task_id, producer=producer, link=link, link_error=link_error, **options
55
54
  )
56
55
 
57
56
  location = self.__class__.__module__ + '.' + self.__class__.__name__
58
- task_meta = models.AsyncTaskMeta.objects.create(
59
- location=location,
60
- description=self.description
61
- )
57
+ task_meta = models.AsyncTaskMeta.objects.create(location=location, description=self.description)
62
58
  params = dict(
63
59
  task_meta=task_meta,
64
60
  status=statuses.STATUS_RECEIVED,
65
61
  object_id=kwargs.get('object_id'),
66
62
  content_type=kwargs.get('content_type'),
67
- task_type_id=kwargs.get(
68
- 'task_type', models.AsyncTaskType.TASK_UNKNOWN
69
- ),
63
+ task_type_id=kwargs.get('task_type', models.AsyncTaskType.TASK_UNKNOWN),
70
64
  # время начала задачи
71
65
  # для случая, когда celery успевает начать задачу ещё до
72
66
  # коммита в БД создаваемой здесь models.RunningTask
73
- queued_on=datetime.now()
67
+ queued_on=datetime.now(),
74
68
  )
75
69
 
76
- models.RunningTask.objects.get_or_create(
77
- task_id=async_result.task_id, defaults=params
78
- )
70
+ models.RunningTask.objects.get_or_create(task_id=async_result.task_id, defaults=params)
79
71
 
80
- self.debug("Task %s added" % self.__name__)
72
+ self.debug(f'Task {self.__name__} added')
81
73
 
82
74
  return async_result
83
75
 
@@ -90,23 +82,19 @@ class AsyncTask(Task):
90
82
  # описание результата
91
83
  'description': self.description,
92
84
  # прогресс выполнения задачи
93
- 'progress': 'Неизвестно'
85
+ 'progress': 'Неизвестно',
94
86
  }
95
- self.update_state(
96
- state=statuses.get_state_str(statuses.STATUS_STARTED),
97
- meta=self.state
98
- )
99
- self.debug("Task %s run (task_id = %s )" % (
100
- self.__name__, self.request.id))
87
+ self.update_state(state=statuses.get_state_str(statuses.STATUS_STARTED), meta=self.state)
88
+ self.debug(f'Task {self.__name__} run (task_id = {self.request.id} )')
89
+
101
90
  return {}
102
91
 
103
92
  def after_return(self, status, retval, task_id, args, kwargs, einfo):
104
93
  """Завершение задачи."""
105
- self.debug("Task %s completed" % self.__name__)
94
+ self.debug(f'Task {self.__name__} completed')
106
95
 
107
96
  if isinstance(retval, dict):
108
- self.update_state(
109
- state=retval.get('task_state', status), meta=retval)
97
+ self.update_state(state=retval.get('task_state', status), meta=retval)
110
98
  else:
111
99
  self.update_state(state=status, meta=retval)
112
100
 
@@ -120,19 +108,15 @@ class AsyncTask(Task):
120
108
  if task_id is None:
121
109
  task_id = self.request.id
122
110
 
123
- running_task = models.RunningTask.objects.filter(
124
- task_id=task_id
125
- ).order_by('queued_on').first()
111
+ running_task = models.RunningTask.objects.filter(task_id=task_id).order_by('queued_on').first()
126
112
 
127
113
  if not running_task:
128
114
  return
129
115
 
130
116
  if state == states.SUCCESS:
131
- self.state['values']['Время выполения'] = (
132
- datetime.now() - running_task.queued_on)
117
+ self.state['values']['Время выполения'] = datetime.now() - running_task.queued_on
133
118
 
134
- super(AsyncTask, self).update_state(
135
- task_id=task_id, state=state, meta=meta)
119
+ super().update_state(task_id=task_id, state=state, meta=meta)
136
120
 
137
121
  running_task.status = statuses.get_status_idx(state)
138
122
 
@@ -141,8 +125,7 @@ class AsyncTask(Task):
141
125
  running_task.queued_on = datetime.now()
142
126
  running_task.clean_and_save()
143
127
 
144
- def set_progress(self, task_id=None, task_state=states.STARTED,
145
- progress=None, values=None):
128
+ def set_progress(self, task_id=None, task_state=states.STARTED, progress=None, values=None):
146
129
  """Обновление состояния выполнения задачи.
147
130
 
148
131
  :param str task_id: id задачи celery
@@ -236,11 +219,12 @@ class LockableAsyncTask(AsyncTask):
236
219
  # UUID задачи
237
220
  task_id,
238
221
  # время жизни
239
- lock_expire
222
+ lock_expire,
240
223
  )
241
224
  locker.raise_if_locked(message)
242
225
 
243
226
  locker.acquire_lock()
227
+
244
228
  return locker.lock_id
245
229
 
246
230
  def _validate_lock_data(self, lock_data):
@@ -250,16 +234,7 @@ class LockableAsyncTask(AsyncTask):
250
234
  """
251
235
  pass
252
236
 
253
- def apply_async(
254
- self,
255
- args=None,
256
- kwargs={},
257
- task_id=None,
258
- producer=None,
259
- link=None,
260
- link_error=None,
261
- **options
262
- ):
237
+ def apply_async(self, args=None, kwargs={}, task_id=None, producer=None, link=None, link_error=None, **options):
263
238
  """Постановка в очередь.
264
239
 
265
240
  Для задач, реализующих уникальность, метод выбрасывает исключение.
@@ -279,21 +254,17 @@ class LockableAsyncTask(AsyncTask):
279
254
  kwargs.update(lock_id=lock_id)
280
255
 
281
256
  async_result = super().apply_async(
282
- args=args, kwargs=kwargs, task_id=task_id, producer=producer,
283
- link=link, link_error=link_error, **options
257
+ args=args, kwargs=kwargs, task_id=task_id, producer=producer, link=link, link_error=link_error, **options
284
258
  )
285
259
 
286
260
  return async_result
287
261
 
288
262
  def after_return(self, status, retval, task_id, args, kwargs, einfo):
289
263
  """Завершение задачи"""
290
- super().after_return(
291
- status, retval, task_id, args, kwargs, einfo
292
- )
264
+ super().after_return(status, retval, task_id, args, kwargs, einfo)
293
265
  # снимаем блокировку
294
266
  lock_id = kwargs.get('lock_id')
295
267
  if lock_id:
296
268
  # задача завершена, убрать лок
297
- self.debug("Unlock task %s lock_id = %s" % (
298
- self.__name__, lock_id))
269
+ self.debug(f'Unlock task {self.__name__} lock_id = {lock_id}')
299
270
  self._locker_class.delete_lock_by_id(lock_id)
@@ -47,6 +47,7 @@
47
47
  - __str__()
48
48
  Для каждой модели проекта желательно добавить/изменить один из данных методов.
49
49
  """
50
+
50
51
  import django
51
52
 
52
53