NatLibFi/marc-record-validators-melinda

View on GitHub
src/sortRelatorTerms.js

Summary

Maintainability
A
3 hrs
Test Coverage
// Validator/fixer for sorting $e relator term subfields
//
// Author(s): Nicholas Volk

import clone from 'clone';
//import createDebugLogger from 'debug';
import {fieldToString} from './utils';
import {fieldFixPunctuation} from './punctuation2';
import {relatorTermScore} from './sortFields';
//const debug = createDebugLogger('@natlibfi/marc-record-validators-melinda:sortRelatorTerms');
//const debugData = debug.extend('data');

const WORST_WORK = 98;

function scoreRelatorTerm(term) {
  const normalizedTerm = normalizeValue(term);
  if (normalizedTerm in relatorTermScore) {
    return relatorTermScore[normalizedTerm];
  }
  return 0;
}

export default function () {

  return {
    description: 'Sort adjacent $e subfields in field [1678][01]0',
    validate, fix
  };

  function fix(record) {
    const res = {message: [], fix: [], valid: true};

    record.fields.forEach(field => {
      sortAdjacentESubfields(field);
    });

    return res;
  }

  function validate(record) {
    const res = {message: []};

    record.fields.forEach(field => {
      const clonedField = clone(field);
      sortAdjacentESubfields(clonedField);
      const clonedFieldAsString = fieldToString(clonedField);
      const fieldAsString = fieldToString(field);
      if (fieldAsString !== clonedFieldAsString) { // eslint-disable-line functional/no-conditional-statements
        res.message.push(`${fieldAsString} => ${clonedFieldAsString}`); // eslint-disable-line functional/immutable-data
      }
    });

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


function normalizeValue(value) {
  // Removing last punc char is good enough for our purposes.
  // We don't handle abbreviations here etc.
  // Brackets should not happen either, should they?
  return value.replace(/[.,]$/u, '');
}


function swapESubfields(field) {
  if (!field.subfields) {
    return;
  }

  const loopAgain = field.subfields.some((sf, index) => {
    if (index === 0 || sf.code !== 'e') {
      return false;
    }
    const currScore = scoreRelatorTerm(sf.value);

    const prevSubfield = field.subfields[index - 1];
    if (currScore === 0 || prevSubfield.code !== 'e') {
      return false;
    }
    const prevScore = scoreRelatorTerm(prevSubfield.value);


    // If this subfield maps to a Work, then subfields can be swapped, even if we don't have a score for the prev subfield!
    if (prevScore === 0 && currScore < WORST_WORK) {
      return false;
    }

    if (currScore > prevScore) {
      // Swap:
      const tmp = field.subfields[index - 1];
      field.subfields[index - 1] = sf; // eslint-disable-line functional/immutable-data
      field.subfields[index] = tmp; // eslint-disable-line functional/immutable-data
      fieldFixPunctuation(field);
      return true;
    }

    return false;

  });

  if (loopAgain) {
    swapESubfields(field); // uh, evil recursion...
    return;
  }

  return;

}

export function sortAdjacentESubfields(field) {
  if (!field.subfields) {
    return field;
  }
  swapESubfields(field);

  return field;
}