diff --git a/LesionTracker/.meteor/packages b/LesionTracker/.meteor/packages index a236d1a0f..fe177b6c7 100644 --- a/LesionTracker/.meteor/packages +++ b/LesionTracker/.meteor/packages @@ -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 diff --git a/LesionTracker/.meteor/versions b/LesionTracker/.meteor/versions index 06060572a..51c778862 100644 --- a/LesionTracker/.meteor/versions +++ b/LesionTracker/.meteor/versions @@ -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 diff --git a/LesionTracker/client/components/viewer.js b/LesionTracker/client/components/viewer.js index db29d5d87..6d337bf6b 100644 --- a/LesionTracker/client/components/viewer.js +++ b/LesionTracker/client/components/viewer.js @@ -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(); diff --git a/Packages/lesiontracker/both/collections.js b/Packages/lesiontracker/both/collections.js new file mode 100644 index 000000000..46a6fa2b5 --- /dev/null +++ b/Packages/lesiontracker/both/collections.js @@ -0,0 +1,2 @@ +Timepoints = new Meteor.Collection('timepoints'); +Measurements = new Meteor.Collection('measurements'); \ No newline at end of file diff --git a/Packages/lesiontracker/compatibility/lesionTool.js b/Packages/lesiontracker/compatibility/lesionTool.js index fe7e91e63..a11e20c3b 100644 --- a/Packages/lesiontracker/compatibility/lesionTool.js +++ b/Packages/lesiontracker/compatibility/lesionTool.js @@ -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); diff --git a/Packages/lesiontracker/compatibility/measurementManagerDAL.js b/Packages/lesiontracker/compatibility/measurementManagerDAL.js index b438bbf21..2db7afa6c 100644 --- a/Packages/lesiontracker/compatibility/measurementManagerDAL.js +++ b/Packages/lesiontracker/compatibility/measurementManagerDAL.js @@ -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; } diff --git a/Packages/lesiontracker/components/lesionLocationDialog/lesionLocationDialog.js b/Packages/lesiontracker/components/lesionLocationDialog/lesionLocationDialog.js index 36ce42d48..03fcb9fe9 100644 --- a/Packages/lesiontracker/components/lesionLocationDialog/lesionLocationDialog.js +++ b/Packages/lesiontracker/components/lesionLocationDialog/lesionLocationDialog.js @@ -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(); diff --git a/Packages/lesiontracker/components/lesionTable/lesionTable.js b/Packages/lesiontracker/components/lesionTable/lesionTable.js index 765da6720..5c1867382 100644 --- a/Packages/lesiontracker/components/lesionTable/lesionTable.js +++ b/Packages/lesiontracker/components/lesionTable/lesionTable.js @@ -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(); diff --git a/Packages/lesiontracker/components/lesionTableTimepointCell/lesionTableTimepointCell.js b/Packages/lesiontracker/components/lesionTableTimepointCell/lesionTableTimepointCell.js index f8edf6ccc..8fe0674df 100644 --- a/Packages/lesiontracker/components/lesionTableTimepointCell/lesionTableTimepointCell.js +++ b/Packages/lesiontracker/components/lesionTableTimepointCell/lesionTableTimepointCell.js @@ -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; } }); \ No newline at end of file diff --git a/Packages/lesiontracker/components/nonTargetLesionDialog/nonTargetLesionDialog.js b/Packages/lesiontracker/components/nonTargetLesionDialog/nonTargetLesionDialog.js index 88e9ac408..bd93618e5 100644 --- a/Packages/lesiontracker/components/nonTargetLesionDialog/nonTargetLesionDialog.js +++ b/Packages/lesiontracker/components/nonTargetLesionDialog/nonTargetLesionDialog.js @@ -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"); diff --git a/Packages/lesiontracker/components/studyDateList/studyDateList.js b/Packages/lesiontracker/components/studyDateList/studyDateList.js index 1082aa6b9..8fc749560 100644 --- a/Packages/lesiontracker/components/studyDateList/studyDateList.js +++ b/Packages/lesiontracker/components/studyDateList/studyDateList.js @@ -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 + }); }); } }); diff --git a/Packages/lesiontracker/log.js b/Packages/lesiontracker/log.js new file mode 100644 index 000000000..259acb9ad --- /dev/null +++ b/Packages/lesiontracker/log.js @@ -0,0 +1,3 @@ +// Create package logger using loglevel +// https://atmospherejs.com/spacejamio/loglevel +log = loglevel.createPackageLogger('lesiontracker', defaultLevel = 'info'); \ No newline at end of file diff --git a/Packages/lesiontracker/package.js b/Packages/lesiontracker/package.js index a63e0afc0..5ac002a7b 100644 --- a/Packages/lesiontracker/package.js +++ b/Packages/lesiontracker/package.js @@ -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']); }); \ No newline at end of file diff --git a/Packages/lesiontracker/server/collections.js b/Packages/lesiontracker/server/collections.js new file mode 100644 index 000000000..fc61f6aa6 --- /dev/null +++ b/Packages/lesiontracker/server/collections.js @@ -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({}); + } + } +}); \ No newline at end of file diff --git a/Packages/viewerbase/components/studyBrowser/thumbnailEntry/thumbnailEntry.js b/Packages/viewerbase/components/studyBrowser/thumbnailEntry/thumbnailEntry.js index 7e7d96ae0..52927b6ad 100644 --- a/Packages/viewerbase/components/studyBrowser/thumbnailEntry/thumbnailEntry.js +++ b/Packages/viewerbase/components/studyBrowser/thumbnailEntry/thumbnailEntry.js @@ -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);