edu-rdm-integration 3.21.0__py3-none-any.whl → 3.22.1__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.
- edu_rdm_integration/pipelines/cleanup_outdated_data/management/commands/rdm_cleanup_outdated_data.py +32 -1
- edu_rdm_integration/stages/collect_data/migrations/0005_alter_rdmcollectingdatasubstage_previous.py +22 -0
- edu_rdm_integration/stages/collect_data/migrations/0006_fix_fk_constraints.py +59 -0
- edu_rdm_integration/stages/collect_data/registry/actions.py +1 -1
- edu_rdm_integration/stages/export_data/migrations/0003_fix_fk_constraints.py +76 -0
- edu_rdm_integration/stages/service/service_outdated_data/cleaners/__init__.py +0 -0
- edu_rdm_integration/stages/service/service_outdated_data/cleaners/base.py +214 -0
- edu_rdm_integration/stages/service/service_outdated_data/cleaners/collect_data.py +126 -0
- edu_rdm_integration/stages/service/service_outdated_data/cleaners/consts.py +12 -0
- edu_rdm_integration/stages/service/service_outdated_data/cleaners/export_data.py +154 -0
- edu_rdm_integration/stages/service/service_outdated_data/cleaners/upload_data.py +64 -0
- edu_rdm_integration/stages/service/service_outdated_data/managers.py +80 -0
- edu_rdm_integration/stages/upload_data/migrations/0003_auto_20251006_1417.py +28 -0
- edu_rdm_integration/stages/upload_data/migrations/0004_fix_fk_constraints.py +79 -0
- edu_rdm_integration/stages/upload_data/models.py +5 -2
- edu_rdm_integration/stages/utils.py +49 -0
- {edu_rdm_integration-3.21.0.dist-info → edu_rdm_integration-3.22.1.dist-info}/METADATA +14 -1
- {edu_rdm_integration-3.21.0.dist-info → edu_rdm_integration-3.22.1.dist-info}/RECORD +21 -8
- {edu_rdm_integration-3.21.0.dist-info → edu_rdm_integration-3.22.1.dist-info}/WHEEL +0 -0
- {edu_rdm_integration-3.21.0.dist-info → edu_rdm_integration-3.22.1.dist-info}/licenses/LICENSE +0 -0
- {edu_rdm_integration-3.21.0.dist-info → edu_rdm_integration-3.22.1.dist-info}/top_level.txt +0 -0
edu_rdm_integration/pipelines/cleanup_outdated_data/management/commands/rdm_cleanup_outdated_data.py
CHANGED
|
@@ -11,6 +11,9 @@ from edu_rdm_integration.rdm_models.models import (
|
|
|
11
11
|
from edu_rdm_integration.stages.service.model_outdated_data.managers import (
|
|
12
12
|
ModelOutdatedDataCleanerManager,
|
|
13
13
|
)
|
|
14
|
+
from edu_rdm_integration.stages.service.service_outdated_data.managers import (
|
|
15
|
+
ServiceOutdatedDataCleanerManager,
|
|
16
|
+
)
|
|
14
17
|
|
|
15
18
|
|
|
16
19
|
class Command(BaseCommand):
|
|
@@ -33,10 +36,26 @@ class Command(BaseCommand):
|
|
|
33
36
|
'--models',
|
|
34
37
|
action='store',
|
|
35
38
|
dest='models',
|
|
36
|
-
type=lambda ml: [m.strip().upper() for m in ml.
|
|
39
|
+
type=lambda ml: [m.strip().upper() for m in ml.split(',')] if ml else None,
|
|
37
40
|
help=models_help_text,
|
|
38
41
|
)
|
|
39
42
|
|
|
43
|
+
# Аргумент для сервисных стадий
|
|
44
|
+
stages_help_text = (
|
|
45
|
+
'Значением параметра является перечисление стадий сервисных данных, '
|
|
46
|
+
'для которых должна быть произведена зачистка устаревших данных. '
|
|
47
|
+
'Перечисление этапов: collect, export, upload. '
|
|
48
|
+
'Если стадии не указываются, то зачистка будет произведена для всех стадий. '
|
|
49
|
+
'Стадии перечисляются через запятую без пробелов.'
|
|
50
|
+
)
|
|
51
|
+
parser.add_argument(
|
|
52
|
+
'--stages',
|
|
53
|
+
action='store',
|
|
54
|
+
dest='stages',
|
|
55
|
+
type=lambda st: [s.strip().lower() for s in st.split(',')] if st else None,
|
|
56
|
+
help=stages_help_text,
|
|
57
|
+
)
|
|
58
|
+
|
|
40
59
|
parser.add_argument(
|
|
41
60
|
'--safe',
|
|
42
61
|
action='store_true',
|
|
@@ -62,7 +81,19 @@ class Command(BaseCommand):
|
|
|
62
81
|
)
|
|
63
82
|
model_data_cleaner_manager.run()
|
|
64
83
|
|
|
84
|
+
def _cleanup_service_outdated_data(self, options):
|
|
85
|
+
"""Очистка устаревших данных сервисных моделей РВД."""
|
|
86
|
+
service_data_cleaner_manager = ServiceOutdatedDataCleanerManager(
|
|
87
|
+
stages=options['stages'],
|
|
88
|
+
safe=options['safe'],
|
|
89
|
+
log_sql=options['log_sql'],
|
|
90
|
+
)
|
|
91
|
+
service_data_cleaner_manager.run()
|
|
92
|
+
|
|
65
93
|
def handle(self, *args, **options):
|
|
66
94
|
"""Запуск очистки устаревших данных РВД."""
|
|
67
95
|
if settings.RDM_ENABLE_CLEANUP_MODELS_OUTDATED_DATA:
|
|
68
96
|
self._cleanup_model_outdated_data(options)
|
|
97
|
+
|
|
98
|
+
if settings.RDM_ENABLE_CLEANUP_SERVICE_OUTDATED_DATA:
|
|
99
|
+
self._cleanup_service_outdated_data(options)
|
edu_rdm_integration/stages/collect_data/migrations/0005_alter_rdmcollectingdatasubstage_previous.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Generated by Django 3.2.24 on 2025-10-08 14:17
|
|
2
|
+
|
|
3
|
+
import django.db.models.deletion
|
|
4
|
+
from django.db import (
|
|
5
|
+
migrations,
|
|
6
|
+
models,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Migration(migrations.Migration):
|
|
11
|
+
|
|
12
|
+
dependencies = [
|
|
13
|
+
('edu_rdm_integration_collect_data_stage', '0004_auto_20250704_0825'),
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
operations = [
|
|
17
|
+
migrations.AlterField(
|
|
18
|
+
model_name='rdmcollectingdatasubstage',
|
|
19
|
+
name='previous',
|
|
20
|
+
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='edu_rdm_integration_collect_data_stage.rdmcollectingdatasubstage', verbose_name='Предыдущий сбор данных'),
|
|
21
|
+
),
|
|
22
|
+
]
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Generated by Django 3.2.24 on 2025-10-08 14:43
|
|
2
|
+
|
|
3
|
+
from django.db import (
|
|
4
|
+
migrations,
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
from edu_rdm_integration.stages.utils import (
|
|
8
|
+
update_foreign_key_constraint,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def apply_fk_updates(apps, schema_editor):
|
|
13
|
+
"""
|
|
14
|
+
Применяет обновления внешних ключей для конкретных моделей.
|
|
15
|
+
|
|
16
|
+
Здесь задаются таблицы и поведение при удалении записей,
|
|
17
|
+
чтобы синхронизировать фактическое состояние БД с логикой моделей.
|
|
18
|
+
"""
|
|
19
|
+
RDMCollectingDataSubStage = apps.get_model('edu_rdm_integration_collect_data_stage', 'RDMCollectingDataSubStage')
|
|
20
|
+
RDMCollectingDataStage = apps.get_model('edu_rdm_integration_collect_data_stage', 'RDMCollectingDataStage')
|
|
21
|
+
|
|
22
|
+
# Модель: RDMCollectingDataSubStage
|
|
23
|
+
# Поле previous_id должно иметь ON DELETE SET NULL
|
|
24
|
+
update_foreign_key_constraint(
|
|
25
|
+
table_name=RDMCollectingDataSubStage._meta.db_table,
|
|
26
|
+
field_name="previous_id",
|
|
27
|
+
target_table=RDMCollectingDataSubStage._meta.db_table,
|
|
28
|
+
on_delete="SET NULL",
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
RDMCollectingDataCommandProgress = apps.get_model('edu_rdm_integration_collect_data_stage', 'RDMCollectingDataCommandProgress')
|
|
32
|
+
# Модель: RDMCollectingDataCommandProgress
|
|
33
|
+
# Поле stage_id должно иметь ON DELETE SET NULL
|
|
34
|
+
update_foreign_key_constraint(
|
|
35
|
+
table_name=RDMCollectingDataCommandProgress._meta.db_table,
|
|
36
|
+
field_name="stage_id",
|
|
37
|
+
target_table=RDMCollectingDataStage._meta.db_table,
|
|
38
|
+
on_delete="SET NULL",
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
# Таблица: rdm_collecting_data_command_progress
|
|
42
|
+
# Поле stage_id должно иметь ON DELETE SET NULL
|
|
43
|
+
update_foreign_key_constraint(
|
|
44
|
+
table_name='rdm_collecting_data_command_progress',
|
|
45
|
+
field_name="stage_id",
|
|
46
|
+
target_table=RDMCollectingDataStage._meta.db_table,
|
|
47
|
+
on_delete="SET NULL",
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class Migration(migrations.Migration):
|
|
52
|
+
|
|
53
|
+
dependencies = [
|
|
54
|
+
('edu_rdm_integration_collect_data_stage', '0005_alter_rdmcollectingdatasubstage_previous'),
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
operations = [
|
|
58
|
+
migrations.RunPython(apply_fk_updates, reverse_code=migrations.RunPython.noop),
|
|
59
|
+
]
|
|
@@ -278,4 +278,4 @@ class BaseCollectingDataProgressPack(BaseCommandProgressPack):
|
|
|
278
278
|
)
|
|
279
279
|
for command in commands_to_save
|
|
280
280
|
]
|
|
281
|
-
self.model.objects.bulk_create(objs, batch_size=settings.
|
|
281
|
+
self.model.objects.bulk_create(objs, batch_size=settings.RDM_COLLECT_PROGRESS_BATCH_SIZE)
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Generated by Django 3.2.24 on 2025-10-08 14:44
|
|
2
|
+
|
|
3
|
+
from django.db import (
|
|
4
|
+
migrations,
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
from edu_rdm_integration.stages.utils import (
|
|
8
|
+
update_foreign_key_constraint,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def apply_fk_updates(apps, schema_editor):
|
|
13
|
+
"""
|
|
14
|
+
Применяет обновления внешних ключей для конкретных моделей.
|
|
15
|
+
|
|
16
|
+
Здесь задаются таблицы и поведение при удалении записей,
|
|
17
|
+
чтобы синхронизировать фактическое состояние БД с логикой моделей.
|
|
18
|
+
"""
|
|
19
|
+
RDMExportingDataSubStage = apps.get_model('edu_rdm_integration_export_data_stage', 'RDMExportingDataSubStage')
|
|
20
|
+
RDMExportingDataStage = apps.get_model('edu_rdm_integration_export_data_stage', 'RDMExportingDataStage')
|
|
21
|
+
|
|
22
|
+
RDMExportingDataSubStageAttachment = apps.get_model(
|
|
23
|
+
'edu_rdm_integration_export_data_stage', 'RDMExportingDataSubStageAttachment'
|
|
24
|
+
)
|
|
25
|
+
# Модель: RDMExportingDataSubStageAttachment
|
|
26
|
+
# Поле exporting_data_sub_stage_id должно иметь ON DELETE CASCADE
|
|
27
|
+
update_foreign_key_constraint(
|
|
28
|
+
table_name=RDMExportingDataSubStageAttachment._meta.db_table,
|
|
29
|
+
field_name='exporting_data_sub_stage_id',
|
|
30
|
+
target_table=RDMExportingDataSubStage._meta.db_table,
|
|
31
|
+
on_delete='CASCADE',
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
RDMExportingDataSubStageEntity = apps.get_model(
|
|
35
|
+
'edu_rdm_integration_export_data_stage', 'RDMExportingDataSubStageEntity'
|
|
36
|
+
)
|
|
37
|
+
# Модель: CollectDataCommandProgress
|
|
38
|
+
# Поле exporting_data_sub_stage_id должно иметь ON DELETE CASCADE
|
|
39
|
+
update_foreign_key_constraint(
|
|
40
|
+
table_name=RDMExportingDataSubStageEntity._meta.db_table,
|
|
41
|
+
field_name='exporting_data_sub_stage_id',
|
|
42
|
+
target_table=RDMExportingDataSubStage._meta.db_table,
|
|
43
|
+
on_delete='CASCADE',
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
RDMExportingDataCommandProgress = apps.get_model(
|
|
47
|
+
'edu_rdm_integration_export_data_stage', 'RDMExportingDataCommandProgress'
|
|
48
|
+
)
|
|
49
|
+
# Модель: RDMExportingDataCommandProgress
|
|
50
|
+
# Поле stage_id должно иметь ON DELETE SET NULL
|
|
51
|
+
update_foreign_key_constraint(
|
|
52
|
+
table_name=RDMExportingDataCommandProgress._meta.db_table,
|
|
53
|
+
field_name='stage_id',
|
|
54
|
+
target_table=RDMExportingDataStage._meta.db_table,
|
|
55
|
+
on_delete='SET NULL',
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
# Таблица: rdm_exporting_data_command_progress
|
|
59
|
+
# Поле stage_id должно иметь ON DELETE SET NULL
|
|
60
|
+
update_foreign_key_constraint(
|
|
61
|
+
table_name='rdm_exporting_data_command_progress',
|
|
62
|
+
field_name="stage_id",
|
|
63
|
+
target_table=RDMExportingDataStage._meta.db_table,
|
|
64
|
+
on_delete="SET NULL",
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class Migration(migrations.Migration):
|
|
69
|
+
|
|
70
|
+
dependencies = [
|
|
71
|
+
('edu_rdm_integration_export_data_stage', '0002_auto_20250704_0810'),
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
operations = [
|
|
75
|
+
migrations.RunPython(apply_fk_updates, reverse_code=migrations.RunPython.noop),
|
|
76
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
from abc import (
|
|
2
|
+
ABCMeta,
|
|
3
|
+
abstractmethod,
|
|
4
|
+
)
|
|
5
|
+
|
|
6
|
+
from django.conf import (
|
|
7
|
+
settings,
|
|
8
|
+
)
|
|
9
|
+
from django.db import (
|
|
10
|
+
connection,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
from educommon import (
|
|
14
|
+
logger,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class BaseServiceOutdatedDataCleaner(metaclass=ABCMeta):
|
|
19
|
+
"""Базовый класс уборщика устаревших сервисных данных."""
|
|
20
|
+
|
|
21
|
+
model = None
|
|
22
|
+
|
|
23
|
+
SELECT_RDM_CHUNK_BOUNDED_SQL = """
|
|
24
|
+
DO $$
|
|
25
|
+
DECLARE
|
|
26
|
+
chunk_size INT := {chunk_size};
|
|
27
|
+
last_id INT := 0;
|
|
28
|
+
first_id INT;
|
|
29
|
+
last_chunk_id INT;
|
|
30
|
+
BEGIN
|
|
31
|
+
DROP TABLE IF EXISTS rdm_chunk_bounds;
|
|
32
|
+
CREATE TEMP TABLE rdm_chunk_bounds (
|
|
33
|
+
chunk_number INT,
|
|
34
|
+
first_id INT,
|
|
35
|
+
last_id INT
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
DROP TABLE IF EXISTS tmp_chunk;
|
|
39
|
+
CREATE TEMP TABLE tmp_chunk (id INT) ON COMMIT DROP;
|
|
40
|
+
|
|
41
|
+
WHILE TRUE LOOP
|
|
42
|
+
TRUNCATE tmp_chunk;
|
|
43
|
+
|
|
44
|
+
INSERT INTO tmp_chunk (id)
|
|
45
|
+
SELECT id
|
|
46
|
+
FROM {table_name}
|
|
47
|
+
WHERE id > last_id
|
|
48
|
+
ORDER BY id
|
|
49
|
+
LIMIT chunk_size;
|
|
50
|
+
|
|
51
|
+
IF NOT FOUND THEN
|
|
52
|
+
EXIT;
|
|
53
|
+
END IF;
|
|
54
|
+
|
|
55
|
+
SELECT MIN(id), MAX(id)
|
|
56
|
+
INTO first_id, last_chunk_id
|
|
57
|
+
FROM tmp_chunk;
|
|
58
|
+
|
|
59
|
+
INSERT INTO rdm_chunk_bounds (chunk_number, first_id, last_id)
|
|
60
|
+
VALUES (
|
|
61
|
+
(SELECT COUNT(*) FROM rdm_chunk_bounds) + 1,
|
|
62
|
+
first_id,
|
|
63
|
+
last_chunk_id
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
last_id := last_chunk_id;
|
|
67
|
+
END LOOP;
|
|
68
|
+
END $$;
|
|
69
|
+
|
|
70
|
+
SELECT * FROM rdm_chunk_bounds ORDER BY chunk_number;
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
REMOVE_OUTDATED_DATA_SQL = """
|
|
74
|
+
WITH deleted_rows AS (
|
|
75
|
+
DELETE FROM {table_name}
|
|
76
|
+
WHERE id IN (
|
|
77
|
+
WITH tbl AS (
|
|
78
|
+
SELECT *
|
|
79
|
+
FROM {table_name}
|
|
80
|
+
WHERE id >= {first_id}
|
|
81
|
+
AND id <= {last_id}
|
|
82
|
+
)
|
|
83
|
+
SELECT tbl.id
|
|
84
|
+
FROM tbl
|
|
85
|
+
WHERE {conditions}
|
|
86
|
+
)
|
|
87
|
+
RETURNING id
|
|
88
|
+
)
|
|
89
|
+
SELECT COUNT(*) AS deleted_count FROM deleted_rows;
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
def __init__(
|
|
93
|
+
self,
|
|
94
|
+
*args,
|
|
95
|
+
safe: bool = False,
|
|
96
|
+
log_sql: bool = False,
|
|
97
|
+
**kwargs
|
|
98
|
+
):
|
|
99
|
+
"""Инициализация уборщика."""
|
|
100
|
+
self._safe = safe
|
|
101
|
+
self._log_sql = log_sql
|
|
102
|
+
self._deleted_count = 0
|
|
103
|
+
|
|
104
|
+
super().__init__(*args, **kwargs)
|
|
105
|
+
|
|
106
|
+
@abstractmethod
|
|
107
|
+
def get_merged_conditions(self) -> str:
|
|
108
|
+
"""Возвращает условия для удаления устаревших данных."""
|
|
109
|
+
|
|
110
|
+
@classmethod
|
|
111
|
+
def get_table_name(cls) -> str:
|
|
112
|
+
"""Возвращает имя таблицы в базе данных."""
|
|
113
|
+
if cls.model is None:
|
|
114
|
+
raise NotImplementedError('Необходимо задать атрибут "model"')
|
|
115
|
+
return cls.model._meta.db_table
|
|
116
|
+
|
|
117
|
+
def get_orphan_reference_condition(
|
|
118
|
+
self,
|
|
119
|
+
reference_table: str,
|
|
120
|
+
reference_field: str,
|
|
121
|
+
local_field: str = 'id'
|
|
122
|
+
) -> str:
|
|
123
|
+
"""Условие проверки отсутствия записей в связанной таблице."""
|
|
124
|
+
return f"""
|
|
125
|
+
NOT EXISTS (
|
|
126
|
+
SELECT 1
|
|
127
|
+
FROM {reference_table} ref
|
|
128
|
+
WHERE ref.{reference_field} = tbl.{local_field}
|
|
129
|
+
)
|
|
130
|
+
"""
|
|
131
|
+
|
|
132
|
+
def get_status_condition(
|
|
133
|
+
self,
|
|
134
|
+
related_table: str,
|
|
135
|
+
related_field: str,
|
|
136
|
+
status_value: str,
|
|
137
|
+
days: int,
|
|
138
|
+
local_field: str = 'id'
|
|
139
|
+
) -> str:
|
|
140
|
+
"""Условие проверки записи с заданным статусом и возрастом."""
|
|
141
|
+
return f"""
|
|
142
|
+
EXISTS (
|
|
143
|
+
SELECT 1
|
|
144
|
+
FROM {related_table} sub
|
|
145
|
+
WHERE sub.{related_field} = tbl.{local_field}
|
|
146
|
+
AND sub.status_id = '{status_value}'
|
|
147
|
+
AND sub.ended_at <= NOW() - INTERVAL '{days} days'
|
|
148
|
+
)
|
|
149
|
+
"""
|
|
150
|
+
|
|
151
|
+
def get_chunk_bounds(self):
|
|
152
|
+
"""Возвращает границы чанков для текущей таблицы."""
|
|
153
|
+
sql = self.SELECT_RDM_CHUNK_BOUNDED_SQL.format(
|
|
154
|
+
table_name=self.get_table_name(),
|
|
155
|
+
chunk_size=settings.CLEANUP_MODELS_OUTDATED_DATA_CHUNK_SIZE,
|
|
156
|
+
)
|
|
157
|
+
with connection.cursor() as cursor:
|
|
158
|
+
cursor.execute(sql)
|
|
159
|
+
return cursor.fetchall()
|
|
160
|
+
|
|
161
|
+
def _log_query(self, sql: str):
|
|
162
|
+
"""Логирует SQL-запрос."""
|
|
163
|
+
try:
|
|
164
|
+
import sqlparse
|
|
165
|
+
sql = sqlparse.format(sql, reindent=True, strip_comments=True)
|
|
166
|
+
except ImportError:
|
|
167
|
+
pass
|
|
168
|
+
|
|
169
|
+
logger.info(
|
|
170
|
+
f'Запрос для удаления устаревших данных модели {self.get_table_name()}:\n{sql}\n'
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
def _execute_delete_sql(self, delete_sql: str) -> int:
|
|
174
|
+
"""Выполняет SQL-запрос на удаление (или только логирует в safe-режиме)."""
|
|
175
|
+
deleted = 0
|
|
176
|
+
if self._log_sql:
|
|
177
|
+
self._log_query(delete_sql)
|
|
178
|
+
|
|
179
|
+
if self._safe:
|
|
180
|
+
logger.info(
|
|
181
|
+
f'Безопасный режим включен — запрос удаления для {self.get_table_name()} не выполнен.'
|
|
182
|
+
)
|
|
183
|
+
return 0
|
|
184
|
+
else:
|
|
185
|
+
with connection.cursor() as cursor:
|
|
186
|
+
cursor.execute(delete_sql)
|
|
187
|
+
result = cursor.fetchone()
|
|
188
|
+
deleted = result[0] if result else 0
|
|
189
|
+
|
|
190
|
+
return deleted
|
|
191
|
+
|
|
192
|
+
def run(self):
|
|
193
|
+
"""Запуск очистки данных."""
|
|
194
|
+
conditions = self.get_merged_conditions()
|
|
195
|
+
|
|
196
|
+
# Разделяем по чанкам
|
|
197
|
+
chunk_bounded = self.get_chunk_bounds()
|
|
198
|
+
for chunk_number, first_id, last_id in chunk_bounded:
|
|
199
|
+
while True:
|
|
200
|
+
delete_sql = self.REMOVE_OUTDATED_DATA_SQL.format(
|
|
201
|
+
table_name=self.get_table_name(),
|
|
202
|
+
first_id=first_id,
|
|
203
|
+
last_id=last_id,
|
|
204
|
+
conditions=conditions,
|
|
205
|
+
)
|
|
206
|
+
deleted = self._execute_delete_sql(delete_sql)
|
|
207
|
+
self._deleted_count += deleted
|
|
208
|
+
|
|
209
|
+
if deleted < self.chunk_size:
|
|
210
|
+
break
|
|
211
|
+
|
|
212
|
+
logger.info(
|
|
213
|
+
f'Удалено устаревших записей сервисной модели {self.model.__name__}: {self._deleted_count}'
|
|
214
|
+
)
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
from typing import (
|
|
2
|
+
Optional,
|
|
3
|
+
)
|
|
4
|
+
|
|
5
|
+
from django.core.exceptions import (
|
|
6
|
+
FieldDoesNotExist,
|
|
7
|
+
)
|
|
8
|
+
from django.db.models import (
|
|
9
|
+
Subquery,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
from edu_rdm_integration.rdm_models.models import (
|
|
13
|
+
RDMModelEnum,
|
|
14
|
+
)
|
|
15
|
+
from edu_rdm_integration.stages.collect_data.models import (
|
|
16
|
+
RDMCollectingDataCommandProgress,
|
|
17
|
+
RDMCollectingDataStage,
|
|
18
|
+
RDMCollectingDataSubStage,
|
|
19
|
+
)
|
|
20
|
+
from educommon.utils.seqtools import (
|
|
21
|
+
make_chunks,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
from web_edu.plugins.regional_data_mart_integration.models import (
|
|
25
|
+
LessonClass,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
from .base import (
|
|
29
|
+
BaseServiceOutdatedDataCleaner,
|
|
30
|
+
)
|
|
31
|
+
from .consts import (
|
|
32
|
+
OLD_RDM_MODEL,
|
|
33
|
+
UNION_CHUNK_SIZE,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class CollectingDataSubStageCleaner(BaseServiceOutdatedDataCleaner):
|
|
38
|
+
"""Очистка подэтапов сбора данных, которые не ссылаются ни на одну модель РВД.
|
|
39
|
+
|
|
40
|
+
Подход:
|
|
41
|
+
- Проходим по всем моделям, зарегистрированным в RDMModelEnum, и собираем
|
|
42
|
+
значения полей collecting_sub_stage_id (если модель содержит такое поле).
|
|
43
|
+
- Объединяем запросы по моделям в UNION, получая набор валидных id.
|
|
44
|
+
- Удаляем те подэтапы, id которых отсутствуют в полученном наборе.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
model = RDMCollectingDataSubStage
|
|
48
|
+
|
|
49
|
+
def _get_valid_substage_ids_subquery(self) -> Optional[Subquery]:
|
|
50
|
+
"""Подзапрос, возвращающий все допустимые collecting_sub_stage_id из моделей, описанных в RDMModelEnum."""
|
|
51
|
+
model_enum_values = RDMModelEnum.get_model_enum_values()
|
|
52
|
+
all_model = [model_enum.model for model_enum in model_enum_values] + OLD_RDM_MODEL
|
|
53
|
+
chunk_queries = []
|
|
54
|
+
|
|
55
|
+
for enum_values_chunk in make_chunks(all_model, UNION_CHUNK_SIZE, is_list=True):
|
|
56
|
+
qs_list = []
|
|
57
|
+
for model_cls in enum_values_chunk:
|
|
58
|
+
if model_cls in [LessonClass]:
|
|
59
|
+
continue
|
|
60
|
+
try:
|
|
61
|
+
model_cls._meta.get_field('collecting_sub_stage_id')
|
|
62
|
+
except FieldDoesNotExist:
|
|
63
|
+
continue
|
|
64
|
+
|
|
65
|
+
qs_list.append(model_cls.objects.values('collecting_sub_stage_id'))
|
|
66
|
+
|
|
67
|
+
if qs_list:
|
|
68
|
+
chunk_union = qs_list[0].union(*qs_list[1:])
|
|
69
|
+
chunk_queries.append(chunk_union)
|
|
70
|
+
|
|
71
|
+
if not chunk_queries:
|
|
72
|
+
return
|
|
73
|
+
|
|
74
|
+
# Объединяем все чанки в один общий UNION
|
|
75
|
+
full_union = chunk_queries[0].union(*chunk_queries[1:])
|
|
76
|
+
|
|
77
|
+
return Subquery(full_union)
|
|
78
|
+
|
|
79
|
+
def get_merged_conditions(self) -> str:
|
|
80
|
+
"""Формирует условие удаления для устаревших данных.
|
|
81
|
+
|
|
82
|
+
Удаляем подэтапы, которых нет в объединённом наборе валидных collecting_sub_stage_id
|
|
83
|
+
(т.е. подэтапы, не используемые ни одной моделью данных).
|
|
84
|
+
"""
|
|
85
|
+
conditions = ''
|
|
86
|
+
subquery = self._get_valid_substage_ids_subquery()
|
|
87
|
+
if subquery:
|
|
88
|
+
conditions = f"""
|
|
89
|
+
NOT EXISTS (
|
|
90
|
+
SELECT collecting_sub_stage_id
|
|
91
|
+
FROM ({str(subquery.query)}) AS valid
|
|
92
|
+
WHERE valid.collecting_sub_stage_id = tbl.id
|
|
93
|
+
)
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
return conditions
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class CollectingDataStageCleaner(BaseServiceOutdatedDataCleaner):
|
|
100
|
+
"""Очистка этапов сбора данных, у которых нет связанных подэтапов."""
|
|
101
|
+
|
|
102
|
+
model = RDMCollectingDataStage
|
|
103
|
+
|
|
104
|
+
def get_merged_conditions(self) -> str:
|
|
105
|
+
"""Формирует условие удаления для устаревших данных."""
|
|
106
|
+
sub_stage_table = CollectingDataSubStageCleaner.get_table_name()
|
|
107
|
+
|
|
108
|
+
return self.get_orphan_reference_condition(sub_stage_table, 'stage_id')
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class CollectingDataCommandProgressCleaner(BaseServiceOutdatedDataCleaner):
|
|
112
|
+
"""Очистка устаревших хранящихся задач по сбору данных."""
|
|
113
|
+
|
|
114
|
+
model = RDMCollectingDataCommandProgress
|
|
115
|
+
|
|
116
|
+
def get_merged_conditions(self) -> str:
|
|
117
|
+
"""Формирует условие удаления для устаревших данных."""
|
|
118
|
+
stage_table = CollectingDataStageCleaner.get_table_name()
|
|
119
|
+
conditions = [
|
|
120
|
+
'stage_id IS NULL',
|
|
121
|
+
f'({self.get_status_condition(stage_table, "id", "FINISHED", 7, "stage_id")})',
|
|
122
|
+
f'({self.get_status_condition(stage_table, "id", "FAILED", 30, "stage_id")})',
|
|
123
|
+
f'({self.get_orphan_reference_condition(stage_table, "id", "stage_id")})',
|
|
124
|
+
]
|
|
125
|
+
|
|
126
|
+
return " OR ".join(conditions)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from web_edu.plugins.regional_data_mart_integration.models.homework import Homework
|
|
2
|
+
from web_edu.plugins.regional_data_mart_integration.models.homework_material import HomeworkMaterial
|
|
3
|
+
from web_edu.plugins.regional_data_mart_integration.models.homework_student import HomeworkStudent
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
UNION_CHUNK_SIZE = 5
|
|
7
|
+
|
|
8
|
+
OLD_RDM_MODEL = [
|
|
9
|
+
HomeworkStudent,
|
|
10
|
+
HomeworkMaterial,
|
|
11
|
+
Homework,
|
|
12
|
+
]
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
from typing import (
|
|
2
|
+
Optional,
|
|
3
|
+
)
|
|
4
|
+
|
|
5
|
+
from django.core.exceptions import (
|
|
6
|
+
FieldDoesNotExist,
|
|
7
|
+
)
|
|
8
|
+
from django.db.models import (
|
|
9
|
+
Subquery,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
from edu_rdm_integration.rdm_models.models import (
|
|
13
|
+
RDMModelEnum,
|
|
14
|
+
)
|
|
15
|
+
from edu_rdm_integration.stages.export_data.models import (
|
|
16
|
+
RDMExportingDataCommandProgress,
|
|
17
|
+
RDMExportingDataStage,
|
|
18
|
+
RDMExportingDataSubStage,
|
|
19
|
+
RDMExportingDataSubStageAttachment,
|
|
20
|
+
RDMExportingDataSubStageEntity,
|
|
21
|
+
)
|
|
22
|
+
from educommon.utils.seqtools import (
|
|
23
|
+
make_chunks,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
from .base import (
|
|
27
|
+
BaseServiceOutdatedDataCleaner,
|
|
28
|
+
)
|
|
29
|
+
from .consts import (
|
|
30
|
+
OLD_RDM_MODEL,
|
|
31
|
+
UNION_CHUNK_SIZE,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class ExportingDataSubStageCleaner(BaseServiceOutdatedDataCleaner):
|
|
36
|
+
"""Очистка подэтапов выгрузки данных, которые не ссылаются ни на одну модель РВД.
|
|
37
|
+
|
|
38
|
+
Подход:
|
|
39
|
+
- Проходим по всем моделям, зарегистрированным в RDMModelEnum, и собираем
|
|
40
|
+
значения полей exporting_sub_stage_id (если модель содержит такое поле).
|
|
41
|
+
- Объединяем запросы по моделям в UNION, получая набор валидных id.
|
|
42
|
+
- Удаляем те подэтапы, id которых отсутствуют в полученном наборе.
|
|
43
|
+
"""
|
|
44
|
+
model = RDMExportingDataSubStage
|
|
45
|
+
|
|
46
|
+
def _get_valid_substage_ids_subquery(self) -> Optional[Subquery]:
|
|
47
|
+
"""Подзапрос, возвращающий все допустимые exporting_sub_stage_id из моделей, описанных в RDMModelEnum."""
|
|
48
|
+
model_enum_values = RDMModelEnum.get_model_enum_values()
|
|
49
|
+
all_model = [model_enum.model for model_enum in model_enum_values] + OLD_RDM_MODEL
|
|
50
|
+
chunk_queries = []
|
|
51
|
+
|
|
52
|
+
for enum_values_chunk in make_chunks(all_model, UNION_CHUNK_SIZE, is_list=True):
|
|
53
|
+
qs_list = []
|
|
54
|
+
for model_cls in enum_values_chunk:
|
|
55
|
+
try:
|
|
56
|
+
model_cls._meta.get_field('exporting_sub_stage_id')
|
|
57
|
+
except FieldDoesNotExist:
|
|
58
|
+
continue
|
|
59
|
+
qs_list.append(model_cls.objects.values('exporting_sub_stage_id'))
|
|
60
|
+
|
|
61
|
+
if qs_list:
|
|
62
|
+
chunk_union = qs_list[0].union(*qs_list[1:])
|
|
63
|
+
chunk_queries.append(chunk_union)
|
|
64
|
+
|
|
65
|
+
if not chunk_queries:
|
|
66
|
+
return
|
|
67
|
+
|
|
68
|
+
# Объединяем все чанки в один общий UNION
|
|
69
|
+
full_union = chunk_queries[0].union(*chunk_queries[1:])
|
|
70
|
+
|
|
71
|
+
return Subquery(full_union.values('exporting_sub_stage_id'))
|
|
72
|
+
|
|
73
|
+
def get_merged_conditions(self) -> str:
|
|
74
|
+
"""Формирует условие удаления для устаревших данных.
|
|
75
|
+
|
|
76
|
+
Удаляем подэтапы, которых нет в объединённом наборе валидных exporting_sub_stage_id
|
|
77
|
+
(т.е. подэтапы, не используемые ни одной моделью данных).
|
|
78
|
+
"""
|
|
79
|
+
conditions = ''
|
|
80
|
+
subquery = self._get_valid_substage_ids_subquery()
|
|
81
|
+
if subquery:
|
|
82
|
+
conditions = f"""
|
|
83
|
+
NOT EXISTS (
|
|
84
|
+
SELECT exporting_sub_stage_id
|
|
85
|
+
FROM ({str(subquery.query)}) AS valid
|
|
86
|
+
WHERE valid.exporting_sub_stage_id = tbl.id
|
|
87
|
+
)
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
return conditions
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class ExportingDataStageCleaner(BaseServiceOutdatedDataCleaner):
|
|
94
|
+
"""Очистка этапов выгрузки данных без подэтапов."""
|
|
95
|
+
|
|
96
|
+
model = RDMExportingDataStage
|
|
97
|
+
|
|
98
|
+
def get_merged_conditions(self) -> str:
|
|
99
|
+
"""Формирует условие удаления для устаревших данных."""
|
|
100
|
+
sub_stage_table = ExportingDataSubStageCleaner.get_table_name()
|
|
101
|
+
|
|
102
|
+
return self.get_orphan_reference_condition(sub_stage_table, 'stage_id')
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class ExportingDataSubStageAttachmentCleaner(BaseServiceOutdatedDataCleaner):
|
|
106
|
+
"""Очистка вложений подэтапов выгрузки данных."""
|
|
107
|
+
|
|
108
|
+
model = RDMExportingDataSubStageAttachment
|
|
109
|
+
|
|
110
|
+
def get_merged_conditions(self) -> str:
|
|
111
|
+
"""Формирует условие удаления для устаревших данных."""
|
|
112
|
+
sub_stage_table = ExportingDataSubStageCleaner.get_table_name()
|
|
113
|
+
conditions = [
|
|
114
|
+
f'({self.get_status_condition(sub_stage_table, "id", "FINISHED", 7, "exporting_data_sub_stage_id")})',
|
|
115
|
+
f'({self.get_status_condition(sub_stage_table, "id", "FAILED",30, "exporting_data_sub_stage_id")})',
|
|
116
|
+
f'({self.get_orphan_reference_condition(sub_stage_table, "id", "exporting_data_sub_stage_id")})',
|
|
117
|
+
]
|
|
118
|
+
|
|
119
|
+
return ' OR '.join(conditions)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class ExportingDataSubStageEntityCleaner(BaseServiceOutdatedDataCleaner):
|
|
123
|
+
"""Очистка связей сущности и подэтапов выгрузки данных."""
|
|
124
|
+
|
|
125
|
+
model = RDMExportingDataSubStageEntity
|
|
126
|
+
|
|
127
|
+
def get_merged_conditions(self) -> str:
|
|
128
|
+
"""Формирует условие удаления для устаревших данных."""
|
|
129
|
+
sub_stage_table = ExportingDataSubStageCleaner.get_table_name()
|
|
130
|
+
conditions = [
|
|
131
|
+
f'({self.get_status_condition(sub_stage_table, "id", "FINISHED", 7, "exporting_data_sub_stage_id")})',
|
|
132
|
+
f'({self.get_status_condition(sub_stage_table, "id", "FAILED",30, "exporting_data_sub_stage_id")})',
|
|
133
|
+
f'({self.get_orphan_reference_condition(sub_stage_table, "id", "exporting_data_sub_stage_id")})',
|
|
134
|
+
]
|
|
135
|
+
|
|
136
|
+
return ' OR '.join(conditions)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class ExportingDataCommandProgressCleaner(BaseServiceOutdatedDataCleaner):
|
|
140
|
+
"""Очистка устаревших хранящихся задач по экспорту данных."""
|
|
141
|
+
|
|
142
|
+
model = RDMExportingDataCommandProgress
|
|
143
|
+
|
|
144
|
+
def get_merged_conditions(self) -> str:
|
|
145
|
+
"""Формирует условие удаления для устаревших данных."""
|
|
146
|
+
stage_table = ExportingDataStageCleaner.get_table_name()
|
|
147
|
+
conditions = [
|
|
148
|
+
'stage_id IS NULL',
|
|
149
|
+
f'({self.get_status_condition(stage_table, "id", "FINISHED", 7, "stage_id")})',
|
|
150
|
+
f'({self.get_status_condition(stage_table, "id", "FAILED",30, "stage_id")})',
|
|
151
|
+
f'({self.get_orphan_reference_condition(stage_table, "id", "stage_id")})',
|
|
152
|
+
]
|
|
153
|
+
|
|
154
|
+
return ' OR '.join(conditions)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from edu_rdm_integration.stages.upload_data.models import (
|
|
2
|
+
RDMExportingDataSubStageUploaderClientLog,
|
|
3
|
+
RDMUploadStatusRequestLog,
|
|
4
|
+
)
|
|
5
|
+
from uploader_client.models import (
|
|
6
|
+
Entry,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
from .base import (
|
|
10
|
+
BaseServiceOutdatedDataCleaner,
|
|
11
|
+
)
|
|
12
|
+
from .export_data import (
|
|
13
|
+
ExportingDataSubStageAttachmentCleaner,
|
|
14
|
+
ExportingDataSubStageCleaner,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ExportingDataSubStageUploaderClientLogCleaner(BaseServiceOutdatedDataCleaner):
|
|
19
|
+
"""Очистка логов загрузчика подэтапов выгрузки данных без связи с подэтапами или файлами."""
|
|
20
|
+
|
|
21
|
+
model = RDMExportingDataSubStageUploaderClientLog
|
|
22
|
+
|
|
23
|
+
def get_merged_conditions(self) -> str:
|
|
24
|
+
"""Формирует условие удаления для устаревших данных."""
|
|
25
|
+
sub_stage_table = ExportingDataSubStageCleaner.get_table_name()
|
|
26
|
+
attachment_table = ExportingDataSubStageAttachmentCleaner.get_table_name()
|
|
27
|
+
|
|
28
|
+
conditions = [
|
|
29
|
+
f'({self.get_status_condition(sub_stage_table, "id", "FINISHED", 7, "sub_stage_id")})',
|
|
30
|
+
f'({self.get_status_condition(sub_stage_table, "id", "FAILED", 30, "sub_stage_id")})',
|
|
31
|
+
f'({self.get_orphan_reference_condition(sub_stage_table, "id", local_field="sub_stage_id")})',
|
|
32
|
+
f'({self.get_orphan_reference_condition(attachment_table, "id", local_field="attachment_id")})',
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
return ' OR '.join(conditions)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class UploadStatusRequestLogCleaner(BaseServiceOutdatedDataCleaner):
|
|
39
|
+
"""Очистка логов статуса загрузки файла в витрину без связей upload."""
|
|
40
|
+
|
|
41
|
+
model = RDMUploadStatusRequestLog
|
|
42
|
+
|
|
43
|
+
def get_merged_conditions(self) -> str:
|
|
44
|
+
"""Формирует условие удаления для устаревших данных."""
|
|
45
|
+
uploader_client_log_table = ExportingDataSubStageUploaderClientLogCleaner.get_table_name()
|
|
46
|
+
|
|
47
|
+
return self.get_orphan_reference_condition(uploader_client_log_table, 'id', local_field='upload_id')
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class EntryCleaner(BaseServiceOutdatedDataCleaner):
|
|
51
|
+
"""Очистка записей журнала, не связанные ни с upload, ни с логами."""
|
|
52
|
+
|
|
53
|
+
model = Entry
|
|
54
|
+
|
|
55
|
+
def get_merged_conditions(self) -> str:
|
|
56
|
+
"""Формирует условие удаления для устаревших данных."""
|
|
57
|
+
uploader_client_log_table = ExportingDataSubStageUploaderClientLogCleaner.get_table_name()
|
|
58
|
+
upload_status_log_table = UploadStatusRequestLogCleaner.get_table_name()
|
|
59
|
+
conditions = [
|
|
60
|
+
f'({self.get_orphan_reference_condition(uploader_client_log_table, "entry_id")})',
|
|
61
|
+
f'({self.get_orphan_reference_condition(upload_status_log_table, "entry_id")})',
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
return ' AND '.join(conditions)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
from typing import (
|
|
2
|
+
Optional,
|
|
3
|
+
)
|
|
4
|
+
|
|
5
|
+
from edu_rdm_integration.stages.service.service_outdated_data.cleaners.collect_data import (
|
|
6
|
+
CollectingDataCommandProgressCleaner,
|
|
7
|
+
CollectingDataStageCleaner,
|
|
8
|
+
CollectingDataSubStageCleaner,
|
|
9
|
+
)
|
|
10
|
+
from edu_rdm_integration.stages.service.service_outdated_data.cleaners.export_data import (
|
|
11
|
+
ExportingDataCommandProgressCleaner,
|
|
12
|
+
ExportingDataStageCleaner,
|
|
13
|
+
ExportingDataSubStageAttachmentCleaner,
|
|
14
|
+
ExportingDataSubStageCleaner,
|
|
15
|
+
ExportingDataSubStageEntityCleaner,
|
|
16
|
+
)
|
|
17
|
+
from edu_rdm_integration.stages.service.service_outdated_data.cleaners.upload_data import (
|
|
18
|
+
EntryCleaner,
|
|
19
|
+
ExportingDataSubStageUploaderClientLogCleaner,
|
|
20
|
+
UploadStatusRequestLogCleaner,
|
|
21
|
+
)
|
|
22
|
+
from educommon import (
|
|
23
|
+
logger,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ServiceOutdatedDataCleanerManager:
|
|
29
|
+
"""Управляет очисткой устаревших сервисных данных по разным этапам."""
|
|
30
|
+
|
|
31
|
+
STAGES_MAPPING = {
|
|
32
|
+
'collect': [
|
|
33
|
+
CollectingDataSubStageCleaner,
|
|
34
|
+
CollectingDataStageCleaner,
|
|
35
|
+
CollectingDataCommandProgressCleaner,
|
|
36
|
+
],
|
|
37
|
+
'export': [
|
|
38
|
+
ExportingDataSubStageCleaner,
|
|
39
|
+
ExportingDataStageCleaner,
|
|
40
|
+
ExportingDataSubStageAttachmentCleaner,
|
|
41
|
+
ExportingDataSubStageEntityCleaner,
|
|
42
|
+
ExportingDataCommandProgressCleaner,
|
|
43
|
+
],
|
|
44
|
+
'upload': [
|
|
45
|
+
ExportingDataSubStageUploaderClientLogCleaner,
|
|
46
|
+
UploadStatusRequestLogCleaner,
|
|
47
|
+
EntryCleaner,
|
|
48
|
+
],
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
def __init__(
|
|
52
|
+
self,
|
|
53
|
+
*args,
|
|
54
|
+
stages: Optional[list[str]]= None,
|
|
55
|
+
safe: bool = False,
|
|
56
|
+
log_sql: bool = False,
|
|
57
|
+
**kwargs
|
|
58
|
+
):
|
|
59
|
+
"""Инициализация менеджера."""
|
|
60
|
+
self._stages = stages
|
|
61
|
+
self._safe = safe
|
|
62
|
+
self._log_sql = log_sql
|
|
63
|
+
|
|
64
|
+
super().__init__(*args, **kwargs)
|
|
65
|
+
|
|
66
|
+
def _process_stage(self, stage: str):
|
|
67
|
+
"""Запускает все зарегистрированные уборщики для указанного этапа."""
|
|
68
|
+
cleaners = self.STAGES_MAPPING.get(stage, [])
|
|
69
|
+
if not cleaners:
|
|
70
|
+
logger.info(f'Для этапа "{stage}" нет зарегистрированных уборщиков.')
|
|
71
|
+
return
|
|
72
|
+
|
|
73
|
+
for cleaner_cls in cleaners:
|
|
74
|
+
cleaner_cls(safe=self._safe, log_sql=self._log_sql).run()
|
|
75
|
+
|
|
76
|
+
def run(self):
|
|
77
|
+
"""Запускает очистку устаревших данных сервисных моделей РВД."""
|
|
78
|
+
stages_to_process = self._stages or self.STAGES_MAPPING.keys()
|
|
79
|
+
for stage in stages_to_process:
|
|
80
|
+
self._process_stage(stage)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Generated by Django 3.2.24 on 2025-10-08 14:17
|
|
2
|
+
|
|
3
|
+
import django.db.models.deletion
|
|
4
|
+
from django.db import (
|
|
5
|
+
migrations,
|
|
6
|
+
models,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Migration(migrations.Migration):
|
|
11
|
+
|
|
12
|
+
dependencies = [
|
|
13
|
+
('uploader_client', '0001_initial'),
|
|
14
|
+
('edu_rdm_integration_upload_data_stage', '0002_auto_20250704_0810'),
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
operations = [
|
|
18
|
+
migrations.AlterField(
|
|
19
|
+
model_name='rdmexportingdatasubstageuploaderclientlog',
|
|
20
|
+
name='entry',
|
|
21
|
+
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='uploader_client_log', to='uploader_client.entry', verbose_name='Лог запроса и ответа'),
|
|
22
|
+
),
|
|
23
|
+
migrations.AlterField(
|
|
24
|
+
model_name='rdmuploadstatusrequestlog',
|
|
25
|
+
name='entry',
|
|
26
|
+
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='upload_status_request_log', to='uploader_client.entry', verbose_name='Cвязь запроса статуса с запросом в витрину'),
|
|
27
|
+
),
|
|
28
|
+
]
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Generated by Django 3.2.24 on 2025-10-08 14:43
|
|
2
|
+
|
|
3
|
+
from django.db import (
|
|
4
|
+
migrations,
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
from edu_rdm_integration.stages.utils import (
|
|
8
|
+
update_foreign_key_constraint,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def apply_fk_updates(apps, schema_editor):
|
|
13
|
+
"""
|
|
14
|
+
Применяет обновления внешних ключей для конкретных моделей.
|
|
15
|
+
|
|
16
|
+
Здесь задаются таблицы и поведение при удалении записей,
|
|
17
|
+
чтобы синхронизировать фактическое состояние БД с логикой моделей.
|
|
18
|
+
"""
|
|
19
|
+
Entry = apps.get_model('smev_agent_client', 'Entry')
|
|
20
|
+
RDMExportingDataSubStage = apps.get_model('edu_rdm_integration_export_data_stage', 'RDMExportingDataSubStage')
|
|
21
|
+
RDMExportingDataSubStageAttachment = apps.get_model(
|
|
22
|
+
'edu_rdm_integration_export_data_stage', 'RDMExportingDataSubStageAttachment'
|
|
23
|
+
)
|
|
24
|
+
RDMExportingDataSubStageUploaderClientLog = apps.get_model(
|
|
25
|
+
'edu_rdm_integration_upload_data_stage', 'RDMExportingDataSubStageUploaderClientLog'
|
|
26
|
+
)
|
|
27
|
+
# Модель: RDMExportingDataSubStageUploaderClientLog
|
|
28
|
+
# Поле sub_stage_id должно иметь ON DELETE CASCADE
|
|
29
|
+
update_foreign_key_constraint(
|
|
30
|
+
table_name=RDMExportingDataSubStageUploaderClientLog._meta.db_table,
|
|
31
|
+
field_name="sub_stage_id",
|
|
32
|
+
target_table=RDMExportingDataSubStage._meta.db_table,
|
|
33
|
+
on_delete="CASCADE",
|
|
34
|
+
)
|
|
35
|
+
# Модель: RDMExportingDataSubStageUploaderClientLog
|
|
36
|
+
# Поле attachment_id должно иметь ON DELETE CASCADE
|
|
37
|
+
update_foreign_key_constraint(
|
|
38
|
+
table_name=RDMExportingDataSubStageUploaderClientLog._meta.db_table,
|
|
39
|
+
field_name="attachment_id",
|
|
40
|
+
target_table=RDMExportingDataSubStageAttachment._meta.db_table,
|
|
41
|
+
on_delete="CASCADE",
|
|
42
|
+
)
|
|
43
|
+
# Модель: RDMExportingDataSubStageUploaderClientLog
|
|
44
|
+
# Поле entry_id должно иметь ON DELETE SET NULL
|
|
45
|
+
update_foreign_key_constraint(
|
|
46
|
+
table_name=RDMExportingDataSubStageUploaderClientLog._meta.db_table,
|
|
47
|
+
field_name="entry_id",
|
|
48
|
+
target_table=Entry._meta.db_table,
|
|
49
|
+
on_delete="SET NULL",
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
RDMUploadStatusRequestLog = apps.get_model('edu_rdm_integration_upload_data_stage', 'RDMUploadStatusRequestLog')
|
|
53
|
+
# Модель: RDMUploadStatusRequestLog
|
|
54
|
+
# Поле upload_id должно иметь ON DELETE CASCADE
|
|
55
|
+
update_foreign_key_constraint(
|
|
56
|
+
table_name=RDMUploadStatusRequestLog._meta.db_table,
|
|
57
|
+
field_name="upload_id",
|
|
58
|
+
target_table=RDMExportingDataSubStageUploaderClientLog._meta.db_table,
|
|
59
|
+
on_delete="CASCADE",
|
|
60
|
+
)
|
|
61
|
+
# Модель: RDMUploadStatusRequestLog
|
|
62
|
+
# Поле entry_id должно иметь ON DELETE SET NULL
|
|
63
|
+
update_foreign_key_constraint(
|
|
64
|
+
table_name=RDMUploadStatusRequestLog._meta.db_table,
|
|
65
|
+
field_name="entry_id",
|
|
66
|
+
target_table=Entry._meta.db_table,
|
|
67
|
+
on_delete="SET NULL",
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class Migration(migrations.Migration):
|
|
72
|
+
|
|
73
|
+
dependencies = [
|
|
74
|
+
('edu_rdm_integration_upload_data_stage', '0003_auto_20251006_1417'),
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
operations = [
|
|
78
|
+
migrations.RunPython(apply_fk_updates, reverse_code=migrations.RunPython.noop),
|
|
79
|
+
]
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from django.db.models import (
|
|
2
2
|
CASCADE,
|
|
3
3
|
PROTECT,
|
|
4
|
+
SET_NULL,
|
|
4
5
|
BooleanField,
|
|
5
6
|
CharField,
|
|
6
7
|
DateTimeField,
|
|
@@ -91,7 +92,8 @@ class RDMExportingDataSubStageUploaderClientLog(ReprStrPreModelMixin, BaseObject
|
|
|
91
92
|
entry = ForeignKey(
|
|
92
93
|
to=Entry,
|
|
93
94
|
verbose_name='Лог запроса и ответа',
|
|
94
|
-
on_delete=
|
|
95
|
+
on_delete=SET_NULL,
|
|
96
|
+
null=True,
|
|
95
97
|
related_name='uploader_client_log',
|
|
96
98
|
)
|
|
97
99
|
|
|
@@ -158,7 +160,8 @@ class RDMUploadStatusRequestLog(ReprStrPreModelMixin, BaseObjectModel):
|
|
|
158
160
|
entry = ForeignKey(
|
|
159
161
|
verbose_name='Cвязь запроса статуса с запросом в витрину',
|
|
160
162
|
to=Entry,
|
|
161
|
-
|
|
163
|
+
null=True,
|
|
164
|
+
on_delete=SET_NULL,
|
|
162
165
|
related_name='upload_status_request_log',
|
|
163
166
|
)
|
|
164
167
|
request_status = ForeignKey(
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from django.db import (
|
|
2
|
+
connection,
|
|
3
|
+
)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def update_foreign_key_constraint(
|
|
7
|
+
table_name: str,
|
|
8
|
+
field_name: str,
|
|
9
|
+
target_table: str,
|
|
10
|
+
on_delete: str,
|
|
11
|
+
):
|
|
12
|
+
"""
|
|
13
|
+
Обновляет внешний ключ (FOREIGN KEY) в PostgreSQL.
|
|
14
|
+
|
|
15
|
+
Выполняет:
|
|
16
|
+
1. Поиск существующего constraint'а по таблице и целевой модели.
|
|
17
|
+
2. Удаление найденного constraint'а.
|
|
18
|
+
3. Создание нового constraint'а с заданным ON DELETE.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
table_name (str): Имя таблицы, содержащей внешний ключ.
|
|
22
|
+
field_name (str): Имя поля внешнего ключа.
|
|
23
|
+
target_table (str): Таблица, на которую указывает внешний ключ.
|
|
24
|
+
on_delete (str): Поведение при удалении связанной записи.
|
|
25
|
+
"""
|
|
26
|
+
with connection.cursor() as cursor:
|
|
27
|
+
# Найти имя constraint'а
|
|
28
|
+
cursor.execute(f"""
|
|
29
|
+
SELECT conname
|
|
30
|
+
FROM pg_constraint
|
|
31
|
+
WHERE conrelid = '{table_name}'::regclass
|
|
32
|
+
AND confrelid = '{target_table}'::regclass
|
|
33
|
+
AND contype = 'f'
|
|
34
|
+
""")
|
|
35
|
+
row = cursor.fetchone()
|
|
36
|
+
if not row:
|
|
37
|
+
return
|
|
38
|
+
|
|
39
|
+
constraint_name = row[0]
|
|
40
|
+
|
|
41
|
+
# Удалить старый constraint, Добавить новый constraint с нужным ON DELETE
|
|
42
|
+
cursor.execute(f'''
|
|
43
|
+
ALTER TABLE "{table_name}"
|
|
44
|
+
DROP CONSTRAINT IF EXISTS "{constraint_name}",
|
|
45
|
+
ADD CONSTRAINT "{constraint_name}"
|
|
46
|
+
FOREIGN KEY ("{field_name}")
|
|
47
|
+
REFERENCES "{target_table}"(id)
|
|
48
|
+
ON DELETE {on_delete};
|
|
49
|
+
''')
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: edu-rdm-integration
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.22.1
|
|
4
4
|
Summary: Интеграция с Региональной витриной данных
|
|
5
5
|
Author-email: BARS Group <education_dev@bars.group>
|
|
6
6
|
Project-URL: Homepage, https://stash.bars-open.ru/projects/EDUBASE/repos/edu-rdm-integration/browse
|
|
@@ -153,6 +153,7 @@ INSTALLED_APPS = (
|
|
|
153
153
|
('rdm_general', 'EXPORT_CHUNK_SIZE'): 500,
|
|
154
154
|
('rdm_general', 'UPLOAD_QUEUE_MAX_SIZE'): 500_000_000,
|
|
155
155
|
('rdm_general', 'RDM_MENU_ITEM'): False,
|
|
156
|
+
('rdm_general', 'COLLECT_PROGRESS_BATCH_SIZE'): 10000,
|
|
156
157
|
('rdm_transfer_task', 'MINUTE'): '0',
|
|
157
158
|
('rdm_transfer_task', 'HOUR'): '*/4',
|
|
158
159
|
('rdm_transfer_task', 'DAY_OF_WEEK'): '*',
|
|
@@ -198,6 +199,7 @@ INSTALLED_APPS = (
|
|
|
198
199
|
('uploader_client', 'INSTALLATION_NAME'): '',
|
|
199
200
|
('uploader_client', 'INSTALLATION_ID'): '',
|
|
200
201
|
('rdm_cleanup_outdated_data', 'ENABLE_CLEANUP_MODELS_OUTDATED_DATA'): False,
|
|
202
|
+
('rdm_cleanup_outdated_data', 'ENABLE_CLEANUP_SERVICE_OUTDATED_DATA'): False,
|
|
201
203
|
('rdm_cleanup_outdated_data', 'CLEANUP_MODELS_OUTDATED_DATA_CHUNK_SIZE'): 10000,
|
|
202
204
|
('rdm_cleanup_outdated_data', 'CLEANUP_MODELS_OUTDATED_DATA_POOL_SIZE'): 10,
|
|
203
205
|
})
|
|
@@ -230,6 +232,9 @@ INSTALLED_APPS = (
|
|
|
230
232
|
# Пункт меню "Региональная витрина данных" - Спрятать (False) / Отображать (True)
|
|
231
233
|
RDM_MENU_ITEM = conf.get_bool('rdm_general', 'RDM_MENU_ITEM')
|
|
232
234
|
|
|
235
|
+
# Размер батча для bulk_create операций при сборе данных РВД.
|
|
236
|
+
RDM_COLLECT_PROGRESS_BATCH_SIZE = conf.get_int('rmd_general', 'COLLECT_PROGRESS_BATCH_SIZE')
|
|
237
|
+
|
|
233
238
|
# Настройка запуска периодической задачи выгрузки данных:
|
|
234
239
|
RDM_TRANSFER_TASK_MINUTE = conf.get('rdm_transfer_task', 'MINUTE')
|
|
235
240
|
RDM_TRANSFER_TASK_HOUR = conf.get('rdm_transfer_task', 'HOUR')
|
|
@@ -315,6 +320,8 @@ INSTALLED_APPS = (
|
|
|
315
320
|
|
|
316
321
|
# Включить зачистку устаревших данных моделей РВД
|
|
317
322
|
RDM_ENABLE_CLEANUP_MODELS_OUTDATED_DATA = conf.get_bool('rdm_cleanup_outdated_data', 'ENABLE_CLEANUP_MODELS_OUTDATED_DATA')
|
|
323
|
+
# Включить зачистку устаревших данных сервисных моделей РВД
|
|
324
|
+
RDM_ENABLE_CLEANUP_SERVICE_OUTDATED_DATA = conf.get_bool('rdm_cleanup_outdated_data', 'ENABLE_CLEANUP_SERVICE_OUTDATED_DATA')
|
|
318
325
|
# Размер чанка записей зачистки устаревших данных моделей РВД
|
|
319
326
|
RDM_CLEANUP_MODELS_OUTDATED_DATA_CHUNK_SIZE = conf.get_int('rdm_cleanup_outdated_data', 'CLEANUP_MODELS_OUTDATED_DATA_CHUNK_SIZE')
|
|
320
327
|
# Количество подключений к БД для зачистки устаревших данных моделей РВД
|
|
@@ -332,6 +339,7 @@ INSTALLED_APPS = (
|
|
|
332
339
|
| RDM_UPDATE_NON_EXPORTED_CHUNK_SIZE | # Количество не экспортированных записей моделей обрабатываемых за одну итерацию обновления поля modified | 5000 |
|
|
333
340
|
| RDM_UPLOAD_QUEUE_MAX_SIZE | Объем очереди файлов в витрину (в байтах). | 500_000_000 |
|
|
334
341
|
| RDM_MENU_ITEM | Отображение пункта меню Пункт меню "Региональная витрина данных" | False |
|
|
342
|
+
| RDM_COLLECT_PROGRESS_BATCH_SIZE | Размер батча для сохранений записей в базу при сборе данных РВД | 10000 |
|
|
335
343
|
| RDM_TRANSFER_TASK_MINUTE | Настройка запуска периодической задачи выгрузки данных. Минута | '0' |
|
|
336
344
|
| RDM_TRANSFER_TASK_HOUR | Настройка запуска периодической задачи выгрузки данных. Час | '*/4' |
|
|
337
345
|
| RDM_TRANSFER_TASK_DAY_OF_WEEK | Настройка запуска периодической задачи выгрузки данных. День недели | '*' |
|
|
@@ -379,6 +387,7 @@ INSTALLED_APPS = (
|
|
|
379
387
|
| RDM_EXPORT_LOG_DIR | Директория логов экспорта данных, доступных для скачивания | |
|
|
380
388
|
| RDM_UPLOAD_LOG_DIR | Директория логов отправки данных в витрину, доступных для скачивания | |
|
|
381
389
|
| RDM_ENABLE_CLEANUP_MODELS_OUTDATED_DATA | Включение зачистки устаревших данных моделей РВД | False |
|
|
390
|
+
| RDM_ENABLE_CLEANUP_SERVICE_OUTDATED_DATA | Включение зачистки устаревших данных сервисных моделей РВД | False |
|
|
382
391
|
| RDM_CLEANUP_MODELS_OUTDATED_DATA_CHUNK_SIZE | Размер чанка записей зачистки устаревших данных моделей РВД | 10000 |
|
|
383
392
|
| RDM_CLEANUP_MODELS_OUTDATED_DATA_POOL_SIZE | Количество подключений к БД для зачистки устаревших данных моделей РВД | 10 |
|
|
384
393
|
|
|
@@ -395,6 +404,8 @@ INSTALLED_APPS = (
|
|
|
395
404
|
EXPORT_CHUNK_SIZE = 500
|
|
396
405
|
# Количество записей моделей ЭШ обрабатываемых за одну итерацию сбора данных
|
|
397
406
|
COLLECT_CHUNK_SIZE = 500
|
|
407
|
+
# Размер батча для bulk_create операций при сборе данных РВД
|
|
408
|
+
COLLECT_PROGRESS_BATCH_SIZE = 10000
|
|
398
409
|
# Количество не экспортированных записей моделей обрабатываемых за одну итерацию обновления поля modified
|
|
399
410
|
UPDATE_NON_EXPORTED_CHUNK_SIZE = 5000
|
|
400
411
|
# Объем очереди файлов в витрину (в байтах) - по умолчанию 512 Мбайт.
|
|
@@ -482,6 +493,8 @@ INSTALLED_APPS = (
|
|
|
482
493
|
[rdm_cleanup_outdated_data]
|
|
483
494
|
# Включить зачистку устаревших данных моделей РВД
|
|
484
495
|
ENABLE_CLEANUP_MODELS_OUTDATED_DATA = False
|
|
496
|
+
# Включить зачистку устаревших данных сервисных моделей РВД
|
|
497
|
+
ENABLE_CLEANUP_SERVICE_OUTDATED_DATA = False
|
|
485
498
|
# Размер чанка записей проверки устаревших данных моделей РВД
|
|
486
499
|
CLEANUP_MODELS_OUTDATED_DATA_CHUNK_SIZE = 10000
|
|
487
500
|
# Количество подключений к БД для проверки устаревших данных моделей РВД
|
|
@@ -48,7 +48,7 @@ edu_rdm_integration/pipelines/cleanup_outdated_data/__init__.py,sha256=47DEQpj8H
|
|
|
48
48
|
edu_rdm_integration/pipelines/cleanup_outdated_data/apps.py,sha256=tyTlr1Wt574eby2vIV68FMm7SPFSZKYw6zNkD8x6COE,507
|
|
49
49
|
edu_rdm_integration/pipelines/cleanup_outdated_data/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
50
50
|
edu_rdm_integration/pipelines/cleanup_outdated_data/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
51
|
-
edu_rdm_integration/pipelines/cleanup_outdated_data/management/commands/rdm_cleanup_outdated_data.py,sha256=
|
|
51
|
+
edu_rdm_integration/pipelines/cleanup_outdated_data/management/commands/rdm_cleanup_outdated_data.py,sha256=DJU_T6Ezz48Rq6OoScjZ3ZyfHn6drlUo4_Wdj2OGAPY,4500
|
|
52
52
|
edu_rdm_integration/pipelines/transfer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
53
53
|
edu_rdm_integration/pipelines/transfer/actions.py,sha256=e94NVtTcFIqBBTZ9vbSfh_0oXUWK9ZOx2pDYnIePJVc,5920
|
|
54
54
|
edu_rdm_integration/pipelines/transfer/app_meta.py,sha256=jshfepDDJrbCACtJBJBPuidAVJ6rcziQiet27wqOIjk,373
|
|
@@ -79,6 +79,7 @@ edu_rdm_integration/rdm_models/migrations/0001_initial.py,sha256=qXgObuG2nfOLEnG
|
|
|
79
79
|
edu_rdm_integration/rdm_models/migrations/0002_rename_regionaldatamartmodelenum_rdmmodelenum.py,sha256=hNTLriOc9r9WEVKahJURA3yXhZ3ivbwJJ_HaMC46PpI,451
|
|
80
80
|
edu_rdm_integration/rdm_models/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
81
81
|
edu_rdm_integration/stages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
82
|
+
edu_rdm_integration/stages/utils.py,sha256=XTOXXP1KeLCURjkjObP-lcIEARIBkgguk4MfN2dXivQ,1780
|
|
82
83
|
edu_rdm_integration/stages/collect_data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
83
84
|
edu_rdm_integration/stages/collect_data/apps.py,sha256=PhrxzAMXvzXajSlgwLudAgasVjh9IFS5M7a7pGbEKC0,339
|
|
84
85
|
edu_rdm_integration/stages/collect_data/consts.py,sha256=tzaK9oxzdMRq3oTEocPz4umoXSJWUtFc7YhyXucCNbs,127
|
|
@@ -154,9 +155,11 @@ edu_rdm_integration/stages/collect_data/migrations/0001_initial.py,sha256=gujp1W
|
|
|
154
155
|
edu_rdm_integration/stages/collect_data/migrations/0002_edurdmcollectdatacommandprogress.py,sha256=TMePcN2Y1wuT1ZKcXgsFTClGWNsf82Ut_mDsPE0BR8Q,4841
|
|
155
156
|
edu_rdm_integration/stages/collect_data/migrations/0003_auto_20250704_0810.py,sha256=5JkymsRshIKN3az4bbpL_xQxjs6XiknwwCRISS7c2Ts,1079
|
|
156
157
|
edu_rdm_integration/stages/collect_data/migrations/0004_auto_20250704_0825.py,sha256=B6SUsxlhQvWoD8lFGNwaMUCFDzhPj91bsMdmAcSuEDg,1379
|
|
158
|
+
edu_rdm_integration/stages/collect_data/migrations/0005_alter_rdmcollectingdatasubstage_previous.py,sha256=Q7OBeKXMRf1W_BHcz99UXYjvnav85vYdjoyiDitSXnk,670
|
|
159
|
+
edu_rdm_integration/stages/collect_data/migrations/0006_fix_fk_constraints.py,sha256=H58FBhu43KHy1aw7whdA-1ZktIXIWKwp4Nx3tpLz2nI,2284
|
|
157
160
|
edu_rdm_integration/stages/collect_data/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
158
161
|
edu_rdm_integration/stages/collect_data/registry/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
159
|
-
edu_rdm_integration/stages/collect_data/registry/actions.py,sha256=
|
|
162
|
+
edu_rdm_integration/stages/collect_data/registry/actions.py,sha256=PiNoiIR6svxPwH9J5Flj9KJdwlYfh9RUqysIlGDNk-Y,10264
|
|
160
163
|
edu_rdm_integration/stages/collect_data/registry/apps.py,sha256=K5f97YXKMmdM7m33qgQYvJjrA8_eGAJ4VWyuRjJ0gwQ,439
|
|
161
164
|
edu_rdm_integration/stages/collect_data/registry/ui.py,sha256=pw13DAASxqnX_E5D4RG9CywtnQKQeljXHief7mojVgk,8398
|
|
162
165
|
edu_rdm_integration/stages/collect_data/registry/templates/ui-js/collect-command-window.js,sha256=QfxVSAA0282-41K0XGtyPa9WPzpoX_uClke8pHdAzBo,3112
|
|
@@ -209,6 +212,7 @@ edu_rdm_integration/stages/export_data/management/commands/export_entities_data.
|
|
|
209
212
|
edu_rdm_integration/stages/export_data/management/commands/export_latest_entities_data.py,sha256=eYjBs_tZxcUAIseCyvsy5Jk-8k9Gm3xrG2dCuWNnrEs,1163
|
|
210
213
|
edu_rdm_integration/stages/export_data/migrations/0001_initial.py,sha256=h7HIT-QkWONCzIergDp2c861Lw_-f9TZQ5FPIREeHTk,16864
|
|
211
214
|
edu_rdm_integration/stages/export_data/migrations/0002_auto_20250704_0810.py,sha256=yjGlH25Mke2_VAe188LEkWZ04Xy1qenai7D2zOA-ecY,1558
|
|
215
|
+
edu_rdm_integration/stages/export_data/migrations/0003_fix_fk_constraints.py,sha256=BjTE7YGk3Qtjqo0MsEJsuR0MeFzolBSm87ivqdrsr4A,2978
|
|
212
216
|
edu_rdm_integration/stages/export_data/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
213
217
|
edu_rdm_integration/stages/export_data/registry/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
214
218
|
edu_rdm_integration/stages/export_data/registry/actions.py,sha256=C5qLwGQeVd9ddOKo2zBJKBLtYn5x_8XV2MaTjK-SGFM,10940
|
|
@@ -223,6 +227,13 @@ edu_rdm_integration/stages/service/model_outdated_data/__init__.py,sha256=47DEQp
|
|
|
223
227
|
edu_rdm_integration/stages/service/model_outdated_data/cleaners.py,sha256=enlEPTj5_elo-vQTXfq0ar8i1aiN4BzfZQLXNGBsZ68,16795
|
|
224
228
|
edu_rdm_integration/stages/service/model_outdated_data/managers.py,sha256=0LNjvycTtSMGsN37U-otlPL_vlYJKZXtNXxwkseK1wA,2353
|
|
225
229
|
edu_rdm_integration/stages/service/service_outdated_data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
230
|
+
edu_rdm_integration/stages/service/service_outdated_data/managers.py,sha256=zKGQWIvv1go5OlBIkgBDLNyEKkkHvm0qKFfCatddd68,2715
|
|
231
|
+
edu_rdm_integration/stages/service/service_outdated_data/cleaners/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
232
|
+
edu_rdm_integration/stages/service/service_outdated_data/cleaners/base.py,sha256=boVxqk2OHYN1-UkOjr3aOx-ClX2YX6OOUnYPrTkZ2d8,6733
|
|
233
|
+
edu_rdm_integration/stages/service/service_outdated_data/cleaners/collect_data.py,sha256=zol47C4RQFnHd_MNldOHTtEjzeWsIZji1nDzKVVkJRg,4894
|
|
234
|
+
edu_rdm_integration/stages/service/service_outdated_data/cleaners/consts.py,sha256=ONLW_NJFbnYU1CbDF4mzBXXqDt7fyKVfep3Qlf5sJOM,384
|
|
235
|
+
edu_rdm_integration/stages/service/service_outdated_data/cleaners/export_data.py,sha256=A0denkwa7N7GxkRZ0tV4FUZAjfWkXpd7UOdYjHLD1Z4,6514
|
|
236
|
+
edu_rdm_integration/stages/service/service_outdated_data/cleaners/upload_data.py,sha256=a8oJ8Sg1eLZf1NemehREkJsvdtmyLwtOIuN5iXpzSb4,2846
|
|
226
237
|
edu_rdm_integration/stages/upload_data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
227
238
|
edu_rdm_integration/stages/upload_data/apps.py,sha256=aFhVPK-65b35CGKoAeAgQ0mm3STaWtZg7rqk3eL-b7s,620
|
|
228
239
|
edu_rdm_integration/stages/upload_data/consts.py,sha256=yTygXxS5dBRCvrE7Q3D0jEGSC5apIKvVAAViDM8QcKA,223
|
|
@@ -230,7 +241,7 @@ edu_rdm_integration/stages/upload_data/dataclasses.py,sha256=zwxlED6yEeMStfc-Y6p
|
|
|
230
241
|
edu_rdm_integration/stages/upload_data/enums.py,sha256=uGRqcflf6moPWfFa8KKbeSY2y88-AegAcRJKlQ1PA_Y,453
|
|
231
242
|
edu_rdm_integration/stages/upload_data/export_managers.py,sha256=DYHX0LwB6RkRqOEaB5EIWqbSJqjaUfR09AjRTjI2CR4,13646
|
|
232
243
|
edu_rdm_integration/stages/upload_data/helpers.py,sha256=lCvM8QsERXRXP9rcpgaSQOKUkvi3bxkeHEC_zZruQy4,6183
|
|
233
|
-
edu_rdm_integration/stages/upload_data/models.py,sha256=
|
|
244
|
+
edu_rdm_integration/stages/upload_data/models.py,sha256=4XiodbnbK2MRBi8wTfU0beZQW3dLPT4_5NAh9DM-9gw,5220
|
|
234
245
|
edu_rdm_integration/stages/upload_data/operations.py,sha256=CVQYaucerJbOAE3JDO05FaX4Bz9hBMKBXYqJohXKZpI,6462
|
|
235
246
|
edu_rdm_integration/stages/upload_data/queues.py,sha256=MbDnltQLjTCXLnNQVO0KA4SaAzyU8xO51CRid6Cle4g,6130
|
|
236
247
|
edu_rdm_integration/stages/upload_data/tasks.py,sha256=SYoQjL4sl0W0yOBFczlnQW-N4YcFiFgdMlEQUDO7IsQ,3573
|
|
@@ -243,6 +254,8 @@ edu_rdm_integration/stages/upload_data/management/commands/datamart_status.py,sh
|
|
|
243
254
|
edu_rdm_integration/stages/upload_data/management/commands/datamart_upload.py,sha256=VF94eqO3nIbvhPFwtUlYLAMwPXEU0uGt9m5xnIjLu68,1525
|
|
244
255
|
edu_rdm_integration/stages/upload_data/migrations/0001_initial.py,sha256=4dRroCI8ZxBuyRJC81si-LVAfzLMn3mYDccy48gUzCo,8080
|
|
245
256
|
edu_rdm_integration/stages/upload_data/migrations/0002_auto_20250704_0810.py,sha256=OLXfvs_x_S9W4wS6GKikNaaLUmI4h-LyLBdOs3K_e9I,818
|
|
257
|
+
edu_rdm_integration/stages/upload_data/migrations/0003_auto_20251006_1417.py,sha256=C32q9tAHXWgZjCH05yxON7fXELBR24oor1Xsf7Ms2Wc,1081
|
|
258
|
+
edu_rdm_integration/stages/upload_data/migrations/0004_fix_fk_constraints.py,sha256=OdVgXHEdr-EMdP0N2Qf5Aq4yShNiua90fmPpHjgvfAM,3245
|
|
246
259
|
edu_rdm_integration/stages/upload_data/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
247
260
|
edu_rdm_integration/stages/upload_data/uploader_log/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
248
261
|
edu_rdm_integration/stages/upload_data/uploader_log/actions.py,sha256=stXHS_CB93AtnuToimsnsbl0gqOn_DbPk6jnCwJs9NQ,8030
|
|
@@ -254,8 +267,8 @@ edu_rdm_integration/stages/upload_data/uploader_log/ui.py,sha256=mU3XA9zVKHGqzNk
|
|
|
254
267
|
edu_rdm_integration/stages/upload_data/uploader_log/migrations/0001_initial.py,sha256=r5oOB7DBK9-mfuqPAgjXUJY5-hEcmMdILCwDTpaLnBc,753
|
|
255
268
|
edu_rdm_integration/stages/upload_data/uploader_log/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
256
269
|
edu_rdm_integration/stages/upload_data/uploader_log/templates/ui-js/object-grid-buttons.js,sha256=2xyGe0wdVokM0RhpzRzcRvJPBkBmPe3SlZry4oP4Nzs,6201
|
|
257
|
-
edu_rdm_integration-3.
|
|
258
|
-
edu_rdm_integration-3.
|
|
259
|
-
edu_rdm_integration-3.
|
|
260
|
-
edu_rdm_integration-3.
|
|
261
|
-
edu_rdm_integration-3.
|
|
270
|
+
edu_rdm_integration-3.22.1.dist-info/licenses/LICENSE,sha256=uw43Gjjj-1vXWCItfSrNDpbejnOwZMrNerUh8oWbq8Q,3458
|
|
271
|
+
edu_rdm_integration-3.22.1.dist-info/METADATA,sha256=_GoPYkH6ufAkdZ-Ysai4vcZmDyhdUFjZcCQCOcb4Zsw,43714
|
|
272
|
+
edu_rdm_integration-3.22.1.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
|
|
273
|
+
edu_rdm_integration-3.22.1.dist-info/top_level.txt,sha256=nRJV0O14UtNE-jGIYG03sohgFnZClvf57H5m6VBXe9Y,20
|
|
274
|
+
edu_rdm_integration-3.22.1.dist-info/RECORD,,
|
|
File without changes
|
{edu_rdm_integration-3.21.0.dist-info → edu_rdm_integration-3.22.1.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
|
File without changes
|