folio-migration-tools 1.9.0rc9__py3-none-any.whl → 1.9.0rc10__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.
@@ -272,7 +272,7 @@ class MapperBase:
272
272
  [
273
273
  object_type == FOLIONamespaces.instances,
274
274
  (not getattr(self.task_configuration, "data_import_marc", False)),
275
- getattr(self.task_configuration, "create_source_records", True),
275
+ getattr(self, "create_source_records", True),
276
276
  ]
277
277
  ):
278
278
  return (legacy_id, folio_record["id"], folio_record["hrid"])
@@ -3,7 +3,7 @@ import os
3
3
  import sys
4
4
  import time
5
5
  import traceback
6
- from typing import List
6
+ from typing import BinaryIO, Dict, List, Set, TextIO
7
7
 
8
8
  import i18n
9
9
  from folio_uuid.folio_namespaces import FOLIONamespaces
@@ -24,24 +24,26 @@ from folio_migration_tools.migration_report import MigrationReport
24
24
 
25
25
  class MarcFileProcessor:
26
26
  def __init__(
27
- self, mapper: RulesMapperBase, folder_structure: FolderStructure, created_objects_file
27
+ self, mapper: RulesMapperBase, folder_structure: FolderStructure, created_objects_file: TextIO
28
28
  ):
29
29
  self.object_type: FOLIONamespaces = folder_structure.object_type
30
30
  self.folder_structure: FolderStructure = folder_structure
31
31
  self.mapper: RulesMapperBase = mapper
32
- self.created_objects_file = created_objects_file
33
- if mapper.task_configuration.create_source_records:
34
- self.srs_records_file = open(self.folder_structure.srs_records_path, "w+")
32
+ self.created_objects_file: TextIO = created_objects_file
33
+ if mapper.create_source_records and any(
34
+ x.create_source_records for x in mapper.task_configuration.files
35
+ ):
36
+ self.srs_records_file: TextIO = open(self.folder_structure.srs_records_path, "w+")
35
37
  if getattr(mapper.task_configuration, "data_import_marc", False):
36
- self.data_import_marc_file = open(self.folder_structure.data_import_marc_path, "wb+")
37
- self.unique_001s: set = set()
38
+ self.data_import_marc_file: BinaryIO = open(self.folder_structure.data_import_marc_path, "wb+")
39
+ self.unique_001s: Set[str] = set()
38
40
  self.failed_records_count: int = 0
39
41
  self.records_count: int = 0
40
42
  self.start: float = time.time()
41
- self.legacy_ids: set = set()
43
+ self.legacy_ids: Set[str] = set()
42
44
  if (
43
45
  self.object_type == FOLIONamespaces.holdings
44
- and self.mapper.task_configuration.create_source_records
46
+ and self.mapper.create_source_records
45
47
  ):
46
48
  logging.info("Loading Parent HRID map for SRS creation")
47
49
  self.parent_hrids = {entity[1]: entity[2] for entity in mapper.parent_id_map.values()}
@@ -78,7 +80,7 @@ class MarcFileProcessor:
78
80
 
79
81
  if (
80
82
  file_def.create_source_records
81
- and self.mapper.task_configuration.create_source_records
83
+ and self.mapper.create_source_records
82
84
  ):
83
85
  self.save_srs_record(
84
86
  marc_record,
@@ -132,7 +134,7 @@ class MarcFileProcessor:
132
134
  def save_marc_record(
133
135
  self,
134
136
  marc_record: Record,
135
- folio_rec: dict,
137
+ folio_rec: Dict,
136
138
  object_type: FOLIONamespaces
137
139
  ):
138
140
  self.mapper.save_data_import_marc_record(
@@ -146,14 +148,10 @@ class MarcFileProcessor:
146
148
  self,
147
149
  marc_record: Record,
148
150
  file_def: FileDefinition,
149
- folio_rec,
151
+ folio_rec: Dict,
150
152
  legacy_ids: List[str],
151
153
  object_type: FOLIONamespaces,
152
154
  ):
153
- if not all(
154
- [file_def.create_source_records, self.mapper.task_configuration.create_source_records]
155
- ):
156
- return
157
155
  if object_type in [FOLIONamespaces.holdings]:
158
156
  if "008" in marc_record and len(marc_record["008"].data) > 32:
159
157
  remain, rest = (
@@ -188,7 +186,7 @@ class MarcFileProcessor:
188
186
  )
189
187
  self.mapper.migration_report.add_general_statistics(i18n.t("SRS records written to disk"))
190
188
 
191
- def add_mapped_location_code_to_record(self, marc_record, folio_rec):
189
+ def add_mapped_location_code_to_record(self, marc_record: Record, folio_rec: Dict):
192
190
  location_code = next(
193
191
  (
194
192
  location["code"]
@@ -225,9 +223,9 @@ class MarcFileProcessor:
225
223
 
226
224
  @staticmethod
227
225
  def get_valid_folio_record_ids(
228
- legacy_ids, folio_record_identifiers, migration_report: MigrationReport
229
- ):
230
- new_ids = set()
226
+ legacy_ids: List[str], folio_record_identifiers: Set[str], migration_report: MigrationReport
227
+ ) -> List[str]:
228
+ new_ids: Set[str] = set()
231
229
  for legacy_id in legacy_ids:
232
230
  if legacy_id not in folio_record_identifiers:
233
231
  new_ids.add(legacy_id)
@@ -266,12 +264,12 @@ class MarcFileProcessor:
266
264
  self.mapper.mapped_folio_fields,
267
265
  self.mapper.mapped_legacy_fields,
268
266
  )
269
- if self.mapper.task_configuration.create_source_records:
267
+ if hasattr(self, "srs_records_file"):
270
268
  self.srs_records_file.seek(0)
271
269
  if not self.srs_records_file.seek(0):
272
270
  os.remove(self.srs_records_file.name)
273
271
  self.srs_records_file.close()
274
- if getattr(self.mapper.task_configuration, "data_import_marc", False):
272
+ if hasattr(self, "data_import_marc_file"):
275
273
  self.data_import_marc_file.seek(0)
276
274
  if not self.data_import_marc_file.read(1):
277
275
  os.remove(self.data_import_marc_file.name)
@@ -281,7 +279,7 @@ class MarcFileProcessor:
281
279
  logging.info("Transformation report written to %s", report_file.name)
282
280
  logging.info("Processor is done.")
283
281
 
284
- def add_legacy_ids_to_map(self, folio_rec, filtered_legacy_ids):
282
+ def add_legacy_ids_to_map(self, folio_rec: Dict, filtered_legacy_ids: List[str]):
285
283
  for legacy_id in filtered_legacy_ids:
286
284
  self.legacy_ids.add(legacy_id)
287
285
  if legacy_id not in self.mapper.id_map:
@@ -51,6 +51,9 @@ class RulesMapperBase(MapperBase):
51
51
  self.item_json_schema = ""
52
52
  self.mappings: dict = {}
53
53
  self.schema_properties = None
54
+ self.create_source_records = all(
55
+ [self.task_configuration.create_source_records, (not getattr(self.task_configuration, "data_import_marc", False))]
56
+ )
54
57
  if hasattr(self.task_configuration, "hrid_handling"):
55
58
  self.hrid_handler = HRIDHandler(
56
59
  folio_client,
@@ -7,7 +7,7 @@ import time
7
7
  import typing
8
8
  import uuid
9
9
  from pathlib import Path
10
- from typing import Generator, List
10
+ from typing import Dict, Generator, List
11
11
 
12
12
  import i18n
13
13
  import pymarc
@@ -16,6 +16,7 @@ from folio_uuid.folio_namespaces import FOLIONamespaces
16
16
  from folio_uuid.folio_uuid import FolioUUID
17
17
  from folioclient import FolioClient
18
18
  from pymarc.record import Leader, Record
19
+ from pymarc.field import Field
19
20
 
20
21
  from folio_migration_tools.custom_exceptions import (
21
22
  TransformationProcessError,
@@ -32,6 +33,7 @@ from folio_migration_tools.marc_rules_transformation.conditions import Condition
32
33
  from folio_migration_tools.marc_rules_transformation.rules_mapper_base import (
33
34
  RulesMapperBase,
34
35
  )
36
+ from folio_migration_tools.migration_tasks.migration_task_base import MarcTaskConfigurationBase
35
37
 
36
38
 
37
39
  class BibsRulesMapper(RulesMapperBase):
@@ -40,9 +42,9 @@ class BibsRulesMapper(RulesMapperBase):
40
42
 
41
43
  def __init__(
42
44
  self,
43
- folio_client,
45
+ folio_client: FolioClient,
44
46
  library_configuration: LibraryConfiguration,
45
- task_configuration,
47
+ task_configuration: MarcTaskConfigurationBase,
46
48
  ):
47
49
  super().__init__(
48
50
  folio_client,
@@ -59,15 +61,12 @@ class BibsRulesMapper(RulesMapperBase):
59
61
  self.instance_relationships: dict = {}
60
62
  self.instance_relationship_types: dict = {}
61
63
  self.other_mode_of_issuance_id = get_unspecified_mode_of_issuance(self.folio_client)
62
- self.create_source_records = all(
63
- [self.task_configuration.create_source_records, (not getattr(self.task_configuration, "data_import_marc", False))]
64
- )
65
64
  self.data_import_marc = self.task_configuration.data_import_marc
66
65
  if self.data_import_marc:
67
66
  self.hrid_handler.deactivate035_from001 = True
68
67
  self.start = time.time()
69
68
 
70
- def perform_initial_preparation(self, marc_record: pymarc.Record, legacy_ids):
69
+ def perform_initial_preparation(self, file_def: FileDefinition, marc_record: Record, legacy_ids: List[str]):
71
70
  folio_instance = {}
72
71
  folio_instance["id"] = str(
73
72
  FolioUUID(
@@ -76,7 +75,10 @@ class BibsRulesMapper(RulesMapperBase):
76
75
  str(legacy_ids[-1]),
77
76
  )
78
77
  )
79
- if self.create_source_records or self.hrid_handler.handling == HridHandling.preserve001:
78
+ if (
79
+ all([self.create_source_records, file_def.create_source_records])
80
+ or self.hrid_handler.handling == HridHandling.preserve001
81
+ ):
80
82
  self.hrid_handler.handle_hrid(
81
83
  FOLIONamespaces.instances,
82
84
  folio_instance,
@@ -90,7 +92,7 @@ class BibsRulesMapper(RulesMapperBase):
90
92
 
91
93
  return folio_instance
92
94
 
93
- def handle_leader_05(self, marc_record, legacy_ids):
95
+ def handle_leader_05(self, marc_record: Record, legacy_ids: List[str]):
94
96
  leader_05 = marc_record.leader[5] or "Empty"
95
97
  self.migration_report.add("RecordStatus", i18n.t("Original value") + f": {leader_05}")
96
98
  if leader_05 not in ["a", "c", "d", "n", "p"]:
@@ -102,14 +104,14 @@ class BibsRulesMapper(RulesMapperBase):
102
104
  Helper.log_data_issue(legacy_ids, "d in leader. Is this correct?", marc_record.leader)
103
105
 
104
106
  def parse_record(
105
- self, marc_record: pymarc.Record, file_def: FileDefinition, legacy_ids: List[str]
107
+ self, marc_record: Record, file_def: FileDefinition, legacy_ids: List[str]
106
108
  ) -> list[dict]:
107
109
  """Parses a bib recod into a FOLIO Inventory instance object
108
110
  Community mapping suggestion: https://bit.ly/2S7Gyp3
109
111
  This is the main function
110
112
 
111
113
  Args:
112
- marc_record (pymarc.Record): _description_
114
+ marc_record (Record): _description_
113
115
  file_def (FileDefinition): _description_
114
116
  legacy_ids (List[str]): List of legacy ids in record
115
117
 
@@ -119,7 +121,7 @@ class BibsRulesMapper(RulesMapperBase):
119
121
  self.print_progress()
120
122
  ignored_subsequent_fields: set = set()
121
123
  bad_tags = set(self.task_configuration.tags_to_delete) # "907"
122
- folio_instance = self.perform_initial_preparation(marc_record, legacy_ids)
124
+ folio_instance = self.perform_initial_preparation(file_def, marc_record, legacy_ids)
123
125
  if self.data_import_marc:
124
126
  self.simple_bib_map(folio_instance, marc_record, ignored_subsequent_fields, legacy_ids)
125
127
  else:
@@ -178,7 +180,7 @@ class BibsRulesMapper(RulesMapperBase):
178
180
  legacy_ids (List[str]): _description_
179
181
  file_def (FileDefinition): _description_
180
182
  """
181
- if file_def.create_source_records and self.task_configuration.create_source_records:
183
+ if file_def.create_source_records and self.create_source_records:
182
184
  folio_instance["source"] = "MARC"
183
185
  else:
184
186
  folio_instance["source"] = "FOLIO"
@@ -198,7 +200,7 @@ class BibsRulesMapper(RulesMapperBase):
198
200
  del folio_instance["succeedingTitles"]
199
201
  self.migration_report.add("PrecedingSuccedingTitles", f"{len(succ_titles)}")
200
202
 
201
- def handle_languages(self, folio_instance, marc_record, legacy_ids):
203
+ def handle_languages(self, folio_instance: Dict, marc_record: Record, legacy_ids: List[str]):
202
204
  if "languages" in folio_instance:
203
205
  orig_languages = {lang: None for lang in folio_instance["languages"]}
204
206
  orig_languages.update(
@@ -255,7 +257,7 @@ class BibsRulesMapper(RulesMapperBase):
255
257
  if self.task_configuration.update_hrid_settings:
256
258
  self.hrid_handler.store_hrid_settings()
257
259
 
258
- def get_instance_type_id(self, marc_record, legacy_id):
260
+ def get_instance_type_id(self, marc_record: Record, legacy_ids: List[str]) -> str:
259
261
  return_id = ""
260
262
 
261
263
  def get_folio_id_by_name(f336a: str):
@@ -283,7 +285,7 @@ class BibsRulesMapper(RulesMapperBase):
283
285
  + f" ({f336a})",
284
286
  )
285
287
  Helper.log_data_issue(
286
- legacy_id,
288
+ legacy_ids,
287
289
  "instance type name (336$a) -Unsuccessful matching",
288
290
  f336a,
289
291
  )
@@ -316,7 +318,7 @@ class BibsRulesMapper(RulesMapperBase):
316
318
  ),
317
319
  )
318
320
  Helper.log_data_issue(
319
- legacy_id,
321
+ legacy_ids,
320
322
  i18n.t("instance type code (%{code}) not found in FOLIO", code="336$b"),
321
323
  f336_b,
322
324
  )
@@ -337,7 +339,7 @@ class BibsRulesMapper(RulesMapperBase):
337
339
  return_id = t[0]
338
340
  return return_id
339
341
 
340
- def get_instance_format_id_by_code(self, legacy_id: str, code: str):
342
+ def get_instance_format_id_by_code(self, legacy_ids: List[str], code: str):
341
343
  try:
342
344
  match = next(f for f in self.folio_client.instance_formats if f["code"] == code)
343
345
  self.migration_report.add(
@@ -347,14 +349,14 @@ class BibsRulesMapper(RulesMapperBase):
347
349
  return match["id"]
348
350
  except Exception:
349
351
  # TODO: Distinguish between generated codes and proper 338bs
350
- Helper.log_data_issue(legacy_id, "Instance format Code not found in FOLIO", code)
352
+ Helper.log_data_issue(legacy_ids, "Instance format Code not found in FOLIO", code)
351
353
  self.migration_report.add(
352
354
  "InstanceFormat",
353
355
  i18n.t("Code '%{code}' not found in FOLIO", code=code),
354
356
  )
355
357
  return ""
356
358
 
357
- def get_instance_format_id_by_name(self, f337a: str, f338a: str, legacy_id: str):
359
+ def get_instance_format_id_by_name(self, f337a: str, f338a: str, legacy_ids: List[str]):
358
360
  f337a = f337a.lower().strip()
359
361
  f338a = f338a.lower().strip()
360
362
  match_template = f"{f337a} -- {f338a}"
@@ -376,7 +378,7 @@ class BibsRulesMapper(RulesMapperBase):
376
378
  return match["id"]
377
379
  except Exception:
378
380
  Helper.log_data_issue(
379
- legacy_id,
381
+ legacy_ids,
380
382
  "Unsuccessful matching on 337$a and 338$a",
381
383
  match_template,
382
384
  )
@@ -391,7 +393,7 @@ class BibsRulesMapper(RulesMapperBase):
391
393
  )
392
394
  return ""
393
395
 
394
- def f338_source_is_rda_carrier(self, field: pymarc.Field):
396
+ def f338_source_is_rda_carrier(self, field: Field):
395
397
  if "2" not in field:
396
398
  self.migration_report.add(
397
399
  "InstanceFormat",
@@ -407,7 +409,7 @@ class BibsRulesMapper(RulesMapperBase):
407
409
  return False
408
410
 
409
411
  def get_instance_format_ids_from_a(
410
- self, field_index, f_338: pymarc.Field, all_337s, legacy_id
412
+ self, field_index: int, f_338: Field, all_337s: List[Field], legacy_id: List[str]
411
413
  ):
412
414
  self.migration_report.add(
413
415
  "InstanceFormat",
@@ -421,7 +423,7 @@ class BibsRulesMapper(RulesMapperBase):
421
423
  ):
422
424
  yield fmt_id
423
425
 
424
- def get_instance_format_ids(self, marc_record, legacy_id):
426
+ def get_instance_format_ids(self, marc_record: Record, legacy_id: List[str]):
425
427
  all_337s = marc_record.get_fields("337")
426
428
  all_338s = marc_record.get_fields("338")
427
429
  for fidx, f_338 in enumerate(all_338s):
@@ -466,7 +468,7 @@ class BibsRulesMapper(RulesMapperBase):
466
468
  legacy_id, combined_code
467
469
  )
468
470
 
469
- def get_mode_of_issuance_id(self, marc_record: Record, legacy_id: List[str]) -> str:
471
+ def get_mode_of_issuance_id(self, marc_record: Record, legacy_ids: List[str]) -> str:
470
472
  level = marc_record.leader[7]
471
473
  try:
472
474
  name = "unspecified"
@@ -496,7 +498,7 @@ class BibsRulesMapper(RulesMapperBase):
496
498
  return ret
497
499
  except IndexError:
498
500
  self.migration_report.add(
499
- "PossibleCleaningTasks", i18n.t("No Leader[7] in") + f" {legacy_id}"
501
+ "PossibleCleaningTasks", i18n.t("No Leader[7] in") + f" {legacy_ids}"
500
502
  )
501
503
 
502
504
  return self.other_mode_of_issuance_id
@@ -512,7 +514,7 @@ class BibsRulesMapper(RulesMapperBase):
512
514
  return "".join(marc_record["008"].data[35:38])
513
515
  return ""
514
516
 
515
- def get_languages_041(self, marc_record, legacy_id):
517
+ def get_languages_041(self, marc_record: Record, legacy_id: List[str]) -> Dict[str, None]:
516
518
  languages = dict()
517
519
  lang_fields = marc_record.get_fields("041")
518
520
  if not any(lang_fields):
@@ -543,21 +545,21 @@ class BibsRulesMapper(RulesMapperBase):
543
545
  }
544
546
  return languages
545
547
 
546
- def get_languages(self, marc_record: Record, legacy_id: str) -> List[str]:
548
+ def get_languages(self, marc_record: Record, legacy_id: List[str]) -> List[str]:
547
549
  """Get languages and tranforms them to correct codes
548
550
 
549
551
  Args:
550
- marc_record (Record): _description_
551
- legacy_id (str): _description_
552
+ marc_record (Record): A pymarc Record object
553
+ legacy_id (List[str]): A list of legacy ids from the legacy record
552
554
 
553
555
  Returns:
554
- List[str]: _description_
556
+ List[str]: List of language codes
555
557
  """
556
558
  languages = self.get_languages_041(marc_record, legacy_id)
557
559
  languages[self.get_languages_008(marc_record)] = None
558
- for lang in languages.keys():
560
+ for lang in languages:
559
561
  self.migration_report.add("LanguagesInRecords", lang)
560
- return list(languages.keys())
562
+ return list(languages)
561
563
 
562
564
  def fetch_language_codes(self) -> Generator[str, None, None]:
563
565
  """Loads the list of standardized language codes from LoC
@@ -575,7 +577,7 @@ class BibsRulesMapper(RulesMapperBase):
575
577
  yield code.text
576
578
 
577
579
  def filter_langs(
578
- self, language_values: List[str], marc_record: Record, index_or_legacy_id
580
+ self, language_values: List[str], marc_record: Record, index_or_legacy_id: List[str]
579
581
  ) -> typing.Generator:
580
582
  forbidden_values = ["###", "zxx", "n/a", "N/A", "|||"]
581
583
  for language_value in language_values:
@@ -629,7 +631,7 @@ class BibsRulesMapper(RulesMapperBase):
629
631
  else:
630
632
  raise TransformationProcessError("", f"ILS {ils_flavour} not configured")
631
633
 
632
- def get_aleph_bib_id(self, marc_record: Record):
634
+ def get_aleph_bib_id(self, marc_record: Record) -> List[str]:
633
635
  res = {f["b"].strip(): None for f in marc_record.get_fields("998") if "b" in f}
634
636
  if any(res):
635
637
  self.migration_report.add_general_statistics(
@@ -651,7 +653,7 @@ class BibsRulesMapper(RulesMapperBase):
651
653
  ) from e
652
654
 
653
655
 
654
- def get_unspecified_mode_of_issuance(folio_client: FolioClient):
656
+ def get_unspecified_mode_of_issuance(folio_client: FolioClient) -> str:
655
657
  m_o_is = list(folio_client.modes_of_issuance)
656
658
  if not any(m_o_is):
657
659
  logging.critical("No Modes of issuance set up in tenant. Quitting...")
@@ -684,7 +686,7 @@ def get_custom_bib_id(marc_record: Record, field_string: str):
684
686
  )
685
687
 
686
688
 
687
- def get_iii_bib_id(marc_record: Record):
689
+ def get_iii_bib_id(marc_record: Record) -> List[str]:
688
690
  try:
689
691
  return [marc_record["907"]["a"]]
690
692
  except Exception as e:
@@ -1,7 +1,7 @@
1
1
  import copy
2
2
  import json
3
3
  import logging
4
- from typing import List
4
+ from typing import Dict, List, Set
5
5
 
6
6
  import i18n
7
7
  from folio_uuid.folio_namespaces import FOLIONamespaces
@@ -235,10 +235,10 @@ class RulesMapperHoldings(RulesMapperBase):
235
235
 
236
236
  def process_marc_field(
237
237
  self,
238
- folio_holding: dict,
238
+ folio_holding: Dict,
239
239
  marc_field: Field,
240
- ignored_subsequent_fields,
241
- index_or_legacy_ids,
240
+ ignored_subsequent_fields: Set,
241
+ index_or_legacy_ids: List[str],
242
242
  ):
243
243
  """This overwrites the implementation for Auth and instances
244
244
 
@@ -261,7 +261,7 @@ class RulesMapperHoldings(RulesMapperBase):
261
261
  ignored_subsequent_fields.add(marc_field.tag)
262
262
 
263
263
  def perform_additional_mapping(
264
- self, marc_record: Record, folio_holding, legacy_ids: List[str], file_def: FileDefinition
264
+ self, marc_record: Record, folio_holding: Dict, legacy_ids: List[str], file_def: FileDefinition
265
265
  ):
266
266
  """_summary_
267
267
 
@@ -279,11 +279,8 @@ class RulesMapperHoldings(RulesMapperBase):
279
279
  self.pick_first_location_if_many(folio_holding, legacy_ids)
280
280
  self.parse_coded_holdings_statements(marc_record, folio_holding, legacy_ids)
281
281
  HoldingsHelper.handle_notes(folio_holding)
282
- create_source_records = all(
283
- [file_def.create_source_records, self.task_configuration.create_source_records]
284
- )
285
282
  if (
286
- create_source_records
283
+ all([file_def.create_source_records, self.create_source_records])
287
284
  or self.task_configuration.hrid_handling == HridHandling.preserve001
288
285
  ):
289
286
  self.hrid_handler.handle_hrid(
@@ -298,9 +295,9 @@ class RulesMapperHoldings(RulesMapperBase):
298
295
  "",
299
296
  )
300
297
  self.handle_suppression(folio_holding, file_def, True)
301
- self.set_source_id(self.task_configuration, folio_holding, self.holdingssources, file_def)
298
+ self.set_source_id(self.create_source_records, folio_holding, self.holdingssources, file_def)
302
299
 
303
- def pick_first_location_if_many(self, folio_holding, legacy_ids: List[str]):
300
+ def pick_first_location_if_many(self, folio_holding: Dict, legacy_ids: List[str]):
304
301
  if " " in folio_holding.get("permanentLocationId", ""):
305
302
  Helper.log_data_issue(
306
303
  legacy_ids,
@@ -312,14 +309,14 @@ class RulesMapperHoldings(RulesMapperBase):
312
309
  ]
313
310
 
314
311
  @staticmethod
315
- def set_source_id(task_configuration, folio_rec, holdingssources, file_def):
316
- if file_def.create_source_records and task_configuration.create_source_records:
312
+ def set_source_id(create_source_records: bool, folio_rec: Dict, holdingssources: Dict, file_def: FileDefinition):
313
+ if file_def.create_source_records and create_source_records:
317
314
  folio_rec["sourceId"] = holdingssources.get("MARC")
318
315
  else:
319
316
  folio_rec["sourceId"] = holdingssources.get("FOLIO")
320
317
 
321
318
  def parse_coded_holdings_statements(
322
- self, marc_record: Record, folio_holding, legacy_ids: List[str]
319
+ self, marc_record: Record, folio_holding: Dict, legacy_ids: List[str]
323
320
  ):
324
321
  # TODO: Should one be able to switch these things off?
325
322
  a = {
@@ -351,7 +348,7 @@ class RulesMapperHoldings(RulesMapperBase):
351
348
  x.create_source_records for x in self.task_configuration.files
352
349
  ]
353
350
  if all(source_file_create_source_records):
354
- create_source_records = self.task_configuration.create_source_records
351
+ create_source_records = self.create_source_records
355
352
  else:
356
353
  logging.info(
357
354
  "If all source files have create_source_records set to false, "
@@ -369,7 +366,7 @@ class RulesMapperHoldings(RulesMapperBase):
369
366
  logging.info("Fetching HoldingsRecord schema...")
370
367
  return folio_client.get_holdings_schema()
371
368
 
372
- def set_holdings_type(self, marc_record: Record, folio_holding, legacy_ids: List[str]):
369
+ def set_holdings_type(self, marc_record: Record, folio_holding: Dict, legacy_ids: List[str]):
373
370
  # Holdings type mapping
374
371
  ldr06 = marc_record.leader[6]
375
372
  # TODO: map this better
@@ -425,7 +422,7 @@ class RulesMapperHoldings(RulesMapperBase):
425
422
  ldr06,
426
423
  )
427
424
 
428
- def set_default_call_number_type_if_empty(self, folio_holding):
425
+ def set_default_call_number_type_if_empty(self, folio_holding: Dict):
429
426
  if not folio_holding.get("callNumberTypeId", ""):
430
427
  folio_holding["callNumberTypeId"] = self.conditions.default_call_number_type["id"]
431
428
 
@@ -462,7 +459,7 @@ class RulesMapperHoldings(RulesMapperBase):
462
459
  )
463
460
  return results
464
461
 
465
- def verity_boundwith_map_entry(self, entry):
462
+ def verity_boundwith_map_entry(self, entry: Dict):
466
463
  if "MFHD_ID" not in entry or not entry.get("MFHD_ID", ""):
467
464
  raise TransformationProcessError(
468
465
  "", "Column MFHD_ID missing from Boundwith relationship map", ""
@@ -472,7 +469,7 @@ class RulesMapperHoldings(RulesMapperBase):
472
469
  "", "Column BIB_ID missing from Boundwith relationship map", ""
473
470
  )
474
471
 
475
- def setup_boundwith_relationship_map(self, boundwith_relationship_map):
472
+ def setup_boundwith_relationship_map(self, boundwith_relationship_map: List[Dict]):
476
473
  """
477
474
  Creates a map of MFHD_ID to BIB_ID for boundwith relationships.
478
475
 
@@ -503,7 +500,7 @@ class RulesMapperHoldings(RulesMapperBase):
503
500
  self.handle_transformation_record_failed_error(idx, trfe)
504
501
  return new_map
505
502
 
506
- def get_bw_instance_id_map_tuple(self, entry):
503
+ def get_bw_instance_id_map_tuple(self, entry: Dict):
507
504
  try:
508
505
  return self.parent_id_map[entry["BIB_ID"]]
509
506
  except KeyError:
@@ -18,35 +18,11 @@ from folio_migration_tools.marc_rules_transformation.marc_file_processor import
18
18
  from folio_migration_tools.marc_rules_transformation.rules_mapper_bibs import (
19
19
  BibsRulesMapper,
20
20
  )
21
- from folio_migration_tools.migration_tasks.migration_task_base import MigrationTaskBase
22
- from folio_migration_tools.task_configuration import AbstractTaskConfiguration
21
+ from folio_migration_tools.migration_tasks.migration_task_base import MarcTaskConfigurationBase, MigrationTaskBase
23
22
 
24
23
 
25
24
  class BibsTransformer(MigrationTaskBase):
26
- class TaskConfiguration(AbstractTaskConfiguration):
27
- name: Annotated[
28
- str,
29
- Field(
30
- description=(
31
- "Name of this migration task. The name is being used to call the specific "
32
- "task, and to distinguish tasks of similar types"
33
- )
34
- ),
35
- ]
36
- migration_task_type: Annotated[
37
- str,
38
- Field(
39
- title="Migration task type",
40
- description=("The type of migration task you want to perform."),
41
- ),
42
- ]
43
- files: Annotated[
44
- List[FileDefinition],
45
- Field(
46
- title="Source files",
47
- description=("List of MARC21 files with bibliographic records."),
48
- ),
49
- ]
25
+ class TaskConfiguration(MarcTaskConfigurationBase):
50
26
  ils_flavour: Annotated[
51
27
  IlsFlavour,
52
28
  Field(
@@ -87,16 +63,6 @@ class BibsTransformer(MigrationTaskBase):
87
63
  ),
88
64
  ),
89
65
  ] = []
90
- create_source_records: Annotated[
91
- bool,
92
- Field(
93
- title="Create source records",
94
- description=(
95
- "Controls wheter or not to retain the MARC records in "
96
- "Source Record Storage."
97
- ),
98
- ),
99
- ] = True
100
66
  data_import_marc: Annotated[
101
67
  bool,
102
68
  Field(
@@ -107,7 +73,7 @@ class BibsTransformer(MigrationTaskBase):
107
73
  "of FOLIO instance records (and optional SRS records) will be generated."
108
74
  ),
109
75
  )
110
- ] = False
76
+ ] = True
111
77
  parse_cataloged_date: Annotated[
112
78
  bool,
113
79
  Field(
@@ -118,17 +84,6 @@ class BibsTransformer(MigrationTaskBase):
118
84
  ),
119
85
  ),
120
86
  ] = False
121
- hrid_handling: Annotated[
122
- HridHandling,
123
- Field(
124
- title="HRID Handling",
125
- description=(
126
- "Setting to default will make FOLIO generate HRIDs and move the existing "
127
- "001:s into a 035, concatenated with the 003. Choosing preserve001 means "
128
- "the 001:s will remain in place, and that they will also become the HRIDs"
129
- ),
130
- ),
131
- ] = HridHandling.default
132
87
  reset_hrid_settings: Annotated[
133
88
  bool,
134
89
  Field(
@@ -146,16 +101,6 @@ class BibsTransformer(MigrationTaskBase):
146
101
  description="At the end of the run, update FOLIO with the HRID settings",
147
102
  ),
148
103
  ] = True
149
- deactivate035_from001: Annotated[
150
- bool,
151
- Field(
152
- title="Create 035 from 001 and 003",
153
- description=(
154
- "This deactivates the FOLIO default functionality of moving the previous 001 "
155
- "into a 035, prefixed with the value from 003"
156
- ),
157
- ),
158
- ] = False
159
104
 
160
105
  @staticmethod
161
106
  def get_object_type() -> FOLIONamespaces:
@@ -19,12 +19,11 @@ from folio_migration_tools.library_configuration import (
19
19
  from folio_migration_tools.marc_rules_transformation.rules_mapper_holdings import (
20
20
  RulesMapperHoldings,
21
21
  )
22
- from folio_migration_tools.migration_tasks.migration_task_base import MigrationTaskBase
23
- from folio_migration_tools.task_configuration import AbstractTaskConfiguration
22
+ from folio_migration_tools.migration_tasks.migration_task_base import MarcTaskConfigurationBase, MigrationTaskBase
24
23
 
25
24
 
26
25
  class HoldingsMarcTransformer(MigrationTaskBase):
27
- class TaskConfiguration(AbstractTaskConfiguration):
26
+ class TaskConfiguration(MarcTaskConfigurationBase):
28
27
  name: Annotated[
29
28
  str,
30
29
  Field(
@@ -59,16 +58,6 @@ class HoldingsMarcTransformer(MigrationTaskBase):
59
58
  ),
60
59
  ),
61
60
  ] = HridHandling.default
62
- deactivate035_from001: Annotated[
63
- bool,
64
- Field(
65
- title="Create 035 from 001 and 003",
66
- description=(
67
- "This deactivates the FOLIO default functionality of moving the previous 001 "
68
- "into a 035, prefixed with the value from 003"
69
- ),
70
- ),
71
- ] = False
72
61
  holdings_type_uuid_for_boundwiths: Annotated[
73
62
  str,
74
63
  Field(
@@ -89,16 +78,6 @@ class HoldingsMarcTransformer(MigrationTaskBase):
89
78
  ),
90
79
  ),
91
80
  ] = ""
92
- create_source_records: Annotated[
93
- bool,
94
- Field(
95
- title="Create source records",
96
- description=(
97
- "Controls wheter or not to retain the MARC records in "
98
- "Source Record Storage."
99
- ),
100
- ),
101
- ] = True
102
81
  update_hrid_settings: Annotated[
103
82
  bool,
104
83
  Field(
@@ -9,11 +9,12 @@ from abc import abstractmethod
9
9
  from datetime import datetime, timezone
10
10
  from genericpath import isfile
11
11
  from pathlib import Path
12
- from typing import Optional
12
+ from typing import Annotated, List, Optional
13
13
 
14
14
  import folioclient
15
15
  from folio_uuid.folio_namespaces import FOLIONamespaces
16
16
  from folioclient import FolioClient
17
+ from pydantic import Field
17
18
 
18
19
  from folio_migration_tools import library_configuration, task_configuration
19
20
  from folio_migration_tools.custom_exceptions import (
@@ -460,6 +461,68 @@ class MigrationTaskBase:
460
461
  return None
461
462
 
462
463
 
464
+ class MarcTaskConfigurationBase(task_configuration.AbstractTaskConfiguration):
465
+ """
466
+ Base class for MARC task configurations.
467
+
468
+ Attributes:
469
+ files (List[library_configuration.FileDefinition]):
470
+ List of MARC21 files to be processed.
471
+
472
+ create_source_records (bool):
473
+ Controls whether or not to retain the MARC records in Source Record Storage.
474
+ Default is False, meaning MARC records will not be retained.
475
+
476
+ hrid_handling (library_configuration.HridHandling):
477
+ Determines how HRIDs are handled.
478
+ - 'default': FOLIO generates HRIDs and moves existing 001 fields into a 035 field, concatenated with the 003 field.
479
+ - 'preserve001': Keeps the 001 fields in place and uses them as HRIDs.
480
+ Default is 'default'.
481
+
482
+ deactivate035_from001 (bool):
483
+ Disables the default FOLIO functionality of moving the previous 001 field into a 035 field, prefixed with the value from 003.
484
+ Default is False, meaning the functionality remains active.
485
+ """
486
+
487
+ files: Annotated[
488
+ List[library_configuration.FileDefinition],
489
+ Field(
490
+ title="Source files",
491
+ description=("List of MARC21 files with bibliographic records."),
492
+ ),
493
+ ]
494
+ create_source_records: Annotated[
495
+ bool,
496
+ Field(
497
+ title="Create source records",
498
+ description=(
499
+ "Controls whether or not to retain the MARC records in "
500
+ "Source Record Storage."
501
+ ),
502
+ ),
503
+ ] = False
504
+ hrid_handling: Annotated[
505
+ library_configuration.HridHandling,
506
+ Field(
507
+ title="HRID Handling",
508
+ description=(
509
+ "Setting to default will make FOLIO generate HRIDs and move the existing "
510
+ "001:s into a 035, concatenated with the 003. Choosing preserve001 means "
511
+ "the 001:s will remain in place, and that they will also become the HRIDs"
512
+ ),
513
+ ),
514
+ ] = library_configuration.HridHandling.default
515
+ deactivate035_from001: Annotated[
516
+ bool,
517
+ Field(
518
+ title="Create 035 from 001 and 003",
519
+ description=(
520
+ "This deactivates the FOLIO default functionality of moving the previous 001 "
521
+ "into a 035, prefixed with the value from 003"
522
+ ),
523
+ ),
524
+ ] = False
525
+
463
526
  class ExcludeLevelFilter(logging.Filter):
464
527
  def __init__(self, level):
465
528
  super().__init__()
@@ -10,7 +10,24 @@ def to_camel(string):
10
10
 
11
11
 
12
12
  class AbstractTaskConfiguration(BaseModel):
13
- name: str
13
+ """Abstract class for task configuration."""
14
+
15
+ name: Annotated[
16
+ str,
17
+ Field(
18
+ description=(
19
+ "Name of this migration task. The name is being used to call the specific "
20
+ "task, and to distinguish tasks of similar types"
21
+ )
22
+ ),
23
+ ]
24
+ migration_task_type: Annotated[
25
+ str,
26
+ Field(
27
+ title="Migration task type",
28
+ description=("The type of migration task you want to perform."),
29
+ ),
30
+ ]
14
31
  ecs_tenant_id: Annotated[
15
32
  str,
16
33
  Field(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: folio_migration_tools
3
- Version: 1.9.0rc9
3
+ Version: 1.9.0rc10
4
4
  Summary: A tool allowing you to migrate data from legacy ILS:s (Library systems) into FOLIO LSP
5
5
  License: MIT
6
6
  Keywords: FOLIO,ILS,LSP,Library Systems,MARC21,Library data
@@ -11,7 +11,7 @@ folio_migration_tools/helper.py,sha256=KkOkNAGO_fuYqxdLrsbLzCJLQHUrFZG1NzD4RmpQ-
11
11
  folio_migration_tools/holdings_helper.py,sha256=yJpz6aJrKRBiJ1MtT5bs2vXAc88uJuGh2_KDuCySOKc,7559
12
12
  folio_migration_tools/i18n_config.py,sha256=3AH_2b9zTsxE4XTe4isM_zYtPJSlK0ix6eBmV7kAYUM,228
13
13
  folio_migration_tools/library_configuration.py,sha256=UhHNiz9SI2nEnm6XME2ESD33LNwqdRzIgCU9kjYPHQQ,4863
14
- folio_migration_tools/mapper_base.py,sha256=WnUA2KBJrvAWRuq7KsTPi9YXD76pXfX7lyI5pExEwLI,20139
14
+ folio_migration_tools/mapper_base.py,sha256=itmmZyPzpFl52-yOeDZvrO2WT2jxSobfhjMXRmzr3QU,20120
15
15
  folio_migration_tools/mapping_file_transformation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  folio_migration_tools/mapping_file_transformation/courses_mapper.py,sha256=RuNkdG9XumpgPO3Zvcx_JYzZ598Xle_AMNf18zLR2UM,8095
17
17
  folio_migration_tools/mapping_file_transformation/holdings_mapper.py,sha256=nJS-xx1LszvbYfw0qdTUHX9xXHlxS7wP5mYmixFMh8A,7221
@@ -28,30 +28,30 @@ folio_migration_tools/marc_rules_transformation/conditions.py,sha256=ttTZISieqve
28
28
  folio_migration_tools/marc_rules_transformation/holdings_statementsparser.py,sha256=lTb5QWEAgwyFHy5vdSK6oDl1Q5v2GnzuV04xWV3p4rc,12401
29
29
  folio_migration_tools/marc_rules_transformation/hrid_handler.py,sha256=Ihdv0_1q7gL_pZ3HWU3GcfV_jjpIfOLithWk9z_uH3Y,9997
30
30
  folio_migration_tools/marc_rules_transformation/loc_language_codes.xml,sha256=ztn2_yKws6qySL4oSsZh7sOjxq5bCC1PhAnXJdtgmJ0,382912
31
- folio_migration_tools/marc_rules_transformation/marc_file_processor.py,sha256=WkOQRDi7f4PZ5qmVH3Q-1_zdGEKYSvOGC6jixDwDp98,12349
31
+ folio_migration_tools/marc_rules_transformation/marc_file_processor.py,sha256=M-PHduzMYmZnrMwOSlwnWQ5bT-566gVRFSMo-JgS2d4,12346
32
32
  folio_migration_tools/marc_rules_transformation/marc_reader_wrapper.py,sha256=9ATjYMRAjy0QcXtmNZaHVhHLJ5hE1WUgOcF6KMJjbgo,5309
33
33
  folio_migration_tools/marc_rules_transformation/rules_mapper_authorities.py,sha256=e-wwJs8s8qEgIp8NvQgjx9lEyv7uvt08Fp6fPsy1GK8,9603
34
- folio_migration_tools/marc_rules_transformation/rules_mapper_base.py,sha256=C2jTzdXGkGjE3EWHxUh8jJeqE9tVk0qwRWVxFPZUj-Y,41223
35
- folio_migration_tools/marc_rules_transformation/rules_mapper_bibs.py,sha256=ZZHsuxlrHRcxkWPeiTjze0SahkNW_rhY3vkOQKnm1cU,28923
36
- folio_migration_tools/marc_rules_transformation/rules_mapper_holdings.py,sha256=wTZ2x8VIvCuLuNJLsbUAMCVjbMN7SS1teq0G6LAcOhU,21240
34
+ folio_migration_tools/marc_rules_transformation/rules_mapper_base.py,sha256=6EcOQSDG3wgW7S7re2uWiLkbup9Flg4ZqhtX6cCLnbk,41402
35
+ folio_migration_tools/marc_rules_transformation/rules_mapper_bibs.py,sha256=Id14IKso7hoIhrXgg8_C5o2MRVs8aUqIbDhfbDU6etI,29252
36
+ folio_migration_tools/marc_rules_transformation/rules_mapper_holdings.py,sha256=wTk8BvQlKaDipcbY5otDMJoMGixpk5MroOyhGKFdRtA,21228
37
37
  folio_migration_tools/migration_report.py,sha256=BkRspM1hwTBnWeqsHamf7yVEofzLj560Q-9G--O00hw,4258
38
38
  folio_migration_tools/migration_tasks/__init__.py,sha256=ZkbY_yGyB84Ke8OMlYUzyyBj4cxxNrhMTwQlu_GbdDs,211
39
39
  folio_migration_tools/migration_tasks/authority_transformer.py,sha256=AoXg9s-GLO3yEEDCrQV7hc4YVXxwxsdxDdpj1zhHydE,4251
40
40
  folio_migration_tools/migration_tasks/batch_poster.py,sha256=wI4lCXU5BQDbKErF6pQxT6srq_Wf_nfFAJc4f1sRCoo,36388
41
- folio_migration_tools/migration_tasks/bibs_transformer.py,sha256=XzlPo-0uuugJA4SM80xOlOj5nDK6OMDXFnAYg80hOBc,7791
41
+ folio_migration_tools/migration_tasks/bibs_transformer.py,sha256=TDVeeYr0RRp3MeqQqIUQBCidA2_wl9YwSYkIzA-h1eQ,5779
42
42
  folio_migration_tools/migration_tasks/courses_migrator.py,sha256=CzXnsu-KGP7B4zcINJzLYUqz47D16NuFfzu_DPqRlTQ,7061
43
43
  folio_migration_tools/migration_tasks/holdings_csv_transformer.py,sha256=NtysoayEIqQ8c_GNcRi6LXDYR-7OLmqFCfciMwzsyT4,21668
44
- folio_migration_tools/migration_tasks/holdings_marc_transformer.py,sha256=gL2LoXgavVQDpIH-t2vF2za04W8IjBul7MiVifuzvD8,11637
44
+ folio_migration_tools/migration_tasks/holdings_marc_transformer.py,sha256=E0IpsikXbEU8tHwIQvtMhRpIQ8Z-iZvJ_Bw5oC9W0n4,10877
45
45
  folio_migration_tools/migration_tasks/items_transformer.py,sha256=qk0sLPBxE5MtPnpLzO_gEhVVe1BqHHnpn2Zaz_vo1RY,19083
46
46
  folio_migration_tools/migration_tasks/loans_migrator.py,sha256=JE1e0i2HFzhYl05SqEkg79p9KzwSq_hPboVT9mJhgmk,34510
47
47
  folio_migration_tools/migration_tasks/manual_fee_fines_transformer.py,sha256=CnmlTge7nChUJ10EiUkriQtJlVxWqglgfhjgneh2_yM,7247
48
- folio_migration_tools/migration_tasks/migration_task_base.py,sha256=8U3g4PMexjqaAZ8K70Akylf5ayzMTMMKPze5IlibQ04,19177
48
+ folio_migration_tools/migration_tasks/migration_task_base.py,sha256=cZVPqWMuaNJYsnqiiFlyjiRSQ4trQyaSDwykDHfbkyg,21640
49
49
  folio_migration_tools/migration_tasks/orders_transformer.py,sha256=ry3oUUVQTFKCDUbGF5Zjo5ppa6AseKQwpF-wb1sb5UY,14214
50
50
  folio_migration_tools/migration_tasks/organization_transformer.py,sha256=vcCjhN1sS55c_a0LXi1Yw1eq3zpDn5E4BGbm2zDQ_Z4,16885
51
51
  folio_migration_tools/migration_tasks/requests_migrator.py,sha256=QP9OBezC3FfcKpI78oMmydxcPaUIYAgHyKevyLwC-WQ,14841
52
52
  folio_migration_tools/migration_tasks/reserves_migrator.py,sha256=SA3b7FQWHMHb7bEO8ZqOlblQ9m65zWUMH71uRk-zOKw,9950
53
53
  folio_migration_tools/migration_tasks/user_transformer.py,sha256=cNBT-wn_xx1OQXiB-vMLZmvyzkg1X562AJXUcYfThaE,12279
54
- folio_migration_tools/task_configuration.py,sha256=C5-OQtZLH7b4lVeyj5v8OXsqKNN4tzfp9F3b4vhthN4,632
54
+ folio_migration_tools/task_configuration.py,sha256=2GXVog0-_cFybqsU2WFcxnTNGDhvDzqb7gYyIimdPAk,1131
55
55
  folio_migration_tools/test_infrastructure/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
56
56
  folio_migration_tools/test_infrastructure/mocked_classes.py,sha256=trK1ZvxTdebc8qHtFtLtc-6SLlNdGDtX2z4zhP8GMcI,11278
57
57
  folio_migration_tools/transaction_migration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -60,8 +60,8 @@ folio_migration_tools/transaction_migration/legacy_request.py,sha256=1ulyFzPQw_I
60
60
  folio_migration_tools/transaction_migration/legacy_reserve.py,sha256=d0qbh2fWpwlVSYRL6wZyZG20__NAYNxh7sPSsB-LAes,1804
61
61
  folio_migration_tools/transaction_migration/transaction_result.py,sha256=cTdCN0BnlI9_ZJB2Z3Fdkl9gpymIi-9mGZsRFlQcmDk,656
62
62
  folio_migration_tools/translations/en.json,sha256=HOVpkb_T-SN_x0NpDp8gyvV1hMLCui3SsG7ByyIv0OU,38669
63
- folio_migration_tools-1.9.0rc9.dist-info/LICENSE,sha256=PhIEkitVi3ejgq56tt6sWoJIG_zmv82cjjd_aYPPGdI,1072
64
- folio_migration_tools-1.9.0rc9.dist-info/METADATA,sha256=mmS_JIsaYjr9Ep6MpDnNN-9gRPT89JgqohMuvj5LgEk,7447
65
- folio_migration_tools-1.9.0rc9.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
66
- folio_migration_tools-1.9.0rc9.dist-info/entry_points.txt,sha256=Hbe-HjqMcU8FwVshVIkeWyZd9XwgT1CCMNf06EpHQu8,77
67
- folio_migration_tools-1.9.0rc9.dist-info/RECORD,,
63
+ folio_migration_tools-1.9.0rc10.dist-info/LICENSE,sha256=PhIEkitVi3ejgq56tt6sWoJIG_zmv82cjjd_aYPPGdI,1072
64
+ folio_migration_tools-1.9.0rc10.dist-info/METADATA,sha256=K2O8fByrZ-v658N-h8dCoCcr-VafkXsKekyZirbbX-0,7448
65
+ folio_migration_tools-1.9.0rc10.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
66
+ folio_migration_tools-1.9.0rc10.dist-info/entry_points.txt,sha256=Hbe-HjqMcU8FwVshVIkeWyZd9XwgT1CCMNf06EpHQu8,77
67
+ folio_migration_tools-1.9.0rc10.dist-info/RECORD,,