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 ⓘ<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 ⓘ<span onclick="event.stopPropagation()" class="tooltiptext">English common name</span></div></th>'; headerRow += '<th><div class="tooltip">scientific name ⓘ<span onclick="event.stopPropagation()" class="tooltiptext">scientific name</span></div></th>'; headerRow += '<th><div class="tooltip">NCBI Assembly ⓘ<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 ⓘ<span onclick="event.stopPropagation()" class="tooltiptextright">Year assembly was released.</span></div></th>'; headerRow += '<th><div class="tooltip"><em>genark</em> clade ⓘ<span onclick="event.stopPropagation()" class="tooltiptextright">clade specification as found in the GenArk system.</span></div></th>'; headerRow += '<th><div class="tooltip">description ⓘ<span onclick="event.stopPropagation()" class="tooltiptextright">other meta data for this assembly.</span></div></th>'; + headerRow += '<th><div class="tooltip">status ⓘ<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 = " "; + 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(); }