LT-291: Re-implementing the Non-Target tool behavior

This commit is contained in:
Bruno Alves de Faria 2016-11-14 15:22:51 -02:00 committed by Erik Ziegler
parent e348ff1d4d
commit 5d8a99dc5f
12 changed files with 195 additions and 56 deletions

View File

@ -3,7 +3,7 @@
<div class="viewerDialogs">
{{>confirmDeleteDialog}}
{{>measurementLocationDialog (clone this)}}
{{>nonTargetMeasurementDialog (clone this)}}
{{! >nonTargetMeasurementDialog (clone this)}}
{{>nonTargetResponseDialog (clone this)}}
{{>measurementTableHUD (clone this)}}
{{>cineDialog}}

View File

@ -59,3 +59,7 @@ label.form-group
.modal-header, .modal-footer
theme('border-color', '$uiBorderColor')
button.close
theme('color', '$textSecondaryColor')
opacity: 1

View File

@ -4,18 +4,18 @@ import { MeasurementSchemaTypes } from 'meteor/ohif:measurements/both/schema/mea
const CornerstoneHandleSchema = MeasurementSchemaTypes.CornerstoneHandleSchema;
const NonTargetHandlesSchema = new SimpleSchema({
start: {
type: CornerstoneHandleSchema,
label: 'Start'
},
end: {
type: CornerstoneHandleSchema,
label: 'End'
},
textBox: {
type: CornerstoneHandleSchema,
label: 'Text Box'
}
start: {
type: CornerstoneHandleSchema,
label: 'Start'
},
end: {
type: CornerstoneHandleSchema,
label: 'End'
},
textBox: {
type: CornerstoneHandleSchema,
label: 'Text Box'
}
});
const NonTargetSchema = new SimpleSchema([MeasurementSchemaTypes.CornerstoneToolMeasurement, {
@ -24,10 +24,15 @@ const NonTargetSchema = new SimpleSchema([MeasurementSchemaTypes.CornerstoneTool
label: 'Handles'
},
response: {
type: String,
label: 'Response',
type: String,
label: 'Response',
optional: true // Optional because it is added after initial drawing, via a callback
},
location: {
type: String,
label: 'Location',
optional: true
},
locationUid: {
type: String,
label: 'Location UID',
@ -50,6 +55,6 @@ export const nonTarget = {
measurementTableOptions: {
displayFunction: displayFunction
},
includeInCaseProgress: true,
includeInCaseProgress: true,
},
}
};

View File

@ -0,0 +1,60 @@
export const FieldLesionLocation = {
type: String,
label: 'Lesion Location',
allowedValues: [
'',
'Abdominal/Chest Wall',
'Adrenal',
'Bladder',
'Bone',
'Brain',
'Breast',
'Colon',
'Esophagus',
'Extremities',
'Gallbladder',
'Kidney',
'Liver',
'Lung',
'Lymph Node',
'Mediastinum/Hilum',
'Muscle',
'Neck',
'Other: Soft Tissue',
'Ovary',
'Pancreas',
'Pelvis',
'Peritoneum/Omentum',
'Prostate',
'Retroperitoneum',
'Small Bowel',
'Spleen',
'Stomach',
'Subcutaneous'
]
};
export const FieldLesionLocationResponse = {
type: String,
label: 'Lesion Location Response',
allowedValues: [
'',
'-1',
'PD',
'SD',
'Present',
'NE',
'NN',
'EX'
],
valuesLabels: [
'',
'CR - Complete response',
'PD - Progressive disease',
'SD - Stable disease',
'Present - Present',
'NE - Not Evaluable',
'NN - Non-CR/Non-PD',
'EX - Excluded from Assessment'
]
};

View File

@ -0,0 +1 @@
export * from './fields.js';

View File

@ -5,9 +5,8 @@
var toolType = 'nonTarget';
var configuration = {
setMeasurementNumberCallback: setMeasurementNumberCallback,
getmeasurementLocationCallback: getmeasurementLocationCallback,
changemeasurementLocationCallback: changemeasurementLocationCallback,
getMeasurementLocationCallback: getMeasurementLocationCallback,
changeMeasurementLocationCallback: changeMeasurementLocationCallback,
drawHandles: false,
drawHandlesOnHover: true,
arrowFirst: true
@ -17,20 +16,20 @@
var keys = {
ESC: 27
};
// Set measurement number
// Get Non-Target measurements on image
function setMeasurementNumberCallback(measurementData, eventData, doneCallback) {
var measurementNumber = 1;
doneCallback(measurementNumber);
}
// Define a callback to get your text annotation
// This could be used, e.g. to open a modal
function getmeasurementLocationCallback(measurementData, eventData, doneCallback) {
doneCallback(prompt('Enter your measurement location:'));
function getMeasurementLocationCallback(measurementData, eventData, doneCallback) {
const dialogSettings = {
title: 'Select Lesion Location',
measurementData,
eventData
};
OHIF.ui.showFormDialog('dialogNonTargetMeasurement', dialogSettings).then(formData => {
doneCallback(formData.measurementNumber);
});
}
function changemeasurementLocationCallback(measurementData, eventData, doneCallback) {
function changeMeasurementLocationCallback(measurementData, eventData, doneCallback) {
doneCallback(prompt('Change your measurement location:'));
}
@ -53,11 +52,6 @@
var config = cornerstoneTools.nonTarget.getConfiguration();
// Set measurement number and measurement name
if (measurementData.measurementNumber === undefined) {
config.setMeasurementNumberCallback(measurementData, mouseEventData, doneCallback);
}
// associate this data with this imageId so we can render it and manipulate it
cornerstoneTools.addToolState(mouseEventData.element, toolType, measurementData);
@ -92,7 +86,7 @@
// delete the measurement
cornerstoneTools.removeToolState(mouseEventData.element, toolType, measurementData);
} else {
config.getmeasurementLocationCallback(measurementData, mouseEventData, doneCallback);
config.getMeasurementLocationCallback(measurementData, mouseEventData, doneCallback);
}
// Unbind the Esc keydown hook
@ -103,8 +97,6 @@
$(element).on('CornerstoneToolsMouseDownActivate', eventData, cornerstoneTools.nonTarget.mouseDownActivateCallback);
$(element).on('CornerstoneToolsMouseDoubleClick', eventData, doubleClickCallback);
$(element).off('keydown', cancelCallback);
cornerstone.updateImage(mouseEventData.element);
});
}
@ -272,11 +264,6 @@
$(element).off('CornerstoneToolsTap', cornerstoneTools.nonTargetTouch.tapCallback);
var config = cornerstoneTools.nonTarget.getConfiguration();
// Set measurement number and measurement name
if (measurementData.measurementName === undefined) {
config.setMeasurementNumberCallback(measurementData, touchEventData, doneCallback);
}
cornerstone.updateImage(element);
cornerstoneTools.moveNewHandleTouch(touchEventData, toolType, measurementData, measurementData.handles.end, function() {
@ -287,7 +274,7 @@
cornerstoneTools.removeToolState(element, toolType, measurementData);
}
config.getmeasurementLocationCallback(measurementData, touchEventData, doneCallback);
config.getMeasurementLocationCallback(measurementData, touchEventData, doneCallback);
$(element).on('CornerstoneToolsTouchDrag', cornerstoneTools.nonTargetTouch.touchMoveHandle);
$(element).on('CornerstoneToolsDragStartActive', cornerstoneTools.nonTargetTouch.touchDownActivateCallback);
@ -330,7 +317,7 @@
data.active = true;
cornerstone.updateImage(element);
// Allow relabelling via a callback
config.changemeasurementLocationCallback(data, eventData, doneCallback);
config.changeMeasurementLocationCallback(data, eventData, doneCallback);
e.stopImmediatePropagation();
return false;

View File

@ -0,0 +1,9 @@
<template name="dialogNonTargetMeasurement">
{{#dialogForm (extend this dialogClass='modal-sm' schema=(choose this.schema instance.schema))}}
{{>inputHidden key='measurementNumber'}}
{{>inputSelect labelClass='form-group' key='location' hideSearch=true
options=(clone placeholder='Choose a lesion location')}}
{{>inputSelect labelClass='form-group' key='response' hideSearch=true
options=(clone placeholder='Choose a lesion location response')}}
{{/dialogForm}}
</template>

View File

@ -0,0 +1,69 @@
import { Template } from 'meteor/templating';
import { Blaze } from 'meteor/blaze';
import { SimpleSchema } from 'meteor/aldeed:simple-schema';
import { _ } from 'meteor/underscore';
import { FieldLesionLocation, FieldLesionLocationResponse } from 'meteor/ohif:lesiontracker/both/schema/fields';
Template.dialogNonTargetMeasurement.onCreated(() => {
const instance = Template.instance();
instance.schema = new SimpleSchema({
location: FieldLesionLocation,
response: FieldLesionLocationResponse,
measurementNumber: {
type: Number
}
});
});
Template.dialogNonTargetMeasurement.onRendered(() => {
const instance = Template.instance();
const form = instance.$('form').data('component');
const viewerMain = $(instance.data.eventData.element).closest('.viewerMain')[0];
const viewerData = Blaze.getData(viewerMain);
console.warn('>>>>dialogData/viewerData', instance.data, viewerData);
const measurementApi = viewerData.measurementApi;
const measurementTypeId = 'nonTargets';
const measurementData = instance.data.measurementData;
const collection = measurementApi[measurementTypeId];
// Get the current inserted measurement from the collection
const currentMeasurement = collection.findOne({
_id: measurementData._id
});
// Synchronize the measurement number with the one inserted in the collection
measurementData.measurementNumber = currentMeasurement.measurementNumber;
// Delete the measurement if dialog is closed
instance.data.promise.catch(() => {
viewerData.measurementApi.deleteMeasurements('nonTargets', {
_id: measurementData._id
});
// Repaint the images on all viewports without the removed measurements
_.each($('.imageViewerViewport'), element => cornerstone.updateImage(element));
});
// Update the location and response after confirming the dialog data
instance.data.promise.then(formData => {
collection.update({
_id: measurementData._id,
}, {
$set: {
location: formData.location,
response: formData.response
}
});
});
// Inject the measurement number in form data to enable the tool from getting the value
form.value({
measurementNumber: currentMeasurement.measurementNumber
});
});

View File

@ -1,3 +1,6 @@
import './dialog/nonTargetMeasurement.html';
import './dialog/nonTargetMeasurement.js';
import './optionsModal/optionsModal.html';
import './optionsModal/optionsModal.styl';
import './optionsModal/optionsModal.js';

View File

@ -68,11 +68,10 @@ function selectNonTargetResponse(responseCode) {
// If there already exists a measurement with this specific measurement number,
// related to the chosen location.
function getMeasurementLocationCallback(measurementData, eventData) {
return 'Test Location';
Template.nonTargetMeasurementDialog.measurementData = measurementData;
// Get the non-target measurement location dialog
var dialog = $('#nonTargetMeasurementLocationDialog');
var dialog = $('#nonTargetMeasurementDialog');
Template.nonTargetMeasurementDialog.dialog = dialog;
// Show the backdrop
@ -89,7 +88,7 @@ function getMeasurementLocationCallback(measurementData, eventData) {
selectorLocation.find('option:first').prop('selected', 'selected');
// LT-112 "Non-target response shall default to non-measurable on baseline, present on follow-up"
var timepoint = Timepoints.findOne({
var timepoint = StudyList.timepointApi.timepoints.findOne({
timepointId: measurementData.timepointId
});
@ -104,7 +103,7 @@ function getMeasurementLocationCallback(measurementData, eventData) {
// Find out if this measurement number is already added in the measurement manager for another timepoint
// If it is, disable selector location
var locationId = OHIF.measurements.MeasurementManager.getLocationIdIfMeasurementExists(measurementData);
var locationId = OHIF.measurements.MeasurementManager.getLocationIdIfMeasurementExists(measurementData, StudyList.timepointApi.timepoints);
if (locationId) {
// Add an ID value to the tool data to link it to the Measurements collection
measurementData.id = 'notready';
@ -156,6 +155,7 @@ function getMeasurementLocationCallback(measurementData, eventData) {
dialogProperty.margin = 'auto';
}
console.warn('>>>>SHOW DIALOG', dialog);
dialog.css(dialogProperty);
}

View File

@ -90,18 +90,16 @@ class MeasurementApi {
});
}
deleteMeasurements(measurementTypeId, toolType, measurementNumber) {
deleteMeasurements(measurementTypeId, filter) {
const collection = this[measurementTypeId];
const filter = {
toolType,
measurementNumber
};
// Get the entries information before removing them
const entries = collection.find(filter).fetch();
collection.remove(filter);
// If the filter doesn't have the measurement number, get it from the first entry
const measurementNumber = filter.measurementNumber || entries[0].measurementNumber;
// Synchronize the new data with cornerstone tools
const toolState = cornerstoneTools.globalImageIdSpecificToolStateManager.toolState;
_.each(entries, entry => {

View File

@ -58,7 +58,10 @@ Template.measurementTableRow.events({
const api = instance.data.measurementApi;
// Remove all the measurements with the given type and number
api.deleteMeasurements(measurementTypeId, toolType, measurementNumber);
api.deleteMeasurements(measurementTypeId, {
toolType,
measurementNumber
});
// Repaint the images on all viewports without the removed measurements
_.each($('.imageViewerViewport'), element => cornerstone.updateImage(element));