init
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
import RetrieveMetadataLoaderSync from './retrieveMetadataLoaderSync';
|
||||
import RetrieveMetadataLoaderAsync from './retrieveMetadataLoaderAsync';
|
||||
|
||||
/**
|
||||
* Retrieve Study metadata from a DICOM server. If the server is configured to use lazy load, only the first series
|
||||
* will be loaded and the property "studyLoader" will be set to let consumer load remaining series as needed.
|
||||
*
|
||||
* @param {*} dicomWebClient The DICOMWebClient instance to be used for series load
|
||||
* @param {*} StudyInstanceUID The UID of the Study to be retrieved
|
||||
* @param {*} enableStudyLazyLoad Whether the study metadata should be loaded asynchronously
|
||||
* @param {object} filters Object containing filters to be applied on retrieve metadata process
|
||||
* @param {string} [filters.seriesInstanceUID] Series instance uid to filter results against
|
||||
* @param {function} [sortCriteria] Sort criteria function
|
||||
* @param {function} [sortFunction] Sort function
|
||||
*
|
||||
* @returns {Promise} A promises that resolves the study descriptor object
|
||||
*/
|
||||
async function RetrieveMetadata(
|
||||
dicomWebClient,
|
||||
StudyInstanceUID,
|
||||
enableStudyLazyLoad,
|
||||
filters = {},
|
||||
sortCriteria,
|
||||
sortFunction
|
||||
) {
|
||||
const RetrieveMetadataLoader =
|
||||
enableStudyLazyLoad !== false ? RetrieveMetadataLoaderAsync : RetrieveMetadataLoaderSync;
|
||||
|
||||
const retrieveMetadataLoader = new RetrieveMetadataLoader(
|
||||
dicomWebClient,
|
||||
StudyInstanceUID,
|
||||
filters,
|
||||
sortCriteria,
|
||||
sortFunction
|
||||
);
|
||||
const data = await retrieveMetadataLoader.execLoad();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
export default RetrieveMetadata;
|
||||
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Class to define inheritance of load retrieve strategy.
|
||||
* The process can be async load (lazy) or sync load
|
||||
*
|
||||
* There are methods that must be implemented at consumer level
|
||||
* To retrieve study call execLoad
|
||||
*/
|
||||
export default class RetrieveMetadataLoader {
|
||||
/**
|
||||
* @constructor
|
||||
* @param {Object} client The dicomweb-client.
|
||||
* @param {Array} studyInstanceUID Study instance ui to be retrieved
|
||||
* @param {Object} [filters] - Object containing filters to be applied on retrieve metadata process
|
||||
* @param {string} [filters.seriesInstanceUID] - series instance uid to filter results against
|
||||
* @param {Object} [sortCriteria] - Custom sort criteria used for series
|
||||
* @param {Function} [sortFunction] - Custom sort function for series
|
||||
*/
|
||||
constructor(
|
||||
client,
|
||||
studyInstanceUID,
|
||||
filters = {},
|
||||
sortCriteria = undefined,
|
||||
sortFunction = undefined
|
||||
) {
|
||||
this.client = client;
|
||||
this.studyInstanceUID = studyInstanceUID;
|
||||
this.filters = filters;
|
||||
this.sortCriteria = sortCriteria;
|
||||
this.sortFunction = sortFunction;
|
||||
}
|
||||
|
||||
async execLoad() {
|
||||
const preLoadData = await this.preLoad();
|
||||
const loadData = await this.load(preLoadData);
|
||||
const postLoadData = await this.posLoad(loadData);
|
||||
return postLoadData;
|
||||
}
|
||||
|
||||
/**
|
||||
* It iterates over given loaders running each one. Loaders parameters must be bind when getting it.
|
||||
* @param {Array} loaders - array of loader to retrieve data.
|
||||
*/
|
||||
async runLoaders(loaders) {
|
||||
let result;
|
||||
for (const loader of loaders) {
|
||||
result = await loader();
|
||||
if (result && result.length) {
|
||||
break; // closes iterator in case data is retrieved successfully
|
||||
}
|
||||
}
|
||||
|
||||
if (loaders.next().done && !result) {
|
||||
throw new Error('RetrieveMetadataLoader failed');
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Methods to be overwrite
|
||||
async configLoad() {}
|
||||
async preLoad() {}
|
||||
async load(preLoadData) {}
|
||||
async posLoad(loadData) {}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
import dcmjs from 'dcmjs';
|
||||
import { sortStudySeries } from '@ohif/core/src/utils/sortStudy';
|
||||
import RetrieveMetadataLoader from './retrieveMetadataLoader';
|
||||
|
||||
// Series Date, Series Time, Series Description and Series Number to be included
|
||||
// in the series metadata query result
|
||||
const includeField = ['00080021', '00080031', '0008103E', '00200011'].join(',');
|
||||
|
||||
export class DeferredPromise {
|
||||
metadata = undefined;
|
||||
processFunction = undefined;
|
||||
internalPromise = undefined;
|
||||
thenFunction = undefined;
|
||||
rejectFunction = undefined;
|
||||
|
||||
setMetadata(metadata) {
|
||||
this.metadata = metadata;
|
||||
}
|
||||
setProcessFunction(func) {
|
||||
this.processFunction = func;
|
||||
}
|
||||
getPromise() {
|
||||
return this.start();
|
||||
}
|
||||
start() {
|
||||
if (this.internalPromise) {
|
||||
return this.internalPromise;
|
||||
}
|
||||
this.internalPromise = this.processFunction();
|
||||
// in case then and reject functions called before start
|
||||
if (this.thenFunction) {
|
||||
this.then(this.thenFunction);
|
||||
this.thenFunction = undefined;
|
||||
}
|
||||
if (this.rejectFunction) {
|
||||
this.reject(this.rejectFunction);
|
||||
this.rejectFunction = undefined;
|
||||
}
|
||||
return this.internalPromise;
|
||||
}
|
||||
then(func) {
|
||||
if (this.internalPromise) {
|
||||
return this.internalPromise.then(func);
|
||||
} else {
|
||||
this.thenFunction = func;
|
||||
}
|
||||
}
|
||||
reject(func) {
|
||||
if (this.internalPromise) {
|
||||
return this.internalPromise.reject(func);
|
||||
} else {
|
||||
this.rejectFunction = func;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Creates an immutable series loader object which loads each series sequentially using the iterator interface.
|
||||
*
|
||||
* @param {DICOMWebClient} dicomWebClient The DICOMWebClient instance to be used for series load
|
||||
* @param {string} studyInstanceUID The Study Instance UID from which series will be loaded
|
||||
* @param {Array} seriesInstanceUIDList A list of Series Instance UIDs
|
||||
*
|
||||
* @returns {Object} Returns an object which supports loading of instances from each of given Series Instance UID
|
||||
*/
|
||||
function makeSeriesAsyncLoader(client, studyInstanceUID, seriesInstanceUIDList) {
|
||||
return Object.freeze({
|
||||
hasNext() {
|
||||
return seriesInstanceUIDList.length > 0;
|
||||
},
|
||||
next() {
|
||||
const { seriesInstanceUID, metadata } = seriesInstanceUIDList.shift();
|
||||
const promise = new DeferredPromise();
|
||||
promise.setMetadata(metadata);
|
||||
promise.setProcessFunction(() => {
|
||||
return client.retrieveSeriesMetadata({
|
||||
studyInstanceUID,
|
||||
seriesInstanceUID,
|
||||
});
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for async load of study metadata.
|
||||
* It inherits from RetrieveMetadataLoader
|
||||
*
|
||||
* It loads the one series and then append to seriesLoader the others to be consumed/loaded
|
||||
*/
|
||||
export default class RetrieveMetadataLoaderAsync extends RetrieveMetadataLoader {
|
||||
/**
|
||||
* @returns {Array} Array of preLoaders. To be consumed as queue
|
||||
*/
|
||||
*getPreLoaders() {
|
||||
const preLoaders = [];
|
||||
const { studyInstanceUID, filters: { seriesInstanceUID } = {}, client } = this;
|
||||
|
||||
// asking to include Series Date, Series Time, Series Description
|
||||
// and Series Number in the series metadata returned to better sort series
|
||||
// in preLoad function
|
||||
let options = {
|
||||
studyInstanceUID,
|
||||
queryParams: {
|
||||
includefield: includeField,
|
||||
},
|
||||
};
|
||||
|
||||
if (seriesInstanceUID) {
|
||||
options.queryParams.SeriesInstanceUID = seriesInstanceUID;
|
||||
preLoaders.push(client.searchForSeries.bind(client, options));
|
||||
}
|
||||
// Fallback preloader
|
||||
preLoaders.push(client.searchForSeries.bind(client, options));
|
||||
|
||||
yield* preLoaders;
|
||||
}
|
||||
|
||||
async preLoad() {
|
||||
const preLoaders = this.getPreLoaders();
|
||||
const result = await this.runLoaders(preLoaders);
|
||||
const sortCriteria = this.sortCriteria;
|
||||
const sortFunction = this.sortFunction;
|
||||
|
||||
const { naturalizeDataset } = dcmjs.data.DicomMetaDictionary;
|
||||
const naturalized = result.map(naturalizeDataset);
|
||||
|
||||
return sortStudySeries(naturalized, sortCriteria, sortFunction);
|
||||
}
|
||||
|
||||
async load(preLoadData) {
|
||||
const { client, studyInstanceUID } = this;
|
||||
|
||||
const seriesInstanceUIDs = preLoadData.map(seriesMetadata => {
|
||||
return { seriesInstanceUID: seriesMetadata.SeriesInstanceUID, metadata: seriesMetadata };
|
||||
});
|
||||
|
||||
const seriesAsyncLoader = makeSeriesAsyncLoader(client, studyInstanceUID, seriesInstanceUIDs);
|
||||
|
||||
const promises = [];
|
||||
|
||||
while (seriesAsyncLoader.hasNext()) {
|
||||
const promise = seriesAsyncLoader.next();
|
||||
promises.push(promise);
|
||||
}
|
||||
|
||||
return {
|
||||
preLoadData,
|
||||
promises,
|
||||
};
|
||||
}
|
||||
|
||||
async posLoad({ preLoadData, promises }) {
|
||||
return {
|
||||
preLoadData,
|
||||
promises,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// import { api } from 'dicomweb-client';
|
||||
// import DICOMWeb from '../../../DICOMWeb/';
|
||||
import { createStudyFromSOPInstanceList } from './studyInstanceHelpers';
|
||||
import RetrieveMetadataLoader from './retrieveMetadataLoader';
|
||||
|
||||
/**
|
||||
* Class for sync load of study metadata.
|
||||
* It inherits from RetrieveMetadataLoader
|
||||
*
|
||||
* A list of loaders (getLoaders) can be created so, it will be applied a fallback load strategy.
|
||||
* I.e Retrieve metadata using all loaders possibilities.
|
||||
*/
|
||||
export default class RetrieveMetadataLoaderSync extends RetrieveMetadataLoader {
|
||||
getOptions() {
|
||||
const { studyInstanceUID, filters } = this;
|
||||
|
||||
const options = {
|
||||
studyInstanceUID,
|
||||
};
|
||||
|
||||
const { seriesInstanceUID } = filters;
|
||||
if (seriesInstanceUID) {
|
||||
options['seriesInstanceUID'] = seriesInstanceUID;
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Array} Array of loaders. To be consumed as queue
|
||||
*/
|
||||
*getLoaders() {
|
||||
const loaders = [];
|
||||
const { studyInstanceUID, filters: { seriesInstanceUID } = {}, client } = this;
|
||||
|
||||
if (seriesInstanceUID) {
|
||||
loaders.push(
|
||||
client.retrieveSeriesMetadata.bind(client, {
|
||||
studyInstanceUID,
|
||||
seriesInstanceUID,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
loaders.push(client.retrieveStudyMetadata.bind(client, { studyInstanceUID }));
|
||||
|
||||
yield* loaders;
|
||||
}
|
||||
|
||||
async load(preLoadData) {
|
||||
const loaders = this.getLoaders();
|
||||
const result = this.runLoaders(loaders);
|
||||
return result;
|
||||
}
|
||||
|
||||
async posLoad(loadData) {
|
||||
return loadData;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user