From abd75b0ba1877c9ff70dc75f9a83f3b1ed3b93f1 Mon Sep 17 00:00:00 2001 From: Bill Wallace Date: Tue, 22 Nov 2022 09:09:24 -0500 Subject: [PATCH] feat(datasourcename):Allow specifying the datasourcename as a parameter (#3029) --- extensions/default/src/ViewerLayout/index.tsx | 15 +- platform/viewer/.webpack/webpack.pwa.js | 3 + platform/viewer/public/config/local_static.js | 5 +- platform/viewer/public/config/multiple.js | 183 ++++++++++++++++++ .../viewer/src/routes/DataSourceWrapper.tsx | 52 +++-- .../viewer/src/routes/WorkList/WorkList.tsx | 6 +- 6 files changed, 237 insertions(+), 27 deletions(-) create mode 100644 platform/viewer/public/config/multiple.js diff --git a/extensions/default/src/ViewerLayout/index.tsx b/extensions/default/src/ViewerLayout/index.tsx index fb5fb5503..3211dd350 100644 --- a/extensions/default/src/ViewerLayout/index.tsx +++ b/extensions/default/src/ViewerLayout/index.tsx @@ -2,6 +2,8 @@ import React, { useEffect, useState } from 'react'; import PropTypes from 'prop-types'; import { useNavigate } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; +import { useLocation } from 'react-router'; + import { SidePanel, ErrorBoundary, @@ -11,7 +13,6 @@ import { useModal, LoadingIndicatorProgress, } from '@ohif/ui'; - import i18n from '@ohif/i18n'; import { hotkeys } from '@ohif/core'; import { useAppConfig } from '@state'; @@ -35,9 +36,19 @@ function ViewerLayout({ }) { const [appConfig] = useAppConfig(); const navigate = useNavigate(); + const location = useLocation(); const onClickReturnButton = () => { - navigate('/'); + const { pathname } = location; + const dataSourceIdx = pathname.indexOf('/', 1); + const search = + dataSourceIdx === -1 + ? undefined + : `datasourcename=${pathname.substring(dataSourceIdx + 1)}`; + navigate({ + pathname: '/', + search, + }); }; const { t } = useTranslation(); diff --git a/platform/viewer/.webpack/webpack.pwa.js b/platform/viewer/.webpack/webpack.pwa.js index edeea8280..439810acf 100644 --- a/platform/viewer/.webpack/webpack.pwa.js +++ b/platform/viewer/.webpack/webpack.pwa.js @@ -143,6 +143,9 @@ module.exports = (env, argv) => { client: { overlay: { errors: true, warnings: false }, }, + proxy: { + '/dicomweb': 'http://localhost:5000', + }, 'static': [ { directory: '../../testdata', diff --git a/platform/viewer/public/config/local_static.js b/platform/viewer/public/config/local_static.js index 784579cb9..9fcc36225 100644 --- a/platform/viewer/public/config/local_static.js +++ b/platform/viewer/public/config/local_static.js @@ -16,9 +16,8 @@ window.config = { sourceName: 'dicomweb', configuration: { name: 'DCM4CHEE', - wadoUriRoot: 'http://localhost:5000/dicomweb', - qidoRoot: 'http://localhost:5000/dicomweb', - wadoRoot: 'http://localhost:5000/dicomweb', + qidoRoot: '/dicomweb', + wadoRoot: '/dicomweb', qidoSupportsIncludeField: false, supportsReject: false, imageRendering: 'wadors', diff --git a/platform/viewer/public/config/multiple.js b/platform/viewer/public/config/multiple.js new file mode 100644 index 000000000..0ea0ac499 --- /dev/null +++ b/platform/viewer/public/config/multiple.js @@ -0,0 +1,183 @@ +window.config = { + routerBasename: '/', + customizationService: [ + ], + extensions: [], + modes: [], + showStudyList: true, + maxNumberOfWebWorkers: 4, + // below flag is for performance reasons, but it might not work for all servers + omitQuotationForMultipartRequest: true, + showLoadingIndicator: true, + // filterQueryParam: false, + dataSources: [ + { + friendlyName: 'Static WADO Local Data', + namespace: '@ohif/extension-default.dataSourcesModule.dicomweb', + sourceName: 'default', + configuration: { + name: 'DCM4CHEE', + qidoRoot: '/dicomweb', + wadoRoot: '/dicomweb', + qidoSupportsIncludeField: false, + supportsReject: false, + imageRendering: 'wadors', + thumbnailRendering: 'wadors', + enableStudyLazyLoad: true, + supportsFuzzyMatching: false, + supportsWildcard: true, + staticWado: true, + singlepart: 'bulkdata,video,pdf', + }, + }, + { + friendlyName: 'AWS S3 OHIF', + namespace: '@ohif/extension-default.dataSourcesModule.dicomweb', + sourceName: 'aws', + configuration: { + name: 'aws', + qidoRoot: 'https://viewer.flexview.ai/dicomweb', + wadoRoot: 'https://viewer.flexview.ai/dicomweb', + qidoSupportsIncludeField: false, + supportsReject: false, + imageRendering: 'wadors', + thumbnailRendering: 'wadors', + enableStudyLazyLoad: true, + supportsFuzzyMatching: false, + supportsWildcard: true, + staticWado: true, + singlepart: 'bulkdata,video,pdf', + }, + }, + { + friendlyName: 'E2E Test Data', + namespace: '@ohif/extension-default.dataSourcesModule.dicomweb', + sourceName: 'e2e', + configuration: { + name: 'DCM4CHEE', + wadoUriRoot: '/viewer-testdata', + qidoRoot: '/viewer-testdata', + wadoRoot: '/viewer-testdata', + qidoSupportsIncludeField: false, + supportsReject: false, + imageRendering: 'wadors', + thumbnailRendering: 'wadors', + enableStudyLazyLoad: true, + supportsFuzzyMatching: false, + supportsWildcard: true, + staticWado: true, + singlepart: 'video,thumbnail,pdf', + }, + }, + { + friendlyName: 'dicom json', + namespace: '@ohif/extension-default.dataSourcesModule.dicomjson', + sourceName: 'dicomjson', + configuration: { + name: 'json', + }, + }, + { + friendlyName: 'dicom local', + namespace: '@ohif/extension-default.dataSourcesModule.dicomlocal', + sourceName: 'dicomlocal', + configuration: {}, + }, + ], + httpErrorHandler: error => { + // This is 429 when rejected from the public idc sandbox too often. + console.warn(error.status); + + // Could use services manager here to bring up a dialog/modal if needed. + console.warn('test, navigate to https://ohif.org/'); + }, + defaultDataSourceName: 'default', + hotkeys: [ + { + commandName: 'incrementActiveViewport', + label: 'Next Viewport', + keys: ['right'], + }, + { + commandName: 'decrementActiveViewport', + label: 'Previous Viewport', + keys: ['left'], + }, + { commandName: 'rotateViewportCW', label: 'Rotate Right', keys: ['r'] }, + { commandName: 'rotateViewportCCW', label: 'Rotate Left', keys: ['l'] }, + { commandName: 'invertViewport', label: 'Invert', keys: ['i'] }, + { + commandName: 'flipViewportHorizontal', + label: 'Flip Horizontally', + keys: ['h'], + }, + { + commandName: 'flipViewportVertical', + label: 'Flip Vertically', + keys: ['v'], + }, + { commandName: 'scaleUpViewport', label: 'Zoom In', keys: ['+'] }, + { commandName: 'scaleDownViewport', label: 'Zoom Out', keys: ['-'] }, + { commandName: 'fitViewportToWindow', label: 'Zoom to Fit', keys: ['='] }, + { commandName: 'resetViewport', label: 'Reset', keys: ['space'] }, + { commandName: 'nextImage', label: 'Next Image', keys: ['down'] }, + { commandName: 'previousImage', label: 'Previous Image', keys: ['up'] }, + // { + // commandName: 'previousViewportDisplaySet', + // label: 'Previous Series', + // keys: ['pagedown'], + // }, + // { + // commandName: 'nextViewportDisplaySet', + // label: 'Next Series', + // keys: ['pageup'], + // }, + { commandName: 'setZoomTool', label: 'Zoom', keys: ['z'] }, + // ~ Window level presets + { + commandName: 'windowLevelPreset1', + label: 'W/L Preset 1', + keys: ['1'], + }, + { + commandName: 'windowLevelPreset2', + label: 'W/L Preset 2', + keys: ['2'], + }, + { + commandName: 'windowLevelPreset3', + label: 'W/L Preset 3', + keys: ['3'], + }, + { + commandName: 'windowLevelPreset4', + label: 'W/L Preset 4', + keys: ['4'], + }, + { + commandName: 'windowLevelPreset5', + label: 'W/L Preset 5', + keys: ['5'], + }, + { + commandName: 'windowLevelPreset6', + label: 'W/L Preset 6', + keys: ['6'], + }, + { + commandName: 'windowLevelPreset7', + label: 'W/L Preset 7', + keys: ['7'], + }, + { + commandName: 'windowLevelPreset8', + label: 'W/L Preset 8', + keys: ['8'], + }, + { + commandName: 'windowLevelPreset9', + label: 'W/L Preset 9', + keys: ['9'], + }, + ], +}; diff --git a/platform/viewer/src/routes/DataSourceWrapper.tsx b/platform/viewer/src/routes/DataSourceWrapper.tsx index 60a22eeec..951e5dba8 100644 --- a/platform/viewer/src/routes/DataSourceWrapper.tsx +++ b/platform/viewer/src/routes/DataSourceWrapper.tsx @@ -19,29 +19,38 @@ function DataSourceWrapper(props) { const params = useParams(); const location = useLocation(); - // TODO: Fetch by type, name, etc? - const dataSourceModules = extensionManager.modules[MODULE_TYPES.DATA_SOURCE]; - // TODO: Good usecase for flatmap? - const webApiDataSources = dataSourceModules.reduce((acc, curr) => { - const mods = []; - curr.module.forEach(mod => { - if (mod.type === 'webApi') { - mods.push(mod); - } - }); - return acc.concat(mods); - }, []); + // TODO - get the variable from the props all the time... + let dataSourceName = new URLSearchParams(location.search).get( + 'datasourcename' + ); + const dataPath = dataSourceName ? `/${dataSourceName}` : ''; - // Grabbing first defined for now - should get active - // TODO: Why does this return an array? - const dataSource = webApiDataSources - .map(ds => extensionManager.getDataSources(ds.name)?.[0]) - .find(it => it !== undefined); - if (!dataSource) { - throw new Error( - `No data source found for any of ${webApiDataSources.map(it => it.name)}` - ); + if (!dataSourceName && window.config.defaultDataSourceName) { + dataSourceName = window.config.defaultDataSourceName; + } else if (!dataSourceName) { + // Gets the first defined datasource with the right name + // Mostly for historical reasons - new configs should use the defaultDataSourceName + const dataSourceModules = + extensionManager.modules[MODULE_TYPES.DATA_SOURCE]; + // TODO: Good usecase for flatmap? + const webApiDataSources = dataSourceModules.reduce((acc, curr) => { + const mods = []; + curr.module.forEach(mod => { + if (mod.type === 'webApi') { + mods.push(mod); + } + }); + return acc.concat(mods); + }, []); + dataSourceName = webApiDataSources + .map(ds => ds.name) + .find(it => extensionManager.getDataSources(it)?.[0] !== undefined); } + const dataSource = extensionManager.getDataSources(dataSourceName)?.[0]; + if (!dataSource) { + throw new Error(`No data source found for ${dataSourceName}`); + } + // Route props --> studies.mapParams // mapParams --> studies.search // studies.search --> studies.processResults @@ -114,6 +123,7 @@ function DataSourceWrapper(props) {