src/prepublicationUtils.js
import {fieldHasSubfield, nvdebug, nvdebugFieldArray} from './utils';
import createDebugLogger from 'debug';
const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda:prepublicationUtils');
//const debugData = debug.extend('data');
const debugDev = debug.extend('dev');
const KONEELLISESTI_TUOTETTU_TIETUE = 1; // Best
const TARKISTETTU_ENNAKKOTIETO = 2;
const ENNAKKOTIETO = 3;
//const EI_TASOA = 4;
const encodingLevelPreferenceArray = [' ', '1', '3', '4', '5', '2', '7', 'u', 'z', '8']; // MET-145
const prepublicationLevelIndex = encodingLevelPreferenceArray.indexOf('8');
export function prepublicationLevelIsKoneellisestiTuotettuTietueOrTarkistettuEnnakkotieto(prepublicationLevel) {
return prepublicationLevel === KONEELLISESTI_TUOTETTU_TIETUE || prepublicationLevel === TARKISTETTU_ENNAKKOTIETO;
}
export function encodingLevelIsBetterThanPrepublication(encodingLevel) {
const index = encodingLevelPreferenceArray.indexOf(encodingLevel);
return index > -1 && index < prepublicationLevelIndex;
}
function containsSubstringInSubfieldA(field, substring) {
return field.subfields.some(sf => sf.code === 'a' && sf.value.includes(substring));
}
// These three functions below all refer to field 500:
export function fieldRefersToKoneellisestiTuotettuTietue(field) {
return containsSubstringInSubfieldA(field, 'Koneellisesti tuotettu tietue');
}
export function fieldRefersToTarkistettuEnnakkotieto(field) {
return containsSubstringInSubfieldA(field, 'TARKISTETTU ENNAKKOTIETO');
}
export function fieldRefersToEnnakkotieto(field) {
// NB! This no longer matches 'TARKISTETTU ENNAKKOTIETO' case! Bug or Feature?
if (containsSubstringInSubfieldA(field, 'ENNAKKOTIETO') && !fieldRefersToTarkistettuEnnakkotieto(field)) {
return true;
}
// MRA-420: "EI VIELĂ„ ILMESTYNYT" is a Helmet note, that is semantically similar to ENNAKKOTIETO:
return containsSubstringInSubfieldA(field, 'EI VIELĂ„ ILMESTYNYT');
}
export function firstFieldHasBetterPrepubEncodingLevel(field1, field2) {
if (fieldRefersToKoneellisestiTuotettuTietue(field2)) {
return false;
}
if (fieldRefersToKoneellisestiTuotettuTietue(field1)) {
return true;
}
if (fieldRefersToTarkistettuEnnakkotieto(field2)) {
return false;
}
if (fieldRefersToTarkistettuEnnakkotieto(field1)) {
return true;
}
if (fieldRefersToEnnakkotieto(field2)) {
return false;
}
if (fieldRefersToEnnakkotieto(field1)) {
return true;
}
return false;
}
/*
function hasEnnakkotietoSubfield(field) {
return field.subfields.some(sf => ['g', '9'].includes(sf.code) && sf.value.includes('ENNAKKOTIETO'));
}
*/
/*
export function isPrepublicationField6XX(field) {
if (!field.tag.match(/^6(?:[0-4][0-9]|5[0-5])$/u)) { // Not within 600 ... 655 range
return false;
}
return field.subfields.some(sf => hasEnnakkotietoSubfield(sf));
}
*/
export function getRelevant5XXFields(record, f500 = false, f594 = false) {
const cands = actualGetFields();
//nvdebugFieldArray(cands, 'gR5XXa: ', debugDev);
const filtered = cands.filter(field => hasRelevantPrepubData(field));
//nvdebugFieldArray(filtered, 'gR5XXb: ', debugDev);
return filtered;
//return actualGetFields().filter(field => hasRelevantPrepubData(field));
function hasRelevantPrepubData(field) {
// Check prepub ($a):
if (!fieldRefersToKoneellisestiTuotettuTietue(field) && !fieldRefersToTarkistettuEnnakkotieto(field) && !fieldRefersToEnnakkotieto(field)) {
return false;
}
// Check relevance (594$5):
if (field.tag === '500') {
return field.subfields.every(sf => sf.code !== '5'); //true;
}
return field.subfields.some(sf => sf.code === '5' && ['FENNI', 'FIKKA', 'VIOLA'].includes(sf.value));
}
function actualGetFields() {
if (f500 && f594) {
return record.get(/^(?:500|594)$/u);
}
if (f500) {
return record.get(/^500$/u);
}
if (f594) {
return record.get(/^594$/u);
}
return [];
}
}
// Very similar to getPrepublicationLevel() in melinda-record-match-validator's getPrepublicationLevel()...
// We should use that and not have a copy here...
export function getPrepublicationLevel(record, f500 = false, f594 = false) {
// Smaller return value is better
const fields = getRelevant5XXFields(record, f500, f594);
if (!fields) {
return null;
}
if (fields.some(f => fieldRefersToKoneellisestiTuotettuTietue(f))) {
return KONEELLISESTI_TUOTETTU_TIETUE;
}
if (fields.some(f => fieldRefersToTarkistettuEnnakkotieto(f))) {
return TARKISTETTU_ENNAKKOTIETO;
}
if (fields.some(f => fieldRefersToEnnakkotieto(f))) {
return ENNAKKOTIETO;
}
return null;
}
export function baseHasEqualOrHigherEncodingLevel(baseEncodingLevel, sourceEncodingLevel) {
const baseIndex = encodingLevelPreferenceArray.indexOf(baseEncodingLevel);
const sourceIndex = encodingLevelPreferenceArray.indexOf(sourceEncodingLevel);
if (baseIndex === -1) {
// Base wins if both are bad:
return sourceIndex === -1;
}
return baseIndex <= sourceIndex;
}
function hasFikkaLOW(record) {
return record.fields.some(field => field.tag === 'LOW' && fieldHasSubfield(field, 'a', 'FIKKA'));
}
function hasNatLibFi042(record) {
return record.fields.some(field => field.tag === '042' && (fieldHasSubfield(field, 'a', 'finb') || fieldHasSubfield(field, 'a', 'finbd')));
}
export function isFikkaRecord(record) {
// NB! Does not include Humaniora. Pienpainatteet (not that they'd have duplicates)?
return hasFikkaLOW(record) && hasNatLibFi042(record);
}
export function getEncodingLevel(record) {
return record.leader.substring(17, 18);
}
export function deleteAllPrepublicationNotesFromField500InNonPubRecord(record) {
const encodingLevel = getEncodingLevel(record);
// Skip prepublication (or theoretically even worse) records:
if (!encodingLevelIsBetterThanPrepublication(encodingLevel)) {
//if (['2', '8'].includes(encodingLevel)) { // MET-306: added '2' here
return;
}
// MET-306: keep "koneellisesti tuotettu tietue" if encoding level is '2':
const f500 = getRelevant5XXFields(record, true, false).filter(field => encodingLevel === '2' ? !fieldRefersToKoneellisestiTuotettuTietue(field) : true);
if (f500.length === 0) {
return;
}
nvdebug(`Delete all ${f500.length} instance(s) of field 500`, debugDev);
f500.forEach(field => record.removeField(field));
}
export function removeWorsePrepubField500s(record) {
// Remove lower-level entries:
const fields = getRelevant5XXFields(record, true, false); // 500=false, 594=true
nvdebugFieldArray(fields, ' Candidates for non-best 500 b4 filtering: ', debugDev);
const nonBest = fields.filter(field => fields.some(field2 => firstFieldHasBetterPrepubEncodingLevel(field2, field)));
nvdebugFieldArray(nonBest, ' Remove non-best 500: ', debugDev);
nonBest.forEach(field => record.removeField(field));
}
export function removeWorsePrepubField594s(record) {
// Remove lower-level entries:
const fields594 = getRelevant5XXFields(record, false, true); // 500=false, 594=true
nvdebugFieldArray(fields594, ' Candidates for non-best 594 b4 filtering: ', debugDev);
const nonBest = fields594.filter(field => fields594.some(field2 => firstFieldHasBetterPrepubEncodingLevel(field2, field)));
nvdebugFieldArray(nonBest, ' Remove non-best 594: ', debugDev);
nonBest.forEach(field => record.removeField(field));
}
export function isEnnakkotietoSubfield(subfield) {
if (subfield.code !== '9' && subfield.code !== 'g') {
return false;
}
// Length <= 13 allows punctuation, but does not require it:
if (subfield.value.substr(0, 12) === 'ENNAKKOTIETO' && subfield.value.length <= 13) {
return true;
}
return false;
}
export function isEnnakkotietoField(field) {
return field.subfields.some(sf => isEnnakkotietoSubfield(sf));
}
export function isKingOfTheHill(field, opposingFields) {
// Field is no better than at least one of the opposing fields
return opposingFields.every(opposingField => firstFieldHasBetterPrepubEncodingLevel(field, opposingField));
}