Added data persistence for lesion measurements and timepoints
This commit is contained in:
parent
1facbfb0b1
commit
0af0069d69
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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();
|
||||
|
||||
2
Packages/lesiontracker/both/collections.js
Normal file
2
Packages/lesiontracker/both/collections.js
Normal file
@ -0,0 +1,2 @@
|
||||
Timepoints = new Meteor.Collection('timepoints');
|
||||
Measurements = new Meteor.Collection('measurements');
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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;
|
||||
}
|
||||
});
|
||||
@ -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");
|
||||
|
||||
@ -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
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
3
Packages/lesiontracker/log.js
Normal file
3
Packages/lesiontracker/log.js
Normal file
@ -0,0 +1,3 @@
|
||||
// Create package logger using loglevel
|
||||
// https://atmospherejs.com/spacejamio/loglevel
|
||||
log = loglevel.createPackageLogger('lesiontracker', defaultLevel = 'info');
|
||||
@ -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']);
|
||||
});
|
||||
24
Packages/lesiontracker/server/collections.js
Normal file
24
Packages/lesiontracker/server/collections.js
Normal 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({});
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -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);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user