aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrederick Muriuki Muriithi2023-03-23 17:51:11 +0300
committerFrederick Muriuki Muriithi2023-03-23 17:53:01 +0300
commite21250a8dcc61bc476945a2f13fe7e27cb69d17e (patch)
tree96d00710ab3fbdb7149b1ddc8c5a3c8155aa8ad5
parentf2774c328f88752186934c29fe992cc373587e4e (diff)
downloadgenenetwork2-e21250a8dcc61bc476945a2f13fe7e27cb69d17e.tar.gz
oauth2: data: Provide a UI for linking data, with search.
Provide a UI for the phenotype datasets with a search interface (currently inactive) that is eventually going to allow for linking the traits to user groups.
-rw-r--r--wqflask/wqflask/oauth2/data.py98
-rw-r--r--wqflask/wqflask/templates/oauth2/data-list-phenotype.html124
-rw-r--r--wqflask/wqflask/templates/oauth2/data-list.html188
3 files changed, 215 insertions, 195 deletions
diff --git a/wqflask/wqflask/oauth2/data.py b/wqflask/wqflask/oauth2/data.py
index 0cd110f6..432ca3b9 100644
--- a/wqflask/wqflask/oauth2/data.py
+++ b/wqflask/wqflask/oauth2/data.py
@@ -7,6 +7,39 @@ from .client import oauth2_get, oauth2_post
data = Blueprint("data", __name__)
+def __render_template__(templatepath, **kwargs):
+ roles = kwargs.get("roles", tuple())
+ user_privileges = tuple(
+ privilege["privilege_id"] for role in roles
+ for privilege in role["privileges"])
+ return render_template(
+ templatepath, **kwargs, user_privileges=user_privileges)
+
+@data.route("/<string:species_name>/<string:dataset_type>/list",
+ methods=["GET", "POST"])
+def list_data_by_species_and_dataset(
+ species_name: str, dataset_type: str) -> Response:
+ templates = {
+ "mrna": "oauth2/data-list-mrna.html",
+ "genotype": "oauth2/data-list-genotype.html",
+ "phenotype": "oauth2/data-list-phenotype.html"}
+ roles = oauth2_get("oauth2/user/roles").either(
+ lambda err: {"roles_error": process_error(err)},
+ lambda roles: {"roles": roles})
+ per_page = int(request.args.get("per_page", 500))
+ traits = oauth2_get(
+ (f"search/?type={dataset_type}&per_page={per_page}&query="
+ f"species:{species_name}")).either(
+ lambda err: {"traits_error": process_error(er)},
+ lambda trts: {"traits": tuple({
+ "index": idx, **trait
+ } for idx, trait in enumerate(trts, start=1))})
+
+ return __render_template__(
+ templates[dataset_type], **roles, **traits, species_name=species_name,
+ dataset_type=dataset_type, per_page=per_page,
+ query=request.args.get("query", ""))
+
@data.route("/list", methods=["GET", "POST"])
def list_data():
"""List ungrouped data."""
@@ -23,65 +56,29 @@ def list_data():
**{key:val for key,val in kwargs.items()
if key not in ("groups", "data_items", "user_privileges")})
- def __process_menus__(mns):
- return {
- species_id: {
- "display_name": display_name,
- "family": family,
- "groups": {
- group_id: {
- "group_name": group_name,
- "family": family,
- "types": {
- type_id: {
- "menu_value": type_menu_value,
- "menu_heading": type_menu_heading,
- "datasets": tuple(
- dict(zip(("accession_id", "dataset_id",
- "dataset_fullname"),
- dataset_row))
- for dataset_row in mns["datasets"][
- species_id][group_id][type_id])
- }
- for type_id, type_menu_value, type_menu_heading
- in mns["types"][species_id][group_id]
- }
- }
- for group_id, group_name, family in mns["groups"][species_id]
- }
- }
- for species_id, display_name, family in mns["species"]}
-
groups = oauth2_get("oauth2/group/list").either(
lambda err: {"groups_error": process_error(err)},
lambda grp: {"groups": grp})
roles = oauth2_get("oauth2/user/roles").either(
lambda err: {"roles_error": process_error(err)},
lambda roles: {"roles": roles})
- menus = oauth2_get("menu/generate/json").either(
- lambda err: {"menus_error": process_error(err)},
- lambda mns: {"menus": __process_menus__(mns)})
+ species = oauth2_get("oauth2/data/species").either(
+ lambda err: {"species_error": process_error(err)},
+ lambda species: {"species": species})
if request.method == "GET":
- return __render__(**{**groups, **roles, **menus})
+ return __render__(**{**groups, **roles, **species})
+ species_name = request.form["species_name"]
dataset_type = request.form["dataset_type"]
- offset = int(request.form.get("offset", 0)) + (
- 0 if request.form.get("offset_submit") is None else(
- 100 if request.form["offset_submit"] == "Next" else -100))
if dataset_type not in ("mrna", "genotype", "phenotype"):
flash("InvalidDatasetType: An invalid dataset type was provided",
"alert-danger")
- return __render__(**{**groups, **roles})
+ return __render__(**{**groups, **roles, **species})
- data_items = oauth2_get(
- f"oauth2/group/{dataset_type}/ungrouped-data?offset={offset}").either(
- lambda err: {"data_items_error": process_error(err)},
- lambda data: {"data_items": data})
- return __render__(**{
- **groups, **roles, **menus, **data_items, "dataset_type": dataset_type,
- "offset": offset
- })
+ return redirect(url_for(
+ "oauth2.data.list_data_by_species_and_dataset",
+ species_name=species_name, dataset_type=dataset_type))
@data.route("/link", methods=["POST"])
def link_data():
@@ -96,20 +93,25 @@ def link_data():
form = request.form
try:
- keys = ("dataset_id", "dataset_type", "group_id")
+ keys = ("dataset_type", "group_id")
assert all(item in form for item in keys)
assert all(bool(form[item]) for item in keys)
state_data = {
"dataset_type": form["dataset_type"],
"offset": form.get("offset", 0)}
+ dataset_ids = form.getlist("dataset_ids")
+ if len(dataset_ids) == 0:
+ flash("You must select at least one item to link", "alert-danger")
+ return redirect(url_for(
+ "oauth2.data.list_data", **state_data))
return oauth2_post(
"oauth2/group/data/link",
data={
"dataset_type": form["dataset_type"],
- "dataset_id": form["dataset_id"],
+ "dataset_ids": dataset_ids,
"group_id": form["group_id"]
}).either(lambda err: __error__(err, state_data),
lambda success: __success__(success, state_data))
except AssertionError as aserr:
- flash("You must provide all the expected data.", "alert-error")
+ flash("You must provide all the expected data.", "alert-danger")
return redirect(url_for("oauth2.data.list_data"))
diff --git a/wqflask/wqflask/templates/oauth2/data-list-phenotype.html b/wqflask/wqflask/templates/oauth2/data-list-phenotype.html
new file mode 100644
index 00000000..3c58f275
--- /dev/null
+++ b/wqflask/wqflask/templates/oauth2/data-list-phenotype.html
@@ -0,0 +1,124 @@
+{%extends "base.html"%}
+{%from "oauth2/profile_nav.html" import profile_nav%}
+{%from "oauth2/display_error.html" import display_error%}
+
+{%block title%}View User{%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="min-width: 1250px;">
+ {{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-select-datatype"
+ action="#"
+ method="POST">
+ {%if dataset_type == "mrna"%}
+ <legend>mRNA: Search</legend>
+ {%else%}
+ <legend style="text-transform: capitalize;">
+ {{dataset_type}}: Search
+ </legend>
+ {%endif%}
+ <input type="hidden" value="{{species_name}}" name="species" />
+ <input type="hidden" value="{{dataset_type}}" name="dataset_type" />
+ <input type="hidden" value="{{per_page}}" name="per_page" />
+
+ <div class="form-group">
+ <label for="txt-query">Search</label>
+ <div class="input-group">
+ <span class="input-group-addon">species:{{species_name}}</span>
+ <input type="text" id="txt-query" name="query" class="form-control"
+ value="{{query}}"/>
+ </div>
+ </div>
+ </form>
+ </div>
+
+ <div class="row">
+ <table id="tbl-phenotypes" class="table-hover table-striped cell-border">
+ <tbody>
+ <tr>
+ <td colspan="100%" align="center">
+ <br><b><font size="15">Loading...</font></b><br>
+ </td>
+ </tr>
+ </table>
+ </div>
+
+</div>
+
+{%endblock%}
+
+{%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>
+
+<script language="javascript" type="text/javascript"
+ src="/static/new/javascript/create_datatable.js"></script>
+
+<script language="javascript" type="text/javascript">
+ var traits = {{traits | list | tojson}};
+ create_table(
+ tableId="tbl-phenotypes", tableData=traits,
+ columnDefs=[
+ {"data": null, "render": function(data) {
+ return (
+ '<input type="checkbox" name="pheno_traits" ' +
+ 'class="checkbox" value="' +
+ data.dataset + '::' + data.name +
+ '" />');
+ }},
+ {"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) {
+ console.debug(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";
+ }}]);
+</script>
+{%endblock%}
diff --git a/wqflask/wqflask/templates/oauth2/data-list.html b/wqflask/wqflask/templates/oauth2/data-list.html
index 019e2346..4e0cf5ae 100644
--- a/wqflask/wqflask/templates/oauth2/data-list.html
+++ b/wqflask/wqflask/templates/oauth2/data-list.html
@@ -3,156 +3,50 @@
{%from "oauth2/display_error.html" import display_error%}
{%block title%}View User{%endblock%}
{%block content%}
- <div class="container" style="min-width: 1250px;">
- {{profile_nav("data", user_privileges)}}
-
- {{flash_me()}}
-
- <div class="container-fluid">
- <div class="row">
- <form method="POST" action="{{url_for('oauth2.data.list_data')}}">
- <legend>Dataset Type</legend>
- <input type="hidden" name="offset" value="0" />
- <input type="hidden" name="menus" value="{{menus | tojson}}" />
- <div class="form-group">
- <label for="select-menu">Dataset</label>
- <select id="select-menu" name="dataset">
- {%for species_id, species_data in menus.items()%}
- <optgroup label="{{species_data.display_name}}">
- {%for group_id, group_data in species_data["groups"].items()%}
- <optgroup label=" {{group_data.group_name}}">
- {%for type_id, type_data in group_data["types"].items()%}
- <optgroup label="{{type_data.menu_heading}}">
- {%for dataset in type_data["datasets"]%}
- <option value="{{dataset.dataset_id}}">
- {{dataset.dataset_fullname}}
- </option>
- {%endfor%}
- </optgroup>
- {%endfor%}
- </optgroup>
- {%endfor%}
- </optgroup>
- {%endfor%}
- </select>
- </div>
- <div class="form-group">
- <label for="dataset_type" class="form-label">Dataset Type</label>
- <select name="dataset_type" required="required">
- <option value="">Select dataset type</option>
- <option value="mrna"
- {%if dataset_type=="mrna"%}
- selected="selected"
- {%endif%}>mRNA Assay Datasets</option>
- <option value="genotype"
- {%if dataset_type=="genotype"%}
- selected="selected"
- {%endif%}>Genotype Datasets</option>
- <option value="phenotype"
- {%if dataset_type=="phenotype"%}
- selected="selected"
- {%endif%}>Phenotype/Publish Datasets</option>
- </select>
- </div>
- <input type="submit" value="Fetch Unlinked Data" class="btn btn-primary" />
- </form>
+<div class="container" style="min-width: 1250px;">
+ {{profile_nav("data", user_privileges)}}
+
+ {{flash_me()}}
+
+ <div class="row">
+ <form id="frm-select-datatype"
+ action="{{url_for('oauth2.data.list_data')}}"
+ method="POST">
+ <legend>Search</legend>
+ {%if species_error is defined%}
+ {{display_error("Species", species_error)}}
+ {%elif species | length == 0%}
+ <span class="glyphicon glyphicon-info-sign text-danger">
+ </span>
+ &nbsp;
+ <strong class="text-danger">No list of species to select from</strong>
+ {%else%}
+ <div class="form-group">
+ <label for="select-species">Species</label>
+ <select id="select-species" name="species_name" required="required">
+ <option value="">Select Species</option>
+ {%for spc in species%}
+ <option value="{{spc.Name}}">
+ {{spc.MenuName}} ({{spc.FullName}})
+ </option>
+ {%endfor%}
+ </select>
</div>
- {%if dataset_type is defined%}
- {%if data_items_error is defined%}
- {{display_error("Data Error", data_items_error)}}
- {%else%}
- <div class="row">
- <form method="POST" action="{{url_for('oauth2.data.link_data')}}">
- <legend>Link Data to Group</legend>
- <input type="hidden" name="offset" value="{{offset or 0}}" />
- <input type="hidden" name="dataset_type" value="{{dataset_type}}" />
-
- <div class="form-group">
- <label for="select-group-id">Group</label>
- <select name="group_id" required="required">
- <option value="">Select group</option>
- {%for group in groups%}
- <option value="{{group.group_id}}">{{group.group_name}}</option>
- {%endfor%}
- </select>
- </div>
-
- <table class="table">
- <caption>Link Data to Group</caption>
-
- <thead>
- <tr>
- <th>Select</th>
- {%if dataset_type == "phenotype"%}
- <th>Trait ID</th>
- <th>Data Group</th>
- {%endif%}
- <th>Dataset Name</th>
- <th>Dataset Full Name</th>
- <th>Link</th>
- </tr>
- </thead>
-
- <tbody>
- {%for data_item in data_items%}
- <tr>
- <td>
- <input type="checkbox" value="{{data_item.Id}}"
- name="dataset_ids" />
- </td>
- {%if dataset_type == "phenotype"%}
- <td>
- <a href="/show_trait?trait_id={{data_item.Id}}&dataset={{data_item.dataset_name}}"
- title="Trait Page">
- {{data_item.Id}}
- </a>
- </td>
- <td>{{data_item.InbredSetName}}</td>
- {%endif%}
- <td>
- <a href="https://gn1.genenetwork.org/webqtl/main.py?FormID=sharinginfo&GN_AccessionId={{data_item.accession_id}}&InfoPageName={{data_item.dataset_name}}"
- title="Link to information on dataset '{{data_item.dataset_fullname}}'"
- target="_blank">
- {{data_item.dataset_name}}
- </a>
- </td>
- <td>{{data_item.dataset_fullname}}</td>
- <td>
- <input type="submit" value="Link" class="btn btn-info" />
- </td>
- </tr>
- {%else%}
- <tr>
- <td colspan="4">
- <span class="glyphicon glyphicon-info-sign text-danger">
- </span>
- &nbsp;
- <strong class="text-info">No available data to link</strong>
- </td>
- </tr>
- {%endfor%}
- </tbody>
- </table>
- </form>
-
- <form method="POST" action="{{url_for('oauth2.data.list_data')}}">
- <input type="hidden" name="dataset_type" value="{{dataset_type}}" />
- <input type="hidden" name="offset" value="{{offset or 0}}" />
- <div class="form-group" style="margin: auto;width: 50%;">
- {%if offset != 0%}
- <input type="submit" name="offset_submit" value="Previous" class="btn btn-warning" />
- {%endif%}
- {%if data_items | length == 100:%}
- <input type="submit" name="offset_submit" value="Next" class="btn btn-warning" />
- {%endif%}
- </div>
- </form>
+ <div class="form-group">
+ <label for="select-dataset-type">Dataset/Trait Type</label>
+ <select id="select-dataset-type" name="dataset_type"
+ required="required">
+ <option value="">Select dataset type</option>
+ <option value="mrna">mRNA Assay (ProbeSet) Dataset</option>
+ <option value="genotype">Genotype Dataset</option>
+ <option value="phenotype">Phenotype (Publish) Dataset</option>
+ </select>
</div>
- {%endif%}
- {%endif%}
-
- </div>
+ <input type="submit" class="btn btn-primary" value="Search" />
+ {%endif%}
+ </form>
</div>
+</div>
{%endblock%}