@natlibfi/marc-record-validators-melinda 11.3.1 → 11.3.2-alpha.2
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/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/melindaCustomMergeFields.json +5120 -0
- package/dist/merge-fields/config.json +83 -0
- package/dist/merge-fields/controlSubfields.js +278 -0
- package/dist/merge-fields/controlSubfields.js.map +1 -0
- package/dist/merge-fields/counterpartField.js +674 -0
- package/dist/merge-fields/counterpartField.js.map +1 -0
- package/dist/merge-fields/index.js +76 -0
- package/dist/merge-fields/index.js.map +1 -0
- package/dist/merge-fields/mergableIndicator.js +95 -0
- package/dist/merge-fields/mergableIndicator.js.map +1 -0
- package/dist/merge-fields/mergableTag.js +33 -0
- package/dist/merge-fields/mergableTag.js.map +1 -0
- package/dist/merge-fields/mergeConstraints.js +1225 -0
- package/dist/merge-fields/mergeConstraints.js.map +1 -0
- package/dist/merge-fields/mergeField.js +190 -0
- package/dist/merge-fields/mergeField.js.map +1 -0
- package/dist/merge-fields/mergeIndicator.js +171 -0
- package/dist/merge-fields/mergeIndicator.js.map +1 -0
- package/dist/merge-fields/mergeOrAddPostprocess.js +57 -0
- package/dist/merge-fields/mergeOrAddPostprocess.js.map +1 -0
- package/dist/merge-fields/mergeOrAddSubfield.js +203 -0
- package/dist/merge-fields/mergeOrAddSubfield.js.map +1 -0
- package/dist/merge-fields/mergeSubfield.js +277 -0
- package/dist/merge-fields/mergeSubfield.js.map +1 -0
- package/dist/merge-fields/removeDuplicateSubfields.js +48 -0
- package/dist/merge-fields/removeDuplicateSubfields.js.map +1 -0
- package/dist/merge-fields/worldKnowledge.js +98 -0
- package/dist/merge-fields/worldKnowledge.js.map +1 -0
- package/dist/merge-fields.spec.js +51 -0
- package/dist/merge-fields.spec.js.map +1 -0
- package/dist/subfield6Utils.js +16 -1
- package/dist/subfield6Utils.js.map +1 -1
- package/dist/utils.js +108 -0
- package/dist/utils.js.map +1 -1
- package/package.json +6 -6
- package/src/index.js +3 -1
- package/src/melindaCustomMergeFields.json +5120 -0
- package/src/merge-fields/config.json +83 -0
- package/src/merge-fields/controlSubfields.js +307 -0
- package/src/merge-fields/counterpartField.js +736 -0
- package/src/merge-fields/index.js +69 -0
- package/src/merge-fields/mergableIndicator.js +90 -0
- package/src/merge-fields/mergableTag.js +89 -0
- package/src/merge-fields/mergeConstraints.js +309 -0
- package/src/merge-fields/mergeField.js +187 -0
- package/src/merge-fields/mergeIndicator.js +185 -0
- package/src/merge-fields/mergeOrAddPostprocess.js +56 -0
- package/src/merge-fields/mergeOrAddSubfield.js +218 -0
- package/src/merge-fields/mergeSubfield.js +306 -0
- package/src/merge-fields/removeDuplicateSubfields.js +50 -0
- package/src/merge-fields/worldKnowledge.js +104 -0
- package/src/merge-fields.spec.js +52 -0
- package/src/subfield6Utils.js +14 -1
- package/src/utils.js +119 -0
- package/test-fixtures/merge-fields/f01/expectedResult.json +11 -0
- package/test-fixtures/merge-fields/f01/metadata.json +5 -0
- package/test-fixtures/merge-fields/f01/record.json +13 -0
- package/test-fixtures/merge-fields/f02/expectedResult.json +14 -0
- package/test-fixtures/merge-fields/f02/metadata.json +6 -0
- package/test-fixtures/merge-fields/f02/record.json +16 -0
- package/test-fixtures/merge-fields/f03/expectedResult.json +17 -0
- package/test-fixtures/merge-fields/f03/metadata.json +7 -0
- package/test-fixtures/merge-fields/f03/record.json +23 -0
- package/test-fixtures/merge-fields/f04/expectedResult.json +14 -0
- package/test-fixtures/merge-fields/f04/metadata.json +5 -0
- package/test-fixtures/merge-fields/f04/record.json +19 -0
- package/test-fixtures/merge-fields/v01/expectedResult.json +6 -0
- package/test-fixtures/merge-fields/v01/metadata.json +5 -0
- package/test-fixtures/merge-fields/v01/record.json +13 -0
- package/test-fixtures/merge-fields/v02/expectedResult.json +4 -0
- package/test-fixtures/merge-fields/v02/metadata.json +5 -0
- package/test-fixtures/merge-fields/v02/record.json +13 -0
- package/test-fixtures/merge-fields/v03/expectedResult.json +6 -0
- package/test-fixtures/merge-fields/v03/metadata.json +6 -0
- package/test-fixtures/merge-fields/v03/record.json +16 -0
- package/test-fixtures/merge-fields/v04/expectedResult.json +4 -0
- package/test-fixtures/merge-fields/v04/metadata.json +6 -0
- package/test-fixtures/merge-fields/v04/record.json +16 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mergeOrAddPostprocess.js","names":["_punctuation","require","_removeDuplicateSubfields","_sortSubfields","_sortRelatorTerms","postprocessBaseRecord","base","fields","forEach","field","merged","fieldRemoveDuplicateSubfields","fieldFixPunctuation","sortAdjacentSubfields","sortAdjacentESubfields","useExternalEndPunctuation","added","removeDeletedFields","record","filter","f","deleted","postprocessRecords","source"],"sources":["../../src/merge-fields/mergeOrAddPostprocess.js"],"sourcesContent":["// This field should be renamed, as it is called also from outside megre.\n\n//import {MarcRecord} from '@natlibfi/marc-record';\nimport {fieldFixPunctuation} from '../punctuation2';\nimport {fieldRemoveDuplicateSubfields} from './removeDuplicateSubfields';\nimport {sortAdjacentSubfields} from '../sortSubfields';\nimport {sortAdjacentESubfields} from '../sortRelatorTerms';\n\nfunction postprocessBaseRecord(base) {\n\n base.fields.forEach(field => {\n // NB! Relator terms are now expanded and translated already at preprocess stage!\n\n // remove merge-specific information:\n if (field.merged) { // eslint-disable-line functional/no-conditional-statements\n // Field level ideas about things that could be done here:\n // - Fix indicators?\n // Record level fixes should be implemented as validators/fixers\n // in marc-record-validators-melinda and ust called from here.\n fieldRemoveDuplicateSubfields(field);\n fieldFixPunctuation(field); // NB! This will fix only fields with merged content\n sortAdjacentSubfields(field); // Put the added $e subfield to proper places.\n sortAdjacentESubfields(field); // Sort $e subfields with each other\n fieldFixPunctuation(field);\n\n delete field.merged; // eslint-disable-line functional/immutable-data\n }\n\n if (field.useExternalEndPunctuation) { // eslint-disable-line functional/no-conditional-statements\n delete field.useExternalEndPunctuation; // eslint-disable-line functional/immutable-data\n }\n\n if (field.added) { // eslint-disable-line functional/no-conditional-statements\n delete field.added; // eslint-disable-line functional/immutable-data\n }\n\n /*\n if (field.deleted) { // eslint-disable-line functional/no-conditional-statements\n delete field.deleted; // eslint-disable-line functional/immutable-data\n }\n*/\n\n });\n}\n\n\nfunction removeDeletedFields(record) {\n // remove fields that are marked as deleted:\n record.fields = record.fields.filter(f => !f.deleted); // eslint-disable-line functional/immutable-data\n}\n\n\nexport function postprocessRecords(base, source) {\n postprocessBaseRecord(base);\n removeDeletedFields(source); // So that we may know what was used, and what not.\n}\n"],"mappings":";;;;;;AAGA,IAAAA,YAAA,GAAAC,OAAA;AACA,IAAAC,yBAAA,GAAAD,OAAA;AACA,IAAAE,cAAA,GAAAF,OAAA;AACA,IAAAG,iBAAA,GAAAH,OAAA;AANA;;AAEA;;AAMA,SAASI,qBAAqBA,CAACC,IAAI,EAAE;EAEnCA,IAAI,CAACC,MAAM,CAACC,OAAO,CAACC,KAAK,IAAI;IAC3B;;IAEA;IACA,IAAIA,KAAK,CAACC,MAAM,EAAE;MAAE;MAClB;MACA;MACA;MACA;MACA,IAAAC,uDAA6B,EAACF,KAAK,CAAC;MACpC,IAAAG,gCAAmB,EAACH,KAAK,CAAC,CAAC,CAAC;MAC5B,IAAAI,oCAAqB,EAACJ,KAAK,CAAC,CAAC,CAAC;MAC9B,IAAAK,wCAAsB,EAACL,KAAK,CAAC,CAAC,CAAC;MAC/B,IAAAG,gCAAmB,EAACH,KAAK,CAAC;MAE1B,OAAOA,KAAK,CAACC,MAAM,CAAC,CAAC;IACvB;IAEA,IAAID,KAAK,CAACM,yBAAyB,EAAE;MAAE;MACrC,OAAON,KAAK,CAACM,yBAAyB,CAAC,CAAC;IAC1C;IAEA,IAAIN,KAAK,CAACO,KAAK,EAAE;MAAE;MACjB,OAAOP,KAAK,CAACO,KAAK,CAAC,CAAC;IACtB;;IAEA;AACJ;AACA;AACA;AACA;EAEE,CAAC,CAAC;AACJ;AAGA,SAASC,mBAAmBA,CAACC,MAAM,EAAE;EACnC;EACAA,MAAM,CAACX,MAAM,GAAGW,MAAM,CAACX,MAAM,CAACY,MAAM,CAACC,CAAC,IAAI,CAACA,CAAC,CAACC,OAAO,CAAC,CAAC,CAAC;AACzD;AAGO,SAASC,kBAAkBA,CAAChB,IAAI,EAAEiB,MAAM,EAAE;EAC/ClB,qBAAqB,CAACC,IAAI,CAAC;EAC3BW,mBAAmB,CAACM,MAAM,CAAC,CAAC,CAAC;AAC/B","ignoreList":[]}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.mergeOrAddSubfield = mergeOrAddSubfield;
|
|
7
|
+
var _debug = _interopRequireDefault(require("debug"));
|
|
8
|
+
var _normalizeFieldForComparison = require("../normalizeFieldForComparison.js");
|
|
9
|
+
var _normalizeIdentifiers = require("../normalize-identifiers");
|
|
10
|
+
var _utils = require("../utils.js");
|
|
11
|
+
var _mergeSubfield = require("./mergeSubfield.js");
|
|
12
|
+
var _sortSubfields = require("../sortSubfields");
|
|
13
|
+
var _worldKnowledge = require("./worldKnowledge.js");
|
|
14
|
+
var _subfield6Utils = require("../subfield6Utils.js");
|
|
15
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
16
|
+
//'./sortSubfields.js';
|
|
17
|
+
|
|
18
|
+
const debug = (0, _debug.default)('@natlibfi/melinda-marc-record-merge-reducers:mergeOrAddSubfield');
|
|
19
|
+
//const debugData = debug.extend('data');
|
|
20
|
+
const debugDev = debug.extend('dev');
|
|
21
|
+
function catalogingSourceModifyingAgencyCandIsOriginalCatalogingSourceAgencyInTargetField(targetField, candSubfieldData) {
|
|
22
|
+
if (targetField.tag !== '040' || candSubfieldData.code !== 'd') {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
(0, _utils.nvdebug)(`${(0, _utils.fieldToString)(targetField)} vs $d ${candSubfieldData.originalValue}}`, debugDev);
|
|
26
|
+
// Add hard-coded exceptions here
|
|
27
|
+
if (targetField.subfields.some(sf => sf.code === 'a' && sf.value === candSubfieldData.originalValue)) {
|
|
28
|
+
(0, _utils.nvdebug)('040‡d matched 040‡a', debugDev);
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
function ennakkotietoInSubfieldG(candSubfieldData) {
|
|
34
|
+
if (candSubfieldData.code === 'g' && ['ENNAKKOTIETO.', 'ENNAKKOTIETO'].includes(candSubfieldData.originalValue)) {
|
|
35
|
+
// Skip just ‡g subfield or the whole field?
|
|
36
|
+
// We decided to skip just this subfield. We want at least $0 and maybe even more from ennakkotieto.
|
|
37
|
+
debugDev('Skip ‡g ENNAKKOTIETO.');
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
function mergeOrAddSubfieldNotRequiredSpecialCases(targetField, candSubfieldData) {
|
|
43
|
+
// Don't bring WHATEVER<KEEP> from source 7XX to base 1XX.
|
|
44
|
+
// Exceptionally we can merge <KEEP>ed 7XX with un-<KEEP>ed 1XX as 1XX should not use <KEEP>s.
|
|
45
|
+
if (targetField.tag.charAt(0) === '1' && candSubfieldData.tag.charAt(0) === '7' && candSubfieldData.code === '9' && candSubfieldData.originalValue.match(/<KEEP>/u)) {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Don't add 264$b 'Kustannuspaikka tuntematon' etc
|
|
50
|
+
if (!(0, _worldKnowledge.valueCarriesMeaning)(targetField.tag, candSubfieldData.code, candSubfieldData.normalizedValue)) {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Don't add $0 subfields that mean the same even if they look different:
|
|
55
|
+
const alephIdentifierType = (0, _normalizeIdentifiers.normalizeAs)(targetField.tag, candSubfieldData.code);
|
|
56
|
+
if (alephIdentifierType !== undefined) {
|
|
57
|
+
const normalizedSubfieldValue = (0, _normalizeIdentifiers.normalizeControlSubfieldValue)(candSubfieldData.originalValue, alephIdentifierType);
|
|
58
|
+
if (targetField.subfields.some(sf => (0, _normalizeIdentifiers.normalizeControlSubfieldValue)(sf.value) === normalizedSubfieldValue && sf.code === candSubfieldData.code)) {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
function skipNormalizedComparison(tag, subfieldCode, subfieldValue) {
|
|
65
|
+
if (tag === '020' && subfieldCode === 'a') {
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
// Hackish: we want 'ntamo' to win 'Ntamo'...
|
|
69
|
+
// If there are other similar excepting put them into an array.
|
|
70
|
+
if (['110', '610', '710', '810'].includes(tag) && subfieldCode === 'a' && subfieldValue.substring(0, 5) === 'ntamo') {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
function mergeOrAddSubfieldNotRequired(targetField, candSubfieldData) {
|
|
76
|
+
if (catalogingSourceModifyingAgencyCandIsOriginalCatalogingSourceAgencyInTargetField(targetField, candSubfieldData) || ennakkotietoInSubfieldG(candSubfieldData)) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
if (mergeOrAddSubfieldNotRequiredSpecialCases(targetField, candSubfieldData)) {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
const relevantTargetSubfields = targetField.subfields.filter(sf => sf.code === candSubfieldData.code);
|
|
83
|
+
// Target field does not have this subfield yet:
|
|
84
|
+
if (relevantTargetSubfields.length === 0) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
(0, _utils.nvdebug)(` Look for identical subfields in '${(0, _utils.fieldToString)(targetField)}' using`, debugDev);
|
|
88
|
+
(0, _utils.nvdebug)(` ORIG. ‡${candSubfieldData.code} ${candSubfieldData.originalValue}`, debugDev);
|
|
89
|
+
(0, _utils.nvdebug)(` NO-PUNC ‡${candSubfieldData.code} ${candSubfieldData.punctuationlessValue}`, debugDev);
|
|
90
|
+
if (relevantTargetSubfields.some(sf => sf.code === candSubfieldData.code && sf.value === candSubfieldData.originalValue)) {
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
if (relevantTargetSubfields.some(sf => sf.code === candSubfieldData.code && sf.value === candSubfieldData.punctuationlessValue)) {
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
if (!skipNormalizedComparison(targetField.tag, candSubfieldData.code, candSubfieldData.originalValue)) {
|
|
97
|
+
const normalizedTargetField = (0, _normalizeFieldForComparison.cloneAndNormalizeFieldForComparison)(targetField);
|
|
98
|
+
(0, _utils.nvdebug)(` Look for identical normalized subfields in '${(0, _utils.fieldToString)(normalizedTargetField)}'`, debugDev);
|
|
99
|
+
(0, _utils.nvdebug)(` NO-PUNC ‡${candSubfieldData.code} ${candSubfieldData.normalizedValue})`, debugDev);
|
|
100
|
+
if (normalizedTargetField.subfields.some(sf => sf.code === candSubfieldData.code && sf.value === candSubfieldData.normalizedValue)) {
|
|
101
|
+
// Subfield with identical normalized value exists. Do nothing.
|
|
102
|
+
// Not ideal 382‡n subfields, I guess... Nor 505‡trg repetitions... These need to be fixed...
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return false; // (note that this is a double negation: not required is false)
|
|
107
|
+
}
|
|
108
|
+
function addSubfield(targetField, candSubfield) {
|
|
109
|
+
(0, _utils.nvdebug)(` Added subfield '${(0, _utils.subfieldToString)(candSubfield)}' to field`, debugDev);
|
|
110
|
+
// Add subfield to the end of all subfields. NB! Implement a separate function that does this + subfield reordering somehow...
|
|
111
|
+
targetField.subfields.push(candSubfield); // eslint-disable-line functional/immutable-data
|
|
112
|
+
|
|
113
|
+
targetField.merged = 1; // eslint-disable-line functional/immutable-data
|
|
114
|
+
|
|
115
|
+
setPunctuationFlag(targetField, candSubfield);
|
|
116
|
+
(0, _sortSubfields.sortAdjacentSubfields)(targetField);
|
|
117
|
+
}
|
|
118
|
+
function setPunctuationFlag(field, addedSubfield) {
|
|
119
|
+
if ((0, _utils.isControlSubfieldCode)(addedSubfield.code)) {
|
|
120
|
+
// These are never punctuation related
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
field.useExternalEndPunctuation = 1; // eslint-disable-line functional/immutable-data
|
|
124
|
+
}
|
|
125
|
+
function resetPaired880(candFieldPair880, targetField, punctlessCandSubfield) {
|
|
126
|
+
// No relevant:
|
|
127
|
+
if (punctlessCandSubfield.code !== '6') {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
if (targetField.tag === '880') {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
// NB! $6 comes first:
|
|
134
|
+
if (candFieldPair880 === undefined || !candFieldPair880.subfields || candFieldPair880.subfields[0].code !== '6') {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
(0, _utils.nvdebug)(`880 contents: ${(0, _utils.fieldToString)(candFieldPair880)}`, debugDev);
|
|
138
|
+
(0, _subfield6Utils.resetSubfield6Tag)(candFieldPair880.subfields[0], targetField.tag);
|
|
139
|
+
}
|
|
140
|
+
function mergeOrAddSubfield(targetField, candSubfieldData, candFieldPairs880 = []) {
|
|
141
|
+
const candSubfieldAsString = `${candSubfieldData.code} ${candSubfieldData.originalValue}`;
|
|
142
|
+
(0, _utils.nvdebug)(` Q: mergeOrAddSubfield '${candSubfieldAsString}'\n with field '${(0, _utils.fieldToString)(targetField)}'?`, debugDev);
|
|
143
|
+
if (mergeOrAddSubfieldNotRequired(targetField, candSubfieldData)) {
|
|
144
|
+
(0, _utils.nvdebug)(` A: No. No need to merge nor to add the subfield '${candSubfieldAsString}'`, debugDev);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const candSubfield = {
|
|
148
|
+
'code': candSubfieldData.code,
|
|
149
|
+
'value': candSubfieldData.punctuationlessValue
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
// Currently only for X00$d 1984- => 1984-2000 type of changes, where source version is better that what base has.
|
|
153
|
+
// It all other cases the original subfield is kept.
|
|
154
|
+
const original = (0, _utils.fieldToString)(targetField);
|
|
155
|
+
if ((0, _mergeSubfield.mergeSubfield)(targetField, candSubfield)) {
|
|
156
|
+
// We might need the normalizedCandSubfield later on
|
|
157
|
+
mergeSubfieldPostprocessor();
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Subfield codes missing from the original record can be added by default:
|
|
162
|
+
if (addSubfieldWithPreviouslyUnseenSubfieldCode()) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// melindaCustomMergeFields.json tells us whether the subfield is repeatable or not:
|
|
167
|
+
if ((0, _utils.subfieldIsRepeatable)(targetField.tag, candSubfield.code)) {
|
|
168
|
+
// We don't want to add multiple, say, 260$c
|
|
169
|
+
if (['260', '264'].includes(targetField.tag)) {
|
|
170
|
+
(0, _utils.nvdebug)(` A: Exceptionally skip repeatable existing subfield '${(0, _utils.subfieldToString)(candSubfield)}'`, debugDev);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
(0, _utils.nvdebug)(` A: Yes. Add repeatable subfield '${(0, _utils.subfieldToString)(candSubfield)}'`, debugDev);
|
|
174
|
+
targetField.merged = 1; // eslint-disable-line functional/immutable-data
|
|
175
|
+
setPunctuationFlag(targetField, candSubfield);
|
|
176
|
+
addSubfield(targetField, candSubfield);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
(0, _utils.nvdebug)(` A: No. Non-repeatable subfield '${(0, _utils.subfieldToString)(candSubfield)}'`, debugDev);
|
|
180
|
+
return;
|
|
181
|
+
function mergeSubfieldPostprocessor() {
|
|
182
|
+
if (original !== (0, _utils.fieldToString)(targetField)) {
|
|
183
|
+
(0, _utils.nvdebug)(` A: Merge. Subfield '${candSubfieldAsString}' replaces the original subfield.`, debugDev);
|
|
184
|
+
targetField.merged = 1; // eslint-disable-line functional/immutable-data
|
|
185
|
+
setPunctuationFlag(targetField, candSubfield);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
(0, _utils.nvdebug)(` A: No. Field ${original} already had the same or a synonymous or a better merge candidate than our subfield '${candSubfieldAsString}'.`, debugDev);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
function addSubfieldWithPreviouslyUnseenSubfieldCode() {
|
|
192
|
+
if (!(0, _utils.fieldHasSubfield)(targetField, candSubfield.code)) {
|
|
193
|
+
(0, _utils.nvdebug)(` A: Yes. Add previously unseen subfield '${(0, _utils.subfieldToString)(candSubfield)}'`, debugDev);
|
|
194
|
+
targetField.merged = 1; // eslint-disable-line functional/immutable-data
|
|
195
|
+
setPunctuationFlag(targetField, candSubfield);
|
|
196
|
+
candFieldPairs880.forEach(pair => resetPaired880(pair, targetField, candSubfield));
|
|
197
|
+
addSubfield(targetField, candSubfield);
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
//# sourceMappingURL=mergeOrAddSubfield.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mergeOrAddSubfield.js","names":["_debug","_interopRequireDefault","require","_normalizeFieldForComparison","_normalizeIdentifiers","_utils","_mergeSubfield","_sortSubfields","_worldKnowledge","_subfield6Utils","e","__esModule","default","debug","createDebugLogger","debugDev","extend","catalogingSourceModifyingAgencyCandIsOriginalCatalogingSourceAgencyInTargetField","targetField","candSubfieldData","tag","code","nvdebug","fieldToString","originalValue","subfields","some","sf","value","ennakkotietoInSubfieldG","includes","mergeOrAddSubfieldNotRequiredSpecialCases","charAt","match","valueCarriesMeaning","normalizedValue","alephIdentifierType","normalizeAs","undefined","normalizedSubfieldValue","normalizeControlSubfieldValue","skipNormalizedComparison","subfieldCode","subfieldValue","substring","mergeOrAddSubfieldNotRequired","relevantTargetSubfields","filter","length","punctuationlessValue","normalizedTargetField","cloneAndNormalizeFieldForComparison","addSubfield","candSubfield","subfieldToString","push","merged","setPunctuationFlag","sortAdjacentSubfields","field","addedSubfield","isControlSubfieldCode","useExternalEndPunctuation","resetPaired880","candFieldPair880","punctlessCandSubfield","resetSubfield6Tag","mergeOrAddSubfield","candFieldPairs880","candSubfieldAsString","original","mergeSubfield","mergeSubfieldPostprocessor","addSubfieldWithPreviouslyUnseenSubfieldCode","subfieldIsRepeatable","fieldHasSubfield","forEach","pair"],"sources":["../../src/merge-fields/mergeOrAddSubfield.js"],"sourcesContent":["import createDebugLogger from 'debug';\nimport {cloneAndNormalizeFieldForComparison} from '../normalizeFieldForComparison.js';\nimport {normalizeAs, normalizeControlSubfieldValue} from '../normalize-identifiers';\nimport {fieldHasSubfield, fieldToString, isControlSubfieldCode, nvdebug, subfieldIsRepeatable, subfieldToString} from '../utils.js';\nimport {mergeSubfield} from './mergeSubfield.js';\nimport {sortAdjacentSubfields} from '../sortSubfields'; //'./sortSubfields.js';\n\nimport {valueCarriesMeaning} from './worldKnowledge.js';\nimport {resetSubfield6Tag} from '../subfield6Utils.js';\n\nconst debug = createDebugLogger('@natlibfi/melinda-marc-record-merge-reducers:mergeOrAddSubfield');\n//const debugData = debug.extend('data');\nconst debugDev = debug.extend('dev');\n\nfunction catalogingSourceModifyingAgencyCandIsOriginalCatalogingSourceAgencyInTargetField(targetField, candSubfieldData) {\n if (targetField.tag !== '040' || candSubfieldData.code !== 'd') {\n return false;\n }\n nvdebug(`${fieldToString(targetField)} vs $d ${candSubfieldData.originalValue}}`, debugDev);\n // Add hard-coded exceptions here\n if (targetField.subfields.some(sf => sf.code === 'a' && sf.value === candSubfieldData.originalValue)) {\n nvdebug('040‡d matched 040‡a', debugDev);\n return true;\n }\n return false;\n}\n\nfunction ennakkotietoInSubfieldG(candSubfieldData) {\n if (candSubfieldData.code === 'g' && ['ENNAKKOTIETO.', 'ENNAKKOTIETO'].includes(candSubfieldData.originalValue)) {\n // Skip just ‡g subfield or the whole field?\n // We decided to skip just this subfield. We want at least $0 and maybe even more from ennakkotieto.\n debugDev('Skip ‡g ENNAKKOTIETO.');\n return true;\n }\n return false;\n}\n\n\nfunction mergeOrAddSubfieldNotRequiredSpecialCases(targetField, candSubfieldData) {\n\n // Don't bring WHATEVER<KEEP> from source 7XX to base 1XX.\n // Exceptionally we can merge <KEEP>ed 7XX with un-<KEEP>ed 1XX as 1XX should not use <KEEP>s.\n if (targetField.tag.charAt(0) === '1' && candSubfieldData.tag.charAt(0) === '7' && candSubfieldData.code === '9' && candSubfieldData.originalValue.match(/<KEEP>/u)) {\n return true;\n }\n\n // Don't add 264$b 'Kustannuspaikka tuntematon' etc\n if (!valueCarriesMeaning(targetField.tag, candSubfieldData.code, candSubfieldData.normalizedValue)) {\n return true;\n }\n\n\n // Don't add $0 subfields that mean the same even if they look different:\n const alephIdentifierType = normalizeAs(targetField.tag, candSubfieldData.code);\n if (alephIdentifierType !== undefined) {\n const normalizedSubfieldValue = normalizeControlSubfieldValue(candSubfieldData.originalValue, alephIdentifierType);\n if (targetField.subfields.some(sf => normalizeControlSubfieldValue(sf.value) === normalizedSubfieldValue && sf.code === candSubfieldData.code)) {\n return true;\n }\n }\n return false;\n}\n\n\nfunction skipNormalizedComparison(tag, subfieldCode, subfieldValue) {\n if (tag === '020' && subfieldCode === 'a') {\n return true;\n }\n // Hackish: we want 'ntamo' to win 'Ntamo'...\n // If there are other similar excepting put them into an array.\n if (['110', '610', '710', '810'].includes(tag) && subfieldCode === 'a' && subfieldValue.substring(0, 5) === 'ntamo') {\n return true;\n }\n return false;\n}\n\nfunction mergeOrAddSubfieldNotRequired(targetField, candSubfieldData) {\n if (catalogingSourceModifyingAgencyCandIsOriginalCatalogingSourceAgencyInTargetField(targetField, candSubfieldData) || ennakkotietoInSubfieldG(candSubfieldData)) {\n return true;\n }\n\n if (mergeOrAddSubfieldNotRequiredSpecialCases(targetField, candSubfieldData)) {\n return true;\n }\n\n const relevantTargetSubfields = targetField.subfields.filter(sf => sf.code === candSubfieldData.code);\n // Target field does not have this subfield yet:\n if (relevantTargetSubfields.length === 0) {\n return false;\n }\n nvdebug(` Look for identical subfields in '${fieldToString(targetField)}' using`, debugDev);\n nvdebug(` ORIG. ‡${candSubfieldData.code} ${candSubfieldData.originalValue}`, debugDev);\n nvdebug(` NO-PUNC ‡${candSubfieldData.code} ${candSubfieldData.punctuationlessValue}`, debugDev);\n if (relevantTargetSubfields.some(sf => sf.code === candSubfieldData.code && sf.value === candSubfieldData.originalValue)) {\n return true;\n }\n if (relevantTargetSubfields.some(sf => sf.code === candSubfieldData.code && sf.value === candSubfieldData.punctuationlessValue)) {\n return true;\n }\n\n if (!skipNormalizedComparison(targetField.tag, candSubfieldData.code, candSubfieldData.originalValue)) {\n const normalizedTargetField = cloneAndNormalizeFieldForComparison(targetField);\n nvdebug(` Look for identical normalized subfields in '${fieldToString(normalizedTargetField)}'`, debugDev);\n nvdebug(` NO-PUNC ‡${candSubfieldData.code} ${candSubfieldData.normalizedValue})`, debugDev);\n\n if (normalizedTargetField.subfields.some(sf => sf.code === candSubfieldData.code && sf.value === candSubfieldData.normalizedValue)) {\n // Subfield with identical normalized value exists. Do nothing.\n // Not ideal 382‡n subfields, I guess... Nor 505‡trg repetitions... These need to be fixed...\n return true;\n }\n }\n\n return false; // (note that this is a double negation: not required is false)\n}\n\nfunction addSubfield(targetField, candSubfield) {\n nvdebug(` Added subfield '${subfieldToString(candSubfield)}' to field`, debugDev);\n // Add subfield to the end of all subfields. NB! Implement a separate function that does this + subfield reordering somehow...\n targetField.subfields.push(candSubfield); // eslint-disable-line functional/immutable-data\n\n targetField.merged = 1; // eslint-disable-line functional/immutable-data\n\n setPunctuationFlag(targetField, candSubfield);\n sortAdjacentSubfields(targetField);\n\n}\n\nfunction setPunctuationFlag(field, addedSubfield) {\n if (isControlSubfieldCode(addedSubfield.code)) { // These are never punctuation related\n return;\n }\n field.useExternalEndPunctuation = 1; // eslint-disable-line functional/immutable-data\n}\n\n\nfunction resetPaired880(candFieldPair880, targetField, punctlessCandSubfield) {\n // No relevant:\n if (punctlessCandSubfield.code !== '6') {\n return;\n }\n if (targetField.tag === '880') {\n return;\n }\n // NB! $6 comes first:\n if (candFieldPair880 === undefined || !candFieldPair880.subfields || candFieldPair880.subfields[0].code !== '6') {\n return;\n\n }\n nvdebug(`880 contents: ${fieldToString(candFieldPair880)}`, debugDev);\n resetSubfield6Tag(candFieldPair880.subfields[0], targetField.tag);\n}\n\nexport function mergeOrAddSubfield(targetField, candSubfieldData, candFieldPairs880 = []) {\n\n const candSubfieldAsString = `${candSubfieldData.code} ${candSubfieldData.originalValue}`;\n\n nvdebug(` Q: mergeOrAddSubfield '${candSubfieldAsString}'\\n with field '${fieldToString(targetField)}'?`, debugDev);\n if (mergeOrAddSubfieldNotRequired(targetField, candSubfieldData)) {\n nvdebug(` A: No. No need to merge nor to add the subfield '${candSubfieldAsString}'`, debugDev);\n return;\n }\n\n const candSubfield = {'code': candSubfieldData.code, 'value': candSubfieldData.punctuationlessValue};\n\n // Currently only for X00$d 1984- => 1984-2000 type of changes, where source version is better that what base has.\n // It all other cases the original subfield is kept.\n const original = fieldToString(targetField);\n\n if (mergeSubfield(targetField, candSubfield)) { // We might need the normalizedCandSubfield later on\n mergeSubfieldPostprocessor();\n return;\n }\n\n // Subfield codes missing from the original record can be added by default:\n if (addSubfieldWithPreviouslyUnseenSubfieldCode()) {\n return;\n }\n\n // melindaCustomMergeFields.json tells us whether the subfield is repeatable or not:\n if (subfieldIsRepeatable(targetField.tag, candSubfield.code)) {\n // We don't want to add multiple, say, 260$c\n if (['260', '264'].includes(targetField.tag)) {\n nvdebug(` A: Exceptionally skip repeatable existing subfield '${subfieldToString(candSubfield)}'`, debugDev);\n return;\n }\n nvdebug(` A: Yes. Add repeatable subfield '${subfieldToString(candSubfield)}'`, debugDev);\n targetField.merged = 1; // eslint-disable-line functional/immutable-data\n setPunctuationFlag(targetField, candSubfield);\n addSubfield(targetField, candSubfield);\n return;\n }\n\n nvdebug(` A: No. Non-repeatable subfield '${subfieldToString(candSubfield)}'`, debugDev);\n return;\n\n function mergeSubfieldPostprocessor() {\n if (original !== fieldToString(targetField)) {\n nvdebug(` A: Merge. Subfield '${candSubfieldAsString}' replaces the original subfield.`, debugDev);\n targetField.merged = 1; // eslint-disable-line functional/immutable-data\n setPunctuationFlag(targetField, candSubfield);\n return;\n }\n nvdebug(` A: No. Field ${original} already had the same or a synonymous or a better merge candidate than our subfield '${candSubfieldAsString}'.`, debugDev);\n return;\n }\n\n function addSubfieldWithPreviouslyUnseenSubfieldCode() {\n if (!fieldHasSubfield(targetField, candSubfield.code)) {\n nvdebug(` A: Yes. Add previously unseen subfield '${subfieldToString(candSubfield)}'`, debugDev);\n targetField.merged = 1; // eslint-disable-line functional/immutable-data\n setPunctuationFlag(targetField, candSubfield);\n candFieldPairs880.forEach(pair => resetPaired880(pair, targetField, candSubfield));\n addSubfield(targetField, candSubfield);\n return true;\n }\n return false;\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.mergeSubfield = mergeSubfield;
|
|
7
|
+
var _debug = _interopRequireDefault(require("debug"));
|
|
8
|
+
var _normalizeSubfieldValueForComparison = require("../normalizeSubfieldValueForComparison");
|
|
9
|
+
var _worldKnowledge = require("./worldKnowledge");
|
|
10
|
+
var _utils = require("../utils");
|
|
11
|
+
var _normalizeFieldForComparison = require("../normalizeFieldForComparison.js");
|
|
12
|
+
var _counterpartField = require("./counterpartField");
|
|
13
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
14
|
+
const debug = (0, _debug.default)('@natlibfi/melinda-marc-record-merge-reducers:mergeSubfield');
|
|
15
|
+
//const debugData = debug.extend('data');
|
|
16
|
+
const debugDev = debug.extend('dev');
|
|
17
|
+
|
|
18
|
+
// NB! These are X00 specific. Should we somehow parametrize them?
|
|
19
|
+
const onlyBirthYear = /^[1-9][0-9]*-[,.]?$/u;
|
|
20
|
+
const onlyDeathYear = /^-[1-9][0-9]*[,.]?$/u;
|
|
21
|
+
const birthYearAndDeathYear = /^[1-9][0-9]*-[1-9][0-9]*[,.]?$/u;
|
|
22
|
+
function getDeathYear(str) {
|
|
23
|
+
return parseInt(str.substring(str.indexOf('-') + 1), 10);
|
|
24
|
+
}
|
|
25
|
+
function isValidBirthYearAndDeathYear(str) {
|
|
26
|
+
if (!birthYearAndDeathYear.test(str)) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
// We have two years
|
|
30
|
+
const b = parseInt(str, 10);
|
|
31
|
+
const d = getDeathYear(str);
|
|
32
|
+
if (b > d) {
|
|
33
|
+
// died before birth! Rather unlikely.
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
if (d - b > 125) {
|
|
37
|
+
// Over 125 years old. Rather unlikely.
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
// Possible sanity check: Died after current year?
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
function anyYear(str) {
|
|
44
|
+
if (onlyBirthYear.test(str) || onlyDeathYear.test(str) || isValidBirthYearAndDeathYear(str)) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
function replaceEntrysBirthAndDeathYear(targetField, candSubfield, relevantSubfields) {
|
|
50
|
+
if (birthYearAndDeathYear.test(candSubfield.value)) {
|
|
51
|
+
if (onlyBirthYear.test(relevantSubfields[0].value) && parseInt(relevantSubfields[0].value, 10) === parseInt(candSubfield.value, 10)) {
|
|
52
|
+
relevantSubfields[0].value = candSubfield.value; // eslint-disable-line functional/immutable-data
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
if (onlyDeathYear.test(relevantSubfields[0].value) && getDeathYear(relevantSubfields[0].value) === getDeathYear(candSubfield.value)) {
|
|
56
|
+
relevantSubfields[0].value = candSubfield.value; // eslint-disable-line functional/immutable-data
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
function replaceDatesAssociatedWithName(targetField, candSubfield, relevantSubfields) {
|
|
63
|
+
// Add also the death year, if the original value only contains birth year.
|
|
64
|
+
// This function treats only with X00$d subfields:
|
|
65
|
+
if (candSubfield.code !== 'd' || !/^[1678]00$/u.test(targetField.tag)) {
|
|
66
|
+
// njsscan-ignore: regex_dos
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
if (!anyYear(relevantSubfields[0].value) && anyYear(candSubfield.value)) {
|
|
70
|
+
relevantSubfields[0].value = candSubfield.value; // eslint-disable-line functional/immutable-data
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
if (replaceEntrysBirthAndDeathYear(targetField, candSubfield, relevantSubfields)) {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// use array.includes(value) for easy extendability (Swedish, other languages, abbrs, etc.()
|
|
80
|
+
function isKierreselka(value) {
|
|
81
|
+
return ['kierreselkä', 'spiral bound', 'spiral-bound', 'spiralrygg'].includes(value);
|
|
82
|
+
}
|
|
83
|
+
function isKovakantinen(value) {
|
|
84
|
+
return ['hardback', 'hardcover', 'hårda pärmar', 'kovakantinen'].includes(value);
|
|
85
|
+
}
|
|
86
|
+
function isPehmeakantinen(value) {
|
|
87
|
+
return ['mjuka pärmar', 'paperback', 'pehmeäkantinen', 'softcover'].includes(value);
|
|
88
|
+
}
|
|
89
|
+
function isItsenainenJatkoOsa(value) {
|
|
90
|
+
if (value.match(/^Fristående fortsättning på verket[^a-z]*$/ui)) {
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
if (value.match(/^Itsenäinen jatko-osa teokselle[^a-z]*$/ui)) {
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
function isSisaltaaTeos(value) {
|
|
99
|
+
if (value.match(/^Innehåller \(verk\)[^a-z]*$/ui)) {
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
if (value.match(/^Sisältää \(teos\)[^a-z]*$/ui)) {
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
function relationInformationMatches(candSubfield, relevantSubfields) {
|
|
108
|
+
if (isSisaltaaTeos(candSubfield.value) && relevantSubfields.some(sf => isSisaltaaTeos(sf.value))) {
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
if (isItsenainenJatkoOsa(candSubfield.value) && relevantSubfields.some(sf => isItsenainenJatkoOsa(sf.value))) {
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
function coverTypesMatch(candSubfield, relevantSubfields) {
|
|
117
|
+
if (isPehmeakantinen(candSubfield.value) && relevantSubfields.some(sf => isPehmeakantinen(sf.value))) {
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
if (isKovakantinen(candSubfield.value) && relevantSubfields.some(sf => isKovakantinen(sf.value))) {
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
if (isKierreselka(candSubfield.value) && relevantSubfields.some(sf => isKierreselka(sf.value))) {
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
function httpToHttps(val) {
|
|
129
|
+
return val.replace(/http:\/\//ug, 'https://');
|
|
130
|
+
}
|
|
131
|
+
function pairHttpAndHttps(candSubfield, relevantSubfields) {
|
|
132
|
+
const a = httpToHttps(candSubfield.value);
|
|
133
|
+
const bs = relevantSubfields.map(sf => httpToHttps(sf.value));
|
|
134
|
+
return bs.includes(a);
|
|
135
|
+
}
|
|
136
|
+
function isSynonym(field, candSubfield, relevantSubfields) {
|
|
137
|
+
if (candSubfield.code === 'q' && ['015', '020', '024', '028'].includes(field.tag)) {
|
|
138
|
+
return coverTypesMatch(candSubfield, relevantSubfields);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
//nvdebug(`Looking for synonyms for '${subfieldToString(candSubfield)}'...`, debugDev);
|
|
142
|
+
|
|
143
|
+
if (relationInformationMatches(candSubfield, relevantSubfields)) {
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
if (pairHttpAndHttps(candSubfield, relevantSubfields)) {
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
function preferHyphenatedISBN(field, candSubfield, relevantSubfields) {
|
|
152
|
+
if (!(0, _normalizeFieldForComparison.tagAndSubfieldCodeReferToIsbn)(field.tag, candSubfield.code) || candSubfield.value.includes('-') === -1) {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Must not already exist:
|
|
157
|
+
if (relevantSubfields.some(sf => sf.value === candSubfield.value)) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
const hyphenlessSubfields = relevantSubfields.filter(sf => sf.value.includes('-') > -1);
|
|
161
|
+
const pair = hyphenlessSubfields.find(sf => sf.value === candSubfield.value.replace(/-/gu, ''));
|
|
162
|
+
if (!pair) {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
pair.value = candSubfield.value; // eslint-disable-line functional/immutable-data
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
function preferHttpsOverHttp(candSubfield, relevantSubfields) {
|
|
169
|
+
if (candSubfield.value.substring(0, 8) !== 'https://') {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
const httpVersion = `http://${candSubfield.value.substring(8)}`;
|
|
173
|
+
const pair = relevantSubfields.find(sf => sf.value === httpVersion);
|
|
174
|
+
if (!pair) {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
pair.value = candSubfield.value; // eslint-disable-line functional/immutable-data
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
function preferQualifierVersion(field, candSubfield, relevantSubfields) {
|
|
181
|
+
if (!(0, _counterpartField.canContainOptionalQualifier)(field.tag, candSubfield.code)) {
|
|
182
|
+
// currently only 300$a and 776$i can prefer source...
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
const [name1, qualifier1] = (0, _counterpartField.splitToNameAndQualifier)(candSubfield.value);
|
|
186
|
+
const pair = relevantSubfields.find(sf => subfieldQualifierCheck(sf, name1, qualifier1));
|
|
187
|
+
if (!pair) {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
// SN: "Kuvailuohjeiden näkökulmasta epubille ei pitäisi koskaan merkitä sivumäärää"
|
|
191
|
+
if (field.tag === '300' && candSubfield.code === 'a' && candSubfield.value.match(/(?:online|verkko)/iu)) {
|
|
192
|
+
return true; // True, but don't prefer the source value
|
|
193
|
+
}
|
|
194
|
+
pair.value = candSubfield.value; // eslint-disable-line functional/immutable-data
|
|
195
|
+
return true;
|
|
196
|
+
function subfieldQualifierCheck(subfield, name, qualifier) {
|
|
197
|
+
const [name2, qualifier2] = (0, _counterpartField.splitToNameAndQualifier)(candSubfield.value);
|
|
198
|
+
if (name !== name2) {
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
if (!qualifier || !qualifier2 || qualifier === qualifier2) {
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
function preferSourceCorporateName(field, candSubfield, pair) {
|
|
208
|
+
if (candSubfield.code !== 'a' || !['110', '610', '710', '810'].includes(field.tag)) {
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
(0, _utils.nvdebug)(`CORP base '${pair.value}' vs '${candSubfield.value}'`, debugDev);
|
|
212
|
+
const prefer = actualPrefenceCheck();
|
|
213
|
+
if (prefer) {
|
|
214
|
+
pair.value = candSubfield.value; // eslint-disable-line functional/immutable-data
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
217
|
+
return false;
|
|
218
|
+
function actualPrefenceCheck() {
|
|
219
|
+
if (candSubfield.value.match(/^Werner Söderström/u) && pair.value.match(/^WSOY/ui)) {
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
if (candSubfield.value.match(/^ntamo/u) && pair.value.match(/^N(?:tamo|TAMO)/u)) {
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
225
|
+
// Prefer (qualifier):
|
|
226
|
+
const [sourceName, sourceQualifier] = (0, _counterpartField.splitToNameAndQualifier)(candSubfield.value);
|
|
227
|
+
const [baseName, baseQualifier] = (0, _counterpartField.splitToNameAndQualifier)(pair.value);
|
|
228
|
+
if (sourceName === baseName && baseQualifier === undefined && sourceQualifier !== undefined) {
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
// Not taking prefix and suffix into account here...
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
function mergeSubfield(targetField, candSubfield) {
|
|
236
|
+
// Replace existing subfield with the incoming field. These replacements are by name rather hacky...
|
|
237
|
+
// Currenty we only select the better X00$d.
|
|
238
|
+
// In future we might do more things here. Examples:
|
|
239
|
+
// - "FOO" gets replaced by "Foo" in certain fields.
|
|
240
|
+
// - "Etunimi Sukunimi" might lose to "Sukunimi, Etunimi" in X00 fields.
|
|
241
|
+
// - [put your ideas here]
|
|
242
|
+
// Return true, if replace is done.
|
|
243
|
+
// However, replacing/succeeding requires a sanity check, that the new value is a better one...
|
|
244
|
+
// Thus, typically this function fails...
|
|
245
|
+
|
|
246
|
+
const relevantSubfields = targetField.subfields.filter(subfield => subfield.code === candSubfield.code);
|
|
247
|
+
|
|
248
|
+
// There's nothing to replace the incoming subfield with. Thus abort:
|
|
249
|
+
if (relevantSubfields.length === 0) {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
(0, _utils.nvdebug)(`Got ${relevantSubfields.length} sf-cand(s) for field ${targetField.tag}‡${candSubfield.code}`, debugDev);
|
|
253
|
+
if (replaceDatesAssociatedWithName(targetField, candSubfield, relevantSubfields) || preferHyphenatedISBN(targetField, candSubfield, relevantSubfields) || preferHttpsOverHttp(candSubfield, relevantSubfields) || preferSourceCorporateName(targetField, candSubfield, relevantSubfields[0]) ||
|
|
254
|
+
// SF is non-repeat
|
|
255
|
+
preferQualifierVersion(targetField, candSubfield, relevantSubfields) || isSynonym(targetField, candSubfield, relevantSubfields)) {
|
|
256
|
+
return true;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// We found a crappy empty subfield: replace that with a meaningful one.
|
|
260
|
+
// 260 $a value "[S.l]" is the main type for this.
|
|
261
|
+
const meaninglessSubfields = relevantSubfields.filter(sf => !(0, _worldKnowledge.valueCarriesMeaning)(targetField.tag, sf.code, sf.value));
|
|
262
|
+
if (meaninglessSubfields.length > 0) {
|
|
263
|
+
meaninglessSubfields[0].value = candSubfield.value; // eslint-disable-line functional/immutable-data
|
|
264
|
+
return true;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Mark 490$v "osa 1" vs "1" as merged (2nd part of MET-53).
|
|
268
|
+
// NB! Keeps the original value and drops the incoming value. (Just preventing it from going to add-part...)
|
|
269
|
+
// NB! We could improve this and choose the longer value later on.
|
|
270
|
+
if ((0, _normalizeSubfieldValueForComparison.subfieldContainsPartData)(targetField.tag, candSubfield.code)) {
|
|
271
|
+
if (relevantSubfields.some(sf => (0, _normalizeSubfieldValueForComparison.partsAgree)(sf.value, candSubfield.value, targetField.tag, candSubfield.code))) {
|
|
272
|
+
return true;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return false; // default to failure
|
|
276
|
+
}
|
|
277
|
+
//# sourceMappingURL=mergeSubfield.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mergeSubfield.js","names":["_debug","_interopRequireDefault","require","_normalizeSubfieldValueForComparison","_worldKnowledge","_utils","_normalizeFieldForComparison","_counterpartField","e","__esModule","default","debug","createDebugLogger","debugDev","extend","onlyBirthYear","onlyDeathYear","birthYearAndDeathYear","getDeathYear","str","parseInt","substring","indexOf","isValidBirthYearAndDeathYear","test","b","d","anyYear","replaceEntrysBirthAndDeathYear","targetField","candSubfield","relevantSubfields","value","replaceDatesAssociatedWithName","code","tag","isKierreselka","includes","isKovakantinen","isPehmeakantinen","isItsenainenJatkoOsa","match","isSisaltaaTeos","relationInformationMatches","some","sf","coverTypesMatch","httpToHttps","val","replace","pairHttpAndHttps","a","bs","map","isSynonym","field","preferHyphenatedISBN","tagAndSubfieldCodeReferToIsbn","hyphenlessSubfields","filter","pair","find","preferHttpsOverHttp","httpVersion","preferQualifierVersion","canContainOptionalQualifier","name1","qualifier1","splitToNameAndQualifier","subfieldQualifierCheck","subfield","name","qualifier","name2","qualifier2","preferSourceCorporateName","nvdebug","prefer","actualPrefenceCheck","sourceName","sourceQualifier","baseName","baseQualifier","undefined","mergeSubfield","subfields","length","meaninglessSubfields","valueCarriesMeaning","subfieldContainsPartData","partsAgree"],"sources":["../../src/merge-fields/mergeSubfield.js"],"sourcesContent":["import createDebugLogger from 'debug';\nimport {partsAgree, subfieldContainsPartData} from '../normalizeSubfieldValueForComparison';\nimport {valueCarriesMeaning} from './worldKnowledge';\nimport {nvdebug} from '../utils';\nimport {tagAndSubfieldCodeReferToIsbn} from '../normalizeFieldForComparison.js';\nimport {canContainOptionalQualifier, splitToNameAndQualifier} from './counterpartField';\n\nconst debug = createDebugLogger('@natlibfi/melinda-marc-record-merge-reducers:mergeSubfield');\n//const debugData = debug.extend('data');\nconst debugDev = debug.extend('dev');\n\n// NB! These are X00 specific. Should we somehow parametrize them?\nconst onlyBirthYear = /^[1-9][0-9]*-[,.]?$/u;\nconst onlyDeathYear = /^-[1-9][0-9]*[,.]?$/u;\nconst birthYearAndDeathYear = /^[1-9][0-9]*-[1-9][0-9]*[,.]?$/u;\n\nfunction getDeathYear(str) {\n return parseInt(str.substring(str.indexOf('-') + 1), 10);\n}\n\nfunction isValidBirthYearAndDeathYear(str) {\n if (!birthYearAndDeathYear.test(str)) {\n return false;\n }\n // We have two years\n const b = parseInt(str, 10);\n const d = getDeathYear(str);\n if (b > d) { // died before birth! Rather unlikely.\n return false;\n }\n if (d - b > 125) { // Over 125 years old. Rather unlikely.\n return false;\n }\n // Possible sanity check: Died after current year?\n return true;\n}\n\nfunction anyYear(str) {\n if (onlyBirthYear.test(str) || onlyDeathYear.test(str) || isValidBirthYearAndDeathYear(str)) {\n return true;\n }\n return false;\n}\n\nfunction replaceEntrysBirthAndDeathYear(targetField, candSubfield, relevantSubfields) {\n if (birthYearAndDeathYear.test(candSubfield.value)) {\n if (onlyBirthYear.test(relevantSubfields[0].value) && parseInt(relevantSubfields[0].value, 10) === parseInt(candSubfield.value, 10)) {\n relevantSubfields[0].value = candSubfield.value; // eslint-disable-line functional/immutable-data\n return true;\n }\n\n if (onlyDeathYear.test(relevantSubfields[0].value) && getDeathYear(relevantSubfields[0].value) === getDeathYear(candSubfield.value)) {\n relevantSubfields[0].value = candSubfield.value; // eslint-disable-line functional/immutable-data\n return true;\n }\n }\n return false;\n}\n\nfunction replaceDatesAssociatedWithName(targetField, candSubfield, relevantSubfields) {\n // Add also the death year, if the original value only contains birth year.\n // This function treats only with X00$d subfields:\n if (candSubfield.code !== 'd' || !(/^[1678]00$/u).test(targetField.tag)) { // njsscan-ignore: regex_dos\n return false;\n }\n\n if (!anyYear(relevantSubfields[0].value) && anyYear(candSubfield.value)) {\n relevantSubfields[0].value = candSubfield.value; // eslint-disable-line functional/immutable-data\n return true;\n }\n\n if (replaceEntrysBirthAndDeathYear(targetField, candSubfield, relevantSubfields)) {\n return true;\n }\n return false;\n}\n\n// use array.includes(value) for easy extendability (Swedish, other languages, abbrs, etc.()\nfunction isKierreselka(value) {\n return ['kierreselkä', 'spiral bound', 'spiral-bound', 'spiralrygg'].includes(value);\n}\n\nfunction isKovakantinen(value) {\n return ['hardback', 'hardcover', 'hårda pärmar', 'kovakantinen'].includes(value);\n}\n\nfunction isPehmeakantinen(value) {\n return ['mjuka pärmar', 'paperback', 'pehmeäkantinen', 'softcover'].includes(value);\n}\n\nfunction isItsenainenJatkoOsa(value) {\n if (value.match(/^Fristående fortsättning på verket[^a-z]*$/ui)) {\n return true;\n }\n if (value.match(/^Itsenäinen jatko-osa teokselle[^a-z]*$/ui)) {\n return true;\n }\n return false;\n}\n\nfunction isSisaltaaTeos(value) {\n if (value.match(/^Innehåller \\(verk\\)[^a-z]*$/ui)) {\n return true;\n }\n if (value.match(/^Sisältää \\(teos\\)[^a-z]*$/ui)) {\n return true;\n }\n return false;\n}\nfunction relationInformationMatches(candSubfield, relevantSubfields) {\n if (isSisaltaaTeos(candSubfield.value) && relevantSubfields.some(sf => isSisaltaaTeos(sf.value))) {\n return true;\n }\n if (isItsenainenJatkoOsa(candSubfield.value) && relevantSubfields.some(sf => isItsenainenJatkoOsa(sf.value))) {\n return true;\n }\n\n return false;\n}\n\nfunction coverTypesMatch(candSubfield, relevantSubfields) {\n if (isPehmeakantinen(candSubfield.value) && relevantSubfields.some(sf => isPehmeakantinen(sf.value))) {\n return true;\n }\n if (isKovakantinen(candSubfield.value) && relevantSubfields.some(sf => isKovakantinen(sf.value))) {\n return true;\n }\n if (isKierreselka(candSubfield.value) && relevantSubfields.some(sf => isKierreselka(sf.value))) {\n return true;\n }\n return false;\n}\n\nfunction httpToHttps(val) {\n return val.replace(/http:\\/\\//ug, 'https://');\n}\n\nfunction pairHttpAndHttps(candSubfield, relevantSubfields) {\n const a = httpToHttps(candSubfield.value);\n const bs = relevantSubfields.map(sf => httpToHttps(sf.value));\n return bs.includes(a);\n}\n\nfunction isSynonym(field, candSubfield, relevantSubfields) {\n if (candSubfield.code === 'q' && ['015', '020', '024', '028'].includes(field.tag)) {\n return coverTypesMatch(candSubfield, relevantSubfields);\n }\n\n //nvdebug(`Looking for synonyms for '${subfieldToString(candSubfield)}'...`, debugDev);\n\n if (relationInformationMatches(candSubfield, relevantSubfields)) {\n return true;\n }\n\n if (pairHttpAndHttps(candSubfield, relevantSubfields)) {\n return true;\n }\n\n return false;\n}\n\nfunction preferHyphenatedISBN(field, candSubfield, relevantSubfields) {\n if (!tagAndSubfieldCodeReferToIsbn(field.tag, candSubfield.code) || candSubfield.value.includes('-') === -1) {\n return false;\n }\n\n // Must not already exist:\n if (relevantSubfields.some(sf => sf.value === candSubfield.value)) {\n return false;\n }\n\n const hyphenlessSubfields = relevantSubfields.filter(sf => sf.value.includes('-') > -1);\n const pair = hyphenlessSubfields.find(sf => sf.value === candSubfield.value.replace(/-/gu, ''));\n if (!pair) {\n return false;\n }\n pair.value = candSubfield.value; // eslint-disable-line functional/immutable-data\n return true;\n}\n\nfunction preferHttpsOverHttp(candSubfield, relevantSubfields) {\n if (candSubfield.value.substring(0, 8) !== 'https://') {\n return false;\n }\n\n const httpVersion = `http://${candSubfield.value.substring(8)}`;\n const pair = relevantSubfields.find(sf => sf.value === httpVersion);\n\n if (!pair) {\n return false;\n }\n pair.value = candSubfield.value; // eslint-disable-line functional/immutable-data\n return true;\n}\n\n\nfunction preferQualifierVersion(field, candSubfield, relevantSubfields) {\n if (!canContainOptionalQualifier(field.tag, candSubfield.code)) { // currently only 300$a and 776$i can prefer source...\n return false;\n }\n\n const [name1, qualifier1] = splitToNameAndQualifier(candSubfield.value);\n const pair = relevantSubfields.find(sf => subfieldQualifierCheck(sf, name1, qualifier1));\n if (!pair) {\n return false;\n }\n // SN: \"Kuvailuohjeiden näkökulmasta epubille ei pitäisi koskaan merkitä sivumäärää\"\n if (field.tag === '300' && candSubfield.code === 'a' && candSubfield.value.match(/(?:online|verkko)/iu)) {\n return true; // True, but don't prefer the source value\n }\n\n pair.value = candSubfield.value; // eslint-disable-line functional/immutable-data\n return true;\n\n function subfieldQualifierCheck(subfield, name, qualifier) {\n const [name2, qualifier2] = splitToNameAndQualifier(candSubfield.value);\n if (name !== name2) {\n return false;\n }\n if (!qualifier || !qualifier2 || qualifier === qualifier2) {\n return true;\n }\n return false;\n }\n\n}\n\nfunction preferSourceCorporateName(field, candSubfield, pair) {\n if (candSubfield.code !== 'a' || !['110', '610', '710', '810'].includes(field.tag)) {\n return false;\n }\n nvdebug(`CORP base '${pair.value}' vs '${candSubfield.value}'`, debugDev);\n const prefer = actualPrefenceCheck();\n if (prefer) {\n pair.value = candSubfield.value; // eslint-disable-line functional/immutable-data\n return true;\n }\n return false;\n\n function actualPrefenceCheck() {\n if (candSubfield.value.match(/^Werner Söderström/u) && pair.value.match(/^WSOY/ui)) {\n return true;\n }\n if (candSubfield.value.match(/^ntamo/u) && pair.value.match(/^N(?:tamo|TAMO)/u)) {\n return true;\n }\n // Prefer (qualifier):\n const [sourceName, sourceQualifier] = splitToNameAndQualifier(candSubfield.value);\n const [baseName, baseQualifier] = splitToNameAndQualifier(pair.value);\n if (sourceName === baseName && baseQualifier === undefined && sourceQualifier !== undefined) {\n return true;\n }\n // Not taking prefix and suffix into account here...\n return false;\n }\n\n}\n\nexport function mergeSubfield(targetField, candSubfield) {\n // Replace existing subfield with the incoming field. These replacements are by name rather hacky...\n // Currenty we only select the better X00$d.\n // In future we might do more things here. Examples:\n // - \"FOO\" gets replaced by \"Foo\" in certain fields.\n // - \"Etunimi Sukunimi\" might lose to \"Sukunimi, Etunimi\" in X00 fields.\n // - [put your ideas here]\n // Return true, if replace is done.\n // However, replacing/succeeding requires a sanity check, that the new value is a better one...\n // Thus, typically this function fails...\n\n const relevantSubfields = targetField.subfields.filter(subfield => subfield.code === candSubfield.code);\n\n // There's nothing to replace the incoming subfield with. Thus abort:\n if (relevantSubfields.length === 0) {\n return false;\n }\n\n nvdebug(`Got ${relevantSubfields.length} sf-cand(s) for field ${targetField.tag}‡${candSubfield.code}`, debugDev);\n\n\n if (replaceDatesAssociatedWithName(targetField, candSubfield, relevantSubfields) ||\n preferHyphenatedISBN(targetField, candSubfield, relevantSubfields) ||\n preferHttpsOverHttp(candSubfield, relevantSubfields) ||\n preferSourceCorporateName(targetField, candSubfield, relevantSubfields[0]) || // SF is non-repeat\n preferQualifierVersion(targetField, candSubfield, relevantSubfields) ||\n isSynonym(targetField, candSubfield, relevantSubfields)) {\n return true;\n }\n\n // We found a crappy empty subfield: replace that with a meaningful one.\n // 260 $a value \"[S.l]\" is the main type for this.\n const meaninglessSubfields = relevantSubfields.filter(sf => !valueCarriesMeaning(targetField.tag, sf.code, sf.value));\n if (meaninglessSubfields.length > 0) {\n meaninglessSubfields[0].value = candSubfield.value; // eslint-disable-line functional/immutable-data\n return true;\n }\n\n // Mark 490$v \"osa 1\" vs \"1\" as merged (2nd part of MET-53).\n // NB! Keeps the original value and drops the incoming value. (Just preventing it from going to add-part...)\n // NB! We could improve this and choose the longer value later on.\n if (subfieldContainsPartData(targetField.tag, candSubfield.code)) {\n if (relevantSubfields.some(sf => partsAgree(sf.value, candSubfield.value, targetField.tag, candSubfield.code))) {\n return true;\n }\n }\n return false; // default to failure\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.fieldRemoveDuplicateSubfields = fieldRemoveDuplicateSubfields;
|
|
7
|
+
exports.recordRemoveDuplicateSubfieldsFromFields = recordRemoveDuplicateSubfieldsFromFields;
|
|
8
|
+
var _utils = require("../utils.js");
|
|
9
|
+
var _debug = _interopRequireDefault(require("debug"));
|
|
10
|
+
var _normalizeFieldForComparison = require("../normalizeFieldForComparison.js");
|
|
11
|
+
var _sortSubfields = require("../sortSubfields");
|
|
12
|
+
var _punctuation = require("../punctuation2");
|
|
13
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
14
|
+
// NB This should be moved and converted to a validator/fixer as well...
|
|
15
|
+
const debug = (0, _debug.default)('@natlibfi/marc-record-validators-melinda:merge-fields:removeDuplicateSubfields');
|
|
16
|
+
//const debugData = debug.extend('data');
|
|
17
|
+
const debugDev = debug.extend('dev');
|
|
18
|
+
function recordRemoveDuplicateSubfieldsFromFields(record) {
|
|
19
|
+
record.fields.forEach(field => fieldRemoveDuplicateSubfields(field));
|
|
20
|
+
}
|
|
21
|
+
function fieldRemoveDuplicateSubfields(field) {
|
|
22
|
+
// Skip bad (382, 505) and risky (264 ...) stuff: 382$n, 505$r, others...
|
|
23
|
+
if (!field.subfields || ['264', '300', '382', '505'].includes(field.tag)) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const strippedField = (0, _normalizeFieldForComparison.cloneAndRemovePunctuation)(field); // make punctuation-less version
|
|
27
|
+
/* eslint-disable */
|
|
28
|
+
let seen = {};
|
|
29
|
+
field.subfields = field.subfields.filter((sf, i) => notSeenBefore(sf, i));
|
|
30
|
+
if (field.collapsed) {
|
|
31
|
+
(0, _sortSubfields.sortAdjacentSubfields)(field);
|
|
32
|
+
(0, _punctuation.fieldFixPunctuation)(field);
|
|
33
|
+
delete field.collapsed;
|
|
34
|
+
}
|
|
35
|
+
function notSeenBefore(sf, index) {
|
|
36
|
+
const subfieldAsString = (0, _utils.subfieldToString)(strippedField.subfields[index]); // use normalized form
|
|
37
|
+
if (seen[subfieldAsString]) {
|
|
38
|
+
(0, _utils.nvdebug)(`Remove field-internal duplicate subfield ${(0, _utils.subfieldToString)(sf)}`, debugDev);
|
|
39
|
+
field.collapsed = 1; // trigger punctuation reset
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
//nvdebug(`identical subfield removal: Add ${subfieldAsString} to seen[]`, debugDev);
|
|
43
|
+
seen[subfieldAsString] = subfieldAsString;
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
/* eslint-enable */
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=removeDuplicateSubfields.js.map
|