Added persistent single-image measurments (length, ellipse) (LT-37, LT-156)

This commit is contained in:
Erik Ziegler committed 2016-02-14 13:36:44 +01:00
1 parent b624d12762
commit 36ff46be41
20 files changed
+227 -34

No files matched your search

@@ -1,5 +1,6 @@
Timepoints = new Meteor.Collection('timepoints');
Studies = new Meteor.Collection('studies');
Measurements = new Meteor.Collection('measurements');
ImageMeasurements = new Meteor.Collection('imageMeasurements');
WorklistSubscriptions = ['studies', 'timepoints'];
@@ -99,7 +99,8 @@
longestDiameter: 0,
shortestDiameter: 0,
isDeleted: false,
isTarget: true
isTarget: true,
measurementType: 'bidirectional'
};
return measurementData;
}
@@ -153,7 +153,8 @@
studyInstanceUid: studyInstanceUid,
patientId: patientId,
response: '',
isTarget: false
isTarget: false,
measurementType: 'nonTarget'
};
return measurementData;
@@ -1,7 +1,14 @@
Template.lesionTable.helpers({
measurement: function() {
// All Targets shall be listed first followed by Non-Targets
return Measurements.find({}, {
return Measurements.find({
measurementType: {
$in: [
'bidirectional',
'nonTarget'
]
}
}, {
sort: {
isTarget: -1,
lesionNumberAbsolute: 1
+24 -5
View File
@@ -1,3 +1,17 @@
measurementValuesByType = {
bidirectional: [
'shortestDiameter',
'longestDiameter'
],
nonTarget: ['response'],
length: ['length'],
ellipticalRoi: [
'area',
'mean',
'stdev'
]
};
/**
* Update the Timepoint object for a specific Measurement.
* If no measurement exists yet, one will be created.
@@ -46,13 +60,18 @@ function updateLesionData(lesionData) {
imageId: lesionData.imageId
};
if (lesionData.isTarget === true) {
timepointData.shortestDiameter = lesionData.shortestDiameter;
timepointData.longestDiameter = lesionData.longestDiameter;
} else {
timepointData.response = lesionData.response;
if (!lesionData.measurementType) {
// For debugging
log.warn('No MeasurementType available?');
}
// Populate this timepoint's data with whichever values
// are stored for this Measurement type
var values = measurementValuesByType[lesionData.measurementType];
values.forEach(function(valueName) {
timepointData[valueName] = lesionData[valueName];
});
// If no such lesion exists, we need to add one
if (!existingMeasurement) {
// Create a data structure for the Measurement
@@ -233,7 +233,6 @@ function validateSingleMeasurement(measurementData) {
// If no such Measurement exists, stop here
if (!measurement) {
log.warn('No Measurement found?');
return;
}
+2 -1
View File
@@ -1,6 +1,6 @@
clearTools = function() {
var patientId = Session.get('patientId');
var toolTypes = [ 'lesion', 'nonTarget', 'length' ];
var toolTypes = [ 'lesion', 'nonTarget', 'length', 'ellipticalRoi'];
var toolState = cornerstoneTools.globalImageIdSpecificToolStateManager.toolState;
var toolStateKeys = Object.keys(toolState).slice(0);
@@ -43,6 +43,7 @@ clearTools = function() {
// Remove patient's measurements
Meteor.call('removeMeasurementsByPatientId', patientId);
Meteor.call('removeImageMeasurementsByPatientId', patientId);
// Clear all validation errors
ValidationErrors.remove({});
@@ -7,6 +7,27 @@ handleMeasurementAdded = function(e, eventData) {
log.info('CornerstoneToolsMeasurementAdded');
LesionManager.updateLesionData(measurementData);
TrialResponseCriteria.validateDelayed(measurementData);
break;
case 'ellipticalRoi':
case 'length':
var enabledElement = cornerstone.getEnabledElement(eventData.element);
var imageId = enabledElement.image.imageId;
// Get the studyInstanceUid and series metaData
var study = cornerstoneTools.metaData.get('study', imageId);
var series = cornerstoneTools.metaData.get('series', imageId);
// Add the relevant metaData to this ImageMeasurement's toolData
measurementData.clientId = ClientId;
measurementData.imageId = imageId;
measurementData.measurementType = eventData.toolType;
measurementData.patientId = study.patientId;
measurementData.studyInstanceUid = study.studyInstanceUid;
measurementData.seriesInstanceUid = series.seriesInstanceUid;
// Create the ImageMeasurement in the database
measurementData._id = ImageMeasurements.insert(measurementData);
break;
}
};
@@ -8,5 +8,31 @@ handleMeasurementModified = function(e, eventData) {
LesionManager.updateLesionData(measurementData);
TrialResponseCriteria.validateDelayed(measurementData);
break;
case 'ellipticalRoi':
case 'length':
var enabledElement = cornerstone.getEnabledElement(eventData.element);
var imageId = enabledElement.image.imageId;
// Get the studyInstanceUid and series metaData
var study = cornerstoneTools.metaData.get('study', imageId);
var series = cornerstoneTools.metaData.get('series', imageId);
// Add the relevant metaData to this ImageMeasurement's toolData
measurementData.clientId = ClientId;
measurementData.imageId = imageId;
measurementData.measurementType = eventData.toolType;
measurementData.patientId = study.patientId;
measurementData.studyInstanceUid = study.studyInstanceUid;
measurementData.seriesInstanceUid = series.seriesInstanceUid;
var toUpdate = $.extend({}, measurementData);
// Remove the Mongo _id otherwise it will complain when we try to 'mod' it.
delete toUpdate._id;
// Update the ImageMeasurement in the database
ImageMeasurements.update(measurementData._id, {
$set: toUpdate
});
break;
}
};
@@ -15,13 +15,14 @@ removeToolDataWithMeasurementId = function(imageId, toolType, measurementId) {
// Search toolData for entries linked to the specified Measurement
var toRemove = [];
toolData.forEach(function(measurement, index) {
if (measurement.id === measurementId) {
if (measurement.id === measurementId ||
measurement._id === measurementId) {
toRemove.push(index);
return false;
}
});
log.info("Removing Indices: ");
log.info('Removing Indices: ');
log.info(toRemove);
// If any toolData entries need to be removed, splice them from
@@ -0,0 +1,59 @@
syncImageMeasurementAndToolData = function(measurement) {
// TODO: Refactor this to merge it with syncMeasurementAndToolData somehow
log.info('syncImageMeasurementAndToolData');
var toolState = cornerstoneTools.globalImageIdSpecificToolStateManager.toolState;
var imageId = measurement.imageId;
var toolType = measurement.measurementType;
// If no tool state exists for this imageId, create an empty object to store it
if (!toolState[imageId]) {
toolState[imageId] = {};
}
// Check if we already have toolData for this imageId and toolType
if (toolState[imageId][toolType] &&
toolState[imageId][toolType].data &&
toolState[imageId][toolType].data.length) {
// If we have toolData, we should search it for any toolData
// related to the current ImageMeasurement
var toolData = toolState[imageId][toolType].data;
// Create a flag so we know if we have successfully updated
// this ImageMeasurement's timepoint data in the toolData
var alreadyExists = false;
// Loop through the toolData to search for this ImageMeasurement
toolData.forEach(function(tool) {
// Break the loop if this isn't the ImageMeasurement we are looking for
if (tool._id !== measurement._id) {
return;
}
// If we find the ImageMeasurement, set the flag to True
alreadyExists = true;
// Update the toolData from the ImageMeasurement data
$.extend(tool, measurement);
return false;
});
// If we found the ImageMeasurement we intended to update, we can stop
// this function here
if (alreadyExists === true) {
return;
}
} else {
// If no toolData exists for this toolType, create an empty array to hold some
toolState[imageId][toolType] = {
data: []
};
}
// If we have reached this point, it means we haven't found the ImageMeasurement we are
// looking for in the current toolData. This means we need to add it.
// Add the ImageMeasurementData into the toolData for this imageId
toolState[imageId][toolType].data.push(measurement);
};
+3
View File
@@ -146,6 +146,7 @@ Package.onUse(function(api) {
api.addFiles('lib/calculateTotalLesionBurden.js', 'client');
api.addFiles('lib/syncMeasurementAndToolData.js', 'client');
api.addFiles('lib/syncImageMeasurementAndToolData.js', 'client');
api.addFiles('lib/updateRelatedElements.js', 'client');
api.addFiles('lib/handleMeasurementAdded.js', 'client');
@@ -159,6 +160,7 @@ Package.onUse(function(api) {
api.export('handleMeasurementModified', 'client');
api.export('handleMeasurementRemoved', 'client');
api.export('syncMeasurementAndToolData', 'client');
api.export('syncImageMeasurementAndToolData', 'client');
api.export('updateRelatedElements', 'client');
api.export('openNewTabWithTimepoint', 'client');
api.export('activateLesion', 'client');
@@ -184,6 +186,7 @@ Package.onUse(function(api) {
api.export('LocationResponses', 'client');
// Export collections spanning both client and server
api.export('ImageMeasurements', [ 'client', 'server' ]);
api.export('Measurements', [ 'client', 'server' ]);
api.export('Studies', [ 'client', 'server' ]);
api.export('Timepoints', [ 'client', 'server' ]);
@@ -24,6 +24,12 @@ Meteor.publish('singlePatientMeasurements', function(patientId) {
});
});
Meteor.publish('singlePatientImageMeasurements', function(patientId) {
return ImageMeasurements.find({
patientId: patientId
});
});
// Temporary fix to drop all Collections on server restart
// http://stackoverflow.com/questions/23891631/meteor-how-can-i-drop-all-mongo-collections-and-clear-all-data-on-startup
Meteor.startup(function() {
@@ -44,6 +44,11 @@ Meteor.methods({
patientId: patientId
});
},
removeImageMeasurementsByPatientId: function(patientId) {
ImageMeasurements.remove({
patientId: patientId
});
},
clearAllTimepoints: function() {
Timepoints.remove({});
},