Improving new study browser and moving studies services to ohif:studies

This commit is contained in:
Bruno Alves de Faria 2017-10-11 08:03:35 -03:00
parent 0b51a53976
commit eb372bed03
54 changed files with 560 additions and 495 deletions

View File

@ -3,3 +3,4 @@ import './bootstrap';
import './playground/playground.html'; import './playground/playground.html';
import './playground/playground.styl'; import './playground/playground.styl';
import './playground/playground.js'; import './playground/playground.js';
import './scrollArea';

View File

@ -0,0 +1,2 @@
import './scrollArea.html';
import './scrollArea.js';

View File

@ -0,0 +1,7 @@
<template name="scrollArea">
<div class="scroll-area {{this.class}}">
<div class="scrollable {{#if instance.config.scrollX}}scroll-x{{/if}} {{#if instance.config.scrollY}}scroll-y{{/if}}">
{{>Template.contentBlock}}
</div>
</div>
</template>

View File

@ -0,0 +1,29 @@
import { Template } from 'meteor/templating';
import { _ } from 'meteor/underscore';
Template.scrollArea.onCreated(() => {
const instance = Template.instance();
const { data } = instance;
const defaultConfig = {
hideScrollbar: true,
scrollY: true,
scrollX: false
};
instance.config = _.defaults(data || {}, defaultConfig);
});
Template.scrollArea.onRendered(() => {
const instance = Template.instance();
const { config } = instance;
if (config.hideScrollbar) {
const $scrollable = instance.$('.scrollable').first();
const scrollable = $scrollable[0];
const x = config.scrollX ? 1 : 0;
const y = config.scrollY ? 1 : 0;
$scrollable.css({
right: 0 - (scrollable.offsetWidth - scrollable.clientWidth) * y,
bottom: 0 - (scrollable.offsetHeight - scrollable.clientHeight) * x
});
}
});

View File

@ -30,16 +30,19 @@ $.fn.tempShow = function(callback) {
$.fn.adjustMax = function(dimension) { $.fn.adjustMax = function(dimension) {
const $element = $(this); const $element = $(this);
$element.tempShow(() => { $element.tempShow(() => {
// Add a class to remove the max restriction const maxProperty = `max-${dimension}`;
const maxClass = `no-max-${dimension}`;
$element.addClass(maxClass); // Remove the current max restriction
$element.each((i, e) => e.style.setProperty(maxProperty, 'none', 'important'));
// Get the dimension function to obtain the outer dimension // Get the dimension function to obtain the outer dimension
const dimensionFn = 'outer' + dimension.charAt(0).toUpperCase() + dimension.slice(1); const dimensionFn = 'outer' + dimension.charAt(0).toUpperCase() + dimension.slice(1);
const value = $element[dimensionFn](); const value = $element[dimensionFn]();
// Remove the max restriction class and set the new max // Remove the property (needed for IE)
const maxProperty = `max-${dimension}`; $element.each((i, e) => e.style.removeProperty(maxProperty));
$element.removeClass(maxClass).css(maxProperty, value);
// Set the new max restriction
$element.css(maxProperty, value);
}); });
}; };

View File

@ -1,8 +1,2 @@
body *.visible body *.visible
display: block !important display: block !important
body *.no-max-height:not(.bypassNot)
max-height: none !important
body *.no-max-width:not(.bypassNot)
max-width: none !important

View File

@ -79,9 +79,6 @@ body .select2-container--default .select2-results>.select2-results__options
.height-auto .height-auto
height: auto !important height: auto !important
.full-width
width: 100%
.caret-down .caret-down
display: inline-block display: inline-block
width: 0 width: 0
@ -126,3 +123,38 @@ body .select2-container--default .select2-results>.select2-results__options
&:hover span &:hover span
transform(rotate(180deg)) transform(rotate(180deg))
.full-width
width: 100%
.full-height
height: 100%
.flex-h
display: flex
flex-direction: row
.flex-v
display: flex
flex-direction: column
.flex-grow
flex-grow: 1
.scroll-area
overflow: hidden
position: relative
.scrollable
bottom: 0
left: 0
overflow: hidden
position: absolute
right: 0
top: 0
&.scroll-x
overflow-x: scroll
&.scroll-y
overflow-y: scroll

View File

@ -161,7 +161,7 @@ class ConformanceCriteria {
return; return;
} }
const promise = OHIF.studylist.retrieveStudyMetadata(studyInstanceUid); const promise = OHIF.studies.retrieveStudyMetadata(studyInstanceUid);
promise.then(study => { promise.then(study => {
const studyMetadata = OHIF.viewerbase.getStudyMetadata(study); const studyMetadata = OHIF.viewerbase.getStudyMetadata(study);

View File

@ -46,7 +46,7 @@ function renderIntoViewport(viewportIndex, studyInstanceUid, seriesInstanceUid,
const element = $viewports.get(viewportIndex); const element = $viewports.get(viewportIndex);
const startLoadingHandler = cornerstoneTools.loadHandlerManager.getStartLoadHandler(); const startLoadingHandler = cornerstoneTools.loadHandlerManager.getStartLoadHandler();
startLoadingHandler(element); startLoadingHandler(element);
OHIF.studylist.retrieveStudyMetadata(studyInstanceUid).then(study => { OHIF.studies.retrieveStudyMetadata(studyInstanceUid).then(study => {
OHIF.log.warn('renderIntoViewport'); OHIF.log.warn('renderIntoViewport');
// Double check to make sure this study wasn't already inserted // Double check to make sure this study wasn't already inserted

View File

@ -1,5 +1,6 @@
import './list.html'; import './list.html';
import './list.js';
import './item.html'; import './item.html';
import './item.js'; import './item.js';
import './item.styl'; import './item.styl';
import './series.html';
import './series.js';

View File

@ -1,5 +1,5 @@
<template name="studyBrowserItem"> <template name="studyBrowserItem">
<div class="study-browser-item {{this.settings.studyItemClass}} {{#if isActive}}active{{/if}} {{#if isLoading}}loading{{/if}}" data-uid="{{this.studyInformation.studyInstanceUid}}"> <div class="study-browser-item {{this.studyItemClass}} {{#if isActive}}active{{/if}} {{#if isLoading}}loading{{/if}}" data-uid="{{this.studyInformation.studyInstanceUid}}">
<div class="study-item"> <div class="study-item">
{{#if isLoading}} {{#if isLoading}}
{{>loadingText}} {{>loadingText}}
@ -14,8 +14,8 @@
</div> </div>
</div> </div>
</div> </div>
{{#if this.settings.studyTemplate}} {{#if this.studyChildTemplate}}
{{>Template.dynamic template=this.settings.studyTemplate data=(clone this)}} {{>Template.dynamic template=this.studyChildTemplate data=(clone this)}}
{{/if}} {{/if}}
</div> </div>
</template> </template>

View File

@ -1,26 +1,52 @@
import { Template } from 'meteor/templating'; import { Template } from 'meteor/templating';
import { ReactiveVar } from 'meteor/reactive-var';
import { $ } from 'meteor/jquery'; import { $ } from 'meteor/jquery';
import { OHIF } from 'meteor/ohif:core';
Template.studyBrowserItem.onCreated(() => { Template.studyBrowserItem.onCreated(() => {
const instance = Template.instance(); const instance = Template.instance();
instance.loaded = false;
instance.loading = new ReactiveVar(false);
const modality = instance.data.studyInformation.modality || 'UN'; const modality = instance.data.studyInformation.modality || 'UN';
instance.modalities = modality.replace(/\\/g, ' '); instance.modalities = modality.replace(/\\/g, ' ');
}); });
Template.studyBrowserItem.events({ Template.studyBrowserItem.events({
'click .study-browser-item'(event, instance) { 'click .study-item'(event, instance) {
const element = event.currentTarget; if (instance.loading.get()) return;
const { studyClickCallback, studyInformation } = instance.data;
const element = event.currentTarget.parentElement;
const $element = $(element); const $element = $(element);
$element.trigger('ohif.studies.study.click', instance.data.studyInformation); $element.trigger('ohif.studies.study.click', studyInformation);
const { settings, studyInformation } = instance.data;
if (settings && typeof settings.studyClickCallback) { const triggerClickCallback = () => {
settings.studyClickCallback(studyInformation, element); if (typeof studyClickCallback === 'function') {
studyClickCallback(studyInformation, element);
}
};
if (instance.loaded) {
triggerClickCallback();
} else {
instance.loading.set(true);
OHIF.studies.retrieveStudyMetadata(studyInformation.studyInstanceUid).then(() => {
instance.loaded = true;
instance.loading.set(false);
$element.trigger('ohif.studies.study.load', studyInformation);
triggerClickCallback();
});
} }
} }
}); });
Template.studyBrowserItem.helpers({ Template.studyBrowserItem.helpers({
isLoading() {
return Template.instance().loading.get();
},
modalityStyle() { modalityStyle() {
// Responsively styles the Modality Acronyms for studies // Responsively styles the Modality Acronyms for studies
// with more than one modality // with more than one modality

View File

@ -46,13 +46,25 @@ $spacerY = 12px
theme('background-color', '$activeColor') theme('background-color', '$activeColor')
border-color: $boxActiveBorderColor border-color: $boxActiveBorderColor
&:not(.active) .studyTimepointThumbnails &:not(.active) .study-browser-series
max-height: 0 !important max-height: 0 !important
.studyTimepointThumbnails .study-series-container
opacity: 0
transform(translateY(-100%))
.study-browser-series
overflow: hidden overflow: hidden
transform(translateY(0))
transform-origin(50% 0%)
transition($sidebarTransition) transition($sidebarTransition)
.study-series-container
opacity: 1
transition($sidebarTransition)
transform(translateY(0))
transform-origin(50% 0%)
.study-item-box .study-item-box
border: 1px solid $boxBorderColor border: 1px solid $boxBorderColor
border-radius: 12px border-radius: 12px

View File

@ -1,5 +1,5 @@
<template name="studyBrowserList"> <template name="studyBrowserList">
<div class="study-browser-list {{this.settings.studyListClass}}"> <div class="study-browser-list {{this.class}}">
{{#each studyInformation in studiesInformation}} {{#each studyInformation in studiesInformation}}
{{>studyBrowserItem (clone this studyInformation=studyInformation)}} {{>studyBrowserItem (clone this studyInformation=studyInformation)}}
{{/each}} {{/each}}

View File

@ -0,0 +1,9 @@
<template name="studyBrowserSeries">
<div class="study-browser-series">
<div class="study-series-container">
{{#each thumbnail in instance.thumbnails.get}}
{{>thumbnailEntry (clone '' thumbnail=thumbnail)}}
{{/each}}
</div>
</div>
</template>

View File

@ -0,0 +1,40 @@
import { Template } from 'meteor/templating';
import { ReactiveVar } from 'meteor/reactive-var';
import { Tracker } from 'meteor/tracker';
import { OHIF } from 'meteor/ohif:core';
Template.studyBrowserSeries.onCreated(() => {
const instance = Template.instance();
const { studyInformation } = instance.data;
const { studyInstanceUid } = studyInformation;
instance.thumbnails = new ReactiveVar([]);
// Get the study metadata and update the study thumbnails
instance.autorun(() => {
const studyMetadata = OHIF.viewer.Studies.findBy({ studyInstanceUid });
// Defines the resulting thumbnails list
const thumbnails = [];
studyMetadata.displaySets.forEach((stack, thumbnailIndex) => {
thumbnails.push({
thumbnailIndex,
stack
});
});
instance.thumbnails.set(thumbnails);
});
});
Template.studyBrowserSeries.onRendered(() => {
const instance = Template.instance();
// Run this computation every time the thumbnails are changed
instance.autorun(() => {
instance.thumbnails.get();
Tracker.afterFlush(() => {
instance.$('.study-browser-series').adjustMax('height');
});
});
});

View File

@ -0,0 +1,2 @@
import './retrieveStudiesMetadata';
import './retrieveStudyMetadata';

View File

@ -0,0 +1,32 @@
import { OHIF } from 'meteor/ohif:core';
/**
* Retrieves metaData for multiple studies at once.
*
* This function calls retrieveStudyMetadata several times, asynchronously,
* and waits for all of the results to be returned.
*
* @param studyInstanceUids The UIDs of the Studies to be retrieved
* @return Promise
*/
OHIF.studies.retrieveStudiesMetadata = (studyInstanceUids, seriesInstanceUids) => {
// Create an empty array to store the Promises for each metaData retrieval call
const promises = [];
// Loop through the array of studyInstanceUids
studyInstanceUids.forEach(function(studyInstanceUid) {
// Send the call and resolve or reject the related promise based on its outcome
const promise = OHIF.studies.retrieveStudyMetadata(studyInstanceUid, seriesInstanceUids);
// Add the current promise to the array of promises
promises.push(promise);
});
// When all of the promises are complete, this callback runs
const promise = Promise.all(promises);
// Warn the error on console if some retrieval failed
promise.catch(error => OHIF.log.warn(error));
return promise;
};

View File

@ -0,0 +1,3 @@
import './lib';
import './methods';
import './services';

View File

@ -19,9 +19,9 @@ Meteor.methods({
try { try {
if (server.type === 'dicomWeb') { if (server.type === 'dicomWeb') {
return Services.WADO.RetrieveMetadata(server, studyInstanceUid); return OHIF.studies.services.WADO.RetrieveMetadata(server, studyInstanceUid);
} else if (server.type === 'dimse') { } else if (server.type === 'dimse') {
return Services.DIMSE.RetrieveMetadata(studyInstanceUid); return OHIF.studies.services.DIMSE.RetrieveMetadata(studyInstanceUid);
} }
} catch (error) { } catch (error) {
OHIF.log.trace(); OHIF.log.trace();

View File

@ -0,0 +1 @@
import './getStudyMetadata.js';

View File

@ -57,7 +57,7 @@ function resultDataToStudyMetadata(resultData, studyInstanceUid) {
* @param studyInstanceUid * @param studyInstanceUid
* @returns {{wadoUriRoot: String, studyInstanceUid: String, seriesList: Array}} * @returns {{wadoUriRoot: String, studyInstanceUid: String, seriesList: Array}}
*/ */
Services.DIMSE.Instances = function(studyInstanceUid) { OHIF.studies.services.DIMSE.Instances = function(studyInstanceUid) {
//var url = buildUrl(server, studyInstanceUid); //var url = buildUrl(server, studyInstanceUid);
const result = DIMSE.retrieveInstances(studyInstanceUid); const result = DIMSE.retrieveInstances(studyInstanceUid);

View File

@ -1,5 +1,5 @@
import { OHIF } from 'meteor/ohif:core'; import { OHIF } from 'meteor/ohif:core';
import { parseFloatArray } from 'meteor/ohif:study-list/server/lib/parseFloatArray'; import { parseFloatArray } from 'meteor/ohif:studies/imports/server/lib/parseFloatArray';
/** /**
* Returns the value of the element (e.g. '00280009') * Returns the value of the element (e.g. '00280009')
@ -151,7 +151,7 @@ function resultDataToStudyMetadata(studyInstanceUid, resultData) {
* @param studyInstanceUid * @param studyInstanceUid
* @returns {{seriesList: Array, patientName: *, patientId: *, accessionNumber: *, studyDate: *, modalities: *, studyDescription: *, imageCount: *, studyInstanceUid: *}} * @returns {{seriesList: Array, patientName: *, patientId: *, accessionNumber: *, studyDate: *, modalities: *, studyDescription: *, imageCount: *, studyInstanceUid: *}}
*/ */
Services.DIMSE.RetrieveMetadata = function(studyInstanceUid) { OHIF.studies.services.DIMSE.RetrieveMetadata = function(studyInstanceUid) {
// TODO: Check which peer it should point to // TODO: Check which peer it should point to
const activeServer = OHIF.servers.getCurrentServer().peers[0]; const activeServer = OHIF.servers.getCurrentServer().peers[0];
const supportsInstanceRetrievalByStudyUid = activeServer.supportsInstanceRetrievalByStudyUid; const supportsInstanceRetrievalByStudyUid = activeServer.supportsInstanceRetrievalByStudyUid;

View File

@ -32,7 +32,7 @@ function resultDataToStudies(resultData) {
return studies; return studies;
} }
Services.DIMSE.Studies = function(filter) { OHIF.studies.services.DIMSE.Studies = function(filter) {
OHIF.log.info('Services.DIMSE.Studies'); OHIF.log.info('Services.DIMSE.Studies');
let filterStudyDate = ''; let filterStudyDate = '';

View File

@ -0,0 +1,13 @@
import { OHIF } from 'meteor/ohif:core';
const Services = {};
Services.QIDO = {};
Services.WADO = {};
Services.DIMSE = {};
Services.REMOTE = {};
OHIF.studies.services = Services;
remoteGetValue = function(obj) {
return obj ? obj.Value : null;
};

View File

@ -74,7 +74,7 @@ function resultDataToStudyMetadata(server, studyInstanceUid, resultData) {
* @throws ECONNREFUSED * @throws ECONNREFUSED
* @returns {{wadoUriRoot: String, studyInstanceUid: String, seriesList: Array}} * @returns {{wadoUriRoot: String, studyInstanceUid: String, seriesList: Array}}
*/ */
Services.QIDO.Instances = function(server, studyInstanceUid) { OHIF.studies.services.QIDO.Instances = function(server, studyInstanceUid) {
var url = buildUrl(server, studyInstanceUid); var url = buildUrl(server, studyInstanceUid);
try { try {

View File

@ -91,7 +91,7 @@ function resultDataToStudies(resultData) {
return studies; return studies;
} }
Services.QIDO.Studies = function(server, filter) { OHIF.studies.services.QIDO.Studies = function(server, filter) {
var url = filterToQIDOURL(server, filter); var url = filterToQIDOURL(server, filter);
try { try {

View File

@ -1,84 +1,85 @@
import { remoteGetValue } from '../../lib/remoteGetValue'; import { OHIF } from 'meteor/ohif:core';
import { remoteGetValue } from '../../lib/remoteGetValue';
/**
* Parses data returned from a QIDO search and transforms it into /**
* an array of series that are present in the study * Parses data returned from a QIDO search and transforms it into
* * an array of series that are present in the study
* @param server The DICOM server *
* @param studyInstanceUid * @param server The DICOM server
* @param resultData * @param studyInstanceUid
* @returns {Array} Series List * @param resultData
*/ * @returns {Array} Series List
function resultDataToStudyMetadata(server, studyInstanceUid, resultData) { */
var seriesMap = {}; function resultDataToStudyMetadata(server, studyInstanceUid, resultData) {
var seriesList = []; var seriesMap = {};
var seriesList = [];
resultData.forEach(function(instance) {
// Use seriesMap to cache series data resultData.forEach(function(instance) {
// If the series instance UID has already been used to // Use seriesMap to cache series data
// process series data, continue using that series // If the series instance UID has already been used to
var seriesInstanceUid = remoteGetValue(instance['0020,000e']); // process series data, continue using that series
var series = seriesMap[seriesInstanceUid]; var seriesInstanceUid = remoteGetValue(instance['0020,000e']);
var series = seriesMap[seriesInstanceUid];
// If no series data exists in the seriesMap cache variable,
// process any available series data // If no series data exists in the seriesMap cache variable,
if(!series) { // process any available series data
series = { if(!series) {
seriesInstanceUid : seriesInstanceUid, series = {
seriesNumber : parseFloat(remoteGetValue(instance['0020,0011'])), seriesInstanceUid : seriesInstanceUid,
instances: [] seriesNumber : parseFloat(remoteGetValue(instance['0020,0011'])),
}; instances: []
};
// Save this data in the seriesMap cache variable
seriesMap[seriesInstanceUid] = series; // Save this data in the seriesMap cache variable
seriesList.push(series); seriesMap[seriesInstanceUid] = series;
} seriesList.push(series);
}
// The uri for the dicomweb
// NOTE: DCM4CHEE seems to return the data zipped // The uri for the dicomweb
// NOTE: Orthanc returns the data with multi-part mime which cornerstoneWADOImageLoader doesn't // NOTE: DCM4CHEE seems to return the data zipped
// know how to parse yet // NOTE: Orthanc returns the data with multi-part mime which cornerstoneWADOImageLoader doesn't
//var uri = DICOMWeb.getString(instance['00081190']); // know how to parse yet
//uri = uri.replace('wado-rs', 'dicom-web'); //var uri = DICOMWeb.getString(instance['00081190']);
//uri = uri.replace('wado-rs', 'dicom-web');
// manually create a WADO-URI from the UIDs
// NOTE: Haven't been able to get Orthanc's WADO-URI to work yet - maybe its not configured? // manually create a WADO-URI from the UIDs
var sopInstanceUid = remoteGetValue(instance['0008,0018']); // NOTE: Haven't been able to get Orthanc's WADO-URI to work yet - maybe its not configured?
var uri = server.wadoUriRoot + '?requestType=WADO&studyUID=' + studyInstanceUid + '&seriesUID=' + seriesInstanceUid + '&objectUID=' + sopInstanceUid + "&contentType=application%2Fdicom"; var sopInstanceUid = remoteGetValue(instance['0008,0018']);
var uri = server.wadoUriRoot + '?requestType=WADO&studyUID=' + studyInstanceUid + '&seriesUID=' + seriesInstanceUid + '&objectUID=' + sopInstanceUid + "&contentType=application%2Fdicom";
// Add this instance to the current series
series.instances.push({ // Add this instance to the current series
sopClassUid: remoteGetValue(instance['0008,0016']), series.instances.push({
sopInstanceUid: sopInstanceUid, sopClassUid: remoteGetValue(instance['0008,0016']),
uri: uri, sopInstanceUid: sopInstanceUid,
instanceNumber: parseFloat(remoteGetValue(instance['0020,0013'])) uri: uri,
}); instanceNumber: parseFloat(remoteGetValue(instance['0020,0013']))
}); });
return seriesList; });
} return seriesList;
}
/**
* Retrieve a set of instances using a QIDO call /**
* @param server * Retrieve a set of instances using a QIDO call
* @param studyInstanceUid * @param server
* @returns {{wadoUriRoot: String, studyInstanceUid: String, seriesList: Array}} * @param studyInstanceUid
*/ * @returns {{wadoUriRoot: String, studyInstanceUid: String, seriesList: Array}}
Services.REMOTE.Instances = function(server, studyInstanceUid) { */
var parameters = { OHIF.studies.services.REMOTE.Instances = function(server, studyInstanceUid) {
PatientName: "", var parameters = {
PatientID: "", PatientName: "",
AccessionNumber: "", PatientID: "",
SeriesInstanceUID: "", AccessionNumber: "",
SeriesNumber : "", SeriesInstanceUID: "",
SOPClassUID : "", SeriesNumber : "",
InstanceNumber : "" SOPClassUID : "",
}; InstanceNumber : ""
};
var remote = new OrthancRemote(server.root, server.sourceAE);
var remote = new OrthancRemote(server.root, server.sourceAE);
return {
wadoUriRoot: server.wadoUriRoot, return {
studyInstanceUid: studyInstanceUid, wadoUriRoot: server.wadoUriRoot,
seriesList: resultDataToStudyMetadata(server, studyInstanceUid, remote.findInstances(server.modality, studyInstanceUid, null, parameters)) studyInstanceUid: studyInstanceUid,
}; seriesList: resultDataToStudyMetadata(server, studyInstanceUid, remote.findInstances(server.modality, studyInstanceUid, null, parameters))
}; };
};

View File

@ -1,141 +1,142 @@
import { remoteGetValue } from '../../lib/remoteGetValue'; import { OHIF } from 'meteor/ohif:core';
import { parseFloatArray } from '../../lib/parseFloatArray'; import { remoteGetValue } from '../../lib/remoteGetValue';
import { parseFloatArray } from '../../lib/parseFloatArray';
/**
* Parses the SourceImageSequence, if it exists, in order /**
* to return a ReferenceSOPInstanceUID. The ReferenceSOPInstanceUID * Parses the SourceImageSequence, if it exists, in order
* is used to refer to this image in any accompanying DICOM-SR documents. * to return a ReferenceSOPInstanceUID. The ReferenceSOPInstanceUID
* * is used to refer to this image in any accompanying DICOM-SR documents.
* @param instance *
* @returns {String} The ReferenceSOPInstanceUID * @param instance
*/ * @returns {String} The ReferenceSOPInstanceUID
function getSourceImageInstanceUid(instance) { */
// TODO= Parse the whole Source Image Sequence function getSourceImageInstanceUid(instance) {
// This is a really poor workaround for now. // TODO= Parse the whole Source Image Sequence
// Later we should probably parse the whole sequence. // This is a really poor workaround for now.
var SourceImageSequence = remoteGetValue(instance['0008,2112']); // Later we should probably parse the whole sequence.
if (SourceImageSequence && SourceImageSequence.Value && SourceImageSequence.Value.length) { var SourceImageSequence = remoteGetValue(instance['0008,2112']);
return SourceImageSequence.Value[0]['0008,1155'].Value[0]; if (SourceImageSequence && SourceImageSequence.Value && SourceImageSequence.Value.length) {
} return SourceImageSequence.Value[0]['0008,1155'].Value[0];
} }
}
/**
* Parses result data from a WADO search into Study MetaData /**
* Returns an object populated with study metadata, including the * Parses result data from a WADO search into Study MetaData
* series list. * Returns an object populated with study metadata, including the
* * series list.
* @param server *
* @param studyInstanceUid * @param server
* @param resultData * @param studyInstanceUid
* @returns {{seriesList: Array, patientName: *, patientId: *, accessionNumber: *, studyDate: *, modalities: *, studyDescription: *, imageCount: *, studyInstanceUid: *}} * @param resultData
*/ * @returns {{seriesList: Array, patientName: *, patientId: *, accessionNumber: *, studyDate: *, modalities: *, studyDescription: *, imageCount: *, studyInstanceUid: *}}
function resultDataToStudyMetadata(server, studyInstanceUid, resultData) { */
var seriesMap = {}; function resultDataToStudyMetadata(server, studyInstanceUid, resultData) {
var seriesList = []; var seriesMap = {};
var seriesList = [];
if (!resultData.length) {
return; if (!resultData.length) {
} return;
}
var anInstance = resultData[0];
if (!anInstance) { var anInstance = resultData[0];
return; if (!anInstance) {
} return;
}
var studyData = {
seriesList: seriesList, var studyData = {
patientName: remoteGetValue(anInstance['0010,0010']), seriesList: seriesList,
patientId: remoteGetValue(anInstance['0010,0020']), patientName: remoteGetValue(anInstance['0010,0010']),
accessionNumber: remoteGetValue(anInstance['0008,0050']), patientId: remoteGetValue(anInstance['0010,0020']),
studyDate: remoteGetValue(anInstance['0008,0020']), accessionNumber: remoteGetValue(anInstance['0008,0050']),
modalities: remoteGetValue(anInstance['0008,0061']), studyDate: remoteGetValue(anInstance['0008,0020']),
studyDescription: remoteGetValue(anInstance['0008,1030']), modalities: remoteGetValue(anInstance['0008,0061']),
imageCount: remoteGetValue(anInstance['0020,1208']), studyDescription: remoteGetValue(anInstance['0008,1030']),
studyInstanceUid: remoteGetValue(anInstance['0020,000d']) imageCount: remoteGetValue(anInstance['0020,1208']),
}; studyInstanceUid: remoteGetValue(anInstance['0020,000d'])
};
resultData.forEach(function(instance) {
var seriesInstanceUid = remoteGetValue(instance['0020,000e']); resultData.forEach(function(instance) {
var series = seriesMap[seriesInstanceUid]; var seriesInstanceUid = remoteGetValue(instance['0020,000e']);
if (!series) { var series = seriesMap[seriesInstanceUid];
series = { if (!series) {
seriesDescription: remoteGetValue(instance['0008,103e']), series = {
modality: remoteGetValue(instance['0008,0060']), seriesDescription: remoteGetValue(instance['0008,103e']),
seriesInstanceUid: seriesInstanceUid, modality: remoteGetValue(instance['0008,0060']),
seriesNumber: parseFloat(remoteGetValue(instance['0020,0011'])), seriesInstanceUid: seriesInstanceUid,
instances: [] seriesNumber: parseFloat(remoteGetValue(instance['0020,0011'])),
}; instances: []
seriesMap[seriesInstanceUid] = series; };
seriesList.push(series); seriesMap[seriesInstanceUid] = series;
} seriesList.push(series);
}
var sopInstanceUid = remoteGetValue(instance['0008,0018']);
var sopInstanceUid = remoteGetValue(instance['0008,0018']);
var instanceSummary = {
imageType: remoteGetValue(instance['0008,0008']), var instanceSummary = {
sopClassUid: remoteGetValue(instance['0008,0016']), imageType: remoteGetValue(instance['0008,0008']),
sopInstanceUid: sopInstanceUid, sopClassUid: remoteGetValue(instance['0008,0016']),
instanceNumber: parseFloat(remoteGetValue(instance['0020,0013'])), sopInstanceUid: sopInstanceUid,
imagePositionPatient: remoteGetValue(instance['0020,0032']), instanceNumber: parseFloat(remoteGetValue(instance['0020,0013'])),
imageOrientationPatient: remoteGetValue(instance['0020,0037']), imagePositionPatient: remoteGetValue(instance['0020,0032']),
frameOfReferenceUID: remoteGetValue(instance['0020,0052']), imageOrientationPatient: remoteGetValue(instance['0020,0037']),
sliceLocation: parseFloat(remoteGetValue(instance['0020,1041'])), frameOfReferenceUID: remoteGetValue(instance['0020,0052']),
samplesPerPixel: parseFloat(remoteGetValue(instance['0028,0002'])), sliceLocation: parseFloat(remoteGetValue(instance['0020,1041'])),
photometricInterpretation: remoteGetValue(instance['0028,0004']), samplesPerPixel: parseFloat(remoteGetValue(instance['0028,0002'])),
rows: parseFloat(remoteGetValue(instance['0028,0010'])), photometricInterpretation: remoteGetValue(instance['0028,0004']),
columns: parseFloat(remoteGetValue(instance['0028,0011'])), rows: parseFloat(remoteGetValue(instance['0028,0010'])),
pixelSpacing: remoteGetValue(instance['0028,0030']), columns: parseFloat(remoteGetValue(instance['0028,0011'])),
bitsAllocated: parseFloat(remoteGetValue(instance['0028,0100'])), pixelSpacing: remoteGetValue(instance['0028,0030']),
bitsStored: parseFloat(remoteGetValue(instance['0028,0101'])), bitsAllocated: parseFloat(remoteGetValue(instance['0028,0100'])),
highBit: parseFloat(remoteGetValue(instance['0028,0102'])), bitsStored: parseFloat(remoteGetValue(instance['0028,0101'])),
pixelRepresentation: parseFloat(remoteGetValue(instance['0028,0103'])), highBit: parseFloat(remoteGetValue(instance['0028,0102'])),
windowCenter: remoteGetValue(instance['0028,1050']), pixelRepresentation: parseFloat(remoteGetValue(instance['0028,0103'])),
windowWidth: remoteGetValue(instance['0028,1051']), windowCenter: remoteGetValue(instance['0028,1050']),
rescaleIntercept: parseFloat(remoteGetValue(instance['0028,1052'])), windowWidth: remoteGetValue(instance['0028,1051']),
rescaleSlope: parseFloat(remoteGetValue(instance['0028,1053'])), rescaleIntercept: parseFloat(remoteGetValue(instance['0028,1052'])),
sourceImageInstanceUid: getSourceImageInstanceUid(instance), rescaleSlope: parseFloat(remoteGetValue(instance['0028,1053'])),
laterality: remoteGetValue(instance['0020,0062']), sourceImageInstanceUid: getSourceImageInstanceUid(instance),
viewPosition: remoteGetValue(instance['0018,5101']), laterality: remoteGetValue(instance['0020,0062']),
acquisitionDateTime: remoteGetValue(instance['0008,002A']), viewPosition: remoteGetValue(instance['0018,5101']),
numberOfFrames: parseFloat(remoteGetValue(instance['0028,0008'])), acquisitionDateTime: remoteGetValue(instance['0008,002A']),
frameIncrementPointer: remoteGetValue(instance['0028,0009']), numberOfFrames: parseFloat(remoteGetValue(instance['0028,0008'])),
frameTime: parseFloat(remoteGetValue(instance['0018,1063'])), frameIncrementPointer: remoteGetValue(instance['0028,0009']),
frameTimeVector: parseFloatArray(remoteGetValue(instance['0018,1065'])), frameTime: parseFloat(remoteGetValue(instance['0018,1063'])),
echoNumber: remoteGetValue(instance['0018,0086']), frameTimeVector: parseFloatArray(remoteGetValue(instance['0018,1065'])),
contrastBolusAgent: remoteGetValue(instance['0018,0010']) echoNumber: remoteGetValue(instance['0018,0086']),
}; contrastBolusAgent: remoteGetValue(instance['0018,0010'])
};
var iid = instance['xxxx,0001'].Value;
if (server.imageRendering === 'wadouri') { var iid = instance['xxxx,0001'].Value;
instanceSummary.wadouri = server.wadoUriRoot + '?requestType=WADO&studyUID=' + studyInstanceUid + '&seriesUID=' + seriesInstanceUid + '&objectUID=' + sopInstanceUid + "&contentType=application%2Fdicom"; if (server.imageRendering === 'wadouri') {
} else if (server.imageRendering == 'orthanc') { instanceSummary.wadouri = server.wadoUriRoot + '?requestType=WADO&studyUID=' + studyInstanceUid + '&seriesUID=' + seriesInstanceUid + '&objectUID=' + sopInstanceUid + "&contentType=application%2Fdicom";
instanceSummary.wadouri = server.root + '/instances/' + iid + '/file'; } else if (server.imageRendering == 'orthanc') {
} else { instanceSummary.wadouri = server.root + '/instances/' + iid + '/file';
instanceSummary.wadorsuri = server.wadoRoot + '/studies/' + studyInstanceUid + '/series/' + seriesInstanceUid + '/instances/' + sopInstanceUid + '/frames/1'; } else {
} instanceSummary.wadorsuri = server.wadoRoot + '/studies/' + studyInstanceUid + '/series/' + seriesInstanceUid + '/instances/' + sopInstanceUid + '/frames/1';
}
series.instances.push(instanceSummary);
}); series.instances.push(instanceSummary);
console.log(studyData.seriesList[0].instances); });
return studyData; console.log(studyData.seriesList[0].instances);
} return studyData;
}
/**
* Retrieved Study MetaData from a DICOM server using a WADO call /**
* @param server * Retrieved Study MetaData from a DICOM server using a WADO call
* @param studyInstanceUid * @param server
* @returns {{seriesList: Array, patientName: *, patientId: *, accessionNumber: *, studyDate: *, modalities: *, studyDescription: *, imageCount: *, studyInstanceUid: *}} * @param studyInstanceUid
*/ * @returns {{seriesList: Array, patientName: *, patientId: *, accessionNumber: *, studyDate: *, modalities: *, studyDescription: *, imageCount: *, studyInstanceUid: *}}
Services.REMOTE.RetrieveMetadata = function(server, studyInstanceUid) { */
var remote = new OrthancRemote(server.root, server.sourceAE); OHIF.studies.services.REMOTE.RetrieveMetadata = function(server, studyInstanceUid) {
var remote = new OrthancRemote(server.root, server.sourceAE);
var study = resultDataToStudyMetadata(server, studyInstanceUid, remote.retrieveMetadata(server.modality, studyInstanceUid));
if (!study) { var study = resultDataToStudyMetadata(server, studyInstanceUid, remote.retrieveMetadata(server.modality, studyInstanceUid));
study = {}; if (!study) {
} study = {};
}
study.wadoUriRoot = server.wadoUriRoot;
study.studyInstanceUid = studyInstanceUid; study.wadoUriRoot = server.wadoUriRoot;
study.studyInstanceUid = studyInstanceUid;
return study;
}; return study;
};

View File

@ -1,49 +1,50 @@
import { remoteGetValue } from '../../lib/remoteGetValue'; import { OHIF } from 'meteor/ohif:core';
import { remoteGetValue } from '../../lib/remoteGetValue';
function resultDataToStudies(resultData) {
var studies = []; function resultDataToStudies(resultData) {
var studies = [];
resultData.forEach(function(study) {
studies.push({ resultData.forEach(function(study) {
studyInstanceUid: remoteGetValue(study['0020,000d']), studies.push({
// 00080005 = SpecificCharacterSet studyInstanceUid: remoteGetValue(study['0020,000d']),
studyDate: remoteGetValue(study['0008,0020']), // 00080005 = SpecificCharacterSet
studyTime: remoteGetValue(study['0008,0030']), studyDate: remoteGetValue(study['0008,0020']),
accessionNumber: remoteGetValue(study['0008,0050']), studyTime: remoteGetValue(study['0008,0030']),
referringPhysicianName: remoteGetValue(study['0008,0090']), accessionNumber: remoteGetValue(study['0008,0050']),
// 00081190 = URL referringPhysicianName: remoteGetValue(study['0008,0090']),
patientName: remoteGetValue(study['0010,0010']), // 00081190 = URL
patientId: remoteGetValue(study['0010,0020']), patientName: remoteGetValue(study['0010,0010']),
patientBirthdate: remoteGetValue(study['0010,0030']), patientId: remoteGetValue(study['0010,0020']),
patientSex: remoteGetValue(study['0010,0040']), patientBirthdate: remoteGetValue(study['0010,0030']),
studyId: remoteGetValue(study['0020,0010']), patientSex: remoteGetValue(study['0010,0040']),
numberOfStudyRelatedSeries: parseFloat(remoteGetValue(study['0020,1206'])), studyId: remoteGetValue(study['0020,0010']),
numberOfStudyRelatedInstances: parseFloat(remoteGetValue(study['0020,1208'])), numberOfStudyRelatedSeries: parseFloat(remoteGetValue(study['0020,1206'])),
studyDescription: remoteGetValue(study['0008,1030']), numberOfStudyRelatedInstances: parseFloat(remoteGetValue(study['0020,1208'])),
modalities: remoteGetValue(study['0008,0061']) studyDescription: remoteGetValue(study['0008,1030']),
}); modalities: remoteGetValue(study['0008,0061'])
}); });
return studies; });
} return studies;
}
Services.REMOTE.Studies = function(server, filter) {
var parameters = { OHIF.studies.services.REMOTE.Studies = function(server, filter) {
PatientName: filter.patientName ? filter.patientName : "", var parameters = {
PatientID: filter.patientId, PatientName: filter.patientName ? filter.patientName : "",
AccessionNumber: filter.accessionNumber ? filter.accessionNumber : "", PatientID: filter.patientId,
StudyDescription: "", AccessionNumber: filter.accessionNumber ? filter.accessionNumber : "",
StudyDate : "", StudyDescription: "",
StudyTime : "", StudyDate : "",
ReferringPhysicianName : "", StudyTime : "",
PatientBirthDate : "", ReferringPhysicianName : "",
PatientSex : "", PatientBirthDate : "",
StudyID : "", PatientSex : "",
NumberOfStudyRelatedSeries : "", StudyID : "",
NumberOfStudyRelatedInstances : "", NumberOfStudyRelatedSeries : "",
ModalitiesInStudy : "" NumberOfStudyRelatedInstances : "",
}; ModalitiesInStudy : ""
var remote = new OrthancRemote(server.root, server.sourceAE); };
var data = remote.findStudies(server.modality, parameters); var remote = new OrthancRemote(server.root, server.sourceAE);
var data = remote.findStudies(server.modality, parameters);
return resultDataToStudies(data.results);
}; return resultDataToStudies(data.results);
};

View File

@ -337,20 +337,20 @@ function resultDataToStudyMetadata(server, studyInstanceUid, resultData) {
* @param studyInstanceUid * @param studyInstanceUid
* @returns {{seriesList: Array, patientName: *, patientId: *, accessionNumber: *, studyDate: *, modalities: *, studyDescription: *, imageCount: *, studyInstanceUid: *}} * @returns {{seriesList: Array, patientName: *, patientId: *, accessionNumber: *, studyDate: *, modalities: *, studyDescription: *, imageCount: *, studyInstanceUid: *}}
*/ */
Services.WADO.RetrieveMetadata = function(server, studyInstanceUid) { OHIF.studies.services.WADO.RetrieveMetadata = function(server, studyInstanceUid) {
var url = buildUrl(server, studyInstanceUid); var url = buildUrl(server, studyInstanceUid);
try { try {
var result = DICOMWeb.getJSON(url, server.requestOptions); var result = DICOMWeb.getJSON(url, server.requestOptions);
var study = resultDataToStudyMetadata(server, studyInstanceUid, result.data); var study = resultDataToStudyMetadata(server, studyInstanceUid, result.data);
if (!study) { if (!study) {
study = {}; study = {};
} }
study.wadoUriRoot = server.wadoUriRoot; study.wadoUriRoot = server.wadoUriRoot;
study.studyInstanceUid = studyInstanceUid; study.studyInstanceUid = studyInstanceUid;
return study; return study;
} catch (error) { } catch (error) {
OHIF.log.trace(); OHIF.log.trace();

View File

@ -36,7 +36,7 @@ Template.seriesDetailsTable.onRendered(() => {
// Get series list for the study // Get series list for the study
_.map(studies, (selectedStudy, index) => { _.map(studies, (selectedStudy, index) => {
studies[index].seriesList = []; studies[index].seriesList = [];
OHIF.studylist.retrieveStudyMetadata(study => { OHIF.studies.retrieveStudyMetadata(study => {
// Set series list // Set series list
studies[index].seriesList = study.seriesList; studies[index].seriesList = study.seriesList;
studies[index].displaySeriesLoadingText = false; studies[index].displaySeriesLoadingText = false;

View File

@ -13,7 +13,7 @@ export class OHIFStudyMetadataSource extends OHIF.viewerbase.StudyMetadataSource
* @return {Promise} A Promise object * @return {Promise} A Promise object
*/ */
getByInstanceUID(studyInstanceUID) { getByInstanceUID(studyInstanceUID) {
return OHIF.studylist.retrieveStudyMetadata(studyInstanceUID); return OHIF.studies.retrieveStudyMetadata(studyInstanceUID);
} }
/** /**

View File

@ -12,7 +12,7 @@ queryStudies = function(studiesToQuery, options) {
const promises = []; const promises = [];
studiesToQuery.forEach(studyToQuery => { studiesToQuery.forEach(studyToQuery => {
const promise = OHIF.studylist.retrieveStudyMetadata(studyToQuery.studyInstanceUid); const promise = OHIF.studies.retrieveStudyMetadata(studyToQuery.studyInstanceUid);
promise.then(study => { promise.then(study => {
studiesQueried++; studiesQueried++;
notify({ notify({

View File

@ -1,42 +1,12 @@
import { OHIF } from 'meteor/ohif:core'; import { OHIF } from 'meteor/ohif:core';
const note = 'OHIF.studylist.retrieveStudiesMetadata is deprecated.';
const instructions = 'Please use OHIF.studies.retrieveStudiesMetadata instead.';
/** /**
* Retrieves metaData for multiple studies at once. * @deprecated Please use OHIF.studies.retrieveStudiesMetadata instead
*
* This function calls retrieveStudyMetadata several times, asynchronously,
* and waits for all of the results to be returned.
*
* @param studyInstanceUids The UIDs of the Studies to be retrieved
* @return Promise
*/ */
OHIF.studylist.retrieveStudiesMetadata = (studyInstanceUids, seriesInstanceUids, doneCallback, failCallback) => { OHIF.studylist.retrieveStudiesMetadata = function() {
// // Check to make sure studyInstanceUids were actually input OHIF.log.warn(`${note}\n${instructions}`);
// if (!studyInstanceUids || !studyInstanceUids.length) { OHIF.studies.retrieveStudiesMetadata.apply(this, arguments);
// if (failCallback && typeof failCallback === 'function') {
// failCallback('No studyInstanceUids were input');
// }
//
// return;
// }
// Create an empty array to store the Promises for each metaData retrieval call
const promises = [];
// Loop through the array of studyInstanceUids
studyInstanceUids.forEach(function(studyInstanceUid) {
// Send the call, and attach doneCallbacks and failCallbacks
// which can resolve or reject the related promise based on its outcome
const promise = OHIF.studylist.retrieveStudyMetadata(studyInstanceUid, seriesInstanceUids);
// Add the current promise to the array of promises
promises.push(promise);
});
// When all of the promises are complete, this callback runs
const promise = Promise.all(promises);
// Warn the error on console if some retrieval failed
promise.catch(error => OHIF.log.warn(error));
return promise;
}; };

View File

@ -1,110 +1,12 @@
import { Meteor } from 'meteor/meteor';
import { OHIF } from 'meteor/ohif:core'; import { OHIF } from 'meteor/ohif:core';
import 'meteor/ohif:viewerbase';
// Define the StudyMetaDataPromises object. This is used as a cache to store study meta data const note = 'OHIF.studylist.retrieveStudyMetadata is deprecated.';
// promises and prevent unnecessary subsequent calls to the server const instructions = 'Please use OHIF.studies.retrieveStudyMetadata instead.';
const StudyMetaDataPromises = new Map();
/** /**
* Retrieves study metadata using a server call * @deprecated Please use OHIF.studies.retrieveStudyMetadata instead
*
* @param {String} studyInstanceUid The UID of the Study to be retrieved
* @returns {Promise} that will be resolved with the metadata or rejected with the error
*/ */
OHIF.studylist.retrieveStudyMetadata = (studyInstanceUid, seriesInstanceUids) => { OHIF.studylist.retrieveStudyMetadata = function() {
OHIF.log.warn(`${note}\n${instructions}`);
// @TODO: Whenever a study metadata request has failed, its related promise will be rejected once and for all OHIF.studies.retrieveStudyMetadata.apply(this, arguments);
// and further requests for that metadata will always fail. On failure, we probably need to remove the
// corresponding promise from the "StudyMetaDataPromises" map...
// If the StudyMetaDataPromises cache already has a pending or resolved promise related to the
// given studyInstanceUid, then that promise is returned
if (StudyMetaDataPromises.has(studyInstanceUid)) {
return StudyMetaDataPromises.get(studyInstanceUid);
}
console.time('retrieveStudyMetadata');
// Create a promise to handle the data retrieval
const promise = new Promise((resolve, reject) => {
// If no study metadata is in the cache variable, we need to retrieve it from
// the server with a call.
Meteor.call('GetStudyMetadata', studyInstanceUid, function(error, study) {
console.timeEnd('retrieveStudyMetadata');
if (error) {
const errorType = error.error;
let errorMessage = '';
if (errorType === 'server-connection-error') {
errorMessage = 'There was an error connecting to the DICOM server, please verify if it is up and running.'
} else if (errorType === 'server-internal-error') {
errorMessage = `There was an internal error with the DICOM server getting metadeta for ${studyInstanceUid}`;
} else {
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;
}
// Filter series if seriesInstanceUid exists
if (seriesInstanceUids && seriesInstanceUids.length) {
study.seriesList = study.seriesList.filter(series => seriesInstanceUids.indexOf(series.seriesInstanceUid) > -1);
}
if (!study) {
reject(`GetStudyMetadata: No study data returned from server: ${studyInstanceUid}`);
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);
// Add additional metadata to our study from the studylist
const studylistStudy = OHIF.studylist.collections.Studies.findOne({
studyInstanceUid: study.studyInstanceUid,
'seriesList.seriesInstanceUid': '123456',
});
if (studylistStudy) {
Object.assign(study, studylistStudy);
}
// 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);
});
// Resolve the promise with the final study metadata object
resolve(study);
});
});
// Store the promise in cache
StudyMetaDataPromises.set(studyInstanceUid, promise);
return promise;
}; };

View File

@ -44,6 +44,4 @@ Package.onUse(function(api) {
// Client imports // Client imports
api.addFiles('client/index.js', 'client'); api.addFiles('client/index.js', 'client');
api.export('Services', 'server');
}); });

View File

@ -1,5 +1 @@
import './publications.js'; import './publications.js';
import './lib';
import './methods';
import './services';

View File

@ -1,3 +1,2 @@
import './getStudyMetadata.js';
import './importStudies.js'; import './importStudies.js';
import './studylistSearch.js'; import './studylistSearch.js';

View File

@ -18,9 +18,9 @@ Meteor.methods({
try { try {
if (server.type === 'dicomWeb') { if (server.type === 'dicomWeb') {
return Services.QIDO.Studies(server, filter); return OHIF.studies.services.QIDO.Studies(server, filter);
} else if (server.type === 'dimse') { } else if (server.type === 'dimse') {
return Services.DIMSE.Studies(filter); return OHIF.studies.services.DIMSE.Studies(filter);
} }
} catch (error) { } catch (error) {
OHIF.log.trace(); OHIF.log.trace();

View File

@ -1,9 +0,0 @@
Services = {};
Services.QIDO = {};
Services.WADO = {};
Services.DIMSE = {};
Services.REMOTE = {};
remoteGetValue = function(obj) {
return obj ? obj.Value : null;
};

View File

@ -4,8 +4,8 @@
<div class="p-x-1"> <div class="p-x-1">
{{>imageThumbnail (clone this)}} {{>imageThumbnail (clone this)}}
</div> </div>
<div class="seriesDetails clearfix m-x-1 {{#unless stack.seriesDescription}}info-only{{/unless}}"> <div class="seriesDetails flex-h m-x-1 {{#unless stack.seriesDescription}}info-only{{/unless}}">
<div class="seriesDescription"> <div class="seriesDescription flex-grow">
{{stack.seriesDescription}} {{stack.seriesDescription}}
</div> </div>
<div class="seriesInformation"> <div class="seriesInformation">

View File

@ -47,13 +47,7 @@ $seriesCountBackgroundColor = #678696
margin-left: 0 margin-left: 0
width: auto width: auto
.seriesDescription
float: left
width: calc(100% - 50px)
.seriesInformation .seriesInformation
display: table
float: right
padding-right: 4px padding-right: 4px
max-width: 50px max-width: 50px
@ -66,6 +60,9 @@ $seriesCountBackgroundColor = #678696
float: right float: right
font-size: 12px font-size: 12px
margin-left: 4px margin-left: 4px
overflow: hidden
text-overflow: ellipsis
white-space: nowrap
width: calc(100% - 15px) width: calc(100% - 15px)
.icon .icon

View File

@ -68,7 +68,7 @@ Template.studyTimepoint.events({
$selection.removeClass('loading'); $selection.removeClass('loading');
$selection.toggleClass('active'); $selection.toggleClass('active');
// Recalculates the timepoint height to make CSS transition smoother // Recalculates the timepoint height to make CSS transition smoother
const $thumbnails = $selection.find('.studyTimepointThumbnails'); const $thumbnails = $selection.find('.study-browser-series');
$thumbnails.one('transitionend', () => $timepoint.trigger('displayStateChanged')); $thumbnails.one('transitionend', () => $timepoint.trigger('displayStateChanged'));
} }

View File

@ -18,10 +18,12 @@
</div> </div>
</div> </div>
{{#if isSidebar}} {{#if isSidebar}}
<div class="studyTimepointThumbnails"> <div class="study-browser-series">
{{#each thumbnail in (studyThumbnails this.study)}} <div class="study-series-container">
{{>thumbnailEntry (clone this thumbnail=thumbnail)}} {{#each thumbnail in (studyThumbnails this.study)}}
{{/each}} {{>thumbnailEntry (clone this thumbnail=thumbnail)}}
{{/each}}
</div>
</div> </div>
{{/if}} {{/if}}
</div> </div>

View File

@ -52,7 +52,7 @@ Template.studyTimepointStudy.onCreated(() => {
} }
const $study = instance.getStudyElement(); const $study = instance.getStudyElement();
const $thumbnails = $study.find('.studyTimepointThumbnails'); const $thumbnails = $study.find('.study-browser-series');
$study.addClass('active'); $study.addClass('active');
// If element already has max-height property set, .height() // If element already has max-height property set, .height()
// will return that value, so remove it to recalculate // will return that value, so remove it to recalculate
@ -80,7 +80,7 @@ Template.studyTimepointStudy.onRendered(() => {
Template.studyTimepointStudy.events({ Template.studyTimepointStudy.events({
// Recalculates the timepoint height to make CSS transition smoother // Recalculates the timepoint height to make CSS transition smoother
'transitionend .studyTimepointThumbnails'(event, instance) { 'transitionend .study-browser-series'(event, instance) {
if (event.target === event.currentTarget) { if (event.target === event.currentTarget) {
$(event.currentTarget).closest('.studyTimepoint').trigger('displayStateChanged'); $(event.currentTarget).closest('.studyTimepoint').trigger('displayStateChanged');
} }
@ -117,7 +117,7 @@ Template.studyTimepointStudy.events({
if (!alreadyLoaded) { if (!alreadyLoaded) {
const $studies = instance.getStudyElement(true); const $studies = instance.getStudyElement(true);
$studies.trigger('loadStarted'); $studies.trigger('loadStarted');
OHIF.studylist.retrieveStudyMetadata(studyInstanceUid).then(study => { OHIF.studies.retrieveStudyMetadata(studyInstanceUid).then(study => {
instance.data.study = study; instance.data.study = study;
OHIF.viewer.Studies.insert(study); OHIF.viewer.Studies.insert(study);

View File

@ -17,7 +17,7 @@ export const prepareViewerData = ({ studyInstanceUids, seriesInstanceUids, timep
// Retrieve the studies metadata // Retrieve the studies metadata
const promise = new Promise((resolve, reject) => { const promise = new Promise((resolve, reject) => {
const processData = viewerData => { const processData = viewerData => {
OHIF.studylist.retrieveStudiesMetadata(viewerData.studyInstanceUids, viewerData.seriesInstanceUids).then(studies => { OHIF.studies.retrieveStudiesMetadata(viewerData.studyInstanceUids, viewerData.seriesInstanceUids).then(studies => {
// Add additional metadata to our study from the studylist // Add additional metadata to our study from the studylist
studies.forEach(study => { studies.forEach(study => {
const studylistStudy = OHIF.studylist.collections.Studies.findOne({ const studylistStudy = OHIF.studylist.collections.Studies.findOne({