@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.
@@ -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":"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"}
@@ -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
@@ -14,7 +14,7 @@
14
14
  "url": "git@github.com:natlibfi/marc-record-validators-melinda.git"
15
15
  },
16
16
  "license": "MIT",
17
- "version": "9.0.13",
17
+ "version": "9.0.14",
18
18
  "main": "./dist/index.js",
19
19
  "publishConfig": {
20
20
  "access": "public"
@@ -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
+ }