about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--wqflask/wqflask/collect.py31
-rw-r--r--wqflask/wqflask/database.py12
-rw-r--r--wqflask/wqflask/partial_correlations_views.py415
-rw-r--r--wqflask/wqflask/static/new/javascript/partial_correlations.js358
-rw-r--r--wqflask/wqflask/templates/collections/view.html27
-rw-r--r--wqflask/wqflask/templates/partial_correlations.html355
-rw-r--r--wqflask/wqflask/templates/partial_correlations/pcorrs_error.html54
-rw-r--r--wqflask/wqflask/templates/partial_correlations/pcorrs_poll_results.html15
-rw-r--r--wqflask/wqflask/templates/partial_correlations/pcorrs_results_presentation.html249
-rw-r--r--wqflask/wqflask/templates/partial_correlations/pcorrs_select_operations.html146
-rw-r--r--wqflask/wqflask/templates/tool_buttons.html7
11 files changed, 762 insertions, 907 deletions
diff --git a/wqflask/wqflask/collect.py b/wqflask/wqflask/collect.py
index 3475ae5d..815bb7c1 100644
--- a/wqflask/wqflask/collect.py
+++ b/wqflask/wqflask/collect.py
@@ -189,6 +189,30 @@ def delete_collection():
     return redirect(url_for('list_collections'))
 
 
+def trait_info_str(trait):
+    """Provide a string representation for given trait"""
+    def __trait_desc(trt):
+        if trait.dataset.type == "Geno":
+            return f"Marker: {trt.name}"
+        return trt.description_display or "N/A"
+
+    def __symbol(trt):
+        return (trt.symbol or trt.abbreviation or "N/A")[:20]
+
+    def __lrs(trt):
+        return (
+            f"{float(trait.LRS_score_repr):0.3f}" if float(trait.LRS_score_repr) > 0
+            else f"{trait.LRS_score_repr}")
+
+    def __location(trt):
+        if hasattr(trt, "location_repr"):
+            return trt.location_repr
+        return None
+
+    return "{}|||{}|||{}|||{}|||{}|||{:0.3f}|||{}|||{}".format(
+        trait.name, trait.dataset.name, __trait_desc(trait), __symbol(trait),
+        __location(trait), trait.mean, __lrs(trait), trait.LRS_location_repr)
+
 @app.route("/collections/view")
 def view_collection():
     params = request.args
@@ -227,9 +251,10 @@ def view_collection():
     if "json" in params:
         return json.dumps(json_version)
     else:
-        return render_template("collections/view.html",
-                               **collection_info
-                               )
+        return render_template(
+            "collections/view.html",
+            trait_info_str=trait_info_str,
+            **collection_info)
 
 
 @app.route("/collections/change_name", methods=('POST',))
diff --git a/wqflask/wqflask/database.py b/wqflask/wqflask/database.py
index 11f8d287..e485bcf1 100644
--- a/wqflask/wqflask/database.py
+++ b/wqflask/wqflask/database.py
@@ -1,10 +1,13 @@
 # Module to initialize sqlalchemy with flask
+import MySQLdb
 
 from sqlalchemy import create_engine
 from sqlalchemy.orm import scoped_session, sessionmaker
 from sqlalchemy.ext.declarative import declarative_base
 from utility.tools import SQL_URI
 
+from flask import current_app
+
 
 engine = create_engine(SQL_URI, encoding="latin1")
 
@@ -16,3 +19,12 @@ Base.query = db_session.query_property()
 
 # Initialise the db
 Base.metadata.create_all(bind=engine)
+
+
+def database_connection():
+    """Returns a database connection"""
+    return MySQLdb.Connect(
+        db=current_app.config.get("DB_NAME"),
+        user=current_app.config.get("DB_USER"),
+        passwd=current_app.config.get("DB_PASS"),
+        host=current_app.config.get("DB_HOST"))
diff --git a/wqflask/wqflask/partial_correlations_views.py b/wqflask/wqflask/partial_correlations_views.py
index 6bc5efee..659b49e9 100644
--- a/wqflask/wqflask/partial_correlations_views.py
+++ b/wqflask/wqflask/partial_correlations_views.py
@@ -1,142 +1,31 @@
+import json
+import math
+import requests
+from functools import reduce
 from typing import Union, Tuple
 
-import MySQLdb
-from gn3.db.traits import retrieve_trait_info
-from flask import flash, request, current_app, render_template
-from gn3.computations.partial_correlations import partial_correlations_entry
+from flask import (
+    flash,
+    request,
+    url_for,
+    redirect,
+    current_app,
+    render_template)
 
 from wqflask import app
 from utility.tools import GN_SERVER_URL
+from wqflask.database import database_connection
+from gn3.db.partial_correlations import traits_info
 
-def parse_trait(trait_str: str) -> Union[dict, None]:
-    keys = (
-        "name", "dataset", "symbol", "description", "location", "mean_expr",
-        "max_lrs", "data_hmac")
-    parts = tuple(part.strip() for part in trait_str.split(":::"))
-    if len(parts) == len(keys):
-        return dict(zip(keys, parts))
-    return None
-
-def process_step_select_primary(
-        primary_trait: dict, control_traits: Tuple[dict, ...],
-        target_traits: Tuple[dict, ...],
-        traits_list: Tuple[dict, ...], corr_method: str) -> Tuple[
-            str, dict, Tuple[dict, ...], Tuple[dict, ...], Tuple[dict, ...],
-            str]:
-    if primary_trait is None:
-        flash("You must select a primary trait", "alert-danger")
-        return (
-            "select-primary", primary_trait, control_traits, target_traits,
-            traits_list, corr_method)
-
-    return (
-        "select-controls", primary_trait, control_traits, target_traits,
-        tuple(
-            trait for trait in traits_list
-            if trait["data_hmac"] != primary_trait["data_hmac"]),
-        corr_method)
-
-def process_step_select_controls(
-        primary_trait: dict, control_traits: Tuple[dict, ...],
-        target_traits: Tuple[dict, ...],
-        traits_list: Tuple[dict, ...], corr_method: str) -> Tuple[
-            str, dict, Tuple[dict, ...], Tuple[dict, ...], Tuple[dict, ...],
-            str]:
-    if len(control_traits) == 0 or len(control_traits) > 3:
-        flash(
-            ("You must select a minimum of one control trait, up to a maximum "
-             "of three control traits."),
-            "alert-danger")
-        return (
-            "select-controls", primary_trait, control_traits, target_traits,
-            traits_list, corr_method)
-
-    hmacs =(primary_trait["data_hmac"],) + tuple(
-        trait["data_hmac"] for trait in control_traits)
-    return (
-        "select-corr-method", primary_trait, control_traits, target_traits,
-        tuple(
-            trait for trait in traits_list if trait["data_hmac"] not in hmacs),
-        corr_method)
-
-def process_step_select_targets(
-        primary_trait: dict, control_traits: Tuple[dict, ...],
-        target_traits: Tuple[dict, ...],
-        traits_list: Tuple[dict, ...], corr_method: str) -> Tuple[
-            str, dict, Tuple[dict, ...], Tuple[dict, ...], Tuple[dict, ...],
-            str]:
-    if len(target_traits) == 0:
-        flash(
-            "You must select at least one target trait.", "alert-danger")
-        return (
-            "select-targets", primary_trait, control_traits, target_traits,
-            traits_list, corr_method)
-
-    hmacs =(primary_trait["data_hmac"],) + tuple(
-        trait["data_hmac"] for trait in (control_traits + target_traits))
-    return (
-        "select-corr-method", primary_trait, control_traits, target_traits,
-        tuple(
-            trait for trait in traits_list if trait["data_hmac"] not in hmacs),
-        corr_method)
-
-def process_step_select_corr_method(
-        primary_trait: dict, control_traits: Tuple[dict, ...],
-        target_traits: Tuple[dict, ...],
-        traits_list: Tuple[dict, ...], corr_method: str) -> Tuple[
-            str, dict, Tuple[dict, ...], Tuple[dict, ...], Tuple[dict, ...],
-            str]:
-    methods = (
-        "genetic correlation, pearson's r",
-        "genetic correlation, spearman's rho",
-        "sgo literature correlation",
-        "tissue correlation, pearson's r",
-        "tissue correlation, spearman's rho")
-    if corr_method.lower() not in methods:
-        flash(
-            "Selected method is unknown.", "alert-danger")
-        return (
-            "select-corr-method", primary_trait, control_traits, target_traits,
-            traits_list, corr_method)
-
-    hmacs =(primary_trait["data_hmac"],) + tuple(
-        trait["data_hmac"] for trait in (control_traits + target_traits))
-    return (
-        "run-correlation", primary_trait, control_traits, target_traits,
-        tuple(
-            trait for trait in traits_list if trait["data_hmac"] not in hmacs),
-        corr_method)
-
-def process_step(
-        step: str, primary_trait: dict, control_traits: Tuple[dict, ...],
-        target_traits: Tuple[dict, ...], traits_list: Tuple[dict, ...],
-        corr_method: str) -> Tuple[
-            str, dict, Tuple[dict, ...], Tuple[dict, ...], Tuple[dict, ...],
-            str]:
-    processor_functions = {
-        # "select-traits": lambda arg: arg,
-        "select-primary": process_step_select_primary,
-        "select-controls": process_step_select_controls,
-        "select-targets": process_step_select_targets,
-        "select-corr-method": process_step_select_corr_method
-    }
-    return processor_functions[(step or "select-primary")](
-        primary_trait, control_traits, target_traits, traits_list, corr_method)
-
-def sequence_of_traits(trait_strs) -> Tuple[dict, ...]:
-    return tuple(filter(
-        lambda trt: trt is not None,
-        (parse_trait(tstr.strip()) for tstr in trait_strs)))
-
-def publish_target_dabases(conn, group, threshold):
+def publish_target_databases(conn, groups, threshold):
     query = (
         "SELECT PublishFreeze.FullName,PublishFreeze.Name "
         "FROM PublishFreeze, InbredSet "
         "WHERE PublishFreeze.InbredSetId = InbredSet.Id "
-        "AND InbredSet.Name = %(group)s "
-        "AND PublishFreeze.public > %(threshold)s")
+        f"AND InbredSet.Name IN ({', '.join(['%s'] * len(groups))}) "
+        "AND PublishFreeze.public > %s")
     with conn.cursor() as cursor:
-        cursor.execute(query, {"group": group, "threshold": threshold})
+        cursor.execute(query, tuple(groups) + (threshold,))
         res = cursor.fetchall()
         if res:
             return tuple(
@@ -144,15 +33,15 @@ def publish_target_dabases(conn, group, threshold):
 
     return tuple()
 
-def geno_target_databases(conn, group, threshold):
+def geno_target_databases(conn, groups, threshold):
     query = (
         "SELECT GenoFreeze.FullName,GenoFreeze.Name "
         "FROM GenoFreeze, InbredSet "
         "WHERE GenoFreeze.InbredSetId = InbredSet.Id "
-        "AND InbredSet.Name = %(group)s "
-        "AND GenoFreeze.public > %(threshold)s")
+        f"AND InbredSet.Name IN ({', '.join(['%s'] * len(groups))}) "
+        "AND GenoFreeze.public > %s")
     with conn.cursor() as cursor:
-        cursor.execute(query, {"group": group, "threshold": threshold})
+        cursor.execute(query, tuple(groups) + (threshold,))
         res = cursor.fetchall()
         if res:
             return tuple(
@@ -160,27 +49,26 @@ def geno_target_databases(conn, group, threshold):
 
     return tuple()
 
-def probeset_target_databases(conn, group, threshold):
+def probeset_target_databases(conn, groups, threshold):
     query1 = "SELECT Id, Name FROM Tissue order by Name"
-    query2 = (
-        "SELECT ProbeFreeze.TissueId, ProbeSetFreeze.FullName, ProbeSetFreeze.Name "
-        "FROM ProbeSetFreeze, ProbeFreeze, InbredSet "
-        "WHERE ProbeSetFreeze.ProbeFreezeId = ProbeFreeze.Id "
-        "AND ProbeFreeze.TissueId IN %(tissue_ids)s "
-        "AND ProbeSetFreeze.public > %(threshold)s "
-        "AND ProbeFreeze.InbredSetId = InbredSet.Id "
-        "AND InbredSet.Name like %(group)s "
-        "ORDER BY ProbeSetFreeze.CreateTime desc, ProbeSetFreeze.AvgId")
     with conn.cursor() as cursor:
         cursor.execute(query1)
         tissue_res = cursor.fetchall()
         if tissue_res:
             tissue_ids = tuple(row[0] for row in tissue_res)
-            cursor.execute(
-                query2,{
-                    "tissue_ids": tissue_ids, "group": f"{group}%%",
-                    "threshold": threshold
-                })
+            groups_clauses = ["InbredSet.Name like %s"] * len(groups)
+            query2 = (
+                "SELECT ProbeFreeze.TissueId, ProbeSetFreeze.FullName, "
+                "ProbeSetFreeze.Name "
+                "FROM ProbeSetFreeze, ProbeFreeze, InbredSet "
+                "WHERE ProbeSetFreeze.ProbeFreezeId = ProbeFreeze.Id "
+                "AND ProbeFreeze.TissueId IN "
+                f"({', '.join(['%s'] * len(tissue_ids))}) "
+                "AND ProbeSetFreeze.public > %s "
+                "AND ProbeFreeze.InbredSetId = InbredSet.Id "
+                f"AND ({' OR '.join(groups_clauses)}) "
+                "ORDER BY ProbeSetFreeze.CreateTime desc, ProbeSetFreeze.AvgId")
+            cursor.execute(query2, tissue_ids + (threshold,) + tuple(groups))
             db_res = cursor.fetchall()
             if db_res:
                 databases = tuple(
@@ -197,70 +85,201 @@ def probeset_target_databases(conn, group, threshold):
 
     return tuple()
 
-def target_databases(conn, step, trait, threshold):
+def target_databases(conn, traits, threshold):
     """
     Retrieves the names of possible target databases from the database.
     """
-    if step != "select-corr-method":
-        return None
-
-    trait_info = retrieve_trait_info(
-        threshold, f"{trait['dataset']}::{trait['name']}", conn)
-    group = trait_info["group"]
+    trait_info = traits_info(
+        conn, threshold,
+        tuple(f"{trait['dataset']}::{trait['trait_name']}" for trait in traits))
+    groups = tuple(set(row["db"]["group"] for row in trait_info))
     return (
-        publish_target_dabases(conn, group, threshold) +
-        geno_target_databases(conn, group, threshold) +
-        probeset_target_databases(conn, group, threshold))
+        publish_target_databases(conn, groups, threshold) +
+        geno_target_databases(conn, groups, threshold) +
+        probeset_target_databases(conn, groups, threshold))
+
+def primary_error(args):
+    if len(args["primary_trait"]) == 0 or len(args["primary_trait"]) > 1:
+        return {
+            **args,
+            "errors": (args.get("errors", tuple()) +
+                       ("You must provide one, and only one primary trait",))}
+    return args
+
+def controls_error(args):
+    if len(args["control_traits"]) == 0 or len(args["control_traits"]) > 3:
+        return {
+            **args,
+            "errors": (
+                args.get("errors", tuple()) +
+                (("You must provide at least one control trait, and a maximum "
+                  "of three control traits"),))}
+    return args
+
+def target_db_error(args):
+    if not args["target_db"]:
+        return {
+            **args,
+            "errors": (
+                args.get("errors", tuple()) +
+                ("The target database must be provided",))}
+    return args
+
+def method_error(args):
+    methods = (
+        "genetic correlation, pearson's r",
+        "genetic correlation, spearman's rho",
+        "sgo literature correlation",
+        "tissue correlation, pearson's r",
+        "tissue correlation, spearman's rho")
+    if not args["method"] or args["method"].lower() not in methods:
+        return {
+            **args,
+            "errors": (
+                args.get("errors", tuple()) +
+                ("Invalid correlation method provided",))}
+    return args
 
-def pcorrelations(conn, values):
-    if values["step"] != "run-correlation":
-        return None
+def criteria_error(args):
+    try:
+        int(args.get("criteria", "invalid"))
+        return args
+    except ValueError:
+        return {
+            **args,
+            "errors": (
+                args.get("errors", tuple()) +
+                ("Invalid return number provided",))}
 
-    def trait_fullname(trait):
-        return f"{trait['dataset']}::{trait['name']}"
+def errors(args):
+    return criteria_error(method_error(target_db_error(controls_error(
+        primary_error(args)))))
 
-    return partial_correlations_entry(
-        conn, trait_fullname(values["primary_trait"]),
-        tuple(trait_fullname(trait) for trait in values["control_traits"]),
-        values["method"], values["criteria"], values["target_db"])
+def __classify_args(acc, item):
+    if item[1].startswith("primary_"):
+        return {
+            **acc,
+            "primary_trait": (acc.get("primary_trait", tuple()) + (item,))} 
+    if item[1].startswith("controls_"):
+        return {**acc, "control_traits": (acc.get("control_traits", tuple()) + (item,))}
+    if item[0] == "target_db":
+        return {**acc, "target_db": item[1]}
+    if item[0] == "method":
+        return {**acc, "method": item[1]}
+    if item[0] == "criteria":
+        return {**acc, "criteria": item[1]}
+    return acc
+
+def __build_args(raw_form, traits):
+    args = reduce(__classify_args, raw_form.items(), {})
+    return {
+        **args,
+        "primary_trait": [
+            item for item in traits if item["trait_name"] in
+            (name[1][8:] for name in args["primary_trait"])],
+        "control_traits": [
+            item for item in traits if item["trait_name"] in
+            (name[1][9:] for name in args["control_traits"])]
+    }
+
+def parse_trait(trait_str):
+    return dict(zip(
+        ("trait_name", "dataset", "description", "symbol", "location", "mean",
+         "lrs", "lrs_location"),
+        trait_str.strip().split("|||")))
+
+def response_error_message(response):
+    error_messages = {
+        404: ("We could not connect to the API server at this time. "
+              "Try again later."),
+        500: ("The API server experienced a problem. We will be working on a "
+              "fix. Please try again later.")
+    }
+    return error_messages.get(
+        response.status_code,
+        "General API server error!!")
+
+def render_error(error_message):
+    return render_template(
+        "partial_correlations/pcorrs_error.html",
+        message = error_message)
+
+def handle_200_response(response):
+    if response["status"] == "success":
+        return redirect(
+            url_for(
+                "poll_partial_correlation_results",
+                command_id=response["results"]),
+            code=303)
+    return render_error(response["results"])
+
+def handle_response(response):
+    if response.status_code != 200:
+        return render_template(
+            "partial_correlations/pcorrs_error.html",
+            message = response_error_message(response))
+    return handle_200_response(response.json())
 
 @app.route("/partial_correlations", methods=["POST"])
 def partial_correlations():
     form = request.form
-    traits_list = tuple(filter(
-        lambda trt: trt is not None,
-        (parse_trait(tstr) for tstr in form.get("traits_list", "").split("|||"))))
+    traits = tuple(
+        parse_trait(trait) for trait in
+        form.get("trait_list").split(";;;"))
 
-    args_dict = dict(zip(
-        ("step", "primary_trait", "control_traits", "target_traits",
-         "traits_list", "method"),
-        process_step(
-            form.get("step", None),
-            parse_trait(form.get("primary_trait", "")),
-            sequence_of_traits(
-                form.getlist("control_traits[]") or
-                form.get("control_traits", "").split("|||")),
-            sequence_of_traits(
-                form.getlist("target_traits[]") or
-                form.get("target_traits", "").split("|||")),
-            sequence_of_traits(form.get("traits_list", "").split("|||")),
-            form.get("method"))))
+    if form.get("submit") == "Run Partial Correlations":
+        args = errors(__build_args(form, traits))
+        if len(args.get("errors", [])) == 0:
+            post_data = {
+                **args,
+                "primary_trait": args["primary_trait"][0]
+            }
+            return handle_response(requests.post(
+                url=f"{GN_SERVER_URL}api/correlation/partial",
+                json=json.dumps(post_data)))
 
-    conn = MySQLdb.Connect(
-        db=current_app.config.get("DB_NAME"),
-        user=current_app.config.get("DB_USER"),
-        passwd=current_app.config.get("DB_PASS"),
-        host=current_app.config.get("DB_HOST"))
-    target_dbs = target_databases(
-        conn, args_dict["step"], args_dict["primary_trait"], 0)
+        for error in args["errors"]:
+            flash(error, "alert-danger")
 
-    if args_dict["step"] == "run-correlation":
-        args_dict = {
-            **args_dict, "target_db": form.get("target_db"),
-            "criteria": int(form.get("criteria", 500))}
+    with database_connection() as conn:
+        target_dbs = target_databases(conn, traits, threshold=0)
+        return render_template(
+            "partial_correlations/pcorrs_select_operations.html",
+            trait_list_str=form.get("trait_list"),
+            traits=traits,
+            target_dbs=target_dbs)
 
-    corr_results = pcorrelations(conn, args_dict)
+def process_pcorrs_command_output(result):
+    if result["status"] == "success":
+        def __format_number(num):
+            if num is None or math.isnan(num):
+                return ""
+            if abs(num) <= 1.04E-4:
+                return f"{num:.2e}"
+            return f"{num:.5f}"
 
-    return render_template(
-        "partial_correlations.html", **args_dict, target_dbs=target_dbs,
-        corr_results=corr_results, part_corr_url=f"{GN_SERVER_URL}api/correlation/partial")
+        return render_template(
+            "partial_correlations/pcorrs_results_presentation.html",
+            primary=result["results"]["primary_trait"],
+            controls=result["results"]["control_traits"],
+            correlations=result["results"]["correlations"],
+            dataset_type=result["results"]["dataset_type"],
+            method=result["results"]["method"],
+            format_number=__format_number)
+    if result["status"] == "error":
+        return render_error(
+            "The partial correlations computation failed with an error")
+
+@app.route("/partial_correlations/<command_id>", methods=["GET"])
+def poll_partial_correlation_results(command_id):
+    response = requests.get(
+        url=f"{GN_SERVER_URL}api/async_commands/state/{command_id}")
+    if response.status_code == 200:
+        data = response.json()
+        if data["status"] == "error":
+            return render_error(response["result"])
+        if data["status"] == "success":
+            return process_pcorrs_command_output(json.loads(data["result"]))
+        return render_template(
+            "partial_correlations/pcorrs_poll_results.html",
+            command_id = command_id)
diff --git a/wqflask/wqflask/static/new/javascript/partial_correlations.js b/wqflask/wqflask/static/new/javascript/partial_correlations.js
index 3bf2acc4..5de1204c 100644
--- a/wqflask/wqflask/static/new/javascript/partial_correlations.js
+++ b/wqflask/wqflask/static/new/javascript/partial_correlations.js
@@ -1,336 +1,26 @@
-/**
- * This is, hopefully, a short-term stop-gap measure to get the system working
- * and to get some feedback, even as better optimisation is done in the
- * background to get better response/performance for the partial correlation
- * computations
- */
-
-function key_value(keys, values) {
-    if(!(keys.length == values.length)) {
-	Error("The 'keys' and 'values' objects MUST be the same length");
-	return null;
-    }
-    return values.reduce(function(accumulator, item, index) {
-	accumulator[keys[index]] = item;
-	return accumulator;
-    }, {});
-}
-
-function trait(trait_str) {
-    return key_value(
-	["name", "dataset", "symbol", "description", "location", "mean_expr",
-	 "max_lrs", "data_hmac"],
-	trait_str.split(":::"));
-}
-
-function primary_trait() {
-    trait_string = document.querySelector(
-	"#partial-correlations-form input[name=primary_trait]").value;
-    return trait(trait_string);
-}
-
-function control_traits() {
-    return document.querySelector(
-	"#partial-correlations-form input[name=control_traits]").value.split(
-	    "|||").map(trait).filter(trait => !(trait === null));
-}
-
-function correlation_method() {
-    return document.querySelector(
-	"#partial-correlations-form select[name=method]").value;
-}
-
-function criteria() {
-    return document.querySelector(
-	"#partial-correlations-form select[name=criteria]").value;
-}
-
-function target_db() {
-    return document.querySelector(
-	"#partial-correlations-form select[name=target_db]").value;
-}
-
-function partial_corr_request_data() {
-    return {
-	"primary_trait": primary_trait(),
-	"control_traits": control_traits(),
-	"method": correlation_method(),
-	"criteria": criteria(),
-	"target_db": target_db()
-    }
-}
-
-function rho_or_r(method) {
-    if (method === "spearman") {
-	return "rho";
-    }
-    return "r";
-}
-
-function format_number(num) {
-    if(num === null) {
-	return NaN;
-    }
-    if(Math.abs(num) <= 1.04e-4) {
-	return num.toExponential(2);
-    }
-    return num.toFixed(5);
-}
-
-function display_publish_results(primary, controls, correlations, method) {
-    table = document.getElementById("part-corr-results-publish");
-    table.setAttribute("style", "display: block;");
-    table_body = document.querySelector("#part-corr-results-publish tbody");
-    template_row = document.querySelector(
-	"#part-corr-results-publish tr.template-publish-results-row");
-    correlations.forEach(function(item, index, arr) {
-	new_row = template_row.cloneNode(true);
-	new_row.setAttribute("class", "results-row");
-	new_row.querySelector(
-	    'td[data-column-heading="Record"]').innerHTML = item["trait_name"];
-	new_row.querySelector(
-	    'td[data-column-heading="Phenotype"]').innerHTML = (
-	    item["post_publication_description"]);
-	new_row.querySelector(
-	    'td[data-column-heading="Authors"]').innerHTML = item["authors"];
-	new_row.querySelector(
-	    'td[data-column-heading="Year"]').innerHTML = item["year"];
-	new_row.querySelector(
-	    'td[data-column-heading="N"]').innerHTML = item["noverlap"];
-	new_row.querySelector(
-	    `td[data-column-heading="Partial ${rho_or_r(method)}"]`
-	).innerHTML = format_number(item["partial_corr"]);
-	new_row.querySelector(
-	    `td[data-column-heading="p(partial ${rho_or_r(method)})"]`
-	).innerHTML = format_number(item["partial_corr_p_value"]);
-	new_row.querySelector(
-	    `td[data-column-heading="${rho_or_r(method)}"]`
-	).innerHTML = format_number(item["corr"]);
-	new_row.querySelector(
-	    `td[data-column-heading="p(${rho_or_r(method)})"]`
-	).innerHTML = format_number(item["corr_p_value"]);
-	new_row.querySelector(
-	    `td[data-column-heading="delta ${rho_or_r(method)}"]`
-	).innerHTML = format_number(item["delta"]);
-	table_body.appendChild(new_row);
-    });
-    table_body.removeChild(template_row);
-}
-
-function display_geno_results(primary, controls, correlations, method) {
-    table = document.getElementById("part-corr-results-geno");
-    table.setAttribute("style", "display: block;");
-    table_body = document.querySelector("#part-corr-results-geno tbody");
-    template_row = document.querySelector(
-	"#part-corr-results-geno tr.template-geno-results-row");
-    correlations.forEach(function(item, index, arr) {
-	new_row = template_row.cloneNode(true);
-	new_row.setAttribute("class", "results-row");
-	new_row.querySelector(
-	    'td[data-column-heading="Locus"]').innerHTML = item["trait_name"];
-	new_row.querySelector(
-	    'td[data-column-heading="Chr"]').innerHTML = item["chr"];
-	new_row.querySelector(
-	    'td[data-column-heading="Megabase"]').innerHTML = item["mb"];
-	new_row.querySelector(
-	    'td[data-column-heading="N"]').innerHTML = item["noverlap"];
-	new_row.querySelector(
-	    `td[data-column-heading="Partial ${rho_or_r(method)}"]`
-	).innerHTML = format_number(item["partial_corr"]);
-	new_row.querySelector(
-	    `td[data-column-heading="p(partial ${rho_or_r(method)})"]`
-	).innerHTML = format_number(item["partial_corr_p_value"]);
-	new_row.querySelector(
-	    `td[data-column-heading="${rho_or_r(method)}"]`
-	).innerHTML = format_number(item["corr"]);
-	new_row.querySelector(
-	    `td[data-column-heading="p(${rho_or_r(method)})"]`
-	).innerHTML = format_number(item["corr_p_value"]);
-	new_row.querySelector(
-	    `td[data-column-heading="delta ${rho_or_r(method)}"]`
-	).innerHTML = format_number(item["delta"]);
-	table_body.appendChild(new_row);
-    });
-    table_body.removeChild(template_row);
-}
-
-function display_probeset_results(primary, controls, correlations, method) {
-    table = document.getElementById("part-corr-results-probeset");
-    table.setAttribute("style", "display: block;");
-    table_body = document.querySelector("#part-corr-results-probeset tbody");
-    template_row = document.querySelector(
-	"#part-corr-results-probeset tr.template-probeset-results-row");
-    correlations.forEach(function(item, index, arr) {
-	new_row = template_row.cloneNode(true);
-	new_row.setAttribute("class", "results-row");
-	new_row.querySelector(
-	    'td[data-column-heading="Record"]').innerHTML = item["trait_name"];
-	new_row.querySelector(
-	    'td[data-column-heading="Gene ID"]').innerHTML = item["geneid"];
-	new_row.querySelector(
-	    'td[data-column-heading="Homologene ID"]').innerHTML = item["homologeneid"];
-	new_row.querySelector(
-	    'td[data-column-heading="Symbol"]').innerHTML = item["symbol"];
-	new_row.querySelector(
-	    'td[data-column-heading="Description"]').innerHTML = item["description"];
-	new_row.querySelector(
-	    'td[data-column-heading="Chr"]').innerHTML = item["chr"];
-	new_row.querySelector(
-	    'td[data-column-heading="Megabase"]').innerHTML = item["mb"];
-	new_row.querySelector(
-	    'td[data-column-heading="Mean Expr"]').innerHTML = item["mean_expr"];
-	new_row.querySelector(
-	    'td[data-column-heading="N"]').innerHTML = item["noverlap"];
-	new_row.querySelector(
-	    `td[data-column-heading="Sample Partial ${rho_or_r(method)}"]`
-	).innerHTML = format_number(item["partial_corr"] || NaN);
-	new_row.querySelector(
-	    `td[data-column-heading="Sample p(partial ${rho_or_r(method)})"]`
-	).innerHTML = format_number(item["partial_corr_p_value"] || NaN);
-	new_row.querySelector(
-	    `td[data-column-heading="Sample ${rho_or_r(method)}"]`
-	).innerHTML = format_number(item["corr"] || NaN);
-	new_row.querySelector(
-	    `td[data-column-heading="Sample p(${rho_or_r(method)})"]`
-	).innerHTML = format_number(item["corr_p_value"] || NaN);
-	new_row.querySelector(
-	    `td[data-column-heading="delta ${rho_or_r(method)}"]`
-	).innerHTML = format_number(item["delta"] || NaN);
-	new_row.querySelector(
-	    `td[data-column-heading="Lit Corr"]`
-	).innerHTML = format_number(item["l_corr"] || NaN);
-	new_row.querySelector(
-	    `td[data-column-heading="Tissue ${rho_or_r(method)}"]`
-	).innerHTML = format_number(item["tissue_corr"] || NaN);
-	new_row.querySelector(
-	    `td[data-column-heading="Tissue p(${rho_or_r(method)})"]`
-	).innerHTML = format_number(item["tissue_p_value"] || NaN);
-	table_body.appendChild(new_row);
-    });
-    template_row.setAttribute("display", "none");
-    /*table_body.removeChild(template_row);*/
-}
-
-function replace_r_with_rho(method) {
-    /* Mostly utility: Replace `r` with `rho` in the appropriate places */
-    pattern = /\br\b/;
-    if(method == "spearman") {
-        results_div = document.getElementById("partial-correlation-results");
-	headers = results_div.getElementsByTagName("th");
-	for(let header of headers) {
-	    header.innerHTML = header.innerHTML.replace(pattern, "rho");
-	}
-
-	cells = results_div.getElementsByTagName("td");
-	for(let cell of cells) {
-	    cell.setAttribute(
-		"data-column-heading",
-		cell.getAttribute(
-		    "data-column-heading").replace(pattern, "rho"));
-	}
+function selected_traits() {
+    traits = $("#trait_table input:checked").map(function() {
+      return $(this).attr("data-trait-info");
+    }).get();
+    if (traits.length == 0){
+      num_traits = $("#trait_table input").length
+      if (num_traits <= 100){
+        traits = $("#trait_table input").map(function() {
+          return $(this).attr("data-trait-info");
+        }).get();
+      }
     }
-}
-
-function display_partial_corr_results(data, status, xhr) {
-    progress_indicator = document.getElementById(
-	"partial-correlations-progress-indicator").style.display = "none";
-    console.log(data);
-
-    replace_r_with_rho(data["results"]["method"]);
-
-    display_functions = {
-	"Publish": display_publish_results,
-	"Geno": display_geno_results,
-	"ProbeSet": display_probeset_results
-    }
-
-    display_functions[data["results"]["dataset_type"]](
-	data["results"]["primary_traits"],
-	data["results"]["control_traits"],
-	data["results"]["correlations"],
-	data["results"]["method"]);
-
-	initializePcorrTable(data["results"]["dataset_type"]);
-}
-
-function display_partial_corr_error(xhr, status, error) {
-    document.getElementById(
-	"partial-correlations-progress-indicator").style.display = "none";
-    error_element = document.getElementById("part-corr-error");
-    panel = document.createElement("div");
-    panel.setAttribute("class", "panel panel-danger");
-    error_element.appendChild(panel);
-
-    panel_header = document.createElement("div");
-    panel_header.setAttribute("class", "panel-heading");
-    panel_header.textContent = "Error: " + xhr.status;
-    panel.appendChild(panel_header);
-
-    panel_body = document.createElement("div");
-    panel_body.setAttribute("class", "panel-body");
-    panel_body.textContent = xhr.statusText;
-    panel.appendChild(panel_body);
-    console.log(xhr)
-}
-
-function send_data_and_process_results(
-    remote_url, request_data, success_fn, error_fn, indicator_id) {
-    document.getElementById(indicator_id).style.display = "block";
-    $.ajax({
-	type: "POST",
-	url: remote_url,
-	contentType: "application/json",
-	data: JSON.stringify(request_data),
-	dataType: "JSON",
-	success: success_fn,
-	error: error_fn
-    });
-}
-
-function initializePcorrTable(dataType){
-	tableId = "part-corr-results-" + dataType.toLowerCase();
-	if (dataType == "Publish") {
-		orderCol = 8;
-	} else if (dataType == "ProbeSet") {
-		orderCol = 12;
-	} else {
-		orderCol = 7;
-	}
-
-	$('#' + tableId).dataTable( {
-		'drawCallback': function( settings ) {
-			  $('#' + tableId + ' tr').off().on("click", function(event) {
-				if (event.target.type !== 'checkbox' && event.target.tagName.toLowerCase() !== 'a') {
-				  var obj =$(this).find('input');
-				  obj.prop('checked', !obj.is(':checked'));
-				}
-				if ($(this).hasClass("selected") && event.target.tagName.toLowerCase() !== 'a'){
-				  $(this).removeClass("selected")
-				} else if (event.target.tagName.toLowerCase() !== 'a') {
-				  $(this).addClass("selected")
-				}
-			  });
-		},
-		"order": [[orderCol, "asc" ]],
-		"sDom": "itir",
-		"iDisplayLength": -1,
-		"autoWidth": false,
-		"bDeferRender": true,
-		"bSortClasses": false,
-		"paging": false
-	} );
-}
-
-$("#partial-correlations-form").submit(function(e) {
-    e.preventDefault();
-});
-
-$("#run-partial-corr-button").click(function(evt) {
-    send_data_and_process_results(
-	document.getElementById(
-	    "run-partial-corr-button").getAttribute("data-url"),
-	partial_corr_request_data(),
-	display_partial_corr_results,
-	display_partial_corr_error,
-	"partial-correlations-progress-indicator");
+    return traits
+}
+
+$("#partial-correlations").on("click", function() {
+    // Submit the form to the `partial_correlations` endpoint
+    url = $(this).data("url")
+    traits = selected_traits();
+    $("#trait_list").val(traits.reduce(function(acc, str) {
+	return acc.concat(";;;".concat(str));
+    }));
+    $("input[name=tool_used]").val("Partial Correlation")
+    $("input[name=form_url]").val(url)
+    return submit_special(url)
 })
diff --git a/wqflask/wqflask/templates/collections/view.html b/wqflask/wqflask/templates/collections/view.html
index f896471a..6f1a9680 100644
--- a/wqflask/wqflask/templates/collections/view.html
+++ b/wqflask/wqflask/templates/collections/view.html
@@ -67,22 +67,6 @@
                 </div>
             </form>
             &nbsp;
-            <form id="partial-correlations-form"
-                method="POST"
-                action="{{url_for('partial_correlations')}}">
-            <input type="hidden"
-                id="collection_uuid"
-                value="{{uc.uc_id}}" />
-            <input type="hidden"
-                name="traits_list"
-                value="{% for this_trait in trait_obs %}{{ this_trait.name }}:::{{ this_trait.dataset.name }}:::{{this_trait.symbol}}:::{{this_trait.description_display}}:::{{this_trait.location_repr}}:::{{this_trait.mean}}:::{{this_trait['LRS_location_repr']}}:::{{data_hmac('{}:{}'.format(this_trait.name, this_trait.dataset.name))}}|||{% endfor %}" />
-            <button id="run-partial-correlations"
-                class="btn btn-primary"
-                title="Run partial correlations"
-                type="submit" style="margin-top: 10px;">
-                Partial Correlations
-            </button>
-            </form>
         </div>
 
         <div>
@@ -128,7 +112,13 @@
                     <tbody>
                     {% for this_trait in trait_obs %}
                         <TR id="trait:{{ this_trait.name }}:{{ this_trait.dataset.name }}">
-                            <TD align="center" style="padding: 0px;"><INPUT TYPE="checkbox" NAME="searchResult" class="checkbox trait_checkbox" VALUE="{{ data_hmac('{}:{}'.format(this_trait.name, this_trait.dataset.name)) }}"></TD>
+                          <TD align="center" style="padding: 0px;">
+			    <input type="checkbox"
+				   name="searchResult"
+				   class="checkbox trait_checkbox"
+				   value="{{data_hmac('{}:{}'.format(this_trait.name, this_trait.dataset.name))}}"
+				   data-trait-info="{{trait_info_str(this_trait)}}">
+			  </TD>
                             <TD data-export="{{ loop.index }}" align="right">{{ loop.index }}</TD>
                             <TD title="{{ this_trait.dataset.fullname }}" data-export="{{ this_trait.dataset.fullname }}">{{ this_trait.dataset.fullname }}</TD>
                             <TD data-export="{{ this_trait.name }}">
@@ -193,6 +183,9 @@
 
     <script type="text/javascript" src="{{ url_for('js', filename='plotly/plotly.min.js') }}"></script>
 
+    <script type="text/javascript"
+	    src="/static/new/javascript/partial_correlations.js"></script>
+
 
     <script language="javascript" type="text/javascript">
         $(document).ready( function () {
diff --git a/wqflask/wqflask/templates/partial_correlations.html b/wqflask/wqflask/templates/partial_correlations.html
deleted file mode 100644
index 0cf09afc..00000000
--- a/wqflask/wqflask/templates/partial_correlations.html
+++ /dev/null
@@ -1,355 +0,0 @@
-{%extends "base.html"%}
-
-{%block title%}Partial Correlations:{%endblock%}
-
-{%block css%}
-<link rel="stylesheet" type="text/css" href="/static/new/css/partial_correlations.css" />
-<link rel="stylesheet" type="text/css" href="{{ url_for('css', filename='DataTables/css/jquery.dataTables.css') }}" />
-<link rel="stylesheet" type="text/css" href="/static/new/css/trait_list.css" />
-<link rel="stylesheet" type="text/css" href="/static/new/css/show_trait.css" />
-{%endblock%}
-
-{%block content%}
-<div class="container">
-<form id="partial-correlations-form"
-      method="POST"
-      action="{{url_for('partial_correlations')}}">
-
-  <div id="main-form">
-    {%with messages = get_flashed_messages(with_categories=true)%}
-    {%if messages:%}
-    <ul class=flashes>
-      {%for category, message in messages:%}
-      <li class="{{category}}">{{message}}</li>
-      {%endfor%}
-    </ul>
-    {%endif%}
-    {%endwith%}
-
-    {%if step == "select-primary":%}
-    <p>Please select the primary trait (X)</p>
-    {%include "with-trait-items.html" %}
-
-    <button type="submit" class="btn btn-primary">
-      Next: Select Control Traits
-    </button>
-    {%endif%}
-
-
-
-    {%if step == "select-controls":%}
-
-    <p>Select a maximum of three (3) control traits (Z)</p>
-    {%include "with-trait-items.html" %}
-
-    <button type="submit" class="btn btn-primary">
-      Next: Select Target
-    </button>
-    {%endif%}
-
-
-
-    {%if step == "select-targets":%}
-    <p>Select at least one target trait (Y)</p>
-    {%for trait in traits_list:%}
-    <div class="label-element-combo">
-      <input type="checkbox"
-	     name="target_traits[]"
-	     value="{{trait['name']}}:::{{trait['dataset']}}:::{{trait['symbol']}}:::{{trait['description']}}:::{{trait['data_hmac']}}"
-	     checked="checked"
-	     id="trait_{{trait['data_hmac']}}" />
-      <label for="trait_{{trait['data_hmac']}}">
-	{{trait["name"]}} - {{trait["symbol"]}} - {{trait["description"]}}
-      </label>
-    </div>
-    {%endfor%}
-    <button type="submit" class="btn btn-primary">
-      Next: Select Correlation Method
-    </button>
-    {%endif%}
-
-
-
-    {%if step == "select-corr-method":%}
-    <div class="label-element-combo">
-      <label for="target-db-input">Choose Database</label>
-      <select id="target-db-input" required="required" name="target_db">
-	{%if target_dbs:%}
-	{%for item in target_dbs:%}
-	{%if "description" in item.keys():%}
-        <option value="{{item['value']}}">{{item['description']}}</option>
-	{%else:%}
-	{%for group, opts in item.items()%}
-	{%if opts | length > 0:%}
-	<optgroup label="{{group}} ------">
-	  {%for item2 in opts:%}
-	  <option value="{{item2['value']}}">{{item2['description']}}</option>
-	  {%endfor%}
-	</optgroup>
-	{%endif%}
-	{%endfor%}
-	{%endif%}
-	{%endfor%}
-	{%endif%}
-      </select>
-    </div>
-
-    <div class="label-element-combo">
-      <label for="corr-method-input">Compute</label>
-      <select id="corr-method-input" required="required" name="method">
-	<option value="Genetic Correlation, Pearson's r">
-	  Genetic Correlation, Pearson's r</option>
-	<option value="Genetic Correlation, Spearman's rho">
-	  Genetic Correlation, Spearman's rho</option>
-	<option value="SGO Literature Correlation">
-	  SGO Literature Correlation</option>
-	<option value="Tissue Correlation, Pearson's r">
-	  Tissue Correlation, Pearson's r</option>
-	<option value="Tissue Correlation, Spearman's rho">
-	  Tissue Correlation, Spearman's rho</option>
-      </select>
-    </div>
-
-    <div class="label-element-combo">
-      <label for="criteria-input">Return</label>
-      <select id="criteria-input" required="required" name="criteria" size="1">
-	<option value="100">top 100</option>
-	<option value="200">top 200</option>
-	<option value="500" selected="selected">top 500</option>
-	<option value="1000">top 1000</option>
-	<option value="2000">top 2000</option>
-	<option value="5000">top 5000</option>
-	<option value="10000">top 10000</option>
-	<option value="15000">top 15000</option>
-	<option value="20000">top 20000</option>
-      </select>
-    </div>
-
-    <button id="run-partial-corr-button"
-	    type="submit"
-	    class="btn btn-primary"
-	    data-url="{{part_corr_url}}">
-      Run Partial Correlation
-    </button>
-    {%endif%}
-
-    {%if step == "run-correlation":%}
-    <input type="hidden" name="selected_method" value="{{method}}" />
-    <input type="hidden" name="selected_target_db" value="{{target_db}}" />
-    <input type="hidden" name="selected_criteria" value="{{criteria}}" />
-
-    {{corr_results}}
-    {%endif%}
-  </div>
-
-  <div id="form-display-area">
-    <input type="hidden" name="step" id="step-indicator" value="{{step}}" />
-    <input type="hidden"
-	   name="traits_list"
-	   value="{% for trait in traits_list %}{{trait['name']}}:::{{trait['dataset']}}:::{{trait['symbol']}}:::{{trait['description']}}:::{{trait['location']}}:::{{trait['mean_expr']}}:::{{trait['max_lrs']}}:::{{trait['data_hmac']}}|||{% endfor %}">
-
-    {%if primary_trait:%}
-    <input type="hidden"
-	   name="primary_trait"
-	   value="{{primary_trait['name']}}:::{{primary_trait['dataset']}}:::{{primary_trait['symbol']}}:::{{primary_trait['description']}}:::{{primary_trait['location']}}:::{{primary_trait['mean_expr']}}:::{{primary_trait['max_lrs']}}:::{{primary_trait['data_hmac']}}"
-	   id="trait_{{primary_trait['data_hmac']}}" />
-
-    <div class="panel panel-info">
-      <div class="panel-heading">Primary Trait (X)</div>
-      <div class="panel-body">
-	{{primary_trait["name"]}}&nbsp;-&nbsp;
-	{{primary_trait["symbol"]}}&nbsp;-&nbsp;{{primary_trait["description"]}}
-      </div>
-    </div>
-    {%endif%}
-
-    {%if target_traits:%}
-    <input type="hidden"
-	   name="target_traits"
-	   value="{%for trait in target_traits:%}{{trait['name']}}:::{{trait['dataset']}}:::{{trait['symbol']}}:::{{trait['description']}}:::{{trait['location']}}:::{{trait['mean_expr']}}:::{{trait['max_lrs']}}:::{{trait['data_hmac']}}|||{%endfor%}" />
-    <div class="panel panel-info">
-      <div class="panel-heading">Target Traits (Y)</div>
-      <div class="panel-body">
-	<ul>
-	  {%for trait in target_traits:%}
-	  <li>
-	    {{trait["name"]}}&nbsp;-&nbsp;{{trait["symbol"]}}&nbsp;-&nbsp;
-	    {{trait["description"]}}</li>
-	  {%endfor%}
-	</ul>
-      </div>
-    </div>
-    {%endif%}
-
-    {%if control_traits:%}
-    <input type="hidden"
-	   name="control_traits"
-	   value="{%for trait in control_traits:%}{{trait['name']}}:::{{trait['dataset']}}:::{{trait['symbol']}}:::{{trait['description']}}:::{{trait['location']}}:::{{trait['mean_expr']}}:::{{trait['max_lrs']}}:::{{trait['data_hmac']}}|||{%endfor%}" />
-    <div class="panel panel-info">
-      <div class="panel-heading">Control Traits (Z)</div>
-      <div class="panel-body">
-	<ul>
-	  {%for trait in control_traits:%}
-	  <li>
-	    {{trait["name"]}}&nbsp;-&nbsp;{{trait["symbol"]}}&nbsp;-&nbsp;
-	    {{trait["description"]}}</li>
-	  {%endfor%}
-	</ul>
-      </div>
-    </div>
-    {%endif%}
-  </div>
-
-</form>
-
-<div id="partial-correlation-results">
-  <span id="partial-correlations-progress-indicator"
-	style="display: {%if step == 'run-correlation':%}block{%else:%}none{%endif%};">
-    <img src="/static/gif/waitAnima2.gif">
-  </span>
-  <div id="part-corr-success">
-    <table id="part-corr-results-publish" class="table-hover table-striped cell-border" style="display:none; float: left;">
-      <thead>
-	<tr>
-	  <th></th>
-	  <th>Index</th>
-	  <th>Record</th>
-	  <th>Phenotype</th>
-	  <th>Authors</th>
-	  <th>Year</th>
-	  <th>N</th>
-	  <th>Partial {%if "spearman" in (method | lower):%}rho{%else:%}r{%endif%}</th>
-	  <th>p(partial {%if "spearman" in (method | lower):%}rho{%else:%}r{%endif%})</th>
-	  <th>{%if "spearman" in (method | lower):%}rho{%else:%}r{%endif%}</th>
-	  <th>p({%if "spearman" in (method | lower):%}rho{%else:%}r{%endif%})</th>
-	  <th>delta {%if "spearman" in (method | lower):%}rho{%else:%}r{%endif%}</th>
-	</tr>
-      </thead>
-
-      <tbody>
-	<tr class="template-publish-results-row">
-	  <td></td>
-	  <td data-column-heading="Index"></td>
-	  <td data-column-heading="Record"></td>
-	  <td data-column-heading="Phenotype"></td>
-	  <td data-column-heading="Authors"></td>
-	  <td data-column-heading="Year"></td>
-	  <td data-column-heading="N"></td>
-	  <td data-column-heading="Partial {%if 'spearman' in (method | lower):%}rho{%else:%}r{%endif%}">
-	  </td>
-	  <td data-column-heading="p(partial {%if 'spearman' in (method | lower):%}rho{%else:%}r{%endif%})">
-	  </td>
-	  <td data-column-heading="{%if 'spearman' in (method | lower):%}rho{%else:%}r{%endif%}">
-	  </td>
-	  <td data-column-heading="p({%if 'spearman' in (method | lower):%}rho{%else:%}r{%endif%})">
-	  </td>
-	  <td data-column-heading="delta {%if 'spearman' in (method | lower):%}rho{%else:%}r{%endif%}">
-	  </td>
-	</tr>
-      </tbody>
-    </table>
-
-    <table id="part-corr-results-geno" style="display:none;">
-      <thead>
-	<tr>
-	  <th></th>
-	  <th>Index</th>
-	  <th>Locus</th>
-	  <th>Chr</th>
-	  <th>Megabase</th>
-	  <th>N</th>
-	  <th>Partial {%if "spearman" in (method | lower):%}rho{%else:%}r{%endif%}</th>
-	  <th>p(partial {%if "spearman" in (method | lower):%}rho{%else:%}r{%endif%})</th>
-	  <th>{%if "spearman" in (method | lower):%}rho{%else:%}r{%endif%}</th>
-	  <th>p({%if "spearman" in (method | lower):%}rho{%else:%}r{%endif%})</th>
-	  <th>delta {%if "spearman" in (method | lower):%}rho{%else:%}r{%endif%}</th>
-	</tr>
-      </thead>
-
-      <tbody>
-	<tr class="template-geno-results-row">
-	  <td></td>
-	  <td data-column-heading="Index"></td>
-	  <td data-column-heading="Locus"></td>
-	  <td data-column-heading="Chr"></td>
-	  <td data-column-heading="Megabase"></td>
-	  <td data-column-heading="N"></td>
-	  <td data-column-heading="Partial {%if 'spearman' in (method | lower):%}rho{%else:%}r{%endif%}">
-	  </td>
-	  <td data-column-heading="p(partial {%if 'spearman' in (method | lower):%}rho{%else:%}r{%endif%})">
-	  </td>
-	  <td data-column-heading="{%if 'spearman' in (method | lower):%}rho{%else:%}r{%endif%}">
-	  </td>
-	  <td data-column-heading="p({%if 'spearman' in (method | lower):%}rho{%else:%}r{%endif%})">
-	  </td>
-	  <td data-column-heading="delta {%if 'spearman' in (method | lower):%}rho{%else:%}r{%endif%}">
-	  </td>
-	</tr>
-      </tbody>
-    </table>
-
-    <table id="part-corr-results-probeset" style="display:none;">
-      <thead>
-	<tr>
-	  <th></th>
-	  <th>Index</th>
-	  <th>Record</th>
-	  <th>Gene ID</th>
-	  <th>Homologene ID</th>
-	  <th>Symbol</th>
-	  <th>Description</th>
-	  <th>Chr</th>
-	  <th>Megabase</th>
-	  <th>Mean Expr</th>
-	  <th>N</th>
-	  <th>Sample Partial {%if "spearman" in (method | lower):%}rho{%else:%}r{%endif%}</th>
-	  <th>Sample p(partial {%if "spearman" in (method | lower):%}rho{%else:%}r{%endif%})</th>
-	  <th>Sample {%if "spearman" in (method | lower):%}rho{%else:%}r{%endif%}</th>
-	  <th>Sample p({%if "spearman" in (method | lower):%}rho{%else:%}r{%endif%})</th>
-	  <th>delta {%if "spearman" in (method | lower):%}rho{%else:%}r{%endif%}</th>
-	  <th>Lit Corr</th>
-	  <th>Tissue {%if "spearman" in (method | lower):%}rho{%else:%}r{%endif%}</th>
-	  <th>Tissue p({%if "spearman" in (method | lower):%}rho{%else:%}r{%endif%})</th>
-	</tr>
-      </thead>
-
-      <tbody>
-	<tr class="template-probeset-results-row">
-	  <td></td>
-	  <td data-column-heading="Index"></td>
-	  <td data-column-heading="_"></td>
-	  <td data-column-heading="Record"></td>
-	  <td data-column-heading="Gene ID"></td>
-	  <td data-column-heading="Homologene ID"></td>
-	  <td data-column-heading="Symbol"></td>
-	  <td data-column-heading="Description"></td>
-	  <td data-column-heading="Chr"></td>
-	  <td data-column-heading="Megabase"></td>
-	  <td data-column-heading="Mean Expr"></td>
-	  <td data-column-heading="N"></td>
-	  <td data-column-heading="Sample Partial {%if 'spearman' in (method | lower):%}rho{%else:%}r{%endif%}"></td>
-	  <td data-column-heading="Sample p(partial {%if 'spearman' in (method | lower):%}rho{%else:%}r{%endif%})"></td>
-	  <td data-column-heading="Sample {%if 'spearman' in (method | lower):%}rho{%else:%}r{%endif%}"></td>
-	  <td data-column-heading="Sample p({%if 'spearman' in (method | lower):%}rho{%else:%}r{%endif%})"></td>
-	  <td data-column-heading="delta {%if 'spearman' in (method | lower):%}rho{%else:%}r{%endif%}"></td>
-	  <td data-column-heading="Lit Corr"></td>
-	  <td data-column-heading="Tissue {%if 'spearman' in (method | lower):%}rho{%else:%}r{%endif%}"></td>
-	  <td data-column-heading="Tissue p({%if 'spearman' in (method | lower):%}rho{%else:%}r{%endif%})"></td>
-	</tr>
-      </tbody>
-    </table>
-  </div>
-  <div id="part-corr-error">
-  </div>
-</div>
-</div>
-{%endblock%}
-
-{%block js%}
-{%if step == "select-corr-method":%}
-<script type="text/javascript"
-	src="/static/new/javascript/partial_correlations.js"></script>
-<script language="javascript" type="text/javascript"
-	src="{{ url_for('js', filename='DataTables/js/jquery.dataTables.min.js') }}"></script>
-{%endif%}
-{%endblock%}
diff --git a/wqflask/wqflask/templates/partial_correlations/pcorrs_error.html b/wqflask/wqflask/templates/partial_correlations/pcorrs_error.html
new file mode 100644
index 00000000..36847f57
--- /dev/null
+++ b/wqflask/wqflask/templates/partial_correlations/pcorrs_error.html
@@ -0,0 +1,54 @@
+{% extends "base.html" %}
+{% block title %}Error: {{message}}{% endblock %}
+{% block content %}
+<!-- Start of body -->
+
+<div class="col-md-8">
+<div class="form-group has-error">
+  <div class="control-label" for="inputError1">
+
+    <img src="/static/gif/error/{{ error_image }}">
+
+    <h1>ERROR</h1>
+
+    <p>
+      This error is not what we wanted to see. Unfortunately errors
+      are part of all software systems and we need to resolve this
+      together.
+    </p>
+    <p>
+      <b>It is important to report this ERROR so we can fix it for everyone</b>.
+    </p>
+
+    <p>
+      Report to the GeneNetwork team by recording the steps you take
+      to reproduce this ERROR. Next to those steps, copy-paste below
+      stack trace, either as
+      a <a href="https://github.com/genenetwork/genenetwork2/issues/new">new
+      issue</a> or E-mail this full page to one of the developers
+      directly.
+    </p>
+  </div>
+
+  <p>
+  (GeneNetwork error: {{message[:128]}})
+  </p>
+
+  <p>
+    To check if this already a known issue, search the
+    <a href="https://github.com/genenetwork/genenetwork2/issues">issue
+      tracker</a>.
+  </p>
+
+  <a href="#Stack" class="btn btn-default" data-toggle="collapse">Toggle full stack trace</a>
+  <div id="Stack" class="collapse">
+    <pre>
+      GeneNetwork {{ version }} {% for line in stack %} {{ line }}
+      {% endfor %}
+    </pre>
+  </div>
+</div>
+</div>
+
+
+{% endblock %}
diff --git a/wqflask/wqflask/templates/partial_correlations/pcorrs_poll_results.html b/wqflask/wqflask/templates/partial_correlations/pcorrs_poll_results.html
new file mode 100644
index 00000000..47acc294
--- /dev/null
+++ b/wqflask/wqflask/templates/partial_correlations/pcorrs_poll_results.html
@@ -0,0 +1,15 @@
+{%extends "base.html"%}
+
+{%block title%}Partial Correlations:{%endblock%}
+
+{%block css%}
+<meta http-equiv="refresh"
+      content="10;URL=/partial_correlations/{{command_id}}">
+{%endblock%}
+
+{%block content%}
+
+<p>Computing partial correlations...</p>
+<img src="/static/gif/waitAnima2.gif"
+     alt="Image indicating computation of partial correlations is ongoing" />
+{%endblock%}
diff --git a/wqflask/wqflask/templates/partial_correlations/pcorrs_results_presentation.html b/wqflask/wqflask/templates/partial_correlations/pcorrs_results_presentation.html
new file mode 100644
index 00000000..c109f24b
--- /dev/null
+++ b/wqflask/wqflask/templates/partial_correlations/pcorrs_results_presentation.html
@@ -0,0 +1,249 @@
+{%extends "base.html"%}
+
+{%block title%}Partial Correlations:{%endblock%}
+
+{%block css%}
+<link rel="stylesheet" type="text/css" href="/static/new/css/partial_correlations.css" />
+<link rel="stylesheet" type="text/css" href="{{ url_for('css', filename='DataTables/css/jquery.dataTables.css') }}" />
+<link rel="stylesheet" type="text/css" href="/static/new/css/trait_list.css" />
+<link rel="stylesheet" type="text/css" href="/static/new/css/show_trait.css" />
+{%endblock%}
+
+{%block content%}
+<p>
+  <strong>Primary Trait</strong><br /><br />
+  <a href="{{url_for(
+	   'show_trait_page',
+	   trait_id=primary['trait_name'],
+	   dataset=primary['dataset_name'])}}"
+     title="Link to trait data for trait {{primary['trait_name']}}">
+    {{primary["dataset_type"]}}/{{primary["trait_name"]}}
+    [{{primary["symbol"] }} on Chr {{primary["chr"]}} @ {{primary["mb"]}}]:
+    {{primary["description"]}}
+  </a> --- FROM: {{primary["dataset_name"]}}
+</p>
+<p><strong>Control Traits</strong><br /><br />
+  {%for trait in controls:%}
+  <a href="{{url_for(
+	   'show_trait_page',
+	   trait_id=trait['trait_name'],
+	   dataset=trait['dataset_name'])}}"
+     title="Link to trait data for trait {{trait['trait_name']}}">
+    {{trait["dataset_type"]}}/{{trait["trait_name"]}}
+    [{{trait["symbol"] }} on Chr {{trait["chr"]}} @ {{trait["mb"]}}]:
+    {{trait["description"]}}
+  </a> --- FROM: {{trait["dataset_name"]}}<br />
+  {%endfor%}
+</p>
+
+<div id="partial-correlation-results">
+  {%if dataset_type == "Publish":%}
+  <table id="part-corr-results-publish"
+	 class="table-hover table-striped cell-border"
+	 style="float: left;">
+    <thead>
+      <tr>
+	<th></th>
+	<th>Index</th>
+	<th>Record</th>
+	<th>Phenotype</th>
+	<th>Authors</th>
+	<th>Year</th>
+	<th>N</th>
+	<th>Partial {%if "spearman" in (method | lower):%}rho{%else:%}r{%endif%}</th>
+	<th>p(partial {%if "spearman" in (method | lower):%}rho{%else:%}r{%endif%})</th>
+	<th>{%if "spearman" in (method | lower):%}rho{%else:%}r{%endif%}</th>
+	<th>p({%if "spearman" in (method | lower):%}rho{%else:%}r{%endif%})</th>
+	<th>delta {%if "spearman" in (method | lower):%}rho{%else:%}r{%endif%}</th>
+      </tr>
+    </thead>
+
+    <tbody>
+      {%for trait in correlations:%}
+      <tr class="results-row">
+	<td></td>
+	<td data-column-heading="Index"></td>
+	<td data-column-heading="Record">
+	  <a href="{{url_for(
+		   'show_trait_page',
+		   trait_id=trait['trait_name'],
+		   dataset=trait['dataset_name'])}}"
+	     title="Link to trait data for trait {{trait['trait_name']}}">
+	    {{trait["trait_name"]}}
+	  </a>
+	</td>
+	<td data-column-heading="Phenotype">
+	  {{trait["post_publication_description"]}}</td>
+	<td data-column-heading="Authors">{{trait["authors"]}}</td>
+	<td data-column-heading="Year">{{trait["year"]}}</td>
+	<td data-column-heading="N">{{trait["noverlap"]}}</td>
+	<td data-column-heading="Partial {%if 'spearman' in (method | lower):%}rho{%else:%}r{%endif%}">
+	  {{format_number(trait.get("partial_corr"))}}
+	</td>
+	<td data-column-heading="p(partial {%if 'spearman' in (method | lower):%}rho{%else:%}r{%endif%})">
+	  {{format_number(trait.get("partial_corr_p_value"))}}
+	</td>
+	<td data-column-heading="{%if 'spearman' in (method | lower):%}rho{%else:%}r{%endif%}">
+	  {{format_number(trait.get("corr"))}}
+	</td>
+	<td data-column-heading="p({%if 'spearman' in (method | lower):%}rho{%else:%}r{%endif%})">
+	  {{format_number(trait.get("corr_p_value"))}}
+	</td>
+	<td data-column-heading="delta {%if 'spearman' in (method | lower):%}rho{%else:%}r{%endif%}">
+	  {{format_number(trait.get("delta"))}}
+	</td>
+      </tr>
+      {%endfor%}
+    </tbody>
+  </table>
+  {%endif%}
+
+  {%if dataset_type == "Geno":%}
+  <table id="part-corr-results-geno"
+	 class="table-hover table-striped cell-border"
+	 style="float: left;">
+    <thead>
+      <tr>
+	<th></th>
+	<th>Index</th>
+	<th>Locus</th>
+	<th>Chr</th>
+	<th>Megabase</th>
+	<th>N</th>
+	<th>Partial {%if "spearman" in (method | lower):%}rho{%else:%}r{%endif%}</th>
+	<th>p(partial {%if "spearman" in (method | lower):%}rho{%else:%}r{%endif%})</th>
+	<th>{%if "spearman" in (method | lower):%}rho{%else:%}r{%endif%}</th>
+	<th>p({%if "spearman" in (method | lower):%}rho{%else:%}r{%endif%})</th>
+	<th>delta {%if "spearman" in (method | lower):%}rho{%else:%}r{%endif%}</th>
+      </tr>
+    </thead>
+
+    <tbody>
+      {%for trait in correlations:%}
+      <tr class="results-row">
+	<td></td>
+	<td data-column-heading="Index"></td>
+	<td data-column-heading="Locus">
+	  <a href="{{url_for(
+		   'show_trait_page',
+		   trait_id=trait['trait_name'],
+		   dataset=trait['dataset_name'])}}"
+	     title="Link to trait data for trait {{trait['trait_name']}}">
+	    {{trait["trait_name"]}}
+	  </a>
+	</td>
+	<td data-column-heading="Chr">{{trait["chr"]}}</td>
+	<td data-column-heading="Megabase">{{trait["mb"]}}</td>
+	<td data-column-heading="N">{{trait["noverlap"]}}</td>
+	<td data-column-heading="Partial {%if 'spearman' in (method | lower):%}rho{%else:%}r{%endif%}">
+	  {{format_number(trait.get("partial_corr"))}}
+	</td>
+	<td data-column-heading="p(partial {%if 'spearman' in (method | lower):%}rho{%else:%}r{%endif%})">
+	  {{format_number(trait.get("partial_corr_p_value"))}}
+	</td>
+	<td data-column-heading="{%if 'spearman' in (method | lower):%}rho{%else:%}r{%endif%}">
+	  {{format_number(trait.get("corr"))}}
+	</td>
+	<td data-column-heading="p({%if 'spearman' in (method | lower):%}rho{%else:%}r{%endif%})">
+	  {{format_number(trait.get("corr_p_value"))}}
+	</td>
+	<td data-column-heading="delta {%if 'spearman' in (method | lower):%}rho{%else:%}r{%endif%}">
+	  {{format_number(trait.get("delta"))}}
+	</td>
+      </tr>
+      {%endfor%}
+    </tbody>
+  </table>
+  {%endif%}
+
+  {%if dataset_type == "ProbeSet":%}
+  <table id="part-corr-results-probeset"
+	 class="table-hover table-striped cell-border"
+	 style="float: left;">
+    <thead>
+      <tr>
+	<th></th>
+	<th>Index</th>
+	<th>Record</th>
+	<th>Gene ID</th>
+	<th>Homologene ID</th>
+	<th>Symbol</th>
+	<th>Description</th>
+	<th>Chr</th>
+	<th>Megabase</th>
+	<th>Mean Expr</th>
+	<th>N</th>
+	<th>Sample Partial {%if "spearman" in (method | lower):%}rho{%else:%}r{%endif%}</th>
+	<th>Sample p(partial {%if "spearman" in (method | lower):%}rho{%else:%}r{%endif%})</th>
+	<th>Sample {%if "spearman" in (method | lower):%}rho{%else:%}r{%endif%}</th>
+	<th>Sample p({%if "spearman" in (method | lower):%}rho{%else:%}r{%endif%})</th>
+	<th>delta {%if "spearman" in (method | lower):%}rho{%else:%}r{%endif%}</th>
+	<th>Lit Corr</th>
+	<th>Tissue {%if "spearman" in (method | lower):%}rho{%else:%}r{%endif%}</th>
+	<th>Tissue p({%if "spearman" in (method | lower):%}rho{%else:%}r{%endif%})</th>
+      </tr>
+    </thead>
+
+    <tbody>
+      {%for trait in correlations:%}
+      <tr class="results-row">
+	<td></td>
+	<td data-column-heading="Index"></td>
+	<td data-column-heading="Record">
+	  <a href="{{url_for(
+		   'show_trait_page',
+		   trait_id=trait['trait_name'],
+		   dataset=trait['dataset_name'])}}"
+	     title="Link to trait data for trait {{trait['trait_name']}}">
+	    {{trait["trait_name"]}}
+	  </a>
+	</td>
+	<td data-column-heading="Gene ID">{{trait["geneid"]}}</td>
+	<td data-column-heading="Homologene ID">{{trait["homologeneid"]}}</td>
+	<td data-column-heading="Symbol">{{trait["symbol"]}}</td>
+	<td data-column-heading="Description">{{trait["description"]}}</td>
+	<td data-column-heading="Chr">{{trait["chr"]}}</td>
+	<td data-column-heading="Megabase">{{trait["mb"]}}</td>
+	<td data-column-heading="Mean Expr">{{trait["mean_expr"]}}</td>
+	<td data-column-heading="N">{{trait["noverlap"]}}</td>
+	<td data-column-heading="Sample Partial {%if 'spearman' in (method | lower):%}rho{%else:%}r{%endif%}">
+	  {{format_number(trait.get("partial_corr"))}}
+	</td>
+	<td data-column-heading="Sample p(partial {%if 'spearman' in (method | lower):%}rho{%else:%}r{%endif%})">
+	  {{format_number(trait.get("partial_corr_p_value"))}}
+	</td>
+	<td data-column-heading="Sample {%if 'spearman' in (method | lower):%}rho{%else:%}r{%endif%}">
+	  {{format_number(trait.get("corr"))}}
+	</td>
+	<td data-column-heading="Sample p({%if 'spearman' in (method | lower):%}rho{%else:%}r{%endif%})">
+	  {{format_number(trait.get("corr_p_value"))}}
+	</td>
+	<td data-column-heading="delta {%if 'spearman' in (method | lower):%}rho{%else:%}r{%endif%}">
+	  {{format_number(trait.get("delta"))}}
+	</td>
+	<td data-column-heading="Lit Corr">
+	  {{format_number(trait.get("l_corr"))}}
+	</td>
+	<td data-column-heading="Tissue {%if 'spearman' in (method | lower):%}rho{%else:%}r{%endif%}">
+	  {{format_number(trait.get("tissue_corr"))}}
+	</td>
+	<td data-column-heading="Tissue p({%if 'spearman' in (method | lower):%}rho{%else:%}r{%endif%})">
+	  {{format_number(trait.get("tissue_p_value"))}}
+	</td>
+      </tr>
+      {%endfor%}
+    </tbody>
+  </table>
+  {%endif%}
+
+</div>
+{%endblock%}
+
+{%block js%}
+{%if step == "select-corr-method":%}
+<script type="text/javascript"
+	src="/static/new/javascript/partial_correlations.js"></script>
+<script language="javascript" type="text/javascript"
+	src="{{ url_for('js', filename='DataTables/js/jquery.dataTables.min.js') }}"></script>
+{%endif%}
+{%endblock%}
diff --git a/wqflask/wqflask/templates/partial_correlations/pcorrs_select_operations.html b/wqflask/wqflask/templates/partial_correlations/pcorrs_select_operations.html
new file mode 100644
index 00000000..e541f31b
--- /dev/null
+++ b/wqflask/wqflask/templates/partial_correlations/pcorrs_select_operations.html
@@ -0,0 +1,146 @@
+{%extends "base.html"%}
+
+{%block title%}Partial Correlations:{%endblock%}
+
+{%block css%}
+<link rel="stylesheet" type="text/css"
+      href="{{url_for('css', filename='DataTables/css/jquery.dataTables.css')}}" />
+<link rel="stylesheet" type="text/css"
+      href="{{url_for('js', filename='DataTablesExtensions/buttonStyles/css/buttons.dataTables.min.css')}}">
+<link rel="stylesheet" type="text/css" href="/static/new/css/show_trait.css" />
+<link rel="stylesheet" type="text/css" href="/static/new/css/trait_list.css" />
+<link rel="stylesheet" type="text/css"
+      href="/static/new/css/partial_correlations.css" />
+{%endblock%}
+
+{%block content%}
+<form id="pcorrs-form"
+      method="POST"
+      action="{{url_for('partial_correlations')}}">
+  {%with messages = get_flashed_messages(with_categories=true)%}
+  {%if messages:%}
+  <ul class=flashes>
+    {%for category, message in messages:%}
+    <li class="{{category}}">{{message}}</li>
+    {%endfor%}
+  </ul>
+  {%endif%}
+  {%endwith%}
+
+  <input type="hidden" value="{{trait_list_str}}" name="trait_list">
+  <table id="pcorrs_traits_table" class="table-hover table-striped cell-border">
+    <thead>
+      <tr>
+	<th>Primary (X)</th>
+	<th>Controls (Z)</th>
+	<th>Ignored</th>
+	<th>Dataset</th>
+	<th>Trait ID</th>
+	<th>Symbol</th>
+	<th>Description</th>
+	<th>Location</th>
+	<th>Mean</th>
+	<th>Max LRS</th>
+	<th>Max LRS Location Chr and Mb</th>
+      </tr>
+    </thead>
+
+    <tbody>
+      {%for trait in traits:%}
+      <tr>
+	<td>
+	  <input type="radio" name="trait_{{trait['trait_name']}}"
+		 id="trait_{{trait['trait_name']}}"
+		 value="primary_{{trait['trait_name']}}">
+	</td>
+	<td>
+	  <input type="radio" name="trait_{{trait['trait_name']}}"
+		 id="trait_{{trait['trait_name']}}"
+		 value="controls_{{trait['trait_name']}}">
+	</td>
+	<td>
+	  <input type="radio" name="trait_{{trait['trait_name']}}"
+		 id="trait_{{trait['trait_name']}}"
+		 value="ignored_{{trait['trait_name']}}" checked="checked">
+	</td>
+	<td>{{trait.get("dataset", "_")}}
+	<td>{{trait.get("trait_name", "_")}}</td>
+	<td>{{trait.get("symbol", "_")}}</td>
+	<td>{{trait.get("description", "_")}}</td>
+	<td>{{trait.get("location", "_")}}</td>
+	<td>{{trait.get("mean", "_")}}</td>
+	<td>{{trait.get("lrs", "_")}}</td>
+	<td>{{trait.get("lrs_location", "_")}}</td>
+      </tr>
+      {%endfor%}
+    </tbody>
+  </table>
+
+  <div class="form-group">
+    <label for="corr-method-input" class="form-label">Compute</label>
+    <select id="corr-method-input" required="required" name="method"
+	    class="form-control">
+      <option value="Genetic Correlation, Pearson's r">
+	Genetic Correlation, Pearson's r</option>
+      <option value="Genetic Correlation, Spearman's rho">
+	Genetic Correlation, Spearman's rho</option>
+      <option value="SGO Literature Correlation">
+	SGO Literature Correlation</option>
+      <option value="Tissue Correlation, Pearson's r">
+	Tissue Correlation, Pearson's r</option>
+      <option value="Tissue Correlation, Spearman's rho">
+	Tissue Correlation, Spearman's rho</option>
+    </select>
+  </div>
+
+  <div class="form-group">
+    <label for="target-db-input" class="form-label">Choose Database</label>
+    <select id="target-db-input" required="required" name="target_db"
+	    class="form-control">
+      {%if target_dbs:%}
+      {%for item in target_dbs:%}
+      {%if "description" in item.keys():%}
+      <option value="{{item['value']}}">{{item['description']}}</option>
+      {%else:%}
+      {%for group, opts in item.items()%}
+      {%if opts | length > 0:%}
+      <optgroup label="{{group}} ------">
+	{%for item2 in opts:%}
+	<option value="{{item2['value']}}">{{item2['description']}}</option>
+	{%endfor%}
+      </optgroup>
+      {%endif%}
+      {%endfor%}
+      {%endif%}
+      {%endfor%}
+      {%endif%}
+    </select>
+  </div>
+
+  <div class="form-group">
+      <label for="criteria-input" class="form-label">Return</label>
+      <select id="criteria-input" required="required" name="criteria" size="1"
+	      class="form-control">
+	<option value="100">top 100</option>
+	<option value="200">top 200</option>
+	<option value="500" selected="selected">top 500</option>
+	<option value="1000">top 1000</option>
+	<option value="2000">top 2000</option>
+	<option value="5000">top 5000</option>
+	<option value="10000">top 10000</option>
+	<option value="15000">top 15000</option>
+	<option value="20000">top 20000</option>
+      </select>
+    </div>
+
+  <button type="submit" class="btn btn-primary" name="submit"
+	  value="Run Partial Correlations">
+    Run Partial Correlations
+  </button>
+</form>
+{%endblock%}
+
+{%block js%}
+<script type="text/javascript"
+	src="/static/new/javascript/partial_correlations.js"></script>
+{%endblock%}
diff --git a/wqflask/wqflask/templates/tool_buttons.html b/wqflask/wqflask/templates/tool_buttons.html
index 3ee5be19..c3927495 100644
--- a/wqflask/wqflask/templates/tool_buttons.html
+++ b/wqflask/wqflask/templates/tool_buttons.html
@@ -33,3 +33,10 @@
 <button id="comp_bar_chart" class="btn btn-primary submit_special" data-url="/comparison_bar_chart" title="Comparison Bar Chart" >
     Comparison Bar Chart
 </button>
+
+<button id="partial-correlations"
+	class="btn btn-primary submit_special"
+	data-url="{{url_for('partial_correlations')}}"
+	title="Run partial correlations with the selected traits">
+    Partial Correlations
+</button>