LT-92: Restructuring the criteria in JSON format

This commit is contained in:
Bruno Alves de Faria 2017-01-13 14:39:49 -02:00
parent b8c397a9b4
commit ada2d1e284
13 changed files with 90 additions and 106 deletions

View File

@ -1,6 +1,7 @@
import { OHIF } from 'meteor/ohif:core';
import { _ } from 'meteor/underscore';
import { RecistChecker } from './checkers/RecistChecker';
import { CriteriaEvaluator } from './CriteriaEvaluator';
import * as recistBaselineEvaluation from './evaluations/recistBaseline.json';
class ConformanceCriteria {
@ -17,8 +18,8 @@ class ConformanceCriteria {
}
validateRecist(data) {
const recistChecker = new RecistChecker();
console.warn('>>>>check', recistChecker.check(data));
const recistBaselineEvaluator = new CriteriaEvaluator(recistBaselineEvaluation);
console.warn('>>>>check', recistBaselineEvaluator.evaluate(data));
}
/*

View File

@ -0,0 +1,33 @@
import * as Criteria from './criteria';
import { _ } from 'meteor/underscore';
export class CriteriaEvaluator {
constructor(criteriaObject) {
this.criteria = [];
_.each(criteriaObject, (optionsObject, criterionkey) => {
const Criterion = Criteria[`${criterionkey}Criterion`];
if (optionsObject instanceof Array) {
_.each(optionsObject, options => {
const criterion = new Criterion(options);
this.criteria.push(criterion);
});
} else {
this.criteria.push(new Criterion(optionsObject));
}
});
}
evaluate(data) {
const nonconformity = [];
this.criteria.forEach(criterion => {
const criterionResult = criterion.evaluate(data);
if (!criterionResult.passed) {
nonconformity.push(criterionResult);
}
});
return nonconformity;
}
}

View File

@ -1,18 +0,0 @@
export class BaseChecker {
constructor() {
this.criteria = [];
}
check(data) {
const nonconformity = [];
this.criteria.forEach(criterion => {
const criterionResult = criterion.evaluate(data);
if (!criterionResult.passed) {
nonconformity.push(criterionResult);
}
});
return nonconformity;
}
}

View File

@ -1,66 +0,0 @@
import { BaseChecker } from './BaseChecker';
import { MaxTargetPerOrganCriterion } from '../criteria/MaxTargetPerOrgan';
import { MaxTargetsCriterion } from '../criteria/MaxTargets';
import { MeasurementsLengthCriterion } from '../criteria/MeasurementsLength';
import { ModalityCriterion } from '../criteria/Modality';
import { NonTargetResponseCriterion } from '../criteria/NonTargetResponse';
import { TargetTypeCriterion } from '../criteria/TargetType';
export class RecistChecker extends BaseChecker {
constructor() {
super();
const addCriterion = criterion => this.criteria.push(criterion);
addCriterion(new MaxTargetsCriterion(5));
addCriterion(new MaxTargetPerOrganCriterion(2));
addCriterion(new ModalityCriterion(this.getModalityOptions()));
addCriterion(new NonTargetResponseCriterion());
addCriterion(new TargetTypeCriterion());
addCriterion(new MeasurementsLengthCriterion(this.getExtranodalLengthOptions()));
addCriterion(new MeasurementsLengthCriterion(this.getExtranodalXrayLengthOptions()));
addCriterion(new MeasurementsLengthCriterion(this.getNodalLengthOptions()));
}
getModalityOptions() {
return {
method: 'restrict',
modalities: ['US']
};
}
getExtranodalLengthOptions() {
return {
longAxis: 10,
longAxisSliceThicknessMultiplier: 2,
modalityIn: ['CT', 'MR'],
locationNotIn: ['Lymph Node'],
message: 'Extranodal lesions must be >= 10mm long axis AND ' +
'>= double the acquisition slice thickness by CT and MR'
};
}
getExtranodalXrayLengthOptions() {
return {
shortAxis: 20,
longAxis: 20,
modalityIn: ['PX', 'XA'],
locationNotIn: ['Lymph Node'],
message: 'Extranodal lesions must be >= 20mm on chest x-ray ' +
'(although x-rays rarely used for clinical trial assessment)'
};
}
getNodalLengthOptions() {
return {
shortAxis: 15,
shortAxisSliceThicknessMultiplier: 2,
modalityIn: ['CT', 'MR'],
locationIn: ['Lymph Node'],
message: 'Nodal lesions must be >= 15mm short axis AND ' +
'>= double the acquisition slice thickness by CT and MR'
};
}
}

View File

@ -1,6 +1,8 @@
export class BaseCriterion {
constructor() {}
constructor(options) {
this.options = options;
}
generateResponse(message, measurements) {
const passed = !message;

View File

@ -2,9 +2,8 @@ import { BaseCriterion } from './BaseCriterion';
export class MaxTargetPerOrganCriterion extends BaseCriterion {
constructor(targetsLimit) {
super();
this.targetsLimit = targetsLimit;
constructor(options) {
super(options);
}
evaluate(data) {
@ -20,13 +19,13 @@ export class MaxTargetPerOrganCriterion extends BaseCriterion {
}
targetsPerOrgan[location].add(measurementNumber);
if (targetsPerOrgan[location].size > this.targetsLimit) {
if (targetsPerOrgan[location].size > this.options.limit) {
measurements.push(measurement);
}
}
if (measurements.length) {
message = `Each organ should not have more than ${this.targetsLimit} targets.`;
message = `Each organ should not have more than ${this.options.limit} targets.`;
}
return this.generateResponse(message, measurements);

View File

@ -3,9 +3,8 @@ import { _ } from 'meteor/underscore';
export class MaxTargetsCriterion extends BaseCriterion {
constructor(targetsLimit) {
super();
this.targetsLimit = targetsLimit;
constructor(options) {
super(options);
}
evaluate(data) {
@ -14,8 +13,8 @@ export class MaxTargetsCriterion extends BaseCriterion {
}));
let message;
if (measurementNumbers.length > this.targetsLimit) {
message = `The study should not have more than ${this.targetsLimit} targets.`;
if (measurementNumbers.length > this.options.limit) {
message = `The study should not have more than ${this.options.limit} targets.`;
}
return this.generateResponse(message);

View File

@ -3,8 +3,7 @@ import { BaseCriterion } from './BaseCriterion';
export class MeasurementsLengthCriterion extends BaseCriterion {
constructor(options) {
super();
this.options = options;
super(options);
}
evaluate(data) {

View File

@ -6,13 +6,13 @@ import { _ } from 'meteor/underscore';
* Check if a modality is allowed or restricted
* Options:
* method (string): allow, restrict
* measurementTypes (string[]): list of measurement types that will be evaluated
* modalities (string[]): list of allowed/restricted modalities
*/
export class ModalityCriterion extends BaseCriterion {
constructor(options) {
super();
this.options = options;
super(options);
}
evaluate(data) {

View File

@ -2,8 +2,8 @@ import { BaseCriterion } from './BaseCriterion';
export class NonTargetResponseCriterion extends BaseCriterion {
constructor() {
super();
constructor(options) {
super(options);
}
evaluate(data) {

View File

@ -2,8 +2,8 @@ import { BaseCriterion } from './BaseCriterion';
export class TargetTypeCriterion extends BaseCriterion {
constructor() {
super();
constructor(options) {
super(options);
}
evaluate(data) {

View File

@ -0,0 +1,6 @@
export { MaxTargetPerOrganCriterion } from './MaxTargetPerOrgan';
export { MaxTargetsCriterion } from './MaxTargets';
export { MeasurementsLengthCriterion } from './MeasurementsLength';
export { ModalityCriterion } from './Modality';
export { NonTargetResponseCriterion } from './NonTargetResponse';
export { TargetTypeCriterion } from './TargetType';

View File

@ -0,0 +1,29 @@
{
"MaxTargetPerOrgan": {
"limit": 2
},
"MaxTargets": {
"limit": 5
},
"MeasurementsLength": [{
"longAxis": 10,
"longAxisSliceThicknessMultiplier": 2,
"modalityIn": ["CT", "MR"],
"locationNotIn": ["Lymph Node"],
"message": "Extranodal lesions must be >= 10mm long axis AND >= double the acquisition slice thickness by CT and MR"
}, {
"shortAxis": 20,
"longAxis": 20,
"modalityIn": ["PX", "XA"],
"locationNotIn": ["Lymph Node"],
"message": "Extranodal lesions must be >= 20mm on chest x-ray (although x-rays rarely used for clinical trial assessment)"
}, {
"shortAxis": 15,
"shortAxisSliceThicknessMultiplier": 2,
"modalityIn": ["CT", "MR"],
"locationIn": ["Lymph Node"],
"message": "Nodal lesions must be >= 15mm short axis AND >= double the acquisition slice thickness by CT and MR"
}],
"NonTargetResponse": {},
"TargetType": {}
}