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
|
@@ -3,9 +3,8 @@ from typing import Protocol
|
|
|
3
3
|
|
|
4
4
|
__version__ = importlib.metadata.version("folio_migration_tools")
|
|
5
5
|
|
|
6
|
+
|
|
6
7
|
class StrCoercible(Protocol):
|
|
7
|
-
def __repr__(self) -> str:
|
|
8
|
-
...
|
|
8
|
+
def __repr__(self) -> str: ...
|
|
9
9
|
|
|
10
|
-
def __str__(self) -> str:
|
|
11
|
-
...
|
|
10
|
+
def __str__(self) -> str: ...
|
|
@@ -33,23 +33,25 @@ def parse_args(args):
|
|
|
33
33
|
|
|
34
34
|
parser.add_argument(
|
|
35
35
|
"task_name",
|
|
36
|
-
help=("Task name. Use one of:
|
|
36
|
+
help=(f"Task name. Use one of: {tasks_string}"),
|
|
37
37
|
nargs="?" if "FOLIO_MIGRATION_TOOLS_TASK_NAME" in environ else None,
|
|
38
38
|
prompt="FOLIO_MIGRATION_TOOLS_TASK_NAME" not in environ,
|
|
39
39
|
default=environ.get("FOLIO_MIGRATION_TOOLS_TASK_NAME"),
|
|
40
40
|
)
|
|
41
41
|
parser.add_argument(
|
|
42
|
-
"--folio_password",
|
|
42
|
+
"--folio_password",
|
|
43
|
+
"--okapi_password",
|
|
43
44
|
help="password for the tenant in the configuration file",
|
|
44
45
|
prompt="FOLIO_MIGRATION_TOOLS_OKAPI_PASSWORD" not in environ,
|
|
45
|
-
default=environ.get(
|
|
46
|
+
default=environ.get(
|
|
47
|
+
"FOLIO_MIGRATION_TOOLS_FOLIO_PASSWORD",
|
|
48
|
+
environ.get("FOLIO_MIGRATION_TOOLS_OKAPI_PASSWORD"),
|
|
49
|
+
),
|
|
46
50
|
secure=True,
|
|
47
51
|
)
|
|
48
52
|
parser.add_argument(
|
|
49
53
|
"--base_folder_path",
|
|
50
|
-
help=(
|
|
51
|
-
"path to the base folder for this library. Built on migration_repo_template"
|
|
52
|
-
),
|
|
54
|
+
help=("path to the base folder for this library. Built on migration_repo_template"),
|
|
53
55
|
prompt="FOLIO_MIGRATION_TOOLS_BASE_FOLDER_PATH" not in environ,
|
|
54
56
|
default=environ.get("FOLIO_MIGRATION_TOOLS_BASE_FOLDER_PATH"),
|
|
55
57
|
)
|
|
@@ -62,34 +64,43 @@ def parse_args(args):
|
|
|
62
64
|
prompt=False,
|
|
63
65
|
)
|
|
64
66
|
parser.add_argument(
|
|
65
|
-
"--version",
|
|
67
|
+
"--version",
|
|
68
|
+
"-V",
|
|
66
69
|
help="Show the version of the FOLIO Migration Tools",
|
|
67
70
|
action="store_true",
|
|
68
71
|
prompt=False,
|
|
69
72
|
)
|
|
70
73
|
return parser.parse_args(args)
|
|
71
74
|
|
|
75
|
+
|
|
72
76
|
def prep_library_config(args):
|
|
73
|
-
|
|
74
|
-
config_file_humped["libraryInformation"]["okapiPassword"] = args.folio_password
|
|
75
|
-
config_file_humped["libraryInformation"]["baseFolder"] = args.base_folder_path
|
|
76
|
-
config_file = humps.decamelize(config_file_humped)
|
|
77
|
-
library_config = LibraryConfiguration(**config_file["library_information"])
|
|
78
|
-
if library_config.ecs_tenant_id:
|
|
79
|
-
library_config.is_ecs = True
|
|
80
|
-
if library_config.ecs_tenant_id and not library_config.ecs_central_iteration_identifier:
|
|
81
|
-
print(
|
|
82
|
-
"ECS tenant ID is set, but no central iteration identifier is provided. "
|
|
83
|
-
"Please provide the central iteration identifier in the configuration file."
|
|
84
|
-
)
|
|
85
|
-
sys.exit("ECS Central Iteration Identifier Not Found")
|
|
86
|
-
return config_file, library_config
|
|
77
|
+
config_file_humped = merge_load(args.configuration_path)
|
|
87
78
|
|
|
88
|
-
|
|
89
|
-
|
|
79
|
+
# Only set folioPassword if neither folioPassword nor okapiPassword exist in config
|
|
80
|
+
# The Pydantic validator will handle backward compatibility for existing okapiPassword
|
|
81
|
+
if (
|
|
82
|
+
"folioPassword" not in config_file_humped["libraryInformation"]
|
|
83
|
+
and "okapiPassword" not in config_file_humped["libraryInformation"]
|
|
84
|
+
):
|
|
85
|
+
config_file_humped["libraryInformation"]["folioPassword"] = args.folio_password
|
|
86
|
+
|
|
87
|
+
config_file_humped["libraryInformation"]["baseFolder"] = args.base_folder_path
|
|
88
|
+
config_file = humps.decamelize(config_file_humped)
|
|
89
|
+
library_config = LibraryConfiguration(**config_file["library_information"])
|
|
90
|
+
if library_config.ecs_tenant_id:
|
|
91
|
+
library_config.is_ecs = True
|
|
92
|
+
if library_config.ecs_tenant_id and not library_config.ecs_central_iteration_identifier:
|
|
90
93
|
print(
|
|
91
|
-
|
|
94
|
+
"ECS tenant ID is set, but no central iteration identifier is provided. "
|
|
95
|
+
"Please provide the central iteration identifier in the configuration file."
|
|
92
96
|
)
|
|
97
|
+
sys.exit("ECS Central Iteration Identifier Not Found")
|
|
98
|
+
return config_file, library_config
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def print_version(args):
|
|
102
|
+
if "-V" in args or "--version" in args:
|
|
103
|
+
print(f"FOLIO Migration Tools: {metadata.version('folio_migration_tools')}")
|
|
93
104
|
sys.exit(0)
|
|
94
105
|
return None
|
|
95
106
|
|
|
@@ -122,7 +133,7 @@ def main():
|
|
|
122
133
|
task_names = [t.get("name", "") for t in config_file["migration_tasks"]]
|
|
123
134
|
print(
|
|
124
135
|
f"Referenced task name {args.task_name} not found in the "
|
|
125
|
-
f
|
|
136
|
+
f"configuration file. Use one of {', '.join(task_names)}"
|
|
126
137
|
"\nHalting..."
|
|
127
138
|
)
|
|
128
139
|
sys.exit("Task Name Not Found")
|
|
@@ -134,13 +145,15 @@ def main():
|
|
|
134
145
|
)
|
|
135
146
|
except StopIteration:
|
|
136
147
|
print(
|
|
137
|
-
f
|
|
148
|
+
f"Referenced task {migration_task_config['migration_task_type']} "
|
|
138
149
|
"is not a valid option. Update your task to incorporate "
|
|
139
150
|
f"one of {json.dumps([tc.__name__ for tc in task_classes], indent=4)}"
|
|
140
151
|
)
|
|
141
152
|
sys.exit("Task Type Not Found")
|
|
142
153
|
try:
|
|
143
|
-
logging.getLogger("httpx").setLevel(
|
|
154
|
+
logging.getLogger("httpx").setLevel(
|
|
155
|
+
logging.WARNING
|
|
156
|
+
) # Exclude info messages from httpx
|
|
144
157
|
with FolioClient(
|
|
145
158
|
library_config.gateway_url,
|
|
146
159
|
library_config.tenant_id,
|
|
@@ -161,16 +174,15 @@ def main():
|
|
|
161
174
|
logging.critical(json_error)
|
|
162
175
|
print(json_error.doc)
|
|
163
176
|
print(
|
|
164
|
-
f"\n{json_error}"
|
|
165
|
-
f"\nError parsing the above JSON mapping or configruation file. Halting."
|
|
177
|
+
f"\n{json_error}\nError parsing the above JSON mapping or configruation file. Halting."
|
|
166
178
|
)
|
|
167
179
|
sys.exit("Invalid JSON")
|
|
168
180
|
except ValidationError as e:
|
|
169
|
-
print(e.
|
|
181
|
+
print(json.dumps(e.errors(), indent=2))
|
|
170
182
|
print("Validation errors in configuration file:")
|
|
171
183
|
print("==========================================")
|
|
172
184
|
|
|
173
|
-
for validation_message in
|
|
185
|
+
for validation_message in e.errors():
|
|
174
186
|
print(
|
|
175
187
|
f"{validation_message['msg']}\t"
|
|
176
188
|
f"{', '.join(humps.camelize(str(x)) for x in validation_message['loc'])}"
|
|
@@ -199,6 +211,7 @@ def main():
|
|
|
199
211
|
sys.exit(ee.__class__.__name__)
|
|
200
212
|
sys.exit(0)
|
|
201
213
|
|
|
214
|
+
|
|
202
215
|
def inheritors(base_class):
|
|
203
216
|
subclasses = set()
|
|
204
217
|
work = [base_class]
|
|
@@ -3,12 +3,12 @@ import json
|
|
|
3
3
|
import logging
|
|
4
4
|
import re
|
|
5
5
|
import time
|
|
6
|
+
from http import HTTPStatus
|
|
6
7
|
from typing import Set
|
|
7
8
|
|
|
8
9
|
import httpx
|
|
9
10
|
import i18n
|
|
10
|
-
from folioclient import FolioClient
|
|
11
|
-
from httpx import HTTPError
|
|
11
|
+
from folioclient import FolioClient, FolioClientError, FolioConnectionError, FolioValidationError
|
|
12
12
|
|
|
13
13
|
from folio_migration_tools.helper import Helper
|
|
14
14
|
from folio_migration_tools.migration_report import MigrationReport
|
|
@@ -138,7 +138,6 @@ class CirculationHelper:
|
|
|
138
138
|
if legacy_loan.proxy_patron_barcode:
|
|
139
139
|
data.update({"proxyUserBarcode": legacy_loan.proxy_patron_barcode})
|
|
140
140
|
path = "/circulation/check-out-by-barcode"
|
|
141
|
-
url = f"{self.folio_client.gateway_url}{path}"
|
|
142
141
|
try:
|
|
143
142
|
if legacy_loan.patron_barcode in self.missing_patron_barcodes:
|
|
144
143
|
error_message = i18n.t("Patron barcode already detected as missing")
|
|
@@ -147,108 +146,109 @@ class CirculationHelper:
|
|
|
147
146
|
f"Item Barcode:{legacy_loan.item_barcode}"
|
|
148
147
|
)
|
|
149
148
|
return TransactionResult(False, False, "", error_message, error_message)
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
None,
|
|
178
|
-
error_message_from_folio,
|
|
179
|
-
stat_message,
|
|
180
|
-
)
|
|
181
|
-
|
|
182
|
-
elif "find user with matching barcode" in error_message_from_folio:
|
|
183
|
-
self.missing_patron_barcodes.add(legacy_loan.patron_barcode)
|
|
184
|
-
error_message = f"No patron with barcode {legacy_loan.patron_barcode} in FOLIO"
|
|
185
|
-
stat_message = i18n.t("Patron barcode not in FOLIO")
|
|
186
|
-
return TransactionResult(
|
|
187
|
-
False,
|
|
188
|
-
False,
|
|
189
|
-
None,
|
|
190
|
-
error_message_from_folio,
|
|
191
|
-
stat_message,
|
|
192
|
-
)
|
|
193
|
-
elif "Cannot check out item that already has an open" in error_message_from_folio:
|
|
194
|
-
return TransactionResult(
|
|
195
|
-
False,
|
|
196
|
-
False,
|
|
197
|
-
None,
|
|
198
|
-
error_message_from_folio,
|
|
199
|
-
error_message_from_folio,
|
|
200
|
-
)
|
|
201
|
-
elif "Item is already checked out" in error_message_from_folio:
|
|
202
|
-
return TransactionResult(
|
|
203
|
-
False,
|
|
204
|
-
True,
|
|
205
|
-
None,
|
|
206
|
-
error_message_from_folio,
|
|
207
|
-
error_message_from_folio,
|
|
208
|
-
)
|
|
209
|
-
logging.error(
|
|
210
|
-
f"{error_message} "
|
|
211
|
-
f"Patron barcode: {legacy_loan.patron_barcode} "
|
|
212
|
-
f"Item Barcode:{legacy_loan.item_barcode}"
|
|
149
|
+
loan = self.folio_client.folio_post(path, data)
|
|
150
|
+
stats = "Successfully checked out by barcode"
|
|
151
|
+
logging.debug(
|
|
152
|
+
"%s (item barcode %s}) in %ss",
|
|
153
|
+
stats,
|
|
154
|
+
legacy_loan.item_barcode,
|
|
155
|
+
f"{(time.time() - t0_function):.2f}",
|
|
156
|
+
)
|
|
157
|
+
return TransactionResult(True, False, loan, "", stats)
|
|
158
|
+
except FolioValidationError as fve:
|
|
159
|
+
error_message_from_folio = self.folio_client.handle_json_response(fve.response)[
|
|
160
|
+
"errors"
|
|
161
|
+
][0]["message"]
|
|
162
|
+
stat_message = error_message_from_folio
|
|
163
|
+
error_message = error_message_from_folio
|
|
164
|
+
if "has the item status" in error_message_from_folio:
|
|
165
|
+
stat_message = re.findall(
|
|
166
|
+
r"(?<=has the item status\s).*(?=\sand cannot be checked out)",
|
|
167
|
+
error_message_from_folio,
|
|
168
|
+
)[0]
|
|
169
|
+
error_message = f"{stat_message} for item with barcode {legacy_loan.item_barcode}"
|
|
170
|
+
return TransactionResult(
|
|
171
|
+
False,
|
|
172
|
+
True,
|
|
173
|
+
None,
|
|
174
|
+
error_message_from_folio,
|
|
175
|
+
stat_message,
|
|
213
176
|
)
|
|
214
|
-
|
|
177
|
+
elif "No item with barcode" in error_message_from_folio:
|
|
178
|
+
error_message = f"No item with barcode {legacy_loan.item_barcode} in FOLIO"
|
|
179
|
+
stat_message = "Item barcode not in FOLIO"
|
|
180
|
+
self.missing_item_barcodes.add(legacy_loan.item_barcode)
|
|
215
181
|
return TransactionResult(
|
|
216
|
-
False,
|
|
182
|
+
False,
|
|
183
|
+
False,
|
|
184
|
+
None,
|
|
185
|
+
error_message_from_folio,
|
|
186
|
+
stat_message,
|
|
217
187
|
)
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
188
|
+
|
|
189
|
+
elif "find user with matching barcode" in error_message_from_folio:
|
|
190
|
+
self.missing_patron_barcodes.add(legacy_loan.patron_barcode)
|
|
191
|
+
error_message = f"No patron with barcode {legacy_loan.patron_barcode} in FOLIO"
|
|
192
|
+
stat_message = i18n.t("Patron barcode not in FOLIO")
|
|
193
|
+
return TransactionResult(
|
|
194
|
+
False,
|
|
195
|
+
False,
|
|
196
|
+
None,
|
|
197
|
+
error_message_from_folio,
|
|
198
|
+
stat_message,
|
|
225
199
|
)
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
req.status_code,
|
|
200
|
+
elif "Cannot check out item that already has an open" in error_message_from_folio:
|
|
201
|
+
return TransactionResult(
|
|
202
|
+
False,
|
|
203
|
+
False,
|
|
204
|
+
None,
|
|
205
|
+
error_message_from_folio,
|
|
206
|
+
error_message_from_folio,
|
|
234
207
|
)
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
208
|
+
elif "Item is already checked out" in error_message_from_folio:
|
|
209
|
+
return TransactionResult(
|
|
210
|
+
False,
|
|
211
|
+
True,
|
|
212
|
+
None,
|
|
213
|
+
error_message_from_folio,
|
|
214
|
+
error_message_from_folio,
|
|
215
|
+
)
|
|
216
|
+
logging.error(
|
|
217
|
+
f"{error_message} "
|
|
218
|
+
f"Patron barcode: {legacy_loan.patron_barcode} "
|
|
219
|
+
f"Item Barcode:{legacy_loan.item_barcode}"
|
|
220
|
+
)
|
|
221
|
+
self.migration_report.add("Details", stat_message)
|
|
222
|
+
return TransactionResult(
|
|
223
|
+
False, True, None, error_message, f"Check out error: {stat_message}"
|
|
224
|
+
)
|
|
225
|
+
except FolioClientError as fce:
|
|
239
226
|
logging.exception(
|
|
240
227
|
"%s\tPOST FAILED %s\n\t%s\n\t%s",
|
|
241
|
-
|
|
242
|
-
url,
|
|
228
|
+
fce.response.status_code,
|
|
229
|
+
fce.request.url,
|
|
243
230
|
json.dumps(data),
|
|
244
|
-
|
|
231
|
+
fce.response.text,
|
|
245
232
|
)
|
|
246
233
|
return TransactionResult(
|
|
247
234
|
False,
|
|
248
235
|
False,
|
|
249
236
|
None,
|
|
250
237
|
"5XX",
|
|
251
|
-
i18n.t("Failed checkout http status %{code}", code=
|
|
238
|
+
i18n.t("Failed checkout http status %{code}", code=fce.response.status_code),
|
|
239
|
+
)
|
|
240
|
+
except FolioConnectionError as fce:
|
|
241
|
+
logging.exception(
|
|
242
|
+
"Connection error\tPOST FAILED %s\n\t%s\n\t%s",
|
|
243
|
+
fce.request.url,
|
|
244
|
+
json.dumps(data),
|
|
245
|
+
)
|
|
246
|
+
return TransactionResult(
|
|
247
|
+
False,
|
|
248
|
+
False,
|
|
249
|
+
None,
|
|
250
|
+
"Connection error",
|
|
251
|
+
i18n.t("Connection error during checkout"),
|
|
252
252
|
)
|
|
253
253
|
|
|
254
254
|
@staticmethod
|
|
@@ -257,7 +257,6 @@ class CirculationHelper:
|
|
|
257
257
|
):
|
|
258
258
|
try:
|
|
259
259
|
path = "/circulation/requests"
|
|
260
|
-
url = f"{folio_client.gateway_url}{path}"
|
|
261
260
|
data = legacy_request.serialize()
|
|
262
261
|
data["requestProcessingParameters"] = {
|
|
263
262
|
"overrideBlocks": {
|
|
@@ -269,21 +268,31 @@ class CirculationHelper:
|
|
|
269
268
|
"comment": "Migrated from legacy system",
|
|
270
269
|
}
|
|
271
270
|
}
|
|
272
|
-
|
|
273
|
-
logging.debug(f"POST {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
271
|
+
_ = folio_client.folio_post(path, data)
|
|
272
|
+
logging.debug(f"POST {path}\t{json.dumps(data)}")
|
|
273
|
+
logging.info(
|
|
274
|
+
"%s Successfully created %s",
|
|
275
|
+
HTTPStatus.OK,
|
|
276
|
+
legacy_request.request_type,
|
|
277
|
+
)
|
|
278
|
+
return True
|
|
279
|
+
except FolioValidationError as fve:
|
|
280
|
+
message = folio_client.handle_json_response(fve.response)["errors"][0]["message"]
|
|
281
|
+
logging.error(message)
|
|
282
|
+
migration_report.add_general_statistics(message)
|
|
283
|
+
return False
|
|
284
|
+
except (FolioConnectionError, FolioClientError) as fce:
|
|
285
|
+
if client_response := getattr(fce, "response", None):
|
|
286
|
+
message = (
|
|
287
|
+
f"HTTP {client_response.status_code} Error creating request: "
|
|
288
|
+
f"{client_response.text}"
|
|
289
|
+
)
|
|
290
|
+
logging.error(message)
|
|
277
291
|
migration_report.add_general_statistics(message)
|
|
278
|
-
return False
|
|
279
292
|
else:
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
req.status_code,
|
|
284
|
-
legacy_request.request_type,
|
|
285
|
-
)
|
|
286
|
-
return True
|
|
293
|
+
logging.error(f"Connection error creating request: {fce}")
|
|
294
|
+
migration_report.add_general_statistics("Connection error creating request")
|
|
295
|
+
return False
|
|
287
296
|
except Exception as exception:
|
|
288
297
|
logging.error(exception, exc_info=True)
|
|
289
298
|
migration_report.add("Details", exception)
|
|
@@ -7,10 +7,10 @@ class InsensitiveDictReader(csv.DictReader):
|
|
|
7
7
|
# spaces and to lower case.
|
|
8
8
|
@property
|
|
9
9
|
def fieldnames(self):
|
|
10
|
-
return [field.strip().lower() for field in csv.DictReader.fieldnames.fget(self)]
|
|
10
|
+
return [field.strip().lower() for field in csv.DictReader.fieldnames.fget(self)] # type: ignore
|
|
11
11
|
|
|
12
12
|
def next(self):
|
|
13
|
-
return InsensitiveDict(csv.DictReader.next(self))
|
|
13
|
+
return InsensitiveDict(csv.DictReader.next(self)) # type: ignore
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class InsensitiveDict(dict):
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
from typing import Union
|
|
3
2
|
import i18n
|
|
4
3
|
|
|
5
4
|
from folio_migration_tools import StrCoercible
|
|
@@ -13,10 +12,10 @@ class TransformationFieldMappingError(TransformationError):
|
|
|
13
12
|
"""Raised when the field mapping fails, but the error is not critical.
|
|
14
13
|
The issue should be logged for the library to act upon it"""
|
|
15
14
|
|
|
16
|
-
def __init__(self, index_or_id="", message="", data_value:
|
|
15
|
+
def __init__(self, index_or_id="", message="", data_value: str | StrCoercible = ""):
|
|
17
16
|
self.index_or_id = index_or_id or ""
|
|
18
17
|
self.message = message
|
|
19
|
-
self.data_value:
|
|
18
|
+
self.data_value: str | StrCoercible = data_value
|
|
20
19
|
super().__init__(self.message)
|
|
21
20
|
|
|
22
21
|
def __str__(self):
|
|
@@ -41,7 +40,7 @@ class TransformationRecordFailedError(TransformationError):
|
|
|
41
40
|
def __init__(self, index_or_id, message="", data_value=""):
|
|
42
41
|
self.index_or_id = index_or_id
|
|
43
42
|
self.message = message
|
|
44
|
-
self.data_value:
|
|
43
|
+
self.data_value: str | StrCoercible = data_value
|
|
45
44
|
# logging.log(26, f"RECORD FAILED\t{self.id}\t{self.message}\t{self.data_value}")
|
|
46
45
|
super().__init__(self.message)
|
|
47
46
|
|
|
@@ -70,7 +69,7 @@ class TransformationProcessError(TransformationError):
|
|
|
70
69
|
index_or_id,
|
|
71
70
|
message="Critical Process issue. Transformation failed."
|
|
72
71
|
" Check configuration, mapping files and reference data",
|
|
73
|
-
data_value:
|
|
72
|
+
data_value: str | StrCoercible = "",
|
|
74
73
|
):
|
|
75
74
|
self.index_or_id = index_or_id
|
|
76
75
|
self.message = message
|
|
@@ -58,7 +58,7 @@ class FolderStructure:
|
|
|
58
58
|
logging.info("Migration report file will be saved at %s", self.migration_reports_file)
|
|
59
59
|
|
|
60
60
|
def setup_migration_file_structure(self, source_file_type: str = ""):
|
|
61
|
-
self.time_stamp = f
|
|
61
|
+
self.time_stamp = f"_{time.strftime('%Y%m%d-%H%M%S')}"
|
|
62
62
|
self.time_str = self.time_stamp if self.add_time_stamp_to_file_names else ""
|
|
63
63
|
self.file_template = f"{self.time_str}_{self.migration_task_name}"
|
|
64
64
|
object_type_string = str(self.object_type.name).lower()
|
folio_migration_tools/helper.py
CHANGED
|
@@ -56,7 +56,7 @@ class Helper:
|
|
|
56
56
|
@staticmethod
|
|
57
57
|
def log_data_issue(index_or_id, message, legacy_value):
|
|
58
58
|
logging.log(26, "DATA ISSUE\t%s\t%s\t%s", index_or_id, message, legacy_value)
|
|
59
|
-
|
|
59
|
+
|
|
60
60
|
@staticmethod
|
|
61
61
|
def log_data_issue_failed(index_or_id, message, legacy_value):
|
|
62
62
|
logging.log(26, "RECORD FAILED\t%s\t%s\t%s", index_or_id, message, legacy_value)
|