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.
- folio_migration_tools/__init__.py +11 -0
- folio_migration_tools/__main__.py +169 -85
- folio_migration_tools/circulation_helper.py +96 -59
- folio_migration_tools/config_file_load.py +66 -0
- folio_migration_tools/custom_dict.py +6 -4
- folio_migration_tools/custom_exceptions.py +21 -19
- folio_migration_tools/extradata_writer.py +46 -0
- folio_migration_tools/folder_structure.py +63 -66
- folio_migration_tools/helper.py +29 -21
- folio_migration_tools/holdings_helper.py +57 -34
- folio_migration_tools/i18n_config.py +9 -0
- folio_migration_tools/library_configuration.py +173 -13
- folio_migration_tools/mapper_base.py +317 -106
- folio_migration_tools/mapping_file_transformation/courses_mapper.py +203 -0
- folio_migration_tools/mapping_file_transformation/holdings_mapper.py +83 -69
- folio_migration_tools/mapping_file_transformation/item_mapper.py +98 -94
- folio_migration_tools/mapping_file_transformation/manual_fee_fines_mapper.py +352 -0
- folio_migration_tools/mapping_file_transformation/mapping_file_mapper_base.py +702 -223
- folio_migration_tools/mapping_file_transformation/notes_mapper.py +90 -0
- folio_migration_tools/mapping_file_transformation/order_mapper.py +492 -0
- folio_migration_tools/mapping_file_transformation/organization_mapper.py +389 -0
- folio_migration_tools/mapping_file_transformation/ref_data_mapping.py +38 -27
- folio_migration_tools/mapping_file_transformation/user_mapper.py +149 -361
- folio_migration_tools/marc_rules_transformation/conditions.py +650 -246
- folio_migration_tools/marc_rules_transformation/holdings_statementsparser.py +292 -130
- folio_migration_tools/marc_rules_transformation/hrid_handler.py +244 -0
- folio_migration_tools/marc_rules_transformation/loc_language_codes.xml +20846 -0
- folio_migration_tools/marc_rules_transformation/marc_file_processor.py +300 -0
- folio_migration_tools/marc_rules_transformation/marc_reader_wrapper.py +136 -0
- folio_migration_tools/marc_rules_transformation/rules_mapper_authorities.py +241 -0
- folio_migration_tools/marc_rules_transformation/rules_mapper_base.py +681 -201
- folio_migration_tools/marc_rules_transformation/rules_mapper_bibs.py +395 -429
- folio_migration_tools/marc_rules_transformation/rules_mapper_holdings.py +531 -100
- folio_migration_tools/migration_report.py +85 -38
- folio_migration_tools/migration_tasks/__init__.py +1 -3
- folio_migration_tools/migration_tasks/authority_transformer.py +119 -0
- folio_migration_tools/migration_tasks/batch_poster.py +911 -198
- folio_migration_tools/migration_tasks/bibs_transformer.py +121 -116
- folio_migration_tools/migration_tasks/courses_migrator.py +192 -0
- folio_migration_tools/migration_tasks/holdings_csv_transformer.py +252 -247
- folio_migration_tools/migration_tasks/holdings_marc_transformer.py +321 -115
- folio_migration_tools/migration_tasks/items_transformer.py +264 -84
- folio_migration_tools/migration_tasks/loans_migrator.py +506 -195
- folio_migration_tools/migration_tasks/manual_fee_fines_transformer.py +187 -0
- folio_migration_tools/migration_tasks/migration_task_base.py +364 -74
- folio_migration_tools/migration_tasks/orders_transformer.py +373 -0
- folio_migration_tools/migration_tasks/organization_transformer.py +451 -0
- folio_migration_tools/migration_tasks/requests_migrator.py +130 -62
- folio_migration_tools/migration_tasks/reserves_migrator.py +253 -0
- folio_migration_tools/migration_tasks/user_transformer.py +180 -139
- folio_migration_tools/task_configuration.py +46 -0
- folio_migration_tools/test_infrastructure/__init__.py +0 -0
- folio_migration_tools/test_infrastructure/mocked_classes.py +406 -0
- folio_migration_tools/transaction_migration/legacy_loan.py +148 -34
- folio_migration_tools/transaction_migration/legacy_request.py +65 -25
- folio_migration_tools/transaction_migration/legacy_reserve.py +47 -0
- folio_migration_tools/transaction_migration/transaction_result.py +12 -1
- folio_migration_tools/translations/en.json +476 -0
- folio_migration_tools-1.9.10.dist-info/METADATA +169 -0
- folio_migration_tools-1.9.10.dist-info/RECORD +67 -0
- {folio_migration_tools-1.2.1.dist-info → folio_migration_tools-1.9.10.dist-info}/WHEEL +1 -2
- folio_migration_tools-1.9.10.dist-info/entry_points.txt +3 -0
- folio_migration_tools/generate_schemas.py +0 -46
- folio_migration_tools/mapping_file_transformation/mapping_file_mapping_base_impl.py +0 -44
- folio_migration_tools/mapping_file_transformation/user_mapper_base.py +0 -212
- folio_migration_tools/marc_rules_transformation/bibs_processor.py +0 -163
- folio_migration_tools/marc_rules_transformation/holdings_processor.py +0 -284
- folio_migration_tools/report_blurbs.py +0 -219
- folio_migration_tools/transaction_migration/legacy_fee_fine.py +0 -36
- folio_migration_tools-1.2.1.dist-info/METADATA +0 -134
- folio_migration_tools-1.2.1.dist-info/RECORD +0 -50
- folio_migration_tools-1.2.1.dist-info/top_level.txt +0 -1
- {folio_migration_tools-1.2.1.dist-info → folio_migration_tools-1.9.10.dist-info/licenses}/LICENSE +0 -0
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import datetime
|
|
2
2
|
import logging
|
|
3
3
|
import uuid
|
|
4
|
+
from zoneinfo import ZoneInfo
|
|
5
|
+
|
|
6
|
+
from dateutil import tz
|
|
4
7
|
from dateutil.parser import parse
|
|
5
8
|
|
|
9
|
+
from folio_migration_tools.custom_exceptions import TransformationRecordFailedError
|
|
10
|
+
|
|
11
|
+
utc = ZoneInfo("UTC")
|
|
12
|
+
|
|
6
13
|
|
|
7
14
|
class LegacyRequest(object):
|
|
8
|
-
def __init__(self, legacy_request_dict,
|
|
15
|
+
def __init__(self, legacy_request_dict, tenant_timezone=utc, row=0):
|
|
9
16
|
# validate
|
|
10
17
|
correct_headers = [
|
|
11
18
|
"item_barcode",
|
|
@@ -26,59 +33,70 @@ class LegacyRequest(object):
|
|
|
26
33
|
|
|
27
34
|
self.item_barcode = legacy_request_dict["item_barcode"].strip()
|
|
28
35
|
self.patron_id = ""
|
|
29
|
-
self.
|
|
36
|
+
self.tenant_timezone = tenant_timezone
|
|
30
37
|
self.item_id = ""
|
|
38
|
+
self.instance_id = ""
|
|
39
|
+
self.holdings_record_id = ""
|
|
31
40
|
self.patron_barcode = legacy_request_dict["patron_barcode"].strip()
|
|
32
41
|
self.comment = legacy_request_dict["comment"].strip()
|
|
33
42
|
self.request_type = legacy_request_dict["request_type"].strip()
|
|
34
|
-
self.pickup_servicepoint_id = legacy_request_dict[
|
|
35
|
-
"pickup_servicepoint_id"
|
|
36
|
-
].strip()
|
|
43
|
+
self.pickup_servicepoint_id = legacy_request_dict["pickup_servicepoint_id"].strip()
|
|
37
44
|
self.fulfillment_preference = "Hold Shelf"
|
|
38
45
|
|
|
39
46
|
if self.request_type not in ["Hold", "Recall", "Page"]:
|
|
40
47
|
self.errors.append((f"{self.request_type} not allowd", "request_type"))
|
|
41
48
|
|
|
42
49
|
try:
|
|
43
|
-
temp_request_date: datetime.datetime = parse(
|
|
44
|
-
|
|
45
|
-
|
|
50
|
+
temp_request_date: datetime.datetime = parse(legacy_request_dict["request_date"])
|
|
51
|
+
if temp_request_date.tzinfo != tz.UTC:
|
|
52
|
+
temp_request_date = temp_request_date.replace(tzinfo=self.tenant_timezone)
|
|
46
53
|
except Exception:
|
|
47
54
|
self.errors.append(("Parse date failure. Setting UTC NOW", "request_date"))
|
|
48
|
-
temp_request_date = datetime.now(
|
|
55
|
+
temp_request_date = datetime.now(ZoneInfo("UTC"))
|
|
49
56
|
try:
|
|
50
57
|
temp_expiration_date: datetime.datetime = parse(
|
|
51
58
|
legacy_request_dict["request_expiration_date"]
|
|
52
59
|
)
|
|
60
|
+
if temp_expiration_date.tzinfo != tz.UTC:
|
|
61
|
+
temp_expiration_date = temp_expiration_date.replace(tzinfo=self.tenant_timezone)
|
|
53
62
|
except Exception:
|
|
54
|
-
temp_expiration_date = datetime.now(
|
|
55
|
-
self.errors.append(
|
|
56
|
-
|
|
57
|
-
)
|
|
63
|
+
temp_expiration_date = datetime.now(ZoneInfo("UTC"))
|
|
64
|
+
self.errors.append(("Parse date failure. Setting UTC NOW", "request_expiration_date"))
|
|
65
|
+
if temp_expiration_date.hour == 0 and temp_expiration_date.minute == 0:
|
|
66
|
+
temp_expiration_date = temp_expiration_date.replace(hour=23, minute=59)
|
|
58
67
|
|
|
59
68
|
self.request_date: datetime.datetime = temp_request_date
|
|
60
69
|
self.request_expiration_date: datetime.datetime = temp_expiration_date
|
|
70
|
+
self.correct_for_1_day_requests()
|
|
71
|
+
|
|
72
|
+
def correct_for_1_day_requests(self):
|
|
61
73
|
try:
|
|
62
|
-
self.
|
|
63
|
-
|
|
64
|
-
|
|
74
|
+
if self.request_expiration_date.date() <= self.request_date.date():
|
|
75
|
+
if (
|
|
76
|
+
self.request_expiration_date.hour == 0
|
|
77
|
+
and self.request_expiration_date.minute == 0
|
|
78
|
+
):
|
|
65
79
|
self.request_expiration_date = self.request_expiration_date.replace(
|
|
66
80
|
hour=23, minute=59
|
|
67
81
|
)
|
|
68
|
-
if self.request_date.hour == 0:
|
|
82
|
+
if self.request_date.hour == 0 and self.request_date.minute == 0:
|
|
69
83
|
self.request_date = self.request_date.replace(hour=0, minute=1)
|
|
84
|
+
self.make_request_utc()
|
|
70
85
|
except Exception as ee:
|
|
71
86
|
logging.error(ee)
|
|
72
87
|
self.errors.append(("Time alignment issues", "both dates"))
|
|
73
88
|
|
|
74
89
|
def to_dict(self):
|
|
75
90
|
return {
|
|
91
|
+
"requestLevel": "Item",
|
|
76
92
|
"requestType": self.request_type,
|
|
77
93
|
"fulfilmentPreference": self.fulfillment_preference,
|
|
78
94
|
"requester": {"barcode": self.patron_barcode},
|
|
79
95
|
"requesterId": self.patron_id,
|
|
80
96
|
"item": {"barcode": self.item_barcode},
|
|
81
97
|
"itemId": self.item_id,
|
|
98
|
+
"instanceId": self.instance_id,
|
|
99
|
+
"holdingsRecordId": self.holdings_record_id,
|
|
82
100
|
"requestExpirationDate": self.request_expiration_date.isoformat(),
|
|
83
101
|
"patronComments": self.comment,
|
|
84
102
|
"pickupServicePointId": self.pickup_servicepoint_id,
|
|
@@ -86,6 +104,28 @@ class LegacyRequest(object):
|
|
|
86
104
|
"id": str(uuid.uuid4()),
|
|
87
105
|
}
|
|
88
106
|
|
|
107
|
+
def serialize(self):
|
|
108
|
+
req = self.to_dict()
|
|
109
|
+
required = [
|
|
110
|
+
"instanceId",
|
|
111
|
+
"requesterId",
|
|
112
|
+
"requestType",
|
|
113
|
+
"requestLevel",
|
|
114
|
+
"requestDate",
|
|
115
|
+
"holdingsRecordId",
|
|
116
|
+
"itemId",
|
|
117
|
+
"fulfilmentPreference",
|
|
118
|
+
"pickupServicePointId",
|
|
119
|
+
]
|
|
120
|
+
if req["requestLevel"] == "Title":
|
|
121
|
+
required = [r for r in required if r not in ["itemId", "holdingsRecordId"]]
|
|
122
|
+
missing = [r for r in required if not req.get(r, "")]
|
|
123
|
+
if any(missing):
|
|
124
|
+
raise TransformationRecordFailedError(
|
|
125
|
+
"", "Required properties missing:" ", ".join(missing)
|
|
126
|
+
)
|
|
127
|
+
return req
|
|
128
|
+
|
|
89
129
|
def to_source_dict(self):
|
|
90
130
|
return {
|
|
91
131
|
"item_barcode": self.item_barcode,
|
|
@@ -98,11 +138,11 @@ class LegacyRequest(object):
|
|
|
98
138
|
}
|
|
99
139
|
|
|
100
140
|
def make_request_utc(self):
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
)
|
|
141
|
+
try:
|
|
142
|
+
if self.tenant_timezone != ZoneInfo("UTC"):
|
|
143
|
+
self.request_date = self.request_date.astimezone(ZoneInfo("UTC"))
|
|
144
|
+
self.request_expiration_date = self.request_expiration_date.astimezone(
|
|
145
|
+
ZoneInfo("UTC")
|
|
146
|
+
)
|
|
147
|
+
except Exception:
|
|
148
|
+
self.errors.append(("UTC correction issues", "both dates"))
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from typing import Dict, List, Tuple
|
|
3
|
+
|
|
4
|
+
from folio_uuid.folio_namespaces import FOLIONamespaces
|
|
5
|
+
from folio_uuid.folio_uuid import FolioUUID
|
|
6
|
+
from folioclient import FolioClient
|
|
7
|
+
|
|
8
|
+
from folio_migration_tools.custom_exceptions import TransformationProcessError
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class LegacyReserve(object):
|
|
12
|
+
def __init__(self, legacy_request_dict: Dict, folio_client: FolioClient, row: int = 0):
|
|
13
|
+
# validate
|
|
14
|
+
correct_headers = ["legacy_identifier", "item_barcode"]
|
|
15
|
+
for h in correct_headers:
|
|
16
|
+
if h not in legacy_request_dict:
|
|
17
|
+
raise TransformationProcessError(
|
|
18
|
+
row,
|
|
19
|
+
"Missing header in file. The following are required:",
|
|
20
|
+
", ".join(correct_headers),
|
|
21
|
+
)
|
|
22
|
+
self.errors: List[Tuple[str, str]] = [
|
|
23
|
+
("Missing properties in legacy data", prop)
|
|
24
|
+
for prop in correct_headers
|
|
25
|
+
if prop not in legacy_request_dict
|
|
26
|
+
]
|
|
27
|
+
self.id = str(uuid.uuid4())
|
|
28
|
+
self.item_barcode: str = legacy_request_dict["item_barcode"].strip()
|
|
29
|
+
if not self.item_barcode:
|
|
30
|
+
self.errors.append(("Missing data.", "item_barcode"))
|
|
31
|
+
self.legacy_identifier: str = legacy_request_dict["legacy_identifier"].strip()
|
|
32
|
+
if not self.legacy_identifier:
|
|
33
|
+
self.errors.append(("Missing data.", "legacy_identifier"))
|
|
34
|
+
self.course_listing_id: str = str(
|
|
35
|
+
FolioUUID(
|
|
36
|
+
folio_client.gateway_url,
|
|
37
|
+
FOLIONamespaces.course_listing,
|
|
38
|
+
legacy_request_dict["legacy_identifier"],
|
|
39
|
+
)
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
def to_dict(self):
|
|
43
|
+
return {
|
|
44
|
+
"courseListingId": self.course_listing_id,
|
|
45
|
+
"copiedItem": {"barcode": self.item_barcode},
|
|
46
|
+
"id": self.id,
|
|
47
|
+
}
|
|
@@ -1,9 +1,20 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
|
|
1
4
|
class TransactionResult(object):
|
|
5
|
+
__slots__ = [
|
|
6
|
+
"was_successful",
|
|
7
|
+
"folio_loan",
|
|
8
|
+
"should_be_retried",
|
|
9
|
+
"error_message",
|
|
10
|
+
"migration_report_message",
|
|
11
|
+
]
|
|
12
|
+
|
|
2
13
|
def __init__(
|
|
3
14
|
self,
|
|
4
15
|
was_successful: bool,
|
|
5
16
|
should_be_retried: bool,
|
|
6
|
-
folio_loan:
|
|
17
|
+
folio_loan: Any,
|
|
7
18
|
error_message: str,
|
|
8
19
|
migration_report_message: str,
|
|
9
20
|
):
|