start cleaning up panels

This commit is contained in:
dannyrb 2020-05-18 15:05:49 -04:00
parent d0c3fb6db0
commit 351e0f76d6
4 changed files with 304 additions and 280 deletions

View File

@ -0,0 +1,257 @@
import React, { useState, useEffect, useCallback } from 'react';
import PropTypes from 'prop-types';
import { StudyBrowser } from '@ohif/ui';
import { dicomMetadataStore, useViewModel } from '@ohif/core';
function PanelStudyBrowser({ getDataSources, commandsManager }) {
console.warn('StudyBrowserPanel rerender');
const [activeTabName, setActiveTabName] = useState('primary');
const [studyData, setStudyData] = useState([]);
const [thumbnailImageSrcMap, setThumbnailImageSrcMap] = useState(new Map());
const updateThumbnailMap = (k, v) => {
setThumbnailImageSrcMap(thumbnailImageSrcMap.set(k, v));
};
const viewModel = useViewModel();
const dataSource = getDataSources('dicomweb')[0];
const viewportData = []; //useViewportGrid();
const seriesTracking = {}; //useSeriesTracking();
// This effect
useEffect(() => {
if (!viewModel.displaySetInstanceUIDs.length) {
return;
}
let isSubscribed = true;
const command = commandsManager.getCommand(
'getCornerstoneLibraries',
'VIEWER'
);
if (!command) {
throw new Error('Required command not found');
}
const { cornerstone, cornerstoneTools } = command.commandFn();
viewModel.displaySetInstanceUIDs.forEach(uid => {
const imageIds = dataSource.getImageIdsForDisplaySet(uid);
const imageId = imageIds[Math.floor(imageIds.length / 2)];
getImageSrc(imageId, { cornerstone }).then(imageSrc => {
if (isSubscribed) {
updateThumbnailMap(uid, imageSrc);
}
});
});
const displaySets = viewModel.displaySetInstanceUIDs.map(
displaySetManager.getDisplaySetByUID
);
const aDisplaySet = displaySets[0];
const firstStudy = dicomMetadataStore.getStudy(
aDisplaySet.StudyInstanceUID
);
const firstInstance = firstStudy.series[0].instances[0];
const PatientID = firstInstance.PatientID;
dataSource.query.studies.search({ patientId: PatientID }).then(results => {
const studies = results.map(study => {
// TODO: Why does the data source return in this format?
return {
AccessionNumber: study.accession,
StudyDate: study.date,
StudyDescription: study.description,
NumInstances: study.instances,
ModalitiesInStudy: study.modalities,
PatientID: study.mrn,
PatientName: study.patientName,
StudyInstanceUID: study.studyInstanceUid,
StudyTime: study.time,
};
});
const studiesFromInstanceData = {};
displaySets.forEach(ds => {
const displaySet = {
displaySetInstanceUID: ds.displaySetInstanceUID,
description: ds.SeriesDescription,
seriesNumber: ds.SeriesNumber,
modality: ds.Modality,
date: ds.SeriesDate,
numInstances: ds.numImageFrames,
};
const displaySetViewportData = viewportData.find(
a => a.displaySetInstanceUID === ds.displaySetInstanceUID
);
if (displaySetViewportData) {
displaySet.viewportIdentificator = displaySetViewportData.identifier;
}
const trackingInfo = seriesTracking[ds.SeriesInstanceUID];
if (trackingInfo) {
displaySet.isTracked = trackingInfo.isTracked;
} else {
displaySet.isTracked = false;
}
displaySet.componentType = 'thumbnailTracked';
if (
!Object.keys(studiesFromInstanceData).includes(ds.StudyInstanceUID)
) {
const study = dicomMetadataStore.getStudy(ds.StudyInstanceUID);
const anInstance = study.series[0].instances[0];
studiesFromInstanceData[ds.StudyInstanceUID] = {
date: anInstance.StudyDate, // TODO: Format this date to DD-MMM-YYYY
description: anInstance.StudyDescription,
displaySets: [],
numInstances: 0,
modalitiesSet: new Set(),
};
}
studiesFromInstanceData[ds.StudyInstanceUID].displaySets.push(
displaySet
);
studiesFromInstanceData[ds.StudyInstanceUID].numInstances +=
displaySet.numInstances;
studiesFromInstanceData[ds.StudyInstanceUID].modalitiesSet.add(
displaySet.modality
);
const modalitiesSet =
studiesFromInstanceData[ds.StudyInstanceUID].modalitiesSet;
studiesFromInstanceData[ds.StudyInstanceUID].modalities = Array.from(
modalitiesSet
).join(', ');
});
// QIDO for all by MRN
const allStudies = studies.map(studyLevelData => {
const studyFromInstanceData =
studiesFromInstanceData[studyLevelData.StudyInstanceUID];
if (!studyFromInstanceData) {
return {
studyInstanceUid: studyLevelData.StudyInstanceUID,
date: studyLevelData.StudyDate,
description: studyLevelData.StudyDescription,
modalities: studyLevelData.ModalitiesInStudy,
numInstances: studyLevelData.NumInstances,
displaySets: [],
};
}
return {
studyInstanceUid: studyLevelData.StudyInstanceUID,
date: studyLevelData.StudyDate || studyFromInstanceData.date,
description:
studyLevelData.StudyDescription ||
studyFromInstanceData.description,
modalities:
studyFromInstanceData.modalities ||
studyLevelData.ModalitiesInStudy,
numInstances:
studyLevelData.NumInstances || studyFromInstanceData.numInstances,
displaySets: studyFromInstanceData.displaySets,
};
});
if (isSubscribed) {
setStudyData(allStudies);
}
});
return () => (isSubscribed = false);
}, [viewModel.displaySetInstanceUIDs, thumbnailImageSrcMap, setStudyData]);
studyData.forEach(study => {
study.displaySets.forEach(ds => {
ds.imageSrc = thumbnailImageSrcMap.get(ds.displaySetInstanceUID);
});
});
const primary = studyData.find(study => {
return true; // TODO: check study.StudyInstanceUID matches queryparam?
});
// TODO: Filter allStudies to dates within one year of current date
const recentStudies = studyData.filter(study => {
return true; // TODO: check study.date
});
const tabs = [
{
name: 'primary',
label: 'Primary',
studies: primary ? [primary] : [],
},
{
name: 'recent',
label: 'Recent',
studies: recentStudies,
},
{
name: 'all',
label: 'All',
studies: studyData,
},
];
function onClickStudy(StudyInstanceUID) {
const study = studyData.find(a => a.studyInstanceUid === StudyInstanceUID);
if (study && study.displaySets && study.displaySets.length) {
return;
}
console.warn(`onClickStudy: ${StudyInstanceUID}`);
// TODO: This is weird, why can't the data source just be used as
// as function that doesn't expect a query string?
const queryParams = `?StudyInstanceUIDs=${StudyInstanceUID}`;
dataSource.retrieve.series.metadata(
queryParams,
displaySetManager.makeDisplaySets
);
}
const memoOnClickStudy = useCallback(
StudyInstanceUID => {
onClickStudy(StudyInstanceUID);
},
[studyData]
);
return (
<StudyBrowser
activeTabName={activeTabName}
tabs={tabs}
onClickStudy={memoOnClickStudy}
onSetTabActive={setActiveTabName}
/>
);
}
function WrappedPanelStudyBrowser({ commandsManager, extensionManager }) {
return (
<PanelStudyBrowser
getDataSources={extensionManager.getDataSources}
commandsManager={commandsManager}
/>
);
}
WrappedPanelStudyBrowser.propTypes = {
commandsManager: PropTypes.object.isRequired,
extensionManager: PropTypes.object.isRequired,
};
export { WrappedPanelStudyBrowser };

View File

@ -0,0 +1,5 @@
import PanelStudyBrowser, {
WrappedPanelStudyBrowser,
} from './PanelStudyBrowser';
export { PanelStudyBrowser, WrappedPanelStudyBrowser };

View File

@ -1,6 +1,5 @@
import React, { useState, useEffect, useCallback } from 'react';
import { StudyBrowser } from '@ohif/ui';
import { dicomMetadataStore, useViewModel, useToolbarLayout } from '@ohif/core';
import React from 'react';
import { WrappedPanelStudyBrowser } from './Panels'
import MeasurementTable from './MeasurementTable.js';
@ -23,281 +22,11 @@ function getImageSrc(imageId, { cornerstone }) {
});
}
function StudyBrowserUIData({ getDataSources, commandsManager }) {
const [activeTabName, setActiveTabName] = useState('primary');
const [studyData, setStudyData] = useState([]);
const [thumbnailImageSrcMap, setThumbnailImageSrcMap] = useState(new Map());
const updateThumbnailMap = (k, v) => {
setThumbnailImageSrcMap(thumbnailImageSrcMap.set(k, v));
};
console.log('StudyBrowserUIData rerender');
return (
<StudyBrowserPanel
activeTabName={activeTabName}
getDataSources={getDataSources}
commandsManager={commandsManager}
onSetTabActive={setActiveTabName}
studyData={studyData}
setStudyData={setStudyData}
thumbnailImageSrcMap={thumbnailImageSrcMap}
updateThumbnailMap={updateThumbnailMap}
/>
);
}
function StudyBrowserPanel({
activeTabName,
getDataSources,
function getPanelModule({
commandsManager,
onSetTabActive,
studyData,
setStudyData,
thumbnailImageSrcMap,
updateThumbnailMap,
extensionManager,
servicesManager,
}) {
console.warn('StudyBrowserPanel rerender');
const viewModel = useViewModel();
const dataSource = getDataSources('dicomweb')[0];
const viewportData = []; //useViewportGrid();
const seriesTracking = {}; //useSeriesTracking();
// This effect
useEffect(() => {
if (!viewModel.displaySetInstanceUIDs.length) {
return;
}
let isSubscribed = true;
const command = commandsManager.getCommand(
'getCornerstoneLibraries',
'VIEWER'
);
if (!command) {
throw new Error('Required command not found');
}
const { cornerstone, cornerstoneTools } = command.commandFn();
viewModel.displaySetInstanceUIDs.forEach(uid => {
const imageIds = dataSource.getImageIdsForDisplaySet(uid);
const imageId = imageIds[Math.floor(imageIds.length / 2)];
getImageSrc(imageId, { cornerstone }).then(imageSrc => {
if (isSubscribed) {
updateThumbnailMap(uid, imageSrc);
}
});
});
const displaySets = viewModel.displaySetInstanceUIDs.map(
displaySetManager.getDisplaySetByUID
);
const aDisplaySet = displaySets[0];
const firstStudy = dicomMetadataStore.getStudy(
aDisplaySet.StudyInstanceUID
);
const firstInstance = firstStudy.series[0].instances[0];
const PatientID = firstInstance.PatientID;
dataSource.query.studies.search({ patientId: PatientID }).then(results => {
const studies = results.map(study => {
// TODO: Why does the data source return in this format?
return {
AccessionNumber: study.accession,
StudyDate: study.date,
StudyDescription: study.description,
NumInstances: study.instances,
ModalitiesInStudy: study.modalities,
PatientID: study.mrn,
PatientName: study.patientName,
StudyInstanceUID: study.studyInstanceUid,
StudyTime: study.time,
};
});
const studiesFromInstanceData = {};
displaySets.forEach(ds => {
const displaySet = {
displaySetInstanceUID: ds.displaySetInstanceUID,
description: ds.SeriesDescription,
seriesNumber: ds.SeriesNumber,
modality: ds.Modality,
date: ds.SeriesDate,
numInstances: ds.numImageFrames,
};
const displaySetViewportData = viewportData.find(
a => a.displaySetInstanceUID === ds.displaySetInstanceUID
);
if (displaySetViewportData) {
displaySet.viewportIdentificator = displaySetViewportData.identifier;
}
const trackingInfo = seriesTracking[ds.SeriesInstanceUID];
if (trackingInfo) {
displaySet.isTracked = trackingInfo.isTracked;
} else {
displaySet.isTracked = false;
}
displaySet.componentType = 'thumbnailTracked';
if (
!Object.keys(studiesFromInstanceData).includes(ds.StudyInstanceUID)
) {
const study = dicomMetadataStore.getStudy(ds.StudyInstanceUID);
const anInstance = study.series[0].instances[0];
studiesFromInstanceData[ds.StudyInstanceUID] = {
date: anInstance.StudyDate, // TODO: Format this date to DD-MMM-YYYY
description: anInstance.StudyDescription,
displaySets: [],
numInstances: 0,
modalitiesSet: new Set(),
};
}
studiesFromInstanceData[ds.StudyInstanceUID].displaySets.push(
displaySet
);
studiesFromInstanceData[ds.StudyInstanceUID].numInstances +=
displaySet.numInstances;
studiesFromInstanceData[ds.StudyInstanceUID].modalitiesSet.add(
displaySet.modality
);
const modalitiesSet =
studiesFromInstanceData[ds.StudyInstanceUID].modalitiesSet;
studiesFromInstanceData[ds.StudyInstanceUID].modalities = Array.from(
modalitiesSet
).join(', ');
});
// QIDO for all by MRN
const allStudies = studies.map(studyLevelData => {
const studyFromInstanceData =
studiesFromInstanceData[studyLevelData.StudyInstanceUID];
if (!studyFromInstanceData) {
return {
studyInstanceUid: studyLevelData.StudyInstanceUID,
date: studyLevelData.StudyDate,
description: studyLevelData.StudyDescription,
modalities: studyLevelData.ModalitiesInStudy,
numInstances: studyLevelData.NumInstances,
displaySets: [],
};
}
return {
studyInstanceUid: studyLevelData.StudyInstanceUID,
date: studyLevelData.StudyDate || studyFromInstanceData.date,
description:
studyLevelData.StudyDescription ||
studyFromInstanceData.description,
modalities:
studyFromInstanceData.modalities ||
studyLevelData.ModalitiesInStudy,
numInstances:
studyLevelData.NumInstances || studyFromInstanceData.numInstances,
displaySets: studyFromInstanceData.displaySets,
};
});
if (isSubscribed) {
setStudyData(allStudies);
}
});
return () => (isSubscribed = false);
}, [viewModel.displaySetInstanceUIDs, thumbnailImageSrcMap, setStudyData]);
studyData.forEach(study => {
study.displaySets.forEach(ds => {
ds.imageSrc = thumbnailImageSrcMap.get(ds.displaySetInstanceUID);
});
});
const primary = studyData.find(study => {
return true; // TODO: check study.StudyInstanceUID matches queryparam?
});
// TODO: Filter allStudies to dates within one year of current date
const recentStudies = studyData.filter(study => {
return true; // TODO: check study.date
});
const tabs = [
{
name: 'primary',
label: 'Primary',
studies: primary ? [primary] : [],
},
{
name: 'recent',
label: 'Recent',
studies: recentStudies,
},
{
name: 'all',
label: 'All',
studies: studyData,
},
];
function onClickStudy(StudyInstanceUID) {
const study = studyData.find(a => a.studyInstanceUid === StudyInstanceUID);
if (study && study.displaySets && study.displaySets.length) {
return;
}
console.warn(`onClickStudy: ${StudyInstanceUID}`);
// TODO: This is weird, why can't the data source just be used as
// as function that doesn't expect a query string?
const queryParams = `?StudyInstanceUIDs=${StudyInstanceUID}`;
dataSource.retrieve.series.metadata(
queryParams,
displaySetManager.makeDisplaySets
);
}
const memoOnClickStudy = useCallback(
StudyInstanceUID => {
onClickStudy(StudyInstanceUID);
},
[studyData]
);
return (
<StudyBrowser
activeTabName={activeTabName}
tabs={tabs}
onClickStudy={memoOnClickStudy}
onSetTabActive={onSetTabActive}
/>
);
}
function getPanelModule({ getDataSources, commandsManager, servicesManager }) {
const wrappedStudyBrowserPanel = () => {
return (
<StudyBrowserUIData
getDataSources={getDataSources}
commandsManager={commandsManager}
/>
);
};
const wrappedMeasurementPanel = () => {
return (
<MeasurementTable
@ -313,7 +42,10 @@ function getPanelModule({ getDataSources, commandsManager, servicesManager }) {
iconName: 'group-layers',
iconLabel: 'Studies',
label: 'Studies',
component: wrappedStudyBrowserPanel,
component: WrappedPanelStudyBrowser.bind(null, {
commandsManager,
extensionManager,
}),
},
{
name: 'measure',
@ -326,3 +58,36 @@ function getPanelModule({ getDataSources, commandsManager, servicesManager }) {
}
export default getPanelModule;
// // TODO -> Need some way of selecting which displaySets hit the viewports.
// const { DisplaySetService } = servicesManager.services;
// // TODO -> Make a HangingProtocolService
// const HangingProtocolService = displaySets => {
// const displaySetInstanceUid = displaySets[Object.keys(displaySets)[0]][0].displaySetInstanceUid;
// return {
// numRows: 1,
// numCols: 1,
// activeViewportIndex: 0,
// viewports: [
// {
// displaySetInstanceUid,
// },
// ],
// };
// };
// const handleDisplaySetSubscription = useCallback(displaySets => {
// setViewportGrid(HangingProtocolService(displaySets));
// });
// useEffect(() => {
// const { unsubscribe } = DisplaySetService.subscribe(
// DisplaySetService.EVENTS.DISPLAY_SET_ADDED,
// handleDisplaySetSubscription
// );
// return unsubscribe;
// }, []);

View File

@ -24,11 +24,8 @@ import { ViewModelProvider } from '@ohif/core';
*/
export default function buildModeRoutes(
modes,
dataSources,
extensionManager,
servicesManager
) {
const routes = [];