a213555a50875b9a04c3bb4a267a54bea2dd427f
hiram
  Thu Aug 29 14:02:39 2024 -0700
now with proper interface to assembly status, refSeq category, assembly level refs #32596

diff --git src/hg/js/assemblySearch.js src/hg/js/assemblySearch.js
index bd3cd90..37b1fc1 100644
--- src/hg/js/assemblySearch.js
+++ src/hg/js/assemblySearch.js
@@ -1,19 +1,19 @@
 // global variables:
 
-var debug = false;
-var measureTiming = false;
+var debug = true;
+var measureTiming = true;
 var urlParams;
 var query = "";
 var maxItemsOutput = 500;
 var asmIdText = null;
 // adjust default here and in assemblySearch.html
 var browserExist = "mayExist";
 var betterCommonName = null;
 var comment = null;
 var stateObject = {};	// maintain page state
 var requestSubmitButton = null;
 var completedAsmId = new Map();	// keep track of requests completed
 				// so they won't be repeated
 
 // This function is called on DOMContentLoaded as the initialization
 //  procedure for first time page draw
@@ -69,101 +69,124 @@
     }
 
     // default starts as hidden
     stateObject.advancedSearchVisible = false;
     var searchForm = document.getElementById('searchForm');
     var advancedSearchButton = document.getElementById('advancedSearchButton');
     var searchInput = document.getElementById('searchBox');
     var clearButton = document.getElementById('clearSearch');
     asmIdText = document.getElementById("formAsmId");
     betterCommonName = document.getElementById("betterCommonName");
     comment = document.getElementById("comment");
     requestSubmitButton = document.getElementById("submitButton");
 
     document.getElementById("modalFeedback").addEventListener("submit", checkForm, false);
     modalInit();
+    var tableBody = document.getElementById('tableBody');
+    tableBody.innerHTML = '<tr><td style="text-align:center;" colspan=8><b>(empty table)</b></td></tr>';
 
     clearButton.addEventListener('click', function() {
         searchInput.value = ''; // Clear the search input field
     });
 
     searchForm.addEventListener('submit', function(event) {
         event.preventDefault(); // Prevent form submission
 
         var searchTerm = document.getElementById('searchBox').value;
         var resultCountLimit = document.getElementById('maxItemsOutput');
         var mustExist = document.getElementById('mustExist').checked;
         var notExist = document.getElementById('notExist').checked;
         browserExist = "mustExist";
         if (mustExist && notExist) {
            browserExist = "mayExist";
         } else if (notExist) {
            browserExist = "notExist";
         }
-        var wordMatch = document.querySelector('input[name="wordMatch"]:checked').value;
-        makeRequest(searchTerm, browserExist, resultCountLimit.value, wordMatch);
+        makeRequest(searchTerm, browserExist, resultCountLimit.value);
     });
 
     advancedSearchButton.addEventListener('click', function() {
        var searchOptions = document.getElementById("advancedSearchOptions");
        // I don't know why it is false the first time ?
        if (! searchOptions.style.display || searchOptions.style.display === "none") {
           advancedSearchVisible(true);
        } else {
           advancedSearchVisible(false);
        }
     });
 
     // restore history on back button
     window.addEventListener('popstate', function(e) {
        var state = event.state;
        if (state) {
           stateObject.queryString = state.queryString;
           stateObject.maxItemsOutput = state.maxItemsOutput;
           stateObject.browser = state.browser;
           stateObject.debug = state.debug;
           stateObject.measureTiming = state.measureTiming;
           stateObject.wordMatch = state.wordMatch;
+          stateObject.asmStatus = state.asmStatus;
+          stateObject.refSeqCategory = state.refSeqCategory;
+          stateObject.asmLevel = state.asmLevel;
           stateObject.jsonData = state.jsonData;
           document.getElementById('mustExist').checked = false;
           document.getElementById('notExist').checked = false;
           if (stateObject.browser === "mustExist") {
              document.getElementById('mustExist').checked = true;
           }
           if (stateObject.browser === "notExist") {
              document.getElementById('notExist').checked = true;
           }
           if (stateObject.browser === "mayExist") {
              document.getElementById('mustExist').checked = true;
              document.getElementById('notExist').checked = true;
           }
           advancedSearchVisible(stateObject.advancedSearchVisible);
           if (stateObject.wordMatch === "allWords") {
              document.getElementById("allWords").checked = true;
           } else {
              document.getElementById("anyWord").checked = true;
           }
-          if (stateObject.refSeqLatest) {
-             document.getElementById('refSeqLatest').checked = true;
-          } else {
-             document.getElementById('refSeqLatest').checked = false;
-          }
-          if (stateObject.refSeqRepresentative) {
+          // only one of these four cases will be true
+          if (stateObject.asmStatus === "statusAny")
+             document.getElementById('statusAny').checked = true;
+          if (stateObject.asmStatus === "latest")
+             document.getElementById('statusLatest').checked = true;
+          if (stateObject.asmStatus === "replaced")
+             document.getElementById('statusReplaced').checked = true;
+          if (stateObject.asmStatus === "suppressed")
+             document.getElementById('statusSuppressed').checked = true;
+
+          // only one of these three cases will be true
+          if (stateObject.refSeqCategory === "refSeqAny")
+             document.getElementById('refSeqAny').checked = true;
+          if (stateObject.refSeqCategory === "reference")
+             document.getElementById('refSeqReference').checked = true;
+          if (stateObject.refSeqCategory === "representative")
              document.getElementById('refSeqRepresentative').checked = true;
-          } else {
-             document.getElementById('refSeqRepresentative').checked = false;
-          }
+
+          // only one of these five cases will be true
+          if (stateObject.asmLevel === "asmLevelAny")
+             document.getElementById('asmLevelAny').checked = true;
+          if (stateObject.asmLevel === "complete")
+             document.getElementById('asmLevelComplete').checked = true;
+          if (stateObject.asmLevel === "chromosome")
+             document.getElementById('asmLevelChromosome').checked = true;
+          if (stateObject.asmLevel === "scaffold")
+             document.getElementById('asmLevelScaffold').checked = true;
+          if (stateObject.asmLevel === "contig")
+             document.getElementById('asmLevelContig').checked = true;
 
           document.getElementById('searchBox').value = stateObject.queryString;
 	  populateTableAndInfo(JSON.parse(stateObject.jsonData));
 //          alert("state: '" + JSON.stringify(stateObject) + "'");
        }
     });
 
     var tableHeader = document.getElementById('tableHeader');
     headerRefresh(tableHeader);
 
     if (urlParams.has('maxItemsOutput')) {
        maxItemsOutput = parseInt(urlParams.get('maxItemsOutput'), 10);
        if (maxItemsOutput < 1) {
          maxItemsOutput = 1;
        } else if (maxItemsOutput > 1000) {
@@ -181,32 +204,34 @@
     document.getElementById("measureTiming").style.display = "none";
 });
 
 // refresh the thead columns in the specified table
 function headerRefresh(tableHead) {
   // clear existing content
   tableHead.innerHTML = '';
   // re-populate header row - the sortable system added a class to
   //  the last sorted column, need to rebuild the headerRow to get the
   //  header back to pristine condition for the next sort
   var headerRow = '<tr>';
   headerRow += '<th><div class=tooltip>view/<br>request &#9432;<span onclick="event.stopPropagation()" class="tooltiptext"><em>"view"</em> opens the genome browser for an existing assembly, <em>"request"</em> opens an assembly request form.</span></div></th>';
   headerRow += '<th><div class="tooltip">English common name &#9432;<span onclick="event.stopPropagation()" class="tooltiptext">English common name</span></div></th>';
   headerRow += '<th><div class="tooltip">scientific name &#9432;<span onclick="event.stopPropagation()" class="tooltiptext">scientific name</span></div></th>';
   headerRow += '<th><div class="tooltip">NCBI Assembly &#9432;<span onclick="event.stopPropagation()" class="tooltiptext">Links to NCBI resource record<br>or UCSC downloads for local UCSC assemblies</span></div></th>';
+  headerRow += '<th><div class="tooltip">year &#9432;<span onclick="event.stopPropagation()" class="tooltiptextright">Year assembly was released.</span></div></th>';
   headerRow += '<th><div class="tooltip"><em>genark</em> clade &#9432;<span onclick="event.stopPropagation()" class="tooltiptextright">clade specification as found in the GenArk system.</span></div></th>';
   headerRow += '<th><div class="tooltip">description &#9432;<span onclick="event.stopPropagation()" class="tooltiptextright">other meta data for this assembly.</span></div></th>';
+  headerRow += '<th><div class="tooltip">status &#9432;<span onclick="event.stopPropagation()" class="tooltiptextright">various other status</span></div></th>';
   headerRow += '</tr>';
   tableHead.innerHTML = headerRow;
 }
 
 // call with visible true to make visible, false to hide
 function advancedSearchVisible(visible) {
   var advancedSearchButton = document.getElementById("advancedSearchButton");
   var searchOptions = document.getElementById("advancedSearchOptions");
   if (visible) {
     searchOptions.style.display = "flex";
     advancedSearchButton.textContent = "hide advanced search options";
     stateObject.advancedSearchVisible = true;
   } else {
     searchOptions.style.display = "none";
     advancedSearchButton.textContent = "show advanced search options";
@@ -249,32 +274,50 @@
         if (genomicEntries[id].browserExists) {
           if (id.startsWith("GC")) {
             browserUrl = "<a href='/h/" + id + "?position=lastDbPos' target=_blank>view</a>";
             asmInfoUrl = "<a href='https://www.ncbi.nlm.nih.gov/assembly/" + id + "' target=_blank>" + id + "</a>";
           } else {
             browserUrl = "<a href='/cgi-bin/hgTracks?db=" + id + "' target=_blank>view</a>";
             asmInfoUrl = "<a href='https://hgdownload.soe.ucsc.edu/goldenPath/" + id + "/bigZips/' target=_blank>" + id + "</a>";
           }
           dataRow += "<th>" + browserUrl + "</th>";
         } else {
           dataRow += "<th><button type=button' onclick='asmOpenModal(this)' name=" + id + "'>request</button></th>";
         }
         dataRow += "<td>" + genomicEntries[id].commonName + "</td>";
         dataRow += "<td>" + genomicEntries[id].scientificName + "</td>";
         dataRow += "<th>" + asmInfoUrl + "</th>";
+        dataRow += "<td>" + genomicEntries[id].year + "</td>";
         dataRow += "<td>" + genomicEntries[id].clade + "</td>";
         dataRow += "<td>" + genomicEntries[id].description + "</td>";
+        var status =  "<td>" + genomicEntries[id].priority + " ";
+        var hardSpace = "&nbsp;";
+        if (genomicEntries[id].refSeqCategory) {
+           status += " " + genomicEntries[id].refSeqCategory;
+           hardSpace = "";
+        }
+        if (genomicEntries[id].versionStatus) {
+           status += " " + genomicEntries[id].versionStatus;
+           hardSpace = "";
+        }
+        if (genomicEntries[id].assemblyLevel) {
+           status += " " + genomicEntries[id].assemblyLevel;
+           hardSpace = "";
+        }
+        status += "</td>";
+//        status += hardSpace + "</td>";
+        dataRow += status;
         dataRow += '</tr>';
         tableBody.innerHTML += dataRow;
     }
     var dataTable = document.getElementById('dataTable');
     sorttable.makeSortable(dataTable);
 
     var itemCount = parseInt(extraInfo.itemCount, 10);
     var totalMatchCount = parseInt(extraInfo.totalMatchCount, 10);
     var availableAssemblies = parseInt(extraInfo.availableAssemblies, 10);
 
     var resultCounts = "<em>results for search string: </em><b>'" + extraInfo.q + "'</b>, ";
     if ( itemCount === totalMatchCount ) {
       resultCounts += "<em>showing </em><b>" + itemCount.toLocaleString() + "</b> <em>match results</em>, ";
     } else {
       resultCounts += "<em>showing </em><b>" + itemCount.toLocaleString() + "</b> <em>match results</em> ";
@@ -504,115 +547,118 @@
     requestSubmitButton.disabled = false;
     var overflow = modalWindow.offsetHeight - document.documentElement.clientHeight;
     if (overflow > 0) {
         modalWindow.style.maxHeight = (parseInt(window.getComputedStyle(modalWindow).height) - overflow) + "px";
     }
     modalWindow.style.marginTop = (-modalWindow.offsetHeight)/2 + "px";
     modalWindow.style.marginLeft = (-modalWindow.offsetWidth)/2 + "px";
   }
   if (e.preventDefault) {
     e.preventDefault();
   } else {
     e.returnValue = false;
   }
 }
 
-function makeRequest(query, browserExist, resultLimit, wordMatch) {
+function makeRequest(query, browserExist, resultLimit) {
     // Disable the submit button
     disableButtons();
+    var wordMatch = document.querySelector('input[name="wordMatch"]:checked').value;
+    var asmStatus = document.querySelector('input[name="asmStatus"]:checked').value;
+    var refSeqCategory = document.querySelector('input[name="refSeqCategory"]:checked').value;
+    var asmLevel = document.querySelector('input[name="asmLevel"]:checked').value;
+
+    // start with what the user requested:
     var queryString = query;
-    // if 'latest' requested, add '+latest' if not already in query
-    if (document.getElementById('refSeqLatest').checked) {
-       if (! /latest/.test(queryString) ) {
-         queryString += " +latest";
-       }
-    }
-    if (document.getElementById('refSeqRepresentative').checked) {
-       if (! /representative/.test(queryString) ) {
-         queryString += " +representative";
-       }
-    }
+
     // for allWords, place + sign in front of each word if not already there
     if (wordMatch === "allWords") {
       var words = queryString.split(/\s+/);
       if (words.length > 1) {	// not needed on only one word
         var queryPlus = "";	// compose new query string
         words.forEach(function(word) {
           if (word.startsWith("+")) {
             queryPlus += " " + word; // space separates each word
           } else {
             queryPlus += " +" + word;
           }
         });
         queryString = queryPlus.trim();
       }
     }
     // remove stray white space from beginning or end
     queryString = queryString.trim();
 
     // Show the wait spinner
     document.querySelector(".submitContainer").classList.add("loading");
     document.getElementById("loadingSpinner").style.display = "block";
 
     var xhr = new XMLHttpRequest();
     var urlPrefix = "/cgi-bin/hubApi";
     var url = "/findGenome?q=" + encodeURIComponent(queryString);
     url += ";browser=" + browserExist;
     url += ";maxItemsOutput=" + resultLimit;
+    if (asmStatus !== "statusAny")	// default is any assembly status
+       url += ";status=" + asmStatus;	// something specific is being requested
+    if (refSeqCategory !== "refSeqAny")	// default is any RefSeq category
+       url += ";category=" + refSeqCategory;	// something specific
+    if (asmLevel !== "asmLevelAny")	// default is any level of assembly
+       url += ";level=" + asmLevel;	// something specific
 
     var historyUrl = "?q=" + encodeURIComponent(queryString) + ";browser=" + browserExist + ";maxItemsOutput=" + resultLimit;
     if (debug) {
        historyUrl += ";debug=1";
     }
     if (measureTiming) {
        historyUrl += ";measureTiming=1";
     }
 
     if (debug) {
       var apiUrl = "<a href='" + urlPrefix + url + "' target=_blank>" + url + "</a>";
       document.getElementById("recentAjax").innerHTML = apiUrl;
     }
     stateObject.queryString = queryString;
     stateObject.maxItemsOutput = maxItemsOutput;
     stateObject.browser = browserExist;
     stateObject.debug = debug;
     stateObject.measureTiming = measureTiming;
     stateObject.wordMatch = wordMatch;
-    stateObject.refSeqLatest = document.getElementById('refSeqLatest').checked;
-    stateObject.refSeqRepresentative = document.getElementById('refSeqRepresentative').checked;
+    stateObject.asmStatus = asmStatus;
+    stateObject.refSeqCategory = refSeqCategory;
+    stateObject.asmLevel = asmLevel;
 
     xhr.open('GET', urlPrefix + url, true);
 
     xhr.onload = function() {
         if (xhr.status === 200) {
             // Hide the wait spinner once the AJAX request is complete
             document.querySelector(".submitContainer").classList.remove("loading");
             document.getElementById("loadingSpinner").style.display = "none";
             enableButtons();
 
             stateObject.jsonData = xhr.responseText;
             history.pushState(stateObject, '', historyUrl);
 
             var data = JSON.parse(xhr.responseText);
 	    populateTableAndInfo(data);
         } else {
             // Hide the wait spinner once the AJAX request is complete
             document.querySelector(".submitContainer").classList.remove("loading");
             document.getElementById("loadingSpinner").style.display = "none";
             enableButtons();
 	    var tableBody = document.getElementById('tableBody');
+            tableBody.innerHTML = "<tr><td style='text-align:center;' colspan=8><b>no results found for query: <em>'" + queryString + "'</em></b></td></tr>";
             var metaData = document.getElementById('metaData');
-            tableBody.innerHTML = '';
             metaData.innerHTML = '';
-            metaData.innerHTML = "<b>no results found for query: '" + queryString + "'</b>";
+//            metaData.innerHTML = "<b>no results found for query: '" + queryString + "'</b>";
             document.getElementById('resultCounts').innerHTML = "";
             document.getElementById('elapsedTime').innerHTML = "0";
         }
     };
 
     xhr.onerror = function() {
         alert('console.log("Request failed")');
         console.log('Request failed');
     };
 
     xhr.send();
 }