From 61c13a09dba95958f183dc55f3d59c8856b5f753 Mon Sep 17 00:00:00 2001
From: Pjotr Prins
Date: Wed, 13 Feb 2019 12:41:45 +0000
Subject: Removed pylmm references and related functions
---
wqflask/utility/tools.py | 4 ----
1 file changed, 4 deletions(-)
(limited to 'wqflask/utility/tools.py')
diff --git a/wqflask/utility/tools.py b/wqflask/utility/tools.py
index ea216a35..86ef2e1e 100644
--- a/wqflask/utility/tools.py
+++ b/wqflask/utility/tools.py
@@ -107,9 +107,6 @@ def js_path(module=None):
return try_guix
raise "No JS path found for "+module+" (if not in Guix check JS_GN_PATH)"
-def pylmm_command(guess=None):
- return assert_bin(get_setting("PYLMM_COMMAND",guess))
-
def gemma_command(guess=None):
return assert_bin(get_setting("GEMMA_COMMAND",guess))
@@ -276,7 +273,6 @@ SMTP_CONNECT = get_setting('SMTP_CONNECT')
SMTP_USERNAME = get_setting('SMTP_USERNAME')
SMTP_PASSWORD = get_setting('SMTP_PASSWORD')
-PYLMM_COMMAND = app_set("PYLMM_COMMAND",pylmm_command())
GEMMA_COMMAND = app_set("GEMMA_COMMAND",gemma_command())
assert(GEMMA_COMMAND is not None)
PLINK_COMMAND = app_set("PLINK_COMMAND",plink_command())
--
cgit v1.2.3
From 496759ad08efb02b4268ea7f3bbb7905974237e9 Mon Sep 17 00:00:00 2001
From: Pjotr Prins
Date: Mon, 18 Feb 2019 09:39:41 +0000
Subject: Updated installation instructions and SERVER_PORT for single flask
server
---
doc/README.org | 71 ++++++++++++++++++++++++++++++++++--------------
wqflask/runserver.py | 8 ++++--
wqflask/utility/tools.py | 1 +
3 files changed, 57 insertions(+), 23 deletions(-)
(limited to 'wqflask/utility/tools.py')
diff --git a/doc/README.org b/doc/README.org
index 5dc9e994..620c946c 100644
--- a/doc/README.org
+++ b/doc/README.org
@@ -12,13 +12,15 @@
- [[#load-the-small-database-in-mysql][Load the small database in MySQL]]
- [[#gn2-dependency-graph][GN2 Dependency Graph]]
- [[#working-with-the-gn2-source-code][Working with the GN2 source code]]
+ - [[#running-elasticsearch][Running ElasticSearch]]
+ - [[#systemd][SystemD]]
+ - [[#read-more][Read more]]
- [[#trouble-shooting][Trouble shooting]]
- [[#importerror-no-module-named-jinja2][ImportError: No module named jinja2]]
- - [[#error-can-not-find-directory-homegn2_data][ERROR: can not find directory $HOME/gn2_data]]
+ - [[#error-can-not-find-directory-homegn2_data-or-can-not-find-directory-homegenotype_filesgenotype][ERROR: 'can not find directory $HOME/gn2_data' or 'can not find directory $HOME/genotype_files/genotype']]
- [[#cant-run-a-module][Can't run a module]]
- [[#rpy2-error-show-now-found][Rpy2 error 'show' now found]]
- [[#mysql-cant-connect-server-through-socket-error][Mysql can't connect server through socket ERROR]]
- - [[#read-more][Read more]]
- [[#irc-session][IRC session]]
* Introduction
@@ -100,11 +102,13 @@ mysql (which comes as part of the GNU Guix genenetwork2 install).
As root configure and run
-: adduser mysql && addgroup mysql
-: mysqld --datadir=/var/mysql --initialize-insecure
-: mkdir -p /var/run/mysqld
-: chown mysql.mysql ~/mysql /var/run/mysqld
-: mysqld -u mysql --datadir=/var/mysql --explicit_defaults_for_timestamp -P 12048"
+#+BEGIN_SRC bash
+adduser mysql && addgroup mysql
+mysqld --datadir=/var/mysql --initialize-insecure
+mkdir -p /var/run/mysqld
+chown mysql.mysql ~/mysql /var/run/mysqld
+mysqld -u mysql --datadir=/var/mysql --explicit_defaults_for_timestamp -P 12048"
+#+END_SRC
If you want to run as root you may have to set
@@ -192,6 +196,41 @@ http://biogems.info/contrib/genenetwork/gn2.svg
See [[development.org]].
+* Running ElasticSearch
+
+In order to start up elasticsearch:
+Penguin - change user to "elasticsearch" and use the following command: "env JAVA_HOME=/opt/jdk-9.0.4 /opt/elasticsearch-6.2.1/bin/elasticsearch"
+
+
+** SystemD
+
+New server - as root run "systemctl restart elasticsearch"
+
+#+BEGIN_SRC
+tux01:/etc/systemd/system# cat elasticsearch.service
+[Unit]
+Description=Run Elasticsearch
+
+[Service]
+ExecStart=/opt/elasticsearch-6.2.1/bin/elasticsearch
+Environment=JAVA_HOME=/opt/jdk-9.0.4
+Environment="ES_JAVA_OPTS=-Xms1g -Xmx8g"
+Environment="PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:/opt/jdk-9.0.4/bin"
+LimitNOFILE=65536
+StandardOutput=syslog
+StandardError=syslog
+User=elasticsearch
+
+[Install]
+WantedBy=multi-user.target
+#+END_SRC
+
+* Read more
+
+If you want to understand the architecture of GN2 read
+[[Architecture.org]]. The rest of this document is mostly on deployment
+of GN2.
+
* Trouble shooting
** ImportError: No module named jinja2
@@ -210,13 +249,17 @@ On one system:
: export GEM_PATH="$HOME/.guix-profile/lib/ruby/gems/2.2.0"
and perhaps a few more.
-** ERROR: can not find directory $HOME/gn2_data
+** ERROR: 'can not find directory $HOME/gn2_data' or 'can not find directory $HOME/genotype_files/genotype'
The default settings file looks in your $HOME/gn2_data. Since these
files come with a Guix installation you should take a hint from the
values in the installed version of default_settings.py (see above in
this document).
+You can use the GENENETWORK_FILES switch to set the datadir, for example
+
+: env GN2_PROFILE=~/opt/gn-latest GENENETWORK_FILES=/gnu/data/gn2_data ./bin/genenetwork2
+
** Can't run a module
In rare cases, development modules are not brought in with Guix
@@ -257,18 +300,6 @@ if that works run genenetwork after setting SQL_URI to something like
: export SQL_URI=mysql://gn2:mysql_password@127.0.0.1/db_webqtl_s
-* Running ElasticSearch
-
-In order to start up elasticsearch:
-Penguin - change user to "elasticsearch" and use the following command: "env JAVA_HOME=/opt/jdk-9.0.4 /opt/elasticsearch-6.2.1/bin/elasticsearch"
-
-New server - as root run "systemctl restart elasticsearch"
-
-* Read more
-
-If you want to understand the architecture of GN2 read
-[[Architecture.org]]. The rest of this document is mostly on deployment
-of GN2.
* IRC session
diff --git a/wqflask/runserver.py b/wqflask/runserver.py
index 5f41d04d..7c06356b 100644
--- a/wqflask/runserver.py
+++ b/wqflask/runserver.py
@@ -27,9 +27,11 @@ app_config()
werkzeug_logger = logging.getLogger('werkzeug')
+from utility.tools import WEBSERVER_MODE, SERVER_PORT
+
if WEBSERVER_MODE == 'DEBUG':
app.run(host='0.0.0.0',
- port=port,
+ port=SERVER_PORT,
debug=True,
use_debugger=False,
threaded=False,
@@ -38,7 +40,7 @@ if WEBSERVER_MODE == 'DEBUG':
elif WEBSERVER_MODE == 'DEV':
werkzeug_logger.setLevel(logging.WARNING)
app.run(host='0.0.0.0',
- port=port,
+ port=SERVER_PORT,
debug=False,
use_debugger=False,
threaded=False,
@@ -46,7 +48,7 @@ elif WEBSERVER_MODE == 'DEV':
use_reloader=True)
else: # staging/production modes
app.run(host='0.0.0.0',
- port=port,
+ port=SERVER_PORT,
debug=False,
use_debugger=False,
threaded=True,
diff --git a/wqflask/utility/tools.py b/wqflask/utility/tools.py
index 86ef2e1e..8b2260f5 100644
--- a/wqflask/utility/tools.py
+++ b/wqflask/utility/tools.py
@@ -232,6 +232,7 @@ GN_VERSION = get_setting('GN_VERSION')
HOME = get_setting('HOME')
WEBSERVER_MODE = get_setting('WEBSERVER_MODE')
GN_SERVER_URL = get_setting('GN_SERVER_URL')
+SERVER_PORT = get_setting_int('SERVER_PORT')
SQL_URI = get_setting('SQL_URI')
LOG_LEVEL = get_setting('LOG_LEVEL')
LOG_LEVEL_DEBUG = get_setting_int('LOG_LEVEL_DEBUG')
--
cgit v1.2.3
From 5f5adb62bf13ace887c03522b0b8e83181cd6503 Mon Sep 17 00:00:00 2001
From: zsloan
Date: Tue, 23 Jul 2019 11:24:56 -0500
Subject: Added change to automatically update datasets list using rest api
instead of json file
Removed option to edit certain html pages, like news, policies, etc
---
etc/default_settings.py | 1 +
wqflask/base/data_set.py | 4 +-
wqflask/utility/tools.py | 4 ++
.../marker_regression/display_mapping_results.py | 1 -
wqflask/wqflask/marker_regression/run_mapping.py | 17 +++++++-
.../new/javascript/dataset_menu_structure.json | 51 ++++++++++++----------
wqflask/wqflask/templates/docs.html | 4 +-
7 files changed, 53 insertions(+), 29 deletions(-)
(limited to 'wqflask/utility/tools.py')
diff --git a/etc/default_settings.py b/etc/default_settings.py
index a1fe81e5..a16ac3ad 100644
--- a/etc/default_settings.py
+++ b/etc/default_settings.py
@@ -91,4 +91,5 @@ JS_GN_PATH = os.environ['HOME']+"/genenetwork/javascript"
# ---- GN2 Executables (overwrite for testing only)
# PLINK_COMMAND = str.strip(os.popen("which plink2").read())
# GEMMA_COMMAND = str.strip(os.popen("which gemma").read())
+REAPER_COMMAND = HOME + "/gn2-zach/rust-qtlreaper/target/release/qtlreaper"
# GEMMA_WRAPPER_COMMAND = str.strip(os.popen("which gemma-wrapper").read())
diff --git a/wqflask/base/data_set.py b/wqflask/base/data_set.py
index d766e284..41de8492 100644
--- a/wqflask/base/data_set.py
+++ b/wqflask/base/data_set.py
@@ -46,6 +46,8 @@ from utility import chunks
from utility import gen_geno_ob
from utility.tools import locate, locate_ignore_error, flat_files
+from wqflask.api import gen_menu
+
from maintenance import get_group_samplelists
from MySQLdb import escape_string as escape
@@ -92,7 +94,7 @@ Publish or ProbeSet. E.g.
"""
self.datasets = {}
if USE_GN_SERVER:
- data = menu_main()
+ data = gen_menu.gen_dropdown_json()
else:
file_name = "wqflask/static/new/javascript/dataset_menu_structure.json"
with open(file_name, 'r') as fh:
diff --git a/wqflask/utility/tools.py b/wqflask/utility/tools.py
index 8b2260f5..31ab2046 100644
--- a/wqflask/utility/tools.py
+++ b/wqflask/utility/tools.py
@@ -107,6 +107,9 @@ def js_path(module=None):
return try_guix
raise "No JS path found for "+module+" (if not in Guix check JS_GN_PATH)"
+def reaper_command(guess=None):
+ return get_setting("REAPER_COMMAND",guess)
+
def gemma_command(guess=None):
return assert_bin(get_setting("GEMMA_COMMAND",guess))
@@ -274,6 +277,7 @@ SMTP_CONNECT = get_setting('SMTP_CONNECT')
SMTP_USERNAME = get_setting('SMTP_USERNAME')
SMTP_PASSWORD = get_setting('SMTP_PASSWORD')
+REAPER_COMMAND = app_set("REAPER_COMMAND",reaper_command())
GEMMA_COMMAND = app_set("GEMMA_COMMAND",gemma_command())
assert(GEMMA_COMMAND is not None)
PLINK_COMMAND = app_set("PLINK_COMMAND",plink_command())
diff --git a/wqflask/wqflask/marker_regression/display_mapping_results.py b/wqflask/wqflask/marker_regression/display_mapping_results.py
index 3bcd613f..d9601405 100644
--- a/wqflask/wqflask/marker_regression/display_mapping_results.py
+++ b/wqflask/wqflask/marker_regression/display_mapping_results.py
@@ -1790,7 +1790,6 @@ class DisplayMappingResults(object):
m = 0
thisLRSColor = self.colorCollection[0]
if qtlresult['chr'] != previous_chr and self.selectedChr == -1:
-
if self.manhattan_plot != True:
canvas.drawPolygon(LRSCoordXY,edgeColor=thisLRSColor,closed=0, edgeWidth=lrsEdgeWidth, clipX=(xLeftOffset, xLeftOffset + plotWidth))
diff --git a/wqflask/wqflask/marker_regression/run_mapping.py b/wqflask/wqflask/marker_regression/run_mapping.py
index 2bde2b53..6e9fe85c 100644
--- a/wqflask/wqflask/marker_regression/run_mapping.py
+++ b/wqflask/wqflask/marker_regression/run_mapping.py
@@ -36,7 +36,7 @@ from utility import helper_functions
from utility import Plot, Bunch
from utility import temp_data
from utility.benchmark import Bench
-from wqflask.marker_regression import gemma_mapping, rqtl_mapping, qtlreaper_mapping, plink_mapping
+from wqflask.marker_regression import gemma_mapping, rqtl_mapping, qtlreaper_mapping, plink_mapping, rust_reaper_mapping
from utility.tools import locate, locate_ignore_error, GEMMA_COMMAND, PLINK_COMMAND, TEMPDIR
from utility.external import shell
@@ -242,7 +242,8 @@ class RunMapping(object):
self.control_marker = start_vars['control_marker']
self.do_control = start_vars['do_control']
logger.info("Running qtlreaper")
- results, self.json_data, self.perm_output, self.suggestive, self.significant, self.bootstrap_results = qtlreaper_mapping.gen_reaper_results(self.this_trait,
+
+ results, self.perm_output, self.suggestive, self.significant, self.bootstrap_results = rust_reaper_mapping.run_reaper(self.this_trait,
self.dataset,
self.samples,
self.vals,
@@ -253,6 +254,18 @@ class RunMapping(object):
self.do_control,
self.control_marker,
self.manhattan_plot)
+
+ # results, self.json_data, self.perm_output, self.suggestive, self.significant, self.bootstrap_results = qtlreaper_mapping.gen_reaper_results(self.this_trait,
+ # self.dataset,
+ # self.samples,
+ # self.vals,
+ # self.json_data,
+ # self.num_perm,
+ # self.bootCheck,
+ # self.num_bootstrap,
+ # self.do_control,
+ # self.control_marker,
+ # self.manhattan_plot)
elif self.mapping_method == "plink":
self.score_type = "-log(p)"
self.manhattan_plot = True
diff --git a/wqflask/wqflask/static/new/javascript/dataset_menu_structure.json b/wqflask/wqflask/static/new/javascript/dataset_menu_structure.json
index 0d73213d..6531f5a0 100644
--- a/wqflask/wqflask/static/new/javascript/dataset_menu_structure.json
+++ b/wqflask/wqflask/static/new/javascript/dataset_menu_structure.json
@@ -1440,7 +1440,7 @@
[
"None",
"HSBPublish",
- "HSB Phenotypes"
+ "HSB Published Phenotypes"
]
],
"Posterior Inferior Parietal Cortex mRNA": [
@@ -1721,7 +1721,7 @@
[
"None",
"B6D2Publish",
- "B6D2 Phenotypes"
+ "UTHSC-Glaucoma-Aged-Retina Phenotypes"
]
]
},
@@ -2247,6 +2247,11 @@
"Eye_M2_0908_R_ND",
"Eye M430v2 WT Gpnmb (Sep08) RMA"
],
+ [
+ "382",
+ "Eye_M2_0908_WTWT",
+ "Eye M430v2 WT WT (Sep08) RMA"
+ ],
[
"279",
"Eye_M2_0908_R_WT",
@@ -2257,11 +2262,6 @@
"Eye_M2_0908_R_MT",
"Eye M430v2 Mutant Tyrp1 (Sep08) RMA"
],
- [
- "382",
- "Eye_M2_0908_WTWT",
- "Eye M430v2 WT WT (Sep08) RMA"
- ],
[
"400",
"DBA2J-ONH-1212",
@@ -2500,6 +2500,11 @@
"245",
"UT_ILM_BXD_hipp_RSE_0909",
"UTHSC Hippocampus Illumina v6.1 RSE (Sep09) RankInv"
+ ],
+ [
+ "711",
+ "UTHSC_BXD_AgeHipp0515",
+ "UTHSC BXD Aged Hippocampus Affy MoGene1.0 ST (May15) RMA Gene Level"
]
],
"Hypothalamus mRNA": [
@@ -3028,16 +3033,6 @@
]
],
"Striatum mRNA": [
- [
- "376",
- "DevStriatum_ILM6.2P3RInv_1111",
- "BIDMC/UTHSC Dev Striatum P3 ILMv6.2 (Nov11) RankInv"
- ],
- [
- "377",
- "DevStriatum_ILM6.2P14RInv_1111",
- "BIDMC/UTHSC Dev Striatum P14 ILMv6.2 (Nov11) RankInv"
- ],
[
"399",
"Striatum_Exon_1212",
@@ -3048,6 +3043,16 @@
"Striatum_Exon_0209",
"HQF Striatum Affy Mouse Exon 1.0ST Exon Level (Dec09) RMA"
],
+ [
+ "376",
+ "DevStriatum_ILM6.2P3RInv_1111",
+ "BIDMC/UTHSC Dev Striatum P3 ILMv6.2 (Nov11) RankInv"
+ ],
+ [
+ "377",
+ "DevStriatum_ILM6.2P14RInv_1111",
+ "BIDMC/UTHSC Dev Striatum P14 ILMv6.2 (Nov11) RankInv"
+ ],
[
"285",
"UTHSC_Striatum_RankInv_1210",
@@ -3554,6 +3559,11 @@
]
],
"Hippocampus mRNA": [
+ [
+ "211",
+ "Illum_LXS_Hipp_RSS_1008",
+ "Hippocampus Illumina RSS (Oct08) RankInv beta"
+ ],
[
"213",
"Illum_LXS_Hipp_NOS_1008",
@@ -3574,11 +3584,6 @@
"Illum_LXS_Hipp_NOE_1008",
"Hippocampus Illumina NOE (Oct08) RankInv beta"
],
- [
- "211",
- "Illum_LXS_Hipp_RSS_1008",
- "Hippocampus Illumina RSS (Oct08) RankInv beta"
- ],
[
"143",
"Illum_LXS_Hipp_loess0807",
@@ -3835,7 +3840,7 @@
[
"None",
"HSNIH-RGSMCPublish",
- "HSNIH-RGSMC Phenotypes"
+ "HSNIH Published Phenotypes"
]
]
},
diff --git a/wqflask/wqflask/templates/docs.html b/wqflask/wqflask/templates/docs.html
index c485f757..1a241ce7 100644
--- a/wqflask/wqflask/templates/docs.html
+++ b/wqflask/wqflask/templates/docs.html
@@ -6,9 +6,9 @@
{{title}}
{{content|safe}}
--
cgit v1.2.3
From 4bd1db34b109bff3b9abdcc161472885e9ae700d Mon Sep 17 00:00:00 2001
From: Pjotr Prins
Date: Fri, 6 Sep 2019 03:29:12 -0500
Subject: Fix name of javascript-twitter-post-fetcher
---
wqflask/utility/tools.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
(limited to 'wqflask/utility/tools.py')
diff --git a/wqflask/utility/tools.py b/wqflask/utility/tools.py
index 31ab2046..ec9f01bd 100644
--- a/wqflask/utility/tools.py
+++ b/wqflask/utility/tools.py
@@ -289,11 +289,14 @@ assert_dir(TEMPDIR)
JS_GUIX_PATH = get_setting("JS_GUIX_PATH")
assert_dir(JS_GUIX_PATH)
assert_dir(JS_GUIX_PATH+'/cytoscape-panzoom')
+
CSS_PATH = "UNKNOWN"
# assert_dir(JS_PATH)
-JS_TWITTER_POST_FETCHER_PATH = get_setting("JS_TWITTER_POST_FETCHER_PATH",js_path("Twitter-Post-Fetcher"))
+
+JS_TWITTER_POST_FETCHER_PATH = get_setting("JS_TWITTER_POST_FETCHER_PATH",js_path("javascript-twitter-post-fetcher"))
assert_dir(JS_TWITTER_POST_FETCHER_PATH)
assert_file(JS_TWITTER_POST_FETCHER_PATH+"/js/twitterFetcher_min.js")
+
JS_CYTOSCAPE_PATH = get_setting("JS_CYTOSCAPE_PATH",js_path("cytoscape"))
assert_dir(JS_CYTOSCAPE_PATH)
assert_file(JS_CYTOSCAPE_PATH+'/cytoscape.min.js')
--
cgit v1.2.3
From f9849394e3a252b5a1ac59c78a06728d20ca69ed Mon Sep 17 00:00:00 2001
From: zsloan
Date: Tue, 3 Mar 2020 15:08:48 -0600
Subject: Refactored all the anonymous (not logged-in) user stuff so that both
logged-in and anonymous users share the same code
---
wqflask/utility/tools.py | 3 +-
wqflask/wqflask/static/new/images/CITGLogo.png | Bin 0 -> 11962 bytes
wqflask/wqflask/user_login.py | 467 +++++++++++++++++++++++++
wqflask/wqflask/user_session.py | 272 ++++++++++++++
wqflask/wqflask/views.py | 7 +-
5 files changed, 744 insertions(+), 5 deletions(-)
create mode 100644 wqflask/wqflask/static/new/images/CITGLogo.png
create mode 100644 wqflask/wqflask/user_login.py
create mode 100644 wqflask/wqflask/user_session.py
(limited to 'wqflask/utility/tools.py')
diff --git a/wqflask/utility/tools.py b/wqflask/utility/tools.py
index ec9f01bd..75bddb24 100644
--- a/wqflask/utility/tools.py
+++ b/wqflask/utility/tools.py
@@ -254,7 +254,6 @@ JS_GN_PATH = get_setting('JS_GN_PATH')
GITHUB_CLIENT_ID = get_setting('GITHUB_CLIENT_ID')
GITHUB_CLIENT_SECRET = get_setting('GITHUB_CLIENT_SECRET')
-GITHUB_AUTH_URL = None
if GITHUB_CLIENT_ID != 'UNKNOWN' and GITHUB_CLIENT_SECRET:
GITHUB_AUTH_URL = "https://github.com/login/oauth/authorize?client_id=" + \
GITHUB_CLIENT_ID+"&client_secret="+GITHUB_CLIENT_SECRET
@@ -264,7 +263,7 @@ ORCID_CLIENT_ID = get_setting('ORCID_CLIENT_ID')
ORCID_CLIENT_SECRET = get_setting('ORCID_CLIENT_SECRET')
ORCID_AUTH_URL = None
if ORCID_CLIENT_ID != 'UNKNOWN' and ORCID_CLIENT_SECRET:
- ORCID_AUTH_URL = "https://sandbox.orcid.org/oauth/authorize?response_type=code&scope=/authenticate&show_login=true&client_id=" + \
+ ORCID_AUTH_URL = "https://orcid.org/oauth/authorize?response_type=code&scope=/authenticate&show_login=true&client_id=" + \
ORCID_CLIENT_ID+"&client_secret="+ORCID_CLIENT_SECRET
ORCID_TOKEN_URL = get_setting('ORCID_TOKEN_URL')
diff --git a/wqflask/wqflask/static/new/images/CITGLogo.png b/wqflask/wqflask/static/new/images/CITGLogo.png
new file mode 100644
index 00000000..ae99fedb
Binary files /dev/null and b/wqflask/wqflask/static/new/images/CITGLogo.png differ
diff --git a/wqflask/wqflask/user_login.py b/wqflask/wqflask/user_login.py
new file mode 100644
index 00000000..05885e2c
--- /dev/null
+++ b/wqflask/wqflask/user_login.py
@@ -0,0 +1,467 @@
+from __future__ import print_function, division, absolute_import
+
+import os
+import hashlib
+import datetime
+import time
+import logging
+import uuid
+import hashlib
+import hmac
+import base64
+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)
+
+from wqflask import app
+from wqflask import pbkdf2
+from wqflask.hmac_func import hmac_creation
+from wqflask.user_session import UserSession
+
+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.logger import getLogger
+logger = getLogger(__name__)
+
+from smtplib import SMTP
+from utility.tools import SMTP_CONNECT, SMTP_USERNAME, SMTP_PASSWORD, LOG_SQL_ALCHEMY
+
+THREE_DAYS = 60 * 60 * 24 * 3
+
+def timestamp():
+ return datetime.datetime.utcnow().isoformat()
+
+def basic_info():
+ return dict(timestamp = timestamp(),
+ ip_address = request.remote_addr,
+ user_agent = request.headers.get('User-Agent'))
+
+def encode_password(pass_gen_fields):
+ hashfunc = getattr(hashlib, pass_gen_fields['hashfunc'])
+
+ salt = base64.b64decode(pass_gen_fields['salt'])
+ password = pbkdf2.pbkdf2_hex(str(pass_gen_fields['unencrypted_password']),
+ pass_gen_fields['salt'],
+ pass_gen_fields['iterations'],
+ pass_gen_fields['keylength'],
+ hashfunc)
+
+ return password
+
+def set_password(password):
+ pass_gen_fields = {
+ "unencrypted_password": password,
+ "algorithm": "pbkdf2",
+ "hashfunc": "sha256",
+ "salt": base64.b64encode(os.urandom(32)),
+ "iterations": 100000,
+ "keylength": 32,
+ "created_timestamp": timestamp()
+ }
+
+ assert len(password) >= 6, "Password shouldn't be shorter than 6 characters"
+
+ encoded_password = encode_password(pass_gen_fields)
+
+ return encoded_password
+
+def encrypt_password(unencrypted_password, pwfields):
+ hashfunc = getattr(hashlib, pwfields['hashfunc'])
+ salt = base64.b64decode(pwfields['salt'])
+ iterations = pwfields['iterations']
+ keylength = pwfields['keylength']
+ encrypted_password = pbkdf2.pbkdf2_hex(str(unencrypted_password),
+ salt, iterations, keylength, hashfunc)
+ return encrypted_password
+
+def get_signed_session_id(user):
+ session_id = str(uuid.uuid4())
+
+ session_id_signature = hmac_creation(session_id)
+ session_id_signed = session_id + ":" + session_id_signature
+
+ #ZS: Need to check if this is ever actually used or exists
+ if 'user_id' not in user:
+ user['user_id'] = str(uuid.uuid4())
+ save_user(user, user['user_id'])
+
+ if 'github_id' in user:
+ session = dict(login_time = time.time(),
+ user_type = "github",
+ user_id = user['user_id'],
+ github_id = user['github_id'],
+ user_name = user['name'],
+ user_url = user['user_url'])
+ elif 'orcid' in user:
+ session = dict(login_time = time.time(),
+ user_type = "orcid",
+ user_id = user['user_id'],
+ github_id = user['orcid'],
+ user_name = user['name'],
+ user_url = user['user_url'])
+ else:
+ session = dict(login_time = time.time(),
+ user_type = "gn2",
+ user_id = user['user_id'],
+ user_name = user['full_name'],
+ user_email_address = user['email_address'])
+
+ key = UserSession.user_cookie_name + ":" + session_id
+ Redis.hmset(key, session)
+ Redis.expire(key, THREE_DAYS)
+
+ return session_id_signed
+
+def send_email(toaddr, msg, fromaddr="no-reply@genenetwork.org"):
+ """Send an E-mail through SMTP_CONNECT host. If SMTP_USERNAME is not
+ 'UNKNOWN' TLS is used
+
+ """
+ if SMTP_USERNAME == 'UNKNOWN':
+ server = SMTP(SMTP_CONNECT)
+ server.sendmail(fromaddr, toaddr, msg)
+ else:
+ server = SMTP(SMTP_CONNECT)
+ server.starttls()
+ server.login(SMTP_USERNAME, SMTP_PASSWORD)
+ server.sendmail(fromaddr, toaddr, msg)
+ 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"):
+ verification_code = str(uuid.uuid4())
+ key = key_prefix + ":" + verification_code
+
+ data = json.dumps(dict(id=user_details['user_id'], timestamp = timestamp()))
+
+ Redis.set(key, data)
+ Redis.expire(key, THREE_DAYS)
+
+ recipient = user_details['email_address']
+ body = render_template(template_name, verification_code = verification_code)
+ send_email(recipient, subject, body)
+ return {"recipient": recipient, "subject": subject, "body": body}
+
+@app.route("/n/login", methods=('GET', 'POST'))
+def login():
+ params = request.form if request.form else request.args
+ logger.debug("in login params are:", params)
+
+ if not params: #ZS: If coming to page for first time
+ from utility.tools import GITHUB_AUTH_URL, GITHUB_CLIENT_ID, ORCID_AUTH_URL, ORCID_CLIENT_ID
+ external_login = {}
+ if GITHUB_AUTH_URL and GITHUB_CLIENT_ID != 'UNKNOWN':
+ external_login["github"] = GITHUB_AUTH_URL
+ if ORCID_AUTH_URL and ORCID_CLIENT_ID != 'UNKNOWN':
+ external_login["orcid"] = ORCID_AUTH_URL
+ return render_template("new_security/login_user.html", external_login = external_login, redis_is_available=is_redis_available())
+ else: #ZS: After clicking sign-in
+ if 'type' in params and 'uid' in params:
+ user_details = get_user_by_unique_column("user_id", params['uid'])
+ if user_details:
+ session_id_signed = get_signed_session_id(user_details)
+ if 'name' in user_details and user_details['name'] != "None":
+ display_id = user_details['name']
+ elif 'github_id' in user_details:
+ display_id = user_details['github_id']
+ elif 'orcid' in user_details:
+ display_id = user_details['orcid']
+ else:
+ display_id = ""
+ flash("Thank you for logging in {}.".format(display_id), "alert-success")
+ response = make_response(redirect(url_for('index_page')))
+ response.set_cookie(UserSession.user_cookie_name, session_id_signed, max_age=None)
+ else:
+ flash("Something went unexpectedly wrong.", "alert-danger")
+ response = make_response(redirect(url_for('index_page')))
+ return response
+ else:
+ user_details = get_user_by_unique_column("email_address", params['email_address'])
+ password_match = False
+ if user_details:
+ submitted_password = params['password']
+ pwfields = json.loads(user_details['password'])
+ encrypted_pass = encrypt_password(submitted_password, pwfields)
+ password_match = pbkdf2.safe_str_cmp(encrypted_pass, pwfields['password'])
+ else: # Invalid e-mail
+ flash("Invalid e-mail address. Please try again.", "alert-danger")
+ response = make_response(redirect(url_for('login')))
+
+ return response
+ if password_match: # If password correct
+ if user_details['confirmed']: # If account confirmed
+ import_col = "false"
+ if 'import_collections' in params:
+ import_col = "true"
+
+ 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)))
+ response.set_cookie(UserSession.user_cookie_name, session_id_signed, max_age=None)
+ return response
+ else:
+ email_ob = send_verification_email(user_details)
+ 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
+ flash("Invalid password. Please try again.", "alert-danger")
+ response = make_response(redirect(url_for('login')))
+
+ return response
+
+@app.route("/n/login/github_oauth2", methods=('GET', 'POST'))
+def github_oauth2():
+ from utility.tools import GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET, GITHUB_AUTH_URL
+ code = request.args.get("code")
+ data = {
+ "client_id": GITHUB_CLIENT_ID,
+ "client_secret": GITHUB_CLIENT_SECRET,
+ "code": code
+ }
+ logger.debug("LOGIN DATA:", data)
+ result = requests.post("https://github.com/login/oauth/access_token", json=data)
+ result_dict = {arr[0]:arr[1] for arr in [tok.split("=") for tok in [token.encode("utf-8") for token in result.text.split("&")]]}
+
+ github_user = get_github_user_details(result_dict["access_token"])
+
+ user_details = get_user_by_unique_column("github_id", github_user["id"])
+ if user_details == None:
+ user_details = {
+ "user_id": str(uuid.uuid4()),
+ "name": github_user["name"].encode("utf-8") if github_user["name"] else "None",
+ "github_id": github_user["id"],
+ "user_url": github_user["html_url"].encode("utf-8"),
+ "login_type": "github",
+ "organization": "",
+ "active": 1,
+ "confirmed": 1
+ }
+ save_user(user_details, user_details["user_id"])
+
+ url = "/n/login?type=github&uid="+user_details["user_id"]
+ return redirect(url)
+
+def get_github_user_details(access_token):
+ from utility.tools import GITHUB_API_URL
+ result = requests.get(GITHUB_API_URL, headers = {'Authorization':'token ' + access_token }).content
+
+ return json.loads(result)
+
+@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
+ code = request.args.get("code")
+ error = request.args.get("error")
+ url = "/n/login"
+ if code:
+ data = {
+ "client_id": ORCID_CLIENT_ID,
+ "client_secret": ORCID_CLIENT_SECRET,
+ "grant_type": "authorization_code",
+ "code": code
+ }
+ result = requests.post(ORCID_TOKEN_URL, data=data)
+ result_dict = json.loads(result.text.encode("utf-8"))
+
+ user_details = get_user_by_unique_column("orcid", result_dict["orcid"])
+ if user_details == None:
+ user_details = {
+ "user_id": str(uuid4()),
+ "name": result_dict["name"],
+ "orcid": result_dict["orcid"],
+ "user_url": "%s/%s" % ("/".join(ORCID_AUTH_URL.split("/")[:-2]), result_dict["orcid"]),
+ "login_type": "orcid",
+ "organization": "",
+ "active": 1,
+ "confirmed": 1
+ }
+ save_user(user_details, user_details["user_id"])
+
+ url = "/n/login?type=orcid&uid="+user_details["user_id"]
+ else:
+ flash("There was an error getting code from ORCID")
+ return redirect(url)
+
+def get_github_user_details(access_token):
+ from utility.tools import GITHUB_API_URL
+ result = requests.get(GITHUB_API_URL, headers = {'Authorization':'token ' + access_token }).content
+
+ return json.loads(result)
+
+
+@app.route("/n/logout")
+def logout():
+ logger.debug("Logging out...")
+ UserSession().delete_session()
+ flash("You are now logged out. We hope you come back soon!")
+ response = make_response(redirect(url_for('index_page')))
+ # Delete the cookie
+ response.set_cookie(UserSession.user_cookie_name, '', expires=0)
+ return response
+
+@app.route("/n/forgot_password", methods=['GET'])
+def forgot_password():
+ """Entry point for forgotten password"""
+ print("ARGS: ", request.args)
+ errors = {"no-email": request.args.get("no-email")}
+ print("ERRORS: ", errors)
+ return render_template("new_security/forgot_password.html", errors=errors)
+
+def send_forgot_password_email(verification_email):
+ from email.MIMEMultipart import MIMEMultipart
+ from email.MIMEText import MIMEText
+
+ template_name = "email/forgot_password.txt"
+ key_prefix = "forgot_password_code"
+ subject = "GeneNetwork password reset"
+ fromaddr = "no-reply@genenetwork.org"
+
+ verification_code = str(uuid.uuid4())
+ key = key_prefix + ":" + verification_code
+
+ data = {
+ "verification_code": verification_code,
+ "email_address": verification_email,
+ "timestamp": timestamp()
+ }
+
+ save_verification_code(verification_email, verification_code)
+
+ body = render_template(template_name, verification_code = verification_code)
+
+ msg = MIMEMultipart()
+ msg["To"] = verification_email
+ msg["Subject"] = subject
+ msg["From"] = fromaddr
+ msg.attach(MIMEText(body, "plain"))
+
+ send_email(verification_email, msg.as_string())
+
+ return subject
+
+@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
+ email_address = params['email_address']
+ next_page = None
+ if email_address != "":
+ logger.debug("Wants to send password E-mail to ", email_address)
+ user_details = get_user_by_unique_column("email_address", email_address)
+ if user_details:
+ email_subject = send_forgot_password_email(user_details["email_address"])
+ return render_template("new_security/forgot_password_step2.html",
+ subject=email_subject)
+ else:
+ flash("The e-mail entered is not associated with an account.", "alert-danger")
+ return redirect(url_for("forgot_password"))
+
+ else:
+ flash("You MUST provide an email", "alert-danger")
+ return redirect(url_for("forgot_password"))
+
+@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)
+
+ verification_code = request.args.get('code')
+ 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")
+ else:
+ flash("Invalid code: Password reset code does not exist or might have expired!", "error")
+ else:
+ return redirect(url_for("login"))
+
+@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)
+
+ errors = []
+ user_email = request.form['user_encode']
+
+ password = request.form['password']
+ encoded_password = set_password(password)
+
+ 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
+
+def register_user(params):
+ thank_you_mode = False
+ errors = []
+ user_details = {}
+
+ user_details['email_address'] = params.get('email_address', '').encode("utf-8").strip()
+ if not (5 <= len(user_details['email_address']) <= 50):
+ errors.append('Email Address needs to be between 5 and 50 characters.')
+ else:
+ email_exists = get_user_by_unique_column("email_address", user_details['email_address'])
+ if email_exists:
+ errors.append('User already exists with that email')
+
+ user_details['full_name'] = params.get('full_name', '').encode("utf-8").strip()
+ if not (5 <= len(user_details['full_name']) <= 50):
+ errors.append('Full Name needs to be between 5 and 50 characters.')
+
+ user_details['organization'] = params.get('organization', '').encode("utf-8").strip()
+ if user_details['organization'] and not (5 <= len(user_details['organization']) <= 50):
+ errors.append('Organization needs to be empty or between 5 and 50 characters.')
+
+ password = str(params.get('password', ''))
+ if not (6 <= len(password)):
+ errors.append('Password needs to be at least 6 characters.')
+
+ if params.get('password_confirm') != password:
+ errors.append("Passwords don't match.")
+
+ if errors:
+ return errors
+
+ user_details['password'] = set_password(password)
+ user_details['user_id'] = str(uuid.uuid4())
+ user_details['confirmed'] = 1
+
+ user_details['registration_info'] = json.dumps(basic_info(), sort_keys=True)
+ save_user(user_details, user_details['user_id'])
+
+@app.route("/n/register", methods=('GET', 'POST'))
+def register():
+ errors = None
+
+ params = request.form if request.form else request.args
+ params = params.to_dict(flat=True)
+
+ if params:
+ logger.debug("Attempting to register the user...")
+ errors = register_user(params)
+
+ if len(errors) == 0:
+ flash("Registration successful. You may login with your new account", "alert-info")
+ return redirect(url_for("login"))
+
+ return render_template("new_security/register_user.html", values=params, errors=errors)
+
+@app.errorhandler(401)
+def unauthorized(error):
+ return redirect(url_for('login'))
\ No newline at end of file
diff --git a/wqflask/wqflask/user_session.py b/wqflask/wqflask/user_session.py
new file mode 100644
index 00000000..1f3e6558
--- /dev/null
+++ b/wqflask/wqflask/user_session.py
@@ -0,0 +1,272 @@
+from __future__ import print_function, division, absolute_import
+
+import datetime
+import time
+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)
+
+from wqflask import app
+from wqflask.hmac_func import hmac_creation
+
+#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.logger import getLogger
+logger = getLogger(__name__)
+
+THREE_DAYS = 60 * 60 * 24 * 3
+THIRTY_DAYS = 60 * 60 * 24 * 30
+
+def verify_cookie(cookie):
+ the_uuid, separator, the_signature = cookie.partition(':')
+ assert len(the_uuid) == 36, "Is session_id a uuid?"
+ assert separator == ":", "Expected a : here"
+ assert the_signature == hmac_creation(the_uuid), "Uh-oh, someone tampering with the cookie?"
+ return the_uuid
+
+def create_signed_cookie():
+ the_uuid = str(uuid.uuid4())
+ signature = hmac_creation(the_uuid)
+ uuid_signed = the_uuid + ":" + signature
+ logger.debug("uuid_signed:", uuid_signed)
+ return the_uuid, uuid_signed
+
+class UserSession(object):
+ """Logged in user handling"""
+
+ user_cookie_name = 'session_id_v1'
+ anon_cookie_name = 'anon_user_v1'
+
+ def __init__(self):
+ user_cookie = request.cookies.get(self.user_cookie_name)
+ if not user_cookie:
+ self.logged_in = False
+ anon_cookie = request.cookies.get(self.anon_cookie_name)
+ self.cookie_name = self.anon_cookie_name
+ if anon_cookie:
+ self.cookie = anon_cookie
+ session_id = verify_cookie(self.cookie)
+ else:
+ session_id, self.cookie = create_signed_cookie()
+ else:
+ self.cookie_name = self.user_cookie_name
+ self.cookie = user_cookie
+ session_id = verify_cookie(self.cookie)
+
+ self.redis_key = self.cookie_name + ":" + session_id
+ self.session_id = session_id
+ self.record = Redis.hgetall(self.redis_key)
+
+ #ZS: If user correctled logged in but their session expired
+ #ZS: Need to test this by setting the time-out to be really short or something
+ if not self.record:
+ if user_cookie:
+ self.logged_in = False
+
+ ########### Grrr...this won't work because of the way flask handles cookies
+ # Delete the cookie
+ response = make_response(redirect(url_for('login')))
+ #response.set_cookie(self.cookie_name, '', expires=0)
+ flash("Due to inactivity your session has expired. If you'd like please login again.")
+ return response
+ #return
+ else:
+ self.record = dict(login_time = time.time(),
+ user_type = "anon",
+ user_id = str(uuid.uuid4()))
+
+ Redis.hmset(self.redis_key, self.record)
+ Redis.expire(self.redis_key, THIRTY_DAYS)
+ else:
+ if user_cookie:
+ self.logged_in = True
+
+ if user_cookie:
+ session_time = THREE_DAYS
+ else:
+ session_time = THIRTY_DAYS
+
+ if Redis.ttl(self.redis_key) < session_time:
+ # (Almost) everytime the user does something we extend the session_id in Redis...
+ logger.debug("Extending ttl...")
+ Redis.expire(self.redis_key, session_time)
+
+ @property
+ def user_id(self):
+ """Shortcut to the user_id"""
+ if 'user_id' in self.record:
+ return self.record['user_id']
+ else:
+ return ''
+
+ @property
+ def redis_user_id(self):
+ """User id from Redis (need to check if this is the same as the id stored in self.records)"""
+
+ #ZS: This part is a bit weird. Some accounts used to not have saved user ids, and in the process of testing I think I created some duplicate accounts for myself.
+ #ZS: Accounts should automatically generate user_ids if they don't already have one now, so this might not be necessary for anything other than my account's collections
+
+ if 'user_email_address' in self.record:
+ user_email = self.record['user_email_address']
+
+ #ZS: Get user's collections if they exist
+ user_id = None
+ user_id = get_user_id("email_address", user_email)
+ elif 'user_id' in self.record:
+ user_id = self.record['user_id']
+ elif 'github_id' in self.record:
+ user_github_id = self.record['github_id']
+ user_id = None
+ user_id = get_user_id("github_id", user_github_id)
+ else: #ZS: Anonymous user
+ return None
+
+ return user_id
+
+ @property
+ def user_name(self):
+ """Shortcut to the user_name"""
+ if 'user_name' in self.record:
+ return self.record['user_name']
+ else:
+ return ''
+
+ @property
+ def user_collections(self):
+ """List of user's collections"""
+
+ #ZS: Get user's collections if they exist
+ collections = get_user_collections(self.redis_user_id)
+ return collections
+
+ @property
+ def num_collections(self):
+ """Number of user's collections"""
+
+ return len(self.user_collections)
+
+ def add_collection(self, collection_name, traits):
+ """Add collection into ElasticSearch"""
+
+ collection_dict = {'id': unicode(uuid.uuid4()),
+ 'name': collection_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'),
+ 'num_members': len(traits),
+ 'members': list(traits) }
+
+ current_collections = self.user_collections
+ current_collections.append(collection_dict)
+ self.update_collections(current_collections)
+
+ return collection_dict['id']
+
+ def change_collection_name(self, collection_id, new_name):
+ for collection in self.user_collections:
+ if collection['id'] == collection_id:
+ collection['name'] = new_name
+ break
+
+ return new_name
+
+ def delete_collection(self, collection_id):
+ """Remove collection with given ID"""
+
+ updated_collections = []
+ for collection in self.user_collections:
+ if collection['id'] == collection_id:
+ continue
+ else:
+ updated_collections.append(collection)
+
+ self.update_collections(updated_collections)
+
+ return collection['name']
+
+ def add_traits_to_collection(self, collection_id, traits_to_add):
+ """Add specified traits to a collection"""
+
+ this_collection = self.get_collection_by_id(collection_id)
+
+ updated_collection = this_collection
+ updated_traits = this_collection['members'] + traits_to_add
+
+ updated_collection['members'] = updated_traits
+ updated_collection['num_members'] = len(updated_traits)
+ updated_collection['changed_timestamp'] = datetime.datetime.utcnow().strftime('%b %d %Y %I:%M%p')
+
+ updated_collections = []
+ for collection in self.user_collections:
+ if collection['id'] == collection_id:
+ updated_collections.append(updated_collection)
+ else:
+ updated_collections.append(collection)
+
+ self.update_collections(updated_collections)
+
+ def remove_traits_from_collection(self, collection_id, traits_to_remove):
+ """Remove specified traits from a collection"""
+
+ this_collection = self.get_collection_by_id(collection_id)
+
+ updated_collection = this_collection
+ updated_traits = []
+ for trait in this_collection['members']:
+ if trait in traits_to_remove:
+ continue
+ else:
+ updated_traits.append(trait)
+
+ updated_collection['members'] = updated_traits
+ updated_collection['num_members'] = len(updated_traits)
+ updated_collection['changed_timestamp'] = datetime.datetime.utcnow().strftime('%b %d %Y %I:%M%p')
+
+ updated_collections = []
+ for collection in self.user_collections:
+ if collection['id'] == collection_id:
+ updated_collections.append(updated_collection)
+ else:
+ updated_collections.append(collection)
+
+ self.update_collections(updated_collections)
+
+ return updated_traits
+
+ def get_collection_by_id(self, collection_id):
+ for collection in self.user_collections:
+ if collection['id'] == collection_id:
+ return collection
+
+ def get_collection_by_name(self, collection_name):
+ for collection in self.user_collections:
+ if collection['name'] == collection_name:
+ return collection
+
+ return None
+
+ def update_collections(self, updated_collections):
+ collection_body = json.dumps(updated_collections)
+
+ save_collections(self.redis_user_id, collection_body)
+
+ def delete_session(self):
+ # And more importantly delete the redis record
+ Redis.delete(self.redis_key)
+ self.logged_in = False
+
+@app.before_request
+def before_request():
+ g.user_session = UserSession()
+
+@app.after_request
+def set_cookie(response):
+ if not request.cookies.get(g.user_session.cookie_name):
+ response.set_cookie(g.user_session.cookie_name, g.user_session.cookie)
+ return response
\ No newline at end of file
diff --git a/wqflask/wqflask/views.py b/wqflask/wqflask/views.py
index a84a7480..436ebc91 100644
--- a/wqflask/wqflask/views.py
+++ b/wqflask/wqflask/views.py
@@ -65,9 +65,8 @@ from utility.benchmark import Bench
from pprint import pformat as pf
-#from wqflask import user_login
-#from wqflask import user_session
-from wqflask import user_manager
+from wqflask import user_login
+from wqflask import user_session
from wqflask import collect
from wqflask.database import db_session
@@ -839,6 +838,8 @@ def browser_inputs():
return flask.jsonify(file_contents)
+
+
##########################################################################
def json_default_handler(obj):
--
cgit v1.2.3
From 6178a48d29cd83fd3beb70854721070826e230e3 Mon Sep 17 00:00:00 2001
From: zsloan
Date: Wed, 22 Apr 2020 16:03:07 -0500
Subject: Fixed a variety of issues related to users registering and logging in
GN2 should now work when Redis is empty
---
wqflask/utility/startup_config.py | 4 +-
wqflask/utility/tools.py | 8 +--
wqflask/wqflask/collect.py | 2 +-
wqflask/wqflask/show_trait/SampleList.py | 84 ++++++++++++++++----------------
wqflask/wqflask/show_trait/show_trait.py | 4 +-
wqflask/wqflask/user_login.py | 44 ++++++++---------
wqflask/wqflask/user_session.py | 13 +++--
7 files changed, 80 insertions(+), 79 deletions(-)
(limited to 'wqflask/utility/tools.py')
diff --git a/wqflask/utility/startup_config.py b/wqflask/utility/startup_config.py
index 5a62cc50..817284dd 100644
--- a/wqflask/utility/startup_config.py
+++ b/wqflask/utility/startup_config.py
@@ -33,7 +33,7 @@ def app_config():
if page.status_code != 200:
raise Exception("API server not found!")
- import utility.elasticsearch_tools as es
- es.test_elasticsearch_connection()
+ # import utility.elasticsearch_tools as es
+ # es.test_elasticsearch_connection()
print("GN2 is running. Visit %s[http://localhost:%s/%s](%s)" % (BLUE,str(port),ENDC,get_setting("WEBSERVER_URL")))
diff --git a/wqflask/utility/tools.py b/wqflask/utility/tools.py
index 75bddb24..0fbedccb 100644
--- a/wqflask/utility/tools.py
+++ b/wqflask/utility/tools.py
@@ -267,10 +267,10 @@ if ORCID_CLIENT_ID != 'UNKNOWN' and ORCID_CLIENT_SECRET:
ORCID_CLIENT_ID+"&client_secret="+ORCID_CLIENT_SECRET
ORCID_TOKEN_URL = get_setting('ORCID_TOKEN_URL')
-ELASTICSEARCH_HOST = get_setting('ELASTICSEARCH_HOST')
-ELASTICSEARCH_PORT = get_setting('ELASTICSEARCH_PORT')
-import utility.elasticsearch_tools as es
-es.test_elasticsearch_connection()
+# ELASTICSEARCH_HOST = get_setting('ELASTICSEARCH_HOST')
+# ELASTICSEARCH_PORT = get_setting('ELASTICSEARCH_PORT')
+# import utility.elasticsearch_tools as es
+# es.test_elasticsearch_connection()
SMTP_CONNECT = get_setting('SMTP_CONNECT')
SMTP_USERNAME = get_setting('SMTP_USERNAME')
diff --git a/wqflask/wqflask/collect.py b/wqflask/wqflask/collect.py
index fa6e03b4..1d74b699 100644
--- a/wqflask/wqflask/collect.py
+++ b/wqflask/wqflask/collect.py
@@ -108,7 +108,7 @@ def collections_new():
if 'existing_collection' not in params:
collections = g.user_session.user_collections
for collection in collections:
- if collection["name"] == "Default Collection":
+ if collection["name"] == "Your Default Collection":
collection_id = collection["id"]
collection_name = collection["name"]
default_collection_exists = True
diff --git a/wqflask/wqflask/show_trait/SampleList.py b/wqflask/wqflask/show_trait/SampleList.py
index 7e126a36..ad78ebcc 100644
--- a/wqflask/wqflask/show_trait/SampleList.py
+++ b/wqflask/wqflask/show_trait/SampleList.py
@@ -14,8 +14,6 @@ import simplejson as json
import itertools
-from utility.elasticsearch_tools import get_elasticsearch_connection
-
import utility.logger
logger = utility.logger.getLogger(__name__ )
@@ -158,47 +156,47 @@ class SampleList(object):
return any(sample.variance for sample in self.sample_list)
-def get_transform_vals(dataset, trait):
- es = get_elasticsearch_connection(for_user=False)
-
- logger.info("DATASET NAME:", dataset.name)
-
- query = '{"bool": {"must": [{"match": {"name": "%s"}}, {"match": {"dataset": "%s"}}]}}' % (trait.name, dataset.name)
-
- es_body = {
- "query": {
- "bool": {
- "must": [
- {
- "match": {
- "name": "%s" % (trait.name)
- }
- },
- {
- "match": {
- "dataset": "%s" % (dataset.name)
- }
- }
- ]
- }
- }
- }
-
- response = es.search( index = "traits", doc_type = "trait", body = es_body )
- logger.info("THE RESPONSE:", response)
- results = response['hits']['hits']
-
- if len(results) > 0:
- samples = results[0]['_source']['samples']
-
- sample_dict = {}
- for sample in samples:
- sample_dict[sample['name']] = sample['qnorm']
-
- #logger.info("SAMPLE DICT:", sample_dict)
- return sample_dict
- else:
- return None
+# def get_transform_vals(dataset, trait):
+# es = get_elasticsearch_connection(for_user=False)
+
+# logger.info("DATASET NAME:", dataset.name)
+
+# query = '{"bool": {"must": [{"match": {"name": "%s"}}, {"match": {"dataset": "%s"}}]}}' % (trait.name, dataset.name)
+
+# es_body = {
+# "query": {
+# "bool": {
+# "must": [
+# {
+# "match": {
+# "name": "%s" % (trait.name)
+# }
+# },
+# {
+# "match": {
+# "dataset": "%s" % (dataset.name)
+# }
+# }
+# ]
+# }
+# }
+# }
+
+# response = es.search( index = "traits", doc_type = "trait", body = es_body )
+# logger.info("THE RESPONSE:", response)
+# results = response['hits']['hits']
+
+# if len(results) > 0:
+# samples = results[0]['_source']['samples']
+
+# sample_dict = {}
+# for sample in samples:
+# sample_dict[sample['name']] = sample['qnorm']
+
+# #logger.info("SAMPLE DICT:", sample_dict)
+# return sample_dict
+# else:
+# return None
def natural_sort_key(x):
"""Get expected results when using as a key for sort - ints or strings are sorted properly"""
diff --git a/wqflask/wqflask/show_trait/show_trait.py b/wqflask/wqflask/show_trait/show_trait.py
index d35ba749..8883e627 100644
--- a/wqflask/wqflask/show_trait/show_trait.py
+++ b/wqflask/wqflask/show_trait/show_trait.py
@@ -364,8 +364,8 @@ class ShowTrait(object):
if self.dataset:
dataset_menu_selected = self.dataset.name
- return_results_menu = (100, 200, 500, 1000, 2000, 5000, 10000, 15000, 20000)
- return_results_menu_selected = 500
+ return_results_menu = (100, 200, 500, 1000, 2000, 5000, 10000, 15000, 20000)
+ return_results_menu_selected = 500
self.corr_tools = dict(dataset_menu = dataset_menu,
dataset_menu_selected = dataset_menu_selected,
diff --git a/wqflask/wqflask/user_login.py b/wqflask/wqflask/user_login.py
index da3cc504..40d9925c 100644
--- a/wqflask/wqflask/user_login.py
+++ b/wqflask/wqflask/user_login.py
@@ -6,7 +6,6 @@ import datetime
import time
import logging
import uuid
-import hashlib
import hmac
import base64
import requests
@@ -42,17 +41,23 @@ def basic_info():
ip_address = request.remote_addr,
user_agent = request.headers.get('User-Agent'))
-def encode_password(pass_gen_fields):
+def encode_password(pass_gen_fields, unencrypted_password):
+ logger.debug("THE TYPE:", type(pass_gen_fields))
+ logger.debug("pass_gen_fields:", pass_gen_fields)
+ logger.debug("hashfunc:", pass_gen_fields['hashfunc'])
hashfunc = getattr(hashlib, pass_gen_fields['hashfunc'])
salt = base64.b64decode(pass_gen_fields['salt'])
- password = pbkdf2.pbkdf2_hex(str(pass_gen_fields['unencrypted_password']),
+ encrypted_password = pbkdf2.pbkdf2_hex(str(unencrypted_password),
pass_gen_fields['salt'],
pass_gen_fields['iterations'],
pass_gen_fields['keylength'],
hashfunc)
- return password
+ pass_gen_fields.pop("unencrypted_password", None)
+ pass_gen_fields["password"] = encrypted_password
+
+ return pass_gen_fields
def set_password(password):
pass_gen_fields = {
@@ -67,19 +72,10 @@ def set_password(password):
assert len(password) >= 6, "Password shouldn't be shorter than 6 characters"
- encoded_password = encode_password(pass_gen_fields)
+ encoded_password = encode_password(pass_gen_fields, pass_gen_fields['unencrypted_password'])
return encoded_password
-def encrypt_password(unencrypted_password, pwfields):
- hashfunc = getattr(hashlib, pwfields['hashfunc'])
- salt = base64.b64decode(pwfields['salt'])
- iterations = pwfields['iterations']
- keylength = pwfields['keylength']
- encrypted_password = pbkdf2.pbkdf2_hex(str(unencrypted_password),
- salt, iterations, keylength, hashfunc)
- return encrypted_password
-
def get_signed_session_id(user):
session_id = str(uuid.uuid4())
@@ -186,9 +182,12 @@ def login():
password_match = False
if user_details:
submitted_password = params['password']
- pwfields = json.loads(user_details['password'])
- encrypted_pass = encrypt_password(submitted_password, pwfields)
- password_match = pbkdf2.safe_str_cmp(encrypted_pass, pwfields['password'])
+ pwfields = user_details['password']
+ if type(pwfields) is str:
+ pwfields = json.loads(pwfields)
+ encrypted_pass_fields = encode_password(pwfields, submitted_password)
+ password_match = pbkdf2.safe_str_cmp(encrypted_pass_fields['password'], pwfields['password'])
+
else: # Invalid e-mail
flash("Invalid e-mail address. Please try again.", "alert-danger")
response = make_response(redirect(url_for('login')))
@@ -226,7 +225,7 @@ def github_oauth2():
"client_secret": GITHUB_CLIENT_SECRET,
"code": code
}
- logger.debug("LOGIN DATA:", data)
+
result = requests.post("https://github.com/login/oauth/access_token", json=data)
result_dict = {arr[0]:arr[1] for arr in [tok.split("=") for tok in [token.encode("utf-8") for token in result.text.split("&")]]}
@@ -437,19 +436,18 @@ def register_user(params):
if params.get('password_confirm') != password:
errors.append("Passwords don't match.")
- if errors:
- return errors
-
user_details['password'] = set_password(password)
user_details['user_id'] = str(uuid.uuid4())
user_details['confirmed'] = 1
- user_details['registration_info'] = json.dumps(basic_info(), sort_keys=True)
+ user_details['registration_info'] = basic_info()
save_user(user_details, user_details['user_id'])
+ return errors
+
@app.route("/n/register", methods=('GET', 'POST'))
def register():
- errors = None
+ errors = []
params = request.form if request.form else request.args
params = params.to_dict(flat=True)
diff --git a/wqflask/wqflask/user_session.py b/wqflask/wqflask/user_session.py
index d75a03df..fd1779fb 100644
--- a/wqflask/wqflask/user_session.py
+++ b/wqflask/wqflask/user_session.py
@@ -9,6 +9,7 @@ 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)
@@ -69,19 +70,23 @@ class UserSession(object):
if not self.record or self.record == []:
if user_cookie:
self.logged_in = False
+ self.record = dict(login_time = time.time(),
+ user_type = "anon",
+ 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
- response = make_response(redirect(url_for('login')))
- #response.set_cookie(self.cookie_name, '', expires=0)
flash("Due to inactivity your session has expired. If you'd like please login again.")
- #return response
+ return response
#return
else:
self.record = dict(login_time = time.time(),
user_type = "anon",
user_id = str(uuid.uuid4()))
-
Redis.hmset(self.redis_key, self.record)
Redis.expire(self.redis_key, THIRTY_DAYS)
else:
--
cgit v1.2.3
From be09d207ee4e2705e358102f8bdbcd1da7f70ca2 Mon Sep 17 00:00:00 2001
From: zsloan
Date: Fri, 24 Apr 2020 16:06:36 -0500
Subject: Replaced hard-coded instances of GN2 urls with ones pulled from
settings file
---
doc/API_readme.md | 42 +++++++++++-----------
wqflask/base/data_set.py | 4 +--
wqflask/base/trait.py | 7 ++--
wqflask/utility/tools.py | 2 ++
wqflask/wqflask/ctl/ctl_analysis.py | 4 ++-
wqflask/wqflask/do_search.py | 3 +-
wqflask/wqflask/network_graph/network_graph.py | 2 ++
wqflask/wqflask/search_results.py | 3 +-
wqflask/wqflask/static/new/javascript/ctl_graph.js | 8 +----
.../new/javascript/dataset_select_menu_orig.js | 1 -
.../wqflask/static/new/javascript/network_graph.js | 4 +--
wqflask/wqflask/templates/ctl_results.html | 1 +
wqflask/wqflask/templates/index_page.html | 4 +--
wqflask/wqflask/templates/index_page_orig.html | 4 +--
wqflask/wqflask/templates/network_graph.html | 1 +
15 files changed, 47 insertions(+), 43 deletions(-)
(limited to 'wqflask/utility/tools.py')
diff --git a/doc/API_readme.md b/doc/API_readme.md
index 652376a0..be6668dc 100644
--- a/doc/API_readme.md
+++ b/doc/API_readme.md
@@ -6,17 +6,17 @@
To get a list of species with data available in GN (and their associated names and ids):
```
-curl http://gn2.genenetwork.org/api/v_pre1/species
+curl http://genenetwork.org/api/v_pre1/species
[ { "FullName": "Mus musculus", "Id": 1, "Name": "mouse", "TaxonomyId": 10090 }, ... { "FullName": "Populus trichocarpa", "Id": 10, "Name": "poplar", "TaxonomyId": 3689 } ]
```
Or to get a single species info:
```
-curl http://gn2.genenetwork.org/api/v_pre1/species/mouse
+curl http://genenetwork.org/api/v_pre1/species/mouse
```
OR
```
-curl http://gn2.genenetwork.org/api/v_pre1/species/mouse.json
+curl http://genenetwork.org/api/v_pre1/species/mouse.json
```
*For all queries where the last field is a user-specified name/ID, there will be the option to append a file format type. Currently there is only JSON (and it will default to JSON if none is provided), but other formats will be added later*
@@ -26,33 +26,33 @@ curl http://gn2.genenetwork.org/api/v_pre1/species/mouse.json
This query can optionally filter by species:
```
-curl http://gn2.genenetwork.org/api/v_pre1/groups (for all species)
+curl http://genenetwork.org/api/v_pre1/groups (for all species)
```
OR
```
-curl http://gn2.genenetwork.org/api/v_pre1/groups/mouse (for just mouse groups/RISets)
+curl http://genenetwork.org/api/v_pre1/groups/mouse (for just mouse groups/RISets)
[ { "DisplayName": "BXD", "FullName": "BXD RI Family", "GeneticType": "riset", "Id": 1, "MappingMethodId": "1", "Name": "BXD", "SpeciesId": 1, "public": 2 }, ... { "DisplayName": "AIL LGSM F34 and F39-43 (GBS)", "FullName": "AIL LGSM F34 and F39-43 (GBS)", "GeneticType": "intercross", "Id": 72, "MappingMethodId": "2", "Name": "AIL-LGSM-F34-F39-43-GBS", "SpeciesId": 1, "public": 2 } ]
```
## Fetch Genotypes for Group/RISet ##
```
-curl http://gn2.genenetwork.org/api/v_pre1/genotypes/bimbam/BXD
-curl http://gn2.genenetwork.org/api/v_pre1/genotypes/BXD.bimbam
+curl http://genenetwork.org/api/v_pre1/genotypes/bimbam/BXD
+curl http://genenetwork.org/api/v_pre1/genotypes/BXD.bimbam
```
Returns a group's genotypes in one of several formats - bimbam, rqtl2, or geno (a format used by qtlreaper which is just a CSV file consisting of marker positions and genotypes)
Rqtl2 genotype queries can also include the dataset name and will return a zip of the genotypes, phenotypes, and gene map (marker names/positions). For example:
```
-curl http://gn2.genenetwork.org/api/v_pre1/genotypes/rqtl2/BXD/HC_M2_0606_P.zip
+curl http://genenetwork.org/api/v_pre1/genotypes/rqtl2/BXD/HC_M2_0606_P.zip
```
## Fetch Datasets ##
```
-curl http://gn2.genenetwork.org/api/v_pre1/datasets/bxd
+curl http://genenetwork.org/api/v_pre1/datasets/bxd
```
OR
```
-curl http://gn2.genenetwork.org/api/v_pre1/datasets/mouse/bxd
+curl http://genenetwork.org/api/v_pre1/datasets/mouse/bxd
[ { "AvgID": 1, "CreateTime": "Fri, 01 Aug 2003 00:00:00 GMT", "DataScale": "log2", "FullName": "UTHSC/ETHZ/EPFL BXD Liver Polar Metabolites Extraction A, CD Cohorts (Mar 2017) log2", "Id": 1, "Long_Abbreviation": "BXDMicroArray_ProbeSet_August03", "ProbeFreezeId": 3, "ShortName": "Brain U74Av2 08/03 MAS5", "Short_Abbreviation": "Br_U_0803_M", "confidentiality": 0, "public": 0 }, ... { "AvgID": 3, "CreateTime": "Tue, 14 Aug 2018 00:00:00 GMT", "DataScale": "log2", "FullName": "EPFL/LISP BXD CD Liver Affy Mouse Gene 1.0 ST (Aug18) RMA", "Id": 859, "Long_Abbreviation": "EPFLMouseLiverCDRMAApr18", "ProbeFreezeId": 181, "ShortName": "EPFL/LISP BXD CD Liver Affy Mouse Gene 1.0 ST (Aug18) RMA", "Short_Abbreviation": "EPFLMouseLiverCDRMA0818", "confidentiality": 0, "public": 1 } ]
```
(I added the option to specify species just in case we end up with the same group name across multiple species at some point, though it's currently unnecessary)
@@ -61,11 +61,11 @@ curl http://gn2.genenetwork.org/api/v_pre1/datasets/mouse/bxd
### For mRNA Assay/"ProbeSet" ###
```
-curl http://gn2.genenetwork.org/api/v_pre1/dataset/HC_M2_0606_P
+curl http://genenetwork.org/api/v_pre1/dataset/HC_M2_0606_P
```
OR
```
-curl http://gn2.genenetwork.org/api/v_pre1/dataset/bxd/HC_M2_0606_P```
+curl http://genenetwork.org/api/v_pre1/dataset/bxd/HC_M2_0606_P```
{ "confidential": 0, "data_scale": "log2", "dataset_type": "mRNA expression", "full_name": "Hippocampus Consortium M430v2 (Jun06) PDNN", "id": 112, "name": "HC_M2_0606_P", "public": 2, "short_name": "Hippocampus M430v2 BXD 06/06 PDNN", "tissue": "Hippocampus mRNA", "tissue_id": 9 }
```
(This also has the option to specify group/riset)
@@ -73,26 +73,26 @@ curl http://gn2.genenetwork.org/api/v_pre1/dataset/bxd/HC_M2_0606_P```
### For "Phenotypes" (basically non-mRNA Expression; stuff like weight, sex, etc) ###
For these traits, the query fetches publication info and takes the group and phenotype 'ID' as input. For example:
```
-curl http://gn2.genenetwork.org/api/v_pre1/dataset/bxd/10001
+curl http://genenetwork.org/api/v_pre1/dataset/bxd/10001
{ "dataset_type": "phenotype", "description": "Central nervous system, morphology: Cerebellum weight, whole, bilateral in adults of both sexes [mg]", "id": 10001, "name": "CBLWT2", "pubmed_id": 11438585, "title": "Genetic control of the mouse cerebellum: identification of quantitative trait loci modulating size and architecture", "year": "2001" }
```
## Fetch Sample Data for Dataset ##
```
-curl http://gn2.genenetwork.org/api/v_pre1/sample_data/HSNIH-PalmerPublish.csv
+curl http://genenetwork.org/api/v_pre1/sample_data/HSNIH-PalmerPublish.csv
```
Returns a CSV file with sample/strain names as the columns and trait IDs as rows
## Fetch Sample Data for Single Trait ##
```
-curl http://gn2.genenetwork.org/api/v_pre1/sample_data/HC_M2_0606_P/1436869_at
+curl http://genenetwork.org/api/v_pre1/sample_data/HC_M2_0606_P/1436869_at
[ { "data_id": 23415463, "sample_name": "129S1/SvImJ", "sample_name_2": "129S1/SvImJ", "se": 0.123, "value": 8.201 }, { "data_id": 23415463, "sample_name": "A/J", "sample_name_2": "A/J", "se": 0.046, "value": 8.413 }, { "data_id": 23415463, "sample_name": "AKR/J", "sample_name_2": "AKR/J", "se": 0.134, "value": 8.856 }, ... ]
```
## Fetch Trait List for Dataset ##
```
-curl http://gn2.genenetwork.org/api/v_pre1/traits/HXBBXHPublish.json
+curl http://genenetwork.org/api/v_pre1/traits/HXBBXHPublish.json
[ { "Additive": 0.0499967532467532, "Id": 10001, "LRS": 16.2831307029479, "Locus": "rs106114574", "PhenotypeId": 1449, "PublicationId": 319, "Sequence": 1 }, ... ]
```
@@ -101,7 +101,7 @@ Both JSON and CSV formats can be specified, with JSON as default. There is also
## Fetch Trait Info (Name, Description, Location, etc) ##
### For mRNA Expression/"ProbeSet" ###
```
-curl http://gn2.genenetwork.org/api/v_pre1/trait/HC_M2_0606_P/1436869_at
+curl http://genenetwork.org/api/v_pre1/trait/HC_M2_0606_P/1436869_at
{ "additive": -0.214087568058076, "alias": "HHG1; HLP3; HPE3; SMMCI; Dsh; Hhg1", "chr": "5", "description": "sonic hedgehog (hedgehog)", "id": 99602, "locus": "rs8253327", "lrs": 12.7711275309832, "mb": 28.457155, "mean": 9.27909090909091, "name": "1436869_at", "p_value": 0.306, "se": null, "symbol": "Shh" }
```
@@ -110,7 +110,7 @@ For phenotypes this just gets the max LRS, its location, and additive effect (a
Since each group/riset only has one phenotype "dataset", this query takes either the group/riset name or the group/riset name + "Publish" (for example "BXDPublish", which is the dataset name in the DB) as input
```
-curl http://gn2.genenetwork.org/api/v_pre1/trait/BXD/10001
+curl http://genenetwork.org/api/v_pre1/trait/BXD/10001
{ "additive": 2.39444435069444, "id": 4, "locus": "rs48756159", "lrs": 13.4974911471087 }
```
@@ -130,7 +130,7 @@ Each method's query takes the following parameters respectively (more will be ad
Example query:
```
-curl http://gn2.genenetwork.org/api/v_pre1/mapping?trait_id=10015&db=BXDPublish&method=gemma&use_loco=true
+curl http://genenetwork.org/api/v_pre1/mapping?trait_id=10015&db=BXDPublish&method=gemma&use_loco=true
```
### R/qtl ###
@@ -146,7 +146,7 @@ curl http://gn2.genenetwork.org/api/v_pre1/mapping?trait_id=10015&db=BXDPublish&
Example query:
```
-curl http://gn2.genenetwork.org/api/v_pre1/mapping?trait_id=1418701_at&db=HC_M2_0606_P&method=rqtl&num_perm=100
+curl http://genenetwork.org/api/v_pre1/mapping?trait_id=1418701_at&db=HC_M2_0606_P&method=rqtl&num_perm=100
```
Some combinations of methods/models may not make sense. The R/qtl manual should be referred to for any questions on its use (specifically the scanone function in this case)
@@ -164,6 +164,6 @@ This query currently takes the following parameters (though more will be added):
Example query:
```
-curl http://gn2.genenetwork.org/api/v_pre1/correlation?trait_id=1427571_at&db=HC_M2_0606_P&target_db=BXDPublish&type=sample&return_count=100
+curl http://genenetwork.org/api/v_pre1/correlation?trait_id=1427571_at&db=HC_M2_0606_P&target_db=BXDPublish&type=sample&return_count=100
[ { "#_strains": 6, "p_value": 0.004804664723032055, "sample_r": -0.942857142857143, "trait": 20511 }, { "#_strains": 6, "p_value": 0.004804664723032055, "sample_r": -0.942857142857143, "trait": 20724 }, { "#_strains": 12, "p_value": 1.8288943424888848e-05, "sample_r": -0.9233615170820528, "trait": 13536 }, { "#_strains": 7, "p_value": 0.006807187408935392, "sample_r": 0.8928571428571429, "trait": 10157 }, { "#_strains": 7, "p_value": 0.006807187408935392, "sample_r": -0.8928571428571429, "trait": 20392 }, ... ]
```
diff --git a/wqflask/base/data_set.py b/wqflask/base/data_set.py
index fae62875..8652e6b7 100644
--- a/wqflask/base/data_set.py
+++ b/wqflask/base/data_set.py
@@ -56,7 +56,7 @@ from pprint import pformat as pf
from db.gn_server import menu_main
from db.call import fetchall,fetchone,fetch1
-from utility.tools import USE_GN_SERVER, USE_REDIS, flat_files, flat_file_exists
+from utility.tools import USE_GN_SERVER, USE_REDIS, flat_files, flat_file_exists, GN2_BASE_URL
from utility.logger import getLogger
logger = getLogger(__name__ )
@@ -94,7 +94,7 @@ Publish or ProbeSet. E.g.
"""
self.datasets = {}
if rebuild: #ZS: May make this the only option
- data = json.loads(requests.get("http://gn2.genenetwork.org/api/v_pre1/gen_dropdown").content)
+ data = json.loads(requests.get(GN2_BASE_URL + "/api/v_pre1/gen_dropdown").content)
#data = gen_menu.gen_dropdown_json()
else:
file_name = "wqflask/static/new/javascript/dataset_menu_structure.json"
diff --git a/wqflask/base/trait.py b/wqflask/base/trait.py
index 5525472e..e454c593 100644
--- a/wqflask/base/trait.py
+++ b/wqflask/base/trait.py
@@ -14,6 +14,7 @@ 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 wqflask import app
@@ -135,9 +136,9 @@ class GeneralTrait(object):
alias = 'Not available'
if self.symbol:
- human_response = requests.get("http://gn2.genenetwork.org/gn3/gene/aliases/" + self.symbol.upper())
- mouse_response = requests.get("http://gn2.genenetwork.org/gn3/gene/aliases/" + self.symbol.capitalize())
- other_response = requests.get("http://gn2.genenetwork.org/gn3/gene/aliases/" + self.symbol.lower())
+ human_response = requests.get(GN2_BASE_URL + "gn3/gene/aliases/" + self.symbol.upper())
+ mouse_response = requests.get(GN2_BASE_URL + "gn3/gene/aliases/" + self.symbol.capitalize())
+ other_response = requests.get(GN2_BASE_URL + "gn3/gene/aliases/" + self.symbol.lower())
if human_response and mouse_response and other_response:
alias_list = json.loads(human_response.content) + json.loads(mouse_response.content) + json.loads(other_response.content)
diff --git a/wqflask/utility/tools.py b/wqflask/utility/tools.py
index 0fbedccb..9354ece6 100644
--- a/wqflask/utility/tools.py
+++ b/wqflask/utility/tools.py
@@ -234,6 +234,8 @@ def show_settings():
GN_VERSION = get_setting('GN_VERSION')
HOME = get_setting('HOME')
WEBSERVER_MODE = get_setting('WEBSERVER_MODE')
+GN2_BASE_URL = get_setting('GN2_BASE_URL')
+GN2_BRANCH_URL = get_setting('GN2_BRANCH_URL')
GN_SERVER_URL = get_setting('GN_SERVER_URL')
SERVER_PORT = get_setting_int('SERVER_PORT')
SQL_URI = get_setting('SQL_URI')
diff --git a/wqflask/wqflask/ctl/ctl_analysis.py b/wqflask/wqflask/ctl/ctl_analysis.py
index 6fda02fd..4415b86a 100644
--- a/wqflask/wqflask/ctl/ctl_analysis.py
+++ b/wqflask/wqflask/ctl/ctl_analysis.py
@@ -20,7 +20,7 @@ from base import data_set
from base import trait as TRAIT
from utility import helper_functions
-from utility.tools import locate
+from utility.tools import locate, GN2_BRANCH_URL
from rpy2.robjects.packages import importr
@@ -56,6 +56,8 @@ class CTL(object):
self.edges_list = []
logger.info("Obtained pointers to CTL functions")
+ self.gn2_url = GN2_BRANCH_URL
+
def addNode(self, gt):
node_dict = { 'data' : {'id' : str(gt.name) + ":" + str(gt.dataset.name),
'sid' : str(gt.name),
diff --git a/wqflask/wqflask/do_search.py b/wqflask/wqflask/do_search.py
index 05caa100..b0ca5ced 100644
--- a/wqflask/wqflask/do_search.py
+++ b/wqflask/wqflask/do_search.py
@@ -13,6 +13,7 @@ import sys
# sys.path.append("..") Never in a running webserver
from db import webqtlDatabaseFunction
+from utility.tools import GN2_BASE_URL
import logging
from utility.logger import getLogger
@@ -919,7 +920,7 @@ def get_aliases(symbol, species):
return []
filtered_aliases = []
- response = requests.get("http://gn2.genenetwork.org/gn3/gene/aliases/" + symbol_string)
+ response = requests.get(GN2_BASE_URL + "/gn3/gene/aliases/" + symbol_string)
if response:
alias_list = json.loads(response.content)
diff --git a/wqflask/wqflask/network_graph/network_graph.py b/wqflask/wqflask/network_graph/network_graph.py
index a332db46..152e4168 100644
--- a/wqflask/wqflask/network_graph/network_graph.py
+++ b/wqflask/wqflask/network_graph/network_graph.py
@@ -47,6 +47,7 @@ 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 utility.tools import GN2_BRANCH_URL
from db import webqtlDatabaseFunction
import utility.webqtlUtil #this is for parallel computing only.
from wqflask.correlation import correlation_functions
@@ -195,6 +196,7 @@ class NetworkGraph(object):
self.nodes_list.append(node_dict)
self.elements = json.dumps(self.nodes_list + self.edges_list)
+ self.gn2_url = GN2_BRANCH_URL
groups = []
for sample in self.all_sample_list:
diff --git a/wqflask/wqflask/search_results.py b/wqflask/wqflask/search_results.py
index 698389ab..8f702d58 100644
--- a/wqflask/wqflask/search_results.py
+++ b/wqflask/wqflask/search_results.py
@@ -28,6 +28,7 @@ from flask import render_template, Flask, g
from utility import formatting
from utility import hmac
+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.logger import getLogger
@@ -295,7 +296,7 @@ def get_aliases(symbol_list, species):
symbols_string = ",".join(updated_symbols)
filtered_aliases = []
- response = requests.get("http://gn2.genenetwork.org/gn3/gene/aliases2/" + symbols_string)
+ response = requests.get(GN2_BASE_URL + "/gn3/gene/aliases2/" + symbols_string)
if response:
alias_lists = json.loads(response.content)
seen = set()
diff --git a/wqflask/wqflask/static/new/javascript/ctl_graph.js b/wqflask/wqflask/static/new/javascript/ctl_graph.js
index 94bd7e9d..bd950592 100644
--- a/wqflask/wqflask/static/new/javascript/ctl_graph.js
+++ b/wqflask/wqflask/static/new/javascript/ctl_graph.js
@@ -82,18 +82,12 @@ window.onload=function() {
function create_qtips(cy){
cy.nodes().qtip({
content: function(){
- gn_link = ''+''+this.data().id +''+'
'
+ gn_link = ''+''+this.data().id +''+'
'
ncbi_link = 'NCBI'+'
'
omim_link = 'OMIM'+'
'
qtip_content = gn_link + ncbi_link + omim_link
return qtip_content
- //return ''+''+this.data().id +''+''
},
- // content: {
- // title: ''+''+this.target() +''+'',
- // text: this.target,
- // button: true
- // },
position: {
my: 'top center',
at: 'bottom center'
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 fad600d2..794804f4 100644
--- a/wqflask/wqflask/static/new/javascript/dataset_select_menu_orig.js
+++ b/wqflask/wqflask/static/new/javascript/dataset_select_menu_orig.js
@@ -74,7 +74,6 @@ redo_dropdown = function(dropdown, items) {
this_opt_group = null
for (_i = 0, _len = group_family_list.length; _i < _len; _i++) {
item = group_family_list[_i];
- console.log("THE ITEM:", item)
if (item[2] != "None" && current_family == ""){
current_family = item[2]
this_opt_group = $("