174 lines
5.6 KiB
JavaScript
174 lines
5.6 KiB
JavaScript
import { Random } from 'meteor/random';
|
|
|
|
import { comparators } from '../lib/comparators';
|
|
|
|
const EQUALS_REGEXP = /^equals$/;
|
|
|
|
/**
|
|
* This Class represents a Rule to be evaluated given a set of attributes
|
|
* Rules have:
|
|
* - An attribute (e.g. 'seriesDescription')
|
|
* - A constraint Object, in the form required by Validate.js:
|
|
*
|
|
* rule.constraint = {
|
|
* contains: {
|
|
* value: 'T-1'
|
|
* }
|
|
* };
|
|
*
|
|
* Note: In this example we use the 'contains' Validator, which is a custom Validator defined in Viewerbase
|
|
*
|
|
* - A value for whether or not they are Required to be matched (default: False)
|
|
* - A value for their relative weighting during Protocol or Image matching (default: 1)
|
|
*/
|
|
export class Rule {
|
|
/**
|
|
* The Constructor for the Class to create a Rule with the bare
|
|
* minimum information
|
|
*
|
|
* @param name The desired name for the Rule
|
|
*/
|
|
constructor(attribute, constraint, required, weight) {
|
|
// Create a new UUID for this Rule
|
|
this.id = Random.id();
|
|
|
|
// Set the Rule's weight (defaults to 1)
|
|
this.weight = weight || 1;
|
|
|
|
// If an attribute is specified, assign it
|
|
if (attribute) {
|
|
this.attribute = attribute;
|
|
}
|
|
|
|
// If a constraint is specified, assign it
|
|
if (constraint) {
|
|
this.constraint = constraint;
|
|
}
|
|
|
|
// If a value for 'required' is specified, assign it
|
|
if (required === undefined) {
|
|
// If no value was specified, default to False
|
|
this.required = false;
|
|
} else {
|
|
this.required = required;
|
|
}
|
|
|
|
// Cache for constraint info object
|
|
this._constraintInfo = void 0;
|
|
|
|
// Cache for validator and value object
|
|
this._validatorAndValue = void 0;
|
|
}
|
|
|
|
/**
|
|
* Occasionally the Rule class needs to be instantiated from a JavaScript Object.
|
|
* This function fills in a Protocol with the Object data.
|
|
*
|
|
* @param input A Rule as a JavaScript Object, e.g. retrieved from MongoDB or JSON
|
|
*/
|
|
fromObject(input) {
|
|
// Check if the input already has an ID
|
|
// If so, keep it. It not, create a new UUID
|
|
this.id = input.id || Random.id();
|
|
|
|
// Assign the specified input data to the Rule
|
|
this.required = input.required;
|
|
this.weight = input.weight;
|
|
this.attribute = input.attribute;
|
|
this.constraint = input.constraint;
|
|
}
|
|
|
|
/**
|
|
* Get the constraint info object for the current constraint
|
|
* @return {Object\undefined} Constraint object or undefined if current constraint
|
|
* is not valid or not found in comparators list
|
|
*/
|
|
getConstraintInfo() {
|
|
let constraintInfo = this._constraintInfo;
|
|
// Check if info is cached already
|
|
if (constraintInfo !== void 0) {
|
|
return constraintInfo;
|
|
}
|
|
|
|
const ruleConstraint = Object.keys(this.constraint)[0];
|
|
|
|
if (ruleConstraint !== void 0) {
|
|
constraintInfo = comparators.find(comparator => ruleConstraint === comparator.id)
|
|
}
|
|
|
|
// Cache this information for later use
|
|
this._constraintInfo = constraintInfo;
|
|
|
|
return constraintInfo;
|
|
}
|
|
|
|
/**
|
|
* Check if current rule is related to priors
|
|
* @return {Boolean} True if a rule is related to priors or false otherwise
|
|
*/
|
|
isRuleForPrior() {
|
|
// @TODO: Should we check this too? this.attribute === 'relativeTime'
|
|
return this.attribute === 'abstractPriorValue';
|
|
}
|
|
|
|
/**
|
|
* If the current rule is a rule for priors, returns the number of referenced priors. Otherwise, returns -1.
|
|
* @return {Number} The number of referenced priors or -1 if not applicable. Returns zero if the actual value could not be determined.
|
|
*/
|
|
getNumberOfPriorsReferenced() {
|
|
if (!this.isRuleForPrior()) {
|
|
return -1;
|
|
}
|
|
|
|
// Get rule's validator and value
|
|
const ruleValidatorAndValue = this.getConstraintValidatorAndValue();
|
|
const { value, validator } = ruleValidatorAndValue;
|
|
const intValue = parseInt(value, 10) || 0; // avoid possible NaN
|
|
|
|
// "Equal to" validators
|
|
if (EQUALS_REGEXP.test(validator)) {
|
|
// In this case, -1 (the oldest prior) indicates that at least one study is used
|
|
return intValue < 0 ? 1 : intValue;
|
|
}
|
|
|
|
// Default cases return value
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Get the constraint validator and value
|
|
* @return {Object|undefined} Returns an object containing the validator and it's value or undefined
|
|
*/
|
|
getConstraintValidatorAndValue() {
|
|
let validatorAndValue = this._validatorAndValue;
|
|
|
|
// Check if validator and value are cached already
|
|
if (validatorAndValue !== void 0) {
|
|
return validatorAndValue;
|
|
}
|
|
|
|
// Get the constraint info object
|
|
const constraintInfo = this.getConstraintInfo();
|
|
|
|
// Constraint info object exists and is valid
|
|
if (constraintInfo !== void 0) {
|
|
const validator = constraintInfo.validator;
|
|
const currentValidator = this.constraint[validator];
|
|
|
|
if (currentValidator) {
|
|
const constraintValidator = constraintInfo.validatorOption;
|
|
const constraintValue = currentValidator[constraintValidator];
|
|
|
|
validatorAndValue = {
|
|
value: constraintValue,
|
|
validator: constraintInfo.id
|
|
};
|
|
|
|
this._validatorAndValue = validatorAndValue;
|
|
}
|
|
}
|
|
|
|
return validatorAndValue;
|
|
}
|
|
}
|