folio-migration-tools 1.10.2__py3-none-any.whl → 1.10.3__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 +10 -2
- folio_migration_tools/__main__.py +7 -0
- folio_migration_tools/circulation_helper.py +23 -8
- folio_migration_tools/colors.py +7 -0
- folio_migration_tools/config_file_load.py +7 -0
- folio_migration_tools/custom_dict.py +17 -0
- folio_migration_tools/custom_exceptions.py +40 -4
- folio_migration_tools/extradata_writer.py +12 -0
- folio_migration_tools/folder_structure.py +16 -0
- folio_migration_tools/helper.py +7 -0
- folio_migration_tools/holdings_helper.py +11 -5
- folio_migration_tools/i18n_config.py +6 -0
- folio_migration_tools/library_configuration.py +19 -5
- folio_migration_tools/mapper_base.py +15 -0
- folio_migration_tools/mapping_file_transformation/__init__.py +1 -0
- folio_migration_tools/mapping_file_transformation/courses_mapper.py +17 -0
- folio_migration_tools/mapping_file_transformation/holdings_mapper.py +19 -0
- folio_migration_tools/mapping_file_transformation/item_mapper.py +24 -0
- folio_migration_tools/mapping_file_transformation/manual_fee_fines_mapper.py +18 -0
- folio_migration_tools/mapping_file_transformation/mapping_file_mapper_base.py +26 -9
- folio_migration_tools/mapping_file_transformation/notes_mapper.py +16 -0
- folio_migration_tools/mapping_file_transformation/order_mapper.py +40 -27
- folio_migration_tools/mapping_file_transformation/organization_mapper.py +40 -33
- folio_migration_tools/mapping_file_transformation/ref_data_mapping.py +17 -0
- folio_migration_tools/mapping_file_transformation/user_mapper.py +16 -0
- folio_migration_tools/marc_rules_transformation/__init__.py +1 -0
- folio_migration_tools/marc_rules_transformation/conditions.py +49 -36
- folio_migration_tools/marc_rules_transformation/holdings_statementsparser.py +9 -3
- folio_migration_tools/marc_rules_transformation/hrid_handler.py +16 -1
- folio_migration_tools/marc_rules_transformation/marc_file_processor.py +15 -1
- folio_migration_tools/marc_rules_transformation/marc_reader_wrapper.py +7 -0
- folio_migration_tools/marc_rules_transformation/rules_mapper_base.py +35 -29
- folio_migration_tools/marc_rules_transformation/rules_mapper_bibs.py +23 -18
- folio_migration_tools/marc_rules_transformation/rules_mapper_holdings.py +46 -27
- folio_migration_tools/migration_report.py +14 -6
- folio_migration_tools/migration_tasks/__init__.py +2 -0
- folio_migration_tools/migration_tasks/batch_poster.py +34 -18
- folio_migration_tools/migration_tasks/bibs_transformer.py +16 -0
- folio_migration_tools/migration_tasks/courses_migrator.py +15 -0
- folio_migration_tools/migration_tasks/holdings_csv_transformer.py +18 -3
- folio_migration_tools/migration_tasks/holdings_marc_transformer.py +17 -0
- folio_migration_tools/migration_tasks/inventory_batch_poster.py +424 -0
- folio_migration_tools/migration_tasks/items_transformer.py +16 -0
- folio_migration_tools/migration_tasks/loans_migrator.py +17 -2
- folio_migration_tools/migration_tasks/manual_fee_fines_transformer.py +16 -0
- folio_migration_tools/migration_tasks/marc_import.py +407 -0
- folio_migration_tools/migration_tasks/migration_task_base.py +49 -17
- folio_migration_tools/migration_tasks/orders_transformer.py +16 -0
- folio_migration_tools/migration_tasks/organization_transformer.py +17 -2
- folio_migration_tools/migration_tasks/requests_migrator.py +15 -0
- folio_migration_tools/migration_tasks/reserves_migrator.py +15 -0
- folio_migration_tools/migration_tasks/user_importer.py +347 -0
- folio_migration_tools/migration_tasks/user_transformer.py +16 -0
- folio_migration_tools/task_configuration.py +7 -0
- folio_migration_tools/transaction_migration/__init__.py +1 -0
- folio_migration_tools/transaction_migration/legacy_loan.py +16 -0
- folio_migration_tools/transaction_migration/legacy_request.py +14 -0
- folio_migration_tools/transaction_migration/legacy_reserve.py +14 -0
- folio_migration_tools/transaction_migration/transaction_result.py +16 -0
- {folio_migration_tools-1.10.2.dist-info → folio_migration_tools-1.10.3.dist-info}/METADATA +1 -1
- folio_migration_tools-1.10.3.dist-info/RECORD +66 -0
- folio_migration_tools-1.10.2.dist-info/RECORD +0 -63
- {folio_migration_tools-1.10.2.dist-info → folio_migration_tools-1.10.3.dist-info}/WHEEL +0 -0
- {folio_migration_tools-1.10.2.dist-info → folio_migration_tools-1.10.3.dist-info}/entry_points.txt +0 -0
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
"""Course reserves migration task.
|
|
2
|
+
|
|
3
|
+
Migrates course reserve records from legacy ILS to FOLIO Course Reserves module.
|
|
4
|
+
Handles course listings, items on reserve, and reserve relationships.
|
|
5
|
+
"""
|
|
6
|
+
|
|
1
7
|
import csv
|
|
2
8
|
import json
|
|
3
9
|
import logging
|
|
@@ -27,6 +33,8 @@ from folio_migration_tools.transaction_migration.legacy_reserve import LegacyRes
|
|
|
27
33
|
|
|
28
34
|
class ReservesMigrator(MigrationTaskBase):
|
|
29
35
|
class TaskConfiguration(AbstractTaskConfiguration):
|
|
36
|
+
"""Task configuration for ReservesMigrator."""
|
|
37
|
+
|
|
30
38
|
name: Annotated[
|
|
31
39
|
str,
|
|
32
40
|
Field(
|
|
@@ -62,6 +70,13 @@ class ReservesMigrator(MigrationTaskBase):
|
|
|
62
70
|
library_config: LibraryConfiguration,
|
|
63
71
|
folio_client,
|
|
64
72
|
):
|
|
73
|
+
"""Initialize ReservesMigrator for migrating course reserves.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
task_configuration (TaskConfiguration): Reserves migration configuration.
|
|
77
|
+
library_config (LibraryConfiguration): Library configuration.
|
|
78
|
+
folio_client: FOLIO API client.
|
|
79
|
+
"""
|
|
65
80
|
csv.register_dialect("tsv", delimiter="\t")
|
|
66
81
|
self.migration_report = MigrationReport()
|
|
67
82
|
self.valid_reserves = []
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
"""UserImporterTask module for FOLIO user import operations.
|
|
2
|
+
|
|
3
|
+
This module provides an adapter that wraps folio_data_import.UserImporter
|
|
4
|
+
to conform to the folio_migration_tools MigrationTaskBase interface.
|
|
5
|
+
It supports importing users with full relationship handling including
|
|
6
|
+
request preferences, permission users, and service points.
|
|
7
|
+
|
|
8
|
+
This provides an alternative to posting users via BatchPoster with the
|
|
9
|
+
/user-import endpoint, offering more granular control and better error handling.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import asyncio
|
|
13
|
+
import logging
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Annotated, List, Literal
|
|
16
|
+
|
|
17
|
+
from folio_uuid.folio_namespaces import FOLIONamespaces
|
|
18
|
+
from pydantic import Field
|
|
19
|
+
|
|
20
|
+
from folio_data_import.UserImport import UserImporter as FDIUserImporter
|
|
21
|
+
from folio_data_import.UserImport import UserImporterStats
|
|
22
|
+
|
|
23
|
+
from folio_migration_tools.library_configuration import (
|
|
24
|
+
FileDefinition,
|
|
25
|
+
LibraryConfiguration,
|
|
26
|
+
)
|
|
27
|
+
from folio_migration_tools.migration_report import MigrationReport
|
|
28
|
+
from folio_migration_tools.migration_tasks.migration_task_base import MigrationTaskBase
|
|
29
|
+
from folio_migration_tools.task_configuration import AbstractTaskConfiguration
|
|
30
|
+
from folio_data_import._progress import RichProgressReporter
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class UserImportTask(MigrationTaskBase):
|
|
34
|
+
"""A wrapper for folio_data_import.UserImporter.
|
|
35
|
+
|
|
36
|
+
This class adapts the UserImporter from folio_data_import to fit within the
|
|
37
|
+
folio_migration_tools MigrationTaskBase framework.
|
|
38
|
+
|
|
39
|
+
This implementation handles:
|
|
40
|
+
- User create/update with full upsert support
|
|
41
|
+
- Automatic mapping of patron groups, address types, departments, service points
|
|
42
|
+
- Creation/update of request preferences
|
|
43
|
+
- Creation of permission users
|
|
44
|
+
- Creation/update of service points users
|
|
45
|
+
- Field protection to prevent overwriting specific fields
|
|
46
|
+
|
|
47
|
+
Parents:
|
|
48
|
+
MigrationTaskBase: Base class for all migration tasks
|
|
49
|
+
|
|
50
|
+
Raises:
|
|
51
|
+
TransformationProcessError: When a critical error occurs during processing
|
|
52
|
+
FileNotFoundError: When input files are not found
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
class TaskConfiguration(AbstractTaskConfiguration):
|
|
56
|
+
"""Task configuration for UserImporter."""
|
|
57
|
+
|
|
58
|
+
name: Annotated[
|
|
59
|
+
str,
|
|
60
|
+
Field(
|
|
61
|
+
title="Task name",
|
|
62
|
+
description="The name of the task",
|
|
63
|
+
),
|
|
64
|
+
]
|
|
65
|
+
migration_task_type: Annotated[
|
|
66
|
+
str,
|
|
67
|
+
Field(
|
|
68
|
+
title="Migration task type",
|
|
69
|
+
description="The type of migration task",
|
|
70
|
+
),
|
|
71
|
+
]
|
|
72
|
+
files: Annotated[
|
|
73
|
+
List[FileDefinition],
|
|
74
|
+
Field(
|
|
75
|
+
title="List of files",
|
|
76
|
+
description=(
|
|
77
|
+
"List of JSONL files to be processed. These should be output files "
|
|
78
|
+
"from UserTransformer containing mod-user-import compatible objects."
|
|
79
|
+
),
|
|
80
|
+
),
|
|
81
|
+
]
|
|
82
|
+
batch_size: Annotated[
|
|
83
|
+
int,
|
|
84
|
+
Field(
|
|
85
|
+
title="Batch size",
|
|
86
|
+
description="Number of users to process concurrently in each batch",
|
|
87
|
+
ge=1,
|
|
88
|
+
le=1000,
|
|
89
|
+
),
|
|
90
|
+
] = 250
|
|
91
|
+
user_match_key: Annotated[
|
|
92
|
+
Literal["externalSystemId", "username", "barcode"],
|
|
93
|
+
Field(
|
|
94
|
+
title="User match key",
|
|
95
|
+
description=(
|
|
96
|
+
"The key to use for matching existing users during upsert. "
|
|
97
|
+
"Users with matching values will be updated rather than created."
|
|
98
|
+
),
|
|
99
|
+
),
|
|
100
|
+
] = "externalSystemId"
|
|
101
|
+
only_update_present_fields: Annotated[
|
|
102
|
+
bool,
|
|
103
|
+
Field(
|
|
104
|
+
title="Only update present fields",
|
|
105
|
+
description=(
|
|
106
|
+
"When enabled, only fields present in the input will be updated. "
|
|
107
|
+
"Missing fields will be left unchanged in existing records. "
|
|
108
|
+
"When disabled, missing fields may be cleared."
|
|
109
|
+
),
|
|
110
|
+
),
|
|
111
|
+
] = False
|
|
112
|
+
default_preferred_contact_type: Annotated[
|
|
113
|
+
Literal["001", "002", "003", "004", "005", "mail", "email", "text", "phone", "mobile"],
|
|
114
|
+
Field(
|
|
115
|
+
title="Default preferred contact type",
|
|
116
|
+
description=(
|
|
117
|
+
"Default preferred contact type for users. "
|
|
118
|
+
"Can be specified as ID (001-005) or name (mail/email/text/phone/mobile). "
|
|
119
|
+
"Will be applied to users without a valid value already set."
|
|
120
|
+
),
|
|
121
|
+
),
|
|
122
|
+
] = "002"
|
|
123
|
+
fields_to_protect: Annotated[
|
|
124
|
+
List[str],
|
|
125
|
+
Field(
|
|
126
|
+
title="Fields to protect",
|
|
127
|
+
description=(
|
|
128
|
+
"List of field paths to protect from updates "
|
|
129
|
+
"(e.g., ['personal.email', 'barcode']). "
|
|
130
|
+
"Protected fields will not be modified during updates."
|
|
131
|
+
),
|
|
132
|
+
),
|
|
133
|
+
] = []
|
|
134
|
+
limit_simultaneous_requests: Annotated[
|
|
135
|
+
int,
|
|
136
|
+
Field(
|
|
137
|
+
title="Limit simultaneous requests",
|
|
138
|
+
description="Maximum number of concurrent async HTTP requests",
|
|
139
|
+
ge=1,
|
|
140
|
+
le=100,
|
|
141
|
+
),
|
|
142
|
+
] = 10
|
|
143
|
+
no_progress: Annotated[
|
|
144
|
+
bool,
|
|
145
|
+
Field(
|
|
146
|
+
title="No progress",
|
|
147
|
+
description="Disable progress reporting in the console output.",
|
|
148
|
+
),
|
|
149
|
+
] = False
|
|
150
|
+
|
|
151
|
+
task_configuration: TaskConfiguration
|
|
152
|
+
|
|
153
|
+
@staticmethod
|
|
154
|
+
def get_object_type() -> FOLIONamespaces:
|
|
155
|
+
return FOLIONamespaces.users
|
|
156
|
+
|
|
157
|
+
def __init__(
|
|
158
|
+
self,
|
|
159
|
+
task_config: TaskConfiguration,
|
|
160
|
+
library_config: LibraryConfiguration,
|
|
161
|
+
folio_client,
|
|
162
|
+
use_logging: bool = True,
|
|
163
|
+
):
|
|
164
|
+
"""Initialize UserImporter for bulk user import via /users APIs.
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
task_config (TaskConfiguration): User import configuration.
|
|
168
|
+
library_config (LibraryConfiguration): Library configuration.
|
|
169
|
+
folio_client: FOLIO API client.
|
|
170
|
+
use_logging (bool): Whether to set up task logging.
|
|
171
|
+
"""
|
|
172
|
+
super().__init__(library_config, task_config, folio_client, use_logging)
|
|
173
|
+
self.migration_report = MigrationReport()
|
|
174
|
+
self.stats: UserImporterStats = UserImporterStats()
|
|
175
|
+
self.total_records = 0
|
|
176
|
+
self.files_processed: List[str] = []
|
|
177
|
+
|
|
178
|
+
logging.info("UserImporterTask initialized")
|
|
179
|
+
logging.info("Batch size: %s", self.task_configuration.batch_size)
|
|
180
|
+
logging.info("User match key: %s", self.task_configuration.user_match_key)
|
|
181
|
+
|
|
182
|
+
def _create_fdi_config(self, file_paths: List[Path]) -> FDIUserImporter.Config:
|
|
183
|
+
"""Create a folio_data_import.UserImporter.Config from our TaskConfiguration.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
file_paths: List of file paths to process
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
FDIUserImporter.Config: Configuration for the underlying UserImporter
|
|
190
|
+
"""
|
|
191
|
+
return FDIUserImporter.Config(
|
|
192
|
+
library_name=self.library_configuration.library_name,
|
|
193
|
+
batch_size=self.task_configuration.batch_size,
|
|
194
|
+
user_match_key=self.task_configuration.user_match_key,
|
|
195
|
+
only_update_present_fields=self.task_configuration.only_update_present_fields,
|
|
196
|
+
default_preferred_contact_type=self.task_configuration.default_preferred_contact_type,
|
|
197
|
+
fields_to_protect=self.task_configuration.fields_to_protect,
|
|
198
|
+
limit_simultaneous_requests=self.task_configuration.limit_simultaneous_requests,
|
|
199
|
+
user_file_paths=file_paths,
|
|
200
|
+
no_progress=self.task_configuration.no_progress,
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
async def _do_work_async(self) -> None:
|
|
204
|
+
"""Async implementation of the work logic."""
|
|
205
|
+
# Build list of file paths
|
|
206
|
+
file_paths: List[Path] = []
|
|
207
|
+
for file_def in self.task_configuration.files:
|
|
208
|
+
path = self.folder_structure.results_folder / file_def.file_name
|
|
209
|
+
if not path.exists():
|
|
210
|
+
logging.error("File not found: %s", path)
|
|
211
|
+
raise FileNotFoundError(f"File not found: {path}")
|
|
212
|
+
file_paths.append(path)
|
|
213
|
+
self.files_processed.append(file_def.file_name)
|
|
214
|
+
logging.info("Will process file: %s", path)
|
|
215
|
+
|
|
216
|
+
# Count total records for reporting
|
|
217
|
+
for file_path in file_paths:
|
|
218
|
+
with open(file_path, "rb") as f:
|
|
219
|
+
self.total_records += sum(
|
|
220
|
+
buf.count(b"\n") for buf in iter(lambda: f.read(1024 * 1024), b"")
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
# Create the folio_data_import UserImporter config
|
|
224
|
+
fdi_config = self._create_fdi_config(file_paths)
|
|
225
|
+
|
|
226
|
+
# Create Progress Reporter
|
|
227
|
+
if self.task_configuration.no_progress:
|
|
228
|
+
from folio_data_import._progress import NoOpProgressReporter
|
|
229
|
+
|
|
230
|
+
reporter = NoOpProgressReporter()
|
|
231
|
+
else:
|
|
232
|
+
reporter = RichProgressReporter(enabled=True)
|
|
233
|
+
|
|
234
|
+
# Error file path
|
|
235
|
+
error_file_path = self.folder_structure.failed_recs_path
|
|
236
|
+
|
|
237
|
+
# Create and run the importer
|
|
238
|
+
importer = FDIUserImporter(
|
|
239
|
+
folio_client=self.folio_client,
|
|
240
|
+
config=fdi_config,
|
|
241
|
+
reporter=reporter,
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
await importer.setup(error_file_path)
|
|
245
|
+
|
|
246
|
+
try:
|
|
247
|
+
await importer.do_import()
|
|
248
|
+
self.stats = importer.stats
|
|
249
|
+
finally:
|
|
250
|
+
await importer.close()
|
|
251
|
+
|
|
252
|
+
def do_work(self) -> None:
|
|
253
|
+
"""Main work method that processes files and imports users to FOLIO.
|
|
254
|
+
|
|
255
|
+
This method reads user records from the configured files and imports them
|
|
256
|
+
to FOLIO using the folio_data_import.UserImporter, handling all related
|
|
257
|
+
objects (request preferences, permission users, service points).
|
|
258
|
+
"""
|
|
259
|
+
logging.info("Starting UserImportTask work...")
|
|
260
|
+
|
|
261
|
+
try:
|
|
262
|
+
# Run the async work in an event loop
|
|
263
|
+
asyncio.run(self._do_work_async())
|
|
264
|
+
except FileNotFoundError as e:
|
|
265
|
+
logging.error("File not found: %s", e)
|
|
266
|
+
raise
|
|
267
|
+
except Exception as e:
|
|
268
|
+
logging.error("Error during user import: %s", e)
|
|
269
|
+
raise
|
|
270
|
+
|
|
271
|
+
logging.info("UserImportTask work complete")
|
|
272
|
+
|
|
273
|
+
def _translate_stats_to_migration_report(self) -> None:
|
|
274
|
+
"""Translate UserImporterStats to MigrationReport format."""
|
|
275
|
+
# General statistics
|
|
276
|
+
total_processed = self.stats.created + self.stats.updated + self.stats.failed
|
|
277
|
+
self.migration_report.set(
|
|
278
|
+
"GeneralStatistics",
|
|
279
|
+
"Total records in files",
|
|
280
|
+
self.total_records,
|
|
281
|
+
)
|
|
282
|
+
self.migration_report.set(
|
|
283
|
+
"GeneralStatistics",
|
|
284
|
+
"Records processed",
|
|
285
|
+
total_processed,
|
|
286
|
+
)
|
|
287
|
+
self.migration_report.set(
|
|
288
|
+
"GeneralStatistics",
|
|
289
|
+
"Users created",
|
|
290
|
+
self.stats.created,
|
|
291
|
+
)
|
|
292
|
+
self.migration_report.set(
|
|
293
|
+
"GeneralStatistics",
|
|
294
|
+
"Users updated",
|
|
295
|
+
self.stats.updated,
|
|
296
|
+
)
|
|
297
|
+
self.migration_report.set(
|
|
298
|
+
"GeneralStatistics",
|
|
299
|
+
"Users failed",
|
|
300
|
+
self.stats.failed,
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
# Add file information
|
|
304
|
+
for file_name in self.files_processed:
|
|
305
|
+
self.migration_report.add("FilesProcessed", file_name)
|
|
306
|
+
|
|
307
|
+
def wrap_up(self) -> None:
|
|
308
|
+
"""Finalize the migration task and write reports.
|
|
309
|
+
|
|
310
|
+
This method translates statistics from the underlying UserImporter
|
|
311
|
+
to the MigrationReport format and writes both markdown and JSON reports.
|
|
312
|
+
"""
|
|
313
|
+
logging.info("Done. Wrapping up UserImportTask")
|
|
314
|
+
|
|
315
|
+
# Translate stats to migration report
|
|
316
|
+
self._translate_stats_to_migration_report()
|
|
317
|
+
|
|
318
|
+
# Log summary
|
|
319
|
+
logging.info("=" * 60)
|
|
320
|
+
logging.info("UserImportTask Summary")
|
|
321
|
+
logging.info("=" * 60)
|
|
322
|
+
logging.info("Total records in files: %d", self.total_records)
|
|
323
|
+
logging.info("Users created: %d", self.stats.created)
|
|
324
|
+
logging.info("Users updated: %d", self.stats.updated)
|
|
325
|
+
logging.info("Users failed: %d", self.stats.failed)
|
|
326
|
+
if self.stats.failed > 0:
|
|
327
|
+
logging.info(
|
|
328
|
+
"Failed users written to: %s",
|
|
329
|
+
self.folder_structure.failed_recs_path,
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
# Write markdown report
|
|
333
|
+
with open(self.folder_structure.migration_reports_file, "w+") as report_file:
|
|
334
|
+
self.migration_report.write_migration_report(
|
|
335
|
+
"User import report",
|
|
336
|
+
report_file,
|
|
337
|
+
self.start_datetime,
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
# Write raw JSON report
|
|
341
|
+
with open(self.folder_structure.migration_reports_raw_file, "w") as raw_report_file:
|
|
342
|
+
self.migration_report.write_json_report(raw_report_file)
|
|
343
|
+
|
|
344
|
+
# Clean up empty log files
|
|
345
|
+
self.clean_out_empty_logs()
|
|
346
|
+
|
|
347
|
+
logging.info("UserImportTask wrap up complete")
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
"""User/patron records transformation task.
|
|
2
|
+
|
|
3
|
+
Transforms user/patron data from CSV files to FOLIO Users. Handles patron groups,
|
|
4
|
+
addresses, departments, and user permissions with validation and cleanup.
|
|
5
|
+
"""
|
|
6
|
+
|
|
1
7
|
import json
|
|
2
8
|
import logging
|
|
3
9
|
import sys
|
|
@@ -27,6 +33,8 @@ from folio_migration_tools.task_configuration import AbstractTaskConfiguration
|
|
|
27
33
|
|
|
28
34
|
class UserTransformer(MigrationTaskBase):
|
|
29
35
|
class TaskConfiguration(AbstractTaskConfiguration):
|
|
36
|
+
"""Task configuration for UserTransformer."""
|
|
37
|
+
|
|
30
38
|
name: Annotated[
|
|
31
39
|
str,
|
|
32
40
|
Field(
|
|
@@ -123,6 +131,14 @@ class UserTransformer(MigrationTaskBase):
|
|
|
123
131
|
folio_client,
|
|
124
132
|
use_logging: bool = True,
|
|
125
133
|
):
|
|
134
|
+
"""Initialize UserTransformer for user record transformations.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
task_config (TaskConfiguration): Users transformation configuration.
|
|
138
|
+
library_config (LibraryConfiguration): Library configuration.
|
|
139
|
+
folio_client: FOLIO API client.
|
|
140
|
+
use_logging (bool): Whether to set up task logging.
|
|
141
|
+
"""
|
|
126
142
|
super().__init__(library_config, task_config, folio_client, use_logging)
|
|
127
143
|
self.task_config = task_config
|
|
128
144
|
self.task_configuration = self.task_config
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
"""Base task configuration model.
|
|
2
|
+
|
|
3
|
+
Defines the abstract AbstractTaskConfiguration class that all migration task
|
|
4
|
+
configurations inherit from. Provides common configuration fields and utilities
|
|
5
|
+
for camelCase conversion for FOLIO API compatibility.
|
|
6
|
+
"""
|
|
7
|
+
|
|
1
8
|
from typing import Annotated
|
|
2
9
|
|
|
3
10
|
from humps import camelize
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Transaction migration for loans, requests, and other circulation data."""
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
"""Legacy loan data model and validation.
|
|
2
|
+
|
|
3
|
+
Defines the LegacyLoan class for representing circulation loans from legacy ILS systems.
|
|
4
|
+
Handles validation, timezone conversion, date normalization, and transformation to FOLIO
|
|
5
|
+
loan format. Supports renewal counts and loan policy mapping.
|
|
6
|
+
"""
|
|
7
|
+
|
|
1
8
|
import json
|
|
2
9
|
import logging
|
|
3
10
|
import i18n
|
|
@@ -23,6 +30,15 @@ class LegacyLoan(object):
|
|
|
23
30
|
tenant_timezone=utc,
|
|
24
31
|
row=0,
|
|
25
32
|
):
|
|
33
|
+
"""Initialize LegacyLoan from legacy loan data.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
legacy_loan_dict: Dictionary containing legacy loan data.
|
|
37
|
+
fallback_service_point_id (str): Service point to use if not specified.
|
|
38
|
+
migration_report (MigrationReport): Report for tracking issues.
|
|
39
|
+
tenant_timezone: Timezone of the tenant (default: UTC).
|
|
40
|
+
row (int): Row number in source data for error reporting.
|
|
41
|
+
"""
|
|
26
42
|
self.migration_report: MigrationReport = migration_report
|
|
27
43
|
# validate
|
|
28
44
|
correct_headers = [
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
"""Legacy request data model and validation.
|
|
2
|
+
|
|
3
|
+
Defines the LegacyRequest class for representing patron requests from legacy ILS systems.
|
|
4
|
+
Handles validation, timezone conversion, request type mapping, and transformation to FOLIO
|
|
5
|
+
request format. Supports hold queue positioning and expiration dates.
|
|
6
|
+
"""
|
|
7
|
+
|
|
1
8
|
import datetime
|
|
2
9
|
import logging
|
|
3
10
|
import uuid
|
|
@@ -13,6 +20,13 @@ utc = ZoneInfo("UTC")
|
|
|
13
20
|
|
|
14
21
|
class LegacyRequest(object):
|
|
15
22
|
def __init__(self, legacy_request_dict, tenant_timezone=utc, row=0):
|
|
23
|
+
"""Initialize LegacyRequest from legacy request data.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
legacy_request_dict: Dictionary containing legacy request data.
|
|
27
|
+
tenant_timezone: Timezone of the tenant (default: UTC).
|
|
28
|
+
row (int): Row number in source data for error reporting.
|
|
29
|
+
"""
|
|
16
30
|
# validate
|
|
17
31
|
correct_headers = [
|
|
18
32
|
"item_barcode",
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
"""Legacy course reserve data model and validation.
|
|
2
|
+
|
|
3
|
+
Defines the LegacyReserve class for representing course reserves from legacy ILS systems.
|
|
4
|
+
Handles validation, course listing lookups, and transformation to FOLIO course reserve
|
|
5
|
+
format. Links items to course listings via barcode lookups.
|
|
6
|
+
"""
|
|
7
|
+
|
|
1
8
|
import uuid
|
|
2
9
|
from typing import Dict, List, Tuple
|
|
3
10
|
|
|
@@ -10,6 +17,13 @@ from folio_migration_tools.custom_exceptions import TransformationProcessError
|
|
|
10
17
|
|
|
11
18
|
class LegacyReserve(object):
|
|
12
19
|
def __init__(self, legacy_request_dict: Dict, folio_client: FolioClient, row: int = 0):
|
|
20
|
+
"""Initialize LegacyReserve from legacy reserve data.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
legacy_request_dict (Dict): Dictionary containing legacy reserve data.
|
|
24
|
+
folio_client (FolioClient): FOLIO API client for lookups.
|
|
25
|
+
row (int): Row number in source data for error reporting.
|
|
26
|
+
"""
|
|
13
27
|
# validate
|
|
14
28
|
correct_headers = ["legacy_identifier", "item_barcode"]
|
|
15
29
|
for h in correct_headers:
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
"""Transaction result container for migration operations.
|
|
2
|
+
|
|
3
|
+
Defines the TransactionResult class for encapsulating the outcome of transaction
|
|
4
|
+
migration attempts. Tracks success/failure status, error messages, and whether
|
|
5
|
+
retries should be attempted.
|
|
6
|
+
"""
|
|
7
|
+
|
|
1
8
|
from typing import Any
|
|
2
9
|
|
|
3
10
|
|
|
@@ -18,6 +25,15 @@ class TransactionResult(object):
|
|
|
18
25
|
error_message: str,
|
|
19
26
|
migration_report_message: str,
|
|
20
27
|
):
|
|
28
|
+
"""Initialize TransactionResult for migration tracking.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
was_successful (bool): Whether the transaction was successfully created.
|
|
32
|
+
should_be_retried (bool): Whether a failed transaction should be retried.
|
|
33
|
+
folio_loan (Any): The created FOLIO transaction object.
|
|
34
|
+
error_message (str): Error message if transaction failed.
|
|
35
|
+
migration_report_message (str): Message for migration report.
|
|
36
|
+
"""
|
|
21
37
|
self.was_successful = was_successful
|
|
22
38
|
self.folio_loan = folio_loan
|
|
23
39
|
self.should_be_retried = should_be_retried
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: folio-migration-tools
|
|
3
|
-
Version: 1.10.
|
|
3
|
+
Version: 1.10.3
|
|
4
4
|
Summary: A tool allowing you to migrate data from legacy ILS:s (Library systems) into FOLIO LSP
|
|
5
5
|
Keywords: FOLIO,ILS,LSP,Library Systems,MARC21,Library data
|
|
6
6
|
Author: Theodor Tolstoy, Lisa Sjögren, Brooks Travis, Jeremy Nelson, Clinton Bradford
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
folio_migration_tools/__init__.py,sha256=FFvY3IjvJnP0h6j8Qff49iqpzV1hXFqZbBiDip0go78,445
|
|
2
|
+
folio_migration_tools/__main__.py,sha256=pk_BGmhjB3-BGY36r9SLw6MNshY0tr4jCtYP_liLmBo,9531
|
|
3
|
+
folio_migration_tools/circulation_helper.py,sha256=Sybw-UBzgK_0dlTjfIPyGFQsRqghMYmTYLtZFuRd9zw,15487
|
|
4
|
+
folio_migration_tools/colors.py,sha256=0Z381jvWzzgA62PW9QWliYls4K9vu9M_TdA7IL-onT0,389
|
|
5
|
+
folio_migration_tools/config_file_load.py,sha256=KtjWsPiXJHtdK3PIUv77j8N0p8r_fnKsVyR6EumQgRo,3080
|
|
6
|
+
folio_migration_tools/custom_dict.py,sha256=dF9_VfUpvyatLDxhqeJ1Wq2MUVPoVmq6leHlvdm4WyE,1340
|
|
7
|
+
folio_migration_tools/custom_exceptions.py,sha256=RDoomoyBo0a-seZW3q2GAWBHlsodIOyiQ0WHuwIUyi8,4112
|
|
8
|
+
folio_migration_tools/extradata_writer.py,sha256=1BqKJa721fPIWRxjTPci658RVBoZ2kmI0XLsul2C7Bo,2128
|
|
9
|
+
folio_migration_tools/folder_structure.py,sha256=qQwmjdzC64ZfB-l6SVqjN984QTiS13TQTTCzmmWpt5U,8068
|
|
10
|
+
folio_migration_tools/helper.py,sha256=VIDFhOJg1_BU5YcHLqspXGTKwWNUrFDFeIOM530vFP4,3252
|
|
11
|
+
folio_migration_tools/holdings_helper.py,sha256=hvr6HLkZSuJsh6htbeStpKwHo8FnBb7YvRzETOj2PgQ,7809
|
|
12
|
+
folio_migration_tools/i18n_cache.py,sha256=wOJc3OkHwItlFNC2jQB5R9AoIga0g0P1YhQqHgjeqK4,2858
|
|
13
|
+
folio_migration_tools/i18n_config.py,sha256=0qMW0JI8X1T8c6qhaK58GU31ytEMKA2csdqHly5hEik,438
|
|
14
|
+
folio_migration_tools/library_configuration.py,sha256=Mc22UaKfzzrztMWp71kZizycWDyEklI54WsOjzB5dck,9514
|
|
15
|
+
folio_migration_tools/mapper_base.py,sha256=qe9DggRmqf2jq2jYjgsD_p_pbXLqEqge6lmlAnZpDU0,24518
|
|
16
|
+
folio_migration_tools/mapping_file_transformation/__init__.py,sha256=iUABykFuB2NsUml12D0-gB_6GnkZ8nNqU0DkfTZyLWo,80
|
|
17
|
+
folio_migration_tools/mapping_file_transformation/courses_mapper.py,sha256=YtprlWcH8edryEy9Ujlm74bShmnS7EwhGnLywrzfF2Y,8842
|
|
18
|
+
folio_migration_tools/mapping_file_transformation/holdings_mapper.py,sha256=djq12ur8-woz9tGM3vZY-Bm1KFK2_Xk2UaVRZhO-GJk,9243
|
|
19
|
+
folio_migration_tools/mapping_file_transformation/item_mapper.py,sha256=si0EjNxtIUsYwb11qlsTPP9Xlwce6Q1zfbocblGUGHs,11528
|
|
20
|
+
folio_migration_tools/mapping_file_transformation/manual_fee_fines_mapper.py,sha256=bSlARlhGllnZYqFWg2gFSEP6nFKGIqgJLtBK5grxyi8,14350
|
|
21
|
+
folio_migration_tools/mapping_file_transformation/mapping_file_mapper_base.py,sha256=KGxhSFQ1JVgIt6PuK6VGBbj1MRPA5mppnQVJUu9l56A,39388
|
|
22
|
+
folio_migration_tools/mapping_file_transformation/notes_mapper.py,sha256=rlSRzWWWkUFOdgIkhXG2jkoxVKpbW9b9J1wZpI7gxnA,4261
|
|
23
|
+
folio_migration_tools/mapping_file_transformation/order_mapper.py,sha256=cFyBSs8xsN1vBGif0Cn_VON-LlWeWHmfzuEk9c1AyzA,19432
|
|
24
|
+
folio_migration_tools/mapping_file_transformation/organization_mapper.py,sha256=kJkmsZNj5ow9N839jhD5kKZEZftbJh6JjTYxHynI6WI,15581
|
|
25
|
+
folio_migration_tools/mapping_file_transformation/ref_data_mapping.py,sha256=oQEQCBh7c3XQYX1zbo-NgwxTa9i4DMBggs9OLRr75ko,9790
|
|
26
|
+
folio_migration_tools/mapping_file_transformation/user_mapper.py,sha256=8sz6glwn7mMLpmTOyF-IuHQb3fs0i4a2BGon2qpIJuk,9586
|
|
27
|
+
folio_migration_tools/marc_rules_transformation/__init__.py,sha256=XHQdK6uc_-Y2k158M6hSaojWWcMZjJIzuHYaj8J8ViI,60
|
|
28
|
+
folio_migration_tools/marc_rules_transformation/conditions.py,sha256=Xv4RU4h3QgozKvOn67VsLj3t9mjByRP5FqyRYxQFj10,47306
|
|
29
|
+
folio_migration_tools/marc_rules_transformation/holdings_statementsparser.py,sha256=LiYsBY7nORJ2A4L2P08fWEGfswbspxhPRKhFl1QdWp0,13932
|
|
30
|
+
folio_migration_tools/marc_rules_transformation/hrid_handler.py,sha256=8mmu7bNE-CFDwLPW1F6YFyAC4_KF2tWEJkHeA5oPhmM,10728
|
|
31
|
+
folio_migration_tools/marc_rules_transformation/loc_language_codes.xml,sha256=ztn2_yKws6qySL4oSsZh7sOjxq5bCC1PhAnXJdtgmJ0,382912
|
|
32
|
+
folio_migration_tools/marc_rules_transformation/marc_file_processor.py,sha256=aLWYorowhhq4KySnXdAPXNQMv27lCT9e_cWi2Br4Yf0,13146
|
|
33
|
+
folio_migration_tools/marc_rules_transformation/marc_reader_wrapper.py,sha256=-rR9d62BiYHQU9h31otlcM_Mi9-80EhWyBUWlxt4Pq8,5543
|
|
34
|
+
folio_migration_tools/marc_rules_transformation/rules_mapper_base.py,sha256=q981tWbjdKILWwZylxs2VwR_DvIqER1H8J8nZFUpnKY,46102
|
|
35
|
+
folio_migration_tools/marc_rules_transformation/rules_mapper_bibs.py,sha256=KU9wTo60AVge80xUHkkEOauqwBaTnFZ1q9GU_-NLgfU,30850
|
|
36
|
+
folio_migration_tools/marc_rules_transformation/rules_mapper_holdings.py,sha256=89auENlVrQExwgOHNmqXn0RXTZlXdg7f5wFDLYwcfU8,30247
|
|
37
|
+
folio_migration_tools/migration_report.py,sha256=sTyNPXNxies91RNHrhGF01xI9K4Ti0-k3Q9pl-ezOOg,4961
|
|
38
|
+
folio_migration_tools/migration_tasks/__init__.py,sha256=S7zG9fYfp-B_Wzo4VW_Mhdtu0f18OVf-yfAV0sFW57E,279
|
|
39
|
+
folio_migration_tools/migration_tasks/batch_poster.py,sha256=MxQn3Pzl5Wxm0iLCJZi80VjkiYZaJkRPS0H7_AnokW8,47561
|
|
40
|
+
folio_migration_tools/migration_tasks/bibs_transformer.py,sha256=-1PIW8zT_o07fdEE0fwRO3rmyvJyNcX-DN0OorsWL24,7186
|
|
41
|
+
folio_migration_tools/migration_tasks/courses_migrator.py,sha256=tr3Ed3kOZQ5bv_S1FuTm0F9CAWcNJZ5g650KPauZVoA,7748
|
|
42
|
+
folio_migration_tools/migration_tasks/holdings_csv_transformer.py,sha256=gwROvFN2QmLAJAe7f4skP07YeI1HLhBa716-ANhLkMk,22698
|
|
43
|
+
folio_migration_tools/migration_tasks/holdings_marc_transformer.py,sha256=kL6YVrtAOxTg0O3JWorsG_vRbuJNjq7XjdC9B8v0imI,15116
|
|
44
|
+
folio_migration_tools/migration_tasks/inventory_batch_poster.py,sha256=yrwu8G3vJFzIF6wwFlfK2WcQhogws1lRF3BWCQexIPQ,16388
|
|
45
|
+
folio_migration_tools/migration_tasks/items_transformer.py,sha256=eNlxk50IUqzmG2PhB-oL7IYcwQonnRXEa0k0bnI1u20,20342
|
|
46
|
+
folio_migration_tools/migration_tasks/loans_migrator.py,sha256=fOPujpWBZgxHtS89g8yiPr0UFTysPL5Dqbh3zqh6U1A,39555
|
|
47
|
+
folio_migration_tools/migration_tasks/manual_fee_fines_transformer.py,sha256=gKApM9nLjyATX6qWn-i3hjxoRbY9QXO4JKd3h9hxmXc,8105
|
|
48
|
+
folio_migration_tools/migration_tasks/marc_import.py,sha256=ItRYdS3ZdqAlazW1HWmldlVWWQl0Bz24gWyv_OXBhOU,15575
|
|
49
|
+
folio_migration_tools/migration_tasks/migration_task_base.py,sha256=TdDF0K49sTDBf9S0fjEtkWG45UwYcNIIwm6Jgdt0lIs,23661
|
|
50
|
+
folio_migration_tools/migration_tasks/orders_transformer.py,sha256=qxE5rE0IJH9lKWo6a5hixJ9fS14ryAdAf9yZDS7zbUM,14742
|
|
51
|
+
folio_migration_tools/migration_tasks/organization_transformer.py,sha256=E_g9c-A_NwNQLm8Ciij9jYjAfXgcp_cInuz2Vrn8S1c,17558
|
|
52
|
+
folio_migration_tools/migration_tasks/requests_migrator.py,sha256=0bcjjGZGKasfqsHhUaAKPyJE-lhbNVKQ245txbo8nNg,15537
|
|
53
|
+
folio_migration_tools/migration_tasks/reserves_migrator.py,sha256=hduA8ihe2K54TAKWhMjLICxuETaNYxV2-R-nbSx-80A,10716
|
|
54
|
+
folio_migration_tools/migration_tasks/user_importer.py,sha256=QEqK-FmFGIqZdtMDOF6OS5UWhqSOzQzEl5L_pT4WghM,12960
|
|
55
|
+
folio_migration_tools/migration_tasks/user_transformer.py,sha256=4AxprsUaekHlUQdSeAKu-vj3-mu6K9Z36N1OSjsu5ow,13452
|
|
56
|
+
folio_migration_tools/task_configuration.py,sha256=fvFtelR20At1hPFhh10WOX1pmPe39AY8aOosTaH2VbU,1397
|
|
57
|
+
folio_migration_tools/transaction_migration/__init__.py,sha256=BmTOUIqofyCcM-pv5xrexhT68xg1qxrq87M-NRNnSO4,77
|
|
58
|
+
folio_migration_tools/transaction_migration/legacy_loan.py,sha256=Ts_nyA1dxcSJcTQWtJGVh5aD8UCfy37ylchcOhDbkJ0,8138
|
|
59
|
+
folio_migration_tools/transaction_migration/legacy_request.py,sha256=wcVNOMuqlfD0ZlGaWDGiSLoPzArfnFY8ENcIq91ctQs,6726
|
|
60
|
+
folio_migration_tools/transaction_migration/legacy_reserve.py,sha256=NRxpKsPe3cczzudZeSDXAIwJHAsQuvk07lW0Ex3SNEo,2413
|
|
61
|
+
folio_migration_tools/transaction_migration/transaction_result.py,sha256=wSHEKSE0QKs8pTBX9FS0Gte52rQ_zeE2VyT4VJlsxV4,1383
|
|
62
|
+
folio_migration_tools/translations/en.json,sha256=pS7dhHmj4XBqTcFNIcqFgRMY557fQan1RomdNg6PtdA,40941
|
|
63
|
+
folio_migration_tools-1.10.3.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
|
|
64
|
+
folio_migration_tools-1.10.3.dist-info/entry_points.txt,sha256=mJRRiCNP9j7_NpVXamHEiW8pDEjWQs1vEqD89G354cM,79
|
|
65
|
+
folio_migration_tools-1.10.3.dist-info/METADATA,sha256=rO2e7GZ1zqHRXTMhPDbPWOu7SiUNqROBq5ENbophnhs,7207
|
|
66
|
+
folio_migration_tools-1.10.3.dist-info/RECORD,,
|