@natlibfi/marc-record-validators-melinda 12.0.2 → 12.0.3-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.
@@ -1,23 +1,39 @@
1
+ import createDebugLogger from "debug";
1
2
  import clone from "clone";
2
3
  import { fieldToString } from "./utils.js";
4
+ const debug = createDebugLogger("@natlibfi/marc-record-validators-melinda:fix-sami-041");
3
5
  export default function() {
4
6
  const samiLanguages = ["sma", "sme", "smj", "smn", "sms"];
5
- const relevantSubfieldCodes = ["a", "d"];
7
+ const subfieldCodesUsingSmi = ["a", "d"];
6
8
  return {
7
- description: "Add corresponing 'smi' subfield before a specific sami language subfields, if needed",
9
+ description: "Add corresponding 'smi' subfield before a specific sami language subfields and update 008/35-37, if needed",
8
10
  validate,
9
11
  fix
10
12
  };
13
+ function getRelevantSubfieldCodes(record) {
14
+ if (record && record.leader && record.leader[6]) {
15
+ debug(` LDR/06 is '${record.leader[6]}'`);
16
+ if (["i", "j"].includes(record.leader[6])) {
17
+ return ["d"];
18
+ }
19
+ return ["a"];
20
+ }
21
+ return ["a", "d"];
22
+ }
11
23
  function fix(record, validateMode = false) {
12
- const relevantFields = record.fields.filter((f) => isRelevantField(f, validateMode)).map((f) => validateMode ? clone(f) : f);
24
+ debug(`Start ${validateMode ? "validator" : "fixer"}`);
25
+ const relevantSubfieldCodes = getRelevantSubfieldCodes(record);
26
+ debug(` Relevant subfield codes are '${relevantSubfieldCodes.join("', '")}'`);
27
+ const relevantFields = record.fields.filter((f) => isRelevantField(f, relevantSubfieldCodes)).map((f) => validateMode ? clone(f) : f);
13
28
  if (relevantFields.length === 0) {
29
+ debug(` No relevant f041 fields found`);
14
30
  if (validateMode) {
15
31
  return { message: [], valid: true };
16
32
  }
17
33
  return { message: [], fix: [], valid: true };
18
34
  }
19
35
  const relevantFieldsAsStrings = relevantFields.map((f) => fieldToString(f));
20
- relevantFields.forEach((f) => processField(f));
36
+ relevantFields.forEach((f) => processField(f, relevantSubfieldCodes));
21
37
  const modFieldsAsStrings = relevantFields.map((f) => fieldToString(f));
22
38
  const report = [...updateAndReport008(), ...createReport(relevantFieldsAsStrings, modFieldsAsStrings)];
23
39
  if (validateMode) {
@@ -27,19 +43,57 @@ export default function() {
27
43
  function updateAndReport008() {
28
44
  const [f008] = record.get("008").map((f) => validateMode ? clone(f) : f);
29
45
  if (!f008) {
46
+ debug(" WARNING: no f008 found");
30
47
  return [];
31
48
  }
32
49
  const currLang = f008.value.substr(35, 3);
33
50
  if (!samiLanguages.includes(currLang)) {
51
+ debug(` Existing 008/35-37 '${currLang}' is not a sami language. No need to update 008/35-37`);
34
52
  return [];
35
53
  }
36
54
  const origValue = f008.value;
37
- const firstRelevantSubfield = relevantFields[0].subfields.find((sf) => sf.code === "a" || sf.code === "d");
38
- if (firstRelevantSubfield.value === "smi") {
39
- f008.value = `${f008.value.substr(0, 35)}smi${f008.value.substr(38)}`;
55
+ const firstRelevantSubfield = relevantFields[0].subfields.find((sf) => relevantSubfieldCodes.includes(sf.code));
56
+ if (firstRelevantSubfield.value !== "smi") {
57
+ debug(` First relevant subfield is '$${firstRelevantSubfield.code} ${firstRelevantSubfield.value}'. No need to update 008/35-37`);
58
+ return [];
40
59
  }
60
+ f008.value = `${f008.value.substr(0, 35)}smi${f008.value.substr(38)}`;
61
+ debug(` Update 008/35-37: '${currLang}' => 'smi'`);
41
62
  return createReport([origValue], [f008.value]);
42
63
  }
64
+ function processField(f) {
65
+ f.subfields = processSubfields(f.subfields, []);
66
+ }
67
+ function processSubfields(incomingSubfields, outgoingSubfields = []) {
68
+ const [currSubfield, ...otherSubfields] = incomingSubfields;
69
+ if (!currSubfield) {
70
+ return outgoingSubfields;
71
+ }
72
+ if (!isRelevantSamiSubfield(currSubfield, [...outgoingSubfields, ...otherSubfields])) {
73
+ return processSubfields(otherSubfields, [...outgoingSubfields, currSubfield]);
74
+ }
75
+ const smiSubfield = {
76
+ code: currSubfield.code,
77
+ value: "smi"
78
+ };
79
+ debug(` f041: Add '$${currSubfield.code} smi' before '${currSubfield.value}'`);
80
+ return processSubfields(otherSubfields, [...outgoingSubfields, smiSubfield, currSubfield]);
81
+ }
82
+ function isRelevantField(f) {
83
+ if (f.tag !== "041" || !f.subfields || f.subfields.some((sf) => sf.code === "2")) {
84
+ return false;
85
+ }
86
+ return f.subfields.some((sf) => isRelevantSamiSubfield(sf, f.subfields));
87
+ }
88
+ function isRelevantSamiSubfield(sf, otherSubfields) {
89
+ if (!subfieldCodesUsingSmi.includes(sf.code) || !samiLanguages.includes(sf.value)) {
90
+ return false;
91
+ }
92
+ if (otherSubfields.some((sf2) => sf2.code === sf.code && sf2.value === "smi")) {
93
+ return false;
94
+ }
95
+ return true;
96
+ }
43
97
  }
44
98
  function createReport(origArray, modArray) {
45
99
  return origArray.map((entry, index) => createEntry(entry, index));
@@ -50,38 +104,5 @@ export default function() {
50
104
  function validate(record) {
51
105
  return fix(record, true);
52
106
  }
53
- function processField(f) {
54
- f.subfields = prosessSubfields(f.subfields);
55
- return f;
56
- }
57
- function prosessSubfields(incomingSubfields, outgoingSubfields = []) {
58
- const [currSubfield, ...otherSubfields] = incomingSubfields;
59
- if (!currSubfield) {
60
- return outgoingSubfields;
61
- }
62
- if (!isRelevantSamiSubfield(currSubfield, [...outgoingSubfields, ...otherSubfields])) {
63
- return prosessSubfields(otherSubfields, [...outgoingSubfields, currSubfield]);
64
- }
65
- const smiSubfield = {
66
- code: currSubfield.code,
67
- value: "smi"
68
- };
69
- return prosessSubfields(otherSubfields, [...outgoingSubfields, smiSubfield, currSubfield]);
70
- }
71
- function isRelevantField(f) {
72
- if (f.tag !== "041" || !f.subfields || f.subfields.some((sf) => sf.code === "2")) {
73
- return false;
74
- }
75
- return f.subfields.some((sf) => isRelevantSamiSubfield(sf, f.subfields));
76
- }
77
- function isRelevantSamiSubfield(sf, otherSubfields) {
78
- if (!relevantSubfieldCodes.includes(sf.code) || !samiLanguages.includes(sf.value)) {
79
- return false;
80
- }
81
- if (otherSubfields.some((sf2) => sf2.code === sf.code && sf2.value === "smi")) {
82
- return false;
83
- }
84
- return true;
85
- }
86
107
  }
87
108
  //# sourceMappingURL=fix-sami-041.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/fix-sami-041.js"],
4
- "sourcesContent": ["// Author(s): Nicholas Volk\n\n//import createDebugLogger from 'debug';\nimport clone from 'clone';\n\nimport {fieldToString} from './utils.js';\n\n// const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda:fix-sami-041');\n\n\n\nexport default function () {\n /* 'sma': etel\u00E4saame, 'sme': pohjoissaame, 'smj': luulajansaame, 'smn': inarinsaame, 'sms': koltansaame */\n const samiLanguages = ['sma', 'sme', 'smj', 'smn', 'sms'];\n const relevantSubfieldCodes = ['a', 'd']; // Subfield codes that should also have 'smi' if a sami langauge is used. Confirmed by A.R. via Slack 2025-12-05\n\n return {\n description: 'Add corresponing \\'smi\\' subfield before a specific sami language subfields, if needed',\n validate, fix\n };\n\n function fix(record, validateMode = false) {\n const relevantFields = record.fields.filter(f => isRelevantField(f, validateMode)).map(f => validateMode ? clone(f) : f); // NV! relevant fields are cloned in validation mode!\n // Nothing to do:\n if (relevantFields.length === 0) {\n if (validateMode) {\n return {message: [], valid: true};\n }\n return {message: [], fix: [], valid: true};\n }\n\n const relevantFieldsAsStrings = relevantFields.map(f => fieldToString(f)); // get original values\n\n relevantFields.forEach(f => processField(f));\n const modFieldsAsStrings = relevantFields.map(f => fieldToString(f));\n const report = [...updateAndReport008(), ...createReport(relevantFieldsAsStrings, modFieldsAsStrings)];\n\n if (validateMode) {\n return {'message': report, 'valid': false};\n }\n\n return {message: [], fix: report, valid: true};\n\n function updateAndReport008() {\n const [f008] = record.get('008').map(f => validateMode ? clone(f) : f);\n\n if (!f008) {\n return [];\n }\n const currLang = f008.value.substr(35, 3);\n if (!samiLanguages.includes(currLang)) { // NB! If original 008/35-37 was not a sami language, we don't change anything!\n return [];\n }\n const origValue = f008.value;\n const firstRelevantSubfield = relevantFields[0].subfields.find(sf => sf.code === 'a' || sf.code === 'd'); // NB! don't use relevantSubfieldCodes here!\n if (firstRelevantSubfield.value === 'smi') { // First relevant subfield is \n f008.value = `${f008.value.substr(0, 35)}smi${f008.value.substr(38)}`;\n }\n return createReport([origValue], [f008.value]);\n }\n }\n\n function createReport(origArray, modArray) {\n return origArray.map((entry, index) => createEntry(entry, index));\n\n function createEntry(item, index) {\n return `'${item}' => '${modArray[index]}'`;\n }\n }\n\n function validate(record) {\n return fix(record, true);\n }\n\n function processField(f) {\n f.subfields = prosessSubfields(f.subfields);\n return f;\n }\n\n function prosessSubfields(incomingSubfields, outgoingSubfields = []) {\n const [currSubfield, ...otherSubfields] = incomingSubfields;\n if (!currSubfield) {\n return outgoingSubfields;\n }\n if (!isRelevantSamiSubfield(currSubfield, [...outgoingSubfields, ...otherSubfields])) {\n return prosessSubfields(otherSubfields, [...outgoingSubfields, currSubfield]);\n }\n\n const smiSubfield = {\n code: currSubfield.code,\n value: 'smi'\n };\n return prosessSubfields(otherSubfields, [...outgoingSubfields, smiSubfield, currSubfield]);\n }\n\n function isRelevantField(f) {\n if (f.tag !== '041' || !f.subfields || f.subfields.some(sf => sf.code === '2')) {\n return false;\n }\n return f.subfields.some(sf => isRelevantSamiSubfield(sf, f.subfields)); // it's ok to pass sf in f.subfields also\n }\n\n function isRelevantSamiSubfield(sf, otherSubfields) {\n if (!relevantSubfieldCodes.includes(sf.code) || !samiLanguages.includes(sf.value)) {\n return false;\n }\n if (otherSubfields.some(sf2 => sf2.code === sf.code && sf2.value === 'smi')) {\n return false;\n }\n return true;\n }\n}\n\n"],
5
- "mappings": "AAGA,OAAO,WAAW;AAElB,SAAQ,qBAAoB;AAM5B,0BAA2B;AAEzB,QAAM,gBAAgB,CAAC,OAAO,OAAO,OAAO,OAAO,KAAK;AACxD,QAAM,wBAAwB,CAAC,KAAK,GAAG;AAEvC,SAAO;AAAA,IACL,aAAa;AAAA,IACb;AAAA,IAAU;AAAA,EACZ;AAEA,WAAS,IAAI,QAAQ,eAAe,OAAO;AACzC,UAAM,iBAAiB,OAAO,OAAO,OAAO,OAAK,gBAAgB,GAAG,YAAY,CAAC,EAAE,IAAI,OAAK,eAAe,MAAM,CAAC,IAAI,CAAC;AAEvH,QAAI,eAAe,WAAW,GAAG;AAC/B,UAAI,cAAc;AAChB,eAAO,EAAC,SAAS,CAAC,GAAG,OAAO,KAAI;AAAA,MAClC;AACA,aAAO,EAAC,SAAS,CAAC,GAAG,KAAK,CAAC,GAAG,OAAO,KAAI;AAAA,IAC3C;AAEA,UAAM,0BAA0B,eAAe,IAAI,OAAK,cAAc,CAAC,CAAC;AAExE,mBAAe,QAAQ,OAAK,aAAa,CAAC,CAAC;AAC3C,UAAM,qBAAqB,eAAe,IAAI,OAAK,cAAc,CAAC,CAAC;AACnE,UAAM,SAAS,CAAC,GAAG,mBAAmB,GAAG,GAAG,aAAa,yBAAyB,kBAAkB,CAAC;AAErG,QAAI,cAAc;AAChB,aAAO,EAAC,WAAW,QAAQ,SAAS,MAAK;AAAA,IAC3C;AAEA,WAAO,EAAC,SAAS,CAAC,GAAG,KAAK,QAAQ,OAAO,KAAI;AAE7C,aAAS,qBAAqB;AAC5B,YAAM,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,EAAE,IAAI,OAAK,eAAe,MAAM,CAAC,IAAI,CAAC;AAErE,UAAI,CAAC,MAAM;AACT,eAAO,CAAC;AAAA,MACV;AACA,YAAM,WAAW,KAAK,MAAM,OAAO,IAAI,CAAC;AACxC,UAAI,CAAC,cAAc,SAAS,QAAQ,GAAG;AACrC,eAAO,CAAC;AAAA,MACV;AACA,YAAM,YAAY,KAAK;AACvB,YAAM,wBAAwB,eAAe,CAAC,EAAE,UAAU,KAAK,QAAM,GAAG,SAAS,OAAO,GAAG,SAAS,GAAG;AACvG,UAAI,sBAAsB,UAAU,OAAO;AACzC,aAAK,QAAQ,GAAG,KAAK,MAAM,OAAO,GAAG,EAAE,CAAC,MAAM,KAAK,MAAM,OAAO,EAAE,CAAC;AAAA,MACrE;AACA,aAAO,aAAa,CAAC,SAAS,GAAG,CAAC,KAAK,KAAK,CAAC;AAAA,IAC/C;AAAA,EACF;AAEA,WAAS,aAAa,WAAW,UAAU;AACzC,WAAO,UAAU,IAAI,CAAC,OAAO,UAAU,YAAY,OAAO,KAAK,CAAC;AAEhE,aAAS,YAAY,MAAM,OAAO;AAChC,aAAO,IAAI,IAAI,SAAS,SAAS,KAAK,CAAC;AAAA,IACzC;AAAA,EACF;AAEA,WAAS,SAAS,QAAQ;AACxB,WAAO,IAAI,QAAQ,IAAI;AAAA,EACzB;AAEA,WAAS,aAAa,GAAG;AACvB,MAAE,YAAY,iBAAiB,EAAE,SAAS;AAC1C,WAAO;AAAA,EACT;AAEA,WAAS,iBAAiB,mBAAmB,oBAAoB,CAAC,GAAG;AACnE,UAAM,CAAC,cAAc,GAAG,cAAc,IAAI;AAC1C,QAAI,CAAC,cAAc;AACjB,aAAO;AAAA,IACT;AACA,QAAI,CAAC,uBAAuB,cAAc,CAAC,GAAG,mBAAmB,GAAG,cAAc,CAAC,GAAG;AACpF,aAAO,iBAAiB,gBAAgB,CAAC,GAAG,mBAAmB,YAAY,CAAC;AAAA,IAC9E;AAEA,UAAM,cAAc;AAAA,MAClB,MAAM,aAAa;AAAA,MACnB,OAAO;AAAA,IACT;AACA,WAAO,iBAAiB,gBAAgB,CAAC,GAAG,mBAAmB,aAAa,YAAY,CAAC;AAAA,EAC3F;AAEA,WAAS,gBAAgB,GAAG;AAC1B,QAAI,EAAE,QAAQ,SAAS,CAAC,EAAE,aAAa,EAAE,UAAU,KAAK,QAAM,GAAG,SAAS,GAAG,GAAG;AAC9E,aAAO;AAAA,IACT;AACA,WAAO,EAAE,UAAU,KAAK,QAAM,uBAAuB,IAAI,EAAE,SAAS,CAAC;AAAA,EACvE;AAEA,WAAS,uBAAuB,IAAI,gBAAgB;AAClD,QAAI,CAAC,sBAAsB,SAAS,GAAG,IAAI,KAAK,CAAC,cAAc,SAAS,GAAG,KAAK,GAAG;AACjF,aAAO;AAAA,IACT;AACA,QAAI,eAAe,KAAK,SAAO,IAAI,SAAS,GAAG,QAAQ,IAAI,UAAU,KAAK,GAAG;AAC3E,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AACF;",
4
+ "sourcesContent": ["// Author(s): Nicholas Volk\n\nimport createDebugLogger from 'debug';\nimport clone from 'clone';\n\nimport {fieldToString} from './utils.js';\n\nconst debug = createDebugLogger('@natlibfi/marc-record-validators-melinda:fix-sami-041');\n\n\n\nexport default function () {\n /* 'sma': etel\u00E4saame, 'sme': pohjoissaame, 'smj': luulajansaame, 'smn': inarinsaame, 'sms': koltansaame */\n const samiLanguages = ['sma', 'sme', 'smj', 'smn', 'sms'];\n const subfieldCodesUsingSmi = ['a', 'd'];\n\n return {\n description: 'Add corresponding \\'smi\\' subfield before a specific sami language subfields and update 008/35-37, if needed',\n validate, fix\n };\n\n function getRelevantSubfieldCodes(record) { // Maybe this should be an exportable utility function...\n if (record && record.leader && record.leader[6]) {\n debug(` LDR/06 is '${record.leader[6]}'`);\n // We should test this properly...\n if (['i', 'j'].includes(record.leader[6])) { // Check type of record: sound recordings use 'd'\n return ['d'];\n }\n return ['a'];\n }\n\n return ['a', 'd']; // B\u00E5de-och\n }\n\n\n function fix(record, validateMode = false) {\n debug(`Start ${validateMode ? 'validator' : 'fixer'}`);\n const relevantSubfieldCodes = getRelevantSubfieldCodes(record);\n debug(` Relevant subfield codes are '${relevantSubfieldCodes.join(\"', '\")}'`);\n const relevantFields = record.fields.filter(f => isRelevantField(f, relevantSubfieldCodes)).map(f => validateMode ? clone(f) : f); // NV! relevant fields are cloned in validation mode!\n // Nothing to do:\n if (relevantFields.length === 0) {\n debug(` No relevant f041 fields found`);\n if (validateMode) {\n return {message: [], valid: true};\n }\n return {message: [], fix: [], valid: true};\n }\n\n const relevantFieldsAsStrings = relevantFields.map(f => fieldToString(f)); // get original values\n\n relevantFields.forEach(f => processField(f, relevantSubfieldCodes));\n const modFieldsAsStrings = relevantFields.map(f => fieldToString(f));\n const report = [...updateAndReport008(), ...createReport(relevantFieldsAsStrings, modFieldsAsStrings)];\n\n if (validateMode) {\n return {'message': report, 'valid': false};\n }\n\n return {message: [], fix: report, valid: true};\n\n function updateAndReport008() { // Update 008/35-37 if necessary + report it\n const [f008] = record.get('008').map(f => validateMode ? clone(f) : f);\n\n if (!f008) {\n debug(' WARNING: no f008 found');\n return [];\n }\n const currLang = f008.value.substr(35, 3);\n if (!samiLanguages.includes(currLang)) { // NB! If original 008/35-37 was not a sami language, we don't change anything!\n debug(` Existing 008/35-37 '${currLang}' is not a sami language. No need to update 008/35-37`);\n return [];\n }\n const origValue = f008.value;\n const firstRelevantSubfield = relevantFields[0].subfields.find(sf => relevantSubfieldCodes.includes(sf.code));\n if (firstRelevantSubfield.value !== 'smi') {\n debug(` First relevant subfield is '\\$${firstRelevantSubfield.code} ${firstRelevantSubfield.value}'. No need to update 008/35-37`);\n return [];\n }\n f008.value = `${f008.value.substr(0, 35)}smi${f008.value.substr(38)}`;\n debug(` Update 008/35-37: '${currLang}' => 'smi'`);\n return createReport([origValue], [f008.value]);\n }\n\n function processField(f) {\n f.subfields = processSubfields(f.subfields, []);\n }\n\n function processSubfields(incomingSubfields, outgoingSubfields = []) {\n const [currSubfield, ...otherSubfields] = incomingSubfields;\n if (!currSubfield) {\n return outgoingSubfields;\n }\n if (!isRelevantSamiSubfield(currSubfield, [...outgoingSubfields, ...otherSubfields])) {\n return processSubfields(otherSubfields, [...outgoingSubfields, currSubfield]);\n }\n\n const smiSubfield = {\n code: currSubfield.code,\n value: 'smi'\n };\n debug(` f041: Add '\\$${currSubfield.code} smi' before '${currSubfield.value}'`);\n\n return processSubfields(otherSubfields, [...outgoingSubfields, smiSubfield, currSubfield]);\n }\n\n function isRelevantField(f) {\n if (f.tag !== '041' || !f.subfields || f.subfields.some(sf => sf.code === '2')) {\n return false;\n }\n return f.subfields.some(sf => isRelevantSamiSubfield(sf, f.subfields)); // it's ok to pass sf in f.subfields also\n }\n\n function isRelevantSamiSubfield(sf, otherSubfields) {\n // NB! preceding 'smi' is added to all $a and $d fields, regardless of the LDR/06 value! (However, copying 041$a/d -> 008/35-37 depends on LDR/06)\n if (!subfieldCodesUsingSmi.includes(sf.code) || !samiLanguages.includes(sf.value)) {\n return false;\n }\n if (otherSubfields.some(sf2 => sf2.code === sf.code && sf2.value === 'smi')) { // fail if 'smi' already exists\n return false;\n }\n //debug(` '\\${sf.code} ${sf.value}' requires preceding 'smi'`);\n return true;\n }\n }\n\n function createReport(origArray, modArray) {\n return origArray.map((entry, index) => createEntry(entry, index));\n\n function createEntry(item, index) {\n return `'${item}' => '${modArray[index]}'`;\n }\n }\n\n function validate(record) {\n return fix(record, true);\n }\n\n\n\n\n\n\n}\n\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -14,7 +14,7 @@
14
14
  "url": "https://github.com/NatLibFi/marc-record-validators-melinda"
15
15
  },
16
16
  "license": "MIT",
17
- "version": "12.0.2",
17
+ "version": "12.0.3-alpha.1",
18
18
  "main": "./dist/index.js",
19
19
  "publishConfig": {
20
20
  "access": "public"
@@ -59,8 +59,8 @@
59
59
  "@natlibfi/fixugen": "^3.0.0",
60
60
  "@natlibfi/fixura": "^4.0.0",
61
61
  "cross-env": "^10.1.0",
62
- "esbuild": "^0.27.1",
63
- "eslint": "^9.39.1",
62
+ "esbuild": "^0.27.2",
63
+ "eslint": "^9.39.2",
64
64
  "fetch-mock": "^12.6.0"
65
65
  },
66
66
  "overrides": {
@@ -1,28 +1,46 @@
1
1
  // Author(s): Nicholas Volk
2
2
 
3
- //import createDebugLogger from 'debug';
3
+ import createDebugLogger from 'debug';
4
4
  import clone from 'clone';
5
5
 
6
6
  import {fieldToString} from './utils.js';
7
7
 
8
- // const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda:fix-sami-041');
8
+ const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda:fix-sami-041');
9
9
 
10
10
 
11
11
 
12
12
  export default function () {
13
13
  /* 'sma': eteläsaame, 'sme': pohjoissaame, 'smj': luulajansaame, 'smn': inarinsaame, 'sms': koltansaame */
14
14
  const samiLanguages = ['sma', 'sme', 'smj', 'smn', 'sms'];
15
- const relevantSubfieldCodes = ['a', 'd']; // Subfield codes that should also have 'smi' if a sami langauge is used. Confirmed by A.R. via Slack 2025-12-05
15
+ const subfieldCodesUsingSmi = ['a', 'd'];
16
16
 
17
17
  return {
18
- description: 'Add corresponing \'smi\' subfield before a specific sami language subfields, if needed',
18
+ description: 'Add corresponding \'smi\' subfield before a specific sami language subfields and update 008/35-37, if needed',
19
19
  validate, fix
20
20
  };
21
21
 
22
+ function getRelevantSubfieldCodes(record) { // Maybe this should be an exportable utility function...
23
+ if (record && record.leader && record.leader[6]) {
24
+ debug(` LDR/06 is '${record.leader[6]}'`);
25
+ // We should test this properly...
26
+ if (['i', 'j'].includes(record.leader[6])) { // Check type of record: sound recordings use 'd'
27
+ return ['d'];
28
+ }
29
+ return ['a'];
30
+ }
31
+
32
+ return ['a', 'd']; // Både-och
33
+ }
34
+
35
+
22
36
  function fix(record, validateMode = false) {
23
- const relevantFields = record.fields.filter(f => isRelevantField(f, validateMode)).map(f => validateMode ? clone(f) : f); // NV! relevant fields are cloned in validation mode!
37
+ debug(`Start ${validateMode ? 'validator' : 'fixer'}`);
38
+ const relevantSubfieldCodes = getRelevantSubfieldCodes(record);
39
+ debug(` Relevant subfield codes are '${relevantSubfieldCodes.join("', '")}'`);
40
+ const relevantFields = record.fields.filter(f => isRelevantField(f, relevantSubfieldCodes)).map(f => validateMode ? clone(f) : f); // NV! relevant fields are cloned in validation mode!
24
41
  // Nothing to do:
25
42
  if (relevantFields.length === 0) {
43
+ debug(` No relevant f041 fields found`);
26
44
  if (validateMode) {
27
45
  return {message: [], valid: true};
28
46
  }
@@ -31,7 +49,7 @@ export default function () {
31
49
 
32
50
  const relevantFieldsAsStrings = relevantFields.map(f => fieldToString(f)); // get original values
33
51
 
34
- relevantFields.forEach(f => processField(f));
52
+ relevantFields.forEach(f => processField(f, relevantSubfieldCodes));
35
53
  const modFieldsAsStrings = relevantFields.map(f => fieldToString(f));
36
54
  const report = [...updateAndReport008(), ...createReport(relevantFieldsAsStrings, modFieldsAsStrings)];
37
55
 
@@ -41,23 +59,69 @@ export default function () {
41
59
 
42
60
  return {message: [], fix: report, valid: true};
43
61
 
44
- function updateAndReport008() {
62
+ function updateAndReport008() { // Update 008/35-37 if necessary + report it
45
63
  const [f008] = record.get('008').map(f => validateMode ? clone(f) : f);
46
64
 
47
65
  if (!f008) {
66
+ debug(' WARNING: no f008 found');
48
67
  return [];
49
68
  }
50
69
  const currLang = f008.value.substr(35, 3);
51
70
  if (!samiLanguages.includes(currLang)) { // NB! If original 008/35-37 was not a sami language, we don't change anything!
71
+ debug(` Existing 008/35-37 '${currLang}' is not a sami language. No need to update 008/35-37`);
52
72
  return [];
53
73
  }
54
74
  const origValue = f008.value;
55
- const firstRelevantSubfield = relevantFields[0].subfields.find(sf => sf.code === 'a' || sf.code === 'd'); // NB! don't use relevantSubfieldCodes here!
56
- if (firstRelevantSubfield.value === 'smi') { // First relevant subfield is
57
- f008.value = `${f008.value.substr(0, 35)}smi${f008.value.substr(38)}`;
75
+ const firstRelevantSubfield = relevantFields[0].subfields.find(sf => relevantSubfieldCodes.includes(sf.code));
76
+ if (firstRelevantSubfield.value !== 'smi') {
77
+ debug(` First relevant subfield is '\$${firstRelevantSubfield.code} ${firstRelevantSubfield.value}'. No need to update 008/35-37`);
78
+ return [];
58
79
  }
80
+ f008.value = `${f008.value.substr(0, 35)}smi${f008.value.substr(38)}`;
81
+ debug(` Update 008/35-37: '${currLang}' => 'smi'`);
59
82
  return createReport([origValue], [f008.value]);
60
83
  }
84
+
85
+ function processField(f) {
86
+ f.subfields = processSubfields(f.subfields, []);
87
+ }
88
+
89
+ function processSubfields(incomingSubfields, outgoingSubfields = []) {
90
+ const [currSubfield, ...otherSubfields] = incomingSubfields;
91
+ if (!currSubfield) {
92
+ return outgoingSubfields;
93
+ }
94
+ if (!isRelevantSamiSubfield(currSubfield, [...outgoingSubfields, ...otherSubfields])) {
95
+ return processSubfields(otherSubfields, [...outgoingSubfields, currSubfield]);
96
+ }
97
+
98
+ const smiSubfield = {
99
+ code: currSubfield.code,
100
+ value: 'smi'
101
+ };
102
+ debug(` f041: Add '\$${currSubfield.code} smi' before '${currSubfield.value}'`);
103
+
104
+ return processSubfields(otherSubfields, [...outgoingSubfields, smiSubfield, currSubfield]);
105
+ }
106
+
107
+ function isRelevantField(f) {
108
+ if (f.tag !== '041' || !f.subfields || f.subfields.some(sf => sf.code === '2')) {
109
+ return false;
110
+ }
111
+ return f.subfields.some(sf => isRelevantSamiSubfield(sf, f.subfields)); // it's ok to pass sf in f.subfields also
112
+ }
113
+
114
+ function isRelevantSamiSubfield(sf, otherSubfields) {
115
+ // NB! preceding 'smi' is added to all $a and $d fields, regardless of the LDR/06 value! (However, copying 041$a/d -> 008/35-37 depends on LDR/06)
116
+ if (!subfieldCodesUsingSmi.includes(sf.code) || !samiLanguages.includes(sf.value)) {
117
+ return false;
118
+ }
119
+ if (otherSubfields.some(sf2 => sf2.code === sf.code && sf2.value === 'smi')) { // fail if 'smi' already exists
120
+ return false;
121
+ }
122
+ //debug(` '\${sf.code} ${sf.value}' requires preceding 'smi'`);
123
+ return true;
124
+ }
61
125
  }
62
126
 
63
127
  function createReport(origArray, modArray) {
@@ -72,42 +136,10 @@ export default function () {
72
136
  return fix(record, true);
73
137
  }
74
138
 
75
- function processField(f) {
76
- f.subfields = prosessSubfields(f.subfields);
77
- return f;
78
- }
79
139
 
80
- function prosessSubfields(incomingSubfields, outgoingSubfields = []) {
81
- const [currSubfield, ...otherSubfields] = incomingSubfields;
82
- if (!currSubfield) {
83
- return outgoingSubfields;
84
- }
85
- if (!isRelevantSamiSubfield(currSubfield, [...outgoingSubfields, ...otherSubfields])) {
86
- return prosessSubfields(otherSubfields, [...outgoingSubfields, currSubfield]);
87
- }
88
140
 
89
- const smiSubfield = {
90
- code: currSubfield.code,
91
- value: 'smi'
92
- };
93
- return prosessSubfields(otherSubfields, [...outgoingSubfields, smiSubfield, currSubfield]);
94
- }
95
141
 
96
- function isRelevantField(f) {
97
- if (f.tag !== '041' || !f.subfields || f.subfields.some(sf => sf.code === '2')) {
98
- return false;
99
- }
100
- return f.subfields.some(sf => isRelevantSamiSubfield(sf, f.subfields)); // it's ok to pass sf in f.subfields also
101
- }
102
142
 
103
- function isRelevantSamiSubfield(sf, otherSubfields) {
104
- if (!relevantSubfieldCodes.includes(sf.code) || !samiLanguages.includes(sf.value)) {
105
- return false;
106
- }
107
- if (otherSubfields.some(sf2 => sf2.code === sf.code && sf2.value === 'smi')) {
108
- return false;
109
- }
110
- return true;
111
- }
143
+
112
144
  }
113
145
 
@@ -1,4 +1,5 @@
1
1
  {
2
+ "leader": "012345a78901234567894500",
2
3
  "fields": [
3
4
  {
4
5
  "tag": "005",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "_validationOptions": {},
3
- "leader": "",
3
+ "leader": "012345j78901234567894500",
4
4
  "fields": [
5
5
  { "tag": "041", "ind1": " ", "ind2": " ", "subfields": [ { "code": "a", "value": "eng" } ]},
6
6
  { "tag": "041", "ind1": "1", "ind2": "7", "subfields": [ { "code": "a", "value": "smi" }, {"code": "2", "value": "iso-639-3"} ]}
@@ -0,0 +1,10 @@
1
+ {
2
+ "_validationOptions": {},
3
+ "leader": "01234ci789012345678 2500",
4
+ "fields": [
5
+ { "tag": "008", "value": "01234567890123456789012345678901234|||89" },
6
+ { "tag": "041", "ind1": " ", "ind2": " ",
7
+ "subfields": [ { "code": "a", "value": "smi" }, { "code": "a", "value": "sme" } ]
8
+ }
9
+ ]
10
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "description": "06: Add $a 'smi' to f041, but don't touch 008/35-37 as subfield code is $a as LDR/06 is 'i'",
3
+ "fix": true
4
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "leader": "01234ci789012345678 2500",
3
+ "fields": [
4
+ { "tag": "008", "value": "01234567890123456789012345678901234|||89" },
5
+ { "tag": "041", "ind1": " ", "ind2": " ",
6
+ "subfields": [ { "code": "a", "value": "smi" }, { "code": "a", "value": "sme" } ]
7
+ }
8
+ ]
9
+ }