@natlibfi/marc-record-validators-melinda 12.1.0-alpha.1 → 13.0.0
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.
- package/.github/workflows/melinda-node-tests-and-publish.yml +17 -17
- package/README.md +1 -1
- package/dist/addMissingField041.js +6 -3
- package/dist/addMissingField041.js.map +2 -2
- package/dist/addMissingField336.js +7 -4
- package/dist/addMissingField336.js.map +2 -2
- package/dist/addMissingField337.js +6 -3
- package/dist/addMissingField337.js.map +2 -2
- package/dist/addMissingField338.js +8 -5
- package/dist/addMissingField338.js.map +2 -2
- package/dist/cyrillux-usemarcon-replacement.js +5 -2
- package/dist/cyrillux-usemarcon-replacement.js.map +2 -2
- package/dist/cyrillux.js +24 -18
- package/dist/cyrillux.js.map +2 -2
- package/dist/dataProvenanceUtils.js +19 -0
- package/dist/dataProvenanceUtils.js.map +7 -0
- package/dist/disambiguateSeriesStatements.js +2 -1
- package/dist/disambiguateSeriesStatements.js.map +2 -2
- package/dist/disambiguateSeriesStatements.test.js +1 -7
- package/dist/disambiguateSeriesStatements.test.js.map +2 -2
- package/dist/drop-terms.js +123 -0
- package/dist/drop-terms.js.map +7 -0
- package/dist/drop-terms.test.js +56 -0
- package/dist/drop-terms.test.js.map +7 -0
- package/dist/field-008-18-34-character-groups.test.js +1 -7
- package/dist/field-008-18-34-character-groups.test.js.map +2 -2
- package/dist/field-505-separators.test.js +1 -7
- package/dist/field-505-separators.test.js.map +2 -2
- package/dist/field-521-fix.test.js +1 -7
- package/dist/field-521-fix.test.js.map +2 -2
- package/dist/fix-33X.js +7 -4
- package/dist/fix-33X.js.map +2 -2
- package/dist/fix-33X.test.js +1 -1
- package/dist/fix-33X.test.js.map +1 -1
- package/dist/fix-country-codes.js +5 -0
- package/dist/fix-country-codes.js.map +2 -2
- package/dist/fix-country-codes.test.js +1 -7
- package/dist/fix-country-codes.test.js.map +2 -2
- package/dist/fix-language-codes.js +5 -1
- package/dist/fix-language-codes.js.map +2 -2
- package/dist/fix-sami-041.js +109 -0
- package/dist/fix-sami-041.js.map +7 -0
- package/dist/fix-sami-041.test.js +40 -0
- package/dist/fix-sami-041.test.js.map +7 -0
- package/dist/fixRelatorTerms.js +2 -0
- package/dist/fixRelatorTerms.js.map +2 -2
- package/dist/fixRelatorTerms.test.js +1 -7
- package/dist/fixRelatorTerms.test.js.map +2 -2
- package/dist/index.js +18 -4
- package/dist/index.js.map +2 -2
- package/dist/indicator-fixes.js +15 -1
- package/dist/indicator-fixes.js.map +2 -2
- package/dist/indicator-fixes.test.js +1 -7
- package/dist/indicator-fixes.test.js.map +2 -2
- package/dist/item-language.js +41 -39
- package/dist/item-language.js.map +2 -2
- package/dist/item-language.test.js +3 -3
- package/dist/item-language.test.js.map +2 -2
- package/dist/melindaCustomMergeFields.js +31 -0
- package/dist/melindaCustomMergeFields.js.map +2 -2
- package/dist/merge-fields/controlSubfields.js.map +2 -2
- package/dist/merge-fields/counterpartField.js +149 -152
- package/dist/merge-fields/counterpartField.js.map +3 -3
- package/dist/merge-fields/dataProvenance.js +6 -20
- package/dist/merge-fields/dataProvenance.js.map +2 -2
- package/dist/merge-fields/index.js +1 -1
- package/dist/merge-fields/index.js.map +2 -2
- package/dist/merge-fields/mergableIndicator.js +1 -5
- package/dist/merge-fields/mergableIndicator.js.map +2 -2
- package/dist/merge-fields/mergableTag.js +2 -0
- package/dist/merge-fields/mergableTag.js.map +2 -2
- package/dist/merge-fields/mergeConfig.js +2 -0
- package/dist/merge-fields/mergeConfig.js.map +2 -2
- package/dist/merge-fields/mergeConstraints.js +38 -35
- package/dist/merge-fields/mergeConstraints.js.map +3 -3
- package/dist/merge-fields/mergeField.js +4 -3
- package/dist/merge-fields/mergeField.js.map +2 -2
- package/dist/merge-fields/mergeOrAddSubfield.js +8 -7
- package/dist/merge-fields/mergeOrAddSubfield.js.map +2 -2
- package/dist/merge-fields/mergeSubfield.js +5 -1
- package/dist/merge-fields/mergeSubfield.js.map +2 -2
- package/dist/merge-fields/worldKnowledge.js +52 -0
- package/dist/merge-fields/worldKnowledge.js.map +2 -2
- package/dist/merge-fields.test.js +4 -9
- package/dist/merge-fields.test.js.map +2 -2
- package/dist/mergeField500Lisapainokset.test.js +1 -7
- package/dist/mergeField500Lisapainokset.test.js.map +2 -2
- package/dist/mergeRelatorTermFields.js +9 -6
- package/dist/mergeRelatorTermFields.js.map +2 -2
- package/dist/mergeRelatorTermFields.test.js +1 -7
- package/dist/mergeRelatorTermFields.test.js.map +2 -2
- package/dist/multiple-subfield-0.test.js +1 -7
- package/dist/multiple-subfield-0.test.js.map +2 -2
- package/dist/normalize-dashes.js +9 -6
- package/dist/normalize-dashes.js.map +2 -2
- package/dist/normalize-dashes.test.js +1 -7
- package/dist/normalize-dashes.test.js.map +2 -2
- package/dist/normalize-identifiers.js.map +2 -2
- package/dist/normalize-identifiers.test.js +1 -7
- package/dist/normalize-identifiers.test.js.map +2 -2
- package/dist/normalize-qualifying-information.test.js +1 -7
- package/dist/normalize-qualifying-information.test.js.map +2 -2
- package/dist/normalize-utf8-diacritics.js.map +2 -2
- package/dist/normalize-utf8-diacritics.test.js +1 -7
- package/dist/normalize-utf8-diacritics.test.js.map +2 -2
- package/dist/normalizeFieldForComparison.js +24 -14
- package/dist/normalizeFieldForComparison.js.map +2 -2
- package/dist/normalizeSubfieldValueForComparison.js.map +1 -1
- package/dist/prepublicationUtils.js +1 -1
- package/dist/prepublicationUtils.js.map +2 -2
- package/dist/punctuation.test.js +1 -7
- package/dist/punctuation.test.js.map +2 -2
- package/dist/punctuation2.js +15 -12
- package/dist/punctuation2.js.map +2 -2
- package/dist/punctuation2.test.js +1 -7
- package/dist/punctuation2.test.js.map +2 -2
- package/dist/reindexSubfield6OccurenceNumbers.js +11 -10
- package/dist/reindexSubfield6OccurenceNumbers.js.map +2 -2
- package/dist/reindexSubfield6OccurenceNumbers.test.js +1 -7
- package/dist/reindexSubfield6OccurenceNumbers.test.js.map +2 -2
- package/dist/remove-041-zxx.js +56 -0
- package/dist/remove-041-zxx.js.map +7 -0
- package/dist/remove-041-zxx.test.js +40 -0
- package/dist/remove-041-zxx.test.js.map +7 -0
- package/dist/removeDuplicateDataFields.js +4 -26
- package/dist/removeDuplicateDataFields.js.map +2 -2
- package/dist/removeDuplicateDataFields.test.js +1 -7
- package/dist/removeDuplicateDataFields.test.js.map +2 -2
- package/dist/removeInferiorDataFields.js +82 -45
- package/dist/removeInferiorDataFields.js.map +3 -3
- package/dist/removeInferiorDataFields.test.js +5 -9
- package/dist/removeInferiorDataFields.test.js.map +2 -2
- package/dist/resolveOrphanedSubfield6s.js +4 -5
- package/dist/resolveOrphanedSubfield6s.js.map +2 -2
- package/dist/resolveOrphanedSubfield6s.test.js +1 -7
- package/dist/resolveOrphanedSubfield6s.test.js.map +2 -2
- package/dist/sanitize-vocabulary-source-codes.test.js +1 -7
- package/dist/sanitize-vocabulary-source-codes.test.js.map +2 -2
- package/dist/sortFields.js +16 -1
- package/dist/sortFields.js.map +2 -2
- package/dist/sortFields.test.js +1 -7
- package/dist/sortFields.test.js.map +2 -2
- package/dist/sortRelatorTerms.test.js +1 -7
- package/dist/sortRelatorTerms.test.js.map +2 -2
- package/dist/sortSubfields.js +27 -21
- package/dist/sortSubfields.js.map +2 -2
- package/dist/sortSubfields.test.js +1 -7
- package/dist/sortSubfields.test.js.map +2 -2
- package/dist/stripPunctuation.js +7 -3
- package/dist/stripPunctuation.js.map +2 -2
- package/dist/stripPunctuation.test.js +1 -7
- package/dist/stripPunctuation.test.js.map +2 -2
- package/dist/subfield6Utils.js +2 -12
- package/dist/subfield6Utils.js.map +2 -2
- package/dist/subfield8Utils.js +0 -5
- package/dist/subfield8Utils.js.map +2 -2
- package/dist/subfieldValueNormalizations.test.js +1 -7
- package/dist/subfieldValueNormalizations.test.js.map +2 -2
- package/dist/sync-007-and-300.test.js +1 -7
- package/dist/sync-007-and-300.test.js.map +2 -2
- package/dist/sync-language.js +103 -0
- package/dist/sync-language.js.map +7 -0
- package/dist/sync-language.test.js +40 -0
- package/dist/sync-language.test.js.map +7 -0
- package/dist/translate-terms.js +124 -87
- package/dist/translate-terms.js.map +3 -3
- package/dist/translate-terms.test.js +5 -8
- package/dist/translate-terms.test.js.map +2 -2
- package/dist/typeOfDate-008.js +3 -1
- package/dist/typeOfDate-008.js.map +2 -2
- package/dist/update-field-540.js.map +2 -2
- package/dist/update-field-540.test.js +1 -7
- package/dist/update-field-540.test.js.map +2 -2
- package/dist/urn.js +13 -12
- package/dist/urn.js.map +2 -2
- package/dist/urn.test.js +1 -7
- package/dist/urn.test.js.map +2 -2
- package/dist/utils.js +29 -3
- package/dist/utils.js.map +2 -2
- package/package.json +20 -27
- package/src/addMissingField041.js +8 -4
- package/src/addMissingField336.js +10 -5
- package/src/addMissingField337.js +9 -5
- package/src/addMissingField338.js +11 -6
- package/src/cyrillux-usemarcon-replacement.js +9 -5
- package/src/cyrillux.js +34 -23
- package/src/dataProvenanceUtils.js +21 -0
- package/src/disambiguateSeriesStatements.js +4 -1
- package/src/disambiguateSeriesStatements.test.js +3 -8
- package/src/drop-terms.js +164 -0
- package/src/drop-terms.test.js +81 -0
- package/src/field-008-18-34-character-groups.test.js +3 -8
- package/src/field-505-separators.test.js +3 -8
- package/src/field-521-fix.test.js +3 -8
- package/src/fix-33X.js +10 -6
- package/src/fix-33X.test.js +1 -1
- package/src/fix-country-codes.js +7 -3
- package/src/fix-country-codes.test.js +3 -8
- package/src/fix-language-codes.js +8 -4
- package/src/fix-sami-041.js +147 -0
- package/src/fix-sami-041.test.js +52 -0
- package/src/fixRelatorTerms.js +5 -0
- package/src/fixRelatorTerms.test.js +3 -8
- package/src/index.js +20 -7
- package/src/indicator-fixes.js +22 -7
- package/src/indicator-fixes.test.js +3 -8
- package/src/item-language.js +49 -42
- package/src/item-language.test.js +3 -3
- package/src/melindaCustomMergeFields.js +31 -0
- package/src/merge-fields/controlSubfields.js +0 -1
- package/src/merge-fields/counterpartField.js +191 -290
- package/src/merge-fields/dataProvenance.js +8 -25
- package/src/merge-fields/index.js +1 -1
- package/src/merge-fields/mergableIndicator.js +4 -5
- package/src/merge-fields/mergableTag.js +2 -0
- package/src/merge-fields/mergeConfig.js +2 -1
- package/src/merge-fields/mergeConstraints.js +41 -36
- package/src/merge-fields/mergeField.js +4 -7
- package/src/merge-fields/mergeOrAddSubfield.js +8 -7
- package/src/merge-fields/mergeSubfield.js +11 -2
- package/src/merge-fields/worldKnowledge.js +77 -3
- package/src/merge-fields.test.js +5 -10
- package/src/mergeField500Lisapainokset.test.js +3 -8
- package/src/mergeRelatorTermFields.js +12 -11
- package/src/mergeRelatorTermFields.test.js +3 -8
- package/src/multiple-subfield-0.test.js +3 -8
- package/src/normalize-dashes.js +13 -7
- package/src/normalize-dashes.test.js +3 -8
- package/src/normalize-identifiers.js +12 -19
- package/src/normalize-identifiers.test.js +3 -8
- package/src/normalize-qualifying-information.test.js +3 -8
- package/src/normalize-utf8-diacritics.js +6 -3
- package/src/normalize-utf8-diacritics.test.js +3 -8
- package/src/normalizeFieldForComparison.js +39 -22
- package/src/normalizeSubfieldValueForComparison.js +2 -2
- package/src/prepublicationUtils.js +1 -1
- package/src/punctuation.test.js +3 -8
- package/src/punctuation2.js +44 -40
- package/src/punctuation2.test.js +3 -8
- package/src/reindexSubfield6OccurenceNumbers.js +13 -11
- package/src/reindexSubfield6OccurenceNumbers.test.js +3 -8
- package/src/remove-041-zxx.js +85 -0
- package/src/remove-041-zxx.test.js +52 -0
- package/src/removeDuplicateDataFields.js +30 -28
- package/src/removeDuplicateDataFields.test.js +3 -8
- package/src/removeInferiorDataFields.js +126 -71
- package/src/removeInferiorDataFields.test.js +7 -10
- package/src/resolveOrphanedSubfield6s.js +7 -5
- package/src/resolveOrphanedSubfield6s.test.js +3 -8
- package/src/sanitize-vocabulary-source-codes.test.js +3 -8
- package/src/sortFields.js +20 -1
- package/src/sortFields.test.js +3 -8
- package/src/sortRelatorTerms.test.js +3 -8
- package/src/sortSubfields.js +31 -25
- package/src/sortSubfields.test.js +3 -8
- package/src/stripPunctuation.js +10 -5
- package/src/stripPunctuation.test.js +3 -8
- package/src/subfield6Utils.js +31 -33
- package/src/subfield8Utils.js +10 -7
- package/src/subfieldValueNormalizations.test.js +3 -8
- package/src/sync-007-and-300.test.js +3 -8
- package/src/sync-language.js +148 -0
- package/src/sync-language.test.js +52 -0
- package/src/translate-terms.js +168 -109
- package/src/translate-terms.test.js +12 -16
- package/src/typeOfDate-008.js +4 -1
- package/src/update-field-540.js +7 -5
- package/src/update-field-540.test.js +3 -8
- package/src/urn.js +17 -13
- package/src/urn.test.js +3 -8
- package/src/utils.js +39 -12
- package/test-fixtures/cyrillux/f02b/expectedResult.json +23 -0
- package/test-fixtures/cyrillux/f02b/metadata.json +11 -0
- package/test-fixtures/cyrillux/f02b/record.json +14 -0
- package/test-fixtures/cyrillux/f14/expectedResult.json +32 -0
- package/test-fixtures/cyrillux/f14/metadata.json +10 -0
- package/test-fixtures/cyrillux/f14/record.json +14 -0
- package/test-fixtures/drop-terms/01/expectedResult.json +31 -0
- package/test-fixtures/drop-terms/01/metadata.json +6 -0
- package/test-fixtures/drop-terms/01/record.json +35 -0
- package/test-fixtures/drop-terms/02/expectedResult.json +7 -0
- package/test-fixtures/drop-terms/02/metadata.json +6 -0
- package/test-fixtures/drop-terms/02/record.json +40 -0
- package/test-fixtures/drop-terms/03/expectedResult.json +6 -0
- package/test-fixtures/drop-terms/03/metadata.json +18 -0
- package/test-fixtures/drop-terms/03/record.json +39 -0
- package/test-fixtures/drop-terms/04/expectedResult.json +6 -0
- package/test-fixtures/drop-terms/04/metadata.json +19 -0
- package/test-fixtures/drop-terms/04/record.json +24 -0
- package/test-fixtures/fix-language-codes/02/metadata.json +1 -1
- package/test-fixtures/fix-relator-terms/f02/metadata.json +1 -1
- package/test-fixtures/fix-relator-terms/f04/expectedResult.json +18 -0
- package/test-fixtures/fix-relator-terms/f04/metadata.json +5 -0
- package/test-fixtures/fix-relator-terms/f04/record.json +20 -0
- package/test-fixtures/fix-sami-041/01/expectedResult.json +6 -0
- package/test-fixtures/fix-sami-041/01/metadata.json +4 -0
- package/test-fixtures/fix-sami-041/01/record.json +14 -0
- package/test-fixtures/fix-sami-041/02/expectedResult.json +10 -0
- package/test-fixtures/fix-sami-041/02/metadata.json +4 -0
- package/test-fixtures/fix-sami-041/02/record.json +8 -0
- package/test-fixtures/fix-sami-041/03/expectedResult.json +5 -0
- package/test-fixtures/fix-sami-041/03/metadata.json +5 -0
- package/test-fixtures/fix-sami-041/03/record.json +8 -0
- package/test-fixtures/fix-sami-041/04/expectedResult.json +7 -0
- package/test-fixtures/fix-sami-041/04/metadata.json +4 -0
- package/test-fixtures/fix-sami-041/04/record.json +10 -0
- package/test-fixtures/fix-sami-041/05/expectedResult.json +10 -0
- package/test-fixtures/fix-sami-041/05/metadata.json +6 -0
- package/test-fixtures/fix-sami-041/05/record.json +8 -0
- package/test-fixtures/fix-sami-041/06/expectedResult.json +10 -0
- package/test-fixtures/fix-sami-041/06/metadata.json +4 -0
- package/test-fixtures/fix-sami-041/06/record.json +9 -0
- package/test-fixtures/indicator-fixes/11/expectedResult.json +10 -0
- package/test-fixtures/indicator-fixes/11/metadata.json +4 -0
- package/test-fixtures/indicator-fixes/11/record.json +10 -0
- package/test-fixtures/merge-fields/f042_01/expectedResult.json +12 -0
- package/test-fixtures/merge-fields/f042_01/metadata.json +6 -0
- package/test-fixtures/merge-fields/f042_01/record.json +13 -0
- package/test-fixtures/merge-fields/f05/metadata.json +1 -1
- package/test-fixtures/merge-fields/f06/expectedResult.json +42 -0
- package/test-fixtures/merge-fields/f06/metadata.json +6 -0
- package/test-fixtures/merge-fields/f06/record.json +41 -0
- package/test-fixtures/merge-fields/f07/expectedResult.json +18 -0
- package/test-fixtures/merge-fields/f07/metadata.json +6 -0
- package/test-fixtures/merge-fields/f07/record.json +18 -0
- package/test-fixtures/merge-fields/f08/expectedResult.json +12 -0
- package/test-fixtures/merge-fields/f08/metadata.json +7 -0
- package/test-fixtures/merge-fields/f08/record.json +10 -0
- package/test-fixtures/merge-fields/f09/expectedResult.json +14 -0
- package/test-fixtures/merge-fields/f09/metadata.json +6 -0
- package/test-fixtures/merge-fields/f09/record.json +14 -0
- package/test-fixtures/merge-fields/f10/expectedResult.json +25 -0
- package/test-fixtures/merge-fields/f10/metadata.json +6 -0
- package/test-fixtures/merge-fields/f10/record.json +25 -0
- package/test-fixtures/merge-fields/f11/expectedResult.json +40 -0
- package/test-fixtures/merge-fields/f11/metadata.json +7 -0
- package/test-fixtures/merge-fields/f11/record.json +50 -0
- package/test-fixtures/merge-fields/f12/expectedResult.json +17 -0
- package/test-fixtures/merge-fields/f12/metadata.json +6 -0
- package/test-fixtures/merge-fields/f12/record.json +25 -0
- package/test-fixtures/merge-fields/f13/expectedResult.json +18 -0
- package/test-fixtures/merge-fields/f13/metadata.json +6 -0
- package/test-fixtures/merge-fields/f13/record.json +28 -0
- package/test-fixtures/merge-fields/f14/expectedResult.json +25 -0
- package/test-fixtures/merge-fields/f14/metadata.json +6 -0
- package/test-fixtures/merge-fields/f14/record.json +25 -0
- package/test-fixtures/merge-fields/f15/expectedResult.json +8 -0
- package/test-fixtures/merge-fields/f15/metadata.json +6 -0
- package/test-fixtures/merge-fields/f15/record.json +9 -0
- package/test-fixtures/merge-fields/f16/expectedResult.json +12 -0
- package/test-fixtures/merge-fields/f16/metadata.json +7 -0
- package/test-fixtures/merge-fields/f16/record.json +13 -0
- package/test-fixtures/merge-fields/f300_01/expectedResult.json +9 -0
- package/test-fixtures/merge-fields/f300_01/metadata.json +6 -0
- package/test-fixtures/merge-fields/f300_01/record.json +8 -0
- package/test-fixtures/merge-fields/f300_02/expectedResult.json +13 -0
- package/test-fixtures/merge-fields/f300_02/metadata.json +6 -0
- package/test-fixtures/merge-fields/f300_02/record.json +16 -0
- package/test-fixtures/merge-fields/f300_03/expectedResult.json +13 -0
- package/test-fixtures/merge-fields/f300_03/metadata.json +6 -0
- package/test-fixtures/merge-fields/f300_03/record.json +16 -0
- package/test-fixtures/merge-fields/f490_01/expectedResult.json +13 -0
- package/test-fixtures/merge-fields/f490_01/metadata.json +6 -0
- package/test-fixtures/merge-fields/f490_01/record.json +16 -0
- package/test-fixtures/remove-041-zxx/01/expectedResult.json +5 -0
- package/test-fixtures/remove-041-zxx/01/metadata.json +5 -0
- package/test-fixtures/remove-041-zxx/01/record.json +10 -0
- package/test-fixtures/remove-041-zxx/02/expectedResult.json +7 -0
- package/test-fixtures/remove-041-zxx/02/metadata.json +5 -0
- package/test-fixtures/remove-041-zxx/02/record.json +9 -0
- package/test-fixtures/remove-041-zxx/11/expectedResult.json +10 -0
- package/test-fixtures/remove-041-zxx/11/metadata.json +5 -0
- package/test-fixtures/remove-041-zxx/11/record.json +9 -0
- package/test-fixtures/remove-041-zxx/12/expectedResult.json +10 -0
- package/test-fixtures/remove-041-zxx/12/metadata.json +5 -0
- package/test-fixtures/remove-041-zxx/12/record.json +9 -0
- package/test-fixtures/remove-inferior-datafields/f17/expectedResult.json +11 -0
- package/test-fixtures/remove-inferior-datafields/f17/metadata.json +5 -0
- package/test-fixtures/remove-inferior-datafields/f17/record.json +15 -0
- package/test-fixtures/remove-inferior-datafields/f18/expectedResult.json +13 -0
- package/test-fixtures/remove-inferior-datafields/f18/metadata.json +5 -0
- package/test-fixtures/remove-inferior-datafields/f18/record.json +18 -0
- package/test-fixtures/remove-inferior-datafields/f19/expectedResult.json +17 -0
- package/test-fixtures/remove-inferior-datafields/f19/metadata.json +5 -0
- package/test-fixtures/remove-inferior-datafields/f19/record.json +18 -0
- package/test-fixtures/remove-inferior-datafields/f20/expectedResult.json +15 -0
- package/test-fixtures/remove-inferior-datafields/f20/metadata.json +5 -0
- package/test-fixtures/remove-inferior-datafields/f20/record.json +29 -0
- package/test-fixtures/sort-fields/15/input.json +9 -0
- package/test-fixtures/sort-fields/15/metadata.json +5 -0
- package/test-fixtures/sort-fields/15/result.json +10 -0
- package/test-fixtures/sync-language/01/expectedResult.json +5 -0
- package/test-fixtures/sync-language/01/metadata.json +5 -0
- package/test-fixtures/sync-language/01/record.json +7 -0
- package/test-fixtures/sync-language/02/expectedResult.json +6 -0
- package/test-fixtures/sync-language/02/metadata.json +5 -0
- package/test-fixtures/sync-language/02/record.json +10 -0
- package/test-fixtures/sync-language/03/expectedResult.json +6 -0
- package/test-fixtures/sync-language/03/metadata.json +5 -0
- package/test-fixtures/sync-language/03/record.json +6 -0
- package/test-fixtures/sync-language/10/expectedResult.json +10 -0
- package/test-fixtures/sync-language/10/metadata.json +5 -0
- package/test-fixtures/sync-language/10/record.json +8 -0
- package/test-fixtures/sync-language/11/expectedResult.json +10 -0
- package/test-fixtures/sync-language/11/metadata.json +5 -0
- package/test-fixtures/sync-language/11/record.json +7 -0
- package/test-fixtures/sync-language/12/expectedResult.json +9 -0
- package/test-fixtures/sync-language/12/metadata.json +6 -0
- package/test-fixtures/sync-language/12/record.json +6 -0
- package/test-fixtures/sync-language/13/expectedResult.json +10 -0
- package/test-fixtures/sync-language/13/metadata.json +5 -0
- package/test-fixtures/sync-language/13/record.json +8 -0
- package/test-fixtures/sync-language/14/expectedResult.json +9 -0
- package/test-fixtures/sync-language/14/metadata.json +5 -0
- package/test-fixtures/sync-language/14/record.json +7 -0
- package/test-fixtures/sync-language/15/expectedResult.json +9 -0
- package/test-fixtures/sync-language/15/metadata.json +5 -0
- package/test-fixtures/sync-language/15/record.json +7 -0
- package/test-fixtures/translate-terms/05/expectedResult.json +12 -0
- package/test-fixtures/translate-terms/05/metadata.json +7 -0
- package/test-fixtures/translate-terms/05/record.json +11 -0
- package/test-fixtures/translate-terms/06/expectedResult.json +12 -0
- package/test-fixtures/translate-terms/06/metadata.json +7 -0
- package/test-fixtures/translate-terms/06/record.json +11 -0
- package/test-fixtures/translate-terms-data.js +23 -0
package/dist/normalize-dashes.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import clone from "clone";
|
|
2
|
-
import { fieldToString,
|
|
2
|
+
import { fieldToString, isContentSubfieldCode, nvdebug } from "./utils.js";
|
|
3
|
+
import createDebugLogger from "debug";
|
|
4
|
+
const debug = createDebugLogger("@natlibfi/marc-record-validators-melinda:normaliza-dashes");
|
|
5
|
+
const debugDev = debug.extend("dev");
|
|
3
6
|
export default function() {
|
|
4
7
|
return {
|
|
5
8
|
description: 'Normalize various dashes to "-"',
|
|
@@ -7,7 +10,7 @@ export default function() {
|
|
|
7
10
|
fix
|
|
8
11
|
};
|
|
9
12
|
function fix(record) {
|
|
10
|
-
nvdebug(`FIX ME
|
|
13
|
+
nvdebug(`FIX ME`, debugDev);
|
|
11
14
|
record.fields.forEach((field) => {
|
|
12
15
|
fixDashes(field);
|
|
13
16
|
});
|
|
@@ -16,7 +19,7 @@ export default function() {
|
|
|
16
19
|
}
|
|
17
20
|
function validate(record) {
|
|
18
21
|
const res = { message: [] };
|
|
19
|
-
nvdebug(`VALIDATE ME
|
|
22
|
+
nvdebug(`VALIDATE ME`, debugDev);
|
|
20
23
|
record.fields?.forEach((field) => {
|
|
21
24
|
validateField(field, res);
|
|
22
25
|
});
|
|
@@ -25,7 +28,7 @@ export default function() {
|
|
|
25
28
|
}
|
|
26
29
|
function validateField(field, res) {
|
|
27
30
|
const orig = fieldToString(field);
|
|
28
|
-
nvdebug(` VALIDATE FIELD '${orig}'
|
|
31
|
+
nvdebug(` VALIDATE FIELD '${orig}'`, debugDev);
|
|
29
32
|
const normalizedField = fixDashes(clone(field));
|
|
30
33
|
const mod = fieldToString(normalizedField);
|
|
31
34
|
if (orig === mod) {
|
|
@@ -39,11 +42,11 @@ function fixDashes(field) {
|
|
|
39
42
|
if (!field.subfields) {
|
|
40
43
|
return field;
|
|
41
44
|
}
|
|
42
|
-
nvdebug(`Dashing ${fieldToString(field)}
|
|
45
|
+
nvdebug(`Dashing ${fieldToString(field)}`, debugDev);
|
|
43
46
|
field.subfields.forEach((sf) => subfieldFixDashes(sf));
|
|
44
47
|
return field;
|
|
45
48
|
function subfieldFixDashes(subfield) {
|
|
46
|
-
if (
|
|
49
|
+
if (!isContentSubfieldCode(subfield.code, field.tag)) {
|
|
47
50
|
return;
|
|
48
51
|
}
|
|
49
52
|
subfield.value = subfield.value.replace(/[\u2010\u2011\u2012\u2013\u2014\u2015]/ug, "-");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/normalize-dashes.js"],
|
|
4
|
-
"sourcesContent": ["
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import clone from 'clone';\nimport {fieldToString, isContentSubfieldCode, nvdebug} from './utils.js';\n\nimport createDebugLogger from 'debug';\n\nconst debug = createDebugLogger('@natlibfi/marc-record-validators-melinda:normaliza-dashes');\n//const debugData = debug.extend('data');\nconst debugDev = debug.extend('dev');\n\n\n// Author(s): Nicholas Volk\nexport default function () {\n\n return {\n description: 'Normalize various dashes to \"-\"',\n validate, fix\n };\n\n function fix(record) {\n nvdebug(`FIX ME`, debugDev);\n record.fields.forEach(field => {\n fixDashes(field);\n });\n\n const res = {message: [], fix: [], valid: true};\n return res;\n }\n\n function validate(record) {\n const res = {message: []};\n\n nvdebug(`VALIDATE ME`, debugDev);\n record.fields?.forEach(field => {\n validateField(field, res);\n });\n\n res.valid = !(res.message.length >= 1);\n return res;\n }\n\n function validateField(field, res) {\n const orig = fieldToString(field);\n nvdebug(` VALIDATE FIELD '${orig}'`, debugDev);\n\n const normalizedField = fixDashes(clone(field));\n const mod = fieldToString(normalizedField);\n if (orig === mod) { // Fail as the input is \"broken\"/\"crap\"/sumthing\n return;\n }\n res.message.push(`'TODO: ${orig}' => '${mod}'`);\n return;\n }\n}\n\n\nfunction fixDashes(field) {\n if (!field.subfields) {\n return field;\n }\n\n nvdebug(`Dashing ${fieldToString(field)}`, debugDev);\n\n field.subfields.forEach(sf => subfieldFixDashes(sf));\n\n return field;\n\n function subfieldFixDashes(subfield) {\n if (!isContentSubfieldCode(subfield.code, field.tag)) {\n return;\n }\n // Normalize dashes U+2010 ... U+2015 to '-':\n subfield.value = subfield.value.replace(/[\\u2010\\u2011\\u2012\\u2013\\u2014\\u2015]/ug, '-');\n }\n\n}\n"],
|
|
5
|
+
"mappings": "AAAA,OAAO,WAAW;AAClB,SAAQ,eAAe,uBAAuB,eAAc;AAE5D,OAAO,uBAAuB;AAE9B,MAAM,QAAQ,kBAAkB,2DAA2D;AAE3F,MAAM,WAAW,MAAM,OAAO,KAAK;AAInC,0BAA2B;AAEzB,SAAO;AAAA,IACL,aAAa;AAAA,IACb;AAAA,IAAU;AAAA,EACZ;AAEA,WAAS,IAAI,QAAQ;AACnB,YAAQ,UAAU,QAAQ;AAC1B,WAAO,OAAO,QAAQ,WAAS;AAC7B,gBAAU,KAAK;AAAA,IACjB,CAAC;AAED,UAAM,MAAM,EAAC,SAAS,CAAC,GAAG,KAAK,CAAC,GAAG,OAAO,KAAI;AAC9C,WAAO;AAAA,EACT;AAEA,WAAS,SAAS,QAAQ;AACxB,UAAM,MAAM,EAAC,SAAS,CAAC,EAAC;AAExB,YAAQ,eAAe,QAAQ;AAC/B,WAAO,QAAQ,QAAQ,WAAS;AAC9B,oBAAc,OAAO,GAAG;AAAA,IAC1B,CAAC;AAED,QAAI,QAAQ,EAAE,IAAI,QAAQ,UAAU;AACpC,WAAO;AAAA,EACT;AAEA,WAAS,cAAc,OAAO,KAAK;AACjC,UAAM,OAAO,cAAc,KAAK;AAChC,YAAQ,oBAAoB,IAAI,KAAK,QAAQ;AAE7C,UAAM,kBAAkB,UAAU,MAAM,KAAK,CAAC;AAC9C,UAAM,MAAM,cAAc,eAAe;AACzC,QAAI,SAAS,KAAK;AAChB;AAAA,IACF;AACA,QAAI,QAAQ,KAAK,UAAU,IAAI,SAAS,GAAG,GAAG;AAC9C;AAAA,EACF;AACF;AAGA,SAAS,UAAU,OAAO;AACxB,MAAI,CAAC,MAAM,WAAW;AACpB,WAAO;AAAA,EACT;AAEA,UAAQ,WAAW,cAAc,KAAK,CAAC,IAAI,QAAQ;AAEnD,QAAM,UAAU,QAAQ,QAAM,kBAAkB,EAAE,CAAC;AAEnD,SAAO;AAEP,WAAS,kBAAkB,UAAU;AACnC,QAAI,CAAC,sBAAsB,SAAS,MAAM,MAAM,GAAG,GAAG;AACpD;AAAA,IACF;AAEA,aAAS,QAAQ,SAAS,MAAM,QAAQ,4CAA4C,GAAG;AAAA,EACzF;AAEF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -3,7 +3,6 @@ import { MarcRecord } from "@natlibfi/marc-record";
|
|
|
3
3
|
import validatorFactory from "./normalize-dashes.js";
|
|
4
4
|
import { READERS } from "@natlibfi/fixura";
|
|
5
5
|
import generateTests from "@natlibfi/fixugen";
|
|
6
|
-
import createDebugLogger from "debug";
|
|
7
6
|
generateTests({
|
|
8
7
|
callback,
|
|
9
8
|
path: [import.meta.dirname, "..", "test-fixtures", "normalize-dashes"],
|
|
@@ -18,18 +17,13 @@ generateTests({
|
|
|
18
17
|
}
|
|
19
18
|
}
|
|
20
19
|
});
|
|
21
|
-
const debug = createDebugLogger("@natlibfi/marc-record-validators-melinda/normalize-dashes:test");
|
|
22
20
|
async function testValidatorFactory() {
|
|
23
21
|
const validator = await validatorFactory();
|
|
24
22
|
assert.equal(typeof validator, "object");
|
|
25
23
|
assert.equal(typeof validator.description, "string");
|
|
26
24
|
assert.equal(typeof validator.validate, "function");
|
|
27
25
|
}
|
|
28
|
-
async function callback({ getFixture,
|
|
29
|
-
if (enabled === false) {
|
|
30
|
-
debug("TEST SKIPPED!");
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
26
|
+
async function callback({ getFixture, fix = false }) {
|
|
33
27
|
const validator = await validatorFactory();
|
|
34
28
|
const record = new MarcRecord(getFixture("record.json"));
|
|
35
29
|
const expectedResult = getFixture("expectedResult.json");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/normalize-dashes.test.js"],
|
|
4
|
-
"sourcesContent": ["import assert from 'node:assert';\nimport {MarcRecord} from '@natlibfi/marc-record';\nimport validatorFactory from './normalize-dashes.js';\nimport {READERS} from '@natlibfi/fixura';\nimport generateTests from '@natlibfi/fixugen';\
|
|
5
|
-
"mappings": "AAAA,OAAO,YAAY;AACnB,SAAQ,kBAAiB;AACzB,OAAO,sBAAsB;AAC7B,SAAQ,eAAc;AACtB,OAAO,mBAAmB;
|
|
4
|
+
"sourcesContent": ["import assert from 'node:assert';\nimport {MarcRecord} from '@natlibfi/marc-record';\nimport validatorFactory from './normalize-dashes.js';\nimport {READERS} from '@natlibfi/fixura';\nimport generateTests from '@natlibfi/fixugen';\n//import createDebugLogger from 'debug';\n\ngenerateTests({\n callback,\n path: [import.meta.dirname, '..', 'test-fixtures', 'normalize-dashes'],\n useMetadataFile: true,\n recurse: false,\n fixura: {\n reader: READERS.JSON\n },\n hooks: {\n before: async () => {\n testValidatorFactory();\n }\n }\n});\n//const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda/normalize-dashes:test');\n\nasync function testValidatorFactory() {\n const validator = await validatorFactory();\n\n assert.equal(typeof validator, 'object');\n assert.equal(typeof validator.description, 'string');\n assert.equal(typeof validator.validate, 'function');\n}\n\nasync function callback({getFixture, fix = false}) {\n const validator = await validatorFactory();\n const record = new MarcRecord(getFixture('record.json'));\n const expectedResult = getFixture('expectedResult.json');\n // console.log(expectedResult); // eslint-disable-line\n\n if (!fix) {\n const result = await validator.validate(record);\n assert.deepEqual(result, expectedResult);\n return;\n }\n\n await validator.fix(record);\n assert.deepEqual(record, expectedResult);\n}\n"],
|
|
5
|
+
"mappings": "AAAA,OAAO,YAAY;AACnB,SAAQ,kBAAiB;AACzB,OAAO,sBAAsB;AAC7B,SAAQ,eAAc;AACtB,OAAO,mBAAmB;AAG1B,cAAc;AAAA,EACZ;AAAA,EACA,MAAM,CAAC,YAAY,SAAS,MAAM,iBAAiB,kBAAkB;AAAA,EACrE,iBAAiB;AAAA,EACjB,SAAS;AAAA,EACT,QAAQ;AAAA,IACN,QAAQ,QAAQ;AAAA,EAClB;AAAA,EACA,OAAO;AAAA,IACL,QAAQ,YAAY;AAClB,2BAAqB;AAAA,IACvB;AAAA,EACF;AACF,CAAC;AAGD,eAAe,uBAAuB;AACpC,QAAM,YAAY,MAAM,iBAAiB;AAEzC,SAAO,MAAM,OAAO,WAAW,QAAQ;AACvC,SAAO,MAAM,OAAO,UAAU,aAAa,QAAQ;AACnD,SAAO,MAAM,OAAO,UAAU,UAAU,UAAU;AACpD;AAEA,eAAe,SAAS,EAAC,YAAY,MAAM,MAAK,GAAG;AACjD,QAAM,YAAY,MAAM,iBAAiB;AACzC,QAAM,SAAS,IAAI,WAAW,WAAW,aAAa,CAAC;AACvD,QAAM,iBAAiB,WAAW,qBAAqB;AAGvD,MAAI,CAAC,KAAK;AACR,UAAM,SAAS,MAAM,UAAU,SAAS,MAAM;AAC9C,WAAO,UAAU,QAAQ,cAAc;AACvC;AAAA,EACF;AAEA,QAAM,UAAU,IAAI,MAAM;AAC1B,SAAO,UAAU,QAAQ,cAAc;AACzC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/normalize-identifiers.js"],
|
|
4
|
-
"sourcesContent": ["// Relocated from melinda-marc-record-merge-reducers (and renamed)\
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["// Relocated from melinda-marc-record-merge-reducers (and renamed)\nimport clone from 'clone';\nimport {fieldToString, /*nvdebug*/} from './utils.js';\n\n//const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda:normalize-identifiers');\n//const debugData = debug.extend('data');\n//const debugDev = debug.extend('dev');\n\nexport default function () {\n\n // NB! We should and could handle ISNIs here as well.\n return {\n description: 'Normalizes identifiers such as subfield $0 values',\n validate, fix\n };\n\n function fix(record) {\n const res = {message: [], fix: [], valid: true};\n //message.fix = [];\n\n // Actual parsing of all fields\n /*\n if (!record.fields) {\n return false;\n }\n */\n\n //nvdebug(`NORMALIZE CONTROL NUMBER FIX`, debugDev);\n record.fields.forEach(field => {\n //nvdebug(` NORMALIZE CONTROL NUMBER FIX ${fieldToString(field)}`, debugDev);\n\n fieldNormalizeControlNumbers(field);\n //validateField(field, true, message);\n });\n\n // message.valid = !(message.message.length >= 1);\n return res;\n }\n\n function validate(record) {\n const res = {message: []};\n //nvdebug(`NORMALIZE CONTROL NUMBER VALIDATE`, debugDev);\n // Actual parsing of all fields\n /*\n if (!record.fields) {\n return false;\n }\n */\n\n record.fields.forEach(field => {\n //nvdebug(` NORMALIZE CONTROL NUMBER VALIDATE ${fieldToString(field)}`, debugDev);\n validateField(field, res);\n });\n\n res.valid = !(res.message.length >= 1);\n return res;\n }\n\n function validateField(field, res) {\n if (!field.subfields) {\n return;\n }\n\n const normalizedField = clone(field);\n fieldNormalizeControlNumbers(normalizedField);\n\n const orig = fieldToString(field);\n const mod = fieldToString(normalizedField);\n if (orig !== mod) { // Fail as the input is \"broken\"/\"crap\"/sumthing\n res.message.push(`'${orig}' could do with control number identifier normalization`);\n return;\n }\n\n return;\n }\n}\n\nconst alephInternal01 = '(FIN01)';\nconst alephInternal10 = '(FIN10)';\nconst alephInternal11 = '(FIN11)';\nconst alephInternal12 = '(FIN12)';\nconst alephInternal13 = '(FIN13)';\n\nconst standard01 = '(FI-MELINDA)';\nconst standard10 = '(FI-ASTERI-S)';\nconst standard11 = '(FI-ASTERI-N)';\nconst standard12 = '(FI-ASTERI-A)';\nconst standard13 = '(FI-ASTERI-W)';\n\nconst both01 = {ALEPH_INTERNAL: alephInternal01, STANDARD: standard01};\nconst both10 = {ALEPH_INTERNAL: alephInternal10, STANDARD: standard10};\nconst both11 = {ALEPH_INTERNAL: alephInternal11, STANDARD: standard11};\nconst both12 = {ALEPH_INTERNAL: alephInternal12, STANDARD: standard12};\nconst both13 = {ALEPH_INTERNAL: alephInternal13, STANDARD: standard13};\n\nconst mappings = {\n 'FCC': both01,\n '(FI-ASTERI-A)': both12,\n '(FI-ASTERI-N)': both11,\n '(FI-ASTERI-S)': both10,\n '(FI-ASTERI-W)': both13,\n '(FI-MELINDA)': both01,\n '(FIN01)': both01,\n '(FIN10)': both10,\n '(FIN11)': both11,\n '(FIN12)': both12,\n '(FIN13)': both13,\n 'http://urn.fi/URN:NBN:fi:au:finaf:': both11,\n 'https://urn.fi/URN:NBN:fi:au:finaf:': both11\n};\n\nfunction normalizeNineDigitIDs(value, targetFormat = 'ALEPH_INTERNAL') {\n // $value should be prefix + nine-digits. Do nothing if nine-digit tail condition is not met:\n const nineDigitTail = value.slice(-9);\n if (!(/^[0-9]{9}$/u).test(nineDigitTail)) {\n return value;\n }\n // Normalize prefix:\n const currPrefix = value.slice(0, -9);\n\n if (currPrefix in mappings) {\n //nvdebug(`${currPrefix}, TF:${targetFormat}...`, debugDev);\n //nvdebug(`${JSON.stringify(mappings[currPrefix])}`, debugDev);\n return `${mappings[currPrefix][targetFormat]}${nineDigitTail}`;\n }\n return value;\n}\n\nexport function isIsni(value) {\n if ((/^\\(isni\\)(?: ?[0-9]{4}){4}$/u).test(value)) {\n return true;\n }\n if ((/^https:\\/\\/isni.org\\/isni\\/[0-9]{16}$/u).test(value)) {\n return true;\n }\n return false;\n}\n\nexport function normalizeIsni(value) {\n if (isIsni(value)) {\n return `https://isni.org/isni/${value.replace(/[^0-9]/gu, '')}`;\n }\n return value;\n}\n\n\nexport function normalizeControlSubfieldValue(value = '', targetFormat = 'ALEPH_INTERNAL') {\n if (isIsni(value)) {\n return normalizeIsni(value);\n }\n\n const normalizedValue = normalizeNineDigitIDs(value, targetFormat);\n if (normalizedValue !== value) {\n return normalizedValue;\n }\n\n // Something for isni IDs?\n return value;\n}\n\n//export function normalizableSubfieldPrefix(tag, sf) {\nexport function normalizeAs(tag, subfieldCode) {\n //nvdebug(`nAs ${tag}, ${subfieldCode}`, debugDev);\n if (subfieldCode === '0' || subfieldCode === '1' || subfieldCode === 'w') {\n return 'ALEPH_INTERNAL';\n }\n\n if (tag === '035' && ['a', 'z'].includes(subfieldCode)) {\n return 'STANDARD';\n }\n return undefined;\n}\n\nexport function fieldNormalizeControlNumbers(field) {\n // Rename \"Prefixes\" as \"ControlNumberIdentifiers\"?\n // No, since isni etc... however, just \"ControlNumber\" would do...\n // \"identifiers\" ?\n if (!field.subfields) {\n return;\n }\n\n field.subfields.forEach(sf => {\n const targetFormat = normalizeAs(field.tag, sf.code);\n if (targetFormat !== undefined) {\n //nvdebug(`NORMALIZE SUBFIELD $${sf.code} IN FIELD: '${fieldToString(field)}' TO ${targetFormat}`, debugDev);\n sf.value = normalizeControlSubfieldValue(sf.value, targetFormat);\n return;\n }\n });\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -3,7 +3,6 @@ import { MarcRecord } from "@natlibfi/marc-record";
|
|
|
3
3
|
import validatorFactory from "./normalize-identifiers.js";
|
|
4
4
|
import { READERS } from "@natlibfi/fixura";
|
|
5
5
|
import generateTests from "@natlibfi/fixugen";
|
|
6
|
-
import createDebugLogger from "debug";
|
|
7
6
|
generateTests({
|
|
8
7
|
callback,
|
|
9
8
|
path: [import.meta.dirname, "..", "test-fixtures", "normalize-identifiers"],
|
|
@@ -18,18 +17,13 @@ generateTests({
|
|
|
18
17
|
}
|
|
19
18
|
}
|
|
20
19
|
});
|
|
21
|
-
const debug = createDebugLogger("@natlibfi/marc-record-validators-melinda/normalize-identifiers:test");
|
|
22
20
|
async function testValidatorFactory() {
|
|
23
21
|
const validator = await validatorFactory();
|
|
24
22
|
assert.equal(typeof validator, "object");
|
|
25
23
|
assert.equal(typeof validator.description, "string");
|
|
26
24
|
assert.equal(typeof validator.validate, "function");
|
|
27
25
|
}
|
|
28
|
-
async function callback({ getFixture,
|
|
29
|
-
if (enabled === false) {
|
|
30
|
-
debug("TEST SKIPPED!");
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
26
|
+
async function callback({ getFixture, fix = false }) {
|
|
33
27
|
const validator = await validatorFactory();
|
|
34
28
|
const record = new MarcRecord(getFixture("record.json"));
|
|
35
29
|
const expectedResult = getFixture("expectedResult.json");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/normalize-identifiers.test.js"],
|
|
4
|
-
"sourcesContent": ["import assert from 'node:assert';\nimport {MarcRecord} from '@natlibfi/marc-record';\nimport validatorFactory from './normalize-identifiers.js';\nimport {READERS} from '@natlibfi/fixura';\nimport generateTests from '@natlibfi/fixugen';\
|
|
5
|
-
"mappings": "AAAA,OAAO,YAAY;AACnB,SAAQ,kBAAiB;AACzB,OAAO,sBAAsB;AAC7B,SAAQ,eAAc;AACtB,OAAO,mBAAmB;
|
|
4
|
+
"sourcesContent": ["import assert from 'node:assert';\nimport {MarcRecord} from '@natlibfi/marc-record';\nimport validatorFactory from './normalize-identifiers.js';\nimport {READERS} from '@natlibfi/fixura';\nimport generateTests from '@natlibfi/fixugen';\n//import createDebugLogger from 'debug';\n\ngenerateTests({\n callback,\n path: [import.meta.dirname, '..', 'test-fixtures', 'normalize-identifiers'],\n useMetadataFile: true,\n recurse: false,\n fixura: {\n reader: READERS.JSON\n },\n hooks: {\n before: async () => {\n testValidatorFactory();\n }\n }\n});\n//const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda/normalize-identifiers:test');\n\nasync function testValidatorFactory() {\n const validator = await validatorFactory();\n\n assert.equal(typeof validator, 'object');\n assert.equal(typeof validator.description, 'string');\n assert.equal(typeof validator.validate, 'function');\n}\n\nasync function callback({getFixture, fix = false}) {\n const validator = await validatorFactory();\n const record = new MarcRecord(getFixture('record.json'));\n const expectedResult = getFixture('expectedResult.json');\n // console.log(expectedResult); // eslint-disable-line\n\n if (!fix) {\n const result = await validator.validate(record);\n assert.deepEqual(result, expectedResult);\n return;\n }\n\n await validator.fix(record);\n assert.deepEqual(record, expectedResult);\n}\n"],
|
|
5
|
+
"mappings": "AAAA,OAAO,YAAY;AACnB,SAAQ,kBAAiB;AACzB,OAAO,sBAAsB;AAC7B,SAAQ,eAAc;AACtB,OAAO,mBAAmB;AAG1B,cAAc;AAAA,EACZ;AAAA,EACA,MAAM,CAAC,YAAY,SAAS,MAAM,iBAAiB,uBAAuB;AAAA,EAC1E,iBAAiB;AAAA,EACjB,SAAS;AAAA,EACT,QAAQ;AAAA,IACN,QAAQ,QAAQ;AAAA,EAClB;AAAA,EACA,OAAO;AAAA,IACL,QAAQ,YAAY;AAClB,2BAAqB;AAAA,IACvB;AAAA,EACF;AACF,CAAC;AAGD,eAAe,uBAAuB;AACpC,QAAM,YAAY,MAAM,iBAAiB;AAEzC,SAAO,MAAM,OAAO,WAAW,QAAQ;AACvC,SAAO,MAAM,OAAO,UAAU,aAAa,QAAQ;AACnD,SAAO,MAAM,OAAO,UAAU,UAAU,UAAU;AACpD;AAEA,eAAe,SAAS,EAAC,YAAY,MAAM,MAAK,GAAG;AACjD,QAAM,YAAY,MAAM,iBAAiB;AACzC,QAAM,SAAS,IAAI,WAAW,WAAW,aAAa,CAAC;AACvD,QAAM,iBAAiB,WAAW,qBAAqB;AAGvD,MAAI,CAAC,KAAK;AACR,UAAM,SAAS,MAAM,UAAU,SAAS,MAAM;AAC9C,WAAO,UAAU,QAAQ,cAAc;AACvC;AAAA,EACF;AAEA,QAAM,UAAU,IAAI,MAAM;AAC1B,SAAO,UAAU,QAAQ,cAAc;AACzC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -3,7 +3,6 @@ import { MarcRecord } from "@natlibfi/marc-record";
|
|
|
3
3
|
import validatorFactory from "./normalize-qualifying-information.js";
|
|
4
4
|
import { READERS } from "@natlibfi/fixura";
|
|
5
5
|
import generateTests from "@natlibfi/fixugen";
|
|
6
|
-
import createDebugLogger from "debug";
|
|
7
6
|
generateTests({
|
|
8
7
|
callback,
|
|
9
8
|
path: [import.meta.dirname, "..", "test-fixtures", "normalize-qualifying-information"],
|
|
@@ -18,18 +17,13 @@ generateTests({
|
|
|
18
17
|
}
|
|
19
18
|
}
|
|
20
19
|
});
|
|
21
|
-
const debug = createDebugLogger("@natlibfi/marc-record-validators-melinda/normalize-qualifying-information:test");
|
|
22
20
|
async function testValidatorFactory() {
|
|
23
21
|
const validator = await validatorFactory();
|
|
24
22
|
assert.equal(typeof validator, "object");
|
|
25
23
|
assert.equal(typeof validator.description, "string");
|
|
26
24
|
assert.equal(typeof validator.validate, "function");
|
|
27
25
|
}
|
|
28
|
-
async function callback({ getFixture,
|
|
29
|
-
if (enabled === false) {
|
|
30
|
-
debug("TEST SKIPPED!");
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
26
|
+
async function callback({ getFixture, fix = false }) {
|
|
33
27
|
const validator = await validatorFactory();
|
|
34
28
|
const record = new MarcRecord(getFixture("record.json"));
|
|
35
29
|
const expectedResult = getFixture("expectedResult.json");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/normalize-qualifying-information.test.js"],
|
|
4
|
-
"sourcesContent": ["import assert from 'node:assert';\nimport {MarcRecord} from '@natlibfi/marc-record';\nimport validatorFactory from './normalize-qualifying-information.js';\nimport {READERS} from '@natlibfi/fixura';\nimport generateTests from '@natlibfi/fixugen';\
|
|
5
|
-
"mappings": "AAAA,OAAO,YAAY;AACnB,SAAQ,kBAAiB;AACzB,OAAO,sBAAsB;AAC7B,SAAQ,eAAc;AACtB,OAAO,mBAAmB;
|
|
4
|
+
"sourcesContent": ["import assert from 'node:assert';\nimport {MarcRecord} from '@natlibfi/marc-record';\nimport validatorFactory from './normalize-qualifying-information.js';\nimport {READERS} from '@natlibfi/fixura';\nimport generateTests from '@natlibfi/fixugen';\n//import createDebugLogger from 'debug';\n\ngenerateTests({\n callback,\n path: [import.meta.dirname, '..', 'test-fixtures', 'normalize-qualifying-information'],\n useMetadataFile: true,\n recurse: false,\n fixura: {\n reader: READERS.JSON\n },\n hooks: {\n before: async () => {\n testValidatorFactory();\n }\n }\n});\n//const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda/normalize-qualifying-information:test');\n\nasync function testValidatorFactory() {\n const validator = await validatorFactory();\n\n assert.equal(typeof validator, 'object');\n assert.equal(typeof validator.description, 'string');\n assert.equal(typeof validator.validate, 'function');\n}\n\nasync function callback({getFixture, fix = false}) {\n const validator = await validatorFactory();\n const record = new MarcRecord(getFixture('record.json'));\n const expectedResult = getFixture('expectedResult.json');\n // console.log(expectedResult); // eslint-disable-line\n\n if (!fix) {\n const result = await validator.validate(record);\n assert.deepEqual(result, expectedResult);\n return;\n }\n\n await validator.fix(record);\n assert.deepEqual(record, expectedResult);\n}\n"],
|
|
5
|
+
"mappings": "AAAA,OAAO,YAAY;AACnB,SAAQ,kBAAiB;AACzB,OAAO,sBAAsB;AAC7B,SAAQ,eAAc;AACtB,OAAO,mBAAmB;AAG1B,cAAc;AAAA,EACZ;AAAA,EACA,MAAM,CAAC,YAAY,SAAS,MAAM,iBAAiB,kCAAkC;AAAA,EACrF,iBAAiB;AAAA,EACjB,SAAS;AAAA,EACT,QAAQ;AAAA,IACN,QAAQ,QAAQ;AAAA,EAClB;AAAA,EACA,OAAO;AAAA,IACL,QAAQ,YAAY;AAClB,2BAAqB;AAAA,IACvB;AAAA,EACF;AACF,CAAC;AAGD,eAAe,uBAAuB;AACpC,QAAM,YAAY,MAAM,iBAAiB;AAEzC,SAAO,MAAM,OAAO,WAAW,QAAQ;AACvC,SAAO,MAAM,OAAO,UAAU,aAAa,QAAQ;AACnD,SAAO,MAAM,OAAO,UAAU,UAAU,UAAU;AACpD;AAEA,eAAe,SAAS,EAAC,YAAY,MAAM,MAAK,GAAG;AACjD,QAAM,YAAY,MAAM,iBAAiB;AACzC,QAAM,SAAS,IAAI,WAAW,WAAW,aAAa,CAAC;AACvD,QAAM,iBAAiB,WAAW,qBAAqB;AAGvD,MAAI,CAAC,KAAK;AACR,UAAM,SAAS,MAAM,UAAU,SAAS,MAAM;AAC9C,WAAO,UAAU,QAAQ,cAAc;AACvC;AAAA,EACF;AAEA,QAAM,UAAU,IAAI,MAAM;AAC1B,SAAO,UAAU,QAAQ,cAAc;AACzC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/normalize-utf8-diacritics.js"],
|
|
4
|
-
"sourcesContent": ["//import createDebugLogger from 'debug';\nimport clone from 'clone';\nimport {convert as nongenericNormalization} from './unicode-decomposition.js';\nimport {fieldToString} from './utils.js';\n\n// Note that https://github.com/NatLibFi/marc-record-validators-melinda/blob/master/src/unicode-decomposition.js contains\n// similar functionalities. It's less generic and lacks diacritic removal but has it advantages as well.\n\n//const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda/normalize-utf-diacritics');\n\n// See also https://github.com/NatLibFi/marc-record-validators-melinda/blob/master/src/unicode-decomposition.js .\n// It uses a list of convertable characters whilst this uses a generic stuff as well.\n// It handles various '.' and '\u00A9' type normalizations as well.\n// NB! This version has minor bug/feature issue regarding fixComposition()\n\n// Author(s): Nicholas Volk\nexport default function () {\n\n return {\n description: 'Generic normalization of latin UTF-8 diacritics. Precompose Finnish \u00E5, \u00E4 and \u00F6. Decompose others.',\n validate, fix\n };\n\n function fix(record) {\n const res = {message: [], fix: [], valid: true};\n //message.fix = [];\n\n // Actual parsing of all fields\n /*\n if (!record.fields) {\n return false;\n }\n */\n\n record.fields.forEach(field => {\n fieldFixComposition(field);\n //validateField(field, true, message);\n });\n\n // message.valid = !(message.message.length >= 1);\n return res;\n }\n\n function validate(record) {\n const res = {message: []};\n\n // Actual parsing of all fields\n /*\n if (!record.fields) {\n return false;\n }\n */\n\n record.fields.forEach(field => {\n validateField(field, res);\n });\n\n res.valid = !(res.message.length >= 1);\n return res;\n }\n\n function validateField(field, res) {\n if (!field.subfields) {\n return;\n }\n const orig = fieldToString(field);\n\n const normalizedField = fieldFixComposition(clone(field));\n const mod = fieldToString(normalizedField);\n if (orig !== mod) { // Fail as the input is \"broken\"/\"crap\"/sumthing\n res.message.push(`'${orig}' requires normalization`);\n return;\n }\n return;\n }\n}\n\n\n// Traditionally these six are precomposed and all the rest decomposed\nexport function precomposeFinnishLetters(value = '') {\n return value.\n replace(/a\u030A/gu, '\u00E5').\n replace(/a\u0308/gu, '\u00E4').\n replace(/o\u0308/gu, '\u00F6').\n replace(/A\u030A/gu, '\u00C5').\n replace(/A\u0308/gu, '\u00C4').\n replace(/O\u0308/gu, '\u00D6');\n}\n\nexport function fixComposition(value = '') {\n // Target: Diacritics use Melinda internal notation.\n // General solution: Decompose everything and then compose '\u00E5', '\u00E4', '\u00F6', '\u00C5', '\u00C4' and '\u00D6'.\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize\n // Bug/Feature: the generic normalize() function also normalizes non-latin encodings as well, is this ok?\n // Exception: Input contains non-Latin script letters: don't decompose (see field 880 tests):\n if (value.match(/[^\\p{Script=Latin}\\p{Script=Common}\\p{Script=Inherited}]/u)) {\n // Problem with this approach: mixed language content (eg. cyrillic + latin) won't get normalized.\n // Hack/Damage control: we might add decomposition rules for most common diacritis here (eg. \u00FC, \u00E9...).\n // OR we could split input to words and handle them separately?\n // NB! Hack not implemented yet. The main source of problematic case would probably be greek characters\n // within texts, that are written with latin alphabet.\n //return precomposeFinnishLetters(value);\n return nongenericNormalization(value);\n }\n return precomposeFinnishLetters(String(value).normalize('NFD'));\n}\n\n\nexport function fieldFixComposition(field) {\n if (!field.subfields) {\n return field;\n }\n //const originalValue = fieldToString(field);\n //nvdebug(`fFC: '${originalValue}'`,
|
|
5
|
-
"mappings": "AACA,OAAO,WAAW;AAClB,SAAQ,WAAW,+BAA8B;AACjD,SAAQ,
|
|
4
|
+
"sourcesContent": ["//import createDebugLogger from 'debug';\nimport clone from 'clone';\nimport {convert as nongenericNormalization} from './unicode-decomposition.js';\nimport {fieldToString /*, nvdebug */} from './utils.js';\n\n// Note that https://github.com/NatLibFi/marc-record-validators-melinda/blob/master/src/unicode-decomposition.js contains\n// similar functionalities. It's less generic and lacks diacritic removal but has it advantages as well.\n\n//const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda/normalize-utf-diacritics');\n//const debugData = debug.extend('data');\n//const debugDev = debug.extend('dev');\n\n\n// See also https://github.com/NatLibFi/marc-record-validators-melinda/blob/master/src/unicode-decomposition.js .\n// It uses a list of convertable characters whilst this uses a generic stuff as well.\n// It handles various '.' and '\u00A9' type normalizations as well.\n// NB! This version has minor bug/feature issue regarding fixComposition()\n\n// Author(s): Nicholas Volk\nexport default function () {\n\n return {\n description: 'Generic normalization of latin UTF-8 diacritics. Precompose Finnish \u00E5, \u00E4 and \u00F6. Decompose others.',\n validate, fix\n };\n\n function fix(record) {\n const res = {message: [], fix: [], valid: true};\n //message.fix = [];\n\n // Actual parsing of all fields\n /*\n if (!record.fields) {\n return false;\n }\n */\n\n record.fields.forEach(field => {\n fieldFixComposition(field);\n //validateField(field, true, message);\n });\n\n // message.valid = !(message.message.length >= 1);\n return res;\n }\n\n function validate(record) {\n const res = {message: []};\n\n // Actual parsing of all fields\n /*\n if (!record.fields) {\n return false;\n }\n */\n\n record.fields.forEach(field => {\n validateField(field, res);\n });\n\n res.valid = !(res.message.length >= 1);\n return res;\n }\n\n function validateField(field, res) {\n if (!field.subfields) {\n return;\n }\n const orig = fieldToString(field);\n\n const normalizedField = fieldFixComposition(clone(field));\n const mod = fieldToString(normalizedField);\n if (orig !== mod) { // Fail as the input is \"broken\"/\"crap\"/sumthing\n res.message.push(`'${orig}' requires normalization`);\n return;\n }\n return;\n }\n}\n\n\n// Traditionally these six are precomposed and all the rest decomposed\nexport function precomposeFinnishLetters(value = '') {\n return value.\n replace(/a\u030A/gu, '\u00E5').\n replace(/a\u0308/gu, '\u00E4').\n replace(/o\u0308/gu, '\u00F6').\n replace(/A\u030A/gu, '\u00C5').\n replace(/A\u0308/gu, '\u00C4').\n replace(/O\u0308/gu, '\u00D6');\n}\n\nexport function fixComposition(value = '') {\n // Target: Diacritics use Melinda internal notation.\n // General solution: Decompose everything and then compose '\u00E5', '\u00E4', '\u00F6', '\u00C5', '\u00C4' and '\u00D6'.\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize\n // Bug/Feature: the generic normalize() function also normalizes non-latin encodings as well, is this ok?\n // Exception: Input contains non-Latin script letters: don't decompose (see field 880 tests):\n if (value.match(/[^\\p{Script=Latin}\\p{Script=Common}\\p{Script=Inherited}]/u)) {\n // Problem with this approach: mixed language content (eg. cyrillic + latin) won't get normalized.\n // Hack/Damage control: we might add decomposition rules for most common diacritis here (eg. \u00FC, \u00E9...).\n // OR we could split input to words and handle them separately?\n // NB! Hack not implemented yet. The main source of problematic case would probably be greek characters\n // within texts, that are written with latin alphabet.\n //return precomposeFinnishLetters(value);\n return nongenericNormalization(value);\n }\n return precomposeFinnishLetters(String(value).normalize('NFD'));\n}\n\n\nexport function fieldFixComposition(field) {\n if (!field.subfields) {\n return field;\n }\n //const originalValue = fieldToString(field);\n //nvdebug(`fFC: '${originalValue}'`, debugDev);\n field.subfields.forEach((subfield, index) => {\n field.subfields[index].value = fixComposition(subfield.value);\n });\n //const newValue = fieldToString(field);\n //if (originalValue !== newValue) {\n // nvdebug(`FIXCOMP: '${originalValue}' => '${newValue}'`, debugDev);\n //}\n return field;\n}\n\n/*\nexport function fieldRemoveDecomposedDiacritics(field) {\n // Raison d'\u00EAtre/motivation: \"Sir\u00E9n\" and diacriticless \"Siren\" might refer to a same surname, so this normalization\n // allows us to compare authors and avoid duplicate fields.\n field.subfields.forEach((sf) => {\n sf.value = removeDecomposedDiacritics(sf.value);\n });\n\n function removeDecomposedDiacritics(value = '') {\n // NB #1: Does nothing to precomposed letters. String.normalize('NFD') can handle them.\n // NB #2: Finnish letters '\u00E5', '\u00E4', '\u00F6', '\u00C5', \u00C4', and '\u00D6' should be handled (=precomposed) before calling this.\n // NB #3: Calling our very own fixComposition() before this function handles both #1 and #2.\n return String(value).replace(/\\p{Diacritic}/gu, '');\n }\n}\n*/\n\n"],
|
|
5
|
+
"mappings": "AACA,OAAO,WAAW;AAClB,SAAQ,WAAW,+BAA8B;AACjD,SAAQ,qBAAmC;AAgB3C,0BAA2B;AAEzB,SAAO;AAAA,IACL,aAAa;AAAA,IACb;AAAA,IAAU;AAAA,EACZ;AAEA,WAAS,IAAI,QAAQ;AACnB,UAAM,MAAM,EAAC,SAAS,CAAC,GAAG,KAAK,CAAC,GAAG,OAAO,KAAI;AAU9C,WAAO,OAAO,QAAQ,WAAS;AAC7B,0BAAoB,KAAK;AAAA,IAE3B,CAAC;AAGD,WAAO;AAAA,EACT;AAEA,WAAS,SAAS,QAAQ;AACxB,UAAM,MAAM,EAAC,SAAS,CAAC,EAAC;AASxB,WAAO,OAAO,QAAQ,WAAS;AAC7B,oBAAc,OAAO,GAAG;AAAA,IAC1B,CAAC;AAED,QAAI,QAAQ,EAAE,IAAI,QAAQ,UAAU;AACpC,WAAO;AAAA,EACT;AAEA,WAAS,cAAc,OAAO,KAAK;AACjC,QAAI,CAAC,MAAM,WAAW;AACpB;AAAA,IACF;AACA,UAAM,OAAO,cAAc,KAAK;AAEhC,UAAM,kBAAkB,oBAAoB,MAAM,KAAK,CAAC;AACxD,UAAM,MAAM,cAAc,eAAe;AACzC,QAAI,SAAS,KAAK;AAChB,UAAI,QAAQ,KAAK,IAAI,IAAI,0BAA0B;AACnD;AAAA,IACF;AACA;AAAA,EACF;AACF;AAIO,gBAAS,yBAAyB,QAAQ,IAAI;AACnD,SAAO,MACL,QAAQ,QAAQ,MAAG,EACnB,QAAQ,QAAQ,MAAG,EACnB,QAAQ,QAAQ,MAAG,EACnB,QAAQ,QAAQ,MAAG,EACnB,QAAQ,QAAQ,MAAG,EACnB,QAAQ,QAAQ,MAAG;AACvB;AAEO,gBAAS,eAAe,QAAQ,IAAI;AAMzC,MAAI,MAAM,MAAM,2DAA2D,GAAG;AAO5E,WAAO,wBAAwB,KAAK;AAAA,EACtC;AACA,SAAO,yBAAyB,OAAO,KAAK,EAAE,UAAU,KAAK,CAAC;AAChE;AAGO,gBAAS,oBAAoB,OAAO;AACzC,MAAI,CAAC,MAAM,WAAW;AACpB,WAAO;AAAA,EACT;AAGA,QAAM,UAAU,QAAQ,CAAC,UAAU,UAAU;AAC3C,UAAM,UAAU,KAAK,EAAE,QAAQ,eAAe,SAAS,KAAK;AAAA,EAC9D,CAAC;AAKD,SAAO;AACT;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -3,7 +3,6 @@ import { MarcRecord } from "@natlibfi/marc-record";
|
|
|
3
3
|
import validatorFactory from "./normalize-utf8-diacritics.js";
|
|
4
4
|
import { READERS } from "@natlibfi/fixura";
|
|
5
5
|
import generateTests from "@natlibfi/fixugen";
|
|
6
|
-
import createDebugLogger from "debug";
|
|
7
6
|
generateTests({
|
|
8
7
|
callback,
|
|
9
8
|
path: [import.meta.dirname, "..", "test-fixtures", "normalize-utf8-diacritics"],
|
|
@@ -18,18 +17,13 @@ generateTests({
|
|
|
18
17
|
}
|
|
19
18
|
}
|
|
20
19
|
});
|
|
21
|
-
const debug = createDebugLogger("@natlibfi/marc-record-validators-melinda/normalize-utf8-diacritics:test");
|
|
22
20
|
async function testValidatorFactory() {
|
|
23
21
|
const validator = await validatorFactory();
|
|
24
22
|
assert.equal(typeof validator, "object");
|
|
25
23
|
assert.equal(typeof validator.description, "string");
|
|
26
24
|
assert.equal(typeof validator.validate, "function");
|
|
27
25
|
}
|
|
28
|
-
async function callback({ getFixture,
|
|
29
|
-
if (enabled === false) {
|
|
30
|
-
debug("TEST SKIPPED!");
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
26
|
+
async function callback({ getFixture, fix = false }) {
|
|
33
27
|
const validator = await validatorFactory();
|
|
34
28
|
const record = new MarcRecord(getFixture("record.json"));
|
|
35
29
|
const expectedResult = getFixture("expectedResult.json");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/normalize-utf8-diacritics.test.js"],
|
|
4
|
-
"sourcesContent": ["import assert from 'node:assert';\nimport {MarcRecord} from '@natlibfi/marc-record';\nimport validatorFactory from './normalize-utf8-diacritics.js';\nimport {READERS} from '@natlibfi/fixura';\nimport generateTests from '@natlibfi/fixugen';\
|
|
5
|
-
"mappings": "AAAA,OAAO,YAAY;AACnB,SAAQ,kBAAiB;AACzB,OAAO,sBAAsB;AAC7B,SAAQ,eAAc;AACtB,OAAO,mBAAmB;
|
|
4
|
+
"sourcesContent": ["import assert from 'node:assert';\nimport {MarcRecord} from '@natlibfi/marc-record';\nimport validatorFactory from './normalize-utf8-diacritics.js';\nimport {READERS} from '@natlibfi/fixura';\nimport generateTests from '@natlibfi/fixugen';\n//import createDebugLogger from 'debug';\n\ngenerateTests({\n callback,\n path: [import.meta.dirname, '..', 'test-fixtures', 'normalize-utf8-diacritics'],\n useMetadataFile: true,\n recurse: false,\n fixura: {\n reader: READERS.JSON\n },\n hooks: {\n before: async () => {\n testValidatorFactory();\n }\n }\n});\n\n//const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda/normalize-utf8-diacritics:test');\n\nasync function testValidatorFactory() {\n const validator = await validatorFactory();\n\n assert.equal(typeof validator, 'object');\n assert.equal(typeof validator.description, 'string');\n assert.equal(typeof validator.validate, 'function');\n}\n\nasync function callback({getFixture, fix = false}) {\n const validator = await validatorFactory();\n const record = new MarcRecord(getFixture('record.json'));\n const expectedResult = getFixture('expectedResult.json');\n // console.log(expectedResult); // eslint-disable-line\n\n if (!fix) {\n const result = await validator.validate(record);\n assert.deepEqual(result, expectedResult);\n return;\n }\n\n await validator.fix(record);\n assert.deepEqual(record, expectedResult);\n}\n"],
|
|
5
|
+
"mappings": "AAAA,OAAO,YAAY;AACnB,SAAQ,kBAAiB;AACzB,OAAO,sBAAsB;AAC7B,SAAQ,eAAc;AACtB,OAAO,mBAAmB;AAG1B,cAAc;AAAA,EACZ;AAAA,EACA,MAAM,CAAC,YAAY,SAAS,MAAM,iBAAiB,2BAA2B;AAAA,EAC9E,iBAAiB;AAAA,EACjB,SAAS;AAAA,EACT,QAAQ;AAAA,IACN,QAAQ,QAAQ;AAAA,EAClB;AAAA,EACA,OAAO;AAAA,IACL,QAAQ,YAAY;AAClB,2BAAqB;AAAA,IACvB;AAAA,EACF;AACF,CAAC;AAID,eAAe,uBAAuB;AACpC,QAAM,YAAY,MAAM,iBAAiB;AAEzC,SAAO,MAAM,OAAO,WAAW,QAAQ;AACvC,SAAO,MAAM,OAAO,UAAU,aAAa,QAAQ;AACnD,SAAO,MAAM,OAAO,UAAU,UAAU,UAAU;AACpD;AAEA,eAAe,SAAS,EAAC,YAAY,MAAM,MAAK,GAAG;AACjD,QAAM,YAAY,MAAM,iBAAiB;AACzC,QAAM,SAAS,IAAI,WAAW,WAAW,aAAa,CAAC;AACvD,QAAM,iBAAiB,WAAW,qBAAqB;AAGvD,MAAI,CAAC,KAAK;AACR,UAAM,SAAS,MAAM,UAAU,SAAS,MAAM;AAC9C,WAAO,UAAU,QAAQ,cAAc;AACvC;AAAA,EACF;AAEA,QAAM,UAAU,IAAI,MAAM;AAC1B,SAAO,UAAU,QAAQ,cAAc;AACzC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,20 +1,13 @@
|
|
|
1
1
|
import clone from "clone";
|
|
2
2
|
import { fieldStripPunctuation } from "./punctuation2.js";
|
|
3
|
-
import { fieldToString,
|
|
3
|
+
import { fieldToString, isContentSubfieldCode } from "./utils.js";
|
|
4
4
|
import { fieldNormalizeControlNumbers } from "./normalize-identifiers.js";
|
|
5
5
|
import createDebugLogger from "debug";
|
|
6
6
|
import { normalizePartData, subfieldContainsPartData } from "./normalizeSubfieldValueForComparison.js";
|
|
7
|
+
import { isEnnakkotietoSubfield } from "./prepublicationUtils.js";
|
|
8
|
+
import { getSynonym } from "./merge-fields/worldKnowledge.js";
|
|
7
9
|
const debug = createDebugLogger("@natlibfi/melinda-marc-record-merge-reducers:normalizeFieldForComparison");
|
|
8
10
|
const debugDev = debug.extend("dev");
|
|
9
|
-
export function isEnnakkotietoSubfieldG(subfield) {
|
|
10
|
-
if (valuelessSubfield(subfield)) {
|
|
11
|
-
return false;
|
|
12
|
-
}
|
|
13
|
-
if (subfield.code !== "g") {
|
|
14
|
-
return false;
|
|
15
|
-
}
|
|
16
|
-
return subfield.value.match(/^ENNAKKOTIETO\.?$/gui);
|
|
17
|
-
}
|
|
18
11
|
function debugFieldComparison(oldField, newField) {
|
|
19
12
|
const oldString = fieldToString(oldField);
|
|
20
13
|
const newString = fieldToString(newField);
|
|
@@ -39,13 +32,13 @@ function containsCorporateName(tag = "???", subfieldCode = void 0) {
|
|
|
39
32
|
return false;
|
|
40
33
|
}
|
|
41
34
|
function skipAllSubfieldNormalizations(value, subfieldCode, tag) {
|
|
42
|
-
if (
|
|
35
|
+
if (isEnnakkotietoSubfield({ "code": subfieldCode, value })) {
|
|
43
36
|
return true;
|
|
44
37
|
}
|
|
45
38
|
if (tag === "035" && ["a", "z"].includes(subfieldCode)) {
|
|
46
39
|
return true;
|
|
47
40
|
}
|
|
48
|
-
if (
|
|
41
|
+
if (!isContentSubfieldCode(subfieldCode, tag)) {
|
|
49
42
|
return true;
|
|
50
43
|
}
|
|
51
44
|
return false;
|
|
@@ -93,6 +86,21 @@ function fieldLowercase(field) {
|
|
|
93
86
|
return false;
|
|
94
87
|
}
|
|
95
88
|
}
|
|
89
|
+
function normalize300SubfieldA(field) {
|
|
90
|
+
if (field.tag !== "300") {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
field.subfields.forEach((sf) => normalizePages(sf));
|
|
94
|
+
function normalizePages(subfield) {
|
|
95
|
+
if (valuelessSubfield(subfield)) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (subfield.code !== "a") {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
subfield.value = subfield.value.replace(/(?:\b(?:pages?|pp\.|Seite|sida|sidor|sivu|sivua)\b|\bs$)/g, "s.");
|
|
102
|
+
}
|
|
103
|
+
}
|
|
96
104
|
function hack490SubfieldA(field) {
|
|
97
105
|
if (field.tag !== "490") {
|
|
98
106
|
return;
|
|
@@ -143,6 +151,7 @@ function normalizeISBN(field) {
|
|
|
143
151
|
}
|
|
144
152
|
function fieldSpecificHacks(field) {
|
|
145
153
|
normalizeISBN(field);
|
|
154
|
+
normalize300SubfieldA(field);
|
|
146
155
|
hack490SubfieldA(field);
|
|
147
156
|
}
|
|
148
157
|
export function fieldTrimSubfieldValues(field) {
|
|
@@ -167,6 +176,8 @@ function removeDecomposedDiacritics(value = "") {
|
|
|
167
176
|
return String(value).replace(/\p{Diacritic}/gu, "");
|
|
168
177
|
}
|
|
169
178
|
function normalizeSubfieldValue(value, subfieldCode, tag) {
|
|
179
|
+
value = removeCharsThatDontCarryMeaning(value, tag, subfieldCode);
|
|
180
|
+
value = getSynonym(tag, subfieldCode, value);
|
|
170
181
|
value = subfieldValueLowercase(value, subfieldCode, tag);
|
|
171
182
|
value = normalizePartData(value, subfieldCode, tag);
|
|
172
183
|
value = value.replace(/^\[([^[\]]+)\]/gu, "$1");
|
|
@@ -203,7 +214,7 @@ function normalizeField(field) {
|
|
|
203
214
|
return field;
|
|
204
215
|
}
|
|
205
216
|
export function cloneAndNormalizeFieldForComparison(field) {
|
|
206
|
-
const clonedField =
|
|
217
|
+
const clonedField = cloneAndRemovePunctuation(field);
|
|
207
218
|
if (fieldSkipNormalization(field)) {
|
|
208
219
|
return clonedField;
|
|
209
220
|
}
|
|
@@ -212,7 +223,6 @@ export function cloneAndNormalizeFieldForComparison(field) {
|
|
|
212
223
|
return;
|
|
213
224
|
}
|
|
214
225
|
sf.value = normalizeSubfieldValue(sf.value, sf.code, field.tag);
|
|
215
|
-
sf.value = removeCharsThatDontCarryMeaning(sf.value, field.tag, sf.code);
|
|
216
226
|
});
|
|
217
227
|
normalizeField(clonedField);
|
|
218
228
|
fieldRemoveDecomposedDiacritics(clonedField);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/normalizeFieldForComparison.js"],
|
|
4
|
-
"sourcesContent": ["/*\n Note that this file contains very powerful normalizations and spells that are:\n - meant for comparing similarity/mergability of two fields (clone, normalize, compare),\n - and NOT for modifying the actual field!\n\n This is mainly used by melinda-marc-record-merge-reducers. However, also removeInferiorDataFields fixer also used this.\n Thus it is here. However, most of the testing is done via merge-reducers...\n*/\nimport clone from 'clone';\nimport {fieldStripPunctuation} from './punctuation2.js';\nimport {fieldToString, isControlSubfieldCode} from './utils.js';\n\nimport {fieldNormalizeControlNumbers/*, normalizeControlSubfieldValue*/} from './normalize-identifiers.js';\nimport createDebugLogger from 'debug';\nimport {normalizePartData, subfieldContainsPartData} from './normalizeSubfieldValueForComparison.js';\n\nconst debug = createDebugLogger('@natlibfi/melinda-marc-record-merge-reducers:normalizeFieldForComparison');\n//const debugData = debug.extend('data');\nconst debugDev = debug.extend('dev');\n\nexport function isEnnakkotietoSubfieldG(subfield) {\n if (valuelessSubfield(subfield)) {\n return false;\n }\n if (subfield.code !== 'g') {\n return false;\n }\n return subfield.value.match(/^ENNAKKOTIETO\\.?$/gui);\n}\n\nfunction debugFieldComparison(oldField, newField) { // NB: Debug-only function!\n /*\n // We may drop certain subfields:\n if (oldField.subfields.length === newField.subfields.length) {\n oldField.subfields.forEach((subfield, index) => {\n const newValue = newField.subfields[index].value;\n if (subfield.value !== newValue) {\n nvdebug(`NORMALIZE SUBFIELD: '${subfield.value}' => '${newValue}'`, debugDev);\n }\n });\n }\n */\n const oldString = fieldToString(oldField);\n const newString = fieldToString(newField);\n if (oldString === newString) {\n return;\n }\n //nvdebug(`NORMALIZE FIELD:\\n '${fieldToString(oldField)}' =>\\n '${fieldToString(newField)}'`, debugDev);\n}\n\nfunction containsHumanName(tag = '???', subfieldCode = undefined) {\n // NB! This set is for bibs! Auth has 400... What else...\n if (['100', '600', '700', '800'].includes(tag)) {\n if (subfieldCode === undefined || subfieldCode === 'a') {\n return true;\n }\n }\n // Others?\n return false;\n}\n\nfunction containsCorporateName(tag = '???', subfieldCode = undefined) {\n // NB! This set is for bibs! Auth has 400... What else...\n if (['110', '610', '710', '810'].includes(tag)) {\n if (subfieldCode === undefined || subfieldCode === 'a') {\n return true;\n }\n }\n // Others?\n return false;\n}\n\nfunction skipAllSubfieldNormalizations(value, subfieldCode, tag) {\n\n if (isEnnakkotietoSubfieldG({'code': subfieldCode, value})) {\n return true;\n }\n\n if (tag === '035' && ['a', 'z'].includes(subfieldCode)) { // A\n return true;\n }\n\n if (isControlSubfieldCode(subfieldCode)) {\n return true;\n }\n return false;\n}\n\nfunction skipSubfieldLowercase(value, subfieldCode, tag) {\n // These may contain Roman Numerals...\n if (subfieldContainsPartData(tag, subfieldCode)) {\n return true;\n }\n\n return skipAllSubfieldNormalizations(value, subfieldCode, tag);\n}\n\nfunction skipAllFieldNormalizations(tag) {\n if (['LOW', 'SID'].includes(tag)) {\n return true;\n }\n return false;\n}\n\n\nfunction subfieldValueLowercase(value, subfieldCode, tag) {\n if (skipSubfieldLowercase(value, subfieldCode, tag)) {\n return value;\n }\n\n //return value.toLowerCase();\n const newValue = value.toLowerCase();\n if (newValue !== value) {\n //nvdebug(`SVL ${tag} $${subfieldCode} '${value}' =>`, debugDev);\n //nvdebug(`SVL ${tag} $${subfieldCode} '${newValue}'`, debugDev);\n return newValue;\n }\n return value;\n}\n\nfunction subfieldLowercase(sf, tag) {\n if (valuelessSubfield(sf)) {\n return;\n }\n sf.value = subfieldValueLowercase(sf.value, sf.code, tag);\n}\n\nfunction fieldLowercase(field) {\n if (skipFieldLowercase(field)) {\n return;\n }\n\n field.subfields.forEach(sf => subfieldLowercase(sf, field.tag));\n\n function skipFieldLowercase(field) {\n if (skipAllFieldNormalizations(field.tag)) {\n return true;\n }\n // Skip non-interesting fields\n if (!containsHumanName(field.tag) && !containsCorporateName(field.tag) && !['240', '245', '630'].includes(field.tag)) {\n return true;\n }\n\n return false;\n }\n}\n\n\nfunction hack490SubfieldA(field) {\n if (field.tag !== '490') {\n return;\n }\n field.subfields.forEach(sf => removeSarja(sf));\n\n // NB! This won't work, if the punctuation has not been stripped beforehand!\n function removeSarja(subfield) {\n if (valuelessSubfield(subfield)) {\n return;\n }\n\n if (subfield.code !== 'a') {\n return;\n }\n const tmp = subfield.value.replace(/ ?-(?:[a-z]|\u00E4|\u00F6)*sarja$/u, '');\n if (tmp.length > 0) {\n subfield.value = tmp;\n return;\n }\n }\n}\n\nexport function tagAndSubfieldCodeReferToIsbn(tag, subfieldCode) {\n // NB! We don't do this to 020$z!\n if (subfieldCode === 'z' && ['765', '767', '770', '772', '773', '774', '776', '777', '780', '785', '786', '787'].includes(tag)) {\n return true;\n }\n if (tag === '020' && subfieldCode === 'a') {\n return true;\n }\n return false;\n}\n\nfunction looksLikeIsbn(value) {\n // Does not check validity!\n if (value.match(/^(?:[0-9]-?){9}(?:[0-9]-?[0-9]-?[0-9]-?)?[0-9Xx]$/u)) {\n return true;\n }\n return false;\n}\n\nfunction normalizeISBN(field) {\n if (!field.subfields) {\n return;\n }\n\n //nvdebug(`ISBN-field? ${fieldToString(field)}`);\n const relevantSubfields = field.subfields.filter(sf => tagAndSubfieldCodeReferToIsbn(field.tag, sf.code) && looksLikeIsbn(sf.value));\n relevantSubfields.forEach(sf => normalizeIsbnSubfield(sf));\n\n function normalizeIsbnSubfield(sf) {\n if (valuelessSubfield(sf)) {\n return;\n }\n //nvdebug(` ISBN-subfield? ${subfieldToString(sf)}`);\n sf.value = sf.value.replace(/-/ug, '');\n sf.value = sf.value.replace(/x/u, 'X');\n }\n\n}\n\nfunction fieldSpecificHacks(field) {\n normalizeISBN(field); // 020$a, not $z!\n hack490SubfieldA(field);\n}\n\nexport function fieldTrimSubfieldValues(field) {\n field.subfields?.forEach((sf) => {\n if (valuelessSubfield(sf)) {\n return;\n }\n sf.value = sf.value.replace(/^[ \\t\\n]+/u, '');\n sf.value = sf.value.replace(/[ \\t\\n]+$/u, '');\n sf.value = sf.value.replace(/[ \\t\\n]+/gu, ' ');\n });\n}\n\nfunction fieldRemoveDecomposedDiacritics(field) {\n // Raison d'\u00EAtre/motivation: \"Sir\u00E9n\" and diacriticless \"Siren\" might refer to a same surname, so this normalization\n // allows us to compare authors and avoid duplicate fields.\n field.subfields.forEach((sf) => {\n if (valuelessSubfield(sf)) {\n return;\n }\n sf.value = removeDecomposedDiacritics(sf.value);\n });\n}\n\nfunction removeDecomposedDiacritics(value = '') {\n // NB #1: Does nothing to precomposed letters. Do String.normalize('NFD') first, if you want to handle them.\n // NB #2: Finnish letters '\u00E5', '\u00E4', '\u00F6', '\u00C5', \u00C4', and '\u00D6' should be handled (=precomposed) before calling this. (= keep them as is)\n // NB #3: Calling our very own fixComposition() before this function handles both #1 and #2.\n return String(value).replace(/\\p{Diacritic}/gu, '');\n}\n\nfunction normalizeSubfieldValue(value, subfieldCode, tag) {\n // NB! For comparison of values only\n /* eslint-disable */\n value = subfieldValueLowercase(value, subfieldCode, tag);\n\n // Normalize: s. = sivut = pp.\n value = normalizePartData(value, subfieldCode, tag);\n value = value.replace(/^\\[([^[\\]]+)\\]/gu, '$1'); // eslint-disable-line functional/immutable-data\n\n if (['130', '730'].includes(tag) && subfieldCode === 'a') {\n value = value.replace(' : ', ', '); // \"Halloween ends (elokuva, 2022)\" vs \"Halloween ends (elokuva : 2023)\"\n }\n /* eslint-enable */\n\n // Not going to do these in the foreseeable future, but keeping them here for discussion:\n // Possible normalizations include but are not limited to:\n // \u00F8 => \u00F6? Might be language dependent: 041 $a fin => \u00F6, 041 $a eng => o?\n // \u00D8 => \u00D6?\n // \u00DF => ss\n // \u00FE => th (NB! Both upper and lower case)\n // ...\n // Probably nots:\n // \u00FC => y (probably not, though this correlates with Finnish letter-to-sound rules)\n // w => v (OK for Finnish sorting in certain cases, but we are not here, are we?)\n // I guess we should use decomposed values in code here. (Not sure what composition my examples above use.)\n return value;\n}\n\nexport function cloneAndRemovePunctuation(field) {\n const clonedField = clone(field);\n if (fieldSkipNormalization(field)) {\n return clonedField;\n }\n fieldStripPunctuation(clonedField);\n fieldTrimSubfieldValues(clonedField);\n debugDev('PUNC');\n debugFieldComparison(field, clonedField);\n\n return clonedField;\n}\n\nfunction removeCharsThatDontCarryMeaning(value, tag, subfieldCode) {\n if (tag === '080') {\n return value;\n }\n\n // 3\" refers to inches, but as this is for comparison only we don't mind...\n value = value.replace(/['\u2018\u2019\"\u201E\u201C\u201D\u00AB\u00BB]/gu, ''); // MET-570 et al. Subset of https://hexdocs.pm/ex_unicode/Unicode.Category.QuoteMarks.html\n // MRA-273: Handle X00$a name initials.\n // NB #1: that we remove spaces for comparison (as it simpler), though actually space should be used. Doesn't matter as this is comparison only.\n // NB #2: we might/should eventually write a validator/fixer that adds those spaces. After that point, this expection should become obsolete.\n if (subfieldCode === 'a' && ['100', '400', '600', '700', '800'].includes(tag)) { // 400 is used in auth records. It's not a bib field at all.\n value = value.replace(/([A-Z]|\u00C5|\u00C4|\u00D6)\\. +/ugi, '$1.');\n }\n\n return value;\n}\n\nfunction normalizeField(field) {\n //sf.value = removeDecomposedDiacritics(sf.value);\n fieldStripPunctuation(field);\n fieldLowercase(field);\n fieldNormalizeControlNumbers(field); // FIN11 vs FI-MELINDA etc.\n return field;\n}\n\nexport function cloneAndNormalizeFieldForComparison(field) {\n // NB! This new field is for comparison purposes only.\n // Some of the normalizations might be considered a bit overkill for other purposes.\n const clonedField = clone(field);\n if (fieldSkipNormalization(field)) {\n return clonedField;\n }\n clonedField.subfields.forEach((sf) => { // Do this for all fields or some fields?\n if (valuelessSubfield(sf)) {\n return;\n }\n sf.value = normalizeSubfieldValue(sf.value, sf.code, field.tag);\n sf.value = removeCharsThatDontCarryMeaning(sf.value, field.tag, sf.code);\n });\n\n normalizeField(clonedField);\n fieldRemoveDecomposedDiacritics(clonedField);\n fieldSpecificHacks(clonedField);\n fieldTrimSubfieldValues(clonedField);\n\n\n debugFieldComparison(field, clonedField); // For debugging purposes only\n\n return clonedField;\n}\n\nfunction fieldSkipNormalization(field) {\n if (!field.subfields || ['018', '066', '080', '083'].includes(field.tag)) {\n return true;\n }\n return false;\n}\n\nfunction valuelessSubfield(sf) {\n return sf.value === undefined;\n}"],
|
|
5
|
-
"mappings": "AAQA,OAAO,WAAW;AAClB,SAAQ,6BAA4B;AACpC,SAAQ,eAAe,6BAA4B;AAEnD,SAAQ,oCAAsE;AAC9E,OAAO,uBAAuB;AAC9B,SAAQ,mBAAmB,gCAA+B;
|
|
4
|
+
"sourcesContent": ["/*\n Note that this file contains very powerful normalizations and spells that are:\n - meant for comparing similarity/mergability of two fields (clone, normalize, compare),\n - and NOT for modifying the actual field!\n\n This is mainly used by melinda-marc-record-merge-reducers. However, also removeInferiorDataFields fixer also used this.\n Thus it is here. However, most of the testing is done via merge-reducers...\n*/\nimport clone from 'clone';\nimport {fieldStripPunctuation} from './punctuation2.js';\nimport {fieldToString, isContentSubfieldCode} from './utils.js';\n\nimport {fieldNormalizeControlNumbers/*, normalizeControlSubfieldValue*/} from './normalize-identifiers.js';\nimport createDebugLogger from 'debug';\nimport {normalizePartData, subfieldContainsPartData} from './normalizeSubfieldValueForComparison.js';\nimport {isEnnakkotietoSubfield} from './prepublicationUtils.js';\nimport {getSynonym} from './merge-fields/worldKnowledge.js';\n\nconst debug = createDebugLogger('@natlibfi/melinda-marc-record-merge-reducers:normalizeFieldForComparison');\n//const debugData = debug.extend('data');\nconst debugDev = debug.extend('dev');\n\nfunction debugFieldComparison(oldField, newField) { // NB: Debug-only function!\n /*\n // We may drop certain subfields:\n if (oldField.subfields.length === newField.subfields.length) {\n oldField.subfields.forEach((subfield, index) => {\n const newValue = newField.subfields[index].value;\n if (subfield.value !== newValue) {\n nvdebug(`NORMALIZE SUBFIELD: '${subfield.value}' => '${newValue}'`, debugDev);\n }\n });\n }\n */\n const oldString = fieldToString(oldField);\n const newString = fieldToString(newField);\n if (oldString === newString) {\n return;\n }\n //nvdebug(`NORMALIZE FIELD:\\n '${fieldToString(oldField)}' =>\\n '${fieldToString(newField)}'`, debugDev);\n}\n\nfunction containsHumanName(tag = '???', subfieldCode = undefined) {\n // NB! This set is for bibs! Auth has 400... What else...\n if (['100', '600', '700', '800'].includes(tag)) {\n if (subfieldCode === undefined || subfieldCode === 'a') {\n return true;\n }\n }\n // Others?\n return false;\n}\n\nfunction containsCorporateName(tag = '???', subfieldCode = undefined) {\n // NB! This set is for bibs! Auth has 410... What else...\n if (['110', '610', '710', '810'].includes(tag)) {\n if (subfieldCode === undefined || subfieldCode === 'a') {\n return true;\n }\n }\n // Others?\n return false;\n}\n\nfunction skipAllSubfieldNormalizations(value, subfieldCode, tag) {\n\n if (isEnnakkotietoSubfield({'code': subfieldCode, value})) {\n return true;\n }\n\n if (tag === '035' && ['a', 'z'].includes(subfieldCode)) {\n return true;\n }\n\n if (!isContentSubfieldCode(subfieldCode, tag)) {\n return true;\n }\n return false;\n}\n\nfunction skipSubfieldLowercase(value, subfieldCode, tag) {\n // These may contain Roman Numerals...\n if (subfieldContainsPartData(tag, subfieldCode)) {\n return true;\n }\n\n return skipAllSubfieldNormalizations(value, subfieldCode, tag);\n}\n\nfunction skipAllFieldNormalizations(tag) {\n if (['LOW', 'SID'].includes(tag)) {\n return true;\n }\n return false;\n}\n\n\nfunction subfieldValueLowercase(value, subfieldCode, tag) {\n if (skipSubfieldLowercase(value, subfieldCode, tag)) {\n return value;\n }\n\n //return value.toLowerCase();\n const newValue = value.toLowerCase();\n if (newValue !== value) {\n //nvdebug(`SVL ${tag} $${subfieldCode} '${value}' =>`, debugDev);\n //nvdebug(`SVL ${tag} $${subfieldCode} '${newValue}'`, debugDev);\n return newValue;\n }\n return value;\n}\n\nfunction subfieldLowercase(sf, tag) {\n if (valuelessSubfield(sf)) {\n return;\n }\n sf.value = subfieldValueLowercase(sf.value, sf.code, tag);\n}\n\nfunction fieldLowercase(field) {\n if (skipFieldLowercase(field)) {\n return;\n }\n\n field.subfields.forEach(sf => subfieldLowercase(sf, field.tag));\n\n function skipFieldLowercase(field) {\n if (skipAllFieldNormalizations(field.tag)) {\n return true;\n }\n // Skip non-interesting fields\n if (!containsHumanName(field.tag) && !containsCorporateName(field.tag) && !['240', '245', '630'].includes(field.tag)) {\n return true;\n }\n\n return false;\n }\n}\n\nfunction normalize300SubfieldA(field) {\n if (field.tag !== '300') {\n return;\n }\n field.subfields.forEach(sf => normalizePages(sf));\n\n function normalizePages(subfield) {\n if (valuelessSubfield(subfield)) {\n return;\n }\n\n if (subfield.code !== 'a') {\n return;\n }\n subfield.value = subfield.value.replace(/(?:\\b(?:pages?|pp\\.|Seite|sida|sidor|sivu|sivua)\\b|\\bs$)/g, 's.');\n }\n}\n\nfunction hack490SubfieldA(field) {\n if (field.tag !== '490') {\n return;\n }\n field.subfields.forEach(sf => removeSarja(sf));\n\n // NB! This won't work, if the punctuation has not been stripped beforehand!\n function removeSarja(subfield) {\n if (valuelessSubfield(subfield)) {\n return;\n }\n\n if (subfield.code !== 'a') {\n return;\n }\n const tmp = subfield.value.replace(/ ?-(?:[a-z]|\u00E4|\u00F6)*sarja$/u, '');\n if (tmp.length > 0) {\n subfield.value = tmp;\n return;\n }\n }\n}\n\nexport function tagAndSubfieldCodeReferToIsbn(tag, subfieldCode) {\n // NB! We don't do this to 020$z!\n if (subfieldCode === 'z' && ['765', '767', '770', '772', '773', '774', '776', '777', '780', '785', '786', '787'].includes(tag)) {\n return true;\n }\n if (tag === '020' && subfieldCode === 'a') {\n return true;\n }\n return false;\n}\n\nfunction looksLikeIsbn(value) {\n // Does not check validity!\n if (value.match(/^(?:[0-9]-?){9}(?:[0-9]-?[0-9]-?[0-9]-?)?[0-9Xx]$/u)) {\n return true;\n }\n return false;\n}\n\nfunction normalizeISBN(field) {\n if (!field.subfields) {\n return;\n }\n\n //nvdebug(`ISBN-field? ${fieldToString(field)}`, debugDev);\n const relevantSubfields = field.subfields.filter(sf => tagAndSubfieldCodeReferToIsbn(field.tag, sf.code) && looksLikeIsbn(sf.value));\n relevantSubfields.forEach(sf => normalizeIsbnSubfield(sf));\n\n function normalizeIsbnSubfield(sf) {\n if (valuelessSubfield(sf)) {\n return;\n }\n //nvdebug(` ISBN-subfield? ${subfieldToString(sf)}`, debugDev);\n sf.value = sf.value.replace(/-/ug, '');\n sf.value = sf.value.replace(/x/u, 'X');\n }\n\n}\n\nfunction fieldSpecificHacks(field) {\n normalizeISBN(field); // 020$a, not $z!\n normalize300SubfieldA(field);\n hack490SubfieldA(field);\n}\n\nexport function fieldTrimSubfieldValues(field) {\n field.subfields?.forEach((sf) => {\n if (valuelessSubfield(sf)) {\n return;\n }\n sf.value = sf.value.replace(/^[ \\t\\n]+/u, '');\n sf.value = sf.value.replace(/[ \\t\\n]+$/u, '');\n sf.value = sf.value.replace(/[ \\t\\n]+/gu, ' ');\n });\n}\n\nfunction fieldRemoveDecomposedDiacritics(field) {\n // Raison d'\u00EAtre/motivation: \"Sir\u00E9n\" and diacriticless \"Siren\" might refer to a same surname, so this normalization\n // allows us to compare authors and avoid duplicate fields.\n field.subfields.forEach((sf) => {\n if (valuelessSubfield(sf)) {\n return;\n }\n sf.value = removeDecomposedDiacritics(sf.value);\n });\n}\n\nfunction removeDecomposedDiacritics(value = '') {\n // NB #1: Does nothing to precomposed letters. Do String.normalize('NFD') first, if you want to handle them.\n // NB #2: Finnish letters '\u00E5', '\u00E4', '\u00F6', '\u00C5', \u00C4', and '\u00D6' should be handled (=precomposed) before calling this. (= keep them as is)\n // NB #3: Calling our very own fixComposition() before this function handles both #1 and #2.\n return String(value).replace(/\\p{Diacritic}/gu, '');\n}\n\nfunction normalizeSubfieldValue(value, subfieldCode, tag) {\n // NB! For comparison of values only\n /* beslint-disable */\n value = removeCharsThatDontCarryMeaning(value, tag, subfieldCode);\n value = getSynonym(tag, subfieldCode, value); // Must be done before punc stripping and lowercasing...\n value = subfieldValueLowercase(value, subfieldCode, tag);\n\n\n // Normalize: s. = sivut = pp.\n value = normalizePartData(value, subfieldCode, tag);\n value = value.replace(/^\\[([^[\\]]+)\\]/gu, '$1');\n\n if (['130', '730'].includes(tag) && subfieldCode === 'a') {\n value = value.replace(' : ', ', '); // \"Halloween ends (elokuva, 2022)\" vs \"Halloween ends (elokuva : 2023)\"\n }\n /* beslint-enable */\n\n // Not going to do these in the foreseeable future, but keeping them here for discussion:\n // Possible normalizations include but are not limited to:\n // \u00F8 => \u00F6? Might be language dependent: 041 $a fin => \u00F6, 041 $a eng => o?\n // \u00D8 => \u00D6?\n // \u00DF => ss\n // \u00FE => th (NB! Both upper and lower case)\n // ...\n // Probably nots:\n // \u00FC => y (probably not, though this correlates with Finnish letter-to-sound rules)\n // w => v (OK for Finnish sorting in certain cases, but we are not here, are we?)\n // I guess we should use decomposed values in code here. (Not sure what composition my examples above use.)\n return value;\n}\n\nexport function cloneAndRemovePunctuation(field) {\n const clonedField = clone(field);\n if (fieldSkipNormalization(field)) {\n return clonedField;\n }\n fieldStripPunctuation(clonedField);\n fieldTrimSubfieldValues(clonedField);\n debugDev('PUNC');\n debugFieldComparison(field, clonedField);\n\n return clonedField;\n}\n\nfunction removeCharsThatDontCarryMeaning(value, tag, subfieldCode) {\n if (tag === '080') {\n return value;\n }\n\n // 3\" refers to inches, but as this is for comparison only we don't mind...\n value = value.replace(/['\u2018\u2019\"\u201E\u201C\u201D\u00AB\u00BB]/gu, ''); // MET-570 et al. Subset of https://hexdocs.pm/ex_unicode/Unicode.Category.QuoteMarks.html\n // MRA-273: Handle X00$a name initials.\n // NB #1: that we remove spaces for comparison (as it simpler), though actually space should be used. Doesn't matter as this is comparison only.\n // NB #2: we might/should eventually write a validator/fixer that adds those spaces. After that point, this expection should become obsolete.\n if (subfieldCode === 'a' && ['100', '400', '600', '700', '800'].includes(tag)) { // 400 is used in auth records. It's not a bib field at all.\n value = value.replace(/([A-Z]|\u00C5|\u00C4|\u00D6)\\. +/ugi, '$1.');\n }\n\n return value;\n}\n\nfunction normalizeField(field) {\n //sf.value = removeDecomposedDiacritics(sf.value);\n fieldStripPunctuation(field);\n fieldLowercase(field);\n fieldNormalizeControlNumbers(field); // FIN11 vs FI-MELINDA etc.\n return field;\n}\n\nexport function cloneAndNormalizeFieldForComparison(field) {\n // NB! This new field is for comparison purposes only.\n // Some of the normalizations might be considered a bit overkill for other purposes.\n const clonedField = cloneAndRemovePunctuation(field); // was only clone(field)\n\n if (fieldSkipNormalization(field)) {\n return clonedField;\n }\n clonedField.subfields.forEach((sf) => { // Do this for all fields or some fields?\n if (valuelessSubfield(sf)) {\n return;\n }\n\n sf.value = normalizeSubfieldValue(sf.value, sf.code, field.tag);\n //sf.value = normalizeForSamenessCheck(field.tag, sf.code, sf.value);\n\n\n });\n\n normalizeField(clonedField);\n fieldRemoveDecomposedDiacritics(clonedField);\n fieldSpecificHacks(clonedField);\n fieldTrimSubfieldValues(clonedField);\n\n\n debugFieldComparison(field, clonedField); // For debugging purposes only\n\n return clonedField;\n}\n\nfunction fieldSkipNormalization(field) {\n if (!field.subfields || ['018', '066', '080', '083'].includes(field.tag)) {\n return true;\n }\n return false;\n}\n\nfunction valuelessSubfield(sf) {\n return sf.value === undefined;\n}"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["field"]
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/normalizeSubfieldValueForComparison.js"],
|
|
4
|
-
"sourcesContent": ["import {nvdebug} from './utils.js';\nimport createDebugLogger from 'debug';\n\n// Normalizes at least 490$v and 773$g which contain information such as \"Raita 5\" vs \"5\", and \"Osa 3\" vs \"Osa III\".\n\nconst debug = createDebugLogger('@natlibfi/melinda-marc-record-merge-reducers:normalizeSubfieldValueForComparison');\n//const debugData = debug.extend('data');\nconst debugDev = debug.extend('dev');\n\nexport function subfieldContainsPartData(tag, subfieldCode) {\n // NB! Used by reducers' mergeSubield.js\n if (subfieldCode === 'v' && ['490', '800', '810', '811', '830'].includes(tag)) {\n return true;\n }\n if (tag === '773' && subfieldCode === 'g') {\n return true;\n }\n return false;\n}\n\nfunction splitPartData(originalValue) {\n // This a very hacky function, but cand really help it, as the the data is very iffy as well...\n // Remove punctuation and brackets:\n const value = originalValue.replace(/[-.,:; ]+$/ui, '').replace(/^\\[([0-9]+)\\]$/ui, '$1');\n\n const [year, rest] = extractYear(value);\n\n const splitPoint = rest.lastIndexOf(' '); // MRA-627: \"5, 2017\" should be split here. Think of this later on...\n if (splitPoint === -1) {\n return [undefined, year, rest];\n }\n const lhs = rest.substr(0, splitPoint);\n const rhs = rest.substr(splitPoint + 1);\n return [lhs, year, rhs];\n\n function extractYear(value) {\n // NB! Note that this is far for perfect. It cover just some very common cases...\n\n // \"2023, 3\" => [\"2023\", \"3\"]\n if (value.match(/^(?:1[89][0-9][0-9]|20[012][0-9]), (?:nro |n:o)?[1-9][0-9]{0,2}$/ui)) {\n return [value.substr(0, 4), value.substr(6)];\n }\n // \"2023/12\" => [\"2023\", \"12\"]\n if (value.match(/^(?:1[89][0-9][0-9]|20[012][0-9])[/:][1-9][0-9]{0,2}$/u)) {\n return [value.substr(0, 4), value.substr(5)];\n }\n // \"Vol. 3/2023\" => [\"2023\", \"Vol. 3\"]\n if (value.match(/^[^0-9]*[1-9][0-9]{0,2}\\/(?:1[89][0-9][0-9]|20[012][0-9])$/u)) {\n const len = value.length;\n return [value.substr(len - 4), value.substr(0, len - 5)];\n }\n\n\n return [undefined, value];\n }\n}\n\nfunction normalizePartType(originalValue) {\n if (originalValue === undefined) {\n return undefined;\n }\n const value = originalValue.toLowerCase();\n\n // Return Finnish singular nominative. Choise of language is arbitrary. This is best-ish for debug purposes...\n if (['n:o', 'no', 'nr', 'nro', 'number', 'numero', 'nummer'].includes(value)) {\n return 'numero';\n }\n if (['band', 'bd', 'h\u00E4fte', 'nide', 'osa', 'part', 'teil', 'vol', 'vol.', 'volume'].includes(value)) {\n return 'osa';\n }\n\n if (['p.', 'page', 'pages', 'pp.', 's.', 'sidor', 'sivu', 'sivut'].includes(value)) {\n return 'sivu';\n }\n\n return value;\n}\n\nconst romanNumbers = {'I': '1', 'II': '2', 'III': '3', 'IV': '4', 'V': '5', 'VI': '6', 'X': '10'};\n\nfunction normalizePartNumber(value) {\n // Should we handle all Roman numbers or some range of them?\n // There's probably a library for our purposes..\n if (value in romanNumbers) {\n const arabicValue = romanNumbers[value];\n nvdebug(` MAP ${value} to ${arabicValue}`, debugDev);\n return arabicValue;\n }\n return value.toLowerCase();\n}\n\nfunction splitAndNormalizePartData(value) {\n // This is just a stub. Does not handle eg. \"Levy 2, raita 15\"\n const [partType, partYear, partNumber] = splitPartData(value);\n //nvdebug(` LHS: '${lhs}'`, debugDev);\n //nvdebug(` RHS: '${rhs}'`, debugDev);\n return [normalizePartType(partType), partYear, normalizePartNumber(partNumber)];\n}\n\nexport function partsAgree(value1, value2, tag, subfieldCode) {\n // Note, that parts can not be normalized away, as \"2\" can agree with \"Part 2\" and \"Raita 2\" and \"Volume 2\"...\n // NB! Used by reducers' mergeSubield.js\n if (!subfieldContainsPartData(tag, subfieldCode)) {\n return false;\n }\n const [partType1, partYear1, partNumber1] = splitAndNormalizePartData(value1);\n const [partType2, partYear2, partNumber2] = splitAndNormalizePartData(value2);\n //nvdebug(`P1: ${partType1} | ${partYear1} | ${partNumber1}
|
|
4
|
+
"sourcesContent": ["import {nvdebug} from './utils.js';\nimport createDebugLogger from 'debug';\n\n// Normalizes at least 490$v and 773$g which contain information such as \"Raita 5\" vs \"5\", and \"Osa 3\" vs \"Osa III\".\n\nconst debug = createDebugLogger('@natlibfi/melinda-marc-record-merge-reducers:normalizeSubfieldValueForComparison');\n//const debugData = debug.extend('data');\nconst debugDev = debug.extend('dev');\n\nexport function subfieldContainsPartData(tag, subfieldCode) {\n // NB! Used by reducers' mergeSubield.js\n if (subfieldCode === 'v' && ['490', '800', '810', '811', '830'].includes(tag)) {\n return true;\n }\n if (tag === '773' && subfieldCode === 'g') {\n return true;\n }\n return false;\n}\n\nfunction splitPartData(originalValue) {\n // This a very hacky function, but cand really help it, as the the data is very iffy as well...\n // Remove punctuation and brackets:\n const value = originalValue.replace(/[-.,:; ]+$/ui, '').replace(/^\\[([0-9]+)\\]$/ui, '$1');\n\n const [year, rest] = extractYear(value);\n\n const splitPoint = rest.lastIndexOf(' '); // MRA-627: \"5, 2017\" should be split here. Think of this later on...\n if (splitPoint === -1) {\n return [undefined, year, rest];\n }\n const lhs = rest.substr(0, splitPoint);\n const rhs = rest.substr(splitPoint + 1);\n return [lhs, year, rhs];\n\n function extractYear(value) {\n // NB! Note that this is far for perfect. It cover just some very common cases...\n\n // \"2023, 3\" => [\"2023\", \"3\"]\n if (value.match(/^(?:1[89][0-9][0-9]|20[012][0-9]), (?:nro |n:o)?[1-9][0-9]{0,2}$/ui)) {\n return [value.substr(0, 4), value.substr(6)];\n }\n // \"2023/12\" => [\"2023\", \"12\"]\n if (value.match(/^(?:1[89][0-9][0-9]|20[012][0-9])[/:][1-9][0-9]{0,2}$/u)) {\n return [value.substr(0, 4), value.substr(5)];\n }\n // \"Vol. 3/2023\" => [\"2023\", \"Vol. 3\"]\n if (value.match(/^[^0-9]*[1-9][0-9]{0,2}\\/(?:1[89][0-9][0-9]|20[012][0-9])$/u)) {\n const len = value.length;\n return [value.substr(len - 4), value.substr(0, len - 5)];\n }\n\n\n return [undefined, value];\n }\n}\n\nfunction normalizePartType(originalValue) {\n if (originalValue === undefined) {\n return undefined;\n }\n const value = originalValue.toLowerCase();\n\n // Return Finnish singular nominative. Choise of language is arbitrary. This is best-ish for debug purposes...\n if (['n:o', 'no', 'nr', 'nro', 'number', 'numero', 'nummer'].includes(value)) {\n return 'numero';\n }\n if (['band', 'bd', 'h\u00E4fte', 'nide', 'osa', 'part', 'teil', 'vol', 'vol.', 'volume'].includes(value)) {\n return 'osa';\n }\n\n if (['p.', 'page', 'pages', 'pp.', 's.', 'sidor', 'sivu', 'sivut'].includes(value)) {\n return 'sivu';\n }\n\n return value;\n}\n\nconst romanNumbers = {'I': '1', 'II': '2', 'III': '3', 'IV': '4', 'V': '5', 'VI': '6', 'X': '10'};\n\nfunction normalizePartNumber(value) {\n // Should we handle all Roman numbers or some range of them?\n // There's probably a library for our purposes..\n if (value in romanNumbers) {\n const arabicValue = romanNumbers[value];\n nvdebug(` MAP ${value} to ${arabicValue}`, debugDev);\n return arabicValue;\n }\n return value.toLowerCase();\n}\n\nfunction splitAndNormalizePartData(value) {\n // This is just a stub. Does not handle eg. \"Levy 2, raita 15\"\n const [partType, partYear, partNumber] = splitPartData(value);\n //nvdebug(` LHS: '${lhs}'`, debugDev);\n //nvdebug(` RHS: '${rhs}'`, debugDev);\n return [normalizePartType(partType), partYear, normalizePartNumber(partNumber)];\n}\n\nexport function partsAgree(value1, value2, tag, subfieldCode) {\n // Note, that parts can not be normalized away, as \"2\" can agree with \"Part 2\" and \"Raita 2\" and \"Volume 2\"...\n // NB! Used by reducers' mergeSubield.js\n if (!subfieldContainsPartData(tag, subfieldCode)) {\n return false;\n }\n const [partType1, partYear1, partNumber1] = splitAndNormalizePartData(value1);\n const [partType2, partYear2, partNumber2] = splitAndNormalizePartData(value2);\n //nvdebug(`P1: ${partType1} | ${partYear1} | ${partNumber1}`, debugDev);\n //nvdebug(`P2: ${partType2} | ${partYear2} | ${partNumber2}`, debugDev);\n if (partNumber1 !== partNumber2) {\n return false;\n }\n if (partType1 !== undefined && partType2 !== undefined && partType1 !== partType2) {\n return false;\n }\n if (partYear1 !== undefined && partYear2 !== undefined && partYear1 !== partYear2) {\n return false;\n }\n\n\n return true;\n}\n\nexport function normalizePartData(value, subfieldCode, tag) {\n // This is for normalizing values for equality comparison only!\n if (!subfieldContainsPartData(tag, subfieldCode)) {\n return value;\n }\n\n const [partType, partYear, partNumber] = splitAndNormalizePartData(value);\n if (partType === undefined) {\n if (partYear === undefined) {\n return partNumber;\n }\n return `${partNumber}/${partYear}`;\n }\n if (partYear === undefined) {\n return `${partType} ${partNumber}`;\n }\n return `${partType} ${partNumber}/${partYear}`;\n}\n"],
|
|
5
5
|
"mappings": "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",
|
|
6
6
|
"names": ["value"]
|
|
7
7
|
}
|
|
@@ -136,7 +136,7 @@ export function removeWorsePrepubField594s(record) {
|
|
|
136
136
|
nonBest.forEach((field) => record.removeField(field));
|
|
137
137
|
}
|
|
138
138
|
export function isEnnakkotietoSubfield(subfield) {
|
|
139
|
-
if (
|
|
139
|
+
if (!["g", "9", "7"].includes(subfield.code)) {
|
|
140
140
|
return false;
|
|
141
141
|
}
|
|
142
142
|
if (subfield.value.length <= 13) {
|