From a046c6c9017a84e5ead71d5113b5e892333b4130 Mon Sep 17 00:00:00 2001 From: Bill Wallace Date: Mon, 4 Oct 2021 03:30:36 -0400 Subject: [PATCH] feat: Added support for WADO servers built with static hosting (e.g. S3) (#2566) --- .../studies/services/qido/StaticWadoClient.js | 85 +++++++++++++++++++ .../core/src/studies/services/qido/studies.js | 7 +- .../wado/retrieveMetadataLoaderAsync.js | 5 +- platform/viewer/public/config/local_static.js | 20 +++++ 4 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 platform/core/src/studies/services/qido/StaticWadoClient.js create mode 100644 platform/viewer/public/config/local_static.js diff --git a/platform/core/src/studies/services/qido/StaticWadoClient.js b/platform/core/src/studies/services/qido/StaticWadoClient.js new file mode 100644 index 000000000..41f973911 --- /dev/null +++ b/platform/core/src/studies/services/qido/StaticWadoClient.js @@ -0,0 +1,85 @@ +import { api } from 'dicomweb-client'; + +/** + * An implementation of the static wado client, that fetches data from + * a static response rather than actually doing real queries. This allows + * fast encoding of test data, but because it is static, anything actually + * performing searches doesn't work. This version fixes the query issue + * by manually implementing a query option. + */ +export default class StaticWadoClient extends api.DICOMwebClient { + static filterKeys = { + StudyInstanceUID: '0020000D', + PatientName: '00100010', + PatientID: '00100020', + StudyDescription: '00081030', + ModalitiesInStudy: '00080061', + }; + + constructor(qidoConfig) { + super(qidoConfig); + this.staticWado = qidoConfig.staticWado; + this.extendMetadataWithInstances = qidoConfig.extendMetadataWithInstances; + } + + async retrieveSeriesMetadata(options) { + if (!this.extendMetadataWithInstances) + return super.retrieveSeriesMetadata(options); + const results = await Promise.all([ + super.retrieveSeriesMetadata(options), + this.searchForInstances(options), + ]); + const metadata = results[0]; + const instances = results[1]; + return metadata.map(item => { + const sopUID = item['00080018'].Value[0]; + const instance = instances.find( + instance => instance['00080018'].Value[0] == sopUID + ); + Object.assign(item, instance); + return item; + }); + } + + async searchForStudies(options) { + if (!this.staticWado) return super.searchForStudies(options); + + let searchResult = await super.searchForStudies(options); + const { queryParams } = options; + if (!queryParams) return searchResult; + const filtered = searchResult.filter(study => { + for (const key of Object.keys(StaticWadoClient.filterKeys)) { + if (!this.filterItem(key, queryParams, study)) return false; + } + return true; + }); + return filtered; + } + + filterItem(key, queryParams, study) { + const altKey = StaticWadoClient.filterKeys[key] || key; + if (!queryParams) return true; + const testValue = queryParams[key] || queryParams[altKey]; + if (!testValue) return true; + const valueElem = study[key] || study[altKey]; + if (!valueElem) return false; + const value = valueElem.Value; + return this.eqItem(testValue, value); + } + + eqItem(testValue, value) { + if (testValue.filter) { + return ( + testValue.filter(testItem => this.eqItem(testItem, value)).length > 0 + ); + } + if (value === testValue) return true; + if (typeof value == 'string') { + return value.indexOf(testValue) != -1; + } + if (value.Alphabetic) return this.eqItem(testValue, value.Alphabetic); + if (value.filter) + return value.filter(item => this.eqItem(testValue, item)).length > 0; + return false; + } +} diff --git a/platform/core/src/studies/services/qido/studies.js b/platform/core/src/studies/services/qido/studies.js index 7a66c88f4..0e5c6ad1d 100644 --- a/platform/core/src/studies/services/qido/studies.js +++ b/platform/core/src/studies/services/qido/studies.js @@ -1,4 +1,5 @@ import { api } from 'dicomweb-client'; +import StaticWadoClient from './StaticWadoClient'; import DICOMWeb from '../../../DICOMWeb/'; import errorHandler from '../../../errorHandler'; @@ -115,14 +116,18 @@ function resultDataToStudies(resultData) { } export default function Studies(server, filter) { + const { staticWado } = server; const config = { + ...server, url: server.qidoRoot, headers: DICOMWeb.getAuthorizationHeader(server), errorInterceptor: errorHandler.getHTTPErrorHandler(), requestHooks: [getXHRRetryRequestHook()], }; - const dicomWeb = new api.DICOMwebClient(config); + const dicomWeb = staticWado + ? new StaticWadoClient(config) + : new api.DICOMwebClient(config); server.qidoSupportsIncludeField = server.qidoSupportsIncludeField === undefined ? true diff --git a/platform/core/src/studies/services/wado/retrieveMetadataLoaderAsync.js b/platform/core/src/studies/services/wado/retrieveMetadataLoaderAsync.js index 8e8830239..c22a33323 100644 --- a/platform/core/src/studies/services/wado/retrieveMetadataLoaderAsync.js +++ b/platform/core/src/studies/services/wado/retrieveMetadataLoaderAsync.js @@ -1,4 +1,4 @@ -import { api } from 'dicomweb-client'; +import StaticWadoClient from '../qido/StaticWadoClient'; import dcmjs from 'dcmjs'; import DICOMWeb from '../../../DICOMWeb/'; import RetrieveMetadataLoader from './retrieveMetadataLoader'; @@ -73,7 +73,8 @@ export default class RetrieveMetadataLoaderAsync extends RetrieveMetadataLoader configLoad() { const { server } = this; - const client = new api.DICOMwebClient({ + const client = new StaticWadoClient({ + ...server, url: server.qidoRoot, headers: DICOMWeb.getAuthorizationHeader(server), errorInterceptor: errorHandler.getHTTPErrorHandler(), diff --git a/platform/viewer/public/config/local_static.js b/platform/viewer/public/config/local_static.js new file mode 100644 index 000000000..672454fb8 --- /dev/null +++ b/platform/viewer/public/config/local_static.js @@ -0,0 +1,20 @@ +window.config = { + routerBasename: '/', + showStudyList: true, + servers: { + dicomWeb: [ + { + name: 'LocalStatic', + wadoUriRoot: 'http://localhost:5000/', + qidoRoot: 'http://localhost:5000', + wadoRoot: 'http://localhost:5000/', + qidoSupportsIncludeField: false, + imageRendering: 'wadors', + thumbnailRendering: 'thumbnail', + enableStudyLazyLoad: true, + supportsFuzzyMatching: false, + staticWado: true, + }, + ], + }, +};