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.
Files changed (73) hide show
  1. folio_migration_tools/__init__.py +11 -0
  2. folio_migration_tools/__main__.py +169 -85
  3. folio_migration_tools/circulation_helper.py +96 -59
  4. folio_migration_tools/config_file_load.py +66 -0
  5. folio_migration_tools/custom_dict.py +6 -4
  6. folio_migration_tools/custom_exceptions.py +21 -19
  7. folio_migration_tools/extradata_writer.py +46 -0
  8. folio_migration_tools/folder_structure.py +63 -66
  9. folio_migration_tools/helper.py +29 -21
  10. folio_migration_tools/holdings_helper.py +57 -34
  11. folio_migration_tools/i18n_config.py +9 -0
  12. folio_migration_tools/library_configuration.py +173 -13
  13. folio_migration_tools/mapper_base.py +317 -106
  14. folio_migration_tools/mapping_file_transformation/courses_mapper.py +203 -0
  15. folio_migration_tools/mapping_file_transformation/holdings_mapper.py +83 -69
  16. folio_migration_tools/mapping_file_transformation/item_mapper.py +98 -94
  17. folio_migration_tools/mapping_file_transformation/manual_fee_fines_mapper.py +352 -0
  18. folio_migration_tools/mapping_file_transformation/mapping_file_mapper_base.py +702 -223
  19. folio_migration_tools/mapping_file_transformation/notes_mapper.py +90 -0
  20. folio_migration_tools/mapping_file_transformation/order_mapper.py +492 -0
  21. folio_migration_tools/mapping_file_transformation/organization_mapper.py +389 -0
  22. folio_migration_tools/mapping_file_transformation/ref_data_mapping.py +38 -27
  23. folio_migration_tools/mapping_file_transformation/user_mapper.py +149 -361
  24. folio_migration_tools/marc_rules_transformation/conditions.py +650 -246
  25. folio_migration_tools/marc_rules_transformation/holdings_statementsparser.py +292 -130
  26. folio_migration_tools/marc_rules_transformation/hrid_handler.py +244 -0
  27. folio_migration_tools/marc_rules_transformation/loc_language_codes.xml +20846 -0
  28. folio_migration_tools/marc_rules_transformation/marc_file_processor.py +300 -0
  29. folio_migration_tools/marc_rules_transformation/marc_reader_wrapper.py +136 -0
  30. folio_migration_tools/marc_rules_transformation/rules_mapper_authorities.py +241 -0
  31. folio_migration_tools/marc_rules_transformation/rules_mapper_base.py +681 -201
  32. folio_migration_tools/marc_rules_transformation/rules_mapper_bibs.py +395 -429
  33. folio_migration_tools/marc_rules_transformation/rules_mapper_holdings.py +531 -100
  34. folio_migration_tools/migration_report.py +85 -38
  35. folio_migration_tools/migration_tasks/__init__.py +1 -3
  36. folio_migration_tools/migration_tasks/authority_transformer.py +119 -0
  37. folio_migration_tools/migration_tasks/batch_poster.py +911 -198
  38. folio_migration_tools/migration_tasks/bibs_transformer.py +121 -116
  39. folio_migration_tools/migration_tasks/courses_migrator.py +192 -0
  40. folio_migration_tools/migration_tasks/holdings_csv_transformer.py +252 -247
  41. folio_migration_tools/migration_tasks/holdings_marc_transformer.py +321 -115
  42. folio_migration_tools/migration_tasks/items_transformer.py +264 -84
  43. folio_migration_tools/migration_tasks/loans_migrator.py +506 -195
  44. folio_migration_tools/migration_tasks/manual_fee_fines_transformer.py +187 -0
  45. folio_migration_tools/migration_tasks/migration_task_base.py +364 -74
  46. folio_migration_tools/migration_tasks/orders_transformer.py +373 -0
  47. folio_migration_tools/migration_tasks/organization_transformer.py +451 -0
  48. folio_migration_tools/migration_tasks/requests_migrator.py +130 -62
  49. folio_migration_tools/migration_tasks/reserves_migrator.py +253 -0
  50. folio_migration_tools/migration_tasks/user_transformer.py +180 -139
  51. folio_migration_tools/task_configuration.py +46 -0
  52. folio_migration_tools/test_infrastructure/__init__.py +0 -0
  53. folio_migration_tools/test_infrastructure/mocked_classes.py +406 -0
  54. folio_migration_tools/transaction_migration/legacy_loan.py +148 -34
  55. folio_migration_tools/transaction_migration/legacy_request.py +65 -25
  56. folio_migration_tools/transaction_migration/legacy_reserve.py +47 -0
  57. folio_migration_tools/transaction_migration/transaction_result.py +12 -1
  58. folio_migration_tools/translations/en.json +476 -0
  59. folio_migration_tools-1.9.10.dist-info/METADATA +169 -0
  60. folio_migration_tools-1.9.10.dist-info/RECORD +67 -0
  61. {folio_migration_tools-1.2.1.dist-info → folio_migration_tools-1.9.10.dist-info}/WHEEL +1 -2
  62. folio_migration_tools-1.9.10.dist-info/entry_points.txt +3 -0
  63. folio_migration_tools/generate_schemas.py +0 -46
  64. folio_migration_tools/mapping_file_transformation/mapping_file_mapping_base_impl.py +0 -44
  65. folio_migration_tools/mapping_file_transformation/user_mapper_base.py +0 -212
  66. folio_migration_tools/marc_rules_transformation/bibs_processor.py +0 -163
  67. folio_migration_tools/marc_rules_transformation/holdings_processor.py +0 -284
  68. folio_migration_tools/report_blurbs.py +0 -219
  69. folio_migration_tools/transaction_migration/legacy_fee_fine.py +0 -36
  70. folio_migration_tools-1.2.1.dist-info/METADATA +0 -134
  71. folio_migration_tools-1.2.1.dist-info/RECORD +0 -50
  72. folio_migration_tools-1.2.1.dist-info/top_level.txt +0 -1
  73. {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 os.path import isfile
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.report_blurbs import Blurbs
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(BaseModel):
40
- name: str
41
- migration_task_type: str
42
- hrid_handling: HridHandling
43
- files: List[FileDefinition]
44
- items_mapping_file_name: str
45
- location_map_file_name: str
46
- default_call_number_type_name: str
47
- temp_location_map_file_name: Optional[str] = ""
48
- material_types_map_file_name: str
49
- loan_types_map_file_name: str
50
- temp_loan_types_map_file_name: Optional[str] = ""
51
- statistical_codes_map_file_name: Optional[str] = ""
52
- item_statuses_map_file_name: str
53
- call_number_type_map_file_name: str
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.files = [
69
- f
70
- for f in self.task_config.files
71
- if isfile(self.folder_structure.legacy_records_folder / f.file_name)
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 any(k for k in self.folio_keys if k.startswith("statisticalCodeIds")):
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.temp_loan_type_map_path,
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.temp_locations_map_path,
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.folder_structure.temp_locations_map_path,
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 file_name in self.files:
326
+ for file_def in self.task_config.files:
180
327
  try:
181
- self.process_single_file(file_name, results_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
- Blurbs.FailedFiles, f"{file_name} - {exception}"
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, file_name: FileDefinition, results_file):
198
- full_path = self.folder_structure.legacy_records_folder / file_name.file_name
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.handle_circiulation_notes(
219
- folio_rec, self.folio_client.current_user
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 asynchrounous task
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 excepion:
243
- self.mapper.handle_generic_exception(idx, excepion)
395
+ except Exception as exception:
396
+ self.mapper.handle_generic_exception(idx, exception)
244
397
  self.mapper.migration_report.add(
245
- Blurbs.GeneralStatistics,
246
- f"Number of Legacy items in {file_name}",
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 legacy items in total"
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 {file_name} containing {records_in_file:,} records. "
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 handle_circiulation_notes(folio_rec, current_user_uuid):
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 correclty"
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 wrap_up(self):
301
- logging.info("Work done. Wrapping up...")
302
- with open(
303
- self.folder_structure.migration_reports_file, "w"
304
- ) as migration_report_file:
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
- "Writing migration and mapping report to %s",
307
- self.folder_structure.migration_reports_file,
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!")