about summary refs log tree commit diff
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%}