From b65a91129e5c4a0283833cccfb71e3959acaadfb Mon Sep 17 00:00:00 2001 From: Erik Ziegler Date: Tue, 19 May 2020 10:59:50 +0200 Subject: [PATCH] Still broken, but app runs --- .../src/OHIFCornerstoneViewport.js | 1 - .../default/src/Panels/PanelStudyBrowser.jsx | 244 ++++++++++-------- .../DisplaySetService/DisplaySetService.js | 11 +- .../MeasurementTable/MeasurementTable.jsx | 1 - .../viewer/src/components/ViewportGrid.jsx | 4 +- 5 files changed, 149 insertions(+), 112 deletions(-) diff --git a/extensions/cornerstone/src/OHIFCornerstoneViewport.js b/extensions/cornerstone/src/OHIFCornerstoneViewport.js index a71b7f0d7..8e567e89c 100644 --- a/extensions/cornerstone/src/OHIFCornerstoneViewport.js +++ b/extensions/cornerstone/src/OHIFCornerstoneViewport.js @@ -78,7 +78,6 @@ class OHIFCornerstoneViewport extends Component { } getViewportData = async displaySet => { - debugger; let viewportData; const { dataSource } = this.props; diff --git a/extensions/default/src/Panels/PanelStudyBrowser.jsx b/extensions/default/src/Panels/PanelStudyBrowser.jsx index ad639780b..33236a1d7 100644 --- a/extensions/default/src/Panels/PanelStudyBrowser.jsx +++ b/extensions/default/src/Panels/PanelStudyBrowser.jsx @@ -7,63 +7,106 @@ import { dicomMetadataStore } from '@ohif/core'; function PanelStudyBrowser({ servicesManager, getImageSrc, getStudiesByPatientId, dataSource }) { console.warn('StudyBrowserPanel rerender'); const [activeTabName, setActiveTabName] = useState('primary'); - const [studyData, setStudyData] = useState([]); + + const { DisplaySetService } = servicesManager.services; + + const _createInitialState = (displaySets) => { + // If no study data exists yet (i.e. this is initial load): + // - Use the current DisplaySetService state to create the initial StudyData + // - Launch a QIDO request for related studies + const currentDisplaySets = DisplaySetService.activeDisplaySets; + if (!currentDisplaySets || !currentDisplaySets.length) { + return; + } + + const mappedStudiesFromInstances = _getMappedStudiesFromDisplaySets( + currentDisplaySets + ); + + setStudyData(mappedStudiesFromInstances); + + const displaySet = displaySets[0]; + const study = dicomMetadataStore.getStudy( + displaySet.StudyInstanceUID + ); + const instance = study.series[0].instances[0]; + const PatientID = instance.PatientID; + getStudiesByPatientId(PatientID).then(studiesForPatient => { + const mappedStudiesFromDataSource = _mapDataSourceStudies( + studiesForPatient + ); + const merged = _mergeStudyDataAndDataSourceStudies( + studyData, + mappedStudiesFromDataSource, + ); + + setStudyData(merged); + }); + } + + // In case display sets were added before the component was created, + // initialize the state from the current state of the DisplaySetService + const currentDisplaySets = DisplaySetService.activeDisplaySets; + const mappedStudiesFromInstances = _getMappedStudiesFromDisplaySets( + currentDisplaySets + ); + const [studyData, setStudyData] = useState(mappedStudiesFromInstances); + + if (!studyData.length) { + _createInitialState(currentDisplaySets); + } + const [thumbnailImageSrcMap, setThumbnailImageSrcMap] = useState(new Map()); const updateThumbnailMap = (k, v) => { setThumbnailImageSrcMap(thumbnailImageSrcMap.set(k, v)); }; - const { DisplaySetService } = servicesManager.services; - const handleDisplaySetSubscription = useCallback(displaySets => { - if (!displaySets) { - return; - } - - if (getImageSrc) { - Object.keys(displaySets).forEach(StudyInstanceUid => { - const dSets = displaySets[StudyInstanceUid]; - dSets.forEach(async (dset) => { - const uid = dset.displaySetInstanceUID; - const imageIds = dataSource.getImageIdsForDisplaySet(dset); - const imageId = imageIds[Math.floor(imageIds.length / 2)]; - const imageSrc = await getImageSrc(imageId); - - updateThumbnailMap(uid, imageSrc); - }); - }) - } - - // TODO: This isn't really the 'first' - const firstStudyInstanceUID = Object.keys(displaySets)[0]; - const firstStudy = dicomMetadataStore.getStudy( - firstStudyInstanceUID - ); - const firstInstance = firstStudy.series[0].instances[0]; - const PatientID = firstInstance.PatientID; + const handleDisplaySetsAdded = displaySets => { + // On initial load + // 1. DisplaySets are added for the primary studies, which are used to create + // the initial 'StudyData' to be displayed in the browser + // 2. Requests for thumbnails are made for each display set, which + // resolve asynchronously and update thumbnailImageSrcMap + // 3. QIDO-RS call is made to retrieve StudyData for related studies, which + // resolves later and is merged into StudyData // - async function getData() { - const studiesForPatient = await getStudiesByPatientId(PatientID); - const mappedStudiesFromDataSource = _mapDataSourceStudies( - studiesForPatient - ); + // Later, if another study is loaded + // 1. Display sets are added again, triggering requests for thumbnails + // 2. StudyData is updated from any new information provided by the series-level requests + + // First, launch requests for a thumbnail for the new display sets + displaySets.forEach(dset => { + const uid = dset.displaySetInstanceUID; + const imageIds = dataSource.getImageIdsForDisplaySet(dset); + const imageId = imageIds[Math.floor(imageIds.length / 2)]; + + // When the image arrives, render it and store the result in the thumbnailImgSrcMap + getImageSrc(imageId).then(imageSrc => updateThumbnailMap(uid, imageSrc)); + }); + + // Does any studyData already exist? + if (studyData.length) { + // If it does, map the new display set data and merge it into the local state + // for display const mappedStudiesFromInstances = _getMappedStudiesFromDisplaySets( displaySets ); - const ourFinalFormIGuess = _mergeDataSourceAndInstanceStudies( - mappedStudiesFromDataSource, - mappedStudiesFromInstances + + const merged = _mergeStudyDataAndInstanceStudies( + studyData, + mappedStudiesFromInstances, ); - setStudyData(ourFinalFormIGuess); + setStudyData(merged); + } else { + _createInitialState(displaySets) } - - getData(); - }); + }; useEffect(() => { const { unsubscribe } = DisplaySetService.subscribe( - DisplaySetService.EVENTS.DISPLAY_SET_ADDED, - handleDisplaySetSubscription + DisplaySetService.EVENTS.DISPLAY_SETS_ADDED, + handleDisplaySetsAdded ); return unsubscribe; @@ -175,71 +218,70 @@ function _mapDataSourceStudies(studies) { * Iterates over displaysets and creates mapped studies from * instance metadata. * - * @param {*} displaySetsPerStudy + * @param {*} displaySets */ -function _getMappedStudiesFromDisplaySets(displaySetsPerStudy) { +function _getMappedStudiesFromDisplaySets(displaySets) { + if (!displaySets) { + return []; + } + const studiesFromInstanceData = {}; - Object.keys(displaySetsPerStudy).forEach(StudyInstanceUID => { - const displaySets = displaySetsPerStudy[StudyInstanceUID]; - - displaySets.forEach(ds => { - const displaySet = { - displaySetInstanceUID: ds.displaySetInstanceUID, - description: ds.SeriesDescription, - seriesNumber: ds.SeriesNumber, - modality: ds.Modality, - date: ds.SeriesDate, - numInstances: ds.numImageFrames, - componentType: 'thumbnailTracked' // TODO: PUT THIS SOMEWHERE ELSE - }; - - studiesFromInstanceData[ds.StudyInstanceUID] = - studiesFromInstanceData[ds.StudyInstanceUID] || - _mapStudyFromInstance(ds.StudyInstanceUID); - - const mappedStudy = studiesFromInstanceData[ds.StudyInstanceUID]; - - mappedStudy.displaySets.push(displaySet); - mappedStudy.numInstances += displaySet.numInstances; - mappedStudy.modalitiesSet.add(displaySet.modality); - - const modalitiesSet = mappedStudy.modalitiesSet; - mappedStudy.modalities = Array.from(modalitiesSet).join(', '); - }); - }) - - return studiesFromInstanceData; -} - -function _mergeDataSourceAndInstanceStudies(mappedQidoStudies, mappedInstanceStudies) { - const allStudies = mappedQidoStudies.map(qidoStudy => { - const studyFromInstances = - mappedInstanceStudies[qidoStudy.StudyInstanceUID]; - - if (!studyFromInstances) { - return { - studyInstanceUid: qidoStudy.StudyInstanceUID, - date: qidoStudy.StudyDate, - description: qidoStudy.StudyDescription, - modalities: qidoStudy.ModalitiesInStudy, - numInstances: qidoStudy.NumInstances, - displaySets: [], - }; - } - - return { - studyInstanceUid: qidoStudy.StudyInstanceUID, - date: qidoStudy.StudyDate || studyFromInstances.date, - description: qidoStudy.StudyDescription || studyFromInstances.description, - modalities: studyFromInstances.modalities || qidoStudy.ModalitiesInStudy, - numInstances: qidoStudy.NumInstances || studyFromInstances.numInstances, - seriesNumber: studyFromInstances.seriesNumber, - displaySets: studyFromInstances.displaySets, + displaySets.forEach(ds => { + const displaySet = { + displaySetInstanceUID: ds.displaySetInstanceUID, + description: ds.SeriesDescription, + seriesNumber: ds.SeriesNumber, + modality: ds.Modality, + date: ds.SeriesDate, + numInstances: ds.numImageFrames, + componentType: 'thumbnailTracked' // TODO: PUT THIS SOMEWHERE ELSE }; + + studiesFromInstanceData[ds.StudyInstanceUID] = + studiesFromInstanceData[ds.StudyInstanceUID] || + _mapStudyFromInstance(ds.StudyInstanceUID); + + const mappedStudy = studiesFromInstanceData[ds.StudyInstanceUID]; + + mappedStudy.displaySets.push(displaySet); + mappedStudy.numInstances += displaySet.numInstances; + mappedStudy.modalitiesSet.add(displaySet.modality); + + const modalitiesSet = mappedStudy.modalitiesSet; + mappedStudy.modalities = Array.from(modalitiesSet).join(', '); }); - return allStudies; + return Object.values(studiesFromInstanceData); +} + +function _mergeStudyDataAndDataSourceStudies(studyData, mappedQidoStudies) { + return mappedQidoStudies.map(qidoStudy => { + const { StudyInstanceUID } = qidoStudy; + const existing = studyData.find(a => a.studyInstanceUid === StudyInstanceUID) || {}; + + return Object.assign({ + displaySets: [] + }, existing, { + studyInstanceUid: StudyInstanceUID, + date: qidoStudy.StudyDate, + description: qidoStudy.StudyDescription, + modalities: qidoStudy.ModalitiesInStudy, + numInstances: qidoStudy.NumInstances, + }); + }); +} + +function _mergeStudyDataAndInstanceStudies(studyData, mappedInstanceStudies) { + return studyData.map(study => { + const studyFromInstances = + mappedInstanceStudies.find(a => a.StudyInstanceUID === study.studyInstanceUid); + + return Object.assign({}, study, { + displaySets: studyFromInstances.displaySets, + modalities: studyFromInstances.modalities + }) + }); } function _mapStudyFromInstance(StudyInstanceUID) { diff --git a/platform/core/src/services/DisplaySetService/DisplaySetService.js b/platform/core/src/services/DisplaySetService/DisplaySetService.js index 231c16c67..1708d4485 100644 --- a/platform/core/src/services/DisplaySetService/DisplaySetService.js +++ b/platform/core/src/services/DisplaySetService/DisplaySetService.js @@ -1,7 +1,7 @@ import pubSubServiceInterface from '../pubSubServiceInterface'; const EVENTS = { - DISPLAY_SETS_ADDED: 'event::displaySetService:displaySetAdded', + DISPLAY_SETS_ADDED: 'event::displaySetService:displaySetsAdded', }; const displaySetCache = []; @@ -81,11 +81,8 @@ export default class DisplaySetService { } // If array of instances => One instance. - let displaySetsAdded = []; - debugger; - if (batch) { input.forEach(instances => { const displaySets = this.makeDisplaySetForInstances(instances); @@ -99,12 +96,12 @@ export default class DisplaySetService { } // If array of array of instances - - this._broadcastChange(EVENTS.DISPLAY_SETS_ADDED, displaySetsAdded); + if (displaySetsAdded && displaySetsAdded.length) { + this._broadcastChange(EVENTS.DISPLAY_SETS_ADDED, displaySetsAdded); + } }; makeDisplaySetForInstances(instances) { - debugger; const instance = instances[0]; const existingDisplaySets = diff --git a/platform/ui/src/components/MeasurementTable/MeasurementTable.jsx b/platform/ui/src/components/MeasurementTable/MeasurementTable.jsx index 8afcd8291..95ba02982 100644 --- a/platform/ui/src/components/MeasurementTable/MeasurementTable.jsx +++ b/platform/ui/src/components/MeasurementTable/MeasurementTable.jsx @@ -61,7 +61,6 @@ const MeasurementTable = ({ data, title, amount, onClick, onEdit }) => { right: 4, transform: isActive ? '' : 'translateX(100%)', }} - e onClick={(e) => { // stopPropagation needed to avoid disable the current active item e.stopPropagation(); diff --git a/platform/viewer/src/components/ViewportGrid.jsx b/platform/viewer/src/components/ViewportGrid.jsx index ecd8e14a2..912d7cd84 100644 --- a/platform/viewer/src/components/ViewportGrid.jsx +++ b/platform/viewer/src/components/ViewportGrid.jsx @@ -20,7 +20,7 @@ function ViewerViewportGrid(props) { // TODO -> Make a HangingProtocolService const HangingProtocolService = displaySets => { - const displaySetInstanceUID = displaySets[Object.keys(displaySets)[0]][0].displaySetInstanceUID; + const displaySetInstanceUID = displaySets[0].displaySetInstanceUID; return { numRows: 1, @@ -40,7 +40,7 @@ function ViewerViewportGrid(props) { useEffect(() => { const { unsubscribe } = DisplaySetService.subscribe( - DisplaySetService.EVENTS.DISPLAY_SET_ADDED, + DisplaySetService.EVENTS.DISPLAY_SETS_ADDED, handleDisplaySetSubscription );