diff --git a/frontend/app/components/panel/manage-genotype.hbs b/frontend/app/components/panel/manage-genotype.hbs index 46f8419b..fd220d77 100644 --- a/frontend/app/components/panel/manage-genotype.hbs +++ b/frontend/app/components/panel/manage-genotype.hbs @@ -345,12 +345,20 @@
{{#if this.vcfExportText.length}} - {{/if}} + + +
+ {{/tab.pane}} {{/let}} {{/let}} diff --git a/frontend/app/components/panel/manage-genotype.js b/frontend/app/components/panel/manage-genotype.js index 9a52594f..5c4f4db9 100644 --- a/frontend/app/components/panel/manage-genotype.js +++ b/frontend/app/components/panel/manage-genotype.js @@ -21,6 +21,7 @@ import NamesFilters from '../../utils/data/names-filters'; import { toPromiseProxy, toArrayPromiseProxy, addObjectArrays, arrayClear } from '../../utils/ember-devel'; import { thenOrNow, contentOf, pollCondition, promiseThrottle } from '../../utils/common/promises'; import { responseTextParseHtml } from '../../utils/domElements'; +import { fileDownloadBlob, fileDownloadAsCSV, text2Gzip } from '../../utils/dom/file-download'; import { clipboard_writeText } from '../../utils/common/html'; import { arrayChoose } from '../../utils/common/arrays'; import { intervalSize } from '../../utils/interval-calcs'; @@ -183,6 +184,7 @@ function featureHasSamplesLoaded(feature) { * .replaceResults default: false * .showResultText default: false + * .compressVCF default : true * .showConfigureLookup default: false * .showSampleFilters default : false @@ -475,6 +477,9 @@ export default class PanelManageGenotypeComponent extends Component { if (userSettings.showResultText === undefined) { userSettings.showResultText = false; } + if (userSettings.compressVCF === undefined) { + userSettings.compressVCF = true; + } if (userSettings.showConfigureLookup === undefined) { userSettings.showConfigureLookup = true; } @@ -3155,6 +3160,11 @@ export default class PanelManageGenotypeComponent extends Component { //---------------------------------------------------------------------------- + /** + * @return a file base name for VCF Download of displayed data of this.lookupBlock. + * The base name does not include the extension, because either + * '.vcf.txt' or '.vcf.gz' may be used + */ @computed( 'lookupDatasetId', 'lookupScope', 'vcfGenotypeLookupDomain', 'vcfGenotypeSamplesSelected', 'requestFormat') @@ -3168,8 +3178,7 @@ export default class PanelManageGenotypeComponent extends Component { '_' + scope + '_' + domainText + '_' + this.requestFormat + - '_' + samplesLength + - '.vcf' ; + '_' + samplesLength; return fileName; } @@ -3954,6 +3963,27 @@ export default class PanelManageGenotypeComponent extends Component { return combinedP; } + /** Export as a user file download a VCF file constructed from .headerTextP, + * .vcfGenotypeText via combineHeader() + * + * The exported MIME Type used is 'text/csv' because + * 'text/tsv'is would be interpreted as Vcard, refn + * https://github.com/samtools/hts-specs/issues/407 + * https://developer.mozilla.org/en-US/docs/Web/HTTP/MIME_types/Common_types + */ + vcfExportTextToFile() { + this.vcfExportTextP.then(combined => { + const data = combined.join('\n'); + if (this.args.userSettings.compressVCF) { + const blobP = text2Gzip(data, 'application/gzip'); + blobP.then(blob => + fileDownloadBlob(this.vcfExportFileName + '.vcf.gz', blob, 'application/gzip')); + } else { + fileDownloadAsCSV(this.vcfExportFileName + '.vcf.txt', data, 'text/plain'); + } + }); + } + combineHeader(headerText, vcfGenotypeText) { /** remove trailing \n, so that split does not create a trailing empty line. */ headerText = headerText.trim().split('\n'); diff --git a/frontend/app/utils/dom/file-download.js b/frontend/app/utils/dom/file-download.js index 991ef582..3380b24c 100644 --- a/frontend/app/utils/dom/file-download.js +++ b/frontend/app/utils/dom/file-download.js @@ -1,22 +1,28 @@ //------------------------------------------------------------------------------ /* global Blob */ +/* global removeEventListener */ +/* global document */ +/* global URL */ +/* global CompressionStream */ +/* global Response */ //------------------------------------------------------------------------------ -export {fileDownloadAsCSV} -/** Trigger a download of a CSV / TSV file containing the given CSV / TSV string +export {fileDownloadBlob, fileDownloadAsCSV} +/** Trigger a download of a file containing the given data blob. * - * Based on a post on discuss.emberjs.com by skaterdav85, May 2018 : + * fileDownloadBlob() and fileDownloadAsCSV() are partly based on a post on discuss.emberjs.com by skaterdav85, May 2018 : * https://discuss.emberjs.com/t/whats-the-best-strategy-for-letting-users-download-ember-data-returns-as-csvs/14767/2 + * + * @param filename string + * @param blob */ -function fileDownloadAsCSV(filename, contents) { +function fileDownloadBlob(filename, blob) { const { document, URL, removeEventListener } = window; const anchor = document.createElement('a'); anchor.download = filename; - const url = anchor.href = URL.createObjectURL(new Blob([contents], { - type: 'text/csv' - })); + const url = anchor.href = URL.createObjectURL(blob); //---------------------------------------------------------------------------- @@ -31,6 +37,16 @@ function fileDownloadAsCSV(filename, contents) { anchor.click(); URL.revokeObjectURL(url); anchor.remove(); + +} +/** Trigger a download of a CSV / TSV file containing the given CSV / TSV string + * + * @param filename string + * @param type default is 'text/csv' + */ +function fileDownloadAsCSV(filename, contents, type = 'text/csv') { + const blob = new Blob([contents], { type }); + fileDownloadBlob(filename, blob); } //------------------------------------------------------------------------------ @@ -64,3 +80,18 @@ function exportAsCSVFile(fileName, data, keyArray, columnHeaders, quoteIfNeeded) } //------------------------------------------------------------------------------ + +export { text2Gzip } +/** Compress the given string, and return content for a .gz file download + * @return promise yielding a blob, of gzip data + */ +function text2Gzip(contents, type = 'text/csv') { + const + stream = new CompressionStream("gzip"), + blob = new Blob([contents], { type }), + compressedStream = blob.stream().pipeThrough(stream), + promise = new Response(compressedStream).blob(); + + return promise; + +}