PWV-1: Refactoring ohif:studylist package
This commit is contained in:
parent
984ef94403
commit
4d79bf84a8
@ -35,7 +35,6 @@
|
|||||||
{{ >serverInformationModal }}
|
{{ >serverInformationModal }}
|
||||||
{{ >confirmRemoveTimepointAssociation }}
|
{{ >confirmRemoveTimepointAssociation }}
|
||||||
{{ >lastLoginModal }}
|
{{ >lastLoginModal }}
|
||||||
{{ >studyContextMenu }}
|
|
||||||
{{ >progressDialog }}
|
{{ >progressDialog }}
|
||||||
{{ >viewSeriesDetailsModal }}
|
{{ >viewSeriesDetailsModal }}
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -33,7 +33,6 @@
|
|||||||
</div>
|
</div>
|
||||||
{{ >serverInformationModal }}
|
{{ >serverInformationModal }}
|
||||||
{{ >lastLoginModal }}
|
{{ >lastLoginModal }}
|
||||||
{{ >studyContextMenu }}
|
|
||||||
{{ >progressDialog }}
|
{{ >progressDialog }}
|
||||||
{{ >viewSeriesDetailsModal }}
|
{{ >viewSeriesDetailsModal }}
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -12,7 +12,7 @@ OHIF.mixins.button = new OHIF.Mixin({
|
|||||||
const component = instance.component;
|
const component = instance.component;
|
||||||
|
|
||||||
// Set the element to be controlled
|
// Set the element to be controlled
|
||||||
component.$element = instance.$('button:first');
|
component.$element = instance.$('button').first();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -54,7 +54,7 @@ OHIF.mixins.form = new OHIF.Mixin({
|
|||||||
const component = instance.component;
|
const component = instance.component;
|
||||||
|
|
||||||
// Set the component main and style elements
|
// Set the component main and style elements
|
||||||
component.$style = component.$element = instance.$('form:first');
|
component.$style = component.$element = instance.$('form').first();
|
||||||
|
|
||||||
// Block page redirecting on submit
|
// Block page redirecting on submit
|
||||||
component.$element[0].onsubmit = () => false;
|
component.$element[0].onsubmit = () => false;
|
||||||
@ -66,7 +66,7 @@ OHIF.mixins.form = new OHIF.Mixin({
|
|||||||
const targetKey = $(event.currentTarget).attr('data-target');
|
const targetKey = $(event.currentTarget).attr('data-target');
|
||||||
|
|
||||||
// Focus the first input inside the element with error state
|
// Focus the first input inside the element with error state
|
||||||
instance.$(`.state-error[data-key="${targetKey}"] :input:first`).focus();
|
instance.$(`.state-error[data-key="${targetKey}"]`).find(':input:first').focus();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@ -147,7 +147,7 @@ OHIF.mixins.group = new OHIF.Mixin({
|
|||||||
const component = instance.component;
|
const component = instance.component;
|
||||||
|
|
||||||
// Set the element to be controlled
|
// Set the element to be controlled
|
||||||
component.$element = instance.$('.component-group:first');
|
component.$element = instance.$('.component-group').first();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -12,7 +12,7 @@ OHIF.mixins.input = new OHIF.Mixin({
|
|||||||
const component = instance.component;
|
const component = instance.component;
|
||||||
|
|
||||||
// Set the element to be controlled
|
// Set the element to be controlled
|
||||||
component.$element = instance.$('input:first');
|
component.$element = instance.$('input').first();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -12,7 +12,7 @@ OHIF.mixins.link = new OHIF.Mixin({
|
|||||||
const component = instance.component;
|
const component = instance.component;
|
||||||
|
|
||||||
// Set the element to be controlled
|
// Set the element to be controlled
|
||||||
component.$element = instance.$('a:first');
|
component.$element = instance.$('a').first();
|
||||||
},
|
},
|
||||||
|
|
||||||
events: {
|
events: {
|
||||||
|
|||||||
@ -12,7 +12,7 @@ OHIF.mixins.select = new OHIF.Mixin({
|
|||||||
const component = instance.component;
|
const component = instance.component;
|
||||||
|
|
||||||
// Set the element to be controlled
|
// Set the element to be controlled
|
||||||
component.$element = instance.$('select:first');
|
component.$element = instance.$('select').first();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -0,0 +1,3 @@
|
|||||||
|
<template name="dropdownBackdrop">
|
||||||
|
<div class="dropdown-backdrop"></div>
|
||||||
|
</template>
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
<template name="dropdownForm">
|
||||||
|
{{#form class='dropdown' api=this.api hideValidationBox=true}}
|
||||||
|
{{>dropdownBackdrop}}
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
{{>UI.contentBlock}}
|
||||||
|
</ul>
|
||||||
|
{{/form}}
|
||||||
|
</template>
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
import { Template } from 'meteor/templating';
|
||||||
|
import { Blaze } from 'meteor/blaze';
|
||||||
|
import { _ } from 'meteor/underscore';
|
||||||
|
|
||||||
|
Template.dropdownForm.onRendered(() => {
|
||||||
|
const instance = Template.instance();
|
||||||
|
|
||||||
|
// Show the dropdown
|
||||||
|
instance.$('.dropdown').addClass('open');
|
||||||
|
|
||||||
|
// Destroy the Blaze created view (either created with template calls or with renderWithData)
|
||||||
|
instance.destroyView = () => {
|
||||||
|
if (_.isFunction(instance.data.destroyView)) {
|
||||||
|
instance.data.destroyView();
|
||||||
|
} else {
|
||||||
|
Blaze.remove(instance.view);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.dropdownForm.events({
|
||||||
|
'click .dropdown'(event, instance) {
|
||||||
|
instance.destroyView();
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -3,6 +3,10 @@ import './dialog/form.js';
|
|||||||
import './dialog/login.html';
|
import './dialog/login.html';
|
||||||
import './dialog/login.js';
|
import './dialog/login.js';
|
||||||
|
|
||||||
|
import './dropdown/backdrop.html';
|
||||||
|
import './dropdown/form.html';
|
||||||
|
import './dropdown/form.js';
|
||||||
|
|
||||||
import './form/button.html';
|
import './form/button.html';
|
||||||
import './form/form.html';
|
import './form/form.html';
|
||||||
import './form/group.html';
|
import './form/group.html';
|
||||||
|
|||||||
@ -36,10 +36,15 @@ class Bounded {
|
|||||||
options(options={}) {
|
options(options={}) {
|
||||||
// Process the given options and store it in the instance
|
// Process the given options and store it in the instance
|
||||||
const { boundingElement, allowResizing } = options;
|
const { boundingElement, allowResizing } = options;
|
||||||
this.boundingElement = boundingElement || window;
|
this.boundingElement = boundingElement;
|
||||||
this.$boundingElement = $(this.boundingElement);
|
this.$boundingElement = $(this.boundingElement);
|
||||||
this.allowResizing = allowResizing;
|
this.allowResizing = allowResizing;
|
||||||
|
|
||||||
|
// Check for fixed positioning
|
||||||
|
if (this.$element.css('position') === 'fixed') {
|
||||||
|
this.boundingElement = window;
|
||||||
|
}
|
||||||
|
|
||||||
// Destroy and initialize again the instance
|
// Destroy and initialize again the instance
|
||||||
this.destroy();
|
this.destroy();
|
||||||
this.init();
|
this.init();
|
||||||
@ -55,6 +60,9 @@ class Bounded {
|
|||||||
|
|
||||||
// Add the bounded class to the element
|
// Add the bounded class to the element
|
||||||
this.$element.addClass('bounded');
|
this.$element.addClass('bounded');
|
||||||
|
|
||||||
|
// Trigger the bounding check for the first timepoint
|
||||||
|
setTimeout(() => this.$element.trigger('spatialChanged'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destroy this instance, returning the element to its previous state
|
// Destroy this instance, returning the element to its previous state
|
||||||
@ -70,12 +78,26 @@ class Bounded {
|
|||||||
// Create the result object
|
// Create the result object
|
||||||
const result = {};
|
const result = {};
|
||||||
|
|
||||||
|
// Check if the element is the window
|
||||||
|
if (element === window) {
|
||||||
|
const $window = $(window);
|
||||||
|
const width = $window.outerWidth();
|
||||||
|
const height = $window.outerHeight();
|
||||||
|
return {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
x0: 0,
|
||||||
|
y0: 0,
|
||||||
|
x1: width,
|
||||||
|
y1: height
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Get the jQuery object for the element
|
// Get the jQuery object for the element
|
||||||
const el = element === window ? window.document.body : element;
|
const $element = $(element);
|
||||||
const $element = $(el);
|
|
||||||
|
|
||||||
// Get the element style
|
// Get the element style
|
||||||
const style = el.style;
|
const style = element.style;
|
||||||
|
|
||||||
// Get the integer numbers for element's width
|
// Get the integer numbers for element's width
|
||||||
if (style.width && style.width.indexOf('px') > -1) {
|
if (style.width && style.width.indexOf('px') > -1) {
|
||||||
@ -91,10 +113,13 @@ class Bounded {
|
|||||||
result.height = $element.outerHeight();
|
result.height = $element.outerHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the position property based on the element position CSS attribute
|
||||||
|
const positionProperty = $element.css('position') === 'fixed' ? 'position' : 'offset';
|
||||||
|
|
||||||
// Get the element's start position
|
// Get the element's start position
|
||||||
const offset = $element.offset();
|
const position = $element[positionProperty]();
|
||||||
result.x0 = offset.left;
|
result.x0 = position.left;
|
||||||
result.y0 = offset.top;
|
result.y0 = position.top;
|
||||||
|
|
||||||
// Get the element's end position
|
// Get the element's end position
|
||||||
result.x1 = result.x0 + result.width;
|
result.x1 = result.x0 + result.width;
|
||||||
|
|||||||
29
Packages/ohif-core/client/ui/dropdown/form.js
Normal file
29
Packages/ohif-core/client/ui/dropdown/form.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { Template } from 'meteor/templating';
|
||||||
|
import { Blaze } from 'meteor/blaze';
|
||||||
|
import { _ } from 'meteor/underscore';
|
||||||
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
|
|
||||||
|
OHIF.ui.showFormDropdown = (templateName, dropdownData) => {
|
||||||
|
// Check if the given template exists
|
||||||
|
const template = Template[templateName];
|
||||||
|
if (!template) {
|
||||||
|
throw {
|
||||||
|
name: 'TEMPLATE_NOT_FOUND',
|
||||||
|
message: `Template ${templateName} not found.`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare the method to destroy the view
|
||||||
|
let view;
|
||||||
|
const destroyView = () => Blaze.remove(view);
|
||||||
|
const templateData = _.extend({}, dropdownData, {
|
||||||
|
destroyView
|
||||||
|
});
|
||||||
|
|
||||||
|
// Render the dialog with the given template and data
|
||||||
|
view = Blaze.renderWithData(template, templateData, document.body);
|
||||||
|
|
||||||
|
// Return the dropdown element to enable position manipulation
|
||||||
|
const dropdown = view.firstNode();
|
||||||
|
return dropdown;
|
||||||
|
};
|
||||||
@ -1,4 +1,5 @@
|
|||||||
import './bounded/bounded.js';
|
import './bounded/bounded.js';
|
||||||
import './dialog/form.js';
|
import './dialog/form.js';
|
||||||
import './draggable/draggable.js';
|
import './draggable/draggable.js';
|
||||||
|
import './dropdown/form.js';
|
||||||
import './resizable/resizable.js';
|
import './resizable/resizable.js';
|
||||||
|
|||||||
@ -7,8 +7,7 @@ import { Meteor } from 'meteor/meteor';
|
|||||||
const OHIF = {
|
const OHIF = {
|
||||||
log: {},
|
log: {},
|
||||||
ui: {},
|
ui: {},
|
||||||
viewer: {},
|
viewer: {}
|
||||||
measurements: {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Expose the OHIF object to the client if it is on development mode
|
// Expose the OHIF object to the client if it is on development mode
|
||||||
|
|||||||
3
Packages/ohif-measurements/both/base.js
Normal file
3
Packages/ohif-measurements/both/base.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
|
|
||||||
|
OHIF.measurements = {};
|
||||||
@ -1,2 +1,3 @@
|
|||||||
|
import './base.js';
|
||||||
import './configuration';
|
import './configuration';
|
||||||
import './schema';
|
import './schema';
|
||||||
|
|||||||
@ -1,3 +1,7 @@
|
|||||||
|
import { Template } from 'meteor/templating';
|
||||||
|
import { moment } from 'meteor/momentjs:moment';
|
||||||
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds related studies within defined time window of =/- 14 days of selected studies
|
* Finds related studies within defined time window of =/- 14 days of selected studies
|
||||||
* @param selectedStudies
|
* @param selectedStudies
|
||||||
@ -14,13 +18,13 @@ function getDateRange(selectedStudies, range) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var earliestStudy = selectedStudies[0];
|
const earliestStudy = selectedStudies[0];
|
||||||
var latestStudy = selectedStudies[selectedStudies.length - 1];
|
const latestStudy = selectedStudies[selectedStudies.length - 1];
|
||||||
|
|
||||||
var earliestDate = moment(earliestStudy.studyDate, 'YYYYMMDD');
|
const earliestDate = moment(earliestStudy.studyDate, 'YYYYMMDD');
|
||||||
earliestDate.subtract(range);
|
earliestDate.subtract(range);
|
||||||
|
|
||||||
var latestDate = moment(latestStudy.studyDate, 'YYYYMMDD');
|
const latestDate = moment(latestStudy.studyDate, 'YYYYMMDD');
|
||||||
latestDate.add(range);
|
latestDate.add(range);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -43,12 +47,12 @@ function autoSelectStudies(selectedStudies) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var range = getDateRange(selectedStudies);
|
const range = getDateRange(selectedStudies);
|
||||||
|
|
||||||
// Fetch autoselected studies based on the date range
|
// Fetch autoselected studies based on the date range
|
||||||
// Note that we used MongoDB's fetch here so we have a mutable array,
|
// Note that we used MongoDB's fetch here so we have a mutable array,
|
||||||
// rather than a Cursor
|
// rather than a Cursor
|
||||||
var autoselected = StudyListStudies.find({
|
const autoselected = StudyListStudies.find({
|
||||||
studyDate: {
|
studyDate: {
|
||||||
$gte: range.earliestDate.format('YYYYMMDD'),
|
$gte: range.earliestDate.format('YYYYMMDD'),
|
||||||
$lte: range.latestDate.format('YYYYMMDD')
|
$lte: range.latestDate.format('YYYYMMDD')
|
||||||
@ -60,12 +64,10 @@ function autoSelectStudies(selectedStudies) {
|
|||||||
}).fetch();
|
}).fetch();
|
||||||
|
|
||||||
// Make an array of studyInstanceUids in selectedStudies
|
// Make an array of studyInstanceUids in selectedStudies
|
||||||
var studyInstanceUids = selectedStudies.map(function(selectedStudy) {
|
const studyInstanceUids = selectedStudies.map(selectedStudy => selectedStudy.studyInstanceUid);
|
||||||
return selectedStudy.studyInstanceUid;
|
|
||||||
});
|
|
||||||
|
|
||||||
autoselected.forEach(function(study) {
|
autoselected.forEach(study => {
|
||||||
var exists = studyInstanceUids.indexOf(study.studyInstanceUid);
|
const exists = studyInstanceUids.indexOf(study.studyInstanceUid);
|
||||||
if (exists > -1) {
|
if (exists > -1) {
|
||||||
study.autoselected = false;
|
study.autoselected = false;
|
||||||
return;
|
return;
|
||||||
@ -84,12 +86,8 @@ Template.studyAssociationTable.helpers({
|
|||||||
*
|
*
|
||||||
* @returns {Array.<T>}
|
* @returns {Array.<T>}
|
||||||
*/
|
*/
|
||||||
relevantStudies: function() {
|
relevantStudies() {
|
||||||
var selectedStudies = StudyListSelectedStudies.find({}, {
|
const selectedStudies = OHIF.studylist.getSelectedStudies();
|
||||||
sort: {
|
|
||||||
studyDate: 1
|
|
||||||
}
|
|
||||||
}).fetch() || [];
|
|
||||||
|
|
||||||
return autoSelectStudies(selectedStudies);
|
return autoSelectStudies(selectedStudies);
|
||||||
},
|
},
|
||||||
@ -98,7 +96,7 @@ Template.studyAssociationTable.helpers({
|
|||||||
*
|
*
|
||||||
* @returns {Array.<T>}
|
* @returns {Array.<T>}
|
||||||
*/
|
*/
|
||||||
timepointOptions: function() {
|
timepointOptions() {
|
||||||
return [{
|
return [{
|
||||||
value: 'baseline',
|
value: 'baseline',
|
||||||
name: 'Baseline',
|
name: 'Baseline',
|
||||||
@ -109,26 +107,18 @@ Template.studyAssociationTable.helpers({
|
|||||||
checked: false
|
checked: false
|
||||||
}];
|
}];
|
||||||
},
|
},
|
||||||
earliestDate: function() {
|
earliestDate() {
|
||||||
var selectedStudies = StudyListSelectedStudies.find({}, {
|
const selectedStudies = OHIF.studylist.getSelectedStudies();
|
||||||
sort: {
|
|
||||||
studyDate: 1
|
|
||||||
}
|
|
||||||
}).fetch();
|
|
||||||
|
|
||||||
var range = getDateRange(selectedStudies);
|
const range = getDateRange(selectedStudies);
|
||||||
if (range) {
|
if (range) {
|
||||||
return range.earliestDate;
|
return range.earliestDate;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
latestDate: function() {
|
latestDate() {
|
||||||
var selectedStudies = StudyListSelectedStudies.find({}, {
|
const selectedStudies = OHIF.studylist.getSelectedStudies();
|
||||||
sort: {
|
|
||||||
studyDate: 1
|
|
||||||
}
|
|
||||||
}).fetch();
|
|
||||||
|
|
||||||
var range = getDateRange(selectedStudies);
|
const range = getDateRange(selectedStudies);
|
||||||
if (range) {
|
if (range) {
|
||||||
return range.latestDate;
|
return range.latestDate;
|
||||||
}
|
}
|
||||||
@ -136,9 +126,9 @@ Template.studyAssociationTable.helpers({
|
|||||||
});
|
});
|
||||||
|
|
||||||
Template.studyAssociationTable.events({
|
Template.studyAssociationTable.events({
|
||||||
'change input.includeStudy': function(e) {
|
'change input.includeStudy'(event, instance) {
|
||||||
var checkbox = e.currentTarget;
|
const checkbox = event.currentTarget;
|
||||||
var studyDataCells = $(checkbox).parents('tr').find('td.studyDataCell');
|
const studyDataCells = $(checkbox).parents('tr').find('td.studyDataCell');
|
||||||
if (checkbox.checked === true) {
|
if (checkbox.checked === true) {
|
||||||
studyDataCells.removeClass('disabled');
|
studyDataCells.removeClass('disabled');
|
||||||
studyDataCells.find('input').attr('disabled', false);
|
studyDataCells.find('input').attr('disabled', false);
|
||||||
|
|||||||
@ -1,30 +1,20 @@
|
|||||||
<template name="longitudinalStudyListContextMenu">
|
<template name="longitudinalStudyListContextMenu">
|
||||||
{{#if uiSettings.studyListFunctionsEnabled}}
|
{{#dropdownForm}}
|
||||||
<div id="studyContextMenu" class="studyContextMenu noselect"
|
<li><a id="viewStudies" type="button" title="View Studies">View</a></li>
|
||||||
oncontextmenu='return false;'
|
<li class="divider" role="separator"></li>
|
||||||
unselectable='on'
|
|
||||||
onselectstart='return false;'>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<a id="viewStudies" type="button" title="View Studies">View</a>
|
|
||||||
<hr/>
|
|
||||||
<!-- {{#link action='associate'}}Associate{{/link}} -->
|
<!-- {{#link action='associate'}}Associate{{/link}} -->
|
||||||
<a id="launchStudyAssociation" title="Launch Study Association">Associate</a>
|
<li><a id="launchStudyAssociation" title="Launch Study Association">Associate</a></li>
|
||||||
<a id="launchRemoveAssociation" type="button"
|
<li><a id="launchRemoveAssociation" type="button" data-toggle="modal"
|
||||||
data-toggle="modal"
|
data-target="#confirmRemoveTimepointAssociation" title="Remove Timepoint Association"
|
||||||
data-target="#confirmRemoveTimepointAssociation"
|
>Remove Association</a></li>
|
||||||
title="Remove Timepoint Association">Remove Association</a>
|
<li class="divider" role="separator"></li>
|
||||||
<hr/>
|
<li><a id="viewSeriesDetails" type="button"
|
||||||
<a id="viewSeriesDetails" type="button"
|
title="View Series Details"
|
||||||
title="View Series Details">View Series Details</a>
|
>View Series Details</a></li>
|
||||||
<a class="disabled">Anonymize</a>
|
<li class="disabled"><a class="disabled">Anonymize</a></li>
|
||||||
<a class="disabled">Send</a>
|
<li class="disabled"><a class="disabled">Send</a></li>
|
||||||
<hr/>
|
<li class="divider" role="separator"></li>
|
||||||
<a id="exportSelectedStudies"
|
<li><a id="exportSelectedStudies" title="Export Selected Studies">Export</a></li>
|
||||||
title="Export Selected Studies">Export</a>
|
<li class="disabled"><a class="disabled">Delete</a></li>
|
||||||
<a class="disabled">Delete</a>
|
{{/dropdownForm}}
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,3 +1,6 @@
|
|||||||
|
import { Template } from 'meteor/templating';
|
||||||
|
import { Meteor } from 'meteor/meteor';
|
||||||
|
import { Random } from 'meteor/random';
|
||||||
import { OHIF } from 'meteor/ohif:core';
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
|
|
||||||
// Use Aldeed's meteor-template-extension package to replace the
|
// Use Aldeed's meteor-template-extension package to replace the
|
||||||
@ -7,21 +10,17 @@ const defaultTemplate = 'studyContextMenu';
|
|||||||
|
|
||||||
Template.longitudinalStudyListContextMenu.replaces(defaultTemplate);
|
Template.longitudinalStudyListContextMenu.replaces(defaultTemplate);
|
||||||
|
|
||||||
StudyList.functions['launchStudyAssociation'] = () => OHIF.ui.showFormDialog('dialogStudyAssociation');
|
StudyList.functions.launchStudyAssociation = () => OHIF.ui.showFormDialog('dialogStudyAssociation');
|
||||||
StudyList.functions['removeTimepointAssociations'] = removeTimepointAssociations;
|
StudyList.functions.removeTimepointAssociations = removeTimepointAssociations;
|
||||||
StudyList.functions['exportSelectedStudies'] = exportSelectedStudies;
|
StudyList.functions.exportSelectedStudies = exportSelectedStudies;
|
||||||
StudyList.functions['viewStudies'] = viewStudies;
|
StudyList.functions.viewStudies = viewStudies;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes all present study / timepoint associations from the Clinical Trial
|
* Removes all present study / timepoint associations from the Clinical Trial
|
||||||
*/
|
*/
|
||||||
function removeTimepointAssociations() {
|
function removeTimepointAssociations() {
|
||||||
// Get a Cursor pointing to the selected Studies from the StudyList
|
// Get a Cursor pointing to the selected Studies from the StudyList
|
||||||
const selectedStudies = StudyListSelectedStudies.find({}, {
|
const selectedStudies = OHIF.studylist.getSelectedStudies();
|
||||||
sort: {
|
|
||||||
studyDate: 1
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Loop through the Cursor of Selected Studies
|
// Loop through the Cursor of Selected Studies
|
||||||
selectedStudies.forEach(function(selectedStudy) {
|
selectedStudies.forEach(function(selectedStudy) {
|
||||||
@ -31,14 +30,14 @@ function removeTimepointAssociations() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let timepoint = timepointApi.study(data.study.studyInstanceUid)[0];
|
let timepoint = timepointApi.study(selectedStudy.studyInstanceUid)[0];
|
||||||
if (!timepoint) {
|
if (!timepoint) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the index of the current studyInstanceUid in the array
|
// Find the index of the current studyInstanceUid in the array
|
||||||
// of reference studyInstanceUids
|
// of reference studyInstanceUids
|
||||||
var index = timepoint.studyInstanceUids.indexOf(study.studyInstanceUid);
|
const index = timepoint.studyInstanceUids.indexOf(selectedStudy.studyInstanceUid);
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -57,14 +56,14 @@ function removeTimepointAssociations() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Log
|
// Log
|
||||||
var hipaaEvent = {
|
const hipaaEvent = {
|
||||||
eventType: 'modify',
|
eventType: 'modify',
|
||||||
userId: Meteor.userId(),
|
userId: Meteor.userId(),
|
||||||
userName: Meteor.user().profile.fullName,
|
userName: Meteor.user().profile.fullName,
|
||||||
collectionName: "Timepoints",
|
collectionName: 'Timepoints',
|
||||||
recordId: study.timepointId,
|
recordId: selectedStudy.timepointId,
|
||||||
patientId: study.patientId,
|
patientId: selectedStudy.patientId,
|
||||||
patientName: study.patientName
|
patientName: selectedStudy.patientName
|
||||||
};
|
};
|
||||||
HipaaLogger.logEvent(hipaaEvent);
|
HipaaLogger.logEvent(hipaaEvent);
|
||||||
} else {
|
} else {
|
||||||
@ -77,14 +76,14 @@ function removeTimepointAssociations() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Log
|
// Log
|
||||||
var hipaaEvent = {
|
const hipaaEvent = {
|
||||||
eventType: 'delete',
|
eventType: 'delete',
|
||||||
userId: Meteor.userId(),
|
userId: Meteor.userId(),
|
||||||
userName: Meteor.user().profile.fullName,
|
userName: Meteor.user().profile.fullName,
|
||||||
collectionName: "Timepoints",
|
collectionName: 'Timepoints',
|
||||||
recordId: study.timepointId,
|
recordId: selectedStudy.timepointId,
|
||||||
patientId: study.patientId,
|
patientId: selectedStudy.patientId,
|
||||||
patientName: study.patientName
|
patientName: selectedStudy.patientName
|
||||||
};
|
};
|
||||||
HipaaLogger.logEvent(hipaaEvent);
|
HipaaLogger.logEvent(hipaaEvent);
|
||||||
});
|
});
|
||||||
@ -94,43 +93,32 @@ function removeTimepointAssociations() {
|
|||||||
|
|
||||||
// ---------- TODO: Remove these duplicated functions below -------------
|
// ---------- TODO: Remove these duplicated functions below -------------
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exports all selected studies on the studylist
|
* Exports all selected studies on the studylist
|
||||||
*/
|
*/
|
||||||
function exportSelectedStudies() {
|
function exportSelectedStudies() {
|
||||||
const selectedStudies = StudyListSelectedStudies.find({}, {
|
const selectedStudies = OHIF.studylist.getSelectedStudies();
|
||||||
sort: {
|
|
||||||
studyDate: 1
|
|
||||||
}
|
|
||||||
}).fetch();
|
|
||||||
|
|
||||||
if (!selectedStudies || !selectedStudies.length) {
|
if (!selectedStudies || !selectedStudies.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
exportStudies(selectedStudies);
|
OHIF.studylist.exportStudies(selectedStudies);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads multiple unassociated studies in the Viewer
|
* Loads multiple unassociated studies in the Viewer
|
||||||
*/
|
*/
|
||||||
function viewStudies() {
|
function viewStudies() {
|
||||||
console.log('viewStudies');
|
OHIF.log.info('viewStudies');
|
||||||
const selectedStudies = StudyListSelectedStudies.find({}, {
|
const selectedStudies = OHIF.studylist.getSelectedStudies();
|
||||||
sort: {
|
|
||||||
studyDate: 1
|
|
||||||
}
|
|
||||||
}).fetch();
|
|
||||||
|
|
||||||
if (!selectedStudies || !selectedStudies.length) {
|
if (!selectedStudies || !selectedStudies.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var title = selectedStudies[0].patientName;
|
const title = selectedStudies[0].patientName;
|
||||||
var studyInstanceUids = selectedStudies.map(function(study) {
|
const studyInstanceUids = selectedStudies.map(study => study.studyInstanceUid);
|
||||||
return study.studyInstanceUid;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Generate a unique ID to represent this tab
|
// Generate a unique ID to represent this tab
|
||||||
// We can't just use the Mongo entry ID because
|
// We can't just use the Mongo entry ID because
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
import './components';
|
|
||||||
import './lib';
|
import './lib';
|
||||||
import './helpers';
|
import './helpers';
|
||||||
|
import './components';
|
||||||
|
|||||||
6
Packages/ohif-study-list/both/base.js
Normal file
6
Packages/ohif-study-list/both/base.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
|
|
||||||
|
OHIF.studylist = {
|
||||||
|
collections: {},
|
||||||
|
actions: {}
|
||||||
|
};
|
||||||
@ -1,8 +1,10 @@
|
|||||||
import { Mongo } from 'meteor/mongo';
|
import { Mongo } from 'meteor/mongo';
|
||||||
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
import { Servers as ServerSchema } from 'meteor/ohif:study-list/both/schema/servers.js';
|
import { Servers as ServerSchema } from 'meteor/ohif:study-list/both/schema/servers.js';
|
||||||
|
|
||||||
StudyImportStatus = new Mongo.Collection('studyImportStatus');
|
const StudyImportStatus = new Mongo.Collection('studyImportStatus');
|
||||||
StudyImportStatus._debugName = 'StudyImportStatus';
|
StudyImportStatus._debugName = 'StudyImportStatus';
|
||||||
|
OHIF.studylist.collections.StudyImportStatus = StudyImportStatus;
|
||||||
|
|
||||||
// Servers describe the DICOM servers configurations
|
// Servers describe the DICOM servers configurations
|
||||||
Servers = new Mongo.Collection('servers');
|
Servers = new Mongo.Collection('servers');
|
||||||
|
|||||||
4
Packages/ohif-study-list/both/index.js
Normal file
4
Packages/ohif-study-list/both/index.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import './lib';
|
||||||
|
import './schema';
|
||||||
|
import './base.js';
|
||||||
|
import './collections.js';
|
||||||
1
Packages/ohif-study-list/both/lib/index.js
Normal file
1
Packages/ohif-study-list/both/lib/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
import './getCurrentServer.js';
|
||||||
1
Packages/ohif-study-list/both/schema/index.js
Normal file
1
Packages/ohif-study-list/both/schema/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
import './servers.js';
|
||||||
1
Packages/ohif-study-list/client/collections/index.js
Normal file
1
Packages/ohif-study-list/client/collections/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
import './subscriptions.js';
|
||||||
5
Packages/ohif-study-list/client/components/index.js
Normal file
5
Packages/ohif-study-list/client/components/index.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import './progressDialog';
|
||||||
|
import './seriesDetailsModal';
|
||||||
|
import './serverInformation';
|
||||||
|
import './studyContextMenu';
|
||||||
|
import './studylist';
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
import './progressDialog.html';
|
||||||
|
import './progressDialog.styl';
|
||||||
|
import './progressDialog.js';
|
||||||
@ -7,9 +7,8 @@
|
|||||||
<div class="progress">
|
<div class="progress">
|
||||||
<div class="progress-bar progress-bar-striped active"
|
<div class="progress-bar progress-bar-striped active"
|
||||||
role="progressbar"
|
role="progressbar"
|
||||||
style="width:{{progressStatus}}">
|
style="width: {{progressStatus}}%"
|
||||||
{{formatNumberPrecision progressStatus 0}}%
|
>{{formatNumberPrecision progressStatus 0}}%</div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div id="message">
|
<div id="message">
|
||||||
{{{progressMessage}}}
|
{{{progressMessage}}}
|
||||||
|
|||||||
@ -1,4 +1,28 @@
|
|||||||
progressDialog = {
|
import { Template } from 'meteor/templating';
|
||||||
|
import { Session } from 'meteor/session';
|
||||||
|
import { _ } from 'meteor/underscore';
|
||||||
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
|
|
||||||
|
const updateProgress = numberOfCompleted => {
|
||||||
|
const progressDialogSettings = Session.get('progressDialogSettings');
|
||||||
|
progressDialogSettings.numberOfCompleted = numberOfCompleted;
|
||||||
|
|
||||||
|
Session.set('progressDialogSettings', progressDialogSettings);
|
||||||
|
|
||||||
|
if (progressDialogSettings.numberOfCompleted === progressDialogSettings.numberOfTotal) {
|
||||||
|
OHIF.studylist.progressDialog.close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const setProgressMessage = message => {
|
||||||
|
let progressDialogSettings = Session.get('progressDialogSettings');
|
||||||
|
progressDialogSettings.message = message;
|
||||||
|
Session.set('progressDialogSettings', progressDialogSettings);
|
||||||
|
};
|
||||||
|
|
||||||
|
OHIF.studylist.progressDialog = {
|
||||||
|
update: _.debounce(updateProgress, 100),
|
||||||
|
setMessage: _.debounce(setProgressMessage, 100),
|
||||||
show: function(title, numberOfTotal) {
|
show: function(title, numberOfTotal) {
|
||||||
Session.set('progressDialogSettings', {
|
Session.set('progressDialogSettings', {
|
||||||
title: title,
|
title: title,
|
||||||
@ -8,16 +32,6 @@ progressDialog = {
|
|||||||
|
|
||||||
$('#progressDialog').css('display', 'block');
|
$('#progressDialog').css('display', 'block');
|
||||||
},
|
},
|
||||||
update: function(numberOfCompleted) {
|
|
||||||
var progressDialogSettings = Session.get('progressDialogSettings');
|
|
||||||
progressDialogSettings.numberOfCompleted = numberOfCompleted;
|
|
||||||
|
|
||||||
Session.set('progressDialogSettings', progressDialogSettings);
|
|
||||||
|
|
||||||
if (progressDialogSettings.numberOfCompleted === progressDialogSettings.numberOfTotal) {
|
|
||||||
progressDialog.close();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
close: function() {
|
close: function() {
|
||||||
Session.set('progressDialogSettings', {
|
Session.set('progressDialogSettings', {
|
||||||
title: '',
|
title: '',
|
||||||
@ -26,17 +40,12 @@ progressDialog = {
|
|||||||
});
|
});
|
||||||
|
|
||||||
$('#progressDialog').css('display', 'none');
|
$('#progressDialog').css('display', 'none');
|
||||||
},
|
|
||||||
setMessage: function(message) {
|
|
||||||
let progressDialogSettings = Session.get('progressDialogSettings');
|
|
||||||
progressDialogSettings.message = message;
|
|
||||||
Session.set('progressDialogSettings', progressDialogSettings);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Template.progressDialog.helpers({
|
Template.progressDialog.helpers({
|
||||||
progressDialogTitle() {
|
progressDialogTitle() {
|
||||||
var settings = Session.get('progressDialogSettings');
|
const settings = Session.get('progressDialogSettings');
|
||||||
if (!settings) {
|
if (!settings) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -55,11 +64,11 @@ Template.progressDialog.helpers({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return settings.numberOfCompleted / settings.numberOfTotal;
|
return (settings.numberOfCompleted / settings.numberOfTotal) * 100;
|
||||||
},
|
},
|
||||||
|
|
||||||
progressMessage() {
|
progressMessage() {
|
||||||
var settings = Session.get('progressDialogSettings');
|
const settings = Session.get('progressDialogSettings');
|
||||||
if (!settings) {
|
if (!settings) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,5 @@
|
|||||||
|
import './viewSeriesDetailsModal.html';
|
||||||
|
|
||||||
|
import './seriesDetailsTable/seriesDetailsTable.html';
|
||||||
|
import './seriesDetailsTable/seriesDetailsTable.styl';
|
||||||
|
import './seriesDetailsTable/seriesDetailsTable.js';
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
import './serverInformationDicomWeb/serverInformationDicomWeb.html';
|
||||||
|
import './serverInformationDicomWeb/serverInformationDicomWeb.js';
|
||||||
|
|
||||||
|
import './serverInformationDimse/serverInformationDimse.html';
|
||||||
|
import './serverInformationDimse/serverInformationDimse.js';
|
||||||
|
|
||||||
|
import './serverInformationForm/serverInformationForm.html';
|
||||||
|
import './serverInformationForm/serverInformationForm.js';
|
||||||
|
|
||||||
|
import './serverInformationList/serverInformationList.html';
|
||||||
|
import './serverInformationList/serverInformationList.js';
|
||||||
|
|
||||||
|
import './serverInformationModal/serverInformationModal.html';
|
||||||
|
import './serverInformationModal/serverInformationModal.styl';
|
||||||
|
import './serverInformationModal/serverInformationModal.js';
|
||||||
@ -1,10 +0,0 @@
|
|||||||
<template name="serverInformationFormField">
|
|
||||||
<div class="col-lg-6">
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="wrapper">
|
|
||||||
<span>{{this.label}}</span>
|
|
||||||
<input type="text" name="{{this.name}}" class="form-control">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
import './studyContextMenu.html';
|
||||||
|
import './studyContextMenu.js';
|
||||||
@ -1,25 +1,12 @@
|
|||||||
<template name="studyContextMenu">
|
<template name="studyContextMenu">
|
||||||
{{#if uiSettings.studyListFunctionsEnabled}}
|
{{#dropdownForm}}
|
||||||
<div id="studyContextMenu" class="studyContextMenu noselect"
|
<li><a id="viewSeriesDetails" type="button"
|
||||||
oncontextmenu='return false;'
|
title="View Series Details"
|
||||||
unselectable='on'
|
>View Series Details</a></li>
|
||||||
onselectstart='return false;'>
|
<li class="disabled"><a class="disabled">Anonymize</a></li>
|
||||||
<ul>
|
<li class="disabled"><a class="disabled">Send</a></li>
|
||||||
<li>
|
<li class="divider" role="separator"></li>
|
||||||
<a id="viewSeriesDetails" type="button"
|
<li class="disabled"><a class="disabled">Delete</a></li>
|
||||||
title="View Series Details">
|
<li><a id="exportSelectedStudies" title="Export Selected Studies">Export</a></li>
|
||||||
View Series Details
|
{{/dropdownForm}}
|
||||||
</a>
|
|
||||||
<a class="disabled">Anonymize</a>
|
|
||||||
<a class="disabled">Send</a>
|
|
||||||
<hr/>
|
|
||||||
<a class="disabled">Delete</a>
|
|
||||||
<a id="exportSelectedStudies"
|
|
||||||
title="Export Selected Studies">
|
|
||||||
Export
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,84 +1,48 @@
|
|||||||
function closeHandler(dialog) {
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
// Hide the dialog
|
|
||||||
$(dialog).css('display', 'none');
|
|
||||||
|
|
||||||
// Remove the backdrop
|
|
||||||
$('.removableBackdrop').remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function is used inside the StudyList package to define a right click callback
|
* This function is used inside the StudyList package to define a right click callback
|
||||||
*
|
*
|
||||||
* @param e
|
* @param event
|
||||||
*/
|
*/
|
||||||
openStudyContextMenu = function(e) {
|
openStudyContextMenu = event => {
|
||||||
StudyList.functions['exportSelectedStudies'] = exportSelectedStudies;
|
if (!OHIF.uiSettings.studyListFunctionsEnabled) {
|
||||||
StudyList.functions['viewSeriesDetails'] = viewSeriesDetails;
|
return;
|
||||||
|
|
||||||
|
|
||||||
Template.studyContextMenu.study = $(e.currentTarget);
|
|
||||||
|
|
||||||
var dialog = $('#studyContextMenu');
|
|
||||||
|
|
||||||
// Show the nonTargetLesion dialog above
|
|
||||||
var dialogProperty = {
|
|
||||||
display: 'block'
|
|
||||||
};
|
|
||||||
|
|
||||||
var pageHeight = $(document).height();
|
|
||||||
dialogProperty.top = Math.max(e.pageY, 0);
|
|
||||||
dialogProperty.top = Math.min(dialogProperty.top, pageHeight - dialog.outerHeight());
|
|
||||||
|
|
||||||
var pageWidth = $(document).width();
|
|
||||||
dialogProperty.left = Math.max(e.pageX, 0);
|
|
||||||
dialogProperty.left = Math.min(dialogProperty.left, pageWidth - dialog.outerWidth());
|
|
||||||
|
|
||||||
// Device is touch device or not
|
|
||||||
// If device is touch device, set position center of screen vertically and horizontally
|
|
||||||
if (isTouchDevice()) {
|
|
||||||
// add dialogMobile class to provide a black, transparent background
|
|
||||||
dialog.addClass('dialogMobile');
|
|
||||||
dialogProperty.top = 0;
|
|
||||||
dialogProperty.left = 0;
|
|
||||||
dialogProperty.right = 0;
|
|
||||||
dialogProperty.bottom = 0;
|
|
||||||
dialogProperty.margin = 'auto';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dialog.css(dialogProperty);
|
StudyList.functions.exportSelectedStudies = exportSelectedStudies;
|
||||||
dialog.focus();
|
StudyList.functions.viewSeriesDetails = viewSeriesDetails;
|
||||||
|
|
||||||
// Show the backdrop
|
Template.studyContextMenu.$study = $(event.currentTarget);
|
||||||
UI.render(Template.removableBackdrop, document.body);
|
|
||||||
|
|
||||||
// Make sure the context menu is closed when the user clicks away
|
const dropdown = OHIF.ui.showFormDropdown('studyContextMenu');
|
||||||
$('.removableBackdrop').one('mousedown touchstart', function() {
|
const $dropdown = $(dropdown);
|
||||||
closeHandler(dialog);
|
const $dropdownMenu = $dropdown.children('.dropdown-menu');
|
||||||
});
|
|
||||||
|
dropdown.oncontextmenu = () => false;
|
||||||
|
|
||||||
|
$dropdownMenu.css({
|
||||||
|
position: 'fixed',
|
||||||
|
left: `${event.clientX}px`,
|
||||||
|
top: `${event.clientY}px`,
|
||||||
|
'z-index': 10000
|
||||||
|
}).bounded().focus();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exports all selected studies on the studylist
|
* Exports all selected studies on the studylist
|
||||||
*/
|
*/
|
||||||
function exportSelectedStudies() {
|
function exportSelectedStudies() {
|
||||||
var selectedStudies = StudyListSelectedStudies.find({}, {
|
const selectedStudies = OHIF.studylist.getSelectedStudies();
|
||||||
sort: {
|
|
||||||
studyDate: 1
|
|
||||||
}
|
|
||||||
}).fetch() || [];
|
|
||||||
|
|
||||||
exportStudies(selectedStudies);
|
OHIF.studylist.exportStudies(selectedStudies);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display series details of study in modal
|
* Display series details of study in modal
|
||||||
*/
|
*/
|
||||||
function viewSeriesDetails() {
|
function viewSeriesDetails() {
|
||||||
var selectedStudies = StudyListSelectedStudies.find({}, {
|
const selectedStudies = OHIF.studylist.getSelectedStudies();
|
||||||
sort: {
|
|
||||||
studyDate: 1
|
|
||||||
}
|
|
||||||
}).fetch();
|
|
||||||
|
|
||||||
if (!selectedStudies) {
|
if (!selectedStudies) {
|
||||||
return;
|
return;
|
||||||
@ -91,7 +55,7 @@ function viewSeriesDetails() {
|
|||||||
|
|
||||||
Template.studyContextMenu.events({
|
Template.studyContextMenu.events({
|
||||||
'click a': function(e) {
|
'click a': function(e) {
|
||||||
var study = Template.studyContextMenu.study;
|
var study = Template.studyContextMenu.$study;
|
||||||
var id = $(e.currentTarget).attr('id');
|
var id = $(e.currentTarget).attr('id');
|
||||||
|
|
||||||
var fn = StudyList.functions[id];
|
var fn = StudyList.functions[id];
|
||||||
@ -100,6 +64,5 @@ Template.studyContextMenu.events({
|
|||||||
}
|
}
|
||||||
|
|
||||||
var dialog = $('#studyContextMenu');
|
var dialog = $('#studyContextMenu');
|
||||||
closeHandler(dialog);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,63 +0,0 @@
|
|||||||
@import "{design}/app"
|
|
||||||
|
|
||||||
.studyContextMenu
|
|
||||||
z-index: 10000
|
|
||||||
display: none
|
|
||||||
position: absolute
|
|
||||||
color: #2f2f2f
|
|
||||||
background-color: #f2f2f2
|
|
||||||
|
|
||||||
width: -moz-fit-content
|
|
||||||
width: -webkit-fit-content
|
|
||||||
width: fit-content
|
|
||||||
width: 207px
|
|
||||||
|
|
||||||
height: -moz-fit-content
|
|
||||||
height: -webkit-fit-content
|
|
||||||
height: fit-content
|
|
||||||
|
|
||||||
min-width: 100px
|
|
||||||
font-size: 14px
|
|
||||||
text-align: left
|
|
||||||
-webkit-background-clip: padding-box
|
|
||||||
background-clip: padding-box
|
|
||||||
border: 1px solid rgba(0, 0, 0, .15)
|
|
||||||
border-radius: 3px
|
|
||||||
-webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175)
|
|
||||||
box-shadow: 0 6px 12px rgba(0, 0, 0, .175)
|
|
||||||
|
|
||||||
hr
|
|
||||||
border-top: solid 1px #d1d1d1
|
|
||||||
margin: 2px
|
|
||||||
|
|
||||||
ul
|
|
||||||
width: 100%
|
|
||||||
margin: 5px 0
|
|
||||||
list-style: none
|
|
||||||
padding: 0
|
|
||||||
|
|
||||||
li
|
|
||||||
clear: both
|
|
||||||
white-space: nowrap
|
|
||||||
|
|
||||||
a
|
|
||||||
padding: 3px 15px
|
|
||||||
display: block
|
|
||||||
text-decoration: none
|
|
||||||
cursor: pointer
|
|
||||||
font-size: 15px
|
|
||||||
line-height: 1.7
|
|
||||||
color: black
|
|
||||||
|
|
||||||
&:not(.disabled):hover
|
|
||||||
theme('color', '$activeColor')
|
|
||||||
text-decoration: none
|
|
||||||
|
|
||||||
&.disabled
|
|
||||||
opacity: 0.4
|
|
||||||
pointer-events: none
|
|
||||||
cursor: default
|
|
||||||
|
|
||||||
.fa-lg
|
|
||||||
width: 40px
|
|
||||||
text-align: center
|
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
import './studylist.html';
|
||||||
|
import './studylist.js';
|
||||||
|
import './studylist.styl';
|
||||||
|
|
||||||
|
import './studylistStudy/studylistStudy.html';
|
||||||
|
import './studylistStudy/studylistStudy.js';
|
||||||
|
import './studylistStudy/studylistStudy.styl';
|
||||||
|
|
||||||
|
import './studylistResult/studylistResult.html';
|
||||||
|
import './studylistResult/studylistResult.js';
|
||||||
|
import './studylistResult/studylistResult.styl';
|
||||||
|
|
||||||
|
import './studylistToolbar/studylistToolbar.html';
|
||||||
|
import './studylistToolbar/studylistToolbar.js';
|
||||||
|
import './studylistToolbar/studylistToolbar.styl';
|
||||||
|
|
||||||
|
import './studylistPagination/studylistPagination.html';
|
||||||
|
import './studylistPagination/studylistPagination.styl';
|
||||||
|
import './studylistPagination/studylistPagination.js';
|
||||||
@ -14,6 +14,5 @@
|
|||||||
{{#each templateName in additionalTemplates}}
|
{{#each templateName in additionalTemplates}}
|
||||||
{{> UI.dynamic template=templateName}}
|
{{> UI.dynamic template=templateName}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{>studyContextMenu }}
|
|
||||||
{{>progressDialog}}
|
{{>progressDialog}}
|
||||||
</template>
|
</template>
|
||||||
@ -44,18 +44,3 @@ Template.studylist.onRendered(() => {
|
|||||||
|
|
||||||
Meteor.subscribe('hangingprotocols');
|
Meteor.subscribe('hangingprotocols');
|
||||||
});
|
});
|
||||||
|
|
||||||
Template.studylist.events({
|
|
||||||
'click #tablist a[data-toggle="tab"]': function(e) {
|
|
||||||
// If this tab is already active, do nothing
|
|
||||||
const tabButton = $(e.currentTarget);
|
|
||||||
const tabTitle = tabButton.parents('.tabTitle');
|
|
||||||
if (tabTitle.hasClass('active')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, switch to the tab
|
|
||||||
const contentId = tabButton.data('target').replace('#', '');
|
|
||||||
switchToTab(contentId);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
<template name="tabTitle">
|
|
||||||
<li role="presentation" class="tabTitle">
|
|
||||||
<a data-target="#{{contentid}}" role="tab" data-toggle="tab">
|
|
||||||
{{title}}
|
|
||||||
<button class="close">×</button>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</template>
|
|
||||||
@ -1,42 +0,0 @@
|
|||||||
Template.tabTitle.events({
|
|
||||||
/**
|
|
||||||
* Closes a tab when the close button is pressed in the title
|
|
||||||
* The next tab to the left is loaded if the current tab is closed
|
|
||||||
*
|
|
||||||
* @param e The click event used to close the tab
|
|
||||||
*/
|
|
||||||
'click .close': function(e) {
|
|
||||||
console.log('click .close');
|
|
||||||
|
|
||||||
// Identify the tab title DOM node
|
|
||||||
var tab = $(e.currentTarget).parents('a[data-toggle="tab"]').eq(0);
|
|
||||||
|
|
||||||
// Get the relevent contentId that this tab title represents
|
|
||||||
// Replace any hash marks (#) that were required by Bootstrap's tab switching
|
|
||||||
var contentId = tab.data('target').replace('#', '');
|
|
||||||
|
|
||||||
// Check if we are closing the active tab. If we are, prepare to switch
|
|
||||||
// to the next tab to the left.
|
|
||||||
var activeContentId = Session.get('activeContentId');
|
|
||||||
if (activeContentId === contentId) {
|
|
||||||
// Find the index of the tab that is being closed
|
|
||||||
var tabIndex = tab.parent('li').index();
|
|
||||||
|
|
||||||
// Find the index the tab to its left
|
|
||||||
var newActiveTabIndex = Math.max(tabIndex - 1, 0);
|
|
||||||
|
|
||||||
// Find the DOM node of the tab that will be activated
|
|
||||||
var newActiveTab = $('.tabTitle').eq(newActiveTabIndex);
|
|
||||||
|
|
||||||
// Find the content ID of the tab that will be switched to
|
|
||||||
var newActiveTabLink = newActiveTab.find('a[data-toggle=tab]');
|
|
||||||
var newContentId = newActiveTabLink.data('target').replace('#', '');
|
|
||||||
|
|
||||||
// Switch to this tab
|
|
||||||
switchToTab(newContentId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove any stored data related to this tab from the global ViewerData structure
|
|
||||||
delete ViewerData[contentId];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
.tabTitle
|
|
||||||
button.close
|
|
||||||
// Prevent blue glow around button in Chrome
|
|
||||||
outline: none
|
|
||||||
3
Packages/ohif-study-list/client/index.js
Normal file
3
Packages/ohif-study-list/client/index.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import './collections';
|
||||||
|
import './lib';
|
||||||
|
import './components';
|
||||||
@ -1,10 +1,13 @@
|
|||||||
var exportFailed;
|
import { JSZip } from 'meteor/silentcicero:jszip';
|
||||||
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
|
|
||||||
|
let exportFailed;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exports requested studies
|
* Exports requested studies
|
||||||
* @param studiesToExport Studies to export
|
* @param studiesToExport Studies to export
|
||||||
*/
|
*/
|
||||||
exportStudies = function(studiesToExport) {
|
OHIF.studylist.exportStudies = studiesToExport => {
|
||||||
if (studiesToExport.length < 1) {
|
if (studiesToExport.length < 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -12,37 +15,45 @@ exportStudies = function(studiesToExport) {
|
|||||||
queryStudies(studiesToExport, exportQueriedStudies);
|
queryStudies(studiesToExport, exportQueriedStudies);
|
||||||
};
|
};
|
||||||
|
|
||||||
function exportQueriedStudies(studiesToExport) {
|
const exportQueriedStudies = studiesToExport => {
|
||||||
var numberOfFilesToExport = 0;
|
let numberOfFilesToExport = 0;
|
||||||
studiesToExport.forEach(function(study) {
|
studiesToExport.forEach(study => {
|
||||||
numberOfFilesToExport += getNumberOfFilesInStudy(study);
|
numberOfFilesToExport += getNumberOfFilesInStudy(study);
|
||||||
});
|
});
|
||||||
|
|
||||||
progressDialog.show("Exporting Studies...", numberOfFilesToExport);
|
OHIF.studylist.progressDialog.show('Exporting Studies...', numberOfFilesToExport);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
exportQueriedStudiesInternal(studiesToExport, numberOfFilesToExport);
|
exportQueriedStudiesInternal(studiesToExport, numberOfFilesToExport);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
progressDialog.close();
|
OHIF.studylist.progressDialog.close();
|
||||||
console.error("Failed to export studies: " + err.message);
|
OHIF.log.error(`Failed to export studies: ${err.message}`);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
function exportQueriedStudiesInternal(studiesToExport, numberOfFilesToExport) {
|
const exportQueriedStudiesInternal = (studiesToExport, numberOfFilesToExport) => {
|
||||||
var zip = new JSZip();
|
const zip = new JSZip();
|
||||||
|
|
||||||
exportFailed = false;
|
exportFailed = false;
|
||||||
var numberOfFilesExported = 0;
|
let numberOfFilesExported = 0;
|
||||||
|
|
||||||
studiesToExport.forEach(function(study) {
|
const onExportFailed = err => {
|
||||||
|
exportFailed = true;
|
||||||
|
OHIF.studylist.progressDialog.close();
|
||||||
|
|
||||||
|
//TODO: Export failed and dialog closed, so let user know
|
||||||
|
OHIF.log.error('Failed to export studies!', err);
|
||||||
|
};
|
||||||
|
|
||||||
|
studiesToExport.forEach(study => {
|
||||||
sortStudy(study);
|
sortStudy(study);
|
||||||
|
|
||||||
var studyFolder = zip.folder(study.studyInstanceUid);
|
const studyFolder = zip.folder(study.studyInstanceUid);
|
||||||
|
|
||||||
study.seriesList.forEach(function(series) {
|
study.seriesList.forEach(series => {
|
||||||
var seriesFolder = studyFolder.folder(series.seriesInstanceUid);
|
const seriesFolder = studyFolder.folder(series.seriesInstanceUid);
|
||||||
|
|
||||||
series.instances.forEach(function(instance) {
|
series.instances.forEach(instance => {
|
||||||
if (!instance.wadouri) {
|
if (!instance.wadouri) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -53,12 +64,12 @@ function exportQueriedStudiesInternal(studiesToExport, numberOfFilesToExport) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Download and Zip the dicom file
|
// Download and Zip the dicom file
|
||||||
var xhr = new XMLHttpRequest();
|
const xhr = new XMLHttpRequest();
|
||||||
xhr.open("GET", instance.wadouri, true);
|
xhr.open('GET', instance.wadouri, true);
|
||||||
xhr.responseType = "blob";
|
xhr.responseType = 'blob';
|
||||||
|
|
||||||
// Downloaded the dicom file completely
|
// Downloaded the dicom file completely
|
||||||
xhr.onload = function(e) {
|
xhr.onload = () => {
|
||||||
// If failed to download a dicom file, skip others
|
// If failed to download a dicom file, skip others
|
||||||
if (exportFailed) {
|
if (exportFailed) {
|
||||||
return;
|
return;
|
||||||
@ -66,17 +77,17 @@ function exportQueriedStudiesInternal(studiesToExport, numberOfFilesToExport) {
|
|||||||
|
|
||||||
// Failed to export a file
|
// Failed to export a file
|
||||||
if (xhr.readyState === 4 && xhr.status !== 200) {
|
if (xhr.readyState === 4 && xhr.status !== 200) {
|
||||||
onExportFailed("File not downloaded: " + instance.wadouri);
|
onExportFailed(`File not downloaded: ${instance.wadouri}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var blobFile = new Blob([xhr.response], {type: 'application/dicom'});
|
const blobFile = new Blob([xhr.response], { type: 'application/dicom' });
|
||||||
|
|
||||||
var fileReader = new FileReader();
|
const fileReader = new FileReader();
|
||||||
|
|
||||||
fileReader.onload = function() {
|
fileReader.onload = () => {
|
||||||
try {
|
try {
|
||||||
seriesFolder.file(instance.sopInstanceUid + ".dcm", fileReader.result, { binary: true });
|
seriesFolder.file(`${instance.sopInstanceUid}.dcm`, fileReader.result, { binary: true });
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
onExportFailed(err.message);
|
onExportFailed(err.message);
|
||||||
return;
|
return;
|
||||||
@ -85,31 +96,21 @@ function exportQueriedStudiesInternal(studiesToExport, numberOfFilesToExport) {
|
|||||||
numberOfFilesExported++;
|
numberOfFilesExported++;
|
||||||
|
|
||||||
if (numberOfFilesExported === numberOfFilesToExport) {
|
if (numberOfFilesExported === numberOfFilesToExport) {
|
||||||
var zipContent = zip.generate({ type: "blob" });
|
const zipContent = zip.generate({ type: 'blob' });
|
||||||
saveAs(zipContent, "studies.zip");
|
saveAs(zipContent, 'studies.zip');
|
||||||
}
|
}
|
||||||
|
|
||||||
progressDialog.update(numberOfFilesExported);
|
OHIF.studylist.progressDialog.update(numberOfFilesExported);
|
||||||
};
|
};
|
||||||
|
|
||||||
fileReader.readAsArrayBuffer(blobFile);
|
fileReader.readAsArrayBuffer(blobFile);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Failed to download the dicom file
|
// Failed to download the dicom file
|
||||||
xhr.onerror = function() {
|
xhr.onerror = () => onExportFailed(`File not downloaded: ${instance.wadouri}`);
|
||||||
onExportFailed("File not downloaded: " + instance.wadouri);
|
|
||||||
};
|
|
||||||
|
|
||||||
xhr.send();
|
xhr.send();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
function onExportFailed(err) {
|
|
||||||
exportFailed = true;
|
|
||||||
progressDialog.close();
|
|
||||||
|
|
||||||
//TODO: Export failed and dialog closed, so let user know
|
|
||||||
console.error("Failed to export studies!", err);
|
|
||||||
}
|
|
||||||
|
|||||||
@ -0,0 +1,9 @@
|
|||||||
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
|
|
||||||
|
OHIF.studylist.getSelectedStudies = () => {
|
||||||
|
return StudyListSelectedStudies.find({}, {
|
||||||
|
sort: {
|
||||||
|
studyDate: 1
|
||||||
|
}
|
||||||
|
}).fetch() || [];
|
||||||
|
};
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { $ } from 'meteor/jquery';
|
||||||
import { OHIF } from 'meteor/ohif:core';
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -10,7 +11,7 @@ import { OHIF } from 'meteor/ohif:core';
|
|||||||
* @param doneCallback The callback function to be executed when the study retrieval has finished
|
* @param doneCallback The callback function to be executed when the study retrieval has finished
|
||||||
* @param failCallback The callback function to be executed when the study retrieval has failed
|
* @param failCallback The callback function to be executed when the study retrieval has failed
|
||||||
*/
|
*/
|
||||||
getStudiesMetadata = function(studyInstanceUids, doneCallback, failCallback) {
|
OHIF.studylist.getStudiesMetadata = (studyInstanceUids, doneCallback, failCallback) => {
|
||||||
// Check to make sure studyInstanceUids were actually input
|
// Check to make sure studyInstanceUids were actually input
|
||||||
if (!studyInstanceUids || !studyInstanceUids.length) {
|
if (!studyInstanceUids || !studyInstanceUids.length) {
|
||||||
if (failCallback && typeof failCallback === 'function') {
|
if (failCallback && typeof failCallback === 'function') {
|
||||||
@ -22,13 +23,13 @@ getStudiesMetadata = function(studyInstanceUids, doneCallback, failCallback) {
|
|||||||
|
|
||||||
// Create an empty array to store the Promises for each metaData
|
// Create an empty array to store the Promises for each metaData
|
||||||
// retrieval call
|
// retrieval call
|
||||||
var promises = [];
|
const promises = [];
|
||||||
|
|
||||||
// Loop through the array of studyInstanceUids
|
// Loop through the array of studyInstanceUids
|
||||||
studyInstanceUids.forEach(function(studyInstanceUid) {
|
studyInstanceUids.forEach(function(studyInstanceUid) {
|
||||||
// Create a new Deferred to monitor the progress of the asynchronous
|
// Create a new Deferred to monitor the progress of the asynchronous
|
||||||
// metaData retrieval
|
// metaData retrieval
|
||||||
var deferred = new $.Deferred();
|
const deferred = new $.Deferred();
|
||||||
|
|
||||||
// Send the call, and attach doneCallbacks and failCallbacks
|
// Send the call, and attach doneCallbacks and failCallbacks
|
||||||
// which can resolve or reject the related promise based on its outcome
|
// which can resolve or reject the related promise based on its outcome
|
||||||
@ -45,7 +46,7 @@ getStudiesMetadata = function(studyInstanceUids, doneCallback, failCallback) {
|
|||||||
// When all of the promises are complete, this callback runs
|
// When all of the promises are complete, this callback runs
|
||||||
$.when.apply($, promises).done(function() {
|
$.when.apply($, promises).done(function() {
|
||||||
// Convert the Arguments Array-like Object to an actual array
|
// Convert the Arguments Array-like Object to an actual array
|
||||||
var studies = $.makeArray(arguments);
|
const studies = $.makeArray(arguments);
|
||||||
|
|
||||||
// Pass the studies array to the doneCallback, if one exists
|
// Pass the studies array to the doneCallback, if one exists
|
||||||
if (doneCallback && typeof doneCallback === 'function') {
|
if (doneCallback && typeof doneCallback === 'function') {
|
||||||
|
|||||||
@ -14,7 +14,7 @@ importStudies = function(filesToImport, importCallback) {
|
|||||||
};
|
};
|
||||||
var numberOfFilesToUpload = filesToImport.length;
|
var numberOfFilesToUpload = filesToImport.length;
|
||||||
var studiesToImport = [];
|
var studiesToImport = [];
|
||||||
progressDialog.show({
|
OHIF.studylist.progressDialog.show({
|
||||||
title: "Uploading Files...",
|
title: "Uploading Files...",
|
||||||
numberOfCompleted: 0,
|
numberOfCompleted: 0,
|
||||||
numberOfTotal: numberOfFilesToUpload
|
numberOfTotal: numberOfFilesToUpload
|
||||||
@ -38,7 +38,7 @@ importStudies = function(filesToImport, importCallback) {
|
|||||||
updateFileUploadStatus(fileUploadStatus, true);
|
updateFileUploadStatus(fileUploadStatus, true);
|
||||||
|
|
||||||
var numberOfFilesProcessedToUpload = fileUploadStatus.numberOfFilesUploaded + fileUploadStatus.numberOfFilesFailed;
|
var numberOfFilesProcessedToUpload = fileUploadStatus.numberOfFilesUploaded + fileUploadStatus.numberOfFilesFailed;
|
||||||
progressDialog.update(numberOfFilesProcessedToUpload);
|
OHIF.studylist.progressDialog.update(numberOfFilesProcessedToUpload);
|
||||||
|
|
||||||
if (numberOfFilesToUpload === numberOfFilesProcessedToUpload) {
|
if (numberOfFilesToUpload === numberOfFilesProcessedToUpload) {
|
||||||
// The upload is completed, so import files
|
// The upload is completed, so import files
|
||||||
@ -74,7 +74,7 @@ function importStudiesInternal(studiesToImport, importCallback) {
|
|||||||
|
|
||||||
var numberOfStudiesToImport = studiesToImport.length;
|
var numberOfStudiesToImport = studiesToImport.length;
|
||||||
|
|
||||||
progressDialog.show({
|
OHIF.studylist.progressDialog.show({
|
||||||
title: "Importing Studies...",
|
title: "Importing Studies...",
|
||||||
numberOfCompleted: 0,
|
numberOfCompleted: 0,
|
||||||
numberOfTotal: numberOfStudiesToImport
|
numberOfTotal: numberOfStudiesToImport
|
||||||
@ -84,13 +84,13 @@ function importStudiesInternal(studiesToImport, importCallback) {
|
|||||||
Meteor.call("createStudyImportStatus", function(err, studyImportStatusId) {
|
Meteor.call("createStudyImportStatus", function(err, studyImportStatusId) {
|
||||||
if (err) {
|
if (err) {
|
||||||
// Hide dialog
|
// Hide dialog
|
||||||
progressDialog.close();
|
OHIF.studylist.progressDialog.close();
|
||||||
console.log(err.message);
|
console.log(err.message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle when StudyImportStatus collection is updated
|
// Handle when StudyImportStatus collection is updated
|
||||||
StudyImportStatus.find(studyImportStatusId).observe({
|
OHIF.studylist.collections.StudyImportStatus.find(studyImportStatusId).observe({
|
||||||
changed: function(studyImportStatus) {
|
changed: function(studyImportStatus) {
|
||||||
if (!studyImportStatus) {
|
if (!studyImportStatus) {
|
||||||
return;
|
return;
|
||||||
@ -100,16 +100,16 @@ function importStudiesInternal(studiesToImport, importCallback) {
|
|||||||
|
|
||||||
// Show number of imported files
|
// Show number of imported files
|
||||||
var successMessage = 'Imported '+studyImportStatus.numberOfStudiesImported+' of '+numberOfStudiesToImport;
|
var successMessage = 'Imported '+studyImportStatus.numberOfStudiesImported+' of '+numberOfStudiesToImport;
|
||||||
progressDialog.setMessage({
|
OHIF.studylist.progressDialog.setMessage({
|
||||||
message: successMessage,
|
message: successMessage,
|
||||||
messageType: 'success'
|
messageType: 'success'
|
||||||
});
|
});
|
||||||
progressDialog.update(numberOfStudiesProcessedToImport);
|
OHIF.studylist.progressDialog.update(numberOfStudiesProcessedToImport);
|
||||||
|
|
||||||
// Show number of failed files if there is at least one failed file
|
// Show number of failed files if there is at least one failed file
|
||||||
if (studyImportStatus.numberOfStudiesFailed > 0) {
|
if (studyImportStatus.numberOfStudiesFailed > 0) {
|
||||||
var successMessage = 'Failed '+studyImportStatus.numberOfStudiesFailed+' of '+numberOfStudiesToImport;
|
var successMessage = 'Failed '+studyImportStatus.numberOfStudiesFailed+' of '+numberOfStudiesToImport;
|
||||||
progressDialog.setMessage({
|
OHIF.studylist.progressDialog.setMessage({
|
||||||
message: successMessage,
|
message: successMessage,
|
||||||
messageType: 'warning'
|
messageType: 'warning'
|
||||||
});
|
});
|
||||||
|
|||||||
11
Packages/ohif-study-list/client/lib/index.js
Normal file
11
Packages/ohif-study-list/client/lib/index.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import './third-party/jquery.twbsPagination.min.js';
|
||||||
|
|
||||||
|
import './exportStudies.js';
|
||||||
|
import './getSelectedStudies.js';
|
||||||
|
import './getStudyMetadata.js';
|
||||||
|
import './getStudiesMetadata.js';
|
||||||
|
import './importStudies.js';
|
||||||
|
import './openNewTab.js';
|
||||||
|
import './queryStudies.js';
|
||||||
|
import './studylist.js';
|
||||||
|
import './switchToTab.js';
|
||||||
@ -1,3 +1,5 @@
|
|||||||
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queries requested studies to get their metadata from PACS
|
* Queries requested studies to get their metadata from PACS
|
||||||
* @param studiesToQuery Studies to query
|
* @param studiesToQuery Studies to query
|
||||||
@ -11,7 +13,7 @@ queryStudies = function(studiesToQuery, doneCallback) {
|
|||||||
var studiesQueried = [];
|
var studiesQueried = [];
|
||||||
var numberOfStudiesToQuery = studiesToQuery.length;
|
var numberOfStudiesToQuery = studiesToQuery.length;
|
||||||
|
|
||||||
progressDialog.show("Querying Studies...", numberOfStudiesToQuery);
|
OHIF.studylist.progressDialog.show("Querying Studies...", numberOfStudiesToQuery);
|
||||||
|
|
||||||
studiesToQuery.forEach(function(studyToQuery) {
|
studiesToQuery.forEach(function(studyToQuery) {
|
||||||
getStudyMetadata(studyToQuery.studyInstanceUid, function(study) {
|
getStudyMetadata(studyToQuery.studyInstanceUid, function(study) {
|
||||||
@ -19,7 +21,7 @@ queryStudies = function(studiesToQuery, doneCallback) {
|
|||||||
|
|
||||||
var numberOfStudiesQueried = studiesQueried.length;
|
var numberOfStudiesQueried = studiesQueried.length;
|
||||||
|
|
||||||
progressDialog.update(numberOfStudiesQueried);
|
OHIF.studylist.progressDialog.update(numberOfStudiesQueried);
|
||||||
|
|
||||||
if (numberOfStudiesQueried === numberOfStudiesToQuery) {
|
if (numberOfStudiesQueried === numberOfStudiesToQuery) {
|
||||||
doneCallback(studiesQueried);
|
doneCallback(studiesQueried);
|
||||||
|
|||||||
@ -13,9 +13,6 @@ switchToTab = function(contentId) {
|
|||||||
|
|
||||||
OHIF.log.info('Switching to tab: ' + contentId);
|
OHIF.log.info('Switching to tab: ' + contentId);
|
||||||
|
|
||||||
$('.tabTitle').removeClass('active');
|
|
||||||
$('.tabTitle a[data-target="#' + contentId + '"]').addClass('active');
|
|
||||||
|
|
||||||
$('.tab-content .tab-pane').removeClass('active');
|
$('.tab-content .tab-pane').removeClass('active');
|
||||||
if (contentId !== 'studylistTab') {
|
if (contentId !== 'studylistTab') {
|
||||||
$('.tab-content .tab-pane#viewerTab').addClass('active');
|
$('.tab-content .tab-pane#viewerTab').addClass('active');
|
||||||
@ -71,7 +68,7 @@ switchToTab = function(contentId) {
|
|||||||
var studyInstanceUids = ViewerData[contentId].studyInstanceUids;
|
var studyInstanceUids = ViewerData[contentId].studyInstanceUids;
|
||||||
|
|
||||||
// Attempt to retrieve the meta data (it might be cached)
|
// Attempt to retrieve the meta data (it might be cached)
|
||||||
getStudiesMetadata(studyInstanceUids, function(studies) {
|
OHIF.studylist.getStudiesMetadata(studyInstanceUids, function(studies) {
|
||||||
viewStudiesInTab(contentId, studies);
|
viewStudiesInTab(contentId, studies);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
Package.describe({
|
Package.describe({
|
||||||
name: "ohif:study-list",
|
name: 'ohif:study-list',
|
||||||
summary: "Basic study list for web-based DICOM viewers",
|
summary: 'Basic study list for web-based DICOM viewers',
|
||||||
version: '0.0.1'
|
version: '0.0.1'
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -34,109 +34,14 @@ Package.onUse(function (api) {
|
|||||||
// TODO: Replace with NPM dependency
|
// TODO: Replace with NPM dependency
|
||||||
api.use('ohif:cornerstone'); // Only for HammerJS
|
api.use('ohif:cornerstone'); // Only for HammerJS
|
||||||
|
|
||||||
api.addFiles('both/collections.js', ['client', 'server']);
|
// Client and server imports
|
||||||
api.addFiles('both/schema/servers.js', ['client', 'server']);
|
api.addFiles('both/index.js', [ 'client', 'server' ]);
|
||||||
api.addFiles('both/lib/getCurrentServer.js', ['client', 'server']);
|
|
||||||
|
|
||||||
// Client collections and subscriptions
|
// Server imports
|
||||||
api.addFiles('client/collections/subscriptions.js', 'client');
|
api.addFiles('server/index.js', 'server');
|
||||||
|
|
||||||
// Components
|
// Client imports
|
||||||
api.addFiles('client/components/studylist.html', 'client');
|
api.addFiles('client/index.js', 'client');
|
||||||
api.addFiles('client/components/studylist.js', 'client');
|
|
||||||
api.addFiles('client/components/studylist.styl', 'client');
|
|
||||||
|
|
||||||
api.addFiles('client/components/tabTitle/tabTitle.html', 'client');
|
|
||||||
api.addFiles('client/components/tabTitle/tabTitle.js', 'client');
|
|
||||||
api.addFiles('client/components/tabTitle/tabTitle.styl', 'client');
|
|
||||||
|
|
||||||
api.addFiles('client/components/studylistStudy/studylistStudy.html', 'client');
|
|
||||||
api.addFiles('client/components/studylistStudy/studylistStudy.js', 'client');
|
|
||||||
api.addFiles('client/components/studylistStudy/studylistStudy.styl', 'client');
|
|
||||||
|
|
||||||
api.addFiles('client/components/studylistResult/studylistResult.html', 'client');
|
|
||||||
api.addFiles('client/components/studylistResult/studylistResult.js', 'client');
|
|
||||||
api.addFiles('client/components/studylistResult/studylistResult.styl', 'client');
|
|
||||||
|
|
||||||
api.addFiles('client/components/studyContextMenu/studyContextMenu.html', 'client');
|
|
||||||
api.addFiles('client/components/studyContextMenu/studyContextMenu.js', 'client');
|
|
||||||
api.addFiles('client/components/studyContextMenu/studyContextMenu.styl', 'client');
|
|
||||||
|
|
||||||
api.addFiles('client/components/studylistToolbar/studylistToolbar.html', 'client');
|
|
||||||
api.addFiles('client/components/studylistToolbar/studylistToolbar.js', 'client');
|
|
||||||
api.addFiles('client/components/studylistToolbar/studylistToolbar.styl', 'client');
|
|
||||||
|
|
||||||
api.addFiles('client/components/progressDialog/progressDialog.html', 'client');
|
|
||||||
api.addFiles('client/components/progressDialog/progressDialog.styl', 'client');
|
|
||||||
api.addFiles('client/components/progressDialog/progressDialog.js', 'client');
|
|
||||||
|
|
||||||
api.addFiles('client/components/studylistPagination/studylistPagination.html', 'client');
|
|
||||||
api.addFiles('client/components/studylistPagination/studylistPagination.styl', 'client');
|
|
||||||
api.addFiles('client/components/studylistPagination/studylistPagination.js', 'client');
|
|
||||||
|
|
||||||
api.addFiles('client/components/viewSeriesDetailsModal/viewSeriesDetailsModal.html', 'client');
|
|
||||||
|
|
||||||
api.addFiles('client/components/seriesDetailsTable/seriesDetailsTable.html', 'client');
|
|
||||||
api.addFiles('client/components/seriesDetailsTable/seriesDetailsTable.styl', 'client');
|
|
||||||
api.addFiles('client/components/seriesDetailsTable/seriesDetailsTable.js', 'client');
|
|
||||||
|
|
||||||
api.addFiles('client/components/serverInformation/serverInformationDicomWeb/serverInformationDicomWeb.html', 'client');
|
|
||||||
api.addFiles('client/components/serverInformation/serverInformationDicomWeb/serverInformationDicomWeb.js', 'client');
|
|
||||||
|
|
||||||
api.addFiles('client/components/serverInformation/serverInformationDimse/serverInformationDimse.html', 'client');
|
|
||||||
api.addFiles('client/components/serverInformation/serverInformationDimse/serverInformationDimse.js', 'client');
|
|
||||||
|
|
||||||
api.addFiles('client/components/serverInformation/serverInformationForm/serverInformationForm.html', 'client');
|
|
||||||
api.addFiles('client/components/serverInformation/serverInformationForm/serverInformationForm.js', 'client');
|
|
||||||
api.addFiles('client/components/serverInformation/serverInformationForm/serverInformationFormField.html', 'client');
|
|
||||||
|
|
||||||
api.addFiles('client/components/serverInformation/serverInformationList/serverInformationList.html', 'client');
|
|
||||||
api.addFiles('client/components/serverInformation/serverInformationList/serverInformationList.js', 'client');
|
|
||||||
|
|
||||||
api.addFiles('client/components/serverInformation/serverInformationModal/serverInformationModal.html', 'client');
|
|
||||||
api.addFiles('client/components/serverInformation/serverInformationModal/serverInformationModal.styl', 'client');
|
|
||||||
api.addFiles('client/components/serverInformation/serverInformationModal/serverInformationModal.js', 'client');
|
|
||||||
|
|
||||||
// Client-side library functions
|
|
||||||
api.addFiles('client/lib/getStudyMetadata.js', 'client');
|
|
||||||
api.addFiles('client/lib/getStudiesMetadata.js', 'client');
|
|
||||||
api.addFiles('client/lib/openNewTab.js', 'client');
|
|
||||||
api.addFiles('client/lib/switchToTab.js', 'client');
|
|
||||||
api.addFiles('client/lib/exportStudies.js', 'client');
|
|
||||||
api.addFiles('client/lib/studylist.js', 'client');
|
|
||||||
api.addFiles('client/lib/queryStudies.js', 'client');
|
|
||||||
api.addFiles('client/lib/importStudies.js', 'client');
|
|
||||||
api.addFiles('client/lib/jquery.twbsPagination/jquery.twbsPagination.min.js', 'client');
|
|
||||||
|
|
||||||
// Server-side functions
|
|
||||||
api.addFiles('server/publications.js', 'server');
|
|
||||||
api.addFiles('server/validateServerConfiguration.js', 'server');
|
|
||||||
api.addFiles('server/servers.js', 'server');
|
|
||||||
api.addFiles('server/lib/remoteGetValue.js', 'server');
|
|
||||||
api.addFiles('server/lib/encodeQueryData.js', 'server');
|
|
||||||
api.addFiles('server/lib/parseFloatArray.js', 'server');
|
|
||||||
|
|
||||||
api.addFiles('server/methods/getStudyMetadata.js', 'server');
|
|
||||||
api.addFiles('server/methods/importStudies.js', 'server');
|
|
||||||
api.addFiles('server/methods/studylistSearch.js', 'server');
|
|
||||||
|
|
||||||
api.addFiles('server/services/namespace.js', 'server');
|
|
||||||
|
|
||||||
// DICOMWeb instance, study, and metadata retrieval
|
|
||||||
api.addFiles('server/services/qido/instances.js', 'server');
|
|
||||||
api.addFiles('server/services/qido/studies.js', 'server');
|
|
||||||
api.addFiles('server/services/wado/retrieveMetadata.js', 'server');
|
|
||||||
|
|
||||||
// DIMSE instance, study, and metadata retrieval
|
|
||||||
api.addFiles('server/services/dimse/instances.js', 'server');
|
|
||||||
api.addFiles('server/services/dimse/studies.js', 'server');
|
|
||||||
api.addFiles('server/services/dimse/retrieveMetadata.js', 'server');
|
|
||||||
api.addFiles('server/services/dimse/setup.js', 'server');
|
|
||||||
|
|
||||||
// Study, instance, and metadata retrieval from remote PACS via Orthanc as a proxy
|
|
||||||
api.addFiles('server/services/remote/instances.js', 'server');
|
|
||||||
api.addFiles('server/services/remote/studies.js', 'server');
|
|
||||||
api.addFiles('server/services/remote/retrieveMetadata.js', 'server');
|
|
||||||
|
|
||||||
// Export Servers and CurrentServer Collections
|
// Export Servers and CurrentServer Collections
|
||||||
api.export('Servers', ['client', 'server']);
|
api.export('Servers', ['client', 'server']);
|
||||||
@ -149,10 +54,8 @@ Package.onUse(function (api) {
|
|||||||
|
|
||||||
// Export StudyList helper functions for usage in Routes
|
// Export StudyList helper functions for usage in Routes
|
||||||
api.export('getStudyMetadata', 'client');
|
api.export('getStudyMetadata', 'client');
|
||||||
api.export('getStudiesMetadata', 'client');
|
|
||||||
api.export('openNewTab', 'client');
|
api.export('openNewTab', 'client');
|
||||||
api.export('switchToTab', 'client');
|
api.export('switchToTab', 'client');
|
||||||
api.export('progressDialog', 'client');
|
|
||||||
api.export('StudyList');
|
api.export('StudyList');
|
||||||
|
|
||||||
// Export the global ViewerData object
|
// Export the global ViewerData object
|
||||||
@ -160,5 +63,5 @@ Package.onUse(function (api) {
|
|||||||
|
|
||||||
// Export the Collections
|
// Export the Collections
|
||||||
api.export('StudyListStudies', 'client');
|
api.export('StudyListStudies', 'client');
|
||||||
api.export('StudyListSelectedStudies', 'client')
|
api.export('StudyListSelectedStudies', 'client');
|
||||||
});
|
});
|
||||||
|
|||||||
6
Packages/ohif-study-list/server/index.js
Normal file
6
Packages/ohif-study-list/server/index.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import './publications.js';
|
||||||
|
import './validateServerConfiguration.js';
|
||||||
|
|
||||||
|
import './lib';
|
||||||
|
import './methods';
|
||||||
|
import './services';
|
||||||
2
Packages/ohif-study-list/server/lib/index.js
Normal file
2
Packages/ohif-study-list/server/lib/index.js
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
import './remoteGetValue.js';
|
||||||
|
import './encodeQueryData.js';
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import { StudyImportStatus } from '../../both/collections';
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
|
|
||||||
var fs = Npm.require('fs');
|
var fs = Npm.require('fs');
|
||||||
var fiber = Npm.require('fibers');
|
var fiber = Npm.require('fibers');
|
||||||
@ -73,14 +73,14 @@ Meteor.methods({
|
|||||||
*/
|
*/
|
||||||
createStudyImportStatus: function() {
|
createStudyImportStatus: function() {
|
||||||
var studyImportStatus = { numberOfStudiesImported: 0, numberOfStudiesFailed: 0 };
|
var studyImportStatus = { numberOfStudiesImported: 0, numberOfStudiesFailed: 0 };
|
||||||
return StudyImportStatus.insert(studyImportStatus);
|
return OHIF.studylist.collections.StudyImportStatus.insert(studyImportStatus);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Remove the study import status item from the collection
|
* Remove the study import status item from the collection
|
||||||
* @param id Collection id of the study import status in the collection
|
* @param id Collection id of the study import status in the collection
|
||||||
*/
|
*/
|
||||||
removeStudyImportStatus: function(id) {
|
removeStudyImportStatus: function(id) {
|
||||||
StudyImportStatus.remove(id);
|
OHIF.studylist.collections.StudyImportStatus.remove(id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -96,16 +96,16 @@ function importStudiesDIMSE(studiesToImport, studyImportStatusId) {
|
|||||||
try {
|
try {
|
||||||
// Update the import status
|
// Update the import status
|
||||||
if (err) {
|
if (err) {
|
||||||
StudyImportStatus.update({_id: studyImportStatusId}, {$inc: {'numberOfStudiesFailed': 1}});
|
OHIF.studylist.collections.StudyImportStatus.update({_id: studyImportStatusId}, {$inc: {'numberOfStudiesFailed': 1}});
|
||||||
console.log("Failed to import study via DIMSE: ", file, err);
|
console.log("Failed to import study via DIMSE: ", file, err);
|
||||||
} else {
|
} else {
|
||||||
StudyImportStatus.update({_id: studyImportStatusId}, {$inc: {'numberOfStudiesImported': 1}});
|
OHIF.studylist.collections.StudyImportStatus.update({_id: studyImportStatusId}, {$inc: {'numberOfStudiesImported': 1}});
|
||||||
console.log("Study successfully imported via DIMSE: ", file);
|
console.log("Study successfully imported via DIMSE: ", file);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch(error) {
|
} catch(error) {
|
||||||
|
|
||||||
StudyImportStatus.update({_id: studyImportStatusId}, {$inc: {'numberOfStudiesFailed': 1}});
|
OHIF.studylist.collections.StudyImportStatus.update({_id: studyImportStatusId}, {$inc: {'numberOfStudiesFailed': 1}});
|
||||||
console.log("Failed to import study via DIMSE: ", file, error);
|
console.log("Failed to import study via DIMSE: ", file, error);
|
||||||
} finally {
|
} finally {
|
||||||
// The import operation of this file is completed, so delete it if still exists
|
// The import operation of this file is completed, so delete it if still exists
|
||||||
@ -116,7 +116,7 @@ function importStudiesDIMSE(studiesToImport, studyImportStatusId) {
|
|||||||
|
|
||||||
}).run();
|
}).run();
|
||||||
} catch(error) {
|
} catch(error) {
|
||||||
StudyImportStatus.update({_id: studyImportStatusId}, {$inc: {'numberOfStudiesFailed': 1}});
|
OHIF.studylist.collections.StudyImportStatus.update({_id: studyImportStatusId}, {$inc: {'numberOfStudiesFailed': 1}});
|
||||||
console.log("Failed to import study via DIMSE: ", file, error);
|
console.log("Failed to import study via DIMSE: ", file, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
3
Packages/ohif-study-list/server/methods/index.js
Normal file
3
Packages/ohif-study-list/server/methods/index.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import './getStudyMetadata.js';
|
||||||
|
import './importStudies.js';
|
||||||
|
import './studylistSearch.js';
|
||||||
@ -1,5 +1,8 @@
|
|||||||
|
import { Meteor } from 'meteor/meteor';
|
||||||
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
|
|
||||||
Meteor.publish('studyImportStatus', () => {
|
Meteor.publish('studyImportStatus', () => {
|
||||||
return StudyImportStatus.find();
|
return OHIF.studylist.collections.StudyImportStatus.find();
|
||||||
});
|
});
|
||||||
|
|
||||||
Meteor.publish('servers', () => {
|
Meteor.publish('servers', () => {
|
||||||
|
|||||||
16
Packages/ohif-study-list/server/services/index.js
Normal file
16
Packages/ohif-study-list/server/services/index.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import './namespace.js';
|
||||||
|
|
||||||
|
// DICOMWeb instance, study, and metadata retrieval
|
||||||
|
import './qido/instances.js';
|
||||||
|
import './qido/studies.js';
|
||||||
|
import './wado/retrieveMetadata.js';
|
||||||
|
|
||||||
|
// DIMSE instance, study, and metadata retrieval
|
||||||
|
import './dimse/instances.js';
|
||||||
|
import './dimse/studies.js';
|
||||||
|
import './dimse/retrieveMetadata.js';
|
||||||
|
|
||||||
|
// Study, instance, and metadata retrieval from remote PACS via Orthanc as a proxy
|
||||||
|
import './remote/instances.js';
|
||||||
|
import './remote/studies.js';
|
||||||
|
import './remote/retrieveMetadata.js';
|
||||||
@ -183,7 +183,6 @@ Package.onUse(function(api) {
|
|||||||
api.export('updateAllViewports', 'client');
|
api.export('updateAllViewports', 'client');
|
||||||
api.export('queryStudies', 'client');
|
api.export('queryStudies', 'client');
|
||||||
api.export('getNumberOfFilesInStudy', 'client');
|
api.export('getNumberOfFilesInStudy', 'client');
|
||||||
api.export('exportStudies', 'client');
|
|
||||||
api.export('importStudies', 'client');
|
api.export('importStudies', 'client');
|
||||||
api.export('getActiveViewportElement', 'client');
|
api.export('getActiveViewportElement', 'client');
|
||||||
api.export('getInstanceClassDefaultViewport', 'client');
|
api.export('getInstanceClassDefaultViewport', 'client');
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user