NatLibFi/marc-record-validators-melinda

View on GitHub
src/mergeRelatorTermFields.js

Summary

Maintainability
B
4 hrs
Test Coverage
// Merge author/agent fields
//
// Rationale: Same author can appear in one 1XX and multiple 7XX fields having only different $e subfields.
// These fields can be merged (and $e-subfields can then be sorted)...
//
// Author(s): Nicholas Volk


import clone from 'clone';
import {fieldFixPunctuation, fieldStripPunctuation} from './punctuation2';
import {fieldToString, nvdebug} from './utils';
import {sortAdjacentSubfields} from './sortSubfields';
import {sortAdjacentESubfields} from './sortRelatorTerms';
//import createDebugLogger from 'debug';
/*
//const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda:mergeRelatorTermFields');
//const debugData = debug.extend('data');
*/

export default function () {

  return {
    description: 'Merge author fields that only differ in $e relator terms',
    validate, fix
  };

  function fix(record) {
    const msg = mergeRelatorTermFields(record, true);
    const res = {message: msg, fix: msg, valid: true};
    return res;
  }

  function validate(record) {
    const msg = mergeRelatorTermFields(record, false);
    const res = {message: msg};

    res.valid = !(res.message.length >= 1); // eslint-disable-line functional/immutable-data
    return res;
  }
}

function createNormalizedClone(field) {
  const clonedField = clone(field);
  // Normalize
  fieldStripPunctuation(clonedField);
  return clonedField;
}

function createNormalizedCloneWithoutRelatorTerms(field) {
  const clonedField = createNormalizedClone(field);
  // Remove relator terms $e subfi:
  clonedField.subfields = clonedField.subfields.filter(sf => sf.code !== 'e'); // eslint-disable-line functional/immutable-data
  return clonedField;
}

function fieldToRelatorTermSubfieldCode(field) {
  if (['100', '110', '700', '710', '720', '751', '752'].includes(field.tag)) {
    return 'e';
  }
  if (field.tag === '111' || field.tag === '711') {
    return 'j';
  }
  return '?'; // No need to complain. Nothing is found.
}

function getRelatorTermStrings(relatorTermSubfieldCode, field) {
  return field.subfields.filter(sf => sf.code === relatorTermSubfieldCode).map(sf => sf.value);

}

function extractAddableRelatorTerms(fromField, toField) {
  const relatorTermSubfieldCode = fieldToRelatorTermSubfieldCode(fromField);
  const normalizedFromFieldRelatorTerms = getRelatorTermStrings(relatorTermSubfieldCode, fromField);
  if (normalizedFromFieldRelatorTerms.length === 0) {
    return [];
  }
  // Remove values that already exist:
  const normalizedToFieldRelatorTerms = getRelatorTermStrings(relatorTermSubfieldCode, toField);
  return normalizedFromFieldRelatorTerms.filter(str => !normalizedToFieldRelatorTerms.includes(str));
}


function copyRelatorSubfields(fromField, toField) {
  const relatorTermSubfieldCode = fieldToRelatorTermSubfieldCode(fromField);
  const newRelatorTerms = extractAddableRelatorTerms(fromField, toField);

  newRelatorTerms.forEach(term => toField.subfields.push({code: relatorTermSubfieldCode, value: term})); // eslint-disable-line functional/immutable-data

}

function mergeRelatorTermFields(record, fix = false) {
  /* eslint-disable */
  // NV: 111/711, 751 and 752 where so rare that I did not add them here
  let fields = record.get('(?:[17][01]0|720)'); 
  let result = [];
  const comparisonFieldsAsString = fields.map(f => fieldToString(createNormalizedCloneWithoutRelatorTerms(f)));

  nvdebug(`mergeRelatorTermFields(): ${fields.length} cand field(s) found`);
  for(let i=0; i < fields.length-1; i++) {
    let currField = fields[i];
    if (currField.deleted) {
      continue;
    }
    nvdebug(`RT: Trying to pair ${comparisonFieldsAsString[i]}/${i}`);
    for (let j=i+1; j < fields.length; j++) {
      nvdebug(` Compare with ${comparisonFieldsAsString[j]}/${j}`);
      let mergableField = fields[j];
      // Skip 1/7 from 1XX/7XX for similarity check:
      if ( comparisonFieldsAsString[i].substring(1) !== comparisonFieldsAsString[j].substring(1)) {
        nvdebug("  NOT PAIR");
        continue;
      }
      if (mergableField.deleted) {
        nvdebug("  DELETED");
        continue;
      }
      const str = `MERGE RELATOR TERM FIELD: ${fieldToString(mergableField)}`;
      nvdebug(str);

      if(!result.includes(str)) {
        result.push(str)
      }

      if (fix) {
        mergableField.deleted = 1;
        copyRelatorSubfields(mergableField, currField);
        fieldFixPunctuation(currField);
        sortAdjacentSubfields(currField); // Put the added $e subfield to proper places.
        sortAdjacentESubfields(currField); // Sort $e subfields with each other
        fieldFixPunctuation(currField);

      }
    }
  }

  if(!fix) {
    fields.forEach(f => delete f.deleted);
  }

  record.fields = record.fields.filter(f => !f.deleted);
  /* eslint-enable */
  return result;
}