aboutsummaryrefslogtreecommitdiff
path: root/wqflask
diff options
context:
space:
mode:
authorPjotr Prins2020-07-08 04:52:07 -0500
committerPjotr Prins2020-07-08 04:52:07 -0500
commitc249ba2ef7d691227da8864838dfc97db68d4084 (patch)
tree52e28fe60dc2bbe25fcfa0cd18a04a660e9573fe /wqflask
parent421848837d3b489762bb9e58e5e7047d02cdb20b (diff)
parentb026c18a1263f84cbed86018e3ba2d20e97b61d4 (diff)
downloadgenenetwork2-c249ba2ef7d691227da8864838dfc97db68d4084.tar.gz
Merge branch 'testing' of github.com:genenetwork/genenetwork2 into testing
Diffstat (limited to 'wqflask')
-rw-r--r--wqflask/base/data_set.py44
-rw-r--r--wqflask/base/trait.py245
-rw-r--r--wqflask/base/webqtlConfig.py5
-rw-r--r--wqflask/maintenance/set_resource_defaults.py166
-rw-r--r--wqflask/utility/authentication_tools.py96
-rw-r--r--wqflask/utility/helper_functions.py11
-rw-r--r--wqflask/utility/hmac.py2
-rw-r--r--wqflask/utility/redis_tools.py240
-rw-r--r--wqflask/utility/tools.py2
-rw-r--r--wqflask/wqflask/api/correlation.py472
-rw-r--r--wqflask/wqflask/api/gen_menu.py19
-rw-r--r--wqflask/wqflask/api/mapping.py4
-rw-r--r--wqflask/wqflask/collect.py22
-rw-r--r--wqflask/wqflask/comparison_bar_chart/comparison_bar_chart.py4
-rw-r--r--wqflask/wqflask/correlation/corr_scatter_plot.py23
-rw-r--r--wqflask/wqflask/correlation/show_corr_results.py13
-rw-r--r--wqflask/wqflask/correlation_matrix/show_corr_matrix.py22
-rw-r--r--wqflask/wqflask/ctl/ctl_analysis.py10
-rw-r--r--wqflask/wqflask/do_search.py11
-rw-r--r--wqflask/wqflask/docs.py4
-rw-r--r--wqflask/wqflask/group_manager.py145
-rw-r--r--wqflask/wqflask/gsearch.py18
-rw-r--r--wqflask/wqflask/marker_regression/display_mapping_results.py38
-rw-r--r--wqflask/wqflask/marker_regression/gemma_mapping.py6
-rw-r--r--wqflask/wqflask/marker_regression/rqtl_mapping.py217
-rw-r--r--wqflask/wqflask/marker_regression/run_mapping.py27
-rw-r--r--wqflask/wqflask/network_graph/network_graph.py4
-rw-r--r--wqflask/wqflask/resource_manager.py134
-rw-r--r--wqflask/wqflask/search_results.py106
-rw-r--r--wqflask/wqflask/show_trait/export_trait_data.py30
-rw-r--r--wqflask/wqflask/show_trait/show_trait.py145
-rw-r--r--wqflask/wqflask/static/new/css/show_trait.css4
-rw-r--r--wqflask/wqflask/static/new/javascript/dataset_select_menu_orig.js35
-rw-r--r--wqflask/wqflask/static/new/javascript/draw_corr_scatterplot.js2
-rw-r--r--wqflask/wqflask/static/new/javascript/get_covariates_from_collection.js2
-rw-r--r--wqflask/wqflask/static/new/javascript/group_manager.js38
-rw-r--r--wqflask/wqflask/static/new/javascript/search_results.js35
-rw-r--r--wqflask/wqflask/static/new/javascript/show_trait.js40
-rw-r--r--wqflask/wqflask/static/new/javascript/show_trait_mapping_tools.js27
-rw-r--r--wqflask/wqflask/templates/admin/change_resource_owner.html116
-rw-r--r--wqflask/wqflask/templates/admin/create_group.html89
-rw-r--r--wqflask/wqflask/templates/admin/group_manager.html174
-rw-r--r--wqflask/wqflask/templates/admin/manage_resource.html108
-rw-r--r--wqflask/wqflask/templates/admin/search_for_groups.html134
-rw-r--r--wqflask/wqflask/templates/admin/set_group_privileges.html102
-rw-r--r--wqflask/wqflask/templates/admin/view_group.html247
-rw-r--r--wqflask/wqflask/templates/base.html5
-rw-r--r--wqflask/wqflask/templates/collections/list.html6
-rw-r--r--wqflask/wqflask/templates/collections/view.html24
-rw-r--r--wqflask/wqflask/templates/corr_scatterplot.html2
-rw-r--r--wqflask/wqflask/templates/correlation_matrix.html4
-rw-r--r--wqflask/wqflask/templates/correlation_page.html71
-rw-r--r--wqflask/wqflask/templates/email/verification.txt7
-rw-r--r--wqflask/wqflask/templates/gsearch_gene.html4
-rw-r--r--wqflask/wqflask/templates/gsearch_pheno.html6
-rw-r--r--wqflask/wqflask/templates/loading.html2
-rw-r--r--wqflask/wqflask/templates/mapping_results.html43
-rw-r--r--wqflask/wqflask/templates/new_security/not_authenticated.html11
-rw-r--r--wqflask/wqflask/templates/pair_scan_results.html4
-rw-r--r--wqflask/wqflask/templates/search_error.html2
-rw-r--r--wqflask/wqflask/templates/search_result_page.html69
-rw-r--r--wqflask/wqflask/templates/set_group_privileges.html77
-rw-r--r--wqflask/wqflask/templates/show_trait.html5
-rw-r--r--wqflask/wqflask/templates/show_trait_calculate_correlations.html4
-rw-r--r--wqflask/wqflask/templates/show_trait_details.html13
-rwxr-xr-xwqflask/wqflask/templates/show_trait_mapping_tools.html47
-rw-r--r--wqflask/wqflask/templates/show_trait_transform_and_filter.html2
-rw-r--r--wqflask/wqflask/templates/snp_browser.html2
-rw-r--r--wqflask/wqflask/user_login.py43
-rw-r--r--wqflask/wqflask/user_manager.py22
-rw-r--r--wqflask/wqflask/user_session.py32
-rw-r--r--wqflask/wqflask/views.py118
72 files changed, 3133 insertions, 930 deletions
diff --git a/wqflask/base/data_set.py b/wqflask/base/data_set.py
index cab708ef..2272b6ee 100644
--- a/wqflask/base/data_set.py
+++ b/wqflask/base/data_set.py
@@ -65,6 +65,9 @@ logger = getLogger(__name__ )
DS_NAME_MAP = {}
def create_dataset(dataset_name, dataset_type = None, get_samplelist = True, group_name = None):
+ if dataset_name == "Temp":
+ dataset_type = "Temp"
+
if not dataset_type:
dataset_type = Dataset_Getter(dataset_name)
@@ -131,7 +134,7 @@ Publish or ProbeSet. E.g.
ProbeSetFreeze.Name = "{0}"
""".format(name)
- results = g.db.execute(geno_query).fetchall()
+ results = g.db.execute(mrna_expr_query).fetchall()
if len(results):
self.datasets[name] = "ProbeSet"
Redis.set("dataset_structure", json.dumps(self.datasets))
@@ -165,12 +168,11 @@ Publish or ProbeSet. E.g.
geno_query = """
SELECT
- GenoFreezeId
+ GenoFreeze.Id
FROM
GenoFreeze
WHERE
GenoFreeze.Name = "{0}"
- {1}
""".format(name)
results = g.db.execute(geno_query).fetchall()
@@ -487,25 +489,18 @@ class DatasetGroup(object):
def datasets(group_name, this_group = None):
key = "group_dataset_menu:v2:" + group_name
- logger.debug("key is2:", key)
dataset_menu = []
- logger.debug("[tape4] webqtlConfig.PUBLICTHRESH:", webqtlConfig.PUBLICTHRESH)
- logger.debug("[tape4] type webqtlConfig.PUBLICTHRESH:", type(webqtlConfig.PUBLICTHRESH))
the_results = fetchall('''
(SELECT '#PublishFreeze',PublishFreeze.FullName,PublishFreeze.Name
FROM PublishFreeze,InbredSet
WHERE PublishFreeze.InbredSetId = InbredSet.Id
and InbredSet.Name = '%s'
- and PublishFreeze.public > %s
- and PublishFreeze.confidentiality < 1
ORDER BY PublishFreeze.Id ASC)
UNION
(SELECT '#GenoFreeze',GenoFreeze.FullName,GenoFreeze.Name
FROM GenoFreeze, InbredSet
WHERE GenoFreeze.InbredSetId = InbredSet.Id
- and InbredSet.Name = '%s'
- and GenoFreeze.public > %s
- and GenoFreeze.confidentiality < 1)
+ and InbredSet.Name = '%s')
UNION
(SELECT Tissue.Name, ProbeSetFreeze.FullName,ProbeSetFreeze.Name
FROM ProbeSetFreeze, ProbeFreeze, InbredSet, Tissue
@@ -513,12 +508,10 @@ def datasets(group_name, this_group = None):
and ProbeFreeze.TissueId = Tissue.Id
and ProbeFreeze.InbredSetId = InbredSet.Id
and InbredSet.Name like %s
- and ProbeSetFreeze.public > %s
- and ProbeSetFreeze.confidentiality < 1
ORDER BY Tissue.Name, ProbeSetFreeze.OrderList DESC)
- ''' % (group_name, webqtlConfig.PUBLICTHRESH,
- group_name, webqtlConfig.PUBLICTHRESH,
- "'" + group_name + "'", webqtlConfig.PUBLICTHRESH))
+ ''' % (group_name,
+ group_name,
+ "'" + group_name + "'"))
sorted_results = sorted(the_results, key=lambda kv: kv[0])
@@ -638,29 +631,25 @@ class DataSet(object):
"""
-
try:
if self.type == "ProbeSet":
query_args = tuple(escape(x) for x in (
- str(webqtlConfig.PUBLICTHRESH),
self.name,
self.name,
self.name))
self.id, self.name, self.fullname, self.shortname, self.data_scale, self.tissue = fetch1("""
-SELECT ProbeSetFreeze.Id, ProbeSetFreeze.Name, ProbeSetFreeze.FullName, ProbeSetFreeze.ShortName, ProbeSetFreeze.DataScale, Tissue.Name
-FROM ProbeSetFreeze, ProbeFreeze, Tissue
-WHERE ProbeSetFreeze.public > %s
-AND ProbeSetFreeze.ProbeFreezeId = ProbeFreeze.Id
-AND ProbeFreeze.TissueId = Tissue.Id
-AND (ProbeSetFreeze.Name = '%s' OR ProbeSetFreeze.FullName = '%s' OR ProbeSetFreeze.ShortName = '%s')
+ SELECT ProbeSetFreeze.Id, ProbeSetFreeze.Name, ProbeSetFreeze.FullName, ProbeSetFreeze.ShortName, ProbeSetFreeze.DataScale, Tissue.Name
+ FROM ProbeSetFreeze, ProbeFreeze, Tissue
+ WHERE ProbeSetFreeze.ProbeFreezeId = ProbeFreeze.Id
+ AND ProbeFreeze.TissueId = Tissue.Id
+ AND (ProbeSetFreeze.Name = '%s' OR ProbeSetFreeze.FullName = '%s' OR ProbeSetFreeze.ShortName = '%s')
""" % (query_args),"/dataset/"+self.name+".json",
lambda r: (r["id"],r["name"],r["full_name"],r["short_name"],r["data_scale"],r["tissue"])
)
else:
query_args = tuple(escape(x) for x in (
(self.type + "Freeze"),
- str(webqtlConfig.PUBLICTHRESH),
self.name,
self.name,
self.name))
@@ -669,9 +658,8 @@ AND (ProbeSetFreeze.Name = '%s' OR ProbeSetFreeze.FullName = '%s' OR ProbeSetFre
self.id, self.name, self.fullname, self.shortname = fetchone("""
SELECT Id, Name, FullName, ShortName
FROM %s
- WHERE public > %s AND
- (Name = '%s' OR FullName = '%s' OR ShortName = '%s')
- """ % (query_args))
+ WHERE (Name = '%s' OR FullName = '%s' OR ShortName = '%s')
+ """ % (query_args))
except TypeError:
logger.debug("Dataset {} is not yet available in GeneNetwork.".format(self.name))
diff --git a/wqflask/base/trait.py b/wqflask/base/trait.py
index e454c593..8e11c11d 100644
--- a/wqflask/base/trait.py
+++ b/wqflask/base/trait.py
@@ -1,12 +1,12 @@
from __future__ import absolute_import, division, print_function
+import os
import string
import resource
import codecs
import requests
-
-import redis
-Redis = redis.StrictRedis()
+import random
+import urllib
from base import webqtlConfig
from base.webqtlCaseData import webqtlCaseData
@@ -14,7 +14,10 @@ from base.data_set import create_dataset
from db import webqtlDatabaseFunction
from utility import webqtlUtil
from utility import hmac
-from utility.tools import GN2_BASE_URL
+from utility.authentication_tools import check_resource_availability
+from utility.tools import GN2_BASE_URL, GN_VERSION
+from utility.redis_tools import get_redis_conn, get_resource_id, get_resource_info
+Redis = get_redis_conn()
from wqflask import app
@@ -22,11 +25,36 @@ import simplejson as json
from MySQLdb import escape_string as escape
from pprint import pformat as pf
-from flask import Flask, g, request, url_for
+from flask import Flask, g, request, url_for, redirect, make_response, render_template
from utility.logger import getLogger
logger = getLogger(__name__ )
+def create_trait(**kw):
+ assert bool(kw.get('dataset')) != bool(kw.get('dataset_name')), "Needs dataset ob. or name";
+
+ permitted = True
+ if kw.get('name'):
+ if kw.get('dataset_name'):
+ if kw.get('dataset_name') != "Temp":
+ dataset = create_dataset(kw.get('dataset_name'))
+ else:
+ dataset = kw.get('dataset')
+
+ if kw.get('dataset_name') != "Temp":
+ if dataset.type == 'Publish':
+ permissions = check_resource_availability(dataset, kw.get('name'))
+ else:
+ permissions = check_resource_availability(dataset)
+
+ if "view" in permissions['data']:
+ the_trait = GeneralTrait(**kw)
+ if the_trait.dataset.type != "Temp":
+ the_trait = retrieve_trait_info(the_trait, the_trait.dataset, get_qtl_info=kw.get('get_qtl_info'))
+ return the_trait
+ else:
+ return None
+
class GeneralTrait(object):
"""
Trait class defines a trait in webqtl, can be either Microarray,
@@ -51,6 +79,7 @@ class GeneralTrait(object):
self.haveinfo = kw.get('haveinfo', False)
self.sequence = kw.get('sequence') # Blat sequence, available for ProbeSet
self.data = kw.get('data', {})
+ self.view = True
# Sets defaults
self.locus = None
@@ -76,8 +105,6 @@ class GeneralTrait(object):
# Todo: These two lines are necessary most of the time, but perhaps not all of the time
# So we could add a simple if statement to short-circuit this if necessary
- if self.dataset.type != "Temp":
- self = retrieve_trait_info(self, self.dataset, get_qtl_info=get_qtl_info)
if get_sample_info != False:
self = retrieve_sample_data(self, self.dataset)
@@ -117,6 +144,7 @@ class GeneralTrait(object):
formatted = self.post_publication_description
else:
formatted = "Not available"
+
return formatted
@property
@@ -213,26 +241,28 @@ def get_sample_data():
trait = params['trait']
dataset = params['dataset']
- trait_ob = GeneralTrait(name=trait, dataset_name=dataset)
-
- trait_dict = {}
- trait_dict['name'] = trait
- trait_dict['db'] = dataset
- trait_dict['type'] = trait_ob.dataset.type
- trait_dict['group'] = trait_ob.dataset.group.name
- trait_dict['tissue'] = trait_ob.dataset.tissue
- trait_dict['species'] = trait_ob.dataset.group.species
- trait_dict['url'] = url_for('show_trait_page', trait_id = trait, dataset = dataset)
- trait_dict['description'] = trait_ob.description_display
- if trait_ob.dataset.type == "ProbeSet":
- trait_dict['symbol'] = trait_ob.symbol
- trait_dict['location'] = trait_ob.location_repr
- elif trait_ob.dataset.type == "Publish":
- if trait_ob.pubmed_id:
- trait_dict['pubmed_link'] = trait_ob.pubmed_link
- trait_dict['pubmed_text'] = trait_ob.pubmed_text
-
- return json.dumps([trait_dict, {key: value.value for key, value in trait_ob.data.iteritems() }])
+ trait_ob = create_trait(name=trait, dataset_name=dataset)
+ if trait_ob:
+ trait_dict = {}
+ trait_dict['name'] = trait
+ trait_dict['db'] = dataset
+ trait_dict['type'] = trait_ob.dataset.type
+ trait_dict['group'] = trait_ob.dataset.group.name
+ trait_dict['tissue'] = trait_ob.dataset.tissue
+ trait_dict['species'] = trait_ob.dataset.group.species
+ trait_dict['url'] = url_for('show_trait_page', trait_id = trait, dataset = dataset)
+ trait_dict['description'] = trait_ob.description_display
+ if trait_ob.dataset.type == "ProbeSet":
+ trait_dict['symbol'] = trait_ob.symbol
+ trait_dict['location'] = trait_ob.location_repr
+ elif trait_ob.dataset.type == "Publish":
+ if trait_ob.pubmed_id:
+ trait_dict['pubmed_link'] = trait_ob.pubmed_link
+ trait_dict['pubmed_text'] = trait_ob.pubmed_text
+
+ return json.dumps([trait_dict, {key: value.value for key, value in trait_ob.data.iteritems() }])
+ else:
+ return None
def jsonable(trait):
"""Return a dict suitable for using as json
@@ -347,90 +377,94 @@ def jsonable_table_row(trait, dataset_name, index):
else:
return dict()
+
def retrieve_trait_info(trait, dataset, get_qtl_info=False):
assert dataset, "Dataset doesn't exist"
-
+
+ resource_id = get_resource_id(dataset, trait.name)
if dataset.type == 'Publish':
- query = """
- SELECT
- PublishXRef.Id, InbredSet.InbredSetCode, Publication.PubMed_ID,
- Phenotype.Pre_publication_description, Phenotype.Post_publication_description, Phenotype.Original_description,
- Phenotype.Pre_publication_abbreviation, Phenotype.Post_publication_abbreviation, PublishXRef.mean,
- Phenotype.Lab_code, Phenotype.Submitter, Phenotype.Owner, Phenotype.Authorized_Users,
- Publication.Authors, Publication.Title, Publication.Abstract,
- Publication.Journal, Publication.Volume, Publication.Pages,
- Publication.Month, Publication.Year, PublishXRef.Sequence,
- Phenotype.Units, PublishXRef.comments
- FROM
- PublishXRef, Publication, Phenotype, PublishFreeze, InbredSet
- WHERE
- PublishXRef.Id = %s AND
- Phenotype.Id = PublishXRef.PhenotypeId AND
- Publication.Id = PublishXRef.PublicationId AND
- PublishXRef.InbredSetId = PublishFreeze.InbredSetId AND
- PublishXRef.InbredSetId = InbredSet.Id AND
- PublishFreeze.Id = %s
- """ % (trait.name, dataset.id)
+ the_url = "http://localhost:8080/run-action?resource={}&user={}&branch=data&action=view".format(resource_id, g.user_session.user_id)
+ else:
+ the_url = "http://localhost:8080/run-action?resource={}&user={}&branch=data&action=view&trait={}".format(resource_id, g.user_session.user_id, trait.name)
- logger.sql(query)
- trait_info = g.db.execute(query).fetchone()
-
-
- #XZ, 05/08/2009: Xiaodong add this block to use ProbeSet.Id to find the probeset instead of just using ProbeSet.Name
- #XZ, 05/08/2009: to avoid the problem of same probeset name from different platforms.
- elif dataset.type == 'ProbeSet':
- display_fields_string = ', ProbeSet.'.join(dataset.display_fields)
- display_fields_string = 'ProbeSet.' + display_fields_string
- query = """
- SELECT %s
- FROM ProbeSet, ProbeSetFreeze, ProbeSetXRef
- WHERE
- ProbeSetXRef.ProbeSetFreezeId = ProbeSetFreeze.Id AND
- ProbeSetXRef.ProbeSetId = ProbeSet.Id AND
- ProbeSetFreeze.Name = '%s' AND
- ProbeSet.Name = '%s'
- """ % (escape(display_fields_string),
- escape(dataset.name),
- escape(str(trait.name)))
- logger.sql(query)
- trait_info = g.db.execute(query).fetchone()
- #XZ, 05/08/2009: We also should use Geno.Id to find marker instead of just using Geno.Name
- # to avoid the problem of same marker name from different species.
- elif dataset.type == 'Geno':
- display_fields_string = string.join(dataset.display_fields,',Geno.')
- display_fields_string = 'Geno.' + display_fields_string
- query = """
- SELECT %s
- FROM Geno, GenoFreeze, GenoXRef
- WHERE
- GenoXRef.GenoFreezeId = GenoFreeze.Id AND
- GenoXRef.GenoId = Geno.Id AND
- GenoFreeze.Name = '%s' AND
- Geno.Name = '%s'
- """ % (escape(display_fields_string),
- escape(dataset.name),
- escape(trait.name))
- logger.sql(query)
- trait_info = g.db.execute(query).fetchone()
- else: #Temp type
- query = """SELECT %s FROM %s WHERE Name = %s"""
- logger.sql(query)
- trait_info = g.db.execute(query,
- (string.join(dataset.display_fields,','),
- dataset.type, trait.name)).fetchone()
+ try:
+ response = requests.get(the_url).content
+ trait_info = json.loads(response)
+ except: #ZS: I'm assuming the trait is viewable if the try fails for some reason; it should never reach this point unless the user has privileges, since that's dealt with in create_trait
+ if dataset.type == 'Publish':
+ query = """
+ SELECT
+ PublishXRef.Id, InbredSet.InbredSetCode, Publication.PubMed_ID,
+ Phenotype.Pre_publication_description, Phenotype.Post_publication_description, Phenotype.Original_description,
+ Phenotype.Pre_publication_abbreviation, Phenotype.Post_publication_abbreviation, PublishXRef.mean,
+ Phenotype.Lab_code, Phenotype.Submitter, Phenotype.Owner, Phenotype.Authorized_Users,
+ Publication.Authors, Publication.Title, Publication.Abstract,
+ Publication.Journal, Publication.Volume, Publication.Pages,
+ Publication.Month, Publication.Year, PublishXRef.Sequence,
+ Phenotype.Units, PublishXRef.comments
+ FROM
+ PublishXRef, Publication, Phenotype, PublishFreeze
+ WHERE
+ PublishXRef.Id = %s AND
+ Phenotype.Id = PublishXRef.PhenotypeId AND
+ Publication.Id = PublishXRef.PublicationId AND
+ PublishXRef.InbredSetId = PublishFreeze.InbredSetId AND
+ PublishFreeze.Id = %s
+ """ % (trait.name, dataset.id)
+
+ logger.sql(query)
+ trait_info = g.db.execute(query).fetchone()
+
+ #XZ, 05/08/2009: Xiaodong add this block to use ProbeSet.Id to find the probeset instead of just using ProbeSet.Name
+ #XZ, 05/08/2009: to avoid the problem of same probeset name from different platforms.
+ elif dataset.type == 'ProbeSet':
+ display_fields_string = ', ProbeSet.'.join(dataset.display_fields)
+ display_fields_string = 'ProbeSet.' + display_fields_string
+ query = """
+ SELECT %s
+ FROM ProbeSet, ProbeSetFreeze, ProbeSetXRef
+ WHERE
+ ProbeSetXRef.ProbeSetFreezeId = ProbeSetFreeze.Id AND
+ ProbeSetXRef.ProbeSetId = ProbeSet.Id AND
+ ProbeSetFreeze.Name = '%s' AND
+ ProbeSet.Name = '%s'
+ """ % (escape(display_fields_string),
+ escape(dataset.name),
+ escape(str(trait.name)))
+ logger.sql(query)
+ trait_info = g.db.execute(query).fetchone()
+ #XZ, 05/08/2009: We also should use Geno.Id to find marker instead of just using Geno.Name
+ # to avoid the problem of same marker name from different species.
+ elif dataset.type == 'Geno':
+ display_fields_string = string.join(dataset.display_fields,',Geno.')
+ display_fields_string = 'Geno.' + display_fields_string
+ query = """
+ SELECT %s
+ FROM Geno, GenoFreeze, GenoXRef
+ WHERE
+ GenoXRef.GenoFreezeId = GenoFreeze.Id AND
+ GenoXRef.GenoId = Geno.Id AND
+ GenoFreeze.Name = '%s' AND
+ Geno.Name = '%s'
+ """ % (escape(display_fields_string),
+ escape(dataset.name),
+ escape(trait.name))
+ logger.sql(query)
+ trait_info = g.db.execute(query).fetchone()
+ else: #Temp type
+ query = """SELECT %s FROM %s WHERE Name = %s"""
+ logger.sql(query)
+ trait_info = g.db.execute(query,
+ (string.join(dataset.display_fields,','),
+ dataset.type, trait.name)).fetchone()
if trait_info:
trait.haveinfo = True
-
- #XZ: assign SQL query result to trait attributes.
for i, field in enumerate(dataset.display_fields):
- holder = trait_info[i]
+ holder = trait_info[i]
# if isinstance(trait_info[i], basestring):
- # logger.debug("HOLDER:", holder)
- # logger.debug("HOLDER2:", holder.decode(encoding='latin1'))
- # holder = unicode(trait_info[i], "utf-8", "ignore")
- if isinstance(trait_info[i], basestring):
- holder = holder.encode('latin1')
+ # holder = unicode(holder.strip(codecs.BOM_UTF8), 'utf-8', "ignore")
+
setattr(trait, field, holder)
if dataset.type == 'Publish':
@@ -449,13 +483,6 @@ def retrieve_trait_info(trait, dataset, get_qtl_info=False):
if trait.confidential:
trait.abbreviation = trait.pre_publication_abbreviation
trait.description_display = trait.pre_publication_description
-
- #if not webqtlUtil.hasAccessToConfidentialPhenotypeTrait(
- # privilege=self.dataset.privilege,
- # userName=self.dataset.userName,
- # authorized_users=self.authorized_users):
- #
- # description = self.pre_publication_description
else:
trait.abbreviation = trait.post_publication_abbreviation
if description:
diff --git a/wqflask/base/webqtlConfig.py b/wqflask/base/webqtlConfig.py
index 018d5d54..3d86bc22 100644
--- a/wqflask/base/webqtlConfig.py
+++ b/wqflask/base/webqtlConfig.py
@@ -17,6 +17,10 @@ DEBUG = 1
#USER privilege
USERDICT = {'guest':1,'user':2, 'admin':3, 'root':4}
+#Set privileges
+SUPER_PRIVILEGES = {'data': ['no-access', 'view', 'edit'], 'metadata': ['no-access', 'view', 'edit'], 'admin': ['not-admin', 'edit-access', 'edit-admins']}
+DEFAULT_PRIVILEGES = {'data': ['no-access', 'view'], 'metadata': ['no-access', 'view'], 'admin': ['not-admin']}
+
#minimum number of informative strains
KMININFORMATIVE = 5
@@ -58,6 +62,7 @@ PROTEIN_ATLAS_URL = "http://www.proteinatlas.org/search/%s"
OPEN_TARGETS_URL = "https://genetics.opentargets.org/gene/%s"
UNIPROT_URL = "https://www.uniprot.org/uniprot/%s"
RGD_URL = "https://rgd.mcw.edu/rgdweb/elasticResults.html?term=%s&category=Gene&species=%s"
+PHENOGEN_URL = "https://phenogen.org/gene.jsp?speciesCB=Rn&auto=Y&geneTxt=%s&genomeVer=rn6&section=geneEQTL"
# Temporary storage (note that this TMPDIR can be set as an
# environment variable - use utility.tools.TEMPDIR when you
diff --git a/wqflask/maintenance/set_resource_defaults.py b/wqflask/maintenance/set_resource_defaults.py
new file mode 100644
index 00000000..54fd8e7e
--- /dev/null
+++ b/wqflask/maintenance/set_resource_defaults.py
@@ -0,0 +1,166 @@
+"""
+
+Script that sets default resource access masks for use with the DB proxy
+
+Defaults will be:
+Owner - omni_gn
+Mask - Public/non-confidential: { data: "view",
+ metadata: "view",
+ admin: "not-admin" }
+ Private/confidentia: { data: "no-access",
+ metadata: "no-access",
+ admin: "not-admin" }
+
+To run:
+./bin/genenetwork2 ~/my_settings.py -c ./wqflask/maintenance/gen_select_dataset.py
+
+"""
+
+from __future__ import print_function, division
+
+import sys
+import json
+
+# NEW: Note we prepend the current path - otherwise a guix instance of GN2 may be used instead
+sys.path.insert(0,'./')
+
+# NEW: import app to avoid a circular dependency on utility.tools
+from wqflask import app
+
+from utility import hmac
+from utility.tools import SQL_URI
+from utility.redis_tools import get_redis_conn, get_user_id, add_resource, get_resources, get_resource_info
+Redis = get_redis_conn()
+
+import MySQLdb
+
+import urlparse
+
+from utility.logger import getLogger
+logger = getLogger(__name__)
+
+def parse_db_uri():
+ """Converts a database URI to the db name, host name, user name, and password"""
+
+ parsed_uri = urlparse.urlparse(SQL_URI)
+
+ db_conn_info = dict(
+ db = parsed_uri.path[1:],
+ host = parsed_uri.hostname,
+ user = parsed_uri.username,
+ passwd = parsed_uri.password)
+
+ print(db_conn_info)
+ return db_conn_info
+
+def insert_probeset_resources(default_owner_id):
+ current_resources = Redis.hgetall("resources")
+ Cursor.execute(""" SELECT
+ ProbeSetFreeze.Id, ProbeSetFreeze.Name, ProbeSetFreeze.confidentiality, ProbeSetFreeze.public
+ FROM
+ ProbeSetFreeze""")
+
+ resource_results = Cursor.fetchall()
+ for i, resource in enumerate(resource_results):
+ resource_ob = {}
+ resource_ob['name'] = resource[1]
+ resource_ob['owner_id'] = default_owner_id
+ resource_ob['data'] = { "dataset" : str(resource[0])}
+ resource_ob['type'] = "dataset-probeset"
+ if resource[2] < 1 and resource[3] > 0:
+ resource_ob['default_mask'] = { "data": "view",
+ "metadata": "view",
+ "admin": "not-admin"}
+ else:
+ resource_ob['default_mask'] = { "data": "no-access",
+ "metadata": "no-access",
+ "admin": "not-admin"}
+ resource_ob['group_masks'] = {}
+
+ add_resource(resource_ob, update=False)
+
+def insert_publish_resources(default_owner_id):
+ current_resources = Redis.hgetall("resources")
+ Cursor.execute(""" SELECT
+ PublishXRef.Id, PublishFreeze.Id, InbredSet.InbredSetCode
+ FROM
+ PublishXRef, PublishFreeze, InbredSet, Publication
+ WHERE
+ PublishFreeze.InbredSetId = PublishXRef.InbredSetId AND
+ InbredSet.Id = PublishXRef.InbredSetId AND
+ Publication.Id = PublishXRef.PublicationId""")
+
+ resource_results = Cursor.fetchall()
+ for resource in resource_results:
+ if resource[2]:
+ resource_ob = {}
+ if resource[2]:
+ resource_ob['name'] = resource[2] + "_" + str(resource[0])
+ else:
+ resource_ob['name'] = str(resource[0])
+ resource_ob['owner_id'] = default_owner_id
+ resource_ob['data'] = { "dataset" : str(resource[1]) ,
+ "trait" : str(resource[0])}
+ resource_ob['type'] = "dataset-publish"
+ resource_ob['default_mask'] = { "data": "view",
+ "metadata": "view",
+ "admin": "not-admin"}
+
+ resource_ob['group_masks'] = {}
+
+ add_resource(resource_ob, update=False)
+ else:
+ continue
+
+def insert_geno_resources(default_owner_id):
+ current_resources = Redis.hgetall("resources")
+ Cursor.execute(""" SELECT
+ GenoFreeze.Id, GenoFreeze.ShortName, GenoFreeze.confidentiality
+ FROM
+ GenoFreeze""")
+
+ resource_results = Cursor.fetchall()
+ for i, resource in enumerate(resource_results):
+ resource_ob = {}
+ resource_ob['name'] = resource[1]
+ if resource[1] == "HET3-ITPGeno":
+ resource_ob['owner_id'] = "c5ce8c56-78a6-474f-bcaf-7129d97f56ae"
+ else:
+ resource_ob['owner_id'] = default_owner_id
+ resource_ob['data'] = { "dataset" : str(resource[0]) }
+ resource_ob['type'] = "dataset-geno"
+ if resource[2] < 1:
+ resource_ob['default_mask'] = { "data": "view",
+ "metadata": "view",
+ "admin": "not-admin"}
+ else:
+ resource_ob['default_mask'] = { "data": "no-access",
+ "metadata": "no-access",
+ "admin": "not-admin"}
+ resource_ob['group_masks'] = {}
+
+ add_resource(resource_ob, update=False)
+
+def insert_resources(default_owner_id):
+ current_resources = get_resources()
+ print("START")
+ insert_publish_resources(default_owner_id)
+ print("AFTER PUBLISH")
+ insert_geno_resources(default_owner_id)
+ print("AFTER GENO")
+ insert_probeset_resources(default_owner_id)
+ print("AFTER PROBESET")
+
+def main():
+ """Generates and outputs (as json file) the data for the main dropdown menus on the home page"""
+
+ Redis.delete("resources")
+
+ owner_id = "c5ce8c56-78a6-474f-bcaf-7129d97f56ae"
+
+ insert_resources(owner_id)
+
+if __name__ == '__main__':
+ Conn = MySQLdb.Connect(**parse_db_uri())
+ Cursor = Conn.cursor()
+ main() \ No newline at end of file
diff --git a/wqflask/utility/authentication_tools.py b/wqflask/utility/authentication_tools.py
new file mode 100644
index 00000000..f9028f32
--- /dev/null
+++ b/wqflask/utility/authentication_tools.py
@@ -0,0 +1,96 @@
+from __future__ import absolute_import, print_function, division
+
+import json
+import requests
+
+from base import data_set, webqtlConfig
+
+from utility import hmac
+from utility.redis_tools import get_redis_conn, get_resource_info, get_resource_id
+Redis = get_redis_conn()
+
+from flask import Flask, g, redirect, url_for
+
+import logging
+logger = logging.getLogger(__name__ )
+
+def check_resource_availability(dataset, trait_id=None):
+
+ #ZS: Check if super-user - we should probably come up with some way to integrate this into the proxy
+ if g.user_session.user_id in Redis.smembers("super_users"):
+ return webqtlConfig.SUPER_PRIVILEGES
+
+ response = None
+
+ #At least for now assume temporary entered traits are accessible#At least for now assume temporary entered traits are accessible
+ if type(dataset) == str:
+ return webqtlConfig.DEFAULT_PRIVILEGES
+ if dataset.type == "Temp":
+ return webqtlConfig.DEFAULT_PRIVILEGES
+
+ resource_id = get_resource_id(dataset, trait_id)
+
+ if resource_id:
+ resource_info = get_resource_info(resource_id)
+ if not resource_info:
+ return webqtlConfig.DEFAULT_PRIVILEGES
+ else:
+ return response #ZS: Need to substitute in something that creates the resource in Redis later
+
+ the_url = "http://localhost:8080/available?resource={}&user={}".format(resource_id, g.user_session.user_id)
+ try:
+ response = json.loads(requests.get(the_url).content)
+ except:
+ response = resource_info['default_mask']
+
+ if response:
+ return response
+ else: #ZS: No idea how this would happen, but just in case
+ return False
+
+def check_admin(resource_id=None):
+ the_url = "http://localhost:8080/available?resource={}&user={}".format(resource_id, g.user_session.user_id)
+ try:
+ response = json.loads(requests.get(the_url).content)['admin']
+ except:
+ response = resource_info['default_mask']['admin']
+
+ if 'edit-admins' in response:
+ return "edit-admins"
+ elif 'edit-access' in response:
+ return "edit-access"
+ else:
+ return "not-admin"
+
+def check_owner(dataset=None, trait_id=None, resource_id=None):
+ if resource_id:
+ resource_info = get_resource_info(resource_id)
+ if g.user_session.user_id == resource_info['owner_id']:
+ return resource_id
+ else:
+ resource_id = get_resource_id(dataset, trait_id)
+ if resource_id:
+ resource_info = get_resource_info(resource_id)
+ if g.user_session.user_id == resource_info['owner_id']:
+ return resource_id
+
+ return False
+
+def check_owner_or_admin(dataset=None, trait_id=None, resource_id=None):
+ if not resource_id:
+ if dataset.type == "Temp":
+ return "not-admin"
+ else:
+ resource_id = get_resource_id(dataset, trait_id)
+
+ if g.user_session.user_id in Redis.smembers("super_users"):
+ return "owner"
+
+ resource_info = get_resource_info(resource_id)
+ if resource_info:
+ if g.user_session.user_id == resource_info['owner_id']:
+ return "owner"
+ else:
+ return check_admin(resource_id)
+
+ return "not-admin" \ No newline at end of file
diff --git a/wqflask/utility/helper_functions.py b/wqflask/utility/helper_functions.py
index e7c04fef..9ce809b6 100644
--- a/wqflask/utility/helper_functions.py
+++ b/wqflask/utility/helper_functions.py
@@ -1,7 +1,7 @@
from __future__ import absolute_import, print_function, division
-from base.trait import GeneralTrait
from base import data_set
+from base.trait import create_trait
from base.species import TheSpecies
from utility import hmac
@@ -11,7 +11,6 @@ from flask import Flask, g
import logging
logger = logging.getLogger(__name__ )
-
def get_species_dataset_trait(self, start_vars):
#assert type(read_genotype) == type(bool()), "Expecting boolean value for read_genotype"
if "temp_trait" in start_vars.keys():
@@ -24,7 +23,7 @@ def get_species_dataset_trait(self, start_vars):
logger.debug("After creating dataset")
self.species = TheSpecies(dataset=self.dataset)
logger.debug("After creating species")
- self.this_trait = GeneralTrait(dataset=self.dataset,
+ self.this_trait = create_trait(dataset=self.dataset,
name=start_vars['trait_id'],
cellid=None,
get_qtl_info=True)
@@ -34,7 +33,6 @@ def get_species_dataset_trait(self, start_vars):
#self.dataset.group.read_genotype_file()
#self.genotype = self.dataset.group.genotype
-
def get_trait_db_obs(self, trait_db_list):
if isinstance(trait_db_list, basestring):
trait_db_list = trait_db_list.split(",")
@@ -49,10 +47,11 @@ def get_trait_db_obs(self, trait_db_list):
dataset_ob = data_set.create_dataset(dataset_name=dataset_name, dataset_type="Temp", group_name=trait_name.split("_")[2])
else:
dataset_ob = data_set.create_dataset(dataset_name)
- trait_ob = GeneralTrait(dataset=dataset_ob,
+ trait_ob = create_trait(dataset=dataset_ob,
name=trait_name,
cellid=None)
- self.trait_list.append((trait_ob, dataset_ob))
+ if trait_ob:
+ self.trait_list.append((trait_ob, dataset_ob))
def get_species_groups():
diff --git a/wqflask/utility/hmac.py b/wqflask/utility/hmac.py
index d8a0eace..b08be97e 100644
--- a/wqflask/utility/hmac.py
+++ b/wqflask/utility/hmac.py
@@ -3,6 +3,8 @@ from __future__ import print_function, division, absolute_import
import hmac
import hashlib
+from flask import url_for
+
from wqflask import app
def hmac_creation(stringy):
diff --git a/wqflask/utility/redis_tools.py b/wqflask/utility/redis_tools.py
index 0754e16f..6c912a23 100644
--- a/wqflask/utility/redis_tools.py
+++ b/wqflask/utility/redis_tools.py
@@ -1,17 +1,26 @@
from __future__ import print_function, division, absolute_import
+import uuid
import simplejson as json
+import datetime
import redis # used for collections
-Redis = redis.StrictRedis()
import logging
from flask import (render_template, flash)
+from utility import hmac
+
from utility.logger import getLogger
logger = getLogger(__name__)
+def get_redis_conn():
+ Redis = redis.StrictRedis(port=6379)
+ return Redis
+
+Redis = get_redis_conn()
+
def is_redis_available():
try:
Redis.ping()
@@ -21,6 +30,7 @@ def is_redis_available():
def get_user_id(column_name, column_value):
user_list = Redis.hgetall("users")
+ key_list = []
for key in user_list:
user_ob = json.loads(user_list[key])
if column_name in user_ob and user_ob[column_name] == column_value:
@@ -42,6 +52,30 @@ def get_user_by_unique_column(column_name, column_value):
return item_details
+def get_users_like_unique_column(column_name, column_value):
+ """
+ Like previous function, but this only checks if the input is a subset of a field and can return multiple results
+ """
+ matched_users = []
+
+ if column_value != "":
+ user_list = Redis.hgetall("users")
+ if column_name != "user_id":
+ for key in user_list:
+ user_ob = json.loads(user_list[key])
+ if "user_id" not in user_ob:
+ set_user_attribute(key, "user_id", key)
+ user_ob["user_id"] = key
+ if column_name in user_ob:
+ if column_value in user_ob[column_name]:
+ matched_users.append(user_ob)
+ else:
+ matched_users.append(json.loads(user_list[column_value]))
+
+ return matched_users
+
+# def search_users_by_unique_column(column_name, column_value):
+
def set_user_attribute(user_id, column_name, column_value):
user_info = json.loads(Redis.hget("users", user_id))
user_info[column_name] = column_value
@@ -70,11 +104,209 @@ def check_verification_code(code):
email_address = None
user_details = None
email_address = Redis.hget("verification_codes", code)
- return email_address
if email_address:
user_details = get_user_by_unique_column('email_address', email_address)
- return user_details
+ if user_details:
+ return user_details
+ else:
+ return None
+ else:
+ return None
+
+def get_user_groups(user_id):
+ #ZS: Get the groups where a user is an admin or a member and return lists corresponding to those two sets of groups
+ admin_group_ids = [] #ZS: Group IDs where user is an admin
+ user_group_ids = [] #ZS: Group IDs where user is a regular user
+ groups_list = Redis.hgetall("groups")
+ for key in groups_list:
+ try:
+ group_ob = json.loads(groups_list[key])
+ group_admins = set(group_ob['admins'])
+ group_members = set(group_ob['members'])
+ if user_id in group_admins:
+ admin_group_ids.append(group_ob['id'])
+ elif user_id in group_members:
+ user_group_ids.append(group_ob['id'])
+ else:
+ continue
+ except:
+ continue
+
+ admin_groups = []
+ user_groups = []
+ for the_id in admin_group_ids:
+ admin_groups.append(get_group_info(the_id))
+ for the_id in user_group_ids:
+ user_groups.append(get_group_info(the_id))
+
+ return admin_groups, user_groups
+
+def get_group_info(group_id):
+ group_json = Redis.hget("groups", group_id)
+ group_info = None
+ if group_json:
+ group_info = json.loads(group_json)
+
+ return group_info
+
+def get_group_by_unique_column(column_name, column_value):
+ """ Get group by column; not sure if there's a faster way to do this """
+
+ matched_groups = []
+
+ all_group_list = Redis.hgetall("groups")
+ for key in all_group_list:
+ group_info = json.loads(all_group_list[key])
+ if column_name == "admins" or column_name == "members": #ZS: Since these fields are lists, search in the list
+ if column_value in group_info[column_name]:
+ matched_groups.append(group_info)
+ else:
+ if group_info[column_name] == column_value:
+ matched_groups.append(group_info)
+
+ return matched_groups
+
+def get_groups_like_unique_column(column_name, column_value):
+ """
+ Like previous function, but this only checks if the input is a subset of a field and can return multiple results
+ """
+ matched_groups = []
+
+ if column_value != "":
+ group_list = Redis.hgetall("groups")
+ if column_name != "group_id":
+ for key in group_list:
+ group_info = json.loads(group_list[key])
+ if column_name == "admins" or column_name == "members": #ZS: Since these fields are lists, search in the list
+ if column_value in group_info[column_name]:
+ matched_groups.append(group_info)
+ else:
+ if column_name in group_info:
+ if column_value in group_info[column_name]:
+ matched_groups.append(group_info)
+ else:
+ matched_groups.append(json.loads(group_list[column_value]))
+
+ return matched_groups
+
+def create_group(admin_user_ids, member_user_ids = [], group_name = "Default Group Name"):
+ group_id = str(uuid.uuid4())
+ new_group = {
+ "id" : group_id,
+ "admins": admin_user_ids,
+ "members" : member_user_ids,
+ "name" : group_name,
+ "created_timestamp": datetime.datetime.utcnow().strftime('%b %d %Y %I:%M%p'),
+ "changed_timestamp": datetime.datetime.utcnow().strftime('%b %d %Y %I:%M%p')
+ }
+
+ Redis.hset("groups", group_id, json.dumps(new_group))
+
+ return new_group
+
+def delete_group(user_id, group_id):
+ #ZS: If user is an admin of a group, remove it from the groups hash
+ group_info = get_group_info(group_id)
+ if user_id in group_info["admins"]:
+ Redis.hdel("groups", group_id)
+ return get_user_groups(user_id)
+ else:
+ None
+
+def add_users_to_group(user_id, group_id, user_emails = [], admins = False): #ZS "admins" is just to indicate whether the users should be added to the groups admins or regular users set
+ group_info = get_group_info(group_id)
+ if user_id in group_info["admins"]: #ZS: Just to make sure that the user is an admin for the group, even though they shouldn't be able to reach this point unless they are
+ if admins:
+ group_users = set(group_info["admins"])
+ else:
+ group_users = set(group_info["members"])
+
+ for email in user_emails:
+ user_id = get_user_id("email_address", email)
+ group_users.add(user_id)
+
+ if admins:
+ group_info["admins"] = list(group_users)
+ else:
+ group_info["members"] = list(group_users)
+
+ group_info["changed_timestamp"] = datetime.datetime.utcnow().strftime('%b %d %Y %I:%M%p')
+ Redis.hset("groups", group_id, json.dumps(group_info))
+ return group_info
+ else:
+ return None
+
+def remove_users_from_group(user_id, users_to_remove_ids, group_id, user_type = "members"): #ZS: User type is because I assume admins can remove other admins
+ group_info = get_group_info(group_id)
+
+ if user_id in group_info["admins"]:
+ users_to_remove_set = set(users_to_remove_ids)
+ if user_type == "admins" and user_id in users_to_remove_set: #ZS: Make sure an admin can't remove themselves from a group, since I imagine we don't want groups to be able to become admin-less
+ users_to_remove_set.remove(user_id)
+ group_users = set(group_info[user_type])
+ group_users -= users_to_remove_set
+ group_info[user_type] = list(group_users)
+ group_info["changed_timestamp"] = datetime.datetime.utcnow().strftime('%b %d %Y %I:%M%p')
+ Redis.hset("groups", group_id, json.dumps(group_info))
+
+def change_group_name(user_id, group_id, new_name):
+ group_info = get_group_info(group_id)
+ if user_id in group_info["admins"]:
+ group_info["name"] = new_name
+ Redis.hset("groups", group_id, json.dumps(group_info))
+ return group_info
+ else:
+ return None
+
+def get_resources():
+ resource_list = Redis.hgetall("resources")
+ return resource_list
+
+def get_resource_id(dataset, trait_id=None):
+ if dataset.type == "Publish":
+ if trait_id:
+ resource_id = hmac.hmac_creation("{}:{}:{}".format('dataset-publish', dataset.id, trait_id))
+ else:
+ return False
+ elif dataset.type == "ProbeSet":
+ resource_id = hmac.hmac_creation("{}:{}".format('dataset-probeset', dataset.id))
+ elif dataset.type == "Geno":
+ resource_id = hmac.hmac_creation("{}:{}".format('dataset-geno', dataset.id))
+ else:
+ return False
+
+ return resource_id
+
+def get_resource_info(resource_id):
+ resource_info = Redis.hget("resources", resource_id)
+ if resource_info:
+ return json.loads(resource_info)
else:
return None
- flash("Invalid code: Password reset code does not exist or might have expired!", "error")
+
+def add_resource(resource_info, update=True):
+ if 'trait' in resource_info['data']:
+ resource_id = hmac.hmac_creation('{}:{}:{}'.format(str(resource_info['type']), str(resource_info['data']['dataset']), str(resource_info['data']['trait'])))
+ else:
+ resource_id = hmac.hmac_creation('{}:{}'.format(str(resource_info['type']), str(resource_info['data']['dataset'])))
+
+ if not Redis.hexists("resources", resource_id):
+ Redis.hset("resources", resource_id, json.dumps(resource_info))
+
+ return resource_info
+
+def add_access_mask(resource_id, group_id, access_mask):
+ the_resource = get_resource_info(resource_id)
+ the_resource['group_masks'][group_id] = access_mask
+
+ Redis.hset("resources", resource_id, json.dumps(the_resource))
+
+ return the_resource
+
+def change_resource_owner(resource_id, new_owner_id):
+ the_resource= get_resource_info(resource_id)
+ the_resource['owner_id'] = new_owner_id
+
+ Redis.delete("resource")
+ Redis.hset("resources", resource_id, json.dumps(the_resource)) \ No newline at end of file
diff --git a/wqflask/utility/tools.py b/wqflask/utility/tools.py
index 2914d354..89d88516 100644
--- a/wqflask/utility/tools.py
+++ b/wqflask/utility/tools.py
@@ -292,7 +292,7 @@ JS_GUIX_PATH = get_setting("JS_GUIX_PATH")
assert_dir(JS_GUIX_PATH)
assert_dir(JS_GUIX_PATH+'/cytoscape-panzoom')
-CSS_PATH = "UNKNOWN"
+CSS_PATH = JS_GUIX_PATH # The CSS is bundled together with the JS
# assert_dir(JS_PATH)
JS_TWITTER_POST_FETCHER_PATH = get_setting("JS_TWITTER_POST_FETCHER_PATH",js_path("javascript-twitter-post-fetcher"))
diff --git a/wqflask/wqflask/api/correlation.py b/wqflask/wqflask/api/correlation.py
index 66eb94ac..7f5312c1 100644
--- a/wqflask/wqflask/api/correlation.py
+++ b/wqflask/wqflask/api/correlation.py
@@ -1,237 +1,237 @@
-from __future__ import absolute_import, division, print_function
-
-import collections
-
-import scipy
-
-from MySQLdb import escape_string as escape
-
-from flask import g
-
-from base import data_set
-from base.trait import GeneralTrait, retrieve_sample_data
-
-from wqflask.correlation.show_corr_results import generate_corr_json
-from wqflask.correlation import correlation_functions
-
-from utility import webqtlUtil, helper_functions, corr_result_helpers
-from utility.benchmark import Bench
-
-import utility.logger
-logger = utility.logger.getLogger(__name__ )
-
-def do_correlation(start_vars):
- assert('db' in start_vars)
- assert('target_db' in start_vars)
- assert('trait_id' in start_vars)
-
- this_dataset = data_set.create_dataset(dataset_name = start_vars['db'])
- target_dataset = data_set.create_dataset(dataset_name = start_vars['target_db'])
- this_trait = GeneralTrait(dataset = this_dataset, name = start_vars['trait_id'])
- this_trait = retrieve_sample_data(this_trait, this_dataset)
-
- corr_params = init_corr_params(start_vars)
-
- corr_results = calculate_results(this_trait, this_dataset, target_dataset, corr_params)
- #corr_results = collections.OrderedDict(sorted(corr_results.items(), key=lambda t: -abs(t[1][0])))
-
- final_results = []
- for _trait_counter, trait in enumerate(corr_results.keys()[:corr_params['return_count']]):
- if corr_params['type'] == "tissue":
- [sample_r, num_overlap, sample_p, symbol] = corr_results[trait]
- result_dict = {
- "trait" : trait,
- "sample_r" : sample_r,
- "#_strains" : num_overlap,
- "p_value" : sample_p,
- "symbol" : symbol
- }
- elif corr_params['type'] == "literature" or corr_params['type'] == "lit":
- [gene_id, sample_r] = corr_results[trait]
- result_dict = {
- "trait" : trait,
- "sample_r" : sample_r,
- "gene_id" : gene_id
- }
- else:
- [sample_r, sample_p, num_overlap] = corr_results[trait]
- result_dict = {
- "trait" : trait,
- "sample_r" : sample_r,
- "#_strains" : num_overlap,
- "p_value" : sample_p
- }
-
- final_results.append(result_dict)
-
- # json_corr_results = generate_corr_json(final_corr_results, this_trait, this_dataset, target_dataset, for_api = True)
-
- return final_results
-
-def calculate_results(this_trait, this_dataset, target_dataset, corr_params):
- corr_results = {}
-
- target_dataset.get_trait_data()
-
- if corr_params['type'] == "tissue":
- trait_symbol_dict = this_dataset.retrieve_genes("Symbol")
- corr_results = do_tissue_correlation_for_all_traits(this_trait, trait_symbol_dict, corr_params)
- sorted_results = collections.OrderedDict(sorted(corr_results.items(),
- key=lambda t: -abs(t[1][1])))
- elif corr_params['type'] == "literature" or corr_params['type'] == "lit": #ZS: Just so a user can use either "lit" or "literature"
- trait_geneid_dict = this_dataset.retrieve_genes("GeneId")
- corr_results = do_literature_correlation_for_all_traits(this_trait, this_dataset, trait_geneid_dict, corr_params)
- sorted_results = collections.OrderedDict(sorted(corr_results.items(),
- key=lambda t: -abs(t[1][1])))
- else:
- for target_trait, target_vals in target_dataset.trait_data.iteritems():
- result = get_sample_r_and_p_values(this_trait, this_dataset, target_vals, target_dataset, corr_params['type'])
- if result is not None:
- corr_results[target_trait] = result
-
- sorted_results = collections.OrderedDict(sorted(corr_results.items(), key=lambda t: -abs(t[1][0])))
-
- return sorted_results
-
-def do_tissue_correlation_for_all_traits(this_trait, trait_symbol_dict, corr_params, tissue_dataset_id=1):
- #Gets tissue expression values for the primary trait
- primary_trait_tissue_vals_dict = correlation_functions.get_trait_symbol_and_tissue_values(symbol_list = [this_trait.symbol])
-
- if this_trait.symbol.lower() in primary_trait_tissue_vals_dict:
- primary_trait_tissue_values = primary_trait_tissue_vals_dict[this_trait.symbol.lower()]
-
- corr_result_tissue_vals_dict = correlation_functions.get_trait_symbol_and_tissue_values(symbol_list=trait_symbol_dict.values())
-
- tissue_corr_data = {}
- for trait, symbol in trait_symbol_dict.iteritems():
- if symbol and symbol.lower() in corr_result_tissue_vals_dict:
- this_trait_tissue_values = corr_result_tissue_vals_dict[symbol.lower()]
-
- result = correlation_functions.cal_zero_order_corr_for_tiss(primary_trait_tissue_values,
- this_trait_tissue_values,
- corr_params['method'])
-
- tissue_corr_data[trait] = [result[0], result[1], result[2], symbol]
-
- return tissue_corr_data
-
-def do_literature_correlation_for_all_traits(this_trait, target_dataset, trait_geneid_dict, corr_params):
- input_trait_mouse_gene_id = convert_to_mouse_gene_id(target_dataset.group.species.lower(), this_trait.geneid)
-
- lit_corr_data = {}
- for trait, gene_id in trait_geneid_dict.iteritems():
- mouse_gene_id = convert_to_mouse_gene_id(target_dataset.group.species.lower(), gene_id)
-
- if mouse_gene_id and str(mouse_gene_id).find(";") == -1:
- result = g.db.execute(
- """SELECT value
- FROM LCorrRamin3
- WHERE GeneId1='%s' and
- GeneId2='%s'
- """ % (escape(mouse_gene_id), escape(input_trait_mouse_gene_id))
- ).fetchone()
- if not result:
- result = g.db.execute("""SELECT value
- FROM LCorrRamin3
- WHERE GeneId2='%s' and
- GeneId1='%s'
- """ % (escape(mouse_gene_id), escape(input_trait_mouse_gene_id))
- ).fetchone()
- if result:
- lit_corr = result.value
- lit_corr_data[trait] = [gene_id, lit_corr]
- else:
- lit_corr_data[trait] = [gene_id, 0]
- else:
- lit_corr_data[trait] = [gene_id, 0]
-
- return lit_corr_data
-
-def get_sample_r_and_p_values(this_trait, this_dataset, target_vals, target_dataset, type):
- """
- Calculates the sample r (or rho) and p-value
-
- Given a primary trait and a target trait's sample values,
- calculates either the pearson r or spearman rho and the p-value
- using the corresponding scipy functions.
- """
-
- this_trait_vals = []
- shared_target_vals = []
- for i, sample in enumerate(target_dataset.group.samplelist):
- if sample in this_trait.data:
- this_sample_value = this_trait.data[sample].value
- target_sample_value = target_vals[i]
- this_trait_vals.append(this_sample_value)
- shared_target_vals.append(target_sample_value)
-
- this_trait_vals, shared_target_vals, num_overlap = corr_result_helpers.normalize_values(this_trait_vals, shared_target_vals)
-
- if type == 'pearson':
- sample_r, sample_p = scipy.stats.pearsonr(this_trait_vals, shared_target_vals)
- else:
- sample_r, sample_p = scipy.stats.spearmanr(this_trait_vals, shared_target_vals)
-
- if num_overlap > 5:
- if scipy.isnan(sample_r):
- return None
- else:
- return [sample_r, sample_p, num_overlap]
-
-def convert_to_mouse_gene_id(species=None, gene_id=None):
- """If the species is rat or human, translate the gene_id to the mouse geneid
-
- If there is no input gene_id or there's no corresponding mouse gene_id, return None
-
- """
- if not gene_id:
- return None
-
- mouse_gene_id = None
-
- if species == 'mouse':
- mouse_gene_id = gene_id
-
- elif species == 'rat':
-
- query = """SELECT mouse
- FROM GeneIDXRef
- WHERE rat='%s'""" % escape(gene_id)
-
- result = g.db.execute(query).fetchone()
- if result != None:
- mouse_gene_id = result.mouse
-
- elif species == 'human':
-
- query = """SELECT mouse
- FROM GeneIDXRef
- WHERE human='%s'""" % escape(gene_id)
-
- result = g.db.execute(query).fetchone()
- if result != None:
- mouse_gene_id = result.mouse
-
- return mouse_gene_id
-
-def init_corr_params(start_vars):
- method = "pearson"
- if 'method' in start_vars:
- method = start_vars['method']
-
- type = "sample"
- if 'type' in start_vars:
- type = start_vars['type']
-
- return_count = 500
- if 'return_count' in start_vars:
- assert(start_vars['return_count'].isdigit())
- return_count = int(start_vars['return_count'])
-
- corr_params = {
- 'method' : method,
- 'type' : type,
- 'return_count' : return_count
- }
-
+from __future__ import absolute_import, division, print_function
+
+import collections
+
+import scipy
+
+from MySQLdb import escape_string as escape
+
+from flask import g
+
+from base import data_set
+from base.trait import create_trait, retrieve_sample_data
+
+from wqflask.correlation.show_corr_results import generate_corr_json
+from wqflask.correlation import correlation_functions
+
+from utility import webqtlUtil, helper_functions, corr_result_helpers
+from utility.benchmark import Bench
+
+import utility.logger
+logger = utility.logger.getLogger(__name__ )
+
+def do_correlation(start_vars):
+ assert('db' in start_vars)
+ assert('target_db' in start_vars)
+ assert('trait_id' in start_vars)
+
+ this_dataset = data_set.create_dataset(dataset_name = start_vars['db'])
+ target_dataset = data_set.create_dataset(dataset_name = start_vars['target_db'])
+ this_trait = create_trait(dataset = this_dataset, name = start_vars['trait_id'])
+ this_trait = retrieve_sample_data(this_trait, this_dataset)
+
+ corr_params = init_corr_params(start_vars)
+
+ corr_results = calculate_results(this_trait, this_dataset, target_dataset, corr_params)
+ #corr_results = collections.OrderedDict(sorted(corr_results.items(), key=lambda t: -abs(t[1][0])))
+
+ final_results = []
+ for _trait_counter, trait in enumerate(corr_results.keys()[:corr_params['return_count']]):
+ if corr_params['type'] == "tissue":
+ [sample_r, num_overlap, sample_p, symbol] = corr_results[trait]
+ result_dict = {
+ "trait" : trait,
+ "sample_r" : sample_r,
+ "#_strains" : num_overlap,
+ "p_value" : sample_p,
+ "symbol" : symbol
+ }
+ elif corr_params['type'] == "literature" or corr_params['type'] == "lit":
+ [gene_id, sample_r] = corr_results[trait]
+ result_dict = {
+ "trait" : trait,
+ "sample_r" : sample_r,
+ "gene_id" : gene_id
+ }
+ else:
+ [sample_r, sample_p, num_overlap] = corr_results[trait]
+ result_dict = {
+ "trait" : trait,
+ "sample_r" : sample_r,
+ "#_strains" : num_overlap,
+ "p_value" : sample_p
+ }
+
+ final_results.append(result_dict)
+
+ # json_corr_results = generate_corr_json(final_corr_results, this_trait, this_dataset, target_dataset, for_api = True)
+
+ return final_results
+
+def calculate_results(this_trait, this_dataset, target_dataset, corr_params):
+ corr_results = {}
+
+ target_dataset.get_trait_data()
+
+ if corr_params['type'] == "tissue":
+ trait_symbol_dict = this_dataset.retrieve_genes("Symbol")
+ corr_results = do_tissue_correlation_for_all_traits(this_trait, trait_symbol_dict, corr_params)
+ sorted_results = collections.OrderedDict(sorted(corr_results.items(),
+ key=lambda t: -abs(t[1][1])))
+ elif corr_params['type'] == "literature" or corr_params['type'] == "lit": #ZS: Just so a user can use either "lit" or "literature"
+ trait_geneid_dict = this_dataset.retrieve_genes("GeneId")
+ corr_results = do_literature_correlation_for_all_traits(this_trait, this_dataset, trait_geneid_dict, corr_params)
+ sorted_results = collections.OrderedDict(sorted(corr_results.items(),
+ key=lambda t: -abs(t[1][1])))
+ else:
+ for target_trait, target_vals in target_dataset.trait_data.iteritems():
+ result = get_sample_r_and_p_values(this_trait, this_dataset, target_vals, target_dataset, corr_params['type'])
+ if result is not None:
+ corr_results[target_trait] = result
+
+ sorted_results = collections.OrderedDict(sorted(corr_results.items(), key=lambda t: -abs(t[1][0])))
+
+ return sorted_results
+
+def do_tissue_correlation_for_all_traits(this_trait, trait_symbol_dict, corr_params, tissue_dataset_id=1):
+ #Gets tissue expression values for the primary trait
+ primary_trait_tissue_vals_dict = correlation_functions.get_trait_symbol_and_tissue_values(symbol_list = [this_trait.symbol])
+
+ if this_trait.symbol.lower() in primary_trait_tissue_vals_dict:
+ primary_trait_tissue_values = primary_trait_tissue_vals_dict[this_trait.symbol.lower()]
+
+ corr_result_tissue_vals_dict = correlation_functions.get_trait_symbol_and_tissue_values(symbol_list=trait_symbol_dict.values())
+
+ tissue_corr_data = {}
+ for trait, symbol in trait_symbol_dict.iteritems():
+ if symbol and symbol.lower() in corr_result_tissue_vals_dict:
+ this_trait_tissue_values = corr_result_tissue_vals_dict[symbol.lower()]
+
+ result = correlation_functions.cal_zero_order_corr_for_tiss(primary_trait_tissue_values,
+ this_trait_tissue_values,
+ corr_params['method'])
+
+ tissue_corr_data[trait] = [result[0], result[1], result[2], symbol]
+
+ return tissue_corr_data
+
+def do_literature_correlation_for_all_traits(this_trait, target_dataset, trait_geneid_dict, corr_params):
+ input_trait_mouse_gene_id = convert_to_mouse_gene_id(target_dataset.group.species.lower(), this_trait.geneid)
+
+ lit_corr_data = {}
+ for trait, gene_id in trait_geneid_dict.iteritems():
+ mouse_gene_id = convert_to_mouse_gene_id(target_dataset.group.species.lower(), gene_id)
+
+ if mouse_gene_id and str(mouse_gene_id).find(";") == -1:
+ result = g.db.execute(
+ """SELECT value
+ FROM LCorrRamin3
+ WHERE GeneId1='%s' and
+ GeneId2='%s'
+ """ % (escape(mouse_gene_id), escape(input_trait_mouse_gene_id))
+ ).fetchone()
+ if not result:
+ result = g.db.execute("""SELECT value
+ FROM LCorrRamin3
+ WHERE GeneId2='%s' and
+ GeneId1='%s'
+ """ % (escape(mouse_gene_id), escape(input_trait_mouse_gene_id))
+ ).fetchone()
+ if result:
+ lit_corr = result.value
+ lit_corr_data[trait] = [gene_id, lit_corr]
+ else:
+ lit_corr_data[trait] = [gene_id, 0]
+ else:
+ lit_corr_data[trait] = [gene_id, 0]
+
+ return lit_corr_data
+
+def get_sample_r_and_p_values(this_trait, this_dataset, target_vals, target_dataset, type):
+ """
+ Calculates the sample r (or rho) and p-value
+
+ Given a primary trait and a target trait's sample values,
+ calculates either the pearson r or spearman rho and the p-value
+ using the corresponding scipy functions.
+ """
+
+ this_trait_vals = []
+ shared_target_vals = []
+ for i, sample in enumerate(target_dataset.group.samplelist):
+ if sample in this_trait.data:
+ this_sample_value = this_trait.data[sample].value
+ target_sample_value = target_vals[i]
+ this_trait_vals.append(this_sample_value)
+ shared_target_vals.append(target_sample_value)
+
+ this_trait_vals, shared_target_vals, num_overlap = corr_result_helpers.normalize_values(this_trait_vals, shared_target_vals)
+
+ if type == 'pearson':
+ sample_r, sample_p = scipy.stats.pearsonr(this_trait_vals, shared_target_vals)
+ else:
+ sample_r, sample_p = scipy.stats.spearmanr(this_trait_vals, shared_target_vals)
+
+ if num_overlap > 5:
+ if scipy.isnan(sample_r):
+ return None
+ else:
+ return [sample_r, sample_p, num_overlap]
+
+def convert_to_mouse_gene_id(species=None, gene_id=None):
+ """If the species is rat or human, translate the gene_id to the mouse geneid
+
+ If there is no input gene_id or there's no corresponding mouse gene_id, return None
+
+ """
+ if not gene_id:
+ return None
+
+ mouse_gene_id = None
+
+ if species == 'mouse':
+ mouse_gene_id = gene_id
+
+ elif species == 'rat':
+
+ query = """SELECT mouse
+ FROM GeneIDXRef
+ WHERE rat='%s'""" % escape(gene_id)
+
+ result = g.db.execute(query).fetchone()
+ if result != None:
+ mouse_gene_id = result.mouse
+
+ elif species == 'human':
+
+ query = """SELECT mouse
+ FROM GeneIDXRef
+ WHERE human='%s'""" % escape(gene_id)
+
+ result = g.db.execute(query).fetchone()
+ if result != None:
+ mouse_gene_id = result.mouse
+
+ return mouse_gene_id
+
+def init_corr_params(start_vars):
+ method = "pearson"
+ if 'method' in start_vars:
+ method = start_vars['method']
+
+ type = "sample"
+ if 'type' in start_vars:
+ type = start_vars['type']
+
+ return_count = 500
+ if 'return_count' in start_vars:
+ assert(start_vars['return_count'].isdigit())
+ return_count = int(start_vars['return_count'])
+
+ corr_params = {
+ 'method' : method,
+ 'type' : type,
+ 'return_count' : return_count
+ }
+
return corr_params \ No newline at end of file
diff --git a/wqflask/wqflask/api/gen_menu.py b/wqflask/wqflask/api/gen_menu.py
index c7bcb65d..cfce0c8e 100644
--- a/wqflask/wqflask/api/gen_menu.py
+++ b/wqflask/wqflask/api/gen_menu.py
@@ -68,12 +68,12 @@ def get_types(groups):
types[species] = {}
for group_name, _group_full_name, _family_name in group_dict:
if phenotypes_exist(group_name):
- types[species][group_name] = [("Phenotypes", "Phenotypes")]
+ types[species][group_name] = [("Phenotypes", "Traits and Cofactors", "Phenotypes")]
if genotypes_exist(group_name):
if group_name in types[species]:
- types[species][group_name] += [("Genotypes", "Genotypes")]
+ types[species][group_name] += [("Genotypes", "DNA Markers and SNPs", "Genotypes")]
else:
- types[species][group_name] = [("Genotypes", "Genotypes")]
+ types[species][group_name] = [("Genotypes", "DNA Markers and SNPs", "Genotypes")]
if group_name in types[species]:
types_list = build_types(species, group_name)
if len(types_list) > 0:
@@ -126,9 +126,7 @@ def build_types(species, group):
InbredSet.Name = '{1}' AND
ProbeFreeze.TissueId = Tissue.Id AND
ProbeFreeze.InbredSetId = InbredSet.Id AND
- ProbeSetFreeze.ProbeFreezeId = ProbeFreeze.Id AND
- ProbeSetFreeze.public > 0 AND
- ProbeSetFreeze.confidentiality < 1
+ ProbeSetFreeze.ProbeFreezeId = ProbeFreeze.Id
ORDER BY Tissue.Name""".format(species, group)
results = []
@@ -136,7 +134,7 @@ def build_types(species, group):
if len(result):
these_datasets = build_datasets(species, group, result[0])
if len(these_datasets) > 0:
- results.append([str(result[0]), str(result[0])])
+ results.append([str(result[0]), str(result[0]), "Molecular Trait Datasets"])
return results
@@ -194,9 +192,7 @@ def build_datasets(species, group, type_name):
FROM InfoFiles, GenoFreeze, InbredSet
WHERE InbredSet.Name = '{}' AND
GenoFreeze.InbredSetId = InbredSet.Id AND
- InfoFiles.InfoPageName = GenoFreeze.ShortName AND
- GenoFreeze.public > 0 AND
- GenoFreeze.confidentiality < 1
+ InfoFiles.InfoPageName = GenoFreeze.ShortName
ORDER BY GenoFreeze.CreateTime DESC""".format(group)).fetchone()
if results != None:
@@ -214,8 +210,7 @@ def build_datasets(species, group, type_name):
Species.Id = InbredSet.SpeciesId AND
InbredSet.Name = '{1}' AND
ProbeSetFreeze.ProbeFreezeId = ProbeFreeze.Id and Tissue.Name = '{2}' AND
- ProbeFreeze.TissueId = Tissue.Id and ProbeFreeze.InbredSetId = InbredSet.Id AND
- ProbeSetFreeze.confidentiality < 1 and ProbeSetFreeze.public > 0
+ ProbeFreeze.TissueId = Tissue.Id and ProbeFreeze.InbredSetId = InbredSet.Id
ORDER BY ProbeSetFreeze.CreateTime DESC""".format(species, group, type_name)).fetchall()
datasets = []
diff --git a/wqflask/wqflask/api/mapping.py b/wqflask/wqflask/api/mapping.py
index d830cefc..92c27c9b 100644
--- a/wqflask/wqflask/api/mapping.py
+++ b/wqflask/wqflask/api/mapping.py
@@ -4,7 +4,7 @@ import string
from base import data_set
from base import webqtlConfig
-from base.trait import GeneralTrait, retrieve_sample_data
+from base.trait import create_trait, retrieve_sample_data
from utility import helper_functions
from wqflask.marker_regression import gemma_mapping, rqtl_mapping, qtlreaper_mapping, plink_mapping
@@ -18,7 +18,7 @@ def do_mapping_for_api(start_vars):
dataset = data_set.create_dataset(dataset_name = start_vars['db'])
dataset.group.get_markers()
- this_trait = GeneralTrait(dataset = dataset, name = start_vars['trait_id'])
+ this_trait = create_trait(dataset = dataset, name = start_vars['trait_id'])
this_trait = retrieve_sample_data(this_trait, dataset)
samples = []
diff --git a/wqflask/wqflask/collect.py b/wqflask/wqflask/collect.py
index b22e0004..42a09fed 100644
--- a/wqflask/wqflask/collect.py
+++ b/wqflask/wqflask/collect.py
@@ -14,9 +14,6 @@ import urlparse
import simplejson as json
-import redis
-Redis = redis.StrictRedis()
-
from flask import (Flask, g, render_template, url_for, request, make_response,
redirect, flash, jsonify)
@@ -30,8 +27,10 @@ from wqflask import model
from utility import Bunch, Struct, hmac
from utility.formatting import numify
+from utility.redis_tools import get_redis_conn
+Redis = get_redis_conn()
-from base import trait
+from base.trait import create_trait, retrieve_trait_info, jsonable
from base.data_set import create_dataset
import logging
@@ -158,15 +157,12 @@ def remove_traits():
params = request.form
uc_id = params['uc_id']
- traits_to_remove = params.getlist('traits[]')
+ traits_to_remove = params['trait_list']
traits_to_remove = process_traits(traits_to_remove)
- logger.debug("\n\n after processing, traits_to_remove:", traits_to_remove)
members_now = g.user_session.remove_traits_from_collection(uc_id, traits_to_remove)
- # We need to return something so we'll return this...maybe in the future
- # we can use it to check the results
- return str(len(members_now))
+ return redirect(url_for("view_collection", uc_id=uc_id))
@app.route("/collections/delete", methods=('POST',))
@@ -208,14 +204,14 @@ def view_collection():
if dataset_name == "Temp":
group = name.split("_")[2]
dataset = create_dataset(dataset_name, dataset_type = "Temp", group_name = group)
- trait_ob = trait.GeneralTrait(name=name, dataset=dataset)
+ trait_ob = create_trait(name=name, dataset=dataset)
else:
dataset = create_dataset(dataset_name)
- trait_ob = trait.GeneralTrait(name=name, dataset=dataset)
- trait_ob = trait.retrieve_trait_info(trait_ob, dataset, get_qtl_info=True)
+ trait_ob = create_trait(name=name, dataset=dataset)
+ trait_ob = retrieve_trait_info(trait_ob, dataset, get_qtl_info=True)
trait_obs.append(trait_ob)
- json_version.append(trait.jsonable(trait_ob))
+ json_version.append(jsonable(trait_ob))
collection_info = dict(trait_obs=trait_obs,
uc = uc)
diff --git a/wqflask/wqflask/comparison_bar_chart/comparison_bar_chart.py b/wqflask/wqflask/comparison_bar_chart/comparison_bar_chart.py
index 21eb1493..5d74dc9d 100644
--- a/wqflask/wqflask/comparison_bar_chart/comparison_bar_chart.py
+++ b/wqflask/wqflask/comparison_bar_chart/comparison_bar_chart.py
@@ -37,7 +37,7 @@ from pprint import pformat as pf
import reaper
-from base.trait import GeneralTrait
+from base.trait import create_trait
from base import data_set
from utility import webqtlUtil, helper_functions, corr_result_helpers
from db import webqtlDatabaseFunction
@@ -108,7 +108,7 @@ class ComparisonBarChart(object):
trait_name, dataset_name = trait_db.split(":")
#print("dataset_name:", dataset_name)
dataset_ob = data_set.create_dataset(dataset_name)
- trait_ob = GeneralTrait(dataset=dataset_ob,
+ trait_ob = create_trait(dataset=dataset_ob,
name=trait_name,
cellid=None)
self.trait_list.append((trait_ob, dataset_ob))
diff --git a/wqflask/wqflask/correlation/corr_scatter_plot.py b/wqflask/wqflask/correlation/corr_scatter_plot.py
index dfb81c54..819836b1 100644
--- a/wqflask/wqflask/correlation/corr_scatter_plot.py
+++ b/wqflask/wqflask/correlation/corr_scatter_plot.py
@@ -4,7 +4,7 @@ import math
from flask import g
-from base.trait import GeneralTrait
+from base.trait import create_trait, retrieve_sample_data
from base import data_set
from utility import corr_result_helpers
from scipy import stats
@@ -17,12 +17,21 @@ class CorrScatterPlot(object):
"""Page that displays a correlation scatterplot with a line fitted to it"""
def __init__(self, params):
- self.data_set_1 = data_set.create_dataset(params['dataset_1'])
- self.data_set_2 = data_set.create_dataset(params['dataset_2'])
- #self.data_set_3 = data_set.create_dataset(params['dataset_3'])
- self.trait_1 = GeneralTrait(name=params['trait_1'], dataset=self.data_set_1)
- self.trait_2 = GeneralTrait(name=params['trait_2'], dataset=self.data_set_2)
- #self.trait_3 = GeneralTrait(name=params['trait_3'], dataset=self.data_set_3)
+ self.dataset_1 = data_set.create_dataset(params['dataset_1'])
+ self.dataset_2 = data_set.create_dataset(params['dataset_2'])
+ #self.dataset_3 = data_set.create_dataset(params['dataset_3'])
+ self.trait_1 = create_trait(name=params['trait_1'], dataset=self.dataset_1)
+ self.trait_2 = create_trait(name=params['trait_2'], dataset=self.dataset_2)
+ #self.trait_3 = create_trait(name=params['trait_3'], dataset=self.dataset_3)
+
+ primary_samples = self.dataset_1.group.samplelist
+ if self.dataset_1.group.parlist != None:
+ primary_samples += self.dataset_1.group.parlist
+ if self.dataset_1.group.f1list != None:
+ primary_samples += self.dataset_1.group.f1list
+
+ self.trait_1 = retrieve_sample_data(self.trait_1, self.dataset_1, primary_samples)
+ self.trait_2 = retrieve_sample_data(self.trait_2, self.dataset_2, primary_samples)
samples_1, samples_2, num_overlap = corr_result_helpers.normalize_values_with_samples(self.trait_1.data, self.trait_2.data)
diff --git a/wqflask/wqflask/correlation/show_corr_results.py b/wqflask/wqflask/correlation/show_corr_results.py
index cc74c8e4..bc2912f2 100644
--- a/wqflask/wqflask/correlation/show_corr_results.py
+++ b/wqflask/wqflask/correlation/show_corr_results.py
@@ -47,7 +47,7 @@ import reaper
from base import webqtlConfig
from utility.THCell import THCell
from utility.TDCell import TDCell
-from base.trait import GeneralTrait
+from base.trait import create_trait
from base import data_set
from utility import webqtlUtil, helper_functions, corr_result_helpers, hmac
from db import webqtlDatabaseFunction
@@ -97,7 +97,7 @@ class CorrelationResults(object):
if start_vars['dataset'] == "Temp":
self.dataset = data_set.create_dataset(dataset_name = "Temp", dataset_type = "Temp", group_name = start_vars['group'])
self.trait_id = start_vars['trait_id']
- self.this_trait = GeneralTrait(dataset=self.dataset,
+ self.this_trait = create_trait(dataset=self.dataset,
name=self.trait_id,
cellid=None)
else:
@@ -199,7 +199,7 @@ class CorrelationResults(object):
range_chr_as_int = order_id
for _trait_counter, trait in enumerate(self.correlation_data.keys()[:self.return_number]):
- trait_object = GeneralTrait(dataset=self.target_dataset, name=trait, get_qtl_info=True, get_sample_info=False)
+ trait_object = create_trait(dataset=self.target_dataset, name=trait, get_qtl_info=True, get_sample_info=False)
if self.target_dataset.type == "ProbeSet" or self.target_dataset.type == "Geno":
#ZS: Convert trait chromosome to an int for the location range option
@@ -235,9 +235,8 @@ class CorrelationResults(object):
trait_object.tissue_pvalue = tissue_corr_data[trait][2]
elif self.corr_type == "lit":
trait_object.lit_corr = lit_corr_data[trait][1]
- self.correlation_results.append(trait_object)
- self.target_dataset.get_trait_info(self.correlation_results, self.target_dataset.group.species)
+ self.correlation_results.append(trait_object)
if self.corr_type != "lit" and self.dataset.type == "ProbeSet" and self.target_dataset.type == "ProbeSet":
self.do_lit_correlation_for_trait_list()
@@ -496,6 +495,8 @@ def do_bicor(this_trait_vals, target_trait_vals):
def generate_corr_json(corr_results, this_trait, dataset, target_dataset, for_api = False):
results_list = []
for i, trait in enumerate(corr_results):
+ if trait.view == False:
+ continue
results_dict = {}
if not for_api:
results_dict['checkbox'] = "<INPUT TYPE='checkbox' NAME='searchResult' class='checkbox trait_checkbox' style='padding-right: 0px;' VALUE='" + hmac.hmac_creation('{}:{}'.format(trait.name, trait.dataset.name)) + "'>"
@@ -609,6 +610,7 @@ def get_header_fields(data_type, corr_method):
if corr_method == "spearman":
header_fields = ['Index',
'Record',
+ 'Abbreviation',
'Description',
'Authors',
'Year',
@@ -621,6 +623,7 @@ def get_header_fields(data_type, corr_method):
else:
header_fields = ['Index',
'Record',
+ 'Abbreviation',
'Description',
'Authors',
'Year',
diff --git a/wqflask/wqflask/correlation_matrix/show_corr_matrix.py b/wqflask/wqflask/correlation_matrix/show_corr_matrix.py
index b5c45d05..2b9467d1 100644
--- a/wqflask/wqflask/correlation_matrix/show_corr_matrix.py
+++ b/wqflask/wqflask/correlation_matrix/show_corr_matrix.py
@@ -43,14 +43,16 @@ from pprint import pformat as pf
import reaper
-import redis
-Redis = redis.StrictRedis()
+from utility.redis_tools import get_redis_conn
+Redis = get_redis_conn()
+THIRTY_DAYS = 60 * 60 * 24 * 30
from utility.THCell import THCell
from utility.TDCell import TDCell
from base.trait import GeneralTrait
from base import data_set
from utility import webqtlUtil, helper_functions, corr_result_helpers
+
from db import webqtlDatabaseFunction
import utility.webqtlUtil #this is for parallel computing only.
from wqflask.correlation import correlation_functions
@@ -204,20 +206,6 @@ class CorrelationMatrix(object):
samples = self.all_sample_list,
sample_data = self.sample_data,)
# corr_results = [result[1] for result in result_row for result_row in self.corr_results])
-
- def get_trait_db_obs(self, trait_db_list):
-
- self.trait_list = []
- for i, trait_db in enumerate(trait_db_list):
- if i == (len(trait_db_list) - 1):
- break
- trait_name, dataset_name = trait_db.split(":")
- #print("dataset_name:", dataset_name)
- dataset_ob = data_set.create_dataset(dataset_name)
- trait_ob = GeneralTrait(dataset=dataset_ob,
- name=trait_name,
- cellid=None)
- self.trait_list.append((trait_ob, dataset_ob))
def calculate_pca(self, cols, corr_eigen_value, corr_eigen_vectors):
base = importr('base')
@@ -257,7 +245,7 @@ class CorrelationMatrix(object):
this_vals_string += "x "
this_vals_string = this_vals_string[:-1]
- Redis.set(trait_id, this_vals_string)
+ Redis.set(trait_id, this_vals_string, ex=THIRTY_DAYS)
self.pca_trait_ids.append(trait_id)
return pca
diff --git a/wqflask/wqflask/ctl/ctl_analysis.py b/wqflask/wqflask/ctl/ctl_analysis.py
index 4415b86a..35067036 100644
--- a/wqflask/wqflask/ctl/ctl_analysis.py
+++ b/wqflask/wqflask/ctl/ctl_analysis.py
@@ -17,7 +17,7 @@ import csv
import itertools
from base import data_set
-from base import trait as TRAIT
+from base.trait import create_trait, retrieve_sample_data
from utility import helper_functions
from utility.tools import locate, GN2_BRANCH_URL
@@ -122,8 +122,8 @@ class CTL(object):
logger.debug("retrieving data for", trait)
if trait != "":
ts = trait.split(':')
- gt = TRAIT.GeneralTrait(name = ts[0], dataset_name = ts[1])
- gt = TRAIT.retrieve_sample_data(gt, dataset, individuals)
+ gt = create_trait(name = ts[0], dataset_name = ts[1])
+ gt = retrieve_sample_data(gt, dataset, individuals)
for ind in individuals:
if ind in gt.data.keys():
traits.append(gt.data[ind].value)
@@ -180,8 +180,8 @@ class CTL(object):
logger.debug(significant[0][x], significant[1][x], significant[2][x]) # Debug to console
tsS = significant[0][x].split(':') # Source
tsT = significant[2][x].split(':') # Target
- gtS = TRAIT.GeneralTrait(name = tsS[0], dataset_name = tsS[1]) # Retrieve Source info from the DB
- gtT = TRAIT.GeneralTrait(name = tsT[0], dataset_name = tsT[1]) # Retrieve Target info from the DB
+ gtS = create_trait(name = tsS[0], dataset_name = tsS[1]) # Retrieve Source info from the DB
+ gtT = create_trait(name = tsT[0], dataset_name = tsT[1]) # Retrieve Target info from the DB
self.addNode(gtS)
self.addNode(gtT)
self.addEdge(gtS, gtT, significant, x)
diff --git a/wqflask/wqflask/do_search.py b/wqflask/wqflask/do_search.py
index b0ca5ced..1e15d28f 100644
--- a/wqflask/wqflask/do_search.py
+++ b/wqflask/wqflask/do_search.py
@@ -34,10 +34,7 @@ class DoSearch(object):
self.search_type = search_type
if self.dataset:
- logger.debug("self.dataset is boo: ", type(self.dataset), pf(self.dataset))
- logger.debug("self.dataset.group is: ", pf(self.dataset.group))
#Get group information for dataset and the species id
-
self.species_id = webqtlDatabaseFunction.retrieve_species_id(self.dataset.group.name)
def execute(self, query):
@@ -54,10 +51,6 @@ class DoSearch(object):
return keyword
- #def escape(self, stringy):
- # """Shorter name than self.db_conn.escape_string"""
- # return escape(str(stringy))
-
def mescape(self, *items):
"""Multiple escape"""
escaped = [escape(str(item)) for item in items]
@@ -71,8 +64,6 @@ class DoSearch(object):
@classmethod
def get_search(cls, search_type):
- logger.debug("search_types are:", pf(cls.search_types))
-
search_type_string = search_type['dataset_type']
if 'key' in search_type and search_type['key'] != None:
search_type_string += '_' + search_type['key']
@@ -648,7 +639,7 @@ class CisTransLrsSearch(DoSearch):
escape(self.dataset.type),
chromosome)
else:
- location_clause = "(ABS(%s.Mb-Geno.Mb) %s %s and %s.Chr = Geno.Chr) or (%s.Chr != Geno.Chr)" % (escape(self.dataset.type), the_operator, escape(str(self.mb_buffer)), escape(self.dataset.type))
+ location_clause = "(ABS(%s.Mb-Geno.Mb) %s %s and %s.Chr = Geno.Chr) or (%s.Chr != Geno.Chr)" % (escape(self.dataset.type), the_operator, escape(str(self.mb_buffer)), escape(self.dataset.type), escape(self.dataset.type))
where_clause = sub_clause + """
%sXRef.Locus = Geno.name and
Geno.SpeciesId = %s and
diff --git a/wqflask/wqflask/docs.py b/wqflask/wqflask/docs.py
index 0187f32e..78407e22 100644
--- a/wqflask/wqflask/docs.py
+++ b/wqflask/wqflask/docs.py
@@ -1,5 +1,7 @@
from __future__ import absolute_import, print_function, division
+import codecs
+
from flask import g
from utility.logger import getLogger
@@ -20,7 +22,7 @@ class Docs(object):
self.content = ""
else:
self.title = result[0]
- self.content = result[1]
+ self.content = result[1].encode("latin1")
self.editable = "false"
# ZS: Removing option to edit to see if text still gets vandalized
diff --git a/wqflask/wqflask/group_manager.py b/wqflask/wqflask/group_manager.py
new file mode 100644
index 00000000..24848ed8
--- /dev/null
+++ b/wqflask/wqflask/group_manager.py
@@ -0,0 +1,145 @@
+
+from __future__ import print_function, division, absolute_import
+
+from flask import (Flask, g, render_template, url_for, request, make_response,
+ redirect, flash)
+
+from wqflask import app
+from wqflask.user_login import send_verification_email
+
+from utility.redis_tools import get_user_groups, get_group_info, create_group, delete_group, add_users_to_group, remove_users_from_group, \
+ change_group_name, save_verification_code, check_verification_code, get_user_by_unique_column, get_resources, get_resource_info
+
+from utility.logger import getLogger
+logger = getLogger(__name__)
+
+@app.route("/groups/manage", methods=('GET', 'POST'))
+def manage_groups():
+ params = request.form if request.form else request.args
+ if "add_new_group" in params:
+ return redirect(url_for('add_group'))
+ else:
+ admin_groups, member_groups = get_user_groups(g.user_session.user_id)
+ return render_template("admin/group_manager.html", admin_groups=admin_groups, member_groups=member_groups)
+
+@app.route("/groups/view", methods=('GET', 'POST'))
+def view_group():
+ params = request.form if request.form else request.args
+ group_id = params['id']
+ group_info = get_group_info(group_id)
+ admins_info = []
+ user_is_admin = False
+ if g.user_session.user_id in group_info['admins']:
+ user_is_admin = True
+ for user_id in group_info['admins']:
+ if user_id:
+ user_info = get_user_by_unique_column("user_id", user_id)
+ admins_info.append(user_info)
+ members_info = []
+ for user_id in group_info['members']:
+ if user_id:
+ user_info = get_user_by_unique_column("user_id", user_id)
+ members_info.append(user_info)
+
+ #ZS: This whole part might not scale well with many resources
+ resources_info = []
+ all_resources = get_resources()
+ for resource_id in all_resources:
+ resource_info = get_resource_info(resource_id)
+ group_masks = resource_info['group_masks']
+ if group_id in group_masks:
+ this_resource = {}
+ privileges = group_masks[group_id]
+ this_resource['id'] = resource_id
+ this_resource['name'] = resource_info['name']
+ this_resource['data'] = privileges['data']
+ this_resource['metadata'] = privileges['metadata']
+ this_resource['admin'] = privileges['admin']
+ resources_info.append(this_resource)
+
+ return render_template("admin/view_group.html", group_info=group_info, admins=admins_info, members=members_info, user_is_admin=user_is_admin, resources=resources_info)
+
+@app.route("/groups/remove", methods=('POST',))
+def remove_groups():
+ group_ids_to_remove = request.form['selected_group_ids']
+ for group_id in group_ids_to_remove.split(":"):
+ delete_group(g.user_session.user_id, group_id)
+
+ return redirect(url_for('manage_groups'))
+
+@app.route("/groups/remove_users", methods=('POST',))
+def remove_users():
+ group_id = request.form['group_id']
+ admin_ids_to_remove = request.form['selected_admin_ids']
+ member_ids_to_remove = request.form['selected_member_ids']
+
+ remove_users_from_group(g.user_session.user_id, admin_ids_to_remove.split(":"), group_id, user_type="admins")
+ remove_users_from_group(g.user_session.user_id, member_ids_to_remove.split(":"), group_id, user_type="members")
+
+ return redirect(url_for('view_group', id=group_id))
+
+@app.route("/groups/add_<path:user_type>", methods=('POST',))
+def add_users(user_type='members'):
+ group_id = request.form['group_id']
+ if user_type == "admins":
+ user_emails = request.form['admin_emails_to_add'].split(",")
+ add_users_to_group(g.user_session.user_id, group_id, user_emails, admins = True)
+ elif user_type == "members":
+ user_emails = request.form['member_emails_to_add'].split(",")
+ add_users_to_group(g.user_session.user_id, group_id, user_emails, admins = False)
+
+ return redirect(url_for('view_group', id=group_id))
+
+@app.route("/groups/change_name", methods=('POST',))
+def change_name():
+ group_id = request.form['group_id']
+ new_name = request.form['new_name']
+ group_info = change_group_name(g.user_session.user_id, group_id, new_name)
+
+ return new_name
+
+@app.route("/groups/create", methods=('GET', 'POST'))
+def add_or_edit_group():
+ params = request.form if request.form else request.args
+ if "group_name" in params:
+ member_user_ids = set()
+ admin_user_ids = set()
+ admin_user_ids.add(g.user_session.user_id) #ZS: Always add the user creating the group as an admin
+ if "admin_emails_to_add" in params:
+ admin_emails = params['admin_emails_to_add'].split(",")
+ for email in admin_emails:
+ user_details = get_user_by_unique_column("email_address", email)
+ if user_details:
+ admin_user_ids.add(user_details['user_id'])
+ #send_group_invites(params['group_id'], user_email_list = admin_emails, user_type="admins")
+ if "member_emails_to_add" in params:
+ member_emails = params['member_emails_to_add'].split(",")
+ for email in member_emails:
+ user_details = get_user_by_unique_column("email_address", email)
+ if user_details:
+ member_user_ids.add(user_details['user_id'])
+ #send_group_invites(params['group_id'], user_email_list = user_emails, user_type="members")
+
+ create_group(list(admin_user_ids), list(member_user_ids), params['group_name'])
+ return redirect(url_for('manage_groups'))
+ else:
+ return render_template("admin/create_group.html")
+
+#ZS: Will integrate this later, for now just letting users be added directly
+def send_group_invites(group_id, user_email_list = [], user_type="members"):
+ for user_email in user_email_list:
+ user_details = get_user_by_unique_column("email_address", user_email)
+ if user_details:
+ group_info = get_group_info(group_id)
+ #ZS: Probably not necessary since the group should normally always exist if group_id is being passed here,
+ # but it's technically possible to hit it if Redis is cleared out before submitting the new users or something
+ if group_info:
+ #ZS: Don't add user if they're already an admin or if they're being added a regular user and are already a regular user,
+ # but do add them if they're a regular user and are added as an admin
+ if (user_details['user_id'] in group_info['admins']) or \
+ ((user_type == "members") and (user_details['user_id'] in group_info['members'])):
+ continue
+ else:
+ send_verification_email(user_details, template_name = "email/group_verification.txt", key_prefix = "verification_code", subject = "You've been invited to join a GeneNetwork user group")
+
+#@app.route() \ No newline at end of file
diff --git a/wqflask/wqflask/gsearch.py b/wqflask/wqflask/gsearch.py
index 3d9b508a..c65a1415 100644
--- a/wqflask/wqflask/gsearch.py
+++ b/wqflask/wqflask/gsearch.py
@@ -4,7 +4,7 @@ import json
from flask import Flask, g
from base.data_set import create_dataset
-from base.trait import GeneralTrait
+from base.trait import create_trait
from db import webqtlDatabaseFunction
from base import webqtlConfig
@@ -75,7 +75,10 @@ class GSearch(object):
this_trait['group'] = line[1]
this_trait['tissue'] = line[2]
this_trait['symbol'] = line[6]
- this_trait['description'] = line[7].decode('utf-8', 'replace')
+ if line[7]:
+ this_trait['description'] = line[7].decode('utf-8', 'replace')
+ else:
+ this_trait['description'] = "N/A"
this_trait['location_repr'] = 'N/A'
if (line[8] != "NULL" and line[8] != "") and (line[9] != 0):
this_trait['location_repr'] = 'Chr%s: %.6f' % (line[8], float(line[9]))
@@ -93,7 +96,9 @@ class GSearch(object):
#dataset = create_dataset(line[3], "ProbeSet", get_samplelist=False)
#trait_id = line[4]
#with Bench("Building trait object"):
- trait_ob = GeneralTrait(dataset_name=this_trait['dataset'], name=this_trait['name'], get_qtl_info=True, get_sample_info=False)
+ trait_ob = create_trait(dataset_name=this_trait['dataset'], name=this_trait['name'], get_qtl_info=True, get_sample_info=False)
+ if not trait_ob:
+ continue
max_lrs_text = "N/A"
if trait_ob.locus_chr != "" and trait_ob.locus_mb != "":
max_lrs_text = "Chr" + str(trait_ob.locus_chr) + ": " + str(trait_ob.locus_mb)
@@ -207,13 +212,12 @@ class GSearch(object):
if line[11] != "" and line[11] != None:
this_trait['additive'] = '%.3f' % line[11]
- #dataset = create_dataset(line[2], "Publish")
- #trait_id = line[3]
- #this_trait = GeneralTrait(dataset=dataset, name=trait_id, get_qtl_info=True, get_sample_info=False)
this_trait['max_lrs_text'] = "N/A"
+ trait_ob = create_trait(dataset_name=this_trait['dataset'], name=this_trait['name'], get_qtl_info=True, get_sample_info=False)
+ if not trait_ob:
+ continue
if this_trait['dataset'] == this_trait['group'] + "Publish":
try:
- trait_ob = GeneralTrait(dataset_name=this_trait['dataset'], name=this_trait['name'], get_qtl_info=True, get_sample_info=False)
if trait_ob.locus_chr != "" and trait_ob.locus_mb != "":
this_trait['max_lrs_text'] = "Chr" + str(trait_ob.locus_chr) + ": " + str(trait_ob.locus_mb)
except:
diff --git a/wqflask/wqflask/marker_regression/display_mapping_results.py b/wqflask/wqflask/marker_regression/display_mapping_results.py
index 2a53b60e..89f56c30 100644
--- a/wqflask/wqflask/marker_regression/display_mapping_results.py
+++ b/wqflask/wqflask/marker_regression/display_mapping_results.py
@@ -147,7 +147,7 @@ class DisplayMappingResults(object):
self.dataset = start_vars['dataset']
self.this_trait = start_vars['this_trait']
- self.n_samples = start_vars['num_vals']
+ self.n_samples = start_vars['n_samples']
self.species = start_vars['species']
self.genofile_string = ""
if 'genofile_string' in start_vars:
@@ -246,6 +246,12 @@ class DisplayMappingResults(object):
if 'output_files' in start_vars:
self.output_files = ",".join(start_vars['output_files'])
+ self.categorical_vars = ""
+ self.perm_strata = ""
+ if 'perm_strata' in start_vars.keys() and 'categorical_vars' in start_vars.keys():
+ self.categorical_vars = start_vars['categorical_vars']
+ self.perm_strata = start_vars['perm_strata']
+
self.selectedChr = int(start_vars['selected_chr'])
self.strainlist = start_vars['samples']
@@ -265,14 +271,12 @@ class DisplayMappingResults(object):
else:
self.colorCollection = [self.LRS_COLOR]
+ self.dataset.group.genofile = self.genofile_string.split(":")[0]
if self.mapping_method == "reaper" and self.manhattan_plot != True:
self.genotype = self.dataset.group.read_genotype_file(use_reaper=True)
else:
self.genotype = self.dataset.group.read_genotype_file()
- #if self.mapping_method == "rqtl_geno" and self.genotype.filler == True:
- # self.genotype = self.genotype.read_rdata_output(self.qtlresults)
-
#Darwing Options
try:
if self.selectedChr > -1:
@@ -510,7 +514,10 @@ class DisplayMappingResults(object):
yTopOffset = max(90, yTopOffset)
else:
if self.legendChecked:
- yTopOffset = max(90, yTopOffset)
+ if self.covariates != "" and self.controlLocus and self.doControl != "false":
+ yTopOffset = max(120, yTopOffset)
+ else:
+ yTopOffset = max(100, yTopOffset)
else:
pass
@@ -1761,9 +1768,9 @@ class DisplayMappingResults(object):
break
if all_int:
- max_lrs_width = canvas.stringWidth("%d" % LRS_LOD_Max, font=LRSScaleFont) + 30
+ max_lrs_width = canvas.stringWidth("%d" % LRS_LOD_Max, font=LRSScaleFont) + 40
else:
- max_lrs_width = canvas.stringWidth("%2.1f" % LRS_LOD_Max, font=LRSScaleFont) + 20
+ max_lrs_width = canvas.stringWidth("%2.1f" % LRS_LOD_Max, font=LRSScaleFont) + 30
#draw the "LRS" or "LOD" string to the left of the axis
canvas.drawString(self.LRS_LOD, xLeftOffset - max_lrs_width - 15*(zoom-1), \
@@ -1899,13 +1906,16 @@ class DisplayMappingResults(object):
this_chr = str(self.ChrList[self.selectedChr][1]+1)
if self.selectedChr == -1 or str(qtlresult['chr']) == this_chr:
- if self.plotScale != "physic" and self.genotype.filler == True:
- if self.selectedChr != -1:
- start_cm = self.genotype[self.selectedChr - 1][0].cM
- Xc = startPosX + (qtlresult['Mb'] - start_cm)*plotXScale
- else:
- start_cm = self.genotype[previous_chr_as_int][0].cM
- Xc = startPosX + ((qtlresult['Mb']-start_cm-startMb)*plotXScale)*(((qtlresult['Mb']-start_cm-startMb)*plotXScale)/((qtlresult['Mb']-start_cm-startMb+self.GraphInterval)*plotXScale))
+ if self.plotScale != "physic" and self.mapping_method == "reaper" and not self.manhattan_plot:
+ Xc = startPosX + (qtlresult['cM']-startMb)*plotXScale
+ if hasattr(self.genotype, "filler"):
+ if self.genotype.filler:
+ if self.selectedChr != -1:
+ start_cm = self.genotype[self.selectedChr - 1][0].cM
+ Xc = startPosX + (qtlresult['Mb'] - start_cm)*plotXScale
+ else:
+ start_cm = self.genotype[previous_chr_as_int][0].cM
+ Xc = startPosX + ((qtlresult['Mb']-start_cm-startMb)*plotXScale)*(((qtlresult['Mb']-start_cm-startMb)*plotXScale)/((qtlresult['Mb']-start_cm-startMb+self.GraphInterval)*plotXScale))
else:
Xc = startPosX + (qtlresult['Mb']-startMb)*plotXScale
diff --git a/wqflask/wqflask/marker_regression/gemma_mapping.py b/wqflask/wqflask/marker_regression/gemma_mapping.py
index e2b15c26..b858b573 100644
--- a/wqflask/wqflask/marker_regression/gemma_mapping.py
+++ b/wqflask/wqflask/marker_regression/gemma_mapping.py
@@ -1,7 +1,7 @@
import os, math, string, random, json
from base import webqtlConfig
-from base.trait import GeneralTrait
+from base.trait import create_trait
from base.data_set import create_dataset
from utility.tools import flat_files, GEMMA_COMMAND, GEMMA_WRAPPER_COMMAND, TEMPDIR, WEBSERVER_MODE
@@ -129,16 +129,14 @@ def gen_covariates_file(this_dataset, covariates, samples):
this_covariate_data = []
trait_name = covariate.split(":")[0]
dataset_ob = create_dataset(covariate.split(":")[1])
- trait_ob = GeneralTrait(dataset=dataset_ob,
+ trait_ob = create_trait(dataset=dataset_ob,
name=trait_name,
cellid=None)
#trait_samples = this_dataset.group.all_samples_ordered()
this_dataset.group.get_samplelist()
trait_samples = this_dataset.group.samplelist
- logger.debug("SAMPLES:", trait_samples)
trait_sample_data = trait_ob.data
- logger.debug("SAMPLE DATA:", trait_sample_data)
for index, sample in enumerate(trait_samples):
if sample in samples:
if sample in trait_sample_data:
diff --git a/wqflask/wqflask/marker_regression/rqtl_mapping.py b/wqflask/wqflask/marker_regression/rqtl_mapping.py
index e1aa290b..c5590a85 100644
--- a/wqflask/wqflask/marker_regression/rqtl_mapping.py
+++ b/wqflask/wqflask/marker_regression/rqtl_mapping.py
@@ -1,17 +1,40 @@
import rpy2.robjects as ro
import rpy2.robjects.numpy2ri as np2r
import numpy as np
+import json
+
+from flask import g
from base.webqtlConfig import TMPDIR
-from base.trait import GeneralTrait
+from base.trait import create_trait
from base.data_set import create_dataset
from utility import webqtlUtil
from utility.tools import locate, TEMPDIR
+from flask import g
import utility.logger
logger = utility.logger.getLogger(__name__ )
-def run_rqtl_geno(vals, samples, dataset, method, model, permCheck, num_perm, perm_strata_list, do_control, control_marker, manhattan_plot, pair_scan, cofactors):
+# Get a trait's type (numeric, categorical, etc) from the DB
+def get_trait_data_type(trait_db_string):
+ logger.info("get_trait_data_type");
+ the_query = "SELECT value FROM TraitMetadata WHERE type='trait_data_type'"
+ logger.info("the_query done");
+ results_json = g.db.execute(the_query).fetchone()
+ logger.info("the_query executed");
+ results_ob = json.loads(results_json[0])
+ logger.info("json results loaded");
+ if trait_db_string in results_ob:
+ logger.info("found");
+ return results_ob[trait_db_string]
+ else:
+ logger.info("not found");
+ return "numeric"
+
+
+# Run qtl mapping using R/qtl
+def run_rqtl_geno(vals, samples, dataset, mapping_scale, method, model, permCheck, num_perm, perm_strata_list, do_control, control_marker, manhattan_plot, pair_scan, cofactors):
+ logger.info("Start run_rqtl_geno");
## Get pointers to some common R functions
r_library = ro.r["library"] # Map the library function
r_c = ro.r["c"] # Map the c function
@@ -21,6 +44,8 @@ def run_rqtl_geno(vals, samples, dataset, method, model, permCheck, num_perm, pe
print(r_library("qtl")) # Load R/qtl
+ logger.info("QTL library loaded");
+
## Get pointers to some R/qtl functions
scanone = ro.r["scanone"] # Map the scanone function
scantwo = ro.r["scantwo"] # Map the scantwo function
@@ -33,35 +58,47 @@ def run_rqtl_geno(vals, samples, dataset, method, model, permCheck, num_perm, pe
# genofilelocation = locate(crossname + ".RData", "genotype/rdata")
# cross_object = read_cross_from_rdata(genofilelocation) # Map the local GENOtoCSVR function
#except:
- generate_cross_from_geno(dataset)
+
+ if mapping_scale == "morgan":
+ scale_units = "cM"
+ else:
+ scale_units = "Mb"
+
+ generate_cross_from_geno(dataset, scale_units)
GENOtoCSVR = ro.r["GENOtoCSVR"] # Map the local GENOtoCSVR function
crossfilelocation = TMPDIR + crossname + ".cross"
if dataset.group.genofile:
genofilelocation = locate(dataset.group.genofile, "genotype")
else:
genofilelocation = locate(dataset.group.name + ".geno", "genotype")
+ logger.info("Going to create a cross from geno");
cross_object = GENOtoCSVR(genofilelocation, crossfilelocation) # TODO: Add the SEX if that is available
-
+ logger.info("before calc_genoprob");
if manhattan_plot:
cross_object = calc_genoprob(cross_object)
else:
- cross_object = calc_genoprob(cross_object, step=1, stepwidth="max")
-
+ cross_object = calc_genoprob(cross_object, step=5, stepwidth="max")
+ logger.info("after calc_genoprob");
pheno_string = sanitize_rqtl_phenotype(vals)
-
+ logger.info("phenostring done");
+ names_string = sanitize_rqtl_names(samples)
+ logger.info("sanitized pheno and names");
cross_object = add_phenotype(cross_object, pheno_string, "the_pheno") # Add the phenotype
-
- # Scan for QTLs
+ cross_object = add_names(cross_object, names_string, "the_names") # Add the phenotype
+ logger.info("Added pheno and names");
marker_covars = create_marker_covariates(control_marker, cross_object) # Create the additive covariate markers
-
+ logger.info("Marker covars done");
if cofactors != "":
- cross_object, trait_covars = add_cofactors(cross_object, dataset, cofactors, samples) # Create the covariates from selected traits
+ logger.info("Cofactors: " + cofactors);
+ cross_object, trait_covars = add_cofactors(cross_object, dataset, cofactors, samples) # Create the covariates from selected traits
ro.r('all_covars <- cbind(marker_covars, trait_covars)')
else:
ro.r('all_covars <- marker_covars')
-
+ #logger.info("Saving");
+ #ro.r('save.image(file = "/home/dannya/gn2-danny/cross.RData")')
+ #logger.info("Saving Done");
covars = ro.r['all_covars']
-
+ #DEBUG to save the session object to file
if pair_scan:
if do_control == "true":
logger.info("Using covariate"); result_data_frame = scantwo(cross_object, pheno = "the_pheno", addcovar = covars, model=model, method=method, n_cluster = 16)
@@ -77,6 +114,7 @@ def run_rqtl_geno(vals, samples, dataset, method, model, permCheck, num_perm, pe
else:
if do_control == "true" or cofactors != "":
logger.info("Using covariate"); result_data_frame = scanone(cross_object, pheno = "the_pheno", addcovar = covars, model=model, method=method)
+ ro.r('save.image(file = "/home/zas1024/gn2-zach/itp_cofactor_test.RData")')
else:
logger.info("No covariates"); result_data_frame = scanone(cross_object, pheno = "the_pheno", model=model, method=method)
@@ -94,11 +132,9 @@ def run_rqtl_geno(vals, samples, dataset, method, model, permCheck, num_perm, pe
perm_data_frame = scanone(cross_object, pheno_col = "the_pheno", n_perm = num_perm, model=model, method=method)
perm_output, suggestive, significant = process_rqtl_perm_results(num_perm, perm_data_frame) # Functions that sets the thresholds for the webinterface
- the_scale = check_mapping_scale(genofilelocation)
- return perm_output, suggestive, significant, process_rqtl_results(result_data_frame, dataset.group.species), the_scale
+ return perm_output, suggestive, significant, process_rqtl_results(result_data_frame, dataset.group.species)
else:
- the_scale = check_mapping_scale(genofilelocation)
- return process_rqtl_results(result_data_frame, dataset.group.species), the_scale
+ return process_rqtl_results(result_data_frame, dataset.group.species)
def generate_cross_from_rdata(dataset):
rdata_location = locate(dataset.group.name + ".RData", "genotype/rdata")
@@ -110,25 +146,22 @@ def generate_cross_from_rdata(dataset):
}
""" % (rdata_location))
-def generate_cross_from_geno(dataset): # TODO: Need to figure out why some genofiles have the wrong format and don't convert properly
+def generate_cross_from_geno(dataset, scale_units): # TODO: Need to figure out why some genofiles have the wrong format and don't convert properly
ro.r("""
trim <- function( x ) { gsub("(^[[:space:]]+|[[:space:]]+$)", "", x) }
-
getGenoCode <- function(header, name = 'unk'){
mat = which(unlist(lapply(header,function(x){ length(grep(paste('@',name,sep=''), x)) })) == 1)
return(trim(strsplit(header[mat],':')[[1]][2]))
}
-
GENOtoCSVR <- function(genotypes = '%s', out = 'cross.csvr', phenotype = NULL, sex = NULL, verbose = FALSE){
header = readLines(genotypes, 40) # Assume a geno header is not longer than 40 lines
toskip = which(unlist(lapply(header, function(x){ length(grep("Chr\t", x)) })) == 1)-1 # Major hack to skip the geno headers
-
type <- getGenoCode(header, 'type')
if(type == '4-way'){
genocodes <- c('1','2','3','4')
} else {
- genocodes <- c(getGenoCode(header, 'mat'), getGenoCode(header, 'het'), getGenoCode(header, 'pat')) # Get the genotype codes
+ genocodes <- c(getGenoCode(header, 'mat'), getGenoCode(header, 'het'), getGenoCode(header, 'pat')) # Get the genotype codes
}
genodata <- read.csv(genotypes, sep='\t', skip=toskip, header=TRUE, na.strings=getGenoCode(header,'unk'), colClasses='character', comment.char = '#')
cat('Genodata:', toskip, " ", dim(genodata), genocodes, '\n')
@@ -136,14 +169,26 @@ def generate_cross_from_geno(dataset): # TODO: Need to figure out why som
if(is.null(sex)) sex <- rep('m', (ncol(genodata)-4)) # If there isn't a sex phenotype, treat all as males
outCSVR <- rbind(c('Pheno', '', '', phenotype), # Phenotype
c('sex', '', '', sex), # Sex phenotype for the mice
- cbind(genodata[,c('Locus','Chr', 'cM')], genodata[, 5:ncol(genodata)])) # Genotypes
+ cbind(genodata[,c('Locus','Chr', '%s')], genodata[, 5:ncol(genodata)])) # Genotypes
write.table(outCSVR, file = out, row.names=FALSE, col.names=FALSE,quote=FALSE, sep=',') # Save it to a file
require(qtl)
- cross = read.cross(file=out, 'csvr', genotypes=genocodes) # Load the created cross file using R/qtl read.cross
- if(type == 'riset') cross <- convert2riself(cross) # If its a RIL, convert to a RIL in R/qtl
+ if(type == '4-way'){
+ cat('Loading in as 4-WAY\n')
+ cross = read.cross(file=out, 'csvr', genotypes=genocodes, crosstype="4way", convertXdata=FALSE) # Load the created cross file using R/qtl read.cross
+ }else if(type == 'f2'){
+ cat('Loading in as F2\n')
+ cross = read.cross(file=out, 'csvr', genotypes=genocodes, crosstype="f2") # Load the created cross file using R/qtl read.cross
+ }else{
+ cat('Loading in as normal\n')
+ cross = read.cross(file=out, 'csvr', genotypes=genocodes) # Load the created cross file using R/qtl read.cross
+ }
+ if(type == 'riset'){
+ cat('Converting to RISELF\n')
+ cross <- convert2riself(cross) # If its a RIL, convert to a RIL in R/qtl
+ }
return(cross)
}
- """ % (dataset.group.genofile))
+ """ % (dataset.group.genofile, scale_units))
def add_perm_strata(cross, perm_strata):
col_string = 'c("the_strata")'
@@ -176,9 +221,61 @@ def sanitize_rqtl_phenotype(vals):
return pheno_as_string
+def sanitize_rqtl_names(vals):
+ pheno_as_string = "c("
+ for i, val in enumerate(vals):
+ if val == "x":
+ if i < (len(vals) - 1):
+ pheno_as_string += "NA,"
+ else:
+ pheno_as_string += "NA"
+ else:
+ if i < (len(vals) - 1):
+ pheno_as_string += "'" + str(val) + "',"
+ else:
+ pheno_as_string += "'" + str(val) + "'"
+ pheno_as_string += ")"
+
+ return pheno_as_string
+
def add_phenotype(cross, pheno_as_string, col_name):
ro.globalenv["the_cross"] = cross
- ro.r('the_cross$pheno <- cbind(pull.pheno(the_cross), ' + col_name + ' = '+ pheno_as_string +')')
+ ro.r('pheno <- data.frame(pull.pheno(the_cross))')
+ ro.r('the_cross$pheno <- cbind(pheno, ' + col_name + ' = as.numeric('+ pheno_as_string +'))')
+ return ro.r["the_cross"]
+
+def add_categorical_covar(cross, covar_as_string, i):
+ ro.globalenv["the_cross"] = cross
+ logger.info("cross set");
+ ro.r('covar <- as.factor(' + covar_as_string + ')')
+ logger.info("covar set");
+ ro.r('newcovar <- model.matrix(~covar-1)')
+ logger.info("model.matrix finished");
+ ro.r('cat("new covar columns", ncol(newcovar), "\n")')
+ nCol = ro.r('ncol(newcovar)')
+ logger.info("ncol covar done: " + str(nCol[0]));
+ ro.r('pheno <- data.frame(pull.pheno(the_cross))')
+ logger.info("pheno pulled from cross");
+ nCol = int(nCol[0])
+ logger.info("nCol python int:" + str(nCol));
+ col_names = []
+ #logger.info("loop")
+ for x in range(1, (nCol+1)):
+ #logger.info("loop" + str(x));
+ col_name = "covar_" + str(i) + "_" + str(x)
+ #logger.info("col_name" + col_name);
+ ro.r('the_cross$pheno <- cbind(pheno, ' + col_name + ' = newcovar[,' + str(x) + '])')
+ col_names.append(col_name)
+ #logger.info("loop" + str(x) + "done");
+
+ logger.info("returning from add_categorical_covar");
+ return ro.r["the_cross"], col_names
+
+
+def add_names(cross, names_as_string, col_name):
+ ro.globalenv["the_cross"] = cross
+ ro.r('pheno <- data.frame(pull.pheno(the_cross))')
+ ro.r('the_cross$pheno <- cbind(pheno, ' + col_name + ' = '+ names_as_string +')')
return ro.r["the_cross"]
def pull_var(var_name, cross, var_string):
@@ -193,19 +290,20 @@ def add_cofactors(cross, this_dataset, covariates, samples):
covariate_list = covariates.split(",")
covar_name_string = "c("
for i, covariate in enumerate(covariate_list):
+ logger.info("Covariate: " + covariate);
this_covar_data = []
covar_as_string = "c("
trait_name = covariate.split(":")[0]
dataset_ob = create_dataset(covariate.split(":")[1])
- trait_ob = GeneralTrait(dataset=dataset_ob,
+ trait_ob = create_trait(dataset=dataset_ob,
name=trait_name,
cellid=None)
this_dataset.group.get_samplelist()
trait_samples = this_dataset.group.samplelist
trait_sample_data = trait_ob.data
- for index, sample in enumerate(trait_samples):
- if sample in samples:
+ for index, sample in enumerate(samples):
+ if sample in trait_samples:
if sample in trait_sample_data:
sample_value = trait_sample_data[sample].value
this_covar_data.append(sample_value)
@@ -220,31 +318,50 @@ def add_cofactors(cross, this_dataset, covariates, samples):
covar_as_string += ")"
- col_name = "covar_" + str(i)
- cross = add_phenotype(cross, covar_as_string, col_name)
+ datatype = get_trait_data_type(covariate)
+ logger.info("Covariate: " + covariate + " is of type: " + datatype);
+ if(datatype == "categorical"): # Cat variable
+ logger.info("call of add_categorical_covar");
+ cross, col_names = add_categorical_covar(cross, covar_as_string, i) # Expand and add it to the cross
+ logger.info("add_categorical_covar returned");
+ for z, col_name in enumerate(col_names): # Go through the additional covar names
+ if i < (len(covariate_list) - 1):
+ covar_name_string += '"' + col_name + '", '
+ else:
+ if(z < (len(col_names) -1)):
+ covar_name_string += '"' + col_name + '", '
+ else:
+ covar_name_string += '"' + col_name + '"'
- if i < (len(covariate_list) - 1):
- covar_name_string += '"' + col_name + '", '
+ logger.info("covar_name_string:" + covar_name_string)
else:
- covar_name_string += '"' + col_name + '"'
+ col_name = "covar_" + str(i)
+ cross = add_phenotype(cross, covar_as_string, col_name)
+ if i < (len(covariate_list) - 1):
+ covar_name_string += '"' + col_name + '", '
+ else:
+ covar_name_string += '"' + col_name + '"'
covar_name_string += ")"
-
+ logger.info("covar_name_string:" + covar_name_string);
covars_ob = pull_var("trait_covars", cross, covar_name_string)
-
return cross, covars_ob
def create_marker_covariates(control_marker, cross):
ro.globalenv["the_cross"] = cross
ro.r('genotypes <- pull.geno(the_cross)') # Get the genotype matrix
- userinputS = control_marker.replace(" ", "").split(",") # TODO: sanitize user input, Never Ever trust a user
- covariate_names = ', '.join('"{0}"'.format(w) for w in userinputS)
- ro.r('covnames <- c(' + covariate_names + ')')
+ userinput_sanitized = control_marker.replace(" ", "").split(",") # TODO: sanitize user input, Never Ever trust a user
+ logger.debug(userinput_sanitized)
+ if len(userinput_sanitized) > 0:
+ covariate_names = ', '.join('"{0}"'.format(w) for w in userinput_sanitized)
+ ro.r('covnames <- c(' + covariate_names + ')')
+ else:
+ ro.r('covnames <- c()')
ro.r('covInGeno <- which(covnames %in% colnames(genotypes))')
ro.r('covnames <- covnames[covInGeno]')
ro.r("cat('covnames (purged): ', covnames,'\n')")
ro.r('marker_covars <- genotypes[,covnames]') # Get the covariate matrix by using the marker name as index to the genotype file
-
+ # TODO: Create a design matrix from the marker covars for the markers in case of an F2, 4way, etc
return ro.r["marker_covars"]
def process_pair_scan_results(result):
@@ -291,20 +408,4 @@ def process_rqtl_results(result, species_name): # TODO: how to make this
marker['lod_score'] = output[i][2]
qtl_results.append(marker)
- return qtl_results
-
-def check_mapping_scale(genofile_location):
- scale = "physic"
- with open(genofile_location, "r") as geno_fh:
- for line in geno_fh:
- if line[0] == "@" or line[0] == "#":
-
- if "@scale" in line:
- scale = line.split(":")[1].strip()
- break
- else:
- continue
- else:
- break
-
- return scale \ No newline at end of file
+ return qtl_results \ No newline at end of file
diff --git a/wqflask/wqflask/marker_regression/run_mapping.py b/wqflask/wqflask/marker_regression/run_mapping.py
index 589be702..c9d10f7c 100644
--- a/wqflask/wqflask/marker_regression/run_mapping.py
+++ b/wqflask/wqflask/marker_regression/run_mapping.py
@@ -124,7 +124,10 @@ class RunMapping(object):
self.samples.append(sample)
self.vals.append(value)
- self.num_vals = start_vars['num_vals']
+ if 'n_samples' in start_vars:
+ self.n_samples = start_vars['n_samples']
+ else:
+ self.n_samples = len([val for val in self.vals if val != "x"])
#ZS: Check if genotypes exist in the DB in order to create links for markers
@@ -156,10 +159,13 @@ class RunMapping(object):
self.transform = ""
self.score_type = "LRS" #ZS: LRS or LOD
self.mapping_scale = "physic"
+ if "mapping_scale" in start_vars:
+ self.mapping_scale = start_vars['mapping_scale']
self.num_perm = 0
self.perm_output = []
self.bootstrap_results = []
- self.covariates = start_vars['covariates'] if "covariates" in start_vars else None
+ self.covariates = start_vars['covariates'] if "covariates" in start_vars else ""
+ self.categorical_vars = []
#ZS: This is passed to GN1 code for single chr mapping
self.selected_chr = -1
@@ -255,9 +261,9 @@ class RunMapping(object):
#if start_vars['pair_scan'] == "true":
# self.pair_scan = True
if self.permCheck and self.num_perm > 0:
- self.perm_output, self.suggestive, self.significant, results, self.mapping_scale = rqtl_mapping.run_rqtl_geno(self.vals, self.samples, self.dataset, self.method, self.model, self.permCheck, self.num_perm, perm_strata, self.do_control, self.control_marker, self.manhattan_plot, self.pair_scan, self.covariates)
+ self.perm_output, self.suggestive, self.significant, results= rqtl_mapping.run_rqtl_geno(self.vals, self.samples, self.dataset, self.mapping_scale, self.method, self.model, self.permCheck, self.num_perm, perm_strata, self.do_control, self.control_marker, self.manhattan_plot, self.pair_scan, self.covariates)
else:
- results, self.mapping_scale = rqtl_mapping.run_rqtl_geno(self.vals, self.samples, self.dataset, self.method, self.model, self.permCheck, self.num_perm, perm_strata, self.do_control, self.control_marker, self.manhattan_plot, self.pair_scan, self.covariates)
+ results = rqtl_mapping.run_rqtl_geno(self.vals, self.samples, self.dataset, self.mapping_scale, self.method, self.model, self.permCheck, self.num_perm, perm_strata, self.do_control, self.control_marker, self.manhattan_plot, self.pair_scan, self.covariates)
elif self.mapping_method == "reaper":
if "startMb" in start_vars: #ZS: Check if first time page loaded, so it can default to ON
if "additiveCheck" in start_vars:
@@ -429,7 +435,7 @@ class RunMapping(object):
with Bench("Trimming Markers for Table"):
self.trimmed_markers = trim_markers_for_table(results)
- chr_lengths = get_chr_lengths(self.mapping_scale, self.dataset, self.qtl_results)
+ chr_lengths = get_chr_lengths(self.mapping_scale, self.mapping_method, self.dataset, self.qtl_results)
#ZS: For zooming into genome browser, need to pass chromosome name instead of number
if self.dataset.group.species == "mouse":
@@ -465,6 +471,7 @@ class RunMapping(object):
#mapping_scale = self.mapping_scale,
#chromosomes = chromosome_mb_lengths,
#qtl_results = self.qtl_results,
+ categorical_vars = self.categorical_vars,
chr_lengths = chr_lengths,
num_perm = self.num_perm,
perm_results = self.perm_output,
@@ -643,7 +650,7 @@ def geno_db_exists(this_dataset):
except:
return "False"
-def get_chr_lengths(mapping_scale, dataset, qtl_results):
+def get_chr_lengths(mapping_scale, mapping_method, dataset, qtl_results):
chr_lengths = []
if mapping_scale == "physic":
for i, the_chr in enumerate(dataset.species.chromosomes.chromosomes):
@@ -666,8 +673,12 @@ def get_chr_lengths(mapping_scale, dataset, qtl_results):
this_chr = chr_as_num
highest_pos = 0
else:
- if float(result['Mb']) > highest_pos:
- highest_pos = float(result['Mb'])
+ if mapping_method == "reaper":
+ if float(result['cM']) > highest_pos:
+ highest_pos = float(result['cM'])
+ else:
+ if float(result['Mb']) > highest_pos:
+ highest_pos = float(result['Mb'])
return chr_lengths
diff --git a/wqflask/wqflask/network_graph/network_graph.py b/wqflask/wqflask/network_graph/network_graph.py
index 152e4168..f41f3017 100644
--- a/wqflask/wqflask/network_graph/network_graph.py
+++ b/wqflask/wqflask/network_graph/network_graph.py
@@ -44,7 +44,7 @@ import reaper
from utility.THCell import THCell
from utility.TDCell import TDCell
-from base.trait import GeneralTrait
+from base.trait import create_trait
from base import data_set
from utility import webqtlUtil, helper_functions, corr_result_helpers
from utility.tools import GN2_BRANCH_URL
@@ -217,7 +217,7 @@ class NetworkGraph(object):
break
trait_name, dataset_name = trait_db.split(":")
dataset_ob = data_set.create_dataset(dataset_name)
- trait_ob = GeneralTrait(dataset=dataset_ob,
+ trait_ob = create_trait(dataset=dataset_ob,
name=trait_name,
cellid=None)
self.trait_list.append((trait_ob, dataset_ob)) \ No newline at end of file
diff --git a/wqflask/wqflask/resource_manager.py b/wqflask/wqflask/resource_manager.py
new file mode 100644
index 00000000..39a07310
--- /dev/null
+++ b/wqflask/wqflask/resource_manager.py
@@ -0,0 +1,134 @@
+from __future__ import print_function, division, absolute_import
+
+import json
+
+from flask import (Flask, g, render_template, url_for, request, make_response,
+ redirect, flash)
+
+from wqflask import app
+
+from utility.authentication_tools import check_owner_or_admin
+from utility.redis_tools import get_resource_info, get_group_info, get_groups_like_unique_column, get_user_id, get_user_by_unique_column, get_users_like_unique_column, add_access_mask, add_resource, change_resource_owner
+
+from utility.logger import getLogger
+logger = getLogger(__name__)
+
+@app.route("/resources/manage", methods=('GET', 'POST'))
+def manage_resource():
+ params = request.form if request.form else request.args
+ if 'resource_id' in request.args:
+ resource_id = request.args['resource_id']
+ admin_status = check_owner_or_admin(resource_id=resource_id)
+
+ resource_info = get_resource_info(resource_id)
+ group_masks = resource_info['group_masks']
+ group_masks_with_names = get_group_names(group_masks)
+ default_mask = resource_info['default_mask']['data']
+ owner_id = resource_info['owner_id']
+ owner_info = get_user_by_unique_column("user_id", owner_id)
+
+ if 'name' in owner_info:
+ owner_display_name = owner_info['full_name']
+ elif 'user_name' in owner_info:
+ owner_display_name = owner_info['user_name']
+ elif 'email_address' in owner_info:
+ owner_display_name = owner_info['email_address']
+ else:
+ owner_display_name = None
+
+ return render_template("admin/manage_resource.html", owner_name = owner_display_name, resource_id = resource_id, resource_info=resource_info, default_mask=default_mask, group_masks=group_masks_with_names, admin_status=admin_status)
+
+@app.route("/search_for_users", methods=('POST',))
+def search_for_user():
+ params = request.form
+ user_list = []
+ user_list += get_users_like_unique_column("full_name", params['user_name'])
+ user_list += get_users_like_unique_column("email_address", params['user_email'])
+
+ return json.dumps(user_list)
+
+@app.route("/search_for_groups", methods=('POST',))
+def search_for_groups():
+ params = request.form
+ group_list = []
+ group_list += get_groups_like_unique_column("id", params['group_id'])
+ group_list += get_groups_like_unique_column("name", params['group_name'])
+
+ user_list = []
+ user_list += get_users_like_unique_column("full_name", params['user_name'])
+ user_list += get_users_like_unique_column("email_address", params['user_email'])
+ for user in user_list:
+ group_list += get_groups_like_unique_column("admins", user['user_id'])
+ group_list += get_groups_like_unique_column("members", user['user_id'])
+
+ return json.dumps(group_list)
+
+@app.route("/resources/change_owner", methods=('POST',))
+def change_owner():
+ resource_id = request.form['resource_id']
+ if 'new_owner' in request.form:
+ admin_status = check_owner_or_admin(resource_id=resource_id)
+ if admin_status == "owner":
+ new_owner_id = request.form['new_owner']
+ change_resource_owner(resource_id, new_owner_id)
+ flash("The resource's owner has beeen changed.", "alert-info")
+ return redirect(url_for("manage_resource", resource_id=resource_id))
+ else:
+ flash("You lack the permissions to make this change.", "error")
+ return redirect(url_for("manage_resource", resource_id=resource_id))
+ else:
+ return render_template("admin/change_resource_owner.html", resource_id = resource_id)
+
+@app.route("/resources/change_default_privileges", methods=('POST',))
+def change_default_privileges():
+ resource_id = request.form['resource_id']
+ admin_status = check_owner_or_admin(resource_id=resource_id)
+ if admin_status == "owner" or admin_status == "edit-admins":
+ resource_info = get_resource_info(resource_id)
+ default_mask = resource_info['default_mask']
+ if request.form['open_to_public'] == "True":
+ default_mask['data'] = 'view'
+ else:
+ default_mask['data'] = 'no-access'
+ resource_info['default_mask'] = default_mask
+ add_resource(resource_info)
+ flash("Your changes have been saved.", "alert-info")
+ return redirect(url_for("manage_resource", resource_id=resource_id))
+ else:
+ return redirect(url_for("no_access_page"))
+
+@app.route("/resources/add_group", methods=('POST',))
+def add_group_to_resource():
+ resource_id = request.form['resource_id']
+ admin_status = check_owner_or_admin(resource_id=resource_id)
+ if admin_status == "owner" or admin_status == "edit-admins" or admin_status == "edit-access":
+ if 'selected_group' in request.form:
+ group_id = request.form['selected_group']
+ resource_info = get_resource_info(resource_id)
+ default_privileges = resource_info['default_mask']
+ return render_template("admin/set_group_privileges.html", resource_id = resource_id, group_id = group_id, default_privileges = default_privileges)
+ elif all(key in request.form for key in ('data_privilege', 'metadata_privilege', 'admin_privilege')):
+ group_id = request.form['group_id']
+ group_name = get_group_info(group_id)['name']
+ access_mask = {
+ 'data': request.form['data_privilege'],
+ 'metadata': request.form['metadata_privilege'],
+ 'admin': request.form['admin_privilege']
+ }
+ add_access_mask(resource_id, group_id, access_mask)
+ flash("Privileges have been added for group {}.".format(group_name), "alert-info")
+ return redirect(url_for("manage_resource", resource_id=resource_id))
+ else:
+ return render_template("admin/search_for_groups.html", resource_id = resource_id)
+ else:
+ return redirect(url_for("no_access_page"))
+
+def get_group_names(group_masks):
+ group_masks_with_names = {}
+ for group_id, group_mask in group_masks.iteritems():
+ this_mask = group_mask
+ group_name = get_group_info(group_id)['name']
+ this_mask['name'] = group_name
+ group_masks_with_names[group_id] = this_mask
+
+ return group_masks_with_names \ No newline at end of file
diff --git a/wqflask/wqflask/search_results.py b/wqflask/wqflask/search_results.py
index 8f702d58..de4b01eb 100644
--- a/wqflask/wqflask/search_results.py
+++ b/wqflask/wqflask/search_results.py
@@ -1,15 +1,9 @@
-# from __future__ import absolute_import, print_function, division
+from __future__ import absolute_import, print_function, division
-
-import os
-import cPickle
import re
import uuid
from math import *
import time
-import math
-import datetime
-import collections
import re
import requests
@@ -18,18 +12,16 @@ from pprint import pformat as pf
import json
from base.data_set import create_dataset
-from base import trait
+from base.trait import create_trait
from wqflask import parser
from wqflask import do_search
-from utility import webqtlUtil,tools
from db import webqtlDatabaseFunction
-from flask import render_template, Flask, g
+from flask import Flask, g
-from utility import formatting
-from utility import hmac
+from utility import hmac, helper_functions
from utility.tools import GN2_BASE_URL
-from utility.type_checking import is_float, is_int, is_str, get_float, get_int, get_string
+from utility.type_checking import is_str
from utility.logger import getLogger
logger = getLogger(__name__ )
@@ -86,7 +78,7 @@ views.py).
try:
self.search()
except:
- self.search_term_exists = False
+ self.search_term_exists = False
if self.search_term_exists:
self.gen_search_result()
@@ -113,50 +105,49 @@ views.py).
trait_dict = {}
trait_id = result[0]
- trait_dict['index'] = index + 1
- this_trait = trait.GeneralTrait(dataset=self.dataset, name=trait_id, get_qtl_info=True, get_sample_info=False)
- trait_dict['name'] = this_trait.name
- if this_trait.dataset.type == "Publish":
- trait_dict['display_name'] = this_trait.display_name
- else:
- trait_dict['display_name'] = this_trait.name
- trait_dict['dataset'] = this_trait.dataset.name
- trait_dict['hmac'] = hmac.data_hmac('{}:{}'.format(this_trait.name, this_trait.dataset.name))
- if this_trait.dataset.type == "ProbeSet":
- trait_dict['symbol'] = this_trait.symbol
- trait_dict['description'] = this_trait.description_display.decode('utf-8', 'replace')
- trait_dict['location'] = this_trait.location_repr
- trait_dict['mean'] = "N/A"
- trait_dict['additive'] = "N/A"
- if this_trait.mean != "" and this_trait.mean != None:
- trait_dict['mean'] = '%.3f' % this_trait.mean
- trait_dict['lrs_score'] = this_trait.LRS_score_repr
- trait_dict['lrs_location'] = this_trait.LRS_location_repr
- if this_trait.additive != "":
- trait_dict['additive'] = '%.3f' % this_trait.additive
- elif this_trait.dataset.type == "Geno":
- trait_dict['location'] = this_trait.location_repr
- elif this_trait.dataset.type == "Publish":
- trait_dict['description'] = this_trait.description_display
- trait_dict['authors'] = this_trait.authors
- trait_dict['pubmed_id'] = "N/A"
- if this_trait.pubmed_id:
- trait_dict['pubmed_id'] = this_trait.pubmed_id
- trait_dict['pubmed_link'] = this_trait.pubmed_link
- trait_dict['pubmed_text'] = this_trait.pubmed_text
- trait_dict['mean'] = "N/A"
- if this_trait.mean != "" and this_trait.mean != None:
- trait_dict['mean'] = '%.3f' % this_trait.mean
- trait_dict['lrs_score'] = this_trait.LRS_score_repr
- trait_dict['lrs_location'] = this_trait.LRS_location_repr
- trait_dict['additive'] = "N/A"
- if this_trait.additive != "":
- trait_dict['additive'] = '%.3f' % this_trait.additive
- trait_list.append(trait_dict)
- #json_trait_list.append(trait.jsonable_table_row(this_trait, self.dataset.name, index + 1))
+ this_trait = create_trait(dataset=self.dataset, name=trait_id, get_qtl_info=True, get_sample_info=False)
+ if this_trait:
+ trait_dict['index'] = index + 1
+ trait_dict['name'] = this_trait.name
+ if this_trait.dataset.type == "Publish":
+ trait_dict['display_name'] = this_trait.display_name
+ else:
+ trait_dict['display_name'] = this_trait.name
+ trait_dict['dataset'] = this_trait.dataset.name
+ trait_dict['hmac'] = hmac.data_hmac('{}:{}'.format(this_trait.name, this_trait.dataset.name))
+ if this_trait.dataset.type == "ProbeSet":
+ trait_dict['symbol'] = this_trait.symbol
+ trait_dict['description'] = this_trait.description_display.decode('utf-8', 'replace')
+ trait_dict['location'] = this_trait.location_repr
+ trait_dict['mean'] = "N/A"
+ trait_dict['additive'] = "N/A"
+ if this_trait.mean != "" and this_trait.mean != None:
+ trait_dict['mean'] = '%.3f' % this_trait.mean
+ trait_dict['lrs_score'] = this_trait.LRS_score_repr
+ trait_dict['lrs_location'] = this_trait.LRS_location_repr
+ if this_trait.additive != "":
+ trait_dict['additive'] = '%.3f' % this_trait.additive
+ elif this_trait.dataset.type == "Geno":
+ trait_dict['location'] = this_trait.location_repr
+ elif this_trait.dataset.type == "Publish":
+ trait_dict['description'] = this_trait.description_display
+ trait_dict['authors'] = this_trait.authors
+ trait_dict['pubmed_id'] = "N/A"
+ if this_trait.pubmed_id:
+ trait_dict['pubmed_id'] = this_trait.pubmed_id
+ trait_dict['pubmed_link'] = this_trait.pubmed_link
+ trait_dict['pubmed_text'] = this_trait.pubmed_text
+ trait_dict['mean'] = "N/A"
+ if this_trait.mean != "" and this_trait.mean != None:
+ trait_dict['mean'] = '%.3f' % this_trait.mean
+ trait_dict['lrs_score'] = this_trait.LRS_score_repr
+ trait_dict['lrs_location'] = this_trait.LRS_location_repr
+ trait_dict['additive'] = "N/A"
+ if this_trait.additive != "":
+ trait_dict['additive'] = '%.3f' % this_trait.additive
+ trait_list.append(trait_dict)
self.trait_list = json.dumps(trait_list)
- #self.json_trait_list = json.dumps(json_trait_list)
def search(self):
"""
@@ -234,7 +225,6 @@ views.py).
self.header_fields = the_search.header_fields
def get_search_ob(self, a_search):
- logger.debug("[kodak] item is:", pf(a_search))
search_term = a_search['search_term']
search_operator = a_search['separator']
search_type = {}
@@ -243,12 +233,10 @@ views.py).
search_type['key'] = a_search['key'].upper()
else:
search_type['key'] = None
- logger.debug("search_type is:", pf(search_type))
search_ob = do_search.DoSearch.get_search(search_type)
if search_ob:
search_class = getattr(do_search, search_ob)
- logger.debug("search_class is: ", pf(search_class))
the_search = search_class(search_term,
search_operator,
self.dataset,
diff --git a/wqflask/wqflask/show_trait/export_trait_data.py b/wqflask/wqflask/show_trait/export_trait_data.py
index 7ca4a4c0..253c887b 100644
--- a/wqflask/wqflask/show_trait/export_trait_data.py
+++ b/wqflask/wqflask/show_trait/export_trait_data.py
@@ -4,11 +4,17 @@ import simplejson as json
from pprint import pformat as pf
+from base.trait import create_trait
+from base import data_set
+
def export_sample_table(targs):
sample_data = json.loads(targs['export_data'])
trait_name = targs['trait_display_name']
- final_sample_data = []
+
+ meta_data = get_export_metadata(targs['trait_id'], targs['dataset'])
+
+ final_sample_data = meta_data
for sample_group in ['primary_samples', 'other_samples']:
for row in sample_data[sample_group]:
@@ -18,6 +24,28 @@ def export_sample_table(targs):
return trait_name, final_sample_data
+def get_export_metadata(trait_id, dataset_name):
+ dataset = data_set.create_dataset(dataset_name)
+ this_trait = create_trait(dataset=dataset,
+ name=trait_id,
+ cellid=None,
+ get_qtl_info=False)
+
+ metadata = []
+ if dataset.type == "Publish":
+ metadata.append(["Phenotype ID: " + trait_id])
+ metadata.append(["Phenotype URL: " + "http://genenetwork.org/show_trait?trait_id=" + trait_id + "&dataset=" + dataset_name])
+ metadata.append(["Group: " + dataset.group.name])
+ metadata.append(["Phenotype: " + this_trait.description_display.replace(",", "\",\"")])
+ metadata.append(["Authors: " + this_trait.authors])
+ metadata.append(["Title: " + this_trait.title])
+ metadata.append(["Journal: " + this_trait.journal])
+ metadata.append(["Dataset Link: http://gn1.genenetwork.org/webqtl/main.py?FormID=sharinginfo&InfoPageName=" + dataset.name])
+ metadata.append([])
+
+ return metadata
+
+
def dict_to_sorted_list(dictionary):
sorted_list = [item for item in dictionary.iteritems()]
sorted_list = sorted(sorted_list, cmp=cmp_samples)
diff --git a/wqflask/wqflask/show_trait/show_trait.py b/wqflask/wqflask/show_trait/show_trait.py
index 8883e627..5fc69cab 100644
--- a/wqflask/wqflask/show_trait/show_trait.py
+++ b/wqflask/wqflask/show_trait/show_trait.py
@@ -10,9 +10,6 @@ import json as json
from collections import OrderedDict
-import redis
-Redis = redis.StrictRedis()
-
import numpy as np
import scipy.stats as ss
@@ -21,10 +18,15 @@ from flask import Flask, g
from base import webqtlConfig
from base import webqtlCaseData
from wqflask.show_trait.SampleList import SampleList
-from utility import webqtlUtil, Plot, Bunch, helper_functions
-from base.trait import GeneralTrait
+from base.trait import create_trait
from base import data_set
from db import webqtlDatabaseFunction
+from utility import webqtlUtil, Plot, Bunch, helper_functions
+from utility.authentication_tools import check_owner_or_admin
+from utility.tools import locate_ignore_error
+from utility.redis_tools import get_redis_conn, get_resource_id
+Redis = get_redis_conn()
+ONE_YEAR = 60 * 60 * 24 * 365
from pprint import pformat as pf
@@ -47,28 +49,35 @@ class ShowTrait(object):
self.temp_trait = False
self.trait_id = kw['trait_id']
helper_functions.get_species_dataset_trait(self, kw)
+ self.resource_id = get_resource_id(self.dataset, self.trait_id)
+ self.admin_status = check_owner_or_admin(resource_id=self.resource_id)
elif 'group' in kw:
self.temp_trait = True
self.trait_id = "Temp_"+kw['species']+ "_" + kw['group'] + "_" + datetime.datetime.now().strftime("%m%d%H%M%S")
self.temp_species = kw['species']
self.temp_group = kw['group']
self.dataset = data_set.create_dataset(dataset_name = "Temp", dataset_type = "Temp", group_name = self.temp_group)
+
# Put values in Redis so they can be looked up later if added to a collection
- Redis.set(self.trait_id, kw['trait_paste'])
+ Redis.set(self.trait_id, kw['trait_paste'], ex=ONE_YEAR)
self.trait_vals = kw['trait_paste'].split()
- self.this_trait = GeneralTrait(dataset=self.dataset,
+ self.this_trait = create_trait(dataset=self.dataset,
name=self.trait_id,
cellid=None)
+
+ self.admin_status = check_owner_or_admin(dataset=self.dataset, trait_id=self.trait_id)
else:
self.temp_trait = True
self.trait_id = kw['trait_id']
self.temp_species = self.trait_id.split("_")[1]
self.temp_group = self.trait_id.split("_")[2]
self.dataset = data_set.create_dataset(dataset_name = "Temp", dataset_type = "Temp", group_name = self.temp_group)
- self.this_trait = GeneralTrait(dataset=self.dataset,
+ self.this_trait = create_trait(dataset=self.dataset,
name=self.trait_id,
cellid=None)
+
self.trait_vals = Redis.get(self.trait_id).split()
+ self.admin_status = check_owner_or_admin(dataset=self.dataset, trait_id=self.trait_id)
#ZS: Get verify/rna-seq link URLs
try:
@@ -76,12 +85,12 @@ class ShowTrait(object):
if not blatsequence:
#XZ, 06/03/2009: ProbeSet name is not unique among platforms. We should use ProbeSet Id instead.
query1 = """SELECT Probe.Sequence, Probe.Name
- FROM Probe, ProbeSet, ProbeSetFreeze, ProbeSetXRef
- WHERE ProbeSetXRef.ProbeSetFreezeId = ProbeSetFreeze.Id AND
- ProbeSetXRef.ProbeSetId = ProbeSet.Id AND
- ProbeSetFreeze.Name = '%s' AND
- ProbeSet.Name = '%s' AND
- Probe.ProbeSetId = ProbeSet.Id order by Probe.SerialOrder""" % (self.this_trait.dataset.name, self.this_trait.name)
+ FROM Probe, ProbeSet, ProbeSetFreeze, ProbeSetXRef
+ WHERE ProbeSetXRef.ProbeSetFreezeId = ProbeSetFreeze.Id AND
+ ProbeSetXRef.ProbeSetId = ProbeSet.Id AND
+ ProbeSetFreeze.Name = '%s' AND
+ ProbeSet.Name = '%s' AND
+ Probe.ProbeSetId = ProbeSet.Id order by Probe.SerialOrder""" % (self.this_trait.dataset.name, self.this_trait.name)
seqs = g.db.execute(query1).fetchall()
if not seqs:
raise ValueError
@@ -97,10 +106,10 @@ class ShowTrait(object):
query2 = """SELECT Probe.Sequence, Probe.Name
FROM Probe, ProbeSet, ProbeSetFreeze, ProbeSetXRef
WHERE ProbeSetXRef.ProbeSetFreezeId = ProbeSetFreeze.Id AND
- ProbeSetXRef.ProbeSetId = ProbeSet.Id AND
- ProbeSetFreeze.Name = '%s' AND
- ProbeSet.Name = '%s' AND
- Probe.ProbeSetId = ProbeSet.Id order by Probe.SerialOrder""" % (self.this_trait.dataset.name, self.this_trait.name)
+ ProbeSetXRef.ProbeSetId = ProbeSet.Id AND
+ ProbeSetFreeze.Name = '%s' AND
+ ProbeSet.Name = '%s' AND
+ Probe.ProbeSetId = ProbeSet.Id order by Probe.SerialOrder""" % (self.this_trait.dataset.name, self.this_trait.name)
seqs = g.db.execute(query2).fetchall()
for seqt in seqs:
@@ -120,8 +129,8 @@ class ShowTrait(object):
self.UCSC_BLAT_URL = ""
self.UTHSC_BLAT_URL = ""
except:
- self.UCSC_BLAT_URL = ""
- self.UTHSC_BLAT_URL = ""
+ self.UCSC_BLAT_URL = ""
+ self.UTHSC_BLAT_URL = ""
if self.dataset.type == "ProbeSet":
self.show_probes = "True"
@@ -170,6 +179,14 @@ class ShowTrait(object):
self.genofiles = self.dataset.group.get_genofiles()
+ if "QTLReaper" or "R/qtl" in dataset.group.mapping_names: #ZS: No need to grab scales from .geno file unless it's using a mapping method that reads .geno files
+ if self.genofiles:
+ self.scales_in_geno = get_genotype_scales(self.genofiles)
+ else:
+ self.scales_in_geno = get_genotype_scales(self.dataset.group.name + ".geno")
+ else:
+ self.scales_in_geno = {}
+
self.has_num_cases = has_num_cases(self.this_trait)
self.stats_table_width, self.trait_table_width = get_table_widths(self.sample_groups, self.has_num_cases)
@@ -239,9 +256,12 @@ class ShowTrait(object):
#hddn['control_marker'] = self.nearest_marker1+","+self.nearest_marker2
hddn['do_control'] = False
hddn['maf'] = 0.05
+ hddn['mapping_scale'] = "physic"
hddn['compare_traits'] = []
hddn['export_data'] = ""
hddn['export_format'] = "excel"
+ if len(self.scales_in_geno) < 2:
+ hddn['mapping_scale'] = self.scales_in_geno[self.scales_in_geno.keys()[0]][0][0]
# We'll need access to this_trait and hddn in the Jinja2 Template, so we put it inside self
self.hddn = hddn
@@ -251,6 +271,7 @@ class ShowTrait(object):
short_description = short_description,
unit_type = trait_units,
dataset_type = self.dataset.type,
+ scales_in_geno = self.scales_in_geno,
data_scale = self.dataset.data_scale,
sample_group_types = self.sample_group_types,
sample_lists = sample_lists,
@@ -281,7 +302,7 @@ class ShowTrait(object):
if check_if_attr_exists(self.this_trait, 'uniprotid'):
self.uniprot_link = webqtlConfig.UNIPROT_URL % self.this_trait.uniprotid
- self.genotation_link = self.rgd_link = self.gtex_link = self.genebridge_link = self.ucsc_blat_link = self.biogps_link = self.protein_atlas_link = None
+ self.genotation_link = self.rgd_link = self.phenogen_link = self.gtex_link = self.genebridge_link = self.ucsc_blat_link = self.biogps_link = self.protein_atlas_link = None
self.string_link = self.panther_link = self.aba_link = self.ebi_gwas_link = self.wiki_pi_link = self.genemania_link = self.ensembl_link = None
if self.this_trait.symbol:
self.genotation_link = webqtlConfig.GENOTATION_URL % self.this_trait.symbol
@@ -319,6 +340,7 @@ class ShowTrait(object):
if self.dataset.group.species == "rat":
self.rgd_link = webqtlConfig.RGD_URL % (self.this_trait.symbol, self.dataset.group.species.capitalize())
+ self.phenogen_link = webqtlConfig.PHENOGEN_URL % (self.this_trait.symbol)
self.genemania_link = webqtlConfig.GENEMANIA_URL % ("rattus-norvegicus", self.this_trait.symbol)
query = """SELECT kgID, chromosome, txStart, txEnd
@@ -590,11 +612,86 @@ def get_categorical_variables(this_trait, sample_list):
for attribute in sample_list.attributes:
attribute_vals = []
for sample_name in this_trait.data.keys():
- attribute_vals.append(this_trait.data[sample_name].extra_attributes[sample_list.attributes[attribute].name])
-
+ if sample_list.attributes[attribute].name in this_trait.data[sample_name].extra_attributes:
+ attribute_vals.append(this_trait.data[sample_name].extra_attributes[sample_list.attributes[attribute].name])
+ else:
+ attribute_vals.append("N/A")
num_distinct = len(set(attribute_vals))
if num_distinct < 10:
categorical_var_list.append(sample_list.attributes[attribute].name)
- return categorical_var_list \ No newline at end of file
+ return categorical_var_list
+
+def get_genotype_scales(genofiles):
+ geno_scales = {}
+ if type(genofiles) is list:
+ for the_file in genofiles:
+ file_location = the_file['location']
+ geno_scales[file_location] = get_scales_from_genofile(file_location)
+ else:
+ geno_scales[genofiles] = get_scales_from_genofile(genofiles)
+
+ return geno_scales
+
+def get_scales_from_genofile(file_location):
+ geno_path = locate_ignore_error(file_location, 'genotype')
+
+ if not geno_path: #ZS: This is just to allow the code to run when
+ return [["physic", "Mb"]]
+ cm_and_mb_cols_exist = True
+ cm_column = None
+ mb_column = None
+ with open(geno_path, "r") as geno_fh:
+ for i, line in enumerate(geno_fh):
+ if line[0] == "#" or line[0] == "@":
+ if "@scale" in line: #ZS: If the scale is made explicit in the metadata, use that
+ scale = line.split(":")[1].strip()
+ if scale == "morgan":
+ return [["morgan", "cM"]]
+ else:
+ return [["physic", "Mb"]]
+ else:
+ continue
+ if line[:3] == "Chr":
+ first_marker_line = i + 1
+ if line.split("\t")[2].strip() == "cM":
+ cm_column = 2
+ elif line.split("\t")[3].strip() == "cM":
+ cm_column = 3
+ if line.split("\t")[2].strip() == "Mb":
+ mb_column = 2
+ elif line.split("\t")[3].strip() == "Mb":
+ mb_column = 3
+ break
+
+ #ZS: This attempts to check whether the cM and Mb columns are 'real', since some .geno files have one column be a copy of the other column, or have one column that is all 0s
+ cm_all_zero = True
+ mb_all_zero = True
+ cm_mb_all_equal = True
+ for i, line in enumerate(geno_fh):
+ if first_marker_line <= i < first_marker_line + 10: #ZS: I'm assuming there won't be more than 10 markers where the position is listed as 0
+ if cm_column:
+ cm_val = line.split("\t")[cm_column].strip()
+ if cm_val != "0":
+ cm_all_zero = False
+ if mb_column:
+ mb_val = line.split("\t")[mb_column].strip()
+ if mb_val != "0":
+ mb_all_zero = False
+ if cm_column and mb_column:
+ if cm_val != mb_val:
+ cm_mb_all_equal = False
+ else:
+ if i > first_marker_line + 10:
+ break
+
+ #ZS: This assumes that both won't be all zero, since if that's the case mapping shouldn't be an option to begin with
+ if mb_all_zero:
+ return [["morgan", "cM"]]
+ elif cm_mb_all_equal:
+ return [["physic", "Mb"]]
+ elif cm_and_mb_cols_exist:
+ return [["physic", "Mb"], ["morgan", "cM"]]
+ else:
+ return [["physic", "Mb"]]
diff --git a/wqflask/wqflask/static/new/css/show_trait.css b/wqflask/wqflask/static/new/css/show_trait.css
index 09d5e1e3..d3e6672a 100644
--- a/wqflask/wqflask/static/new/css/show_trait.css
+++ b/wqflask/wqflask/static/new/css/show_trait.css
@@ -2,6 +2,10 @@ tr .outlier {
background-color: #ffff99;
}
+table.dataTable tbody tr.selected {
+ background-color: #ffee99;
+}
+
#bar_chart_container {
overflow-x:scroll;
}
diff --git a/wqflask/wqflask/static/new/javascript/dataset_select_menu_orig.js b/wqflask/wqflask/static/new/javascript/dataset_select_menu_orig.js
index ee7be68c..d172907a 100644
--- a/wqflask/wqflask/static/new/javascript/dataset_select_menu_orig.js
+++ b/wqflask/wqflask/static/new/javascript/dataset_select_menu_orig.js
@@ -94,6 +94,40 @@ redo_dropdown = function(dropdown, items) {
_results.push(dropdown.append($("<option />").val(item[0]).text(item[1])));
}
}
+ } else if (dropdown.attr('id') == "type"){
+ type_family_list = [];
+ for (_i = 0, _len = items.length; _i < _len; _i++) {
+ item = items[_i];
+ type_family_list.push([item[0], item[1], item[2]])
+ }
+
+ current_family = ""
+ this_opt_group = null
+ for (_i = 0, _len = type_family_list.length; _i < _len; _i++) {
+ item = type_family_list[_i];
+ if (item[2] != "None" && current_family == ""){
+ current_family = item[2]
+ this_opt_group = $("<optgroup label=\"" + item[2] + "\">")
+ this_opt_group.append($("<option />").val(item[0]).text(item[1]));
+ } else if (current_family != "" && item[2] == current_family){
+ this_opt_group.append($("<option />").val(item[0]).text(item[1]));
+ if (_i == type_family_list.length - 1){
+ _results.push(dropdown.append(this_opt_group))
+ }
+ } else if (current_family != "" && item[2] != current_family && item[2] != "None"){
+ current_family = item[2]
+ _results.push(dropdown.append(this_opt_group))
+ this_opt_group = $("<optgroup label=\"" + current_family + "\">")
+ this_opt_group.append($("<option />").val(item[0]).text(item[1]));
+ if (_i == type_family_list.length - 1){
+ _results.push(dropdown.append(this_opt_group))
+ }
+ } else {
+ _results.push(dropdown.append(this_opt_group))
+ current_family = ""
+ _results.push(dropdown.append($("<option />").val(item[0]).text(item[1])));
+ }
+ }
} else {
for (_i = 0, _len = items.length; _i < _len; _i++) {
item = items[_i];
@@ -104,6 +138,7 @@ redo_dropdown = function(dropdown, items) {
}
}
}
+
return _results;
};
$('#species').change((function(_this) {
diff --git a/wqflask/wqflask/static/new/javascript/draw_corr_scatterplot.js b/wqflask/wqflask/static/new/javascript/draw_corr_scatterplot.js
index a0a88773..956e0467 100644
--- a/wqflask/wqflask/static/new/javascript/draw_corr_scatterplot.js
+++ b/wqflask/wqflask/static/new/javascript/draw_corr_scatterplot.js
@@ -132,6 +132,8 @@ var modebar_options = {
Plotly.downloadImage(gd, {format: 'jpeg'})
}
}],
+ showEditInChartStudio: true,
+ plotlyServerURL: "https://chart-studio.plotly.com",
modeBarButtonsToRemove:['toImage', 'sendDataToCloud', 'hoverClosest', 'hoverCompare', 'hoverClosestCartesian', 'hoverCompareCartesian', 'lasso2d', 'toggleSpikelines'],
displaylogo: false
}
diff --git a/wqflask/wqflask/static/new/javascript/get_covariates_from_collection.js b/wqflask/wqflask/static/new/javascript/get_covariates_from_collection.js
index 8cd6dac3..934cc14d 100644
--- a/wqflask/wqflask/static/new/javascript/get_covariates_from_collection.js
+++ b/wqflask/wqflask/static/new/javascript/get_covariates_from_collection.js
@@ -110,7 +110,7 @@ submit_click = function() {
trait_click = function() {
var dataset, this_trait_url, trait;
trait = $(this).parent().find('.trait').text();
- dataset = $(this).parent().find('.dataset').text();
+ dataset = $(this).parent().find('.dataset').data("dataset");
$("input[name=covariates]").val(trait + ":" + dataset)
$(".selected_covariates").text(trait)
return $.colorbox.close();
diff --git a/wqflask/wqflask/static/new/javascript/group_manager.js b/wqflask/wqflask/static/new/javascript/group_manager.js
new file mode 100644
index 00000000..4c172cbf
--- /dev/null
+++ b/wqflask/wqflask/static/new/javascript/group_manager.js
@@ -0,0 +1,38 @@
+$('#add_to_admins').click(function() {
+ add_emails('admin')
+})
+
+$('#add_to_members').click(function() {
+ add_emails('member')
+})
+
+$('#clear_admins').click(function(){
+ clear_emails('admin')
+})
+
+$('#clear_members').click(function(){
+ clear_emails('member')
+})
+
+
+function add_emails(user_type){
+ var email_address = $('input[name=user_email]').val();
+ var email_list_string = $('input[name=' + user_type + '_emails_to_add]').val().trim()
+ console.log(email_list_string)
+ if (email_list_string == ""){
+ var email_set = new Set();
+ } else {
+ var email_set = new Set(email_list_string.split(","))
+ }
+ email_set.add(email_address)
+
+ $('input[name=' + user_type + '_emails_to_add]').val(Array.from(email_set).join(','))
+
+ var emails_display_string = Array.from(email_set).join('\n')
+ $('.added_' + user_type + 's').val(emails_display_string)
+}
+
+function clear_emails(user_type){
+ $('input[name=' + user_type + '_emails_to_add]').val("")
+ $('.added_' + user_type + 's').val("")
+} \ No newline at end of file
diff --git a/wqflask/wqflask/static/new/javascript/search_results.js b/wqflask/wqflask/static/new/javascript/search_results.js
index 8fa698b4..39aae113 100644
--- a/wqflask/wqflask/static/new/javascript/search_results.js
+++ b/wqflask/wqflask/static/new/javascript/search_results.js
@@ -137,40 +137,6 @@ $(function() {
}
};
- remove = function() {
- var traits, uc_id;
- checked_traits = $("#trait_table input:checked");
- traits = checked_traits.map(function() {
- return $(this).val();
- }).get();
- console.log("checked length is:", traits.length);
- console.log("checked is:", traits);
- if ( $("#uc_id").length ) {
- uc_id = $("#uc_id").val();
- return $.ajax({
- type: "POST",
- url: "/collections/remove",
- data: {
- uc_id: uc_id,
- traits: traits
- },
- success: removed_traits
- });
- }
- else {
- collection_name = $("#collection_name").val();
- return $.ajax({
- type: "POST",
- url: "/collections/remove",
- data: {
- collection_name: collection_name,
- traits: traits
- },
- success: removed_traits
- });
- }
- };
-
submit_bnw = function() {
trait_data = get_traits_from_table("trait_table", "submit_bnw")
}
@@ -296,7 +262,6 @@ $(function() {
$("#deselect_all").click(deselect_all);
$("#invert").click(invert);
$("#add").click(add);
- $("#remove").click(remove);
$("#submit_bnw").click(submit_bnw);
$("#export_traits").click(export_traits);
$('.trait_checkbox, .btn').click(change_buttons);
diff --git a/wqflask/wqflask/static/new/javascript/show_trait.js b/wqflask/wqflask/static/new/javascript/show_trait.js
index d94a2347..c0784073 100644
--- a/wqflask/wqflask/static/new/javascript/show_trait.js
+++ b/wqflask/wqflask/static/new/javascript/show_trait.js
@@ -515,10 +515,10 @@ $('select[name=corr_type]').change(on_corr_method_change);
submit_special = function(url) {
$("#trait_data_form").attr("action", url);
- return $("#trait_data_form").submit();
+ $("#trait_data_form").submit();
};
-var corr_input_list = ['corr_type', 'trait_id', 'dataset', 'group', 'tool_used', 'form_url', 'corr_sample_method', 'corr_samples_group', 'corr_dataset', 'min_expr',
+var corr_input_list = ['corr_type', 'primary_samples', 'trait_id', 'dataset', 'group', 'tool_used', 'form_url', 'corr_sample_method', 'corr_samples_group', 'corr_dataset', 'min_expr',
'corr_return_results', 'loc_chr', 'min_loc_mb', 'max_loc_mb', 'p_range_lower', 'p_range_upper']
$(".corr_compute").on("click", (function(_this) {
@@ -755,12 +755,12 @@ check_for_zero_to_one_vals = function() {
$('.trait_value_input').each(function() {
current_value = $(this).data("value")
if(isNaN(current_value)) {
- return;
+ return true;
} else {
current_value = parseFloat(current_value)
- if (0 < current_value && current_value < 1){
+ if (0 <= current_value && current_value < 1){
zero_to_one_vals_exist = true
- return false;
+ return false
}
}
});
@@ -769,7 +769,6 @@ check_for_zero_to_one_vals = function() {
normalize_data = function() {
if ($('#norm_method option:selected').val() == 'log2' || $('#norm_method option:selected').val() == 'log10'){
- zero_to_one_vals_exist = check_for_zero_to_one_vals();
if ($('input[name="transform"]').val() != "log2" && $('#norm_method option:selected').val() == 'log2') {
log2_normalize_data(zero_to_one_vals_exist)
$('input[name="transform"]').val("log2")
@@ -810,7 +809,28 @@ normalize_data = function() {
}
}
-$('#normalize').click(normalize_data);
+zero_to_one_vals_exist = false
+
+show_transform_warning = function() {
+ transform_type = $('#norm_method option:selected').val()
+ zero_to_one_vals_exist = check_for_zero_to_one_vals();
+ if (transform_type == "log2" || transform_type == "log10"){
+ if (zero_to_one_vals_exist){
+ $('#transform_alert').css("display", "block")
+ }
+ } else {
+ $('#transform_alert').css("display", "none")
+ }
+}
+
+$('#norm_method').change(function(){
+ show_transform_warning()
+});
+$('#normalize').hover(function(){
+ show_transform_warning()
+});
+
+$('#normalize').click(normalize_data)
switch_qnorm_data = function() {
return $('.trait_value_input').each((function(_this) {
@@ -1033,12 +1053,16 @@ val_range = root.chart_range[1] - root.chart_range[0]
if (val_range < 0.05){
tick_digits = '.3f'
+ left_margin = 80
} else if (val_range < 0.5) {
tick_digits = '.2f'
+ left_margin = 70
} else if (val_range < 5){
tick_digits = '.1f'
+ left_margin = 60
} else {
tick_digits = 'f'
+ left_margin = 55
}
if (js_data.num_values < 256) {
@@ -1077,7 +1101,7 @@ if (js_data.num_values < 256) {
width: bar_chart_width,
height: 600,
margin: {
- l: 55,
+ l: left_margin,
r: 30,
t: 30,
b: bottom_margin
diff --git a/wqflask/wqflask/static/new/javascript/show_trait_mapping_tools.js b/wqflask/wqflask/static/new/javascript/show_trait_mapping_tools.js
index 478ed87e..4dce0705 100644
--- a/wqflask/wqflask/static/new/javascript/show_trait_mapping_tools.js
+++ b/wqflask/wqflask/static/new/javascript/show_trait_mapping_tools.js
@@ -3,9 +3,8 @@
var block_outliers, composite_mapping_fields, do_ajax_post, get_progress, mapping_method_fields, open_mapping_results, outlier_text, showalert, submit_special, toggle_enable_disable, update_time_remaining;
submit_special = function(url) {
- console.log("In submit_special");
$("#trait_data_form").attr("action", url);
- return $("#trait_data_form").submit();
+ $("#trait_data_form").submit();
};
update_time_remaining = function(percent_complete) {
@@ -131,11 +130,8 @@
};
outlier_text = "One or more outliers exist in this data set. Please review values before mapping. Including outliers when mapping may lead to misleading results.";
-
- runtime_warning_text = "This function could take as long as 10-20 minutes to run, so please do not close your browser window until it finishes."
-
showalert = function(message, alerttype) {
- return $('#alert_placeholder').append('<div id="alertdiv" class="alert ' + alerttype + '"><a class="close" data-dismiss="alert">�</a><span>' + message + '</span></div>');
+ return $('#outlier_alert_placeholder').append('<div id="mapping_alert" class="alert ' + alerttype + '"><a class="close" data-dismiss="alert">�</a><span>' + message + '</span></div>');
};
$('#suggestive').hide();
@@ -165,6 +161,7 @@
url = "/loading";
$('input[name=method]').val("rqtl_geno");
$('input[name=selected_chr]').val($('#chr_rqtl_geno').val());
+ $('input[name=mapping_scale]').val($('#scale_rqtl_geno').val());
$('input[name=genofile]').val($('#genofile_rqtl_geno').val());
$('input[name=num_perm]').val($('input[name=num_perm_rqtl_geno]').val());
$('input[name=categorical_vars]').val(js_data.categorical_vars)
@@ -210,6 +207,7 @@
url = "/loading";
$('input[name=method]').val("reaper");
$('input[name=selected_chr]').val($('#chr_reaper').val());
+ $('input[name=mapping_scale]').val($('#scale_reaper').val());
$('input[name=genofile]').val($('#genofile_reaper').val());
$('input[name=num_perm]').val($('input[name=num_perm_reaper]').val());
$('input[name=control_marker]').val($('input[name=control_reaper]').val());
@@ -289,4 +287,21 @@
return toggle_enable_disable("#suggestive_lrs");
});
+ $('#genofile_rqtl_geno').change(function() {
+ geno_location = $(this).children("option:selected").val().split(":")[0]
+ $('#scale_rqtl_geno').empty()
+ the_scales = js_data.scales_in_geno[geno_location]
+ for (var i = 0; i < the_scales.length; i++){
+ $('#scale_rqtl_geno').append($("<option></option>").attr("value", the_scales[i][0]).text(the_scales[i][1]));
+ }
+ });
+ $('#genofile_reaper').change(function() {
+ geno_location = $(this).children("option:selected").val().split(":")[0]
+ $('#scale_reaper').empty()
+ the_scales = js_data.scales_in_geno[geno_location]
+ for (var i = 0; i < the_scales.length; i++){
+ $('#scale_reaper').append($("<option></option>").attr("value", the_scales[i][0]).text(the_scales[i][1]));
+ }
+ });
+
}).call(this);
diff --git a/wqflask/wqflask/templates/admin/change_resource_owner.html b/wqflask/wqflask/templates/admin/change_resource_owner.html
new file mode 100644
index 00000000..ae9409b0
--- /dev/null
+++ b/wqflask/wqflask/templates/admin/change_resource_owner.html
@@ -0,0 +1,116 @@
+{% extends "base.html" %}
+{% block title %}Resource Manager{% 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="/static/new/css/show_trait.css" />
+{% endblock %}
+{% block content %}
+<!-- Start of body -->
+ <div class="container">
+ <div class="page-header">
+ <h1>Search for user to assign ownership to:</h1>
+ </div>
+ <form id="change_owner_form" action="/resources/change_owner" method="POST">
+ <input type="hidden" name="resource_id" value="{{ resource_id }}">
+ <div style="min-width: 600px; max-width: 800px;">
+ <fieldset>
+ <div class="form-horizontal" style="width: 900px;">
+ <div style="margin-bottom: 30px;">
+ <h2>Search for user by either name or e-mail:</h2>
+ </div>
+ <div class="form-group" style="padding-left: 20px;">
+ <label for="user_name" class="col-xs-3" style="float: left; font-size: 18px;">User's Name:</label>
+ <div class="controls input-append col-xs-9" style="display: flex; padding-left: 20px; float: left;">
+ <input name="user_name" type="text" value="">
+ </div>
+ </div>
+ <div class="form-group" style="padding-left: 20px;">
+ <label for="user_email" class="col-xs-3" style="float: left; font-size: 18px;">User's E-mail:</label>
+ <div class="controls input-append col-xs-9" style="display: flex; padding-left: 20px; float: left;">
+ <input name="user_email" type="text" value="">
+ </div>
+ </div>
+ <div class="form-group" style="padding-left: 20px;">
+ <label class="col-xs-3" style="float: left; font-size: 18px;"></label>
+ <div class="controls input-append col-xs-9" style="display: flex; padding-left: 20px; float: left;">
+ <button type="button" id="find_users" class="btn btn-primary">Search</button>
+ <button style="margin-left: 20px; display: none;" type="submit" id="submit_new_owner" class="btn btn-success">Assign Ownership to Selected User</button>
+ </div>
+ </div>
+ </div>
+ </fieldset>
+ <hr>
+ <div id="user_results">
+ </div>
+ </div>
+ </form>
+ </div>
+
+<!-- End of body -->
+
+{% endblock %}
+
+{% block js %}
+ <script language="javascript" type="text/javascript" src="{{ url_for('js', filename='DataTables/js/jquery.dataTables.min.js') }}"></script>
+
+ <script language="javascript">
+ $('#find_users').click(function() {
+ $.ajax({
+ method: "POST",
+ url: "/search_for_users",
+ data: {
+ user_name: $('input[name=user_name]').val(),
+ user_email: $('input[name=user_email]').val()
+ },
+ success: populate_users
+ });
+ })
+
+ populate_users = function(json_user_list){
+ var user_list = JSON.parse(json_user_list)
+
+ var the_html = ""
+ if (user_list.length > 0){
+ the_html += "<table id='users_table' style='padding-top: 10px; width: 100%;' class='table-hover table-striped cell-border'>";
+ the_html += "<thead><tr><th></th><th>Index</th><th>Name</th><th>E-mail Address</th><th>Organization</th></tr></thead>";
+ the_html += "<tbody>";
+ for (_i = 0, _len = user_list.length; _i < _len; _i++) {
+ this_user = user_list[_i]
+ the_html += "<tr>";
+ the_html += "<td align='center' class='select_user'><input type='radio' name='new_owner' value='" + this_user.user_id + "'></td>";
+ the_html += "<td>" + (_i + 1).toString() + "</td>"
+ if ("full_name" in this_user) {
+ the_html += "<td>" + this_user.full_name + "</td>";
+ } else {
+ the_html += "<td>N/A</td>"
+ }
+ if ("email_address" in this_user) {
+ the_html += "<td>" + this_user.email_address + "</td>";
+ } else {
+ the_html += "<td>N/A</td>"
+ }
+ if ("organization" in this_user) {
+ the_html += "<td>" + this_user.organization + "</td>";
+ } else {
+ the_html += "<td>N/A</td>"
+ }
+ the_html += "</tr>"
+ }
+ the_html += "</tbody>";
+ the_html += "</table>";
+ } else {
+ the_html = "<span>No users were found matching the entered criteria.</span>"
+ }
+
+ $('#user_results').html(the_html)
+ if (user_list.length > 0){
+ $('#users_table').dataTable({
+ 'order': [[1, "asc" ]],
+ 'sDom': 'tr'
+ });
+ $('input[name=select_user]:eq(0)').prop("checked", true)
+ $('#submit_new_owner').css("display", "inline-block")
+ }
+ }
+ </script>
+{% endblock %}
diff --git a/wqflask/wqflask/templates/admin/create_group.html b/wqflask/wqflask/templates/admin/create_group.html
new file mode 100644
index 00000000..5a6929fb
--- /dev/null
+++ b/wqflask/wqflask/templates/admin/create_group.html
@@ -0,0 +1,89 @@
+{% extends "base.html" %}
+{% block title %}Group Manager{% endblock %}
+{% block content %}
+<!-- Start of body -->
+ <div class="container">
+ <div class="page-header">
+ <h1>Create Group</h1>
+ </div>
+ <form action="/groups/create" method="POST">
+ <input type="hidden" name="admin_emails_to_add" value="">
+ <input type="hidden" name="member_emails_to_add" value="">
+ <fieldset>
+ <div class="form-horizontal" style="width: 900px; margin-bottom: 50px;">
+ <div class="form-group" style="padding-left: 20px;">
+ <label for="group_name" class="col-xs-3" style="float: left; font-size: 18px;">Group Name:</label>
+ <div class="controls input-append col-xs-9" style="display: flex; padding-left: 20px; float: left;">
+ <div class="col-xs-12">
+ <input name="group_name" type="text" style="width:100%;"></input>
+ </div>
+ </div>
+ </div>
+ <div class="form-group" style="padding-left: 20px;">
+ <label for="user_email" class="col-xs-3" style="float: left; font-size: 18px;">Add User Email:</label>
+ <div class="controls input-append col-xs-9" style="display: flex; padding-left: 20px; float: left;">
+ <div class="col-xs-12">
+ <input name="user_email" type="text" style="width:100%;"></input>
+ </div>
+ </div>
+ </div>
+ <div class="form-group" style="padding-left: 20px;">
+ <label class="col-xs-3"></label>
+ <div class="controls input-append col-xs-9" style="display: flex; padding-left: 20px; float: left;">
+ <div class="col-xs-6">
+ <button type="button" id="add_to_admins" class="btn btn-default">Add to Admins</button>
+ </div>
+ <div class="col-xs-6">
+ <button type="button" id="add_to_members" class="btn btn-default">Add to Members</button>
+ </div>
+ </div>
+ </div>
+ <div class="form-group" style="padding-left: 20px;">
+ <label class="col-xs-3" style="font-size: 18px;">Members to be added:</label>
+ <div class="controls input-append col-xs-9" style="display: flex; padding-left: 20px; float: left;">
+ <div class="col-xs-6">
+ <textarea rows="8" cols="60" readonly placeholder="No users added" style="overflow-y: scroll; resize: none; width: 200px;" class="added_admins"></textarea>
+ </div>
+ <div class="col-xs-6">
+ <textarea rows="8" cols="60" readonly placeholder="No users added" style="overflow-y: scroll; resize: none; width: 200px;" class="added_members"></textarea>
+ </div>
+ </div>
+ </div>
+ <div class="form-group" style="padding-left: 20px;">
+ <label class="col-xs-3"></label>
+ <div class="controls input-append col-xs-9" style="display: flex; padding-left: 20px; float: left;">
+ <div class="col-xs-6">
+ <button type="button" id="clear_admins" class="btn btn-default">Clear</button>
+ </div>
+ <div class="col-xs-6">
+ <button type="button" id="clear_members" class="btn btn-default">Clear</button>
+ </div>
+ </div>
+ </div>
+ <div class="form-group" style="padding-left: 20px;">
+ <label for="create_group" class="col-xs-3" style="float: left; font-size: 18px;"></label>
+ <div class="controls input-append col-xs-9" style="display: flex; padding-left: 20px; float: left;">
+ <div class="col-xs-6">
+ <button type="submit" id="create_group" class="btn btn-primary">Create Group</button>
+ </div>
+ </div>
+ </div>
+ </div>
+ </fieldset>
+ </form>
+ </div>
+
+
+
+<!-- End of body -->
+
+{% endblock %}
+
+{% block js %}
+ <script language="javascript" type="text/javascript" src="/static/new/packages/DataTables/js/jquery.js"></script>
+ <script language="javascript" type="text/javascript" src="/static/new/javascript/group_manager.js"></script>
+ <script language="javascript" type="text/javascript" src="/static/packages/underscore/underscore-min.js"></script>
+
+ <script type="text/javascript" charset="utf-8">
+ </script>
+{% endblock %}
diff --git a/wqflask/wqflask/templates/admin/group_manager.html b/wqflask/wqflask/templates/admin/group_manager.html
index ea9026a6..c8ed6851 100644
--- a/wqflask/wqflask/templates/admin/group_manager.html
+++ b/wqflask/wqflask/templates/admin/group_manager.html
@@ -1,42 +1,97 @@
{% extends "base.html" %}
{% block title %}Group Manager{% 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="/static/new/packages/DataTables/extensions/buttons.dataTables.css">
+ <link rel="stylesheet" type="text/css" href="/static/new/css/show_trait.css" />
+{% endblock %}
{% block content %}
<!-- Start of body -->
- {{ header("List of groups", "" )}}
-
<div class="container">
<div class="page-header">
-
+ <h1>Manage Groups</h1>
+ {% if admin_groups|length != 0 or member_groups|length != 0 %}
+ <div style="display: inline;">
+ <button type="button" id="create_group" class="btn btn-primary" data-url="/groups/create">Create Group</button>
+ <button type="button" id="remove_groups" class="btn btn-primary" data-url="/groups/remove">Remove Selected Groups</button>
+ </div>
+ {% endif %}
</div>
- <form>
- <div class="control-group">
- <b>Group Name: </b>
- <div class="input-append">
- <input type="text" name="group_name">
- <button type="submit" class="btn btn-primary">Save</button>
- </div>
+ <form id="groups_form" action="/groups/manage" method="POST">
+ <input type="hidden" name="selected_group_ids" value="">
+ <div style="min-width: 800px; max-width: 1000px;">
+ {% if admin_groups|length == 0 and member_groups|length == 0 %}
+ <h4>You currently aren't a member or admin of any groups.</h4>
+ <br>
+ <button type="button" id="create_group" class="btn btn-primary" data-url="/groups/create">Create a new group</button>
+ {% else %}
+ <div style="margin-top: 20px;"><h2>Admin Groups</h2></div>
+ <hr>
+ {% if admin_groups|length == 0 %}
+ <h4>You currently aren't the administrator of any groups.</h4>
+ {% else %}
+ <table id="admin_groups" class="table-hover table-striped cell-border" style="float: left;">
+ <thead>
+ <tr>
+ <th></th>
+ <th>Index</th>
+ <th>Name</th>
+ <th># Members</th>
+ <th>Created</th>
+ <th>Last Changed</th>
+ <th>Group ID</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for group in admin_groups %}
+ <tr>
+ <td><input type="checkbox" name="group_id" value="{{ group.id }}"></td>
+ <td align="right">{{ loop.index }}</td>
+ <td><a href="/groups/view?id={{ group.id }}">{{ group.name }}</a></td>
+ <td align="right">{{ group.admins|length + group.members|length }}</td>
+ <td>{{ group.created_timestamp }}</td>
+ <td>{{ group.changed_timestamp }}</td>
+ <td>{{ group.id }}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+ {% endif %}
+ </div>
+ <hr>
+ <div style="min-width: 800px; max-width: 1000px;">
+ <div><h2>User Groups</h2></div>
+ <hr>
+ {% if member_groups|length == 0 %}
+ <h4>You currently aren't a member of any groups.</h4>
+ {% else %}
+ <table id="member_groups" class="table-hover table-striped cell-border" style="float: left;">
+ <thead>
+ <tr>
+ <th></th>
+ <th>Index</th>
+ <th>Name</th>
+ <th># Members</th>
+ <th>Created</th>
+ <th>Last Changed</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for group in member_groups %}
+ <tr>
+ <td><input type="checkbox" name="read" value="{{ group.id }}"></td>
+ <td>{{ loop.index }}</td>
+ <td>{{ group.name }}</td>
+ <td>{{ group.admins|length + group.members|length }}</td>
+ <td>{{ group.created_timestamp }}</td>
+ <td>{{ group.changed_timestamp }}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+ {% endif %}
+ {% endif %}
</div>
-
- <table id="dataset_list" class="table table-hover">
- <thead>
- <tr>
- <th>Read</th>
- <th>Type</th>
- <th>ID</th>
- <th>Name</th>
- <th>Full Name</th>
- </tr>
- </thead>
- {% for dataset in datasets %}
- <tr>
- <td><input type="checkbox" name="read" value="{{ dataset.type }}:{{ dataset.name }}"></td>
- <td>{{ dataset.type }}</td>
- <td>{{ dataset.id }}</td>
- <td>{{ dataset.name }}</td>
- <td>{{ dataset.fullname }}</td>
- </tr>
- {% endfor %}
- </table>
</form>
</div>
@@ -45,35 +100,40 @@
{% endblock %}
{% block js %}
- <script language="javascript" type="text/javascript" src="/static/new/packages/DataTables/js/jquery.js"></script>
- <script language="javascript" type="text/javascript" src="/static/new/packages/DataTables/js/jquery.dataTables.min.js"></script>
- <script language="javascript" type="text/javascript" src="/static/packages/DT_bootstrap/DT_bootstrap.js"></script>
- <script language="javascript" type="text/javascript" src="/static/packages/TableTools/media/js/TableTools.min.js"></script>
- <script language="javascript" type="text/javascript" src="/static/packages/underscore/underscore-min.js"></script>
+ <script language="javascript" type="text/javascript" src="{{ url_for('js', filename='DataTables/js/jquery.dataTables.min.js') }}"></script>
<script type="text/javascript" charset="utf-8">
$(document).ready( function () {
- console.time("Creating table");
- $('#dataset_list').dataTable( {
- "sDom": "Tftipr",
- "oTableTools": {
- "aButtons": [
- "copy",
- "print",
- {
- "sExtends": "collection",
- "sButtonText": 'Save <span class="caret" />',
- "aButtons": [ "csv", "xls", "pdf" ]
- }
- ],
- "sSwfPath": "/static/packages/TableTools/media/swf/copy_csv_xls_pdf.swf"
- },
- "iDisplayLength": 50,
- "bLengthChange": true,
- "bDeferRender": true,
- "bSortClasses": false
- } );
- console.timeEnd("Creating table");
+ {% if admin_groups|length != 0 %}
+ $('#admin_groups').dataTable({
+ 'sDom': 'tr'
+ });
+ {% endif %}
+ {% if member_groups|length != 0 %}
+ $('#member_groups').dataTable({
+ 'sDom': 'tr'
+ });
+ {% endif %}
+ submit_special = function(url) {
+ $("#groups_form").attr("action", url);
+ return $("#groups_form").submit();
+ };
+
+ $("#create_group").on("click", function() {
+ url = $(this).data("url")
+ return submit_special(url)
+ });
+
+ $("#remove_groups").on("click", function() {
+ url = $(this).data("url")
+ groups = []
+ $("input[name=group_id]:checked").each(function() {
+ groups.push($(this).val());
+ });
+ groups_string = groups.join(":")
+ $("input[name=selected_group_ids]").val(groups_string)
+ return submit_special(url)
+ });
});
</script>
{% endblock %}
diff --git a/wqflask/wqflask/templates/admin/manage_resource.html b/wqflask/wqflask/templates/admin/manage_resource.html
new file mode 100644
index 00000000..0b12eaae
--- /dev/null
+++ b/wqflask/wqflask/templates/admin/manage_resource.html
@@ -0,0 +1,108 @@
+{% extends "base.html" %}
+{% block title %}Resource Manager{% 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="/static/new/css/show_trait.css" />
+{% endblock %}
+{% block content %}
+<!-- Start of body -->
+ <div class="container">
+ {{ flash_me() }}
+ <div class="page-header" style="display: inline-block;">
+ <h1>Resource Manager</h1>
+ <h3>{% if owner_name is not none %}Current Owner: {{ owner_name }}{% endif %} {% if admin_status == "owner" %}<button id="change_owner" class="btn btn-danger" data-url="/resources/change_owner" style="margin-left: 20px;">Change Owner</button>{% endif %}</h3>
+ </div>
+ <form id="manage_resource" action="/resources/manage" method="POST">
+ <input type="hidden" name="resource_id" value="{{ resource_id }}">
+ <div style="min-width: 600px; max-width: 800px;">
+ <fieldset>
+ <div class="form-horizontal" style="width: 900px; margin-bottom: 50px;">
+ <div class="form-group" style="padding-left: 20px;">
+ <label for="group_name" class="col-xs-3" style="float: left; font-size: 18px;">Resource Name:</label>
+ <div class="controls input-append col-xs-9" style="display: flex; padding-left: 20px; float: left;">
+ {{ resource_info.name }}
+ </div>
+ </div>
+ {% if admin_status == "owner" %}
+ <div class="form-group" style="padding-left: 20px;">
+ <label for="user_email" class="col-xs-3" style="float: left; font-size: 18px;">Open to Public:</label>
+ <div class="controls input-append col-xs-9" style="display: flex; padding-left: 20px; float: left;">
+ <label class="radio-inline">
+ <input type="radio" name="open_to_public" value="True" {% if default_mask != 'no-access' %}checked{% endif %}>
+ Yes
+ </label>
+ <label class="radio-inline">
+ <input type="radio" name="open_to_public" value="False" {% if default_mask == 'no-access' %}checked{% endif %}>
+ No
+ </label>
+ </div>
+ </div>
+ <div class="form-group" style="padding-left: 20px;">
+ <label class="col-xs-3" style="float: left; font-size: 18px;"></label>
+ <div class="controls input-append col-xs-9" style="display: flex; padding-left: 20px; float: left;">
+ <button id="save_changes" class="btn btn-primary" data-url="/resources/change_default_privileges">Save Changes</button>
+ </div>
+ </div>
+ {% endif %}
+ </div>
+ </fieldset>
+ </div>
+ {% if admin_status == "owner" or admin_status == "edit-admins" or admin_status == "edit-access" %}
+ <div style="min-width: 600px; max-width: 800px;">
+ <hr>
+ <button id="add_group_to_resource" class="btn btn-primary" style="margin-bottom: 30px;" data-url="/resources/add_group">Add Group</button>
+ <br>
+ {% if group_masks|length > 0 %}
+ <h2>Current Group Permissions</h2>
+ <hr>
+ <table id="groups_table" class="table-hover table-striped cell-border">
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Data</th>
+ <th>Metadata</th>
+ <th>Admin</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for key, value in group_masks.iteritems() %}
+ <tr>
+ <td>{{ value.name }}</td>
+ <td>{{ value.data }}</td>
+ <td>{{ value.metadata }}</td>
+ <td>{{ value.admin }}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+ {% else %}
+ <h3>No groups are currently added to this resource.</h3>
+ {% endif %}
+ </div>
+ {% endif %}
+ </form>
+ </div>
+
+
+
+<!-- End of body -->
+
+{% endblock %}
+
+{% block js %}
+ <script language="javascript" type="text/javascript" src="{{ url_for('js', filename='DataTables/js/jquery.dataTables.min.js') }}"></script>
+
+ <script type="text/javascript" charset="utf-8">
+ $('#add_group_to_resource, #save_changes, #change_owner').click(function(){
+ url = $(this).data("url");
+ $('#manage_resource').attr("action", url)
+ $('#manage_resource').submit()
+ })
+
+ {% if group_masks|length > 0 %}
+ $('#groups_table').dataTable({
+ 'sDom': 'tr',
+ });
+ {% endif %}
+ </script>
+{% endblock %}
diff --git a/wqflask/wqflask/templates/admin/search_for_groups.html b/wqflask/wqflask/templates/admin/search_for_groups.html
new file mode 100644
index 00000000..f304a172
--- /dev/null
+++ b/wqflask/wqflask/templates/admin/search_for_groups.html
@@ -0,0 +1,134 @@
+{% extends "base.html" %}
+{% block title %}Resource Manager{% 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="/static/new/css/show_trait.css" />
+{% endblock %}
+{% block content %}
+<!-- Start of body -->
+ <div class="container">
+ <div class="page-header">
+ <h1>Find Groups</h1>
+ </div>
+ <form id="add_group" action="/resources/add_group" method="POST">
+ <input type="hidden" name="resource_id" value="{{ resource_id }}">
+ <div style="min-width: 600px; max-width: 800px;">
+ <fieldset>
+ <div class="form-horizontal" style="width: 900px;">
+ <div style="margin-bottom: 30px;">
+ <h2>Search by:</h2>
+ </div>
+ <div class="form-group" style="padding-left: 20px;">
+ <label for="group_name" class="col-xs-3" style="float: left; font-size: 18px;">Group ID:</label>
+ <div class="controls input-append col-xs-9" style="display: flex; padding-left: 20px; float: left;">
+ <input name="group_id" type="text" value="">
+ </div>
+ </div>
+ <div class="form-group" style="padding-left: 20px;">
+ <label for="group_name" class="col-xs-3" style="float: left; font-size: 18px;">Group Name:</label>
+ <div class="controls input-append col-xs-9" style="display: flex; padding-left: 20px; float: left;">
+ <input name="group_name" type="text" value="">
+ </div>
+ </div>
+ <div class="form-group" style="padding-left: 20px;">
+ <label for="user_name" class="col-xs-3" style="float: left; font-size: 18px;">User Name:</label>
+ <div class="controls input-append col-xs-9" style="display: flex; padding-left: 20px; float: left;">
+ <input name="user_name" type="text" value="">
+ </div>
+ </div>
+ <div class="form-group" style="padding-left: 20px;">
+ <label for="user_email" class="col-xs-3" style="float: left; font-size: 18px;">User E-mail:</label>
+ <div class="controls input-append col-xs-9" style="display: flex; padding-left: 20px; float: left;">
+ <input name="user_email" type="text" value="">
+ </div>
+ </div>
+ <div class="form-group" style="padding-left: 20px;">
+ <label class="col-xs-3" style="float: left; font-size: 18px;"></label>
+ <div class="controls input-append col-xs-9" style="display: flex; padding-left: 20px; float: left;">
+ <button type="button" id="find_groups" class="btn btn-primary">Search</button>
+ <button style="margin-left: 20px; display: none;" type="submit" id="submit_group" class="btn btn-success">Add Privileges for Selected Group</button>
+ </div>
+ </div>
+ </div>
+ </fieldset>
+ <hr>
+ <div id="group_results">
+ </div>
+ </div>
+ </form>
+ </div>
+
+<!-- End of body -->
+
+{% endblock %}
+
+{% block js %}
+ <script language="javascript" type="text/javascript" src="/static/new/packages/DataTables/js/jquery.js"></script>
+ <script language="javascript" type="text/javascript" src="/static/new/javascript/group_manager.js"></script>
+ <script language="javascript" type="text/javascript" src="{{ url_for('js', filename='DataTables/js/jquery.dataTables.min.js') }}"></script>
+
+ <script type="text/javascript" charset="utf-8">
+
+ $('#find_groups').click(function() {
+ $.ajax({
+ method: "POST",
+ url: "/search_for_groups",
+ data: {
+ group_id: $('input[name=group_id]').val(),
+ group_name: $('input[name=group_name]').val(),
+ user_name: $('input[name=user_name]').val(),
+ user_email: $('input[name=user_email]').val()
+ },
+ success: populate_groups
+ });
+ })
+
+ populate_groups = function(json_group_list){
+ console.log(json_group_list)
+ var group_list = JSON.parse(json_group_list)
+
+ var the_html = ""
+ if (group_list.length > 0){
+ the_html += "<table id='groups_table' style='padding-top: 10px; width: 100%;' class='table-hover table-striped cell-border'>";
+ the_html += "<thead><tr><th></th><th>Index</th><th>Name</th><th># Admins</th><th># Members</th></tr></thead>";
+ the_html += "<tbody>";
+ for (_i = 0, _len = group_list.length; _i < _len; _i++) {
+ this_group = group_list[_i]
+ the_html += "<tr>";
+ the_html += "<td align='center' class='select_group'><input type='radio' name='selected_group' value='" + this_group.id + "'></td>";
+ the_html += "<td>" + (_i + 1).toString() + "</td>"
+ if ("name" in this_group) {
+ the_html += "<td>" + this_group.name + "</td>";
+ } else {
+ the_html += "<td>N/A</td>"
+ }
+ if ("admins" in this_group) {
+ the_html += "<td>" + this_group.admins.length + "</td>";
+ } else {
+ the_html += "<td>0</td>"
+ }
+ if ("members" in this_group) {
+ the_html += "<td>" + this_group.members.length + "</td>";
+ } else {
+ the_html += "<td>0</td>"
+ }
+ the_html += "</tr>"
+ }
+ the_html += "</tbody>";
+ the_html += "</table>";
+ } else {
+ the_html = "<span>No groups were found matching the entered criteria.</span>"
+ }
+
+ $('#group_results').html(the_html)
+ if (group_list.length > 0){
+ $('#groups_table').dataTable({
+ 'order': [[1, "asc" ]],
+ 'sDom': 'tr'
+ });
+ $('input[name=selected_group]:eq(0)').prop("checked", true)
+ $('#submit_group').css("display", "inline-block")
+ }
+ }
+ </script>
+{% endblock %}
diff --git a/wqflask/wqflask/templates/admin/set_group_privileges.html b/wqflask/wqflask/templates/admin/set_group_privileges.html
new file mode 100644
index 00000000..bc52788f
--- /dev/null
+++ b/wqflask/wqflask/templates/admin/set_group_privileges.html
@@ -0,0 +1,102 @@
+{% extends "base.html" %}
+{% block title %}Set Group Privileges{% 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="/static/new/packages/DataTables/extensions/buttons.dataTables.css">
+ <link rel="stylesheet" type="text/css" href="/static/new/css/show_trait.css" />
+{% endblock %}
+{% block content %}
+<!-- Start of body -->
+ <div class="container">
+ <h1>Group Privileges</h1>
+ <br>
+ <form id="set_group_privileges" action="/resources/add_group" method="POST">
+ <input type="hidden" name="resource_id" value="{{ resource_id }}">
+ <input type="hidden" name="group_id" value="{{ group_id }}">
+ <div style="min-width: 600px; max-width: 800px;">
+ <button type="submit" class="btn btn-primary" style="margin-bottom: 10px;">Add Group</button>
+ <hr>
+ <h2>Data and Metadata Privileges</h2>
+ <table id="data_privileges_table" class="table-hover table-striped cell-border" style="float: left;">
+ <thead>
+ <tr>
+ <th></th>
+ <th>No-Access</th>
+ <th>View</th>
+ <th>Edit</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>Data:</td>
+ {% if 'data' in default_privileges %}
+ <td align="center" style="padding: 0px;"><input type="radio" name="data_privilege" VALUE="no-access" {% if default_privileges.data == "no-access" %}checked{% endif %}></td>
+ <td align="center" style="padding: 0px;"><input type="radio" name="data_privilege" VALUE="view" {% if default_privileges.data == "view" %}checked{% endif %}></td>
+ <td align="center" style="padding: 0px;"><input type="radio" name="data_privilege" VALUE="edit" {% if default_privileges.data == "edit" %}checked{% endif %}></td>
+ {% else %}
+ <td align="center" style="padding: 0px;"><input type="radio" name="data_privilege" VALUE="no-access" checked></td>
+ <td align="center" style="padding: 0px;"><input type="radio" name="data_privilege" VALUE="view"></td>
+ <td align="center" style="padding: 0px;"><input type="radio" name="data_privilege" VALUE="edit"></td>
+ {% endif %}
+ </tr>
+ <tr>
+ <td>Metadata:</td>
+ {% if 'metadata' in default_privileges %}
+ <td align="center" style="padding: 0px;"><input type="radio" name="metadata_privilege" VALUE="no-access" {% if default_privileges.metadata == "no-access" %}checked{% endif %}></td>
+ <td align="center" style="padding: 0px;"><input type="radio" name="metadata_privilege" VALUE="view" {% if default_privileges.metadata == "view" %}checked{% endif %}></td>
+ <td align="center" style="padding: 0px;"><input type="radio" name="metadata_privilege" VALUE="edit" {% if default_privileges.metadata[-1] == "edit" %}checked{% endif %}></td>
+ {% else %}
+ <td align="center" style="padding: 0px;"><input type="radio" name="metadata_privilege" VALUE="no-access" checked></td>
+ <td align="center" style="padding: 0px;"><input type="radio" name="metadata_privilege" VALUE="view"></td>
+ <td align="center" style="padding: 0px;"><input type="radio" name="metadata_privilege" VALUE="edit"></td>
+ {% endif %}
+ </tr>
+ </tbody>
+ </table>
+ <hr>
+ <h2>Admin Privileges</h2>
+ <table id="admin_privileges_table" class="table-hover table-striped cell-border" style="float: left;">
+ <thead>
+ <tr>
+ <th></th>
+ <th>Not Admin</th>
+ <th>Edit Access</th>
+ <th>Edit Admins</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>Admin:</td>
+ {% if 'admin' in default_privileges %}
+ <td align="center" style="padding: 0px;"><input type="radio" name="admin_privilege" VALUE="not-admin" {% if default_privileges.admin == "not-admin" %}checked{% endif %}></td>
+ <td align="center" style="padding: 0px;"><input type="radio" name="admin_privilege" VALUE="edit-access" {% if default_privileges.admin == "edit-access" %}checked{% endif %}></td>
+ <td align="center" style="padding: 0px;"><input type="radio" name="admin_privilege" VALUE="edit-admins" {% if default_privileges.admin == "edit-admins" %}checked{% endif %}></td>
+ {% else %}
+ <td align="center" style="padding: 0px;"><input type="radio" name="admin_privilege" VALUE="not-admin" checked></td>
+ <td align="center" style="padding: 0px;"><input type="radio" name="admin_privilege" VALUE="edit-access"></td>
+ <td align="center" style="padding: 0px;"><input type="radio" name="admin_privilege" VALUE="edit-admins"></td>
+ {% endif %}
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </form>
+ </div>
+
+<!-- End of body -->
+
+{% endblock %}
+
+{% block js %}
+ <script language="javascript" type="text/javascript" src="{{ url_for('js', filename='DataTables/js/jquery.dataTables.min.js') }}"></script>
+ <script>
+ $('#data_privileges_table').dataTable({
+ 'sDom': 'tr',
+ 'bSort': false
+ });
+ $('#admin_privileges_table').dataTable({
+ 'sDom': 'tr',
+ 'bSort': false
+ });
+ </script>
+{% endblock %}
diff --git a/wqflask/wqflask/templates/admin/view_group.html b/wqflask/wqflask/templates/admin/view_group.html
new file mode 100644
index 00000000..9e3cce7b
--- /dev/null
+++ b/wqflask/wqflask/templates/admin/view_group.html
@@ -0,0 +1,247 @@
+{% extends "base.html" %}
+{% block title %}View and Edit Group{% 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="/static/new/packages/DataTables/extensions/buttons.dataTables.css">
+ <link rel="stylesheet" type="text/css" href="/static/new/css/show_trait.css" />
+{% endblock %}
+{% block content %}
+<!-- Start of body -->
+ <div class="container">
+ <div class="page-header">
+ <h1>
+ <span id="group_name">{{ group_info.name }}</span>
+ <input type="text" name="new_group_name" style="font-size: 20px; display: none; width: 500px;" class="form-control" placeholder="{{ group_info.name }}">
+ <button class="btn btn-default" style="display: inline;" id="change_group_name">Change Group Name</button>
+ </h1>
+ {% if user_is_admin == true %}
+ <div style="display: inline;">
+ <button type="button" id="remove_users" class="btn btn-danger" data-url="/groups/remove_users">Remove Selected Users from Group</button>
+ </div>
+ {% endif %}
+ </div>
+ <form id="group_form" action="/groups/view" method="POST">
+ <input type="hidden" name="group_id" value="{{ group_info.id }}">
+ <input type="hidden" name="selected_admin_ids" value="">
+ <input type="hidden" name="selected_member_ids" value="">
+ <div class="row">
+ <div id="groups_div" class="col-xs-6" style="margin-right: 30px; min-width: 600px; max-width: 800px;">
+ <div>
+ <div style="margin-top: 20px;"><h2>Admins</h2></div>
+ <hr>
+ <table id="group_admins" class="table-hover table-striped cell-border" style="float: left;">
+ <thead>
+ <tr>
+ <th></th>
+ <th>Index</th>
+ <th>Name</th>
+ <th>Email Address</th>
+ <th>Organization</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for admin in admins %}
+ <tr>
+ <td style="text-align: center; padding: 0px 10px 2px 10px;"><input type="checkbox" name="admin_id" value="{{ admin.user_id }}"></td>
+ <td align="right">{{ loop.index }}</td>
+ <td>{% if 'full_name' in admin %}{{ admin.full_name }}{% elif 'name' in admin %}{{ admin.name }}{% else %}N/A{% endif %}</td>
+ <td>{% if 'email_address' in admin %}{{ admin.email_address }}{% else %}N/A{% endif %}</td>
+ <td>{% if 'organization' in admin %}{{ admin.organization }}{% else %}N/A{% endif %}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+ {% if user_is_admin == true %}
+ <div style="margin-top: 20px;">
+ <span>E-mail of user to add to admins (multiple e-mails can be added separated by commas):</span>
+ <input type="text" size="60" name="admin_emails_to_add" placeholder="Enter E-mail(s)" value="">
+ </div>
+ <div style="margin-bottom: 30px; margin-top: 20px;">
+ <button type="button" id="add_admins" class="btn btn-primary" data-usertype="admin" data-url="/groups/add_admins">Add Admin(s)</button>
+ </div>
+ {% endif %}
+ </div>
+ <hr>
+ <div>
+ {% if members|length > 0 %}
+ <div><h2>Members</h2></div>
+ <hr>
+ <table id="group_members" class="table-hover table-striped cell-border" style="float: left;">
+ <thead>
+ <tr>
+ <th></th>
+ <th>Index</th>
+ <th>Name</th>
+ <th>Email Address</th>
+ <th>Organization</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for member in members %}
+ <tr>
+ <td style="text-align: center; padding: 0px 10px 2px 10px;"><input type="checkbox" name="member_id" value="{{ member.user_id }}"></td>
+ <td align="right">{{ loop.index }}</td>
+ <td>{% if 'full_name' in member %}{{ member.full_name }}{% elif 'name' in admin %}{{ admin.name }}{% else %}N/A{% endif %}</td>
+ <td>{% if 'email_address' in member %}{{ member.email_address }}{% else %}N/A{% endif %}</td>
+ <td>{% if 'organization' in member %}{{ member.organization }}{% else %}N/A{% endif %}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+ {% if user_is_admin == true %}
+ <div style="margin-top: 20px;">
+ <span>E-mail of user to add to members (multiple e-mails can be added separated by commas):</span>
+ <input type="text" size="60" name="member_emails_to_add" placeholder="Enter E-mail(s)" value="">
+ </div>
+ <div style="margin-bottom: 30px; margin-top: 20px;">
+ <button type="button" id="add_members" class="btn btn-primary" data-usertype="member" data-url="/groups/add_members">Add Member(s)</button>
+ </div>
+ {% endif %}
+ {% else %}
+ There are currently no members in this group.
+ {% if user_is_admin == true %}
+ <div style="margin-top: 20px;">
+ <span>E-mail of user to add to members (multiple e-mails can be added separated by commas):</span>
+ <input type="text" size="60" name="member_emails_to_add" placeholder="Enter E-mail(s)" value="">
+ </div>
+ <div style="margin-bottom: 30px; margin-top: 20px;">
+ <button type="button" id="add_members" class="btn btn-primary" data-usertype="member" data-url="/groups/add_members">Add Member(s)</button>
+ </div>
+ {% endif %}
+ {% endif %}
+ </div>
+ </div>
+ <div id="resources_div" class="col-xs-6" style="border-left: 1px solid #eee; margin-right: 30px; min-width: 600px; max-width: 800px;">
+ <div style="margin-top: 20px;"><h2>Resources</h2></div>
+ <hr>
+ {% if resources|length > 0 %}
+ <table id="resources" class="table-hover table-striped cell-border" style="float: left;">
+ <thead>
+ <tr>
+ <th>Index</th>
+ <th>Name</th>
+ <th>Data</th>
+ <th>Metadata</th>
+ <th>Admin</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for resource in resources %}
+ <tr>
+ <td align="right">{{ loop.index }}</td>
+ <td>{% if 'name' in resource %}<a href="/resources/manage?resource_id={{ resource.id }}">{{ resource.name }}</a>{% else %}N/A{% endif %}</td>
+ <td>{% if 'data' in resource %}{{ resource.data }}{% else %}N/A{% endif %}</td>
+ <td>{% if 'metadata' in resource %}{{ resource.metadata }}{% else %}N/A{% endif %}</td>
+ <td>{% if 'admin' in resource %}{{ resource.admin }}{% else %}N/A{% endif %}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+ {% else %}
+ There are currently no resources associated with this group.
+ {% endif %}
+ </div>
+ </div>
+ </form>
+ </div>
+
+<!-- End of body -->
+
+{% endblock %}
+
+{% block js %}
+ <script language="javascript" type="text/javascript" src="{{ url_for('js', filename='DataTables/js/jquery.dataTables.min.js') }}"></script>
+
+ <script type="text/javascript" charset="utf-8">
+ $(document).ready( function () {
+ $('#group_admins').dataTable({
+ 'order': [[1, "asc" ]],
+ 'columns': [
+ { "type": "natural", "width": "25px"},
+ { "type": "natural", "width": "30px" },
+ { "type": "natural", "width": "150px" },
+ { "type": "natural" },
+ { "type": "natural" }
+ ],
+ 'sDom': 'tr'
+ });
+ {% if members|length > 0 %}
+ $('#group_members').dataTable({
+ 'order': [[1, "asc" ]],
+ 'columns': [
+ { "type": "natural", "width": "25px"},
+ { "type": "natural", "width": "30px" },
+ { "type": "natural", "width": "150px" },
+ { "type": "natural" },
+ { "type": "natural" }
+ ],
+ 'sDom': 'tr'
+ });
+ {% endif %}
+ {% if resources|length > 0 %}
+ $('#resources').dataTable({
+ 'order': [[0, "asc" ]],
+ 'columns': [
+ { "type": "natural", "width": "30px" },
+ { "type": "natural", "width": "150px" },
+ { "type": "natural" },
+ { "type": "natural" },
+ { "type": "natural" }
+ ],
+ 'sDom': 'tr'
+ });
+ {% endif %}
+
+ $('#resources_div').css('height', $('#groups_div').css('height'))
+
+ submit_special = function(url) {
+ $("#group_form").attr("action", url);
+ return $("#group_form").submit();
+ };
+
+ $("#remove_users").on("click", function() {
+ url = $(this).data("url");
+ admins = [];
+ $("input[name=admin_id]:checked").each(function() {
+ admins.push($(this).val());
+ });
+ admins_string = admins.join(":")
+ $("input[name=selected_admin_ids]").val(admins_string)
+
+ members = [];
+ $("input[name=member_id]:checked").each(function() {
+ members.push($(this).val());
+ });
+ members_string = members.join(":")
+ $("input[name=selected_member_ids]").val(members_string)
+ return submit_special(url)
+ });
+
+ $("#add_admins, #add_members").on("click", function() {
+ url = $(this).data("url");
+ return submit_special(url)
+ });
+
+ $("#change_group_name").on("click", function() {
+ if ($('input[name=new_group_name]').css('display') == 'none') {
+ $('input[name=new_group_name]').css('display', 'inline');
+ $('#group_name').css('display', 'none');
+ } else {
+ new_name = $('input[name=new_group_name]').val()
+ $.ajax({
+ type: "POST",
+ url: "/groups/change_name",
+ data: {
+ group_id: $('input[name=group_id]').val(),
+ new_name: new_name
+ }
+ });
+ $('input[name=new_group_name]').css('display', 'none');
+ $('input[name=group_name]').val(new_name);
+ $('#group_name').text(new_name)
+ $('#group_name').css('display', 'inline');
+ }
+ });
+ });
+ </script>
+{% endblock %}
diff --git a/wqflask/wqflask/templates/base.html b/wqflask/wqflask/templates/base.html
index 07c1b48e..262d9ee5 100644
--- a/wqflask/wqflask/templates/base.html
+++ b/wqflask/wqflask/templates/base.html
@@ -94,6 +94,11 @@
<a id="login_in" href="/n/login">Sign in</a>
{% endif %}
</li>
+ {% if g.user_session.logged_in %}
+ <li class="">
+ <a id="manage_groups" title="Manage Groups" href="/groups/manage">Manage Groups</a>
+ </li>
+ {% endif %}
{% endif %}
<!--
<li style="margin-left: 20px;">
diff --git a/wqflask/wqflask/templates/collections/list.html b/wqflask/wqflask/templates/collections/list.html
index 3829b950..94e22c4d 100644
--- a/wqflask/wqflask/templates/collections/list.html
+++ b/wqflask/wqflask/templates/collections/list.html
@@ -1,7 +1,7 @@
{% extends "base.html" %}
{% block title %}Your Collections{% endblock %}
{% block css %}
- <link rel="stylesheet" type="text/css" href="/static/new/packages/DataTables/css/jquery.dataTables.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/packages/DataTables/extensions/buttons.bootstrap.css" />
<link rel="stylesheet" type="text/css" href="/static/new/css/show_trait.css" />
{% endblock %}
@@ -42,6 +42,7 @@
<tbody>
{% for uc in collections %}
+ {% if uc.num_members > 0 %}
<tr class="collection_line">
<td align="center" style="padding: 0px;"><INPUT TYPE="checkbox" NAME="collection" class="checkbox trait_checkbox" VALUE="{{ uc.id }}"></td>
<td align="right">{{ loop.index }}
@@ -50,6 +51,7 @@
<td>{{ uc.changed_timestamp }}</td>
<td align="right">{{ uc.num_members }}</td>
</tr>
+ {% endif %}
{% endfor %}
</tbody>
</table>
@@ -66,7 +68,7 @@
{% block js %}
<script type="text/javascript" src="/static/packages/smart-time-ago/lib/timeago.js"></script>
<script type="text/javascript" src="/static/new/javascript/search_results.js"></script>
- <script language="javascript" type="text/javascript" src="/static/new/packages/DataTables/js/jquery.dataTables.min.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="/static/new/js_external/jszip.min.js"></script>
<script language="javascript" type="text/javascript" src="/static/new/packages/DataTables/js/dataTables.naturalSort.js"></script>
<script language="javascript" type="text/javascript" src="/static/new/packages/DataTables/extensions/dataTables.buttons.min.js"></script>
diff --git a/wqflask/wqflask/templates/collections/view.html b/wqflask/wqflask/templates/collections/view.html
index 6639f46a..b56a89da 100644
--- a/wqflask/wqflask/templates/collections/view.html
+++ b/wqflask/wqflask/templates/collections/view.html
@@ -1,14 +1,14 @@
{% extends "base.html" %}
{% block title %}View Collection{% endblock %}
{% block css %}
- <link rel="stylesheet" type="text/css" href="/static/new/packages/DataTables/css/jquery.dataTables.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/packages/DataTables/extensions/buttons.dataTables.css">
<link rel="stylesheet" type="text/css" href="/static/new/css/show_trait.css" />
{% endblock %}
{% block content %}
<!-- Start of body -->
- <div class="container" style="min-width: 1250px;">
+ <div class="container" style="min-width: 2050px;">
<h1>
<span id="collection_name">{{ uc.name }}</span>
<input type="text" name="new_collection_name" style="font-size: 20px; display: none; width: 500px;" class="form-control" placeholder="{{ uc.name }}">
@@ -80,7 +80,7 @@
<input type="text" id="searchbox" class="form-control" style="width: 200px; display: inline; padding-bottom: 9px;" placeholder="Search Table For ...">
<input type="text" id="select_top" class="form-control" style="width: 200px; display: inline; padding-bottom: 9px;" placeholder="Select Top ...">
<button class="btn btn-default" id="deselect_all" type="button"><span class="glyphicon glyphicon-remove"></span> Deselect</button>
- <button class="btn btn-danger" id="remove" disabled="disabled" type="button"><i class="icon-minus-sign"></i> Delete Rows</button>
+ <button id="remove" class="btn btn-danger" data-url="/collections/remove" disabled="disabled" type="button"><i class="icon-minus-sign"></i> Delete Rows</button>
<button id="delete" class="btn btn-danger submit_special" data-url="/collections/delete" title="Delete this collection" > Delete Collection</button>
</form>
<div style="margin-top: 10px; margin-bottom: 5px;">
@@ -122,9 +122,9 @@
</a>
</TD>
{% if this_trait.symbol %}
- <TD data-export="{{ this_trait.symbol }}">{{ this_trait.symbol }}</TD>
+ <TD title="{{ this_trait.symbol }}" data-export="{{ this_trait.symbol }}">{% if this_trait.symbol|length > 20 %}{{ this_trait.symbol[:20] }}...{% else %}{{ this_trait.symbol }}{% endif %}</TD>
{% elif this_trait.abbreviation %}
- <TD data-export="{{ this_trait.abbreviation }}">{{ this_trait.abbreviation }}</TD>
+ <TD title="{{ this_trait.abbreviation }}" data-export="{{ this_trait.abbreviation }}">{% if this_trait.abbreviation|length > 20 %}{{ this_trait.abbreviation[:20] }}...{% else %}{{ this_trait.abbreviation }}{% endif %}</TD>
{% else %}
<TD data-export="N/A">N/A</TD>
{% endif %}
@@ -166,7 +166,7 @@
<script language="javascript" type="text/javascript" src="/static/new/js_external/jszip.min.js"></script>
<script language="javascript" type="text/javascript" src="/static/new/js_external/md5.min.js"></script>
<script type="text/javascript" src="/static/new/javascript/search_results.js"></script>
- <script language="javascript" type="text/javascript" src="/static/new/packages/DataTables/js/jquery.dataTables.min.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="/static/new/packages/DataTables/js/dataTables.naturalSort.js"></script>
<script language="javascript" type="text/javascript" src="/static/new/packages/DataTables/extensions/dataTables.colResize.js"></script>
<script language="javascript" type="text/javascript" src="/static/new/packages/DataTables/extensions/dataTables.colReorder.js"></script>
@@ -190,7 +190,7 @@
{ "type": "natural", "width": 50 },
{ "type": "natural" },
{ "type": "natural", "width": 120 },
- { "type": "natural", "width": 120 },
+ { "type": "natural" },
{ "type": "natural" },
{ "type": "natural", "width": 130 },
{ "type": "natural", "width": 35 },
@@ -230,6 +230,16 @@
return submit_special(url)
});
+ $("#remove").on("click", function() {
+ url = $(this).data("url")
+ traits = $("#trait_table input:checked").map(function() {
+ return $(this).val();
+ }).get();
+ $("#trait_list").val(traits)
+
+ return submit_special(url)
+ });
+
$("#change_collection_name").on("click", function() {
if ($('input[name=new_collection_name]').css('display') == 'none') {
$('input[name=new_collection_name]').css('display', 'inline');
diff --git a/wqflask/wqflask/templates/corr_scatterplot.html b/wqflask/wqflask/templates/corr_scatterplot.html
index ffc8244d..5877e367 100644
--- a/wqflask/wqflask/templates/corr_scatterplot.html
+++ b/wqflask/wqflask/templates/corr_scatterplot.html
@@ -1,7 +1,7 @@
{% extends "base.html" %}
{% block css %}
- <link rel="stylesheet" type="text/css" href="/static/new/packages/DataTables/css/jquery.dataTables.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/panelutil.css" />
<link rel="stylesheet" type="text/css" href="/static/new/css/d3-tip.min.css" />
<link rel="stylesheet" type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/nvd3/1.8.5/nv.d3.min.css">
diff --git a/wqflask/wqflask/templates/correlation_matrix.html b/wqflask/wqflask/templates/correlation_matrix.html
index 9af52021..cb9fb815 100644
--- a/wqflask/wqflask/templates/correlation_matrix.html
+++ b/wqflask/wqflask/templates/correlation_matrix.html
@@ -1,6 +1,6 @@
{% extends "base.html" %}
{% block css %}
- <link rel="stylesheet" type="text/css" href="/static/new/packages/DataTables/css/jquery.dataTables.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/corr_matrix.css" />
<link rel="stylesheet" type="text/css" href="/static/new/css/show_trait.css" />
<link rel="stylesheet" type="text/css" href="/static/new/css/panelutil.css" />
@@ -135,7 +135,7 @@
<script language="javascript" type="text/javascript" src="/static/new/js_external/chroma.js"></script>
<script language="javascript" type="text/javascript" src="/static/new/javascript/loadings_plot.js"></script>
<script type="text/javascript" src="/static/new/javascript/create_corr_matrix.js"></script>
- <script language="javascript" type="text/javascript" src="/static/new/packages/DataTables/js/jquery.dataTables.min.js"></script>
+ <script language="javascript" type="text/javascript" src="{{ url_for('js', filename='DataTables/js/jquery.dataTables.min.js') }}"></script>
<script type="text/javascript" src="/static/new/javascript/search_results.js"></script>
<script>
diff --git a/wqflask/wqflask/templates/correlation_page.html b/wqflask/wqflask/templates/correlation_page.html
index 03b03aa7..3d750bea 100644
--- a/wqflask/wqflask/templates/correlation_page.html
+++ b/wqflask/wqflask/templates/correlation_page.html
@@ -1,8 +1,10 @@
{% extends "base.html" %}
+{% block title %}Correlation Results{% endblock %}
{% block css %}
- <link rel="stylesheet" type="text/css" href="/static/new/packages/DataTables/css/jquery.dataTables.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/packages/DataTables/extensions/buttons.bootstrap.css" />
<link rel="stylesheet" type="text/css" href="/static/new/packages/DataTables/extensions/buttons.dataTables.css">
+ <link rel="stylesheet" type="text/css" href="/static/new/css/show_trait.css" />
{% endblock %}
{% block content %}
<div class="container" style="min-width: 1250px;">
@@ -82,6 +84,7 @@
<button id="redraw" class="btn btn-default" type="button">Reset Columns</button>
</form>
<br />
+ {% if target_dataset.type != "Publish" %}
<br />
<button id="more_options" class="btn btn-primary">More Options...</button>
<br />
@@ -107,12 +110,13 @@
<br />
<br />
</div>
+ {% endif %}
</div>
<div style="margin-bottom: 5px;">
<b>Show/Hide Columns:</b>
</div>
- <div style="width: {% if target_dataset.type == "ProbeSet" %}1600px{% elif target_dataset.type == "Publish" %}1400px{% else %}800px{% endif %};">
- <table id="trait_table" class="display dataTable nowrap" style="font-size: 12px; float: left;">
+ <div style="width: 100%; min-width: {% if target_dataset.type == "ProbeSet" %}1700px{% elif target_dataset.type == "Publish" %}1600px{% else %}600px{% endif %};">
+ <table id="trait_table" class="table-hover table-striped cell-border" style="float: left;">
<thead>
<tr>
<th></th>
@@ -126,7 +130,7 @@
{% for trait in correlation_results %}
<tr>
<td><INPUT TYPE="checkbox" NAME="searchResult" class="checkbox trait_checkbox" style="padding-right: 0px;" VALUE="{{ data_hmac('{}:{}'.format(trait.name, trait.dataset.name)) }}"></td>
- <td data-export="{{ loop.index }}" style="padding-left: 8px; padding-right: 0px; padding-top: 4px; align: center;">{{ loop.index }}</td>
+ <td data-export="{{ loop.index }}" style="padding-left: 8px; padding-right: 0px; padding-top: 4px; align: right;">{{ loop.index }}</td>
<td data-export="{{ trait.name }}">
<a href="{{ url_for('show_trait_page',
trait_id = trait.name,
@@ -140,7 +144,7 @@
<td data-export="{{ trait.description_display }}">{{ trait.description_display }}</TD>
<td data-export="{{ trait.location_repr }}" style="white-space: nowrap;">{{ trait.location_repr }}</td>
<td data-export="{{ '%0.3f' % trait.mean|float }}" align="right">{{ '%0.3f' % trait.mean|float }}</td>
- <td data-export="{{ '%0.3f'|format(trait.sample_r) }} align="right"><a target="_blank" href="corr_scatter_plot?dataset_1={{dataset.name}}&dataset_2={{trait.dataset.name}}&trait_1={{this_trait.name}}&trait_2={{trait.name}}">{{ '%0.3f'|format(trait.sample_r) }}</a></td>
+ <td data-export="{{ '%0.3f'|format(trait.sample_r) }}"" align="right"><a target="_blank" href="corr_scatter_plot?dataset_1={{dataset.name}}&dataset_2={{trait.dataset.name}}&trait_1={{this_trait.name}}&trait_2={{trait.name}}">{{ '%0.3f'|format(trait.sample_r) }}</a></td>
<td data-export="{{ trait.num_overlap }}" align="right">{{ trait.num_overlap }}</td>
<td data-export="{{ '%0.3e'|format(trait.sample_p) }}" align="right">{{ '%0.3e'|format(trait.sample_p) }}</td>
{% if trait.lit_corr == "" or trait.lit_corr == 0.000 %}
@@ -159,8 +163,17 @@
<td data-export="{{ trait.LRS_location_repr }}" align="right">{{ trait.LRS_location_repr }}</td>
<td data-export={% if trait.additive != "" %}"{{ '%0.3f' % trait.additive|float }}"{% else %}"N/A"{% endif %} align="right">{% if trait.additive != "" %}{{ '%0.3f' % trait.additive|float }}{% else %}N/A{% endif %}</td>
{% elif target_dataset.type == "Publish" %}
- <td data-export="{{ trait.description_display }}">{{ trait.description_display }}</td>
- <td data-export="{{ trait.authors }}">{{ trait.authors }}</td>
+ {% if trait.abbreviation %}
+ <TD title="{{ trait.abbreviation }}" data-export="{{ trait.abbreviation }}">{% if trait.abbreviation|length > 20 %}{{ trait.abbreviation[:20] }}...{% else %}{{ trait.abbreviation }}{% endif %}</TD>
+ {% else %}
+ <TD data-export="N/A">N/A</TD>
+ {% endif %}
+ <td data-export="{{ trait.description_display }}">{% if trait.description_display|length > 70 %}{{ trait.description_display[:70] }}...{% else %}{{ trait.description_display }}{% endif %}</td>
+ {% if trait.authors %}
+ <td data-export="{{ trait.authors }}">{% if trait.authors.split(',') > 6 %}{{ trait.authors.split(',')[:6]|join(', ') }}, et al.{% else %}{{ trait.authors }}{% endif %}</td>
+ {% else %}
+ <TD data-export="N/A">N/A</TD>
+ {% endif %}
<td data-export="{{ trait.pubmed_text }}">
<a href="{{ trait.pubmed_link }}">
{{ trait.pubmed_text }}
@@ -187,13 +200,13 @@
{% endblock %}
{% block js %}
- <script type="text/javascript" src="/static/new/js_external/md5.min.js"></script>
+ <!--<script type="text/javascript" src="/static/new/js_external/md5.min.js"></script>-->
<script type="text/javascript" src="/static/new/javascript/search_results.js"></script>
<script language="javascript" type="text/javascript" src="/static/new/js_external/jszip.min.js"></script>
<script language="javascript" type="text/javascript" src="/static/packages/underscore/underscore-min.js"></script>
- <script language="javascript" type="text/javascript" src="https://cdn.datatables.net/1.10.13/js/jquery.dataTables.min.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="/static/new/packages/DataTables/extensions/dataTables.buttons.min.js"></script>
<script language="javascript" type="text/javascript" src="/static/new/packages/DataTables/extensions/buttons.colVis.min.js"></script>
<script language="javascript" type="text/javascript" src="/static/new/packages/DataTables/js/dataTables.naturalSort.js"></script>
@@ -203,7 +216,6 @@
</script>
<script type="text/javascript" charset="utf-8">
-
$.fn.dataTable.ext.order['dom-innertext'] = function (settings, col) {
return this.api().column(col, { order: 'index' }).nodes().map(function (td, i) {
return Math.abs(parseFloat($('a', td).text()));
@@ -234,8 +246,7 @@
return ((x < y) ? 1 : ((x > y) ? -1 : 0));
};
- //$.fn.dataTableExt.afnFiltering.push(
-
+ {% if target_dataset.type != "Publish" %}
$.fn.dataTable.ext.search.push( function( settings, data, dataIndex ) {
var r_column = {{ filter_cols[0] }};
var r_greater = parseFloat($('input[name=r_greater_select]').val())
@@ -275,6 +286,7 @@
}
return true
});
+ {% endif %}
$(document).ready( function () {
@@ -298,7 +310,6 @@
$(button).prop("disabled", false);
}
}
- //});
if ($(this).is(":checked")) {
if (!$(this).closest('tr').hasClass('selected')) {
$(this).closest('tr').addClass('selected')
@@ -377,8 +388,15 @@
{% elif target_dataset.type == "Publish" %}
table_conf = {
- "paging": false,
- buttons: [
+ "drawCallback": function( settings ) {
+ $('#trait_table tr').click(function(event) {
+ if (event.target.type !== 'checkbox') {
+ $(':checkbox', this).trigger('click');
+ }
+ });
+ $('.trait_checkbox:checkbox').on("change", change_buttons);
+ },
+ "buttons": [
{
extend: 'columnsToggle',
columns: function( idx, data, node ) {
@@ -398,6 +416,7 @@
{ "type": "natural" },
{ "type": "natural" },
{ "type": "natural" },
+ { "type": "natural" },
{ "type": "natural", "width": "20%" },
{ "type": "natural", "width": "12%" },
{ "orderDataType": "dom-innertext" },
@@ -409,21 +428,17 @@
{ "type": "natural" }
],
"createdRow": function ( row, data, index ) {
- $('td', row).eq(3).attr('title', $('td', row).eq(3).text());
- if ($('td', row).eq(3).text().length > 50) {
- $('td', row).eq(3).text($('td', row).eq(3).text().substring(0, 50));
- $('td', row).eq(3).text($('td', row).eq(3).text() + '...')
- }
- $('td', row).eq(4).attr('title', $('td', row).eq(4).text());
- if ($('td', row).eq(4).text().length > 40) {
- $('td', row).eq(4).text($('td', row).eq(4).text().substring(0, 40));
- $('td', row).eq(4).text($('td', row).eq(4).text() + '...')
- }
+ $('td', row).eq(4).text(decodeURIComponent(escape($('td', row).eq(4).text())));
+ $('td', row).eq(5).text(decodeURIComponent(escape($('td', row).eq(5).text())));
},
- "order": [[8, "asc" ]],
+ "order": [[9, "asc" ]],
"sDom": "Btir",
+ "iDisplayLength": -1,
"autoWidth": false,
- "bDeferRender": true
+ "deferRender": true,
+ "bSortClasses": false,
+ "paging": false,
+ "orderClasses": true,
}
{% elif target_dataset.type == "Geno" %}
table_conf = {
@@ -462,7 +477,7 @@
}
{% endif %}
- var the_table = $('#trait_table').DataTable(table_conf);
+ the_table = $('#trait_table').DataTable(table_conf);
console.timeEnd("Creating table");
diff --git a/wqflask/wqflask/templates/email/verification.txt b/wqflask/wqflask/templates/email/verification.txt
deleted file mode 100644
index 76149a3a..00000000
--- a/wqflask/wqflask/templates/email/verification.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-Thank you for signing up for GeneNetwork.
-
-We need to verify your email address.
-
-To do that please click the following link, or cut and paste it into your browser window:
-
-{{ url_for_hmac("verify_email", code = verification_code, _external=True )}}
diff --git a/wqflask/wqflask/templates/gsearch_gene.html b/wqflask/wqflask/templates/gsearch_gene.html
index 556d46d3..8c261eec 100644
--- a/wqflask/wqflask/templates/gsearch_gene.html
+++ b/wqflask/wqflask/templates/gsearch_gene.html
@@ -1,7 +1,7 @@
{% extends "base.html" %}
{% block title %}Search Results{% endblock %}
{% block css %}
- <link rel="stylesheet" type="text/css" href="/static/new/packages/DataTables/css/jquery.dataTables.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/show_trait.css" />
{% endblock %}
{% block content %}
@@ -48,7 +48,7 @@
{% block js %}
<script language="javascript" type="text/javascript" src="/static/new/js_external/md5.min.js"></script>
<script language="javascript" type="text/javascript" src="/static/new/javascript/search_results.js"></script>
- <script language="javascript" type="text/javascript" src="https://cdn.datatables.net/1.10.12/js/jquery.dataTables.min.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="/static/new/js_external/jszip.min.js"></script>
<script language="javascript" type="text/javascript" src="/static/new/packages/DataTables/js/dataTables.naturalSort.js"></script>
<script language="javascript" type="text/javascript" src="/static/new/packages/DataTables/extensions/dataTables.colReorder.js"></script>
diff --git a/wqflask/wqflask/templates/gsearch_pheno.html b/wqflask/wqflask/templates/gsearch_pheno.html
index f6ffff47..04b45659 100644
--- a/wqflask/wqflask/templates/gsearch_pheno.html
+++ b/wqflask/wqflask/templates/gsearch_pheno.html
@@ -1,7 +1,7 @@
{% extends "base.html" %}
{% block title %}Search Results{% endblock %}
{% block css %}
- <link rel="stylesheet" type="text/css" href="/static/new/packages/DataTables/css/jquery.dataTables.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/show_trait.css" />
{% endblock %}
{% block content %}
@@ -31,7 +31,7 @@
</form>
<br />
<br />
- <div style="width: 100%;">
+ <div style="min-width: 2000px; width: 100%;">
<table id="trait_table" class="table-hover table-striped cell-border" style="float: left;">
<tbody>
<td colspan="100%" align="center"><br><b><font size="15">Loading...</font></b><br></td>
@@ -48,7 +48,7 @@
{% block js %}
<script language="javascript" type="text/javascript" src="/static/new/js_external/md5.min.js"></script>
<script language="javascript" type="text/javascript" src="/static/new/javascript/search_results.js"></script>
- <script language="javascript" type="text/javascript" src="https://cdn.datatables.net/1.10.12/js/jquery.dataTables.min.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="/static/new/js_external/jszip.min.js"></script>
<script language="javascript" type="text/javascript" src="/static/new/packages/DataTables/js/dataTables.naturalSort.js"></script>
<script language="javascript" type="text/javascript" src="/static/new/packages/DataTables/extensions/dataTables.colReorder.js"></script>
diff --git a/wqflask/wqflask/templates/loading.html b/wqflask/wqflask/templates/loading.html
index 49bcbff7..15ab4080 100644
--- a/wqflask/wqflask/templates/loading.html
+++ b/wqflask/wqflask/templates/loading.html
@@ -11,7 +11,7 @@
{% if start_vars.tool_used == "Mapping" %}
<h1>Computing the Maps</h1>
<br>
- <i>n</i> = {{ start_vars.num_vals }}
+ <i>n</i> = {{ start_vars.n_samples }}
<br>
Method = {% if start_vars.method == "gemma" %}GEMMA{% else %}{{ start_vars.method }}{% endif %}
<br>
diff --git a/wqflask/wqflask/templates/mapping_results.html b/wqflask/wqflask/templates/mapping_results.html
index 7e05be18..132d5249 100644
--- a/wqflask/wqflask/templates/mapping_results.html
+++ b/wqflask/wqflask/templates/mapping_results.html
@@ -1,7 +1,7 @@
{% extends "base.html" %}
{% block title %}Mapping Results{% endblock %}
{% block css %}
- <link rel="stylesheet" type="text/css" href="/static/new/packages/DataTables/css/jquery.dataTables.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/packages/DT_bootstrap/DT_bootstrap.css" />
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css" integrity="sha384-mzrmE5qonljUremFsqc01SB46JvROS7bZs3IO2EmfFsd15uHvIt+Y8vEf7N7fWAU" crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="/static/packages/purescript_genome_browser/css/purescript-genetics-browser.css" />
@@ -35,13 +35,14 @@
{% for sample in samples %}
<input type="hidden" name="value:{{ sample }}" value="{{ vals[loop.index - 1] }}">
{% endfor %}
- <input type="hidden" name="num_vals" value="{{ n_samples }}">
+ <input type="hidden" name="n_samples" value="{{ n_samples }}">
<input type="hidden" name="maf" value="{{ maf }}">
<input type="hidden" name="use_loco" value="{{ use_loco }}">
<input type="hidden" name="selected_chr" value="{{ selectedChr }}">
<input type="hidden" name="manhattan_plot" value="{{ manhattan_plot }}">
<input type="hidden" name="num_perm" value="{{ nperm }}">
- <input type="hidden" name="perm_results" value="">
+ <input type="hidden" name="perm_info" value="">
+ <input type="hidden" name="perm_strata" value="{{ perm_strata }}">
<input type="hidden" name="num_bootstrap" value="{{ nboot }}">
<input type="hidden" name="do_control" value="{{ doControl }}">
<input type="hidden" name="control_marker" value="{{ controlLocus }}">
@@ -274,7 +275,11 @@
{% endif %}
<td align="right">{{marker.chr}}</td>
{% if plotScale != "physic" %}
+ {% if 'cM' in marker %}
+ <td align="right">{{ '%0.3f' | format(marker.cM|float) }}</td>
+ {% else %}
<td align="right">{{ '%0.3f' | format(marker.Mb|float) }}</td>
+ {% endif %}
{% else %}
<td align="right">{{ '%0.6f' | format(marker.Mb|float) }}</td>
{% endif %}
@@ -333,7 +338,7 @@
<script type="text/javascript" src="/static/new/js_external/d3-tip.min.js"></script>
<script type="text/javascript" src="/static/new/js_external/plotly-latest.min.js"></script>
- <script language="javascript" type="text/javascript" src="https://cdn.datatables.net/1.10.12/js/jquery.dataTables.min.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="https://cdn.datatables.net/buttons/1.0.0/js/dataTables.buttons.min.js"></script>
<script language="javascript" type="text/javascript" src="/static/new/packages/DataTables/js/dataTables.scientific.js"></script>
<script language="javascript" type="text/javascript" src="/static/new/packages/DataTables/js/dataTables.naturalSort.js"></script>
@@ -430,7 +435,7 @@
'score_type', 'suggestive', 'significant', 'num_perm', 'permCheck', 'perm_output', 'perm_strata', 'categorical_vars', 'num_bootstrap', 'bootCheck', 'bootstrap_results',
'LRSCheck', 'covariates', 'maf', 'use_loco', 'manhattan_plot', 'control_marker', 'control_marker_db', 'do_control', 'genofile',
'pair_scan', 'startMb', 'endMb', 'graphWidth', 'lrsMax', 'additiveCheck', 'showSNP', 'showGenes', 'viewLegend', 'haplotypeAnalystCheck',
- 'mapmethod_rqtl_geno', 'mapmodel_rqtl_geno', 'temp_trait', 'group', 'species', 'reaper_version', 'primary_samples', 'num_vals']
+ 'mapmethod_rqtl_geno', 'mapmodel_rqtl_geno', 'temp_trait', 'group', 'species', 'reaper_version', 'primary_samples', 'n_samples']
$('input[name=wanted_inputs]').val(mapping_input_list.join(","));
@@ -460,13 +465,27 @@
{% if mapping_method != "gemma" and mapping_method != "plink" %}
$('#download_perm').click(function(){
- var num_perm, perm_data;
- num_perm = js_data.num_perm
- perm_data = js_data.perm_results
- json_perm_data = JSON.stringify(perm_data);
- $('input[name=perm_results]').val(json_perm_data);
- $('#marker_regression_form').attr('action', '/export_perm_data');
- return $('#marker_regression_form').submit();
+ perm_info_dict = {
+ perm_data: js_data.perm_results,
+ num_perm: "{{ nperm }}",
+ trait_name: "{{ this_trait.display_name }}",
+ trait_description: "{{ this_trait.description_display }}",
+ cofactors: "{{ covariates }}",
+ n_samples: {{ n_samples }},
+ n_genotypes: {{ qtl_results|length }},
+ {% if genofile_string is defined %}
+ genofile: "{{ genofile_string }}",
+ {% else %}
+ genofile: "",
+ {% endif %}
+ units_linkage: "{{ LRS_LOD }}",
+ strat_cofactors: js_data.categorical_vars
+ }
+ json_perm_data = JSON.stringify(perm_info_dict);
+
+ $('input[name=perm_info]').val(json_perm_data);
+ $('#marker_regression_form').attr('action', '/export_perm_data');
+ return $('#marker_regression_form').submit();
});
modebar_options = {
diff --git a/wqflask/wqflask/templates/new_security/not_authenticated.html b/wqflask/wqflask/templates/new_security/not_authenticated.html
new file mode 100644
index 00000000..7d0d3060
--- /dev/null
+++ b/wqflask/wqflask/templates/new_security/not_authenticated.html
@@ -0,0 +1,11 @@
+{% extends "base.html" %}
+{% block title %}Authentication Needed{% endblock %}
+{% block content %}
+ <div class="container">
+ <div class="page-header">
+ <h3>You lack the permissions to view this data.</h3>
+ </div>
+ <p>Please contact the data's owner or GN administrators if you believe you should have access to this data.</p>
+ </div>
+
+{% endblock %} \ No newline at end of file
diff --git a/wqflask/wqflask/templates/pair_scan_results.html b/wqflask/wqflask/templates/pair_scan_results.html
index 1ccb2b27..b13b393f 100644
--- a/wqflask/wqflask/templates/pair_scan_results.html
+++ b/wqflask/wqflask/templates/pair_scan_results.html
@@ -1,7 +1,7 @@
{% extends "base.html" %}
{% block title %}Pair Scan{% endblock %}
{% block css %}
- <link rel="stylesheet" type="text/css" href="/static/new/packages/DataTables/css/jquery.dataTables.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/packages/DT_bootstrap/DT_bootstrap.css" />
<link rel="stylesheet" type="text/css" href="/static/packages/TableTools/media/css/TableTools.css" />
<link rel="stylesheet" type="text/css" href="/static/new/css/d3-tip.min.css" />
@@ -65,7 +65,7 @@
<script language="javascript" type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
<script language="javascript" type="text/javascript" src="/static/new/js_external/d3-tip.min.js"></script>
<script language="javascript" type="text/javascript" src="/static/new/packages/DataTables/js/jquery.js"></script>
- <script language="javascript" type="text/javascript" src="/static/new/packages/DataTables/js/jquery.dataTables.min.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="/static/new/packages/DataTables/js/dataTables.scientific.js"></script>
<script language="javascript" type="text/javascript" src="/static/packages/DT_bootstrap/DT_bootstrap.js"></script>
<script language="javascript" type="text/javascript" src="/static/packages/TableTools/media/js/TableTools.min.js"></script>
diff --git a/wqflask/wqflask/templates/search_error.html b/wqflask/wqflask/templates/search_error.html
index 7399b377..df8d9dff 100644
--- a/wqflask/wqflask/templates/search_error.html
+++ b/wqflask/wqflask/templates/search_error.html
@@ -1,7 +1,7 @@
{% extends "base.html" %}
{% block title %}Search Results{% endblock %}
{% block css %}
- <link rel="stylesheet" type="text/css" href="/static/new/packages/DataTables/css/jquery.dataTables.css" />
+ <link rel="stylesheet" type="text/css" href="{{ url_for('css', filename='DataTables/css/jquery.dataTables.css') }}" />
{% endblock %}
{% block content %}
<!-- Start of body -->
diff --git a/wqflask/wqflask/templates/search_result_page.html b/wqflask/wqflask/templates/search_result_page.html
index 0f5c39b0..3dfae3dd 100644
--- a/wqflask/wqflask/templates/search_result_page.html
+++ b/wqflask/wqflask/templates/search_result_page.html
@@ -1,14 +1,14 @@
{% extends "base.html" %}
{% block title %}Search Results{% endblock %}
{% block css %}
- <link rel="stylesheet" type="text/css" href="/static/new/packages/DataTables/css/jquery.dataTables.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/packages/DataTables/extensions/scroller.dataTables.min.css">
<link rel="stylesheet" type="text/css" href="/static/new/packages/DataTables/extensions/buttons.dataTables.css">
<link rel="stylesheet" type="text/css" href="/static/new/css/show_trait.css" />
{% endblock %}
{% block content %}
<!-- Start of body -->
- <div style="padding-left: 10px; {% if dataset.type == 'Publish' %}min-width: 1000px;{% else %}min-width: 1200px;{% endif %}">
+ <div style="padding-left: 10px;>
<input type="hidden" name="uc_id" id="uc_id" value="{{ uc_id }}">
<div style="padding-top: 10px; padding-bottom: 10px; font-size: 16px;">
@@ -59,7 +59,7 @@
</div>
- <div>
+ <div style="min-width: 950px;">
<form id="trait_submission_form" target="_blank" action="/corr_matrix" method="post">
<input type="hidden" name="tool_used" value="" />
<input type="hidden" name="form_url" value="" />
@@ -111,29 +111,34 @@
<div>
<br />
<form id="export_form" method="POST" action="/export_traits_csv" style="display: inline;">
- <input type="hidden" name="headers" id="headers" value="{% for field in header_fields %}{{ field }},{% endfor %}">
- <input type="hidden" name="search_string" id="search_string" value="{{ original_search_string }}">
- <input type="hidden" name="database_name" id="database_name" value="{{ dataset.fullname }}">
- <input type="hidden" name="file_name" id="file_name" value="search_results">
- <input type="hidden" name="filter_term" id="filter_term" value="None">
- {% if dataset.accession_id is defined %}
- <input type="hidden" name="accession_id" id="accession_id" value="{{ dataset.accession_id }}">
- {% endif %}
- <input type="hidden" name="export_data" id="export_data" value="">
- <button class="btn btn-default" id="select_all" type="button"><span class="glyphicon glyphicon-ok"></span> Select</button>
- <button class="btn btn-default" id="add" type="button" disabled><span class="glyphicon glyphicon-plus-sign"></span> Add</button>
- <button class="btn btn-default" id="export_traits">Download CSV</button>
- <input type="text" id="searchbox" class="form-control" style="width: 200px; display: inline;" placeholder="Search This Table For ...">
- <input type="text" id="select_top" class="form-control" style="width: 200px; display: inline;" placeholder="Select Top ...">
- <button class="btn btn-default" id="deselect_all" type="button"><span class="glyphicon glyphicon-remove"></span> Deselect</button>
+ <div style="min-width: 950px;">
+ <input type="hidden" name="headers" id="headers" value="{% for field in header_fields %}{{ field }},{% endfor %}">
+ <input type="hidden" name="search_string" id="search_string" value="{{ original_search_string }}">
+ <input type="hidden" name="database_name" id="database_name" value="{{ dataset.fullname }}">
+ <input type="hidden" name="file_name" id="file_name" value="search_results">
+ <input type="hidden" name="filter_term" id="filter_term" value="None">
+ {% if dataset.accession_id is defined %}
+ <input type="hidden" name="accession_id" id="accession_id" value="{{ dataset.accession_id }}">
+ {% endif %}
+ <input type="hidden" name="export_data" id="export_data" value="">
+ <button class="btn btn-default" id="select_all" type="button"><span class="glyphicon glyphicon-ok"></span> Select</button>
+ <button class="btn btn-default" id="add" type="button" disabled><span class="glyphicon glyphicon-plus-sign"></span> Add</button>
+ <button class="btn btn-default" id="export_traits">Download CSV</button>
+ <input type="text" id="searchbox" class="form-control" style="width: 200px; display: inline;" placeholder="Search This Table For ...">
+ <input type="text" id="select_top" class="form-control" style="width: 200px; display: inline;" placeholder="Select Top ...">
+ <button class="btn btn-default" id="deselect_all" type="button"><span class="glyphicon glyphicon-remove"></span> Deselect</button>
+ </div>
</form>
<br />
+ {% if dataset.type != 'Geno' %}
<br />
<div style="margin-bottom: 5px;">
<b>Show/Hide Columns:</b>
</div>
+ {% endif %}
+ <!--<div id="table_container" style="min-width: {% if dataset.type == 'ProbeSet' or dataset.type == 'Publish' %}2000{% else %}380{% endif %}px;">-->
<div id="table_container">
- <table class="table-hover table-striped cell-border" id='trait_table' style="float: left;">
+ <table class="table-hover table-striped cell-border" id='trait_table' style="float: left; width: {% if dataset.type == 'Geno' %}380px{% else %}100%{% endif %};">
<tbody>
<td colspan="100%" align="center"><br><b><font size="15">Loading...</font></b><br></td>
</tbody>
@@ -152,7 +157,7 @@
<script language="javascript" type="text/javascript" src="/static/new/js_external/md5.min.js"></script>
<script language="javascript" type="text/javascript" src="/static/new/javascript/search_results.js"></script>
- <script language="javascript" type="text/javascript" src="https://cdn.datatables.net/1.10.13/js/jquery.dataTables.min.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="/static/new/js_external/jszip.min.js"></script>
<script language="javascript" type="text/javascript" src="/static/new/packages/DataTables/js/dataTables.naturalSort.js"></script>
<script language="javascript" type="text/javascript" src="/static/new/packages/DataTables/extensions/dataTables.buttons.min.js"></script>
@@ -265,7 +270,7 @@
'columns': [
{
'data': null,
- 'width': "30px",
+ 'width': "25px",
'orderDataType': "dom-checkbox",
'orderSequence': [ "desc", "asc"],
'render': function(data, type, row, meta) {
@@ -291,11 +296,13 @@
{
'title': "Symbol",
'type': "natural",
+ 'width': "120px",
'data': "symbol"
},
{
'title': "Description",
'type': "natural",
+ 'width': "500px",
'data': null,
'render': function(data, type, row, meta) {
try {
@@ -322,24 +329,26 @@
'title': "Max LRS<a href=\"http://genenetwork.org//glossary.html#LRS\" target=\"_blank\" style=\"color: white;\"><sup>?</sup></a>",
'type': "natural",
'data': "lrs_score",
+ 'width': "80px",
'orderSequence': [ "desc", "asc"]
},
{
'title': "Max LRS Location",
'type': "natural",
- 'width': "120px",
+ 'width': "150px",
'data': "lrs_location"
},
{
'title': "Additive Effect<a href=\"http://genenetwork.org//glossary.html#A\" target=\"_blank\" style=\"color: white;\"><sup>?</sup></a>",
'type': "natural",
'data': "additive",
+ 'width': "120px",
'orderSequence': [ "desc", "asc"]
}{% elif dataset.type == 'Publish' %},
{
'title': "Description",
'type': "natural",
- 'width': "800px",
+ 'width': "500px",
'data': null,
'render': function(data, type, row, meta) {
try {
@@ -380,6 +389,7 @@
'type': "natural",
'orderDataType': "dom-inner-text",
'data': null,
+ 'width': "80px",
'render': function(data, type, row, meta) {
if (data.pubmed_id != "N/A"){
return '<a href="' + data.pubmed_link + '">' + data.pubmed_text + '</a>'
@@ -393,29 +403,31 @@
'title': "Max LRS<a href=\"http://genenetwork.org//glossary.html#LRS\" target=\"_blank\" style=\"color: white;\"><sup>?</sup></a>",
'type': "natural",
'data': "lrs_score",
+ 'width': "80px",
'orderSequence': [ "desc", "asc"]
},
{
'title': "Max LRS Location",
'type': "natural",
- 'width': "200px",
+ 'width': "150px",
'data': "lrs_location"
},
{
- 'title': "Additive<br>Effect<a href=\"http://genenetwork.org//glossary.html#A\" target=\"_blank\" style=\"color: white;\"><sup>?</sup></a>",
+ 'title': "Additive Effect<a href=\"http://genenetwork.org//glossary.html#A\" target=\"_blank\" style=\"color: white;\"><sup>?</sup></a>",
'type': "natural",
- 'width': "100px",
+ 'width': "120px",
'data': "additive",
'orderSequence': [ "desc", "asc"]
}{% elif dataset.type == 'Geno' %},
{
'title': "Location",
'type': "natural",
- 'width': "160px",
+ 'width': "140px",
'data': "location"
}{% endif %}
],
"order": [[1, "asc" ]],
+ {% if dataset.type != 'Geno' %}
buttons: [
{
extend: 'columnsToggle',
@@ -430,6 +442,9 @@
}
],
'sDom': "Bitir",
+ {% else %}
+ 'sDom': "itir",
+ {% endif %}
'deferRender': true,
'paging': false,
'orderClasses': true,
diff --git a/wqflask/wqflask/templates/set_group_privileges.html b/wqflask/wqflask/templates/set_group_privileges.html
new file mode 100644
index 00000000..98b0cc12
--- /dev/null
+++ b/wqflask/wqflask/templates/set_group_privileges.html
@@ -0,0 +1,77 @@
+{% extends "base.html" %}
+{% block title %}Set Group Privileges{% 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="/static/new/packages/DataTables/extensions/buttons.dataTables.css">
+ <link rel="stylesheet" type="text/css" href="/static/new/css/show_trait.css" />
+{% endblock %}
+{% block content %}
+<!-- Start of body -->
+ <div class="container">
+ <h1>Group Privileges</h1>
+ <br>
+ <form id="set_group_privileges">
+ <input type="hidden" name="resource_id" value="{{ resource_id }}">
+ <div style="min-width: 600px; max-width: 800px;">
+ <button type="submit" class="btn btn-primary" style="margin-bottom: 40px;">Add Group</button>
+ <hr>
+ <h2>Data and Metadata Privileges</h2>
+ <table id="data_privileges_table" class="table-hover table-striped cell-border" style="float: left;">
+ <thead>
+ <tr>
+ <th></th>
+ <th>No-Access</th>
+ <th>View</th>
+ <th>Edit</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>Data:</td>
+ <td align="center" style="padding: 0px;"><input type="radio" name="data_privilege" VALUE="no-access" checked></td>
+ <td align="center" style="padding: 0px;"><input type="radio" name="data_privilege" VALUE="view"></td>
+ <td align="center" style="padding: 0px;"><input type="radio" name="data_privilege" VALUE="edit"></td>
+ </tr>
+ <tr>
+ <td>Metadata:</td>
+ <td align="center" style="padding: 0px;"><input type="radio" name="metadata_privilege" VALUE="no-access" checked></td>
+ <td align="center" style="padding: 0px;"><input type="radio" name="metadata_privilege" VALUE="view"></td>
+ <td align="center" style="padding: 0px;"><input type="radio" name="metadata_privilege" VALUE="edit"></td>
+ </tr>
+ </tbody>
+ </table>
+ <hr>
+ <h2>Admin Privileges</h2>
+ <table id="admin_privileges_table" class="table-hover table-striped cell-border" style="float: left;">
+ <thead>
+ <tr>
+ <th></th>
+ <th>Not Admin</th>
+ <th>Edit Access</th>
+ <th>Edit Admins</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>Admin:</td>
+ <td align="center" style="padding: 0px;"><input type="radio" name="admin_privilege" VALUE="not-admin" checked></td>
+ <td align="center" style="padding: 0px;"><input type="radio" name="admin_privilege" VALUE="edit-access"></td>
+ <td align="center" style="padding: 0px;"><input type="radio" name="admin_privilege" VALUE="edit-admins"></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </form>
+ </div>
+
+<!-- End of body -->
+
+{% endblock %}
+
+{% block js %}
+ <script language="javascript" type="text/javascript" src="{{ url_for('js', filename='DataTables/js/jquery.dataTables.min.js') }}"></script>
+ <script>
+ $('#data_privileges_table').dataTable();
+ $('#admin_privileges_table').dataTable();
+ </script>
+{% endblock %}
diff --git a/wqflask/wqflask/templates/show_trait.html b/wqflask/wqflask/templates/show_trait.html
index 27c3e398..7380d198 100644
--- a/wqflask/wqflask/templates/show_trait.html
+++ b/wqflask/wqflask/templates/show_trait.html
@@ -6,7 +6,7 @@
<link rel="stylesheet" type="text/css" href="/static/new/css/prob_plot.css" />
<link rel="stylesheet" type="text/css" href="/static/new/css/scatter-matrix.css" />
<link rel="stylesheet" type="text/css" href="/static/new/css/d3-tip.min.css" />
- <link rel="stylesheet" type="text/css" href="/static/new/packages/DataTables/css/jquery.dataTables.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/packages/noUiSlider/nouislider.css" />
<link rel="stylesheet" type="text/css" href="/static/new/packages/noUiSlider/nouislider.pips.css" />
<link rel="stylesheet" type="text/css" href="/static/new/css/show_trait.css" />
@@ -74,6 +74,7 @@
<div class="panel-body">
{% include 'show_trait_transform_and_filter.html' %}
</div>
+ <div id="transform_alert_placeholder"><div id="transform_alert" style="display: none;"class="alert alert-success outlier-alert"><a class="close" data-dismiss="alert">�</a><span>Because there are some values between 0 and 1, log2 and log10 transforms will add 1 to each value.</span></div></div>
</div>
</div>
<div class="panel panel-default">
@@ -98,7 +99,7 @@
<div class="panel-body">
{% include 'show_trait_mapping_tools.html' %}
</div>
- <div id="alert_placeholder"></div>
+ <div id="outlier_alert_placeholder"></div>
</div>
</div>
<div class="panel panel-default">
diff --git a/wqflask/wqflask/templates/show_trait_calculate_correlations.html b/wqflask/wqflask/templates/show_trait_calculate_correlations.html
index 297d62ce..a9b371b8 100644
--- a/wqflask/wqflask/templates/show_trait_calculate_correlations.html
+++ b/wqflask/wqflask/templates/show_trait_calculate_correlations.html
@@ -109,9 +109,7 @@
<div class="form-group">
<label for="corr_sample_method" class="col-xs-2 control-label"></label>
<div class="col-xs-3 controls">
- <button class="btn corr_compute submit_special btn-success" data-url="/corr_compute" title="Compute Correlation">
- <i class="icon-ok-circle icon-white"></i> Compute
- </button>
+ <input type="button" class="btn corr_compute submit_special btn-success" data-url="/corr_compute" title="Compute Correlation" value="Compute">
</div>
</div>
</div>
diff --git a/wqflask/wqflask/templates/show_trait_details.html b/wqflask/wqflask/templates/show_trait_details.html
index 20dee54e..965c0340 100644
--- a/wqflask/wqflask/templates/show_trait_details.html
+++ b/wqflask/wqflask/templates/show_trait_details.html
@@ -141,6 +141,12 @@
GTEx Portal
</a>
&nbsp;&nbsp;
+ {% if phenogen_link %}
+ <a href="{{ phenogen_link }}" target="_blank">
+ PhenoGen
+ </a>
+ &nbsp;&nbsp;
+ {% endif %}
{% if genebridge_link %}
<a href="{{ genebridge_link }}" target="_blank">
GeneBridge
@@ -212,7 +218,7 @@
<a href="#redirect">
<button type="button" id="add_to_collection" class="btn btn-primary" title="Add to collection">Add</button>
</a>
- {% if this_trait.dataset.type == 'ProbeSet' %}
+ {% if this_trait.dataset.type == 'ProbeSet' or this_trait.dataset.type == 'Geno' %}
{% if this_trait.symbol != None %}
<a target="_blank" href="http://gn1.genenetwork.org/webqtl/main.py?cmd=sch&amp;gene={{ this_trait.symbol }}&amp;alias=1&amp;species={{ dataset.group.species }}">
<button type="button" class="btn btn-default" title="Find similar expression data">Find</button>
@@ -242,6 +248,11 @@
<a target="_blank" href="http://gn1.genenetwork.org/webqtl/main.py?cmd=show&db={{ this_trait.dataset.name }}&probeset={{ this_trait.name }}">
<button type="button" id="view_in_gn1" class="btn btn-primary" title="View Trait in GN1">View in GN1</button>
</a>
+ {% if admin_status == "owner" or admin_status == "edit-admins" or admin_status == "edit-access" %}
+ <a target="_blank" href="./resources/manage?resource_id={{ resource_id }}">
+ <button type="button" id="edit_resource" class="btn btn-success" title="Edit Resource">Edit</button>
+ </a>
+ {% endif %}
</div>
</div>
diff --git a/wqflask/wqflask/templates/show_trait_mapping_tools.html b/wqflask/wqflask/templates/show_trait_mapping_tools.html
index 777d4a2d..4d51adff 100755
--- a/wqflask/wqflask/templates/show_trait_mapping_tools.html
+++ b/wqflask/wqflask/templates/show_trait_mapping_tools.html
@@ -90,9 +90,7 @@
<div class="mapping_method_fields form-group">
<label class="col-xs-3 control-label"></label>
<div style="margin-left:20px;" class="col-xs-6">
- <button id="gemma_compute" class="btn submit_special btn-success" data-url="/marker_regression" title="Compute Marker Regression">
- Compute
- </button>
+ <input type="button" id="gemma_compute" class="btn submit_special btn-success" data-url="/marker_regression" title="Compute Marker Regression" value="Compute">
</div>
</div>
</div>
@@ -121,6 +119,16 @@
</div>
{% if genofiles and genofiles|length>0 %}
<div class="mapping_method_fields form-group">
+ <label for="scale_select" style="text-align: right;" class="col-xs-3 control-label">Map Scale</label>
+ <div style="margin-left:20px;" class="col-xs-2 controls">
+ <select id="scale_reaper" class="form-control" style="width: 80px;">
+ {% for item in scales_in_geno[genofiles[0]['location']] %}
+ <option value="{{ item[0] }}">{{ item[1] }}</option>
+ {% endfor %}
+ </select>
+ </div>
+ </div>
+ <div class="mapping_method_fields form-group">
<label style="text-align: right;" for="genofiles" class="col-xs-3 control-label">Genotypes</label>
<div style="margin-left:20px;" class="col-xs-4 controls">
<select id="genofile_reaper" class="form-control">
@@ -130,6 +138,17 @@
</select>
</div>
</div>
+ {% else %}
+ <div class="mapping_method_fields form-group">
+ <label for="scale_select" style="text-align: right;" class="col-xs-3 control-label">Map Scale</label>
+ <div style="margin-left:20px;" class="col-xs-2 controls">
+ <select id="scale_reaper" class="form-control" style="width: 80px;">
+ {% for item in scales_in_geno[dataset.group.name + ".geno"] %}
+ <option value="{{ item[0] }}">{{ item[1] }}</option>
+ {% endfor %}
+ </select>
+ </div>
+ </div>
{% endif %}
<div class="mapping_method_fields form-group">
<label style="text-align: right;" for="mapping_permutations" class="col-xs-3 control-label">Permutations</label>
@@ -205,9 +224,7 @@
<div class="mapping_method_fields form-group">
<label class="col-xs-3 control-label"></label>
<div style="margin-left:20px;" class="col-xs-6">
- <button id="interval_mapping_compute" class="btn submit_special btn-success" data-url="/marker_regression" title="Compute Interval Mapping">
- Compute
- </button>
+ <input type="button" id="interval_mapping_compute" class="btn submit_special btn-success" data-url="/marker_regression" title="Compute Interval Mapping" value="Compute">
</div>
</div>
</div>
@@ -225,7 +242,17 @@
</select>
</div>
</div>
- {% if genofiles and genofiles|length>0 %}
+ {% if genofiles and genofiles|length > 0 %}
+ <div class="mapping_method_fields form-group">
+ <label for="scale_select" style="text-align: right;" class="col-xs-3 control-label">Map Scale</label>
+ <div style="margin-left:20px;" class="col-xs-2 controls">
+ <select id="scale_rqtl_geno" class="form-control" style="width: 80px;">
+ {% for item in scales_in_geno[genofiles[0]['location']] %}
+ <option value="{{ item[0] }}">{{ item[1] }}</option>
+ {% endfor %}
+ </select>
+ </div>
+ </div>
<div class="mapping_method_fields form-group">
<label style="text-align:right;" for="genofiles" class="col-xs-3 control-label">Genotypes</label>
<div style="margin-left:20px;" class="col-xs-4 controls">
@@ -360,9 +387,7 @@
<div class="mapping_method_fields form-group">
<label class="col-xs-3 control-label"></label>
<div style="margin-left:20px;" class="col-xs-6">
- <button id="rqtl_geno_compute" class="btn submit_special btn-success" data-url="/marker_regression" title="Compute Interval Mapping">
- Compute
- </button>
+ <input type="button" id="rqtl_geno_compute" class="btn submit_special btn-success" data-url="/marker_regression" title="Compute Marker Regression" value="Compute">
</div>
</div>
</div>
@@ -379,7 +404,7 @@
<dt style="padding-top: 20px;">GEMMA</dt>
<dd>Maps traits with correction for kinship among samples using a linear mixed model method, and also allows users to fit multiple covariates such as sex, age, treatment, and genetic markers (<a href="https://www.ncbi.nlm.nih.gov/pubmed/24531419">PMID: 2453419</a>, and <a href="https://github.com/genetics-statistics/GEMMA"> GitHub code</a>). GEMMA incorporates the Leave One Chromosome Out (LOCO) method to ensure that the correction for kinship does not remove useful genetic variance near each marker. Markers can be filtered to include only those with minor allele frequencies (MAF) above a threshold. The default MAF is 0.05.</dd>
{% elif mapping_method == "R/qtl" %}
- <dt style="margin-top: 20px;">R/qtl</dt>
+ <dt style="margin-top: 20px;">R/qtl (version 1.44.9</dt>
<dd>The original R/qtl mapping package that supports classic experimental crosses including 4-parent F2 intercrosses (e.g., NIA ITP UM-HET3). R/qtl is ideal for populations that do not have complex kinship or admixture (<a href="https://www.ncbi.nlm.nih.gov/pubmed/12724300">PMID: 12724300</a>). Both R/qtl as implemented here, and R/qtl2 (<a href="https://www.ncbi.nlm.nih.gov/pubmed/30591514">PMID: 30591514</a>) are available as <a href="https://kbroman.org/pages/software.html">R suites</a>.</dd>
{% elif mapping_method == "QTLReaper" %}
<dt style="margin-top: 20px;">Haley-Knott Regression</dt>
diff --git a/wqflask/wqflask/templates/show_trait_transform_and_filter.html b/wqflask/wqflask/templates/show_trait_transform_and_filter.html
index 4be6d249..b4118b04 100644
--- a/wqflask/wqflask/templates/show_trait_transform_and_filter.html
+++ b/wqflask/wqflask/templates/show_trait_transform_and_filter.html
@@ -70,7 +70,7 @@
<div>
<p>Outliers highlighted in
- <strong style="background-color:yellow;">yellow</strong>
+ <strong style="background-color:orange;">orange</strong>
can be hidden using
the <strong>Hide Outliers</strong> button.
</p>
diff --git a/wqflask/wqflask/templates/snp_browser.html b/wqflask/wqflask/templates/snp_browser.html
index 4422ba73..88cb4d31 100644
--- a/wqflask/wqflask/templates/snp_browser.html
+++ b/wqflask/wqflask/templates/snp_browser.html
@@ -1,6 +1,6 @@
{% extends "base.html" %}
{% block css %}
- <link rel="stylesheet" type="text/css" href="/static/new/packages/DataTables/css/jquery.dataTables.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/packages/DataTables/extensions/buttons.bootstrap.css" />
<link rel="stylesheet" type="text/css" href="/static/new/css/typeahead-bootstrap.css" />
<link rel="stylesheet" type="text/css" href="/static/new/css/snp_browser.css" />
diff --git a/wqflask/wqflask/user_login.py b/wqflask/wqflask/user_login.py
index edd272c2..cfee0079 100644
--- a/wqflask/wqflask/user_login.py
+++ b/wqflask/wqflask/user_login.py
@@ -12,9 +12,6 @@ import requests
import simplejson as json
-import redis # used for collections
-Redis = redis.StrictRedis()
-
from flask import (Flask, g, render_template, url_for, request, make_response,
redirect, flash, abort)
@@ -23,7 +20,8 @@ from wqflask import pbkdf2
from wqflask.user_session import UserSession
from utility import hmac
-from utility.redis_tools import is_redis_available, get_user_id, get_user_by_unique_column, set_user_attribute, save_user, save_verification_code, check_verification_code, get_user_collections, save_collections
+from utility.redis_tools import is_redis_available, get_redis_conn, get_user_id, get_user_by_unique_column, set_user_attribute, save_user, save_verification_code, check_verification_code, get_user_collections, save_collections
+Redis = get_redis_conn()
from utility.logger import getLogger
logger = getLogger(__name__)
@@ -127,7 +125,7 @@ def send_email(toaddr, msg, fromaddr="no-reply@genenetwork.org"):
server.quit()
logger.info("Successfully sent email to "+toaddr)
-def send_verification_email(user_details, template_name = "email/verification.txt", key_prefix = "verification_code", subject = "GeneNetwork email verification"):
+def send_verification_email(user_details, template_name = "email/user_verification.txt", key_prefix = "verification_code", subject = "GeneNetwork e-mail verification"):
verification_code = str(uuid.uuid4())
key = key_prefix + ":" + verification_code
@@ -141,6 +139,21 @@ def send_verification_email(user_details, template_name = "email/verification.tx
send_email(recipient, subject, body)
return {"recipient": recipient, "subject": subject, "body": body}
+@app.route("/manage/verify_email")
+def verify_email():
+ if 'code' in request.args:
+ user_details = check_verification_code(request.args['code'])
+ if user_details:
+ # As long as they have access to the email account
+ # We might as well log them in
+ session_id_signed = get_signed_session_id(user_details)
+ flash("Thank you for logging in {}.".format(user_details['full_name']), "alert-success")
+ response = make_response(redirect(url_for('index_page', import_collections = import_col, anon_id = anon_id)))
+ response.set_cookie(UserSession.user_cookie_name, session_id_signed, max_age=None)
+ return response
+ else:
+ flash("Invalid code: Password reset code does not exist or might have expired!", "error")
+
@app.route("/n/login", methods=('GET', 'POST'))
def login():
params = request.form if request.form else request.args
@@ -204,7 +217,7 @@ def login():
response.set_cookie(UserSession.user_cookie_name, session_id_signed, max_age=None)
return response
else:
- email_ob = send_verification_email(user_details)
+ email_ob = send_verification_email(user_details, template_name = "email/user_verification.txt")
return render_template("newsecurity/verification_still_needed.html", subject=email_ob['subject'])
else: # Incorrect password
#ZS: It previously seemed to store that there was an incorrect log-in attempt here, but it did so in the MySQL DB so this might need to be reproduced with Redis
@@ -374,16 +387,13 @@ def password_reset():
hmac = request.args.get('hm')
if verification_code:
- user_email = check_verification_code(verification_code)
- if user_email:
- user_details = get_user_by_unique_column('email_address', user_email)
- if user_details:
- return render_template(
- "new_security/password_reset.html", user_encode=user_details["email_address"])
- else:
- flash("Invalid code: User no longer exists!", "error")
+ user_details = check_verification_code(verification_code)
+ if user_details:
+ return render_template(
+ "new_security/password_reset.html", user_encode=user_details["email_address"])
else:
flash("Invalid code: Password reset code does not exist or might have expired!", "error")
+ return redirect(url_for("login"))
else:
return redirect(url_for("login"))
@@ -394,6 +404,7 @@ def password_reset_step2():
errors = []
user_email = request.form['user_encode']
+ user_id = get_user_id("email_address", user_email)
password = request.form['password']
encoded_password = set_password(password)
@@ -401,9 +412,7 @@ def password_reset_step2():
set_user_attribute(user_id, "password", encoded_password)
flash("Password changed successfully. You can now sign in.", "alert-info")
- response = make_response(redirect(url_for('login')))
-
- return response
+ return redirect(url_for('login'))
def register_user(params):
thank_you_mode = False
diff --git a/wqflask/wqflask/user_manager.py b/wqflask/wqflask/user_manager.py
index ce76e0e8..a871e91a 100644
--- a/wqflask/wqflask/user_manager.py
+++ b/wqflask/wqflask/user_manager.py
@@ -356,7 +356,7 @@ def get_cookie():
g.user_session = UserSession()
g.cookie_session = AnonUser()
-@app.after_request
+#@app.after_request
def set_cookie(response):
if not request.cookies.get(g.cookie_session.cookie_name):
response.set_cookie(g.cookie_session.cookie_name, g.cookie_session.cookie)
@@ -538,7 +538,7 @@ def basic_info():
ip_address = request.remote_addr,
user_agent = request.headers.get('User-Agent'))
-@app.route("/manage/verify_email")
+#@app.route("/manage/verify_email")
def verify_email():
user = DecodeUser(VerificationEmail.key_prefix).user
user.confirmed = json.dumps(basic_info(), sort_keys=True)
@@ -552,7 +552,7 @@ def verify_email():
response.set_cookie(UserSession.cookie_name, session_id_signed)
return response
-@app.route("/n/password_reset", methods=['GET'])
+#@app.route("/n/password_reset", methods=['GET'])
def password_reset():
"""Entry point after user clicks link in E-mail"""
logger.debug("in password_reset request.url is:", request.url)
@@ -576,7 +576,7 @@ def password_reset():
else:
return redirect(url_for("login"))
-@app.route("/n/password_reset_step2", methods=('POST',))
+#@app.route("/n/password_reset_step2", methods=('POST',))
def password_reset_step2():
"""Handle confirmation E-mail for password reset"""
logger.debug("in password_reset request.url is:", request.url)
@@ -620,7 +620,7 @@ class DecodeUser(object):
logger.debug("data is:", data)
return model.User.query.get(data['id'])
-@app.route("/n/login", methods=('GET', 'POST'))
+#@app.route("/n/login", methods=('GET', 'POST'))
def login():
lu = LoginUser()
login_type = request.args.get("type")
@@ -630,7 +630,7 @@ def login():
else:
return lu.standard_login()
-@app.route("/n/login/github_oauth2", methods=('GET', 'POST'))
+#@app.route("/n/login/github_oauth2", methods=('GET', 'POST'))
def github_oauth2():
from utility.tools import GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET
code = request.args.get("code")
@@ -661,7 +661,7 @@ def github_oauth2():
url = "/n/login?type=github&uid="+user_details["user_id"]
return redirect(url)
-@app.route("/n/login/orcid_oauth2", methods=('GET', 'POST'))
+#@app.route("/n/login/orcid_oauth2", methods=('GET', 'POST'))
def orcid_oauth2():
from uuid import uuid4
from utility.tools import ORCID_CLIENT_ID, ORCID_CLIENT_SECRET, ORCID_TOKEN_URL, ORCID_AUTH_URL
@@ -841,7 +841,7 @@ class LoginUser(object):
db_session.add(login_rec)
db_session.commit()
-@app.route("/n/logout")
+#@app.route("/n/logout")
def logout():
logger.debug("Logging out...")
UserSession().delete_session()
@@ -852,7 +852,7 @@ def logout():
return response
-@app.route("/n/forgot_password", methods=['GET'])
+#@app.route("/n/forgot_password", methods=['GET'])
def forgot_password():
"""Entry point for forgotten password"""
print("ARGS: ", request.args)
@@ -860,7 +860,7 @@ def forgot_password():
print("ERRORS: ", errors)
return render_template("new_security/forgot_password.html", errors=errors)
-@app.route("/n/forgot_password_submit", methods=('POST',))
+#@app.route("/n/forgot_password_submit", methods=('POST',))
def forgot_password_submit():
"""When a forgotten password form is submitted we get here"""
params = request.form
@@ -945,7 +945,7 @@ def is_redis_available():
# return LoginUser().actual_login(user, assumed_by=assumed_by)
-@app.route("/n/register", methods=('GET', 'POST'))
+#@app.route("/n/register", methods=('GET', 'POST'))
def register():
params = None
errors = None
diff --git a/wqflask/wqflask/user_session.py b/wqflask/wqflask/user_session.py
index 4c2305ba..ba659fe5 100644
--- a/wqflask/wqflask/user_session.py
+++ b/wqflask/wqflask/user_session.py
@@ -6,10 +6,6 @@ import uuid
import simplejson as json
-import redis # used for collections
-Redis = redis.StrictRedis()
-
-
from flask import (Flask, g, render_template, url_for, request, make_response,
redirect, flash, abort)
@@ -17,7 +13,8 @@ from wqflask import app
from utility import hmac
#from utility.elasticsearch_tools import get_elasticsearch_connection
-from utility.redis_tools import get_user_id, get_user_by_unique_column, get_user_collections, save_collections
+from utility.redis_tools import get_redis_conn, get_user_id, get_user_collections, save_collections
+Redis = get_redis_conn()
from utility.logger import getLogger
logger = getLogger(__name__)
@@ -29,6 +26,11 @@ THIRTY_DAYS = 60 * 60 * 24 * 30
def get_user_session():
logger.info("@app.before_request get_session")
g.user_session = UserSession()
+ #ZS: I think this should solve the issue of deleting the cookie and redirecting to the home page when a user's session has expired
+ if not g.user_session:
+ response = make_response(redirect(url_for('login')))
+ response.set_cookie('session_id_v2', '', expires=0)
+ return response
@app.after_request
def set_user_session(response):
@@ -37,7 +39,6 @@ def set_user_session(response):
response.set_cookie(g.user_session.cookie_name, g.user_session.cookie)
return response
-
def verify_cookie(cookie):
the_uuid, separator, the_signature = cookie.partition(':')
assert len(the_uuid) == 36, "Is session_id a uuid?"
@@ -88,14 +89,11 @@ class UserSession(object):
user_id = str(uuid.uuid4()))
Redis.hmset(self.redis_key, self.record)
Redis.expire(self.redis_key, THIRTY_DAYS)
- response = make_response(redirect(url_for('login')))
- response.set_cookie(self.user_cookie_name, '', expires=0)
########### Grrr...this won't work because of the way flask handles cookies
# Delete the cookie
flash("Due to inactivity your session has expired. If you'd like please login again.")
- return response
- #return
+ return None
else:
self.record = dict(login_time = time.time(),
user_type = "anon",
@@ -119,10 +117,10 @@ class UserSession(object):
@property
def user_id(self):
"""Shortcut to the user_id"""
- if 'user_id' in self.record:
- return self.record['user_id']
- else:
- return ''
+ if 'user_id' not in self.record:
+ self.record['user_id'] = str(uuid.uuid4())
+
+ return self.record['user_id']
@property
def redis_user_id(self):
@@ -161,7 +159,7 @@ class UserSession(object):
"""List of user's collections"""
#ZS: Get user's collections if they exist
- collections = get_user_collections(self.redis_user_id)
+ collections = get_user_collections(self.user_id)
collections = [item for item in collections if item['name'] != "Your Default Collection"] + [item for item in collections if item['name'] == "Your Default Collection"] #ZS: Ensure Default Collection is last in list
return collections
@@ -169,7 +167,7 @@ class UserSession(object):
def num_collections(self):
"""Number of user's collections"""
- return len(self.user_collections)
+ return len([item for item in self.user_collections if item['num_members'] > 0])
def add_collection(self, collection_name, traits):
"""Add collection into Redis"""
@@ -277,7 +275,7 @@ class UserSession(object):
def update_collections(self, updated_collections):
collection_body = json.dumps(updated_collections)
- save_collections(self.redis_user_id, collection_body)
+ save_collections(self.user_id, collection_body)
def import_traits_to_user(self, anon_id):
collections = get_user_collections(anon_id)
diff --git a/wqflask/wqflask/views.py b/wqflask/wqflask/views.py
index 44752246..131345d3 100644
--- a/wqflask/wqflask/views.py
+++ b/wqflask/wqflask/views.py
@@ -23,16 +23,14 @@ import uuid
import simplejson as json
import yaml
-#Switching from Redis to StrictRedis; might cause some issues
-import redis
-Redis = redis.StrictRedis()
-
import flask
import base64
import array
import sqlalchemy
from wqflask import app
-from flask import g, Response, request, make_response, render_template, send_from_directory, jsonify, redirect
+from flask import g, Response, request, make_response, render_template, send_from_directory, jsonify, redirect, url_for
+from wqflask import group_manager
+from wqflask import resource_manager
from wqflask import search_results
from wqflask import export_traits
from wqflask import gsearch
@@ -40,7 +38,7 @@ from wqflask import update_search_results
from wqflask import docs
from wqflask import news
from wqflask.submit_bnw import get_bnw_input
-from base.data_set import DataSet # Used by YAML in marker_regression
+from base.data_set import create_dataset, DataSet # Used by YAML in marker_regression
from wqflask.show_trait import show_trait
from wqflask.show_trait import export_trait_data
from wqflask.heatmap import heatmap
@@ -55,11 +53,13 @@ from wqflask.correlation import corr_scatter_plot
from wqflask.wgcna import wgcna_analysis
from wqflask.ctl import ctl_analysis
from wqflask.snp_browser import snp_browser
-#from wqflask.trait_submission import submit_trait
from utility import temp_data
from utility.tools import SQL_URI,TEMPDIR,USE_REDIS,USE_GN_SERVER,GN_SERVER_URL,GN_VERSION,JS_TWITTER_POST_FETCHER_PATH,JS_GUIX_PATH, CSS_PATH
from utility.helper_functions import get_species_groups
+from utility.authentication_tools import check_resource_availability
+from utility.redis_tools import get_redis_conn
+Redis = get_redis_conn()
from base.webqtlConfig import GENERATED_IMAGE_DIR
from utility.benchmark import Bench
@@ -87,6 +87,32 @@ def connect_db():
g.db = g._database = sqlalchemy.create_engine(SQL_URI, encoding="latin1")
logger.debug(g.db)
+@app.before_request
+def check_access_permissions():
+ logger.debug("@app.before_request check_access_permissions")
+ available = True
+ if "temp_trait" in request.args:
+ if request.args['temp_trait'] == "True":
+ pass
+ else:
+ if 'dataset' in request.args:
+ if request.args['dataset'] == "Temp":
+ permissions = check_resource_availability("Temp")
+ else:
+ dataset = create_dataset(request.args['dataset'])
+
+ if dataset.type == "Temp":
+ permissions = False
+ if 'trait_id' in request.args:
+ permissions = check_resource_availability(dataset, request.args['trait_id'])
+ elif dataset.type != "Publish":
+ permissions = check_resource_availability(dataset)
+ else:
+ return None
+
+ if 'view' not in permissions['data']:
+ return redirect(url_for("no_access_page"))
+
@app.teardown_appcontext
def shutdown_session(exception=None):
db = getattr(g, '_database', None)
@@ -120,6 +146,10 @@ def handle_bad_request(e):
resp.set_cookie(err_msg[:32],animation)
return resp
+@app.route("/authentication_needed")
+def no_access_page():
+ return render_template("new_security/not_authenticated.html")
+
@app.route("/")
def index_page():
logger.info("Sending index_page")
@@ -401,25 +431,43 @@ def export_traits_csv():
def export_perm_data():
"""CSV file consisting of the permutation data for the mapping results"""
logger.info(request.url)
- num_perm = float(request.form['num_perm'])
- perm_data = json.loads(request.form['perm_results'])
+ perm_info = json.loads(request.form['perm_info'])
+
+ now = datetime.datetime.now()
+ time_str = now.strftime('%H:%M_%d%B%Y')
+
+ file_name = "Permutation_" + perm_info['num_perm'] + "_" + perm_info['trait_name'] + "_" + time_str
+
+ the_rows = [
+ ["#Permutation Test"],
+ ["#File_name: " + file_name],
+ ["#Metadata: From GeneNetwork.org"],
+ ["#Trait_ID: " + perm_info['trait_name']],
+ ["#Trait_description: " + perm_info['trait_description']],
+ ["#N_permutations: " + str(perm_info['num_perm'])],
+ ["#Cofactors: " + perm_info['cofactors']],
+ ["#N_cases: " + str(perm_info['n_samples'])],
+ ["#N_genotypes: " + str(perm_info['n_genotypes'])],
+ ["#Genotype_file: " + perm_info['genofile']],
+ ["#Units_linkage: " + perm_info['units_linkage']],
+ ["#Permutation_stratified_by: " + ", ".join([ str(cofactor) for cofactor in perm_info['strat_cofactors']])],
+ ["#RESULTS_1: Suggestive LRS(p=0.63) = " + str(np.percentile(np.array(perm_info['perm_data']), 67))],
+ ["#RESULTS_2: Significant LRS(p=0.05) = " + str(np.percentile(np.array(perm_info['perm_data']), 95))],
+ ["#RESULTS_3: Highly Significant LRS(p=0.01) = " + str(np.percentile(np.array(perm_info['perm_data']), 99))],
+ ["#Comment: Results sorted from low to high peak linkage"]
+ ]
buff = StringIO.StringIO()
writer = csv.writer(buff)
- writer.writerow(["Suggestive LRS (p=0.63) = " + str(np.percentile(np.array(perm_data), 67))])
- writer.writerow(["Significant LRS (p=0.05) = " + str(np.percentile(np.array(perm_data), 95))])
- writer.writerow(["Highly Significant LRS (p=0.01) = " + str(np.percentile(np.array(perm_data), 99))])
- writer.writerow("")
- writer.writerow([str(num_perm) + " Permutations"])
- writer.writerow("")
- for item in perm_data:
+ writer.writerows(the_rows)
+ for item in perm_info['perm_data']:
writer.writerow([item])
csv_data = buff.getvalue()
buff.close()
return Response(csv_data,
mimetype='text/csv',
- headers={"Content-Disposition":"attachment;filename=perm_data.csv"})
+ headers={"Content-Disposition":"attachment;filename=" + file_name + ".csv"})
@app.route("/show_temp_trait", methods=('POST',))
def show_temp_trait_page():
@@ -582,7 +630,7 @@ def loading_page():
logger.info(request.url)
initial_start_vars = request.form
start_vars_container = {}
- num_vals = 0 #ZS: So it can be displayed on loading page
+ n_samples = 0 #ZS: So it can be displayed on loading page
if 'wanted_inputs' in initial_start_vars:
wanted = initial_start_vars['wanted_inputs'].split(",")
start_vars = {}
@@ -590,17 +638,29 @@ def loading_page():
if key in wanted or key.startswith(('value:')):
start_vars[key] = value
- if 'num_vals' in start_vars:
- num_vals = int(start_vars['num_vals'])
+ if 'n_samples' in start_vars:
+ n_samples = int(start_vars['n_samples'])
else:
- if 'primary_samples' in start_vars:
- samples = start_vars['primary_samples'].split(",")
- for sample in samples:
- value = start_vars.get('value:' + sample)
- if value != "x":
- num_vals += 1
-
- start_vars['num_vals'] = num_vals
+ if 'group' in start_vars:
+ dataset = create_dataset(start_vars['dataset'], group_name = start_vars['group'])
+ else:
+ dataset = create_dataset(start_vars['dataset'])
+ genofile_samplelist = []
+ samples = start_vars['primary_samples'].split(",")
+ if 'genofile' in start_vars:
+ if start_vars['genofile'] != "":
+ genofile_string = start_vars['genofile']
+ dataset.group.genofile = genofile_string.split(":")[0]
+ genofile_samples = run_mapping.get_genofile_samplelist(dataset)
+ if len(genofile_samples) > 1:
+ samples = genofile_samples
+
+ for sample in samples:
+ value = start_vars.get('value:' + sample)
+ if value != "x":
+ n_samples += 1
+
+ start_vars['n_samples'] = n_samples
start_vars['wanted_inputs'] = initial_start_vars['wanted_inputs']
start_vars_container['start_vars'] = start_vars
@@ -670,7 +730,7 @@ def mapping_results_page():
'mapmodel_rqtl_geno',
'temp_trait',
'reaper_version',
- 'num_vals',
+ 'n_samples',
'transform'
)
start_vars = {}