6a5052fcb442fafa62ef6800d34246a7ba631891
chmalee
  Thu Nov 14 12:55:15 2024 -0800
Make file type no longer selectable, just infer from the extension and warn (correctly now) if the file type is not supported. Remove the file type batch select. TODO: add a hub name batch change and make the batch change update correctly on change

diff --git src/hg/js/hgMyData.js src/hg/js/hgMyData.js
index e4003ce..9c6ccb4 100644
--- src/hg/js/hgMyData.js
+++ src/hg/js/hgMyData.js
@@ -6,40 +6,48 @@
     if (!num) {return "n/a";}
     if (num < (1000 * 1024)) {
         return `${(num/1000).toFixed(1)}kb`;
     } else if (num < (1000 * 1000 * 1024)) {
         return `${((num/1000)/1000).toFixed(1)}mb`;
     } else {
         return `${(((num/1000)/1000)/1000).toFixed(1)}gb`;
     }
 }
 
 // make our Uppy instance:
 const uppy = new Uppy.Uppy({
     debug: true,
     onBeforeUpload: (files) => {
         // set all the fileTypes and genomes from their selects
+        let doUpload = true;
         for (let [key, file] of Object.entries(files)) {
-            if (!file.meta.genome || !file.meta.fileType) {
-                uppy.getPlugin("Dashboard").info("error!");
+            if (!file.meta.genome) {
+                uppy.info(`Error: No genome selected for file ${file.name}!`, 'error', 2000);
+                doUpload = false;
+                continue;
+            } else if  (!file.meta.fileType) {
+                uppy.info(`Error: File type not supported, please rename file: ${file.name}!`, 'error', 2000);
+                doUpload = false;
+                continue;
             }
             uppy.setFileMeta(file.id, {
                 fileName: file.name,
                 fileSize: file.size,
                 lastModified: file.data.lastModified,
             });
         }
+        return doUpload;
     },
 });
 
 var hubCreate = (function() {
     let uiState = { // our object for keeping track of the current UI and what to do
         toUpload: {}, // set of file objects keyed by name
         input: null, // the hidden input element
         pickedList: null, // the <div> for displaying files in toUpload
         pendingQueue: [], // our queue of pending [tus.Upload, file], kind of like the toUpload object
         fileList: [], // the files this user has uploaded, initially populated by the server
                         // on page load, but gets updated as the user uploades/deletes files
         hubList: [], // the hubs this user has created/uploaded, initially populated by server
                         // on page load, but gets updated as the user creates/deletes hubs
         userUrl: "", // the web accesible path where the uploads are stored for this user
     };
@@ -167,44 +175,49 @@
 
     let extensionMap = {
         "bigBed": [".bb", ".bigbed"],
         "bam": [".bam"],
         "vcf": [".vcf"],
         "vcfTabix": [".vcf.gz", "vcf.bgz"],
         "bigWig": [".bw", ".bigwig"],
         "hic": [".hic"],
         "cram": [".cram"],
         "bigBarChart": [".bigbarchart"],
         "bigGenePred": [".bgp", ".biggenepred"],
         "bigMaf": [".bigmaf"],
         "bigInteract": [".biginteract"],
         "bigPsl": [".bigpsl"],
         "bigChain": [".bigchain"],
+        "bamIndex": [".bam.bai", ".bai"],
+        "tabixIndex": [".vcf.gz.tbi", "vcf.bgz.tbi"],
     };
 
     function detectFileType(fileName) {
         let fileLower = fileName.toLowerCase();
         for (let fileType in extensionMap) {
             for (let extIx in extensionMap[fileType]) {
                 let ext = extensionMap[fileType][extIx];
                 if (fileLower.endsWith(ext)) {
                     return fileType;
                 }
             }
         }
-        //TODO: raise an error
-        alert(`file extension for ${fileName} not found, please explicitly select it`);
+        //we could alert here but instead just explicitly set the value to null
+        //and let the backend reject it instead, forcing the user to rename their
+        //file
+        //alert(`file extension for ${fileName} not found, please explicitly select it`);
+        return null;
     }
 
     /*
     function submitPickedFiles() {
 
         let tusdServer = getTusdEndpoint();
 
         let onBeforeRequest = function(req) {
             let xhr = req.getUnderlyingObject(req);
             xhr.withCredentials = true;
         };
 
         let onSuccess = function(req, metadata) {
             // remove the selected file from the input element and the ul list
             // FileList is a read only setting, so we have to make
@@ -863,59 +876,61 @@
 
             addSelectsForFile(file) {
                 /* create two selects for the file object, to include the db and type */
                 const id = "uppy_" + file.id;
                 let fileDiv = document.getElementById(id);
                 // this might not exist yet depending on where we are in the render cycle
                 if (fileDiv) {
                     let dbSelectId = "db_select_" + file.id;
                     if (!document.getElementById(dbSelectId)) {
                         let dbSelect = document.createElement("select");
                         dbSelect.id = dbSelectId;
                         let dbOpts = makeGenomeSelectOptions();
                         this.createOptsForSelect(dbSelect, dbOpts);
                         fileDiv.appendChild(dbSelect);
                     }
+                    /*
                     let typeSelectId = "type_select_" + file.id;
                     if (!document.getElementById(typeSelectId)) {
                         let typeSelect = document.createElement("select");
                         typeSelect.id = typeSelectId;
                         let typeOpts = makeTypeSelectOptions();
                         this.createOptsForSelect(typeSelect, typeOpts);
                         fileDiv.appendChild(typeSelect);
                     }
+                    */
                 }
             }
 
             removeBatchSelectsFromDashboard() {
                 let batchSelectDiv = document.getElementById("batch-selector-div");
                 if (batchSelectDiv) {
                     batchSelectDiv.remove();
                 }
             }
 
             addBatchSelectsToDashboard() {
                 if (!document.getElementById("batch-selector-div")) {
                     let batchSelectDiv = document.createElement("div");
                     batchSelectDiv.id = "batch-selector-div";
                     let batchDbSelect = document.createElement("select");
-                    let batchTypeSelect = document.createElement("select");
+                    //let batchTypeSelect = document.createElement("select");
                     this.createOptsForSelect(batchDbSelect, makeGenomeSelectOptions());
-                    this.createOptsForSelect(batchTypeSelect, makeTypeSelectOptions());
+                    //this.createOptsForSelect(batchTypeSelect, makeTypeSelectOptions());
                     batchSelectDiv.textContent = "Change options for all files";
                     batchSelectDiv.appendChild(batchDbSelect);
-                    batchSelectDiv.appendChild(batchTypeSelect);
+                    //batchSelectDiv.appendChild(batchTypeSelect);
                     batchSelectDiv.style.display = "flex";
                     batchSelectDiv.style.justifyContent = "center";
                     if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
                         batchSelectDiv.style.color = "#eaeaea";
                     }
                     // append the batch changes to the bottom of the file list, for some reason
                     // I can't append to the actual Dashboard-files, it must be getting emptied
                     // and re-rendered or something
                     let uppyFilesDiv = document.querySelector(".uppy-Dashboard-progressindicators");
                     if (uppyFilesDiv) {
                         uppyFilesDiv.insertBefore(batchSelectDiv, uppyFilesDiv.firstChild);
                     }
                 }
             }
 
@@ -951,77 +966,95 @@
                 });
             }
             uninstall() {
                 // not really used because we aren't ever uninstalling the uppy instance
                 this.uppy.off("file-added");
             }
         }
         let uppyOptions = {
             //target: "#filePickerModal", // this seems nice but then the jquery css interferes with
                                           // the uppy css
             trigger: ".uploadButton",
             showProgressDetails: true,
             note: "Example text in the note field",
             meta: {"genome": null, "fileType": null},
             metaFields: (file) => {
-                const fields = [{id: 'name', name: 'File name'}];
-                fields.push({
+                const fields = [{
+                    id: 'name',
+                    name: 'File name',
+                    render: ({value, onChange, required, form}, h) => {
+                        return h('input',
+                            {type: "text",
+                            value: value,
+                            onChange: e => {
+                                onChange(e.target.value);
+                                file.meta.fileType = detectFileType(e.target.value);
+                            },
+                            required: required,
+                            form: form,
+                            }
+                        );
+                    },
+                },
+                {
                     id: 'genome',
                     name: 'Genome',
                     render: ({value, onChange}, h) => {
                         return h('select',
                             {onChange: e => onChange(e.target.value)}, 
                             makeGenomeSelectOptions().map( (genomeObj) => {
                                 return h('option', genomeObj, genomeObj.label);
                             })
                         );
                     },
-                });
+                },
+                /*
                 fields.push({
                     id: 'fileType',
                     name: 'File Type',
                     render: ({value, onChange}, h) => {
                         return h( 'select',
                             {onChange: e => {
                                 if (e.target.value === "Auto-detect from extension") {
                                     onChange(detectFileType(file.name));
                                 } else {
                                     onChange(e.target.value);
                                 }
                             }},
                             makeTypeSelectOptions().map( (typeObj) => {
                                 return h('option', typeObj, typeObj.label);
                             })
                         );
                     },
                 });
-                fields.push({
+                */
+                {
                     id: 'parentDir',
                     name: 'Hub Name',
                     render: ({value, onChange, required, form}, h) => {
                         return h('input',
                             {type: 'text',
                              required: required,
                              form: form,
                              value: hubNameDefault,
                             }
                         );
                     },
-                });
+                }];
                 return fields;
             },
-            restricted: {requiredMetaFields: ["genome", "fileType"]},
+            restricted: {requiredMetaFields: ["genome"]},
             closeModalOnClickOutside: true,
             closeAfterFinish: true,
             theme: 'auto',
             autoOpen: "metaEditor",
         };
         let tusOptions = {
             endpoint: getTusdEndpoint(),
             withCredentials: true,
             retryDelays: null,
         };
         uppy.use(Uppy.Dashboard, uppyOptions);
         uppy.use(Uppy.Tus, tusOptions);
         uppy.use(BatchChangePlugin, {target: Uppy.Dashboard});
         uppy.on('upload-success', (file, response) => {
             const metadata = file.meta;