diff --git a/extensions/cornerstone/src/init.js b/extensions/cornerstone/src/init.js index b10bff02f..e65efa36b 100644 --- a/extensions/cornerstone/src/init.js +++ b/extensions/cornerstone/src/init.js @@ -236,9 +236,8 @@ export default function init({ id: dialogId, centralize: true, isDraggable: false, - content: Dialog, - useLastPosition: false, showOverlay: true, + content: Dialog, contentProps: { title: 'Enter your annotation', value: { label }, diff --git a/extensions/default/src/DicomWebDataSource/dcm4cheeReject.js b/extensions/default/src/DicomWebDataSource/dcm4cheeReject.js new file mode 100644 index 000000000..542267abe --- /dev/null +++ b/extensions/default/src/DicomWebDataSource/dcm4cheeReject.js @@ -0,0 +1,35 @@ +export default function (wadoRoot) { + return { + series: (StudyInstanceUID, SeriesInstanceUID) => { + return new Promise((resolve, reject) => { + // Reject because of Quality. (Seems the most sensible out of the options) + const CodeValueAndCodeSchemeDesignator = `113001%5EDCM`; + + const url = `${wadoRoot}/studies/${StudyInstanceUID}/series/${SeriesInstanceUID}/reject/${CodeValueAndCodeSchemeDesignator}`; + + const xhr = new XMLHttpRequest(); + xhr.open('POST', url, true); + + //Send the proper header information along with the request + // TODO -> Auth when we re-add authorization. + + console.log(xhr); + + xhr.onreadystatechange = function () { + //Call a function when the state changes. + if (xhr.readyState == 4) { + switch (xhr.status) { + case 204: + resolve(xhr.responseText); + + break; + case 404: + reject('Your dataSource does not support reject functionality'); + } + } + }; + xhr.send(); + }); + }, + }; +} diff --git a/extensions/default/src/DicomWebDataSource/index.js b/extensions/default/src/DicomWebDataSource/index.js index 4fa32d5e5..fa8cdfda0 100644 --- a/extensions/default/src/DicomWebDataSource/index.js +++ b/extensions/default/src/DicomWebDataSource/index.js @@ -6,6 +6,7 @@ import { processResults, processSeriesResults, } from './qido.js'; +import dcm4cheeReject from './dcm4cheeReject'; import { DicomMetadataStore, IWebApiDataSource, utils } from '@ohif/core'; import getImageId from './utils/getImageId'; @@ -34,6 +35,7 @@ const EXPLICIT_VR_LITTLE_ENDIAN = '1.2.840.10008.1.2.1'; * @param {boolean} qidoSupportsIncludeField - Whether QIDO supports the "Include" option to request additional fields in response * @param {string} imageRengering - wadors | ? (unsure of where/how this is used) * @param {string} thumbnailRendering - wadors | ? (unsure of where/how this is used) + * @param {bool} supportsReject - Whether the server supports reject calls (i.e. DCM4CHEE) * @param {bool} lazyLoadStudy - "enableStudyLazyLoad"; Request series meta async instead of blocking */ function createDicomWebApi(dicomWebConfig) { @@ -43,6 +45,7 @@ function createDicomWebApi(dicomWebConfig) { enableStudyLazyLoad, supportsFuzzyMatching, supportsWildcard, + supportsReject, } = dicomWebConfig; const qidoConfig = { @@ -59,7 +62,7 @@ function createDicomWebApi(dicomWebConfig) { const qidoDicomWebClient = new api.DICOMwebClient(qidoConfig); const wadoDicomWebClient = new api.DICOMwebClient(wadoConfig); - return IWebApiDataSource.create({ + const implementation = { query: { studies: { mapParams: mapParams.bind(), @@ -234,7 +237,13 @@ function createDicomWebApi(dicomWebConfig) { return imageIds; }, - }); + }; + + if (supportsReject) { + implementation.reject = dcm4cheeReject(wadoRoot); + } + + return IWebApiDataSource.create(implementation); } export { createDicomWebApi }; diff --git a/extensions/dicom-sr/src/OHIFCornerstoneSRViewport.js b/extensions/dicom-sr/src/OHIFCornerstoneSRViewport.js index 22ddc0ec2..bfda0b808 100644 --- a/extensions/dicom-sr/src/OHIFCornerstoneSRViewport.js +++ b/extensions/dicom-sr/src/OHIFCornerstoneSRViewport.js @@ -43,6 +43,23 @@ function OHIFCornerstoneSRViewport({ const [isHydrated, setIsHydrated] = useState(displaySet.isHydrated); const { viewports, activeViewportIndex } = viewportGrid; + useEffect(() => { + const onDisplaySetsRemovedSubscription = DisplaySetService.subscribe( + DisplaySetService.EVENTS.DISPLAY_SETS_REMOVED, ({ displaySetInstanceUIDs }) => { + const activeViewport = viewports[activeViewportIndex]; + if (displaySetInstanceUIDs.includes(activeViewport.displaySetInstanceUID)) { + viewportGridService.setDisplaysetForViewport({ + viewportIndex: activeViewportIndex, + displaySetInstanceUID: undefined, + }); + } + }); + + return () => { + onDisplaySetsRemovedSubscription.unsubscribe(); + }; + }, []); + // Optional hook into tracking extension, if present. let trackedMeasurements; let sendTrackedMeasurementsEvent; diff --git a/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/index.js b/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/index.js index cfb617a10..d298fc1a5 100644 --- a/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/index.js +++ b/extensions/measurement-tracking/src/panels/PanelMeasurementTableTracking/index.js @@ -191,7 +191,6 @@ function PanelMeasurementTableTracking({ servicesManager, extensionManager }) { id: 'enter-annotation', centralize: true, isDraggable: false, - useLastPosition: false, showOverlay: true, content: Dialog, contentProps: { diff --git a/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/PanelStudyBrowserTracking.jsx b/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/PanelStudyBrowserTracking.jsx index 65390acf5..b0d58a289 100644 --- a/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/PanelStudyBrowserTracking.jsx +++ b/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/PanelStudyBrowserTracking.jsx @@ -1,7 +1,7 @@ import React, { useState, useEffect } from 'react'; import PropTypes from 'prop-types'; import { utils } from '@ohif/core'; -import { StudyBrowser, useImageViewer, useViewportGrid } from '@ohif/ui'; +import { StudyBrowser, useImageViewer, useViewportGrid, Dialog } from '@ohif/ui'; import { useTrackedMeasurements } from '../../getContextModule'; const { formatDate } = utils; @@ -13,6 +13,8 @@ const { formatDate } = utils; function PanelStudyBrowserTracking({ MeasurementService, DisplaySetService, + UIDialogService, + UINotificationService, getImageSrc, getStudiesForPatientByStudyInstanceUID, requestDisplaySetCreationForStudy, @@ -136,7 +138,11 @@ function PanelStudyBrowserTracking({ thumbnailImageSrcMap, trackedSeries, viewports, - isSingleViewport + isSingleViewport, + dataSource, + DisplaySetService, + UIDialogService, + UINotificationService ); setDisplaySets(mappedDisplaySets); @@ -146,6 +152,7 @@ function PanelStudyBrowserTracking({ trackedSeries, thumbnailImageSrcMap, viewports, + dataSource, ]); // ~~ subscriptions --> displaySets @@ -193,7 +200,11 @@ function PanelStudyBrowserTracking({ thumbnailImageSrcMap, trackedSeries, viewports, - isSingleViewport + isSingleViewport, + dataSource, + DisplaySetService, + UIDialogService, + UINotificationService ); setDisplaySets(mappedDisplaySets); @@ -227,10 +238,10 @@ function PanelStudyBrowserTracking({ ); const updatedExpandedStudyInstanceUIDs = shouldCollapseStudy ? [ - ...expandedStudyInstanceUIDs.filter( - stdyUid => stdyUid !== StudyInstanceUID - ), - ] + ...expandedStudyInstanceUIDs.filter( + stdyUid => stdyUid !== StudyInstanceUID + ), + ] : [...expandedStudyInstanceUIDs, StudyInstanceUID]; setExpandedStudyInstanceUIDs(updatedExpandedStudyInstanceUIDs); @@ -304,7 +315,7 @@ function PanelStudyBrowserTracking({ SeriesInstanceUID: displaySet.SeriesInstanceUID, }); }} - onClickThumbnail={() => {}} + onClickThumbnail={() => { }} onDoubleClickThumbnail={onDoubleClickThumbnailHandler} activeDisplaySetInstanceUID={activeDisplaySetInstanceUID} /> @@ -359,8 +370,13 @@ function _mapDisplaySets( thumbnailImageSrcMap, trackedSeriesInstanceUIDs, viewports, // TODO: make array of `displaySetInstanceUIDs`? - isSingleViewport + isSingleViewport, + dataSource, + DisplaySetService, + UIDialogService, + UINotificationService ) { + console.log(displaySets.length); const thumbnailDisplaySets = []; const thumbnailNoImageDisplaySets = []; displaySets.forEach(ds => { @@ -369,19 +385,21 @@ function _mapDisplaySets( const viewportIdentificator = isSingleViewport ? [] : viewports.reduce((acc, viewportData, index) => { - if (viewportData.displaySetInstanceUID === ds.displaySetInstanceUID) { - acc.push(_viewportLabels[index]); - } - return acc; - }, []); + if (viewportData.displaySetInstanceUID === ds.displaySetInstanceUID) { + acc.push(_viewportLabels[index]); + } + return acc; + }, []); const array = componentType === 'thumbnailTracked' ? thumbnailDisplaySets : thumbnailNoImageDisplaySets; - array.push({ - displaySetInstanceUID: ds.displaySetInstanceUID, + const { displaySetInstanceUID } = ds; + + const thumbnailProps = { + displaySetInstanceUID, description: ds.SeriesDescription, seriesNumber: ds.SeriesNumber, modality: ds.Modality, @@ -392,12 +410,71 @@ function _mapDisplaySets( imageSrc, dragData: { type: 'displayset', - displaySetInstanceUID: ds.displaySetInstanceUID, + displaySetInstanceUID, // .. Any other data to pass }, isTracked: trackedSeriesInstanceUIDs.includes(ds.SeriesInstanceUID), viewportIdentificator, - }); + }; + + if (componentType === 'thumbnailNoImage') { + if (dataSource.reject && dataSource.reject.series) { + thumbnailProps.canReject = true; + thumbnailProps.onReject = () => { + UIDialogService.create({ + id: 'ds-reject-sr', + centralize: true, + isDraggable: false, + showOverlay: true, + content: Dialog, + contentProps: { + title: 'Reject Report', + body: () => ( +
This is a destructive action.
+Are you sure you want to continue?
+