Added data persistence for lesion measurements and timepoints

This commit is contained in:
Erik Ziegler 2015-11-19 21:11:41 +01:00
parent 1facbfb0b1
commit 0af0069d69
15 changed files with 157 additions and 73 deletions

View File

@ -16,7 +16,6 @@ standard-minifiers # JS/CSS minifiers run for production mode
es5-shim # ECMAScript 5 compatibility for older browsers.
ecmascript # Enable ECMAScript2015+ syntax in app code
autopublish # Publish all data to the clients (for prototyping)
insecure # Allow all DB writes from clients (for prototyping)
cornerstone
worklist

View File

@ -2,7 +2,6 @@ accounts-base@1.2.2
accounts-password@1.1.4
anti:i18n@0.4.3
arsnebula:reactive-promise@0.9.1
autopublish@1.0.4
autoupdate@1.2.4
babel-compiler@5.8.24_1
babel-runtime@0.1.4

View File

@ -16,12 +16,8 @@ function resizeViewports() {
}, 1);
}
Template.viewer.helpers({
'studyDateIsShown':function(){
return {studyDateIsShown: true};
}
});
Template.viewer.onCreated(function() {
var self = this;
log.info("viewer onCreated");
OHIF = {
@ -88,6 +84,7 @@ Template.viewer.onCreated(function() {
Session.set('activeViewport', ViewerData[contentId].activeViewport || 0);
// Update the ViewerStudies collection with the loaded studies
ViewerStudies = new Meteor.Collection(null);
this.data.studies.forEach(function(study) {
@ -95,9 +92,53 @@ Template.viewer.onCreated(function() {
ViewerStudies.insert(study);
});
var patientId = this.data.studies[0].patientId;
Session.set('patientId', patientId);
timepointsAdded = false;
self.autorun(function() {
var patientId = Session.get('patientId');
self.subscribe('timepoints', patientId);
self.subscribe('measurements', patientId);
if (!self.subscriptionsReady()) {
return;
}
if (timepointsAdded === true) {
return;
}
ViewerStudies.find().forEach(function(study) {
var timepoint = Timepoints.findOne({timepointName: study.studyDate});
if (timepoint) {
log.warn("A timepoint with that study date already exists!");
return;
}
var timepointID = uuid.v4();
var testTimepoint = Timepoints.findOne({});
if (testTimepoint && testTimepoint.patientId !== study.patientId) {
log.warn("Timepoints collection related to the wrong subject");
return;
}
log.info('Inserting a new timepoint');
Timepoints.insert({
patientId: study.patientId,
timepointID: timepointID,
timepointName: study.studyDate
});
});
timepointsAdded = true;
});
OHIF.viewer.updateImageSynchronizer = new cornerstoneTools.Synchronizer("CornerstoneNewImage", cornerstoneTools.updateImageSynchronizer);
});
Template.viewer.onDestroyed(function() {
log.info("onDestroyed");
OHIF.viewer.updateImageSynchronizer.destroy();

View File

@ -0,0 +1,2 @@
Timepoints = new Meteor.Collection('timepoints');
Measurements = new Meteor.Collection('measurements');

View File

@ -1,5 +1,4 @@
var activeLesionMeasurementData;
var timepointID;
var cornerstoneTools = (function($, cornerstone, cornerstoneMath, cornerstoneTools) {
"use strict";
@ -40,7 +39,7 @@ var cornerstoneTools = (function($, cornerstone, cornerstoneMath, cornerstoneToo
var measurementData = createNewMeasurement(mouseEventData);
var eventData = {
mouseButtonMask: mouseEventData.which,
mouseButtonMask: mouseEventData.which
};
// associate this data with this imageId so we can render it and manipulate it
@ -188,10 +187,6 @@ var cornerstoneTools = (function($, cornerstone, cornerstoneMath, cornerstoneToo
return;
}
updateLesions(toolData, eventData);
}
function updateLesions(toolData, eventData) {
// we have tool data for this element - iterate over each one and draw it
var context = eventData.canvasContext.canvas.getContext('2d');
context.setTransform(1, 0, 0, 1, 0, 0);

View File

@ -9,6 +9,10 @@ var measurementManagerDAL = (function() {
// Add timepoint data to Measurements collection
function addTimepointData(lesionData) {
var timepoints = Timepoints.find().fetch();
var study = cornerstoneTools.metaData.get('study', lesionData.imageId);
var series = cornerstoneTools.metaData.get('study', lesionData.imageId);
var timepointsObject = {};
for (var i = 0; i < timepoints.length; i++) {
@ -17,10 +21,12 @@ var measurementManagerDAL = (function() {
var timepointObject;
if (timepointId === lesionTimepointId) {
// Add real mesurement
// Add real measurement
timepointObject = {
longestDiameter: lesionData.measurementText,
imageId: lesionData.imageId,
studyInstanceUid: study.instanceUid,
seriesInstanceUid: series.instanceUid
};
} else {
// Add null measurement
@ -33,6 +39,7 @@ var measurementManagerDAL = (function() {
}
var lesionDataObject = {
patientId: timepoints[0].patientId,
lesionUID: uuid.v4(),
number: Measurements.find().count() + 1,
lesionNumber: lesionData.lesionNumber,
@ -47,10 +54,10 @@ var measurementManagerDAL = (function() {
// Update timepoint data in Measurements collection
function updateTimepointData(lesionData) {
// Find the specific lesion to be updated
var measurement = Measurements.find({
var measurement = Measurements.findOne({
lesionNumber: lesionData.lesionNumber,
isTarget: lesionData.isTarget
}).fetch()[0];
});
// If no such lesion exists, stop here
if (!measurement) {
@ -65,10 +72,7 @@ var measurementManagerDAL = (function() {
timepoints[timepointID].longestDiameter = lesionData.measurementText;
timepoints[timepointID].imageId = lesionData.imageId;
Measurements.update({
lesionNumber: lesionData.lesionNumber,
isTarget: lesionData.isTarget
}, {
Measurements.update(measurement._id, {
$set: {
timepoints: timepoints
}
@ -81,13 +85,14 @@ var measurementManagerDAL = (function() {
lesionNumber: lesionData.lesionNumber,
isTarget: lesionData.isTarget
});
if (timepointData) {
return true;
}
return false;
}
// Adds new timepoint item to tiemepoints array
// Adds new timepoint item to timepoints array
function addLesionData(lesionData) {
if (hasTimepointData(lesionData)) {
// Update data
@ -115,6 +120,10 @@ var measurementManagerDAL = (function() {
var measurement = measurements[i];
var timepoints = measurement.timepoints;
if (!timepoints[timepointID]) {
return;
}
if (timepoints[timepointID].longestDiameter === '') {
return measurement.lesionNumber;
} else {
@ -130,10 +139,10 @@ var measurementManagerDAL = (function() {
return;
}
var measurement = Measurements.find({
var measurement = Measurements.findOne({
lesionNumber: lesionData.lesionNumber,
isTarget: lesionData.isTarget
}).fetch()[0];
});
return measurement.locationUID;
}

View File

@ -174,20 +174,6 @@ LesionLocations.insert({
description: ""
});
var lastAddedLesionData;
Template.lesionLocationDialog.onRendered(function() {
// Observe Measurements Collection Changes
Measurements.find().observe({
added: function(lesionData) {
lastAddedLesionData = lesionData;
},
changed: function(lesionData) {
console.log("lesionData has changed!");
}
});
});
Template.lesionLocationDialog.helpers({
'lesionLocations': function() {
return LesionLocations.find();

View File

@ -1,17 +1,20 @@
Measurements = new Meteor.Collection(null);
Timepoints = new Meteor.Collection(null);
// Activate selected lesions when lesion table row is clicked
function updateLesions(e) {
// lesionNumber of measurement = id of row
var lesionNumber = parseInt($(e.currentTarget).attr("id"), 10);
// TODO= Clarify this
var isTarget = $(e.currentTarget).find('td').eq(2).html().trim() === 'N'?false:true;
// Find data for specific lesion
var measurementData = Measurements.find({
var measurementData = Measurements.findOne({
lesionNumber: lesionNumber,
isTarget: isTarget
}).fetch()[0];
});
if (!measurementData) {
return;
}
var timepoints = measurementData.timepoints;
@ -19,7 +22,14 @@ function updateLesions(e) {
// Get the timepointID related to the image viewer viewport
// from the DOM itself. This will be changed later when a
// real association between viewports and timepoints is created.
var timepointID = $(element).data('timepointID');
var enabledElement = cornerstone.getEnabledElement(element);
var study = cornerstoneTools.metaData.get('study', enabledElement.image.imageId);
var timepoint = Timepoints.findOne({timepointName: study.date});
if (!timepoint) {
return;
}
var timepointID = timepoint.timepointID;
var timepointObject = timepoints[timepointID];
// Defines event data
@ -54,28 +64,6 @@ function updateLesions(e) {
});
}
Template.lesionTable.onRendered(function() {
// Observe ViewerStudies Collection Changes
// Note: This may not be the best place for this
ViewerStudies.find().observe({
added: function(study) {
log.info('ViewerStudies added to');
var timepointID = uuid.v4();
var timepoint = Timepoints.findOne({timepointName: study.studyDate});
if (timepoint) {
log.warn("A timepoint with that study date already exists!");
return;
}
Timepoints.insert({
timepointID: timepointID,
timepointName: study.studyDate
});
}
});
});
Template.lesionTable.helpers({
'measurement': function() {
return Measurements.find();

View File

@ -2,6 +2,12 @@ Template.lesionTableTimepointCell.helpers({
'longestDiameter': function() {
// Search Measurements by lesion and timepoint
var lesionData = Template.parentData(1);
if (!lesionData ||
!lesionData.timepoints ||
!lesionData.timepoints[this.timepointID]) {
return;
}
return lesionData.timepoints[this.timepointID].longestDiameter;
}
});

View File

@ -1,8 +1,16 @@
// This event sets lesion number for new lesion
function setLesionNumberCallback(measurementData, eventData, doneCallback) {
// Get the current element's timepointID from the study date metadata
var element = eventData.element;
var enabledElement = cornerstone.getEnabledElement(element);
var study = cornerstoneTools.metaData.get('study', enabledElement.image.imageId);
var timepoint = Timepoints.findOne({timepointName: study.date});
if (!timepoint) {
return;
}
measurementData.timepointID = timepoint.timepointID;
measurementData.timepointID = $(eventData.element).data('timepointID');
// Get a lesion number for this lesion, depending on whether or not the same lesion previously
// exists at a different timepoint
var lesionNumber = measurementManagerDAL.getNewLesionNumber(measurementData.timepointID, isTarget=false);
@ -16,7 +24,7 @@ function setLesionNumberCallback(measurementData, eventData, doneCallback) {
// If there already exists a lesion with this specific lesion number,
// related to the chosen location.
function getNonTargetLesionLocationCallback(measurementData, eventData, doneCallback) {
function getNonTargetLesionLocationCallback(measurementData, eventData) {
// Get the non-target lesion location dialog
var nonTargetlesionDialog = $("#nonTargetLesionLocationDialog");

View File

@ -83,6 +83,27 @@ Template.studyDateList.events({
// with the value True, and insert it into the ViewerStudies Collection
study.selected = true;
ViewerStudies.insert(study);
var timepointID = uuid.v4();
var timepoint = Timepoints.findOne({timepointName: study.studyDate});
if (timepoint) {
log.warn("A timepoint with that study date already exists!");
return;
}
var testTimepoint = Timepoints.findOne({});
if (testTimepoint && testTimepoint.patientId !== study.patientId) {
log.warn("Timepoints collection related to the wrong subject");
return;
}
log.info('Inserting a new timepoint');
Timepoints.insert({
patientId: study.patientId,
timepointID: timepointID,
timepointName: study.studyDate
});
});
}
});

View File

@ -0,0 +1,3 @@
// Create package logger using loglevel
// https://atmospherejs.com/spacejamio/loglevel
log = loglevel.createPackageLogger('lesiontracker', defaultLevel = 'info');

View File

@ -10,10 +10,13 @@ Package.onUse(function (api) {
api.use('standard-app-packages');
api.use('jquery');
api.use('stylus');
api.use('practicalmeteor:loglevel');
// Our custom package
api.use('cornerstone');
api.addFiles('log.js', ['client', 'server']);
api.addFiles('compatibility/lesionTool.js', 'client', {bare: true});
api.addFiles('compatibility/nonTargetTool.js', 'client', {bare: true});
api.addFiles('compatibility/measurementManagerDAL.js', 'client', {bare: true});
@ -43,14 +46,15 @@ Package.onUse(function (api) {
api.addFiles('components/studyDateList/studyDateList.styl', 'client');
api.addFiles('components/studyDateList/studyDateList.js', 'client');
// Server functions
api.addFiles('server/collections.js', 'server');
// Both client and server functions
api.addFiles('both/collections.js', ['client', 'server']);
// Library functions
api.addFiles('lib/uuid.js', 'client');
api.export('Measurements', 'client');
api.export('Timepoints', 'client');
api.export('Measurements', ['client', 'server']);
api.export('Timepoints', ['client', 'server']);
});

View File

@ -0,0 +1,24 @@
Meteor.publish('timepoints', function(patientId) {
console.log('Publish timepoints');
console.log('patientId ' + patientId);
return Timepoints.find({patientId: patientId});
});
Meteor.publish('measurements', function(patientId) {
console.log('Publish measurements');
console.log('patientId ' + patientId);
return Measurements.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(){
var globalObject=Meteor.isClient?window:global;
for(var property in globalObject){
var object=globalObject[property];
if(object instanceof Meteor.Collection){
object.remove({});
}
}
});

View File

@ -190,7 +190,6 @@ function thumbnailDragEndHandler(e, target) {
}
Template.thumbnailEntry.onRendered(function() {
console.log(this.data);
var entry = this.find('.thumbnailEntry');
$(entry).data('seriesInstanceUid', Template.parentData(0).seriesInstanceUid);
$(entry).data('studyInstanceUid', Template.parentData(1).studyInstanceUid);