@natlibfi/melinda-record-match-validator
Version:
Validates if two records matched by melinda-record-matching can be merged and sets merge priority
277 lines (263 loc) • 9.37 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
exports.matchValidationForMergeUi = matchValidationForMergeUi;
var _debug = _interopRequireDefault(require("debug"));
var _comparisonTasks = require("./comparisonTasks");
var _field = require("./field984");
var _utils = require("./utils");
var _marcRecord = require("@natlibfi/marc-record");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const debug = (0, _debug.default)('@natlibfi/melinda-record-match-validator:index');
const debugDev = debug.extend('dev');
//const debugData = debug.extend('data');
// Apply some recursion evilness/madness/badness to perform only the tests we really really really want.
function runComparisonTasks({
nth,
record1,
record2,
checkPreference = true,
record1External = {},
record2External = {},
returnAll = false,
comparisonTasks = _comparisonTasks.comparisonTasksTable.recordImport
}) {
// DEVELOP: We could skip those tasks that are !validation if !checkPreference - but how?
const currResult = comparisonTasks[nth].function({
record1,
record2,
checkPreference,
record1External,
record2External
});
// NB! Aborts after the last task or after a failure (meaning currResult === false)! No further tests are performed. Recursion means optimization :D
debugDev(`Running task ${nth} (${comparisonTasks[nth].name}) - returnAll: ${returnAll}`);
if (nth === comparisonTasks.length - 1 || !returnAll && currResult === false) {
// eslint-disable-line no-extra-parens
return [currResult];
}
return [currResult].concat(runComparisonTasks({
nth: nth + 1,
record1,
record2,
checkPreference,
record1External,
record2External,
returnAll,
comparisonTasks
}));
}
function makeComparisons({
record1,
record2,
checkPreference = true,
record1External = {},
record2External = {},
returnAll = false,
comparisonTasks = _comparisonTasks.comparisonTasksTable.recordImport
}) {
debugDev(`returnAll: ${returnAll}`);
// Start with sanity check(s): if there are no tasks, it is not a failure:
if (comparisonTasks.length === 0) {
const resultForZeroTasks = {
result: true,
reason: `No rules defined`
};
if (returnAll) {
return [resultForZeroTasks];
}
return resultForZeroTasks;
}
// Get results (if not returnAll, just up to the point of first failure):
const results = runComparisonTasks({
nth: 0,
record1,
record2,
checkPreference,
record1External,
record2External,
returnAll,
comparisonTasks
});
return returnAll ? returnAllResults() : returnDecisionPointResult();
// return array of all comparison task results
function returnAllResults() {
const allResults = results.map((result, i) => ({
result,
reason: comparisonTasks[i].name,
preference: comparisonTasks[i].preference,
validation: comparisonTasks[i].validation,
level: comparisonTasks[i].manual === undefined ? 'error' : comparisonTasks[i].manual,
// eslint-disable-next-line camelcase
validation_message_fi: comparisonTasks[i].validation_message_fi,
// eslint-disable-next-line camelcase
preference_message_fi: comparisonTasks[i].preference_message_fi
}));
if (checkPreference) {
// Add f984 overide result (check preference override from records f984)
const field984OverrideResult = getField984OverrideResult();
if (field984OverrideResult) {
return allResults.concatenate(field984OverrideResult);
}
return allResults;
}
return allResults;
}
// return result from the decision point where first task fails
function returnDecisionPointResult() {
// If we do not want all results, if any of tests fails, return false and description for failing test
if (results.length < comparisonTasks.length || results[results.length - 1] === false) {
(0, _utils.nvdebug)(`makeComparisons() failed. Reason: ${comparisonTasks[results.length - 1].description}. (TEST: ${results.length}/${comparisonTasks.length})`, debugDev);
return {
result: false,
reason: `${comparisonTasks[results.length - 1].description} failed`
};
}
if (!checkPreference) {
// This will also skip separate field 984 check
return {
result: true,
reason: 'all tests passed'
};
}
// Let's do extra check for preference override in records
const field984OverrideResult = getField984OverrideResult();
if (field984OverrideResult) {
return field984OverrideResult;
}
const decisionPoint = results.findIndex(val => val !== true && val !== false);
if (decisionPoint === -1) {
return {
result: true,
reason: 'both records passed all tests, but no winner was found'
};
}
return {
result: results[decisionPoint],
reason: `${results[decisionPoint]} won ${comparisonTasks[decisionPoint].description}`
};
}
// We get a separate override result, because normal preference checks find first preference
function getField984OverrideResult() {
const field984Override = (0, _field.check984)({
record1,
record2
});
if (field984Override === 'A' || field984Override === 'B') {
return {
result: field984Override,
reason: 'Field 984 override applied (MRA-744)'
};
}
return;
}
}
// record1External/record2External includes external information for record (for example whether it is an incomingRecord or databaseRecord)
// MergeUI is currently used for manual merging of two database records
// Returns array of failure responses, empty array if matchValidator does not return failures
// [{ "result": "error/warning", "type": "validation/preference", "message": "finnish message"}]
function matchValidationForMergeUi({
record1Object,
record2Object,
checkPreference = true,
record1External = {
'recordSource': 'databaseRecord'
},
record2External = {
'recordSource': 'databaseRecord'
},
manual = true,
comparisonTasks = _comparisonTasks.comparisonTasksTable.humanMerge
}) {
debugDev(`Manual ${manual} (for Merge UI) - we have ${comparisonTasks.length} comparison tasks`);
// Create MarcRecords here to avoid problems with differing MarcRecord versions etc.
const record1 = new _marcRecord.MarcRecord(record1Object, {
subfieldValues: false
});
const record2 = new _marcRecord.MarcRecord(record2Object, {
subfieldValues: false
});
const result = makeComparisons({
record1,
record2,
checkPreference,
record1External,
record2External,
returnAll: true,
comparisonTasks
});
debugDev(JSON.stringify(result));
// return result-array failed results
const resultForMergeUi = filterResultsForMergeUI(result);
return resultForMergeUi;
// Return to Merge UI only results that require action, ie. those that fail merge or change preference
// MergeUI sends records non-preferred record as record1 and preferred record as record2, so preference result 'A' is a warning
function filterResultsForMergeUI(allResults) {
const failure = allResults.filter(r => r.result !== true) // Filter out passed tests
.filter(r => r.result !== 'B'); // Filter out passed preference tests (record2/B is preferred)
debugDev(`MatchValidator failed: ${JSON.stringify(failure, null, 4)}`);
const messages = failure
// eslint-disable-next-line camelcase
.map(({
result,
level,
validation_message_fi,
preference_message_fi
}) => ({
result: result === 'A' ? 'warning' : level,
// all preference-results are warning in UI
type: result === 'A' ? 'preference' : 'validation',
//
// eslint-disable-next-line camelcase
message: result === 'A' ? preference_message_fi : validation_message_fi
})); // Convert to messages
debugDev(`MatchValidator results for MergeUI: ${JSON.stringify(messages, null, 4)}`);
return messages;
}
}
// record1External/record2External includes external information for record (for example whether it is an incomingRecord or databaseRecord)
var _default = ({
record1Object,
record2Object,
checkPreference = true,
record1External = {},
record2External = {},
manual = false,
comparisonTasks = _comparisonTasks.comparisonTasksTable.recordImport
}) => {
debugDev(`Default (manual: ${manual}) (for record import) we have ${comparisonTasks.length} comparison tasks`);
// Create MarcRecords here to avoid problems with differing MarcRecord versions etc.
const record1 = new _marcRecord.MarcRecord(record1Object, {
subfieldValues: false
});
const record2 = new _marcRecord.MarcRecord(record2Object, {
subfieldValues: false
});
const result = makeComparisons({
record1,
record2,
checkPreference,
record1External,
record2External,
comparisonTasks
});
debug(`Comparison result: ${result.result}, reason: ${result.reason}`);
if (result.result === false) {
return {
action: false,
preference: false,
message: result.reason
};
}
return {
action: 'merge',
preference: {
'name': result.reason,
'value': result.result
}
};
};
exports.default = _default;
//# sourceMappingURL=index.js.map