folio-data-import 0.5.0b3__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.
@@ -0,0 +1,467 @@
1
+ Metadata-Version: 2.4
2
+ Name: folio_data_import
3
+ Version: 0.5.0b3
4
+ Summary: A python module to perform bulk import of data into a FOLIO environment. Currently supports MARC and user data import.
5
+ Author: Brooks Travis
6
+ Author-email: Brooks Travis <brooks.travis@gmail.com>
7
+ License-Expression: MIT
8
+ Requires-Dist: folioclient>=1.0.3,<2.0
9
+ Requires-Dist: pymarc>=5.2.2
10
+ Requires-Dist: pyhumps>=3.8.0
11
+ Requires-Dist: tabulate>=0.9.0
12
+ Requires-Dist: aiofiles>=24.1.0
13
+ Requires-Dist: flake8-black>=0.3.6
14
+ Requires-Dist: flake8-bugbear>=24.8.19
15
+ Requires-Dist: flake8-bandit>=4.1.1
16
+ Requires-Dist: flake8-isort>=6.1.1
17
+ Requires-Dist: flake8-docstrings>=1.7.0
18
+ Requires-Dist: cyclopts>=4.2.0
19
+ Requires-Dist: questionary>=2.1.1
20
+ Requires-Dist: folio-uuid>=1.0.0
21
+ Requires-Dist: pydantic>=2.12.3
22
+ Requires-Dist: redis>=7.1.0 ; extra == 'redis'
23
+ Requires-Python: >=3.10, <4.0
24
+ Provides-Extra: redis
25
+ Description-Content-Type: text/markdown
26
+
27
+ # folio_data_import
28
+
29
+ ## Description
30
+
31
+ This project is designed to import data into the FOLIO LSP. It provides a simple and efficient way to import data from various sources using FOLIO's REST APIs.
32
+
33
+ ## Features
34
+
35
+ - Import MARC records using FOLIO's Data Import system
36
+ - Import User records using FOLIO's User APIs
37
+ - Batch post Instances, Holdings, and Items to FOLIO inventory storage
38
+
39
+ ## Installation
40
+
41
+ ## Installation
42
+
43
+ Using `pip`
44
+ ```shell
45
+ pip install folio_data_import
46
+ ```
47
+ or `uv pip`
48
+ ```shell
49
+ uv pip install folio_data_import
50
+ ```
51
+
52
+ To install the project from the git repo using Poetry, follow these steps:
53
+
54
+ 1. Clone the repository.
55
+ 2. Navigate to the project directory: `$ cd /path/to/folio_data_import`.
56
+ 3. [Install `uv`](https://docs.astral.sh/uv/getting-started/installation/) if you haven't already.
57
+ 4. Install the project and its dependencies: `$ uv sync`.
58
+ 5. Run the application using Poetry: `$ uv run folio-data-import --help`.
59
+
60
+ Make sure to activate the virtual environment created by `uv` before running the application.
61
+
62
+ ## Usage
63
+
64
+ This package provides CLI commands for importing data into FOLIO:
65
+
66
+ ```shell
67
+ # Main command with subcommands
68
+ folio-data-import <subcommand> [options]
69
+
70
+ # Or use standalone commands
71
+ folio-user-import [options]
72
+ folio-marc-import [options]
73
+ folio-batch-poster [options]
74
+ ```
75
+
76
+ **Tab Completion:** Install shell completions for better CLI experience:
77
+ ```shell
78
+ folio-data-import --install-completion
79
+ ```
80
+
81
+ ### Environment Variables
82
+
83
+ All commands support environment variables for FOLIO connection credentials, allowing you to avoid repeating these parameters:
84
+
85
+ ```shell
86
+ export FOLIO_GATEWAY_URL="https://folio-snapshot-okapi.dev.folio.org"
87
+ export FOLIO_TENANT_ID="diku"
88
+ export FOLIO_USERNAME="diku_admin"
89
+ export FOLIO_PASSWORD="admin"
90
+ ```
91
+
92
+ Once set, you can omit these parameters from your commands:
93
+
94
+ ```shell
95
+ # Instead of:
96
+ folio-data-import users --gateway-url "..." --tenant-id "..." --username "..." --password "..." --user-file users.jsonl
97
+
98
+ # You can simply use:
99
+ folio-data-import users --user-file users.jsonl
100
+ ```
101
+
102
+ This works for all subcommands: `users`, `marc`, and `batch-poster`.
103
+
104
+ ## CLI Commands
105
+
106
+ ### folio-data-import users
107
+
108
+ **Alias:** `folio-user-import`
109
+
110
+ Import users to FOLIO with extended functionality beyond `mod-user-import`.
111
+
112
+ #### Quick Start
113
+
114
+ ```shell
115
+ folio-data-import users \
116
+ --gateway-url "https://folio-snapshot-okapi.dev.folio.org" \
117
+ --tenant-id diku \
118
+ --username diku_admin \
119
+ --password admin \
120
+ --user-file users.jsonl
121
+ ```
122
+
123
+ #### Features
124
+
125
+ **Service Point Management:** Specify service points using codes instead of UUIDs:
126
+ ```
127
+ {
128
+ "username": "checkin-all",
129
+ "barcode": "1728439497039848103",
130
+ "active": true,
131
+ "type": "patron",
132
+ "patronGroup": "staff",
133
+ "departments": [],
134
+ "personal": {
135
+ "lastName": "Admin",
136
+ "firstName": "checkin-all",
137
+ "addresses": [
138
+ {
139
+ "countryId": "HU",
140
+ "addressLine1": "Andrássy Street 1.",
141
+ "addressLine2": "",
142
+ "city": "Budapest",
143
+ "region": "Pest",
144
+ "postalCode": "1061",
145
+ "addressTypeId": "Home",
146
+ "primaryAddress": true
147
+ }
148
+ ],
149
+ "preferredContactTypeId": "email"
150
+ },
151
+ "requestPreference": {
152
+ "holdShelf": true,
153
+ "delivery": false,
154
+ "fulfillment": "Hold Shelf"
155
+ }
156
+ "servicePointsUser": {
157
+ "defaultServicePointId": "cd1",
158
+ "servicePointsIds": [
159
+ "cd1",
160
+ "Online",
161
+ "000",
162
+ "cd2"
163
+ ]
164
+ }
165
+ }
166
+ ```
167
+
168
+ **Flexible Matching:** Match users by `id`, `externalSystemId`, `username`, or `barcode`:
169
+ ```shell
170
+ folio-data-import users --user-file users.jsonl --user-match-key username
171
+ ```
172
+
173
+ **Preferred Contact Type:** Accepts FOLIO IDs or human-friendly strings (`mail`, `email`, `text`, `phone`, `mobile`). Set a default for users without a valid value:
174
+ ```shell
175
+ folio-data-import users --user-file users.jsonl --default-preferred-contact-type email
176
+ ```
177
+
178
+ **Field Protection:** Protect specific fields from being updated:
179
+
180
+ - **Job-level protection** (applies to all records):
181
+ ```shell
182
+ folio-data-import users --user-file users.jsonl \
183
+ --fields-to-protect "personal.preferredFirstName,barcode"
184
+ ```
185
+
186
+ - **Per-record protection** (using custom field `protectedFields`):
187
+ ```json
188
+ {
189
+ "username": "jdoe",
190
+ "customFields": {
191
+ "protectedFields": "barcode,personal.telephone,personal.addresses"
192
+ }
193
+ }
194
+ ```
195
+
196
+ #### Input Format
197
+
198
+ JSON Lines format - one user object in the style<sup>*</sup> of [mod-user-import](https://github.com/folio-org/mod-user-import) (with extended support mentioned above) per line
199
+
200
+ <sup>*</sup>also supports dereferenced (UUIDs instead of reference strings) user objects (eg. directly extracted from `/users`)
201
+
202
+ ### folio-data-import marc
203
+
204
+ **Alias:** `folio-marc-import`
205
+
206
+ Import binary MARC21 records via FOLIO's Data Import system using the [change-manager](https://github.com/folio-org/mod-source-record-manager?tab=readme-ov-file#data-import-workflow) APIs.
207
+
208
+ #### Quick Start
209
+
210
+ ```shell
211
+ folio-data-import marc \
212
+ --gateway-url "https://folio-snapshot-okapi.dev.folio.org" \
213
+ --tenant-id diku \
214
+ --username diku_admin \
215
+ --password admin \
216
+ --marc-source-path records.mrc
217
+ ```
218
+
219
+ The command will prompt you to select a [Data Import Job Profile](https://docs.folio.org/docs/metadata/additional-topics/jobprofiles/) configured in your FOLIO tenant.
220
+
221
+ #### Features
222
+
223
+ - Process single files or entire directories of MARC files
224
+ - Interactive job profile selection
225
+ - Real-time progress tracking
226
+ - Automatic retry on transient errors
227
+
228
+ **Note:** FOLIO's import logs can be unreliable. If you don't see a job summary when your job completes, check Data Import in FOLIO (Data Import > Actions > View all logs...).
229
+
230
+ ### folio-data-import batch-poster
231
+
232
+ **Alias:** `folio-batch-poster`
233
+
234
+ Efficiently batch post Instances, Holdings, and Items to FOLIO's inventory storage endpoints with support for creating new records and updating existing ones.
235
+
236
+ #### Quick Start
237
+
238
+ ```shell
239
+ folio-data-import batch-poster \
240
+ --gateway-url "https://folio-snapshot-okapi.dev.folio.org" \
241
+ --tenant-id diku \
242
+ --username diku_admin \
243
+ --password admin \
244
+ --object-type Items \
245
+ --file-paths items.jsonl \
246
+ --batch-size 100 \
247
+ --upsert
248
+ ```
249
+
250
+ #### Key Features
251
+
252
+ - **Multiple File Support**: Process multiple files with glob patterns
253
+ ```shell
254
+ folio-data-import batch-poster --object-type Items --file-paths "items_*.jsonl"
255
+ ```
256
+
257
+ - **Upsert Mode**: Create new records or update existing ones
258
+ ```shell
259
+ folio-data-import batch-poster --object-type Items --file-paths items.jsonl --upsert
260
+ ```
261
+
262
+ - **Field Preservation**: Control which fields are preserved during updates
263
+ ```shell
264
+ folio-data-import batch-poster --object-type Items --file-paths items.jsonl --upsert \
265
+ --preserve-statistical-codes \
266
+ --preserve-administrative-notes \
267
+ --preserve-temporary-locations \
268
+ --overwrite-item-status
269
+ ```
270
+
271
+ - **Selective Patching**: Update only specific fields
272
+ ```shell
273
+ folio-data-import batch-poster --object-type Items --file-paths items.jsonl --upsert \
274
+ --patch-existing-records \
275
+ --patch-paths "barcode,status,itemLevelCallNumber"
276
+ ```
277
+
278
+ - **Failed Records**: Automatically save failed records to a file
279
+ ```shell
280
+ folio-data-import batch-poster --object-type Items --file-paths items.jsonl \
281
+ --failed-records-file failed_items.jsonl
282
+ ```
283
+
284
+ - **Progress Tracking**: Real-time progress bar with statistics
285
+ - Disable with `--no-progress` for CI/CD environments
286
+
287
+ - **Config File Support**: Use a JSON config file for complex configurations
288
+ ```shell
289
+ folio-data-import batch-poster config.json
290
+ ```
291
+
292
+ Example `config.json`:
293
+ ```json
294
+ {
295
+ "object_type": "Items",
296
+ "file_paths": ["items1.jsonl", "items2.jsonl"],
297
+ "batch_size": 100,
298
+ "upsert": true,
299
+ "preserve_statistical_codes": true,
300
+ "preserve_item_status": true,
301
+ "failed_records_file": "failed_items.jsonl"
302
+ }
303
+ ```
304
+
305
+ #### Input Format
306
+
307
+ Input files should be JSONL (JSON Lines) format - one complete JSON object per line:
308
+
309
+ ```jsonl
310
+ {"id": "item-001", "barcode": "12345", "status": {"name": "Available"}}
311
+ {"id": "item-002", "barcode": "12346", "status": {"name": "Available"}}
312
+ {"id": "item-003", "barcode": "12347", "status": {"name": "Checked out"}}
313
+ ```
314
+
315
+ #### Common Use Cases
316
+
317
+ **Create new items:**
318
+ ```shell
319
+ folio-data-import batch-poster --object-type Items --file-paths new_items.jsonl
320
+ ```
321
+
322
+ **Update existing items (by ID):**
323
+ ```shell
324
+ folio-data-import batch-poster --object-type Items --file-paths items.jsonl --upsert
325
+ ```
326
+
327
+ **Update only barcodes and call numbers:**
328
+ ```shell
329
+ folio-data-import batch-poster --object-type Items --file-paths items.jsonl --upsert \
330
+ --patch-existing-records \
331
+ --patch-paths "barcode,itemLevelCallNumber"
332
+ ```
333
+
334
+ **Process multiple files:**
335
+ ```shell
336
+ folio-data-import batch-poster --object-type Holdings \
337
+ --file-paths holdings_*.jsonl \
338
+ --batch-size 500 \
339
+ --upsert
340
+ ```
341
+
342
+ #### Available Options
343
+
344
+ Run `folio-data-import batch-poster --help` to see all available options:
345
+ - `--object-type`: Type of inventory object (Items, Holdings, or Instances) - **Required**
346
+ - `--file-paths`: Path(s) to JSONL file(s) - supports glob patterns - **Required**
347
+ - `--batch-size`: Number of records per batch (1-1000, default: 100)
348
+ - `--upsert`: Enable create-or-update mode
349
+ - `--preserve-statistical-codes`: Keep existing statistical codes during updates
350
+ - `--preserve-administrative-notes`: Keep existing administrative notes
351
+ - `--preserve-temporary-locations`: Keep temporary location (Items only)
352
+ - `--preserve-temporary-loan-types`: Keep temporary loan type (Items only)
353
+ - `--preserve-item-status`: Keep item status (Items only, default: true)
354
+ - `--patch-existing-records`: Enable selective field patching
355
+ - `--patch-paths`: Comma-separated list of fields to patch
356
+ - `--failed-records-file`: Path to save failed records
357
+ - `--no-progress`: Disable progress bar (useful for CI/CD)
358
+
359
+ ## Programmatic Usage
360
+
361
+ All CLI commands can also be used programmatically in your Python applications.
362
+
363
+ ### BatchPoster
364
+
365
+ ```python
366
+ import asyncio
367
+ from folioclient import FolioClient
368
+ from folio_data_import.BatchPoster import BatchPoster
369
+
370
+ async def post_items():
371
+ # Create FOLIO client
372
+ folio = FolioClient(
373
+ okapi_url="https://folio-snapshot-okapi.dev.folio.org",
374
+ tenant_id="diku",
375
+ username="diku_admin",
376
+ password="admin"
377
+ )
378
+
379
+ # Configure batch poster
380
+ config = BatchPoster.Config(
381
+ object_type="Items",
382
+ batch_size=100,
383
+ upsert=True,
384
+ preserve_statistical_codes=True
385
+ )
386
+
387
+ # Post records
388
+ async with BatchPoster(
389
+ folio,
390
+ config,
391
+ failed_records_file="failed_items.jsonl"
392
+ ) as poster:
393
+ await poster.post_records("items.jsonl")
394
+ print(f"Posted: {poster.stats.records_posted}, Failed: {poster.stats.records_failed}")
395
+
396
+ asyncio.run(post_items())
397
+ ```
398
+
399
+ ### UserImporter
400
+
401
+ ```python
402
+ import asyncio
403
+ from folioclient import FolioClient
404
+ from folio_data_import.UserImport import UserImporter
405
+
406
+ async def import_users():
407
+ folio = FolioClient(
408
+ okapi_url="https://folio-snapshot-okapi.dev.folio.org",
409
+ tenant_id="diku",
410
+ username="diku_admin",
411
+ password="admin"
412
+ )
413
+
414
+ config = UserImporter.Config(
415
+ user_file="users.jsonl",
416
+ user_match_key="username",
417
+ default_preferred_contact_type="email"
418
+ )
419
+
420
+ importer = UserImporter(folio, config)
421
+ result = await importer.do_work()
422
+ print(f"Imported {result.records_created} users")
423
+
424
+ asyncio.run(import_users())
425
+ ```
426
+
427
+ ### MARCImportJob
428
+
429
+ ```python
430
+ import asyncio
431
+ from folioclient import FolioClient
432
+ from folio_data_import.MARCDataImport import MARCImportJob
433
+
434
+ async def import_marc():
435
+ folio = FolioClient(
436
+ okapi_url="https://folio-snapshot-okapi.dev.folio.org",
437
+ tenant_id="diku",
438
+ username="diku_admin",
439
+ password="admin"
440
+ )
441
+
442
+ config = MARCImportJob.Config(
443
+ marc_source_path="records.mrc",
444
+ job_profile_id="profile-uuid",
445
+ job_profile_name="Bibliographic records"
446
+ )
447
+
448
+ job = MARCImportJob(folio, config)
449
+ result = await job.start_import()
450
+ print(f"Imported {result.created_records} MARC records")
451
+
452
+ asyncio.run(import_marc())
453
+ ```
454
+
455
+ ### Additional Documentation
456
+
457
+ For complete API documentation and advanced usage:
458
+ - [BatchPoster.md](docs/BatchPoster.md) - Comprehensive BatchPoster guide
459
+ - [BatchPoster_Quick_Reference.md](docs/BatchPoster_Quick_Reference.md) - Quick reference
460
+
461
+ ## Contributing
462
+
463
+ Contributions are welcome! If you have any ideas, suggestions, or bug reports, please open an issue or submit a pull request.
464
+
465
+ ## License
466
+
467
+ This project is licensed under the [MIT License](LICENSE).
@@ -0,0 +1,13 @@
1
+ folio_data_import/BatchPoster.py,sha256=yTZIynNXozMnE6IJGAUzzqKLTrn3XUqa_so6nWUKyn0,45820
2
+ folio_data_import/MARCDataImport.py,sha256=BNlA4a49d2ExreinsRD2ZMf779CtdbYcbKeSzQnmM0s,48688
3
+ folio_data_import/UserImport.py,sha256=or1arWAbvpO6BCs_2g_bzCQHvq1XQDy-FmvVVb3dpcU,48758
4
+ folio_data_import/__init__.py,sha256=U9gWBIR3WJ0N-RMOtul07WRvnI6ox9BSOZR3aQE0c0U,1200
5
+ folio_data_import/__main__.py,sha256=81r87LawkV1JdW7RLf9yVRlCp3-dxlLWu7VStGdZf7M,531
6
+ folio_data_import/_progress.py,sha256=qPJM8pZpvXG90krgaezcvybyVtGncSBHg3vBHOlmMkU,22625
7
+ folio_data_import/custom_exceptions.py,sha256=72d5LvHWjToMadj2npiuIP4lk9oCFCM79Gv7xVMmFKc,1111
8
+ folio_data_import/marc_preprocessors/__init__.py,sha256=Z0OL_qwJjSgNzc6tyTfVkE2As9QQRyBkqjx-1FIDoFU,741
9
+ folio_data_import/marc_preprocessors/_preprocessors.py,sha256=HQNSSnUnNvoGaIdZFarTgbYdJEYOBSCOevQI4QkGNnA,16891
10
+ folio_data_import-0.5.0b3.dist-info/WHEEL,sha256=xDCZ-UyfvkGuEHPeI7BcJzYKIZzdqN8A8o1M5Om8IyA,79
11
+ folio_data_import-0.5.0b3.dist-info/entry_points.txt,sha256=XjH659lA7sbtTLCN18F8AFWP0WfuHKh2lFvEe5C1wXk,235
12
+ folio_data_import-0.5.0b3.dist-info/METADATA,sha256=DpdLjAbE3cPGDxQgY8VNILQEQ6QTCG8xsAJQDvD2txM,13684
13
+ folio_data_import-0.5.0b3.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: uv 0.9.17
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,6 @@
1
+ [console_scripts]
2
+ folio-batch-poster = folio_data_import.BatchPoster:app
3
+ folio-data-import = folio_data_import.__main__:app
4
+ folio-marc-import = folio_data_import.MARCDataImport:app
5
+ folio-user-import = folio_data_import.UserImport:app
6
+