about summary refs log tree commit diff
path: root/wqflask
diff options
context:
space:
mode:
authorPjotr Prins2016-10-06 14:27:17 +0000
committerPjotr Prins2016-10-06 14:27:17 +0000
commit94f7e2494f5e48b12ae10c0bf46ebc2367b0eecc (patch)
treea3e3989d0817e63cd6df0ef7d93b50e659c8288f /wqflask
parentbd35fd6430e5b8b88fd3a7b8afa933d2b0f20b4e (diff)
parent416363b3f8940d145d8aec076865b167e78705d9 (diff)
downloadgenenetwork2-94f7e2494f5e48b12ae10c0bf46ebc2367b0eecc.tar.gz
Merge branch 'master' of github.com:genenetwork/genenetwork2 into mechanize
Diffstat (limited to 'wqflask')
-rw-r--r--wqflask/base/data_set.py2
-rw-r--r--wqflask/base/trait.py2
-rw-r--r--wqflask/base/webqtlCaseData.py2
-rw-r--r--wqflask/maintenance/__init__.py0
-rw-r--r--wqflask/runserver.py19
-rw-r--r--wqflask/utility/helper_functions.py12
-rw-r--r--wqflask/utility/logger.py6
-rw-r--r--wqflask/utility/tools.py40
-rw-r--r--wqflask/wqflask/__init__.py6
-rw-r--r--wqflask/wqflask/collect.py17
-rw-r--r--wqflask/wqflask/marker_regression/marker_regression.py261
-rw-r--r--wqflask/wqflask/marker_regression/rqtl_mapping.py193
-rw-r--r--wqflask/wqflask/static/gif/error/Wild-Type-Mouse.gifbin0 -> 37328 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-aliens-29.gifbin0 -> 14088 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-angels-04.gifbin0 -> 12155 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-cats-016.gifbin0 -> 10388 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-cats-031.gifbin0 -> 6937 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-cell-phones-03.gifbin0 -> 14902 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-cell-phones-16.gifbin0 -> 7307 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-computers-13.gifbin0 -> 7616 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-computers-28.gifbin0 -> 7566 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-computers-32.gifbin0 -> 2970 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-computers-42.gifbin0 -> 9391 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-computers-60.gifbin0 -> 35873 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-computers-64.gifbin0 -> 20728 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-computers-65.gifbin0 -> 26358 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-computers-72.gifbin0 -> 16361 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-computers-74.gifbin0 -> 21087 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-computers-75.gifbin0 -> 98317 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-construction-sites-038.gifbin0 -> 949 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-dogs-04.gifbin0 -> 9450 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-dogs-14.gifbin0 -> 2532 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-dogs-18.gifbin0 -> 3596 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-dogs-47.gifbin0 -> 13920 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-dogs-50.gifbin0 -> 7297 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-lava-lamps-01.gifbin0 -> 27122 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-mice-02.gifbin0 -> 73345 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-mice-09.gifbin0 -> 27822 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-mice-24.gifbin0 -> 10566 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-smileys-063.gifbin0 -> 2230 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-smileys-068.gifbin0 -> 11751 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-smileys-134.gifbin0 -> 21431 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-smileys-211.gifbin0 -> 11209 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-smileys-234.gifbin0 -> 8677 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-stickmen-001.gifbin0 -> 6897 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-stickmen-002.gifbin0 -> 14098 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-stickmen-005.gifbin0 -> 13264 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-stickmen-012.gifbin0 -> 12141 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-stickmen-056.gifbin0 -> 5946 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-stickmen-059.gifbin0 -> 16427 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-stickmen-060.gifbin0 -> 14434 bytes
-rw-r--r--wqflask/wqflask/static/gif/error/animated-gifs-stickmen-069.gifbin0 -> 7668 bytes
-rw-r--r--wqflask/wqflask/templates/collections/add.html2
-rw-r--r--wqflask/wqflask/templates/ctl_setup.html111
-rw-r--r--wqflask/wqflask/templates/error.html61
-rw-r--r--wqflask/wqflask/templates/show_trait_mapping_tools.html28
-rw-r--r--wqflask/wqflask/templates/wgcna_setup.html85
-rw-r--r--wqflask/wqflask/views.py34
58 files changed, 499 insertions, 382 deletions
diff --git a/wqflask/base/data_set.py b/wqflask/base/data_set.py
index 1b4e1195..8bd5bfb9 100644
--- a/wqflask/base/data_set.py
+++ b/wqflask/base/data_set.py
@@ -107,7 +107,7 @@ Publish or ProbeSet. E.g.
                         else:
                             new_type = "ProbeSet"
                         self.datasets[short_dataset_name] = new_type
-        logger.info("datasets",self.datasets)
+        logger.debug("datasets",self.datasets)
 
     def __call__(self, name):
         return self.datasets[name]
diff --git a/wqflask/base/trait.py b/wqflask/base/trait.py
index 900e050c..32032ba7 100644
--- a/wqflask/base/trait.py
+++ b/wqflask/base/trait.py
@@ -363,7 +363,7 @@ class GeneralTrait(object):
                             FROM
                                     Homologene, Species, InbredSet
                             WHERE
-                                    Homologene.GeneId =%s AND
+                                    Homologene.GeneId ='%s' AND
                                     InbredSet.Name = '%s' AND
                                     InbredSet.SpeciesId = Species.Id AND
                                     Species.TaxonomyId = Homologene.TaxonomyId
diff --git a/wqflask/base/webqtlCaseData.py b/wqflask/base/webqtlCaseData.py
index 8df9939e..2f88f778 100644
--- a/wqflask/base/webqtlCaseData.py
+++ b/wqflask/base/webqtlCaseData.py
@@ -16,8 +16,6 @@
 # Contact Drs. Robert W. Williams and Xiaodong Zhou (2010)
 # at rwilliams@uthsc.edu and xzhou15@uthsc.edu
 #
-#
-#
 # This module is used by GeneNetwork project (www.genenetwork.org)
 #
 # Created by GeneNetwork Core Team 2010/08/10
diff --git a/wqflask/maintenance/__init__.py b/wqflask/maintenance/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/wqflask/maintenance/__init__.py
diff --git a/wqflask/runserver.py b/wqflask/runserver.py
index 12ec904e..0342b7ad 100644
--- a/wqflask/runserver.py
+++ b/wqflask/runserver.py
@@ -16,34 +16,37 @@ GREEN = '\033[92m'
 BOLD  = '\033[1m'
 ENDC  = '\033[0m'
 
-logger.info("GN2 is running. Visit %shttp://localhost:5003/%s" % (BLUE,ENDC))
-
 import os
 app.config['SECRET_KEY'] = os.urandom(24)
 
-from utility.tools import WEBSERVER_MODE
+from utility.tools import WEBSERVER_MODE,get_setting_int
+
+port = get_setting_int("SERVER_PORT")
+
+logger.info("GN2 is running. Visit %shttp://localhost:%s/%s" % (BLUE,port,ENDC))
 
 werkzeug_logger = logging.getLogger('werkzeug')
 
 if WEBSERVER_MODE == 'DEBUG':
     app.run(host='0.0.0.0',
-            port=app.config['SERVER_PORT'],
+            port=port,
             debug=True,
-            use_debugger=True,
+            use_debugger=False,
             threaded=False,
+            processes=0,
             use_reloader=True)
 elif WEBSERVER_MODE == 'DEV':
     werkzeug_logger.setLevel(logging.WARNING)
     app.run(host='0.0.0.0',
-            port=app.config['SERVER_PORT'],
+            port=port,
             debug=False,
             use_debugger=False,
             threaded=False,
             processes=0,
             use_reloader=True)
-else: #production mode
+else: # staging/production modes
     app.run(host='0.0.0.0',
-            port=app.config['SERVER_PORT'],
+            port=port,
             debug=False,
             use_debugger=False,
             threaded=True,
diff --git a/wqflask/utility/helper_functions.py b/wqflask/utility/helper_functions.py
index 149ee553..377f6b26 100644
--- a/wqflask/utility/helper_functions.py
+++ b/wqflask/utility/helper_functions.py
@@ -5,19 +5,21 @@ from base import data_set
 from base.species import TheSpecies
 
 from wqflask import user_manager
+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"
     self.dataset = data_set.create_dataset(start_vars['dataset'])
-    print("After creating dataset")
+    logger.debug("After creating dataset")
     self.species = TheSpecies(dataset=self.dataset)
-    print("After creating species")
+    logger.debug("After creating species")
     self.this_trait = GeneralTrait(dataset=self.dataset,
                                    name=start_vars['trait_id'],
                                    cellid=None,
                                    get_qtl_info=True)
-    print("After creating trait")
+    logger.debug("After creating trait")
 
     #if read_genotype:
     #self.dataset.group.read_genotype_file()
@@ -27,7 +29,7 @@ def get_species_dataset_trait(self, start_vars):
 def get_trait_db_obs(self, trait_db_list):
     if isinstance(trait_db_list, basestring):
         trait_db_list = trait_db_list.split(",")
-        
+
     self.trait_list = []
     for trait in trait_db_list:
         data, _separator, hmac = trait.rpartition(':')
@@ -38,4 +40,4 @@ def get_trait_db_obs(self, trait_db_list):
         trait_ob = GeneralTrait(dataset=dataset_ob,
                                name=trait_name,
                                cellid=None)
-        self.trait_list.append((trait_ob, dataset_ob))
\ No newline at end of file
+        self.trait_list.append((trait_ob, dataset_ob))
diff --git a/wqflask/utility/logger.py b/wqflask/utility/logger.py
index ddc0ea82..b873e16f 100644
--- a/wqflask/utility/logger.py
+++ b/wqflask/utility/logger.py
@@ -31,6 +31,7 @@ import string
 from inspect import isfunction
 from pprint import pformat as pf
 from inspect import stack
+import datetime
 
 from utility.tools import LOG_LEVEL, LOG_LEVEL_DEBUG, LOG_SQL, LOG_FORMAT
 
@@ -66,7 +67,10 @@ LOG_LEVEL_DEBUG (NYI).
 
     def error(self,*args):
         """Call logging.error for multiple args"""
-        self.collect(self.logger.error,*args)
+        now = datetime.datetime.utcnow()
+        time_str = now.strftime('%H:%M:%S UTC %Y%m%d')
+        l = [time_str]+list(args)
+        self.collect(self.logger.error,*l)
 
     def infof(self,*args):
         """Call logging.info for multiple args lazily"""
diff --git a/wqflask/utility/tools.py b/wqflask/utility/tools.py
index 22779739..23d6fb62 100644
--- a/wqflask/utility/tools.py
+++ b/wqflask/utility/tools.py
@@ -3,6 +3,7 @@
 
 import os
 import sys
+import json
 
 from wqflask import app
 
@@ -10,6 +11,8 @@ from wqflask import app
 import logging
 logger = logging.getLogger(__name__ )
 
+OVERRIDES = {}
+
 def get_setting(command_id,guess=None):
     """Resolve a setting from the environment or the global settings in
     app.config, with valid_path is a function checking whether the
@@ -45,13 +48,15 @@ def get_setting(command_id,guess=None):
     logger.debug("Looking for "+command_id+"\n")
     command = value(os.environ.get(command_id))
     if command is None or command == "":
-        # ---- Check whether setting exists in app
-        command = value(app.config.get(command_id))
+        command = OVERRIDES.get(command_id)
         if command is None:
-            command = value(guess)
-            if command is None or command == "":
-                print command
-                raise Exception(command_id+' setting unknown or faulty (update default_settings.py?).')
+            # ---- Check whether setting exists in app
+            command = value(app.config.get(command_id))
+            if command is None:
+                command = value(guess)
+                if command is None or command == "":
+                    # print command
+                    raise Exception(command_id+' setting unknown or faulty (update default_settings.py?).')
     logger.debug("Set "+command_id+"="+str(command))
     return command
 
@@ -169,18 +174,20 @@ def show_settings():
     log_level = getattr(logging, LOG_LEVEL.upper())
     logging.basicConfig(level=log_level)
 
+    logger.info(OVERRIDES)
     logger.info(BLUE+"Mr. Mojo Risin 2"+ENDC)
-    print "runserver.py: ****** The webserver has the following configuration ******"
+    print "runserver.py: ****** Webserver configuration ******"
     keylist = app.config.keys()
     keylist.sort()
     for k in keylist:
         try:
-            print("%s %s%s%s%s" % (k,BLUE,BOLD,get_setting(k),ENDC))
+            print("%s: %s%s%s%s" % (k,BLUE,BOLD,get_setting(k),ENDC))
         except:
-            print("%s %s%s%s%s" % (k,GREEN,BOLD,app.config[k],ENDC))
+            print("%s: %s%s%s%s" % (k,GREEN,BOLD,app.config[k],ENDC))
 
 
 # Cached values
+HOME               = get_setting('HOME')
 WEBSERVER_MODE     = get_setting('WEBSERVER_MODE')
 GN_SERVER_URL      = get_setting('GN_SERVER_URL')
 SQL_URI            = get_setting('SQL_URI')
@@ -199,3 +206,18 @@ PYLMM_COMMAND      = pylmm_command()
 GEMMA_COMMAND      = gemma_command()
 PLINK_COMMAND      = plink_command()
 TEMPDIR            = tempdir()
+
+from six import string_types
+
+if os.environ.get('WQFLASK_OVERRIDES'):
+    jsonfn = get_setting('WQFLASK_OVERRIDES')
+    logger.error("WQFLASK_OVERRIDES: %s" % jsonfn)
+    with open(jsonfn) as data_file:
+        overrides = json.load(data_file)
+        for k in overrides:
+            cmd = overrides[k]
+            if isinstance(cmd, string_types):
+                OVERRIDES[k] = eval(cmd)
+            else:
+                OVERRIDES[k] = cmd
+            logger.debug(OVERRIDES)
diff --git a/wqflask/wqflask/__init__.py b/wqflask/wqflask/__init__.py
index af271d02..2188ce17 100644
--- a/wqflask/wqflask/__init__.py
+++ b/wqflask/wqflask/__init__.py
@@ -14,11 +14,7 @@ app = Flask(__name__)
 
 app.config.from_object('cfg.default_settings')   # Get the defaults from cfg.default_settings
 app.config.from_envvar('WQFLASK_SETTINGS')       # See http://flask.pocoo.org/docs/config/#configuring-from-files
-
-logger.debug("System path is")
-logger.debug(sys.path)
-logger.debug("App.config is")
-logger.debug(app.config)
+# Note we also use WQFLASK_OVERRIDES
 
 app.jinja_env.globals.update(
     undefined = jinja2.StrictUndefined,
diff --git a/wqflask/wqflask/collect.py b/wqflask/wqflask/collect.py
index 7e7aba89..81d03d6c 100644
--- a/wqflask/wqflask/collect.py
+++ b/wqflask/wqflask/collect.py
@@ -164,7 +164,7 @@ class UserCollection(object):
             if not uc:
                 return create_new("Default")
         else:
-            uc = model.UserCollection.query.get(params['existing_collection'])
+            uc = model.UserCollection.query.get(params['existing_collection'].split(":")[0])
         members =  list(uc.members_as_set()) #set(json.loads(uc.members))
         len_before = len(members)
 
@@ -218,7 +218,6 @@ def collections_add():
         anon_collections = user_manager.AnonUser().get_collections()
         collection_names = []
         for collection in anon_collections:
-            print("COLLECTION:", collection)
             collection_names.append({'id':collection['id'], 'name':collection['name']})
         return render_template("collections/add.html",
                                 traits = traits,
@@ -229,21 +228,17 @@ def collections_add():
 @app.route("/collections/new")
 def collections_new():
     params = request.args
-    #print("request.args in collections_new are:", params)
-
-    collection_name = params['new_collection']
     
-    if "anonymous_add" in params:
-        AnonCollection(name=collection_name).add_traits(params, "Default")
-        return redirect(url_for('view_collection'))
     if "sign_in" in params:
         return redirect(url_for('login'))
 
     if "create_new" in params:
         print("in create_new")
+        collection_name = params['new_collection']
         return create_new(collection_name)
     elif "add_to_existing" in params:
         print("in add to existing")
+        collection_name = params['existing_collection'].split(":")[1]
         if g.user_session.logged_in:
             return UserCollection().add_traits(params, collection_name)
         else:
@@ -251,7 +246,6 @@ def collections_new():
             ac.add_traits(params)
             return redirect(url_for('view_collection', collection_id=ac.id))
     else:
-        print("ELSE")
         CauseAnError
 
 
@@ -288,7 +282,6 @@ def create_new(collection_name):
         ac = AnonCollection(collection_name)
         ac.changed_timestamp = datetime.datetime.utcnow().strftime('%b %d %Y %I:%M%p')       
         ac.add_traits(params)
-        print("Collection ID:", ac.id)
         return redirect(url_for('view_collection', collection_id=ac.id))
 
 @app.route("/collections/list")
@@ -367,7 +360,6 @@ def view_collection():
         uc_id = params['uc_id']
         uc = model.UserCollection.query.get(uc_id)
         traits = json.loads(uc.members)
-        print("traits are:", traits)
     else:
         user_collections = json.loads(Redis.get(user_manager.AnonUser().key))
         this_collection = {}
@@ -384,7 +376,6 @@ def view_collection():
     json_version = []
 
     for atrait in traits:
-        print("atrait is:", atrait)
         name, dataset_name = atrait.split(':')                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          
         trait_ob = trait.GeneralTrait(name=name, dataset_name=dataset_name)
@@ -393,8 +384,6 @@ def view_collection():
 
         json_version.append(trait_ob.jsonable())
 
-    print("trait_obs:", trait_obs)
-
     if "uc_id" in params:
         collection_info = dict(trait_obs=trait_obs,
                                uc = uc)
diff --git a/wqflask/wqflask/marker_regression/marker_regression.py b/wqflask/wqflask/marker_regression/marker_regression.py
index d7c78db0..37ee42a7 100644
--- a/wqflask/wqflask/marker_regression/marker_regression.py
+++ b/wqflask/wqflask/marker_regression/marker_regression.py
@@ -35,7 +35,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
+from wqflask.marker_regression import gemma_mapping, rqtl_mapping
 
 from utility.tools import locate, locate_ignore_error, PYLMM_COMMAND, GEMMA_COMMAND, PLINK_COMMAND, TEMPDIR
 from utility.external import shell
@@ -169,7 +169,10 @@ class MarkerRegression(object):
             self.model = start_vars['mapmodel_rqtl_geno']
             if start_vars['pair_scan'] == "true":
                 self.pair_scan = True
-            results = self.run_rqtl_geno()
+            if self.permCheck and self.num_perm > 0:
+                perm_output, self.suggestive, self.significant, results = rqtl_mapping.run_rqtl_geno(self.vals, self.dataset, self.method, self.model, self.permCheck, self.num_perm, self.do_control, self.control_marker, self.manhattan_plot, self.pair_scan)
+            else:
+                results = rqtl_mapping.run_rqtl_geno(self.vals, self.dataset, self.method, self.model, self.permCheck, self.num_perm, self.do_control, self.control_marker, self.manhattan_plot, self.pair_scan)
         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:
@@ -202,12 +205,12 @@ class MarkerRegression(object):
         elif self.mapping_method == "plink":
             results = self.run_plink()
         elif self.mapping_method == "pylmm":
-            print("RUNNING PYLMM")
+            logger.debug("RUNNING PYLMM")
             if self.num_perm > 0:
                 self.run_permutations(str(temp_uuid))
             results = self.gen_data(str(temp_uuid))
         else:
-            print("RUNNING NOTHING")
+            logger.debug("RUNNING NOTHING")
 
         if self.pair_scan == True:
             self.qtl_results = []
@@ -261,9 +264,9 @@ class MarkerRegression(object):
             #Need to convert the QTL objects that qtl reaper returns into a json serializable dictionary
             for index, qtl in enumerate(self.qtl_results):
                 #if index<40:
-                #    print("lod score is:", qtl['lod_score'])
+                #    logger.debug("lod score is:", qtl['lod_score'])
                 if qtl['chr'] == highest_chr and highest_chr != "X" and highest_chr != "X/Y":
-                    #print("changing to X")
+                    #logger.debug("changing to X")
                     self.json_data['chr'].append("X")
                 else:
                     self.json_data['chr'].append(str(qtl['chr']))
@@ -281,7 +284,7 @@ class MarkerRegression(object):
                 self.json_data['chrnames'].append([self.species.chromosomes.chromosomes[key].name, self.species.chromosomes.chromosomes[key].mb_length])
                 chromosome_mb_lengths[key] = self.species.chromosomes.chromosomes[key].mb_length
 
-            # print("json_data:", self.json_data)
+            # logger.debug("json_data:", self.json_data)
 
             self.js_data = dict(
                 result_score_type = self.score_type,
@@ -309,7 +312,7 @@ class MarkerRegression(object):
                                                                                                  self.dataset.group.name,
                                                                                                  self.dataset.group.name,
                                                                                                  self.dataset.group.name)
-        #print("gemma_command:" + gemma_command)
+        #logger.debug("gemma_command:" + gemma_command)
 
         os.system(gemma_command)
 
@@ -331,7 +334,7 @@ class MarkerRegression(object):
                     included_markers.append(line.split("\t")[1])
                     p_values.append(float(line.split("\t")[10]))
                     #p_values[line.split("\t")[1]] = float(line.split("\t")[10])
-        #print("p_values: ", p_values)
+        #logger.debug("p_values: ", p_values)
         return included_markers, p_values
 
     def gen_pheno_txt_file(self):
@@ -353,200 +356,13 @@ class MarkerRegression(object):
 
         count, p_values = self.parse_rqtl_output(plink_output_filename)
 
-    def geno_to_rqtl_function(self):        # 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
-
-             genocodes <- c(getGenoCode(header, 'mat'), getGenoCode(header, 'het'), getGenoCode(header, 'pat'))                # Get the genotype codes
-             type <- getGenoCode(header, 'type')
-             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')
-             if(is.null(phenotype)) phenotype <- runif((ncol(genodata)-4))                                                     # If there isn't a phenotype, generate a random one
-             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
-             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
-             return(cross)
-          }
-        """ % (self.dataset.group.name + ".geno"))
-
-    def run_rqtl_geno(self):
-        self.geno_to_rqtl_function()
-
-        ## 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
-        r_sum         = ro.r["sum"]                     # Map the sum function
-        plot          = ro.r["plot"]                    # Map the plot function
-        postscript    = ro.r["postscript"]              # Map the postscript function
-        png           = ro.r["png"]                     # Map the png function
-        dev_off       = ro.r["dev.off"]                 # Map the device off function
-
-        print(r_library("qtl"))                         # Load R/qtl
-
-        ## Get pointers to some R/qtl functions
-        scanone         = ro.r["scanone"]               # Map the scanone function
-        scantwo         = ro.r["scantwo"]               # Map the scantwo function
-        calc_genoprob   = ro.r["calc.genoprob"]         # Map the calc.genoprob function
-        read_cross      = ro.r["read.cross"]            # Map the read.cross function
-        write_cross     = ro.r["write.cross"]           # Map the write.cross function
-        GENOtoCSVR      = ro.r["GENOtoCSVR"]            # Map the local GENOtoCSVR function
-
-        crossname = self.dataset.group.name
-        genofilelocation  = locate(crossname + ".geno", "genotype")
-        crossfilelocation = TMPDIR + crossname + ".cross"
-
-        #print("Conversion of geno to cross at location:", genofilelocation, " to ", crossfilelocation)
-
-        cross_object = GENOtoCSVR(genofilelocation, crossfilelocation)                                  # TODO: Add the SEX if that is available
-
-        if self.manhattan_plot:
-            cross_object = calc_genoprob(cross_object)
-        else:
-            cross_object = calc_genoprob(cross_object, step=1, stepwidth="max")
-
-        cross_object = self.add_phenotype(cross_object, self.sanitize_rqtl_phenotype())                 # Add the phenotype
-
-        # for debug: write_cross(cross_object, "csvr", "test.csvr")
-
-        # Scan for QTLs
-        covar = self.create_covariates(cross_object)                                                    # Create the additive covariate matrix
-
-        if self.pair_scan:
-            if self.do_control == "true":                                                # If sum(covar) > 0 we have a covariate matrix
-                print("Using covariate"); result_data_frame = scantwo(cross_object, pheno = "the_pheno", addcovar = covar, model=self.model, method=self.method, n_cluster = 16)
-            else:
-                print("No covariates"); result_data_frame = scantwo(cross_object, pheno = "the_pheno", model=self.model, method=self.method, n_cluster = 16)
-
-            #print("Pair scan results:", result_data_frame)
-
-            self.pair_scan_filename = webqtlUtil.genRandStr("scantwo_") + ".png"
-            png(file=TEMPDIR+self.pair_scan_filename)
-            plot(result_data_frame)
-            dev_off()
-
-            return self.process_pair_scan_results(result_data_frame)
-
-        else:
-            if self.do_control == "true":
-                print("Using covariate"); result_data_frame = scanone(cross_object, pheno = "the_pheno", addcovar = covar, model=self.model, method=self.method)
-            else:
-                print("No covariates"); result_data_frame = scanone(cross_object, pheno = "the_pheno", model=self.model, method=self.method)
-
-            if self.num_perm > 0 and self.permCheck == "ON":                                                                   # Do permutation (if requested by user)
-                if self.do_control == "true":
-                    perm_data_frame = scanone(cross_object, pheno_col = "the_pheno", addcovar = covar, n_perm = self.num_perm, model=self.model, method=self.method)
-                else:
-                    perm_data_frame = scanone(cross_object, pheno_col = "the_pheno", n_perm = self.num_perm, model=self.model, method=self.method)
-
-                self.process_rqtl_perm_results(perm_data_frame)                                          # Functions that sets the thresholds for the webinterface
-
-            return self.process_rqtl_results(result_data_frame)
-
-    def add_phenotype(self, cross, pheno_as_string):
-        ro.globalenv["the_cross"] = cross
-        ro.r('the_cross$pheno <- cbind(pull.pheno(the_cross), the_pheno = '+ pheno_as_string +')')
-        return ro.r["the_cross"]
-
-    def create_covariates(self, cross):
-        ro.globalenv["the_cross"] = cross
-        ro.r('genotypes <- pull.geno(the_cross)')                             # Get the genotype matrix
-        userinputS = self.control_marker.replace(" ", "").split(",")                 # TODO: sanitize user input, Never Ever trust a user
-        covariate_names = ', '.join('"{0}"'.format(w) for w in userinputS)
-        #print("Marker names of selected covariates:", covariate_names)
-        ro.r('covnames <- c(' + covariate_names + ')')
-        ro.r('covInGeno <- which(covnames %in% colnames(genotypes))')
-        ro.r('covnames <- covnames[covInGeno]')
-        ro.r("cat('covnames (purged): ', covnames,'\n')")
-        ro.r('covariates <- genotypes[,covnames]')                            # Get the covariate matrix by using the marker name as index to the genotype file
-        #print("R/qtl matrix of covariates:", ro.r["covariates"])
-        return ro.r["covariates"]
-
-    def sanitize_rqtl_phenotype(self):
-        pheno_as_string = "c("
-        for i, val in enumerate(self.vals):
-            if val == "x":
-                if i < (len(self.vals) - 1):
-                    pheno_as_string +=  "NA,"
-                else:
-                    pheno_as_string += "NA"
-            else:
-                if i < (len(self.vals) - 1):
-                    pheno_as_string += str(val) + ","
-                else:
-                    pheno_as_string += str(val)
-        pheno_as_string += ")"
-        return pheno_as_string
-
-    def process_pair_scan_results(self, result):
-        pair_scan_results = []
-
-        result = result[1]
-        output = [tuple([result[j][i] for j in range(result.ncol)]) for i in range(result.nrow)]
-        #print("R/qtl scantwo output:", output)
-
-        for i, line in enumerate(result.iter_row()):
-            marker = {}
-            marker['name'] = result.rownames[i]
-            marker['chr1'] = output[i][0]
-            marker['Mb'] = output[i][1]
-            marker['chr2'] = int(output[i][2])
-            pair_scan_results.append(marker)
-
-        #print("pair_scan_results:", pair_scan_results)
-
-        return pair_scan_results
-
-    def process_rqtl_results(self, result):        # TODO: how to make this a one liner and not copy the stuff in a loop
-        qtl_results = []
-
-        output = [tuple([result[j][i] for j in range(result.ncol)]) for i in range(result.nrow)]
-        #print("R/qtl scanone output:", output)
-
-        for i, line in enumerate(result.iter_row()):
-            marker = {}
-            marker['name'] = result.rownames[i]
-            marker['chr'] = output[i][0]
-            marker['Mb'] = output[i][1]
-            marker['lod_score'] = output[i][2]
-            qtl_results.append(marker)
-
-        return qtl_results
-
-    def process_rqtl_perm_results(self, results):
-        perm_vals = []
-        for line in str(results).split("\n")[1:(self.num_perm+1)]:
-            #print("R/qtl permutation line:", line.split())
-            perm_vals.append(float(line.split()[1]))
-
-        self.perm_output = perm_vals
-        self.suggestive = np.percentile(np.array(perm_vals), 67)
-        self.significant = np.percentile(np.array(perm_vals), 95)
-
-        return self.suggestive, self.significant
-
-
     def run_plink(self):
         plink_output_filename = webqtlUtil.genRandStr("%s_%s_"%(self.dataset.group.name, self.this_trait.name))
 
         self.gen_pheno_txt_file_plink(pheno_filename = plink_output_filename)
 
         plink_command = PLINK_COMMAND + ' --noweb --ped %s/%s.ped --no-fid --no-parents --no-sex --no-pheno --map %s/%s.map --pheno %s%s.txt --pheno-name %s --maf %s --missing-phenotype -9999 --out %s%s --assoc ' % (PLINK_PATH, self.dataset.group.name, PLINK_PATH, self.dataset.group.name, TMPDIR, plink_output_filename, self.this_trait.name, self.maf, TMPDIR, plink_output_filename)
-        print("plink_command:", plink_command)
+        logger.debug("plink_command:", plink_command)
 
         os.system(plink_command)
 
@@ -554,11 +370,11 @@ class MarkerRegression(object):
 
         #for marker in self.dataset.group.markers.markers:
         #    if marker['name'] not in included_markers:
-        #        print("marker:", marker)
+        #        logger.debug("marker:", marker)
         #        self.dataset.group.markers.markers.remove(marker)
         #        #del self.dataset.group.markers.markers[marker]
 
-        print("p_values:", pf(p_values))
+        logger.debug("p_values:", pf(p_values))
 
         self.dataset.group.markers.add_pvalues(p_values)
 
@@ -825,7 +641,7 @@ class MarkerRegression(object):
 
         top_lod_scores = []
 
-        #print("self.num_perm:", self.num_perm)
+        #logger.debug("self.num_perm:", self.num_perm)
 
         for permutation in range(self.num_perm):
 
@@ -870,10 +686,10 @@ class MarkerRegression(object):
                     if p_value < lowest_p_value:
                         lowest_p_value = p_value
 
-                #print("lowest_p_value:", lowest_p_value)
+                #logger.debug("lowest_p_value:", lowest_p_value)
                 top_lod_scores.append(-math.log10(lowest_p_value))
 
-        #print("top_lod_scores:", top_lod_scores)
+        #logger.debug("top_lod_scores:", top_lod_scores)
 
         self.suggestive = np.percentile(top_lod_scores, 67)
         self.significant = np.percentile(top_lod_scores, 95)
@@ -881,14 +697,13 @@ class MarkerRegression(object):
     def gen_data(self, temp_uuid):
         """Generates p-values for each marker"""
 
-
-        print("self.vals is:", self.vals)
+        logger.debug("self.vals is:", self.vals)
         pheno_vector = np.array([(val == "x" or val == "") and np.nan or float(val) for val in self.vals])
 
         #lmm_uuid = str(uuid.uuid4())
 
         key = "pylmm:input:" + temp_uuid
-        print("key is:", pf(key))
+        logger.debug("key is:", pf(key))
         #with Bench("Loading cache"):
         #    result = Redis.get(key)
 
@@ -897,7 +712,7 @@ class MarkerRegression(object):
             #p_values = self.trim_results(p_values)
 
         else:
-            print("NOW CWD IS:", os.getcwd())
+            logger.debug("NOW CWD IS:", os.getcwd())
             genotype_data = [marker['genotypes'] for marker in self.dataset.group.markers.markers]
 
             no_val_samples = self.identify_empty_samples()
@@ -905,9 +720,9 @@ class MarkerRegression(object):
 
             genotype_matrix = np.array(genotype_data).T
 
-            #print("pheno_vector: ", pf(pheno_vector))
-            #print("genotype_matrix: ", pf(genotype_matrix))
-            #print("genotype_matrix.shape: ", pf(genotype_matrix.shape))
+            #logger.debug("pheno_vector: ", pf(pheno_vector))
+            #logger.debug("genotype_matrix: ", pf(genotype_matrix))
+            #logger.debug("genotype_matrix.shape: ", pf(genotype_matrix.shape))
 
             #params = {"pheno_vector": pheno_vector,
             #            "genotype_matrix": genotype_matrix,
@@ -915,8 +730,8 @@ class MarkerRegression(object):
             #            "refit": False,
             #            "temp_data": tempdata}
 
-            # print("genotype_matrix:", str(genotype_matrix.tolist()))
-            # print("pheno_vector:", str(pheno_vector.tolist()))
+            # logger.debug("genotype_matrix:", str(genotype_matrix.tolist()))
+            # logger.debug("pheno_vector:", str(pheno_vector.tolist()))
 
             params = dict(pheno_vector = pheno_vector.tolist(),
                         genotype_matrix = genotype_matrix.tolist(),
@@ -929,14 +744,14 @@ class MarkerRegression(object):
                         )
 
             json_params = json.dumps(params)
-            #print("json_params:", json_params)
+            #logger.debug("json_params:", json_params)
             Redis.set(key, json_params)
             Redis.expire(key, 60*60)
-            print("before printing command")
+            logger.debug("before printing command")
 
             command = PYLMM_COMMAND + ' --key {} --species {}'.format(key, "other")
-            print("command is:", command)
-            print("after printing command")
+            logger.debug("command is:", command)
+            logger.debug("after printing command")
 
             shell(command)
 
@@ -946,7 +761,7 @@ class MarkerRegression(object):
             json_results = Redis.blpop("pylmm:results:" + temp_uuid, 45*60)
             results = json.loads(json_results[1])
             p_values = [float(result) for result in results['p_values']]
-            #print("p_values:", p_values[:10])
+            #logger.debug("p_values:", p_values[:10])
             #p_values = self.trim_results(p_values)
             t_stats = results['t_stats']
 
@@ -957,7 +772,7 @@ class MarkerRegression(object):
             #    refit=False,
             #    temp_data=tempdata
             #)
-            #print("p_values:", p_values)
+            #logger.debug("p_values:", p_values)
 
         self.dataset.group.markers.add_pvalues(p_values)
 
@@ -966,7 +781,7 @@ class MarkerRegression(object):
         return self.dataset.group.markers.markers
 
     def trim_results(self, p_values):
-        print("len_p_values:", len(p_values))
+        logger.debug("len_p_values:", len(p_values))
         if len(p_values) > 500:
             p_values.sort(reverse=True)
             trimmed_values = p_values[:500]
@@ -985,7 +800,7 @@ class MarkerRegression(object):
         kinship_matrix = np.fromfile(open(file_base + '.kin','r'),sep=" ")
         kinship_matrix.resize((len(plink_input.indivs),len(plink_input.indivs)))
 
-        print("Before creating params")
+        logger.debug("Before creating params")
 
         params = dict(pheno_vector = pheno_vector.tolist(),
                     covariate_matrix = covariate_matrix.tolist(),
@@ -998,18 +813,18 @@ class MarkerRegression(object):
                     timestamp = datetime.datetime.now().isoformat(),
                     )
 
-        print("After creating params")
+        logger.debug("After creating params")
 
         json_params = json.dumps(params)
         Redis.set(key, json_params)
         Redis.expire(key, 60*60)
 
-        print("Before creating the command")
+        logger.debug("Before creating the command")
 
         command = PYLMM_COMMAND+' --key {} --species {}'.format(key,
                                                                                                                 "human")
 
-        print("command is:", command)
+        logger.debug("command is:", command)
 
         os.system(command)
 
@@ -1032,7 +847,7 @@ class MarkerRegression(object):
         return p_values, t_stats
 
     def get_lod_score_cutoff(self):
-        print("INSIDE GET LOD CUTOFF")
+        logger.debug("INSIDE GET LOD CUTOFF")
         high_qtl_count = 0
         for marker in self.dataset.group.markers.markers:
             if marker['lod_score'] > 1:
diff --git a/wqflask/wqflask/marker_regression/rqtl_mapping.py b/wqflask/wqflask/marker_regression/rqtl_mapping.py
new file mode 100644
index 00000000..93bf717c
--- /dev/null
+++ b/wqflask/wqflask/marker_regression/rqtl_mapping.py
@@ -0,0 +1,193 @@
+import rpy2.robjects as ro

+import numpy as np

+

+from base.webqtlConfig import TMPDIR

+from utility import webqtlUtil

+from utility.tools import locate, TEMPDIR

+

+def run_rqtl_geno(vals, dataset, method, model, permCheck, num_perm, do_control, control_marker, manhattan_plot, pair_scan):

+    geno_to_rqtl_function(dataset)

+

+    ## 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

+    r_sum         = ro.r["sum"]                     # Map the sum function

+    plot          = ro.r["plot"]                    # Map the plot function

+    postscript    = ro.r["postscript"]              # Map the postscript function

+    png           = ro.r["png"]                     # Map the png function

+    dev_off       = ro.r["dev.off"]                 # Map the device off function

+

+    print(r_library("qtl"))                         # Load R/qtl

+

+    ## Get pointers to some R/qtl functions

+    scanone         = ro.r["scanone"]               # Map the scanone function

+    scantwo         = ro.r["scantwo"]               # Map the scantwo function

+    calc_genoprob   = ro.r["calc.genoprob"]         # Map the calc.genoprob function

+    read_cross      = ro.r["read.cross"]            # Map the read.cross function

+    write_cross     = ro.r["write.cross"]           # Map the write.cross function

+    GENOtoCSVR      = ro.r["GENOtoCSVR"]            # Map the local GENOtoCSVR function

+

+    crossname = dataset.group.name

+    genofilelocation  = locate(crossname + ".geno", "genotype")

+    crossfilelocation = TMPDIR + crossname + ".cross"

+

+    #print("Conversion of geno to cross at location:", genofilelocation, " to ", crossfilelocation)

+

+    cross_object = GENOtoCSVR(genofilelocation, crossfilelocation)                                  # TODO: Add the SEX if that is available

+

+    if manhattan_plot:

+        cross_object = calc_genoprob(cross_object)

+    else:

+        cross_object = calc_genoprob(cross_object, step=1, stepwidth="max")

+

+    cross_object = add_phenotype(cross_object, sanitize_rqtl_phenotype(vals))                 # Add the phenotype

+

+    # for debug: write_cross(cross_object, "csvr", "test.csvr")

+

+    # Scan for QTLs

+    covar = create_covariates(control_marker, cross_object)                                                    # Create the additive covariate matrix

+

+    if pair_scan:

+        if do_control == "true":                                                # If sum(covar) > 0 we have a covariate matrix

+            print("Using covariate"); result_data_frame = scantwo(cross_object, pheno = "the_pheno", addcovar = covar, model=model, method=method, n_cluster = 16)

+        else:

+            print("No covariates"); result_data_frame = scantwo(cross_object, pheno = "the_pheno", model=model, method=method, n_cluster = 16)

+

+        #print("Pair scan results:", result_data_frame)

+

+        pair_scan_filename = webqtlUtil.genRandStr("scantwo_") + ".png"

+        png(file=TEMPDIR+pair_scan_filename)

+        plot(result_data_frame)

+        dev_off()

+

+        return process_pair_scan_results(result_data_frame)

+    else:

+        if do_control == "true":

+            print("Using covariate"); result_data_frame = scanone(cross_object, pheno = "the_pheno", addcovar = covar, model=model, method=method)

+        else:

+            print("No covariates"); result_data_frame = scanone(cross_object, pheno = "the_pheno", model=model, method=method)

+

+        if num_perm > 0 and permCheck == "ON":                                                                   # Do permutation (if requested by user)

+            if do_control == "true":

+                perm_data_frame = scanone(cross_object, pheno_col = "the_pheno", addcovar = covar, n_perm = num_perm, model=model, method=method)

+            else:

+                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

+            return perm_output, suggestive, significant, process_rqtl_results(result_data_frame)

+        else:

+            return process_rqtl_results(result_data_frame)

+

+def geno_to_rqtl_function(dataset):        # 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

+

+         genocodes <- c(getGenoCode(header, 'mat'), getGenoCode(header, 'het'), getGenoCode(header, 'pat'))                # Get the genotype codes

+         type <- getGenoCode(header, 'type')

+         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')

+         if(is.null(phenotype)) phenotype <- runif((ncol(genodata)-4))                                                     # If there isn't a phenotype, generate a random one

+         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

+         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

+         return(cross)

+      }

+    """ % (dataset.group.name + ".geno"))

+        

+def add_phenotype(cross, pheno_as_string):

+    ro.globalenv["the_cross"] = cross

+    ro.r('the_cross$pheno <- cbind(pull.pheno(the_cross), the_pheno = '+ pheno_as_string +')')

+    return ro.r["the_cross"]

+

+def create_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)

+    #print("Marker names of selected covariates:", covariate_names)

+    ro.r('covnames <- c(' + covariate_names + ')')

+    ro.r('covInGeno <- which(covnames %in% colnames(genotypes))')

+    ro.r('covnames <- covnames[covInGeno]')

+    ro.r("cat('covnames (purged): ', covnames,'\n')")

+    ro.r('covariates <- genotypes[,covnames]')                            # Get the covariate matrix by using the marker name as index to the genotype file

+    #print("R/qtl matrix of covariates:", ro.r["covariates"])

+    return ro.r["covariates"]

+

+def sanitize_rqtl_phenotype(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 process_pair_scan_results(result):

+    pair_scan_results = []

+

+    result = result[1]

+    output = [tuple([result[j][i] for j in range(result.ncol)]) for i in range(result.nrow)]

+    #print("R/qtl scantwo output:", output)

+

+    for i, line in enumerate(result.iter_row()):

+        marker = {}

+        marker['name'] = result.rownames[i]

+        marker['chr1'] = output[i][0]

+        marker['Mb'] = output[i][1]

+        marker['chr2'] = int(output[i][2])

+        pair_scan_results.append(marker)

+

+    #print("pair_scan_results:", pair_scan_results)

+

+    return pair_scan_results

+

+def process_rqtl_perm_results(num_perm, results):

+    perm_vals = []

+    for line in str(results).split("\n")[1:(num_perm+1)]:

+        #print("R/qtl permutation line:", line.split())

+        perm_vals.append(float(line.split()[1]))

+

+    perm_output = perm_vals

+    suggestive = np.percentile(np.array(perm_vals), 67)

+    significant = np.percentile(np.array(perm_vals), 95)

+    print("SIGNIFICANT:", significant)

+

+    return perm_output, suggestive, significant

+    

+def process_rqtl_results(result):        # TODO: how to make this a one liner and not copy the stuff in a loop

+    qtl_results = []

+

+    output = [tuple([result[j][i] for j in range(result.ncol)]) for i in range(result.nrow)]

+    #print("R/qtl scanone output:", output)

+

+    for i, line in enumerate(result.iter_row()):

+        marker = {}

+        marker['name'] = result.rownames[i]

+        marker['chr'] = output[i][0]

+        marker['Mb'] = output[i][1]

+        marker['lod_score'] = output[i][2]

+        qtl_results.append(marker)

+

+    return qtl_results 
\ No newline at end of file
diff --git a/wqflask/wqflask/static/gif/error/Wild-Type-Mouse.gif b/wqflask/wqflask/static/gif/error/Wild-Type-Mouse.gif
new file mode 100644
index 00000000..2c68b5ee
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/Wild-Type-Mouse.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-aliens-29.gif b/wqflask/wqflask/static/gif/error/animated-gifs-aliens-29.gif
new file mode 100644
index 00000000..e9d38277
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-aliens-29.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-angels-04.gif b/wqflask/wqflask/static/gif/error/animated-gifs-angels-04.gif
new file mode 100644
index 00000000..94e11847
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-angels-04.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-cats-016.gif b/wqflask/wqflask/static/gif/error/animated-gifs-cats-016.gif
new file mode 100644
index 00000000..7e6ec9a3
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-cats-016.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-cats-031.gif b/wqflask/wqflask/static/gif/error/animated-gifs-cats-031.gif
new file mode 100644
index 00000000..af7ef655
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-cats-031.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-cell-phones-03.gif b/wqflask/wqflask/static/gif/error/animated-gifs-cell-phones-03.gif
new file mode 100644
index 00000000..89c79ddf
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-cell-phones-03.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-cell-phones-16.gif b/wqflask/wqflask/static/gif/error/animated-gifs-cell-phones-16.gif
new file mode 100644
index 00000000..7530d180
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-cell-phones-16.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-computers-13.gif b/wqflask/wqflask/static/gif/error/animated-gifs-computers-13.gif
new file mode 100644
index 00000000..afb05c62
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-computers-13.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-computers-28.gif b/wqflask/wqflask/static/gif/error/animated-gifs-computers-28.gif
new file mode 100644
index 00000000..f5b4a563
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-computers-28.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-computers-32.gif b/wqflask/wqflask/static/gif/error/animated-gifs-computers-32.gif
new file mode 100644
index 00000000..7258e594
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-computers-32.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-computers-42.gif b/wqflask/wqflask/static/gif/error/animated-gifs-computers-42.gif
new file mode 100644
index 00000000..ed1f8722
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-computers-42.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-computers-60.gif b/wqflask/wqflask/static/gif/error/animated-gifs-computers-60.gif
new file mode 100644
index 00000000..f58d69f1
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-computers-60.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-computers-64.gif b/wqflask/wqflask/static/gif/error/animated-gifs-computers-64.gif
new file mode 100644
index 00000000..5d5b4fdf
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-computers-64.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-computers-65.gif b/wqflask/wqflask/static/gif/error/animated-gifs-computers-65.gif
new file mode 100644
index 00000000..b4b10845
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-computers-65.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-computers-72.gif b/wqflask/wqflask/static/gif/error/animated-gifs-computers-72.gif
new file mode 100644
index 00000000..e60cb4fe
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-computers-72.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-computers-74.gif b/wqflask/wqflask/static/gif/error/animated-gifs-computers-74.gif
new file mode 100644
index 00000000..bd7b72f3
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-computers-74.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-computers-75.gif b/wqflask/wqflask/static/gif/error/animated-gifs-computers-75.gif
new file mode 100644
index 00000000..916d6b33
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-computers-75.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-construction-sites-038.gif b/wqflask/wqflask/static/gif/error/animated-gifs-construction-sites-038.gif
new file mode 100644
index 00000000..0ec782c4
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-construction-sites-038.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-dogs-04.gif b/wqflask/wqflask/static/gif/error/animated-gifs-dogs-04.gif
new file mode 100644
index 00000000..9515c18a
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-dogs-04.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-dogs-14.gif b/wqflask/wqflask/static/gif/error/animated-gifs-dogs-14.gif
new file mode 100644
index 00000000..f1e2e1f5
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-dogs-14.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-dogs-18.gif b/wqflask/wqflask/static/gif/error/animated-gifs-dogs-18.gif
new file mode 100644
index 00000000..572849d5
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-dogs-18.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-dogs-47.gif b/wqflask/wqflask/static/gif/error/animated-gifs-dogs-47.gif
new file mode 100644
index 00000000..d808c9ee
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-dogs-47.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-dogs-50.gif b/wqflask/wqflask/static/gif/error/animated-gifs-dogs-50.gif
new file mode 100644
index 00000000..9865ee45
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-dogs-50.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-lava-lamps-01.gif b/wqflask/wqflask/static/gif/error/animated-gifs-lava-lamps-01.gif
new file mode 100644
index 00000000..ee9c113d
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-lava-lamps-01.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-mice-02.gif b/wqflask/wqflask/static/gif/error/animated-gifs-mice-02.gif
new file mode 100644
index 00000000..5ca2ee5c
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-mice-02.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-mice-09.gif b/wqflask/wqflask/static/gif/error/animated-gifs-mice-09.gif
new file mode 100644
index 00000000..7cb361e4
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-mice-09.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-mice-24.gif b/wqflask/wqflask/static/gif/error/animated-gifs-mice-24.gif
new file mode 100644
index 00000000..96a26450
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-mice-24.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-smileys-063.gif b/wqflask/wqflask/static/gif/error/animated-gifs-smileys-063.gif
new file mode 100644
index 00000000..62de166c
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-smileys-063.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-smileys-068.gif b/wqflask/wqflask/static/gif/error/animated-gifs-smileys-068.gif
new file mode 100644
index 00000000..3550e978
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-smileys-068.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-smileys-134.gif b/wqflask/wqflask/static/gif/error/animated-gifs-smileys-134.gif
new file mode 100644
index 00000000..954ab614
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-smileys-134.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-smileys-211.gif b/wqflask/wqflask/static/gif/error/animated-gifs-smileys-211.gif
new file mode 100644
index 00000000..596174d7
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-smileys-211.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-smileys-234.gif b/wqflask/wqflask/static/gif/error/animated-gifs-smileys-234.gif
new file mode 100644
index 00000000..5aba636b
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-smileys-234.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-stickmen-001.gif b/wqflask/wqflask/static/gif/error/animated-gifs-stickmen-001.gif
new file mode 100644
index 00000000..7896ff1f
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-stickmen-001.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-stickmen-002.gif b/wqflask/wqflask/static/gif/error/animated-gifs-stickmen-002.gif
new file mode 100644
index 00000000..89da6441
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-stickmen-002.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-stickmen-005.gif b/wqflask/wqflask/static/gif/error/animated-gifs-stickmen-005.gif
new file mode 100644
index 00000000..b7887630
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-stickmen-005.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-stickmen-012.gif b/wqflask/wqflask/static/gif/error/animated-gifs-stickmen-012.gif
new file mode 100644
index 00000000..f6697d02
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-stickmen-012.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-stickmen-056.gif b/wqflask/wqflask/static/gif/error/animated-gifs-stickmen-056.gif
new file mode 100644
index 00000000..2b2496a4
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-stickmen-056.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-stickmen-059.gif b/wqflask/wqflask/static/gif/error/animated-gifs-stickmen-059.gif
new file mode 100644
index 00000000..f2188656
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-stickmen-059.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-stickmen-060.gif b/wqflask/wqflask/static/gif/error/animated-gifs-stickmen-060.gif
new file mode 100644
index 00000000..aa8f7bd3
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-stickmen-060.gif
Binary files differdiff --git a/wqflask/wqflask/static/gif/error/animated-gifs-stickmen-069.gif b/wqflask/wqflask/static/gif/error/animated-gifs-stickmen-069.gif
new file mode 100644
index 00000000..473212e4
--- /dev/null
+++ b/wqflask/wqflask/static/gif/error/animated-gifs-stickmen-069.gif
Binary files differdiff --git a/wqflask/wqflask/templates/collections/add.html b/wqflask/wqflask/templates/collections/add.html
index 47b87d73..d45aa015 100644
--- a/wqflask/wqflask/templates/collections/add.html
+++ b/wqflask/wqflask/templates/collections/add.html
@@ -25,7 +25,7 @@
 
                 <select name="existing_collection" class="form-control">
                 {% for col in collections %}
-                    <option value="{{ col.id }}">{{ col.name }}</option>
+                    <option value="{{ col.id }}:{{ col.name }}">{{ col.name }}</option>
                 {% endfor %}
                 </select>
                 <br />
diff --git a/wqflask/wqflask/templates/ctl_setup.html b/wqflask/wqflask/templates/ctl_setup.html
index 9c0d7bea..51553322 100644
--- a/wqflask/wqflask/templates/ctl_setup.html
+++ b/wqflask/wqflask/templates/ctl_setup.html
@@ -1,65 +1,70 @@
 {% extends "base.html" %}
-{% block title %}WCGNA analysis{% endblock %}
+{% block title %}CTL analysis{% endblock %}
 
 {% block content %} <!-- Start of body -->
 <div class="container">
+  {% if request.form['trait_list'].split(",")|length < 2 %}
+  <div class="alert alert-danger" role="alert">
+    <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
+    <span class="sr-only">Error:</span>
+    <h2>Too few traits as input</h2>
+    Please make sure you select enough traits to perform CTL. Your collection needs to contain at least 2 different traits. You provided {{request.form['trait_list'].split(',')|length}} traits as input.
+  </div>
+  {% else %}
   <h1>CTL analysis parameters</h1>
-  {{(request.form['trait_list'].split(',')|length -1)}} phenotypes as input
+  {{(request.form['trait_list'].split(',')|length -1)}} traits as input
 
-<form action="/ctl_results" method="post" class="form-horizontal">
-	<input type="hidden" name="trait_list" id="trait_list" value= "{{request.form['trait_list']}}">
+  <form action="/ctl_results" method="post" class="form-horizontal">
+    <input type="hidden" name="trait_list" id="trait_list" value= "{{request.form['trait_list']}}">
+    <div class="dropdown">
+      <label for="Strategy">Strategy</label>
+      <div class="col-sm-10">
+        <select  name="strategy" id="strategy">
+          <option value="Exact">Exact</option>
+          <option value="Full">Full</option>
+          <option value="Pairwise">Pairwise</option>
+        </select>
+      </div>
+    </div>
 
-	<div class="dropdown">
-		<label for="Strategy">Strategy</label>
-		<div class="col-sm-10">
-		<select  name="strategy" id="strategy">
-			<option value="Exact">Exact</option>
-			<option value="Full">Full</option>
-			<option value="Pairwise">Pairwise</option>
-		</select>
-		</div>
-	</div>
+    <div class="dropdown">
+      <label for="Permutations">Number of permutation (Used when strategy is Full or Pairwise)</label>
+      <div class="col-sm-10">
+        <select  name="nperm" id="nperm">
+          <option value="100">100</option>
+          <option value="1000" selected="selected">1000</option>
+          <option value="10000">10000</option>
+        </select>
+      </div>
+    </div>
 
-	<div class="dropdown">
-		<label for="Permutations">Number of permutation (Used when strategy is Full or Pairwise)</label>
-		<div class="col-sm-10">
-		<select  name="nperm" id="nperm">
-			<option value="100">100</option>
-			<option value="1000" selected="selected">1000</option>
-			<option value="10000">10000</option>
-		</select>
-		</div>
-	</div>
-
-	<div class="dropdown">
-		<label for="Coefficient">Type of correlation coefficient</label>
-		<div class="col-sm-10">
-		<select  name="parametric" id="parametric">
-			<option value="False">Spearman</option>
-			<option value="True">Pearson</option>
-		</select>
-		</div>
-	</div>
-
-
-	<div class="dropdown">
-		<label for="Significance">Significance level</label>
-		<div class="col-sm-10">
-		<select  name="significance" id="significance">
-			<option value="0.1">0.1</option>
-			<option value="0.05" selected="selected">0.05</option>
-			<option value="0.001">0.001</option>
-		</select>
-		</div>
-	</div>
-  <br>
-	<div class="form-group">
-		<div class="col-sm-10">
-			<input type="submit" class="btn btn-primary" value="Run CTL using these settings" />
-		</div>
-	</div>
+    <div class="dropdown">
+      <label for="Coefficient">Type of correlation coefficient</label>
+      <div class="col-sm-10">
+        <select  name="parametric" id="parametric">
+          <option value="False">Spearman</option>
+          <option value="True">Pearson</option>
+        </select>
+      </div>
+    </div>
 
+    <div class="dropdown">
+      <label for="Significance">Significance level</label>
+      <div class="col-sm-10">
+        <select  name="significance" id="significance">
+          <option value="0.1">0.1</option>
+          <option value="0.05" selected="selected">0.05</option>
+          <option value="0.001">0.001</option>
+        </select>
+      </div>
+    </div>
+    <br>
+    <div class="form-group">
+      <div class="col-sm-10">
+        <input type="submit" class="btn btn-primary" value="Run CTL using these settings" />
+      </div>
+    </div>
 </form>
-
+{% endif %}
 </div>
 {% endblock %}
diff --git a/wqflask/wqflask/templates/error.html b/wqflask/wqflask/templates/error.html
new file mode 100644
index 00000000..7ab2bf2f
--- /dev/null
+++ b/wqflask/wqflask/templates/error.html
@@ -0,0 +1,61 @@
+{% extends "base.html" %}
+{% block title %}Error: {{message}}{% endblock %}
+{% block content %}
+<!-- Start of body -->
+
+<div class="col-md-8">
+<div class="form-group has-error">
+  <div class="control-label" for="inputError1">
+
+    <img src="/static/gif/error/{{ error_image }}">
+
+    <h1>ERROR</h1>
+
+    <p>
+      This error is not what we wanted to see. Unfortunately errors
+      are part of all software systems and we need to resolve this
+      together.
+    </p>
+    <p>
+      <b>It is important to report this ERROR so we can fix it for everyone</b>.
+    </p>
+
+    <p>
+      Report to the GeneNetwork team by recording the steps you take
+      to reproduce this ERROR. Next to those steps, copy-paste below
+      stack trace, either as
+      a <a href="https://github.com/genenetwork/genenetwork2/issues/new">new
+      issue</a> or E-mail this full page to one of the developers
+      directly.
+    </p>
+  </div>
+
+  <p>
+  (GeneNetwork error: {{message[:128]}})
+  </p>
+
+  <pre>
+    {{ stack[0] }}
+    {{ message }} (error)
+    {{ stack[-3] }}
+    {{ stack[-2] }}
+  </pre>
+
+  <p>
+    To check if this already a known issue, search the
+    <a href="https://github.com/genenetwork/genenetwork2/issues">issue
+      tracker</a>.
+  </p>
+
+  <a href="#Stack" class="btn btn-default" data-toggle="collapse">Toggle full stack trace</a>
+  <div id="Stack" class="collapse">
+    <pre>
+      {% for line in stack %} {{ line }}
+      {% endfor %}
+    </pre>
+  </div>
+</div>
+</div>
+
+
+{% endblock %}
diff --git a/wqflask/wqflask/templates/show_trait_mapping_tools.html b/wqflask/wqflask/templates/show_trait_mapping_tools.html
index 3d9c2521..0f293942 100644
--- a/wqflask/wqflask/templates/show_trait_mapping_tools.html
+++ b/wqflask/wqflask/templates/show_trait_mapping_tools.html
@@ -2,7 +2,7 @@
     {% if (use_pylmm_rqtl and dataset.group.species != "human") or use_plink_gemma %}
     <div class="col-xs-4">
         <div class="tabbable"> <!-- Only required for left/right tabs -->
-        
+
             <ul class="nav nav-pills">
                 {% if use_pylmm_rqtl and not use_plink_gemma and dataset.group.species != "human" %}
                 <li class="active">
@@ -61,7 +61,7 @@
                                     No
                                 </label>
                             </div>
-                        </div> 
+                        </div>
 
 <!--
                         <div class="mapping_method_fields form-group">
@@ -75,7 +75,7 @@
 
                         <div class="mapping_method_fields form-group">
                             <label style="text-align:left;" class="col-xs-12 control-label">Display Additive Effect</label>
-                            <div class="col-xs-12 controls" id="display_additive_effect">                      
+                            <div class="col-xs-12 controls" id="display_additive_effect">
                                 <label class="radio-inline">
                                     <input type="radio" name="display_additive" id="display_additive" value="yes" checked="">
                                     Yes
@@ -90,8 +90,8 @@
 
 
                         <div class="mapping_method_fields form-group">
-                            <label style="text-align:left;" class="col-xs-12 control-label">Marker Regr.</label>
-                            <div class="col-xs-12 controls">                      
+                            <label style="text-align:left;" class="col-xs-3 control-label">Marker<br>Regression</label>
+                            <div style="margin-left: 20px;" class="col-xs-4 controls">
                                 <label class="radio-inline">
                                     <input type="radio" name="manhattan_plot_reaper" value="True">
                                     Yes
@@ -146,7 +146,7 @@
 
                         <div class="mapping_method_fields form-group">
                             <label style="text-align:left;" class="col-xs-12 control-label">Manhattan Plot</label>
-                            <div class="col-xs-12 controls">                      
+                            <div class="col-xs-12 controls">
                                 <label class="radio-inline">
                                     <input type="radio" name="manhattan_plot_pylmm" value="True">
                                     Yes
@@ -167,7 +167,7 @@
                     </div>
                 </div>
                 <div class="tab-pane" id="rqtl_geno">
-                    
+
                     <div style="margin-top: 20px" class="form-horizontal">
                         <div class="mapping_method_fields form-group">
                             <label for="mapping_permutations" class="col-xs-3 control-label">Permutations</label>
@@ -226,7 +226,7 @@
 
                         <div class="mapping_method_fields form-group">
                             <label style="text-align:left;" class="col-xs-12 control-label">Pair Scan</label>
-                            <div class="col-xs-12 controls">                      
+                            <div class="col-xs-12 controls">
                                 <label class="radio-inline">
                                     <input type="radio" name="pair_scan" value="true">
                                     Yes
@@ -240,7 +240,7 @@
 
                         <div class="mapping_method_fields form-group">
                             <label style="text-align:left;" class="col-xs-12 control-label">Manhattan Plot</label>
-                            <div class="col-xs-12 controls">                      
+                            <div class="col-xs-12 controls">
                                 <label class="radio-inline">
                                     <input type="radio" name="manhattan_plot_rqtl" value="True">
                                     Yes
@@ -272,7 +272,7 @@
                             </div>
                         </div>
                     </div>
-                    
+
                     <div class="form-group">
                         <label for="plink_compute" class="col-xs-1 control-label"></label>
                         <div style="margin-left:20px;" class="col-xs-4 controls">
@@ -282,7 +282,7 @@
                         </div>
                     </div>
                 </div>
-                
+
                 <div class="tab-pane" id="gemma">
                     <div style="padding: 20px" class="form-horizontal">
                         <div class="mapping_method_fields form-group">
@@ -292,7 +292,7 @@
                             </div>
                         </div>
                     </div>
-                    
+
                     <div class="form-group">
                         <label for="gemma_compute" class="col-xs-1 control-label"></label>
                         <div style="margin-left:20px;" class="col-xs-4 controls">
@@ -301,12 +301,12 @@
                             </button>
                         </div>
                     </div>
-                </div>                
+                </div>
                 {% endif %}
             </div>
         </div>
     </div>
-    <div class="col-xs-6"> 
+    <div class="col-xs-6">
         <dl>
              <dt>Interval Mapping</dt>
              <dd>Interval mapping is a process in which the statistical significance of a hypothetical QTL is evaluated at regular points across a chromosome, even in the absence of explicit genotype data at those points.</dd>
diff --git a/wqflask/wqflask/templates/wgcna_setup.html b/wqflask/wqflask/templates/wgcna_setup.html
index b4a5730d..c5461497 100644
--- a/wqflask/wqflask/templates/wgcna_setup.html
+++ b/wqflask/wqflask/templates/wgcna_setup.html
@@ -4,49 +4,46 @@
 {% block content %} <!-- Start of body -->
 <h1> WGCNA analysis parameters</h1>
 <div class="container">
-		{% if request.form['trait_list'].split(",")|length <= 4 %}
-
-		<div class="alert alert-danger" role="alert">
-				<span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
-				<span class="sr-only">Error:</span>
-				<h2>
-						Too few phenotypes as input
-				</h2>
-				Please make sure you select enough phenotypes / genes to perform WGCNA, your collection needs to contain at least 4 different phenotypes. You provided {{request.form['trait_list'].split(',')|length}} phenotypes as input
-		</div>
-    {% else %}
- 		<form action="/wgcna_results" method="post" class="form-horizontal">
-				<input type="hidden" name="trait_list" id="trait_list" value= "{{request.form['trait_list']}}">
-				<div class="form-group">
-					<label for="SoftThresholds"> Soft threshold: </label>
-					<div class="col-sm-10">
-						<input type="text" class="form-inline" name="SoftThresholds" id="SoftThresholds" value="1,2,3,4,5,6,7,8,9">
-					</div>
-				</div>
-				<div class="form-group">
-					<label for="MinModuleSize"> Minimum module size: </label>
-					<div class="col-sm-10">
-						<input type="text" class="form-inline" name="MinModuleSize" id="MinModuleSize" value="30">
-					</div>
-				</div>
-				<div class="form-group">
-					<label for="TOMtype"> TOMtype: </label>
-					<div class="col-sm-10">
-						<input type="text" class="form-inline" name="TOMtype" id="TOMtype" value="unsigned">
-					</div>
-				</div>
-				<div class="form-group">
-					<label for="mergeCutHeight"> mergeCutHeight: </label>
-					<div class="col-sm-10">
-						<input type="text" class="form-inline" name="mergeCutHeight" id="mergeCutHeight" value="0.25">
-					</div>
-				</div>
-				<div class="form-group">
-					<div class="col-sm-10">
-						<input type="submit" class="btn btn-primary" value="Run WGCNA using these settings" />
-					</div>
-				</div>
-		</form>
-		{% endif %}
+  {% if request.form['trait_list'].split(",")|length < 4 %}
+  <div class="alert alert-danger" role="alert">
+    <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
+    <span class="sr-only">Error:</span>
+    <h2>Too few phenotypes as input</h2>
+    Please make sure you select enough phenotypes / genes to perform WGCNA. Your collection needs to contain at least 4 different phenotypes. You provided {{request.form['trait_list'].split(',')|length}} phenotypes as input.
+  </div>
+  {% else %}
+  <form action="/wgcna_results" method="post" class="form-horizontal">
+    <input type="hidden" name="trait_list" id="trait_list" value= "{{request.form['trait_list']}}">
+    <div class="form-group">
+      <label for="SoftThresholds"> Soft threshold: </label>
+      <div class="col-sm-10">
+        <input type="text" class="form-inline" name="SoftThresholds" id="SoftThresholds" value="1,2,3,4,5,6,7,8,9">
+      </div>
+    </div>
+    <div class="form-group">
+      <label for="MinModuleSize"> Minimum module size: </label>
+      <div class="col-sm-10">
+        <input type="text" class="form-inline" name="MinModuleSize" id="MinModuleSize" value="30">
+      </div>
+    </div>
+    <div class="form-group">
+      <label for="TOMtype"> TOMtype: </label>
+      <div class="col-sm-10">
+        <input type="text" class="form-inline" name="TOMtype" id="TOMtype" value="unsigned">
+      </div>
+    </div>
+    <div class="form-group">
+      <label for="mergeCutHeight"> mergeCutHeight: </label>
+      <div class="col-sm-10">
+        <input type="text" class="form-inline" name="mergeCutHeight" id="mergeCutHeight" value="0.25">
+      </div>
+    </div>
+    <div class="form-group">
+      <div class="col-sm-10">
+        <input type="submit" class="btn btn-primary" value="Run WGCNA using these settings" />
+      </div>
+    </div>
+  </form>
+  {% endif %}
 </div>
 {% endblock %}
diff --git a/wqflask/wqflask/views.py b/wqflask/wqflask/views.py
index 7061e0f1..33fab84d 100644
--- a/wqflask/wqflask/views.py
+++ b/wqflask/wqflask/views.py
@@ -4,6 +4,11 @@
 
 from __future__ import absolute_import, division, print_function
 
+import traceback # for error page
+import os        # for error gifs
+import random    # for random error gif
+import datetime  # for errors
+import time      # for errors
 import sys
 import csv
 import xlsxwriter
@@ -26,7 +31,7 @@ import base64
 import array
 import sqlalchemy
 from wqflask import app
-from flask import g, Response, request, render_template, send_from_directory, jsonify, redirect
+from flask import g, Response, request, make_response, render_template, send_from_directory, jsonify, redirect
 from wqflask import search_results
 from wqflask import gsearch
 from wqflask import update_search_results
@@ -58,6 +63,8 @@ from wqflask import user_manager
 from wqflask import collect
 from wqflask.database import db_session
 
+import werkzeug
+
 import utility.logger
 logger = utility.logger.getLogger(__name__ )
 
@@ -82,6 +89,31 @@ def shutdown_session(exception=None):
 #    from wqflask import tracer
 #    tracer.turn_on()
 
+@app.errorhandler(Exception)
+def handle_bad_request(e):
+    err_msg = str(e)
+    logger.error(err_msg)
+    logger.error(request.url)
+    # get the stack trace and send it to the logger
+    exc_type, exc_value, exc_traceback = sys.exc_info()
+    logger.error(traceback.format_exc())
+    now = datetime.datetime.utcnow()
+    time_str = now.strftime('%l:%M%p UTC %b %d, %Y')
+    formatted_lines = [request.url + " ("+time_str+")"]+traceback.format_exc().splitlines()
+
+    # Handle random animations
+    # Use a cookie to have one animation on refresh
+    animation = request.cookies.get(err_msg[:32])
+    if not animation:
+        list = [fn for fn in os.listdir("./wqflask/static/gif/error") if fn.endswith(".gif") ]
+        animation = random.choice(list)
+
+    resp = make_response(render_template("error.html",message=err_msg,stack=formatted_lines,error_image=animation))
+
+    # logger.error("Set cookie %s with %s" % (err_msg, animation))
+    resp.set_cookie(err_msg[:32],animation)
+    return resp
+
 @app.route("/")
 def index_page():
     logger.info("Sending index_page")