[IDC-1939] Debug Dialog part 1 (#2011)

* WIP debug dialog

* Rename the p10 downloader extension to debugger extension, add button to toolbar. Deactivate it by default.

* Fix unit tests
This commit is contained in:
James Petts 2020-09-03 11:58:04 +01:00 committed by GitHub
parent 6a37a2a661
commit a1dee16a3d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 272 additions and 19 deletions

View File

@ -3,6 +3,7 @@ import init from './init.js';
import commandsModule from './commandsModule.js'; import commandsModule from './commandsModule.js';
import toolbarModule from './toolbarModule.js'; import toolbarModule from './toolbarModule.js';
import CornerstoneViewportDownloadForm from './CornerstoneViewportDownloadForm'; import CornerstoneViewportDownloadForm from './CornerstoneViewportDownloadForm';
import { version } from '../package.json';
const Component = React.lazy(() => { const Component = React.lazy(() => {
return import('./OHIFCornerstoneViewport'); return import('./OHIFCornerstoneViewport');
@ -24,6 +25,7 @@ export default {
* Only required property. Should be a unique value across all extensions. * Only required property. Should be a unique value across all extensions.
*/ */
id: 'cornerstone', id: 'cornerstone',
version,
/** /**
* *

View File

@ -1,7 +1,7 @@
{ {
"name": "@ohif/extension-dicom-p10-downloader", "name": "@ohif/extension-debugging",
"version": "0.1.2", "version": "0.1.2",
"description": "OHIF extension for downloading DICOM P10 files", "description": "OHIF extension for debugging.",
"author": "OHIF", "author": "OHIF",
"license": "MIT", "license": "MIT",
"repository": "OHIF/Viewers", "repository": "OHIF/Viewers",
@ -35,6 +35,7 @@
"@babel/runtime": "^7.5.5", "@babel/runtime": "^7.5.5",
"dicomweb-client": "^0.6.0", "dicomweb-client": "^0.6.0",
"file-saver": "^2.0.2", "file-saver": "^2.0.2",
"jszip": "^3.2.2" "jszip": "^3.2.2",
"detect-browser": "5.1.1"
} }
} }

View File

@ -0,0 +1,4 @@
.debugReportModalHeader {
padding: 10px 0 10px;
color: var(--active-color);
}

View File

@ -0,0 +1,161 @@
import React from 'react';
import { detect } from 'detect-browser';
import './DebugReportModal.css';
const DubugReportModal = ({
viewports,
studies,
servers,
extensionManager,
}) => {
return (
<div>
<table>
{getAppVersion()}
{getExtensionVersions(extensionManager)}
{getBrowserInfo()}
{getCurrentStudyUrl()}
{getLayout(viewports)}
</table>
</div>
);
};
const getAppVersion = () => {
return (
<React.Fragment>
<tr>
<th className="debugReportModalHeader">App</th>
</tr>
<tr>
<td>Version</td>
<td>{window.version}</td>
</tr>
</React.Fragment>
);
};
const getCurrentStudyUrl = () => {
return (
<React.Fragment>
<tr>
<th className="debugReportModalHeader">App</th>
</tr>
<tr>
<td>URL</td>
<td>{window.location.href}</td>
</tr>
</React.Fragment>
);
};
const getExtensionVersions = extensionManager => {
const { registeredExtensionVesions } = extensionManager;
const lineItems = Object.keys(registeredExtensionVesions).map(extensionId => {
const version = registeredExtensionVesions[extensionId];
return (
<tr>
<td>{extensionId}</td>
<td>{version}</td>
</tr>
);
});
return (
<React.Fragment>
<th className="debugReportModalHeader">Extensions</th>
{lineItems}
</React.Fragment>
);
};
const getLayout = viewports => {
const { numRows, numColumns } = viewports;
return (
<React.Fragment>
<tr>
<th className="debugReportModalHeader">Viewports</th>
</tr>
<tr>
<th>Layout</th>
</tr>
<tr>
<td>Rows</td>
<td>{numRows}</td>
</tr>
<tr>
<td>Columns</td>
<td>{numColumns}</td>
</tr>
<tr>
<th>SeriesInstanceUIDs</th>
</tr>
{getSeriesInstanceUIDsPerRow(viewports)}
</React.Fragment>
);
};
const getBrowserInfo = () => {
const browser = detect();
const { name, os, type, version } = browser;
return (
<React.Fragment>
<tr>
<th className="debugReportModalHeader">Platform</th>
</tr>
<tr>
<td>name</td>
<td>{name}</td>
</tr>
<tr>
<td>os</td>
<td>{os}</td>
</tr>
<tr>
<td>type</td>
<td>{type}</td>
</tr>
<tr>
<td>version</td>
<td>{version}</td>
</tr>
</React.Fragment>
);
};
const getSeriesInstanceUIDsPerRow = viewports => {
const { viewportSpecificData, numColumns } = viewports;
debugger;
// NOTE viewportSpecificData is actually an object with numerical keys.
return Object.keys(viewportSpecificData).map(viewportIndex => {
const vsd = viewportSpecificData[viewportIndex];
const [row, column] = _viewportIndexToViewportPosition(
viewportIndex,
numColumns
);
return (
<tr>
<td>{`[${row},${column}]`}</td>
<td>{vsd.SeriesInstanceUID}</td>
</tr>
);
});
};
const _viewportIndexToViewportPosition = (viewportIndex, numColumns) => {
const row = Math.floor(viewportIndex / numColumns);
const column = viewportIndex % numColumns;
return [row, column];
};
export default DubugReportModal;

View File

@ -8,12 +8,14 @@ import {
getSOPInstanceReferencesFromViewports, getSOPInstanceReferencesFromViewports,
} from './utils'; } from './utils';
import _downloadAndZip, { downloadInstances } from './downloadAndZip'; import _downloadAndZip, { downloadInstances } from './downloadAndZip';
import DebugReportModal from './DebugReportModal';
import React from 'react';
const { const {
utils: { Queue }, utils: { Queue },
} = OHIF; } = OHIF;
export function getCommands(context) { export function getCommands(context, servicesManager, extensionManager) {
const queue = new Queue(1); const queue = new Queue(1);
const actions = { const actions = {
/** /**
@ -87,9 +89,32 @@ export function getCommands(context) {
serverConfig serverConfig
); );
}, },
openDebugInfoModal({ viewports, studies, servers }) {
const { UIModalService } = servicesManager.services;
const WrappedDebugReportModal = function() {
return (
<DebugReportModal
viewports={viewports}
studies={studies}
servers={servers}
extensionManager={extensionManager}
/>
);
};
UIModalService.show({
content: WrappedDebugReportModal,
title: `Debugging Information`,
});
},
}; };
const definitions = { const definitions = {
openDebugInfoModal: {
commandFn: actions.openDebugInfoModal,
storeContexts: ['viewports', 'servers', 'studies'],
},
downloadAndZip: { downloadAndZip: {
commandFn: queue.bindSafe(actions.downloadAndZip, error), commandFn: queue.bindSafe(actions.downloadAndZip, error),
storeContexts: ['servers'], storeContexts: ['servers'],

View File

@ -1,5 +1,7 @@
import { getDicomWebClientFromConfig } from './utils'; import { getDicomWebClientFromConfig } from './utils';
import { getCommands } from './commandsModule'; import { getCommands } from './commandsModule';
import { version } from '../package.json';
import toolbarModule from './toolbarModule';
/** /**
* Constants * Constants
@ -21,6 +23,7 @@ export default {
* Only required property. Should be a unique value across all extensions. * Only required property. Should be a unique value across all extensions.
*/ */
id: 'dicom-p10-downloader', id: 'dicom-p10-downloader',
version,
/** /**
* LIFECYCLE HOOKS * LIFECYCLE HOOKS
@ -37,7 +40,11 @@ export default {
* MODULE GETTERS * MODULE GETTERS
*/ */
getCommandsModule() { getCommandsModule({ servicesManager, extensionManager }) {
return getCommands(sharedContext); return getCommands(sharedContext, servicesManager, extensionManager);
},
getToolbarModule() {
return toolbarModule;
}, },
}; };

View File

@ -0,0 +1,20 @@
const TOOLBAR_BUTTON_TYPES = {
COMMAND: 'command',
};
const definitions = [
{
id: 'Debug Info',
label: 'Debug Info',
icon: 'cog',
//
type: TOOLBAR_BUTTON_TYPES.COMMAND,
commandName: 'openDebugInfoModal',
context: 'VIEWER',
},
];
export default {
definitions,
defaultContext: 'VIEWER',
};

View File

@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import OHIFDicomHtmlSopClassHandler from './OHIFDicomHtmlSopClassHandler.js'; import OHIFDicomHtmlSopClassHandler from './OHIFDicomHtmlSopClassHandler.js';
import { version } from '../package.json';
const Component = React.lazy(() => { const Component = React.lazy(() => {
return import('./OHIFDicomHtmlViewport'); return import('./OHIFDicomHtmlViewport');
@ -18,6 +19,7 @@ export default {
* Only required property. Should be a unique value across all extensions. * Only required property. Should be a unique value across all extensions.
*/ */
id: 'html', id: 'html',
version,
getViewportModule() { getViewportModule() {
return OHIFDicomHtmlViewport; return OHIFDicomHtmlViewport;

View File

@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import DicomMicroscopySopClassHandler from './DicomMicroscopySopClassHandler.js'; import DicomMicroscopySopClassHandler from './DicomMicroscopySopClassHandler.js';
import { version } from '../package.json';
const Component = React.lazy(() => { const Component = React.lazy(() => {
return import('./DicomMicroscopyViewport'); return import('./DicomMicroscopyViewport');
@ -18,6 +19,7 @@ export default {
* Only required property. Should be a unique value across all extensions. * Only required property. Should be a unique value across all extensions.
*/ */
id: 'microscopy', id: 'microscopy',
version,
getViewportModule() { getViewportModule() {
return DicomMicroscopyViewport; return DicomMicroscopyViewport;

View File

@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import OHIFDicomPDFSopClassHandler from './OHIFDicomPDFSopClassHandler.js'; import OHIFDicomPDFSopClassHandler from './OHIFDicomPDFSopClassHandler.js';
import { version } from '../package.json';
const Component = React.lazy(() => { const Component = React.lazy(() => {
return import('./ConnectedOHIFDicomPDFViewer'); return import('./ConnectedOHIFDicomPDFViewer');
@ -18,6 +19,7 @@ export default {
* Only required property. Should be a unique value across all extensions. * Only required property. Should be a unique value across all extensions.
*/ */
id: 'pdf', id: 'pdf',
version,
getViewportModule() { getViewportModule() {
return ConnectedOHIFDicomPDFViewer; return ConnectedOHIFDicomPDFViewer;
}, },

View File

@ -1,15 +1,16 @@
import React from 'react'; import React from 'react';
import init from './init.js'; import init from './init.js';
import sopClassHandlerModule from './OHIFDicomRTStructSopClassHandler'; import sopClassHandlerModule from './OHIFDicomRTStructSopClassHandler';
import id from './id.js'; import id from './id.js';
import RTPanel from './components/RTPanel/RTPanel'; import RTPanel from './components/RTPanel/RTPanel';
import { version } from '../package.json';
export default { export default {
/** /**
* Only required property. Should be a unique value across all extensions. * Only required property. Should be a unique value across all extensions.
*/ */
id, id,
version,
/** /**
* *

View File

@ -1,15 +1,16 @@
import React from 'react'; import React from 'react';
import init from './init.js'; import init from './init.js';
import toolbarModule from './toolbarModule.js'; import toolbarModule from './toolbarModule.js';
import getSopClassHandlerModule from './getOHIFDicomSegSopClassHandler.js'; import getSopClassHandlerModule from './getOHIFDicomSegSopClassHandler.js';
import SegmentationPanel from './components/SegmentationPanel/SegmentationPanel.js'; import SegmentationPanel from './components/SegmentationPanel/SegmentationPanel.js';
import { version } from '../package.json';
export default { export default {
/** /**
* Only required property. Should be a unique value across all extensions. * Only required property. Should be a unique value across all extensions.
*/ */
id: 'com.ohif.dicom-segmentation', id: 'com.ohif.dicom-segmentation',
version,
/** /**
* *

View File

@ -1,10 +1,12 @@
import MeasurementComparisonTable from './components/MeasurementComparisonTable'; import MeasurementComparisonTable from './components/MeasurementComparisonTable';
import { version } from '../package.json';
export default { export default {
/** /**
* Only required property. Should be a unique value across all extensions. * Only required property. Should be a unique value across all extensions.
*/ */
id: 'lesion-tracker', id: 'lesion-tracker',
version,
/** /**
* @param {object} params * @param {object} params
@ -26,7 +28,7 @@ export default {
component: MeasurementComparisonTable, component: MeasurementComparisonTable,
}, },
], ],
defaultContext: ['VIEWER'] defaultContext: ['VIEWER'],
}; };
}, },

View File

@ -3,6 +3,7 @@ import asyncComponent from './asyncComponent.js';
import commandsModule from './commandsModule.js'; import commandsModule from './commandsModule.js';
import toolbarModule from './toolbarModule.js'; import toolbarModule from './toolbarModule.js';
import withCommandsManager from './withCommandsManager.js'; import withCommandsManager from './withCommandsManager.js';
import { version } from '../package.json';
// This feels weird // This feels weird
// import loadLocales from './loadLocales'; // import loadLocales from './loadLocales';
@ -15,6 +16,7 @@ const vtkExtension = {
* Only required property. Should be a unique value across all extensions. * Only required property. Should be a unique value across all extensions.
*/ */
id: 'vtk', id: 'vtk',
version,
getViewportModule({ commandsManager, servicesManager }) { getViewportModule({ commandsManager, servicesManager }) {
const ExtendedVTKViewport = props => ( const ExtendedVTKViewport = props => (

View File

@ -5,6 +5,7 @@ export default class ExtensionManager {
constructor({ commandsManager, servicesManager, api, appConfig = {} }) { constructor({ commandsManager, servicesManager, api, appConfig = {} }) {
this.modules = {}; this.modules = {};
this.registeredExtensionIds = []; this.registeredExtensionIds = [];
this.registeredExtensionVesions = {};
this.moduleTypeNames = Object.values(MODULE_TYPES); this.moduleTypeNames = Object.values(MODULE_TYPES);
// //
this._commandsManager = commandsManager; this._commandsManager = commandsManager;
@ -51,6 +52,7 @@ export default class ExtensionManager {
} }
let extensionId = extension.id; let extensionId = extension.id;
const version = extension.version;
if (!extensionId) { if (!extensionId) {
extensionId = Math.random() extensionId = Math.random()
@ -98,6 +100,8 @@ export default class ExtensionManager {
// Track extension registration // Track extension registration
this.registeredExtensionIds.push(extensionId); this.registeredExtensionIds.push(extensionId);
this.registeredExtensionVesions[extensionId] = version;
} }
/** /**
@ -120,7 +124,8 @@ export default class ExtensionManager {
commandsManager: this._commandsManager, commandsManager: this._commandsManager,
appConfig: this._appConfig, appConfig: this._appConfig,
configuration, configuration,
api: this._api api: this._api,
extensionManager: this,
}); });
if (!extensionModule) { if (!extensionModule) {

View File

@ -184,6 +184,8 @@ describe('ExtensionManager.js', () => {
commandsManager, commandsManager,
appConfig, appConfig,
configuration: extensionConfiguration, configuration: extensionConfiguration,
api: undefined,
extensionManager,
}); });
} }
}); });
@ -226,7 +228,7 @@ describe('ExtensionManager.js', () => {
return { return {
definitions: { definitions: {
exampleDefinition: { exampleDefinition: {
commandFn: () => { }, commandFn: () => {},
storeContexts: [], storeContexts: [],
options: {}, options: {},
}, },

View File

@ -9,7 +9,7 @@ services:
max-size: '10m' max-size: '10m'
ports: ports:
- '389:389' - '389:389'
env_file: ./dcm4che/docker-compose-dcm4che.env env_file: ./docker-compose-dcm4che.env
volumes: volumes:
- ./dcm4che/etc/localtime:/etc/localtime:ro - ./dcm4che/etc/localtime:/etc/localtime:ro
- ./dcm4che/etc/timezone:/etc/timezone:ro - ./dcm4che/etc/timezone:/etc/timezone:ro
@ -25,7 +25,7 @@ services:
max-size: '10m' max-size: '10m'
ports: ports:
- '5432:5432' - '5432:5432'
env_file: ./dcm4che/docker-compose-dcm4che.env env_file: ./docker-compose-dcm4che.env
volumes: volumes:
- ./dcm4che/etc/localtime:/etc/localtime:ro - ./dcm4che/etc/localtime:/etc/localtime:ro
- ./dcm4che/etc/timezone:/etc/timezone:ro - ./dcm4che/etc/timezone:/etc/timezone:ro
@ -44,7 +44,7 @@ services:
- '9990:9990' - '9990:9990'
- '11112:11112' - '11112:11112'
- '2575:2575' - '2575:2575'
env_file: ./dcm4che/docker-compose-dcm4che.env env_file: ./docker-compose-dcm4che.env
environment: environment:
WILDFLY_CHOWN: /opt/wildfly/standalone /storage WILDFLY_CHOWN: /opt/wildfly/standalone /storage
WILDFLY_WAIT_FOR: ldap:389 db:5432 WILDFLY_WAIT_FOR: ldap:389 db:5432
@ -73,5 +73,4 @@ services:
restart: always restart: always
networks: networks:
- dcm4che_default - dcm4che_default
networks: dcm4che_default:
networks: dcm4che_default:

View File

@ -48,11 +48,11 @@
}, },
"dependencies": { "dependencies": {
"@babel/runtime": "^7.5.5", "@babel/runtime": "^7.5.5",
"@ohif/extension-debugging": "^0.1.2",
"@ohif/core": "^2.10.2", "@ohif/core": "^2.10.2",
"@ohif/extension-cornerstone": "^2.9.3", "@ohif/extension-cornerstone": "^2.9.3",
"@ohif/extension-dicom-html": "^1.2.6", "@ohif/extension-dicom-html": "^1.2.6",
"@ohif/extension-dicom-microscopy": "^0.51.1", "@ohif/extension-dicom-microscopy": "^0.51.1",
"@ohif/extension-dicom-p10-downloader": "^0.1.2",
"@ohif/extension-dicom-pdf": "^1.0.4", "@ohif/extension-dicom-pdf": "^1.0.4",
"@ohif/extension-dicom-rt": "^0.4.2", "@ohif/extension-dicom-rt": "^0.4.2",
"@ohif/extension-dicom-segmentation": "^0.4.2", "@ohif/extension-dicom-segmentation": "^0.4.2",

View File

@ -2,6 +2,9 @@ import commandsModule from './commandsModule.js';
export default { export default {
id: 'generic-viewer-commands', id: 'generic-viewer-commands',
get version() {
return window.version;
},
getCommandsModule({ commandsManager }) { getCommandsModule({ commandsManager }) {
return commandsModule({ commandsManager }); return commandsModule({ commandsManager });
}, },

View File

@ -9,6 +9,9 @@ export default {
* Only required property. Should be a unique value across all extensions. * Only required property. Should be a unique value across all extensions.
*/ */
id: 'measurements-table', id: 'measurements-table',
get version() {
return window.version;
},
preRegistration({ servicesManager, commandsManager, configuration = {} }) { preRegistration({ servicesManager, commandsManager, configuration = {} }) {
init({ servicesManager, commandsManager, configuration }); init({ servicesManager, commandsManager, configuration });

View File

@ -29,7 +29,9 @@ import OHIFDicomSegmentationExtension from '@ohif/extension-dicom-segmentation';
import OHIFDicomRtExtension from '@ohif/extension-dicom-rt'; import OHIFDicomRtExtension from '@ohif/extension-dicom-rt';
import OHIFDicomMicroscopyExtension from '@ohif/extension-dicom-microscopy'; import OHIFDicomMicroscopyExtension from '@ohif/extension-dicom-microscopy';
import OHIFDicomPDFExtension from '@ohif/extension-dicom-pdf'; import OHIFDicomPDFExtension from '@ohif/extension-dicom-pdf';
import OHIFDicomP10DownloaderExtension from '@ohif/extension-dicom-p10-downloader'; // Add this for Debugging purposes:
//import OHIFDebuggingExtension from '@ohif/extension-debugging';
import { version } from '../package.json';
/* /*
* Default Settings * Default Settings
@ -38,6 +40,8 @@ let config = {};
if (window) { if (window) {
config = window.config || {}; config = window.config || {};
window.version = version;
} }
const appProps = { const appProps = {
@ -49,7 +53,7 @@ const appProps = {
OHIFDicomPDFExtension, OHIFDicomPDFExtension,
OHIFDicomSegmentationExtension, OHIFDicomSegmentationExtension,
OHIFDicomRtExtension, OHIFDicomRtExtension,
OHIFDicomP10DownloaderExtension, //OHIFDebuggingExtension,
], ],
}; };

View File

@ -6782,6 +6782,11 @@ detab@2.0.2, detab@^2.0.0:
dependencies: dependencies:
repeat-string "^1.5.4" repeat-string "^1.5.4"
detect-browser@5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/detect-browser/-/detect-browser-5.1.1.tgz#a800db91d3fd60d0861669f5984f1be9ffbe009c"
integrity sha512-5n2aWI57qC3kZaK4j2zYsG6L1LrxgLptGCNhMQgdKhVn6cSdcq43pp6xHPfTHG3TYM6myF4tIPWiZtfdVDgb9w==
detect-file@^1.0.0: detect-file@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7"