edu-rdm-integration 3.5.2__py3-none-any.whl → 3.5.4__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.
@@ -52,8 +52,11 @@ RDM_UPLOAD_QUEUE_MAX_SIZE = 500_000_000
52
52
  # Таймаут для сохранения параметров в общем кеш
53
53
  RDM_REDIS_CACHE_TIMEOUT_SECONDS = 60 * 60 * 2
54
54
 
55
+
55
56
  # Настройка запуска периодической задачи отправки csv-файлов в витрину:
56
57
  RDM_UPLOAD_DATA_TASK_MINUTE = '0'
57
58
  RDM_UPLOAD_DATA_TASK_HOUR = '*/2'
58
59
  RDM_UPLOAD_DATA_TASK_DAY_OF_WEEK = '*'
59
- RDM_UPLOAD_DATA_TASK_LOCK_EXPIRE_SECONDS = 60 * 60 * 2
60
+ RDM_UPLOAD_DATA_TASK_LOCK_EXPIRE_SECONDS = 60 * 60 * 2
61
+ # Количество подэтапов для обработки в периодической задаче отправки данных
62
+ RDM_UPLOAD_DATA_TASK_EXPORT_STAGES = 500
@@ -173,7 +173,8 @@ class BaseIgnoreLogMixin:
173
173
  def _exclude_logs(self, model_label: str, object_model_ids: Iterable[int]):
174
174
  """Исключаем логи из обработки."""
175
175
  for object_id in object_model_ids:
176
- self.logs[model_label].pop(object_id)
176
+ if object_id in self.logs[model_label]:
177
+ del self.logs[model_label][object_id]
177
178
 
178
179
  def _ignore_logs(self):
179
180
  """
@@ -4,3 +4,6 @@ DELIMITER = ';'
4
4
  REDIS_QUEUE_KEY_DELIMITER = ':'
5
5
  # Ключ параметра Общий объем отправленных в РВД файлов
6
6
  TOTAL_ATTACHMENTS_SIZE_KEY = 'rdm:total_attachments_size'
7
+
8
+ # Ограничение количества файлов на обработку
9
+ ATTACHMENTS_LIMIT = 500
@@ -1,3 +1,4 @@
1
+ import logging
1
2
  import os
2
3
  from collections import (
3
4
  defaultdict,
@@ -67,7 +68,7 @@ from edu_rdm_integration.enums import (
67
68
  FileUploadStatusEnum,
68
69
  )
69
70
  from edu_rdm_integration.export_data.export_manger import (
70
- ExportEntityQueueSender,
71
+ ExportQueueSender,
71
72
  WorkerSender,
72
73
  )
73
74
  from edu_rdm_integration.export_data.queue import (
@@ -370,39 +371,41 @@ class ExportLatestEntitiesData(BaseExportLatestEntitiesData):
370
371
  return RunningTask
371
372
 
372
373
 
373
- class UploadEntitiesData(BaseOperationData):
374
- """Класс отправки файлов с сущностями в витрину."""
374
+ class UploadData(BaseOperationData):
375
+ """Класс отправки файлов в витрину."""
375
376
 
376
377
  def __init__(
377
378
  self,
378
- entities: Iterable[str],
379
379
  data_cache: AbstractCache,
380
380
  queue: Queue,
381
381
  **kwargs,
382
382
  ):
383
383
  super().__init__(**kwargs)
384
384
 
385
- # Если сущности не указаны, берется значение по умолчанию - все сущности:
386
- self.entities = entities if entities else RegionalDataMartEntityEnum.get_enum_data().keys()
387
-
388
385
  self.data_cache = data_cache
389
386
  self.queue = queue
390
387
 
391
- self.task_id = kwargs.get('task_id')
392
-
393
388
  self._configure_agent_client()
389
+ self.result = {
390
+ 'total_file_size': 0, # Общий размер отправленных файлов
391
+ 'queue_is_full': False, # Признак переполнения очереди
392
+ 'uploaded_entities': '' # Список сущностей, попавших в выгрузку
393
+ }
394
394
 
395
395
  @property
396
396
  def _log_file_path(self) -> Union[str, bytes]:
397
397
  """
398
398
  Путь до лог файла.
399
399
  """
400
- if self.command_id:
401
- log_file_path = os.path.join(settings.MEDIA_ROOT, settings.RDM_UPLOAD_LOG_DIR, f'{self.command_id}.log')
402
- else:
403
- log_file_path = os.path.join(settings.MEDIA_ROOT, settings.RDM_UPLOAD_LOG_DIR, 'upload_entity.log')
400
+ return os.path.join(settings.MEDIA_ROOT, settings.RDM_UPLOAD_LOG_DIR, 'upload_entity.log')
404
401
 
405
- return log_file_path
402
+ def _add_file_handler(self) -> None:
403
+ """Добавляет обработчик логов."""
404
+
405
+ self._file_handler = logging.FileHandler(self._log_file_path)
406
+
407
+ logging.getLogger('info_logger').addHandler(self._file_handler)
408
+ logging.getLogger('exception_logger').addHandler(self._file_handler)
406
409
 
407
410
  # TODO https://jira.bars.group/browse/EDUSCHL-22492. Вынужденная мера, т.к. при запуске команды не производится
408
411
  # проверка готовности конфигов приложений. Нужно переработать механизм конфигурирования клиента загрузчика.
@@ -459,14 +462,24 @@ class UploadEntitiesData(BaseOperationData):
459
462
  Запускает отправку данных в витрину.
460
463
  """
461
464
  try:
462
- exporter = ExportEntityQueueSender(self.data_cache, self.queue, self.entities)
465
+ exporter = ExportQueueSender(self.data_cache, self.queue, settings.RDM_UPLOAD_DATA_TASK_EXPORT_STAGES)
463
466
  exporter.run()
464
467
 
465
- sender = WorkerSender(self.queue)
466
- sender.run()
467
-
468
+ self.result['queue_is_full'] = exporter.queue_is_full
469
+ self.result['total_file_size'] = exporter.queue_total_file_size
470
+
471
+ # Если очередь не переполнена - то отправляем данные в витрину
472
+ if not exporter.queue_is_full:
473
+ sender = WorkerSender(self.queue)
474
+ sender.run()
475
+
476
+ if sender.entities:
477
+ self.result['uploaded_entities'] = ','.join(sender.entities)
478
+
468
479
  except Exception as err:
469
480
  logger.exception(err)
470
481
  raise err
471
482
  finally:
472
483
  self._remove_file_handler()
484
+
485
+ return self.result
@@ -3,6 +3,7 @@ from pathlib import (
3
3
  )
4
4
  from typing import (
5
5
  Iterable,
6
+ Optional,
6
7
  )
7
8
 
8
9
  from django.conf import (
@@ -38,6 +39,7 @@ from edu_rdm_integration.export_data.base.requests import (
38
39
  RegionalDataMartEntityRequest,
39
40
  )
40
41
  from edu_rdm_integration.export_data.consts import (
42
+ ATTACHMENTS_LIMIT,
41
43
  TOTAL_ATTACHMENTS_SIZE_KEY,
42
44
  )
43
45
  from edu_rdm_integration.export_data.dataclasses import (
@@ -60,10 +62,12 @@ from edu_rdm_integration.redis_cache import (
60
62
  class ExportQueueSender:
61
63
  """Класс отправки данных в очередь РВД."""
62
64
 
63
- def __init__(self, data_cache: AbstractCache, queue: Queue):
65
+ def __init__(self, data_cache: AbstractCache, queue: Queue, limit: Optional[int] = None):
64
66
  self.queue = queue
65
67
  self.cache = data_cache
66
68
  self.queue_total_file_size = self.cache.get(TOTAL_ATTACHMENTS_SIZE_KEY) or 0
69
+ self.queue_is_full = False
70
+ self.limit = limit or ATTACHMENTS_LIMIT
67
71
 
68
72
  @staticmethod
69
73
  def get_exported_file_size() -> int:
@@ -80,22 +84,25 @@ class ExportQueueSender:
80
84
 
81
85
  return file_size or 0
82
86
 
83
- @staticmethod
84
87
  def _make_stage_filter(self) -> Q:
85
88
  """Формирование фильтра для выборки подэтапов."""
86
89
  return Q(status_id=ExportingDataSubStageStatus.READY_FOR_EXPORT.key)
87
90
 
88
91
  def get_sub_stages_attachments_to_export(self):
89
92
  """Выборка готовых к экспорту подэтапов."""
90
- return ExportingDataSubStage.objects.filter(
93
+ sub_stage_ids = ExportingDataSubStage.objects.filter(
91
94
  self._make_stage_filter()
95
+ ).order_by('started_at').values_list('id', flat=True)[:self.limit]
96
+
97
+ return ExportingDataSubStage.objects.filter(
98
+ id__in=sub_stage_ids
92
99
  ).annotate(
93
100
  attachment_id=F('exportingdatasubstageattachment__id'),
94
101
  attachment_name=F('exportingdatasubstageattachment__attachment'),
95
102
  attachment_size=F('exportingdatasubstageattachment__attachment_size'),
96
103
  operation=F('exportingdatasubstageattachment__operation'),
97
104
  entity=F('exportingdatasubstageentity__entity_id')
98
- ).order_by('started_at', 'operation').values(
105
+ ).order_by('id', 'operation').values(
99
106
  'id',
100
107
  'attachment_id',
101
108
  'attachment_name',
@@ -120,7 +127,7 @@ class ExportQueueSender:
120
127
  )
121
128
 
122
129
  logger.info(
123
- f'ExportedDataSubStage {sub_stage_id} {entity_name} added to the queue'
130
+ f'{LOGS_DELIMITER * 2}ExportedDataSubStage {sub_stage_id} {entity_name} added to the queue'
124
131
  )
125
132
 
126
133
  return True
@@ -129,7 +136,6 @@ class ExportQueueSender:
129
136
  """Запуск работы очереди."""
130
137
  if not self.queue_total_file_size:
131
138
  self.queue_total_file_size = self.get_exported_file_size()
132
-
133
139
  if self.queue_total_file_size < settings.RDM_UPLOAD_QUEUE_MAX_SIZE:
134
140
  stage_files = []
135
141
  prev_sub_stage = None
@@ -166,32 +172,18 @@ class ExportQueueSender:
166
172
  self.queue_total_file_size,
167
173
  timeout=settings.RDM_REDIS_CACHE_TIMEOUT_SECONDS
168
174
  )
175
+ self.queue_is_full = True
169
176
  logger.warning(
170
177
  f'Total exported file size: {self.queue_total_file_size} - queue is full!!!'
171
178
  )
172
179
 
173
180
 
174
- class ExportEntityQueueSender(ExportQueueSender):
175
- """Класс отправки данных по конкретной сущности в очередь РВД."""
176
-
177
- def __init__(self, data_cache: AbstractCache, queue: Queue, entity: Iterable[str]):
178
- super().__init__(data_cache, queue)
179
-
180
- self.entity = entity
181
-
182
- def _make_stage_filter(self) -> Q:
183
- """Формирование фильтра для выборки подэтапов."""
184
- return Q(
185
- status_id=ExportingDataSubStageStatus.READY_FOR_EXPORT.key,
186
- exportingdatasubstageentity__entity_id__in=self.entity
187
- )
188
-
189
-
190
181
  class WorkerSender:
191
182
  """Непосредственная отправка файлов."""
192
183
 
193
184
  def __init__(self, queue: Queue):
194
185
  self.queue = queue
186
+ self.entities = set()
195
187
 
196
188
  def send_files(self):
197
189
  """Отправка файлов."""
@@ -216,7 +208,8 @@ class WorkerSender:
216
208
  try:
217
209
  file_data = file_path.open('rb').read()
218
210
  except (OSError, IOError, FileNotFoundError) as error:
219
- logger.error(f'Ошибка чтения файла {file_path} - {str(error)} ')
211
+ logger.exception(f'Ошибка чтения файла {file_path} - {str(error)} ')
212
+ status = ExportingDataSubStageStatus.FAILED.key
220
213
  continue
221
214
 
222
215
  request = RegionalDataMartEntityRequest(
@@ -260,17 +253,17 @@ class WorkerSender:
260
253
  logger.info(
261
254
  f'Response with {result.response.status_code} code and content {result.response.text}'
262
255
  )
256
+ self.entities.add(entity_key)
263
257
 
264
258
  # Сохраняем информацию об отправке файлов и убираем подэтап с файлами из очереди
265
- if result_to_save:
266
- with atomic():
267
- ExportingDataSubStageUploaderClientLog.objects.bulk_create(result_to_save)
268
- sub_stage.status_id = status
269
- sub_stage.save()
259
+ with atomic():
260
+ ExportingDataSubStageUploaderClientLog.objects.bulk_create(result_to_save)
261
+ sub_stage.status_id = status
262
+ sub_stage.save()
270
263
 
271
264
  self.queue.delete_from_queue(sub_stage_id=sub_stage_id, entity_name=entity_key)
272
265
  logger.info(
273
- f'ExportedDataSubStage {sub_stage_id} {entity_key} sended from the queue'
266
+ f'{LOGS_DELIMITER * 3}ExportedDataSubStage {sub_stage_id} {entity_key} sended from the queue'
274
267
  )
275
268
 
276
269
  def run(self):
@@ -56,7 +56,7 @@ from edu_rdm_integration.enums import (
56
56
  )
57
57
  from edu_rdm_integration.export_data.export import (
58
58
  ExportLatestEntitiesData,
59
- UploadEntitiesData,
59
+ UploadData,
60
60
  )
61
61
  from edu_rdm_integration.export_data.helpers import (
62
62
  set_failed_status_suspended_exporting_data_stages,
@@ -74,7 +74,6 @@ from edu_rdm_integration.models import (
74
74
  ExportingDataSubStageUploaderClientLog,
75
75
  RegionalDataMartEntityEnum,
76
76
  TransferredEntity,
77
- UploadDataCommand,
78
77
  )
79
78
  from edu_rdm_integration.storages import (
80
79
  RegionalDataMartEntityStorage,
@@ -360,31 +359,23 @@ class UploadDataAsyncTask(UniquePeriodicAsyncTask):
360
359
  """Выполнение."""
361
360
  super().process(*args, **kwargs)
362
361
 
363
- entity_ids_for_export = list(
364
- TransferredEntity.objects.filter(export_enabled=True).values_list('entity_id', flat=True)
365
- )
366
362
  queue = RdmRedisSubStageAttachmentQueue()
367
- task_id = RunningTask.objects.filter(
368
- pk=self.request.id,
369
- ).values_list('pk', flat=True).first()
363
+ upload_data = UploadData(
364
+ data_cache=cache,
365
+ queue=queue,
366
+ )
367
+
368
+ upload_result = upload_data.upload_data()
370
369
 
371
- if task_id:
372
- for entity_id in entity_ids_for_export:
373
- upload_data_command = UploadDataCommand.objects.create(
374
- entity_id=entity_id,
375
- task_id=task_id,
376
- type=CommandType.AUTO,
377
- )
378
- if upload_data_command:
379
- upload_data = UploadEntitiesData(
380
- entities=[upload_data_command.entity_id],
381
- data_cache=cache,
382
- queue=queue,
383
- task_id=task_id,
384
- command_id=upload_data_command.id,
385
- )
386
- upload_data.upload_data()
387
- save_command_log_link(upload_data_command, settings.RDM_UPLOAD_LOG_DIR)
370
+ task_result = {
371
+ 'Общий объем отправленных файлов': f"{upload_result['total_file_size']}",
372
+ 'Очередь отправки переполнена': 'Да' if upload_result['queue_is_full'] else 'Нет',
373
+ 'Сущности, отправленные в витрину': upload_result['uploaded_entities']
374
+ }
375
+
376
+ self.set_progress(
377
+ values=task_result
378
+ )
388
379
 
389
380
 
390
381
  celery_app = celery.app.app_or_default()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: edu-rdm-integration
3
- Version: 3.5.2
3
+ Version: 3.5.4
4
4
  Summary: Интеграция с Региональной витриной данных
5
5
  Home-page:
6
6
  Download-URL:
@@ -301,6 +301,27 @@ Requires-Dist: uploader-client <1,>=0.2.1
301
301
  ### Удалено
302
302
 
303
303
 
304
+ ## [3.5.4] - 2025-02-07
305
+
306
+ Внесено исправление в BaseIgnoreLogMixin, в части работы метода _exclude_logs.
307
+
308
+ ### Изменено
309
+
310
+ - [EDUSCHL-23160](https://jira.bars.group/browse/EDUSCHL-23160)
311
+ PATCH Внесено исправление в BaseIgnoreLogMixin, в части работы метода _exclude_logs.
312
+
313
+
314
+ ## [3.5.3] - 2025-01-29
315
+
316
+ Изменена периодическая задача UploadDataAsyncTask.
317
+
318
+ ### Изменено
319
+
320
+ - [EDUSCHL-23092](https://jira.bars.group/browse/EDUSCHL-23092)
321
+ PATCH Изменена периодическая задача UploadDataAsyncTask - удалено формирование очереди по сущностям. Сейчас в очередь
322
+ попадает указанное в настройках количество файлов, готовых к отправке в витрину
323
+
324
+
304
325
  ## [3.5.2] - 2025-02-04
305
326
 
306
327
  Изменён тип поля institute_ids с django.contrib.postgres.fields.JSONField (поддержка до Django 4.0.)
@@ -1,6 +1,6 @@
1
1
  edu_rdm_integration/__init__.py,sha256=fVCvQ7QGI_iCyAeE8dMapyY8gOM617ye5GQqAVGPlZI,72
2
2
  edu_rdm_integration/app_meta.py,sha256=v5IU69yaeLbyHF0Ln6iPN_IfizbtF3rCWrz2n71m8dU,337
3
- edu_rdm_integration/app_settings.py,sha256=jcMh5WC2_TbwGNZ2FsdlQX2u9MWEZ5XPklKSqOAZzTo,2813
3
+ edu_rdm_integration/app_settings.py,sha256=FnTCsAM7lvd05q4YQqZnCS31plnluEJmUtIwJ6zunoM,2996
4
4
  edu_rdm_integration/apps.py,sha256=iDNitsYHE1-djp2NCW4HKnFHNoDSg8Fzc8Tobe26Qzw,3571
5
5
  edu_rdm_integration/base.py,sha256=_G0qPTAXe6bXfgDHNiZMSsYt3sMuUhLKnHuQCWSFttU,1341
6
6
  edu_rdm_integration/consts.py,sha256=Qt52SOCQ-3wOet-_6inJih_W9nToORKXbkxb3jVSjEo,1079
@@ -12,7 +12,7 @@ edu_rdm_integration/models.py,sha256=e1n-1UuG0Okhx6NSxD-m3iSlvStDmBdJs-4HiBa8aTQ
12
12
  edu_rdm_integration/redis_cache.py,sha256=SP_rcL5t6PTVLOnEYn_NTX0Z666VdZT4By2pyED24Z4,1537
13
13
  edu_rdm_integration/signals.py,sha256=3eRlpkDcFCF6TN80-QM8yBYLcyozzcmoPjz6r4_ApWg,73
14
14
  edu_rdm_integration/storages.py,sha256=G4Q4tIyJdEyb9ka551PADCFIm66bpsJe9VBRcvQhLMI,6745
15
- edu_rdm_integration/tasks.py,sha256=TP1oc32xahTQsX_vhWxYaCxDJdw9-iN2L1Lukins3K0,16024
15
+ edu_rdm_integration/tasks.py,sha256=ie9fduL1QMR_ndFXIruY4VXdTnStqiV2vd6iNILFxQw,15540
16
16
  edu_rdm_integration/typing.py,sha256=2asD8biX0l_DVqJSU4y19-zzEBJMk967PUj3UhzICzs,785
17
17
  edu_rdm_integration/utils.py,sha256=NNRfblJ9QoX07qUa2kfKG0jM3fHJ3oaDvG2zwj0As70,12120
18
18
  edu_rdm_integration/adapters/__init__.py,sha256=cU0swn4Ny5ZQz5buWRcWsT1mpWuUFJaUlHf2l7TtEBo,83
@@ -49,7 +49,7 @@ edu_rdm_integration/collect_data/base/caches.py,sha256=_Ja0q3hPSE_Mq-MCZN-1jr0Wv
49
49
  edu_rdm_integration/collect_data/base/functions.py,sha256=E1vYmC8F8NM5cBB5SGQEpluHrMynpjf3Ek3s0HsbxwY,2998
50
50
  edu_rdm_integration/collect_data/base/helpers.py,sha256=MsHEzkUl1KVMIxGfDkhcItXLxQNuNNOhN5tzTcPm0NQ,982
51
51
  edu_rdm_integration/collect_data/base/managers.py,sha256=hza8kugR9hbLK3LU8DBfF61ul7mPPl5pPm530QMjj0k,6143
52
- edu_rdm_integration/collect_data/base/mixins.py,sha256=K3HOWaxAE3Myzh8CgTm-ZKE7_hbW9p6IcCnvtbDzlM4,8482
52
+ edu_rdm_integration/collect_data/base/mixins.py,sha256=kwZ77dc5dCmHVGCN6JMslpyfXfEEoks7vmXH-CzezJc,8538
53
53
  edu_rdm_integration/collect_data/base/runners.py,sha256=Mf5Lux6IWWqKd798v149OjqfBWiDtigeFd701HSRBGg,1565
54
54
  edu_rdm_integration/collect_data/calculated/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
55
55
  edu_rdm_integration/collect_data/calculated/strategies.py,sha256=nppmheuzhF8H7JcfEhEZCmHI0eeRj8BbrLGNF0nsoa0,7619
@@ -87,10 +87,10 @@ edu_rdm_integration/enum_register/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeR
87
87
  edu_rdm_integration/enum_register/mixins.py,sha256=lry5JDGIleBCcOqsfX0sVOqwnnSV2Jlb4T2heD-xg54,4621
88
88
  edu_rdm_integration/enum_register/register.py,sha256=jb16O7TZDGVRo5NwWqyY_rcbBxOncYeOoSq6UAbTf48,2208
89
89
  edu_rdm_integration/export_data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
90
- edu_rdm_integration/export_data/consts.py,sha256=rmJ_18wHCE54j0VABxfo7Zu01EPFYSjYrj5L79hVW2Y,333
90
+ edu_rdm_integration/export_data/consts.py,sha256=ZEi1kXMs-54KFKxkyGIQVwZ4d8OrOF_vLFQIjjWdSPQ,441
91
91
  edu_rdm_integration/export_data/dataclasses.py,sha256=IhftRopP4lS-m3ygdBU5Bz0HF71VSBP4JQ6-8VIVgtY,260
92
- edu_rdm_integration/export_data/export.py,sha256=h2WjbgkrFpp7pA5XmDgKjhn_n-C7vgULCgN3dT1E1S4,18474
93
- edu_rdm_integration/export_data/export_manger.py,sha256=xAokqaYi00GA7Otsu9pfwqMRQXtYReoX6g1BSjAJtnU,10937
92
+ edu_rdm_integration/export_data/export.py,sha256=WSnli8-ZBtZw4b2_8qnmPTgKW52XMdZ9RlJ4eo25xyQ,19071
93
+ edu_rdm_integration/export_data/export_manger.py,sha256=Q1O1lO8kF0s4jzQsMj-qWcFe14_YPM0AXXWywXWj-z8,10776
94
94
  edu_rdm_integration/export_data/generators.py,sha256=UIoX49rQnUwwC9PL17te7Rb4WRDe_5ShYQ1Rygf36NQ,4010
95
95
  edu_rdm_integration/export_data/helpers.py,sha256=JKL4MC0IuoJv72NNnL8hG7HLy7kNHOXFp1uLCKF15AM,2900
96
96
  edu_rdm_integration/export_data/queue.py,sha256=GlwRwhMdv38OxmtFpFD5-Pt79gMe3IcLeWFQCLXTcik,6128
@@ -178,9 +178,9 @@ edu_rdm_integration/uploader_log/enums.py,sha256=rgSO3BL2rh2xpfm0Pt4waQW8fB1VMJL
178
178
  edu_rdm_integration/uploader_log/managers.py,sha256=y5wTSMzF9hpOpIU_A7nIafL_LBU3QEie6LAYWoB-pBQ,3203
179
179
  edu_rdm_integration/uploader_log/ui.py,sha256=YM9Buqp2wxE95Wf5gvAATBzuYzDOossK1sEmvFk07cI,2110
180
180
  edu_rdm_integration/uploader_log/templates/ui-js/object-grid-buttons.js,sha256=2xyGe0wdVokM0RhpzRzcRvJPBkBmPe3SlZry4oP4Nzs,6201
181
- edu_rdm_integration-3.5.2.dist-info/LICENSE,sha256=uw43Gjjj-1vXWCItfSrNDpbejnOwZMrNerUh8oWbq8Q,3458
182
- edu_rdm_integration-3.5.2.dist-info/METADATA,sha256=Y8n72AFmj2fGwdui8iYNWtQcdb2iZuf7iWlxCdNT4wc,78751
183
- edu_rdm_integration-3.5.2.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
184
- edu_rdm_integration-3.5.2.dist-info/namespace_packages.txt,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
185
- edu_rdm_integration-3.5.2.dist-info/top_level.txt,sha256=nRJV0O14UtNE-jGIYG03sohgFnZClvf57H5m6VBXe9Y,20
186
- edu_rdm_integration-3.5.2.dist-info/RECORD,,
181
+ edu_rdm_integration-3.5.4.dist-info/LICENSE,sha256=uw43Gjjj-1vXWCItfSrNDpbejnOwZMrNerUh8oWbq8Q,3458
182
+ edu_rdm_integration-3.5.4.dist-info/METADATA,sha256=NkUahYegND2vl7RrspU-lhHOIL0dsye-AEdszKoQCdY,79645
183
+ edu_rdm_integration-3.5.4.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
184
+ edu_rdm_integration-3.5.4.dist-info/namespace_packages.txt,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
185
+ edu_rdm_integration-3.5.4.dist-info/top_level.txt,sha256=nRJV0O14UtNE-jGIYG03sohgFnZClvf57H5m6VBXe9Y,20
186
+ edu_rdm_integration-3.5.4.dist-info/RECORD,,