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.
- folio_migration_tools/__init__.py +3 -4
- folio_migration_tools/__main__.py +44 -31
- folio_migration_tools/circulation_helper.py +114 -105
- folio_migration_tools/custom_dict.py +2 -2
- folio_migration_tools/custom_exceptions.py +4 -5
- folio_migration_tools/folder_structure.py +1 -1
- folio_migration_tools/helper.py +1 -1
- folio_migration_tools/library_configuration.py +65 -37
- folio_migration_tools/mapper_base.py +38 -25
- folio_migration_tools/mapping_file_transformation/courses_mapper.py +1 -1
- folio_migration_tools/mapping_file_transformation/holdings_mapper.py +7 -3
- folio_migration_tools/mapping_file_transformation/item_mapper.py +13 -26
- folio_migration_tools/mapping_file_transformation/manual_fee_fines_mapper.py +1 -2
- folio_migration_tools/mapping_file_transformation/mapping_file_mapper_base.py +13 -11
- folio_migration_tools/mapping_file_transformation/order_mapper.py +6 -5
- folio_migration_tools/mapping_file_transformation/organization_mapper.py +3 -3
- folio_migration_tools/mapping_file_transformation/user_mapper.py +43 -28
- folio_migration_tools/marc_rules_transformation/conditions.py +84 -70
- folio_migration_tools/marc_rules_transformation/holdings_statementsparser.py +13 -5
- folio_migration_tools/marc_rules_transformation/hrid_handler.py +3 -2
- folio_migration_tools/marc_rules_transformation/marc_file_processor.py +14 -22
- folio_migration_tools/marc_rules_transformation/rules_mapper_authorities.py +1 -0
- folio_migration_tools/marc_rules_transformation/rules_mapper_base.py +46 -36
- folio_migration_tools/marc_rules_transformation/rules_mapper_bibs.py +25 -15
- folio_migration_tools/marc_rules_transformation/rules_mapper_holdings.py +62 -32
- folio_migration_tools/migration_report.py +1 -1
- folio_migration_tools/migration_tasks/authority_transformer.py +1 -2
- folio_migration_tools/migration_tasks/batch_poster.py +78 -68
- folio_migration_tools/migration_tasks/bibs_transformer.py +12 -7
- folio_migration_tools/migration_tasks/courses_migrator.py +2 -3
- folio_migration_tools/migration_tasks/holdings_csv_transformer.py +14 -15
- folio_migration_tools/migration_tasks/holdings_marc_transformer.py +11 -21
- folio_migration_tools/migration_tasks/items_transformer.py +17 -30
- folio_migration_tools/migration_tasks/loans_migrator.py +53 -131
- folio_migration_tools/migration_tasks/migration_task_base.py +33 -55
- folio_migration_tools/migration_tasks/orders_transformer.py +21 -39
- folio_migration_tools/migration_tasks/organization_transformer.py +9 -18
- folio_migration_tools/migration_tasks/requests_migrator.py +11 -15
- folio_migration_tools/migration_tasks/reserves_migrator.py +1 -1
- folio_migration_tools/migration_tasks/user_transformer.py +10 -15
- folio_migration_tools/task_configuration.py +6 -7
- folio_migration_tools/transaction_migration/legacy_loan.py +15 -27
- folio_migration_tools/transaction_migration/legacy_request.py +1 -1
- {folio_migration_tools-1.9.10.dist-info → folio_migration_tools-1.10.0b1.dist-info}/METADATA +18 -28
- {folio_migration_tools-1.9.10.dist-info → folio_migration_tools-1.10.0b1.dist-info}/RECORD +47 -50
- folio_migration_tools-1.10.0b1.dist-info/WHEEL +4 -0
- folio_migration_tools-1.10.0b1.dist-info/entry_points.txt +3 -0
- folio_migration_tools/test_infrastructure/__init__.py +0 -0
- folio_migration_tools/test_infrastructure/mocked_classes.py +0 -406
- folio_migration_tools-1.9.10.dist-info/WHEEL +0 -4
- folio_migration_tools-1.9.10.dist-info/entry_points.txt +0 -3
- folio_migration_tools-1.9.10.dist-info/licenses/LICENSE +0 -21
|
@@ -53,10 +53,7 @@ class ItemsTransformer(MigrationTaskBase):
|
|
|
53
53
|
HridHandling,
|
|
54
54
|
Field(
|
|
55
55
|
title="HRID handling",
|
|
56
|
-
description=(
|
|
57
|
-
"Determining how the HRID generation "
|
|
58
|
-
"should be handled."
|
|
59
|
-
),
|
|
56
|
+
description=("Determining how the HRID generation should be handled."),
|
|
60
57
|
),
|
|
61
58
|
]
|
|
62
59
|
files: Annotated[
|
|
@@ -91,10 +88,7 @@ class ItemsTransformer(MigrationTaskBase):
|
|
|
91
88
|
Optional[str],
|
|
92
89
|
Field(
|
|
93
90
|
title="Temporary location map file name",
|
|
94
|
-
description=(
|
|
95
|
-
"Temporary file name for location map. "
|
|
96
|
-
"Empty string by default."
|
|
97
|
-
),
|
|
91
|
+
description=("Temporary file name for location map. Empty string by default."),
|
|
98
92
|
),
|
|
99
93
|
] = ""
|
|
100
94
|
material_types_map_file_name: Annotated[
|
|
@@ -115,10 +109,7 @@ class ItemsTransformer(MigrationTaskBase):
|
|
|
115
109
|
Optional[str],
|
|
116
110
|
Field(
|
|
117
111
|
title="Temporary loan types map file name",
|
|
118
|
-
description=(
|
|
119
|
-
"File name for temporary loan types map. "
|
|
120
|
-
"Empty string by default."
|
|
121
|
-
),
|
|
112
|
+
description=("File name for temporary loan types map. Empty string by default."),
|
|
122
113
|
),
|
|
123
114
|
] = ""
|
|
124
115
|
statistical_codes_map_file_name: Annotated[
|
|
@@ -218,9 +209,8 @@ class ItemsTransformer(MigrationTaskBase):
|
|
|
218
209
|
self.folio_keys = MappingFileMapperBase.get_mapped_folio_properties_from_map(
|
|
219
210
|
self.items_map
|
|
220
211
|
)
|
|
221
|
-
if (
|
|
222
|
-
|
|
223
|
-
or any(getattr(k, "statistical_code", "") for k in self.task_configuration.files)
|
|
212
|
+
if any(k for k in self.folio_keys if k.startswith("statisticalCodeIds")) or any(
|
|
213
|
+
getattr(k, "statistical_code", "") for k in self.task_configuration.files
|
|
224
214
|
):
|
|
225
215
|
statcode_mapping = self.load_ref_data_mapping_file(
|
|
226
216
|
"statisticalCodeIds",
|
|
@@ -307,7 +297,7 @@ class ItemsTransformer(MigrationTaskBase):
|
|
|
307
297
|
temporary_loan_type_mapping,
|
|
308
298
|
temporary_location_mapping,
|
|
309
299
|
self.library_configuration,
|
|
310
|
-
self.task_configuration
|
|
300
|
+
self.task_configuration,
|
|
311
301
|
)
|
|
312
302
|
if (
|
|
313
303
|
self.task_configuration.reset_hrid_settings
|
|
@@ -363,9 +353,7 @@ class ItemsTransformer(MigrationTaskBase):
|
|
|
363
353
|
self.handle_notes(folio_rec)
|
|
364
354
|
if folio_rec["holdingsRecordId"] in self.boundwith_relationship_map:
|
|
365
355
|
for idx_, instance_id in enumerate(
|
|
366
|
-
self.boundwith_relationship_map.get(
|
|
367
|
-
folio_rec["holdingsRecordId"]
|
|
368
|
-
)
|
|
356
|
+
self.boundwith_relationship_map.get(folio_rec["holdingsRecordId"])
|
|
369
357
|
):
|
|
370
358
|
if idx_ == 0:
|
|
371
359
|
bw_id = folio_rec["holdingsRecordId"]
|
|
@@ -453,29 +441,28 @@ class ItemsTransformer(MigrationTaskBase):
|
|
|
453
441
|
def load_boundwith_relationships(self):
|
|
454
442
|
try:
|
|
455
443
|
with open(
|
|
456
|
-
|
|
444
|
+
self.folder_structure.boundwith_relationships_map_path
|
|
457
445
|
) as boundwith_relationship_file:
|
|
458
446
|
self.boundwith_relationship_map = dict(
|
|
459
|
-
|
|
460
|
-
)
|
|
461
|
-
logging.info(
|
|
462
|
-
"Rows in Bound with relationship map: %s",
|
|
463
|
-
len(self.boundwith_relationship_map)
|
|
447
|
+
json.loads(x) for x in boundwith_relationship_file
|
|
464
448
|
)
|
|
465
|
-
|
|
449
|
+
logging.info(
|
|
450
|
+
"Rows in Bound with relationship map: %s", len(self.boundwith_relationship_map)
|
|
451
|
+
)
|
|
452
|
+
except FileNotFoundError as fnfe:
|
|
466
453
|
raise TransformationProcessError(
|
|
467
454
|
"",
|
|
468
455
|
"Boundwith relationship file specified, but relationships file "
|
|
469
456
|
"from holdings transformation not found.",
|
|
470
|
-
self.folder_structure.boundwith_relationships_map_path
|
|
471
|
-
)
|
|
472
|
-
except ValueError:
|
|
457
|
+
self.folder_structure.boundwith_relationships_map_path,
|
|
458
|
+
) from fnfe
|
|
459
|
+
except ValueError as ve:
|
|
473
460
|
raise TransformationProcessError(
|
|
474
461
|
"",
|
|
475
462
|
"Boundwith relationship file specified, but relationships file "
|
|
476
463
|
"from holdings transformation is not a valid line JSON.",
|
|
477
464
|
self.folder_structure.boundwith_relationships_map_path,
|
|
478
|
-
)
|
|
465
|
+
) from ve
|
|
479
466
|
|
|
480
467
|
def wrap_up(self):
|
|
481
468
|
logging.info("Done. Transformer wrapping up...")
|
|
@@ -76,18 +76,14 @@ class LoansMigrator(MigrationTaskBase):
|
|
|
76
76
|
Optional[list[FileDefinition]],
|
|
77
77
|
Field(
|
|
78
78
|
title="Item files",
|
|
79
|
-
description=(
|
|
80
|
-
"List of files containing item data. By default is empty list."
|
|
81
|
-
),
|
|
79
|
+
description=("List of files containing item data. By default is empty list."),
|
|
82
80
|
),
|
|
83
81
|
] = []
|
|
84
82
|
patron_files: Annotated[
|
|
85
83
|
Optional[list[FileDefinition]],
|
|
86
84
|
Field(
|
|
87
85
|
title="Patron files",
|
|
88
|
-
description=(
|
|
89
|
-
"List of files containing patron data. By default is empty list."
|
|
90
|
-
),
|
|
86
|
+
description=("List of files containing patron data. By default is empty list."),
|
|
91
87
|
),
|
|
92
88
|
] = []
|
|
93
89
|
|
|
@@ -124,9 +120,7 @@ class LoansMigrator(MigrationTaskBase):
|
|
|
124
120
|
my_path = "/configurations/entries?query=(module==ORG%20and%20configName==localeSettings)"
|
|
125
121
|
try:
|
|
126
122
|
self.tenant_timezone_str = json.loads(
|
|
127
|
-
self.folio_client.folio_get_single_object(my_path)["configs"][0][
|
|
128
|
-
"value"
|
|
129
|
-
]
|
|
123
|
+
self.folio_client.folio_get_single_object(my_path)["configs"][0]["value"]
|
|
130
124
|
)["timezone"]
|
|
131
125
|
logging.info("Tenant timezone is: %s", self.tenant_timezone_str)
|
|
132
126
|
except Exception:
|
|
@@ -135,14 +129,10 @@ class LoansMigrator(MigrationTaskBase):
|
|
|
135
129
|
self.tenant_timezone = ZoneInfo(self.tenant_timezone_str)
|
|
136
130
|
self.semi_valid_legacy_loans = []
|
|
137
131
|
for file_def in task_configuration.open_loans_files:
|
|
138
|
-
loans_file_path =
|
|
139
|
-
self.folder_structure.legacy_records_folder / file_def.file_name
|
|
140
|
-
)
|
|
132
|
+
loans_file_path = self.folder_structure.legacy_records_folder / file_def.file_name
|
|
141
133
|
with open(loans_file_path, "r", encoding="utf-8") as loans_file:
|
|
142
|
-
total_rows, empty_rows, reader = (
|
|
143
|
-
|
|
144
|
-
loans_file, loans_file_path
|
|
145
|
-
)
|
|
134
|
+
total_rows, empty_rows, reader = MappingFileMapperBase._get_delimited_file_reader(
|
|
135
|
+
loans_file, loans_file_path
|
|
146
136
|
)
|
|
147
137
|
logging.info("Source data file contains %d rows", total_rows)
|
|
148
138
|
logging.info("Source data file contains %d empty rows", empty_rows)
|
|
@@ -159,8 +149,7 @@ class LoansMigrator(MigrationTaskBase):
|
|
|
159
149
|
self.semi_valid_legacy_loans.extend(
|
|
160
150
|
self.load_and_validate_legacy_loans(
|
|
161
151
|
reader,
|
|
162
|
-
file_def.service_point_id
|
|
163
|
-
or task_configuration.fallback_service_point_id,
|
|
152
|
+
file_def.service_point_id or task_configuration.fallback_service_point_id,
|
|
164
153
|
)
|
|
165
154
|
)
|
|
166
155
|
|
|
@@ -169,12 +158,8 @@ class LoansMigrator(MigrationTaskBase):
|
|
|
169
158
|
len(self.semi_valid_legacy_loans),
|
|
170
159
|
file_def.file_name,
|
|
171
160
|
)
|
|
172
|
-
logging.info(
|
|
173
|
-
|
|
174
|
-
)
|
|
175
|
-
if any(self.task_configuration.item_files) or any(
|
|
176
|
-
self.task_configuration.patron_files
|
|
177
|
-
):
|
|
161
|
+
logging.info("Loaded and validated %s loans in total", len(self.semi_valid_legacy_loans))
|
|
162
|
+
if any(self.task_configuration.item_files) or any(self.task_configuration.patron_files):
|
|
178
163
|
self.valid_legacy_loans = list(self.check_barcodes())
|
|
179
164
|
logging.info(
|
|
180
165
|
"Loaded and validated %s loans against barcodes",
|
|
@@ -182,8 +167,7 @@ class LoansMigrator(MigrationTaskBase):
|
|
|
182
167
|
)
|
|
183
168
|
else:
|
|
184
169
|
logging.info(
|
|
185
|
-
"No item or user files supplied. Not validating
|
|
186
|
-
"previously migrated objects"
|
|
170
|
+
"No item or user files supplied. Not validating againstpreviously migrated objects"
|
|
187
171
|
)
|
|
188
172
|
self.valid_legacy_loans = self.semi_valid_legacy_loans
|
|
189
173
|
logging.info("Starting row number is %s", task_configuration.starting_row)
|
|
@@ -191,9 +175,9 @@ class LoansMigrator(MigrationTaskBase):
|
|
|
191
175
|
|
|
192
176
|
def check_smtp_config(self):
|
|
193
177
|
try:
|
|
194
|
-
smtp_config = self.folio_client.folio_get_single_object(
|
|
195
|
-
"
|
|
196
|
-
|
|
178
|
+
smtp_config = self.folio_client.folio_get_single_object("/smtp-configuration")[
|
|
179
|
+
"smtpConfigurations"
|
|
180
|
+
][0]
|
|
197
181
|
smtp_config_disabled = "disabled" in smtp_config["host"].lower()
|
|
198
182
|
except IndexError:
|
|
199
183
|
smtp_config_disabled = True
|
|
@@ -201,9 +185,7 @@ class LoansMigrator(MigrationTaskBase):
|
|
|
201
185
|
if not smtp_config_disabled:
|
|
202
186
|
logging.warn("SMTP connection not disabled...")
|
|
203
187
|
for i in range(10, 0, -1):
|
|
204
|
-
sys.stdout.write(
|
|
205
|
-
"Pausing for {:02d} seconds. Press Ctrl+C to exit...\r".format(i)
|
|
206
|
-
)
|
|
188
|
+
sys.stdout.write("Pausing for {:02d} seconds. Press Ctrl+C to exit...\r".format(i))
|
|
207
189
|
time.sleep(1)
|
|
208
190
|
else:
|
|
209
191
|
logging.info("SMTP connection is disabled...")
|
|
@@ -229,7 +211,8 @@ class LoansMigrator(MigrationTaskBase):
|
|
|
229
211
|
self.checkout_single_loan(legacy_loan)
|
|
230
212
|
except TransformationRecordFailedError as ee:
|
|
231
213
|
logging.error(
|
|
232
|
-
f"Transformation failed in row {num_loans}
|
|
214
|
+
f"Transformation failed in row {num_loans} "
|
|
215
|
+
f"Item barcode: {legacy_loan.item_barcode} "
|
|
233
216
|
f"Patron barcode: {legacy_loan.patron_barcode}"
|
|
234
217
|
)
|
|
235
218
|
ee.log_it()
|
|
@@ -239,9 +222,7 @@ class LoansMigrator(MigrationTaskBase):
|
|
|
239
222
|
f"Patron barcode: {legacy_loan.patron_barcode} {ee}"
|
|
240
223
|
)
|
|
241
224
|
if num_loans % 25 == 0:
|
|
242
|
-
logging.info(
|
|
243
|
-
f"{timings(self.t0, t0_migration, num_loans)} {num_loans}"
|
|
244
|
-
)
|
|
225
|
+
logging.info(f"{timings(self.t0, t0_migration, num_loans)} {num_loans}")
|
|
245
226
|
|
|
246
227
|
def checkout_single_loan(self, legacy_loan: LegacyLoan):
|
|
247
228
|
"""Checks a legacy loan out. Retries once if it fails.
|
|
@@ -253,20 +234,14 @@ class LoansMigrator(MigrationTaskBase):
|
|
|
253
234
|
|
|
254
235
|
if res_checkout.was_successful:
|
|
255
236
|
self.migration_report.add("Details", i18n.t("Checked out on first try"))
|
|
256
|
-
self.migration_report.add_general_statistics(
|
|
257
|
-
i18n.t("Successfully checked out")
|
|
258
|
-
)
|
|
237
|
+
self.migration_report.add_general_statistics(i18n.t("Successfully checked out"))
|
|
259
238
|
self.set_renewal_count(legacy_loan, res_checkout)
|
|
260
239
|
self.set_new_status(legacy_loan, res_checkout)
|
|
261
240
|
elif res_checkout.should_be_retried:
|
|
262
241
|
res_checkout2 = self.handle_checkout_failure(legacy_loan, res_checkout)
|
|
263
242
|
if res_checkout2.was_successful and res_checkout2.folio_loan:
|
|
264
|
-
self.migration_report.add(
|
|
265
|
-
|
|
266
|
-
)
|
|
267
|
-
self.migration_report.add_general_statistics(
|
|
268
|
-
i18n.t("Successfully checked out")
|
|
269
|
-
)
|
|
243
|
+
self.migration_report.add("Details", i18n.t("Checked out on second try"))
|
|
244
|
+
self.migration_report.add_general_statistics(i18n.t("Successfully checked out"))
|
|
270
245
|
logging.info("Checked out on second try")
|
|
271
246
|
self.set_renewal_count(legacy_loan, res_checkout2)
|
|
272
247
|
self.set_new_status(legacy_loan, res_checkout2)
|
|
@@ -274,8 +249,7 @@ class LoansMigrator(MigrationTaskBase):
|
|
|
274
249
|
if res_checkout2.error_message == "Aged to lost and checked out":
|
|
275
250
|
self.migration_report.add(
|
|
276
251
|
"Details",
|
|
277
|
-
i18n.t("Second failure")
|
|
278
|
-
+ f": {res_checkout2.migration_report_message}",
|
|
252
|
+
i18n.t("Second failure") + f": {res_checkout2.migration_report_message}",
|
|
279
253
|
)
|
|
280
254
|
logging.error(
|
|
281
255
|
f"{res_checkout2.error_message}. Item barcode: {legacy_loan.item_barcode}"
|
|
@@ -283,13 +257,10 @@ class LoansMigrator(MigrationTaskBase):
|
|
|
283
257
|
else:
|
|
284
258
|
self.failed[legacy_loan.item_barcode] = legacy_loan
|
|
285
259
|
self.migration_report.add_general_statistics(i18n.t("Failed loans"))
|
|
286
|
-
logging.error(
|
|
287
|
-
"Failed on second try: %s", res_checkout2.error_message
|
|
288
|
-
)
|
|
260
|
+
logging.error("Failed on second try: %s", res_checkout2.error_message)
|
|
289
261
|
self.migration_report.add(
|
|
290
262
|
"Details",
|
|
291
|
-
i18n.t("Second failure")
|
|
292
|
-
+ f": {res_checkout2.migration_report_message}",
|
|
263
|
+
i18n.t("Second failure") + f": {res_checkout2.migration_report_message}",
|
|
293
264
|
)
|
|
294
265
|
raise TransformationRecordFailedError(
|
|
295
266
|
f"Row {legacy_loan.row}",
|
|
@@ -297,9 +268,7 @@ class LoansMigrator(MigrationTaskBase):
|
|
|
297
268
|
json.dumps(legacy_loan.to_dict()),
|
|
298
269
|
)
|
|
299
270
|
elif not res_checkout.should_be_retried:
|
|
300
|
-
logging.error(
|
|
301
|
-
"Failed first time. No retries: %s", res_checkout.error_message
|
|
302
|
-
)
|
|
271
|
+
logging.error("Failed first time. No retries: %s", res_checkout.error_message)
|
|
303
272
|
self.migration_report.add_general_statistics(i18n.t("Failed loans"))
|
|
304
273
|
self.migration_report.add(
|
|
305
274
|
"Details",
|
|
@@ -328,14 +297,10 @@ class LoansMigrator(MigrationTaskBase):
|
|
|
328
297
|
elif legacy_loan.next_item_status not in ["Available", "", "Checked out"]:
|
|
329
298
|
self.set_item_status(legacy_loan)
|
|
330
299
|
|
|
331
|
-
def set_renewal_count(
|
|
332
|
-
self, legacy_loan: LegacyLoan, res_checkout: TransactionResult
|
|
333
|
-
):
|
|
300
|
+
def set_renewal_count(self, legacy_loan: LegacyLoan, res_checkout: TransactionResult):
|
|
334
301
|
if legacy_loan.renewal_count > 0:
|
|
335
302
|
self.update_open_loan(res_checkout.folio_loan, legacy_loan)
|
|
336
|
-
self.migration_report.add_general_statistics(
|
|
337
|
-
i18n.t("Updated renewal count for loan")
|
|
338
|
-
)
|
|
303
|
+
self.migration_report.add_general_statistics(i18n.t("Updated renewal count for loan"))
|
|
339
304
|
|
|
340
305
|
def wrap_up(self):
|
|
341
306
|
for k, v in self.failed.items():
|
|
@@ -362,9 +327,7 @@ class LoansMigrator(MigrationTaskBase):
|
|
|
362
327
|
"service_point_id",
|
|
363
328
|
]
|
|
364
329
|
with open(self.folder_structure.failed_recs_path, "w+") as failed_loans_file:
|
|
365
|
-
writer = csv.DictWriter(
|
|
366
|
-
failed_loans_file, fieldnames=csv_columns, dialect="tsv"
|
|
367
|
-
)
|
|
330
|
+
writer = csv.DictWriter(failed_loans_file, fieldnames=csv_columns, dialect="tsv")
|
|
368
331
|
writer.writeheader()
|
|
369
332
|
for _k, failed_loan in self.failed_and_not_dupe.items():
|
|
370
333
|
writer.writerow(failed_loan[0])
|
|
@@ -379,16 +342,12 @@ class LoansMigrator(MigrationTaskBase):
|
|
|
379
342
|
user_barcodes, self.task_configuration.patron_files, self.folder_structure
|
|
380
343
|
)
|
|
381
344
|
for loan in self.semi_valid_legacy_loans:
|
|
382
|
-
has_item_barcode = loan.item_barcode in item_barcodes or not any(
|
|
383
|
-
|
|
384
|
-
)
|
|
385
|
-
has_patron_barcode = loan.patron_barcode in user_barcodes or not any(
|
|
386
|
-
user_barcodes
|
|
387
|
-
)
|
|
345
|
+
has_item_barcode = loan.item_barcode in item_barcodes or not any(item_barcodes)
|
|
346
|
+
has_patron_barcode = loan.patron_barcode in user_barcodes or not any(user_barcodes)
|
|
388
347
|
has_proxy_barcode = True
|
|
389
348
|
if loan.proxy_patron_barcode:
|
|
390
|
-
has_proxy_barcode = (
|
|
391
|
-
|
|
349
|
+
has_proxy_barcode = loan.proxy_patron_barcode in user_barcodes or not any(
|
|
350
|
+
user_barcodes
|
|
392
351
|
)
|
|
393
352
|
if has_item_barcode and has_patron_barcode and has_proxy_barcode:
|
|
394
353
|
self.migration_report.add_general_statistics(
|
|
@@ -424,9 +383,7 @@ class LoansMigrator(MigrationTaskBase):
|
|
|
424
383
|
json.dumps(loan.to_dict()),
|
|
425
384
|
)
|
|
426
385
|
|
|
427
|
-
def load_and_validate_legacy_loans(
|
|
428
|
-
self, loans_reader, service_point_id: str
|
|
429
|
-
) -> list:
|
|
386
|
+
def load_and_validate_legacy_loans(self, loans_reader, service_point_id: str) -> list:
|
|
430
387
|
results = []
|
|
431
388
|
num_bad = 0
|
|
432
389
|
logging.info("Validating legacy loans in file...")
|
|
@@ -446,29 +403,23 @@ class LoansMigrator(MigrationTaskBase):
|
|
|
446
403
|
)
|
|
447
404
|
self.migration_report.add_general_statistics(i18n.t("Failed loans"))
|
|
448
405
|
for error in legacy_loan.errors:
|
|
449
|
-
self.migration_report.add(
|
|
450
|
-
"DiscardedLoans", f"{error[0]} - {error[1]}"
|
|
451
|
-
)
|
|
406
|
+
self.migration_report.add("DiscardedLoans", f"{error[0]} - {error[1]}")
|
|
452
407
|
# Add this loan to failed loans for later correction and re-run.
|
|
453
|
-
self.failed[
|
|
454
|
-
legacy_loan
|
|
455
|
-
|
|
408
|
+
self.failed[legacy_loan.item_barcode or f"no_barcode_{legacy_loan_count}"] = (
|
|
409
|
+
legacy_loan
|
|
410
|
+
)
|
|
456
411
|
else:
|
|
457
412
|
results.append(legacy_loan)
|
|
458
413
|
except TransformationRecordFailedError as trfe:
|
|
459
414
|
num_bad += 1
|
|
460
|
-
self.migration_report.add_general_statistics(
|
|
461
|
-
i18n.t("Loans failed pre-validation")
|
|
462
|
-
)
|
|
415
|
+
self.migration_report.add_general_statistics(i18n.t("Loans failed pre-validation"))
|
|
463
416
|
self.migration_report.add(
|
|
464
417
|
"DiscardedLoans",
|
|
465
418
|
f"{trfe.message} - see data issues log",
|
|
466
419
|
)
|
|
467
420
|
trfe.log_it()
|
|
468
421
|
self.failed[
|
|
469
|
-
legacy_loan_dict.get(
|
|
470
|
-
"item_barcode", f"no_barcode_{legacy_loan_count}"
|
|
471
|
-
)
|
|
422
|
+
legacy_loan_dict.get("item_barcode", f"no_barcode_{legacy_loan_count}")
|
|
472
423
|
] = legacy_loan_dict
|
|
473
424
|
except ValueError as ve:
|
|
474
425
|
logging.exception(ve)
|
|
@@ -516,9 +467,7 @@ class LoansMigrator(MigrationTaskBase):
|
|
|
516
467
|
return self.handle_lost_item(legacy_loan, "Aged to lost")
|
|
517
468
|
elif folio_checkout.error_message == "Declared lost":
|
|
518
469
|
return self.handle_lost_item(legacy_loan, "Declared lost")
|
|
519
|
-
elif folio_checkout.error_message.startswith(
|
|
520
|
-
"Cannot check out to inactive user"
|
|
521
|
-
):
|
|
470
|
+
elif folio_checkout.error_message.startswith("Cannot check out to inactive user"):
|
|
522
471
|
return self.checkout_to_inactive_user(legacy_loan)
|
|
523
472
|
elif (
|
|
524
473
|
"has the item status Claimed returned and cannot be checked out"
|
|
@@ -550,9 +499,7 @@ class LoansMigrator(MigrationTaskBase):
|
|
|
550
499
|
f"Duplicate loans (or failed twice) Item barcode: "
|
|
551
500
|
f"{legacy_loan.item_barcode} Patron barcode: {legacy_loan.patron_barcode}"
|
|
552
501
|
)
|
|
553
|
-
self.migration_report.add(
|
|
554
|
-
"Details", i18n.t("Duplicate loans (or failed twice)")
|
|
555
|
-
)
|
|
502
|
+
self.migration_report.add("Details", i18n.t("Duplicate loans (or failed twice)"))
|
|
556
503
|
del self.failed[legacy_loan.item_barcode]
|
|
557
504
|
return TransactionResult(False, False, "", "", "")
|
|
558
505
|
|
|
@@ -563,9 +510,7 @@ class LoansMigrator(MigrationTaskBase):
|
|
|
563
510
|
user["expirationDate"] = datetime.isoformat(datetime.now() + timedelta(days=1))
|
|
564
511
|
self.activate_user(user)
|
|
565
512
|
logging.debug("Successfully Activated user")
|
|
566
|
-
res = self.circulation_helper.check_out_by_barcode(
|
|
567
|
-
legacy_loan
|
|
568
|
-
) # checkout_and_update
|
|
513
|
+
res = self.circulation_helper.check_out_by_barcode(legacy_loan) # checkout_and_update
|
|
569
514
|
if res.should_be_retried:
|
|
570
515
|
res = self.handle_checkout_failure(legacy_loan, res)
|
|
571
516
|
self.migration_report.add("Details", res.migration_report_message)
|
|
@@ -670,9 +615,7 @@ class LoansMigrator(MigrationTaskBase):
|
|
|
670
615
|
loan_to_put["dueDate"] = due_date.isoformat()
|
|
671
616
|
loan_to_put["loanDate"] = out_date.isoformat()
|
|
672
617
|
loan_to_put["renewalCount"] = renewal_count
|
|
673
|
-
url =
|
|
674
|
-
f"{self.folio_client.gateway_url}/circulation/loans/{loan_to_put['id']}"
|
|
675
|
-
)
|
|
618
|
+
url = f"{self.folio_client.gateway_url}/circulation/loans/{loan_to_put['id']}"
|
|
676
619
|
req = self.http_client.put(
|
|
677
620
|
url,
|
|
678
621
|
headers=self.folio_client.okapi_headers,
|
|
@@ -693,8 +636,7 @@ class LoansMigrator(MigrationTaskBase):
|
|
|
693
636
|
else:
|
|
694
637
|
self.migration_report.add(
|
|
695
638
|
"Details",
|
|
696
|
-
i18n.t("Update open loan error http status")
|
|
697
|
-
+ f": {req.status_code}",
|
|
639
|
+
i18n.t("Update open loan error http status") + f": {req.status_code}",
|
|
698
640
|
)
|
|
699
641
|
req.raise_for_status()
|
|
700
642
|
logging.debug("Updating open loan was successful")
|
|
@@ -724,41 +666,27 @@ class LoansMigrator(MigrationTaskBase):
|
|
|
724
666
|
"servicePointId": str(self.task_configuration.fallback_service_point_id),
|
|
725
667
|
}
|
|
726
668
|
logging.debug(f"Declare lost data: {json.dumps(data, indent=4)}")
|
|
727
|
-
if self.folio_put_post(
|
|
728
|
-
|
|
729
|
-
):
|
|
730
|
-
self.migration_report.add(
|
|
731
|
-
"Details", i18n.t("Successfully declared loan as lost")
|
|
732
|
-
)
|
|
669
|
+
if self.folio_put_post(declare_lost_url, data, "POST", i18n.t("Declare item as lost")):
|
|
670
|
+
self.migration_report.add("Details", i18n.t("Successfully declared loan as lost"))
|
|
733
671
|
else:
|
|
734
672
|
logging.error(f"Unsuccessfully declared loan {folio_loan} as lost")
|
|
735
|
-
self.migration_report.add(
|
|
736
|
-
"Details", i18n.t("Unsuccessfully declared loan as lost")
|
|
737
|
-
)
|
|
673
|
+
self.migration_report.add("Details", i18n.t("Unsuccessfully declared loan as lost"))
|
|
738
674
|
|
|
739
675
|
def claim_returned(self, folio_loan):
|
|
740
|
-
claim_returned_url =
|
|
741
|
-
f"/circulation/loans/{folio_loan['id']}/claim-item-returned"
|
|
742
|
-
)
|
|
676
|
+
claim_returned_url = f"/circulation/loans/{folio_loan['id']}/claim-item-returned"
|
|
743
677
|
logging.debug(f"Claim returned url:{claim_returned_url}")
|
|
744
678
|
due_date = du_parser.isoparse(folio_loan["dueDate"])
|
|
745
679
|
data = {
|
|
746
|
-
"itemClaimedReturnedDateTime": datetime.isoformat(
|
|
747
|
-
due_date + timedelta(days=1)
|
|
748
|
-
),
|
|
680
|
+
"itemClaimedReturnedDateTime": datetime.isoformat(due_date + timedelta(days=1)),
|
|
749
681
|
"comment": "Created at migration. Date is due date + 1 day",
|
|
750
682
|
}
|
|
751
683
|
logging.debug(f"Claim returned data:\t{json.dumps(data)}")
|
|
752
|
-
if self.folio_put_post(
|
|
753
|
-
claim_returned_url, data, "POST", i18n.t("Claim item returned")
|
|
754
|
-
):
|
|
684
|
+
if self.folio_put_post(claim_returned_url, data, "POST", i18n.t("Claim item returned")):
|
|
755
685
|
self.migration_report.add(
|
|
756
686
|
"Details", i18n.t("Successfully declared loan as Claimed returned")
|
|
757
687
|
)
|
|
758
688
|
else:
|
|
759
|
-
logging.error(
|
|
760
|
-
f"Unsuccessfully declared loan {folio_loan} as Claimed returned"
|
|
761
|
-
)
|
|
689
|
+
logging.error(f"Unsuccessfully declared loan {folio_loan} as Claimed returned")
|
|
762
690
|
self.migration_report.add(
|
|
763
691
|
"Details",
|
|
764
692
|
i18n.t(
|
|
@@ -770,13 +698,9 @@ class LoansMigrator(MigrationTaskBase):
|
|
|
770
698
|
def set_item_status(self, legacy_loan: LegacyLoan):
|
|
771
699
|
try:
|
|
772
700
|
# Get Item by barcode, update status.
|
|
773
|
-
item_path = (
|
|
774
|
-
f'item-storage/items?query=(barcode=="{legacy_loan.item_barcode}")'
|
|
775
|
-
)
|
|
701
|
+
item_path = f'item-storage/items?query=(barcode=="{legacy_loan.item_barcode}")'
|
|
776
702
|
item_url = f"{self.folio_client.gateway_url}/{item_path}"
|
|
777
|
-
resp = self.http_client.get(
|
|
778
|
-
item_url, headers=self.folio_client.okapi_headers
|
|
779
|
-
)
|
|
703
|
+
resp = self.http_client.get(item_url, headers=self.folio_client.okapi_headers)
|
|
780
704
|
resp.raise_for_status()
|
|
781
705
|
data = resp.json()
|
|
782
706
|
folio_item = data["items"][0]
|
|
@@ -897,9 +821,7 @@ class LoansMigrator(MigrationTaskBase):
|
|
|
897
821
|
try:
|
|
898
822
|
api_path = f"{folio_loan['id']}/change-due-date"
|
|
899
823
|
api_url = f"{self.folio_client.gateway_url}/circulation/loans/{api_path}"
|
|
900
|
-
body = {
|
|
901
|
-
"dueDate": du_parser.isoparse(str(legacy_loan.due_date)).isoformat()
|
|
902
|
-
}
|
|
824
|
+
body = {"dueDate": du_parser.isoparse(str(legacy_loan.due_date)).isoformat()}
|
|
903
825
|
req = self.http_client.post(
|
|
904
826
|
api_url, headers=self.folio_client.okapi_headers, json=body
|
|
905
827
|
)
|