feat(panel and auth): fix study panel to not rely on priors and add token param to url (#3381)
* fix: multiframe * fix the unnecessary use of prior since some servers dont like it * add authentication via token in url * add data citation * add resources * upgrade packages * yarn lock * apply review comments
This commit is contained in:
1 parent
cba57a1b65
commit
84cb709b44
24 files changed
+387
-211
No files matched your search
@@ -46,7 +46,7 @@
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"classnames": "^2.3.2",
|
||||
"@cornerstonejs/adapters": "^0.6.0",
|
||||
"@cornerstonejs/core": "^0.44.2",
|
||||
"@cornerstonejs/tools": "^0.66.2"
|
||||
"@cornerstonejs/core": "^0.46.2",
|
||||
"@cornerstonejs/tools": "^0.67.2"
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "^3.0.0",
|
||||
"@ohif/ui": "^2.0.0",
|
||||
"@cornerstonejs/dicom-image-loader": "^0.4.0",
|
||||
"@cornerstonejs/dicom-image-loader": "^0.6.4",
|
||||
"@cornerstonejs/codec-charls": "^1.2.3",
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
|
||||
"@cornerstonejs/codec-openjpeg": "^1.2.2",
|
||||
@@ -53,9 +53,9 @@
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/adapters": "^0.6.0",
|
||||
"@cornerstonejs/core": "^0.44.2",
|
||||
"@cornerstonejs/streaming-image-volume-loader": "^0.19.2",
|
||||
"@cornerstonejs/tools": "^0.66.2",
|
||||
"@cornerstonejs/core": "^0.46.2",
|
||||
"@cornerstonejs/streaming-image-volume-loader": "^0.20.2",
|
||||
"@cornerstonejs/tools": "^0.67.2",
|
||||
"@kitware/vtk.js": "27.3.1",
|
||||
"html2canvas": "^1.4.1",
|
||||
"lodash.debounce": "4.0.8",
|
||||
|
||||
@@ -23,6 +23,7 @@ import * as CornerstoneExtensionTypes from './types';
|
||||
import { toolNames } from './initCornerstoneTools';
|
||||
import { getEnabledElement, reset as enabledElementReset } from './state';
|
||||
import dicomLoaderService from './utils/dicomLoaderService';
|
||||
import getActiveViewportEnabledElement from './utils/getActiveViewportEnabledElement';
|
||||
import { registerColormap } from './utils/colormap/transferFunctionHelpers';
|
||||
|
||||
import { id } from './id';
|
||||
@@ -141,5 +142,5 @@ const cornerstoneExtension: Types.Extensions.Extension = {
|
||||
};
|
||||
|
||||
export type { PublicViewportOptions };
|
||||
export { measurementMappingUtils, CornerstoneExtensionTypes, toolNames };
|
||||
export { measurementMappingUtils, CornerstoneExtensionTypes, toolNames , getActiveViewportEnabledElement};
|
||||
export default cornerstoneExtension;
|
||||
@@ -200,6 +200,7 @@ function createDicomLocalApi(dicomLocalConfig) {
|
||||
displaySet.images.forEach(instance => {
|
||||
const NumberOfFrames = instance.NumberOfFrames;
|
||||
if (NumberOfFrames > 1) {
|
||||
// in multiframe we start at frame 1
|
||||
for (let i = 1; i <= NumberOfFrames; i++) {
|
||||
const imageId = this.getImageIdsForInstance({
|
||||
instance,
|
||||
|
||||
@@ -12,7 +12,7 @@ const { sortStudyInstances, formatDate } = utils;
|
||||
function PanelStudyBrowser({
|
||||
servicesManager,
|
||||
getImageSrc,
|
||||
getStudiesForPatientByStudyInstanceUID,
|
||||
getStudiesForPatientByMRN,
|
||||
requestDisplaySetCreationForStudy,
|
||||
dataSource,
|
||||
}) {
|
||||
@@ -64,12 +64,24 @@ function PanelStudyBrowser({
|
||||
useEffect(() => {
|
||||
// Fetch all studies for the patient in each primary study
|
||||
async function fetchStudiesForPatient(StudyInstanceUID) {
|
||||
const qidoStudiesForPatient =
|
||||
(await getStudiesForPatientByStudyInstanceUID(StudyInstanceUID)) || [];
|
||||
// current study qido
|
||||
const qidoForStudyUID = await dataSource.query.studies.search({
|
||||
studyInstanceUid: StudyInstanceUID,
|
||||
});
|
||||
|
||||
let qidoStudiesForPatient = qidoForStudyUID;
|
||||
|
||||
// try to fetch the prior studies based on the patientID if the
|
||||
// server can respond.
|
||||
try {
|
||||
qidoStudiesForPatient = await getStudiesForPatientByMRN(
|
||||
qidoForStudyUID
|
||||
);
|
||||
} catch (error) {
|
||||
console.warn(error);
|
||||
}
|
||||
|
||||
// TODO: This should be "naturalized DICOM JSON" studies
|
||||
const mappedStudies = _mapDataSourceStudies(qidoStudiesForPatient);
|
||||
|
||||
const actuallyMappedStudies = mappedStudies.map(qidoStudy => {
|
||||
return {
|
||||
studyInstanceUid: qidoStudy.StudyInstanceUID,
|
||||
@@ -77,29 +89,27 @@ function PanelStudyBrowser({
|
||||
description: qidoStudy.StudyDescription,
|
||||
modalities: qidoStudy.ModalitiesInStudy,
|
||||
numInstances: qidoStudy.NumInstances,
|
||||
// displaySets: []
|
||||
};
|
||||
});
|
||||
if (isMounted.current) {
|
||||
setStudyDisplayList(prevArray => {
|
||||
const ret = [...prevArray];
|
||||
for (const study of actuallyMappedStudies) {
|
||||
if (
|
||||
!prevArray.find(
|
||||
it => it.studyInstanceUid === study.studyInstanceUid
|
||||
)
|
||||
) {
|
||||
ret.push(study);
|
||||
}
|
||||
|
||||
setStudyDisplayList(prevArray => {
|
||||
const ret = [...prevArray];
|
||||
for (const study of actuallyMappedStudies) {
|
||||
if (
|
||||
!prevArray.find(
|
||||
it => it.studyInstanceUid === study.studyInstanceUid
|
||||
)
|
||||
) {
|
||||
ret.push(study);
|
||||
}
|
||||
return ret;
|
||||
});
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
});
|
||||
}
|
||||
|
||||
StudyInstanceUIDs.forEach(sid => fetchStudiesForPatient(sid));
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [StudyInstanceUIDs, getStudiesForPatientByStudyInstanceUID]);
|
||||
}, [StudyInstanceUIDs, getStudiesForPatientByMRN]);
|
||||
|
||||
// // ~~ Initial Thumbnails
|
||||
useEffect(() => {
|
||||
@@ -254,7 +264,7 @@ PanelStudyBrowser.propTypes = {
|
||||
getImageIdsForDisplaySet: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
getImageSrc: PropTypes.func.isRequired,
|
||||
getStudiesForPatientByStudyInstanceUID: PropTypes.func.isRequired,
|
||||
getStudiesForPatientByMRN: PropTypes.func.isRequired,
|
||||
requestDisplaySetCreationForStudy: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
|
||||
//
|
||||
import PanelStudyBrowser from './PanelStudyBrowser';
|
||||
import getImageSrcFromImageId from './getImageSrcFromImageId';
|
||||
import getStudiesForPatientByStudyInstanceUID from './getStudiesForPatientByStudyInstanceUID';
|
||||
import getStudiesForPatientByMRN from './getStudiesForPatientByMRN';
|
||||
import requestDisplaySetCreationForStudy from './requestDisplaySetCreationForStudy';
|
||||
|
||||
/**
|
||||
@@ -21,7 +21,7 @@ function WrappedPanelStudyBrowser({
|
||||
// TODO: This should be made available a different way; route should have
|
||||
// already determined our datasource
|
||||
const dataSource = extensionManager.getDataSources()[0];
|
||||
const _getStudiesForPatientByStudyInstanceUID = getStudiesForPatientByStudyInstanceUID.bind(
|
||||
const _getStudiesForPatientByMRN = getStudiesForPatientByMRN.bind(
|
||||
null,
|
||||
dataSource
|
||||
);
|
||||
@@ -38,9 +38,7 @@ function WrappedPanelStudyBrowser({
|
||||
servicesManager={servicesManager}
|
||||
dataSource={dataSource}
|
||||
getImageSrc={_getImageSrcFromImageId}
|
||||
getStudiesForPatientByStudyInstanceUID={
|
||||
_getStudiesForPatientByStudyInstanceUID
|
||||
}
|
||||
getStudiesForPatientByMRN={_getStudiesForPatientByMRN}
|
||||
requestDisplaySetCreationForStudy={_requestDisplaySetCreationForStudy}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -6,7 +6,7 @@ function getImageSrcFromImageId(cornerstone, imageId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const canvas = document.createElement('canvas');
|
||||
cornerstone.utilities
|
||||
.loadImageToCanvas(canvas, imageId)
|
||||
.loadImageToCanvas({ canvas, imageId })
|
||||
.then(imageId => {
|
||||
resolve(canvas.toDataURL());
|
||||
})
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
async function getStudiesForPatientByMRN(dataSource, qidoForStudyUID) {
|
||||
if (qidoForStudyUID && qidoForStudyUID.length && qidoForStudyUID[0].mrn) {
|
||||
return dataSource.query.studies.search({
|
||||
patientId: qidoForStudyUID[0].mrn,
|
||||
});
|
||||
}
|
||||
console.log('No mrn found for', qidoForStudyUID);
|
||||
return qidoForStudyUID;
|
||||
}
|
||||
|
||||
export default getStudiesForPatientByMRN;
|
||||
@@ -1,29 +0,0 @@
|
||||
async function getStudiesForPatientByStudyInstanceUID(
|
||||
dataSource,
|
||||
StudyInstanceUID
|
||||
) {
|
||||
if (StudyInstanceUID === undefined) {
|
||||
return;
|
||||
}
|
||||
// TODO: The `DicomMetadataStore` should short-circuit both of these requests
|
||||
// Data _could_ be here from route query, or if using JSON data source
|
||||
// We could also force this to "await" these values being available in the DICOMStore?
|
||||
// Kind of like promise fulfillment in @cornerstonejs/dicom-image-loader when there are multiple
|
||||
// outgoing requests for the same data
|
||||
const getStudyResult = await dataSource.query.studies.search({
|
||||
studyInstanceUid: StudyInstanceUID,
|
||||
});
|
||||
|
||||
// TODO: To Erik's point, the data source likely shouldn't deviate from
|
||||
// Naturalized DICOM JSON when returning. It makes things like this awkward (mrn)
|
||||
if (getStudyResult && getStudyResult.length && getStudyResult[0].mrn) {
|
||||
return dataSource.query.studies.search({
|
||||
patientId: getStudyResult[0].mrn,
|
||||
});
|
||||
}
|
||||
console.log('No mrn found for', getStudyResult);
|
||||
// The original study we KNOW belongs to the same set, so just return it
|
||||
return getStudyResult;
|
||||
}
|
||||
|
||||
export default getStudiesForPatientByStudyInstanceUID;
|
||||
@@ -7,7 +7,7 @@ import getSopClassHandlerModule from './getSopClassHandlerModule.js';
|
||||
import getToolbarModule from './getToolbarModule';
|
||||
import getCommandsModule from './commandsModule';
|
||||
import getHangingProtocolModule from './getHangingProtocolModule';
|
||||
import getStudiesForPatientByStudyInstanceUID from './Panels/getStudiesForPatientByStudyInstanceUID';
|
||||
import getStudiesForPatientByMRN from './Panels/getStudiesForPatientByMRN';
|
||||
import getCustomizationModule from './getCustomizationModule';
|
||||
import { id } from './id.js';
|
||||
import preRegistration from './init';
|
||||
@@ -34,7 +34,7 @@ const defaultExtension: Types.Extensions.Extension = {
|
||||
{
|
||||
name: 'common',
|
||||
exports: {
|
||||
getStudiesForPatientByStudyInstanceUID,
|
||||
getStudiesForPatientByMRN,
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -45,4 +45,8 @@ const defaultExtension: Types.Extensions.Extension = {
|
||||
|
||||
export default defaultExtension;
|
||||
|
||||
export { ContextMenuController, CustomizeableContextMenuTypes };
|
||||
export {
|
||||
ContextMenuController,
|
||||
CustomizeableContextMenuTypes,
|
||||
getStudiesForPatientByMRN,
|
||||
};
|
||||
@@ -32,8 +32,8 @@
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "^3.0.0",
|
||||
"classnames": "^2.3.2",
|
||||
"@cornerstonejs/core": "^0.44.2",
|
||||
"@cornerstonejs/tools": "^0.66.2",
|
||||
"@cornerstonejs/core": "^0.46.2",
|
||||
"@cornerstonejs/tools": "^0.67.2",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "^3.0.0",
|
||||
"dcmjs": "^0.29.5",
|
||||
"lodash.debounce": "^4.17.21",
|
||||
|
||||
+32
-13
@@ -18,7 +18,7 @@ const { formatDate } = utils;
|
||||
function PanelStudyBrowserTracking({
|
||||
servicesManager,
|
||||
getImageSrc,
|
||||
getStudiesForPatientByStudyInstanceUID,
|
||||
getStudiesForPatientByMRN,
|
||||
requestDisplaySetCreationForStudy,
|
||||
dataSource,
|
||||
}) {
|
||||
@@ -76,8 +76,6 @@ function PanelStudyBrowserTracking({
|
||||
const activeViewportDisplaySetInstanceUIDs =
|
||||
viewports[activeViewportIndex]?.displaySetInstanceUIDs;
|
||||
|
||||
const isSingleViewport = numCols === 1 && numRows === 1;
|
||||
|
||||
useEffect(() => {
|
||||
const added = measurementService.EVENTS.MEASUREMENT_ADDED;
|
||||
const addedRaw = measurementService.EVENTS.RAW_MEASUREMENT_ADDED;
|
||||
@@ -108,15 +106,29 @@ function PanelStudyBrowserTracking({
|
||||
};
|
||||
}, [measurementService, activeViewportIndex, sendTrackedMeasurementsEvent]);
|
||||
|
||||
const { trackedStudy, trackedSeries } = trackedMeasurements.context;
|
||||
const { trackedSeries } = trackedMeasurements.context;
|
||||
|
||||
// ~~ studyDisplayList
|
||||
useEffect(() => {
|
||||
// Fetch all studies for the patient in each primary study
|
||||
async function fetchStudiesForPatient(StudyInstanceUID) {
|
||||
const qidoStudiesForPatient =
|
||||
(await getStudiesForPatientByStudyInstanceUID(StudyInstanceUID)) || [];
|
||||
// TODO: This should be "naturalized DICOM JSON" studies
|
||||
// current study qido
|
||||
const qidoForStudyUID = await dataSource.query.studies.search({
|
||||
studyInstanceUid: StudyInstanceUID,
|
||||
});
|
||||
|
||||
let qidoStudiesForPatient = qidoForStudyUID;
|
||||
|
||||
// try to fetch the prior studies based on the patientID if the
|
||||
// server can respond.
|
||||
try {
|
||||
qidoStudiesForPatient = await getStudiesForPatientByMRN(
|
||||
qidoForStudyUID
|
||||
);
|
||||
} catch (error) {
|
||||
console.warn(error);
|
||||
}
|
||||
|
||||
const mappedStudies = _mapDataSourceStudies(qidoStudiesForPatient);
|
||||
const actuallyMappedStudies = mappedStudies.map(qidoStudy => {
|
||||
return {
|
||||
@@ -125,7 +137,6 @@ function PanelStudyBrowserTracking({
|
||||
description: qidoStudy.StudyDescription,
|
||||
modalities: qidoStudy.ModalitiesInStudy,
|
||||
numInstances: qidoStudy.NumInstances,
|
||||
// displaySets: []
|
||||
};
|
||||
});
|
||||
|
||||
@@ -146,11 +157,16 @@ function PanelStudyBrowserTracking({
|
||||
|
||||
StudyInstanceUIDs.forEach(sid => fetchStudiesForPatient(sid));
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [StudyInstanceUIDs, getStudiesForPatientByStudyInstanceUID]);
|
||||
}, [StudyInstanceUIDs, getStudiesForPatientByMRN]);
|
||||
|
||||
// ~~ Initial Thumbnails
|
||||
useEffect(() => {
|
||||
const currentDisplaySets = displaySetService.activeDisplaySets;
|
||||
|
||||
if (!currentDisplaySets.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentDisplaySets.forEach(async dSet => {
|
||||
const newImageSrcEntry = {};
|
||||
const displaySet = displaySetService.getDisplaySetByUID(
|
||||
@@ -170,13 +186,16 @@ function PanelStudyBrowserTracking({
|
||||
});
|
||||
}
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [displaySetService, dataSource, getImageSrc]);
|
||||
|
||||
// ~~ displaySets
|
||||
useEffect(() => {
|
||||
// TODO: Are we sure `activeDisplaySets` will always be accurate?
|
||||
const currentDisplaySets = displaySetService.activeDisplaySets;
|
||||
|
||||
if (!currentDisplaySets.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const mappedDisplaySets = _mapDisplaySets(
|
||||
currentDisplaySets,
|
||||
thumbnailImageSrcMap,
|
||||
@@ -194,9 +213,9 @@ function PanelStudyBrowserTracking({
|
||||
}, [
|
||||
displaySetService.activeDisplaySets,
|
||||
trackedSeries,
|
||||
thumbnailImageSrcMap,
|
||||
viewports,
|
||||
dataSource,
|
||||
thumbnailImageSrcMap,
|
||||
]);
|
||||
|
||||
// ~~ subscriptions --> displaySets
|
||||
@@ -379,7 +398,7 @@ PanelStudyBrowserTracking.propTypes = {
|
||||
getImageIdsForDisplaySet: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
getImageSrc: PropTypes.func.isRequired,
|
||||
getStudiesForPatientByStudyInstanceUID: PropTypes.func.isRequired,
|
||||
getStudiesForPatientByMRN: PropTypes.func.isRequired,
|
||||
requestDisplaySetCreationForStudy: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ function getImageSrcFromImageId(cornerstone, imageId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const canvas = document.createElement('canvas');
|
||||
cornerstone.utilities
|
||||
.loadImageToCanvas(canvas, imageId)
|
||||
.loadImageToCanvas({ canvas, imageId })
|
||||
.then(imageId => {
|
||||
resolve(canvas.toDataURL());
|
||||
})
|
||||
|
||||
@@ -10,8 +10,8 @@ function _getStudyForPatientUtility(extensionManager) {
|
||||
'@ohif/extension-default.utilityModule.common'
|
||||
);
|
||||
|
||||
const { getStudiesForPatientByStudyInstanceUID } = utilityModule.exports;
|
||||
return getStudiesForPatientByStudyInstanceUID;
|
||||
const { getStudiesForPatientByMRN } = utilityModule.exports;
|
||||
return getStudiesForPatientByMRN;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -28,10 +28,10 @@ function WrappedPanelStudyBrowserTracking({
|
||||
}) {
|
||||
const dataSource = extensionManager.getActiveDataSource()[0];
|
||||
|
||||
const getStudiesForPatientByStudyInstanceUID = _getStudyForPatientUtility(
|
||||
const getStudiesForPatientByMRN = _getStudyForPatientUtility(
|
||||
extensionManager
|
||||
);
|
||||
const _getStudiesForPatientByStudyInstanceUID = getStudiesForPatientByStudyInstanceUID.bind(
|
||||
const _getStudiesForPatientByMRN = getStudiesForPatientByMRN.bind(
|
||||
null,
|
||||
dataSource
|
||||
);
|
||||
@@ -48,9 +48,7 @@ function WrappedPanelStudyBrowserTracking({
|
||||
servicesManager={servicesManager}
|
||||
dataSource={dataSource}
|
||||
getImageSrc={_getImageSrcFromImageId}
|
||||
getStudiesForPatientByStudyInstanceUID={
|
||||
_getStudiesForPatientByStudyInstanceUID
|
||||
}
|
||||
getStudiesForPatientByMRN={_getStudiesForPatientByMRN}
|
||||
requestDisplaySetCreationForStudy={_requestDisplaySetCreationForStudy}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
async function getStudiesForPatientByStudyInstanceUID(
|
||||
dataSource,
|
||||
StudyInstanceUID
|
||||
) {
|
||||
if (StudyInstanceUID === undefined) {
|
||||
return;
|
||||
}
|
||||
// TODO: The `DicomMetadataStore` should short-circuit both of these requests
|
||||
// Data _could_ be here from route query, or if using JSON data source
|
||||
// We could also force this to "await" these values being available in the DICOMStore?
|
||||
// Kind of like promise fulfillment in @cornerstonejs/dicom-image-loader when there are multiple
|
||||
// outgoing requests for the same data
|
||||
const getStudyResult = await dataSource.query.studies.search({
|
||||
studyInstanceUid: StudyInstanceUID,
|
||||
});
|
||||
|
||||
// TODO: To Erik's point, the data source likely shouldn't deviate from
|
||||
// Naturalized DICOM JSON when returning. It makes things like this awkward (mrn)
|
||||
if (getStudyResult && getStudyResult.length && getStudyResult[0].mrn) {
|
||||
return dataSource.query.studies.search({
|
||||
patientId: getStudyResult[0].mrn,
|
||||
});
|
||||
}
|
||||
console.log('No mrn found for', getStudyResult);
|
||||
// The original study we KNOW belongs to the same set, so just return it
|
||||
return getStudyResult;
|
||||
}
|
||||
|
||||
export default getStudiesForPatientByStudyInstanceUID;
|
||||
Reference in new issue
Block a user