[WIP] Moving ViewerStudies collection to TypeSafeCollection implementation + Fixing sorting bug in findAllBy method in TypeSafeCollection
This commit is contained in:
parent
a4e7aa86ab
commit
d25141f336
@ -64,13 +64,16 @@ Template.viewer.onCreated(() => {
|
||||
// Set lesion tool buttons as disabled if pixel spacing is not available for active element
|
||||
instance.autorun(OHIF.lesiontracker.pixelSpacingAutorunCheck);
|
||||
|
||||
// Update the ViewerStudies collection with the loaded studies
|
||||
ViewerStudies.remove({});
|
||||
// @TypeSafeStudies
|
||||
debugger;
|
||||
|
||||
// Update the OHIF.viewer.Studies collection with the loaded studies
|
||||
OHIF.viewer.Studies.removeAll();
|
||||
|
||||
instance.data.studies.forEach(study => {
|
||||
study.selected = true;
|
||||
study.displaySets = OHIF.viewerbase.createStacks(study);
|
||||
ViewerStudies.insert(study);
|
||||
OHIF.viewer.Studies.insert(study);
|
||||
});
|
||||
|
||||
instance.data.timepointApi = new OHIF.measurements.TimepointApi(instance.data.currentTimepointId);
|
||||
|
||||
@ -48,8 +48,11 @@ Template.viewer.onCreated(() => {
|
||||
|
||||
Session.set('activeViewport', ViewerData[contentId].activeViewport || 0);
|
||||
|
||||
// Update the ViewerStudies collection with the loaded studies
|
||||
ViewerStudies.remove({});
|
||||
// @TypeSafeStudies
|
||||
debugger;
|
||||
|
||||
// Update the OHIF.viewer.Studies collection with the loaded studies
|
||||
OHIF.viewer.Studies.removeAll();
|
||||
|
||||
ViewerData[contentId].studyInstanceUids = [];
|
||||
instance.data.studies.forEach(study => {
|
||||
@ -57,7 +60,7 @@ Template.viewer.onCreated(() => {
|
||||
study.displaySets = OHIF.viewerbase.createStacks(study);
|
||||
// const studyMetadata = new OHIF.metadata.StudyMetadata(study);
|
||||
// study.displaySets = OHIF.viewerbase.sortingManager.getDisplaySets(studyMetadata);
|
||||
ViewerStudies.insert(study);
|
||||
OHIF.viewer.Studies.insert(study);
|
||||
ViewerData[contentId].studyInstanceUids.push(study.studyInstanceUid);
|
||||
});
|
||||
|
||||
|
||||
@ -135,16 +135,22 @@ function getActiveViewportImageId() {
|
||||
}
|
||||
|
||||
function getAbstractPriorValue(imageId) {
|
||||
const currentStudy = ViewerStudies.findOne({}, {
|
||||
sort: {
|
||||
studyDate: -1
|
||||
},
|
||||
limit: 1
|
||||
// @TypeSafeStudies
|
||||
debugger;
|
||||
// @TODO: Implement MIN and MAX methods in TypeSafeCollection
|
||||
// const currentStudy = ViewerStudies.findOne({}, {
|
||||
// sort: {
|
||||
// studyDate: -1
|
||||
// },
|
||||
// limit: 1
|
||||
// });
|
||||
const allStudies = OHIF.viewer.Studies.all({
|
||||
sort: [ ['studyDate', 'desc'] ]
|
||||
});
|
||||
|
||||
if (!currentStudy) {
|
||||
if (allStudies.length < 1) {
|
||||
return;
|
||||
}
|
||||
const currentStudy = allStudies[0];
|
||||
|
||||
const priorStudy = cornerstoneTools.metaData.get('study', imageId);
|
||||
if (!priorStudy) {
|
||||
|
||||
@ -403,7 +403,10 @@ HP.ProtocolEngine = class ProtocolEngine {
|
||||
return;
|
||||
}
|
||||
|
||||
var alreadyLoaded = ViewerStudies.findOne({
|
||||
// @TypeSafeStudies
|
||||
debugger;
|
||||
|
||||
var alreadyLoaded = OHIF.viewer.Studies.findBy({
|
||||
studyInstanceUid: priorStudy.studyInstanceUid
|
||||
});
|
||||
|
||||
@ -411,7 +414,7 @@ HP.ProtocolEngine = class ProtocolEngine {
|
||||
getStudyMetadata(priorStudy.studyInstanceUid, study => {
|
||||
study.abstractPriorValue = abstractPriorValue;
|
||||
study.displaySets = OHIF.viewerbase.sortingManager.getDisplaySets(study);
|
||||
ViewerStudies.insert(study);
|
||||
OHIF.viewer.Studies.insert(study);
|
||||
this.studies.push(study);
|
||||
this.matchImages(viewport);
|
||||
this.updateViewports();
|
||||
|
||||
@ -170,7 +170,7 @@ const FrameLevelMeasurement = new SimpleSchema([
|
||||
min: 0,
|
||||
label: 'Frame index in Instance'
|
||||
},
|
||||
// TODO: In the future we should remove this in favour of searching ViewerStudies and display sets when
|
||||
// TODO: In the future we should remove this in favour of searching OHIF.viewer.Studies and display sets when
|
||||
// re-displaying measurements. Otherwise if a study moves servers the measurements will not be displayed correctly
|
||||
imageId: {
|
||||
type: String,
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { $ } from 'meteor/jquery';
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
import 'meteor/ohif:viewerbase';
|
||||
|
||||
function findAndRenderDisplaySet(displaySets, viewportIndex, studyInstanceUid, seriesInstanceUid, sopInstanceUid, renderedCallback) {
|
||||
// Find the proper stack to display
|
||||
@ -29,8 +30,12 @@ function findAndRenderDisplaySet(displaySets, viewportIndex, studyInstanceUid, s
|
||||
}
|
||||
|
||||
function renderIntoViewport(viewportIndex, studyInstanceUid, seriesInstanceUid, sopInstanceUid, renderedCallback) {
|
||||
|
||||
// @TypeSafeStudies
|
||||
debugger;
|
||||
|
||||
// First, check if we already have this study loaded
|
||||
const alreadyLoadedStudy = ViewerStudies.findOne({studyInstanceUid});
|
||||
const alreadyLoadedStudy = OHIF.viewer.Studies.findBy({ studyInstanceUid });
|
||||
|
||||
if (alreadyLoadedStudy) {
|
||||
// If the Study is already loaded, find the display set and render it
|
||||
@ -47,10 +52,12 @@ function renderIntoViewport(viewportIndex, studyInstanceUid, seriesInstanceUid,
|
||||
OHIF.log.warn('renderIntoViewport');
|
||||
|
||||
// Double check to make sure this study wasn't already inserted
|
||||
// into ViewerStudies, so we don't cause duplicate entry errors
|
||||
const loaded = ViewerStudies.findOne(loadedStudy._id);
|
||||
// into OHIF.viewer.Studies, so we don't cause duplicate entry errors
|
||||
const loaded = OHIF.viewer.Studies.findBy({
|
||||
studyInstanceUid: loadedStudy.studyInstanceUid
|
||||
});
|
||||
if (!loaded) {
|
||||
ViewerStudies.insert(loadedStudy);
|
||||
OHIF.viewer.Studies.insert(loadedStudy);
|
||||
}
|
||||
|
||||
findAndRenderDisplaySet(loadedStudy.displaySets, viewportIndex, studyInstanceUid, seriesInstanceUid, sopInstanceUid, renderedCallback)
|
||||
|
||||
@ -1,8 +1,12 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { OHIF } from '../namespace';
|
||||
import { TypeSafeCollection } from './lib/classes/TypeSafeCollection';
|
||||
|
||||
Studies = new TypeSafeCollection();
|
||||
// Create main Studies collection which will be used across the entire viewer...
|
||||
const Studies = new TypeSafeCollection();
|
||||
|
||||
ViewerStudies = new Meteor.Collection(null);
|
||||
ViewerStudies._debugName = 'ViewerStudies';
|
||||
// Make it publicly available on "OHIF.viewer" namespace...
|
||||
OHIF.viewer.Studies = Studies;
|
||||
|
||||
// Subscriptions...
|
||||
Meteor.subscribe('studyImportStatus');
|
||||
|
||||
@ -1,6 +1,11 @@
|
||||
import { Template } from 'meteor/templating';
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
|
||||
Template.studyBrowser.helpers({
|
||||
studies() {
|
||||
return ViewerStudies.find({
|
||||
// @TypeSafeStudies
|
||||
debugger;
|
||||
return OHIF.viewer.Studies.findAllBy({
|
||||
selected: true
|
||||
});
|
||||
}
|
||||
|
||||
@ -585,14 +585,9 @@ Template.imageViewerViewport.onRendered(function() {
|
||||
return;
|
||||
}
|
||||
|
||||
// @TODO: Reconcile ViewerStudies and Studies Collection.
|
||||
// const study = Studies.findBy({ studyInstanceUid });
|
||||
|
||||
// Look through the ViewerStudies collection for a
|
||||
// study with this studyInstanceUid
|
||||
const study = ViewerStudies.findOne({
|
||||
studyInstanceUid: this.data.studyInstanceUid
|
||||
});
|
||||
// @TypeSafeStudies
|
||||
debugger;
|
||||
const study = OHIF.viewer.Studies.findBy({ studyInstanceUid });
|
||||
|
||||
data.study = study;
|
||||
setDisplaySet(data, displaySetInstanceUid, templateData);
|
||||
@ -669,7 +664,9 @@ Template.imageViewerViewport.events({
|
||||
// Set the active viewport as the previous zoomed viewport
|
||||
viewportIndexToZoom = layoutManager.zoomedViewportIndex || 0;
|
||||
}
|
||||
setActiveViewport($('.imageViewerViewport').get(viewportIndexToZoom));
|
||||
// Set zoomed viewport as active...
|
||||
const element = $('.imageViewerViewport').get(viewportIndexToZoom);
|
||||
setActiveViewport(element);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@ -4,7 +4,6 @@ import { Session } from 'meteor/session';
|
||||
import { $ } from 'meteor/jquery';
|
||||
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
import 'meteor/ohif:viewerbase';
|
||||
|
||||
Template.studySeriesQuickSwitch.onCreated(() => {
|
||||
const instance = Template.instance();
|
||||
@ -29,13 +28,16 @@ Template.studySeriesQuickSwitch.onCreated(() => {
|
||||
|
||||
const viewportData = instance.getViewportData(viewportIndex);
|
||||
|
||||
// @TypeSafeStudies
|
||||
debugger;
|
||||
|
||||
if (viewportData) {
|
||||
// Finds the current study and return it
|
||||
instance.study = ViewerStudies.findOne({
|
||||
instance.study = OHIF.viewer.Studies.findBy({
|
||||
studyInstanceUid: viewportData.studyInstanceUid
|
||||
});
|
||||
} else {
|
||||
instance.study = ViewerStudies.findOne();
|
||||
instance.study = OHIF.viewer.Studies.getElementByIndex(0);
|
||||
}
|
||||
|
||||
if (!instance.study) {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { Template } from 'meteor/templating';
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
|
||||
Template.studyTimepoint.onCreated(() => {
|
||||
const instance = Template.instance();
|
||||
@ -39,9 +40,12 @@ Template.studyTimepoint.events({
|
||||
// Defines where will be the studies searched
|
||||
let $studiesTarget = instance.$('.studyTimepoint');
|
||||
|
||||
// @TypeSafeStudies
|
||||
debugger;
|
||||
|
||||
if (changed.isQuickSwitch) {
|
||||
// Changes the current quick switch study
|
||||
const study = ViewerStudies.findOne({
|
||||
const study = OHIF.viewer.Studies.findBy({
|
||||
studyInstanceUid: changed.studyInstanceUid
|
||||
});
|
||||
instance.data.currentStudy.set(study);
|
||||
|
||||
@ -2,7 +2,7 @@ import { Template } from 'meteor/templating';
|
||||
import { ReactiveVar } from 'meteor/reactive-var';
|
||||
import { Tracker } from 'meteor/tracker';
|
||||
import { _ } from 'meteor/underscore';
|
||||
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
import { OHIFError } from '../../../lib/classes/OHIFError';
|
||||
|
||||
Template.studyTimepointBrowser.onCreated(() => {
|
||||
@ -21,8 +21,11 @@ Template.studyTimepointBrowser.onCreated(() => {
|
||||
|
||||
// Get the studies for a specific timepoint
|
||||
instance.getStudies = timepoint => {
|
||||
// @TypeSafeStudies
|
||||
debugger;
|
||||
|
||||
if (!timepoint) {
|
||||
return ViewerStudies.find().fetch();
|
||||
return OHIF.viewer.Studies.all();
|
||||
}
|
||||
|
||||
return timepoint.studyInstanceUids.map(studyInstanceUid => {
|
||||
@ -31,7 +34,7 @@ Template.studyTimepointBrowser.onCreated(() => {
|
||||
studyInstanceUid: studyInstanceUid
|
||||
};
|
||||
|
||||
const loadedStudy = ViewerStudies.findOne(query);
|
||||
const loadedStudy = OHIF.viewer.Studies.findBy(query);
|
||||
if (loadedStudy) {
|
||||
return loadedStudy;
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ import { Template } from 'meteor/templating';
|
||||
import { ReactiveVar } from 'meteor/reactive-var';
|
||||
import { _ } from 'meteor/underscore';
|
||||
import { $ } from 'meteor/jquery';
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
import { sortingManager } from '../../../lib/sortingManager';
|
||||
|
||||
Template.studyTimepointStudy.onCreated(() => {
|
||||
@ -97,10 +98,12 @@ Template.studyTimepointStudy.events({
|
||||
|
||||
const isQuickSwitch = !_.isUndefined(instance.data.viewportIndex);
|
||||
|
||||
// @TypeSafeStudies
|
||||
debugger;
|
||||
// Check if the study already has series data,
|
||||
// and if not, retrieve it.
|
||||
if (!studyData.seriesList) {
|
||||
const alreadyLoaded = ViewerStudies.findOne({ _id });
|
||||
const alreadyLoaded = OHIF.viewer.Studies.findBy({ studyInstanceUid });
|
||||
|
||||
if (!alreadyLoaded) {
|
||||
const $studies = instance.getStudyElement(true);
|
||||
@ -108,12 +111,10 @@ Template.studyTimepointStudy.events({
|
||||
getStudyMetadata(studyInstanceUid, study => {
|
||||
study.displaySets = sortingManager.getDisplaySets(study);
|
||||
instance.data.study = study;
|
||||
ViewerStudies.insert(study, () => {
|
||||
// To make sure studies are rendered in the DOM
|
||||
// use minimongo insert callback
|
||||
$studies.trigger('loadEnded');
|
||||
instance.select(isQuickSwitch);
|
||||
});
|
||||
OHIF.viewer.Studies.insert(study);
|
||||
// make sure studies are rendered in the DOM
|
||||
$studies.trigger('loadEnded');
|
||||
instance.select(isQuickSwitch);
|
||||
});
|
||||
} else {
|
||||
studyData.seriesList = alreadyLoaded.seriesList;
|
||||
@ -126,9 +127,11 @@ Template.studyTimepointStudy.events({
|
||||
|
||||
Template.studyTimepointStudy.helpers({
|
||||
isLoading() {
|
||||
// @TypeSafeStudies
|
||||
debugger;
|
||||
const instance = Template.instance();
|
||||
const studyData = instance.data.study;
|
||||
const alreadyLoaded = ViewerStudies.findOne({ _id: studyData._id });
|
||||
const alreadyLoaded = OHIF.viewer.Studies.findBy({ studyInstanceUid: studyData.studyInstanceUid });
|
||||
return instance.loading.get() && !alreadyLoaded;
|
||||
},
|
||||
modalities() {
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { Template } from 'meteor/templating';
|
||||
import { Session } from 'meteor/session';
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
import { viewportOverlayUtils } from '../../../lib/viewportOverlayUtils';
|
||||
import { getElementIfNotEmpty } from '../../../lib/getElementIfNotEmpty';
|
||||
import { getStackDataIfNotEmpty } from '../../../lib/getStackDataIfNotEmpty';
|
||||
@ -217,17 +218,19 @@ Template.viewportOverlay.helpers({
|
||||
return;
|
||||
}
|
||||
|
||||
// @TypeSafeStudies
|
||||
debugger
|
||||
// Make sure there are more than two studies loaded in the viewer
|
||||
const viewportStudies = ViewerStudies.find();
|
||||
if (viewportStudies.count() < 2) {
|
||||
const viewportStudies = OHIF.viewer.Studies.all();
|
||||
if (viewportStudies.length < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Here we sort the collection in ascending order by study date, so
|
||||
// that we can obtain the oldest study as the first element of the array
|
||||
//
|
||||
// TODO= Find out if we should encode studyDate as a Date in the ViewerStudies Collection
|
||||
const viewportStudiesArray = _.sortBy(viewportStudies.fetch(), function(study) {
|
||||
// TODO= Find out if we should encode studyDate as a Date in the OHIF.viewer.Studies Collection
|
||||
const viewportStudiesArray = _.sortBy(viewportStudies, function(study) {
|
||||
return viewportOverlayUtils.formatDateTime(study.studyDate, study.studyTime);
|
||||
});
|
||||
|
||||
|
||||
@ -1,5 +1,13 @@
|
||||
import { Viewerbase } from '../namespace';
|
||||
|
||||
/**
|
||||
* Imports file with side effects only (files that do not export anything...)
|
||||
*/
|
||||
|
||||
import './collections';
|
||||
import './lib/stackImagePositionOffsetSynchronizer.js';
|
||||
import './lib/debugReactivity';
|
||||
|
||||
/**
|
||||
* Exported Functions
|
||||
*/
|
||||
@ -192,10 +200,3 @@ Viewerbase.TypeSafeCollection = TypeSafeCollection;
|
||||
// OHIFError
|
||||
import { OHIFError } from './lib/classes/OHIFError';
|
||||
Viewerbase.OHIFError = OHIFError;
|
||||
|
||||
/**
|
||||
* Imports for Side Effects Only (Files that do not export anything...)
|
||||
*/
|
||||
|
||||
import './lib/stackImagePositionOffsetSynchronizer.js';
|
||||
import './lib/debugReactivity';
|
||||
|
||||
@ -67,6 +67,21 @@ export class TypeSafeCollection {
|
||||
return propertyValue;
|
||||
}
|
||||
|
||||
// [WIP]...
|
||||
static compareToPropertyMapStrict(propertyMap, object) {
|
||||
const hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
const getPropertyValue = TypeSafeCollection.getPropertyValue;
|
||||
for (let propertyName in propertyMap) {
|
||||
if (hasOwnProperty.call(propertyMap, propertyName)) {
|
||||
const propertyValue = getPropertyValue(object, propertyName);
|
||||
if (propertyMap[propertyName] !== propertyValue) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a sorting specifier is valid.
|
||||
* A valid sorting specifier consists of an array of arrays being each subarray a pair
|
||||
@ -194,7 +209,7 @@ export class TypeSafeCollection {
|
||||
}
|
||||
|
||||
remove(propertyMap) {
|
||||
let found = this.findAllBy(propertyMap),
|
||||
let found = this.findAllEntriesBy(propertyMap),
|
||||
foundCount = found.length,
|
||||
removed = [];
|
||||
if (foundCount > 0) {
|
||||
@ -267,13 +282,11 @@ export class TypeSafeCollection {
|
||||
* Find all elements that match a specified property map.
|
||||
* Attention!!! The reactive source will not be notified if no valid property map is supplied...
|
||||
* @param {Object} propertyMap A property map that will be macthed against all collection elements.
|
||||
* @param {Object} options A set of options. Currently only "options.sort" option is supported.
|
||||
* @param {Object.SortingSpecifier} options.sort An optional sorting specifier. If a sorting specifier is supplied
|
||||
* but it's not valid, an exception will be thrown.
|
||||
* @returns {Array} An array with all elements stored in the collection.
|
||||
* @returns {Array} An array of entries of all elements that match the given criteria. Each set in
|
||||
* in the array has the following format: [ elementData, elementId, elementIndex ].
|
||||
*/
|
||||
findAllBy(propertyMap, options) {
|
||||
let found = [];
|
||||
findAllEntriesBy(propertyMap) {
|
||||
const found = [];
|
||||
if (typeof propertyMap === OBJECT && propertyMap !== null) {
|
||||
const hasOwn = Object.prototype.hasOwnProperty;
|
||||
this._elements().forEach((item, index) => {
|
||||
@ -289,6 +302,20 @@ export class TypeSafeCollection {
|
||||
found.push([ payload, item.id, index ]);
|
||||
});
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all elements that match a specified property map.
|
||||
* Attention!!! The reactive source will not be notified if no valid property map is supplied...
|
||||
* @param {Object} propertyMap A property map that will be macthed against all collection elements.
|
||||
* @param {Object} options A set of options. Currently only "options.sort" option is supported.
|
||||
* @param {Object.SortingSpecifier} options.sort An optional sorting specifier. If a sorting specifier is supplied
|
||||
* but it's not valid, an exception will be thrown.
|
||||
* @returns {Array} An array with all elements that match the given criteria and sorted in the specified sorting order.
|
||||
*/
|
||||
findAllBy(propertyMap, options) {
|
||||
const found = this.findAllEntriesBy(propertyMap).map(item => item[0]); // Only payload is relevant...
|
||||
if (options instanceof Object && 'sort' in options) {
|
||||
TypeSafeCollection.sortListBy(found, options.sort);
|
||||
}
|
||||
|
||||
@ -89,9 +89,6 @@ Package.onUse(function(api) {
|
||||
});
|
||||
api.addFiles('client/compatibility/dialogPolyfill.styl', 'client');
|
||||
|
||||
// ---------- Collections ----------
|
||||
api.addFiles('client/collections.js', 'client');
|
||||
|
||||
// ---------- Components ----------
|
||||
|
||||
// Basic components
|
||||
@ -201,11 +198,6 @@ Package.onUse(function(api) {
|
||||
api.addFiles('client/components/viewer/studyTimepointBrowser/studyTimepointStudy.styl', 'client');
|
||||
api.addFiles('client/components/viewer/studyTimepointBrowser/studyTimepointStudy.js', 'client');
|
||||
|
||||
// Library functions
|
||||
|
||||
// Collections
|
||||
api.export('ViewerStudies', 'client');
|
||||
|
||||
api.export('dialogPolyfill', 'client');
|
||||
|
||||
api.mainModule('main.js', 'client');
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { OHIF } from 'meteor/ohif:core';
|
||||
import 'meteor/ohif:viewerbase';
|
||||
|
||||
OHIF.viewer = OHIF.viewer || {};
|
||||
|
||||
@ -42,14 +43,16 @@ Template.viewer.onCreated(() => {
|
||||
|
||||
Session.set('activeViewport', ViewerData[contentId].activeViewport || 0);
|
||||
|
||||
// Update the ViewerStudies collection with the loaded studies
|
||||
ViewerStudies.remove({});
|
||||
// @TypeSafeStudies
|
||||
debugger;
|
||||
// Update the OHIF.viewer.Studies collection with the loaded studies
|
||||
OHIF.viewer.Studies.removeAll();
|
||||
|
||||
ViewerData[contentId].studyInstanceUids = [];
|
||||
instance.data.studies.forEach(study => {
|
||||
study.selected = true;
|
||||
study.displaySets = createStacks(study);
|
||||
ViewerStudies.insert(study);
|
||||
OHIF.viewer.Studies.insert(study);
|
||||
ViewerData[contentId].studyInstanceUids.push(study.studyInstanceUid);
|
||||
});
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user