Study metadata is added on study browser click

This commit is contained in:
Erik Ziegler 2016-06-09 19:11:11 +02:00
parent 757513906c
commit 521de2fad3
13 changed files with 117 additions and 179 deletions

View File

@ -67,6 +67,13 @@ $expandedHeight = 160px
#worklistTabs
height: "calc(100% - %s)" % $topBarHeight
.tab-pane
.viewerContainer
.loadingTextDiv
color: $textSecondaryColor
font-size: 30px
font-weight: 200
// Override Viewerbase's borders
#imageViewerViewports .viewportContainer
border: none !important

View File

@ -1,15 +0,0 @@
<template name="studyDateList">
<div class="studyDateList">
<label>Study Date:</label>
<span class="loading"><i class="fa fa-spinner fa-spin"></i></span>
<select id="selectStudyDate">
{{#each relatedStudies}}
{{ #if timepointName }}
<option value="{{studyInstanceUid}}" selected={{selected}}>{{formatDA studyDate}} ({{timepointName}}): {{studyDescription}}</option>
{{ else }}
<option value="{{studyInstanceUid}}" selected={{selected}}>{{formatDA studyDate}}: {{studyDescription}}</option>
{{ /if }}
{{/each}}
</select>
</div>
</template>

View File

@ -1,125 +0,0 @@
Template.studyDateList.helpers({
/**
* Returns an array of studies that are related to the current study by patient ID.
* The value for 'selected' for the currently loaded study is set to true, so that
* this becomes the current option in the combo box.
*
* @returns {*} Array of studies that are related to the current study by patient ID
*/
relatedStudies: function() {
// Check which study is currently loaded into the study browser
var currentStudyInBrowser = ViewerStudies.findOne({
selected: true
});
// Find all Timepoint-associated studies which have the same patientId as the currently selected study
var relatedStudies = Studies.find({
patientId: currentStudyInBrowser.patientId
}, {
sort: {
studyDate: -1
}
}).fetch();
// If no Study / Timepoint associated studies exist, just
// return the list of loaded studies
if (!relatedStudies.length) {
return ViewerStudies.find();
}
// Modify the array of related studies so the default option is the currently selected study
relatedStudies.forEach(function(study, index) {
// If the studyInstanceUid matches that of the current study in the browser,
// Set this to 'selected', so that it becomes the default option
if (study.studyInstanceUid === currentStudyInBrowser.studyInstanceUid) {
relatedStudies[index].selected = true;
}
});
// Use this array to populate the combo box
return relatedStudies;
},
timepointName: function() {
var data = this;
var study = Studies.findOne({
studyInstanceUid: data.studyInstanceUid
});
if (!study) {
return;
}
var timepoint = Timepoints.findOne({
timepointId: study.timepointId
});
if (!timepoint) {
return;
}
return getTimepointName(timepoint);
}
});
Template.studyDateList.events({
/**
* When the study date selector combo box is changed, we will
* hide the select box, temporarily display a loading sign, and grab
* the selected study. Once the study has been retrieved it is added
* into the ViewerStudies collection and set as selected. This reactively
* populated the thumbnail browser.
*
* @param e The select box change event
*/
'change select#selectStudyDate': function(e) {
var selectBox = $(e.currentTarget);
var studyInstanceUid = selectBox.val();
// Hide the select box
selectBox.css('display', 'none');
// Show the loading indicator
var loadingIndicator = selectBox.siblings('.loading');
loadingIndicator.css('display', 'block');
getStudyMetadata(studyInstanceUid, function(study) {
sortStudy(study);
// Hide the loading indicator
loadingIndicator.css('display', 'none');
// Show the select box again
selectBox.css('display', 'block');
// Set "Selected" to false for the entire collection
ViewerStudies.update({}, {
$set: {
selected: false
}
}, {
multi: true
});
// Check if this study already exists in the ViewerStudies collection
// of loaded studies. If it does, set it's 'selected' value to true.
var existingStudy = ViewerStudies.findOne({
studyInstanceUid: studyInstanceUid
});
if (existingStudy) {
// Set the current finding in the collection to true
ViewerStudies.update(existingStudy._id, {
$set: {
selected: true
}
});
return;
}
// If the study does not exist, add the 'selected' key to the object
// with the value True, and insert it into the ViewerStudies Collection
study.selected = true;
ViewerStudies.insert(study);
});
}
});

View File

@ -1,23 +0,0 @@
.studyDateList
margin: 0 auto 15px
text-align: center
width: 100%
#selectStudyDate
color: black
background-color: #666
border-color: #666
width: 96%
margin: 0 auto
label
font-weight: normal
color: #666
float: left
padding-left: 2%
.loading
display: none
color: white
text-align: center

View File

@ -1,8 +1,8 @@
<template name="studyTimepoint">
<div class="studyTimepointWrapper">
<div class="studyTimepoint">
{{#each study in this.studies}}
{{>studyTimepointStudy study=study}}
{{#each studies}}
{{>studyTimepointStudy study=this active=isActive}}
{{/each}}
</div>
</div>

View File

@ -1,3 +1,5 @@
var isActive = {};
// Initialize the timepoint wrapper max-height to enable CSS transition
Template.studyTimepoint.onRendered(() => {
const instance = Template.instance();
@ -21,19 +23,22 @@ Template.studyTimepoint.onRendered(() => {
Template.studyTimepoint.events({
// Changes the selected study
'selectionChanged .studyTimepoint'(event, instance, selection) {
const $selection = $(selection);
'selectionChanged .studyTimepoint'(event, instance, changed) {
const $selection = $(changed.selection);
const $thumbnails = $selection.find('.studyTimepointThumbnails');
const $timepoint = instance.$('.studyTimepoint');
const studyInstanceUid = changed.studyInstanceUid;
// Set the max-height to inherit to be able to expand the wrapper on its full height
instance.$('.studyTimepointWrapper').css('max-height', 'inherit');
// Removes selected state from all studies but the triggered study
instance.$('.studyTimepointStudy').not(selection).removeClass('active');
instance.$('.studyTimepointStudy').not($selection).removeClass('active');
// Toggle selected state for the triggered study
$selection.removeClass('loading');
$selection.toggleClass('active');
isActive[studyInstanceUid] = $selection.hasClass('active');
// Recalculates the timepoint height to make CSS transition smoother
$thumbnails.one('transitionend', () => $timepoint.trigger('displayStateChanged'));
@ -47,3 +52,13 @@ Template.studyTimepoint.events({
$wrapper.css('max-height', $timepoint.height());
}
});
Template.studyTimepoint.helpers({
isActive() {
if (!this.studyInstanceUid) {
return;
}
return isActive[this.studyInstanceUid];
}
})

View File

@ -3,10 +3,25 @@ Template.studyTimepointBrowser.onCreated(() => {
// Get the studies for a specific timepoint
instance.getStudies = timepoint => {
const query = {
selected: true
};
return ViewerStudies.find(query);
return timepoint.studyInstanceUids.map(studyInstanceUid => {
const query = {
patientId: timepoint.patientId,
timepointId: timepoint.timepointId,
studyInstanceUid: studyInstanceUid
};
var loadedStudy = ViewerStudies.findOne(query);
if (loadedStudy) {
return loadedStudy;
}
var notYetLoaded = Studies.findOne(query);
if (!notYetLoaded) {
throw "No study data available for Study: " + studyInstanceUid;
}
return notYetLoaded;
});
};
});

View File

@ -1,5 +1,6 @@
<template name="studyTimepointStudy">
<div class="studyTimepointStudy">
<div class="studyTimepointStudy {{#if this.active}}active{{/if}}">
{{ >loadingText }}
<div class="studyModality">
<div class="studyModalityBox">
{{#if this.study.modalities}}

View File

@ -6,6 +6,14 @@ Template.studyTimepointStudy.onRendered(() => {
$study.addClass('active');
$thumbnails.css('max-height', $thumbnails.height());
$study.removeClass('active');
// Here we add, remove, and add the active class again because this way
// the max-height animation appears smooth to the user.
if (instance.data.active) {
Meteor.setTimeout(() => {
$study.addClass('active');
}, 1);
}
});
Template.studyTimepointStudy.events({
@ -20,7 +28,32 @@ Template.studyTimepointStudy.events({
const $study = $(event.currentTarget).closest('.studyTimepointStudy');
const $timepoint = $study.closest('.studyTimepoint');
const study = $study[0];
$timepoint.trigger('selectionChanged', [study]);
const studyInstanceUid = this.study.studyInstanceUid;
const selectionChanged = {
selection: [study],
studyInstanceUid: studyInstanceUid
};
// Check if the study already has series data,
// and if not, retrieve it.
if (!this.study.seriesList) {
var alreadyLoaded = ViewerStudies.findOne({
studyInstanceUid: studyInstanceUid
});
if (!alreadyLoaded) {
study.classList.add('loading');
getStudyMetadata(studyInstanceUid, (studyData) => {
ViewerStudies.insert(studyData);
$timepoint.trigger('selectionChanged', selectionChanged);
});
} else {
this.study.seriesList = alreadyLoaded.seriesList;
}
} else {
$timepoint.trigger('selectionChanged', selectionChanged);
}
}
});

View File

@ -14,6 +14,26 @@ $spacerX = 7px
$spacerY = 12px
.studyTimepointStudy
position: relative
.loadingTextDiv
padding: 25px
border-radius: 12px
position: absolute
opacity: 0
z-index: 100
background-color: $boxHoverBackgroundColor
pointer-events: none
margin: 0
&.loading
.loadingTextDiv
width: 100%
height: 100%
opacity: 0.75
.studyModality
opacity: 0.2
&.active
.studyModality

View File

@ -151,16 +151,14 @@ Package.onUse(function(api) {
api.addFiles('client/components/nonTargetLesionDialog/nonTargetLesionDialog.styl', 'client');
api.addFiles('client/components/nonTargetLesionDialog/nonTargetLesionDialog.js', 'client');
api.addFiles('client/components/studyDateList/studyDateList.html', 'client');
api.addFiles('client/components/studyDateList/studyDateList.styl', 'client');
api.addFiles('client/components/studyDateList/studyDateList.js', 'client');
api.addFiles('client/components/studyTimepointBrowser/studyTimepoint.html', 'client');
api.addFiles('client/components/studyTimepointBrowser/studyTimepoint.styl', 'client');
api.addFiles('client/components/studyTimepointBrowser/studyTimepoint.js', 'client');
api.addFiles('client/components/studyTimepointBrowser/studyTimepointBrowser.html', 'client');
api.addFiles('client/components/studyTimepointBrowser/studyTimepointBrowser.styl', 'client');
api.addFiles('client/components/studyTimepointBrowser/studyTimepointBrowser.js', 'client');
api.addFiles('client/components/studyTimepointBrowser/studyTimepointStudy.html', 'client');
api.addFiles('client/components/studyTimepointBrowser/studyTimepointStudy.styl', 'client');
api.addFiles('client/components/studyTimepointBrowser/studyTimepointStudy.js', 'client');

View File

@ -9,7 +9,7 @@ $inputPlaceholderColor = lightgray
$studyListToolbarHeight = 75px
$theadBackgroundColor = #151a1f
$tablePadding = 60px
$tablePadding = 8%
$bodyCellHeight = 40px
.studyListToolbar
@ -47,6 +47,7 @@ $bodyCellHeight = 40px
z-index: 1
#studyListContainer
width: 100%
padding: 0 $tablePadding
position: absolute
z-index: 2

View File

@ -49,6 +49,17 @@ getStudyMetadata = function(studyInstanceUid, doneCallback, failCallback) {
// and instance number in ascending order
sortStudy(study);
// Add additional metadata to our study from the worklist
var worklistStudy = WorklistStudies.findOne({
studyInstanceUid: study.studyInstanceUid
});
if (!worklistStudy) {
return;
}
$.extend(study, worklistStudy);
// Then we store this data in the cache variable
StudyMetaData[studyInstanceUid] = study;