educommon 3.13.0__py3-none-any.whl → 3.13.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- educommon/__init__.py +0 -1
- educommon/about/ui/actions.py +16 -30
- educommon/about/ui/ui.py +3 -12
- educommon/about/utils.py +6 -5
- educommon/async_task/__init__.py +0 -1
- educommon/async_task/actions.py +18 -13
- educommon/async_task/apps.py +4 -0
- educommon/async_task/locker.py +2 -5
- educommon/async_task/migrations/0001_initial.py +55 -9
- educommon/async_task/migrations/0002_task_type_and_status_data.py +94 -89
- educommon/async_task/migrations/0003_alter_runningtask_options.py +0 -1
- educommon/async_task/models.py +9 -6
- educommon/async_task/tasks.py +11 -7
- educommon/async_task/ui.py +16 -35
- educommon/async_tasks/__init__.py +0 -1
- educommon/async_tasks/apps.py +4 -0
- educommon/async_tasks/locks.py +11 -21
- educommon/async_tasks/migrations/0001_initial.py +68 -8
- educommon/async_tasks/migrations/0002_load_initial_data.py +0 -1
- educommon/async_tasks/models.py +9 -29
- educommon/async_tasks/tasks.py +25 -54
- educommon/audit_log/__init__.py +1 -0
- educommon/audit_log/actions.py +27 -36
- educommon/audit_log/app_meta.py +7 -4
- educommon/audit_log/apps.py +44 -29
- educommon/audit_log/constants.py +7 -4
- educommon/audit_log/error_log/actions.py +1 -3
- educommon/audit_log/helpers.py +2 -4
- educommon/audit_log/management/commands/reinstall_audit_log.py +11 -7
- educommon/audit_log/migrations/0001_initial.py +91 -16
- educommon/audit_log/migrations/0002_install_audit_log.py +13 -13
- educommon/audit_log/migrations/0003_logproxy.py +1 -3
- educommon/audit_log/migrations/0004_reinstall_audit_log.py +1 -4
- educommon/audit_log/migrations/0005_postgresql_error.py +4 -2
- educommon/audit_log/migrations/0006_auto_20200806_1707.py +3 -4
- educommon/audit_log/migrations/0007_create_selective_tables_function.py +8 -5
- educommon/audit_log/migrations/0008_table_logged.py +0 -1
- educommon/audit_log/migrations/0009_reinstall_audit_log.py +0 -1
- educommon/audit_log/models.py +36 -42
- educommon/audit_log/permissions.py +11 -9
- educommon/audit_log/proxies.py +12 -23
- educommon/audit_log/ui.py +18 -15
- educommon/audit_log/utils/__init__.py +28 -60
- educommon/audit_log/utils/operations.py +16 -2
- educommon/auth/__init__.py +0 -3
- educommon/auth/rbac/__init__.py +2 -4
- educommon/auth/rbac/actions.py +148 -145
- educommon/auth/rbac/app_meta.py +9 -6
- educommon/auth/rbac/backends/base.py +2 -8
- educommon/auth/rbac/backends/caching.py +27 -37
- educommon/auth/rbac/backends/simple.py +1 -4
- educommon/auth/rbac/checker.py +1 -3
- educommon/auth/rbac/management/commands/rbac.py +6 -11
- educommon/auth/rbac/manager.py +18 -47
- educommon/auth/rbac/migrations/0001_initial.py +73 -12
- educommon/auth/rbac/migrations/0002_model_modifier_metaclass_fix.py +7 -6
- educommon/auth/rbac/migrations/0003_permission_hidden.py +1 -5
- educommon/auth/rbac/migrations/0004_auto_20171024_1245.py +26 -19
- educommon/auth/rbac/models.py +63 -68
- educommon/auth/rbac/permissions.py +6 -7
- educommon/auth/rbac/ui.py +83 -84
- educommon/auth/rbac/utils.py +10 -11
- educommon/auth/rbac/validators.py +4 -5
- educommon/auth/simple_auth/__init__.py +1 -5
- educommon/auth/simple_auth/actions.py +79 -92
- educommon/auth/simple_auth/app_meta.py +2 -9
- educommon/auth/simple_auth/checkers.py +3 -3
- educommon/auth/simple_auth/migrations/0001_initial.py +23 -4
- educommon/auth/simple_auth/validators.py +0 -1
- educommon/contingent/actions.py +7 -7
- educommon/contingent/app_meta.py +1 -4
- educommon/contingent/base.py +10 -15
- educommon/contingent/catalogs.py +424 -540
- educommon/contingent/contingent_plugin/actions.py +4 -15
- educommon/contingent/contingent_plugin/apps.py +10 -4
- educommon/contingent/contingent_plugin/migrations/0001_initial.py +5 -6
- educommon/contingent/contingent_plugin/migrations/0002_add_contingent_model_deleted.py +6 -11
- educommon/contingent/contingent_plugin/model_views.py +2 -12
- educommon/contingent/contingent_plugin/models.py +2 -7
- educommon/contingent/contingent_plugin/observer.py +14 -13
- educommon/contingent/contingent_plugin/plugin_meta.py +1 -3
- educommon/contingent/contingent_plugin/storage.py +8 -7
- educommon/contingent/contingent_plugin/utils.py +6 -6
- educommon/django/db/fields.py +72 -86
- educommon/django/db/migration/__init__.py +3 -7
- educommon/django/db/migration/operations.py +29 -51
- educommon/django/db/mixins/__init__.py +16 -10
- educommon/django/db/mixins/date_interval.py +47 -75
- educommon/django/db/mixins/validation.py +26 -26
- educommon/django/db/model_view/__init__.py +18 -22
- educommon/django/db/models.py +9 -8
- educommon/django/db/observer.py +9 -27
- educommon/django/db/partitioning/__init__.py +66 -92
- educommon/django/db/partitioning/management/commands/apply_partitioning.py +3 -13
- educommon/django/db/partitioning/management/commands/clear_table.py +18 -14
- educommon/django/db/partitioning/management/commands/split_table.py +18 -13
- educommon/django/db/routers.py +6 -15
- educommon/django/db/signals.py +4 -2
- educommon/django/db/utils.py +14 -19
- educommon/django/db/validators/__init__.py +1 -0
- educommon/django/db/validators/simple.py +72 -100
- educommon/django/storages/atcfs/api.py +39 -53
- educommon/django/storages/atcfs/app_meta.py +1 -1
- educommon/django/storages/atcfs/management/commands/atcfs_migrate.py +42 -55
- educommon/django/storages/atcfs/models.py +0 -3
- educommon/django/storages/atcfs/monkey_patching.py +18 -12
- educommon/django/storages/atcfs/storage.py +14 -23
- educommon/extjs/fields/input_params.py +15 -45
- educommon/importer/XLSReader.py +143 -241
- educommon/importer/__init__.py +86 -4
- educommon/importer/api.py +53 -84
- educommon/importer/constants.py +4 -14
- educommon/importer/loggers.py +16 -26
- educommon/importer/proxy.py +131 -176
- educommon/importer/proxy_import.py +11 -12
- educommon/importer/report.py +4 -6
- educommon/importer/ui.py +32 -26
- educommon/importer/validators.py +4 -7
- educommon/integration_entities/helpers.py +14 -18
- educommon/ioc/__init__.py +3 -6
- educommon/logger/loggers.py +10 -14
- educommon/m3/__init__.py +20 -38
- educommon/m3/extensions/__init__.py +1 -0
- educommon/m3/extensions/listeners/__init__.py +22 -38
- educommon/m3/extensions/listeners/delete_check/listeners.py +31 -41
- educommon/m3/extensions/listeners/delete_check/mixins.py +20 -25
- educommon/m3/extensions/listeners/delete_check/signals.py +2 -2
- educommon/m3/extensions/listeners/delete_check/ui.py +15 -14
- educommon/m3/extensions/listeners/delete_check/utils.py +9 -11
- educommon/m3/extensions/ui.py +15 -33
- educommon/m3/transaction_context.py +17 -19
- educommon/objectpack/actions.py +70 -88
- educommon/objectpack/apps.py +5 -0
- educommon/objectpack/filters.py +9 -15
- educommon/objectpack/ui.py +59 -77
- educommon/report/__init__.py +9 -5
- educommon/report/actions.py +29 -32
- educommon/report/constructor/__init__.py +5 -8
- educommon/report/constructor/app_meta.py +1 -3
- educommon/report/constructor/apps.py +1 -0
- educommon/report/constructor/base.py +33 -80
- educommon/report/constructor/builders/excel/_base.py +138 -286
- educommon/report/constructor/builders/excel/_header.py +2 -9
- educommon/report/constructor/builders/excel/product.py +13 -34
- educommon/report/constructor/builders/excel/with_merged_cells.py +18 -14
- educommon/report/constructor/config.py +2 -0
- educommon/report/constructor/editor/actions.py +101 -215
- educommon/report/constructor/editor/ui.py +71 -93
- educommon/report/constructor/exceptions.py +6 -12
- educommon/report/constructor/migrations/0001_initial.py +36 -44
- educommon/report/constructor/migrations/0002_report_filters.py +86 -72
- educommon/report/constructor/migrations/0003_reportfilter_exclude.py +5 -5
- educommon/report/constructor/migrations/0004_reportfilter_fields.py +22 -18
- educommon/report/constructor/migrations/0005_reportcolumn_visible.py +5 -4
- educommon/report/constructor/migrations/0006_reportsorting.py +21 -17
- educommon/report/constructor/migrations/0007_include_available_units.py +14 -14
- educommon/report/constructor/migrations/0008_auto_20170407_1318.py +4 -5
- educommon/report/constructor/migrations/0009_auto_20180405_0642.py +1 -4
- educommon/report/constructor/migrations/0010_add_aggregate_fields.py +7 -8
- educommon/report/constructor/mixins.py +14 -15
- educommon/report/constructor/models.py +76 -124
- educommon/report/constructor/utils.py +3 -8
- educommon/report/constructor/validators.py +1 -3
- educommon/report/reporter.py +25 -43
- educommon/report/utils.py +14 -40
- educommon/rest/actions.py +7 -11
- educommon/rest/context.py +6 -16
- educommon/rest/controllers.py +10 -10
- educommon/rest/mixins.py +29 -27
- educommon/secure_media/app_meta.py +9 -9
- educommon/utils/__init__.py +3 -2
- educommon/utils/caching.py +1 -3
- educommon/utils/conversion.py +1 -3
- educommon/utils/crypto.py +1 -2
- educommon/utils/date.py +13 -26
- educommon/utils/db/__init__.py +17 -26
- educommon/utils/db/postgresql.py +1 -4
- educommon/utils/fonts/__init__.py +3 -4
- educommon/utils/licence/__init__.py +5 -16
- educommon/utils/misc.py +9 -18
- educommon/utils/object_grid.py +55 -62
- educommon/utils/phone_number/modelfields.py +1 -3
- educommon/utils/phone_number/phone_number.py +5 -8
- educommon/utils/phone_number/validators.py +8 -23
- educommon/utils/plugins.py +15 -28
- educommon/utils/registry.py +2 -1
- educommon/utils/seqtools.py +1 -3
- educommon/utils/serializer.py +9 -16
- educommon/utils/storage.py +3 -2
- educommon/utils/system.py +1 -3
- educommon/utils/system_app/management/commands/delete_objects.py +17 -34
- educommon/utils/ui.py +87 -84
- educommon/utils/xml/__init__.py +2 -7
- educommon/utils/xml/resolver.py +1 -0
- educommon/ws_log/actions.py +31 -76
- educommon/ws_log/base.py +6 -20
- educommon/ws_log/migrations/0001_initial.py +25 -8
- educommon/ws_log/migrations/0002_auto_20160628_1334.py +0 -1
- educommon/ws_log/migrations/0003_add_fields_to_smev_logs.py +20 -4
- educommon/ws_log/migrations/0004_auto_20160727_1600.py +7 -6
- educommon/ws_log/migrations/0005_auto_20161130_1615.py +14 -4
- educommon/ws_log/migrations/0006_auto_20170327_1027.py +3 -2
- educommon/ws_log/migrations/0007_auto_20180607_1040.py +8 -9
- educommon/ws_log/migrations/0008_auto_20180713_1445.py +23 -10
- educommon/ws_log/migrations/0009_auto_20201130_1553.py +7 -2
- educommon/ws_log/models.py +21 -35
- educommon/ws_log/provider.py +2 -1
- educommon/ws_log/report.py +8 -13
- educommon/ws_log/smev/applications.py +12 -27
- educommon/ws_log/smev/exceptions.py +2 -3
- educommon/ws_log/ui.py +32 -32
- educommon/ws_log/utils.py +1 -3
- {educommon-3.13.0.dist-info → educommon-3.13.2.dist-info}/METADATA +26 -14
- educommon-3.13.2.dist-info/RECORD +354 -0
- educommon/utils/patches.py +0 -27
- educommon/version.conf +0 -11
- educommon-3.13.0.dist-info/RECORD +0 -357
- educommon-3.13.0.dist-info/dependency_links.txt +0 -1
- {educommon-3.13.0.dist-info → educommon-3.13.2.dist-info}/WHEEL +0 -0
- {educommon-3.13.0.dist-info → educommon-3.13.2.dist-info}/top_level.txt +0 -0
educommon/utils/db/__init__.py
CHANGED
@@ -74,10 +74,10 @@ def get_related_fields(model, skip_func=None):
|
|
74
74
|
if not related_model._meta.proxy:
|
75
75
|
for field in related_model._meta.get_fields():
|
76
76
|
if (
|
77
|
-
field.concrete
|
78
|
-
isinstance(field, RelatedField)
|
79
|
-
issubclass(model, get_related(field).parent_model)
|
80
|
-
(not skip_func or not skip_func(field))
|
77
|
+
field.concrete
|
78
|
+
and isinstance(field, RelatedField)
|
79
|
+
and issubclass(model, get_related(field).parent_model)
|
80
|
+
and (not skip_func or not skip_func(field))
|
81
81
|
):
|
82
82
|
yield field
|
83
83
|
|
@@ -134,10 +134,7 @@ def get_related_instances(obj, collapse=True, skip_func=None):
|
|
134
134
|
"""
|
135
135
|
|
136
136
|
assert isinstance(obj, Model), type(obj)
|
137
|
-
for model, fields in groupby(
|
138
|
-
get_related_fields(obj.__class__, skip_func),
|
139
|
-
lambda field: field.model
|
140
|
-
):
|
137
|
+
for model, fields in groupby(get_related_fields(obj.__class__, skip_func), lambda field: field.model):
|
141
138
|
conditions = reduce(or_, (Q(**{field.name: obj}) for field in fields))
|
142
139
|
for related_obj in model.objects.filter(conditions).iterator():
|
143
140
|
if collapse:
|
@@ -145,13 +142,10 @@ def get_related_instances(obj, collapse=True, skip_func=None):
|
|
145
142
|
else:
|
146
143
|
for field in model._meta.get_fields():
|
147
144
|
if (
|
148
|
-
field.concrete
|
149
|
-
isinstance(field, RelatedField)
|
150
|
-
(
|
151
|
-
|
152
|
-
getattr(related_obj, field.attname) == obj.pk
|
153
|
-
) and
|
154
|
-
(not skip_func or not skip_func(field))
|
145
|
+
field.concrete
|
146
|
+
and isinstance(field, RelatedField)
|
147
|
+
and (field.related_model == obj.__class__ and getattr(related_obj, field.attname) == obj.pk)
|
148
|
+
and (not skip_func or not skip_func(field))
|
155
149
|
):
|
156
150
|
yield related_obj
|
157
151
|
|
@@ -188,21 +182,18 @@ def get_non_block_relations(obj):
|
|
188
182
|
"""
|
189
183
|
|
190
184
|
displayed_relations = dict()
|
191
|
-
collector = Collector(
|
192
|
-
using=router.db_for_write(obj.__class__, instance=obj)
|
193
|
-
)
|
185
|
+
collector = Collector(using=router.db_for_write(obj.__class__, instance=obj))
|
194
186
|
collector.collect((obj,))
|
195
187
|
# Коллектор содержит ссылку на obj, удалим сам объект из коллектора
|
196
188
|
collector.data[obj.__class__].remove(obj)
|
197
189
|
|
198
190
|
for model, related_objects in collector.data.items():
|
199
|
-
if all((
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
))
|
191
|
+
if all((getattr(model, 'display_related_error', True), related_objects)):
|
192
|
+
key = '.'.join(
|
193
|
+
(
|
194
|
+
model._meta.app_label,
|
195
|
+
model._meta.object_name,
|
196
|
+
)
|
197
|
+
)
|
207
198
|
displayed_relations[key] = tuple(related_objects)
|
208
199
|
return displayed_relations
|
educommon/utils/db/postgresql.py
CHANGED
@@ -19,10 +19,7 @@ def is_extension_exists(alias, name):
|
|
19
19
|
:rtype: bool
|
20
20
|
"""
|
21
21
|
with closing(connections[alias].cursor()) as cursor:
|
22
|
-
cursor.execute(
|
23
|
-
"SELECT 1 FROM pg_extension WHERE extname = %s",
|
24
|
-
(name,)
|
25
|
-
)
|
22
|
+
cursor.execute('SELECT 1 FROM pg_extension WHERE extname = %s', (name,))
|
26
23
|
return cursor.fetchone() is not None
|
27
24
|
|
28
25
|
|
@@ -1,4 +1,5 @@
|
|
1
1
|
"""Средства для работы со шрифтами."""
|
2
|
+
|
2
3
|
import os.path
|
3
4
|
|
4
5
|
from PIL import (
|
@@ -51,6 +52,7 @@ def _split_word(font, word, max_width):
|
|
51
52
|
|
52
53
|
:rtype: tuple: (<Часть слова, умещающаяся в max_width>, <остаток>).
|
53
54
|
"""
|
55
|
+
|
54
56
|
def _split_index(index, max_index=None):
|
55
57
|
"""Рекурсивно разбивает слово на две части.
|
56
58
|
|
@@ -59,14 +61,12 @@ def _split_word(font, word, max_width):
|
|
59
61
|
больше максимальной.
|
60
62
|
"""
|
61
63
|
index = int(index)
|
62
|
-
assert index > 0,
|
63
|
-
'чтобы поместить один символ')
|
64
|
+
assert index > 0, 'Ширины строки не хватает, чтобы поместить один символ'
|
64
65
|
|
65
66
|
# определяем ширину слова в пикселях
|
66
67
|
width, _ = font.getsize(word[:index])
|
67
68
|
|
68
69
|
if width > max_width:
|
69
|
-
|
70
70
|
if index == max_index:
|
71
71
|
# Если индексы равны, значит предыдущее условие было
|
72
72
|
# width < max_width, а предыдущий index на единицу меньше.
|
@@ -77,7 +77,6 @@ def _split_word(font, word, max_width):
|
|
77
77
|
return _split_index(index // 2, index)
|
78
78
|
|
79
79
|
elif width < max_width:
|
80
|
-
|
81
80
|
if max_index is None:
|
82
81
|
# Нас обманули, слово влазит в одну строку.
|
83
82
|
return index
|
@@ -23,8 +23,7 @@ class LicenceError(Exception):
|
|
23
23
|
class Licence:
|
24
24
|
"""Базовый класс лицензии."""
|
25
25
|
|
26
|
-
def __init__(self, licence_file_path, schema_file_path, config,
|
27
|
-
params_root='//BarsLicence/LicenceData'):
|
26
|
+
def __init__(self, licence_file_path, schema_file_path, config, params_root='//BarsLicence/LicenceData'):
|
28
27
|
"""Инициализация экземпляра класса.
|
29
28
|
|
30
29
|
:param basestring licence_file_path: Путь к файлу лицензии.
|
@@ -67,14 +66,10 @@ class Licence:
|
|
67
66
|
существует, либо к нему нет доступа.
|
68
67
|
"""
|
69
68
|
if not os.path.exists(file_path):
|
70
|
-
raise ImproperlyConfigured(
|
71
|
-
'Licence file not found: ' + file_path
|
72
|
-
)
|
69
|
+
raise ImproperlyConfigured(f'Licence file not found: {file_path}')
|
73
70
|
|
74
71
|
if not os.access(file_path, os.R_OK):
|
75
|
-
raise ImproperlyConfigured(
|
76
|
-
"Can't read licence file: " + file_path
|
77
|
-
)
|
72
|
+
raise ImproperlyConfigured(f"Can't read licence file: {file_path}")
|
78
73
|
|
79
74
|
@cached_property
|
80
75
|
def _params_elements(self):
|
@@ -97,14 +92,10 @@ class Licence:
|
|
97
92
|
schema_uri=schema_uri,
|
98
93
|
)
|
99
94
|
except etree.XMLSyntaxError as error:
|
100
|
-
raise LicenceError(
|
101
|
-
'Error parsing licence XML document {}:\n{}'
|
102
|
-
.format(self.licence_file_path, str(error))
|
103
|
-
)
|
95
|
+
raise LicenceError('Error parsing licence XML document {}:\n{}'.format(self.licence_file_path, str(error)))
|
104
96
|
|
105
97
|
result = document_tree.xpath(self._params_root)[0]
|
106
98
|
|
107
|
-
|
108
99
|
# поскольку промежуточный результат в памяти хранить необязательно,
|
109
100
|
# а необходим он только в 2 местах, кладем его в список 2 раза, после
|
110
101
|
# этого достаем в каждом и не храним в памяти промежуточные результаты
|
@@ -131,9 +122,7 @@ class Licence:
|
|
131
122
|
"""
|
132
123
|
params_root_element = self._params_elements.pop()
|
133
124
|
|
134
|
-
return set(
|
135
|
-
params_root_element.xpath('plugins/plugin/attribute::name')
|
136
|
-
)
|
125
|
+
return set(params_root_element.xpath('plugins/plugin/attribute::name'))
|
137
126
|
|
138
127
|
def __getattr__(self, attr_name):
|
139
128
|
return self._params[attr_name]
|
educommon/utils/misc.py
CHANGED
@@ -19,7 +19,7 @@ class cached_property(property):
|
|
19
19
|
"""
|
20
20
|
|
21
21
|
def __init__(self, method):
|
22
|
-
super(
|
22
|
+
super().__init__(method)
|
23
23
|
|
24
24
|
self.__doc__ = method.__doc__
|
25
25
|
|
@@ -66,11 +66,7 @@ def get_nested_attr(obj, attr, default=Undefined):
|
|
66
66
|
if default is not Undefined:
|
67
67
|
return default
|
68
68
|
else:
|
69
|
-
raise AttributeError(
|
70
|
-
"'{}' object has no attribute '{}'".format(
|
71
|
-
type(obj), nested_attribute
|
72
|
-
)
|
73
|
-
)
|
69
|
+
raise AttributeError("'{}' object has no attribute '{}'".format(type(obj), nested_attribute))
|
74
70
|
|
75
71
|
return nested_object
|
76
72
|
|
@@ -78,7 +74,7 @@ def get_nested_attr(obj, attr, default=Undefined):
|
|
78
74
|
def md5sum(filepath):
|
79
75
|
"""Возвращает контрольную сумму MD5 указанного файла.
|
80
76
|
|
81
|
-
:param
|
77
|
+
:param str filepath: Путь к файлу.
|
82
78
|
|
83
79
|
:rtype: str
|
84
80
|
"""
|
@@ -113,9 +109,10 @@ def get_mime_type_for_extension(extension):
|
|
113
109
|
:rtype: str
|
114
110
|
"""
|
115
111
|
import mimetypes
|
112
|
+
|
116
113
|
mimetypes.init()
|
117
114
|
if not extension.startswith('.'):
|
118
|
-
extension = '
|
115
|
+
extension = f'.{extension}'
|
119
116
|
return mimetypes.types_map.get(extension.lower())
|
120
117
|
|
121
118
|
|
@@ -131,19 +128,13 @@ def message_to_sentry(message=None, extra=None, tag=None, level=logging.INFO):
|
|
131
128
|
from sentry_sdk import (
|
132
129
|
capture_message,
|
133
130
|
)
|
134
|
-
|
135
|
-
|
136
|
-
level=level,
|
137
|
-
extras=extra,
|
138
|
-
tags={'tags': tag} if tag else None
|
139
|
-
)
|
131
|
+
|
132
|
+
capture_message(message=message, level=level, extras=extra, tags={'tags': tag} if tag else None)
|
140
133
|
else:
|
141
134
|
from raven.contrib.django.raven_compat.models import (
|
142
135
|
client as sentry_client,
|
143
136
|
)
|
137
|
+
|
144
138
|
sentry_client.captureMessage(
|
145
|
-
message=message,
|
146
|
-
data={'level': level},
|
147
|
-
extra=extra,
|
148
|
-
tags={'tags': tag} if tag else None
|
139
|
+
message=message, data={'level': level}, extra=extra, tags={'tags': tag} if tag else None
|
149
140
|
)
|
educommon/utils/object_grid.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
"""Утилиты для ObjectGrid."""
|
2
|
+
|
2
3
|
import json
|
3
4
|
|
4
5
|
from m3.actions.urls import (
|
@@ -31,18 +32,14 @@ def add_action_button(label, grid, action, icon_cls, index=None):
|
|
31
32
|
add_action_button('Печать', grid, self.print_action, Icons.PRINTER)
|
32
33
|
"""
|
33
34
|
action_url = get_url(action)
|
34
|
-
button = ext.ExtButton(
|
35
|
-
text=label,
|
36
|
-
handler=_get_action_handler(action_url),
|
37
|
-
icon_cls=icon_cls)
|
35
|
+
button = ext.ExtButton(text=label, handler=_get_action_handler(action_url), icon_cls=icon_cls)
|
38
36
|
if index is not None:
|
39
37
|
grid.top_bar.items.insert(index, button)
|
40
38
|
else:
|
41
39
|
grid.top_bar.items.append(button)
|
42
40
|
|
43
41
|
|
44
|
-
def add_one_row_button(label, grid, action, icon_cls,
|
45
|
-
dbl_clicked=False, index=None):
|
42
|
+
def add_one_row_button(label, grid, action, icon_cls, dbl_clicked=False, index=None):
|
46
43
|
"""
|
47
44
|
Добавление кнопки в тулбар и popup меню ObjectGrid'а.
|
48
45
|
|
@@ -81,8 +78,7 @@ def add_one_row_button(label, grid, action, icon_cls,
|
|
81
78
|
grid.handler_dblclick = grid.dblclick_handler
|
82
79
|
|
83
80
|
|
84
|
-
def add_multi_row_button(label, grid, action, icon_cls,
|
85
|
-
confirm_required=False, index=None):
|
81
|
+
def add_multi_row_button(label, grid, action, icon_cls, confirm_required=False, index=None):
|
86
82
|
"""
|
87
83
|
Добавление кнопки в тулбар и popup меню ObjectGrid'а.
|
88
84
|
|
@@ -137,27 +133,27 @@ def _get_multirow_params(label, action, icon_cls, confirm_required):
|
|
137
133
|
|
138
134
|
|
139
135
|
def _get_action_handler(url):
|
140
|
-
return """
|
141
|
-
function(){
|
142
|
-
onObjGridAction(objGrid, '
|
143
|
-
}
|
144
|
-
"""
|
136
|
+
return f"""
|
137
|
+
function() {{
|
138
|
+
onObjGridAction(objGrid, '{url}');
|
139
|
+
}}
|
140
|
+
"""
|
145
141
|
|
146
142
|
|
147
143
|
def _get_one_row_handler(action_name, url):
|
148
|
-
return """
|
149
|
-
function(){
|
150
|
-
onObjGridOneRecordAction(objGrid, '
|
151
|
-
}
|
152
|
-
"""
|
144
|
+
return f"""
|
145
|
+
function() {{
|
146
|
+
onObjGridOneRecordAction(objGrid, '{action_name}', '{url}');
|
147
|
+
}}
|
148
|
+
"""
|
153
149
|
|
154
150
|
|
155
151
|
def _get_multi_row_handler(action_name, url, confirm_required):
|
156
|
-
return """
|
157
|
-
function(){
|
158
|
-
onObjGridMultiRecordAction(objGrid, '
|
159
|
-
}
|
160
|
-
"""
|
152
|
+
return f"""
|
153
|
+
function() {{
|
154
|
+
onObjGridMultiRecordAction(objGrid, '{action_name}', '{url}', {int(bool(confirm_required))});
|
155
|
+
}}
|
156
|
+
"""
|
161
157
|
|
162
158
|
|
163
159
|
def column_style_renderer(styles_map, default_style=''):
|
@@ -166,13 +162,15 @@ def column_style_renderer(styles_map, default_style=''):
|
|
166
162
|
:param styles_map: словарь карты стилей по значению в колонке
|
167
163
|
:param default_style: стиль по-умолчанию
|
168
164
|
"""
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
165
|
+
styles_map = json.dumps(styles_map)
|
166
|
+
|
167
|
+
return f"""
|
168
|
+
function (value, metaData, record, rowIndex, colIndex, store) {{
|
169
|
+
var styles_map = Ext.util.JSON.decode('{styles_map}');
|
170
|
+
metaData.style += styles_map[value] || '{default_style}';
|
171
|
+
return value;
|
172
|
+
}}
|
173
|
+
"""
|
176
174
|
|
177
175
|
|
178
176
|
def set_grid_initial(grid, initializers):
|
@@ -194,12 +192,12 @@ def set_grid_initial(grid, initializers):
|
|
194
192
|
))
|
195
193
|
"""
|
196
194
|
grid_initializers = ''.join(initializers)
|
197
|
-
grid._listeners['added'] = """
|
198
|
-
function() {
|
199
|
-
var grid = Ext.getCmp('
|
200
|
-
|
201
|
-
}
|
202
|
-
"""
|
195
|
+
grid._listeners['added'] = f"""
|
196
|
+
function() {{
|
197
|
+
var grid = Ext.getCmp('{grid.client_id}');
|
198
|
+
{grid_initializers}
|
199
|
+
}}
|
200
|
+
"""
|
203
201
|
|
204
202
|
|
205
203
|
def styling_grid_rows(data_index, styles_map, default_style=''):
|
@@ -209,18 +207,16 @@ def styling_grid_rows(data_index, styles_map, default_style=''):
|
|
209
207
|
:param styles_map: словарь карты стилей по значению в колонке
|
210
208
|
:param default_style: стиль по-умолчанию
|
211
209
|
"""
|
212
|
-
|
213
|
-
|
214
|
-
|
210
|
+
styles_map = json.dumps(styles_map)
|
211
|
+
|
212
|
+
return f"""
|
213
|
+
var styles_map = Ext.util.JSON.decode('{styles_map}');
|
214
|
+
grid.getView().getRowClass = function(record, rowIndex, rp, ds) {{
|
215
215
|
return styles_map[
|
216
|
-
record.json
|
217
|
-
] || '
|
218
|
-
};
|
219
|
-
"""
|
220
|
-
'data_index': data_index,
|
221
|
-
'styles_map': json.dumps(styles_map),
|
222
|
-
'default_style': default_style,
|
223
|
-
}
|
216
|
+
record.json.{data_index}
|
217
|
+
] || '{default_style}';
|
218
|
+
}};
|
219
|
+
"""
|
224
220
|
|
225
221
|
|
226
222
|
def add_tooltip_to_grid_rows(delegate: str, column_with_text: str) -> str:
|
@@ -229,26 +225,23 @@ def add_tooltip_to_grid_rows(delegate: str, column_with_text: str) -> str:
|
|
229
225
|
:param delegate: css-класс строк к которым добавляется подсказка
|
230
226
|
:param column_with_text: наименование колонки содержащей текст подсказки
|
231
227
|
"""
|
232
|
-
return """
|
228
|
+
return f"""
|
233
229
|
var view = grid.getView();
|
234
230
|
var store = grid.getStore();
|
235
|
-
grid.on('render', function(grid) {
|
236
|
-
grid.tooltip = new Ext.ToolTip({
|
231
|
+
grid.on('render', function(grid) {{
|
232
|
+
grid.tooltip = new Ext.ToolTip({{
|
237
233
|
target: view.mainBody,
|
238
|
-
delegate: '
|
239
|
-
listeners: {
|
240
|
-
show: function updateTipBody(tooltip) {
|
234
|
+
delegate: '{delegate}',
|
235
|
+
listeners: {{
|
236
|
+
show: function updateTipBody(tooltip) {{
|
241
237
|
var rowIndex = view.findRowIndex(tooltip.triggerElement);
|
242
|
-
var text = store.getAt(rowIndex).data['
|
238
|
+
var text = store.getAt(rowIndex).data['{column_with_text}'];
|
243
239
|
tooltip.update(text);
|
244
|
-
}
|
245
|
-
}
|
246
|
-
});
|
247
|
-
});
|
248
|
-
"""
|
249
|
-
'delegate': delegate,
|
250
|
-
'column': column_with_text,
|
251
|
-
}
|
240
|
+
}}
|
241
|
+
}}
|
242
|
+
}});
|
243
|
+
}});
|
244
|
+
"""
|
252
245
|
|
253
246
|
|
254
247
|
def boolean_column_renderer():
|
@@ -69,9 +69,7 @@ class PhoneField(CharField):
|
|
69
69
|
kwargs.setdefault('max_length', 31)
|
70
70
|
super().__init__(*args, **kwargs)
|
71
71
|
|
72
|
-
self.validators.append(
|
73
|
-
PHONE_FIELD_TYPE_TO_VALIDATOR.get(phone_type, validate_common_phone_number)
|
74
|
-
)
|
72
|
+
self.validators.append(PHONE_FIELD_TYPE_TO_VALIDATOR.get(phone_type, validate_common_phone_number))
|
75
73
|
|
76
74
|
def to_python(self, value):
|
77
75
|
"""Преобразование входящего значения в ожидаемый тип Python."""
|
@@ -11,12 +11,12 @@ from educommon.utils.phone_number.enums import (
|
|
11
11
|
|
12
12
|
|
13
13
|
PHONE_REGEX = re.compile(
|
14
|
-
r'^(?:'
|
15
|
-
r'(\+[1-6,90]{1,3}|\+?7|\+?8)?-?\s*'
|
14
|
+
r'^(?:' # группа с кодом страны и кодом региона
|
15
|
+
r'(\+[1-6,90]{1,3}|\+?7|\+?8)?-?\s*' # опциональный код страны (+XXX, для России может быть +7, 7, 8)
|
16
16
|
r'\(?(\d{3}(?:[-\s]?\d{0,2}(?=[)-.\s]))?)\)?[-.\s]*' # оцпиональный код региона/города в необязательных скобках
|
17
|
-
r')?'
|
17
|
+
r')?' # группа с кодом страны и кодом региона может отсутствовать
|
18
18
|
r'(\d{1,2}[-.\s]*\d?)[-.\s]*(\d[-.\s]*\d)[-.\s]*(\d[-.\s]*\d)$' # абонентский номер в формате XXX-XX-XX
|
19
|
-
|
19
|
+
# первая группа может состоять от 1 до 3 цифр
|
20
20
|
)
|
21
21
|
|
22
22
|
# российский мобильный номер вида +7 (9XX) XXX-XX-XX
|
@@ -177,10 +177,7 @@ class PhoneNumber:
|
|
177
177
|
@property
|
178
178
|
def is_russia(self) -> bool:
|
179
179
|
"""Признак, что номер российский."""
|
180
|
-
return (
|
181
|
-
self._cleaned.startswith(RU_PHONE_CODE)
|
182
|
-
or (self.is_valid and not self.region_code)
|
183
|
-
)
|
180
|
+
return self._cleaned.startswith(RU_PHONE_CODE) or (self.is_valid and not self.region_code)
|
184
181
|
|
185
182
|
@property
|
186
183
|
def cleaned(self) -> str:
|
@@ -18,8 +18,7 @@ def validate_common_phone_number(phone_number: Union[str, PhoneNumber]):
|
|
18
18
|
|
19
19
|
if isinstance(phone_number, PhoneNumber) and not phone_number.is_valid:
|
20
20
|
raise ValidationError(
|
21
|
-
'Неверный формат! Примеры допустимых форматов: '
|
22
|
-
'+XXX (XXX) XXX XX XX, 8 (XXXXX) X-XX-XX, XXX-XX-XX',
|
21
|
+
'Неверный формат! Примеры допустимых форматов: +XXX (XXX) XXX XX XX, 8 (XXXXX) X-XX-XX, XXX-XX-XX',
|
23
22
|
code='invalid',
|
24
23
|
)
|
25
24
|
|
@@ -28,14 +27,9 @@ def validate_e164_phone_number(phone_number: Union[str, PhoneNumber]):
|
|
28
27
|
"""Валидация номера телефона в международном формате."""
|
29
28
|
phone_number = to_python(phone_number)
|
30
29
|
|
31
|
-
if (
|
32
|
-
isinstance(phone_number, PhoneNumber)
|
33
|
-
and not phone_number.is_valid
|
34
|
-
or not phone_number.is_e164
|
35
|
-
):
|
30
|
+
if isinstance(phone_number, PhoneNumber) and not phone_number.is_valid or not phone_number.is_e164:
|
36
31
|
raise ValidationError(
|
37
|
-
'Неверный формат! Примеры допустимых форматов: '
|
38
|
-
'+XXX (XXX) XXX XX XX, +7 (XXX) XXX-XX-XX, 8 (XXXXX) X-XX-XX',
|
32
|
+
'Неверный формат! Примеры допустимых форматов: +XXX (XXX) XXX XX XX, +7 (XXX) XXX-XX-XX, 8 (XXXXX) X-XX-XX',
|
39
33
|
code='invalid',
|
40
34
|
)
|
41
35
|
|
@@ -44,14 +38,9 @@ def validate_ru_phone_number(phone_number: Union[str, PhoneNumber]):
|
|
44
38
|
"""Валидация российского номера телефона в общем формате."""
|
45
39
|
phone_number = to_python(phone_number)
|
46
40
|
|
47
|
-
if (
|
48
|
-
isinstance(phone_number, PhoneNumber)
|
49
|
-
and not phone_number.is_valid
|
50
|
-
or not phone_number.is_russia
|
51
|
-
):
|
41
|
+
if isinstance(phone_number, PhoneNumber) and not phone_number.is_valid or not phone_number.is_russia:
|
52
42
|
raise ValidationError(
|
53
|
-
'Неверный формат! Примеры допустимых форматов: '
|
54
|
-
'+7 (XXX) XXX-XX-XX, 8 (XXXXX) X-XX-XX, XXX-XX-XX',
|
43
|
+
'Неверный формат! Примеры допустимых форматов: +7 (XXX) XXX-XX-XX, 8 (XXXXX) X-XX-XX, XXX-XX-XX',
|
55
44
|
code='invalid',
|
56
45
|
)
|
57
46
|
|
@@ -66,8 +55,7 @@ def validate_ru_e164_phone_number(phone_number: Union[str, PhoneNumber]):
|
|
66
55
|
or not (phone_number.is_russia and phone_number.is_e164)
|
67
56
|
):
|
68
57
|
raise ValidationError(
|
69
|
-
'Неверный формат! Примеры допустимых форматов: '
|
70
|
-
'+7 (XXX) XXX-XX-XX, 8 (XXXXX) X-XX-XX, (XXX) XXX-XX-XX',
|
58
|
+
'Неверный формат! Примеры допустимых форматов: +7 (XXX) XXX-XX-XX, 8 (XXXXX) X-XX-XX, (XXX) XXX-XX-XX',
|
71
59
|
code='invalid',
|
72
60
|
)
|
73
61
|
|
@@ -80,13 +68,10 @@ def validate_ru_mobile_phone_number(phone_number: Union[str, PhoneNumber]):
|
|
80
68
|
isinstance(phone_number, PhoneNumber)
|
81
69
|
and not phone_number.is_valid
|
82
70
|
or not (
|
83
|
-
phone_number.region_code
|
84
|
-
and len(phone_number.region_code) == 3
|
85
|
-
and phone_number.region_code.startswith('9')
|
71
|
+
phone_number.region_code and len(phone_number.region_code) == 3 and phone_number.region_code.startswith('9')
|
86
72
|
)
|
87
73
|
):
|
88
74
|
raise ValidationError(
|
89
|
-
'Неверный формат! Примеры допустимых форматов: '
|
90
|
-
'+7 (9XX) XXX-XX-XX, 7 (9XX) XXX XX XX, 89XXXXXXXXX',
|
75
|
+
'Неверный формат! Примеры допустимых форматов: +7 (9XX) XXX-XX-XX, 7 (9XX) XXX XX XX, 89XXXXXXXXX',
|
91
76
|
code='invalid',
|
92
77
|
)
|
educommon/utils/plugins.py
CHANGED
@@ -29,7 +29,6 @@ __all__ = ['extender_for']
|
|
29
29
|
|
30
30
|
|
31
31
|
class _ExtenderRegistry(metaclass=SingletonMeta):
|
32
|
-
|
33
32
|
"""Реестр расширителей классов.
|
34
33
|
|
35
34
|
Обеспечивает работу декораторов
|
@@ -90,13 +89,7 @@ def _function_types():
|
|
90
89
|
WrapperDescriptorType,
|
91
90
|
)
|
92
91
|
|
93
|
-
return (
|
94
|
-
FunctionType,
|
95
|
-
MethodType,
|
96
|
-
WrapperDescriptorType,
|
97
|
-
MethodWrapperType,
|
98
|
-
MethodDescriptorType
|
99
|
-
)
|
92
|
+
return (FunctionType, MethodType, WrapperDescriptorType, MethodWrapperType, MethodDescriptorType)
|
100
93
|
|
101
94
|
|
102
95
|
_function_types = lru_cache(maxsize=1)(_function_types)
|
@@ -172,10 +165,8 @@ def _extendable(func):
|
|
172
165
|
:caption: Пример использования
|
173
166
|
|
174
167
|
class EditWindow(BaseEditWindow):
|
175
|
-
|
176
168
|
@extendable
|
177
|
-
def set_params(self, params):
|
178
|
-
...
|
169
|
+
def set_params(self, params): ...
|
179
170
|
"""
|
180
171
|
assert isinstance(func, _function_types()), func
|
181
172
|
|
@@ -184,12 +175,13 @@ def _extendable(func):
|
|
184
175
|
from m3_django_compat import (
|
185
176
|
WrapperDescriptorType,
|
186
177
|
)
|
187
|
-
unbound_types = (FunctionType, WrapperDescriptorType,)
|
188
178
|
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
)
|
179
|
+
unbound_types = (
|
180
|
+
FunctionType,
|
181
|
+
WrapperDescriptorType,
|
182
|
+
)
|
183
|
+
|
184
|
+
assert all((isinstance(func, unbound_types), not isclass(self))), 'Нельзя расширить staticmethod и classmethod'
|
193
185
|
|
194
186
|
result = func(self, *args, **kwargs)
|
195
187
|
|
@@ -222,9 +214,9 @@ def get_plugin_apps():
|
|
222
214
|
app_config
|
223
215
|
for app_config in apps.get_app_configs()
|
224
216
|
if (
|
225
|
-
hasattr(app_config.module, 'plugin_meta')
|
226
|
-
hasattr(app_config.module.plugin_meta, 'connect_plugin')
|
227
|
-
callable(app_config.module.plugin_meta.connect_plugin)
|
217
|
+
hasattr(app_config.module, 'plugin_meta')
|
218
|
+
and hasattr(app_config.module.plugin_meta, 'connect_plugin')
|
219
|
+
and callable(app_config.module.plugin_meta.connect_plugin)
|
228
220
|
)
|
229
221
|
)
|
230
222
|
|
@@ -250,20 +242,15 @@ def init_plugin(package, settings=None, config=None):
|
|
250
242
|
{}, # globals
|
251
243
|
{}, # locals
|
252
244
|
['connect_plugin'], # from list
|
253
|
-
0 # absolute import
|
245
|
+
0, # absolute import
|
254
246
|
)
|
255
247
|
except ImportError:
|
256
|
-
raise ImproperlyConfigured(
|
257
|
-
'{} is not plugin application.'.format(package)
|
258
|
-
)
|
248
|
+
raise ImproperlyConfigured('{} is not plugin application.'.format(package))
|
259
249
|
|
260
250
|
meta_module.__package__ = package
|
261
251
|
|
262
|
-
if
|
263
|
-
not callable(meta_module.connect_plugin)):
|
252
|
+
if not hasattr(meta_module, 'connect_plugin') or not callable(meta_module.connect_plugin):
|
264
253
|
raise ImproperlyConfigured(
|
265
|
-
'{} is not a plugin application '
|
266
|
-
'(module {} has not connect_plugin function)'
|
267
|
-
.format(package, meta_module)
|
254
|
+
'{} is not a plugin application (module {} has not connect_plugin function)'.format(package, meta_module)
|
268
255
|
)
|
269
256
|
meta_module.connect_plugin(settings, config or {})
|
educommon/utils/registry.py
CHANGED
@@ -10,7 +10,8 @@ class ModelHandlerRegistry:
|
|
10
10
|
assert isinstance(handlers, dict)
|
11
11
|
assert all(map(callable, handlers.values()))
|
12
12
|
|
13
|
-
super(
|
13
|
+
super().__init__()
|
14
|
+
|
14
15
|
self._handlers = handlers
|
15
16
|
|
16
17
|
def get_model_handler(self, model):
|
educommon/utils/seqtools.py
CHANGED
@@ -17,7 +17,5 @@ def make_chunks(
|
|
17
17
|
|
18
18
|
for first in iterator:
|
19
19
|
yield (
|
20
|
-
list(chain([first], islice(iterator, size - 1)))
|
21
|
-
if is_list
|
22
|
-
else chain([first], islice(iterator, size - 1))
|
20
|
+
list(chain([first], islice(iterator, size - 1))) if is_list else chain([first], islice(iterator, size - 1))
|
23
21
|
)
|