Creating ohif-studies package to deal with studies browsing
This commit is contained in:
parent
956e6da0b3
commit
0b51a53976
@ -87,6 +87,7 @@
|
|||||||
"wsh" : false, // Windows Scripting Host
|
"wsh" : false, // Windows Scripting Host
|
||||||
"yui" : false, // Yahoo User Interface
|
"yui" : false, // Yahoo User Interface
|
||||||
"globals" : {
|
"globals" : {
|
||||||
|
"require": true,
|
||||||
"Package" : true, // Meteor Package definition
|
"Package" : true, // Meteor Package definition
|
||||||
|
|
||||||
"cornerstone": true, // This group: cornerstone globals
|
"cornerstone": true, // This group: cornerstone globals
|
||||||
|
|||||||
@ -6,7 +6,7 @@ html body
|
|||||||
|
|
||||||
html body.stretch
|
html body.stretch
|
||||||
height: 100%
|
height: 100%
|
||||||
minWidth: 0
|
min-width: 0
|
||||||
overflow: hidden
|
overflow: hidden
|
||||||
position: fixed
|
position: fixed
|
||||||
width: 100%
|
width: 100%
|
||||||
|
|||||||
@ -7,3 +7,22 @@ const defaultLevel = Meteor.isProduction ? 'ERROR' : 'TRACE';
|
|||||||
// Create package logger using loglevel
|
// Create package logger using loglevel
|
||||||
OHIF.log = loglevel.getLogger('OHIF');
|
OHIF.log = loglevel.getLogger('OHIF');
|
||||||
OHIF.log.setLevel(defaultLevel);
|
OHIF.log.setLevel(defaultLevel);
|
||||||
|
|
||||||
|
// Add time and timeEnd to OHIF.log namespace
|
||||||
|
const times = new Map();
|
||||||
|
|
||||||
|
// Register the time method
|
||||||
|
OHIF.log.time = givenKey => {
|
||||||
|
const key = typeof givenKey === 'undefined' ? 'default' : givenKey;
|
||||||
|
times.set(key, new Date().getTime());
|
||||||
|
};
|
||||||
|
|
||||||
|
// Register the timeEnd method
|
||||||
|
OHIF.log.timeEnd = givenKey => {
|
||||||
|
const key = typeof givenKey === 'undefined' ? 'default' : givenKey;
|
||||||
|
const now = new Date().getTime();
|
||||||
|
const last = times.get(key) || now;
|
||||||
|
times.delete(key);
|
||||||
|
const duration = now - last;
|
||||||
|
OHIF.log.info(`${key}: ${duration}ms`);
|
||||||
|
};
|
||||||
|
|||||||
5
Packages/ohif-studies/both/main.js
Normal file
5
Packages/ohif-studies/both/main.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
|
|
||||||
|
OHIF.studies = {};
|
||||||
|
|
||||||
|
require('../imports/both');
|
||||||
1
Packages/ohif-studies/client/main.js
Normal file
1
Packages/ohif-studies/client/main.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
require('../imports/client');
|
||||||
0
Packages/ohif-studies/imports/both/index.js
Normal file
0
Packages/ohif-studies/imports/both/index.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import './list.html';
|
||||||
|
import './list.js';
|
||||||
|
import './item.html';
|
||||||
|
import './item.js';
|
||||||
|
import './item.styl';
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
<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-item">
|
||||||
|
{{#if isLoading}}
|
||||||
|
{{>loadingText}}
|
||||||
|
{{/if}}
|
||||||
|
<div class="study-item-box">
|
||||||
|
<div class="study-modality">
|
||||||
|
<div class="study-modality-text" style="{{modalityStyle}}">{{instance.modalities}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="study-text">
|
||||||
|
<div class="study-date">{{formatDA this.studyInformation.date 'D-MMM-YYYY'}}</div>
|
||||||
|
<div class="study-description">{{this.studyInformation.description}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{#if this.settings.studyTemplate}}
|
||||||
|
{{>Template.dynamic template=this.settings.studyTemplate data=(clone this)}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@ -0,0 +1,43 @@
|
|||||||
|
import { Template } from 'meteor/templating';
|
||||||
|
import { $ } from 'meteor/jquery';
|
||||||
|
|
||||||
|
Template.studyBrowserItem.onCreated(() => {
|
||||||
|
const instance = Template.instance();
|
||||||
|
|
||||||
|
const modality = instance.data.studyInformation.modality || 'UN';
|
||||||
|
instance.modalities = modality.replace(/\\/g, ' ');
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.studyBrowserItem.events({
|
||||||
|
'click .study-browser-item'(event, instance) {
|
||||||
|
const element = event.currentTarget;
|
||||||
|
const $element = $(element);
|
||||||
|
$element.trigger('ohif.studies.study.click', instance.data.studyInformation);
|
||||||
|
const { settings, studyInformation } = instance.data;
|
||||||
|
if (settings && typeof settings.studyClickCallback) {
|
||||||
|
settings.studyClickCallback(studyInformation, element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.studyBrowserItem.helpers({
|
||||||
|
modalityStyle() {
|
||||||
|
// Responsively styles the Modality Acronyms for studies
|
||||||
|
// with more than one modality
|
||||||
|
const instance = Template.instance();
|
||||||
|
const numModalities = instance.modalities.split(/\s/g).length;
|
||||||
|
|
||||||
|
if (numModalities === 1) {
|
||||||
|
// If we have only one modality, it should take up the whole div.
|
||||||
|
return 'font-size: 1em';
|
||||||
|
} else if (numModalities === 2) {
|
||||||
|
// If we have two, let them sit side-by-side
|
||||||
|
return 'font-size: 0.75em';
|
||||||
|
} else {
|
||||||
|
// If we have more than two modalities, change the line height to display multiple rows,
|
||||||
|
// depending on the number of modalities we need to display.
|
||||||
|
const lineHeight = Math.ceil(numModalities / 2) * 1.2;
|
||||||
|
return 'line-height: ' + lineHeight + 'em';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -1,4 +1,4 @@
|
|||||||
@import "{ohif:design}/app"
|
@require '{ohif:design}/app'
|
||||||
|
|
||||||
$boxBorderColor = transparent
|
$boxBorderColor = transparent
|
||||||
$boxHoverBackgroundColor = #14191E
|
$boxHoverBackgroundColor = #14191E
|
||||||
@ -11,7 +11,7 @@ $nestingMargin = 6px
|
|||||||
$spacerX = 7px
|
$spacerX = 7px
|
||||||
$spacerY = 12px
|
$spacerY = 12px
|
||||||
|
|
||||||
.studyTimepointStudy
|
.study-browser-item
|
||||||
position: relative
|
position: relative
|
||||||
// required transformation to make inner fixed elements relative to this one
|
// required transformation to make inner fixed elements relative to this one
|
||||||
transform(scale(1))
|
transform(scale(1))
|
||||||
@ -33,12 +33,12 @@ $spacerY = 12px
|
|||||||
opacity: 0.75
|
opacity: 0.75
|
||||||
right: 16px
|
right: 16px
|
||||||
|
|
||||||
.studyModality
|
.study-item-box
|
||||||
opacity: 0.2
|
opacity: 0.2
|
||||||
|
|
||||||
&.active
|
&.active
|
||||||
.studyModality
|
.study-item-box
|
||||||
.studyModalityBox
|
.study-modality
|
||||||
theme('color', '$primaryBackgroundColor')
|
theme('color', '$primaryBackgroundColor')
|
||||||
&
|
&
|
||||||
&:before
|
&:before
|
||||||
@ -53,7 +53,7 @@ $spacerY = 12px
|
|||||||
overflow: hidden
|
overflow: hidden
|
||||||
transition($sidebarTransition)
|
transition($sidebarTransition)
|
||||||
|
|
||||||
.studyModality
|
.study-item-box
|
||||||
border: 1px solid $boxBorderColor
|
border: 1px solid $boxBorderColor
|
||||||
border-radius: 12px
|
border-radius: 12px
|
||||||
cursor: pointer
|
cursor: pointer
|
||||||
@ -78,28 +78,29 @@ $spacerY = 12px
|
|||||||
padding: 0
|
padding: 0
|
||||||
text-align: center
|
text-align: center
|
||||||
|
|
||||||
.studyText
|
.study-text
|
||||||
font-size: 13px
|
font-size: 13px
|
||||||
left: ($spacerX * 3) + $boxWidth + ($nestingMargin * 3)
|
left: ($spacerX * 3) + $boxWidth + ($nestingMargin * 3)
|
||||||
line-height: 14px
|
line-height: 14px
|
||||||
position: absolute
|
position: absolute
|
||||||
right: $spacerX
|
right: $spacerX
|
||||||
top: $spacerY
|
top: $spacerY
|
||||||
.studyDate
|
.study-date
|
||||||
margin-top: 8px
|
margin-top: 8px
|
||||||
theme('color', '$textSecondaryColor')
|
theme('color', '$textSecondaryColor')
|
||||||
.studyDescription
|
.study-description
|
||||||
margin-top: 8px
|
margin-top: 8px
|
||||||
theme('color', '$textPrimaryColor')
|
theme('color', '$textPrimaryColor')
|
||||||
|
|
||||||
.studyModalityBox
|
.study-modality
|
||||||
theme('color', '$textSecondaryColor')
|
theme('color', '$textSecondaryColor')
|
||||||
|
font-size: 20px
|
||||||
line-height: $boxWidth
|
line-height: $boxWidth
|
||||||
margin-left: $nestingMargin * 2
|
margin-left: $nestingMargin * 2
|
||||||
margin-top: $nestingMargin * 2
|
margin-top: $nestingMargin * 2
|
||||||
position: relative
|
position: relative
|
||||||
|
|
||||||
.studyModalityText
|
.study-modality-text
|
||||||
text-align: center
|
text-align: center
|
||||||
text-transform: uppercase
|
text-transform: uppercase
|
||||||
height: 100%
|
height: 100%
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
<template name="studyBrowserList">
|
||||||
|
<div class="study-browser-list {{this.settings.studyListClass}}">
|
||||||
|
{{#each studyInformation in studiesInformation}}
|
||||||
|
{{>studyBrowserItem (clone this studyInformation=studyInformation)}}
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
1
Packages/ohif-studies/imports/client/components/index.js
Normal file
1
Packages/ohif-studies/imports/client/components/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
import './browser';
|
||||||
2
Packages/ohif-studies/imports/client/index.js
Normal file
2
Packages/ohif-studies/imports/client/index.js
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
import './components';
|
||||||
|
import './lib';
|
||||||
0
Packages/ohif-studies/imports/client/lib/index.js
Normal file
0
Packages/ohif-studies/imports/client/lib/index.js
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
import { Meteor } from 'meteor/meteor';
|
||||||
|
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
|
||||||
|
// promises and prevent unnecessary subsequent calls to the server
|
||||||
|
const StudyMetaDataPromises = new Map();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves study metadata using a server call
|
||||||
|
*
|
||||||
|
* @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.studies.retrieveStudyMetadata = (studyInstanceUid, seriesInstanceUids) => {
|
||||||
|
|
||||||
|
// @TODO: Whenever a study metadata request has failed, its related promise will be rejected once and for all
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
const seriesKeys = Array.isArray(seriesInstanceUids) ? '|' + seriesInstanceUids.join('|') : '';
|
||||||
|
const timingKey = `retrieveStudyMetadata[${studyInstanceUid}${seriesKeys}]`;
|
||||||
|
OHIF.log.time(timingKey);
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
OHIF.log.timeEnd(timingKey);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
};
|
||||||
0
Packages/ohif-studies/imports/server/index.js
Normal file
0
Packages/ohif-studies/imports/server/index.js
Normal file
35
Packages/ohif-studies/package.js
Normal file
35
Packages/ohif-studies/package.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
Package.describe({
|
||||||
|
name: 'ohif:studies',
|
||||||
|
summary: 'OHIF Studies Library to deal with studies UI, retrieval and manipulation',
|
||||||
|
version: '0.0.1'
|
||||||
|
});
|
||||||
|
|
||||||
|
Package.onUse(function(api) {
|
||||||
|
api.versionsFrom('1.4');
|
||||||
|
|
||||||
|
api.use([
|
||||||
|
'ecmascript',
|
||||||
|
'templating',
|
||||||
|
'stylus',
|
||||||
|
'http'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Our custom packages
|
||||||
|
api.use([
|
||||||
|
'ohif:design',
|
||||||
|
'ohif:core',
|
||||||
|
'ohif:log',
|
||||||
|
'ohif:dicom-services',
|
||||||
|
'ohif:viewerbase',
|
||||||
|
'ohif:wadoproxy'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Client and server imports
|
||||||
|
api.addFiles('both/main.js', [ 'client', 'server' ]);
|
||||||
|
|
||||||
|
// Server imports
|
||||||
|
api.addFiles('server/main.js', 'server');
|
||||||
|
|
||||||
|
// Client imports
|
||||||
|
api.addFiles('client/main.js', 'client');
|
||||||
|
});
|
||||||
1
Packages/ohif-studies/server/main.js
Normal file
1
Packages/ohif-studies/server/main.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
require('../imports/server');
|
||||||
@ -92,7 +92,7 @@ Template.studySeriesQuickSwitch.events({
|
|||||||
instance.$('.js-quick-switch, .switchSectionSeries').removeClass('hover');
|
instance.$('.js-quick-switch, .switchSectionSeries').removeClass('hover');
|
||||||
instance.$('.quickSwitchWrapper').removeClass('overlay');
|
instance.$('.quickSwitchWrapper').removeClass('overlay');
|
||||||
},
|
},
|
||||||
'click .studyTimepointStudy'(event, instance) {
|
'click .study-browser-item'(event, instance) {
|
||||||
instance.$('.switchSectionSeries').addClass('hover');
|
instance.$('.switchSectionSeries').addClass('hover');
|
||||||
},
|
},
|
||||||
'scroll .scrollArea'(event) {
|
'scroll .scrollArea'(event) {
|
||||||
|
|||||||
@ -105,7 +105,7 @@ $seriesSpacing = 2px
|
|||||||
.studySwitch
|
.studySwitch
|
||||||
.studyTimepointBrowser
|
.studyTimepointBrowser
|
||||||
background-color: transparent
|
background-color: transparent
|
||||||
.studyTimepointStudy.active .studyModality
|
.study-browser-item.active .study-item-box
|
||||||
theme('box-shadow', 'inset 0 0 0 3px $activeColor')
|
theme('box-shadow', 'inset 0 0 0 3px $activeColor')
|
||||||
.studyBox
|
.studyBox
|
||||||
theme('background-color', '$uiGrayDark')
|
theme('background-color', '$uiGrayDark')
|
||||||
|
|||||||
@ -53,7 +53,7 @@ Template.studyTimepoint.events({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Removes selected state from all studies but the triggered study
|
// Removes selected state from all studies but the triggered study
|
||||||
$studiesTarget.find('.studyTimepointStudy').not($selection).removeClass('active');
|
$studiesTarget.find('.study-browser-item').not($selection).removeClass('active');
|
||||||
|
|
||||||
if (changed.isQuickSwitch) {
|
if (changed.isQuickSwitch) {
|
||||||
// Reset active studies map to allow only one active study
|
// Reset active studies map to allow only one active study
|
||||||
|
|||||||
@ -26,7 +26,7 @@
|
|||||||
<hr>
|
<hr>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{#if and this.currentStudy (not showAdditionalTimepoints) hasAdditionalTimepoints}}
|
{{#if and this.currentStudy (not showAdditionalTimepoints) hasAdditionalTimepoints}}
|
||||||
<div class="studyModality additional">
|
<div class="study-item-box additional">
|
||||||
Show additional timepoints
|
Show additional timepoints
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|||||||
@ -28,22 +28,18 @@ Template.studyTimepointBrowser.onCreated(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return timepoint.studyInstanceUids.map(studyInstanceUid => {
|
return timepoint.studyInstanceUids.map(studyInstanceUid => {
|
||||||
const query = {
|
const query = { studyInstanceUid };
|
||||||
patientId: timepoint.patientId,
|
|
||||||
studyInstanceUid: studyInstanceUid
|
|
||||||
};
|
|
||||||
|
|
||||||
const loadedStudy = OHIF.viewer.Studies.findBy(query);
|
const loadedStudy = OHIF.viewer.Studies.findBy(query);
|
||||||
if (loadedStudy) {
|
if (loadedStudy) return loadedStudy;
|
||||||
return loadedStudy;
|
|
||||||
}
|
|
||||||
|
|
||||||
const notYetLoaded = OHIF.studylist.collections.Studies.findOne(query);
|
const notYetLoaded = OHIF.studylist.collections.Studies.findOne(query);
|
||||||
if (!notYetLoaded) {
|
if (notYetLoaded) return notYetLoaded;
|
||||||
throw new OHIFError(`No study data available for Study: ${studyInstanceUid}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return notYetLoaded;
|
// const studyData = _.findWhere(timepoint.studiesData, query);
|
||||||
|
// if (studyData) return studyData;
|
||||||
|
|
||||||
|
throw new OHIFError(`No study data available for Study: ${studyInstanceUid}`);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@ -57,7 +53,7 @@ Template.studyTimepointBrowser.onRendered(() => {
|
|||||||
const type = instance.timepointViewType.get();
|
const type = instance.timepointViewType.get();
|
||||||
|
|
||||||
// Removes all active classes to collapse the timepoints and studies
|
// Removes all active classes to collapse the timepoints and studies
|
||||||
instance.$('.timepointEntry, .studyTimepointStudy').removeClass('active');
|
instance.$('.timepointEntry, .study-browser-item').removeClass('active');
|
||||||
if (type === 'key' && !instance.data.currentStudy) {
|
if (type === 'key' && !instance.data.currentStudy) {
|
||||||
// Show only first timepoint expanded for key timepoints
|
// Show only first timepoint expanded for key timepoints
|
||||||
instance.$('.timepointEntry:first').addClass('active');
|
instance.$('.timepointEntry:first').addClass('active');
|
||||||
@ -71,7 +67,7 @@ Template.studyTimepointBrowser.onRendered(() => {
|
|||||||
// Wait for rerendering and set the timepoint as active
|
// Wait for rerendering and set the timepoint as active
|
||||||
instance.refreshActiveStudies = () => Tracker.afterFlush(() => {
|
instance.refreshActiveStudies = () => Tracker.afterFlush(() => {
|
||||||
_.each(activeStudiesUids, studyInstanceUid => {
|
_.each(activeStudiesUids, studyInstanceUid => {
|
||||||
instance.$(`.studyTimepointStudy[data-uid='${studyInstanceUid}']`).addClass('active');
|
instance.$(`.study-browser-item[data-uid='${studyInstanceUid}']`).addClass('active');
|
||||||
});
|
});
|
||||||
// Show only first timepoint expanded for key timepoints
|
// Show only first timepoint expanded for key timepoints
|
||||||
instance.$('.timepointEntry:first').addClass('active');
|
instance.$('.timepointEntry:first').addClass('active');
|
||||||
@ -108,7 +104,7 @@ Template.studyTimepointBrowser.events({
|
|||||||
$timepoint.toggleClass('active');
|
$timepoint.toggleClass('active');
|
||||||
},
|
},
|
||||||
|
|
||||||
'click .studyModality.additional'(event, instance) {
|
'click .study-item-box.additional'(event, instance) {
|
||||||
// Show all key timepoints
|
// Show all key timepoints
|
||||||
instance.showAdditionalTimepoints.set(true);
|
instance.showAdditionalTimepoints.set(true);
|
||||||
}
|
}
|
||||||
@ -199,12 +195,22 @@ Template.studyTimepointBrowser.helpers({
|
|||||||
|
|
||||||
const studies = instance.getStudies(timepoint);
|
const studies = instance.getStudies(timepoint);
|
||||||
|
|
||||||
|
const includedUids = new Set();
|
||||||
const modalities = {};
|
const modalities = {};
|
||||||
studies.forEach(study => {
|
studies.forEach(study => {
|
||||||
const modality = study.modalities || 'UN';
|
const modality = study.modalities || 'UN';
|
||||||
modalities[modality] = modalities[modality] + 1 || 1;
|
modalities[modality] = modalities[modality] + 1 || 1;
|
||||||
|
includedUids.add(study.studyInstanceUid);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (_.isArray(timepoint.studiesData)) {
|
||||||
|
timepoint.studiesData.forEach(({ modality, studyInstanceUid }) => {
|
||||||
|
if (includedUids.has(studyInstanceUid)) return;
|
||||||
|
modalities[modality] = modalities[modality] + 1 || 1;
|
||||||
|
includedUids.add(studyInstanceUid);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const result = [];
|
const result = [];
|
||||||
_.each(modalities, (count, modality) => {
|
_.each(modalities, (count, modality) => {
|
||||||
result.push(`${count} ${modality}`);
|
result.push(`${count} ${modality}`);
|
||||||
|
|||||||
@ -1,23 +1,19 @@
|
|||||||
<template name="studyTimepointStudy">
|
<template name="studyTimepointStudy">
|
||||||
{{#let isSidebar=(isUndefined viewportIndex)}}
|
{{#let isSidebar=(isUndefined viewportIndex)}}
|
||||||
<div class="studyTimepointStudy {{#if isSidebar}}studySidebarTimepoint{{else}}studyQuickSwitchTimepoint{{/if}} {{#if this.active}}active{{/if}} {{#if isLoading}}loading{{/if}}" data-uid="{{this.study.studyInstanceUid}}">
|
<div class="study-browser-item {{#if isSidebar}}studySidebarTimepoint{{else}}studyQuickSwitchTimepoint{{/if}} {{#if this.active}}active{{/if}} {{#if isLoading}}loading{{/if}}" data-uid="{{this.study.studyInstanceUid}}">
|
||||||
<div class="studyItem">
|
<div class="study-item">
|
||||||
{{#if isLoading}}
|
{{#if isLoading}}
|
||||||
{{>loadingText}}
|
{{>loadingText}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<div class="studyModality">
|
<div class="study-item-box">
|
||||||
<div class="studyModalityBox">
|
<div class="study-modality">
|
||||||
<div class="studyModalityText" style="{{modalityStyle}}">
|
<div class="study-modality-text" style="{{modalityStyle}}">
|
||||||
{{#if instance.modalities}}
|
{{choose instance.modalities 'UN'}}
|
||||||
{{modalities}}
|
|
||||||
{{else}}
|
|
||||||
UN
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="studyText">
|
<div class="study-text">
|
||||||
<div class="studyDate">{{formatDA instance.studyDate 'D-MMM-YYYY'}}</div>
|
<div class="study-date">{{formatDA instance.studyDate 'D-MMM-YYYY'}}</div>
|
||||||
<div class="studyDescription">{{instance.studyDescription}}</div>
|
<div class="study-description">{{instance.studyDescription}}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -21,7 +21,7 @@ Template.studyTimepointStudy.onCreated(() => {
|
|||||||
// Get the current study element
|
// Get the current study element
|
||||||
instance.getStudyElement = (isGlobal=false) => {
|
instance.getStudyElement = (isGlobal=false) => {
|
||||||
const studyInstanceUid = instance.data.study.studyInstanceUid;
|
const studyInstanceUid = instance.data.study.studyInstanceUid;
|
||||||
const selector = `.studyTimepointStudy[data-uid='${studyInstanceUid}']`;
|
const selector = `.study-browser-item[data-uid='${studyInstanceUid}']`;
|
||||||
return isGlobal ? $(selector) : instance.$browser.find(selector);
|
return isGlobal ? $(selector) : instance.$browser.find(selector);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ Template.studyTimepointStudy.onRendered(() => {
|
|||||||
const instance = Template.instance();
|
const instance = Template.instance();
|
||||||
|
|
||||||
// Keep the study timepoint browser element to manipulate elements even after DOM is removed
|
// Keep the study timepoint browser element to manipulate elements even after DOM is removed
|
||||||
instance.$browser = instance.$('.studyTimepointStudy').closest('.studyTimepointBrowser');
|
instance.$browser = instance.$('.study-browser-item').closest('.studyTimepointBrowser');
|
||||||
|
|
||||||
instance.initializeStudyWrapper();
|
instance.initializeStudyWrapper();
|
||||||
});
|
});
|
||||||
@ -87,23 +87,23 @@ Template.studyTimepointStudy.events({
|
|||||||
},
|
},
|
||||||
|
|
||||||
// Transfers the active state to the current study
|
// Transfers the active state to the current study
|
||||||
'click .studyQuickSwitchTimepoint .studyModality'(event, instance) {
|
'click .studyQuickSwitchTimepoint .study-item-container'(event, instance) {
|
||||||
instance.select(true);
|
instance.select(true);
|
||||||
},
|
},
|
||||||
|
|
||||||
// Set loading state
|
// Set loading state
|
||||||
'loadStarted .studyTimepointStudy'(event, instance) {
|
'loadStarted .study-browser-item'(event, instance) {
|
||||||
instance.loading.set(true);
|
instance.loading.set(true);
|
||||||
},
|
},
|
||||||
|
|
||||||
// Remove loading state and fix the thumbnails wrappers height
|
// Remove loading state and fix the thumbnails wrappers height
|
||||||
'loadEnded .studyTimepointStudy'(event, instance) {
|
'loadEnded .study-browser-item'(event, instance) {
|
||||||
instance.loading.set(false);
|
instance.loading.set(false);
|
||||||
instance.initializeStudyWrapper();
|
instance.initializeStudyWrapper();
|
||||||
},
|
},
|
||||||
|
|
||||||
// Changes the current study selection for the clicked study
|
// Changes the current study selection for the clicked study
|
||||||
'click .studyModality'(event, instance) {
|
'click .study-item-box'(event, instance) {
|
||||||
const studyData = instance.data.study;
|
const studyData = instance.data.study;
|
||||||
const { studyInstanceUid } = studyData;
|
const { studyInstanceUid } = studyData;
|
||||||
const isQuickSwitch = instance.isQuickSwitch();
|
const isQuickSwitch = instance.isQuickSwitch();
|
||||||
@ -167,15 +167,15 @@ Template.studyTimepointStudy.helpers({
|
|||||||
|
|
||||||
if (numModalities === 1) {
|
if (numModalities === 1) {
|
||||||
// If we have only one modality, it should take up the whole div.
|
// If we have only one modality, it should take up the whole div.
|
||||||
return 'font-size: 1vw';
|
return 'font-size: 1em';
|
||||||
} else if (numModalities === 2) {
|
} else if (numModalities === 2) {
|
||||||
// If we have two, let them sit side-by-side
|
// If we have two, let them sit side-by-side
|
||||||
return 'font-size: 0.75vw';
|
return 'font-size: 0.75em';
|
||||||
} else {
|
} else {
|
||||||
// If we have more than two modalities, change the line height to display multiple rows,
|
// If we have more than two modalities, change the line height to display multiple rows,
|
||||||
// depending on the number of modalities we need to display.
|
// depending on the number of modalities we need to display.
|
||||||
const lineHeight = Math.ceil(numModalities / 2) * 1.2;
|
const lineHeight = Math.ceil(numModalities / 2) * 1.2;
|
||||||
return 'line-height: ' + lineHeight + 'vh';
|
return 'line-height: ' + lineHeight + 'em';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -17,30 +17,25 @@ 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 => {
|
||||||
const studies = [];
|
OHIF.studylist.retrieveStudiesMetadata(viewerData.studyInstanceUids, viewerData.seriesInstanceUids).then(studies => {
|
||||||
|
// Add additional metadata to our study from the studylist
|
||||||
|
studies.forEach(study => {
|
||||||
|
const studylistStudy = OHIF.studylist.collections.Studies.findOne({
|
||||||
|
studyInstanceUid: study.studyInstanceUid
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!studylistStudy) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(study, studylistStudy);
|
||||||
|
});
|
||||||
|
|
||||||
resolve({
|
resolve({
|
||||||
studies,
|
studies,
|
||||||
viewerData
|
viewerData
|
||||||
});
|
});
|
||||||
// OHIF.studylist.retrieveStudiesMetadata(viewerData.studyInstanceUids, viewerData.seriesInstanceUids).then(studies => {
|
}).catch(reject);
|
||||||
// // Add additional metadata to our study from the studylist
|
|
||||||
// studies.forEach(study => {
|
|
||||||
// const studylistStudy = OHIF.studylist.collections.Studies.findOne({
|
|
||||||
// studyInstanceUid: study.studyInstanceUid
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// if (!studylistStudy) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Object.assign(study, studylistStudy);
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// resolve({
|
|
||||||
// studies,
|
|
||||||
// viewerData
|
|
||||||
// });
|
|
||||||
// }).catch(reject);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check if the studies are already given and ignore the timepoint ID if so
|
// Check if the studies are already given and ignore the timepoint ID if so
|
||||||
|
|||||||
@ -192,7 +192,6 @@ Package.onUse(function(api) {
|
|||||||
api.addFiles('client/components/viewer/studyTimepointBrowser/studyTimepointBrowser.js', 'client');
|
api.addFiles('client/components/viewer/studyTimepointBrowser/studyTimepointBrowser.js', 'client');
|
||||||
|
|
||||||
api.addFiles('client/components/viewer/studyTimepointBrowser/studyTimepointStudy.html', 'client');
|
api.addFiles('client/components/viewer/studyTimepointBrowser/studyTimepointStudy.html', 'client');
|
||||||
api.addFiles('client/components/viewer/studyTimepointBrowser/studyTimepointStudy.styl', 'client');
|
|
||||||
api.addFiles('client/components/viewer/studyTimepointBrowser/studyTimepointStudy.js', 'client');
|
api.addFiles('client/components/viewer/studyTimepointBrowser/studyTimepointStudy.js', 'client');
|
||||||
|
|
||||||
api.addFiles('client/components/viewer/windowLevelPresets/form.html', 'client');
|
api.addFiles('client/components/viewer/windowLevelPresets/form.html', 'client');
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user