edu-rdm-integration 3.15.2__py3-none-any.whl → 3.15.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.
@@ -21,6 +21,14 @@ HASH_ALGORITHM = HashGostFunctionVersion.GOST12_512
21
21
 
22
22
  BATCH_SIZE = 5000
23
23
 
24
+ CHUNK_MAX_VALUE = 100000
25
+ """Максимальное значение для размера чанка при сборе данных РВД.
26
+ Ограничение 100000 предотвращает чрезмерное потребление памяти и блокировку БД
27
+ при обработке больших объемов данных в одном батче."""
28
+
29
+ SPLIT_BY_QUANTITY_MAX_VALUE = 366
30
+ """Максимальное значение для размера подпериода при временном разделении данных."""
31
+
24
32
  ACADEMIC_YEAR = {
25
33
  'start_day': 1,
26
34
  'start_month': 9,
@@ -167,15 +167,15 @@ class BaseFirstCollectModelsDataCommandsGenerator:
167
167
  period_ended_at=self.logs_period_ended_at,
168
168
  )
169
169
 
170
- for start_datetime, end_datetime in intervals:
171
- params_for_commands.append(
172
- {
173
- 'period_started_at': start_datetime,
174
- 'period_ended_at': end_datetime,
175
- 'model': rdm_model.key,
176
- 'generation_id': self.generation_id,
177
- }
178
- )
170
+ params_for_model = [
171
+ {
172
+ 'period_started_at': start_datetime,
173
+ 'period_ended_at': end_datetime,
174
+ 'model': rdm_model.key,
175
+ 'generation_id': self.generation_id,
176
+ }
177
+ for start_datetime, end_datetime in intervals
178
+ ]
179
179
 
180
180
  # Если подпериод не указан, то формируется список с разбиением по batch_size
181
181
  else:
@@ -213,15 +213,32 @@ class BaseFirstCollectModelsDataCommandsGenerator:
213
213
  cursor.execute(temp_get_logs_periods_sql)
214
214
  rows = cursor.fetchall()
215
215
 
216
- for period_started_at, period_ended_at, batch_number in rows:
217
- params_for_commands.append(
218
- {
219
- 'period_started_at': period_started_at,
220
- 'period_ended_at': period_ended_at,
221
- 'model': rdm_model.key,
222
- 'generation_id': self.generation_id,
223
- }
224
- )
216
+ params_for_model = [
217
+ {
218
+ 'period_started_at': period_started_at,
219
+ 'period_ended_at': period_ended_at,
220
+ 'model': rdm_model.key,
221
+ 'generation_id': self.generation_id,
222
+ }
223
+ for period_started_at, period_ended_at, _ in rows
224
+ ]
225
+
226
+ if params_for_model:
227
+ # Корректируем границы начала и конца сбора данных под значения введенные пользователем
228
+ params_for_model[0]['period_started_at'] = self.logs_period_started_at
229
+ params_for_model[-1]['period_ended_at'] = self.logs_period_ended_at
230
+ else:
231
+ # Создаем команду для отображения в UI, даже если не нашлось данных для обработки
232
+ params_for_model.append(
233
+ {
234
+ 'period_started_at': self.logs_period_started_at,
235
+ 'period_ended_at': self.logs_period_ended_at,
236
+ 'model': rdm_model.key,
237
+ 'generation_id': self.generation_id,
238
+ }
239
+ )
240
+
241
+ params_for_commands.extend(params_for_model)
225
242
 
226
243
  return params_for_commands
227
244
 
@@ -2,6 +2,9 @@ from functools import (
2
2
  partial,
3
3
  )
4
4
 
5
+ from django.conf import (
6
+ settings,
7
+ )
5
8
  from django.db.models import (
6
9
  F,
7
10
  Func,
@@ -268,6 +271,4 @@ class BaseCollectingDataProgressPack(BaseCommandProgressPack):
268
271
  )
269
272
  for command in commands_to_save
270
273
  ]
271
-
272
- for obj in objs:
273
- super().save_row(obj, create_new, request, context, *args, **kwargs)
274
+ self.model.objects.bulk_create(objs, batch_size=settings.COLLECT_PROGRESS_BATCH_SIZE)
@@ -0,0 +1,77 @@
1
+ {% load educommon %}
2
+ {% include "ui-js/validators.js" %}
3
+
4
+ Ext.onReady(function() {
5
+ // Инициализация валидаторов
6
+ initializeValidators();
7
+
8
+ // Инициализация взаимозависимых полей
9
+ initializeBatchSizeSplitByLogic();
10
+ });
11
+
12
+ function initializeValidators() {
13
+ // Валидатор для institute_ids
14
+ var instituteIdsField = Ext.getCmp('institute_ids');
15
+ if (instituteIdsField) {
16
+ instituteIdsField.validate = instituteIdsValidator;
17
+ instituteIdsField.on('blur', function() {
18
+ this.validate();
19
+ });
20
+ }
21
+
22
+ // Валидатор для institute_count
23
+ var instituteCountField = Ext.getCmp('institute_count');
24
+ if (instituteCountField) {
25
+ var originalValidator = instituteCountField.validate;
26
+
27
+ instituteCountField.validate = function() {
28
+ if (!instituteCountValidator.call(this)) {
29
+ return false;
30
+ }
31
+ if (originalValidator) {
32
+ return originalValidator.call(this);
33
+ }
34
+ return true;
35
+ };
36
+
37
+ instituteCountField.on('blur', function() {
38
+ this.validate();
39
+ });
40
+ }
41
+ }
42
+
43
+ function initializeBatchSizeSplitByLogic() {
44
+ var batchSizeField = Ext.getCmp('batch_size');
45
+ var splitByField = Ext.getCmp('split_by');
46
+
47
+ if (!batchSizeField || !splitByField) {
48
+ return;
49
+ }
50
+
51
+ // Функция для обновления обязательности полей
52
+ function updateFieldRequirements() {
53
+ var batchSizeValue = batchSizeField.getValue();
54
+ var splitByValue = splitByField.getValue();
55
+
56
+ if (splitByValue && splitByValue !== '') {
57
+ batchSizeField.allowBlank = true;
58
+ batchSizeField.clearInvalid();
59
+ } else {
60
+ batchSizeField.allowBlank = false;
61
+ }
62
+
63
+ if (batchSizeValue && batchSizeValue !== '' && batchSizeValue !== 0) {
64
+ splitByField.allowBlank = true;
65
+ splitByField.clearInvalid();
66
+ } else {
67
+ splitByField.allowBlank = false;
68
+ }
69
+ }
70
+
71
+ // Обработчики изменения полей
72
+ batchSizeField.on('change', updateFieldRequirements);
73
+ splitByField.on('change', updateFieldRequirements);
74
+
75
+ // Инициализация при загрузке
76
+ updateFieldRequirements();
77
+ }
@@ -0,0 +1,14 @@
1
+ var win = Ext.getCmp('{{ component.client_id }}');
2
+ var form = win.getForm();
3
+ var logsPeriodEndField = form.findField('logs_period_ended_at');
4
+
5
+ // Устанавливаем текущую дату и время как максимальное значение
6
+ // и выполняем валидацию каждую секунду
7
+ var validationInterval = setInterval(function() {
8
+ logsPeriodEndField.setMaxValue(new Date())
9
+ logsPeriodEndField.validate();
10
+ }, 1000);
11
+
12
+ win.on('destroy', function() {
13
+ clearInterval(validationInterval);
14
+ });
@@ -0,0 +1,51 @@
1
+ // Валидатор для поля institute_ids
2
+ var instituteIdsValidator = function () {
3
+ var value = this.getValue();
4
+
5
+ // Если поле пустое - это допустимо
6
+ if (!value || value.trim() === '') {
7
+ this.clearInvalid();
8
+ return true;
9
+ }
10
+
11
+ value = value.trim();
12
+
13
+ if (!/^[0-9,]+$/.test(value)) {
14
+ this.markInvalid('Разрешены только цифры и запятые');
15
+ return false;
16
+ }
17
+
18
+ var numbers = value.split(',');
19
+
20
+ for (var i = 0; i < numbers.length; i++) {
21
+ if (numbers[i].trim() === '') {
22
+ this.markInvalid('Между запятыми не должно быть пустых значений');
23
+ return false;
24
+ }
25
+ }
26
+
27
+ var uniqueNumbers = [];
28
+ for (var j = 0; j < numbers.length; j++) {
29
+ var num = parseInt(numbers[j].trim());
30
+ if (uniqueNumbers.indexOf(num) !== -1) {
31
+ this.markInvalid('ID организаций не должны повторяться');
32
+ return false;
33
+ }
34
+ uniqueNumbers.push(num);
35
+ }
36
+
37
+ this.clearInvalid();
38
+ return true;
39
+ };
40
+
41
+ // Валидатор для поля institute_count - запрещаем 0
42
+ var instituteCountValidator = function() {
43
+ var value = this.getValue();
44
+ if (value === 0) {
45
+ this.markInvalid('Количество организаций не может быть равно 0');
46
+ return false;
47
+ }
48
+
49
+ this.clearInvalid();
50
+ return true;
51
+ };
@@ -24,6 +24,8 @@ from educommon.utils.date import (
24
24
 
25
25
  from edu_rdm_integration.core.consts import (
26
26
  BATCH_SIZE,
27
+ CHUNK_MAX_VALUE,
28
+ SPLIT_BY_QUANTITY_MAX_VALUE,
27
29
  )
28
30
  from edu_rdm_integration.core.registry.ui import (
29
31
  BaseCreateCommandWindow,
@@ -39,6 +41,11 @@ from edu_rdm_integration.stages.collect_data.consts import (
39
41
  class CreateCollectCommandWindow(BaseCreateCommandWindow):
40
42
  """Окно создания команды сбора данных модели РВД."""
41
43
 
44
+ def __init__(self):
45
+ super().__init__()
46
+
47
+ self.template_globals = 'ui-js/create-collect-command-win.js'
48
+
42
49
  def _init_components(self):
43
50
  """Инициализация компонентов."""
44
51
  super()._init_components()
@@ -70,8 +77,9 @@ class CreateCollectCommandWindow(BaseCreateCommandWindow):
70
77
  display_field='split_by',
71
78
  label='Единица подпериода',
72
79
  anchor='100%',
73
- editable=True,
80
+ editable=False,
74
81
  trigger_action_all=True,
82
+ client_id='split_by',
75
83
  )
76
84
  split_by.set_store(ExtDataStore(enumerate(DatesSplitter.get_split_by_modes())))
77
85
  split_by_quantity = ExtNumberField(
@@ -80,6 +88,7 @@ class CreateCollectCommandWindow(BaseCreateCommandWindow):
80
88
  allow_blank=False,
81
89
  allow_decimals=False,
82
90
  allow_negative=False,
91
+ max_value=SPLIT_BY_QUANTITY_MAX_VALUE,
83
92
  anchor='100%',
84
93
  value=1,
85
94
  )
@@ -104,6 +113,8 @@ class CreateCollectCommandWindow(BaseCreateCommandWindow):
104
113
  allow_negative=False,
105
114
  anchor='100%',
106
115
  value=BATCH_SIZE,
116
+ max_value=CHUNK_MAX_VALUE,
117
+ client_id='batch_size',
107
118
  )
108
119
  by_institutes = ExtCheckBox(
109
120
  anchor='100%',
@@ -111,10 +122,12 @@ class CreateCollectCommandWindow(BaseCreateCommandWindow):
111
122
  name='by_institutes',
112
123
  )
113
124
  institute_ids = ExtStringField(
114
- label='id организаций',
125
+ label='ID организаций (через запятую, например: 1,2,3 или оставить пустым для всех)',
115
126
  name='institute_ids',
116
127
  allow_blank=True,
117
128
  anchor='100%',
129
+ max_length=100,
130
+ client_id='institute_ids',
118
131
  )
119
132
  institute_count = ExtNumberField(
120
133
  name='institute_count',
@@ -123,6 +136,8 @@ class CreateCollectCommandWindow(BaseCreateCommandWindow):
123
136
  anchor='100%',
124
137
  min_value=ALL_UNITS_IN_COMMAND,
125
138
  value=ALL_UNITS_IN_COMMAND,
139
+ client_id='institute_count',
140
+ allow_blank=False,
126
141
  )
127
142
  hint_text = ExtDisplayField(
128
143
  value=(
@@ -151,6 +166,12 @@ class CreateCollectCommandWindow(BaseCreateCommandWindow):
151
166
  split_mode,
152
167
  )
153
168
 
169
+ def set_params(self, params: Dict[str, Any]) -> None:
170
+ """Устанавливает параметры окна."""
171
+ super().set_params(params)
172
+
173
+ self.template_globals = 'ui-js/collect-command-window.js'
174
+
154
175
 
155
176
  class DetailCollectCommandWindow(BaseEditWindow):
156
177
  """Окно просмотра команды сбора данных модели РВД."""
@@ -0,0 +1,14 @@
1
+ var win = Ext.getCmp('{{ component.client_id }}');
2
+ var form = win.getForm();
3
+ var periodEndField = form.findField('period_ended_at');
4
+
5
+ // Устанавливаем текущую дату и время как максимальное значение
6
+ // и выполняем валидацию каждую секунду
7
+ var validationInterval = setInterval(function() {
8
+ periodEndField.setMaxValue(new Date())
9
+ periodEndField.validate();
10
+ }, 1000);
11
+
12
+ win.on('destroy', function() {
13
+ clearInterval(validationInterval);
14
+ });
@@ -44,6 +44,11 @@ from edu_rdm_integration.rdm_entities.models import (
44
44
  class CreateExportCommandWindow(BaseCreateCommandWindow):
45
45
  """Окно создания команды экспорта данных сущности РВД."""
46
46
 
47
+ def __init__(self):
48
+ super().__init__()
49
+
50
+ self.template_globals = 'ui-js/create-export-command-win.js'
51
+
47
52
  def _init_components(self):
48
53
  """Инициализация компонентов."""
49
54
  super()._init_components()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: edu-rdm-integration
3
- Version: 3.15.2
3
+ Version: 3.15.4
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
@@ -9,7 +9,7 @@ edu_rdm_integration/collect_and_export_data/migrations/0002_auto_20250204_1413.p
9
9
  edu_rdm_integration/collect_and_export_data/migrations/0003_auto_20250704_0725.py,sha256=IcBTuQWp7D_mP1chCgTXUIJcm5nf2Scdt3ZJgjCgvgI,1108
10
10
  edu_rdm_integration/collect_and_export_data/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  edu_rdm_integration/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
- edu_rdm_integration/core/consts.py,sha256=5pFDMWNGqKYqgqqUjb_QiUyWnjugHzmpl6beMJgWVEI,955
12
+ edu_rdm_integration/core/consts.py,sha256=4dklnQS7rtuYJu0Hh7oX1Z9dc72pbIWZl9gXMUvc4rI,1526
13
13
  edu_rdm_integration/core/enums.py,sha256=HHZxX2vIe-c4SY6BIWgOg-Tu-_LVJjltFZ1lI9YITLw,3824
14
14
  edu_rdm_integration/core/helpers.py,sha256=Fe2AyI8kHAfcnXAn2I_SyNraI5uAjNzHObUNtZtum-g,12183
15
15
  edu_rdm_integration/core/mapping.py,sha256=1B6TsC4Os9wiM8L8BChnCNv_iWqjeWu3bdDsqKVsId0,616
@@ -77,7 +77,7 @@ edu_rdm_integration/stages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
77
77
  edu_rdm_integration/stages/collect_data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
78
78
  edu_rdm_integration/stages/collect_data/apps.py,sha256=PhrxzAMXvzXajSlgwLudAgasVjh9IFS5M7a7pGbEKC0,339
79
79
  edu_rdm_integration/stages/collect_data/consts.py,sha256=tzaK9oxzdMRq3oTEocPz4umoXSJWUtFc7YhyXucCNbs,127
80
- edu_rdm_integration/stages/collect_data/generators.py,sha256=k6An9y3_3mzopYkoudmhspHB6sIUSDdLv6JPdMdMwWo,12075
80
+ edu_rdm_integration/stages/collect_data/generators.py,sha256=OM5KsInz_as8K2TX4XLJ-AWTnul-VR4v6oS5ORdU0fI,12970
81
81
  edu_rdm_integration/stages/collect_data/helpers.py,sha256=xy8z9yJKEMjNUPNhrsRRtnYy6RVbwDoD5zSDAX7y_6U,5260
82
82
  edu_rdm_integration/stages/collect_data/mixins.py,sha256=izioaiPC26BDODgi_Lhy33IaH207945tGjFnbFLMQyI,2072
83
83
  edu_rdm_integration/stages/collect_data/models.py,sha256=hxB2JUF0U0d7Gr8UcPDEhONLACTuqA63SFPEK5qn2Mo,8916
@@ -151,9 +151,12 @@ edu_rdm_integration/stages/collect_data/migrations/0003_auto_20250704_0810.py,sh
151
151
  edu_rdm_integration/stages/collect_data/migrations/0004_auto_20250704_0825.py,sha256=B6SUsxlhQvWoD8lFGNwaMUCFDzhPj91bsMdmAcSuEDg,1379
152
152
  edu_rdm_integration/stages/collect_data/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
153
153
  edu_rdm_integration/stages/collect_data/registry/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
154
- edu_rdm_integration/stages/collect_data/registry/actions.py,sha256=5eT6nw4xYuJlfVrFxoqn0SF4PqSgHIbMc7rlx8dWypE,9769
154
+ edu_rdm_integration/stages/collect_data/registry/actions.py,sha256=mUMGv1FjzjY0G46gTgi_mfL0yUjuCJlA2wQutq3jmPo,9798
155
155
  edu_rdm_integration/stages/collect_data/registry/apps.py,sha256=K5f97YXKMmdM7m33qgQYvJjrA8_eGAJ4VWyuRjJ0gwQ,439
156
- edu_rdm_integration/stages/collect_data/registry/ui.py,sha256=zmWZ0qgdu83V2MxRDIPyvDrgRcQKaeEzvMZ2CuvDE1M,7613
156
+ edu_rdm_integration/stages/collect_data/registry/ui.py,sha256=oOghmS49uCOEycOL3Wz-4-NUSy9mp7_pcwXf_VVz8rU,8427
157
+ edu_rdm_integration/stages/collect_data/registry/templates/ui-js/collect-command-window.js,sha256=IWG4CczBG9-Bi6X2Hivod4z63gSBZ6Fd126QVRRFacA,2386
158
+ edu_rdm_integration/stages/collect_data/registry/templates/ui-js/create-collect-command-win.js,sha256=BypYLy6-xKjmQucGy_uVEx2830jh5f7VFvUaAaqL_AY,549
159
+ edu_rdm_integration/stages/collect_data/registry/templates/ui-js/validators.js,sha256=c0p0ND7i2C-fZrADgiUv9Eekjx_ZlFHBrgd-oXuqXKI,1525
157
160
  edu_rdm_integration/stages/export_data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
158
161
  edu_rdm_integration/stages/export_data/apps.py,sha256=TU6AaMZGQE2oHVwKgtUDzFplaVasb2tMIurhkhwkxZo,406
159
162
  edu_rdm_integration/stages/export_data/consts.py,sha256=ZEi1kXMs-54KFKxkyGIQVwZ4d8OrOF_vLFQIjjWdSPQ,441
@@ -206,7 +209,8 @@ edu_rdm_integration/stages/export_data/migrations/__init__.py,sha256=47DEQpj8HBS
206
209
  edu_rdm_integration/stages/export_data/registry/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
207
210
  edu_rdm_integration/stages/export_data/registry/actions.py,sha256=r-NAPs4pYVectt_J38UzMsnKrWfYkjy2rhx3LUGKQWk,10423
208
211
  edu_rdm_integration/stages/export_data/registry/apps.py,sha256=71DtJQ2ULt8_3CnTu2VAfT5ABBrDNY1nKTmZ6UtvIpw,448
209
- edu_rdm_integration/stages/export_data/registry/ui.py,sha256=W5hFTBBgIPc7q0PAK8ObENxOYb5iq1SnV0-DZbiL4-U,5920
212
+ edu_rdm_integration/stages/export_data/registry/ui.py,sha256=GPp52ziV_xb0cnveprqIYmrx4kv0zgvrhMg-PHqUFeE,6042
213
+ edu_rdm_integration/stages/export_data/registry/templates/ui-js/create-export-command-win.js,sha256=ADE04kltbx1e0spaFK_ycBmtxBQPz6pqfaNY_czo9z8,532
210
214
  edu_rdm_integration/stages/export_data/registry/templates/ui-js/stage_for_export.js,sha256=329OZIpiKHlQ-i8JStjBLXtouIMKuJHbycArUGSskfk,737
211
215
  edu_rdm_integration/stages/service/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
212
216
  edu_rdm_integration/stages/service/apps.py,sha256=lgCG4_kpwgfDWh6y-GNuUwz5SOjkP7oS8kkUyVUcNRg,648
@@ -242,8 +246,8 @@ edu_rdm_integration/stages/upload_data/uploader_log/ui.py,sha256=mU3XA9zVKHGqzNk
242
246
  edu_rdm_integration/stages/upload_data/uploader_log/migrations/0001_initial.py,sha256=r5oOB7DBK9-mfuqPAgjXUJY5-hEcmMdILCwDTpaLnBc,753
243
247
  edu_rdm_integration/stages/upload_data/uploader_log/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
244
248
  edu_rdm_integration/stages/upload_data/uploader_log/templates/ui-js/object-grid-buttons.js,sha256=2xyGe0wdVokM0RhpzRzcRvJPBkBmPe3SlZry4oP4Nzs,6201
245
- edu_rdm_integration-3.15.2.dist-info/licenses/LICENSE,sha256=uw43Gjjj-1vXWCItfSrNDpbejnOwZMrNerUh8oWbq8Q,3458
246
- edu_rdm_integration-3.15.2.dist-info/METADATA,sha256=IPzmLm3bBn0vH34ibUeQuYNwOHfq1vYACpPlE4mFKDg,39741
247
- edu_rdm_integration-3.15.2.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
248
- edu_rdm_integration-3.15.2.dist-info/top_level.txt,sha256=nRJV0O14UtNE-jGIYG03sohgFnZClvf57H5m6VBXe9Y,20
249
- edu_rdm_integration-3.15.2.dist-info/RECORD,,
249
+ edu_rdm_integration-3.15.4.dist-info/licenses/LICENSE,sha256=uw43Gjjj-1vXWCItfSrNDpbejnOwZMrNerUh8oWbq8Q,3458
250
+ edu_rdm_integration-3.15.4.dist-info/METADATA,sha256=hkEr7M93oXb0udY1lgc4_zF_YdGnuH9wEZStdA4xV00,39741
251
+ edu_rdm_integration-3.15.4.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
252
+ edu_rdm_integration-3.15.4.dist-info/top_level.txt,sha256=nRJV0O14UtNE-jGIYG03sohgFnZClvf57H5m6VBXe9Y,20
253
+ edu_rdm_integration-3.15.4.dist-info/RECORD,,