folio-migration-tools 1.2.1__py3-none-any.whl → 1.9.10__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- folio_migration_tools/__init__.py +11 -0
- folio_migration_tools/__main__.py +169 -85
- folio_migration_tools/circulation_helper.py +96 -59
- folio_migration_tools/config_file_load.py +66 -0
- folio_migration_tools/custom_dict.py +6 -4
- folio_migration_tools/custom_exceptions.py +21 -19
- folio_migration_tools/extradata_writer.py +46 -0
- folio_migration_tools/folder_structure.py +63 -66
- folio_migration_tools/helper.py +29 -21
- folio_migration_tools/holdings_helper.py +57 -34
- folio_migration_tools/i18n_config.py +9 -0
- folio_migration_tools/library_configuration.py +173 -13
- folio_migration_tools/mapper_base.py +317 -106
- folio_migration_tools/mapping_file_transformation/courses_mapper.py +203 -0
- folio_migration_tools/mapping_file_transformation/holdings_mapper.py +83 -69
- folio_migration_tools/mapping_file_transformation/item_mapper.py +98 -94
- folio_migration_tools/mapping_file_transformation/manual_fee_fines_mapper.py +352 -0
- folio_migration_tools/mapping_file_transformation/mapping_file_mapper_base.py +702 -223
- folio_migration_tools/mapping_file_transformation/notes_mapper.py +90 -0
- folio_migration_tools/mapping_file_transformation/order_mapper.py +492 -0
- folio_migration_tools/mapping_file_transformation/organization_mapper.py +389 -0
- folio_migration_tools/mapping_file_transformation/ref_data_mapping.py +38 -27
- folio_migration_tools/mapping_file_transformation/user_mapper.py +149 -361
- folio_migration_tools/marc_rules_transformation/conditions.py +650 -246
- folio_migration_tools/marc_rules_transformation/holdings_statementsparser.py +292 -130
- folio_migration_tools/marc_rules_transformation/hrid_handler.py +244 -0
- folio_migration_tools/marc_rules_transformation/loc_language_codes.xml +20846 -0
- folio_migration_tools/marc_rules_transformation/marc_file_processor.py +300 -0
- folio_migration_tools/marc_rules_transformation/marc_reader_wrapper.py +136 -0
- folio_migration_tools/marc_rules_transformation/rules_mapper_authorities.py +241 -0
- folio_migration_tools/marc_rules_transformation/rules_mapper_base.py +681 -201
- folio_migration_tools/marc_rules_transformation/rules_mapper_bibs.py +395 -429
- folio_migration_tools/marc_rules_transformation/rules_mapper_holdings.py +531 -100
- folio_migration_tools/migration_report.py +85 -38
- folio_migration_tools/migration_tasks/__init__.py +1 -3
- folio_migration_tools/migration_tasks/authority_transformer.py +119 -0
- folio_migration_tools/migration_tasks/batch_poster.py +911 -198
- folio_migration_tools/migration_tasks/bibs_transformer.py +121 -116
- folio_migration_tools/migration_tasks/courses_migrator.py +192 -0
- folio_migration_tools/migration_tasks/holdings_csv_transformer.py +252 -247
- folio_migration_tools/migration_tasks/holdings_marc_transformer.py +321 -115
- folio_migration_tools/migration_tasks/items_transformer.py +264 -84
- folio_migration_tools/migration_tasks/loans_migrator.py +506 -195
- folio_migration_tools/migration_tasks/manual_fee_fines_transformer.py +187 -0
- folio_migration_tools/migration_tasks/migration_task_base.py +364 -74
- folio_migration_tools/migration_tasks/orders_transformer.py +373 -0
- folio_migration_tools/migration_tasks/organization_transformer.py +451 -0
- folio_migration_tools/migration_tasks/requests_migrator.py +130 -62
- folio_migration_tools/migration_tasks/reserves_migrator.py +253 -0
- folio_migration_tools/migration_tasks/user_transformer.py +180 -139
- folio_migration_tools/task_configuration.py +46 -0
- folio_migration_tools/test_infrastructure/__init__.py +0 -0
- folio_migration_tools/test_infrastructure/mocked_classes.py +406 -0
- folio_migration_tools/transaction_migration/legacy_loan.py +148 -34
- folio_migration_tools/transaction_migration/legacy_request.py +65 -25
- folio_migration_tools/transaction_migration/legacy_reserve.py +47 -0
- folio_migration_tools/transaction_migration/transaction_result.py +12 -1
- folio_migration_tools/translations/en.json +476 -0
- folio_migration_tools-1.9.10.dist-info/METADATA +169 -0
- folio_migration_tools-1.9.10.dist-info/RECORD +67 -0
- {folio_migration_tools-1.2.1.dist-info → folio_migration_tools-1.9.10.dist-info}/WHEEL +1 -2
- folio_migration_tools-1.9.10.dist-info/entry_points.txt +3 -0
- folio_migration_tools/generate_schemas.py +0 -46
- folio_migration_tools/mapping_file_transformation/mapping_file_mapping_base_impl.py +0 -44
- folio_migration_tools/mapping_file_transformation/user_mapper_base.py +0 -212
- folio_migration_tools/marc_rules_transformation/bibs_processor.py +0 -163
- folio_migration_tools/marc_rules_transformation/holdings_processor.py +0 -284
- folio_migration_tools/report_blurbs.py +0 -219
- folio_migration_tools/transaction_migration/legacy_fee_fine.py +0 -36
- folio_migration_tools-1.2.1.dist-info/METADATA +0 -134
- folio_migration_tools-1.2.1.dist-info/RECORD +0 -50
- folio_migration_tools-1.2.1.dist-info/top_level.txt +0 -1
- {folio_migration_tools-1.2.1.dist-info → folio_migration_tools-1.9.10.dist-info/licenses}/LICENSE +0 -0
|
@@ -1,43 +1,106 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
import
|
|
3
|
-
import time
|
|
4
|
-
from datetime import datetime as dt
|
|
5
|
-
from os.path import isfile
|
|
6
|
-
from typing import List, Optional
|
|
2
|
+
from typing import Annotated, List
|
|
7
3
|
|
|
4
|
+
import i18n
|
|
8
5
|
from folio_uuid.folio_namespaces import FOLIONamespaces
|
|
9
|
-
from
|
|
10
|
-
|
|
11
|
-
TransformationProcessError,
|
|
12
|
-
TransformationRecordFailedError,
|
|
13
|
-
)
|
|
6
|
+
from pydantic import Field
|
|
7
|
+
|
|
14
8
|
from folio_migration_tools.helper import Helper
|
|
15
9
|
from folio_migration_tools.library_configuration import (
|
|
16
|
-
FileDefinition,
|
|
17
|
-
HridHandling,
|
|
18
10
|
IlsFlavour,
|
|
19
11
|
LibraryConfiguration,
|
|
20
12
|
)
|
|
21
|
-
from folio_migration_tools.marc_rules_transformation.
|
|
13
|
+
from folio_migration_tools.marc_rules_transformation.marc_file_processor import (
|
|
14
|
+
MarcFileProcessor,
|
|
15
|
+
)
|
|
22
16
|
from folio_migration_tools.marc_rules_transformation.rules_mapper_bibs import (
|
|
23
17
|
BibsRulesMapper,
|
|
24
18
|
)
|
|
25
|
-
from folio_migration_tools.migration_tasks.migration_task_base import MigrationTaskBase
|
|
26
|
-
from pydantic import BaseModel
|
|
27
|
-
from pymarc import MARCReader
|
|
28
|
-
from pymarc.record import Record
|
|
19
|
+
from folio_migration_tools.migration_tasks.migration_task_base import MarcTaskConfigurationBase, MigrationTaskBase
|
|
29
20
|
|
|
30
21
|
|
|
31
22
|
class BibsTransformer(MigrationTaskBase):
|
|
32
|
-
class TaskConfiguration(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
23
|
+
class TaskConfiguration(MarcTaskConfigurationBase):
|
|
24
|
+
ils_flavour: Annotated[
|
|
25
|
+
IlsFlavour,
|
|
26
|
+
Field(
|
|
27
|
+
title="ILS flavour",
|
|
28
|
+
description="The type of ILS you are migrating records from.",
|
|
29
|
+
alias="ils_flavor"
|
|
30
|
+
),
|
|
31
|
+
]
|
|
32
|
+
custom_bib_id_field: Annotated[
|
|
33
|
+
str,
|
|
34
|
+
Field(
|
|
35
|
+
title="Custom BIB ID field",
|
|
36
|
+
description=(
|
|
37
|
+
'A string representing a MARC field with optional subfield indicated by a "$" '
|
|
38
|
+
'(eg. "991$a") from which to draw legacy Bib ID. Use this in combination '
|
|
39
|
+
'with `ilsFlavour: "custom"`. Defaults to "001", and is ignored for all other '
|
|
40
|
+
"ilsFlavours."
|
|
41
|
+
),
|
|
42
|
+
),
|
|
43
|
+
] = "001"
|
|
44
|
+
add_administrative_notes_with_legacy_ids: Annotated[
|
|
45
|
+
bool,
|
|
46
|
+
Field(
|
|
47
|
+
title="Add administrative notes with legacy IDs",
|
|
48
|
+
description=(
|
|
49
|
+
"If set to true, an Administrative note will be added to the records "
|
|
50
|
+
"containing the legacy ID. Use this in order to protect the values from "
|
|
51
|
+
"getting overwritten by overlays,"
|
|
52
|
+
),
|
|
53
|
+
),
|
|
54
|
+
] = True
|
|
55
|
+
tags_to_delete: Annotated[
|
|
56
|
+
List[str],
|
|
57
|
+
Field(
|
|
58
|
+
title="Tags to delete from MARC record",
|
|
59
|
+
description=(
|
|
60
|
+
"Tags in the incoming MARC authority that the process should remove "
|
|
61
|
+
"before adding them into FOLIO. These tags will be used in the "
|
|
62
|
+
"transformation before getting removed."
|
|
63
|
+
),
|
|
64
|
+
),
|
|
65
|
+
] = []
|
|
66
|
+
data_import_marc: Annotated[
|
|
67
|
+
bool,
|
|
68
|
+
Field(
|
|
69
|
+
title="Generate a MARC file for data import overlay of instances",
|
|
70
|
+
description=(
|
|
71
|
+
"If set to true, the process will generate a file of binary MARC records that can"
|
|
72
|
+
"be imported into FOLIO using the Data Import APIs. If set to false, only a file"
|
|
73
|
+
"of FOLIO instance records (and optional SRS records) will be generated."
|
|
74
|
+
),
|
|
75
|
+
)
|
|
76
|
+
] = True
|
|
77
|
+
parse_cataloged_date: Annotated[
|
|
78
|
+
bool,
|
|
79
|
+
Field(
|
|
80
|
+
title="Parse cataloged date",
|
|
81
|
+
description=(
|
|
82
|
+
"Parse fields mapped to catalogedDate into a FOLIO accepted date string using "
|
|
83
|
+
"dateutil.parser. Verify results carefully when using"
|
|
84
|
+
),
|
|
85
|
+
),
|
|
86
|
+
] = False
|
|
87
|
+
reset_hrid_settings: Annotated[
|
|
88
|
+
bool,
|
|
89
|
+
Field(
|
|
90
|
+
title="Reset HRID settings",
|
|
91
|
+
description=(
|
|
92
|
+
"Setting to true means the task will "
|
|
93
|
+
"reset the HRID counters for this particular record type"
|
|
94
|
+
),
|
|
95
|
+
),
|
|
96
|
+
] = False
|
|
97
|
+
update_hrid_settings: Annotated[
|
|
98
|
+
bool,
|
|
99
|
+
Field(
|
|
100
|
+
title="Update HRID settings",
|
|
101
|
+
description="At the end of the run, update FOLIO with the HRID settings",
|
|
102
|
+
),
|
|
103
|
+
] = True
|
|
41
104
|
|
|
42
105
|
@staticmethod
|
|
43
106
|
def get_object_type() -> FOLIONamespaces:
|
|
@@ -47,72 +110,48 @@ class BibsTransformer(MigrationTaskBase):
|
|
|
47
110
|
self,
|
|
48
111
|
task_config: TaskConfiguration,
|
|
49
112
|
library_config: LibraryConfiguration,
|
|
113
|
+
folio_client,
|
|
50
114
|
use_logging: bool = True,
|
|
51
115
|
):
|
|
52
|
-
|
|
53
|
-
super().__init__(library_config, task_config, use_logging)
|
|
116
|
+
super().__init__(library_config, task_config, folio_client, use_logging)
|
|
54
117
|
self.task_config = task_config
|
|
55
|
-
|
|
56
|
-
self.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
raise TransformationProcessError(
|
|
64
|
-
"",
|
|
65
|
-
f"Files {ret_str} not found in {self.folder_structure.data_folder / 'items'}",
|
|
118
|
+
self.task_configuration = self.task_config
|
|
119
|
+
if self.task_config.statistical_codes_map_file_name:
|
|
120
|
+
statcode_mapping = self.load_ref_data_mapping_file(
|
|
121
|
+
"statisticalCodeIds",
|
|
122
|
+
self.folder_structure.mapping_files_folder
|
|
123
|
+
/ self.task_config.statistical_codes_map_file_name,
|
|
124
|
+
[],
|
|
125
|
+
False,
|
|
66
126
|
)
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
self.
|
|
127
|
+
else:
|
|
128
|
+
statcode_mapping = None
|
|
129
|
+
self.processor: MarcFileProcessor
|
|
130
|
+
self.check_source_files(
|
|
131
|
+
self.folder_structure.legacy_records_folder, self.task_configuration.files
|
|
132
|
+
)
|
|
133
|
+
self.mapper = BibsRulesMapper(self.folio_client, library_config, self.task_configuration, statcode_mapping)
|
|
134
|
+
self.bib_ids: set = set()
|
|
135
|
+
if (
|
|
136
|
+
self.task_configuration.reset_hrid_settings
|
|
137
|
+
and self.task_configuration.update_hrid_settings
|
|
138
|
+
):
|
|
139
|
+
self.mapper.hrid_handler.reset_instance_hrid_counter()
|
|
74
140
|
logging.info("Init done")
|
|
75
141
|
|
|
76
142
|
def do_work(self):
|
|
77
|
-
|
|
78
|
-
with open(
|
|
79
|
-
self.folder_structure.created_objects_path, "w+"
|
|
80
|
-
) as created_records_file:
|
|
81
|
-
self.processor = BibsProcessor(
|
|
82
|
-
self.mapper,
|
|
83
|
-
self.folio_client,
|
|
84
|
-
created_records_file,
|
|
85
|
-
self.folder_structure,
|
|
86
|
-
)
|
|
87
|
-
for file_obj in self.files:
|
|
88
|
-
try:
|
|
89
|
-
with open(
|
|
90
|
-
self.folder_structure.legacy_records_folder
|
|
91
|
-
/ file_obj.file_name,
|
|
92
|
-
"rb",
|
|
93
|
-
) as marc_file:
|
|
94
|
-
reader = MARCReader(marc_file, to_unicode=True, permissive=True)
|
|
95
|
-
reader.hide_utf8_warnings = True
|
|
96
|
-
reader.force_utf8 = False
|
|
97
|
-
logging.info("running %s", file_obj.file_name)
|
|
98
|
-
self.read_records(reader, file_obj)
|
|
99
|
-
except TransformationProcessError as tpe:
|
|
100
|
-
logging.critical(tpe)
|
|
101
|
-
sys.exit(1)
|
|
102
|
-
except Exception:
|
|
103
|
-
logging.exception(file_obj, stack_info=True)
|
|
104
|
-
logging.critical(
|
|
105
|
-
"File %s failed for unknown reason. Halting", file_obj.file_name
|
|
106
|
-
)
|
|
107
|
-
sys.exit(1)
|
|
143
|
+
self.do_work_marc_transformer()
|
|
108
144
|
|
|
109
145
|
def wrap_up(self):
|
|
110
|
-
logging.info("Done.
|
|
146
|
+
logging.info("Done. Transformer wrapping up...")
|
|
147
|
+
self.extradata_writer.flush()
|
|
111
148
|
self.processor.wrap_up()
|
|
112
149
|
with open(self.folder_structure.migration_reports_file, "w+") as report_file:
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
150
|
+
self.mapper.migration_report.write_migration_report(
|
|
151
|
+
i18n.t("Bibliographic records transformation report"),
|
|
152
|
+
report_file,
|
|
153
|
+
self.start_datetime,
|
|
154
|
+
)
|
|
116
155
|
Helper.print_mapping_report(
|
|
117
156
|
report_file,
|
|
118
157
|
self.mapper.parsed_records,
|
|
@@ -124,38 +163,4 @@ class BibsTransformer(MigrationTaskBase):
|
|
|
124
163
|
"Done. Transformation report written to %s",
|
|
125
164
|
self.folder_structure.migration_reports_file.name,
|
|
126
165
|
)
|
|
127
|
-
|
|
128
|
-
def read_records(self, reader, source_file: FileDefinition):
|
|
129
|
-
with open(self.folder_structure.failed_bibs_file, "wb") as failed_bibs_file:
|
|
130
|
-
for idx, record in enumerate(reader):
|
|
131
|
-
self.mapper.migration_report.add_general_statistics(
|
|
132
|
-
"Records in file before parsing"
|
|
133
|
-
)
|
|
134
|
-
try:
|
|
135
|
-
if record is None:
|
|
136
|
-
self.mapper.migration_report.add_general_statistics(
|
|
137
|
-
"Records with encoding errors - parsing failed",
|
|
138
|
-
)
|
|
139
|
-
failed_bibs_file.write(reader.current_chunk)
|
|
140
|
-
raise TransformationRecordFailedError(
|
|
141
|
-
f"Index in {source_file.file_name}:{idx}",
|
|
142
|
-
f"MARC parsing error: {reader.current_exception}",
|
|
143
|
-
"Failed records stored in results/failed_bib_records.mrc",
|
|
144
|
-
)
|
|
145
|
-
|
|
146
|
-
else:
|
|
147
|
-
self.set_leader(record)
|
|
148
|
-
self.mapper.migration_report.add_general_statistics(
|
|
149
|
-
"Records successfully parsed from MARC21",
|
|
150
|
-
)
|
|
151
|
-
self.processor.process_record(
|
|
152
|
-
idx, record, source_file.suppressed
|
|
153
|
-
)
|
|
154
|
-
except TransformationRecordFailedError as error:
|
|
155
|
-
error.log_it()
|
|
156
|
-
logging.info("Done reading %s records from file", idx + 1)
|
|
157
|
-
|
|
158
|
-
@staticmethod
|
|
159
|
-
def set_leader(marc_record: Record):
|
|
160
|
-
new_leader = marc_record.leader
|
|
161
|
-
marc_record.leader = f"{new_leader[:9]}a{new_leader[10:]}"
|
|
166
|
+
self.clean_out_empty_logs()
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import csv
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
4
|
+
import sys
|
|
5
|
+
import time
|
|
6
|
+
import traceback
|
|
7
|
+
from typing import Optional, Annotated
|
|
8
|
+
from pydantic import Field
|
|
9
|
+
|
|
10
|
+
import i18n
|
|
11
|
+
from folio_uuid.folio_namespaces import FOLIONamespaces
|
|
12
|
+
|
|
13
|
+
from folio_migration_tools.custom_exceptions import (
|
|
14
|
+
TransformationProcessError,
|
|
15
|
+
TransformationRecordFailedError,
|
|
16
|
+
)
|
|
17
|
+
from folio_migration_tools.library_configuration import (
|
|
18
|
+
FileDefinition,
|
|
19
|
+
LibraryConfiguration,
|
|
20
|
+
)
|
|
21
|
+
from folio_migration_tools.mapping_file_transformation.courses_mapper import (
|
|
22
|
+
CoursesMapper,
|
|
23
|
+
)
|
|
24
|
+
from folio_migration_tools.mapping_file_transformation.mapping_file_mapper_base import (
|
|
25
|
+
MappingFileMapperBase,
|
|
26
|
+
)
|
|
27
|
+
from folio_migration_tools.migration_tasks.migration_task_base import MigrationTaskBase
|
|
28
|
+
from folio_migration_tools.task_configuration import AbstractTaskConfiguration
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class CoursesMigrator(MigrationTaskBase):
|
|
32
|
+
class TaskConfiguration(AbstractTaskConfiguration):
|
|
33
|
+
name: Annotated[
|
|
34
|
+
str,
|
|
35
|
+
Field(
|
|
36
|
+
title="Task name",
|
|
37
|
+
description="The name of the task",
|
|
38
|
+
),
|
|
39
|
+
]
|
|
40
|
+
composite_course_map_path: Annotated[
|
|
41
|
+
str,
|
|
42
|
+
Field(
|
|
43
|
+
title="Composite course map path",
|
|
44
|
+
description="Path to the composite course map file",
|
|
45
|
+
),
|
|
46
|
+
]
|
|
47
|
+
migration_task_type: Annotated[
|
|
48
|
+
str,
|
|
49
|
+
Field(
|
|
50
|
+
title="Migration task type",
|
|
51
|
+
description="Type of migration task",
|
|
52
|
+
),
|
|
53
|
+
]
|
|
54
|
+
courses_file: Annotated[
|
|
55
|
+
FileDefinition,
|
|
56
|
+
Field(
|
|
57
|
+
title="Courses file",
|
|
58
|
+
description="File containing course data",
|
|
59
|
+
),
|
|
60
|
+
]
|
|
61
|
+
terms_map_path: Annotated[
|
|
62
|
+
str,
|
|
63
|
+
Field(
|
|
64
|
+
title="Terms map path",
|
|
65
|
+
description="Path to the terms map file",
|
|
66
|
+
),
|
|
67
|
+
]
|
|
68
|
+
departments_map_path: Annotated[
|
|
69
|
+
str,
|
|
70
|
+
Field(
|
|
71
|
+
title="Departments map path",
|
|
72
|
+
description="Path to the departments map file",
|
|
73
|
+
),
|
|
74
|
+
]
|
|
75
|
+
look_up_instructor: Annotated[
|
|
76
|
+
Optional[bool],
|
|
77
|
+
Field(
|
|
78
|
+
title="Look up instructor",
|
|
79
|
+
description=(
|
|
80
|
+
"Flag to indicate whether to look up instructors. "
|
|
81
|
+
"By default is False."
|
|
82
|
+
),
|
|
83
|
+
),
|
|
84
|
+
] = False
|
|
85
|
+
|
|
86
|
+
@staticmethod
|
|
87
|
+
def get_object_type() -> FOLIONamespaces:
|
|
88
|
+
return FOLIONamespaces.course
|
|
89
|
+
|
|
90
|
+
def __init__(
|
|
91
|
+
self,
|
|
92
|
+
task_configuration: TaskConfiguration,
|
|
93
|
+
library_config: LibraryConfiguration,
|
|
94
|
+
folio_client
|
|
95
|
+
):
|
|
96
|
+
csv.register_dialect("tsv", delimiter="\t")
|
|
97
|
+
self.task_configuration = task_configuration
|
|
98
|
+
super().__init__(library_config, task_configuration, folio_client)
|
|
99
|
+
self.t0 = time.time()
|
|
100
|
+
self.courses_map = self.setup_records_map(
|
|
101
|
+
self.folder_structure.mapping_files_folder
|
|
102
|
+
/ self.task_configuration.composite_course_map_path
|
|
103
|
+
)
|
|
104
|
+
self.folio_keys = MappingFileMapperBase.get_mapped_folio_properties_from_map(
|
|
105
|
+
self.courses_map
|
|
106
|
+
)
|
|
107
|
+
terms_map = self.load_ref_data_mapping_file(
|
|
108
|
+
"terms",
|
|
109
|
+
self.folder_structure.mapping_files_folder / self.task_configuration.terms_map_path,
|
|
110
|
+
self.folio_keys,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
departments_map = self.load_ref_data_mapping_file(
|
|
114
|
+
"departments",
|
|
115
|
+
self.folder_structure.mapping_files_folder
|
|
116
|
+
/ self.task_configuration.departments_map_path,
|
|
117
|
+
self.folio_keys,
|
|
118
|
+
)
|
|
119
|
+
self.mapper: CoursesMapper = CoursesMapper(
|
|
120
|
+
self.folio_client,
|
|
121
|
+
self.courses_map,
|
|
122
|
+
terms_map,
|
|
123
|
+
departments_map,
|
|
124
|
+
self.library_configuration,
|
|
125
|
+
self.task_configuration,
|
|
126
|
+
)
|
|
127
|
+
logging.info("Init completed")
|
|
128
|
+
|
|
129
|
+
def do_work(self):
|
|
130
|
+
logging.info("Starting")
|
|
131
|
+
full_path = (
|
|
132
|
+
self.folder_structure.legacy_records_folder
|
|
133
|
+
/ self.task_configuration.courses_file.file_name
|
|
134
|
+
)
|
|
135
|
+
logging.info("Processing %s", full_path)
|
|
136
|
+
start = time.time()
|
|
137
|
+
with open(full_path, encoding="utf-8-sig") as records_file:
|
|
138
|
+
for idx, record in enumerate(self.mapper.get_objects(records_file, full_path)):
|
|
139
|
+
try:
|
|
140
|
+
if idx == 0:
|
|
141
|
+
logging.info("First legacy record:")
|
|
142
|
+
logging.info(json.dumps(record, indent=4))
|
|
143
|
+
self.mapper.verify_legacy_record(record, idx)
|
|
144
|
+
folio_rec, legacy_id = self.mapper.do_map(
|
|
145
|
+
record, f"row {idx}", FOLIONamespaces.course
|
|
146
|
+
)
|
|
147
|
+
self.mapper.perform_additional_mappings((folio_rec, legacy_id))
|
|
148
|
+
if idx == 0:
|
|
149
|
+
logging.info("First FOLIO record:")
|
|
150
|
+
logging.info(json.dumps(folio_rec, indent=4))
|
|
151
|
+
self.mapper.store_objects((folio_rec, legacy_id))
|
|
152
|
+
self.mapper.notes_mapper.map_notes(
|
|
153
|
+
record, legacy_id, folio_rec["course"]["id"], FOLIONamespaces.course
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
except TransformationProcessError as process_error:
|
|
157
|
+
self.mapper.handle_transformation_process_error(idx, process_error)
|
|
158
|
+
except TransformationRecordFailedError as data_error:
|
|
159
|
+
self.mapper.handle_transformation_record_failed_error(idx, data_error)
|
|
160
|
+
except AttributeError as attribute_error:
|
|
161
|
+
traceback.print_exc()
|
|
162
|
+
logging.fatal(attribute_error)
|
|
163
|
+
logging.info("Quitting...")
|
|
164
|
+
sys.exit(1)
|
|
165
|
+
except Exception as excepion:
|
|
166
|
+
self.mapper.handle_generic_exception(idx, excepion)
|
|
167
|
+
self.mapper.migration_report.add(
|
|
168
|
+
"GeneralStatistics",
|
|
169
|
+
i18n.t("Number of Legacy items in %{container}", container=full_path),
|
|
170
|
+
)
|
|
171
|
+
self.mapper.migration_report.add_general_statistics(
|
|
172
|
+
i18n.t("Number of Legacy items in total")
|
|
173
|
+
)
|
|
174
|
+
self.print_progress(idx, start)
|
|
175
|
+
|
|
176
|
+
def wrap_up(self):
|
|
177
|
+
self.extradata_writer.flush()
|
|
178
|
+
with open(self.folder_structure.migration_reports_file, "w+") as report_file:
|
|
179
|
+
self.mapper.migration_report.write_migration_report(
|
|
180
|
+
i18n.t("Courses migration report"), report_file, self.mapper.start_datetime
|
|
181
|
+
)
|
|
182
|
+
self.clean_out_empty_logs()
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def timings(t0, t0func, num_objects):
|
|
186
|
+
avg = (time.time() - t0) / num_objects
|
|
187
|
+
elapsed = time.time() - t0
|
|
188
|
+
elapsed_func = time.time() - t0func
|
|
189
|
+
return (
|
|
190
|
+
f"Total objects: {num_objects}\tTotal elapsed: {elapsed:.2f}\t"
|
|
191
|
+
f"Average per object: {avg:.2f}s\tElapsed this time: {elapsed_func:.2f}"
|
|
192
|
+
)
|