-
Notifications
You must be signed in to change notification settings - Fork 0
/
StudyListRouting.bundle.ff927011e19fe3e41406.js.map
1 lines (1 loc) · 97.5 KB
/
StudyListRouting.bundle.ff927011e19fe3e41406.js.map
1
{"version":3,"sources":["webpack:///./googleCloud/api/GoogleCloudApi.js","webpack:///./lib/localFileLoaders/fileLoader.js","webpack:///./lib/localFileLoaders/pdfFileLoader.js","webpack:///./lib/localFileLoaders/dicomFileLoader.js","webpack:///./lib/localFileLoaders/fileLoaderService.js","webpack:///./lib/filesToStudies.js","webpack:///./customHooks/usePrevious.js","webpack:///./googleCloud/utils/getServers.js","webpack:///./customHooks/useServer.js","webpack:///./googleCloud/utils/helpers.js","webpack:///./googleCloud/utils/CancellationToken.js","webpack:///./googleCloud/api/DicomUploadService.js","webpack:///./googleCloud/DicomUploader.js","webpack:///./googleCloud/DicomFileUploaderModal.js","webpack:///./googleCloud/ConnectedDicomFilesUploader.js","webpack:///./googleCloud/DicomStoreList.js","webpack:///./googleCloud/DicomStorePicker.js","webpack:///./googleCloud/DatasetsList.js","webpack:///./googleCloud/DatasetPicker.js","webpack:///./googleCloud/ProjectsList.js","webpack:///./googleCloud/ProjectPicker.js","webpack:///./googleCloud/LocationsList.js","webpack:///./googleCloud/LocationPicker.js","webpack:///./googleCloud/DatasetSelector.js","webpack:///./googleCloud/DicomStorePickerModal.js","webpack:///./googleCloud/ConnectedDicomStorePicker.js","webpack:///./studylist/StudyListRoute.js","webpack:///./studylist/ConnectedStudyList.js","webpack:///./studylist/StudyListRouting.js"],"names":["GoogleCloudApi","accessToken","console","error","this","project","location","dataset","dicomStore","urlBase","urlStr","config","params","url","URL","data","search","URLSearchParams","fetch","fetchConfig","response","json","status","nextPageToken","pageToken","doRequest","key","subPage","hasOwnProperty","concat","isError","message","projectId","urlBaseProject","locationId","Error","method","headers","Authorization","healthcareApiEndpoint","FileLoader","file","imageId","image","PDFFileLoader","cornerstoneWADOImageLoader","wadouri","loadFileRequest","getDefaultStudy","StudyInstanceUID","StudyDate","StudyTime","AccessionNumber","ReferringPhysicianName","PatientName","PatientID","PatientBirthdate","PatientSex","StudyId","StudyDescription","series","SeriesInstanceUID","SeriesDescription","SeriesNumber","instances","metadata","SOPInstanceUID","SOPClassUID","Rows","Columns","NumberOfFrames","InstanceNumber","getImageId","isLocalFile","metadataProvider","OHIF","cornerstone","DICOMFileLoader","dicomData","dcmjs","DicomMessage","readFile","DicomMetaDictionary","naturalizeDataset","dict","addInstance","_meta","namifyDataset","meta","e","getStudyFromDataset","PatientBirthDate","StudyID","instance","study","FileLoaderService","fileType","type","loader","getLoader","studies","groupBy","list","groupByKey","listKey","nonKeyCounter","reduce","acc","obj","push","Object","values","map","studyGroup","seriesGrouped","fileManager","add","loadFile","getDataset","getStudies","processFile","fileLoaderService","addFile","log","name","filesToStudies","files","processFilesPromises","Promise","all","groupSeries","flat","usePrevious","value","ref","useRef","useEffect","current","getServers","wadoUriRoot","qidoRoot","wadoRoot","imageRendering","thumbnailRendering","active","supportsFuzzyMatching","qidoSupportsIncludeField","isValidServer","server","isEqualServer","toCompare","serverLength","keys","length","toCompareLength","getActiveServer","servers","find","a","appConfig","enableGoogleCloudAdapter","pathUrl","getUrlBaseDicomWeb","GoogleCloudUtilServers","setServers","dispatch","useServerFromUrl","previousServers","activeServer","urlBasedServers","serverHasChanged","newServer","exists","some","bind","undefined","useServer","useSelector","state","useDispatch","useContext","AppContext","formatFileSize","size","n","Math","floor","pow","toFixed","httpErrorToStr","jsonResponse","JSON","parse","code","err","checkDicomFile","arrayBuffer","arr","Uint8Array","slice","Array","from","every","char","i","charCodeAt","CancellationToken","cancelled","uploadCallback","cancellationToken","CHUNK_SIZE","MAX_PARALLEL_JOBS","filesArray","parallelJobsCount","min","completed","processJob","resolve","reject","get","chunk","simpleUpload","forEach","fileId","client","getClient","loadedFile","content","storeInstances","datasets","reader","FileReader","onload","result","onerror","readAsArrayBuffer","func","retrieveAuthHeaderFunc","errorHandler","getHTTPErrorHandler","api","DICOMwebClient","DicomUploader","isCancelled","errorsCount","uploadedVolume","wholeVolumeStr","isFilesListHidden","timeLeft","uploadedList","totalCount","successfullyUploadedCount","lastFile","uploadContext","target","filesDict","fileDesc","id","path","webkitRelativePath","processed","processedInUI","wholeVolume","f","b","random","setState","dicomUploader","setRetrieveAuthHeaderFunction","props","retrieveAuthHeaderFunction","smartUpload","call","style","color","className","parseInt","errors","htmlFor","src","alt","onChange","uploadFiles","multiple","webkitdirectory","mozdirectory","percents","filesLeft","renderTableRow","Component","PropTypes","string","event","DicomFileUploaderModal","isOpen","onClose","t","UIModalService","servicesManager","services","Fragment","show","title","contentProps","propTypes","bool","isRequired","withTranslation","isActive","ConnectedDicomFileUploader","connect","DICOMWeb","getAuthorizationHeader","DicomStoreList","store","highlightedItem","onMouseEnter","onHighlightItem","onClick","onSelect","split","loading","stores","filter","loadingIcon","body","toLowerCase","includes","array","DicomStorePicker","locations","filterStr","setAccessToken","loadDicomStores","dicomStores","object","DatasetsList","DatasetPicker","loadDatasets","ProjectsList","projects","lowerCaseFilter","filteredProjects","ProjectPicker","loadProjects","LocationsList","LocationPicker","loadLocations","DatasetSelector","unloading","dicomStoreJson","parts","user","access_token","onProjectClick","onLocationClick","onDatasetClick","onProjectSelect","onLocationSelect","onDatasetSelect","onDicomStoreSelect","projectBreadcrumbs","canClose","DicomStorePickerModal","dicomstore","hide","ConnectedDicomStorePicker","oidc","UrlUtil","utils","urlUtil","StudyListRoute","history","studyListFunctionsEnabled","useTranslation","useState","fieldName","direction","sort","setSort","studyDateTo","studyDateFrom","modalities","patientNameOrId","accessionOrModalityOrDescription","allFields","filterValues","setFilterValues","setStudies","isSearchingForStudies","searchStatus","setSearchStatus","activeModalId","setActiveModalId","rowsPerPage","setRowsPerPage","pageNumber","setPageNumber","appContext","displaySize","useMedia","debouncedSort","useDebounce","debouncedFilters","getStudyList","warn","isFetching","fetchStudies","stringify","healthCareApiButtons","healthCareApiWindows","isModalOpen","listPath","RoutesUtil","paramString","isValidPath","pathname","replace","updateURL","padding","ConnectedDicomFilesUploader","WhiteLabelingContext","Consumer","whiteLabeling","UserManagerContext","userManager","ConnectedHeader","useLargeLogo","createLogoComponentFn","React","fontWeight","fontSize","onImport","isLoading","hasError","onSelectItem","studyInstanceUID","viewerPath","studyInstanceUIDs","onSort","sortFieldName","sortDirection","onFilterChange","studyListDateFilterNumDays","currentPage","nextPageFunc","prevPageFunc","onRowsPerPageChange","recordCount","filters","mappedFilters","ModalitiesInStudy","limit","offset","fuzzymatching","_fetchStudies","mappedStudies","sortedStudies","_sortStudies","numToTake","field","order","moment","isValid","format","fieldA","fieldB","toISOString","queryFiltersArray","firstSet","_getQueryFiltersForValue","secondSet","queryPromises","searchStudiesPromise","searchStudies","lotsOfStudies","arrayOfStudies","s","fields","queryFilters","assign","defaultProps","withRouter","ConnectedStudyList","StudyListRouting","routeMatch","match","routeLocation","seriesInstanceUIDs","queryString","getQueryFilters","shape"],"mappings":"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","file":"StudyListRouting.bundle.ff927011e19fe3e41406.js","sourcesContent":["class GoogleCloudApi {\n setAccessToken(accessToken) {\n if (!accessToken) console.error('Access token is empty');\n this.accessToken = accessToken;\n }\n\n get fetchConfig() {\n if (!this.accessToken) throw new Error('OIDC access_token is not set');\n return {\n method: 'GET',\n headers: {\n Authorization: 'Bearer ' + this.accessToken,\n },\n };\n }\n\n get urlBase() {\n return this.healthcareApiEndpoint || 'https://healthcare.googleapis.com/v1beta1';\n }\n\n set urlBase(url) {\n this.healthcareApiEndpoint = url;\n }\n\n get urlBaseProject() {\n return this.urlBase + `/projects`;\n }\n\n getUrlBaseDicomWeb(project, location, dataset, dicomStore) {\n return (\n this.urlBase +\n `/projects/${project}/locations/${location}/datasets/${dataset}/dicomStores/${dicomStore}/dicomWeb`\n );\n }\n\n getUrlPath(project, location, dataset, dicomStore) {\n `/projects/${project}/locations/${location}/datasets/${dataset}/dicomStores/${dicomStore}`;\n }\n\n async doRequest(urlStr, config = {}, params = {}) {\n const url = new URL(urlStr);\n let data = null;\n url.search = new URLSearchParams(params);\n\n try {\n const response = await fetch(url, { ...this.fetchConfig, config });\n try {\n data = await response.json();\n } catch (err) {}\n if (response.status >= 200 && response.status < 300 && data != null) {\n if (data.nextPageToken != null) {\n params.pageToken = data.nextPageToken;\n let subPage = await this.doRequest(urlStr, config, params);\n for (let key in data) {\n if (data.hasOwnProperty(key)) {\n data[key] = data[key].concat(subPage.data[key]);\n }\n }\n }\n return {\n isError: false,\n status: response.status,\n data,\n };\n } else {\n return {\n isError: true,\n status: response.status,\n message:\n (data && data.error && data.error.message) || 'Unknown error',\n };\n }\n } catch (err) {\n if (data && data.error) {\n return {\n isError: true,\n status: err.status,\n message: err.response.data.error.message || 'Unspecified error',\n };\n }\n return {\n isError: true,\n message: (err && err.message) || 'Oops! Something went wrong',\n };\n }\n }\n\n async loadProjects() {\n return this.doRequest(\n 'https://cloudresourcemanager.googleapis.com/v1/projects'\n );\n }\n\n async loadLocations(projectId) {\n return this.doRequest(`${this.urlBaseProject}/${projectId}/locations`);\n }\n\n async loadDatasets(projectId, locationId) {\n return this.doRequest(\n `${this.urlBaseProject}/${projectId}/locations/${locationId}/datasets`\n );\n }\n\n async loadDicomStores(dataset) {\n return this.doRequest(`${this.urlBase}/${dataset}/dicomStores`);\n }\n}\n\nexport default new GoogleCloudApi();\n","export default class FileLoader {\n fileType;\n loadFile(file, imageId) { }\n getDataset(image, imageId) { }\n getStudies(dataset, imageId) { }\n}\n","import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';\nimport FileLoader from './fileLoader';\n\nconst PDFFileLoader = new (class extends FileLoader {\n fileType = 'application/pdf';\n loadFile(file, imageId) {\n return cornerstoneWADOImageLoader.wadouri.loadFileRequest(imageId);\n }\n\n getDataset(image, imageId) {\n const dataset = {};\n dataset.imageId = image.imageId || imageId;\n return dataset;\n }\n\n getStudies(dataset, imageId) {\n return this.getDefaultStudy(imageId);\n }\n\n getDefaultStudy(imageId) {\n const study = {\n StudyInstanceUID: '',\n StudyDate: '',\n StudyTime: '',\n AccessionNumber: '',\n ReferringPhysicianName: '',\n PatientName: '',\n PatientID: '',\n PatientBirthdate: '',\n PatientSex: '',\n StudyId: '',\n StudyDescription: '',\n series: [\n {\n SeriesInstanceUID: '',\n SeriesDescription: '',\n SeriesNumber: '',\n instances: [\n {\n metadata: {\n SOPInstanceUID: '',\n SOPClassUID: '1.2.840.10008.5.1.4.1.1.104.1',\n Rows: '',\n Columns: '',\n NumberOfFrames: 0,\n InstanceNumber: 1,\n },\n getImageId: () => imageId,\n isLocalFile: true,\n },\n ],\n },\n ],\n };\n\n return study;\n }\n})();\n\nexport default PDFFileLoader;\n","import dcmjs from 'dcmjs';\nimport cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';\nimport FileLoader from './fileLoader';\nimport OHIF from '@ohif/core';\n\nconst metadataProvider = OHIF.cornerstone.metadataProvider;\n\nconst DICOMFileLoader = new (class extends FileLoader {\n fileType = 'application/dicom';\n loadFile(file, imageId) {\n return cornerstoneWADOImageLoader.wadouri.loadFileRequest(imageId);\n }\n\n getDataset(image, imageId) {\n let dataset = {};\n try {\n const dicomData = dcmjs.data.DicomMessage.readFile(image);\n\n dataset = dcmjs.data.DicomMetaDictionary.naturalizeDataset(\n dicomData.dict\n );\n\n metadataProvider.addInstance(dataset);\n\n dataset._meta = dcmjs.data.DicomMetaDictionary.namifyDataset(\n dicomData.meta\n );\n } catch (e) {\n console.error('Error reading dicom file', e);\n }\n // Set imageId on dataset to be consumed later on\n dataset.imageId = imageId;\n\n return dataset;\n }\n\n getStudies(dataset, imageId) {\n return this.getStudyFromDataset(dataset);\n }\n\n getStudyFromDataset(dataset = {}) {\n const {\n StudyInstanceUID,\n StudyDate,\n StudyTime,\n AccessionNumber,\n ReferringPhysicianName,\n PatientName,\n PatientID,\n PatientBirthDate,\n PatientSex,\n StudyID,\n StudyDescription,\n SeriesInstanceUID,\n SeriesDescription,\n SeriesNumber,\n imageId,\n } = dataset;\n\n const instance = {\n metadata: dataset,\n url: imageId,\n };\n\n const series = {\n SeriesInstanceUID: SeriesInstanceUID,\n SeriesDescription: SeriesDescription,\n SeriesNumber: SeriesNumber,\n instances: [instance],\n };\n\n const study = {\n StudyInstanceUID,\n StudyDate,\n StudyTime,\n AccessionNumber,\n ReferringPhysicianName,\n PatientName,\n PatientID,\n PatientBirthDate,\n PatientSex,\n StudyID,\n StudyDescription,\n /*\n TODO: in case necessary to uncomment this block, double check every property\n numberOfStudyRelatedSeries: NumberOfStudyRelatedSeries || DICOMWeb.getString(dataset['00201206']),\n numberOfStudyRelatedInstances: NumberOfStudyRelatedInstances || DICOMWeb.getString(dataset['00201208']),\n Modality: Modality || DICOMWeb.getString(dataset['00080060']),\n ModalitiesInStudy: ModalitiesInStudy || DICOMWeb.getString(dataset['00080061']),\n modalities:\n */\n series: [series],\n };\n\n return study;\n }\n})();\n\nexport default DICOMFileLoader;\n","import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';\nimport FileLoader from './fileLoader';\nimport PDFFileLoader from './pdfFileLoader';\nimport DICOMFileLoader from './dicomFileLoader';\n\nclass FileLoaderService extends FileLoader {\n fileType;\n loader;\n constructor(file) {\n super();\n const fileType = file && file.type;\n this.loader = this.getLoader(fileType);\n this.fileType = this.loader.fileType;\n }\n\n static groupSeries(studies) {\n const groupBy = (list, groupByKey, listKey) => {\n let nonKeyCounter = 1;\n\n return list.reduce((acc, obj) => {\n let key = obj[groupByKey];\n const list = obj[listKey];\n\n // in case key not found, group it using counter\n key = !!key ? key : '' + nonKeyCounter++;\n\n if (!acc[key]) {\n acc[key] = { ...obj };\n acc[key][listKey] = [];\n }\n\n acc[key][listKey].push(...list);\n\n return acc;\n }, {});\n };\n\n const studiesGrouped = Object.values(\n groupBy(studies, 'StudyInstanceUID', 'series')\n );\n\n const result = studiesGrouped.map(studyGroup => {\n const seriesGrouped = groupBy(\n studyGroup.series,\n 'SeriesInstanceUID',\n 'instances'\n );\n studyGroup.series = Object.values(seriesGrouped);\n\n return studyGroup;\n });\n\n return result;\n }\n\n addFile(file) {\n return cornerstoneWADOImageLoader.wadouri.fileManager.add(file);\n }\n\n loadFile(file, imageId) {\n return this.loader.loadFile(file, imageId);\n }\n\n getDataset(image, imageId) {\n return this.loader.getDataset(image, imageId);\n }\n\n getStudies(dataset, imageId) {\n return this.loader.getStudies(dataset, imageId);\n }\n\n getLoader(fileType) {\n if (fileType === 'application/pdf') {\n return PDFFileLoader;\n } else {\n // Default to dicom loader\n return DICOMFileLoader;\n }\n }\n}\n\nexport default FileLoaderService;\n","import FileLoaderService from './localFileLoaders/fileLoaderService';\n\nconst processFile = async file => {\n try {\n const fileLoaderService = new FileLoaderService(file);\n const imageId = fileLoaderService.addFile(file);\n const image = await fileLoaderService.loadFile(file, imageId);\n const dataset = await fileLoaderService.getDataset(image, imageId);\n const studies = await fileLoaderService.getStudies(dataset, imageId);\n\n return studies;\n } catch (error) {\n console.log(\n error.name,\n ':Error when trying to load and process local files:',\n error.message\n );\n }\n};\n\nexport default async function filesToStudies(files) {\n const processFilesPromises = files.map(processFile);\n const studies = await Promise.all(processFilesPromises);\n\n return FileLoaderService.groupSeries(studies.flat());\n}\n","import React, { useEffect, useRef } from 'react';\nexport default function usePrevious(value) {\n const ref = useRef();\n useEffect(() => {\n ref.current = value;\n }, [value]);\n\n return ref.current;\n}\n","const getServers = (data, name) => {\n const {\n wadoUriRoot,\n qidoRoot,\n wadoRoot,\n dataset = '',\n dicomStore = '',\n location = '',\n project = '',\n } = data;\n\n return [\n {\n name: name,\n dataset,\n dicomStore,\n location,\n project,\n imageRendering: 'wadors',\n thumbnailRendering: 'wadors',\n type: 'dicomWeb',\n active: true,\n wadoUriRoot,\n qidoRoot,\n wadoRoot,\n supportsFuzzyMatching: false,\n qidoSupportsIncludeField: false,\n },\n ];\n};\n\nconst isValidServer = server => {\n return (\n server &&\n !!server.dataset &&\n !!server.dicomStore &&\n !!server.location &&\n !!server.project\n );\n};\n\nconst isEqualServer = (server = {}, toCompare = {}) => {\n const serverLength = Object.keys(server).length;\n const toCompareLength = Object.keys(toCompare).length;\n\n if (!serverLength || !toCompareLength) {\n return false;\n }\n\n return (\n server.dataset === toCompare.dataset &&\n server.dataset === toCompare.dataset &&\n server.dicomStore === toCompare.dicomStore &&\n server.location === toCompare.location &&\n server.project === toCompare.project\n );\n};\n\nexport { getServers, isValidServer, isEqualServer };\n","import React, { useContext } from 'react';\nimport GoogleCloudApi from '../googleCloud/api/GoogleCloudApi';\nimport usePrevious from './usePrevious';\n\nimport * as GoogleCloudUtilServers from '../googleCloud/utils/getServers';\nimport { useSelector, useDispatch } from 'react-redux';\nimport isEqual from 'lodash.isequal';\n\n// Contexts\nimport AppContext from '../context/AppContext';\n\nconst getActiveServer = servers => {\n const isActive = a => a.active === true;\n\n return servers && servers.servers && servers.servers.find(isActive);\n};\n\nconst getServers = (appConfig, project, location, dataset, dicomStore) => {\n let servers = [];\n if (appConfig.enableGoogleCloudAdapter) {\n GoogleCloudApi.urlBase = appConfig.healthcareApiEndpoint;\n const pathUrl = GoogleCloudApi.getUrlBaseDicomWeb(\n project,\n location,\n dataset,\n dicomStore\n );\n const data = {\n project,\n location,\n dataset,\n dicomStore,\n wadoUriRoot: pathUrl,\n qidoRoot: pathUrl,\n wadoRoot: pathUrl,\n };\n servers = GoogleCloudUtilServers.getServers(data, dicomStore);\n if (!isValidServer(servers[0], appConfig)) {\n return;\n }\n }\n\n return servers;\n};\n\nconst isValidServer = (server, appConfig) => {\n if (appConfig.enableGoogleCloudAdapter) {\n return GoogleCloudUtilServers.isValidServer(server);\n }\n\n return !!server;\n};\n\nconst setServers = (dispatch, servers) => {\n const action = {\n type: 'SET_SERVERS',\n servers,\n };\n dispatch(action);\n};\n\nconst useServerFromUrl = (\n servers = [],\n previousServers,\n activeServer,\n urlBasedServers,\n appConfig,\n project,\n location,\n dataset,\n dicomStore\n) => {\n // update state from url available only when gcloud on\n if (!appConfig.enableGoogleCloudAdapter) {\n return false;\n }\n\n const serverHasChanged = previousServers !== servers && previousServers;\n\n // do not update from url. use state instead.\n if (serverHasChanged) {\n return false;\n }\n\n // if no valid urlbased servers\n if (!urlBasedServers || !urlBasedServers.length) {\n return false;\n } else if (!servers.length || !activeServer) {\n // no current valid server\n return true;\n }\n\n const newServer = urlBasedServers[0];\n\n let exists = servers.some(\n GoogleCloudUtilServers.isEqualServer.bind(undefined, newServer)\n );\n\n return !exists;\n};\n\nexport default function useServer({\n project,\n location,\n dataset,\n dicomStore,\n} = {}) {\n // Hooks\n const servers = useSelector(state => state && state.servers);\n const previousServers = usePrevious(servers);\n const dispatch = useDispatch();\n\n const { appConfig = {} } = useContext(AppContext);\n\n const activeServer = getActiveServer(servers);\n const urlBasedServers =\n getServers(appConfig, project, location, dataset, dicomStore) || [];\n const shouldUpdateServer = useServerFromUrl(\n servers.servers,\n previousServers,\n activeServer,\n urlBasedServers,\n appConfig,\n project,\n location,\n dataset,\n dicomStore\n );\n\n if (shouldUpdateServer) {\n setServers(dispatch, urlBasedServers);\n } else if (isValidServer(activeServer, appConfig)) {\n return activeServer;\n }\n}\n","export function formatFileSize(size) {\n if (size === 0) return '0 B';\n const n = Math.floor(Math.log(size) / Math.log(1024));\n return (\n (size / Math.pow(1024, n)).toFixed(2) * 1 +\n ' ' +\n ['B', 'kB', 'MB', 'GB', 'TB'][n]\n );\n}\n\nexport function httpErrorToStr(error) {\n if (!error) return 'Unknown error';\n if (error.response) {\n try {\n const jsonResponse = JSON.parse(error.response);\n if (\n jsonResponse.error &&\n jsonResponse.error.code &&\n jsonResponse.error.message\n )\n return jsonResponse.error.code + ' - ' + jsonResponse.error.message;\n } catch (err) {\n return error.response;\n }\n }\n return error.message || 'Unknown error.';\n}\n\n/* eslint-disable */\nexport function checkDicomFile(arrayBuffer) {\n if (arrayBuffer.length <= 132) return false;\n const arr = new Uint8Array(arrayBuffer.slice(128, 132));\n // bytes from 128 to 132 must be \"DICM\"\n return Array.from('DICM').every((char, i) => char.charCodeAt(0) === arr[i]);\n}\n","export default class CancellationToken {\n constructor() {\n this.cancelled = false;\n }\n\n get() {\n return this.cancelled;\n }\n\n set(value) {\n this.cancelled = value;\n }\n}\n","import { httpErrorToStr, checkDicomFile } from '../utils/helpers';\nimport { api } from 'dicomweb-client';\nimport { errorHandler } from '@ohif/core';\n\nclass DicomUploadService {\n async smartUpload(files, url, uploadCallback, cancellationToken) {\n const CHUNK_SIZE = 1; // Only one file per request is supported so far\n const MAX_PARALLEL_JOBS = 50; // FIXME: tune MAX_PARALLEL_JOBS number\n\n let filesArray = Array.from(files);\n if (filesArray.length === 0) {\n throw new Error('No files were provided.');\n }\n\n let parallelJobsCount = Math.min(filesArray.length, MAX_PARALLEL_JOBS);\n let completed = false;\n\n const processJob = async (resolve, reject) => {\n while (filesArray.length > 0) {\n if (cancellationToken.get()) return;\n let chunk = filesArray.slice(0, CHUNK_SIZE);\n filesArray = filesArray.slice(CHUNK_SIZE);\n let error = null;\n try {\n if (chunk.length > 1) throw new Error('Not implemented');\n if (chunk.length === 1) await this.simpleUpload(chunk[0], url);\n } catch (err) {\n // It looks like a stupid bug of Babel that err is not an actual Exception object\n error = httpErrorToStr(err);\n }\n chunk.forEach(file => uploadCallback(file.fileId, error));\n if (!completed && filesArray.length === 0) {\n completed = true;\n resolve();\n return;\n }\n }\n };\n\n await new Promise(resolve => {\n for (let i = 0; i < parallelJobsCount; i++) {\n processJob(resolve);\n }\n });\n }\n\n async simpleUpload(file, url) {\n const client = this.getClient(url);\n const loadedFile = await this.readFile(file);\n const content = loadedFile.content;\n if (!checkDicomFile(content))\n throw new Error('This is not a valid DICOM file.');\n\n await client.storeInstances({ datasets: [content] });\n }\n\n readFile(file) {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => {\n resolve({\n name: file.name,\n size: file.size,\n type: file.type,\n content: reader.result,\n });\n };\n reader.onerror = error => reject(error);\n reader.readAsArrayBuffer(file);\n });\n }\n\n setRetrieveAuthHeaderFunction(func) {\n this.retrieveAuthHeaderFunc = func;\n }\n\n getClient(url) {\n const headers = this.retrieveAuthHeaderFunc();\n const errorInterceptor = errorHandler.getHTTPErrorHandler();\n\n // TODO: a bit weird we are creating a new dicomweb client instance for every upload\n return new api.DICOMwebClient({\n url,\n headers,\n });\n }\n}\n\nexport default new DicomUploadService();\n","import React, { Component } from 'react';\nimport PropTypes from 'prop-types';\nimport { formatFileSize } from './utils/helpers';\nimport CancellationToken from './utils/CancellationToken';\nimport dicomUploader from './api/DicomUploadService';\nimport './DicomUploader.css';\n\nexport default class DicomUploader extends Component {\n state = {\n status: 'Upload',\n isCancelled: false,\n errorsCount: 0,\n files: null,\n uploadedVolume: null,\n wholeVolumeStr: null,\n isFilesListHidden: true,\n timeLeft: null,\n uploadedList: null,\n totalCount: 0,\n successfullyUploadedCount: 0,\n lastFile: '',\n uploadContext: null, // this is probably not needed, but we use this variable to distinguish between different downloads\n };\n\n static propTypes = {\n id: PropTypes.string,\n event: PropTypes.string,\n url: PropTypes.string,\n retrieveAuthHeaderFunction: PropTypes.func,\n };\n\n filesLeft() {\n return (\n this.state.uploadedList.length + ' of ' + this.state.totalCount + ' files'\n );\n }\n\n volumeLeft() {\n let left = formatFileSize(this.state.uploadedVolume);\n return left + ' of ' + this.state.wholeVolumeStr;\n }\n\n percents() {\n return parseInt(\n (100 * this.state.uploadedList.length) /\n Object.keys(this.state.files).length\n );\n }\n\n isFinished() {\n return (\n this.state.isCancelled ||\n Object.keys(this.state.files).length === this.state.uploadedList.length\n );\n }\n\n errorsMessage() {\n const errors = this.state.errorsCount === 1 ? ' error' : ' errors';\n return (\n this.state.errorsCount + errors + ' while uploading, click for more info'\n );\n }\n\n uploadFiles = files => {\n const filesArray = Array.from(files.target.files);\n const filesDict = {};\n filesArray.forEach((file, i) => {\n const fileDesc = {\n id: i,\n name: file.name,\n path: file.webkitRelativePath || file.name,\n size: file.size,\n error: null,\n processed: false,\n processedInUI: false,\n };\n filesDict[i] = fileDesc;\n file.fileId = i;\n });\n const wholeVolume = filesArray.map(f => f.size).reduce((a, b) => a + b);\n const uploadContext = Math.random();\n this.setState({\n status: 'Uploading...',\n files: filesDict,\n uploadedList: [],\n uploadedVolume: 0,\n lastFile: filesArray[0].name,\n totalCount: filesArray.length,\n wholeVolumeStr: formatFileSize(wholeVolume),\n uploadContext: uploadContext,\n cancellationToken: new CancellationToken(),\n });\n const cancellationToken = new CancellationToken();\n const uploadCallback = (fileId, error) =>\n uploadContext === this.state.uploadContext &&\n this.uploadCallback.call(this, fileId, error);\n\n dicomUploader.setRetrieveAuthHeaderFunction(this.props.retrieveAuthHeaderFunction);\n\n dicomUploader.smartUpload(\n files.target.files,\n this.props.url,\n uploadCallback,\n cancellationToken\n );\n };\n\n uploadCallback(fileId, error) {\n const file = this.state.files[fileId];\n file.processed = true;\n if (!error) {\n let uploadedVolume = this.state.uploadedVolume + file.size;\n this.setState({ uploadedVolume });\n } else {\n file.error = error;\n this.setState({ errorsCount: this.state.errorsCount + 1 });\n }\n this.setState({ lastFile: file.name });\n let uploadedList = this.state.uploadedList;\n uploadedList.push(file);\n this.setState({ uploadedList });\n }\n\n renderTableRow = file => {\n let error = null;\n if (file.error !== null) {\n error = <p style={{ color: 'red' }}>{file.error}</p>;\n }\n return (\n <tr key={file.id}>\n <td className=\"project\">\n {file.name} {error}\n </td>\n </tr>\n );\n };\n\n render() {\n if (this.state.files === null) {\n return (\n <div className=\"dicom-uploader\">\n <div className=\"button\">\n <label htmlFor=\"file\">\n <img src=\"./assets/Button_File.svg\" alt=\"upload file\"></img>\n </label>\n <input\n className=\"invisible-input\"\n onChange={this.uploadFiles}\n type=\"file\"\n id=\"file\"\n multiple\n />\n </div>\n\n <div className=\"button\">\n <label htmlFor=\"folder\">\n <img src=\"./assets/Button_Folder.svg\" alt=\"upload folder\"></img>\n </label>\n <input\n className=\"invisible-input\"\n type=\"file\"\n onChange={this.uploadFiles}\n id=\"folder\"\n webkitdirectory=\"true\"\n mozdirectory=\"true\"\n multiple\n />\n </div>\n </div>\n );\n }\n\n return (\n <table id=\"tblProjectList\" className=\"table noselect\">\n <thead>\n <tr>\n <th className=\"table-header\">\n {this.percents()}% {this.filesLeft()}\n </th>\n </tr>\n </thead>\n <tbody id=\"ProjectList\">\n {this.state.uploadedList.map(this.renderTableRow)}\n </tbody>\n </table>\n );\n }\n}\n","import React, { Component } from 'react';\nimport PropTypes from 'prop-types';\nimport DicomUploader from './DicomUploader';\nimport { withTranslation } from 'react-i18next';\nimport { servicesManager } from './../App.js';\n\nfunction DicomFileUploaderModal({\n isOpen = false,\n onClose,\n url,\n retrieveAuthHeaderFunction,\n t,\n }) {\n const { UIModalService } = servicesManager.services;\n\n const showDicomStorePickerModal = () => {\n if (!UIModalService) {\n return\n }\n\n UIModalService.show({\n content: DicomUploader,\n title: t('Upload DICOM Files'),\n contentProps: {\n url,\n retrieveAuthHeaderFunction\n },\n onClose,\n });\n };\n\n return (\n <React.Fragment>{isOpen && showDicomStorePickerModal()}</React.Fragment>\n );\n}\n\nDicomFileUploaderModal.propTypes = {\n isOpen: PropTypes.bool.isRequired,\n retrieveAuthHeaderFunction: PropTypes.func.isRequired,\n onClose: PropTypes.func,\n url: PropTypes.string,\n};\n\nexport default withTranslation('Common')(DicomFileUploaderModal);\n","import { connect } from 'react-redux';\nimport DicomFileUploaderModal from './DicomFileUploaderModal.js';\nimport OHIF from '@ohif/core';\n\nconst isActive = a => a.active === true;\n\nconst mapStateToProps = state => {\n const activeServer = state.servers.servers.find(isActive);\n\n // TODO: Not sure I like this approach since it means we are recreating\n // this function every time redux changes\n const retrieveAuthHeaderFunction = () =>\n OHIF.DICOMWeb.getAuthorizationHeader(activeServer);\n\n return {\n retrieveAuthHeaderFunction,\n url: activeServer && activeServer.qidoRoot,\n };\n};\n\nconst ConnectedDicomFileUploader = connect(\n mapStateToProps,\n null\n)(DicomFileUploaderModal);\n\nexport default ConnectedDicomFileUploader;\n","import React, { Component } from 'react';\nimport PropTypes from 'prop-types';\nimport './googleCloud.css';\nimport { withTranslation } from 'react-i18next';\nimport { Icon } from '@ohif/ui';\n\nclass DicomStoreList extends Component {\n state = {\n search: '',\n };\n\n static propTypes = {\n stores: PropTypes.array,\n loading: PropTypes.bool.isRequired,\n error: PropTypes.string,\n onSelect: PropTypes.func,\n };\n\n static defaultProps = {\n loading: true,\n };\n\n renderTableRow = store => {\n return (\n <tr\n key={store.name}\n className={\n this.state.highlightedItem === store.name\n ? 'noselect active'\n : 'noselect'\n }\n onMouseEnter={() => {\n this.onHighlightItem(store.name);\n }}\n onClick={() => {\n this.props.onSelect(store);\n }}\n >\n <td className=\"project\">{store.name.split('/')[7]}</td>\n </tr>\n );\n };\n\n onHighlightItem(store) {\n this.setState({ highlightedItem: store });\n }\n\n render() {\n const { loading, stores, filter, error } = this.props;\n\n if (error) {\n return <p>{error}</p>;\n }\n\n const loadingIcon = (\n <Icon name=\"circle-notch\" className=\"loading-icon-spin loading-icon\" />\n );\n\n if (loading) {\n return loadingIcon;\n }\n\n const body = (\n <tbody id=\"StoreList\">{\n stores.filter(store => (store.name.split('/')[7].toLowerCase().includes(filter.toLowerCase()) || filter==\"\")).map(this.renderTableRow)}\n </tbody>\n );\n\n return (\n <table id=\"tblStoreList\" className=\"gcp-table table noselect\">\n <thead>\n <tr>\n <th>{this.props.t('DICOM Store')}</th>\n </tr>\n </thead>\n {stores && body}\n </table>\n );\n }\n}\n\nexport default withTranslation('Common')(DicomStoreList);\n","import React, { Component } from 'react';\nimport PropTypes from 'prop-types';\nimport api from './api/GoogleCloudApi';\nimport DicomStoreList from './DicomStoreList';\nimport './googleCloud.css';\n\nexport default class DicomStorePicker extends Component {\n state = {\n error: null,\n loading: true,\n stores: [],\n locations: [],\n filterStr: '',\n };\n\n static propTypes = {\n dataset: PropTypes.object,\n onSelect: PropTypes.func,\n accessToken: PropTypes.string.isRequired,\n };\n\n async componentDidMount() {\n api.setAccessToken(this.props.accessToken);\n\n const response = await api.loadDicomStores(this.props.dataset.name);\n\n if (response.isError) {\n this.setState({\n error: response.message,\n });\n\n return;\n }\n\n this.setState({\n stores: response.data.dicomStores || [],\n loading: false,\n });\n }\n\n render() {\n const { stores, loading, error, filterStr } = this.state;\n const { onSelect } = this.props;\n\n return (\n <div>\n <input\n className=\"form-control gcp-input\"\n type=\"text\"\n value={filterStr}\n onChange={e => this.setState({ filterStr: e.target.value })}\n />\n <DicomStoreList\n stores={stores}\n loading={loading}\n error={error}\n filter={filterStr}\n onSelect={onSelect}\n />\n </div>\n );\n }\n}\n","import React, { Component } from 'react';\nimport PropTypes from 'prop-types';\nimport './googleCloud.css';\nimport { withTranslation } from 'react-i18next';\nimport { Icon } from '@ohif/ui';\n\nclass DatasetsList extends Component {\n state = {\n search: '',\n };\n\n static propTypes = {\n datasets: PropTypes.array,\n loading: PropTypes.bool,\n error: PropTypes.string,\n onSelect: PropTypes.func,\n };\n\n static defaultProps = {\n loading: true,\n };\n\n renderTableRow = dataset => {\n return (\n <tr\n key={dataset.name}\n className={\n this.state.highlightedItem === dataset.name\n ? 'noselect active'\n : 'noselect'\n }\n onMouseEnter={() => {\n this.onHighlightItem(dataset.name);\n }}\n onClick={() => {\n this.props.onSelect(dataset);\n }}\n >\n <td>{dataset.name.split('/')[5]}</td>\n </tr>\n );\n };\n\n onHighlightItem(dataset) {\n this.setState({ highlightedItem: dataset });\n }\n\n render() {\n const { loading, datasets, filter, error } = this.props;\n \n if (error) {\n return <p>{error}</p>;\n }\n\n const loadingIcon = (\n <Icon name=\"circle-notch\" className=\"loading-icon-spin loading-icon\" />\n );\n\n if (loading) {\n return loadingIcon;\n }\n\n const body = (\n <tbody id=\"DatasetList\">\n {datasets.filter(dataset => (dataset.name.split('/')[5].toLowerCase().includes(filter.toLowerCase()) || filter==\"\")).map(this.renderTableRow)}\n </tbody>\n );\n\n return (\n <table id=\"tblDatasetList\" className=\"gcp-table table noselect\">\n <thead>\n <tr>\n <th>{this.props.t('Dataset')}</th>\n </tr>\n </thead>\n {datasets && body}\n </table>\n );\n }\n}\n\nexport default withTranslation('Common')(DatasetsList);\n","import React, { Component } from 'react';\nimport PropTypes from 'prop-types';\nimport api from './api/GoogleCloudApi';\nimport DatasetsList from './DatasetsList';\nimport './googleCloud.css';\n\nexport default class DatasetPicker extends Component {\n state = {\n error: null,\n loading: true,\n datasets: [],\n filterStr: '',\n };\n\n static propTypes = {\n project: PropTypes.object,\n location: PropTypes.object,\n onSelect: PropTypes.func,\n accessToken: PropTypes.string,\n };\n\n async componentDidMount() {\n api.setAccessToken(this.props.accessToken);\n\n const response = await api.loadDatasets(\n this.props.project.projectId,\n this.props.location.locationId\n );\n\n if (response.isError) {\n this.setState({\n error: response.message,\n });\n\n return;\n }\n\n this.setState({\n datasets: response.data.datasets || [],\n loading: false,\n });\n }\n\n render() {\n const { datasets, loading, error, filterStr } = this.state;\n const { onSelect } = this.props;\n return (\n <div>\n <input\n className=\"form-control gcp-input\"\n type=\"text\"\n value={filterStr}\n onChange={e => this.setState({ filterStr: e.target.value })}\n />\n <DatasetsList\n datasets={datasets}\n loading={loading}\n error={error}\n filter={filterStr}\n onSelect={onSelect}\n />\n </div>\n );\n }\n}\n","import React, { Component } from 'react';\nimport PropTypes from 'prop-types';\nimport './googleCloud.css';\nimport { withTranslation } from 'react-i18next';\nimport { Icon } from '@ohif/ui';\n\nclass ProjectsList extends Component {\n state = {\n search: '',\n highlightedItem: null,\n };\n\n static propTypes = {\n projects: PropTypes.array,\n loading: PropTypes.bool.isRequired,\n error: PropTypes.string,\n onSelect: PropTypes.func.isRequired,\n t: PropTypes.func,\n };\n\n static defaultProps = {\n loading: true,\n };\n\n renderTableRow = project => {\n return (\n <tr\n key={project.projectId}\n className={\n this.state.highlightedItem === project.projectId\n ? 'noselect active'\n : 'noselect'\n }\n onMouseEnter={() => {\n this.onHighlightItem(project.projectId);\n }}\n onClick={() => {\n this.onHighlightItem(project.projectId);\n this.props.onSelect(project);\n }}\n >\n <td>{project.name}</td>\n <td>{project.projectId}</td>\n </tr>\n );\n };\n\n onHighlightItem(project) {\n this.setState({ highlightedItem: project });\n }\n\n render() {\n const { loading, projects, filter, error } = this.props;\n\n if (error) {\n return <p>{error}</p>;\n }\n\n const loadingIcon = (\n <Icon name=\"circle-notch\" className=\"loading-icon-spin loading-icon\" />\n );\n\n if (loading) {\n return loadingIcon;\n }\n\n const lowerCaseFilter = filter.toLowerCase();\n const filteredProjects = projects.filter(project => \n typeof project.name === 'string' &&\n (filter === \"\" || project.name.toLowerCase().includes(lowerCaseFilter))\n );\n\n const body = (\n <tbody id=\"ProjectList\">\n {\n filteredProjects.map(this.renderTableRow)\n }\n </tbody>\n );\n\n return (\n <table id=\"tblProjectList\" className=\"gcp-table table noselect\">\n <thead>\n <tr>\n <th>{this.props.t('Project')}</th>\n <th>{this.props.t('ID')}</th>\n </tr>\n </thead>\n {projects && body}\n </table>\n );\n }\n}\n\nexport default withTranslation('Common')(ProjectsList);\n","import React, { Component } from 'react';\nimport PropTypes from 'prop-types';\nimport api from './api/GoogleCloudApi';\nimport ProjectsList from './ProjectsList';\nimport './googleCloud.css';\n\nexport default class ProjectPicker extends Component {\n state = {\n error: null,\n loading: true,\n projects: [],\n };\n\n static propTypes = {\n onSelect: PropTypes.func,\n accessToken: PropTypes.string,\n };\n\n async componentDidMount() {\n api.setAccessToken(this.props.accessToken);\n const response = await api.loadProjects();\n\n if (response.isError) {\n this.setState({\n error: response.message,\n });\n\n return;\n }\n\n this.setState({\n projects: response.data.projects || [],\n filterStr: '',\n loading: false,\n });\n }\n\n render() {\n const { projects, loading, filterStr, error } = this.state;\n const { onSelect } = this.props;\n return (\n <div>\n <input\n className=\"form-control gcp-input\"\n type=\"text\"\n value={filterStr}\n onChange={e => this.setState({ filterStr: e.target.value })}\n />\n <ProjectsList\n projects={projects}\n loading={loading}\n filter={filterStr}\n error={error}\n onSelect={onSelect}\n />\n </div>\n );\n }\n}\n","import React, { Component } from 'react';\nimport PropTypes from 'prop-types';\nimport './googleCloud.css';\nimport { withTranslation } from 'react-i18next';\nimport { Icon } from '@ohif/ui';\n\nclass LocationsList extends Component {\n state = {\n search: '',\n };\n\n static propTypes = {\n locations: PropTypes.array,\n loading: PropTypes.bool.isRequired,\n error: PropTypes.string,\n onSelect: PropTypes.func,\n };\n\n static defaultProps = {\n loading: true,\n };\n\n renderTableRow = location => {\n return (\n <tr\n key={location.locationId}\n className={\n this.state.highlightedItem === location.locationId\n ? 'noselect active'\n : 'noselect'\n }\n onMouseEnter={() => {\n this.onHighlightItem(location.locationId);\n }}\n onClick={() => {\n this.props.onSelect(location);\n }}\n >\n <td>{location.name.split('/')[3]}</td>\n </tr>\n );\n };\n\n onHighlightItem(locationId) {\n this.setState({ highlightedItem: locationId });\n }\n\n render() {\n const { loading, locations, filter, error } = this.props;\n\n if (error) {\n return <p>{error}</p>;\n }\n\n const loadingIcon = (\n <Icon name=\"circle-notch\" className=\"loading-icon-spin loading-icon\" />\n );\n\n if (loading) {\n return loadingIcon;\n }\n\n const body = (\n <tbody id=\"LocationList\">\n {locations.filter(location => (location.name.split('/')[3].toLowerCase().includes(filter.toLowerCase()) || filter==\"\")).map(this.renderTableRow)}\n </tbody>\n );\n\n return (\n <table id=\"tblLocationList\" className=\"gcp-table table noselect\">\n <thead>\n <tr>\n <th>{this.props.t('Location')}</th>\n </tr>\n </thead>\n {locations && body}\n </table>\n );\n }\n}\n\nexport default withTranslation('Common')(LocationsList);\n","import React, { Component } from 'react';\nimport PropTypes from 'prop-types';\nimport api from './api/GoogleCloudApi';\nimport LocationsList from './LocationsList';\nimport './googleCloud.css';\n\nexport default class LocationPicker extends Component {\n state = {\n error: null,\n loading: true,\n locations: [],\n filterStr: '',\n };\n\n static propTypes = {\n project: PropTypes.object,\n onSelect: PropTypes.func,\n accessToken: PropTypes.string,\n };\n\n async componentDidMount() {\n api.setAccessToken(this.props.accessToken);\n\n const response = await api.loadLocations(this.props.project.projectId);\n\n if (response.isError) {\n this.setState({\n error: response.message,\n });\n\n return;\n }\n\n this.setState({\n locations: response.data.locations || [],\n loading: false,\n });\n }\n\n render() {\n const { locations, loading, error, filterStr } = this.state;\n const { onSelect } = this.props;\n return (\n <div>\n <input\n className=\"form-control gcp-input\"\n type=\"text\"\n value={filterStr}\n onChange={e => this.setState({ filterStr: e.target.value })}\n />\n <LocationsList\n locations={locations}\n loading={loading}\n error={error}\n filter={filterStr}\n onSelect={onSelect}\n />\n </div>\n );\n }\n}\n","import React, { Component } from 'react';\nimport PropTypes from 'prop-types';\nimport { withTranslation } from 'react-i18next';\nimport DicomStorePicker from './DicomStorePicker';\nimport DatasetPicker from './DatasetPicker';\nimport ProjectPicker from './ProjectPicker';\nimport LocationPicker from './LocationPicker';\nimport GoogleCloudApi from './api/GoogleCloudApi';\nimport './googleCloud.css';\n\nclass DatasetSelector extends Component {\n state = {\n project: null,\n location: null,\n dataset: null,\n unloading: false,\n };\n\n static propTypes = {\n id: PropTypes.string,\n event: PropTypes.string,\n user: PropTypes.object,\n canClose: PropTypes.string,\n setServers: PropTypes.func.isRequired,\n };\n\n onProjectSelect = project => {\n this.setState({\n project,\n });\n };\n\n onLocationSelect = location => {\n this.setState({\n location,\n });\n };\n\n onDatasetSelect = dataset => {\n this.setState({\n dataset,\n });\n };\n\n onProjectClick = () => {\n this.setState({\n dataset: null,\n location: null,\n project: null,\n });\n };\n\n onLocationClick = () => {\n this.setState({\n dataset: null,\n location: null,\n });\n };\n\n onDatasetClick = () => {\n this.setState({\n dataset: null,\n });\n };\n\n onDicomStoreSelect = dicomStoreJson => {\n const dicomStore = dicomStoreJson.name;\n const parts = dicomStore.split('/');\n const result = {\n wadoUriRoot: GoogleCloudApi.urlBase + `/${dicomStore}/dicomWeb`,\n qidoRoot: GoogleCloudApi.urlBase + `/${dicomStore}/dicomWeb`,\n wadoRoot: GoogleCloudApi.urlBase + `/${dicomStore}/dicomWeb`,\n project: parts[1],\n location: parts[3],\n dataset: parts[5],\n dicomStore: parts[7],\n };\n this.props.setServers(result);\n };\n\n render() {\n const accessToken = this.props.user.access_token;\n\n const { project, location, dataset } = this.state;\n const {\n onProjectClick,\n onLocationClick,\n onDatasetClick,\n onProjectSelect,\n onLocationSelect,\n onDatasetSelect,\n onDicomStoreSelect,\n } = this;\n\n let projectBreadcrumbs = (\n <div className=\"gcp-picker--path\">\n <span>{this.props.t('Select a Project')}</span>\n </div>\n );\n\n if (project) {\n projectBreadcrumbs = (\n <div className=\"gcp-picker--path\">\n <span onClick={onProjectClick}>{project.name}</span>\n {project && location && (\n <span onClick={onLocationClick}>\n {' '}\n -> {location.name.split('/')[3]}\n </span>\n )}\n {project && location && dataset && (\n <span onClick={onDatasetClick}>\n {' '}\n -> {dataset.name.split('/')[5]}\n </span>\n )}\n </div>\n );\n }\n\n return (\n <>\n {projectBreadcrumbs}\n {!project && (\n <ProjectPicker accessToken={accessToken} onSelect={onProjectSelect} />\n )}\n\n {project && !location && (\n <LocationPicker\n accessToken={accessToken}\n project={project}\n onSelect={onLocationSelect}\n />\n )}\n {project && location && !dataset && (\n <DatasetPicker\n accessToken={accessToken}\n project={project}\n location={location}\n onSelect={onDatasetSelect}\n />\n )}\n {project && location && dataset && (\n <DicomStorePicker\n accessToken={accessToken}\n dataset={dataset}\n onSelect={onDicomStoreSelect}\n />\n )}\n </>\n );\n }\n}\n\nexport default withTranslation('Common')(DatasetSelector);\n","import React from 'react';\nimport PropTypes from 'prop-types';\nimport DatasetSelector from './DatasetSelector';\nimport './googleCloud.css';\nimport { withTranslation } from 'react-i18next';\nimport * as GoogleCloudUtilServers from './utils/getServers';\n\nimport { servicesManager } from './../App.js';\n\nfunction DicomStorePickerModal({\n isOpen = false,\n setServers,\n onClose,\n user,\n url,\n t,\n}) {\n const { UIModalService } = servicesManager.services;\n\n const showDicomStorePickerModal = () => {\n const handleEvent = data => {\n const servers = GoogleCloudUtilServers.getServers(data, data.dicomstore);\n setServers(servers);\n\n // Force auto close\n UIModalService.hide();\n onClose();\n };\n\n if (UIModalService) {\n UIModalService.show({\n content: DatasetSelector,\n title: t('Google Cloud Healthcare API'),\n contentProps: {\n setServers: handleEvent,\n user,\n url,\n },\n onClose\n });\n }\n };\n\n return (\n <React.Fragment>{isOpen && showDicomStorePickerModal()}</React.Fragment>\n );\n}\n\nDicomStorePickerModal.propTypes = {\n isOpen: PropTypes.bool.isRequired,\n setServers: PropTypes.func.isRequired,\n onClose: PropTypes.func,\n user: PropTypes.object.isRequired,\n url: PropTypes.string,\n};\n\nexport default withTranslation('Common')(DicomStorePickerModal);\n","import { connect } from 'react-redux';\nimport DicomStorePickerModal from './DicomStorePickerModal.js';\n\nconst isActive = a => a.active === true;\n\nconst mapStateToProps = state => {\n const activeServer = state.servers.servers.find(isActive);\n\n return {\n user: state.oidc && state.oidc.user,\n url: activeServer && activeServer.qidoRoot,\n };\n};\n\nconst mapDispatchToProps = dispatch => {\n return {\n setServers: servers => {\n const action = {\n type: 'SET_SERVERS',\n servers,\n };\n dispatch(action);\n },\n };\n};\n\nconst ConnectedDicomStorePicker = connect(\n mapStateToProps,\n mapDispatchToProps\n)(DicomStorePickerModal);\n\nexport default ConnectedDicomStorePicker;\n","import React, { useState, useEffect, useContext } from 'react';\nimport PropTypes from 'prop-types';\nimport OHIF from '@ohif/core';\nimport { withRouter } from 'react-router-dom';\nimport { useTranslation } from 'react-i18next';\nimport {\n StudyList,\n PageToolbar,\n TablePagination,\n useDebounce,\n useMedia,\n} from '@ohif/ui';\nimport ConnectedHeader from '../connectedComponents/ConnectedHeader.js';\nimport * as RoutesUtil from '../routes/routesUtil';\nimport moment from 'moment';\nimport ConnectedDicomFilesUploader from '../googleCloud/ConnectedDicomFilesUploader';\nimport ConnectedDicomStorePicker from '../googleCloud/ConnectedDicomStorePicker';\nimport filesToStudies from '../lib/filesToStudies.js';\n\n// Contexts\nimport UserManagerContext from '../context/UserManagerContext';\nimport WhiteLabelingContext from '../context/WhiteLabelingContext';\nimport AppContext from '../context/AppContext';\n\nconst { urlUtil: UrlUtil } = OHIF.utils;\n\nfunction StudyListRoute(props) {\n const { history, server, user, studyListFunctionsEnabled } = props;\n const [t] = useTranslation('Common');\n // ~~ STATE\n const [sort, setSort] = useState({\n fieldName: 'PatientName',\n direction: 'desc',\n });\n const [filterValues, setFilterValues] = useState({\n studyDateTo: null,\n studyDateFrom: null,\n PatientName: '',\n PatientID: '',\n AccessionNumber: '',\n StudyDate: '',\n modalities: '',\n StudyDescription: '',\n //\n patientNameOrId: '',\n accessionOrModalityOrDescription: '',\n //\n allFields: '',\n });\n const [studies, setStudies] = useState([]);\n const [searchStatus, setSearchStatus] = useState({\n isSearchingForStudies: false,\n error: null,\n });\n const [activeModalId, setActiveModalId] = useState(null);\n const [rowsPerPage, setRowsPerPage] = useState(25);\n const [pageNumber, setPageNumber] = useState(0);\n const appContext = useContext(AppContext);\n // ~~ RESPONSIVE\n const displaySize = useMedia(\n [\n '(min-width: 1750px)',\n '(min-width: 1000px) and (max-width: 1749px)',\n '(max-width: 999px)',\n ],\n ['large', 'medium', 'small'],\n 'small'\n );\n // ~~ DEBOUNCED INPUT\n const debouncedSort = useDebounce(sort, 200);\n const debouncedFilters = useDebounce(filterValues, 250);\n\n // Google Cloud Adapter for DICOM Store Picking\n const { appConfig = {} } = appContext;\n const isGoogleCHAIntegrationEnabled =\n !server && appConfig.enableGoogleCloudAdapter;\n if (isGoogleCHAIntegrationEnabled && activeModalId !== 'DicomStorePicker') {\n setActiveModalId('DicomStorePicker');\n }\n\n // Called when relevant state/props are updated\n // Watches filters and sort, debounced\n useEffect(\n () => {\n const fetchStudies = async () => {\n try {\n setSearchStatus({ error: null, isSearchingForStudies: true });\n\n const response = await getStudyList(\n server,\n debouncedFilters,\n debouncedSort,\n rowsPerPage,\n pageNumber,\n displaySize\n );\n\n setStudies(response);\n setSearchStatus({ error: null, isSearchingForStudies: false });\n } catch (error) {\n console.warn(error);\n setSearchStatus({ error: true, isFetching: false });\n }\n };\n\n if (server) {\n fetchStudies();\n }\n },\n // TODO: Can we update studies directly?\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [\n debouncedFilters,\n debouncedSort,\n rowsPerPage,\n pageNumber,\n displaySize,\n server,\n ]\n );\n\n // TODO: Update Server\n // if (this.props.server !== prevProps.server) {\n // this.setState({\n // modalComponentId: null,\n // searchData: null,\n // studies: null,\n // });\n // }\n\n const onDrop = async acceptedFiles => {\n try {\n const studiesFromFiles = await filesToStudies(acceptedFiles);\n setStudies(studiesFromFiles);\n } catch (error) {\n setSearchStatus({ isSearchingForStudies: false, error });\n }\n };\n\n if (searchStatus.error) {\n return <div>Error: {JSON.stringify(searchStatus.error)}</div>;\n } else if (studies === [] && !activeModalId) {\n return <div>Loading...</div>;\n }\n\n let healthCareApiButtons = null;\n let healthCareApiWindows = null;\n\n if (appConfig.enableGoogleCloudAdapter) {\n const isModalOpen = activeModalId === 'DicomStorePicker';\n updateURL(isModalOpen, appConfig, server, history);\n\n healthCareApiWindows = (\n <ConnectedDicomStorePicker\n isOpen={activeModalId === 'DicomStorePicker'}\n onClose={() => setActiveModalId(null)}\n />\n );\n\n healthCareApiButtons = (\n <div\n className=\"form-inline btn-group pull-right\"\n style={{ padding: '20px' }}\n >\n <button\n className=\"btn btn-primary\"\n onClick={() => setActiveModalId('DicomStorePicker')}\n >\n {t('Change DICOM Store')}\n </button>\n </div>\n );\n }\n\n function handleSort(fieldName) {\n let sortFieldName = fieldName;\n let sortDirection = 'asc';\n\n if (fieldName === sort.fieldName) {\n if (sort.direction === 'asc') {\n sortDirection = 'desc';\n } else {\n sortFieldName = null;\n sortDirection = null;\n }\n }\n\n setSort({\n fieldName: sortFieldName,\n direction: sortDirection,\n });\n }\n\n function handleFilterChange(fieldName, value) {\n setFilterValues(state => {\n return {\n ...state,\n [fieldName]: value,\n };\n });\n }\n\n return (\n <>\n {studyListFunctionsEnabled ? (\n <ConnectedDicomFilesUploader\n isOpen={activeModalId === 'DicomFilesUploader'}\n onClose={() => setActiveModalId(null)}\n />\n ) : null}\n {healthCareApiWindows}\n <WhiteLabelingContext.Consumer>\n {whiteLabeling => (\n <UserManagerContext.Consumer>\n {userManager => (\n <ConnectedHeader\n useLargeLogo={true}\n user={user}\n userManager={userManager}\n >\n {whiteLabeling &&\n whiteLabeling.createLogoComponentFn &&\n whiteLabeling.createLogoComponentFn(React)}\n </ConnectedHeader>\n )}\n </UserManagerContext.Consumer>\n )}\n </WhiteLabelingContext.Consumer>\n <div className=\"study-list-header\">\n <div className=\"header\">\n <h1 style={{ fontWeight: 300, fontSize: '22px' }}>\n {t('StudyList')}\n </h1>\n </div>\n <div className=\"actions\">\n {studyListFunctionsEnabled && healthCareApiButtons}\n {studyListFunctionsEnabled && (\n <PageToolbar\n onImport={() => setActiveModalId('DicomFilesUploader')}\n />\n )}\n <span className=\"study-count\">{studies.length}</span>\n </div>\n </div>\n\n <div className=\"table-head-background\" />\n <div className=\"study-list-container\">\n {/* STUDY LIST OR DROP ZONE? */}\n <StudyList\n isLoading={searchStatus.isSearchingForStudies}\n hasError={searchStatus.error === true}\n // Rows\n studies={studies}\n onSelectItem={studyInstanceUID => {\n const viewerPath = RoutesUtil.parseViewerPath(appConfig, server, {\n studyInstanceUIDs: studyInstanceUID,\n });\n history.push(viewerPath);\n }}\n // Table Header\n sort={sort}\n onSort={handleSort}\n filterValues={filterValues}\n onFilterChange={handleFilterChange}\n studyListDateFilterNumDays={appConfig.studyListDateFilterNumDays}\n displaySize={displaySize}\n />\n {/* PAGINATION FOOTER */}\n <TablePagination\n currentPage={pageNumber}\n nextPageFunc={() => setPageNumber(pageNumber + 1)}\n prevPageFunc={() => setPageNumber(pageNumber - 1)}\n onRowsPerPageChange={Rows => setRowsPerPage(Rows)}\n rowsPerPage={rowsPerPage}\n recordCount={studies.length}\n />\n </div>\n </>\n );\n}\n\nStudyListRoute.propTypes = {\n filters: PropTypes.object,\n PatientID: PropTypes.string,\n server: PropTypes.object,\n user: PropTypes.object,\n history: PropTypes.object,\n studyListFunctionsEnabled: PropTypes.bool,\n};\n\nStudyListRoute.defaultProps = {\n studyListFunctionsEnabled: true,\n};\n\nfunction updateURL(isModalOpen, appConfig, server, history) {\n if (isModalOpen) {\n return;\n }\n\n const listPath = RoutesUtil.parseStudyListPath(appConfig, server);\n\n if (UrlUtil.paramString.isValidPath(listPath)) {\n const { location = {} } = history;\n if (location.pathname !== listPath) {\n history.replace(listPath);\n }\n }\n}\n\n/**\n * Not ideal, but we use displaySize to determine how the filters should be used\n * to build the collection of promises we need to fetch a result set.\n *\n * @param {*} server\n * @param {*} filters\n * @param {object} sort\n * @param {string} sort.fieldName - field to sort by\n * @param {string} sort.direction - direction to sort\n * @param {number} rowsPerPage - Number of results to return\n * @param {number} pageNumber - Used to determine results offset\n * @param {string} displaySize - small, medium, large\n * @returns\n */\nasync function getStudyList(\n server,\n filters,\n sort,\n rowsPerPage,\n pageNumber,\n displaySize\n) {\n const {\n allFields,\n patientNameOrId,\n accessionOrModalityOrDescription,\n } = filters;\n const sortFieldName = sort.fieldName || 'PatientName';\n const sortDirection = sort.direction || 'desc';\n\n const mappedFilters = {\n PatientID: filters.PatientID,\n PatientName: filters.PatientName,\n AccessionNumber: filters.AccessionNumber,\n StudyDescription: filters.StudyDescription,\n ModalitiesInStudy: filters.modalities,\n // NEVER CHANGE\n studyDateFrom: filters.studyDateFrom,\n studyDateTo: filters.studyDateTo,\n limit: rowsPerPage,\n offset: pageNumber * rowsPerPage,\n fuzzymatching: server.supportsFuzzyMatching === true,\n };\n\n const studies = await _fetchStudies(server, mappedFilters, displaySize, {\n allFields,\n patientNameOrId,\n accessionOrModalityOrDescription,\n });\n\n // Only the fields we use\n const mappedStudies = studies.map(study => {\n const PatientName =\n typeof study.PatientName === 'string' ? study.PatientName : undefined;\n\n return {\n AccessionNumber: study.AccessionNumber, // \"1\"\n modalities: study.modalities, // \"SEG\\\\MR\" \n // numberOfStudyRelatedInstances: \"3\"\n // numberOfStudyRelatedSeries: \"3\"\n // PatientBirthdate: undefined\n PatientID: study.PatientID, // \"NOID\"\n PatientName, // \"NAME^NONE\"\n // PatientSex: \"M\"\n // referringPhysicianName: undefined\n StudyDate: study.StudyDate, // \"Jun 28, 2002\"\n StudyDescription: study.StudyDescription, // \"BRAIN\"\n // studyId: \"No Study ID\"\n StudyInstanceUID: study.StudyInstanceUID, // \"1.3.6.1.4.1.5962.99.1.3814087073.479799962.1489872804257.3.0\"\n // StudyTime: \"160956.0\"\n };\n });\n\n // For our smaller displays, map our field name to a single\n // field we can actually sort by.\n const sortFieldNameMapping = {\n allFields: 'PatientName',\n patientNameOrId: 'PatientName',\n accessionOrModalityOrDescription: 'modalities',\n };\n const mappedSortFieldName =\n sortFieldNameMapping[sortFieldName] || sortFieldName;\n\n const sortedStudies = _sortStudies(\n mappedStudies,\n mappedSortFieldName,\n sortDirection\n );\n\n // Because we've merged multiple requests, we may have more than\n // our Rows per page. Let's `take` that number from our sorted array.\n // This \"might\" cause paging issues.\n const numToTake =\n sortedStudies.length < rowsPerPage ? sortedStudies.length : rowsPerPage;\n const result = sortedStudies.slice(0, numToTake);\n\n return result;\n}\n\n/**\n *\n *\n * @param {object[]} studies - Array of studies to sort\n * @param {string} studies.StudyDate - Date in 'MMM DD, YYYY' format\n * @param {string} field - name of properties on study to sort by\n * @param {string} order - 'asc' or 'desc'\n * @returns\n */\nfunction _sortStudies(studies, field, order) {\n // Make sure our StudyDate is in a valid format and create copy of studies array\n const sortedStudies = studies.map(study => {\n if (!moment(study.StudyDate, 'MMM DD, YYYY', true).isValid()) {\n study.StudyDate = moment(study.StudyDate, 'YYYYMMDD').format(\n 'MMM DD, YYYY'\n );\n }\n return study;\n });\n\n // Sort by field\n sortedStudies.sort(function(a, b) {\n let fieldA = a[field];\n let fieldB = b[field];\n if (field === 'StudyDate') {\n fieldA = moment(fieldA).toISOString();\n fieldB = moment(fieldB).toISOString();\n }\n\n // Order\n if (order === 'desc') {\n if (fieldA < fieldB) {\n return -1;\n }\n if (fieldA > fieldB) {\n return 1;\n }\n return 0;\n } else {\n if (fieldA > fieldB) {\n return -1;\n }\n if (fieldA < fieldB) {\n return 1;\n }\n return 0;\n }\n });\n\n return sortedStudies;\n}\n\n/**\n * We're forced to do this because DICOMWeb does not support \"AND|OR\" searches\n * across multiple fields. This allows us to make multiple requests, remove\n * duplicates, and return the result set as if it were supported\n *\n * @param {object} server\n * @param {Object} filters\n * @param {string} displaySize - small, medium, or large\n * @param {string} multi.allFields\n * @param {string} multi.patientNameOrId\n * @param {string} multi.accessionOrModalityOrDescription\n */\nasync function _fetchStudies(\n server,\n filters,\n displaySize,\n { allFields, patientNameOrId, accessionOrModalityOrDescription }\n) {\n let queryFiltersArray = [filters];\n\n if (displaySize === 'small') {\n const firstSet = _getQueryFiltersForValue(\n filters,\n [\n 'PatientID',\n 'PatientName',\n 'AccessionNumber',\n 'StudyDescription',\n 'ModalitiesInStudy',\n ],\n allFields\n );\n\n if (firstSet.length) {\n queryFiltersArray = firstSet;\n }\n } else if (displaySize === 'medium') {\n const firstSet = _getQueryFiltersForValue(\n filters,\n ['PatientID', 'PatientName'],\n patientNameOrId\n );\n\n const secondSet = _getQueryFiltersForValue(\n filters,\n ['AccessionNumber', 'StudyDescription', 'ModalitiesInStudy'],\n accessionOrModalityOrDescription\n );\n\n if (firstSet.length || secondSet.length) {\n queryFiltersArray = firstSet.concat(secondSet);\n }\n }\n\n const queryPromises = [];\n\n queryFiltersArray.forEach(filter => {\n const searchStudiesPromise = OHIF.studies.searchStudies(server, filter);\n queryPromises.push(searchStudiesPromise);\n });\n\n const lotsOfStudies = await Promise.all(queryPromises);\n const studies = [];\n\n // Flatten and dedupe\n lotsOfStudies.forEach(arrayOfStudies => {\n if (arrayOfStudies) {\n arrayOfStudies.forEach(study => {\n if (!studies.some(s => s.StudyInstanceUID === study.StudyInstanceUID)) {\n studies.push(study);\n }\n });\n }\n });\n\n return studies;\n}\n\n/**\n *\n *\n * @param {*} filters\n * @param {*} fields - Array of string fields\n * @param {*} value\n */\nfunction _getQueryFiltersForValue(filters, fields, value) {\n const queryFilters = [];\n\n if (value === '' || !value) {\n return queryFilters;\n }\n\n fields.forEach(field => {\n const filter = Object.assign(\n {\n PatientID: '',\n PatientName: '',\n AccessionNumber: '',\n StudyDescription: '',\n ModalitiesInStudy: '',\n },\n filters\n );\n\n filter[field] = value;\n queryFilters.push(filter);\n });\n\n return queryFilters;\n}\n\nexport default withRouter(StudyListRoute);\n","import { connect } from 'react-redux';\n\nimport StudyListRoute from './StudyListRoute.js';\n\nconst isActive = a => a.active === true;\n\nconst mapStateToProps = state => {\n const activeServer = state.servers.servers.find(isActive);\n\n return {\n server: activeServer,\n user: state.oidc.user,\n };\n};\n\nconst ConnectedStudyList = connect(\n mapStateToProps,\n null\n)(StudyListRoute);\n\nexport default ConnectedStudyList;\n","import React, { useContext } from 'react';\nimport PropTypes from 'prop-types';\nimport { withRouter } from 'react-router-dom';\nimport ConnectedStudyList from './ConnectedStudyList';\nimport useServer from '../customHooks/useServer';\nimport OHIF from '@ohif/core';\nconst { urlUtil: UrlUtil } = OHIF.utils;\n\n// Contexts\nimport AppContext from '../context/AppContext';\n\nfunction StudyListRouting({ match: routeMatch, location: routeLocation }) {\n const {\n project,\n location,\n dataset,\n dicomStore,\n studyInstanceUIDs,\n seriesInstanceUIDs,\n } = routeMatch.params;\n const server = useServer({ project, location, dataset, dicomStore });\n const { appConfig = {} } = useContext(AppContext);\n\n const filters = UrlUtil.queryString.getQueryFilters(routeLocation);\n\n let studyListFunctionsEnabled = false;\n if (appConfig.studyListFunctionsEnabled) {\n studyListFunctionsEnabled = appConfig.studyListFunctionsEnabled;\n }\n return (\n <ConnectedStudyList\n filters={filters}\n studyListFunctionsEnabled={studyListFunctionsEnabled}\n />\n );\n}\n\nStudyListRouting.propTypes = {\n location: PropTypes.shape({\n search: PropTypes.string,\n }).isRequired,\n};\n\nexport default withRouter(StudyListRouting);\n"],"sourceRoot":""}