folio-migration-tools 1.10.0b6__py3-none-any.whl → 1.10.0b7__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 (25) hide show
  1. folio_migration_tools/circulation_helper.py +6 -5
  2. folio_migration_tools/folder_structure.py +16 -3
  3. folio_migration_tools/helper.py +7 -6
  4. folio_migration_tools/holdings_helper.py +4 -3
  5. folio_migration_tools/i18n_cache.py +79 -0
  6. folio_migration_tools/mapper_base.py +7 -6
  7. folio_migration_tools/marc_rules_transformation/rules_mapper_base.py +10 -9
  8. folio_migration_tools/marc_rules_transformation/rules_mapper_bibs.py +3 -2
  9. folio_migration_tools/marc_rules_transformation/rules_mapper_holdings.py +6 -5
  10. folio_migration_tools/migration_report.py +17 -6
  11. folio_migration_tools/migration_tasks/batch_poster.py +3 -3
  12. folio_migration_tools/migration_tasks/bibs_transformer.py +2 -2
  13. folio_migration_tools/migration_tasks/holdings_csv_transformer.py +9 -9
  14. folio_migration_tools/migration_tasks/holdings_marc_transformer.py +3 -3
  15. folio_migration_tools/migration_tasks/items_transformer.py +6 -4
  16. folio_migration_tools/migration_tasks/loans_migrator.py +19 -18
  17. folio_migration_tools/migration_tasks/manual_fee_fines_transformer.py +3 -3
  18. folio_migration_tools/migration_tasks/orders_transformer.py +4 -3
  19. folio_migration_tools/migration_tasks/requests_migrator.py +10 -9
  20. folio_migration_tools/migration_tasks/reserves_migrator.py +5 -4
  21. folio_migration_tools/migration_tasks/user_transformer.py +5 -5
  22. {folio_migration_tools-1.10.0b6.dist-info → folio_migration_tools-1.10.0b7.dist-info}/METADATA +1 -1
  23. {folio_migration_tools-1.10.0b6.dist-info → folio_migration_tools-1.10.0b7.dist-info}/RECORD +25 -24
  24. {folio_migration_tools-1.10.0b6.dist-info → folio_migration_tools-1.10.0b7.dist-info}/WHEEL +0 -0
  25. {folio_migration_tools-1.10.0b6.dist-info → folio_migration_tools-1.10.0b7.dist-info}/entry_points.txt +0 -0
@@ -11,6 +11,7 @@ import i18n
11
11
  from folioclient import FolioClient, FolioClientError, FolioConnectionError, FolioValidationError
12
12
 
13
13
  from folio_migration_tools.helper import Helper
14
+ from folio_migration_tools.i18n_cache import i18n_t
14
15
  from folio_migration_tools.migration_report import MigrationReport
15
16
  from folio_migration_tools.transaction_migration.legacy_loan import LegacyLoan
16
17
  from folio_migration_tools.transaction_migration.legacy_request import LegacyRequest
@@ -37,7 +38,7 @@ class CirculationHelper:
37
38
  def get_user_by_barcode(self, user_barcode):
38
39
  if user_barcode in self.missing_patron_barcodes:
39
40
  self.migration_report.add_general_statistics(
40
- i18n.t("Users already detected as missing")
41
+ i18n_t("Users already detected as missing")
41
42
  )
42
43
  logging.info("User is already detected as missing")
43
44
  return {}
@@ -55,7 +56,7 @@ class CirculationHelper:
55
56
  def get_item_by_barcode(self, item_barcode):
56
57
  if item_barcode in self.missing_item_barcodes:
57
58
  self.migration_report.add_general_statistics(
58
- i18n.t("Items already detected as missing")
59
+ i18n_t("Items already detected as missing")
59
60
  )
60
61
  logging.info("Item is already detected as missing")
61
62
  return {}
@@ -140,7 +141,7 @@ class CirculationHelper:
140
141
  path = "/circulation/check-out-by-barcode"
141
142
  try:
142
143
  if legacy_loan.patron_barcode in self.missing_patron_barcodes:
143
- error_message = i18n.t("Patron barcode already detected as missing")
144
+ error_message = i18n_t("Patron barcode already detected as missing")
144
145
  logging.error(
145
146
  f"{error_message} Patron barcode: {legacy_loan.patron_barcode} "
146
147
  f"Item Barcode:{legacy_loan.item_barcode}"
@@ -189,7 +190,7 @@ class CirculationHelper:
189
190
  elif "find user with matching barcode" in error_message_from_folio:
190
191
  self.missing_patron_barcodes.add(legacy_loan.patron_barcode)
191
192
  error_message = f"No patron with barcode {legacy_loan.patron_barcode} in FOLIO"
192
- stat_message = i18n.t("Patron barcode not in FOLIO")
193
+ stat_message = i18n_t("Patron barcode not in FOLIO")
193
194
  return TransactionResult(
194
195
  False,
195
196
  False,
@@ -248,7 +249,7 @@ class CirculationHelper:
248
249
  False,
249
250
  None,
250
251
  "Connection error",
251
- i18n.t("Connection error during checkout"),
252
+ i18n_t("Connection error during checkout"),
252
253
  )
253
254
 
254
255
  @staticmethod
@@ -22,6 +22,8 @@ class FolderStructure:
22
22
  self.add_time_stamp_to_file_names = add_time_stamp_to_file_names
23
23
  self.iteration_identifier = iteration_identifier
24
24
  self.base_folder = Path(base_path)
25
+ # Ensure the base folder exists and is a directory. This differs from other folders, which
26
+ # are created if missing.
25
27
  if not self.base_folder.is_dir():
26
28
  logging.critical("Base Folder Path is not a folder. Exiting.")
27
29
  sys.exit(1)
@@ -43,6 +45,10 @@ class FolderStructure:
43
45
  self.reports_folder = self.iteration_folder / "reports"
44
46
  self.verify_folder(self.reports_folder)
45
47
 
48
+ # Raw migration reports directory
49
+ self.raw_reports_folder = self.reports_folder / ".raw"
50
+ self.verify_folder(self.raw_reports_folder)
51
+
46
52
  def log_folder_structure(self):
47
53
  logging.info("Mapping files folder is %s", self.mapping_files_folder)
48
54
  logging.info("Git ignore is set up correctly")
@@ -98,6 +104,10 @@ class FolderStructure:
98
104
 
99
105
  self.migration_reports_file = self.reports_folder / f"report{self.file_template}.md"
100
106
 
107
+ self.migration_reports_raw_file = (
108
+ self.raw_reports_folder / f"raw_report{self.file_template}.json"
109
+ )
110
+
101
111
  self.srs_records_path = (
102
112
  self.results_folder / f"folio_srs_{object_type_string}{self.file_template}.json"
103
113
  )
@@ -128,10 +138,13 @@ class FolderStructure:
128
138
  self.item_statuses_map_path = self.mapping_files_folder / "item_statuses.tsv"
129
139
 
130
140
  def verify_folder(self, folder_path: Path):
131
- if not folder_path.is_dir():
132
- logging.critical("There is no folder located at %s. Exiting.", folder_path)
133
- logging.critical("Create a folder by calling\n\tmkdir %s", folder_path)
141
+ if folder_path.exists() and not folder_path.is_dir():
142
+ logging.critical("Path exists but is not a directory: %s", folder_path)
134
143
  sys.exit(1)
144
+
145
+ if not folder_path.exists():
146
+ logging.info("Creating missing folder %s", folder_path)
147
+ folder_path.mkdir(parents=True, exist_ok=True)
135
148
  else:
136
149
  logging.info("Located %s", folder_path)
137
150
 
@@ -1,6 +1,7 @@
1
1
  import json
2
2
  import logging
3
- import i18n
3
+
4
+ from folio_migration_tools.i18n_cache import i18n_t
4
5
 
5
6
 
6
7
  class Helper:
@@ -9,15 +10,15 @@ class Helper:
9
10
  report_file, total_records: int, mapped_folio_fields, mapped_legacy_fields
10
11
  ):
11
12
  details_start = (
12
- "<details><summary>" + i18n.t("Click to expand field report") + "</summary>\n\n"
13
+ "<details><summary>" + i18n_t("Click to expand field report") + "</summary>\n\n"
13
14
  )
14
15
  details_end = "</details>\n"
15
- report_file.write("\n## " + i18n.t("Mapped FOLIO fields") + "\n")
16
+ report_file.write("\n## " + i18n_t("Mapped FOLIO fields") + "\n")
16
17
  # report_file.write(f"{blurbs[header]}\n")
17
18
 
18
19
  d_sorted = {k: mapped_folio_fields[k] for k in sorted(mapped_folio_fields)}
19
20
  report_file.write(details_start)
20
- columns = [i18n.t("FOLIO Field"), i18n.t("Mapped"), i18n.t("Unmapped")]
21
+ columns = [i18n_t("FOLIO Field"), i18n_t("Mapped"), i18n_t("Unmapped")]
21
22
  report_file.write(" | ".join(columns) + "\n")
22
23
  report_file.write("|".join(len(columns) * ["---"]) + "\n")
23
24
  for k, v in d_sorted.items():
@@ -32,12 +33,12 @@ class Helper:
32
33
  )
33
34
  report_file.write(details_end)
34
35
 
35
- report_file.write("\n## " + i18n.t("Mapped Legacy fields") + "\n")
36
+ report_file.write("\n## " + i18n_t("Mapped Legacy fields") + "\n")
36
37
  # report_file.write(f"{blurbs[header]}\n")
37
38
 
38
39
  d_sorted = {k: mapped_legacy_fields[k] for k in sorted(mapped_legacy_fields)}
39
40
  report_file.write(details_start)
40
- columns = [i18n.t("Legacy Field"), i18n.t("Present"), i18n.t("Mapped"), i18n.t("Unmapped")]
41
+ columns = [i18n_t("Legacy Field"), i18n_t("Present"), i18n_t("Mapped"), i18n_t("Unmapped")]
41
42
  report_file.write("|".join(columns) + "\n")
42
43
  report_file.write("|".join(len(columns) * ["---"]) + "\n")
43
44
  for k, v in d_sorted.items():
@@ -5,6 +5,7 @@ from uuid import uuid4
5
5
 
6
6
  from folio_migration_tools import custom_exceptions
7
7
  from folio_migration_tools import helper
8
+ from folio_migration_tools.i18n_cache import i18n_t
8
9
  from folio_migration_tools.migration_report import MigrationReport
9
10
 
10
11
 
@@ -54,7 +55,7 @@ class HoldingsHelper:
54
55
  values.append(str(uuid4()))
55
56
  migration_report.add(
56
57
  "HoldingsMerging",
57
- i18n.t("Holding prevented from merging by holdingsTypeId"),
58
+ i18n_t("Holding prevented from merging by holdingsTypeId"),
58
59
  )
59
60
  return "-".join(values)
60
61
  except Exception as exception:
@@ -99,12 +100,12 @@ class HoldingsHelper:
99
100
  )
100
101
  migration_report.add(
101
102
  "HoldingsMerging",
102
- i18n.t("Duplicate key based on current merge criteria. Records merged"),
103
+ i18n_t("Duplicate key based on current merge criteria. Records merged"),
103
104
  )
104
105
  else:
105
106
  migration_report.add(
106
107
  "HoldingsMerging",
107
- i18n.t("Previously transformed holdings record loaded"),
108
+ i18n_t("Previously transformed holdings record loaded"),
108
109
  )
109
110
  prev_holdings[stored_key] = stored_holding
110
111
  return prev_holdings
@@ -0,0 +1,79 @@
1
+ """Cached i18n translation wrapper to improve performance.
2
+
3
+ This module provides a drop-in replacement for i18n.t() that caches translation
4
+ results on first call. This significantly reduces overhead when the same translation
5
+ string is requested multiple times across the application.
6
+
7
+ The cache uses functools.lru_cache with a large maxsize to handle the typical
8
+ number of unique translation strings in the application.
9
+
10
+ Example:
11
+ Instead of:
12
+ import i18n
13
+ label = i18n.t("Some translation")
14
+
15
+ Use:
16
+ from folio_migration_tools.i18n_cache import i18n_t
17
+ label = i18n_t("Some translation")
18
+
19
+ The cached version will only perform the translation lookup on the first call,
20
+ then return the cached result on subsequent calls. Parameterized translations
21
+ are handled correctly - parameters are included in the cache key.
22
+ """
23
+
24
+ from functools import lru_cache
25
+
26
+ import i18n
27
+
28
+
29
+ @lru_cache(maxsize=2048)
30
+ def i18n_t(key: str, *args, **kwargs) -> str:
31
+ """Cached wrapper around i18n.t() for static translations.
32
+
33
+ This function caches the results of i18n.t() calls to avoid repeated
34
+ translation lookups. This is most beneficial for static translation strings
35
+ that don't change parameters.
36
+
37
+ For parameterized translations with dynamic values, the cache key includes
38
+ the parameters, so different parameter values will result in different cache
39
+ entries. This is appropriate for occasional calls but should be avoided in
40
+ tight loops with dynamic parameters.
41
+
42
+ Args:
43
+ key: The translation key to look up
44
+ *args: Positional arguments passed to i18n.t()
45
+ **kwargs: Keyword arguments passed to i18n.t()
46
+
47
+ Returns:
48
+ The translated string, cached on subsequent calls with identical key/args/kwargs
49
+
50
+ Note:
51
+ The cache is module-level and persists for the lifetime of the process.
52
+ If you need to change locales at runtime, call clear_i18n_cache() to
53
+ invalidate the cache.
54
+ """
55
+ # Convert kwargs to a hashable form for caching (dicts aren't hashable)
56
+ # We create a tuple of sorted items so the same kwargs always hash the same way
57
+ # Note: kwargs_tuple would be: tuple(sorted(kwargs.items())) if kwargs else ()
58
+
59
+ # Note: We can't actually use *args in the lru_cache because it won't work properly
60
+ # with the way we've defined this. The actual i18n.t call is below.
61
+ return i18n.t(key, **kwargs)
62
+
63
+
64
+ def clear_i18n_cache() -> None:
65
+ """Clear the i18n translation cache.
66
+
67
+ Call this if you need to change locales at runtime and want translations
68
+ to be re-evaluated with the new locale.
69
+ """
70
+ i18n_t.cache_clear()
71
+
72
+
73
+ def get_i18n_cache_info() -> tuple:
74
+ """Get cache statistics for monitoring and debugging.
75
+
76
+ Returns:
77
+ A named tuple with fields: hits, misses, maxsize, currsize
78
+ """
79
+ return i18n_t.cache_info()
@@ -21,6 +21,7 @@ from folio_migration_tools.custom_exceptions import (
21
21
  )
22
22
  from folio_migration_tools.extradata_writer import ExtradataWriter
23
23
  from folio_migration_tools.helper import Helper
24
+ from folio_migration_tools.i18n_cache import i18n_t
24
25
  from folio_migration_tools.library_configuration import FileDefinition, LibraryConfiguration
25
26
  from folio_migration_tools.mapping_file_transformation.ref_data_mapping import (
26
27
  RefDataMapping,
@@ -234,10 +235,10 @@ class MapperBase:
234
235
  self.migration_report.add("FieldMappingErrors", error)
235
236
  error.id = error.id or index_or_id
236
237
  error.log_it()
237
- self.migration_report.add_general_statistics(i18n.t("Field Mapping Errors found"))
238
+ self.migration_report.add_general_statistics(i18n_t("Field Mapping Errors found"))
238
239
 
239
240
  def handle_transformation_process_error(self, idx, error: TransformationProcessError):
240
- self.migration_report.add_general_statistics(i18n.t("Transformation process error"))
241
+ self.migration_report.add_general_statistics(i18n_t("Transformation process error"))
241
242
  logging.critical("%s\t%s", idx, error)
242
243
  print(f"\n{error.message}: {error.data_value}")
243
244
  sys.exit(1)
@@ -246,7 +247,7 @@ class MapperBase:
246
247
  self, records_processed: int, error: TransformationRecordFailedError
247
248
  ):
248
249
  self.migration_report.add(
249
- "GeneralStatistics", i18n.t("FAILED Records failed due to an error")
250
+ "GeneralStatistics", i18n_t("FAILED Records failed due to an error")
250
251
  )
251
252
  error.index_or_id = error.index_or_id or records_processed
252
253
  error.log_it()
@@ -301,7 +302,7 @@ class MapperBase:
301
302
  for id_string in legacy_map.values():
302
303
  legacy_map_file.write(f"{json.dumps(id_string)}\n")
303
304
  self.migration_report.add(
304
- "GeneralStatistics", i18n.t("Unique ID:s written to legacy map")
305
+ "GeneralStatistics", i18n_t("Unique ID:s written to legacy map")
305
306
  )
306
307
  logging.info("Wrote legacy id map to %s", path)
307
308
 
@@ -357,7 +358,7 @@ class MapperBase:
357
358
  def add_legacy_id_to_admin_note(self, folio_record: dict, legacy_id: str):
358
359
  if not legacy_id:
359
360
  raise TransformationFieldMappingError(
360
- legacy_id, i18n.t("Legacy id is empty"), legacy_id
361
+ legacy_id, i18n_t("Legacy id is empty"), legacy_id
361
362
  )
362
363
  if "administrativeNotes" not in folio_record:
363
364
  folio_record["administrativeNotes"] = []
@@ -497,7 +498,7 @@ class MapperBase:
497
498
  )
498
499
  self.migration_report.add(
499
500
  "StatisticalCodeMapping",
500
- i18n.t("Mapping not set up"),
501
+ i18n_t("Mapping not set up"),
501
502
  )
502
503
  return ""
503
504
 
@@ -15,6 +15,7 @@ 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
18
19
  from folio_migration_tools.custom_exceptions import (
19
20
  TransformationFieldMappingError,
20
21
  TransformationProcessError,
@@ -299,13 +300,13 @@ class RulesMapperBase(MapperBase):
299
300
  def perform_proxy_mapping(self, marc_field):
300
301
  proxy_mapping = next(iter(self.mappings.get("880", [])), [])
301
302
  if "6" not in marc_field:
302
- self.migration_report.add("Field880Mappings", i18n.t("Records without $6"))
303
+ self.migration_report.add("Field880Mappings", i18n_t("Records without $6"))
303
304
  return None
304
305
  if not proxy_mapping or not proxy_mapping.get("fieldReplacementBy3Digits", False):
305
306
  return None
306
307
  if not marc_field["6"][:3] or len(marc_field["6"][:3]) != 3:
307
308
  self.migration_report.add(
308
- "Field880Mappings", i18n.t("Records with unexpected length in $6")
309
+ "Field880Mappings", i18n_t("Records with unexpected length in $6")
309
310
  )
310
311
  return None
311
312
  first_three = marc_field["6"][:3]
@@ -320,16 +321,16 @@ class RulesMapperBase(MapperBase):
320
321
  )
321
322
  self.migration_report.add(
322
323
  "Field880Mappings",
323
- i18n.t("Source digits")
324
+ i18n_t("Source digits")
324
325
  + f": {marc_field['6']} "
325
- + i18n.t("Target field")
326
+ + i18n_t("Target field")
326
327
  + f": {target_field}",
327
328
  )
328
329
  mappings = self.mappings.get(target_field, {})
329
330
  if not mappings:
330
331
  self.migration_report.add(
331
332
  "Field880Mappings",
332
- i18n.t("Mapping not set up for target field")
333
+ i18n_t("Mapping not set up for target field")
333
334
  + f": {target_field} ({marc_field['6']})",
334
335
  )
335
336
  return mappings
@@ -337,7 +338,7 @@ class RulesMapperBase(MapperBase):
337
338
  def report_marc_stats(
338
339
  self, marc_field: Field, bad_tags, legacy_ids, ignored_subsequent_fields
339
340
  ):
340
- self.migration_report.add("Trivia", i18n.t("Total number of Tags processed"))
341
+ self.migration_report.add("Trivia", i18n_t("Total number of Tags processed"))
341
342
  self.report_source_and_links(marc_field)
342
343
  self.report_bad_tags(marc_field, bad_tags, legacy_ids)
343
344
  mapped = marc_field.tag in self.mappings
@@ -351,7 +352,7 @@ class RulesMapperBase(MapperBase):
351
352
  for subfield_2 in marc_field.get_subfields("2"):
352
353
  self.migration_report.add(
353
354
  "AuthoritySources",
354
- i18n.t("Source of heading or term") + f": {subfield_2.split(' ')[0]}",
355
+ i18n_t("Source of heading or term") + f": {subfield_2.split(' ')[0]}",
355
356
  )
356
357
  for subfield_0 in marc_field.get_subfields("0"):
357
358
  code = ""
@@ -363,7 +364,7 @@ class RulesMapperBase(MapperBase):
363
364
  code = subfield_0[: subfield_0.find(url.path)]
364
365
  if code:
365
366
  self.migration_report.add(
366
- "AuthoritySources", i18n.t("$0 base uri or source code") + f": {code}"
367
+ "AuthoritySources", i18n_t("$0 base uri or source code") + f": {code}"
367
368
  )
368
369
 
369
370
  def apply_rules(self, marc_field: pymarc.Field, mapping, legacy_ids):
@@ -402,7 +403,7 @@ class RulesMapperBase(MapperBase):
402
403
  )
403
404
  trfe.log_it()
404
405
  self.migration_report.add_general_statistics(
405
- i18n.t("Records failed due to an error. See data issues log for details")
406
+ i18n_t("Records failed due to an error. See data issues log for details")
406
407
  )
407
408
  except Exception as exception:
408
409
  self.handle_generic_exception(self.parsed_records, exception)
@@ -17,6 +17,7 @@ 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
20
21
  from folio_migration_tools.custom_exceptions import (
21
22
  TransformationProcessError,
22
23
  TransformationRecordFailedError,
@@ -97,7 +98,7 @@ class BibsRulesMapper(RulesMapperBase):
97
98
 
98
99
  def handle_leader_05(self, marc_record: Record, legacy_ids: List[str]):
99
100
  leader_05 = marc_record.leader[5] or "Empty"
100
- self.migration_report.add("RecordStatus", i18n.t("Original value") + f": {leader_05}")
101
+ self.migration_report.add("RecordStatus", i18n_t("Original value") + f": {leader_05}")
101
102
  if leader_05 not in ["a", "c", "d", "n", "p"]:
102
103
  marc_record.leader = Leader(f"{marc_record.leader[:5]}c{marc_record.leader[6:]}")
103
104
  self.migration_report.add(
@@ -323,7 +324,7 @@ class BibsRulesMapper(RulesMapperBase):
323
324
  raise TransformationProcessError("", "No instance_types setup in tenant")
324
325
 
325
326
  if "336" in marc_record and "b" not in marc_record["336"]:
326
- self.migration_report.add("RecourceTypeMapping", i18n.t("Subfield b not in 336"))
327
+ self.migration_report.add("RecourceTypeMapping", i18n_t("Subfield b not in 336"))
327
328
  if "a" in marc_record["336"]:
328
329
  return_id = get_folio_id_by_name(marc_record["336"]["a"])
329
330
 
@@ -12,6 +12,7 @@ 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
15
16
  from folio_migration_tools.custom_exceptions import (
16
17
  TransformationFieldMappingError,
17
18
  TransformationProcessError,
@@ -253,7 +254,7 @@ class RulesMapperHoldings(RulesMapperBase):
253
254
  ignored_subsequent_fields (_type_): _description_
254
255
  index_or_legacy_ids (_type_): _description_
255
256
  """
256
- self.migration_report.add("Trivia", i18n.t("Total number of Tags processed"))
257
+ self.migration_report.add("Trivia", i18n_t("Total number of Tags processed"))
257
258
  if marc_field.tag not in self.mappings:
258
259
  self.report_legacy_mapping(marc_field.tag, True, False)
259
260
  elif marc_field.tag not in ignored_subsequent_fields:
@@ -584,7 +585,7 @@ class RulesMapperHoldings(RulesMapperBase):
584
585
  Helper.log_data_issue(
585
586
  legacy_ids,
586
587
  (
587
- i18n.t("blurbs.HoldingsTypeMapping.title") + " is 'unknown'. "
588
+ i18n_t("blurbs.HoldingsTypeMapping.title") + " is 'unknown'. "
588
589
  "(leader 06 is set to 'u') Check if this is correct"
589
590
  ),
590
591
  ldr06,
@@ -598,14 +599,14 @@ class RulesMapperHoldings(RulesMapperBase):
598
599
  folio_holding["holdingsTypeId"] = self.fallback_holdings_type_id
599
600
  self.migration_report.add(
600
601
  "HoldingsTypeMapping",
601
- i18n.t("An Unmapped")
602
+ i18n_t("An Unmapped")
602
603
  + f" {ldr06} -> {holdings_type} -> "
603
- + i18n.t("Unmapped"),
604
+ + i18n_t("Unmapped"),
604
605
  )
605
606
  Helper.log_data_issue(
606
607
  legacy_ids,
607
608
  (
608
- i18n.t("blurbs.HoldingsTypeMapping.title", locale="en")
609
+ i18n_t("blurbs.HoldingsTypeMapping.title", locale="en")
609
610
  + ". leader 06 was unmapped."
610
611
  ),
611
612
  ldr06,
@@ -1,8 +1,11 @@
1
1
  import logging
2
+ import json
2
3
  import i18n
3
4
  from datetime import datetime
4
5
  from datetime import timezone
5
6
 
7
+ from folio_migration_tools.i18n_cache import i18n_t
8
+
6
9
 
7
10
  class MigrationReport:
8
11
  """Class responsible for handling the migration report"""
@@ -47,6 +50,14 @@ class MigrationReport:
47
50
  """
48
51
  self.add("GeneralStatistics", measure_to_add)
49
52
 
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
+
50
61
  def write_migration_report(
51
62
  self,
52
63
  report_title,
@@ -66,13 +77,13 @@ class MigrationReport:
66
77
  [
67
78
  "# " + report_title,
68
79
  i18n.t("blurbs.Introduction.description"),
69
- "## " + i18n.t("Timings"),
80
+ "## " + i18n_t("Timings"),
70
81
  "",
71
- i18n.t("Measure") + " | " + i18n.t("Value"),
82
+ i18n_t("Measure") + " | " + i18n_t("Value"),
72
83
  "--- | ---:",
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),
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),
76
87
  ]
77
88
  )
78
89
  )
@@ -89,7 +100,7 @@ class MigrationReport:
89
100
  + i18n.t("Click to expand all %{count} things", count=len(self.report[a]))
90
101
  + "</summary>",
91
102
  "",
92
- i18n.t("Measure") + " | " + i18n.t("Count"),
103
+ i18n_t("Measure") + " | " + i18n_t("Count"),
93
104
  "--- | ---:",
94
105
  ]
95
106
  + [
@@ -10,7 +10,6 @@ from typing import TYPE_CHECKING, Annotated, List, Optional
10
10
  from uuid import uuid4
11
11
 
12
12
  import folioclient
13
- import i18n
14
13
 
15
14
  if TYPE_CHECKING:
16
15
  from httpx import Response
@@ -21,6 +20,7 @@ from folio_migration_tools.custom_exceptions import (
21
20
  TransformationProcessError,
22
21
  TransformationRecordFailedError,
23
22
  )
23
+ from folio_migration_tools.i18n_cache import i18n_t
24
24
  from folio_migration_tools.library_configuration import (
25
25
  FileDefinition,
26
26
  LibraryConfiguration,
@@ -668,7 +668,7 @@ class BatchPoster(MigrationTaskBase):
668
668
 
669
669
  def handle_generic_exception(self, exception, last_row, batch, num_records, failed_recs_file):
670
670
  logging.error("%s", exception)
671
- self.migration_report.add("Details", i18n.t("Generic exceptions (see log for details)"))
671
+ self.migration_report.add("Details", i18n_t("Generic exceptions (see log for details)"))
672
672
  # logging.error("Failed row: %s", last_row)
673
673
  self.failed_batches += 1
674
674
  self.num_failures += len(batch)
@@ -681,7 +681,7 @@ class BatchPoster(MigrationTaskBase):
681
681
  sys.exit(1)
682
682
 
683
683
  def handle_unicode_error(self, unicode_error, last_row):
684
- self.migration_report.add("Details", i18n.t("Encoding errors"))
684
+ self.migration_report.add("Details", i18n_t("Encoding errors"))
685
685
  logging.info("=========ERROR==============")
686
686
  logging.info(
687
687
  "%s Posting failed. Encoding error reading file",
@@ -1,11 +1,11 @@
1
1
  import logging
2
2
  from typing import Annotated, List
3
3
 
4
- import i18n
5
4
  from folio_uuid.folio_namespaces import FOLIONamespaces
6
5
  from pydantic import Field
7
6
 
8
7
  from folio_migration_tools.helper import Helper
8
+ from folio_migration_tools.i18n_cache import i18n_t
9
9
  from folio_migration_tools.library_configuration import (
10
10
  IlsFlavour,
11
11
  LibraryConfiguration,
@@ -153,7 +153,7 @@ class BibsTransformer(MigrationTaskBase):
153
153
  self.processor.wrap_up()
154
154
  with open(self.folder_structure.migration_reports_file, "w+") as report_file:
155
155
  self.mapper.migration_report.write_migration_report(
156
- i18n.t("Bibliographic records transformation report"),
156
+ i18n_t("Bibliographic records transformation report"),
157
157
  report_file,
158
158
  self.start_datetime,
159
159
  )
@@ -7,7 +7,6 @@ import time
7
7
  import traceback
8
8
  from typing import Annotated, List, Optional
9
9
 
10
- import i18n
11
10
  from folio_uuid.folio_namespaces import FOLIONamespaces
12
11
  from httpx import HTTPError
13
12
  from pydantic import Field
@@ -18,6 +17,7 @@ from folio_migration_tools.custom_exceptions import (
18
17
  )
19
18
  from folio_migration_tools.helper import Helper
20
19
  from folio_migration_tools.holdings_helper import HoldingsHelper
20
+ from folio_migration_tools.i18n_cache import i18n_t
21
21
  from folio_migration_tools.library_configuration import (
22
22
  FileDefinition,
23
23
  HridHandling,
@@ -351,14 +351,14 @@ class HoldingsCsvTransformer(MigrationTaskBase):
351
351
  )
352
352
  Helper.write_to_file(holdings_file, holding)
353
353
  self.mapper.migration_report.add_general_statistics(
354
- i18n.t("Holdings Records Written to disk")
354
+ i18n_t("Holdings Records Written to disk")
355
355
  )
356
356
  self.mapper.save_id_map_file(
357
357
  self.folder_structure.holdings_id_map_path, self.holdings_id_map
358
358
  )
359
359
  with open(self.folder_structure.migration_reports_file, "w") as migration_report_file:
360
360
  self.mapper.migration_report.write_migration_report(
361
- i18n.t("Holdings transformation report"),
361
+ i18n_t("Holdings transformation report"),
362
362
  migration_report_file,
363
363
  self.mapper.start_datetime,
364
364
  )
@@ -393,7 +393,7 @@ class HoldingsCsvTransformer(MigrationTaskBase):
393
393
  full_path = self.folder_structure.data_folder / "items" / file_def.file_name
394
394
  with open(full_path, encoding="utf-8-sig") as records_file:
395
395
  self.mapper.migration_report.add_general_statistics(
396
- i18n.t("Number of files processed")
396
+ i18n_t("Number of files processed")
397
397
  )
398
398
  start = time.time()
399
399
  records_processed = 0
@@ -412,7 +412,7 @@ class HoldingsCsvTransformer(MigrationTaskBase):
412
412
  except Exception as excepion:
413
413
  self.mapper.handle_generic_exception(idx, excepion)
414
414
  self.mapper.migration_report.add_general_statistics(
415
- i18n.t("Number of Legacy items in file")
415
+ i18n_t("Number of Legacy items in file")
416
416
  )
417
417
  if idx > 1 and idx % 10000 == 0:
418
418
  elapsed = idx / (time.time() - start)
@@ -481,7 +481,7 @@ class HoldingsCsvTransformer(MigrationTaskBase):
481
481
  self.holdings[bw_key] = incoming_holding
482
482
  self.mapper.create_and_write_boundwith_part(legacy_item_id, incoming_holding["id"])
483
483
  self.mapper.migration_report.add_general_statistics(
484
- i18n.t("Unique BW Holdings created from Items")
484
+ i18n_t("Unique BW Holdings created from Items")
485
485
  )
486
486
  else:
487
487
  self.merge_holding(bw_key, incoming_holding)
@@ -492,7 +492,7 @@ class HoldingsCsvTransformer(MigrationTaskBase):
492
492
  legacy_item_id, self.holdings[bw_key], self.object_type
493
493
  )
494
494
  self.mapper.migration_report.add_general_statistics(
495
- i18n.t("BW Items found tied to previously created BW Holdings")
495
+ i18n_t("BW Items found tied to previously created BW Holdings")
496
496
  )
497
497
  else:
498
498
  # Regular holding. Merge according to criteria
@@ -504,12 +504,12 @@ class HoldingsCsvTransformer(MigrationTaskBase):
504
504
  )
505
505
  if self.holdings.get(new_holding_key, None):
506
506
  self.mapper.migration_report.add_general_statistics(
507
- i18n.t("Holdings already created from Item")
507
+ i18n_t("Holdings already created from Item")
508
508
  )
509
509
  self.merge_holding(new_holding_key, incoming_holding)
510
510
  else:
511
511
  self.mapper.migration_report.add_general_statistics(
512
- i18n.t("Unique Holdings created from Items")
512
+ i18n_t("Unique Holdings created from Items")
513
513
  )
514
514
  self.holdings[new_holding_key] = incoming_holding
515
515
 
@@ -3,12 +3,12 @@ import json
3
3
  import logging
4
4
  from typing import Annotated, List
5
5
 
6
- import i18n
7
6
  from folio_uuid.folio_namespaces import FOLIONamespaces
8
7
  from pydantic import Field
9
8
 
10
9
  from folio_migration_tools.custom_exceptions import TransformationProcessError
11
10
  from folio_migration_tools.helper import Helper
11
+ from folio_migration_tools.i18n_cache import i18n_t
12
12
  from folio_migration_tools.library_configuration import (
13
13
  FileDefinition,
14
14
  HridHandling,
@@ -269,7 +269,7 @@ class HoldingsMarcTransformer(MigrationTaskBase):
269
269
  except FileNotFoundError as fnfe:
270
270
  raise TransformationProcessError(
271
271
  "",
272
- i18n.t("Provided boundwith relationship file not found"),
272
+ i18n_t("Provided boundwith relationship file not found"),
273
273
  self.task_configuration.boundwith_relationship_file_path,
274
274
  ) from fnfe
275
275
 
@@ -350,7 +350,7 @@ class HoldingsMarcTransformer(MigrationTaskBase):
350
350
 
351
351
  with open(self.folder_structure.migration_reports_file, "w+") as report_file:
352
352
  self.mapper.migration_report.write_migration_report(
353
- i18n.t("Bibliographic records transformation report"),
353
+ i18n_t("Bibliographic records transformation report"),
354
354
  report_file,
355
355
  self.start_datetime,
356
356
  )
@@ -12,6 +12,8 @@ import i18n
12
12
  from folio_uuid.folio_namespaces import FOLIONamespaces
13
13
  from pydantic import Field
14
14
 
15
+ from folio_migration_tools.i18n_cache import i18n_t
16
+
15
17
  from folio_migration_tools.custom_exceptions import (
16
18
  TransformationProcessError,
17
19
  TransformationRecordFailedError,
@@ -335,7 +337,7 @@ class ItemsTransformer(MigrationTaskBase):
335
337
  records_in_file = 0
336
338
  with open(full_path, encoding="utf-8-sig") as records_file:
337
339
  self.mapper.migration_report.add_general_statistics(
338
- i18n.t("Number of files processed")
340
+ i18n_t("Number of files processed")
339
341
  )
340
342
  start = time.time()
341
343
  for idx, record in enumerate(self.mapper.get_objects(records_file, full_path)):
@@ -368,7 +370,7 @@ class ItemsTransformer(MigrationTaskBase):
368
370
  # TODO: turn this into a asynchronous task
369
371
  Helper.write_to_file(results_file, folio_rec)
370
372
  self.mapper.migration_report.add_general_statistics(
371
- i18n.t("Number of records written to disk")
373
+ i18n_t("Number of records written to disk")
372
374
  )
373
375
  self.mapper.report_folio_mapping(folio_rec, self.mapper.schema)
374
376
  except TransformationProcessError as process_error:
@@ -387,7 +389,7 @@ class ItemsTransformer(MigrationTaskBase):
387
389
  i18n.t("Number of Legacy items in %{container}", container=file_def),
388
390
  )
389
391
  self.mapper.migration_report.add_general_statistics(
390
- i18n.t("Number of Legacy items in total")
392
+ i18n_t("Number of Legacy items in total")
391
393
  )
392
394
  self.print_progress(idx, start)
393
395
  records_in_file = idx + 1
@@ -469,7 +471,7 @@ class ItemsTransformer(MigrationTaskBase):
469
471
  self.extradata_writer.flush()
470
472
  with open(self.folder_structure.migration_reports_file, "w") as migration_report_file:
471
473
  self.mapper.migration_report.write_migration_report(
472
- i18n.t("Item transformation report"),
474
+ i18n_t("Item transformation report"),
473
475
  migration_report_file,
474
476
  self.mapper.start_datetime,
475
477
  )
@@ -19,6 +19,7 @@ from art import tprint
19
19
  from folio_migration_tools.circulation_helper import CirculationHelper
20
20
  from folio_migration_tools.custom_exceptions import TransformationRecordFailedError
21
21
  from folio_migration_tools.helper import Helper
22
+ from folio_migration_tools.i18n_cache import i18n_t
22
23
  from folio_migration_tools.library_configuration import (
23
24
  FileDefinition,
24
25
  LibraryConfiguration,
@@ -205,7 +206,7 @@ class LoansMigrator(MigrationTaskBase):
205
206
  ):
206
207
  t0_migration = time.time()
207
208
  self.migration_report.add_general_statistics(
208
- i18n.t("Processed pre-validated loans")
209
+ i18n_t("Processed pre-validated loans")
209
210
  )
210
211
  try:
211
212
  self.checkout_single_loan(legacy_loan)
@@ -233,15 +234,15 @@ class LoansMigrator(MigrationTaskBase):
233
234
  res_checkout = self.circulation_helper.check_out_by_barcode(legacy_loan)
234
235
 
235
236
  if res_checkout.was_successful:
236
- self.migration_report.add("Details", i18n.t("Checked out on first try"))
237
- self.migration_report.add_general_statistics(i18n.t("Successfully checked out"))
237
+ self.migration_report.add("Details", i18n_t("Checked out on first try"))
238
+ self.migration_report.add_general_statistics(i18n_t("Successfully checked out"))
238
239
  self.set_renewal_count(legacy_loan, res_checkout)
239
240
  self.set_new_status(legacy_loan, res_checkout)
240
241
  elif res_checkout.should_be_retried:
241
242
  res_checkout2 = self.handle_checkout_failure(legacy_loan, res_checkout)
242
243
  if res_checkout2.was_successful and res_checkout2.folio_loan:
243
- self.migration_report.add("Details", i18n.t("Checked out on second try"))
244
- self.migration_report.add_general_statistics(i18n.t("Successfully checked out"))
244
+ self.migration_report.add("Details", i18n_t("Checked out on second try"))
245
+ self.migration_report.add_general_statistics(i18n_t("Successfully checked out"))
245
246
  logging.info("Checked out on second try")
246
247
  self.set_renewal_count(legacy_loan, res_checkout2)
247
248
  self.set_new_status(legacy_loan, res_checkout2)
@@ -256,7 +257,7 @@ class LoansMigrator(MigrationTaskBase):
256
257
  )
257
258
  else:
258
259
  self.failed[legacy_loan.item_barcode] = legacy_loan
259
- self.migration_report.add_general_statistics(i18n.t("Failed loans"))
260
+ self.migration_report.add_general_statistics(i18n_t("Failed loans"))
260
261
  logging.error("Failed on second try: %s", res_checkout2.error_message)
261
262
  self.migration_report.add(
262
263
  "Details",
@@ -264,21 +265,21 @@ class LoansMigrator(MigrationTaskBase):
264
265
  )
265
266
  raise TransformationRecordFailedError(
266
267
  f"Row {legacy_loan.row}",
267
- i18n.t("Loans failing during checkout, second try"),
268
+ i18n_t("Loans failing during checkout, second try"),
268
269
  json.dumps(legacy_loan.to_dict()),
269
270
  )
270
271
  elif not res_checkout.should_be_retried:
271
272
  logging.error("Failed first time. No retries: %s", res_checkout.error_message)
272
- self.migration_report.add_general_statistics(i18n.t("Failed loans"))
273
+ self.migration_report.add_general_statistics(i18n_t("Failed loans"))
273
274
  self.migration_report.add(
274
275
  "Details",
275
- i18n.t("Failed 1st time. No retries")
276
+ i18n_t("Failed 1st time. No retries")
276
277
  + f": {res_checkout.migration_report_message}",
277
278
  )
278
279
  self.failed[legacy_loan.item_barcode] = legacy_loan
279
280
  raise TransformationRecordFailedError(
280
281
  f"Row {legacy_loan.row}",
281
- i18n.t("Loans failing during checkout"),
282
+ i18n_t("Loans failing during checkout"),
282
283
  json.dumps(legacy_loan.to_dict()),
283
284
  )
284
285
 
@@ -300,7 +301,7 @@ class LoansMigrator(MigrationTaskBase):
300
301
  def set_renewal_count(self, legacy_loan: LegacyLoan, res_checkout: TransactionResult):
301
302
  if legacy_loan.renewal_count > 0:
302
303
  self.update_open_loan(res_checkout.folio_loan, legacy_loan)
303
- self.migration_report.add_general_statistics(i18n.t("Updated renewal count for loan"))
304
+ self.migration_report.add_general_statistics(i18n_t("Updated renewal count for loan"))
304
305
 
305
306
  def wrap_up(self):
306
307
  for k, v in self.failed.items():
@@ -311,7 +312,7 @@ class LoansMigrator(MigrationTaskBase):
311
312
 
312
313
  with open(self.folder_structure.migration_reports_file, "w+") as report_file:
313
314
  self.migration_report.write_migration_report(
314
- i18n.t("Loans migration report"), report_file, self.start_datetime
315
+ i18n_t("Loans migration report"), report_file, self.start_datetime
315
316
  )
316
317
  self.clean_out_empty_logs()
317
318
 
@@ -351,7 +352,7 @@ class LoansMigrator(MigrationTaskBase):
351
352
  )
352
353
  if has_item_barcode and has_patron_barcode and has_proxy_barcode:
353
354
  self.migration_report.add_general_statistics(
354
- i18n.t("Loans verified against migrated user and item")
355
+ i18n_t("Loans verified against migrated user and item")
355
356
  )
356
357
  yield loan
357
358
  else:
@@ -360,9 +361,9 @@ class LoansMigrator(MigrationTaskBase):
360
361
  self.migration_report.add_general_statistics(i18n.t("Failed loans"))
361
362
  self.migration_report.add(
362
363
  "DiscardedLoans",
363
- i18n.t("Loans discarded. Had migrated item barcode")
364
+ i18n_t("Loans discarded. Had migrated item barcode")
364
365
  + f": {has_item_barcode}. "
365
- + i18n.t("Had migrated user barcode")
366
+ + i18n_t("Had migrated user barcode")
366
367
  + f": {has_patron_barcode}"
367
368
  + f": {has_proxy_barcode}",
368
369
  )
@@ -399,9 +400,9 @@ class LoansMigrator(MigrationTaskBase):
399
400
  if any(legacy_loan.errors):
400
401
  num_bad += 1
401
402
  self.migration_report.add_general_statistics(
402
- i18n.t("Loans failed pre-validation")
403
+ i18n_t("Loans failed pre-validation")
403
404
  )
404
- self.migration_report.add_general_statistics(i18n.t("Failed loans"))
405
+ self.migration_report.add_general_statistics(i18n_t("Failed loans"))
405
406
  for error in legacy_loan.errors:
406
407
  self.migration_report.add("DiscardedLoans", f"{error[0]} - {error[1]}")
407
408
  # Add this loan to failed loans for later correction and re-run.
@@ -412,7 +413,7 @@ class LoansMigrator(MigrationTaskBase):
412
413
  results.append(legacy_loan)
413
414
  except TransformationRecordFailedError as trfe:
414
415
  num_bad += 1
415
- self.migration_report.add_general_statistics(i18n.t("Loans failed pre-validation"))
416
+ self.migration_report.add_general_statistics(i18n_t("Loans failed pre-validation"))
416
417
  self.migration_report.add(
417
418
  "DiscardedLoans",
418
419
  f"{trfe.message} - see data issues log",
@@ -6,7 +6,6 @@ import time
6
6
  import traceback
7
7
  from typing import List, Optional
8
8
 
9
- import i18n
10
9
  from folio_uuid.folio_namespaces import FOLIONamespaces
11
10
 
12
11
  from folio_migration_tools.custom_exceptions import (
@@ -15,6 +14,7 @@ from folio_migration_tools.custom_exceptions import (
15
14
  TransformationRecordFailedError,
16
15
  )
17
16
  from folio_migration_tools.helper import Helper
17
+ from folio_migration_tools.i18n_cache import i18n_t
18
18
  from folio_migration_tools.library_configuration import (
19
19
  FileDefinition,
20
20
  LibraryConfiguration,
@@ -118,7 +118,7 @@ class ManualFeeFinesTransformer(MigrationTaskBase):
118
118
  full_path = self.folder_structure.legacy_records_folder / file_def.file_name
119
119
  with open(full_path, encoding="utf-8-sig") as records_file:
120
120
  self.mapper.migration_report.add_general_statistics(
121
- i18n.t("Number of files processed")
121
+ i18n_t("Number of files processed")
122
122
  )
123
123
  start = time.time()
124
124
 
@@ -172,7 +172,7 @@ class ManualFeeFinesTransformer(MigrationTaskBase):
172
172
  self.folder_structure.migration_reports_file,
173
173
  )
174
174
  self.mapper.migration_report.write_migration_report(
175
- i18n.t("Manual fee/fine transformation report"),
175
+ i18n_t("Manual fee/fine transformation report"),
176
176
  migration_report_file,
177
177
  self.start_datetime,
178
178
  )
@@ -16,6 +16,7 @@ from folio_migration_tools.custom_exceptions import (
16
16
  TransformationRecordFailedError,
17
17
  )
18
18
  from folio_migration_tools.helper import Helper
19
+ from folio_migration_tools.i18n_cache import i18n_t
19
20
  from folio_migration_tools.library_configuration import (
20
21
  FileDefinition,
21
22
  LibraryConfiguration,
@@ -234,7 +235,7 @@ class OrdersTransformer(MigrationTaskBase):
234
235
  open(self.folder_structure.created_objects_path, "w+") as results_file,
235
236
  ):
236
237
  self.mapper.migration_report.add_general_statistics(
237
- i18n.t("Number of files processed")
238
+ i18n_t("Number of files processed")
238
239
  )
239
240
  start = time.time()
240
241
  records_processed = 0
@@ -253,7 +254,7 @@ class OrdersTransformer(MigrationTaskBase):
253
254
  self.mapper.perform_additional_mapping(legacy_id, folio_rec)
254
255
 
255
256
  self.mapper.migration_report.add_general_statistics(
256
- i18n.t("TOTAL Purchase Order Lines created")
257
+ i18n_t("TOTAL Purchase Order Lines created")
257
258
  )
258
259
  self.mapper.report_folio_mapping(folio_rec, self.mapper.composite_order_schema)
259
260
  self.mapper.notes_mapper.map_notes(
@@ -316,7 +317,7 @@ class OrdersTransformer(MigrationTaskBase):
316
317
  self.folder_structure.migration_reports_file,
317
318
  )
318
319
  self.mapper.migration_report.write_migration_report(
319
- i18n.t("Pruchase Orders and Purchase Order Lines Transformation Report"),
320
+ i18n_t("Pruchase Orders and Purchase Order Lines Transformation Report"),
320
321
  migration_report_file,
321
322
  self.start_datetime,
322
323
  )
@@ -13,6 +13,7 @@ from zoneinfo import ZoneInfo
13
13
  from folio_migration_tools.circulation_helper import CirculationHelper
14
14
  from folio_migration_tools.custom_dict import InsensitiveDictReader
15
15
  from folio_migration_tools.helper import Helper
16
+ from folio_migration_tools.i18n_cache import i18n_t
16
17
  from folio_migration_tools.library_configuration import (
17
18
  FileDefinition,
18
19
  LibraryConfiguration,
@@ -147,7 +148,7 @@ class RequestsMigrator(MigrationTaskBase):
147
148
 
148
149
  def prepare_legacy_request(self, legacy_request: LegacyRequest):
149
150
  patron = self.circulation_helper.get_user_by_barcode(legacy_request.patron_barcode)
150
- self.migration_report.add_general_statistics(i18n.t("Patron lookups performed"))
151
+ self.migration_report.add_general_statistics(i18n_t("Patron lookups performed"))
151
152
 
152
153
  if not patron:
153
154
  logging.error(f"No user with barcode {legacy_request.patron_barcode} found in FOLIO")
@@ -157,18 +158,18 @@ class RequestsMigrator(MigrationTaskBase):
157
158
  f"{legacy_request.patron_barcode}",
158
159
  )
159
160
  self.migration_report.add_general_statistics(
160
- i18n.t("No user with barcode found in FOLIO")
161
+ i18n_t("No user with barcode found in FOLIO")
161
162
  )
162
163
  self.failed_requests.add(legacy_request)
163
164
  return False, legacy_request
164
165
  legacy_request.patron_id = patron.get("id")
165
166
 
166
167
  item = self.circulation_helper.get_item_by_barcode(legacy_request.item_barcode)
167
- self.migration_report.add_general_statistics(i18n.t("Item lookups performed"))
168
+ self.migration_report.add_general_statistics(i18n_t("Item lookups performed"))
168
169
  if not item:
169
170
  logging.error(f"No item with barcode {legacy_request.item_barcode} found in FOLIO")
170
171
  self.migration_report.add_general_statistics(
171
- i18n.t("No item with barcode found in FOLIO")
172
+ i18n_t("No item with barcode found in FOLIO")
172
173
  )
173
174
  Helper.log_data_issue(
174
175
  f"{legacy_request.item_barcode}",
@@ -178,7 +179,7 @@ class RequestsMigrator(MigrationTaskBase):
178
179
  self.failed_requests.add(legacy_request)
179
180
  return False, legacy_request
180
181
  holding = self.circulation_helper.get_holding_by_uuid(item.get("holdingsRecordId"))
181
- self.migration_report.add_general_statistics(i18n.t("Holdings lookups performed"))
182
+ self.migration_report.add_general_statistics(i18n_t("Holdings lookups performed"))
182
183
  legacy_request.item_id = item.get("id")
183
184
  legacy_request.holdings_record_id = item.get("holdingsRecordId")
184
185
  legacy_request.instance_id = holding.get("instanceId")
@@ -186,7 +187,7 @@ class RequestsMigrator(MigrationTaskBase):
186
187
  legacy_request.request_type = "Page"
187
188
  logging.info(f"Setting request to Page, since the status is {item['status']['name']}")
188
189
  self.migration_report.add_general_statistics(
189
- i18n.t("Valid, prepared requests, ready for posting")
190
+ i18n_t("Valid, prepared requests, ready for posting")
190
191
  )
191
192
  return True, legacy_request
192
193
 
@@ -206,11 +207,11 @@ class RequestsMigrator(MigrationTaskBase):
206
207
  self.folio_client, legacy_request, self.migration_report
207
208
  ):
208
209
  self.migration_report.add_general_statistics(
209
- i18n.t("Successfully migrated requests")
210
+ i18n_t("Successfully migrated requests")
210
211
  )
211
212
  else:
212
213
  self.migration_report.add_general_statistics(
213
- i18n.t("Unsuccessfully migrated requests")
214
+ i18n_t("Unsuccessfully migrated requests")
214
215
  )
215
216
  self.failed_requests.add(legacy_request)
216
217
  if num_requests == 1:
@@ -233,7 +234,7 @@ class RequestsMigrator(MigrationTaskBase):
233
234
 
234
235
  with open(self.folder_structure.migration_reports_file, "w+") as report_file:
235
236
  self.migration_report.write_migration_report(
236
- i18n.t("Requests migration report"), report_file, self.start_datetime
237
+ i18n_t("Requests migration report"), report_file, self.start_datetime
237
238
  )
238
239
  self.clean_out_empty_logs()
239
240
 
@@ -14,6 +14,7 @@ from folio_uuid.folio_namespaces import FOLIONamespaces
14
14
 
15
15
  from folio_migration_tools.custom_dict import InsensitiveDictReader
16
16
  from folio_migration_tools.custom_exceptions import TransformationProcessError
17
+ from folio_migration_tools.i18n_cache import i18n_t
17
18
  from folio_migration_tools.library_configuration import (
18
19
  FileDefinition,
19
20
  LibraryConfiguration,
@@ -90,7 +91,7 @@ class ReservesMigrator(MigrationTaskBase):
90
91
  logging.info("Starting")
91
92
  for num_reserves, legacy_reserve in enumerate(self.valid_reserves, start=1):
92
93
  t0_migration = time.time()
93
- self.migration_report.add_general_statistics(i18n.t("Processed reserves"))
94
+ self.migration_report.add_general_statistics(i18n_t("Processed reserves"))
94
95
  try:
95
96
  self.post_single_reserve(legacy_reserve)
96
97
  except Exception as ee:
@@ -107,10 +108,10 @@ class ReservesMigrator(MigrationTaskBase):
107
108
  path, legacy_reserve.to_dict(), "POST", i18n.t("Posted reserves")
108
109
  ):
109
110
  self.migration_report.add_general_statistics(
110
- i18n.t("Successfully posted reserves")
111
+ i18n_t("Successfully posted reserves")
111
112
  )
112
113
  else:
113
- self.migration_report.add_general_statistics(i18n.t("Failure to post reserve"))
114
+ self.migration_report.add_general_statistics(i18n_t("Failure to post reserve"))
114
115
  except Exception as ee:
115
116
  logging.error(ee)
116
117
 
@@ -123,7 +124,7 @@ class ReservesMigrator(MigrationTaskBase):
123
124
 
124
125
  with open(self.folder_structure.migration_reports_file, "w+") as report_file:
125
126
  self.migration_report.write_migration_report(
126
- i18n.t("Reserves migration report"), report_file, self.start_datetime
127
+ i18n_t("Reserves migration report"), report_file, self.start_datetime
127
128
  )
128
129
  self.clean_out_empty_logs()
129
130
 
@@ -4,7 +4,6 @@ import sys
4
4
  from typing import Optional, Annotated
5
5
  from pydantic import Field
6
6
 
7
- import i18n
8
7
  from folio_uuid.folio_namespaces import FOLIONamespaces
9
8
  from art import tprint
10
9
 
@@ -13,6 +12,7 @@ from folio_migration_tools.custom_exceptions import (
13
12
  TransformationRecordFailedError,
14
13
  )
15
14
  from folio_migration_tools.helper import Helper
15
+ from folio_migration_tools.i18n_cache import i18n_t
16
16
  from folio_migration_tools.library_configuration import (
17
17
  FileDefinition,
18
18
  LibraryConfiguration,
@@ -218,13 +218,13 @@ class UserTransformer(MigrationTaskBase):
218
218
  logging.info("## First FOLIO user")
219
219
  logging.info(json.dumps(folio_user, indent=4, sort_keys=True))
220
220
  self.mapper.migration_report.add_general_statistics(
221
- i18n.t("Successful user transformations")
221
+ i18n_t("Successful user transformations")
222
222
  )
223
223
  if num_users % 1000 == 0:
224
224
  logging.info(f"{num_users} users processed.")
225
225
  except TransformationRecordFailedError as tre:
226
226
  self.mapper.migration_report.add_general_statistics(
227
- i18n.t("Records failed")
227
+ i18n_t("Records failed")
228
228
  )
229
229
  Helper.log_data_issue(tre.index_or_id, tre.message, tre.data_value)
230
230
  logging.error(tre)
@@ -241,7 +241,7 @@ class UserTransformer(MigrationTaskBase):
241
241
  logging.error(num_users)
242
242
  logging.error(json.dumps(legacy_user))
243
243
  self.mapper.migration_report.add_general_statistics(
244
- i18n.t("Failed user transformations")
244
+ i18n_t("Failed user transformations")
245
245
  )
246
246
  logging.error(ee, exc_info=True)
247
247
 
@@ -255,7 +255,7 @@ class UserTransformer(MigrationTaskBase):
255
255
  self.extradata_writer.flush()
256
256
  with open(self.folder_structure.migration_reports_file, "w") as migration_report_file:
257
257
  self.mapper.migration_report.write_migration_report(
258
- i18n.t("Users transformation report"),
258
+ i18n_t("Users transformation report"),
259
259
  migration_report_file,
260
260
  self.mapper.start_datetime,
261
261
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: folio-migration-tools
3
- Version: 1.10.0b6
3
+ Version: 1.10.0b7
4
4
  Summary: A tool allowing you to migrate data from legacy ILS:s (Library systems) into FOLIO LSP
5
5
  Keywords: FOLIO,ILS,LSP,Library Systems,MARC21,Library data
6
6
  Author: Theodor Tolstoy, Lisa Sjögren, Brooks Travis, Jeremy Nelson, Clinton Bradford
@@ -1,17 +1,18 @@
1
1
  folio_migration_tools/__init__.py,sha256=lnYgqA47l0iA-iORkVH3dgevk7gyGxVwg3MnLltA-U8,223
2
2
  folio_migration_tools/__main__.py,sha256=KJdmLkKwAygTKuIKfvDL3M0JdVgsCbf2_LTL1FP6GxU,9233
3
- folio_migration_tools/circulation_helper.py,sha256=r1zpOKy47VFRHyXHvwUEjPfQ4jyJpjMAYc1IktJ94WU,14661
3
+ folio_migration_tools/circulation_helper.py,sha256=CIrr_8DVDlFN8gvNKRBxgOqW8zNRamP7SZtxOdzz5PQ,14713
4
4
  folio_migration_tools/colors.py,sha256=GP0wdI_GZ2WD5SjrbPN-S3u8vvN_u6rGQIBBcWv_0ZM,227
5
5
  folio_migration_tools/config_file_load.py,sha256=zHHa6NDkN6EJiQE4DgjrFQPVKsd70POsfbGkB8308jg,2822
6
6
  folio_migration_tools/custom_dict.py,sha256=rRd9_RQqI85171p7wTfpMM0Mladh-LChbgMSmLvN7N0,680
7
7
  folio_migration_tools/custom_exceptions.py,sha256=BLP1gMPbTHSN-rqxzTawT4sRLiyAU3blBdkUBwiiPRk,2642
8
8
  folio_migration_tools/extradata_writer.py,sha256=fuchNcMc6BYb9IyfAcvXg7X4J2TfX6YiROfT2hr0JMw,1678
9
- folio_migration_tools/folder_structure.py,sha256=ExrXNEWvCB5QMH17kQSyTDQ04thq--t8_p3F_iuyf0k,6776
10
- folio_migration_tools/helper.py,sha256=Jb-9PrMkgOUGYScRf8jMmGGTcPIohm3eFHenGSi3cUA,2979
11
- folio_migration_tools/holdings_helper.py,sha256=yJpz6aJrKRBiJ1MtT5bs2vXAc88uJuGh2_KDuCySOKc,7559
9
+ folio_migration_tools/folder_structure.py,sha256=YTLCjmiY0RhQwsCiXSQ0awkLOncZZmLdFuaP7rDVAIY,7301
10
+ folio_migration_tools/helper.py,sha256=t2SgSkNKqdz1eenZjpqgokrLnFQcx7vO7xeQ-UxTniE,3020
11
+ folio_migration_tools/holdings_helper.py,sha256=Thx8vJyEZKuJTQvgPODtaUv73Dhmqb_Nb4JprGyhV4U,7611
12
+ folio_migration_tools/i18n_cache.py,sha256=wOJc3OkHwItlFNC2jQB5R9AoIga0g0P1YhQqHgjeqK4,2858
12
13
  folio_migration_tools/i18n_config.py,sha256=3AH_2b9zTsxE4XTe4isM_zYtPJSlK0ix6eBmV7kAYUM,228
13
14
  folio_migration_tools/library_configuration.py,sha256=lgfnY1yXIK-sNlfooV646H6O0QEUvF-yRRh-YdOGjG4,8993
14
- folio_migration_tools/mapper_base.py,sha256=NopqwmHXcNe6s7zKCzZzcy0Qp-rCrQGknepes0sw4ns,23725
15
+ folio_migration_tools/mapper_base.py,sha256=sSnPK9yicr7fJEvlfG8AHN2Ght4FVwiqcUR3gRKHYS8,23777
15
16
  folio_migration_tools/mapping_file_transformation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
17
  folio_migration_tools/mapping_file_transformation/courses_mapper.py,sha256=ZRQ8KNgmM4Ki3ZQ3eqwyceOM55Bn8t3ZT8Pn3T-wmK8,8092
17
18
  folio_migration_tools/mapping_file_transformation/holdings_mapper.py,sha256=1y64W1i4v3lV7LD0_QwrHBPx6HkA1o5muyXpWKiH2Hk,8305
@@ -30,25 +31,25 @@ folio_migration_tools/marc_rules_transformation/hrid_handler.py,sha256=WudBOzCwc
30
31
  folio_migration_tools/marc_rules_transformation/loc_language_codes.xml,sha256=ztn2_yKws6qySL4oSsZh7sOjxq5bCC1PhAnXJdtgmJ0,382912
31
32
  folio_migration_tools/marc_rules_transformation/marc_file_processor.py,sha256=o03d_G-4MR4e5VPfu7ljxAVDl79o2ONpQIqQ-V2RCdA,12523
32
33
  folio_migration_tools/marc_rules_transformation/marc_reader_wrapper.py,sha256=9ATjYMRAjy0QcXtmNZaHVhHLJ5hE1WUgOcF6KMJjbgo,5309
33
- folio_migration_tools/marc_rules_transformation/rules_mapper_base.py,sha256=KxyZjizbLwwAY2PfMSyh6u_mVTpfhyvdaii_PlpLscw,45857
34
- folio_migration_tools/marc_rules_transformation/rules_mapper_bibs.py,sha256=F8tKn59zHUV3Gqa9NY-JvTbWgfDjNTcPvQONk8gzwGs,30428
35
- folio_migration_tools/marc_rules_transformation/rules_mapper_holdings.py,sha256=YILyEfO-LkQPk-4OjiuY68X5xDA0LlI7UUp7_mvzLUE,29184
36
- folio_migration_tools/migration_report.py,sha256=B8e4tMfT0xCJ3BxkSg7ZZJYmg0VLQVXmmVnWwmojZD4,4260
34
+ folio_migration_tools/marc_rules_transformation/rules_mapper_base.py,sha256=xxLH27uGGUyMjpE_Hdh1c1kGhg4X0eANm3dq49TfqNs,45909
35
+ folio_migration_tools/marc_rules_transformation/rules_mapper_bibs.py,sha256=f62Ig0Nqe0szUHzr7th6V65kcsfzhtzKzjGkxAc7ui4,30480
36
+ folio_migration_tools/marc_rules_transformation/rules_mapper_holdings.py,sha256=zMmS2j4_HegVFlQMerTuZzH0WyGILJcRITzrGSeyll4,29236
37
+ folio_migration_tools/migration_report.py,sha256=Hecfb9XcXhH01VgHWD4D0e4dQwGMlanm6mgVuLBolu4,4589
37
38
  folio_migration_tools/migration_tasks/__init__.py,sha256=ZkbY_yGyB84Ke8OMlYUzyyBj4cxxNrhMTwQlu_GbdDs,211
38
- folio_migration_tools/migration_tasks/batch_poster.py,sha256=rbSx3dyF4UbNdFiAhBRDpBTvAwQI3BR4s4_sIBsM-70,46214
39
- folio_migration_tools/migration_tasks/bibs_transformer.py,sha256=zPxh2tjyqx88fuH1FuKLwhT6lhZ5fVTQAqE08IggYgM,6351
39
+ folio_migration_tools/migration_tasks/batch_poster.py,sha256=XKtCM0HgHJNuWR4NGA2r9bedvO_KLPlzPwHg4qQrzsg,46254
40
+ folio_migration_tools/migration_tasks/bibs_transformer.py,sha256=zAKKAyLmqv2-AaJsP0sLHg4lQVEJtVyJjKJwmVG_xys,6391
40
41
  folio_migration_tools/migration_tasks/courses_migrator.py,sha256=sKIeyUlc7o189lw88XbGILVkwnR9krqO0PgS-vLCCm8,7039
41
- folio_migration_tools/migration_tasks/holdings_csv_transformer.py,sha256=JzOufqjSR2V-gUvOq0pdQFsXjpxk1ldGJBQWIWGfCps,21915
42
- folio_migration_tools/migration_tasks/holdings_marc_transformer.py,sha256=b1lWbYmBdvN37Yk_hpeh7y2TDrzdHmUm5jXxP2jxZ-0,14191
43
- folio_migration_tools/migration_tasks/items_transformer.py,sha256=gIJ9SKUENE3OaEouGAFTsGjciN_YxwRoUAAEKJlfG-E,19498
44
- folio_migration_tools/migration_tasks/loans_migrator.py,sha256=6mwtA9-6B_pU1GKS9VD7Wu5stZ8YLlyeliFJhyPuho0,38785
45
- folio_migration_tools/migration_tasks/manual_fee_fines_transformer.py,sha256=CnmlTge7nChUJ10EiUkriQtJlVxWqglgfhjgneh2_yM,7247
42
+ folio_migration_tools/migration_tasks/holdings_csv_transformer.py,sha256=-SKgDJLNAWamLAfXEyJVC9vZCKUY3eyZqVfA_oaVmmU,21955
43
+ folio_migration_tools/migration_tasks/holdings_marc_transformer.py,sha256=L4oY0N25NLk_rbzO6HsMbGRh-aeG7LWgr_xypFOJzGw,14231
44
+ folio_migration_tools/migration_tasks/items_transformer.py,sha256=GKDer0Nwff_1Wv5wOofElC84I4ZDRg0w2oTZZB-xGCU,19551
45
+ folio_migration_tools/migration_tasks/loans_migrator.py,sha256=vSmhcyUJraPRY3GNc4F4sUN3GHYHQCO_Nfc1XJaL3tY,38837
46
+ folio_migration_tools/migration_tasks/manual_fee_fines_transformer.py,sha256=qFu5QcaFIvo7K8DD6VEgtJ1h9InHr1x5-D3pdcXt4bg,7287
46
47
  folio_migration_tools/migration_tasks/migration_task_base.py,sha256=rhIYFZ_dYWEyopkR2shht5ybKk4JQzxctSlBizml50g,22551
47
- folio_migration_tools/migration_tasks/orders_transformer.py,sha256=h8EyRbvbtwDZJq1y73J7oZFRdI1U4vq1Vrlay4GLf4M,13885
48
+ folio_migration_tools/migration_tasks/orders_transformer.py,sha256=XaQ0q6Hoe2n-NsrjxE_iB5n5CC69F4-WW6xN_EC5WHI,13937
48
49
  folio_migration_tools/migration_tasks/organization_transformer.py,sha256=5s-ACb9-R8JLlPnROOq1ZnDIRCLQeWaxORDn0SrhQqs,16747
49
- folio_migration_tools/migration_tasks/requests_migrator.py,sha256=Q7sWOxqq73Fdg3Q1tmpvRxU9qhhG1BV3AGMoCMwh2cE,14768
50
- folio_migration_tools/migration_tasks/reserves_migrator.py,sha256=jdiwWAlMydXE2vlv0lgHRUljarslveyVOu-TCnymAEs,9953
51
- folio_migration_tools/migration_tasks/user_transformer.py,sha256=apgVCoJQ4sB5aFp7p8FdSQrDfXSIzgEbEQ3C-6dG568,12621
50
+ folio_migration_tools/migration_tasks/requests_migrator.py,sha256=6WtYyMNGEQgyCHwgUWj9K_fpWtXfcCiZFy56Y2ZJSIw,14820
51
+ folio_migration_tools/migration_tasks/reserves_migrator.py,sha256=RQdAQQORgHUJzA3DyVLVJ9N_fqNj2mDG688fWRSvda0,10005
52
+ folio_migration_tools/migration_tasks/user_transformer.py,sha256=p_B-Ywnk4d6c-gEyU24sv2YpZYByJFfDJRGlidXSed0,12661
52
53
  folio_migration_tools/task_configuration.py,sha256=6eqbjjSWfi-qgp0bhCsuBVE3gTK4HaXzXsAo68JPGc0,1146
53
54
  folio_migration_tools/transaction_migration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
54
55
  folio_migration_tools/transaction_migration/legacy_loan.py,sha256=A5qvThfP3g62YnykLyti_tqTY7dq1SbLi3WZz7QXk6s,7399
@@ -56,7 +57,7 @@ folio_migration_tools/transaction_migration/legacy_request.py,sha256=Kv7jpBIuZ_q
56
57
  folio_migration_tools/transaction_migration/legacy_reserve.py,sha256=qzw0okg4axAE_ezXopP9gFsQ_e60o0zh7zqRzFBSWHY,1806
57
58
  folio_migration_tools/transaction_migration/transaction_result.py,sha256=cTdCN0BnlI9_ZJB2Z3Fdkl9gpymIi-9mGZsRFlQcmDk,656
58
59
  folio_migration_tools/translations/en.json,sha256=pS7dhHmj4XBqTcFNIcqFgRMY557fQan1RomdNg6PtdA,40941
59
- folio_migration_tools-1.10.0b6.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
60
- folio_migration_tools-1.10.0b6.dist-info/entry_points.txt,sha256=mJRRiCNP9j7_NpVXamHEiW8pDEjWQs1vEqD89G354cM,79
61
- folio_migration_tools-1.10.0b6.dist-info/METADATA,sha256=EcDMNh4diSH6vDbrIQWdk-kcTdM2W2Agw3X4lo22h2Y,7162
62
- folio_migration_tools-1.10.0b6.dist-info/RECORD,,
60
+ folio_migration_tools-1.10.0b7.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
61
+ folio_migration_tools-1.10.0b7.dist-info/entry_points.txt,sha256=mJRRiCNP9j7_NpVXamHEiW8pDEjWQs1vEqD89G354cM,79
62
+ folio_migration_tools-1.10.0b7.dist-info/METADATA,sha256=QUOCc0f645zhkB5innLfODWQ1Pj2L2KHV69-87_q9Wo,7162
63
+ folio_migration_tools-1.10.0b7.dist-info/RECORD,,