Remove some subscription / reactivity / caching bugs and the unnecessary PatientLocations collection

This commit is contained in:
Erik Ziegler committed 2016-02-10 16:38:49 +01:00
1 parent 7d42456f57
commit 9cde04bcfe
43 files changed
+321 -234

No files matched your search

@@ -1,4 +1,5 @@
LesionLocations = new Meteor.Collection(null);
LesionLocations._debugName = 'LesionLocations';
var organGroups = [
'Abdominal/Chest Wall',
@@ -1,4 +1,5 @@
LocationResponses = new Meteor.Collection(null);
LocationResponses._debugName = 'LocationResponses';
LocationResponses.insert({
text: 'Complete response',
@@ -1 +0,0 @@
PatientLocations = new Meteor.Collection(null);
@@ -194,11 +194,10 @@
y: mouseEventData.currentPoints.image.y - 70,
pointNearHandle: pointNearTextBox,
active: false,
movesIndependently: true,
movesIndependently: false,
drawnIndependently: true,
allowedOutsideImage: true
},
perpendicularStart: {
x: mouseEventData.currentPoints.image.x,
y: mouseEventData.currentPoints.image.y,
@@ -208,7 +207,6 @@
drawnIndependently: true,
index: 2
},
perpendicularEnd: {
x: mouseEventData.currentPoints.image.x,
y: mouseEventData.currentPoints.image.y,
@@ -217,7 +215,6 @@
drawnIndependently: true,
index: 3
}
},
imageId: imageId,
seriesInstanceUid: seriesInstanceUid,
@@ -141,7 +141,7 @@
y: mouseEventData.currentPoints.image.y - 50,
pointNearHandle: pointNearTextBox,
active: false,
movesIndependently: true,
movesIndependently: false,
drawnIndependently: true,
allowedOutsideImage: true
}
@@ -59,18 +59,53 @@ Template.associationModal.events({
// Sort the study dates, so we can get a range for these values
studyDates = studyDates.sort();
// Create a new timepoint to represent the (baseline or follow-up) studies
var timepoint = {
timepointType: timepointType,
timepointId: uuid.new(),
studyInstanceUids: studyInstanceUids,
patientId: relatedStudies[0].patientId, // TODO: Revisit this (Should timepoints be related to patientId?)
earliestDate: studyDates[0].format('YYYYMMDD'),
latestDate: studyDates[studyDates.length - 1].format('YYYYMMDD')
};
// Check if these studies are already associated with an existing Timepoint
var existingTimepoint;
if (timepointType === 'baseline') {
// If we're trying to associate them to the Baseline, we don't need to
// check if the studyInstanceUids are already associated with anything else
existingTimepoint = Timepoints.findOne({
patientId: relatedStudies[0].patientId,
timepointType: 'baseline'
});
} else {
// If we're trying to associate them to a Follow-up, we should check if any
// of them are already part of a Follow-up (e.g. Follow-up 1), so that
// the rest will also be associated with Follow-up 1.
existingTimepoint = Timepoints.findOne({
patientId: relatedStudies[0].patientId,
studyInstanceUids: {
$in: studyInstanceUids
}
});
}
// Insert this timepoint into the Timepoints Collection
Timepoints.insert(timepoint);
var timepointId;
if (existingTimepoint) {
// If these studies are already associated with an existing Timepoint,
// and the desired timepoint type is the same (e.g. Follow-up), update
// this Timepoint instead of creating a new one
Timepoints.update(existingTimepoint._id, {
$set: {
studyInstanceUids: studyInstanceUids
}
});
timepointId = existingTimepoint.timepointId;
} else {
// Create a new timepoint to represent the (baseline or follow-up) studies
var timepoint = {
timepointType: timepointType,
timepointId: uuid.new(),
studyInstanceUids: studyInstanceUids,
patientId: relatedStudies[0].patientId,
earliestDate: studyDates[0].format('YYYYMMDD'),
latestDate: studyDates[studyDates.length - 1].format('YYYYMMDD')
};
// Insert this timepoint into the Timepoints Collection
Timepoints.insert(timepoint);
timepointId = timepoint.timepointId;
}
// Loop through these studies to associate them with the newly created timepoint
relatedStudies.forEach(function(study) {
@@ -83,7 +118,7 @@ Template.associationModal.events({
// If a study already exists, update the entry with the new timepointId
Studies.update(existingStudy._id, {
$set: {
timepointId: timepoint.timepointId
timepointId: timepointId
}
});
} else {
@@ -93,7 +128,7 @@ Template.associationModal.events({
delete study._id;
// Attach the timepointId and insert it into the Studies Collection
study.timepointId = timepoint.timepointId;
study.timepointId = timepointId;
Studies.insert(study);
}
});
@@ -205,20 +205,6 @@ Template.lesionLocationDialog.events({
_id: selectedOptionId
});
var id;
var existingLocation = PatientLocations.findOne({
location: locationObj.location
});
if (existingLocation) {
id = existingLocation._id;
} else {
// Adds location data to PatientLocation and retrieve the location ID
id = PatientLocations.insert({
location: locationObj.location,
locationId: locationObj._id
});
}
// Add an ID value to the tool data to link it to the Measurements collection
if (!measurementData.id) {
measurementData.id = 'notready';
@@ -226,7 +212,6 @@ Template.lesionLocationDialog.events({
// Link locationUID with active lesion measurementData
measurementData.location = locationObj.location;
measurementData.locationId = locationObj.id;
measurementData.locationUID = id;
/// Set the isTarget value to true, since this is the target-lesion dialog callback
measurementData.isTarget = true;
@@ -241,8 +226,7 @@ Template.lesionLocationDialog.events({
$set: {
location: locationObj.location,
locationId: locationObj.id,
isNodal: locationObj.isNodal,
locationUID: id
isNodal: locationObj.isNodal
}
});
}
@@ -31,10 +31,10 @@ function removeTimepointAssociations() {
return;
}
// Update the Studies Collection to remove the link to this Timepoint
Studies.update(study._id, {
unset: {
timepointId: ''
// Remove this entry from the Studies Collection
Meteor.call('removeAssociatedStudy', study._id, function(error) {
if (error) {
log.warn(error);
}
});
@@ -70,27 +70,28 @@ function getLesionLocationCallback(measurementData, eventData) {
// Find out if this lesion number is already added in the lesion manager for another timepoint
// If it is, disable selector location
var locationUID = LesionManager.lesionNumberExists(measurementData);
if (locationUID) {
var locationId = LesionManager.lesionNumberExists(measurementData);
if (locationId) {
// Add an ID value to the tool data to link it to the Measurements collection
measurementData.id = 'notready';
measurementData.locationUID = locationUID;
measurementData.locationId = locationId;
// Disable the selection of a new location
disableLocationSelection(measurementData.locationUID);
disableLocationSelection(measurementData.locationId);
}
// Disable selector location to prevent selecting a new location
function disableLocationSelection(locationUID) {
var locationName = LesionManager.getLocationName(locationUID);
selectorLocation.find('option').each(function() {
if ($(this).text() === locationName) {
// Select location in locations dropdown list
selectorLocation.find('option').eq($(this).index()).prop('selected', true);
}
});
function disableLocationSelection(locationId) {
var locationObject = LesionLocations.findOne({
id: locationId
});
if (!locationObject) {
return;
}
selectorLocation.find('option[value="' + locationObject._id + '"]').prop('selected', true);
selectorLocation.prop('disabled', true);
}
@@ -273,26 +274,12 @@ Template.nonTargetLesionDialog.events({
_id: selectedOptionId
});
var id;
var existingLocation = PatientLocations.findOne({
location: locationObj.location
});
if (existingLocation) {
id = existingLocation._id;
} else {
// Adds location data to PatientLocation and retrieve the location ID
id = PatientLocations.insert({
location: locationObj.location
});
}
if (measurementData.id) {
// Update the location data
Measurements.update(measurementData.id, {
$set: {
location: locationObj.location,
locationId: locationObj.id,
locationUID: id
}
});
} else {
@@ -300,9 +287,6 @@ Template.nonTargetLesionDialog.events({
measurementData.id = 'notready';
}
// Link locationUID with active lesion measurementData
measurementData.locationUID = id;
/// Set the isTarget value to true, since this is the target-lesion dialog callback
measurementData.isTarget = false;
@@ -76,12 +76,7 @@ Template.studyDateList.events({
var loadingIndicator = selectBox.siblings('.loading');
loadingIndicator.css('display', 'block');
Meteor.call('GetStudyMetadata', studyInstanceUid, function(error, study) {
if (error) {
log.warn(error);
return;
}
getStudyMetadata(studyInstanceUid, function(study) {
sortStudy(study);
// Hide the loading indicator
@@ -1,12 +0,0 @@
<template name="timepointTextDialog">
<div id="timepointTextDialog">
<div class="timepointDialogContentWrapper">
<div class="dialogContent">
<div class="timepointContent">
<input type="checkbox" id="checkBoxBaseline" value="ok">
<label for="checkBoxBaseline">Baseline</label>
</div>
</div>
</div>
</div>
</template>
@@ -1,25 +0,0 @@
#timepointTextDialog
display: none
position: absolute
z-index: 99
.timepointDialogContentWrapper
position: absolute
width: 100px
height: 30px
padding: 5px
background-color: rgba(255,255,255,1)
border-top-left-radius: 5px
border-top-right-radius: 5px
.dialogContent
margin: 0 auto
float: none
padding: 0
.timepointContent
font-size: 16px
#checkBoxBaseline
height: 16px
width: 16px
+3 -22
View File
@@ -1,20 +1,3 @@
/**
* Retrieve a location name (e.g. Liver Right) from the
* PatientLocations Collection by id, if it exists. Otherwise,
* return an empty string.
*
* @param id
* @returns {*|string}
*/
function getLocationName(id) {
var locationObject = PatientLocations.findOne(id);
if (!locationObject || !locationObject.location) {
return '';
}
return locationObject.location;
}
/**
* Update the Timepoint object for a specific Measurement.
* If no measurement exists yet, one will be created.
@@ -33,7 +16,6 @@ function updateLesionData(lesionData) {
return;
}
var timepoint = Timepoints.findOne({
timepointId: study.timepointId
});
@@ -84,7 +66,7 @@ function updateLesionData(lesionData) {
// Retrieve the location name given the locationUID
if (lesionData.locationUID !== undefined) {
var locationObj = PatientLocations.findOne({
var locationObj = LesionLocations.findOne({
locationUID: lesionData.locationUID
});
@@ -191,12 +173,11 @@ function lesionNumberExists(lesionData) {
return;
}
return measurement.locationUID;
return measurement.locationId;
}
LesionManager = {
updateLesionData: updateLesionData,
getNewLesionNumber: getNewLesionNumber,
lesionNumberExists: lesionNumberExists,
getLocationName: getLocationName
lesionNumberExists: lesionNumberExists
};
@@ -50,7 +50,7 @@ function RECIST(image) {
shortestDiameter: {
numericality: {
greaterThanOrEqualTo: 15,
message: '^Nodal target lesions must be >= %{count} mm short axis'
//message: '^Nodal target lesions must be >= %{count} mm short axis'
}
}
};
@@ -72,7 +72,7 @@ function RECIST(image) {
totalLesionBurden: {
numericality: {
greaterThanOrEqualTo: 2, // TODO: Check this, the value wasn't specified!
message: '^Total lesion burden (SPD target lesions + SPD new lesions) should be greater than %{count}.'
//message: '^Total lesion burden (SPD target lesions + SPD new lesions) should be greater than %{count}.'
}
}
};
@@ -126,7 +126,7 @@ function RECIST(image) {
longestDiameter: {
numericality: {
greaterThanOrEqualTo: 20,
message: '^Extranodal lesions must be >= %{count} mm on chest X-ray'
//message: '^Extranodal lesions must be >= %{count} mm on chest X-ray'
}
}
};
@@ -135,7 +135,7 @@ function RECIST(image) {
longestDiameter: {
numericality: {
greaterThanOrEqualTo: 10,
message: '^Extranodal target lesions must be >= %{count} mm long axis'
//message: '^Extranodal target lesions must be >= %{count} mm long axis'
}
}
};
@@ -340,6 +340,9 @@ getTrialCriteriaConstraints = function(criteriaType, imageId) {
// Otherwise, retrieve the series metaData to identify the modality of the image
var seriesMetaData = cornerstoneTools.metaData.get('series', imageId);
if (!seriesMetaData) {
return;
}
// TODO: Get the rest of the metaData that has already been loaded by Cornerstone
var image = {};
@@ -363,7 +366,6 @@ getTrialCriteriaConstraints = function(criteriaType, imageId) {
});
if (!study) {
log.warn('No study/timepoint association.');
return;
}
@@ -1,5 +1,6 @@
// Create a client-only Collection to store our Validation Errors
ValidationErrors = new Meteor.Collection(null);
ValidationErrors._debugName = 'ValidationErrors';
// Set Validate.js Library's default options
validate.options = {
@@ -216,8 +217,8 @@ function validateSingleMeasurement(measurementData) {
var criteriaType = Session.get('TrialResponseAssessmentCriteria');
var currentConstraints = getTrialCriteriaConstraints(criteriaType, measurementData.imageId);
// If we have no relevant constraints, stop here
if (!currentConstraints) {
log.warn('No relevant contraints could be applied');
return;
}
@@ -236,6 +237,11 @@ function validateSingleMeasurement(measurementData) {
// Find the specific measurement data for this Measurement at this Timepoint
var currentMeasurement = measurement.timepoints[timepointId];
// Return here if the measurement was removed during validation
if (!currentMeasurement) {
return;
}
// Include target and nodal flags on the timepoint-specific data so it is easier to validate
// TODO: Rethink what to pass to assessSingleMeasurement?
currentMeasurement.isTarget = measurement.isTarget;
@@ -256,10 +262,11 @@ function validateGroups() {
var criteriaType = Session.get('TrialResponseAssessmentCriteria');
Timepoints.find().forEach(function(timepoint) {
// TODO: Criteria for the specific image are retrieved from the general set of criteria.
// - The acquisitionSliceThickness, for example, may be pulled from the image metadata
// - The organ in question, e.g. Chest X-ray, may determine the exact specifications for the current trial criteria
// Criteria for the specific image are retrieved from the general set of criteria.
var currentConstraints = getTrialCriteriaConstraints(criteriaType);
if (!currentConstraints) {
return;
}
// Retrieve the current constraints which apply to the specific Timepoint type
// (e.g. baseline, followup) that this Measurement is being edited on.
@@ -289,12 +296,9 @@ function validateAll() {
currentMeasurement.lesionNumber = measurement.lesionNumber;
currentMeasurement._id = measurement._id;
// TODO: Criteria for the specific image are retrieved from the general set of criteria.
// - The acquisitionSliceThickness, for example, may be pulled from the image metadata
// - The organ in question, e.g. Chest X-ray, may determine the exact specifications for the current trial criteria
// Criteria for the specific image are retrieved from the general set of criteria.
var currentConstraints = getTrialCriteriaConstraints(criteriaType, currentMeasurement.imageId);
if (!currentConstraints) {
log.warn('No relevant contraints could be applied');
return;
}
@@ -12,6 +12,10 @@ activateMeasurements = function(element, measurementId, templateData, viewportIn
var timepointData = getTimepointObject(imageId);
var measurementData = Measurements.findOne(measurementId);
if (!timepointData) {
return;
}
var measurementAtTimepoint = measurementData.timepoints[timepointData.timepointId];
if (!measurementAtTimepoint) {
return;
+12 -4
View File
@@ -5,11 +5,15 @@ clearTools = function() {
var toolStateKeys = Object.keys(toolState).slice(0);
var viewportElements = $('.imageViewerViewport').not('.empty');
var seriesInstanceUIds = []; // Holds seriesInstanceUId of imageViewerViewport elements
var seriesInstanceUids = []; // Holds seriesInstanceUid of imageViewerViewport elements
viewportElements.each(function(index, element) {
var enabledElement = cornerstone.getEnabledElement(element);
var series = cornerstoneTools.metaData.get('series', enabledElement.image.imageId);
seriesInstanceUIds.push(series.seriesInstanceUid);
if (!series) {
return;
}
seriesInstanceUids.push(series.seriesInstanceUid);
});
// Set null array for toolState data found by imageId and toolType
@@ -18,8 +22,12 @@ clearTools = function() {
var toolTypeData = toolState[imageId][toolType];
if (toolTypeData && toolTypeData.data.length > 0) {
var series = cornerstoneTools.metaData.get('series', imageId);
// If seriesInstanceUid is found in seriesInstanceUIds, set toolState data as null
if (seriesInstanceUIds.indexOf(series.seriesInstanceUid) > -1) {
if (!series) {
return;
}
// If seriesInstanceUid is found in seriesInstanceUids, set toolState data as null
if (seriesInstanceUids.indexOf(series.seriesInstanceUid) > -1) {
toolState[imageId][toolType] = {
data: []
};
@@ -1,10 +1,10 @@
handleMeasurementAdded = function(e, eventData) {
log.info('CornerstoneToolsMeasurementAdded');
var measurementData = eventData.measurementData;
switch (eventData.toolType) {
case 'nonTarget':
case 'lesion':
log.info('CornerstoneToolsMeasurementAdded');
LesionManager.updateLesionData(measurementData);
TrialResponseCriteria.validateDelayed(measurementData);
break;
@@ -1,10 +1,10 @@
handleMeasurementModified = function(e, eventData) {
log.info('CornerstoneToolsMeasurementModified');
var measurementData = eventData.measurementData;
switch (eventData.toolType) {
case 'nonTarget':
case 'lesion':
log.info('CornerstoneToolsMeasurementModified');
LesionManager.updateLesionData(measurementData);
TrialResponseCriteria.validateDelayed(measurementData);
break;
@@ -62,7 +62,7 @@ syncMeasurementAndToolData = function(data) {
measurementData.isDeleted = data.isDeleted;
measurementData.location = data.location;
measurementData.locationUID = data.locationUID;
measurementData.patientId = patientId;
measurementData.patientId = data.patientId;
measurementData.visible = data.visible;
measurementData.active = data.active;
measurementData.uid = data.uid;
@@ -33,5 +33,8 @@ function dblClickOnStudy(data) {
return;
}
// Add the Timepoint name to the Patient name to create the tab title
title += ' ' + getTimepointName(timepoint);
openNewTabWithTimepoint(timepoint.timepointId, title);
}
-5
View File
@@ -29,7 +29,6 @@ Package.onUse(function(api) {
// Client-side collections
api.addFiles('client/collections/LesionLocations.js', 'client');
api.addFiles('client/collections/LocationResponses.js', 'client');
api.addFiles('client/collections/PatientLocations.js', 'client');
// Additional Custom Cornerstone Tools for Lesion Tracker
api.addFiles('client/compatibility/lesionTool.js', 'client', {
@@ -108,9 +107,6 @@ Package.onUse(function(api) {
api.addFiles('client/components/nonTargetResponseDialog/nonTargetResponseDialog.styl', 'client');
api.addFiles('client/components/nonTargetResponseDialog/nonTargetResponseDialog.js', 'client');
api.addFiles('client/components/timepointTextDialog/timepointTextDialog.html', 'client');
api.addFiles('client/components/timepointTextDialog/timepointTextDialog.styl', 'client');
api.addFiles('client/components/lesionTrackerWorklistStudy/lesionTrackerWorklistStudy.html', 'client');
api.addFiles('client/components/lesionTrackerWorklistStudy/lesionTrackerWorklistStudy.styl', 'client');
api.addFiles('client/components/lesionTrackerWorklistStudy/lesionTrackerWorklistStudy.js', 'client');
@@ -186,7 +182,6 @@ Package.onUse(function(api) {
api.export('ValidationErrors', 'client');
api.export('LesionLocations', 'client');
api.export('LocationResponses', 'client');
api.export('PatientLocations', 'client');
// Export collections spanning both client and server
api.export('Measurements', [ 'client', 'server' ]);
@@ -12,6 +12,12 @@ Meteor.publish('studies', function() {
return Studies.find();
});
Meteor.publish('singlePatientAssociatedStudies', function(patientId) {
return Studies.find({
patientId: patientId
});
});
Meteor.publish('singlePatientMeasurements', function(patientId) {
return Measurements.find({
patientId: patientId
@@ -49,5 +49,8 @@ Meteor.methods({
},
removeTimepoint: function(id) {
Timepoints.remove(id);
},
removeAssociatedStudy: function(id) {
Studies.remove(id);
}
});