Study metadata is added on study browser click
This commit is contained in:
parent
757513906c
commit
521de2fad3
@ -67,6 +67,13 @@ $expandedHeight = 160px
|
|||||||
#worklistTabs
|
#worklistTabs
|
||||||
height: "calc(100% - %s)" % $topBarHeight
|
height: "calc(100% - %s)" % $topBarHeight
|
||||||
|
|
||||||
|
.tab-pane
|
||||||
|
.viewerContainer
|
||||||
|
.loadingTextDiv
|
||||||
|
color: $textSecondaryColor
|
||||||
|
font-size: 30px
|
||||||
|
font-weight: 200
|
||||||
|
|
||||||
// Override Viewerbase's borders
|
// Override Viewerbase's borders
|
||||||
#imageViewerViewports .viewportContainer
|
#imageViewerViewports .viewportContainer
|
||||||
border: none !important
|
border: none !important
|
||||||
|
|||||||
@ -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>
|
|
||||||
@ -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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@ -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
|
|
||||||
|
|
||||||
@ -1,8 +1,8 @@
|
|||||||
<template name="studyTimepoint">
|
<template name="studyTimepoint">
|
||||||
<div class="studyTimepointWrapper">
|
<div class="studyTimepointWrapper">
|
||||||
<div class="studyTimepoint">
|
<div class="studyTimepoint">
|
||||||
{{#each study in this.studies}}
|
{{#each studies}}
|
||||||
{{>studyTimepointStudy study=study}}
|
{{>studyTimepointStudy study=this active=isActive}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
var isActive = {};
|
||||||
|
|
||||||
// Initialize the timepoint wrapper max-height to enable CSS transition
|
// Initialize the timepoint wrapper max-height to enable CSS transition
|
||||||
Template.studyTimepoint.onRendered(() => {
|
Template.studyTimepoint.onRendered(() => {
|
||||||
const instance = Template.instance();
|
const instance = Template.instance();
|
||||||
@ -21,19 +23,22 @@ Template.studyTimepoint.onRendered(() => {
|
|||||||
|
|
||||||
Template.studyTimepoint.events({
|
Template.studyTimepoint.events({
|
||||||
// Changes the selected study
|
// Changes the selected study
|
||||||
'selectionChanged .studyTimepoint'(event, instance, selection) {
|
'selectionChanged .studyTimepoint'(event, instance, changed) {
|
||||||
const $selection = $(selection);
|
const $selection = $(changed.selection);
|
||||||
const $thumbnails = $selection.find('.studyTimepointThumbnails');
|
const $thumbnails = $selection.find('.studyTimepointThumbnails');
|
||||||
const $timepoint = instance.$('.studyTimepoint');
|
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
|
// Set the max-height to inherit to be able to expand the wrapper on its full height
|
||||||
instance.$('.studyTimepointWrapper').css('max-height', 'inherit');
|
instance.$('.studyTimepointWrapper').css('max-height', 'inherit');
|
||||||
|
|
||||||
// Removes selected state from all studies but the triggered study
|
// 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
|
// Toggle selected state for the triggered study
|
||||||
|
$selection.removeClass('loading');
|
||||||
$selection.toggleClass('active');
|
$selection.toggleClass('active');
|
||||||
|
isActive[studyInstanceUid] = $selection.hasClass('active');
|
||||||
|
|
||||||
// Recalculates the timepoint height to make CSS transition smoother
|
// Recalculates the timepoint height to make CSS transition smoother
|
||||||
$thumbnails.one('transitionend', () => $timepoint.trigger('displayStateChanged'));
|
$thumbnails.one('transitionend', () => $timepoint.trigger('displayStateChanged'));
|
||||||
@ -47,3 +52,13 @@ Template.studyTimepoint.events({
|
|||||||
$wrapper.css('max-height', $timepoint.height());
|
$wrapper.css('max-height', $timepoint.height());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Template.studyTimepoint.helpers({
|
||||||
|
isActive() {
|
||||||
|
if (!this.studyInstanceUid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isActive[this.studyInstanceUid];
|
||||||
|
}
|
||||||
|
})
|
||||||
@ -3,10 +3,25 @@ Template.studyTimepointBrowser.onCreated(() => {
|
|||||||
|
|
||||||
// Get the studies for a specific timepoint
|
// Get the studies for a specific timepoint
|
||||||
instance.getStudies = timepoint => {
|
instance.getStudies = timepoint => {
|
||||||
const query = {
|
return timepoint.studyInstanceUids.map(studyInstanceUid => {
|
||||||
selected: true
|
const query = {
|
||||||
};
|
patientId: timepoint.patientId,
|
||||||
return ViewerStudies.find(query);
|
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;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
<template name="studyTimepointStudy">
|
<template name="studyTimepointStudy">
|
||||||
<div class="studyTimepointStudy">
|
<div class="studyTimepointStudy {{#if this.active}}active{{/if}}">
|
||||||
|
{{ >loadingText }}
|
||||||
<div class="studyModality">
|
<div class="studyModality">
|
||||||
<div class="studyModalityBox">
|
<div class="studyModalityBox">
|
||||||
{{#if this.study.modalities}}
|
{{#if this.study.modalities}}
|
||||||
|
|||||||
@ -6,6 +6,14 @@ Template.studyTimepointStudy.onRendered(() => {
|
|||||||
$study.addClass('active');
|
$study.addClass('active');
|
||||||
$thumbnails.css('max-height', $thumbnails.height());
|
$thumbnails.css('max-height', $thumbnails.height());
|
||||||
$study.removeClass('active');
|
$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({
|
Template.studyTimepointStudy.events({
|
||||||
@ -20,7 +28,32 @@ Template.studyTimepointStudy.events({
|
|||||||
const $study = $(event.currentTarget).closest('.studyTimepointStudy');
|
const $study = $(event.currentTarget).closest('.studyTimepointStudy');
|
||||||
const $timepoint = $study.closest('.studyTimepoint');
|
const $timepoint = $study.closest('.studyTimepoint');
|
||||||
const study = $study[0];
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -14,6 +14,26 @@ $spacerX = 7px
|
|||||||
$spacerY = 12px
|
$spacerY = 12px
|
||||||
|
|
||||||
.studyTimepointStudy
|
.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
|
&.active
|
||||||
.studyModality
|
.studyModality
|
||||||
|
|||||||
@ -151,16 +151,14 @@ Package.onUse(function(api) {
|
|||||||
api.addFiles('client/components/nonTargetLesionDialog/nonTargetLesionDialog.styl', 'client');
|
api.addFiles('client/components/nonTargetLesionDialog/nonTargetLesionDialog.styl', 'client');
|
||||||
api.addFiles('client/components/nonTargetLesionDialog/nonTargetLesionDialog.js', '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.html', 'client');
|
||||||
api.addFiles('client/components/studyTimepointBrowser/studyTimepoint.styl', 'client');
|
api.addFiles('client/components/studyTimepointBrowser/studyTimepoint.styl', 'client');
|
||||||
api.addFiles('client/components/studyTimepointBrowser/studyTimepoint.js', 'client');
|
api.addFiles('client/components/studyTimepointBrowser/studyTimepoint.js', 'client');
|
||||||
|
|
||||||
api.addFiles('client/components/studyTimepointBrowser/studyTimepointBrowser.html', 'client');
|
api.addFiles('client/components/studyTimepointBrowser/studyTimepointBrowser.html', 'client');
|
||||||
api.addFiles('client/components/studyTimepointBrowser/studyTimepointBrowser.styl', 'client');
|
api.addFiles('client/components/studyTimepointBrowser/studyTimepointBrowser.styl', 'client');
|
||||||
api.addFiles('client/components/studyTimepointBrowser/studyTimepointBrowser.js', 'client');
|
api.addFiles('client/components/studyTimepointBrowser/studyTimepointBrowser.js', 'client');
|
||||||
|
|
||||||
api.addFiles('client/components/studyTimepointBrowser/studyTimepointStudy.html', 'client');
|
api.addFiles('client/components/studyTimepointBrowser/studyTimepointStudy.html', 'client');
|
||||||
api.addFiles('client/components/studyTimepointBrowser/studyTimepointStudy.styl', 'client');
|
api.addFiles('client/components/studyTimepointBrowser/studyTimepointStudy.styl', 'client');
|
||||||
api.addFiles('client/components/studyTimepointBrowser/studyTimepointStudy.js', 'client');
|
api.addFiles('client/components/studyTimepointBrowser/studyTimepointStudy.js', 'client');
|
||||||
|
|||||||
@ -9,7 +9,7 @@ $inputPlaceholderColor = lightgray
|
|||||||
|
|
||||||
$studyListToolbarHeight = 75px
|
$studyListToolbarHeight = 75px
|
||||||
$theadBackgroundColor = #151a1f
|
$theadBackgroundColor = #151a1f
|
||||||
$tablePadding = 60px
|
$tablePadding = 8%
|
||||||
$bodyCellHeight = 40px
|
$bodyCellHeight = 40px
|
||||||
|
|
||||||
.studyListToolbar
|
.studyListToolbar
|
||||||
@ -47,6 +47,7 @@ $bodyCellHeight = 40px
|
|||||||
z-index: 1
|
z-index: 1
|
||||||
|
|
||||||
#studyListContainer
|
#studyListContainer
|
||||||
|
width: 100%
|
||||||
padding: 0 $tablePadding
|
padding: 0 $tablePadding
|
||||||
position: absolute
|
position: absolute
|
||||||
z-index: 2
|
z-index: 2
|
||||||
|
|||||||
@ -49,6 +49,17 @@ getStudyMetadata = function(studyInstanceUid, doneCallback, failCallback) {
|
|||||||
// and instance number in ascending order
|
// and instance number in ascending order
|
||||||
sortStudy(study);
|
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
|
// Then we store this data in the cache variable
|
||||||
StudyMetaData[studyInstanceUid] = study;
|
StudyMetaData[studyInstanceUid] = study;
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user