folio-migration-tools 1.2.1__py3-none-any.whl → 1.9.10__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.
- folio_migration_tools/__init__.py +11 -0
- folio_migration_tools/__main__.py +169 -85
- folio_migration_tools/circulation_helper.py +96 -59
- folio_migration_tools/config_file_load.py +66 -0
- folio_migration_tools/custom_dict.py +6 -4
- folio_migration_tools/custom_exceptions.py +21 -19
- folio_migration_tools/extradata_writer.py +46 -0
- folio_migration_tools/folder_structure.py +63 -66
- folio_migration_tools/helper.py +29 -21
- folio_migration_tools/holdings_helper.py +57 -34
- folio_migration_tools/i18n_config.py +9 -0
- folio_migration_tools/library_configuration.py +173 -13
- folio_migration_tools/mapper_base.py +317 -106
- folio_migration_tools/mapping_file_transformation/courses_mapper.py +203 -0
- folio_migration_tools/mapping_file_transformation/holdings_mapper.py +83 -69
- folio_migration_tools/mapping_file_transformation/item_mapper.py +98 -94
- folio_migration_tools/mapping_file_transformation/manual_fee_fines_mapper.py +352 -0
- folio_migration_tools/mapping_file_transformation/mapping_file_mapper_base.py +702 -223
- folio_migration_tools/mapping_file_transformation/notes_mapper.py +90 -0
- folio_migration_tools/mapping_file_transformation/order_mapper.py +492 -0
- folio_migration_tools/mapping_file_transformation/organization_mapper.py +389 -0
- folio_migration_tools/mapping_file_transformation/ref_data_mapping.py +38 -27
- folio_migration_tools/mapping_file_transformation/user_mapper.py +149 -361
- folio_migration_tools/marc_rules_transformation/conditions.py +650 -246
- folio_migration_tools/marc_rules_transformation/holdings_statementsparser.py +292 -130
- folio_migration_tools/marc_rules_transformation/hrid_handler.py +244 -0
- folio_migration_tools/marc_rules_transformation/loc_language_codes.xml +20846 -0
- folio_migration_tools/marc_rules_transformation/marc_file_processor.py +300 -0
- folio_migration_tools/marc_rules_transformation/marc_reader_wrapper.py +136 -0
- folio_migration_tools/marc_rules_transformation/rules_mapper_authorities.py +241 -0
- folio_migration_tools/marc_rules_transformation/rules_mapper_base.py +681 -201
- folio_migration_tools/marc_rules_transformation/rules_mapper_bibs.py +395 -429
- folio_migration_tools/marc_rules_transformation/rules_mapper_holdings.py +531 -100
- folio_migration_tools/migration_report.py +85 -38
- folio_migration_tools/migration_tasks/__init__.py +1 -3
- folio_migration_tools/migration_tasks/authority_transformer.py +119 -0
- folio_migration_tools/migration_tasks/batch_poster.py +911 -198
- folio_migration_tools/migration_tasks/bibs_transformer.py +121 -116
- folio_migration_tools/migration_tasks/courses_migrator.py +192 -0
- folio_migration_tools/migration_tasks/holdings_csv_transformer.py +252 -247
- folio_migration_tools/migration_tasks/holdings_marc_transformer.py +321 -115
- folio_migration_tools/migration_tasks/items_transformer.py +264 -84
- folio_migration_tools/migration_tasks/loans_migrator.py +506 -195
- folio_migration_tools/migration_tasks/manual_fee_fines_transformer.py +187 -0
- folio_migration_tools/migration_tasks/migration_task_base.py +364 -74
- folio_migration_tools/migration_tasks/orders_transformer.py +373 -0
- folio_migration_tools/migration_tasks/organization_transformer.py +451 -0
- folio_migration_tools/migration_tasks/requests_migrator.py +130 -62
- folio_migration_tools/migration_tasks/reserves_migrator.py +253 -0
- folio_migration_tools/migration_tasks/user_transformer.py +180 -139
- folio_migration_tools/task_configuration.py +46 -0
- folio_migration_tools/test_infrastructure/__init__.py +0 -0
- folio_migration_tools/test_infrastructure/mocked_classes.py +406 -0
- folio_migration_tools/transaction_migration/legacy_loan.py +148 -34
- folio_migration_tools/transaction_migration/legacy_request.py +65 -25
- folio_migration_tools/transaction_migration/legacy_reserve.py +47 -0
- folio_migration_tools/transaction_migration/transaction_result.py +12 -1
- folio_migration_tools/translations/en.json +476 -0
- folio_migration_tools-1.9.10.dist-info/METADATA +169 -0
- folio_migration_tools-1.9.10.dist-info/RECORD +67 -0
- {folio_migration_tools-1.2.1.dist-info → folio_migration_tools-1.9.10.dist-info}/WHEEL +1 -2
- folio_migration_tools-1.9.10.dist-info/entry_points.txt +3 -0
- folio_migration_tools/generate_schemas.py +0 -46
- folio_migration_tools/mapping_file_transformation/mapping_file_mapping_base_impl.py +0 -44
- folio_migration_tools/mapping_file_transformation/user_mapper_base.py +0 -212
- folio_migration_tools/marc_rules_transformation/bibs_processor.py +0 -163
- folio_migration_tools/marc_rules_transformation/holdings_processor.py +0 -284
- folio_migration_tools/report_blurbs.py +0 -219
- folio_migration_tools/transaction_migration/legacy_fee_fine.py +0 -36
- folio_migration_tools-1.2.1.dist-info/METADATA +0 -134
- folio_migration_tools-1.2.1.dist-info/RECORD +0 -50
- folio_migration_tools-1.2.1.dist-info/top_level.txt +0 -1
- {folio_migration_tools-1.2.1.dist-info → folio_migration_tools-1.9.10.dist-info/licenses}/LICENSE +0 -0
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
'''Main "script."'''
|
|
2
1
|
import csv
|
|
3
2
|
import ctypes
|
|
4
3
|
import json
|
|
@@ -7,16 +6,16 @@ import sys
|
|
|
7
6
|
import time
|
|
8
7
|
import traceback
|
|
9
8
|
import uuid
|
|
10
|
-
from
|
|
11
|
-
from pathlib import Path
|
|
12
|
-
from typing import List, Optional
|
|
9
|
+
from typing import Annotated, List, Optional
|
|
13
10
|
|
|
11
|
+
import i18n
|
|
14
12
|
from folio_uuid.folio_namespaces import FOLIONamespaces
|
|
13
|
+
from pydantic import Field
|
|
14
|
+
|
|
15
15
|
from folio_migration_tools.custom_exceptions import (
|
|
16
16
|
TransformationProcessError,
|
|
17
17
|
TransformationRecordFailedError,
|
|
18
18
|
)
|
|
19
|
-
from folio_migration_tools.folder_structure import FolderStructure
|
|
20
19
|
from folio_migration_tools.helper import Helper
|
|
21
20
|
from folio_migration_tools.library_configuration import (
|
|
22
21
|
FileDefinition,
|
|
@@ -27,30 +26,171 @@ from folio_migration_tools.mapping_file_transformation.item_mapper import ItemMa
|
|
|
27
26
|
from folio_migration_tools.mapping_file_transformation.mapping_file_mapper_base import (
|
|
28
27
|
MappingFileMapperBase,
|
|
29
28
|
)
|
|
30
|
-
from folio_migration_tools.
|
|
31
|
-
from pydantic.main import BaseModel
|
|
32
|
-
|
|
29
|
+
from folio_migration_tools.marc_rules_transformation.hrid_handler import HRIDHandler
|
|
33
30
|
from folio_migration_tools.migration_tasks.migration_task_base import MigrationTaskBase
|
|
31
|
+
from folio_migration_tools.task_configuration import AbstractTaskConfiguration
|
|
34
32
|
|
|
35
33
|
csv.field_size_limit(int(ctypes.c_ulong(-1).value // 2))
|
|
36
34
|
|
|
37
35
|
|
|
38
36
|
class ItemsTransformer(MigrationTaskBase):
|
|
39
|
-
class TaskConfiguration(
|
|
40
|
-
name:
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
37
|
+
class TaskConfiguration(AbstractTaskConfiguration):
|
|
38
|
+
name: Annotated[
|
|
39
|
+
str,
|
|
40
|
+
Field(
|
|
41
|
+
title="Task name",
|
|
42
|
+
description="Name of the task.",
|
|
43
|
+
),
|
|
44
|
+
]
|
|
45
|
+
migration_task_type: Annotated[
|
|
46
|
+
str,
|
|
47
|
+
Field(
|
|
48
|
+
title="Migration task type",
|
|
49
|
+
description="Type of migration task.",
|
|
50
|
+
),
|
|
51
|
+
]
|
|
52
|
+
hrid_handling: Annotated[
|
|
53
|
+
HridHandling,
|
|
54
|
+
Field(
|
|
55
|
+
title="HRID handling",
|
|
56
|
+
description=(
|
|
57
|
+
"Determining how the HRID generation "
|
|
58
|
+
"should be handled."
|
|
59
|
+
),
|
|
60
|
+
),
|
|
61
|
+
]
|
|
62
|
+
files: Annotated[
|
|
63
|
+
List[FileDefinition],
|
|
64
|
+
Field(
|
|
65
|
+
title="Files",
|
|
66
|
+
description="List of files.",
|
|
67
|
+
),
|
|
68
|
+
]
|
|
69
|
+
items_mapping_file_name: Annotated[
|
|
70
|
+
str,
|
|
71
|
+
Field(
|
|
72
|
+
title="Items mapping file name",
|
|
73
|
+
description="File name for items mapping.",
|
|
74
|
+
),
|
|
75
|
+
]
|
|
76
|
+
location_map_file_name: Annotated[
|
|
77
|
+
str,
|
|
78
|
+
Field(
|
|
79
|
+
title="Location map file name",
|
|
80
|
+
description="File name for location map.",
|
|
81
|
+
),
|
|
82
|
+
]
|
|
83
|
+
default_call_number_type_name: Annotated[
|
|
84
|
+
str,
|
|
85
|
+
Field(
|
|
86
|
+
title="Default call number type name",
|
|
87
|
+
description="Default name for call number type.",
|
|
88
|
+
),
|
|
89
|
+
]
|
|
90
|
+
temp_location_map_file_name: Annotated[
|
|
91
|
+
Optional[str],
|
|
92
|
+
Field(
|
|
93
|
+
title="Temporary location map file name",
|
|
94
|
+
description=(
|
|
95
|
+
"Temporary file name for location map. "
|
|
96
|
+
"Empty string by default."
|
|
97
|
+
),
|
|
98
|
+
),
|
|
99
|
+
] = ""
|
|
100
|
+
material_types_map_file_name: Annotated[
|
|
101
|
+
str,
|
|
102
|
+
Field(
|
|
103
|
+
title="Material types map file name",
|
|
104
|
+
description="File name for material types map.",
|
|
105
|
+
),
|
|
106
|
+
]
|
|
107
|
+
loan_types_map_file_name: Annotated[
|
|
108
|
+
str,
|
|
109
|
+
Field(
|
|
110
|
+
title="Loan types map file name",
|
|
111
|
+
description="File name for loan types map.",
|
|
112
|
+
),
|
|
113
|
+
]
|
|
114
|
+
temp_loan_types_map_file_name: Annotated[
|
|
115
|
+
Optional[str],
|
|
116
|
+
Field(
|
|
117
|
+
title="Temporary loan types map file name",
|
|
118
|
+
description=(
|
|
119
|
+
"File name for temporary loan types map. "
|
|
120
|
+
"Empty string by default."
|
|
121
|
+
),
|
|
122
|
+
),
|
|
123
|
+
] = ""
|
|
124
|
+
statistical_codes_map_file_name: Annotated[
|
|
125
|
+
Optional[str],
|
|
126
|
+
Field(
|
|
127
|
+
title="Statistical code map file name",
|
|
128
|
+
description=(
|
|
129
|
+
"Path to the file containing the mapping of statistical codes. "
|
|
130
|
+
"The file should be in TSV format with legacy_stat_code "
|
|
131
|
+
"and folio_code columns."
|
|
132
|
+
),
|
|
133
|
+
),
|
|
134
|
+
] = ""
|
|
135
|
+
item_statuses_map_file_name: Annotated[
|
|
136
|
+
str,
|
|
137
|
+
Field(
|
|
138
|
+
title="Item statuses map file name",
|
|
139
|
+
description="File name for item statuses map.",
|
|
140
|
+
),
|
|
141
|
+
]
|
|
142
|
+
call_number_type_map_file_name: Annotated[
|
|
143
|
+
str,
|
|
144
|
+
Field(
|
|
145
|
+
title="Call number type map file name",
|
|
146
|
+
description="File name for call number type map.",
|
|
147
|
+
),
|
|
148
|
+
]
|
|
149
|
+
reset_hrid_settings: Annotated[
|
|
150
|
+
Optional[bool],
|
|
151
|
+
Field(
|
|
152
|
+
title="Reset HRID settings",
|
|
153
|
+
description=(
|
|
154
|
+
"At the end of the run "
|
|
155
|
+
"reset FOLIO with the HRID settings. "
|
|
156
|
+
"By default is False."
|
|
157
|
+
),
|
|
158
|
+
),
|
|
159
|
+
] = False
|
|
160
|
+
update_hrid_settings: Annotated[
|
|
161
|
+
bool,
|
|
162
|
+
Field(
|
|
163
|
+
title="Update HRID settings",
|
|
164
|
+
description=(
|
|
165
|
+
"At the end of the run "
|
|
166
|
+
"update FOLIO with the HRID settings. "
|
|
167
|
+
"By default is True."
|
|
168
|
+
),
|
|
169
|
+
),
|
|
170
|
+
] = True
|
|
171
|
+
boundwith_relationship_file_path: Annotated[
|
|
172
|
+
str,
|
|
173
|
+
Field(
|
|
174
|
+
title="Boundwith relationship file path",
|
|
175
|
+
description=(
|
|
176
|
+
"Path to a file outlining Boundwith relationships, "
|
|
177
|
+
"in the style of Voyager. "
|
|
178
|
+
"A TSV file with MFHD_ID and BIB_ID headers and values. "
|
|
179
|
+
"By default is empty string."
|
|
180
|
+
),
|
|
181
|
+
),
|
|
182
|
+
] = ""
|
|
183
|
+
prevent_permanent_location_map_default: Annotated[
|
|
184
|
+
bool,
|
|
185
|
+
Field(
|
|
186
|
+
title="Prevent permanent location map default",
|
|
187
|
+
description=(
|
|
188
|
+
"Prevent the default mapping of permanent location "
|
|
189
|
+
"to the default location. "
|
|
190
|
+
"By default is False."
|
|
191
|
+
),
|
|
192
|
+
),
|
|
193
|
+
] = False
|
|
54
194
|
|
|
55
195
|
@staticmethod
|
|
56
196
|
def get_object_type() -> FOLIONamespaces:
|
|
@@ -60,36 +200,28 @@ class ItemsTransformer(MigrationTaskBase):
|
|
|
60
200
|
self,
|
|
61
201
|
task_config: TaskConfiguration,
|
|
62
202
|
library_config: LibraryConfiguration,
|
|
203
|
+
folio_client,
|
|
63
204
|
use_logging: bool = True,
|
|
64
205
|
):
|
|
65
206
|
csv.register_dialect("tsv", delimiter="\t")
|
|
66
|
-
super().__init__(library_config, task_config, use_logging)
|
|
207
|
+
super().__init__(library_config, task_config, folio_client, use_logging)
|
|
67
208
|
self.task_config = task_config
|
|
68
|
-
self.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
]
|
|
73
|
-
if not any(self.files):
|
|
74
|
-
ret_str = ",".join(f.file_name for f in self.task_config.files)
|
|
75
|
-
raise TransformationProcessError(
|
|
76
|
-
"",
|
|
77
|
-
f"Files {ret_str} not found in {self.folder_structure.data_folder / 'items'}",
|
|
78
|
-
)
|
|
79
|
-
logging.info("Files to process:")
|
|
80
|
-
for filename in self.files:
|
|
81
|
-
logging.info("\t%s", filename.file_name)
|
|
82
|
-
|
|
209
|
+
self.task_configuration = self.task_config
|
|
210
|
+
self.check_source_files(
|
|
211
|
+
self.folder_structure.legacy_records_folder, self.task_config.files
|
|
212
|
+
)
|
|
83
213
|
self.total_records = 0
|
|
84
214
|
self.folio_keys = []
|
|
85
215
|
self.items_map = self.setup_records_map(
|
|
86
|
-
self.folder_structure.mapping_files_folder
|
|
87
|
-
/ self.task_config.items_mapping_file_name
|
|
216
|
+
self.folder_structure.mapping_files_folder / self.task_config.items_mapping_file_name
|
|
88
217
|
)
|
|
89
218
|
self.folio_keys = MappingFileMapperBase.get_mapped_folio_properties_from_map(
|
|
90
219
|
self.items_map
|
|
91
220
|
)
|
|
92
|
-
if
|
|
221
|
+
if (
|
|
222
|
+
any(k for k in self.folio_keys if k.startswith("statisticalCodeIds"))
|
|
223
|
+
or any(getattr(k, "statistical_code", "") for k in self.task_configuration.files)
|
|
224
|
+
):
|
|
93
225
|
statcode_mapping = self.load_ref_data_mapping_file(
|
|
94
226
|
"statisticalCodeIds",
|
|
95
227
|
self.folder_structure.mapping_files_folder
|
|
@@ -106,7 +238,8 @@ class ItemsTransformer(MigrationTaskBase):
|
|
|
106
238
|
).is_file():
|
|
107
239
|
temporary_loan_type_mapping = self.load_ref_data_mapping_file(
|
|
108
240
|
"temporaryLoanTypeId",
|
|
109
|
-
self.folder_structure.
|
|
241
|
+
self.folder_structure.mapping_files_folder
|
|
242
|
+
/ self.task_config.temp_loan_types_map_file_name,
|
|
110
243
|
self.folio_keys,
|
|
111
244
|
)
|
|
112
245
|
else:
|
|
@@ -115,20 +248,24 @@ class ItemsTransformer(MigrationTaskBase):
|
|
|
115
248
|
self.folder_structure.temp_loan_type_map_path,
|
|
116
249
|
)
|
|
117
250
|
temporary_loan_type_mapping = None
|
|
118
|
-
|
|
251
|
+
# Load Boundwith relationship map
|
|
252
|
+
self.boundwith_relationship_map = {}
|
|
253
|
+
if self.task_config.boundwith_relationship_file_path:
|
|
254
|
+
self.load_boundwith_relationships()
|
|
119
255
|
if (
|
|
120
256
|
self.folder_structure.mapping_files_folder
|
|
121
257
|
/ self.task_config.temp_location_map_file_name
|
|
122
258
|
).is_file():
|
|
123
259
|
temporary_location_mapping = self.load_ref_data_mapping_file(
|
|
124
260
|
"temporaryLocationId",
|
|
125
|
-
self.folder_structure.
|
|
261
|
+
self.folder_structure.mapping_files_folder
|
|
262
|
+
/ self.task_config.temp_location_map_file_name,
|
|
126
263
|
self.folio_keys,
|
|
127
264
|
)
|
|
128
265
|
else:
|
|
129
266
|
logging.info(
|
|
130
267
|
"%s not found. No temporary location mapping will be performed",
|
|
131
|
-
self.
|
|
268
|
+
self.task_config.temp_location_map_file_name,
|
|
132
269
|
)
|
|
133
270
|
temporary_location_mapping = None
|
|
134
271
|
self.mapper = ItemMapper(
|
|
@@ -170,42 +307,48 @@ class ItemsTransformer(MigrationTaskBase):
|
|
|
170
307
|
temporary_loan_type_mapping,
|
|
171
308
|
temporary_location_mapping,
|
|
172
309
|
self.library_configuration,
|
|
310
|
+
self.task_configuration
|
|
173
311
|
)
|
|
312
|
+
if (
|
|
313
|
+
self.task_configuration.reset_hrid_settings
|
|
314
|
+
and self.task_configuration.update_hrid_settings
|
|
315
|
+
):
|
|
316
|
+
hrid_handler = HRIDHandler(
|
|
317
|
+
self.folio_client, HridHandling.default, self.mapper.migration_report, True
|
|
318
|
+
)
|
|
319
|
+
hrid_handler.reset_item_hrid_counter()
|
|
320
|
+
|
|
174
321
|
logging.info("Init done")
|
|
175
322
|
|
|
176
323
|
def do_work(self):
|
|
177
324
|
logging.info("Starting....")
|
|
178
325
|
with open(self.folder_structure.created_objects_path, "w+") as results_file:
|
|
179
|
-
for
|
|
326
|
+
for file_def in self.task_config.files:
|
|
180
327
|
try:
|
|
181
|
-
self.process_single_file(
|
|
328
|
+
self.process_single_file(file_def, results_file)
|
|
182
329
|
except Exception as exception:
|
|
183
|
-
error_str = f"\n\nProcessing of {file_name} failed:\n{exception}."
|
|
330
|
+
error_str = f"\n\nProcessing of {file_def.file_name} failed:\n{exception}."
|
|
184
331
|
logging.exception(error_str, stack_info=True)
|
|
185
|
-
logging.fatal(
|
|
186
|
-
"Check source files for empty lines or missing reference data."
|
|
187
|
-
)
|
|
332
|
+
logging.fatal("Check source files for empty rows or missing reference data.")
|
|
188
333
|
self.mapper.migration_report.add(
|
|
189
|
-
|
|
334
|
+
"FailedFiles", f"{file_def.file_name} - {exception}"
|
|
190
335
|
)
|
|
191
336
|
logging.fatal(error_str)
|
|
192
337
|
sys.exit(1)
|
|
193
338
|
logging.info(
|
|
194
|
-
f"processed {self.total_records:,} records in {len(self.files)} files"
|
|
339
|
+
f"processed {self.total_records:,} records in {len(self.task_config.files)} files"
|
|
195
340
|
)
|
|
196
341
|
|
|
197
|
-
def process_single_file(self,
|
|
198
|
-
full_path = self.folder_structure.legacy_records_folder /
|
|
342
|
+
def process_single_file(self, file_def: FileDefinition, results_file):
|
|
343
|
+
full_path = self.folder_structure.legacy_records_folder / file_def.file_name
|
|
199
344
|
logging.info("Processing %s", full_path)
|
|
200
345
|
records_in_file = 0
|
|
201
346
|
with open(full_path, encoding="utf-8-sig") as records_file:
|
|
202
347
|
self.mapper.migration_report.add_general_statistics(
|
|
203
|
-
"Number of files processed"
|
|
348
|
+
i18n.t("Number of files processed")
|
|
204
349
|
)
|
|
205
350
|
start = time.time()
|
|
206
|
-
for idx, record in enumerate(
|
|
207
|
-
self.mapper.get_objects(records_file, full_path)
|
|
208
|
-
):
|
|
351
|
+
for idx, record in enumerate(self.mapper.get_objects(records_file, full_path)):
|
|
209
352
|
try:
|
|
210
353
|
if idx == 0:
|
|
211
354
|
logging.info("First legacy record:")
|
|
@@ -215,44 +358,54 @@ class ItemsTransformer(MigrationTaskBase):
|
|
|
215
358
|
record, f"row {idx}", FOLIONamespaces.items
|
|
216
359
|
)
|
|
217
360
|
|
|
218
|
-
self.
|
|
219
|
-
|
|
220
|
-
)
|
|
361
|
+
self.mapper.perform_additional_mappings(legacy_id, folio_rec, file_def)
|
|
362
|
+
self.handle_circulation_notes(folio_rec, self.folio_client.current_user)
|
|
221
363
|
self.handle_notes(folio_rec)
|
|
364
|
+
if folio_rec["holdingsRecordId"] in self.boundwith_relationship_map:
|
|
365
|
+
for idx_, instance_id in enumerate(
|
|
366
|
+
self.boundwith_relationship_map.get(
|
|
367
|
+
folio_rec["holdingsRecordId"]
|
|
368
|
+
)
|
|
369
|
+
):
|
|
370
|
+
if idx_ == 0:
|
|
371
|
+
bw_id = folio_rec["holdingsRecordId"]
|
|
372
|
+
else:
|
|
373
|
+
bw_id = self.mapper.generate_boundwith_holding_uuid(
|
|
374
|
+
folio_rec["holdingsRecordId"], instance_id
|
|
375
|
+
)
|
|
376
|
+
self.mapper.create_and_write_boundwith_part(legacy_id, bw_id)
|
|
222
377
|
if idx == 0:
|
|
223
378
|
logging.info("First FOLIO record:")
|
|
224
379
|
logging.info(json.dumps(folio_rec, indent=4))
|
|
225
|
-
# TODO: turn this into a
|
|
380
|
+
# TODO: turn this into a asynchronous task
|
|
226
381
|
Helper.write_to_file(results_file, folio_rec)
|
|
227
382
|
self.mapper.migration_report.add_general_statistics(
|
|
228
|
-
"Number of records written to disk"
|
|
383
|
+
i18n.t("Number of records written to disk")
|
|
229
384
|
)
|
|
230
385
|
self.mapper.report_folio_mapping(folio_rec, self.mapper.schema)
|
|
231
386
|
except TransformationProcessError as process_error:
|
|
232
387
|
self.mapper.handle_transformation_process_error(idx, process_error)
|
|
233
388
|
except TransformationRecordFailedError as data_error:
|
|
234
|
-
self.mapper.handle_transformation_record_failed_error(
|
|
235
|
-
idx, data_error
|
|
236
|
-
)
|
|
389
|
+
self.mapper.handle_transformation_record_failed_error(idx, data_error)
|
|
237
390
|
except AttributeError as attribute_error:
|
|
238
391
|
traceback.print_exc()
|
|
239
392
|
logging.fatal(attribute_error)
|
|
240
393
|
logging.info("Quitting...")
|
|
241
394
|
sys.exit(1)
|
|
242
|
-
except Exception as
|
|
243
|
-
self.mapper.handle_generic_exception(idx,
|
|
395
|
+
except Exception as exception:
|
|
396
|
+
self.mapper.handle_generic_exception(idx, exception)
|
|
244
397
|
self.mapper.migration_report.add(
|
|
245
|
-
|
|
246
|
-
|
|
398
|
+
"GeneralStatistics",
|
|
399
|
+
i18n.t("Number of Legacy items in %{container}", container=file_def),
|
|
247
400
|
)
|
|
248
401
|
self.mapper.migration_report.add_general_statistics(
|
|
249
|
-
"Number of
|
|
402
|
+
i18n.t("Number of Legacy items in total")
|
|
250
403
|
)
|
|
251
404
|
self.print_progress(idx, start)
|
|
252
405
|
records_in_file = idx + 1
|
|
253
406
|
|
|
254
407
|
logging.info(
|
|
255
|
-
f"Done processing {
|
|
408
|
+
f"Done processing {file_def} containing {records_in_file:,} records. "
|
|
256
409
|
f"Total records processed: {records_in_file:,}"
|
|
257
410
|
)
|
|
258
411
|
self.total_records += records_in_file
|
|
@@ -276,14 +429,14 @@ class ItemsTransformer(MigrationTaskBase):
|
|
|
276
429
|
del folio_object["notes"]
|
|
277
430
|
|
|
278
431
|
@staticmethod
|
|
279
|
-
def
|
|
432
|
+
def handle_circulation_notes(folio_rec, current_user_uuid):
|
|
280
433
|
if not folio_rec.get("circulationNotes", []):
|
|
281
434
|
return
|
|
282
435
|
filtered_notes = []
|
|
283
436
|
for circ_note in folio_rec.get("circulationNotes", []):
|
|
284
437
|
if circ_note.get("noteType", "") not in ["Check in", "Check out"]:
|
|
285
438
|
raise TransformationProcessError(
|
|
286
|
-
"", "Circulation Note types are not mapped
|
|
439
|
+
"", "Circulation Note types are not mapped correctly"
|
|
287
440
|
)
|
|
288
441
|
if circ_note.get("note", ""):
|
|
289
442
|
circ_note["id"] = str(uuid.uuid4())
|
|
@@ -297,20 +450,47 @@ class ItemsTransformer(MigrationTaskBase):
|
|
|
297
450
|
else:
|
|
298
451
|
del folio_rec["circulationNotes"]
|
|
299
452
|
|
|
300
|
-
def
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
453
|
+
def load_boundwith_relationships(self):
|
|
454
|
+
try:
|
|
455
|
+
with open(
|
|
456
|
+
self.folder_structure.boundwith_relationships_map_path
|
|
457
|
+
) as boundwith_relationship_file:
|
|
458
|
+
self.boundwith_relationship_map = dict(
|
|
459
|
+
json.loads(x) for x in boundwith_relationship_file
|
|
460
|
+
)
|
|
305
461
|
logging.info(
|
|
306
|
-
"
|
|
307
|
-
self.
|
|
462
|
+
"Rows in Bound with relationship map: %s",
|
|
463
|
+
len(self.boundwith_relationship_map)
|
|
464
|
+
)
|
|
465
|
+
except FileNotFoundError:
|
|
466
|
+
raise TransformationProcessError(
|
|
467
|
+
"",
|
|
468
|
+
"Boundwith relationship file specified, but relationships file "
|
|
469
|
+
"from holdings transformation not found.",
|
|
470
|
+
self.folder_structure.boundwith_relationships_map_path
|
|
471
|
+
)
|
|
472
|
+
except ValueError:
|
|
473
|
+
raise TransformationProcessError(
|
|
474
|
+
"",
|
|
475
|
+
"Boundwith relationship file specified, but relationships file "
|
|
476
|
+
"from holdings transformation is not a valid line JSON.",
|
|
477
|
+
self.folder_structure.boundwith_relationships_map_path,
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
def wrap_up(self):
|
|
481
|
+
logging.info("Done. Transformer wrapping up...")
|
|
482
|
+
self.extradata_writer.flush()
|
|
483
|
+
with open(self.folder_structure.migration_reports_file, "w") as migration_report_file:
|
|
484
|
+
self.mapper.migration_report.write_migration_report(
|
|
485
|
+
i18n.t("Item transformation report"),
|
|
486
|
+
migration_report_file,
|
|
487
|
+
self.mapper.start_datetime,
|
|
308
488
|
)
|
|
309
|
-
self.mapper.migration_report.write_migration_report(migration_report_file)
|
|
310
489
|
Helper.print_mapping_report(
|
|
311
490
|
migration_report_file,
|
|
312
491
|
self.total_records,
|
|
313
492
|
self.mapper.mapped_folio_fields,
|
|
314
493
|
self.mapper.mapped_legacy_fields,
|
|
315
494
|
)
|
|
495
|
+
self.clean_out_empty_logs()
|
|
316
496
|
logging.info("All done!")
|