folio-migration-tools 1.9.10__py3-none-any.whl → 1.10.0b1__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 (52) hide show
  1. folio_migration_tools/__init__.py +3 -4
  2. folio_migration_tools/__main__.py +44 -31
  3. folio_migration_tools/circulation_helper.py +114 -105
  4. folio_migration_tools/custom_dict.py +2 -2
  5. folio_migration_tools/custom_exceptions.py +4 -5
  6. folio_migration_tools/folder_structure.py +1 -1
  7. folio_migration_tools/helper.py +1 -1
  8. folio_migration_tools/library_configuration.py +65 -37
  9. folio_migration_tools/mapper_base.py +38 -25
  10. folio_migration_tools/mapping_file_transformation/courses_mapper.py +1 -1
  11. folio_migration_tools/mapping_file_transformation/holdings_mapper.py +7 -3
  12. folio_migration_tools/mapping_file_transformation/item_mapper.py +13 -26
  13. folio_migration_tools/mapping_file_transformation/manual_fee_fines_mapper.py +1 -2
  14. folio_migration_tools/mapping_file_transformation/mapping_file_mapper_base.py +13 -11
  15. folio_migration_tools/mapping_file_transformation/order_mapper.py +6 -5
  16. folio_migration_tools/mapping_file_transformation/organization_mapper.py +3 -3
  17. folio_migration_tools/mapping_file_transformation/user_mapper.py +43 -28
  18. folio_migration_tools/marc_rules_transformation/conditions.py +84 -70
  19. folio_migration_tools/marc_rules_transformation/holdings_statementsparser.py +13 -5
  20. folio_migration_tools/marc_rules_transformation/hrid_handler.py +3 -2
  21. folio_migration_tools/marc_rules_transformation/marc_file_processor.py +14 -22
  22. folio_migration_tools/marc_rules_transformation/rules_mapper_authorities.py +1 -0
  23. folio_migration_tools/marc_rules_transformation/rules_mapper_base.py +46 -36
  24. folio_migration_tools/marc_rules_transformation/rules_mapper_bibs.py +25 -15
  25. folio_migration_tools/marc_rules_transformation/rules_mapper_holdings.py +62 -32
  26. folio_migration_tools/migration_report.py +1 -1
  27. folio_migration_tools/migration_tasks/authority_transformer.py +1 -2
  28. folio_migration_tools/migration_tasks/batch_poster.py +78 -68
  29. folio_migration_tools/migration_tasks/bibs_transformer.py +12 -7
  30. folio_migration_tools/migration_tasks/courses_migrator.py +2 -3
  31. folio_migration_tools/migration_tasks/holdings_csv_transformer.py +14 -15
  32. folio_migration_tools/migration_tasks/holdings_marc_transformer.py +11 -21
  33. folio_migration_tools/migration_tasks/items_transformer.py +17 -30
  34. folio_migration_tools/migration_tasks/loans_migrator.py +53 -131
  35. folio_migration_tools/migration_tasks/migration_task_base.py +33 -55
  36. folio_migration_tools/migration_tasks/orders_transformer.py +21 -39
  37. folio_migration_tools/migration_tasks/organization_transformer.py +9 -18
  38. folio_migration_tools/migration_tasks/requests_migrator.py +11 -15
  39. folio_migration_tools/migration_tasks/reserves_migrator.py +1 -1
  40. folio_migration_tools/migration_tasks/user_transformer.py +10 -15
  41. folio_migration_tools/task_configuration.py +6 -7
  42. folio_migration_tools/transaction_migration/legacy_loan.py +15 -27
  43. folio_migration_tools/transaction_migration/legacy_request.py +1 -1
  44. {folio_migration_tools-1.9.10.dist-info → folio_migration_tools-1.10.0b1.dist-info}/METADATA +18 -28
  45. {folio_migration_tools-1.9.10.dist-info → folio_migration_tools-1.10.0b1.dist-info}/RECORD +47 -50
  46. folio_migration_tools-1.10.0b1.dist-info/WHEEL +4 -0
  47. folio_migration_tools-1.10.0b1.dist-info/entry_points.txt +3 -0
  48. folio_migration_tools/test_infrastructure/__init__.py +0 -0
  49. folio_migration_tools/test_infrastructure/mocked_classes.py +0 -406
  50. folio_migration_tools-1.9.10.dist-info/WHEEL +0 -4
  51. folio_migration_tools-1.9.10.dist-info/entry_points.txt +0 -3
  52. folio_migration_tools-1.9.10.dist-info/licenses/LICENSE +0 -21
@@ -1,7 +1,7 @@
1
1
  from enum import Enum
2
2
  from typing import Annotated
3
3
 
4
- from pydantic import BaseModel, Field
4
+ from pydantic import BaseModel, Field, model_validator
5
5
  from pydantic.types import DirectoryPath
6
6
 
7
7
 
@@ -25,8 +25,7 @@ class FileDefinition(BaseModel):
25
25
  Field(
26
26
  title="File name",
27
27
  description=(
28
- "Name of the file to be processed. "
29
- "The location of the file depends on the context"
28
+ "Name of the file to be processed. The location of the file depends on the context"
30
29
  ),
31
30
  ),
32
31
  ] = ""
@@ -37,10 +36,9 @@ class FileDefinition(BaseModel):
37
36
  Field(
38
37
  title="Service point ID",
39
38
  description=(
40
- "Service point to be used for "
41
- "transactions created from this file (Loans-only)."
39
+ "Service point to be used for transactions created from this file (Loans-only)."
42
40
  ),
43
- )
41
+ ),
44
42
  ] = ""
45
43
  statistical_code: Annotated[
46
44
  str,
@@ -51,7 +49,7 @@ class FileDefinition(BaseModel):
51
49
  "this file (Instances, Holdings, Items). Specify multiple codes using "
52
50
  "multi_field_delimiter."
53
51
  ),
54
- )
52
+ ),
55
53
  ] = ""
56
54
  create_source_records: Annotated[
57
55
  bool,
@@ -85,6 +83,7 @@ class FolioRelease(str, Enum):
85
83
  ramsons = "ramsons"
86
84
  sunflower = "sunflower"
87
85
  trillium = "trillium"
86
+ umbrellaleaf = "umbrellaleaf"
88
87
 
89
88
 
90
89
  class LibraryConfiguration(BaseModel):
@@ -96,7 +95,6 @@ class LibraryConfiguration(BaseModel):
96
95
  "The URL of the FOLIO API gateway instance. "
97
96
  "You can find this in Settings > Software versions > API gateway services."
98
97
  ),
99
- alias="okapi_url"
100
98
  ),
101
99
  ]
102
100
  tenant_id: Annotated[
@@ -106,7 +104,8 @@ class LibraryConfiguration(BaseModel):
106
104
  description=(
107
105
  "The ID of the FOLIO tenant instance. "
108
106
  "You can find this in Settings > Software versions > API gateway services. "
109
- "In an ECS environment, this is the ID of the central tenant, for all configurations."
107
+ "In an ECS environment, this is the ID of the central tenant, for all "
108
+ "configurations."
110
109
  ),
111
110
  ),
112
111
  ]
@@ -128,18 +127,14 @@ class LibraryConfiguration(BaseModel):
128
127
  "The username for the FOLIO user account performing the migration. "
129
128
  "User should have a full admin permissions/roles in FOLIO. "
130
129
  ),
131
- alias="okapi_username"
132
130
  ),
133
131
  ]
134
132
  folio_password: Annotated[
135
133
  str,
136
134
  Field(
137
135
  title="FOLIO API Gateway password",
138
- description=(
139
- "The password for the FOLIO user account performing the migration. "
140
- ),
141
- alias="okapi_password"
142
- )
136
+ description=("The password for the FOLIO user account performing the migration. "),
137
+ ),
143
138
  ]
144
139
  base_folder: DirectoryPath = Field(
145
140
  description=(
@@ -153,7 +148,8 @@ class LibraryConfiguration(BaseModel):
153
148
  title="Multi field delimiter",
154
149
  description=(
155
150
  "The delimiter used to separate multiple values in a single field. "
156
- "This is used for delimited text (CSV/TSV) fields with multiple sub-delimited values."
151
+ "This is used for delimited text (CSV/TSV) fields with multiple sub-delimited "
152
+ "values."
157
153
  ),
158
154
  ),
159
155
  ] = "<delimiter>"
@@ -163,36 +159,42 @@ class LibraryConfiguration(BaseModel):
163
159
  ] = 5000
164
160
  failed_percentage_threshold: Annotated[
165
161
  int,
166
- Field(
167
- description=("Percentage of failed records until the process shuts down")
168
- ),
162
+ Field(description=("Percentage of failed records until the process shuts down")),
169
163
  ] = 20
170
164
  generic_exception_threshold: Annotated[
171
165
  int,
166
+ Field(description=("Number of generic exceptions until the process shuts down")),
167
+ ] = 50
168
+ library_name: Annotated[str, Field(description="Name of the library being migrated")]
169
+ log_level_debug: Annotated[bool, Field(description="Enable debug level logging")] = False
170
+ folio_release: Annotated[
171
+ FolioRelease,
172
172
  Field(
173
- description=("Number of generic exceptions until the process shuts down")
173
+ description=(
174
+ "The Flavour of the ILS you are migrating from. This choice is "
175
+ "maninly tied to the handling of legacy identifiers and thereby the "
176
+ "deterministic UUIDs generated from them."
177
+ )
174
178
  ),
175
- ] = 50
176
- library_name: str
177
- log_level_debug: bool
178
- folio_release: FolioRelease = Field(
179
- description=(
180
- "The Flavour of the ILS you are migrating from. This choice is "
181
- "maninly tied to the handling of legacy identifiers and thereby the "
182
- "deterministic UUIDs generated from them."
183
- )
179
+ ]
180
+ iteration_identifier: Annotated[
181
+ str,
182
+ Field(
183
+ description="The name of the current directory under base_folder/iterations/ to be "
184
+ "used for this migration."
185
+ ),
186
+ ]
187
+ add_time_stamp_to_file_names: Annotated[bool, Field(title="Add time stamp to file names")] = (
188
+ False
184
189
  )
185
- iteration_identifier: str
186
- add_time_stamp_to_file_names: Annotated[
187
- bool, Field(title="Add time stamp to file names")
188
- ] = False
189
190
  use_gateway_url_for_uuids: Annotated[
190
191
  bool,
191
192
  Field(
192
193
  title="Use gateway URL for UUIDs",
193
194
  description=(
194
- "If set to true, folio_uuid will use the gateway URL when generating deterministic UUIDs for FOLIO records. "
195
- "If set to false (default), the UUIDs will be generated using the tenant_id (or ecs_tenant_id)."
195
+ "If set to true, folio_uuid will use the gateway URL when generating deterministic"
196
+ " UUIDs for FOLIO records. If set to false (default), the UUIDs will be generated"
197
+ " using the tenant_id (or ecs_tenant_id)."
196
198
  ),
197
199
  ),
198
200
  ] = False
@@ -212,8 +214,34 @@ class LibraryConfiguration(BaseModel):
212
214
  Field(
213
215
  title="ECS central iteration identifier",
214
216
  description=(
215
- "The iteration_identifier value from the central tenant configuration that corresponds "
216
- "to this configuration's iteration_identifier. Used to access the central instances_id_map."
217
+ "The iteration_identifier value from the central tenant configuration that "
218
+ "corresponds to this configuration's iteration_identifier. Used to access the "
219
+ "central instances_id_map."
217
220
  ),
218
221
  ),
219
222
  ] = ""
223
+
224
+ @model_validator(mode="before")
225
+ @classmethod
226
+ def handle_legacy_field_names(cls, values):
227
+ """Handle backward compatibility for legacy okapi field names."""
228
+ # Handle folio_password / okapi_password backward compatibility
229
+ if "folio_password" not in values and "okapi_password" in values:
230
+ values["folio_password"] = values["okapi_password"]
231
+ if "gateway_url" not in values and "okapi_url" in values:
232
+ values["gateway_url"] = values["okapi_url"]
233
+ if "folio_username" not in values and "okapi_username" in values:
234
+ values["folio_username"] = values["okapi_username"]
235
+ return values
236
+
237
+ @model_validator(mode="before")
238
+ @classmethod
239
+ def set_error_thresholds_for_debug(cls, values):
240
+ """If log_level_debug is true, set error thresholds to very high values to avoid
241
+ process shutdown during debugging.
242
+ """
243
+ if values.get("log_level_debug", False):
244
+ values["failed_records_threshold"] = 10_000_000
245
+ values["failed_percentage_threshold"] = 100
246
+ values["generic_exception_threshold"] = 10_000_000
247
+ return values
@@ -6,7 +6,7 @@ import sys
6
6
  import uuid
7
7
  from datetime import datetime, timezone
8
8
  from pathlib import Path
9
- from typing import Dict, List, Optional, Tuple, Union
9
+ from typing import Dict, List, Tuple
10
10
 
11
11
  import i18n
12
12
  from folio_uuid.folio_namespaces import FOLIONamespaces
@@ -38,10 +38,10 @@ class MapperBase:
38
38
  library_configuration: LibraryConfiguration,
39
39
  task_configuration: AbstractTaskConfiguration,
40
40
  folio_client: FolioClient,
41
- parent_id_map: Dict[str, Tuple] = {},
41
+ parent_id_map: Dict[str, Tuple] | None = None,
42
42
  ):
43
43
  logging.info("MapperBase initiating")
44
- self.parent_id_map: dict[str, tuple] = parent_id_map
44
+ self.parent_id_map: dict[str, tuple] = parent_id_map or {}
45
45
  self.extradata_writer: ExtradataWriter = ExtradataWriter(Path(""))
46
46
  self.start_datetime = datetime.now(timezone.utc)
47
47
  self.folio_client: FolioClient = folio_client
@@ -119,8 +119,8 @@ class MapperBase:
119
119
  self.migration_report.add(
120
120
  ref_data_mapping.blurb_id,
121
121
  (
122
- f'{" - ".join(fieldvalues)} '
123
- f'-> {right_mapping[f"folio_{ref_data_mapping.key_type}"]}'
122
+ f"{' - '.join(fieldvalues)} "
123
+ f"-> {right_mapping[f'folio_{ref_data_mapping.key_type}']}"
124
124
  ),
125
125
  )
126
126
  return next(v for k, v in right_mapping.items() if k.startswith("folio_"))
@@ -129,14 +129,14 @@ class MapperBase:
129
129
  if prevent_default:
130
130
  self.migration_report.add(
131
131
  ref_data_mapping.blurb_id,
132
- (f"Not to be mapped. " f'(No default) -- {" - ".join(fieldvalues)} -> ""'),
132
+ (f'Not to be mapped. (No default) -- {" - ".join(fieldvalues)} -> ""'),
133
133
  )
134
134
  return ""
135
135
  self.migration_report.add(
136
136
  ref_data_mapping.blurb_id,
137
137
  (
138
138
  f"Unmapped (Default value was set) -- "
139
- f'{" - ".join(fieldvalues)} -> {ref_data_mapping.default_name}'
139
+ f"{' - '.join(fieldvalues)} -> {ref_data_mapping.default_name}"
140
140
  ),
141
141
  )
142
142
  return ref_data_mapping.default_name
@@ -192,8 +192,8 @@ class MapperBase:
192
192
  self.migration_report.add(
193
193
  ref_data_mapping.blurb_id,
194
194
  (
195
- f'{" - ".join(fieldvalues)} '
196
- f'-> {right_mapping[f"folio_{ref_data_mapping.key_type}"]}'
195
+ f"{' - '.join(fieldvalues)} "
196
+ f"-> {right_mapping[f'folio_{ref_data_mapping.key_type}']}"
197
197
  ),
198
198
  )
199
199
  return right_mapping["folio_id"]
@@ -201,14 +201,14 @@ class MapperBase:
201
201
  if prevent_default:
202
202
  self.migration_report.add(
203
203
  ref_data_mapping.blurb_id,
204
- (f"Not to be mapped. " f'(No default) -- {" - ".join(fieldvalues)} -> ""'),
204
+ (f'Not to be mapped. (No default) -- {" - ".join(fieldvalues)} -> ""'),
205
205
  )
206
206
  return ""
207
207
  self.migration_report.add(
208
208
  ref_data_mapping.blurb_id,
209
209
  (
210
210
  f"Unmapped (Default value was set) -- "
211
- f'{" - ".join(fieldvalues)} -> {ref_data_mapping.default_name}'
211
+ f"{' - '.join(fieldvalues)} -> {ref_data_mapping.default_name}"
212
212
  ),
213
213
  )
214
214
  return ref_data_mapping.default_id
@@ -430,7 +430,7 @@ class MapperBase:
430
430
  folio_holding["id"], instance_uuid
431
431
  )
432
432
  if bound_with_holding.get("hrid", ""):
433
- bound_with_holding["hrid"] = f'{bound_with_holding["hrid"]}_bw_{bwidx}'
433
+ bound_with_holding["hrid"] = f"{bound_with_holding['hrid']}_bw_{bwidx}"
434
434
  self.migration_report.add_general_statistics(i18n.t("Bound-with holdings created"))
435
435
  yield bound_with_holding
436
436
 
@@ -443,7 +443,12 @@ class MapperBase:
443
443
  )
444
444
  )
445
445
 
446
- def map_statistical_codes(self, folio_record: dict, file_def: FileDefinition, legacy_record: Optional[Union[dict, Record]] = None):
446
+ def map_statistical_codes(
447
+ self,
448
+ folio_record: dict,
449
+ file_def: FileDefinition,
450
+ legacy_record: dict | Record | None = None,
451
+ ):
447
452
  """Map statistical codes to the folio record.
448
453
 
449
454
  This method checks if the file definition contains statistical codes and
@@ -454,13 +459,15 @@ class MapperBase:
454
459
  Args:
455
460
  folio_record (dict): The FOLIO record to which the statistical codes will be added.
456
461
  file_def (FileDefinition): The file definition containing the statistical codes.
457
- legacy_record (Optional[Union[dict, Record]]): The legacy record from which the statistical codes are derived.
458
- """
462
+ legacy_record (dict | Record | None): The legacy record from which the statistical codes are derived.
463
+ """ # noqa: E501
459
464
  if file_def.statistical_code:
460
465
  code_strings = file_def.statistical_code.split(
461
466
  self.library_configuration.multi_field_delimiter
462
467
  )
463
- folio_record["statisticalCodeIds"] = folio_record.get("statisticalCodeIds", []) + code_strings
468
+ folio_record["statisticalCodeIds"] = (
469
+ folio_record.get("statisticalCodeIds", []) + code_strings
470
+ )
464
471
 
465
472
  def setup_statistical_codes_map(self, statistical_codes_map):
466
473
  if statistical_codes_map:
@@ -472,7 +479,9 @@ class MapperBase:
472
479
  "code",
473
480
  "StatisticalCodeMapping",
474
481
  )
475
- logging.info(f"Statistical codes mapping set up {self.statistical_codes_mapping.mapped_legacy_keys}")
482
+ logging.info(
483
+ f"Statistical codes mapping set up {self.statistical_codes_mapping.mapped_legacy_keys}" # noqa: E501
484
+ )
476
485
  else:
477
486
  self.statistical_codes_mapping = None
478
487
  logging.info("Statistical codes map is not set up")
@@ -492,13 +501,13 @@ class MapperBase:
492
501
  )
493
502
  return ""
494
503
 
495
- def map_statistical_code_ids(
496
- self, legacy_ids, folio_record: dict
497
- ):
498
- if stat_codes := {x: None for x in folio_record.pop("statisticalCodeIds", [])}:
504
+ def map_statistical_code_ids(self, legacy_ids, folio_record: dict):
505
+ if stat_codes := dict.fromkeys(folio_record.pop("statisticalCodeIds", [])):
499
506
  folio_code_ids = set()
500
507
  for stat_code in stat_codes:
501
- if stat_code_id := self.get_statistical_code({"legacy_stat_code": stat_code}, "statisticalCodeId", legacy_ids):
508
+ if stat_code_id := self.get_statistical_code(
509
+ {"legacy_stat_code": stat_code}, "statisticalCodeId", legacy_ids
510
+ ):
502
511
  folio_code_ids.add(stat_code_id)
503
512
  else:
504
513
  Helper.log_data_issue(
@@ -513,7 +522,10 @@ class MapperBase:
513
522
 
514
523
  @property
515
524
  def base_string_for_folio_uuid(self):
516
- if self.library_configuration.use_gateway_url_for_uuids and not self.library_configuration.is_ecs:
525
+ if (
526
+ self.library_configuration.use_gateway_url_for_uuids
527
+ and not self.library_configuration.is_ecs
528
+ ):
517
529
  return str(self.folio_client.gateway_url)
518
530
  elif self.library_configuration.ecs_tenant_id:
519
531
  return str(self.library_configuration.ecs_tenant_id)
@@ -522,8 +534,8 @@ class MapperBase:
522
534
 
523
535
  @staticmethod
524
536
  def validate_location_map(location_map: List[Dict], locations: List[Dict]) -> List[Dict]:
525
- mapped_codes = [x['folio_code'] for x in location_map]
526
- existing_codes = [x['code'] for x in locations]
537
+ mapped_codes = [x["folio_code"] for x in location_map]
538
+ existing_codes = [x["code"] for x in locations]
527
539
  missing_codes = set(mapped_codes) - set(existing_codes)
528
540
  if missing_codes:
529
541
  raise TransformationProcessError(
@@ -537,6 +549,7 @@ class MapperBase:
537
549
  def get_object_type() -> FOLIONamespaces:
538
550
  raise NotImplementedError("This method should be overridden in subclasses")
539
551
 
552
+
540
553
  def flatten(my_dict: dict, path=""):
541
554
  for k, v in iter(my_dict.items()):
542
555
  if not path:
@@ -45,7 +45,7 @@ class CoursesMapper(MappingFileMapperBase):
45
45
  None,
46
46
  FOLIONamespaces.course,
47
47
  library_configuration,
48
- task_configuration
48
+ task_configuration,
49
49
  )
50
50
  self.course_map = course_map
51
51
  if terms_map:
@@ -6,7 +6,10 @@ import i18n
6
6
  from folio_uuid.folio_uuid import FOLIONamespaces
7
7
  from folioclient import FolioClient
8
8
 
9
- from folio_migration_tools.custom_exceptions import TransformationProcessError, TransformationRecordFailedError
9
+ from folio_migration_tools.custom_exceptions import (
10
+ TransformationProcessError,
11
+ TransformationRecordFailedError,
12
+ )
10
13
  from folio_migration_tools.library_configuration import (
11
14
  FileDefinition,
12
15
  LibraryConfiguration,
@@ -19,6 +22,7 @@ from folio_migration_tools.mapping_file_transformation.ref_data_mapping import (
19
22
  )
20
23
  from folio_migration_tools.task_configuration import AbstractTaskConfiguration
21
24
 
25
+
22
26
  class HoldingsMapper(MappingFileMapperBase):
23
27
  def __init__(
24
28
  self,
@@ -40,7 +44,7 @@ class HoldingsMapper(MappingFileMapperBase):
40
44
  statistical_codes_map,
41
45
  FOLIONamespaces.holdings,
42
46
  library_configuration,
43
- task_config
47
+ task_config,
44
48
  )
45
49
  self.holdings_map = holdings_map
46
50
 
@@ -86,7 +90,7 @@ class HoldingsMapper(MappingFileMapperBase):
86
90
  folio_record["discoverySuppress"] = file_def.discovery_suppressed
87
91
  self.migration_report.add(
88
92
  "Suppression",
89
- i18n.t("Suppressed from discovery") + f' = {folio_record["discoverySuppress"]}',
93
+ i18n.t("Suppressed from discovery") + f" = {folio_record['discoverySuppress']}",
90
94
  )
91
95
 
92
96
  def get_prop(self, legacy_item, folio_prop_name, index_or_id, schema_default_value):
@@ -2,7 +2,7 @@ import json
2
2
  import logging
3
3
  import sys
4
4
  from datetime import datetime, timezone
5
- from typing import Dict, List, Set, Union
5
+ from typing import Dict, List, Set
6
6
  from uuid import uuid4
7
7
 
8
8
  import i18n
@@ -117,7 +117,9 @@ class ItemMapper(MappingFileMapperBase):
117
117
  "LocationMapping",
118
118
  )
119
119
 
120
- def perform_additional_mappings(self, legacy_ids: Union[str, List[str]], folio_rec: Dict, file_def: FileDefinition):
120
+ def perform_additional_mappings(
121
+ self, legacy_ids: List[str] | str, folio_rec: Dict, file_def: FileDefinition
122
+ ):
121
123
  self.handle_suppression(folio_rec, file_def)
122
124
  self.map_statistical_codes(folio_rec, file_def)
123
125
  self.map_statistical_code_ids(legacy_ids, folio_rec)
@@ -126,24 +128,17 @@ class ItemMapper(MappingFileMapperBase):
126
128
  folio_record["discoverySuppress"] = file_def.discovery_suppressed
127
129
  self.migration_report.add(
128
130
  "Suppression",
129
- i18n.t("Suppressed from discovery")
130
- + f" = {folio_record['discoverySuppress']}",
131
+ i18n.t("Suppressed from discovery") + f" = {folio_record['discoverySuppress']}",
131
132
  )
132
133
 
133
134
  def setup_status_mapping(self, item_statuses_map):
134
- statuses = self.item_schema["properties"]["status"]["properties"]["name"][
135
- "enum"
136
- ]
135
+ statuses = self.item_schema["properties"]["status"]["properties"]["name"]["enum"]
137
136
  for mapping in item_statuses_map:
138
137
  if "folio_name" not in mapping:
139
- logging.critical(
140
- "folio_name is not a column in the status mapping file"
141
- )
138
+ logging.critical("folio_name is not a column in the status mapping file")
142
139
  sys.exit(1)
143
140
  elif "legacy_code" not in mapping:
144
- logging.critical(
145
- "legacy_code is not a column in the status mapping file"
146
- )
141
+ logging.critical("legacy_code is not a column in the status mapping file")
147
142
  sys.exit(1)
148
143
  elif mapping["folio_name"] not in statuses:
149
144
  logging.critical(
@@ -158,9 +153,7 @@ class ItemMapper(MappingFileMapperBase):
158
153
  )
159
154
  sys.exit(1)
160
155
  elif not all(mapping.values()):
161
- logging.critical(
162
- "empty value in mapping %s. Check mapping file", mapping.values()
163
- )
156
+ logging.critical("empty value in mapping %s. Check mapping file", mapping.values())
164
157
  sys.exit(1)
165
158
  else:
166
159
  self.status_mapping = {
@@ -168,7 +161,7 @@ class ItemMapper(MappingFileMapperBase):
168
161
  }
169
162
  logging.info(json.dumps(statuses, indent=True))
170
163
 
171
- def get_prop(self, legacy_item, folio_prop_name, index_or_id, schema_default_value):
164
+ def get_prop(self, legacy_item, folio_prop_name, index_or_id, schema_default_value): # noqa: C901
172
165
  if folio_prop_name == "permanentLocationId":
173
166
  return self.get_mapped_ref_data_value(
174
167
  self.location_mapping,
@@ -213,9 +206,7 @@ class ItemMapper(MappingFileMapperBase):
213
206
  index_or_id,
214
207
  True,
215
208
  )
216
- self.migration_report.add(
217
- "TemporaryLoanTypeMapping", f"{folio_prop_name} -> {ltid}"
218
- )
209
+ self.migration_report.add("TemporaryLoanTypeMapping", f"{folio_prop_name} -> {ltid}")
219
210
  return ltid
220
211
  elif folio_prop_name == "permanentLoanTypeId":
221
212
  return self.get_mapped_ref_data_value(
@@ -232,9 +223,7 @@ class ItemMapper(MappingFileMapperBase):
232
223
  normalized_barcode = barcode.strip().lower()
233
224
  if normalized_barcode and normalized_barcode in self.unique_barcodes:
234
225
  Helper.log_data_issue(index_or_id, "Duplicate barcode", mapped_value)
235
- self.migration_report.add_general_statistics(
236
- i18n.t("Duplicate barcodes")
237
- )
226
+ self.migration_report.add_general_statistics(i18n.t("Duplicate barcodes"))
238
227
  return f"{barcode}-{uuid4()}"
239
228
  else:
240
229
  if normalized_barcode:
@@ -259,9 +248,7 @@ class ItemMapper(MappingFileMapperBase):
259
248
  self.migration_report.add("UnmappedProperties", f"{folio_prop_name}")
260
249
  return ""
261
250
 
262
- def get_item_level_call_number_type_id(
263
- self, legacy_item, folio_prop_name: str, index_or_id
264
- ):
251
+ def get_item_level_call_number_type_id(self, legacy_item, folio_prop_name: str, index_or_id):
265
252
  if self.call_number_mapping:
266
253
  return self.get_mapped_ref_data_value(
267
254
  self.call_number_mapping, legacy_item, index_or_id, folio_prop_name
@@ -172,8 +172,7 @@ class ManualFeeFinesMapper(MappingFileMapperBase):
172
172
  except TypeError as te:
173
173
  raise TransformationProcessError(
174
174
  "",
175
- "Failed to fetch Tenant Locale Settings. "
176
- "Is your library configuration correct?",
175
+ "Failed to fetch Tenant Locale Settings. Is your library configuration correct?",
177
176
  ) from te
178
177
  except KeyError as ke:
179
178
  raise TransformationProcessError(
@@ -133,7 +133,7 @@ class MappingFileMapperBase(MapperBase):
133
133
  raise TransformationProcessError(
134
134
  "",
135
135
  f"property legacyIdentifier not setup in map: "
136
- f"{field_map.get('legacyIdentifier', '') ({exception})}",
136
+ f"{field_map.get('legacyIdentifier', '')({exception})}",
137
137
  ) from exception
138
138
  del field_map["legacyIdentifier"]
139
139
  return field_map
@@ -213,12 +213,10 @@ class MappingFileMapperBase(MapperBase):
213
213
  }
214
214
  )
215
215
  if object_type == FOLIONamespaces.holdings and hasattr(self, "holdings_sources"):
216
- folio_object['sourceId'] = self.holdings_sources.get("FOLIO")
216
+ folio_object["sourceId"] = self.holdings_sources.get("FOLIO")
217
217
  elif object_type == FOLIONamespaces.holdings and not hasattr(self, "holdings_sources"):
218
218
  raise TransformationProcessError(
219
- index_or_id,
220
- "Holdings source not set in the mapper",
221
- None
219
+ index_or_id, "Holdings source not set in the mapper", None
222
220
  )
223
221
  return folio_object, legacy_id
224
222
 
@@ -400,7 +398,7 @@ class MappingFileMapperBase(MapperBase):
400
398
  value = replaced_val
401
399
  if value and mapping_file_entry.get("rules", {}).get("regexGetFirstMatchOrEmpty", ""):
402
400
  my_pattern = (
403
- f'{mapping_file_entry.get("rules", {}).get("regexGetFirstMatchOrEmpty")}|$'
401
+ f"{mapping_file_entry.get('rules', {}).get('regexGetFirstMatchOrEmpty')}|$"
404
402
  )
405
403
  value = re.findall(my_pattern, value)[0]
406
404
  if not value and mapping_file_entry.get("fallback_legacy_field", ""):
@@ -498,7 +496,7 @@ class MappingFileMapperBase(MapperBase):
498
496
  set_deep(folio_object, schema_property_name, temp_object)
499
497
  # folio_object[schema_property_name] = temp_object
500
498
 
501
- def map_objects_array_props(
499
+ def map_objects_array_props( # noqa: C901
502
500
  self,
503
501
  legacy_object,
504
502
  prop_name: str,
@@ -553,7 +551,9 @@ class MappingFileMapperBase(MapperBase):
553
551
  )
554
552
  multi_field_props.append(sub_prop_name)
555
553
  else:
556
- self.validate_enums(res, sub_prop, sub_prop_name, index_or_id, required)
554
+ self.validate_enums(
555
+ res, sub_prop, sub_prop_name, index_or_id, required
556
+ )
557
557
 
558
558
  if res or isinstance(res, bool):
559
559
  temp_object[sub_prop_name] = res
@@ -619,8 +619,8 @@ class MappingFileMapperBase(MapperBase):
619
619
  @staticmethod
620
620
  def split_obj_by_delim(delimiter: str, folio_obj: dict, delimited_props: List[str]):
621
621
  non_split_props = [(k, v) for k, v in folio_obj.items() if k not in delimited_props]
622
- delimited_props = map(lambda x: [x, *folio_obj[x].split(delimiter)], delimited_props)
623
- zipped = list(zip(*delimited_props))
622
+ delimited_props = ([x, *folio_obj[x].split(delimiter)] for x in delimited_props)
623
+ zipped = list(zip(*delimited_props, strict=False))
624
624
  res = []
625
625
  for (prop_name_idx, prop_name), (value_idx, ra) in itertools.product(
626
626
  enumerate(zipped[0]), enumerate(zipped[1:])
@@ -973,4 +973,6 @@ def in_deep(dictionary, keys):
973
973
 
974
974
 
975
975
  def is_set_or_bool_or_numeric(any_value):
976
- return (isinstance(any_value, str) and (any_value.strip() not in empty_vals)) or isinstance(any_value, (int, float, complex))
976
+ return (isinstance(any_value, str) and (any_value.strip() not in empty_vals)) or isinstance(
977
+ any_value, (int, float, complex)
978
+ )
@@ -25,7 +25,6 @@ from folio_migration_tools.mapping_file_transformation.ref_data_mapping import (
25
25
 
26
26
 
27
27
  class CompositeOrderMapper(MappingFileMapperBase):
28
-
29
28
  def __init__(
30
29
  self,
31
30
  folio_client: FolioClient,
@@ -291,7 +290,9 @@ class CompositeOrderMapper(MappingFileMapperBase):
291
290
  ):
292
291
  object_schema["properties"] = CompositeOrderMapper.inject_schema_by_ref(
293
292
  submodule_path, github_headers, object_schema
294
- ).get("properties", {})#TODO: Investigate new CustomFields schema and figure out how to actually handle it
293
+ ).get(
294
+ "properties", {}
295
+ ) # TODO: Investigate new CustomFields schema and figure out how to actually handle it # noqa: E501
295
296
 
296
297
  for property_name_level1, property_level1 in object_schema.get(
297
298
  "properties", {}
@@ -400,9 +401,9 @@ class CompositeOrderMapper(MappingFileMapperBase):
400
401
  return composite_order
401
402
 
402
403
  def validate_po_number(
403
- self,
404
- index_or_id: str,
405
- po_number: str,
404
+ self,
405
+ index_or_id: str,
406
+ po_number: str,
406
407
  ):
407
408
  if not self.is_valid_po_number(po_number):
408
409
  self.migration_report.add(
@@ -330,9 +330,9 @@ class OrganizationMapper(MappingFileMapperBase):
330
330
  ["username", "password", "interfaceId"],
331
331
  )
332
332
 
333
- interface_schema["properties"][
334
- "interfaceCredential"
335
- ] = interface_credential_schema
333
+ interface_schema["properties"]["interfaceCredential"] = (
334
+ interface_credential_schema
335
+ )
336
336
 
337
337
  property_level1["items"] = interface_schema
338
338