about summary refs log tree commit diff
path: root/wqflask/base
diff options
context:
space:
mode:
authorZachary Sloan2013-01-11 23:59:41 +0000
committerZachary Sloan2013-01-11 23:59:41 +0000
commit2b2970d167c5b555e0e0ad0b34b72f817c1fac91 (patch)
tree36fa8c708138fff03593e0f50cc933bcb62b5592 /wqflask/base
parent1db9237a05fd27c80dc963db9916072594156198 (diff)
parentd39b691994a395c45fa242de6d64d12a5470af10 (diff)
downloadgenenetwork2-2b2970d167c5b555e0e0ad0b34b72f817c1fac91.tar.gz
Merge branch 'flask' of http://github.com/zsloan/genenetwork
Diffstat (limited to 'wqflask/base')
-rwxr-xr-xwqflask/base/GeneralObject.py68
-rw-r--r--wqflask/base/JinjaPage.py28
-rwxr-xr-xwqflask/base/__init__.py0
-rwxr-xr-xwqflask/base/admin.py86
-rwxr-xr-xwqflask/base/cgiData.py66
-rwxr-xr-xwqflask/base/cookieData.py49
-rwxr-xr-xwqflask/base/data_set.py743
-rwxr-xr-xwqflask/base/footer.py6
-rwxr-xr-xwqflask/base/header.py6
-rwxr-xr-xwqflask/base/indexBody.py290
-rwxr-xr-xwqflask/base/myCookie.py51
-rwxr-xr-xwqflask/base/sessionData.py50
-rw-r--r--wqflask/base/species.py117
-rwxr-xr-xwqflask/base/template.py123
-rwxr-xr-xwqflask/base/templatePage.py222
-rwxr-xr-xwqflask/base/trait.py564
-rwxr-xr-xwqflask/base/webqtlCaseData.py72
-rwxr-xr-xwqflask/base/webqtlConfig.py73
-rwxr-xr-xwqflask/base/webqtlConfigLocal.py20
-rwxr-xr-xwqflask/base/webqtlFormData.py358
20 files changed, 2992 insertions, 0 deletions
diff --git a/wqflask/base/GeneralObject.py b/wqflask/base/GeneralObject.py
new file mode 100755
index 00000000..53d1357b
--- /dev/null
+++ b/wqflask/base/GeneralObject.py
@@ -0,0 +1,68 @@
+# Copyright (C) University of Tennessee Health Science Center, Memphis, TN.
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License
+# as published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU Affero General Public License for more details.
+#
+# This program is available from Source Forge: at GeneNetwork Project
+# (sourceforge.net/projects/genenetwork/).
+#
+# 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
+#
+# Last updated by GeneNetwork Core Team 2010/10/20
+
+class GeneralObject:
+    """
+    Base class to define an Object.
+    a = [Spam(1, 4), Spam(9, 3), Spam(4,6)]
+    a.sort(lambda x, y: cmp(x.eggs, y.eggs))
+    """
+
+    def __init__(self, *args, **kw):
+        self.contents = list(args)
+        for name, value in kw.items():
+            setattr(self, name, value)
+
+    def __setitem__(self, key, value):
+        setattr(self, key, value)
+
+    def __getitem__(self, key):
+        return getattr(self, key)
+
+    def __getattr__(self, key):
+        if key in self.__dict__.keys():
+            return self.__dict__[key]
+        else:
+            return eval("self.__dict__.%s" % key)
+
+    def __len__(self):
+        return len(self.__dict__) - 1
+
+    def __str__(self):
+        s = ''
+        for key in self.__dict__.keys():
+            if key != 'contents':
+                s += '%s = %s\n' % (key,self.__dict__[key])
+        return s
+
+    def __repr__(self):
+        s = ''
+        for key in self.__dict__.keys():
+            s += '%s = %s\n' % (key,self.__dict__[key])
+        return s
+
+    def __cmp__(self,other):
+        return len(self.__dict__.keys()).__cmp__(len(other.__dict__.keys()))
diff --git a/wqflask/base/JinjaPage.py b/wqflask/base/JinjaPage.py
new file mode 100644
index 00000000..07e485b1
--- /dev/null
+++ b/wqflask/base/JinjaPage.py
@@ -0,0 +1,28 @@
+import logging
+logging.basicConfig(filename="/tmp/gn_log", level=logging.INFO)
+_log = logging.getLogger("search")
+
+from pprint import pformat as pf
+
+import templatePage
+
+from utility import formatting
+
+import jinja2
+JinjaEnv = jinja2.Environment(loader=jinja2.FileSystemLoader('/gnshare/gn/web/webqtl/templates'))
+JinjaEnv.globals['numify'] = formatting.numify
+
+
+class JinjaPage(templatePage.templatePage):
+    """Class derived from our regular templatePage, but uses Jinja2 instead.
+
+    When converting pages from Python generated templates, change the base class from templatePage
+    to JinjaPage
+
+    """
+
+
+    def write(self):
+        """We override the base template write so we can use Jinja2."""
+        _log.info(pf(self.__dict__))
+        return self.jtemplate.render(**self.__dict__)
diff --git a/wqflask/base/__init__.py b/wqflask/base/__init__.py
new file mode 100755
index 00000000..e69de29b
--- /dev/null
+++ b/wqflask/base/__init__.py
diff --git a/wqflask/base/admin.py b/wqflask/base/admin.py
new file mode 100755
index 00000000..1ba75117
--- /dev/null
+++ b/wqflask/base/admin.py
@@ -0,0 +1,86 @@
+# Copyright (C) University of Tennessee Health Science Center, Memphis, TN.
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License
+# as published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU Affero General Public License for more details.
+#
+# This program is available from Source Forge: at GeneNetwork Project
+# (sourceforge.net/projects/genenetwork/).
+#
+# 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
+#
+# Last updated by GeneNetwork Core Team 2010/10/20
+
+
+
+
+
+#XZ, 04/02/2009: we should put this into database.
+
+
+###LIST of databases used into gene name query
+
+
+ADMIN_search_dbs = {
+                    'rat': {'PERITONEAL FAT': ['FT_2A_0605_Rz'],
+                            'KIDNEY': ['KI_2A_0405_Rz'],
+                            'ADRENAL GLAND': ['HXB_Adrenal_1208'],
+                            'HEART': ['HXB_Heart_1208']
+                           },
+                    'mouse': {'CEREBELLUM': ['CB_M_0305_R'],
+                              'STRIATUM': ['SA_M2_0905_R', 'SA_M2_0405_RC', 'UTHSC_1107_RankInv', 'Striatum_Exon_0209'],
+                              'HIPPOCAMPUS': ['HC_M2_0606_R', 'UMUTAffyExon_0209_RMA'],
+                              'WHOLE BRAIN': ['BR_M2_1106_R', 'IBR_M_0106_R', 'BRF2_M_0805_R', 'UCLA_BHF2_BRAIN_0605'],
+                              'LIVER': ['LV_G_0106_B', 'UCLA_BHF2_LIVER_0605'],
+                              'EYE': ['Eye_M2_0908_R'],
+                              'HEMATOPOIETIC STEM CELLS': ['HC_U_0304_R'],
+                              'KIDNEY': ['MA_M2_0806_R'],
+                              'MAMMARY TUMORS': ['MA_M_0704_R', 'NCI_Agil_Mam_Tum_RMA_0409'],
+                              'PREFRONTAL CORTEX': ['VCUSal_1206_R'],
+                              'SPLEEN': ['IoP_SPL_RMA_0509'],
+                              'NUCLEUS ACCUMBENS': ['VCUSalo_1007_R'],
+                              'NEOCORTEX': ['HQFNeoc_0208_RankInv'],
+                              'ADIPOSE': ['UCLA_BHF2_ADIPOSE_0605'],
+                              'RETINA': ['Illum_Retina_BXD_RankInv0410']
+                             },
+                    'human': {
+                              'LYMPHOBLAST B CELL': ['Human_1008', 'UT_CEPH_RankInv0909'],
+                              'WHOLE BRAIN': ['GSE5281_F_RMA0709', 'GSE15222_F_RI_0409']
+                             }
+                   }
+
+
+###LIST of tissue alias
+
+ADMIN_tissue_alias = {'CEREBELLUM': ['Cb'],
+                      'STRIATUM': ['Str'],
+                      'HIPPOCAMPUS': ['Hip'],
+                      'WHOLE BRAIN': ['Brn'],
+                      'LIVER': ['Liv'],
+                      'EYE': ['Eye'],
+                      'PERITONEAL FAT': ['Fat'],
+                      'HEMATOPOIETIC STEM CELLS': ['Hsc'],
+                      'KIDNEY': ['Kid'],
+                      'ADRENAL GLAND': ['Adr'],
+                      'HEART': ['Hea'],
+                      'MAMMARY TUMORS': ['Mam'],
+                      'PREFRONTAL CORTEX': ['Pfc'],
+                      'SPLEEN': ['Spl'],
+                      'NUCLEUS ACCUMBENS': ['Nac'],
+                      'NEOCORTEX': ['Ctx'],
+                      'ADIPOSE': ['Wfat'],
+                      'RETINA': ['Ret']
+                     }
diff --git a/wqflask/base/cgiData.py b/wqflask/base/cgiData.py
new file mode 100755
index 00000000..155b3ec3
--- /dev/null
+++ b/wqflask/base/cgiData.py
@@ -0,0 +1,66 @@
+# Copyright (C) University of Tennessee Health Science Center, Memphis, TN.
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License
+# as published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU Affero General Public License for more details.
+#
+# This program is available from Source Forge: at GeneNetwork Project
+# (sourceforge.net/projects/genenetwork/).
+#
+# 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
+#
+# Last updated by GeneNetwork Core Team 2010/10/20
+
+#########################################
+#convert Field storage object to Dict object
+#in order to be able to saved into a session file
+#########################################
+
+class cgiData(dict):
+    '''convert Field storage object to Dict object
+       Filed storage object cannot be properly dumped
+    '''
+
+    def __init__(self, field_storage=None):
+
+        if not field_storage:
+            field_storage={}
+
+        for key in field_storage.keys():
+            temp = field_storage.getlist(key)
+            if len(temp) > 1:
+                temp = map(self.toValue, temp)
+            elif len(temp) == 1:
+                temp = self.toValue(temp[0])
+            else:
+                temp = None
+            self[key]= temp
+
+    def toValue(self, obj):
+        '''fieldstorge returns different type of objects, \
+                need to convert to string or None'''
+        try:
+            return obj.value
+        except:
+            return ""
+
+    def getvalue(self, k, default= None):
+        try:
+            return self[k]
+        except:
+            return default
+
+    getfirst = getvalue
diff --git a/wqflask/base/cookieData.py b/wqflask/base/cookieData.py
new file mode 100755
index 00000000..eeb7c0cf
--- /dev/null
+++ b/wqflask/base/cookieData.py
@@ -0,0 +1,49 @@
+# Copyright (C) University of Tennessee Health Science Center, Memphis, TN.
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License
+# as published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU Affero General Public License for more details.
+#
+# This program is available from Source Forge: at GeneNetwork Project
+# (sourceforge.net/projects/genenetwork/).
+#
+# 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
+#
+# Last updated by GeneNetwork Core Team 2010/10/20
+
+#########################################
+#convert mod_python object to Dict object
+#in order to be able to be pickled
+#########################################
+
+class cookieData(dict):
+    'convert mod python Cookie object to Dict object'
+
+    def __init__(self, cookies=None):
+
+        if not cookies:
+            cookies={}
+
+        for key in cookies.keys():
+            self[key.lower()]= cookies[key].value
+
+    def getvalue(self, k, default= None):
+        try:
+            return self[k.lower()]
+        except:
+            return default
+
+    getfirst = getvalue
diff --git a/wqflask/base/data_set.py b/wqflask/base/data_set.py
new file mode 100755
index 00000000..50ef8f57
--- /dev/null
+++ b/wqflask/base/data_set.py
@@ -0,0 +1,743 @@
+# Copyright (C) University of Tennessee Health Science Center, Memphis, TN.
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License
+# as published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU Affero General Public License for more details.
+#
+# This program is available from Source Forge: at GeneNetwork Project
+# (sourceforge.net/projects/genenetwork/).
+#
+# Contact Drs. Robert W. Williams and Xiaodong Zhou (2010)
+# at rwilliams@uthsc.edu and xzhou15@uthsc.edu
+#
+#we
+#
+# This module is used by GeneNetwork project (www.genenetwork.org)
+
+from __future__ import absolute_import, print_function, division
+import os
+
+from flask import Flask, g
+
+from htmlgen import HTMLgen2 as HT
+
+import reaper
+
+from base import webqtlConfig
+from base import species
+from dbFunction import webqtlDatabaseFunction
+from utility import webqtlUtil
+
+from MySQLdb import escape_string as escape
+from pprint import pformat as pf
+
+# Used by create_database to instantiate objects
+DS_NAME_MAP = {}
+
+def create_dataset(dataset_name):
+    #cursor = db_conn.cursor()
+    print("dataset_name:", dataset_name)
+
+    query = """
+        SELECT DBType.Name
+        FROM DBList, DBType
+        WHERE DBList.Name = '%s' and
+              DBType.Id = DBList.DBTypeId
+        """ % (escape(dataset_name))
+    print("query is: ", pf(query))
+    dataset_type = g.db.execute(query).fetchone().Name
+
+    #dataset_type = cursor.fetchone()[0]
+    print("[blubber] dataset_type:", pf(dataset_type))
+
+    dataset_ob = DS_NAME_MAP[dataset_type]
+    #dataset_class = getattr(data_set, dataset_ob)
+    print("dataset_ob:", dataset_ob)
+    print("DS_NAME_MAP:", pf(DS_NAME_MAP))
+
+    dataset_class = globals()[dataset_ob]
+    return dataset_class(dataset_name)
+
+
+class DatasetGroup(object):
+    """
+    Each group has multiple datasets; each species has multiple groups.
+    
+    For example, Mouse has multiple groups (BXD, BXA, etc), and each group
+    has multiple datasets associated with it.
+    
+    """
+    def __init__(self, dataset):
+        """This sets self.group and self.group_id"""
+        self.name, self.id = g.db.execute(dataset.query_for_group).fetchone()
+        if self.name == 'BXD300':
+            self.name = "BXD"
+        
+        self.species = webqtlDatabaseFunction.retrieve_species(self.name)
+        
+        self.incparentsf1 = False
+        self.f1list = None
+        self.parlist = None
+        self.allsamples = None
+
+
+    #def read_genotype(self):
+    #    self.read_genotype_file()
+    #
+    #    if not self.genotype:   # Didn'd succeed, so we try method 2
+    #        self.read_genotype_data()
+            
+    def read_genotype_file(self):
+        '''read genotype from .geno file instead of database'''
+        #if self.group == 'BXD300':
+        #    self.group = 'BXD'
+        #
+        #assert self.group, "self.group needs to be set"
+
+        #genotype_1 is Dataset Object without parents and f1
+        #genotype_2 is Dataset Object with parents and f1 (not for intercross)
+
+        genotype_1 = reaper.Dataset()
+        
+        # reaper barfs on unicode filenames, so here we ensure it's a string
+        full_filename = str(os.path.join(webqtlConfig.GENODIR, self.name + '.geno'))
+        genotype_1.read(full_filename)
+
+        print("Got to after read")
+
+        try:
+            # NL, 07/27/2010. ParInfo has been moved from webqtlForm.py to webqtlUtil.py;
+            f1, f12, maternal, paternal = webqtlUtil.ParInfo[self.name]
+        except KeyError:
+            f1 = f12 = maternal = paternal = None
+
+
+        if genotype_1.type == "group" and maternal and paternal:
+            genotype_2 = genotype_1.add(Mat=maternal, Pat=paternal)       #, F1=_f1)
+        else:
+            genotype_2 = genotype_1
+
+        #determine default genotype object
+        if self.incparentsf1 and genotype_1.type != "intercross":
+            self.genotype = genotype_2
+        else:
+            self.incparentsf1 = 0
+            self.genotype = genotype_1
+
+        self.samplelist = list(self.genotype.prgy)
+
+        if f1 and f12:
+            self.f1list = [f1, f12]
+        if maternal and paternal:
+            self.parlist = [maternal, paternal]
+
+
+class DataSet(object):
+    """
+    DataSet class defines a dataset in webqtl, can be either Microarray,
+    Published phenotype, genotype, or user input dataset(temp)
+
+    """
+
+    def __init__(self, name):
+
+        assert name, "Need a name"
+        self.name = name
+        self.id = None
+        self.type = None
+
+        self.setup()
+
+        self.check_confidentiality()
+
+        self.retrieve_other_names()
+        
+        self.group = DatasetGroup(self)   # sets self.group and self.group_id and gets genotype
+        self.species = species.TheSpecies(self)
+    
+       
+        
+    def get_desc(self):
+        """Gets overridden later, at least for Temp...used by trait's get_given_name"""
+        return None
+    
+    #@staticmethod
+    #def get_by_trait_id(trait_id):
+    #    """Gets the dataset object given the trait id"""
+    #    
+    #    
+    #
+    #    name = g.db.execute(""" SELECT 
+    #                        
+    #                        """)
+    #    
+    #    return DataSet(name)
+
+    # Delete this eventually
+    @property
+    def riset():
+        Weve_Renamed_This_As_Group
+        
+        
+    #@property
+    #def group(self):
+    #    if not self._group:
+    #        self.get_group()
+    #        
+    #    return self._group
+
+
+
+    def retrieve_other_names(self):
+        """
+        If the data set name parameter is not found in the 'Name' field of the data set table,
+        check if it is actually the FullName or ShortName instead.
+
+        This is not meant to retrieve the data set info if no name at all is passed.
+
+        """
+
+        query_args = tuple(escape(x) for x in (
+            (self.type + "Freeze"),
+            str(webqtlConfig.PUBLICTHRESH),
+            self.name,
+            self.name,
+            self.name))
+        print("query_args are:", query_args)
+
+        print("""
+                SELECT Id, Name, FullName, ShortName
+                FROM %s
+                WHERE public > %s AND
+                     (Name = '%s' OR FullName = '%s' OR ShortName = '%s')
+          """ % (query_args))
+
+        self.id, self.name, self.fullname, self.shortname = g.db.execute("""
+                SELECT Id, Name, FullName, ShortName
+                FROM %s
+                WHERE public > %s AND
+                     (Name = '%s' OR FullName = '%s' OR ShortName = '%s')
+          """ % (query_args)).fetchone()
+
+        #self.cursor.execute(query)
+        #self.id, self.name, self.fullname, self.shortname = self.cursor.fetchone()
+
+
+    #def genHTML(self, Class='c0dd'):
+    #    return  HT.Href(text = HT.Span('%s Database' % self.fullname, Class= "fwb " + Class),
+    #            url= webqtlConfig.INFOPAGEHREF % self.name,target="_blank")
+
+class PhenotypeDataSet(DataSet):
+    DS_NAME_MAP['Publish'] = 'PhenotypeDataSet'
+
+    def setup(self):
+        # Fields in the database table
+        self.search_fields = ['Phenotype.Post_publication_description',
+                            'Phenotype.Pre_publication_description',
+                            'Phenotype.Pre_publication_abbreviation',
+                            'Phenotype.Post_publication_abbreviation',
+                            'Phenotype.Lab_code',
+                            'Publication.PubMed_ID',
+                            'Publication.Abstract',
+                            'Publication.Title',
+                            'Publication.Authors',
+                            'PublishXRef.Id']
+
+        # Figure out what display_fields is
+        self.display_fields = ['name',
+                               'pubmed_id',
+                               'pre_publication_description',
+                               'post_publication_description',
+                               'original_description',
+                               'pre_publication_abbreviation',
+                               'post_publication_abbreviation',
+                               'lab_code',
+                               'submitter', 'owner',
+                               'authorized_users',
+                               'authors', 'title',
+                               'abstract', 'journal',
+                               'volume', 'pages',
+                               'month', 'year',
+                               'sequence', 'units', 'comments']
+
+        # Fields displayed in the search results table header
+        self.header_fields = ['',
+                            'ID',
+                            'Description',
+                            'Authors',
+                            'Year',
+                            'Max LRS',
+                            'Max LRS Location']
+
+        self.type = 'Publish'
+
+        self.query_for_group = '''
+                            SELECT
+                                    InbredSet.Name, InbredSet.Id
+                            FROM
+                                    InbredSet, PublishFreeze
+                            WHERE
+                                    PublishFreeze.InbredSetId = InbredSet.Id AND
+                                    PublishFreeze.Name = "%s"
+                    ''' % escape(self.name)
+
+    def check_confidentiality(self):
+        # (Urgently?) Need to write this
+        pass
+
+    def get_trait_info(self, trait_list, species = ''):
+        for this_trait in trait_list:
+            if not this_trait.haveinfo:
+                this_trait.retrieveInfo(QTL=1)
+
+            description = this_trait.post_publication_description
+            if this_trait.confidential:
+                continue   # for now
+                if not webqtlUtil.hasAccessToConfidentialPhenotypeTrait(privilege=self.privilege, userName=self.userName, authorized_users=this_trait.authorized_users):
+                    description = this_trait.pre_publication_description
+            this_trait.description_display = description
+
+            if not this_trait.year.isdigit():
+                this_trait.pubmed_text = "N/A"
+
+            if this_trait.pubmed_id:
+                this_trait.pubmed_link = webqtlConfig.PUBMEDLINK_URL % this_trait.pubmed_id
+
+            #LRS and its location
+            this_trait.LRS_score_repr = "N/A"
+            this_trait.LRS_score_value = 0
+            this_trait.LRS_location_repr = "N/A"
+            this_trait.LRS_location_value = 1000000
+
+            if this_trait.lrs:
+                result = g.db.execute("""
+                    select Geno.Chr, Geno.Mb from Geno, Species
+                    where Species.Name = %s and
+                        Geno.Name = %s and
+                        Geno.SpeciesId = Species.Id
+                """, (species, this_trait.locus)).fetchone()
+                #result = self.cursor.fetchone()
+
+                if result:
+                    if result[0] and result[1]:
+                        LRS_Chr = result[0]
+                        LRS_Mb = result[1]
+
+                        #XZ: LRS_location_value is used for sorting
+                        try:
+                            LRS_location_value = int(LRS_Chr)*1000 + float(LRS_Mb)
+                        except:
+                            if LRS_Chr.upper() == 'X':
+                                LRS_location_value = 20*1000 + float(LRS_Mb)
+                            else:
+                                LRS_location_value = ord(str(LRS_chr).upper()[0])*1000 + float(LRS_Mb)
+
+                        this_trait.LRS_score_repr = LRS_score_repr = '%3.1f' % this_trait.lrs
+                        this_trait.LRS_score_value = LRS_score_value = this_trait.lrs
+                        this_trait.LRS_location_repr = LRS_location_repr = 'Chr %s: %.4f Mb' % (LRS_Chr, float(LRS_Mb))
+                        
+    def retrieve_sample_data(self, trait):
+        query = """
+                    SELECT
+                            Strain.Name, PublishData.value, PublishSE.error, NStrain.count
+                    FROM
+                            (PublishData, Strain, PublishXRef, PublishFreeze)
+                    left join PublishSE on
+                            (PublishSE.DataId = PublishData.Id AND PublishSE.StrainId = PublishData.StrainId)
+                    left join NStrain on
+                            (NStrain.DataId = PublishData.Id AND
+                            NStrain.StrainId = PublishData.StrainId)
+                    WHERE
+                            PublishXRef.InbredSetId = PublishFreeze.InbredSetId AND
+                            PublishData.Id = PublishXRef.DataId AND PublishXRef.Id = %s AND
+                            PublishFreeze.Id = %d AND PublishData.StrainId = Strain.Id
+                    Order BY
+                            Strain.Name
+                    """ % (trait.name, self.id)
+        results = g.db.execute(query).fetchall()
+        return results
+
+
+class GenotypeDataSet(DataSet):
+    DS_NAME_MAP['Geno'] = 'GenotypeDataSet'
+
+    def setup(self):
+        # Fields in the database table
+        self.search_fields = ['Name',
+                              'Chr']
+
+        # Find out what display_fields is
+        self.display_fields = ['name',
+                               'chr',
+                               'mb',
+                               'source2',
+                               'sequence']
+
+        # Fields displayed in the search results table header
+        self.header_fields = ['',
+                              'ID',
+                              'Location']
+
+        # Todo: Obsolete or rename this field
+        self.type = 'Geno'
+
+        self.query_for_group = '''
+                SELECT
+                        InbredSet.Name, InbredSet.Id
+                FROM
+                        InbredSet, GenoFreeze
+                WHERE
+                        GenoFreeze.InbredSetId = InbredSet.Id AND
+                        GenoFreeze.Name = "%s"
+                ''' % escape(self.name)
+
+    def check_confidentiality(self):
+        return geno_mrna_confidentiality(self)
+
+    def get_trait_info(self, trait_list, species=None):
+        for this_trait in trait_list:
+            if not this_trait.haveinfo:
+                this_trait.retrieveInfo()
+
+            #XZ: trait_location_value is used for sorting
+            trait_location_repr = 'N/A'
+            trait_location_value = 1000000
+
+            if this_trait.chr and this_trait.mb:
+                try:
+                    trait_location_value = int(this_trait.chr)*1000 + this_trait.mb
+                except:
+                    if this_trait.chr.upper() == 'X':
+                        trait_location_value = 20*1000 + this_trait.mb
+                    else:
+                        trait_location_value = ord(str(this_trait.chr).upper()[0])*1000 + this_trait.mb
+
+                this_trait.location_repr = 'Chr%s: %.4f' % (this_trait.chr, float(this_trait.mb) )
+                this_trait.location_value = trait_location_value
+                
+    def retrieve_sample_data(self, trait):
+        query = """
+                    SELECT
+                            Strain.Name, GenoData.value, GenoSE.error, GenoData.Id
+                    FROM
+                            (GenoData, GenoFreeze, Strain, Geno, GenoXRef)
+                    left join GenoSE on
+                            (GenoSE.DataId = GenoData.Id AND GenoSE.StrainId = GenoData.StrainId)
+                    WHERE
+                            Geno.SpeciesId = %s AND Geno.Name = '%s' AND GenoXRef.GenoId = Geno.Id AND
+                            GenoXRef.GenoFreezeId = GenoFreeze.Id AND
+                            GenoFreeze.Name = '%s' AND
+                            GenoXRef.DataId = GenoData.Id AND
+                            GenoData.StrainId = Strain.Id
+                    Order BY
+                            Strain.Name
+                    """ % (webqtlDatabaseFunction.retrieve_species_id(self.group.name), trait.name, self.name)
+        results = g.db.execute(query).fetchall()
+        return results
+
+
+class MrnaAssayDataSet(DataSet):
+    '''
+    An mRNA Assay is a quantitative assessment (assay) associated with an mRNA trait
+
+    This used to be called ProbeSet, but that term only refers specifically to the Affymetrix
+    platform and is far too specific.
+
+    '''
+    DS_NAME_MAP['ProbeSet'] = 'MrnaAssayDataSet'
+
+    def setup(self):
+        # Fields in the database table
+        self.search_fields = ['Name',
+                              'Description',
+                              'Probe_Target_Description',
+                              'Symbol',
+                              'Alias',
+                              'GenbankId',
+                              'UniGeneId',
+                              'RefSeq_TranscriptId']
+
+        # Find out what display_fields is
+        self.display_fields = ['name', 'symbol',
+                               'description', 'probe_target_description',
+                               'chr', 'mb',
+                               'alias', 'geneid',
+                               'genbankid', 'unigeneid',
+                               'omim', 'refseq_transcriptid',
+                               'blatseq', 'targetseq',
+                               'chipid', 'comments',
+                               'strand_probe', 'strand_gene',
+                               'probe_set_target_region',
+                               'probe_set_specificity',
+                               'probe_set_blat_score',
+                               'probe_set_blat_mb_start',
+                               'probe_set_blat_mb_end',
+                               'probe_set_strand',
+                               'probe_set_note_by_rw',
+                               'flag']
+
+        # Fields displayed in the search results table header
+        self.header_fields = ['',
+                             'ID',
+                             'Symbol',
+                             'Description',
+                             'Location',
+                             'Mean Expr',
+                             'Max LRS',
+                             'Max LRS Location']
+
+        # Todo: Obsolete or rename this field
+        self.type = 'ProbeSet'
+
+        self.query_for_group = '''
+                        SELECT
+                                InbredSet.Name, InbredSet.Id
+                        FROM
+                                InbredSet, ProbeSetFreeze, ProbeFreeze
+                        WHERE
+                                ProbeFreeze.InbredSetId = InbredSet.Id AND
+                                ProbeFreeze.Id = ProbeSetFreeze.ProbeFreezeId AND
+                                ProbeSetFreeze.Name = "%s"
+                ''' % escape(self.name)
+
+
+    def check_confidentiality(self):
+        return geno_mrna_confidentiality(self)
+
+    def get_trait_info(self, trait_list=None, species=''):
+
+        #  Note: setting trait_list to [] is probably not a great idea.
+        if not trait_list:
+            trait_list = []
+
+        for this_trait in trait_list:
+
+            if not this_trait.haveinfo:
+                this_trait.retrieveInfo(QTL=1)
+
+            if this_trait.symbol:
+                pass
+            else:
+                this_trait.symbol = "N/A"
+
+            #XZ, 12/08/2008: description
+            #XZ, 06/05/2009: Rob asked to add probe target description
+            description_string = str(this_trait.description).strip()
+            target_string = str(this_trait.probe_target_description).strip()
+
+            description_display = ''
+
+            if len(description_string) > 1 and description_string != 'None':
+                description_display = description_string
+            else:
+                description_display = this_trait.symbol
+
+            if len(description_display) > 1 and description_display != 'N/A' and len(target_string) > 1 and target_string != 'None':
+                description_display = description_display + '; ' + target_string.strip()
+
+            # Save it for the jinja2 template
+            this_trait.description_display = description_display
+            #print("  xxxxdd [%s]: %s" % (type(this_trait.description_display), description_display))
+
+            #XZ: trait_location_value is used for sorting
+            trait_location_repr = 'N/A'
+            trait_location_value = 1000000
+
+            if this_trait.chr and this_trait.mb:
+                try:
+                    trait_location_value = int(this_trait.chr)*1000 + this_trait.mb
+                except:
+                    if this_trait.chr.upper() == 'X':
+                        trait_location_value = 20*1000 + this_trait.mb
+                    else:
+                        trait_location_value = ord(str(this_trait.chr).upper()[0])*1000 + this_trait.mb
+
+                this_trait.location_repr = 'Chr %s: %.4f Mb' % (this_trait.chr, float(this_trait.mb) )
+                this_trait.location_value = trait_location_value
+                #this_trait.trait_location_value = trait_location_value
+
+            #XZ, 01/12/08: This SQL query is much faster.
+            query = (
+"""select ProbeSetXRef.mean from ProbeSetXRef, ProbeSet
+    where ProbeSetXRef.ProbeSetFreezeId = %s and
+    ProbeSet.Id = ProbeSetXRef.ProbeSetId and
+    ProbeSet.Name = '%s'
+            """ % (escape(str(this_trait.dataset.id)),
+                   escape(this_trait.name)))
+
+            print("query is:", pf(query))
+
+            result = g.db.execute(query).fetchone()
+
+            if result:
+                if result[0]:
+                    mean = result[0]
+                else:
+                    mean=0
+            else:
+                mean = 0
+
+            #XZ, 06/05/2009: It is neccessary to turn on nowrap
+            this_trait.mean = repr = "%2.3f" % mean
+
+            #LRS and its location
+            this_trait.LRS_score_repr = 'N/A'
+            this_trait.LRS_score_value = 0
+            this_trait.LRS_location_repr = 'N/A'
+            this_trait.LRS_location_value = 1000000
+
+            #Max LRS and its Locus location
+            if this_trait.lrs and this_trait.locus:
+                self.cursor.execute("""
+                    select Geno.Chr, Geno.Mb from Geno, Species
+                    where Species.Name = '%s' and
+                        Geno.Name = '%s' and
+                        Geno.SpeciesId = Species.Id
+                """ % (species, this_trait.locus))
+                result = self.cursor.fetchone()
+
+                if result:
+                    if result[0] and result[1]:
+                        LRS_Chr = result[0]
+                        LRS_Mb = result[1]
+
+                        #XZ: LRS_location_value is used for sorting
+                        try:
+                            LRS_location_value = int(LRS_Chr)*1000 + float(LRS_Mb)
+                        except:
+                            if LRS_Chr.upper() == 'X':
+                                LRS_location_value = 20*1000 + float(LRS_Mb)
+                            else:
+                                LRS_location_value = ord(str(LRS_chr).upper()[0])*1000 + float(LRS_Mb)
+
+                        this_trait.LRS_score_repr = LRS_score_repr = '%3.1f' % this_trait.lrs
+                        this_trait.LRS_score_value = LRS_score_value = this_trait.lrs
+                        this_trait.LRS_location_repr = LRS_location_repr = 'Chr %s: %.4f Mb' % (LRS_Chr, float(LRS_Mb) )
+                        
+    def get_sequence(self):
+        query = """
+                    SELECT
+                            ProbeSet.BlatSeq
+                    FROM
+                            ProbeSet, ProbeSetFreeze, ProbeSetXRef
+                    WHERE
+                            ProbeSet.Id=ProbeSetXRef.ProbeSetId and
+                            ProbeSetFreeze.Id = ProbeSetXRef.ProbSetFreezeId and
+                            ProbeSet.Name = %s
+                            ProbeSetFreeze.Name = %s
+                """ % (escape(self.name), escape(self.dataset.name))
+        results = g.db.execute(query).fetchone()
+
+        return results[0]
+    
+    def retrieve_sample_data(self, trait):
+        query = """
+                    SELECT
+                            Strain.Name, ProbeSetData.value, ProbeSetSE.error, ProbeSetData.Id
+                    FROM
+                            (ProbeSetData, ProbeSetFreeze, Strain, ProbeSet, ProbeSetXRef)
+                    left join ProbeSetSE on
+                            (ProbeSetSE.DataId = ProbeSetData.Id AND ProbeSetSE.StrainId = ProbeSetData.StrainId)
+                    WHERE
+                            ProbeSet.Name = '%s' AND ProbeSetXRef.ProbeSetId = ProbeSet.Id AND
+                            ProbeSetXRef.ProbeSetFreezeId = ProbeSetFreeze.Id AND
+                            ProbeSetFreeze.Name = '%s' AND
+                            ProbeSetXRef.DataId = ProbeSetData.Id AND
+                            ProbeSetData.StrainId = Strain.Id
+                    Order BY
+                            Strain.Name
+                    """ % (escape(trait.name), escape(self.name))
+        results = g.db.execute(query).fetchall()
+        return results
+
+
+class TempDataSet(DataSet):
+    '''Temporary user-generated data set'''
+
+    def setup(self):
+        self.search_fields = ['name',
+                              'description']
+
+        self.display_fields = ['name',
+                               'description']
+
+        self.header_fields = ['Name',
+                              'Description']
+
+        self.type = 'Temp'
+
+        # Need to double check later how these are used
+        self.id = 1
+        self.fullname = 'Temporary Storage'
+        self.shortname = 'Temp'
+        
+       
+    @staticmethod
+    def handle_pca(desc):
+        if 'PCA' in desc:
+            # Todo: Modernize below lines
+            desc = desc[desc.rindex(':')+1:].strip()
+        else:
+            desc = desc[:desc.index('entered')].strip()
+        return desc
+        
+    def get_desc(self):
+        g.db.execute('SELECT description FROM Temp WHERE Name=%s', self.name)
+        desc = g.db.fetchone()[0]
+        desc = self.handle_pca(desc)
+        return desc    
+        
+    def get_group(self):
+        self.cursor.execute("""
+                    SELECT
+                            InbredSet.Name, InbredSet.Id
+                    FROM
+                            InbredSet, Temp
+                    WHERE
+                            Temp.InbredSetId = InbredSet.Id AND
+                            Temp.Name = "%s"
+            """, self.name)
+        self.group, self.group_id = self.cursor.fetchone()
+        #return self.group
+        
+    def retrieve_sample_data(self, trait):
+        query = """
+                SELECT
+                        Strain.Name, TempData.value, TempData.SE, TempData.NStrain, TempData.Id
+                FROM
+                        TempData, Temp, Strain
+                WHERE
+                        TempData.StrainId = Strain.Id AND
+                        TempData.Id = Temp.DataId AND
+                        Temp.name = '%s'
+                Order BY
+                        Strain.Name
+                """ % escape(trait.name)
+                
+        results = g.db.execute(query).fetchall()
+
+
+def geno_mrna_confidentiality(ob):
+    dataset_table = ob.type + "Freeze"
+    print("dataset_table [%s]: %s" % (type(dataset_table), dataset_table))
+
+    query = '''SELECT Id, Name, FullName, confidentiality,
+                        AuthorisedUsers FROM %s WHERE Name = %%s''' % (dataset_table)
+
+    result = g.db.execute(query, ob.name)
+
+    (dataset_id,
+     name,
+     full_name,
+     confidential,
+     authorized_users) = result.fetchall()[0]
+
+    if confidential:
+        # Allow confidential data later
+        NoConfindetialDataForYouTodaySorry
diff --git a/wqflask/base/footer.py b/wqflask/base/footer.py
new file mode 100755
index 00000000..6f92fdf8
--- /dev/null
+++ b/wqflask/base/footer.py
@@ -0,0 +1,6 @@
+import webqtlConfig
+
+footer_html = open(webqtlConfig.HTMLPATH + 'footer.html', 'r').read()
+footer = footer_html.replace('%"','%%"')
+
+footer_string = footer.replace('<!-- %s -->', '%s')
diff --git a/wqflask/base/header.py b/wqflask/base/header.py
new file mode 100755
index 00000000..ec15e174
--- /dev/null
+++ b/wqflask/base/header.py
@@ -0,0 +1,6 @@
+import webqtlConfig
+
+header_string = open(webqtlConfig.HTMLPATH + 'header.html', 'r').read()
+header_string = header_string.replace("\\'", "'")
+header_string = header_string.replace('%"','%%"')
+header_string = header_string.replace('<!-- %s -->', '%s')
diff --git a/wqflask/base/indexBody.py b/wqflask/base/indexBody.py
new file mode 100755
index 00000000..a5bc4c17
--- /dev/null
+++ b/wqflask/base/indexBody.py
@@ -0,0 +1,290 @@
+index_body_string = """
+<TD vAlign=top width="40%" align="left" height=10 bgColor=#eeeeee>
+                                                        <p style="font-size:18px;font-family:verdana;color:black"><B> Select and Search</B>
+                                                        <Form METHOD="POST" ACTION="/webqtl/main.py" ENCTYPE="multipart/form-data" NAME="SEARCHFORM">
+
+                                                                <TABLE width="100%">
+
+                                        <!--  SPECIES  SELECTION -->
+                                                                        <TR>
+                                                                                <TD align=right height="35" style="font-size:14px;font-family:verdana;color:black" width="16%">
+                                                                                        <B>Species:</B>
+                                                                                </TD>
+
+                                                                                <TD width="3%">
+                                                                                </TD>
+
+                                                                                <TD NOWRAP width="85%" align="left">
+                                                                                        <DIV Id="menu0">
+                                                                                                <Select NAME="species" size=1 id="species" onchange="fillOptions('species');">
+                                                                                                </Select>
+                                                                                        </DIV>
+                                                                                </TD>
+                                                                        </TR>
+
+                                        <!--  GROUP  SELECTION -->
+
+                                                                        <TR>
+
+                                                                                <TD align="right" height="35" style="font-size:14px;font-family:verdana;color:black">
+                                                                                        <B>Group:</B>
+                                                                                </TD>
+
+                                                                                <TD width="3%">
+                                                                                </TD>
+
+                                                                                <TD NOWRAP width="85%" align="left">
+                                                                                        <DIV Id="menu1">
+
+                                                                                                <Select NAME="cross" size=1 id="cross" onchange="fillOptions('cross');">
+                                                                                                </Select>
+                                                                                        <input type="button" class="button" value=" Info " onCLick="javascript:crossinfo();">
+                                                                                        </DIV>
+                                                                                </TD>
+                                                                        </TR>
+
+
+                                        <!--  TYPE  SELECTION -->
+
+                                                                        <TR>
+
+                                                                                <TD align=right height=35 style="font-size:14px;font-family:verdana;color:black">
+                                                                                        <B>Type:</B>
+                                                                                </TD>
+
+                                                                                <TD width="3%">
+                                                                                </TD>
+                                                                                <TD NOWRAP width="85%" align="left">
+                                                                                        <DIV Id="menu2">
+                                                                                                <Select NAME="tissue" size=1 id="tissue" onchange="fillOptions('tissue');">
+
+                                                                                                </Select>
+                                                                                        </DIV>
+                                                                                </TD>
+                                                                        </TR>
+
+
+                                        <!--  DATABASE  SELECTION -->
+                                                                        <TR>
+                                                                                <TD align=right height=35 style="font-size:14px;font-family:verdana;color:black">
+                                                                                        <B>Database:</B>
+                                                                                </TD>
+
+                                                                                <TD width="3%">
+                                                                                </TD>
+
+                                                                                <TD NOWRAP width="85%" align="left">
+                                                                                        <DIV Id="menu3">
+                                                                                                <Select NAME="database" size=1 id="database">
+                                                                                                </Select>
+                                                                                                <input type="button" class="button" value=" Info " onCLick="javascript:databaseinfo();">
+                                                                                        </DIV>
+
+                                                                                </TD>
+                                                                        </TR>
+
+                                        <!--  USER HELP   -->
+                                                                        <TR>
+                                                                                <TD align=right height=20 width="10%">
+                                                                                </TD>
+                                                                                <TD width="3%">
+                                                                                </TD>
+
+                                                                                <TD align="left" width="85%">
+                                                                                        <P class="fs12">&nbsp;&nbsp;&nbsp;&nbsp;Databases marked with <B>**</B> suffix are not public yet.
+                                                                                        <BR>&nbsp;&nbsp;&nbsp;&nbsp;Access requires <A HREF="/account.html" target="_blank" class="fs14"><small>user login</small></A>.</P>
+                                                                                </TD>
+                                                                        </TR>
+
+
+<!--  GET ANY  SEARCH -->
+                                                                        <TR>
+                                                                                <TD align=right height=35 NOWRAP="on" style="font-size:14px;font-family:verdana;color:black" width="10%">
+                                                                                        <B>Get Any:</B>
+                                                                                </TD>
+
+                                                                                <TD width="3%">
+                                                                                </TD>
+
+                                                                                <TD NOWRAP width="85%" align="left">
+
+                                                                                        <input id="tfor" name="ORkeyword" style="width:420px; background-color:white; font-family:verdana; font-size:14px" type="text" maxlength="500">
+                                                                                </TD>
+                                                                        </TR>
+
+
+
+<!--  GET ANY HELP   -->
+                                                                        <TR>
+                                                                                <TD align=right height=20 width="10%">
+                                                                                </TD>
+                                                                                <TD width="3%">
+
+                                                                                </TD>
+                                                                                <TD width="85%" align="left">
+                                                                                        <P class="fs12">&nbsp;&nbsp;&nbsp;&nbsp;Enter terms, genes, ID numbers in the <B>Get Any</B> field.
+                                                                                        <BR>&nbsp;&nbsp;&nbsp;&nbsp;Use <B>*</B> or <B>?</B> wildcards (Cyp*a?, synap*).
+                                                                                        <BR>&nbsp;&nbsp;&nbsp;&nbsp;Use <B>Combined</B> for terms such as <I>tyrosine kinase</I>.</P>
+
+                                                                                </TD>
+                                                                        </TR>
+
+
+
+<!--  COMBINED SEARCH  -->
+
+                                                                        <TR>
+                                                                                <TD align=right height=35 NOWRAP="on" STYLE="font-size:14px;font-family:verdana;color:black" width="10%">
+                                                                                        <B>Combined:</B>
+                                                                                </TD>
+
+                                                                                <TD width="3%">
+                                                                                </TD>
+
+                                                                                <TD NOWRAP width="85%" align="left">
+                                                                                        <input id="tfand" NAME="ANDkeyword"  STYLE="width:420px; background-color:white; font-family:verdana; font-size:14px" type="text" maxlength="500">
+                                                                                        <input name="matchwhole" type="hidden" value="ON">
+                                                                                </TD>
+                                                                        </TR>
+
+
+
+<!--  SEARCH, MAKE DEFAULT, ADVANCED SEARCH -->
+
+                                                                        <TR ALIGN="center">
+                                                                                <TD width="3%">
+                                                                                </TD>
+                                                                                <TD width="3%">
+                                                                                </TD>
+                                                                                <TD ALIGN="left" HEIGHT="40" COLSPAN=3>
+                                                                                        <INPUT id="btsearch" TYPE="Submit" CLASS="button" STYLE="font-size:12px" VALUE="  Search  ">&nbsp;&nbsp;&nbsp;&nbsp;
+                                                                                       <INPUT TYPE="button" CLASS="button" STYLE="font-size:12px" VALUE="  Make Default  " onClick = "setDefault(this.form);">&nbsp;&nbsp;&nbsp;&nbsp;
+                                                                                        <INPUT TYPE="button" CLASS="button" STYLE="font-size:12px" VALUE="  Advanced Search  " onClick="javascript:window.open('/index3.html', '_self');">
+
+                                                                                </TD>
+                                                                        </TR>
+                                                                </TABLE>
+                                                                <INPUT TYPE="hidden" NAME="FormID" VALUE="searchResult">
+                                                                <INPUT TYPE="hidden" NAME="RISet" VALUE="BXD">
+                                                                <SCRIPT SRC="/javascript/selectDatasetMenu.js"></SCRIPT>
+                                                        </FORM>
+                                                        </CENTER>
+
+
+
+
+
+<!--  QUICK HELP  -->
+
+<P><LEFT>&nbsp;______________________________________________________
+
+<P STYLE="font-size:13px;font-family:verdana;color:black"><B>&nbsp;
+
+Quick HELP Examples and </B>
+<A HREF="http://www.genenetwork.org/index4.html" target="_blank"  class="fs14"><B>
+ User's Guide</B></A></P>
+
+</CENTER style="font-size:12px;font-family:verdana;color:black">
+&nbsp;&nbsp;You can also use advanced commands. Copy these simple examples
+<BR>&nbsp;&nbsp;into the <B>Get Any</B> or <B>Combined</B> search fields:
+<UL style="font-size:12px;font-family:verdana;color:black">
+
+<LI><B><I>POSITION=(chr1 25 30)</I></B> finds genes, markers, or transcripts on chromosome 1 between 25 and 30 Mb.
+
+<LI><B><I>MEAN=(15 16) LRS=(23 46)</I></B> in the <B>Combined</B> field finds highly expressed genes (15 to 16 log2 units) AND with peak <A HREF="http://www.genenetwork.org/glossary.html#L" target="_blank"  class="fs14"><small>LRS</small></A> linkage between 23 and 46.
+
+
+<LI><B><I>RIF=mitochondrial</I></B> searches RNA databases for <A HREF="http://www.ncbi.nlm.nih.gov/projects/GeneRIF/GeneRIFhelp.html" target="_blank"  class="fs14"><small>GeneRIF</small></A> links.
+
+<LI><B><I>WIKI=nicotine</I></B> searches <A HREF="http://www.genenetwork.org/webqtl/main.py?FormID=geneWiki" target="_blank"  class="fs14"><small>GeneWiki</small></A> for genes that you or other users have annotated with the word <I>nicotine</I>.
+
+<LI><B><I>GO:0045202</I></B> searches for synapse-associated genes listed in the <A HREF="http://www.godatabase.org/cgi-bin/amigo/go.cgi" target="_blank"  class="fs14"><small>Gene Ontology</small></A>.
+
+
+<LI><B><I>GO:0045202 LRS=(9 99 Chr4 122 155) cisLRS=(9 999 10)</I> </B><BR> in <B>Combined</B> finds synapse-associated genes with <A HREF="http://www.genenetwork.org/glossary.html#E" target="_blank"  class="fs14"><small>cis eQTL</small></A> on Chr 4 from 122 and 155 Mb with LRS scores between 9 and 999.
+
+<LI><B><I>RIF=diabetes LRS=(9 999 Chr2 100 105) transLRS=(9 999 10)</I> </B><BR> in <B>Combined</B> finds diabetes-associated transcripts with peak <A HREF="http://www.genenetwork.org/glossary.html#E" target="_blank"  class="fs14"><small>trans eQTLs</small></A> on Chr 2 between 100 and 105 Mb with LRS scores between 9 and 999.
+
+
+</UL>
+</DIR>
+                                        </TD>
+<!-- END OF FIND SELECTOR PULL-DOWN PANEL (LEFT SIDE)  -->
+
+<!-- START OF TOP RIGHT PANEL  -->
+
+<TD vAlign=top width="40%" bgColor=#FFFFFF>
+                                                        <p style="font-size:15px;font-family:verdana;color:black"><B>Websites Affiliated with GeneNetwork</B></p>
+                <p style="font-size:12px;font-family:verdana;color:black">
+                        <ul>
+                                <li><a href="http://ucscbrowser.genenetwork.org/" target="_blank">Genome Browser</a> at UTHSC</li>
+                                <li><a href="http://galaxy.genenetwork.org/" target="_blank">Galaxy</a> at UTHSC</li>
+                                <li>GeneNetwork at <a href="http://ec2.genenetwork.org/" target="_blank">Amazon Cloud (EC2)</a></li>
+                                <li>GeneNetwork Source Codes at <a href="http://sourceforge.net/projects/genenetwork/" target="_blank">SourceForge</a></li>
+                        </ul>
+                </p>
+                                                        <P>____________________________
+
+                                                        <p style="font-size:15px;font-family:verdana;color:black"><B>Getting Started</B> &nbsp;&nbsp; </p>
+                                                        <OL style="font-size:12px;font-family:verdana;color:black">
+                                                                <LI>Select <B>Species</B> (or select All)
+                                                                <LI>Select <B>Group</B> (a specific sample)
+                                                                <LI>Select <B>Type</B> of data:
+                                                                <UL>
+                                                                        <LI>Phenotype (traits)
+                                                                        <LI>Genotype (markers)
+                                                                        <LI>Expression (mRNAs)
+                                                                </UL>
+                                                                <LI>Select a <B>Database</B>
+                                                                <LI>Enter search terms in the <B>Get Any</B> or <B>Combined</B> field: words, genes, ID numbers, probes, advanced search commands
+                                                                <LI>Click on the <B>Search</B> button
+                                                                <LI>Optional: Use the <B>Make Default</B> button to save your preferences
+                                                        </OL>
+
+                                                        <P>____________________________
+
+<p style="font-size:14px;font-family:verdana;color:black"><B>How to Use GeneNetwork</B>
+
+                                                        <BLOCKQUOTE>
+                                                                <p style="font-size:12px;font-family:verdana;color:black">Take a 20-40 minute GeneNetwork <A HREF="http://www.genenetwork.org/tutorial/WebQTLTour/" target="_blank" class="fs14"><small>Tour</small></A> that includes screen shots and typical steps in the analysis.</P>
+                                                        </BLOCKQUOTE>
+                                                        <BLOCKQUOTE>
+                                                                <p style="font-size:12px;font-family:verdana;color:black">For information about resources and methods, select the <img src="http://www.genenetwork.org/images/upload/Info.png" alt="INFO" border= 0 valign="middle"> buttons.</P>
+
+
+
+<p style="font-size:12px;font-family:verdana;color:black">Try the <A HREF="http://alexandria.uthsc.edu/" target="_blank" class="fs14"><small>Workstation</small></A> site to explore data and features that are being implemented.</P>
+
+
+<p style="font-size:12px;font-family:verdana;color:black">Review the <A HREF="/conditionsofUse.html" target="_blank" class="fs14"><small>Conditions</small></A> and <A HREF="/statusandContact.html" target="_blank" class="fs14"><small>Contacts</small></A> pages for information on the status of data sets and advice on their use and citation.</P>
+
+
+                                                        </BLOCKQUOTE>
+
+
+
+                                                        <p style="font-size:14px;font-family:verdana;color:black"><B>Mirror and Development Sites</B></P>
+
+                                                        <UL>
+                                                                <LI><A HREF="http://www.genenetwork.org/" target="_blank" style="font-size:12px;font-family:verdana;color:blue">Main GN site at UTHSC</A> (main site)
+                                                                <LI><A HREF="http://www.genenetwork.waimr.uwa.edu.au/" target="_blank" style="font-size:12px;font-family:verdana;color:blue">Australia at the UWA</A>
+                                                                <LI><A HREF="http://gn.genetics.ucla.edu/" target="_blank" style="font-size:12px;font-family:verdana;color:blue">California at UCLA</A>
+                                                                <LI><A HREF="http://genenetwork.helmholtz-hzi.de/" target="_blank" style="font-size:12px;font-family:verdana;color:blue">Germany at the HZI</A>
+                                                                <LI><A HREF="https://genenetwork.hubrecht.eu/" target="_blank" style="font-size:12px;font-family:verdana;color:blue">Netherlands at the Hubrecht</A> (Development)
+                                                                <LI><A HREF="http://xzhou3.memphis.edu/" target="_blank" style="font-size:12px;font-family:verdana;color:blue">Memphis at the U of M</A>
+                                                                <LI><A HREF="http://webqtl.bic.nus.edu.sg/" target="_blank" style="font-size:12px;font-family:verdana;color:blue">Singapore at the NUS</A>
+                                                                <LI><A HREF="http://genenetwork.epfl.ch/" target="_blank" style="font-size:12px;font-family:verdana;color:blue">Switzerland at the EPFL</A>
+                                                        </UL>
+
+
+                                                        <p style="font-size:14px;font-family:verdana;color:black"><B>History and Archive</B>
+
+                                                        <BLOCKQUOTE>
+                                                                <p style="font-size:12px;font-family:verdana;color:black">GeneNetwork's <A HREF="http://artemis.uthsc.edu" target="_blank" class="fs14"><small>Time Machine</small></A> links to earlier versions that correspond to specific publication dates.</P>
+
+                                                        </BLOCKQUOTE>
+
+
+</P>
+                                                </TD>
+"""
diff --git a/wqflask/base/myCookie.py b/wqflask/base/myCookie.py
new file mode 100755
index 00000000..add7e6ea
--- /dev/null
+++ b/wqflask/base/myCookie.py
@@ -0,0 +1,51 @@
+# Copyright (C) University of Tennessee Health Science Center, Memphis, TN.
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License
+# as published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU Affero General Public License for more details.
+#
+# This program is available from Source Forge: at GeneNetwork Project
+# (sourceforge.net/projects/genenetwork/).
+#
+# 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
+#
+# Last updated by GeneNetwork Core Team 2010/10/20
+
+#########################################
+## python cookie and mod python cookie are
+## not compatible
+#########################################
+
+class myCookie(dict):
+    'define my own cookie'
+
+    def __init__(self, name="", value="", expire = None, path="/"):
+        self['name']= name
+        self['value']= value
+        self['expire']= expire
+        self['path']= path
+
+    def __getattr__(self, key):
+        if key in self.keys():
+            return self[key]
+        else:
+            return None
+
+    def __nonzero__ (self):
+        if self['name']:
+            return 1
+        else:
+            return 0
diff --git a/wqflask/base/sessionData.py b/wqflask/base/sessionData.py
new file mode 100755
index 00000000..4b23060f
--- /dev/null
+++ b/wqflask/base/sessionData.py
@@ -0,0 +1,50 @@
+# Copyright (C) University of Tennessee Health Science Center, Memphis, TN.
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License
+# as published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU Affero General Public License for more details.
+#
+# This program is available from Source Forge: at GeneNetwork Project
+# (sourceforge.net/projects/genenetwork/).
+#
+# 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
+#
+# Last updated by GeneNetwork Core Team 2010/10/20
+
+#########################################
+#convert mod_python object to Dict object
+#in order to be able to be pickled
+#########################################
+
+class sessionData(dict):
+    'convert mod python Session object to Dict object'
+
+    def __init__(self, mod_python_session=None):
+
+        if not mod_python_session:
+            mod_python_session = {}
+
+        for key in mod_python_session.keys():
+            self[key]= mod_python_session[key]
+
+
+    def getvalue(self, k, default= None):
+        try:
+            return self[k]
+        except:
+            return default
+
+    getfirst = getvalue
diff --git a/wqflask/base/species.py b/wqflask/base/species.py
new file mode 100644
index 00000000..9d4cac4c
--- /dev/null
+++ b/wqflask/base/species.py
@@ -0,0 +1,117 @@
+from __future__ import absolute_import, print_function, division
+
+import collections
+
+from flask import Flask, g
+
+#from MySQLdb import escape_string as escape
+
+from utility import Bunch
+
+from pprint import pformat as pf
+
+class TheSpecies(object):
+    def __init__(self, dataset):
+        self.dataset = dataset
+        print("self.dataset is:", pf(self.dataset.__dict__))
+        self.chromosomes = Chromosomes(self.dataset)
+        self.genome_mb_length = self.chromosomes.get_genome_mb_length()
+        
+        
+    #@property
+    #def chromosomes(self):
+    #    chromosomes = [("All", -1)]
+    #
+    #    for counter, genotype in enumerate(self.dataset.group.genotype):
+    #        if len(genotype) > 1:
+    #            chromosomes.append((genotype.name, counter))
+    #            
+    #    print("chromosomes is: ", pf(chromosomes))       
+    #            
+    #    return chromosomes
+
+class IndChromosome(object):
+    def __init__(self, length):
+        self.length = length
+        
+    @property
+    def mb_length(self):
+        """Chromosome length in megabases"""
+        return self.length / 1000000
+    
+    def set_cm_length(self, genofile_chr):
+        self.cm_length = genofile_chr[-1].cM - genofile_chr[0].cM
+
+
+class Chromosomes(object):
+    def __init__(self, dataset):
+        self.dataset = dataset
+        self.chromosomes = collections.OrderedDict()
+
+        results = g.db.execute("""
+                Select
+                        Chr_Length.Name, Length from Chr_Length, InbredSet
+                where
+                        Chr_Length.SpeciesId = InbredSet.SpeciesId AND
+                        InbredSet.Name = %s
+                Order by OrderId
+                """, self.dataset.group.name).fetchall()
+        print("bike:", results)
+
+        for item in results:
+            self.chromosomes[item.Name] = IndChromosome(item.Length)
+        
+        self.set_mb_graph_interval()
+        self.get_cm_length_list()
+
+
+    def set_mb_graph_interval(self):
+        """Empirical megabase interval"""
+        
+        #if self.chromosomes:
+        assert self.chromosomes, "Have to add some code back in apparently to set it to 1"
+        self.mb_graph_interval = self.get_genome_mb_length()/(len(self.chromosomes)*12)
+        #else:
+            #self.mb_graph_interval = 1
+
+
+    def get_genome_mb_length(self):
+        """Gets the sum of each chromosome's length in megabases"""
+
+        return sum([ind_chromosome.mb_length for ind_chromosome in self.chromosomes.values()])
+
+
+    def get_genome_cm_length(self):
+        """Gets the sum of each chromosome's length in centimorgans"""
+
+        return sum([ind_chromosome.cm_length for ind_chromosome in self.chromosomes.values()])
+
+    def get_cm_length_list(self):
+        """Chromosome length in centimorgans
+        
+        Calculates the length in centimorgans by subtracting the centimorgan position
+        of the last marker in a chromosome by the position of the first marker
+        
+        """
+        
+        self.dataset.group.read_genotype_file()
+        
+        self.cm_length_list = []
+        
+        for chromosome in self.dataset.group.genotype:
+            self.cm_length_list.append(chromosome[-1].cM - chromosome[0].cM)
+            
+        print("self.cm_length_list:", pf(self.cm_length_list))
+        
+        assert len(self.cm_length_list) == len(self.chromosomes), "Uh-oh lengths should be equal!"
+        for counter, chromosome in enumerate(self.chromosomes.values()):
+            chromosome.cm_length = self.cm_length_list[counter]
+            #self.chromosomes[counter].cm_length = item
+            
+        for key, value in self.chromosomes.items():
+            print("bread - %s: %s" % (key, pf(vars(value))))
+        
+
+# Testing                
+#if __name__ == '__main__':    
+#    foo = dict(bar=dict(length))
\ No newline at end of file
diff --git a/wqflask/base/template.py b/wqflask/base/template.py
new file mode 100755
index 00000000..aa8f90dc
--- /dev/null
+++ b/wqflask/base/template.py
@@ -0,0 +1,123 @@
+# Copyright (C) University of Tennessee Health Science Center, Memphis, TN.
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License
+# as published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU Affero General Public License for more details.
+#
+# This program is available from Source Forge: at GeneNetwork Project
+# (sourceforge.net/projects/genenetwork/).
+#
+# 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
+#
+# Last updated by GeneNetwork Core Team 2010/10/20
+
+template = """
+<?XML VERSION="1.0" ENCODING="UTF-8">
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+<TITLE>%s</TITLE>
+
+<META http-equiv=Content-Type content="text/html; charset=iso-8859-1">
+<META NAME="keywords" CONTENT="genetics, bioinformatics, genome, phenome, gene expression, complex trait analysis, gene mapping, SNP, quantitative trait locus QTL, expression eQTL, WebQTL, Traitnet, Traitnetwork, personalized medicine">
+<META NAME="description" CONTENT ="GeneNetwork is a free scientific web resource used to study relationships between differences in genes, environmental factors, phenotypes, and disease risk." >
+<META NAME="author" CONTENT ="GeneNetwork developers" >
+<META NAME="geo.placename" CONTENT ="Memphis, TN" >
+<META NAME="geo.region" CONTENT="US-TN">
+%s
+<LINK REL="stylesheet" TYPE="text/css" HREF='/css/general.css'>
+<LINK REL="stylesheet" TYPE="text/css" HREF='/css/menu.css'>
+<link rel="stylesheet" media="all" type="text/css" href="/css/tabbed_pages.css" />
+<LINK REL="apple-touch-icon" href="/images/ipad_icon3.png" />
+<link type="text/css" href='/css/custom-theme/jquery-ui-1.8.12.custom.css' rel='Stylesheet' />
+<link type="text/css" href='/css/tab_style.css' rel='Stylesheet' />
+
+<script type="text/javascript" src="/javascript/jquery-1.5.2.min.js"></script>
+<SCRIPT SRC="/javascript/webqtl.js"></SCRIPT>
+<SCRIPT SRC="/javascript/dhtml.js"></SCRIPT>
+<SCRIPT SRC="/javascript/tablesorter.js"></SCRIPT>
+<SCRIPT SRC="/javascript/jqueryFunction.js"></SCRIPT>
+<script src="/javascript/tabbed_pages.js" type="text/javascript"></script>
+<script src="/javascript/jquery-ui-1.8.12.custom.min.js" type="text/javascript"></script>
+%s
+
+<script type="text/javascript">
+  var _gaq = _gaq || [];
+  _gaq.push(['_setAccount', 'UA-3782271-1']);
+  _gaq.push(['_trackPageview']);
+  (function() {
+    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
+    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
+    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
+  })();
+</script>
+</HEAD>
+<BODY  bottommargin="2" leftmargin="2" rightmargin="2" topmargin="2" text=#000000 bgColor=#ffffff %s>
+%s
+<TABLE cellSpacing=5 cellPadding=4 width="100%%" border=0>
+        <TBODY>
+        <!-- Start of header -->
+        <TR>
+                %s
+        </TR>
+        <!-- End of header -->
+
+        <!-- Start of body -->
+        <TR>
+                <TD  bgColor=#eeeeee class="solidBorder">
+                <Table width= "100%%" cellSpacing=0 cellPadding=5>
+                <TR>
+                %s
+                </TR>
+                </TABLE>
+                </TD>
+        </TR>
+        <!-- End of body -->
+
+        <!-- Start of footer -->
+        <TR>
+                <TD align=center bgColor=#ddddff class="solidBorder">
+                        <TABLE width="90%%">%s</table>
+                </td>
+        </TR>
+        <!-- End of footer -->
+</TABLE>
+
+<!-- menu script itself. you should not modify this file -->
+<script language="JavaScript" src="/javascript/menu_new.js"></script>
+<!-- items structure. menu hierarchy and links are stored there -->
+<script language="JavaScript" src="/javascript/menu_items.js"></script>
+<!-- files with geometry and styles structures -->
+<script language="JavaScript" src="/javascript/menu_tpl.js"></script>
+<script language="JavaScript">
+        <!--//
+        // Note where menu initialization block is located in HTML document.
+        // Don't try to position menu locating menu initialization block in
+        // some table cell or other HTML element. Always put it before </body>
+        // each menu gets two parameters (see demo files)
+        // 1. items structure
+        // 2. geometry structure
+        new menu (MENU_ITEMS, MENU_POS);
+        // make sure files containing definitions for these variables are linked to the document
+        // if you got some javascript error like "MENU_POS is not defined", then you've made syntax
+        // error in menu_tpl.js file or that file isn't linked properly.
+
+        // also take a look at stylesheets loaded in header in order to set styles
+        //-->
+</script>
+</BODY>
+</HTML>
+"""
diff --git a/wqflask/base/templatePage.py b/wqflask/base/templatePage.py
new file mode 100755
index 00000000..a94d5153
--- /dev/null
+++ b/wqflask/base/templatePage.py
@@ -0,0 +1,222 @@
+# Copyright (C) University of Tennessee Health Science Center, Memphis, TN.
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License
+# as published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU Affero General Public License for more details.
+#
+# This program is available from Source Forge: at GeneNetwork Project
+# (sourceforge.net/projects/genenetwork/).
+#
+# 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
+#
+# Last updated by GeneNetwork Core Team 2010/10/20
+
+#templatePage.py
+#
+#--Genenetwork generates a lot of pages; this file is the generic version of them, defining routines they all use.
+#
+#Classes:
+#templatePage
+#
+#Functions (of templatePage):
+#__init__(...) -- class constructor, allows a more specific template to be used in addition to templatePage
+#__str__(self) -- returns the object's elements as a tuple
+#__del__(self) -- closes the current connection to MySQL, if there is one
+#write -- explained below
+#writefile -- explained below
+#openMysql(self) -- opens a MySQL connection and stores the resulting cursor in the object's cursor variable
+#updMysql(self) -- same as openMysql
+#error -- explained below
+#session -- explained below
+
+
+import socket
+import time
+import shutil
+import MySQLdb
+import os
+
+from htmlgen import HTMLgen2 as HT
+
+import template
+import webqtlConfig
+import header
+import footer
+from utility import webqtlUtil
+
+
+
+class templatePage:
+
+    contents = ['title','basehref','js1','js2', 'layer', 'header', 'body', 'footer']
+
+    # you can pass in another template here if you want
+    def __init__(self, fd=None, template=template.template):
+
+        # initiate dictionary
+        self.starttime = time.time()
+        self.dict = {}
+        self.template = template
+
+        for item in self.contents:
+            self.dict[item] = ""
+
+        self.dict['basehref'] = "" #webqtlConfig.BASEHREF
+        self.cursor = None
+
+        self.cookie = [] #XZ: list to hold cookies (myCookie object) being changed
+        self.content_type = 'text/html'
+        self.content_disposition = ''
+        self.redirection = ''
+        self.debug = ''
+        self.attachment = ''
+
+        #XZ: Holding data (new data or existing data being changed) that should be saved to session. The data must be picklable!!!
+        self.session_data_changed = {}
+
+        self.userName = 'Guest'
+        self.privilege = 'guest'
+
+        # Commenting this out for flask - we'll have to reimplement later - Sam
+        #if fd.input_session_data.has_key('user'):
+        #       self.userName = fd.input_session_data['user']
+        #if fd.input_session_data.has_key('privilege'):
+        #       self.privilege = fd.input_session_data['privilege']
+
+    def __str__(self):
+
+        #XZ: default setting
+        thisUserName = self.userName
+        thisPrivilege = self.privilege
+        #XZ: user may just go through login or logoff page
+        if self.session_data_changed.has_key('user'):
+            thisUserName = self.session_data_changed['user']
+        if self.session_data_changed.has_key('privilege'):
+            thisPrivilege = self.session_data_changed['privilege']
+
+        if thisUserName == 'Guest':
+            userInfo = 'Welcome! <a href=/account.html><U>Login</U></a>'
+        else:
+            userInfo = 'Hi, %s! <a href=/webqtl/main.py?FormID=userLogoff><U>Logout</U></a>' % thisUserName
+
+        reload(header)
+        self.dict['header'] = header.header_string % userInfo
+
+        serverInfo = "It took %2.3f second(s) for %s to generate this page" % (time.time()-self.starttime, socket.getfqdn())
+        reload(footer)
+        self.dict['footer'] = footer.footer_string % serverInfo
+
+        slist = []
+        for item in self.contents:
+            slist.append(self.dict[item])
+        return self.template % tuple(slist)
+
+
+    def __del__(self):
+        if self.cursor:
+            self.cursor.close()
+
+    def write(self):
+        'return string representation of this object'
+
+        if self.cursor:
+            self.cursor.close()
+
+        return str(self)
+
+    def writeFile(self, filename):
+        'save string representation of this object into a file'
+        if self.cursor:
+            self.cursor.close()
+
+        try:
+            'it could take a long time to generate the file, save to .tmp first'
+            fp = open(os.path.join(webqtlConfig.TMPDIR, filename+'.tmp'), 'wb')
+            fp.write(str(self))
+            fp.close()
+            path_tmp = os.path.join(webqtlConfig.TMPDIR, filename+'.tmp')
+            path_html = os.path.join(webqtlConfig.TMPDIR, filename)
+            shutil.move(path_tmp,path_html)
+        except:
+            pass
+
+    def openMysql(self):
+        try:
+            self.db_conn = MySQLdb.Connect(db=webqtlConfig.DB_NAME,host=webqtlConfig.MYSQL_SERVER, \
+                                    user=webqtlConfig.DB_USER,passwd=webqtlConfig.DB_PASSWD)
+            self.cursor = self.db_conn.cursor()
+            return True
+        except Exception:
+            heading = "Connect MySQL Server"
+            detail = ["Can't connect to MySQL server on '"+ webqtlConfig.MYSQL_SERVER+"':100061. \
+                            The server may be down at this time"]
+            self.error(heading=heading,detail=detail,error="Error 2003")
+            return False
+
+    def updMysql(self):
+        try:
+            self.db_conn = MySQLdb.Connect(db=webqtlConfig.DB_UPDNAME,host=webqtlConfig.MYSQL_UPDSERVER, \
+                                    user=webqtlConfig.DB_UPDUSER,passwd=webqtlConfig.DB_UPDPASSWD)
+            self.cursor = self.db_conn.cursor()
+            return True
+        except Exception:
+            heading = "Connect MySQL Server"
+            detail = ["update: Can't connect to MySQL server on '"+ webqtlConfig.MYSQL_UPDSERVER+"':100061. \
+                            The server may be down at this time "]
+            self.error(heading=heading,detail=detail,error="Error 2003")
+            return False
+
+    def error(self,heading="",intro=[],detail=[],title="Error",error="Error"):
+        'generating a WebQTL style error page'
+        Heading = HT.Paragraph(heading)
+        Heading.__setattr__("class","title")
+
+        Intro = HT.Blockquote()
+        if intro:
+            for item in intro:
+                Intro.append(item)
+        else:
+            Intro.append(HT.Strong('Sorry!'),' Error occurred while processing\
+                    your request.', HT.P(),'The nature of the error generated is as\
+                    follows:')
+
+        Detail = HT.Blockquote()
+        Detail.append(HT.Span("%s : " % error,Class="fwb cr"))
+        if detail:
+            Detail2 = HT.Blockquote()
+            for item in detail:
+                Detail2.append(item)
+            Detail.append(HT.Italic(Detail2))
+
+        #Detail.__setattr__("class","subtitle")
+        TD_LR = HT.TD(height=200,width="100%",bgColor='#eeeeee',valign="top")
+        TD_LR.append(Heading,Intro,Detail)
+        self.dict['body'] = str(TD_LR)
+        self.dict['title'] = title
+
+    def session(self,mytitle="",myHeading=""):
+        'generate a auto-refreshing temporary html file(waiting page)'
+        self.filename = webqtlUtil.generate_session()
+        self.dict['title'] = mytitle
+        self.dict['basehref'] = webqtlConfig.REFRESHSTR % (webqtlConfig.CGIDIR, self.filename) + "" #webqtlConfig.BASEHREF
+
+        TD_LR = HT.TD(align="center", valign="middle", height=200,width="100%", bgColor='#eeeeee')
+        Heading = HT.Paragraph(myHeading, Class="fwb fs16 cr")
+        # NL, 07/27/2010. variable 'PROGRESSBAR' has been moved from templatePage.py to webqtlUtil.py;
+        TD_LR.append(Heading, HT.BR(), webqtlUtil.PROGRESSBAR)
+        self.dict['body'] = TD_LR
+        self.writeFile(self.filename + '.html')
+        return self.filename
diff --git a/wqflask/base/trait.py b/wqflask/base/trait.py
new file mode 100755
index 00000000..241bf2ab
--- /dev/null
+++ b/wqflask/base/trait.py
@@ -0,0 +1,564 @@
+from __future__ import absolute_import, division, print_function
+
+import string
+
+from htmlgen import HTMLgen2 as HT
+
+from base import webqtlConfig
+from base.webqtlCaseData import webqtlCaseData
+from base.data_set import create_dataset
+from dbFunction import webqtlDatabaseFunction
+from utility import webqtlUtil
+
+from MySQLdb import escape_string as escape
+from pprint import pformat as pf
+
+from flask import Flask, g
+
+class GeneralTrait:
+    """
+    Trait class defines a trait in webqtl, can be either Microarray,
+    Published phenotype, genotype, or user input trait
+
+    """
+
+    def __init__(self, **kw):
+        print("in GeneralTrait")
+        self.dataset = kw.get('dataset')           # database name
+        self.name = kw.get('name')                 # Trait ID, ProbeSet ID, Published ID, etc.
+        self.cellid = kw.get('cellid')
+        self.identification = kw.get('identification', 'un-named trait')
+        self.haveinfo = kw.get('haveinfo', False)
+        self.sequence = kw.get('sequence')         # Blat sequence, available for ProbeSet
+        self.data = kw.get('data', {})
+
+        if kw.get('fullname'):
+            name2 = value.split("::")
+            if len(name2) == 2:
+                self.dataset, self.name = name2
+                # self.cellid is set to None above
+            elif len(name2) == 3:
+                self.dataset, self.name, self.cellid = name2
+
+        self.dataset = create_dataset(self.dataset)
+        
+        # Todo: These two lines are necessary most of the time, but perhaps not all of the time
+        # So we could add a simple if statement to short-circuit this if necessary
+        self.retrieve_info()
+        self.retrieve_sample_data()
+
+
+    def get_name(self):
+        stringy = ""
+        if self.dataset and self.name:
+            stringy = "%s::%s" % (self.dataset, self.name)
+            if self.cellid:
+                stringy += "::" + self.cellid
+        else:
+            stringy = self.description
+        return stringy
+
+
+    def get_given_name(self):
+        """    
+         when user enter a trait or GN generate a trait, user want show the name
+         not the name that generated by GN randomly, the two follow function are
+         used to give the real name and the database. displayName() will show the
+         database also, getGivenName() just show the name.
+         For other trait, displayName() as same as getName(), getGivenName() as
+         same as self.name
+        
+         Hongqiang 11/29/07
+         
+        """
+        stringy = self.name
+        if self.dataset and self.name:
+            desc = self.dataset.get_desc()  
+            if desc:
+                #desc = self.handle_pca(desc)
+                stringy = desc
+        return stringy
+    
+
+
+    def display_name(self):
+        stringy = ""
+        if self.dataset and self.name:
+            desc = self.dataset.get_desc()
+            #desc = self.handle_pca(desc)
+            if desc:
+                #desc = self.handle_pca(desc)
+                #stringy = desc
+                #if desc.__contains__('PCA'):
+                #    desc = desc[desc.rindex(':')+1:].strip()
+                #else:
+                #    desc = desc[:desc.index('entered')].strip()
+                #desc = self.handle_pca(desc)
+                stringy = "%s::%s" % (self.dataset, desc)
+            else:
+                stringy = "%s::%s" % (self.dataset, self.name)
+                if self.cellid:
+                    stringy += "::" + self.cellid
+        else:
+            stringy = self.description
+
+        return stringy
+
+
+    #def __str__(self):
+    #       #return "%s %s" % (self.getName(), self.group)
+    #       return self.getName()
+    #__str__ = getName
+    #__repr__ = __str__
+
+    def export_data(self, samplelist, the_type="val"):
+        """
+        export data according to samplelist
+        mostly used in calculating correlation
+        
+        """
+        result = []
+        for sample in samplelist:
+            if self.data.has_key(sample):
+                if the_type=='val':
+                    result.append(self.data[sample].val)
+                elif the_type=='var':
+                    result.append(self.data[sample].var)
+                elif the_type=='N':
+                    result.append(self.data[sample].N)
+                else:
+                    raise KeyError, `the_type`+' the_type is incorrect.'
+            else:
+                result.append(None)
+        return result
+
+    def export_informative(self, incVar=0):
+        """
+        export informative sample
+        mostly used in qtl regression
+        
+        """
+        samples = []
+        vals = []
+        the_vars = []
+        for sample, value in self.data.items():
+            if value.val != None:
+                if not incVar or value.var != None:
+                    samples.append(sample)
+                    vals.append(value.val)
+                    the_vars.append(value.var)
+        return  samples, vals, the_vars
+
+
+    #
+    # In ProbeSet, there are maybe several annotations match one sequence
+    # so we need use sequence(BlatSeq) as the identification, when we update
+    # one annotation, we update the others who match the sequence also.
+    #
+    # Hongqiang Li, 3/3/2008
+    #
+    #def getSequence(self):
+    #    assert self.cursor
+    #    if self.dataset.type == 'ProbeSet':
+    #        self.cursor.execute('''
+    #                        SELECT
+    #                                ProbeSet.BlatSeq
+    #                        FROM
+    #                                ProbeSet, ProbeSetFreeze, ProbeSetXRef
+    #                        WHERE
+    #                                ProbeSet.Id=ProbeSetXRef.ProbeSetId and
+    #                                ProbeSetFreeze.Id = ProbeSetXRef.ProbSetFreezeId and
+    #                                ProbeSet.Name = %s
+    #                                ProbeSetFreeze.Name = %s
+    #                ''', self.name, self.dataset.name)
+    #        #self.cursor.execute(query)
+    #        results = self.fetchone()
+    #
+    #        return results[0]
+
+
+
+    def retrieve_sample_data(self, samplelist=None):
+        if samplelist == None:
+            samplelist = []
+            
+        #assert self.dataset
+        
+        #if self.cellid:
+        #     #Probe Data
+        #    query = '''
+        #            SELECT
+        #                    Strain.Name, ProbeData.value, ProbeSE.error, ProbeData.Id
+        #            FROM
+        #                    (ProbeData, ProbeFreeze, ProbeSetFreeze, ProbeXRef,
+        #                    Strain, Probe, ProbeSet)
+        #            left join ProbeSE on
+        #                    (ProbeSE.DataId = ProbeData.Id AND ProbeSE.StrainId = ProbeData.StrainId)
+        #            WHERE
+        #                    Probe.Name = '%s' AND ProbeSet.Name = '%s' AND
+        #                    Probe.ProbeSetId = ProbeSet.Id AND
+        #                    ProbeXRef.ProbeId = Probe.Id AND
+        #                    ProbeXRef.ProbeFreezeId = ProbeFreeze.Id AND
+        #                    ProbeSetFreeze.ProbeFreezeId = ProbeFreeze.Id AND
+        #                    ProbeSetFreeze.Name = '%s' AND
+        #                    ProbeXRef.DataId = ProbeData.Id AND
+        #                    ProbeData.StrainId = Strain.Id
+        #            Order BY
+        #                    Strain.Name
+        #            ''' % (self.cellid, self.name, self.dataset.name)
+        #            
+        #else:
+        results = self.dataset.retrieve_sample_data(self)
+
+        # Todo: is this necessary? If not remove
+        self.data.clear()
+
+        if results:
+            for item in results:
+                #name, value, variance, num_cases = item
+                if not samplelist or (samplelist and name in samplelist):
+                    name = item[0]
+                    self.data[name] = webqtlCaseData(*item)   #name, value, variance, num_cases)
+
+    #def keys(self):
+    #    return self.__dict__.keys()
+    #
+    #def has_key(self, key):
+    #    return self.__dict__.has_key(key)
+    #
+    #def items(self):
+    #    return self.__dict__.items()
+
+    def retrieve_info(self, QTL=False):
+        assert self.dataset, "Dataset doesn't exist"
+        if self.dataset.type == 'Publish':
+            query = """
+                    SELECT
+                            PublishXRef.Id, Publication.PubMed_ID,
+                            Phenotype.Pre_publication_description, Phenotype.Post_publication_description, Phenotype.Original_description,
+                            Phenotype.Pre_publication_abbreviation, Phenotype.Post_publication_abbreviation,
+                            Phenotype.Lab_code, Phenotype.Submitter, Phenotype.Owner, Phenotype.Authorized_Users,
+                            Publication.Authors, Publication.Title, Publication.Abstract,
+                            Publication.Journal, Publication.Volume, Publication.Pages,
+                            Publication.Month, Publication.Year, PublishXRef.Sequence,
+                            Phenotype.Units, PublishXRef.comments
+                    FROM
+                            PublishXRef, Publication, Phenotype, PublishFreeze
+                    WHERE
+                            PublishXRef.Id = %s AND
+                            Phenotype.Id = PublishXRef.PhenotypeId AND
+                            Publication.Id = PublishXRef.PublicationId AND
+                            PublishXRef.InbredSetId = PublishFreeze.InbredSetId AND
+                            PublishFreeze.Id = %s
+                    """ % (self.name, self.dataset.id)
+            traitInfo = g.db.execute(query).fetchone()
+        #XZ, 05/08/2009: Xiaodong add this block to use ProbeSet.Id to find the probeset instead of just using ProbeSet.Name
+        #XZ, 05/08/2009: to avoid the problem of same probeset name from different platforms.
+        elif self.dataset.type == 'ProbeSet':
+            display_fields_string = ', ProbeSet.'.join(self.dataset.display_fields)
+            display_fields_string = 'ProbeSet.' + display_fields_string
+            query = """
+                    SELECT %s
+                    FROM ProbeSet, ProbeSetFreeze, ProbeSetXRef
+                    WHERE
+                            ProbeSetXRef.ProbeSetFreezeId = ProbeSetFreeze.Id AND
+                            ProbeSetXRef.ProbeSetId = ProbeSet.Id AND
+                            ProbeSetFreeze.Name = '%s' AND
+                            ProbeSet.Name = '%s'
+                    """ % (escape(display_fields_string),
+                           escape(self.dataset.name),
+                           escape(self.name))
+            traitInfo = g.db.execute(query).fetchone()
+            print("traitInfo is: ", pf(traitInfo))
+        #XZ, 05/08/2009: We also should use Geno.Id to find marker instead of just using Geno.Name
+        # to avoid the problem of same marker name from different species.
+        elif self.dataset.type == 'Geno':
+            display_fields_string = string.join(self.dataset.display_fields,',Geno.')
+            display_fields_string = 'Geno.' + display_fields_string
+            query = """
+                    SELECT %s
+                    FROM Geno, GenoFreeze, GenoXRef
+                    WHERE
+                            GenoXRef.GenoFreezeId = GenoFreeze.Id AND
+                            GenoXRef.GenoId = Geno.Id AND
+                            GenoFreeze.Name = '%s' AND
+                            Geno.Name = '%s'
+                    """ % (escape(display_fields_string),
+                           escape(self.dataset.name),
+                           escape(self.name))
+            traitInfo = g.db.execute(query).fetchone()
+            print("traitInfo is: ", pf(traitInfo))
+        else: #Temp type
+            query = """SELECT %s FROM %s WHERE Name = %s
+                                     """ % (string.join(self.dataset.display_fields,','),
+                                            self.dataset.type, self.name)
+            traitInfo = g.db.execute(query).fetchone()
+
+
+        #self.cursor.execute(query)
+        #traitInfo = self.cursor.fetchone()
+        if traitInfo:
+            self.haveinfo = True
+
+            #XZ: assign SQL query result to trait attributes.
+            for i, field in enumerate(self.dataset.display_fields):
+                setattr(self, field, traitInfo[i])
+
+            if self.dataset.type == 'Publish':
+                self.confidential = 0
+                if self.pre_publication_description and not self.pubmed_id:
+                    self.confidential = 1
+
+            self.homologeneid = None
+            if self.dataset.type == 'ProbeSet' and self.dataset.group and self.geneid:
+                #XZ, 05/26/2010: From time to time, this query get error message because some geneid values in database are not number.
+                #XZ: So I have to test if geneid is number before execute the query.
+                #XZ: The geneid values in database should be cleaned up.
+                try:
+                    junk = float(self.geneid)
+                    geneidIsNumber = 1
+                except:
+                    geneidIsNumber = 0
+
+                if geneidIsNumber:
+                    query = """
+                            SELECT
+                                    HomologeneId
+                            FROM
+                                    Homologene, Species, InbredSet
+                            WHERE
+                                    Homologene.GeneId =%s AND
+                                    InbredSet.Name = '%s' AND
+                                    InbredSet.SpeciesId = Species.Id AND
+                                    Species.TaxonomyId = Homologene.TaxonomyId
+                            """ % (escape(str(self.geneid)), escape(self.dataset.group.name))
+                    result = g.db.execute(query).fetchone()
+                else:
+                    result = None
+
+                if result:
+                    self.homologeneid = result[0]
+
+            if QTL:
+                if self.dataset.type == 'ProbeSet' and not self.cellid:
+                    traitQTL = g.db.execute("""
+                            SELECT
+                                    ProbeSetXRef.Locus, ProbeSetXRef.LRS, ProbeSetXRef.pValue, ProbeSetXRef.mean
+                            FROM
+                                    ProbeSetXRef, ProbeSet
+                            WHERE
+                                    ProbeSetXRef.ProbeSetId = ProbeSet.Id AND
+                                    ProbeSet.Name = "%s" AND
+                                    ProbeSetXRef.ProbeSetFreezeId =%s
+                            """, (self.name, self.dataset.id)).fetchone()
+                    #self.cursor.execute(query)
+                    #traitQTL = self.cursor.fetchone()
+                    if traitQTL:
+                        self.locus, self.lrs, self.pvalue, self.mean = traitQTL
+                    else:
+                        self.locus = self.lrs = self.pvalue = self.mean = ""
+                if self.dataset.type == 'Publish':
+                    traitQTL = g.db.execute("""
+                            SELECT
+                                    PublishXRef.Locus, PublishXRef.LRS
+                            FROM
+                                    PublishXRef, PublishFreeze
+                            WHERE
+                                    PublishXRef.Id = %s AND
+                                    PublishXRef.InbredSetId = PublishFreeze.InbredSetId AND
+                                    PublishFreeze.Id =%s
+                            """, (self.name, self.dataset.id)).fetchone()
+                    #self.cursor.execute(query)
+                    #traitQTL = self.cursor.fetchone()
+                    if traitQTL:
+                        self.locus, self.lrs = traitQTL
+                    else:
+                        self.locus = self.lrs = ""
+        else:
+            raise KeyError, `self.name`+' information is not found in the database.'
+
+    def genHTML(self, formName = "", dispFromDatabase=0, privilege="guest", userName="Guest", authorized_users=""):
+        if not self.haveinfo:
+            self.retrieveInfo()
+
+        if self.dataset.type == 'Publish':
+            PubMedLink = ""
+            if self.pubmed_id:
+                PubMedLink = HT.Href(text="PubMed %d : " % self.pubmed_id,
+                target = "_blank", url = webqtlConfig.PUBMEDLINK_URL % self.pubmed_id)
+            else:
+                PubMedLink = HT.Span("Unpublished : ", Class="fs15")
+
+            if formName:
+                setDescription2 = HT.Href(url="javascript:showDatabase3('%s','%s','%s','')" %
+                (formName, self.dataset.name, self.name), Class = "fs14")
+            else:
+                setDescription2 = HT.Href(url="javascript:showDatabase2('%s','%s','')" %
+                (self.dataset.name,self.name), Class = "fs14")
+
+            if self.confidential and not webqtlUtil.hasAccessToConfidentialPhenotypeTrait(privilege=privilege, userName=userName, authorized_users=authorized_users):
+                setDescription2.append('RecordID/%s - %s' % (self.name, self.pre_publication_description))
+            else:
+                setDescription2.append('RecordID/%s - %s' % (self.name, self.post_publication_description))
+
+            #XZ 03/26/2011: Xiaodong comment out the following two lins as Rob asked. Need to check with Rob why in PublishXRef table, there are few row whose Sequence > 1.
+            #if self.sequence > 1:
+            #       setDescription2.append(' btach %d' % self.sequence)
+            if self.authors:
+                a1 = string.split(self.authors,',')[0]
+                while a1[0] == '"' or a1[0] == "'" :
+                    a1 = a1[1:]
+                setDescription2.append(' by ')
+                setDescription2.append(HT.Italic('%s, and colleagues' % a1))
+            setDescription = HT.Span(PubMedLink, setDescription2)
+
+        elif self.dataset.type == 'Temp':
+            setDescription = HT.Href(text="%s" % (self.description),url="javascript:showDatabase2\
+            ('%s','%s','')" % (self.dataset.name,self.name), Class = "fs14")
+            setDescription = HT.Span(setDescription)
+
+        elif self.dataset.type == 'Geno': # Genome DB only available for single search
+            if formName:
+                setDescription = HT.Href(text="Locus %s [Chr %s @ %s Mb]" % (self.name,self.chr,\
+        '%2.3f' % self.mb),url="javascript:showDatabase3('%s','%s','%s','')" % \
+        (formName, self.dataset.name, self.name), Class = "fs14")
+            else:
+                setDescription = HT.Href(text="Locus %s [Chr %s @ %s Mb]" % (self.name,self.chr,\
+        '%2.3f' % self.mb),url="javascript:showDatabase2('%s','%s','')" % \
+        (self.dataset.name,self.name), Class = "fs14")
+
+            setDescription = HT.Span(setDescription)
+
+        else:
+            if self.cellid:
+                if formName:
+                    setDescription = HT.Href(text="ProbeSet/%s/%s" % (self.name, self.cellid),url=\
+            "javascript:showDatabase3('%s','%s','%s','%s')" % (formName, self.dataset.name,self.name,self.cellid), \
+            Class = "fs14")
+                else:
+                    setDescription = HT.Href(text="ProbeSet/%s/%s" % (self.name,self.cellid),url=\
+            "javascript:showDatabase2('%s','%s','%s')" % (self.dataset.name,self.name,self.cellid), \
+            Class = "fs14")
+            else:
+                if formName:
+                    setDescription = HT.Href(text="ProbeSet/%s" % self.name, url=\
+            "javascript:showDatabase3('%s','%s','%s','')" % (formName, self.dataset.name,self.name), \
+            Class = "fs14")
+                else:
+                    setDescription = HT.Href(text="ProbeSet/%s" % self.name, url=\
+            "javascript:showDatabase2('%s','%s','')" % (self.dataset.name,self.name), \
+            Class = "fs14")
+            if self.symbol and self.chr and self.mb:
+                setDescription.append(' [')
+                setDescription.append(HT.Italic('%s' % self.symbol,Class="cdg fwb"))
+                setDescription.append(' on Chr %s @ %s Mb]' % (self.chr,self.mb))
+            if self.description:
+                setDescription.append(': %s' % self.description)
+            if self.probe_target_description:
+                setDescription.append('; %s' % self.probe_target_description)
+            setDescription = HT.Span(setDescription)
+
+        if self.dataset.type != 'Temp' and dispFromDatabase:
+            setDescription.append( ' --- FROM : ')
+            setDescription.append(self.dataset.genHTML(Class='cori'))
+        return setDescription
+
+    @property
+    def description_fmt(self):
+        '''Return a text formated description'''
+        if self.description:
+            formatted = self.description
+            if self.probe_target_description:
+                formatted += "; " + self.probe_target_description
+        else:
+            formatted = "Not available"
+        return formatted.capitalize()
+
+    @property
+    def alias_fmt(self):
+        '''Return a text formatted alias'''
+        if self.alias:
+            alias = string.replace(self.alias, ";", " ")
+            alias = string.join(string.split(alias), ", ")
+        return alias
+
+
+    @property
+    def location_fmt(self):
+        '''Return a text formatted location
+
+        While we're at it we set self.location in case we need it later (do we?)
+
+        '''
+
+        if self.chr and self.mb:
+            self.location = 'Chr %s @ %s Mb'  % (self.chr,self.mb)
+        elif self.chr:
+            self.location = 'Chr %s @ Unknown position' % (self.chr)
+        else:
+            self.location = 'Not available'
+
+        fmt = self.location
+        ##XZ: deal with direction
+        if self.strand_probe == '+':
+            fmt += (' on the plus strand ')
+        elif self.strand_probe == '-':
+            fmt += (' on the minus strand ')
+
+        return fmt
+
+
+    def get_database(self):
+        """
+        Returns the database, and the url referring to the database if it exists
+
+        We're going to to return two values here, and we don't want to have to call this twice from
+        the template. So it's not a property called from the template, but instead is called from the view
+
+        """
+        if self.cellid:
+            self.cursor.execute("""
+                            select ProbeFreeze.Name from ProbeFreeze, ProbeSetFreeze
+                                    where
+                            ProbeFreeze.Id = ProbeSetFreeze.ProbeFreezeId AND
+                            ProbeSetFreeze.Id = %d""" % thisTrait.dataset.id)
+            probeDBName = self.cursor.fetchone()[0]
+            return dict(name = probeDBName,
+                        url = None)
+        else:
+            return dict(name = self.dataset.fullname,
+                        url = webqtlConfig.INFOPAGEHREF % self.dataset.name)
+
+    def calculate_correlation(self, values, method):
+        """Calculate the correlation value and p value according to the method specified"""
+
+        #ZS: This takes the list of values of the trait our selected trait is being correlated against and removes the values of the samples our trait has no value for
+        #There's probably a better way of dealing with this, but I'll have to ask Christian
+        updated_raw_values = []
+        updated_values = []
+        for i in range(len(values)):
+            if values[i] != "None":
+                updated_raw_values.append(self.raw_values[i])
+                updated_values.append(values[i])
+
+        self.raw_values = updated_raw_values
+        values = updated_values
+
+        if method == METHOD_SAMPLE_PEARSON or method == METHOD_LIT or method == METHOD_TISSUE_PEARSON:
+            corr, nOverlap = webqtlUtil.calCorrelation(self.raw_values, values, len(values))
+        else:
+            corr, nOverlap = webqtlUtil.calCorrelationRank(self.raw_values, values, len(values))
+
+        self.correlation = corr
+        self.overlap = nOverlap
+
+        if self.overlap < 3:
+            self.p_value = 1.0
+        else:
+            #ZS - This is probably the wrong way to deal with this. Correlation values of 1.0 definitely exist (the trait correlated against itself), so zero division needs to br prevented.
+            if abs(self.correlation) >= 1.0:
+                self.p_value = 0.0
+            else:
+                ZValue = 0.5*log((1.0+self.correlation)/(1.0-self.correlation))
+                ZValue = ZValue*sqrt(self.overlap-3)
+                self.p_value = 2.0*(1.0 - reaper.normp(abs(ZValue)))
diff --git a/wqflask/base/webqtlCaseData.py b/wqflask/base/webqtlCaseData.py
new file mode 100755
index 00000000..42763aed
--- /dev/null
+++ b/wqflask/base/webqtlCaseData.py
@@ -0,0 +1,72 @@
+# Copyright (C) University of Tennessee Health Science Center, Memphis, TN.
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License
+# as published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU Affero General Public License for more details.
+#
+# This program is available from Source Forge: at GeneNetwork Project
+# (sourceforge.net/projects/genenetwork/).
+#
+# 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
+#
+# Last updated by GeneNetwork Core Team 2010/10/20
+
+print("Mr. Mojo Risin 2")
+
+class webqtlCaseData(object):
+    """one case data in one trait"""
+
+    def __init__(self, name, value=None, variance=None, num_cases=None):
+        self.name = name
+        self.value = value                  # Trait Value
+        self.variance = variance            # Trait Variance
+        self.num_cases = num_cases          # Number of individuals/cases
+        self.extra_attributes = None
+        self.this_id = None   # Set a sane default (can't be just "id" cause that's a reserved word)
+        self.outlier = None   # Not set to True/False until later
+
+    def __repr__(self):
+        str = "<webqtlCaseData> "
+        if self.value != None:
+            str += "value=%2.3f" % self.value
+        if self.variance != None:
+            str += " variance=%2.3f" % self.variance
+        if self.num_cases != None:
+            str += " ndata=%d" % self.num_cases
+        return str
+    
+    @property
+    def class_outlier(self):
+        """Template helper"""
+        if self.outlier:
+            return "outlier"
+        else:
+            return ""
+    
+    @property
+    def display_value(self):
+        if self.value:
+            return "%2.3f" % self.value
+        else:
+            return "x"
+        
+    @property
+    def display_variance(self):
+        if self.variance:
+            return "%2.3f" % self.variance
+        else:
+            return "x"
+
diff --git a/wqflask/base/webqtlConfig.py b/wqflask/base/webqtlConfig.py
new file mode 100755
index 00000000..755595e0
--- /dev/null
+++ b/wqflask/base/webqtlConfig.py
@@ -0,0 +1,73 @@
+from webqtlConfigLocal import *
+#########################################'
+#      Environment Variables - public
+#########################################
+
+#Debug Level
+#1 for debug, mod python will reload import each time
+DEBUG = 1
+
+#USER privilege
+USERDICT = {'guest':1,'user':2, 'admin':3, 'root':4}
+
+#minimum number of informative strains
+KMININFORMATIVE = 5
+
+#maximum number of traits for interval mapping
+MULTIPLEMAPPINGLIMIT = 11
+
+#maximum number of traits for correlation
+MAXCORR = 100
+
+#Daily download limit from one IP
+DAILYMAXIMUM = 1000
+
+#maximum LRS value
+MAXLRS = 460.0
+
+#temporary data life span
+MAXLIFE = 86400
+
+#MINIMUM Database public value
+PUBLICTHRESH = 0
+
+#NBCI address
+NCBI_LOCUSID = "http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?db=gene&cmd=Retrieve&dopt=Graphics&list_uids=%s"
+UCSC_REFSEQ = "http://genome.cse.ucsc.edu/cgi-bin/hgGene?db=%s&hgg_gene=%s&hgg_chrom=chr%s&hgg_start=%s&hgg_end=%s"
+GENBANK_ID = "http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?db=Nucleotide&cmd=search&doptcmdl=DocSum&term=%s"
+OMIM_ID = "http://www.ncbi.nlm.nih.gov/omim/%s"
+UNIGEN_ID = "http://www.ncbi.nlm.nih.gov/UniGene/clust.cgi?ORG=%s&CID=%s"
+HOMOLOGENE_ID = "http://www.ncbi.nlm.nih.gov/sites/entrez?Db=homologene&Cmd=DetailsSearch&Term=%s"
+PUBMEDLINK_URL = "http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&list_uids=%s&dopt=Abstract"
+UCSC_POS = "http://genome.ucsc.edu/cgi-bin/hgTracks?clade=mammal&org=%s&db=%s&position=chr%s:%s-%s&pix=800&Submit=submit"
+UCSC_BLAT = 'http://genome.ucsc.edu/cgi-bin/hgBlat?org=%s&db=%s&type=0&sort=0&output=0&userSeq=%s'
+UTHSC_BLAT = 'http://ucscbrowser.genenetwork.org/cgi-bin/hgBlat?org=%s&db=%s&type=0&sort=0&output=0&userSeq=%s'
+UCSC_GENOME = "http://genome.ucsc.edu/cgi-bin/hgTracks?db=%s&position=chr%s:%d-%d&hgt.customText=http://web2qtl.utmem.edu:88/snp/chr%s"
+ENSEMBLE_BLAT = 'http://www.ensembl.org/Mus_musculus/featureview?type=AffyProbe&id=%s'
+DBSNP = 'http://www.ncbi.nlm.nih.gov/SNP/snp_ref.cgi?type=rs&rs=%s'
+UCSC_RUDI_TRACK_URL = " http://genome.cse.ucsc.edu/cgi-bin/hgTracks?org=%s&db=%s&hgt.customText=http://gbic.biol.rug.nl/~ralberts/tracks/%s/%s"
+GENOMEBROWSER_URL="http://ucscbrowser.genenetwork.org/cgi-bin/hgTracks?clade=mammal&org=Mouse&db=mm9&position=%s&hgt.suggest=&pix=800&Submit=submit"
+ENSEMBLETRANSCRIPT_URL="http://useast.ensembl.org/Mus_musculus/Lucene/Details?species=Mus_musculus;idx=Transcript;end=1;q=%s"
+
+SECUREDIR = GNROOT + 'secure/'
+COMMON_LIB = GNROOT + 'support/admin'
+HTMLPATH = GNROOT + 'web/'
+IMGDIR = HTMLPATH +'image/'
+IMAGESPATH = HTMLPATH + 'images/'
+UPLOADPATH = IMAGESPATH + 'upload/'
+TMPDIR = '/tmp/'
+GENODIR = HTMLPATH + 'genotypes/'
+GENO_ARCHIVE_DIR = GENODIR + 'archive/'
+TEXTDIR = HTMLPATH + 'ProbeSetFreeze_DataMatrix/'
+CMDLINEDIR = HTMLPATH + 'webqtl/cmdLine/'
+ChangableHtmlPath = GNROOT + 'web/'
+
+SITENAME = 'GN'
+PORTADDR = "http://132.192.47.32"
+BASEHREF = '<base href="http://132.192.47.32/">'
+INFOPAGEHREF = '/dbdoc/%s.html'
+GLOSSARYFILE = "/glossary.html"
+CGIDIR = '/webqtl/' #XZ: The variable name 'CGIDIR' should be changed to 'PYTHONDIR'
+SCRIPTFILE = 'main.py'
+REFRESHSTR = '<meta http-equiv="refresh" content="5;url=%s' + SCRIPTFILE +'?sid=%s">'
+REFRESHDIR = '%s' + SCRIPTFILE +'?sid=%s'
diff --git a/wqflask/base/webqtlConfigLocal.py b/wqflask/base/webqtlConfigLocal.py
new file mode 100755
index 00000000..84686234
--- /dev/null
+++ b/wqflask/base/webqtlConfigLocal.py
@@ -0,0 +1,20 @@
+#########################################'
+#      Environment Variables - private
+#########################################
+
+MYSQL_SERVER = 'localhost'
+DB_NAME = 'db_webqtl_zas1024'
+DB_USER = 'webqtl'
+DB_PASSWD = 'webqtl'
+
+MYSQL_UPDSERVER = 'localhost'
+DB_UPDNAME = 'db_webqtl_zas1024'
+DB_UPDUSER = 'webqtl'
+DB_UPDPASSWD = 'webqtl'
+
+GNROOT = '/home/zas1024/gn/'
+ROOT_URL = 'http://alexandria.uthsc.edu:91/'
+PythonPath = '/usr/bin/python'
+PIDDLE_FONT_PATH = '/usr/lib/python2.4/site-packages/piddle/truetypefonts/'
+
+TEXTUI=1 # XZ: This is to protect GN production server. If set to 0, no text UI is allowed.
diff --git a/wqflask/base/webqtlFormData.py b/wqflask/base/webqtlFormData.py
new file mode 100755
index 00000000..a3537c87
--- /dev/null
+++ b/wqflask/base/webqtlFormData.py
@@ -0,0 +1,358 @@
+# Copyright (C) University of Tennessee Health Science Center, Memphis, TN.
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License
+# as published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU Affero General Public License for more details.
+#
+# This program is available from Source Forge: at GeneNetwork Project
+# (sourceforge.net/projects/genenetwork/).
+#
+# 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
+#
+# Last updated by GeneNetwork Core Team 2010/10/20
+
+#from mod_python import Cookie
+
+from __future__ import print_function
+from pprint import pformat as pf
+
+import string
+import os
+
+import reaper
+
+import webqtlConfig
+import cookieData
+import sessionData
+from cgiData import cgiData
+from webqtlCaseData import webqtlCaseData
+from utility import webqtlUtil
+
+
+
+
+class webqtlFormData(object):
+    'Represents data from a WebQTL form page, needed to generate the next page'
+
+    attrs = ('formID','group','genotype','samplelist','allsamplelist', 'display_variance'
+                'suggestive','significance','submitID','identification', 'enablevariance',
+                'nperm','nboot','email','incparentsf1','genotype_1','genotype_2','traitInfo')
+
+    #XZ: Attention! All attribute values must be picklable!
+
+    def __init__(self,
+                 start_vars = None,
+                 req = None,
+                 mod_python_session=None,
+                 FieldStorage_formdata=None):
+        # Todo: rework this whole thing
+        print("in webqtlFormData start_vars are:", pf(start_vars))
+        for item in webqtlFormData.attrs:
+            self.__dict__[item] = None
+
+        #ZS: This is only used in DataEditingPage.py (as far as I know)
+        self.varianceDispName = None
+
+        for item in start_vars:
+            self.__dict__[item] = start_vars[item]
+        print("  Now self.dict is:", pf(self.__dict__))
+
+        #Todo: This can't be good below...rework
+        try:
+            self.remote_ip = req.connection.remote_ip
+        except:
+            self.remote_ip = '1.2.3.4'
+
+        if req and req.headers_in.has_key('referer'):
+            self.refURL = req.headers_in['referer']
+        else:
+            self.refURL = None
+
+        # For now let's just comment all this out - Sam
+
+        #self.cookies = cookieData.cookieData(Cookie.get_cookies(req)) #XZ: dictionary type. To hold values transfered from mod_python Cookie.
+        #
+        ##XZ: dictionary type. To hold values transfered from mod_python Session object. We assume that it is always picklable.
+        #self.input_session_data = sessionData.sessionData( mod_python_session )
+        #
+        ##XZ: FieldStorage_formdata may contain item that can't be pickled. Must convert to picklable data.
+        #self.formdata = cgiData( FieldStorage_formdata )
+        #
+        ##get Form ID
+        #self.formID = self.formdata.getfirst('FormID')
+        #
+        ##get rest of the attributes
+        #if self.formID:
+        #       for item in self.attrs:
+        #               value = self.formdata.getfirst(item)
+        #               if value != None:
+        #                       setattr(self,item,string.strip(value))
+
+        self.ppolar = None
+        self.mpolar = None
+
+        print("[yellow] self.group is:", self.group)
+        if self.group:
+            #try:
+            #    # NL, 07/27/2010. ParInfo has been moved from webqtlForm.py to webqtlUtil.py;
+            _f1, _f12, self.mpolar, self.ppolar = webqtlUtil.ParInfo[self.group]
+            #except:
+            #    f1 = f12 = self.mpolar = self.ppolar = None
+
+
+        def set_number(stringy):
+            return int(stringy) if stringy else 2000 # Rob asked to change the default value to 2000
+
+        self.nperm = set_number(self.nperm)
+        self.nboot = set_number(self.nboot)
+
+
+        #if self.allsamplelist:
+        #    self.allsamplelist = map(string.strip, string.split(self.allsamplelist))
+        print("self.allsamplelist is:", self.allsamplelist)
+        if self.allsamplelist:
+            self.allsamplelist = self.allsamplelist.split()
+        print("now self.allsamplelist is:", self.allsamplelist)
+        #self.readGenotype()
+        #self.readData()
+
+        if self.group == 'BXD300':
+            self.group = 'BXD'
+
+
+    def __getitem__(self, key):
+        print("in __getitem__")
+        return self.__dict__[key]
+
+    def get(self, key, default=None):
+        if key in self.__dict__:
+            return self.__dict__[key]
+        else:
+            return default
+
+    def __str__(self):
+        rstr = ''
+        for item in self.attrs:
+            if item != 'genotype':
+                rstr += '%s:%s\n' % (item,str(getattr(self,item)))
+        return rstr
+
+
+    def readGenotype(self):
+        '''read genotype from .geno file'''
+        if self.group == 'BXD300':
+            self.group = 'BXD'
+
+        assert self.group, "self.group needs to be set"
+
+        #genotype_1 is Dataset Object without parents and f1
+        #genotype_2 is Dataset Object with parents and f1 (not for intercross)
+
+        self.genotype_1 = reaper.Dataset()
+
+        full_filename = os.path.join(webqtlConfig.GENODIR, self.group + '.geno')
+
+        # reaper barfs on unicode filenames, so here we ensure it's a string
+        full_filename = str(full_filename)
+        self.genotype_1.read(full_filename)
+
+        print("Got to after read")
+
+        try:
+            # NL, 07/27/2010. ParInfo has been moved from webqtlForm.py to webqtlUtil.py;
+            _f1, _f12, _mat, _pat = webqtlUtil.ParInfo[self.group]
+        except KeyError:
+            _f1 = _f12 = _mat = _pat = None
+
+        self.genotype_2 = self.genotype_1
+        if self.genotype_1.type == "group" and _mat and _pat:
+            self.genotype_2 = self.genotype_1.add(Mat=_mat, Pat=_pat)       #, F1=_f1)
+
+        #determine default genotype object
+        if self.incparentsf1 and self.genotype_1.type != "intercross":
+            self.genotype = self.genotype_2
+        else:
+            self.incparentsf1 = 0
+            self.genotype = self.genotype_1
+
+        self.samplelist = list(self.genotype.prgy)
+        self.f1list = []
+        self.parlist = []
+
+        if _f1 and _f12:
+            self.f1list = [_f1, _f12]
+        if _mat and _pat:
+            self.parlist = [_mat, _pat]
+
+
+    def readData(self, samplelist, incf1=None):
+        '''read user input data or from trait data and analysis form'''
+
+        if incf1 == None:
+            incf1 = []
+
+        if not self.genotype:
+            self.readGenotype()
+        if not samplelist:
+            if incf1:
+                samplelist = self.f1list + self.samplelist
+            else:
+                samplelist = self.samplelist
+
+        #print("before traitfiledata self.traitfile is:", pf(self.traitfile))
+
+        traitfiledata = getattr(self, "traitfile", None)
+        traitpastedata = getattr(self, "traitpaste", None)
+        variancefiledata = getattr(self, "variancefile", None)
+        variancepastedata = getattr(self, "variancepaste", None)
+        Nfiledata = getattr(self, "Nfile", None)
+
+        #### Todo: Rewrite below when we get to someone submitting their own trait #####
+
+        def to_float(item):
+            try:
+                return float(item)
+            except ValueError:
+                return None
+
+        print("bottle samplelist is:", samplelist)
+        if traitfiledata:
+            tt = traitfiledata.split()
+            values = map(webqtlUtil.StringAsFloat, tt)
+        elif traitpastedata:
+            tt = traitpastedata.split()
+            values = map(webqtlUtil.StringAsFloat, tt)
+        else:
+            print("mapping formdataasfloat")
+            #values = map(self.FormDataAsFloat, samplelist)
+            values = [to_float(getattr(self, key)) for key in samplelist]
+        print("rocket values is:", values)
+
+
+        if len(values) < len(samplelist):
+            values += [None] * (len(samplelist) - len(values))
+        elif len(values) > len(samplelist):
+            values = values[:len(samplelist)]
+        print("now values is:", values)
+
+
+        if variancefiledata:
+            tt = variancefiledata.split()
+            variances = map(webqtlUtil.StringAsFloat, tt)
+        elif variancepastedata:
+            tt = variancepastedata.split()
+            variances = map(webqtlUtil.StringAsFloat, tt)
+        else:
+            variances = map(self.FormVarianceAsFloat, samplelist)
+
+        if len(variances) < len(samplelist):
+            variances += [None]*(len(samplelist) - len(variances))
+        elif len(variances) > len(samplelist):
+            variances = variances[:len(samplelist)]
+
+        if Nfiledata:
+            tt = string.split(Nfiledata)
+            nsamples = map(webqtlUtil.IntAsFloat, tt)
+            if len(nsamples) < len(samplelist):
+                nsamples += [None]*(len(samplelist) - len(nsamples))
+        else:
+            nsamples = map(self.FormNAsFloat, samplelist)
+
+        ##values, variances, nsamples is obsolete
+        self.allTraitData = {}
+        for i, _sample in enumerate(samplelist):
+            if values[i] != None:
+                self.allTraitData[_sample] = webqtlCaseData(
+                    _sample, values[i], variances[i], nsamples[i])
+        print("allTraitData is:", pf(self.allTraitData))
+
+
+
+    def informativeStrains(self, samplelist=None, include_variances = None):
+        '''if readData was called, use this to output informative samples (sample with values)'''
+
+        if not samplelist:
+            samplelist = self.samplelist
+
+        samples = []
+        values = []
+        variances = []
+
+        #print("self.allTraitData is:", pf(self.allTraitData))
+
+        for sample in samplelist:
+            if sample in self.allTraitData:
+                _val, _var = self.allTraitData[sample].value, self.allTraitData[sample].variance
+                if _val != None:
+                    if include_variances:
+                        if _var != None:
+                            samples.append(sample)
+                            values.append(_val)
+                            variances.append(_var)
+                    else:
+                        samples.append(sample)
+                        values.append(_val)
+                        variances.append(None)
+
+        return samples, values, variances, len(samples)
+
+
+
+    #def FormDataAsFloat(self, key):
+    #
+    #    #try:
+    #    #    return float(self.key)
+    #    #except:
+    #    #    return None
+
+
+    def FormVarianceAsFloat(self, key):
+        try:
+            return float(self.formdata.getfirst('V' + key))
+        except:
+            return None
+
+    def FormNAsFloat(self, key):
+        try:
+            return int(self.formdata.getfirst('N' + key))
+        except:
+            return None
+
+    def Sample(self):
+        'Create some dummy data for testing'
+        self.group = 'BXD'
+        self.incparentsf1 = 'on'
+        #self.display = 9.2
+        #self.significance = 16.1
+        self.readGenotype()
+        self.identification = 'BXD : Coat color example by Lu Lu, et al'
+        #self.readGenotype()
+        #self.genotype.ReadMM('AXBXAforQTL')
+        #self.samplelist = map((lambda x, y='': '%s%s' % (y,x)), self.genotype.prgy)
+        #self.samplelist.sort()
+        self.allTraitData = {'BXD29': webqtlCaseData(3), 'BXD28': webqtlCaseData(2),
+        'BXD25': webqtlCaseData(2), 'BXD24': webqtlCaseData(2), 'BXD27': webqtlCaseData(2),
+        'BXD21': webqtlCaseData(1), 'BXD20': webqtlCaseData(4), 'BXD23': webqtlCaseData(4),
+        'BXD22': webqtlCaseData(3), 'BXD14': webqtlCaseData(4), 'BXD15': webqtlCaseData(2),
+        'BXD16': webqtlCaseData(3), 'BXD11': webqtlCaseData(4), 'BXD12': webqtlCaseData(3),
+        'BXD13': webqtlCaseData(2), 'BXD18': webqtlCaseData(3), 'BXD19': webqtlCaseData(3),
+        'BXD38': webqtlCaseData(3), 'BXD39': webqtlCaseData(3), 'BXD36': webqtlCaseData(2),
+        'BXD34': webqtlCaseData(4), 'BXD35': webqtlCaseData(4), 'BXD32': webqtlCaseData(4),
+        'BXD33': webqtlCaseData(3), 'BXD30': webqtlCaseData(1), 'BXD31': webqtlCaseData(4),
+        'DBA/2J': webqtlCaseData(1), 'BXD8': webqtlCaseData(3), 'BXD9': webqtlCaseData(1),
+        'BXD6': webqtlCaseData(3), 'BXD5': webqtlCaseData(3), 'BXD2': webqtlCaseData(4),
+        'BXD1': webqtlCaseData(1), 'C57BL/6J': webqtlCaseData(4), 'B6D2F1': webqtlCaseData(4),
+        'BXD42': webqtlCaseData(4), 'BXD40': webqtlCaseData(3)}