folio-migration-tools 1.9.6__tar.gz → 1.9.8__tar.gz
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-1.9.6 → folio_migration_tools-1.9.8}/PKG-INFO +5 -4
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/pyproject.toml +1 -1
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/migration_tasks/batch_poster.py +156 -29
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/migration_tasks/holdings_csv_transformer.py +1 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/LICENSE +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/README.md +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/__init__.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/__main__.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/circulation_helper.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/colors.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/config_file_load.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/custom_dict.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/custom_exceptions.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/extradata_writer.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/folder_structure.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/helper.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/holdings_helper.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/i18n_config.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/library_configuration.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/mapper_base.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/mapping_file_transformation/__init__.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/mapping_file_transformation/courses_mapper.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/mapping_file_transformation/holdings_mapper.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/mapping_file_transformation/item_mapper.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/mapping_file_transformation/manual_fee_fines_mapper.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/mapping_file_transformation/mapping_file_mapper_base.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/mapping_file_transformation/notes_mapper.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/mapping_file_transformation/order_mapper.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/mapping_file_transformation/organization_mapper.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/mapping_file_transformation/ref_data_mapping.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/mapping_file_transformation/user_mapper.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/marc_rules_transformation/__init__.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/marc_rules_transformation/conditions.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/marc_rules_transformation/holdings_statementsparser.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/marc_rules_transformation/hrid_handler.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/marc_rules_transformation/loc_language_codes.xml +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/marc_rules_transformation/marc_file_processor.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/marc_rules_transformation/marc_reader_wrapper.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/marc_rules_transformation/rules_mapper_authorities.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/marc_rules_transformation/rules_mapper_base.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/marc_rules_transformation/rules_mapper_bibs.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/marc_rules_transformation/rules_mapper_holdings.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/migration_report.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/migration_tasks/__init__.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/migration_tasks/authority_transformer.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/migration_tasks/bibs_transformer.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/migration_tasks/courses_migrator.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/migration_tasks/holdings_marc_transformer.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/migration_tasks/items_transformer.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/migration_tasks/loans_migrator.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/migration_tasks/manual_fee_fines_transformer.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/migration_tasks/migration_task_base.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/migration_tasks/orders_transformer.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/migration_tasks/organization_transformer.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/migration_tasks/requests_migrator.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/migration_tasks/reserves_migrator.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/migration_tasks/user_transformer.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/task_configuration.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/test_infrastructure/__init__.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/test_infrastructure/mocked_classes.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/transaction_migration/__init__.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/transaction_migration/legacy_loan.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/transaction_migration/legacy_request.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/transaction_migration/legacy_reserve.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/transaction_migration/transaction_result.py +0 -0
- {folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/translations/en.json +0 -0
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: folio_migration_tools
|
|
3
|
-
Version: 1.9.
|
|
3
|
+
Version: 1.9.8
|
|
4
4
|
Summary: A tool allowing you to migrate data from legacy ILS:s (Library systems) into FOLIO LSP
|
|
5
|
-
License: MIT
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
License-File: LICENSE
|
|
6
7
|
Keywords: FOLIO,ILS,LSP,Library Systems,MARC21,Library data
|
|
7
8
|
Author: Theodor Tolstoy
|
|
8
9
|
Author-email: github.teddes@tolstoy.se
|
|
9
10
|
Requires-Python: >=3.10,<4.0
|
|
10
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
11
11
|
Classifier: Programming Language :: Python :: 3
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.10
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
16
17
|
Provides-Extra: docs
|
|
17
18
|
Requires-Dist: argparse-prompt (>=0.0.5,<0.0.6)
|
|
18
19
|
Requires-Dist: art (>=6.5,<7.0)
|
|
@@ -2,6 +2,7 @@ import asyncio
|
|
|
2
2
|
import copy
|
|
3
3
|
import json
|
|
4
4
|
import logging
|
|
5
|
+
import re
|
|
5
6
|
import sys
|
|
6
7
|
import time
|
|
7
8
|
import traceback
|
|
@@ -181,6 +182,19 @@ class BatchPoster(MigrationTaskBase):
|
|
|
181
182
|
),
|
|
182
183
|
),
|
|
183
184
|
] = True
|
|
185
|
+
patch_existing_records: Annotated[bool, Field(
|
|
186
|
+
title="Patch existing records",
|
|
187
|
+
description=(
|
|
188
|
+
"Toggles whether or not to patch existing records "
|
|
189
|
+
"during the upsert process. Defaults to False"
|
|
190
|
+
),
|
|
191
|
+
)] = False
|
|
192
|
+
patch_paths: Annotated[List[str], Field(
|
|
193
|
+
title="Patch paths",
|
|
194
|
+
description=(
|
|
195
|
+
"A list of fields in JSON Path notation to patch during the upsert process (leave off the $). If empty, all fields will be patched. Examples: ['statisticalCodeIds', 'administrativeNotes', 'instanceStatusId']"
|
|
196
|
+
),
|
|
197
|
+
)] = []
|
|
184
198
|
|
|
185
199
|
task_configuration: TaskConfiguration
|
|
186
200
|
|
|
@@ -368,16 +382,33 @@ class BatchPoster(MigrationTaskBase):
|
|
|
368
382
|
if record["id"] in existing_records:
|
|
369
383
|
self.prepare_record_for_upsert(record, existing_records[record["id"]])
|
|
370
384
|
|
|
371
|
-
def
|
|
385
|
+
def patch_record(self, new_record: dict, existing_record: dict, patch_paths: List[str]):
|
|
386
|
+
"""
|
|
387
|
+
Updates new_record with values from existing_record according to patch_paths.
|
|
388
|
+
|
|
389
|
+
Args:
|
|
390
|
+
new_record (dict): The new record to be updated.
|
|
391
|
+
existing_record (dict): The existing record to patch from.
|
|
392
|
+
patch_paths (List[str]): List of fields in JSON Path notation (e.g., ['statisticalCodeIds', 'administrativeNotes', 'instanceStatusId']) to patch during the upsert process. If empty, all fields will be patched.
|
|
393
|
+
"""
|
|
372
394
|
updates = {}
|
|
373
395
|
updates.update(existing_record)
|
|
374
|
-
|
|
375
|
-
self.
|
|
376
|
-
|
|
396
|
+
keep_existing = {}
|
|
397
|
+
self.handle_upsert_for_administrative_notes(updates, keep_existing)
|
|
398
|
+
self.handle_upsert_for_statistical_codes(updates, keep_existing)
|
|
399
|
+
if not patch_paths:
|
|
400
|
+
keep_new = new_record
|
|
401
|
+
else:
|
|
402
|
+
keep_new = extract_paths(new_record, patch_paths)
|
|
377
403
|
if "instanceStatusId" in new_record:
|
|
378
404
|
updates["instanceStatusId"] = new_record["instanceStatusId"]
|
|
379
|
-
|
|
380
|
-
|
|
405
|
+
deep_update(updates, keep_new)
|
|
406
|
+
for key, value in keep_existing.items():
|
|
407
|
+
if isinstance(value, list) and key in keep_new:
|
|
408
|
+
updates[key] = list(dict.fromkeys(updates.get(key, []) + value))
|
|
409
|
+
elif key not in keep_new:
|
|
410
|
+
updates[key] = value
|
|
411
|
+
new_record.clear()
|
|
381
412
|
new_record.update(updates)
|
|
382
413
|
|
|
383
414
|
@staticmethod
|
|
@@ -393,21 +424,29 @@ class BatchPoster(MigrationTaskBase):
|
|
|
393
424
|
response.text,
|
|
394
425
|
)
|
|
395
426
|
|
|
396
|
-
def handle_upsert_for_statistical_codes(self, updates: dict):
|
|
427
|
+
def handle_upsert_for_statistical_codes(self, updates: dict, keep_existing: dict):
|
|
397
428
|
if not self.task_configuration.preserve_statistical_codes:
|
|
398
|
-
updates
|
|
429
|
+
updates["statisticalCodeIds"] = []
|
|
430
|
+
keep_existing["statisticalCodeIds"] = []
|
|
431
|
+
else:
|
|
432
|
+
keep_existing["statisticalCodeIds"] = updates.pop("statisticalCodeIds", [])
|
|
433
|
+
updates["statisticalCodeIds"] = []
|
|
399
434
|
|
|
400
|
-
def handle_upsert_for_administrative_notes(self, updates: dict):
|
|
435
|
+
def handle_upsert_for_administrative_notes(self, updates: dict, keep_existing: dict):
|
|
401
436
|
if not self.task_configuration.preserve_administrative_notes:
|
|
402
|
-
updates
|
|
437
|
+
updates["administrativeNotes"] = []
|
|
438
|
+
keep_existing["administrativeNotes"] = []
|
|
439
|
+
else:
|
|
440
|
+
keep_existing["administrativeNotes"] = updates.pop("administrativeNotes", [])
|
|
441
|
+
updates["administrativeNotes"] = []
|
|
403
442
|
|
|
404
|
-
def handle_upsert_for_temporary_locations(self, updates: dict):
|
|
405
|
-
if
|
|
406
|
-
updates.pop("temporaryLocationId", None)
|
|
443
|
+
def handle_upsert_for_temporary_locations(self, updates: dict, keep_existing: dict):
|
|
444
|
+
if self.task_configuration.preserve_temporary_locations:
|
|
445
|
+
keep_existing["temporaryLocationId"] = updates.pop("temporaryLocationId", None)
|
|
407
446
|
|
|
408
|
-
def handle_upsert_for_temporary_loan_types(self, updates: dict):
|
|
409
|
-
if
|
|
410
|
-
updates.pop("temporaryLoanTypeId", None)
|
|
447
|
+
def handle_upsert_for_temporary_loan_types(self, updates: dict, keep_existing: dict):
|
|
448
|
+
if self.task_configuration.preserve_temporary_loan_types:
|
|
449
|
+
keep_existing["temporaryLoanTypeId"] = updates.pop("temporaryLoanTypeId", None)
|
|
411
450
|
|
|
412
451
|
def keep_existing_fields(self, updates: dict, existing_record: dict):
|
|
413
452
|
keep_existing_fields = ["hrid", "lastCheckIn"]
|
|
@@ -419,25 +458,31 @@ class BatchPoster(MigrationTaskBase):
|
|
|
419
458
|
|
|
420
459
|
def prepare_record_for_upsert(self, new_record: dict, existing_record: dict):
|
|
421
460
|
if "source" in existing_record and "MARC" in existing_record["source"]:
|
|
422
|
-
self.
|
|
461
|
+
if self.task_configuration.patch_paths:
|
|
462
|
+
logging.debug(
|
|
463
|
+
"Record %s is a MARC record, patch_paths will be ignored",
|
|
464
|
+
existing_record["id"],
|
|
465
|
+
)
|
|
466
|
+
self.patch_record(new_record, existing_record, ["statisticalCodeIds", "administrativeNotes", "instanceStatusId"])
|
|
467
|
+
elif self.task_configuration.patch_existing_records:
|
|
468
|
+
self.patch_record(new_record, existing_record, self.task_configuration.patch_paths)
|
|
423
469
|
else:
|
|
424
470
|
updates = {
|
|
425
471
|
"_version": existing_record["_version"],
|
|
426
472
|
}
|
|
427
473
|
self.keep_existing_fields(updates, existing_record)
|
|
428
474
|
keep_new = {k: v for k, v in new_record.items() if k in ["statisticalCodeIds", "administrativeNotes"]}
|
|
429
|
-
|
|
430
|
-
self.
|
|
431
|
-
self.
|
|
432
|
-
self.
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
updates[key] = existing_record[key]
|
|
475
|
+
keep_existing = {}
|
|
476
|
+
self.handle_upsert_for_statistical_codes(existing_record, keep_existing)
|
|
477
|
+
self.handle_upsert_for_administrative_notes(existing_record, keep_existing)
|
|
478
|
+
self.handle_upsert_for_temporary_locations(existing_record, keep_existing)
|
|
479
|
+
self.handle_upsert_for_temporary_loan_types(existing_record, keep_existing)
|
|
480
|
+
for k, v in keep_existing.items():
|
|
481
|
+
if isinstance(v, list) and k in keep_new:
|
|
482
|
+
keep_new[k] = list(dict.fromkeys(v + keep_new.get(k, [])))
|
|
483
|
+
elif k not in keep_new:
|
|
484
|
+
keep_new[k] = v
|
|
485
|
+
updates.update(keep_new)
|
|
441
486
|
new_record.update(updates)
|
|
442
487
|
|
|
443
488
|
async def get_with_retry(self, client: httpx.AsyncClient, url: str, params=None):
|
|
@@ -1076,3 +1121,85 @@ def get_req_size(response: httpx.Response):
|
|
|
1076
1121
|
size += "\r\n".join(f"{k}{v}" for k, v in response.request.headers.items())
|
|
1077
1122
|
size += response.request.content.decode("utf-8") or ""
|
|
1078
1123
|
return get_human_readable(len(size.encode("utf-8")))
|
|
1124
|
+
|
|
1125
|
+
def parse_path(path):
|
|
1126
|
+
"""
|
|
1127
|
+
Parses a path like 'foo.bar[0].baz' into ['foo', 'bar', 0, 'baz']
|
|
1128
|
+
"""
|
|
1129
|
+
tokens = []
|
|
1130
|
+
# Split by dot, then extract indices
|
|
1131
|
+
for part in path.split('.'):
|
|
1132
|
+
# Find all [index] parts
|
|
1133
|
+
matches = re.findall(r'([^\[\]]+)|\[(\d+)\]', part)
|
|
1134
|
+
for name, idx in matches:
|
|
1135
|
+
if name:
|
|
1136
|
+
tokens.append(name)
|
|
1137
|
+
if idx:
|
|
1138
|
+
tokens.append(int(idx))
|
|
1139
|
+
return tokens
|
|
1140
|
+
|
|
1141
|
+
def get_by_path(data, path):
|
|
1142
|
+
keys = parse_path(path)
|
|
1143
|
+
for key in keys:
|
|
1144
|
+
data = data[key]
|
|
1145
|
+
return data
|
|
1146
|
+
|
|
1147
|
+
def set_by_path(data, path, value):
|
|
1148
|
+
keys = parse_path(path)
|
|
1149
|
+
for i, key in enumerate(keys[:-1]):
|
|
1150
|
+
next_key = keys[i + 1]
|
|
1151
|
+
if isinstance(key, int):
|
|
1152
|
+
while len(data) <= key:
|
|
1153
|
+
data.append({} if not isinstance(next_key, int) else [])
|
|
1154
|
+
data = data[key]
|
|
1155
|
+
else:
|
|
1156
|
+
if key not in data or not isinstance(data[key], (dict, list)):
|
|
1157
|
+
data[key] = {} if not isinstance(next_key, int) else []
|
|
1158
|
+
data = data[key]
|
|
1159
|
+
last_key = keys[-1]
|
|
1160
|
+
if isinstance(last_key, int):
|
|
1161
|
+
while len(data) <= last_key:
|
|
1162
|
+
data.append(None)
|
|
1163
|
+
data[last_key] = value
|
|
1164
|
+
else:
|
|
1165
|
+
data[last_key] = value
|
|
1166
|
+
|
|
1167
|
+
def extract_paths(data, paths):
|
|
1168
|
+
result = {}
|
|
1169
|
+
for path in paths:
|
|
1170
|
+
try:
|
|
1171
|
+
value = get_by_path(data, path)
|
|
1172
|
+
set_by_path(result, path, value)
|
|
1173
|
+
except KeyError:
|
|
1174
|
+
continue
|
|
1175
|
+
return result
|
|
1176
|
+
|
|
1177
|
+
def deep_update(target, patch):
|
|
1178
|
+
"""
|
|
1179
|
+
Recursively update target dict/list with values from patch dict/list.
|
|
1180
|
+
For lists, only non-None values in patch are merged into target.
|
|
1181
|
+
"""
|
|
1182
|
+
if isinstance(patch, dict):
|
|
1183
|
+
for k, v in patch.items():
|
|
1184
|
+
if (
|
|
1185
|
+
k in target
|
|
1186
|
+
and isinstance(target[k], (dict, list))
|
|
1187
|
+
and isinstance(v, (dict, list))
|
|
1188
|
+
):
|
|
1189
|
+
deep_update(target[k], v)
|
|
1190
|
+
else:
|
|
1191
|
+
target[k] = v
|
|
1192
|
+
elif isinstance(patch, list):
|
|
1193
|
+
for i, v in enumerate(patch):
|
|
1194
|
+
if v is None:
|
|
1195
|
+
continue # Skip None values, leave target unchanged
|
|
1196
|
+
if i < len(target):
|
|
1197
|
+
if isinstance(target[i], (dict, list)) and isinstance(v, (dict, list)):
|
|
1198
|
+
deep_update(target[i], v)
|
|
1199
|
+
else:
|
|
1200
|
+
target[i] = v
|
|
1201
|
+
else:
|
|
1202
|
+
# Only append if not None
|
|
1203
|
+
target.append(v)
|
|
1204
|
+
else:
|
|
1205
|
+
return patch
|
|
File without changes
|
|
File without changes
|
{folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/__init__.py
RENAMED
|
File without changes
|
{folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/__main__.py
RENAMED
|
File without changes
|
|
File without changes
|
{folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/colors.py
RENAMED
|
File without changes
|
|
File without changes
|
{folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/custom_dict.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/helper.py
RENAMED
|
File without changes
|
|
File without changes
|
{folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/i18n_config.py
RENAMED
|
File without changes
|
|
File without changes
|
{folio_migration_tools-1.9.6 → folio_migration_tools-1.9.8}/src/folio_migration_tools/mapper_base.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|