aboutsummaryrefslogtreecommitdiff
path: root/wqflask
diff options
context:
space:
mode:
Diffstat (limited to 'wqflask')
-rw-r--r--wqflask/wqflask/metadata_edits.py2
-rw-r--r--wqflask/wqflask/oauth2/client.py14
-rw-r--r--wqflask/wqflask/oauth2/data.py165
-rw-r--r--wqflask/wqflask/oauth2/groups.py8
-rw-r--r--wqflask/wqflask/oauth2/request_utils.py8
-rw-r--r--wqflask/wqflask/static/new/javascript/auth/search.js169
-rw-r--r--wqflask/wqflask/static/new/javascript/auth/search_genotypes.js94
-rw-r--r--wqflask/wqflask/static/new/javascript/auth/search_mrna.js96
-rw-r--r--wqflask/wqflask/static/new/javascript/auth/search_phenotypes.js160
-rw-r--r--wqflask/wqflask/templates/correlation_page.html2
-rw-r--r--wqflask/wqflask/templates/generif.html102
-rw-r--r--wqflask/wqflask/templates/oauth2/data-list-genotype.html124
-rw-r--r--wqflask/wqflask/templates/oauth2/data-list-mrna.html168
-rw-r--r--wqflask/wqflask/templates/oauth2/data-list-phenotype.html209
-rw-r--r--wqflask/wqflask/templates/oauth2/group.html6
-rw-r--r--wqflask/wqflask/templates/show_trait_details.html3
-rw-r--r--wqflask/wqflask/views.py16
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("&nbsp;");
+ 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("&nbsp;");
+ 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>
+ &nbsp
+ <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("&nbsp;");
- 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>
+ &nbsp
+ 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>
+ &nbsp
+ <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>
+ &nbsp
+ 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>
+ &nbsp;
+ 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
+ )