feat(WADO) Allow WADO / QIDO requests to originate from client (#222)
This commit is contained in:
parent
92408d4c86
commit
ebe0a40f3e
@ -39,6 +39,7 @@ ohif:cornerstone-settings
|
|||||||
ohif:viewerbase
|
ohif:viewerbase
|
||||||
ohif:study-list
|
ohif:study-list
|
||||||
ohif:dicom-services
|
ohif:dicom-services
|
||||||
|
ohif:dicomweb-client
|
||||||
ohif:hanging-protocols
|
ohif:hanging-protocols
|
||||||
ohif:metadata
|
ohif:metadata
|
||||||
|
|
||||||
|
|||||||
@ -86,6 +86,7 @@ ohif:cornerstone@0.0.1
|
|||||||
ohif:cornerstone-settings@0.0.1
|
ohif:cornerstone-settings@0.0.1
|
||||||
ohif:design@0.0.1
|
ohif:design@0.0.1
|
||||||
ohif:dicom-services@0.0.1
|
ohif:dicom-services@0.0.1
|
||||||
|
ohif:dicomweb-client@0.0.1
|
||||||
ohif:hanging-protocols@0.0.1
|
ohif:hanging-protocols@0.0.1
|
||||||
ohif:header@0.0.1
|
ohif:header@0.0.1
|
||||||
ohif:hotkeys@0.0.1
|
ohif:hotkeys@0.0.1
|
||||||
|
|||||||
@ -120,7 +120,7 @@ Template.viewer.onCreated(() => {
|
|||||||
const studyMetadata = new OHIF.metadata.StudyMetadata(study, study.studyInstanceUid);
|
const studyMetadata = new OHIF.metadata.StudyMetadata(study, study.studyInstanceUid);
|
||||||
let displaySets = study.displaySets;
|
let displaySets = study.displaySets;
|
||||||
|
|
||||||
if(!study.displaySets) {
|
if (!study.displaySets) {
|
||||||
displaySets = OHIF.viewerbase.sortingManager.getDisplaySets(studyMetadata);
|
displaySets = OHIF.viewerbase.sortingManager.getDisplaySets(studyMetadata);
|
||||||
study.displaySets = displaySets;
|
study.displaySets = displaySets;
|
||||||
}
|
}
|
||||||
@ -131,6 +131,9 @@ Template.viewer.onCreated(() => {
|
|||||||
OHIF.viewer.Studies.insert(study);
|
OHIF.viewer.Studies.insert(study);
|
||||||
OHIF.viewer.StudyMetadataList.insert(studyMetadata);
|
OHIF.viewer.StudyMetadataList.insert(studyMetadata);
|
||||||
OHIF.viewer.data.studyInstanceUids.push(study.studyInstanceUid);
|
OHIF.viewer.data.studyInstanceUids.push(study.studyInstanceUid);
|
||||||
|
|
||||||
|
// Updates WADO-RS metaDataManager
|
||||||
|
OHIF.viewerbase.updateMetaDataManager(study);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,13 @@ import { OHIF } from 'meteor/ohif:core';
|
|||||||
|
|
||||||
Router.configure({
|
Router.configure({
|
||||||
layoutTemplate: 'layout',
|
layoutTemplate: 'layout',
|
||||||
loadingTemplate: 'layout'
|
});
|
||||||
|
|
||||||
|
Router.waitOn(function() {
|
||||||
|
return [
|
||||||
|
Meteor.subscribe('servers'),
|
||||||
|
Meteor.subscribe('currentServer')
|
||||||
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
Router.onBeforeAction('loading');
|
Router.onBeforeAction('loading');
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
Package.describe({
|
Package.describe({
|
||||||
name: 'ohif:dicom-services',
|
name: 'ohif:dicom-services',
|
||||||
summary: 'DICOM Services: DICOMWeb and DIMSE C-Service functions',
|
summary: 'DICOM Services: DIMSE C-Service functions',
|
||||||
version: '0.0.1'
|
version: '0.0.1'
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -10,18 +10,6 @@ Package.onUse(function(api) {
|
|||||||
api.use('http');
|
api.use('http');
|
||||||
api.use('ecmascript');
|
api.use('ecmascript');
|
||||||
|
|
||||||
// DICOMWeb functions
|
|
||||||
api.addFiles('server/DICOMWeb/namespace.js', 'server');
|
|
||||||
api.addFiles('server/DICOMWeb/getJSON.js', 'server');
|
|
||||||
api.addFiles('server/DICOMWeb/getName.js', 'server');
|
|
||||||
api.addFiles('server/DICOMWeb/getNumber.js', 'server');
|
|
||||||
api.addFiles('server/DICOMWeb/getString.js', 'server');
|
|
||||||
api.addFiles('server/DICOMWeb/getModalities.js', 'server');
|
|
||||||
api.addFiles('server/DICOMWeb/getAttribute.js', 'server');
|
|
||||||
api.addFiles('server/DICOMWeb/getBulkData.js', 'server');
|
|
||||||
|
|
||||||
api.export('DICOMWeb', 'server');
|
|
||||||
|
|
||||||
// DIMSE functions
|
// DIMSE functions
|
||||||
api.addFiles('server/DIMSE/require.js', 'server');
|
api.addFiles('server/DIMSE/require.js', 'server');
|
||||||
api.addFiles('server/DIMSE/constants.js', 'server');
|
api.addFiles('server/DIMSE/constants.js', 'server');
|
||||||
|
|||||||
@ -1,230 +0,0 @@
|
|||||||
import { OHIF } from 'meteor/ohif:core';
|
|
||||||
|
|
||||||
const ASCII = 'ascii';
|
|
||||||
const http = Npm.require('http')
|
|
||||||
const url = Npm.require('url');
|
|
||||||
|
|
||||||
function getMultipartContentInfo(headers) {
|
|
||||||
|
|
||||||
let dict = null,
|
|
||||||
multipartRegex = /^\s*multipart\/[^;]+/g,
|
|
||||||
contentType = headers['content-type'];
|
|
||||||
|
|
||||||
if (typeof contentType === 'string' && multipartRegex.test(contentType)) {
|
|
||||||
let match,
|
|
||||||
pairRegex = /;\s*([^=]+)=([^;\r\n]+)/g;
|
|
||||||
pairRegex.lastIndex = multipartRegex.lastIndex;
|
|
||||||
while ((match = pairRegex.exec(contentType)) !== null) {
|
|
||||||
let key = match[1], value = match[2];
|
|
||||||
if (dict === null) {
|
|
||||||
dict = {};
|
|
||||||
}
|
|
||||||
dict[key] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return dict;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseContentHeader(data, offset) {
|
|
||||||
|
|
||||||
let endOfHeader = data.indexOf('\r\n\r\n', offset, ASCII);
|
|
||||||
|
|
||||||
if (endOfHeader < 0 || endOfHeader <= offset || endOfHeader > data.length) {
|
|
||||||
throw {
|
|
||||||
name: 'DICOMWebContentParsingError',
|
|
||||||
message: 'End of content header cannot be determined...'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let match,
|
|
||||||
header = {
|
|
||||||
start: offset,
|
|
||||||
end: endOfHeader + 4,
|
|
||||||
fields: {}
|
|
||||||
},
|
|
||||||
headerRegex = /([\w\-]+):\s*([^\r\n]+)(?:\r\n)?/g,
|
|
||||||
headerContentRegex = /\t([^\r\n]+)(?:\r\n)?/g,
|
|
||||||
headerString = data.toString('ascii', offset, endOfHeader);
|
|
||||||
|
|
||||||
while ((match = headerRegex.exec(headerString)) !== null) {
|
|
||||||
let key = match[1].toLowerCase(), value = match[2];
|
|
||||||
while (headerString.charAt(headerRegex.lastIndex) === '\t') {
|
|
||||||
headerContentRegex.lastIndex = headerRegex.lastIndex;
|
|
||||||
if ((match = headerContentRegex.exec(headerString)) !== null) {
|
|
||||||
headerRegex.lastIndex = headerContentRegex.lastIndex;
|
|
||||||
value += ' ' + match[1];
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
header.fields[key] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return header;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseResponse(headers, data) {
|
|
||||||
|
|
||||||
let contentInfo = getMultipartContentInfo(headers);
|
|
||||||
|
|
||||||
if (!contentInfo || !contentInfo.boundary) {
|
|
||||||
throw {
|
|
||||||
name: 'DICOMWebContentParsingError',
|
|
||||||
message: 'Content boundary not specified...'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let boundary = contentInfo.boundary,
|
|
||||||
delimiter = '--' + boundary + '\r\n',
|
|
||||||
index = data.indexOf(delimiter, 0, ASCII);
|
|
||||||
|
|
||||||
if (index < 0) {
|
|
||||||
throw {
|
|
||||||
name: 'DICOMWebContentParsingError',
|
|
||||||
message: 'The specified boundary could not be found...'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let closingIndex, closingDelimiter = Buffer.from('\r\n--' + boundary + '--', ASCII);
|
|
||||||
|
|
||||||
index += delimiter.length;
|
|
||||||
if (data[index] !== 0x0D || data[index + 1] !== 0x0A) {
|
|
||||||
// multipart content headers are present, so let's parse them...
|
|
||||||
let contentHeader = parseContentHeader(data, index);
|
|
||||||
if (contentHeader && contentHeader.end > index) {
|
|
||||||
if (contentHeader.fields['content-length'] > 0) {
|
|
||||||
let endOfData = contentHeader.end + parseInt(contentHeader.fields['content-length'], 10);
|
|
||||||
if (endOfData === data.indexOf(closingDelimiter, endOfData)) {
|
|
||||||
// content-length is valid...
|
|
||||||
return data.slice(contentHeader.end, endOfData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
index = contentHeader.end;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// no headers, the content comes right after...
|
|
||||||
index += 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
closingIndex = data.indexOf(closingDelimiter, index);
|
|
||||||
if (closingIndex < 0 || closingIndex < index) {
|
|
||||||
throw {
|
|
||||||
name: 'DICOMWebContentParsingError',
|
|
||||||
message: 'The end of the content could not be determined...'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return data.slice(index, closingIndex);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeRequest(geturl, options, callback) {
|
|
||||||
const headers = 'multipart/related; type=application/octet-stream';
|
|
||||||
const parsed = url.parse(geturl);
|
|
||||||
|
|
||||||
let requestOpt = {
|
|
||||||
hostname: parsed.hostname,
|
|
||||||
headers: {
|
|
||||||
Accept: headers
|
|
||||||
},
|
|
||||||
path: parsed.path,
|
|
||||||
method: 'GET',
|
|
||||||
};
|
|
||||||
|
|
||||||
let requester;
|
|
||||||
if (parsed.protocol === 'https:') {
|
|
||||||
requester = https.request;
|
|
||||||
|
|
||||||
const allowUnauthorizedAgent = new https.Agent({ rejectUnauthorized: false });
|
|
||||||
requestOpt.agent = allowUnauthorizedAgent
|
|
||||||
} else {
|
|
||||||
requester = http.request;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parsed.port) {
|
|
||||||
requestOpt.port = parsed.port;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.auth) {
|
|
||||||
requestOpt.auth = options.auth;
|
|
||||||
}
|
|
||||||
|
|
||||||
let req = requester(requestOpt, function(resp) {
|
|
||||||
|
|
||||||
let data = [];
|
|
||||||
|
|
||||||
if (resp.statusCode !== 200) {
|
|
||||||
callback({
|
|
||||||
name: 'DICOMWebRequestError',
|
|
||||||
message: `Unexpected status code for DICOMWeb response (${resp.statusCode})...`
|
|
||||||
}, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
resp.on('data', function(chunk) {
|
|
||||||
data.push(chunk);
|
|
||||||
});
|
|
||||||
|
|
||||||
resp.on('error', function (responseError) {
|
|
||||||
OHIF.log.error('There was an error in the DICOMWeb Server');
|
|
||||||
OHIF.log.error(responseError.stack);
|
|
||||||
OHIF.log.trace();
|
|
||||||
|
|
||||||
callback(responseError, null);
|
|
||||||
});
|
|
||||||
|
|
||||||
resp.on('end', function() {
|
|
||||||
try {
|
|
||||||
callback(null, parseResponse(resp.headers, Buffer.concat(data)));
|
|
||||||
} catch (error) {
|
|
||||||
callback(error, null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
req.on('error', function (requestError) {
|
|
||||||
OHIF.log.error('Couldn\'t connect to DICOMWeb server.');
|
|
||||||
OHIF.log.error('Make sure you are trying to connect to the right server and that it is up and running.');
|
|
||||||
OHIF.log.error(requestError.stack);
|
|
||||||
OHIF.log.trace();
|
|
||||||
|
|
||||||
callback(requestError, null);
|
|
||||||
});
|
|
||||||
|
|
||||||
req.end();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const makeRequestSync = Meteor.wrapAsync(makeRequest);
|
|
||||||
|
|
||||||
// TODO: Unify this stuff with the getJSON code
|
|
||||||
DICOMWeb.getBulkData = function(geturl, options) {
|
|
||||||
|
|
||||||
if (options && options.logRequests) {
|
|
||||||
OHIF.log.info(geturl);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options && options.logTiming) {
|
|
||||||
console.time(geturl);
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = makeRequestSync(geturl, options);
|
|
||||||
|
|
||||||
if (options && options.logTiming) {
|
|
||||||
console.timeEnd(geturl);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options && options.logResponses) {
|
|
||||||
OHIF.log.info(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Buffer.isBuffer(result)) {
|
|
||||||
throw result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
|
|
||||||
};
|
|
||||||
@ -1,110 +0,0 @@
|
|||||||
import { Meteor } from 'meteor/meteor';
|
|
||||||
import { OHIF } from 'meteor/ohif:core';
|
|
||||||
|
|
||||||
const http = Npm.require('http');
|
|
||||||
const https = Npm.require('https');
|
|
||||||
const url = Npm.require('url');
|
|
||||||
|
|
||||||
function makeRequest(geturl, options, callback) {
|
|
||||||
const parsed = url.parse(geturl);
|
|
||||||
const jsonHeaders = ['application/json', 'application/dicom+json'];
|
|
||||||
|
|
||||||
let requestOpt = {
|
|
||||||
hostname: parsed.hostname,
|
|
||||||
headers: {
|
|
||||||
Accept: 'application/json'
|
|
||||||
},
|
|
||||||
path: parsed.path,
|
|
||||||
method: 'GET'
|
|
||||||
};
|
|
||||||
|
|
||||||
let requester;
|
|
||||||
if (parsed.protocol === 'https:') {
|
|
||||||
requester = https.request;
|
|
||||||
|
|
||||||
const allowUnauthorizedAgent = new https.Agent({ rejectUnauthorized: false });
|
|
||||||
requestOpt.agent = allowUnauthorizedAgent;
|
|
||||||
} else {
|
|
||||||
requester = http.request;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parsed.port) {
|
|
||||||
requestOpt.port = parsed.port;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.auth) {
|
|
||||||
requestOpt.auth = options.auth;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.headers) {
|
|
||||||
Object.keys(options.headers).forEach(key => {
|
|
||||||
const value = options.headers[key];
|
|
||||||
requestOpt.headers[key] = value;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const req = requester(requestOpt, function(resp) {
|
|
||||||
// TODO: handle errors with 400+ code
|
|
||||||
const contentType = (resp.headers['content-type'] || '').split(';')[0];
|
|
||||||
if (jsonHeaders.indexOf(contentType) === -1) {
|
|
||||||
const errorMessage = `We only support json but "${contentType}" was sent by the server`;
|
|
||||||
callback(new Error(errorMessage), null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let output = '';
|
|
||||||
|
|
||||||
resp.setEncoding('utf8');
|
|
||||||
|
|
||||||
resp.on('data', function(chunk){
|
|
||||||
output += chunk;
|
|
||||||
});
|
|
||||||
|
|
||||||
resp.on('error', function (responseError) {
|
|
||||||
OHIF.log.error('There was an error in the DICOMWeb Server')
|
|
||||||
OHIF.log.error(error.stack);
|
|
||||||
OHIF.log.trace();
|
|
||||||
|
|
||||||
callback(new Meteor.Error('server-internal-error', responseError.message), null);
|
|
||||||
});
|
|
||||||
|
|
||||||
resp.on('end', function(){
|
|
||||||
callback(null, { data: JSON.parse(output) });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
req.on('error', function (requestError) {
|
|
||||||
OHIF.log.error('Couldn\'t connect to DICOMWeb server.');
|
|
||||||
OHIF.log.error('Make sure you are trying to connect to the right server and that it is up and running.');
|
|
||||||
OHIF.log.error(requestError.stack);
|
|
||||||
OHIF.log.trace();
|
|
||||||
|
|
||||||
callback(new Meteor.Error('server-connection-error', requestError.message), null);
|
|
||||||
});
|
|
||||||
|
|
||||||
req.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
const makeRequestSync = Meteor.wrapAsync(makeRequest);
|
|
||||||
|
|
||||||
DICOMWeb.getJSON = function(geturl, options) {
|
|
||||||
if (options && options.logRequests) {
|
|
||||||
OHIF.log.info(geturl);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options && options.logTiming) {
|
|
||||||
console.time(geturl);
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = makeRequestSync(geturl, options);
|
|
||||||
|
|
||||||
if (options && options.logTiming) {
|
|
||||||
console.timeEnd(geturl);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options && options.logResponses) {
|
|
||||||
OHIF.log.info(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
@ -1 +0,0 @@
|
|||||||
DICOMWeb = {};
|
|
||||||
21
Packages/ohif-dicomweb-client/package.js
Normal file
21
Packages/ohif-dicomweb-client/package.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
Package.describe({
|
||||||
|
name: 'ohif:dicomweb-client',
|
||||||
|
summary: 'DICOM Services: DICOMWeb',
|
||||||
|
version: '0.0.1'
|
||||||
|
});
|
||||||
|
|
||||||
|
Npm.depends({
|
||||||
|
'url-parse': '1.4.1',
|
||||||
|
'isomorphic-fetch': '2.2.1',
|
||||||
|
});
|
||||||
|
|
||||||
|
Package.onUse(function(api) {
|
||||||
|
api.versionsFrom('1.6');
|
||||||
|
|
||||||
|
api.use('ecmascript');
|
||||||
|
|
||||||
|
// DICOMWeb functions
|
||||||
|
api.mainModule('src/index.js');
|
||||||
|
|
||||||
|
api.export('DICOMWeb');
|
||||||
|
});
|
||||||
33
Packages/ohif-dicomweb-client/src/get.js
Normal file
33
Packages/ohif-dicomweb-client/src/get.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import makeRequest from "./makeRequest";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a request to the URL given a set of options.
|
||||||
|
* This function will add optional timing, request, and respone logging.
|
||||||
|
*
|
||||||
|
* @param {String} url
|
||||||
|
* @param {Object} options
|
||||||
|
* @return {Promise<*>}
|
||||||
|
*/
|
||||||
|
const get = async function get(url, options) {
|
||||||
|
if (options.logRequests) {
|
||||||
|
console.log(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.logTiming) {
|
||||||
|
console.time(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await makeRequest(url, options);
|
||||||
|
|
||||||
|
if (options.logTiming) {
|
||||||
|
console.timeEnd(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.logResponses) {
|
||||||
|
console.info(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default get;
|
||||||
@ -5,7 +5,7 @@
|
|||||||
* @param [defaultValue] - The value to return if the element is not present
|
* @param [defaultValue] - The value to return if the element is not present
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
DICOMWeb.getAttribute = function(element, defaultValue) {
|
const getAttribute = function(element, defaultValue) {
|
||||||
if (!element) {
|
if (!element) {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
@ -42,3 +42,5 @@ function convertToInt(input) {
|
|||||||
|
|
||||||
return parseInt(output, 16);
|
return parseInt(output, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default getAttribute;
|
||||||
133
Packages/ohif-dicomweb-client/src/getBulkData.js
Normal file
133
Packages/ohif-dicomweb-client/src/getBulkData.js
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
import get from './get.js';
|
||||||
|
|
||||||
|
const ASCII = 'ascii';
|
||||||
|
|
||||||
|
function getMultipartContentInfo(headers) {
|
||||||
|
const multipartRegex = /^\s*multipart\/[^;]+/g;
|
||||||
|
const contentType = headers.get('content-type');
|
||||||
|
let dict = null;
|
||||||
|
|
||||||
|
if (typeof contentType === 'string' && multipartRegex.test(contentType)) {
|
||||||
|
let match;
|
||||||
|
let pairRegex = /;\s*([^=]+)=([^;\r\n]+)/g;
|
||||||
|
|
||||||
|
pairRegex.lastIndex = multipartRegex.lastIndex;
|
||||||
|
while ((match = pairRegex.exec(contentType)) !== null) {
|
||||||
|
let key = match[1]
|
||||||
|
let value = match[2];
|
||||||
|
|
||||||
|
if (dict === null) {
|
||||||
|
dict = {};
|
||||||
|
}
|
||||||
|
dict[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseContentHeader(data, offset) {
|
||||||
|
const endOfHeader = data.indexOf('\r\n\r\n', offset);
|
||||||
|
|
||||||
|
if (endOfHeader < 0 || endOfHeader <= offset || endOfHeader > data.length) {
|
||||||
|
throw new Error('End of content header cannot be determined...');
|
||||||
|
}
|
||||||
|
|
||||||
|
const header = {
|
||||||
|
start: offset,
|
||||||
|
end: endOfHeader + 4,
|
||||||
|
fields: {}
|
||||||
|
};
|
||||||
|
const headerRegex = /([\w\-]+):\s*([^\r\n]+)(?:\r\n)?/g;
|
||||||
|
const headerContentRegex = /\t([^\r\n]+)(?:\r\n)?/g;
|
||||||
|
const headerString = data.slice(offset, endOfHeader);
|
||||||
|
|
||||||
|
let match;
|
||||||
|
|
||||||
|
while ((match = headerRegex.exec(headerString)) !== null) {
|
||||||
|
const key = match[1].toLowerCase(), value = match[2];
|
||||||
|
|
||||||
|
while (headerString.charAt(headerRegex.lastIndex) === '\t') {
|
||||||
|
headerContentRegex.lastIndex = headerRegex.lastIndex;
|
||||||
|
|
||||||
|
if ((match = headerContentRegex.exec(headerString)) !== null) {
|
||||||
|
headerRegex.lastIndex = headerContentRegex.lastIndex;
|
||||||
|
value += ' ' + match[1];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
header.fields[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return header;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function toBytes(input) {
|
||||||
|
const data = [];
|
||||||
|
for (let i = 0; i < input.length; i++){
|
||||||
|
data.push(input.charCodeAt(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function parseResponse(headers, data) {
|
||||||
|
const contentInfo = getMultipartContentInfo(headers);
|
||||||
|
|
||||||
|
if (!contentInfo || !contentInfo.boundary) {
|
||||||
|
throw new Error('Content boundary not specified...');
|
||||||
|
}
|
||||||
|
|
||||||
|
const boundary = contentInfo.boundary;
|
||||||
|
const delimiter = '--' + boundary + '\r\n';
|
||||||
|
let index = data.indexOf(delimiter, 0, ASCII);
|
||||||
|
|
||||||
|
if (index < 0) {
|
||||||
|
throw new Error('DICOMWeb: The specified boundary could not be found...');
|
||||||
|
}
|
||||||
|
|
||||||
|
let closingIndex
|
||||||
|
let closingDelimiter = '\r\n--' + boundary + '--';
|
||||||
|
|
||||||
|
index += delimiter.length;
|
||||||
|
if (data[index] !== 0x0D || data[index + 1] !== 0x0A) {
|
||||||
|
// multipart content headers are present, so let's parse them...
|
||||||
|
let contentHeader = parseContentHeader(data, index);
|
||||||
|
if (contentHeader && contentHeader.end > index) {
|
||||||
|
if (contentHeader.fields['content-length'] > 0) {
|
||||||
|
let endOfData = contentHeader.end + parseInt(contentHeader.fields['content-length'], 10);
|
||||||
|
if (endOfData === data.indexOf(closingDelimiter, endOfData)) {
|
||||||
|
// content-length is valid...
|
||||||
|
return data.slice(contentHeader.end, endOfData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
index = contentHeader.end;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// no headers, the content comes right after...
|
||||||
|
index += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
closingIndex = data.indexOf(closingDelimiter, index);
|
||||||
|
if (closingIndex < 0 || closingIndex < index) {
|
||||||
|
throw new Error('DICOMWeb: The end of the content could not be determined...')
|
||||||
|
}
|
||||||
|
|
||||||
|
return toBytes(data.slice(index, closingIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
const getBulkData = async function(url, options = {}) {
|
||||||
|
options.headers = options.headers || {};
|
||||||
|
options.headers.Accept = 'multipart/related; type=application/octet-stream';
|
||||||
|
|
||||||
|
const response = await get(url, options);
|
||||||
|
const data = await response.text();
|
||||||
|
|
||||||
|
return parseResponse(response.headers, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default getBulkData;
|
||||||
16
Packages/ohif-dicomweb-client/src/getJSON.js
Normal file
16
Packages/ohif-dicomweb-client/src/getJSON.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import get from './get.js';
|
||||||
|
import getAttribute from "./getAttribute";
|
||||||
|
|
||||||
|
const getJSON = async function (url, options) {
|
||||||
|
options.headers = options.headers || {};
|
||||||
|
options.headers.Accept = 'application/json';
|
||||||
|
return get(url, options).then(response => {
|
||||||
|
if (!(response instanceof Response)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.json();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getJSON;
|
||||||
@ -1,4 +1,4 @@
|
|||||||
DICOMWeb.getModalities = function(modality, modalitiesInStudy) {
|
const getModalities = function(modality, modalitiesInStudy) {
|
||||||
var modalities = {};
|
var modalities = {};
|
||||||
if (modality) {
|
if (modality) {
|
||||||
modalities = modality;
|
modalities = modality;
|
||||||
@ -19,3 +19,5 @@ DICOMWeb.getModalities = function(modality, modalitiesInStudy) {
|
|||||||
}
|
}
|
||||||
return modalities;
|
return modalities;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default getModalities;
|
||||||
@ -5,7 +5,7 @@
|
|||||||
* @param [defaultValue] - The default value to return if the element is not found
|
* @param [defaultValue] - The default value to return if the element is not found
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
DICOMWeb.getName = function(element, defaultValue) {
|
export default function getName(element, defaultValue) {
|
||||||
if (!element) {
|
if (!element) {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
@ -4,7 +4,7 @@
|
|||||||
* @param [defaultValue] - The default value to return if the element does not exist
|
* @param [defaultValue] - The default value to return if the element does not exist
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
DICOMWeb.getNumber = function(element, defaultValue) {
|
export default function getNumber(element, defaultValue) {
|
||||||
if (!element) {
|
if (!element) {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
@ -5,7 +5,7 @@
|
|||||||
* @param [defaultValue] - The value to return if the element is not present
|
* @param [defaultValue] - The value to return if the element is not present
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
DICOMWeb.getString = function(element, defaultValue) {
|
export default function getString(element, defaultValue) {
|
||||||
if (!element) {
|
if (!element) {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
23
Packages/ohif-dicomweb-client/src/index.js
Normal file
23
Packages/ohif-dicomweb-client/src/index.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import get from './get.js';
|
||||||
|
import getAttribute from './getAttribute.js';
|
||||||
|
import getBulkData from './getBulkData.js';
|
||||||
|
import getJSON from './getJSON.js';
|
||||||
|
import getModalities from './getModalities.js';
|
||||||
|
import getName from './getName.js';
|
||||||
|
import getNumber from './getNumber.js';
|
||||||
|
import getString from './getString.js';
|
||||||
|
import makeRequest from './makeRequest.js';
|
||||||
|
|
||||||
|
const DICOMWeb = {
|
||||||
|
get,
|
||||||
|
getAttribute,
|
||||||
|
getBulkData,
|
||||||
|
getJSON,
|
||||||
|
getModalities,
|
||||||
|
getName,
|
||||||
|
getNumber,
|
||||||
|
getString,
|
||||||
|
makeRequest
|
||||||
|
};
|
||||||
|
|
||||||
|
export { DICOMWeb };
|
||||||
60
Packages/ohif-dicomweb-client/src/makeRequest.js
Normal file
60
Packages/ohif-dicomweb-client/src/makeRequest.js
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import { Meteor } from "meteor/meteor";
|
||||||
|
import URL from 'url-parse';
|
||||||
|
import 'isomorphic-fetch';
|
||||||
|
|
||||||
|
async function makeRequest(url, options) {
|
||||||
|
const parsed = new URL(url);
|
||||||
|
|
||||||
|
let requestOpt = {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: Clean this up
|
||||||
|
const accessToken = false //Meteor.user().services.keycloak.accessToken;
|
||||||
|
if (accessToken) {
|
||||||
|
requestOpt.headers = {
|
||||||
|
Authorization: `Bearer ${accessToken}`
|
||||||
|
};
|
||||||
|
} else if (requestOpt.auth) {
|
||||||
|
requestOpt.headers = {
|
||||||
|
Authorization: requestOpt.auth
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.headers) {
|
||||||
|
Object.keys(options.headers).forEach(key => {
|
||||||
|
requestOpt.headers[key] = options.headers[key];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// TODO: Kept getting weird build errors from RegExp
|
||||||
|
const isAbsolute = parsed.href.indexOf('http://') === 0 || parsed.href.indexOf('https://');
|
||||||
|
|
||||||
|
let url = parsed.href;
|
||||||
|
|
||||||
|
if (isAbsolute === false) {
|
||||||
|
url = Meteor.absoluteUrl(parsed.href);
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch(url, requestOpt).then((response) => {
|
||||||
|
if (response.status >= 400) {
|
||||||
|
reject(new Error(response.status));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.status === 204) {
|
||||||
|
resolve([]);
|
||||||
|
} else {
|
||||||
|
// TODO: Handle 204 no content
|
||||||
|
resolve(response);
|
||||||
|
}
|
||||||
|
}, (error) => {
|
||||||
|
console.error('There was an error in the DICOMWeb Server');
|
||||||
|
|
||||||
|
reject(new Error(error));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default makeRequest;
|
||||||
@ -29,6 +29,12 @@ export const DICOMWebRequestOptions = new SimpleSchema({
|
|||||||
defaultValue: 'orthanc:orthanc',
|
defaultValue: 'orthanc:orthanc',
|
||||||
optional: true
|
optional: true
|
||||||
},
|
},
|
||||||
|
requestFromBrowser: {
|
||||||
|
type: Boolean,
|
||||||
|
label: 'Make DICOMWeb requests from the Browser',
|
||||||
|
defaultValue: false,
|
||||||
|
optional: true
|
||||||
|
},
|
||||||
logRequests: {
|
logRequests: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
defaultValue: true,
|
defaultValue: true,
|
||||||
@ -76,7 +82,7 @@ export const DICOMWebServer = new SimpleSchema({
|
|||||||
},
|
},
|
||||||
qidoSupportsIncludeField: {
|
qidoSupportsIncludeField: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
label: 'QIDO supports including fields',
|
label: 'QIDO supports "includefield" query key',
|
||||||
defaultValue: false
|
defaultValue: false
|
||||||
},
|
},
|
||||||
requestOptions: {
|
requestOptions: {
|
||||||
|
|||||||
@ -4,11 +4,11 @@
|
|||||||
{{>inputText labelClass='form-group' key='wadoUriRoot'}}
|
{{>inputText labelClass='form-group' key='wadoUriRoot'}}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-6">
|
<div class="col-lg-6">
|
||||||
{{>inputText labelClass='form-group' key='wadoRoot'}}
|
{{>inputSelect labelClass='form-group' key='imageRendering' hideSearch=true
|
||||||
|
options=(clone placeholder='Select an option')}}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-6">
|
<div class="col-lg-6">
|
||||||
{{>inputSelect labelClass='form-group' key='imageRendering' hideSearch=true
|
{{>inputText labelClass='form-group' key='wadoRoot'}}
|
||||||
options=(clone placeholder='Select an option')}}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-6">
|
<div class="col-lg-6">
|
||||||
{{>inputSelect labelClass='form-group' key='thumbnailRendering' hideSearch=true
|
{{>inputSelect labelClass='form-group' key='thumbnailRendering' hideSearch=true
|
||||||
@ -30,9 +30,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
<h4>Request options</h4>
|
<h4>Request options</h4>
|
||||||
{{#group class='row' key='requestOptions'}}
|
{{#group class='row p-y-1' key='requestOptions'}}
|
||||||
<div class="col-lg-6">
|
<div class="col-lg-6">
|
||||||
{{>inputText labelClass='form-group' key='auth'}}
|
{{>inputText labelClass='form-group' key='auth'}}
|
||||||
|
<div class="checkbox pull-left">
|
||||||
|
{{>inputCheckbox key='requestFromBrowser'}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-6">
|
<div class="col-lg-6">
|
||||||
<strong>Logging options</strong>
|
<strong>Logging options</strong>
|
||||||
|
|||||||
@ -0,0 +1,2 @@
|
|||||||
|
import './lib';
|
||||||
|
import './services';
|
||||||
@ -1 +1,2 @@
|
|||||||
import './encodeQueryData.js';
|
import './encodeQueryData.js';
|
||||||
|
import './parseFloatArray.js';
|
||||||
6
Packages/ohif-studies/imports/both/services/index.js
Normal file
6
Packages/ohif-studies/imports/both/services/index.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import './namespace';
|
||||||
|
|
||||||
|
// DICOMWeb instance, study, and metadata retrieval
|
||||||
|
import './qido/instances.js';
|
||||||
|
import './qido/studies.js';
|
||||||
|
import './wado/retrieveMetadata.js';
|
||||||
6
Packages/ohif-studies/imports/both/services/namespace.js
Normal file
6
Packages/ohif-studies/imports/both/services/namespace.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
|
|
||||||
|
OHIF.studies.services = {
|
||||||
|
QIDO: {},
|
||||||
|
WADO: {}
|
||||||
|
};
|
||||||
@ -1,4 +1,5 @@
|
|||||||
import { OHIF } from 'meteor/ohif:core';
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
|
import { DICOMWeb } from 'meteor/ohif:dicomweb-client';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a QIDO URL given the server settings and a study instance UID
|
* Creates a QIDO URL given the server settings and a study instance UID
|
||||||
@ -1,4 +1,5 @@
|
|||||||
import { OHIF } from 'meteor/ohif:core';
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
|
import { DICOMWeb } from 'meteor/ohif:dicomweb-client';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a QIDO date string for a date range query
|
* Creates a QIDO date string for a date range query
|
||||||
@ -99,13 +100,13 @@ function resultDataToStudies(resultData) {
|
|||||||
OHIF.studies.services.QIDO.Studies = (server, filter) => {
|
OHIF.studies.services.QIDO.Studies = (server, filter) => {
|
||||||
const url = filterToQIDOURL(server, filter);
|
const url = filterToQIDOURL(server, filter);
|
||||||
|
|
||||||
try {
|
return new Promise((resolve, reject) => {
|
||||||
const result = DICOMWeb.getJSON(url, server.requestOptions);
|
console.warn(DICOMWeb);
|
||||||
|
|
||||||
return resultDataToStudies(result.data);
|
DICOMWeb.getJSON(url, server.requestOptions).then(result => {
|
||||||
} catch (error) {
|
const studies = resultDataToStudies(result);
|
||||||
OHIF.log.trace();
|
|
||||||
|
|
||||||
throw error;
|
resolve(studies);
|
||||||
}
|
}, reject);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
@ -1,4 +1,5 @@
|
|||||||
import { OHIF } from 'meteor/ohif:core';
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
|
import { DICOMWeb } from 'meteor/ohif:dicomweb-client';
|
||||||
import { parseFloatArray } from '../../lib/parseFloatArray';
|
import { parseFloatArray } from '../../lib/parseFloatArray';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -67,9 +68,8 @@ function buildInstanceWadoUrl(server, studyInstanceUid, seriesInstanceUid, sopIn
|
|||||||
params.push('transferSyntax=*');
|
params.push('transferSyntax=*');
|
||||||
|
|
||||||
const paramString = params.join('&');
|
const paramString = params.join('&');
|
||||||
const uri = encodeURIComponent(paramString);
|
|
||||||
|
|
||||||
return `${server.wadoUriRoot}?${uri}`;
|
return `${server.wadoUriRoot}?${paramString}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildInstanceWadoRsUri(server, studyInstanceUid, seriesInstanceUid, sopInstanceUid) {
|
function buildInstanceWadoRsUri(server, studyInstanceUid, seriesInstanceUid, sopInstanceUid) {
|
||||||
@ -106,17 +106,19 @@ function getPaletteColor(server, instance, tag, lutDescriptor) {
|
|||||||
const numLutEntries = lutDescriptor[0];
|
const numLutEntries = lutDescriptor[0];
|
||||||
const bits = lutDescriptor[2];
|
const bits = lutDescriptor[2];
|
||||||
const uri = WADOProxy.convertURL(instance[tag].BulkDataURI, server)
|
const uri = WADOProxy.convertURL(instance[tag].BulkDataURI, server)
|
||||||
const data = DICOMWeb.getBulkData(uri);
|
const bulkDataPromise = DICOMWeb.getBulkData(uri);
|
||||||
|
|
||||||
for (var i = 0; i < numLutEntries; i++) {
|
return bulkDataPromise.then(data => {
|
||||||
if(bits === 16) {
|
for (var i = 0; i < numLutEntries; i++) {
|
||||||
lut[i] = data.readUInt16LE(i*2);
|
if(bits === 16) {
|
||||||
} else {
|
lut[i] = data[i * 65536] + data[i + 1];
|
||||||
lut[i] = data.readUInt8(i);
|
} else {
|
||||||
|
lut[i] = data[i];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return lut;
|
return lut;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -126,38 +128,33 @@ function getPaletteColor(server, instance, tag, lutDescriptor) {
|
|||||||
* @param instance {Object} The retrieved instance metadata;
|
* @param instance {Object} The retrieved instance metadata;
|
||||||
* @returns {String} The ReferenceSOPInstanceUID
|
* @returns {String} The ReferenceSOPInstanceUID
|
||||||
*/
|
*/
|
||||||
function getPaletteColors(server, instance, lutDescriptor) {
|
async function getPaletteColors(server, instance, lutDescriptor) {
|
||||||
|
let entry = null;
|
||||||
|
let paletteUID = DICOMWeb.getString(instance['00281199']);
|
||||||
|
|
||||||
let entry = null,
|
return new Promise((resolve, reject) => {
|
||||||
paletteUID = DICOMWeb.getString(instance['00281199']);
|
if (paletteColorCache.isValidUID(paletteUID)) {
|
||||||
|
entry = paletteColorCache.get(paletteUID);
|
||||||
if (paletteColorCache.isValidUID(paletteUID)) {
|
return resolve(entry);
|
||||||
entry = paletteColorCache.get(paletteUID);
|
|
||||||
} else {
|
|
||||||
paletteUID = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!entry) {
|
|
||||||
// no entry on cache... Fetch remote data.
|
|
||||||
try {
|
|
||||||
let r, g, b;
|
|
||||||
r = getPaletteColor(server, instance, '00281201', lutDescriptor);
|
|
||||||
g = getPaletteColor(server, instance, '00281202', lutDescriptor);;
|
|
||||||
b = getPaletteColor(server, instance, '00281203', lutDescriptor);;
|
|
||||||
|
|
||||||
entry = { red: r, green: g, blue: b };
|
|
||||||
if (paletteUID !== null) {
|
|
||||||
// when paletteUID is present, the entry can be cached...
|
|
||||||
entry.uid = paletteUID;
|
|
||||||
paletteColorCache.add(entry);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
OHIF.log.error(`(${error.name}) ${error.message}`);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return entry;
|
// no entry in cache... Fetch remote data.
|
||||||
|
const r = getPaletteColor(server, instance, '00281201', lutDescriptor);
|
||||||
|
const g = getPaletteColor(server, instance, '00281202', lutDescriptor);;
|
||||||
|
const b = getPaletteColor(server, instance, '00281203', lutDescriptor);;
|
||||||
|
|
||||||
|
const promises = [r, g, b];
|
||||||
|
|
||||||
|
Promise.all(promises).then((args) => {
|
||||||
|
entry = { red: r, green: g, blue: b };
|
||||||
|
|
||||||
|
// when paletteUID is present, the entry can be cached...
|
||||||
|
entry.uid = paletteUID;
|
||||||
|
paletteColorCache.add(entry);
|
||||||
|
|
||||||
|
resolve(entry);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFrameIncrementPointer(element) {
|
function getFrameIncrementPointer(element) {
|
||||||
@ -204,21 +201,27 @@ function getRadiopharmaceuticalInfo(instance) {
|
|||||||
* @param resultData
|
* @param resultData
|
||||||
* @returns {{seriesList: Array, patientName: *, patientId: *, accessionNumber: *, studyDate: *, modalities: *, studyDescription: *, imageCount: *, studyInstanceUid: *}}
|
* @returns {{seriesList: Array, patientName: *, patientId: *, accessionNumber: *, studyDate: *, modalities: *, studyDescription: *, imageCount: *, studyInstanceUid: *}}
|
||||||
*/
|
*/
|
||||||
function resultDataToStudyMetadata(server, studyInstanceUid, resultData) {
|
async function resultDataToStudyMetadata(server, studyInstanceUid, resultData) {
|
||||||
var seriesMap = {};
|
const seriesMap = {};
|
||||||
var seriesList = [];
|
const seriesList = [];
|
||||||
|
|
||||||
|
if (!WADOProxy) {
|
||||||
|
WADOProxy = {
|
||||||
|
convertURL: (a => a)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (!resultData.length) {
|
if (!resultData.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var anInstance = resultData[0];
|
const anInstance = resultData[0];
|
||||||
if (!anInstance) {
|
if (!anInstance) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var studyData = {
|
const studyData = {
|
||||||
seriesList: seriesList,
|
seriesList,
|
||||||
patientName: DICOMWeb.getName(anInstance['00100010']),
|
patientName: DICOMWeb.getName(anInstance['00100010']),
|
||||||
patientId: DICOMWeb.getString(anInstance['00100020']),
|
patientId: DICOMWeb.getString(anInstance['00100020']),
|
||||||
patientAge: DICOMWeb.getNumber(anInstance['00101010']),
|
patientAge: DICOMWeb.getNumber(anInstance['00101010']),
|
||||||
@ -233,7 +236,7 @@ function resultDataToStudyMetadata(server, studyInstanceUid, resultData) {
|
|||||||
institutionName: DICOMWeb.getString(anInstance['00080080'])
|
institutionName: DICOMWeb.getString(anInstance['00080080'])
|
||||||
};
|
};
|
||||||
|
|
||||||
resultData.forEach(function(instance) {
|
await Promise.all(resultData.map(async function(instance) {
|
||||||
var seriesInstanceUid = DICOMWeb.getString(instance['0020000E']);
|
var seriesInstanceUid = DICOMWeb.getString(instance['0020000E']);
|
||||||
var series = seriesMap[seriesInstanceUid];
|
var series = seriesMap[seriesInstanceUid];
|
||||||
if (!series) {
|
if (!series) {
|
||||||
@ -250,17 +253,16 @@ function resultDataToStudyMetadata(server, studyInstanceUid, resultData) {
|
|||||||
seriesList.push(series);
|
seriesList.push(series);
|
||||||
}
|
}
|
||||||
|
|
||||||
var sopInstanceUid = DICOMWeb.getString(instance['00080018']);
|
const sopInstanceUid = DICOMWeb.getString(instance['00080018']);
|
||||||
|
|
||||||
const wadouri = buildInstanceWadoUrl(server, studyInstanceUid, seriesInstanceUid, sopInstanceUid);
|
const wadouri = buildInstanceWadoUrl(server, studyInstanceUid, seriesInstanceUid, sopInstanceUid);
|
||||||
const baseWadoRsUri = buildInstanceWadoRsUri(server, studyInstanceUid, seriesInstanceUid, sopInstanceUid);
|
const baseWadoRsUri = buildInstanceWadoRsUri(server, studyInstanceUid, seriesInstanceUid, sopInstanceUid);
|
||||||
const wadorsuri = buildInstanceFrameWadoRsUri(server, studyInstanceUid, seriesInstanceUid, sopInstanceUid);
|
const wadorsuri = buildInstanceFrameWadoRsUri(server, studyInstanceUid, seriesInstanceUid, sopInstanceUid);
|
||||||
|
|
||||||
var instanceSummary = {
|
const instanceSummary = {
|
||||||
imageType: DICOMWeb.getString(instance['00080008']),
|
imageType: DICOMWeb.getString(instance['00080008']),
|
||||||
sopClassUid: DICOMWeb.getString(instance['00080016']),
|
sopClassUid: DICOMWeb.getString(instance['00080016']),
|
||||||
modality: DICOMWeb.getString(instance['00080060']),
|
modality: DICOMWeb.getString(instance['00080060']),
|
||||||
sopInstanceUid: sopInstanceUid,
|
sopInstanceUid,
|
||||||
instanceNumber: DICOMWeb.getNumber(instance['00200013']),
|
instanceNumber: DICOMWeb.getNumber(instance['00200013']),
|
||||||
imagePositionPatient: DICOMWeb.getString(instance['00200032']),
|
imagePositionPatient: DICOMWeb.getString(instance['00200032']),
|
||||||
imageOrientationPatient: DICOMWeb.getString(instance['00200037']),
|
imageOrientationPatient: DICOMWeb.getString(instance['00200037']),
|
||||||
@ -312,12 +314,13 @@ function resultDataToStudyMetadata(server, studyInstanceUid, resultData) {
|
|||||||
const redPaletteColorLookupTableDescriptor = parseFloatArray(DICOMWeb.getString(instance['00281101']));
|
const redPaletteColorLookupTableDescriptor = parseFloatArray(DICOMWeb.getString(instance['00281101']));
|
||||||
const greenPaletteColorLookupTableDescriptor = parseFloatArray(DICOMWeb.getString(instance['00281102']));
|
const greenPaletteColorLookupTableDescriptor = parseFloatArray(DICOMWeb.getString(instance['00281102']));
|
||||||
const bluePaletteColorLookupTableDescriptor = parseFloatArray(DICOMWeb.getString(instance['00281103']));
|
const bluePaletteColorLookupTableDescriptor = parseFloatArray(DICOMWeb.getString(instance['00281103']));
|
||||||
const palettes = getPaletteColors(server, instance, redPaletteColorLookupTableDescriptor);
|
const palettes = await getPaletteColors(server, instance, redPaletteColorLookupTableDescriptor);
|
||||||
|
|
||||||
if (palettes) {
|
if (palettes) {
|
||||||
if (palettes.uid) {
|
if (palettes.uid) {
|
||||||
instanceSummary.paletteColorLookupTableUID = palettes.uid;
|
instanceSummary.paletteColorLookupTableUID = palettes.uid;
|
||||||
}
|
}
|
||||||
|
|
||||||
instanceSummary.redPaletteColorLookupTableData = palettes.red;
|
instanceSummary.redPaletteColorLookupTableData = palettes.red;
|
||||||
instanceSummary.greenPaletteColorLookupTableData = palettes.green;
|
instanceSummary.greenPaletteColorLookupTableData = palettes.green;
|
||||||
instanceSummary.bluePaletteColorLookupTableData = palettes.blue;
|
instanceSummary.bluePaletteColorLookupTableData = palettes.blue;
|
||||||
@ -328,8 +331,7 @@ function resultDataToStudyMetadata(server, studyInstanceUid, resultData) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
series.instances.push(instanceSummary);
|
series.instances.push(instanceSummary);
|
||||||
|
}));
|
||||||
});
|
|
||||||
|
|
||||||
return studyData;
|
return studyData;
|
||||||
}
|
}
|
||||||
@ -338,26 +340,19 @@ function resultDataToStudyMetadata(server, studyInstanceUid, resultData) {
|
|||||||
* Retrieved Study MetaData from a DICOM server using a WADO call
|
* Retrieved Study MetaData from a DICOM server using a WADO call
|
||||||
* @param server
|
* @param server
|
||||||
* @param studyInstanceUid
|
* @param studyInstanceUid
|
||||||
* @returns {{seriesList: Array, patientName: *, patientId: *, accessionNumber: *, studyDate: *, modalities: *, studyDescription: *, imageCount: *, studyInstanceUid: *}}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
OHIF.studies.services.WADO.RetrieveMetadata = function(server, studyInstanceUid) {
|
OHIF.studies.services.WADO.RetrieveMetadata = async function(server, studyInstanceUid) {
|
||||||
var url = buildUrl(server, studyInstanceUid);
|
const url = buildUrl(server, studyInstanceUid);
|
||||||
|
|
||||||
try {
|
return new Promise((resolve, reject) => {
|
||||||
var result = DICOMWeb.getJSON(url, server.requestOptions);
|
DICOMWeb.getJSON(url, server.requestOptions).then(result => {
|
||||||
|
resultDataToStudyMetadata(server, studyInstanceUid, result).then((study) => {
|
||||||
|
study.wadoUriRoot = server.wadoUriRoot;
|
||||||
|
study.studyInstanceUid = studyInstanceUid;
|
||||||
|
|
||||||
var study = resultDataToStudyMetadata(server, studyInstanceUid, result.data);
|
resolve(study);
|
||||||
if (!study) {
|
}, reject);
|
||||||
study = {};
|
}, reject);
|
||||||
}
|
});
|
||||||
|
|
||||||
study.wadoUriRoot = server.wadoUriRoot;
|
|
||||||
study.studyInstanceUid = studyInstanceUid;
|
|
||||||
|
|
||||||
return study;
|
|
||||||
} catch (error) {
|
|
||||||
OHIF.log.trace();
|
|
||||||
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
@ -1,6 +1,7 @@
|
|||||||
import { ReactiveDict } from 'meteor/reactive-dict';
|
import { ReactiveDict } from 'meteor/reactive-dict';
|
||||||
import { Tracker } from 'meteor/tracker';
|
import { Tracker } from 'meteor/tracker';
|
||||||
import { OHIF } from 'meteor/ohif:core';
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
|
import { Meteor } from "meteor/meteor";
|
||||||
|
|
||||||
// Create a studies loaded state dictionary to enable reactivity. Values: loading|loaded|failed
|
// Create a studies loaded state dictionary to enable reactivity. Values: loading|loaded|failed
|
||||||
OHIF.studies.loadingDict = new ReactiveDict();
|
OHIF.studies.loadingDict = new ReactiveDict();
|
||||||
@ -24,16 +25,34 @@ OHIF.studies.loadStudy = studyInstanceUid => new Promise((resolve, reject) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return OHIF.studies.retrieveStudyMetadata(studyInstanceUid).then(study => {
|
return OHIF.studies.retrieveStudyMetadata(studyInstanceUid).then(study => {
|
||||||
// Add the display sets to the study if not present
|
if (window.HipaaLogger && Meteor.user && Meteor.user()) {
|
||||||
if (!study.displaySets) {
|
window.HipaaLogger.logEvent({
|
||||||
const displaySets = OHIF.viewerbase.sortingManager.getDisplaySets(study);
|
eventType: 'viewed',
|
||||||
study.displaySets = displaySets;
|
userId: Meteor.userId(),
|
||||||
study.setDisplaySets(displaySets);
|
userName: Meteor.user().profile.fullName,
|
||||||
study.forEachDisplaySet(displaySet => {
|
collectionName: 'Study',
|
||||||
OHIF.viewerbase.stackManager.makeAndAddStack(study, displaySet);
|
recordId: studyInstanceUid,
|
||||||
|
patientId: study.patientId,
|
||||||
|
patientName: study.patientName
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Once the data was retrieved, the series are sorted by series and instance number
|
||||||
|
OHIF.viewerbase.sortStudy(study);
|
||||||
|
|
||||||
|
// Updates WADO-RS metaDataManager
|
||||||
|
OHIF.viewerbase.updateMetaDataManager(study);
|
||||||
|
|
||||||
|
// Transform the study in a StudyMetadata object
|
||||||
|
const studyMetadata = new OHIF.metadata.StudyMetadata(study);
|
||||||
|
|
||||||
|
// Add the display sets to the study
|
||||||
|
study.displaySets = OHIF.viewerbase.sortingManager.getDisplaySets(studyMetadata);
|
||||||
|
study.displaySets.forEach(displaySet => {
|
||||||
|
OHIF.viewerbase.stackManager.makeAndAddStack(study, displaySet);
|
||||||
|
studyMetadata.addDisplaySet(displaySet);
|
||||||
|
});
|
||||||
|
|
||||||
// Double check to make sure this study wasn't already inserted into OHIF.viewer.Studies
|
// Double check to make sure this study wasn't already inserted into OHIF.viewer.Studies
|
||||||
// so we don't cause duplicate entry errors
|
// so we don't cause duplicate entry errors
|
||||||
const loaded = OHIF.viewer.Studies.findBy({ studyInstanceUid: study.studyInstanceUid });
|
const loaded = OHIF.viewer.Studies.findBy({ studyInstanceUid: study.studyInstanceUid });
|
||||||
|
|||||||
@ -6,6 +6,18 @@ import 'meteor/ohif:viewerbase';
|
|||||||
// promises and prevent unnecessary subsequent calls to the server
|
// promises and prevent unnecessary subsequent calls to the server
|
||||||
const StudyMetaDataPromises = new Map();
|
const StudyMetaDataPromises = new Map();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the study promise cached to ensure next retrieve will get it retrieved by server call
|
||||||
|
*
|
||||||
|
* @param {String} studyInstanceUid The UID of the Study to be removed from cache
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
OHIF.studies.deleteStudyMetadataPromise = (studyInstanceUid) => {
|
||||||
|
if (StudyMetaDataPromises.has(studyInstanceUid)) {
|
||||||
|
StudyMetaDataPromises.delete(studyInstanceUid);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves study metadata using a server call
|
* Retrieves study metadata using a server call
|
||||||
*
|
*
|
||||||
@ -30,70 +42,50 @@ OHIF.studies.retrieveStudyMetadata = (studyInstanceUid, seriesInstanceUids) => {
|
|||||||
|
|
||||||
// Create a promise to handle the data retrieval
|
// Create a promise to handle the data retrieval
|
||||||
const promise = new Promise((resolve, reject) => {
|
const promise = new Promise((resolve, reject) => {
|
||||||
|
const server = OHIF.servers.getCurrentServer();
|
||||||
|
|
||||||
// If no study metadata is in the cache variable, we need to retrieve it from
|
// If no study metadata is in the cache variable, we need to retrieve it from
|
||||||
// the server with a call.
|
// the server with a call.
|
||||||
Meteor.call('GetStudyMetadata', studyInstanceUid, function(error, study) {
|
if (server.type === 'dicomWeb' && server.requestOptions.requestFromBrowser === true) {
|
||||||
OHIF.log.timeEnd(timingKey);
|
OHIF.studies.services.WADO.RetrieveMetadata(server, studyInstanceUid).then(function (data) {
|
||||||
|
resolve(data)
|
||||||
|
}, reject);
|
||||||
|
} else {
|
||||||
|
Meteor.call('GetStudyMetadata', studyInstanceUid, function (error, study) {
|
||||||
|
OHIF.log.timeEnd(timingKey);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
const errorType = error.error;
|
const errorType = error.error;
|
||||||
let errorMessage = '';
|
let errorMessage = '';
|
||||||
|
|
||||||
if (errorType === 'server-connection-error') {
|
if (errorType === 'server-connection-error') {
|
||||||
errorMessage = 'There was an error connecting to the DICOM server, please verify if it is up and running.';
|
errorMessage = 'There was an error connecting to the DICOM server, please verify if it is up and running.';
|
||||||
} else if (errorType === 'server-internal-error') {
|
} else if (errorType === 'server-internal-error') {
|
||||||
errorMessage = `There was an internal error with the DICOM server getting metadeta for ${studyInstanceUid}`;
|
errorMessage = `There was an internal error with the DICOM server getting metadeta for ${studyInstanceUid}`;
|
||||||
} else {
|
} else {
|
||||||
errorMessage = `For some reason we could not retrieve the study\'s metadata for ${studyInstanceUid}.`;
|
errorMessage = `For some reason we could not retrieve the study\'s metadata for ${studyInstanceUid}.`;
|
||||||
|
}
|
||||||
|
|
||||||
|
OHIF.log.error(errorMessage);
|
||||||
|
OHIF.log.error(error.stack);
|
||||||
|
reject(`GetStudyMetadata: ${errorMessage}`);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
OHIF.log.error(errorMessage);
|
// Filter series if seriesInstanceUid exists
|
||||||
OHIF.log.error(error.stack);
|
if (seriesInstanceUids && seriesInstanceUids.length) {
|
||||||
reject(`GetStudyMetadata: ${errorMessage}`);
|
study.seriesList = study.seriesList.filter(series => seriesInstanceUids.indexOf(series.seriesInstanceUid) > -1);
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Filter series if seriesInstanceUid exists
|
if (!study) {
|
||||||
if (seriesInstanceUids && seriesInstanceUids.length) {
|
reject(`GetStudyMetadata: No study data returned from server: ${studyInstanceUid}`);
|
||||||
study.seriesList = study.seriesList.filter(series => seriesInstanceUids.indexOf(series.seriesInstanceUid) > -1);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!study) {
|
// Resolve the promise with the final study metadata object
|
||||||
reject(`GetStudyMetadata: No study data returned from server: ${studyInstanceUid}`);
|
resolve(study);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (window.HipaaLogger && Meteor.user && Meteor.user()) {
|
|
||||||
window.HipaaLogger.logEvent({
|
|
||||||
eventType: 'viewed',
|
|
||||||
userId: Meteor.userId(),
|
|
||||||
userName: Meteor.user().profile.fullName,
|
|
||||||
collectionName: 'Study',
|
|
||||||
recordId: studyInstanceUid,
|
|
||||||
patientId: study.patientId,
|
|
||||||
patientName: study.patientName
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Once the data was retrieved, the series are sorted by series and instance number
|
|
||||||
OHIF.viewerbase.sortStudy(study);
|
|
||||||
|
|
||||||
// Updates WADO-RS metaDataManager
|
|
||||||
OHIF.viewerbase.updateMetaDataManager(study);
|
|
||||||
|
|
||||||
// Transform the study in a StudyMetadata object
|
|
||||||
const studyMetadata = new OHIF.metadata.StudyMetadata(study);
|
|
||||||
|
|
||||||
// Add the display sets to the study
|
|
||||||
study.displaySets = OHIF.viewerbase.sortingManager.getDisplaySets(studyMetadata);
|
|
||||||
study.displaySets.forEach(displaySet => {
|
|
||||||
OHIF.viewerbase.stackManager.makeAndAddStack(study, displaySet);
|
|
||||||
studyMetadata.addDisplaySet(displaySet);
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
// Resolve the promise with the final study metadata object
|
|
||||||
resolve(study);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Store the promise in cache
|
// Store the promise in cache
|
||||||
|
|||||||
@ -15,13 +15,19 @@ OHIF.studies.searchStudies = filter => {
|
|||||||
return studySearchPromises.get(promiseKey);
|
return studySearchPromises.get(promiseKey);
|
||||||
} else {
|
} else {
|
||||||
const promise = new Promise((resolve, reject) => {
|
const promise = new Promise((resolve, reject) => {
|
||||||
Meteor.call('StudyListSearch', filter, (error, studiesData) => {
|
const server = OHIF.servers.getCurrentServer();
|
||||||
if (error) {
|
|
||||||
reject(error);
|
if (server.type === 'dicomWeb' && server.requestOptions.requestFromBrowser === true) {
|
||||||
} else {
|
OHIF.studies.services.QIDO.Studies(server, filter).then(resolve, reject);
|
||||||
resolve(studiesData);
|
} else {
|
||||||
}
|
Meteor.call('StudyListSearch', filter, (error, studiesData) => {
|
||||||
});
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
} else {
|
||||||
|
resolve(studiesData);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
studySearchPromises.set(promiseKey, promise);
|
studySearchPromises.set(promiseKey, promise);
|
||||||
return promise;
|
return promise;
|
||||||
|
|||||||
@ -1,3 +1,2 @@
|
|||||||
import './lib';
|
|
||||||
import './methods';
|
import './methods';
|
||||||
import './services';
|
import './services';
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { OHIF } from 'meteor/ohif:core';
|
|||||||
* an array of series that are present in the study
|
* an array of series that are present in the study
|
||||||
*
|
*
|
||||||
* @param resultData
|
* @param resultData
|
||||||
|
* @param studyInstanceUid
|
||||||
* @returns {Array} Series List
|
* @returns {Array} Series List
|
||||||
*/
|
*/
|
||||||
function resultDataToStudyMetadata(resultData, studyInstanceUid) {
|
function resultDataToStudyMetadata(resultData, studyInstanceUid) {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { OHIF } from 'meteor/ohif:core';
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
import { parseFloatArray } from 'meteor/ohif:studies/imports/server/lib/parseFloatArray';
|
import { parseFloatArray } from 'meteor/ohif:studies/imports/both/lib/parseFloatArray';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the value of the element (e.g. '00280009')
|
* Returns the value of the element (e.g. '00280009')
|
||||||
|
|||||||
@ -1,10 +1,5 @@
|
|||||||
import './namespace.js';
|
import './namespace.js';
|
||||||
|
|
||||||
// DICOMWeb instance, study, and metadata retrieval
|
|
||||||
import './qido/instances.js';
|
|
||||||
import './qido/studies.js';
|
|
||||||
import './wado/retrieveMetadata.js';
|
|
||||||
|
|
||||||
// DIMSE instance, study, and metadata retrieval
|
// DIMSE instance, study, and metadata retrieval
|
||||||
import './dimse/instances.js';
|
import './dimse/instances.js';
|
||||||
import './dimse/studies.js';
|
import './dimse/studies.js';
|
||||||
|
|||||||
@ -1,8 +1,3 @@
|
|||||||
import { OHIF } from 'meteor/ohif:core';
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
|
|
||||||
const Services = {};
|
OHIF.studies.services.DIMSE = {};
|
||||||
Services.QIDO = {};
|
|
||||||
Services.WADO = {};
|
|
||||||
Services.DIMSE = {};
|
|
||||||
|
|
||||||
OHIF.studies.services = Services;
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@ Package.describe({
|
|||||||
});
|
});
|
||||||
|
|
||||||
Package.onUse(function(api) {
|
Package.onUse(function(api) {
|
||||||
api.versionsFrom('1.4');
|
api.versionsFrom('1.6');
|
||||||
|
|
||||||
api.use([
|
api.use([
|
||||||
'ecmascript',
|
'ecmascript',
|
||||||
@ -21,6 +21,7 @@ Package.onUse(function(api) {
|
|||||||
'ohif:log',
|
'ohif:log',
|
||||||
'ohif:servers',
|
'ohif:servers',
|
||||||
'ohif:dicom-services',
|
'ohif:dicom-services',
|
||||||
|
'ohif:dicomweb-client',
|
||||||
'ohif:viewerbase',
|
'ohif:viewerbase',
|
||||||
'ohif:wadoproxy'
|
'ohif:wadoproxy'
|
||||||
]);
|
]);
|
||||||
|
|||||||
@ -151,29 +151,12 @@ function search() {
|
|||||||
// returned as 'undefined'
|
// returned as 'undefined'
|
||||||
const modality = replaceUndefinedColumnValue($('input#modality').val());
|
const modality = replaceUndefinedColumnValue($('input#modality').val());
|
||||||
|
|
||||||
Meteor.call('StudyListSearch', filter, (error, studies) => {
|
OHIF.studies.searchStudies(filter).then((studies) => {
|
||||||
OHIF.log.info('StudyListSearch');
|
OHIF.log.info('StudyListSearch');
|
||||||
// Hide loading text
|
// Hide loading text
|
||||||
|
|
||||||
Session.set('showLoadingText', false);
|
Session.set('showLoadingText', false);
|
||||||
|
|
||||||
if (error) {
|
|
||||||
Session.set('serverError', true);
|
|
||||||
|
|
||||||
const errorType = error.error;
|
|
||||||
|
|
||||||
if (errorType === 'server-connection-error') {
|
|
||||||
OHIF.log.error('There was an error connecting to the DICOM server, please verify if it is up and running.');
|
|
||||||
} else if (errorType === 'server-internal-error') {
|
|
||||||
OHIF.log.error('There was an internal error with the DICOM server');
|
|
||||||
} else {
|
|
||||||
OHIF.log.error('For some reason we could not list the studies.')
|
|
||||||
}
|
|
||||||
|
|
||||||
OHIF.log.error(error.stack);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!studies) {
|
if (!studies) {
|
||||||
OHIF.log.warn('No studies found');
|
OHIF.log.warn('No studies found');
|
||||||
return;
|
return;
|
||||||
@ -196,6 +179,20 @@ function search() {
|
|||||||
OHIF.studylist.collections.Studies.insert(study);
|
OHIF.studylist.collections.Studies.insert(study);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}, (error) => {
|
||||||
|
Session.set('serverError', true);
|
||||||
|
|
||||||
|
const errorType = error.error;
|
||||||
|
|
||||||
|
if (errorType === 'server-connection-error') {
|
||||||
|
OHIF.log.error('There was an error connecting to the DICOM server, please verify if it is up and running.');
|
||||||
|
} else if (errorType === 'server-internal-error') {
|
||||||
|
OHIF.log.error('There was an internal error with the DICOM server');
|
||||||
|
} else {
|
||||||
|
OHIF.log.error('For some reason we could not list the studies.')
|
||||||
|
}
|
||||||
|
|
||||||
|
OHIF.log.error(error.stack);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,8 +3,6 @@ import './exportStudies.js';
|
|||||||
import './getSelectedStudies.js';
|
import './getSelectedStudies.js';
|
||||||
import './importStudies.js';
|
import './importStudies.js';
|
||||||
import './queryStudies.js';
|
import './queryStudies.js';
|
||||||
import './retrieveStudiesMetadata.js';
|
|
||||||
import './retrieveStudyMetadata.js';
|
|
||||||
import './studylist.js';
|
import './studylist.js';
|
||||||
import './viewSeriesDetails.js';
|
import './viewSeriesDetails.js';
|
||||||
import './viewStudies.js';
|
import './viewStudies.js';
|
||||||
|
|||||||
@ -1,12 +0,0 @@
|
|||||||
import { OHIF } from 'meteor/ohif:core';
|
|
||||||
|
|
||||||
const note = 'OHIF.studylist.retrieveStudiesMetadata is deprecated.';
|
|
||||||
const instructions = 'Please use OHIF.studies.retrieveStudiesMetadata instead.';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Please use OHIF.studies.retrieveStudiesMetadata instead
|
|
||||||
*/
|
|
||||||
OHIF.studylist.retrieveStudiesMetadata = function() {
|
|
||||||
OHIF.log.warn(`${note}\n${instructions}`);
|
|
||||||
OHIF.studies.retrieveStudiesMetadata.apply(this, arguments);
|
|
||||||
};
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
import { OHIF } from 'meteor/ohif:core';
|
|
||||||
|
|
||||||
const note = 'OHIF.studylist.retrieveStudyMetadata is deprecated.';
|
|
||||||
const instructions = 'Please use OHIF.studies.retrieveStudyMetadata instead.';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Please use OHIF.studies.retrieveStudyMetadata instead
|
|
||||||
*/
|
|
||||||
OHIF.studylist.retrieveStudyMetadata = function() {
|
|
||||||
OHIF.log.warn(`${note}\n${instructions}`);
|
|
||||||
OHIF.studies.retrieveStudyMetadata.apply(this, arguments);
|
|
||||||
};
|
|
||||||
Loading…
Reference in New Issue
Block a user