diff --git a/extensions/cornerstone-dicom-seg/src/index.tsx b/extensions/cornerstone-dicom-seg/src/index.tsx index 79ec45594..c05426007 100644 --- a/extensions/cornerstone-dicom-seg/src/index.tsx +++ b/extensions/cornerstone-dicom-seg/src/index.tsx @@ -91,4 +91,4 @@ const extension = { }; export default extension; -export { hydrateSEGDisplaySet }; \ No newline at end of file +export { hydrateSEGDisplaySet }; diff --git a/extensions/cornerstone-dicom-seg/src/panels/PanelSegmentation.tsx b/extensions/cornerstone-dicom-seg/src/panels/PanelSegmentation.tsx index bc645652a..f11858422 100644 --- a/extensions/cornerstone-dicom-seg/src/panels/PanelSegmentation.tsx +++ b/extensions/cornerstone-dicom-seg/src/panels/PanelSegmentation.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState, useCallback } from 'react'; import PropTypes from 'prop-types'; import { SegmentationGroupTable } from '@ohif/ui'; import callInputDialog from './callInputDialog'; - +import { useAppConfig } from '@state'; import { useTranslation } from 'react-i18next'; export default function PanelSegmentation({ @@ -10,6 +10,8 @@ export default function PanelSegmentation({ commandsManager, }) { const { segmentationService, uiDialogService } = servicesManager.services; + const [appConfig] = useAppConfig(); + const disableEditing = appConfig?.disableEditing; const { t } = useTranslation('PanelSegmentation'); const [selectedSegmentationId, setSelectedSegmentationId] = useState(null); @@ -203,6 +205,7 @@ export default function PanelSegmentation({ onSegmentationEdit={onSegmentationEdit} onSegmentClick={onSegmentClick} onSegmentEdit={onSegmentEdit} + disableEditing={disableEditing} onSegmentColorClick={onSegmentColorClick} onSegmentDelete={onSegmentDelete} onToggleSegmentVisibility={onToggleSegmentVisibility} @@ -268,6 +271,7 @@ PanelSegmentation.propTypes = { commandsManager: PropTypes.shape({ runCommand: PropTypes.func.isRequired, }), + appConfig: PropTypes.object.isRequired, servicesManager: PropTypes.shape({ services: PropTypes.shape({ segmentationService: PropTypes.shape({ diff --git a/extensions/cornerstone-dicom-sr/src/utils/hydrateStructuredReport.js b/extensions/cornerstone-dicom-sr/src/utils/hydrateStructuredReport.js index a25a74c91..fbe06c922 100644 --- a/extensions/cornerstone-dicom-sr/src/utils/hydrateStructuredReport.js +++ b/extensions/cornerstone-dicom-sr/src/utils/hydrateStructuredReport.js @@ -2,6 +2,8 @@ import { utilities, metaData } from '@cornerstonejs/core'; import OHIF, { DicomMetadataStore } from '@ohif/core'; import getLabelFromDCMJSImportedToolData from './getLabelFromDCMJSImportedToolData'; import { adaptersSR } from '@cornerstonejs/adapters'; +import { annotation as CsAnnotation } from '@cornerstonejs/tools'; +const { locking } = CsAnnotation; const { guid } = OHIF.utils; const { MeasurementReport, CORNERSTONE_3D_TAG } = adaptersSR.Cornerstone3D; @@ -41,9 +43,11 @@ const convertSites = (codingValues, sites) => { * */ export default function hydrateStructuredReport( - { servicesManager, extensionManager }, + { servicesManager, extensionManager, appConfig }, displaySetInstanceUID ) { + const annotationManager = CsAnnotation.state.getAnnotationManager(); + const disableEditing = appConfig?.disableEditing; const dataSource = extensionManager.getActiveDataSource()[0]; const { measurementService, @@ -218,7 +222,7 @@ export default function hydrateStructuredReport( m => m.annotationType === annotationType ); - measurementService.addRawMeasurement( + const newAnnotationUID = measurementService.addRawMeasurement( source, annotationType, { annotation }, @@ -226,6 +230,13 @@ export default function hydrateStructuredReport( dataSource ); + if (disableEditing) { + const addedAnnotation = annotationManager.getAnnotation( + newAnnotationUID + ); + locking.setAnnotationLocked(addedAnnotation, true); + } + if (!imageIds.includes(imageId)) { imageIds.push(imageId); } diff --git a/extensions/cornerstone-dicom-sr/src/viewports/OHIFCornerstoneSRViewport.tsx b/extensions/cornerstone-dicom-sr/src/viewports/OHIFCornerstoneSRViewport.tsx index 3eb2dfe58..da60f2d83 100644 --- a/extensions/cornerstone-dicom-sr/src/viewports/OHIFCornerstoneSRViewport.tsx +++ b/extensions/cornerstone-dicom-sr/src/viewports/OHIFCornerstoneSRViewport.tsx @@ -7,6 +7,7 @@ import { setTrackingUniqueIdentifiersForElement } from '../tools/modules/dicomSR import { Icon, Tooltip, useViewportGrid, ViewportActionBar } from '@ohif/ui'; import hydrateStructuredReport from '../utils/hydrateStructuredReport'; +import { useAppConfig } from '@state'; const { formatDate } = utils; @@ -27,6 +28,8 @@ function OHIFCornerstoneSRViewport(props) { extensionManager, } = props; + const [appConfig] = useAppConfig(); + const { displaySetService, cornerstoneViewportService, @@ -76,7 +79,7 @@ function OHIFCornerstoneSRViewport(props) { sendTrackedMeasurementsEvent = (eventName, { displaySetInstanceUID }) => { measurementService.clearMeasurements(); const { SeriesInstanceUIDs } = hydrateStructuredReport( - { servicesManager, extensionManager }, + { servicesManager, extensionManager, appConfig }, displaySetInstanceUID ); const displaySets = displaySetService.getDisplaySetsForSeries( diff --git a/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/TrackedMeasurementsContext.tsx b/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/TrackedMeasurementsContext.tsx index ccb95cb2e..c260cf7d8 100644 --- a/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/TrackedMeasurementsContext.tsx +++ b/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/TrackedMeasurementsContext.tsx @@ -13,6 +13,7 @@ import promptTrackNewStudy from './promptTrackNewStudy'; import promptSaveReport from './promptSaveReport'; import promptHydrateStructuredReport from './promptHydrateStructuredReport'; import hydrateStructuredReport from './hydrateStructuredReport'; +import { useAppConfig } from '@state'; const TrackedMeasurementsContext = React.createContext(); TrackedMeasurementsContext.displayName = 'TrackedMeasurementsContext'; @@ -29,6 +30,8 @@ function TrackedMeasurementsContextProvider( { servicesManager, commandsManager, extensionManager }, // Bound by consumer { children } // Component props ) { + const [appConfig] = useAppConfig(); + const [viewportGrid, viewportGridService] = useViewportGrid(); const { activeViewportIndex, viewports } = viewportGrid; const { measurementService, displaySetService } = servicesManager.services; @@ -125,27 +128,33 @@ function TrackedMeasurementsContextProvider( promptBeginTracking: promptBeginTracking.bind(null, { servicesManager, extensionManager, + appConfig, }), promptTrackNewSeries: promptTrackNewSeries.bind(null, { servicesManager, extensionManager, + appConfig, }), promptTrackNewStudy: promptTrackNewStudy.bind(null, { servicesManager, extensionManager, + appConfig, }), promptSaveReport: promptSaveReport.bind(null, { servicesManager, commandsManager, extensionManager, + appConfig, }), promptHydrateStructuredReport: promptHydrateStructuredReport.bind(null, { servicesManager, extensionManager, + appConfig, }), hydrateStructuredReport: hydrateStructuredReport.bind(null, { servicesManager, extensionManager, + appConfig, }), }); @@ -245,6 +254,7 @@ TrackedMeasurementsContextProvider.propTypes = { servicesManager: PropTypes.object.isRequired, commandsManager: PropTypes.object.isRequired, extensionManager: PropTypes.object.isRequired, + appConfig: PropTypes.object, }; export { diff --git a/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/promptHydrateStructuredReport.js b/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/promptHydrateStructuredReport.js index 3eb35c33a..925396b9a 100644 --- a/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/promptHydrateStructuredReport.js +++ b/extensions/measurement-tracking/src/contexts/TrackedMeasurementsContext/promptHydrateStructuredReport.js @@ -12,7 +12,7 @@ const RESPONSE = { }; function promptHydrateStructuredReport( - { servicesManager, extensionManager }, + { servicesManager, extensionManager, appConfig }, ctx, evt ) { @@ -25,7 +25,7 @@ function promptHydrateStructuredReport( displaySetInstanceUID ); - return new Promise(async function(resolve, reject) { + return new Promise(async function (resolve, reject) { const promptResult = await _askTrackMeasurements( uiViewportDialogService, viewportIndex @@ -37,7 +37,7 @@ function promptHydrateStructuredReport( if (promptResult === RESPONSE.HYDRATE_REPORT) { console.warn('!! HYDRATING STRUCTURED REPORT'); const hydrationResult = hydrateStructuredReport( - { servicesManager, extensionManager }, + { servicesManager, extensionManager, appConfig }, displaySetInstanceUID ); @@ -57,7 +57,7 @@ function promptHydrateStructuredReport( } function _askTrackMeasurements(uiViewportDialogService, viewportIndex) { - return new Promise(function(resolve, reject) { + return new Promise(function (resolve, reject) { const message = 'Do you want to continue tracking measurements for this study?'; const actions = [ diff --git a/platform/core/src/services/MeasurementService/MeasurementService.ts b/platform/core/src/services/MeasurementService/MeasurementService.ts index bd1ac8b84..66ef23d6a 100644 --- a/platform/core/src/services/MeasurementService/MeasurementService.ts +++ b/platform/core/src/services/MeasurementService/MeasurementService.ts @@ -484,7 +484,7 @@ class MeasurementService extends PubSubService { }); } - return newMeasurement.id; + return newMeasurement.uid; } /** diff --git a/platform/docs/docs/configuration/configurationFiles.md b/platform/docs/docs/configuration/configurationFiles.md index 40a2d84d3..238260e46 100644 --- a/platform/docs/docs/configuration/configurationFiles.md +++ b/platform/docs/docs/configuration/configurationFiles.md @@ -111,7 +111,8 @@ window.config = ({ servicesManager } = {}) => { ## Configuration Options Here are a list of some options available: - +- `disableEditing`: If true, it disables editing in OHIF, hiding edit buttons in segmentation + panel and locking already stored measurements. - `maxNumberOfWebWorkers`: The maximum number of web workers to use for decoding. Defaults to minimum of `navigator.hardwareConcurrency` and what is specified by `maxNumberOfWebWorkers`. Some windows machines require smaller values. @@ -163,10 +164,10 @@ if auth headers are used, a preflight request is required. onModeEnter: ({ servicesManager, extensionManager, commandsManager }) => { ... }, - /** - * This mode allows its configuration to be overwritten by + /** + * This mode allows its configuration to be overwritten by * destructuring the modeConfiguration value from the mode fatory function - * at the end of the mode configuration definition. + * at the end of the mode configuration definition. */ ...modeConfiguration, }; diff --git a/platform/ui/src/components/SegmentationGroupTable/SegmentationGroup.tsx b/platform/ui/src/components/SegmentationGroupTable/SegmentationGroup.tsx index 16be9a7cf..827d32cde 100644 --- a/platform/ui/src/components/SegmentationGroupTable/SegmentationGroup.tsx +++ b/platform/ui/src/components/SegmentationGroupTable/SegmentationGroup.tsx @@ -13,11 +13,12 @@ const AddNewSegmentRow = ({ onSegmentAdd, isVisible, showAddSegment, + disableEditing, }) => { return (
- {showAddSegment && ( + {showAddSegment && !disableEditing && (
onSegmentAdd()}>
Add Segment
@@ -53,6 +54,7 @@ const SegmentGroupHeader = ({ isActive, segmentCount, onSegmentationEdit, + disableEditing, onSegmentationDelete, }) => { return ( @@ -92,34 +94,36 @@ const SegmentGroupHeader = ({ e.stopPropagation(); }} > - { - onSegmentationEdit(id); + {!disableEditing && ( + { + onSegmentationEdit(id); + }, }, - }, - { - title: 'Delete', - onClick: () => { - onSegmentationDelete(id); + { + title: 'Delete', + onClick: () => { + onSegmentationDelete(id); + }, }, - }, - ]} - > - - - - + + + + + )}
); @@ -134,6 +138,7 @@ const SegmentationGroup = ({ onSegmentClick, isMinimized, onSegmentColorClick, + disableEditing, showAddSegment, segments, activeSegmentIndex, @@ -162,6 +167,7 @@ const SegmentationGroup = ({ segmentCount={segmentCount} onSegmentationEdit={onSegmentationEdit} onSegmentationDelete={onSegmentationDelete} + disableEditing={disableEditing} /> {!isMinimized && (
@@ -171,6 +177,7 @@ const SegmentationGroup = ({ isVisible={isVisible} onToggleSegmentationVisibility={onToggleSegmentationVisibility} id={id} + disableEditing={disableEditing} showAddSegment={showAddSegment} />
@@ -197,6 +204,7 @@ const SegmentationGroup = ({ isActive={activeSegmentIndex === segmentIndex} isLocked={isLocked} isVisible={isVisible} + disableEditing={disableEditing} onClick={onSegmentClick} onEdit={onSegmentEdit} onDelete={onSegmentDelete} @@ -241,6 +249,7 @@ SegmentationGroup.propTypes = { onSegmentationConfigChange: PropTypes.func.isRequired, onSegmentationDelete: PropTypes.func.isRequired, onSegmentEdit: PropTypes.func.isRequired, + disableEditing: PropTypes.bool, }; SegmentationGroup.defaultProps = { diff --git a/platform/ui/src/components/SegmentationGroupTable/SegmentationGroupSegment.tsx b/platform/ui/src/components/SegmentationGroupTable/SegmentationGroupSegment.tsx index 6a33b42fe..f88aa8502 100644 --- a/platform/ui/src/components/SegmentationGroupTable/SegmentationGroupSegment.tsx +++ b/platform/ui/src/components/SegmentationGroupTable/SegmentationGroupSegment.tsx @@ -12,6 +12,7 @@ const SegmentItem = ({ isVisible, color, showSegmentDelete, + disableEditing, isLocked = false, onClick, onEdit, @@ -116,17 +117,19 @@ const SegmentItem = ({ )} {isHovering && (
- { - e.stopPropagation(); - onEdit(segmentationId, segmentIndex); - }} - /> + {!disableEditing && ( + { + e.stopPropagation(); + onEdit(segmentationId, segmentIndex); + }} + /> + )} {isVisible ? ( - {showAddSegmentation && ( + {showAddSegmentation && !disableEditing && (
onSegmentationAdd()} @@ -106,6 +108,7 @@ SegmentationGroupTable.propTypes = { onToggleVisibility: PropTypes.func.isRequired, onToggleVisibilityAll: PropTypes.func.isRequired, segmentationConfig: PropTypes.object, + disableEditing: PropTypes.bool, }; SegmentationGroupTable.defaultProps = {