educommon 3.9.4__py3-none-any.whl → 3.9.6__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/async_task/models.py +3 -3
- educommon/async_task/tasks.py +6 -6
- educommon/auth/rbac/models.py +4 -0
- educommon/contingent/contingent_plugin/apps.py +9 -0
- educommon/django/db/mixins/__init__.py +4 -3
- educommon/django/db/partitioning/README.md +45 -4
- educommon/django/db/partitioning/__init__.py +47 -23
- educommon/django/db/partitioning/const.py +24 -0
- educommon/django/db/partitioning/management/commands/apply_partitioning.py +53 -7
- educommon/django/db/partitioning/management/commands/clear_table.py +19 -5
- educommon/django/db/partitioning/management/commands/split_table.py +24 -4
- educommon/django/db/partitioning/partitioning.sql +24 -2
- educommon/django/db/partitioning/partitioning_set_search_path.sql +11 -0
- educommon/m3/extensions/listeners/delete_check/mixins.py +4 -1
- educommon/m3/extensions/listeners/delete_check/utils.py +6 -1
- educommon/report/constructor/base.py +3 -1
- educommon/report/constructor/editor/actions.py +3 -3
- educommon/report/constructor/utils.py +4 -3
- educommon/version.conf +5 -5
- {educommon-3.9.4.dist-info → educommon-3.9.6.dist-info}/METADATA +5 -3
- {educommon-3.9.4.dist-info → educommon-3.9.6.dist-info}/RECORD +24 -22
- {educommon-3.9.4.dist-info → educommon-3.9.6.dist-info}/WHEEL +0 -0
- {educommon-3.9.4.dist-info → educommon-3.9.6.dist-info}/dependency_links.txt +0 -0
- {educommon-3.9.4.dist-info → educommon-3.9.6.dist-info}/top_level.txt +0 -0
educommon/async_task/models.py
CHANGED
@@ -26,14 +26,14 @@ from django.db.models import (
|
|
26
26
|
from m3.db import (
|
27
27
|
BaseObjectModel,
|
28
28
|
)
|
29
|
-
from m3_django_compat.models import (
|
30
|
-
GenericForeignKey,
|
31
|
-
)
|
32
29
|
|
33
30
|
from m3_db_utils.models import (
|
34
31
|
ModelEnumValue,
|
35
32
|
TitledModelEnum,
|
36
33
|
)
|
34
|
+
from m3_django_compat.models import (
|
35
|
+
GenericForeignKey,
|
36
|
+
)
|
37
37
|
|
38
38
|
from educommon.async_task.consts import (
|
39
39
|
TASK_DEFAULT_USER_NAME,
|
educommon/async_task/tasks.py
CHANGED
@@ -5,6 +5,7 @@ from datetime import (
|
|
5
5
|
)
|
6
6
|
from typing import (
|
7
7
|
Optional,
|
8
|
+
Union,
|
8
9
|
)
|
9
10
|
|
10
11
|
from celery import (
|
@@ -210,7 +211,7 @@ class AsyncTask(Task):
|
|
210
211
|
if self.logging_in_db:
|
211
212
|
update_running_task(task_id, **params)
|
212
213
|
|
213
|
-
def after_return(self, status, retval, task_id, args, kwargs, einfo):
|
214
|
+
def after_return(self, status: str, retval: Union[dict, Exception], task_id: str, args, kwargs, einfo):
|
214
215
|
"""Завершение задачи."""
|
215
216
|
self.debug(f'Task {self.__name__} (id = {task_id}) finished')
|
216
217
|
|
@@ -219,12 +220,11 @@ class AsyncTask(Task):
|
|
219
220
|
self.release_lock(lock_id)
|
220
221
|
|
221
222
|
if isinstance(retval, dict):
|
222
|
-
|
223
|
-
state=retval.get('task_state', status),
|
224
|
-
meta=retval,
|
225
|
-
)
|
223
|
+
state = retval.get('task_state', status)
|
226
224
|
else:
|
227
|
-
|
225
|
+
state = status
|
226
|
+
|
227
|
+
self.update_state(state=state, meta=retval)
|
228
228
|
|
229
229
|
def update_state(self, task_id=None, state=None, meta=None, **kwargs):
|
230
230
|
"""Обновление состояния задачи.
|
educommon/auth/rbac/models.py
CHANGED
@@ -25,10 +25,14 @@ from m3 import (
|
|
25
25
|
from m3.db import (
|
26
26
|
safe_delete,
|
27
27
|
)
|
28
|
+
|
28
29
|
from m3_django_compat import (
|
29
30
|
ModelOptions,
|
30
31
|
atomic,
|
31
32
|
)
|
33
|
+
from m3_django_compat.exceptions import (
|
34
|
+
FieldDoesNotExist,
|
35
|
+
)
|
32
36
|
from m3_django_compat.models import (
|
33
37
|
GenericForeignKey,
|
34
38
|
)
|
@@ -2,15 +2,24 @@ from importlib import (
|
|
2
2
|
import_module,
|
3
3
|
)
|
4
4
|
|
5
|
+
from django import (
|
6
|
+
VERSION,
|
7
|
+
)
|
5
8
|
from django.apps.config import (
|
6
9
|
AppConfig,
|
7
10
|
)
|
8
11
|
|
9
12
|
|
13
|
+
_VERSION = VERSION[:2]
|
14
|
+
|
15
|
+
|
10
16
|
class ContingentPluginAppConfig(AppConfig):
|
11
17
|
|
12
18
|
name = __package__
|
13
19
|
|
20
|
+
if _VERSION >= (3, 2):
|
21
|
+
default = False
|
22
|
+
|
14
23
|
def _register_related_objects_views(self):
|
15
24
|
"""Добавляет представления для моделей приложения."""
|
16
25
|
from educommon.django.db.model_view import (
|
@@ -1,13 +1,14 @@
|
|
1
1
|
"""Классы-примеси для моделей Django."""
|
2
|
-
from django.core.exceptions import (
|
3
|
-
FieldDoesNotExist,
|
4
|
-
)
|
5
2
|
from django.db import (
|
6
3
|
models,
|
7
4
|
)
|
5
|
+
|
8
6
|
from m3_django_compat import (
|
9
7
|
atomic,
|
10
8
|
)
|
9
|
+
from m3_django_compat.exceptions import (
|
10
|
+
FieldDoesNotExist,
|
11
|
+
)
|
11
12
|
|
12
13
|
|
13
14
|
class DeferredActionsMixin(models.Model):
|
@@ -55,11 +55,22 @@ management-команды
|
|
55
55
|
секционирования для конкретной таблицы можно с помощью команды ``apply_partitioning``:
|
56
56
|
|
57
57
|
```bash
|
58
|
-
$ ./manage.py apply_partitioning app_label model_name field_name
|
58
|
+
$ ./manage.py apply_partitioning --app_label --model_name --field_name --is_foreign_table --schema_name
|
59
59
|
```
|
60
60
|
|
61
|
-
где
|
62
|
-
``
|
61
|
+
где обязательные параметры
|
62
|
+
``app_label`` -- лейбл приложения, ``model_name`` -- имя модели, а ``field_label`` -- название поля.
|
63
|
+
``field_name`` выступает в качестве ключа секционирования,
|
64
|
+
|
65
|
+
необязательные параметры
|
66
|
+
``is_foreign_table`` -- признак того, что в базе есть таблицы, на которые ссылаются из сторонних баз (внешние таблицы)
|
67
|
+
``schemas_names`` -- строка со списком схем таблиц, на которые ссылаются из сторонних баз (внешние таблицы) - важно
|
68
|
+
перечислять все необходимые схемы - например, --schemas_names=public,pg_temp
|
69
|
+
|
70
|
+
|
71
|
+
Пример
|
72
|
+
1) apply_partitioning --app_label=mark --model_name=MarkLog --field_name=timestamp
|
73
|
+
2) apply_partitioning --app_label=mark --model_name=MarkLog --field_name=timestamp --is_foreign_table=True --schemas_names=public,pg_temp
|
63
74
|
|
64
75
|
Перенос записей из родительской таблицы в ее разделы
|
65
76
|
----------------------------------------------------
|
@@ -68,8 +79,38 @@ $ ./manage.py apply_partitioning app_label model_name field_name
|
|
68
79
|
секции можно с помощью management-команды ``split_table``:
|
69
80
|
|
70
81
|
```bash
|
71
|
-
$ ./manage.py split_table app_label model_name field_name --timeout
|
82
|
+
$ ./manage.py split_table --app_label --model_name --field_name --timeout=5 --cursor_itersize=1000
|
83
|
+
```
|
84
|
+
|
85
|
+
Необязательный параметр
|
86
|
+
``--timeout`` задает время ожидания между переносом записей из родительской таблицы
|
87
|
+
в соответствующую секцию.
|
88
|
+
``--cursor_itersize``- задает количесвто удаляемых записей за одну итерацию, по умолчанию 100
|
89
|
+
|
90
|
+
Пример
|
91
|
+
```bash
|
92
|
+
$ ./manage.py split_table --app_label=mark --model_name=MarkLog --field_name=timestamp
|
93
|
+
```
|
94
|
+
|
95
|
+
Удаление записей из родительской таблицы по условию до ее партицирования
|
96
|
+
----------------------------------------------------
|
97
|
+
|
98
|
+
Удаление записей из родительской таблицы по условию
|
99
|
+
```bash
|
100
|
+
$ ./manage.py clear_table --app_label --model_name --field_name --before_value --timeout --cursor_itersize
|
72
101
|
```
|
73
102
|
|
103
|
+
где обязательные аргументы -``app_label`` -- лейбл приложения, ``model_name`` -- имя модели,
|
104
|
+
``field_name`` -- название поля, ``before_value`` - значение до которого удаляем все данные
|
105
|
+
|
106
|
+
Необязательный параметр
|
74
107
|
``--timeout`` задает время ожидания между переносом каждой записи из родительской таблицы
|
75
108
|
в соответствующую секцию.
|
109
|
+
``--cursor_itersize``- задает количество удаляемых записей за одну итерацию, по умолчанию 10 000
|
110
|
+
|
111
|
+
|
112
|
+
Пример
|
113
|
+
|
114
|
+
```bash
|
115
|
+
$ ./manage.py clear_table --app_label=mark --model_name=MarkLog --field_name=timestamp --before_value='2022-09-01' --timeout=2 --cursor_itersize=1000
|
116
|
+
```
|
@@ -41,6 +41,9 @@ from time import (
|
|
41
41
|
from types import (
|
42
42
|
MethodType,
|
43
43
|
)
|
44
|
+
from typing import (
|
45
|
+
Optional,
|
46
|
+
)
|
44
47
|
|
45
48
|
from django.conf import (
|
46
49
|
settings,
|
@@ -61,6 +64,7 @@ from django.db.utils import (
|
|
61
64
|
from django.utils.functional import (
|
62
65
|
cached_property,
|
63
66
|
)
|
67
|
+
|
64
68
|
from m3_django_compat import (
|
65
69
|
ModelOptions,
|
66
70
|
commit_unless_managed,
|
@@ -69,6 +73,11 @@ from m3_django_compat import (
|
|
69
73
|
from educommon.django.db.observer import (
|
70
74
|
ModelObserverBase,
|
71
75
|
)
|
76
|
+
from educommon.django.db.partitioning.const import (
|
77
|
+
CURSOR_ITERSIZE,
|
78
|
+
INSERT_CURSOR_ITERSIZE,
|
79
|
+
PARTITION_TRIGGERS_FUNCTIONS,
|
80
|
+
)
|
72
81
|
|
73
82
|
|
74
83
|
_MESSAGE_PREFIX = '[Partitioning] '
|
@@ -114,17 +123,7 @@ def is_initialized(database_alias):
|
|
114
123
|
return False
|
115
124
|
|
116
125
|
# Проверка наличия всех функций схемы.
|
117
|
-
function_names =
|
118
|
-
'getattr',
|
119
|
-
'get_partition_name',
|
120
|
-
'set_partition_constraint',
|
121
|
-
'table_exists',
|
122
|
-
'is_table_partitioned',
|
123
|
-
'create_partition',
|
124
|
-
'before_insert',
|
125
|
-
'instead_of_insert',
|
126
|
-
'before_update',
|
127
|
-
))
|
126
|
+
function_names = PARTITION_TRIGGERS_FUNCTIONS
|
128
127
|
for function_name in function_names:
|
129
128
|
with closing(connections[database_alias].cursor()) as cursor:
|
130
129
|
cursor.execute(
|
@@ -227,7 +226,7 @@ def set_partitioning_for_model(model, column_name, force=False):
|
|
227
226
|
значение будет определять раздел таблицы, в который будет помещена
|
228
227
|
запись.
|
229
228
|
|
230
|
-
:raises
|
229
|
+
:raises m3_django_compat.exceptions.FieldDoesNotExist: если модель *model* не
|
231
230
|
содержит поля *column_name*
|
232
231
|
"""
|
233
232
|
database_alias, table_name, pk_column_name = _get_model_params(model)
|
@@ -244,7 +243,7 @@ def set_partitioning_for_model(model, column_name, force=False):
|
|
244
243
|
_execute_sql_file(database_alias, 'triggers.sql', locals())
|
245
244
|
|
246
245
|
|
247
|
-
def split_table(model, column_name, timeout=0):
|
246
|
+
def split_table(model, column_name: str, timeout: float = 0, cursor_itersize: Optional[int] = None):
|
248
247
|
"""Переносит записи из разбиваемой таблицы в ее разделы.
|
249
248
|
|
250
249
|
Недостающие разделы будут созданы автоматически.
|
@@ -257,12 +256,17 @@ def split_table(model, column_name, timeout=0):
|
|
257
256
|
запись.
|
258
257
|
:param float timeout: Время ожидания в секундах между переносом записей
|
259
258
|
(можно использовать для снижения нагрузки на СУБД).
|
259
|
+
:param int cursor_itersize: Количество записей, попадающих в итератор курсора бд
|
260
|
+
при запросе разбиения таблиц.
|
260
261
|
|
261
|
-
:raises
|
262
|
+
:raises m3_django_compat.exceptions.FieldDoesNotExist: если модель *model* не
|
262
263
|
содержит поля *column_name*
|
263
264
|
"""
|
264
265
|
database_alias, table_name, pk_column_name = _get_model_params(model)
|
265
266
|
|
267
|
+
if not cursor_itersize:
|
268
|
+
cursor_itersize = INSERT_CURSOR_ITERSIZE
|
269
|
+
|
266
270
|
# для проверки наличия поля в модели
|
267
271
|
ModelOptions(model).get_field(column_name)
|
268
272
|
|
@@ -294,10 +298,14 @@ def split_table(model, column_name, timeout=0):
|
|
294
298
|
move_cursor = connection.cursor()
|
295
299
|
|
296
300
|
results = cursor_iter(
|
297
|
-
ids_cursor, connection.features.empty_fetchmany_value, 1
|
301
|
+
ids_cursor, connection.features.empty_fetchmany_value, 1, cursor_itersize
|
298
302
|
)
|
299
303
|
for rows in results:
|
300
|
-
|
304
|
+
# Если всего одна запись, то используем строку, чтобы не получать ошибку sql-запроса на обновление
|
305
|
+
if len(rows) == 1:
|
306
|
+
pk_column_values = f'{rows[0]}'.replace(',', '')
|
307
|
+
else:
|
308
|
+
pk_column_values = tuple(pkv for (pkv,) in rows)
|
301
309
|
# Этот update выполняется для того, чтобы сработала триггерная
|
302
310
|
# функция partitioning.before_update.
|
303
311
|
move_cursor.execute((
|
@@ -306,13 +314,11 @@ def split_table(model, column_name, timeout=0):
|
|
306
314
|
'where {pk_column_name} in {pk_column_values}'
|
307
315
|
).format(**locals()))
|
308
316
|
|
309
|
-
commit_unless_managed(database_alias)
|
310
|
-
|
311
317
|
if timeout:
|
312
318
|
sleep(timeout)
|
313
319
|
|
314
320
|
|
315
|
-
def clear_table(model, column_name, column_value, timeout=0):
|
321
|
+
def clear_table(model, column_name: str, column_value: str, timeout=0, cursor_itersize: Optional[int] = None):
|
316
322
|
"""Удаление записей по условию.
|
317
323
|
|
318
324
|
С помощью данной команды удаляются записи из основной (не секционированной)
|
@@ -327,9 +333,14 @@ def clear_table(model, column_name, column_value, timeout=0):
|
|
327
333
|
будут удаляться записи.
|
328
334
|
:param float timeout: Время ожидания в секундах между удалением 100
|
329
335
|
записей (можно использовать для снижения нагрузки на СУБД).
|
336
|
+
:param int cursor_itersize: Количество записей, попадающих в итератор курсора бд
|
337
|
+
при запросе удаления таблиц.
|
330
338
|
"""
|
331
339
|
database_alias, table_name, pk_column_name = _get_model_params(model)
|
332
340
|
|
341
|
+
if not cursor_itersize:
|
342
|
+
cursor_itersize = CURSOR_ITERSIZE
|
343
|
+
|
333
344
|
connection = connections[database_alias]
|
334
345
|
|
335
346
|
if settings.DATABASES[database_alias]['DISABLE_SERVER_SIDE_CURSORS']:
|
@@ -349,17 +360,19 @@ def clear_table(model, column_name, column_value, timeout=0):
|
|
349
360
|
delete_cursor = connection.cursor()
|
350
361
|
|
351
362
|
results = cursor_iter(
|
352
|
-
ids_cursor, connection.features.empty_fetchmany_value, 1
|
363
|
+
ids_cursor, connection.features.empty_fetchmany_value, 1, cursor_itersize,
|
353
364
|
)
|
354
365
|
for rows in results:
|
355
|
-
|
366
|
+
# Если всего одна запись, то используем строку, чтобы не получать ошибку sql-запроса на удаление
|
367
|
+
if len(rows) == 1:
|
368
|
+
pk_column_values = f'{rows[0]}'.replace(',', '')
|
369
|
+
else:
|
370
|
+
pk_column_values = tuple(pkv for (pkv,) in rows)
|
356
371
|
delete_cursor.execute((
|
357
372
|
'delete from {table_name} '
|
358
373
|
'where {pk_column_name} in {pk_column_values}'
|
359
374
|
).format(**locals()))
|
360
375
|
|
361
|
-
commit_unless_managed(database_alias)
|
362
|
-
|
363
376
|
if timeout:
|
364
377
|
sleep(timeout)
|
365
378
|
|
@@ -441,6 +454,17 @@ def drop_partitions_before_date(model, date):
|
|
441
454
|
)
|
442
455
|
|
443
456
|
|
457
|
+
def set_partitioned_function_search_path(database_alias: str, schema_names: Optional[str] = None):
|
458
|
+
""""Проставляет параметры поиска для существующих функций партицирования.
|
459
|
+
|
460
|
+
Это необходимо для корректной работы с таблицами к которым обращаются как к внешним через postgres_fdw.
|
461
|
+
"""
|
462
|
+
schema_names = schema_names or 'public'
|
463
|
+
_execute_sql_file(database_alias, 'partitioning_set_search_path.sql', dict(
|
464
|
+
schema_names=schema_names,
|
465
|
+
))
|
466
|
+
|
467
|
+
|
444
468
|
class PartitioningObserver(ModelObserverBase):
|
445
469
|
"""Оптимизирует операции вставки в партиционированные таблицы.
|
446
470
|
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# Количество записей, попадающих в итератор курсора бд при запросе удаления таблиц
|
2
|
+
CURSOR_ITERSIZE = 10_000
|
3
|
+
# Количество записей, попадающих в итератор курсора бд при запросе разбиения таблиц.
|
4
|
+
INSERT_CURSOR_ITERSIZE = 100
|
5
|
+
|
6
|
+
|
7
|
+
# Стандартный список функций при инициализации партицирования
|
8
|
+
BASE_PARTITION_FUNCTIONS = (
|
9
|
+
'getattr',
|
10
|
+
'get_partition_name',
|
11
|
+
'set_partition_constraint',
|
12
|
+
'table_exists',
|
13
|
+
'is_table_partitioned',
|
14
|
+
'create_partition',
|
15
|
+
'trigger_exists',
|
16
|
+
)
|
17
|
+
|
18
|
+
# Функции для работы триггеров партицированных таблиц
|
19
|
+
PARTITION_TRIGGERS_FUNCTIONS = BASE_PARTITION_FUNCTIONS + (
|
20
|
+
'before_insert',
|
21
|
+
'instead_of_insert',
|
22
|
+
'before_update',
|
23
|
+
'after_insert',
|
24
|
+
)
|
@@ -1,6 +1,13 @@
|
|
1
|
+
from django.core.exceptions import (
|
2
|
+
FieldDoesNotExist,
|
3
|
+
)
|
4
|
+
from django.core.management.base import (
|
5
|
+
CommandError,
|
6
|
+
)
|
1
7
|
from django.db import (
|
2
8
|
router,
|
3
9
|
)
|
10
|
+
|
4
11
|
from m3_django_compat import (
|
5
12
|
BaseCommand,
|
6
13
|
get_model,
|
@@ -9,6 +16,9 @@ from m3_django_compat import (
|
|
9
16
|
from educommon.django.db import (
|
10
17
|
partitioning,
|
11
18
|
)
|
19
|
+
from educommon.logger import (
|
20
|
+
info as logger_info,
|
21
|
+
)
|
12
22
|
|
13
23
|
|
14
24
|
class Command(BaseCommand):
|
@@ -20,30 +30,66 @@ class Command(BaseCommand):
|
|
20
30
|
`educommon.django.db.partitioning.set_partitioning_for_model`.
|
21
31
|
|
22
32
|
"""
|
23
|
-
help = 'Applies partitioning to the table.'
|
33
|
+
help = 'Applies partitioning to the table.' # noqa: A003
|
24
34
|
|
25
35
|
def add_arguments(self, parser):
|
36
|
+
"""Обработка аргументов команды."""
|
26
37
|
parser.add_argument(
|
27
|
-
'app_label',
|
38
|
+
'--app_label',
|
39
|
+
type=str,
|
28
40
|
help='App label of an application.',
|
29
41
|
)
|
30
42
|
parser.add_argument(
|
31
|
-
'model_name',
|
43
|
+
'--model_name',
|
44
|
+
type=str,
|
32
45
|
help='Model name.',
|
33
46
|
)
|
34
47
|
parser.add_argument(
|
35
|
-
'field_name',
|
48
|
+
'--field_name',
|
49
|
+
type=str,
|
36
50
|
help='Field name. It will be the partition key.',
|
37
51
|
)
|
52
|
+
parser.add_argument(
|
53
|
+
'--is_foreign_table',
|
54
|
+
type=bool,
|
55
|
+
default=False,
|
56
|
+
help='Партицирование для внешних таблиц'
|
57
|
+
)
|
58
|
+
parser.add_argument(
|
59
|
+
'--schemas_names',
|
60
|
+
type=str,
|
61
|
+
default=None,
|
62
|
+
help='Cхемы внешних таблиц при партицировании.'
|
63
|
+
)
|
38
64
|
|
39
65
|
def handle(self, *args, **options):
|
66
|
+
"""Выполнение команды."""
|
40
67
|
app_label = options['app_label']
|
41
68
|
model_name = options['model_name']
|
42
69
|
field_name = options['field_name']
|
43
|
-
|
44
|
-
|
70
|
+
is_foreign_table = options['is_foreign_table']
|
71
|
+
schemas_names = options['schemas_names']
|
72
|
+
|
73
|
+
logger_info('Apply partitioning started\n')
|
74
|
+
try:
|
75
|
+
django_db_model = get_model(app_label, model_name)
|
76
|
+
except LookupError as e:
|
77
|
+
raise CommandError(e.message)
|
78
|
+
|
79
|
+
try:
|
80
|
+
django_db_model._meta.get_field(field_name)
|
81
|
+
except FieldDoesNotExist:
|
82
|
+
raise CommandError('Invalid field name ({0})'.format(field_name))
|
83
|
+
|
84
|
+
db_alias = router.db_for_write(django_db_model)
|
45
85
|
|
46
86
|
if not partitioning.is_initialized(db_alias):
|
47
87
|
partitioning.init(db_alias)
|
48
88
|
|
49
|
-
|
89
|
+
# Дополнительно пробросим схемы для работ с внешними таблицами
|
90
|
+
if is_foreign_table:
|
91
|
+
partitioning.set_partitioned_function_search_path(db_alias, schemas_names)
|
92
|
+
|
93
|
+
partitioning.set_partitioning_for_model(django_db_model, field_name, force=True)
|
94
|
+
|
95
|
+
logger_info('Apply partitioning ended\n')
|
@@ -9,6 +9,9 @@ from m3_django_compat import (
|
|
9
9
|
BaseCommand,
|
10
10
|
get_model,
|
11
11
|
)
|
12
|
+
from m3_django_compat.exceptions import (
|
13
|
+
FieldDoesNotExist,
|
14
|
+
)
|
12
15
|
|
13
16
|
from educommon.django.db import (
|
14
17
|
partitioning,
|
@@ -30,19 +33,23 @@ class Command(BaseCommand):
|
|
30
33
|
|
31
34
|
def add_arguments(self, parser):
|
32
35
|
parser.add_argument(
|
33
|
-
'app_label',
|
36
|
+
'--app_label',
|
37
|
+
type=str,
|
34
38
|
help='App label of an application.',
|
35
39
|
)
|
36
40
|
parser.add_argument(
|
37
|
-
'model_name',
|
41
|
+
'--model_name',
|
42
|
+
type=str,
|
38
43
|
help='Model name.',
|
39
44
|
)
|
40
45
|
parser.add_argument(
|
41
|
-
'field_name',
|
46
|
+
'--field_name',
|
47
|
+
type=str,
|
42
48
|
help='Field name. It will be a check column.',
|
43
49
|
)
|
44
50
|
parser.add_argument(
|
45
|
-
'before_value',
|
51
|
+
'--before_value',
|
52
|
+
type=str,
|
46
53
|
help='Deleting rows before this value.',
|
47
54
|
)
|
48
55
|
parser.add_argument(
|
@@ -51,6 +58,12 @@ class Command(BaseCommand):
|
|
51
58
|
help=('Timeout (in seconds) between the data removes iterations. '
|
52
59
|
'It used to reduce the database load.')
|
53
60
|
)
|
61
|
+
parser.add_argument(
|
62
|
+
'--cursor_itersize', action='store', dest='cursor_itersize',
|
63
|
+
type=int,
|
64
|
+
default=None,
|
65
|
+
help='Количество строк загруженных за раз при загрузке строк при работе команды.'
|
66
|
+
)
|
54
67
|
|
55
68
|
def handle(self, *args, **options):
|
56
69
|
app_label = options['app_label']
|
@@ -58,6 +71,7 @@ class Command(BaseCommand):
|
|
58
71
|
field_name = options['field_name']
|
59
72
|
before_value = options['before_value']
|
60
73
|
timeout = options['timeout']
|
74
|
+
cursor_itersize = options['cursor_itersize']
|
61
75
|
|
62
76
|
try:
|
63
77
|
model = get_model(app_label, model_name)
|
@@ -69,4 +83,4 @@ class Command(BaseCommand):
|
|
69
83
|
except FieldDoesNotExist:
|
70
84
|
raise CommandError('Invalid field name ({0})'.format(field_name))
|
71
85
|
|
72
|
-
partitioning.clear_table(model, field_name, before_value, timeout)
|
86
|
+
partitioning.clear_table(model, field_name, before_value, timeout, cursor_itersize)
|
@@ -9,10 +9,16 @@ from m3_django_compat import (
|
|
9
9
|
BaseCommand,
|
10
10
|
get_model,
|
11
11
|
)
|
12
|
+
from m3_django_compat.exceptions import (
|
13
|
+
FieldDoesNotExist,
|
14
|
+
)
|
12
15
|
|
13
16
|
from educommon.django.db import (
|
14
17
|
partitioning,
|
15
18
|
)
|
19
|
+
from educommon.logger import (
|
20
|
+
info as logger_info,
|
21
|
+
)
|
16
22
|
|
17
23
|
|
18
24
|
class Command(BaseCommand):
|
@@ -30,15 +36,18 @@ class Command(BaseCommand):
|
|
30
36
|
|
31
37
|
def add_arguments(self, parser):
|
32
38
|
parser.add_argument(
|
33
|
-
'app_label',
|
39
|
+
'--app_label',
|
40
|
+
type=str,
|
34
41
|
help='App label of an application.',
|
35
42
|
)
|
36
43
|
parser.add_argument(
|
37
|
-
'model_name',
|
44
|
+
'--model_name',
|
45
|
+
type=str,
|
38
46
|
help='Model name.',
|
39
47
|
)
|
40
48
|
parser.add_argument(
|
41
|
-
'field_name',
|
49
|
+
'--field_name',
|
50
|
+
type=str,
|
42
51
|
help='Field name. It will be the partition key.',
|
43
52
|
)
|
44
53
|
parser.add_argument(
|
@@ -47,12 +56,19 @@ class Command(BaseCommand):
|
|
47
56
|
help=('Timeout (in seconds) between the data transfer iterations. '
|
48
57
|
'It used to reduce the database load.')
|
49
58
|
)
|
59
|
+
parser.add_argument(
|
60
|
+
'--cursor_itersize', action='store', dest='cursor_itersize',
|
61
|
+
type=int,
|
62
|
+
default=None,
|
63
|
+
help='Количество строк загруженных за раз при загрузке строк при работе команды.'
|
64
|
+
)
|
50
65
|
|
51
66
|
def handle(self, *args, **options):
|
52
67
|
app_label = options['app_label']
|
53
68
|
model_name = options['model_name']
|
54
69
|
field_name = options['field_name']
|
55
70
|
timeout = options['timeout']
|
71
|
+
cursor_itersize = options['cursor_itersize']
|
56
72
|
|
57
73
|
try:
|
58
74
|
model = get_model(app_label, model_name)
|
@@ -64,4 +80,8 @@ class Command(BaseCommand):
|
|
64
80
|
except FieldDoesNotExist:
|
65
81
|
raise CommandError('Invalid field name ({0})'.format(field_name))
|
66
82
|
|
67
|
-
|
83
|
+
logger_info('Split table started\n')
|
84
|
+
|
85
|
+
partitioning.split_table(model, field_name, timeout, cursor_itersize)
|
86
|
+
|
87
|
+
logger_info('Split table ended\n')
|
@@ -134,6 +134,23 @@ create or replace function partitioning.table_exists(
|
|
134
134
|
$BODY$ language plpgsql immutable;
|
135
135
|
|
136
136
|
|
137
|
+
create or replace function partitioning.trigger_exists(
|
138
|
+
table_name text, trigger_name text
|
139
|
+
) returns boolean as $BODY$
|
140
|
+
---------------------------------------------------------------------------
|
141
|
+
-- Проверяет, существует ли в таблице table_name триггер с именем trigger_name.
|
142
|
+
---------------------------------------------------------------------------
|
143
|
+
begin
|
144
|
+
return exists(
|
145
|
+
select 1
|
146
|
+
from pg_catalog.pg_trigger
|
147
|
+
where tgrelid = table_name::regclass::oid and
|
148
|
+
tgname = trigger_name
|
149
|
+
);
|
150
|
+
end;
|
151
|
+
$BODY$ language plpgsql immutable;
|
152
|
+
|
153
|
+
|
137
154
|
create or replace function partitioning.is_table_partitioned(
|
138
155
|
table_name text
|
139
156
|
) returns boolean as $BODY$
|
@@ -197,6 +214,7 @@ create or replace function partitioning.create_partition(
|
|
197
214
|
partition_name text;
|
198
215
|
tablespace_name name;
|
199
216
|
tablespace_sql text;
|
217
|
+
trigger_name text;
|
200
218
|
begin
|
201
219
|
-- имя создаваемой таблицы
|
202
220
|
partition_name := partitioning.get_partition_name(
|
@@ -235,14 +253,18 @@ create or replace function partitioning.create_partition(
|
|
235
253
|
|
236
254
|
-- создание триггера для раздела обрабатывающего изменения, которые
|
237
255
|
-- требуют переноса записи из одного раздела таблицы в другой
|
238
|
-
|
239
|
-
|
256
|
+
-- перед созданием проверяем, что такого триггера нет для партиции
|
257
|
+
trigger_name = 'partitioning__' || partition_name || '__update';
|
258
|
+
if not partitioning.trigger_exists(partition_name, trigger_name) then
|
259
|
+
execute
|
260
|
+
'create trigger ' || trigger_name || ' '
|
240
261
|
'before update on ' ||
|
241
262
|
schema_name || '.' || partition_name || ' ' ||
|
242
263
|
'for each row ' ||
|
243
264
|
'execute procedure partitioning.before_update(' ||
|
244
265
|
'''' || pk_column_name || ''', ''' || column_name || ''''
|
245
266
|
')';
|
267
|
+
end if;
|
246
268
|
end;
|
247
269
|
$BODY$ language plpgsql;
|
248
270
|
|
@@ -0,0 +1,11 @@
|
|
1
|
+
begin;
|
2
|
+
|
3
|
+
alter function partitioning.before_insert() set search_path = '{schema_names}';
|
4
|
+
|
5
|
+
alter function partitioning.after_insert() set search_path = '{schema_names}';
|
6
|
+
|
7
|
+
alter function partitioning.instead_of_insert() set search_path = '{schema_names}';
|
8
|
+
|
9
|
+
alter function partitioning.before_update() set search_path = '{schema_names}';
|
10
|
+
|
11
|
+
commit;
|
@@ -152,7 +152,10 @@ class CascadeDeleteMixin:
|
|
152
152
|
|
153
153
|
@staticmethod
|
154
154
|
def skip_field(field):
|
155
|
-
|
155
|
+
cascade_delete_for = getattr(field.model, 'cascade_delete_for', ())
|
156
|
+
if isinstance(cascade_delete_for, dict):
|
157
|
+
cascade_delete_for = tuple(cascade_delete_for.keys())
|
158
|
+
return field in cascade_delete_for
|
156
159
|
|
157
160
|
@atomic
|
158
161
|
def safe_delete(self):
|
@@ -2,6 +2,7 @@ from django.db.models import (
|
|
2
2
|
SET_DEFAULT,
|
3
3
|
SET_NULL,
|
4
4
|
)
|
5
|
+
|
5
6
|
from m3.db import (
|
6
7
|
safe_delete,
|
7
8
|
)
|
@@ -53,7 +54,11 @@ def get_custom_on_delete_function(field):
|
|
53
54
|
not isinstance(field.model.cascade_delete_for, dict)
|
54
55
|
):
|
55
56
|
return None
|
56
|
-
|
57
|
+
|
58
|
+
on_delete_params = {}
|
59
|
+
for _field, _field_params in field.model.cascade_delete_for.items():
|
60
|
+
if _field == field:
|
61
|
+
on_delete_params = _field_params
|
57
62
|
return on_delete_params.get('on_delete')
|
58
63
|
|
59
64
|
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# pylint: disable=protected-access
|
2
2
|
from django.core.exceptions import (
|
3
|
-
FieldDoesNotExist,
|
4
3
|
ValidationError,
|
5
4
|
)
|
6
5
|
from django.db import (
|
@@ -24,6 +23,9 @@ from m3_django_compat import (
|
|
24
23
|
ModelOptions,
|
25
24
|
get_related,
|
26
25
|
)
|
26
|
+
from m3_django_compat.exceptions import (
|
27
|
+
FieldDoesNotExist,
|
28
|
+
)
|
27
29
|
|
28
30
|
from educommon.django.db.utils import (
|
29
31
|
LazyModel,
|
@@ -10,9 +10,6 @@ from itertools import (
|
|
10
10
|
from django.contrib.contenttypes.models import (
|
11
11
|
ContentType,
|
12
12
|
)
|
13
|
-
from django.core.exceptions import (
|
14
|
-
FieldDoesNotExist,
|
15
|
-
)
|
16
13
|
from django.db.transaction import (
|
17
14
|
atomic,
|
18
15
|
)
|
@@ -27,6 +24,9 @@ from m3_ext.ui.results import (
|
|
27
24
|
ExtUIScriptResult,
|
28
25
|
)
|
29
26
|
|
27
|
+
from m3_django_compat.exceptions import (
|
28
|
+
FieldDoesNotExist,
|
29
|
+
)
|
30
30
|
from objectpack.actions import (
|
31
31
|
BaseAction,
|
32
32
|
ObjectPack,
|
@@ -5,9 +5,6 @@ from inspect import (
|
|
5
5
|
isclass,
|
6
6
|
)
|
7
7
|
|
8
|
-
from django.core.exceptions import (
|
9
|
-
FieldDoesNotExist,
|
10
|
-
)
|
11
8
|
from django.db.models.fields import (
|
12
9
|
BooleanField,
|
13
10
|
CharField,
|
@@ -26,9 +23,13 @@ from django.db.models.fields.related import (
|
|
26
23
|
from django.db.models.fields.reverse_related import (
|
27
24
|
ForeignObjectRel,
|
28
25
|
)
|
26
|
+
|
29
27
|
from m3_django_compat import (
|
30
28
|
get_related,
|
31
29
|
)
|
30
|
+
from m3_django_compat.exceptions import (
|
31
|
+
FieldDoesNotExist,
|
32
|
+
)
|
32
33
|
|
33
34
|
from educommon.report.constructor.constants import (
|
34
35
|
CT_BOOLEAN,
|
educommon/version.conf
CHANGED
@@ -4,8 +4,8 @@
|
|
4
4
|
# нормальной установки обновлений.
|
5
5
|
|
6
6
|
[version]
|
7
|
-
BRANCH = tags/3.9.
|
8
|
-
VERSION = 3.9.
|
9
|
-
REVISION =
|
10
|
-
VERSION_DATE =
|
11
|
-
REVISION_DATE =
|
7
|
+
BRANCH = tags/3.9.6
|
8
|
+
VERSION = 3.9.6
|
9
|
+
REVISION = 1db60d8567b7d574828c7217b421726deb3ab035
|
10
|
+
VERSION_DATE = 12.04.2024
|
11
|
+
REVISION_DATE = 12.04.2024
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: educommon
|
3
|
-
Version: 3.9.
|
3
|
+
Version: 3.9.6
|
4
4
|
Summary: Общая кодовая база для проектов БЦ Образование
|
5
5
|
Home-page: https://stash.bars-open.ru/projects/EDUBASE/repos/educommon
|
6
6
|
Author: BARS Group
|
@@ -17,8 +17,10 @@ Classifier: Programming Language :: Python :: 3.8
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.9
|
18
18
|
Classifier: Framework :: Django :: 2.2
|
19
19
|
Classifier: Framework :: Django :: 3.0
|
20
|
+
Classifier: Framework :: Django :: 3.1
|
21
|
+
Classifier: Framework :: Django :: 3.2
|
20
22
|
Requires-Dist: packaging <24,>=21.3
|
21
|
-
Requires-Dist: Django <
|
23
|
+
Requires-Dist: Django <4,>=2.2
|
22
24
|
Requires-Dist: django-mptt
|
23
25
|
Requires-Dist: python-dateutil
|
24
26
|
Requires-Dist: termcolor
|
@@ -28,7 +30,7 @@ Requires-Dist: celery
|
|
28
30
|
Requires-Dist: spyne
|
29
31
|
Requires-Dist: xlsxwriter <1,>=0.9.3
|
30
32
|
Requires-Dist: m3-builder <2,>=1.2
|
31
|
-
Requires-Dist: m3-db-utils >=0.3.
|
33
|
+
Requires-Dist: m3-db-utils >=0.3.13
|
32
34
|
Requires-Dist: m3-django-compat <2,>=1.10.2
|
33
35
|
Requires-Dist: m3-core <3,>=2.2.16
|
34
36
|
Requires-Dist: m3-ui <3,>=2.2.40
|
@@ -1,6 +1,6 @@
|
|
1
1
|
educommon/__init__.py,sha256=fvsBDL7g8HgOTd-JHOh7TSvMcnUauvGVgPuyA2Z9hUI,419
|
2
2
|
educommon/thread_data.py,sha256=n0XtdesP9H92O3rJ8K6fVnJLiHqyJEfh2xpuT36wzxs,61
|
3
|
-
educommon/version.conf,sha256=
|
3
|
+
educommon/version.conf,sha256=l7UfRQw8wmcaWx-1EAAFDaIke-pX2sbG3vJcPUn4CEA,448
|
4
4
|
educommon/about/README.rst,sha256=U48UW5jv-8qHyaV56atzzkNMvzHKXVcWSb_NR06PnMo,2685
|
5
5
|
educommon/about/__init__.py,sha256=H1W0IgW-qX9LCZ49GOJzHdmQGHhh-MA6U1xmNx7WnfM,132
|
6
6
|
educommon/about/apps.py,sha256=GrpJAOE2sF0ukWsqugP_WJS88DO4aL-T3kTLprrJrcA,259
|
@@ -20,8 +20,8 @@ educommon/async_task/consts.py,sha256=LulbNKSlS_T8sNjFqMS7DtaotVI0HibD1a0Iz9iDo8
|
|
20
20
|
educommon/async_task/exceptions.py,sha256=PaaG2nqP0G2eZ-p0yssca2hhxIccil6MYKH6ZteezP8,117
|
21
21
|
educommon/async_task/helpers.py,sha256=HihuIHrszhVQAb-Cs6-l4npnlNAgctItGBJ-oIPQX74,2706
|
22
22
|
educommon/async_task/locker.py,sha256=TwVFhuEGsK0JHPXsWuqx7t2Op2mUKETY5EvHSeuKjc8,3844
|
23
|
-
educommon/async_task/models.py,sha256=
|
24
|
-
educommon/async_task/tasks.py,sha256=
|
23
|
+
educommon/async_task/models.py,sha256=pICrDFr4Vq6KWpa28NIALP45aLs0lqP7YC2p1IQDFSc,7430
|
24
|
+
educommon/async_task/tasks.py,sha256=A6tIuDyqMo9pPEezluyO8_Gs_XerMvWdFmluZ6VjFwI,11537
|
25
25
|
educommon/async_task/ui.py,sha256=x7SeQZ_JXCy7gbUtVTcDXqmS-mCWKH_BgJc2gK1i_hw,5446
|
26
26
|
educommon/async_task/migrations/0001_initial.py,sha256=W0HfBmhatmQ7Dy4giGeqaZNtLHiEVAvJqx0AvHaxGZ0,3677
|
27
27
|
educommon/async_task/migrations/0002_task_type_and_status_data.py,sha256=rDGxKnbwu4OqfcodFwA_TR-kfDGRKJhivEBFKuurM30,3359
|
@@ -79,7 +79,7 @@ educommon/auth/rbac/checker.py,sha256=8A583rwsXDeb4dCegN5mufMFhyDwvd72zfug90usvF
|
|
79
79
|
educommon/auth/rbac/config.py,sha256=mRpOzD29ZHc0zX-CxhUyyICj3h0xj9gUM6N1OoLEWm0,1074
|
80
80
|
educommon/auth/rbac/constants.py,sha256=Hm4kuG7HZ9krTwF6HIj3eMe8v98ui3EMueToBTeOh4A,502
|
81
81
|
educommon/auth/rbac/manager.py,sha256=3WecXF0SU-uBGMxAWnsVLGmVI9QmHS7zDQ9wVgu6mZE,16250
|
82
|
-
educommon/auth/rbac/models.py,sha256=
|
82
|
+
educommon/auth/rbac/models.py,sha256=fnLEYsr22ylGdefhT8bZz3mO8MZv8yAM5k6K8GXZYLQ,15214
|
83
83
|
educommon/auth/rbac/permissions.py,sha256=rvAYUhWiB4xln39BgxRaXs7T5IzHAg0ZAtKgU__uPk8,865
|
84
84
|
educommon/auth/rbac/ui.py,sha256=RK3YpoKyxKoJyK_qaBzV6HpwffA_S99ZzQbNKS134Ng,15735
|
85
85
|
educommon/auth/rbac/utils.py,sha256=L9F5yXfDhkrgVHjgqsO7r5gYrfbM32OVAMiH_bMU4hQ,7819
|
@@ -122,7 +122,7 @@ educommon/contingent/base.py,sha256=jo-8ViLDsb7-AgIVDbkfw6DMvYG-AfMFtO4WGsROCr8,
|
|
122
122
|
educommon/contingent/catalogs.py,sha256=OafO1jDi7q7wm_7cnu8k-xiUc6mwKhXE36HklJX2Sb4,61449
|
123
123
|
educommon/contingent/contingent_plugin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
124
124
|
educommon/contingent/contingent_plugin/actions.py,sha256=SXjFCDGyFHrOZNcroEouRLZB7NqFSs7M_1Gw89bfe48,717
|
125
|
-
educommon/contingent/contingent_plugin/apps.py,sha256=
|
125
|
+
educommon/contingent/contingent_plugin/apps.py,sha256=2RustoCdwosXEIXpXlgK1UVfFvx729K_lP6F6l8xCr8,810
|
126
126
|
educommon/contingent/contingent_plugin/model_views.py,sha256=Xk-Xck9GsiB2AGdW750fsGtT2p1hybZmGI5hE2JhIwM,769
|
127
127
|
educommon/contingent/contingent_plugin/models.py,sha256=Du9Ks2irXNLDmH0yE5fmID--eMyWufHYJihOdvEbceA,2506
|
128
128
|
educommon/contingent/contingent_plugin/observer.py,sha256=JywNusbhDhTHe1sbWS_joSvK2sB_-b3yYFdEY4ZYX08,5181
|
@@ -145,20 +145,22 @@ educommon/django/db/signals.py,sha256=pbBr31q3hK6aBW6FZXYmATxKp8KrCuqmrNdDZNnpw3
|
|
145
145
|
educommon/django/db/utils.py,sha256=S9rZhfLn4GYWXiVOPSsEGl382NzUFeOxh-G7OtB43vE,9206
|
146
146
|
educommon/django/db/migration/__init__.py,sha256=FwICNxrFGFwzy5klIKzYIhVpsunLipS4W8ysS8C0z5k,1882
|
147
147
|
educommon/django/db/migration/operations.py,sha256=R_SCK8R1IF6x5qjJPoTnGjiAedqnBT-OpaEjyyJaxVM,9527
|
148
|
-
educommon/django/db/mixins/__init__.py,sha256=
|
148
|
+
educommon/django/db/mixins/__init__.py,sha256=SnFhMU701HuRcmS8QuIx4SS3B4vRdVsfwK7SgrqDD0U,14746
|
149
149
|
educommon/django/db/mixins/date_interval.py,sha256=9Ro7KxqSf-dskU9MjAra3aS2a09_11cv1Tol7JWQw3g,24715
|
150
150
|
educommon/django/db/mixins/validation.py,sha256=u3SDRSDvEkOjEMgfgCIFtnTUk4LRPdru0swzTGDSqIY,12173
|
151
151
|
educommon/django/db/model_view/__init__.py,sha256=x0rHvR0q6RSDtuMWDGASrtCIYsytfNh-jHcsra0BTZY,12363
|
152
152
|
educommon/django/db/model_view/table-view.html,sha256=EEz_tSJagl-mT-_bJaELshAEnFnuhPerpWegb3XeGO8,631
|
153
|
-
educommon/django/db/partitioning/README.md,sha256=
|
154
|
-
educommon/django/db/partitioning/__init__.py,sha256=
|
155
|
-
educommon/django/db/partitioning/
|
153
|
+
educommon/django/db/partitioning/README.md,sha256=OgmFZwK_wD1EFKRprnFkeF0Rt7igcEuBncsOF9CsBjI,6927
|
154
|
+
educommon/django/db/partitioning/__init__.py,sha256=SGUfTkw3iWcWXjOk7tcXc7RiYpeACssc5O8AMie0CbI,24083
|
155
|
+
educommon/django/db/partitioning/const.py,sha256=52Ue7NylmDUMfV0kbk5fqCaE045IhMIh5ZX9juedYbY,927
|
156
|
+
educommon/django/db/partitioning/partitioning.sql,sha256=UfPrtxy82tIr3XjXg_qTg-rUS0KqUyBexp3TBPdU0gk,21304
|
157
|
+
educommon/django/db/partitioning/partitioning_set_search_path.sql,sha256=oGlljp2A6xd3FctJAqPAO7RCBSar7C_nsXEu8Ssi0WM,342
|
156
158
|
educommon/django/db/partitioning/triggers.sql,sha256=OEwQqMwzRB79LcFM0-cWH8x_UdLKw-iZTSPILTGJ1Uk,3401
|
157
159
|
educommon/django/db/partitioning/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
158
160
|
educommon/django/db/partitioning/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
159
|
-
educommon/django/db/partitioning/management/commands/apply_partitioning.py,sha256=
|
160
|
-
educommon/django/db/partitioning/management/commands/clear_table.py,sha256=
|
161
|
-
educommon/django/db/partitioning/management/commands/split_table.py,sha256=
|
161
|
+
educommon/django/db/partitioning/management/commands/apply_partitioning.py,sha256=iDpw7Fl_PvuQK0R6yfcwKM8s6ctRi7eLMaW-qEaJLNM,3174
|
162
|
+
educommon/django/db/partitioning/management/commands/clear_table.py,sha256=dohXtylU31ZxOJ1XPOrDcYi9DCA1fU13HWGJ_LgHqW4,2785
|
163
|
+
educommon/django/db/partitioning/management/commands/split_table.py,sha256=gZwTsewKmToaeHh1MpDjttKUrCGYAxAKEy63e7EyPfA,2756
|
162
164
|
educommon/django/db/validators/__init__.py,sha256=Hyx-L4suVzUuCEb-HQB9g8LnaxKQp2ymv-DtaOW6qbw,1986
|
163
165
|
educommon/django/db/validators/simple.py,sha256=cwG-OLVPdD146A9dzLOaGyzZOoUWgQoFu9KDQ7_k8xI,38886
|
164
166
|
educommon/django/storages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -214,11 +216,11 @@ educommon/m3/extensions/listeners/__init__.py,sha256=ax097TgSn7rWH8L3DSiVCc1MOKn
|
|
214
216
|
educommon/m3/extensions/listeners/delete_check/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
215
217
|
educommon/m3/extensions/listeners/delete_check/cancel-confirm-window.js,sha256=Xh0Rzk9ua9NtR9TR0x7oPn0Q96IKqBQF0XkkFzTOUVI,916
|
216
218
|
educommon/m3/extensions/listeners/delete_check/listeners.py,sha256=r_PhNLlt16Tcikl4lOmhG-E7Hl7KL596ZQL3cEr3f8w,6131
|
217
|
-
educommon/m3/extensions/listeners/delete_check/mixins.py,sha256=
|
219
|
+
educommon/m3/extensions/listeners/delete_check/mixins.py,sha256=kelvluswqu8u2h5QyVs_VEfwatkVWrSVbRlv4skScmw,7823
|
218
220
|
educommon/m3/extensions/listeners/delete_check/related-objects-window.html,sha256=Do_SnmWfexnhDzgvr7o7JJubH4N8pvPNa268cd-JIcA,94
|
219
221
|
educommon/m3/extensions/listeners/delete_check/signals.py,sha256=QnfevsAKpKtOE_EBc7h1qg4hTV4LxXYL5ivvpHyc704,467
|
220
222
|
educommon/m3/extensions/listeners/delete_check/ui.py,sha256=W3sBsZGbF3q0rxuMUEmycBvFngUh--UQ-77xdjgvDP0,3507
|
221
|
-
educommon/m3/extensions/listeners/delete_check/utils.py,sha256=
|
223
|
+
educommon/m3/extensions/listeners/delete_check/utils.py,sha256=87OmVAzx2lX_7M12Tsgr6cZocrnvuwnDMpIrx23Xmzw,3092
|
222
224
|
educommon/objectpack/__init__.py,sha256=TwmrbrumJoH9KjYhbLnKExWmo4A0UQeyNYTK__93Vr8,69
|
223
225
|
educommon/objectpack/actions.py,sha256=KanZUKMl4aUSRPZV-SJB3JwjDTYzuGweI9L5cE1KziI,14735
|
224
226
|
educommon/objectpack/apps.py,sha256=LZl_kQ9s7G9pQ4a5_mWNPN52mFWJ7RGvGIHCHwhKYPo,220
|
@@ -238,7 +240,7 @@ educommon/report/constructor/README.rst,sha256=M6bnt3eAdRMUSIbRXdbuleron11RajRmT
|
|
238
240
|
educommon/report/constructor/__init__.py,sha256=sMDEr17Etf69p4NKRelFv9ySrNT6JulFO1IByVfZA1w,1252
|
239
241
|
educommon/report/constructor/app_meta.py,sha256=4hFKVlGCO4Mlu46fCw5k7iD9kSse8b9MQt3tRV5WyfE,200
|
240
242
|
educommon/report/constructor/apps.py,sha256=NUp1dCkzHduZFXT6XPDQPasAei3Bs3TYdEQzXhqn1J8,371
|
241
|
-
educommon/report/constructor/base.py,sha256=
|
243
|
+
educommon/report/constructor/base.py,sha256=ABSNm1QrUyN4E6RybN7NM72D5W_4U6bz4vvxyTkIsNA,27333
|
242
244
|
educommon/report/constructor/config.py,sha256=6PjPv4A3zww_DnZqE-aj72zduW7cZqbzVwuCP8JDsYg,1058
|
243
245
|
educommon/report/constructor/constants.py,sha256=VF3tu6vEWrN37oPyipD3LGCElbaY2JHGaKy7AxUbQIU,3381
|
244
246
|
educommon/report/constructor/exceptions.py,sha256=NL2mQggBHbkHaQFKpr9mTj7R5IVtxtCRKzJ00dgfTqM,1287
|
@@ -246,7 +248,7 @@ educommon/report/constructor/mixins.py,sha256=4Oh3JB3VRr8Es6yLS3W1sXVPrV61SVPm-v
|
|
246
248
|
educommon/report/constructor/models.py,sha256=g-fbs_kM16GBh_5MY0NenXc8cVwihDLVXz3uS_JN1Mg,18764
|
247
249
|
educommon/report/constructor/plugin_meta.py,sha256=52IdIKr7ArMiHOnyZlKV5Fn6PxMhSbttipXz_Vbfkmg,114
|
248
250
|
educommon/report/constructor/registries.py,sha256=aTFeW0htAeDYWd9HlLqn3GTK3mttjS8I8LddPc6Ea7M,1883
|
249
|
-
educommon/report/constructor/utils.py,sha256=
|
251
|
+
educommon/report/constructor/utils.py,sha256=lXfKGlqPhmlnSLg4O0FTxRd3kLQtx3WlLQ7z84lbres,6563
|
250
252
|
educommon/report/constructor/validators.py,sha256=E_VKNz0Z3YqJOJTjd7OdZXFMDnW8UUiVUIH9Jk6Irtw,524
|
251
253
|
educommon/report/constructor/builders/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
252
254
|
educommon/report/constructor/builders/excel/__init__.py,sha256=ZjiilnR4FRJm0DM06G3yKHBEDAPXs5JvuPO3JOg2FB8,104
|
@@ -256,7 +258,7 @@ educommon/report/constructor/builders/excel/constants.py,sha256=SM5OEkd7CMrGhuCp
|
|
256
258
|
educommon/report/constructor/builders/excel/product.py,sha256=NsfjhUViZyi_XpDjiUJDwfgHVRlE98v40RMAa6mpAzk,6229
|
257
259
|
educommon/report/constructor/builders/excel/with_merged_cells.py,sha256=mOlzkfqkbjTc3u9VsUyoNUAgH3AKc351E4Jz-5J6OTs,5150
|
258
260
|
educommon/report/constructor/editor/__init__.py,sha256=WS6vGDp9CjGE-e_-AgywqN-W67tv5F_WcPgebLHwKbg,41
|
259
|
-
educommon/report/constructor/editor/actions.py,sha256=
|
261
|
+
educommon/report/constructor/editor/actions.py,sha256=EHJKR3AxwAPcK02YmL2U8ti-brstJK0ZzqulhoJ6oyk,38815
|
260
262
|
educommon/report/constructor/editor/edit-window.js,sha256=AcbwuumR3k6Nzh8PNz8Rz35hREjl0cyenPI_3BcLq6o,39225
|
261
263
|
educommon/report/constructor/editor/list-window.js,sha256=S2KQGqg5GnFMMYmPNEm_Yf9nXTbfwvIfs4E3LgWO9-0,3247
|
262
264
|
educommon/report/constructor/editor/ui.py,sha256=sZjfiiQa2LIcfoyczPlMz3ImzO7TSNNAaV2We5hywIc,24628
|
@@ -342,8 +344,8 @@ educommon/ws_log/smev/exceptions.py,sha256=lmy7o2T3dJkqgIhG07qyh5yPqO3qZAYABuT4J
|
|
342
344
|
educommon/ws_log/templates/report/smev_logs.xlsx,sha256=nnYgB0Z_ix8HoxsRICjsZfFRQBdra-5Gd8nWhCxTjYg,10439
|
343
345
|
educommon/ws_log/templates/ui-js/smev-logs-list-window.js,sha256=AGup3D8GTJSY9WdDPj0zBJeYQBFOmGgcbxPOJbKK-nY,513
|
344
346
|
educommon/ws_log/templates/ui-js/smev-logs-report-setting-window.js,sha256=nQ7QYK9frJcE7g7kIt6INg9TlEEJAPPayBJgRaoTePA,1103
|
345
|
-
educommon-3.9.
|
346
|
-
educommon-3.9.
|
347
|
-
educommon-3.9.
|
348
|
-
educommon-3.9.
|
349
|
-
educommon-3.9.
|
347
|
+
educommon-3.9.6.dist-info/METADATA,sha256=S0A8Y2rjK_IgCSYD5Gs3bpq3RSLUYzUioswDSuDlANs,1562
|
348
|
+
educommon-3.9.6.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
349
|
+
educommon-3.9.6.dist-info/dependency_links.txt,sha256=RNlr4t-BxZRm7e_IfVo1ikr5ln-7viimzLHvQMO1C_Q,43
|
350
|
+
educommon-3.9.6.dist-info/top_level.txt,sha256=z5fbW7bz_0V1foUm_FGcZ9_MTpW3N1dBN7-kEmMowl4,10
|
351
|
+
educommon-3.9.6.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|