From ada2d1e2848baf2bc5c8ba9383e679cd7409e0fb Mon Sep 17 00:00:00 2001 From: Bruno Alves de Faria Date: Fri, 13 Jan 2017 14:39:49 -0200 Subject: [PATCH] LT-92: Restructuring the criteria in JSON format --- .../client/conformance/ConformanceCriteria.js | 7 +- .../client/conformance/CriteriaEvaluator.js | 33 ++++++++++ .../conformance/checkers/BaseChecker.js | 18 ----- .../conformance/checkers/RecistChecker.js | 66 ------------------- .../conformance/criteria/BaseCriterion.js | 4 +- .../conformance/criteria/MaxTargetPerOrgan.js | 9 ++- .../client/conformance/criteria/MaxTargets.js | 9 ++- .../criteria/MeasurementsLength.js | 3 +- .../client/conformance/criteria/Modality.js | 4 +- .../conformance/criteria/NonTargetResponse.js | 4 +- .../client/conformance/criteria/TargetType.js | 4 +- .../client/conformance/criteria/index.js | 6 ++ .../evaluations/recistBaseline.json | 29 ++++++++ 13 files changed, 90 insertions(+), 106 deletions(-) create mode 100644 Packages/ohif-measurements/client/conformance/CriteriaEvaluator.js delete mode 100644 Packages/ohif-measurements/client/conformance/checkers/BaseChecker.js delete mode 100644 Packages/ohif-measurements/client/conformance/checkers/RecistChecker.js create mode 100644 Packages/ohif-measurements/client/conformance/criteria/index.js create mode 100644 Packages/ohif-measurements/client/conformance/evaluations/recistBaseline.json diff --git a/Packages/ohif-measurements/client/conformance/ConformanceCriteria.js b/Packages/ohif-measurements/client/conformance/ConformanceCriteria.js index 9d655982a..ddf1d9bcb 100644 --- a/Packages/ohif-measurements/client/conformance/ConformanceCriteria.js +++ b/Packages/ohif-measurements/client/conformance/ConformanceCriteria.js @@ -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)); } /* diff --git a/Packages/ohif-measurements/client/conformance/CriteriaEvaluator.js b/Packages/ohif-measurements/client/conformance/CriteriaEvaluator.js new file mode 100644 index 000000000..7c1469395 --- /dev/null +++ b/Packages/ohif-measurements/client/conformance/CriteriaEvaluator.js @@ -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; + } + +} diff --git a/Packages/ohif-measurements/client/conformance/checkers/BaseChecker.js b/Packages/ohif-measurements/client/conformance/checkers/BaseChecker.js deleted file mode 100644 index d0bba5067..000000000 --- a/Packages/ohif-measurements/client/conformance/checkers/BaseChecker.js +++ /dev/null @@ -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; - } - -} diff --git a/Packages/ohif-measurements/client/conformance/checkers/RecistChecker.js b/Packages/ohif-measurements/client/conformance/checkers/RecistChecker.js deleted file mode 100644 index 49aac6fde..000000000 --- a/Packages/ohif-measurements/client/conformance/checkers/RecistChecker.js +++ /dev/null @@ -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' - }; - } - -} diff --git a/Packages/ohif-measurements/client/conformance/criteria/BaseCriterion.js b/Packages/ohif-measurements/client/conformance/criteria/BaseCriterion.js index 65a80756e..2cfb00d37 100644 --- a/Packages/ohif-measurements/client/conformance/criteria/BaseCriterion.js +++ b/Packages/ohif-measurements/client/conformance/criteria/BaseCriterion.js @@ -1,6 +1,8 @@ export class BaseCriterion { - constructor() {} + constructor(options) { + this.options = options; + } generateResponse(message, measurements) { const passed = !message; diff --git a/Packages/ohif-measurements/client/conformance/criteria/MaxTargetPerOrgan.js b/Packages/ohif-measurements/client/conformance/criteria/MaxTargetPerOrgan.js index 094c5eb19..12b2cab35 100644 --- a/Packages/ohif-measurements/client/conformance/criteria/MaxTargetPerOrgan.js +++ b/Packages/ohif-measurements/client/conformance/criteria/MaxTargetPerOrgan.js @@ -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); diff --git a/Packages/ohif-measurements/client/conformance/criteria/MaxTargets.js b/Packages/ohif-measurements/client/conformance/criteria/MaxTargets.js index f1e50b26f..96a2343d9 100644 --- a/Packages/ohif-measurements/client/conformance/criteria/MaxTargets.js +++ b/Packages/ohif-measurements/client/conformance/criteria/MaxTargets.js @@ -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); diff --git a/Packages/ohif-measurements/client/conformance/criteria/MeasurementsLength.js b/Packages/ohif-measurements/client/conformance/criteria/MeasurementsLength.js index d641323ec..d89cca3d0 100644 --- a/Packages/ohif-measurements/client/conformance/criteria/MeasurementsLength.js +++ b/Packages/ohif-measurements/client/conformance/criteria/MeasurementsLength.js @@ -3,8 +3,7 @@ import { BaseCriterion } from './BaseCriterion'; export class MeasurementsLengthCriterion extends BaseCriterion { constructor(options) { - super(); - this.options = options; + super(options); } evaluate(data) { diff --git a/Packages/ohif-measurements/client/conformance/criteria/Modality.js b/Packages/ohif-measurements/client/conformance/criteria/Modality.js index 4a87b4c12..713c1b887 100644 --- a/Packages/ohif-measurements/client/conformance/criteria/Modality.js +++ b/Packages/ohif-measurements/client/conformance/criteria/Modality.js @@ -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) { diff --git a/Packages/ohif-measurements/client/conformance/criteria/NonTargetResponse.js b/Packages/ohif-measurements/client/conformance/criteria/NonTargetResponse.js index 57876b6ad..8f636ebfa 100644 --- a/Packages/ohif-measurements/client/conformance/criteria/NonTargetResponse.js +++ b/Packages/ohif-measurements/client/conformance/criteria/NonTargetResponse.js @@ -2,8 +2,8 @@ import { BaseCriterion } from './BaseCriterion'; export class NonTargetResponseCriterion extends BaseCriterion { - constructor() { - super(); + constructor(options) { + super(options); } evaluate(data) { diff --git a/Packages/ohif-measurements/client/conformance/criteria/TargetType.js b/Packages/ohif-measurements/client/conformance/criteria/TargetType.js index 3ec41f5a1..f12fbede6 100644 --- a/Packages/ohif-measurements/client/conformance/criteria/TargetType.js +++ b/Packages/ohif-measurements/client/conformance/criteria/TargetType.js @@ -2,8 +2,8 @@ import { BaseCriterion } from './BaseCriterion'; export class TargetTypeCriterion extends BaseCriterion { - constructor() { - super(); + constructor(options) { + super(options); } evaluate(data) { diff --git a/Packages/ohif-measurements/client/conformance/criteria/index.js b/Packages/ohif-measurements/client/conformance/criteria/index.js new file mode 100644 index 000000000..b4b010241 --- /dev/null +++ b/Packages/ohif-measurements/client/conformance/criteria/index.js @@ -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'; diff --git a/Packages/ohif-measurements/client/conformance/evaluations/recistBaseline.json b/Packages/ohif-measurements/client/conformance/evaluations/recistBaseline.json new file mode 100644 index 000000000..07108dcbb --- /dev/null +++ b/Packages/ohif-measurements/client/conformance/evaluations/recistBaseline.json @@ -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": {} +}