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/report/utils.py
CHANGED
@@ -39,10 +39,7 @@ def get_cell_bounds(section, row_index, column_index):
|
|
39
39
|
"""
|
40
40
|
merged_ranges = section.writer.wtsheet.merged_ranges
|
41
41
|
for first_row, last_row, first_column, last_column in merged_ranges:
|
42
|
-
if
|
43
|
-
first_row <= row_index <= last_row and
|
44
|
-
first_column <= column_index <= last_column
|
45
|
-
):
|
42
|
+
if first_row <= row_index <= last_row and first_column <= column_index <= last_column:
|
46
43
|
return first_row, last_row, first_column, last_column
|
47
44
|
|
48
45
|
return row_index, row_index, column_index, column_index
|
@@ -69,13 +66,8 @@ def get_cell_width(section, row_index, column_index):
|
|
69
66
|
|
70
67
|
:rtype: int
|
71
68
|
"""
|
72
|
-
_, _, first_column, last_column = get_cell_bounds(
|
73
|
-
|
74
|
-
)
|
75
|
-
return sum(
|
76
|
-
section.writer.wtsheet.col(i).width
|
77
|
-
for i in range(first_column, last_column + 1)
|
78
|
-
)
|
69
|
+
_, _, first_column, last_column = get_cell_bounds(section, row_index, column_index)
|
70
|
+
return sum(section.writer.wtsheet.col(i).width for i in range(first_column, last_column + 1))
|
79
71
|
|
80
72
|
|
81
73
|
def get_cell_height(section, row_index, column_index):
|
@@ -95,17 +87,11 @@ def get_cell_height(section, row_index, column_index):
|
|
95
87
|
:returns: Высоту ячейки в `твипах <https://goo.gl/hfUW7x>`_.
|
96
88
|
:rtype: int
|
97
89
|
"""
|
98
|
-
first_row, last_row, _, _ = get_cell_bounds(
|
99
|
-
|
100
|
-
)
|
101
|
-
return sum(
|
102
|
-
section.writer.wtsheet.row(i).height
|
103
|
-
for i in range(first_row, last_row + 1)
|
104
|
-
)
|
90
|
+
first_row, last_row, _, _ = get_cell_bounds(section, row_index, column_index)
|
91
|
+
return sum(section.writer.wtsheet.row(i).height for i in range(first_row, last_row + 1))
|
105
92
|
|
106
93
|
|
107
|
-
def adjust_row_height(section, row_idx, col_idx, text, font,
|
108
|
-
adjusted_row_index=None):
|
94
|
+
def adjust_row_height(section, row_idx, col_idx, text, font, adjusted_row_index=None):
|
109
95
|
"""Увеличивает высоту строки, если необходимо.
|
110
96
|
|
111
97
|
Высота строки устанавливается такая, чтобы текст `text`
|
@@ -135,6 +121,7 @@ def adjust_row_height(section, row_idx, col_idx, text, font,
|
|
135
121
|
ячейки будет увеличена равномерно. Номер указывается относительно
|
136
122
|
ячейки, нумерация начинается с нуля.
|
137
123
|
"""
|
124
|
+
|
138
125
|
def get_text_height(column_width):
|
139
126
|
"""Вычисляет высоту строки отчета в зависимости от текста."""
|
140
127
|
# Т.к. высота и ширина ячейки измеряется в единицах относительно
|
@@ -145,9 +132,7 @@ def adjust_row_height(section, row_idx, col_idx, text, font,
|
|
145
132
|
# DEFAULT_CHAR_SIZE * 0.8 определяет ширину отступов слева и справа (в
|
146
133
|
# сумме, а не каждого отступа по отдельности). Источник точной
|
147
134
|
# информации об отступах в ячейке найти не удалось.
|
148
|
-
width_in_chars = (
|
149
|
-
int(column_width - DEFAULT_CHAR_SIZE * 0.8) // DEFAULT_CHAR_SIZE
|
150
|
-
)
|
135
|
+
width_in_chars = int(column_width - DEFAULT_CHAR_SIZE * 0.8) // DEFAULT_CHAR_SIZE
|
151
136
|
|
152
137
|
str_width_px, str_height_px = normal_font.getsize('0' * width_in_chars)
|
153
138
|
|
@@ -155,17 +140,13 @@ def adjust_row_height(section, row_idx, col_idx, text, font,
|
|
155
140
|
_, choosen_font_height_px = font.getsize('0')
|
156
141
|
height_scale = (choosen_font_height_px * 1.0) / str_height_px
|
157
142
|
|
158
|
-
rows_count = len(
|
159
|
-
split_text(text, font, str_width_px)
|
160
|
-
)
|
143
|
+
rows_count = len(split_text(text, font, str_width_px))
|
161
144
|
|
162
145
|
height = int(rows_count * height_scale * DEFAULT_CHAR_SIZE)
|
163
146
|
|
164
147
|
return height
|
165
148
|
|
166
|
-
first_row, last_row, first_column, last_column = get_cell_bounds(
|
167
|
-
section, row_idx, col_idx
|
168
|
-
)
|
149
|
+
first_row, last_row, first_column, last_column = get_cell_bounds(section, row_idx, col_idx)
|
169
150
|
|
170
151
|
merged = first_row != last_row or first_column != last_column
|
171
152
|
|
@@ -192,20 +173,15 @@ def adjust_row_height(section, row_idx, col_idx, text, font,
|
|
192
173
|
row = section.writer.wtsheet.row(r)
|
193
174
|
add_row_height(row, height_delta // row_count)
|
194
175
|
else:
|
195
|
-
assert adjusted_row_index > row_count, (
|
196
|
-
|
197
|
-
)
|
198
|
-
row = section.writer.wtsheet.row(
|
199
|
-
first_row + adjusted_row_index
|
200
|
-
)
|
176
|
+
assert adjusted_row_index > row_count, (adjusted_row_index, row_count)
|
177
|
+
row = section.writer.wtsheet.row(first_row + adjusted_row_index)
|
201
178
|
add_row_height(row, height_delta)
|
202
179
|
else:
|
203
180
|
row = section.writer.wtsheet.row(row_idx)
|
204
181
|
add_row_height(row, height_delta)
|
205
182
|
|
206
183
|
|
207
|
-
def adjust_row_height_arial(section, row_idx, col_idx, text, font_size=10,
|
208
|
-
adjusted_row_index=None):
|
184
|
+
def adjust_row_height_arial(section, row_idx, col_idx, text, font_size=10, adjusted_row_index=None):
|
209
185
|
"""Устанавливает высоту строки автоматически.
|
210
186
|
|
211
187
|
Высота строки устанавливается такая, чтобы текст text
|
@@ -226,6 +202,4 @@ def adjust_row_height_arial(section, row_idx, col_idx, text, font_size=10,
|
|
226
202
|
ячейки, нумерация начинается с нуля.
|
227
203
|
"""
|
228
204
|
font = get_font(ARIAL, font_size)
|
229
|
-
adjust_row_height(
|
230
|
-
section, row_idx, col_idx, text, font, adjusted_row_index
|
231
|
-
)
|
205
|
+
adjust_row_height(section, row_idx, col_idx, text, font, adjusted_row_index)
|
educommon/rest/actions.py
CHANGED
@@ -8,7 +8,7 @@ from educommon.rest import (
|
|
8
8
|
|
9
9
|
|
10
10
|
class BaseRestPack(BasePack):
|
11
|
-
"""Базовый пак для всех REST
|
11
|
+
"""Базовый пак для всех REST паков."""
|
12
12
|
|
13
13
|
|
14
14
|
class RestPack(
|
@@ -17,10 +17,10 @@ class RestPack(
|
|
17
17
|
mixins.CreateModelMixin,
|
18
18
|
mixins.UpdateModelMixin,
|
19
19
|
mixins.DestroyModelMixin,
|
20
|
-
BaseRestPack
|
20
|
+
BaseRestPack,
|
21
21
|
):
|
22
|
-
"""
|
23
|
-
|
22
|
+
"""Пак для обработки запросов методами: GET, POST, PUT, PATCH, DELETE.
|
23
|
+
|
24
24
|
Для обработки запросов методом GET исользуются методы пака list и retrieve.
|
25
25
|
Для обработки запросов методом POST используется метод пака create.
|
26
26
|
Для обработки запросов методом PUT и PATCH используется метод пака update.
|
@@ -28,12 +28,8 @@ class RestPack(
|
|
28
28
|
"""
|
29
29
|
|
30
30
|
|
31
|
-
class RestReadOnlyPack(
|
32
|
-
|
33
|
-
|
34
|
-
BaseRestPack
|
35
|
-
):
|
36
|
-
"""
|
37
|
-
Пак который обрабатывает запросы только методом GET.
|
31
|
+
class RestReadOnlyPack(mixins.ListModelMixin, mixins.RetrieveModelMixin, BaseRestPack):
|
32
|
+
"""Пак который обрабатывает запросы только методом GET.
|
33
|
+
|
38
34
|
Доступные методы пака: list и retrieve.
|
39
35
|
"""
|
educommon/rest/context.py
CHANGED
@@ -12,14 +12,9 @@ from educommon.rest.misc import (
|
|
12
12
|
)
|
13
13
|
|
14
14
|
|
15
|
-
class RestDeclarativeActionContext(
|
16
|
-
ObservableController.VerboseDeclarativeContext
|
17
|
-
):
|
18
|
-
|
15
|
+
class RestDeclarativeActionContext(ObservableController.VerboseDeclarativeContext):
|
19
16
|
def build(self, request, rules):
|
20
|
-
"""
|
21
|
-
Выполняет заполнение собственных атрибутов
|
22
|
-
согласно переданному запросу, исходя из списка правил
|
17
|
+
"""Выполняет заполнение собственных атрибутов согласно переданному запросу, исходя из списка правил.
|
23
18
|
|
24
19
|
:param request:запрос, на основе которого производится
|
25
20
|
заполнение контекста
|
@@ -30,7 +25,7 @@ class RestDeclarativeActionContext(
|
|
30
25
|
|
31
26
|
:raise: TypeError, ContextBuildingError, CriticalContextBuildingError
|
32
27
|
"""
|
33
|
-
assert self.matches(rules),
|
28
|
+
assert self.matches(rules), 'rules must be a dict or pair!'
|
34
29
|
# определяем режим, если правилла описаны парой
|
35
30
|
if isinstance(rules, tuple):
|
36
31
|
# режим
|
@@ -54,10 +49,7 @@ class RestDeclarativeActionContext(
|
|
54
49
|
try:
|
55
50
|
parser = self._parsers[parser]
|
56
51
|
except KeyError:
|
57
|
-
raise TypeError(
|
58
|
-
'Неизвестный парсер контекста: "%s"' %
|
59
|
-
parser
|
60
|
-
)
|
52
|
+
raise TypeError(f'Неизвестный парсер контекста: "{parser}"')
|
61
53
|
|
62
54
|
add_error_to = None
|
63
55
|
try:
|
@@ -84,12 +76,10 @@ class RestDeclarativeActionContext(
|
|
84
76
|
add_error_to = errors
|
85
77
|
|
86
78
|
if add_error_to is not None:
|
87
|
-
add_error_to.append(
|
88
|
-
parser_data.get('verbose_name', key))
|
79
|
+
add_error_to.append(parser_data.get('verbose_name', key))
|
89
80
|
# ошибка критична, если хотя бы один из параметров
|
90
81
|
# не имеет verbose_name
|
91
|
-
only_noncritical = only_noncritical and (
|
92
|
-
'verbose_name' in parser_data)
|
82
|
+
only_noncritical = only_noncritical and ('verbose_name' in parser_data)
|
93
83
|
continue
|
94
84
|
|
95
85
|
setattr(self, key, val)
|
educommon/rest/controllers.py
CHANGED
@@ -24,9 +24,7 @@ class RestObservableController(ObservableController):
|
|
24
24
|
api = None
|
25
25
|
object_id_regex = re.compile(r'/(?P<id>\d+)$')
|
26
26
|
|
27
|
-
class VerboseDeclarativeContext(
|
28
|
-
RestDeclarativeActionContext
|
29
|
-
):
|
27
|
+
class VerboseDeclarativeContext(RestDeclarativeActionContext):
|
30
28
|
def build(self, request, rules):
|
31
29
|
self.__declared = list(rules.keys()) + self.__internal_attrs
|
32
30
|
|
@@ -36,7 +34,7 @@ class RestObservableController(ObservableController):
|
|
36
34
|
if self.__debug:
|
37
35
|
raise
|
38
36
|
else:
|
39
|
-
_warn('
|
37
|
+
_warn(f'{e!r}, url="{request.path_info}"')
|
40
38
|
|
41
39
|
for k, v in list(get_request_params(request).items()):
|
42
40
|
if not hasattr(self, k):
|
@@ -53,17 +51,18 @@ class RestObservableController(ObservableController):
|
|
53
51
|
pass
|
54
52
|
|
55
53
|
def __init__(self, observer, *args, **kwargs):
|
56
|
-
super(
|
57
|
-
|
54
|
+
super().__init__(observer, *args, **kwargs)
|
55
|
+
|
58
56
|
self.api = {}
|
59
57
|
|
60
58
|
def append_pack(self, pack):
|
61
|
-
super(
|
59
|
+
super().append_pack(pack)
|
60
|
+
|
62
61
|
self.api[pack.__class__.__name__] = pack.get_absolute_url()
|
63
62
|
|
64
63
|
def process_request(self, request):
|
65
|
-
"""
|
66
|
-
|
64
|
+
"""Обработка входящего запроса *request* от клиента.
|
65
|
+
|
67
66
|
Экшен определяется исходя из метода запроса.
|
68
67
|
"""
|
69
68
|
|
@@ -73,4 +72,5 @@ class RestObservableController(ObservableController):
|
|
73
72
|
|
74
73
|
path = self.object_id_regex.sub('', request.path)
|
75
74
|
request.path = '{}/{}'.format(path.rstrip('/'), request.method.lower())
|
76
|
-
|
75
|
+
|
76
|
+
return super().process_request(request)
|
educommon/rest/mixins.py
CHANGED
@@ -12,48 +12,50 @@ class BaseRestAction(BaseAction):
|
|
12
12
|
|
13
13
|
|
14
14
|
class ListModelMixin:
|
15
|
-
"""
|
16
|
-
|
15
|
+
"""Примесь для REST паков, обработка запросов метода GET.
|
16
|
+
|
17
17
|
Получения списка объектов.
|
18
18
|
"""
|
19
19
|
|
20
20
|
def __init__(self):
|
21
|
-
super(
|
21
|
+
super().__init__()
|
22
|
+
|
22
23
|
if not hasattr(self, 'get_action'):
|
23
24
|
self.get_action = GetAction()
|
24
25
|
self.actions.append(self.get_action)
|
25
26
|
|
26
27
|
def list(self, request, context):
|
27
|
-
"""
|
28
|
-
|
28
|
+
"""Метод отвечает за обработку запроса методом GET.
|
29
|
+
|
29
30
|
Получение списка объектов.
|
30
31
|
"""
|
31
32
|
raise NotImplementedError
|
32
33
|
|
33
34
|
|
34
35
|
class RetrieveModelMixin:
|
35
|
-
"""
|
36
|
-
|
36
|
+
"""Примесь для REST паков, обработка запросов метода GET.
|
37
|
+
|
37
38
|
Получение конкретно объекта по context.id.
|
38
39
|
"""
|
39
40
|
|
40
41
|
def __init__(self):
|
41
|
-
super(
|
42
|
+
super().__init__()
|
43
|
+
|
42
44
|
if not hasattr(self, 'get_action'):
|
43
45
|
self.get_action = GetAction()
|
44
46
|
self.actions.append(self.get_action)
|
45
47
|
|
46
48
|
def retrieve(self, request, context):
|
47
|
-
"""
|
48
|
-
|
49
|
+
"""Метод отвечает за обработку запроса методом GET.
|
50
|
+
|
49
51
|
Получение конкретного объекта по context.id.
|
50
52
|
"""
|
51
53
|
raise NotImplementedError
|
52
54
|
|
53
55
|
|
54
56
|
class GetAction(BaseRestAction):
|
55
|
-
"""
|
56
|
-
|
57
|
+
"""Экшен обработки запроса методом GET.
|
58
|
+
|
57
59
|
Делегирует обработку методам пака.
|
58
60
|
"""
|
59
61
|
|
@@ -71,7 +73,8 @@ class CreateModelMixin:
|
|
71
73
|
"""Примесь для REST паков, обработка запросов методом POST."""
|
72
74
|
|
73
75
|
def __init__(self):
|
74
|
-
super(
|
76
|
+
super().__init__()
|
77
|
+
|
75
78
|
self.post_action = PostAction()
|
76
79
|
self.actions.append(self.post_action)
|
77
80
|
|
@@ -81,8 +84,8 @@ class CreateModelMixin:
|
|
81
84
|
|
82
85
|
|
83
86
|
class PostAction(BaseRestAction):
|
84
|
-
"""
|
85
|
-
|
87
|
+
"""Экшен обработки запроса методом POST.
|
88
|
+
|
86
89
|
Делегирует обработку методам пака.
|
87
90
|
"""
|
88
91
|
|
@@ -97,13 +100,11 @@ class UpdateModelMixin:
|
|
97
100
|
"""Примесь для REST паков, обработка запросов методом PUT и PATCH."""
|
98
101
|
|
99
102
|
def __init__(self):
|
100
|
-
super(
|
103
|
+
super().__init__()
|
104
|
+
|
101
105
|
self.put_action = PutAction()
|
102
106
|
self.patch_action = PatchAction()
|
103
|
-
self.actions.extend([
|
104
|
-
self.put_action,
|
105
|
-
self.patch_action
|
106
|
-
])
|
107
|
+
self.actions.extend([self.put_action, self.patch_action])
|
107
108
|
|
108
109
|
def update(self, request, context):
|
109
110
|
"""Метод отвечает за изменение объекта."""
|
@@ -111,8 +112,8 @@ class UpdateModelMixin:
|
|
111
112
|
|
112
113
|
|
113
114
|
class PutAction(BaseRestAction):
|
114
|
-
"""
|
115
|
-
|
115
|
+
"""Экшен обработки запроса методом PUT.
|
116
|
+
|
116
117
|
Делегирует обработку методам пака.
|
117
118
|
"""
|
118
119
|
|
@@ -124,8 +125,8 @@ class PutAction(BaseRestAction):
|
|
124
125
|
|
125
126
|
|
126
127
|
class PatchAction(BaseRestAction):
|
127
|
-
"""
|
128
|
-
|
128
|
+
"""Экшен обработки запроса методом PATCH.
|
129
|
+
|
129
130
|
Делегирует обработку методам пака.
|
130
131
|
"""
|
131
132
|
|
@@ -140,7 +141,8 @@ class DestroyModelMixin:
|
|
140
141
|
"""Примесь для REST паков, обработка запросов методом DELETE."""
|
141
142
|
|
142
143
|
def __init__(self):
|
143
|
-
super(
|
144
|
+
super().__init__()
|
145
|
+
|
144
146
|
self.delete_action = DeleteAction()
|
145
147
|
self.actions.append(self.delete_action)
|
146
148
|
|
@@ -150,8 +152,8 @@ class DestroyModelMixin:
|
|
150
152
|
|
151
153
|
|
152
154
|
class DeleteAction(BaseRestAction):
|
153
|
-
"""
|
154
|
-
|
155
|
+
"""Экшен обработки запроса методом PATCH.
|
156
|
+
|
155
157
|
Делегирует обработку методам пака.
|
156
158
|
"""
|
157
159
|
|
@@ -22,24 +22,24 @@ from m3_django_compat import (
|
|
22
22
|
|
23
23
|
|
24
24
|
def check_autorization(request, path):
|
25
|
-
|
26
25
|
# Если файл в media/public, то отдаем сразу без проверки
|
27
26
|
# Вообще это делается соответствующим конфигурированием NGINX
|
28
27
|
path_list = path.split(os.path.sep)
|
29
|
-
if path_list and path_list[0] ==
|
28
|
+
if path_list and path_list[0] == 'public':
|
30
29
|
return sendfile(request, os.path.join(settings.MEDIA_ROOT, path))
|
31
30
|
|
32
31
|
if not is_authenticated(request.user):
|
33
32
|
result = M3JSONEncoder().encode(
|
34
|
-
{
|
35
|
-
|
36
|
-
|
37
|
-
|
33
|
+
{
|
34
|
+
'success': False,
|
35
|
+
'message': 'Вы не авторизованы. Возможно, закончилось время '
|
36
|
+
'пользовательской сессии. Для повторной '
|
37
|
+
'аутентификации обновите страницу.',
|
38
|
+
}
|
39
|
+
)
|
38
40
|
return http.HttpResponse(result, content_type='application/json')
|
39
41
|
|
40
42
|
return sendfile(request, os.path.join(settings.MEDIA_ROOT, path))
|
41
43
|
|
42
44
|
|
43
|
-
urlpatterns = [
|
44
|
-
re_path(r'^media/(?P<path>.*)$', check_autorization)
|
45
|
-
]
|
45
|
+
urlpatterns = [re_path(r'^media/(?P<path>.*)$', check_autorization)]
|
educommon/utils/__init__.py
CHANGED
@@ -57,11 +57,12 @@ class SingletonMeta(type):
|
|
57
57
|
"""
|
58
58
|
|
59
59
|
def __init__(cls, name, bases, attrs):
|
60
|
-
super(
|
60
|
+
super().__init__(name, bases, attrs)
|
61
|
+
|
61
62
|
cls.instance = None
|
62
63
|
|
63
64
|
def __call__(cls, *args, **kwargs):
|
64
65
|
if cls.instance is None:
|
65
|
-
cls.instance = super(
|
66
|
+
cls.instance = super().__call__(*args, **kwargs)
|
66
67
|
|
67
68
|
return cls.instance
|
educommon/utils/caching.py
CHANGED
@@ -87,9 +87,7 @@ class CacheWrapper:
|
|
87
87
|
"""
|
88
88
|
kwarg_vals = tuple(callargs[k] for k in self.names_kwargs)
|
89
89
|
hash_kwargs = hash(kwarg_vals)
|
90
|
-
cache_key = '_'.join((
|
91
|
-
self.cache_prefix, self.fn.__name__, str(hash_kwargs)
|
92
|
-
))
|
90
|
+
cache_key = '_'.join((self.cache_prefix, self.fn.__name__, str(hash_kwargs)))
|
93
91
|
return cache_key
|
94
92
|
|
95
93
|
def update_cache(self, value, **kw):
|
educommon/utils/conversion.py
CHANGED
@@ -72,7 +72,5 @@ def float_or_none(value: Union[int, str, None]) -> Optional[float]:
|
|
72
72
|
return result
|
73
73
|
|
74
74
|
|
75
|
-
control_chars = ''.join(map(
|
76
|
-
chr, itertools.chain(range(0x00, 0x20), range(0x7f, 0xa0))
|
77
|
-
))
|
75
|
+
control_chars = ''.join(map(chr, itertools.chain(range(0x00, 0x20), range(0x7F, 0xA0))))
|
78
76
|
control_char_re = re.compile(f'[{re.escape(control_chars)}]')
|
educommon/utils/crypto.py
CHANGED
@@ -36,8 +36,7 @@ class HashData:
|
|
36
36
|
|
37
37
|
if openssl_stderr:
|
38
38
|
raise IOError(
|
39
|
-
'Ошибка вычисления значения HASH по алгоритму '
|
40
|
-
f'{self.hash_algorithm}',
|
39
|
+
f'Ошибка вычисления значения HASH по алгоритму {self.hash_algorithm}',
|
41
40
|
)
|
42
41
|
|
43
42
|
matches = re.compile(self.HASH_PATTERN).search(openssl_stdout.decode())
|
educommon/utils/date.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
"""Вспомогательные средства для работы с датами."""
|
2
|
+
|
2
3
|
import datetime
|
3
4
|
from collections import (
|
4
5
|
namedtuple,
|
@@ -38,7 +39,7 @@ WEEKDAYS = (
|
|
38
39
|
(THU_IDX, 'Четверг'),
|
39
40
|
(FRI_IDX, 'Пятница'),
|
40
41
|
(SAT_IDX, 'Суббота'),
|
41
|
-
(SUN_IDX, 'Воскресенье')
|
42
|
+
(SUN_IDX, 'Воскресенье'),
|
42
43
|
)
|
43
44
|
WEEKDAYS_DICT = dict(WEEKDAYS)
|
44
45
|
|
@@ -59,6 +60,7 @@ def date_range_to_str(date_from, date_to, can_be_one_day_long=False):
|
|
59
60
|
(<2001.01.01>, None, , True) -> "01.01.2001"
|
60
61
|
(None, <2002.02.02>, True) -> "02.02.2002"
|
61
62
|
"""
|
63
|
+
|
62
64
|
def fmt(date):
|
63
65
|
return date.strftime('%d.%m.%Y') if date else ''
|
64
66
|
|
@@ -73,14 +75,14 @@ def date_range_to_str(date_from, date_to, can_be_one_day_long=False):
|
|
73
75
|
if date_from == date_to:
|
74
76
|
result = fmt(date_from)
|
75
77
|
else:
|
76
|
-
result = 'с
|
78
|
+
result = f'с {fmt(date_from)} по {fmt(date_to)}'
|
77
79
|
else:
|
78
80
|
if can_be_one_day_long:
|
79
81
|
result = fmt(date_from or date_to or None)
|
80
82
|
elif date_from:
|
81
|
-
result = 'с
|
83
|
+
result = f'с {fmt(date_from)}'
|
82
84
|
elif date_to:
|
83
|
-
result = 'по
|
85
|
+
result = f'по {fmt(date_to)}'
|
84
86
|
return result
|
85
87
|
|
86
88
|
|
@@ -97,9 +99,7 @@ def iter_days_between(date_from, date_to, odd_weeks_only=False):
|
|
97
99
|
if date_from > date_to:
|
98
100
|
raise ValueError('date_from must be lower or equal date_to!')
|
99
101
|
|
100
|
-
for dt in rrule.rrule(
|
101
|
-
rrule.DAILY, dtstart=date_from, until=date_to
|
102
|
-
):
|
102
|
+
for dt in rrule.rrule(rrule.DAILY, dtstart=date_from, until=date_to):
|
103
103
|
if odd_weeks_only and dt.isocalendar()[1] % 2 != 0:
|
104
104
|
# если требуются четные недели относительно начала года
|
105
105
|
continue
|
@@ -154,11 +154,7 @@ def get_week_dates(date=None):
|
|
154
154
|
|
155
155
|
monday = get_week_start(date)
|
156
156
|
|
157
|
-
return (
|
158
|
-
day.date()
|
159
|
-
for day in rrule.rrule(rrule.DAILY, dtstart=monday,
|
160
|
-
count=len(WEEKDAYS))
|
161
|
-
)
|
157
|
+
return (day.date() for day in rrule.rrule(rrule.DAILY, dtstart=monday, count=len(WEEKDAYS)))
|
162
158
|
|
163
159
|
|
164
160
|
def get_weekdays_for_date(date=None, weekday_names=None):
|
@@ -176,10 +172,7 @@ def get_weekdays_for_date(date=None, weekday_names=None):
|
|
176
172
|
"""
|
177
173
|
weekday_names = weekday_names or WEEKDAYS_DICT
|
178
174
|
|
179
|
-
return tuple(
|
180
|
-
(weekday_names[day.weekday()], day)
|
181
|
-
for day in get_week_dates(date)
|
182
|
-
)
|
175
|
+
return tuple((weekday_names[day.weekday()], day) for day in get_week_dates(date))
|
183
176
|
|
184
177
|
|
185
178
|
def get_today_min_datetime() -> datetime:
|
@@ -193,7 +186,7 @@ def get_today_max_datetime() -> datetime:
|
|
193
186
|
|
194
187
|
|
195
188
|
def get_date_range_intersection(
|
196
|
-
*date_ranges: Tuple[datetime.date, datetime.date]
|
189
|
+
*date_ranges: Tuple[datetime.date, datetime.date],
|
197
190
|
) -> Union[Tuple[datetime.date, datetime.date], tuple]:
|
198
191
|
"""Возвращает минимальный внутренний диапазон дат из переданных диапазонов дат.
|
199
192
|
|
@@ -210,19 +203,14 @@ def get_date_range_intersection(
|
|
210
203
|
return date_range
|
211
204
|
|
212
205
|
|
213
|
-
def is_date_range_intersection(
|
214
|
-
*date_ranges: Tuple[datetime.date, datetime.date]
|
215
|
-
) -> bool:
|
206
|
+
def is_date_range_intersection(*date_ranges: Tuple[datetime.date, datetime.date]) -> bool:
|
216
207
|
"""Возвращает признак того, что диапазоны дат пересекаются."""
|
217
208
|
intersection = get_date_range_intersection(*date_ranges)
|
218
209
|
|
219
210
|
return True if intersection else False
|
220
211
|
|
221
212
|
|
222
|
-
def date_to_str(
|
223
|
-
date_: Optional[Union[datetime.date, datetime.datetime]],
|
224
|
-
fmt: str = settings.DATE_FORMAT
|
225
|
-
) -> str:
|
213
|
+
def date_to_str(date_: Optional[Union[datetime.date, datetime.datetime]], fmt: str = settings.DATE_FORMAT) -> str:
|
226
214
|
"""Конвертирует дату в строку или возвращает '' если даты нет."""
|
227
215
|
return date_.strftime(fmt) if date_ else ''
|
228
216
|
|
@@ -244,8 +232,7 @@ class Week(namedtuple('Week', ('year', 'week'))):
|
|
244
232
|
неделя года.
|
245
233
|
"""
|
246
234
|
d = datetime.date(self.year, 1, 4)
|
247
|
-
return d + datetime.timedelta(
|
248
|
-
weeks=self.week - 1, days=-d.weekday() + number)
|
235
|
+
return d + datetime.timedelta(weeks=self.week - 1, days=-d.weekday() + number)
|
249
236
|
|
250
237
|
def monday(self) -> datetime.date:
|
251
238
|
"""Дата понедельника."""
|