educommon 3.12.0__py3-none-any.whl → 3.13.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- educommon/__init__.py +0 -1
- educommon/about/ui/actions.py +16 -30
- educommon/about/ui/ui.py +3 -12
- educommon/about/utils.py +6 -5
- educommon/async_task/__init__.py +0 -1
- educommon/async_task/actions.py +18 -13
- educommon/async_task/apps.py +4 -0
- educommon/async_task/locker.py +2 -5
- educommon/async_task/migrations/0001_initial.py +55 -9
- educommon/async_task/migrations/0002_task_type_and_status_data.py +94 -89
- educommon/async_task/migrations/0003_alter_runningtask_options.py +0 -1
- educommon/async_task/models.py +9 -6
- educommon/async_task/tasks.py +11 -7
- educommon/async_task/ui.py +16 -35
- educommon/async_tasks/__init__.py +0 -1
- educommon/async_tasks/apps.py +4 -0
- educommon/async_tasks/locks.py +11 -21
- educommon/async_tasks/migrations/0001_initial.py +68 -8
- educommon/async_tasks/migrations/0002_load_initial_data.py +0 -1
- educommon/async_tasks/models.py +9 -29
- educommon/async_tasks/tasks.py +25 -54
- educommon/audit_log/__init__.py +1 -0
- educommon/audit_log/actions.py +27 -36
- educommon/audit_log/app_meta.py +7 -4
- educommon/audit_log/apps.py +44 -29
- educommon/audit_log/constants.py +7 -4
- educommon/audit_log/error_log/actions.py +1 -3
- educommon/audit_log/helpers.py +2 -4
- educommon/audit_log/management/commands/reinstall_audit_log.py +11 -7
- educommon/audit_log/migrations/0001_initial.py +91 -16
- educommon/audit_log/migrations/0002_install_audit_log.py +13 -13
- educommon/audit_log/migrations/0003_logproxy.py +1 -3
- educommon/audit_log/migrations/0004_reinstall_audit_log.py +1 -4
- educommon/audit_log/migrations/0005_postgresql_error.py +4 -2
- educommon/audit_log/migrations/0006_auto_20200806_1707.py +3 -4
- educommon/audit_log/migrations/0007_create_selective_tables_function.py +8 -5
- educommon/audit_log/migrations/0008_table_logged.py +0 -1
- educommon/audit_log/migrations/0009_reinstall_audit_log.py +0 -1
- educommon/audit_log/models.py +36 -42
- educommon/audit_log/permissions.py +11 -9
- educommon/audit_log/proxies.py +12 -23
- educommon/audit_log/ui.py +18 -15
- educommon/audit_log/utils/__init__.py +28 -60
- educommon/audit_log/utils/operations.py +16 -2
- educommon/auth/__init__.py +0 -3
- educommon/auth/rbac/__init__.py +2 -4
- educommon/auth/rbac/actions.py +148 -145
- educommon/auth/rbac/app_meta.py +9 -6
- educommon/auth/rbac/backends/base.py +2 -8
- educommon/auth/rbac/backends/caching.py +27 -37
- educommon/auth/rbac/backends/simple.py +1 -4
- educommon/auth/rbac/checker.py +1 -3
- educommon/auth/rbac/management/commands/rbac.py +6 -11
- educommon/auth/rbac/manager.py +18 -47
- educommon/auth/rbac/migrations/0001_initial.py +73 -12
- educommon/auth/rbac/migrations/0002_model_modifier_metaclass_fix.py +7 -6
- educommon/auth/rbac/migrations/0003_permission_hidden.py +1 -5
- educommon/auth/rbac/migrations/0004_auto_20171024_1245.py +26 -19
- educommon/auth/rbac/models.py +63 -68
- educommon/auth/rbac/permissions.py +6 -7
- educommon/auth/rbac/ui.py +83 -84
- educommon/auth/rbac/utils.py +10 -11
- educommon/auth/rbac/validators.py +4 -5
- educommon/auth/simple_auth/__init__.py +1 -5
- educommon/auth/simple_auth/actions.py +79 -92
- educommon/auth/simple_auth/app_meta.py +2 -9
- educommon/auth/simple_auth/checkers.py +3 -3
- educommon/auth/simple_auth/migrations/0001_initial.py +23 -4
- educommon/auth/simple_auth/validators.py +0 -1
- educommon/contingent/actions.py +7 -7
- educommon/contingent/app_meta.py +1 -4
- educommon/contingent/base.py +10 -15
- educommon/contingent/catalogs.py +424 -540
- educommon/contingent/contingent_plugin/actions.py +4 -15
- educommon/contingent/contingent_plugin/apps.py +10 -4
- educommon/contingent/contingent_plugin/migrations/0001_initial.py +5 -6
- educommon/contingent/contingent_plugin/migrations/0002_add_contingent_model_deleted.py +6 -11
- educommon/contingent/contingent_plugin/model_views.py +2 -12
- educommon/contingent/contingent_plugin/models.py +2 -7
- educommon/contingent/contingent_plugin/observer.py +14 -13
- educommon/contingent/contingent_plugin/plugin_meta.py +1 -3
- educommon/contingent/contingent_plugin/storage.py +8 -7
- educommon/contingent/contingent_plugin/utils.py +6 -6
- educommon/django/db/fields.py +72 -86
- educommon/django/db/migration/__init__.py +3 -7
- educommon/django/db/migration/operations.py +29 -51
- educommon/django/db/mixins/__init__.py +16 -10
- educommon/django/db/mixins/date_interval.py +47 -75
- educommon/django/db/mixins/validation.py +26 -26
- educommon/django/db/model_view/__init__.py +18 -22
- educommon/django/db/models.py +9 -8
- educommon/django/db/observer.py +9 -27
- educommon/django/db/partitioning/__init__.py +66 -92
- educommon/django/db/partitioning/management/commands/apply_partitioning.py +3 -13
- educommon/django/db/partitioning/management/commands/clear_table.py +18 -14
- educommon/django/db/partitioning/management/commands/split_table.py +18 -13
- educommon/django/db/routers.py +6 -15
- educommon/django/db/signals.py +149 -2
- educommon/django/db/utils.py +14 -19
- educommon/django/db/validators/__init__.py +1 -0
- educommon/django/db/validators/simple.py +72 -100
- educommon/django/storages/atcfs/api.py +39 -53
- educommon/django/storages/atcfs/app_meta.py +1 -1
- educommon/django/storages/atcfs/management/commands/atcfs_migrate.py +42 -55
- educommon/django/storages/atcfs/models.py +0 -3
- educommon/django/storages/atcfs/monkey_patching.py +18 -12
- educommon/django/storages/atcfs/storage.py +14 -23
- educommon/extjs/fields/input_params.py +15 -45
- educommon/importer/XLSReader.py +143 -241
- educommon/importer/__init__.py +86 -4
- educommon/importer/api.py +53 -84
- educommon/importer/constants.py +4 -14
- educommon/importer/loggers.py +16 -26
- educommon/importer/proxy.py +131 -176
- educommon/importer/proxy_import.py +11 -12
- educommon/importer/report.py +4 -6
- educommon/importer/ui.py +32 -26
- educommon/importer/validators.py +4 -7
- educommon/integration_entities/helpers.py +14 -18
- educommon/ioc/__init__.py +3 -6
- educommon/logger/loggers.py +10 -14
- educommon/m3/__init__.py +20 -38
- educommon/m3/extensions/__init__.py +1 -0
- educommon/m3/extensions/listeners/__init__.py +22 -38
- educommon/m3/extensions/listeners/delete_check/listeners.py +31 -41
- educommon/m3/extensions/listeners/delete_check/mixins.py +20 -25
- educommon/m3/extensions/listeners/delete_check/signals.py +2 -2
- educommon/m3/extensions/listeners/delete_check/ui.py +15 -14
- educommon/m3/extensions/listeners/delete_check/utils.py +9 -11
- educommon/m3/extensions/ui.py +15 -33
- educommon/m3/transaction_context.py +17 -19
- educommon/objectpack/actions.py +70 -88
- educommon/objectpack/apps.py +5 -0
- educommon/objectpack/filters.py +9 -15
- educommon/objectpack/ui.py +59 -77
- educommon/report/__init__.py +9 -5
- educommon/report/actions.py +29 -32
- educommon/report/constructor/__init__.py +5 -8
- educommon/report/constructor/app_meta.py +1 -3
- educommon/report/constructor/apps.py +1 -0
- educommon/report/constructor/base.py +33 -80
- educommon/report/constructor/builders/excel/_base.py +138 -286
- educommon/report/constructor/builders/excel/_header.py +2 -9
- educommon/report/constructor/builders/excel/product.py +13 -34
- educommon/report/constructor/builders/excel/with_merged_cells.py +18 -14
- educommon/report/constructor/config.py +2 -0
- educommon/report/constructor/editor/actions.py +101 -215
- educommon/report/constructor/editor/ui.py +71 -93
- educommon/report/constructor/exceptions.py +6 -12
- educommon/report/constructor/migrations/0001_initial.py +36 -44
- educommon/report/constructor/migrations/0002_report_filters.py +86 -72
- educommon/report/constructor/migrations/0003_reportfilter_exclude.py +5 -5
- educommon/report/constructor/migrations/0004_reportfilter_fields.py +22 -18
- educommon/report/constructor/migrations/0005_reportcolumn_visible.py +5 -4
- educommon/report/constructor/migrations/0006_reportsorting.py +21 -17
- educommon/report/constructor/migrations/0007_include_available_units.py +14 -14
- educommon/report/constructor/migrations/0008_auto_20170407_1318.py +4 -5
- educommon/report/constructor/migrations/0009_auto_20180405_0642.py +1 -4
- educommon/report/constructor/migrations/0010_add_aggregate_fields.py +7 -8
- educommon/report/constructor/mixins.py +14 -15
- educommon/report/constructor/models.py +76 -124
- educommon/report/constructor/utils.py +3 -8
- educommon/report/constructor/validators.py +1 -3
- educommon/report/reporter.py +25 -43
- educommon/report/utils.py +14 -40
- educommon/rest/actions.py +7 -11
- educommon/rest/context.py +6 -16
- educommon/rest/controllers.py +10 -10
- educommon/rest/mixins.py +29 -27
- educommon/secure_media/app_meta.py +9 -9
- educommon/utils/__init__.py +3 -2
- educommon/utils/caching.py +1 -3
- educommon/utils/conversion.py +1 -3
- educommon/utils/crypto.py +1 -2
- educommon/utils/date.py +13 -26
- educommon/utils/db/__init__.py +17 -26
- educommon/utils/db/postgresql.py +1 -4
- educommon/utils/fonts/__init__.py +3 -4
- educommon/utils/licence/__init__.py +5 -16
- educommon/utils/misc.py +9 -18
- educommon/utils/object_grid.py +55 -62
- educommon/utils/phone_number/modelfields.py +1 -3
- educommon/utils/phone_number/phone_number.py +5 -8
- educommon/utils/phone_number/validators.py +8 -23
- educommon/utils/plugins.py +15 -28
- educommon/utils/registry.py +2 -1
- educommon/utils/seqtools.py +1 -3
- educommon/utils/serializer.py +9 -16
- educommon/utils/storage.py +3 -2
- educommon/utils/system.py +1 -3
- educommon/utils/system_app/management/commands/delete_objects.py +17 -34
- educommon/utils/ui.py +87 -84
- educommon/utils/xml/__init__.py +2 -7
- educommon/utils/xml/resolver.py +1 -0
- educommon/ws_log/actions.py +31 -76
- educommon/ws_log/base.py +6 -20
- educommon/ws_log/migrations/0001_initial.py +25 -8
- educommon/ws_log/migrations/0002_auto_20160628_1334.py +0 -1
- educommon/ws_log/migrations/0003_add_fields_to_smev_logs.py +20 -4
- educommon/ws_log/migrations/0004_auto_20160727_1600.py +7 -6
- educommon/ws_log/migrations/0005_auto_20161130_1615.py +14 -4
- educommon/ws_log/migrations/0006_auto_20170327_1027.py +3 -2
- educommon/ws_log/migrations/0007_auto_20180607_1040.py +8 -9
- educommon/ws_log/migrations/0008_auto_20180713_1445.py +23 -10
- educommon/ws_log/migrations/0009_auto_20201130_1553.py +7 -2
- educommon/ws_log/models.py +21 -35
- educommon/ws_log/provider.py +2 -1
- educommon/ws_log/report.py +8 -13
- educommon/ws_log/smev/applications.py +12 -27
- educommon/ws_log/smev/exceptions.py +2 -3
- educommon/ws_log/ui.py +32 -32
- educommon/ws_log/utils.py +1 -3
- educommon-3.13.2.dist-info/METADATA +57 -0
- educommon-3.13.2.dist-info/RECORD +354 -0
- {educommon-3.12.0.dist-info → educommon-3.13.2.dist-info}/WHEEL +1 -1
- educommon/utils/patches.py +0 -27
- educommon/version.conf +0 -11
- educommon-3.12.0.dist-info/METADATA +0 -47
- educommon-3.12.0.dist-info/RECORD +0 -357
- educommon-3.12.0.dist-info/dependency_links.txt +0 -1
- {educommon-3.12.0.dist-info → educommon-3.13.2.dist-info}/top_level.txt +0 -0
educommon/importer/proxy.py
CHANGED
@@ -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(
|
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
|
-
|
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 =
|
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
|
-
|
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(
|
181
|
-
|
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
|
-
|
476
|
-
|
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
|
-
|
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
|
-
|
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(
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
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(
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
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)
|
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
|
-
|
819
|
-
|
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(),
|
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
|
-
|
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 = '
|
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
|