@natlibfi/marc-record-validators-melinda 9.0.13 → 9.0.14
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/indicator-fixes.js +266 -0
- package/dist/indicator-fixes.js.map +1 -0
- package/dist/indicator-fixes.spec.js +61 -0
- package/dist/indicator-fixes.spec.js.map +1 -0
- package/package.json +1 -1
- package/src/indicator-fixes.js +249 -0
- package/src/indicator-fixes.spec.js +52 -0
- package/test-fixtures/indicator-fixes/01/expectedResult.json +9 -0
- package/test-fixtures/indicator-fixes/01/metadata.json +5 -0
- package/test-fixtures/indicator-fixes/01/record.json +64 -0
- package/test-fixtures/indicator-fixes/02/expectedResult.json +66 -0
- package/test-fixtures/indicator-fixes/02/metadata.json +5 -0
- package/test-fixtures/indicator-fixes/02/record.json +64 -0
- package/test-fixtures/indicator-fixes/03/expectedResult.json +55 -0
- package/test-fixtures/indicator-fixes/03/metadata.json +5 -0
- package/test-fixtures/indicator-fixes/03/record.json +54 -0
- package/test-fixtures/indicator-fixes/04/expectedResult.json +66 -0
- package/test-fixtures/indicator-fixes/04/metadata.json +5 -0
- package/test-fixtures/indicator-fixes/04/record.json +65 -0
- package/test-fixtures/indicator-fixes/05/expectedResult.json +66 -0
- package/test-fixtures/indicator-fixes/05/metadata.json +5 -0
- package/test-fixtures/indicator-fixes/05/record.json +65 -0
- package/test-fixtures/indicator-fixes/06/expectedResult.json +75 -0
- package/test-fixtures/indicator-fixes/06/metadata.json +5 -0
- package/test-fixtures/indicator-fixes/06/record.json +74 -0
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = _default;
|
|
7
|
+
exports.recordNormalizeIndicators = recordNormalizeIndicators;
|
|
8
|
+
|
|
9
|
+
// Relocated from melinda-marc-record-merge-reducers (and renamed)
|
|
10
|
+
//import createDebugLogger from 'debug';
|
|
11
|
+
//const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda:normalizeIdentifiers');
|
|
12
|
+
function fieldToString(f) {
|
|
13
|
+
if (!f.subfields) {
|
|
14
|
+
return `${f.tag} ${f.value}`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return `${f.tag} ${f.ind1}${f.ind2} ‡${formatSubfields(f)}`;
|
|
18
|
+
|
|
19
|
+
function formatSubfields(field) {
|
|
20
|
+
//return field.subfields.map(sf => `${sf.code}${sf.value || ''}`).join('‡');
|
|
21
|
+
return field.subfields.map(sf => `${sf.code}${sf.value}`).join(' ‡');
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/*
|
|
25
|
+
function nvdebug(message, func) {
|
|
26
|
+
if (func) { // eslint-disable-line functional/no-conditional-statement
|
|
27
|
+
func(message);
|
|
28
|
+
}
|
|
29
|
+
console.info(message); // eslint-disable-line no-console
|
|
30
|
+
}
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
function _default() {
|
|
35
|
+
// NB! We should and could handle ISNIs here as well.
|
|
36
|
+
return {
|
|
37
|
+
description: 'Normalizes indicator values',
|
|
38
|
+
validate,
|
|
39
|
+
fix
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
function fix(record) {
|
|
43
|
+
const res = {
|
|
44
|
+
message: [],
|
|
45
|
+
fix: [],
|
|
46
|
+
valid: true
|
|
47
|
+
}; //message.fix = []; // eslint-disable-line functional/immutable-data
|
|
48
|
+
// Actual parsing of all fields
|
|
49
|
+
|
|
50
|
+
/*
|
|
51
|
+
if (!record.fields) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
recordNormalizeIndicators(record); // message.valid = !(message.message.length >= 1); // eslint-disable-line functional/immutable-data
|
|
57
|
+
|
|
58
|
+
return res;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function validate(record) {
|
|
62
|
+
const res = {
|
|
63
|
+
message: []
|
|
64
|
+
}; //nvdebug(`NORMALIZE CONTROL NUMBER VALIDATE`, debug);
|
|
65
|
+
// Actual parsing of all fields
|
|
66
|
+
|
|
67
|
+
/*
|
|
68
|
+
if (!record.fields) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
*/
|
|
72
|
+
|
|
73
|
+
validateRecord(record, res);
|
|
74
|
+
res.valid = res.message.length < 1; // eslint-disable-line functional/immutable-data
|
|
75
|
+
|
|
76
|
+
return res;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function validateRecord(record, res) {
|
|
80
|
+
//nvdebug(record);
|
|
81
|
+
const clonedFields = JSON.parse(JSON.stringify(record.fields));
|
|
82
|
+
recordNormalizeIndicators(record);
|
|
83
|
+
record.fields.forEach((field, index) => compareFields(field, index));
|
|
84
|
+
|
|
85
|
+
function compareFields(field, index) {
|
|
86
|
+
const origFieldAsString = fieldToString(clonedFields[index]); //const clonedFieldAsString = fieldToString(field);
|
|
87
|
+
|
|
88
|
+
if (clonedFields[index].ind1 !== field.ind1) {
|
|
89
|
+
// eslint-disable-line functional/no-conditional-statement
|
|
90
|
+
//nvdebug(`FIX IND1: '${clonedFields[index].ind1}' => '${field.ind1}': ${clonedFieldAsString}`);
|
|
91
|
+
res.message.push(`Expected IND1 for '${origFieldAsString}' is '${field.ind1}'`); // eslint-disable-line functional/immutable-data
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (clonedFields[index].ind2 !== field.ind2) {
|
|
95
|
+
// eslint-disable-line functional/no-conditional-statement
|
|
96
|
+
//nvdebug(`FIX IND2: '${clonedFields[index].ind2}' => '${field.ind2}': ${clonedFieldAsString}`);
|
|
97
|
+
res.message.push(`Expected IND2 for '${origFieldAsString}' is '${field.ind2}'`); // eslint-disable-line functional/immutable-data
|
|
98
|
+
}
|
|
99
|
+
} // Validator should not change the original record:
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
record.fields = clonedFields; // eslint-disable-line functional/immutable-data
|
|
103
|
+
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const ind1NonFilingChars = ['130', '630', '730', '740'];
|
|
109
|
+
const ind2NonFilingChars = ['222', '240', '242', '243', '245', '830'];
|
|
110
|
+
|
|
111
|
+
function hasNonFilingIndicator1(field) {
|
|
112
|
+
return ind1NonFilingChars.includes(field.tag);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function modifiableIndicatorValue(value) {
|
|
116
|
+
// If field contains a legit-looking value, don't try to modify it here...
|
|
117
|
+
return !['9', '8', '7', '6', '5', '4', '3', '2', '1'].includes(value);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function hasNonFilingIndicator2(field) {
|
|
121
|
+
return ind2NonFilingChars.includes(field.tag);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function valueBeginsWithDeterminer(value, cands) {
|
|
125
|
+
return cands.find(cand => value.substring(0, cand.length) === cand);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function determineNonFilingIndicatorValue(field, languages = undefined) {
|
|
129
|
+
const subfieldA = field.subfields.find(sf => sf.code === 'a');
|
|
130
|
+
|
|
131
|
+
if (!subfieldA) {
|
|
132
|
+
// nvdebug(' Subfield $a miss!');
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const name = subfieldA.value.toLowerCase();
|
|
137
|
+
|
|
138
|
+
if (languages.includes('eng')) {
|
|
139
|
+
const match = valueBeginsWithDeterminer(name, ['a ', 'an ', 'the ']);
|
|
140
|
+
|
|
141
|
+
if (match) {
|
|
142
|
+
return `${match.length}`;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (languages.includes('fre')) {
|
|
147
|
+
const match = valueBeginsWithDeterminer(name, ['l\'', 'le ']);
|
|
148
|
+
|
|
149
|
+
if (match) {
|
|
150
|
+
return `${match.length}`;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (languages.includes('ger')) {
|
|
155
|
+
const match = valueBeginsWithDeterminer(name, ['das ', 'der ', 'die ']);
|
|
156
|
+
|
|
157
|
+
if (match) {
|
|
158
|
+
return `${match.length}`;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (languages.includes('swe')) {
|
|
163
|
+
const match = valueBeginsWithDeterminer(name, ['en ', 'ett ']);
|
|
164
|
+
|
|
165
|
+
if (match) {
|
|
166
|
+
return `${match.length}`;
|
|
167
|
+
}
|
|
168
|
+
} // Fallback-ish: try to guess even without languages:
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
const match = valueBeginsWithDeterminer(name, ['the ']);
|
|
172
|
+
|
|
173
|
+
if (match) {
|
|
174
|
+
return `${match.length}`;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return '0';
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function normalizeNonFilingIndicator1(field, languages = []) {
|
|
181
|
+
if (!hasNonFilingIndicator1(field) || !modifiableIndicatorValue(field.ind1)) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
field.ind1 = determineNonFilingIndicatorValue(field, languages); // eslint-disable-line functional/immutable-data
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function normalizeNonFilingIndicator2(field, languages = []) {
|
|
189
|
+
if (!hasNonFilingIndicator2(field) || !modifiableIndicatorValue(field.ind2)) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
field.ind2 = determineNonFilingIndicatorValue(field, languages); // eslint-disable-line functional/immutable-data
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function normalize245Indicator1(field, record) {
|
|
197
|
+
if (field.tag !== '245') {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const field1XX = record.get('^1..$');
|
|
202
|
+
field.ind1 = field1XX.length === 0 ? '0' : '1'; // eslint-disable-line functional/immutable-data
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function recordNormalize490(record) {
|
|
206
|
+
const fields490 = record.get('^490$');
|
|
207
|
+
const fields8XX = record.get('^(?:800|810|811|830)$');
|
|
208
|
+
|
|
209
|
+
if (fields490.length === 0) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (fields490.length <= fields8XX.length) {
|
|
214
|
+
// Trace found for each field 490:
|
|
215
|
+
fields490.forEach(f => {
|
|
216
|
+
f.ind1 = '1'; // eslint-disable-line functional/immutable-data
|
|
217
|
+
});
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (fields8XX.length === 0) {
|
|
222
|
+
// Fields 490 are always untraced (no traces found)
|
|
223
|
+
fields490.forEach(f => {
|
|
224
|
+
f.ind1 = '0'; // eslint-disable-line functional/immutable-data
|
|
225
|
+
});
|
|
226
|
+
return;
|
|
227
|
+
} // For other combinations we just can't be sure, so leave them as they are.
|
|
228
|
+
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function getLanguages(record) {
|
|
232
|
+
const langFields = record.get('^041$');
|
|
233
|
+
|
|
234
|
+
if (langFields.length === 0) {
|
|
235
|
+
return [];
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return langFields[0].subfields.filter(sf => isRelevantSubfield(sf)).map(subfield => subfield.value);
|
|
239
|
+
|
|
240
|
+
function isRelevantSubfield(subfield) {
|
|
241
|
+
if (subfield.code !== 'a' && subfield.code !== 'd') {
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (subfield.value.length !== 3) {
|
|
246
|
+
return false;
|
|
247
|
+
} // We could require /^[a-z][a-z][a-z]$/ etc as well, but it's not really that relevant.
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function recordNormalizeIndicators(record) {
|
|
255
|
+
recordNormalize490(record); // Language is used to handle non-filing indicators
|
|
256
|
+
|
|
257
|
+
const languages = getLanguages(record);
|
|
258
|
+
record.fields.forEach(field => fieldNormalizeIndicators(field, record, languages));
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function fieldNormalizeIndicators(field, record, languages) {
|
|
262
|
+
normalize245Indicator1(field, record);
|
|
263
|
+
normalizeNonFilingIndicator1(field, languages);
|
|
264
|
+
normalizeNonFilingIndicator2(field, languages);
|
|
265
|
+
}
|
|
266
|
+
//# sourceMappingURL=indicator-fixes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"indicator-fixes.js","names":["fieldToString","f","subfields","tag","value","ind1","ind2","formatSubfields","field","map","sf","code","join","description","validate","fix","record","res","message","valid","recordNormalizeIndicators","validateRecord","length","clonedFields","JSON","parse","stringify","fields","forEach","index","compareFields","origFieldAsString","push","ind1NonFilingChars","ind2NonFilingChars","hasNonFilingIndicator1","includes","modifiableIndicatorValue","hasNonFilingIndicator2","valueBeginsWithDeterminer","cands","find","cand","substring","determineNonFilingIndicatorValue","languages","undefined","subfieldA","name","toLowerCase","match","normalizeNonFilingIndicator1","normalizeNonFilingIndicator2","normalize245Indicator1","field1XX","get","recordNormalize490","fields490","fields8XX","getLanguages","langFields","filter","isRelevantSubfield","subfield","fieldNormalizeIndicators"],"sources":["../src/indicator-fixes.js"],"sourcesContent":["// Relocated from melinda-marc-record-merge-reducers (and renamed)\n//import createDebugLogger from 'debug';\n//const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda:normalizeIdentifiers');\n\n\nfunction fieldToString(f) {\n if (!f.subfields) {\n return `${f.tag} ${f.value}`;\n }\n return `${f.tag} ${f.ind1}${f.ind2} ‡${formatSubfields(f)}`;\n\n function formatSubfields(field) {\n //return field.subfields.map(sf => `${sf.code}${sf.value || ''}`).join('‡');\n return field.subfields.map(sf => `${sf.code}${sf.value}`).join(' ‡');\n }\n}\n\n/*\nfunction nvdebug(message, func) {\n if (func) { // eslint-disable-line functional/no-conditional-statement\n func(message);\n }\n console.info(message); // eslint-disable-line no-console\n}\n*/\n\nexport default function () {\n\n // NB! We should and could handle ISNIs here as well.\n return {\n description: 'Normalizes indicator values',\n validate, fix\n };\n\n function fix(record) {\n const res = {message: [], fix: [], valid: true};\n //message.fix = []; // eslint-disable-line functional/immutable-data\n\n // Actual parsing of all fields\n /*\n if (!record.fields) {\n return false;\n }\n */\n\n\n recordNormalizeIndicators(record);\n\n\n // message.valid = !(message.message.length >= 1); // eslint-disable-line functional/immutable-data\n return res;\n }\n\n function validate(record) {\n const res = {message: []};\n //nvdebug(`NORMALIZE CONTROL NUMBER VALIDATE`, debug);\n // Actual parsing of all fields\n /*\n if (!record.fields) {\n return false;\n }\n */\n\n validateRecord(record, res);\n\n res.valid = res.message.length < 1; // eslint-disable-line functional/immutable-data\n return res;\n }\n\n\n function validateRecord(record, res) {\n //nvdebug(record);\n const clonedFields = JSON.parse(JSON.stringify(record.fields));\n recordNormalizeIndicators(record);\n\n record.fields.forEach((field, index) => compareFields(field, index));\n\n function compareFields(field, index) {\n const origFieldAsString = fieldToString(clonedFields[index]);\n //const clonedFieldAsString = fieldToString(field);\n if (clonedFields[index].ind1 !== field.ind1) { // eslint-disable-line functional/no-conditional-statement\n //nvdebug(`FIX IND1: '${clonedFields[index].ind1}' => '${field.ind1}': ${clonedFieldAsString}`);\n res.message.push(`Expected IND1 for '${origFieldAsString}' is '${field.ind1}'`); // eslint-disable-line functional/immutable-data\n }\n if (clonedFields[index].ind2 !== field.ind2) { // eslint-disable-line functional/no-conditional-statement\n //nvdebug(`FIX IND2: '${clonedFields[index].ind2}' => '${field.ind2}': ${clonedFieldAsString}`);\n res.message.push(`Expected IND2 for '${origFieldAsString}' is '${field.ind2}'`); // eslint-disable-line functional/immutable-data\n }\n }\n // Validator should not change the original record:\n record.fields = clonedFields; // eslint-disable-line functional/immutable-data\n return;\n }\n}\n\n\nconst ind1NonFilingChars = ['130', '630', '730', '740'];\nconst ind2NonFilingChars = ['222', '240', '242', '243', '245', '830'];\n\nfunction hasNonFilingIndicator1(field) {\n return ind1NonFilingChars.includes(field.tag);\n}\n\nfunction modifiableIndicatorValue(value) {\n // If field contains a legit-looking value, don't try to modify it here...\n return !['9', '8', '7', '6', '5', '4', '3', '2', '1'].includes(value);\n}\n\nfunction hasNonFilingIndicator2(field) {\n return ind2NonFilingChars.includes(field.tag);\n}\n\nfunction valueBeginsWithDeterminer(value, cands) {\n return cands.find(cand => value.substring(0, cand.length) === cand);\n}\n\nfunction determineNonFilingIndicatorValue(field, languages = undefined) {\n const subfieldA = field.subfields.find(sf => sf.code === 'a');\n if (!subfieldA) {\n // nvdebug(' Subfield $a miss!');\n return;\n }\n\n const name = subfieldA.value.toLowerCase();\n\n if (languages.includes('eng')) {\n const match = valueBeginsWithDeterminer(name, ['a ', 'an ', 'the ']);\n if (match) {\n return `${match.length}`;\n }\n }\n\n if (languages.includes('fre')) {\n const match = valueBeginsWithDeterminer(name, ['l\\'', 'le ']);\n if (match) {\n return `${match.length}`;\n }\n }\n\n if (languages.includes('ger')) {\n const match = valueBeginsWithDeterminer(name, ['das ', 'der ', 'die ']);\n if (match) {\n return `${match.length}`;\n }\n }\n\n if (languages.includes('swe')) {\n const match = valueBeginsWithDeterminer(name, ['en ', 'ett ']);\n if (match) {\n return `${match.length}`;\n }\n }\n\n // Fallback-ish: try to guess even without languages:\n const match = valueBeginsWithDeterminer(name, ['the ']);\n if (match) {\n return `${match.length}`;\n }\n\n return '0';\n}\n\nfunction normalizeNonFilingIndicator1(field, languages = []) {\n if (!hasNonFilingIndicator1(field) || !modifiableIndicatorValue(field.ind1)) {\n return;\n }\n\n field.ind1 = determineNonFilingIndicatorValue(field, languages); // eslint-disable-line functional/immutable-data\n}\n\nfunction normalizeNonFilingIndicator2(field, languages = []) {\n if (!hasNonFilingIndicator2(field) || !modifiableIndicatorValue(field.ind2)) {\n return;\n }\n\n field.ind2 = determineNonFilingIndicatorValue(field, languages); // eslint-disable-line functional/immutable-data\n}\n\n\nfunction normalize245Indicator1(field, record) {\n if (field.tag !== '245') {\n return;\n }\n const field1XX = record.get('^1..$');\n field.ind1 = field1XX.length === 0 ? '0' : '1'; // eslint-disable-line functional/immutable-data\n}\n\n\nfunction recordNormalize490(record) {\n const fields490 = record.get('^490$');\n const fields8XX = record.get('^(?:800|810|811|830)$');\n\n if (fields490.length === 0) {\n return;\n }\n if (fields490.length <= fields8XX.length) {\n // Trace found for each field 490:\n fields490.forEach(f => {\n f.ind1 = '1'; // eslint-disable-line functional/immutable-data\n });\n return;\n }\n if (fields8XX.length === 0) { // Fields 490 are always untraced (no traces found)\n fields490.forEach(f => {\n f.ind1 = '0'; // eslint-disable-line functional/immutable-data\n });\n return;\n }\n // For other combinations we just can't be sure, so leave them as they are.\n}\n\n\nfunction getLanguages(record) {\n const langFields = record.get('^041$');\n\n if (langFields.length === 0) {\n return [];\n }\n\n return langFields[0].subfields.filter(sf => isRelevantSubfield(sf)).map(subfield => subfield.value);\n\n function isRelevantSubfield(subfield) {\n if (subfield.code !== 'a' && subfield.code !== 'd') {\n return false;\n }\n if (subfield.value.length !== 3) {\n return false;\n }\n // We could require /^[a-z][a-z][a-z]$/ etc as well, but it's not really that relevant.\n return true;\n }\n\n}\n\nexport function recordNormalizeIndicators(record) {\n recordNormalize490(record);\n\n // Language is used to handle non-filing indicators\n const languages = getLanguages(record);\n\n record.fields.forEach(field => fieldNormalizeIndicators(field, record, languages));\n\n}\n\nfunction fieldNormalizeIndicators(field, record, languages) {\n normalize245Indicator1(field, record);\n normalizeNonFilingIndicator1(field, languages);\n normalizeNonFilingIndicator2(field, languages);\n}\n"],"mappings":";;;;;;;;AAAA;AACA;AACA;AAGA,SAASA,aAAT,CAAuBC,CAAvB,EAA0B;EACxB,IAAI,CAACA,CAAC,CAACC,SAAP,EAAkB;IAChB,OAAQ,GAAED,CAAC,CAACE,GAAI,OAAMF,CAAC,CAACG,KAAM,EAA9B;EACD;;EACD,OAAQ,GAAEH,CAAC,CAACE,GAAI,IAAGF,CAAC,CAACI,IAAK,GAAEJ,CAAC,CAACK,IAAK,KAAIC,eAAe,CAACN,CAAD,CAAI,EAA1D;;EAEA,SAASM,eAAT,CAAyBC,KAAzB,EAAgC;IAC9B;IACA,OAAOA,KAAK,CAACN,SAAN,CAAgBO,GAAhB,CAAoBC,EAAE,IAAK,GAAEA,EAAE,CAACC,IAAK,GAAED,EAAE,CAACN,KAAM,EAAhD,EAAmDQ,IAAnD,CAAwD,IAAxD,CAAP;EACD;AACF;AAED;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AAEe,oBAAY;EAEzB;EACA,OAAO;IACLC,WAAW,EAAE,6BADR;IAELC,QAFK;IAEKC;EAFL,CAAP;;EAKA,SAASA,GAAT,CAAaC,MAAb,EAAqB;IACnB,MAAMC,GAAG,GAAG;MAACC,OAAO,EAAE,EAAV;MAAcH,GAAG,EAAE,EAAnB;MAAuBI,KAAK,EAAE;IAA9B,CAAZ,CADmB,CAEnB;IAEA;;IACA;AACJ;AACA;AACA;AACA;;IAGIC,yBAAyB,CAACJ,MAAD,CAAzB,CAZmB,CAenB;;IACA,OAAOC,GAAP;EACD;;EAED,SAASH,QAAT,CAAkBE,MAAlB,EAA0B;IACxB,MAAMC,GAAG,GAAG;MAACC,OAAO,EAAE;IAAV,CAAZ,CADwB,CAExB;IACA;;IACA;AACJ;AACA;AACA;AACA;;IAEIG,cAAc,CAACL,MAAD,EAASC,GAAT,CAAd;IAEAA,GAAG,CAACE,KAAJ,GAAYF,GAAG,CAACC,OAAJ,CAAYI,MAAZ,GAAqB,CAAjC,CAZwB,CAYY;;IACpC,OAAOL,GAAP;EACD;;EAGD,SAASI,cAAT,CAAwBL,MAAxB,EAAgCC,GAAhC,EAAqC;IACnC;IACA,MAAMM,YAAY,GAAGC,IAAI,CAACC,KAAL,CAAWD,IAAI,CAACE,SAAL,CAAeV,MAAM,CAACW,MAAtB,CAAX,CAArB;IACAP,yBAAyB,CAACJ,MAAD,CAAzB;IAEAA,MAAM,CAACW,MAAP,CAAcC,OAAd,CAAsB,CAACpB,KAAD,EAAQqB,KAAR,KAAkBC,aAAa,CAACtB,KAAD,EAAQqB,KAAR,CAArD;;IAEA,SAASC,aAAT,CAAuBtB,KAAvB,EAA8BqB,KAA9B,EAAqC;MACnC,MAAME,iBAAiB,GAAG/B,aAAa,CAACuB,YAAY,CAACM,KAAD,CAAb,CAAvC,CADmC,CAEnC;;MACA,IAAIN,YAAY,CAACM,KAAD,CAAZ,CAAoBxB,IAApB,KAA6BG,KAAK,CAACH,IAAvC,EAA6C;QAAE;QAC7C;QACAY,GAAG,CAACC,OAAJ,CAAYc,IAAZ,CAAkB,sBAAqBD,iBAAkB,SAAQvB,KAAK,CAACH,IAAK,GAA5E,EAF2C,CAEsC;MAClF;;MACD,IAAIkB,YAAY,CAACM,KAAD,CAAZ,CAAoBvB,IAApB,KAA6BE,KAAK,CAACF,IAAvC,EAA6C;QAAE;QAC7C;QACAW,GAAG,CAACC,OAAJ,CAAYc,IAAZ,CAAkB,sBAAqBD,iBAAkB,SAAQvB,KAAK,CAACF,IAAK,GAA5E,EAF2C,CAEsC;MAClF;IACF,CAlBkC,CAmBnC;;;IACAU,MAAM,CAACW,MAAP,GAAgBJ,YAAhB,CApBmC,CAoBL;;IAC9B;EACD;AACF;;AAGD,MAAMU,kBAAkB,GAAG,CAAC,KAAD,EAAQ,KAAR,EAAe,KAAf,EAAsB,KAAtB,CAA3B;AACA,MAAMC,kBAAkB,GAAG,CAAC,KAAD,EAAQ,KAAR,EAAe,KAAf,EAAsB,KAAtB,EAA6B,KAA7B,EAAoC,KAApC,CAA3B;;AAEA,SAASC,sBAAT,CAAgC3B,KAAhC,EAAuC;EACrC,OAAOyB,kBAAkB,CAACG,QAAnB,CAA4B5B,KAAK,CAACL,GAAlC,CAAP;AACD;;AAED,SAASkC,wBAAT,CAAkCjC,KAAlC,EAAyC;EACvC;EACA,OAAO,CAAC,CAAC,GAAD,EAAM,GAAN,EAAW,GAAX,EAAgB,GAAhB,EAAqB,GAArB,EAA0B,GAA1B,EAA+B,GAA/B,EAAoC,GAApC,EAAyC,GAAzC,EAA8CgC,QAA9C,CAAuDhC,KAAvD,CAAR;AACD;;AAED,SAASkC,sBAAT,CAAgC9B,KAAhC,EAAuC;EACrC,OAAO0B,kBAAkB,CAACE,QAAnB,CAA4B5B,KAAK,CAACL,GAAlC,CAAP;AACD;;AAED,SAASoC,yBAAT,CAAmCnC,KAAnC,EAA0CoC,KAA1C,EAAiD;EAC/C,OAAOA,KAAK,CAACC,IAAN,CAAWC,IAAI,IAAItC,KAAK,CAACuC,SAAN,CAAgB,CAAhB,EAAmBD,IAAI,CAACpB,MAAxB,MAAoCoB,IAAvD,CAAP;AACD;;AAED,SAASE,gCAAT,CAA0CpC,KAA1C,EAAiDqC,SAAS,GAAGC,SAA7D,EAAwE;EACtE,MAAMC,SAAS,GAAGvC,KAAK,CAACN,SAAN,CAAgBuC,IAAhB,CAAqB/B,EAAE,IAAIA,EAAE,CAACC,IAAH,KAAY,GAAvC,CAAlB;;EACA,IAAI,CAACoC,SAAL,EAAgB;IACd;IACA;EACD;;EAED,MAAMC,IAAI,GAAGD,SAAS,CAAC3C,KAAV,CAAgB6C,WAAhB,EAAb;;EAEA,IAAIJ,SAAS,CAACT,QAAV,CAAmB,KAAnB,CAAJ,EAA+B;IAC7B,MAAMc,KAAK,GAAGX,yBAAyB,CAACS,IAAD,EAAO,CAAC,IAAD,EAAO,KAAP,EAAc,MAAd,CAAP,CAAvC;;IACA,IAAIE,KAAJ,EAAW;MACT,OAAQ,GAAEA,KAAK,CAAC5B,MAAO,EAAvB;IACD;EACF;;EAED,IAAIuB,SAAS,CAACT,QAAV,CAAmB,KAAnB,CAAJ,EAA+B;IAC7B,MAAMc,KAAK,GAAGX,yBAAyB,CAACS,IAAD,EAAO,CAAC,KAAD,EAAQ,KAAR,CAAP,CAAvC;;IACA,IAAIE,KAAJ,EAAW;MACT,OAAQ,GAAEA,KAAK,CAAC5B,MAAO,EAAvB;IACD;EACF;;EAED,IAAIuB,SAAS,CAACT,QAAV,CAAmB,KAAnB,CAAJ,EAA+B;IAC7B,MAAMc,KAAK,GAAGX,yBAAyB,CAACS,IAAD,EAAO,CAAC,MAAD,EAAS,MAAT,EAAiB,MAAjB,CAAP,CAAvC;;IACA,IAAIE,KAAJ,EAAW;MACT,OAAQ,GAAEA,KAAK,CAAC5B,MAAO,EAAvB;IACD;EACF;;EAED,IAAIuB,SAAS,CAACT,QAAV,CAAmB,KAAnB,CAAJ,EAA+B;IAC7B,MAAMc,KAAK,GAAGX,yBAAyB,CAACS,IAAD,EAAO,CAAC,KAAD,EAAQ,MAAR,CAAP,CAAvC;;IACA,IAAIE,KAAJ,EAAW;MACT,OAAQ,GAAEA,KAAK,CAAC5B,MAAO,EAAvB;IACD;EACF,CAnCqE,CAqCtE;;;EACA,MAAM4B,KAAK,GAAGX,yBAAyB,CAACS,IAAD,EAAO,CAAC,MAAD,CAAP,CAAvC;;EACA,IAAIE,KAAJ,EAAW;IACT,OAAQ,GAAEA,KAAK,CAAC5B,MAAO,EAAvB;EACD;;EAED,OAAO,GAAP;AACD;;AAED,SAAS6B,4BAAT,CAAsC3C,KAAtC,EAA6CqC,SAAS,GAAG,EAAzD,EAA6D;EAC3D,IAAI,CAACV,sBAAsB,CAAC3B,KAAD,CAAvB,IAAkC,CAAC6B,wBAAwB,CAAC7B,KAAK,CAACH,IAAP,CAA/D,EAA6E;IAC3E;EACD;;EAEDG,KAAK,CAACH,IAAN,GAAauC,gCAAgC,CAACpC,KAAD,EAAQqC,SAAR,CAA7C,CAL2D,CAKM;AAClE;;AAED,SAASO,4BAAT,CAAsC5C,KAAtC,EAA6CqC,SAAS,GAAG,EAAzD,EAA6D;EAC3D,IAAI,CAACP,sBAAsB,CAAC9B,KAAD,CAAvB,IAAkC,CAAC6B,wBAAwB,CAAC7B,KAAK,CAACF,IAAP,CAA/D,EAA6E;IAC3E;EACD;;EAEDE,KAAK,CAACF,IAAN,GAAasC,gCAAgC,CAACpC,KAAD,EAAQqC,SAAR,CAA7C,CAL2D,CAKM;AAClE;;AAGD,SAASQ,sBAAT,CAAgC7C,KAAhC,EAAuCQ,MAAvC,EAA+C;EAC7C,IAAIR,KAAK,CAACL,GAAN,KAAc,KAAlB,EAAyB;IACvB;EACD;;EACD,MAAMmD,QAAQ,GAAGtC,MAAM,CAACuC,GAAP,CAAW,OAAX,CAAjB;EACA/C,KAAK,CAACH,IAAN,GAAaiD,QAAQ,CAAChC,MAAT,KAAoB,CAApB,GAAwB,GAAxB,GAA8B,GAA3C,CAL6C,CAKG;AACjD;;AAGD,SAASkC,kBAAT,CAA4BxC,MAA5B,EAAoC;EAClC,MAAMyC,SAAS,GAAGzC,MAAM,CAACuC,GAAP,CAAW,OAAX,CAAlB;EACA,MAAMG,SAAS,GAAG1C,MAAM,CAACuC,GAAP,CAAW,uBAAX,CAAlB;;EAEA,IAAIE,SAAS,CAACnC,MAAV,KAAqB,CAAzB,EAA4B;IAC1B;EACD;;EACD,IAAImC,SAAS,CAACnC,MAAV,IAAoBoC,SAAS,CAACpC,MAAlC,EAA0C;IACxC;IACAmC,SAAS,CAAC7B,OAAV,CAAkB3B,CAAC,IAAI;MACrBA,CAAC,CAACI,IAAF,GAAS,GAAT,CADqB,CACP;IACf,CAFD;IAGA;EACD;;EACD,IAAIqD,SAAS,CAACpC,MAAV,KAAqB,CAAzB,EAA4B;IAAE;IAC5BmC,SAAS,CAAC7B,OAAV,CAAkB3B,CAAC,IAAI;MACrBA,CAAC,CAACI,IAAF,GAAS,GAAT,CADqB,CACP;IACf,CAFD;IAGA;EACD,CAnBiC,CAoBlC;;AACD;;AAGD,SAASsD,YAAT,CAAsB3C,MAAtB,EAA8B;EAC5B,MAAM4C,UAAU,GAAG5C,MAAM,CAACuC,GAAP,CAAW,OAAX,CAAnB;;EAEA,IAAIK,UAAU,CAACtC,MAAX,KAAsB,CAA1B,EAA6B;IAC3B,OAAO,EAAP;EACD;;EAED,OAAOsC,UAAU,CAAC,CAAD,CAAV,CAAc1D,SAAd,CAAwB2D,MAAxB,CAA+BnD,EAAE,IAAIoD,kBAAkB,CAACpD,EAAD,CAAvD,EAA6DD,GAA7D,CAAiEsD,QAAQ,IAAIA,QAAQ,CAAC3D,KAAtF,CAAP;;EAEA,SAAS0D,kBAAT,CAA4BC,QAA5B,EAAsC;IACpC,IAAIA,QAAQ,CAACpD,IAAT,KAAkB,GAAlB,IAAyBoD,QAAQ,CAACpD,IAAT,KAAkB,GAA/C,EAAoD;MAClD,OAAO,KAAP;IACD;;IACD,IAAIoD,QAAQ,CAAC3D,KAAT,CAAekB,MAAf,KAA0B,CAA9B,EAAiC;MAC/B,OAAO,KAAP;IACD,CANmC,CAOpC;;;IACA,OAAO,IAAP;EACD;AAEF;;AAEM,SAASF,yBAAT,CAAmCJ,MAAnC,EAA2C;EAChDwC,kBAAkB,CAACxC,MAAD,CAAlB,CADgD,CAGhD;;EACA,MAAM6B,SAAS,GAAGc,YAAY,CAAC3C,MAAD,CAA9B;EAEAA,MAAM,CAACW,MAAP,CAAcC,OAAd,CAAsBpB,KAAK,IAAIwD,wBAAwB,CAACxD,KAAD,EAAQQ,MAAR,EAAgB6B,SAAhB,CAAvD;AAED;;AAED,SAASmB,wBAAT,CAAkCxD,KAAlC,EAAyCQ,MAAzC,EAAiD6B,SAAjD,EAA4D;EAC1DQ,sBAAsB,CAAC7C,KAAD,EAAQQ,MAAR,CAAtB;EACAmC,4BAA4B,CAAC3C,KAAD,EAAQqC,SAAR,CAA5B;EACAO,4BAA4B,CAAC5C,KAAD,EAAQqC,SAAR,CAA5B;AACD"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _chai = require("chai");
|
|
4
|
+
|
|
5
|
+
var _marcRecord = require("@natlibfi/marc-record");
|
|
6
|
+
|
|
7
|
+
var _indicatorFixes = _interopRequireDefault(require("./indicator-fixes"));
|
|
8
|
+
|
|
9
|
+
var _fixura = require("@natlibfi/fixura");
|
|
10
|
+
|
|
11
|
+
var _fixugen = _interopRequireDefault(require("@natlibfi/fixugen"));
|
|
12
|
+
|
|
13
|
+
var _debug = _interopRequireDefault(require("debug"));
|
|
14
|
+
|
|
15
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
16
|
+
|
|
17
|
+
(0, _fixugen.default)({
|
|
18
|
+
callback,
|
|
19
|
+
path: [__dirname, '..', 'test-fixtures', 'indicator-fixes'],
|
|
20
|
+
useMetadataFile: true,
|
|
21
|
+
recurse: false,
|
|
22
|
+
fixura: {
|
|
23
|
+
reader: _fixura.READERS.JSON
|
|
24
|
+
},
|
|
25
|
+
mocha: {
|
|
26
|
+
before: () => testValidatorFactory()
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
const debug = (0, _debug.default)('@natlibfi/marc-record-validators-melinda/indicator-fixes:test');
|
|
30
|
+
|
|
31
|
+
async function testValidatorFactory() {
|
|
32
|
+
const validator = await (0, _indicatorFixes.default)();
|
|
33
|
+
(0, _chai.expect)(validator).to.be.an('object').that.has.any.keys('description', 'validate');
|
|
34
|
+
(0, _chai.expect)(validator.description).to.be.a('string');
|
|
35
|
+
(0, _chai.expect)(validator.validate).to.be.a('function');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function callback({
|
|
39
|
+
getFixture,
|
|
40
|
+
enabled = true,
|
|
41
|
+
fix = false
|
|
42
|
+
}) {
|
|
43
|
+
if (enabled === false) {
|
|
44
|
+
debug('TEST SKIPPED!');
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const validator = await (0, _indicatorFixes.default)();
|
|
49
|
+
const record = new _marcRecord.MarcRecord(getFixture('record.json'));
|
|
50
|
+
const expectedResult = getFixture('expectedResult.json'); // console.log(expectedResult); // eslint-disable-line
|
|
51
|
+
|
|
52
|
+
if (!fix) {
|
|
53
|
+
const result = await validator.validate(record);
|
|
54
|
+
(0, _chai.expect)(result).to.eql(expectedResult);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
await validator.fix(record);
|
|
59
|
+
(0, _chai.expect)(record).to.eql(expectedResult);
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=indicator-fixes.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"indicator-fixes.spec.js","names":["generateTests","callback","path","__dirname","useMetadataFile","recurse","fixura","reader","READERS","JSON","mocha","before","testValidatorFactory","debug","createDebugLogger","validator","validatorFactory","expect","to","be","an","that","has","any","keys","description","a","validate","getFixture","enabled","fix","record","MarcRecord","expectedResult","result","eql"],"sources":["../src/indicator-fixes.spec.js"],"sourcesContent":["import {expect} from 'chai';\nimport {MarcRecord} from '@natlibfi/marc-record';\nimport validatorFactory from './indicator-fixes';\nimport {READERS} from '@natlibfi/fixura';\nimport generateTests from '@natlibfi/fixugen';\nimport createDebugLogger from 'debug';\n\ngenerateTests({\n callback,\n path: [__dirname, '..', 'test-fixtures', 'indicator-fixes'],\n useMetadataFile: true,\n recurse: false,\n fixura: {\n reader: READERS.JSON\n },\n mocha: {\n before: () => testValidatorFactory()\n }\n});\nconst debug = createDebugLogger('@natlibfi/marc-record-validators-melinda/indicator-fixes:test');\n\nasync function testValidatorFactory() {\n const validator = await validatorFactory();\n\n expect(validator)\n .to.be.an('object')\n .that.has.any.keys('description', 'validate');\n\n expect(validator.description).to.be.a('string');\n expect(validator.validate).to.be.a('function');\n}\n\nasync function callback({getFixture, enabled = true, fix = false}) {\n if (enabled === false) {\n debug('TEST SKIPPED!');\n return;\n }\n\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 expect(result).to.eql(expectedResult);\n return;\n }\n\n await validator.fix(record);\n expect(record).to.eql(expectedResult);\n}\n"],"mappings":";;AAAA;;AACA;;AACA;;AACA;;AACA;;AACA;;;;AAEA,IAAAA,gBAAA,EAAc;EACZC,QADY;EAEZC,IAAI,EAAE,CAACC,SAAD,EAAY,IAAZ,EAAkB,eAAlB,EAAmC,iBAAnC,CAFM;EAGZC,eAAe,EAAE,IAHL;EAIZC,OAAO,EAAE,KAJG;EAKZC,MAAM,EAAE;IACNC,MAAM,EAAEC,eAAA,CAAQC;EADV,CALI;EAQZC,KAAK,EAAE;IACLC,MAAM,EAAE,MAAMC,oBAAoB;EAD7B;AARK,CAAd;AAYA,MAAMC,KAAK,GAAG,IAAAC,cAAA,EAAkB,+DAAlB,CAAd;;AAEA,eAAeF,oBAAf,GAAsC;EACpC,MAAMG,SAAS,GAAG,MAAM,IAAAC,uBAAA,GAAxB;EAEA,IAAAC,YAAA,EAAOF,SAAP,EACGG,EADH,CACMC,EADN,CACSC,EADT,CACY,QADZ,EAEGC,IAFH,CAEQC,GAFR,CAEYC,GAFZ,CAEgBC,IAFhB,CAEqB,aAFrB,EAEoC,UAFpC;EAIA,IAAAP,YAAA,EAAOF,SAAS,CAACU,WAAjB,EAA8BP,EAA9B,CAAiCC,EAAjC,CAAoCO,CAApC,CAAsC,QAAtC;EACA,IAAAT,YAAA,EAAOF,SAAS,CAACY,QAAjB,EAA2BT,EAA3B,CAA8BC,EAA9B,CAAiCO,CAAjC,CAAmC,UAAnC;AACD;;AAED,eAAezB,QAAf,CAAwB;EAAC2B,UAAD;EAAaC,OAAO,GAAG,IAAvB;EAA6BC,GAAG,GAAG;AAAnC,CAAxB,EAAmE;EACjE,IAAID,OAAO,KAAK,KAAhB,EAAuB;IACrBhB,KAAK,CAAC,eAAD,CAAL;IACA;EACD;;EAED,MAAME,SAAS,GAAG,MAAM,IAAAC,uBAAA,GAAxB;EACA,MAAMe,MAAM,GAAG,IAAIC,sBAAJ,CAAeJ,UAAU,CAAC,aAAD,CAAzB,CAAf;EACA,MAAMK,cAAc,GAAGL,UAAU,CAAC,qBAAD,CAAjC,CARiE,CASjE;;EAEA,IAAI,CAACE,GAAL,EAAU;IACR,MAAMI,MAAM,GAAG,MAAMnB,SAAS,CAACY,QAAV,CAAmBI,MAAnB,CAArB;IACA,IAAAd,YAAA,EAAOiB,MAAP,EAAehB,EAAf,CAAkBiB,GAAlB,CAAsBF,cAAtB;IACA;EACD;;EAED,MAAMlB,SAAS,CAACe,GAAV,CAAcC,MAAd,CAAN;EACA,IAAAd,YAAA,EAAOc,MAAP,EAAeb,EAAf,CAAkBiB,GAAlB,CAAsBF,cAAtB;AACD"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
// Relocated from melinda-marc-record-merge-reducers (and renamed)
|
|
2
|
+
//import createDebugLogger from 'debug';
|
|
3
|
+
//const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda:normalizeIdentifiers');
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
function fieldToString(f) {
|
|
7
|
+
if (!f.subfields) {
|
|
8
|
+
return `${f.tag} ${f.value}`;
|
|
9
|
+
}
|
|
10
|
+
return `${f.tag} ${f.ind1}${f.ind2} ‡${formatSubfields(f)}`;
|
|
11
|
+
|
|
12
|
+
function formatSubfields(field) {
|
|
13
|
+
//return field.subfields.map(sf => `${sf.code}${sf.value || ''}`).join('‡');
|
|
14
|
+
return field.subfields.map(sf => `${sf.code}${sf.value}`).join(' ‡');
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/*
|
|
19
|
+
function nvdebug(message, func) {
|
|
20
|
+
if (func) { // eslint-disable-line functional/no-conditional-statement
|
|
21
|
+
func(message);
|
|
22
|
+
}
|
|
23
|
+
console.info(message); // eslint-disable-line no-console
|
|
24
|
+
}
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
export default function () {
|
|
28
|
+
|
|
29
|
+
// NB! We should and could handle ISNIs here as well.
|
|
30
|
+
return {
|
|
31
|
+
description: 'Normalizes indicator values',
|
|
32
|
+
validate, fix
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
function fix(record) {
|
|
36
|
+
const res = {message: [], fix: [], valid: true};
|
|
37
|
+
//message.fix = []; // eslint-disable-line functional/immutable-data
|
|
38
|
+
|
|
39
|
+
// Actual parsing of all fields
|
|
40
|
+
/*
|
|
41
|
+
if (!record.fields) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
recordNormalizeIndicators(record);
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
// message.valid = !(message.message.length >= 1); // eslint-disable-line functional/immutable-data
|
|
51
|
+
return res;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function validate(record) {
|
|
55
|
+
const res = {message: []};
|
|
56
|
+
//nvdebug(`NORMALIZE CONTROL NUMBER VALIDATE`, debug);
|
|
57
|
+
// Actual parsing of all fields
|
|
58
|
+
/*
|
|
59
|
+
if (!record.fields) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
*/
|
|
63
|
+
|
|
64
|
+
validateRecord(record, res);
|
|
65
|
+
|
|
66
|
+
res.valid = res.message.length < 1; // eslint-disable-line functional/immutable-data
|
|
67
|
+
return res;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
function validateRecord(record, res) {
|
|
72
|
+
//nvdebug(record);
|
|
73
|
+
const clonedFields = JSON.parse(JSON.stringify(record.fields));
|
|
74
|
+
recordNormalizeIndicators(record);
|
|
75
|
+
|
|
76
|
+
record.fields.forEach((field, index) => compareFields(field, index));
|
|
77
|
+
|
|
78
|
+
function compareFields(field, index) {
|
|
79
|
+
const origFieldAsString = fieldToString(clonedFields[index]);
|
|
80
|
+
//const clonedFieldAsString = fieldToString(field);
|
|
81
|
+
if (clonedFields[index].ind1 !== field.ind1) { // eslint-disable-line functional/no-conditional-statement
|
|
82
|
+
//nvdebug(`FIX IND1: '${clonedFields[index].ind1}' => '${field.ind1}': ${clonedFieldAsString}`);
|
|
83
|
+
res.message.push(`Expected IND1 for '${origFieldAsString}' is '${field.ind1}'`); // eslint-disable-line functional/immutable-data
|
|
84
|
+
}
|
|
85
|
+
if (clonedFields[index].ind2 !== field.ind2) { // eslint-disable-line functional/no-conditional-statement
|
|
86
|
+
//nvdebug(`FIX IND2: '${clonedFields[index].ind2}' => '${field.ind2}': ${clonedFieldAsString}`);
|
|
87
|
+
res.message.push(`Expected IND2 for '${origFieldAsString}' is '${field.ind2}'`); // eslint-disable-line functional/immutable-data
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// Validator should not change the original record:
|
|
91
|
+
record.fields = clonedFields; // eslint-disable-line functional/immutable-data
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
const ind1NonFilingChars = ['130', '630', '730', '740'];
|
|
98
|
+
const ind2NonFilingChars = ['222', '240', '242', '243', '245', '830'];
|
|
99
|
+
|
|
100
|
+
function hasNonFilingIndicator1(field) {
|
|
101
|
+
return ind1NonFilingChars.includes(field.tag);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function modifiableIndicatorValue(value) {
|
|
105
|
+
// If field contains a legit-looking value, don't try to modify it here...
|
|
106
|
+
return !['9', '8', '7', '6', '5', '4', '3', '2', '1'].includes(value);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function hasNonFilingIndicator2(field) {
|
|
110
|
+
return ind2NonFilingChars.includes(field.tag);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function valueBeginsWithDeterminer(value, cands) {
|
|
114
|
+
return cands.find(cand => value.substring(0, cand.length) === cand);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function determineNonFilingIndicatorValue(field, languages = undefined) {
|
|
118
|
+
const subfieldA = field.subfields.find(sf => sf.code === 'a');
|
|
119
|
+
if (!subfieldA) {
|
|
120
|
+
// nvdebug(' Subfield $a miss!');
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const name = subfieldA.value.toLowerCase();
|
|
125
|
+
|
|
126
|
+
if (languages.includes('eng')) {
|
|
127
|
+
const match = valueBeginsWithDeterminer(name, ['a ', 'an ', 'the ']);
|
|
128
|
+
if (match) {
|
|
129
|
+
return `${match.length}`;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (languages.includes('fre')) {
|
|
134
|
+
const match = valueBeginsWithDeterminer(name, ['l\'', 'le ']);
|
|
135
|
+
if (match) {
|
|
136
|
+
return `${match.length}`;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (languages.includes('ger')) {
|
|
141
|
+
const match = valueBeginsWithDeterminer(name, ['das ', 'der ', 'die ']);
|
|
142
|
+
if (match) {
|
|
143
|
+
return `${match.length}`;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (languages.includes('swe')) {
|
|
148
|
+
const match = valueBeginsWithDeterminer(name, ['en ', 'ett ']);
|
|
149
|
+
if (match) {
|
|
150
|
+
return `${match.length}`;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Fallback-ish: try to guess even without languages:
|
|
155
|
+
const match = valueBeginsWithDeterminer(name, ['the ']);
|
|
156
|
+
if (match) {
|
|
157
|
+
return `${match.length}`;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return '0';
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function normalizeNonFilingIndicator1(field, languages = []) {
|
|
164
|
+
if (!hasNonFilingIndicator1(field) || !modifiableIndicatorValue(field.ind1)) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
field.ind1 = determineNonFilingIndicatorValue(field, languages); // eslint-disable-line functional/immutable-data
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function normalizeNonFilingIndicator2(field, languages = []) {
|
|
172
|
+
if (!hasNonFilingIndicator2(field) || !modifiableIndicatorValue(field.ind2)) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
field.ind2 = determineNonFilingIndicatorValue(field, languages); // eslint-disable-line functional/immutable-data
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
function normalize245Indicator1(field, record) {
|
|
181
|
+
if (field.tag !== '245') {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const field1XX = record.get('^1..$');
|
|
185
|
+
field.ind1 = field1XX.length === 0 ? '0' : '1'; // eslint-disable-line functional/immutable-data
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
function recordNormalize490(record) {
|
|
190
|
+
const fields490 = record.get('^490$');
|
|
191
|
+
const fields8XX = record.get('^(?:800|810|811|830)$');
|
|
192
|
+
|
|
193
|
+
if (fields490.length === 0) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (fields490.length <= fields8XX.length) {
|
|
197
|
+
// Trace found for each field 490:
|
|
198
|
+
fields490.forEach(f => {
|
|
199
|
+
f.ind1 = '1'; // eslint-disable-line functional/immutable-data
|
|
200
|
+
});
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
if (fields8XX.length === 0) { // Fields 490 are always untraced (no traces found)
|
|
204
|
+
fields490.forEach(f => {
|
|
205
|
+
f.ind1 = '0'; // eslint-disable-line functional/immutable-data
|
|
206
|
+
});
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
// For other combinations we just can't be sure, so leave them as they are.
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
function getLanguages(record) {
|
|
214
|
+
const langFields = record.get('^041$');
|
|
215
|
+
|
|
216
|
+
if (langFields.length === 0) {
|
|
217
|
+
return [];
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return langFields[0].subfields.filter(sf => isRelevantSubfield(sf)).map(subfield => subfield.value);
|
|
221
|
+
|
|
222
|
+
function isRelevantSubfield(subfield) {
|
|
223
|
+
if (subfield.code !== 'a' && subfield.code !== 'd') {
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
if (subfield.value.length !== 3) {
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
// We could require /^[a-z][a-z][a-z]$/ etc as well, but it's not really that relevant.
|
|
230
|
+
return true;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export function recordNormalizeIndicators(record) {
|
|
236
|
+
recordNormalize490(record);
|
|
237
|
+
|
|
238
|
+
// Language is used to handle non-filing indicators
|
|
239
|
+
const languages = getLanguages(record);
|
|
240
|
+
|
|
241
|
+
record.fields.forEach(field => fieldNormalizeIndicators(field, record, languages));
|
|
242
|
+
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function fieldNormalizeIndicators(field, record, languages) {
|
|
246
|
+
normalize245Indicator1(field, record);
|
|
247
|
+
normalizeNonFilingIndicator1(field, languages);
|
|
248
|
+
normalizeNonFilingIndicator2(field, languages);
|
|
249
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import {expect} from 'chai';
|
|
2
|
+
import {MarcRecord} from '@natlibfi/marc-record';
|
|
3
|
+
import validatorFactory from './indicator-fixes';
|
|
4
|
+
import {READERS} from '@natlibfi/fixura';
|
|
5
|
+
import generateTests from '@natlibfi/fixugen';
|
|
6
|
+
import createDebugLogger from 'debug';
|
|
7
|
+
|
|
8
|
+
generateTests({
|
|
9
|
+
callback,
|
|
10
|
+
path: [__dirname, '..', 'test-fixtures', 'indicator-fixes'],
|
|
11
|
+
useMetadataFile: true,
|
|
12
|
+
recurse: false,
|
|
13
|
+
fixura: {
|
|
14
|
+
reader: READERS.JSON
|
|
15
|
+
},
|
|
16
|
+
mocha: {
|
|
17
|
+
before: () => testValidatorFactory()
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda/indicator-fixes:test');
|
|
21
|
+
|
|
22
|
+
async function testValidatorFactory() {
|
|
23
|
+
const validator = await validatorFactory();
|
|
24
|
+
|
|
25
|
+
expect(validator)
|
|
26
|
+
.to.be.an('object')
|
|
27
|
+
.that.has.any.keys('description', 'validate');
|
|
28
|
+
|
|
29
|
+
expect(validator.description).to.be.a('string');
|
|
30
|
+
expect(validator.validate).to.be.a('function');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function callback({getFixture, enabled = true, fix = false}) {
|
|
34
|
+
if (enabled === false) {
|
|
35
|
+
debug('TEST SKIPPED!');
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const validator = await validatorFactory();
|
|
40
|
+
const record = new MarcRecord(getFixture('record.json'));
|
|
41
|
+
const expectedResult = getFixture('expectedResult.json');
|
|
42
|
+
// console.log(expectedResult); // eslint-disable-line
|
|
43
|
+
|
|
44
|
+
if (!fix) {
|
|
45
|
+
const result = await validator.validate(record);
|
|
46
|
+
expect(result).to.eql(expectedResult);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
await validator.fix(record);
|
|
51
|
+
expect(record).to.eql(expectedResult);
|
|
52
|
+
}
|