educommon 3.12.0__py3-none-any.whl → 3.13.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (221) 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 +149 -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.2.dist-info/METADATA +57 -0
  214. educommon-3.13.2.dist-info/RECORD +354 -0
  215. {educommon-3.12.0.dist-info → educommon-3.13.2.dist-info}/WHEEL +1 -1
  216. educommon/utils/patches.py +0 -27
  217. educommon/version.conf +0 -11
  218. educommon-3.12.0.dist-info/METADATA +0 -47
  219. educommon-3.12.0.dist-info/RECORD +0 -357
  220. educommon-3.12.0.dist-info/dependency_links.txt +0 -1
  221. {educommon-3.12.0.dist-info → educommon-3.13.2.dist-info}/top_level.txt +0 -0
@@ -25,7 +25,6 @@ from educommon.m3 import (
25
25
 
26
26
 
27
27
  class SafeDict(dict):
28
-
29
28
  @classmethod
30
29
  def _wrap(cls, value):
31
30
  if not value:
@@ -51,14 +50,12 @@ class SafeDict(dict):
51
50
 
52
51
  def pop(self, *args, **kwargs):
53
52
  try:
54
- return self._wrap(
55
- dict.pop(self, *args, **kwargs)
56
- )
53
+ return self._wrap(dict.pop(self, *args, **kwargs))
57
54
  except KeyError:
58
55
  return None
59
56
 
60
57
  def copy(self):
61
- return self._wrap(super(SafeDict, self).copy())
58
+ return self._wrap(super().copy())
62
59
 
63
60
 
64
61
  class ProxySaveError(Exception):
@@ -78,9 +75,8 @@ class ProxyWarning(Exception):
78
75
 
79
76
 
80
77
  class _ImportProxy:
81
- """
82
- Прототип proxy для загрузки данных
83
- """
78
+ """Прототип proxy для загрузки данных."""
79
+
84
80
  # описание ячеек
85
81
  cells_config = {
86
82
  # 'key': cell
@@ -106,9 +102,7 @@ class _ImportProxy:
106
102
 
107
103
  @classmethod
108
104
  def __make_config(cls, headers):
109
- """
110
- Создание конфигурации колонок для XLSLoader`а
111
- """
105
+ """Создание конфигурации колонок для XLSLoader`а."""
112
106
  if headers:
113
107
  if isinstance(headers, str):
114
108
  # "шапки" могут быть переданы в виде форматирующей строки
@@ -126,7 +120,7 @@ class _ImportProxy:
126
120
  def add_headers(src, path=''):
127
121
  result = {}
128
122
  for key, val in src.items():
129
- path_w_key = ('%s__%s' % (path, key)) if path else key
123
+ path_w_key = f'{path}__{key}' if path else key
130
124
  try:
131
125
  val = add_headers(val, path_w_key)
132
126
  except AttributeError:
@@ -137,35 +131,33 @@ class _ImportProxy:
137
131
  return add_headers(cls.cells_config)
138
132
 
139
133
  def __init__(self, key, context):
140
- """
141
- Проверка зависимостей.
134
+ """Проверка зависимостей.
135
+
142
136
  Если контекст не содержит требуемых данных, должно
143
- возбуждаться исключение AssertionError
137
+ возбуждаться исключение AssertionError.
144
138
  """
145
139
  assert key
146
140
  self._key = key
147
141
  self._context = context
148
142
 
149
143
  def _load(self, data):
150
- """
151
- Загрузка данных из словаря data
144
+ """Загрузка данных из словаря data.
145
+
152
146
  Допустимое исключение - ProxySaveError.
153
147
  """
154
148
  raise NotImplementedError()
155
149
 
156
150
 
157
151
  class ModelProxy(_ImportProxy):
158
- """
159
- Обёртка для импорта модели
160
- """
152
+ """Обёртка для импорта модели."""
153
+
161
154
  # модель
162
155
  model = None
163
156
 
164
157
 
165
158
  class CacheProxy(_ImportProxy):
166
- """
167
- Обёртка для кэша объектов
168
- """
159
+ """Обёртка для кэша объектов."""
160
+
169
161
  model = None
170
162
 
171
163
  # если задано сообщение, оно будет выведено в случае
@@ -177,20 +169,18 @@ class CacheProxy(_ImportProxy):
177
169
  msg_multiple = None
178
170
 
179
171
  def __init__(self, key, context):
180
- super(CacheProxy, self).__init__(key, context)
181
- self._key = '%s_cache' % self._key
172
+ super().__init__(key, context)
173
+
174
+ self._key = f'{self._key}_cache'
182
175
  self._cache = context.setdefault(self._key, {})
183
176
 
184
177
  def _make_identity(self, data):
185
- """
186
- Преобразование данных в параметры для get()
187
- """
178
+ """Преобразование данных в параметры для get()."""
188
179
  return data
189
180
 
190
181
  def _load(self, data):
191
- """
192
- Возврат объекта из кэша, а если его там нет, то из базы
193
- """
182
+ """Возврат объекта из кэша, а если его там нет, то из базы."""
183
+
194
184
  def wrap_to_q(x):
195
185
  if isinstance(x, dict):
196
186
  return Q(**x)
@@ -199,9 +189,7 @@ class CacheProxy(_ImportProxy):
199
189
 
200
190
  def default():
201
191
  try:
202
- obj = self.model.objects.get(
203
- wrap_to_q(self._make_identity(data))
204
- )
192
+ obj = self.model.objects.get(wrap_to_q(self._make_identity(data)))
205
193
  obj._imported_object = True
206
194
  return obj
207
195
 
@@ -224,54 +212,55 @@ class CacheProxy(_ImportProxy):
224
212
 
225
213
 
226
214
  class ContextAdapterProxy(_ImportProxy):
227
- """
228
- Прокси для преобразования контекста.
229
- """
215
+ """Прокси для преобразования контекста."""
216
+
230
217
  @classmethod
231
218
  def _must_be_executed_anyway(cls, is_optional=False):
232
- """
233
- Данный прокси вызывается всегда, т.к. обычне не требует данных
234
- """
219
+ """Данный прокси вызывается всегда, т.к. обычно не требует данных."""
235
220
  return True
236
221
 
237
222
  @classmethod
238
223
  def make_copier(cls, src_key):
224
+ """Конструирует простой копирующий адаптер.
225
+
226
+ При загрузке извлекает значение по ключу src_key из контекста и возвращает его.
239
227
  """
240
- Конструирует простой копирующий адаптер, который при загрузке просто
241
- извлекает значение по ключу src_key из контекста и возвращает его
242
- """
228
+
243
229
  class SimpleCopyingAdepter(cls):
244
230
  def _load(self, data):
245
231
  return self._context[src_key]
232
+
246
233
  return SimpleCopyingAdepter
247
234
 
248
235
  @classmethod
249
236
  def make_mover(cls, src_key):
250
- """
251
- Конструирует простой копирующий адаптер, который при загрузке просто
252
- извлекает значение по ключу src_key из контекста и возвращает его,
237
+ """Конструирует простой копирующий адаптер.
238
+
239
+ При загрузке извлекает значение по ключу src_key из контекста и возвращает его,
253
240
  а значение контекста по ключу заменяется на None.
254
241
  """
242
+
255
243
  class SimpleCopyingAdepter(cls):
256
244
  def _load(self, data):
257
245
  result = self._context[src_key]
258
246
  self._context[src_key] = None
259
247
  return result
248
+
260
249
  return SimpleCopyingAdepter
261
250
 
262
251
  @classmethod
263
252
  def make_setter(cls, value):
264
- """
265
- Конструирует адаптер, который просто возвращает указанное значение
266
- """
253
+ """Конструирует адаптер, который просто возвращает указанное значение."""
254
+
267
255
  class SimpleCopyingAdepter(cls):
268
256
  def _load(self, data):
269
257
  return value
258
+
270
259
  return SimpleCopyingAdepter
271
260
 
272
261
 
273
262
  class LayoutModelProxy(ModelProxy):
274
- """Прокси для загрузки области данных"""
263
+ """Прокси для загрузки области данных."""
275
264
 
276
265
  # словари будут сгенерированы в api
277
266
  cells_config, default_headers = None, None
@@ -293,27 +282,24 @@ class LayoutModelProxy(ModelProxy):
293
282
  # тип ячейки для этой области
294
283
  'cell_type': MaybeStringCell(),
295
284
  # отдельные прокси для каждого столбца области
296
- 'use_separated_proxy': True
285
+ 'use_separated_proxy': True,
297
286
  },
298
287
  }
299
288
 
300
289
  @staticmethod
301
290
  def create_header(proxy, context):
302
- """
303
- Построение кэша объектов, используемых в заголовках области
304
- Вызывается при добавлении в лоадер
291
+ """Построение кэша объектов, используемых в заголовках области.
292
+
293
+ Вызывается при добавлении в лоадер.
305
294
  """
306
295
  raise NotImplementedError()
307
296
 
308
297
 
309
298
  class MultiProxyLoader:
310
- """
311
- Прокси загрузки нескольких моделей
312
- """
299
+ """Прокси загрузки нескольких моделей."""
313
300
 
314
301
  # начальный контекст импорта
315
- initial_context = {
316
- }
302
+ initial_context = {}
317
303
 
318
304
  # proxy моделей в порядке приоритета
319
305
  proxies = [
@@ -347,17 +333,17 @@ class MultiProxyLoader:
347
333
 
348
334
  @classmethod
349
335
  def make_header_context(cls, header_data, context):
350
- """
351
- Преобразование данных шапки.
352
- При возвращении непустого списка ошибок - прекращение загрузки листа
336
+ """Преобразование данных шапки.
337
+
338
+ При возвращении непустого списка ошибок - прекращение загрузки листа.
353
339
  """
354
340
  header_context = {}
355
341
  errors = []
342
+
356
343
  return header_context, errors
357
344
 
358
345
  @classmethod
359
- def load_rows(cls, header_data, rows_data, parse_log, log, context,
360
- warning_log=None, result_logger=None):
346
+ def load_rows(cls, header_data, rows_data, parse_log, log, context, warning_log=None, result_logger=None):
361
347
  """Загрузка строк листа.
362
348
 
363
349
  Логи разделены с целью дать возможность потомку принять решение
@@ -381,9 +367,7 @@ class MultiProxyLoader:
381
367
  # обработчик данных шапки листа
382
368
  transaction.enter_transaction_management()
383
369
  try:
384
- header_context, errors = cls.make_header_context(
385
- header_data, context
386
- )
370
+ header_context, errors = cls.make_header_context(header_data, context)
387
371
 
388
372
  except Exception:
389
373
  transaction.rollback()
@@ -402,8 +386,7 @@ class MultiProxyLoader:
402
386
  # помеченных декоратором @delay_in_situations(delay_situation).
403
387
  # Определение ситуаций.
404
388
  if context:
405
- delay_situation = context.get(
406
- 'delay_situation', cls.default_delay_situation)
389
+ delay_situation = context.get('delay_situation', cls.default_delay_situation)
407
390
  else:
408
391
  delay_situation = cls.default_delay_situation
409
392
 
@@ -422,14 +405,11 @@ class MultiProxyLoader:
422
405
  try:
423
406
  errors, warnings = proxyloader.load(row)
424
407
  result_logger.on_row_processed(row[xls_pos])
425
- result_logger.on_row_errors(
426
- row[xls_pos], errors, warnings)
408
+ result_logger.on_row_errors(row[xls_pos], errors, warnings)
427
409
  except ProxyCriticalError as err:
428
- log.setdefault(row[xls_pos], []).extend(
429
- [str(err)])
410
+ log.setdefault(row[xls_pos], []).extend([str(err)])
430
411
  _errors.append(str(err))
431
- result_logger.on_row_errors(
432
- row[xls_pos], [str(err)])
412
+ result_logger.on_row_errors(row[xls_pos], [str(err)])
433
413
 
434
414
  # XXX при данном эксепшене нужно предотвратить
435
415
  # повторное выполнение load, поэтому откатываем
@@ -437,20 +417,15 @@ class MultiProxyLoader:
437
417
  transaction.savepoint_rollback(inner_t._sid)
438
418
  # откатываем внешний блок
439
419
  transaction.rollback(outer_t._using)
440
- transaction.leave_transaction_management(
441
- using=outer_t._using)
420
+ transaction.leave_transaction_management(using=outer_t._using)
442
421
  break
443
422
 
444
423
  if warnings:
445
424
  # предупреждения выводятся отдельно
446
425
  if warning_log is not None:
447
- warning_log.setdefault(
448
- row[xls_pos], []
449
- ).extend(warnings)
426
+ warning_log.setdefault(row[xls_pos], []).extend(warnings)
450
427
  else:
451
- log.setdefault(
452
- row[xls_pos], []
453
- ).extend(warnings)
428
+ log.setdefault(row[xls_pos], []).extend(warnings)
454
429
 
455
430
  # предусмотренные ошибки, а все непредвиденные всплывут
456
431
  if errors: # предвиденные складываем
@@ -459,21 +434,20 @@ class MultiProxyLoader:
459
434
  log.setdefault(row[xls_pos], []).extend(errors)
460
435
  # откатываем вложенную транзакцию (всю строку)
461
436
  # если это не загрузчик прокси с областями
462
- if not hasattr(
463
- proxyloader, 'layout_proxies_template'
464
- ):
437
+ if not hasattr(proxyloader, 'layout_proxies_template'):
465
438
  inner_t.rollback()
466
- result_logger.on_row_save_rollback(
467
- row[xls_pos])
439
+ result_logger.on_row_save_rollback(row[xls_pos])
468
440
  else:
469
441
  result_logger.on_row_save(row[xls_pos])
470
442
  # если были ошибки и нужно откатить всё
471
443
  # FIXME: Если rollback_all == True, то независимо от флага
472
444
  # FIXME: игнора, будет происходить откат
473
445
  if _errors and (cls.rollback_all or not ignore_bad_rows):
474
- log.setdefault(
475
- row[xls_pos][:2] + ('',), []
476
- ).extend([cls.LOAD_ERROR_MSG, ])
446
+ log.setdefault(row[xls_pos][:2] + ('',), []).extend(
447
+ [
448
+ cls.LOAD_ERROR_MSG,
449
+ ]
450
+ )
477
451
  outer_t.rollback()
478
452
  result_logger.on_save_rollback()
479
453
 
@@ -482,10 +456,8 @@ class MultiProxyLoader:
482
456
  # с использованием transaction.atomic
483
457
  @classmethod
484
458
  @transaction.atomic
485
- def load_rows(cls, header_data, rows_data, parse_log, log, context,
486
- warning_log=None, result_logger=None):
487
- """
488
- Загрузка строк листа.
459
+ def load_rows(cls, header_data, rows_data, parse_log, log, context, warning_log=None, result_logger=None):
460
+ """Загрузка строк листа.
489
461
 
490
462
  Логи разделены с целью дать возможность потомку принять решение
491
463
  о дальнейшей загрузке, если были ошибки парсинга ячеек.
@@ -519,8 +491,7 @@ class MultiProxyLoader:
519
491
  add_log(row, items)
520
492
 
521
493
  # обработчик данных шапки листа
522
- header_context, header_errors = cls.make_header_context(
523
- header_data, context)
494
+ header_context, header_errors = cls.make_header_context(header_data, context)
524
495
 
525
496
  if header_errors:
526
497
  # были ошибки в шапке листа, дальше лист не грузится
@@ -563,16 +534,13 @@ class MultiProxyLoader:
563
534
  except ProxyCriticalError as err:
564
535
  rows_errors.append(str(err))
565
536
  add_log(row, [str(err)])
566
- result_logger.on_critical_error(
567
- row[xls_pos], str(err)
568
- )
537
+ result_logger.on_critical_error(row[xls_pos], str(err))
569
538
  break
570
539
 
571
540
  # если были ошибки и нужно откатить всё
572
541
  # FIXME: Если rollback_all == True, то независимо от флага
573
542
  # FIXME: игнора, будет происходить откат
574
- if result_logger.has_error() and (
575
- cls.rollback_all or not ignore_bad_rows):
543
+ if result_logger.has_error() and (cls.rollback_all or not ignore_bad_rows):
576
544
  key = row[xls_pos][:2] + ('',)
577
545
  log.setdefault(key, []).extend([cls.LOAD_ERROR_MSG])
578
546
 
@@ -581,16 +549,17 @@ class MultiProxyLoader:
581
549
 
582
550
  @classmethod
583
551
  def make_config(cls):
584
- """
585
- Создание конфигурации для XLSLoader`а
586
- """
552
+ """Создание конфигурации для XLSLoader`а."""
553
+
587
554
  def prepare_headers(raw_headers):
588
- """
555
+ """Подготовка заголовка.
556
+
589
557
  Если на входе строка, она оборачивается в словарь-подобный объект,
590
558
  который на любой ключ возвращает эту строку.
591
559
  Если на входе словарь - он остаётся словарём.
592
560
  """
593
561
  if isinstance(raw_headers, str):
562
+
594
563
  class StrAsDict:
595
564
  def __init__(self, value):
596
565
  self._value = value
@@ -602,7 +571,9 @@ class MultiProxyLoader:
602
571
  if isinstance(default, dict):
603
572
  return self
604
573
  return self._value
574
+
605
575
  return StrAsDict(raw_headers)
576
+
606
577
  return raw_headers
607
578
 
608
579
  def make_subconfig(headers, options):
@@ -623,24 +594,24 @@ class MultiProxyLoader:
623
594
 
624
595
  try:
625
596
  # пробуем построить конфиг для proxy
626
- sub_config.update(value._ImportProxy__make_config(
627
- headers.get(key)
628
- ))
597
+ sub_config.update(value._ImportProxy__make_config(headers.get(key)))
629
598
  except AttributeError:
630
599
  # заменяем подопции на {} если они к данному
631
600
  # моменту выродились в булев флаг
632
- sub_options = (
633
- isinstance(sub_options, bool) and {}
634
- ) or sub_options
601
+ sub_options = (isinstance(sub_options, bool) and {}) or sub_options
635
602
 
636
603
  # строим конфиг для поддерева
637
- sub_config.update(dict(map(
638
- make_subconfig(
639
- prepare_headers(headers.get(key, {})),
640
- sub_options,
641
- ),
642
- value
643
- )))
604
+ sub_config.update(
605
+ dict(
606
+ map(
607
+ make_subconfig(
608
+ prepare_headers(headers.get(key, {})),
609
+ sub_options,
610
+ ),
611
+ value,
612
+ )
613
+ )
614
+ )
644
615
 
645
616
  return (key, sub_config)
646
617
 
@@ -648,19 +619,21 @@ class MultiProxyLoader:
648
619
 
649
620
  # начальный вызов
650
621
  result = cls.xlsreader_extra.copy()
651
- result.update(dict(map(
652
- make_subconfig(
653
- prepare_headers(cls.headers),
654
- cls.optionals,
655
- ),
656
- cls.proxies
657
- )))
622
+ result.update(
623
+ dict(
624
+ map(
625
+ make_subconfig(
626
+ prepare_headers(cls.headers),
627
+ cls.optionals,
628
+ ),
629
+ cls.proxies,
630
+ )
631
+ )
632
+ )
658
633
  return result
659
634
 
660
635
  def __init__(self, context=None):
661
- """
662
- Загрузка строки данных
663
- """
636
+ """Загрузка строки данных."""
664
637
  self._context = {
665
638
  'error_format': self.error_format,
666
639
  }
@@ -670,28 +643,25 @@ class MultiProxyLoader:
670
643
  self._set_validators()
671
644
 
672
645
  def _set_validators(self):
673
- """
674
- Устанавливает валидаторы, которые будут применяться при загрузке.
675
- """
646
+ """Устанавливает валидаторы, которые будут применяться при загрузке."""
676
647
  self._validators = []
677
648
 
678
649
  def _validate(self, data):
679
- """
680
- Применяет валидаторы ко входным данным.
650
+ """Применяет валидаторы ко входным данным.
651
+
681
652
  :param data: словарь входных данных
682
653
  :return: два списка, список ошибок и список предупреждений.
683
654
  """
684
655
  errors, warnings = [], []
685
656
  for validator in self._validators:
686
657
  validator(data, errors, warnings)
658
+
687
659
  return errors, warnings
688
660
 
689
661
  def load(self, data):
690
-
691
662
  errors, warnings = self._validate(data)
692
663
 
693
664
  def load_to_proxy(context, options, data, key, proxy):
694
-
695
665
  # проверка на принадлежность proxy к потомкам ImportProxy
696
666
  try:
697
667
  is_single_proxy = issubclass(proxy, _ImportProxy)
@@ -730,19 +700,15 @@ class MultiProxyLoader:
730
700
  context[key] = proxy(key, context)._load(sub_data)
731
701
  except ProxyWarning as err:
732
702
  # Записать в лог и продолжить загрузку строки
733
- warnings.append(
734
- (context.error_format or '%s') % str(err)
735
- )
703
+ warnings.append((context.error_format or '{}').format(str(err)))
736
704
  raise
737
705
  except (ProxySaveError, ValidationError) as err:
738
706
  # такая ошибка складывается в лог
739
- errors.append(
740
- (context.error_format or '%s') % str(err)
741
- )
707
+ errors.append((context.error_format or '{}').format(str(err)))
742
708
  if not is_layout_proxy:
743
709
  # если обычный прокси, вся строка пропускается
744
710
  return False
745
- except (ProxyCriticalError, AssertionError) as err:
711
+ except (ProxyCriticalError, AssertionError):
746
712
  # AssertionError - откатит всю загрузку
747
713
  # ProxyCriticalError - откатит с выводом сообщения
748
714
  raise
@@ -758,13 +724,9 @@ class MultiProxyLoader:
758
724
  sub_data = SafeDict(sub_data)
759
725
  # получаем подопции
760
726
  if isinstance(sub_options, bool):
761
- sub_options = collections.defaultdict(
762
- lambda: sub_options
763
- )
727
+ sub_options = collections.defaultdict(lambda: sub_options)
764
728
  for sub_key, sub_proxy in proxy:
765
- if not load_to_proxy(
766
- sub_context, sub_options, sub_data, sub_key, sub_proxy
767
- ):
729
+ if not load_to_proxy(sub_context, sub_options, sub_data, sub_key, sub_proxy):
768
730
  return False
769
731
  # из подконтекста атрибут с тем же ключем помещается в контекст
770
732
  context[key] = sub_context[key]
@@ -774,8 +736,7 @@ class MultiProxyLoader:
774
736
  if errors:
775
737
  break
776
738
  try:
777
- proxy_result = load_to_proxy(
778
- self._context, self.optionals, data, key, proxy)
739
+ proxy_result = load_to_proxy(self._context, self.optionals, data, key, proxy)
779
740
  except ProxyWarning:
780
741
  continue
781
742
  else:
@@ -786,9 +747,8 @@ class MultiProxyLoader:
786
747
 
787
748
 
788
749
  class LayoutProxyLoader(MultiProxyLoader):
789
- """
790
- Загрузчик, умеющий генерировать себе прокси по размеченной области
791
- """
750
+ """Загрузчик, умеющий генерировать себе прокси по размеченной области."""
751
+
792
752
  # список кортежей шаблонов для прокси, определяющих область данных листа,
793
753
  # которыми будет дополнен основной список proxies
794
754
  layout_proxies_template = []
@@ -797,7 +757,7 @@ class LayoutProxyLoader(MultiProxyLoader):
797
757
 
798
758
  @classmethod
799
759
  def add_proxy(cls, key, proxy, add_log, context):
800
- """Встраивание прокси для области в список задекларированных прокси"""
760
+ """Встраивание прокси для области в список задекларированных прокси."""
801
761
 
802
762
  # обновим копию начального контекста загрузчика контекстом из api
803
763
  loader_context = cls.initial_context.copy()
@@ -814,21 +774,16 @@ class LayoutProxyLoader(MultiProxyLoader):
814
774
  cls.add_proxy_log.setdefault(key, str(err))
815
775
 
816
776
  @classmethod
817
- def load_rows(cls, header_data, rows_data, parse_log, log, context,
818
- warning_log=None):
819
- super(LayoutProxyLoader, cls).load_rows(
820
- header_data, rows_data, parse_log, log, context
821
- )
777
+ def load_rows(cls, header_data, rows_data, parse_log, log, context, warning_log=None):
778
+ super().load_rows(header_data, rows_data, parse_log, log, context)
779
+
822
780
  if cls.add_proxy_log:
823
- log.setdefault(
824
- rows_data[0][context['XLS_POS']], []
825
- ).extend(list(cls.add_proxy_log.values()))
781
+ log.setdefault(rows_data[0][context['XLS_POS']], []).extend(list(cls.add_proxy_log.values()))
826
782
 
827
783
 
828
784
  def _fabricate_proxy(AncestorCls, name, cells_config, default_headers):
829
- """
830
- Изготовление прокси области (для загрузчика)
831
- по шаблону-прокси и по заданным извне настройкам ячеек
785
+ """Изготовление прокси области (для загрузчика) по шаблону-прокси и по заданным извне настройкам ячеек.
786
+
832
787
  :param AncestorCls: класс-предок, описывающий области и метод загрузки
833
788
  :param name: str - имя области
834
789
  :param cells_config: dict,
@@ -836,24 +791,23 @@ def _fabricate_proxy(AncestorCls, name, cells_config, default_headers):
836
791
  """
837
792
 
838
793
  cls = type(
839
- name.capitalize(), (AncestorCls,),
794
+ name.capitalize(),
795
+ (AncestorCls,),
840
796
  {
841
797
  'was_fabricated': True, # для фильтраци при добавлении в загрузчик
842
798
  'cells_config': cells_config,
843
799
  'default_headers': default_headers,
844
800
  # соответствие {модель: (ключ, строка)} для дальнейшего парсинга
845
- 'layout_header': dict([(
846
- AncestorCls.layout_header_model, (k, v)
847
- ) for k, v in default_headers.items()]),
848
- }
801
+ 'layout_header': dict([(AncestorCls.layout_header_model, (k, v)) for k, v in default_headers.items()]),
802
+ },
849
803
  )
804
+
850
805
  return name, cls
851
806
 
852
807
 
853
808
  def fabricate_proxies(AncestorCls, layout_name, head_data):
854
- """
855
- Создает настройки ячеек для области и
856
- возвращает список кортежей сгенерированных прокси для загрузчика
809
+ """Создает настройки ячеек для области и возвращает список кортежей сгенерированных прокси для загрузчика.
810
+
857
811
  :param AncestorCls: класс-предок, описывающий области и метод загрузки
858
812
  :param layout_name: str - имя области
859
813
  :param head_data: list - список строк, определяющий заголовки столбцов
@@ -867,9 +821,9 @@ def fabricate_proxies(AncestorCls, layout_name, head_data):
867
821
  # использовать отдельные прокси для каждого столбца
868
822
  use_separated_proxy = layout_conf.get('use_separated_proxy', False)
869
823
  # заголовки колонок этой области
870
- cols = head_data[layout_conf.get('start_col'):layout_conf.get('end_col')]
824
+ cols = head_data[layout_conf.get('start_col') : layout_conf.get('end_col')]
871
825
  for idx, col_name in enumerate(cols):
872
- key = '%s%i' % (layout_name, idx)
826
+ key = f'{layout_name}{idx}'
873
827
  if use_separated_proxy:
874
828
  cells_config = {key: layout_conf.get('cell_type')}
875
829
  default_headers = {key: col_name}
@@ -883,4 +837,5 @@ def fabricate_proxies(AncestorCls, layout_name, head_data):
883
837
  if not use_separated_proxy and params:
884
838
  # либо генерация одного прокси для всех столбцов
885
839
  proxies.append(_fabricate_proxy(*params))
840
+
886
841
  return proxies