@natlibfi/marc-record-validators-melinda 10.13.1-alpha.2 → 10.14.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/dist/ending-punctuation-conf.js +3 -6
  2. package/dist/ending-punctuation-conf.js.map +1 -1
  3. package/dist/index.js +7 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/punctuation/rules/aut.js +1 -2
  6. package/dist/punctuation/rules/aut.js.map +1 -1
  7. package/dist/punctuation/rules/bib.js +1 -2
  8. package/dist/punctuation/rules/bib.js.map +1 -1
  9. package/dist/punctuation2.js +40 -14
  10. package/dist/punctuation2.js.map +1 -1
  11. package/dist/removeInferiorDataFields.js +166 -168
  12. package/dist/removeInferiorDataFields.js.map +1 -1
  13. package/dist/sortFields.js +383 -0
  14. package/dist/sortFields.js.map +1 -0
  15. package/dist/sortFields.spec.js +51 -0
  16. package/dist/sortFields.spec.js.map +1 -0
  17. package/dist/sortRelatorTerms.js +3 -25
  18. package/dist/sortRelatorTerms.js.map +1 -1
  19. package/dist/sortSubfields.js +14 -4
  20. package/dist/sortSubfields.js.map +1 -1
  21. package/dist/sortSubfields.spec.js +4 -3
  22. package/dist/sortSubfields.spec.js.map +1 -1
  23. package/dist/utils.js +4 -0
  24. package/dist/utils.js.map +1 -1
  25. package/package.json +7 -7
  26. package/src/index.js +3 -1
  27. package/src/punctuation2.js +14 -7
  28. package/src/removeInferiorDataFields.js +166 -169
  29. package/src/sortFields.js +393 -0
  30. package/src/sortFields.spec.js +52 -0
  31. package/src/sortRelatorTerms.js +3 -25
  32. package/src/sortSubfields.js +18 -4
  33. package/src/sortSubfields.spec.js +3 -3
  34. package/src/utils.js +5 -0
  35. package/test-fixtures/remove-inferior-datafields/f01/expectedResult.json +12 -0
  36. package/test-fixtures/remove-inferior-datafields/f01/record.json +19 -0
  37. package/test-fixtures/sort-fields/01/input.json +32 -0
  38. package/test-fixtures/sort-fields/01/metadata.json +5 -0
  39. package/test-fixtures/sort-fields/01/result.json +29 -0
  40. package/test-fixtures/sort-fields/02/input.json +49 -0
  41. package/test-fixtures/sort-fields/02/metadata.json +4 -0
  42. package/test-fixtures/sort-fields/02/result.json +50 -0
  43. package/test-fixtures/sort-fields/03/input.json +20 -0
  44. package/test-fixtures/sort-fields/03/metadata.json +5 -0
  45. package/test-fixtures/sort-fields/03/result.json +21 -0
  46. package/test-fixtures/sort-fields/04/input.json +13 -0
  47. package/test-fixtures/sort-fields/04/metadata.json +4 -0
  48. package/test-fixtures/sort-fields/04/result.json +13 -0
  49. package/test-fixtures/sort-fields/05/input.json +27 -0
  50. package/test-fixtures/sort-fields/05/metadata.json +4 -0
  51. package/test-fixtures/sort-fields/05/result.json +28 -0
  52. package/test-fixtures/sort-fields/06/input.json +36 -0
  53. package/test-fixtures/sort-fields/06/metadata.json +4 -0
  54. package/test-fixtures/sort-fields/06/result.json +37 -0
  55. package/test-fixtures/sort-fields/07/input.json +21 -0
  56. package/test-fixtures/sort-fields/07/metadata.json +4 -0
  57. package/test-fixtures/sort-fields/07/result.json +22 -0
  58. package/test-fixtures/sort-fields/08/input.json +29 -0
  59. package/test-fixtures/sort-fields/08/metadata.json +5 -0
  60. package/test-fixtures/sort-fields/08/result.json +29 -0
  61. package/test-fixtures/sort-fields/09/input.json +41 -0
  62. package/test-fixtures/sort-fields/09/metadata.json +5 -0
  63. package/test-fixtures/sort-fields/09/result.json +42 -0
  64. package/test-fixtures/sort-fields/10/input.json +54 -0
  65. package/test-fixtures/sort-fields/10/metadata.json +5 -0
  66. package/test-fixtures/sort-fields/10/result.json +53 -0
  67. package/test-fixtures/sort-subfields/f02/expectedResult.json +26 -0
  68. package/test-fixtures/sort-subfields/f02/metadata.json +7 -0
  69. package/test-fixtures/sort-subfields/f02/record.json +25 -0
@@ -0,0 +1,393 @@
1
+ // Taken from project marc-record-js, file marcSortFields.js as this contains more and more Melinda-specific rules.
2
+
3
+ import clone from 'clone';
4
+ import createDebugLogger from 'debug';
5
+ import {fieldHasSubfield, fieldToString} from './utils';
6
+ import {isValidSubfield8} from './subfield8Utils';
7
+ import {isValidSubfield6, subfield6GetOccurrenceNumber} from './subfield6Utils';
8
+
9
+
10
+ const BIG_BAD_NUMBER = 999.99;
11
+
12
+ const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda:sortFields');
13
+ //const debugData = debug.extend('data');
14
+ const debugDev = debug.extend('dev');
15
+
16
+ export default function () {
17
+
18
+ return {
19
+ description: 'Sort fields',
20
+ validate, fix
21
+ };
22
+
23
+ function fix(record) {
24
+ const res = {message: [], fix: [], valid: true};
25
+
26
+ record.fields.sort(fieldOrderComparator); // eslint-disable-line functional/immutable-data
27
+
28
+ return res;
29
+ }
30
+
31
+ function validate(record) {
32
+ const res = {message: []};
33
+
34
+ const fields = record.fields.map(f => clone(f));
35
+ fields.sort(fieldOrderComparator); // eslint-disable-line functional/immutable-data
36
+
37
+
38
+ const relocatedFields = fields.filter((f, i) => fieldToString(f) !== fieldToString(record.fields[i]));
39
+
40
+ if (relocatedFields.length > 0) { // eslint-disable-line functional/no-conditional-statements
41
+ res.message.push(`${relocatedFields.length} field(s) in new places`); // eslint-disable-line functional/immutable-data
42
+ }
43
+
44
+ res.valid = !(res.message.length >= 1); // eslint-disable-line functional/immutable-data
45
+ return res;
46
+ }
47
+ }
48
+
49
+
50
+ export const relatorTermScore = { // Here bigger is better
51
+ // NB! This is exportable as field internal $e sorting in marc-record-validators-js uses this.
52
+ // NB! The more abstract, the earlier it appears.
53
+ // Note that terms with same abstraction level might also have order preferences
54
+ // We should 1) check the order of these, and 2) add translations (support Swedish at the very least)
55
+ // work/teos > expression/ekspressio > manifestation/manifestaatio
56
+ 'säveltäjä': 100, 'composer': 100,
57
+ 'kirjoittaja': 99, 'author': 100,
58
+ 'sarjakuvantekijä': 99,
59
+ 'taiteilija': 98,
60
+ 'sanoittaja': 90,
61
+ 'käsikirjoittaja': 90,
62
+ // expression:
63
+ 'toimittaja': 80, 'editor': 80,
64
+ 'sovittaja': 80, 'arranger': 80,
65
+ 'kuvittaja': 75,
66
+ 'editointi': 71, // for music, editor/toimittaja is another thing
67
+ 'kääntäjä': 70,
68
+ 'lukija': 61,
69
+ // Manifestation level
70
+ 'esittäjä': 60,
71
+ 'johtaja': 50, // orkesterinjohtaja
72
+ 'kustantaja': 41,
73
+ 'julkaisija': 40
74
+
75
+ };
76
+
77
+ export function scoreRelatorTerm(value) { // sortRelatorTerms.js validator should call this on future version
78
+ const normValue = value.replace(/[.,]+$/u, '');
79
+ if (normValue in relatorTermScore) {
80
+ return relatorTermScore[normValue];
81
+ }
82
+ return 0;
83
+ }
84
+
85
+ export function fieldOrderComparator(fieldA, fieldB) {
86
+
87
+ const sorterFunctions = [sortByTag, sortByIndexTerms, sortAlphabetically, sortByRelatorTerm, sortByOccurrenceNumber, preferFenniKeep, sortByFieldLinkAndSequenceNumber];
88
+
89
+ for (const sortFn of sorterFunctions) { // eslint-disable-line functional/no-loop-statements
90
+ const result = sortFn(fieldA, fieldB);
91
+ debugDev(`${sortFn.name}: '${fieldToString(fieldA)}' vs '${fieldToString(fieldB)}': ${result}`);
92
+ if (result !== 0) {
93
+ return result;
94
+ }
95
+ }
96
+
97
+ return 0;
98
+
99
+ function sortByTag(fieldA, fieldB) {
100
+
101
+ function getSortIndex(tag) {
102
+ const sortIndex = {
103
+ LDR: '000',
104
+ STA: '001.1', // STA comes now after 001. However 003+001 form a combo, so I'm not sure...
105
+ SID: '999.1',
106
+ LOW: '999.2',
107
+ CAT: '999.3',
108
+ HLI: '999.4'
109
+ };
110
+
111
+ if (tag in sortIndex) { // <- this allows weights for numeric values as well (not that we use them yet)
112
+ return sortIndex[tag];
113
+ }
114
+ if (isNaN(tag)) {
115
+ return '999.9';
116
+ }
117
+ return tag;
118
+ }
119
+
120
+ const orderA = getSortIndex(fieldA.tag);
121
+ const orderB = getSortIndex(fieldB.tag);
122
+
123
+ if (orderA > orderB) {
124
+ return 1;
125
+ }
126
+ if (orderA < orderB) {
127
+ return -1;
128
+ }
129
+
130
+ return 0;
131
+ }
132
+
133
+ function sortByIndexTerms(fieldA, fieldB) { // eslint-disable-line complexity, max-statements
134
+
135
+ const indexTermFields = ['600', '610', '611', '630', '648', '650', '651', '652', '653', '654', '655', '656', '657', '658', '659', '662'];
136
+
137
+ function scoreInd2(val) {
138
+ const ind2Score = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 8, '5': 5, '6': 6, '7': 7};
139
+
140
+ if (val in ind2Score) {
141
+ return ind2Score[val];
142
+ }
143
+ return 9;
144
+ }
145
+
146
+ // ATM this is not needed.
147
+ // You may need this, if you change compare function order in sorterFunctions
148
+ // istanbul ignore next
149
+ if (fieldA.tag !== fieldB.tag) {
150
+ return 0;
151
+ }
152
+
153
+ if (!indexTermFields.includes(fieldA.tag)) {
154
+ return 0;
155
+ }
156
+
157
+ /* Puts ind2=4 last */
158
+ if (scoreInd2(fieldA.ind2) > scoreInd2(fieldB.ind2)) {
159
+ return 1;
160
+ }
161
+ if (scoreInd2(fieldA.ind2) < scoreInd2(fieldB.ind2)) {
162
+ return -1;
163
+ }
164
+
165
+ function scoreDictionary(dictionary) {
166
+ const dictionarySortIndex = {
167
+ 'yso/fin': 0,
168
+ 'yso/swe': 1,
169
+ 'yso/eng': 2,
170
+ 'slm/fin': 0.1,
171
+ 'slm/swe': 1.1,
172
+ 'kauno/fin': 2.1,
173
+ 'kauno/swe': 2.2,
174
+ 'kaunokki': 4,
175
+ 'bella': 5
176
+ };
177
+
178
+ if (dictionary in dictionarySortIndex) {
179
+ return dictionarySortIndex[dictionary];
180
+ }
181
+ return BIG_BAD_NUMBER;
182
+ }
183
+
184
+ const dictionaryA = selectFirstValue(fieldA, '2');
185
+ const dictionaryB = selectFirstValue(fieldB, '2');
186
+
187
+ const dictScoreA = scoreDictionary(dictionaryA);
188
+ const dictScoreB = scoreDictionary(dictionaryB);
189
+ // Use priority order for listed dictionaries:
190
+ if (dictScoreA > dictScoreB) {
191
+ return 1;
192
+ }
193
+ if (dictScoreA < dictScoreB) {
194
+ return -1;
195
+ }
196
+ // Unlisted dictionaries: sort $2 value alphabetically:
197
+ //if (dictScoreA === BIG_BAD_NUMBER) {
198
+ if (dictionaryA > dictionaryB) {
199
+ return 1;
200
+ }
201
+ if (dictionaryA < dictionaryB) {
202
+ return -1;
203
+ }
204
+ //}
205
+ return 0;
206
+ }
207
+
208
+ function preferKeep(fieldA, fieldB, keepOwner = 'FENNI') {
209
+ const hasKeepA = fieldHasSubfield(fieldA, '9', `${keepOwner}<KEEP>`);
210
+ const hasKeepB = fieldHasSubfield(fieldB, '9', `${keepOwner}<KEEP>`);
211
+
212
+ if (hasKeepA && !hasKeepB) {
213
+ return -1;
214
+ }
215
+ if (!hasKeepA && hasKeepB) {
216
+ return 1;
217
+ }
218
+
219
+ return 0;
220
+ }
221
+
222
+ function preferFenniKeep(fieldA, fieldB) {
223
+ const fenniPreference = preferKeep(fieldA, fieldB, 'FENNI');
224
+ if (fenniPreference !== 0) {
225
+ return fenniPreference;
226
+ }
227
+ const violaPreference = preferKeep(fieldA, fieldB, 'VIOLA');
228
+ if (violaPreference !== 0) {
229
+ return violaPreference;
230
+ }
231
+ return preferKeep(fieldA, fieldB, 'FIKKA');
232
+ }
233
+
234
+ function sortByRelatorTerm(fieldA, fieldB) {
235
+ // Should this be done to 6XX and 8XX fields as well? Does $t affect sorting?
236
+ if (!['700', '710', '711', '730'].includes(fieldA.tag)) {
237
+ return 0;
238
+ }
239
+
240
+
241
+ function fieldGetMaxRelatorTermScore(field) {
242
+ if (!field.subfields) {
243
+ return 0;
244
+ }
245
+ const e = field.subfields.filter(sf => sf.code === 'e');
246
+ const scores = e.map(sf => scoreRelatorTerm(sf.value));
247
+ //debugDev(`RELATOR SCORE FOR '${fieldToString(field)}': ${scores.join(', ')}`);
248
+ return Math.max(...scores);
249
+ }
250
+
251
+ const scoreA = fieldGetMaxRelatorTermScore(fieldA);
252
+ const scoreB = fieldGetMaxRelatorTermScore(fieldB);
253
+
254
+ if (scoreA < scoreB) {
255
+ return 1;
256
+ }
257
+ if (scoreA > scoreB) {
258
+ return -1;
259
+ }
260
+ return 0;
261
+ }
262
+
263
+ function fieldGetMinLinkAndSequenceNumber(field) {
264
+ if (!field.subfields) {
265
+ return BIG_BAD_NUMBER;
266
+ }
267
+ const relevantSubfields = field.subfields.filter(sf => isValidSubfield8(sf));
268
+ // If val is something like "1.2\x" parseFloat() would give a syntax erro because of hex-like escape sequnce (at least on Chrome). Thus remove tail:
269
+ const scores = relevantSubfields.map(sf => parseFloat(sf.value.replace(/\\.*$/u, '')));
270
+ if (scores.length === 0) {
271
+ return BIG_BAD_NUMBER;
272
+ }
273
+ return Math.min(...scores);
274
+ }
275
+
276
+ function sortByFieldLinkAndSequenceNumber(fieldA, fieldB) { // Sort by subfield $8 that is...
277
+ const scoreA = fieldGetMinLinkAndSequenceNumber(fieldA);
278
+ const scoreB = fieldGetMinLinkAndSequenceNumber(fieldB);
279
+ //debugDev(` sf-8-A-score for '${fieldToString(fieldA)}: ${scoreA}`);
280
+ //debugDev(` sf-8-B-score for '${fieldToString(fieldB)}: ${scoreB}`);
281
+ if (scoreA === scoreB) {
282
+ return 0;
283
+ }
284
+ if (scoreB === 0) {
285
+ return 1;
286
+ }
287
+ if (scoreA === 0) {
288
+ return -1;
289
+ }
290
+ if (scoreA > scoreB) { // smaller is better
291
+ return 1;
292
+ }
293
+ return -1;
294
+ }
295
+
296
+ function sortByOccurrenceNumber(fieldA, fieldB) { // Sort by subfield $6
297
+
298
+ function fieldGetOccurrenceNumber(field) { // should this function be exported? (based on validator sortRelatorFields.js)
299
+ if (!field.subfields) {
300
+ return 0;
301
+ }
302
+ const subfield6 = field.subfields.find(sf => isValidSubfield6(sf));
303
+ if (subfield6 === undefined) {
304
+ return 0;
305
+ }
306
+ return parseInt(subfield6GetOccurrenceNumber(subfield6), 10);
307
+ }
308
+
309
+ if (fieldA.tag !== '880') {
310
+ return 0;
311
+ }
312
+ const scoreA = fieldGetOccurrenceNumber(fieldA);
313
+ const scoreB = fieldGetOccurrenceNumber(fieldB);
314
+
315
+ //debugDev(`A: '${fieldToString(fieldA)}: ${scoreA}`);
316
+ //debugDev(`B: '${fieldToString(fieldB)}: ${scoreB}`);
317
+
318
+ if (scoreA === scoreB) {
319
+ return 0;
320
+ }
321
+ if (scoreB === 0) {
322
+ return -1;
323
+ }
324
+ if (scoreA === 0) {
325
+ return 1;
326
+ }
327
+ if (scoreA > scoreB) { // smaller is better
328
+ return 1;
329
+ }
330
+ return -1;
331
+ }
332
+
333
+ function sortAlphabetically(fieldA, fieldB) {
334
+ const tagToSortingSubfields = {
335
+ // '028': ['b', 'a']?
336
+ 'LOW': ['a'],
337
+ 'SID': ['c']
338
+ };
339
+
340
+ function scoreSubfieldsAlphabetically(setOfSubfields) {
341
+ if (setOfSubfields.length === 0) {
342
+ return 0;
343
+ }
344
+ const [subfieldCode, ...remainingSubfieldCodes] = setOfSubfields;
345
+ const valA = selectFirstValue(fieldA, subfieldCode);
346
+ const valB = selectFirstValue(fieldB, subfieldCode);
347
+ //debugDev(`CHECKING SUBFIELD '${subfieldCode}'`);
348
+ if (!valA) {
349
+ if (!valB) {
350
+ return scoreSubfieldsAlphabetically(remainingSubfieldCodes);
351
+ }
352
+ return -1;
353
+ }
354
+ if (!valB) {
355
+ return 1;
356
+ }
357
+ //debugDev(`CHECKING SUBFIELD '${subfieldCode}': '${valA}' vs '${valB}'`);
358
+
359
+ if (valA < valB) {
360
+ return -1;
361
+ }
362
+ if (valB < valA) {
363
+ return 1;
364
+ }
365
+ return scoreSubfieldsAlphabetically(remainingSubfieldCodes);
366
+ }
367
+
368
+ if (fieldA.tag === fieldB.tag) {
369
+ if (!(fieldA.tag in tagToSortingSubfields)) {
370
+ return 0;
371
+ }
372
+
373
+ const subfieldsToCheck = tagToSortingSubfields[fieldA.tag];
374
+
375
+ //debugDev(`CHECKING ${subfieldsToCheck.join(', ')}`);
376
+ const result = scoreSubfieldsAlphabetically(subfieldsToCheck);
377
+ //debugDev(`RESULT ${result}`);
378
+ return result;
379
+ }
380
+ return 0;
381
+ }
382
+
383
+ //-----------------------------------------------------------------------------
384
+
385
+
386
+ function selectFirstValue(field, subcode) {
387
+ return field.subfields
388
+ .filter(subfield => subcode === subfield.code)
389
+ .map(subfield => subfield.value)
390
+ .slice(0, 1);
391
+ }
392
+ }
393
+
@@ -0,0 +1,52 @@
1
+ import {expect} from 'chai';
2
+ import {MarcRecord} from '@natlibfi/marc-record';
3
+ import validatorFactory from './sortFields';
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', 'sort-fields'],
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/sortFields: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 = true}) {
34
+ if (enabled === false) {
35
+ debug('TEST SKIPPED!');
36
+ return;
37
+ }
38
+
39
+ const validator = await validatorFactory();
40
+ const record = new MarcRecord(getFixture('input.json'));
41
+ const expectedResult = getFixture('result.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
+ }
@@ -6,38 +6,16 @@ import clone from 'clone';
6
6
  //import createDebugLogger from 'debug';
7
7
  import {fieldToString} from './utils';
8
8
  import {fieldFixPunctuation} from './punctuation2';
9
+ import {relatorTermScore} from './sortFields';
9
10
  //const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda:sortRelatorTerms');
10
11
  //const debugData = debug.extend('data');
11
12
 
12
13
  const WORST_WORK = 98;
13
14
 
14
- const relatorTermValues = { // The higher, the better
15
- // More abstract, the earlier it appears.
16
- // Note that terms with same abstraction level might also have order preferences
17
- // work/teos > expression/ekspressio > manifestation/manifestaatio
18
- 'säveltäjä': 100,
19
- 'kirjoittaja': 99, // Viola wants composer/säveltäjä on top (highly unlikely to ever appear together, but...)
20
- 'taiteilija': 98,
21
- 'sanoittaja': 90,
22
- // ekspressio
23
- 'sovittaja': 80,
24
- 'toimittaja': 80,
25
- 'kuvittaja': 75,
26
- 'editointi': 71,
27
- 'kääntäjä': 70,
28
- 'lukija': 61,
29
- // manifestaatio
30
- 'esittäjä': 60,
31
- 'johtaja': 50,
32
- 'kustantaja': 41,
33
- 'julkaisija': 40
34
-
35
- };
36
-
37
15
  function scoreRelatorTerm(term) {
38
16
  const normalizedTerm = normalizeValue(term);
39
- if (normalizedTerm in relatorTermValues) {
40
- return relatorTermValues[normalizedTerm];
17
+ if (normalizedTerm in relatorTermScore) {
18
+ return relatorTermScore[normalizedTerm];
41
19
  }
42
20
  return 0;
43
21
  }
@@ -15,17 +15,29 @@ const defaultSortOrderFinns = defaultSortOrderStringFinns.split('');
15
15
  const defaultSortOrderOthers = defaultSortOrderStringOthers.split('');
16
16
 
17
17
 
18
- export default function () {
18
+ export default function (tagPattern) {
19
19
 
20
20
  return {
21
21
  description: 'Swap adjacent subfields',
22
22
  validate, fix
23
23
  };
24
24
 
25
- function fix(record) {
25
+ function getRelevantFields(record, tagPattern) {
26
+ const datafields = record.fields.filter(f => f.subfields);
27
+ if (!tagPattern) {
28
+ return datafields;
29
+ }
30
+
31
+ const regexp = new RegExp(tagPattern, 'u');
32
+ return datafields.filter(f => regexp.test(f.tag));
33
+ }
34
+
35
+ function fix(record, tagPattern) {
26
36
  const res = {message: [], fix: [], valid: true};
27
37
 
28
- record.fields.forEach(field => {
38
+ const relevantFields = getRelevantFields(record, tagPattern);
39
+
40
+ relevantFields.forEach(field => {
29
41
  sortAdjacentSubfields(field);
30
42
  });
31
43
 
@@ -35,7 +47,9 @@ export default function () {
35
47
  function validate(record) {
36
48
  const res = {message: []};
37
49
 
38
- record.fields.forEach(field => {
50
+ const relevantFields = getRelevantFields(record, tagPattern);
51
+
52
+ relevantFields.forEach(field => {
39
53
  const clonedField = clone(field);
40
54
  sortAdjacentSubfields(clonedField);
41
55
  const clonedFieldAsString = fieldToString(clonedField);
@@ -30,7 +30,7 @@ async function testValidatorFactory() {
30
30
  expect(validator.validate).to.be.a('function');
31
31
  }
32
32
 
33
- async function callback({getFixture, enabled = true, fix = false}) {
33
+ async function callback({getFixture, enabled = true, fix = false, tagPattern = false}) {
34
34
  if (enabled === false) {
35
35
  debug('TEST SKIPPED!');
36
36
  return;
@@ -42,11 +42,11 @@ async function callback({getFixture, enabled = true, fix = false}) {
42
42
  // console.log(expectedResult); // eslint-disable-line
43
43
 
44
44
  if (!fix) {
45
- const result = await validator.validate(record);
45
+ const result = await validator.validate(record, tagPattern);
46
46
  expect(result).to.eql(expectedResult);
47
47
  return;
48
48
  }
49
49
 
50
- await validator.fix(record);
50
+ await validator.fix(record, tagPattern);
51
51
  expect(record).to.eql(expectedResult);
52
52
  }
package/src/utils.js CHANGED
@@ -73,3 +73,8 @@ export function getCatalogingLanguage(record) {
73
73
  }
74
74
  return b.value;
75
75
  }
76
+
77
+
78
+ export function uniqArray(arr) {
79
+ return arr.filter((val, i) => arr.indexOf(val) === i);
80
+ }
@@ -2,6 +2,18 @@
2
2
  "_validationOptions": {},
3
3
  "fields": [
4
4
  { "tag": "001", "value": "f01" },
5
+
6
+ { "tag": "610", "ind1": "2", "ind2": "4", "subfields": [
7
+ { "code": "a", "value": "MELKEHITYS-2383a."},
8
+ { "code": "0", "value": "(FI_ASTERI-N)000111222" },
9
+ { "code": "9", "value": "FENNI<KEEP>" }
10
+ ]},
11
+
12
+ { "tag": "610", "ind1": "2", "ind2": "4", "subfields": [
13
+ { "code": "a", "value": "MELKEHITYS-2383b."},
14
+ { "code": "0", "value": "(FI_ASTERI-N)000111223" }
15
+ ]},
16
+
5
17
  { "tag": "653", "ind1": " ", "ind2": " ", "subfields": [
6
18
  { "code": "a", "value": "term"},
7
19
  { "code": "9", "value": "FENNI<KEEP>"}
@@ -1,6 +1,25 @@
1
1
  {
2
2
  "fields": [
3
3
  { "tag": "001", "value": "f01" },
4
+
5
+ { "tag": "610", "ind1": "2", "ind2": "4", "subfields": [
6
+ { "code": "a", "value": "MELKEHITYS-2383a."},
7
+ { "code": "0", "value": "(FI_ASTERI-N)000111222" },
8
+ { "code": "9", "value": "FENNI<KEEP>" }
9
+ ]},
10
+ { "tag": "610", "ind1": "2", "ind2": "4", "subfields": [
11
+ { "code": "a", "value": "MELKEHITYS-2383a."}
12
+ ]},
13
+
14
+ { "tag": "610", "ind1": "2", "ind2": "4", "subfields": [
15
+ { "code": "a", "value": "MELKEHITYS-2383b."},
16
+ { "code": "0", "value": "(FI_ASTERI-N)000111223" }
17
+ ]},
18
+ { "tag": "610", "ind1": "2", "ind2": "4", "subfields": [
19
+ { "code": "a", "value": "MELKEHITYS-2383b."}
20
+ ]},
21
+
22
+
4
23
  { "tag": "653", "ind1": " ", "ind2": " ", "subfields": [ { "code": "a", "value": "term"} ]},
5
24
  { "tag": "653", "ind1": " ", "ind2": " ", "subfields": [
6
25
  { "code": "a", "value": "term"},
@@ -0,0 +1,32 @@
1
+ {
2
+ "leader": "12345cam a22002417i 4500",
3
+ "fields": [
4
+ {"tag": "001", "value": "000123456"},
5
+ {"tag": "003", "value": "(FI-MELINDA)"},
6
+ {"tag": "005", "value": "20210730133112.0"},
7
+ {"tag": "020", "ind1": " ", "ind2": " ", "subfields": [{"code": "a", "value": "123456789012X"}]},
8
+ {"tag": "100", "ind1": "1", "ind2": " ", "subfields": [{"code": "a", "value": "Sukunimi, Etunimi"}]},
9
+ {"tag": "245", "ind1": "1", "ind2": " ", "subfields": [{"code": "a", "value": "Nimeke."}]},
10
+ {"tag": "500", "ind1": " ", "ind2": " ", "subfields": [{"code": "a", "value": "Lorum ipsum."}]},
11
+ {"tag": "300", "ind1": " ", "ind2": " ", "subfields": [{"code": "a", "value": "1 nide (123 sivua)"}]},
12
+ {"tag": "FOO", "ind1": " ", "ind2": " ", "subfields": [{"code": "a", "value": "BAR"}]},
13
+ {"tag": "900", "ind1": " ", "ind2": " ", "subfields": [
14
+ {"code": "a", "value": "Auel, Jean M."},
15
+ {"code": "y", "value": "Untinen-Auel, Jean M."}
16
+ ]},
17
+
18
+ {"tag": "STA", "ind1": " ", "ind2": " ", "subfields": [{"code": "a", "value": "DELETED"}]},
19
+
20
+ {"tag": "SID", "ind1": " ", "ind2": " ", "subfields": [{"code": "b", "value": "123"}, {"code": "c", "value": "alma"}]},
21
+
22
+ {"tag": "LOW", "ind1": " ", "ind2": " ", "subfields": [{"code": "a", "value": "ALMA"}]},
23
+
24
+ {"tag": "CAT", "subfields": [
25
+ {"code": "a", "value": "CONV-ISBD"},
26
+ {"code": "b", "value": "30"},
27
+ {"code": "c", "value": "20090101"},
28
+ {"code": "l", "value": "FIN"},
29
+ {"code": "h", "value": "1205"}
30
+ ]}
31
+ ]
32
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "description": "Tag-based sorting (also SID, LOW, ...)",
3
+ "skip": false,
4
+ "only": false
5
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "leader": "12345cam a22002417i 4500",
3
+ "fields": [
4
+ {"tag": "001", "value": "000123456"},
5
+ {"tag": "STA", "ind1": " ", "ind2": " ", "subfields": [{"code": "a", "value": "DELETED"}]},
6
+ {"tag": "003", "value": "(FI-MELINDA)"},
7
+ {"tag": "005", "value": "20210730133112.0"},
8
+ {"tag": "020", "ind1": " ", "ind2": " ", "subfields": [{"code": "a", "value": "123456789012X"}]},
9
+ {"tag": "100", "ind1": "1", "ind2": " ", "subfields": [{"code": "a", "value": "Sukunimi, Etunimi"}]},
10
+ {"tag": "245", "ind1": "1", "ind2": " ", "subfields": [{"code": "a", "value": "Nimeke."}]},
11
+ {"tag": "300", "ind1": " ", "ind2": " ", "subfields": [{"code": "a", "value": "1 nide (123 sivua)"}]},
12
+ {"tag": "500", "ind1": " ", "ind2": " ", "subfields": [{"code": "a", "value": "Lorum ipsum."}]},
13
+ {"tag": "900", "ind1": " ", "ind2": " ", "subfields": [{"code": "a", "value": "Auel, Jean M."}, {"code": "y", "value": "Untinen-Auel, Jean M."}]},
14
+
15
+ {"tag": "SID", "ind1": " ", "ind2": " ", "subfields": [{"code": "b", "value": "123"}, {"code": "c", "value": "alma"}]},
16
+
17
+ {"tag": "LOW", "ind1": " ", "ind2": " ", "subfields": [{"code": "a", "value": "ALMA"}]},
18
+
19
+ {"tag": "CAT", "ind1": " ", "ind2": " ", "subfields": [
20
+ {"code": "a", "value": "CONV-ISBD"},
21
+ {"code": "b", "value": "30"},
22
+ {"code": "c", "value": "20090101"},
23
+ {"code": "l", "value": "FIN"},
24
+ {"code": "h", "value": "1205"}
25
+ ]},
26
+ {"tag": "FOO", "ind1": " ", "ind2": " ", "subfields": [{"code": "a", "value": "BAR"}]}
27
+ ],
28
+ "_validationOptions": {}
29
+ }