From fbaf6e2ee458a3e6cf09a9678264ba74d5046752 Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Fri, 7 Apr 2023 15:06:05 +0300 Subject: Move JS to external file. Generalise functions. Move the javascript to an external file to ease development. Generalise some functions to make it easier to reuse the code down the line. --- .../static/new/javascript/auth/search_genotypes.js | 207 +++++++++++++++++++++ .../templates/oauth2/data-list-genotype.html | 102 +--------- 2 files changed, 210 insertions(+), 99 deletions(-) create mode 100644 wqflask/wqflask/static/new/javascript/auth/search_genotypes.js diff --git a/wqflask/wqflask/static/new/javascript/auth/search_genotypes.js b/wqflask/wqflask/static/new/javascript/auth/search_genotypes.js new file mode 100644 index 00000000..39999e76 --- /dev/null +++ b/wqflask/wqflask/static/new/javascript/auth/search_genotypes.js @@ -0,0 +1,207 @@ +/** + * Build a checkbox: For internal use only + * @param {Genotype Dataset object} A genotype dataset object + * @param {String} A string to initialise the checkbox + */ +function __build_checkbox__(dataset, checkbox_str) { + cell = $(""); + check = $(checkbox_str); + check.val(JSON.stringify(dataset)); + cell.append(check); + return cell; +} + +function link_checkbox(dataset) { + return __build_checkbox__( + dataset, + ''); +} + +function search_checkbox(dataset) { + return __build_checkbox__( + dataset, + ''); +} + +function table_cell(value) { + cell = $(""); + cell.html(value); + return cell; +} + +/** + * Render the table + * @param {String} The selector for the table's ID + * @param {String} The name of the data-* attribute holding the table's data + * @param {Function} The function to call to generate the appropriate checkbox + */ +function render_table(table_id, data_attr_name, checkbox_function) { + $(table_id + " tbody tr").remove(); + table_data = JSON.parse($(table_id).attr(data_attr_name)); + if(table_data.length < 1) { + row = $("") + cell = $(''); + cell.append( + $('')); + cell.append(" "); + cell.append("No genotype datasets remaining."); + row.append(cell); + $(table_id + " tbody").append(row); + } + table_data.forEach(function(dataset) { + row = $("") + row.append(checkbox_function(dataset)); + row.append(table_cell(dataset.InbredSetName)); + row.append(table_cell(dataset.dataset_name)); + row.append(table_cell(dataset.dataset_fullname)); + row.append(table_cell(dataset.dataset_shortname)); + $(table_id + " tbody").append(row); + }); +} + +function in_array(dataset, datasets) { + found = datasets.filter(function(dst) { + return (dst.SpeciesId == dataset.SpeciesId && + dst.InbredSetId == dataset.InbredSetId && + dst.GenoFreezeId == dataset.GenoFreezeId); + }); + return found.length > 0; +} + +function remove_from_table_data(dataset, table_data_source) { + let table_id = table_data_source.table_id.selector; + let data_attr_name = table_data_source.data_attribute_name; + without_dataset = JSON.parse($(table_id).attr(data_attr_name)).filter( + function(dst) { + return !(dst.SpeciesId == dataset.SpeciesId && + dst.InbredSetId == dataset.InbredSetId && + dst.GenoFreezeId == dataset.GenoFreezeId); + }); + $(table_id).attr(data_attr_name, JSON.stringify(without_dataset)); +} + +function add_to_table_data(dataset, table_data_source) { + let table_id = table_data_source.table_id.selector; + let data_attr_name = table_data_source.data_attribute_name; + table_data = JSON.parse($(table_id).attr(data_attr_name)); + if(!in_array(dataset, table_data)) { + table_data.push(dataset); + } + $(table_id).attr(data_attr_name, JSON.stringify(Array.from(table_data))); +} + +function toggle_link_button() { + num_groups = $("#frm-link-genotypes select option").length - 1; + num_selected = JSON.parse( + $("#tbl-link-genotypes").attr("data-selected-datasets")).length; + if(num_groups > 0 && num_selected > 0) { + $("#frm-link-genotypes input[type='submit']").prop("disabled", false); + } else { + $("#frm-link-genotypes input[type='submit']").prop("disabled", true); + } +} + +class InvalidCSSIDSelector extends Error { + constructor(message) { + super(message); + this.name = "InvalidCSSIDSelector"; + } +} + +class InvalidDataAttributeName extends Error { + constructor(message) { + super(message); + this.name = "InvalidDataAttributeName"; + } +} + +/** + * CSSIDSelector: A CSS ID Selector + * @param {String} A CSS selector of the form '#...' + */ +class CSSIDSelector { + constructor(selector) { + if(!selector.startsWith("#")) { + throw new InvalidCSSIDSelector( + "Expected the CSS selector to begin with a `#` character."); + } + let id_str = selector.slice(1, selector.length); + if(document.getElementById(id_str) == null) { + throw new InvalidCSSIDSelector( + "Element with ID '" + id_str + "' does not exist."); + } + this.selector = selector; + } +} + +/** + * TableDataSource: A type to represent a table's data source + * @param {String} A CSS selector for an ID + * @param {String} A `data-*` attribute name + */ +class TableDataSource { + constructor(table_id, data_attribute_name) { + this.table_id = new CSSIDSelector(table_id); + let data = document.querySelector( + table_id).getAttribute(data_attribute_name); + if(data == null) { + throw new InvalidDataAttributeName( + "data-* attribute '" + data_attribute_name + "' does not exist " + + "for table with ID '" + table_id.slice(1, table_id.length) + + "'."); + } else { + this.data_attribute_name = data_attribute_name; + } + } +} + +/** + * Switch the dataset from search table to selection table and vice versa + * @param {Object} A genotype dataset + * @param {TableDataSource} The table to switch the dataset from + * @param {TableDataSource} The table to switch the dataset to + */ +function select_deselect_dataset(dataset, source, destination) { + dest_selector = destination.table_id.selector + dest_data = JSON.parse( + $(dest_selector).attr(destination.data_attribute_name)); + add_to_table_data(dataset, destination); // Add to destination table + remove_from_table_data(dataset, source); // Remove from source table + /***** BEGIN: Re-render tables *****/ + // The `render_table` could be modified to use the checkbox creator function + // from the `TableDataSource` object, once that is modified to have that. + render_table( + "#tbl-link-genotypes", "data-selected-datasets", link_checkbox); + render_table("#tbl-genotypes", "data-datasets", search_checkbox); + toggle_link_button(); + /***** END: Re-render tables *****/ +} + +$(document).ready(function() { + let search_table = new TableDataSource("#tbl-genotypes", "data-datasets"); + let link_table = new TableDataSource( + "#tbl-link-genotypes", "data-selected-datasets"); + + $("#frm-search-traits").submit(function(event) { + event.preventDefault(); + return false; + }); + + /* $("#txt-query").keyup(debounced_search()); */ + + $("#tbl-genotypes").on("change", ".checkbox-search", function(event) { + if(this.checked) { + select_deselect_dataset( + JSON.parse(this.value), search_table, link_table); + } + }); + + $("#tbl-link-genotypes").on("change", ".checkbox-selected", function(event) { + if(!this.checked) { + select_deselect_dataset( + JSON.parse(this.value), link_table, search_table); + } + }); +}); diff --git a/wqflask/wqflask/templates/oauth2/data-list-genotype.html b/wqflask/wqflask/templates/oauth2/data-list-genotype.html index 3ef810ec..3b7cfc1f 100644 --- a/wqflask/wqflask/templates/oauth2/data-list-genotype.html +++ b/wqflask/wqflask/templates/oauth2/data-list-genotype.html @@ -54,7 +54,7 @@ {%for dataset in selected_datasets%} - @@ -151,102 +151,6 @@ {%endblock%} {%block js%} - + {%endblock%} -- cgit v1.2.3 From 1383ab160b302601211b28db3e6a9ad97e53f8e3 Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Sat, 8 Apr 2023 08:07:58 +0300 Subject: oauth2: Link the Genotype datasets. --- wqflask/wqflask/oauth2/client.py | 9 ++++++-- wqflask/wqflask/oauth2/data.py | 26 ++++++++++++++++++++++ .../templates/oauth2/data-list-genotype.html | 5 ++++- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/wqflask/wqflask/oauth2/client.py b/wqflask/wqflask/oauth2/client.py index 999bbfc8..0cecd965 100644 --- a/wqflask/wqflask/oauth2/client.py +++ b/wqflask/wqflask/oauth2/client.py @@ -1,4 +1,5 @@ """Common oauth2 client utilities.""" +from typing import Optional from urllib.parse import urljoin from flask import session, current_app as app @@ -29,13 +30,17 @@ def oauth2_get(uri_path: str, data: dict = {}) -> Either: return Left(resp) -def oauth2_post(uri_path: str, data: dict) -> Either: +def oauth2_post( + uri_path: str, data: Optional[dict] = None, json: Optional[dict] = None, + **kwargs) -> Either: token = session.get("oauth2_token") config = app.config client = OAuth2Session( config["OAUTH2_CLIENT_ID"], config["OAUTH2_CLIENT_SECRET"], token=token, scope=SCOPE) - resp = client.post(urljoin(config["GN_SERVER_URL"], uri_path), data=data) + resp = client.post( + urljoin(config["GN_SERVER_URL"], uri_path), data=data, json=json, + **kwargs) if resp.status_code == 200: return Right(resp.json()) diff --git a/wqflask/wqflask/oauth2/data.py b/wqflask/wqflask/oauth2/data.py index 1dcacb7e..e1b33b56 100644 --- a/wqflask/wqflask/oauth2/data.py +++ b/wqflask/wqflask/oauth2/data.py @@ -1,4 +1,5 @@ """Handle linking data to groups.""" +import json from urllib.parse import urljoin from flask import ( @@ -154,3 +155,28 @@ def link_data(): except AssertionError as aserr: flash("You must provide all the expected data.", "alert-danger") return redirect(url_for("oauth2.data.list_data")) + +@data.route("/link/genotype", methods=["POST"]) +def link_genotype_data(): + """Link genotype data to a group.""" + form = request.form + link_source_url = redirect(url_for("oauth2.data.list_data")) + if bool(form.get("species_name")): + link_source_url = redirect(url_for( + "oauth2.data.list_data_by_species_and_dataset", + species_name=form["species_name"], dataset_type="genotype")) + + def __link_error__(err): + flash(f"{err['error']}: {err['error_description']}", "alert-danger") + return link_source_url + + def __link_success__(success): + flash(success["description"], "alert-success") + return link_source_url + + return oauth2_post("oauth2/data/link/genotype", json={ + "species_name": form.get("species_name"), + "group_id": form.get("group_id"), + "selected_datasets": tuple(json.loads(dataset) for dataset + in form.getlist("selected_datasets")) + }).either(lambda err: __link_error__(process_error(err)), __link_success__) diff --git a/wqflask/wqflask/templates/oauth2/data-list-genotype.html b/wqflask/wqflask/templates/oauth2/data-list-genotype.html index 3b7cfc1f..54479251 100644 --- a/wqflask/wqflask/templates/oauth2/data-list-genotype.html +++ b/wqflask/wqflask/templates/oauth2/data-list-genotype.html @@ -23,9 +23,12 @@
- +{%endblock%} + +{%block js%} + +{%endblock%} -- cgit v1.2.3 From dd94871c61ebb8a57c93f2dd923161e60fbe1ab4 Mon Sep 17 00:00:00 2001 From: zsloan Date: Tue, 11 Apr 2023 18:05:16 +0000 Subject: Fix indentation for conn.commit() --- wqflask/wqflask/metadata_edits.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wqflask/wqflask/metadata_edits.py b/wqflask/wqflask/metadata_edits.py index 28606c8b..64fc3f19 100644 --- a/wqflask/wqflask/metadata_edits.py +++ b/wqflask/wqflask/metadata_edits.py @@ -360,7 +360,7 @@ View the diffs here", "success") json_data=json.dumps(diff_data), ), ) - conn.commit() + conn.commit() flash(f"Diff-data: \n{diff_data}\nhas been uploaded", "success") return redirect( f"/datasets/{dataset_id}/traits/{name}" -- cgit v1.2.3 From b8b62aea1c7924e2ee7455c6fe8b34eee7cb8e74 Mon Sep 17 00:00:00 2001 From: zsloan Date: Tue, 11 Apr 2023 18:06:49 +0000 Subject: Fix the way database_connection() is called I think it now gets the SQL_URI from settings --- wqflask/wqflask/correlation/rust_correlation.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/wqflask/wqflask/correlation/rust_correlation.py b/wqflask/wqflask/correlation/rust_correlation.py index 67bd5ff5..097cc45b 100644 --- a/wqflask/wqflask/correlation/rust_correlation.py +++ b/wqflask/wqflask/correlation/rust_correlation.py @@ -28,7 +28,7 @@ def query_probes_metadata(dataset, trait_list): if not bool(trait_list) or dataset.type!="ProbeSet": return [] - with database_connection(SQL_URI) as conn: + with database_connection() as conn: with conn.cursor() as cursor: query = """ @@ -98,7 +98,7 @@ def chunk_dataset(dataset, steps, name): ProbeSetXRef.ProbeSetId = ProbeSet.Id """.format(name) - with database_connection(SQL_URI) as conn: + with database_connection() as conn: with conn.cursor() as curr: curr.execute(query) traits_name_dict = dict(curr.fetchall()) @@ -122,7 +122,7 @@ def compute_top_n_sample(start_vars, dataset, trait_list): sample_data=json.loads(samples_vals), dataset_samples=dataset.group.all_samples_ordered()) - with database_connection(SQL_URI) as conn: + with database_connection() as conn: with conn.cursor() as curr: curr.execute( """ @@ -140,7 +140,7 @@ def compute_top_n_sample(start_vars, dataset, trait_list): if len(trait_list) == 0: return {} - with database_connection(SQL_URI) as conn: + with database_connection() as conn: with conn.cursor() as curr: # fetching strain data in bulk query = ( @@ -176,7 +176,7 @@ def compute_top_n_lit(corr_results, target_dataset, this_trait) -> dict: geneid_dict = {trait_name: geneid for (trait_name, geneid) in geneid_dict.items() if corr_results.get(trait_name)} - with database_connection(SQL_URI) as conn: + with database_connection() as conn: return reduce( lambda acc, corr: {**acc, **corr}, compute_all_lit_correlation( @@ -251,7 +251,7 @@ def __compute_sample_corr__( if target_dataset.type == "ProbeSet" and start_vars.get("use_cache") == "true": - with database_connection(SQL_URI) as conn: + with database_connection() as conn: file_path = fetch_text_file(target_dataset.name, conn) if file_path: (sample_vals, target_data) = read_text_file( @@ -338,7 +338,7 @@ def __compute_lit_corr__( (this_trait_geneid, geneid_dict, species) = do_lit_correlation( this_trait, target_dataset) - with database_connection(SQL_URI) as conn: + with database_connection() as conn: return reduce( lambda acc, lit: {**acc, **lit}, compute_all_lit_correlation( -- cgit v1.2.3 From e2e78c906ffebc84b59f1f8b3aa921c3b6f3d872 Mon Sep 17 00:00:00 2001 From: zsloan Date: Tue, 11 Apr 2023 18:15:14 +0000 Subject: Fix bug where publication years had links even when there's no PMID This was just caused by the correlation code not directly being passed the PMID (and instead only being passed the link and the text), so the logic in the table code needed to be changed to check for the link instead of the PMID --- wqflask/wqflask/templates/correlation_page.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wqflask/wqflask/templates/correlation_page.html b/wqflask/wqflask/templates/correlation_page.html index 29fdc5f1..a85ac19b 100644 --- a/wqflask/wqflask/templates/correlation_page.html +++ b/wqflask/wqflask/templates/correlation_page.html @@ -411,7 +411,7 @@ 'data': null, 'width': "80px", 'render': function(data) { - if (data.pubmed_id != "N/A"){ + if (data.pubmed_link != "N/A"){ return '' + data.pubmed_text + '' } else { return data.pubmed_text -- cgit v1.2.3 From 78ada84420a4e7820de8b1ebcd4c2f6d078c9757 Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Wed, 12 Apr 2023 10:44:53 +0300 Subject: oauth2: Generalise JS code to work for both mRNA and Genotype datasets. --- wqflask/wqflask/oauth2/data.py | 18 +- .../wqflask/static/new/javascript/auth/search.js | 167 +++++++++++++++++++ .../static/new/javascript/auth/search_genotypes.js | 183 ++------------------- .../static/new/javascript/auth/search_mrna.js | 96 +++++++++++ .../templates/oauth2/data-list-genotype.html | 2 + .../wqflask/templates/oauth2/data-list-mrna.html | 8 +- 6 files changed, 299 insertions(+), 175 deletions(-) create mode 100644 wqflask/wqflask/static/new/javascript/auth/search.js create mode 100644 wqflask/wqflask/static/new/javascript/auth/search_mrna.js diff --git a/wqflask/wqflask/oauth2/data.py b/wqflask/wqflask/oauth2/data.py index 6804dafa..2b4dfc75 100644 --- a/wqflask/wqflask/oauth2/data.py +++ b/wqflask/wqflask/oauth2/data.py @@ -22,7 +22,6 @@ def __render_template__(templatepath, **kwargs): def __search_mrna__(query, template, **kwargs): species_name = kwargs["species_name"] search_uri = urljoin(app.config["GN_SERVER_URL"], "oauth2/data/search") - print(f"SEARCHING FOR mrna") datasets = oauth2_get( "oauth2/data/search", json = { @@ -96,6 +95,23 @@ def json_search_genotypes() -> Response: __handle_error__, lambda datasets: jsonify(datasets)) +@data.route("/mrna/search", methods=["POST"]) +def json_search_mrna() -> Response: + def __handle_error__(err): + error = process_error(err) + return jsonify(error), error["status_code"] + + return oauth2_get( + "oauth2/data/search", + json = { + "query": request.json["query"], + "dataset_type": "mrna", + "species_name": request.json["species_name"], + "selected": __selected_datasets__() + }).either( + __handle_error__, + lambda datasets: jsonify(datasets)) + @data.route("///list", methods=["GET", "POST"]) def list_data_by_species_and_dataset( diff --git a/wqflask/wqflask/static/new/javascript/auth/search.js b/wqflask/wqflask/static/new/javascript/auth/search.js new file mode 100644 index 00000000..5226000d --- /dev/null +++ b/wqflask/wqflask/static/new/javascript/auth/search.js @@ -0,0 +1,167 @@ +class InvalidCSSIDSelector extends Error { + constructor(message) { + super(message); + this.name = "InvalidCSSIDSelector"; + } +} + +class InvalidDataAttributeName extends Error { + constructor(message) { + super(message); + this.name = "InvalidDataAttributeName"; + } +} + +/** + * CSSIDSelector: A CSS ID Selector + * @param {String} A CSS selector of the form '#...' + */ +class CSSIDSelector { + constructor(selector) { + if(!selector.startsWith("#")) { + throw new InvalidCSSIDSelector( + "Expected the CSS selector to begin with a `#` character."); + } + let id_str = selector.slice(1, selector.length); + if(document.getElementById(id_str) == null) { + throw new InvalidCSSIDSelector( + "Element with ID '" + id_str + "' does not exist."); + } + this.selector = selector; + } +} + +/** + * TableDataSource: A type to represent a table's data source + * @param {String} A CSS selector for an ID + * @param {String} A `data-*` attribute name + */ +class TableDataSource { + constructor(table_id, data_attribute_name, checkbox_creation_function) { + this.table_id = new CSSIDSelector(table_id); + let data = document.querySelector( + table_id).getAttribute(data_attribute_name); + if(data == null) { + throw new InvalidDataAttributeName( + "data-* attribute '" + data_attribute_name + "' does not exist " + + "for table with ID '" + table_id.slice(1, table_id.length) + + "'."); + } else { + this.data_attribute_name = data_attribute_name; + } + this.checkbox_creation_function = checkbox_creation_function; + } +} + +/** + * Render the table + * @param {String} The selector for the table's ID + * @param {String} The name of the data-* attribute holding the table's data + * @param {Function} The function to call to generate the appropriate checkbox + */ +function render_table(table_data_source) { + table_id = table_data_source.table_id.selector; + data_attr_name = table_data_source.data_attribute_name; + $(table_id + " tbody tr").remove(); + table_data = JSON.parse($(table_id).attr(data_attr_name)); + if(table_data.length < 1) { + row = $("") + cell = $(''); + cell.append( + $('')); + cell.append(" "); + cell.append("No genotype datasets remaining."); + row.append(cell); + $(table_id + " tbody").append(row); + } + table_data.forEach(function(dataset) { + row = $("") + row.append(table_data_source.checkbox_creation_function(dataset)); + row.append(table_cell(dataset.InbredSetName)); + row.append(table_cell(dataset.dataset_name)); + row.append(table_cell(dataset.dataset_fullname)); + row.append(table_cell(dataset.dataset_shortname)); + $(table_id + " tbody").append(row); + }); +} + +function remove_from_table_data(dataset, table_data_source) { + let table_id = table_data_source.table_id.selector; + let data_attr_name = table_data_source.data_attribute_name; + without_dataset = JSON.parse($(table_id).attr(data_attr_name)).filter( + function(dst) { + return !(dst.SpeciesId == dataset.SpeciesId && + dst.InbredSetId == dataset.InbredSetId && + dst.GenoFreezeId == dataset.GenoFreezeId); + }); + $(table_id).attr(data_attr_name, JSON.stringify(without_dataset)); +} + +function add_to_table_data(dataset, table_data_source) { + let table_id = table_data_source.table_id.selector; + let data_attr_name = table_data_source.data_attribute_name; + table_data = JSON.parse($(table_id).attr(data_attr_name)); + if(!in_array(dataset, table_data)) { + table_data.push(dataset); + } + $(table_id).attr(data_attr_name, JSON.stringify(Array.from(table_data))); +} + +/** + * Switch the dataset from search table to selection table and vice versa + * @param {Object} A genotype dataset + * @param {TableDataSource} The table to switch the dataset from + * @param {TableDataSource} The table to switch the dataset to + */ +function select_deselect_dataset(dataset, source, destination) { + dest_selector = destination.table_id.selector + dest_data = JSON.parse( + $(dest_selector).attr(destination.data_attribute_name)); + add_to_table_data(dataset, destination); // Add to destination table + remove_from_table_data(dataset, source); // Remove from source table + /***** BEGIN: Re-render tables *****/ + render_table(destination); + render_table(source); + /***** END: Re-render tables *****/ +} + +function debounce(func, delay=500) { + var timeout; + return function search(event) { + clearTimeout(timeout); + timeout = setTimeout(func, delay); + }; +} + +/** + * Build a checkbox: For internal use only + * @param {Genotype Dataset object} A genotype dataset object + * @param {String} A string to initialise the checkbox + */ +function __build_checkbox__(dataset, checkbox_str) { + cell = $(""); + check = $(checkbox_str); + check.val(JSON.stringify(dataset)); + cell.append(check); + return cell; +} + +function link_checkbox(dataset) { + return __build_checkbox__( + dataset, + ''); +} + +function search_checkbox(dataset) { + return __build_checkbox__( + dataset, + ''); +} + +function table_cell(value) { + cell = $(""); + cell.html(value); + return cell; +} diff --git a/wqflask/wqflask/static/new/javascript/auth/search_genotypes.js b/wqflask/wqflask/static/new/javascript/auth/search_genotypes.js index 8d821f44..40f88121 100644 --- a/wqflask/wqflask/static/new/javascript/auth/search_genotypes.js +++ b/wqflask/wqflask/static/new/javascript/auth/search_genotypes.js @@ -1,66 +1,8 @@ /** - * Build a checkbox: For internal use only - * @param {Genotype Dataset object} A genotype dataset object - * @param {String} A string to initialise the checkbox + * Check whether `dataset` is in array of `datasets`. + * @param {GenotypeDataset} A genotype dataset. + * @param {Array} An array of genotype datasets. */ -function __build_checkbox__(dataset, checkbox_str) { - cell = $(""); - check = $(checkbox_str); - check.val(JSON.stringify(dataset)); - cell.append(check); - return cell; -} - -function link_checkbox(dataset) { - return __build_checkbox__( - dataset, - ''); -} - -function search_checkbox(dataset) { - return __build_checkbox__( - dataset, - ''); -} - -function table_cell(value) { - cell = $(""); - cell.html(value); - return cell; -} - -/** - * Render the table - * @param {String} The selector for the table's ID - * @param {String} The name of the data-* attribute holding the table's data - * @param {Function} The function to call to generate the appropriate checkbox - */ -function render_table(table_id, data_attr_name, checkbox_function) { - $(table_id + " tbody tr").remove(); - table_data = JSON.parse($(table_id).attr(data_attr_name)); - if(table_data.length < 1) { - row = $("") - cell = $(''); - cell.append( - $('')); - cell.append(" "); - cell.append("No genotype datasets remaining."); - row.append(cell); - $(table_id + " tbody").append(row); - } - table_data.forEach(function(dataset) { - row = $("") - row.append(checkbox_function(dataset)); - row.append(table_cell(dataset.InbredSetName)); - row.append(table_cell(dataset.dataset_name)); - row.append(table_cell(dataset.dataset_fullname)); - row.append(table_cell(dataset.dataset_shortname)); - $(table_id + " tbody").append(row); - }); -} - function in_array(dataset, datasets) { found = datasets.filter(function(dst) { return (dst.SpeciesId == dataset.SpeciesId && @@ -70,28 +12,6 @@ function in_array(dataset, datasets) { return found.length > 0; } -function remove_from_table_data(dataset, table_data_source) { - let table_id = table_data_source.table_id.selector; - let data_attr_name = table_data_source.data_attribute_name; - without_dataset = JSON.parse($(table_id).attr(data_attr_name)).filter( - function(dst) { - return !(dst.SpeciesId == dataset.SpeciesId && - dst.InbredSetId == dataset.InbredSetId && - dst.GenoFreezeId == dataset.GenoFreezeId); - }); - $(table_id).attr(data_attr_name, JSON.stringify(without_dataset)); -} - -function add_to_table_data(dataset, table_data_source) { - let table_id = table_data_source.table_id.selector; - let data_attr_name = table_data_source.data_attribute_name; - table_data = JSON.parse($(table_id).attr(data_attr_name)); - if(!in_array(dataset, table_data)) { - table_data.push(dataset); - } - $(table_id).attr(data_attr_name, JSON.stringify(Array.from(table_data))); -} - function toggle_link_button() { num_groups = $("#frm-link-genotypes select option").length - 1; num_selected = JSON.parse( @@ -103,96 +23,14 @@ function toggle_link_button() { } } -class InvalidCSSIDSelector extends Error { - constructor(message) { - super(message); - this.name = "InvalidCSSIDSelector"; - } -} - -class InvalidDataAttributeName extends Error { - constructor(message) { - super(message); - this.name = "InvalidDataAttributeName"; - } -} - -/** - * CSSIDSelector: A CSS ID Selector - * @param {String} A CSS selector of the form '#...' - */ -class CSSIDSelector { - constructor(selector) { - if(!selector.startsWith("#")) { - throw new InvalidCSSIDSelector( - "Expected the CSS selector to begin with a `#` character."); - } - let id_str = selector.slice(1, selector.length); - if(document.getElementById(id_str) == null) { - throw new InvalidCSSIDSelector( - "Element with ID '" + id_str + "' does not exist."); - } - this.selector = selector; - } -} - -/** - * TableDataSource: A type to represent a table's data source - * @param {String} A CSS selector for an ID - * @param {String} A `data-*` attribute name - */ -class TableDataSource { - constructor(table_id, data_attribute_name) { - this.table_id = new CSSIDSelector(table_id); - let data = document.querySelector( - table_id).getAttribute(data_attribute_name); - if(data == null) { - throw new InvalidDataAttributeName( - "data-* attribute '" + data_attribute_name + "' does not exist " + - "for table with ID '" + table_id.slice(1, table_id.length) + - "'."); - } else { - this.data_attribute_name = data_attribute_name; - } - } -} - -/** - * Switch the dataset from search table to selection table and vice versa - * @param {Object} A genotype dataset - * @param {TableDataSource} The table to switch the dataset from - * @param {TableDataSource} The table to switch the dataset to - */ -function select_deselect_dataset(dataset, source, destination) { - dest_selector = destination.table_id.selector - dest_data = JSON.parse( - $(dest_selector).attr(destination.data_attribute_name)); - add_to_table_data(dataset, destination); // Add to destination table - remove_from_table_data(dataset, source); // Remove from source table - /***** BEGIN: Re-render tables *****/ - // The `render_table` could be modified to use the checkbox creator function - // from the `TableDataSource` object, once that is modified to have that. - render_table( - "#tbl-link-genotypes", "data-selected-datasets", link_checkbox); - render_table("#tbl-genotypes", "data-datasets", search_checkbox); - toggle_link_button(); - /***** END: Re-render tables *****/ -} - -function debounce(func, delay=500) { - var timeout; - return function search(event) { - clearTimeout(timeout); - timeout = setTimeout(func, delay); - }; -} - function search_genotypes() { query = document.getElementById("txt-query").value; selected = JSON.parse(document.getElementById( "tbl-link-genotypes").getAttribute("data-selected-datasets")); species_name = document.getElementById("txt-species-name").value search_endpoint = "/oauth2/data/genotype/search" + search_table = new TableDataSource( + "#tbl-genotypes", "data-datasets", search_checkbox); $.ajax( search_endpoint, { @@ -213,22 +51,23 @@ function search_genotypes() { data.error_description); document.getElementById("tbl-genotypes").setAttribute( "data-datasets", JSON.stringify([])); - render_table("#tbl-genotypes", "data-datasets", search_checkbox); + render_table(search_table); }, "success": function(data, textStatus, jqXHR) { document.getElementById("search-error").setAttribute( "style", "display: none;"); document.getElementById("tbl-genotypes").setAttribute( "data-datasets", JSON.stringify(data)); - render_table("#tbl-genotypes", "data-datasets", search_checkbox); + render_table(search_table); } }); } $(document).ready(function() { - let search_table = new TableDataSource("#tbl-genotypes", "data-datasets"); + let search_table = new TableDataSource( + "#tbl-genotypes", "data-datasets", search_checkbox); let link_table = new TableDataSource( - "#tbl-link-genotypes", "data-selected-datasets"); + "#tbl-link-genotypes", "data-selected-datasets", link_checkbox); $("#frm-search-traits").submit(function(event) { event.preventDefault(); @@ -241,6 +80,7 @@ $(document).ready(function() { if(this.checked) { select_deselect_dataset( JSON.parse(this.value), search_table, link_table); + toggle_link_button(); } }); @@ -248,6 +88,7 @@ $(document).ready(function() { if(!this.checked) { select_deselect_dataset( JSON.parse(this.value), link_table, search_table); + toggle_link_button(); } }); }); diff --git a/wqflask/wqflask/static/new/javascript/auth/search_mrna.js b/wqflask/wqflask/static/new/javascript/auth/search_mrna.js new file mode 100644 index 00000000..e754ae76 --- /dev/null +++ b/wqflask/wqflask/static/new/javascript/auth/search_mrna.js @@ -0,0 +1,96 @@ +/** + * Check whether `dataset` is in array of `datasets`. + * @param {mRNADataset} A mrna dataset. + * @param {Array} An array of mrna datasets. + */ +function in_array(dataset, datasets) { + found = datasets.filter(function(dst) { + return (dst.SpeciesId == dataset.SpeciesId && + dst.InbredSetId == dataset.InbredSetId && + dst.ProbeFreezeId == dataset.ProbeFreezeId && + dst.ProbeSetFreezeId == dataset.ProbeSetFreezeId); + }); + return found.length > 0; +} + +function toggle_link_button() { + num_groups = $("#frm-link select option").length - 1; + num_selected = JSON.parse( + $("#tbl-link").attr("data-datasets")).length; + if(num_groups > 0 && num_selected > 0) { + $("#frm-link input[type='submit']").prop("disabled", false); + } else { + $("#frm-link input[type='submit']").prop("disabled", true); + } +} + +function search_mrna() { + query = document.getElementById("txt-query").value; + selected = JSON.parse(document.getElementById( + "tbl-link").getAttribute("data-datasets")); + species_name = document.getElementById("txt-species-name").value + search_endpoint = "/oauth2/data/mrna/search" + search_table = new TableDataSource( + "#tbl-search", "data-datasets", search_checkbox); + $.ajax( + search_endpoint, + { + "method": "POST", + "contentType": "application/json; charset=utf-8", + "dataType": "json", + "data": JSON.stringify({ + "query": query, + "selected": selected, + "dataset_type": "mrna", + "species_name": species_name}), + "error": function(jqXHR, textStatus, errorThrown) { + error_data = jqXHR.responseJSON + console.debug("ERROR_DATA:", error_data); + elt = document.getElementById("search-error").setAttribute( + "style", "display: block;"); + document.getElementById("search-error-text").innerHTML = ( + error_data.error + " (" + error_data.status_code + "): " + + error_data.error_description); + document.getElementById("tbl-search").setAttribute( + "data-datasets", JSON.stringify([])); + render_table(search_table); + }, + "success": function(data, textStatus, jqXHR) { + document.getElementById("search-error").setAttribute( + "style", "display: none;"); + document.getElementById("tbl-search").setAttribute( + "data-datasets", JSON.stringify(data)); + render_table(search_table); + } + }); +} + +$(document).ready(function() { + let search_table = new TableDataSource( + "#tbl-search", "data-datasets", search_checkbox); + let link_table = new TableDataSource( + "#tbl-link", "data-datasets", link_checkbox); + + $("#frm-search").submit(function(event) { + event.preventDefault(); + return false; + }); + + $("#txt-query").keyup(debounce(search_mrna)); + + $("#tbl-search").on("change", ".checkbox-search", function(event) { + if(this.checked) { + select_deselect_dataset( + JSON.parse(this.value), search_table, link_table); + toggle_link_button(); + } + }); + + $("#tbl-link").on("change", ".checkbox-selected", function(event) { + if(!this.checked) { + select_deselect_dataset( + JSON.parse(this.value), link_table, search_table); + toggle_link_button(); + } + }); +}); diff --git a/wqflask/wqflask/templates/oauth2/data-list-genotype.html b/wqflask/wqflask/templates/oauth2/data-list-genotype.html index 30ee0d27..c780a583 100644 --- a/wqflask/wqflask/templates/oauth2/data-list-genotype.html +++ b/wqflask/wqflask/templates/oauth2/data-list-genotype.html @@ -159,6 +159,8 @@ {%endblock%} {%block js%} + {%endblock%} diff --git a/wqflask/wqflask/templates/oauth2/data-list-mrna.html b/wqflask/wqflask/templates/oauth2/data-list-mrna.html index 1c22f65c..0e163235 100644 --- a/wqflask/wqflask/templates/oauth2/data-list-mrna.html +++ b/wqflask/wqflask/templates/oauth2/data-list-mrna.html @@ -23,7 +23,7 @@
-
- + data-datasets='{{selected_datasets | list | tojson}}'> @@ -161,6 +161,8 @@ {%endblock%} {%block js%} + {%endblock%} -- cgit v1.2.3 From a46ad1d468fe5ace7a4503693e2e4d404f87ebf3 Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Fri, 14 Apr 2023 08:46:26 +0300 Subject: Handle errors for group consistent with the UI. --- wqflask/wqflask/oauth2/groups.py | 8 ++++++-- wqflask/wqflask/oauth2/request_utils.py | 5 +++-- wqflask/wqflask/templates/oauth2/group.html | 6 ++++++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/wqflask/wqflask/oauth2/groups.py b/wqflask/wqflask/oauth2/groups.py index 551c0640..3bc5fb6d 100644 --- a/wqflask/wqflask/oauth2/groups.py +++ b/wqflask/wqflask/oauth2/groups.py @@ -9,7 +9,7 @@ from flask import ( from .checks import require_oauth2 from .client import oauth2_get, oauth2_post from .request_utils import ( - user_details, handle_error, request_error, process_error, handle_success, + user_details, handle_error, process_error, handle_success, raise_unimplemented) groups = Blueprint("group", __name__) @@ -32,8 +32,12 @@ def user_group(): user_error=process_error(error)), partial(__get_join_requests__, group)) + def __group_error__(err): + return render_template( + "oauth2/group.html", group_error=process_error(err)) + return oauth2_get("oauth2/user/group").either( - request_error, __success__) + __group_error__, __success__) @groups.route("/create", methods=["POST"]) @require_oauth2 diff --git a/wqflask/wqflask/oauth2/request_utils.py b/wqflask/wqflask/oauth2/request_utils.py index eefae900..fae3a347 100644 --- a/wqflask/wqflask/oauth2/request_utils.py +++ b/wqflask/wqflask/oauth2/request_utils.py @@ -20,10 +20,11 @@ def process_error(error: Response, "server.") ) -> dict: if error.status_code == 404: + msg = error.json()["error_description"] if hasattr(error, "json") else message return { "error": "NotFoundError", - "error_message": message, - "error_description": message, + "error_message": msg, + "error_description": msg, "status_code": error.status_code } return {**error.json(), "status_code": error.status_code} diff --git a/wqflask/wqflask/templates/oauth2/group.html b/wqflask/wqflask/templates/oauth2/group.html index a6a3e6c3..a2380df7 100644 --- a/wqflask/wqflask/templates/oauth2/group.html +++ b/wqflask/wqflask/templates/oauth2/group.html @@ -8,6 +8,11 @@ {{flash_me()}} + {%if group_error is defined%} +
+ {{display_error("Group", group_error)}} +
+ {%else%}
{%if group_join_requests_error is defined %} @@ -103,6 +108,7 @@
+ {%endif%} {%endblock%} -- cgit v1.2.3 From 2c517dc9b463f63ffef3c17bd56265b168755c6b Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Sat, 15 Apr 2023 19:04:55 +0300 Subject: Revert "Fix the way database_connection() is called" This reverts commit b8b62aea1c7924e2ee7455c6fe8b34eee7cb8e74. We do need to decouple the entire `gn3.db_utils` from the `flask.current_app` object, and as such, the use of `SQL_URI` from the `utility.tools` module in GN2 was on purpose. This is also to help towards fixing issue https://issues.genenetwork.org/issues/bugfix_coupling_current_app_and_db_utils --- wqflask/wqflask/correlation/rust_correlation.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/wqflask/wqflask/correlation/rust_correlation.py b/wqflask/wqflask/correlation/rust_correlation.py index 097cc45b..67bd5ff5 100644 --- a/wqflask/wqflask/correlation/rust_correlation.py +++ b/wqflask/wqflask/correlation/rust_correlation.py @@ -28,7 +28,7 @@ def query_probes_metadata(dataset, trait_list): if not bool(trait_list) or dataset.type!="ProbeSet": return [] - with database_connection() as conn: + with database_connection(SQL_URI) as conn: with conn.cursor() as cursor: query = """ @@ -98,7 +98,7 @@ def chunk_dataset(dataset, steps, name): ProbeSetXRef.ProbeSetId = ProbeSet.Id """.format(name) - with database_connection() as conn: + with database_connection(SQL_URI) as conn: with conn.cursor() as curr: curr.execute(query) traits_name_dict = dict(curr.fetchall()) @@ -122,7 +122,7 @@ def compute_top_n_sample(start_vars, dataset, trait_list): sample_data=json.loads(samples_vals), dataset_samples=dataset.group.all_samples_ordered()) - with database_connection() as conn: + with database_connection(SQL_URI) as conn: with conn.cursor() as curr: curr.execute( """ @@ -140,7 +140,7 @@ def compute_top_n_sample(start_vars, dataset, trait_list): if len(trait_list) == 0: return {} - with database_connection() as conn: + with database_connection(SQL_URI) as conn: with conn.cursor() as curr: # fetching strain data in bulk query = ( @@ -176,7 +176,7 @@ def compute_top_n_lit(corr_results, target_dataset, this_trait) -> dict: geneid_dict = {trait_name: geneid for (trait_name, geneid) in geneid_dict.items() if corr_results.get(trait_name)} - with database_connection() as conn: + with database_connection(SQL_URI) as conn: return reduce( lambda acc, corr: {**acc, **corr}, compute_all_lit_correlation( @@ -251,7 +251,7 @@ def __compute_sample_corr__( if target_dataset.type == "ProbeSet" and start_vars.get("use_cache") == "true": - with database_connection() as conn: + with database_connection(SQL_URI) as conn: file_path = fetch_text_file(target_dataset.name, conn) if file_path: (sample_vals, target_data) = read_text_file( @@ -338,7 +338,7 @@ def __compute_lit_corr__( (this_trait_geneid, geneid_dict, species) = do_lit_correlation( this_trait, target_dataset) - with database_connection() as conn: + with database_connection(SQL_URI) as conn: return reduce( lambda acc, lit: {**acc, **lit}, compute_all_lit_correlation( -- cgit v1.2.3 From c21016add8fb6b66e4413f8cacba256d4dbf2404 Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Mon, 17 Apr 2023 14:18:56 +0300 Subject: oauth2: Build code to do search At this point, the code fails with a 404, despite the appropriate endpoint existing. Will need further debugging. --- wqflask/wqflask/oauth2/data.py | 44 +++--- .../wqflask/static/new/javascript/auth/search.js | 29 ++-- .../new/javascript/auth/search_phenotypes.js | 114 +++++++++++++++ .../templates/oauth2/data-list-phenotype.html | 160 +++------------------ 4 files changed, 177 insertions(+), 170 deletions(-) create mode 100644 wqflask/wqflask/static/new/javascript/auth/search_phenotypes.js diff --git a/wqflask/wqflask/oauth2/data.py b/wqflask/wqflask/oauth2/data.py index 2b4dfc75..c3d426cf 100644 --- a/wqflask/wqflask/oauth2/data.py +++ b/wqflask/wqflask/oauth2/data.py @@ -1,11 +1,16 @@ """Handle linking data to groups.""" +import sys import json +import uuid +from datetime import datetime from urllib.parse import urljoin +from redis import Redis from flask import ( flash, request, jsonify, url_for, redirect, Response, Blueprint, render_template, current_app as app) +from jobs import jobs from .request_utils import process_error from .client import oauth2_get, oauth2_post @@ -59,24 +64,31 @@ def __search_genotypes__(query, template, **kwargs): return __render_template__(template, search_uri=search_uri, **datasets, **kwargs) def __search_phenotypes__(query, template, **kwargs): + page = int(request.args.get("page", 1)) per_page = int(request.args.get("per_page", 500)) - species_name = kwargs["species_name"] - search_uri = (f"search/?type=phenotype&per_page={per_page}&query=" - f"species:{species_name}") + ( - f" AND ({query})" if bool(query) else "") - traits = oauth2_get(search_uri).either( - lambda err: {"traits_error": process_error(err)}, - lambda trts: {"traits": tuple({ - "index": idx, **trait - } for idx, trait in enumerate(trts, start=1))}) - selected_traits = request.form.getlist("selected_traits") - - return __render_template__( - template, **traits, per_page=per_page, query=query, - selected_traits=selected_traits, - search_endpoint=urljoin(app.config["GN_SERVER_URL"], "search/"), - **kwargs) + def __search_error__(error): + raise Exception(error) + def __search_success__(search_results): + job_id = uuid.UUID(search_results["job_id"]) + return __render_template__( + template, traits=[], per_page=per_page, query=query, + selected_traits=selected_traits, search_results=search_results, + search_endpoint=urljoin( + app.config["GN_SERVER_URL"], "oauth2/data/search"), + results_endpoint=urljoin( + app.config["GN_SERVER_URL"], + f"oauth2/data/search/phenotype/{job_id}"), + **kwargs) + return oauth2_get("oauth2/data/search", json={ + "dataset_type": "phenotype", + "species_name": kwargs["species_name"], + "per_page": per_page, + "page": page, + "gn3_server_uri": app.config["GN_SERVER_URL"] + }).either( + lambda err: __search_error__(process_error(err)), + __search_success__) @data.route("/genotype/search", methods=["POST"]) def json_search_genotypes() -> Response: diff --git a/wqflask/wqflask/static/new/javascript/auth/search.js b/wqflask/wqflask/static/new/javascript/auth/search.js index 5226000d..cd86a9df 100644 --- a/wqflask/wqflask/static/new/javascript/auth/search.js +++ b/wqflask/wqflask/static/new/javascript/auth/search.js @@ -134,30 +134,31 @@ function debounce(func, delay=500) { } /** - * Build a checkbox: For internal use only - * @param {Genotype Dataset object} A genotype dataset object - * @param {String} A string to initialise the checkbox + * Build a checkbox + * @param {Dataset Object} A JSON.stringify-able object + * @param {String} The name to assign the checkbox */ -function __build_checkbox__(dataset, checkbox_str) { +function build_checkbox(data_object, checkbox_name, checkbox_aux_classes="") { cell = $(""); - check = $(checkbox_str); - check.val(JSON.stringify(dataset)); + check = $( + ''); + check.val(JSON.stringify(data_object)); + auxilliary_classes = checkbox_aux_class.trim(); + if(Boolean(auxilliary_classes)) { + check.attr("class", + check.attr("class") + " " + auxilliary_classes.trim()); + } cell.append(check); return cell; } function link_checkbox(dataset) { - return __build_checkbox__( - dataset, - ''); + return build_checkbox(dataset, "selected", "checkbox-selected"); } function search_checkbox(dataset) { - return __build_checkbox__( - dataset, - ''); + return build_checkbox(dataset, "search_datasets", "checkbox-search"); } function table_cell(value) { diff --git a/wqflask/wqflask/static/new/javascript/auth/search_phenotypes.js b/wqflask/wqflask/static/new/javascript/auth/search_phenotypes.js new file mode 100644 index 00000000..b172ffd1 --- /dev/null +++ b/wqflask/wqflask/static/new/javascript/auth/search_phenotypes.js @@ -0,0 +1,114 @@ +/** + * Global variables: Bad idea - figure out how to pass them down a call stack. + */ +search_table = new TableDataSource( + "#tbl-phenotypes", "data-traits", + function(trait) {build_checkbox(trait, "search_traits")}); +link_table = new TableDataSource( + "#tbl-link-phenotypes", "data-traits", + function(trait) {build_checkbox(trait, "selected")}); + +/** + * Toggle the state for the "Link Traits" button + */ +function toggle_link_button() { + num_groups = $("#frm-link-phenotypes select option").length - 1; + num_selected = JSON.parse( + $("#tbl-link-phenotypes").attr("data-datasets")).length; + if(num_groups > 0 && num_selected > 0) { + $("#frm-link-phenotypes input[type='submit']").prop("disabled", false); + } else { + $("#frm-link-phenotypes input[type='submit']").prop("disabled", true); + } +} + +/** + * Default error function: print out debug messages + */ +function default_error_fn(jqXHR, textStatus, errorThrown) { + console.debug("XHR:", jqXHR); + console.debug("STATUS:", textStatus); + console.debug("ERROR:", errorThrown); +} + +function display_search_results(data, textStatus, jqXHR) { + $("#tbl-phenotypes").attr( + "data-traits", JSON.stringify(data.search_results)); + render_table(search_table); +} + +/** + * Fetch the search results + * @param {UUID}: The job id to fetch data for + */ +function fetch_search_results(job_id, success, error=default_error_fn) { + endpoint = $("#frm-search-traits").attr("data-search-results-endpoint"); + $.ajax( + endpoint, + { + "method": "GET", + "contentType": "application/json; charset=utf-8", + "dataType": "json", + "error": error, + "success": success + } + ); +} + +function search_phenotypes() { + query = document.getElementById("txt-query").value; + selected = JSON.parse(document.getElementById( + "tbl-link-phenotypes").getAttribute("data-traitss")); + species_name = document.getElementById("txt-species-name").value + per_page = document.getElementById("txt-per-page").value + search_table = new TableDataSource( + "#tbl-phenotypes", "data-traits", search_checkbox); + $.ajax( + "/oauth2/data/search", + { + "method": "GET", + "contentType": "application/json; charset=utf-8", + "dataType": "json", + "data": JSON.stringify({ + "query": query, + "species_name": species_name, + "dataset_type": "phenotype", + "per_page": per_page + }), + "error": default_error_fn, + "success": (data, textStatus, jqXHR) => { + fetch_search_results(data.job_id); + } + }); +} + +$(document).ready(function() { + $("#frm-search-traits").submit(event => { + event.preventDefault(); + return false; + }); + + $("#txt-query").keyup(debounce(search_phenotypes)); + + $("#tbl-phenotypes").on("change", ".checkbox-selected", function(event) { + if(this.checked) { + select_deselect(JSON.parse(this.value), search_table, link_table); + toggle_link_button(); + } + }); + + $("#tbl-link-phenotypes").on("change", ".checkbox-search", function(event) { + if(!this.checked) { + select_deselect(JSON.parse(this.value), search_table, link_table); + toggle_link_button(); + } + }); + + setTimeout( + function() { + fetch_search_results( + $("#tbl-phenotypes").attr("data-initial-job-id"), + display_search_results) + }, + 500); +}); diff --git a/wqflask/wqflask/templates/oauth2/data-list-phenotype.html b/wqflask/wqflask/templates/oauth2/data-list-phenotype.html index 53c6ce8c..e6998d28 100644 --- a/wqflask/wqflask/templates/oauth2/data-list-phenotype.html +++ b/wqflask/wqflask/templates/oauth2/data-list-phenotype.html @@ -40,7 +40,9 @@ - + - {%if groups | length > 0%} - - {%else%} - - {%endif%} +
+ +
@@ -66,7 +70,8 @@
+ method="POST" + data-search-results-endpoint="{{results_endpoint}}"> {%if dataset_type == "mrna"%} mRNA: Search {%else%} @@ -84,17 +89,19 @@
- species:{{species_name}} AND ( + species:{{species_name}} AND - )
- +
") + cell = $('") + row.append(table_data_source.checkbox_creation_function(trait)); + row.append(table_cell(trait.name)); + row.append(table_cell(trait.group)); + row.append(table_cell(trait.dataset)); + row.append(table_cell(trait.dataset_fullname)); + row.append(table_cell(trait.description)); + row.append(table_cell(trait.authors.join(", "))); + row.append(table_cell( + '' + + trait.year + "")); + row.append(table_cell("Chr:" + trait.geno_chr + "@" + trait.geno_mb)); + row.append(table_cell(trait.lrs)); + row.append(table_cell(trait.additive)); + $(table_id + " tbody").append(row); + }); +} + function display_search_results(data, textStatus, jqXHR) { - $("#tbl-phenotypes").attr( - "data-traits", JSON.stringify(data.search_results)); - render_table(search_table); + if(data.status == "queued" || data.status == "started") { + setTimeout(() => { + fetch_search_results(data.job_id, display_search_results); + }, 250); + return; + } + if(data.status == "completed") { + $("#tbl-phenotypes").attr( + "data-traits", JSON.stringify(data.search_results)); + render_pheno_table(search_table); + } + $("#txt-search").prop("disabled", false); } /** @@ -43,6 +90,7 @@ function display_search_results(data, textStatus, jqXHR) { */ function fetch_search_results(job_id, success, error=default_error_fn) { endpoint = $("#frm-search-traits").attr("data-search-results-endpoint"); + $("#txt-search").prop("disabled", true); $.ajax( endpoint, { @@ -104,11 +152,9 @@ $(document).ready(function() { } }); - setTimeout( - function() { - fetch_search_results( - $("#tbl-phenotypes").attr("data-initial-job-id"), - display_search_results) - }, - 500); + setTimeout(() => { + fetch_search_results( + $("#tbl-phenotypes").attr("data-initial-job-id"), + display_search_results); + }, 500); }); diff --git a/wqflask/wqflask/templates/oauth2/data-list-phenotype.html b/wqflask/wqflask/templates/oauth2/data-list-phenotype.html index e6998d28..6afabdf8 100644 --- a/wqflask/wqflask/templates/oauth2/data-list-phenotype.html +++ b/wqflask/wqflask/templates/oauth2/data-list-phenotype.html @@ -80,7 +80,7 @@ {%endif%} + id="txt-species-name" /> + data-traits="{{traits | tojson}}" + data-initial-job-id={{search_results.job_id}} + data-initial-search-res={{search_results | tojson}}> + + + + + + + + + + + + + + + + {%for trait in traits%} + + + + + + + + + + + + + + {%else%} - + {%endfor%}
-- cgit v1.2.3 From bbe74f74c57976c9a336f59e116a6456e7ac913d Mon Sep 17 00:00:00 2001 From: Munyoki Kilyungi Date: Tue, 18 Apr 2023 14:56:13 +0300 Subject: Add a web url to genewiki entries for GN Signed-off-by: Munyoki Kilyungi --- wqflask/wqflask/templates/generif.html | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/wqflask/wqflask/templates/generif.html b/wqflask/wqflask/templates/generif.html index affcbbed..d8229ac1 100644 --- a/wqflask/wqflask/templates/generif.html +++ b/wqflask/wqflask/templates/generif.html @@ -45,7 +45,13 @@ GeneWiki Entry for {{ symbol }} {% for entry in entries.gn_entries %}
  • - {{ entry["entry"]["value"] }} + + {{ entry["entry"]["value"] }} + {% if entry.get("weburl") %} + glyphicon glyphicon-open + web + {% endif %} +
    Author:
    {{ entry["author"]["value"] }}
    -- cgit v1.2.3