{%extends "base.html"%} {%from "flash_messages.html" import flash_all_messages%} {%from "upload_progress_indicator.html" import upload_progress_indicator%} {%block title%}Upload R/qtl2 Bundle{%endblock%} {%block contents%} {%macro rqtl2_file_help()%} <span class="form-text text-muted"> <p> Provide a valid R/qtl2 zip file here. In particular, ensure your zip bundle contains exactly one control file and the corresponding files mentioned in the control file. </p> <p> The control file can be either a YAML or JSON file. <em>ALL</em> other data files in the zip bundle should be CSV files. </p> <p>See the <a href="https://kbroman.org/qtl2/assets/vignettes/input_files.html" target="_blank"> R/qtl2 file format specifications </a> for more details. </p> </span> {%endmacro%} {{upload_progress_indicator()}} <div id="resumable-file-display-template" class="panel panel-info" style="display: none"> <div class="panel-heading"></div> <div class="panel-body"></div> </div> <h2 class="heading">Upload R/qtl2 Bundle</h2> <div id="resumable-drop-area" style="display:none;background:#eeeeee;min-height:12em;border-radius:0.5em;padding:1em;"> <p> <a id="resumable-browse-button" href="#" class="btn btn-info">Browse</a> </p> <p class="form-text text-muted"> You can drag and drop your file here, or click the browse button. Click on the file to remove it. </p> {{rqtl2_file_help()}} <div id="resumable-selected-files" style="display:flex;flex-direction:row;flex-wrap: wrap;justify-content:space-around;gap:10px 20px;"></div> <div id="resumable-class-buttons" style="text-align: right;"> <button id="resumable-upload-button" class="btn btn-primary" style="display: none">start upload</button> <button id="resumable-cancel-upload-button" class="btn btn-danger" style="display: none">cancel upload</button> </div> <div id="resumable-progress-bar" class="progress" style="display: none"> <div class="progress-bar" role="progress-bar" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100" style="width: 0%;"> Uploading: 60% </div> </div> </div> <form id="frm-upload-rqtl2-bundle" action="{{url_for('expression-data.rqtl2.upload_rqtl2_bundle', species_id=species.SpeciesId, population_id=population.InbredSetId)}}" method="POST" enctype="multipart/form-data" data-resumable-target="{{url_for( 'expression-data.rqtl2.upload_rqtl2_bundle_chunked_post', species_id=species.SpeciesId, population_id=population.InbredSetId)}}"> <input type="hidden" name="species_id" value="{{species.SpeciesId}}" /> <input type="hidden" name="population_id" value="{{population.InbredSetId}}" /> {{flash_all_messages()}} <div class="form-group"> <legend class="heading">file upload</legend> <label for="file-rqtl2-bundle" class="form-label">R/qtl2 bundle</label> <input type="file" id="file-rqtl2-bundle" name="rqtl2_bundle_file" accept="application/zip, .zip" required="required" class="form-control" /> {{rqtl2_file_help()}} </div> <button type="submit" class="btn btn-primary" data-toggle="modal" data-target="#upload-progress-indicator">upload R/qtl2 bundle</button> </form> {%endblock%} {%block javascript%} <script src="{{url_for('base.node_modules', filename='resumablejs/resumable.js')}}"></script> <script type="text/javascript" src="/static/js/upload_progress.js"></script> <script type="text/javascript"> function readBinaryFile(file) { return new Promise((resolve, reject) => { var _reader = new FileReader(); _reader.onload = (event) => {resolve(_reader.result);}; _reader.readAsArrayBuffer(file); }); } function computeFileChecksum(file) { return readBinaryFile(file) .then((content) => { return window.crypto.subtle.digest( "SHA-256", new Uint8Array(content)); }).then((digest) => { return Uint8ArrayToHex(new Uint8Array(digest)) }); } function Uint8ArrayToHex(arr) { var toHex = (val) => { _hex = val.toString(16); if(_hex.length < 2) { return "0" + val; } return _hex; }; _hexstr = "" arr.forEach((val) => {_hexstr += toHex(val)}); return _hexstr } var r = Resumable({ target: $("#frm-upload-rqtl2-bundle").attr("data-resumable-target"), fileType: ["zip"], maxFiles: 1, forceChunkSize: true, generateUniqueIdentifier: (file, event) => { return computeFileChecksum(file).then((checksum) => { var _relativePath = (file.webkitRelativePath || file.relativePath || file.fileName || file.name); return checksum + "-" + _relativePath.replace( /[^a-zA-Z0-9_-]/img, ""); }); } }); if(r.support) { //Hide form and display drag&drop UI $("#frm-upload-rqtl2-bundle").css("display", "none"); $("#resumable-drop-area").css("display", "block"); // Define UI elements for browse and drag&drop r.assignDrop(document.getElementById("resumable-drop-area")); r.assignBrowse(document.getElementById("resumable-browse-button")); // Event handlers function display_files(files) { displayArea = $("#resumable-selected-files") displayArea.empty(); files.forEach((file) => { var displayElement = $( "#resumable-file-display-template").clone(); displayElement.removeAttr("id"); displayElement.css("display", ""); displayElement.find(".panel-heading").text(file.fileName); list = $("<ul></ul>"); list.append($("<li><strong>Name</strong>: " + (file.name || file.fileName || file.relativePath || file.webkitRelativePath) + "</li>")); list.append($("<li><strong>Size</strong>: " + (file.size / (1024*1024)).toFixed(2) + " MB</li>")); list.append($("<li><strong>Unique Identifier</strong>: " + file.uniqueIdentifier + "</li>")); list.append($("<li><strong>Mime</strong>: " + file.file.type + "</li>")); displayElement.find(".panel-body").append(list); displayElement.appendTo("#resumable-selected-files"); }); } r.on("filesAdded", function(files) { display_files(files); $("#resumable-upload-button").css("display", ""); $("#resumable-upload-button").on("click", (event) => { r.upload(); }); }); r.on("uploadStart", (event) => { $("#resumable-upload-button").css("display", "none"); $("#resumable-cancel-upload-button").css("display", ""); $("#resumable-cancel-upload-button").on("click", (event) => { r.files.forEach((file) => { if(file.isUploading()) { file.abort(); } }); $("#resumable-cancel-upload-button").css("display", "none"); $("#resumable-upload-button").on("click", (event) => { r.files.forEach((file) => {file.retry();}); }); $("#resumable-upload-button").css("display", ""); }); }); r.on("progress", () => { var progress = (r.progress() * 100).toFixed(2); var pbar = $("#resumable-progress-bar > .progress-bar"); $("#resumable-progress-bar").css("display", ""); pbar.css("width", progress+"%"); pbar.attr("aria-valuenow", progress); pbar.text("Uploading: " + progress + "%"); }) r.on("fileSuccess", (file, message) => { if(message != "OK") { var uri = (window.location.protocol + "//" + window.location.host + message); window.location.replace(uri); } }); r.on("error", (message, file) => { filename = (file.webkitRelativePath || file.relativePath || file.fileName || file.name); jsonmsg = JSON.parse(message); alert("There was an error while uploading your file '" + filename + "'. The error message was:\n\n\t" + jsonmsg.error + " (" + jsonmsg.statuscode + "): " + jsonmsg.message); }) } else { setup_upload_handlers( "frm-upload-rqtl2-bundle", make_data_uploader( function (form) { var formdata = new FormData(); formdata.append( "species_id", form.querySelector('input[name="species_id"]').value); formdata.append( "population_id", form.querySelector('input[name="population_id"]').value); formdata.append( "rqtl2_bundle_file", form.querySelector("#file-rqtl2-bundle").files[0]); return formdata; })); } </script> {%endblock%}