diff options
author | Pjotr Prins | 2020-07-08 04:52:07 -0500 |
---|---|---|
committer | Pjotr Prins | 2020-07-08 04:52:07 -0500 |
commit | c249ba2ef7d691227da8864838dfc97db68d4084 (patch) | |
tree | 52e28fe60dc2bbe25fcfa0cd18a04a660e9573fe /wqflask | |
parent | 421848837d3b489762bb9e58e5e7047d02cdb20b (diff) | |
parent | b026c18a1263f84cbed86018e3ba2d20e97b61d4 (diff) | |
download | genenetwork2-c249ba2ef7d691227da8864838dfc97db68d4084.tar.gz |
Merge branch 'testing' of github.com:genenetwork/genenetwork2 into testing
Diffstat (limited to 'wqflask')
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§ion=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> + {% if phenogen_link %} + <a href="{{ phenogen_link }}" target="_blank"> + PhenoGen + </a> + + {% 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&gene={{ this_trait.symbol }}&alias=1&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 = {} |