From 869f84ee791ce2761ef240a07c641fc5a829b0c3 Mon Sep 17 00:00:00 2001 From: jgart Date: Mon, 17 Jan 2022 05:24:38 -0500 Subject: update live jupyter notebooks --- wqflask/wqflask/jupyter_notebooks.py | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/wqflask/wqflask/jupyter_notebooks.py b/wqflask/wqflask/jupyter_notebooks.py index 7d76828e..ba0b0e0d 100644 --- a/wqflask/wqflask/jupyter_notebooks.py +++ b/wqflask/wqflask/jupyter_notebooks.py @@ -1,17 +1,31 @@ from flask import Blueprint, render_template -jupyter_notebooks = Blueprint('jupyter_notebooks', __name__) +jupyter_notebooks = Blueprint("jupyter_notebooks", __name__) + @jupyter_notebooks.route("/launcher", methods=("GET",)) def launcher(): links = ( - { - "main_url": "http://notebook.genenetwork.org/34301/notebooks/genenetwork-api-using-r.ipynb", - "notebook_name": "R notebook showing how to query the GeneNetwork API.", - "src_link_url": "https://github.com/jgarte/genenetwork-api-r-jupyter-notebook"}, - { - "main_url": "http://notebook.genenetwork.org/57675/notebooks/genenetwork.ipynb", - "notebook_name": "Querying the GeneNetwork API declaratively with python.", - "src_link_url": "https://github.com/jgarte/genenetwork-jupyter-notebook-example"}) + { + "main_url": "http://notebook.genenetwork.org/59373/notebooks/BXD%20Analysis.ipynb", + "notebook_name": "This shows how to model BXD mouse weight data using an AR(1) process", + "src_link_url": "https://github.com/BonfaceKilz/tsaf-analysis-of-bxd-mouse-colonies", + }, + { + "main_url": "http://notebook.genenetwork.org/50939/notebooks/2020-05-08/solberg-rat-analysis.ipynb", + "notebook_name": "Quantitative Genetics Tools for Mapping Trait Variation to Mechanisms, Therapeutics, and Interventions - Webinar Series", + "src_link_url": "https://github.com/senresearch/quant-genetics-webinars", + }, + { + "main_url": "http://notebook.genenetwork.org/34447/notebooks/genenetwork.ipynb", + "notebook_name": "Querying the GeneNetwork API declaratively with python.", + "src_link_url": "https://github.com/jgarte/genenetwork-jupyter-notebook-example", + }, + { + "main_url": "http://notebook.genenetwork.org/53923/notebooks/genenetwork-api-using-r.ipynb", + "notebook_name": "R notebook showing how to query the GeneNetwork API.", + "src_link_url": "https://github.com/jgarte/genenetwork-api-r-jupyter-notebook", + }, + ) return render_template("jupyter_notebooks.html", links=links) -- cgit v1.2.3 From 885b5fb52fd123832fa829484de949d4afaa38d6 Mon Sep 17 00:00:00 2001 From: jgart Date: Mon, 17 Jan 2022 06:17:50 -0500 Subject: update saunak's live jupyter notebook --- wqflask/wqflask/jupyter_notebooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wqflask/wqflask/jupyter_notebooks.py b/wqflask/wqflask/jupyter_notebooks.py index ba0b0e0d..fc3a0af3 100644 --- a/wqflask/wqflask/jupyter_notebooks.py +++ b/wqflask/wqflask/jupyter_notebooks.py @@ -12,7 +12,7 @@ def launcher(): "src_link_url": "https://github.com/BonfaceKilz/tsaf-analysis-of-bxd-mouse-colonies", }, { - "main_url": "http://notebook.genenetwork.org/50939/notebooks/2020-05-08/solberg-rat-analysis.ipynb", + "main_url": "http://notebook.genenetwork.org/53457/notebooks/2020-05-08/solberg-rat-analysis.ipynb", "notebook_name": "Quantitative Genetics Tools for Mapping Trait Variation to Mechanisms, Therapeutics, and Interventions - Webinar Series", "src_link_url": "https://github.com/senresearch/quant-genetics-webinars", }, -- cgit v1.2.3 From 673c37052547cbc3d4a77ccf979972deb91cac94 Mon Sep 17 00:00:00 2001 From: Arun Isaac Date: Tue, 25 Jan 2022 10:10:44 +0530 Subject: guix.scm: Inherit package from genenetwork2, not genenetwork3. The package definition was likely copied from the genenetwork3 repo resulting in this error. * guix.scm: Inherit package from genenetwork2, not genenetwork3. --- guix.scm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/guix.scm b/guix.scm index 9352c7c5..766fddf6 100644 --- a/guix.scm +++ b/guix.scm @@ -18,7 +18,7 @@ (define %source-dir (dirname (current-filename))) (package - (inherit genenetwork3) - (source (local-file %source-dir "genenetwork3-checkout" + (inherit genenetwork2) + (source (local-file %source-dir "genenetwork2-checkout" #:recursive? #t #:select? (git-predicate %source-dir)))) -- cgit v1.2.3 From b8a3ac8a4d60e74f893a6714b8440ad6758a7fbd Mon Sep 17 00:00:00 2001 From: BonfaceKilz Date: Tue, 25 Jan 2022 14:51:32 +0300 Subject: user_login: Remove noisy debug statement --- wqflask/wqflask/user_login.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/wqflask/wqflask/user_login.py b/wqflask/wqflask/user_login.py index ff77982f..45a12f77 100644 --- a/wqflask/wqflask/user_login.py +++ b/wqflask/wqflask/user_login.py @@ -177,8 +177,6 @@ def verify_email(): @app.route("/n/login", methods=('GET', 'POST')) def login(): params = request.form if request.form else request.args - logger.debug("in login params are:", params) - if not params: # ZS: If coming to page for first time from utility.tools import GITHUB_AUTH_URL, GITHUB_CLIENT_ID, ORCID_AUTH_URL, ORCID_CLIENT_ID external_login = {} -- cgit v1.2.3 From ea146928a397d8a813ccd169e1e532fd623f8998 Mon Sep 17 00:00:00 2001 From: BonfaceKilz Date: Wed, 26 Jan 2022 11:13:43 +0300 Subject: wqflask: Ignore diffs with |ε| < 0.001 * wqflask/wqflask/metadata_edits.py (update_phenotype): Filter out values with |ε| < 0.001 when generating the diff file. --- wqflask/wqflask/metadata_edits.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/wqflask/wqflask/metadata_edits.py b/wqflask/wqflask/metadata_edits.py index dc738f88..c1121774 100644 --- a/wqflask/wqflask/metadata_edits.py +++ b/wqflask/wqflask/metadata_edits.py @@ -257,9 +257,29 @@ def update_phenotype(dataset_id: str, name: str): r = run_cmd(cmd=("csvdiff " f"'{uploaded_file_name}' '{new_file_name}' " "--format json")) + json_data = json.loads(r.get("output")) + + # Only consider values where |ε| < 0.001; otherwise, use the + # old value in "Original". + _modifications = [] + for m in json_data.get("Modifications"): + _original = m.get("Original").split(",") + _current = m.get("Current").split(",") + for i, (x, y) in enumerate(zip(_original, _current)): + if (x.replace('.', '').isdigit() + and y.replace('.', '').isdigit() + and abs(float(x) - float(y)) < 0.001): + _current[i] = x + if not (__o:=",".join(_original)) == (__c:=",".join(_current)): + _modifications.append( + { + "Original": __o, + "Current": __c, + }) + json_data['Modifications'] = _modifications # Edge case where the csv file has not been edited! - if not any(json.loads(r.get("output")).values()): + if not any(json_data.values()): flash(f"You have not modified the csv file you downloaded!", "warning") return redirect(f"/datasets/{dataset_id}/traits/{name}" @@ -267,7 +287,7 @@ def update_phenotype(dataset_id: str, name: str): diff_output = (f"{TMPDIR}/sample-data/diffs/" f"{_file_name}.json") with open(diff_output, "w") as f: - dict_ = json.loads(r.get("output")) + dict_ = json_data dict_.update({ "trait_name": str(name), "phenotype_id": str(phenotype_id), -- cgit v1.2.3 From f6490120975eebe879a9d22f6bd741d887ec6ca5 Mon Sep 17 00:00:00 2001 From: jgart Date: Wed, 26 Jan 2022 20:58:58 -0500 Subject: update live jupyter notebooks Removed bonface's notebook until seaborn gets fixed. --- wqflask/wqflask/jupyter_notebooks.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/wqflask/wqflask/jupyter_notebooks.py b/wqflask/wqflask/jupyter_notebooks.py index fc3a0af3..10386d33 100644 --- a/wqflask/wqflask/jupyter_notebooks.py +++ b/wqflask/wqflask/jupyter_notebooks.py @@ -7,22 +7,17 @@ jupyter_notebooks = Blueprint("jupyter_notebooks", __name__) def launcher(): links = ( { - "main_url": "http://notebook.genenetwork.org/59373/notebooks/BXD%20Analysis.ipynb", - "notebook_name": "This shows how to model BXD mouse weight data using an AR(1) process", - "src_link_url": "https://github.com/BonfaceKilz/tsaf-analysis-of-bxd-mouse-colonies", - }, - { - "main_url": "http://notebook.genenetwork.org/53457/notebooks/2020-05-08/solberg-rat-analysis.ipynb", + "main_url": "http://notebook.genenetwork.org/40771/notebooks/2020-05-08/solberg-rat-analysis.ipynb", "notebook_name": "Quantitative Genetics Tools for Mapping Trait Variation to Mechanisms, Therapeutics, and Interventions - Webinar Series", "src_link_url": "https://github.com/senresearch/quant-genetics-webinars", }, { - "main_url": "http://notebook.genenetwork.org/34447/notebooks/genenetwork.ipynb", + "main_url": "http://notebook.genenetwork.org/45837/notebooks/genenetwork.ipynb", "notebook_name": "Querying the GeneNetwork API declaratively with python.", "src_link_url": "https://github.com/jgarte/genenetwork-jupyter-notebook-example", }, { - "main_url": "http://notebook.genenetwork.org/53923/notebooks/genenetwork-api-using-r.ipynb", + "main_url": "http://notebook.genenetwork.org/38079/notebooks/genenetwork-api-using-r.ipynb", "notebook_name": "R notebook showing how to query the GeneNetwork API.", "src_link_url": "https://github.com/jgarte/genenetwork-api-r-jupyter-notebook", }, -- cgit v1.2.3 From 627f86392f082b9a42bd9efeb59e7ee9374932b3 Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Thu, 27 Jan 2022 10:37:03 +0300 Subject: Fix 'Bad substitution' issue In posix shell, string indexing is undefined. This commit replaces the string indexing with a more portable implementation. --- bin/genenetwork2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/genenetwork2 b/bin/genenetwork2 index ce3678e4..7669a89a 100755 --- a/bin/genenetwork2 +++ b/bin/genenetwork2 @@ -59,7 +59,7 @@ else echo INFO: GN2 is running from a source tree GIT_HASH=$(git rev-parse HEAD) GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD) - export GN_VERSION=$GN2_ID:$(cat $GN2_BASE_DIR/etc/VERSION)-$GIT_BRANCH-${GIT_HASH:0:9} + export GN_VERSION="${GN2_ID}:$(cat ${GN2_BASE_DIR}/etc/VERSION)-${GIT_BRANCH}-$(echo ${GIT_HASH} | cut -c1-9)" fi echo GN_VERSION=$GN_VERSION -- cgit v1.2.3 From 978da92d6fdf6c1f3f9f4c56b963339725921248 Mon Sep 17 00:00:00 2001 From: zsloan Date: Tue, 18 Jan 2022 20:42:44 +0000 Subject: This fixes an issue where you couldn't select more than 150-200 rows when submitting search result traits to a collection; it previously just used JQuery and needed to be changed to use DataTables API to select from rows --- wqflask/wqflask/static/new/javascript/search_results.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/wqflask/wqflask/static/new/javascript/search_results.js b/wqflask/wqflask/static/new/javascript/search_results.js index 48b9b7be..9e507123 100644 --- a/wqflask/wqflask/static/new/javascript/search_results.js +++ b/wqflask/wqflask/static/new/javascript/search_results.js @@ -132,9 +132,13 @@ $(function() { add_to_collection = function() { var traits; - traits = $("#trait_table input:checked").map(function() { - return $(this).val(); - }).get(); + table_api = $('#trait_table').DataTable(); + check_nodes = table_api.column(0).nodes().to$(); + traits = Array.from(check_nodes.map(function() { + if ($(this)[0].childNodes[0].checked){ + return $(this)[0].childNodes[0].value + } + })) var traits_hash = md5(traits.toString()); @@ -337,4 +341,4 @@ $(function() { } apply_default(); -}); \ No newline at end of file +}); -- cgit v1.2.3 From 2e4442e0d552200a79769680bdcd1dab9ae98078 Mon Sep 17 00:00:00 2001 From: zsloan Date: Tue, 18 Jan 2022 20:49:08 +0000 Subject: Increase Y of search result table to 1000px so more rows are visible without scrolling --- wqflask/wqflask/templates/search_result_page.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wqflask/wqflask/templates/search_result_page.html b/wqflask/wqflask/templates/search_result_page.html index f73cba17..5e111882 100644 --- a/wqflask/wqflask/templates/search_result_page.html +++ b/wqflask/wqflask/templates/search_result_page.html @@ -447,7 +447,7 @@ "destroy": true, "autoWidth": false, "bSortClasses": false, - "scrollY": "500px", + "scrollY": "1000px", "scrollCollapse": true, {% if trait_list|length > 5 %} "scroller": true, -- cgit v1.2.3 From 0761eb67c601efe7d285e556fb1a07d52398eb6b Mon Sep 17 00:00:00 2001 From: zsloan Date: Tue, 18 Jan 2022 21:15:01 +0000 Subject: Fix issue where Geno query was fetching from the chr_num column instead of the Chr column (and the chr_num column isn't always populated for certain Genotype datasets) --- wqflask/wqflask/do_search.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wqflask/wqflask/do_search.py b/wqflask/wqflask/do_search.py index 99272ee3..ee5a4d91 100644 --- a/wqflask/wqflask/do_search.py +++ b/wqflask/wqflask/do_search.py @@ -343,7 +343,7 @@ class GenotypeSearch(DoSearch): GenoFreeze.createtime as thistable, Geno.Name as Geno_Name, Geno.Source2 as Geno_Source2, - Geno.chr_num as Geno_chr_num, + Geno.Chr as Geno_Chr, Geno.Mb as Geno_Mb FROM GenoXRef, GenoFreeze, Geno """ -- cgit v1.2.3 From 03c0557192242d28a35e0f9f1c20510cf457d985 Mon Sep 17 00:00:00 2001 From: zsloan Date: Tue, 18 Jan 2022 22:26:45 +0000 Subject: Replaced the info icon in the Peak LOD and Effect Size columns with a superscript question mark, since the icon was appearing strangely in some browsers (namely Rob's) but in a while I couldn't reproducd --- wqflask/wqflask/templates/search_result_page.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/wqflask/wqflask/templates/search_result_page.html b/wqflask/wqflask/templates/search_result_page.html index 5e111882..065d9049 100644 --- a/wqflask/wqflask/templates/search_result_page.html +++ b/wqflask/wqflask/templates/search_result_page.html @@ -245,7 +245,7 @@ 'orderSequence': [ "desc", "asc"] }, { - 'title': "
Peak  
LOD  
", + 'title': "
Peak  ?
LOD  
", 'type': "natural-minus-na", 'data': "lod_score", 'width': "60px", @@ -260,7 +260,7 @@ 'data': "lrs_location" }, { - 'title': "
Effect  
Size  
", + 'title': "
Effect  ?
Size  
", 'type': "natural-minus-na", 'data': "additive", 'width': "65px", @@ -339,7 +339,7 @@ 'orderSequence': [ "desc", "asc"] }, { - 'title': "
Peak  
LOD  
", + 'title': "
Peak  ?
LOD  
", 'type': "natural-minus-na", 'data': "lod_score", 'targets': 7, @@ -354,7 +354,7 @@ 'data': "lrs_location" }, { - 'title': "
Effect  
Size  
", + 'title': "
Effect  ?
Size  
", 'type': "natural-minus-na", 'width': "60px", 'data': "additive", -- cgit v1.2.3 From da49ee44334723358957dab3d1393a98ea9d7f51 Mon Sep 17 00:00:00 2001 From: zsloan Date: Tue, 18 Jan 2022 22:28:24 +0000 Subject: Increased the width of the location columns a bit, since they were being pushed onto a second line in some browsers (specifically Robs, but couldn't reproduce in mine, so I just changed it to something that should work for him) --- wqflask/wqflask/templates/search_result_page.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/wqflask/wqflask/templates/search_result_page.html b/wqflask/wqflask/templates/search_result_page.html index 065d9049..63e06237 100644 --- a/wqflask/wqflask/templates/search_result_page.html +++ b/wqflask/wqflask/templates/search_result_page.html @@ -232,7 +232,7 @@ { 'title': "
Location
", 'type': "natural-minus-na", - 'width': "125px", + 'width': "130px", 'targets': 5, 'data': "location" }, @@ -255,7 +255,7 @@ { 'title': "
Peak Location
", 'type': "natural-minus-na", - 'width': "125px", + 'width': "130px", 'targets': 8, 'data': "lrs_location" }, @@ -349,7 +349,7 @@ { 'title': "
Peak Location
", 'type': "natural-minus-na", - 'width': "120px", + 'width': "125px", 'targets': 8, 'data': "lrs_location" }, @@ -374,7 +374,7 @@ { 'title': "
Location
", 'type': "natural-minus-na", - 'width': "120px", + 'width': "125px", 'targets': 2, 'data': "location" }{% endif %} -- cgit v1.2.3 From 3e3335ec46a61056ffd8130969bca1d6857d182f Mon Sep 17 00:00:00 2001 From: zsloan Date: Tue, 18 Jan 2022 22:30:53 +0000 Subject: Made the search result table wider for genotype results, since previously the Location text was being pushed to a second line (this was actually visible for me as well, not just Rob) --- wqflask/wqflask/templates/search_result_page.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wqflask/wqflask/templates/search_result_page.html b/wqflask/wqflask/templates/search_result_page.html index 63e06237..eb4fe6a1 100644 --- a/wqflask/wqflask/templates/search_result_page.html +++ b/wqflask/wqflask/templates/search_result_page.html @@ -126,7 +126,7 @@ {% endif %} {% endif %} -
+
-- cgit v1.2.3 From 77f15b22adf297a8d46c3d590183b8f52b2baf51 Mon Sep 17 00:00:00 2001 From: zsloan Date: Wed, 19 Jan 2022 20:05:42 +0000 Subject: Make glossary link question marks italicized and red --- wqflask/wqflask/templates/search_result_page.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/wqflask/wqflask/templates/search_result_page.html b/wqflask/wqflask/templates/search_result_page.html index eb4fe6a1..c86aae39 100644 --- a/wqflask/wqflask/templates/search_result_page.html +++ b/wqflask/wqflask/templates/search_result_page.html @@ -245,7 +245,7 @@ 'orderSequence': [ "desc", "asc"] }, { - 'title': "
Peak  ?
LOD  
", + 'title': "
Peak ?
LOD  
", 'type': "natural-minus-na", 'data': "lod_score", 'width': "60px", @@ -260,7 +260,7 @@ 'data': "lrs_location" }, { - 'title': "
Effect  ?
Size  
", + 'title': "
Effect ?
Size  
", 'type': "natural-minus-na", 'data': "additive", 'width': "65px", @@ -339,7 +339,7 @@ 'orderSequence': [ "desc", "asc"] }, { - 'title': "
Peak  ?
LOD  
", + 'title': "
Peak ?
LOD  
", 'type': "natural-minus-na", 'data': "lod_score", 'targets': 7, @@ -354,7 +354,7 @@ 'data': "lrs_location" }, { - 'title': "
Effect  ?
Size  
", + 'title': "
Effect ?
Size  
", 'type': "natural-minus-na", 'width': "60px", 'data': "additive", -- cgit v1.2.3 From 7a8bfd0d99801e07c18dd5f9606e069b7643df89 Mon Sep 17 00:00:00 2001 From: zsloan Date: Wed, 19 Jan 2022 20:09:38 +0000 Subject: Reordered search result options as described in Rob's 1/19/2022 e-mail --- wqflask/wqflask/templates/search_result_page.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/wqflask/wqflask/templates/search_result_page.html b/wqflask/wqflask/templates/search_result_page.html index c86aae39..054c1a98 100644 --- a/wqflask/wqflask/templates/search_result_page.html +++ b/wqflask/wqflask/templates/search_result_page.html @@ -94,12 +94,12 @@ {% endif %} - - - - + + + + -- cgit v1.2.3 From 8f6248c71d04bf111d1cc38a398ea331f48d4fae Mon Sep 17 00:00:00 2001 From: zsloan Date: Wed, 19 Jan 2022 20:10:17 +0000 Subject: Moved the Show/Hide Columns text before the column buttons to save vertical space --- wqflask/wqflask/templates/search_result_page.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wqflask/wqflask/templates/search_result_page.html b/wqflask/wqflask/templates/search_result_page.html index 054c1a98..a7d72807 100644 --- a/wqflask/wqflask/templates/search_result_page.html +++ b/wqflask/wqflask/templates/search_result_page.html @@ -105,7 +105,7 @@ {% if dataset.type != 'Geno' %}
- Show/Hide Columns: + Show/Hide Columns: 
{% if dataset.type == 'ProbeSet' %} -- cgit v1.2.3 From 07412521f896a609ae0011bc0a5ac0adf252b637 Mon Sep 17 00:00:00 2001 From: zsloan Date: Wed, 19 Jan 2022 20:47:51 +0000 Subject: Replaced the Select Top feature with one that functions more like the Block By Index feature from the trait page; you can now use text indicating index ranges or specific indices, and the filter triggers on either hitting Enter or clicking outside of the text area --- .../static/new/javascript/search_results.js | 74 ++++++++++++---------- wqflask/wqflask/templates/search_result_page.html | 2 +- 2 files changed, 43 insertions(+), 33 deletions(-) diff --git a/wqflask/wqflask/static/new/javascript/search_results.js b/wqflask/wqflask/static/new/javascript/search_results.js index 9e507123..355fd18e 100644 --- a/wqflask/wqflask/static/new/javascript/search_results.js +++ b/wqflask/wqflask/static/new/javascript/search_results.js @@ -93,41 +93,51 @@ $(function() { $('#trait_table').DataTable().search($(this).val()).draw(); }); - $('#select_top').keyup(function(){ - num_rows = $(this).val() - - if (num_rows = parseInt(num_rows)){ - table_api = $('#trait_table').DataTable(); - - check_cells = table_api.column(0).nodes().to$(); - for (let i = 0; i < num_rows; i++) { - check_cells[i].childNodes[0].checked = true; - } - - check_rows = table_api.rows().nodes(); - for (let i=0; i < num_rows; i++) { - if (check_rows[i].classList.contains("selected")){ - continue - } else { - check_rows[i].classList.add("selected") - } - } - for (let i = num_rows; i < check_rows.length; i++){ - check_cells[i].childNodes[0].checked = false; - if (check_rows[i].classList.contains("selected")){ - check_rows[i].classList.remove("selected") - } - } - } - else { - for (let i = 0; i < check_rows.length; i++){ - check_cells[i].childNodes[0].checked = false; - if (check_rows[i].classList.contains("selected")){ - check_rows[i].classList.remove("selected") + parse_index_string = function(idx_string) { + index_list = []; + _ref = idx_string.split(","); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + index_set = _ref[_i]; + if (index_set.indexOf('-') !== -1) { + try { + start_index = parseInt(index_set.split("-")[0]); + end_index = parseInt(index_set.split("-")[1]); + for (index = _j = start_index; start_index <= end_index ? _j <= end_index : _j >= end_index; index = start_index <= end_index ? ++_j : --_j) { + index_list.push(index); } + } catch (_error) { + error = _error; + alert("Syntax error"); } + } else { + index = parseInt(index_set); + index_list.push(index); + } + } + return new Set(index_list) + } + + filter_by_index = function() { + index_string = $('#select_top').val() + index_set = parse_index_string(index_string) + + table_api = $('#trait_table').DataTable(); + check_nodes = table_api.column(0).nodes().to$(); + check_nodes.each(function(index) { + if (index_set.has(index + 1)){ + $(this)[0].childNodes[0].checked = true } - change_buttons(); + }) + } + + $('#select_top').keyup(function(event){ + if (event.keyCode === 13) { + filter_by_index() + } + }); + + $('#select_top').blur(function() { + filter_by_index() }); add_to_collection = function() { diff --git a/wqflask/wqflask/templates/search_result_page.html b/wqflask/wqflask/templates/search_result_page.html index a7d72807..fb335fd3 100644 --- a/wqflask/wqflask/templates/search_result_page.html +++ b/wqflask/wqflask/templates/search_result_page.html @@ -97,7 +97,7 @@ - + -- cgit v1.2.3 From 43706c381ed211ff92338be689a7d32c010dcf70 Mon Sep 17 00:00:00 2001 From: zsloan Date: Wed, 19 Jan 2022 21:01:54 +0000 Subject: Changed search result index column to be static (so it doesn't change when the user sorts) --- wqflask/wqflask/templates/search_result_page.html | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/wqflask/wqflask/templates/search_result_page.html b/wqflask/wqflask/templates/search_result_page.html index fb335fd3..56ac6e82 100644 --- a/wqflask/wqflask/templates/search_result_page.html +++ b/wqflask/wqflask/templates/search_result_page.html @@ -195,6 +195,8 @@ 'title': "Index", 'type': "natural", 'width': "35px", + "searchable": false, + "orderable": false, 'targets': 1, 'data': "index" } @@ -491,6 +493,12 @@ } } + trait_table.on( 'order.dt search.dt', function () { + trait_table.column(1, {search:'applied', order:'applied'}).nodes().each( function (cell, i) { + cell.innerHTML = i+1; + } ); + } ).draw(); + window.addEventListener('resize', function(){ trait_table.columns.adjust(); }); -- cgit v1.2.3 From 90103e1847256c8123cdb309b0acfa5e116751e4 Mon Sep 17 00:00:00 2001 From: zsloan Date: Wed, 19 Jan 2022 21:19:48 +0000 Subject: Changed the glossary question mark to appear at the very end of the colum text, in the hopes that this will show up normally for Rob --- wqflask/wqflask/templates/search_result_page.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/wqflask/wqflask/templates/search_result_page.html b/wqflask/wqflask/templates/search_result_page.html index 56ac6e82..62c0164b 100644 --- a/wqflask/wqflask/templates/search_result_page.html +++ b/wqflask/wqflask/templates/search_result_page.html @@ -247,7 +247,7 @@ 'orderSequence': [ "desc", "asc"] }, { - 'title': "
Peak ?
LOD  
", + 'title': "
Peak
LOD ?
", 'type': "natural-minus-na", 'data': "lod_score", 'width': "60px", @@ -262,7 +262,7 @@ 'data': "lrs_location" }, { - 'title': "
Effect ?
Size  
", + 'title': "
Effect
Size ?
", 'type': "natural-minus-na", 'data': "additive", 'width': "65px", @@ -341,7 +341,7 @@ 'orderSequence': [ "desc", "asc"] }, { - 'title': "
Peak ?
LOD  
", + 'title': "
Peak
LOD ?
", 'type': "natural-minus-na", 'data': "lod_score", 'targets': 7, @@ -356,7 +356,7 @@ 'data': "lrs_location" }, { - 'title': "
Effect ?
Size  
", + 'title': "
Effect
Size ?
", 'type': "natural-minus-na", 'width': "60px", 'data': "additive", -- cgit v1.2.3 From 65ebd0ae48d77120569fb830b593c16f52a15cca Mon Sep 17 00:00:00 2001 From: zsloan Date: Wed, 19 Jan 2022 21:20:19 +0000 Subject: Moved the Show/Hide Columns text onto the same line as the buttons, to save a little vertical space --- wqflask/wqflask/templates/search_result_page.html | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/wqflask/wqflask/templates/search_result_page.html b/wqflask/wqflask/templates/search_result_page.html index 62c0164b..419f9512 100644 --- a/wqflask/wqflask/templates/search_result_page.html +++ b/wqflask/wqflask/templates/search_result_page.html @@ -105,8 +105,7 @@ {% if dataset.type != 'Geno' %}
- Show/Hide Columns:  -
+ Show/Hide Columns:   {% if dataset.type == 'ProbeSet' %} -- cgit v1.2.3 From fb8e8c5f56b122e4561d448ce2873183da73f561 Mon Sep 17 00:00:00 2001 From: zsloan Date: Thu, 20 Jan 2022 19:40:47 +0000 Subject: Added some JS disabling form submission on hitting Enter, so that users can hit Enter in the Select Rows text area --- wqflask/wqflask/static/new/javascript/search_results.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/wqflask/wqflask/static/new/javascript/search_results.js b/wqflask/wqflask/static/new/javascript/search_results.js index 355fd18e..8ab41f25 100644 --- a/wqflask/wqflask/static/new/javascript/search_results.js +++ b/wqflask/wqflask/static/new/javascript/search_results.js @@ -130,6 +130,13 @@ $(function() { }) } + $(window).keydown(function(event){ + if((event.keyCode == 13)) { + event.preventDefault(); + return false; + } + }); + $('#select_top').keyup(function(event){ if (event.keyCode === 13) { filter_by_index() -- cgit v1.2.3 From 95d06e4b9028e25527239911d99bf584666073a8 Mon Sep 17 00:00:00 2001 From: zsloan Date: Thu, 20 Jan 2022 19:43:42 +0000 Subject: Changed the placeholder text for the table search to Search For --- wqflask/wqflask/templates/search_result_page.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wqflask/wqflask/templates/search_result_page.html b/wqflask/wqflask/templates/search_result_page.html index 419f9512..a5b2242c 100644 --- a/wqflask/wqflask/templates/search_result_page.html +++ b/wqflask/wqflask/templates/search_result_page.html @@ -94,7 +94,7 @@ {% endif %} - + -- cgit v1.2.3 From 58cda1ea8740792beb5d0f3f1de15c51b2f47f82 Mon Sep 17 00:00:00 2001 From: zsloan Date: Wed, 26 Jan 2022 18:41:31 +0000 Subject: Removed unnecessary variable from for-loop in parse_index_string --- wqflask/wqflask/static/new/javascript/search_results.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wqflask/wqflask/static/new/javascript/search_results.js b/wqflask/wqflask/static/new/javascript/search_results.js index 8ab41f25..17b41812 100644 --- a/wqflask/wqflask/static/new/javascript/search_results.js +++ b/wqflask/wqflask/static/new/javascript/search_results.js @@ -96,7 +96,7 @@ $(function() { parse_index_string = function(idx_string) { index_list = []; _ref = idx_string.split(","); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { + for (_i = 0; _i < _ref.length; _i++) { index_set = _ref[_i]; if (index_set.indexOf('-') !== -1) { try { -- cgit v1.2.3 From e374cac6eec2d05345d6f7a05061ee38a63db9f1 Mon Sep 17 00:00:00 2001 From: zsloan Date: Wed, 26 Jan 2022 19:21:24 +0000 Subject: Changed functions in search_results.js to be camelCased, except for change_buttons because it's referenced in many other files --- .../static/new/javascript/search_results.js | 68 +++++++++++----------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/wqflask/wqflask/static/new/javascript/search_results.js b/wqflask/wqflask/static/new/javascript/search_results.js index 17b41812..7124151e 100644 --- a/wqflask/wqflask/static/new/javascript/search_results.js +++ b/wqflask/wqflask/static/new/javascript/search_results.js @@ -25,10 +25,10 @@ change_buttons = function(check_node = 0) { }; $(function() { - var add, checked_traits, deselect_all, invert, remove, removed_traits, select_all; + var add, checked_traits, deselectAll, invert, selectAll; checked_traits = null; - select_all = function() { + selectAll = function() { table_api = $('#trait_table').DataTable(); check_cells = table_api.column(0).nodes().to$(); @@ -44,7 +44,7 @@ $(function() { change_buttons(); }; - deselect_all = function() { + deselectAll = function() { table_api = $('#trait_table').DataTable(); check_cells = table_api.column(0).nodes().to$(); @@ -93,7 +93,7 @@ $(function() { $('#trait_table').DataTable().search($(this).val()).draw(); }); - parse_index_string = function(idx_string) { + parseIndexString = function(idx_string) { index_list = []; _ref = idx_string.split(","); for (_i = 0; _i < _ref.length; _i++) { @@ -117,9 +117,9 @@ $(function() { return new Set(index_list) } - filter_by_index = function() { + filterByIndex = function() { index_string = $('#select_top').val() - index_set = parse_index_string(index_string) + index_set = parseIndexString(index_string) table_api = $('#trait_table').DataTable(); check_nodes = table_api.column(0).nodes().to$(); @@ -139,15 +139,15 @@ $(function() { $('#select_top').keyup(function(event){ if (event.keyCode === 13) { - filter_by_index() + filterByIndex() } }); $('#select_top').blur(function() { - filter_by_index() + filterByIndex() }); - add_to_collection = function() { + addToCollection = function() { var traits; table_api = $('#trait_table').DataTable(); check_nodes = table_api.column(0).nodes().to$(); @@ -174,19 +174,19 @@ $(function() { }; - removed_traits = function() { + removedTraits = function() { return checked_traits.closest("tr").fadeOut(); }; - submit_bnw = function() { - trait_data = submit_traits_to_export_or_bnw("trait_table", "submit_bnw") + submitBnw = function() { + trait_data = submitTraitsToExportOrBnw("trait_table", "submit_bnw") } - export_traits = function() { - trait_data = submit_traits_to_export_or_bnw("trait_table", "export_csv") + exportTraits = function() { + trait_data = submitTraitsToExportOrBnw("trait_table", "export_csv") }; - submit_traits_to_export_or_bnw = function(table_name, destination) { + submitTraitsToExportOrBnw = function(table_name, destination) { trait_table = $('#'+table_name); table_dict = {}; @@ -227,7 +227,7 @@ $(function() { $('#export_form').submit(); }; - get_traits_from_table = function(){ + getTraitsFromTable = function(){ traits = $("#trait_table input:checked").map(function() { return $(this).val(); }).get(); @@ -243,42 +243,42 @@ $(function() { } $("#corr_matrix").on("click", function() { - traits = get_traits_from_table() + traits = getTraitsFromTable() $("#trait_list").val(traits) $("input[name=tool_used]").val("Correlation Matrix") $("input[name=form_url]").val($(this).data("url")) return submit_special("/loading") }); $("#network_graph").on("click", function() { - traits = get_traits_from_table() + traits = getTraitsFromTable() $("#trait_list").val(traits) $("input[name=tool_used]").val("Network Graph") $("input[name=form_url]").val($(this).data("url")) return submit_special("/loading") }); $("#wgcna_setup").on("click", function() { - traits = get_traits_from_table() + traits = getTraitsFromTable() $("#trait_list").val(traits) $("input[name=tool_used]").val("WGCNA Setup") $("input[name=form_url]").val($(this).data("url")) return submit_special("/loading") }); $("#ctl_setup").on("click", function() { - traits = get_traits_from_table() + traits = getTraitsFromTable() $("#trait_list").val(traits) $("input[name=tool_used]").val("CTL Setup") $("input[name=form_url]").val($(this).data("url")) return submit_special("/loading") }); $("#heatmap").on("click", function() { - traits = get_traits_from_table() + traits = getTraitsFromTable() $("#trait_list").val(traits) $("input[name=tool_used]").val("Heatmap") $("input[name=form_url]").val($(this).data("url")) return submit_special("/loading") }); $("#comp_bar_chart").on("click", function() { - traits = get_traits_from_table() + traits = getTraitsFromTable() $("#trait_list").val(traits) $("input[name=tool_used]").val("Comparison Bar Chart") $("input[name=form_url]").val($(this).data("url")) @@ -286,32 +286,32 @@ $(function() { }); $("#send_to_webgestalt, #send_to_bnw, #send_to_geneweaver").on("click", function() { - traits = get_traits_from_table() + traits = getTraitsFromTable() $("#trait_list").val(traits) url = $(this).data("url") return submit_special(url) }); - $("#select_all").click(select_all); - $("#deselect_all").click(deselect_all); + $("#select_all").click(selectAll); + $("#deselect_all").click(deselectAll); $("#invert").click(invert); - $("#add").click(add_to_collection); - $("#submit_bnw").click(submit_bnw); - $("#export_traits").click(export_traits); + $("#add").click(addToCollection); + $("#submit_bnw").click(submitBnw); + $("#export_traits").click(exportTraits); let naturalAsc = $.fn.dataTableExt.oSort["natural-ci-asc"] let naturalDesc = $.fn.dataTableExt.oSort["natural-ci-desc"] let na_equivalent_vals = ["N/A", "--", ""]; //ZS: Since there are multiple values that should be treated the same as N/A - function extract_inner_text(the_string){ + function extractInnerText(the_string){ var span = document.createElement('span'); span.innerHTML = the_string; return span.textContent || span.innerText; } - function sort_NAs(a, b, sort_function){ + function sortNAs(a, b, sort_function){ if ( na_equivalent_vals.includes(a) && na_equivalent_vals.includes(b)) { return 0; } @@ -326,10 +326,10 @@ $(function() { $.extend( $.fn.dataTableExt.oSort, { "natural-minus-na-asc": function (a, b) { - return sort_NAs(extract_inner_text(a), extract_inner_text(b), naturalAsc) + return sortNAs(extractInnerText(a), extractInnerText(b), naturalAsc) }, "natural-minus-na-desc": function (a, b) { - return sort_NAs(extract_inner_text(a), extract_inner_text(b), naturalDesc) + return sortNAs(extractInnerText(a), extractInnerText(b), naturalDesc) } }); @@ -347,7 +347,7 @@ $(function() { } ); } - apply_default = function() { + applyDefault = function() { let default_collection_id = $.cookie('default_collection'); if (default_collection_id) { let the_option = $('[name=existing_collection] option').filter(function() { @@ -356,6 +356,6 @@ $(function() { the_option.prop('selected', true); } } - apply_default(); + applyDefault(); }); -- cgit v1.2.3 From 6ab213921932d904c64c8b37a327a971a3c46cfc Mon Sep 17 00:00:00 2001 From: zsloan Date: Wed, 26 Jan 2022 19:36:38 +0000 Subject: Replaced a confusing for loop with something that is (hopefully) easier to read and explicitly inverts start_index and end_index when the former is greater than the latter --- wqflask/wqflask/static/new/javascript/search_results.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/wqflask/wqflask/static/new/javascript/search_results.js b/wqflask/wqflask/static/new/javascript/search_results.js index 7124151e..a665e3d2 100644 --- a/wqflask/wqflask/static/new/javascript/search_results.js +++ b/wqflask/wqflask/static/new/javascript/search_results.js @@ -102,7 +102,13 @@ $(function() { try { start_index = parseInt(index_set.split("-")[0]); end_index = parseInt(index_set.split("-")[1]); - for (index = _j = start_index; start_index <= end_index ? _j <= end_index : _j >= end_index; index = start_index <= end_index ? ++_j : --_j) { + + // If start index is higher than end index (for example is the string "10-5" exists) swap values so it'll be interpreted as "5-10" instead + if (start_index > end_index) { + [start_index, end_index] = [end_index, start_index] + } + + for (index = start_index; index <= end_index; index++) { index_list.push(index); } } catch (_error) { -- cgit v1.2.3 From 480d71d9e12878449f7526cacb644f686a9169af Mon Sep 17 00:00:00 2001 From: zsloan Date: Wed, 26 Jan 2022 19:42:30 +0000 Subject: Added a comment explaining what the parseIndexString function does --- wqflask/wqflask/static/new/javascript/search_results.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/wqflask/wqflask/static/new/javascript/search_results.js b/wqflask/wqflask/static/new/javascript/search_results.js index a665e3d2..b7ad359f 100644 --- a/wqflask/wqflask/static/new/javascript/search_results.js +++ b/wqflask/wqflask/static/new/javascript/search_results.js @@ -93,6 +93,13 @@ $(function() { $('#trait_table').DataTable().search($(this).val()).draw(); }); + /** + * parseIndexString takes a string consisting of integers, + * hyphens, and/or commas to indicate range(s) of indices + * to select a rows and returns the corresponding set of indices + * For example - "1, 5-10, 15" would return a set of 8 rows + * @return {Set} The list of indices as a Set + */ parseIndexString = function(idx_string) { index_list = []; _ref = idx_string.split(","); -- cgit v1.2.3 From e5f179330b024671e066edb0672a5a8b628766ed Mon Sep 17 00:00:00 2001 From: zsloan Date: Wed, 26 Jan 2022 20:54:03 +0000 Subject: Replaced the try/catch with something checking for the correct pattern with regex, for validating the Select Rows input --- wqflask/wqflask/static/new/javascript/search_results.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/wqflask/wqflask/static/new/javascript/search_results.js b/wqflask/wqflask/static/new/javascript/search_results.js index b7ad359f..a3e21081 100644 --- a/wqflask/wqflask/static/new/javascript/search_results.js +++ b/wqflask/wqflask/static/new/javascript/search_results.js @@ -102,11 +102,17 @@ $(function() { */ parseIndexString = function(idx_string) { index_list = []; + _ref = idx_string.split(","); for (_i = 0; _i < _ref.length; _i++) { index_set = _ref[_i]; + if (!/^ *([0-9]+$) *| *([0-9]+ *- *[0-9]+$) *|(^$)$/.test(index_set)) { + $('#select_samples_invalid').show(); + break + } else { + $('#select_samples_invalid').hide(); + } if (index_set.indexOf('-') !== -1) { - try { start_index = parseInt(index_set.split("-")[0]); end_index = parseInt(index_set.split("-")[1]); @@ -118,10 +124,6 @@ $(function() { for (index = start_index; index <= end_index; index++) { index_list.push(index); } - } catch (_error) { - error = _error; - alert("Syntax error"); - } } else { index = parseInt(index_set); index_list.push(index); -- cgit v1.2.3 From b25d68790fdf8d66644297dc104408140baf77e5 Mon Sep 17 00:00:00 2001 From: zsloan Date: Wed, 26 Jan 2022 20:55:37 +0000 Subject: Added the div for showing the error when the Select Rows text has wrong syntax --- wqflask/wqflask/templates/search_result_page.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/wqflask/wqflask/templates/search_result_page.html b/wqflask/wqflask/templates/search_result_page.html index a5b2242c..26cdc437 100644 --- a/wqflask/wqflask/templates/search_result_page.html +++ b/wqflask/wqflask/templates/search_result_page.html @@ -102,6 +102,10 @@
+ {% if dataset.type != 'Geno' %}
-- cgit v1.2.3 From 3dc47b73cd597291c8410cad05d1143819303e1d Mon Sep 17 00:00:00 2001 From: zsloan Date: Thu, 27 Jan 2022 19:21:41 +0000 Subject: Removed unused variables code --- wqflask/wqflask/static/new/javascript/search_results.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/wqflask/wqflask/static/new/javascript/search_results.js b/wqflask/wqflask/static/new/javascript/search_results.js index a3e21081..4c634399 100644 --- a/wqflask/wqflask/static/new/javascript/search_results.js +++ b/wqflask/wqflask/static/new/javascript/search_results.js @@ -25,9 +25,8 @@ change_buttons = function(check_node = 0) { }; $(function() { - var add, checked_traits, deselectAll, invert, selectAll; + var selectAll, deselectAll, invert; - checked_traits = null; selectAll = function() { table_api = $('#trait_table').DataTable(); @@ -189,10 +188,6 @@ $(function() { }; - removedTraits = function() { - return checked_traits.closest("tr").fadeOut(); - }; - submitBnw = function() { trait_data = submitTraitsToExportOrBnw("trait_table", "submit_bnw") } -- cgit v1.2.3 From 63cfd61c030312158aba242ff239e79e2025f2e0 Mon Sep 17 00:00:00 2001 From: zsloan Date: Thu, 27 Jan 2022 19:24:04 +0000 Subject: Changed var to let --- wqflask/wqflask/static/new/javascript/search_results.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wqflask/wqflask/static/new/javascript/search_results.js b/wqflask/wqflask/static/new/javascript/search_results.js index 4c634399..c5f9fe00 100644 --- a/wqflask/wqflask/static/new/javascript/search_results.js +++ b/wqflask/wqflask/static/new/javascript/search_results.js @@ -25,7 +25,7 @@ change_buttons = function(check_node = 0) { }; $(function() { - var selectAll, deselectAll, invert; + let selectAll, deselectAll, invert; selectAll = function() { table_api = $('#trait_table').DataTable(); -- cgit v1.2.3 From 04ef9ee2f18e812b4a24d478bdbf78303923623e Mon Sep 17 00:00:00 2001 From: zsloan Date: Thu, 27 Jan 2022 19:40:54 +0000 Subject: Highlight rows selected by filterByIndex This adds the "selected" class to rows selected by filterByIndex, which adds highlighting to them I also made the variables in this part of the code camel cased --- .../wqflask/static/new/javascript/search_results.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/wqflask/wqflask/static/new/javascript/search_results.js b/wqflask/wqflask/static/new/javascript/search_results.js index c5f9fe00..ff2452af 100644 --- a/wqflask/wqflask/static/new/javascript/search_results.js +++ b/wqflask/wqflask/static/new/javascript/search_results.js @@ -132,16 +132,23 @@ $(function() { } filterByIndex = function() { - index_string = $('#select_top').val() - index_set = parseIndexString(index_string) + indexString = $('#select_top').val() + indexSet = parseIndexString(indexString) - table_api = $('#trait_table').DataTable(); - check_nodes = table_api.column(0).nodes().to$(); - check_nodes.each(function(index) { - if (index_set.has(index + 1)){ + tableApi = $('#trait_table').DataTable(); + checkNodes = tableApi.column(0).nodes().to$(); + checkNodes.each(function(index) { + if (indexSet.has(index + 1)){ $(this)[0].childNodes[0].checked = true } }) + + checkRows = tableApi.rows().nodes().to$(); + checkRows.each(function(index) { + if (indexSet.has(index + 1)){ + $(this)[0].classList.add("selected"); + } + }) } $(window).keydown(function(event){ -- cgit v1.2.3 From fab4168e813524f8d904cd93e1e4810f9768a09c Mon Sep 17 00:00:00 2001 From: Arun Isaac Date: Fri, 28 Jan 2022 13:38:00 +0530 Subject: Revert "README: Document `guix shell' development process." This reverts commit e0a260b0de55fbea0c507eb0ca5fbdc3c1a825c0. --- README.md | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index ea473699..2b41a415 100644 --- a/README.md +++ b/README.md @@ -11,27 +11,24 @@ many different populations and many types of molecular, cellular, and physiologi The system is used by scientists and clinicians in the field of precision health care and systems genetics. GN and its predecessors have been in operation since Jan 1994, making it one of the longest-lived web services in biomedical research (https://en.wikipedia.org/wiki/GeneNetwork, and see a partial list of publications using GN and its predecessor, WebQTL (https://genenetwork.org/references/). -## Run +## Install -We recommend you use GNU Guix. GNU Guix allows you to deploy -GeneNetwork2 and dependencies as a self contained unit on any machine. +The recommended installation is with GNU Guix which allows you to +deploy GN2 and dependencies as a self contained unit on any machine. The database can be run separately as well as the source tree (for -developers). +developers). See the [installation docs](doc/README.org). + +## Run + +Once having installed GN2 it can be run through a browser +interface -Make sure you have the -[guix-bioinformatics](https://git.genenetwork.org/guix-bioinformatics/guix-bioinformatics) -channel set up. Then, to drop into a development environment with all -dependencies, run ```sh -guix shell -Df guix.scm -``` -Or, to drop into a development environment in a container, run -``` -guix shell -C --network -Df guix.scm +genenetwork2 ``` -In the development environment, start GeneNetwork2 by running, for -example, +A quick example is + ```sh env SERVER_PORT=5300 \ GENENETWORK_FILES=~/data/gn2_data/ \ @@ -42,12 +39,13 @@ env SERVER_PORT=5300 \ For full examples (you may need to set a number of environment variables), including running scripts and a Python REPL, also see the -startup script -[./bin/genenetwork2](https://github.com/genenetwork/genenetwork2/blob/testing/bin/genenetwork2). +startup script [./bin/genenetwork2](https://github.com/genenetwork/genenetwork2/blob/testing/bin/genenetwork2). Also mariadb and redis need to be running, see [INSTALL](./doc/README.org). +## Development + ## Testing To have tests pass, the redis and mariadb instance should be running, because of -- cgit v1.2.3 From 2b629077ba1ec52233c118707d44dca2fb5e6afb Mon Sep 17 00:00:00 2001 From: Arun Isaac Date: Fri, 28 Jan 2022 13:38:20 +0530 Subject: Revert "bin: Do not set environment variables set by guix shell." This reverts commit 344e428126b60932bff4c62c5ded8c36519155e8. --- README.md | 4 ++++ bin/genenetwork2 | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/README.md b/README.md index 2b41a415..7b4fc01f 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,10 @@ Also mariadb and redis need to be running, see ## Development +It may be useful to pull in the GN3 python modules locally. For this +use `GN3_PYTHONPATH` environment that gets injected in +the ./bin/genenetwork2 startup. + ## Testing To have tests pass, the redis and mariadb instance should be running, because of diff --git a/bin/genenetwork2 b/bin/genenetwork2 index 7669a89a..f1e04eef 100755 --- a/bin/genenetwork2 +++ b/bin/genenetwork2 @@ -91,7 +91,14 @@ fi export GN2_SETTINGS=$settings # Python echo GN2_SETTINGS=$settings +export PATH=$GN2_PROFILE/bin:$PATH +export PYTHONPATH="$GN2_PROFILE/lib/python3.8/site-packages" # never inject another PYTHONPATH!! +export R_LIBS_SITE=$GN2_PROFILE/site-library export JS_GUIX_PATH=$GN2_PROFILE/share/genenetwork2/javascript +export GUIX_GTK3_PATH="$GN2_PROFILE/lib/gtk-3.0" +export GI_TYPELIB_PATH="$GN2_PROFILE/lib/girepository-1.0" +export XDG_DATA_DIRS="$GN2_PROFILE/share" +export GIO_EXTRA_MODULES="$GN2_PROFILE/lib/gio/modules" export LC_ALL=C # FIXME export GUIX_GENENETWORK_FILES="$GN2_PROFILE/share/genenetwork2" export PLINK_COMMAND="$GN2_PROFILE/bin/plink2" @@ -99,6 +106,25 @@ export GEMMA_COMMAND="$GN2_PROFILE/bin/gemma" if [ -z $GEMMA_WRAPPER_COMMAND ]; then export GEMMA_WRAPPER_COMMAND="$GN2_PROFILE/bin/gemma-wrapper" fi +while IFS=":" read -ra PPATH; do + for PPART in "${PPATH[@]}"; do + if [ ! -d $PPART ] ; then echo "$PPART in PYTHONPATH not valid $PYTHONPATH" ; exit 1 ; fi + done +done <<< "$PYTHONPATH" +if [ ! -d $R_LIBS_SITE ] ; then echo "R_LIBS_SITE not valid "$R_LIBS_SITE ; exit 1 ; fi +if [ -z $PYTHONPATH ] ; then + echo "ERROR PYTHONPATH has not been set - use GN2_PROFILE!" + exit 1 +fi +if [ ! -d $R_LIBS_SITE ] ; then + echo "ERROR R_LIBS_SITE has not been set correctly (we only allow one path) - use GN2_PROFILE!" + echo "Paste into your shell the output of (for example)" + echo "guix package -p \$GN2_PROFILE --search-paths" + exit 1 +fi + +# We may change this one: +export PYTHONPATH=$PYTHON_GN_PATH:$GN2_BASE_DIR/wqflask:$GN3_PYTHONPATH:$PYTHONPATH # Our UNIX TMPDIR defaults to /tmp - change this on a shared server if [ -z $TMPDIR ]; then @@ -114,6 +140,7 @@ set|grep TMPDIR if [ "$1" = '-c' ] ; then cd $GN2_BASE_DIR/wqflask cmd=${2#wqflask/} + echo PYTHONPATH=$PYTHONPATH shift ; shift echo RUNNING COMMAND $cmd $* python $cmd $* @@ -124,6 +151,7 @@ fi if [ "$1" = "-cli" ] ; then cd $GN2_BASE_DIR/wqflask cmd=$2 + echo PYTHONPATH=$PYTHONPATH shift ; shift echo RUNNING COMMAND $cmd $* $cmd $* @@ -132,12 +160,14 @@ fi if [ "$1" = '-gunicorn' ] ; then cd $GN2_BASE_DIR/wqflask cmd=$2 + echo PYTHONPATH=$PYTHONPATH echo RUNNING gunicorn $cmd gunicorn $cmd exit $? fi if [ "$1" = '-gunicorn-dev' ] ; then cd $GN2_BASE_DIR/wqflask + echo PYTHONPATH=$PYTHONPATH if [ -z $SERVER_PORT ]; then echo "ERROR: Provide a SERVER_PORT" ; exit 1 ; fi cmd="--bind 0.0.0.0:$SERVER_PORT --workers=1 --timeout 180 --reload wsgi" echo RUNNING gunicorn $cmd @@ -146,6 +176,7 @@ if [ "$1" = '-gunicorn-dev' ] ; then fi if [ "$1" = '-gunicorn-prod' ] ; then cd $GN2_BASE_DIR/wqflask + echo PYTHONPATH=$PYTHONPATH if [ -z $SERVER_PORT ]; then echo "ERROR: Provide a SERVER_PORT" ; exit 1 ; fi PID=$TMPDIR/gunicorn.$USER.pid cmd="--bind 0.0.0.0:$SERVER_PORT --pid $PID --workers 20 --keep-alive 6000 --max-requests 100 --max-requests-jitter 30 --timeout 1200 wsgi" @@ -159,6 +190,9 @@ echo -n "dir $TMPDIR dbfilename gn2.rdb " | redis-server - & +# Overrides for packages that are not yet public (currently r-auwerx) +# export R_LIBS_SITE=$R_LIBS_SITE:$HOME/.Rlibs/das1i1pm54dj6lbdcsw5w0sdwhccyj1a-r-3.3.2/lib/R/lib + # Start the flask server running GN2 cd $GN2_BASE_DIR/wqflask echo "Starting with $settings" -- cgit v1.2.3 From fadf0b836d512c9839f91df77f5f957e7aca6a1c Mon Sep 17 00:00:00 2001 From: Arun Isaac Date: Fri, 28 Jan 2022 13:38:24 +0530 Subject: Revert "bin: Set GN2_PROFILE from GUIX_ENVIRONMENT." This reverts commit f4e336eb1ea526156e112cff97a3ec8137a2bc90. --- README.md | 17 +++++++----- bin/genenetwork2 | 83 ++++++++++++++++++++++++++++++++++++-------------------- 2 files changed, 63 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 7b4fc01f..52382c98 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ genenetwork2 A quick example is ```sh -env SERVER_PORT=5300 \ +env GN2_PROFILE=~/opt/gn-latest SERVER_PORT=5300 \ GENENETWORK_FILES=~/data/gn2_data/ \ GN_PROXY_URL="http://localhost:8080"\ GN3_LOCAL_URL="http://localhost:8081"\ @@ -69,16 +69,19 @@ We are building 'Mechanical Rob' automated testing using Python which can be run with: ```sh -env ./bin/genenetwork2 \ +env GN2_PROFILE=~/opt/gn-latest \ + ./bin/genenetwork2 \ GN_PROXY_URL="http://localhost:8080" \ GN3_LOCAL_URL="http://localhost:8081 "\ ./etc/default_settings.py -c \ ../test/requests/test-website.py -a http://localhost:5003 ``` -The ./bin/genenetwork2 script sets up the environment and executes -test-website.py in a Python interpreter. The -a switch says to run all -tests and the URL points to the running GN2 http server. +The GN2_PROFILE is the Guix profile that contains all +dependencies. The ./bin/genenetwork2 script sets up the environment +and executes test-website.py in a Python interpreter. The -a switch +says to run all tests and the URL points to the running GN2 http +server. #### Unit tests @@ -99,9 +102,9 @@ runcmd coverage html The `runcmd` and `runpython` are shell aliases defined in the following way: ```sh -alias runpython="env TMPDIR=/tmp SERVER_PORT=5004 GENENETWORK_FILES=/gnu/data/gn2_data/ GN_PROXY_URL="http://localhost:8080" GN3_LOCAL_URL="http://localhost:8081" ./bin/genenetwork2 +alias runpython="env GN2_PROFILE=~/opt/gn-latest TMPDIR=/tmp SERVER_PORT=5004 GENENETWORK_FILES=/gnu/data/gn2_data/ GN_PROXY_URL="http://localhost:8080" GN3_LOCAL_URL="http://localhost:8081" ./bin/genenetwork2 -alias runcmd="time env TMPDIR=//tmp SERVER_PORT=5004 GENENETWORK_FILES=/gnu/data/gn2_data/ GN_PROXY_URL="http://localhost:8080" GN3_LOCAL_URL="http://localhost:8081" ./bin/genenetwork2 ./etc/default_settings.py -cli" +alias runcmd="time env GN2_PROFILE=~/opt/gn-latest TMPDIR=//tmp SERVER_PORT=5004 GENENETWORK_FILES=/gnu/data/gn2_data/ GN_PROXY_URL="http://localhost:8080" GN3_LOCAL_URL="http://localhost:8081" ./bin/genenetwork2 ./etc/default_settings.py -cli" ``` Replace some of the env variables as per your use case. diff --git a/bin/genenetwork2 b/bin/genenetwork2 index f1e04eef..ea5a4f50 100755 --- a/bin/genenetwork2 +++ b/bin/genenetwork2 @@ -5,45 +5,55 @@ # # Typical usage # -# ./bin/genenetwork2 ~/my_settings.py +# env GN2_PROFILE=~/opt/genenetwork2-phewas ./bin/genenetwork2 ~/my_settings.py +# +# Where GN2_PROFILE points to the GNU Guix profile used for deployment. # # This will run the GN2 server (with default settings if none -# supplied). +# supplied). Typically you need a GNU Guix profile which is set with +# an environment variable (this profile is dictated by the +# installation path of genenetwork). Say your profile is in +# ~/opt/gn-latest-guix +# +# env GN2_PROFILE=~/opt/gn-latest ./bin/genenetwork2 +# +# You can pass in your own settings file, e.g. +# +# env GN2_PROFILE=~/opt/gn-latest ./bin/genenetwork2 ~/my_settings.py # # To run a maintenance python script with settings (instead of the # webserver) run from the base-dir with settings file and add that # script with a -c switch, e.g. # -# env TMPDIR=/export/local/home/zas1024/gn2-zach/tmp WEBSERVER_MODE=DEBUG LOG_LEVEL=DEBUG SERVER_PORT=5002 GENENETWORK_FILES=/export/local/home/zas1024/gn2-zach/genotype_files SQL_URI=mysql://webqtlout:webqtlout@lily.uthsc.edu/db_webqtl ./bin/genenetwork2 ./etc/default_settings.py -c ./maintenance/gen_select_dataset.py +# env GN2_PROFILE=/usr/local/guix-profiles/gn-latest-20190905 TMPDIR=/export/local/home/zas1024/gn2-zach/tmp WEBSERVER_MODE=DEBUG LOG_LEVEL=DEBUG SERVER_PORT=5002 GENENETWORK_FILES=/export/local/home/zas1024/gn2-zach/genotype_files SQL_URI=mysql://webqtlout:webqtlout@lily.uthsc.edu/db_webqtl ./bin/genenetwork2 ./etc/default_settings.py -c ./maintenance/gen_select_dataset.py # # To run any script in the environment # -# ./bin/genenetwork2 ./etc/default_settings.py -cli echo "HELLO WORLD" +# env GN2_PROFILE=~/opt/gn-latest ./bin/genenetwork2 ./etc/default_settings.py -cli echo "HELLO WORLD" # # To get a python REPL(!) # -# ./bin/genenetwork2 ./etc/default_settings.py -cli python +# env GN2_PROFILE=~/opt/gn-latest ./bin/genenetwork2 ./etc/default_settings.py -cli python # # For development you may want to run # -# env WEBSERVER_MODE=DEBUG LOG_LEVEL=DEBUG ./bin/genenetwork2 +# env GN2_PROFILE=~/opt/gn-latest WEBSERVER_MODE=DEBUG LOG_LEVEL=DEBUG ./bin/genenetwork2 # # For staging and production we use gunicorn. Run with something like # (note you have to provide the server port). Provide a settings file! # -# env SERVER_PORT=5003 ./bin/genenetwork2 ./etc/default_settings.py -gunicorn-prod +# env GN2_PROFILE=~/opt/gn-latest-guix SERVER_PORT=5003 ./bin/genenetwork2 ./etc/default_settings.py -gunicorn-prod # # For development use # -# env SERVER_PORT=5003 ./bin/genenetwork2 ./etc/default_settings.py -gunicorn-dev +# env GN2_PROFILE=~/opt/gn-latest-guix SERVER_PORT=5003 ./bin/genenetwork2 ./etc/default_settings.py -gunicorn-dev # # For extra flexibility you can also provide gunicorn parameters yourself with something like # -# ./bin/genenetwork2 ./etc/default_settings.py -gunicorn "--bind 0.0.0.0:5003 --workers=1 wsgi" +# env GN2_PROFILE=~/opt/gn-latest-guix ./bin/genenetwork2 ./etc/default_settings.py -gunicorn "--bind 0.0.0.0:5003 --workers=1 wsgi" SCRIPT=$(realpath "$0") echo SCRIPT=$SCRIPT -export GN2_PROFILE=$GUIX_ENVIRONMENT echo GN2_PROFILE=$GN2_PROFILE GN2_BASE_DIR=$(dirname $(dirname "$SCRIPT")) GN2_ID=$(cat /etc/hostname):$(basename $GN2_BASE_DIR) @@ -91,27 +101,40 @@ fi export GN2_SETTINGS=$settings # Python echo GN2_SETTINGS=$settings -export PATH=$GN2_PROFILE/bin:$PATH -export PYTHONPATH="$GN2_PROFILE/lib/python3.8/site-packages" # never inject another PYTHONPATH!! -export R_LIBS_SITE=$GN2_PROFILE/site-library -export JS_GUIX_PATH=$GN2_PROFILE/share/genenetwork2/javascript -export GUIX_GTK3_PATH="$GN2_PROFILE/lib/gtk-3.0" -export GI_TYPELIB_PATH="$GN2_PROFILE/lib/girepository-1.0" -export XDG_DATA_DIRS="$GN2_PROFILE/share" -export GIO_EXTRA_MODULES="$GN2_PROFILE/lib/gio/modules" -export LC_ALL=C # FIXME -export GUIX_GENENETWORK_FILES="$GN2_PROFILE/share/genenetwork2" -export PLINK_COMMAND="$GN2_PROFILE/bin/plink2" -export GEMMA_COMMAND="$GN2_PROFILE/bin/gemma" -if [ -z $GEMMA_WRAPPER_COMMAND ]; then - export GEMMA_WRAPPER_COMMAND="$GN2_PROFILE/bin/gemma-wrapper" +if [ -z $GN2_PROFILE ] ; then + echo "WARNING: GN2_PROFILE has not been set - you need the environment, so I hope you know what you are doing!" + export GN2_PROFILE=$(dirname $(dirname $(which genenetwork2))) + if [ -d $GN2_PROFILE ]; then + echo "Best guess is $GN2_PROFILE" + fi + echo "ERROR: always set GN2_PROFILE" + exit 1 +fi +if [ -z $GN2_PROFILE ]; then + read -p "PRESS [ENTER] TO CONTINUE..." +else + export PATH=$GN2_PROFILE/bin:$PATH + export PYTHONPATH="$GN2_PROFILE/lib/python3.8/site-packages" # never inject another PYTHONPATH!! + export R_LIBS_SITE=$GN2_PROFILE/site-library + export JS_GUIX_PATH=$GN2_PROFILE/share/genenetwork2/javascript + export GUIX_GTK3_PATH="$GN2_PROFILE/lib/gtk-3.0" + export GI_TYPELIB_PATH="$GN2_PROFILE/lib/girepository-1.0" + export XDG_DATA_DIRS="$GN2_PROFILE/share" + export GIO_EXTRA_MODULES="$GN2_PROFILE/lib/gio/modules" + export LC_ALL=C # FIXME + export GUIX_GENENETWORK_FILES="$GN2_PROFILE/share/genenetwork2" + export PLINK_COMMAND="$GN2_PROFILE/bin/plink2" + export GEMMA_COMMAND="$GN2_PROFILE/bin/gemma" + if [ -z $GEMMA_WRAPPER_COMMAND ]; then + export GEMMA_WRAPPER_COMMAND="$GN2_PROFILE/bin/gemma-wrapper" + fi + while IFS=":" read -ra PPATH; do + for PPART in "${PPATH[@]}"; do + if [ ! -d $PPART ] ; then echo "$PPART in PYTHONPATH not valid $PYTHONPATH" ; exit 1 ; fi + done + done <<< "$PYTHONPATH" + if [ ! -d $R_LIBS_SITE ] ; then echo "R_LIBS_SITE not valid "$R_LIBS_SITE ; exit 1 ; fi fi -while IFS=":" read -ra PPATH; do - for PPART in "${PPATH[@]}"; do - if [ ! -d $PPART ] ; then echo "$PPART in PYTHONPATH not valid $PYTHONPATH" ; exit 1 ; fi - done -done <<< "$PYTHONPATH" -if [ ! -d $R_LIBS_SITE ] ; then echo "R_LIBS_SITE not valid "$R_LIBS_SITE ; exit 1 ; fi if [ -z $PYTHONPATH ] ; then echo "ERROR PYTHONPATH has not been set - use GN2_PROFILE!" exit 1 -- cgit v1.2.3 From 631504680ce31b1a181cf6617e87ef0cc2cefa26 Mon Sep 17 00:00:00 2001 From: zsloan Date: Fri, 28 Jan 2022 13:40:10 -0600 Subject: Revert 'bin: Set shebang to sh instead of bash' It seems that this also needed to be reverted, because I was getting the error below (which was prevented by reverting to #! /bin/bash): ./bin/genenetwork2: 135: ./bin/genenetwork2: Syntax error: redirection unexpected--- bin/genenetwork2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/genenetwork2 b/bin/genenetwork2 index ea5a4f50..ea70835b 100755 --- a/bin/genenetwork2 +++ b/bin/genenetwork2 @@ -1,4 +1,4 @@ -#! /bin/sh -e +#! /bin/bash # # This is the startup script for GN2. It sets the environment variables to pick # up a Guix profile and allows for overriding parameters. -- cgit v1.2.3 From b9f16a3b28c4d996ac91befa5a919a9e0a7ac981 Mon Sep 17 00:00:00 2001 From: zsloan Date: Fri, 28 Jan 2022 14:53:53 -0600 Subject: Update PYTHONPATH The PYTHONPATH apparently needs to be updated to 3.9, because 3.8 throws an error (at least on production).--- bin/genenetwork2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/genenetwork2 b/bin/genenetwork2 index ea70835b..1d645c23 100755 --- a/bin/genenetwork2 +++ b/bin/genenetwork2 @@ -114,7 +114,7 @@ if [ -z $GN2_PROFILE ]; then read -p "PRESS [ENTER] TO CONTINUE..." else export PATH=$GN2_PROFILE/bin:$PATH - export PYTHONPATH="$GN2_PROFILE/lib/python3.8/site-packages" # never inject another PYTHONPATH!! + export PYTHONPATH="$GN2_PROFILE/lib/python3.9/site-packages" # never inject another PYTHONPATH!! export R_LIBS_SITE=$GN2_PROFILE/site-library export JS_GUIX_PATH=$GN2_PROFILE/share/genenetwork2/javascript export GUIX_GTK3_PATH="$GN2_PROFILE/lib/gtk-3.0" -- cgit v1.2.3 From a01033438a85e3097e4ade30e9a208fd5109d4e1 Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Mon, 6 Dec 2021 17:58:28 +0300 Subject: Add "Partial Correlations" UI elements on collections page Issue: https://github.com/genenetwork/gn-gemtext-threads/blob/main/topics/gn1-migration-to-gn2/partial-correlations.gmi * Add UI elements to trigger the partial correlations feature * Connect partial correlation view to the application * Point to correct application element for gunicorn --- bin/genenetwork2 | 2 +- wqflask/wqflask/__init__.py | 1 + wqflask/wqflask/partial_correlations_views.py | 263 +++++++++++++++++++++ wqflask/wqflask/templates/collections/view.html | 17 ++ .../wqflask/templates/partial_correlations.html | 199 ++++++++++++++++ 5 files changed, 481 insertions(+), 1 deletion(-) create mode 100644 wqflask/wqflask/partial_correlations_views.py create mode 100644 wqflask/wqflask/templates/partial_correlations.html diff --git a/bin/genenetwork2 b/bin/genenetwork2 index 1d645c23..0a2c18cf 100755 --- a/bin/genenetwork2 +++ b/bin/genenetwork2 @@ -192,7 +192,7 @@ if [ "$1" = '-gunicorn-dev' ] ; then cd $GN2_BASE_DIR/wqflask echo PYTHONPATH=$PYTHONPATH if [ -z $SERVER_PORT ]; then echo "ERROR: Provide a SERVER_PORT" ; exit 1 ; fi - cmd="--bind 0.0.0.0:$SERVER_PORT --workers=1 --timeout 180 --reload wsgi" + cmd="--bind 0.0.0.0:$SERVER_PORT --workers=1 --timeout 180 --reload run_gunicorn:app" echo RUNNING gunicorn $cmd gunicorn $cmd exit $? diff --git a/wqflask/wqflask/__init__.py b/wqflask/wqflask/__init__.py index 05e040ed..ab8b9e66 100644 --- a/wqflask/wqflask/__init__.py +++ b/wqflask/wqflask/__init__.py @@ -98,3 +98,4 @@ from wqflask import user_login from wqflask import user_session import wqflask.views +import wqflask.partial_correlations_views diff --git a/wqflask/wqflask/partial_correlations_views.py b/wqflask/wqflask/partial_correlations_views.py new file mode 100644 index 00000000..bee0a033 --- /dev/null +++ b/wqflask/wqflask/partial_correlations_views.py @@ -0,0 +1,263 @@ +from typing import Union, Tuple + +import MySQLdb +from gn3.db.traits import retrieve_trait_info +from flask import flash, request, current_app, render_template +from gn3.computations.partial_correlations import partial_correlations_entry + +from wqflask import app + +def parse_trait(trait_str: str) -> Union[dict, None]: + keys = ("name", "dataset", "symbol", "description", "data_hmac") + parts = tuple(part.strip() for part in trait_str.split(":::")) + if len(parts) == len(keys): + return dict(zip(keys, parts)) + return None + +def process_step_select_primary( + primary_trait: dict, control_traits: Tuple[dict, ...], + target_traits: Tuple[dict, ...], + traits_list: Tuple[dict, ...], corr_method: str) -> Tuple[ + str, dict, Tuple[dict, ...], Tuple[dict, ...], Tuple[dict, ...], + str]: + if primary_trait is None: + flash("You must select a primary trait", "alert-danger") + return ( + "select-primary", primary_trait, control_traits, target_traits, + traits_list, corr_method) + + return ( + "select-controls", primary_trait, control_traits, target_traits, + tuple( + trait for trait in traits_list + if trait["data_hmac"] != primary_trait["data_hmac"]), + corr_method) + +def process_step_select_controls( + primary_trait: dict, control_traits: Tuple[dict, ...], + target_traits: Tuple[dict, ...], + traits_list: Tuple[dict, ...], corr_method: str) -> Tuple[ + str, dict, Tuple[dict, ...], Tuple[dict, ...], Tuple[dict, ...], + str]: + if len(control_traits) == 0 or len(control_traits) > 3: + flash( + ("You must select a minimum of one control trait, up to a maximum " + "of three control traits."), + "alert-danger") + return ( + "select-controls", primary_trait, control_traits, target_traits, + traits_list, corr_method) + + hmacs =(primary_trait["data_hmac"],) + tuple( + trait["data_hmac"] for trait in control_traits) + return ( + "select-targets", primary_trait, control_traits, target_traits, + tuple( + trait for trait in traits_list if trait["data_hmac"] not in hmacs), + corr_method) + +def process_step_select_targets( + primary_trait: dict, control_traits: Tuple[dict, ...], + target_traits: Tuple[dict, ...], + traits_list: Tuple[dict, ...], corr_method: str) -> Tuple[ + str, dict, Tuple[dict, ...], Tuple[dict, ...], Tuple[dict, ...], + str]: + if len(target_traits) == 0: + flash( + "You must select at least one target trait.", "alert-danger") + return ( + "select-targets", primary_trait, control_traits, target_traits, + traits_list, corr_method) + + hmacs =(primary_trait["data_hmac"],) + tuple( + trait["data_hmac"] for trait in (control_traits + target_traits)) + return ( + "select-corr-method", primary_trait, control_traits, target_traits, + tuple( + trait for trait in traits_list if trait["data_hmac"] not in hmacs), + corr_method) + +def process_step_select_corr_method( + primary_trait: dict, control_traits: Tuple[dict, ...], + target_traits: Tuple[dict, ...], + traits_list: Tuple[dict, ...], corr_method: str) -> Tuple[ + str, dict, Tuple[dict, ...], Tuple[dict, ...], Tuple[dict, ...], + str]: + methods = ( + "genetic correlation, pearson's r", + "genetic correlation, spearman's rho", + "sgo literature correlation", + "tissue correlation, pearson's r", + "tissue correlation, spearman's rho") + if corr_method.lower() not in methods: + flash( + "Selected method is unknown.", "alert-danger") + return ( + "select-corr-method", primary_trait, control_traits, target_traits, + traits_list, corr_method) + + hmacs =(primary_trait["data_hmac"],) + tuple( + trait["data_hmac"] for trait in (control_traits + target_traits)) + return ( + "run-correlation", primary_trait, control_traits, target_traits, + tuple( + trait for trait in traits_list if trait["data_hmac"] not in hmacs), + corr_method) + +def process_step( + step: str, primary_trait: dict, control_traits: Tuple[dict, ...], + target_traits: Tuple[dict, ...], traits_list: Tuple[dict, ...], + corr_method: str) -> Tuple[ + str, dict, Tuple[dict, ...], Tuple[dict, ...], Tuple[dict, ...], + str]: + processor_functions = { + # "select-traits": lambda arg: arg, + "select-primary": process_step_select_primary, + "select-controls": process_step_select_controls, + "select-targets": process_step_select_targets, + "select-corr-method": process_step_select_corr_method + } + return processor_functions[(step or "select-primary")]( + primary_trait, control_traits, target_traits, traits_list, corr_method) + +def sequence_of_traits(trait_strs) -> Tuple[dict, ...]: + return tuple(filter( + lambda trt: trt is not None, + (parse_trait(tstr.strip()) for tstr in trait_strs))) + +def publish_target_dabases(conn, group, threshold): + query = ( + "SELECT PublishFreeze.FullName,PublishFreeze.Name " + "FROM PublishFreeze, InbredSet " + "WHERE PublishFreeze.InbredSetId = InbredSet.Id " + "AND InbredSet.Name = %(group)s " + "AND PublishFreeze.public > %(threshold)s") + with conn.cursor() as cursor: + cursor.execute(query, {"group": group, "threshold": threshold}) + res = cursor.fetchall() + if res: + return tuple( + dict(zip(("description", "value"), row)) for row in res) + + return tuple() + +def geno_target_databases(conn, group, threshold): + query = ( + "SELECT GenoFreeze.FullName,GenoFreeze.Name " + "FROM GenoFreeze, InbredSet " + "WHERE GenoFreeze.InbredSetId = InbredSet.Id " + "AND InbredSet.Name = %(group)s " + "AND GenoFreeze.public > %(threshold)s") + with conn.cursor() as cursor: + cursor.execute(query, {"group": group, "threshold": threshold}) + res = cursor.fetchall() + if res: + return tuple( + dict(zip(("description", "value"), row)) for row in res) + + return tuple() + +def probeset_target_databases(conn, group, threshold): + query1 = "SELECT Id, Name FROM Tissue order by Name" + query2 = ( + "SELECT ProbeFreeze.TissueId, ProbeSetFreeze.FullName, ProbeSetFreeze.Name " + "FROM ProbeSetFreeze, ProbeFreeze, InbredSet " + "WHERE ProbeSetFreeze.ProbeFreezeId = ProbeFreeze.Id " + "AND ProbeFreeze.TissueId IN %(tissue_ids)s " + "AND ProbeSetFreeze.public > %(threshold)s " + "AND ProbeFreeze.InbredSetId = InbredSet.Id " + "AND InbredSet.Name like %(group)s " + "ORDER BY ProbeSetFreeze.CreateTime desc, ProbeSetFreeze.AvgId") + with conn.cursor() as cursor: + cursor.execute(query1) + tissue_res = cursor.fetchall() + if tissue_res: + tissue_ids = tuple(row[0] for row in tissue_res) + cursor.execute( + query2,{ + "tissue_ids": tissue_ids, "group": f"{group}%%", + "threshold": threshold + }) + db_res = cursor.fetchall() + if db_res: + databases = tuple( + dict(zip(("tissue_id", "description", "value"), row)) + for row in db_res) + return tuple( + {tissue_name: tuple( + { + "value": item["value"], + "description": item["description"] + } for item in databases + if item["tissue_id"] == tissue_id)} + for tissue_id, tissue_name in tissue_res) + + return tuple() + +def target_databases(conn, step, trait, threshold): + """ + Retrieves the names of possible target databases from the database. + """ + if step != "select-corr-method": + return None + + trait_info = retrieve_trait_info( + threshold, f"{trait['dataset']}::{trait['name']}", conn) + group = trait_info["group"] + return ( + publish_target_dabases(conn, group, threshold) + + geno_target_databases(conn, group, threshold) + + probeset_target_databases(conn, group, threshold)) + +def pcorrelations(conn, values): + if values["step"] != "run-correlation": + return None + + def trait_fullname(trait): + return f"{trait['dataset']}::{trait['name']}" + + return partial_correlations_entry( + conn, trait_fullname(values["primary_trait"]), + tuple(trait_fullname(trait) for trait in values["control_traits"]), + values["method"], values["criteria"], values["target_db"]) + +@app.route("/partial_correlations", methods=("POST",)) +def partial_correlations(): + form = request.form + traits_list = tuple(filter( + lambda trt: trt is not None, + (parse_trait(tstr) for tstr in form.get("traits_list", "").split("|||")))) + + args_dict = dict(zip( + ("step", "primary_trait", "control_traits", "target_traits", + "traits_list", "method"), + process_step( + form.get("step", None), + parse_trait(form.get("primary_trait", "")), + sequence_of_traits( + form.getlist("control_traits[]") or + form.get("control_traits", "").split("|||")), + sequence_of_traits( + form.getlist("target_traits[]") or + form.get("target_traits", "").split("|||")), + sequence_of_traits(form.get("traits_list", "").split("|||")), + form.get("method")))) + + conn = MySQLdb.Connect( + db=current_app.config.get("DB_NAME"), + user=current_app.config.get("DB_USER"), + passwd=current_app.config.get("DB_PASS"), + host=current_app.config.get("DB_HOST")) + target_dbs = target_databases( + conn, args_dict["step"], args_dict["primary_trait"], 0) + + if args_dict["step"] == "run-correlation": + args_dict = { + **args_dict, "target_db": form.get("target_db"), + "criteria": int(form.get("criteria", 500))} + + corr_results = pcorrelations(conn, args_dict) + + return render_template( + "partial_correlations.html", **args_dict, target_dbs=target_dbs, + corr_results=corr_results) diff --git a/wqflask/wqflask/templates/collections/view.html b/wqflask/wqflask/templates/collections/view.html index 0ded66a6..f4270b67 100644 --- a/wqflask/wqflask/templates/collections/view.html +++ b/wqflask/wqflask/templates/collections/view.html @@ -34,6 +34,23 @@
+
+ + + + +

diff --git a/wqflask/wqflask/templates/partial_correlations.html b/wqflask/wqflask/templates/partial_correlations.html new file mode 100644 index 00000000..b61f7fc4 --- /dev/null +++ b/wqflask/wqflask/templates/partial_correlations.html @@ -0,0 +1,199 @@ +{%extends "base.html"%} + +{%block content%} +
+ + {%with messages = get_flashed_messages(with_categories=true)%} + {%if messages:%} +
    + {%for category, message in messages:%} +
  • {{message}}
  • + {%endfor%} +
+ {%endif%} + {%endwith%} + + + + + {%if primary_trait:%} + +

+ Primary Trait: {{primary_trait["name"]}} -  + {{primary_trait["symbol"]}} - {{primary_trait["description"]}} +

+ {%endif%} + + {%if control_traits:%} + +

+ Control Traits: +

    + {%for trait in control_traits:%} +
  • + {{trait["name"]}} - {{trait["symbol"]}} -  + {{trait["description"]}}
  • + {%endfor%} +
+

+ {%endif%} + + {%if target_traits:%} + +

+ Target Traits: +

    + {%for trait in target_traits:%} +
  • + {{trait["name"]}} - {{trait["symbol"]}} -  + {{trait["description"]}}
  • + {%endfor%} +
+

+ {%endif%} + + + + {%if step == "select-primary":%} +

Please select the primary trait (X)

+ {%for trait in traits_list:%} +
+ + +
+ {%endfor%} + + {%endif%} + + + + {%if step == "select-controls":%} + +

Select a maximum of three (3) control traits (Z)

+ {%for trait in traits_list:%} +
+ + +
+ {%endfor%} + + {%endif%} + + + + {%if step == "select-targets":%} +

Select at least one target trait (Y)

+ {%for trait in traits_list:%} +
+ + +
+ {%endfor%} + + {%endif%} + + + + {%if step == "select-corr-method":%} +
+ + +
+ +
+ + +
+ +
+ + +
+ + + {%endif%} + + {%if step == "run-correlation":%} + + + + + {{corr_results}} + {%endif%} + +
+{%endblock%} -- cgit v1.2.3 From c0d4de52e28135b002ab18ff59ac0fb6fd827a4f Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Fri, 10 Dec 2021 09:52:38 +0300 Subject: Automate setting of the development GN3 repo * When developing the GN2 code, there are times we need to use the latest code in GN3, and rather than manually editing the `bin/genenetwork` file every time we need that, this commit enables the use of an environment variable. In this case, launching the application with something like: $ env GN3_DEV_REPO_PATH="$HOME/genenetwork3" ...\ ./bin/genenetwork2 ./the_settings.py -gunicorn-dev will add the path to the development version to the PYTHONPATH. If the GN3_DEV_REPO_PATH variable is not set, then the PYTHONPATH is not altered, and would be as if there is no development version of GN3 in use. --- bin/genenetwork2 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bin/genenetwork2 b/bin/genenetwork2 index 0a2c18cf..1b933e80 100755 --- a/bin/genenetwork2 +++ b/bin/genenetwork2 @@ -149,6 +149,8 @@ fi # We may change this one: export PYTHONPATH=$PYTHON_GN_PATH:$GN2_BASE_DIR/wqflask:$GN3_PYTHONPATH:$PYTHONPATH +export PYTHONPATH="${GN3_DEV_REPO_PATH:+$GN3_DEV_REPO_PATH:}:${PYTHONPATH}" + # Our UNIX TMPDIR defaults to /tmp - change this on a shared server if [ -z $TMPDIR ]; then TMPDIR="/tmp" -- cgit v1.2.3 From db0fc679dcef0360c4b99d28d9126026b893af70 Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Fri, 10 Dec 2021 09:06:31 +0300 Subject: Implement better styling Issue: https://github.com/genenetwork/gn-gemtext-threads/blob/main/topics/gn1-migration-to-gn2/partial-correlations.gmi * Separate the form elements that allow selection from the elements that display previous selections. This allows styling of the "active" area of the form in a different way from the "display" area of the form, allowing the user to see information on their previous choices, even while making further selections for the partial correlations. --- .../static/new/css/partial_correlations.css | 30 +++++ .../wqflask/templates/partial_correlations.html | 140 ++++++++++++--------- 2 files changed, 108 insertions(+), 62 deletions(-) create mode 100644 wqflask/wqflask/static/new/css/partial_correlations.css diff --git a/wqflask/wqflask/static/new/css/partial_correlations.css b/wqflask/wqflask/static/new/css/partial_correlations.css new file mode 100644 index 00000000..ebc00302 --- /dev/null +++ b/wqflask/wqflask/static/new/css/partial_correlations.css @@ -0,0 +1,30 @@ +#partial-correlations-form { + width: 100%; + display: grid; + grid-column-gap: 1em; +} + +#main-form { + grid-column-start: 1; + grid-column-end: 2; +} + +#form-display-area { + grid-column-start: 2; + grid-column-end: 3; +} + +.label-element-combo { + width: 100%; + display: grid; +} + +.with-trait > label { + width: 80%; + grid-column-start: 2; +} + +.with-trait > input { + width: 20%; + grid-column-start: 2; +} diff --git a/wqflask/wqflask/templates/partial_correlations.html b/wqflask/wqflask/templates/partial_correlations.html index b61f7fc4..ddf6fb4e 100644 --- a/wqflask/wqflask/templates/partial_correlations.html +++ b/wqflask/wqflask/templates/partial_correlations.html @@ -1,10 +1,17 @@ {%extends "base.html"%} +{%block title%}Partial Correlations:{%endblock%} + +{%block css%} + +{%endblock%} + {%block content%} -
-
+ + +
{%with messages = get_flashed_messages(with_categories=true)%} {%if messages:%}
    @@ -15,60 +22,10 @@ {%endif%} {%endwith%} - - - - {%if primary_trait:%} - -

    - Primary Trait: {{primary_trait["name"]}} -  - {{primary_trait["symbol"]}} - {{primary_trait["description"]}} -

    - {%endif%} - - {%if control_traits:%} - -

    - Control Traits: -

      - {%for trait in control_traits:%} -
    • - {{trait["name"]}} - {{trait["symbol"]}} -  - {{trait["description"]}}
    • - {%endfor%} -
    -

    - {%endif%} - - {%if target_traits:%} - -

    - Target Traits: -

      - {%for trait in target_traits:%} -
    • - {{trait["name"]}} - {{trait["symbol"]}} -  - {{trait["description"]}}
    • - {%endfor%} -
    -

    - {%endif%} - - - {%if step == "select-primary":%}

    Please select the primary trait (X)

    {%for trait in traits_list:%} -
    +
    Select a maximum of three (3) control traits (Z)

    {%for trait in traits_list:%} -
    +
    Select at least one target trait (Y)

    {%for trait in traits_list:%} -
    +
    +
    -
    +
    -
    +
    + + + {%if primary_trait:%} + + +
    +
    Primary Trait (X)
    +
    + {{primary_trait["name"]}} -  + {{primary_trait["symbol"]}} - {{primary_trait["description"]}} +
    +
    + {%endif%} + + {%if target_traits:%} + +
    +
    Target Traits (Y)
    +
    +
      + {%for trait in target_traits:%} +
    • + {{trait["name"]}} - {{trait["symbol"]}} -  + {{trait["description"]}}
    • + {%endfor%} +
    +
    +
    + {%endif%} + + {%if control_traits:%} + +
    +
    Control Traits (Z)
    +
    +
      + {%for trait in control_traits:%} +
    • + {{trait["name"]}} - {{trait["symbol"]}} -  + {{trait["description"]}}
    • + {%endfor%} +
    +
    +
    + {%endif%} +
    + + {%endblock%} -- cgit v1.2.3 From 2822237883b9a91fb35f33fb9094d2dcb9e44855 Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Mon, 13 Dec 2021 07:24:57 +0300 Subject: Skip the target traits selection step Issue: https://github.com/genenetwork/gn-gemtext-threads/blob/main/topics/gn1-migration-to-gn2/partial-correlations.gmi * Skip the target traits selection step, and only allow running the partial correlations against one of the available databases. --- wqflask/wqflask/partial_correlations_views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wqflask/wqflask/partial_correlations_views.py b/wqflask/wqflask/partial_correlations_views.py index bee0a033..9a443897 100644 --- a/wqflask/wqflask/partial_correlations_views.py +++ b/wqflask/wqflask/partial_correlations_views.py @@ -51,7 +51,7 @@ def process_step_select_controls( hmacs =(primary_trait["data_hmac"],) + tuple( trait["data_hmac"] for trait in control_traits) return ( - "select-targets", primary_trait, control_traits, target_traits, + "select-corr-method", primary_trait, control_traits, target_traits, tuple( trait for trait in traits_list if trait["data_hmac"] not in hmacs), corr_method) -- cgit v1.2.3 From 6c7231c0dec704ad2722e12190914ce5cf8822fd Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Fri, 17 Dec 2021 11:01:32 +0300 Subject: Use list rather than tuple Issue: https://github.com/genenetwork/gn-gemtext-threads/blob/main/topics/gn1-migration-to-gn2/partial-correlations.gmi * Using a tuple, rather than a list for the HTTP methods in the route definition causes an error. This fixes that by using a list. --- wqflask/wqflask/partial_correlations_views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wqflask/wqflask/partial_correlations_views.py b/wqflask/wqflask/partial_correlations_views.py index 9a443897..0b7ffd37 100644 --- a/wqflask/wqflask/partial_correlations_views.py +++ b/wqflask/wqflask/partial_correlations_views.py @@ -221,7 +221,7 @@ def pcorrelations(conn, values): tuple(trait_fullname(trait) for trait in values["control_traits"]), values["method"], values["criteria"], values["target_db"]) -@app.route("/partial_correlations", methods=("POST",)) +@app.route("/partial_correlations", methods=["POST"]) def partial_correlations(): form = request.form traits_list = tuple(filter( -- cgit v1.2.3 From 5027abd962dfc599a9885d12a556fd12c37cadfa Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Fri, 17 Dec 2021 11:03:23 +0300 Subject: Add elements to display results. Provide PoC JS Issue: https://github.com/genenetwork/gn-gemtext-threads/blob/main/topics/gn1-migration-to-gn2/partial-correlations.gmi * Add some html elements to be used to display the results of running the partial correlations * Provide some initial proof-of-concept javascript code to animate the various elements and to use for displaying both successful and error results. --- wqflask/wqflask/partial_correlations_views.py | 3 +- .../static/new/javascript/partial_correlations.js | 117 +++++++++++++++++++++ .../wqflask/templates/partial_correlations.html | 23 +++- 3 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 wqflask/wqflask/static/new/javascript/partial_correlations.js diff --git a/wqflask/wqflask/partial_correlations_views.py b/wqflask/wqflask/partial_correlations_views.py index 0b7ffd37..82287312 100644 --- a/wqflask/wqflask/partial_correlations_views.py +++ b/wqflask/wqflask/partial_correlations_views.py @@ -6,6 +6,7 @@ from flask import flash, request, current_app, render_template from gn3.computations.partial_correlations import partial_correlations_entry from wqflask import app +from utility.tools import GN_SERVER_URL def parse_trait(trait_str: str) -> Union[dict, None]: keys = ("name", "dataset", "symbol", "description", "data_hmac") @@ -260,4 +261,4 @@ def partial_correlations(): return render_template( "partial_correlations.html", **args_dict, target_dbs=target_dbs, - corr_results=corr_results) + corr_results=corr_results, part_corr_url=f"{GN_SERVER_URL}correlation/partial") diff --git a/wqflask/wqflask/static/new/javascript/partial_correlations.js b/wqflask/wqflask/static/new/javascript/partial_correlations.js new file mode 100644 index 00000000..71fac21d --- /dev/null +++ b/wqflask/wqflask/static/new/javascript/partial_correlations.js @@ -0,0 +1,117 @@ +/** + * This is, hopefully, a short-term stop-gap measure to get the system working + * and to get some feedback, even as better optimisation is done in the + * background to get better response/performance for the partial correlation + * computations + */ + +function key_value(keys, values) { + if(!(keys.length == values.length)) { + Error("The 'keys' and 'values' objects MUST be the same length"); + return null; + } + return values.reduce(function(accumulator, item, index) { + accumulator[keys[index]] = item; + return accumulator; + }, {}); +} + +function trait(trait_str) { + return key_value( + ["name", "dataset", "symbol", "description", "data_hmac"], + trait_str.split(":::")); +} + +function primary_trait() { + trait_string = document.querySelector( + "#partial-correlations-form input[name=primary_trait]").value; + return trait(trait_string); +} + +function control_traits() { + return document.querySelector( + "#partial-correlations-form input[name=control_traits]").value.split( + "|||").map(trait).filter(trait => !(trait === null)); +} + +function correlation_method() { + return document.querySelector( + "#partial-correlations-form select[name=method]").value; +} + +function criteria() { + return document.querySelector( + "#partial-correlations-form select[name=criteria]").value; +} + +function target_db() { + return document.querySelector( + "#partial-correlations-form select[name=target_db]").value; +} + +function partial_corr_request_data() { + return { + "primary_trait": primary_trait(), + "control_traits": control_traits(), + "method": correlation_method(), + "criteria": criteria(), + "target_db": target_db() + } +} + +function display_partial_corr_results(data, status, xhr) { + progress_indicator = document.getElementById( + "partial-correlations-progress-indicator").style.display = "none"; + parent = document.getElementById("part-corr-success"); + child = document.createElement("p"); + child.textContent = data; + parent.appendChild(child); +} + +function display_partial_corr_error(xhr, status, error) { + document.getElementById( + "partial-correlations-progress-indicator").style.display = "none"; + error_element = document.getElementById("part-corr-error"); + panel = document.createElement("div"); + panel.setAttribute("class", "panel panel-danger"); + error_element.appendChild(panel); + + panel_header = document.createElement("div"); + panel_header.setAttribute("class", "panel-heading"); + panel_header.textContent = "Error: " + xhr.status; + panel.appendChild(panel_header); + + panel_body = document.createElement("div"); + panel_body.setAttribute("class", "panel-body"); + panel_body.textContent = xhr.statusText; + panel.appendChild(panel_body); + console.log(xhr) +} + +function send_data_and_process_results( + remote_url, request_data, success_fn, error_fn, indicator_id) { + document.getElementById(indicator_id).style.display = "block"; + $.ajax({ + type: "POST", + url: remote_url, + contentType: "application/json", + data: JSON.stringify(request_data), + dataType: "JSON", + success: success_fn, + error: error_fn + }); +} + +$("#partial-correlations-form").submit(function(e) { + e.preventDefault(); +}); + +$("#run-partial-corr-button").click(function(evt) { + send_data_and_process_results( + document.getElementById( + "run-partial-corr-button").getAttribute("data-url"), + partial_corr_request_data(), + display_partial_corr_results, + display_partial_corr_error, + "partial-correlations-progress-indicator"); +}) diff --git a/wqflask/wqflask/templates/partial_correlations.html b/wqflask/wqflask/templates/partial_correlations.html index ddf6fb4e..08d6fc2a 100644 --- a/wqflask/wqflask/templates/partial_correlations.html +++ b/wqflask/wqflask/templates/partial_correlations.html @@ -139,7 +139,10 @@
    - {%endif%} @@ -212,4 +215,22 @@
    + +
    + + + +
    +
    +
    +
    +
    +{%endblock%} + +{%block js%} +{%if step == "select-corr-method":%} + +{%endif%} {%endblock%} -- cgit v1.2.3 From 37c668eb1c3d9ad06f8caaf78ac77b69515b6956 Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Sat, 18 Dec 2021 08:11:37 +0300 Subject: Add templates for the results tables Issue: https://github.com/genenetwork/gn-gemtext-threads/blob/main/topics/gn1-migration-to-gn2/partial-correlations.gmi * Add templates for the tables that will display the results. --- .../static/new/javascript/partial_correlations.js | 1 + .../wqflask/templates/partial_correlations.html | 121 +++++++++++++++++++++ 2 files changed, 122 insertions(+) diff --git a/wqflask/wqflask/static/new/javascript/partial_correlations.js b/wqflask/wqflask/static/new/javascript/partial_correlations.js index 71fac21d..b1b12ee7 100644 --- a/wqflask/wqflask/static/new/javascript/partial_correlations.js +++ b/wqflask/wqflask/static/new/javascript/partial_correlations.js @@ -66,6 +66,7 @@ function display_partial_corr_results(data, status, xhr) { child = document.createElement("p"); child.textContent = data; parent.appendChild(child); + console.log(data); } function display_partial_corr_error(xhr, status, error) { diff --git a/wqflask/wqflask/templates/partial_correlations.html b/wqflask/wqflask/templates/partial_correlations.html index 08d6fc2a..215ba32e 100644 --- a/wqflask/wqflask/templates/partial_correlations.html +++ b/wqflask/wqflask/templates/partial_correlations.html @@ -222,6 +222,127 @@
    +

Loading...
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
-- cgit v1.2.3 From aea641e0a6fbd0ea68241efffb7c280222491238 Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Mon, 20 Dec 2021 07:58:39 +0300 Subject: Display data for "Publish" datasets Issue: https://github.com/genenetwork/gn-gemtext-threads/blob/main/topics/gn1-migration-to-gn2/partial-correlations.gmi * Display the correlations results for "Publish" datasets. --- .../static/new/css/partial_correlations.css | 10 ++++ .../static/new/javascript/partial_correlations.js | 65 ++++++++++++++++++++-- 2 files changed, 71 insertions(+), 4 deletions(-) diff --git a/wqflask/wqflask/static/new/css/partial_correlations.css b/wqflask/wqflask/static/new/css/partial_correlations.css index ebc00302..41dd04df 100644 --- a/wqflask/wqflask/static/new/css/partial_correlations.css +++ b/wqflask/wqflask/static/new/css/partial_correlations.css @@ -7,6 +7,7 @@ #main-form { grid-column-start: 1; grid-column-end: 2; + text-align: left; } #form-display-area { @@ -28,3 +29,12 @@ width: 20%; grid-column-start: 2; } + +td, th { + border: 1px solid; + text-align: left; +} + +tr:nth-of-type(2n) { + background: #DDDDDD; +} diff --git a/wqflask/wqflask/static/new/javascript/partial_correlations.js b/wqflask/wqflask/static/new/javascript/partial_correlations.js index b1b12ee7..e61dc250 100644 --- a/wqflask/wqflask/static/new/javascript/partial_correlations.js +++ b/wqflask/wqflask/static/new/javascript/partial_correlations.js @@ -59,14 +59,71 @@ function partial_corr_request_data() { } } +function rho_or_r(method) { + if (method === "spearman") { + return "rho"; + } + return "r"; +} + +function display_publish_results(primary, controls, correlations, method) { + table = document.getElementById("part-corr-results-publish"); + table.setAttribute("style", "display: block;"); + table_body = document.querySelector("#part-corr-results-publish tbody"); + template_row = document.querySelector( + "#part-corr-results-publish tr.template-publish-results-row"); + correlations.forEach(function(item, index, arr) { + new_row = template_row.cloneNode(true); + new_row.setAttribute("class", "results-row"); + new_row.querySelector( + 'td[data-column-heading="Record"]').innerHTML = item["trait_name"]; + new_row.querySelector( + 'td[data-column-heading="Phenotype"]').innerHTML = ( + item["post_publication_description"]); + new_row.querySelector( + 'td[data-column-heading="Authors"]').innerHTML = item["authors"]; + new_row.querySelector( + 'td[data-column-heading="Year"]').innerHTML = item["year"]; + new_row.querySelector( + 'td[data-column-heading="N"]').innerHTML = item["noverlap"]; + new_row.querySelector( + `td[data-column-heading="Partial ${rho_or_r(method)}"]` + ).innerHTML = item["partial_corr"]; + new_row.querySelector( + `td[data-column-heading="p(partial ${rho_or_r(method)})"]` + ).innerHTML = item["partial_corr_p_value"]; + new_row.querySelector( + `td[data-column-heading="${rho_or_r(method)}"]` + ).innerHTML = item["corr"]; + new_row.querySelector( + `td[data-column-heading="p(${rho_or_r(method)})"]` + ).innerHTML = item["corr_p_value"]; + new_row.querySelector( + `td[data-column-heading="delta ${rho_or_r(method)}"]` + ).innerHTML = format_number(item["delta"]); + table_body.appendChild(new_row); + }); + table_body.removeChild(template_row); +} + +function display_geno_results(primary, controls, correlations) {} +function display_probeset_results(primary, controls, correlations) {} + function display_partial_corr_results(data, status, xhr) { progress_indicator = document.getElementById( "partial-correlations-progress-indicator").style.display = "none"; - parent = document.getElementById("part-corr-success"); - child = document.createElement("p"); - child.textContent = data; - parent.appendChild(child); console.log(data); + display_functions = { + "Publish": display_publish_results, + "Geno": display_geno_results, + "ProbeSet": display_probeset_results + } + + display_functions[data["results"]["dataset_type"]]( + data["results"]["primary_traits"], + data["results"]["control_traits"], + data["results"]["correlations"], + data["results"]["method"]); } function display_partial_corr_error(xhr, status, error) { -- cgit v1.2.3 From 53985f5ac92d31f38748d101c9bb1574b11e5f73 Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Mon, 20 Dec 2021 11:49:22 +0300 Subject: Format numbers for display Issue: https://github.com/genenetwork/gn-gemtext-threads/blob/main/topics/gn1-migration-to-gn2/partial-correlations.gmi * Format the numbers for display, to reduce the number of columns needed for display. --- .../static/new/javascript/partial_correlations.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/wqflask/wqflask/static/new/javascript/partial_correlations.js b/wqflask/wqflask/static/new/javascript/partial_correlations.js index e61dc250..fdba247f 100644 --- a/wqflask/wqflask/static/new/javascript/partial_correlations.js +++ b/wqflask/wqflask/static/new/javascript/partial_correlations.js @@ -66,6 +66,16 @@ function rho_or_r(method) { return "r"; } +function format_number(num) { + if(num === null) { + return NaN; + } + if(Math.abs(num) <= 1.04e-4) { + return num.toExponential(2); + } + return num.toFixed(5); +} + function display_publish_results(primary, controls, correlations, method) { table = document.getElementById("part-corr-results-publish"); table.setAttribute("style", "display: block;"); @@ -88,16 +98,16 @@ function display_publish_results(primary, controls, correlations, method) { 'td[data-column-heading="N"]').innerHTML = item["noverlap"]; new_row.querySelector( `td[data-column-heading="Partial ${rho_or_r(method)}"]` - ).innerHTML = item["partial_corr"]; + ).innerHTML = format_number(item["partial_corr"]); new_row.querySelector( `td[data-column-heading="p(partial ${rho_or_r(method)})"]` - ).innerHTML = item["partial_corr_p_value"]; + ).innerHTML = format_number(item["partial_corr_p_value"]); new_row.querySelector( `td[data-column-heading="${rho_or_r(method)}"]` - ).innerHTML = item["corr"]; + ).innerHTML = format_number(item["corr"]); new_row.querySelector( `td[data-column-heading="p(${rho_or_r(method)})"]` - ).innerHTML = item["corr_p_value"]; + ).innerHTML = format_number(item["corr_p_value"]); new_row.querySelector( `td[data-column-heading="delta ${rho_or_r(method)}"]` ).innerHTML = format_number(item["delta"]); -- cgit v1.2.3 From 81355831321642f09f2dc606f0670e6b7ded79c9 Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Mon, 20 Dec 2021 11:50:36 +0300 Subject: Improve UI elements Issue: https://github.com/genenetwork/gn-gemtext-threads/blob/main/topics/gn1-migration-to-gn2/partial-correlations.gmi * Force results area to use up the entire width of the page * Set background colour and line-height of header row * Add some padding to the cells * Reorganise layout * Set up appropriate classes for .with-trait items --- .../static/new/css/partial_correlations.css | 30 +++++++++++++++++----- .../wqflask/templates/partial_correlations.html | 12 +++++---- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/wqflask/wqflask/static/new/css/partial_correlations.css b/wqflask/wqflask/static/new/css/partial_correlations.css index 41dd04df..a4f45ace 100644 --- a/wqflask/wqflask/static/new/css/partial_correlations.css +++ b/wqflask/wqflask/static/new/css/partial_correlations.css @@ -15,26 +15,44 @@ grid-column-end: 3; } +#part-corr-success { + grid-column-start: 1; + grid-column-end: 3; +} + .label-element-combo { width: 100%; display: grid; } -.with-trait > label { - width: 80%; - grid-column-start: 2; +.with-trait { + margin-left: 0.7em; + text-align: left; } -.with-trait > input { - width: 20%; +.with-trait .label-element { + width: 90%; grid-column-start: 2; + grid-column-end: 3 +} + +.with-trait .selector-element { + width: 1em; + grid-column-start: 1; + grid-column-end: 2 } td, th { border: 1px solid; text-align: left; + padding: 0.2em 0.5em 0.2em 0.7em; } tr:nth-of-type(2n) { - background: #DDDDDD; + background: #F9F9F9; +} + +thead tr { + background: #336699; + line-height: 1.5em; } diff --git a/wqflask/wqflask/templates/partial_correlations.html b/wqflask/wqflask/templates/partial_correlations.html index 215ba32e..800222ba 100644 --- a/wqflask/wqflask/templates/partial_correlations.html +++ b/wqflask/wqflask/templates/partial_correlations.html @@ -29,8 +29,9 @@ -
@@ -46,12 +47,13 @@

Select a maximum of three (3) control traits (Z)

{%for trait in traits_list:%} -
+
-
-- cgit v1.2.3 From ea728239a571e1991530d17d48d7b93b939b3b11 Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Tue, 21 Dec 2021 13:28:02 +0300 Subject: Display results for Geno and ProbeSet tables Issue: https://github.com/genenetwork/gn-gemtext-threads/blob/main/topics/gn1-migration-to-gn2/partial-correlations.gmi * Add function to display the results for the **Geno** and **ProbeSet** tables. --- .../static/new/javascript/partial_correlations.js | 92 +++++++++++++++++++++- 1 file changed, 90 insertions(+), 2 deletions(-) diff --git a/wqflask/wqflask/static/new/javascript/partial_correlations.js b/wqflask/wqflask/static/new/javascript/partial_correlations.js index fdba247f..a6a07558 100644 --- a/wqflask/wqflask/static/new/javascript/partial_correlations.js +++ b/wqflask/wqflask/static/new/javascript/partial_correlations.js @@ -116,8 +116,96 @@ function display_publish_results(primary, controls, correlations, method) { table_body.removeChild(template_row); } -function display_geno_results(primary, controls, correlations) {} -function display_probeset_results(primary, controls, correlations) {} +function display_geno_results(primary, controls, correlations) { + table = document.getElementById("part-corr-results-geno"); + table.setAttribute("style", "display: block;"); + table_body = document.querySelector("#part-corr-results-geno tbody"); + template_row = document.querySelector( + "#part-corr-results-geno tr.template-geno-results-row"); + correlations.forEach(function(item, index, arr) { + new_row = template_row.cloneNode(true); + new_row.setAttribute("class", "results-row"); + new_row.querySelector( + 'td[data-column-heading="Chr"]').innerHTML = item["chr"]; + new_row.querySelector( + 'td[data-column-heading="Megabase"]').innerHTML = item["mb"]; + new_row.querySelector( + 'td[data-column-heading="N"]').innerHTML = item["noverlap"]; + new_row.querySelector( + `td[data-column-heading="Partial ${rho_or_r(method)}"]` + ).innerHTML = format_number(item["partial_corr"]); + new_row.querySelector( + `td[data-column-heading="p(partial ${rho_or_r(method)})"]` + ).innerHTML = format_number(item["partial_corr_p_value"]); + new_row.querySelector( + `td[data-column-heading="${rho_or_r(method)}"]` + ).innerHTML = format_number(item["corr"]); + new_row.querySelector( + `td[data-column-heading="p(${rho_or_r(method)})"]` + ).innerHTML = format_number(item["corr_p_value"]); + new_row.querySelector( + `td[data-column-heading="delta ${rho_or_r(method)}"]` + ).innerHTML = format_number(item["delta"]); + table_body.appendChild(new_row); + }); + table_body.removeChild(template_row); +} + +function display_probeset_results(primary, controls, correlations) { + table = document.getElementById("part-corr-results-probeset"); + table.setAttribute("style", "display: block;"); + table_body = document.querySelector("#part-corr-results-probeset tbody"); + template_row = document.querySelector( + "#part-corr-results-probeset tr.template-probeset-results-row"); + correlations.forEach(function(item, index, arr) { + new_row = template_row.cloneNode(true); + new_row.setAttribute("class", "results-row"); + new_row.querySelector( + 'td[data-column-heading="Record"]').innerHTML = item["trait_name"]; + new_row.querySelector( + 'td[data-column-heading="Gene ID"]').innerHTML = item["geneid"]; + new_row.querySelector( + 'td[data-column-heading="Homologene ID"]').innerHTML = item["homologeneid"]; + new_row.querySelector( + 'td[data-column-heading="Symbol"]').innerHTML = item["symbol"]; + new_row.querySelector( + 'td[data-column-heading="Description"]').innerHTML = item["description"]; + new_row.querySelector( + 'td[data-column-heading="Chr"]').innerHTML = item["chr"]; + new_row.querySelector( + 'td[data-column-heading="Megabase"]').innerHTML = item["mb"]; + new_row.querySelector( + 'td[data-column-heading="Mean Expr"]').innerHTML = item["mean_expr"]; + new_row.querySelector( + 'td[data-column-heading="N"]').innerHTML = item["noverlap"]; + new_row.querySelector( + `td[data-column-heading="Sample Partial ${rho_or_r(method)}"]` + ).innerHTML = format_number(item["partial_corr"]); + new_row.querySelector( + `td[data-column-heading="Sample p(partial ${rho_or_r(method)})"]` + ).innerHTML = format_number(item["partial_corr_p_value"]); + new_row.querySelector( + `td[data-column-heading="Sample ${rho_or_r(method)}"]` + ).innerHTML = format_number(item["corr"]); + new_row.querySelector( + `td[data-column-heading="Sample p(${rho_or_r(method)})"]` + ).innerHTML = format_number(item["corr_p_value"]); + new_row.querySelector( + `td[data-column-heading="delta ${rho_or_r(method)}"]` + ).innerHTML = format_number(item["delta"]); + new_row.querySelector( + `td[data-column-heading="Lit Corr"]` + ).innerHTML = format_number(item["l_corr"]); + new_row.querySelector( + `td[data-column-heading="Tissue ${rho_or_r(method)}"]` + ).innerHTML = format_number(item["tissue_corr"]); + new_row.querySelector( + `td[data-column-heading="Tissue p(${rho_or_r(method)})"]` + ).innerHTML = format_number(item["tissue_p_value"]); + table_body.appendChild(new_row); + }); + table_body.removeChild(template_row); +} function display_partial_corr_results(data, status, xhr) { progress_indicator = document.getElementById( -- cgit v1.2.3 From ddd8e18cb236d6131e9f12a56d4f869649466a09 Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Mon, 3 Jan 2022 11:03:21 +0300 Subject: Include template to get rid of repetitive template code Issue: https://github.com/genenetwork/gn-gemtext-threads/blob/main/topics/gn1-migration-to-gn2/partial-correlations.gmi * The generation of input elements that relate to the traits is very similar, therefore this commit pulls it out into a separate template that can be included where needed. --- .../wqflask/templates/partial_correlations.html | 30 ++++------------------ wqflask/wqflask/templates/with-trait-items.html | 18 +++++++++++++ 2 files changed, 23 insertions(+), 25 deletions(-) create mode 100644 wqflask/wqflask/templates/with-trait-items.html diff --git a/wqflask/wqflask/templates/partial_correlations.html b/wqflask/wqflask/templates/partial_correlations.html index 800222ba..c565d4bf 100644 --- a/wqflask/wqflask/templates/partial_correlations.html +++ b/wqflask/wqflask/templates/partial_correlations.html @@ -24,18 +24,8 @@ {%if step == "select-primary":%}

Please select the primary trait (X)

- {%for trait in traits_list:%} -
- - -
- {%endfor%} + {%include "with-trait-items.html" %} + @@ -46,20 +36,10 @@ {%if step == "select-controls":%}

Select a maximum of three (3) control traits (Z)

- {%for trait in traits_list:%} -
- - -
- {%endfor%} + {%include "with-trait-items.html" %} + {%endif%} diff --git a/wqflask/wqflask/templates/with-trait-items.html b/wqflask/wqflask/templates/with-trait-items.html new file mode 100644 index 00000000..66d6fd22 --- /dev/null +++ b/wqflask/wqflask/templates/with-trait-items.html @@ -0,0 +1,18 @@ +{%for trait in traits_list:%} +
+ + +
+{%endfor%} -- cgit v1.2.3 From cd2e1760751bbffb729c0d8df85e65f1641f93fa Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Mon, 3 Jan 2022 11:05:11 +0300 Subject: Update styling: Display traits in a tabular-like form Issue: https://github.com/genenetwork/gn-gemtext-threads/blob/main/topics/gn1-migration-to-gn2/partial-correlations.gmi * Display the trait details in a tabular-life form without using a table. --- .../static/new/css/partial_correlations.css | 94 +++++++++++++++++----- 1 file changed, 75 insertions(+), 19 deletions(-) diff --git a/wqflask/wqflask/static/new/css/partial_correlations.css b/wqflask/wqflask/static/new/css/partial_correlations.css index a4f45ace..8ff5eae7 100644 --- a/wqflask/wqflask/static/new/css/partial_correlations.css +++ b/wqflask/wqflask/static/new/css/partial_correlations.css @@ -20,39 +20,95 @@ grid-column-end: 3; } -.label-element-combo { - width: 100%; - display: grid; +td, th { + border: 1px solid; + text-align: left; + padding: 0.2em 0.5em 0.2em 0.7em; +} + +tr:nth-of-type(2n) { + background: #F9F9F9; +} + +thead tr { + background: #336699; + line-height: 1.5em; } .with-trait { margin-left: 0.7em; + position: relative; + display: grid; + width: 100%; + grid-column-gap: 1em; + grid-template-columns: 1em 1fr; text-align: left; } -.with-trait .label-element { - width: 90%; - grid-column-start: 2; - grid-column-end: 3 +.with-trait:nth-of-type(2n) { + background: #E5E5FF; } .with-trait .selector-element { - width: 1em; - grid-column-start: 1; - grid-column-end: 2 + grid-column: 1 / 2; + grid-row: 1 / 1; } -td, th { - border: 1px solid; - text-align: left; - padding: 0.2em 0.5em 0.2em 0.7em; +.with-trait:first-of-type { + padding-top: 2.5em; } -tr:nth-of-type(2n) { - background: #F9F9F9; +.with-trait:first-of-type label *:before{ + position: absolute; + top: 0px; + + text-transform: capitalize; + font-weight: bolder; + content: attr(data-title); + background: #336699; /*#FFCCCC;*/ + color: #FFFFFF; + padding: 0.5em; } -thead tr { - background: #336699; - line-height: 1.5em; +.with-trait .label-element { + display: grid; + grid-column-gap: 0.5em; + grid-template-columns: 4fr 2fr 2fr 9fr 2fr 1fr 2fr; + grid-column: 2 / 2; + grid-row: 1 / 1; +} + +.with-trait .label-element .trait-dataset { + grid-column: 1; + grid-row: 1 / 1; +} + +.with-trait .label-element .trait-name { + grid-column: 2; + grid-row: 1 / 1; +} + +.with-trait .label-element .trait-symbol { + grid-column: 3; + grid-row: 1 / 1; +} + +.with-trait .label-element .trait-description { + grid-column: 4; + grid-row: 1 / 1; +} + +.with-trait .label-element .trait-location { + grid-column: 5; + grid-row: 1 / 1; +} + +.with-trait .label-element .trait-mean-expr { + grid-column: 6; + grid-row: 1 / 1; +} + +.with-trait .label-element .trait-max-lrs { + grid-column: 7; + grid-row: 1 / 1; } -- cgit v1.2.3 From bfc3d9483ec97c8ed70034f2de4a3ec414e402b4 Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Mon, 3 Jan 2022 11:40:33 +0300 Subject: Add more items to trait string Issue: https://github.com/genenetwork/gn-gemtext-threads/blob/main/topics/gn1-migration-to-gn2/partial-correlations.gmi --- wqflask/wqflask/partial_correlations_views.py | 4 +++- wqflask/wqflask/static/new/javascript/partial_correlations.js | 3 ++- wqflask/wqflask/templates/collections/view.html | 2 +- wqflask/wqflask/templates/partial_correlations.html | 8 ++++---- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/wqflask/wqflask/partial_correlations_views.py b/wqflask/wqflask/partial_correlations_views.py index 82287312..f3ec0f78 100644 --- a/wqflask/wqflask/partial_correlations_views.py +++ b/wqflask/wqflask/partial_correlations_views.py @@ -9,7 +9,9 @@ from wqflask import app from utility.tools import GN_SERVER_URL def parse_trait(trait_str: str) -> Union[dict, None]: - keys = ("name", "dataset", "symbol", "description", "data_hmac") + keys = ( + "name", "dataset", "symbol", "description", "location", "mean_expr", + "max_lrs", "data_hmac") parts = tuple(part.strip() for part in trait_str.split(":::")) if len(parts) == len(keys): return dict(zip(keys, parts)) diff --git a/wqflask/wqflask/static/new/javascript/partial_correlations.js b/wqflask/wqflask/static/new/javascript/partial_correlations.js index a6a07558..dea4c675 100644 --- a/wqflask/wqflask/static/new/javascript/partial_correlations.js +++ b/wqflask/wqflask/static/new/javascript/partial_correlations.js @@ -18,7 +18,8 @@ function key_value(keys, values) { function trait(trait_str) { return key_value( - ["name", "dataset", "symbol", "description", "data_hmac"], + ["name", "dataset", "symbol", "description", "location", "mean_expr", + "max_lrs", "data_hmac"], trait_str.split(":::")); } diff --git a/wqflask/wqflask/templates/collections/view.html b/wqflask/wqflask/templates/collections/view.html index f4270b67..432393a7 100644 --- a/wqflask/wqflask/templates/collections/view.html +++ b/wqflask/wqflask/templates/collections/view.html @@ -42,7 +42,7 @@ value="{{uc.uc_id}}" /> + value="{% for this_trait in trait_obs %}{{ this_trait.name }}:::{{ this_trait.dataset.name }}:::{{this_trait.symbol}}:::{{this_trait.description_display}}:::{{this_trait.location_repr}}:::{{this_trait.mean}}:::{{this_trait['LRS_location_repr']}}:::{{data_hmac('{}:{}'.format(this_trait.name, this_trait.dataset.name))}}|||{% endfor %}" /> - +
+ +

Use tools like cytoscape to visualize the network

+ +Download Sif file + +Download Node file
@@ -78,7 +88,6 @@ @@ -121,8 +74,6 @@ - + @@ -86,4 +85,5 @@ $('#significance').DataTable({ }) }); -{% endblock %} \ No newline at end of file +{% endblock %} + -- cgit v1.2.3 From d082c40370e45187994e90b146ae6a5e15a2c9c9 Mon Sep 17 00:00:00 2001 From: Alexander Kabui Date: Mon, 31 Jan 2022 22:28:15 +0300 Subject: autopep8 formatting --- wqflask/wqflask/ctl/gn3_ctl_analysis.py | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/wqflask/wqflask/ctl/gn3_ctl_analysis.py b/wqflask/wqflask/ctl/gn3_ctl_analysis.py index 4afae119..67f76a30 100644 --- a/wqflask/wqflask/ctl/gn3_ctl_analysis.py +++ b/wqflask/wqflask/ctl/gn3_ctl_analysis.py @@ -11,21 +11,13 @@ from base import data_set def process_significance_data(significant_data): - """function to process significance the data for - datatables""" - col_names = ["trait", "marker", "trait_2", "LOD", "dcor"] - dataset_rows = [[] for _ in range(len(significant_data["trait"]))] - for col in col_names: for (index, col_data) in enumerate(significant_data[col]): - if col in ["dcor", "LOD"]: - dataset_rows[index].append(round(float(col_data), 2)) else: - dataset_rows[index].append(col_data) return { @@ -35,16 +27,11 @@ def process_significance_data(significant_data): def parse_geno_data(dataset_group_name) -> dict: - """function to parse geno file data""" genofile_location = locate(dataset_group_name + ".geno", "genotype") parser = genofile_parser.ConvertGenoFile(genofile_location) - parser.process_csv() - markers = [] - markernames = [] - for marker in parser.markers: markernames.append(marker["name"]) markers.append(marker["genotypes"]) @@ -60,8 +47,6 @@ def parse_geno_data(dataset_group_name) -> dict: def parse_phenotype_data(trait_db_list, dataset, individuals): - """function to parse and generate phenodata""" - traits = [] for trait in trait_db_list: if trait != "": @@ -90,27 +75,24 @@ def parse_form_data(form_data: dict): trait_db_list = [trait.strip() for trait in form_data['trait_list'].split(',')] - form_data["trait_db_list"] = [x for x in trait_db_list if x] + form_data["trait_db_list"] = [x for x in trait_db_list if x] form_data["nperm"] = int(form_data["nperm"]) form_data["significance"] = float(form_data["significance"]) form_data["strategy"] = form_data["strategy"].capitalize() + return form_data def run_ctl(requestform): """function to make an api call to gn3 and run ctl""" - ctl_api = f"{GN3_LOCAL_URL}/api/ctl/run_ctl" form_data = parse_form_data(requestform.to_dict()) - trait_db_list = form_data["trait_db_list"] dataset = data_set.create_dataset(trait_db_list[0].split(":")[1]) - geno_data = parse_geno_data(dataset.group.name) - pheno_data = parse_phenotype_data( trait_db_list, dataset, geno_data["individuals"]) -- cgit v1.2.3 From 6d3f48d3fa257e5fc22e874b3e0746b809a278a4 Mon Sep 17 00:00:00 2001 From: Alexander Kabui Date: Tue, 1 Feb 2022 00:42:41 +0300 Subject: handle exceptions --- wqflask/wqflask/ctl/gn3_ctl_analysis.py | 28 +++++++++++++++++--------- wqflask/wqflask/templates/gn3_ctl_results.html | 14 ++++++++++++- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/wqflask/wqflask/ctl/gn3_ctl_analysis.py b/wqflask/wqflask/ctl/gn3_ctl_analysis.py index 67f76a30..7af0013f 100644 --- a/wqflask/wqflask/ctl/gn3_ctl_analysis.py +++ b/wqflask/wqflask/ctl/gn3_ctl_analysis.py @@ -96,18 +96,28 @@ def run_ctl(requestform): pheno_data = parse_phenotype_data( trait_db_list, dataset, geno_data["individuals"]) - response = requests.post(ctl_api, json={ + # todo refactor this chunk;;similar to wgcna check - "genoData": geno_data, - "phenoData": pheno_data, + try: - **form_data, + response = requests.post(ctl_api, json={ - }).json()["results"] + "genoData": geno_data, + "phenoData": pheno_data, + **form_data, - response["significance_data"] = process_significance_data( - response["significance_data"]) + }) + if response.status_code != 200: + return {"error": response.json()} + response = response.json()["results"] + response["significance_data"] = process_significance_data( + response["significance_data"]) + + return response + + except requests.exceptions.ConnectionError: + return { + "error": "A connection error to perform computation occurred" + } - # todo check for errors - return response diff --git a/wqflask/wqflask/templates/gn3_ctl_results.html b/wqflask/wqflask/templates/gn3_ctl_results.html index fedddc40..c42707f6 100644 --- a/wqflask/wqflask/templates/gn3_ctl_results.html +++ b/wqflask/wqflask/templates/gn3_ctl_results.html @@ -14,6 +14,14 @@ }
+ +{% if error %} +
+

{{error}}

+
+ +{% else %} +

CTL/QTL Plots for Individual Traits

@@ -68,7 +76,11 @@
- +{% endif %} +
+ + + -- cgit v1.2.3 From 78a544ea268c28b34c2761e6d65465882578d235 Mon Sep 17 00:00:00 2001 From: Alexander Kabui Date: Wed, 2 Feb 2022 09:03:20 +0300 Subject: variable naming and docstrings fix --- wqflask/wqflask/ctl/gn3_ctl_analysis.py | 39 ++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/wqflask/wqflask/ctl/gn3_ctl_analysis.py b/wqflask/wqflask/ctl/gn3_ctl_analysis.py index 7af0013f..8f790597 100644 --- a/wqflask/wqflask/ctl/gn3_ctl_analysis.py +++ b/wqflask/wqflask/ctl/gn3_ctl_analysis.py @@ -10,11 +10,11 @@ from base.trait import retrieve_sample_data from base import data_set -def process_significance_data(significant_data): +def process_significance_data(dataset): col_names = ["trait", "marker", "trait_2", "LOD", "dcor"] - dataset_rows = [[] for _ in range(len(significant_data["trait"]))] + dataset_rows = [[] for _ in range(len(dataset["trait"]))] for col in col_names: - for (index, col_data) in enumerate(significant_data[col]): + for (index, col_data) in enumerate(dataset[col]): if col in ["dcor", "LOD"]: dataset_rows[index].append(round(float(col_data), 2)) else: @@ -27,6 +27,12 @@ def process_significance_data(significant_data): def parse_geno_data(dataset_group_name) -> dict: + """ + Args: + dataset_group_name: string name + + @returns : dict with keys genotypes,markernames & individuals + """ genofile_location = locate(dataset_group_name + ".geno", "genotype") parser = genofile_parser.ConvertGenoFile(genofile_location) parser.process_csv() @@ -46,9 +52,21 @@ def parse_geno_data(dataset_group_name) -> dict: } -def parse_phenotype_data(trait_db_list, dataset, individuals): +def parse_phenotype_data(trait_list, dataset, individuals): + """ + Args: + trait_list:list contains the traits + dataset: object + individuals:a list contains the individual vals + Returns: + traits_db_List:parsed list of traits + traits: list contains trait names + individuals + + """ + traits = [] - for trait in trait_db_list: + for trait in trait_list: if trait != "": ts = trait.split(':') gt = create_trait(name=ts[0], dataset_name=ts[1]) @@ -60,18 +78,13 @@ def parse_phenotype_data(trait_db_list, dataset, individuals): traits.append("-999") return { - "trait_db_list": trait_db_list, + "trait_db_list": trait_list, "traits": traits, "individuals": individuals } def parse_form_data(form_data: dict): - """function to parse/validate form data - input: dict containing required data - output: dict with parsed data - - """ trait_db_list = [trait.strip() for trait in form_data['trait_list'].split(',')] @@ -96,8 +109,6 @@ def run_ctl(requestform): pheno_data = parse_phenotype_data( trait_db_list, dataset, geno_data["individuals"]) - # todo refactor this chunk;;similar to wgcna check - try: response = requests.post(ctl_api, json={ @@ -119,5 +130,3 @@ def run_ctl(requestform): return { "error": "A connection error to perform computation occurred" } - - -- cgit v1.2.3 From 2c22e593c59a9b4f9129a2e669443709d9c5154a Mon Sep 17 00:00:00 2001 From: BonfaceKilz Date: Wed, 9 Feb 2022 16:52:00 +0300 Subject: doc: Document how to fetch case-attribute data from a sample-list --- doc/database.org | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/doc/database.org b/doc/database.org index d5462d4e..99d2905a 100644 --- a/doc/database.org +++ b/doc/database.org @@ -1370,6 +1370,27 @@ Empty set (0.00 sec) Hmmm. This is the test database. Then there are the plink files and VCF files. +** How to fetch case attribute data + +To fetch case attribute data, you need the PublixshXRef Id and it's +PhenotypeId. Run the following query (as an example): + +#+begin_src sql +SELECT concat(st.Name, ',', ifnull(pd.value, 'x'), ',', + ifnull(ps.error, 'x'), ',', ifnull(ns.count, 'x')) as 'Data', + ifnull(ca.Name, 'x') as 'CaseAttr', ifnull(cxref.value, 'x') as 'Value' +FROM PublishFreeze pf +JOIN PublishXRef px ON px.InbredSetId = pf.InbredSetId +JOIN PublishData pd ON pd.Id = px.DataId +JOIN Strain st ON pd.StrainId = st.Id +LEFT JOIN PublishSE ps ON ps.DataId = pd.Id AND ps.StrainId = pd.StrainId +LEFT JOIN NStrain ns ON ns.DataId = pd.Id AND ns.StrainId = pd.StrainId +LEFT JOIN CaseAttributeXRefNew cxref ON + cxref.InbredSetId = px.InbredSetId +LEFT JOIN CaseAttribute ca ON ca.Id = cxref.CaseAttributeId +WHERE px.Id = 10006 AND px.PhenotypeId = 28409 LIMIT 10; +#+end_src + * Optimize SQL? We were facing some issues with slow queries. A query -- cgit v1.2.3 From 91691799956450cac4e763f96e2152aa1cd44f3b Mon Sep 17 00:00:00 2001 From: zsloan Date: Mon, 14 Feb 2022 21:31:07 +0000 Subject: Fix CisTrans search bug ', Geno' was being added to the FROM clause, which is wrong becausee Geno is included in the query via a JOIN (and caused an error making those searches not work) --- wqflask/wqflask/do_search.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/wqflask/wqflask/do_search.py b/wqflask/wqflask/do_search.py index ee5a4d91..5c182260 100644 --- a/wqflask/wqflask/do_search.py +++ b/wqflask/wqflask/do_search.py @@ -609,9 +609,6 @@ class PhenotypeLrsSearch(LrsSearch, PhenotypeSearch): class CisTransLrsSearch(DoSearch): - def get_from_clause(self): - return ", Geno" - def get_where_clause(self, cis_trans): self.mb_buffer = 5 # default chromosome = None -- cgit v1.2.3 From c147a6e75a9496263fd76f2eb9711ddf77a643ff Mon Sep 17 00:00:00 2001 From: zsloan Date: Mon, 14 Feb 2022 21:44:40 +0000 Subject: Add search text for cis/transLRS/LOD searches There wasn't any search text for cis/trans searches including both chromosome and exclusion zone, so I added somee --- wqflask/wqflask/templates/search_result_page.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wqflask/wqflask/templates/search_result_page.html b/wqflask/wqflask/templates/search_result_page.html index 26cdc437..70c10946 100644 --- a/wqflask/wqflask/templates/search_result_page.html +++ b/wqflask/wqflask/templates/search_result_page.html @@ -40,6 +40,8 @@ with {{ word.key|upper }} between {{ word.search_term[0] }} and {{ word.search_term[1] }}{% if loop.last %}.{% else %} and {% endif %} {% elif word.search_term|length == 3 %} with {{ word.key|upper }} between {{ word.search_term[0] }} and {{ word.search_term[1] }} on chromosome {{ word.search_term[2] }}{% if loop.last %}.{% else %} and {% endif %} + {% elif word.search_term|length == 4 %} + with {{ word.key|upper }} between {{ word.search_term[0] }} and {{ word.search_term[1] }} on chromosome {{ word.search_term[3] }} with an exclusion zone of {{ word.search_term[2] }} Mb {% elif word.search_term|length == 5 %} with {{ word.key|upper }} between {{ word.search_term[0] }} and {{ word.search_term[1] }} on chromosome {{ word.search_term[2] }} between {{ word.search_term[3] }} and {{ word.search_term[4] }} Mb{% if loop.last %}.{% else %} and {% endif %} {% endif %} -- cgit v1.2.3 From 25a817bfa97fcb6d202397252e55bba088ead32d Mon Sep 17 00:00:00 2001 From: Arun Isaac Date: Thu, 17 Feb 2022 18:25:55 +0530 Subject: etc: Close VERSION file after opening. Use a context manager so that the VERSION file is automatically closed on exiting the block. * etc/default_settings.py: Close VERSION file after opening. --- etc/default_settings.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/etc/default_settings.py b/etc/default_settings.py index 8636f4db..a0d01de0 100644 --- a/etc/default_settings.py +++ b/etc/default_settings.py @@ -24,7 +24,8 @@ import os import sys -GN_VERSION = open("../etc/VERSION", "r").read() +with open("../etc/VERSION", "r") as version_file: + GN_VERSION = version_file.read() # Redis REDIS_URL = "redis://:@localhost:6379/0" -- cgit v1.2.3 From 84a1f972cab8d7976919a65c7bc4e14a76d78d02 Mon Sep 17 00:00:00 2001 From: Arun Isaac Date: Thu, 17 Feb 2022 22:50:20 +0530 Subject: bin: Allow for not starting redis. redis needs to be started and managed as a separate service. We create a flag NO_REDIS so that it can be so. If NO_REDIS is not defined, the present behavior of starting redis remains. Thus current use cases are not broken. * bin/genenetwork2: Allow for not starting redis with the NO_REDIS flag. --- bin/genenetwork2 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bin/genenetwork2 b/bin/genenetwork2 index a6ba7241..36bf11a6 100755 --- a/bin/genenetwork2 +++ b/bin/genenetwork2 @@ -213,10 +213,12 @@ if [ "$1" = '-gunicorn-prod' ] ; then exit $? fi -echo "Starting the redis server:" -echo -n "dir $TMPDIR +if [ -z "$NO_REDIS" ] ; then + echo "Starting the redis server:" + echo -n "dir $TMPDIR dbfilename gn2.rdb " | redis-server - & +fi # Overrides for packages that are not yet public (currently r-auwerx) # export R_LIBS_SITE=$R_LIBS_SITE:$HOME/.Rlibs/das1i1pm54dj6lbdcsw5w0sdwhccyj1a-r-3.3.2/lib/R/lib -- cgit v1.2.3 From 843b6819bb59beda79c5849f5071dfb544280586 Mon Sep 17 00:00:00 2001 From: Arun Isaac Date: Fri, 18 Feb 2022 14:11:42 +0530 Subject: README: Replace GitHub actions badge with Laminar CI badge. * README.md: Replace GitHub actions badge with Laminar CI badge. --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 52382c98..50fe49f6 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ [![DOI](https://zenodo.org/badge/5591/genenetwork/genenetwork2.svg)](https://zenodo.org/badge/latestdoi/5591/genenetwork/genenetwork2) [![JOSS](http://joss.theoj.org/papers/10.21105/joss.00025/status.svg)](http://joss.theoj.org/papers/10.21105/joss.00025) -[![Actions Status](https://github.com/genenetwork/genenetwork2/workflows/tests/badge.svg)](https://github.com/genenetwork/genenetwork2/actions) +[![GeneNetwork2 CI +badge](https://penguin2.genenetwork.org/badge/genenetwork2.svg)](https://penguin2.genenetwork.org/jobs/genenetwork2) # GeneNetwork -- cgit v1.2.3 From 17652b17455bd58bf82d130b60b3e80c57b7f80c Mon Sep 17 00:00:00 2001 From: Arun Isaac Date: Fri, 18 Feb 2022 16:01:01 +0530 Subject: wqflask: Remove python pre-2.2 checks. These checks are only relevant for python pre-2.2. * wqflask/utility/svg.py: Remove python pre-2.2 checks. --- wqflask/utility/svg.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/wqflask/utility/svg.py b/wqflask/utility/svg.py index eddb97da..912cd04c 100644 --- a/wqflask/utility/svg.py +++ b/wqflask/utility/svg.py @@ -108,12 +108,6 @@ if use_dom_implementation != 0: # The implementation is used for the creating the XML document. # The prettyprint module is used for converting the xml document object to a xml file -assert sys.version_info[0] >= 2 -if sys.version_info[1] < 2: - True = 1 - False = 0 - file = open - sys.setrecursionlimit = 50 # The recursion limit is set conservative so mistakes like s=svg() s.addElement(s) # won't eat up too much processor time. -- cgit v1.2.3 From 7c9e73f196575cd6d1de7df4430bc2b4ecb28466 Mon Sep 17 00:00:00 2001 From: zsloan Date: Mon, 21 Feb 2022 21:18:46 +0000 Subject: Fix incorrect dataset trait data caching Trait data caching wasn't working correctly because it didn't account for the samplelist, causing caching to work incorrect in any situation where the target dataset's samplelist wasn't the same as that of the trait being correlated against. Trait data is stored as a dictionary where the keys are trait IDs and values are *lists* of sample values. This means that the caching needs to account for the exact same set of samples; otherwise you'll end up with samples being mismatched (since "the third sample with a value" for one dataset's trait might not be the same as "the third sample with a value" for another dataset's trait). To fix this, I added the samplelist to the functions that generate and fetch the hash file. This will require more cache files, though, so this should probably be reexamined later to make the code work with only a single cache file for each dataset. --- wqflask/base/data_set.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/wqflask/base/data_set.py b/wqflask/base/data_set.py index af248659..d7e4e62f 100644 --- a/wqflask/base/data_set.py +++ b/wqflask/base/data_set.py @@ -756,7 +756,7 @@ class DataSet: chunk_size = 50 number_chunks = int(math.ceil(len(sample_ids) / chunk_size)) - cached_results = fetch_cached_results(self.name, self.type) + cached_results = fetch_cached_results(self.name, self.type, self.samplelist) if cached_results is None: trait_sample_data = [] for sample_ids_step in chunks.divide_into_chunks(sample_ids, number_chunks): @@ -812,9 +812,8 @@ class DataSet: trait_sample_data[chunk_counter][trait_counter][data_start_pos:]) cache_dataset_results( - self.name, self.type, self.trait_data) + self.name, self.type, self.samplelist, self.trait_data) else: - self.trait_data = cached_results @@ -1278,14 +1277,14 @@ def query_table_timestamp(dataset_type: str): return date_time_obj.strftime("%Y-%m-%d %H:%M:%S") -def generate_hash_file(dataset_name: str, dataset_type: str, dataset_timestamp: str): +def generate_hash_file(dataset_name: str, dataset_type: str, dataset_timestamp: str, samplelist: str): """given the trait_name generate a unique name for this""" - string_unicode = f"{dataset_name}{dataset_timestamp}".encode() + string_unicode = f"{dataset_name}{dataset_timestamp}{samplelist}".encode() md5hash = hashlib.md5(string_unicode) return md5hash.hexdigest() -def cache_dataset_results(dataset_name: str, dataset_type: str, query_results: List): +def cache_dataset_results(dataset_name: str, dataset_type: str, samplelist: List, query_results: List): """function to cache dataset query results to file input dataset_name and type query_results(already processed in default dict format) """ @@ -1293,21 +1292,22 @@ def cache_dataset_results(dataset_name: str, dataset_type: str, query_results: L # store the file path on redis table_timestamp = query_table_timestamp(dataset_type) + samplelist_as_str = ",".join(samplelist) - - file_name = generate_hash_file(dataset_name, dataset_type, table_timestamp) + file_name = generate_hash_file(dataset_name, dataset_type, table_timestamp, samplelist_as_str) file_path = os.path.join(TMPDIR, f"{file_name}.json") with open(file_path, "w") as file_handler: json.dump(query_results, file_handler) -def fetch_cached_results(dataset_name: str, dataset_type: str): +def fetch_cached_results(dataset_name: str, dataset_type: str, samplelist: List): """function to fetch the cached results""" table_timestamp = query_table_timestamp(dataset_type) + samplelist_as_str = ",".join(samplelist) - file_name = generate_hash_file(dataset_name, dataset_type, table_timestamp) + file_name = generate_hash_file(dataset_name, dataset_type, table_timestamp, samplelist_as_str) file_path = os.path.join(TMPDIR, f"{file_name}.json") try: with open(file_path, "r") as file_handler: -- cgit v1.2.3 From abdc642cea9ab40f5e71d65abaab0362bc5df74b Mon Sep 17 00:00:00 2001 From: zsloan Date: Wed, 23 Feb 2022 21:27:11 +0000 Subject: Add top margin to Partial Correlations button Later this button should just be included with the other function buttons. There's no need for it to be in a separate form, since it should be able to take the same trait list input as all the other collection page functions (with just the action url for the form changing) --- wqflask/wqflask/templates/collections/view.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wqflask/wqflask/templates/collections/view.html b/wqflask/wqflask/templates/collections/view.html index 432393a7..3e3d3972 100644 --- a/wqflask/wqflask/templates/collections/view.html +++ b/wqflask/wqflask/templates/collections/view.html @@ -46,7 +46,7 @@ -- cgit v1.2.3 From 348e506ce811f4f314b7c9e52073bf03aeb70d50 Mon Sep 17 00:00:00 2001 From: zsloan Date: Wed, 23 Feb 2022 21:43:57 +0000 Subject: Reposition Partial Correlations and Heatmap buttons Moved Partial Correlations and Generate Heatmap buttons to be lined up below the other buttons and hide the Heatmap orientation selection (JS will be added to show it after Heatmap is run) --- wqflask/wqflask/templates/collections/view.html | 81 +++++++++++++------------ 1 file changed, 42 insertions(+), 39 deletions(-) diff --git a/wqflask/wqflask/templates/collections/view.html b/wqflask/wqflask/templates/collections/view.html index 3e3d3972..447a821a 100644 --- a/wqflask/wqflask/templates/collections/view.html +++ b/wqflask/wqflask/templates/collections/view.html @@ -33,48 +33,51 @@
- -
- - - -
+
+
+ + + +
+   +
+
+
+ Heatmap Orientation + + + + +
+
+ +
+

-
-
- Heatmap Orientation - - - - -
- -
-
-- cgit v1.2.3 From 42cb64c8d89766cd28276c03b37674a227d39aad Mon Sep 17 00:00:00 2001 From: zsloan Date: Wed, 23 Feb 2022 22:04:34 +0000 Subject: Show heatmap options when heatmap is run Heatmap options are now shown after the user clicks the button I also reordered the Heatmap and Partial Correlation buttons, since it's awkward to get the Heatmap options to display well if its button is second. We'll probably completely rework this later, but for now this looks a little less ugly. --- wqflask/wqflask/templates/collections/view.html | 49 +++++++++++++------------ 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/wqflask/wqflask/templates/collections/view.html b/wqflask/wqflask/templates/collections/view.html index 447a821a..804c52e9 100644 --- a/wqflask/wqflask/templates/collections/view.html +++ b/wqflask/wqflask/templates/collections/view.html @@ -34,6 +34,30 @@
+
+ +
+ +
+  
@@ -50,30 +74,6 @@ Partial Correlations
-   -
-
-
- Heatmap Orientation - - - - -
-
- -
@@ -342,6 +342,7 @@ $("#clustered-heatmap").on("click", function() { clear_heatmap_area(); + $("#heatmap-options").show(); intv = window.setInterval(generate_progress_indicator(), 300); vert_element = document.getElementById("heatmap-orient-vertical"); vert_true = vert_element == null ? false : vert_element.checked; -- cgit v1.2.3 From 82018ffcb426812c3d986e262e0a1523fbe973aa Mon Sep 17 00:00:00 2001 From: zsloan Date: Wed, 23 Feb 2022 22:15:16 +0000 Subject: Improve Heatmap options' appearance Moved the Heatmap display area above the table selection options (since it didn't make sense to have it below those) Added a button for clearing the Heatmap area and hiding the Heatmap options Added some margins to the Heatmap options --- wqflask/wqflask/templates/collections/view.html | 42 ++++++++++++++++--------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/wqflask/wqflask/templates/collections/view.html b/wqflask/wqflask/templates/collections/view.html index 804c52e9..f896471a 100644 --- a/wqflask/wqflask/templates/collections/view.html +++ b/wqflask/wqflask/templates/collections/view.html @@ -43,18 +43,27 @@
  @@ -77,6 +86,7 @@
+

@@ -94,8 +104,6 @@
-
-
Show/Hide Columns:
@@ -369,6 +377,12 @@ } }); }); + + $("#clear-heatmap").on("click", function() { + clear_heatmap_area(); + $("#heatmap-options").hide(); + }); + }); -- cgit v1.2.3 From 510829d9ed7ff47d52989d79e235d86205bc5e2e Mon Sep 17 00:00:00 2001 From: zsloan Date: Wed, 23 Feb 2022 22:19:02 +0000 Subject: Fix Heatmap GN3 URL --- wqflask/wqflask/collect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wqflask/wqflask/collect.py b/wqflask/wqflask/collect.py index 76ef5ca4..3475ae5d 100644 --- a/wqflask/wqflask/collect.py +++ b/wqflask/wqflask/collect.py @@ -222,7 +222,7 @@ def view_collection(): collection_info = dict( trait_obs=trait_obs, uc=uc, - heatmap_data_url=f"{GN_SERVER_URL}heatmaps/clustered") + heatmap_data_url=f"{GN_SERVER_URL}api/heatmaps/clustered") if "json" in params: return json.dumps(json_version) -- cgit v1.2.3 From 86ead9b3d823e46350b5566b197463b5fdc46102 Mon Sep 17 00:00:00 2001 From: Alexander Kabui Date: Thu, 24 Feb 2022 19:27:28 +0300 Subject: init replace rpy2 for pca --- .../wqflask/correlation_matrix/show_corr_matrix.py | 28 ++++++++++------------ 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/wqflask/wqflask/correlation_matrix/show_corr_matrix.py b/wqflask/wqflask/correlation_matrix/show_corr_matrix.py index e7b16e77..9462f973 100644 --- a/wqflask/wqflask/correlation_matrix/show_corr_matrix.py +++ b/wqflask/wqflask/correlation_matrix/show_corr_matrix.py @@ -23,8 +23,6 @@ import math import random import string -import rpy2.robjects as ro -from rpy2.robjects.packages import importr import numpy as np import scipy @@ -38,6 +36,12 @@ from utility import helper_functions from utility import corr_result_helpers from utility.redis_tools import get_redis_conn + + +from gn3.computations.principal_component_analysis import compute_pca + +from gn3.computations.principal_component_analysis import process_factor_loadings_tdata + Redis = get_redis_conn() THIRTY_DAYS = 60 * 60 * 24 * 30 @@ -174,7 +178,7 @@ class CorrelationMatrix: self.pca_trait_ids = [] pca = self.calculate_pca( list(range(len(self.traits))), corr_eigen_value, corr_eigen_vectors) - self.loadings_array = self.process_loadings() + self.loadings_array = process_factor_loadings_tdata(self.loadings,len(self.trait_list)) else: self.pca_works = "False" except: @@ -188,18 +192,12 @@ class CorrelationMatrix: sample_data=self.sample_data,) def calculate_pca(self, cols, corr_eigen_value, corr_eigen_vectors): - base = importr('base') - stats = importr('stats') - - corr_results_to_list = ro.FloatVector( - [item for sublist in self.pca_corr_results for item in sublist]) - - m = ro.r.matrix(corr_results_to_list, nrow=len(cols)) - eigen = base.eigen(m) - pca = stats.princomp(m, cor="TRUE") - self.loadings = pca.rx('loadings') - self.scores = pca.rx('scores') - self.scale = pca.rx('scale') + + + pca = compute_pca(self.pca_corr_results) + + self.loadings = pca["components"] + self.scores = pca["scores"] trait_array = zScore(self.trait_data_array) trait_array_vectors = np.dot(corr_eigen_vectors, trait_array) -- cgit v1.2.3 From 0ee723d14957c01162a67f4f6b99a25d43908b5b Mon Sep 17 00:00:00 2001 From: Alexander Kabui Date: Thu, 24 Feb 2022 22:19:00 +0300 Subject: remove redundant functions and code --- .../wqflask/correlation_matrix/show_corr_matrix.py | 70 ++-------------------- 1 file changed, 6 insertions(+), 64 deletions(-) diff --git a/wqflask/wqflask/correlation_matrix/show_corr_matrix.py b/wqflask/wqflask/correlation_matrix/show_corr_matrix.py index 9462f973..bcd73436 100644 --- a/wqflask/wqflask/correlation_matrix/show_corr_matrix.py +++ b/wqflask/wqflask/correlation_matrix/show_corr_matrix.py @@ -41,6 +41,7 @@ from utility.redis_tools import get_redis_conn from gn3.computations.principal_component_analysis import compute_pca from gn3.computations.principal_component_analysis import process_factor_loadings_tdata +from gn3.computations.principal_component_analysis import generate_pca_traits_vals Redis = get_redis_conn() THIRTY_DAYS = 60 * 60 * 24 * 30 @@ -169,15 +170,12 @@ class CorrelationMatrix: self.pca_works = "False" try: - corr_result_eigen = np.linalg.eig(np.array(self.pca_corr_results)) - corr_eigen_value, corr_eigen_vectors = sortEigenVectors( - corr_result_eigen) if self.do_PCA == True: self.pca_works = "True" self.pca_trait_ids = [] pca = self.calculate_pca( - list(range(len(self.traits))), corr_eigen_value, corr_eigen_vectors) + list(range(len(self.traits)))) self.loadings_array = process_factor_loadings_tdata(self.loadings,len(self.trait_list)) else: self.pca_works = "False" @@ -191,7 +189,7 @@ class CorrelationMatrix: samples=self.all_sample_list, sample_data=self.sample_data,) - def calculate_pca(self, cols, corr_eigen_value, corr_eigen_vectors): + def calculate_pca(self, cols): pca = compute_pca(self.pca_corr_results) @@ -199,8 +197,9 @@ class CorrelationMatrix: self.loadings = pca["components"] self.scores = pca["scores"] - trait_array = zScore(self.trait_data_array) - trait_array_vectors = np.dot(corr_eigen_vectors, trait_array) + trait_array_vectors = generate_pca_traits_vals(self.trait_data_array,self.pca_corr_results) + + pca_traits = [] for i, vector in enumerate(trait_array_vectors): @@ -231,21 +230,6 @@ class CorrelationMatrix: return pca - def process_loadings(self): - loadings_array = [] - loadings_row = [] - for i in range(len(self.trait_list)): - loadings_row = [] - if len(self.trait_list) > 2: - the_range = 3 - else: - the_range = 2 - for j in range(the_range): - position = i + len(self.trait_list) * j - loadings_row.append(self.loadings[0][position]) - loadings_array.append(loadings_row) - return loadings_array - def export_corr_matrix(corr_results): corr_matrix_filename = "corr_matrix_" + \ @@ -285,45 +269,3 @@ def export_corr_matrix(corr_results): return corr_matrix_filename, matrix_export_path - -def zScore(trait_data_array): - NN = len(trait_data_array[0]) - if NN < 10: - return trait_data_array - else: - i = 0 - for data in trait_data_array: - N = len(data) - S = reduce(lambda x, y: x + y, data, 0.) - SS = reduce(lambda x, y: x + y * y, data, 0.) - mean = S / N - var = SS - S * S / N - stdev = math.sqrt(var / (N - 1)) - if stdev == 0: - stdev = 1e-100 - data2 = [(x - mean) / stdev for x in data] - trait_data_array[i] = data2 - i += 1 - return trait_data_array - - -def sortEigenVectors(vector): - try: - eigenValues = vector[0].tolist() - eigenVectors = vector[1].T.tolist() - combines = [] - i = 0 - for item in eigenValues: - combines.append([eigenValues[i], eigenVectors[i]]) - i += 1 - sorted(combines, key=cmp_to_key(webqtlUtil.cmpEigenValue)) - A = [] - B = [] - for item in combines: - A.append(item[0]) - B.append(item[1]) - sum = reduce(lambda x, y: x + y, A, 0.0) - A = [x * 100.0 / sum for x in A] - return [A, B] - except: - return [] -- cgit v1.2.3 From 494e91a2dd04cdb6b8cd20fc1709d847ac2c012a Mon Sep 17 00:00:00 2001 From: zsloan Date: Thu, 24 Feb 2022 21:32:42 +0000 Subject: Change parser.py to also detect hyphen in search terms --- wqflask/wqflask/parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wqflask/wqflask/parser.py b/wqflask/wqflask/parser.py index bd1c4407..7a808ac9 100644 --- a/wqflask/wqflask/parser.py +++ b/wqflask/wqflask/parser.py @@ -33,7 +33,7 @@ def parse(pstring): pstring = re.split(r"""(?:(\w+\s*=\s*[\('"\[][^)'"]*[\)\]'"]) | # LRS=(1 2 3), cisLRS=[4 5 6], etc (\w+\s*[=:\>\<][\w\*]+) | # wiki=bar, GO:foobar, etc (".*?") | ('.*?') | # terms in quotes, i.e. "brain weight" - ([\w\*\?]+)) # shh, brain, etc """, pstring, + ([\w\*\?\-]+)) # shh, brain, etc """, pstring, flags=re.VERBOSE) pstring = [item.strip() for item in pstring if item and item.strip()] -- cgit v1.2.3 From bbeba72a82e0a5698b4fb413430203e8aaee1830 Mon Sep 17 00:00:00 2001 From: zsloan Date: Thu, 24 Feb 2022 21:33:38 +0000 Subject: Fix alias query --- wqflask/wqflask/search_results.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/wqflask/wqflask/search_results.py b/wqflask/wqflask/search_results.py index cf2905c9..6062f354 100644 --- a/wqflask/wqflask/search_results.py +++ b/wqflask/wqflask/search_results.py @@ -360,7 +360,8 @@ def get_aliases(symbol_list, species): filtered_aliases = [] response = requests.get( - GN2_BASE_URL + "/gn3/gene/aliases2/" + symbols_string) + GN2_BASE_URL + "gn3/gene/aliases/" + symbols_string) + if response: alias_lists = json.loads(response.content) seen = set() -- cgit v1.2.3 From 6496f75af521d6801c25de682d2852e14d597fed Mon Sep 17 00:00:00 2001 From: zsloan Date: Thu, 24 Feb 2022 21:53:56 +0000 Subject: Fix ProbeSet query for aliases ProbeSet queries previously weren't dealing with aliases correctly, because it was doing a MATCH/AGAINST against the ProbeSet.alias field, but that field usually contains a list of gene symbols separated by semi-colons (so it wouldn't detect the alias unless there was only a single alias. To fix this, I added some LIKE conditions, searching for the possible variations. This is a little awkward, because I needed to make sure to avoid a situation where, for example, an alias like 'LPD-1' matches a search for 'PD-1'. I don't think the way it currently works is efficient, but I don't know of any good alternative without changing the way we store aliases in the database. --- wqflask/wqflask/do_search.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/wqflask/wqflask/do_search.py b/wqflask/wqflask/do_search.py index 5c182260..f8cc482f 100644 --- a/wqflask/wqflask/do_search.py +++ b/wqflask/wqflask/do_search.py @@ -137,15 +137,18 @@ class MrnaAssaySearch(DoSearch): search_string = escape(self.search_term[0]) if self.search_term[0] != "*": - match_clause = """((MATCH (ProbeSet.Name, + match_clause = f"""((MATCH (ProbeSet.Name, ProbeSet.description, ProbeSet.symbol, - alias, GenbankId, UniGeneId, Probe_Target_Description) - AGAINST ('%s' IN BOOLEAN MODE))) AND - """ % (search_string) + AGAINST ('{search_string}' IN BOOLEAN MODE)) OR ( + alias LIKE '%%; {search_string};%%' OR + alias LIKE '{search_string};%%' OR + alias LIKE '%%; {search_string}' OR + alias LIKE '{search_string}' + )) AND """ else: match_clause = "" -- cgit v1.2.3 From d5d0e0f3c271e056057c0311083ab3684ccc4386 Mon Sep 17 00:00:00 2001 From: Alexander Kabui Date: Fri, 25 Feb 2022 17:33:19 +0300 Subject: integrating generating temp dataset for pca --- .../wqflask/correlation_matrix/show_corr_matrix.py | 63 +++++++++++----------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/wqflask/wqflask/correlation_matrix/show_corr_matrix.py b/wqflask/wqflask/correlation_matrix/show_corr_matrix.py index bcd73436..d5ec738b 100644 --- a/wqflask/wqflask/correlation_matrix/show_corr_matrix.py +++ b/wqflask/wqflask/correlation_matrix/show_corr_matrix.py @@ -19,7 +19,6 @@ # This module is used by GeneNetwork project (www.genenetwork.org) import datetime -import math import random import string @@ -29,9 +28,6 @@ import scipy from base import data_set from base.webqtlConfig import GENERATED_TEXT_DIR -from functools import reduce -from functools import cmp_to_key -from utility import webqtlUtil from utility import helper_functions from utility import corr_result_helpers from utility.redis_tools import get_redis_conn @@ -42,6 +38,8 @@ from gn3.computations.principal_component_analysis import compute_pca from gn3.computations.principal_component_analysis import process_factor_loadings_tdata from gn3.computations.principal_component_analysis import generate_pca_traits_vals +from gn3.computations.principal_component_analysis import generate_pca_temp_dataset +from gn3.computations.principal_component_analysis import cache_pca_dataset Redis = get_redis_conn() THIRTY_DAYS = 60 * 60 * 24 * 30 @@ -171,11 +169,11 @@ class CorrelationMatrix: self.pca_works = "False" try: + if self.do_PCA == True: self.pca_works = "True" self.pca_trait_ids = [] - pca = self.calculate_pca( - list(range(len(self.traits)))) + pca = self.calculate_pca() self.loadings_array = process_factor_loadings_tdata(self.loadings,len(self.trait_list)) else: self.pca_works = "False" @@ -189,7 +187,7 @@ class CorrelationMatrix: samples=self.all_sample_list, sample_data=self.sample_data,) - def calculate_pca(self, cols): + def calculate_pca(self): pca = compute_pca(self.pca_corr_results) @@ -197,36 +195,37 @@ class CorrelationMatrix: self.loadings = pca["components"] self.scores = pca["scores"] - trait_array_vectors = generate_pca_traits_vals(self.trait_data_array,self.pca_corr_results) - - - - pca_traits = [] - for i, vector in enumerate(trait_array_vectors): - # ZS: Check if below check is necessary - # if corr_eigen_value[i-1] > 100.0/len(self.trait_list): - pca_traits.append((vector * -1.0).tolist()) this_group_name = self.trait_list[0][1].group.name temp_dataset = data_set.create_dataset( dataset_name="Temp", dataset_type="Temp", group_name=this_group_name) temp_dataset.group.get_samplelist() - for i, pca_trait in enumerate(pca_traits): - trait_id = "PCA" + str(i + 1) + "_" + temp_dataset.group.species + "_" + \ - this_group_name + "_" + datetime.datetime.now().strftime("%m%d%H%M%S") - this_vals_string = "" - position = 0 - for sample in temp_dataset.group.all_samples_ordered(): - if sample in self.shared_samples_list: - this_vals_string += str(pca_trait[position]) - this_vals_string += " " - position += 1 - else: - this_vals_string += "x " - this_vals_string = this_vals_string[:-1] - Redis.set(trait_id, this_vals_string, ex=THIRTY_DAYS) - self.pca_trait_ids.append(trait_id) + + species = temp_dataset.group.species + + group =this_group_name + + trait_data_array = self.trait_data_array + + pca_corr = self.pca_corr_results + + sample_list = temp_dataset.group.all_samples_ordered() + + + shared = self.shared_samples_list + + dt_time = datetime.datetime.now().strftime("%m%d%H%M%S") + + + + results = generate_pca_temp_dataset(species = species, group= group,traits_data = self.trait_data_array,corr_array = self.pca_corr_results,dataset_samples = sample_list, shared_samples=shared,create_time=dt_time) + + + + cache_pca_dataset(Redis,THIRTY_DAYS,results) + + self.pca_trait_ids = list(results.keys()) return pca @@ -269,3 +268,5 @@ def export_corr_matrix(corr_results): return corr_matrix_filename, matrix_export_path + + -- cgit v1.2.3 From b1629deb7822b165dbb42de4662e9ab968170e95 Mon Sep 17 00:00:00 2001 From: zsloan Date: Tue, 22 Feb 2022 22:02:09 +0000 Subject: Add JS initializing DataTables --- .../static/new/javascript/partial_correlations.js | 36 ++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/wqflask/wqflask/static/new/javascript/partial_correlations.js b/wqflask/wqflask/static/new/javascript/partial_correlations.js index b3a89c5e..030b63f2 100644 --- a/wqflask/wqflask/static/new/javascript/partial_correlations.js +++ b/wqflask/wqflask/static/new/javascript/partial_correlations.js @@ -249,6 +249,8 @@ function display_partial_corr_results(data, status, xhr) { data["results"]["control_traits"], data["results"]["correlations"], data["results"]["method"]); + + initializePcorrTable(data["results"]["dataset_type"]); } function display_partial_corr_error(xhr, status, error) { @@ -285,6 +287,40 @@ function send_data_and_process_results( }); } +function initializePcorrTable(dataType){ + tableId = "part-corr-results-" + dataType.toLowerCase(); + if (dataType == "Publish") { + orderCol = 7; + } else if (dataType == "ProbeSet") { + orderCol = 11; + } else { + orderCol = 6; + } + + $('#' + tableId).dataTable( { + 'drawCallback': function( settings ) { + $('#' + tableId + ' tr').off().on("click", function(event) { + if (event.target.type !== 'checkbox' && event.target.tagName.toLowerCase() !== 'a') { + var obj =$(this).find('input'); + obj.prop('checked', !obj.is(':checked')); + } + if ($(this).hasClass("selected") && event.target.tagName.toLowerCase() !== 'a'){ + $(this).removeClass("selected") + } else if (event.target.tagName.toLowerCase() !== 'a') { + $(this).addClass("selected") + } + }); + }, + "order": [[orderCol, "asc" ]], + "sDom": "itir", + "iDisplayLength": -1, + "autoWidth": false, + "bDeferRender": true, + "bSortClasses": false, + "paging": false + } ); +} + $("#partial-correlations-form").submit(function(e) { e.preventDefault(); }); -- cgit v1.2.3 From 140707eab56f8878d0b149e6fe92819fe5054d43 Mon Sep 17 00:00:00 2001 From: zsloan Date: Tue, 22 Feb 2022 22:02:52 +0000 Subject: Include DataTables imports and necessary CSS --- wqflask/wqflask/templates/partial_correlations.html | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/wqflask/wqflask/templates/partial_correlations.html b/wqflask/wqflask/templates/partial_correlations.html index 4be4890e..ad1c674a 100644 --- a/wqflask/wqflask/templates/partial_correlations.html +++ b/wqflask/wqflask/templates/partial_correlations.html @@ -4,6 +4,9 @@ {%block css%} + + + {%endblock%} {%block content%} @@ -204,7 +207,7 @@
- + @@ -337,5 +340,7 @@ {%if step == "select-corr-method":%} + {%endif%} {%endblock%} -- cgit v1.2.3 From d7d88b138fa4b418ba8590410999fdff8c4eed2b Mon Sep 17 00:00:00 2001 From: zsloan Date: Tue, 22 Feb 2022 22:09:41 +0000 Subject: Add checkbox and index columns --- wqflask/wqflask/static/new/javascript/partial_correlations.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wqflask/wqflask/static/new/javascript/partial_correlations.js b/wqflask/wqflask/static/new/javascript/partial_correlations.js index 030b63f2..3bf2acc4 100644 --- a/wqflask/wqflask/static/new/javascript/partial_correlations.js +++ b/wqflask/wqflask/static/new/javascript/partial_correlations.js @@ -290,11 +290,11 @@ function send_data_and_process_results( function initializePcorrTable(dataType){ tableId = "part-corr-results-" + dataType.toLowerCase(); if (dataType == "Publish") { - orderCol = 7; + orderCol = 8; } else if (dataType == "ProbeSet") { - orderCol = 11; + orderCol = 12; } else { - orderCol = 6; + orderCol = 7; } $('#' + tableId).dataTable( { -- cgit v1.2.3 From 73a0ad695f4c7ad9d4a313b9bb9231dcaab5fcb7 Mon Sep 17 00:00:00 2001 From: zsloan Date: Tue, 22 Feb 2022 22:11:10 +0000 Subject: Add container div --- wqflask/wqflask/templates/partial_correlations.html | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/wqflask/wqflask/templates/partial_correlations.html b/wqflask/wqflask/templates/partial_correlations.html index ad1c674a..0cf09afc 100644 --- a/wqflask/wqflask/templates/partial_correlations.html +++ b/wqflask/wqflask/templates/partial_correlations.html @@ -10,6 +10,7 @@ {%endblock%} {%block content%} +
@@ -210,7 +211,8 @@
- + + @@ -226,7 +228,8 @@ - + + @@ -249,7 +252,8 @@ - + + @@ -264,7 +268,8 @@ - + + @@ -286,7 +291,8 @@ - + + @@ -309,6 +315,8 @@ + + @@ -334,6 +342,7 @@
+ {%endblock%} {%block js%} -- cgit v1.2.3 From f1e0a91802e7236412e7de211ec249cd8725c64a Mon Sep 17 00:00:00 2001 From: zsloan Date: Tue, 22 Feb 2022 22:11:59 +0000 Subject: Remove CSS from partial_correlations.css This CSS is overwritten by CSS from trait_list.css and show_trait.css --- wqflask/wqflask/static/new/css/partial_correlations.css | 5 ----- 1 file changed, 5 deletions(-) diff --git a/wqflask/wqflask/static/new/css/partial_correlations.css b/wqflask/wqflask/static/new/css/partial_correlations.css index 8ff5eae7..84a0877f 100644 --- a/wqflask/wqflask/static/new/css/partial_correlations.css +++ b/wqflask/wqflask/static/new/css/partial_correlations.css @@ -30,11 +30,6 @@ tr:nth-of-type(2n) { background: #F9F9F9; } -thead tr { - background: #336699; - line-height: 1.5em; -} - .with-trait { margin-left: 0.7em; position: relative; -- cgit v1.2.3 From 78d8ed64a072851ddc58281553dfc9806c25b332 Mon Sep 17 00:00:00 2001 From: Alexander Kabui Date: Sun, 27 Feb 2022 10:21:46 +0300 Subject: code refactoring --- .../wqflask/correlation_matrix/show_corr_matrix.py | 50 ++++++---------------- 1 file changed, 14 insertions(+), 36 deletions(-) diff --git a/wqflask/wqflask/correlation_matrix/show_corr_matrix.py b/wqflask/wqflask/correlation_matrix/show_corr_matrix.py index d5ec738b..499a4e13 100644 --- a/wqflask/wqflask/correlation_matrix/show_corr_matrix.py +++ b/wqflask/wqflask/correlation_matrix/show_corr_matrix.py @@ -33,7 +33,6 @@ from utility import corr_result_helpers from utility.redis_tools import get_redis_conn - from gn3.computations.principal_component_analysis import compute_pca from gn3.computations.principal_component_analysis import process_factor_loadings_tdata @@ -44,6 +43,7 @@ from gn3.computations.principal_component_analysis import cache_pca_dataset Redis = get_redis_conn() THIRTY_DAYS = 60 * 60 * 24 * 30 + class CorrelationMatrix: def __init__(self, start_vars): @@ -54,7 +54,6 @@ class CorrelationMatrix: self.all_sample_list = [] self.traits = [] - self.insufficient_shared_samples = False self.do_PCA = True # ZS: Getting initial group name before verifying all traits are in the same group in the following loop this_group = self.trait_list[0][1].group.name @@ -169,12 +168,12 @@ class CorrelationMatrix: self.pca_works = "False" try: - if self.do_PCA == True: self.pca_works = "True" self.pca_trait_ids = [] pca = self.calculate_pca() - self.loadings_array = process_factor_loadings_tdata(self.loadings,len(self.trait_list)) + self.loadings_array = process_factor_loadings_tdata( + self.loadings, len(self.trait_list)) else: self.pca_works = "False" except: @@ -189,43 +188,25 @@ class CorrelationMatrix: def calculate_pca(self): - pca = compute_pca(self.pca_corr_results) self.loadings = pca["components"] self.scores = pca["scores"] - this_group_name = self.trait_list[0][1].group.name temp_dataset = data_set.create_dataset( dataset_name="Temp", dataset_type="Temp", group_name=this_group_name) temp_dataset.group.get_samplelist() + pca_dataset = generate_pca_temp_dataset(species=temp_dataset.group.species, group=this_group_name, + traits_data=self.trait_data_array, corr_array=self.pca_corr_results, + dataset_samples=temp_dataset.group.all_samples_ordered(), + shared_samples=self.shared_samples_list, + create_time=datetime.datetime.now().strftime("%m%d%H%M%S")) - species = temp_dataset.group.species - - group =this_group_name - - trait_data_array = self.trait_data_array - - pca_corr = self.pca_corr_results - - sample_list = temp_dataset.group.all_samples_ordered() - - - shared = self.shared_samples_list + cache_pca_dataset(Redis, THIRTY_DAYS, pca_dataset) - dt_time = datetime.datetime.now().strftime("%m%d%H%M%S") - - - - results = generate_pca_temp_dataset(species = species, group= group,traits_data = self.trait_data_array,corr_array = self.pca_corr_results,dataset_samples = sample_list, shared_samples=shared,create_time=dt_time) - - - - cache_pca_dataset(Redis,THIRTY_DAYS,results) - - self.pca_trait_ids = list(results.keys()) + self.pca_trait_ids = list(pca_dataset.keys()) return pca @@ -242,11 +223,11 @@ def export_corr_matrix(corr_results): output_file.write("\n") output_file.write("Correlation ") for i, item in enumerate(corr_results[0]): - output_file.write("Trait" + str(i + 1) + ": " + \ + output_file.write("Trait" + str(i + 1) + ": " + str(item[0].dataset.name) + "::" + str(item[0].name) + "\t") output_file.write("\n") for i, row in enumerate(corr_results): - output_file.write("Trait" + str(i + 1) + ": " + \ + output_file.write("Trait" + str(i + 1) + ": " + str(row[0][0].dataset.name) + "::" + str(row[0][0].name) + "\t") for item in row: output_file.write(str(item[1]) + "\t") @@ -256,17 +237,14 @@ def export_corr_matrix(corr_results): output_file.write("\n") output_file.write("N ") for i, item in enumerate(corr_results[0]): - output_file.write("Trait" + str(i) + ": " + \ + output_file.write("Trait" + str(i) + ": " + str(item[0].dataset.name) + "::" + str(item[0].name) + "\t") output_file.write("\n") for i, row in enumerate(corr_results): - output_file.write("Trait" + str(i) + ": " + \ + output_file.write("Trait" + str(i) + ": " + str(row[0][0].dataset.name) + "::" + str(row[0][0].name) + "\t") for item in row: output_file.write(str(item[2]) + "\t") output_file.write("\n") return corr_matrix_filename, matrix_export_path - - - -- cgit v1.2.3 From f61bf4beb6622c1553ea441cd63192d4ec778670 Mon Sep 17 00:00:00 2001 From: zsloan Date: Mon, 28 Feb 2022 20:22:49 +0000 Subject: Add distinct to fix issue where GeneRIF search returns duplicates --- wqflask/wqflask/do_search.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wqflask/wqflask/do_search.py b/wqflask/wqflask/do_search.py index f8cc482f..9a06ea80 100644 --- a/wqflask/wqflask/do_search.py +++ b/wqflask/wqflask/do_search.py @@ -82,7 +82,7 @@ class MrnaAssaySearch(DoSearch): DoSearch.search_types['ProbeSet'] = "MrnaAssaySearch" base_query = """ - SELECT + SELECT DISTINCT ProbeSetFreeze.`Name`, ProbeSetFreeze.`FullName`, ProbeSet.`Name`, -- cgit v1.2.3 From c71218d9221789f850f5d93329f2d466527d3c52 Mon Sep 17 00:00:00 2001 From: zsloan Date: Mon, 28 Feb 2022 21:16:20 +0000 Subject: Fix fulltext search for certain searches Hyphens in fulltext searches were causing problems, but a recent commit I made to fix the issue apparently had some side effects for other types of searches, in addition to make such searches someewhat slower Apparently the issue wasn't just the hyphens, but also the text to either side of the hyphen being lower than the minimum word length (which is either 2 or 3 for us, can't remember). To try and address this, I did a regular expression check for a pattern with text of <3 legnth to either side of a hyphen, and when that's the case I add quotes from the search term plus an asterisk, which seeems to be necessary to get it to not treat the hyphen as a delimiter and to correctly detect the search term --- wqflask/wqflask/do_search.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/wqflask/wqflask/do_search.py b/wqflask/wqflask/do_search.py index 9a06ea80..2cc6aa61 100644 --- a/wqflask/wqflask/do_search.py +++ b/wqflask/wqflask/do_search.py @@ -1,6 +1,7 @@ -import string -import requests import json +import re +import requests +import string from flask import Flask, g @@ -137,18 +138,17 @@ class MrnaAssaySearch(DoSearch): search_string = escape(self.search_term[0]) if self.search_term[0] != "*": + if re.search("\w{1,2}\-\w+|\w+\-\w{1,2}", self.search_term[0]): + search_string = f'"{search_string}*"' + match_clause = f"""((MATCH (ProbeSet.Name, ProbeSet.description, ProbeSet.symbol, + alias, GenbankId, UniGeneId, Probe_Target_Description) - AGAINST ('{search_string}' IN BOOLEAN MODE)) OR ( - alias LIKE '%%; {search_string};%%' OR - alias LIKE '{search_string};%%' OR - alias LIKE '%%; {search_string}' OR - alias LIKE '{search_string}' - )) AND """ + AGAINST ('{search_string}' IN BOOLEAN MODE))) AND """ else: match_clause = "" -- cgit v1.2.3 From f733cf955ed5137c4a1a67860b26bbaf23650c83 Mon Sep 17 00:00:00 2001 From: Alexander Kabui Date: Mon, 7 Mar 2022 11:05:24 +0300 Subject: Fix/caching (#679) * unlink file for JSONDecodeError * fix for avoiding caching empty dicts * fix for checking null dicts--- wqflask/wqflask/correlation/pre_computes.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/wqflask/wqflask/correlation/pre_computes.py b/wqflask/wqflask/correlation/pre_computes.py index 975a53b8..cb2f4470 100644 --- a/wqflask/wqflask/correlation/pre_computes.py +++ b/wqflask/wqflask/correlation/pre_computes.py @@ -6,29 +6,40 @@ from pathlib import Path from base.data_set import query_table_timestamp from base.webqtlConfig import TMPDIR +from json.decoder import JSONDecodeError + def fetch_all_cached_metadata(dataset_name): """in a gvein dataset fetch all the traits metadata""" file_name = generate_filename(dataset_name, suffix="metadata") - file_path = os.path.join(TMPDIR, file_name) + file_path = Path(TMPDIR, file_name) try: with open(file_path, "r+") as file_handler: dataset_metadata = json.load(file_handler) + return (file_path, dataset_metadata) except FileNotFoundError: - Path(file_path).touch(exist_ok=True) - return (file_path, {}) + pass + + except JSONDecodeError: + file_path.unlink() + + file_path.touch(exist_ok=True) + + return (file_path, {}) def cache_new_traits_metadata(dataset_metadata: dict, new_traits_metadata, file_path: str): """function to cache the new traits metadata""" - if bool(new_traits_metadata): - dataset_metadata.update(new_traits_metadata) - + if (dataset_metadata == {} and new_traits_metadata == {}): + return + + dataset_metadata.update(new_traits_metadata) + with open(file_path, "w+") as file_handler: json.dump(dataset_metadata, file_handler) -- cgit v1.2.3 From aed325dcf84629bc26809eae6d537f81dcc40cf7 Mon Sep 17 00:00:00 2001 From: Alexander Kabui Date: Tue, 8 Mar 2022 17:33:27 +0300 Subject: make fixes;variable names and kwargs --- .../wqflask/correlation_matrix/show_corr_matrix.py | 29 +++++++++++----------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/wqflask/wqflask/correlation_matrix/show_corr_matrix.py b/wqflask/wqflask/correlation_matrix/show_corr_matrix.py index 499a4e13..9b4cb2eb 100644 --- a/wqflask/wqflask/correlation_matrix/show_corr_matrix.py +++ b/wqflask/wqflask/correlation_matrix/show_corr_matrix.py @@ -33,12 +33,11 @@ from utility import corr_result_helpers from utility.redis_tools import get_redis_conn -from gn3.computations.principal_component_analysis import compute_pca +from gn3.computations.pca import compute_pca -from gn3.computations.principal_component_analysis import process_factor_loadings_tdata -from gn3.computations.principal_component_analysis import generate_pca_traits_vals -from gn3.computations.principal_component_analysis import generate_pca_temp_dataset -from gn3.computations.principal_component_analysis import cache_pca_dataset +from gn3.computations.pca import process_factor_loadings_tdata +from gn3.computations.pca import generate_pca_temp_traits +from gn3.computations.pca import cache_pca_dataset Redis = get_redis_conn() THIRTY_DAYS = 60 * 60 * 24 * 30 @@ -168,12 +167,12 @@ class CorrelationMatrix: self.pca_works = "False" try: - if self.do_PCA == True: + if self.do_PCA: self.pca_works = "True" self.pca_trait_ids = [] pca = self.calculate_pca() self.loadings_array = process_factor_loadings_tdata( - self.loadings, len(self.trait_list)) + factor_loadings=self.loadings, traits_num=len(self.trait_list)) else: self.pca_works = "False" except: @@ -198,15 +197,17 @@ class CorrelationMatrix: dataset_name="Temp", dataset_type="Temp", group_name=this_group_name) temp_dataset.group.get_samplelist() - pca_dataset = generate_pca_temp_dataset(species=temp_dataset.group.species, group=this_group_name, - traits_data=self.trait_data_array, corr_array=self.pca_corr_results, - dataset_samples=temp_dataset.group.all_samples_ordered(), - shared_samples=self.shared_samples_list, - create_time=datetime.datetime.now().strftime("%m%d%H%M%S")) + pca_temp_traits = generate_pca_temp_traits(species=temp_dataset.group.species, group=this_group_name, + traits_data=self.trait_data_array, corr_array=self.pca_corr_results, + dataset_samples=temp_dataset.group.all_samples_ordered(), + shared_samples=self.shared_samples_list, + create_time=datetime.datetime.now().strftime("%m%d%H%M%S")) - cache_pca_dataset(Redis, THIRTY_DAYS, pca_dataset) + + cache_pca_dataset(redis_conn=get_redis_conn( + ), exp_days=60 * 60 * 24 * 30, pca_trait_dict=pca_temp_traits) - self.pca_trait_ids = list(pca_dataset.keys()) + self.pca_trait_ids = list(pca_temp_traits.keys()) return pca -- cgit v1.2.3 From 6359dc2bf8973991072634e6a2b8d6a8a038166a Mon Sep 17 00:00:00 2001 From: Alexander Kabui Date: Tue, 8 Mar 2022 17:39:22 +0300 Subject: remove global variables;pep8 formatting --- .../wqflask/correlation_matrix/show_corr_matrix.py | 24 +++++++++------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/wqflask/wqflask/correlation_matrix/show_corr_matrix.py b/wqflask/wqflask/correlation_matrix/show_corr_matrix.py index 9b4cb2eb..88d62045 100644 --- a/wqflask/wqflask/correlation_matrix/show_corr_matrix.py +++ b/wqflask/wqflask/correlation_matrix/show_corr_matrix.py @@ -21,27 +21,23 @@ import datetime import random import string - - import numpy as np import scipy -from base import data_set +from base.data_set import create_dataset from base.webqtlConfig import GENERATED_TEXT_DIR -from utility import helper_functions -from utility import corr_result_helpers + + +from utility.helper_functions import get_trait_db_obs +from utility.corr_result_helpers import normalize_values from utility.redis_tools import get_redis_conn from gn3.computations.pca import compute_pca - from gn3.computations.pca import process_factor_loadings_tdata from gn3.computations.pca import generate_pca_temp_traits from gn3.computations.pca import cache_pca_dataset -Redis = get_redis_conn() -THIRTY_DAYS = 60 * 60 * 24 * 30 - class CorrelationMatrix: @@ -49,7 +45,7 @@ class CorrelationMatrix: trait_db_list = [trait.strip() for trait in start_vars['trait_list'].split(',')] - helper_functions.get_trait_db_obs(self, trait_db_list) + get_trait_db_obs(self, trait_db_list) self.all_sample_list = [] self.traits = [] @@ -117,7 +113,7 @@ class CorrelationMatrix: if sample in self.shared_samples_list: self.shared_samples_list.remove(sample) - this_trait_vals, target_vals, num_overlap = corr_result_helpers.normalize_values( + this_trait_vals, target_vals, num_overlap = normalize_values( this_trait_vals, target_vals) if num_overlap < self.lowest_overlap: @@ -193,8 +189,9 @@ class CorrelationMatrix: self.scores = pca["scores"] this_group_name = self.trait_list[0][1].group.name - temp_dataset = data_set.create_dataset( - dataset_name="Temp", dataset_type="Temp", group_name=this_group_name) + temp_dataset = create_dataset( + dataset_name="Temp", dataset_type="Temp", + group_name=this_group_name) temp_dataset.group.get_samplelist() pca_temp_traits = generate_pca_temp_traits(species=temp_dataset.group.species, group=this_group_name, @@ -203,7 +200,6 @@ class CorrelationMatrix: shared_samples=self.shared_samples_list, create_time=datetime.datetime.now().strftime("%m%d%H%M%S")) - cache_pca_dataset(redis_conn=get_redis_conn( ), exp_days=60 * 60 * 24 * 30, pca_trait_dict=pca_temp_traits) -- cgit v1.2.3 From 80b02d37f60d172be01bf8cd62bd84b406b1e0dd Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Sat, 26 Feb 2022 08:11:09 +0300 Subject: Use one-step process for the partial correlations computations This commit gets rid of the multi-step partial correlations process replacing it with a single-step process. Summary of changes: * wqflask/wqflask/collect.py: Add function to format the trait details in a format that is usable for the partial correlations system. * wqflask/wqflask/database.py: Provide function to create a connection to the database * wqflask/wqflask/partial_correlations_views.py: Rework the code to enable the one-step process for the partial correlations computations * wqflask/wqflask/static/new/javascript/partial_correlations.js: Get rid of code that supported the multi-step process * wqflask/wqflask/templates/collections/view.html: Remove inconsistent UI elements. Attach traits info in a form usable for the partial correlations * wqflask/wqflask/templates/partial_correlations.html: delete html template * wqflask/wqflask/templates/partial_correlations/pcorrs_error.html: provide a html template to display errors in the partial correlations computation process * wqflask/wqflask/templates/partial_correlations/pcorrs_poll_results.html: UI template to provide user with feedback as the computations continue in the background * wqflask/wqflask/templates/partial_correlations/pcorrs_results_presentation.html: UI template to present the results of successful computation * wqflask/wqflask/templates/partial_correlations/pcorrs_select_operations.html: UI template to trigger the partial correlations computations * wqflask/wqflask/templates/tool_buttons.html: Add the partial correlations button to the template to ensure a consistent look and feel --- wqflask/wqflask/collect.py | 31 +- wqflask/wqflask/database.py | 12 + wqflask/wqflask/partial_correlations_views.py | 415 +++++++++++---------- .../static/new/javascript/partial_correlations.js | 358 ++---------------- wqflask/wqflask/templates/collections/view.html | 27 +- .../wqflask/templates/partial_correlations.html | 355 ------------------ .../partial_correlations/pcorrs_error.html | 54 +++ .../partial_correlations/pcorrs_poll_results.html | 15 + .../pcorrs_results_presentation.html | 249 +++++++++++++ .../pcorrs_select_operations.html | 146 ++++++++ wqflask/wqflask/templates/tool_buttons.html | 7 + 11 files changed, 762 insertions(+), 907 deletions(-) delete mode 100644 wqflask/wqflask/templates/partial_correlations.html create mode 100644 wqflask/wqflask/templates/partial_correlations/pcorrs_error.html create mode 100644 wqflask/wqflask/templates/partial_correlations/pcorrs_poll_results.html create mode 100644 wqflask/wqflask/templates/partial_correlations/pcorrs_results_presentation.html create mode 100644 wqflask/wqflask/templates/partial_correlations/pcorrs_select_operations.html diff --git a/wqflask/wqflask/collect.py b/wqflask/wqflask/collect.py index 3475ae5d..815bb7c1 100644 --- a/wqflask/wqflask/collect.py +++ b/wqflask/wqflask/collect.py @@ -189,6 +189,30 @@ def delete_collection(): return redirect(url_for('list_collections')) +def trait_info_str(trait): + """Provide a string representation for given trait""" + def __trait_desc(trt): + if trait.dataset.type == "Geno": + return f"Marker: {trt.name}" + return trt.description_display or "N/A" + + def __symbol(trt): + return (trt.symbol or trt.abbreviation or "N/A")[:20] + + def __lrs(trt): + return ( + f"{float(trait.LRS_score_repr):0.3f}" if float(trait.LRS_score_repr) > 0 + else f"{trait.LRS_score_repr}") + + def __location(trt): + if hasattr(trt, "location_repr"): + return trt.location_repr + return None + + return "{}|||{}|||{}|||{}|||{}|||{:0.3f}|||{}|||{}".format( + trait.name, trait.dataset.name, __trait_desc(trait), __symbol(trait), + __location(trait), trait.mean, __lrs(trait), trait.LRS_location_repr) + @app.route("/collections/view") def view_collection(): params = request.args @@ -227,9 +251,10 @@ def view_collection(): if "json" in params: return json.dumps(json_version) else: - return render_template("collections/view.html", - **collection_info - ) + return render_template( + "collections/view.html", + trait_info_str=trait_info_str, + **collection_info) @app.route("/collections/change_name", methods=('POST',)) diff --git a/wqflask/wqflask/database.py b/wqflask/wqflask/database.py index 11f8d287..e485bcf1 100644 --- a/wqflask/wqflask/database.py +++ b/wqflask/wqflask/database.py @@ -1,10 +1,13 @@ # Module to initialize sqlalchemy with flask +import MySQLdb from sqlalchemy import create_engine from sqlalchemy.orm import scoped_session, sessionmaker from sqlalchemy.ext.declarative import declarative_base from utility.tools import SQL_URI +from flask import current_app + engine = create_engine(SQL_URI, encoding="latin1") @@ -16,3 +19,12 @@ Base.query = db_session.query_property() # Initialise the db Base.metadata.create_all(bind=engine) + + +def database_connection(): + """Returns a database connection""" + return MySQLdb.Connect( + db=current_app.config.get("DB_NAME"), + user=current_app.config.get("DB_USER"), + passwd=current_app.config.get("DB_PASS"), + host=current_app.config.get("DB_HOST")) diff --git a/wqflask/wqflask/partial_correlations_views.py b/wqflask/wqflask/partial_correlations_views.py index 6bc5efee..659b49e9 100644 --- a/wqflask/wqflask/partial_correlations_views.py +++ b/wqflask/wqflask/partial_correlations_views.py @@ -1,142 +1,31 @@ +import json +import math +import requests +from functools import reduce from typing import Union, Tuple -import MySQLdb -from gn3.db.traits import retrieve_trait_info -from flask import flash, request, current_app, render_template -from gn3.computations.partial_correlations import partial_correlations_entry +from flask import ( + flash, + request, + url_for, + redirect, + current_app, + render_template) from wqflask import app from utility.tools import GN_SERVER_URL +from wqflask.database import database_connection +from gn3.db.partial_correlations import traits_info -def parse_trait(trait_str: str) -> Union[dict, None]: - keys = ( - "name", "dataset", "symbol", "description", "location", "mean_expr", - "max_lrs", "data_hmac") - parts = tuple(part.strip() for part in trait_str.split(":::")) - if len(parts) == len(keys): - return dict(zip(keys, parts)) - return None - -def process_step_select_primary( - primary_trait: dict, control_traits: Tuple[dict, ...], - target_traits: Tuple[dict, ...], - traits_list: Tuple[dict, ...], corr_method: str) -> Tuple[ - str, dict, Tuple[dict, ...], Tuple[dict, ...], Tuple[dict, ...], - str]: - if primary_trait is None: - flash("You must select a primary trait", "alert-danger") - return ( - "select-primary", primary_trait, control_traits, target_traits, - traits_list, corr_method) - - return ( - "select-controls", primary_trait, control_traits, target_traits, - tuple( - trait for trait in traits_list - if trait["data_hmac"] != primary_trait["data_hmac"]), - corr_method) - -def process_step_select_controls( - primary_trait: dict, control_traits: Tuple[dict, ...], - target_traits: Tuple[dict, ...], - traits_list: Tuple[dict, ...], corr_method: str) -> Tuple[ - str, dict, Tuple[dict, ...], Tuple[dict, ...], Tuple[dict, ...], - str]: - if len(control_traits) == 0 or len(control_traits) > 3: - flash( - ("You must select a minimum of one control trait, up to a maximum " - "of three control traits."), - "alert-danger") - return ( - "select-controls", primary_trait, control_traits, target_traits, - traits_list, corr_method) - - hmacs =(primary_trait["data_hmac"],) + tuple( - trait["data_hmac"] for trait in control_traits) - return ( - "select-corr-method", primary_trait, control_traits, target_traits, - tuple( - trait for trait in traits_list if trait["data_hmac"] not in hmacs), - corr_method) - -def process_step_select_targets( - primary_trait: dict, control_traits: Tuple[dict, ...], - target_traits: Tuple[dict, ...], - traits_list: Tuple[dict, ...], corr_method: str) -> Tuple[ - str, dict, Tuple[dict, ...], Tuple[dict, ...], Tuple[dict, ...], - str]: - if len(target_traits) == 0: - flash( - "You must select at least one target trait.", "alert-danger") - return ( - "select-targets", primary_trait, control_traits, target_traits, - traits_list, corr_method) - - hmacs =(primary_trait["data_hmac"],) + tuple( - trait["data_hmac"] for trait in (control_traits + target_traits)) - return ( - "select-corr-method", primary_trait, control_traits, target_traits, - tuple( - trait for trait in traits_list if trait["data_hmac"] not in hmacs), - corr_method) - -def process_step_select_corr_method( - primary_trait: dict, control_traits: Tuple[dict, ...], - target_traits: Tuple[dict, ...], - traits_list: Tuple[dict, ...], corr_method: str) -> Tuple[ - str, dict, Tuple[dict, ...], Tuple[dict, ...], Tuple[dict, ...], - str]: - methods = ( - "genetic correlation, pearson's r", - "genetic correlation, spearman's rho", - "sgo literature correlation", - "tissue correlation, pearson's r", - "tissue correlation, spearman's rho") - if corr_method.lower() not in methods: - flash( - "Selected method is unknown.", "alert-danger") - return ( - "select-corr-method", primary_trait, control_traits, target_traits, - traits_list, corr_method) - - hmacs =(primary_trait["data_hmac"],) + tuple( - trait["data_hmac"] for trait in (control_traits + target_traits)) - return ( - "run-correlation", primary_trait, control_traits, target_traits, - tuple( - trait for trait in traits_list if trait["data_hmac"] not in hmacs), - corr_method) - -def process_step( - step: str, primary_trait: dict, control_traits: Tuple[dict, ...], - target_traits: Tuple[dict, ...], traits_list: Tuple[dict, ...], - corr_method: str) -> Tuple[ - str, dict, Tuple[dict, ...], Tuple[dict, ...], Tuple[dict, ...], - str]: - processor_functions = { - # "select-traits": lambda arg: arg, - "select-primary": process_step_select_primary, - "select-controls": process_step_select_controls, - "select-targets": process_step_select_targets, - "select-corr-method": process_step_select_corr_method - } - return processor_functions[(step or "select-primary")]( - primary_trait, control_traits, target_traits, traits_list, corr_method) - -def sequence_of_traits(trait_strs) -> Tuple[dict, ...]: - return tuple(filter( - lambda trt: trt is not None, - (parse_trait(tstr.strip()) for tstr in trait_strs))) - -def publish_target_dabases(conn, group, threshold): +def publish_target_databases(conn, groups, threshold): query = ( "SELECT PublishFreeze.FullName,PublishFreeze.Name " "FROM PublishFreeze, InbredSet " "WHERE PublishFreeze.InbredSetId = InbredSet.Id " - "AND InbredSet.Name = %(group)s " - "AND PublishFreeze.public > %(threshold)s") + f"AND InbredSet.Name IN ({', '.join(['%s'] * len(groups))}) " + "AND PublishFreeze.public > %s") with conn.cursor() as cursor: - cursor.execute(query, {"group": group, "threshold": threshold}) + cursor.execute(query, tuple(groups) + (threshold,)) res = cursor.fetchall() if res: return tuple( @@ -144,15 +33,15 @@ def publish_target_dabases(conn, group, threshold): return tuple() -def geno_target_databases(conn, group, threshold): +def geno_target_databases(conn, groups, threshold): query = ( "SELECT GenoFreeze.FullName,GenoFreeze.Name " "FROM GenoFreeze, InbredSet " "WHERE GenoFreeze.InbredSetId = InbredSet.Id " - "AND InbredSet.Name = %(group)s " - "AND GenoFreeze.public > %(threshold)s") + f"AND InbredSet.Name IN ({', '.join(['%s'] * len(groups))}) " + "AND GenoFreeze.public > %s") with conn.cursor() as cursor: - cursor.execute(query, {"group": group, "threshold": threshold}) + cursor.execute(query, tuple(groups) + (threshold,)) res = cursor.fetchall() if res: return tuple( @@ -160,27 +49,26 @@ def geno_target_databases(conn, group, threshold): return tuple() -def probeset_target_databases(conn, group, threshold): +def probeset_target_databases(conn, groups, threshold): query1 = "SELECT Id, Name FROM Tissue order by Name" - query2 = ( - "SELECT ProbeFreeze.TissueId, ProbeSetFreeze.FullName, ProbeSetFreeze.Name " - "FROM ProbeSetFreeze, ProbeFreeze, InbredSet " - "WHERE ProbeSetFreeze.ProbeFreezeId = ProbeFreeze.Id " - "AND ProbeFreeze.TissueId IN %(tissue_ids)s " - "AND ProbeSetFreeze.public > %(threshold)s " - "AND ProbeFreeze.InbredSetId = InbredSet.Id " - "AND InbredSet.Name like %(group)s " - "ORDER BY ProbeSetFreeze.CreateTime desc, ProbeSetFreeze.AvgId") with conn.cursor() as cursor: cursor.execute(query1) tissue_res = cursor.fetchall() if tissue_res: tissue_ids = tuple(row[0] for row in tissue_res) - cursor.execute( - query2,{ - "tissue_ids": tissue_ids, "group": f"{group}%%", - "threshold": threshold - }) + groups_clauses = ["InbredSet.Name like %s"] * len(groups) + query2 = ( + "SELECT ProbeFreeze.TissueId, ProbeSetFreeze.FullName, " + "ProbeSetFreeze.Name " + "FROM ProbeSetFreeze, ProbeFreeze, InbredSet " + "WHERE ProbeSetFreeze.ProbeFreezeId = ProbeFreeze.Id " + "AND ProbeFreeze.TissueId IN " + f"({', '.join(['%s'] * len(tissue_ids))}) " + "AND ProbeSetFreeze.public > %s " + "AND ProbeFreeze.InbredSetId = InbredSet.Id " + f"AND ({' OR '.join(groups_clauses)}) " + "ORDER BY ProbeSetFreeze.CreateTime desc, ProbeSetFreeze.AvgId") + cursor.execute(query2, tissue_ids + (threshold,) + tuple(groups)) db_res = cursor.fetchall() if db_res: databases = tuple( @@ -197,70 +85,201 @@ def probeset_target_databases(conn, group, threshold): return tuple() -def target_databases(conn, step, trait, threshold): +def target_databases(conn, traits, threshold): """ Retrieves the names of possible target databases from the database. """ - if step != "select-corr-method": - return None - - trait_info = retrieve_trait_info( - threshold, f"{trait['dataset']}::{trait['name']}", conn) - group = trait_info["group"] + trait_info = traits_info( + conn, threshold, + tuple(f"{trait['dataset']}::{trait['trait_name']}" for trait in traits)) + groups = tuple(set(row["db"]["group"] for row in trait_info)) return ( - publish_target_dabases(conn, group, threshold) + - geno_target_databases(conn, group, threshold) + - probeset_target_databases(conn, group, threshold)) + publish_target_databases(conn, groups, threshold) + + geno_target_databases(conn, groups, threshold) + + probeset_target_databases(conn, groups, threshold)) + +def primary_error(args): + if len(args["primary_trait"]) == 0 or len(args["primary_trait"]) > 1: + return { + **args, + "errors": (args.get("errors", tuple()) + + ("You must provide one, and only one primary trait",))} + return args + +def controls_error(args): + if len(args["control_traits"]) == 0 or len(args["control_traits"]) > 3: + return { + **args, + "errors": ( + args.get("errors", tuple()) + + (("You must provide at least one control trait, and a maximum " + "of three control traits"),))} + return args + +def target_db_error(args): + if not args["target_db"]: + return { + **args, + "errors": ( + args.get("errors", tuple()) + + ("The target database must be provided",))} + return args + +def method_error(args): + methods = ( + "genetic correlation, pearson's r", + "genetic correlation, spearman's rho", + "sgo literature correlation", + "tissue correlation, pearson's r", + "tissue correlation, spearman's rho") + if not args["method"] or args["method"].lower() not in methods: + return { + **args, + "errors": ( + args.get("errors", tuple()) + + ("Invalid correlation method provided",))} + return args -def pcorrelations(conn, values): - if values["step"] != "run-correlation": - return None +def criteria_error(args): + try: + int(args.get("criteria", "invalid")) + return args + except ValueError: + return { + **args, + "errors": ( + args.get("errors", tuple()) + + ("Invalid return number provided",))} - def trait_fullname(trait): - return f"{trait['dataset']}::{trait['name']}" +def errors(args): + return criteria_error(method_error(target_db_error(controls_error( + primary_error(args))))) - return partial_correlations_entry( - conn, trait_fullname(values["primary_trait"]), - tuple(trait_fullname(trait) for trait in values["control_traits"]), - values["method"], values["criteria"], values["target_db"]) +def __classify_args(acc, item): + if item[1].startswith("primary_"): + return { + **acc, + "primary_trait": (acc.get("primary_trait", tuple()) + (item,))} + if item[1].startswith("controls_"): + return {**acc, "control_traits": (acc.get("control_traits", tuple()) + (item,))} + if item[0] == "target_db": + return {**acc, "target_db": item[1]} + if item[0] == "method": + return {**acc, "method": item[1]} + if item[0] == "criteria": + return {**acc, "criteria": item[1]} + return acc + +def __build_args(raw_form, traits): + args = reduce(__classify_args, raw_form.items(), {}) + return { + **args, + "primary_trait": [ + item for item in traits if item["trait_name"] in + (name[1][8:] for name in args["primary_trait"])], + "control_traits": [ + item for item in traits if item["trait_name"] in + (name[1][9:] for name in args["control_traits"])] + } + +def parse_trait(trait_str): + return dict(zip( + ("trait_name", "dataset", "description", "symbol", "location", "mean", + "lrs", "lrs_location"), + trait_str.strip().split("|||"))) + +def response_error_message(response): + error_messages = { + 404: ("We could not connect to the API server at this time. " + "Try again later."), + 500: ("The API server experienced a problem. We will be working on a " + "fix. Please try again later.") + } + return error_messages.get( + response.status_code, + "General API server error!!") + +def render_error(error_message): + return render_template( + "partial_correlations/pcorrs_error.html", + message = error_message) + +def handle_200_response(response): + if response["status"] == "success": + return redirect( + url_for( + "poll_partial_correlation_results", + command_id=response["results"]), + code=303) + return render_error(response["results"]) + +def handle_response(response): + if response.status_code != 200: + return render_template( + "partial_correlations/pcorrs_error.html", + message = response_error_message(response)) + return handle_200_response(response.json()) @app.route("/partial_correlations", methods=["POST"]) def partial_correlations(): form = request.form - traits_list = tuple(filter( - lambda trt: trt is not None, - (parse_trait(tstr) for tstr in form.get("traits_list", "").split("|||")))) + traits = tuple( + parse_trait(trait) for trait in + form.get("trait_list").split(";;;")) - args_dict = dict(zip( - ("step", "primary_trait", "control_traits", "target_traits", - "traits_list", "method"), - process_step( - form.get("step", None), - parse_trait(form.get("primary_trait", "")), - sequence_of_traits( - form.getlist("control_traits[]") or - form.get("control_traits", "").split("|||")), - sequence_of_traits( - form.getlist("target_traits[]") or - form.get("target_traits", "").split("|||")), - sequence_of_traits(form.get("traits_list", "").split("|||")), - form.get("method")))) + if form.get("submit") == "Run Partial Correlations": + args = errors(__build_args(form, traits)) + if len(args.get("errors", [])) == 0: + post_data = { + **args, + "primary_trait": args["primary_trait"][0] + } + return handle_response(requests.post( + url=f"{GN_SERVER_URL}api/correlation/partial", + json=json.dumps(post_data))) - conn = MySQLdb.Connect( - db=current_app.config.get("DB_NAME"), - user=current_app.config.get("DB_USER"), - passwd=current_app.config.get("DB_PASS"), - host=current_app.config.get("DB_HOST")) - target_dbs = target_databases( - conn, args_dict["step"], args_dict["primary_trait"], 0) + for error in args["errors"]: + flash(error, "alert-danger") - if args_dict["step"] == "run-correlation": - args_dict = { - **args_dict, "target_db": form.get("target_db"), - "criteria": int(form.get("criteria", 500))} + with database_connection() as conn: + target_dbs = target_databases(conn, traits, threshold=0) + return render_template( + "partial_correlations/pcorrs_select_operations.html", + trait_list_str=form.get("trait_list"), + traits=traits, + target_dbs=target_dbs) - corr_results = pcorrelations(conn, args_dict) +def process_pcorrs_command_output(result): + if result["status"] == "success": + def __format_number(num): + if num is None or math.isnan(num): + return "" + if abs(num) <= 1.04E-4: + return f"{num:.2e}" + return f"{num:.5f}" - return render_template( - "partial_correlations.html", **args_dict, target_dbs=target_dbs, - corr_results=corr_results, part_corr_url=f"{GN_SERVER_URL}api/correlation/partial") + return render_template( + "partial_correlations/pcorrs_results_presentation.html", + primary=result["results"]["primary_trait"], + controls=result["results"]["control_traits"], + correlations=result["results"]["correlations"], + dataset_type=result["results"]["dataset_type"], + method=result["results"]["method"], + format_number=__format_number) + if result["status"] == "error": + return render_error( + "The partial correlations computation failed with an error") + +@app.route("/partial_correlations/", methods=["GET"]) +def poll_partial_correlation_results(command_id): + response = requests.get( + url=f"{GN_SERVER_URL}api/async_commands/state/{command_id}") + if response.status_code == 200: + data = response.json() + if data["status"] == "error": + return render_error(response["result"]) + if data["status"] == "success": + return process_pcorrs_command_output(json.loads(data["result"])) + return render_template( + "partial_correlations/pcorrs_poll_results.html", + command_id = command_id) diff --git a/wqflask/wqflask/static/new/javascript/partial_correlations.js b/wqflask/wqflask/static/new/javascript/partial_correlations.js index 3bf2acc4..5de1204c 100644 --- a/wqflask/wqflask/static/new/javascript/partial_correlations.js +++ b/wqflask/wqflask/static/new/javascript/partial_correlations.js @@ -1,336 +1,26 @@ -/** - * This is, hopefully, a short-term stop-gap measure to get the system working - * and to get some feedback, even as better optimisation is done in the - * background to get better response/performance for the partial correlation - * computations - */ - -function key_value(keys, values) { - if(!(keys.length == values.length)) { - Error("The 'keys' and 'values' objects MUST be the same length"); - return null; - } - return values.reduce(function(accumulator, item, index) { - accumulator[keys[index]] = item; - return accumulator; - }, {}); -} - -function trait(trait_str) { - return key_value( - ["name", "dataset", "symbol", "description", "location", "mean_expr", - "max_lrs", "data_hmac"], - trait_str.split(":::")); -} - -function primary_trait() { - trait_string = document.querySelector( - "#partial-correlations-form input[name=primary_trait]").value; - return trait(trait_string); -} - -function control_traits() { - return document.querySelector( - "#partial-correlations-form input[name=control_traits]").value.split( - "|||").map(trait).filter(trait => !(trait === null)); -} - -function correlation_method() { - return document.querySelector( - "#partial-correlations-form select[name=method]").value; -} - -function criteria() { - return document.querySelector( - "#partial-correlations-form select[name=criteria]").value; -} - -function target_db() { - return document.querySelector( - "#partial-correlations-form select[name=target_db]").value; -} - -function partial_corr_request_data() { - return { - "primary_trait": primary_trait(), - "control_traits": control_traits(), - "method": correlation_method(), - "criteria": criteria(), - "target_db": target_db() - } -} - -function rho_or_r(method) { - if (method === "spearman") { - return "rho"; - } - return "r"; -} - -function format_number(num) { - if(num === null) { - return NaN; - } - if(Math.abs(num) <= 1.04e-4) { - return num.toExponential(2); - } - return num.toFixed(5); -} - -function display_publish_results(primary, controls, correlations, method) { - table = document.getElementById("part-corr-results-publish"); - table.setAttribute("style", "display: block;"); - table_body = document.querySelector("#part-corr-results-publish tbody"); - template_row = document.querySelector( - "#part-corr-results-publish tr.template-publish-results-row"); - correlations.forEach(function(item, index, arr) { - new_row = template_row.cloneNode(true); - new_row.setAttribute("class", "results-row"); - new_row.querySelector( - 'td[data-column-heading="Record"]').innerHTML = item["trait_name"]; - new_row.querySelector( - 'td[data-column-heading="Phenotype"]').innerHTML = ( - item["post_publication_description"]); - new_row.querySelector( - 'td[data-column-heading="Authors"]').innerHTML = item["authors"]; - new_row.querySelector( - 'td[data-column-heading="Year"]').innerHTML = item["year"]; - new_row.querySelector( - 'td[data-column-heading="N"]').innerHTML = item["noverlap"]; - new_row.querySelector( - `td[data-column-heading="Partial ${rho_or_r(method)}"]` - ).innerHTML = format_number(item["partial_corr"]); - new_row.querySelector( - `td[data-column-heading="p(partial ${rho_or_r(method)})"]` - ).innerHTML = format_number(item["partial_corr_p_value"]); - new_row.querySelector( - `td[data-column-heading="${rho_or_r(method)}"]` - ).innerHTML = format_number(item["corr"]); - new_row.querySelector( - `td[data-column-heading="p(${rho_or_r(method)})"]` - ).innerHTML = format_number(item["corr_p_value"]); - new_row.querySelector( - `td[data-column-heading="delta ${rho_or_r(method)}"]` - ).innerHTML = format_number(item["delta"]); - table_body.appendChild(new_row); - }); - table_body.removeChild(template_row); -} - -function display_geno_results(primary, controls, correlations, method) { - table = document.getElementById("part-corr-results-geno"); - table.setAttribute("style", "display: block;"); - table_body = document.querySelector("#part-corr-results-geno tbody"); - template_row = document.querySelector( - "#part-corr-results-geno tr.template-geno-results-row"); - correlations.forEach(function(item, index, arr) { - new_row = template_row.cloneNode(true); - new_row.setAttribute("class", "results-row"); - new_row.querySelector( - 'td[data-column-heading="Locus"]').innerHTML = item["trait_name"]; - new_row.querySelector( - 'td[data-column-heading="Chr"]').innerHTML = item["chr"]; - new_row.querySelector( - 'td[data-column-heading="Megabase"]').innerHTML = item["mb"]; - new_row.querySelector( - 'td[data-column-heading="N"]').innerHTML = item["noverlap"]; - new_row.querySelector( - `td[data-column-heading="Partial ${rho_or_r(method)}"]` - ).innerHTML = format_number(item["partial_corr"]); - new_row.querySelector( - `td[data-column-heading="p(partial ${rho_or_r(method)})"]` - ).innerHTML = format_number(item["partial_corr_p_value"]); - new_row.querySelector( - `td[data-column-heading="${rho_or_r(method)}"]` - ).innerHTML = format_number(item["corr"]); - new_row.querySelector( - `td[data-column-heading="p(${rho_or_r(method)})"]` - ).innerHTML = format_number(item["corr_p_value"]); - new_row.querySelector( - `td[data-column-heading="delta ${rho_or_r(method)}"]` - ).innerHTML = format_number(item["delta"]); - table_body.appendChild(new_row); - }); - table_body.removeChild(template_row); -} - -function display_probeset_results(primary, controls, correlations, method) { - table = document.getElementById("part-corr-results-probeset"); - table.setAttribute("style", "display: block;"); - table_body = document.querySelector("#part-corr-results-probeset tbody"); - template_row = document.querySelector( - "#part-corr-results-probeset tr.template-probeset-results-row"); - correlations.forEach(function(item, index, arr) { - new_row = template_row.cloneNode(true); - new_row.setAttribute("class", "results-row"); - new_row.querySelector( - 'td[data-column-heading="Record"]').innerHTML = item["trait_name"]; - new_row.querySelector( - 'td[data-column-heading="Gene ID"]').innerHTML = item["geneid"]; - new_row.querySelector( - 'td[data-column-heading="Homologene ID"]').innerHTML = item["homologeneid"]; - new_row.querySelector( - 'td[data-column-heading="Symbol"]').innerHTML = item["symbol"]; - new_row.querySelector( - 'td[data-column-heading="Description"]').innerHTML = item["description"]; - new_row.querySelector( - 'td[data-column-heading="Chr"]').innerHTML = item["chr"]; - new_row.querySelector( - 'td[data-column-heading="Megabase"]').innerHTML = item["mb"]; - new_row.querySelector( - 'td[data-column-heading="Mean Expr"]').innerHTML = item["mean_expr"]; - new_row.querySelector( - 'td[data-column-heading="N"]').innerHTML = item["noverlap"]; - new_row.querySelector( - `td[data-column-heading="Sample Partial ${rho_or_r(method)}"]` - ).innerHTML = format_number(item["partial_corr"] || NaN); - new_row.querySelector( - `td[data-column-heading="Sample p(partial ${rho_or_r(method)})"]` - ).innerHTML = format_number(item["partial_corr_p_value"] || NaN); - new_row.querySelector( - `td[data-column-heading="Sample ${rho_or_r(method)}"]` - ).innerHTML = format_number(item["corr"] || NaN); - new_row.querySelector( - `td[data-column-heading="Sample p(${rho_or_r(method)})"]` - ).innerHTML = format_number(item["corr_p_value"] || NaN); - new_row.querySelector( - `td[data-column-heading="delta ${rho_or_r(method)}"]` - ).innerHTML = format_number(item["delta"] || NaN); - new_row.querySelector( - `td[data-column-heading="Lit Corr"]` - ).innerHTML = format_number(item["l_corr"] || NaN); - new_row.querySelector( - `td[data-column-heading="Tissue ${rho_or_r(method)}"]` - ).innerHTML = format_number(item["tissue_corr"] || NaN); - new_row.querySelector( - `td[data-column-heading="Tissue p(${rho_or_r(method)})"]` - ).innerHTML = format_number(item["tissue_p_value"] || NaN); - table_body.appendChild(new_row); - }); - template_row.setAttribute("display", "none"); - /*table_body.removeChild(template_row);*/ -} - -function replace_r_with_rho(method) { - /* Mostly utility: Replace `r` with `rho` in the appropriate places */ - pattern = /\br\b/; - if(method == "spearman") { - results_div = document.getElementById("partial-correlation-results"); - headers = results_div.getElementsByTagName("th"); - for(let header of headers) { - header.innerHTML = header.innerHTML.replace(pattern, "rho"); - } - - cells = results_div.getElementsByTagName("td"); - for(let cell of cells) { - cell.setAttribute( - "data-column-heading", - cell.getAttribute( - "data-column-heading").replace(pattern, "rho")); - } +function selected_traits() { + traits = $("#trait_table input:checked").map(function() { + return $(this).attr("data-trait-info"); + }).get(); + if (traits.length == 0){ + num_traits = $("#trait_table input").length + if (num_traits <= 100){ + traits = $("#trait_table input").map(function() { + return $(this).attr("data-trait-info"); + }).get(); + } } -} - -function display_partial_corr_results(data, status, xhr) { - progress_indicator = document.getElementById( - "partial-correlations-progress-indicator").style.display = "none"; - console.log(data); - - replace_r_with_rho(data["results"]["method"]); - - display_functions = { - "Publish": display_publish_results, - "Geno": display_geno_results, - "ProbeSet": display_probeset_results - } - - display_functions[data["results"]["dataset_type"]]( - data["results"]["primary_traits"], - data["results"]["control_traits"], - data["results"]["correlations"], - data["results"]["method"]); - - initializePcorrTable(data["results"]["dataset_type"]); -} - -function display_partial_corr_error(xhr, status, error) { - document.getElementById( - "partial-correlations-progress-indicator").style.display = "none"; - error_element = document.getElementById("part-corr-error"); - panel = document.createElement("div"); - panel.setAttribute("class", "panel panel-danger"); - error_element.appendChild(panel); - - panel_header = document.createElement("div"); - panel_header.setAttribute("class", "panel-heading"); - panel_header.textContent = "Error: " + xhr.status; - panel.appendChild(panel_header); - - panel_body = document.createElement("div"); - panel_body.setAttribute("class", "panel-body"); - panel_body.textContent = xhr.statusText; - panel.appendChild(panel_body); - console.log(xhr) -} - -function send_data_and_process_results( - remote_url, request_data, success_fn, error_fn, indicator_id) { - document.getElementById(indicator_id).style.display = "block"; - $.ajax({ - type: "POST", - url: remote_url, - contentType: "application/json", - data: JSON.stringify(request_data), - dataType: "JSON", - success: success_fn, - error: error_fn - }); -} - -function initializePcorrTable(dataType){ - tableId = "part-corr-results-" + dataType.toLowerCase(); - if (dataType == "Publish") { - orderCol = 8; - } else if (dataType == "ProbeSet") { - orderCol = 12; - } else { - orderCol = 7; - } - - $('#' + tableId).dataTable( { - 'drawCallback': function( settings ) { - $('#' + tableId + ' tr').off().on("click", function(event) { - if (event.target.type !== 'checkbox' && event.target.tagName.toLowerCase() !== 'a') { - var obj =$(this).find('input'); - obj.prop('checked', !obj.is(':checked')); - } - if ($(this).hasClass("selected") && event.target.tagName.toLowerCase() !== 'a'){ - $(this).removeClass("selected") - } else if (event.target.tagName.toLowerCase() !== 'a') { - $(this).addClass("selected") - } - }); - }, - "order": [[orderCol, "asc" ]], - "sDom": "itir", - "iDisplayLength": -1, - "autoWidth": false, - "bDeferRender": true, - "bSortClasses": false, - "paging": false - } ); -} - -$("#partial-correlations-form").submit(function(e) { - e.preventDefault(); -}); - -$("#run-partial-corr-button").click(function(evt) { - send_data_and_process_results( - document.getElementById( - "run-partial-corr-button").getAttribute("data-url"), - partial_corr_request_data(), - display_partial_corr_results, - display_partial_corr_error, - "partial-correlations-progress-indicator"); + return traits +} + +$("#partial-correlations").on("click", function() { + // Submit the form to the `partial_correlations` endpoint + url = $(this).data("url") + traits = selected_traits(); + $("#trait_list").val(traits.reduce(function(acc, str) { + return acc.concat(";;;".concat(str)); + })); + $("input[name=tool_used]").val("Partial Correlation") + $("input[name=form_url]").val(url) + return submit_special(url) }) diff --git a/wqflask/wqflask/templates/collections/view.html b/wqflask/wqflask/templates/collections/view.html index f896471a..6f1a9680 100644 --- a/wqflask/wqflask/templates/collections/view.html +++ b/wqflask/wqflask/templates/collections/view.html @@ -67,22 +67,6 @@   -
- - - -
@@ -128,7 +112,13 @@
{% for this_trait in trait_obs %} - +