Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions .github/workflows/deploy_ghpages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
name: Deploy viewer to github pages

on:
push:
branches: ['gradienthealth/rebase-viewer-to-v3.10.1']
#pull_request:
#branches: [ "main" ]

jobs:
build-and-deploy:
runs-on: ubuntu-latest

strategy:
matrix:
node-version: [20.9.x]

steps:
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}

- name: Checkout cornerstone3D
uses: actions/checkout@v3
with:
repository: gradienthealth/cornerstone3D-beta
ref: gradienthealth/rebase-viewer-to-v3.10.1
path: ./cornerstone3D

- name: Build cornerstone3D
run: |
cd ./cornerstone3D
yarn install
yarn build:all

- name: Checkout GradientExtensionsAndModes
uses: actions/checkout@v3
with:
repository: gradienthealth/GradientExtensionsAndModes
ref: gradienthealth/rebase-viewer-to-v3.10.1
path: ./GradientExtensionsAndModes

#- name: Build GradientExtensionsAndModes
# run: |
# cd ./GradientExtensionsAndModes/extensions/ohif-gradienthealth-extension
# yarn install
# yarn build:package
# cd ./modes/cohort
# yarn install
# yarn build:package

- name: Checkout Viewers
uses: actions/checkout@v3
with:
repository: gradienthealth/Viewers
path: ./Viewers

- name: Link
run: |
cd ./cornerstone3D/packages/adapters/dist
yarn link
cd ../../ai/dist
yarn link
cd ../../core/dist
yarn link
cd ../../dicomImageLoader/dist
yarn link
cd ../../labelmap-interpolation/dist
yarn link
#cd ../../nifti-volume-loader/dist
#yarn link
cd ../../polymorphic-segmentation/dist
yarn link
cd ../../tools/dist
yarn link
cd ../../../../Viewers
yarn link @cornerstonejs/adapters
yarn link @cornerstonejs/ai
yarn link @cornerstonejs/core
yarn link @cornerstonejs/dicom-image-loader
#yarn link @cornerstonejs/nifti-volume-loader
yarn link @cornerstonejs/labelmap-interpolation
yarn link @cornerstonejs/polymorphic-segmentation
yarn link @cornerstonejs/tools
yarn install
yarn run cli link-extension ../GradientExtensionsAndModes/extensions/ohif-gradienthealth-extension
yarn run cli link-mode ../GradientExtensionsAndModes/modes/breast-density-mode
yarn run build:gradient

- name: Checkout gh page
uses: actions/checkout@v3
with:
repository: gradienthealth/gradienthealth.github.io
path: ./gradienthealth.github.io
token: ${{ secrets.GH_DEPLOY_TOKEN }}

- name: Copy
run: |
mv ./gradienthealth.github.io/.git /tmp/
rm -r ./gradienthealth.github.io
cp -r ./Viewers/platform/app/dist/ ./gradienthealth.github.io
mv /tmp/.git ./gradienthealth.github.io
cd ./gradienthealth.github.io
cp index.html 404.html
git config --global user.name "Adithyan-Dinesh-Trenser"
git config --global user.email "adithyan.dinesh@trenser.com"
git remote set-url origin https://Adithyan-Dinesh-Trenser:${{ secrets.GH_DEPLOY_TOKEN }}@github.com/gradienthealth/gradienthealth.github.io
git add .
git commit -a -m "publishing viewer"
git push -u origin
3 changes: 2 additions & 1 deletion extensions/default/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"@babel/runtime": "^7.20.13",
"@cornerstonejs/calculate-suv": "^1.1.0",
"lodash.get": "^4.4.2",
"lodash.uniqby": "^4.7.0"
"lodash.uniqby": "^4.7.0",
"cod-dicomweb-server": "^1.3.4"
}
}
235 changes: 235 additions & 0 deletions extensions/default/src/DicomWebDataSource/codDicomWebServerWrapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
import { CodDicomWebServer } from 'cod-dicomweb-server';

const Properties = {
StudyUID: '0020000D',
SeriesUID: '0020000E',
};

class CodDicomWebServerClient {
/**
* @param {Object} config
*/
constructor(config) {
this.baseURL = config.url;
this.qidoURL = this.baseURL;
this.wadoURL = this.baseURL;
this.config = config;
this.headers = config.headers;
this.errorInterceptor = config.errorInterceptor;

this._codServer = new CodDicomWebServer({ domain: parseDomainFromBaseURL(this.baseURL) });
this.deidStudyInstanceUIDMap = new Map(); // Map of study instance UIDs to deid study instance UIDs
}

/**
* @param {URLSearchParams} queryParams
*/
async fetchStudiesMetadata(queryParams) {
this._studiesMetadata = await this.filesFromStudyInstanceUID({
wadoURL: this.wadoURL,
bucketName: queryParams.get('bucket'),
prefix: queryParams.get('bucket-prefix'),
studyuids: queryParams.getAll('StudyInstanceUIDs'),
headers: this.headers,
})
.then(studies => {
return studies.filter(study => {
study.series = study.series.filter(aSeries => {
if (aSeries.instances.length) {
return true;
}

console.warn('No instance found in series ' + aSeries.deidSeriesInstanceUID);
return false;
});

if (study.series.length) {
const studyUID = study.series[0].instances[0]['0020000D'].Value[0];
this.deidStudyInstanceUIDMap.set(study.deidStudyInstanceUID, studyUID);
return true;
}

return false;
});
})
.catch(error => {
this.errorInterceptor(error);
return [];
});
}

/**
* @param {string} deidStudyInstanceUID
*/
getStudyUIDForDeidStudyUID(deidStudyInstanceUID) {
const studyWithDeidStudyUID = this._studiesMetadata.find(
study => (study.deidStudyInstanceUID = deidStudyInstanceUID)
);

return this._getProperty(studyWithDeidStudyUID, Properties.StudyUID);
}

/**
* @param {Object} data
* @param {string} property
*/
_getProperty(data, property) {
if (!data) {
return;
}

return (
data[property]?.Value[0] ||
data.instances?.[0][property].Value[0] ||
data.series?.[0].instances[0][property].Value[0]
);
}

/**
* @param {Object[]} studies
* @param {string} studyInstanceUID
*/
_findStudy(studies = [], studyInstanceUID) {
return studies.find(
study => this._getProperty(study, Properties.StudyUID) === studyInstanceUID
);
}

/**
* @param {Object[]} series
* @param {string} seriesInstanceUID
*/
_findSeries(series = [], seriesInstanceUID) {
return series.find(
series => this._getProperty(series, Properties.SeriesUID) === seriesInstanceUID
);
}

/**
* @param {Object} options
* @param {string} options.studyInstanceUID
* @param {string} options.seriesInstanceUID
*/
retrieveSeriesMetadata({ studyInstanceUID, seriesInstanceUID }) {
const studyFound = this._findStudy(this._studiesMetadata, studyInstanceUID);
const seriesFound = this._findSeries(studyFound?.series, seriesInstanceUID);

return new Promise((resolve, reject) => {
if (seriesFound) {
resolve(seriesFound.instances);
} else {
reject();
}
});
}

/**
* @param {Object} options
* @param {string} options.studyInstanceUID
*/
retrieveStudyMetadata({ studyInstanceUID }) {
const studyFound = this._findStudy(this._studiesMetadata, studyInstanceUID);

return new Promise((resolve, reject) => {
if (studyFound) {
resolve(studyFound.series.flatMap(aSeries => aSeries.instances));
} else {
reject();
}
});
}

/**
* @param {Object} options
* @param {string} options.studyInstanceUID
*/
searchForSeries({ studyInstanceUID }) {
const studyFound = this._findStudy(this._studiesMetadata, studyInstanceUID);

return new Promise(resolve => {
if (studyFound) {
resolve(studyFound.series.map(aSeries => aSeries.instances[0]));
} else {
resolve([]);
}
});
}

/**
* @param {Object} options
* @param {Object} options.queryParams
* @param {string} options.queryParams.StudyInstanceUID
*/
searchForStudies({ queryParams }) {
const studyFound = this._studiesMetadata.find(study => {
if (this._getProperty(study, Properties.StudyUID) === queryParams.StudyInstanceUID) {
return true;
}

const tagFound = Object.entries(queryParams).find(
([tag, value]) => this._getProperty(study, tag) === value
);
if (tagFound) {
return true;
}

return false;
});

return new Promise(resolve => {
if (studyFound) {
resolve([studyFound.series[0].instances[0]]);
} else {
resolve([]);
}
});
}

/**
* @param {Object} params
* @param {string} params.wadoURL
* @param {string} params.bucketName
* @param {string[]} params.studyuids
* @param {string} params.prefix
* @param {Object} params.headers
*/
async filesFromStudyInstanceUID({ wadoURL, bucketName, prefix, studyuids, headers }) {
const delimiter = '/';
const domain = parseDomainFromBaseURL(wadoURL);
const bucketComponents = wadoURL.split(domain + delimiter)[1].split(delimiter);
const bucket = bucketName || bucketComponents[0];
const bucketPrefix = prefix || bucketComponents.slice(1).join(delimiter) || 'dicomweb';
const urlRoot = `${domain}/${bucket}/${bucketPrefix}`;

const studyMetadata = studyuids.map(async (/** @type {string} */ deidStudyInstanceuid) => {
const folderPath = `${bucketPrefix}/studies/${deidStudyInstanceuid}/series/`;
const apiUrl = `${domain}/storage/v1/b/${bucket}/o?prefix=${folderPath}&delimiter=${delimiter}`;
const response = await fetch(apiUrl, { headers });
const res = await response.json();
const folders = res.prefixes || [];
const series = folders.map(async (/** @type {string} */ folderPath) => {
const deidSeriesInstanceUID = folderPath.split('/series/')[1].split(delimiter)[0];
const wadoUrl = `${urlRoot}/studies/${deidStudyInstanceuid}/series/${deidSeriesInstanceUID}/metadata`;
return {
deidSeriesInstanceUID,
instances: await this._codServer.fetchCod(wadoUrl, headers),
};
});
return Promise.all(series).then(result => ({
deidStudyInstanceUID: deidStudyInstanceuid,
series: result,
}));
});
return await Promise.all(studyMetadata);
}
}

/**
* @param {string} baseRoot
*/
function parseDomainFromBaseURL(baseRoot) {
const [firstPart, secondPart] = baseRoot.split('://');
return `${firstPart}://${secondPart.split('/')[0]}`;
}

export default CodDicomWebServerClient;
25 changes: 25 additions & 0 deletions extensions/default/src/DicomWebDataSource/getCodImageId.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import getWADORSImageId from './utils/getWADORSImageId';

/**
* @param {Object} params
* @param {Object} params.instance
* @param {number} [params.frame]
* @param {Object} params.config
*/
export default function getCodImageId({ instance, frame, config }) {
if (!instance) {
return;
}

let wadoRsImageId;

if (instance.imageId && frame === undefined) {
wadoRsImageId = instance.imageId;
} else if (instance.url) {
wadoRsImageId = instance.url;
} else {
wadoRsImageId = getWADORSImageId(instance, config, frame);
}

return wadoRsImageId.replace('wadors:', 'cod:');
}
Loading