diff --git a/LesionTracker/client/components/viewer/viewer.html b/LesionTracker/client/components/viewer/viewer.html index 574fc0b80..c4653644a 100644 --- a/LesionTracker/client/components/viewer/viewer.html +++ b/LesionTracker/client/components/viewer/viewer.html @@ -3,7 +3,7 @@
{{>confirmDeleteDialog}} {{>measurementLocationDialog (clone this)}} - {{>nonTargetMeasurementDialog (clone this)}} + {{! >nonTargetMeasurementDialog (clone this)}} {{>nonTargetResponseDialog (clone this)}} {{>measurementTableHUD (clone this)}} {{>cineDialog}} diff --git a/Packages/ohif-design/styles/common/global.styl b/Packages/ohif-design/styles/common/global.styl index afe263a0a..b5ff2d1bb 100644 --- a/Packages/ohif-design/styles/common/global.styl +++ b/Packages/ohif-design/styles/common/global.styl @@ -59,3 +59,7 @@ label.form-group .modal-header, .modal-footer theme('border-color', '$uiBorderColor') + + button.close + theme('color', '$textSecondaryColor') + opacity: 1 diff --git a/Packages/ohif-lesiontracker/both/configuration/nonTarget.js b/Packages/ohif-lesiontracker/both/configuration/nonTarget.js index 8de6c1a2a..24ab328ad 100644 --- a/Packages/ohif-lesiontracker/both/configuration/nonTarget.js +++ b/Packages/ohif-lesiontracker/both/configuration/nonTarget.js @@ -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, }, -} \ No newline at end of file +}; diff --git a/Packages/ohif-lesiontracker/both/schema/fields.js b/Packages/ohif-lesiontracker/both/schema/fields.js new file mode 100644 index 000000000..e9f6d5624 --- /dev/null +++ b/Packages/ohif-lesiontracker/both/schema/fields.js @@ -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' + ] +}; diff --git a/Packages/ohif-lesiontracker/both/schema/index.js b/Packages/ohif-lesiontracker/both/schema/index.js new file mode 100644 index 000000000..fe2407f77 --- /dev/null +++ b/Packages/ohif-lesiontracker/both/schema/index.js @@ -0,0 +1 @@ +export * from './fields.js'; diff --git a/Packages/ohif-lesiontracker/client/compatibility/nonTargetTool.js b/Packages/ohif-lesiontracker/client/compatibility/nonTargetTool.js index 808f132bb..9dfe2faa3 100644 --- a/Packages/ohif-lesiontracker/client/compatibility/nonTargetTool.js +++ b/Packages/ohif-lesiontracker/client/compatibility/nonTargetTool.js @@ -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; diff --git a/Packages/ohif-lesiontracker/client/components/dialog/nonTargetMeasurement.html b/Packages/ohif-lesiontracker/client/components/dialog/nonTargetMeasurement.html new file mode 100644 index 000000000..02dc1f7f2 --- /dev/null +++ b/Packages/ohif-lesiontracker/client/components/dialog/nonTargetMeasurement.html @@ -0,0 +1,9 @@ + diff --git a/Packages/ohif-lesiontracker/client/components/dialog/nonTargetMeasurement.js b/Packages/ohif-lesiontracker/client/components/dialog/nonTargetMeasurement.js new file mode 100644 index 000000000..799e13623 --- /dev/null +++ b/Packages/ohif-lesiontracker/client/components/dialog/nonTargetMeasurement.js @@ -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 + }); + +}); diff --git a/Packages/ohif-lesiontracker/client/components/index.js b/Packages/ohif-lesiontracker/client/components/index.js index 6c151dd94..c534fbb27 100644 --- a/Packages/ohif-lesiontracker/client/components/index.js +++ b/Packages/ohif-lesiontracker/client/components/index.js @@ -1,3 +1,6 @@ +import './dialog/nonTargetMeasurement.html'; +import './dialog/nonTargetMeasurement.js'; + import './optionsModal/optionsModal.html'; import './optionsModal/optionsModal.styl'; import './optionsModal/optionsModal.js'; diff --git a/Packages/ohif-lesiontracker/client/components/nonTargetMeasurementDialog/nonTargetMeasurementDialog.js b/Packages/ohif-lesiontracker/client/components/nonTargetMeasurementDialog/nonTargetMeasurementDialog.js index 347dc83ac..de2043668 100644 --- a/Packages/ohif-lesiontracker/client/components/nonTargetMeasurementDialog/nonTargetMeasurementDialog.js +++ b/Packages/ohif-lesiontracker/client/components/nonTargetMeasurementDialog/nonTargetMeasurementDialog.js @@ -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); } diff --git a/Packages/ohif-measurements/both/configuration/measurements.js b/Packages/ohif-measurements/both/configuration/measurements.js index f979e3d43..c8b9a7d07 100644 --- a/Packages/ohif-measurements/both/configuration/measurements.js +++ b/Packages/ohif-measurements/both/configuration/measurements.js @@ -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 => { diff --git a/Packages/ohif-measurements/client/components/measurementTable/measurementTableRow/measurementTableRow.js b/Packages/ohif-measurements/client/components/measurementTable/measurementTableRow/measurementTableRow.js index 850d7be4e..f664c665e 100644 --- a/Packages/ohif-measurements/client/components/measurementTable/measurementTableRow/measurementTableRow.js +++ b/Packages/ohif-measurements/client/components/measurementTable/measurementTableRow/measurementTableRow.js @@ -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));