fix: cache thumbnail in display set (#4782)
This commit is contained in:
parent
d26c9551b6
commit
2410c6a509
@ -341,8 +341,10 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- install_bun
|
- install_bun
|
||||||
- run:
|
- run:
|
||||||
name: Install Xvfb
|
name: Install System Dependencies
|
||||||
command: sudo apt-get update && sudo apt-get install -y xvfb
|
command: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y xvfb libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 libxtst6
|
||||||
- run:
|
- run:
|
||||||
name: Start Xvfb
|
name: Start Xvfb
|
||||||
command: Xvfb :99 -screen 0 1920x1080x24 &
|
command: Xvfb :99 -screen 0 1920x1080x24 &
|
||||||
@ -351,13 +353,11 @@ jobs:
|
|||||||
name: Export Display Variable
|
name: Export Display Variable
|
||||||
command: export DISPLAY=:99
|
command: export DISPLAY=:99
|
||||||
- cypress/install:
|
- cypress/install:
|
||||||
install-browsers: true
|
|
||||||
install-command: bun install --no-save
|
install-command: bun install --no-save
|
||||||
package-manager: yarn
|
package-manager: yarn
|
||||||
- cypress/run-tests:
|
- cypress/run-tests:
|
||||||
cypress-command:
|
cypress-command: |
|
||||||
npx wait-on@latest http://localhost:3000 && cd platform/app && npx cypress run --record
|
npx wait-on@latest http://localhost:3000 && cd platform/app && npx cypress run --record --parallel
|
||||||
--browser chrome --parallel
|
|
||||||
start-command: bun run test:data && bun run test:e2e:serve
|
start-command: bun run test:data && bun run test:e2e:serve
|
||||||
|
|
||||||
DOCKER_MULTIARCH_MANIFEST:
|
DOCKER_MULTIARCH_MANIFEST:
|
||||||
|
|||||||
@ -25,6 +25,14 @@ export function getToolbarModule({ servicesManager }: withAppTypes) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const activeSegmentation = segmentationService.getActiveSegmentation(viewportId);
|
||||||
|
if (!Object.keys(activeSegmentation.segments).length) {
|
||||||
|
return {
|
||||||
|
disabled: true,
|
||||||
|
disabledText: 'Add segment to enable this tool',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);
|
const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);
|
||||||
|
|
||||||
if (!toolGroup) {
|
if (!toolGroup) {
|
||||||
|
|||||||
@ -809,7 +809,8 @@ class SegmentationService extends PubSubService {
|
|||||||
if (!segmentIndex) {
|
if (!segmentIndex) {
|
||||||
// grab the next available segment index based on the object keys,
|
// grab the next available segment index based on the object keys,
|
||||||
// so basically get the highest segment index value + 1
|
// so basically get the highest segment index value + 1
|
||||||
segmentIndex = Math.max(...Object.keys(csSegmentation.segments).map(Number)) + 1;
|
const segmentKeys = Object.keys(csSegmentation.segments);
|
||||||
|
segmentIndex = segmentKeys.length === 0 ? 1 : Math.max(...segmentKeys.map(Number)) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// update the segmentation
|
// update the segmentation
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
|
|||||||
import { useImageViewer } from '@ohif/ui';
|
import { useImageViewer } from '@ohif/ui';
|
||||||
import { useViewportGrid } from '@ohif/ui-next';
|
import { useViewportGrid } from '@ohif/ui-next';
|
||||||
import { StudyBrowser } from '@ohif/ui-next';
|
import { StudyBrowser } from '@ohif/ui-next';
|
||||||
import { utils } from '@ohif/core';
|
import { useSystem, utils } from '@ohif/core';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { Separator } from '@ohif/ui-next';
|
import { Separator } from '@ohif/ui-next';
|
||||||
import { PanelStudyBrowserHeader } from './PanelStudyBrowserHeader';
|
import { PanelStudyBrowserHeader } from './PanelStudyBrowserHeader';
|
||||||
@ -16,13 +16,12 @@ const { sortStudyInstances, formatDate, createStudyBrowserTabs } = utils;
|
|||||||
* @param {*} param0
|
* @param {*} param0
|
||||||
*/
|
*/
|
||||||
function PanelStudyBrowser({
|
function PanelStudyBrowser({
|
||||||
servicesManager,
|
|
||||||
getImageSrc,
|
getImageSrc,
|
||||||
getStudiesForPatientByMRN,
|
getStudiesForPatientByMRN,
|
||||||
requestDisplaySetCreationForStudy,
|
requestDisplaySetCreationForStudy,
|
||||||
dataSource,
|
dataSource,
|
||||||
commandsManager,
|
}) {
|
||||||
}: withAppTypes) {
|
const { servicesManager, commandsManager } = useSystem();
|
||||||
const { hangingProtocolService, displaySetService, uiNotificationService, customizationService } =
|
const { hangingProtocolService, displaySetService, uiNotificationService, customizationService } =
|
||||||
servicesManager.services;
|
servicesManager.services;
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import PanelStudyBrowser from './StudyBrowser/PanelStudyBrowser';
|
|||||||
import getImageSrcFromImageId from './getImageSrcFromImageId';
|
import getImageSrcFromImageId from './getImageSrcFromImageId';
|
||||||
import getStudiesForPatientByMRN from './getStudiesForPatientByMRN';
|
import getStudiesForPatientByMRN from './getStudiesForPatientByMRN';
|
||||||
import requestDisplaySetCreationForStudy from './requestDisplaySetCreationForStudy';
|
import requestDisplaySetCreationForStudy from './requestDisplaySetCreationForStudy';
|
||||||
|
import { useSystem } from '@ohif/core';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps the PanelStudyBrowser and provides features afforded by managers/services
|
* Wraps the PanelStudyBrowser and provides features afforded by managers/services
|
||||||
@ -13,7 +14,8 @@ import requestDisplaySetCreationForStudy from './requestDisplaySetCreationForStu
|
|||||||
* @param {object} commandsManager
|
* @param {object} commandsManager
|
||||||
* @param {object} extensionManager
|
* @param {object} extensionManager
|
||||||
*/
|
*/
|
||||||
function WrappedPanelStudyBrowser({ extensionManager, servicesManager, commandsManager }) {
|
function WrappedPanelStudyBrowser() {
|
||||||
|
const { extensionManager } = useSystem();
|
||||||
// TODO: This should be made available a different way; route should have
|
// TODO: This should be made available a different way; route should have
|
||||||
// already determined our datasource
|
// already determined our datasource
|
||||||
const [dataSource] = extensionManager.getActiveDataSource();
|
const [dataSource] = extensionManager.getActiveDataSource();
|
||||||
@ -29,8 +31,6 @@ function WrappedPanelStudyBrowser({ extensionManager, servicesManager, commandsM
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PanelStudyBrowser
|
<PanelStudyBrowser
|
||||||
servicesManager={servicesManager}
|
|
||||||
commandsManager={commandsManager}
|
|
||||||
dataSource={dataSource}
|
dataSource={dataSource}
|
||||||
getImageSrc={_getImageSrcFromImageId}
|
getImageSrc={_getImageSrcFromImageId}
|
||||||
getStudiesForPatientByMRN={_getStudiesForPatientByMRN}
|
getStudiesForPatientByMRN={_getStudiesForPatientByMRN}
|
||||||
@ -61,10 +61,4 @@ function _createGetImageSrcFromImageIdFn(extensionManager) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WrappedPanelStudyBrowser.propTypes = {
|
|
||||||
commandsManager: PropTypes.object.isRequired,
|
|
||||||
extensionManager: PropTypes.object.isRequired,
|
|
||||||
servicesManager: PropTypes.object.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default WrappedPanelStudyBrowser;
|
export default WrappedPanelStudyBrowser;
|
||||||
|
|||||||
@ -293,9 +293,6 @@ function TrackedMeasurementsContextProvider(
|
|||||||
|
|
||||||
TrackedMeasurementsContextProvider.propTypes = {
|
TrackedMeasurementsContextProvider.propTypes = {
|
||||||
children: PropTypes.oneOf([PropTypes.func, PropTypes.node]),
|
children: PropTypes.oneOf([PropTypes.func, PropTypes.node]),
|
||||||
servicesManager: PropTypes.object.isRequired,
|
|
||||||
commandsManager: PropTypes.object.isRequired,
|
|
||||||
extensionManager: PropTypes.object.isRequired,
|
|
||||||
appConfig: PropTypes.object,
|
appConfig: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -15,14 +15,7 @@ function getPanelModule({ commandsManager, extensionManager, servicesManager }):
|
|||||||
iconName: 'tab-studies',
|
iconName: 'tab-studies',
|
||||||
iconLabel: 'Studies',
|
iconLabel: 'Studies',
|
||||||
label: i18n.t('SidePanel:Studies'),
|
label: i18n.t('SidePanel:Studies'),
|
||||||
component: props => (
|
component: props => <PanelStudyBrowserTracking {...props} />,
|
||||||
<PanelStudyBrowserTracking
|
|
||||||
{...props}
|
|
||||||
commandsManager={commandsManager}
|
|
||||||
extensionManager={extensionManager}
|
|
||||||
servicesManager={servicesManager}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'trackedMeasurements',
|
name: 'trackedMeasurements',
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
|
|||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { utils } from '@ohif/core';
|
import { useSystem, utils } from '@ohif/core';
|
||||||
import { useImageViewer, Dialog, ButtonEnums } from '@ohif/ui';
|
import { useImageViewer, Dialog, ButtonEnums } from '@ohif/ui';
|
||||||
import { useViewportGrid } from '@ohif/ui-next';
|
import { useViewportGrid } from '@ohif/ui-next';
|
||||||
import { StudyBrowser } from '@ohif/ui-next';
|
import { StudyBrowser } from '@ohif/ui-next';
|
||||||
@ -30,13 +30,12 @@ const thumbnailNoImageModalities = [
|
|||||||
* @param {*} param0
|
* @param {*} param0
|
||||||
*/
|
*/
|
||||||
export default function PanelStudyBrowserTracking({
|
export default function PanelStudyBrowserTracking({
|
||||||
servicesManager,
|
|
||||||
getImageSrc,
|
getImageSrc,
|
||||||
getStudiesForPatientByMRN,
|
getStudiesForPatientByMRN,
|
||||||
requestDisplaySetCreationForStudy,
|
requestDisplaySetCreationForStudy,
|
||||||
dataSource,
|
dataSource,
|
||||||
commandsManager,
|
}) {
|
||||||
}: withAppTypes) {
|
const { servicesManager, commandsManager } = useSystem();
|
||||||
const {
|
const {
|
||||||
displaySetService,
|
displaySetService,
|
||||||
uiDialogService,
|
uiDialogService,
|
||||||
@ -183,7 +182,7 @@ export default function PanelStudyBrowserTracking({
|
|||||||
// so wait a bit of time to allow the viewports preferential loading
|
// so wait a bit of time to allow the viewports preferential loading
|
||||||
// which improves user experience of responsiveness significantly on slower
|
// which improves user experience of responsiveness significantly on slower
|
||||||
// systems.
|
// systems.
|
||||||
window.setTimeout(() => setHasLoadedViewports(true), 250);
|
window.setTimeout(() => setHasLoadedViewports(true), 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -211,7 +210,15 @@ export default function PanelStudyBrowserTracking({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// When the image arrives, render it and store the result in the thumbnailImgSrcMap
|
// When the image arrives, render it and store the result in the thumbnailImgSrcMap
|
||||||
newImageSrcEntry[dSet.displaySetInstanceUID] = await getImageSrc(imageId);
|
let { thumbnailSrc } = displaySet;
|
||||||
|
if (!thumbnailSrc && displaySet.getThumbnailSrc) {
|
||||||
|
thumbnailSrc = await displaySet.getThumbnailSrc();
|
||||||
|
}
|
||||||
|
if (!thumbnailSrc) {
|
||||||
|
let thumbnailSrc = await getImageSrc(imageId);
|
||||||
|
displaySet.thumbnailSrc = thumbnailSrc;
|
||||||
|
}
|
||||||
|
newImageSrcEntry[dSet.displaySetInstanceUID] = thumbnailSrc;
|
||||||
|
|
||||||
setThumbnailImageSrcMap(prevState => {
|
setThumbnailImageSrcMap(prevState => {
|
||||||
return { ...prevState, ...newImageSrcEntry };
|
return { ...prevState, ...newImageSrcEntry };
|
||||||
@ -532,7 +539,6 @@ export default function PanelStudyBrowserTracking({
|
|||||||
}
|
}
|
||||||
|
|
||||||
PanelStudyBrowserTracking.propTypes = {
|
PanelStudyBrowserTracking.propTypes = {
|
||||||
servicesManager: PropTypes.object.isRequired,
|
|
||||||
dataSource: PropTypes.shape({
|
dataSource: PropTypes.shape({
|
||||||
getImageIdsForDisplaySet: PropTypes.func.isRequired,
|
getImageIdsForDisplaySet: PropTypes.func.isRequired,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
@ -593,13 +599,12 @@ function _mapDisplaySets(
|
|||||||
displaySets
|
displaySets
|
||||||
.filter(ds => !ds.excludeFromThumbnailBrowser)
|
.filter(ds => !ds.excludeFromThumbnailBrowser)
|
||||||
.forEach(ds => {
|
.forEach(ds => {
|
||||||
const imageSrc = thumbnailImageSrcMap[ds.displaySetInstanceUID];
|
const { thumbnailSrc, displaySetInstanceUID } = ds; // thumbnailImageSrcMap[ds.displaySetInstanceUID];
|
||||||
const componentType = _getComponentType(ds);
|
const componentType = _getComponentType(ds);
|
||||||
|
|
||||||
const array =
|
const array =
|
||||||
componentType === 'thumbnailTracked' ? thumbnailDisplaySets : thumbnailNoImageDisplaySets;
|
componentType === 'thumbnailTracked' ? thumbnailDisplaySets : thumbnailNoImageDisplaySets;
|
||||||
|
|
||||||
const { displaySetInstanceUID } = ds;
|
|
||||||
const loadingProgress = displaySetLoadingState?.[displaySetInstanceUID];
|
const loadingProgress = displaySetLoadingState?.[displaySetInstanceUID];
|
||||||
|
|
||||||
const thumbnailProps = {
|
const thumbnailProps = {
|
||||||
@ -614,7 +619,7 @@ function _mapDisplaySets(
|
|||||||
messages: ds.messages,
|
messages: ds.messages,
|
||||||
StudyInstanceUID: ds.StudyInstanceUID,
|
StudyInstanceUID: ds.StudyInstanceUID,
|
||||||
componentType,
|
componentType,
|
||||||
imageSrc,
|
imageSrc: thumbnailSrc || thumbnailImageSrcMap[displaySetInstanceUID],
|
||||||
dragData: {
|
dragData: {
|
||||||
type: 'displayset',
|
type: 'displayset',
|
||||||
displaySetInstanceUID,
|
displaySetInstanceUID,
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
|
|||||||
import PanelStudyBrowserTracking from './PanelStudyBrowserTracking';
|
import PanelStudyBrowserTracking from './PanelStudyBrowserTracking';
|
||||||
import getImageSrcFromImageId from './getImageSrcFromImageId';
|
import getImageSrcFromImageId from './getImageSrcFromImageId';
|
||||||
import { requestDisplaySetCreationForStudy } from '@ohif/extension-default';
|
import { requestDisplaySetCreationForStudy } from '@ohif/extension-default';
|
||||||
|
import { useSystem } from '@ohif/core';
|
||||||
|
|
||||||
function _getStudyForPatientUtility(extensionManager) {
|
function _getStudyForPatientUtility(extensionManager) {
|
||||||
const utilityModule = extensionManager.getModuleEntry(
|
const utilityModule = extensionManager.getModuleEntry(
|
||||||
@ -21,11 +22,8 @@ function _getStudyForPatientUtility(extensionManager) {
|
|||||||
* @param {object} commandsManager
|
* @param {object} commandsManager
|
||||||
* @param {object} extensionManager
|
* @param {object} extensionManager
|
||||||
*/
|
*/
|
||||||
function WrappedPanelStudyBrowserTracking({
|
function WrappedPanelStudyBrowserTracking() {
|
||||||
commandsManager,
|
const { extensionManager } = useSystem();
|
||||||
extensionManager,
|
|
||||||
servicesManager,
|
|
||||||
}: withAppTypes) {
|
|
||||||
const dataSource = extensionManager.getActiveDataSource()[0];
|
const dataSource = extensionManager.getActiveDataSource()[0];
|
||||||
|
|
||||||
const getStudiesForPatientByMRN = _getStudyForPatientUtility(extensionManager);
|
const getStudiesForPatientByMRN = _getStudyForPatientUtility(extensionManager);
|
||||||
@ -41,8 +39,6 @@ function WrappedPanelStudyBrowserTracking({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PanelStudyBrowserTracking
|
<PanelStudyBrowserTracking
|
||||||
servicesManager={servicesManager}
|
|
||||||
commandsManager={commandsManager}
|
|
||||||
dataSource={dataSource}
|
dataSource={dataSource}
|
||||||
getImageSrc={_getImageSrcFromImageId}
|
getImageSrc={_getImageSrcFromImageId}
|
||||||
getStudiesForPatientByMRN={_getStudiesForPatientByMRN}
|
getStudiesForPatientByMRN={_getStudiesForPatientByMRN}
|
||||||
@ -73,10 +69,4 @@ function _createGetImageSrcFromImageIdFn(extensionManager) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WrappedPanelStudyBrowserTracking.propTypes = {
|
|
||||||
commandsManager: PropTypes.object.isRequired,
|
|
||||||
extensionManager: PropTypes.object.isRequired,
|
|
||||||
servicesManager: PropTypes.object.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default WrappedPanelStudyBrowserTracking;
|
export default WrappedPanelStudyBrowserTracking;
|
||||||
|
|||||||
@ -12,6 +12,12 @@ export type DisplaySet = {
|
|||||||
Modality?: string;
|
Modality?: string;
|
||||||
imageIds?: string[];
|
imageIds?: string[];
|
||||||
images?: unknown[];
|
images?: unknown[];
|
||||||
|
|
||||||
|
// Details about how to display:
|
||||||
|
/** A URL that can be used to display the thumbnail. Typically a data url */
|
||||||
|
thumbnailSrc?: string;
|
||||||
|
/** A fetch method to get the thumbnail */
|
||||||
|
getThumbnailSrc?(imageId?: string): Promise<string>;
|
||||||
SeriesDate?: string;
|
SeriesDate?: string;
|
||||||
SeriesTime?: string;
|
SeriesTime?: string;
|
||||||
instance?: InstanceMetadata;
|
instance?: InstanceMetadata;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user