Improvements to study list selection, study/timepoint association UI (LT-116 and LT-60)
This commit is contained in:
parent
41b75de814
commit
cb8672576e
@ -4,6 +4,8 @@
|
||||
<div class="col-md-12">
|
||||
<h4>Instructions</h4>
|
||||
<p>Associate the selected studies with timepoints in the clinical trial.</p>
|
||||
<p>We have automatically retrieved all studies within 14 days (<strong>{{formatDA earliestDate}}</strong> to <strong>{{formatDA latestDate}}</strong>) of your selected studies, in case you forgot to
|
||||
select a study.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -12,6 +14,7 @@
|
||||
<tr>
|
||||
<th class="center">Include Study?</th>
|
||||
<th class="center">Study Date</th>
|
||||
<th class="center">Patient Name</th>
|
||||
<th class="center">Study Description</th>
|
||||
<th class="center">Timepoint Type</th>
|
||||
</tr>
|
||||
@ -21,25 +24,34 @@
|
||||
{{ #each relevantStudies }}
|
||||
<tr>
|
||||
<td class="center">
|
||||
<input type="checkbox" class="includeStudy" checked/>
|
||||
<input type="checkbox" class="includeStudy" {{inlineIf autoselected false 'checked'}}/>
|
||||
</td>
|
||||
<td class="center">
|
||||
<td class="center studyDataCell {{ #if autoselected }}disabled{{ /if }}">
|
||||
{{ #if autoselected}}
|
||||
<p class="studyDate autoselected"
|
||||
title="This study was automatically added to your list due to its
|
||||
similarity with your other selected studies">
|
||||
data-toggle="tooltip"
|
||||
title="This study was autoselected.">
|
||||
{{formatDA studyDate}}
|
||||
</p>
|
||||
{{ else }}
|
||||
<p class="studyDate">{{formatDA studyDate}}</p>
|
||||
{{ /if }}
|
||||
</td>
|
||||
<td>
|
||||
<td class="center studyDataCell {{ #if autoselected }}disabled{{ /if }}">
|
||||
<p>{{formatPN patientName}}</p>
|
||||
</td>
|
||||
<td class="studyDataCell {{ #if autoselected }}disabled{{ /if }}">
|
||||
<p>{{studyDescription}}</p>
|
||||
</td>
|
||||
<td class="timepointOptions center">
|
||||
<td class="timepointOptions center studyDataCell {{ #if autoselected }}disabled{{ /if }}">
|
||||
{{ #each timepointOptions }}
|
||||
<label><input type="radio" name="{{_id}}" value={{type}}> {{name}}</label>
|
||||
<label>
|
||||
<input type="radio"
|
||||
name="{{../_id}}"
|
||||
value={{type}}
|
||||
{{ inlineIf autoselected true 'disabled'}}>
|
||||
{{name}}
|
||||
</label>
|
||||
{{ /each }}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@ -1,5 +1,72 @@
|
||||
function autoSelectStudies() {
|
||||
return [];
|
||||
/**
|
||||
* Finds related studies within defined time window of =/- 14 days of selected studies
|
||||
* @param selectedStudies
|
||||
* @param range Object
|
||||
*/
|
||||
function getDateRange(selectedStudies, range) {
|
||||
if (range === undefined) {
|
||||
range = {
|
||||
days: 14
|
||||
};
|
||||
}
|
||||
|
||||
if (!selectedStudies.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
var earliestStudy = selectedStudies[0];
|
||||
var latestStudy = selectedStudies[selectedStudies.length - 1];
|
||||
|
||||
var earliestDate = moment(earliestStudy.studyDate, 'YYYYMMDD');
|
||||
earliestDate.subtract(range);
|
||||
|
||||
var latestDate = moment(latestStudy.studyDate, 'YYYYMMDD');
|
||||
latestDate.add(range);
|
||||
|
||||
return {
|
||||
earliestDate: earliestDate,
|
||||
latestDate: latestDate
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {Array}
|
||||
*/
|
||||
function autoSelectStudies(selectedStudies) {
|
||||
if (!selectedStudies.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
var range = getDateRange(selectedStudies);
|
||||
|
||||
var autoselected = WorklistStudies.find({
|
||||
studyDate: {
|
||||
$gte: range.earliestDate.format('YYYYMMDD'),
|
||||
$lte: range.latestDate.format('YYYYMMDD')
|
||||
}
|
||||
}, {
|
||||
sort: {
|
||||
studyDate: 1
|
||||
}
|
||||
}).fetch();
|
||||
|
||||
// Make an array of studyInstanceUids in selectedStudies
|
||||
var studyInstanceUids = selectedStudies.map(function(selectedStudy) {
|
||||
return selectedStudy.studyInstanceUid;
|
||||
});
|
||||
|
||||
autoselected.forEach(function(study) {
|
||||
var exists = studyInstanceUids.indexOf(study.studyInstanceUid);
|
||||
if (exists > -1) {
|
||||
study.autoselected = false;
|
||||
return;
|
||||
}
|
||||
|
||||
study.autoselected = true;
|
||||
});
|
||||
|
||||
return autoselected;
|
||||
}
|
||||
|
||||
Template.studyAssociationTable.helpers({
|
||||
@ -10,9 +77,13 @@ Template.studyAssociationTable.helpers({
|
||||
* @returns {Array.<T>}
|
||||
*/
|
||||
relevantStudies: function() {
|
||||
var userSelectedStudies = WorklistSelectedStudies.find().fetch() || [];
|
||||
var autoselected = autoSelectStudies(userSelectedStudies);
|
||||
return userSelectedStudies.concat(autoselected);
|
||||
var selectedStudies = WorklistSelectedStudies.find({}, {
|
||||
sort: {
|
||||
studyDate: 1
|
||||
}
|
||||
}).fetch() || [];
|
||||
var autoselected = autoSelectStudies(selectedStudies);
|
||||
return autoselected;
|
||||
},
|
||||
/**
|
||||
* This helper returns the list of Timepoint types the user can set for this study
|
||||
@ -30,7 +101,54 @@ Template.studyAssociationTable.helpers({
|
||||
name: 'Follow-up'
|
||||
}
|
||||
];
|
||||
},
|
||||
earliestDate: function() {
|
||||
var selectedStudies = WorklistSelectedStudies.find({}, {
|
||||
sort: {
|
||||
studyDate: 1
|
||||
}
|
||||
}).fetch();
|
||||
var range = getDateRange(selectedStudies);
|
||||
if (!range) {
|
||||
return;
|
||||
}
|
||||
|
||||
return range.earliestDate;
|
||||
},
|
||||
latestDate: function() {
|
||||
var selectedStudies = WorklistSelectedStudies.find({}, {
|
||||
sort: {
|
||||
studyDate: 1
|
||||
}
|
||||
}).fetch();
|
||||
var range = getDateRange(selectedStudies);
|
||||
if (!range) {
|
||||
return;
|
||||
}
|
||||
|
||||
return range.latestDate;
|
||||
}
|
||||
});
|
||||
|
||||
//trial criteria!
|
||||
Template.studyAssociationTable.events({
|
||||
'change input.includeStudy': function(e) {
|
||||
var checkbox = e.currentTarget;
|
||||
var studyDataCells = $(checkbox).parents('tr').find('td.studyDataCell');
|
||||
if (checkbox.checked === true) {
|
||||
studyDataCells.removeClass('disabled');
|
||||
studyDataCells.find('input').attr('disabled', false);
|
||||
} else {
|
||||
studyDataCells.addClass('disabled');
|
||||
studyDataCells.find('input').attr('disabled', true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//trial criteria!
|
||||
/*There shall be Associate option in right-click dialog
|
||||
If associated, double-click shall go to image view.
|
||||
If not associated, user shall be directed to Associate Time Points Dialog
|
||||
Use shall also be allowed to select multiple studies from study list to associate
|
||||
Associate Time Point dialog shall present selected studies and studies within defined time window of =/- 14 days of selected studies
|
||||
User should only be able to associate one time point at a time (user should not be able to select both BL and F/U for different studies in associate dialog)
|
||||
*/
|
||||
@ -13,4 +13,7 @@
|
||||
margin: 0 3px
|
||||
|
||||
label
|
||||
padding: 0 5px
|
||||
padding: 0 5px
|
||||
|
||||
td.disabled
|
||||
opacity: 0.2
|
||||
10
Packages/viewerbase/lib/helpers/inlineIf.js
Normal file
10
Packages/viewerbase/lib/helpers/inlineIf.js
Normal file
@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Helper for setting checkboxes as checked or unchecked inside templates,
|
||||
* based on another variable's value
|
||||
*/
|
||||
UI.registerHelper("inlineIf", function (value, match, attributeName) {
|
||||
if(value === match) {
|
||||
return attributeName;
|
||||
}
|
||||
return '';
|
||||
});
|
||||
@ -155,6 +155,7 @@ Package.onUse(function (api) {
|
||||
api.addFiles('lib/helpers/formatNumberPrecision.js', 'client');
|
||||
api.addFiles('lib/helpers/formatPN.js', 'client');
|
||||
api.addFiles('lib/helpers/formatTM.js', 'client');
|
||||
api.addFiles('lib/helpers/inlineIf.js', 'client');
|
||||
|
||||
// Server-side functions
|
||||
api.addFiles('server/seed.js', 'server');
|
||||
|
||||
@ -20,8 +20,10 @@
|
||||
</span> Anonymize
|
||||
</a>
|
||||
<a><i class="fa fa-trash fa-lg"></i> Delete</a>
|
||||
<a><i class="fa fa-share fa-lg"></i> Share</a>
|
||||
<a><i class="fa fa-send-o fa-lg"></i> Send</a>
|
||||
<a><i class="fa fa-exchange fa-lg"></i> Export</a>
|
||||
<a><i class="fa fa-download fa-lg"></i> Download</a>
|
||||
<a><i class="fa fa-photo fa-lg"></i> View Series Details</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@ -36,11 +36,11 @@ openStudyContextMenu = function(e, template) {
|
||||
dialogProperty.top = e.pageY;// - dialog.outerHeight() - 40;
|
||||
dialogProperty.left = e.pageX;// - dialog.outerWidth() / 2;
|
||||
|
||||
var pageHeight = $(window).height();
|
||||
var pageHeight = $(document.body).height();
|
||||
dialogProperty.top = Math.max(dialogProperty.top, 0);
|
||||
dialogProperty.top = Math.min(dialogProperty.top, pageHeight - dialog.outerHeight());
|
||||
|
||||
var pageWidth = $(window).width();
|
||||
var pageWidth = $(document.body).width();
|
||||
dialogProperty.left = Math.max(dialogProperty.left, 0);
|
||||
dialogProperty.left = Math.min(dialogProperty.left, pageWidth - dialog.outerWidth());
|
||||
}
|
||||
|
||||
@ -33,21 +33,28 @@ function handleShiftClick(studyRow, data) {
|
||||
}
|
||||
|
||||
// Loop through the rows in between current and previous selected studies
|
||||
rowsInBetween.each(function(index, row) {
|
||||
if ($(row).is(studyRow)) {
|
||||
// When we reach the currently clicked-on row, stop
|
||||
return false;
|
||||
} else if ($(row).hasClass('active')) {
|
||||
rowsInBetween.each(function() {
|
||||
var row = $(this);
|
||||
|
||||
if (row.hasClass('active')) {
|
||||
// If we find one that is already selected, do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the relevant studyInstanceUid
|
||||
var studyInstanceUid = $(row).attr('studyInstanceUid');
|
||||
var studyInstanceUid = row.attr('studyInstanceUid');
|
||||
|
||||
// Retrieve the data context through Blaze
|
||||
var data = Blaze.getData(this);
|
||||
|
||||
// Set the current study as selected
|
||||
WorklistSelectedStudies.insert(data);
|
||||
studyRow.addClass('active');
|
||||
row.addClass('active');
|
||||
|
||||
// When we reach the currently clicked-on row, stop the loop
|
||||
if (row.is(studyRow)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
@ -77,6 +84,7 @@ function handleCtrlClick(studyRow, data) {
|
||||
// Set this as the previously selected row, so the user can
|
||||
// use Shift to select from this point onwards
|
||||
Worklist.previouslySelected = studyRow;
|
||||
log.info('Worklist PreviouslySelected set: ' + studyRow.index());
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,6 +110,7 @@ Template.worklistStudy.events({
|
||||
|
||||
// Set the previous study to the currently clicked-on study
|
||||
Worklist.previouslySelected = studyRow;
|
||||
log.info('Worklist PreviouslySelected set: ' + studyRow.index());
|
||||
|
||||
// Set the current study as selected
|
||||
WorklistSelectedStudies.insert(data);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user