@natlibfi/marc-record-validators-melinda 11.5.5 → 11.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/field-505-separators.js +3 -0
  2. package/dist/field-505-separators.js.map +1 -1
  3. package/dist/index.js +7 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/modernize-502.js +117 -0
  6. package/dist/modernize-502.js.map +1 -0
  7. package/dist/modernize-502.spec.js +49 -0
  8. package/dist/modernize-502.spec.js.map +1 -0
  9. package/dist/punctuation2.js +17 -4
  10. package/dist/punctuation2.js.map +1 -1
  11. package/dist/subfieldValueNormalizations.js +47 -8
  12. package/dist/subfieldValueNormalizations.js.map +1 -1
  13. package/package.json +7 -6
  14. package/src/field-505-separators.js +3 -0
  15. package/src/index.js +2 -0
  16. package/src/modernize-502.js +118 -0
  17. package/src/modernize-502.spec.js +48 -0
  18. package/src/punctuation2.js +6 -4
  19. package/src/subfieldValueNormalizations.js +50 -9
  20. package/test-fixtures/field-505-separators/03/expectedResult.json +23 -0
  21. package/test-fixtures/field-505-separators/03/metadata.json +6 -0
  22. package/test-fixtures/field-505-separators/03/record.json +21 -0
  23. package/test-fixtures/modernize-502/01/expectedResult.json +6 -0
  24. package/test-fixtures/modernize-502/01/metadata.json +5 -0
  25. package/test-fixtures/modernize-502/01/record.json +15 -0
  26. package/test-fixtures/modernize-502/02/expectedResult.json +29 -0
  27. package/test-fixtures/modernize-502/02/metadata.json +5 -0
  28. package/test-fixtures/modernize-502/02/record.json +31 -0
  29. package/test-fixtures/normalize-subfield-value/04/expectedResult.json +24 -0
  30. package/test-fixtures/normalize-subfield-value/04/metadata.json +5 -0
  31. package/test-fixtures/normalize-subfield-value/04/record.json +22 -0
  32. package/test-fixtures/normalize-subfield-value/05/expectedResult.json +27 -0
  33. package/test-fixtures/normalize-subfield-value/05/metadata.json +6 -0
  34. package/test-fixtures/normalize-subfield-value/05/record.json +25 -0
  35. package/test-fixtures/punctuation2/01/expectedResult.json +2 -2
  36. package/test-fixtures/punctuation2/100_and_880/expectedResult.json +12 -2
  37. package/test-fixtures/punctuation2/100_and_880/metadata.json +2 -1
  38. package/test-fixtures/punctuation2/100_and_880/record.json +12 -2
  39. package/test-fixtures/punctuation2/97/expectedResult.json +1 -1
  40. package/test-fixtures/punctuation2/98/expectedResult.json +2 -2
  41. package/test-fixtures/sort-relator-terms/f02/expectedResult.json +8 -1
  42. package/test-fixtures/sort-relator-terms/f02/record.json +7 -0
@@ -0,0 +1,118 @@
1
+ //import createDebugLogger from 'debug';
2
+ import clone from 'clone';
3
+ import {fieldToString} from './utils';
4
+
5
+ //const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda/modernize-502');
6
+
7
+ // Author(s): Nicholas Volk
8
+ export default function () {
9
+
10
+ return {
11
+ description: 'Normalizes Finnish national convention 502$a$c$d fields to a 502$a (which is better supported by LoC).',
12
+ validate, fix
13
+ };
14
+
15
+ function fix(record) {
16
+ const res = {message: [], fix: [], valid: true};
17
+
18
+ record.fields.forEach(field => {
19
+ normalizeField502(field);
20
+ });
21
+
22
+ // message.valid = !(message.message.length >= 1); // eslint-disable-line functional/immutable-data
23
+ return res;
24
+ }
25
+
26
+ function validate(record) {
27
+ const res = {message: []};
28
+
29
+ record.fields.forEach(field => {
30
+ validateField(field, res);
31
+ });
32
+
33
+ res.valid = !(res.message.length >= 1); // eslint-disable-line functional/immutable-data
34
+ return res;
35
+ }
36
+
37
+ function validateField(field, res) {
38
+ if (!field.subfields) {
39
+ return;
40
+ }
41
+ const orig = fieldToString(field);
42
+
43
+ const normalizedField = normalizeField502(clone(field));
44
+ const mod = fieldToString(normalizedField);
45
+ if (orig !== mod) { // Fail as the input is "broken"/"crap"/sumthing
46
+ res.message.push(`Fix '${orig}' => '${mod}'`); // eslint-disable-line functional/immutable-data
47
+ return;
48
+ }
49
+ return;
50
+ }
51
+ }
52
+
53
+
54
+ export function normalizeField502(field) {
55
+ if (!field.subfields) {
56
+ return field;
57
+ }
58
+ const acd = field.subfields.filter(sf => 'acd'.includes(sf.code));
59
+ const str = acd.map(sf => sf.code).join('');
60
+ // Check that we have relevant subfields and that they are in relevant order (with each other):
61
+ if (!['acd', 'ac', 'ad'].includes(str)) {
62
+ return field;
63
+ }
64
+
65
+ // "a = acd[0]"" is way more readable than "[a] = acd"...
66
+ const a = acd[0]; // eslint-disable-line prefer-destructuring
67
+ const c = acd[1].code === 'c' ? acd[1] : null;
68
+ const d = acd[acd.length - 1].code === 'd' ? acd[acd.length - 1] : null;
69
+
70
+ //console.log(JSON.stringify(d)); // eslint-disable-line no-console
71
+
72
+ if (!hasValidA() || !hasValidD()) {
73
+ return field;
74
+ }
75
+
76
+ const newValue = `${extractA()}--${extractC()}${extractD()}`;
77
+ a.value = newValue; // eslint-disable-line functional/immutable-data
78
+ field.subfields = field.subfields.filter(sf => !['c', 'd'].includes(sf.code)); // eslint-disable-line functional/immutable-data
79
+ return field;
80
+
81
+ function extractA() {
82
+ return a.value.replace(/[ ,:]+$/u, '');
83
+ }
84
+
85
+ function extractC() {
86
+ if (!c) {
87
+ return '';
88
+ }
89
+ if (c) {
90
+ // Here we assume that there was correct punctuation between $c and $d...
91
+ return `${c.value}${d ? ' ' : ''}`;
92
+ }
93
+ return c.value;
94
+ }
95
+
96
+ function extractD() {
97
+ return d ? d.value : '';
98
+ }
99
+
100
+ function hasValidA() {
101
+ // Belongs to https://finto.fi/mts/fi/page/m91
102
+ if (a.value.match(/^(?:AMK-opinnäytetyö|Anbalyysiseminaarityö|Artikkeliväitöskirja|Diplomityö|Erikoistyö|Esseeväitöskirja|Kandidaatintutkielma|Laudaturseminaarityö|Laudaturtyö|Lisensiaatintyö|Lopputyö|Monografiaväitöskirja|Opinnäyte|Opinnäytetyö|Pro gradu -tutkielma|Proseminaarityö|Seminaarityö|Väitöskirja|Ylempi AMK-opinnäytetyö)[, :]*$/u) ||
103
+ a.value.match(/^(?:Analysseminariearbete|Artikelavhandling|Diplomarbete|Doktorsavhandling|Essäavhandling|Högre YH-examensarbete|Kandidatavhandling|Laudaturarbete|Laudaturseminariearbete|Licentiatavhandling|Lärdomsprov|Monografiavhandling|Pro gradu-avhandling|Proseminariearbete|Seminariearbete|Slutarbete|Specialarbete|YH-examesarbete)[:, ]*$/u)) {
104
+ return true;
105
+ }
106
+ return false;
107
+ }
108
+
109
+
110
+ function hasValidD() {
111
+ if (!d) { // We can live without $d:
112
+ return true;
113
+ }
114
+ // Content makes sense:
115
+ return d.value.match(/^\[?(?:[0-9]{4}|[0-9]{4}-[0-9]{4}|1[89]uu|Vuosien [0-9]{4} ja [0-9]{4} välillä)[\].]{0,2}$/u);
116
+ }
117
+
118
+ }
@@ -0,0 +1,48 @@
1
+ import {expect} from 'chai';
2
+ import {MarcRecord} from '@natlibfi/marc-record';
3
+ import validatorFactory from './modernize-502';
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', 'modernize-502'],
11
+ useMetadataFile: true,
12
+ recurse: false,
13
+ fixura: {
14
+ reader: READERS.JSON
15
+ },
16
+ mocha: {
17
+ before: () => testValidatorFactory()
18
+ }
19
+ });
20
+
21
+ // const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda/modernize-502:test');
22
+
23
+ async function testValidatorFactory() {
24
+ const validator = await validatorFactory();
25
+
26
+ expect(validator)
27
+ .to.be.an('object')
28
+ .that.has.any.keys('description', 'validate');
29
+
30
+ expect(validator.description).to.be.a('string');
31
+ expect(validator.validate).to.be.a('function');
32
+ }
33
+
34
+ async function callback({getFixture, fix = false}) {
35
+ const validator = await validatorFactory();
36
+ const record = new MarcRecord(getFixture('record.json'));
37
+ const expectedResult = getFixture('expectedResult.json');
38
+ // console.log(expectedResult); // eslint-disable-line
39
+
40
+ if (!fix) {
41
+ const result = await validator.validate(record);
42
+ expect(result).to.eql(expectedResult);
43
+ return;
44
+ }
45
+
46
+ await validator.fix(record);
47
+ expect(record).to.eql(expectedResult);
48
+ }
@@ -100,13 +100,14 @@ const puncIsProbablyPunc = /(?:[a-z0-9)]|å|ä|ö) ?[.,:;]$/u;
100
100
 
101
101
  // Will unfortunately trigger "Sukunimi, Th." type:
102
102
  const removeColons = {'code': 'abcdefghijklmnopqrstuvwxyz', 'remove': / *[;:]$/u};
103
- const removeX00Comma = {'code': 'abcdenqt', 'followedBy': 'abcdenqtv#', 'context': /.,$/u, 'remove': /,$/u};
103
+ const removeX00Comma = {'code': 'abcdejnqt', 'followedBy': 'abcdenqtv#', 'context': /.,$/u, 'remove': /,$/u};
104
104
  const cleanRHS = {'code': 'abcd', 'followedBy': 'bcde', 'context': /(?:(?:[a-z0-9]|å|ä|ö)\.|,)$/u, 'contextRHS': blocksPuncRHS, 'remove': /[.,]$/u};
105
105
  const cleanX00dCommaOrDot = {'code': 'd', 'followedBy': 'et#', 'context': /[0-9]-[,.]$/u, 'remove': /[,.]$/u};
106
106
  const cleanX00aDot = {'code': 'abcde', 'followedBy': 'cdegj', 'context': dotIsProbablyPunc, 'remove': /\.$/u};
107
107
  const cleanCorruption = {'code': 'abcdefghijklmnopqrstuvwxyz', 'remove': / \.$/u};
108
108
  // These $e dot removals are tricky: before removing the comma, we should know that it ain't an abbreviation such as "esitt."...
109
109
  const cleanX00eDot = {'code': 'e', 'followedBy': 'egj#', 'context': /(?:[ai]ja|jä)[.,]$/u, 'remove': /\.$/u};
110
+ const cleanX11jDot = {'code': 'e', 'followedBy': 'egj#', 'context': /(?:[ai]ja|jä)[.,]$/u, 'remove': /\.$/u};
110
111
  const removeCommaBeforeLanguageSubfieldL = {'followedBy': 'l', 'remove': /,$/u};
111
112
  const removeCommaBeforeTitleSubfieldT = {'followedBy': 't', 'remove': /,$/u};
112
113
 
@@ -117,7 +118,8 @@ const cleanPuncBeforeLanguage = {'code': 'atvxyz', 'followedBy': 'l', 'context':
117
118
  const addX00aComma = {'add': ',', 'code': 'abcqej', 'followedBy': 'cdeg', 'context': doesNotEndInPunc, 'contextRHS': allowsPuncRHS};
118
119
  const addX00dComma = {'name': 'X00$d ending in "-" does not get comma', 'add': ',', 'code': 'd', 'followedBy': 'cdeg', 'context': /[^-,.!]$/u, 'contextRHS': allowsPuncRHS};
119
120
  const addX00aComma2 = {'add': ',', 'code': 'abcdej', 'followedBy': 'cdeg', 'context': /(?:[A-Z]|Å|Ä|Ö)\.$/u, 'contextRHS': allowsPuncRHS};
120
- const addX00Dot = {'add': '.', 'code': 'abcdetv', 'followedBy': '#fklptu', 'context': needsPuncAfterAlphanumeric};
121
+ const addX00Dot = {'add': '.', 'code': 'abcdetv', 'followedBy': 'fklptu', 'context': needsPuncAfterAlphanumeric};
122
+ const addEntryFieldFinalDot = {'name': 'X00 final dot', 'add': '.', 'code': 'abcdefghijklmnopqrstuvwxyz', 'followedBy': '#', 'context': /[^.)]$/u};
121
123
 
122
124
 
123
125
  const addX10iColon = {name: 'Punctuate relationship information', add: ':', code: 'i', context: defaultNeedsPuncAfter2};
@@ -145,7 +147,7 @@ const removeCrapFromAllEntryFields = [removeCommaBeforeLanguageSubfieldL, remove
145
147
 
146
148
  const removeX00Whatever = [removeX00Comma, cleanX00aDot, cleanX00eDot, cleanCorruption, cleanX00dCommaOrDot, cleanRHS, X00RemoveDotAfterBracket, removeColons, cleanPuncBeforeLanguage, ...removeCrapFromAllEntryFields];
147
149
  const removeX10Whatever = [removeX00Comma, cleanX00aDot, cleanX00eDot, cleanCorruption, removeColons, cleanPuncBeforeLanguage, ...removeCrapFromAllEntryFields];
148
- const removeX11Whatever = [...removeCrapFromAllEntryFields];
150
+ const removeX11Whatever = [removeX00Comma, cleanX11jDot, ...removeCrapFromAllEntryFields];
149
151
  const removeX30Whatever = removeCrapFromAllEntryFields;
150
152
 
151
153
  const remove490And830Whatever = [{'code': 'axyzv', 'followedBy': 'axyzv', 'remove': /(?: *;| *=|,)$/u}];
@@ -299,7 +301,7 @@ const cleanValidPunctuationRules = {
299
301
 
300
302
 
301
303
  // Overgeneralizes a bit: eg. addColonToRelationshipInformation only applies to 700/710 but as others don't have $i, it's fine.
302
- const addToAllEntryFields = [addDotBeforeLanguageSubfieldL, addSemicolonBeforeVolumeDesignation, addColonToRelationshipInformation];
304
+ const addToAllEntryFields = [addDotBeforeLanguageSubfieldL, addSemicolonBeforeVolumeDesignation, addColonToRelationshipInformation, addEntryFieldFinalDot];
303
305
 
304
306
 
305
307
  const addX00 = [addX00aComma, addX00aComma2, addX00Dot, addX00dComma, ...addToAllEntryFields];
@@ -1,9 +1,10 @@
1
1
  //import createDebugLogger from 'debug';
2
2
  import clone from 'clone';
3
- import {fieldHasSubfield, fieldToString} from './utils';
3
+ import {fieldHasSubfield, fieldToString, getCatalogingLanguage} from './utils';
4
4
  import {fieldFixPunctuation} from './punctuation2';
5
5
  import {fieldGetUnambiguousTag} from './subfield6Utils';
6
6
 
7
+ // NB! You should probably run punctuation fixes after this validator!
7
8
 
8
9
  // Author(s): Nicholas Volk
9
10
  export default function () {
@@ -14,10 +15,11 @@ export default function () {
14
15
  };
15
16
 
16
17
  function fix(record) {
18
+ const catLang = getCatalogingLanguage(record, 'fin');
17
19
  const res = {message: [], fix: [], valid: true};
18
20
 
19
21
  record.fields.forEach(field => {
20
- normalizeSubfieldValues(field);
22
+ normalizeSubfieldValues(field, catLang);
21
23
  });
22
24
 
23
25
  // message.valid = !(message.message.length >= 1); // eslint-disable-line functional/immutable-data
@@ -25,23 +27,24 @@ export default function () {
25
27
  }
26
28
 
27
29
  function validate(record) {
30
+ const catLang = getCatalogingLanguage(record, 'fin');
28
31
  const res = {message: []};
29
32
 
30
33
  record.fields.forEach(field => {
31
- validateField(field, res);
34
+ validateField(field, res, catLang);
32
35
  });
33
36
 
34
37
  res.valid = !(res.message.length >= 1); // eslint-disable-line functional/immutable-data
35
38
  return res;
36
39
  }
37
40
 
38
- function validateField(field, res) {
41
+ function validateField(field, res, catLang) {
39
42
  if (!field.subfields) {
40
43
  return;
41
44
  }
42
45
  const orig = fieldToString(field);
43
46
 
44
- const normalizedField = normalizeSubfieldValues(clone(field));
47
+ const normalizedField = normalizeSubfieldValues(clone(field), catLang);
45
48
  const mod = fieldToString(normalizedField);
46
49
  if (orig !== mod) { // Fail as the input is "broken"/"crap"/sumthing
47
50
  res.message.push(`'${orig}' requires subfield internal mods/normalization`); // eslint-disable-line functional/immutable-data
@@ -73,9 +76,46 @@ function handleInitials(value, subfieldCode, field) {
73
76
  }
74
77
  }
75
78
 
76
- function getNormalizedValue(subfield, field) {
77
- return uppercaseLanguage(handleMovies(handleInitials(subfield.value, subfield.code, field)));
79
+ function getNormalizedValue(subfield, field, catLang) {
80
+ return handleFikt(uppercaseLanguage(handleMovies(handleInitials(subfield.value, subfield.code, field))));
78
81
 
82
+ function handleFikt(value) {
83
+ if (subfield.code !== 'c' || field.tag !== '600') {
84
+ return value;
85
+ }
86
+
87
+ return wrapFiktiivinenHahmo(expandFikt(value));
88
+
89
+ function wrapFiktiivinenHahmo(value) {
90
+ if (value.includes('(')) { // Some kind of parentheses already found -> nothing to do here
91
+ return value;
92
+ }
93
+ if (value.match(/^(?:fiktiivinen hahmo|fiktiv gestalt|fiktiivinen yhteisö)[,.]?$/u)) {
94
+ // Hope that some other module handles punctuation, if needed:
95
+ return `(${value.replace(/[.,]$/u, '')})`;
96
+ }
97
+ return value;
98
+ }
99
+
100
+ // wrap text around parentheses
101
+ function expandFikt(value) {
102
+ if (field.ind1 === '3') { // Not handling "fiktiivinen yhteisö" at the moment
103
+ return value;
104
+ }
105
+
106
+ if (value.match(/\bfikt\.?(?:$|[,)])/u) && !value.match(/fikt.*fikt/ui)) {
107
+ // NB! Dot '.' in 'fikt.' might also be punctuation as well. Run punctuation2 fixer after this fixer!
108
+ if (catLang === 'fin') {
109
+ return value.replace(/\bfikt\./u, 'fiktiivinen hahmo');
110
+ }
111
+ if (catLang === 'swe') {
112
+ return value.replace(/\bfikt\./u, 'fiktiv gestalt');
113
+ }
114
+ }
115
+ return value;
116
+ }
117
+
118
+ }
79
119
 
80
120
  function handleMovies(value) {
81
121
  if (subfield.code === 'a' && ['130', '630', '730'].includes(field.tag)) {
@@ -85,6 +125,7 @@ function getNormalizedValue(subfield, field) {
85
125
  return value;
86
126
  }
87
127
 
128
+
88
129
  function uppercaseLanguage(value) { // Part of MET-549
89
130
  const relevantTags = ['130', '240', '243', '600', '610', '611', '630', '700', '710', '711', '730', '800', '810', '811', '830'];
90
131
 
@@ -108,12 +149,12 @@ function getNormalizedValue(subfield, field) {
108
149
  }
109
150
  }
110
151
 
111
- function normalizeSubfieldValues(field) {
152
+ function normalizeSubfieldValues(field, catLang) {
112
153
  if (!field.subfields) {
113
154
  return field;
114
155
  }
115
156
  field.subfields.forEach((subfield, index) => {
116
- field.subfields[index].value = getNormalizedValue(subfield, field); // eslint-disable-line functional/immutable-data
157
+ field.subfields[index].value = getNormalizedValue(subfield, field, catLang); // eslint-disable-line functional/immutable-data
117
158
  });
118
159
  return field;
119
160
  }
@@ -0,0 +1,23 @@
1
+ {
2
+ "_validationOptions": {},
3
+ "fields": [
4
+ {
5
+ "tag": "005",
6
+ "value": "20220202020202.0"
7
+ },
8
+ {
9
+ "tag": "505", "ind1": "0", "ind2": " ",
10
+ "subfields": [
11
+ { "code": "a", "value": "1. Johdanto -- 2. Käsitteelliset lähtökohdat -- 3. Teoksen lähestymistapa, aineistot ja tutkimusmenetelmät -- 4. Nousu eliittiin -- 5. Huippujohtajiin kohdistuvat odotukset -- 6. Vallan verkostot -- 7. Eliittien asenteellinen yhtenäisyys -- 8. Horisontaalinen uraliikkuvuus -- 9. Eliitit muutoksessa." }
12
+ ]
13
+ },
14
+ {
15
+ "tag": "505", "ind1": "0", "ind2": " ",
16
+ "subfields": [
17
+ { "code": "g" },
18
+ { "code": "t"}
19
+ ]
20
+ }
21
+ ],
22
+ "leader": ""
23
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "description": "505 -- survive corruption",
3
+ "comment": "As seen on 2025-03-31 (iffy Helmet data)",
4
+ "fix": true,
5
+ "only": false
6
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "fields": [
3
+ {
4
+ "tag": "005",
5
+ "value": "20220202020202.0"
6
+ },
7
+ {
8
+ "tag": "505", "ind1": "0", "ind2": " ",
9
+ "subfields": [
10
+ { "code": "a", "value": "1. Johdanto -- 2. Käsitteelliset lähtökohdat -- 3. Teoksen lähestymistapa, aineistot ja tutkimusmenetelmät -- 4. Nousu eliittiin -- 5. Huippujohtajiin kohdistuvat odotukset -- 6. Vallan verkostot -- 7. Eliittien asenteellinen yhtenäisyys -- 8. Horisontaalinen uraliikkuvuus -- 9. Eliitit muutoksessa." }
11
+ ]
12
+ },
13
+ {
14
+ "tag": "505", "ind1": "0", "ind2": " ",
15
+ "subfields": [
16
+ { "code": "g" },
17
+ { "code": "t"}
18
+ ]
19
+ }
20
+ ]
21
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "message": [
3
+ "Fix '502 ## ‡a Väitöskirja : ‡c Helsingin yliopisto, ‡d 1980.' => '502 ## ‡a Väitöskirja--Helsingin yliopisto, 1980.'"
4
+ ],
5
+ "valid": false
6
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "description": "Validate 502$a$c$d => 502$a mod",
3
+ "fix": false,
4
+ "only": false
5
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "fields": [
3
+ { "tag": "005", "value": "20220202020202.0" },
4
+ { "tag": "502", "ind1": " ", "ind2": " ", "subfields": [
5
+ { "code": "a", "value": "Väitöskirja :" },
6
+ { "code": "c", "value": "Helsingin yliopisto," },
7
+ { "code": "d", "value": "1980." }
8
+ ]},
9
+ { "tag": "502", "ind1": " ", "ind2": " ", "subfields": [
10
+ { "code": "a", "value": "Uimamaisterin päättökoe :" },
11
+ { "code": "c", "value": "Helsingin Uimastadion," },
12
+ { "code": "d", "value": "1980." }
13
+ ]}
14
+ ]
15
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "_validationOptions": {},
3
+ "leader": "",
4
+ "fields": [
5
+ { "tag": "005", "value": "20220202020202.0" },
6
+ { "tag": "502", "ind1": " ", "ind2": " ", "subfields": [
7
+ { "code": "a", "value": "Väitöskirja--Helsingin yliopisto, 1980." }
8
+ ]},
9
+ { "tag": "502", "ind1": " ", "ind2": " ", "subfields": [
10
+ { "code": "a", "value": "Väitöskirja--Helsingin yliopisto, 1980." }
11
+ ]},
12
+ { "tag": "502", "ind1": " ", "ind2": " ", "subfields": [
13
+ { "code": "a", "value": "Uimamaisterin päättökoe :" },
14
+ { "code": "c", "value": "Helsingin Uimastadion," },
15
+ { "code": "d", "value": "1980." }
16
+ ]},
17
+ { "tag": "502", "ind1": " ", "ind2": " ", "subfields": [
18
+ { "code": "a", "value": "Pro gradu -avledning :" },
19
+ { "code": "c", "value": "Helsingfors universitet," },
20
+ { "code": "d", "value": "19uu." }
21
+ ]},
22
+ { "tag": "502", "ind1": " ", "ind2": " ", "subfields": [
23
+ { "code": "a", "value": "Opinnäyte--Unicafe Rotunda." }
24
+ ]},
25
+ { "tag": "502", "ind1": " ", "ind2": " ", "subfields": [
26
+ { "code": "a", "value": "Opinnäyte--2099." }
27
+ ]}
28
+ ]
29
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "description": "Fix 502$a$c$d => 502$a mod",
3
+ "fix": true,
4
+ "only": false
5
+ }
@@ -0,0 +1,31 @@
1
+ {
2
+ "fields": [
3
+ { "tag": "005", "value": "20220202020202.0" },
4
+ { "tag": "502", "ind1": " ", "ind2": " ", "subfields": [
5
+ { "code": "a", "value": "Väitöskirja :" },
6
+ { "code": "c", "value": "Helsingin yliopisto," },
7
+ { "code": "d", "value": "1980." }
8
+ ]},
9
+ { "tag": "502", "ind1": " ", "ind2": " ", "subfields": [
10
+ { "code": "a", "value": "Väitöskirja--Helsingin yliopisto, 1980." }
11
+ ]},
12
+ { "tag": "502", "ind1": " ", "ind2": " ", "subfields": [
13
+ { "code": "a", "value": "Uimamaisterin päättökoe :" },
14
+ { "code": "c", "value": "Helsingin Uimastadion," },
15
+ { "code": "d", "value": "1980." }
16
+ ]},
17
+ { "tag": "502", "ind1": " ", "ind2": " ", "subfields": [
18
+ { "code": "a", "value": "Pro gradu -avledning :" },
19
+ { "code": "c", "value": "Helsingfors universitet," },
20
+ { "code": "d", "value": "19uu." }
21
+ ]},
22
+ { "tag": "502", "ind1": " ", "ind2": " ", "subfields": [
23
+ { "code": "a", "value": "Opinnäyte :" },
24
+ { "code": "c", "value": "Unicafe Rotunda." }
25
+ ]},
26
+ { "tag": "502", "ind1": " ", "ind2": " ", "subfields": [
27
+ { "code": "a", "value": "Opinnäyte," },
28
+ { "code": "d", "value": "2099." }
29
+ ]}
30
+ ]
31
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "_validationOptions": {},
3
+ "leader": "",
4
+ "fields": [
5
+ { "tag": "005", "value": "20220202020202.0" },
6
+ { "tag": "600", "ind1": "1", "ind2": "4", "subfields": [
7
+ { "code": "a", "value": "What, Ever," },
8
+ { "code": "c", "value": "(fiktiivinen hahmo)" }
9
+ ]},
10
+ { "tag": "600", "ind1": "1", "ind2": "4", "subfields": [
11
+ { "code": "a", "value": "What, Never" },
12
+ { "code": "c", "value": "(fiktiivinen hahmo)" }
13
+ ]},
14
+ { "tag": "600", "ind1": "1", "ind2": "4", "subfields": [
15
+ { "code": "a", "value": "What, Fever" },
16
+ { "code": "c", "value": "(fiktiivinen hahmo)" }
17
+ ]},
18
+ { "tag": "600", "ind1": "3", "ind2": "4", "subfields": [
19
+ { "code": "a", "value": "Whatever" },
20
+ { "code": "c", "value": "(fikt.)" }
21
+ ]}
22
+
23
+ ]
24
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "description": "Apply 600$c fikt. normalization",
3
+ "fix": true,
4
+ "only": false
5
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "fields": [
3
+ { "tag": "005", "value": "20220202020202.0" },
4
+ { "tag": "600", "ind1": "1", "ind2": "4", "subfields": [
5
+ { "code": "a", "value": "What, Ever," },
6
+ { "code": "c", "value": "fikt." }
7
+ ]},
8
+ { "tag": "600", "ind1": "1", "ind2": "4", "subfields": [
9
+ { "code": "a", "value": "What, Never" },
10
+ { "code": "c", "value": "(fikt.)" }
11
+ ]},
12
+ { "tag": "600", "ind1": "1", "ind2": "4", "subfields": [
13
+ { "code": "a", "value": "What, Fever" },
14
+ { "code": "c", "value": "fiktiivinen hahmo." }
15
+ ]},
16
+ { "tag": "600", "ind1": "3", "ind2": "4", "subfields": [
17
+ { "code": "a", "value": "Whatever" },
18
+ { "code": "c", "value": "(fikt.)" }
19
+ ]}
20
+
21
+ ]
22
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "_validationOptions": {},
3
+ "leader": "",
4
+ "fields": [
5
+ { "tag": "005", "value": "20220202020202.0" },
6
+ { "tag": "040", "ind1": " ", "ind2": " ", "subfields": [
7
+ { "code": "b", "value": "swe" }
8
+ ]},
9
+ { "tag": "600", "ind1": "1", "ind2": "4", "subfields": [
10
+ { "code": "a", "value": "What, Ever," },
11
+ { "code": "c", "value": "(fiktiv gestalt)" }
12
+ ]},
13
+ { "tag": "600", "ind1": "1", "ind2": "4", "subfields": [
14
+ { "code": "a", "value": "What, Never" },
15
+ { "code": "c", "value": "(fiktiv gestalt)" }
16
+ ]},
17
+ { "tag": "600", "ind1": "1", "ind2": "4", "subfields": [
18
+ { "code": "a", "value": "What, Fever" },
19
+ { "code": "c", "value": "(fiktiivinen hahmo)" }
20
+ ]},
21
+ { "tag": "600", "ind1": "3", "ind2": "4", "subfields": [
22
+ { "code": "a", "value": "Whatever" },
23
+ { "code": "c", "value": "(fikt.)" }
24
+ ]}
25
+
26
+ ]
27
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "description": "Apply subfield internal normalization",
3
+ "comment": "Related issues: MRA-273, MRA-614...",
4
+ "fix": true,
5
+ "only": false
6
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "fields": [
3
+ { "tag": "005", "value": "20220202020202.0" },
4
+ { "tag": "040", "ind1": " ", "ind2": " ", "subfields": [
5
+ { "code": "b", "value": "swe" }
6
+ ]},
7
+ { "tag": "600", "ind1": "1", "ind2": "4", "subfields": [
8
+ { "code": "a", "value": "What, Ever," },
9
+ { "code": "c", "value": "fikt." }
10
+ ]},
11
+ { "tag": "600", "ind1": "1", "ind2": "4", "subfields": [
12
+ { "code": "a", "value": "What, Never" },
13
+ { "code": "c", "value": "(fikt.)" }
14
+ ]},
15
+ { "tag": "600", "ind1": "1", "ind2": "4", "subfields": [
16
+ { "code": "a", "value": "What, Fever" },
17
+ { "code": "c", "value": "fiktiivinen hahmo." }
18
+ ]},
19
+ { "tag": "600", "ind1": "3", "ind2": "4", "subfields": [
20
+ { "code": "a", "value": "Whatever" },
21
+ { "code": "c", "value": "(fikt.)" }
22
+ ]}
23
+
24
+ ]
25
+ }
@@ -2,10 +2,10 @@
2
2
  "message": [
3
3
  "'100 1# ‡a Tuisku, Sara ‡e turkulainen ‡e testaaja' => '100 1# ‡a Tuisku, Sara, ‡e turkulainen, ‡e testaaja.'",
4
4
  "'700 1# ‡a Reipas, R. ‡d 2000- ‡e esittäjä' => '700 1# ‡a Reipas, R., ‡d 2000- ‡e esittäjä.'",
5
- "'700 1# ‡a Reippaahko, R. ‡d 2000-' => '700 1# ‡a Reippaahko, R., ‡d 2000-'",
5
+ "'700 1# ‡a Reippaahko, R. ‡d 2000-' => '700 1# ‡a Reippaahko, R., ‡d 2000-.'",
6
6
  "'700 1# ‡a Reippaampi, R. ‡d 2000-2050 ‡e esittäjä' => '700 1# ‡a Reippaampi, R., ‡d 2000-2050, ‡e esittäjä.'",
7
7
  "'700 1# ‡a Reippain, R. ‡d 2000-2050' => '700 1# ‡a Reippain, R., ‡d 2000-2050.'",
8
- "'700 1# ‡a Nalle, P. ‡d 1926- ‡0 (FIN11)000000000' => '700 1# ‡a Nalle, P., ‡d 1926- ‡0 (FIN11)000000000'"
8
+ "'700 1# ‡a Nalle, P. ‡d 1926- ‡0 (FIN11)000000000' => '700 1# ‡a Nalle, P., ‡d 1926-. ‡0 (FIN11)000000000'"
9
9
  ],
10
10
  "valid": false
11
11