diff options
author | Frederick Muriuki Muriithi | 2023-04-12 10:44:53 +0300 |
---|---|---|
committer | Frederick Muriuki Muriithi | 2023-04-12 10:47:12 +0300 |
commit | 78ada84420a4e7820de8b1ebcd4c2f6d078c9757 (patch) | |
tree | 6a47d74dde2803bfc3b406b30ca674cbedabb101 | |
parent | e2e78c906ffebc84b59f1f8b3aa921c3b6f3d872 (diff) | |
download | genenetwork2-78ada84420a4e7820de8b1ebcd4c2f6d078c9757.tar.gz |
oauth2: Generalise JS code to work for both mRNA and Genotype datasets.
6 files changed, 299 insertions, 175 deletions
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("/<string:species_name>/<string:dataset_type>/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 = $("<tr>") + cell = $('<td colspan="100%" align="center">'); + cell.append( + $('<span class="glyphicon glyphicon-info-sign text-info">')); + cell.append(" "); + cell.append("No genotype datasets remaining."); + row.append(cell); + $(table_id + " tbody").append(row); + } + table_data.forEach(function(dataset) { + row = $("<tr>") + 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 = $("<td>"); + check = $(checkbox_str); + check.val(JSON.stringify(dataset)); + cell.append(check); + return cell; +} + +function link_checkbox(dataset) { + return __build_checkbox__( + dataset, + '<input type="checkbox" class="checkbox checkbox-selected" ' + + 'name="selected" checked="checked">'); +} + +function search_checkbox(dataset) { + return __build_checkbox__( + dataset, + '<input type="checkbox" class="checkbox checkbox-search" ' + + 'name="search_datasets">'); +} + +function table_cell(value) { + cell = $("<td>"); + 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 = $("<td>"); - check = $(checkbox_str); - check.val(JSON.stringify(dataset)); - cell.append(check); - return cell; -} - -function link_checkbox(dataset) { - return __build_checkbox__( - dataset, - '<input type="checkbox" class="checkbox checkbox-selected" ' + - 'name="selected" checked="checked">'); -} - -function search_checkbox(dataset) { - return __build_checkbox__( - dataset, - '<input type="checkbox" class="checkbox checkbox-search" ' + - 'name="search_datasets">'); -} - -function table_cell(value) { - cell = $("<td>"); - 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 = $("<tr>") - cell = $('<td colspan="100%" align="center">'); - cell.append( - $('<span class="glyphicon glyphicon-info-sign text-info">')); - cell.append(" "); - cell.append("No genotype datasets remaining."); - row.append(cell); - $(table_id + " tbody").append(row); - } - table_data.forEach(function(dataset) { - row = $("<tr>") - 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%} +<script src="/static/new/javascript/auth/search.js" + language="javascript" type="text/javascript"></script> <script src="/static/new/javascript/auth/search_genotypes.js" language="javascript" type="text/javascript"></script> {%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 @@ </div> <div class="row"> - <form id="frm-link-mrna" method="POST" + <form id="frm-link" method="POST" action="{{url_for('oauth2.data.link_mrna_data')}}"> <legend>Link mRNA Assay Datasets to Group</legend> @@ -41,9 +41,9 @@ </div> <div class="form-group"> - <table id="tbl-link-mrna" + <table id="tbl-link" class="table-hover table-striped cell-border dataTable no-footer" - data-selected-datasets='{{selected_datasets | list | tojson}}'> + data-datasets='{{selected_datasets | list | tojson}}'> <thead> <tr> <th>Deselect</th> @@ -161,6 +161,8 @@ {%endblock%} {%block js%} +<script src="/static/new/javascript/auth/search.js" + language="javascript" type="text/javascript"></script> <script src="/static/new/javascript/auth/search_mrna.js" language="javascript" type="text/javascript"></script> {%endblock%} |