folio-migration-tools 1.10.0__py3-none-any.whl → 1.10.0b2__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/__main__.py +0 -9
- folio_migration_tools/circulation_helper.py +5 -6
- folio_migration_tools/folder_structure.py +6 -16
- folio_migration_tools/helper.py +6 -7
- folio_migration_tools/holdings_helper.py +3 -4
- folio_migration_tools/library_configuration.py +0 -12
- folio_migration_tools/mapper_base.py +6 -7
- folio_migration_tools/mapping_file_transformation/user_mapper.py +0 -4
- folio_migration_tools/marc_rules_transformation/conditions.py +29 -0
- folio_migration_tools/marc_rules_transformation/marc_file_processor.py +9 -19
- folio_migration_tools/marc_rules_transformation/rules_mapper_authorities.py +242 -0
- folio_migration_tools/marc_rules_transformation/rules_mapper_base.py +15 -10
- folio_migration_tools/marc_rules_transformation/rules_mapper_bibs.py +2 -3
- folio_migration_tools/marc_rules_transformation/rules_mapper_holdings.py +5 -6
- folio_migration_tools/migration_report.py +6 -17
- folio_migration_tools/migration_tasks/authority_transformer.py +118 -0
- folio_migration_tools/migration_tasks/batch_poster.py +298 -219
- folio_migration_tools/migration_tasks/bibs_transformer.py +2 -2
- folio_migration_tools/migration_tasks/holdings_csv_transformer.py +9 -9
- folio_migration_tools/migration_tasks/holdings_marc_transformer.py +3 -3
- folio_migration_tools/migration_tasks/items_transformer.py +4 -6
- folio_migration_tools/migration_tasks/loans_migrator.py +18 -19
- folio_migration_tools/migration_tasks/manual_fee_fines_transformer.py +3 -3
- folio_migration_tools/migration_tasks/migration_task_base.py +5 -13
- folio_migration_tools/migration_tasks/orders_transformer.py +3 -4
- folio_migration_tools/migration_tasks/requests_migrator.py +9 -10
- folio_migration_tools/migration_tasks/reserves_migrator.py +4 -5
- folio_migration_tools/migration_tasks/user_transformer.py +5 -15
- folio_migration_tools/translations/en.json +7 -0
- {folio_migration_tools-1.10.0.dist-info → folio_migration_tools-1.10.0b2.dist-info}/METADATA +2 -3
- {folio_migration_tools-1.10.0.dist-info → folio_migration_tools-1.10.0b2.dist-info}/RECORD +33 -32
- folio_migration_tools/i18n_cache.py +0 -79
- {folio_migration_tools-1.10.0.dist-info → folio_migration_tools-1.10.0b2.dist-info}/WHEEL +0 -0
- {folio_migration_tools-1.10.0.dist-info → folio_migration_tools-1.10.0b2.dist-info}/entry_points.txt +0 -0
|
@@ -15,7 +15,6 @@ from folio_uuid.folio_uuid import FOLIONamespaces, FolioUUID
|
|
|
15
15
|
from folioclient import FolioClient
|
|
16
16
|
from pymarc import Field, Optional, Record, Subfield
|
|
17
17
|
|
|
18
|
-
from folio_migration_tools.i18n_cache import i18n_t
|
|
19
18
|
from folio_migration_tools.custom_exceptions import (
|
|
20
19
|
TransformationFieldMappingError,
|
|
21
20
|
TransformationProcessError,
|
|
@@ -300,13 +299,13 @@ class RulesMapperBase(MapperBase):
|
|
|
300
299
|
def perform_proxy_mapping(self, marc_field):
|
|
301
300
|
proxy_mapping = next(iter(self.mappings.get("880", [])), [])
|
|
302
301
|
if "6" not in marc_field:
|
|
303
|
-
self.migration_report.add("Field880Mappings",
|
|
302
|
+
self.migration_report.add("Field880Mappings", i18n.t("Records without $6"))
|
|
304
303
|
return None
|
|
305
304
|
if not proxy_mapping or not proxy_mapping.get("fieldReplacementBy3Digits", False):
|
|
306
305
|
return None
|
|
307
306
|
if not marc_field["6"][:3] or len(marc_field["6"][:3]) != 3:
|
|
308
307
|
self.migration_report.add(
|
|
309
|
-
"Field880Mappings",
|
|
308
|
+
"Field880Mappings", i18n.t("Records with unexpected length in $6")
|
|
310
309
|
)
|
|
311
310
|
return None
|
|
312
311
|
first_three = marc_field["6"][:3]
|
|
@@ -321,16 +320,16 @@ class RulesMapperBase(MapperBase):
|
|
|
321
320
|
)
|
|
322
321
|
self.migration_report.add(
|
|
323
322
|
"Field880Mappings",
|
|
324
|
-
|
|
323
|
+
i18n.t("Source digits")
|
|
325
324
|
+ f": {marc_field['6']} "
|
|
326
|
-
+
|
|
325
|
+
+ i18n.t("Target field")
|
|
327
326
|
+ f": {target_field}",
|
|
328
327
|
)
|
|
329
328
|
mappings = self.mappings.get(target_field, {})
|
|
330
329
|
if not mappings:
|
|
331
330
|
self.migration_report.add(
|
|
332
331
|
"Field880Mappings",
|
|
333
|
-
|
|
332
|
+
i18n.t("Mapping not set up for target field")
|
|
334
333
|
+ f": {target_field} ({marc_field['6']})",
|
|
335
334
|
)
|
|
336
335
|
return mappings
|
|
@@ -338,7 +337,7 @@ class RulesMapperBase(MapperBase):
|
|
|
338
337
|
def report_marc_stats(
|
|
339
338
|
self, marc_field: Field, bad_tags, legacy_ids, ignored_subsequent_fields
|
|
340
339
|
):
|
|
341
|
-
self.migration_report.add("Trivia",
|
|
340
|
+
self.migration_report.add("Trivia", i18n.t("Total number of Tags processed"))
|
|
342
341
|
self.report_source_and_links(marc_field)
|
|
343
342
|
self.report_bad_tags(marc_field, bad_tags, legacy_ids)
|
|
344
343
|
mapped = marc_field.tag in self.mappings
|
|
@@ -352,7 +351,7 @@ class RulesMapperBase(MapperBase):
|
|
|
352
351
|
for subfield_2 in marc_field.get_subfields("2"):
|
|
353
352
|
self.migration_report.add(
|
|
354
353
|
"AuthoritySources",
|
|
355
|
-
|
|
354
|
+
i18n.t("Source of heading or term") + f": {subfield_2.split(' ')[0]}",
|
|
356
355
|
)
|
|
357
356
|
for subfield_0 in marc_field.get_subfields("0"):
|
|
358
357
|
code = ""
|
|
@@ -364,7 +363,7 @@ class RulesMapperBase(MapperBase):
|
|
|
364
363
|
code = subfield_0[: subfield_0.find(url.path)]
|
|
365
364
|
if code:
|
|
366
365
|
self.migration_report.add(
|
|
367
|
-
"AuthoritySources",
|
|
366
|
+
"AuthoritySources", i18n.t("$0 base uri or source code") + f": {code}"
|
|
368
367
|
)
|
|
369
368
|
|
|
370
369
|
def apply_rules(self, marc_field: pymarc.Field, mapping, legacy_ids):
|
|
@@ -403,7 +402,7 @@ class RulesMapperBase(MapperBase):
|
|
|
403
402
|
)
|
|
404
403
|
trfe.log_it()
|
|
405
404
|
self.migration_report.add_general_statistics(
|
|
406
|
-
|
|
405
|
+
i18n.t("Records failed due to an error. See data issues log for details")
|
|
407
406
|
)
|
|
408
407
|
except Exception as exception:
|
|
409
408
|
self.handle_generic_exception(self.parsed_records, exception)
|
|
@@ -963,6 +962,7 @@ class RulesMapperBase(MapperBase):
|
|
|
963
962
|
srs_types = {
|
|
964
963
|
FOLIONamespaces.holdings: FOLIONamespaces.srs_records_holdingsrecord,
|
|
965
964
|
FOLIONamespaces.instances: FOLIONamespaces.srs_records_bib,
|
|
965
|
+
FOLIONamespaces.authorities: FOLIONamespaces.srs_records_auth,
|
|
966
966
|
FOLIONamespaces.edifact: FOLIONamespaces.srs_records_edifact,
|
|
967
967
|
}
|
|
968
968
|
|
|
@@ -1020,6 +1020,7 @@ class RulesMapperBase(MapperBase):
|
|
|
1020
1020
|
record_types = {
|
|
1021
1021
|
FOLIONamespaces.holdings: "MARC_HOLDING",
|
|
1022
1022
|
FOLIONamespaces.instances: "MARC_BIB",
|
|
1023
|
+
FOLIONamespaces.authorities: "MARC_AUTHORITY",
|
|
1023
1024
|
FOLIONamespaces.edifact: "EDIFACT",
|
|
1024
1025
|
}
|
|
1025
1026
|
|
|
@@ -1032,6 +1033,10 @@ class RulesMapperBase(MapperBase):
|
|
|
1032
1033
|
"holdingsId": folio_object["id"],
|
|
1033
1034
|
"holdingsHrid": folio_object.get("hrid", ""),
|
|
1034
1035
|
},
|
|
1036
|
+
FOLIONamespaces.authorities: {
|
|
1037
|
+
"authorityId": folio_object["id"],
|
|
1038
|
+
"authorityHrid": marc_record["001"].data,
|
|
1039
|
+
},
|
|
1035
1040
|
FOLIONamespaces.edifact: {},
|
|
1036
1041
|
}
|
|
1037
1042
|
|
|
@@ -17,7 +17,6 @@ from folioclient import FolioClient
|
|
|
17
17
|
from pymarc.record import Leader, Record
|
|
18
18
|
from pymarc.field import Field
|
|
19
19
|
|
|
20
|
-
from folio_migration_tools.i18n_cache import i18n_t
|
|
21
20
|
from folio_migration_tools.custom_exceptions import (
|
|
22
21
|
TransformationProcessError,
|
|
23
22
|
TransformationRecordFailedError,
|
|
@@ -98,7 +97,7 @@ class BibsRulesMapper(RulesMapperBase):
|
|
|
98
97
|
|
|
99
98
|
def handle_leader_05(self, marc_record: Record, legacy_ids: List[str]):
|
|
100
99
|
leader_05 = marc_record.leader[5] or "Empty"
|
|
101
|
-
self.migration_report.add("RecordStatus",
|
|
100
|
+
self.migration_report.add("RecordStatus", i18n.t("Original value") + f": {leader_05}")
|
|
102
101
|
if leader_05 not in ["a", "c", "d", "n", "p"]:
|
|
103
102
|
marc_record.leader = Leader(f"{marc_record.leader[:5]}c{marc_record.leader[6:]}")
|
|
104
103
|
self.migration_report.add(
|
|
@@ -324,7 +323,7 @@ class BibsRulesMapper(RulesMapperBase):
|
|
|
324
323
|
raise TransformationProcessError("", "No instance_types setup in tenant")
|
|
325
324
|
|
|
326
325
|
if "336" in marc_record and "b" not in marc_record["336"]:
|
|
327
|
-
self.migration_report.add("RecourceTypeMapping",
|
|
326
|
+
self.migration_report.add("RecourceTypeMapping", i18n.t("Subfield b not in 336"))
|
|
328
327
|
if "a" in marc_record["336"]:
|
|
329
328
|
return_id = get_folio_id_by_name(marc_record["336"]["a"])
|
|
330
329
|
|
|
@@ -12,7 +12,6 @@ from pymarc import Optional
|
|
|
12
12
|
from pymarc.field import Field
|
|
13
13
|
from pymarc.record import Record
|
|
14
14
|
|
|
15
|
-
from folio_migration_tools.i18n_cache import i18n_t
|
|
16
15
|
from folio_migration_tools.custom_exceptions import (
|
|
17
16
|
TransformationFieldMappingError,
|
|
18
17
|
TransformationProcessError,
|
|
@@ -254,7 +253,7 @@ class RulesMapperHoldings(RulesMapperBase):
|
|
|
254
253
|
ignored_subsequent_fields (_type_): _description_
|
|
255
254
|
index_or_legacy_ids (_type_): _description_
|
|
256
255
|
"""
|
|
257
|
-
self.migration_report.add("Trivia",
|
|
256
|
+
self.migration_report.add("Trivia", i18n.t("Total number of Tags processed"))
|
|
258
257
|
if marc_field.tag not in self.mappings:
|
|
259
258
|
self.report_legacy_mapping(marc_field.tag, True, False)
|
|
260
259
|
elif marc_field.tag not in ignored_subsequent_fields:
|
|
@@ -585,7 +584,7 @@ class RulesMapperHoldings(RulesMapperBase):
|
|
|
585
584
|
Helper.log_data_issue(
|
|
586
585
|
legacy_ids,
|
|
587
586
|
(
|
|
588
|
-
|
|
587
|
+
i18n.t("blurbs.HoldingsTypeMapping.title") + " is 'unknown'. "
|
|
589
588
|
"(leader 06 is set to 'u') Check if this is correct"
|
|
590
589
|
),
|
|
591
590
|
ldr06,
|
|
@@ -599,14 +598,14 @@ class RulesMapperHoldings(RulesMapperBase):
|
|
|
599
598
|
folio_holding["holdingsTypeId"] = self.fallback_holdings_type_id
|
|
600
599
|
self.migration_report.add(
|
|
601
600
|
"HoldingsTypeMapping",
|
|
602
|
-
|
|
601
|
+
i18n.t("An Unmapped")
|
|
603
602
|
+ f" {ldr06} -> {holdings_type} -> "
|
|
604
|
-
+
|
|
603
|
+
+ i18n.t("Unmapped"),
|
|
605
604
|
)
|
|
606
605
|
Helper.log_data_issue(
|
|
607
606
|
legacy_ids,
|
|
608
607
|
(
|
|
609
|
-
|
|
608
|
+
i18n.t("blurbs.HoldingsTypeMapping.title", locale="en")
|
|
610
609
|
+ ". leader 06 was unmapped."
|
|
611
610
|
),
|
|
612
611
|
ldr06,
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
import json
|
|
3
2
|
import i18n
|
|
4
3
|
from datetime import datetime
|
|
5
4
|
from datetime import timezone
|
|
6
5
|
|
|
7
|
-
from folio_migration_tools.i18n_cache import i18n_t
|
|
8
|
-
|
|
9
6
|
|
|
10
7
|
class MigrationReport:
|
|
11
8
|
"""Class responsible for handling the migration report"""
|
|
@@ -50,14 +47,6 @@ class MigrationReport:
|
|
|
50
47
|
"""
|
|
51
48
|
self.add("GeneralStatistics", measure_to_add)
|
|
52
49
|
|
|
53
|
-
def _write_json_report(self, report_file):
|
|
54
|
-
"""Writes the raw migration report data to a JSON file.
|
|
55
|
-
|
|
56
|
-
Args:
|
|
57
|
-
report_file: An open file object to write the JSON data to
|
|
58
|
-
"""
|
|
59
|
-
json.dump(self.report, report_file, indent=2)
|
|
60
|
-
|
|
61
50
|
def write_migration_report(
|
|
62
51
|
self,
|
|
63
52
|
report_title,
|
|
@@ -77,13 +66,13 @@ class MigrationReport:
|
|
|
77
66
|
[
|
|
78
67
|
"# " + report_title,
|
|
79
68
|
i18n.t("blurbs.Introduction.description"),
|
|
80
|
-
"## " +
|
|
69
|
+
"## " + i18n.t("Timings"),
|
|
81
70
|
"",
|
|
82
|
-
|
|
71
|
+
i18n.t("Measure") + " | " + i18n.t("Value"),
|
|
83
72
|
"--- | ---:",
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
73
|
+
i18n.t("Time Started:") + " | " + datetime.isoformat(time_started),
|
|
74
|
+
i18n.t("Time Finished:") + " | " + datetime.isoformat(time_finished),
|
|
75
|
+
i18n.t("Elapsed time:") + " | " + str(time_finished - time_started),
|
|
87
76
|
]
|
|
88
77
|
)
|
|
89
78
|
)
|
|
@@ -100,7 +89,7 @@ class MigrationReport:
|
|
|
100
89
|
+ i18n.t("Click to expand all %{count} things", count=len(self.report[a]))
|
|
101
90
|
+ "</summary>",
|
|
102
91
|
"",
|
|
103
|
-
|
|
92
|
+
i18n.t("Measure") + " | " + i18n.t("Count"),
|
|
104
93
|
"--- | ---:",
|
|
105
94
|
]
|
|
106
95
|
+ [
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Annotated
|
|
3
|
+
from typing import List
|
|
4
|
+
import i18n
|
|
5
|
+
|
|
6
|
+
from folio_uuid.folio_namespaces import FOLIONamespaces
|
|
7
|
+
from pydantic import Field
|
|
8
|
+
|
|
9
|
+
from folio_migration_tools.helper import Helper
|
|
10
|
+
from folio_migration_tools.library_configuration import FileDefinition
|
|
11
|
+
from folio_migration_tools.library_configuration import IlsFlavour
|
|
12
|
+
from folio_migration_tools.library_configuration import LibraryConfiguration
|
|
13
|
+
from folio_migration_tools.marc_rules_transformation.marc_file_processor import (
|
|
14
|
+
MarcFileProcessor,
|
|
15
|
+
)
|
|
16
|
+
from folio_migration_tools.marc_rules_transformation.rules_mapper_authorities import (
|
|
17
|
+
AuthorityMapper,
|
|
18
|
+
)
|
|
19
|
+
from folio_migration_tools.migration_tasks.migration_task_base import MigrationTaskBase
|
|
20
|
+
from folio_migration_tools.task_configuration import AbstractTaskConfiguration
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class AuthorityTransformer(MigrationTaskBase):
|
|
24
|
+
class TaskConfiguration(AbstractTaskConfiguration):
|
|
25
|
+
name: Annotated[
|
|
26
|
+
str,
|
|
27
|
+
Field(
|
|
28
|
+
description=(
|
|
29
|
+
"Name of this migration task. The name is being used to call the specific "
|
|
30
|
+
"task, and to distinguish tasks of similar types"
|
|
31
|
+
)
|
|
32
|
+
),
|
|
33
|
+
]
|
|
34
|
+
migration_task_type: Annotated[
|
|
35
|
+
str,
|
|
36
|
+
Field(
|
|
37
|
+
title="Migration task type",
|
|
38
|
+
description=("The type of migration task you want to perform"),
|
|
39
|
+
),
|
|
40
|
+
]
|
|
41
|
+
files: Annotated[
|
|
42
|
+
List[FileDefinition],
|
|
43
|
+
Field(
|
|
44
|
+
title="Source files", description=("List of MARC21 files with authority records")
|
|
45
|
+
),
|
|
46
|
+
]
|
|
47
|
+
ils_flavour: Annotated[
|
|
48
|
+
IlsFlavour,
|
|
49
|
+
Field(
|
|
50
|
+
title="ILS flavour", description="The type of ILS you are migrating records from."
|
|
51
|
+
),
|
|
52
|
+
]
|
|
53
|
+
tags_to_delete: Annotated[
|
|
54
|
+
List[str],
|
|
55
|
+
Field(
|
|
56
|
+
title="Tags to delete from MARC record",
|
|
57
|
+
description=(
|
|
58
|
+
"Tags in the incoming MARC authority that the process should remove "
|
|
59
|
+
"before adding them into FOLIO. These tags will be used in the "
|
|
60
|
+
"transformation before getting removed."
|
|
61
|
+
),
|
|
62
|
+
),
|
|
63
|
+
] = []
|
|
64
|
+
create_source_records: Annotated[
|
|
65
|
+
bool,
|
|
66
|
+
Field(
|
|
67
|
+
title="Create source records",
|
|
68
|
+
description=(
|
|
69
|
+
"Controls wheter or not to retain the MARC records in Source Record Storage."
|
|
70
|
+
),
|
|
71
|
+
),
|
|
72
|
+
] = True
|
|
73
|
+
|
|
74
|
+
@staticmethod
|
|
75
|
+
def get_object_type() -> FOLIONamespaces:
|
|
76
|
+
return FOLIONamespaces.authorities
|
|
77
|
+
|
|
78
|
+
def __init__(
|
|
79
|
+
self,
|
|
80
|
+
task_config: TaskConfiguration,
|
|
81
|
+
library_config: LibraryConfiguration,
|
|
82
|
+
use_logging: bool = True,
|
|
83
|
+
):
|
|
84
|
+
super().__init__(library_config, task_config, use_logging)
|
|
85
|
+
self.processor: MarcFileProcessor
|
|
86
|
+
self.check_source_files(
|
|
87
|
+
self.folder_structure.legacy_records_folder, self.task_configuration.files
|
|
88
|
+
)
|
|
89
|
+
self.mapper: AuthorityMapper = AuthorityMapper(
|
|
90
|
+
self.folio_client, library_config, task_config
|
|
91
|
+
)
|
|
92
|
+
self.auth_ids: set = set()
|
|
93
|
+
logging.info("Init done")
|
|
94
|
+
|
|
95
|
+
def do_work(self):
|
|
96
|
+
self.do_work_marc_transformer()
|
|
97
|
+
|
|
98
|
+
def wrap_up(self):
|
|
99
|
+
logging.info("Done. Transformer Wrapping up...")
|
|
100
|
+
self.extradata_writer.flush()
|
|
101
|
+
self.processor.wrap_up()
|
|
102
|
+
with open(self.folder_structure.migration_reports_file, "w+") as report_file:
|
|
103
|
+
self.mapper.migration_report.write_migration_report(
|
|
104
|
+
i18n.t("Authority records transformation report"),
|
|
105
|
+
report_file,
|
|
106
|
+
self.start_datetime,
|
|
107
|
+
)
|
|
108
|
+
Helper.print_mapping_report(
|
|
109
|
+
report_file,
|
|
110
|
+
self.mapper.parsed_records,
|
|
111
|
+
self.mapper.mapped_folio_fields,
|
|
112
|
+
self.mapper.mapped_legacy_fields,
|
|
113
|
+
)
|
|
114
|
+
logging.info(
|
|
115
|
+
"Done. Transformation report written to %s",
|
|
116
|
+
self.folder_structure.migration_reports_file.name,
|
|
117
|
+
)
|
|
118
|
+
self.clean_out_empty_logs()
|