Added persistent single-image measurments (length, ellipse) (LT-37, LT-156)
This commit is contained in:
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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
@@ -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({});
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user