about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--doc/heatmap-generation.org34
-rw-r--r--doc/images/gn2_header_collections.pngbin0 -> 7890 bytes
-rw-r--r--doc/images/heatmap_form.pngbin0 -> 9363 bytes
-rw-r--r--doc/images/heatmap_with_hover_tools.pngbin0 -> 42578 bytes
-rw-r--r--etc/default_settings.py1
-rw-r--r--wqflask/tests/unit/wqflask/wgcna/__init__.py0
-rw-r--r--wqflask/tests/unit/wqflask/wgcna/test_wgcna.py50
-rw-r--r--wqflask/wqflask/collect.py7
-rw-r--r--wqflask/wqflask/marker_regression/run_mapping.py2
-rw-r--r--wqflask/wqflask/templates/collections/view.html101
-rw-r--r--wqflask/wqflask/templates/test_wgcna_results.html158
-rw-r--r--wqflask/wqflask/templates/tool_buttons.html4
-rw-r--r--wqflask/wqflask/templates/wgcna_setup.html198
-rw-r--r--wqflask/wqflask/views.py11
-rw-r--r--wqflask/wqflask/wgcna/gn3_wgcna.py102
15 files changed, 617 insertions, 51 deletions
diff --git a/doc/heatmap-generation.org b/doc/heatmap-generation.org
new file mode 100644
index 00000000..a697c70b
--- /dev/null
+++ b/doc/heatmap-generation.org
@@ -0,0 +1,34 @@
+#+STARTUP: inlineimages
+#+TITLE: Heatmap Generation
+#+AUTHOR: Muriithi Frederick Muriuki
+
+* Generating Heatmaps
+
+Like a lot of other features, the heatmap generation requires an existing collection. If none exists, see [[][Creating a new collection]] for how to create a new collection.
+
+Once you have a collection, you can navigate to the collections page by clicking on the "Collections" link in the header
+
+
+[[./images/gn2_header_collections.png]]
+
+From that page, pick the collection that you want to work with by clicking on its name on the collections table.
+
+That takes you to that collection's page, where you can select the data that you want to use to generate the heatmap.
+
+** Selecting Orientation
+
+Once you have selected the data, select the orientation of the heatmap you want generated. You do this by selecting either *"vertical"* or *"horizontal"* in the heatmaps form:
+
+[[./images/heatmap_form.png]]
+
+Once you have selected the orientation, click on the "Generate Heatmap" button as in the image above.
+
+The heatmap generation might take a while, but once it is done, an image shows up above the data table.
+
+** Downloading the PNG copy of the Heatmap
+
+Once the heatmap image is shown, hovering over it, displays some tools to interact with the image.
+
+To download, hover over the heatmap image, and click on the "Download plot as png" icon as shown.
+
+[[./images/heatmap_with_hover_tools.png]]
diff --git a/doc/images/gn2_header_collections.png b/doc/images/gn2_header_collections.png
new file mode 100644
index 00000000..ac23f9c1
--- /dev/null
+++ b/doc/images/gn2_header_collections.png
Binary files differdiff --git a/doc/images/heatmap_form.png b/doc/images/heatmap_form.png
new file mode 100644
index 00000000..163fbb60
--- /dev/null
+++ b/doc/images/heatmap_form.png
Binary files differdiff --git a/doc/images/heatmap_with_hover_tools.png b/doc/images/heatmap_with_hover_tools.png
new file mode 100644
index 00000000..4ab79f99
--- /dev/null
+++ b/doc/images/heatmap_with_hover_tools.png
Binary files differdiff --git a/etc/default_settings.py b/etc/default_settings.py
index a194b10e..023aa53b 100644
--- a/etc/default_settings.py
+++ b/etc/default_settings.py
@@ -25,7 +25,6 @@ import os
 import sys
 
 GN_VERSION = open("../etc/VERSION", "r").read()
-GN_SERVER_URL = "http://localhost:8880/" # REST API server
 
 # ---- MySQL
 
diff --git a/wqflask/tests/unit/wqflask/wgcna/__init__.py b/wqflask/tests/unit/wqflask/wgcna/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/wqflask/tests/unit/wqflask/wgcna/__init__.py
diff --git a/wqflask/tests/unit/wqflask/wgcna/test_wgcna.py b/wqflask/tests/unit/wqflask/wgcna/test_wgcna.py
new file mode 100644
index 00000000..8e947e2f
--- /dev/null
+++ b/wqflask/tests/unit/wqflask/wgcna/test_wgcna.py
@@ -0,0 +1,50 @@
+
+"""module contains for processing gn3 wgcna data"""
+from unittest import TestCase
+
+from wqflask.wgcna.gn3_wgcna import process_wgcna_data
+
+
+class DataProcessingTests(TestCase):
+    """class contains data processing tests"""
+
+    def test_data_processing(self):
+        """test for parsing data for datatable"""
+        output = {
+            "input": {
+                "sample_names": ["BXD1", "BXD2", "BXD3", "BXD4", "BXD5", "BXD6"],
+
+            },
+            "output": {
+                "ModEigens": {
+                    "MEturquoise": [
+                        0.0646677768085351,
+                        0.137200224277058,
+                        0.63451113720732,
+                        -0.544002665501479,
+                        -0.489487590361863,
+                        0.197111117570427
+                    ],
+                    "MEgrey": [
+                        0.213,
+                        0.214,
+                        0.3141,
+                        -0.545,
+                        -0.423,
+                        0.156,
+                    ]
+                }}}
+
+        row_data = [['BXD1', 0.065, 0.213],
+                    ['BXD2', 0.137, 0.214],
+                    ['BXD3', 0.635, 0.314],
+                    ['BXD4', -0.544, -0.545],
+                    ['BXD5', -0.489, -0.423],
+                    ['BXD6', 0.197, 0.156]]
+
+        expected_results = {
+            "col_names": ["sample_names", "MEturquoise", "MEgrey"],
+            "mod_dataset": row_data
+        }
+
+        self.assertEqual(process_wgcna_data(output), expected_results)
diff --git a/wqflask/wqflask/collect.py b/wqflask/wqflask/collect.py
index 01274ba9..3475ae5d 100644
--- a/wqflask/wqflask/collect.py
+++ b/wqflask/wqflask/collect.py
@@ -12,6 +12,7 @@ from flask import flash
 from wqflask import app
 from utility import hmac
 from utility.formatting import numify
+from utility.tools import GN_SERVER_URL
 from utility.redis_tools import get_redis_conn
 
 from base.trait import create_trait
@@ -218,8 +219,10 @@ def view_collection():
 
         json_version.append(jsonable(trait_ob))
 
-    collection_info = dict(trait_obs=trait_obs,
-                           uc=uc)
+    collection_info = dict(
+        trait_obs=trait_obs,
+        uc=uc,
+        heatmap_data_url=f"{GN_SERVER_URL}api/heatmaps/clustered")
 
     if "json" in params:
         return json.dumps(json_version)
diff --git a/wqflask/wqflask/marker_regression/run_mapping.py b/wqflask/wqflask/marker_regression/run_mapping.py
index 80094057..769b9240 100644
--- a/wqflask/wqflask/marker_regression/run_mapping.py
+++ b/wqflask/wqflask/marker_regression/run_mapping.py
@@ -230,7 +230,7 @@ class RunMapping:
 
                     self.perm_strata = get_perm_strata(
                         self.this_trait, primary_samples, self.categorical_vars, self.samples)
-            self.score_type = "LOD"
+            self.score_type = "-logP"
             self.control_marker = start_vars['control_marker']
             self.do_control = start_vars['do_control']
             if 'mapmethod_rqtl_geno' in start_vars:
diff --git a/wqflask/wqflask/templates/collections/view.html b/wqflask/wqflask/templates/collections/view.html
index a3090bcf..0ded66a6 100644
--- a/wqflask/wqflask/templates/collections/view.html
+++ b/wqflask/wqflask/templates/collections/view.html
@@ -36,6 +36,28 @@
 
         <div>
             <br />
+	    <form id="heatmaps_form">
+	      <fieldset>
+		<legend>Heatmap Orientation</legend>
+		<label for="heatmap-orient-vertical">Vertical</label>
+		<input id="heatmap-orient-vertical"
+		       type="radio"
+		       name="vertical"
+		       value="true" />
+		<label for="heatmap-orient-horizontal">Horizontal</label>
+		<input id="heatmap-orient-horizontal"
+		       type="radio"
+		       name="vertical"
+		       value="false" />
+	      </fieldset>
+	      <button id="clustered-heatmap"
+		      class="btn btn-primary"
+		      data-url="{{heatmap_data_url}}"
+		      title="Generate heatmap from this collection">
+		Generate Heatmap
+	      </button>
+	    </form>
+
             <div class="collection-table-options">
                 <form id="export_form" method="POST" action="/export_traits_csv">
                     <button class="btn btn-default" id="select_all" type="button"><span class="glyphicon glyphicon-ok"></span> Select All</button>
@@ -52,6 +74,8 @@
                     <button id="delete" class="btn btn-danger submit_special" data-url="/collections/delete" type="button" title="Delete this collection" > Delete Collection</button>
                 </form>
             </div>
+	    <div id="clustered-heatmap-image-area">
+	    </div>
             <div style="margin-top: 10px; margin-bottom: 5px;">
                 <b>Show/Hide Columns:</b>
             </div>
@@ -139,6 +163,8 @@
     <script language="javascript" type="text/javascript" src="{{ url_for('js', filename='DataTablesExtensions/buttons/js/buttons.colVis.min.js') }}"></script>
     <script type="text/javascript" src="/static/new/javascript/search_results.js"></script>
 
+    <script type="text/javascript" src="{{ url_for('js', filename='plotly/plotly.min.js') }}"></script>
+
 
     <script language="javascript" type="text/javascript">
         $(document).ready( function () {
@@ -247,6 +273,81 @@
             $("#make_default").on("click", function(){
                 make_default();
             });
+
+	    $("#heatmaps_form").submit(function(e) {
+		e.preventDefault();
+	    });
+
+	    function clear_heatmap_area() {
+		area = document.getElementById("clustered-heatmap-image-area");
+		area.querySelectorAll("*").forEach(function(child) {
+		    child.remove();
+		});
+	    }
+
+	    function generate_progress_indicator() {
+		count = 0;
+		default_message = "Computing"
+		return function() {
+		    message = default_message;
+		    if(count >= 10) {
+			count = 0;
+		    }
+		    for(i = 0; i < count; i++) {
+			message = message + " .";
+		    }
+		    clear_heatmap_area();
+		    $("#clustered-heatmap-image-area").append(
+			'<div class="alert alert-info"' +
+			    ' style="font-weigh: bold; font-size: 150%;">' +
+			    message + '</div>');
+		    count = count + 1;
+		};
+	    }
+
+	    function display_clustered_heatmap(heatmap_data) {
+		clear_heatmap_area();
+		image_area = document.getElementById("clustered-heatmap-image-area")
+		Plotly.newPlot(image_area, heatmap_data)
+	    }
+
+	    function process_clustered_heatmap_error(xhr, status, error) {
+		clear_heatmap_area()
+		$("#clustered-heatmap-image-area").append(
+		    $(
+			'<div class="alert alert-danger">ERROR: ' +
+			    xhr.responseJSON.message +
+			    '</div>'));
+	    }
+
+	    $("#clustered-heatmap").on("click", function() {
+		clear_heatmap_area();
+		intv = window.setInterval(generate_progress_indicator(), 300);
+		vert_element = document.getElementById("heatmap-orient-vertical");
+		vert_true = vert_element == null ? false : vert_element.checked;
+		heatmap_url = $(this).attr("data-url")
+		traits = $(".trait_checkbox:checked").map(function() {
+		    return this.value
+		}).get();
+		$.ajax({
+		    type: "POST",
+		    url: heatmap_url,
+		    contentType: "application/json",
+		    data: JSON.stringify({
+			"traits_names": traits,
+			"vertical": vert_true
+		    }),
+		    dataType: "JSON",
+		    success: function(data, status, xhr) {
+			window.clearInterval(intv);
+			display_clustered_heatmap(data);
+		    },
+		    error: function(xhr, status, error) {
+			window.clearInterval(intv);
+			process_clustered_heatmap_error(xhr, status, error);
+		    }
+		});
+	    });
         });
     </script>
 
diff --git a/wqflask/wqflask/templates/test_wgcna_results.html b/wqflask/wqflask/templates/test_wgcna_results.html
new file mode 100644
index 00000000..1dddd393
--- /dev/null
+++ b/wqflask/wqflask/templates/test_wgcna_results.html
@@ -0,0 +1,158 @@
+{% extends "base.html" %}
+{% block title %}WCGNA results{% endblock %}
+{% block content %}
+<!-- Start of body -->
+
+<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/xterm/3.14.5/xterm.min.css" integrity="sha512-iLYuqv+v/P4u9erpk+KM83Ioe/l7SEmr7wB6g+Kg1qmEit8EShDKnKtLHlv2QXUp7GGJhmqDI+1PhJYLTsfb8w==" crossorigin="anonymous" referrerpolicy="no-referrer" />
+
+<link rel="stylesheet" href="https://cdn.datatables.net/1.11.3/css/jquery.dataTables.min.css">
+
+
+<style type="text/css">
+
+
+.container {
+    min-height: 100vh;
+    width: 100vw;
+    padding: 20px;
+
+}
+
+.grid_container {
+
+
+    width: 80vw;
+    margin: auto;
+    padding: 20px;
+
+
+    display: grid;
+    grid-template-columns: repeat(7, 1fr);
+    /*grid-gap: 5px;*/
+    border: 1px solid black;
+    grid-column-gap: 20px;
+
+}
+
+.control_sft_column {
+    text-align: center;
+}
+
+.grid_container div:not(:last-child) {
+    border-right: 1px solid #000;
+}
+
+.grid_container .control_sft_column h3 {
+    font-weight: bold;
+    font-size: 18px;
+}
+
+.control_net_colors {
+
+    display: flex;
+    flex-wrap: wrap;
+    justify-content: space-between;
+    align-items: center;
+    text-align: center;
+}
+
+
+.control_mod_eigens {
+    display: grid;
+    grid-template-columns: repeat(2, 200px);
+}
+
+.control-image{
+  display: block;
+  margin-left: auto;
+  margin-right: auto;
+  width: 80vw;
+}
+</style>
+<div class="container">
+    <div>
+        <div >
+            <h2 style="text-align:center">Soft Thresholds </h2>
+            <div class="grid_container">
+
+            {% for key, value in results["data"]["output"]["soft_threshold"].items()%}
+            <div class="control_sft_column">
+                <h3>{{key}}</h3>
+                {% for val in value %}
+                <p>{{val|round(3)}}</p>
+                {% endfor %}
+            </div>
+            {% endfor %}
+            </div>
+        </div>
+
+        <div>
+        
+            {% if image["image_generated"] %}
+            <div >
+                <img class="control-image" src="data:image/jpeg;base64,{{ image['image_data']| safe }}">
+            </div>
+
+            {% endif %}
+<!--                 <div >
+                <img class="control-image" src="data:image/jpeg;base64,{{ results['data']['output']['image_data2']| safe }}">
+            </div> -->
+        </div>
+
+        <div>
+            <h2 style="text-align:center;"> Module eigen genes </h2>
+            <table id="eigens" class="display" width="80vw"></table>
+        </div>
+
+        <div>
+            <h2 style="text-align:center;">Phenotype modules </h2>
+
+             <table id="phenos" class="display"  width="40vw" ></table>
+        </div>
+    </div>
+</div>
+
+{% endblock %}
+
+{% block js %}
+
+<script src="https://cdnjs.cloudflare.com/ajax/libs/xterm/3.14.5/xterm.min.js"></script>
+
+<script language="javascript" type="text/javascript" src="{{ url_for('js', filename='DataTables/js/jquery.js') }}"></script>
+<script language="javascript" type="text/javascript" src="{{ url_for('js', filename='DataTables/js/jquery.dataTables.min.js') }}"></script>
+<script language="javascript" type="text/javascript" src="{{ url_for('js', filename='DataTablesExtensions/scroller/js/dataTables.scroller.min.js') }}"></script>
+
+
+<script type="text/javascript">
+
+
+let results = {{results|safe}}
+
+let phenoModules = results["data"]["output"]["net_colors"]
+let phenotypes = Object.keys(phenoModules)
+let phenoMods = Object.values(phenoModules)
+
+let {col_names,mod_dataset} = {{data|safe}}
+    $('#eigens').DataTable( {
+        data: mod_dataset,
+        columns: col_names.map((name)=>{
+            return {
+                title:name
+            }
+        })
+    } );
+    $('#phenos').DataTable( {
+        data:phenotypes.map((phenoName,idx)=>{
+            return [phenoName,phenoMods[idx]]
+        }),
+        columns: [{
+            title:"Phenotypes"
+        },
+        {
+        title: "Modules"
+        }]
+    } );
+
+
+</script>
+{% endblock %}
\ No newline at end of file
diff --git a/wqflask/wqflask/templates/tool_buttons.html b/wqflask/wqflask/templates/tool_buttons.html
index 3f9d8211..3ee5be19 100644
--- a/wqflask/wqflask/templates/tool_buttons.html
+++ b/wqflask/wqflask/templates/tool_buttons.html
@@ -18,13 +18,13 @@
   BNW
 </button>
 
-<!-- <button id="wgcna_setup" class="btn btn-primary submit_special" data-url="/wgcna_setup" title="WGCNA Analysis" >
+<button id="wgcna_setup" class="btn btn-primary submit_special" data-url="/wgcna_setup" title="WGCNA Analysis" >
     WGCNA
 </button>
 
 <button id="ctl_setup" class="btn btn-primary submit_special" data-url="/ctl_setup" title="CTL Analysis" >
     CTL Maps
-</button> -->
+</button>
 
 <button id="heatmap" class="btn btn-primary submit_special" data-url="/heatmap" title="Heatmap" >
     MultiMap
diff --git a/wqflask/wqflask/templates/wgcna_setup.html b/wqflask/wqflask/templates/wgcna_setup.html
index c5461497..86d9fa10 100644
--- a/wqflask/wqflask/templates/wgcna_setup.html
+++ b/wqflask/wqflask/templates/wgcna_setup.html
@@ -1,49 +1,161 @@
 {% extends "base.html" %}
 {% block title %}WCGNA analysis{% endblock %}
+{% block content %}
+<!-- Start of body -->
+<style type="text/css">
+
+#terminal {
+    margin-top: 10px;
+}
+
+</style>
+<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@4.14.1/css/xterm.css">
 
-{% block content %} <!-- Start of body -->
-<h1> WGCNA analysis parameters</h1>
 <div class="container">
-  {% if request.form['trait_list'].split(",")|length < 4 %}
-  <div class="alert alert-danger" role="alert">
-    <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
-    <span class="sr-only">Error:</span>
-    <h2>Too few phenotypes as input</h2>
-    Please make sure you select enough phenotypes / genes to perform WGCNA. Your collection needs to contain at least 4 different phenotypes. You provided {{request.form['trait_list'].split(',')|length}} phenotypes as input.
-  </div>
-  {% else %}
-  <form action="/wgcna_results" method="post" class="form-horizontal">
-    <input type="hidden" name="trait_list" id="trait_list" value= "{{request.form['trait_list']}}">
-    <div class="form-group">
-      <label for="SoftThresholds"> Soft threshold: </label>
-      <div class="col-sm-10">
-        <input type="text" class="form-inline" name="SoftThresholds" id="SoftThresholds" value="1,2,3,4,5,6,7,8,9">
-      </div>
-    </div>
-    <div class="form-group">
-      <label for="MinModuleSize"> Minimum module size: </label>
-      <div class="col-sm-10">
-        <input type="text" class="form-inline" name="MinModuleSize" id="MinModuleSize" value="30">
-      </div>
-    </div>
-    <div class="form-group">
-      <label for="TOMtype"> TOMtype: </label>
-      <div class="col-sm-10">
-        <input type="text" class="form-inline" name="TOMtype" id="TOMtype" value="unsigned">
-      </div>
-    </div>
-    <div class="form-group">
-      <label for="mergeCutHeight"> mergeCutHeight: </label>
-      <div class="col-sm-10">
-        <input type="text" class="form-inline" name="mergeCutHeight" id="mergeCutHeight" value="0.25">
-      </div>
+    <div class="col-md-5">
+        <h1 class="mx-3 my-2 "> WGCNA analysis parameters</h1>
+        {% if request.form['trait_list'].split(",")|length < 4 %} <div class="alert alert-danger" role="alert">
+            <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
+            <span class="sr-only">Error:</span>
+            <h2>Too few phenotypes as input</h2>
+            Please make sure you select enough phenotypes / genes to perform WGCNA. Your collection needs to contain at least 4 different phenotypes. You provided {{request.form['trait_list'].split(',')|length}} phenotypes as input.
     </div>
-    <div class="form-group">
-      <div class="col-sm-10">
-        <input type="submit" class="btn btn-primary" value="Run WGCNA using these settings" />
-      </div>
-    </div>
-  </form>
-  {% endif %}
+    {% else %}
+    <form class="col-md-12" action="/wgcna_results" method="post" class="form-horizontal" id="wgcna_form">
+        <input type="hidden" name="trait_list" id="trait_list" value="{{request.form['trait_list']}}">
+        <div class="form-group row ">
+            <label for="SoftThresholds" class="col-md-3 col-form-label col-form-label-sm">Soft threshhold</label>
+            <div class="col-md-9">
+                <input type="text" class="form-control form-control-md" value="1,2,3,4,5,6,7,8,9" id="SoftThresholds" name="SoftThresholds">
+            </div>
+        </div>
+        <div class="form-group row ">
+            <label for="MinModuleSize" class="col-md-3 col-form-label col-form-label-sm">Minimum module size:</label>
+            <div class="col-md-9">
+                <input type="text" class="form-control form-control-md" id="MinModuleSize" value="30" name="MinModuleSize">
+            </div>
+        </div>
+
+        <div class="form-group row">
+          <label for="TOMtype" class="col-md-3 col-form-label col-form-label-sm">TOMtype:</label>
+          <div class="col-md-9">
+            <select class="form-control" id="TOMtype" name="TOMtype">
+            <option value="unsigned">unsigned</option>
+            <option value="signed">signed</option>
+          </select>
+          </div>
+
+        </div>
+        <div class="form-group row ">
+            <label for="mergeCutHeight" class="col-md-3 col-form-label col-form-label-sm">mergeCutHeight:</label>
+            <div class="col-md-9">
+                <input type="text" class="form-control form-control-md" id="mergeCutHeight" value="0.25" name="mergeCutHeight">
+            </div>
+        </div>
+
+        <div class="form-group row">
+          <label for="corType" class="col-md-3 col-form-label col-form-label-sm">corType:</label>
+          <div class="col-md-9">
+            <select class="form-control  col-md-9" id="corType" name="corType">
+            <option value="pearson">pearson</option>
+            <option value="bicor">bicor</option>
+          </select>
+          </div>
+
+        </div>
+        <div class="form-group">
+            <div class="text-center">
+                <input type="submit" class="btn btn-primary" value="Run WGCNA using these settings" />
+            </div>
+        </div>
+
+
+
+    </form>
+    {% endif %}
 </div>
-{% endblock %}
+<div class="col-md-7">
+        <div id="terminal" class="mt-2">
+        </div>
+</div>
+</div>
+
+<script src="https://cdn.socket.io/4.2.0/socket.io.min.js" integrity="sha384-PiBR5S00EtOj2Lto9Uu81cmoyZqR57XcOna1oAuVuIEjzj0wpqDVfD0JA9eXlRsj" crossorigin="anonymous"></script>
+
+<script src="https://cdn.jsdelivr.net/npm/xterm@4.14.1/lib/xterm.min.js"></script>
+
+<script src="https://cdn.jsdelivr.net/npm/xterm-addon-attach@0.6.0/lib/xterm-addon-attach.min.js"></script>
+
+
+<script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.5.0/lib/xterm-addon-fit.min.js"></script>
+ <script src="https://code.jquery.com/jquery-3.5.1.js"
+          integrity="sha256-QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc="
+          crossorigin="anonymous"></script>
+<script>
+// document.addEventListener('DOMContentLoaded', function() {
+let term = new Terminal({
+    cursorBlink: true,
+    lineHeight: 1.3,
+    scrollback: true,
+    macOptionIsMeta: true
+});
+
+let termDebugs = {
+    general: "Computation process to be displayed here....",
+    success: "Computation in process ......",
+    fail: "Too few phenotypes as input must be >=4"
+}
+
+const fitAddon = new FitAddon.FitAddon()
+term.loadAddon(fitAddon)
+
+term.open(document.getElementById('terminal'));
+term.setOption('theme', {
+    background: '#300a24'
+});
+term.writeln(termDebugs.general)
+
+wgcnaForm = document.querySelector("#wgcna_form")
+
+const socket = io("http://127.0.0.1:8081") //issue gn3 private
+const attachAddon = new AttachAddon.AttachAddon(socket);
+
+term.loadAddon(attachAddon);
+
+fitAddon.fit()
+term.onData((data) => {
+    term.write(data)
+})
+
+
+if (wgcnaForm) {
+} else {
+    term.writeln(termDebugs.fail)
+}
+
+socket.on("connect", () => {
+    $("#wgcna_form").append(`<input type="hidden" name="socket_id" value=${socket.id}>`);
+})
+
+socket.on("output", ({
+    data
+}) => {
+    term.writeln(data)
+})
+
+$(document).on('submit', '#wgcna_form', function(e) {
+    term.writeln(termDebugs.success)
+
+    e.preventDefault();
+    var form = $(this);
+    $.ajax({
+        type: 'POST',
+        url: '/wgcna_results',
+        data: form.serialize(),
+        success: function(data) {
+            document.write(data)
+        }
+    })
+});
+</script>
+{% endblock %}
\ No newline at end of file
diff --git a/wqflask/wqflask/views.py b/wqflask/wqflask/views.py
index 5067ca0e..b0da1f21 100644
--- a/wqflask/wqflask/views.py
+++ b/wqflask/wqflask/views.py
@@ -40,8 +40,8 @@ from gn3.db.phenotypes import Probeset
 from gn3.db.phenotypes import Publication
 from gn3.db.phenotypes import PublishXRef
 from gn3.db.phenotypes import probeset_mapping
-from gn3.db.traits import get_trait_csv_sample_data
-from gn3.db.traits import update_sample_data
+# from gn3.db.traits import get_trait_csv_sample_data
+# from gn3.db.traits import update_sample_data
 
 
 from flask import current_app
@@ -79,6 +79,7 @@ from wqflask.correlation_matrix import show_corr_matrix
 from wqflask.correlation import corr_scatter_plot
 # from wqflask.wgcna import wgcna_analysis
 # from wqflask.ctl import ctl_analysis
+from wqflask.wgcna.gn3_wgcna import run_wgcna
 from wqflask.snp_browser import snp_browser
 from wqflask.search_results import SearchResultPage
 from wqflask.export_traits import export_search_results_csv
@@ -376,6 +377,12 @@ def wcgna_setup():
     return render_template("wgcna_setup.html", **request.form)
 
 
+@app.route("/wgcna_results", methods=('POST',))
+def wcgna_results():
+    """call the gn3 api to get wgcna response data"""
+    results = run_wgcna(dict(request.form))
+    return render_template("test_wgcna_results.html", **results)
+
 @app.route("/ctl_setup", methods=('POST',))
 def ctl_setup():
     # We are going to get additional user input for the analysis
diff --git a/wqflask/wqflask/wgcna/gn3_wgcna.py b/wqflask/wqflask/wgcna/gn3_wgcna.py
new file mode 100644
index 00000000..c4cc2e7f
--- /dev/null
+++ b/wqflask/wqflask/wgcna/gn3_wgcna.py
@@ -0,0 +1,102 @@
+"""module contains code to consume gn3-wgcna api
+and process data to be rendered by datatables
+"""
+
+import requests
+from types import SimpleNamespace
+from utility.helper_functions import get_trait_db_obs
+
+
+def fetch_trait_data(requestform):
+    """fetch trait data"""
+    db_obj = SimpleNamespace()
+    get_trait_db_obs(db_obj,
+                     [trait.strip()
+                      for trait in requestform['trait_list'].split(',')])
+
+    return process_dataset(db_obj.trait_list)
+
+
+def process_dataset(trait_list):
+    """process datasets and strains"""
+
+    input_data = {}
+    traits = []
+    strains = []
+
+    # xtodo unique traits and strains
+
+    for trait in trait_list:
+        traits.append(trait[0].name)
+
+        input_data[trait[0].name] = {}
+        for strain in trait[0].data:
+            strains.append(strain)
+            input_data[trait[0].name][strain] = trait[0].data[strain].value
+        # "sample_names": list(set(strains)),
+        # "trait_names": form_traits,
+        # "trait_sample_data": form_strains,
+
+    return {
+        "input": input_data,
+        "trait_names": traits,
+        "sample_names": strains
+    }
+
+
+def process_wgcna_data(response):
+    """function for processing modeigene genes
+    for create row data for datataba"""
+    mod_eigens = response["output"]["ModEigens"]
+
+    sample_names = response["input"]["sample_names"]
+
+    mod_dataset = [[sample] for sample in sample_names]
+
+    for _, mod_values in mod_eigens.items():
+        for (index, _sample) in enumerate(sample_names):
+            mod_dataset[index].append(round(mod_values[index], 3))
+
+    return {
+        "col_names": ["sample_names", *mod_eigens.keys()],
+        "mod_dataset": mod_dataset
+    }
+
+
+def process_image(response):
+    """function to process image check if byte string is empty"""
+    image_data = response["output"]["image_data"]
+    return ({
+        "image_generated": True,
+        "image_data": image_data
+    } if image_data else {
+        "image_generated": False
+    })
+
+
+def run_wgcna(form_data):
+    """function to run wgcna"""
+
+    GN3_URL = "http://127.0.0.1:8081"
+
+    wgcna_api = f"{GN3_URL}/api/wgcna/run_wgcna"
+
+    # parse form data
+
+    trait_dataset = fetch_trait_data(form_data)
+    form_data["minModuleSize"] = int(form_data["MinModuleSize"])
+
+    response = requests.post(wgcna_api, json={
+        "sample_names": list(set(trait_dataset["sample_names"])),
+        "trait_names": trait_dataset["trait_names"],
+        "trait_sample_data": list(trait_dataset["input"].values()),
+        **form_data
+
+    }
+    ).json()
+
+    return {
+        "results": response,
+        "data": process_wgcna_data(response["data"]),
+        "image": process_image(response["data"])
+    }