From e9c44756fec20707ae2bb070cebf2e00f8e8757d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guilhem=20Semp=C3=A9r=C3=A9?= Date: Fri, 28 Jun 2024 09:36:23 +0200 Subject: [PATCH] Provided means to (1) name individual groups, (2) select DB by taxon --- .../controller/gigwa/GigwaRestController.java | 13 ++-- src/main/webapp/index.jsp | 58 ++++++++++++++---- src/main/webapp/js/charts.js | 2 +- src/main/webapp/js/main.js | 59 ++++++++++++++++--- src/main/webapp/navbar.jsp | 3 + src/main/webapp/summaryTable.jsp | 15 +++-- 6 files changed, 120 insertions(+), 30 deletions(-) diff --git a/src/main/java/fr/cirad/web/controller/gigwa/GigwaRestController.java b/src/main/java/fr/cirad/web/controller/gigwa/GigwaRestController.java index 4db11392..53007ae4 100644 --- a/src/main/java/fr/cirad/web/controller/gigwa/GigwaRestController.java +++ b/src/main/java/fr/cirad/web/controller/gigwa/GigwaRestController.java @@ -948,8 +948,9 @@ public void run() { List> callsetIds = gr.getAllCallSetIds(); for (int i = 0; i < callsetIds.size(); i++) try { - individualsByPop.put("Group" + (i+1), callsetIds.get(i).isEmpty() ? MgdbDao.getProjectIndividuals(info[0], projId) /* no selection means all selected */ : callsetIds.get(i).stream().map(csi -> csi.substring(1 + csi.lastIndexOf(Helper.ID_SEPARATOR))).collect(Collectors.toSet())); - annotationFieldThresholdsByPop.put("Group" + (i+1), gr.getAnnotationFieldThresholds(i)); + String groupName = gr.getGroupName(i); + individualsByPop.put(groupName, callsetIds.get(i).isEmpty() ? MgdbDao.getProjectIndividuals(info[0], projId) /* no selection means all selected */ : callsetIds.get(i).stream().map(csi -> csi.substring(1 + csi.lastIndexOf(Helper.ID_SEPARATOR))).collect(Collectors.toSet())); + annotationFieldThresholdsByPop.put(groupName, gr.getAnnotationFieldThresholds(i)); } catch (ObjectNotFoundException neverWillHappen) // module existence has been checked above {} @@ -2731,7 +2732,7 @@ public List searchableGenesLookup( @ApiOperation(authorizations = { @Authorization(value = "AuthorizationToken") }, value = INSTANCE_CONTENT_SUMMARY) @GetMapping(value = BASE_URL + INSTANCE_CONTENT_SUMMARY, produces = "application/json") - public @ResponseBody Map getAllDatabaseInfo(HttpServletRequest request, HttpServletResponse response) throws AvroRemoteException { + public @ResponseBody Map getAllDatabaseInfo(HttpServletRequest request, HttpServletResponse response) throws AvroRemoteException { SearchReferenceSetsResponse accessibleDBs = ga4ghController.searchReferenceSets(request, new SearchReferenceSetsRequest()); Iterator dbIterator = accessibleDBs.getReferenceSets().iterator(); @@ -2739,13 +2740,14 @@ public List searchableGenesLookup( int i = 1; while (dbIterator.hasNext()) { String dbName = dbIterator.next().getName(); + MongoTemplate mongoTemplate = MongoTemplateManager.get(dbName); Map resultObject = new LinkedHashMap(); resultObject.put("database", dbName); resultObject.put("individuals", Helper.estimDocCount(dbName, Individual.class)); resultObject.put("markers", Helper.estimDocCount(dbName, VariantData.class)); - resultObject.put("samples", Helper.estimDocCount(dbName, GenotypingSample.class)); + resultObject.put("taxon", Helper.nullToEmptyString(MongoTemplateManager.getTaxonName(dbName))); Query query = new Query(); query.fields().include(GenotypingProject.FIELDNAME_PLOIDY_LEVEL) @@ -2762,7 +2764,8 @@ public List searchableGenesLookup( pj.put("description", project.getDescription()); pj.put("variantType", project.getVariantTypes()); pj.put("ploidy", project.getPloidyLevel()); - pj.put("runNumber", project.getRuns().size()); + pj.put("samples", mongoTemplate.count(new Query(Criteria.where(GenotypingSample.FIELDNAME_PROJECT_ID).is(project.getId())), GenotypingSample.class)); + pj.put("runs", project.getRuns()); resultObject.put("Project" + j, pj); j++; } diff --git a/src/main/webapp/index.jsp b/src/main/webapp/index.jsp index 069137bf..f17a1261 100644 --- a/src/main/webapp/index.jsp +++ b/src/main/webapp/index.jsp @@ -141,7 +141,7 @@ https://doi.org/10.1093/gigascience/giz051
-
Sequences
+
Sequences
@@ -529,7 +529,7 @@ https://doi.org/10.1093/gigascience/giz051 -
@@ -733,6 +733,7 @@ https://doi.org/10.1093/gigascience/giz051 var referenceNames; var exportedIndividualCount = 0; var indOpt = []; + var databasesByTaxon = {}; $.ajaxSetup({cache: false}); @@ -758,8 +759,12 @@ https://doi.org/10.1093/gigascience/giz051 referenceset = $(this).val(); - if (!loadProjects(referenceset)) + if (referenceset == "" || !loadProjects(referenceset)) { + $("div.alert").hide(); + $("div#searchPanel").fadeOut(); + $("div#welcome").fadeIn(); return; + } $("div#welcome").hide(); @@ -927,7 +932,7 @@ https://doi.org/10.1093/gigascience/giz051 }); $('#Sequences').on('multiple_select_change', function() { var nCount = $('#Sequences').selectmultiple('count'); - $('#sequencesLabel').html("Sequences (" + (nCount == 0 ? seqCount : nCount) + "/" + seqCount + ")"); + $('#sequencesLabel span').text((nCount == 0 ? seqCount : nCount) + "/" + seqCount + ""); }); $('#displayAllGt').on('change', function() { loadGenotypes(true); @@ -1060,18 +1065,49 @@ https://doi.org/10.1093/gigascience/giz051 "pageToken": null }), success: function(jsonResult) { - var option = ""; + var dbListOptions = ""; for (var set in jsonResult.referenceSets) { - option += ''; + let taxon = null; + jsonResult.referenceSets[set].description.split(" ; ").forEach(function(descItem) { + if (descItem.startsWith("Species:")) + taxon = descItem.substring("Species:".length).trim(); + else if (descItem.startsWith("Taxon:")) + taxon = descItem.substring("Taxon:".length).trim(); + + }); + if (taxon == null) + taxon = "(Unspecified taxon)"; + if (databasesByTaxon[taxon] == null) + databasesByTaxon[taxon] = []; + databasesByTaxon[taxon].push(jsonResult.referenceSets[set].name); + + dbListOptions += ''; } - $('#module').html(option).selectpicker('refresh'); + + let sortedTaxa = Object.keys(databasesByTaxon); + sortedTaxa.sort(); + if (Object.keys(databasesByTaxon).length > 1) { + $('#grpTaxa').show(); + for (let i in sortedTaxa) { + databasesByTaxon[sortedTaxa[i]].sort(); + $("#taxa").append("") + } + } + $("#taxa").selectpicker("refresh"); + + $('#module').html(dbListOptions).selectpicker('refresh'); var module = $_GET("module"); // get module from url if (module != null) // sometimes a # appears at the end of the url so we remove it with regexp module = module.replace(new RegExp('#([^\\s]*)', 'g'), ''); - if (module !== null) { + if (module != null) { $('#module').selectpicker('val', module); $('#module').trigger('change'); + let moduleTaxon = $('#module option').filter(':selected').attr("data-taxon"); + if (moduleTaxon != null) { + $("#taxa").val(moduleTaxon); + $("#taxa").selectpicker("refresh").trigger('change'); + } } }, error: function(xhr, ajaxOptions, thrownError) { @@ -1206,7 +1242,7 @@ https://doi.org/10.1093/gigascience/giz051 }), success: function(jsonResult) { seqCount = jsonResult.result.data.length; - $('#sequencesLabel').html("Sequences (" + seqCount + "/" + seqCount + ")"); + $('#sequencesLabel span').text( seqCount + "/" + seqCount + ""); referenceNames = []; jsonResult.result.data.forEach(ref => { referenceNames.push(ref["referenceName"]); @@ -2561,7 +2597,7 @@ https://doi.org/10.1093/gigascience/giz051 trackIndividuals.forEach(function (individuals, index, array) { trackConfigs.push({ - name: array.length > 1 ? "Group " + (index + 1) : "Query", + name: array.length > 1 ? $("input#group" + (index + 1)).val() : "Query", type: "variant", format: "custom", sourceType: "file", diff --git a/src/main/webapp/js/charts.js b/src/main/webapp/js/charts.js index 6a256328..f260b4bf 100644 --- a/src/main/webapp/js/charts.js +++ b/src/main/webapp/js/charts.js @@ -990,7 +990,7 @@ function updateAvailableGroups() { $("#chartGroupSelectionDesc").css("visibility", "hidden"); let selectOptions = ""; for (var i=1; i<=getGenotypeInvestigationMode(); i++) - selectOptions += ''; + selectOptions += ''; $("select#plotGroupingMetadataValues").html(selectOptions); if (!areGroupsOverlapping(groupSelect.val())) $("select#plotGroupingMetadataValues").find('option').prop('selected', true); diff --git a/src/main/webapp/js/main.js b/src/main/webapp/js/main.js index f10aa576..ae9314e2 100644 --- a/src/main/webapp/js/main.js +++ b/src/main/webapp/js/main.js @@ -340,6 +340,12 @@ function getSelectedVariantIds() { return selectedVariantIds.join(";"); } +function getGroupNames() { + return $("input.groupName").map(function() { + return $(this).val(); + }).get(); +} + function getDiscriminateArray() { var result = []; for (var i = 1; i <= $(".genotypeInvestigationDiv").length; i++) @@ -522,6 +528,7 @@ function buildSearchQuery(searchMode, pageToken) { "geneName": getSelectedGenesIds(), "callSetIds": getSelectedIndividuals(activeGroups !== 0 ? [1] : null, true), "discriminate": getDiscriminateArray(), + "groupName": getGroupNames(), "pageSize": 100, "pageToken": pageToken, "sortBy": sortBy, @@ -835,6 +842,22 @@ function isNumberKey(evt) { return true; } +function groupNameChanged(n) { + let name = $("input#group" + n).val().trim().replaceAll(" ", "_"); + $("input#group" + n).val($("input.groupName").filter(function() { + return $(this).val() === name; + }).length > 1 || name == "" ? "Group " + n : name); + + let newName = $("input#group" + n).val(); + for (var i = 1; i <= getGenotypeInvestigationMode(); i++) + if (i != n) { + $('#discriminate' + i + ` option[title='Group ${n}']`).text(newName); + $('#discriminate' + i).selectpicker('refresh'); + + $("#igvGroupsMenu li input[value=group" + n + "]").parent().find("span").text(newName); + } +} + function setGenotypeInvestigationMode(mode) { var container = $("#searchDiv"); var childContainer = container.children().first(); @@ -870,7 +893,7 @@ function setGenotypeInvestigationMode(mode) { for (var i = 1; i <= mode; i++) { if (i >= count + 1) { - var htmlContent = ``; + var htmlContent = ``; childContainer.append(htmlContent); $('#discriminate' + i).selectpicker(); @@ -892,7 +915,7 @@ function setGenotypeInvestigationMode(mode) { checkGroupOverlap(groupNumber); }); - $('#individualsLabel' + i).html("Individuals (" + indOpt.length + "/" + indOpt.length + ")"); + $('#individualsLabel' + i + " span").html(indOpt.length + "/" + indOpt.length); var individualsLabelElement = $('#individualsLabel' + i); individualsLabelElement.show(); @@ -911,7 +934,7 @@ function setGenotypeInvestigationMode(mode) { $("#genotypeInvestigationDiv" + i).show(300); } - $("#igvGroupsMenu ul").append('
  • '); + $("#igvGroupsMenu ul").append('
  • '); } $('#igvGroupsMenu ul li:first input').prop("checked", true); @@ -920,7 +943,7 @@ function setGenotypeInvestigationMode(mode) { $('.indListBox').on('multiple_select_change', function () { var i = this.id.replace("Individuals", ""); var nCount = $('#Individuals' + i).selectmultiple('count'); - $('#individualsLabel' + i).html("Individuals (" + (nCount == 0 ? indOpt.length : nCount) + "/" + indOpt.length + ")"); + $('#individualsLabel' + i + " span").html((nCount == 0 ? indOpt.length : nCount) + "/" + indOpt.length); updateGtPatterns(); }); } @@ -935,7 +958,7 @@ function setGenotypeInvestigationMode(mode) { $('#discriminate' + i).html(""); for (var j = 1; j <= mode; j++) if (j != i) - $('#discriminate' + i).append(""); + $('#discriminate' + i).append(""); $('#discriminate' + i).selectpicker('val', previousVal).selectpicker('refresh'); if ($('#discriminate' + i).val() == '') @@ -1604,6 +1627,7 @@ function saveQuery() { "geneName": getSelectedGenesIds(), "callSetIds": getSelectedIndividuals(activeGroups !== 0 ? [1] : null, true), "discriminate": getDiscriminateArray(), + "groupName": getGroupNames(), "pageSize": 100, "sortBy": sortBy, "sortDir": sortDesc === true ? 'desc' : 'asc' @@ -1711,8 +1735,7 @@ function listQueries(){ if ((queryName = prompt("Enter query name", $(this).parent('p').text())) == null) return; $.ajax({ // load queries - url: loadBookmarkedQueryURL + '?module=' + referenceset - + '&queryId=' + queryId, + url: loadBookmarkedQueryURL + '?module=' + referenceset + '&queryId=' + queryId, type: "GET", dataType: "json", async: false, @@ -1736,6 +1759,7 @@ function listQueries(){ "geneName": jsonResult['geneName'], "callSetIds": jsonResult['callSetIds'], + "groupName": jsonResult['groupName'], "discriminate": jsonResult['discriminate'], "pageSize": jsonResult['pageSize'], "sortBy": jsonResult['sortBy'], @@ -1860,7 +1884,10 @@ function listQueries(){ for (var i= 0 ; i < jsonResult['gtPattern'].length ; i++) { var tabIds = i == 0 ? jsonResult['callSetIds'] : jsonResult['additionalCallSetIds'][i - 1]; - if(tabIds.length != 0) { + if (tabIds.length != 0) { + $("#Individuals" + (i + 1) + " button").filter(function() { + return $(this).text() === "load all"; + }).click(); $('#Individuals'+ (i + 1) +' div select').val(tabIds.map(function(x) { return x.split(idSep)[2]; })); @@ -1876,6 +1903,7 @@ function listQueries(){ $('#maxHeZ'+ (i + 1)).val(jsonResult['maxHeZ'][i]); $('#minMaf'+ (i + 1)).val(jsonResult['minMaf'][i]); $('#maxMaf'+ (i + 1)).val(jsonResult['maxMaf'][i]); + $('#group'+ (i + 1)).val(jsonResult['groupName'][i]); $('#Genotypes'+ (i + 1)).selectpicker('val', jsonResult['gtPattern'][i]); $('#mostSameRatio'+ (i + 1)).val(jsonResult['mostSameRatio'][i]); $('#Genotypes'+ (i + 1)).trigger('change'); @@ -2254,7 +2282,7 @@ function calculateVariantStats() { let groupSize = groupsContent[i].length == 0 ? individuals.size : groupsContent[i].length; let missingCount = groupSize - Object.keys(alleleCounts[i]).map(key => alleleCounts[i][key] || 0).reduce((sum, value) => sum + value, 0) / ploidy; let groupStats = '
    '; - groupStats += "
    " + (i > 0 ? "Group " + i : "Overall figures") + " (" + groupSize + " individuals)
    "; + groupStats += "
    " + (i > 0 ? $("input#group" + i).val() : "Overall figures") + " (" + groupSize + " individuals)
    "; groupStats += "
    Missing data: " + (missingCount * 100 / groupSize).toFixed(2) + "%
    "; if (doHetZ) groupStats += "
    Heterozygous: " + (hetZcount[i] == 0 ? 0 : hetZcount[i] * 100 / (groupSize - missingCount)).toFixed(2) + "%
    "; @@ -2269,4 +2297,17 @@ function extractUniqueAlleles(jsonResult) { var knownAlleles = [jsonResult.referenceBases, ...jsonResult.alternateBases]; var allelesWithDivs = knownAlleles.map(allele => '
    ' + allele + '
    ').join(''); return allelesWithDivs; +} + +function taxonSelected() { + let selectedTaxon = $("#taxa").val(); + $("#module option").each(function() { + let showOption = selectedTaxon == "(Any taxon)" || selectedTaxon == $(this).attr("data-taxon"); + $(this).css("display", showOption ? "block" : "none"); + if (referenceset == $(this).val() && !showOption) { + $("#module").val(null); + $('#module').trigger('change'); + } + }); + $("#module").selectpicker("refresh"); } \ No newline at end of file diff --git a/src/main/webapp/navbar.jsp b/src/main/webapp/navbar.jsp index 1d308e69..fb38b092 100644 --- a/src/main/webapp/navbar.jsp +++ b/src/main/webapp/navbar.jsp @@ -58,6 +58,9 @@