Improve study browser
This commit is contained in:
parent
6044190ab1
commit
ec2df02e83
@ -80,13 +80,13 @@ function createDicomWebApi(dicomWebConfig) {
|
||||
// processResults: processResults.bind(),
|
||||
},
|
||||
instances: {
|
||||
search: (studyInstanceUid, queryParamaters) =>
|
||||
search: (studyInstanceUid, queryParameters) =>
|
||||
qidoSearch.call(
|
||||
undefined,
|
||||
qidoDicomWebClient,
|
||||
studyInstanceUid,
|
||||
null,
|
||||
queryParamaters
|
||||
queryParameters
|
||||
),
|
||||
},
|
||||
},
|
||||
|
||||
@ -105,12 +105,12 @@ function search(
|
||||
dicomWebClient,
|
||||
studyInstanceUid,
|
||||
seriesInstanceUid,
|
||||
queryParamaters
|
||||
queryParameters
|
||||
) {
|
||||
// Studies
|
||||
return dicomWebClient.searchForStudies({
|
||||
studyInstanceUid: undefined,
|
||||
queryParams: queryParamaters,
|
||||
queryParams: queryParameters,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -31,7 +31,7 @@ function ViewerLayout({
|
||||
const getPanelData = id => {
|
||||
const entry = extensionManager.getModuleEntry(id);
|
||||
// TODO, not sure why sidepanel content has to be JSX, and not a children prop?
|
||||
const content = entry.component({});
|
||||
const content = entry.component({ extensionManager });
|
||||
|
||||
return {
|
||||
iconName: entry.iconName,
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
|
||||
import ViewerLayout from './ViewerLayout';
|
||||
/*
|
||||
- Define layout for the viewer in mode configuration.
|
||||
|
||||
@ -1,55 +1,155 @@
|
||||
import React from 'react';
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import { StudyBrowser } from '@ohif/ui';
|
||||
|
||||
import { useViewModel, displaySetManager } from '@ohif/core';
|
||||
import {
|
||||
dicomMetadataStore,
|
||||
useViewModel,
|
||||
displaySetManager,
|
||||
} from '@ohif/core';
|
||||
|
||||
function StudyBrowserPanel({ extensionManager }) {
|
||||
// TODO: need to check how this is intended to be passed in
|
||||
const dataSource = extensionManager.dataSourceMap.dicomweb[0];
|
||||
|
||||
const viewModel = useViewModel();
|
||||
|
||||
// TODO
|
||||
const viewportData = []; //useViewportGrid();
|
||||
const seriesTracking = {}; //useSeriesTracking();
|
||||
|
||||
const [studyData, setStudyData] = useState([]);
|
||||
|
||||
console.log(viewModel);
|
||||
|
||||
const displaySets = viewModel.displaySetInstanceUids.map(
|
||||
displaySetManager.getDisplaySetByUID
|
||||
);
|
||||
|
||||
function displaySetsToStudyPanelInfo(
|
||||
displaySets,
|
||||
viewportData = [],
|
||||
seriesTracking = {}
|
||||
) {
|
||||
console.log(displaySets);
|
||||
|
||||
const studies = {};
|
||||
displaySets.forEach(displaySet => {
|
||||
if (!displaySets.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// - Put this in something so it only runs once
|
||||
// - Have update the query update the dicom data store at the study level and then have this component use the data in the view model
|
||||
useEffect(() => {
|
||||
const dSets = viewModel.displaySetInstanceUids.map(
|
||||
displaySetManager.getDisplaySetByUID
|
||||
);
|
||||
const aDisplaySet = dSets[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,
|
||||
};
|
||||
});
|
||||
|
||||
setStudyData(studies);
|
||||
});
|
||||
}, [viewModel.displaySetInstanceUids]);
|
||||
|
||||
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 === displaySet.displaySetInstanceUid
|
||||
a => a.displaySetInstanceUid === ds.displaySetInstanceUid
|
||||
);
|
||||
|
||||
if (displaySetViewportData) {
|
||||
displaySet.viewportIdentificator = displaySetViewportData.identifier;
|
||||
}
|
||||
|
||||
const trackingInfo = seriesTracking[displaySet.SeriesInstanceUID];
|
||||
const trackingInfo = seriesTracking[ds.SeriesInstanceUID];
|
||||
if (trackingInfo) {
|
||||
displaySet.viewportIdentificator = trackingInfo.isTracked;
|
||||
displaySet.isTracked = trackingInfo.isTracked;
|
||||
|
||||
displaySet.componentType = trackingInfo.isTracked
|
||||
? 'thumbnailTracked'
|
||||
: 'thumbnail';
|
||||
} else {
|
||||
displaySet.isTracked = false;
|
||||
displaySet.componentType = 'thumbnail';
|
||||
}
|
||||
|
||||
if (!Object.keys(studies).includes(displaySet.StudyInstanceUID)) {
|
||||
studies[displaySet.StudyInstanceUID] = {
|
||||
date: displaySet.SeriesDate,
|
||||
description: displaySet.SeriesDescription,
|
||||
seriesNumber: displaySet.SeriesNumber,
|
||||
modalities: displaySet.modalities,
|
||||
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(),
|
||||
};
|
||||
}
|
||||
|
||||
studies[displaySet.StudyInstanceUID].displaySets.push(displaySet);
|
||||
studies[displaySet.StudyInstanceUID].numInstances +=
|
||||
displaySet.numImageFrames;
|
||||
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(', ');
|
||||
});
|
||||
|
||||
const allStudies = Object.keys(studies).map(StudyInstanceUID => {
|
||||
const study = studies[StudyInstanceUID];
|
||||
// QIDO for all by MRN
|
||||
const allStudies = studyData.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: StudyInstanceUID,
|
||||
...study,
|
||||
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,
|
||||
};
|
||||
});
|
||||
|
||||
@ -57,36 +157,16 @@ function displaySetsToStudyPanelInfo(
|
||||
return true; // TODO: check study.StudyInstanceUID matches queryparam?
|
||||
});
|
||||
|
||||
console.log(primary);
|
||||
|
||||
// TODO: Filter allStudies to dates within one year of current date
|
||||
const recentStudies = allStudies.filter(study => {
|
||||
return true; // TODO: check study.date
|
||||
});
|
||||
|
||||
/*const primary = {
|
||||
studyInstanceUid: '1',
|
||||
date: '07-Sept-2010',
|
||||
description: 'CHEST/ABD/PELVIS W/CONTRAST',
|
||||
numInstances: 902,
|
||||
modalities: 'CT,SR',
|
||||
displaySets: [
|
||||
{
|
||||
displaySetInstanceUid: 'f69f6asdasd48c-223e-db7f-c4af-b8906641a66e',
|
||||
description: 'Multiple line image series description lorem sit',
|
||||
seriesNumber: 1,
|
||||
numInstances: 68,
|
||||
componentType: 'thumbnailTracked',
|
||||
viewportIdentificator: 'A',
|
||||
isTracked: true,
|
||||
},
|
||||
],
|
||||
};*/
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
name: 'primary',
|
||||
label: 'Primary',
|
||||
studies: [primary],
|
||||
studies: primary ? [primary] : [],
|
||||
},
|
||||
{
|
||||
name: 'recent',
|
||||
@ -100,51 +180,27 @@ function displaySetsToStudyPanelInfo(
|
||||
},
|
||||
];
|
||||
|
||||
return tabs;
|
||||
}
|
||||
function onClickStudy(StudyInstanceUID) {
|
||||
if (studiesFromInstanceData[StudyInstanceUID]) {
|
||||
return;
|
||||
}
|
||||
|
||||
function StudyBrowserPanel({}) {
|
||||
const viewModel = useViewModel();
|
||||
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}`;
|
||||
|
||||
// TODO
|
||||
const viewportData = []; //useViewportGrid();
|
||||
const seriesTracking = {}; //useSeriesTracking();
|
||||
|
||||
console.log(viewModel);
|
||||
|
||||
const displaySets = viewModel.displaySetInstanceUids.map(
|
||||
displaySetManager.getDisplaySetByUID
|
||||
);
|
||||
|
||||
let tabs;
|
||||
|
||||
if (displaySets.length) {
|
||||
tabs = displaySetsToStudyPanelInfo(
|
||||
displaySets,
|
||||
viewportData,
|
||||
seriesTracking
|
||||
dataSource.retrieve.series.metadata(
|
||||
queryParams,
|
||||
displaySetManager.makeDisplaySets
|
||||
);
|
||||
} else {
|
||||
tabs = [
|
||||
{
|
||||
name: 'primary',
|
||||
label: 'Primary',
|
||||
studies: [],
|
||||
},
|
||||
{
|
||||
name: 'recent',
|
||||
label: 'Recent',
|
||||
studies: [],
|
||||
},
|
||||
{
|
||||
name: 'all',
|
||||
label: 'All',
|
||||
studies: [],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return <StudyBrowser tabs={tabs} />;
|
||||
const memoOnClickStudy = useCallback(StudyInstanceUID => {
|
||||
onClickStudy(StudyInstanceUID);
|
||||
});
|
||||
|
||||
return <StudyBrowser tabs={tabs} onClickStudy={memoOnClickStudy} />;
|
||||
}
|
||||
|
||||
function getPanelModule() {
|
||||
|
||||
@ -13,13 +13,6 @@ const makeDisplaySet = instances => {
|
||||
const instance = instances[0];
|
||||
const imageSet = new ImageSet(instances);
|
||||
|
||||
const modalities = new Set();
|
||||
instances.forEach(i => {
|
||||
modalities.add(i.Modality);
|
||||
});
|
||||
|
||||
const modalitiesInDisplaySet = Array.from(modalities).join(', ');
|
||||
|
||||
// set appropriate attributes to image set...
|
||||
imageSet.setAttributes({
|
||||
displaySetInstanceUid: imageSet.uid, // create a local alias for the imageSet UID
|
||||
@ -30,7 +23,7 @@ const makeDisplaySet = instances => {
|
||||
SeriesNumber: instance.SeriesNumber,
|
||||
FrameRate: instance.FrameTime,
|
||||
SeriesDescription: instance.SeriesDescription,
|
||||
modalities: modalitiesInDisplaySet,
|
||||
Modality: instance.Modality,
|
||||
isMultiFrame: isMultiFrame(instance),
|
||||
numImageFrames: instances.length,
|
||||
SOPClassHandlerId: `${id}.sopClassHandlerModule.${sopClassHandlerName}`,
|
||||
|
||||
@ -47,7 +47,9 @@ const _model = {
|
||||
};
|
||||
|
||||
function _getStudy(StudyInstanceUID) {
|
||||
return _model.studies.find(StudyInstanceUID);
|
||||
return _model.studies.find(
|
||||
aStudy => aStudy.StudyInstanceUID === StudyInstanceUID
|
||||
);
|
||||
}
|
||||
|
||||
function _getSeries(StudyInstanceUID, SeriesInstanceUID) {
|
||||
@ -90,6 +92,27 @@ const dicomMetadataStore = {
|
||||
|
||||
study.addSeries(instances);
|
||||
},
|
||||
addStudy(study) {
|
||||
const { StudyInstanceUID } = study;
|
||||
|
||||
let existingStudy = _model.studies.find(
|
||||
study => study.StudyInstanceUID === StudyInstanceUID
|
||||
);
|
||||
|
||||
if (!existingStudy) {
|
||||
const newStudy = new StudyMetadata(StudyInstanceUID);
|
||||
|
||||
newStudy.PatientID = study.PatientID;
|
||||
newStudy.PatientName = study.PatientName;
|
||||
newStudy.StudyDate = study.StudyDate;
|
||||
newStudy.ModalitiesInStudy = study.ModalitiesInStudy;
|
||||
newStudy.StudyDescription = study.StudyDescription;
|
||||
newStudy.AccessionNumber = study.AccessionNumber;
|
||||
newStudy.NumInstances = study.NumInstances; // todo: Correct naming?
|
||||
|
||||
_model.studies.push(newStudy);
|
||||
}
|
||||
},
|
||||
getStudy: _getStudy,
|
||||
getSeries: _getSeries,
|
||||
getInstance: _getInstance,
|
||||
|
||||
@ -22,7 +22,7 @@ const getTrackedSeries = displaySets => {
|
||||
return trackedSeries;
|
||||
};
|
||||
|
||||
const StudyBrowser = ({ tabs }) => {
|
||||
const StudyBrowser = ({ tabs, onClickStudy, onClickThumbnail }) => {
|
||||
const [tabActive, setTabActive] = useState(getInitialActiveTab(tabs));
|
||||
const [studyActive, setStudyActive] = useState(null);
|
||||
|
||||
@ -54,10 +54,27 @@ const StudyBrowser = ({ tabs }) => {
|
||||
isActive={isActive}
|
||||
onClick={() => {
|
||||
setStudyActive(isActive ? null : studyInstanceUid);
|
||||
|
||||
if (onClickStudy) {
|
||||
onClickStudy(studyInstanceUid);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{isActive && thumbnails && (
|
||||
<ThumbnailList thumbnails={thumbnails} />
|
||||
{isActive && displaySets && (
|
||||
<ThumbnailList
|
||||
thumbnails={displaySets}
|
||||
thumbnailActive={thumbnailActive}
|
||||
onThumbnailClick={thumbnailId => {
|
||||
setThumbnailActive(
|
||||
thumbnailId === thumbnailActive ? null : thumbnailId
|
||||
);
|
||||
|
||||
if (onClickThumbnail) {
|
||||
// TODO: what is thumbnailId? Should pass display set instead
|
||||
onClickThumbnail(thumbnailId);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
@ -103,6 +120,8 @@ const StudyBrowser = ({ tabs }) => {
|
||||
};
|
||||
|
||||
StudyBrowser.propTypes = {
|
||||
onClickStudy: PropTypes.func,
|
||||
onClickThumbnail: PropTypes.func,
|
||||
tabs: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
name: PropTypes.string.isRequired,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user