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.
Files changed (73) hide show
  1. folio_migration_tools/__init__.py +11 -0
  2. folio_migration_tools/__main__.py +169 -85
  3. folio_migration_tools/circulation_helper.py +96 -59
  4. folio_migration_tools/config_file_load.py +66 -0
  5. folio_migration_tools/custom_dict.py +6 -4
  6. folio_migration_tools/custom_exceptions.py +21 -19
  7. folio_migration_tools/extradata_writer.py +46 -0
  8. folio_migration_tools/folder_structure.py +63 -66
  9. folio_migration_tools/helper.py +29 -21
  10. folio_migration_tools/holdings_helper.py +57 -34
  11. folio_migration_tools/i18n_config.py +9 -0
  12. folio_migration_tools/library_configuration.py +173 -13
  13. folio_migration_tools/mapper_base.py +317 -106
  14. folio_migration_tools/mapping_file_transformation/courses_mapper.py +203 -0
  15. folio_migration_tools/mapping_file_transformation/holdings_mapper.py +83 -69
  16. folio_migration_tools/mapping_file_transformation/item_mapper.py +98 -94
  17. folio_migration_tools/mapping_file_transformation/manual_fee_fines_mapper.py +352 -0
  18. folio_migration_tools/mapping_file_transformation/mapping_file_mapper_base.py +702 -223
  19. folio_migration_tools/mapping_file_transformation/notes_mapper.py +90 -0
  20. folio_migration_tools/mapping_file_transformation/order_mapper.py +492 -0
  21. folio_migration_tools/mapping_file_transformation/organization_mapper.py +389 -0
  22. folio_migration_tools/mapping_file_transformation/ref_data_mapping.py +38 -27
  23. folio_migration_tools/mapping_file_transformation/user_mapper.py +149 -361
  24. folio_migration_tools/marc_rules_transformation/conditions.py +650 -246
  25. folio_migration_tools/marc_rules_transformation/holdings_statementsparser.py +292 -130
  26. folio_migration_tools/marc_rules_transformation/hrid_handler.py +244 -0
  27. folio_migration_tools/marc_rules_transformation/loc_language_codes.xml +20846 -0
  28. folio_migration_tools/marc_rules_transformation/marc_file_processor.py +300 -0
  29. folio_migration_tools/marc_rules_transformation/marc_reader_wrapper.py +136 -0
  30. folio_migration_tools/marc_rules_transformation/rules_mapper_authorities.py +241 -0
  31. folio_migration_tools/marc_rules_transformation/rules_mapper_base.py +681 -201
  32. folio_migration_tools/marc_rules_transformation/rules_mapper_bibs.py +395 -429
  33. folio_migration_tools/marc_rules_transformation/rules_mapper_holdings.py +531 -100
  34. folio_migration_tools/migration_report.py +85 -38
  35. folio_migration_tools/migration_tasks/__init__.py +1 -3
  36. folio_migration_tools/migration_tasks/authority_transformer.py +119 -0
  37. folio_migration_tools/migration_tasks/batch_poster.py +911 -198
  38. folio_migration_tools/migration_tasks/bibs_transformer.py +121 -116
  39. folio_migration_tools/migration_tasks/courses_migrator.py +192 -0
  40. folio_migration_tools/migration_tasks/holdings_csv_transformer.py +252 -247
  41. folio_migration_tools/migration_tasks/holdings_marc_transformer.py +321 -115
  42. folio_migration_tools/migration_tasks/items_transformer.py +264 -84
  43. folio_migration_tools/migration_tasks/loans_migrator.py +506 -195
  44. folio_migration_tools/migration_tasks/manual_fee_fines_transformer.py +187 -0
  45. folio_migration_tools/migration_tasks/migration_task_base.py +364 -74
  46. folio_migration_tools/migration_tasks/orders_transformer.py +373 -0
  47. folio_migration_tools/migration_tasks/organization_transformer.py +451 -0
  48. folio_migration_tools/migration_tasks/requests_migrator.py +130 -62
  49. folio_migration_tools/migration_tasks/reserves_migrator.py +253 -0
  50. folio_migration_tools/migration_tasks/user_transformer.py +180 -139
  51. folio_migration_tools/task_configuration.py +46 -0
  52. folio_migration_tools/test_infrastructure/__init__.py +0 -0
  53. folio_migration_tools/test_infrastructure/mocked_classes.py +406 -0
  54. folio_migration_tools/transaction_migration/legacy_loan.py +148 -34
  55. folio_migration_tools/transaction_migration/legacy_request.py +65 -25
  56. folio_migration_tools/transaction_migration/legacy_reserve.py +47 -0
  57. folio_migration_tools/transaction_migration/transaction_result.py +12 -1
  58. folio_migration_tools/translations/en.json +476 -0
  59. folio_migration_tools-1.9.10.dist-info/METADATA +169 -0
  60. folio_migration_tools-1.9.10.dist-info/RECORD +67 -0
  61. {folio_migration_tools-1.2.1.dist-info → folio_migration_tools-1.9.10.dist-info}/WHEEL +1 -2
  62. folio_migration_tools-1.9.10.dist-info/entry_points.txt +3 -0
  63. folio_migration_tools/generate_schemas.py +0 -46
  64. folio_migration_tools/mapping_file_transformation/mapping_file_mapping_base_impl.py +0 -44
  65. folio_migration_tools/mapping_file_transformation/user_mapper_base.py +0 -212
  66. folio_migration_tools/marc_rules_transformation/bibs_processor.py +0 -163
  67. folio_migration_tools/marc_rules_transformation/holdings_processor.py +0 -284
  68. folio_migration_tools/report_blurbs.py +0 -219
  69. folio_migration_tools/transaction_migration/legacy_fee_fine.py +0 -36
  70. folio_migration_tools-1.2.1.dist-info/METADATA +0 -134
  71. folio_migration_tools-1.2.1.dist-info/RECORD +0 -50
  72. folio_migration_tools-1.2.1.dist-info/top_level.txt +0 -1
  73. {folio_migration_tools-1.2.1.dist-info → folio_migration_tools-1.9.10.dist-info/licenses}/LICENSE +0 -0
@@ -1,5 +1,7 @@
1
1
  import logging
2
- from folio_migration_tools.report_blurbs import Blurbs
2
+ import i18n
3
+ from datetime import datetime
4
+ from datetime import timezone
3
5
 
4
6
 
5
7
  class MigrationReport:
@@ -9,58 +11,103 @@ class MigrationReport:
9
11
  self.report = {}
10
12
  self.stats = {}
11
13
 
12
- def add(self, blurb_tuple: tuple, measure_to_add, number=1):
13
- """Add section header and values to migration report."""
14
+ def add(self, blurb_id, measure_to_add, number=1):
15
+ """Add section header and values to migration report.
16
+
17
+ Args:
18
+ blurb_id (string): ID of Blurb in translations file
19
+ measure_to_add (_type_): _description_
20
+ number (int, optional): _description_. Defaults to 1.
21
+ """
14
22
  try:
15
- self.report[blurb_tuple[0]][measure_to_add] += number
23
+ self.report[blurb_id][measure_to_add] += number
16
24
  except KeyError:
17
- if blurb_tuple[0] not in self.report:
18
- self.report[blurb_tuple[0]] = {"blurb_tuple": blurb_tuple}
19
- if measure_to_add not in self.report[blurb_tuple[0]]:
20
- self.report[blurb_tuple[0]][measure_to_add] = number
25
+ if blurb_id not in self.report:
26
+ self.report[blurb_id] = {"blurb_id": blurb_id}
27
+ if measure_to_add not in self.report[blurb_id]:
28
+ self.report[blurb_id][measure_to_add] = number
29
+
30
+ def set(self, blurb_id, measure_to_add: str, number: int):
31
+ """Set a section value to a specific number
21
32
 
22
- def set(self, blurb, measure_to_add: str, number: int):
23
- """set a section value to a specific number"""
24
- if blurb[0] not in self.report:
25
- self.report[blurb[0]] = {}
26
- self.report[blurb[0]][measure_to_add] = number
33
+ Args:
34
+ blurb (_type_): _description_
35
+ measure_to_add (str): _description_
36
+ number (int): _description_
37
+ """
38
+ if blurb_id not in self.report:
39
+ self.report[blurb_id] = {}
40
+ self.report[blurb_id][measure_to_add] = number
27
41
 
28
42
  def add_general_statistics(self, measure_to_add: str):
29
- """Shortcut for adding to the first breakdown"""
30
- self.add(Blurbs.GeneralStatistics, measure_to_add)
43
+ """Shortcut for adding to the first breakdown
44
+
45
+ Args:
46
+ measure_to_add (str): _description_
47
+ """
48
+ self.add("GeneralStatistics", measure_to_add)
31
49
 
32
- def write_migration_report(self, report_file):
33
- """Writes the migration report, including section headers, section blurbs, and values."""
34
- report_file.write(f"{Blurbs.Introduction[1]}\n")
50
+ def write_migration_report(
51
+ self,
52
+ report_title,
53
+ report_file,
54
+ time_started: datetime,
55
+ ):
56
+ """Writes the migration report, including section headers, section blurbs, and values.
35
57
 
58
+ Args:
59
+ report_title (_type_):the header of the report.
60
+ report_file (_type_):path to file
61
+ time_started (datetime): The datetime stamp (in utc), of when the process started
62
+ """
63
+ time_finished = datetime.now(timezone.utc)
64
+ report_file.write(
65
+ "\n".join(
66
+ [
67
+ "# " + report_title,
68
+ i18n.t("blurbs.Introduction.description"),
69
+ "## " + i18n.t("Timings"),
70
+ "",
71
+ i18n.t("Measure") + " | " + i18n.t("Value"),
72
+ "--- | ---:",
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),
76
+ ]
77
+ )
78
+ )
79
+ logging.info(f"Elapsed time: {time_finished-time_started}")
36
80
  for a in self.report:
37
- blurb = self.report[a].get("blurb_tuple") or ("", "")
38
- report_file.write(" \n")
39
- report_file.write(f"## {blurb[0]} \n")
40
- report_file.write(f"{blurb[1]} \n")
81
+ blurb_id = self.report[a].get("blurb_id") or ""
41
82
  report_file.write(
42
- f"<details><summary>Click to expand all {len(self.report[a])} things</summary> \n"
83
+ "\n".join(
84
+ [
85
+ "",
86
+ "## " + i18n.t(f"blurbs.{blurb_id}.title"),
87
+ i18n.t(f"blurbs.{blurb_id}.description"),
88
+ "<details><summary>"
89
+ + i18n.t("Click to expand all %{count} things", count=len(self.report[a]))
90
+ + "</summary>",
91
+ "",
92
+ i18n.t("Measure") + " | " + i18n.t("Count"),
93
+ "--- | ---:",
94
+ ]
95
+ + [
96
+ f"{k or 'EMPTY'} | {self.report[a][k]:,}"
97
+ for k in sorted(self.report[a], key=as_str)
98
+ if k != "blurb_id"
99
+ ]
100
+ + ["</details>", ""]
101
+ )
43
102
  )
44
- report_file.write(" \n")
45
- report_file.write("Measure | Count \n")
46
- report_file.write("--- | ---: \n")
47
- b = self.report[a]
48
- sortedlist = [
49
- (k, b[k]) for k in sorted(b, key=as_str) if k != "blurb_tuple"
50
- ]
51
- for b in sortedlist:
52
- report_file.write(f"{b[0] or 'EMPTY'} | {b[1]:,} \n")
53
- report_file.write("</details> \n")
54
103
 
55
104
  def log_me(self):
56
105
  for a in self.report:
57
- blurb = self.report[a].get("blurb_tuple") or ("", "")
58
- logging.info(f"{blurb[0]} ")
106
+ blurb_id = self.report[a].get("blurb_id") or ""
107
+ logging.info(f"{blurb_id} ")
59
108
  logging.info("_______________")
60
109
  b = self.report[a]
61
- sortedlist = [
62
- (k, b[k]) for k in sorted(b, key=as_str) if k != "blurb_tuple"
63
- ]
110
+ sortedlist = [(k, b[k]) for k in sorted(b, key=as_str) if k != "blurb_id"]
64
111
  for b in sortedlist:
65
112
  logging.info(f"{b[0] or 'EMPTY'} \t\t{b[1]:,} ")
66
113
 
@@ -2,6 +2,4 @@ from os.path import dirname, basename, isfile, join
2
2
  import glob
3
3
 
4
4
  modules = glob.glob(join(dirname(__file__), "*.py"))
5
- __all__ = [
6
- basename(f)[:-3] for f in modules if isfile(f) and not f.endswith("__init__.py")
7
- ]
5
+ __all__ = [basename(f)[:-3] for f in modules if isfile(f) and not f.endswith("__init__.py")]
@@ -0,0 +1,119 @@
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 "
70
+ "Source Record Storage."
71
+ ),
72
+ ),
73
+ ] = True
74
+
75
+ @staticmethod
76
+ def get_object_type() -> FOLIONamespaces:
77
+ return FOLIONamespaces.authorities
78
+
79
+ def __init__(
80
+ self,
81
+ task_config: TaskConfiguration,
82
+ library_config: LibraryConfiguration,
83
+ use_logging: bool = True,
84
+ ):
85
+ super().__init__(library_config, task_config, use_logging)
86
+ self.processor: MarcFileProcessor
87
+ self.check_source_files(
88
+ self.folder_structure.legacy_records_folder, self.task_configuration.files
89
+ )
90
+ self.mapper: AuthorityMapper = AuthorityMapper(
91
+ self.folio_client, library_config, task_config
92
+ )
93
+ self.auth_ids: set = set()
94
+ logging.info("Init done")
95
+
96
+ def do_work(self):
97
+ self.do_work_marc_transformer()
98
+
99
+ def wrap_up(self):
100
+ logging.info("Done. Transformer Wrapping up...")
101
+ self.extradata_writer.flush()
102
+ self.processor.wrap_up()
103
+ with open(self.folder_structure.migration_reports_file, "w+") as report_file:
104
+ self.mapper.migration_report.write_migration_report(
105
+ i18n.t("Authority records transformation report"),
106
+ report_file,
107
+ self.start_datetime,
108
+ )
109
+ Helper.print_mapping_report(
110
+ report_file,
111
+ self.mapper.parsed_records,
112
+ self.mapper.mapped_folio_fields,
113
+ self.mapper.mapped_legacy_fields,
114
+ )
115
+ logging.info(
116
+ "Done. Transformation report written to %s",
117
+ self.folder_structure.migration_reports_file.name,
118
+ )
119
+ self.clean_out_empty_logs()