edu-rdm-integration 3.3.6__py3-none-any.whl → 3.4.0__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/collect_data/tests.py +5 -0
- edu_rdm_integration/enum_register/mixins.py +19 -0
- edu_rdm_integration/export_data/export.py +113 -53
- edu_rdm_integration/export_data/export_manger.py +36 -9
- edu_rdm_integration/helpers.py +16 -13
- edu_rdm_integration/mapping.py +3 -0
- edu_rdm_integration/migrations/0014_uploaddatacommand.py +43 -0
- edu_rdm_integration/models.py +50 -0
- edu_rdm_integration/tasks.py +27 -15
- edu_rdm_integration/typing.py +28 -0
- edu_rdm_integration/utils.py +75 -0
- {edu_rdm_integration-3.3.6.dist-info → edu_rdm_integration-3.4.0.dist-info}/METADATA +25 -3
- {edu_rdm_integration-3.3.6.dist-info → edu_rdm_integration-3.4.0.dist-info}/RECORD +17 -15
- {edu_rdm_integration-3.3.6.dist-info → edu_rdm_integration-3.4.0.dist-info}/LICENSE +0 -0
- {edu_rdm_integration-3.3.6.dist-info → edu_rdm_integration-3.4.0.dist-info}/WHEEL +0 -0
- {edu_rdm_integration-3.3.6.dist-info → edu_rdm_integration-3.4.0.dist-info}/namespace_packages.txt +0 -0
- {edu_rdm_integration-3.3.6.dist-info → edu_rdm_integration-3.4.0.dist-info}/top_level.txt +0 -0
@@ -32,6 +32,9 @@ from educommon.audit_log.models import (
|
|
32
32
|
from educommon.integration_entities.enums import (
|
33
33
|
EntityLogOperation,
|
34
34
|
)
|
35
|
+
from educommon.utils.phone_number.phone_number import (
|
36
|
+
PhoneNumber,
|
37
|
+
)
|
35
38
|
from function_tools.models import (
|
36
39
|
Entity,
|
37
40
|
EntityType,
|
@@ -166,6 +169,8 @@ class BaseCollectingFunctionTestCase(TestCase):
|
|
166
169
|
fields[key] = value.name
|
167
170
|
elif isinstance(value, list):
|
168
171
|
fields[key] = f'{{{",".join(map(str, value))}}}'
|
172
|
+
elif isinstance(value, PhoneNumber):
|
173
|
+
fields[key] = value.cleaned
|
169
174
|
else:
|
170
175
|
fields[key] = value
|
171
176
|
|
@@ -1,7 +1,9 @@
|
|
1
1
|
from typing import (
|
2
2
|
Any,
|
3
3
|
Dict,
|
4
|
+
NamedTuple,
|
4
5
|
Optional,
|
6
|
+
Type,
|
5
7
|
Tuple,
|
6
8
|
)
|
7
9
|
|
@@ -23,6 +25,9 @@ from edu_rdm_integration.models import (
|
|
23
25
|
RegionalDataMartEntityEnum,
|
24
26
|
RegionalDataMartModelEnum,
|
25
27
|
)
|
28
|
+
from edu_rdm_integration.typing import (
|
29
|
+
MODEL_TYPE_VAR,
|
30
|
+
)
|
26
31
|
from edu_rdm_integration.utils import (
|
27
32
|
camel_to_underscore,
|
28
33
|
)
|
@@ -108,3 +113,17 @@ class ModelEnumRegisterMixin(BaseEnumRegisterMixin):
|
|
108
113
|
register_params['loggable_models'] = cls.loggable_models
|
109
114
|
|
110
115
|
return register_params
|
116
|
+
|
117
|
+
|
118
|
+
class FromNamedTupleMixin:
|
119
|
+
"""Миксин получения экземпляра модели из получаемого кэша значений."""
|
120
|
+
|
121
|
+
@classmethod
|
122
|
+
def from_namedtuple(cls: Type[MODEL_TYPE_VAR], namedtuple: NamedTuple) -> MODEL_TYPE_VAR:
|
123
|
+
"""Создает экземпляр класса из NamedTuple."""
|
124
|
+
return cls(
|
125
|
+
**{
|
126
|
+
field: getattr(namedtuple, field)
|
127
|
+
for field in [f.column for f in cls._meta.get_fields()]
|
128
|
+
}
|
129
|
+
)
|
@@ -15,6 +15,7 @@ from typing import (
|
|
15
15
|
List,
|
16
16
|
Set,
|
17
17
|
Type,
|
18
|
+
Union,
|
18
19
|
)
|
19
20
|
|
20
21
|
from django.conf import (
|
@@ -69,6 +70,13 @@ from edu_rdm_integration.consts import (
|
|
69
70
|
from edu_rdm_integration.enums import (
|
70
71
|
FileUploadStatusEnum,
|
71
72
|
)
|
73
|
+
from edu_rdm_integration.export_data.export_manger import (
|
74
|
+
ExportEntityQueueSender,
|
75
|
+
WorkerSender,
|
76
|
+
)
|
77
|
+
from edu_rdm_integration.export_data.queue import (
|
78
|
+
Queue,
|
79
|
+
)
|
72
80
|
from edu_rdm_integration.helpers import (
|
73
81
|
get_exporting_managers_max_period_ended_dates,
|
74
82
|
)
|
@@ -77,6 +85,9 @@ from edu_rdm_integration.models import (
|
|
77
85
|
ExportingDataSubStageStatus,
|
78
86
|
RegionalDataMartEntityEnum,
|
79
87
|
)
|
88
|
+
from edu_rdm_integration.redis_cache import (
|
89
|
+
AbstractCache,
|
90
|
+
)
|
80
91
|
from edu_rdm_integration.signals import (
|
81
92
|
manager_created,
|
82
93
|
)
|
@@ -121,8 +132,6 @@ class BaseExportEntitiesData(BaseOperationData):
|
|
121
132
|
# Карта соответствия manager_id сущности и его основной модели
|
122
133
|
self.manager_main_model_map: Dict[str, ModelBase] = {}
|
123
134
|
|
124
|
-
self._configure_agent_client()
|
125
|
-
|
126
135
|
@property
|
127
136
|
def _log_file_path(self) -> str:
|
128
137
|
"""
|
@@ -130,57 +139,6 @@ class BaseExportEntitiesData(BaseOperationData):
|
|
130
139
|
"""
|
131
140
|
return os.path.join(settings.MEDIA_ROOT, settings.RDM_EXPORT_LOG_DIR, f'{self.command_id}.log')
|
132
141
|
|
133
|
-
def _configure_agent_client(self):
|
134
|
-
"""
|
135
|
-
Конфигурирование клиента загрузчика данных в Витрину.
|
136
|
-
|
137
|
-
#TODO Вынужденная мера, т.к. при запуске команды не производится проверка готовности конфигов приложений.
|
138
|
-
# Нужно переработать механизм конфигурирования клиента загрузчика.
|
139
|
-
"""
|
140
|
-
import uploader_client
|
141
|
-
from django.core.cache import (
|
142
|
-
DEFAULT_CACHE_ALIAS,
|
143
|
-
caches,
|
144
|
-
)
|
145
|
-
from uploader_client.contrib.rdm.interfaces.configurations import (
|
146
|
-
RegionalDataMartUploaderConfig,
|
147
|
-
)
|
148
|
-
if settings.RDM_UPLOADER_CLIENT_ENABLE_REQUEST_EMULATION:
|
149
|
-
uploader_client.set_config(
|
150
|
-
RegionalDataMartUploaderConfig(
|
151
|
-
interface='uploader_client.contrib.rdm.interfaces.rest.OpenAPIInterfaceEmulation',
|
152
|
-
url=settings.RDM_UPLOADER_CLIENT_URL,
|
153
|
-
datamart_name=settings.RDM_UPLOADER_CLIENT_DATAMART_NAME,
|
154
|
-
timeout=1,
|
155
|
-
request_retries=1,
|
156
|
-
)
|
157
|
-
)
|
158
|
-
elif settings.RDM_UPLOADER_CLIENT_USE_PROXY_API:
|
159
|
-
uploader_client.set_config(
|
160
|
-
RegionalDataMartUploaderConfig(
|
161
|
-
interface='uploader_client.contrib.rdm.interfaces.rest.ProxyAPIInterface',
|
162
|
-
cache=caches[DEFAULT_CACHE_ALIAS],
|
163
|
-
url=settings.RDM_UPLOADER_CLIENT_URL,
|
164
|
-
datamart_name=settings.RDM_UPLOADER_CLIENT_DATAMART_NAME,
|
165
|
-
timeout=settings.RDM_UPLOADER_CLIENT_REQUEST_TIMEOUT,
|
166
|
-
request_retries=settings.RDM_UPLOADER_CLIENT_REQUEST_RETRIES,
|
167
|
-
organization_ogrn=settings.RDM_UPLOADER_CLIENT_ORGANIZATION_OGRN,
|
168
|
-
installation_name=settings.RDM_UPLOADER_CLIENT_INSTALLATION_NAME,
|
169
|
-
installation_id=settings.RDM_UPLOADER_CLIENT_INSTALLATION_ID,
|
170
|
-
username=settings.RDM_UPLOADER_CLIENT_USERNAME,
|
171
|
-
password=settings.RDM_UPLOADER_CLIENT_PASSWORD,
|
172
|
-
)
|
173
|
-
)
|
174
|
-
else:
|
175
|
-
uploader_client.set_config(
|
176
|
-
RegionalDataMartUploaderConfig(
|
177
|
-
url=settings.RDM_UPLOADER_CLIENT_URL,
|
178
|
-
datamart_name=settings.RDM_UPLOADER_CLIENT_DATAMART_NAME,
|
179
|
-
timeout=settings.RDM_UPLOADER_CLIENT_REQUEST_TIMEOUT,
|
180
|
-
request_retries=settings.RDM_UPLOADER_CLIENT_REQUEST_RETRIES,
|
181
|
-
)
|
182
|
-
)
|
183
|
-
|
184
142
|
def _has_stage_created_or_in_progress(self, manager_id: str, entity: str) -> bool:
|
185
143
|
"""Проверяет есть ли готовый к работе stage или в работе для данной сущности."""
|
186
144
|
stage_created_or_in_progress = ExportingDataStage.objects.filter(
|
@@ -414,3 +372,105 @@ class ExportLatestEntitiesData(BaseExportLatestEntitiesData):
|
|
414
372
|
def _get_async_task(self) -> Model:
|
415
373
|
"""Возвращает модель асинхронной задачи."""
|
416
374
|
return RunningTask
|
375
|
+
|
376
|
+
|
377
|
+
class UploadEntitiesData(BaseOperationData):
|
378
|
+
"""Класс отправки файлов с сущностями в витрину."""
|
379
|
+
|
380
|
+
def __init__(
|
381
|
+
self,
|
382
|
+
entities: Iterable[str],
|
383
|
+
data_cache: AbstractCache,
|
384
|
+
queue: Queue,
|
385
|
+
**kwargs,
|
386
|
+
):
|
387
|
+
super().__init__(**kwargs)
|
388
|
+
|
389
|
+
# Если сущности не указаны, берется значение по умолчанию - все сущности:
|
390
|
+
self.entities = entities if entities else RegionalDataMartEntityEnum.get_enum_data().keys()
|
391
|
+
|
392
|
+
self.data_cache = data_cache
|
393
|
+
self.queue = queue
|
394
|
+
|
395
|
+
self.task_id = kwargs.get('task_id')
|
396
|
+
|
397
|
+
self._configure_agent_client()
|
398
|
+
|
399
|
+
@property
|
400
|
+
def _log_file_path(self) -> Union[str, bytes]:
|
401
|
+
"""
|
402
|
+
Путь до лог файла.
|
403
|
+
"""
|
404
|
+
if self.command_id:
|
405
|
+
log_file_path = os.path.join(settings.MEDIA_ROOT, settings.RDM_UPLOAD_LOG_DIR, f'{self.command_id}.log')
|
406
|
+
else:
|
407
|
+
log_file_path = os.path.join(settings.MEDIA_ROOT, settings.RDM_UPLOAD_LOG_DIR, 'upload_entity.log')
|
408
|
+
|
409
|
+
return log_file_path
|
410
|
+
|
411
|
+
# TODO https://jira.bars.group/browse/EDUSCHL-22492. Вынужденная мера, т.к. при запуске команды не производится
|
412
|
+
# проверка готовности конфигов приложений. Нужно переработать механизм конфигурирования клиента загрузчика.
|
413
|
+
def _configure_agent_client(self):
|
414
|
+
"""
|
415
|
+
Конфигурирование клиента загрузчика данных в Витрину.
|
416
|
+
"""
|
417
|
+
import uploader_client
|
418
|
+
from django.core.cache import (
|
419
|
+
DEFAULT_CACHE_ALIAS,
|
420
|
+
caches,
|
421
|
+
)
|
422
|
+
from uploader_client.contrib.rdm.interfaces.configurations import (
|
423
|
+
RegionalDataMartUploaderConfig,
|
424
|
+
)
|
425
|
+
if settings.RDM_UPLOADER_CLIENT_ENABLE_REQUEST_EMULATION:
|
426
|
+
uploader_client.set_config(
|
427
|
+
RegionalDataMartUploaderConfig(
|
428
|
+
interface='uploader_client.contrib.rdm.interfaces.rest.OpenAPIInterfaceEmulation',
|
429
|
+
url=settings.RDM_UPLOADER_CLIENT_URL,
|
430
|
+
datamart_name=settings.RDM_UPLOADER_CLIENT_DATAMART_NAME,
|
431
|
+
timeout=1,
|
432
|
+
request_retries=1,
|
433
|
+
)
|
434
|
+
)
|
435
|
+
elif settings.RDM_UPLOADER_CLIENT_USE_PROXY_API:
|
436
|
+
uploader_client.set_config(
|
437
|
+
RegionalDataMartUploaderConfig(
|
438
|
+
interface='uploader_client.contrib.rdm.interfaces.rest.ProxyAPIInterface',
|
439
|
+
cache=caches[DEFAULT_CACHE_ALIAS],
|
440
|
+
url=settings.RDM_UPLOADER_CLIENT_URL,
|
441
|
+
datamart_name=settings.RDM_UPLOADER_CLIENT_DATAMART_NAME,
|
442
|
+
timeout=settings.RDM_UPLOADER_CLIENT_REQUEST_TIMEOUT,
|
443
|
+
request_retries=settings.RDM_UPLOADER_CLIENT_REQUEST_RETRIES,
|
444
|
+
organization_ogrn=settings.RDM_UPLOADER_CLIENT_ORGANIZATION_OGRN,
|
445
|
+
installation_name=settings.RDM_UPLOADER_CLIENT_INSTALLATION_NAME,
|
446
|
+
installation_id=settings.RDM_UPLOADER_CLIENT_INSTALLATION_ID,
|
447
|
+
username=settings.RDM_UPLOADER_CLIENT_USERNAME,
|
448
|
+
password=settings.RDM_UPLOADER_CLIENT_PASSWORD,
|
449
|
+
)
|
450
|
+
)
|
451
|
+
else:
|
452
|
+
uploader_client.set_config(
|
453
|
+
RegionalDataMartUploaderConfig(
|
454
|
+
url=settings.RDM_UPLOADER_CLIENT_URL,
|
455
|
+
datamart_name=settings.RDM_UPLOADER_CLIENT_DATAMART_NAME,
|
456
|
+
timeout=settings.RDM_UPLOADER_CLIENT_REQUEST_TIMEOUT,
|
457
|
+
request_retries=settings.RDM_UPLOADER_CLIENT_REQUEST_RETRIES,
|
458
|
+
)
|
459
|
+
)
|
460
|
+
|
461
|
+
def upload_data(self, *args, **kwargs):
|
462
|
+
"""
|
463
|
+
Запускает отправку данных в витрину.
|
464
|
+
"""
|
465
|
+
try:
|
466
|
+
exporter = ExportEntityQueueSender(self.data_cache, self.queue, self.entities)
|
467
|
+
exporter.run()
|
468
|
+
|
469
|
+
sender = WorkerSender(self.queue)
|
470
|
+
sender.run()
|
471
|
+
|
472
|
+
except Exception as err:
|
473
|
+
logger.exception(err)
|
474
|
+
raise err
|
475
|
+
finally:
|
476
|
+
self._remove_file_handler()
|
@@ -1,10 +1,8 @@
|
|
1
|
-
from datetime import (
|
2
|
-
datetime,
|
3
|
-
)
|
4
1
|
from pathlib import (
|
5
2
|
Path,
|
6
3
|
)
|
7
4
|
from typing import (
|
5
|
+
Iterable,
|
8
6
|
List,
|
9
7
|
)
|
10
8
|
|
@@ -13,6 +11,7 @@ from django.conf import (
|
|
13
11
|
)
|
14
12
|
from django.db.models import (
|
15
13
|
F,
|
14
|
+
Q,
|
16
15
|
Sum,
|
17
16
|
)
|
18
17
|
from django.db.transaction import (
|
@@ -83,10 +82,14 @@ class ExportQueueSender:
|
|
83
82
|
return file_size or 0
|
84
83
|
|
85
84
|
@staticmethod
|
86
|
-
def
|
85
|
+
def _make_stage_filter(self) -> Q:
|
86
|
+
"""Формирование фильтра для выборки подэтапов."""
|
87
|
+
return Q(status_id=ExportingDataSubStageStatus.READY_FOR_EXPORT.key)
|
88
|
+
|
89
|
+
def get_sub_stages_attachments_to_export(self):
|
87
90
|
"""Выборка готовых к экспорту подэтапов."""
|
88
91
|
return ExportingDataSubStage.objects.filter(
|
89
|
-
|
92
|
+
self._make_stage_filter()
|
90
93
|
).annotate(
|
91
94
|
attachment_id=F('exportingdatasubstageattachment__id'),
|
92
95
|
attachment_name=F('exportingdatasubstageattachment__attachment'),
|
@@ -117,10 +120,14 @@ class ExportQueueSender:
|
|
117
120
|
timeout=settings.RDM_REDIS_CACHE_TIMEOUT_SECONDS
|
118
121
|
)
|
119
122
|
|
123
|
+
logger.info(
|
124
|
+
f'ExportedDataSubStage {sub_stage_id} {entity_name} added to the queue'
|
125
|
+
)
|
126
|
+
|
120
127
|
return True
|
121
128
|
|
122
129
|
def run(self):
|
123
|
-
"""Запуск работы
|
130
|
+
"""Запуск работы очереди."""
|
124
131
|
if not self.queue_total_file_size:
|
125
132
|
self.queue_total_file_size = self.get_exported_file_size()
|
126
133
|
|
@@ -133,9 +140,7 @@ class ExportQueueSender:
|
|
133
140
|
if prev_sub_stage != stage_attachment['id']:
|
134
141
|
if stage_files:
|
135
142
|
to_cache = self.set_sub_stage_to_cache(prev_sub_stage, entity, stage_files)
|
136
|
-
|
137
|
-
f'ExportedDataSubStage {prev_sub_stage} {entity} added to the queue'
|
138
|
-
)
|
143
|
+
|
139
144
|
stage_files = []
|
140
145
|
|
141
146
|
if not to_cache:
|
@@ -162,6 +167,25 @@ class ExportQueueSender:
|
|
162
167
|
self.queue_total_file_size,
|
163
168
|
timeout=settings.RDM_REDIS_CACHE_TIMEOUT_SECONDS
|
164
169
|
)
|
170
|
+
logger.warning(
|
171
|
+
f'Total exported file size: {self.queue_total_file_size} - queue is full!!!'
|
172
|
+
)
|
173
|
+
|
174
|
+
|
175
|
+
class ExportEntityQueueSender(ExportQueueSender):
|
176
|
+
"""Класс отправки данных по конкретной сущности в очередь РВД."""
|
177
|
+
|
178
|
+
def __init__(self, data_cache: AbstractCache, queue: Queue, entity: Iterable[str]):
|
179
|
+
super().__init__(data_cache, queue)
|
180
|
+
|
181
|
+
self.entity = entity
|
182
|
+
|
183
|
+
def _make_stage_filter(self) -> Q:
|
184
|
+
"""Формирование фильтра для выборки подэтапов."""
|
185
|
+
return Q(
|
186
|
+
status_id=ExportingDataSubStageStatus.READY_FOR_EXPORT.key,
|
187
|
+
exportingdatasubstageentity__entity_id__in=self.entity
|
188
|
+
)
|
165
189
|
|
166
190
|
|
167
191
|
class WorkerSender:
|
@@ -240,6 +264,9 @@ class WorkerSender:
|
|
240
264
|
sub_stage.save()
|
241
265
|
|
242
266
|
self.queue.delete_from_queue(sub_stage_id=sub_stage_id, entity_name=entity_key)
|
267
|
+
logger.info(
|
268
|
+
f'ExportedDataSubStage {sub_stage_id} {entity_key} sended from the queue'
|
269
|
+
)
|
243
270
|
|
244
271
|
def run(self):
|
245
272
|
"""Запуск воркера отправки."""
|
edu_rdm_integration/helpers.py
CHANGED
@@ -10,8 +10,8 @@ from typing import (
|
|
10
10
|
TYPE_CHECKING,
|
11
11
|
Any,
|
12
12
|
Dict,
|
13
|
-
List,
|
14
13
|
Iterable,
|
14
|
+
List,
|
15
15
|
Optional,
|
16
16
|
Tuple,
|
17
17
|
Union,
|
@@ -33,16 +33,16 @@ from django.db.models.functions import (
|
|
33
33
|
Cast,
|
34
34
|
Least,
|
35
35
|
)
|
36
|
+
from uploader_client.adapters import (
|
37
|
+
adapter,
|
38
|
+
)
|
36
39
|
|
37
40
|
from educommon import (
|
38
41
|
logger,
|
39
42
|
)
|
40
|
-
|
41
|
-
adapter,
|
42
|
-
)
|
43
|
+
|
43
44
|
from edu_rdm_integration.collect_and_export_data.models import (
|
44
|
-
|
45
|
-
EduRdmExportDataCommandProgress,
|
45
|
+
AbstractExportDataCommandProgress,
|
46
46
|
)
|
47
47
|
from edu_rdm_integration.enums import (
|
48
48
|
FileUploadStatusEnum,
|
@@ -54,32 +54,35 @@ from edu_rdm_integration.export_data.consts import (
|
|
54
54
|
TOTAL_ATTACHMENTS_SIZE_KEY,
|
55
55
|
)
|
56
56
|
from edu_rdm_integration.models import (
|
57
|
-
DataMartRequestStatus,
|
58
|
-
ExportingDataStage,
|
59
57
|
CollectingDataStageStatus,
|
60
58
|
CollectingExportedDataStage,
|
59
|
+
DataMartRequestStatus,
|
60
|
+
ExportingDataStage,
|
61
|
+
ExportingDataStageStatus,
|
61
62
|
ExportingDataSubStageUploaderClientLog,
|
63
|
+
UploadDataCommand,
|
62
64
|
UploadStatusRequestLog,
|
63
|
-
ExportingDataStageStatus,
|
64
65
|
)
|
65
66
|
from edu_rdm_integration.redis_cache import (
|
66
67
|
AbstractCache,
|
67
68
|
)
|
68
69
|
|
70
|
+
|
69
71
|
if TYPE_CHECKING:
|
70
72
|
from datetime import (
|
71
73
|
datetime,
|
72
74
|
)
|
73
75
|
|
76
|
+
from uploader_client.models import (
|
77
|
+
Entry,
|
78
|
+
)
|
79
|
+
|
74
80
|
from edu_rdm_integration.collect_data.non_calculated.base.managers import (
|
75
81
|
BaseCollectingExportedDataRunnerManager,
|
76
82
|
)
|
77
83
|
from edu_rdm_integration.export_data.base.managers import (
|
78
84
|
BaseExportDataRunnerManager,
|
79
85
|
)
|
80
|
-
from uploader_client.models import (
|
81
|
-
Entry,
|
82
|
-
)
|
83
86
|
|
84
87
|
|
85
88
|
class UploadStatusHelper:
|
@@ -311,7 +314,7 @@ class Graph:
|
|
311
314
|
|
312
315
|
|
313
316
|
def save_command_log_link(
|
314
|
-
command: Union[
|
317
|
+
command: Union[AbstractExportDataCommandProgress, UploadDataCommand],
|
315
318
|
log_dir: str
|
316
319
|
) -> None:
|
317
320
|
"""Сохраняет ссылку на лог команды."""
|
edu_rdm_integration/mapping.py
CHANGED
@@ -14,3 +14,6 @@ MODEL_FIELDS_LOG_FILTER: Dict[EntityLogOperation, Dict[str, Tuple]] = {
|
|
14
14
|
EntityLogOperation.UPDATE: {},
|
15
15
|
EntityLogOperation.DELETE: {}
|
16
16
|
}
|
17
|
+
|
18
|
+
# Маппинг операций логов моделей и сущностей по умолчанию
|
19
|
+
DEFAULT_ENTITY_LOG_OPERATION_MAP = {op: op for op in EntityLogOperation.values}
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# Generated by Django 3.1.14 on 2024-09-05 20:35
|
2
|
+
|
3
|
+
import uuid
|
4
|
+
|
5
|
+
import django.db.models.deletion
|
6
|
+
import django.utils.timezone
|
7
|
+
from django.db import (
|
8
|
+
migrations,
|
9
|
+
models,
|
10
|
+
)
|
11
|
+
|
12
|
+
import educommon.django.db.mixins
|
13
|
+
|
14
|
+
import edu_rdm_integration.utils
|
15
|
+
|
16
|
+
|
17
|
+
class Migration(migrations.Migration):
|
18
|
+
|
19
|
+
dependencies = [
|
20
|
+
('async_task', '0002_task_type_and_status_data'),
|
21
|
+
('edu_rdm_integration', '0013_set_attachment_size'),
|
22
|
+
]
|
23
|
+
|
24
|
+
operations = [
|
25
|
+
migrations.CreateModel(
|
26
|
+
name='UploadDataCommand',
|
27
|
+
fields=[
|
28
|
+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
29
|
+
('logs_link', models.FileField(max_length=255, upload_to=edu_rdm_integration.utils.get_data_command_progress_attachment_path, verbose_name='Ссылка на файл логов')),
|
30
|
+
('type', models.PositiveSmallIntegerField(choices=[(1, 'Автоматический'), (2, 'Ручной')], verbose_name='Тип команды')),
|
31
|
+
('created', models.DateTimeField(default=django.utils.timezone.now, verbose_name='Дата создания')),
|
32
|
+
('generation_id', models.UUIDField(default=uuid.uuid4, verbose_name='Идентификатор генерации')),
|
33
|
+
('entity', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='edu_rdm_integration.regionaldatamartentityenum', verbose_name='Сущность РВД')),
|
34
|
+
('task', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='async_task.runningtask', verbose_name='Асинхронная задача')),
|
35
|
+
],
|
36
|
+
options={
|
37
|
+
'verbose_name': 'Команда отправки данных в витрину',
|
38
|
+
'verbose_name_plural': 'Команды отправки данных в витрину',
|
39
|
+
'db_table': 'rdm_upload_data_command',
|
40
|
+
},
|
41
|
+
bases=(educommon.django.db.mixins.ReprStrPreModelMixin, models.Model),
|
42
|
+
),
|
43
|
+
]
|
edu_rdm_integration/models.py
CHANGED
@@ -26,6 +26,7 @@ from django.db.models import (
|
|
26
26
|
Manager,
|
27
27
|
OneToOneField,
|
28
28
|
PositiveIntegerField,
|
29
|
+
PositiveSmallIntegerField,
|
29
30
|
SmallIntegerField,
|
30
31
|
UUIDField,
|
31
32
|
)
|
@@ -65,12 +66,14 @@ from m3_db_utils.models import (
|
|
65
66
|
)
|
66
67
|
|
67
68
|
from edu_rdm_integration.enums import (
|
69
|
+
CommandType,
|
68
70
|
FileUploadStatusEnum,
|
69
71
|
)
|
70
72
|
from edu_rdm_integration.uploader_log.managers import (
|
71
73
|
UploaderClientLogManager,
|
72
74
|
)
|
73
75
|
from edu_rdm_integration.utils import (
|
76
|
+
get_data_command_progress_attachment_path,
|
74
77
|
get_exporting_data_stage_attachment_path,
|
75
78
|
)
|
76
79
|
|
@@ -150,6 +153,7 @@ class CollectingExportedDataStage(ReprStrPreModelMixin, BaseObjectModel):
|
|
150
153
|
|
151
154
|
@property
|
152
155
|
def attrs_for_repr_str(self):
|
156
|
+
"""Список атрибутов для отображения экземпляра модели."""
|
153
157
|
return ['manager_id', 'logs_period_started_at', 'logs_period_ended_at', 'started_at', 'ended_at', 'status_id']
|
154
158
|
|
155
159
|
def save(self, *args, **kwargs):
|
@@ -245,6 +249,7 @@ class CollectingExportedDataSubStage(ReprStrPreModelMixin, BaseObjectModel):
|
|
245
249
|
|
246
250
|
@property
|
247
251
|
def attrs_for_repr_str(self):
|
252
|
+
"""Список атрибутов для отображения экземпляра модели."""
|
248
253
|
return ['stage_id', 'function_id', 'started_at', 'ended_at', 'previous_id', 'status_id']
|
249
254
|
|
250
255
|
def save(self, *args, **kwargs):
|
@@ -335,6 +340,7 @@ class ExportingDataStage(ReprStrPreModelMixin, BaseObjectModel):
|
|
335
340
|
|
336
341
|
@property
|
337
342
|
def attrs_for_repr_str(self):
|
343
|
+
"""Список атрибутов для отображения экземпляра модели."""
|
338
344
|
return ['manager_id', 'started_at', 'ended_at', 'status_id']
|
339
345
|
|
340
346
|
def save(self, *args, **kwargs):
|
@@ -421,9 +427,11 @@ class ExportingDataSubStage(ReprStrPreModelMixin, BaseObjectModel):
|
|
421
427
|
|
422
428
|
@property
|
423
429
|
def attrs_for_repr_str(self):
|
430
|
+
"""Список атрибутов для отображения экземпляра модели."""
|
424
431
|
return ['function_id', 'collecting_data_sub_stage_id', 'stage_id', 'started_at', 'ended_at', 'status_id']
|
425
432
|
|
426
433
|
def save(self, *args, **kwargs):
|
434
|
+
"""Сохранение экземпляра модели."""
|
427
435
|
if (
|
428
436
|
self.status_id in {
|
429
437
|
ExportingDataSubStageStatus.FAILED.key,
|
@@ -487,6 +495,7 @@ class ExportingDataSubStageAttachment(ReprStrPreModelMixin, BaseObjectModel):
|
|
487
495
|
|
488
496
|
@property
|
489
497
|
def attrs_for_repr_str(self):
|
498
|
+
"""Список атрибутов для отображения экземпляра модели."""
|
490
499
|
return ['exporting_data_sub_stage_id', 'attachment', 'operation', 'created', 'modified']
|
491
500
|
|
492
501
|
|
@@ -675,6 +684,7 @@ class BaseEntityModel(ReprStrPreModelMixin, BaseObjectModel):
|
|
675
684
|
|
676
685
|
@property
|
677
686
|
def attrs_for_repr_str(self):
|
687
|
+
"""Список атрибутов для отображения экземпляра модели."""
|
678
688
|
return ['collecting_sub_stage', 'exporting_sub_stage', 'operation', 'created', 'modified']
|
679
689
|
|
680
690
|
class Meta:
|
@@ -931,6 +941,7 @@ class TransferredEntity(BaseObjectModel):
|
|
931
941
|
|
932
942
|
@json_encode
|
933
943
|
def no_export(self):
|
944
|
+
"""Формирует отображение признака отключения экспорта."""
|
934
945
|
return 'Нет' if self.export_enabled else 'Да'
|
935
946
|
|
936
947
|
|
@@ -953,3 +964,42 @@ class ExportingDataSubStageEntity(BaseObjectModel):
|
|
953
964
|
db_table = 'rdm_exporting_data_sub_stage_entity'
|
954
965
|
verbose_name = 'Связь сущности и подэтапа выгрузки'
|
955
966
|
verbose_name_plural = 'Связи сущности и подэтапа выгрузки'
|
967
|
+
|
968
|
+
|
969
|
+
class UploadDataCommand(ReprStrPreModelMixin, BaseObjectModel):
|
970
|
+
"""Модель, хранящая данные для формирования и отслеживания асинхронной задачи по отправке данных в витрину."""
|
971
|
+
|
972
|
+
task = ForeignKey(
|
973
|
+
to='async_task.RunningTask',
|
974
|
+
verbose_name='Асинхронная задача',
|
975
|
+
blank=True,
|
976
|
+
null=True,
|
977
|
+
on_delete=SET_NULL,
|
978
|
+
)
|
979
|
+
logs_link = FileField(
|
980
|
+
upload_to=get_data_command_progress_attachment_path,
|
981
|
+
max_length=255,
|
982
|
+
verbose_name='Ссылка на файл логов',
|
983
|
+
)
|
984
|
+
type = PositiveSmallIntegerField( # noqa: A003
|
985
|
+
verbose_name='Тип команды',
|
986
|
+
choices=CommandType.get_choices(),
|
987
|
+
)
|
988
|
+
entity = ForeignKey(
|
989
|
+
to=RegionalDataMartEntityEnum,
|
990
|
+
verbose_name='Сущность РВД',
|
991
|
+
on_delete=PROTECT,
|
992
|
+
)
|
993
|
+
created = DateTimeField(
|
994
|
+
verbose_name='Дата создания',
|
995
|
+
default=timezone.now,
|
996
|
+
)
|
997
|
+
generation_id = UUIDField(
|
998
|
+
'Идентификатор генерации',
|
999
|
+
default=uuid.uuid4,
|
1000
|
+
)
|
1001
|
+
|
1002
|
+
class Meta:
|
1003
|
+
db_table = 'rdm_upload_data_command'
|
1004
|
+
verbose_name = 'Команда отправки данных в витрину'
|
1005
|
+
verbose_name_plural = 'Команды отправки данных в витрину'
|
edu_rdm_integration/tasks.py
CHANGED
@@ -9,9 +9,6 @@ from typing import (
|
|
9
9
|
)
|
10
10
|
|
11
11
|
import celery
|
12
|
-
from celery.exceptions import (
|
13
|
-
Ignore,
|
14
|
-
)
|
15
12
|
from celery.schedules import (
|
16
13
|
crontab,
|
17
14
|
)
|
@@ -25,9 +22,6 @@ from django.utils import (
|
|
25
22
|
timezone,
|
26
23
|
)
|
27
24
|
|
28
|
-
from educommon.async_task.locker import (
|
29
|
-
TaskLocker,
|
30
|
-
)
|
31
25
|
from educommon.async_task.models import (
|
32
26
|
AsyncTaskType,
|
33
27
|
RunningTask,
|
@@ -64,10 +58,7 @@ from edu_rdm_integration.enums import (
|
|
64
58
|
)
|
65
59
|
from edu_rdm_integration.export_data.export import (
|
66
60
|
ExportLatestEntitiesData,
|
67
|
-
|
68
|
-
from edu_rdm_integration.export_data.export_manger import (
|
69
|
-
ExportQueueSender,
|
70
|
-
WorkerSender,
|
61
|
+
UploadEntitiesData,
|
71
62
|
)
|
72
63
|
from edu_rdm_integration.export_data.helpers import (
|
73
64
|
set_failed_status_suspended_exporting_data_stages,
|
@@ -85,6 +76,7 @@ from edu_rdm_integration.models import (
|
|
85
76
|
ExportingDataSubStageUploaderClientLog,
|
86
77
|
RegionalDataMartEntityEnum,
|
87
78
|
TransferredEntity,
|
79
|
+
UploadDataCommand,
|
88
80
|
)
|
89
81
|
from edu_rdm_integration.storages import (
|
90
82
|
RegionalDataMartEntityStorage,
|
@@ -354,6 +346,7 @@ class TransferLatestEntitiesDataPeriodicTask(UniquePeriodicAsyncTask):
|
|
354
346
|
|
355
347
|
class UploadDataAsyncTask(PeriodicAsyncTask):
|
356
348
|
"""Формирование очереди файлов и их отправка."""
|
349
|
+
|
357
350
|
queue = TASK_QUEUE_NAME
|
358
351
|
routing_key = TASK_QUEUE_NAME
|
359
352
|
description = 'Отправка данных в витрину "Региональная витрина данных"'
|
@@ -369,11 +362,31 @@ class UploadDataAsyncTask(PeriodicAsyncTask):
|
|
369
362
|
"""Выполнение."""
|
370
363
|
super().process(*args, **kwargs)
|
371
364
|
|
365
|
+
entity_ids_for_export = list(
|
366
|
+
TransferredEntity.objects.filter(export_enabled=True).values_list('entity_id', flat=True)
|
367
|
+
)
|
372
368
|
queue = RdmRedisSubStageAttachmentQueue()
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
369
|
+
task_id = RunningTask.objects.filter(
|
370
|
+
pk=self.request.id,
|
371
|
+
).values_list('pk', flat=True).first()
|
372
|
+
|
373
|
+
if task_id:
|
374
|
+
for entity_id in entity_ids_for_export:
|
375
|
+
upload_data_command = UploadDataCommand.objects.create(
|
376
|
+
entity_id=entity_id,
|
377
|
+
task_id=task_id,
|
378
|
+
type=CommandType.AUTO,
|
379
|
+
)
|
380
|
+
if upload_data_command:
|
381
|
+
upload_data = UploadEntitiesData(
|
382
|
+
entities=[upload_data_command.entity_id],
|
383
|
+
data_cache=cache,
|
384
|
+
queue=queue,
|
385
|
+
task_id=task_id,
|
386
|
+
command_id=upload_data_command.id,
|
387
|
+
)
|
388
|
+
upload_data.upload_data()
|
389
|
+
save_command_log_link(upload_data_command, settings.RDM_UPLOAD_LOG_DIR)
|
377
390
|
|
378
391
|
|
379
392
|
celery_app = celery.app.app_or_default()
|
@@ -381,4 +394,3 @@ celery_app.register_task(RDMCheckUploadStatus)
|
|
381
394
|
celery_app.register_task(CheckSuspendedExportedStagePeriodicTask)
|
382
395
|
celery_app.register_task(TransferLatestEntitiesDataPeriodicTask)
|
383
396
|
celery_app.register_task(UploadDataAsyncTask)
|
384
|
-
|
@@ -0,0 +1,28 @@
|
|
1
|
+
from typing import (
|
2
|
+
Sequence,
|
3
|
+
TypeVar,
|
4
|
+
Union,
|
5
|
+
)
|
6
|
+
|
7
|
+
from django.db.models import (
|
8
|
+
Expression,
|
9
|
+
Model,
|
10
|
+
)
|
11
|
+
|
12
|
+
|
13
|
+
# Тип, обозначающий любую модель. При указании в качестве type annotation
|
14
|
+
# можно указать, что аргументом может быть любая модель или тип модели (через
|
15
|
+
# Type[MODEL_TYPE_VAR]), а сама функция возвращает инстанс этой
|
16
|
+
# конкретной модели
|
17
|
+
MODEL_TYPE_VAR = TypeVar('MODEL_TYPE_VAR', bound=Model)
|
18
|
+
|
19
|
+
# Аннотация типов для id записи в БД
|
20
|
+
RECORD_IDS = Union[
|
21
|
+
tuple[int, ...],
|
22
|
+
tuple[str, ...],
|
23
|
+
list[int],
|
24
|
+
list[str],
|
25
|
+
Sequence[int],
|
26
|
+
Sequence[str],
|
27
|
+
Expression,
|
28
|
+
]
|
edu_rdm_integration/utils.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import os
|
2
2
|
from datetime import (
|
3
|
+
date,
|
3
4
|
datetime,
|
4
5
|
time,
|
5
6
|
timedelta,
|
@@ -11,12 +12,30 @@ from typing import (
|
|
11
12
|
List,
|
12
13
|
Optional,
|
13
14
|
Set,
|
15
|
+
Type,
|
16
|
+
TypeVar,
|
14
17
|
Union,
|
15
18
|
)
|
16
19
|
|
17
20
|
from django.conf import (
|
18
21
|
settings,
|
19
22
|
)
|
23
|
+
from django.db.models import (
|
24
|
+
DateField,
|
25
|
+
DateTimeField,
|
26
|
+
Expression,
|
27
|
+
F,
|
28
|
+
)
|
29
|
+
from django.db.models.expressions import (
|
30
|
+
Func,
|
31
|
+
)
|
32
|
+
from django.db.models.functions import (
|
33
|
+
Cast,
|
34
|
+
Now,
|
35
|
+
)
|
36
|
+
from educommon.integration_entities.enums import (
|
37
|
+
EntityLogOperation,
|
38
|
+
)
|
20
39
|
|
21
40
|
from edu_rdm_integration.apps import (
|
22
41
|
EduRDMIntegrationConfig,
|
@@ -285,3 +304,59 @@ def get_data_command_progress_attachment_path(
|
|
285
304
|
str(instance.type),
|
286
305
|
filename
|
287
306
|
)
|
307
|
+
|
308
|
+
|
309
|
+
class MakeInterval(Func):
|
310
|
+
"""Функция обработки даты/времени."""
|
311
|
+
|
312
|
+
template = 'make_interval(%(expressions)s)'
|
313
|
+
|
314
|
+
def __init__(
|
315
|
+
self, *,
|
316
|
+
years: Union[int, F] = 0,
|
317
|
+
months: Union[int, F] = 0,
|
318
|
+
weeks: Union[int, F] = 0,
|
319
|
+
days: Union[int, F] = 0,
|
320
|
+
hours: Union[int, F] = 0,
|
321
|
+
minutes: Union[int, F] = 0,
|
322
|
+
seconds: Union[float, F] = 0.0,
|
323
|
+
output_field=None,
|
324
|
+
) -> None:
|
325
|
+
self.years = years
|
326
|
+
self.months = months
|
327
|
+
self.weeks = weeks
|
328
|
+
self.days = days
|
329
|
+
self.hours = hours
|
330
|
+
self.minutes = minutes
|
331
|
+
self.seconds = seconds
|
332
|
+
|
333
|
+
super().__init__(
|
334
|
+
self.years,
|
335
|
+
self.months,
|
336
|
+
self.weeks,
|
337
|
+
self.days,
|
338
|
+
self.hours,
|
339
|
+
self.minutes,
|
340
|
+
self.seconds,
|
341
|
+
output_field=output_field or DateTimeField(),
|
342
|
+
)
|
343
|
+
|
344
|
+
|
345
|
+
def make_passed_datetime_from_today(
|
346
|
+
*,
|
347
|
+
years: int = 0,
|
348
|
+
months: int = 0,
|
349
|
+
days: int = 0,
|
350
|
+
) -> Union[datetime, Expression, F]:
|
351
|
+
"""Возвращает выражение на лет/месяцев/дней меньше относительно сегодня."""
|
352
|
+
now_expr = Now()
|
353
|
+
|
354
|
+
return now_expr - MakeInterval(years=years, months=months, days=days)
|
355
|
+
|
356
|
+
|
357
|
+
TODAY_EXPR: Union[date, Expression, F] = Cast(make_passed_datetime_from_today(), output_field=DateField())
|
358
|
+
|
359
|
+
THREE_YEARS_AGO_DATE_EXPR: Union[date, Expression, F] = Cast(
|
360
|
+
make_passed_datetime_from_today(years=3),
|
361
|
+
output_field=DateField()
|
362
|
+
)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: edu-rdm-integration
|
3
|
-
Version: 3.
|
3
|
+
Version: 3.4.0
|
4
4
|
Summary: Интеграция с Региональной витриной данных
|
5
5
|
Home-page:
|
6
6
|
Download-URL:
|
@@ -28,7 +28,7 @@ Requires-Dist: wheel <0.42,>=0.37.1
|
|
28
28
|
Requires-Dist: transliterate <2
|
29
29
|
Requires-Dist: Django <3.3,>=2.2
|
30
30
|
Requires-Dist: celery <5.3,>=4.4.7
|
31
|
-
Requires-Dist: educommon <4,>=3.
|
31
|
+
Requires-Dist: educommon <4,>=3.11.0
|
32
32
|
Requires-Dist: function-tools <1,>=0.9.0
|
33
33
|
Requires-Dist: m3-db-utils <1,>=0.3.10
|
34
34
|
Requires-Dist: m3-django-compat <2,>=1.10.2
|
@@ -301,6 +301,29 @@ Requires-Dist: uploader-client <1,>=0.2.1
|
|
301
301
|
|
302
302
|
### Удалено
|
303
303
|
|
304
|
+
|
305
|
+
## 3.4.0 - 2024-09-25
|
306
|
+
В базовый класс тестирования функций сбора добавлена поддержка формата PhoneNumber для формирования
|
307
|
+
экземпляра модели AuditLog.
|
308
|
+
Повышена версия зависимости educommon >= 3.11.0.
|
309
|
+
Вынесены утилиты и базовый класс для логирования и сбора по модели в рамках сущности
|
310
|
+
|
311
|
+
## Добавлено
|
312
|
+
- [EDUSCHL-22378](https://jira.bars.group/browse/EDUSCHL-22378)
|
313
|
+
Реализация поля моделей Django содержащего номер телефона
|
314
|
+
|
315
|
+
### Изменено
|
316
|
+
- [EDUKNDG-14516](https://jira.bars.group/browse/EDUKNDG-14516)
|
317
|
+
PATCH Вынесены утилиты и базовый класс для логирования и сбора по модели в рамках сущности
|
318
|
+
|
319
|
+
|
320
|
+
## 3.3.7 - 2024-09-06
|
321
|
+
Добавлены команда (UploadEntitiesData) и модель (UploadDataCommand) для логирования и запуска выгрузки данных в витрину
|
322
|
+
- [EDUSCHL-22042](https://jira.bars.group/browse/EDUSCHL-22042)
|
323
|
+
PATCH Добавлены команда (UploadEntitiesData) и модель (UploadDataCommand) для логирования и запуска выгрузки данных
|
324
|
+
в витрину
|
325
|
+
|
326
|
+
|
304
327
|
## 3.3.6 - 2024-09-02
|
305
328
|
Добавлена фильтрация по дате выгрузки при обновлении данных на экспорт в BaseExportLatestEntitiesData
|
306
329
|
|
@@ -309,7 +332,6 @@ Requires-Dist: uploader-client <1,>=0.2.1
|
|
309
332
|
PATCH Добавлена фильтрация по дате выгрузки при обновлении данных на экспорт в BaseExportLatestEntitiesData
|
310
333
|
|
311
334
|
|
312
|
-
|
313
335
|
## 3.3.5 - 2024-08-26
|
314
336
|
Классы UniquePeriodicAsyncTask, PeriodicTaskLocker перенесены в educommon.
|
315
337
|
|
@@ -6,14 +6,15 @@ edu_rdm_integration/base.py,sha256=_G0qPTAXe6bXfgDHNiZMSsYt3sMuUhLKnHuQCWSFttU,1
|
|
6
6
|
edu_rdm_integration/consts.py,sha256=Qt52SOCQ-3wOet-_6inJih_W9nToORKXbkxb3jVSjEo,1079
|
7
7
|
edu_rdm_integration/entities.py,sha256=qNVWUhjwvX298Ak86_AKmqBZioP0czGwBcAz_4dtUUE,14552
|
8
8
|
edu_rdm_integration/enums.py,sha256=T3Mu5D-CbKO3BSg16MPPnIPlcc_YGLYR-ThS8dzl9gg,4246
|
9
|
-
edu_rdm_integration/helpers.py,sha256=
|
10
|
-
edu_rdm_integration/mapping.py,sha256=
|
11
|
-
edu_rdm_integration/models.py,sha256=
|
9
|
+
edu_rdm_integration/helpers.py,sha256=_zQb3vuMrWClpUErshYTI1aVgdKjXf819pHfBjaIty4,14592
|
10
|
+
edu_rdm_integration/mapping.py,sha256=PGy6oH3Jzg4uuSIjlI0A-gWWpJPozqZHN4wGzp-ldFY,660
|
11
|
+
edu_rdm_integration/models.py,sha256=YP_vot6PbQ3jWv3NNW1wa0gScjOsT2lgdrj7z5V6NHw,33053
|
12
12
|
edu_rdm_integration/redis_cache.py,sha256=GZhtM1d0cVr5TEqxh15K7dS371Msit6wRemIiYb2rzk,1548
|
13
13
|
edu_rdm_integration/signals.py,sha256=3eRlpkDcFCF6TN80-QM8yBYLcyozzcmoPjz6r4_ApWg,73
|
14
14
|
edu_rdm_integration/storages.py,sha256=o5WqUG7SnkeuMt-z8spUi-IraivST-7KHzfY-M3v7FA,6807
|
15
|
-
edu_rdm_integration/tasks.py,sha256=
|
16
|
-
edu_rdm_integration/
|
15
|
+
edu_rdm_integration/tasks.py,sha256=QAOytSCEoWtFWJqmTkj3N6NRl_Hds9EXyRfngFHrbPk,16046
|
16
|
+
edu_rdm_integration/typing.py,sha256=6GOZfRDqOjN-o5NR86-f2o4uXQm_8AUCRRdQTITtrcs,785
|
17
|
+
edu_rdm_integration/utils.py,sha256=QMiohtOaId3Zab91IZPazGzhce5pa5e-ilRgXpISkHk,12249
|
17
18
|
edu_rdm_integration/adapters/__init__.py,sha256=cU0swn4Ny5ZQz5buWRcWsT1mpWuUFJaUlHf2l7TtEBo,83
|
18
19
|
edu_rdm_integration/adapters/apps.py,sha256=TyJTkSPs2qAHJ11fqbwLGk3Ea7ujtqWwbxqmvYNQxG8,363
|
19
20
|
edu_rdm_integration/adapters/caches.py,sha256=OxSqeXySUN42LxEeHBLtC1ZBt-7aicbRbmP1EJYTvV4,1505
|
@@ -41,7 +42,7 @@ edu_rdm_integration/collect_data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRk
|
|
41
42
|
edu_rdm_integration/collect_data/collect.py,sha256=HIKO8Kvw9NY9Nt5M5w9XfcZ9E5IVue_4mT7ESYJ6n4I,11909
|
42
43
|
edu_rdm_integration/collect_data/generators.py,sha256=f34AAwdEcQNIokX0ypqYgjRD1XolwBVLER_HYv9ibNw,9075
|
43
44
|
edu_rdm_integration/collect_data/helpers.py,sha256=gAFZAm9YhMtQhvlwSF3LMthPPa8LsqG_zbVe7vnW_Ag,2995
|
44
|
-
edu_rdm_integration/collect_data/tests.py,sha256
|
45
|
+
edu_rdm_integration/collect_data/tests.py,sha256=hlf2eXUQonYeLY2enm-ND712NrsGcYa03FxjCUOj7EE,5535
|
45
46
|
edu_rdm_integration/collect_data/base/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
46
47
|
edu_rdm_integration/collect_data/base/caches.py,sha256=XPMVZSgR8vRhCHmHsnUKy86rEIPUQXHz0wJabovPyfU,2170
|
47
48
|
edu_rdm_integration/collect_data/base/functions.py,sha256=E1vYmC8F8NM5cBB5SGQEpluHrMynpjf3Ek3s0HsbxwY,2998
|
@@ -82,13 +83,13 @@ edu_rdm_integration/collect_data/non_calculated/base/strings.py,sha256=-k9dex8A7
|
|
82
83
|
edu_rdm_integration/collect_data/non_calculated/base/tests.py,sha256=MoRY-a75Ow-7EjeQYxkXWunwqTGuBMaUyEkEV2oy05I,59
|
83
84
|
edu_rdm_integration/collect_data/non_calculated/base/validators.py,sha256=0YvnfrfK1iFcZVSB-M-Xv82tIjYxEU_BwLofAEuGVW4,973
|
84
85
|
edu_rdm_integration/enum_register/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
85
|
-
edu_rdm_integration/enum_register/mixins.py,sha256=
|
86
|
+
edu_rdm_integration/enum_register/mixins.py,sha256=YYCvSQEf6RMaMnGS2vA2v-P1ojkXA5MgAuBOcFgLIRE,4642
|
86
87
|
edu_rdm_integration/enum_register/register.py,sha256=5OWOjK-M0Erd_5CENpBaXhVtfL0pEaDl3Bev5QKNDJc,2218
|
87
88
|
edu_rdm_integration/export_data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
88
89
|
edu_rdm_integration/export_data/consts.py,sha256=rmJ_18wHCE54j0VABxfo7Zu01EPFYSjYrj5L79hVW2Y,333
|
89
90
|
edu_rdm_integration/export_data/dataclasses.py,sha256=IhftRopP4lS-m3ygdBU5Bz0HF71VSBP4JQ6-8VIVgtY,260
|
90
|
-
edu_rdm_integration/export_data/export.py,sha256=
|
91
|
-
edu_rdm_integration/export_data/export_manger.py,sha256=
|
91
|
+
edu_rdm_integration/export_data/export.py,sha256=xR2QRRL_HLD9MUIoe98Krc4KN0itWCpEl3uDM2tcqeE,18513
|
92
|
+
edu_rdm_integration/export_data/export_manger.py,sha256=lbXK3aV0Te0ZfJHylvCBZ0bJ7vWwD-pMQBX4Du8gPro,10681
|
92
93
|
edu_rdm_integration/export_data/generators.py,sha256=yLDOcHB1PoilJwXtKGxZQhDjpeKBzEWoosahbJJ4Ba4,4020
|
93
94
|
edu_rdm_integration/export_data/helpers.py,sha256=hU346RmQ17Ra2etFvxXI7JQlLyp_0KxH1jm-eeCqejc,2933
|
94
95
|
edu_rdm_integration/export_data/queue.py,sha256=NiWm7e59zOcGT8s87BxiyU4-nA02fH94oVKnhc8htQ4,6159
|
@@ -162,6 +163,7 @@ edu_rdm_integration/migrations/0010_transferredentity_export_enabled.py,sha256=L
|
|
162
163
|
edu_rdm_integration/migrations/0011_exportingdatasubstageentity.py,sha256=2BfIif_hkFv1h6VEfe0Ys4J_uk6LR9YtO711ocDYPso,1263
|
163
164
|
edu_rdm_integration/migrations/0012_exportingdatasubstageattachment_attachment_size.py,sha256=y_JQO69k9pEfrJyimaRiAOBmhaJmssIyepCGd-Sy9hs,511
|
164
165
|
edu_rdm_integration/migrations/0013_set_attachment_size.py,sha256=Pj_n-ytsC0lhyU67qvH8UHHQ-c-TH5MZFfy-UF6y6M4,1809
|
166
|
+
edu_rdm_integration/migrations/0014_uploaddatacommand.py,sha256=Hh0vKKiGgKOvY1kBAcmway4dSYUXwVArHAc9YrsjCIU,2079
|
165
167
|
edu_rdm_integration/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
166
168
|
edu_rdm_integration/registry/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
167
169
|
edu_rdm_integration/registry/actions.py,sha256=YUtCkpcc3DedM_x8rwJ6Aos-8HKkDCrOUdKnGhztfUU,5223
|
@@ -174,9 +176,9 @@ edu_rdm_integration/uploader_log/enums.py,sha256=rgSO3BL2rh2xpfm0Pt4waQW8fB1VMJL
|
|
174
176
|
edu_rdm_integration/uploader_log/managers.py,sha256=y5wTSMzF9hpOpIU_A7nIafL_LBU3QEie6LAYWoB-pBQ,3203
|
175
177
|
edu_rdm_integration/uploader_log/ui.py,sha256=YM9Buqp2wxE95Wf5gvAATBzuYzDOossK1sEmvFk07cI,2110
|
176
178
|
edu_rdm_integration/uploader_log/templates/ui-js/object-grid-buttons.js,sha256=2xyGe0wdVokM0RhpzRzcRvJPBkBmPe3SlZry4oP4Nzs,6201
|
177
|
-
edu_rdm_integration-3.
|
178
|
-
edu_rdm_integration-3.
|
179
|
-
edu_rdm_integration-3.
|
180
|
-
edu_rdm_integration-3.
|
181
|
-
edu_rdm_integration-3.
|
182
|
-
edu_rdm_integration-3.
|
179
|
+
edu_rdm_integration-3.4.0.dist-info/LICENSE,sha256=uw43Gjjj-1vXWCItfSrNDpbejnOwZMrNerUh8oWbq8Q,3458
|
180
|
+
edu_rdm_integration-3.4.0.dist-info/METADATA,sha256=yyTvi9MApMnWTUi9cv5G3MRQ8CKcEb48KFVSvcGQXzk,73632
|
181
|
+
edu_rdm_integration-3.4.0.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
|
182
|
+
edu_rdm_integration-3.4.0.dist-info/namespace_packages.txt,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
183
|
+
edu_rdm_integration-3.4.0.dist-info/top_level.txt,sha256=nRJV0O14UtNE-jGIYG03sohgFnZClvf57H5m6VBXe9Y,20
|
184
|
+
edu_rdm_integration-3.4.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
{edu_rdm_integration-3.3.6.dist-info → edu_rdm_integration-3.4.0.dist-info}/namespace_packages.txt
RENAMED
File without changes
|
File without changes
|