folio-migration-tools 1.10.0__py3-none-any.whl → 1.10.0b1__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.
Files changed (34) hide show
  1. folio_migration_tools/__main__.py +0 -9
  2. folio_migration_tools/circulation_helper.py +5 -6
  3. folio_migration_tools/folder_structure.py +6 -16
  4. folio_migration_tools/helper.py +6 -7
  5. folio_migration_tools/holdings_helper.py +3 -4
  6. folio_migration_tools/library_configuration.py +0 -12
  7. folio_migration_tools/mapper_base.py +6 -7
  8. folio_migration_tools/mapping_file_transformation/user_mapper.py +0 -4
  9. folio_migration_tools/marc_rules_transformation/conditions.py +29 -0
  10. folio_migration_tools/marc_rules_transformation/marc_file_processor.py +9 -19
  11. folio_migration_tools/marc_rules_transformation/rules_mapper_authorities.py +242 -0
  12. folio_migration_tools/marc_rules_transformation/rules_mapper_base.py +15 -10
  13. folio_migration_tools/marc_rules_transformation/rules_mapper_bibs.py +2 -3
  14. folio_migration_tools/marc_rules_transformation/rules_mapper_holdings.py +5 -6
  15. folio_migration_tools/migration_report.py +6 -17
  16. folio_migration_tools/migration_tasks/authority_transformer.py +118 -0
  17. folio_migration_tools/migration_tasks/batch_poster.py +298 -219
  18. folio_migration_tools/migration_tasks/bibs_transformer.py +2 -2
  19. folio_migration_tools/migration_tasks/holdings_csv_transformer.py +9 -9
  20. folio_migration_tools/migration_tasks/holdings_marc_transformer.py +3 -3
  21. folio_migration_tools/migration_tasks/items_transformer.py +4 -6
  22. folio_migration_tools/migration_tasks/loans_migrator.py +18 -19
  23. folio_migration_tools/migration_tasks/manual_fee_fines_transformer.py +3 -3
  24. folio_migration_tools/migration_tasks/migration_task_base.py +6 -15
  25. folio_migration_tools/migration_tasks/orders_transformer.py +3 -4
  26. folio_migration_tools/migration_tasks/requests_migrator.py +9 -10
  27. folio_migration_tools/migration_tasks/reserves_migrator.py +4 -5
  28. folio_migration_tools/migration_tasks/user_transformer.py +5 -15
  29. folio_migration_tools/translations/en.json +7 -0
  30. {folio_migration_tools-1.10.0.dist-info → folio_migration_tools-1.10.0b1.dist-info}/METADATA +2 -3
  31. {folio_migration_tools-1.10.0.dist-info → folio_migration_tools-1.10.0b1.dist-info}/RECORD +33 -32
  32. folio_migration_tools/i18n_cache.py +0 -79
  33. {folio_migration_tools-1.10.0.dist-info → folio_migration_tools-1.10.0b1.dist-info}/WHEEL +0 -0
  34. {folio_migration_tools-1.10.0.dist-info → folio_migration_tools-1.10.0b1.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", i18n_t("Records without $6"))
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", i18n_t("Records with unexpected length in $6")
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
- i18n_t("Source digits")
323
+ i18n.t("Source digits")
325
324
  + f": {marc_field['6']} "
326
- + i18n_t("Target field")
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
- i18n_t("Mapping not set up for target field")
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", i18n_t("Total number of Tags processed"))
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
- i18n_t("Source of heading or term") + f": {subfield_2.split(' ')[0]}",
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", i18n_t("$0 base uri or source code") + f": {code}"
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
- i18n_t("Records failed due to an error. See data issues log for details")
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", i18n_t("Original value") + f": {leader_05}")
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", i18n_t("Subfield b not in 336"))
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", i18n_t("Total number of Tags processed"))
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
- i18n_t("blurbs.HoldingsTypeMapping.title") + " is 'unknown'. "
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
- i18n_t("An Unmapped")
601
+ i18n.t("An Unmapped")
603
602
  + f" {ldr06} -> {holdings_type} -> "
604
- + i18n_t("Unmapped"),
603
+ + i18n.t("Unmapped"),
605
604
  )
606
605
  Helper.log_data_issue(
607
606
  legacy_ids,
608
607
  (
609
- i18n_t("blurbs.HoldingsTypeMapping.title", locale="en")
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
- "## " + i18n_t("Timings"),
69
+ "## " + i18n.t("Timings"),
81
70
  "",
82
- i18n_t("Measure") + " | " + i18n_t("Value"),
71
+ i18n.t("Measure") + " | " + i18n.t("Value"),
83
72
  "--- | ---:",
84
- i18n_t("Time Started:") + " | " + datetime.isoformat(time_started),
85
- i18n_t("Time Finished:") + " | " + datetime.isoformat(time_finished),
86
- i18n_t("Elapsed time:") + " | " + str(time_finished - time_started),
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
- i18n_t("Measure") + " | " + i18n_t("Count"),
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()