LT-117: Add warning icon in study list row and give tip about user when warning icon is hovered if study is in-use by another user

This commit is contained in:
Aysel Afsar 2016-04-04 00:00:33 -04:00
parent 064743a8f7
commit 2da841bab0
15 changed files with 206 additions and 8 deletions

View File

@ -16,11 +16,11 @@ Meteor.startup(function() {
qidoSupportsIncludeField: false,
imageRendering: 'wadouri',
requestOptions: {
auth: 'orthanc:orthanc',
logRequests: true,
logResponses: false,
logTiming: true
}
auth: 'orthanc:orthanc',
logRequests: true,
logResponses: false,
logTiming: true
}
}]
},
dimse: [{
@ -37,4 +37,15 @@ Meteor.startup(function() {
};
console.log('Using default LesionTracker settings with service: ' + Meteor.settings.defaultServiceType);
// Bind events if window is closed
$(window).bind('beforeunload', function (e) {
closingWindow();
// have to return null, unless you want a chrome popup alert
//return 'If you leave this page then any unsaved changes will be lost.';
});
});
closingWindow = function(){
Meteor.call('removeUserFromReviewers', Meteor.userId());
};

View File

@ -3,4 +3,5 @@ Studies = new Meteor.Collection('studies');
Measurements = new Meteor.Collection('measurements');
ImageMeasurements = new Meteor.Collection('imageMeasurements');
WorklistSubscriptions = ['studies', 'timepoints'];
WorklistSubscriptions = ['studies', 'timepoints'];
Reviewers = new Meteor.Collection('reviewers');

View File

@ -0,0 +1 @@
Meteor.subscribe('reviewers');

View File

@ -183,6 +183,14 @@ changeLesionLocationCallback = function(measurementData, eventData, doneCallback
selected: true
}
});
// Set reviewer for this timepoint
Object.keys(measurement.timepoints).forEach(function(key) {
if (measurement.timepoints[key].studyInstanceUid) {
Meteor.call('setReviewer',measurement.timepoints[key].studyInstanceUid);
}
});
};
var config = {

View File

@ -1,6 +1,9 @@
<template name="lesionTrackerWorklistStudy">
<tr class="worklistStudy noselect">
<td>
{{#if reviewerTip}}
<i class="fa fa-exclamation-circle reviewerTip" data-toggle="tooltip" data-placement="bottom" title="{{reviewerTip}}"></i>
{{/if}}
{{formatPN patientName}}
</td>
<td>
@ -27,5 +30,6 @@
{{numberOfStudyRelatedInstances}}
</td>
{{/unless}}
</tr>
</template>

View File

@ -26,5 +26,73 @@ Template[defaultTemplate].helpers({
}
return getTimepointName(timepoint);
},
reviewerTip: function() {
var data = this;
var study = Studies.findOne({
studyInstanceUid: data.studyInstanceUid
});
if (!study) {
return;
}
var timepoint = Timepoints.findOne({
timepointId: study.timepointId
});
if (!timepoint) {
return;
}
var timepointReviewers = Reviewers.findOne({timepointId: timepoint.timepointId});
if (!timepointReviewers) {
return;
}
return getReviewerTipText(timepointReviewers.reviewers);
}
});
function getReviewerTipText(reviewers) {
if (!reviewers || reviewers.length === 0) {
return;
}
var newReviewers = reviewers.filter(function ( reviewer ) {
return reviewer.userId !== Meteor.userId();
});
if (newReviewers.length === 0) {
return;
}
var tipText = 'The study is being reviewed by ';
newReviewers.forEach(function(reviewer, index) {
if (reviewer.userId === Meteor.userId()) {
return;
}
if (index > 0) {
tipText += ',';
}
tipText += reviewer.userName;
});
return tipText;
}
// Observe changes on WorklistTabs
WorklistTabs.find().observe({
removed: function(tab) {
var timepointId = tab.timepointId;
if (!timepointId) {
return;
}
// Remove the current user from Reviewers
Meteor.call('removeReviewer', timepointId);
}
});

View File

@ -1,2 +1,5 @@
.worklistStudy
cursor: pointer
cursor: pointer
.reviewerTip
color: darkred

View File

@ -8,6 +8,14 @@ clearTools = function() {
var seriesInstanceUids = []; // Holds seriesInstanceUid of imageViewerViewport elements
viewportElements.each(function(index, element) {
var enabledElement = cornerstone.getEnabledElement(element);
var study = cornerstoneTools.metaData.get('study', enabledElement.image.imageId);
if (!study) {
return;
}
// Set reviewer for this timepoint
if (study.studyInstanceUid) {
Meteor.call('setReviewer',study.studyInstanceUid);
}
var series = cornerstoneTools.metaData.get('series', enabledElement.image.imageId);
if (!series) {
return;

View File

@ -1,6 +1,5 @@
handleMeasurementAdded = function(e, eventData) {
var measurementData = eventData.measurementData;
switch (measurementData.toolType) {
case 'nonTarget':
case 'bidirectional':
@ -10,6 +9,10 @@ handleMeasurementAdded = function(e, eventData) {
log.info('CornerstoneToolsMeasurementAdded');
LesionManager.updateLesionData(measurementData);
TrialResponseCriteria.validateDelayed(measurementData);
// Set reviewer for this timepoint
if (measurementData.studyInstanceUid) {
Meteor.call('setReviewer',measurementData.studyInstanceUid);
}
break;
case 'ellipticalRoi':
case 'length':
@ -33,4 +36,5 @@ handleMeasurementAdded = function(e, eventData) {
break;
}
};

View File

@ -9,6 +9,10 @@ handleMeasurementModified = function(e, eventData) {
log.info('CornerstoneToolsMeasurementModified');
LesionManager.updateLesionData(measurementData);
TrialResponseCriteria.validateDelayed(measurementData);
// Set reviewer for this timepoint
if (measurementData.studyInstanceUid) {
Meteor.call('setReviewer',measurementData.studyInstanceUid);
}
break;
case 'ellipticalRoi':
case 'length':

View File

@ -16,6 +16,10 @@ handleMeasurementRemoved = function(e, eventData) {
if (!measurement) {
return;
}
// Set reviewer for this timepoint
if (measurementData.studyInstanceUid) {
Meteor.call('setReviewer',measurementData.studyInstanceUid);
}
clearMeasurementTimepointData(measurement._id, measurementData.timepointId);
break;

View File

@ -12,6 +12,11 @@ syncMeasurementAndToolData = function(measurement) {
// Sync the Cornerstone ToolData with this Measurement's timepoint-specific data
syncTimepointDataWithToolData(measurement, timepointData, imageId, toolType);
// Set reviewer for this timepoint
if (measurement.timepoints[key].studyInstanceUid) {
Meteor.call('setReviewer',measurement.timepoints[key].studyInstanceUid);
}
});
};

View File

@ -29,6 +29,8 @@ 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/subscriptions.js', 'client');
// Additional Custom Cornerstone Tools for Lesion Tracker
api.addFiles('client/compatibility/bidirectionalTool.js', 'client', {
@ -158,6 +160,7 @@ Package.onUse(function(api) {
// Server functions
api.addFiles('server/collections.js', 'server');
api.addFiles('server/removeCollections.js', [ 'server' ]);
api.addFiles('server/reviewers.js', [ 'server' ]);
// Both client and server functions
api.addFiles('both/collections.js', [ 'client', 'server' ]);
@ -233,4 +236,7 @@ Package.onUse(function(api) {
api.export('Measurements', [ 'client', 'server' ]);
api.export('Studies', [ 'client', 'server' ]);
api.export('Timepoints', [ 'client', 'server' ]);
api.export('Reviewers', [ 'client', 'server' ]);
});

View File

@ -30,6 +30,11 @@ Meteor.publish('singlePatientImageMeasurements', function(patientId) {
});
});
Meteor.publish('reviewers', function() {
return Reviewers.find();
});
// 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() {

View File

@ -0,0 +1,66 @@
Meteor.methods({
setReviewer: function (studyInstanceUid) {
var study = Studies.findOne({
studyInstanceUid: studyInstanceUid
});
if (!study) {
return;
}
var timepoint = Timepoints.findOne({
timepointId: study.timepointId
});
if (!timepoint) {
return;
}
var reviewerTimepoint = Reviewers.findOne({timepointId: timepoint.timepointId});
var user = Meteor.users.findOne(Meteor.userId());
if (reviewerTimepoint) {
if (reviewerTimepoint.reviewers) {
// Check whether the user exists
var isReviewerFound = reviewerTimepoint.reviewers.filter(function ( reviewer ) {
return reviewer.userId === user._id;
})[0];
// Return if user eixistes for related timepoint
if (isReviewerFound) {
return;
}
var existedReviewers = reviewerTimepoint.reviewers;
existedReviewers.push({userId: user._id, userName: user.profile.fullName});
// Update reviewers array after pushing the user
Reviewers.update(reviewerTimepoint._id, {$set: {reviewers: existedReviewers}});
} else {
Reviewers.update(reviewerTimepoint._id, {$set: {reviewers: [{userId: user._id, userName: user.profile.fullName}]}});
}
} else {
Reviewers.insert({timepointId: timepoint.timepointId, reviewers: [{userId: user._id, userName: user.profile.fullName}]});
}
},
removeReviewer: function (timepointId) {
var reviewerTimepoint = Reviewers.findOne({timepointId: timepointId});
if (!reviewerTimepoint || !reviewerTimepoint.reviewers) {
return;
}
// Initalize a new array without the current user
var reviewers = reviewerTimepoint.reviewers.filter(function ( reviewer ) {
return reviewer.userId !== Meteor.userId();
});
Reviewers.update(reviewerTimepoint._id, {$set: {reviewers: reviewers}});
},
removeUserFromReviewers: function(userId) {
Reviewers.find().map(function (timepoint) {
Reviewers.update({_id: timepoint._id}, {"$pull": {"reviewers": {"userId": userId}}});
});
}
});