diff --git a/extensions/dicom-p10-downloader/.webpack/webpack.dev.js b/extensions/dicom-p10-downloader/.webpack/webpack.dev.js deleted file mode 100644 index 1ae308448..000000000 --- a/extensions/dicom-p10-downloader/.webpack/webpack.dev.js +++ /dev/null @@ -1,8 +0,0 @@ -const path = require('path'); -const webpackCommon = require('./../../../.webpack/webpack.commonjs.js'); -const SRC_DIR = path.join(__dirname, '../src'); -const DIST_DIR = path.join(__dirname, '../dist'); - -module.exports = (env, argv) => { - return webpackCommon(env, argv, { SRC_DIR, DIST_DIR }); -}; diff --git a/extensions/dicom-p10-downloader/.webpack/webpack.prod.js b/extensions/dicom-p10-downloader/.webpack/webpack.prod.js deleted file mode 100644 index 31dff1ae7..000000000 --- a/extensions/dicom-p10-downloader/.webpack/webpack.prod.js +++ /dev/null @@ -1,38 +0,0 @@ -const merge = require('webpack-merge'); -const path = require('path'); -const webpackCommon = require('./../../../.webpack/webpack.commonjs.js'); -const pkg = require('./../package.json'); - -const ROOT_DIR = path.join(__dirname, './..'); -const SRC_DIR = path.join(__dirname, '../src'); -const DIST_DIR = path.join(__dirname, '../dist'); - -module.exports = (env, argv) => { - const commonConfig = webpackCommon(env, argv, { SRC_DIR, DIST_DIR }); - - return merge(commonConfig, { - devtool: 'source-map', - stats: { - colors: true, - hash: true, - timings: true, - assets: true, - chunks: false, - chunkModules: false, - modules: false, - children: false, - warnings: true, - }, - optimization: { - minimize: true, - sideEffects: true, - }, - output: { - path: ROOT_DIR, - library: 'OHIFExtDicomP10Downloader', - libraryTarget: 'umd', - libraryExport: 'default', - filename: pkg.main, - }, - }); -}; diff --git a/extensions/dicom-p10-downloader/CHANGELOG.md b/extensions/dicom-p10-downloader/CHANGELOG.md deleted file mode 100644 index 4243d2af5..000000000 --- a/extensions/dicom-p10-downloader/CHANGELOG.md +++ /dev/null @@ -1,28 +0,0 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -# [0.1.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-p10-downloader@0.0.2...@ohif/extension-dicom-p10-downloader@0.1.0) (2020-04-23) - - -### Features - -* configuration to hook into XHR Error handling ([e96205d](https://github.com/OHIF/Viewers/commit/e96205de35e5bec14dc8a9a8509db3dd4e6ecdb6)) - - - - - -## 0.0.2 (2020-04-15) - -**Note:** Version bump only for package @ohif/extension-dicom-p10-downloader - - - - - -# Change Log - -All notable changes to this project will be documented in this file. See -[Conventional Commits](https://conventionalcommits.org) for commit guidelines. diff --git a/extensions/dicom-p10-downloader/LICENSE b/extensions/dicom-p10-downloader/LICENSE deleted file mode 100644 index 19e20dd35..000000000 --- a/extensions/dicom-p10-downloader/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2018 Open Health Imaging Foundation - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/extensions/dicom-p10-downloader/README.md b/extensions/dicom-p10-downloader/README.md deleted file mode 100644 index 48dcd5a2a..000000000 --- a/extensions/dicom-p10-downloader/README.md +++ /dev/null @@ -1 +0,0 @@ -# @ohif/extension-dicom-p10-downloader diff --git a/extensions/dicom-p10-downloader/package.json b/extensions/dicom-p10-downloader/package.json deleted file mode 100644 index baeac8502..000000000 --- a/extensions/dicom-p10-downloader/package.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "@ohif/extension-dicom-p10-downloader", - "version": "0.1.0", - "description": "OHIF extension for downloading DICOM P10 files", - "author": "OHIF", - "license": "MIT", - "repository": "OHIF/Viewers", - "main": "dist/index.umd.js", - "module": "src/index.js", - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=10", - "npm": ">=6", - "yarn": ">=1.16.0" - }, - "files": [ - "dist", - "README.md" - ], - "scripts": { - "dev": "cross-env NODE_ENV=development webpack --config .webpack/webpack.dev.js --watch --debug --output-pathinfo", - "build": "cross-env NODE_ENV=production webpack --config .webpack/webpack.prod.js", - "build:package": "yarn run build", - "prepublishOnly": "yarn run build", - "start": "yarn run dev" - }, - "peerDependencies": { - "@ohif/core": "^2.6.0", - "dicom-parser": "^1.8.3", - "dicomweb-client": "^0.5.2" - }, - "dependencies": { - "@babel/runtime": "^7.5.5", - "dicomweb-client": "^0.6.0", - "file-saver": "^2.0.2", - "jszip": "^3.2.2" - } -} diff --git a/extensions/dicom-p10-downloader/src/commandsModule.js b/extensions/dicom-p10-downloader/src/commandsModule.js deleted file mode 100644 index 5c37024b1..000000000 --- a/extensions/dicom-p10-downloader/src/commandsModule.js +++ /dev/null @@ -1,103 +0,0 @@ -import OHIF from '@ohif/core'; -import { - save, - getDicomWebClientFromContext, - getSOPInstanceReferenceFromActiveViewport, - getSOPInstanceReferencesFromViewports, -} from './utils'; -import _downloadAndZip from './downloadAndZip'; - -const { - utils: { Queue }, -} = OHIF; - -export function getCommands(context) { - const queue = new Queue(1); - const actions = { - /** - * @example Running this command using Commands Manager - * commandsManager.runCommand( - * 'downloadAndZip', - * { - * listOfUIDs: [...], - * options: { - * progress(status) { - * console.info('Progress:', (status.progress * 100).toFixed(2) + '%'); - * } - * } - * }, - * 'VIEWER' - * ); - */ - downloadAndZip({ servers, dicomWebClient, listOfUIDs, options }) { - return save( - _downloadAndZip( - dicomWebClient || getDicomWebClientFromContext(context, servers), - listOfUIDs, - options - ), - listOfUIDs - ); - }, - downloadAndZipSeriesOnViewports({ servers, viewports, progress }) { - const dicomWebClient = getDicomWebClientFromContext(context, servers); - const listOfUIDs = getSOPInstanceReferencesFromViewports(viewports); - return save( - _downloadAndZip(dicomWebClient, listOfUIDs, { progress }), - listOfUIDs - ); - }, - downloadAndZipSeriesOnActiveViewport({ servers, viewports, progress }) { - const dicomWebClient = getDicomWebClientFromContext(context, servers); - const listOfUIDs = getSOPInstanceReferenceFromActiveViewport(viewports); - return save( - _downloadAndZip(dicomWebClient, listOfUIDs, { progress }), - listOfUIDs - ); - }, - }; - - const definitions = { - downloadAndZip: { - commandFn: queue.bindSafe(actions.downloadAndZip, error), - storeContexts: ['servers'], - }, - downloadAndZipSeriesOnViewports: { - commandFn: queue.bindSafe(actions.downloadAndZipSeriesOnViewports, error), - storeContexts: ['servers', 'viewports'], - options: { progress }, - }, - downloadAndZipSeriesOnActiveViewport: { - commandFn: queue.bindSafe( - actions.downloadAndZipSeriesOnActiveViewport, - error - ), - storeContexts: ['servers', 'viewports'], - options: { progress }, - }, - }; - - return { - actions, - definitions, - }; -} - -/** - * Utils - */ - -function progress(status) { - OHIF.log.info( - 'Download and Zip Progress:', - (status.progress * 100.0).toFixed(2) + '%' - ); -} - -function error(e) { - if (e.message === 'Queue limit reached') { - OHIF.log.warn('A download is already in progress, please wait.'); - } else { - OHIF.log.error(e); - } -} diff --git a/extensions/dicom-p10-downloader/src/downloadAndZip.js b/extensions/dicom-p10-downloader/src/downloadAndZip.js deleted file mode 100644 index ce4403580..000000000 --- a/extensions/dicom-p10-downloader/src/downloadAndZip.js +++ /dev/null @@ -1,244 +0,0 @@ -import OHIF from '@ohif/core'; -import { api } from 'dicomweb-client'; -import dicomParser from 'dicom-parser'; -import JSZip from 'jszip'; - -/** - * Constants - */ - -const { - utils: { - isDicomUid, - hierarchicalListUtils, - progressTrackingUtils: progressUtils, - }, -} = OHIF; - -/** - * Public Methods - */ - -/** - * Download and Zip all DICOM P10 instances from specified DICOM Web Client - * based on an hierarchical list of UIDs; - * - * @param {DICOMwebClient} dicomWebClient A DICOMwebClient instance through - * which the referenced instances will be retrieved; - * @param {Array} listOfUIDs The hierarchical list of UIDs from the instances - * that should be retrieved: - * A hierarchical list of UIDs is a regular JS Array where the type of the UID - * (study, series, instance) is determined by its nasting lavel. For example: - * @ The following list instructs the library to download all the instances - * from both studies "A" and "B": - * - * ['studyUIDFromA', 'studyUIDFromB'] - * - * @ In the previous example both UIDs are treated as STUDY UIDs because both - * of them are listed in the same (top) level of the list. If, on the other - * hand, only instances from series "I" and "J" from the study "B" - * are to be downloaded, the expected hierarchical list would be: - * - * ['studyUIDFromA', ['studyUIDFromB', ['seriesUIDFromI', 'seriesUIDFromJ']]] - * - * @ Which, when prettified, reads like this: - * - * [ - * 'studyUIDFromA', - * ['studyUIDFromB', [ - * 'seriesUIDFromI', - * 'seriesUIDFromJ' - * ]] - * ] - * - * @ Furthermore, if only instances "X", "Y" and "Z" from series "J" need to - * be downloaded (instead of all the instances from that series), the list - * could be changed to: - * - * [ - * 'studyUIDFromA', - * ['studyUIDFromB', [ - * 'seriesUIDFromI', - * ['seriesUIDFromJ', [ - * 'instanceUIDFromX', - * 'instanceUIDFromY', - * 'instanceUIDFromZ' - * ]] - * ]] - * ] - * - * Please refer to hierarchicalListUtils.js for more information and utilities; - * - * @param {Object} options A plain object with options; - * @param {function} options.progress A callback to retrieve notifications - * @returns {Promise} A promise that resolves to an URL from which the ZIP file - * can be downloaded; - */ - -async function downloadAndZip(dicomWebClient, listOfUIDs, options) { - if (dicomWebClient instanceof api.DICOMwebClient) { - const settings = buildSettings(listOfUIDs, options); - const { compression } = settings.tasks; - // Register user-provided progress handler as a task list observer - progressUtils.addObserver(settings.taskList, settings.options.progress); - const buffers = await downloadAll(dicomWebClient, settings).catch(error => { - // Reject promise from compression task on download failure - compression.deferred.reject(error); - throw error; - }); - compression.deferred.resolve(zipAll(buffers, settings)); - const url = await compression.deferred.promise; - return url; - } - throw new Error('A valid DICOM Web Client instance is expected'); -} - -/** - * Utils - */ - -async function zipAll(buffers, settings) { - const zip = new JSZip(); - OHIF.log.info('Adding DICOM P10 files to archive:', buffers.length); - buffers.forEach((buffer, i) => { - const path = buildPath(buffer) || `${i}.dcm`; - zip.file(path, buffer); - }); - // Set compression task progress to 50% - progressUtils.update(settings.tasks.compression.task, 0.5); - const blob = await zip.generateAsync({ type: 'blob' }); - return URL.createObjectURL(blob); -} - -function buildSettings(listOfUIDs, options) { - const taskList = progressUtils.createList(); - const compression = progressUtils.addDeferred(taskList); - const downloads = []; - - // Build downloads list - hierarchicalListUtils.forEach( - listOfUIDs, - (StudyInstanceUID, SeriesInstanceUID, SOPInstanceUID) => { - if (isDicomUid(StudyInstanceUID)) { - downloads.push({ - tracking: progressUtils.addDeferred(taskList), - parameters: [StudyInstanceUID, SeriesInstanceUID, SOPInstanceUID], - }); - } - } - ); - - // Print tree of hierarchical references - OHIF.log.info('Downloading DICOM P10 files for references:'); - OHIF.log.info(hierarchicalListUtils.print(listOfUIDs)); - - return { - options: Object(options), - taskList, - tasks: { - downloads, - compression, - }, - }; -} - -function buildPath(buffer) { - let path; - try { - const byteArray = new Uint8Array(buffer); - const dataSet = dicomParser.parseDicom(byteArray, { - // Stop parsing after SeriesInstanceUID is found - untilTag: 'x0020000e', - }); - const StudyInstanceUID = dataSet.string('x0020000d'); - const SeriesInstanceUID = dataSet.string('x0020000e'); - const SOPInstanceUID = dataSet.string('x00080018'); - if (StudyInstanceUID && SeriesInstanceUID && SOPInstanceUID) { - path = `${StudyInstanceUID}/${SeriesInstanceUID}/${SOPInstanceUID}.dcm`; - } - } catch (e) { - OHIF.log.error('Error parsing downloaded DICOM P10 file...', e); - } - return path; -} - -async function downloadAll(dicomWebClient, settings) { - const { downloads } = settings.tasks; - - // Make sure at least one download was initiated - if (downloads.length < 1) { - throw new Error('No valid reference to be downloaded'); - } - - const promises = downloads.map(item => { - const { - parameters, - tracking: { deferred, task }, - } = item; - deferred.resolve(download(task, dicomWebClient, ...parameters)); - return deferred.promise; - }); - - // Wait on created download promises - return Promise.all(promises).then(results => { - const buffers = []; - // The "results" array may directly contain buffers (ArrayBuffer instances) - // or arrays of buffers, depending on the type of downloads initiated on the - // previous step (retrieveStudy, retrieveSeries or retrieveinstance). Ex: - // results = [buf1, [buf2, buf3], buf4, [buf5], ...]; - results.forEach( - function select(nesting, result) { - if (result instanceof ArrayBuffer) { - buffers.push(result); - } else if (nesting && Array.isArray(result)) { - // "nesting" argument is important to make sure only two levels - // of arrays are visited. For example, "bufX" should not be visited: - // [buf1, [buf2, buf3, [bufX]], buf4, [buf5], ...]; - result.forEach(select.bind(null, false)); - } - }.bind(null, true) - ); - return buffers; - }); -} - -async function download( - task, - dicomWebClient, - studyInstanceUID, - seriesInstanceUID, - sopInstanceUID -) { - // Strict DICOM-formatted variable names COULDN'T be used here because the - // DICOM Web client interface expects them in this specific format. - // @TODO: Add support for download progress handler which will use the - // currently not use "task" param - if (!isDicomUid(studyInstanceUID)) { - throw new Error('Download requires at least a "StudyInstanceUID" property'); - } - if (!isDicomUid(seriesInstanceUID)) { - // Download entire study - return dicomWebClient.retrieveStudy({ - studyInstanceUID, - }); - } - if (!isDicomUid(sopInstanceUID)) { - // Download entire series - return dicomWebClient.retrieveSeries({ - studyInstanceUID, - seriesInstanceUID, - }); - } - // Download specific instance - return dicomWebClient.retrieveInstance({ - studyInstanceUID, - seriesInstanceUID, - sopInstanceUID, - }); -} - -/** - * Exports - */ - -export { downloadAndZip as default, downloadAndZip }; diff --git a/extensions/dicom-p10-downloader/src/index.js b/extensions/dicom-p10-downloader/src/index.js deleted file mode 100644 index 95fe623c4..000000000 --- a/extensions/dicom-p10-downloader/src/index.js +++ /dev/null @@ -1,43 +0,0 @@ -import { getDicomWebClientFromConfig } from './utils'; -import { getCommands } from './commandsModule'; - -/** - * Constants - */ - -/** - * Globals - */ - -const sharedContext = { - dicomWebClient: null, -}; - -/** - * Extension - */ -export default { - /** - * Only required property. Should be a unique value across all extensions. - */ - id: 'dicom-p10-downloader', - - /** - * LIFECYCLE HOOKS - */ - - preRegistration({ appConfig }) { - const dicomWebClient = getDicomWebClientFromConfig(appConfig); - if (dicomWebClient) { - sharedContext.dicomWebClient = dicomWebClient; - } - }, - - /** - * MODULE GETTERS - */ - - getCommandsModule() { - return getCommands(sharedContext); - }, -}; diff --git a/extensions/dicom-p10-downloader/src/utils.js b/extensions/dicom-p10-downloader/src/utils.js deleted file mode 100644 index d606d1f77..000000000 --- a/extensions/dicom-p10-downloader/src/utils.js +++ /dev/null @@ -1,110 +0,0 @@ -import OHIF from '@ohif/core'; -import { api } from 'dicomweb-client'; -import { saveAs } from 'file-saver'; - -const { - utils: { isDicomUid, resolveObjectPath, hierarchicalListUtils }, - DICOMWeb, -} = OHIF; - -function validDicomUid(subject) { - if (isDicomUid(subject)) { - return subject; - } -} - -function getActiveServerFromServersStore(store) { - const servers = resolveObjectPath(store, 'servers'); - if (Array.isArray(servers) && servers.length > 0) { - return servers.find(server => resolveObjectPath(server, 'active') === true); - } -} - -function getDicomWebClientFromConfig(config) { - const servers = resolveObjectPath(config, 'servers.dicomWeb'); - if (Array.isArray(servers) && servers.length > 0) { - const server = servers[0]; - return new api.DICOMwebClient({ - url: server.wadoRoot, - headers: DICOMWeb.getAuthorizationHeader(server), - }); - } -} - -function getDicomWebClientFromContext(context, store) { - const activeServer = getActiveServerFromServersStore(store); - if (activeServer) { - return new api.DICOMwebClient({ - url: activeServer.wadoRoot, - headers: DICOMWeb.getAuthorizationHeader(activeServer), - }); - } else if (context.dicomWebClient instanceof api.DICOMwebClient) { - return context.dicomWebClient; - } -} - -function getSOPInstanceReference(viewports, index) { - if (index >= 0) { - const { StudyInstanceUID, SeriesInstanceUID, SOPInstanceUID } = Object( - resolveObjectPath(viewports, `viewportSpecificData.${index}`) - ); - return Object.freeze( - hierarchicalListUtils.addToList( - [], - validDicomUid(StudyInstanceUID), - validDicomUid(SeriesInstanceUID), - validDicomUid(SOPInstanceUID) - ) - ); - } -} - -function getSOPInstanceReferenceFromActiveViewport(viewports) { - return getSOPInstanceReference( - viewports, - resolveObjectPath(viewports, 'activeViewportIndex') - ); -} - -function getSOPInstanceReferencesFromViewports(viewports) { - const list = []; - const viewportSpecificData = resolveObjectPath( - viewports, - 'viewportSpecificData' - ); - Object.keys(viewportSpecificData).forEach(index => { - const { StudyInstanceUID, SeriesInstanceUID, SOPInstanceUID } = Object( - viewportSpecificData[index] - ); - hierarchicalListUtils.addToList( - list, - validDicomUid(StudyInstanceUID), - validDicomUid(SeriesInstanceUID), - validDicomUid(SOPInstanceUID) - ); - }); - return list; -} - -function save(promise, listOfUIDs) { - return Promise.resolve(promise) - .then(url => { - OHIF.log.info('Files successfully compressed:', url); - const StudyInstanceUID = hierarchicalListUtils.getItem(listOfUIDs, 0); - saveAs(url, `${StudyInstanceUID}.zip`); - return url; - }) - .catch(error => { - OHIF.log.error('Failed to create Zip file...', error); - return null; - }); -} - -export { - save, - validDicomUid, - getDicomWebClientFromConfig, - getDicomWebClientFromContext, - getSOPInstanceReferenceFromActiveViewport, - getSOPInstanceReferencesFromViewports, -};