diff options
-rw-r--r-- | wqflask/wqflask/metadata_edits.py | 2 | ||||
-rw-r--r-- | wqflask/wqflask/oauth2/client.py | 14 | ||||
-rw-r--r-- | wqflask/wqflask/oauth2/data.py | 165 | ||||
-rw-r--r-- | wqflask/wqflask/oauth2/groups.py | 8 | ||||
-rw-r--r-- | wqflask/wqflask/oauth2/request_utils.py | 8 | ||||
-rw-r--r-- | wqflask/wqflask/static/new/javascript/auth/search.js | 169 | ||||
-rw-r--r-- | wqflask/wqflask/static/new/javascript/auth/search_genotypes.js | 94 | ||||
-rw-r--r-- | wqflask/wqflask/static/new/javascript/auth/search_mrna.js | 96 | ||||
-rw-r--r-- | wqflask/wqflask/static/new/javascript/auth/search_phenotypes.js | 160 | ||||
-rw-r--r-- | wqflask/wqflask/templates/correlation_page.html | 2 | ||||
-rw-r--r-- | wqflask/wqflask/templates/generif.html | 102 | ||||
-rw-r--r-- | wqflask/wqflask/templates/oauth2/data-list-genotype.html | 124 | ||||
-rw-r--r-- | wqflask/wqflask/templates/oauth2/data-list-mrna.html | 168 | ||||
-rw-r--r-- | wqflask/wqflask/templates/oauth2/data-list-phenotype.html | 209 | ||||
-rw-r--r-- | wqflask/wqflask/templates/oauth2/group.html | 6 | ||||
-rw-r--r-- | wqflask/wqflask/templates/show_trait_details.html | 3 | ||||
-rw-r--r-- | wqflask/wqflask/views.py | 16 |
17 files changed, 1064 insertions, 282 deletions
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 <a href='{url}' target='_blank'>here</a>", "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}" diff --git a/wqflask/wqflask/oauth2/client.py b/wqflask/wqflask/oauth2/client.py index 999bbfc8..1d3e07ae 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 @@ -15,7 +16,7 @@ def oauth2_client(): scope=SCOPE, token_endpoint_auth_method="client_secret_post", token=session.get("oauth2_token")) -def oauth2_get(uri_path: str, data: dict = {}) -> Either: +def oauth2_get(uri_path: str, data: dict = {}, **kwargs) -> Either: token = session.get("oauth2_token") config = app.config client = OAuth2Session( @@ -23,19 +24,24 @@ def oauth2_get(uri_path: str, data: dict = {}) -> Either: token=token, scope=SCOPE) resp = client.get( urljoin(config["GN_SERVER_URL"], uri_path), - data=data) + data=data, + **kwargs) if resp.status_code == 200: return Right(resp.json()) 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..5026a6d1 100644 --- a/wqflask/wqflask/oauth2/data.py +++ b/wqflask/wqflask/oauth2/data.py @@ -1,10 +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, url_for, redirect, Response, Blueprint, render_template, - current_app as app) + 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 @@ -19,40 +25,104 @@ def __render_template__(templatepath, **kwargs): templatepath, **kwargs, user_privileges=user_privileges) def __search_mrna__(query, template, **kwargs): - return __render_template__(template, **kwargs) + species_name = kwargs["species_name"] + search_uri = urljoin(app.config["GN_SERVER_URL"], "oauth2/data/search") + datasets = oauth2_get( + "oauth2/data/search", + json = { + "query": query, + "dataset_type": "mrna", + "species_name": species_name, + "selected": __selected_datasets__() + }).either( + lambda err: {"datasets_error": process_error(err)}, + lambda datasets: {"datasets": datasets}) + return __render_template__(template, search_uri=search_uri, **datasets, **kwargs) + +def __selected_datasets__(): + if bool(request.json): + return request.json.get( + "selected", + request.args.get("selected", + request.form.get("selected", []))) + return request.args.get("selected", + request.form.get("selected", [])) def __search_genotypes__(query, template, **kwargs): species_name = kwargs["species_name"] + search_uri = urljoin(app.config["GN_SERVER_URL"], "oauth2/data/search") datasets = oauth2_get( "oauth2/data/search", - data = { + json = { "query": query, "dataset_type": "genotype", - "species_name": species_name + "species_name": species_name, + "selected": __selected_datasets__() }).either( lambda err: {"datasets_error": process_error(err)}, lambda datasets: {"datasets": datasets}) - return __render_template__(template, **datasets, **kwargs) + return __render_template__(template, search_uri=search_uri, **datasets, **kwargs) def __search_phenotypes__(query, template, **kwargs): - 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))}) - + page = int(request.args.get("page", 1)) + per_page = int(request.args.get("per_page", 50)) selected_traits = request.form.getlist("selected_traits") + 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: + 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": "genotype", + "species_name": request.json["species_name"], + "selected": __selected_datasets__() + }).either( + __handle_error__, + lambda datasets: jsonify(datasets)) - 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) +@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"]) @@ -154,3 +224,54 @@ 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": tuple(json.loads(dataset) for dataset + in form.getlist("selected")) + }).either(lambda err: __link_error__(process_error(err)), __link_success__) + + +@data.route("/link/mrna", methods=["POST"]) +def link_mrna_data(): + """Link mrna 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="mrna")) + + 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/mrna", json={ + "species_name": form.get("species_name"), + "group_id": form.get("group_id"), + "selected": tuple(json.loads(dataset) for dataset + in form.getlist("selected")) + }).either(lambda err: __link_error__(process_error(err)), __link_success__) 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 ed523614..fae3a347 100644 --- a/wqflask/wqflask/oauth2/request_utils.py +++ b/wqflask/wqflask/oauth2/request_utils.py @@ -20,12 +20,14 @@ 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() + return {**error.json(), "status_code": error.status_code} def request_error(response): app.logger.error(f"{response}: {response.url} [{response.status_code}]") 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..4e79bfd4 --- /dev/null +++ b/wqflask/wqflask/static/new/javascript/auth/search.js @@ -0,0 +1,169 @@ +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 + * @param {Dataset Object} A JSON.stringify-able object + * @param {String} The name to assign the checkbox + */ +function build_checkbox(data_object, checkbox_name, checkbox_aux_classes="", checked=false) { + cell = $("<td>"); + check = $( + '<input type="checkbox" class="checkbox" ' + + 'name="' + checkbox_name + '">'); + check.val(JSON.stringify(data_object)); + check.prop("checked", checked); + auxilliary_classes = checkbox_aux_classes.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, "selected", "checkbox-selected", true); +} + +function search_checkbox(dataset) { + return build_checkbox(dataset, "search_datasets", "checkbox-search"); +} + +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 new file mode 100644 index 00000000..40f88121 --- /dev/null +++ b/wqflask/wqflask/static/new/javascript/auth/search_genotypes.js @@ -0,0 +1,94 @@ +/** + * Check whether `dataset` is in array of `datasets`. + * @param {GenotypeDataset} A genotype dataset. + * @param {Array} An array of genotype datasets. + */ +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 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); + } +} + +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, + { + "method": "POST", + "contentType": "application/json; charset=utf-8", + "dataType": "json", + "data": JSON.stringify({ + "query": query, + "selected": selected, + "dataset_type": "genotype", + "species_name": species_name}), + "error": function(jqXHR, textStatus, errorThrown) { + data = jqXHR.responseJSON + elt = document.getElementById("search-error").setAttribute( + "style", "display: block;"); + document.getElementById("search-error-text").innerHTML = ( + data.error + " (" + data.status_code + "): " + + data.error_description); + document.getElementById("tbl-genotypes").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-genotypes").setAttribute( + "data-datasets", JSON.stringify(data)); + render_table(search_table); + } + }); +} + +$(document).ready(function() { + let search_table = new TableDataSource( + "#tbl-genotypes", "data-datasets", search_checkbox); + let link_table = new TableDataSource( + "#tbl-link-genotypes", "data-selected-datasets", link_checkbox); + + $("#frm-search-traits").submit(function(event) { + event.preventDefault(); + return false; + }); + + $("#txt-query").keyup(debounce(search_genotypes)); + + $("#tbl-genotypes").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-genotypes").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/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/static/new/javascript/auth/search_phenotypes.js b/wqflask/wqflask/static/new/javascript/auth/search_phenotypes.js new file mode 100644 index 00000000..61e71771 --- /dev/null +++ b/wqflask/wqflask/static/new/javascript/auth/search_phenotypes.js @@ -0,0 +1,160 @@ +/** + * Global variables: Bad idea - figure out how to pass them down a call stack. + */ +search_table = new TableDataSource( + "#tbl-phenotypes", "data-traits", (trait) => { + return build_checkbox(trait, "search_traits", "checkbox-search"); + }); +link_table = new TableDataSource( + "#tbl-link-phenotypes", "data-traits", (trait) => { + return build_checkbox( + trait, "selected", "checkbox-selected", checked=true); + }); + +/** + * 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-traits")).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 render_pheno_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 phenotype traits to select from."); + row.append(cell); + $(table_id + " tbody").append(row); + } + table_data.forEach(function(trait) { + row = $("<tr>") + 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( + '<a href="' + trait.pubmed_link + + '" title="Pubmed link for trait ' + trait.name + '.">' + + trait.year + "</a>")); + 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) { + 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); +} + +/** + * 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"); + $("#txt-search").prop("disabled", true); + $.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(() => { + fetch_search_results( + $("#tbl-phenotypes").attr("data-initial-job-id"), + display_search_results); + }, 500); +}); 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 '<a href="' + data.pubmed_link + '">' + data.pubmed_text + '</a>' } else { return data.pubmed_text diff --git a/wqflask/wqflask/templates/generif.html b/wqflask/wqflask/templates/generif.html new file mode 100644 index 00000000..d8229ac1 --- /dev/null +++ b/wqflask/wqflask/templates/generif.html @@ -0,0 +1,102 @@ +{% extends "base.html" %} + +{% block title %} +GeneWiki Entry for {{ symbol }} +{% endblock %} + +{% block css %} +<style> + + .badge { + vertical-align: top; + background-color: #336699; + } + + .list-group { + counter-reset: gnentries; + } + + summary::before { + counter-increment: gnentries; + content: counter(gnentries) "." " "; + } + + summary:hover { + cursor: zoom-in; + } +</style> + +{% endblock %} +{% block content %} + + +<div class="container"> + <h1 class="page-header">GeneWiki For {{ symbol }}</h1> + <p class="well"><strong>GeneWiki</strong> enables you to enrich the annotation of genes and transcripts.</p> + + <h3> + <strong>GeneNetwork</strong> + <span class="badge"> + {{ entries.gn_entries|length if entries.gn_entries[0] else 0 }} + </span>: + </h3> + {% if entries.gn_entries[0] %} + <ul class="list-group"> + {% for entry in entries.gn_entries %} + <li class="list-group-item"> + <details> + <summary> + {{ entry["entry"]["value"] }} + {% if entry.get("weburl") %} + glyphicon glyphicon-open + <sup><small><a href="{{ entry.weburl.value }}" target="_blank"><span class="glyphicon glyphicon-globe" aria-hidden="true"></span> web</a></small></sup> + {% endif %} + </summary> + <dl class="dl-horizontal"> + <dt>Author:</dt> + <dd>{{ entry["author"]["value"] }}</dd> + + {% if entry.get("geneCategory") %} + <dt>Category:</dt> + <dd>{{ entry["geneCategory"]["value"]}}</dd> + {% endif %} + + <dt>Add Time:</dt> + <dd>{{ entry["created"]["value"]}}</dd> + </dl> + </details> + </li> + {% endfor %} + </ul> + + {% else %} + + <p class="well"><u>There are no GeneNetwork entries for <b>{{ symbol }}.</b></u></p> + + {% endif %} + + <h3> + <strong>GeneRIF from NCBI</strong> + <span class="badge"> + {{ entries.ncbi_entries|length if entries.ncbi_entries[0] else 0 }} + </span>: + </h3> + {% if entries.ncbi_entries[0] %} + <ol> + {% for entry in entries.ncbi_entries %} + <li> + {{ entry.entry.value }} + (<a href="{{ entry['generif']['value'] }}" target="_blank">{{ entry["speciesBinomialName"]["value"] }}</a>) + {% if entry.PubMedId.value != "" %} + {% set pmids = entry.PubMedId.value.split(",") %} + (PubMed: {% for id in pmids %} <a href="http://rdf.ncbi.nlm.nih.gov/pubmed/{{ id }}" target="_blank">{{ id }}</a>{% endfor %}) + <sup><small><em>{{ entry.createdOn.value }}</em></small></sup> + {% endif %} + </li> + {% endfor %} + </ol> + {% else %} + <p class="well"><u>There are no NCBI entries for <b>{{ symbol }}.</b></u></p> + {% endif %} +</div> +{% endblock %} diff --git a/wqflask/wqflask/templates/oauth2/data-list-genotype.html b/wqflask/wqflask/templates/oauth2/data-list-genotype.html index 3ef810ec..c780a583 100644 --- a/wqflask/wqflask/templates/oauth2/data-list-genotype.html +++ b/wqflask/wqflask/templates/oauth2/data-list-genotype.html @@ -23,8 +23,11 @@ </div> <div class="row"> - <form id="frm-link-genotypes"> - <legend>Link Genotype Traits to Group</legend> + <form id="frm-link-genotypes" method="POST" + action="{{url_for('oauth2.data.link_genotype_data')}}"> + <legend>Link Genotype Datasets to Group</legend> + + <input type="hidden" name="species_name" value="{{species_name}}" /> <div class="form-group"> <label for="select-group">Group</label> @@ -54,8 +57,8 @@ {%for dataset in selected_datasets%} <tr> <td> - <input type="checkbox" class="checkbox checkbox-link" - name="selected_datasets" + <input type="checkbox" class="checkbox checkbox-selected" + name="selected" value='{{dataset | tojson}}' /> </td> <td>{{dataset.dataset_name}}</td> @@ -88,12 +91,12 @@ <div class="row"> <span id="search-messages" class="alert-danger" style="display:none"></span> - <form id="frm-search-traits" - action="#" + <form id="frm-search" + action="{{search_uri}}" method="POST"> <legend>Search: Genotype</legend> <input type="hidden" value="{{species_name}}" name="species" - id="txt-species" /> + id="txt-species-name" /> <input type="hidden" value="{{dataset_type}}" name="dataset_type" id="txt-dataset-type" /> <input type="hidden" value="{{per_page}}" name="per_page" @@ -108,6 +111,11 @@ </div> <div class="row"> + <div id="search-error" class="text-danger" style="display: none;"> + <span class="glyphicon glyphicon-exclamation-sign"></span> +   + <span id="search-error-text"></span> + </div> <table id="tbl-genotypes" class="table-hover table-striped cell-border dataTable no-footer" data-datasets='{{datasets | list | tojson}}'> @@ -151,102 +159,8 @@ {%endblock%} {%block js%} -<script language="javascript" type="text/javascript"> - function link_checkbox(dataset) { - cell = $("<td>"); - check = $('<input type="checkbox" class="checkbox checkbox-selected" ' + - 'name="selected_datasets" checked="checked">'); - check.val(JSON.stringify(dataset)); - cell.append(check); - return cell; - } - - function table_cell(value) { - cell = $("<td>"); - cell.html(value); - return cell; - } - - function render_table(table_id, data_attr_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(link_checkbox(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_id, data_attr_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 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); - } - } - - $(document).ready(function() { - $("#frm-search-traits").submit(function(event) { - event.preventDefault(); - return false; - }); - /* $("#txt-query").keyup(debounced_search()); */ - $(".checkbox-search").change(function(event) { - if(this.checked) { - selected = JSON.parse( - $("#tbl-link-genotypes").attr("data-selected-datasets")); - this_item = JSON.parse(this.value); - if(!in_array(this_item, selected)) { - selected.push(this_item); - } - $("#tbl-link-genotypes").attr( - "data-selected-datasets", - JSON.stringify(Array.from(selected))); - /* Remove from source table */ - remove_from_table_data( - this_item, "#tbl-genotypes", "data-datasets"); - /* Re-render tables */ - render_table("#tbl-link-genotypes", "data-selected-datasets"); - render_table("#tbl-genotypes", "data-datasets"); - toggle_link_button(); - } - }); - }); -</script> +<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 new file mode 100644 index 00000000..0e163235 --- /dev/null +++ b/wqflask/wqflask/templates/oauth2/data-list-mrna.html @@ -0,0 +1,168 @@ +{%extends "base.html"%} +{%from "oauth2/profile_nav.html" import profile_nav%} +{%from "oauth2/display_error.html" import display_error%} + +{%block title%}Link Data: Genotype{%endblock%} + +{%block css%} +<link rel="stylesheet" type="text/css" + href="/css/DataTables/css/jquery.dataTables.css" /> +<link rel="stylesheet" type="text/css" + href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css" /> +<link rel="stylesheet" type="text/css" href="/static/new/css/show_trait.css" /> +{%endblock%} + +{%block content%} +<div class="container" style="width: 98%;"> + {{profile_nav("data", user_privileges)}} + + {{flash_me()}} + + <div class="row"> + <noscript>This page needs javascript to work correctly</noscript> + </div> + + <div class="row"> + <form id="frm-link" method="POST" + action="{{url_for('oauth2.data.link_mrna_data')}}"> + <legend>Link mRNA Assay Datasets to Group</legend> + + <input type="hidden" name="species_name" value="{{species_name}}" /> + + <div class="form-group"> + <label for="select-group">Group</label> + <select id="select-group" name="group_id" required="required" + class="form-control"> + <option value="">Select group</option> + {%for group in groups%} + <option value="{{group.group_id}}">{{group.group_name}}</option> + {%endfor%} + </select> + </div> + + <div class="form-group"> + <table id="tbl-link" + class="table-hover table-striped cell-border dataTable no-footer" + data-datasets='{{selected_datasets | list | tojson}}'> + <thead> + <tr> + <th>Deselect</th> + <th>Group</th> + <th>Dataset Name</th> + <th>Dataset FullName</th> + <th>Dataset ShortName</th> + </tr> + </thead> + <tbody> + {%for dataset in selected_datasets%} + <tr> + <td> + <input type="checkbox" class="checkbox checkbox-selected" + name="selected" + value='{{dataset | tojson}}' /> + </td> + <td>{{dataset.dataset_name}}</td> + <td>{{dataset.dataset_fullname}}</td> + <td>{{dataset.dataset_shortname}}</td> + </tr> + {%else%} + <tr> + <td colspan="100%" align="center"> + <span class="glyphicon glyphicon-info-sign text-info"></span> +   + No datasets selected for linking. + </td> + </tr> + {%endfor%} + </tbody> + </table> + </div> + + <div class="form-group text-center"> + <input type="submit" value="Link Selected" + class="btn btn-primary" + style="border-top: 0.3em;" + {%if groups | length <= 0 or selected_datasets | length <= 0%} + disabled="disabled" + {%endif%} /> + </div> + </form> + </div> + + <div class="row"> + <span id="search-messages" class="alert-danger" style="display:none"></span> + <form id="frm-search" + action="{{search_uri}}" + method="POST"> + <legend>Search: mRNA Assay</legend> + <input type="hidden" value="{{species_name}}" name="species" + id="txt-species-name" /> + <input type="hidden" value="{{dataset_type}}" name="dataset_type" + id="txt-dataset-type" /> + <input type="hidden" value="{{per_page}}" name="per_page" + id="txt-per-page" /> + + <div class="form-group"> + <label for="txt-query">Dataset Search String</label> + <input type="text" id="txt-query" name="query" class="form-control" + value="{{query}}"/> + </div> + </form> + </div> + + <div class="row"> + <div id="search-error" class="text-danger" style="display: none;"> + <span class="glyphicon glyphicon-exclamation-sign"></span> +   + <span id="search-error-text"></span> + </div> + <table id="tbl-search" + class="table-hover table-striped cell-border dataTable no-footer" + data-datasets='{{datasets | list | tojson}}'> + <thead> + <tr> + <th>Select</th> + <th>Group</th> + <th>Study Name</th> + <th>Dataset Name</th> + <th>Dataset FullName</th> + <th>Dataset ShortName</th> + </tr> + </thead> + <tbody> + {%for dataset in datasets:%} + <tr> + <td> + <input type="checkbox" class="checkbox checkbox-search" + name="search_datasets" + value='{{dataset | tojson}}' /> + </td> + <td>{{dataset.InbredSetName}}</td> + <td>{{dataset.StudyName}}</td> + <td>{{dataset.dataset_name}}</td> + <td>{{dataset.dataset_fullname}}</td> + <td>{{dataset.dataset_shortname}}</td> + </tr> + {%else%} + <tr> + <td colspan="100%" align="center"> + <span class="glyphicon glyphicon-info-sign text-info"></span> +   + No datasets available for selection. + </td> + </tr> + {%endfor%} + </tbody> + </table> + </div> + + +</div> +{%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%} diff --git a/wqflask/wqflask/templates/oauth2/data-list-phenotype.html b/wqflask/wqflask/templates/oauth2/data-list-phenotype.html index 53c6ce8c..6afabdf8 100644 --- a/wqflask/wqflask/templates/oauth2/data-list-phenotype.html +++ b/wqflask/wqflask/templates/oauth2/data-list-phenotype.html @@ -40,7 +40,9 @@ </select> </div> - <table id="tbl-link-phenotypes" class="table-hover table-striped cell-border"> + <table id="tbl-link-phenotypes" + class="table-hover table-striped cell-border dataTable no-footer" + data-traits="[]"> <tbody> <tr> <td colspan="100%" align="center" style="text-align: center;"> @@ -53,12 +55,14 @@ </tr> </table> - {%if groups | length > 0%} - <input type="submit" value="Link Selected" class="btn btn-primary" /> - {%else%} - <input type="submit" value="No group to link to" class="btn btn-warning" - disabled="disabled" /> - {%endif%} + <div class="form-group text-center"> + <input type="submit" value="Link Selected" + class="btn btn-primary" + style="border-top: 0.3em;" + {%if groups | length <= 0 or traits | length <= 0%} + disabled="disabled" + {%endif%} /> + </div> </form> </div> @@ -66,7 +70,8 @@ <span id="search-messages" class="alert-danger" style="display:none"></span> <form id="frm-search-traits" action="#" - method="POST"> + method="POST" + data-search-results-endpoint="{{results_endpoint}}"> {%if dataset_type == "mrna"%} <legend>mRNA: Search</legend> {%else%} @@ -75,7 +80,7 @@ </legend> {%endif%} <input type="hidden" value="{{species_name}}" name="species" - id="txt-species" /> + id="txt-species-name" /> <input type="hidden" value="{{dataset_type}}" name="dataset_type" id="txt-dataset-type" /> <input type="hidden" value="{{per_page}}" name="per_page" @@ -84,23 +89,68 @@ <div class="form-group"> <label for="txt-query">Search</label> <div class="input-group"> - <span class="input-group-addon">species:{{species_name}} AND (</span> + <span class="input-group-addon">species:{{species_name}} AND </span> <input type="text" id="txt-query" name="query" class="form-control" value="{{query}}"/> - <span class="input-group-addon">)</span> </div> </div> </form> </div> <div class="row"> - <table id="tbl-phenotypes" class="table-hover table-striped cell-border"> + <table id="tbl-phenotypes" + class="table-hover table-striped cell-border dataTable no-footer" + data-traits="{{traits | tojson}}" + data-initial-job-id={{search_results.job_id}} + data-initial-search-res={{search_results | tojson}}> + <thead> + <tr> + <th>Select</th> + <th>Name</th> + <th>Group</th> + <th>Dataset</th> + <th>Dataset Fullname</th> + <th>Description</th> + <th>Authors</th> + <th>Year</th> + <th>Location</th> + <th>LRS</th> + <th>Additive</th> + </tr> + </thead> <tbody> + {%for trait in traits%} + <tr> + <th> + <input type="checkbox" class="checkbox checkbox-search" + name="search_traits" value="{{trait | tojson}}" /> + </th> + <th>{{trait.name}}</th> + <th>{{trait.group}}</th> + <th>{{trait.dataset}}</th> + <th>{{trait.dataset_fullname}}</th> + <th>{{trait.description}}</th> + <th>{{trait.authors | join(" ")}}</th> + <th> + <a href="{{trait.pubmed_linj}}" title="Pubmed link for trait"> + {{trait.year}} + </a> + </th> + <th>CHR{{trait.geno_chr}}@{{trait.geno_mb}}</th> + <th>{{trait.lrs}}</th> + <th>{{trait.additive}}</th> + </tr> + {%else%} <tr> - <td colspan="100%" align="center"> - <br><b><font size="15">Loading...</font></b><br> + <td colspan="100%" align="center" style="text-align: center;"> + <br/><b><font size="4"> + <span class="glyphicon glyphicon-info-sign text-info"></span> + + There are no phenotype traits to select from. + </font></b><br /> </td> </tr> + {%endfor%} </table> </div> @@ -110,134 +160,7 @@ {%block js%} <script language="javascript" type="text/javascript" - src="/js/DataTables/js/jquery.dataTables.min.js"></script> - -<script language="javascript" type="text/javascript" - src="/js/DataTablesExtensions/plugins/sorting/natural.js"></script> - -<script language="javascript" type="text/javascript" - src="/js/DataTablesExtensions/colReorder/js/dataTables.colReorder.js"> -</script> - -<script language="javascript" type="text/javascript" - src="/js/DataTablesExtensions/colResize/dataTables.colResize.js"> -</script> - + src="/static/new/javascript/auth/search.js"></script> <script language="javascript" type="text/javascript" - src="/static/new/javascript/create_datatable.js"></script> - -<script language="javascript" type="text/javascript"> - function init_table(table_id, traits) { - create_table( - tableId=table_id, tableData=traits, - columnDefs=[ - {"data": null, "render": function(data) { - return ( - '<input type="checkbox" ' + - (table_id == 'tbltbl-phenotypes' ? - 'name="pheno_traits" ': 'name="selected_traits" ') + - 'class="checkbox" value="' + - data.name + ':' + data.dataset + - '" />'); - }}, - {"title": "Index", "data": "index"}, - {"title": "Dataset", "data": "dataset_fullname"}, - {"title": "Group", "data": "group"}, - {"title": "Record", "data": null, "render": function(data) { - return ( - '<a target="_blank" href="/show_trait?trait_id=' + - data.name + '&dataset=' + data.dataset + - '">' + data.name + "</a>"); - }}, - {"title": "Description", "data": "description"}, - {"title": "Authors", "data": "authors"}, - {"title": "Year", "data": null, render: function(data) { - return ( - '<a target="_blank" href="' + data.pubmed_link + - '" >' + data.year + '</a>'); - }}, - {"title": "LRS", "data": null, "render": function(data) { - return data.lrs ? data.lrs.toFixed(4) : "N/A"; - }}, - {"title": "Peak Location", "data": null, "render": function(data) { - return 'Chr' + data.geno_chr + ': ' + data.geno_mb; - }}, - {"title": "Additive Effects", "data": null, "render": function(data) { - return data.additive ? data.additive.toFixed(4) : "N/A"; - }}], - customSettings = { - "scrollY": "40vh", - "language": { - "emptyTable": "No traits to display!", - "info": "Showing _START_ to _END_ of _TOTAL_ entries", - "infoEmpty": "No entries to show", - "loadingRecords": "Loading entries ..." - } - }); - } - - function add_index(item, index) { - return {"index": index, ...item}; - } - - function do_search() { - /*vent.preventDefault();*/ - user_query = $("#txt-query").val(); - dataset_type = $("#txt-dataset-type").val(); - per_page = $("#txt-per-page").val(); - species = $("#txt-species").val(); - query = "species:" + species; - if (user_query.length > 2) { - query = query + " AND (" + user_query + ")"; - } - $("#search-messages").html(""); - $("#search-messages").attr("style", "display:none;"); - $.ajax( - "{{search_endpoint}}", - { - "method": "GET", - "data": { - "type": dataset_type, - "per_page": per_page, - "query": query - }, - "error": function(jqXHR, textStatus, errorThrown) { - msg_elt = $("#search-messages") - console.debug(jqXHR) - $("#search-messages").html( - "<strong>" + textStatus + "</strong>: Search for '" + - user_query + "' failed! Try a different search."); - $("#search-messages").attr("style", "display: block;"); - }, - "success": function (data, textStatus, jqXHR) { - init_table("tbl-phenotypes", data.map(add_index)); - } - }); - } - - function debounced_search() { - var timeout; - return function search(event) { - clearTimeout(timeout); - timeout = setTimeout(do_search, 500); - }; - } - - function sanitised_table_data(data) { - if(data && data.length > 0) { - return data - } - return null - } - - $(document).ready(function() { - $("#frm-search-traits").submit(function(event) { - event.preventDefault(); - return false; - }); - $("#txt-query").keyup(debounced_search()) - init_table("tbl-phenotypes", {{traits | list | tojson}}); - init_table("tbl-link-phenotypes", {{selected_traits | list | tojson}}); - }); -</script> + src="/static/new/javascript/auth/search_phenotypes.js"></script> {%endblock%} 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%} + <div class="row" style="text-align:center;line-height:5em;"> + {{display_error("Group", group_error)}} + </div> + {%else%} <div class="container-fluid"> <div class="row"> {%if group_join_requests_error is defined %} @@ -103,6 +108,7 @@ </table> </div> + {%endif%} </div> {%endblock%} diff --git a/wqflask/wqflask/templates/show_trait_details.html b/wqflask/wqflask/templates/show_trait_details.html index 2a94bfe1..83ab1482 100644 --- a/wqflask/wqflask/templates/show_trait_details.html +++ b/wqflask/wqflask/templates/show_trait_details.html @@ -224,7 +224,8 @@ <button type="button" class="btn btn-default" title="Check probe locations at UCSC" onclick="window.open('{{ UCSC_BLAT_URL }}', '_blank')">Verify</button> {% endif %} {% if this_trait.symbol != None %} - <button type="button" class="btn btn-default" title="Write or review comments about this gene" onclick="window.open('http://gn1.genenetwork.org/webqtl/main.py?FormID=geneWiki&symbol={{ this_trait.symbol }}', '_blank')">GeneWiki</button> + <button type="button" class="btn btn-default" title="Write or review comments about this gene" onclick="window.open('http://gn1.genenetwork.org/webqtl/main.py?FormID=geneWiki&symbol={{ this_trait.symbol }}', '_blank')">(GN1) GeneWiki</button> + <button type="button" class="btn btn-default" title="Write or review comments about this gene" onclick="window.open('{{ url_for('display_generif_page', symbol=this_trait.symbol) }}', '_blank')">(GN2) GeneWiki</button> {% if dataset.group.species == "mouse" or dataset.group.species == "rat" %} <button type="button" class="btn btn-default" title="View SNPs and Indels" onclick="window.open('/snp_browser?first_run=true&species={{ dataset.group.species }}&gene_name={{ this_trait.symbol }}&limit_strains=on', '_blank')">SNPs</button> {% endif %} diff --git a/wqflask/wqflask/views.py b/wqflask/wqflask/views.py index 69576cc4..24426539 100644 --- a/wqflask/wqflask/views.py +++ b/wqflask/wqflask/views.py @@ -1048,3 +1048,19 @@ def display_diffs_users(): files) return render_template("display_files_user.html", files=files) + + +@app.route("/genewiki/<symbol>") +def display_generif_page(symbol): + """Fetch GeneRIF metadata from GN3 and display it""" + entries = requests.get( + urljoin( + GN3_LOCAL_URL, + f"/api/metadata/genewiki/{symbol}" + ) + ).json() + return render_template( + "generif.html", + symbol=symbol, + entries=entries + ) |