diff --git a/DICOMwebWADORSURI.js b/DICOMwebWADORSURI.js index d433240..3db3cda 100644 --- a/DICOMwebWADORSURI.js +++ b/DICOMwebWADORSURI.js @@ -1,32 +1,10 @@ import URL from "url-parse"; import _ from "lodash"; import fetch from 'node-fetch'; - -import AllowProtocol from "./Config/AllowProtocol.json" assert {type: "json"}; -import QueryMode from "./Config/QueryMode.json" assert {type: "json"}; -import WADOParameter from "./Config/WADOParameter.json" assert {type: "json"}; -import WADORSStructure from "./Config/WADORSStructure.json" assert {type: "json"}; -import WADOURIStructure from "./Config/WADOURIStructure.json" assert {type: "json"}; - -const METADATA_PROPERTY_VALUE = "MetadataURL"; -const METADATA_KEY_VALUE = "Metadata"; -const DESCRIPTION_PROPERTY_VALUE = "Description"; -const DICOM_OBJECT_VALUE_KEYWORD = "Value"; -const DESCRIPTION_ELEMENT_SERVERURL = "{s}"; -const DESCRIPTION_ELEMENT_STUDY = "{study}"; -const DICOM_FILE_STRUCTURE_LEVEL_DECREASE_ARRAY = ["Patient", "Study", "Series", "Instance", "Frame"]; -const WADO_METADATA_START_LEVEL_NAME = "Study"; -const UID_NAME = "UID"; +import Study from "./myClass/Study.js"; class DICOMwebWADORSURI { constructor() { - //Config - this.wadoParameter = WADOParameter; - this.wadoStructure = { - rs: WADORSStructure, - uri: WADOURIStructure - }; - //Token this.isUseToken = false; this.tokenObject = undefined; @@ -41,94 +19,32 @@ class DICOMwebWADORSURI { //Query Mode this.queryMode = undefined; - this.queryStructure = undefined; //Response this.response = undefined; - - //Stage - this.stage = { - Study: false, - Series: false, - Instance: false, - Frame: false - }; } async init() { - //確定有 queryMode 與 url-parse package 的這些數據 - //初始化 serverURL this.serverURL = await this._getServerURL(); - - //初始化 queryStructure - this.queryStructure = await this._getQueryStructureByQueryMode(this.queryMode); - - //初始化 response 的 studyLevel 的 UID & 符合 queryMode 的所有查詢種類的 URL & 查詢 metadata - this.response = await this._getInitStudyLevelElement(); } - async querySeriesByStudy() { - const LAST_LEVEL_NAME = "Study"; - const LEVEL_NAME = "Series"; - - //檢查 this.stage.study 是否為 true,代表 study 已經有資料。 - if (!(await this._isStageComplete(LAST_LEVEL_NAME))) { - throw `${LAST_LEVEL_NAME} 尚未完成初始化。`; - } - - - //取得 study level 的 metadata 之後,建立 SeriesInstanceUIDList。 - let tempStudyLevelMetadata = _.cloneDeep(_.get(this.response, METADATA_KEY_VALUE)); - let SeriesInstanceUIDList = []; - let uidMetadataCode = _.head(_.values(_.get(this.wadoParameter, LAST_LEVEL_NAME))); - _.forEach(tempStudyLevelMetadata, (singleSeriesMetadataObject) => { - let uidOjbect = _.get(singleSeriesMetadataObject, uidMetadataCode) - let uidValue = _.head(_.get(uidOjbect, DICOM_OBJECT_VALUE_KEYWORD)); - SeriesInstanceUIDList.push(uidValue); - }); - - - //根據 SeriesInstanceUIDList,跑迴圈,每個 UID 都開始建立 Series 的 Object,最終更新至 this.response。 - let tempSeriesObject = []; - for (let i = 0; i < SeriesInstanceUIDList.length; i++) { - let uidValue = SeriesInstanceUIDList[i]; - let result = await this._getSeriesObject(LEVEL_NAME, uidValue); - tempSeriesObject.push(_.cloneDeep(result)); - } - _.set(this.response, LEVEL_NAME, _.cloneDeep(tempSeriesObject)); - - - //確認無誤之後,開始更新 this.stage.Series。 - this.stage.Series = true; + async querySeries() { + let study = new Study(this.serverURL, this.queryMode, this.studyInstanceUID); + await study.querySeries(); + this.response = study; } - - async queryInstanceBySeries() { - const LAST_LEVEL_NAME = "Series"; - const LEVEL_NAME = "Instance"; - - //檢查 this.stage.study 是否為 true,代表 study 已經有資料。 - if (!(await this._isStageComplete(LAST_LEVEL_NAME))) { - throw `${LAST_LEVEL_NAME} 尚未完成初始化。`; - } - - //取得 seriesObject,並直接渲染所有 instance。 - let seriesObject = _.has(this.response, LAST_LEVEL_NAME) ? _.get(this.response, LAST_LEVEL_NAME) : undefined; - - for (let index = 0; index < seriesObject.length; index++) { - let singleSeries = seriesObject[index]; - let tempObject = await this._getInstanceBySingleSeries(singleSeries); - _.set(singleSeries, LEVEL_NAME, _.cloneDeep(tempObject)); - _.set(seriesObject, index, _.cloneDeep(singleSeries)); - } - _.set(this.response, LAST_LEVEL_NAME, _.cloneDeep(seriesObject)); + + async renderAllSeries() { + let study = new Study(this.serverURL, this.queryMode, this.studyInstanceUID); + await study.renderAllSeries(); + this.response = study; } - - // Private Function ============================================================================================ - // Private Function ============================================================================================ - // Private Function ============================================================================================ - // Private Function ============================================================================================ - // Private Function ============================================================================================ + async renderSpecificSeries(seriesInstanceUID) { + let study = new Study(this.serverURL, this.queryMode, this.studyInstanceUID); + await study.renderSpecificSeries(seriesInstanceUID); + this.response = study; + } async _getServerURL() { let result = undefined; @@ -137,440 +53,11 @@ class DICOMwebWADORSURI { serverURL.set("hostname", this.hostname); serverURL.set("pathname", this.pathname); serverURL.set("port", this.port); - serverURL.set("protocol", AllowProtocol[this.protocol]); + serverURL.set("protocol", this.protocol); result = serverURL.toString(); return result; } - - async _getQueryStructureByQueryMode(queryMode) { - let result = undefined; - - if (await this._isQueryModeAllow(queryMode)) { - if (queryMode === "uri") { - result = this.wadoStructure.uri; - } else { - result = this.wadoStructure.rs; - } - } - - return result; - } - - async _getInitStudyLevelElement() { - let result = undefined; - const LEVEL_NAME = "Study"; - - - //取得符合 queryMode 的所有查詢種類的 URL =============================================================================== - - //確認本次 queryMode 這個 Level 的 查詢種類 的 URL Template - let urlTemplates = {}; - _.forEach(this.queryStructure[LEVEL_NAME], (value, key) => { - let tempObject = {}; - tempObject[key] = value; - urlTemplates = _.assign(urlTemplates, tempObject); - }); - - //確認本次 URL Template 出現的變數 ValueSet - const DESCRIPTION_VALUE_SET = await this._getDescriptionKeysValueSet(this.queryStructure); - const THIS_LEVEL_PARAMETER_VALUE_SET = await this._getThisLevelParameterValueSet(LEVEL_NAME, this.queryStructure, DESCRIPTION_VALUE_SET); - - //這次的 ValueSet 的變數給予數值 - let descriptionParameter = await this._getDefaultDescriptionParameter(THIS_LEVEL_PARAMETER_VALUE_SET); - - //確定塞的數值是在 本次出現的變數裡面 - if (!(_.isEqual(_.keys(descriptionParameter), THIS_LEVEL_PARAMETER_VALUE_SET))) { - throw "設定的變數集合 不等於 本此出現的變數集合"; - } - - //替換本次 URL Template 的 變數 - let urlResult = _.cloneDeep(urlTemplates); - _.forEach(descriptionParameter, (value, key) => { - let variable = key; - let data = value; - _.forEach(urlResult, (value, key) => { - urlResult[key] = _.replace(value, variable, data); - }); - }); - - //丟到 result - result = _.assign(result, urlResult); - - - //取得 studyLevel 的 UID ============================================================================================== - let tempUIDObject = {}; - tempUIDObject[UID_NAME] = this.studyInstanceUID; - result = _.assign(result, tempUIDObject); - - - //取得 Metadata ======================================================================================================= - let metadataURL = await this._getMetadataUrlInThisObject(result); - result = _.assign(result, await this._getMetadataByURL(metadataURL)); - - - //更新Stage =========================================================================================================== - this.stage.Study = true; - - - return _.cloneDeep(result); - } - - async _getInstanceBySingleSeries(singleSeries) { - let result = undefined; - - const LEVEL_NAME = "Instance"; - - - //取得當前 查詢模式結構 - let urlTemplates = {}; - _.forEach(this.queryStructure[LEVEL_NAME], (value, key) => { - let tempObject = {}; - tempObject[key] = value; - urlTemplates = _.assign(urlTemplates, tempObject); - }); - result = urlTemplates; - - //取得 查詢模式結構所需 uid - const DESCRIPTION_VALUE_SET = await this._getDescriptionKeysValueSet(this.queryStructure); - const THIS_LEVEL_PARAMETER_VALUE_SET = await this._getThisLevelParameterValueSet(LEVEL_NAME, this.queryStructure, DESCRIPTION_VALUE_SET); - let descriptionParameter = await this._getDefaultDescriptionParameter(THIS_LEVEL_PARAMETER_VALUE_SET); - descriptionParameter = _.set(descriptionParameter, "{series}", _.get(singleSeries, UID_NAME)); - result = await this._getRenderedSingleSeries(singleSeries, urlTemplates, descriptionParameter); - - - return result; - } - - async _getRenderedSingleSeries(singleSeries, urlTemplates, descriptionParameter) { - let result = []; - - let MetadataOfInstances = _.get(singleSeries, METADATA_KEY_VALUE); - let tempDescriptionParameter = _.cloneDeep(descriptionParameter); - - //造訪每個 Instance - for (let i = 0; i < MetadataOfInstances.length; i++) { - let tempObject = {}; - - //取得 URL - let MetadataOfInstance = MetadataOfInstances[i]; - let codeOfInstanceUID = _.get(_.get(this.wadoParameter, "Series"), _.get(_.get(this.queryStructure, DESCRIPTION_PROPERTY_VALUE), "{instance}")); - let instanceUID = _.first(_.get(_.get(MetadataOfInstance, codeOfInstanceUID), DICOM_OBJECT_VALUE_KEYWORD)); - tempDescriptionParameter = _.set(tempDescriptionParameter, "{instance}", instanceUID); - let urlResult = _.cloneDeep(urlTemplates); - - _.forEach(tempDescriptionParameter, (variable, data) => { - _.forEach(urlResult, (value, key) => { - urlResult[key] = _.replace(value, data, variable); - }) - }) - tempObject = _.assign(tempObject, urlResult); - - //取得 studyLevel 的 UID - let tempUIDObject = {}; - tempUIDObject[UID_NAME] = instanceUID; - tempObject = _.assign(tempObject, tempUIDObject); - - //取得 Metadata - let metadataURL = await this._getMetadataUrlInThisObject(tempObject); - tempObject = _.assign(tempObject, await this._getMetadataByURL(metadataURL)); - - //取得底下的 Frame - let tempFrameObject = await this._getFrameObject(tempObject, tempDescriptionParameter); - _.set(tempObject, "Frame", _.cloneDeep(tempFrameObject)); - result.push(_.cloneDeep(tempObject)); - - } - - - return _.cloneDeep(result); - } - - - - async _getFrameObject(singleInstance, descriptionParameter) { - let result = undefined; - - const LAST_LEVEL_NAME = "Instance"; - const THIS_LEVEL_NAME = "Frame"; - const FRAME_PARAMETER = "{frames}"; - - let codeOfNumberOfFrames = _.get(_.get(this.wadoParameter, LAST_LEVEL_NAME), _.get(_.get(this.queryStructure, DESCRIPTION_PROPERTY_VALUE), FRAME_PARAMETER)); - let numberOfFrames = _.get(_.get(_.first(_.get(singleInstance, METADATA_KEY_VALUE)), codeOfNumberOfFrames), DICOM_OBJECT_VALUE_KEYWORD); - - - - if (!(_.isEqual(numberOfFrames, undefined))) { - let framesObject = {}; - for (let i = 1; i <= numberOfFrames; i++) { - let tempObject = {}; - - let tempDescriptionParameter = _.set(descriptionParameter, FRAME_PARAMETER, i); - - //取得當前 查詢模式結構 - let urlTemplates = {}; - _.forEach(this.queryStructure[THIS_LEVEL_NAME], (value, key) => { - let tempObject = {}; - tempObject[key] = value; - urlTemplates = _.assign(urlTemplates, tempObject); - }); - - //取得URL - let urlResult = _.cloneDeep(urlTemplates); - _.forEach(tempDescriptionParameter, (variable, data) => { - _.forEach(urlResult, (value, key) => { - urlResult[key] = _.replace(value, data, variable); - }) - }) - - //組成Object - tempObject = _.set(tempObject, i, urlResult); - - framesObject = _.cloneDeep(_.assign(framesObject, tempObject)); - - } - result = _.cloneDeep(_.assign(result, framesObject)); - } - - return result; - } - - - - async _getSeriesObject(levelName, uidValue) { - let result = undefined; - - //取得符合 queryMode 的所有查詢種類的 URL =============================================================================== - - //確認本次 queryMode 這個 Level 的 查詢種類 的 URL Template - let urlTemplates = {}; - _.forEach(this.queryStructure[levelName], (value, key) => { - let tempObject = {}; - tempObject[key] = value; - urlTemplates = _.assign(urlTemplates, tempObject); - }); - - - //確認本次 URL Template 出現的變數 ValueSet - const DESCRIPTION_VALUE_SET = await this._getDescriptionKeysValueSet(this.queryStructure); - const THIS_LEVEL_PARAMETER_VALUE_SET = await this._getThisLevelParameterValueSet(levelName, this.queryStructure, DESCRIPTION_VALUE_SET); - - //這次的 ValueSet 的變數給予數值 - let descriptionParameter = await this._getDefaultDescriptionParameter(THIS_LEVEL_PARAMETER_VALUE_SET); - descriptionParameter = _.set(descriptionParameter, "{series}", uidValue); - - //確定塞的數值是在 本次出現的變數裡面 - if (!(_.isEqual(_.keys(descriptionParameter), THIS_LEVEL_PARAMETER_VALUE_SET))) { - throw "設定的變數集合 不等於 本此出現的變數集合"; - } - - //替換本次 URL Template 的 變數 - let urlResult = _.cloneDeep(urlTemplates); - _.forEach(descriptionParameter, (value, key) => { - let variable = key; - let data = value; - _.forEach(urlResult, (value, key) => { - urlResult[key] = _.replace(value, variable, data); - }); - }); - result = urlResult; - - //丟到 result - result = _.assign(result, urlResult); - - - //取得 seriesLevel 的 UID ============================================================================================== - let tempUIDObject = {}; - tempUIDObject[UID_NAME] = uidValue; - result = _.assign(result, tempUIDObject); - - - //取得 Metadata ======================================================================================================= - let metadataURL = await this._getMetadataUrlInThisObject(result); - result = _.assign(result, await this._getMetadataByURL(metadataURL)); - - - - - - return _.cloneDeep(result); - } - - async _getDefaultDescriptionParameter(thisLevelParameterValueSet) { - let result = undefined; - - _.forEach(thisLevelParameterValueSet, (value) => { - let tempObject = {}; - tempObject[value] = undefined; - result = _.assign(result, tempObject); - }); - - if (_.has(result, DESCRIPTION_ELEMENT_SERVERURL)) { - result[DESCRIPTION_ELEMENT_SERVERURL] = this.serverURL; - } else { - throw "填入伺服器網址時錯誤。"; - } - - if (_.has(result, DESCRIPTION_ELEMENT_STUDY)) { - result[DESCRIPTION_ELEMENT_STUDY] = this.studyInstanceUID; - } else { - throw "填入 Study Instance UID 時錯誤。"; - } - - - return result; - } - - async _isStageComplete(levelName) { - return _.get(this.stage, levelName); - } - - async _getMetadataByURL(url) { - let result = undefined; - - - let tempMetadataObject = {}; - tempMetadataObject[METADATA_KEY_VALUE] = await this._getRequestResponse(url); - result = _.assign(result, tempMetadataObject) - - return result; - } - - async _getMetadataUrlInThisObject(object) { - let result = undefined; - - - result = object[METADATA_PROPERTY_VALUE]; - - return result; - } - - async _getRequestResponse(url) { - let result = undefined; - let response = undefined; - - if (this.isUseToken) { - response = await fetch(url, { - headers: this.tokenObject - }); - } else { - response = await fetch(url); - } - - result = await response.json(); - - return result; - } - - async _getThisLevelParameterValueSet(level, queryStructure, descriptionValueSet) { - let result = []; - - let urlTemplates = []; - _.forEach(queryStructure[level], (value, key) => { - urlTemplates.push(value); - }); - - for (let i = 0; i < urlTemplates.length; i++) { - let urlTemplate = urlTemplates[i]; - for (let j = 0; j < descriptionValueSet.length; j++) { - let descriptionValue = descriptionValueSet[j]; - if (await this._isKeywordInText(urlTemplate, descriptionValue)) { - result.push(descriptionValue); - } - } - } - - result = _.uniq(result); - - return result; - } - - async _getDescriptionKeysValueSet(queryStructure) { - let result = undefined; - - result = _.keys(queryStructure[DESCRIPTION_PROPERTY_VALUE]); - - return result; - } - - async _isKeywordInText(text, keyword) { - return text.includes(keyword); - } - - async _isQueryModeAllow(queryMode) { - let result = false; - - const QUERYMODE_VALUE_SET = _.keys(QueryMode); - if (!(_.includes(QUERYMODE_VALUE_SET, queryMode))) { - throw "queryMode is not allow."; - } else { - result = true; - } - - return result; - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - } export default DICOMwebWADORSURI; \ No newline at end of file diff --git a/README.md b/README.md index 8a93cd3..f6fdecf 100644 --- a/README.md +++ b/README.md @@ -41,11 +41,16 @@ wado.studyInstanceUID = '1.3.46.670589.45.1.1.4993912214784.1.5436.1538560373543 await wado.init(); //只查詢到 Series -await wado.querySeriesByStudy(); +// await wado.querySeries(); -//查詢 Series 底下所有的 Instance 還有底下所有的 Frame -await wado.queryInstanceBySeries(); +//只渲染指定的 Series 底下的所有 URL +// let seriesInstanceUID = "1.3.46.670589.45.1.1.4993912214784.1.5436.1538560606509.3"; +// await wado.renderSpecificSeries(seriesInstanceUID); +//渲染全 Series 底下的所有 URL +await wado.renderAllSeries(); + +// fs.writeFile("response.json", JSON.stringify(wado.response), (err) => { if (err) throw err; }); ``` \ No newline at end of file diff --git a/myClass/Frame.js b/myClass/Frame.js new file mode 100644 index 0000000..839a012 --- /dev/null +++ b/myClass/Frame.js @@ -0,0 +1,90 @@ +import _ from "lodash"; + +class Frame { + constructor(parameter, queryMode, numberOfFrame) { + this.uid = numberOfFrame; + + this.parameter = _.assign(parameter, { "{frames}": numberOfFrame }); + + this.queryMode = queryMode; + + this.url = { + "rs": { + "rendered": "{s}/studies/{study}/series/{series}/instances/{instance}/frames/{frames}/rendered" + }, + "uri": { + "rendered": "{s}/wado?requestType=WADO/&studyUID={study}&seriesUID={series}&objectUID={instance}&frameNumber={frames}&contentType=image/jpeg" + } + }; + + this.isUseToken = false; + this.tokenObject = null; + } + + async init() { + await this._validateQueryMode(); + await this._replaceUrlParameter(); + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + async _validateQueryMode() { + let queryModeValueSet = _.keys(this.url); + if ( !(_.includes(queryModeValueSet, this.queryMode)) ) { + throw `查詢模式必須是 ${_.toString(queryModeValueSet)}`; + } + } + + async _replaceUrlParameter() { + _.forEach(_.get(this.url, this.queryMode), (urlTemplate, key) => { + _.forEach(this.parameter, (valueOfParameter, keyOfParameter) => { + urlTemplate = _.replace(urlTemplate, keyOfParameter, valueOfParameter); + }); + _.set(_.get(this.url, this.queryMode), key, urlTemplate); + }); + } +} + +export default Frame \ No newline at end of file diff --git a/myClass/Instance.js b/myClass/Instance.js new file mode 100644 index 0000000..fe10a5e --- /dev/null +++ b/myClass/Instance.js @@ -0,0 +1,171 @@ +import _ from "lodash"; +import fetch from 'node-fetch'; +import Frame from "./Frame.js"; + +class Instance { + constructor(parameter, queryMode, uid) { + this.uid = uid; + + this.parameter = _.assign(parameter, { "{instance}": uid }); + + this.queryMode = queryMode; + + this.url = { + "rs": { + "entire": "{s}/studies/{study}/series/{series}/instances/{instance}", + "rendered": "{s}/studies/{study}/series/{series}/instances/{instance}/rendered", + "metadata": "{s}/studies/{study}/series/{series}/instances/{instance}/metadata" + }, + "uri": { + "entire": "{s}/wado?requestType=WADO/&studyUID={study}&seriesUID={series}&objectUID={instance}", + "metadata": "{s}/studies/{study}/series/{series}/instances/{instance}/metadata" + } + }; + + this.isUseToken = false; + this.tokenObject = null; + + this.metadata = null; + + this.codeOfNumberOfFrames = "00280008"; + + this.Frames = null; + } + + async init() { + await this._validateQueryMode(); + await this._replaceUrlParameter(); + this.metadata = _.first(await this._getMetadata(_.get(_.get(this.url, this.queryMode), "metadata"))); + this.Frames = await this._getFrames(); + } + + async _getFrames() { + let result = []; + + let numberOfFrame = _.first(_.get(_.get(this.metadata, this.codeOfNumberOfFrames), "Value")); + + for (let i = 1; i <= numberOfFrame; i++) { + let tempObject = undefined; + tempObject = new Frame(this.parameter, this.queryMode, i); + await tempObject.init(); + result.push(tempObject); + } + + + + return result; + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + async _validateQueryMode() { + let queryModeValueSet = _.keys(this.url); + if ( !(_.includes(queryModeValueSet, this.queryMode)) ) { + throw `查詢模式必須是 ${_.toString(queryModeValueSet)}`; + } + } + + async _replaceUrlParameter() { + _.forEach(_.get(this.url, this.queryMode), (urlTemplate, key) => { + _.forEach(this.parameter, (valueOfParameter, keyOfParameter) => { + urlTemplate = _.replace(urlTemplate, keyOfParameter, valueOfParameter); + }); + _.set(_.get(this.url, this.queryMode), key, urlTemplate); + }); + } + + async _getMetadata(metadataURL) { + return await this._getJsonResponseFromURL(metadataURL); + } + + async _getJsonResponseFromURL(url) { + let result = undefined; + let response = undefined; + + if (this.isUseToken) { + response = await fetch(url, { + headers: this.tokenObject + }); + } else { + response = await fetch(url); + } + + result = await response.json(); + + return result; + } +} + +export default Instance \ No newline at end of file diff --git a/myClass/Series.js b/myClass/Series.js new file mode 100644 index 0000000..bc9bf47 --- /dev/null +++ b/myClass/Series.js @@ -0,0 +1,178 @@ +import _ from "lodash"; +import fetch from 'node-fetch'; +import Instance from "./Instance.js" + +class Series { + constructor(parameter, queryMode, uid) { + this.uid = uid; + + this.parameter = _.assign(parameter, { "{series}": uid }); + + this.queryMode = queryMode; + + this.url = { + "rs": { + "entire": "{s}/studies/{study}/series/{series}", + "rendered": "{s}/studies/{study}/series/{series}/rendered", + "metadata": "{s}/studies/{study}/series/{series}/metadata" + }, + "uri": { + "entire": "{s}/wado?requestType=WADO/&studyUID={study}&seriesUID={series}", + "metadata": "{s}/studies/{study}/series/{series}/metadata" + } + }; + + this.isUseToken = false; + this.tokenObject = null; + + this.metadata = null; + + this.codeOfSOPInstanceUID = "00080018"; + + this.Instances = null; + } + + async init(isRenderInstances = false) { + await this._validateQueryMode(); + await this._replaceUrlParameter(); + this.metadata = await this._getMetadata(_.get(_.get(this.url, this.queryMode), "metadata")); + if (isRenderInstances) this.Instances = await this._getInstances(); + } + + async _getInstances() { + let result = []; + + for (let i = 0; i < _.toArray(this.metadata).length; i++) { + let instanceMetadata = _.get(this.metadata, i); + + let tempObject = undefined; + + if (_.has(instanceMetadata, this.codeOfSOPInstanceUID)) { + tempObject = new Instance(this.parameter, this.queryMode, _.first(_.get(_.get(instanceMetadata, this.codeOfSOPInstanceUID), "Value"))); + await tempObject.init(); + } else { + tempObject = `這個 Instance 沒有 ${this.codeOfSeriesInstanceUID}`; + } + result.push(tempObject); + } + return result; + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + async _validateQueryMode() { + let queryModeValueSet = _.keys(this.url); + if ( !(_.includes(queryModeValueSet, this.queryMode)) ) { + throw `查詢模式必須是 ${_.toString(queryModeValueSet)}`; + } + } + + async _replaceUrlParameter() { + _.forEach(_.get(this.url, this.queryMode), (urlTemplate, key) => { + _.forEach(this.parameter, (valueOfParameter, keyOfParameter) => { + urlTemplate = _.replace(urlTemplate, keyOfParameter, valueOfParameter); + }); + _.set(_.get(this.url, this.queryMode), key, urlTemplate); + }); + } + + async _getMetadata(metadataURL) { + return await this._getJsonResponseFromURL(metadataURL); + } + + async _getJsonResponseFromURL(url) { + let result = undefined; + let response = undefined; + + if (this.isUseToken) { + response = await fetch(url, { + headers: this.tokenObject + }); + } else { + response = await fetch(url); + } + + result = await response.json(); + + return result; + } +} + +export default Series \ No newline at end of file diff --git a/myClass/Study.js b/myClass/Study.js new file mode 100644 index 0000000..67e2719 --- /dev/null +++ b/myClass/Study.js @@ -0,0 +1,214 @@ +import _ from "lodash"; +import fetch from 'node-fetch'; +import Series from "./Series.js"; + +class Study { + constructor(serverURL, queryMode, uid) { + this.uid = uid; + + this.parameter = { + "{s}": serverURL, + "{study}": uid + }; + + this.queryMode = queryMode; + + this.url = { + "rs": { + "entire": "{s}/studies/{study}", + "rendered": "{s}/studies/{study}/rendered", + "metadata": "{s}/studies/{study}/series" + }, + "uri": { + "entire": "{s}/wado?requestType=WADO/&studyUID={study}", + "metadata": "{s}/studies/{study}/series" + } + }; + + this.isUseToken = false; + this.tokenObject = null; + + this.metadata = null; + + this.codeOfSeriesInstanceUID = "0020000E"; + + this.Series = null; + } + + async querySeries() { + await this._init(); + this.metadata = await this._getMetadata(_.get(_.get(this.url, this.queryMode), "metadata")); + this.Series = await this._getSeries(); + } + + async renderAllSeries() { + if (_.isNull(this.Series)) { + await this.querySeries(); + } + + for (let i = 0; i < _.toArray(this.Series).length; i++) { + let singleSeries = _.get(this.Series, i); + await this.renderSpecificSeries(singleSeries.uid); + } + } + + async renderSpecificSeries(seriesInstanceUID) { + if (_.isNull(this.Series)) { + await this.querySeries(); + } + + let seriesUidValueSet = []; + _.forEach(this.Series, (singleSeries) => { + seriesUidValueSet.push(_.get(singleSeries, "uid")); + }); + + if ( !(_.includes(seriesUidValueSet, seriesInstanceUID)) ) { + throw `此 seriesInstanceUID: ${seriesInstanceUID} 不存在於此 StudyInstanceUID 之中。`; + } + + for (let i = 0; i < _.toArray(this.Series).length; i++) { + let singleSeries = _.get(this.Series, i); + if (_.isEqual(singleSeries.uid, seriesInstanceUID)) { + let tempObject = await this._getSpecificSeries(seriesInstanceUID); + _.set(this.Series, i, tempObject); + } + } + } + + async _getSpecificSeries(seriesInstanceUID) { + let result = undefined; + + for (let i = 0; i < _.toArray(this.Series).length; i++) { + let singleSeries = _.get(this.Series, i); + if (_.isEqual(singleSeries.uid, seriesInstanceUID)) { + let tempObject = new Series(singleSeries.parameter, singleSeries.queryMode, singleSeries.uid); + let isRenderInstances = true; + await tempObject.init(isRenderInstances); + result = tempObject; + } + } + + return result; + } + + async _init() { + await this._validateQueryMode(); + await this._replaceUrlParameter(); + } + + async _getSeries() { + let result = []; + + for (let i = 0; i < _.toArray(this.metadata).length; i++) { + let singleSeriesMetadata = _.get(this.metadata, i); + + let tempObject = undefined; + + if (_.has(singleSeriesMetadata, this.codeOfSeriesInstanceUID)) { + tempObject = new Series(this.parameter, this.queryMode, _.first(_.get(_.get(singleSeriesMetadata, this.codeOfSeriesInstanceUID), "Value"))); + await tempObject.init(); + } else { + tempObject = `這個 Series 沒有 ${this.codeOfSeriesInstanceUID}`; + } + + result.push(_.cloneDeep(JSON.parse(JSON.stringify(tempObject)))); + } + return result; + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + async _validateQueryMode() { + let queryModeValueSet = _.keys(this.url); + if ( !(_.includes(queryModeValueSet, this.queryMode)) ) { + throw `查詢模式必須是 ${_.toString(queryModeValueSet)}`; + } + } + + async _replaceUrlParameter() { + _.forEach(_.get(this.url, this.queryMode), (urlTemplate, key) => { + _.forEach(this.parameter, (valueOfParameter, keyOfParameter) => { + urlTemplate = _.replace(urlTemplate, keyOfParameter, valueOfParameter); + }); + _.set(_.get(this.url, this.queryMode), key, urlTemplate); + }); + } + + async _getMetadata(metadataURL) { + return await this._getJsonResponseFromURL(metadataURL); + } + + async _getJsonResponseFromURL(url) { + let result = undefined; + let response = undefined; + + if (this.isUseToken) { + response = await fetch(url, { + headers: this.tokenObject + }); + } else { + response = await fetch(url); + } + + result = await response.json(); + + return result; + } +} + +export default Study \ No newline at end of file diff --git a/test.js b/test.js index 7be8c79..e0f0fa1 100644 --- a/test.js +++ b/test.js @@ -1,7 +1,5 @@ //引入套件 -import _ from "lodash"; import WADO from "./DICOMwebWADORSURI.js"; -import fs from "fs"; //實體化 let wado = new WADO(); @@ -29,11 +27,16 @@ let wado = new WADO(); await wado.init(); //只查詢到 Series - await wado.querySeriesByStudy(); - - //查詢 Series 底下所有的 Instance 還有底下所有的 Frame - await wado.queryInstanceBySeries(); - // console.log(wado.response); + // await wado.querySeries(); + + //只渲染指定的 Series 底下的所有 URL + // let seriesInstanceUID = "1.3.46.670589.45.1.1.4993912214784.1.5436.1538560606509.3"; + // await wado.renderSpecificSeries(seriesInstanceUID); + + //渲染全 Series 底下的所有 URL + await wado.renderAllSeries(); + + // fs.writeFile("response.json", JSON.stringify(wado.response), (err) => { if (err) throw err; }); })();