Improvements to study list selection, study/timepoint association UI (LT-116 and LT-60)

This commit is contained in:
Erik Ziegler 2016-01-13 11:50:07 +01:00
parent 41b75de814
commit cb8672576e
8 changed files with 179 additions and 24 deletions

View File

@ -4,6 +4,8 @@
<div class="col-md-12"> <div class="col-md-12">
<h4>Instructions</h4> <h4>Instructions</h4>
<p>Associate the selected studies with timepoints in the clinical trial.</p> <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>
</div> </div>
@ -12,6 +14,7 @@
<tr> <tr>
<th class="center">Include Study?</th> <th class="center">Include Study?</th>
<th class="center">Study Date</th> <th class="center">Study Date</th>
<th class="center">Patient Name</th>
<th class="center">Study Description</th> <th class="center">Study Description</th>
<th class="center">Timepoint Type</th> <th class="center">Timepoint Type</th>
</tr> </tr>
@ -21,25 +24,34 @@
{{ #each relevantStudies }} {{ #each relevantStudies }}
<tr> <tr>
<td class="center"> <td class="center">
<input type="checkbox" class="includeStudy" checked/> <input type="checkbox" class="includeStudy" {{inlineIf autoselected false 'checked'}}/>
</td> </td>
<td class="center"> <td class="center studyDataCell {{ #if autoselected }}disabled{{ /if }}">
{{ #if autoselected}} {{ #if autoselected}}
<p class="studyDate autoselected" <p class="studyDate autoselected"
title="This study was automatically added to your list due to its data-toggle="tooltip"
similarity with your other selected studies"> title="This study was autoselected.">
{{formatDA studyDate}} {{formatDA studyDate}}
</p> </p>
{{ else }} {{ else }}
<p class="studyDate">{{formatDA studyDate}}</p> <p class="studyDate">{{formatDA studyDate}}</p>
{{ /if }} {{ /if }}
</td> </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> <p>{{studyDescription}}</p>
</td> </td>
<td class="timepointOptions center"> <td class="timepointOptions center studyDataCell {{ #if autoselected }}disabled{{ /if }}">
{{ #each timepointOptions }} {{ #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 }} {{ /each }}
</td> </td>
</tr> </tr>

View File

@ -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({ Template.studyAssociationTable.helpers({
@ -10,9 +77,13 @@ Template.studyAssociationTable.helpers({
* @returns {Array.<T>} * @returns {Array.<T>}
*/ */
relevantStudies: function() { relevantStudies: function() {
var userSelectedStudies = WorklistSelectedStudies.find().fetch() || []; var selectedStudies = WorklistSelectedStudies.find({}, {
var autoselected = autoSelectStudies(userSelectedStudies); sort: {
return userSelectedStudies.concat(autoselected); studyDate: 1
}
}).fetch() || [];
var autoselected = autoSelectStudies(selectedStudies);
return autoselected;
}, },
/** /**
* This helper returns the list of Timepoint types the user can set for this study * 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' 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)
*/

View File

@ -13,4 +13,7 @@
margin: 0 3px margin: 0 3px
label label
padding: 0 5px padding: 0 5px
td.disabled
opacity: 0.2

View 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 '';
});

View File

@ -155,6 +155,7 @@ Package.onUse(function (api) {
api.addFiles('lib/helpers/formatNumberPrecision.js', 'client'); api.addFiles('lib/helpers/formatNumberPrecision.js', 'client');
api.addFiles('lib/helpers/formatPN.js', 'client'); api.addFiles('lib/helpers/formatPN.js', 'client');
api.addFiles('lib/helpers/formatTM.js', 'client'); api.addFiles('lib/helpers/formatTM.js', 'client');
api.addFiles('lib/helpers/inlineIf.js', 'client');
// Server-side functions // Server-side functions
api.addFiles('server/seed.js', 'server'); api.addFiles('server/seed.js', 'server');

View File

@ -20,8 +20,10 @@
</span> Anonymize </span> Anonymize
</a> </a>
<a><i class="fa fa-trash fa-lg"></i> Delete</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-download fa-lg"></i> Download</a>
<a><i class="fa fa-photo fa-lg"></i> View Series Details</a>
</li> </li>
</ul> </ul>
</div> </div>

View File

@ -36,11 +36,11 @@ openStudyContextMenu = function(e, template) {
dialogProperty.top = e.pageY;// - dialog.outerHeight() - 40; dialogProperty.top = e.pageY;// - dialog.outerHeight() - 40;
dialogProperty.left = e.pageX;// - dialog.outerWidth() / 2; 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.max(dialogProperty.top, 0);
dialogProperty.top = Math.min(dialogProperty.top, pageHeight - dialog.outerHeight()); 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.max(dialogProperty.left, 0);
dialogProperty.left = Math.min(dialogProperty.left, pageWidth - dialog.outerWidth()); dialogProperty.left = Math.min(dialogProperty.left, pageWidth - dialog.outerWidth());
} }

View File

@ -33,21 +33,28 @@ function handleShiftClick(studyRow, data) {
} }
// Loop through the rows in between current and previous selected studies // Loop through the rows in between current and previous selected studies
rowsInBetween.each(function(index, row) { rowsInBetween.each(function() {
if ($(row).is(studyRow)) { var row = $(this);
// When we reach the currently clicked-on row, stop
return false; if (row.hasClass('active')) {
} else if ($(row).hasClass('active')) {
// If we find one that is already selected, do nothing // If we find one that is already selected, do nothing
return; return;
} }
// Get the relevant studyInstanceUid // 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 // Set the current study as selected
WorklistSelectedStudies.insert(data); 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; return true;
}); });
@ -77,6 +84,7 @@ function handleCtrlClick(studyRow, data) {
// Set this as the previously selected row, so the user can // Set this as the previously selected row, so the user can
// use Shift to select from this point onwards // use Shift to select from this point onwards
Worklist.previouslySelected = studyRow; 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 // Set the previous study to the currently clicked-on study
Worklist.previouslySelected = studyRow; Worklist.previouslySelected = studyRow;
log.info('Worklist PreviouslySelected set: ' + studyRow.index());
// Set the current study as selected // Set the current study as selected
WorklistSelectedStudies.insert(data); WorklistSelectedStudies.insert(data);