From e3b8dd001acfbd6ef43dfe9f8cb719c36fb42639 Mon Sep 17 00:00:00 2001 From: Bruno Alves de Faria Date: Fri, 13 Jan 2017 10:57:06 -0200 Subject: [PATCH] LT-92: Adding checks for measurements length --- .../conformance/checkers/RecistChecker.js | 58 ++++++++++++++----- .../criterias/MeasurementsLengthCriteria.js | 46 ++++++++++++--- 2 files changed, 80 insertions(+), 24 deletions(-) diff --git a/Packages/ohif-measurements/client/conformance/checkers/RecistChecker.js b/Packages/ohif-measurements/client/conformance/checkers/RecistChecker.js index f4c0074ad..4d6661a5f 100644 --- a/Packages/ohif-measurements/client/conformance/checkers/RecistChecker.js +++ b/Packages/ohif-measurements/client/conformance/checkers/RecistChecker.js @@ -11,23 +11,16 @@ export class RecistChecker extends BaseChecker { constructor() { super(); - this.criterias.push(new MaxTargetsCriteria(5)); - this.criterias.push(new MaxTargetPerOrganCriteria(2)); - this.criterias.push(new MeasurementsLengthCriteria({})); - this.criterias.push(new ModalityCriteria(this.getModalityCriteriaOptions())); - this.criterias.push(new NonTargetResponseCriteria()); - this.criterias.push(new TargetTypeCriteria()); - } + const addCriteria = criteria => this.criterias.push(criteria); - check(data) { - const nonconformity = []; - this.criterias.forEach(criteria => { - const criteriaResult = criteria.check(data); - if (!criteriaResult.passed) { - nonconformity.push(criteriaResult); - } - }); - return nonconformity; + addCriteria(new MaxTargetsCriteria(5)); + addCriteria(new MaxTargetPerOrganCriteria(2)); + addCriteria(new ModalityCriteria(this.getModalityCriteriaOptions())); + addCriteria(new NonTargetResponseCriteria()); + addCriteria(new TargetTypeCriteria()); + addCriteria(new MeasurementsLengthCriteria(this.getExtranodalLengthCriteriaOptions())); + addCriteria(new MeasurementsLengthCriteria(this.getExtranodalXrayLengthCriteriaOptions())); + addCriteria(new MeasurementsLengthCriteria(this.getNodalLengthCriteriaOptions())); } getModalityCriteriaOptions() { @@ -37,4 +30,37 @@ export class RecistChecker extends BaseChecker { }; } + getExtranodalLengthCriteriaOptions() { + 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' + }; + } + + getExtranodalXrayLengthCriteriaOptions() { + 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)' + }; + } + + getNodalLengthCriteriaOptions() { + 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/criterias/MeasurementsLengthCriteria.js b/Packages/ohif-measurements/client/conformance/criterias/MeasurementsLengthCriteria.js index 9db32147a..5c9879f9c 100644 --- a/Packages/ohif-measurements/client/conformance/criterias/MeasurementsLengthCriteria.js +++ b/Packages/ohif-measurements/client/conformance/criterias/MeasurementsLengthCriteria.js @@ -4,20 +4,50 @@ export class MeasurementsLengthCriteria extends BaseCriteria { constructor(options) { super(); - options = { - shortAxis: 10, - longAxis: 10, - shortAxisSliceThicknessMultiplier: 2, - longAxisSliceThicknessMultiplier: 2, - modalitiesIn: ['CT', 'MR'], - locationNotIn: ['Lymph Node'] - }; this.options = options; } check(data) { let message; let measurements = []; + const { options } = this; + const longMultiplier = options.longAxisSliceThicknessMultiplier; + const shortMultiplier = options.shortAxisSliceThicknessMultiplier; + + data.targets.forEach(item => { + const { measurement, metadata } = item; + const { location, longestDiameter, shortestDiameter } = measurement; + const { sliceThickness } = metadata; + const modality = metadata.modality.toUpperCase(); + + // Stop here if the measurement does not match the modality and location filters + if (options.locationIn && options.locationIn.indexOf(location) === -1) return; + if (options.modalityIn && options.modalityIn.indexOf(modality) === -1) return; + if (options.locationNotIn && options.locationNotIn.indexOf(location) > -1) return; + if (options.modalityNotIn && options.modalityNotIn.indexOf(modality) > -1) return; + + // Check the measurement length + const failed = ( + (options.longAxis && longestDiameter < options.longAxis) || + (options.shortAxis && shortestDiameter < options.shortAxis) || ( + longMultiplier && !isNaN(sliceThickness) && + longestDiameter < (longMultiplier * sliceThickness) + ) || ( + shortMultiplier && !isNaN(sliceThickness) && + shortestDiameter < (shortMultiplier * sliceThickness) + ) + ); + + // Mark this measurement as invalid if some of the checks have failed + if (failed) { + measurements.push(measurement); + } + }); + + // Use the options' message if some measurement is invalid + if (measurements.length) { + message = options.message; + } return this.respond(message, measurements); }