feat(hp): enable OHIF to run with partial metadata for large studies at the cost of less effective hanging protocol (#3804)

Co-authored-by: rodrigobasilio2022 <rodrigo.basilio@radicalimaging.com>
Co-authored-by: rodrigobasilio2022 <114958722+rodrigobasilio2022@users.noreply.github.com>
This commit is contained in:
authored and GitHub committed 2024-01-09 14:22:00 -05:00
1 parent b5cdf88a57
commit 0049f4c030
9 files changed
+240 -74

No files matched your search

+54 -21
View File
@@ -13,7 +13,7 @@ import { history } from '../../utils/history';
import loadModules from '../../pluginImports';
import isSeriesFilterUsed from '../../utils/isSeriesFilterUsed';
const { getSplitParam } = utils;
const { getSplitParam, sortingCriteria } = utils;
const { TimingEnum } = Types;
/**
@@ -26,11 +26,33 @@ const { TimingEnum } = Types;
* @returns array of subscriptions to cancel
*/
function defaultRouteInit(
{ servicesManager, studyInstanceUIDs, dataSource, filters },
{ servicesManager, studyInstanceUIDs, dataSource, filters, appConfig },
hangingProtocolId
) {
const { displaySetService, hangingProtocolService, uiNotificationService } =
const { displaySetService, hangingProtocolService, uiNotificationService, customizationService } =
servicesManager.services;
/**
* Function to apply the hanging protocol when the minimum number of display sets were
* received or all display sets retrieval were completed
* @returns
*/
function applyHangingProtocol() {
const displaySets = displaySetService.getActiveDisplaySets();
if (!displaySets || !displaySets.length) {
return;
}
// Gets the studies list to use
const studies = getStudies(studyInstanceUIDs, displaySets);
// study being displayed, and is thus the "active" study.
const activeStudy = studies[0];
// run the hanging protocol matching on the displaySets with the predefined
// hanging protocol in the mode configuration
hangingProtocolService.run({ studies, activeStudy, displaySets }, hangingProtocolId);
}
const unsubscriptions = [];
const issuedWarningSeries = [];
@@ -55,6 +77,7 @@ function defaultRouteInit(
duration: 7000,
});
}
displaySetService.makeDisplaySets(seriesMetadata.instances, madeInClient);
}
);
@@ -63,10 +86,15 @@ function defaultRouteInit(
log.time(TimingEnum.STUDY_TO_DISPLAY_SETS);
log.time(TimingEnum.STUDY_TO_FIRST_IMAGE);
const allRetrieves = studyInstanceUIDs.map(StudyInstanceUID =>
dataSource.retrieve.series.metadata({
StudyInstanceUID,
filters,
returnPromises: true,
sortCriteria:
customizationService.get('sortingCriteria') ||
sortingCriteria.seriesSortCriteria.seriesInfoSortingCriteria,
})
);
@@ -77,31 +105,34 @@ function defaultRouteInit(
});
});
// The hanging protocol matching service is fairly expensive to run multiple
// times, and doesn't allow partial matches to be made (it will simply fail
// to display anything if a required match fails), so we wait here until all metadata
// is retrieved (which will synchronously trigger the display set creation)
// until we run the hanging protocol matching service.
Promise.allSettled(allRetrieves).then(() => {
Promise.allSettled(allRetrieves).then(promises => {
log.timeEnd(TimingEnum.STUDY_TO_DISPLAY_SETS);
log.time(TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE);
log.time(TimingEnum.DISPLAY_SETS_TO_ALL_IMAGES);
const displaySets = displaySetService.getActiveDisplaySets();
if (!displaySets || !displaySets.length) {
return;
const allPromises = [];
const remainingPromises = [];
function startRemainingPromises(remainingPromises) {
remainingPromises.forEach(p => p.forEach(p => p.start()));
}
// Gets the studies list to use
const studies = getStudies(studyInstanceUIDs, displaySets);
promises.forEach(promise => {
const retrieveSeriesMetadataPromise = promise.value;
if (Array.isArray(retrieveSeriesMetadataPromise)) {
const { requiredSeries, remaining } = hangingProtocolService.filterSeriesRequiredForRun(
hangingProtocolId,
retrieveSeriesMetadataPromise
);
const requiredSeriesPromises = requiredSeries.map(promise => promise.start());
allPromises.push(Promise.allSettled(requiredSeriesPromises));
remainingPromises.push(remaining);
}
});
// study being displayed, and is thus the "active" study.
const activeStudy = studies[0];
// run the hanging protocol matching on the displaySets with the predefined
// hanging protocol in the mode configuration
hangingProtocolService.run({ studies, activeStudy, displaySets }, hangingProtocolId);
Promise.allSettled(allPromises).then(applyHangingProtocol);
startRemainingPromises(remainingPromises);
applyHangingProtocol();
});
return unsubscriptions;
@@ -401,6 +432,7 @@ export default function ModeRoute({
studyInstanceUIDs,
dataSource,
filters,
appConfig,
},
hangingProtocolIdToUse
);
@@ -482,4 +514,5 @@ ModeRoute.propTypes = {
extensionManager: PropTypes.object,
servicesManager: PropTypes.object,
hotkeysManager: PropTypes.object,
commandsManager: PropTypes.object,
};
@@ -214,6 +214,32 @@ export default class HangingProtocolService extends PubSubService {
};
}
/**
* Filters the series required for running a hanging protocol.
*
* This can be extended in the future with more complex selection rules e.g.
* N series of a given type, and M of a different type, such as all CT series,
* and all SR, and then everything else.
*
* @param protocolId - The ID of the hanging protocol.
* @param seriesPromises - An array of promises representing the series.
* @returns An object containing the required series and the remaining series.
*/
public filterSeriesRequiredForRun(protocolId, seriesPromises) {
if (Array.isArray(protocolId)) {
protocolId = protocolId[0];
}
const minSeriesLoadedToRunHP =
this.getProtocolById(protocolId)?.hpInitiationCriteria?.minSeriesLoaded ||
seriesPromises.length;
const requiredSeries = seriesPromises.slice(0, minSeriesLoadedToRunHP);
const remaining = seriesPromises.slice(minSeriesLoadedToRunHP);
return {
requiredSeries,
remaining,
};
}
/** Gets the protocol with id 'default' */
public getDefaultProtocol(): HangingProtocol.Protocol {
return this.getProtocolById('default');
@@ -543,8 +569,8 @@ export default class HangingProtocolService extends PubSubService {
viewportId: existingViewportId
? existingViewportId
: index === 0
? 'default'
: uuidv4(),
? 'default'
: uuidv4(),
},
displaySets: viewport.displaySets || [],
};
@@ -293,6 +293,17 @@ export type Protocol = {
*/
numberOfPriorsReferenced?: number;
syncDataForViewports?: boolean;
/**
* Set of minimal conditions necessary to run the hanging protocol.
*/
hpInitiationCriteria?: {
/* If configured, sets the minimum number of series needed to run the hanging
* protocol and start displaying images. Used when OHIF needs to handle studies
* with several series and it is required that the first image should be loaded
* faster.
*/
minSeriesLoaded: number;
};
};
/** Used to dynamically generate protocols.