LT-291: Re-implementing the Non-Target tool behavior
This commit is contained in:
1 parent
e348ff1d4d
commit
5d8a99dc5f
12 files changed
+195
-56
No files matched your search
@@ -3,7 +3,7 @@
|
|||||||
<div class="viewerDialogs">
|
<div class="viewerDialogs">
|
||||||
{{>confirmDeleteDialog}}
|
{{>confirmDeleteDialog}}
|
||||||
{{>measurementLocationDialog (clone this)}}
|
{{>measurementLocationDialog (clone this)}}
|
||||||
{{>nonTargetMeasurementDialog (clone this)}}
|
{{! >nonTargetMeasurementDialog (clone this)}}
|
||||||
{{>nonTargetResponseDialog (clone this)}}
|
{{>nonTargetResponseDialog (clone this)}}
|
||||||
{{>measurementTableHUD (clone this)}}
|
{{>measurementTableHUD (clone this)}}
|
||||||
{{>cineDialog}}
|
{{>cineDialog}}
|
||||||
|
|||||||
@@ -59,3 +59,7 @@ label.form-group
|
|||||||
|
|
||||||
.modal-header, .modal-footer
|
.modal-header, .modal-footer
|
||||||
theme('border-color', '$uiBorderColor')
|
theme('border-color', '$uiBorderColor')
|
||||||
|
|
||||||
|
button.close
|
||||||
|
theme('color', '$textSecondaryColor')
|
||||||
|
opacity: 1
|
||||||
@@ -4,18 +4,18 @@ import { MeasurementSchemaTypes } from 'meteor/ohif:measurements/both/schema/mea
|
|||||||
const CornerstoneHandleSchema = MeasurementSchemaTypes.CornerstoneHandleSchema;
|
const CornerstoneHandleSchema = MeasurementSchemaTypes.CornerstoneHandleSchema;
|
||||||
|
|
||||||
const NonTargetHandlesSchema = new SimpleSchema({
|
const NonTargetHandlesSchema = new SimpleSchema({
|
||||||
start: {
|
start: {
|
||||||
type: CornerstoneHandleSchema,
|
type: CornerstoneHandleSchema,
|
||||||
label: 'Start'
|
label: 'Start'
|
||||||
},
|
},
|
||||||
end: {
|
end: {
|
||||||
type: CornerstoneHandleSchema,
|
type: CornerstoneHandleSchema,
|
||||||
label: 'End'
|
label: 'End'
|
||||||
},
|
},
|
||||||
textBox: {
|
textBox: {
|
||||||
type: CornerstoneHandleSchema,
|
type: CornerstoneHandleSchema,
|
||||||
label: 'Text Box'
|
label: 'Text Box'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const NonTargetSchema = new SimpleSchema([MeasurementSchemaTypes.CornerstoneToolMeasurement, {
|
const NonTargetSchema = new SimpleSchema([MeasurementSchemaTypes.CornerstoneToolMeasurement, {
|
||||||
@@ -24,10 +24,15 @@ const NonTargetSchema = new SimpleSchema([MeasurementSchemaTypes.CornerstoneTool
|
|||||||
label: 'Handles'
|
label: 'Handles'
|
||||||
},
|
},
|
||||||
response: {
|
response: {
|
||||||
type: String,
|
type: String,
|
||||||
label: 'Response',
|
label: 'Response',
|
||||||
optional: true // Optional because it is added after initial drawing, via a callback
|
optional: true // Optional because it is added after initial drawing, via a callback
|
||||||
},
|
},
|
||||||
|
location: {
|
||||||
|
type: String,
|
||||||
|
label: 'Location',
|
||||||
|
optional: true
|
||||||
|
},
|
||||||
locationUid: {
|
locationUid: {
|
||||||
type: String,
|
type: String,
|
||||||
label: 'Location UID',
|
label: 'Location UID',
|
||||||
@@ -50,6 +55,6 @@ export const nonTarget = {
|
|||||||
measurementTableOptions: {
|
measurementTableOptions: {
|
||||||
displayFunction: displayFunction
|
displayFunction: displayFunction
|
||||||
},
|
},
|
||||||
includeInCaseProgress: true,
|
includeInCaseProgress: true,
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
@@ -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'
|
||||||
|
]
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './fields.js';
|
||||||
@@ -5,9 +5,8 @@
|
|||||||
var toolType = 'nonTarget';
|
var toolType = 'nonTarget';
|
||||||
|
|
||||||
var configuration = {
|
var configuration = {
|
||||||
setMeasurementNumberCallback: setMeasurementNumberCallback,
|
getMeasurementLocationCallback: getMeasurementLocationCallback,
|
||||||
getmeasurementLocationCallback: getmeasurementLocationCallback,
|
changeMeasurementLocationCallback: changeMeasurementLocationCallback,
|
||||||
changemeasurementLocationCallback: changemeasurementLocationCallback,
|
|
||||||
drawHandles: false,
|
drawHandles: false,
|
||||||
drawHandlesOnHover: true,
|
drawHandlesOnHover: true,
|
||||||
arrowFirst: true
|
arrowFirst: true
|
||||||
@@ -17,20 +16,20 @@
|
|||||||
var keys = {
|
var keys = {
|
||||||
ESC: 27
|
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
|
// Define a callback to get your text annotation
|
||||||
// This could be used, e.g. to open a modal
|
// This could be used, e.g. to open a modal
|
||||||
function getmeasurementLocationCallback(measurementData, eventData, doneCallback) {
|
function getMeasurementLocationCallback(measurementData, eventData, doneCallback) {
|
||||||
doneCallback(prompt('Enter your measurement location:'));
|
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:'));
|
doneCallback(prompt('Change your measurement location:'));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,11 +52,6 @@
|
|||||||
|
|
||||||
var config = cornerstoneTools.nonTarget.getConfiguration();
|
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
|
// associate this data with this imageId so we can render it and manipulate it
|
||||||
cornerstoneTools.addToolState(mouseEventData.element, toolType, measurementData);
|
cornerstoneTools.addToolState(mouseEventData.element, toolType, measurementData);
|
||||||
|
|
||||||
@@ -92,7 +86,7 @@
|
|||||||
// delete the measurement
|
// delete the measurement
|
||||||
cornerstoneTools.removeToolState(mouseEventData.element, toolType, measurementData);
|
cornerstoneTools.removeToolState(mouseEventData.element, toolType, measurementData);
|
||||||
} else {
|
} else {
|
||||||
config.getmeasurementLocationCallback(measurementData, mouseEventData, doneCallback);
|
config.getMeasurementLocationCallback(measurementData, mouseEventData, doneCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unbind the Esc keydown hook
|
// Unbind the Esc keydown hook
|
||||||
@@ -103,8 +97,6 @@
|
|||||||
$(element).on('CornerstoneToolsMouseDownActivate', eventData, cornerstoneTools.nonTarget.mouseDownActivateCallback);
|
$(element).on('CornerstoneToolsMouseDownActivate', eventData, cornerstoneTools.nonTarget.mouseDownActivateCallback);
|
||||||
$(element).on('CornerstoneToolsMouseDoubleClick', eventData, doubleClickCallback);
|
$(element).on('CornerstoneToolsMouseDoubleClick', eventData, doubleClickCallback);
|
||||||
|
|
||||||
$(element).off('keydown', cancelCallback);
|
|
||||||
|
|
||||||
cornerstone.updateImage(mouseEventData.element);
|
cornerstone.updateImage(mouseEventData.element);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -272,11 +264,6 @@
|
|||||||
$(element).off('CornerstoneToolsTap', cornerstoneTools.nonTargetTouch.tapCallback);
|
$(element).off('CornerstoneToolsTap', cornerstoneTools.nonTargetTouch.tapCallback);
|
||||||
var config = cornerstoneTools.nonTarget.getConfiguration();
|
var config = cornerstoneTools.nonTarget.getConfiguration();
|
||||||
|
|
||||||
// Set measurement number and measurement name
|
|
||||||
if (measurementData.measurementName === undefined) {
|
|
||||||
config.setMeasurementNumberCallback(measurementData, touchEventData, doneCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
cornerstone.updateImage(element);
|
cornerstone.updateImage(element);
|
||||||
|
|
||||||
cornerstoneTools.moveNewHandleTouch(touchEventData, toolType, measurementData, measurementData.handles.end, function() {
|
cornerstoneTools.moveNewHandleTouch(touchEventData, toolType, measurementData, measurementData.handles.end, function() {
|
||||||
@@ -287,7 +274,7 @@
|
|||||||
cornerstoneTools.removeToolState(element, toolType, measurementData);
|
cornerstoneTools.removeToolState(element, toolType, measurementData);
|
||||||
}
|
}
|
||||||
|
|
||||||
config.getmeasurementLocationCallback(measurementData, touchEventData, doneCallback);
|
config.getMeasurementLocationCallback(measurementData, touchEventData, doneCallback);
|
||||||
|
|
||||||
$(element).on('CornerstoneToolsTouchDrag', cornerstoneTools.nonTargetTouch.touchMoveHandle);
|
$(element).on('CornerstoneToolsTouchDrag', cornerstoneTools.nonTargetTouch.touchMoveHandle);
|
||||||
$(element).on('CornerstoneToolsDragStartActive', cornerstoneTools.nonTargetTouch.touchDownActivateCallback);
|
$(element).on('CornerstoneToolsDragStartActive', cornerstoneTools.nonTargetTouch.touchDownActivateCallback);
|
||||||
@@ -330,7 +317,7 @@
|
|||||||
data.active = true;
|
data.active = true;
|
||||||
cornerstone.updateImage(element);
|
cornerstone.updateImage(element);
|
||||||
// Allow relabelling via a callback
|
// Allow relabelling via a callback
|
||||||
config.changemeasurementLocationCallback(data, eventData, doneCallback);
|
config.changeMeasurementLocationCallback(data, eventData, doneCallback);
|
||||||
|
|
||||||
e.stopImmediatePropagation();
|
e.stopImmediatePropagation();
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -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
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
|
import './dialog/nonTargetMeasurement.html';
|
||||||
|
import './dialog/nonTargetMeasurement.js';
|
||||||
|
|
||||||
import './optionsModal/optionsModal.html';
|
import './optionsModal/optionsModal.html';
|
||||||
import './optionsModal/optionsModal.styl';
|
import './optionsModal/optionsModal.styl';
|
||||||
import './optionsModal/optionsModal.js';
|
import './optionsModal/optionsModal.js';
|
||||||
|
|||||||
+4
-4
@@ -68,11 +68,10 @@ function selectNonTargetResponse(responseCode) {
|
|||||||
// If there already exists a measurement with this specific measurement number,
|
// If there already exists a measurement with this specific measurement number,
|
||||||
// related to the chosen location.
|
// related to the chosen location.
|
||||||
function getMeasurementLocationCallback(measurementData, eventData) {
|
function getMeasurementLocationCallback(measurementData, eventData) {
|
||||||
return 'Test Location';
|
|
||||||
Template.nonTargetMeasurementDialog.measurementData = measurementData;
|
Template.nonTargetMeasurementDialog.measurementData = measurementData;
|
||||||
|
|
||||||
// Get the non-target measurement location dialog
|
// Get the non-target measurement location dialog
|
||||||
var dialog = $('#nonTargetMeasurementLocationDialog');
|
var dialog = $('#nonTargetMeasurementDialog');
|
||||||
Template.nonTargetMeasurementDialog.dialog = dialog;
|
Template.nonTargetMeasurementDialog.dialog = dialog;
|
||||||
|
|
||||||
// Show the backdrop
|
// Show the backdrop
|
||||||
@@ -89,7 +88,7 @@ function getMeasurementLocationCallback(measurementData, eventData) {
|
|||||||
selectorLocation.find('option:first').prop('selected', 'selected');
|
selectorLocation.find('option:first').prop('selected', 'selected');
|
||||||
|
|
||||||
// LT-112 "Non-target response shall default to non-measurable on baseline, present on follow-up"
|
// 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
|
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
|
// Find out if this measurement number is already added in the measurement manager for another timepoint
|
||||||
// If it is, disable selector location
|
// 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) {
|
if (locationId) {
|
||||||
// Add an ID value to the tool data to link it to the Measurements collection
|
// Add an ID value to the tool data to link it to the Measurements collection
|
||||||
measurementData.id = 'notready';
|
measurementData.id = 'notready';
|
||||||
@@ -156,6 +155,7 @@ function getMeasurementLocationCallback(measurementData, eventData) {
|
|||||||
dialogProperty.margin = 'auto';
|
dialogProperty.margin = 'auto';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.warn('>>>>SHOW DIALOG', dialog);
|
||||||
dialog.css(dialogProperty);
|
dialog.css(dialogProperty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -90,18 +90,16 @@ class MeasurementApi {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteMeasurements(measurementTypeId, toolType, measurementNumber) {
|
deleteMeasurements(measurementTypeId, filter) {
|
||||||
const collection = this[measurementTypeId];
|
const collection = this[measurementTypeId];
|
||||||
|
|
||||||
const filter = {
|
|
||||||
toolType,
|
|
||||||
measurementNumber
|
|
||||||
};
|
|
||||||
|
|
||||||
// Get the entries information before removing them
|
// Get the entries information before removing them
|
||||||
const entries = collection.find(filter).fetch();
|
const entries = collection.find(filter).fetch();
|
||||||
collection.remove(filter);
|
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
|
// Synchronize the new data with cornerstone tools
|
||||||
const toolState = cornerstoneTools.globalImageIdSpecificToolStateManager.toolState;
|
const toolState = cornerstoneTools.globalImageIdSpecificToolStateManager.toolState;
|
||||||
_.each(entries, entry => {
|
_.each(entries, entry => {
|
||||||
|
|||||||
+4
-1
@@ -58,7 +58,10 @@ Template.measurementTableRow.events({
|
|||||||
const api = instance.data.measurementApi;
|
const api = instance.data.measurementApi;
|
||||||
|
|
||||||
// Remove all the measurements with the given type and number
|
// 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
|
// Repaint the images on all viewports without the removed measurements
|
||||||
_.each($('.imageViewerViewport'), element => cornerstone.updateImage(element));
|
_.each($('.imageViewerViewport'), element => cornerstone.updateImage(element));
|
||||||
|
|||||||
Reference in new issue
Block a user