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
@@ -2,274 +2,126 @@ import csv
2
2
  import json
3
3
  import logging
4
4
  import sys
5
- import uuid
6
- from datetime import datetime
7
- from typing import Dict
8
5
 
6
+ import i18n
9
7
  from dateutil.parser import parse
10
8
  from folio_uuid.folio_namespaces import FOLIONamespaces
11
9
  from folioclient import FolioClient
12
- from folio_migration_tools.custom_exceptions import TransformationProcessError
10
+
11
+ from folio_migration_tools.custom_exceptions import (
12
+ TransformationProcessError,
13
+ TransformationRecordFailedError,
14
+ )
13
15
  from folio_migration_tools.mapping_file_transformation.mapping_file_mapper_base import (
14
16
  MappingFileMapperBase,
15
17
  )
16
- from folio_migration_tools.mapping_file_transformation.mapping_file_mapping_base_impl import (
17
- MappingFileMappingBaseImpl,
18
- )
18
+ from folio_migration_tools.mapping_file_transformation.notes_mapper import NotesMapper
19
19
  from folio_migration_tools.mapping_file_transformation.ref_data_mapping import (
20
20
  RefDataMapping,
21
21
  )
22
- from folio_migration_tools.mapping_file_transformation.user_mapper_base import (
23
- UserMapperBase,
24
- )
25
- from folio_migration_tools.report_blurbs import Blurbs
26
22
 
27
23
 
28
- class UserMapper(UserMapperBase):
24
+ class UserMapper(MappingFileMapperBase):
29
25
  def __init__(
30
26
  self,
31
27
  folio_client: FolioClient,
32
28
  task_config,
33
29
  library_config,
30
+ user_map,
34
31
  departments_mapping,
35
32
  groups_map,
36
33
  ):
37
34
  try:
38
- super().__init__(folio_client, library_config)
39
-
40
- self.noteprops = None
41
- self.notes_schemas = None
42
- self.notes_mapper = None
43
- self.task_config = task_config
44
- self.folio_keys = []
45
- self.mapped_legacy_keys = []
46
- self.library_config = library_config
47
- self.user_schema = FolioClient.get_latest_from_github(
35
+ user_schema = folio_client.get_from_github(
48
36
  "folio-org", "mod-user-import", "/ramls/schemas/userdataimport.json"
49
37
  )
50
- self.ids_dict: Dict[str, set] = {}
51
- self.custom_props = {}
52
- # TODO: Use RefDataMapping
53
- if departments_mapping:
54
- self.departments_mapping = RefDataMapping(
55
- self.folio_client,
56
- "/departments",
57
- "departments",
58
- departments_mapping,
59
- "name",
60
- Blurbs.DepartmentsMapping,
61
- )
62
- else:
63
- self.departments_mapping = None
64
- if groups_map:
65
- self.groups_mapping = RefDataMapping(
66
- self.folio_client,
67
- "/groups",
68
- "usergroups",
69
- groups_map,
70
- "group",
71
- Blurbs.UserGroupMapping,
72
- )
73
- else:
74
- self.groups_mapping = None
75
- self.setup_notes_mapping()
76
- logging.info("Init done.")
77
- except TransformationProcessError as tpe:
78
- logging.critical(tpe)
79
- print(f"\n{tpe.message}")
80
- sys.exit(1)
81
-
82
- def setup_notes_mapping(self):
83
- self.notes_schemas = FolioClient.get_latest_from_github(
84
- "folio-org",
85
- "mod-notes",
86
- "src/main/resources/swagger.api/schemas/note.yaml",
87
- )
88
- notes_common = FolioClient.get_latest_from_github(
89
- "folio-org",
90
- "mod-notes",
91
- "src/main/resources/swagger.api/schemas/common.yaml",
92
- )
93
- for prop in self.notes_schemas["note"]["properties"].items():
94
- if prop[1].get("$ref", "") == "common.yaml#/uuid":
95
- prop[1]["type"] = notes_common["uuid"]["type"]
96
-
97
- for p in ["links", "metadata", "id"]:
98
- del self.notes_schemas["note"]["properties"][p]
99
38
 
100
- def map_notes(self, user_map, legacy_user, legacy_id, user_uuid: str):
101
- if self.noteprops is None:
102
- self.noteprops = {
103
- "data": [
104
- p for p in user_map["data"] if p["folio_field"].startswith("notes[")
105
- ]
106
- }
107
- logging.info(
108
- "Set %s props used for note mapping", len(self.noteprops["data"])
39
+ user_schema["properties"]["requestPreference"] = folio_client.get_from_github(
40
+ "folio-org", "mod-user-import", "/ramls/schemas/userImportRequestPreference.json"
109
41
  )
110
- if any(self.noteprops["data"]):
111
- notes_schema = self.notes_schemas["noteCollection"]
112
- notes_schema["properties"]["notes"]["items"] = self.notes_schemas["note"]
113
- notes_schema["required"] = []
114
- if self.notes_mapper is None:
115
- self.notes_mapper = MappingFileMappingBaseImpl(
116
- self.library_config,
117
- self.folio_client,
118
- notes_schema,
119
- self.noteprops,
120
- FOLIONamespaces.other,
121
- True,
122
- )
123
- logging.info("Initiated mapper for User notes")
124
- for note in self.notes_mapper.do_map(
125
- legacy_user, legacy_id, FOLIONamespaces.other
126
- )[0].get("notes", []):
127
- if note.get("content", "").strip():
128
- note["links"] = [{"id": user_uuid, "type": "user"}]
129
42
 
130
- logging.log(25, "notes\t%s", json.dumps(note))
131
- self.migration_report.add(Blurbs.MappedNoteTypes, note["typeId"])
132
- else:
133
- self.migration_report.add_general_statistics(
134
- "Notes without content that were discarded. Set some default "
135
- "value if you only intend to set the note title"
136
- )
137
-
138
- def do_map(self, legacy_user, user_map, legacy_id):
139
- if not self.folio_keys and not self.mapped_legacy_keys:
140
- self.folio_keys = (
141
- MappingFileMapperBase.get_mapped_folio_properties_from_map(user_map)
43
+ super().__init__(
44
+ folio_client,
45
+ user_schema,
46
+ user_map,
47
+ None,
48
+ FOLIONamespaces.users,
49
+ library_config,
50
+ task_config
142
51
  )
143
- self.mapped_legacy_keys = (
144
- MappingFileMapperBase.get_mapped_legacy_properties_from_map(user_map)
52
+ self.task_config = self.task_configuration
53
+ self.notes_mapper: NotesMapper = NotesMapper(
54
+ self.library_configuration,
55
+ None,
56
+ self.folio_client,
57
+ self.record_map,
58
+ FOLIONamespaces.users,
59
+ True,
145
60
  )
146
- missing_keys_in_user = [
147
- f for f in self.mapped_legacy_keys if f not in legacy_user
148
- ]
149
- if any(missing_keys_in_user):
150
- raise TransformationProcessError(
151
- "",
152
- (
153
- "There are mapped legacy fields that are not in the legacy "
154
- "user record"
155
- ),
156
- missing_keys_in_user,
157
- )
61
+ self.notes_mapper.migration_report = self.migration_report
62
+ self.departments_mapping = self.setup_departments_mapping(departments_mapping)
63
+ self.groups_mapping = self.setup_groups_mapping(groups_map)
64
+
65
+ for m in self.record_map["data"]:
66
+ if m["folio_field"].startswith("customFields"):
67
+ if "properties" not in self.schema["properties"]["customFields"]:
68
+ self.schema["properties"]["customFields"]["properties"] = {}
69
+ custom_field_prop_name = m["folio_field"].split(".")[-1]
70
+ self.schema["properties"]["customFields"]["properties"][
71
+ custom_field_prop_name
72
+ ] = {"type": "string", "description": "dynamically added custom prop"}
158
73
 
159
- if not self.custom_props:
160
- for m in user_map["data"]:
161
- if "customFields" in m["folio_field"]:
162
- sub_property = m["folio_field"].split(".")[-1]
163
- self.custom_props[sub_property] = m["legacy_field"]
164
- # TODO: Create ID-Legacy ID Mapping file!
165
- # TODO: Check for ID duplicates (barcodes, externalsystemID:s, usernames, emails?)
166
-
167
- folio_user = self.instantiate_user(legacy_id)
168
- for prop_name, prop in self.user_schema["properties"].items():
169
- self.add_prop(legacy_user, user_map, folio_user, prop_name, prop)
74
+ logging.info("Init done.")
75
+ except TransformationProcessError as tpe:
76
+ logging.critical(tpe)
77
+ print(f"\n{tpe.message}\t{tpe.data_value}")
78
+ sys.exit(1)
170
79
 
171
- folio_user["personal"]["preferredContactTypeId"] = "Email"
80
+ def perform_additional_mapping(self, legacy_user, folio_user, index_or_id):
81
+ self.notes_mapper.map_notes(
82
+ legacy_user, index_or_id, folio_user["id"], FOLIONamespaces.users
83
+ )
84
+ if "personal" not in folio_user:
85
+ folio_user["personal"] = {}
86
+ folio_user["personal"]["preferredContactTypeId"] = "email"
172
87
  folio_user["active"] = True
173
- folio_user["requestPreference"] = {
174
- "userId": folio_user["id"],
175
- "holdShelf": True,
176
- "delivery": False,
177
- "metadata": self.folio_client.get_metadata_construct(),
178
- }
179
- self.map_notes(user_map, legacy_user, legacy_id, folio_user["id"])
88
+ if folio_user.get("requestPreference"):
89
+ folio_user["requestPreference"].update(
90
+ {
91
+ "holdShelf": True,
92
+ "delivery": False,
93
+ }
94
+ )
95
+ else:
96
+ folio_user["requestPreference"] = {
97
+ "holdShelf": True,
98
+ "delivery": False,
99
+ }
100
+
180
101
  clean_folio_object = self.validate_required_properties(
181
- legacy_id, folio_user, self.user_schema, FOLIONamespaces.users
102
+ index_or_id, folio_user, self.schema, FOLIONamespaces.users
182
103
  )
104
+ if not clean_folio_object.get("personal", {}).get("lastName", ""):
105
+ raise TransformationRecordFailedError(index_or_id, "Last name is missing", "")
183
106
 
184
- self.report_folio_mapping(clean_folio_object)
185
- self.report_legacy_mapping(legacy_user)
186
- return clean_folio_object
187
-
188
- def add_prop(self, legacy_object, user_map, folio_user, prop_name, prop):
189
- if prop["type"] == "object":
190
- if "customFields" in prop_name:
191
- for k, v in self.custom_props.items():
192
- if legacy_value := legacy_object.get(v, ""):
193
- folio_user["customFields"][k] = legacy_value
194
- else:
195
- folio_user[prop_name] = {}
196
- prop_key = prop_name
197
- if "properties" in prop:
198
- for sub_prop_name, sub_prop in prop["properties"].items():
199
- sub_prop_key = f"{prop_key}.{sub_prop_name}"
200
- if "properties" in sub_prop:
201
- for sub_prop_name2, sub_prop2 in sub_prop[
202
- "properties"
203
- ].items():
204
- sub_prop_key2 = f"{sub_prop_key}.{sub_prop_name2}"
205
- if sub_prop2["type"] == "array":
206
- logging.warning(f"Array: {sub_prop_key2} ")
207
- elif sub_prop["type"] == "array":
208
- folio_user[prop_name][sub_prop_name] = []
209
- for i in range(5):
210
- if sub_prop["items"]["type"] == "object":
211
- temp = {
212
- sub_prop_name2: self.get_prop(
213
- legacy_object,
214
- user_map,
215
- f"{sub_prop_key}.{sub_prop_name2}",
216
- i,
217
- )
218
- for sub_prop_name2, sub_prop2 in sub_prop[
219
- "items"
220
- ]["properties"].items()
221
- }
107
+ if "preferredFirstName" in clean_folio_object.get(
108
+ "personal", {}
109
+ ) and not clean_folio_object.get("personal", {}).get("preferredFirstName", ""):
110
+ del clean_folio_object["personal"]["preferredFirstName"]
222
111
 
223
- if all(
224
- value == ""
225
- for key, value in temp.items()
226
- if key
227
- not in ["id", "primaryAddress", "addressTypeId"]
228
- ):
229
- continue
230
- folio_user[prop_name][sub_prop_name].append(temp)
231
- else:
232
- mkey = f"{sub_prop_key}.{sub_prop_name2}"
233
- folio_user[prop_name][
234
- sub_prop_name
235
- ] = self.get_prop(legacy_object, mkey, i)
112
+ if self.task_config.remove_id_and_request_preferences:
113
+ del clean_folio_object["id"]
114
+ del clean_folio_object["requestPreference"]
236
115
 
237
- else:
238
- folio_user[prop_name][sub_prop_name] = self.get_prop(
239
- legacy_object, user_map, sub_prop_key
240
- )
241
- if folio_user[prop_name] == {}:
242
- del folio_user[prop_name]
243
- elif prop["type"] == "array":
244
- if prop["items"]["type"] == "string":
245
- prop_names = [p for p in self.folio_keys if p.startswith(prop_name)]
246
- for idx, arr_prop_name in enumerate(prop_names):
247
- actual_prop_name = arr_prop_name.split("[")[0]
248
- if any(folio_user.get(actual_prop_name, [])):
249
- folio_user[actual_prop_name].append(
250
- self.get_prop(
251
- legacy_object, user_map, actual_prop_name, idx
252
- )
253
- )
254
- else:
255
- folio_user[actual_prop_name] = [
256
- self.get_prop(
257
- legacy_object, user_map, actual_prop_name, idx
258
- )
259
- ]
260
- if prop_name in folio_user and not any(folio_user.get(prop_name, [])):
261
- del folio_user[prop_name]
262
- else:
263
- logging.info("Edge case %s", prop_name)
264
- else:
265
- self.map_basic_props(legacy_object, user_map, prop_name, folio_user)
116
+ if self.task_config.remove_request_preferences:
117
+ del clean_folio_object["requestPreference"]
118
+ self.report_folio_mapping_no_schema(clean_folio_object)
119
+ self.report_legacy_mapping_no_schema(legacy_user)
266
120
 
267
- def map_basic_props(self, legacy_user, user_map, prop, folio_user):
268
- if self.has_property(legacy_user, user_map, prop):
269
- if temp_prop := self.get_prop(legacy_user, user_map, prop).strip():
270
- folio_user[prop] = temp_prop
121
+ return clean_folio_object
271
122
 
272
- def get_users(self, source_file, file_format: str):
123
+ @staticmethod
124
+ def get_users(source_file, file_format: str):
273
125
  csv.register_dialect("tsv", delimiter="\t")
274
126
  if file_format == "tsv":
275
127
  reader = csv.DictReader(source_file, dialect="tsv")
@@ -282,149 +134,85 @@ class UserMapper(UserMapperBase):
282
134
  )
283
135
  yield row
284
136
 
285
- def get_prop(self, legacy_user, user_map, folio_prop_name, i=0):
286
- # The value is set on the mapping. Return this instead of the default field
287
- if value := get_legacy__user_value(folio_prop_name, user_map["data"], i):
288
- self.migration_report.add(
289
- Blurbs.DefaultValuesAdded, f"{value} added to {folio_prop_name}"
290
- )
291
- return value
292
- # All other cases are mapped from legacy fields.
293
- legacy_user_keys = list(
294
- get_legacy_user_keys(folio_prop_name, user_map["data"], i)
137
+ def get_prop(self, legacy_user, folio_prop_name, index_or_id, schema_default_value):
138
+ mapped_value = super().get_prop(
139
+ legacy_user, folio_prop_name, index_or_id, schema_default_value
295
140
  )
296
- if not any(legacy_user_keys):
297
- return ""
298
-
299
- legacy_user_key = legacy_user_keys[0]
300
-
301
141
  if folio_prop_name == "personal.addresses.id":
302
142
  return ""
303
- elif folio_prop_name.split("[")[0] == "departments":
304
- if not self.departments_mapping:
305
- raise TransformationProcessError(
306
- "",
307
- "No Departments mapping set up. Set up a departments mapping file "
308
- " or remove the mapping of the Departments field",
309
- )
310
- legacy_dept = legacy_user.get(legacy_user_key, "")
311
- gid = self.get_mapped_name(
312
- self.departments_mapping,
313
- legacy_user,
314
- id,
315
- folio_prop_name.split("[")[0],
316
- True,
317
- )
318
- self.migration_report.add(
319
- Blurbs.DepartmentsMapping, f"{legacy_dept} -> {gid}"
320
- )
321
- return gid
322
143
  elif folio_prop_name == "patronGroup":
323
- legacy_group = legacy_user.get(legacy_user_key, "")
324
144
  if self.groups_mapping:
325
- gid = self.get_mapped_name(
145
+ return self.get_mapped_name(
326
146
  self.groups_mapping,
327
147
  legacy_user,
328
- id,
329
- "",
330
- True,
148
+ index_or_id,
149
+ False,
331
150
  )
332
- return gid
333
151
  else:
334
- self.migration_report.add(
335
- Blurbs.UserGroupMapping,
336
- f"{legacy_group} -> {legacy_group} (one to one)",
337
- )
338
- self.migration_report.add(Blurbs.UsersPerPatronType, legacy_group)
339
- return legacy_group
340
- elif folio_prop_name in [
341
- "expirationDate",
342
- "enrollmentDate",
343
- "personal.dateOfBirth",
344
- ]:
345
- try:
346
- if not legacy_user.get(legacy_user_key):
347
- return ""
348
- format_date = parse(legacy_user.get(legacy_user_key), fuzzy=True)
349
- fmt_string = (
350
- f"{folio_prop_name}: {legacy_user.get(legacy_user_key)}"
351
- f" -> {format_date.isoformat()}"
152
+ return mapped_value
153
+ elif folio_prop_name.startswith("departments"):
154
+ if not self.departments_mapping:
155
+ raise TransformationProcessError(
156
+ "",
157
+ "No Departments mapping set up. Set up a departments mapping file "
158
+ " or remove the mapping of the Departments field",
352
159
  )
353
- self.migration_report.add(Blurbs.DateTimeConversions, fmt_string)
354
- return format_date.isoformat()
355
- except Exception as ee:
356
- v = legacy_user.get(legacy_user_key)
357
- logging.error(f"{folio_prop_name} {v} could not be parsed: {ee}")
358
- fmt_string = (
359
- f"Parsing error! {folio_prop_name}: {v}. NOW() was returned"
160
+ if len(self.departments_mapping.mapped_legacy_keys) == 1 and self.library_configuration.multi_field_delimiter in legacy_user.get(self.departments_mapping.mapped_legacy_keys[0], ""):
161
+ split_departments = legacy_user.get(self.departments_mapping.mapped_legacy_keys[0], "").split(
162
+ self.library_configuration.multi_field_delimiter
360
163
  )
361
- self.migration_report.add(Blurbs.DateTimeConversions, fmt_string)
362
- return datetime.utcnow().isoformat()
363
- elif folio_prop_name.strip() == "personal.addresses.primaryAddress":
364
- return value
365
- elif folio_prop_name == "personal.addresses.addressTypeId":
366
- try:
367
- return user_map["addressTypes"][i]
368
- except (KeyError, IndexError):
369
- return ""
370
- elif legacy_user_keys:
371
- if len(legacy_user_keys) > 1:
372
- self.migration_report.add(
373
- Blurbs.Details, f"{legacy_user_keys} concatenated into one string"
164
+ return self.library_configuration.multi_field_delimiter.join([
165
+ self.get_mapped_name(
166
+ self.departments_mapping,
167
+ {self.departments_mapping.mapped_legacy_keys[0]: dept},
168
+ index_or_id,
169
+ True,
170
+ ) for dept in split_departments
171
+ ])
172
+ else:
173
+ return self.get_mapped_name(
174
+ self.departments_mapping,
175
+ legacy_user,
176
+ index_or_id,
177
+ True,
374
178
  )
375
- return " ".join(legacy_user.get(key, "") for key in legacy_user_keys)
376
- else:
377
- return ""
179
+ elif folio_prop_name in ["expirationDate", "enrollmentDate", "personal.dateOfBirth"]:
180
+ return self.get_parsed_date(mapped_value, folio_prop_name)
181
+ return mapped_value
378
182
 
379
- def has_property(self, user, user_map, folio_prop_name):
380
- user_key = next(
381
- (
382
- k["legacy_field"]
383
- for k in user_map["data"]
384
- if k["folio_field"].split("[")[0] == folio_prop_name
385
- ),
386
- "",
387
- )
388
- return (
389
- user_key and user_key not in ["", "Not mapped"] and user.get(user_key, "")
390
- )
391
-
392
- def legacy_property(self, user_map, folio_prop_name):
393
- if value := next(
394
- (
395
- k.get("value", "")
396
- for k in user_map["data"]
397
- if k["folio_field"] == folio_prop_name
398
- ),
399
- "",
400
- ):
401
- self.migration_report.add(
402
- Blurbs.DefaultValuesAdded, f"{value} added to {folio_prop_name}"
183
+ def get_parsed_date(self, mapped_value, folio_prop_name: str):
184
+ try:
185
+ if not mapped_value.strip():
186
+ return ""
187
+ format_date = parse(mapped_value, fuzzy=True)
188
+ return format_date.isoformat()
189
+ except Exception as ee:
190
+ v = mapped_value
191
+ logging.error(f"{folio_prop_name} {v} could not be parsed: {ee}")
192
+ fmt_string = i18n.t(
193
+ "Parsing error! %{prop_name}: %{value}. The empty string was returned",
194
+ prop_name=folio_prop_name,
195
+ value=v,
403
196
  )
404
- return value
405
- return next(
406
- k["legacy_field"]
407
- for k in user_map["data"]
408
- if k["folio_field"] == folio_prop_name
409
- )
410
-
411
-
412
- def get_legacy__user_value(folio_prop_name, data, i):
413
- return next(
414
- (
415
- k.get("value", "")
416
- for k in data
417
- if k["folio_field"].replace(f"[{i}]", "") == folio_prop_name
418
- or k["folio_field"] == folio_prop_name
419
- ),
420
- "",
421
- )
422
-
197
+ self.migration_report.add("DateTimeConversions", fmt_string)
198
+ return ""
423
199
 
424
- def get_legacy_user_keys(folio_prop_name, data, i):
425
- return (
426
- k["legacy_field"]
427
- for k in data
428
- if k["folio_field"].replace(f"[{i}]", "") == folio_prop_name
429
- and k["legacy_field"].strip()
430
- )
200
+ def setup_groups_mapping(self, groups_map):
201
+ return RefDataMapping(
202
+ self.folio_client,
203
+ "/groups",
204
+ "usergroups",
205
+ groups_map,
206
+ "group",
207
+ "UserGroupMapping",
208
+ ) if groups_map else None
209
+
210
+ def setup_departments_mapping(self, departments_mapping):
211
+ return RefDataMapping(
212
+ self.folio_client,
213
+ "/departments",
214
+ "departments",
215
+ departments_mapping,
216
+ "name",
217
+ "DepartmentsMapping",
218
+ ) if departments_mapping else None