Building the viewer data inside routes
This commit is contained in:
parent
7718d31a77
commit
9ccebb76bd
@ -119,7 +119,7 @@ Template.viewer.onCreated(() => {
|
|||||||
instance.data.studies.forEach(study => (delete study.timepointType));
|
instance.data.studies.forEach(study => (delete study.timepointType));
|
||||||
|
|
||||||
// TODO: Consider combining the retrieval calls into one?
|
// TODO: Consider combining the retrieval calls into one?
|
||||||
const timepointsPromise = timepointApi.retrieveTimepoints(patientId);
|
const timepointsPromise = timepointApi.retrieveTimepoints({ patientId });
|
||||||
timepointsPromise.then(() => {
|
timepointsPromise.then(() => {
|
||||||
const timepoints = timepointApi.all();
|
const timepoints = timepointApi.all();
|
||||||
|
|
||||||
|
|||||||
@ -5,9 +5,6 @@ import { OHIF } from 'meteor/ohif:core';
|
|||||||
// TODO: remove the line below
|
// TODO: remove the line below
|
||||||
window.Router = Router;
|
window.Router = Router;
|
||||||
|
|
||||||
// verifyEmail controls whether emailVerification template will be rendered or not
|
|
||||||
const verifyEmail = Meteor.settings && Meteor.settings.public && Meteor.settings.public.verifyEmail || false;
|
|
||||||
|
|
||||||
Router.configure({
|
Router.configure({
|
||||||
layoutTemplate: 'layout',
|
layoutTemplate: 'layout',
|
||||||
loadingTemplate: 'layout'
|
loadingTemplate: 'layout'
|
||||||
@ -15,80 +12,37 @@ Router.configure({
|
|||||||
|
|
||||||
Router.onBeforeAction('loading');
|
Router.onBeforeAction('loading');
|
||||||
|
|
||||||
const data = {};
|
|
||||||
|
|
||||||
const routerOptions = { data };
|
|
||||||
|
|
||||||
Router.route('/', {
|
|
||||||
name: 'home',
|
|
||||||
onBeforeAction: function() {
|
|
||||||
// Check if user needs to verify its email
|
|
||||||
if (verifyEmail && Meteor.user().emails && !Meteor.user().emails[0].verified) {
|
|
||||||
this.render('emailVerification', routerOptions);
|
|
||||||
} else {
|
|
||||||
this.render('app', routerOptions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Router.route('/viewer/timepoints/:_id', {
|
|
||||||
layoutTemplate: 'layout',
|
|
||||||
name: 'viewerTimepoint',
|
|
||||||
onBeforeAction: function() {
|
|
||||||
const timepointId = this.params._id;
|
|
||||||
|
|
||||||
this.render('app', routerOptions);
|
|
||||||
OHIF.lesiontracker.openNewTabWithTimepoint(timepointId);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
OHIF.viewer.prepare = ({ studyInstanceUids, timepointId }) => {
|
|
||||||
// Clear the cornerstone tool data to sync the measurements with the measurements API
|
|
||||||
cornerstoneTools.globalImageIdSpecificToolStateManager = cornerstoneTools.newImageIdSpecificToolStateManager();
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
OHIF.studylist.retrieveStudiesMetadata(studyInstanceUids).then(studies => {
|
|
||||||
// Add additional metadata to our study from the studylist
|
|
||||||
studies.forEach(study => {
|
|
||||||
const studylistStudy = OHIF.studylist.collections.Studies.findOne({
|
|
||||||
studyInstanceUid: study.studyInstanceUid
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!studylistStudy) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.assign(study, studylistStudy);
|
|
||||||
});
|
|
||||||
|
|
||||||
resolve(studies);
|
|
||||||
}).catch(reject);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Router.route('/viewer/studies/:studyInstanceUids', {
|
|
||||||
name: 'viewerStudies',
|
|
||||||
onBeforeAction: function() {
|
|
||||||
this.render('app', { data: { template: 'loadingText' } });
|
|
||||||
|
|
||||||
const studyInstanceUids = this.params.studyInstanceUids.split(';');
|
|
||||||
OHIF.viewer.prepare({ studyInstanceUids }).then(studies => {
|
|
||||||
this.render('app', {
|
|
||||||
data: {
|
|
||||||
template: 'viewer',
|
|
||||||
studies
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Router.onBeforeAction(function() {
|
Router.onBeforeAction(function() {
|
||||||
|
// verifyEmail controls whether emailVerification template will be rendered or not
|
||||||
|
const publicSettings = Meteor.settings && Meteor.settings.public;
|
||||||
|
const verifyEmail = publicSettings && publicSettings.verifyEmail || false;
|
||||||
|
|
||||||
|
// Check if user is signed in or needs an email verification
|
||||||
if (!Meteor.userId() && !Meteor.loggingIn()) {
|
if (!Meteor.userId() && !Meteor.loggingIn()) {
|
||||||
this.render('entrySignIn');
|
this.render('entrySignIn');
|
||||||
|
} else if (verifyEmail && Meteor.user().emails && !Meteor.user().emails[0].verified) {
|
||||||
|
this.render('emailVerification');
|
||||||
} else {
|
} else {
|
||||||
this.next();
|
this.next();
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
except: ['entrySignIn', 'entrySignUp', 'forgotPassword', 'resetPassword']
|
except: ['entrySignIn', 'entrySignUp', 'forgotPassword', 'resetPassword', 'emailVerification']
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Router.route('/', function() {
|
||||||
|
this.redirect('/studylist');
|
||||||
|
}, { name: 'home' });
|
||||||
|
|
||||||
|
Router.route('/studylist', function() {
|
||||||
|
this.render('app', { data: { template: 'studylist' } });
|
||||||
|
}, { name: 'studylist' });
|
||||||
|
|
||||||
|
Router.route('/viewer/timepoints/:timepointId', function() {
|
||||||
|
const timepointId = this.params.timepointId;
|
||||||
|
OHIF.viewerbase.renderViewer(this, { timepointId });
|
||||||
|
}, { name: 'viewerTimepoint' });
|
||||||
|
|
||||||
|
Router.route('/viewer/studies/:studyInstanceUids', function() {
|
||||||
|
const studyInstanceUids = this.params.studyInstanceUids.split(';');
|
||||||
|
OHIF.viewerbase.renderViewer(this, { studyInstanceUids });
|
||||||
|
}, { name: 'viewerStudies' });
|
||||||
|
|||||||
@ -36,11 +36,11 @@ export const storeMeasurements = (measurementData, timepointIds) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const retrieveTimepoints = (patientId) => {
|
export const retrieveTimepoints = filter => {
|
||||||
console.log('retrieveTimepoints');
|
console.log('retrieveTimepoints');
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
Meteor.call('retrieveTimepoints', patientId, (error, response) => {
|
Meteor.call('retrieveTimepoints', filter, (error, response) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
reject(error);
|
reject(error);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -8,8 +8,6 @@ import { OHIF } from 'meteor/ohif:core';
|
|||||||
* @param title The title to be used for the tab heading
|
* @param title The title to be used for the tab heading
|
||||||
*/
|
*/
|
||||||
OHIF.lesiontracker.openNewTabWithTimepoint = timepointId => {
|
OHIF.lesiontracker.openNewTabWithTimepoint = timepointId => {
|
||||||
const contentId = 'viewerTab';
|
|
||||||
|
|
||||||
const Timepoints = OHIF.studylist.timepointApi.timepoints;
|
const Timepoints = OHIF.studylist.timepointApi.timepoints;
|
||||||
const timepoint = Timepoints.findOne({
|
const timepoint = Timepoints.findOne({
|
||||||
timepointId: timepointId
|
timepointId: timepointId
|
||||||
@ -27,7 +25,6 @@ OHIF.lesiontracker.openNewTabWithTimepoint = timepointId => {
|
|||||||
|
|
||||||
// Update the OHIF.viewer.data global object
|
// Update the OHIF.viewer.data global object
|
||||||
OHIF.viewer.data = {
|
OHIF.viewer.data = {
|
||||||
contentId: contentId,
|
|
||||||
studyInstanceUids: data.studyInstanceUids,
|
studyInstanceUids: data.studyInstanceUids,
|
||||||
timepointIds: data.timepointIds,
|
timepointIds: data.timepointIds,
|
||||||
currentTimepointId: timepointId
|
currentTimepointId: timepointId
|
||||||
|
|||||||
@ -59,8 +59,6 @@ Meteor.methods({
|
|||||||
// Remove the specified studyInstanceUid from the array of associated studyInstanceUids
|
// Remove the specified studyInstanceUid from the array of associated studyInstanceUids
|
||||||
timepoint.studyInstanceUids.splice(index, 1);
|
timepoint.studyInstanceUids.splice(index, 1);
|
||||||
|
|
||||||
let updated = [];
|
|
||||||
let removed = [];
|
|
||||||
if (timepoint.studyInstanceUids.length) {
|
if (timepoint.studyInstanceUids.length) {
|
||||||
Timepoints.update(timepoint._id, {
|
Timepoints.update(timepoint._id, {
|
||||||
$set: {
|
$set: {
|
||||||
@ -78,7 +76,7 @@ Meteor.methods({
|
|||||||
timepointId: timepointId
|
timepointId: timepointId
|
||||||
};
|
};
|
||||||
|
|
||||||
MeasurementCollections[tool.id].remove(filter)
|
MeasurementCollections[tool.id].remove(filter);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -95,14 +93,8 @@ Meteor.methods({
|
|||||||
Timepoints.update(timepointData, query);
|
Timepoints.update(timepointData, query);
|
||||||
},
|
},
|
||||||
|
|
||||||
retrieveTimepoints(patientId) {
|
retrieveTimepoints(filter={}) {
|
||||||
OHIF.log.info('Retrieving Timepoints from the Server');
|
OHIF.log.info('Retrieving Timepoints from the Server');
|
||||||
|
|
||||||
const filter = {}
|
|
||||||
if (patientId) {
|
|
||||||
filter.patientId = patientId;
|
|
||||||
};
|
|
||||||
|
|
||||||
return Timepoints.find(filter).fetch();
|
return Timepoints.find(filter).fetch();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -128,7 +120,7 @@ Meteor.methods({
|
|||||||
OHIF.log.info('Retrieving Measurements from the Server');
|
OHIF.log.info('Retrieving Measurements from the Server');
|
||||||
let measurementData = {};
|
let measurementData = {};
|
||||||
|
|
||||||
const filter = {}
|
const filter = {};
|
||||||
if (patientId) {
|
if (patientId) {
|
||||||
filter.patientId = patientId;
|
filter.patientId = patientId;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,14 +26,14 @@ class TimepointApi {
|
|||||||
this.timepoints._debugName = 'Timepoints';
|
this.timepoints._debugName = 'Timepoints';
|
||||||
}
|
}
|
||||||
|
|
||||||
retrieveTimepoints(patientId) {
|
retrieveTimepoints(filter) {
|
||||||
const retrievalFn = configuration.dataExchange.retrieve;
|
const retrievalFn = configuration.dataExchange.retrieve;
|
||||||
if (!_.isFunction(retrievalFn)) {
|
if (!_.isFunction(retrievalFn)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
retrievalFn(patientId).then(timepointData => {
|
retrievalFn(filter).then(timepointData => {
|
||||||
OHIF.log.info('Timepoint data retrieval');
|
OHIF.log.info('Timepoint data retrieval');
|
||||||
OHIF.log.info(timepointData);
|
OHIF.log.info(timepointData);
|
||||||
_.each(timepointData, timepoint => {
|
_.each(timepointData, timepoint => {
|
||||||
|
|||||||
@ -92,6 +92,14 @@ Viewerbase.imageViewerViewportData = imageViewerViewportData;
|
|||||||
import { panelNavigation } from './lib/panelNavigation';
|
import { panelNavigation } from './lib/panelNavigation';
|
||||||
Viewerbase.panelNavigation = panelNavigation;
|
Viewerbase.panelNavigation = panelNavigation;
|
||||||
|
|
||||||
|
// prepareViewerData
|
||||||
|
import { prepareViewerData } from './lib/prepareViewerData';
|
||||||
|
Viewerbase.prepareViewerData = prepareViewerData;
|
||||||
|
|
||||||
|
// renderViewer
|
||||||
|
import { renderViewer } from './lib/renderViewer';
|
||||||
|
Viewerbase.renderViewer = renderViewer;
|
||||||
|
|
||||||
// WLPresets.*
|
// WLPresets.*
|
||||||
import { WLPresets } from './lib/WLPresets';
|
import { WLPresets } from './lib/WLPresets';
|
||||||
Viewerbase.wlPresets = WLPresets;
|
Viewerbase.wlPresets = WLPresets;
|
||||||
|
|||||||
122
Packages/ohif-viewerbase/client/lib/prepareViewerData.js
Normal file
122
Packages/ohif-viewerbase/client/lib/prepareViewerData.js
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare the studies data to render the viewer template
|
||||||
|
*
|
||||||
|
* @param {Array} studyInstanceUids List of studies that will be loaded into viewer
|
||||||
|
* @param {String} timepointId ID of the current timepoint to get the studies from
|
||||||
|
* @param {Object} timepointsFilter An object containing the filter to retrieve the timepoints
|
||||||
|
* @return {Promise} Promise that will be resolved with the studies when the metadata is loaded
|
||||||
|
*/
|
||||||
|
export const prepareViewerData = ({ studyInstanceUids, timepointId, timepointsFilter={} }) => {
|
||||||
|
// Clear the cornerstone tool data to sync the measurements with the measurements API
|
||||||
|
cornerstoneTools.globalImageIdSpecificToolStateManager = cornerstoneTools.newImageIdSpecificToolStateManager();
|
||||||
|
|
||||||
|
// Retrieve the studies metadata
|
||||||
|
const promise = new Promise((resolve, reject) => {
|
||||||
|
const processData = viewerData => {
|
||||||
|
OHIF.studylist.retrieveStudiesMetadata(viewerData.studyInstanceUids).then(studies => {
|
||||||
|
// Add additional metadata to our study from the studylist
|
||||||
|
studies.forEach(study => {
|
||||||
|
const studylistStudy = OHIF.studylist.collections.Studies.findOne({
|
||||||
|
studyInstanceUid: study.studyInstanceUid
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!studylistStudy) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(study, studylistStudy);
|
||||||
|
});
|
||||||
|
|
||||||
|
resolve({
|
||||||
|
studies,
|
||||||
|
viewerData
|
||||||
|
});
|
||||||
|
}).catch(reject);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check if the studies are already given and ignore the timepoint ID if so
|
||||||
|
if (studyInstanceUids && studyInstanceUids.length) {
|
||||||
|
const viewerData = { studyInstanceUids };
|
||||||
|
processData(viewerData);
|
||||||
|
} else {
|
||||||
|
// Find the timepoint by ID and load the studies from it
|
||||||
|
OHIF.studylist.timepointApi.retrieveTimepoints(timepointsFilter).then(() => {
|
||||||
|
const viewerData = buildViewerDataFromTimepointId(timepointId);
|
||||||
|
processData(viewerData);
|
||||||
|
}).catch(reject);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildViewerDataFromTimepointId = timepointId => {
|
||||||
|
const timepoint = OHIF.studylist.timepointApi.timepoints.findOne({ timepointId });
|
||||||
|
if (!timepoint) {
|
||||||
|
throw new Error('Unable to find a time point with the given ID');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the relevant studyInstanceUids given the timepoints
|
||||||
|
const data = getDataFromTimepoint(timepoint);
|
||||||
|
if (!data.studyInstanceUids) {
|
||||||
|
throw new Error('No studies found that are related to this timepoint');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the viewer data and return it
|
||||||
|
return Object.assign(data, { currentTimepointId: timepointId });
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves related studies given a Baseline or Follow-up Timepoint
|
||||||
|
*
|
||||||
|
* @param {Object} timepoint A document from the Timepoints Collection
|
||||||
|
* @returns {Object} An object containing the related studies UIDs and timepoint IDs
|
||||||
|
*/
|
||||||
|
const getDataFromTimepoint = timepoint => {
|
||||||
|
let relatedStudies = timepoint.studyInstanceUids;
|
||||||
|
|
||||||
|
// If this is the baseline, we should stop here and return the relevant studies
|
||||||
|
if (isBaseline(timepoint)) {
|
||||||
|
return {
|
||||||
|
studyInstanceUids: relatedStudies,
|
||||||
|
timepointIds: [timepoint.timepointId]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, this is a follow-up exam, so we should also find the baseline timepoint,
|
||||||
|
// and all studies related to it. We also enforce that the Baseline should have a studyDate
|
||||||
|
// prior to the latest studyDate in the current (Follow-up) Timepoint.
|
||||||
|
const Timepoints = OHIF.studylist.timepointApi.timepoints;
|
||||||
|
const baseline = Timepoints.findOne({
|
||||||
|
timepointType: 'baseline',
|
||||||
|
patientId: timepoint.patientId,
|
||||||
|
latestDate: {
|
||||||
|
$lte: timepoint.latestDate
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let timepointIds = [];
|
||||||
|
if (baseline) {
|
||||||
|
relatedStudies = relatedStudies.concat(baseline.studyInstanceUids);
|
||||||
|
timepointIds.push(baseline.timepointId);
|
||||||
|
} else {
|
||||||
|
OHIF.log.warn('No Baseline found while opening a Follow-up Timepoint');
|
||||||
|
}
|
||||||
|
|
||||||
|
timepointIds.push(timepoint.timepointId);
|
||||||
|
|
||||||
|
return {
|
||||||
|
studyInstanceUids: relatedStudies,
|
||||||
|
timepointIds: timepointIds
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a Timepoints is a baseline or not
|
||||||
|
*
|
||||||
|
* @param {Object} timepoint A document from the Timepoints Collection
|
||||||
|
* @returns {boolean} Whether or not the timepoint is stored as a Baseline
|
||||||
|
*/
|
||||||
|
const isBaseline = timepoint => timepoint.timepointType === 'baseline';
|
||||||
26
Packages/ohif-viewerbase/client/lib/renderViewer.js
Normal file
26
Packages/ohif-viewerbase/client/lib/renderViewer.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the viewer with the given routing context and parameters
|
||||||
|
*
|
||||||
|
* @param {Context} context Context of the router
|
||||||
|
* @param {Object} params Parameters that will be used to prepare the viewer data
|
||||||
|
*/
|
||||||
|
export const renderViewer = (context, params) => {
|
||||||
|
// Wait until the viewer data is ready to render it
|
||||||
|
const promise = OHIF.viewerbase.prepareViewerData(params);
|
||||||
|
|
||||||
|
// Show loading state while preparing the viewer data
|
||||||
|
OHIF.ui.showDialog('dialogLoading', { promise });
|
||||||
|
|
||||||
|
// Render the viewer when the data is ready
|
||||||
|
promise.then(({ studies, viewerData }) => {
|
||||||
|
context.render('app', {
|
||||||
|
data: {
|
||||||
|
template: 'viewer',
|
||||||
|
studies,
|
||||||
|
viewerData
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
Loading…
Reference in New Issue
Block a user