From efa236f4cd9de12d58cd39b38a1db94767aed24c Mon Sep 17 00:00:00 2001 From: Evren Ozkan Date: Tue, 23 Feb 2016 11:53:49 -0500 Subject: [PATCH] LT-73 Export selected studies from study list to local - Query to get metadata of the selected studies to export - Download and zip the queried dicom files - Show and update the progress dialog while querying and exporting studies --- LesionTracker/.meteor/packages | 1 + LesionTracker/.meteor/versions | 1 + .../lesionTrackerWorklistContextMenu.html | 4 +- .../lesionTrackerWorklistContextMenu.js | 35 +++++++ Packages/lesiontracker/lib/exportStudies.js | 97 +++++++++++++++++++ Packages/lesiontracker/package.js | 3 + 6 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 Packages/lesiontracker/lib/exportStudies.js diff --git a/LesionTracker/.meteor/packages b/LesionTracker/.meteor/packages index 0ce8afcad..a4152e136 100644 --- a/LesionTracker/.meteor/packages +++ b/LesionTracker/.meteor/packages @@ -51,3 +51,4 @@ check aldeed:template-extension@4.0.0 zuuk:stale-session gilbertwat:bootstrap3-daterangepicker +silentcicero:jszip diff --git a/LesionTracker/.meteor/versions b/LesionTracker/.meteor/versions index 3743c3e28..939b745a5 100644 --- a/LesionTracker/.meteor/versions +++ b/LesionTracker/.meteor/versions @@ -97,6 +97,7 @@ rwatts:uuid@0.0.2 service-configuration@1.0.5 session@1.1.1 sha@1.0.4 +silentcicero:jszip@0.0.4 spacebars@1.0.7 spacebars-compiler@1.0.7 srp@1.0.4 diff --git a/Packages/lesiontracker/client/components/lesionTrackerWorklistContextMenu/lesionTrackerWorklistContextMenu.html b/Packages/lesiontracker/client/components/lesionTrackerWorklistContextMenu/lesionTrackerWorklistContextMenu.html index a94b076bc..1d989ae4f 100644 --- a/Packages/lesiontracker/client/components/lesionTrackerWorklistContextMenu/lesionTrackerWorklistContextMenu.html +++ b/Packages/lesiontracker/client/components/lesionTrackerWorklistContextMenu/lesionTrackerWorklistContextMenu.html @@ -24,7 +24,9 @@ Delete Send - Export + + Export Download View Series Details diff --git a/Packages/lesiontracker/client/components/lesionTrackerWorklistContextMenu/lesionTrackerWorklistContextMenu.js b/Packages/lesiontracker/client/components/lesionTrackerWorklistContextMenu/lesionTrackerWorklistContextMenu.js index c199ce98f..82e296c16 100644 --- a/Packages/lesiontracker/client/components/lesionTrackerWorklistContextMenu/lesionTrackerWorklistContextMenu.js +++ b/Packages/lesiontracker/client/components/lesionTrackerWorklistContextMenu/lesionTrackerWorklistContextMenu.js @@ -5,6 +5,7 @@ var defaultTemplate = 'studyContextMenu'; Template.lesionTrackerWorklistContextMenu.replaces(defaultTemplate); Worklist.functions['removeTimepointAssociations'] = removeTimepointAssociations; +Worklist.functions['exportSelectedStudies'] = exportSelectedStudies; /** * Removes all present study / timepoint associations from the Clinical Trial @@ -72,4 +73,38 @@ function removeTimepointAssociations() { }); } }); +} + +/** + * Exports all selected studies on the worklist + */ +function exportSelectedStudies() { + var selectedStudies = WorklistSelectedStudies.find({}, { + sort: { + studyDate: 1 + } + }).fetch() || []; + + if (selectedStudies.length < 1) { + return; + } + + var studiesToExport = []; + var numberOfStudiesToQuery = selectedStudies.length; + + progressDialog.show("Querying Studies...", numberOfStudiesToQuery); + + selectedStudies.forEach(function(selectedStudy) { + getStudyMetadata(selectedStudy.studyInstanceUid, function(study) { + studiesToExport.push(study); + + var numberOfStudiesQueried = studiesToExport.length; + + progressDialog.update(numberOfStudiesQueried); + + if (numberOfStudiesQueried === numberOfStudiesToQuery) { + exportStudies(studiesToExport); + } + }); + }); } \ No newline at end of file diff --git a/Packages/lesiontracker/lib/exportStudies.js b/Packages/lesiontracker/lib/exportStudies.js new file mode 100644 index 000000000..f5fb1b377 --- /dev/null +++ b/Packages/lesiontracker/lib/exportStudies.js @@ -0,0 +1,97 @@ +/** + * Exports requested studies + * @param studiesToExport Studies to export + */ +exportStudies = function(studiesToExport) { + var numberOfFilesToExport = getNumberOfFilesToExport(studiesToExport); + + progressDialog.show("Exporting Studies...", numberOfFilesToExport); + + try { + exportStudiesInternal(studiesToExport, numberOfFilesToExport); + } catch (err) { + progressDialog.close(); + console.error("Failed to export studies: " + err.message); + } +}; + +function exportStudiesInternal(studiesToExport, numberOfFilesToExport) { + var zip = new JSZip(); + + var exportFailed = false; + var numberOfFilesExported = 0; + + studiesToExport.forEach(function(study) { + sortStudy(study); + + var studyFolder = zip.folder(study.studyInstanceUid); + + study.seriesList.forEach(function(series) { + var seriesFolder = studyFolder.folder(series.seriesInstanceUid); + + series.instances.forEach(function(instance) { + if (!instance.wadouri) { + return; + } + + // If failed to download a dicom file, skip others + if (exportFailed) { + return; + } + + // Download and Zip the dicom file + var xhr = new XMLHttpRequest(); + xhr.open("GET", instance.wadouri, true); + xhr.responseType = "arraybuffer"; + + // Downloaded the dicom file completely + xhr.onload = function() { + // If failed to download a dicom file, skip others + if (exportFailed) { + return; + } + + var responseArrayBuffer = xhr.response; + if (responseArrayBuffer) { + seriesFolder.file(instance.sopInstanceUid + ".dcm", responseArrayBuffer, { binary: true }); + } + + numberOfFilesExported++; + + if (numberOfFilesExported === numberOfFilesToExport) { + var zipContent = zip.generate({ type: "blob" }); + saveAs(zipContent, "studies.zip"); + } + + progressDialog.update(numberOfFilesExported); + }; + + // Failed to download the dicom file + xhr.onerror = function() { + exportFailed = true; + + progressDialog.close(); + console.error("Failed to export studies!"); + }; + + xhr.send(null); + }); + }); + }); +} + +function getNumberOfFilesToExport(studiesToExport) { + var numberOFFilesToExport = 0; + + studiesToExport.forEach(function(study) { + study.seriesList.forEach(function(series) { + series.instances.forEach(function(instance) { + if (instance.wadouri) { + numberOFFilesToExport++; + } + }); + }); + }); + + return numberOFFilesToExport; +} diff --git a/Packages/lesiontracker/package.js b/Packages/lesiontracker/package.js index a11146119..72ca75ad9 100644 --- a/Packages/lesiontracker/package.js +++ b/Packages/lesiontracker/package.js @@ -169,6 +169,8 @@ Package.onUse(function(api) { api.addFiles('lib/handleMeasurementModified.js', 'client'); api.addFiles('lib/handleMeasurementRemoved.js', 'client'); + api.addFiles('lib/exportStudies.js', 'client'); + // Export global functions api.export('pixelSpacingAutorunCheck', 'client'); @@ -191,6 +193,7 @@ Package.onUse(function(api) { api.export('getTrialCriteriaConstraints', 'client'); api.export('calculateTotalLesionBurden', 'client'); api.export('convertToNonTarget', 'client'); + api.export('exportStudies', 'client'); // Export global objects api.export('TrialResponseCriteria', 'client');