@natlibfi/marc-record-validators-melinda 10.2.1 → 10.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,7 +9,7 @@ var _utils = require("./utils");
9
9
  // import clone from 'clone';
10
10
  // const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda:multiple-subfield-0');
11
11
 
12
- const asteriPrefixes = ['(FI-ASTERI-N)', '(FIN11)', 'http://urn.fi/URN:NBN:fi:au:finaf:', 'https://urn.fi/URN:NBN:fi:au:finaf:'];
12
+ const asteriNamePrefixes = ['(FI-ASTERI-N)', '(FIN11)', 'http://urn.fi/URN:NBN:fi:au:finaf:', 'https://urn.fi/URN:NBN:fi:au:finaf:'];
13
13
  function _default() {
14
14
  return {
15
15
  description: 'If Asteri subfield $0 is found, remove non-Asteri $0 subfields',
@@ -17,8 +17,8 @@ function _default() {
17
17
  fix
18
18
  };
19
19
  function fix(record) {
20
- function removeNonAsteriSubfields(field) {
21
- const removableSubfields = getDeletableSubfields(field.subfields);
20
+ function fixField(field) {
21
+ const removableSubfields = fieldGetDeletableSubfields(field);
22
22
  removableSubfields.forEach(sf => record.removeSubfield(sf, field));
23
23
  }
24
24
  const res = {
@@ -27,14 +27,21 @@ function _default() {
27
27
  valid: true
28
28
  };
29
29
  const relevantFields = getRelevantFields(record);
30
- relevantFields.forEach(field => removeNonAsteriSubfields(field));
30
+ relevantFields.forEach(field => fixField(field));
31
31
 
32
32
  // message.valid = !(message.message.length >= 1); // eslint-disable-line functional/immutable-data
33
33
  return res;
34
34
  }
35
35
  function validate(record) {
36
+ function validateField(field) {
37
+ const relevantSubfields = fieldGetDeletableSubfields(field);
38
+ if (relevantSubfields.length === 0) {
39
+ return 'TROUBLE';
40
+ }
41
+ return `Field '${(0, _utils.fieldToString)(field)}' contains deletable $0 subfield(s): ${relevantSubfields.map(sf => sf.value).join(', ')}`;
42
+ }
36
43
  const relevantFields = getRelevantFields(record);
37
- const messages = relevantFields.map(field => `Contains deletable $0 subfield(s): ${(0, _utils.fieldToString)(field)}`);
44
+ const messages = relevantFields.map(field => validateField(field));
38
45
  const res = {
39
46
  message: messages
40
47
  };
@@ -44,34 +51,35 @@ function _default() {
44
51
  function fieldGetSubfields(field, code) {
45
52
  return field.subfields.filter(sf => sf.code === code);
46
53
  }
47
- function isAsteriId(value) {
54
+ function isDeletableNamePartID(value) {
55
+ // List here $0s that always refer to name part, and to never to title part
56
+ if (value.match(/(?:isni|orcid)/ui)) {
57
+ return true;
58
+ }
59
+ return false;
60
+ }
61
+ function isAsteriNameId(value) {
62
+ // This is true if have a valid Asteri entry (nine digits etc)
48
63
  const nineDigitTail = value.slice(-9);
49
64
  if (!/^[0-9]{9}$/u.test(nineDigitTail)) {
50
65
  return false;
51
66
  }
52
67
  // Normalize prefix:
53
68
  const currPrefix = value.slice(0, -9);
54
- if (asteriPrefixes.includes(currPrefix)) {
69
+ if (asteriNamePrefixes.includes(currPrefix)) {
55
70
  return true;
56
71
  }
57
72
  return false;
58
73
  }
59
- function getAsteriSubfields(subfields) {
60
- return subfields.filter(sf => isAsteriId(sf.value));
61
- }
62
- function getDeletableSubfields(subfields) {
63
- return subfields.filter(sf => sf.code === '0' && isDeletableId(sf.value));
64
- function isDeletableId(value) {
65
- if (isAsteriId(value)) {
66
- return false;
67
- }
68
- // Bit lazy here, but it's easy to edit, and this should be good enough for proof-of-concept at least
69
- if (value.match(/(?:isni|orcid)/ui)) {
70
- return true;
71
- }
72
- // Currently default to false, and delete only specified values
73
- return false;
74
+ function neverDropThisID(value) {
75
+ if (isAsteriNameId(value)) {
76
+ return true;
77
+ }
78
+ const prefixes = ['(FIN', '(FI-'];
79
+ if (prefixes.some(prefix => value.startsWith(prefix))) {
80
+ return true;
74
81
  }
82
+ return false;
75
83
  }
76
84
  function fieldHasTitlePart(field) {
77
85
  if (['600', '610', '700', '710', '800', '810'].includes(field.tag)) {
@@ -81,22 +89,29 @@ function _default() {
81
89
  }
82
90
  return false;
83
91
  }
84
- function fieldIsRelevant(field) {
92
+ function fieldGetDeletableSubfields(field) {
85
93
  const subfield0s = fieldGetSubfields(field, '0');
86
94
  if (subfield0s.length < 2) {
87
- return false;
95
+ return []; // We have nothing to delete
88
96
  }
89
- const asteriSubfields = getAsteriSubfields(subfield0s);
90
- if (asteriSubfields.length < 1) {
91
- return false;
97
+
98
+ // Field must contain non-Asteri subfields and Asteri subfiels.
99
+ const nonAsteriNameSubfields = subfield0s.filter(sf => !isAsteriNameId(sf.value));
100
+ if (nonAsteriNameSubfields.length === 0 || nonAsteriNameSubfields.length === subfield0s.length) {
101
+ return [];
92
102
  }
103
+ const suspiciousSubfields = nonAsteriNameSubfields.filter(sf => !neverDropThisID(sf.value));
93
104
 
94
- // $0 might refer to name part or title part. If title part is present, don't remove...
105
+ // Field has deletable name part $0s:
106
+ const otherKnownNamePartIdentifiers = suspiciousSubfields.filter(sf => isDeletableNamePartID(sf.value));
95
107
  if (fieldHasTitlePart(field)) {
96
- return false;
108
+ return otherKnownNamePartIdentifiers;
97
109
  }
98
- const deletableSubfields = getDeletableSubfields(subfield0s);
99
- return deletableSubfields.length > 0;
110
+ return suspiciousSubfields;
111
+ }
112
+ function fieldIsRelevant(field) {
113
+ const subfields = fieldGetDeletableSubfields(field);
114
+ return subfields.length > 0;
100
115
  }
101
116
  function getRelevantFields(record) {
102
117
  return record.fields.filter(field => field.subfields && fieldIsRelevant(field));
@@ -1 +1 @@
1
- {"version":3,"file":"multiple-subfield-0.js","names":["asteriPrefixes","description","validate","fix","record","removeNonAsteriSubfields","field","removableSubfields","getDeletableSubfields","subfields","forEach","sf","removeSubfield","res","message","valid","relevantFields","getRelevantFields","messages","map","fieldToString","length","fieldGetSubfields","code","filter","isAsteriId","value","nineDigitTail","slice","test","currPrefix","includes","getAsteriSubfields","isDeletableId","match","fieldHasTitlePart","tag","fieldHasSubfield","fieldIsRelevant","subfield0s","asteriSubfields","deletableSubfields","fields"],"sources":["../src/multiple-subfield-0.js"],"sourcesContent":["// import createDebugLogger from 'debug';\n// import clone from 'clone';\n// const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda:multiple-subfield-0');\n\nimport {fieldHasSubfield, fieldToString} from './utils';\n\nconst asteriPrefixes = ['(FI-ASTERI-N)', '(FIN11)', 'http://urn.fi/URN:NBN:fi:au:finaf:', 'https://urn.fi/URN:NBN:fi:au:finaf:'];\n\nexport default function () {\n\n return {\n description: 'If Asteri subfield $0 is found, remove non-Asteri $0 subfields',\n validate, fix\n };\n\n function fix(record) {\n function removeNonAsteriSubfields(field) {\n const removableSubfields = getDeletableSubfields(field.subfields);\n removableSubfields.forEach(sf => record.removeSubfield(sf, field));\n }\n\n const res = {message: [], fix: [], valid: true};\n\n const relevantFields = getRelevantFields(record);\n\n relevantFields.forEach(field => removeNonAsteriSubfields(field));\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 relevantFields = getRelevantFields(record);\n const messages = relevantFields.map(field => `Contains deletable $0 subfield(s): ${fieldToString(field)}`);\n const res = {message: messages};\n res.valid = !(res.message.length >= 1); // eslint-disable-line functional/immutable-data\n return res;\n }\n\n function fieldGetSubfields(field, code) {\n return field.subfields.filter(sf => sf.code === code);\n }\n\n function isAsteriId(value) {\n const nineDigitTail = value.slice(-9);\n if (!(/^[0-9]{9}$/u).test(nineDigitTail)) {\n return false;\n }\n // Normalize prefix:\n const currPrefix = value.slice(0, -9);\n\n if (asteriPrefixes.includes(currPrefix)) {\n return true;\n }\n return false;\n }\n\n function getAsteriSubfields(subfields) {\n return subfields.filter(sf => isAsteriId(sf.value));\n }\n\n\n function getDeletableSubfields(subfields) {\n return subfields.filter(sf => sf.code === '0' && isDeletableId(sf.value));\n\n function isDeletableId(value) {\n if (isAsteriId(value)) {\n return false;\n }\n // Bit lazy here, but it's easy to edit, and this should be good enough for proof-of-concept at least\n if (value.match(/(?:isni|orcid)/ui)) {\n return true;\n }\n // Currently default to false, and delete only specified values\n return false;\n }\n }\n\n function fieldHasTitlePart(field) {\n if (['600', '610', '700', '710', '800', '810'].includes(field.tag)) {\n if (fieldHasSubfield(field, 't')) {\n return true;\n }\n }\n return false;\n }\n\n function fieldIsRelevant(field) {\n const subfield0s = fieldGetSubfields(field, '0');\n if (subfield0s.length < 2) {\n return false;\n }\n const asteriSubfields = getAsteriSubfields(subfield0s);\n if (asteriSubfields.length < 1) {\n return false;\n }\n\n // $0 might refer to name part or title part. If title part is present, don't remove...\n if (fieldHasTitlePart(field)) {\n return false;\n }\n\n\n const deletableSubfields = getDeletableSubfields(subfield0s);\n return deletableSubfields.length > 0;\n }\n\n function getRelevantFields(record) {\n return record.fields.filter(field => field.subfields && fieldIsRelevant(field));\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"multiple-subfield-0.js","names":["asteriNamePrefixes","description","validate","fix","record","fixField","field","removableSubfields","fieldGetDeletableSubfields","forEach","sf","removeSubfield","res","message","valid","relevantFields","getRelevantFields","validateField","relevantSubfields","length","fieldToString","map","value","join","messages","fieldGetSubfields","code","subfields","filter","isDeletableNamePartID","match","isAsteriNameId","nineDigitTail","slice","test","currPrefix","includes","neverDropThisID","prefixes","some","prefix","startsWith","fieldHasTitlePart","tag","fieldHasSubfield","subfield0s","nonAsteriNameSubfields","suspiciousSubfields","otherKnownNamePartIdentifiers","fieldIsRelevant","fields"],"sources":["../src/multiple-subfield-0.js"],"sourcesContent":["// import createDebugLogger from 'debug';\n// import clone from 'clone';\n// const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda:multiple-subfield-0');\n\nimport {fieldHasSubfield, fieldToString} from './utils';\n\nconst asteriNamePrefixes = ['(FI-ASTERI-N)', '(FIN11)', 'http://urn.fi/URN:NBN:fi:au:finaf:', 'https://urn.fi/URN:NBN:fi:au:finaf:'];\n\nexport default function () {\n\n return {\n description: 'If Asteri subfield $0 is found, remove non-Asteri $0 subfields',\n validate, fix\n };\n\n function fix(record) {\n function fixField(field) {\n const removableSubfields = fieldGetDeletableSubfields(field);\n removableSubfields.forEach(sf => record.removeSubfield(sf, field));\n }\n\n const res = {message: [], fix: [], valid: true};\n\n const relevantFields = getRelevantFields(record);\n\n relevantFields.forEach(field => fixField(field));\n\n // message.valid = !(message.message.length >= 1); // eslint-disable-line functional/immutable-data\n return res;\n }\n\n function validate(record) {\n function validateField(field) {\n const relevantSubfields = fieldGetDeletableSubfields(field);\n if (relevantSubfields.length === 0) {\n return 'TROUBLE';\n }\n return `Field '${fieldToString(field)}' contains deletable $0 subfield(s): ${relevantSubfields.map(sf => sf.value).join(', ')}`;\n }\n const relevantFields = getRelevantFields(record);\n const messages = relevantFields.map(field => validateField(field));\n const res = {message: messages};\n res.valid = !(res.message.length >= 1); // eslint-disable-line functional/immutable-data\n return res;\n }\n\n function fieldGetSubfields(field, code) {\n return field.subfields.filter(sf => sf.code === code);\n }\n\n function isDeletableNamePartID(value) {\n // List here $0s that always refer to name part, and to never to title part\n if (value.match(/(?:isni|orcid)/ui)) {\n return true;\n }\n return false;\n }\n\n function isAsteriNameId(value) { // This is true if have a valid Asteri entry (nine digits etc)\n const nineDigitTail = value.slice(-9);\n if (!(/^[0-9]{9}$/u).test(nineDigitTail)) {\n return false;\n }\n // Normalize prefix:\n const currPrefix = value.slice(0, -9);\n\n if (asteriNamePrefixes.includes(currPrefix)) {\n return true;\n }\n return false;\n }\n\n function neverDropThisID(value) {\n if (isAsteriNameId(value)) {\n return true;\n }\n\n const prefixes = ['(FIN', '(FI-'];\n if (prefixes.some(prefix => value.startsWith(prefix))) {\n return true;\n }\n\n return false;\n }\n\n\n function fieldHasTitlePart(field) {\n if (['600', '610', '700', '710', '800', '810'].includes(field.tag)) {\n if (fieldHasSubfield(field, 't')) {\n return true;\n }\n }\n return false;\n }\n\n function fieldGetDeletableSubfields(field) {\n const subfield0s = fieldGetSubfields(field, '0');\n\n if (subfield0s.length < 2) {\n return []; // We have nothing to delete\n }\n\n // Field must contain non-Asteri subfields and Asteri subfiels.\n const nonAsteriNameSubfields = subfield0s.filter(sf => !isAsteriNameId(sf.value));\n if (nonAsteriNameSubfields.length === 0 || nonAsteriNameSubfields.length === subfield0s.length) {\n return [];\n }\n\n const suspiciousSubfields = nonAsteriNameSubfields.filter(sf => !neverDropThisID(sf.value));\n\n // Field has deletable name part $0s:\n const otherKnownNamePartIdentifiers = suspiciousSubfields.filter(sf => isDeletableNamePartID(sf.value));\n\n if (fieldHasTitlePart(field)) {\n return otherKnownNamePartIdentifiers;\n }\n\n return suspiciousSubfields;\n }\n\n function fieldIsRelevant(field) {\n const subfields = fieldGetDeletableSubfields(field);\n return subfields.length > 0;\n }\n\n function getRelevantFields(record) {\n return record.fields.filter(field => field.subfields && fieldIsRelevant(field));\n }\n}\n"],"mappings":"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"}
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": "10.2.1",
17
+ "version": "10.2.2",
18
18
  "main": "./dist/index.js",
19
19
  "publishConfig": {
20
20
  "access": "public"
@@ -4,7 +4,7 @@
4
4
 
5
5
  import {fieldHasSubfield, fieldToString} from './utils';
6
6
 
7
- const asteriPrefixes = ['(FI-ASTERI-N)', '(FIN11)', 'http://urn.fi/URN:NBN:fi:au:finaf:', 'https://urn.fi/URN:NBN:fi:au:finaf:'];
7
+ const asteriNamePrefixes = ['(FI-ASTERI-N)', '(FIN11)', 'http://urn.fi/URN:NBN:fi:au:finaf:', 'https://urn.fi/URN:NBN:fi:au:finaf:'];
8
8
 
9
9
  export default function () {
10
10
 
@@ -14,8 +14,8 @@ export default function () {
14
14
  };
15
15
 
16
16
  function fix(record) {
17
- function removeNonAsteriSubfields(field) {
18
- const removableSubfields = getDeletableSubfields(field.subfields);
17
+ function fixField(field) {
18
+ const removableSubfields = fieldGetDeletableSubfields(field);
19
19
  removableSubfields.forEach(sf => record.removeSubfield(sf, field));
20
20
  }
21
21
 
@@ -23,15 +23,22 @@ export default function () {
23
23
 
24
24
  const relevantFields = getRelevantFields(record);
25
25
 
26
- relevantFields.forEach(field => removeNonAsteriSubfields(field));
26
+ relevantFields.forEach(field => fixField(field));
27
27
 
28
28
  // message.valid = !(message.message.length >= 1); // eslint-disable-line functional/immutable-data
29
29
  return res;
30
30
  }
31
31
 
32
32
  function validate(record) {
33
+ function validateField(field) {
34
+ const relevantSubfields = fieldGetDeletableSubfields(field);
35
+ if (relevantSubfields.length === 0) {
36
+ return 'TROUBLE';
37
+ }
38
+ return `Field '${fieldToString(field)}' contains deletable $0 subfield(s): ${relevantSubfields.map(sf => sf.value).join(', ')}`;
39
+ }
33
40
  const relevantFields = getRelevantFields(record);
34
- const messages = relevantFields.map(field => `Contains deletable $0 subfield(s): ${fieldToString(field)}`);
41
+ const messages = relevantFields.map(field => validateField(field));
35
42
  const res = {message: messages};
36
43
  res.valid = !(res.message.length >= 1); // eslint-disable-line functional/immutable-data
37
44
  return res;
@@ -41,7 +48,15 @@ export default function () {
41
48
  return field.subfields.filter(sf => sf.code === code);
42
49
  }
43
50
 
44
- function isAsteriId(value) {
51
+ function isDeletableNamePartID(value) {
52
+ // List here $0s that always refer to name part, and to never to title part
53
+ if (value.match(/(?:isni|orcid)/ui)) {
54
+ return true;
55
+ }
56
+ return false;
57
+ }
58
+
59
+ function isAsteriNameId(value) { // This is true if have a valid Asteri entry (nine digits etc)
45
60
  const nineDigitTail = value.slice(-9);
46
61
  if (!(/^[0-9]{9}$/u).test(nineDigitTail)) {
47
62
  return false;
@@ -49,33 +64,26 @@ export default function () {
49
64
  // Normalize prefix:
50
65
  const currPrefix = value.slice(0, -9);
51
66
 
52
- if (asteriPrefixes.includes(currPrefix)) {
67
+ if (asteriNamePrefixes.includes(currPrefix)) {
53
68
  return true;
54
69
  }
55
70
  return false;
56
71
  }
57
72
 
58
- function getAsteriSubfields(subfields) {
59
- return subfields.filter(sf => isAsteriId(sf.value));
60
- }
61
-
62
-
63
- function getDeletableSubfields(subfields) {
64
- return subfields.filter(sf => sf.code === '0' && isDeletableId(sf.value));
73
+ function neverDropThisID(value) {
74
+ if (isAsteriNameId(value)) {
75
+ return true;
76
+ }
65
77
 
66
- function isDeletableId(value) {
67
- if (isAsteriId(value)) {
68
- return false;
69
- }
70
- // Bit lazy here, but it's easy to edit, and this should be good enough for proof-of-concept at least
71
- if (value.match(/(?:isni|orcid)/ui)) {
72
- return true;
73
- }
74
- // Currently default to false, and delete only specified values
75
- return false;
78
+ const prefixes = ['(FIN', '(FI-'];
79
+ if (prefixes.some(prefix => value.startsWith(prefix))) {
80
+ return true;
76
81
  }
82
+
83
+ return false;
77
84
  }
78
85
 
86
+
79
87
  function fieldHasTitlePart(field) {
80
88
  if (['600', '610', '700', '710', '800', '810'].includes(field.tag)) {
81
89
  if (fieldHasSubfield(field, 't')) {
@@ -85,24 +93,34 @@ export default function () {
85
93
  return false;
86
94
  }
87
95
 
88
- function fieldIsRelevant(field) {
96
+ function fieldGetDeletableSubfields(field) {
89
97
  const subfield0s = fieldGetSubfields(field, '0');
98
+
90
99
  if (subfield0s.length < 2) {
91
- return false;
100
+ return []; // We have nothing to delete
92
101
  }
93
- const asteriSubfields = getAsteriSubfields(subfield0s);
94
- if (asteriSubfields.length < 1) {
95
- return false;
102
+
103
+ // Field must contain non-Asteri subfields and Asteri subfiels.
104
+ const nonAsteriNameSubfields = subfield0s.filter(sf => !isAsteriNameId(sf.value));
105
+ if (nonAsteriNameSubfields.length === 0 || nonAsteriNameSubfields.length === subfield0s.length) {
106
+ return [];
96
107
  }
97
108
 
98
- // $0 might refer to name part or title part. If title part is present, don't remove...
109
+ const suspiciousSubfields = nonAsteriNameSubfields.filter(sf => !neverDropThisID(sf.value));
110
+
111
+ // Field has deletable name part $0s:
112
+ const otherKnownNamePartIdentifiers = suspiciousSubfields.filter(sf => isDeletableNamePartID(sf.value));
113
+
99
114
  if (fieldHasTitlePart(field)) {
100
- return false;
115
+ return otherKnownNamePartIdentifiers;
101
116
  }
102
117
 
118
+ return suspiciousSubfields;
119
+ }
103
120
 
104
- const deletableSubfields = getDeletableSubfields(subfield0s);
105
- return deletableSubfields.length > 0;
121
+ function fieldIsRelevant(field) {
122
+ const subfields = fieldGetDeletableSubfields(field);
123
+ return subfields.length > 0;
106
124
  }
107
125
 
108
126
  function getRelevantFields(record) {
@@ -1,5 +1,5 @@
1
1
  {
2
- "description": "v01: removable $0s found",
2
+ "description": "f01: removable $0s found",
3
3
  "enabled": true,
4
4
  "fix": true,
5
5
  "only": false
@@ -16,8 +16,17 @@
16
16
  ]},
17
17
  { "tag": "700", "ind1": " ", "ind2": " ", "subfields": [
18
18
  { "code": "a", "value": "Nimi3" },
19
- { "code": "0", "value": "(FI-ASTERI-N)000654323" },
20
- { "code": "0", "value": "(keep-for-now)666" }
19
+ { "code": "0", "value": "(FI-ASTERI-N)000654323" }
20
+ ]},
21
+ { "tag": "700", "ind1": " ", "ind2": " ", "subfields": [
22
+ { "code": "a", "value": "Keep erronous FIN13, drop orcid" },
23
+ { "code": "0", "value": "(FI-ASTERI-N)000654324" },
24
+ { "code": "0", "value": "(FIN13)123" }
25
+ ]},
26
+ { "tag": "700", "ind1": " ", "ind2": " ", "subfields": [
27
+ { "code": "a", "value": "No FIN11, do nothing" },
28
+ { "code": "0", "value": "(FIN13)123" },
29
+ { "code": "0", "value": "(orcid)0000-0002-1355-5633" }
21
30
  ]}
22
31
 
23
32
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "description": "v01: No $0s to remove",
2
+ "description": "$0 f02: remove some, keep some",
3
3
  "enabled": true,
4
4
  "fix": true,
5
5
  "only": false
@@ -20,7 +20,18 @@
20
20
  { "code": "a", "value": "Nimi3" },
21
21
  { "code": "0", "value": "(FI-ASTERI-N)000654323" },
22
22
  { "code": "0", "value": "https://isni.org/isni/0000000110485853" },
23
- { "code": "0", "value": "(keep-for-now)666" },
23
+ { "code": "0", "value": "(remove-since-no-$t)666" },
24
+ { "code": "0", "value": "(orcid)0000-0002-1355-5633" }
25
+ ]},
26
+ { "tag": "700", "ind1": " ", "ind2": " ", "subfields": [
27
+ { "code": "a", "value": "Keep erronous FIN13, drop orcid" },
28
+ { "code": "0", "value": "(FI-ASTERI-N)000654324" },
29
+ { "code": "0", "value": "(FIN13)123" },
30
+ { "code": "0", "value": "(orcid)0000-0002-1355-5633" }
31
+ ]},
32
+ { "tag": "700", "ind1": " ", "ind2": " ", "subfields": [
33
+ { "code": "a", "value": "No FIN11, do nothing" },
34
+ { "code": "0", "value": "(FIN13)123" },
24
35
  { "code": "0", "value": "(orcid)0000-0002-1355-5633" }
25
36
  ]}
26
37
 
@@ -8,13 +8,13 @@
8
8
 
9
9
  { "tag": "700", "ind1": " ", "ind2": " ", "subfields": [
10
10
  { "code": "a", "value": "Nimi1" },
11
- { "code": "0", "value": "(FI-ASTERI-N)000654321" }
11
+ { "code": "0", "value": "(FI-ASTERI-N)000654322" }
12
12
  ]},
13
13
  { "tag": "700", "ind1": " ", "ind2": " ", "subfields": [
14
14
  { "code": "a", "value": "Nimi1" },
15
- { "code": "t", "value": "Removal-preventing title" },
15
+ { "code": "t", "value": "Removal-preventing title, however known authority name IDs are still removed" },
16
16
  { "code": "0", "value": "(FI-ASTERI-N)000654321" },
17
- { "code": "0", "value": "(orcid)0000-0002-1355-5633" }
17
+ { "code": "0", "value": "(random)000654321" }
18
18
  ]}
19
19
  ],
20
20
  "leader": ""
@@ -1,5 +1,6 @@
1
1
  {
2
- "description": "v01: No $0s to remove",
2
+ "description": "f03: show difference between titlepartful and titlepartless fields",
3
+ "comment": "still remove orcid, but keep unkwown $0 in title part is present",
3
4
  "enabled": true,
4
5
  "fix": true,
5
6
  "only": false
@@ -8,13 +8,15 @@
8
8
 
9
9
  { "tag": "700", "ind1": " ", "ind2": " ", "subfields": [
10
10
  { "code": "a", "value": "Nimi1" },
11
- { "code": "0", "value": "(FI-ASTERI-N)000654321" },
12
- { "code": "0", "value": "(orcid)0000-0002-1355-5633" }
11
+ { "code": "0", "value": "(FI-ASTERI-N)000654322" },
12
+ { "code": "0", "value": "(orcid)0000-0002-1355-5633" },
13
+ { "code": "0", "value": "(random)000654321" }
13
14
  ]},
14
15
  { "tag": "700", "ind1": " ", "ind2": " ", "subfields": [
15
16
  { "code": "a", "value": "Nimi1" },
16
- { "code": "t", "value": "Removal-preventing title" },
17
+ { "code": "t", "value": "Removal-preventing title, however known authority name IDs are still removed" },
17
18
  { "code": "0", "value": "(FI-ASTERI-N)000654321" },
19
+ { "code": "0", "value": "(random)000654321" },
18
20
  { "code": "0", "value": "(orcid)0000-0002-1355-5633" }
19
21
  ]}
20
22
  ],
@@ -1,5 +1,5 @@
1
1
  {
2
- "description": "v01: removable $0s found",
2
+ "description": "v01: removable $0s not found",
3
3
  "enabled": true,
4
4
  "fix": false,
5
5
  "only": false
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "message": [
3
- "Contains deletable $0 subfield(s): 700 ‡a Nimi1 ‡0 (FI-ASTERI-N)000654321 ‡0 (orcid)0000-0002-1355-5633",
4
- "Contains deletable $0 subfield(s): 700 ‡a Nimi2 ‡0 https://isni.org/isni/0000000110485855 ‡0 (FI-ASTERI-N)000654322",
5
- "Contains deletable $0 subfield(s): 700 ‡a Nimi3 ‡0 (FI-ASTERI-N)000654323 ‡0 https://isni.org/isni/0000000110485853 ‡0 (keep-for-now)666 ‡0 (orcid)0000-0002-1355-5633"
3
+ "Field '700 ‡a Nimi1 ‡0 (FI-ASTERI-N)000654321 ‡0 (orcid)0000-0002-1355-5633' contains deletable $0 subfield(s): (orcid)0000-0002-1355-5633",
4
+ "Field '700 ‡a Nimi2 ‡0 https://isni.org/isni/0000000110485855 ‡0 (FI-ASTERI-N)000654322' contains deletable $0 subfield(s): https://isni.org/isni/0000000110485855",
5
+ "Field '700 ‡a Nimi3 ‡0 (FI-ASTERI-N)000654323 ‡0 https://isni.org/isni/0000000110485853 ‡0 (keep-only-if-title-part-present)666 ‡0 (orcid)0000-0002-1355-5633' contains deletable $0 subfield(s): https://isni.org/isni/0000000110485853, (keep-only-if-title-part-present)666, (orcid)0000-0002-1355-5633",
6
+ "Field '700 ‡a Nimi3 ‡0 (FI-ASTERI-N)000654323 ‡t Titlepart prevents unknown $0 identifiers from being removed ‡0 https://isni.org/isni/0000000110485853 ‡0 (keep-only-if-title-part-present)666 ‡0 (orcid)0000-0002-1355-5633' contains deletable $0 subfield(s): https://isni.org/isni/0000000110485853, (orcid)0000-0002-1355-5633"
6
7
  ],
7
8
  "valid": false
8
9
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "description": "v01: No $0s to remove",
2
+ "description": "v02: multiple $0s to remove",
3
3
  "enabled": true,
4
4
  "fix": false,
5
5
  "only": false
@@ -20,7 +20,15 @@
20
20
  { "code": "a", "value": "Nimi3" },
21
21
  { "code": "0", "value": "(FI-ASTERI-N)000654323" },
22
22
  { "code": "0", "value": "https://isni.org/isni/0000000110485853" },
23
- { "code": "0", "value": "(keep-for-now)666" },
23
+ { "code": "0", "value": "(keep-only-if-title-part-present)666" },
24
+ { "code": "0", "value": "(orcid)0000-0002-1355-5633" }
25
+ ]},
26
+ { "tag": "700", "ind1": " ", "ind2": " ", "subfields": [
27
+ { "code": "a", "value": "Nimi3" },
28
+ { "code": "0", "value": "(FI-ASTERI-N)000654323" },
29
+ { "code": "t", "value": "Titlepart prevents unknown $0 identifiers from being removed"},
30
+ { "code": "0", "value": "https://isni.org/isni/0000000110485853" },
31
+ { "code": "0", "value": "(keep-only-if-title-part-present)666" },
24
32
  { "code": "0", "value": "(orcid)0000-0002-1355-5633" }
25
33
  ]}
26
34