From a3caeb381eddca8056df31b7b0e3f67afd2f2ec3 Mon Sep 17 00:00:00 2001 From: Danny Brown Date: Mon, 6 Jan 2020 16:26:57 -0500 Subject: [PATCH] chore: remove unused file; duplicated by studyInstanceHelpers (#1349) * chore: remove unused file; duplicated by studyInstanceHelpers * Change expected study list results num --- platform/core/src/studies/studyUtils.js | 413 ------------------ .../integration/common/OHIFStudyList.spec.js | 38 +- 2 files changed, 19 insertions(+), 432 deletions(-) delete mode 100644 platform/core/src/studies/studyUtils.js diff --git a/platform/core/src/studies/studyUtils.js b/platform/core/src/studies/studyUtils.js deleted file mode 100644 index a0dd54ffd..000000000 --- a/platform/core/src/studies/studyUtils.js +++ /dev/null @@ -1,413 +0,0 @@ -import DICOMWeb from '../DICOMWeb/'; - -const WADOProxy = { - convertURL: (url, server) => { - // TODO: Remove all WADOProxy stuff from this file - return url; - }, -}; -function parseFloatArray(obj) { - const result = []; - - if (!obj) { - return result; - } - - const objs = obj.split('\\'); - for (let i = 0; i < objs.length; i++) { - result.push(parseFloat(objs[i])); - } - - return result; -} - -/** - * Create a plain JS object that describes a study (a study descriptor object) - * @param {Object} server Object with server configuration parameters - * @param {Object} aSopInstance a SOP Instance from which study information will be added - */ -function createStudy(server, aSopInstance) { - // TODO: Pass a reference ID to the server instead of including the URLs here - return { - seriesList: [], - seriesMap: Object.create(null), - seriesLoader: null, - wadoUriRoot: server.wadoUriRoot, - wadoRoot: server.wadoRoot, - qidoRoot: server.qidoRoot, - patientName: DICOMWeb.getName(aSopInstance['00100010']), - patientId: DICOMWeb.getString(aSopInstance['00100020']), - patientAge: DICOMWeb.getNumber(aSopInstance['00101010']), - patientSize: DICOMWeb.getNumber(aSopInstance['00101020']), - patientWeight: DICOMWeb.getNumber(aSopInstance['00101030']), - accessionNumber: DICOMWeb.getString(aSopInstance['00080050']), - studyDate: DICOMWeb.getString(aSopInstance['00080020']), - modalities: DICOMWeb.getString(aSopInstance['00080061']), - studyDescription: DICOMWeb.getString(aSopInstance['00081030']), - imageCount: DICOMWeb.getString(aSopInstance['00201208']), - studyInstanceUid: DICOMWeb.getString(aSopInstance['0020000D']), - institutionName: DICOMWeb.getString(aSopInstance['00080080']), - }; -} - -/** Returns a WADO url for an instance - * - * @param studyInstanceUid - * @param seriesInstanceUid - * @param sopInstanceUid - * @returns {string} - */ -function buildInstanceWadoUrl( - server, - studyInstanceUid, - seriesInstanceUid, - sopInstanceUid -) { - // TODO: This can be removed, since DICOMWebClient has the same function. Not urgent, though - const params = []; - - params.push('requestType=WADO'); - params.push(`studyUID=${studyInstanceUid}`); - params.push(`seriesUID=${seriesInstanceUid}`); - params.push(`objectUID=${sopInstanceUid}`); - params.push('contentType=application/dicom'); - params.push('transferSyntax=*'); - - const paramString = params.join('&'); - - return `${server.wadoUriRoot}?${paramString}`; -} - -function buildInstanceWadoRsUri( - server, - studyInstanceUid, - seriesInstanceUid, - sopInstanceUid -) { - return `${server.wadoRoot}/studies/${studyInstanceUid}/series/${seriesInstanceUid}/instances/${sopInstanceUid}`; -} - -function buildInstanceFrameWadoRsUri( - server, - studyInstanceUid, - seriesInstanceUid, - sopInstanceUid, - frame -) { - const baseWadoRsUri = buildInstanceWadoRsUri( - server, - studyInstanceUid, - seriesInstanceUid, - sopInstanceUid - ); - frame = frame != null || 1; - - return `${baseWadoRsUri}/frames/${frame}`; -} - -function getFrameIncrementPointer(element) { - const frameIncrementPointerNames = { - '00181065': 'frameTimeVector', - '00181063': 'frameTime', - }; - - if (!element || !element.Value || !element.Value.length) { - return; - } - - const value = element.Value[0]; - return frameIncrementPointerNames[value]; -} - -function getRadiopharmaceuticalInfo(instance) { - const modality = DICOMWeb.getString(instance['00080060']); - - if (modality !== 'PT') { - return; - } - - const radiopharmaceuticalInfo = instance['00540016']; - if ( - radiopharmaceuticalInfo === undefined || - !radiopharmaceuticalInfo.Value || - !radiopharmaceuticalInfo.Value.length - ) { - return; - } - - const firstPetRadiopharmaceuticalInfo = radiopharmaceuticalInfo.Value[0]; - return { - radiopharmaceuticalStartTime: DICOMWeb.getString( - firstPetRadiopharmaceuticalInfo['00181072'] - ), - radionuclideTotalDose: DICOMWeb.getNumber( - firstPetRadiopharmaceuticalInfo['00181074'] - ), - radionuclideHalfLife: DICOMWeb.getNumber( - firstPetRadiopharmaceuticalInfo['00181075'] - ), - }; -} - -/** - * Parses the SourceImageSequence, if it exists, in order - * to return a ReferenceSOPInstanceUID. The ReferenceSOPInstanceUID - * is used to refer to this image in any accompanying DICOM-SR documents. - * - * @param instance - * @returns {String} The ReferenceSOPInstanceUID - */ -function getSourceImageInstanceUid(instance) { - // TODO= Parse the whole Source Image Sequence - // This is a really poor workaround for now. - // Later we should probably parse the whole sequence. - var SourceImageSequence = instance['00082112']; - if ( - SourceImageSequence && - SourceImageSequence.Value && - SourceImageSequence.Value.length && - SourceImageSequence.Value[0]['00081155'].Value - ) { - return SourceImageSequence.Value[0]['00081155'].Value[0]; - } -} - -async function makeSOPInstance(server, study, instance) { - const { studyInstanceUid } = study; - const seriesInstanceUid = DICOMWeb.getString(instance['0020000E']); - let series = study.seriesMap[seriesInstanceUid]; - - if (!series) { - series = { - seriesInstanceUid, - seriesDescription: DICOMWeb.getString(instance['0008103E']), - modality: DICOMWeb.getString(instance['00080060']), - seriesNumber: DICOMWeb.getNumber(instance['00200011']), - seriesDate: DICOMWeb.getString(instance['00080021']), - seriesTime: DICOMWeb.getString(instance['00080031']), - instances: [], - }; - study.seriesMap[seriesInstanceUid] = series; - study.seriesList.push(series); - } - - const sopInstanceUid = DICOMWeb.getString(instance['00080018']); - const wadouri = buildInstanceWadoUrl( - server, - studyInstanceUid, - seriesInstanceUid, - sopInstanceUid - ); - const baseWadoRsUri = buildInstanceWadoRsUri( - server, - studyInstanceUid, - seriesInstanceUid, - sopInstanceUid - ); - const wadorsuri = buildInstanceFrameWadoRsUri( - server, - studyInstanceUid, - seriesInstanceUid, - sopInstanceUid - ); - - const sopInstance = { - imageType: DICOMWeb.getString(instance['00080008']), - sopClassUid: DICOMWeb.getString(instance['00080016']), - modality: DICOMWeb.getString(instance['00080060']), - sopInstanceUid, - instanceNumber: DICOMWeb.getNumber(instance['00200013']), - imagePositionPatient: DICOMWeb.getString(instance['00200032']), - imageOrientationPatient: DICOMWeb.getString(instance['00200037']), - frameOfReferenceUID: DICOMWeb.getString(instance['00200052']), - sliceLocation: DICOMWeb.getNumber(instance['00201041']), - samplesPerPixel: DICOMWeb.getNumber(instance['00280002']), - photometricInterpretation: DICOMWeb.getString(instance['00280004']), - planarConfiguration: DICOMWeb.getNumber(instance['00280006']), - rows: DICOMWeb.getNumber(instance['00280010']), - columns: DICOMWeb.getNumber(instance['00280011']), - pixelSpacing: DICOMWeb.getString(instance['00280030']), - pixelAspectRatio: DICOMWeb.getString(instance['00280034']), - bitsAllocated: DICOMWeb.getNumber(instance['00280100']), - bitsStored: DICOMWeb.getNumber(instance['00280101']), - highBit: DICOMWeb.getNumber(instance['00280102']), - pixelRepresentation: DICOMWeb.getNumber(instance['00280103']), - smallestPixelValue: DICOMWeb.getNumber(instance['00280106']), - largestPixelValue: DICOMWeb.getNumber(instance['00280107']), - windowCenter: DICOMWeb.getString(instance['00281050']), - windowWidth: DICOMWeb.getString(instance['00281051']), - rescaleIntercept: DICOMWeb.getNumber(instance['00281052']), - rescaleSlope: DICOMWeb.getNumber(instance['00281053']), - rescaleType: DICOMWeb.getNumber(instance['00281054']), - sourceImageInstanceUid: getSourceImageInstanceUid(instance), - laterality: DICOMWeb.getString(instance['00200062']), - viewPosition: DICOMWeb.getString(instance['00185101']), - acquisitionDateTime: DICOMWeb.getString(instance['0008002A']), - numberOfFrames: DICOMWeb.getNumber(instance['00280008']), - frameIncrementPointer: getFrameIncrementPointer(instance['00280009']), - frameTime: DICOMWeb.getNumber(instance['00181063']), - frameTimeVector: parseFloatArray(DICOMWeb.getString(instance['00181065'])), - sliceThickness: DICOMWeb.getNumber(instance['00180050']), - spacingBetweenSlices: DICOMWeb.getString(instance['00180088']), - lossyImageCompression: DICOMWeb.getString(instance['00282110']), - derivationDescription: DICOMWeb.getString(instance['00282111']), - lossyImageCompressionRatio: DICOMWeb.getString(instance['00282112']), - lossyImageCompressionMethod: DICOMWeb.getString(instance['00282114']), - echoNumber: DICOMWeb.getString(instance['00180086']), - contrastBolusAgent: DICOMWeb.getString(instance['00180010']), - radiopharmaceuticalInfo: getRadiopharmaceuticalInfo(instance), - baseWadoRsUri: baseWadoRsUri, - wadouri: WADOProxy.convertURL(wadouri, server), - wadorsuri: WADOProxy.convertURL(wadorsuri, server), - wadoRoot: server.wadoRoot, - imageRendering: server.imageRendering, - thumbnailRendering: server.thumbnailRendering, - }; - - // Get additional information if the instance uses "PALETTE COLOR" photometric interpretation - if (sopInstance.photometricInterpretation === 'PALETTE COLOR') { - const redPaletteColorLookupTableDescriptor = parseFloatArray( - DICOMWeb.getString(instance['00281101']) - ); - const greenPaletteColorLookupTableDescriptor = parseFloatArray( - DICOMWeb.getString(instance['00281102']) - ); - const bluePaletteColorLookupTableDescriptor = parseFloatArray( - DICOMWeb.getString(instance['00281103']) - ); - const palettes = await getPaletteColors( - server, - instance, - redPaletteColorLookupTableDescriptor - ); - - if (palettes) { - if (palettes.uid) { - sopInstance.paletteColorLookupTableUID = palettes.uid; - } - - sopInstance.redPaletteColorLookupTableData = palettes.red; - sopInstance.greenPaletteColorLookupTableData = palettes.green; - sopInstance.bluePaletteColorLookupTableData = palettes.blue; - sopInstance.redPaletteColorLookupTableDescriptor = redPaletteColorLookupTableDescriptor; - sopInstance.greenPaletteColorLookupTableDescriptor = greenPaletteColorLookupTableDescriptor; - sopInstance.bluePaletteColorLookupTableDescriptor = bluePaletteColorLookupTableDescriptor; - } - } - - series.instances.push(sopInstance); - return sopInstance; -} - -function getPaletteColor(server, instance, tag, lutDescriptor) { - const numLutEntries = lutDescriptor[0]; - const bits = lutDescriptor[2]; - - let uri = WADOProxy.convertURL(instance[tag].BulkDataURI, server); - - // TODO: Workaround for dcm4chee behind SSL-terminating proxy returning - // incorrect bulk data URIs - if (server.wadoRoot.indexOf('https') === 0 && !uri.includes('https')) { - uri = uri.replace('http', 'https'); - } - - const config = { - url: server.wadoRoot, //BulkDataURI is absolute, so this isn't used - headers: DICOMWeb.getAuthorizationHeader(server), - }; - const dicomWeb = new api.DICOMwebClient(config); - const options = { - BulkDataURI: uri, - }; - - const readUInt16 = (byteArray, position) => { - return byteArray[position] + byteArray[position + 1] * 256; - }; - - const arrayBufferToPaletteColorLUT = result => { - const arraybuffer = result[0]; - const byteArray = new Uint8Array(arraybuffer); - const lut = []; - - for (let i = 0; i < numLutEntries; i++) { - if (bits === 16) { - lut[i] = readUInt16(byteArray, i * 2); - } else { - lut[i] = byteArray[i]; - } - } - - return lut; - }; - - return dicomWeb.retrieveBulkData(options).then(arrayBufferToPaletteColorLUT); -} - -/** - * Fetch palette colors for instances with "PALETTE COLOR" photometricInterpretation. - * - * @param server {Object} Current server; - * @param instance {Object} The retrieved instance metadata; - * @returns {String} The ReferenceSOPInstanceUID - */ -async function getPaletteColors(server, instance, lutDescriptor) { - let paletteUID = DICOMWeb.getString(instance['00281199']); - - return new Promise((resolve, reject) => { - let entry; - if (paletteColorCache.isValidUID(paletteUID)) { - entry = paletteColorCache.get(paletteUID); - - if (entry) { - return resolve(entry); - } - } - - // no entry in cache... Fetch remote data. - const r = getPaletteColor(server, instance, '00281201', lutDescriptor); - const g = getPaletteColor(server, instance, '00281202', lutDescriptor); - const b = getPaletteColor(server, instance, '00281203', lutDescriptor); - - const promises = [r, g, b]; - - Promise.all(promises).then(args => { - entry = { - red: args[0], - green: args[1], - blue: args[2], - }; - - // when paletteUID is present, the entry can be cached... - entry.uid = paletteUID; - paletteColorCache.add(entry); - - resolve(entry); - }); - }); -} - -/** - * Add a list of SOP Instances to a given study object descriptor - * @param {Object} server Object with server configuration parameters - * @param {Object} study The study descriptor to which the given SOP instances will be added - * @param {Array} sopInstanceList A list of SOP instance objects - */ -async function addInstancesToStudy(server, study, sopInstanceList) { - return Promise.all( - sopInstanceList.map(function(sopInstance) { - return makeSOPInstance(server, study, sopInstance); - }) - ); -} - -const createStudyFromSOPInstanceList = async (server, sopInstanceList) => { - if (Array.isArray(sopInstanceList) && sopInstanceList.length > 0) { - const firstSopInstance = sopInstanceList[0]; - const study = createStudy(server, firstSopInstance); - await addInstancesToStudy(server, study, sopInstanceList); - return study; - } - throw new Error('Failed to create study out of provided SOP instance list'); -}; - -export { createStudyFromSOPInstanceList, addInstancesToStudy }; diff --git a/platform/viewer/cypress/integration/common/OHIFStudyList.spec.js b/platform/viewer/cypress/integration/common/OHIFStudyList.spec.js index ac80c6e20..c5ce2944f 100644 --- a/platform/viewer/cypress/integration/common/OHIFStudyList.spec.js +++ b/platform/viewer/cypress/integration/common/OHIFStudyList.spec.js @@ -1,14 +1,14 @@ //We are keeping the hardcoded results values for the study list tests //this is intended to be running in a controled docker environment with test data. -describe('OHIF Study List', function () { - context('Desktop resolution', function () { - beforeEach(function () { +describe('OHIF Study List', function() { + context('Desktop resolution', function() { + beforeEach(function() { cy.viewport(1750, 720); cy.openStudyList(); cy.initStudyListAliasesOnDesktop(); }); - it('searches Patient Name with exact string', function () { + it('searches Patient Name with exact string', function() { cy.get('@patientName').type('Juno'); //Wait result list to be displayed cy.waitStudyList(); @@ -18,7 +18,7 @@ describe('OHIF Study List', function () { }); }); - it('searches MRN with exact string', function () { + it('searches MRN with exact string', function() { cy.get('@MRN').type('ProstateX-0000'); //Wait result list to be displayed cy.waitStudyList(); @@ -28,7 +28,7 @@ describe('OHIF Study List', function () { }); }); - it('searches Accession with exact string', function () { + it('searches Accession with exact string', function() { cy.get('@accessionNumber').type('fpcben98890'); //Wait result list to be displayed cy.waitStudyList(); @@ -38,17 +38,17 @@ describe('OHIF Study List', function () { }); }); - it('searches Modality with camel case', function () { + it('searches Modality with camel case', function() { cy.get('@modalities').type('Mr'); //Wait result list to be displayed cy.waitStudyList(); cy.get('@searchResult').should($list => { - expect($list.length).to.be.eq(18); + expect($list.length).to.be.eq(17); expect($list).to.contain('MR'); }); }); - it('searches Description with exact string', function () { + it('searches Description with exact string', function() { cy.get('@studyDescription').type('CHEST'); //Wait result list to be displayed cy.waitStudyList(); @@ -58,7 +58,7 @@ describe('OHIF Study List', function () { }); }); - it('changes rows per page and checks the study count', function () { + it('changes rows per page and checks the study count', function() { //Show rows per page options const pageRows = [25, 50, 100]; @@ -100,14 +100,14 @@ describe('OHIF Study List', function () { // }); }); - context('Tablet resolution', function () { - beforeEach(function () { + context('Tablet resolution', function() { + beforeEach(function() { cy.viewport(1000, 660); cy.openStudyList(); cy.initStudyListAliasesOnTablet(); }); - it('searches Patient Name with exact string', function () { + it('searches Patient Name with exact string', function() { cy.get('@patientNameOrMRN').type('Juno'); //Wait result list to be displayed cy.waitStudyList(); @@ -117,7 +117,7 @@ describe('OHIF Study List', function () { }); }); - it('searches MRN with with exact string', function () { + it('searches MRN with with exact string', function() { cy.get('@patientNameOrMRN').type('ProstateX-0000'); //Wait result list to be displayed cy.waitStudyList(); @@ -127,17 +127,17 @@ describe('OHIF Study List', function () { }); }); - it('searches Modality with exact string', function () { + it('searches Modality with exact string', function() { cy.get('@accessionModalityDescription').type('MR'); //Wait result list to be displayed cy.waitStudyList(); cy.get('@searchResult').should($list => { - expect($list.length).to.be.eq(18); + expect($list.length).to.be.eq(17); expect($list).to.contain('MR'); }); }); - it('searches Accession with exact string', function () { + it('searches Accession with exact string', function() { cy.get('@accessionModalityDescription').type('fpcben98890'); //Wait result list to be displayed cy.waitStudyList(); @@ -147,7 +147,7 @@ describe('OHIF Study List', function () { }); }); - it('searches Description with exact string', function () { + it('searches Description with exact string', function() { cy.get('@accessionModalityDescription').type('CHEST'); //Wait result list to be displayed cy.waitStudyList(); @@ -157,7 +157,7 @@ describe('OHIF Study List', function () { }); }); - it('changes rows per page and checks the study count', function () { + it('changes rows per page and checks the study count', function() { //Show rows per page options const pageRows = [25, 50, 100];