Compare commits
63
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5fa880a29 | ||
|
|
1b9a6db73f | ||
|
|
ed2692908e | ||
|
|
0c6c5a5d51 | ||
|
|
b449f8eb0e | ||
|
|
cf5160fb13 | ||
|
|
4c1c91c006 | ||
|
|
9ed39c149b | ||
|
|
b3f0fe029d | ||
|
|
a1b78afdb9 | ||
|
|
3d3cfb1b41 | ||
|
|
b812fe03d2 | ||
|
|
84f087bca4 | ||
|
|
a7e612eb88 | ||
|
|
9e3afda2f1 | ||
|
|
4d7a91a584 | ||
|
|
1dc320edcd | ||
|
|
1d02f5485f | ||
|
|
4c9a977920 | ||
|
|
b5db09c25e | ||
|
|
8dc4a5b82a | ||
|
|
44add9ce2f | ||
|
|
c19eceeede | ||
|
|
72bc5a5ae5 | ||
|
|
dfdf46dbdd | ||
|
|
571dff2c09 | ||
|
|
b9a66d4424 | ||
|
|
7ab2e3e60d | ||
|
|
a5d6bc6a51 | ||
|
|
df612ca4a0 | ||
|
|
a91ce57c43 | ||
|
|
342884f83e | ||
|
|
0fb39bad84 | ||
|
|
c74c80bb6f | ||
|
|
910d5c65d5 | ||
|
|
d0fb4843ab | ||
|
|
f488b857fe | ||
|
|
256e8b1116 | ||
|
|
168dd87747 | ||
|
|
13c41e6d86 | ||
|
|
7ab0bbb325 | ||
|
|
2213187b14 | ||
|
|
4c4d8a61b6 | ||
|
|
67f58f5f09 | ||
|
|
dfc35885bb | ||
|
|
e9c449d1db | ||
|
|
6dac50c7c6 | ||
|
|
9be54f6164 | ||
|
|
341cc5a4eb | ||
|
|
7b80171203 | ||
|
|
2e5a385e53 | ||
|
|
a3caeb381e | ||
|
|
ab0af86379 | ||
|
|
86426cb024 | ||
|
|
348840ef26 | ||
|
|
52ef934961 | ||
|
|
be995f93b6 | ||
|
|
5036615e00 | ||
|
|
4ee2b135a7 | ||
|
|
388fe04e09 | ||
|
|
2e215aed4c | ||
|
|
ffd27d6db4 | ||
|
|
6540e36818 |
No files matched your search
@@ -328,6 +328,7 @@ workflows:
|
||||
working_directory: platform/viewer
|
||||
build: npx cross-env QUICK_BUILD=true yarn run build
|
||||
start: yarn run test:e2e:serve
|
||||
spec: 'cypress/integration/common/**/*,cypress/integration/pwa/**/*'
|
||||
wait-on: 'http://localhost:3000'
|
||||
cache-key: 'yarn-packages-{{ checksum "yarn.lock" }}'
|
||||
no-workspace: true # Don't persist workspace
|
||||
@@ -352,7 +353,7 @@ workflows:
|
||||
working_directory: platform/viewer
|
||||
build: npx cross-env QUICK_BUILD=true yarn run build:package
|
||||
start: yarn run test:e2e:serve
|
||||
spec: 'cypress/integration/common/**/*'
|
||||
spec: 'cypress/integration/common/**/*,cypress/integration/script-tag/**/*'
|
||||
wait-on: 'http://localhost:3000'
|
||||
cache-key: 'yarn-packages-{{ checksum "yarn.lock" }}'
|
||||
no-workspace: true # Don't persist workspace
|
||||
|
||||
@@ -1,17 +1,31 @@
|
||||
#!/bin/bash
|
||||
|
||||
# If CLIENT_ID is specified, use the google.js configuration with the modified ID
|
||||
if [ ! -z "$CLIENT_ID" ]
|
||||
if [ -n "$CLIENT_ID" ] || [ -n "$HEALTHCARE_API_ENDPOINT" ]
|
||||
then
|
||||
echo "Google Cloud Healthcare $CLIENT_ID has been provided: "
|
||||
echo $CLIENT_ID
|
||||
echo "Updating config..."
|
||||
# If CLIENT_ID is specified, use the google.js configuration with the modified ID
|
||||
if [ -n "$CLIENT_ID" ]
|
||||
then
|
||||
echo "Google Cloud Healthcare \$CLIENT_ID has been provided: "
|
||||
echo "$CLIENT_ID"
|
||||
echo "Updating config..."
|
||||
|
||||
# - Use SED to replace the CLIENT_ID that is currently in google.js
|
||||
sed -i -e "s/YOURCLIENTID.apps.googleusercontent.com/$CLIENT_ID/g" /usr/share/nginx/html/google.js
|
||||
# - Use SED to replace the CLIENT_ID that is currently in google.js
|
||||
sed -i -e "s/YOURCLIENTID.apps.googleusercontent.com/$CLIENT_ID/g" /usr/share/nginx/html/google.js
|
||||
fi
|
||||
|
||||
# - Copy google.js to overwrite app-config.js
|
||||
cp /usr/share/nginx/html/google.js /usr/share/nginx/html/app-config.js
|
||||
# If HEALTHCARE_API_ENDPOINT is specified, use the google.js configuration with the modified endpoint
|
||||
if [ -n "$HEALTHCARE_API_ENDPOINT" ]
|
||||
then
|
||||
echo "Google Cloud Healthcare \$HEALTHCARE_API_ENDPOINT has been provided: "
|
||||
echo "$HEALTHCARE_API_ENDPOINT"
|
||||
echo "Updating config..."
|
||||
|
||||
# - Use SED to replace the HEALTHCARE_API_ENDPOINT that is currently in google.js
|
||||
sed -i -e "s+https://healthcare.googleapis.com/v1beta1+$HEALTHCARE_API_ENDPOINT+g" /usr/share/nginx/html/google.js
|
||||
fi
|
||||
|
||||
# - Copy google.js to overwrite app-config.js
|
||||
cp /usr/share/nginx/html/google.js /usr/share/nginx/html/app-config.js
|
||||
fi
|
||||
|
||||
echo "Starting Nginx to serve the OHIF Viewer..."
|
||||
|
||||
@@ -30,6 +30,7 @@ docker/dcm4che/dcm4che-arc
|
||||
|
||||
# Cypress test results
|
||||
videos/
|
||||
screenshots/
|
||||
|
||||
# Locize settings
|
||||
.locize
|
||||
@@ -3,6 +3,25 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.2.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.2.1...@ohif/extension-cornerstone@2.2.2) (2020-01-28)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.2.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.2.0...@ohif/extension-cornerstone@2.2.1) (2019-12-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 🐛 1241: Make Plugin switch part of ToolbarModule ([#1322](https://github.com/OHIF/Viewers/issues/1322)) ([6540e36](https://github.com/OHIF/Viewers/commit/6540e36818944ac2eccc696186366ae495b33a04)), closes [#1241](https://github.com/OHIF/Viewers/issues/1241)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.2.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.1.1...@ohif/extension-cornerstone@2.2.0) (2019-12-20)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-cornerstone",
|
||||
"version": "2.2.0",
|
||||
"version": "2.2.2",
|
||||
"description": "OHIF extension for Cornerstone",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -32,7 +32,7 @@
|
||||
"@ohif/ui": "^0.50.0",
|
||||
"cornerstone-core": "^2.2.8",
|
||||
"cornerstone-math": "^0.1.8",
|
||||
"cornerstone-tools": "^4.9.0",
|
||||
"cornerstone-tools": "^4.12.0",
|
||||
"cornerstone-wado-image-loader": "^3.0.0",
|
||||
"dcmjs": "^0.8.2",
|
||||
"dicom-parser": "^1.8.3",
|
||||
|
||||
@@ -2,6 +2,7 @@ import cornerstone from 'cornerstone-core';
|
||||
import cornerstoneTools from 'cornerstone-tools';
|
||||
import OHIF from '@ohif/core';
|
||||
|
||||
import setCornerstoneLayout from './utils/setCornerstoneLayout.js';
|
||||
import { getEnabledElement } from './state';
|
||||
import CornerstoneViewportDownloadForm from './CornerstoneViewportDownloadForm';
|
||||
const scroll = cornerstoneTools.import('util/scroll');
|
||||
@@ -242,6 +243,9 @@ const commandsModule = ({ servicesManager }) => {
|
||||
cornerstoneTools.removeToolState(element, toolType, tool);
|
||||
cornerstone.updateImage(element);
|
||||
},
|
||||
setCornerstoneLayout: () => {
|
||||
setCornerstoneLayout();
|
||||
}
|
||||
};
|
||||
|
||||
const definitions = {
|
||||
@@ -348,6 +352,12 @@ const commandsModule = ({ servicesManager }) => {
|
||||
storeContexts: [],
|
||||
options: {},
|
||||
},
|
||||
setCornerstoneLayout: {
|
||||
commandFn: actions.setCornerstoneLayout,
|
||||
storeContexts: [],
|
||||
options: {},
|
||||
context: 'VIEWER',
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@@ -98,6 +98,7 @@ export default function init({ servicesManager, configuration }) {
|
||||
csTools.MagnifyTool,
|
||||
csTools.StackScrollTool,
|
||||
csTools.StackScrollMouseWheelTool,
|
||||
csTools.OverlayTool,
|
||||
],
|
||||
};
|
||||
|
||||
@@ -179,4 +180,5 @@ export default function init({ servicesManager, configuration }) {
|
||||
csTools.setToolActive('StackScrollMouseWheel', {}); // TODO: Empty options should not be required
|
||||
csTools.setToolActive('PanMultiTouch', { pointers: 2 }); // TODO: Better error if no options
|
||||
csTools.setToolActive('ZoomTouchPinch', {});
|
||||
csTools.setToolEnabled('Overlay', {});
|
||||
}
|
||||
@@ -237,6 +237,15 @@ const definitions = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'Exit2DMPR',
|
||||
label: 'Exit 2D MPR',
|
||||
icon: 'times',
|
||||
//
|
||||
type: TOOLBAR_BUTTON_TYPES.COMMAND,
|
||||
commandName: 'setCornerstoneLayout',
|
||||
context: 'ACTIVE_VIEWPORT::VTK',
|
||||
}
|
||||
];
|
||||
|
||||
export default {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { redux } from '@ohif/core';
|
||||
|
||||
const { setLayout } = redux.actions;
|
||||
|
||||
/**
|
||||
* Update the current layout with a simple Cornerstone one
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
const setCornerstoneLayout = () => {
|
||||
const layout = {
|
||||
numRows: 1,
|
||||
numColumns: 1,
|
||||
viewports: [{ plugin: 'cornerstone' }],
|
||||
};
|
||||
|
||||
const action = setLayout(layout);
|
||||
|
||||
window.store.dispatch(action);
|
||||
}
|
||||
|
||||
export default setCornerstoneLayout;
|
||||
@@ -3,6 +3,81 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.2.11](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.2.10...@ohif/extension-vtk@1.2.11) (2020-01-28)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.2.10](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.2.9...@ohif/extension-vtk@1.2.10) (2020-01-28)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.2.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.2.8...@ohif/extension-vtk@1.2.9) (2020-01-27)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.2.8](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.2.7...@ohif/extension-vtk@1.2.8) (2020-01-24)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.2.7](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.2.6...@ohif/extension-vtk@1.2.7) (2020-01-08)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.2.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.2.5...@ohif/extension-vtk@1.2.6) (2020-01-07)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.2.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.2.4...@ohif/extension-vtk@1.2.5) (2020-01-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.2.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.2.3...@ohif/extension-vtk@1.2.4) (2019-12-30)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.2.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.2.2...@ohif/extension-vtk@1.2.3) (2019-12-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 🐛 1241: Make Plugin switch part of ToolbarModule ([#1322](https://github.com/OHIF/Viewers/issues/1322)) ([6540e36](https://github.com/OHIF/Viewers/commit/6540e36818944ac2eccc696186366ae495b33a04)), closes [#1241](https://github.com/OHIF/Viewers/issues/1241)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.2.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.2.1...@ohif/extension-vtk@1.2.2) (2019-12-20)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-vtk
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-vtk",
|
||||
"version": "1.2.2",
|
||||
"version": "1.2.11",
|
||||
"description": "OHIF extension for VTK.js",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -52,9 +52,9 @@
|
||||
"react-vtkjs-viewport": "^0.3.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ohif/core": "^2.3.0",
|
||||
"@ohif/ui": "^1.1.7",
|
||||
"cornerstone-tools": "^4.9.0",
|
||||
"@ohif/core": "^2.3.6",
|
||||
"@ohif/ui": "^1.1.8",
|
||||
"cornerstone-tools": "^4.12.0",
|
||||
"cornerstone-wado-image-loader": "^3.0.0",
|
||||
"dicom-parser": "^1.8.3",
|
||||
"gh-pages": "^2.0.1",
|
||||
|
||||
@@ -234,7 +234,7 @@ const commandsModule = ({ commandsManager }) => {
|
||||
},
|
||||
},
|
||||
{
|
||||
// Sagital
|
||||
// Sagittal
|
||||
orientation: {
|
||||
sliceNormal: [1, 0, 0],
|
||||
viewUp: [0, 0, 1],
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
import React from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import { ToolbarButton } from '@ohif/ui';
|
||||
import { utils } from '@ohif/core';
|
||||
|
||||
const { studyMetadataManager } = utils;
|
||||
|
||||
let isVisible = true;
|
||||
|
||||
const _isDisplaySetReconstructable = (viewportSpecificData = {}, activeViewportIndex) => {
|
||||
if (!viewportSpecificData[activeViewportIndex]) {
|
||||
return false;
|
||||
};
|
||||
|
||||
const { displaySetInstanceUid, studyInstanceUid } = viewportSpecificData[
|
||||
activeViewportIndex
|
||||
];
|
||||
|
||||
const studies = studyMetadataManager.all();
|
||||
|
||||
const study = studies.find(
|
||||
study => study.studyInstanceUID === studyInstanceUid
|
||||
);
|
||||
|
||||
if (!study) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const displaySet = study._displaySets.find(set => set.displaySetInstanceUid === displaySetInstanceUid);
|
||||
|
||||
if (!displaySet) {
|
||||
return false;
|
||||
};
|
||||
|
||||
return displaySet.isReconstructable;
|
||||
};
|
||||
|
||||
function VTKMPRToolbarButton({
|
||||
parentContext,
|
||||
toolbarClickCallback,
|
||||
button,
|
||||
activeButtons,
|
||||
isActive,
|
||||
className,
|
||||
}) {
|
||||
const { id, label, icon } = button;
|
||||
const { viewportSpecificData, activeViewportIndex } = useSelector(state => {
|
||||
const { viewports = {} } = state;
|
||||
const { viewportSpecificData, activeViewportIndex } = viewports;
|
||||
|
||||
return {
|
||||
viewportSpecificData,
|
||||
activeViewportIndex,
|
||||
}
|
||||
});
|
||||
|
||||
isVisible = _isDisplaySetReconstructable(
|
||||
viewportSpecificData,
|
||||
activeViewportIndex,
|
||||
);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{isVisible && (
|
||||
<ToolbarButton
|
||||
key={id}
|
||||
label={label}
|
||||
icon={icon}
|
||||
onClick={evt => toolbarClickCallback(button, evt)}
|
||||
isActive={isActive}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
VTKMPRToolbarButton.propTypes = {
|
||||
parentContext: PropTypes.object.isRequired,
|
||||
toolbarClickCallback: PropTypes.func.isRequired,
|
||||
button: PropTypes.object.isRequired,
|
||||
activeButtons: PropTypes.array.isRequired,
|
||||
isActive: PropTypes.bool,
|
||||
className: PropTypes.string,
|
||||
};
|
||||
|
||||
export default VTKMPRToolbarButton;
|
||||
@@ -1,4 +1,5 @@
|
||||
import SlabThicknessToolbarComponent from './toolbarComponents/SlabThicknessToolbarComponent';
|
||||
import VTKMPRToolbarButton from './toolbarComponents/VTKMPRToolbarButton';
|
||||
|
||||
const TOOLBAR_BUTTON_TYPES = {
|
||||
COMMAND: 'command',
|
||||
@@ -115,9 +116,19 @@ const definitions = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '2DMPR',
|
||||
label: '2D MPR',
|
||||
icon: 'cube',
|
||||
//
|
||||
CustomComponent: VTKMPRToolbarButton,
|
||||
type: TOOLBAR_BUTTON_TYPES.COMMAND,
|
||||
commandName: 'mpr2d',
|
||||
context: 'ACTIVE_VIEWPORT::CORNERSTONE',
|
||||
},
|
||||
];
|
||||
|
||||
export default {
|
||||
definitions,
|
||||
defaultContext: 'ACTIVE_VIEWPORT::VTK',
|
||||
};
|
||||
}
|
||||
@@ -3,6 +3,57 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.3.6](https://github.com/OHIF/Viewers/compare/@ohif/core@2.3.5...@ohif/core@2.3.6) (2020-01-28)
|
||||
|
||||
**Note:** Version bump only for package @ohif/core
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.3.5](https://github.com/OHIF/Viewers/compare/@ohif/core@2.3.4...@ohif/core@2.3.5) (2020-01-28)
|
||||
|
||||
**Note:** Version bump only for package @ohif/core
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.3.4](https://github.com/OHIF/Viewers/compare/@ohif/core@2.3.3...@ohif/core@2.3.4) (2020-01-27)
|
||||
|
||||
**Note:** Version bump only for package @ohif/core
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.3.3](https://github.com/OHIF/Viewers/compare/@ohif/core@2.3.2...@ohif/core@2.3.3) (2020-01-24)
|
||||
|
||||
**Note:** Version bump only for package @ohif/core
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.3.2](https://github.com/OHIF/Viewers/compare/@ohif/core@2.3.1...@ohif/core@2.3.2) (2020-01-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/core
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.3.1](https://github.com/OHIF/Viewers/compare/@ohif/core@2.3.0...@ohif/core@2.3.1) (2019-12-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 🐛 1241: Make Plugin switch part of ToolbarModule ([#1322](https://github.com/OHIF/Viewers/issues/1322)) ([6540e36](https://github.com/OHIF/Viewers/commit/6540e36818944ac2eccc696186366ae495b33a04)), closes [#1241](https://github.com/OHIF/Viewers/issues/1241)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.3.0](https://github.com/OHIF/Viewers/compare/@ohif/core@2.2.1...@ohif/core@2.3.0) (2019-12-20)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/core",
|
||||
"version": "2.3.0",
|
||||
"version": "2.3.6",
|
||||
"description": "Generic business logic for web-based medical imaging applications",
|
||||
"author": "OHIF Core Team",
|
||||
"license": "MIT",
|
||||
@@ -32,7 +32,7 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"cornerstone-core": "^2.2.8",
|
||||
"cornerstone-tools": "^4.9.0",
|
||||
"cornerstone-tools": "^4.12.0",
|
||||
"cornerstone-wado-image-loader": "^3.0.0",
|
||||
"dicom-parser": "^1.8.3"
|
||||
},
|
||||
|
||||
@@ -226,7 +226,7 @@ describe('ExtensionManager.js', () => {
|
||||
return {
|
||||
definitions: {
|
||||
exampleDefinition: {
|
||||
commandFn: () => {},
|
||||
commandFn: () => { },
|
||||
storeContexts: [],
|
||||
options: {},
|
||||
},
|
||||
|
||||
@@ -11,6 +11,16 @@ const servers = (state = defaultState, action) => {
|
||||
servers.forEach(s => (s.active = true));
|
||||
return { ...state, servers };
|
||||
|
||||
case 'ACTIVATE_SERVER': {
|
||||
const newServer = { ...action.server, active: true };
|
||||
const newServers = state.servers;
|
||||
newServers.forEach(s => (s.active = false));
|
||||
return {
|
||||
...state,
|
||||
servers: uniqBy([...newServers, newServer], 'wadoRoot'),
|
||||
};
|
||||
}
|
||||
|
||||
case 'SET_SERVERS':
|
||||
return { ...state, servers: action.servers };
|
||||
|
||||
|
||||
@@ -40,7 +40,9 @@ export default class RetrieveMetadataLoader {
|
||||
if (result && result.length) {
|
||||
break; // closes iterator in case data is retrieved successfully
|
||||
}
|
||||
} catch (e) { }
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
if (loaders.next().done && !result) {
|
||||
@@ -51,8 +53,8 @@ export default class RetrieveMetadataLoader {
|
||||
}
|
||||
|
||||
// Methods to be overwrite
|
||||
async configLoad() { }
|
||||
async preLoad() { }
|
||||
async load(preLoadData) { }
|
||||
async posLoad(loadData) { }
|
||||
async configLoad() {}
|
||||
async preLoad() {}
|
||||
async load(preLoadData) {}
|
||||
async posLoad(loadData) {}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import { api } from 'dicomweb-client';
|
||||
import DICOMWeb from '../../../DICOMWeb';
|
||||
|
||||
const WADOProxy = {
|
||||
@@ -21,6 +22,43 @@ function parseFloatArray(obj) {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple cache schema for retrieved color palettes.
|
||||
*/
|
||||
const paletteColorCache = {
|
||||
count: 0,
|
||||
maxAge: 24 * 60 * 60 * 1000, // 24h cache?
|
||||
entries: {},
|
||||
isValidUID: function(paletteUID) {
|
||||
return typeof paletteUID === 'string' && paletteUID.length > 0;
|
||||
},
|
||||
get: function(paletteUID) {
|
||||
let entry = null;
|
||||
if (this.entries.hasOwnProperty(paletteUID)) {
|
||||
entry = this.entries[paletteUID];
|
||||
// check how the entry is...
|
||||
if (Date.now() - entry.time > this.maxAge) {
|
||||
// entry is too old... remove entry.
|
||||
delete this.entries[paletteUID];
|
||||
this.count--;
|
||||
entry = null;
|
||||
}
|
||||
}
|
||||
return entry;
|
||||
},
|
||||
add: function(entry) {
|
||||
if (this.isValidUID(entry.uid)) {
|
||||
let paletteUID = entry.uid;
|
||||
if (this.entries.hasOwnProperty(paletteUID) !== true) {
|
||||
this.count++; // increment cache entry count...
|
||||
}
|
||||
entry.time = Date.now();
|
||||
this.entries[paletteUID] = entry;
|
||||
// @TODO: Add logic to get rid of old entries and reduce memory usage...
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a plain JS object that describes a study (a study descriptor object)
|
||||
* @param {Object} server Object with server configuration parameters
|
||||
@@ -299,40 +337,41 @@ async function makeSOPInstance(server, study, instance) {
|
||||
return sopInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert String to ArrayBuffer
|
||||
*
|
||||
* @param {String} str Input String
|
||||
* @return {ArrayBuffer} Output converted ArrayBuffer
|
||||
*/
|
||||
function str2ab(str) {
|
||||
const strLen = str.length;
|
||||
const bytes = new Uint8Array(strLen);
|
||||
|
||||
for (let i = 0; i < strLen; i++) {
|
||||
bytes[i] = str.charCodeAt(i);
|
||||
}
|
||||
|
||||
return bytes.buffer;
|
||||
}
|
||||
|
||||
function getPaletteColor(server, instance, tag, lutDescriptor) {
|
||||
const numLutEntries = lutDescriptor[0];
|
||||
const bits = lutDescriptor[2];
|
||||
|
||||
let uri = WADOProxy.convertURL(instance[tag].BulkDataURI, server);
|
||||
|
||||
// TODO: Workaround for dcm4chee behind SSL-terminating proxy returning
|
||||
// incorrect bulk data URIs
|
||||
if (server.wadoRoot.indexOf('https') === 0 && !uri.includes('https')) {
|
||||
uri = uri.replace('http', 'https');
|
||||
}
|
||||
|
||||
const config = {
|
||||
url: server.wadoRoot, //BulkDataURI is absolute, so this isn't used
|
||||
headers: DICOMWeb.getAuthorizationHeader(server),
|
||||
};
|
||||
const dicomWeb = new api.DICOMwebClient(config);
|
||||
const options = {
|
||||
BulkDataURI: uri,
|
||||
};
|
||||
|
||||
const readUInt16 = (byteArray, position) => {
|
||||
return byteArray[position] + byteArray[position + 1] * 256;
|
||||
};
|
||||
|
||||
const arrayBufferToPaletteColorLUT = result => {
|
||||
const arraybuffer = result[0];
|
||||
const arrayBufferToPaletteColorLUT = arraybuffer => {
|
||||
const byteArray = new Uint8Array(arraybuffer);
|
||||
const lut = [];
|
||||
|
||||
for (let i = 0; i < numLutEntries; i++) {
|
||||
if (bits === 16) {
|
||||
if (bits === 16) {
|
||||
for (let i = 0; i < numLutEntries; i++) {
|
||||
lut[i] = readUInt16(byteArray, i * 2);
|
||||
} else {
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < numLutEntries; i++) {
|
||||
lut[i] = byteArray[i];
|
||||
}
|
||||
}
|
||||
@@ -340,7 +379,38 @@ function getPaletteColor(server, instance, tag, lutDescriptor) {
|
||||
return lut;
|
||||
};
|
||||
|
||||
return dicomWeb.retrieveBulkData(options).then(arrayBufferToPaletteColorLUT);
|
||||
if (instance[tag].BulkDataURI) {
|
||||
let uri = WADOProxy.convertURL(instance[tag].BulkDataURI, server);
|
||||
|
||||
// TODO: Workaround for dcm4chee behind SSL-terminating proxy returning
|
||||
// incorrect bulk data URIs
|
||||
if (server.wadoRoot.indexOf('https') === 0 && !uri.includes('https')) {
|
||||
uri = uri.replace('http', 'https');
|
||||
}
|
||||
|
||||
const config = {
|
||||
url: server.wadoRoot, //BulkDataURI is absolute, so this isn't used
|
||||
headers: DICOMWeb.getAuthorizationHeader(server),
|
||||
};
|
||||
const dicomWeb = new api.DICOMwebClient(config);
|
||||
const options = {
|
||||
BulkDataURI: uri,
|
||||
};
|
||||
|
||||
return dicomWeb
|
||||
.retrieveBulkData(options)
|
||||
.then(result => result[0])
|
||||
.then(arrayBufferToPaletteColorLUT);
|
||||
} else if (instance[tag].InlineBinary) {
|
||||
const inlineBinaryData = atob(instance[tag].InlineBinary);
|
||||
const arraybuf = str2ab(inlineBinaryData);
|
||||
|
||||
return arrayBufferToPaletteColorLUT(arraybuf);
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
'Palette Color LUT was not provided as InlineBinary or BulkDataURI'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,413 +0,0 @@
|
||||
import DICOMWeb from '../DICOMWeb/';
|
||||
|
||||
const WADOProxy = {
|
||||
convertURL: (url, server) => {
|
||||
// TODO: Remove all WADOProxy stuff from this file
|
||||
return url;
|
||||
},
|
||||
};
|
||||
function parseFloatArray(obj) {
|
||||
const result = [];
|
||||
|
||||
if (!obj) {
|
||||
return result;
|
||||
}
|
||||
|
||||
const objs = obj.split('\\');
|
||||
for (let i = 0; i < objs.length; i++) {
|
||||
result.push(parseFloat(objs[i]));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a plain JS object that describes a study (a study descriptor object)
|
||||
* @param {Object} server Object with server configuration parameters
|
||||
* @param {Object} aSopInstance a SOP Instance from which study information will be added
|
||||
*/
|
||||
function createStudy(server, aSopInstance) {
|
||||
// TODO: Pass a reference ID to the server instead of including the URLs here
|
||||
return {
|
||||
seriesList: [],
|
||||
seriesMap: Object.create(null),
|
||||
seriesLoader: null,
|
||||
wadoUriRoot: server.wadoUriRoot,
|
||||
wadoRoot: server.wadoRoot,
|
||||
qidoRoot: server.qidoRoot,
|
||||
patientName: DICOMWeb.getName(aSopInstance['00100010']),
|
||||
patientId: DICOMWeb.getString(aSopInstance['00100020']),
|
||||
patientAge: DICOMWeb.getNumber(aSopInstance['00101010']),
|
||||
patientSize: DICOMWeb.getNumber(aSopInstance['00101020']),
|
||||
patientWeight: DICOMWeb.getNumber(aSopInstance['00101030']),
|
||||
accessionNumber: DICOMWeb.getString(aSopInstance['00080050']),
|
||||
studyDate: DICOMWeb.getString(aSopInstance['00080020']),
|
||||
modalities: DICOMWeb.getString(aSopInstance['00080061']),
|
||||
studyDescription: DICOMWeb.getString(aSopInstance['00081030']),
|
||||
imageCount: DICOMWeb.getString(aSopInstance['00201208']),
|
||||
studyInstanceUid: DICOMWeb.getString(aSopInstance['0020000D']),
|
||||
institutionName: DICOMWeb.getString(aSopInstance['00080080']),
|
||||
};
|
||||
}
|
||||
|
||||
/** Returns a WADO url for an instance
|
||||
*
|
||||
* @param studyInstanceUid
|
||||
* @param seriesInstanceUid
|
||||
* @param sopInstanceUid
|
||||
* @returns {string}
|
||||
*/
|
||||
function buildInstanceWadoUrl(
|
||||
server,
|
||||
studyInstanceUid,
|
||||
seriesInstanceUid,
|
||||
sopInstanceUid
|
||||
) {
|
||||
// TODO: This can be removed, since DICOMWebClient has the same function. Not urgent, though
|
||||
const params = [];
|
||||
|
||||
params.push('requestType=WADO');
|
||||
params.push(`studyUID=${studyInstanceUid}`);
|
||||
params.push(`seriesUID=${seriesInstanceUid}`);
|
||||
params.push(`objectUID=${sopInstanceUid}`);
|
||||
params.push('contentType=application/dicom');
|
||||
params.push('transferSyntax=*');
|
||||
|
||||
const paramString = params.join('&');
|
||||
|
||||
return `${server.wadoUriRoot}?${paramString}`;
|
||||
}
|
||||
|
||||
function buildInstanceWadoRsUri(
|
||||
server,
|
||||
studyInstanceUid,
|
||||
seriesInstanceUid,
|
||||
sopInstanceUid
|
||||
) {
|
||||
return `${server.wadoRoot}/studies/${studyInstanceUid}/series/${seriesInstanceUid}/instances/${sopInstanceUid}`;
|
||||
}
|
||||
|
||||
function buildInstanceFrameWadoRsUri(
|
||||
server,
|
||||
studyInstanceUid,
|
||||
seriesInstanceUid,
|
||||
sopInstanceUid,
|
||||
frame
|
||||
) {
|
||||
const baseWadoRsUri = buildInstanceWadoRsUri(
|
||||
server,
|
||||
studyInstanceUid,
|
||||
seriesInstanceUid,
|
||||
sopInstanceUid
|
||||
);
|
||||
frame = frame != null || 1;
|
||||
|
||||
return `${baseWadoRsUri}/frames/${frame}`;
|
||||
}
|
||||
|
||||
function getFrameIncrementPointer(element) {
|
||||
const frameIncrementPointerNames = {
|
||||
'00181065': 'frameTimeVector',
|
||||
'00181063': 'frameTime',
|
||||
};
|
||||
|
||||
if (!element || !element.Value || !element.Value.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const value = element.Value[0];
|
||||
return frameIncrementPointerNames[value];
|
||||
}
|
||||
|
||||
function getRadiopharmaceuticalInfo(instance) {
|
||||
const modality = DICOMWeb.getString(instance['00080060']);
|
||||
|
||||
if (modality !== 'PT') {
|
||||
return;
|
||||
}
|
||||
|
||||
const radiopharmaceuticalInfo = instance['00540016'];
|
||||
if (
|
||||
radiopharmaceuticalInfo === undefined ||
|
||||
!radiopharmaceuticalInfo.Value ||
|
||||
!radiopharmaceuticalInfo.Value.length
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const firstPetRadiopharmaceuticalInfo = radiopharmaceuticalInfo.Value[0];
|
||||
return {
|
||||
radiopharmaceuticalStartTime: DICOMWeb.getString(
|
||||
firstPetRadiopharmaceuticalInfo['00181072']
|
||||
),
|
||||
radionuclideTotalDose: DICOMWeb.getNumber(
|
||||
firstPetRadiopharmaceuticalInfo['00181074']
|
||||
),
|
||||
radionuclideHalfLife: DICOMWeb.getNumber(
|
||||
firstPetRadiopharmaceuticalInfo['00181075']
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the SourceImageSequence, if it exists, in order
|
||||
* to return a ReferenceSOPInstanceUID. The ReferenceSOPInstanceUID
|
||||
* is used to refer to this image in any accompanying DICOM-SR documents.
|
||||
*
|
||||
* @param instance
|
||||
* @returns {String} The ReferenceSOPInstanceUID
|
||||
*/
|
||||
function getSourceImageInstanceUid(instance) {
|
||||
// TODO= Parse the whole Source Image Sequence
|
||||
// This is a really poor workaround for now.
|
||||
// Later we should probably parse the whole sequence.
|
||||
var SourceImageSequence = instance['00082112'];
|
||||
if (
|
||||
SourceImageSequence &&
|
||||
SourceImageSequence.Value &&
|
||||
SourceImageSequence.Value.length &&
|
||||
SourceImageSequence.Value[0]['00081155'].Value
|
||||
) {
|
||||
return SourceImageSequence.Value[0]['00081155'].Value[0];
|
||||
}
|
||||
}
|
||||
|
||||
async function makeSOPInstance(server, study, instance) {
|
||||
const { studyInstanceUid } = study;
|
||||
const seriesInstanceUid = DICOMWeb.getString(instance['0020000E']);
|
||||
let series = study.seriesMap[seriesInstanceUid];
|
||||
|
||||
if (!series) {
|
||||
series = {
|
||||
seriesInstanceUid,
|
||||
seriesDescription: DICOMWeb.getString(instance['0008103E']),
|
||||
modality: DICOMWeb.getString(instance['00080060']),
|
||||
seriesNumber: DICOMWeb.getNumber(instance['00200011']),
|
||||
seriesDate: DICOMWeb.getString(instance['00080021']),
|
||||
seriesTime: DICOMWeb.getString(instance['00080031']),
|
||||
instances: [],
|
||||
};
|
||||
study.seriesMap[seriesInstanceUid] = series;
|
||||
study.seriesList.push(series);
|
||||
}
|
||||
|
||||
const sopInstanceUid = DICOMWeb.getString(instance['00080018']);
|
||||
const wadouri = buildInstanceWadoUrl(
|
||||
server,
|
||||
studyInstanceUid,
|
||||
seriesInstanceUid,
|
||||
sopInstanceUid
|
||||
);
|
||||
const baseWadoRsUri = buildInstanceWadoRsUri(
|
||||
server,
|
||||
studyInstanceUid,
|
||||
seriesInstanceUid,
|
||||
sopInstanceUid
|
||||
);
|
||||
const wadorsuri = buildInstanceFrameWadoRsUri(
|
||||
server,
|
||||
studyInstanceUid,
|
||||
seriesInstanceUid,
|
||||
sopInstanceUid
|
||||
);
|
||||
|
||||
const sopInstance = {
|
||||
imageType: DICOMWeb.getString(instance['00080008']),
|
||||
sopClassUid: DICOMWeb.getString(instance['00080016']),
|
||||
modality: DICOMWeb.getString(instance['00080060']),
|
||||
sopInstanceUid,
|
||||
instanceNumber: DICOMWeb.getNumber(instance['00200013']),
|
||||
imagePositionPatient: DICOMWeb.getString(instance['00200032']),
|
||||
imageOrientationPatient: DICOMWeb.getString(instance['00200037']),
|
||||
frameOfReferenceUID: DICOMWeb.getString(instance['00200052']),
|
||||
sliceLocation: DICOMWeb.getNumber(instance['00201041']),
|
||||
samplesPerPixel: DICOMWeb.getNumber(instance['00280002']),
|
||||
photometricInterpretation: DICOMWeb.getString(instance['00280004']),
|
||||
planarConfiguration: DICOMWeb.getNumber(instance['00280006']),
|
||||
rows: DICOMWeb.getNumber(instance['00280010']),
|
||||
columns: DICOMWeb.getNumber(instance['00280011']),
|
||||
pixelSpacing: DICOMWeb.getString(instance['00280030']),
|
||||
pixelAspectRatio: DICOMWeb.getString(instance['00280034']),
|
||||
bitsAllocated: DICOMWeb.getNumber(instance['00280100']),
|
||||
bitsStored: DICOMWeb.getNumber(instance['00280101']),
|
||||
highBit: DICOMWeb.getNumber(instance['00280102']),
|
||||
pixelRepresentation: DICOMWeb.getNumber(instance['00280103']),
|
||||
smallestPixelValue: DICOMWeb.getNumber(instance['00280106']),
|
||||
largestPixelValue: DICOMWeb.getNumber(instance['00280107']),
|
||||
windowCenter: DICOMWeb.getString(instance['00281050']),
|
||||
windowWidth: DICOMWeb.getString(instance['00281051']),
|
||||
rescaleIntercept: DICOMWeb.getNumber(instance['00281052']),
|
||||
rescaleSlope: DICOMWeb.getNumber(instance['00281053']),
|
||||
rescaleType: DICOMWeb.getNumber(instance['00281054']),
|
||||
sourceImageInstanceUid: getSourceImageInstanceUid(instance),
|
||||
laterality: DICOMWeb.getString(instance['00200062']),
|
||||
viewPosition: DICOMWeb.getString(instance['00185101']),
|
||||
acquisitionDateTime: DICOMWeb.getString(instance['0008002A']),
|
||||
numberOfFrames: DICOMWeb.getNumber(instance['00280008']),
|
||||
frameIncrementPointer: getFrameIncrementPointer(instance['00280009']),
|
||||
frameTime: DICOMWeb.getNumber(instance['00181063']),
|
||||
frameTimeVector: parseFloatArray(DICOMWeb.getString(instance['00181065'])),
|
||||
sliceThickness: DICOMWeb.getNumber(instance['00180050']),
|
||||
spacingBetweenSlices: DICOMWeb.getString(instance['00180088']),
|
||||
lossyImageCompression: DICOMWeb.getString(instance['00282110']),
|
||||
derivationDescription: DICOMWeb.getString(instance['00282111']),
|
||||
lossyImageCompressionRatio: DICOMWeb.getString(instance['00282112']),
|
||||
lossyImageCompressionMethod: DICOMWeb.getString(instance['00282114']),
|
||||
echoNumber: DICOMWeb.getString(instance['00180086']),
|
||||
contrastBolusAgent: DICOMWeb.getString(instance['00180010']),
|
||||
radiopharmaceuticalInfo: getRadiopharmaceuticalInfo(instance),
|
||||
baseWadoRsUri: baseWadoRsUri,
|
||||
wadouri: WADOProxy.convertURL(wadouri, server),
|
||||
wadorsuri: WADOProxy.convertURL(wadorsuri, server),
|
||||
wadoRoot: server.wadoRoot,
|
||||
imageRendering: server.imageRendering,
|
||||
thumbnailRendering: server.thumbnailRendering,
|
||||
};
|
||||
|
||||
// Get additional information if the instance uses "PALETTE COLOR" photometric interpretation
|
||||
if (sopInstance.photometricInterpretation === 'PALETTE COLOR') {
|
||||
const redPaletteColorLookupTableDescriptor = parseFloatArray(
|
||||
DICOMWeb.getString(instance['00281101'])
|
||||
);
|
||||
const greenPaletteColorLookupTableDescriptor = parseFloatArray(
|
||||
DICOMWeb.getString(instance['00281102'])
|
||||
);
|
||||
const bluePaletteColorLookupTableDescriptor = parseFloatArray(
|
||||
DICOMWeb.getString(instance['00281103'])
|
||||
);
|
||||
const palettes = await getPaletteColors(
|
||||
server,
|
||||
instance,
|
||||
redPaletteColorLookupTableDescriptor
|
||||
);
|
||||
|
||||
if (palettes) {
|
||||
if (palettes.uid) {
|
||||
sopInstance.paletteColorLookupTableUID = palettes.uid;
|
||||
}
|
||||
|
||||
sopInstance.redPaletteColorLookupTableData = palettes.red;
|
||||
sopInstance.greenPaletteColorLookupTableData = palettes.green;
|
||||
sopInstance.bluePaletteColorLookupTableData = palettes.blue;
|
||||
sopInstance.redPaletteColorLookupTableDescriptor = redPaletteColorLookupTableDescriptor;
|
||||
sopInstance.greenPaletteColorLookupTableDescriptor = greenPaletteColorLookupTableDescriptor;
|
||||
sopInstance.bluePaletteColorLookupTableDescriptor = bluePaletteColorLookupTableDescriptor;
|
||||
}
|
||||
}
|
||||
|
||||
series.instances.push(sopInstance);
|
||||
return sopInstance;
|
||||
}
|
||||
|
||||
function getPaletteColor(server, instance, tag, lutDescriptor) {
|
||||
const numLutEntries = lutDescriptor[0];
|
||||
const bits = lutDescriptor[2];
|
||||
|
||||
let uri = WADOProxy.convertURL(instance[tag].BulkDataURI, server);
|
||||
|
||||
// TODO: Workaround for dcm4chee behind SSL-terminating proxy returning
|
||||
// incorrect bulk data URIs
|
||||
if (server.wadoRoot.indexOf('https') === 0 && !uri.includes('https')) {
|
||||
uri = uri.replace('http', 'https');
|
||||
}
|
||||
|
||||
const config = {
|
||||
url: server.wadoRoot, //BulkDataURI is absolute, so this isn't used
|
||||
headers: DICOMWeb.getAuthorizationHeader(server),
|
||||
};
|
||||
const dicomWeb = new api.DICOMwebClient(config);
|
||||
const options = {
|
||||
BulkDataURI: uri,
|
||||
};
|
||||
|
||||
const readUInt16 = (byteArray, position) => {
|
||||
return byteArray[position] + byteArray[position + 1] * 256;
|
||||
};
|
||||
|
||||
const arrayBufferToPaletteColorLUT = result => {
|
||||
const arraybuffer = result[0];
|
||||
const byteArray = new Uint8Array(arraybuffer);
|
||||
const lut = [];
|
||||
|
||||
for (let i = 0; i < numLutEntries; i++) {
|
||||
if (bits === 16) {
|
||||
lut[i] = readUInt16(byteArray, i * 2);
|
||||
} else {
|
||||
lut[i] = byteArray[i];
|
||||
}
|
||||
}
|
||||
|
||||
return lut;
|
||||
};
|
||||
|
||||
return dicomWeb.retrieveBulkData(options).then(arrayBufferToPaletteColorLUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch palette colors for instances with "PALETTE COLOR" photometricInterpretation.
|
||||
*
|
||||
* @param server {Object} Current server;
|
||||
* @param instance {Object} The retrieved instance metadata;
|
||||
* @returns {String} The ReferenceSOPInstanceUID
|
||||
*/
|
||||
async function getPaletteColors(server, instance, lutDescriptor) {
|
||||
let paletteUID = DICOMWeb.getString(instance['00281199']);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let entry;
|
||||
if (paletteColorCache.isValidUID(paletteUID)) {
|
||||
entry = paletteColorCache.get(paletteUID);
|
||||
|
||||
if (entry) {
|
||||
return resolve(entry);
|
||||
}
|
||||
}
|
||||
|
||||
// no entry in cache... Fetch remote data.
|
||||
const r = getPaletteColor(server, instance, '00281201', lutDescriptor);
|
||||
const g = getPaletteColor(server, instance, '00281202', lutDescriptor);
|
||||
const b = getPaletteColor(server, instance, '00281203', lutDescriptor);
|
||||
|
||||
const promises = [r, g, b];
|
||||
|
||||
Promise.all(promises).then(args => {
|
||||
entry = {
|
||||
red: args[0],
|
||||
green: args[1],
|
||||
blue: args[2],
|
||||
};
|
||||
|
||||
// when paletteUID is present, the entry can be cached...
|
||||
entry.uid = paletteUID;
|
||||
paletteColorCache.add(entry);
|
||||
|
||||
resolve(entry);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a list of SOP Instances to a given study object descriptor
|
||||
* @param {Object} server Object with server configuration parameters
|
||||
* @param {Object} study The study descriptor to which the given SOP instances will be added
|
||||
* @param {Array} sopInstanceList A list of SOP instance objects
|
||||
*/
|
||||
async function addInstancesToStudy(server, study, sopInstanceList) {
|
||||
return Promise.all(
|
||||
sopInstanceList.map(function(sopInstance) {
|
||||
return makeSOPInstance(server, study, sopInstance);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const createStudyFromSOPInstanceList = async (server, sopInstanceList) => {
|
||||
if (Array.isArray(sopInstanceList) && sopInstanceList.length > 0) {
|
||||
const firstSopInstance = sopInstanceList[0];
|
||||
const study = createStudy(server, firstSopInstance);
|
||||
await addInstancesToStudy(server, study, sopInstanceList);
|
||||
return study;
|
||||
}
|
||||
throw new Error('Failed to create study out of provided SOP instance list');
|
||||
};
|
||||
|
||||
export { createStudyFromSOPInstanceList, addInstancesToStudy };
|
||||
@@ -3,6 +3,17 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.1.8](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.1.7...@ohif/ui@1.1.8) (2020-01-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* measurements panel css and delete button visibility ([#1352](https://github.com/OHIF/Viewers/issues/1352)) ([7ab0bbb](https://github.com/OHIF/Viewers/commit/7ab0bbb32581dcba16ee16b49b92406e2856ac76))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.1.7](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.1.6...@ohif/ui@1.1.7) (2019-12-20)
|
||||
|
||||
**Note:** Version bump only for package @ohif/ui
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/ui",
|
||||
"version": "1.1.7",
|
||||
"version": "1.1.8",
|
||||
"description": "A set of React components for Medical Imaging Viewers",
|
||||
"author": "OHIF Contributors",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
.measurementItem
|
||||
.rowActions
|
||||
margin-left: -1px;
|
||||
background-color: var(--ui-gray-darker)
|
||||
height: 0
|
||||
overflow: hidden
|
||||
@@ -28,7 +29,6 @@
|
||||
visibility: visible
|
||||
|
||||
.measurementLocation
|
||||
margin-left: 9px;
|
||||
margin-top: 9px;
|
||||
overflow: hidden;
|
||||
max-width: calc(100% - 50px);
|
||||
@@ -39,7 +39,6 @@
|
||||
.measurementDisplayText
|
||||
display: inline-block;
|
||||
margin-top: 9px;
|
||||
margin-left: 9px;
|
||||
padding-left: 9px;
|
||||
width: 90px;
|
||||
border-left: 1px solid var(--text-secondary-color);
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
transition(all 0.3s ease)
|
||||
font-size: 14px
|
||||
font-weight: 400
|
||||
margin-right: 5px
|
||||
margin-right: 6px
|
||||
padding: 10px
|
||||
text-align: center
|
||||
-webkit-box-sizing: initial;
|
||||
@@ -38,9 +38,8 @@
|
||||
.itemContent
|
||||
flex: 1
|
||||
color: var(--text-secondary-color)
|
||||
width: calc(100% - 30px);
|
||||
width: calc(100% - 51px);
|
||||
font-weight: 400
|
||||
margin-left: 9px;
|
||||
margin-top: 9px;
|
||||
|
||||
.warning-icon
|
||||
|
||||
@@ -3,6 +3,135 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.4.8](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.4.7...@ohif/viewer@3.4.8) (2020-01-28)
|
||||
|
||||
**Note:** Version bump only for package @ohif/viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.4.7](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.4.6...@ohif/viewer@3.4.7) (2020-01-28)
|
||||
|
||||
**Note:** Version bump only for package @ohif/viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.4.6](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.4.5...@ohif/viewer@3.4.6) (2020-01-28)
|
||||
|
||||
**Note:** Version bump only for package @ohif/viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.4.5](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.4.4...@ohif/viewer@3.4.5) (2020-01-27)
|
||||
|
||||
**Note:** Version bump only for package @ohif/viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.4.4](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.4.3...@ohif/viewer@3.4.4) (2020-01-27)
|
||||
|
||||
**Note:** Version bump only for package @ohif/viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.4.3](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.4.2...@ohif/viewer@3.4.3) (2020-01-24)
|
||||
|
||||
**Note:** Version bump only for package @ohif/viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.4.2](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.4.1...@ohif/viewer@3.4.2) (2020-01-17)
|
||||
|
||||
**Note:** Version bump only for package @ohif/viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.4.1](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.4.0...@ohif/viewer@3.4.1) (2020-01-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 🐛 Metadata is being mistakenly purged ([#1360](https://github.com/OHIF/Viewers/issues/1360)) ([b9a66d4](https://github.com/OHIF/Viewers/commit/b9a66d44241f2896ef184511287fb4984671e16d)), closes [#1326](https://github.com/OHIF/Viewers/issues/1326)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.4.0](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.3.8...@ohif/viewer@3.4.0) (2020-01-14)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Custom Healthcare API endpoint ([#1367](https://github.com/OHIF/Viewers/issues/1367)) ([a5d6bc6](https://github.com/OHIF/Viewers/commit/a5d6bc6a51784ed3a8a40d4ae773de9099f116b9))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.3.8](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.3.7...@ohif/viewer@3.3.8) (2020-01-10)
|
||||
|
||||
**Note:** Version bump only for package @ohif/viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.3.7](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.3.6...@ohif/viewer@3.3.7) (2020-01-08)
|
||||
|
||||
**Note:** Version bump only for package @ohif/viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.3.6](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.3.5...@ohif/viewer@3.3.6) (2020-01-07)
|
||||
|
||||
**Note:** Version bump only for package @ohif/viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.3.5](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.3.4...@ohif/viewer@3.3.5) (2020-01-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.3.4](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.3.3...@ohif/viewer@3.3.4) (2019-12-30)
|
||||
|
||||
**Note:** Version bump only for package @ohif/viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.3.3](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.3.2...@ohif/viewer@3.3.3) (2019-12-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 🐛 1241: Make Plugin switch part of ToolbarModule ([#1322](https://github.com/OHIF/Viewers/issues/1322)) ([6540e36](https://github.com/OHIF/Viewers/commit/6540e36818944ac2eccc696186366ae495b33a04)), closes [#1241](https://github.com/OHIF/Viewers/issues/1241)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.3.2](https://github.com/OHIF/Viewers/compare/@ohif/viewer@3.3.1...@ohif/viewer@3.3.2) (2019-12-20)
|
||||
|
||||
**Note:** Version bump only for package @ohif/viewer
|
||||
|
||||
@@ -5,5 +5,6 @@
|
||||
"defaultCommandTimeout": 10000,
|
||||
"requestTimeout": 10000,
|
||||
"responseTimeout": 10000,
|
||||
"projectId": "4oe38f"
|
||||
"projectId": "4oe38f",
|
||||
"video": false
|
||||
}
|
||||
@@ -47,7 +47,6 @@ describe('OHIF Cornerstone Hotkeys', () => {
|
||||
cy.get('body').type('I');
|
||||
// Visual comparison
|
||||
cy.screenshot('Hotkey I - Should Invert Image');
|
||||
cy.percyCanvasSnapshot('Hotkey I - Should Invert Image');
|
||||
});
|
||||
|
||||
it('checks if hotkeys "+", "-" and "=" can zoom in, out and fit to viewport', () => {
|
||||
@@ -77,7 +76,6 @@ describe('OHIF Cornerstone Hotkeys', () => {
|
||||
|
||||
// Visual comparison to make sure the 'inverted' image was reset
|
||||
cy.screenshot('Hotkey SPACEBAR - Should Reset Image');
|
||||
cy.percyCanvasSnapshot('Hotkey SPACEBAR - Should Reset Image');
|
||||
});
|
||||
|
||||
it('uses hotkeys "RightArrow" and "LeftArrow" to navigate between multiple viewports', () => {
|
||||
|
||||
@@ -119,7 +119,6 @@ describe('OHIF Cornerstone Toolbar', () => {
|
||||
|
||||
// Visual comparison
|
||||
cy.screenshot('Pan tool moved the image inside the viewport');
|
||||
cy.percyCanvasSnapshot('Pan tool moved the image inside the viewport');
|
||||
});
|
||||
|
||||
it('checks if Length annotation can be added on viewport and on measurements panel', () => {
|
||||
@@ -471,7 +470,6 @@ describe('OHIF Cornerstone Toolbar', () => {
|
||||
|
||||
// Visual comparison
|
||||
cy.screenshot('Invert tool - Should Invert Canvas');
|
||||
cy.percyCanvasSnapshot('Invert tool - Should Invert Canvas');
|
||||
});
|
||||
|
||||
it('check if Rotate tool will change the image orientation in the viewport', () => {
|
||||
@@ -487,7 +485,6 @@ describe('OHIF Cornerstone Toolbar', () => {
|
||||
|
||||
// Visual comparison
|
||||
cy.screenshot('Rotate tool - Should Rotate Image to Right');
|
||||
cy.percyCanvasSnapshot('Rotate tool - Should Rotate Image to Right');
|
||||
});
|
||||
|
||||
it('check if Flip H tool will flip the image horizontally in the viewport', () => {
|
||||
@@ -501,7 +498,6 @@ describe('OHIF Cornerstone Toolbar', () => {
|
||||
|
||||
// Visual comparison
|
||||
cy.screenshot('Flip H tool - Should Flip Image on Y axis');
|
||||
cy.percyCanvasSnapshot('Flip H tool - Should Flip Image on Y axis');
|
||||
});
|
||||
|
||||
it('check if Flip V tool will flip the image vertically in the viewport', () => {
|
||||
@@ -515,6 +511,5 @@ describe('OHIF Cornerstone Toolbar', () => {
|
||||
|
||||
// Visual comparison
|
||||
cy.screenshot('Flip V tool - Should Flip Image on X axis');
|
||||
cy.percyCanvasSnapshot('Flip V tool - Should Flip Image on X axis');
|
||||
});
|
||||
});
|
||||
@@ -21,7 +21,6 @@ describe('OHIF Download Snapshot File', () => {
|
||||
cy.viewport(1000, 660);
|
||||
// Visual comparison
|
||||
cy.screenshot('Download Image Modal - Tablet experience');
|
||||
cy.percyCanvasSnapshot('Download Image Modal - Tablet experience');
|
||||
});
|
||||
|
||||
it('checks displayed information for Desktop experience', function() {
|
||||
@@ -29,7 +28,6 @@ describe('OHIF Download Snapshot File', () => {
|
||||
cy.viewport(1750, 720);
|
||||
// Visual comparison
|
||||
cy.screenshot('Download Image Modal - Desktop experience');
|
||||
cy.percyCanvasSnapshot('Download Image Modal - Desktop experience');
|
||||
//Check if all elements are displayed
|
||||
cy.get('[data-cy=modal-header]')
|
||||
.as('downloadImageModal')
|
||||
@@ -109,7 +107,6 @@ describe('OHIF Download Snapshot File', () => {
|
||||
cy.get('[data-cy="image-preview"]').scrollIntoView();
|
||||
// Visual comparison
|
||||
cy.screenshot('Download Image Modal - Show Annotations checked');
|
||||
cy.percyCanvasSnapshot('Download Image Modal - Show Annotations checked');
|
||||
//Compare classes that exists on Image Preview with Annotations and Without Annotation
|
||||
cy.get('[data-cy="modal-content"]')
|
||||
.find('canvas')
|
||||
|
||||
@@ -6,7 +6,9 @@ describe('OHIF Routes', function() {
|
||||
});
|
||||
|
||||
it('checks PT/CT json url study route', function() {
|
||||
cy.visit('/viewer?url=https://s3.eu-central-1.amazonaws.com/ohif-viewer/JSON/PTCTStudy.json');
|
||||
cy.visit(
|
||||
'/viewer?url=https://s3.eu-central-1.amazonaws.com/ohif-viewer/JSON/PTCTStudy.json'
|
||||
);
|
||||
|
||||
cy.server();
|
||||
cy.route('GET', '**/PTCTStudy/**').as('getPTCTStudy');
|
||||
@@ -15,8 +17,5 @@ describe('OHIF Routes', function() {
|
||||
cy.get('@getPTCTStudy').should($route => {
|
||||
expect($route.status).to.be.eq(200);
|
||||
});
|
||||
|
||||
cy.screenshot();
|
||||
cy.percyCanvasSnapshot('PT/CT json study route');
|
||||
});
|
||||
});
|
||||
@@ -4,9 +4,6 @@ describe('OHIFStandaloneViewer', () => {
|
||||
});
|
||||
|
||||
it('loads route with at least 2 rows', () => {
|
||||
cy.screenshot('Study List');
|
||||
cy.percyCanvasSnapshot('Study List');
|
||||
|
||||
cy.get('[data-cy="study-list-results"] tr')
|
||||
.its('length')
|
||||
.should('be.gt', 2);
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
//We are keeping the hardcoded results values for the study list tests
|
||||
//this is intended to be running in a controled docker environment with test data.
|
||||
describe('OHIF Study List', function () {
|
||||
context('Desktop resolution', function () {
|
||||
beforeEach(function () {
|
||||
describe('OHIF Study List', function() {
|
||||
context('Desktop resolution', function() {
|
||||
beforeEach(function() {
|
||||
cy.viewport(1750, 720);
|
||||
cy.openStudyList();
|
||||
cy.initStudyListAliasesOnDesktop();
|
||||
});
|
||||
|
||||
it('searches Patient Name with exact string', function () {
|
||||
it('searches Patient Name with exact string', function() {
|
||||
cy.get('@patientName').type('Juno');
|
||||
//Wait result list to be displayed
|
||||
cy.waitStudyList();
|
||||
@@ -18,7 +18,7 @@ describe('OHIF Study List', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('searches MRN with exact string', function () {
|
||||
it('searches MRN with exact string', function() {
|
||||
cy.get('@MRN').type('ProstateX-0000');
|
||||
//Wait result list to be displayed
|
||||
cy.waitStudyList();
|
||||
@@ -28,7 +28,7 @@ describe('OHIF Study List', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('searches Accession with exact string', function () {
|
||||
it('searches Accession with exact string', function() {
|
||||
cy.get('@accessionNumber').type('fpcben98890');
|
||||
//Wait result list to be displayed
|
||||
cy.waitStudyList();
|
||||
@@ -38,17 +38,17 @@ describe('OHIF Study List', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('searches Modality with camel case', function () {
|
||||
it('searches Modality with camel case', function() {
|
||||
cy.get('@modalities').type('Mr');
|
||||
//Wait result list to be displayed
|
||||
cy.waitStudyList();
|
||||
cy.get('@searchResult').should($list => {
|
||||
expect($list.length).to.be.eq(18);
|
||||
expect($list.length).to.be.eq(16);
|
||||
expect($list).to.contain('MR');
|
||||
});
|
||||
});
|
||||
|
||||
it('searches Description with exact string', function () {
|
||||
it('searches Description with exact string', function() {
|
||||
cy.get('@studyDescription').type('CHEST');
|
||||
//Wait result list to be displayed
|
||||
cy.waitStudyList();
|
||||
@@ -58,7 +58,7 @@ describe('OHIF Study List', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('changes rows per page and checks the study count', function () {
|
||||
it('changes rows per page and checks the study count', function() {
|
||||
//Show rows per page options
|
||||
const pageRows = [25, 50, 100];
|
||||
|
||||
@@ -100,14 +100,14 @@ describe('OHIF Study List', function () {
|
||||
// });
|
||||
});
|
||||
|
||||
context('Tablet resolution', function () {
|
||||
beforeEach(function () {
|
||||
context('Tablet resolution', function() {
|
||||
beforeEach(function() {
|
||||
cy.viewport(1000, 660);
|
||||
cy.openStudyList();
|
||||
cy.initStudyListAliasesOnTablet();
|
||||
});
|
||||
|
||||
it('searches Patient Name with exact string', function () {
|
||||
it('searches Patient Name with exact string', function() {
|
||||
cy.get('@patientNameOrMRN').type('Juno');
|
||||
//Wait result list to be displayed
|
||||
cy.waitStudyList();
|
||||
@@ -117,7 +117,7 @@ describe('OHIF Study List', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('searches MRN with with exact string', function () {
|
||||
it('searches MRN with with exact string', function() {
|
||||
cy.get('@patientNameOrMRN').type('ProstateX-0000');
|
||||
//Wait result list to be displayed
|
||||
cy.waitStudyList();
|
||||
@@ -127,17 +127,17 @@ describe('OHIF Study List', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('searches Modality with exact string', function () {
|
||||
it('searches Modality with exact string', function() {
|
||||
cy.get('@accessionModalityDescription').type('MR');
|
||||
//Wait result list to be displayed
|
||||
cy.waitStudyList();
|
||||
cy.get('@searchResult').should($list => {
|
||||
expect($list.length).to.be.eq(18);
|
||||
expect($list.length).to.be.eq(16);
|
||||
expect($list).to.contain('MR');
|
||||
});
|
||||
});
|
||||
|
||||
it('searches Accession with exact string', function () {
|
||||
it('searches Accession with exact string', function() {
|
||||
cy.get('@accessionModalityDescription').type('fpcben98890');
|
||||
//Wait result list to be displayed
|
||||
cy.waitStudyList();
|
||||
@@ -147,7 +147,7 @@ describe('OHIF Study List', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('searches Description with exact string', function () {
|
||||
it('searches Description with exact string', function() {
|
||||
cy.get('@accessionModalityDescription').type('CHEST');
|
||||
//Wait result list to be displayed
|
||||
cy.waitStudyList();
|
||||
@@ -157,7 +157,7 @@ describe('OHIF Study List', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('changes rows per page and checks the study count', function () {
|
||||
it('changes rows per page and checks the study count', function() {
|
||||
//Show rows per page options
|
||||
const pageRows = [25, 50, 100];
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ describe('OHIF Study Viewer Page', function() {
|
||||
|
||||
it('checks if series thumbnails are being displayed', function() {
|
||||
cy.screenshot('Series Thumbnails - Should Display Thumbnails');
|
||||
cy.percyCanvasSnapshot('Series Thumbnails - Should Display Thumbnails');
|
||||
|
||||
cy.get('[data-cy="thumbnail-list"]')
|
||||
.its('length')
|
||||
@@ -293,7 +292,6 @@ describe('OHIF Study Viewer Page', function() {
|
||||
|
||||
// Visual comparison
|
||||
cy.screenshot('About modal - Should display modal');
|
||||
cy.percyCanvasSnapshot('About modal - Should display modal');
|
||||
|
||||
//close modal
|
||||
cy.get('[data-cy="close-button"]').click();
|
||||
|
||||
@@ -24,10 +24,7 @@ describe('OHIF User Preferences', () => {
|
||||
|
||||
// Visual comparison
|
||||
cy.screenshot(
|
||||
'User Preferences Modal - Hotkeys tab initial state in Study Viewer page'
|
||||
);
|
||||
cy.percyCanvasSnapshot(
|
||||
'User Preferences Modal - Hotkeys tab initial state in Study Viewer page'
|
||||
'User Preferences Modal - Hotkeys tab initial state in Study List page'
|
||||
);
|
||||
cy.get('[data-cy="close-button"]').click();
|
||||
});
|
||||
@@ -42,16 +39,10 @@ describe('OHIF User Preferences', () => {
|
||||
|
||||
// Visual comparison
|
||||
cy.screenshot(
|
||||
'User Preferences Modal - General tab initial state in Study Viewer page'
|
||||
'User Preferences Modal - General tab initial state in Study List page'
|
||||
);
|
||||
cy.percyCanvasSnapshot(
|
||||
'User Preferences Modal - General tab initial state in Study Viewer page'
|
||||
);
|
||||
// Select Spanish and Save
|
||||
cy.get('#language-select').select('Spanish');
|
||||
cy.get('@saveBtn')
|
||||
.scrollIntoView()
|
||||
.click();
|
||||
// Set language to Spanish and save
|
||||
cy.setLanguage('Spanish');
|
||||
|
||||
// Header should be translated to Spanish
|
||||
cy.get('.research-use')
|
||||
@@ -161,11 +152,8 @@ describe('OHIF User Preferences', () => {
|
||||
// Go to General tab
|
||||
cy.get('@userPreferencesGeneralTab').click();
|
||||
|
||||
// Select Spanish and Save
|
||||
cy.get('#language-select').select('Spanish');
|
||||
cy.get('@saveBtn')
|
||||
.scrollIntoView()
|
||||
.click();
|
||||
// Set language to Spanish
|
||||
cy.setLanguage('Spanish');
|
||||
|
||||
// Go to Study Viewer page
|
||||
cy.openStudy('MISTER^MR');
|
||||
@@ -191,7 +179,9 @@ describe('OHIF User Preferences', () => {
|
||||
.should('contain.text', 'Preferencias');
|
||||
|
||||
// Check if new hotkey is working on viewport
|
||||
cy.get('body').type('{shift}Q', { release: false });
|
||||
cy.get('body').type('{shift}Q', {
|
||||
release: false,
|
||||
});
|
||||
cy.get('@viewportInfoMidTop').should('contains.text', 'R');
|
||||
});
|
||||
});
|
||||
@@ -225,10 +215,7 @@ describe('OHIF User Preferences', () => {
|
||||
|
||||
// Visual comparison
|
||||
cy.screenshot(
|
||||
'User Preferences Modal - Hotkeys tab initial state in Study List page'
|
||||
);
|
||||
cy.percyCanvasSnapshot(
|
||||
'User Preferences Modal - Hotkeys tab initial state in Study List page'
|
||||
'User Preferences Modal - Hotkeys tab initial state in Study Viewer page'
|
||||
);
|
||||
cy.get('[data-cy="close-button"]').click(); //close User Preferences modal
|
||||
});
|
||||
@@ -243,16 +230,10 @@ describe('OHIF User Preferences', () => {
|
||||
|
||||
// Visual comparison
|
||||
cy.screenshot(
|
||||
'User Preferences Modal - General tab initial state in Study List page'
|
||||
'User Preferences Modal - General tab initial state in Study Viewer page'
|
||||
);
|
||||
cy.percyCanvasSnapshot(
|
||||
'User Preferences Modal - General tab initial state in Study List page'
|
||||
);
|
||||
// Select Spanish and Save
|
||||
cy.get('#language-select').select('Spanish');
|
||||
cy.get('@saveBtn')
|
||||
.scrollIntoView()
|
||||
.click();
|
||||
// Set language to Spanish
|
||||
cy.setLanguage('Spanish');
|
||||
|
||||
// Header should be translated to Spanish
|
||||
cy.get('.research-use')
|
||||
@@ -310,19 +291,11 @@ describe('OHIF User Preferences', () => {
|
||||
// Language dropdown should be displayed
|
||||
cy.get('#language-select').should('be.visible');
|
||||
|
||||
// Select Spanish and Save
|
||||
cy.get('#language-select').select('Spanish');
|
||||
cy.get('@saveBtn')
|
||||
.scrollIntoView()
|
||||
.click();
|
||||
// Set language to Spanish
|
||||
cy.setLanguage('Spanish');
|
||||
|
||||
//Open Preferences again
|
||||
cy.get('[data-cy="options-menu"]')
|
||||
.scrollIntoView()
|
||||
.click();
|
||||
cy.get('[data-cy="dd-item-menu"]')
|
||||
.last()
|
||||
.click();
|
||||
// Open User Preferences modal
|
||||
cy.openPreferences();
|
||||
|
||||
// Go to general tab
|
||||
cy.get('@userPreferencesGeneralTab').click();
|
||||
@@ -437,9 +410,7 @@ describe('OHIF User Preferences', () => {
|
||||
|
||||
// Visual comparison
|
||||
cy.screenshot('Viewport Navigation - 2nd viewport inverted and rotated');
|
||||
cy.percyCanvasSnapshot(
|
||||
'Viewport Navigation - 2nd viewport inverted and rotated'
|
||||
);
|
||||
|
||||
// Set 1 viewport layout
|
||||
cy.setLayout(1, 1);
|
||||
});
|
||||
|
||||
@@ -23,8 +23,5 @@ describe('OHIF Microscopy Extension', () => {
|
||||
cy.wait(3000); //Waiting for image to render before taking the snapshot
|
||||
// Visual comparison
|
||||
cy.screenshot('Microscopy Extension - Should display loaded canvas');
|
||||
cy.percyCanvasSnapshot(
|
||||
'Microscopy Extension - Should display loaded canvas'
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -23,6 +23,5 @@ describe('OHIF PDF Extension', () => {
|
||||
// This won't work unless we switch to an extension that renders using `canvas`
|
||||
// Currently, we rely on the browser's built-in implementation
|
||||
cy.screenshot('PDF Extension - Should load PDF file');
|
||||
cy.percyCanvasSnapshot('PDF Extension - Should load PDF file');
|
||||
});
|
||||
});
|
||||
@@ -24,7 +24,6 @@ describe('OHIF VTK Extension', () => {
|
||||
cy.get('[data-cy="thumbnail-list"]')
|
||||
.contains('CT WB 5.0 B35f')
|
||||
.drag('.viewport-drop-target')
|
||||
.wait(1000); // TODO: Once issue #1167 is fixed, the wait command should be removed. (https://github.com/OHIF/Viewers/issues/1167)
|
||||
|
||||
//Select 2D MPR button
|
||||
cy.get('[data-cy="2d mpr"]').click();
|
||||
@@ -58,9 +57,6 @@ describe('OHIF VTK Extension', () => {
|
||||
|
||||
// Visual comparison
|
||||
cy.screenshot('VTK initial state - Should display toolbar and 3 viewports');
|
||||
cy.percyCanvasSnapshot(
|
||||
'VTK initial state - Should display toolbar and 3 viewports'
|
||||
);
|
||||
});
|
||||
|
||||
it('checks Crosshairs tool', () => {
|
||||
@@ -76,9 +72,6 @@ describe('OHIF VTK Extension', () => {
|
||||
cy.screenshot(
|
||||
"VTK Crosshairs tool - Should display crosshairs' green lines"
|
||||
);
|
||||
cy.percyCanvasSnapshot(
|
||||
"VTK Crosshairs tool - Should display crosshairs' green lines"
|
||||
);
|
||||
});
|
||||
|
||||
/* TODO: Non-deterministic behavior (const expectedText = 'W: 350 L: -1044';)
|
||||
@@ -102,7 +95,6 @@ describe('OHIF VTK Extension', () => {
|
||||
|
||||
// Visual comparison
|
||||
cy.screenshot('VTK WWWC tool - Canvas should be bright');
|
||||
cy.percyCanvasSnapshot('VTK WWWC tool - Canvas should be bright');
|
||||
}); */
|
||||
|
||||
it('checks Rotate tool', () => {
|
||||
@@ -118,6 +110,5 @@ describe('OHIF VTK Extension', () => {
|
||||
|
||||
// Visual comparison
|
||||
cy.screenshot('VTK Rotate tool - Should rotate image');
|
||||
cy.percyCanvasSnapshot('VTK Rotate tool - Should rotate image');
|
||||
});
|
||||
});
|
||||
@@ -1,15 +1,11 @@
|
||||
describe('OHIF Save Measurements', function() {
|
||||
before(() => {
|
||||
cy.openStudy('Fall 1');
|
||||
cy.expectMinimumThumbnails(2);
|
||||
cy.openStudy('MISTER^MR');
|
||||
cy.waitDicomImage();
|
||||
cy.expectMinimumThumbnails(5);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
// Drags Study thumbnail into viewport
|
||||
cy.get('[data-cy="thumbnail-list"]:nth-child(1)')
|
||||
.scrollIntoView()
|
||||
.drag('.viewport-drop-target');
|
||||
|
||||
// Wait image to load on viewport
|
||||
cy.wait(2000);
|
||||
|
||||
@@ -56,7 +52,9 @@ describe('OHIF Save Measurements', function() {
|
||||
cy.get('.sb-success').should('be.visible');
|
||||
|
||||
// Reload the page
|
||||
cy.reload();
|
||||
cy.reload()
|
||||
.wait(1000) //Wait page to load
|
||||
.expectMinimumThumbnails(2); //wait all thumbnails to load
|
||||
|
||||
//Verify that recently added measurement was retrieved
|
||||
cy.get('@measurementsBtn').click();
|
||||
@@ -105,8 +103,7 @@ describe('OHIF Save Measurements', function() {
|
||||
cy.get('.sb-error')
|
||||
.should('be.visible')
|
||||
.and('contains.text', 'Error while saving the measurements');
|
||||
// Close message overlay
|
||||
cy.get('.sb-closeIcon').click();
|
||||
|
||||
// Close Measurements panel
|
||||
cy.get('@measurementsBtn').click();
|
||||
});
|
||||
@@ -135,7 +132,9 @@ describe('OHIF Save Measurements', function() {
|
||||
.and('contains.text', 'Measurements were saved with success');
|
||||
|
||||
// Reload the page
|
||||
cy.reload();
|
||||
cy.reload()
|
||||
.wait(1000) //Wait page to load
|
||||
.expectMinimumThumbnails(2); //wait all thumbnails to load
|
||||
|
||||
//Verify that measurement for unsupported tool was not saved
|
||||
cy.get('@measurementsBtn').click();
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
describe('Visual Regression - OHIF Cornerstone Hotkeys', () => {
|
||||
before(() => {
|
||||
cy.openStudy('MISTER^MR');
|
||||
cy.waitDicomImage();
|
||||
cy.expectMinimumThumbnails(3);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cy.initCornerstoneToolsAliases();
|
||||
cy.initCommonElementsAliases();
|
||||
cy.resetViewport();
|
||||
});
|
||||
|
||||
it('checks if hotkey "I" can invert the image', () => {
|
||||
// Hotkey I
|
||||
cy.get('body').type('I');
|
||||
// Visual comparison
|
||||
cy.screenshot('Hotkey I - Should Invert Image');
|
||||
cy.percyCanvasSnapshot('Hotkey I - Should Invert Image');
|
||||
});
|
||||
|
||||
it('checks if hotkey "SPACEBAR" can reset the image', () => {
|
||||
// Press multiples hotkeys
|
||||
cy.get('body').type('V+++I');
|
||||
|
||||
// Hotkey SPACEBAR
|
||||
cy.get('body').type(' ');
|
||||
|
||||
// Visual comparison to make sure the 'inverted' image was reset
|
||||
cy.screenshot('Hotkey SPACEBAR - Should Reset Image');
|
||||
cy.percyCanvasSnapshot('Hotkey SPACEBAR - Should Reset Image');
|
||||
});
|
||||
});
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
describe('Visual Regression - OHIF Cornerstone Toolbar', () => {
|
||||
before(() => {
|
||||
cy.openStudy('MISTER^MR');
|
||||
cy.waitDicomImage();
|
||||
cy.expectMinimumThumbnails(5);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cy.initCornerstoneToolsAliases();
|
||||
cy.initCommonElementsAliases();
|
||||
cy.resetViewport();
|
||||
});
|
||||
|
||||
it('checks if Pan tool will move the image inside the viewport', () => {
|
||||
//Click on button and vefiry if icon is active on toolbar
|
||||
cy.get('@panBtn')
|
||||
.click()
|
||||
.then($panBtn => {
|
||||
cy.wrap($panBtn).should('have.class', 'active');
|
||||
});
|
||||
|
||||
cy.get('@viewport')
|
||||
.trigger('mousedown', 'center', { which: 1 })
|
||||
.trigger('mousemove', 'bottom', { which: 1 })
|
||||
.trigger('mouseup', 'bottom');
|
||||
|
||||
// Visual comparison
|
||||
cy.percyCanvasSnapshot('Pan tool moved the image inside the viewport');
|
||||
});
|
||||
|
||||
it('check if Invert tool will change the colors of the image in the viewport', () => {
|
||||
// Click on More button
|
||||
cy.get('@moreBtn').click();
|
||||
// Verify if overlay is displayed
|
||||
cy.get('.tooltip-toolbar-overlay').should('be.visible');
|
||||
|
||||
// Click on Invert button
|
||||
cy.get('[data-cy="invert"]').click();
|
||||
|
||||
// Visual comparison
|
||||
cy.percyCanvasSnapshot('Invert tool - Should Invert Canvas');
|
||||
});
|
||||
|
||||
it('check if Rotate tool will change the image orientation in the viewport', () => {
|
||||
//Click on More button
|
||||
cy.get('@moreBtn').click();
|
||||
//Verify if overlay is displayed
|
||||
cy.get('.tooltip-toolbar-overlay')
|
||||
.should('be.visible')
|
||||
.then(() => {
|
||||
//Click on Rotate button
|
||||
cy.get('[data-cy="rotate right"]').click({ force: true });
|
||||
});
|
||||
|
||||
// Visual comparison
|
||||
cy.percyCanvasSnapshot('Rotate tool - Should Rotate Image to Right');
|
||||
});
|
||||
|
||||
it('check if Flip H tool will flip the image horizontally in the viewport', () => {
|
||||
//Click on More button
|
||||
cy.get('@moreBtn').click();
|
||||
//Verify if overlay is displayed
|
||||
cy.get('.tooltip-toolbar-overlay').should('be.visible');
|
||||
|
||||
//Click on Flip H button
|
||||
cy.get('[data-cy="flip h"]').click();
|
||||
|
||||
// Visual comparison
|
||||
cy.percyCanvasSnapshot('Flip H tool - Should Flip Image on Y axis');
|
||||
});
|
||||
|
||||
it('check if Flip V tool will flip the image vertically in the viewport', () => {
|
||||
//Click on More button
|
||||
cy.get('@moreBtn').click();
|
||||
//Verify if overlay is displayed
|
||||
cy.get('.tooltip-toolbar-overlay').should('be.visible');
|
||||
|
||||
//Click on Flip V button
|
||||
cy.get('[data-cy="flip v"]').click();
|
||||
|
||||
// Visual comparison
|
||||
cy.percyCanvasSnapshot('Flip V tool - Should Flip Image on X axis');
|
||||
});
|
||||
});
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
describe('Visual Regression - OHIF Download Snapshot File', () => {
|
||||
before(() => {
|
||||
cy.openStudy('MISTER^MR');
|
||||
cy.waitDicomImage();
|
||||
cy.expectMinimumThumbnails(5);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cy.openDownloadImageModal();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// Close modal
|
||||
cy.get('[data-cy="close-button"]')
|
||||
.scrollIntoView()
|
||||
.click();
|
||||
});
|
||||
|
||||
it('checks displayed information for Tablet experience', function() {
|
||||
// Set Tablet resolution
|
||||
cy.viewport(1000, 660);
|
||||
// Visual comparison
|
||||
cy.percyCanvasSnapshot('Download Image Modal - Tablet experience');
|
||||
});
|
||||
|
||||
it('checks displayed information for Desktop experience', function() {
|
||||
// Set Desktop resolution
|
||||
cy.viewport(1750, 720);
|
||||
// Visual comparison
|
||||
cy.percyCanvasSnapshot('Download Image Modal - Desktop experience');
|
||||
});
|
||||
|
||||
it('checks if "Show Annotations" checkbox will display annotations', function() {
|
||||
// Close modal that is initially opened
|
||||
cy.get('[data-cy="close-button"]').click();
|
||||
|
||||
// Add measurements in the viewport
|
||||
cy.addLengthMeasurement();
|
||||
cy.addAngleMeasurement();
|
||||
|
||||
// Open Modal
|
||||
cy.openDownloadImageModal();
|
||||
// Select "Show Annotations" option
|
||||
cy.get('[data-cy="show-annotations"]').check();
|
||||
// Check image preview
|
||||
cy.get('[data-cy="image-preview"]').scrollIntoView();
|
||||
// Visual comparison
|
||||
cy.percyCanvasSnapshot('Download Image Modal - Show Annotations checked');
|
||||
});
|
||||
});
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
describe('Visual Regression - OHIF Microscopy Extension', () => {
|
||||
before(() => {
|
||||
cy.openStudyModality('SM');
|
||||
cy.expectMinimumThumbnails(6);
|
||||
});
|
||||
|
||||
it('drags and drop a SM thumbnail into viewport', () => {
|
||||
cy.get('[data-cy="thumbnail-list"]')
|
||||
.contains('SM')
|
||||
.drag('.viewport-drop-target');
|
||||
|
||||
cy.get('.DicomMicroscopyViewer')
|
||||
.its('length')
|
||||
.should('be.eq', 1);
|
||||
|
||||
cy.wait(3000); //Waiting for image to render before taking the snapshot
|
||||
// Visual comparison
|
||||
cy.percyCanvasSnapshot(
|
||||
'Microscopy Extension - Should display loaded canvas'
|
||||
);
|
||||
});
|
||||
});
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
describe('Visual Regression - OHIF PDF Extension', () => {
|
||||
before(() => {
|
||||
cy.openStudy('Dummy');
|
||||
cy.expectMinimumThumbnails(6);
|
||||
});
|
||||
|
||||
it('drags and drop a PDF thumbnail into viewport', () => {
|
||||
cy.get('[data-cy="thumbnail-list"]')
|
||||
.contains('DOC')
|
||||
.drag('.viewport-drop-target');
|
||||
|
||||
cy.get('.DicomPDFViewport')
|
||||
.its('length')
|
||||
.should('be.eq', 1);
|
||||
|
||||
// This won't work unless we switch to an extension that renders using `canvas`
|
||||
// Currently, we rely on the browser's built-in implementation
|
||||
cy.percyCanvasSnapshot('PDF Extension - Should load PDF file');
|
||||
});
|
||||
});
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
describe('Visual Regression - OHIF VTK Extension', () => {
|
||||
before(() => {
|
||||
cy.openStudy('Bellona');
|
||||
cy.waitDicomImage();
|
||||
cy.expectMinimumThumbnails(5);
|
||||
|
||||
//Waiting for the desired thumbnail content to be displayed
|
||||
cy.get('[data-cy="thumbnail-list"]').should($list => {
|
||||
expect($list).to.contain('Chest 1x10 Soft');
|
||||
});
|
||||
|
||||
// Drag and drop thumbnail into viewport
|
||||
cy.get('[data-cy="thumbnail-list"]')
|
||||
.contains('Chest 1x10 Soft')
|
||||
.drag('.viewport-drop-target');
|
||||
|
||||
//Select 2D MPR button
|
||||
cy.get('[data-cy="2d mpr"]').click();
|
||||
|
||||
//Wait Reformatting Images
|
||||
cy.waitVTKReformatting();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cy.initVTKToolsAliases();
|
||||
cy.wait(1000); //Wait toolbar to finish loading
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cy.wait(5000); //wait screen loads back after screenshot
|
||||
|
||||
//Select Exit 2D MPR button
|
||||
cy.get('[data-cy="exit 2d mpr"]').should($btn => {
|
||||
expect($btn).to.be.visible;
|
||||
$btn.click();
|
||||
});
|
||||
//Select 2D MPR button
|
||||
cy.get('[data-cy="2d mpr"]').click();
|
||||
});
|
||||
|
||||
it('checks if VTK buttons are displayed on the toolbar', () => {
|
||||
// Visual comparison
|
||||
cy.percyCanvasSnapshot(
|
||||
'VTK initial state - Should display toolbar and 3 viewports'
|
||||
);
|
||||
});
|
||||
|
||||
it('checks Crosshairs tool', () => {
|
||||
cy.get('@crosshairsBtn').click();
|
||||
|
||||
// Click and Move the mouse inside the viewport
|
||||
cy.get('[data-cy="viewport-container-0"]')
|
||||
.trigger('mousedown', 'center', { which: 1 })
|
||||
.trigger('mousemove', 'top', { which: 1 })
|
||||
.trigger('mouseup');
|
||||
|
||||
// Visual comparison
|
||||
cy.percyCanvasSnapshot(
|
||||
"VTK Crosshairs tool - Should display crosshairs' green lines"
|
||||
);
|
||||
});
|
||||
|
||||
it('checks WWWC tool', () => {
|
||||
cy.get('@wwwcBtn').click();
|
||||
|
||||
// Click and Move the mouse inside the viewport
|
||||
cy.get('[data-cy="viewport-container-0"]')
|
||||
.trigger('mousedown', 'center', { which: 1 })
|
||||
.trigger('mousemove', 'top', { which: 1 })
|
||||
.trigger('mousedown', 'center', { which: 1 })
|
||||
.trigger('mousemove', 'top', { which: 1 })
|
||||
.trigger('mouseup', { which: 1 });
|
||||
|
||||
// Visual comparison
|
||||
cy.percyCanvasSnapshot('VTK WWWC tool - Canvas should be bright');
|
||||
});
|
||||
|
||||
it('checks Rotate tool', () => {
|
||||
cy.get('@rotateBtn').click();
|
||||
|
||||
// Click and Move the mouse inside the viewport
|
||||
cy.get('[data-cy="viewport-container-0"]')
|
||||
.trigger('mousedown', 'center', { which: 1 })
|
||||
.trigger('mousemove', 'top', { which: 1 })
|
||||
.trigger('mousedown', 'center', { which: 1 })
|
||||
.trigger('mousemove', 'top', { which: 1 })
|
||||
.trigger('mouseup', { which: 1 });
|
||||
|
||||
// Visual comparison
|
||||
cy.percyCanvasSnapshot('VTK Rotate tool - Should rotate image');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
//We excluded the tests for '**/studies/**' because the bulk/all of our other study/viewer tests use this route
|
||||
|
||||
describe('Visual Regression - OHIF Routes', function() {
|
||||
beforeEach(function() {
|
||||
cy.openStudyList();
|
||||
});
|
||||
|
||||
it('checks PT/CT json url study route', function() {
|
||||
cy.visit(
|
||||
'/viewer?url=https://s3.eu-central-1.amazonaws.com/ohif-viewer/JSON/PTCTStudy.json'
|
||||
);
|
||||
|
||||
cy.server();
|
||||
cy.route('GET', '**/PTCTStudy/**').as('getPTCTStudy');
|
||||
|
||||
cy.wait('@getPTCTStudy.all');
|
||||
cy.get('@getPTCTStudy').should($route => {
|
||||
expect($route.status).to.be.eq(200);
|
||||
});
|
||||
|
||||
cy.percyCanvasSnapshot('PT/CT json study route');
|
||||
});
|
||||
});
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
describe('Visual Regression - OHIF Study Viewer Page', function() {
|
||||
before(function() {
|
||||
cy.openStudy('MISTER^MR');
|
||||
cy.waitDicomImage();
|
||||
cy.expectMinimumThumbnails(6);
|
||||
});
|
||||
|
||||
beforeEach(function() {
|
||||
cy.initCommonElementsAliases();
|
||||
cy.resetViewport();
|
||||
});
|
||||
|
||||
it('checks if series thumbnails are being displayed', function() {
|
||||
cy.percyCanvasSnapshot('Series Thumbnails - Should Display Thumbnails');
|
||||
});
|
||||
|
||||
it('opens About modal and verify the displayed information', function() {
|
||||
cy.get('[data-cy="options-menu"]')
|
||||
.first()
|
||||
.click();
|
||||
cy.get('[data-cy="dd-item-menu"]')
|
||||
.first()
|
||||
.click();
|
||||
cy.get('[data-cy="about-modal"]')
|
||||
.as('aboutOverlay')
|
||||
.should('be.visible');
|
||||
|
||||
// Visual comparison
|
||||
cy.percyCanvasSnapshot('About modal - Should display modal');
|
||||
|
||||
//close modal
|
||||
cy.get('[data-cy="close-button"]').click();
|
||||
cy.get('@aboutOverlay').should('not.be.enabled');
|
||||
});
|
||||
});
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
describe('Visual Regression - OHIF User Preferences', () => {
|
||||
context('Study List Page', function() {
|
||||
before(() => {
|
||||
cy.visit('/');
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
// Open User Preferences modal
|
||||
cy.openPreferences();
|
||||
cy.initPreferencesModalAliases();
|
||||
});
|
||||
|
||||
it('checks displayed information on User Preferences modal', function() {
|
||||
cy.get('@restoreBtn').scrollIntoView();
|
||||
|
||||
// Visual comparison
|
||||
cy.percyCanvasSnapshot(
|
||||
'User Preferences Modal - Hotkeys tab initial state in StudyList page'
|
||||
);
|
||||
cy.get('[data-cy="close-button"]').click();
|
||||
});
|
||||
|
||||
it('checks translation by selecting Spanish language', function() {
|
||||
cy.get('@userPreferencesGeneralTab')
|
||||
.click()
|
||||
.should('have.class', 'active');
|
||||
|
||||
// Language dropdown should be displayed
|
||||
cy.get('#language-select').should('be.visible');
|
||||
|
||||
// Visual comparison
|
||||
cy.percyCanvasSnapshot(
|
||||
'User Preferences Modal - General tab initial state in StudyList page'
|
||||
);
|
||||
// Set language to Spanish and save
|
||||
cy.setLanguage('Spanish');
|
||||
|
||||
// Options menu should be translated
|
||||
cy.get('[data-cy="options-menu"]')
|
||||
.should('have.text', 'Opciones')
|
||||
.click();
|
||||
|
||||
// Visual comparison
|
||||
cy.percyCanvasSnapshot(
|
||||
'User Preferences Modal - Spanish selected in StudyList page'
|
||||
);
|
||||
|
||||
// Close Options menu
|
||||
cy.get('[data-cy="options-menu"]').click();
|
||||
});
|
||||
});
|
||||
|
||||
context('Study Viewer Page', function() {
|
||||
before(() => {
|
||||
cy.openStudy('MISTER^MR');
|
||||
cy.expectMinimumThumbnails(5);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cy.initCommonElementsAliases();
|
||||
cy.resetViewport();
|
||||
|
||||
cy.resetUserHoktkeyPreferences();
|
||||
// Open User Preferences modal
|
||||
cy.openPreferences();
|
||||
});
|
||||
|
||||
it('checks displayed information on User Preferences modal', function() {
|
||||
cy.get('@restoreBtn').scrollIntoView();
|
||||
|
||||
// Visual comparison
|
||||
cy.percyCanvasSnapshot(
|
||||
'User Preferences Modal - Hotkeys tab initial state in StudyViewer page'
|
||||
);
|
||||
cy.get('[data-cy="close-button"]').click(); //close User Preferences modal
|
||||
});
|
||||
|
||||
it('checks translation by selecting Spanish language', function() {
|
||||
cy.get('@userPreferencesGeneralTab')
|
||||
.click()
|
||||
.should('have.class', 'active');
|
||||
|
||||
// Visual comparison
|
||||
cy.percyCanvasSnapshot(
|
||||
'User Preferences Modal - General tab initial state in StudyViewer page'
|
||||
);
|
||||
// Set language to Spanish
|
||||
cy.setLanguage('Spanish');
|
||||
|
||||
// Options menu should be translated
|
||||
cy.get('[data-cy="options-menu"]')
|
||||
.should('have.text', 'Opciones')
|
||||
.click();
|
||||
|
||||
// Visual comparison
|
||||
cy.percyCanvasSnapshot(
|
||||
'User Preferences Modal - Spanish selected in StudyViewer page'
|
||||
);
|
||||
|
||||
// Close Options menu
|
||||
cy.get('[data-cy="options-menu"]').click();
|
||||
});
|
||||
|
||||
it('checks if user can restore to default the language selection and application will be in English', function() {
|
||||
cy.get('@userPreferencesGeneralTab')
|
||||
.click()
|
||||
.should('have.class', 'active');
|
||||
|
||||
// Set language to Spanish
|
||||
cy.setLanguage('Spanish');
|
||||
|
||||
// Open User Preferences modal
|
||||
cy.openPreferences();
|
||||
|
||||
// Go to general tab
|
||||
cy.get('@userPreferencesGeneralTab').click();
|
||||
|
||||
cy.get('@restoreBtn')
|
||||
.scrollIntoView()
|
||||
.click();
|
||||
|
||||
cy.get('@saveBtn')
|
||||
.scrollIntoView()
|
||||
.click();
|
||||
|
||||
// Options menu should be in English
|
||||
cy.get('[data-cy="options-menu"]')
|
||||
.should('have.text', 'Options')
|
||||
.click();
|
||||
|
||||
// Visual comparison
|
||||
cy.percyCanvasSnapshot(
|
||||
'User Preferences Modal - English selected in StudyViewer page'
|
||||
);
|
||||
|
||||
// Close Options menu
|
||||
cy.get('[data-cy="options-menu"]').click();
|
||||
});
|
||||
|
||||
it('checks new hotkeys for "Next" and "Previous" Image on Viewport', function() {
|
||||
// Go go hotkeys tab
|
||||
cy.get('@userPreferencesHotkeysTab')
|
||||
.click()
|
||||
.should('have.class', 'active');
|
||||
|
||||
// Set new hotkey for 'Next Image Viewport' function
|
||||
cy.setNewHotkeyShortcutOnUserPreferencesModal(
|
||||
'Next Image Viewport',
|
||||
'{shift}{rightarrow}'
|
||||
);
|
||||
|
||||
// Set new hotkey for 'Previous Image Viewport' function
|
||||
cy.setNewHotkeyShortcutOnUserPreferencesModal(
|
||||
'Previous Image Viewport',
|
||||
'{shift}{leftarrow}'
|
||||
);
|
||||
|
||||
// Save new hotkeys
|
||||
cy.get('@saveBtn')
|
||||
.scrollIntoView()
|
||||
.click();
|
||||
|
||||
// Set 3 viewports layout
|
||||
cy.setLayout(3, 1);
|
||||
cy.waitViewportImageLoading();
|
||||
|
||||
// Rotate Right and Invert colors on Viewport #1
|
||||
cy.get('body').type('RI');
|
||||
|
||||
//Move to Next Viewport
|
||||
cy.get('body').type('{shift}{rightarrow}');
|
||||
// Rotate Left and Invert colors on Viewport #2
|
||||
cy.get('body').type('LI');
|
||||
|
||||
//Move to Previous Viewport
|
||||
cy.get('body').type('{shift}{leftarrow}');
|
||||
// Reset viewport #1 with spacebar hotkey
|
||||
cy.get('body').type(' ');
|
||||
|
||||
// Visual comparison
|
||||
cy.percyCanvasSnapshot(
|
||||
'Viewport Navigation - 2nd viewport inverted and rotated'
|
||||
);
|
||||
// Set 1 viewport layout
|
||||
cy.setLayout(1, 1);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -44,6 +44,7 @@ export function initRouteAliases() {
|
||||
|
||||
//Creating aliases for VTK tools buttons
|
||||
export function initVTKToolsAliases() {
|
||||
cy.get('[data-cy="exit 2d mpr"]').as('exit2dmprBtn');
|
||||
cy.get('[data-cy="crosshairs"]').as('crosshairsBtn');
|
||||
cy.get('[data-cy="wwwc"]').as('wwwcBtn');
|
||||
cy.get('[data-cy="rotate"]').as('rotateBtn');
|
||||
|
||||
@@ -168,7 +168,7 @@ Cypress.Commands.add(
|
||||
);
|
||||
|
||||
Cypress.Commands.add('expectMinimumThumbnails', (seriesToWait = 1) => {
|
||||
cy.get('[data-cy=thumbnail-list]', { timeout: 10000 }).should($itemList => {
|
||||
cy.get('[data-cy=thumbnail-list]', { timeout: 20000 }).should($itemList => {
|
||||
expect($itemList.length >= seriesToWait).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/viewer",
|
||||
"version": "3.3.2",
|
||||
"version": "3.4.8",
|
||||
"description": "OHIF Viewer",
|
||||
"author": "OHIF Contributors",
|
||||
"license": "MIT",
|
||||
@@ -27,7 +27,7 @@
|
||||
"dev:viewer": "yarn run dev",
|
||||
"start": "yarn run dev",
|
||||
"test:e2e": "cypress open",
|
||||
"test:e2e:ci": "percy exec -- cypress run --record --browser chrome",
|
||||
"test:e2e:ci": "percy exec -- cypress run --config video=false --record --browser chrome --spec 'cypress/integration/visual-regression/**/*'",
|
||||
"test:e2e:dist": "start-server-and-test test:e2e:serve http://localhost:3000 test:e2e:ci",
|
||||
"test:e2e:serve": "serve -n -l 3000 -s dist",
|
||||
"test:unit": "jest --watchAll",
|
||||
@@ -45,20 +45,20 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"@ohif/core": "^2.3.0",
|
||||
"@ohif/extension-cornerstone": "^2.2.0",
|
||||
"@ohif/core": "^2.3.6",
|
||||
"@ohif/extension-cornerstone": "^2.2.2",
|
||||
"@ohif/extension-dicom-html": "^1.1.0",
|
||||
"@ohif/extension-dicom-microscopy": "^0.50.6",
|
||||
"@ohif/extension-dicom-pdf": "^1.0.1",
|
||||
"@ohif/extension-vtk": "^1.2.2",
|
||||
"@ohif/extension-vtk": "^1.2.11",
|
||||
"@ohif/i18n": "^0.52.4",
|
||||
"@ohif/ui": "^1.1.7",
|
||||
"@ohif/ui": "^1.1.8",
|
||||
"@tanem/react-nprogress": "^1.1.25",
|
||||
"classnames": "^2.2.6",
|
||||
"core-js": "^3.2.1",
|
||||
"cornerstone-core": "^2.2.8",
|
||||
"cornerstone-math": "^0.1.8",
|
||||
"cornerstone-tools": "^4.9.0",
|
||||
"cornerstone-tools": "^4.12.0",
|
||||
"cornerstone-wado-image-loader": "^3.0.0",
|
||||
"dcmjs": "^0.8.2",
|
||||
"dicom-parser": "^1.8.3",
|
||||
|
||||
@@ -2,6 +2,7 @@ window.config = {
|
||||
routerBasename: '/',
|
||||
whiteLabelling: {},
|
||||
enableGoogleCloudAdapter: true,
|
||||
healthcareApiEndpoint: 'https://healthcare.googleapis.com/v1beta1',
|
||||
servers: {
|
||||
// This is an array, but we'll only use the first entry for now
|
||||
dicomWeb: [],
|
||||
|
||||
@@ -83,6 +83,44 @@ class OHIFStandaloneViewer extends Component {
|
||||
path="/callback"
|
||||
render={() => <CallbackPage userManager={userManager} />}
|
||||
/>
|
||||
<Route
|
||||
path="/login"
|
||||
component={() => {
|
||||
const queryParams = new URLSearchParams(
|
||||
this.props.location.search
|
||||
);
|
||||
const iss = queryParams.get('iss');
|
||||
const loginHint = queryParams.get('login_hint');
|
||||
const targetLinkUri = queryParams.get('target_link_uri');
|
||||
const oidcAuthority =
|
||||
appConfig.oidc !== null && appConfig.oidc[0].authority;
|
||||
if (iss !== oidcAuthority) {
|
||||
console.error(
|
||||
'iss of /login does not match the oidc authority'
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
userManager.removeUser().then(() => {
|
||||
if (targetLinkUri !== null) {
|
||||
sessionStorage.setItem(
|
||||
'ohif-redirect-to',
|
||||
new URL(targetLinkUri).pathname
|
||||
);
|
||||
} else {
|
||||
sessionStorage.setItem('ohif-redirect-to', '/');
|
||||
}
|
||||
|
||||
if (loginHint !== null) {
|
||||
userManager.signinRedirect({ login_hint: loginHint });
|
||||
} else {
|
||||
userManager.signinRedirect();
|
||||
}
|
||||
});
|
||||
|
||||
return null;
|
||||
}}
|
||||
/>
|
||||
<Route
|
||||
component={() => {
|
||||
userManager.getUser().then(user => {
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
import OHIF from '@ohif/core';
|
||||
import React from 'react';
|
||||
import PluginSwitch from './PluginSwitch.js';
|
||||
import { commandsManager } from './../App.js';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
const { setLayout } = OHIF.redux.actions;
|
||||
|
||||
const ConnectedPluginSwitch = props => {
|
||||
return <PluginSwitch {...props} />;
|
||||
};
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const { activeViewportIndex, layout, viewportSpecificData } = state.viewports;
|
||||
|
||||
return {
|
||||
activeViewportIndex,
|
||||
viewportSpecificData,
|
||||
layout,
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
setLayout: data => {
|
||||
dispatch(setLayout(data));
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/*function setSingleLayoutData(originalArray, viewportIndex, data) {
|
||||
const viewports = originalArray.slice();
|
||||
const layoutData = Object.assign({}, viewports[viewportIndex], data);
|
||||
|
||||
viewports[viewportIndex] = layoutData;
|
||||
|
||||
return viewports;
|
||||
}*/
|
||||
|
||||
const mergeProps = (propsFromState, propsFromDispatch, ownProps) => {
|
||||
const { activeViewportIndex, viewportSpecificData } = propsFromState;
|
||||
const { studies } = ownProps;
|
||||
const { setLayout } = propsFromDispatch;
|
||||
|
||||
const mpr = () => {
|
||||
commandsManager.runCommand('mpr2d');
|
||||
};
|
||||
|
||||
const exitMpr = () => {
|
||||
const layout = {
|
||||
numRows: 1,
|
||||
numColumns: 1,
|
||||
viewports: [{ plugin: 'cornerstone' }],
|
||||
};
|
||||
|
||||
setLayout(layout);
|
||||
};
|
||||
|
||||
return {
|
||||
mpr,
|
||||
exitMpr,
|
||||
activeViewportIndex,
|
||||
viewportSpecificData,
|
||||
studies,
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps,
|
||||
mergeProps
|
||||
)(ConnectedPluginSwitch);
|
||||
@@ -0,0 +1,21 @@
|
||||
import { connect } from 'react-redux';
|
||||
import StandaloneRouting from '../routes/StandaloneRouting';
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
activateServer: server => {
|
||||
const action = {
|
||||
type: 'ACTIVATE_SERVER',
|
||||
server,
|
||||
};
|
||||
dispatch(action);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const ConnectedStandaloneRouting = connect(
|
||||
null,
|
||||
mapDispatchToProps
|
||||
)(StandaloneRouting);
|
||||
|
||||
export default ConnectedStandaloneRouting;
|
||||
@@ -2,23 +2,21 @@ import { connect } from 'react-redux';
|
||||
import ViewerRetrieveStudyData from './ViewerRetrieveStudyData.js';
|
||||
import OHIF from "@ohif/core";
|
||||
|
||||
const {
|
||||
clearViewportSpecificData
|
||||
} = OHIF.redux.actions;
|
||||
const { clearViewportSpecificData } = OHIF.redux.actions;
|
||||
const isActive = a => a.active === true;
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
const activeServer = state.servers.servers.find(isActive);
|
||||
|
||||
return {
|
||||
server: activeServer,
|
||||
server: ownProps.server || activeServer,
|
||||
};
|
||||
};
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
clearViewportSpecificData: () => {
|
||||
dispatch(clearViewportSpecificData());
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
.icon-rotate-120 {
|
||||
transform: rotate(120deg);
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { ToolbarButton } from '@ohif/ui';
|
||||
import './PluginSwitch.css';
|
||||
|
||||
class PluginSwitch extends Component {
|
||||
static propTypes = {
|
||||
mpr: PropTypes.func,
|
||||
activeViewportIndex: PropTypes.number,
|
||||
viewportSpecificData: PropTypes.object,
|
||||
studies: PropTypes.array,
|
||||
exitMpr: PropTypes.func,
|
||||
};
|
||||
|
||||
static defaultProps = {};
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isPlugSwitchOn: false,
|
||||
label: '2D MPR',
|
||||
icon: 'cube',
|
||||
};
|
||||
}
|
||||
|
||||
handleClick = () => {
|
||||
if (this.state.isPlugSwitchOn) {
|
||||
this.setState({
|
||||
isPlugSwitchOn: false,
|
||||
label: '2D MPR',
|
||||
icon: 'cube',
|
||||
});
|
||||
this.props.exitMpr();
|
||||
} else {
|
||||
this.setState({
|
||||
isPlugSwitchOn: true,
|
||||
label: 'Exit 2D MPR',
|
||||
icon: 'times',
|
||||
});
|
||||
this.props.mpr();
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { label, icon } = this.state;
|
||||
|
||||
// Render exit mpr if switched on, otherwise check if mpr button should be displayed.
|
||||
const shouldRender =
|
||||
this.state.isPlugSwitchOn || _shouldRenderMpr2DButton.call(this);
|
||||
|
||||
return (
|
||||
<>
|
||||
{shouldRender && (
|
||||
<div className="PluginSwitch">
|
||||
<ToolbarButton
|
||||
label={label}
|
||||
icon={icon}
|
||||
onClick={this.handleClick}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function _shouldRenderMpr2DButton() {
|
||||
const { viewportSpecificData, studies, activeViewportIndex } = this.props;
|
||||
|
||||
if (!viewportSpecificData[activeViewportIndex]) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { displaySetInstanceUid, studyInstanceUid } = viewportSpecificData[
|
||||
activeViewportIndex
|
||||
];
|
||||
|
||||
const displaySet = _getDisplaySet(
|
||||
studies,
|
||||
studyInstanceUid,
|
||||
displaySetInstanceUid
|
||||
);
|
||||
|
||||
if (!displaySet) {
|
||||
return;
|
||||
}
|
||||
|
||||
return displaySet.isReconstructable;
|
||||
}
|
||||
|
||||
function _getDisplaySet(studies, studyInstanceUid, displaySetInstanceUid) {
|
||||
const study = studies.find(
|
||||
study => study.studyInstanceUid === studyInstanceUid
|
||||
);
|
||||
|
||||
if (!study) {
|
||||
return;
|
||||
}
|
||||
|
||||
const displaySet = study.displaySets.find(set => {
|
||||
return set.displaySetInstanceUid === displaySetInstanceUid;
|
||||
});
|
||||
|
||||
return displaySet;
|
||||
}
|
||||
|
||||
export default PluginSwitch;
|
||||
@@ -16,7 +16,6 @@ import { commandsManager, extensionManager } from './../App.js';
|
||||
|
||||
import ConnectedCineDialog from './ConnectedCineDialog';
|
||||
import ConnectedLayoutButton from './ConnectedLayoutButton';
|
||||
import ConnectedPluginSwitch from './ConnectedPluginSwitch.js';
|
||||
|
||||
class ToolbarRow extends Component {
|
||||
// TODO: Simplify these? isOpen can be computed if we say "any" value for selected,
|
||||
@@ -124,7 +123,6 @@ class ToolbarRow extends Component {
|
||||
</div>
|
||||
{buttonComponents}
|
||||
<ConnectedLayoutButton />
|
||||
<ConnectedPluginSwitch studies={this.props.studies} />
|
||||
<div
|
||||
className="pull-right m-t-1 rm-x-1"
|
||||
style={{ marginLeft: 'auto' }}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { useState, useEffect, useContext } from 'react';
|
||||
import { metadata, studies, utils, log } from '@ohif/core';
|
||||
import usePrevious from '../customHooks/usePrevious';
|
||||
|
||||
import ConnectedViewer from './ConnectedViewer.js';
|
||||
import PropTypes from 'prop-types';
|
||||
@@ -128,9 +129,9 @@ const _addSeriesToStudy = (studyMetadata, series) => {
|
||||
const _updateMetaDataManager = (study, studyMetadata, series) => {
|
||||
updateMetaDataManager(study, series);
|
||||
|
||||
const { studyInstanceUID } = study;
|
||||
const { studyInstanceUid } = study;
|
||||
|
||||
if (!studyMetadataManager.get(studyInstanceUID)) {
|
||||
if (!studyMetadataManager.get(studyInstanceUid)) {
|
||||
studyMetadataManager.add(studyMetadata);
|
||||
}
|
||||
};
|
||||
@@ -314,9 +315,15 @@ function ViewerRetrieveStudyData({
|
||||
}
|
||||
};
|
||||
|
||||
const prevStudyInstanceUids = usePrevious(studyInstanceUids);
|
||||
|
||||
useEffect(() => {
|
||||
studyMetadataManager.purge();
|
||||
purgeCancellablePromises();
|
||||
const hasStudyInstanceUidsChanged = !(prevStudyInstanceUids && prevStudyInstanceUids.every(e => studyInstanceUids.includes(e)));
|
||||
|
||||
if (hasStudyInstanceUidsChanged) {
|
||||
studyMetadataManager.purge();
|
||||
purgeCancellablePromises();
|
||||
}
|
||||
}, [studyInstanceUids]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -18,6 +18,7 @@ const getActiveServer = servers => {
|
||||
const getServers = (appConfig, project, location, dataset, dicomStore) => {
|
||||
let servers = [];
|
||||
if (appConfig.enableGoogleCloudAdapter) {
|
||||
GoogleCloudApi.urlBase = appConfig.healthcareApiEndpoint;
|
||||
const pathUrl = GoogleCloudApi.getUrlBaseDicomWeb(
|
||||
project,
|
||||
location,
|
||||
|
||||
@@ -15,7 +15,11 @@ class GoogleCloudApi {
|
||||
}
|
||||
|
||||
get urlBase() {
|
||||
return `https://healthcare.googleapis.com/v1beta1`;
|
||||
return this.healthcareApiEndpoint || 'https://healthcare.googleapis.com/v1beta1';
|
||||
}
|
||||
|
||||
set urlBase(url) {
|
||||
this.healthcareApiEndpoint = url;
|
||||
}
|
||||
|
||||
get urlBaseProject() {
|
||||
|
||||
@@ -19,7 +19,7 @@ const DICOMFileLoader = new (class extends FileLoader {
|
||||
dicomData.meta
|
||||
);
|
||||
} catch (e) {
|
||||
console.log('Error on getting dicom file dataset. It defaults to empty');
|
||||
console.error('Error reading dicom file', e);
|
||||
}
|
||||
// Set imageId on dataset to be consumed later on
|
||||
dataset.imageId = imageId;
|
||||
|
||||
@@ -2,14 +2,16 @@ import React from 'react';
|
||||
import './NotFound.css';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
export default function NotFound() {
|
||||
export default function NotFound({ message = 'Sorry, this page does not exist.', showGoBackButton = true }) {
|
||||
return (
|
||||
<div className={'not-found'}>
|
||||
<div>
|
||||
<h4>Sorry, this page does not exist.</h4>
|
||||
<h5>
|
||||
<Link to={'/'}>Go back to the Study List</Link>
|
||||
</h5>
|
||||
<h4>{message}</h4>
|
||||
{showGoBackButton && (
|
||||
<h5>
|
||||
<Link to={'/'}>Go back to the Study List</Link>
|
||||
</h5>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,29 +1,33 @@
|
||||
import React, { Component } from 'react';
|
||||
import { log, metadata, studies, utils } from '@ohif/core';
|
||||
|
||||
import { log, metadata, utils } from '@ohif/core';
|
||||
import PropTypes from 'prop-types';
|
||||
import ConnectedViewer from '../connectedComponents/ConnectedViewer';
|
||||
import { extensionManager } from './../App.js';
|
||||
import qs from 'querystring';
|
||||
|
||||
const { OHIFStudyMetadata } = metadata;
|
||||
const { retrieveStudiesMetadata } = studies;
|
||||
import { extensionManager } from './../App.js';
|
||||
import ConnectedViewer from '../connectedComponents/ConnectedViewer';
|
||||
import ConnectedViewerRetrieveStudyData from '../connectedComponents/ConnectedViewerRetrieveStudyData';
|
||||
import NotFound from '../routes/NotFound';
|
||||
|
||||
const { studyMetadataManager, updateMetaDataManager } = utils;
|
||||
const { OHIFStudyMetadata } = metadata;
|
||||
|
||||
class StandaloneRouting extends Component {
|
||||
state = {
|
||||
studies: null,
|
||||
server: null,
|
||||
studyInstanceUids: null,
|
||||
seriesInstanceUids: null,
|
||||
error: null,
|
||||
loading: true,
|
||||
};
|
||||
|
||||
studyInstanceUids = [];
|
||||
|
||||
static propTypes = {
|
||||
location: PropTypes.object,
|
||||
store: PropTypes.object,
|
||||
setServers: PropTypes.func,
|
||||
};
|
||||
|
||||
static parseQueryAndFetchStudies(query) {
|
||||
parseQueryAndRetrieveDICOMWebData(query) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const url = query.url;
|
||||
|
||||
@@ -43,7 +47,11 @@ class StandaloneRouting extends Component {
|
||||
|
||||
// When the JSON has been returned, parse it into a JavaScript Object
|
||||
// and render the OHIF Viewer with this data
|
||||
oReq.addEventListener('load', () => {
|
||||
oReq.addEventListener('load', event => {
|
||||
if (event.target.status === 404) {
|
||||
reject(new Error('No JSON data found'));
|
||||
}
|
||||
|
||||
// Parse the response content
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseText
|
||||
if (!oReq.responseText) {
|
||||
@@ -54,27 +62,24 @@ class StandaloneRouting extends Component {
|
||||
log.info(JSON.stringify(oReq.responseText, null, 2));
|
||||
|
||||
const data = JSON.parse(oReq.responseText);
|
||||
if (data.servers && query.studyInstanceUids) {
|
||||
if (data.servers) {
|
||||
if (!query.studyInstanceUids) {
|
||||
log.warn('No study instance uids specified');
|
||||
reject(new Error('No study instance uids specified'));
|
||||
}
|
||||
|
||||
const server = data.servers.dicomWeb[0];
|
||||
server.type = 'dicomWeb';
|
||||
|
||||
const studyInstanceUids = query.studyInstanceUids.split(';');
|
||||
const seriesInstanceUids = [];
|
||||
log.warn('Activating server', server);
|
||||
this.props.activateServer(server);
|
||||
|
||||
retrieveStudiesMetadata(
|
||||
server,
|
||||
studyInstanceUids,
|
||||
seriesInstanceUids
|
||||
).then(
|
||||
studies => {
|
||||
resolve(studies);
|
||||
},
|
||||
error => {
|
||||
reject(error);
|
||||
}
|
||||
);
|
||||
const studyInstanceUids = query.studyInstanceUids.split(';');
|
||||
const seriesInstanceUids = query.seriesInstanceUids ? query.seriesInstanceUids.split(';') : [];
|
||||
|
||||
resolve({ server, studyInstanceUids, seriesInstanceUids });
|
||||
} else {
|
||||
resolve(data.studies);
|
||||
resolve({ studies: data.studies, studyInstanceUids: [] });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -96,56 +101,80 @@ class StandaloneRouting extends Component {
|
||||
|
||||
// Remove ? prefix which is included for some reason
|
||||
search = search.slice(1, search.length);
|
||||
|
||||
const query = qs.parse(search);
|
||||
const studies = await StandaloneRouting.parseQueryAndFetchStudies(query);
|
||||
|
||||
studyMetadataManager.purge();
|
||||
let {
|
||||
server,
|
||||
studies,
|
||||
studyInstanceUids,
|
||||
seriesInstanceUids,
|
||||
} = await this.parseQueryAndRetrieveDICOMWebData(query);
|
||||
|
||||
// Map studies to new format, update metadata manager?
|
||||
const uniqueStudyUids = new Set();
|
||||
const updatedStudies = studies.map(study => {
|
||||
const studyMetadata = new OHIFStudyMetadata(
|
||||
study,
|
||||
study.studyInstanceUid
|
||||
);
|
||||
const sopClassHandlerModules =
|
||||
extensionManager.modules['sopClassHandlerModule'];
|
||||
if (studies) {
|
||||
const {
|
||||
studies: updatedStudies,
|
||||
studyInstanceUids: updatedStudiesInstanceUids,
|
||||
} = _mapStudiesToNewFormat(studies);
|
||||
studies = updatedStudies;
|
||||
studyInstanceUids = updatedStudiesInstanceUids;
|
||||
}
|
||||
|
||||
study.displaySets =
|
||||
study.displaySets ||
|
||||
studyMetadata.createDisplaySets(sopClassHandlerModules);
|
||||
studyMetadata.setDisplaySets(study.displaySets);
|
||||
|
||||
// Updates WADO-RS metaDataManager
|
||||
updateMetaDataManager(study);
|
||||
|
||||
studyMetadataManager.add(studyMetadata);
|
||||
uniqueStudyUids.add(study.studyInstanceUid);
|
||||
|
||||
return study;
|
||||
this.setState({
|
||||
studies,
|
||||
server,
|
||||
studyInstanceUids,
|
||||
seriesInstanceUids,
|
||||
loading: false,
|
||||
});
|
||||
|
||||
this.studyInstanceUids = Array.from(uniqueStudyUids);
|
||||
this.setState({ studies: updatedStudies });
|
||||
} catch (error) {
|
||||
this.setState({ error });
|
||||
this.setState({ error: error.message, loading: false });
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.error) {
|
||||
return <div>Error: {JSON.stringify(this.state.error)}</div>;
|
||||
} else if (!this.state.studies) {
|
||||
return <div>Loading...</div>;
|
||||
const message = this.state.error ? `Error: ${JSON.stringify(this.state.error)}` : 'Loading...';
|
||||
if (this.state.error || this.state.loading) {
|
||||
return <NotFound message={message} showGoBackButton={this.state.error} />;
|
||||
}
|
||||
return (
|
||||
<ConnectedViewer
|
||||
studies={this.state.studies}
|
||||
studyInstanceUids={this.studyInstanceUids}
|
||||
/>
|
||||
);
|
||||
|
||||
return this.state.studies ? (
|
||||
<ConnectedViewer studies={this.state.studies} />
|
||||
) : (
|
||||
<ConnectedViewerRetrieveStudyData
|
||||
studyInstanceUids={this.state.studyInstanceUids}
|
||||
seriesInstanceUids={this.state.seriesInstanceUids}
|
||||
server={this.state.server}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const _mapStudiesToNewFormat = studies => {
|
||||
studyMetadataManager.purge();
|
||||
|
||||
/* Map studies to new format, update metadata manager? */
|
||||
const uniqueStudyUids = new Set();
|
||||
const updatedStudies = studies.map(study => {
|
||||
const studyMetadata = new OHIFStudyMetadata(study, study.studyInstanceUid);
|
||||
|
||||
const sopClassHandlerModules = extensionManager.modules['sopClassHandlerModule'];
|
||||
study.displaySets = study.displaySets ||
|
||||
studyMetadata.createDisplaySets(sopClassHandlerModules);
|
||||
studyMetadata.setDisplaySets(study.displaySets);
|
||||
|
||||
/* Updates WADO-RS metaDataManager */
|
||||
updateMetaDataManager(study);
|
||||
|
||||
studyMetadataManager.add(studyMetadata);
|
||||
uniqueStudyUids.add(study.studyInstanceUid);
|
||||
|
||||
return study;
|
||||
});
|
||||
|
||||
return {
|
||||
studies: updatedStudies,
|
||||
studyInstanceUids: Array.from(uniqueStudyUids),
|
||||
};
|
||||
};
|
||||
|
||||
export default StandaloneRouting;
|
||||
@@ -19,7 +19,7 @@ const StudyListRouting = asyncComponent(() =>
|
||||
)
|
||||
);
|
||||
const StandaloneRouting = asyncComponent(() =>
|
||||
import(/* webpackChunkName: "StandaloneRouting" */ './StandaloneRouting.js')
|
||||
import(/* webpackChunkName: "ConnectedStandaloneRouting" */ '../connectedComponents/ConnectedStandaloneRouting.js')
|
||||
);
|
||||
const ViewerLocalFileData = asyncComponent(() =>
|
||||
import(
|
||||
|
||||
@@ -5962,10 +5962,10 @@ cornerstone-math@^0.1.8:
|
||||
resolved "https://registry.yarnpkg.com/cornerstone-math/-/cornerstone-math-0.1.8.tgz#68ab1f9e4fdcd7c5cb23a0d2eb4263f9f894f1c5"
|
||||
integrity sha512-x7NEQHBtVG7j1yeyj/aRoKTpXv1Vh2/H9zNLMyqYJDtJkNng8C4Q8M3CgZ1qer0Yr7eVq2x+Ynmj6kfOm5jXKw==
|
||||
|
||||
cornerstone-tools@^4.9.0:
|
||||
version "4.9.0"
|
||||
resolved "https://registry.yarnpkg.com/cornerstone-tools/-/cornerstone-tools-4.9.0.tgz#8b68603c32aceb84baf860f4dd0d36ac176e8b75"
|
||||
integrity sha512-6yHzXm4qn7VuHMRiaV/oS3khdMidtTho6P1OIYICqZ2pmorsH8jN8LvNgqz+Ky9Nf+wvrvi7ul8ZzcD1sJTkWA==
|
||||
cornerstone-tools@^4.12.0:
|
||||
version "4.12.0"
|
||||
resolved "https://registry.yarnpkg.com/cornerstone-tools/-/cornerstone-tools-4.12.0.tgz#1adfdac3e1569ca3227e656e2b24ffe8db1fc545"
|
||||
integrity sha512-4ecWbIrufF2Pj04XopcZU95A0iSJUAFa7vZiCVVh/okI6fNIaRe3OkyG5ZZ/7GYIzNfVAQ4TK+yY6UOA3bXTdQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "7.1.2"
|
||||
cornerstone-math "0.1.7"
|
||||
|
||||
Reference in new issue
Block a user