Fixing metadata issues

This commit is contained in:
Bruno Alves de Faria 2017-02-22 08:00:00 -03:00
parent a742c8036d
commit 19c31ffbb8
14 changed files with 197 additions and 128 deletions

View File

@ -86,8 +86,6 @@ Template.viewer.onCreated(() => {
instance.data.studies.forEach(study => { instance.data.studies.forEach(study => {
study.selected = true; study.selected = true;
const studyMetadata = new OHIF.metadata.StudyMetadata(study);
study.displaySets = OHIF.viewerbase.sortingManager.getDisplaySets(studyMetadata);
OHIF.viewer.Studies.insert(study); OHIF.viewer.Studies.insert(study);
}); });

View File

@ -77,8 +77,6 @@ Template.viewer.onCreated(() => {
ViewerData[contentId].studyInstanceUids = []; ViewerData[contentId].studyInstanceUids = [];
instance.data.studies.forEach(study => { instance.data.studies.forEach(study => {
study.selected = true; study.selected = true;
const studyMetadata = new OHIF.metadata.StudyMetadata(study);
study.displaySets = OHIF.viewerbase.sortingManager.getDisplaySets(studyMetadata);
OHIF.viewer.Studies.insert(study); OHIF.viewer.Studies.insert(study);
ViewerData[contentId].studyInstanceUids.push(study.studyInstanceUid); ViewerData[contentId].studyInstanceUids.push(study.studyInstanceUid);
}); });

View File

@ -3,7 +3,7 @@ import { parsingUtils } from '../parsingUtils';
export class MetadataProvider { export class MetadataProvider {
constructor() { constructor() {
this.metadataLookup = {}; this.metadataLookup = new Map();
} }
/** /**
@ -58,7 +58,7 @@ export class MetadataProvider {
metadata.imagePlane = this.getImagePlane(instanceMetadata); metadata.imagePlane = this.getImagePlane(instanceMetadata);
// Add the metadata to the imageId lookup object // Add the metadata to the imageId lookup object
this.metadataLookup[imageId] = metadata; this.metadataLookup.set(imageId, metadata);
} }
/** /**
@ -67,7 +67,7 @@ export class MetadataProvider {
* @returns image metadata * @returns image metadata
*/ */
getMetadata(imageId) { getMetadata(imageId) {
return this.metadataLookup[imageId]; return this.metadataLookup.get(imageId);
} }
/** /**
@ -82,7 +82,8 @@ export class MetadataProvider {
const metadata = {}; const metadata = {};
metadata[type] = data; metadata[type] = data;
this.metadataLookup[imageId] = $.extend(this.metadataLookup[imageId], metadata); const oldMetadata = this.metadataLookup.get(imageId);
this.metadataLookup.set(imageId, Object.assign(oldMetadata, metadata));
} }
getFromDataSet(dataSet, type, tag) { getFromDataSet(dataSet, type, tag) {
@ -104,7 +105,7 @@ export class MetadataProvider {
* @param image * @param image
*/ */
updateMetadata(image) { updateMetadata(image) {
const imageMetadata = this.metadataLookup[image.imageId]; const imageMetadata = this.metadataLookup.get(image.imageId);
if (!imageMetadata) { if (!imageMetadata) {
return; return;
} }
@ -252,7 +253,7 @@ export class MetadataProvider {
* @returns {Object} Relevant metadata of the specified type * @returns {Object} Relevant metadata of the specified type
*/ */
provider(type, imageId) { provider(type, imageId) {
const imageMetadata = this.metadataLookup[imageId]; const imageMetadata = this.metadataLookup.get(imageId);
if (!imageMetadata) { if (!imageMetadata) {
return; return;
} }

View File

@ -407,9 +407,8 @@ HP.ProtocolEngine = class ProtocolEngine {
}); });
if (!alreadyLoaded) { if (!alreadyLoaded) {
getStudyMetadata(priorStudy.studyInstanceUid, study => { OHIF.studylist.retrieveStudyMetadata(priorStudy.studyInstanceUid).then(study => {
study.abstractPriorValue = abstractPriorValue; study.abstractPriorValue = abstractPriorValue;
study.displaySets = OHIF.viewerbase.sortingManager.getDisplaySets(study);
OHIF.viewer.Studies.insert(study); OHIF.viewer.Studies.insert(study);
this.studies.push(study); this.studies.push(study);
this.matchImages(viewport); this.matchImages(viewport);

View File

@ -29,31 +29,36 @@ class ConformanceCriteria {
} }
validate(trialCriteriaType) { validate(trialCriteriaType) {
const baselineData = this.getData('baseline'); return new Promise((resolve, reject) => {
const followupData = this.getData('followup'); const baselinePromise = this.getData('baseline');
const mergedData = { const followupPromise = this.getData('followup');
targets: [], Promise.all([baselinePromise, followupPromise]).then(values => {
nonTargets: [] const [baselineData, followupData] = values;
}; const mergedData = {
targets: [],
nonTargets: []
};
mergedData.targets = mergedData.targets.concat(baselineData.targets); mergedData.targets = mergedData.targets.concat(baselineData.targets);
mergedData.targets = mergedData.targets.concat(followupData.targets); mergedData.targets = mergedData.targets.concat(followupData.targets);
mergedData.nonTargets = mergedData.nonTargets.concat(baselineData.nonTargets); mergedData.nonTargets = mergedData.nonTargets.concat(baselineData.nonTargets);
mergedData.nonTargets = mergedData.nonTargets.concat(followupData.nonTargets); mergedData.nonTargets = mergedData.nonTargets.concat(followupData.nonTargets);
this.maxTargets.set(null); this.maxTargets.set(null);
const resultBoth = this.validateTimepoint('both', trialCriteriaType, mergedData); const resultBoth = this.validateTimepoint('both', trialCriteriaType, mergedData);
const resultBaseline = this.validateTimepoint('baseline', trialCriteriaType, baselineData); const resultBaseline = this.validateTimepoint('baseline', trialCriteriaType, baselineData);
const resultFollowup = this.validateTimepoint('followup', trialCriteriaType, followupData); const resultFollowup = this.validateTimepoint('followup', trialCriteriaType, followupData);
const nonconformities = resultBaseline.concat(resultFollowup).concat(resultBoth); const nonconformities = resultBaseline.concat(resultFollowup).concat(resultBoth);
const groupedNonConformities = this.groupNonConformities(nonconformities); const groupedNonConformities = this.groupNonConformities(nonconformities);
// Keep both? Group the data only on viewer/measurementTable views? // Keep both? Group the data only on viewer/measurementTable views?
// Work with not grouped data (worse lookup performance on measurementTableRow)? // Work with not grouped data (worse lookup performance on measurementTableRow)?
this.nonconformities.set(nonconformities); this.nonconformities.set(nonconformities);
this.groupedNonConformities.set(groupedNonConformities); this.groupedNonConformities.set(groupedNonConformities);
return nonconformities; resolve(nonconformities);
});
});
} }
groupNonConformities(nonconformities) { groupNonConformities(nonconformities) {
@ -128,48 +133,47 @@ class ConformanceCriteria {
* Build the data that will be used to do the conformance criteria checks * Build the data that will be used to do the conformance criteria checks
*/ */
getData(timepointType) { getData(timepointType) {
const data = { return new Promise((resolve, reject) => {
targets: [], const data = {
nonTargets: [] targets: [],
}; nonTargets: []
};
const fillData = measurementType => { const studyPromises = [];
const measurements = this.measurementApi.fetch(measurementType);
measurements.forEach(measurement => { const fillData = measurementType => {
const { studyInstanceUid, imageId } = measurement; const measurements = this.measurementApi.fetch(measurementType);
const metadata = this.getImageInstanceMetadata(studyInstanceUid, imageId);
const timepointId = measurement.timepointId;
const timepoint = timepointId && this.timepointApi.timepoints.findOne({ timepointId });
if (!timepoint || ((timepointType !== 'both') && (timepoint.timepointType !== timepointType))) { measurements.forEach(measurement => {
return; const { studyInstanceUid, imageId } = measurement;
}
data[measurementType].push({ const timepointId = measurement.timepointId;
measurement, const timepoint = timepointId && this.timepointApi.timepoints.findOne({ timepointId });
metadata,
timepoint if (!timepoint || ((timepointType !== 'both') && (timepoint.timepointType !== timepointType))) {
return;
}
const promise = OHIF.studylist.retrieveStudyMetadata(studyInstanceUid);
promise.then(study => {
const metadata = OHIF.viewer.metadataProvider.getMetadata(imageId);
data[measurementType].push({
measurement,
metadata: metadata.instance,
timepoint
});
});
studyPromises.push(promise);
}); });
}); };
};
fillData('targets'); fillData('targets');
fillData('nonTargets'); fillData('nonTargets');
return data; Promise.all(studyPromises).then(() => {
} resolve(data);
}).catch(reject);
getImageInstanceMetadata(studyInstanceUid, imageId) { });
const study = OHIF.viewer.Studies.findBy({ studyInstanceUid });
// Stop here if the study was not found
if (!study) {
return;
}
const metadata = OHIF.cornerstone.metadataProvider.getMetadata(imageId);
return metadata ? metadata.instance : undefined;
} }
} }

View File

@ -45,20 +45,19 @@ function renderIntoViewport(viewportIndex, studyInstanceUid, seriesInstanceUid,
const element = $viewports.get(viewportIndex); const element = $viewports.get(viewportIndex);
const startLoadingHandler = cornerstoneTools.loadHandlerManager.getStartLoadHandler(); const startLoadingHandler = cornerstoneTools.loadHandlerManager.getStartLoadHandler();
startLoadingHandler(element); startLoadingHandler(element);
getStudyMetadata(studyInstanceUid, loadedStudy => { OHIF.studylist.retrieveStudyMetadata(studyInstanceUid).then(study => {
loadedStudy.displaySets = createStacks(loadedStudy);
OHIF.log.warn('renderIntoViewport'); OHIF.log.warn('renderIntoViewport');
// Double check to make sure this study wasn't already inserted // Double check to make sure this study wasn't already inserted
// into OHIF.viewer.Studies, so we don't cause duplicate entry errors // into OHIF.viewer.Studies, so we don't cause duplicate entry errors
const loaded = OHIF.viewer.Studies.findBy({ const loaded = OHIF.viewer.Studies.findBy({
studyInstanceUid: loadedStudy.studyInstanceUid studyInstanceUid: study.studyInstanceUid
}); });
if (!loaded) { if (!loaded) {
OHIF.viewer.Studies.insert(loadedStudy); OHIF.viewer.Studies.insert(study);
} }
findAndRenderDisplaySet(loadedStudy.displaySets, viewportIndex, studyInstanceUid, seriesInstanceUid, sopInstanceUid, renderedCallback) findAndRenderDisplaySet(study.displaySets, viewportIndex, studyInstanceUid, seriesInstanceUid, sopInstanceUid, renderedCallback);
}); });
} }
} }

View File

@ -1,3 +1,8 @@
import { Template } from 'meteor/templating';
import { ReactiveDict } from 'meteor/reactive-dict';
import { _ } from 'meteor/underscore';
import { OHIF } from 'meteor/ohif:core';
Template.seriesDetailsTable.onCreated(() => { Template.seriesDetailsTable.onCreated(() => {
const instance = Template.instance(); const instance = Template.instance();
@ -31,8 +36,7 @@ Template.seriesDetailsTable.onRendered(() => {
// Get series list for the study // Get series list for the study
_.map(studies, (selectedStudy, index) => { _.map(studies, (selectedStudy, index) => {
studies[index].seriesList = []; studies[index].seriesList = [];
getStudyMetadata(selectedStudy.studyInstanceUid, study => { OHIF.studylist.retrieveStudyMetadata(study => {
// Set series list // Set series list
studies[index].seriesList = study.seriesList; studies[index].seriesList = study.seriesList;
studies[index].displaySeriesLoadingText = false; studies[index].displaySeriesLoadingText = false;

View File

@ -1,10 +1,9 @@
import { $ } from 'meteor/jquery';
import { OHIF } from 'meteor/ohif:core'; import { OHIF } from 'meteor/ohif:core';
/** /**
* Retrieves metaData for multiple studies at once. * Retrieves metaData for multiple studies at once.
* *
* This function calls getStudyMetadata several times, asynchronously, * This function calls retrieveStudyMetadata several times, asynchronously,
* and waits for all of the results to be returned. * and waits for all of the results to be returned.
* *
* @param studyInstanceUids The UIDs of the Studies to be retrieved * @param studyInstanceUids The UIDs of the Studies to be retrieved
@ -27,32 +26,21 @@ OHIF.studylist.getStudiesMetadata = (studyInstanceUids, doneCallback, failCallba
// Loop through the array of studyInstanceUids // Loop through the array of studyInstanceUids
studyInstanceUids.forEach(function(studyInstanceUid) { studyInstanceUids.forEach(function(studyInstanceUid) {
// Create a new Deferred to monitor the progress of the asynchronous
// metaData retrieval
const deferred = new $.Deferred();
// Send the call, and attach doneCallbacks and failCallbacks // Send the call, and attach doneCallbacks and failCallbacks
// which can resolve or reject the related promise based on its outcome // which can resolve or reject the related promise based on its outcome
getStudyMetadata(studyInstanceUid, function(study) { const promise = OHIF.studylist.retrieveStudyMetadata(studyInstanceUid);
deferred.resolve(study);
}, function(error) {
deferred.reject(error);
});
// Add the current promise to the array of promises // Add the current promise to the array of promises
promises.push(deferred.promise()); promises.push(promise);
}); });
// When all of the promises are complete, this callback runs // When all of the promises are complete, this callback runs
$.when.apply($, promises).done(function() { Promise.all(promises).then(studies => {
// Convert the Arguments Array-like Object to an actual array
const studies = $.makeArray(arguments);
// Pass the studies array to the doneCallback, if one exists // Pass the studies array to the doneCallback, if one exists
if (doneCallback && typeof doneCallback === 'function') { if (doneCallback && typeof doneCallback === 'function') {
doneCallback(studies); doneCallback(studies);
} }
}).fail(function(error) { }).catch(error => {
OHIF.log.warn(error); OHIF.log.warn(error);
if (failCallback && typeof failCallback === 'function') { if (failCallback && typeof failCallback === 'function') {
failCallback(error); failCallback(error);

View File

@ -1,6 +1,7 @@
import { OHIF } from 'meteor/ohif:core'; import { OHIF } from 'meteor/ohif:core';
import 'meteor/ohif:viewerbase'; import 'meteor/ohif:viewerbase';
// DEPRECATED - use OHIF.studylist.retrieveStudyMetadata instead
// Define the StudyMetaData object. This is used as a cache // Define the StudyMetaData object. This is used as a cache
// to store study meta data information to prevent unnecessary // to store study meta data information to prevent unnecessary
// calls to the server // calls to the server

View File

@ -7,5 +7,6 @@ import './getStudiesMetadata.js';
import './importStudies.js'; import './importStudies.js';
import './openNewTab.js'; import './openNewTab.js';
import './queryStudies.js'; import './queryStudies.js';
import './retrieveStudyMetadata.js';
import './studylist.js'; import './studylist.js';
import './switchToTab.js'; import './switchToTab.js';

View File

@ -5,31 +5,26 @@ import { OHIF } from 'meteor/ohif:core';
* @param studiesToQuery Studies to query * @param studiesToQuery Studies to query
*/ */
queryStudies = function(studiesToQuery, options) { queryStudies = function(studiesToQuery, options) {
const studiesQueried = [], let studiesQueried = 0;
numberOfStudiesToQuery = studiesToQuery.length, const numberOfStudiesToQuery = studiesToQuery.length;
notify = (options || {}).notify || function() { /* noop */ } const notify = (options || {}).notify || function() { /* noop */ };
return new Promise((resolve, reject) => { const promises = [];
if (studiesToQuery.length < 1) {
return reject();
}
studiesToQuery.forEach(function(studyToQuery) { studiesToQuery.forEach(studyToQuery => {
getStudyMetadata(studyToQuery.studyInstanceUid, function(study) { const promise = OHIF.studylist.retrieveStudyMetadata(studyToQuery.studyInstanceUid);
studiesQueried.push(study); promise.then(study => {
studiesQueried++;
notify({ notify({
total: numberOfStudiesToQuery, total: numberOfStudiesToQuery,
processed: studiesQueried.length processed: studiesQueried
});
if (studiesQueried.length === numberOfStudiesToQuery) {
resolve(studiesQueried);
}
}); });
}); });
promises.push(promise);
}); });
}
return Promise.all(promises);
};
queryStudiesWithProgress = function(studiesToQuery) { queryStudiesWithProgress = function(studiesToQuery) {
return OHIF.ui.showDialog('dialogProgress', { return OHIF.ui.showDialog('dialogProgress', {
@ -60,7 +55,7 @@ queryStudiesWithProgress = function(studiesToQuery) {
* @returns {number} * @returns {number}
*/ */
getNumberOfFilesInStudy = function(study) { getNumberOfFilesInStudy = function(study) {
var numberOFFilesToExport = 0; let numberOFFilesToExport = 0;
study.seriesList.forEach(function(series) { study.seriesList.forEach(function(series) {
series.instances.forEach(function(instance) { series.instances.forEach(function(instance) {

View File

@ -0,0 +1,85 @@
import { Meteor } from 'meteor/meteor';
import { OHIF } from 'meteor/ohif:core';
import 'meteor/ohif:viewerbase';
// Define the StudyMetaDataPromises object. This is used as a cache to store study meta data
// promises and prevent unnecessary subsequent calls to the server
const StudyMetaDataPromises = new Map();
/**
* Retrieves study metadata using a server call
*
* @param {String} studyInstanceUid The UID of the Study to be retrieved
* @returns {Promise} that will be resolved with the metadata or rejected with the error
*/
OHIF.studylist.retrieveStudyMetadata = studyInstanceUid => {
// If the StudyMetaDataPromises cache already has a pending or resolved promise related to the
// given studyInstanceUid, then that promise is returned
if (StudyMetaDataPromises.has(studyInstanceUid)) {
return StudyMetaDataPromises.get(studyInstanceUid);
}
console.time('retrieveStudyMetadata');
// Create a promise to handle the data retrieval
const promise = new Promise((resolve, reject) => {
// If no study metadata is in the cache variable, we need to retrieve it from
// the server with a call.
Meteor.call('GetStudyMetadata', studyInstanceUid, function(error, study) {
console.timeEnd('retrieveStudyMetadata');
if (Meteor.user && Meteor.user()) {
HipaaLogger.logEvent({
eventType: 'viewed',
userId: Meteor.userId(),
userName: Meteor.user().profile.fullName,
collectionName: 'Study',
recordId: studyInstanceUid,
patientId: study.patientId,
patientName: study.patientName
});
}
if (error) {
OHIF.log.warn(error);
reject(error);
return;
}
if (!study) {
throw new Meteor.Error('GetStudyMetadata', 'No study data returned from server');
}
// Once the data was retrieved, the series are sorted by series and instance number
OHIF.viewerbase.sortStudy(study);
// Add additional metadata to our study from the studylist
const studylistStudy = StudyListStudies.findOne({
studyInstanceUid: study.studyInstanceUid
});
if (!studylistStudy) {
// return;
} else {
Object.assign(study, studylistStudy);
}
// Transform the study in a StudyMetadata object
const studyMetadata = new OHIF.metadata.StudyMetadata(study);
// Add the display sets to the study
study.displaySets = OHIF.viewerbase.sortingManager.getDisplaySets(studyMetadata);
study.displaySets.forEach(displaySet => {
OHIF.viewerbase.stackManager.makeAndAddStack(study, displaySet);
});
// Resolve the promise with the final study metadata object
resolve(study);
});
});
// Store the promise in cache
StudyMetaDataPromises.set(studyInstanceUid, promise);
return promise;
};

View File

@ -110,9 +110,7 @@ Template.studyTimepointStudy.events({
if (!alreadyLoaded) { if (!alreadyLoaded) {
const $studies = instance.getStudyElement(true); const $studies = instance.getStudyElement(true);
$studies.trigger('loadStarted'); $studies.trigger('loadStarted');
getStudyMetadata(studyInstanceUid, study => { OHIF.studylist.retrieveStudyMetadata(studyInstanceUid).then(study => {
const studyMetadata = new OHIF.metadata.StudyMetadata(study);
study.displaySets = sortingManager.getDisplaySets(studyMetadata);
instance.data.study = study; instance.data.study = study;
OHIF.viewer.Studies.insert(study); OHIF.viewer.Studies.insert(study);

View File

@ -52,8 +52,6 @@ Template.viewer.onCreated(() => {
ViewerData[contentId].studyInstanceUids = []; ViewerData[contentId].studyInstanceUids = [];
instance.data.studies.forEach(study => { instance.data.studies.forEach(study => {
study.selected = true; study.selected = true;
const studyMetadata = new OHIF.metadata.StudyMetadata(study);
study.displaySets = OHIF.viewerbase.sortingManager.getDisplaySets(studyMetadata);
OHIF.viewer.Studies.insert(study); OHIF.viewer.Studies.insert(study);
ViewerData[contentId].studyInstanceUids.push(study.studyInstanceUid); ViewerData[contentId].studyInstanceUids.push(study.studyInstanceUid);
}); });