From f05538f8538673b6aac84dd88d01b2add71ce4b9 Mon Sep 17 00:00:00 2001 From: Joe Boccanfuso Date: Mon, 28 Aug 2023 10:50:25 -0400 Subject: [PATCH 1/2] feat(data source UI config): Popup the configuration dialogue whenever a data source is not fully configured. --- .../DataSourceConfigurationComponent.tsx | 40 +++++++++++++------ .../DataSourceConfigurationModalComponent.tsx | 11 +++-- .../GoogleCloudDataSourceConfigurationAPI.ts | 7 +++- platform/app/src/routes/DataSourceWrapper.tsx | 12 +++++- 4 files changed, 52 insertions(+), 18 deletions(-) diff --git a/extensions/default/src/Components/DataSourceConfigurationComponent.tsx b/extensions/default/src/Components/DataSourceConfigurationComponent.tsx index 768f5569638..69d4c08ab7b 100644 --- a/extensions/default/src/Components/DataSourceConfigurationComponent.tsx +++ b/extensions/default/src/Components/DataSourceConfigurationComponent.tsx @@ -1,4 +1,4 @@ -import React, { ReactElement, useEffect, useState } from 'react'; +import React, { ReactElement, useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Icon, useModal } from '@ohif/ui'; import { ExtensionManager, ServicesManager, Types } from '@ohif/core'; @@ -48,6 +48,9 @@ function DataSourceConfigurationComponent({ const configAPI = configurationAPIFactory(activeDataSourceDef.sourceName); setConfigurationAPI(configAPI); + // New configuration API means that the existing configured items must be cleared. + setConfiguredItems(undefined); + configAPI.getConfiguredItems().then(list => { if (shouldUpdate) { setConfiguredItems(list); @@ -68,22 +71,35 @@ function DataSourceConfigurationComponent({ }; }, []); + const showConfigurationModal = useCallback(() => { + show({ + content: DataSourceConfigurationModalComponent, + title: t('Configure Data Source'), + contentProps: { + configurationAPI, + configuredItems, + onHide: hide, + }, + }); + }, [configurationAPI, configuredItems]); + + useEffect(() => { + if (!configurationAPI || !configuredItems) { + return; + } + + if (configuredItems.length !== configurationAPI.getItemLabels().length) { + // Not the correct number of configured items, so show the modal to configure the data source. + showConfigurationModal(); + } + }, [configurationAPI, configuredItems, showConfigurationModal]); + return configuredItems ? (
- show({ - content: DataSourceConfigurationModalComponent, - title: t('Configure Data Source'), - contentProps: { - configurationAPI, - configuredItems, - onHide: hide, - }, - }) - } + onClick={showConfigurationModal} > {configuredItems.map((item, itemIndex) => { return ( diff --git a/extensions/default/src/Components/DataSourceConfigurationModalComponent.tsx b/extensions/default/src/Components/DataSourceConfigurationModalComponent.tsx index 74145d3b0b2..5ad0c057761 100644 --- a/extensions/default/src/Components/DataSourceConfigurationModalComponent.tsx +++ b/extensions/default/src/Components/DataSourceConfigurationModalComponent.tsx @@ -27,14 +27,17 @@ function DataSourceConfigurationModalComponent({ const [selectedItems, setSelectedItems] = useState(configuredItems); - // Determines whether to show the full configuration for the data source. - // This typically occurs when the configuration component is first displayed. - const [showFullConfig, setShowFullConfig] = useState(true); - const [errorMessage, setErrorMessage] = useState(); const [itemLabels] = useState(configurationAPI.getItemLabels()); + // Determines whether to show the full/existing configuration for the data source. + // If the data source is fully configured, then it is shown, otherwise + // configuration starts from scratch. + const [showFullConfig, setShowFullConfig] = useState( + itemLabels.length === configuredItems.length + ); + /** * The index of the selected item that is considered current and for which * its sub-items should be displayed in the items list component. When the diff --git a/extensions/default/src/DataSourceConfigurationAPI/GoogleCloudDataSourceConfigurationAPI.ts b/extensions/default/src/DataSourceConfigurationAPI/GoogleCloudDataSourceConfigurationAPI.ts index d95cbd8ead7..b293242dd65 100644 --- a/extensions/default/src/DataSourceConfigurationAPI/GoogleCloudDataSourceConfigurationAPI.ts +++ b/extensions/default/src/DataSourceConfigurationAPI/GoogleCloudDataSourceConfigurationAPI.ts @@ -148,7 +148,12 @@ class GoogleCloudDataSourceConfigurationAPI const urlSplit = url.substring(projectsIndex).split('/'); const configuredItems = []; - for (let itemType = 0; itemType < 4; itemType += 1) { + + for ( + let itemType = 0; + itemType < 4 && (itemType + 1) * 2 < urlSplit.length; + itemType += 1 + ) { if (itemType === ItemType.projects) { const projectId = urlSplit[1]; const projectUrl = `${initialUrl}/projects/${projectId}`; diff --git a/platform/app/src/routes/DataSourceWrapper.tsx b/platform/app/src/routes/DataSourceWrapper.tsx index c137546d997..91ae5864247 100644 --- a/platform/app/src/routes/DataSourceWrapper.tsx +++ b/platform/app/src/routes/DataSourceWrapper.tsx @@ -125,6 +125,7 @@ function DataSourceWrapper(props) { useEffect(() => { const dataSourceChangedCallback = () => { + setIsLoading(false); setIsDataSourceInitialized(false); setDataSourcePath(''); setDataSource(extensionManager.getActiveDataSource()[0]); @@ -191,7 +192,16 @@ function DataSourceWrapper(props) { (!isLoading && (newOffset !== previousOffset || isLocationUpdated)); if (isDataInvalid) { - getData().catch(() => navigate('/notfoundserver', '_self')); + getData().catch(() => { + // If there is a data source configuration API, then the Worklist will popup the dialog to attempt to configure it + // and attempt to resolve this issue. + if (dataSource.getConfig().configurationAPI) { + return; + } + + // No data source configuration API, so navigate to the not found server page. + navigate('/notfoundserver', '_self'); + }); } } catch (ex) { console.warn(ex); From d46742b2dbd81f9ec7d6bfc4b68a56ddf8d0a2ef Mon Sep 17 00:00:00 2001 From: Joe Boccanfuso Date: Tue, 29 Aug 2023 12:34:54 -0400 Subject: [PATCH 2/2] PR feedback. Updated the cornerstone version. --- extensions/cornerstone-dicom-sr/package.json | 6 +-- extensions/cornerstone/package.json | 10 ++-- .../DataSourceConfigurationComponent.tsx | 2 +- .../DataSourceConfigurationModalComponent.tsx | 6 ++- .../GoogleCloudDataSourceConfigurationAPI.ts | 4 ++ extensions/measurement-tracking/package.json | 4 +- platform/app/package.json | 2 +- platform/core/package.json | 2 +- yarn.lock | 46 +++++++++---------- 9 files changed, 44 insertions(+), 38 deletions(-) diff --git a/extensions/cornerstone-dicom-sr/package.json b/extensions/cornerstone-dicom-sr/package.json index 647272d5cda..9227c37c42f 100644 --- a/extensions/cornerstone-dicom-sr/package.json +++ b/extensions/cornerstone-dicom-sr/package.json @@ -44,9 +44,9 @@ }, "dependencies": { "@babel/runtime": "^7.20.13", - "@cornerstonejs/adapters": "^1.11.1", - "@cornerstonejs/core": "^1.11.1", - "@cornerstonejs/tools": "^1.11.1", + "@cornerstonejs/adapters": "^1.11.4", + "@cornerstonejs/core": "^1.11.4", + "@cornerstonejs/tools": "^1.11.4", "classnames": "^2.3.2" } } diff --git a/extensions/cornerstone/package.json b/extensions/cornerstone/package.json index 191ca19b0ec..e1a6fb7a39f 100644 --- a/extensions/cornerstone/package.json +++ b/extensions/cornerstone/package.json @@ -36,7 +36,7 @@ "@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2", "@cornerstonejs/codec-openjpeg": "^1.2.2", "@cornerstonejs/codec-openjph": "^2.4.2", - "@cornerstonejs/dicom-image-loader": "^1.11.1", + "@cornerstonejs/dicom-image-loader": "^1.11.4", "@ohif/core": "3.7.0-beta.58", "@ohif/ui": "3.7.0-beta.58", "dcmjs": "^0.29.6", @@ -52,10 +52,10 @@ }, "dependencies": { "@babel/runtime": "^7.20.13", - "@cornerstonejs/adapters": "^1.11.1", - "@cornerstonejs/core": "^1.11.1", - "@cornerstonejs/streaming-image-volume-loader": "^1.11.1", - "@cornerstonejs/tools": "^1.11.1", + "@cornerstonejs/adapters": "^1.11.4", + "@cornerstonejs/core": "^1.11.4", + "@cornerstonejs/streaming-image-volume-loader": "^1.11.4", + "@cornerstonejs/tools": "^1.11.4", "@kitware/vtk.js": "27.3.1", "html2canvas": "^1.4.1", "lodash.debounce": "4.0.8", diff --git a/extensions/default/src/Components/DataSourceConfigurationComponent.tsx b/extensions/default/src/Components/DataSourceConfigurationComponent.tsx index 69d4c08ab7b..c9606c54ecd 100644 --- a/extensions/default/src/Components/DataSourceConfigurationComponent.tsx +++ b/extensions/default/src/Components/DataSourceConfigurationComponent.tsx @@ -49,7 +49,7 @@ function DataSourceConfigurationComponent({ setConfigurationAPI(configAPI); // New configuration API means that the existing configured items must be cleared. - setConfiguredItems(undefined); + setConfiguredItems(null); configAPI.getConfiguredItems().then(list => { if (shouldUpdate) { diff --git a/extensions/default/src/Components/DataSourceConfigurationModalComponent.tsx b/extensions/default/src/Components/DataSourceConfigurationModalComponent.tsx index 5ad0c057761..b10267740eb 100644 --- a/extensions/default/src/Components/DataSourceConfigurationModalComponent.tsx +++ b/extensions/default/src/Components/DataSourceConfigurationModalComponent.tsx @@ -32,8 +32,10 @@ function DataSourceConfigurationModalComponent({ const [itemLabels] = useState(configurationAPI.getItemLabels()); // Determines whether to show the full/existing configuration for the data source. - // If the data source is fully configured, then it is shown, otherwise - // configuration starts from scratch. + // A full or complete configuration is one where the data source (path) has the + // maximum/required number of path items. Anything less is considered not complete and + // the configuration starts from scratch (i.e. as if no items are configured at all). + // TODO: consider configuration starting from a partial (i.e. non-empty) configuration const [showFullConfig, setShowFullConfig] = useState( itemLabels.length === configuredItems.length ); diff --git a/extensions/default/src/DataSourceConfigurationAPI/GoogleCloudDataSourceConfigurationAPI.ts b/extensions/default/src/DataSourceConfigurationAPI/GoogleCloudDataSourceConfigurationAPI.ts index b293242dd65..91959237975 100644 --- a/extensions/default/src/DataSourceConfigurationAPI/GoogleCloudDataSourceConfigurationAPI.ts +++ b/extensions/default/src/DataSourceConfigurationAPI/GoogleCloudDataSourceConfigurationAPI.ts @@ -145,12 +145,16 @@ class GoogleCloudDataSourceConfigurationAPI const url = dataSourceDefinition.configuration.wadoUriRoot; const projectsIndex = url.indexOf('projects'); + // Split the configured URL into (essentially) pairs (i.e. item type followed by item) + // Explicitly: ['projects','aProject','locations','aLocation','datasets','aDataSet','dicomStores','aDicomStore'] + // Note that a partial configuration will have a subset of the above. const urlSplit = url.substring(projectsIndex).split('/'); const configuredItems = []; for ( let itemType = 0; + // the number of configured items is either the max (4) or the number extracted from the url split itemType < 4 && (itemType + 1) * 2 < urlSplit.length; itemType += 1 ) { diff --git a/extensions/measurement-tracking/package.json b/extensions/measurement-tracking/package.json index dcf48fca40b..55412e345a8 100644 --- a/extensions/measurement-tracking/package.json +++ b/extensions/measurement-tracking/package.json @@ -30,8 +30,8 @@ "start": "yarn run dev" }, "peerDependencies": { - "@cornerstonejs/core": "^1.11.1", - "@cornerstonejs/tools": "^1.11.1", + "@cornerstonejs/core": "^1.11.4", + "@cornerstonejs/tools": "^1.11.4", "@ohif/core": "3.7.0-beta.58", "@ohif/extension-cornerstone-dicom-sr": "3.7.0-beta.58", "@ohif/ui": "3.7.0-beta.58", diff --git a/platform/app/package.json b/platform/app/package.json index 57d8960aaf6..44a63b6046d 100644 --- a/platform/app/package.json +++ b/platform/app/package.json @@ -50,7 +50,7 @@ "@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2", "@cornerstonejs/codec-openjpeg": "^1.2.2", "@cornerstonejs/codec-openjph": "^2.4.2", - "@cornerstonejs/dicom-image-loader": "^1.11.1", + "@cornerstonejs/dicom-image-loader": "^1.11.4", "@ohif/core": "3.7.0-beta.58", "@ohif/extension-cornerstone": "3.7.0-beta.58", "@ohif/extension-cornerstone-dicom-rt": "3.7.0-beta.58", diff --git a/platform/core/package.json b/platform/core/package.json index a2433f244e2..cd4140a0d37 100644 --- a/platform/core/package.json +++ b/platform/core/package.json @@ -35,7 +35,7 @@ "@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2", "@cornerstonejs/codec-openjpeg": "^1.2.2", "@cornerstonejs/codec-openjph": "^2.4.2", - "@cornerstonejs/dicom-image-loader": "^1.11.1", + "@cornerstonejs/dicom-image-loader": "^1.11.4", "@ohif/ui": "3.7.0-beta.58", "cornerstone-math": "0.1.9", "dicom-parser": "^1.8.21" diff --git a/yarn.lock b/yarn.lock index 583f8723670..24e1a3e59e3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1376,10 +1376,10 @@ resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== -"@cornerstonejs/adapters@^1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@cornerstonejs/adapters/-/adapters-1.11.1.tgz#d6c2b0dc0742a0ffd76e9ed86f911bbd8bc94c05" - integrity sha512-ggSToAWv6fn7J4mq412P6xGV5IdbtHqRR4ZGap12ytus9HCCZLZ3a+lWx21+Rc9zILj8H/58WWb/pYvN0Itp5g== +"@cornerstonejs/adapters@^1.11.4": + version "1.11.4" + resolved "https://registry.yarnpkg.com/@cornerstonejs/adapters/-/adapters-1.11.4.tgz#b318fe3180b7ceda5537420e8cd4e8d2ddac68f3" + integrity sha512-Qb7qfg0HD06yvpu8KgXkstvmrTUIbFft2HWblUbLe0gxO3ze+1LE2ZAT2qXyvC+nfchPTH2Ybo1D3yBxO7CPdQ== dependencies: "@babel/runtime-corejs2" "^7.17.8" buffer "^6.0.3" @@ -1428,43 +1428,43 @@ resolved "https://registry.yarnpkg.com/@cornerstonejs/codec-openjph/-/codec-openjph-2.4.2.tgz#e96721d56f6ec96f7f95c16321d88cc8467d8d81" integrity sha512-lgdvBvvNezleY+4pIe2ceUsJzlZe/0PipdeubQ3vZZOz3xxtHHMR1XFCl4fgd8gosR8COHuD7h6q+MwgrwBsng== -"@cornerstonejs/core@^1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@cornerstonejs/core/-/core-1.11.1.tgz#308904eabcd54bf8529ba174ff714a2aa7cd0be7" - integrity sha512-QgGVRiS2ceDSC0kZlYyYw6owqh6rn7FaYuyJAYis1JCk1yG6xIUfcb6GMuubuEgEammc26i8EfU1H5FLxpHm2g== +"@cornerstonejs/core@^1.11.4": + version "1.11.4" + resolved "https://registry.yarnpkg.com/@cornerstonejs/core/-/core-1.11.4.tgz#035a950a04641a103924ab93b30f257fd884fc8c" + integrity sha512-5kS6xgBimkfjjvd/o+6o4c0NvYVw2lCGDvow3hC5V7aCd6Pn8eo2BaU45QJ8y0A0NJ9xmtr37yb3v8+7lfkGSw== dependencies: "@kitware/vtk.js" "27.3.1" detect-gpu "^5.0.22" gl-matrix "^3.4.3" lodash.clonedeep "4.5.0" -"@cornerstonejs/dicom-image-loader@^1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@cornerstonejs/dicom-image-loader/-/dicom-image-loader-1.11.1.tgz#0a7786b97828038f6faca088b40cc2b96c7db362" - integrity sha512-5/ocRRoYiwKKcKR01oGwWQp3imPEEYJcHull7MuwMWS1mZgYvok0QGs5Wz51rY8qJZCuM7nvkKxI21YzQkxISw== +"@cornerstonejs/dicom-image-loader@^1.11.4": + version "1.11.4" + resolved "https://registry.yarnpkg.com/@cornerstonejs/dicom-image-loader/-/dicom-image-loader-1.11.4.tgz#4daa4f91e9a5f23fa8f39ebb3c73dcc148184b68" + integrity sha512-Hw5XS+SN7VF/C70CzYd8alK/91SccSbNArVc9erwrBP4EmLWOj9XmKMNiV7KXS7d60G0nEAzCKo+e0wQWOvxWA== dependencies: "@cornerstonejs/codec-charls" "^1.2.3" "@cornerstonejs/codec-libjpeg-turbo-8bit" "^1.2.2" "@cornerstonejs/codec-openjpeg" "^1.2.2" "@cornerstonejs/codec-openjph" "^2.4.2" - "@cornerstonejs/core" "^1.11.1" + "@cornerstonejs/core" "^1.11.4" dicom-parser "^1.8.9" pako "^2.0.4" uuid "^9.0.0" -"@cornerstonejs/streaming-image-volume-loader@^1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@cornerstonejs/streaming-image-volume-loader/-/streaming-image-volume-loader-1.11.1.tgz#4f48b9af36da090f51e91b9736d892c4450244cd" - integrity sha512-S++wH3u037Gw5I51DWNwvqYaJl9W8vNhebTSzQIgZO3TVShzJo+0zQWdrsXrqOSzn9+0d4H6yEdxjKvsDRYBMw== +"@cornerstonejs/streaming-image-volume-loader@^1.11.4": + version "1.11.4" + resolved "https://registry.yarnpkg.com/@cornerstonejs/streaming-image-volume-loader/-/streaming-image-volume-loader-1.11.4.tgz#ca166f7488e8bba78e8e2216b5fc00e36ed2000b" + integrity sha512-gucQKRjU+UET1BGho4gOi0iy9iT0WoUnuXqb3zS9y7FrBr10VW6F51kbm+BaS0H0PTHqlC5nwHNrsxWavOqleg== dependencies: - "@cornerstonejs/core" "^1.11.1" + "@cornerstonejs/core" "^1.11.4" -"@cornerstonejs/tools@^1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@cornerstonejs/tools/-/tools-1.11.1.tgz#537caff00b53b7d37ea31d5aa67ec09298ce8057" - integrity sha512-kt6UTHT24PffiEDuHSTOiRTSfYHaluWTY27bigr0wdnrMYMlO0oignFXiSJAJ0FzLZUhuRZeN7qs1fLXhOlcrA== +"@cornerstonejs/tools@^1.11.4": + version "1.11.4" + resolved "https://registry.yarnpkg.com/@cornerstonejs/tools/-/tools-1.11.4.tgz#20ae1501d6f18346230f4b9bf62e23e30343407a" + integrity sha512-OLo69mbL2mgAUKuXSFLif+aJtbqT6hlWI5F6MsqgVun0BP1qLn2FgCfNwSphco4ejjiJG9CSnPBhfdcCxveaoA== dependencies: - "@cornerstonejs/core" "^1.11.1" + "@cornerstonejs/core" "^1.11.4" lodash.clonedeep "4.5.0" lodash.get "^4.4.2"