Fixing serverId issues
This commit is contained in:
1 parent
95533c785b
commit
b4bb44625c
62 files changed
+685
-556
No files matched your search
@@ -1,19 +1,8 @@
|
||||
import { Mongo } from 'meteor/mongo';
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
import { Servers as ServerSchema } from 'meteor/ohif:study-list/both/schema/servers.js';
|
||||
|
||||
const StudyImportStatus = new Mongo.Collection('studyImportStatus');
|
||||
StudyImportStatus._debugName = 'StudyImportStatus';
|
||||
OHIF.studylist.collections.StudyImportStatus = StudyImportStatus;
|
||||
|
||||
// Servers describe the DICOM servers configurations
|
||||
Servers = new Mongo.Collection('servers');
|
||||
// TODO: Make the Schema match what we are currently sticking into the Collection
|
||||
//Servers.attachSchema(ServerSchema);
|
||||
Servers._debugName = 'Servers';
|
||||
|
||||
// CurrentServer is a single document collection to describe which of the Servers is being used
|
||||
CurrentServer = new Mongo.Collection('currentServer');
|
||||
CurrentServer._debugName = 'CurrentServer';
|
||||
|
||||
export { StudyImportStatus, Servers, CurrentServer };
|
||||
export { StudyImportStatus };
|
||||
@@ -1,4 +1,2 @@
|
||||
import './base.js';
|
||||
import './lib';
|
||||
import './schema';
|
||||
import './collections.js';
|
||||
@@ -1,14 +0,0 @@
|
||||
/**
|
||||
* Retrieves the current server configuration used to retrieve studies
|
||||
*/
|
||||
getCurrentServer = () => {
|
||||
const currentServer = CurrentServer.findOne();
|
||||
|
||||
if (!currentServer) {
|
||||
return;
|
||||
}
|
||||
|
||||
const serverConfiguration = Servers.findOne({ _id: currentServer.serverId });
|
||||
|
||||
return serverConfiguration;
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
import './getCurrentServer.js';
|
||||
@@ -1 +0,0 @@
|
||||
import './servers.js';
|
||||
@@ -1,274 +0,0 @@
|
||||
import { SimpleSchema } from 'meteor/aldeed:simple-schema';
|
||||
|
||||
const serverNameDefinitions = {
|
||||
type: String,
|
||||
label: 'Server Name',
|
||||
max: 100
|
||||
};
|
||||
|
||||
const serverTypeDefinitions = {
|
||||
type: String,
|
||||
label: 'Server Type',
|
||||
allowedValues: ['dicomWeb', 'dimse'],
|
||||
valuesLabels: ['DICOM Web', 'DIMSE'],
|
||||
optional: true
|
||||
};
|
||||
|
||||
const wadoUriRootDefinitions = {
|
||||
type: String,
|
||||
label: 'WADO URI root',
|
||||
max: 1000
|
||||
};
|
||||
|
||||
const availableMouseButtonTools = ['wwwc', 'zoom', 'pan', 'stackScroll'];
|
||||
|
||||
export const DICOMWebRequestOptions = new SimpleSchema({
|
||||
auth: {
|
||||
type: String,
|
||||
label: 'Authentication',
|
||||
optional: true
|
||||
},
|
||||
logRequests: {
|
||||
type: Boolean,
|
||||
defaultValue: true,
|
||||
label: 'Requests'
|
||||
},
|
||||
logResponses: {
|
||||
type: Boolean,
|
||||
defaultValue: false,
|
||||
label: 'Responses'
|
||||
},
|
||||
logTiming: {
|
||||
type: Boolean,
|
||||
defaultValue: true,
|
||||
label: 'Timing'
|
||||
}
|
||||
});
|
||||
|
||||
export const DICOMWebServer = new SimpleSchema({
|
||||
name: serverNameDefinitions,
|
||||
type: serverTypeDefinitions,
|
||||
wadoUriRoot: wadoUriRootDefinitions,
|
||||
wadoRoot: {
|
||||
type: String,
|
||||
label: 'WADO root',
|
||||
max: 1000
|
||||
},
|
||||
imageRendering: {
|
||||
type: String,
|
||||
label: 'Image rendering',
|
||||
allowedValues: ['wadouri', 'orthanc'],
|
||||
valuesLabels: ['WADO URI', 'ORTHANC']
|
||||
},
|
||||
qidoRoot: {
|
||||
type: String,
|
||||
label: 'QIDO root',
|
||||
max: 1000
|
||||
},
|
||||
qidoSupportsIncludeField: {
|
||||
type: Boolean,
|
||||
label: 'QIDO supports including fields',
|
||||
defaultValue: false
|
||||
},
|
||||
requestOptions: {
|
||||
type: DICOMWebRequestOptions,
|
||||
label: 'Request Options'
|
||||
}
|
||||
});
|
||||
|
||||
export const DIMSEPeer = new SimpleSchema({
|
||||
aeTitle: {
|
||||
type: String,
|
||||
label: 'AE Title'
|
||||
},
|
||||
hostAE: {
|
||||
type: String,
|
||||
label: 'AE Host',
|
||||
optional: true
|
||||
},
|
||||
host: {
|
||||
type: String,
|
||||
label: 'Host Domain/IP',
|
||||
regEx: SimpleSchema.RegEx.WeakDomain
|
||||
},
|
||||
port: {
|
||||
type: Number,
|
||||
label: 'Port',
|
||||
min: 1,
|
||||
defaultValue: 11112,
|
||||
max: 65535
|
||||
},
|
||||
default: {
|
||||
type: Boolean,
|
||||
label: 'Default',
|
||||
defaultValue: false
|
||||
},
|
||||
server: {
|
||||
type: Boolean,
|
||||
label: 'Server',
|
||||
defaultValue: false
|
||||
},
|
||||
supportsInstanceRetrievalByStudyUid: {
|
||||
type: Boolean,
|
||||
label: 'Supports instance retrieval by StudyUid',
|
||||
defaultValue: true
|
||||
}
|
||||
});
|
||||
|
||||
export const DIMSEServer = new SimpleSchema({
|
||||
name: serverNameDefinitions,
|
||||
type: serverTypeDefinitions,
|
||||
wadoUriRoot: wadoUriRootDefinitions,
|
||||
requestOptions: {
|
||||
type: DICOMWebRequestOptions,
|
||||
label: 'Request Options'
|
||||
},
|
||||
peers: {
|
||||
type: [DIMSEPeer],
|
||||
label: 'Peer List',
|
||||
minCount: 1
|
||||
}
|
||||
});
|
||||
|
||||
export const UISettings = new SimpleSchema({
|
||||
studyListFunctionsEnabled: {
|
||||
type: Boolean,
|
||||
label: 'Study List Functions Enabled?',
|
||||
defaultValue: true
|
||||
},
|
||||
leftSidebarOpen: {
|
||||
type: Boolean,
|
||||
label: 'Left sidebar open by default?',
|
||||
defaultValue: false
|
||||
},
|
||||
displaySetNavigationLoopOverSeries: {
|
||||
type: Boolean,
|
||||
label: 'The UP/DOWN display set navigation buttons will start over when reach the last display set in viewport?',
|
||||
defaultValue: true
|
||||
},
|
||||
displaySetNavigationMultipleViewports: {
|
||||
type: Boolean,
|
||||
label: 'The UP/DOWN display set navigation buttons will iterate over all the viewports at once?',
|
||||
defaultValue: false
|
||||
},
|
||||
displayEchoUltrasoundWorkflow: {
|
||||
type: Boolean,
|
||||
label: 'Enable cine dialog enhancements for multiframe images.',
|
||||
defaultValue: false
|
||||
},
|
||||
autoPositionMeasurementsTextCallOuts: {
|
||||
type: String,
|
||||
label: 'Auto position text call-outs for measurements when creating them.',
|
||||
defaultValue: 'TRBL'
|
||||
},
|
||||
studyListDateFilterNumDays: {
|
||||
type: Number,
|
||||
label: 'Number of days to be used on Study List date filter',
|
||||
min: 1
|
||||
},
|
||||
showStackLoadingProgressBar: {
|
||||
type: Boolean,
|
||||
label: 'Show a progress bar closest to the thumbnail showing how much the stack has loaded',
|
||||
defaultValue: true
|
||||
}
|
||||
});
|
||||
|
||||
export const PrefetchSchema = new SimpleSchema({
|
||||
order: {
|
||||
type: String,
|
||||
label: 'Prefetch Order',
|
||||
allowedValues: ['topdown', 'downward', 'closest'],
|
||||
optional: false
|
||||
},
|
||||
displaySetCount: {
|
||||
type: Number,
|
||||
label: 'Display Set Count',
|
||||
min: 1,
|
||||
defaultValue: 1
|
||||
}
|
||||
});
|
||||
|
||||
export const MouseButtonToolSchema = new SimpleSchema({
|
||||
left: {
|
||||
type: String,
|
||||
label: 'Left Mouse Button',
|
||||
allowedValues: availableMouseButtonTools,
|
||||
optional: true
|
||||
},
|
||||
right: {
|
||||
type: String,
|
||||
label: 'Right Mouse Button',
|
||||
allowedValues: availableMouseButtonTools,
|
||||
optional: true
|
||||
},
|
||||
middle: {
|
||||
type: String,
|
||||
label: 'Middle Mouse Button',
|
||||
allowedValues: availableMouseButtonTools,
|
||||
optional: true
|
||||
}
|
||||
});
|
||||
|
||||
export const PublicServerConfig = new SimpleSchema({
|
||||
verifyEmail: {
|
||||
type: Boolean,
|
||||
label: 'Verify Email',
|
||||
defaultValue: false
|
||||
},
|
||||
demoUserEnabled: {
|
||||
type: Boolean,
|
||||
label: 'Creates demo user on startup and show TestDrive button',
|
||||
defaultValue: true
|
||||
},
|
||||
ui: {
|
||||
type: UISettings,
|
||||
label: 'UI Settings'
|
||||
},
|
||||
prefetch: {
|
||||
type: PrefetchSchema,
|
||||
label: 'Prefetch settings'
|
||||
},
|
||||
defaultMouseButtonTools: {
|
||||
type: MouseButtonToolSchema,
|
||||
label: 'Default Mouse Button Tools'
|
||||
}
|
||||
});
|
||||
|
||||
export const Servers = new SimpleSchema({
|
||||
dicomWeb: {
|
||||
type: [DICOMWebServer],
|
||||
label: 'DICOMWeb Servers',
|
||||
optional: true
|
||||
},
|
||||
dimse: {
|
||||
type: [DIMSEServer],
|
||||
label: 'DIMSE Servers',
|
||||
optional: true
|
||||
}
|
||||
});
|
||||
|
||||
export const ServerConfiguration = new SimpleSchema({
|
||||
servers: {
|
||||
type: Servers,
|
||||
label: 'Servers'
|
||||
},
|
||||
defaultServiceType: {
|
||||
type: String,
|
||||
label: 'Default Service Type',
|
||||
defaultValue: 'dicomWeb'
|
||||
},
|
||||
dropCollections: {
|
||||
type: Boolean,
|
||||
label: 'Drop database collections',
|
||||
defaultValue: false
|
||||
},
|
||||
public: {
|
||||
type: PublicServerConfig,
|
||||
label: 'Public Server Configuration',
|
||||
},
|
||||
origin: {
|
||||
type: String,
|
||||
label: 'Origin',
|
||||
optional: true
|
||||
}
|
||||
});
|
||||
@@ -1,2 +1 @@
|
||||
import './studies.js';
|
||||
import './subscriptions.js';
|
||||
@@ -1,2 +0,0 @@
|
||||
Meteor.subscribe('servers');
|
||||
Meteor.subscribe('currentServer');
|
||||
@@ -1,4 +1,2 @@
|
||||
import './seriesDetailsModal';
|
||||
import './serverInformation';
|
||||
import './studylist';
|
||||
import './themeSelector';
|
||||
@@ -1,15 +0,0 @@
|
||||
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';
|
||||
-50
@@ -1,50 +0,0 @@
|
||||
<template name="serverInformationDicomWeb">
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
{{>inputText labelClass='form-group' key='wadoUriRoot'}}
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
{{>inputText labelClass='form-group' key='wadoRoot'}}
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
{{>inputSelect labelClass='form-group' key='imageRendering' hideSearch=true
|
||||
options=(clone placeholder='Select an option')}}
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
{{>inputText labelClass='form-group' key='qidoRoot'}}
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<strong>QIDO options</strong>
|
||||
<div class="form-group">
|
||||
<div class="form-control">
|
||||
<div class="checkbox">
|
||||
{{>inputCheckbox key='qidoSupportsIncludeField'}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<h4>Request options</h4>
|
||||
{{#group class='row' key='requestOptions'}}
|
||||
<div class="col-lg-6">
|
||||
{{>inputText labelClass='form-group' key='auth'}}
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<strong>Logging options</strong>
|
||||
<div class="form-group">
|
||||
<div class="form-control clearfix">
|
||||
<div class="checkbox pull-left">
|
||||
{{>inputCheckbox key='logRequests'}}
|
||||
</div>
|
||||
<div class="checkbox pull-left">
|
||||
{{>inputCheckbox key='logResponses'}}
|
||||
</div>
|
||||
<div class="checkbox pull-left">
|
||||
{{>inputCheckbox key='logTiming'}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/group}}
|
||||
</template>
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
Template.serverInformationDicomWeb.onRendered(() => {
|
||||
const instance = Template.instance();
|
||||
instance.autorun(function() {
|
||||
const mode = instance.data.mode.get();
|
||||
if (mode === 'edit') {
|
||||
const data = instance.data.currentItem.get();
|
||||
instance.data.form.value(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
-60
@@ -1,60 +0,0 @@
|
||||
<template name="serverInformationDimse">
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
{{>inputText labelClass='form-group' key='wadoUriRoot'}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default panel-body">
|
||||
<div class="clearfix">
|
||||
<h4 class="pull-left">Peer List</h4>
|
||||
{{#button class='btn btn-primary pull-right'
|
||||
action='newPeer' params=peer
|
||||
}}New peer{{/button}}
|
||||
</div>
|
||||
<hr>
|
||||
{{#group class='row' key='peers' arrayValues=true}}
|
||||
{{#each peer in instance.peers.get}}
|
||||
{{#group class='col-lg-6'}}
|
||||
<div class="panel panel-default panel-body">
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
{{>inputText class='form-control' key='aeTitle'}}
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
{{>inputText class='form-control' key='hostAE'}}
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
{{>inputText class='form-control' key='host'}}
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
{{>inputText class='form-control' key='port'}}
|
||||
</div>
|
||||
<div class="col-lg-12">
|
||||
<strong>Settings</strong>
|
||||
<div class="form-group">
|
||||
<div class="form-control height-auto clearfix">
|
||||
<div class="checkbox pull-left">
|
||||
{{>inputCheckbox key='default'}}
|
||||
</div>
|
||||
<div class="checkbox pull-left">
|
||||
{{>inputCheckbox key='server'}}
|
||||
</div>
|
||||
<div class="checkbox pull-left">
|
||||
{{>inputCheckbox key='supportsInstanceRetrievalByStudyUid'}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-12 text-right">
|
||||
{{#button class='btn btn-sm btn-danger'
|
||||
action='removePeer' params=@index
|
||||
}}Remove{{/button}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/group}}
|
||||
{{/each}}
|
||||
{{/group}}
|
||||
</div>
|
||||
<hr>
|
||||
</template>
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
Template.serverInformationDimse.onCreated(() => {
|
||||
const instance = Template.instance();
|
||||
instance.peers = new ReactiveVar([]);
|
||||
instance.autorun(() => {
|
||||
const currentItem = instance.data.currentItem.get();
|
||||
if (currentItem) {
|
||||
instance.peers.set(currentItem.peers || []);
|
||||
}
|
||||
});
|
||||
const api = instance.data.api;
|
||||
_.extend(api, {
|
||||
newPeer() {
|
||||
const peers = instance.peers.get();
|
||||
peers.push({});
|
||||
instance.peers.set(peers);
|
||||
},
|
||||
removePeer(peerIndex) {
|
||||
const peers = instance.peers.get();
|
||||
peers.splice(peerIndex, 1);
|
||||
instance.peers.set(peers);
|
||||
|
||||
const data = instance.data.currentItem.get();
|
||||
data.peers = peers;
|
||||
Tracker.afterFlush(() => instance.data.form.value(data));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Template.serverInformationDimse.onRendered(() => {
|
||||
const instance = Template.instance();
|
||||
instance.autorun(() => {
|
||||
const mode = instance.data.mode.get();
|
||||
if (mode === 'edit') {
|
||||
const data = instance.data.currentItem.get();
|
||||
instance.data.form.value(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
<template name="serverInformationForm">
|
||||
{{#form class='server-information-form' schema=instance.currentSchema api=this.api}}
|
||||
{{>inputHidden key='_id'}}
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
{{>inputText labelClass='form-group' key='name'}}
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
{{>inputSelect labelClass='form-group' key='type'
|
||||
hideSearch=true disabled=(eq this.mode.get 'edit')
|
||||
options=(clone placeholder='Choose a server type')}}
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
{{#if eq this.serverType.get 'dicomWeb'}}
|
||||
{{>serverInformationDicomWeb this}}
|
||||
{{/if}}
|
||||
{{#if eq this.serverType.get 'dimse'}}
|
||||
{{>serverInformationDimse this}}
|
||||
{{/if}}
|
||||
{{#if this.serverType.get}}
|
||||
<div>
|
||||
{{#button class='btn btn-primary' action='save'}}Save{{/button}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/form}}
|
||||
</template>
|
||||
-72
@@ -1,72 +0,0 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { Template } from 'meteor/templating';
|
||||
import { ReactiveVar } from 'meteor/reactive-var';
|
||||
import { DICOMWebServer as dicomSchema } from 'meteor/ohif:study-list/both/schema/servers.js';
|
||||
import { DIMSEServer as dimseSchema } from 'meteor/ohif:study-list/both/schema/servers.js';
|
||||
|
||||
Template.serverInformationForm.onCreated(() => {
|
||||
const instance = Template.instance();
|
||||
|
||||
instance.data.api = {
|
||||
save() {
|
||||
// Stop here if the form validation fails
|
||||
if (!instance.data.form.validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the current form data
|
||||
const formData = instance.data.form.value();
|
||||
|
||||
// Call the save method
|
||||
Meteor.call('serverSave', formData, function(error) {
|
||||
if (error) {
|
||||
// TODO: check for errors: not-authorized, data-write
|
||||
}
|
||||
|
||||
instance.data.resetState();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
instance.currentSchema = new ReactiveVar(dicomSchema);
|
||||
});
|
||||
|
||||
Template.serverInformationForm.onRendered(() => {
|
||||
const instance = Template.instance();
|
||||
|
||||
instance.data.$form = instance.$('form').first();
|
||||
instance.data.form = instance.data.$form.data('component');
|
||||
|
||||
// Handle the server type
|
||||
instance.autorun(() => {
|
||||
// Get the server type component
|
||||
const typeComponent = instance.$('[data-key=type] :input').data('component');
|
||||
|
||||
// Run this computation every time the user change the server type
|
||||
typeComponent.depend();
|
||||
|
||||
// Get the current server type value
|
||||
const type = typeComponent.value();
|
||||
|
||||
// Set the serverType reactive value
|
||||
instance.data.serverType.set(type);
|
||||
|
||||
// Change the schema based on the selected server type
|
||||
if (type === 'dimse') {
|
||||
instance.currentSchema.set(dimseSchema);
|
||||
} else {
|
||||
instance.currentSchema.set(dicomSchema);
|
||||
}
|
||||
});
|
||||
|
||||
// Handle the form mode (edit or add)
|
||||
instance.autorun(() => {
|
||||
const mode = instance.data.mode.get();
|
||||
|
||||
// Check if it is on edit mode and load the saved data
|
||||
if (mode === 'edit') {
|
||||
const data = instance.data.currentItem.get();
|
||||
instance.data.form.value(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
<template name="serverInformationList">
|
||||
{{#form api=instance.api}}
|
||||
<table id="tblServer" class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Type</th>
|
||||
<th class="text-right">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each server in servers}}
|
||||
<tr>
|
||||
<td>{{server.name}}</td>
|
||||
<td>{{server.type}}</td>
|
||||
<td class="text-right">
|
||||
<div class="btn-group" role="group" aria-label="Actions">
|
||||
{{#button class='btn btn-sm btn-default' action='use' params=server title='Use this server' disabled=(isActive server)}}
|
||||
{{#if isActive server}}
|
||||
<i class="fa fa-check-square-o"></i>
|
||||
{{else}}
|
||||
<i class="fa fa-square-o"></i>
|
||||
{{/if}}
|
||||
{{/button}}
|
||||
{{#button class='btn btn-sm btn-default' action='edit' params=server title='Edit' disabled=(eq server.origin 'json')}}
|
||||
<i class="fa fa-pencil"></i>
|
||||
{{/button}}
|
||||
{{#button class='btn btn-sm btn-danger' action='delete' params=server title='Remove' disabled=(eq server.origin 'json')}}
|
||||
<i class="fa fa-trash"></i>
|
||||
{{/button}}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{{else}}
|
||||
<tr>
|
||||
<td class="text-center" colspan="3">No servers found</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{#button class='btn btn-sm btn-default' action='add'}}
|
||||
<i class="fa fa-plus"></i> Add a new server
|
||||
{{/button}}
|
||||
{{/form}}
|
||||
</template>
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
Template.serverInformationList.onCreated(() => {
|
||||
const instance = Template.instance();
|
||||
|
||||
instance.api = {
|
||||
add: () => instance.data.mode.set('create'),
|
||||
edit(server) {
|
||||
instance.data.currentItem.set(server);
|
||||
instance.data.mode.set('edit');
|
||||
},
|
||||
delete(server) {
|
||||
// TODO: Replace this for confirmation dialog after LT-refactor is merged back to master
|
||||
if (!confirm('Are you sure you want to remove this peer?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
Meteor.call('serverRemove', server._id, error => {
|
||||
// TODO: check for errors: data-write
|
||||
});
|
||||
},
|
||||
use(server) {
|
||||
Meteor.call('serverSetActive', server._id, error => {
|
||||
// TODO: check for errors: data-write
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
Template.serverInformationList.helpers({
|
||||
isActive: function(server) {
|
||||
const currentServer = CurrentServer.findOne();
|
||||
if (!currentServer) {
|
||||
return;
|
||||
}
|
||||
|
||||
return server._id === currentServer.serverId;
|
||||
},
|
||||
servers: function() {
|
||||
const options = {
|
||||
sort: {
|
||||
origin: -1,
|
||||
type: 1,
|
||||
name: 1
|
||||
}
|
||||
};
|
||||
return Servers.find({}, options).fetch();
|
||||
}
|
||||
});
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
<template name="serverInformationModal">
|
||||
{{#dialogSimple (extend this dialogClass='modal-lg' title='Server Information')}}
|
||||
{{#if (eq instance.container.mode.get "list")}}
|
||||
{{>serverInformationList instance.container}}
|
||||
{{else}}
|
||||
{{>serverInformationForm instance.container}}
|
||||
{{/if}}
|
||||
{{#section 'dialogFooter'}}{{#unless (eq instance.container.mode.get "list")}}{{!
|
||||
}}<button type="button" class="btn btn-default js-back">Back</button>{{!
|
||||
}}{{/unless}}{{/section}}
|
||||
{{/dialogSimple}}
|
||||
</template>
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
import { Template } from 'meteor/templating';
|
||||
import { ReactiveVar } from 'meteor/reactive-var';
|
||||
|
||||
Template.serverInformationModal.onCreated(function() {
|
||||
const instance = Template.instance();
|
||||
instance.container = {
|
||||
mode: new ReactiveVar('list'),
|
||||
serverType: new ReactiveVar(null),
|
||||
currentItem: new ReactiveVar(null),
|
||||
$form: null,
|
||||
resetState: function() {
|
||||
instance.container.mode.set('list');
|
||||
instance.container.serverType.set(null);
|
||||
instance.container.currentItem.set(null);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
Template.serverInformationModal.events({
|
||||
'click .js-back'(event, instance) {
|
||||
instance.container.resetState();
|
||||
}
|
||||
});
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
.server-information-form
|
||||
label.wrapper
|
||||
display: block
|
||||
.checkbox
|
||||
margin: 0 8px 0 0
|
||||
@@ -1,3 +0,0 @@
|
||||
import './themeSelectorModal/themeSelectorModal.html';
|
||||
import './themeSelectorModal/themeSelectorModal.styl';
|
||||
import './themeSelectorModal/themeSelectorModal.js';
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
<template name="themeSelectorModal">
|
||||
{{#dialogForm (extend this
|
||||
id='themeSelectorModal' dialogClass='modal-lg' confirmLabel='Apply theme'
|
||||
title='Themes' titleIcon='fa fa-sliders'
|
||||
)}}
|
||||
<ul class="theme-list text-center">
|
||||
{{#each theme in themes}}
|
||||
<li class="text-center preview-theme {{printClassIfSelected theme}}" data-theme="{{theme}}">
|
||||
<div class="icon-wrapper">
|
||||
<span class="theme-icon-{{theme}}"></span>
|
||||
</div>
|
||||
<h4 class="theme-title">{{ucFirst theme}}</h4>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{/dialogForm}}
|
||||
</template>
|
||||
-80
@@ -1,80 +0,0 @@
|
||||
import { Template } from 'meteor/templating';
|
||||
import { ReactiveDict } from 'meteor/reactive-dict';
|
||||
|
||||
const DEFAULT_THEME = 'tide';
|
||||
|
||||
const getCurrentTheme = () => sessionStorage.getItem('theme');
|
||||
|
||||
const setActualTheme = theme => sessionStorage.setItem('theme', theme);
|
||||
|
||||
const addThemeToBody = theme => document.body.classList.add('theme-' + theme);
|
||||
|
||||
const removeThemesFromBody = () => {
|
||||
const classList = document.body.classList;
|
||||
|
||||
for (let i = classList.length - 1; i >= 0; i--) {
|
||||
if (classList[i].search('theme-') !== -1) {
|
||||
document.body.classList.remove(classList[i]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Template.themeSelectorModal.onCreated(() => {
|
||||
const instance = Template.instance();
|
||||
let currentTheme = getCurrentTheme();
|
||||
if (!currentTheme) {
|
||||
currentTheme = DEFAULT_THEME;
|
||||
setActualTheme(currentTheme);
|
||||
}
|
||||
|
||||
instance.state = new ReactiveDict();
|
||||
instance.state.set('selectedTheme', currentTheme);
|
||||
|
||||
addThemeToBody(currentTheme);
|
||||
|
||||
instance.container = {
|
||||
currentTheme,
|
||||
|
||||
previewTheme(theme, state) {
|
||||
state.set('selectedTheme', theme);
|
||||
removeThemesFromBody();
|
||||
addThemeToBody(theme);
|
||||
},
|
||||
|
||||
applyTheme(state) {
|
||||
setActualTheme(state.get('selectedTheme'));
|
||||
},
|
||||
|
||||
resetState(state) {
|
||||
const theme = getCurrentTheme();
|
||||
|
||||
state.set('selectedTheme', theme);
|
||||
removeThemesFromBody();
|
||||
addThemeToBody(theme);
|
||||
}
|
||||
};
|
||||
|
||||
const { promise } = instance.data;
|
||||
promise.then(() => instance.container.applyTheme(instance.state));
|
||||
promise.catch(() => instance.container.resetState(instance.state));
|
||||
});
|
||||
|
||||
Template.themeSelectorModal.helpers({
|
||||
themes: [ 'crickets', 'honeycomb', 'mint', 'overcast', 'quartz', 'tide', 'tigerlily' ],
|
||||
|
||||
ucFirst(text) {
|
||||
return text.charAt(0).toUpperCase() + text.slice(1);
|
||||
},
|
||||
|
||||
printClassIfSelected(theme) {
|
||||
const instance = Template.instance();
|
||||
|
||||
return theme === instance.state.get('selectedTheme') ? 'selected' : '';
|
||||
}
|
||||
});
|
||||
|
||||
Template.themeSelectorModal.events({
|
||||
'click .preview-theme'(event, instance) {
|
||||
instance.container.previewTheme(event.currentTarget.dataset.theme, instance.state);
|
||||
}
|
||||
});
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
@import "{ohif:design}/app.styl"
|
||||
|
||||
#themeSelectorModal
|
||||
.theme-list
|
||||
list-style: none
|
||||
padding: 0
|
||||
|
||||
li
|
||||
display: inline-block
|
||||
cursor: pointer
|
||||
margin-right: 10px
|
||||
margin-bottom: 10px
|
||||
padding: 10px
|
||||
width: 110px
|
||||
theme('border', '1px solid $uiBorderColorDark')
|
||||
|
||||
&:hover
|
||||
background-color: rgba(21, 25, 30, 0.7)
|
||||
|
||||
&:last-child
|
||||
margin-right: 0
|
||||
|
||||
&.selected
|
||||
theme('border', '3px solid $uiBorderColorActive')
|
||||
padding: 7px;
|
||||
|
||||
.icon-wrapper
|
||||
height: 64px
|
||||
width: 64px
|
||||
margin: 0 auto
|
||||
line-height: 6.5
|
||||
|
||||
.theme-title
|
||||
font-weight: normal
|
||||
font-size: 1.1em
|
||||
margin-bottom: 0
|
||||
@@ -31,6 +31,7 @@ Package.onUse(function(api) {
|
||||
api.use('ohif:design');
|
||||
api.use('ohif:core');
|
||||
api.use('ohif:log');
|
||||
api.use('ohif:servers');
|
||||
api.use('ohif:dicom-services');
|
||||
api.use('ohif:viewerbase');
|
||||
api.use('ohif:wadoproxy');
|
||||
@@ -44,12 +45,5 @@ Package.onUse(function(api) {
|
||||
// Client imports
|
||||
api.addFiles('client/index.js', 'client');
|
||||
|
||||
// Export Servers and CurrentServer Collections
|
||||
api.export('Servers', ['client', 'server']);
|
||||
api.export('CurrentServer', ['client', 'server']);
|
||||
|
||||
// Export shared lib functions
|
||||
api.export('getCurrentServer', ['client', 'server']);
|
||||
|
||||
api.export('Services', 'server');
|
||||
});
|
||||
@@ -1,6 +1,4 @@
|
||||
import './publications.js';
|
||||
import './servers.js';
|
||||
import './validateServerConfiguration.js';
|
||||
|
||||
import './lib';
|
||||
import './methods';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
|
||||
Meteor.methods({
|
||||
@@ -10,7 +11,7 @@ Meteor.methods({
|
||||
|
||||
// Get the server data. This is user-defined in the config.json files or through servers
|
||||
// configuration modal
|
||||
const server = getCurrentServer();
|
||||
const server = OHIF.servers.getCurrentServer();
|
||||
|
||||
if (!server) {
|
||||
throw 'No properly configured server was available over DICOMWeb or DIMSE.';
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
|
||||
var fs = Npm.require('fs');
|
||||
var fiber = Npm.require('fibers');
|
||||
const fs = Npm.require('fs');
|
||||
const fiber = Npm.require('fibers');
|
||||
|
||||
WebApp.connectHandlers.use('/uploadFilesToImport', function(req, res) {
|
||||
if (!req.headers.filename) {
|
||||
@@ -11,21 +12,21 @@ WebApp.connectHandlers.use('/uploadFilesToImport', function(req, res) {
|
||||
}
|
||||
|
||||
// Store files in temp location (they will be deleted when their import operations are completed)
|
||||
var dicomDir = '/tmp/dicomDir';
|
||||
const dicomDir = '/tmp/dicomDir';
|
||||
createFolderIfNotExist(dicomDir);
|
||||
|
||||
var fullFileName = dicomDir + '/' + req.headers.filename;
|
||||
var file = fs.createWriteStream(fullFileName);
|
||||
const fullFileName = dicomDir + '/' + req.headers.filename;
|
||||
const file = fs.createWriteStream(fullFileName);
|
||||
|
||||
file.on('error',function(error){
|
||||
console.log(error);
|
||||
file.on('error', function(error) {
|
||||
OHIF.log.warn(error);
|
||||
// Response: INTERNAL SERVER ERROR (500)
|
||||
res.statusCode = 400;
|
||||
res.end();
|
||||
});
|
||||
file.on('finish',function(){
|
||||
file.on('finish', function() {
|
||||
// Response: SUCCESS (200)
|
||||
res.writeHead(200, {'Content-Type': 'text/plain'});
|
||||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||
res.end(fullFileName);
|
||||
});
|
||||
|
||||
@@ -39,7 +40,7 @@ Meteor.methods({
|
||||
* @returns {boolean}
|
||||
*/
|
||||
importSupported: function() {
|
||||
const server = getCurrentServer();
|
||||
const server = OHIF.servers.getCurrentServer();
|
||||
if (server && server.type === 'dimse') {
|
||||
return true;
|
||||
}
|
||||
@@ -54,7 +55,7 @@ Meteor.methods({
|
||||
return;
|
||||
}
|
||||
|
||||
const server = getCurrentServer();
|
||||
const server = OHIF.servers.getCurrentServer();
|
||||
|
||||
if (!server) {
|
||||
throw 'No properly configured server was available over DICOMWeb or DIMSE.';
|
||||
@@ -62,7 +63,7 @@ Meteor.methods({
|
||||
|
||||
if (server.type === 'dicomWeb') {
|
||||
//TODO: Support importing studies into dicomWeb
|
||||
console.log('Importing studies into dicomWeb is currently not supported.');
|
||||
OHIF.log.warn('Importing studies into dicomWeb is currently not supported.');
|
||||
} else if (server.type === 'dimse') {
|
||||
importStudiesDIMSE(studiesToImport, studyImportStatusId);
|
||||
}
|
||||
@@ -72,7 +73,10 @@ Meteor.methods({
|
||||
* @returns {studyImportStatusId: string}
|
||||
*/
|
||||
createStudyImportStatus: function() {
|
||||
var studyImportStatus = { numberOfStudiesImported: 0, numberOfStudiesFailed: 0 };
|
||||
const studyImportStatus = {
|
||||
numberOfStudiesImported: 0,
|
||||
numberOfStudiesFailed: 0
|
||||
};
|
||||
return OHIF.studylist.collections.StudyImportStatus.insert(studyImportStatus);
|
||||
},
|
||||
/**
|
||||
@@ -96,17 +100,25 @@ function importStudiesDIMSE(studiesToImport, studyImportStatusId) {
|
||||
try {
|
||||
// Update the import status
|
||||
if (err) {
|
||||
OHIF.studylist.collections.StudyImportStatus.update({_id: studyImportStatusId}, {$inc: {'numberOfStudiesFailed': 1}});
|
||||
console.log("Failed to import study via DIMSE: ", file, err);
|
||||
OHIF.studylist.collections.StudyImportStatus.update(
|
||||
{ _id: studyImportStatusId },
|
||||
{ $inc: { numberOfStudiesFailed: 1 } }
|
||||
);
|
||||
OHIF.log.warn('Failed to import study via DIMSE: ', file, err);
|
||||
} else {
|
||||
OHIF.studylist.collections.StudyImportStatus.update({_id: studyImportStatusId}, {$inc: {'numberOfStudiesImported': 1}});
|
||||
console.log("Study successfully imported via DIMSE: ", file);
|
||||
OHIF.studylist.collections.StudyImportStatus.update(
|
||||
{ _id: studyImportStatusId },
|
||||
{ $inc: { numberOfStudiesImported: 1 } }
|
||||
);
|
||||
OHIF.log.info('Study successfully imported via DIMSE: ', file);
|
||||
}
|
||||
|
||||
} catch(error) {
|
||||
|
||||
OHIF.studylist.collections.StudyImportStatus.update({_id: studyImportStatusId}, {$inc: {'numberOfStudiesFailed': 1}});
|
||||
console.log("Failed to import study via DIMSE: ", file, error);
|
||||
OHIF.studylist.collections.StudyImportStatus.update(
|
||||
{ _id: studyImportStatusId },
|
||||
{ $inc: { numberOfStudiesFailed: 1 } }
|
||||
);
|
||||
OHIF.log.warn('Failed to import study via DIMSE: ', file, error);
|
||||
} finally {
|
||||
// The import operation of this file is completed, so delete it if still exists
|
||||
if (fileExists(file)) {
|
||||
@@ -116,17 +128,20 @@ function importStudiesDIMSE(studiesToImport, studyImportStatusId) {
|
||||
|
||||
}).run();
|
||||
} catch(error) {
|
||||
OHIF.studylist.collections.StudyImportStatus.update({_id: studyImportStatusId}, {$inc: {'numberOfStudiesFailed': 1}});
|
||||
console.log("Failed to import study via DIMSE: ", file, error);
|
||||
OHIF.studylist.collections.StudyImportStatus.update(
|
||||
{ _id: studyImportStatusId },
|
||||
{ $inc: { numberOfStudiesFailed: 1 } }
|
||||
);
|
||||
OHIF.log.warn('Failed to import study via DIMSE: ', file, error);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function createFolderIfNotExist(folder) {
|
||||
var folderParts = folder.split('/');
|
||||
var folderPart = folderParts[0];
|
||||
for (var i = 1; i < folderParts.length; i++) {
|
||||
const folderParts = folder.split('/');
|
||||
let folderPart = folderParts[0];
|
||||
for (let i = 1; i < folderParts.length; i++) {
|
||||
folderPart += '/' + folderParts[i];
|
||||
if (!folderExists(folderPart)) {
|
||||
fs.mkdirSync(folderPart);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
|
||||
Meteor.methods({
|
||||
/**
|
||||
* Use the specified filter to conduct a search from the DICOM server
|
||||
@@ -7,7 +10,7 @@ Meteor.methods({
|
||||
StudyListSearch(filter) {
|
||||
// Get the server data. This is user-defined in the config.json files or through servers
|
||||
// configuration modal
|
||||
const server = getCurrentServer();
|
||||
const server = OHIF.servers.getCurrentServer();
|
||||
|
||||
if (!server) {
|
||||
throw 'No properly configured server was available over DICOMWeb or DIMSE.';
|
||||
|
||||
@@ -2,13 +2,3 @@ import { Meteor } from 'meteor/meteor';
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
|
||||
Meteor.publish('studyImportStatus', () => OHIF.studylist.collections.StudyImportStatus.find());
|
||||
|
||||
// When publishing Servers Collection, do not publish the requestOptions.headers
|
||||
// field in case any authentication information is being passed
|
||||
Meteor.publish('servers', () => Servers.find({}, {
|
||||
fields: {
|
||||
'requestOptions.headers': 0
|
||||
}
|
||||
}));
|
||||
|
||||
Meteor.publish('currentServer', () => CurrentServer.find());
|
||||
@@ -1,94 +0,0 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { _ } from 'meteor/underscore';
|
||||
|
||||
Meteor.startup(function() {
|
||||
console.log('Adding Servers from JSON Configuration');
|
||||
Servers.remove({
|
||||
origin: 'json'
|
||||
});
|
||||
|
||||
_.each(Meteor.settings.servers, function(endpoints, serverType) {
|
||||
_.each(endpoints, function(endpoint) {
|
||||
const server = _.clone(endpoint);
|
||||
server.origin = 'json';
|
||||
server.type = serverType;
|
||||
Servers.insert(server);
|
||||
});
|
||||
});
|
||||
|
||||
ServersControl.resetCurrentServer();
|
||||
});
|
||||
|
||||
class ServersControl {
|
||||
|
||||
static writeCallback(error, affected) {
|
||||
if (error) {
|
||||
throw new Meteor.Error('data-write', error);
|
||||
}
|
||||
}
|
||||
|
||||
static resetCurrentServer() {
|
||||
const currentServer = CurrentServer.findOne();
|
||||
if (currentServer && Servers.find({ _id: currentServer.serverId }).count()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newServer = Servers.findOne({
|
||||
origin: 'json',
|
||||
type: Meteor.settings.defaultServiceType || 'dicomWeb'
|
||||
});
|
||||
|
||||
if (newServer) {
|
||||
CurrentServer.remove({});
|
||||
CurrentServer.insert({
|
||||
serverId: newServer._id
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static find(query) {
|
||||
return Servers.find(query).fetch();
|
||||
}
|
||||
|
||||
static save(serverSettings) {
|
||||
const query = {
|
||||
_id: serverSettings._id
|
||||
};
|
||||
const options = {
|
||||
upsert: true
|
||||
};
|
||||
|
||||
if (!serverSettings._id) {
|
||||
delete serverSettings._id;
|
||||
}
|
||||
|
||||
return Servers.update(query, serverSettings, options, this.writeCallback);
|
||||
}
|
||||
|
||||
static setActive(serverId) {
|
||||
CurrentServer.remove({});
|
||||
CurrentServer.insert({
|
||||
serverId: serverId
|
||||
});
|
||||
}
|
||||
|
||||
static remove(serverId) {
|
||||
const query = {
|
||||
_id: serverId
|
||||
};
|
||||
|
||||
const removeStatus = Servers.remove(query, this.writeCallback);
|
||||
|
||||
ServersControl.resetCurrentServer();
|
||||
|
||||
return removeStatus;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Meteor.methods({
|
||||
serverFind: query => ServersControl.find(query),
|
||||
serverSave: serverSettings => ServersControl.save(serverSettings),
|
||||
serverSetActive: serverId => ServersControl.setActive(serverId),
|
||||
serverRemove: serverId => ServersControl.remove(serverId)
|
||||
});
|
||||
@@ -1,66 +1,68 @@
|
||||
/**
|
||||
* Parses data returned from a study search and transforms it into
|
||||
* an array of series that are present in the study
|
||||
*
|
||||
* @param resultData
|
||||
* @returns {Array} Series List
|
||||
*/
|
||||
function resultDataToStudyMetadata(resultData) {
|
||||
var seriesMap = {};
|
||||
var seriesList = [];
|
||||
|
||||
resultData.forEach(function(instanceRaw) {
|
||||
var instance = instanceRaw.toObject();
|
||||
// Use seriesMap to cache series data
|
||||
// If the series instance UID has already been used to
|
||||
// process series data, continue using that series
|
||||
var seriesInstanceUid = instance[0x0020000E];
|
||||
var series = seriesMap[seriesInstanceUid];
|
||||
|
||||
// If no series data exists in the seriesMap cache variable,
|
||||
// process any available series data
|
||||
if (!series) {
|
||||
series = {
|
||||
seriesInstanceUid: seriesInstanceUid,
|
||||
seriesNumber: instance[0x00200011],
|
||||
instances: []
|
||||
};
|
||||
|
||||
// Save this data in the seriesMap cache variable
|
||||
seriesMap[seriesInstanceUid] = series;
|
||||
seriesList.push(series);
|
||||
}
|
||||
|
||||
// TODO: Check which peer it should point to
|
||||
var server = getCurrentServer().peers[0];
|
||||
|
||||
var serverRoot = server.host + ':' + server.port;
|
||||
|
||||
var sopInstanceUid = instance[0x00080018];
|
||||
var uri = serverRoot + '/studies/' + studyInstanceUid + '/series/' + seriesInstanceUid + '/instances/' + sopInstanceUid + '/frames/1';
|
||||
|
||||
// Add this instance to the current series
|
||||
series.instances.push({
|
||||
sopClassUid: instance[0x00080016],
|
||||
sopInstanceUid: sopInstanceUid,
|
||||
uri: uri,
|
||||
instanceNumber: instance[0x00200013]
|
||||
});
|
||||
});
|
||||
return seriesList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a set of instances using a DIMSE call
|
||||
* @param studyInstanceUid
|
||||
* @returns {{wadoUriRoot: String, studyInstanceUid: String, seriesList: Array}}
|
||||
*/
|
||||
Services.DIMSE.Instances = function(studyInstanceUid) {
|
||||
//var url = buildUrl(server, studyInstanceUid);
|
||||
var result = DIMSE.retrieveInstances(studyInstanceUid);
|
||||
|
||||
return {
|
||||
studyInstanceUid: studyInstanceUid,
|
||||
seriesList: resultDataToStudyMetadata(result)
|
||||
};
|
||||
};
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
|
||||
/**
|
||||
* Parses data returned from a study search and transforms it into
|
||||
* an array of series that are present in the study
|
||||
*
|
||||
* @param resultData
|
||||
* @returns {Array} Series List
|
||||
*/
|
||||
function resultDataToStudyMetadata(resultData, studyInstanceUid) {
|
||||
const seriesMap = {};
|
||||
const seriesList = [];
|
||||
|
||||
resultData.forEach(function(instanceRaw) {
|
||||
const instance = instanceRaw.toObject();
|
||||
// Use seriesMap to cache series data
|
||||
// If the series instance UID has already been used to
|
||||
// process series data, continue using that series
|
||||
const seriesInstanceUid = instance[0x0020000E];
|
||||
let series = seriesMap[seriesInstanceUid];
|
||||
|
||||
// If no series data exists in the seriesMap cache variable,
|
||||
// process any available series data
|
||||
if (!series) {
|
||||
series = {
|
||||
seriesInstanceUid: seriesInstanceUid,
|
||||
seriesNumber: instance[0x00200011],
|
||||
instances: []
|
||||
};
|
||||
|
||||
// Save this data in the seriesMap cache variable
|
||||
seriesMap[seriesInstanceUid] = series;
|
||||
seriesList.push(series);
|
||||
}
|
||||
|
||||
// TODO: Check which peer it should point to
|
||||
const server = OHIF.servers.getCurrentServer().peers[0];
|
||||
|
||||
const serverRoot = server.host + ':' + server.port;
|
||||
|
||||
const sopInstanceUid = instance[0x00080018];
|
||||
const uri = serverRoot + '/studies/' + studyInstanceUid + '/series/' + seriesInstanceUid + '/instances/' + sopInstanceUid + '/frames/1';
|
||||
|
||||
// Add this instance to the current series
|
||||
series.instances.push({
|
||||
sopClassUid: instance[0x00080016],
|
||||
sopInstanceUid,
|
||||
uri,
|
||||
instanceNumber: instance[0x00200013]
|
||||
});
|
||||
});
|
||||
return seriesList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a set of instances using a DIMSE call
|
||||
* @param studyInstanceUid
|
||||
* @returns {{wadoUriRoot: String, studyInstanceUid: String, seriesList: Array}}
|
||||
*/
|
||||
Services.DIMSE.Instances = function(studyInstanceUid) {
|
||||
//var url = buildUrl(server, studyInstanceUid);
|
||||
const result = DIMSE.retrieveInstances(studyInstanceUid);
|
||||
|
||||
return {
|
||||
studyInstanceUid: studyInstanceUid,
|
||||
seriesList: resultDataToStudyMetadata(result, studyInstanceUid)
|
||||
};
|
||||
};
|
||||
@@ -1,169 +1,169 @@
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
import { parseFloatArray } from 'meteor/ohif:study-list/server/lib/parseFloatArray';
|
||||
|
||||
/**
|
||||
* Returns the value of the element (e.g. '00280009')
|
||||
*
|
||||
* @param element - The group/element of the element (e.g. '00280009')
|
||||
* @param defaultValue - The default value to return if the element does not exist
|
||||
* @returns {*}
|
||||
*/
|
||||
function getValue(element, defaultValue) {
|
||||
if (!element || !element.value) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return element.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the SourceImageSequence, if it exists, in order
|
||||
* to return a ReferenceSOPInstanceUID. The ReferenceSOPInstanceUID
|
||||
* is used to refer to this image in any accompanying DICOM-SR documents.
|
||||
*
|
||||
* @param instance
|
||||
* @returns {String} The ReferenceSOPInstanceUID
|
||||
*/
|
||||
function getSourceImageInstanceUid(instance) {
|
||||
// TODO= Parse the whole Source Image Sequence
|
||||
// This is a really poor workaround for now.
|
||||
// Later we should probably parse the whole sequence.
|
||||
var SourceImageSequence = instance[0x00082112];
|
||||
if (SourceImageSequence && SourceImageSequence.length) {
|
||||
return SourceImageSequence[0][0x00081155];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses result data from a DIMSE search into Study MetaData
|
||||
* Returns an object populated with study metadata, including the
|
||||
* series list.
|
||||
*
|
||||
* @param studyInstanceUid
|
||||
* @param resultData
|
||||
* @returns {{seriesList: Array, patientName: *, patientId: *, accessionNumber: *, studyDate: *, modalities: *, studyDescription: *, imageCount: *, studyInstanceUid: *}}
|
||||
*/
|
||||
function resultDataToStudyMetadata(studyInstanceUid, resultData) {
|
||||
OHIF.log.info('resultDataToStudyMetadata');
|
||||
var seriesMap = {};
|
||||
var seriesList = [];
|
||||
|
||||
if (!resultData.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
var anInstance = resultData[0].toObject();
|
||||
if (!anInstance) {
|
||||
return;
|
||||
}
|
||||
|
||||
var studyData = {
|
||||
seriesList: seriesList,
|
||||
patientName: anInstance[0x00100010],
|
||||
patientId: anInstance[0x00100020],
|
||||
patientBirthDate: anInstance[0x00100030],
|
||||
patientSex: anInstance[0x00100040],
|
||||
accessionNumber: anInstance[0x00080050],
|
||||
studyDate: anInstance[0x00080020],
|
||||
modalities: anInstance[0x00080061],
|
||||
studyDescription: anInstance[0x00081030],
|
||||
imageCount: anInstance[0x00201208],
|
||||
studyInstanceUid: anInstance[0x0020000D],
|
||||
institutionName: anInstance[0x00080080]
|
||||
};
|
||||
|
||||
resultData.forEach(function(instanceRaw) {
|
||||
var instance = instanceRaw.toObject();
|
||||
var seriesInstanceUid = instance[0x0020000E];
|
||||
var series = seriesMap[seriesInstanceUid];
|
||||
if (!series) {
|
||||
series = {
|
||||
seriesDescription: instance[0x0008103E],
|
||||
modality: instance[0x00080060],
|
||||
seriesInstanceUid: seriesInstanceUid,
|
||||
seriesNumber: parseFloat(instance[0x00200011]),
|
||||
instances: []
|
||||
};
|
||||
seriesMap[seriesInstanceUid] = series;
|
||||
seriesList.push(series);
|
||||
}
|
||||
|
||||
var sopInstanceUid = instance[0x00080018];
|
||||
|
||||
var instanceSummary = {
|
||||
imageType: instance[0x00080008],
|
||||
sopClassUid: instance[0x00080016],
|
||||
modality: instance[0x00080060],
|
||||
sopInstanceUid: sopInstanceUid,
|
||||
instanceNumber: parseFloat(instance[0x00200013]),
|
||||
imagePositionPatient: instance[0x00200032],
|
||||
imageOrientationPatient: instance[0x00200037],
|
||||
frameOfReferenceUID: instance[0x00200052],
|
||||
sliceThickness: parseFloat(instance[0x00180050]),
|
||||
sliceLocation: parseFloat(instance[0x00201041]),
|
||||
tablePosition: parseFloat(instance[0x00189327]),
|
||||
samplesPerPixel: parseFloat(instance[0x00280002]),
|
||||
photometricInterpretation: instance[0x00280004],
|
||||
planarConfiguration: parseFloat(instance[0x00280006]),
|
||||
rows: parseFloat(instance[0x00280010]),
|
||||
columns: parseFloat(instance[0x00280011]),
|
||||
pixelSpacing: instance[0x00280030],
|
||||
bitsAllocated: parseFloat(instance[0x00280100]),
|
||||
bitsStored: parseFloat(instance[0x00280101]),
|
||||
highBit: parseFloat(instance[0x00280102]),
|
||||
pixelRepresentation: parseFloat(instance[0x00280103]),
|
||||
windowCenter: instance[0x00281050],
|
||||
windowWidth: instance[0x00281051],
|
||||
rescaleIntercept: parseFloat(instance[0x00281052]),
|
||||
rescaleSlope: parseFloat(instance[0x00281053]),
|
||||
sourceImageInstanceUid: getSourceImageInstanceUid(instance),
|
||||
laterality: instance[0x00200062],
|
||||
viewPosition: instance[0x00185101],
|
||||
acquisitionDateTime: instance[0x0008002A],
|
||||
numberOfFrames: parseFloat(instance[0x00280008]),
|
||||
frameIncrementPointer: getValue(instance[0x00280009]),
|
||||
frameTime: parseFloat(instance[0x00181063]),
|
||||
frameTimeVector: parseFloatArray(instance[0x00181065]),
|
||||
lossyImageCompression: instance[0x00282110],
|
||||
derivationDescription: instance[0x00282111],
|
||||
lossyImageCompressionRatio: instance[0x00282112],
|
||||
lossyImageCompressionMethod: instance[0x00282114],
|
||||
spacingBetweenSlices: instance[0x00180088],
|
||||
echoNumber: instance[0x00180086],
|
||||
contrastBolusAgent: instance[0x00180010]
|
||||
};
|
||||
|
||||
// Retrieve the actual data over WADO-URI
|
||||
var server = getCurrentServer();
|
||||
const wadouri = `${server.wadoUriRoot}?requestType=WADO&studyUID=${studyInstanceUid}&seriesUID=${seriesInstanceUid}&objectUID=${sopInstanceUid}&contentType=application%2Fdicom`;
|
||||
instanceSummary.wadouri = WADOProxy.convertURL(wadouri, server);
|
||||
|
||||
series.instances.push(instanceSummary);
|
||||
});
|
||||
|
||||
studyData.studyInstanceUid = studyInstanceUid;
|
||||
|
||||
return studyData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieved Study MetaData from a DICOM server using DIMSE
|
||||
* @param studyInstanceUid
|
||||
* @returns {{seriesList: Array, patientName: *, patientId: *, accessionNumber: *, studyDate: *, modalities: *, studyDescription: *, imageCount: *, studyInstanceUid: *}}
|
||||
*/
|
||||
Services.DIMSE.RetrieveMetadata = function(studyInstanceUid) {
|
||||
// TODO: Check which peer it should point to
|
||||
const activeServer = getCurrentServer().peers[0];
|
||||
const supportsInstanceRetrievalByStudyUid = activeServer.supportsInstanceRetrievalByStudyUid;
|
||||
let results;
|
||||
|
||||
// Check explicitly for a value of false, since this property
|
||||
// may be left undefined in config files
|
||||
if (supportsInstanceRetrievalByStudyUid === false) {
|
||||
results = DIMSE.retrieveInstancesByStudyOnly(studyInstanceUid);
|
||||
} else {
|
||||
results = DIMSE.retrieveInstances(studyInstanceUid);
|
||||
}
|
||||
|
||||
return resultDataToStudyMetadata(studyInstanceUid, results);
|
||||
};
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
import { parseFloatArray } from 'meteor/ohif:study-list/server/lib/parseFloatArray';
|
||||
|
||||
/**
|
||||
* Returns the value of the element (e.g. '00280009')
|
||||
*
|
||||
* @param element - The group/element of the element (e.g. '00280009')
|
||||
* @param defaultValue - The default value to return if the element does not exist
|
||||
* @returns {*}
|
||||
*/
|
||||
function getValue(element, defaultValue) {
|
||||
if (!element || !element.value) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return element.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the SourceImageSequence, if it exists, in order
|
||||
* to return a ReferenceSOPInstanceUID. The ReferenceSOPInstanceUID
|
||||
* is used to refer to this image in any accompanying DICOM-SR documents.
|
||||
*
|
||||
* @param instance
|
||||
* @returns {String} The ReferenceSOPInstanceUID
|
||||
*/
|
||||
function getSourceImageInstanceUid(instance) {
|
||||
// TODO= Parse the whole Source Image Sequence
|
||||
// This is a really poor workaround for now.
|
||||
// Later we should probably parse the whole sequence.
|
||||
const SourceImageSequence = instance[0x00082112];
|
||||
if (SourceImageSequence && SourceImageSequence.length) {
|
||||
return SourceImageSequence[0][0x00081155];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses result data from a DIMSE search into Study MetaData
|
||||
* Returns an object populated with study metadata, including the
|
||||
* series list.
|
||||
*
|
||||
* @param studyInstanceUid
|
||||
* @param resultData
|
||||
* @returns {{seriesList: Array, patientName: *, patientId: *, accessionNumber: *, studyDate: *, modalities: *, studyDescription: *, imageCount: *, studyInstanceUid: *}}
|
||||
*/
|
||||
function resultDataToStudyMetadata(studyInstanceUid, resultData) {
|
||||
OHIF.log.info('resultDataToStudyMetadata');
|
||||
const seriesMap = {};
|
||||
const seriesList = [];
|
||||
|
||||
if (!resultData.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const anInstance = resultData[0].toObject();
|
||||
if (!anInstance) {
|
||||
return;
|
||||
}
|
||||
|
||||
const studyData = {
|
||||
seriesList: seriesList,
|
||||
patientName: anInstance[0x00100010],
|
||||
patientId: anInstance[0x00100020],
|
||||
patientBirthDate: anInstance[0x00100030],
|
||||
patientSex: anInstance[0x00100040],
|
||||
accessionNumber: anInstance[0x00080050],
|
||||
studyDate: anInstance[0x00080020],
|
||||
modalities: anInstance[0x00080061],
|
||||
studyDescription: anInstance[0x00081030],
|
||||
imageCount: anInstance[0x00201208],
|
||||
studyInstanceUid: anInstance[0x0020000D],
|
||||
institutionName: anInstance[0x00080080]
|
||||
};
|
||||
|
||||
resultData.forEach(function(instanceRaw) {
|
||||
const instance = instanceRaw.toObject();
|
||||
const seriesInstanceUid = instance[0x0020000E];
|
||||
let series = seriesMap[seriesInstanceUid];
|
||||
if (!series) {
|
||||
series = {
|
||||
seriesDescription: instance[0x0008103E],
|
||||
modality: instance[0x00080060],
|
||||
seriesInstanceUid: seriesInstanceUid,
|
||||
seriesNumber: parseFloat(instance[0x00200011]),
|
||||
instances: []
|
||||
};
|
||||
seriesMap[seriesInstanceUid] = series;
|
||||
seriesList.push(series);
|
||||
}
|
||||
|
||||
const sopInstanceUid = instance[0x00080018];
|
||||
|
||||
const instanceSummary = {
|
||||
imageType: instance[0x00080008],
|
||||
sopClassUid: instance[0x00080016],
|
||||
modality: instance[0x00080060],
|
||||
sopInstanceUid: sopInstanceUid,
|
||||
instanceNumber: parseFloat(instance[0x00200013]),
|
||||
imagePositionPatient: instance[0x00200032],
|
||||
imageOrientationPatient: instance[0x00200037],
|
||||
frameOfReferenceUID: instance[0x00200052],
|
||||
sliceThickness: parseFloat(instance[0x00180050]),
|
||||
sliceLocation: parseFloat(instance[0x00201041]),
|
||||
tablePosition: parseFloat(instance[0x00189327]),
|
||||
samplesPerPixel: parseFloat(instance[0x00280002]),
|
||||
photometricInterpretation: instance[0x00280004],
|
||||
planarConfiguration: parseFloat(instance[0x00280006]),
|
||||
rows: parseFloat(instance[0x00280010]),
|
||||
columns: parseFloat(instance[0x00280011]),
|
||||
pixelSpacing: instance[0x00280030],
|
||||
bitsAllocated: parseFloat(instance[0x00280100]),
|
||||
bitsStored: parseFloat(instance[0x00280101]),
|
||||
highBit: parseFloat(instance[0x00280102]),
|
||||
pixelRepresentation: parseFloat(instance[0x00280103]),
|
||||
windowCenter: instance[0x00281050],
|
||||
windowWidth: instance[0x00281051],
|
||||
rescaleIntercept: parseFloat(instance[0x00281052]),
|
||||
rescaleSlope: parseFloat(instance[0x00281053]),
|
||||
sourceImageInstanceUid: getSourceImageInstanceUid(instance),
|
||||
laterality: instance[0x00200062],
|
||||
viewPosition: instance[0x00185101],
|
||||
acquisitionDateTime: instance[0x0008002A],
|
||||
numberOfFrames: parseFloat(instance[0x00280008]),
|
||||
frameIncrementPointer: getValue(instance[0x00280009]),
|
||||
frameTime: parseFloat(instance[0x00181063]),
|
||||
frameTimeVector: parseFloatArray(instance[0x00181065]),
|
||||
lossyImageCompression: instance[0x00282110],
|
||||
derivationDescription: instance[0x00282111],
|
||||
lossyImageCompressionRatio: instance[0x00282112],
|
||||
lossyImageCompressionMethod: instance[0x00282114],
|
||||
spacingBetweenSlices: instance[0x00180088],
|
||||
echoNumber: instance[0x00180086],
|
||||
contrastBolusAgent: instance[0x00180010]
|
||||
};
|
||||
|
||||
// Retrieve the actual data over WADO-URI
|
||||
const server = OHIF.servers.getCurrentServer();
|
||||
const wadouri = `${server.wadoUriRoot}?requestType=WADO&studyUID=${studyInstanceUid}&seriesUID=${seriesInstanceUid}&objectUID=${sopInstanceUid}&contentType=application%2Fdicom`;
|
||||
instanceSummary.wadouri = WADOProxy.convertURL(wadouri, server);
|
||||
|
||||
series.instances.push(instanceSummary);
|
||||
});
|
||||
|
||||
studyData.studyInstanceUid = studyInstanceUid;
|
||||
|
||||
return studyData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieved Study MetaData from a DICOM server using DIMSE
|
||||
* @param studyInstanceUid
|
||||
* @returns {{seriesList: Array, patientName: *, patientId: *, accessionNumber: *, studyDate: *, modalities: *, studyDescription: *, imageCount: *, studyInstanceUid: *}}
|
||||
*/
|
||||
Services.DIMSE.RetrieveMetadata = function(studyInstanceUid) {
|
||||
// TODO: Check which peer it should point to
|
||||
const activeServer = OHIF.servers.getCurrentServer().peers[0];
|
||||
const supportsInstanceRetrievalByStudyUid = activeServer.supportsInstanceRetrievalByStudyUid;
|
||||
let results;
|
||||
|
||||
// Check explicitly for a value of false, since this property
|
||||
// may be left undefined in config files
|
||||
if (supportsInstanceRetrievalByStudyUid === false) {
|
||||
results = DIMSE.retrieveInstancesByStudyOnly(studyInstanceUid);
|
||||
} else {
|
||||
results = DIMSE.retrieveInstances(studyInstanceUid);
|
||||
}
|
||||
|
||||
return resultDataToStudyMetadata(studyInstanceUid, results);
|
||||
};
|
||||
@@ -1,11 +1,13 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
import { CurrentServer } from 'meteor/ohif:servers/both/collections';
|
||||
|
||||
const setupDIMSE = () => {
|
||||
// Terminate existing DIMSE servers and sockets and clean up the connection object
|
||||
DIMSE.connection.reset();
|
||||
|
||||
// Get the new server configuration
|
||||
const server = getCurrentServer();
|
||||
const server = OHIF.servers.getCurrentServer();
|
||||
|
||||
// Stop here if the new server is not of DIMSE type
|
||||
if (server.type !== 'dimse') {
|
||||
@@ -15,16 +17,16 @@ const setupDIMSE = () => {
|
||||
// Check if peers were defined in the server configuration and throw an error if not
|
||||
const peers = server.peers;
|
||||
if (!peers || !peers.length) {
|
||||
console.error('dimse-config: ' + 'No DIMSE Peers provided.');
|
||||
OHIF.log.error('dimse-config: ' + 'No DIMSE Peers provided.');
|
||||
throw new Meteor.Error('dimse-config', 'No DIMSE Peers provided.');
|
||||
}
|
||||
|
||||
// Add all the DIMSE peers, establishing the connections
|
||||
console.log('Adding DIMSE peers');
|
||||
OHIF.log.info('Adding DIMSE peers');
|
||||
try {
|
||||
peers.forEach(peer => DIMSE.connection.addPeer(peer));
|
||||
} catch(error) {
|
||||
console.error('dimse-addPeers: ' + error);
|
||||
OHIF.log.error('dimse-addPeers: ' + error);
|
||||
throw new Meteor.Error('dimse-addPeers', error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
// import { check } from 'meteor/check';
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
import { ServerConfiguration } from 'meteor/ohif:study-list/both/schema/servers.js';
|
||||
|
||||
Meteor.startup(() => {
|
||||
// Save custom properties (if any)...
|
||||
// "Meteor.settings" and "Meteor.settings.public" are set by default...
|
||||
let custom = {
|
||||
private: Meteor.settings.custom,
|
||||
public: Meteor.settings.public.custom
|
||||
};
|
||||
|
||||
// ... and remove them to prevent clean up
|
||||
delete Meteor.settings.custom;
|
||||
delete Meteor.settings.public.custom;
|
||||
|
||||
ServerConfiguration.clean(Meteor.settings);
|
||||
|
||||
// TODO: Make the error messages more clear
|
||||
// Taking this out for now to prevent confusion.
|
||||
// check(Meteor.settings, ServerConfiguration);
|
||||
|
||||
Meteor.settings.custom = custom.private;
|
||||
Meteor.settings.public.custom = custom.public;
|
||||
|
||||
OHIF.log.info(JSON.stringify(Meteor.settings, null, 2));
|
||||
});
|
||||
Reference in new issue
Block a user