# 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 import string import os import cPickle from math import * import reaper from htmlgen import HTMLgen2 as HT from base import admin from base import webqtlConfig from base.templatePage import templatePage from utility import webqtlUtil from base.webqtlDataset import webqtlDataset from base.webqtlTrait import webqtlTrait from utility.THCell import THCell from utility.TDCell import TDCell from utility import webqtlUtil class TextSearchPage(templatePage): maxReturn = 200 def __init__(self, fd): templatePage.__init__(self, fd) if not self.openMysql(): return # updated by NL, deleted jquery here, move it to dhtml.js self.dict['js1'] = '' species_list = [] #List of species (mouse, rat, human), with the selected species listed first input_species = string.strip(string.lower(fd.formdata.getfirst('species', "mouse"))) #XZ, Oct 28, 2009: I changed the default species to mouse. species_list.append(input_species) #Create list of species (mouse, rat, human) with the species the user selected first for species in ["mouse","rat","human"]: if species not in species_list: species_list.append(species) ADMIN_tissue_alias = admin.ADMIN_tissue_alias tissue = string.strip(string.lower(fd.formdata.getfirst('tissue', ""))) if tissue: try: rev_ADMIN_tissue_alias = {} for key in ADMIN_tissue_alias.keys(): rev_ADMIN_tissue_alias[key] = key for alias in ADMIN_tissue_alias[key]: rev_ADMIN_tissue_alias[alias.upper()] = key tissue = rev_ADMIN_tissue_alias[tissue.upper()] except: tissue = "UNKNOWN" #possibly text output txtOutput = [] #ZS: if format=text all_species_dataset_count = 0 #XZ: count of datasets across all species; used in the opening text of the page all_species_trait_count = 0 #XZ: count of records across all species; used in opening text of the page and text file (if format = text) #div containing the tabs (species_container), the tabs themselves (species_tab_list, which is inserted into species_tabs), and the table (species_table) containing both the tissue and results tables for each tab species_container = HT.Div(id="species_tabs", Class="tab_container") #Div that will contain tabs for mouse/rat/human species; each tab contains a table with the result count for each tissue group species_tab_list = [HT.Href(text="%s" % species_list[0].capitalize(), url="#species1"), HT.Href(text="%s" % species_list[1].capitalize(), url="#species2"), HT.Href(text="%s" % species_list[2].capitalize(), url="#species3")] species_tabs = HT.List(species_tab_list, Class="tabs") species_table = HT.TableLite(cellSpacing=0,cellPadding=0,width="100%",border=0, align="Left") for i in range(len(species_list)): species_container_table = HT.TableLite(cellSpacing=0,cellPadding=0,width="100%",border=0, align="Left") #ZS: Table containing both the tissue record count and trait record tables as cells; this fixes a display issue in some browsers that places the tables side by side instead of top/bottom species = species_list[i] ADMIN_search_dbs = admin.ADMIN_search_dbs[species] this_species_dataset_count = 0 #XZ: count of the datasets containing results for this species this_species_trait_count = 0 #XZ: count of the trait records for this species div = HT.Div(id="species%s" % (i+1), Class="tab_content") tab_container = HT.Span() #ZS: Contains species_container_table within the species' tab tissuePageTable = HT.TableLite(cellSpacing=0,cellPadding=0,width="100%",border=0, align="Left") tissue_tblobj = {} # object used to create the table listing the results for each tissue tissue_tblobj['header'] = self.getTissueTableHeader() # creates header for table listing results for selected tissue traitPageTable = HT.TableLite(cellSpacing=0,cellPadding=0,width="100%",border=0, align="Left") trait_tblobj = {} # object used to create the table listing the trait results for each tissue trait_tblobj['header'] = self.getTraitTableHeader() # creates header for table listing trait results for selected tissue tissue_tblobj['body'], trait_tblobj['body'], this_species_dataset_count, this_species_trait_count, this_species_txtOutput = self.createTableBodies(fd, species, tissue, ADMIN_search_dbs) if species == input_species: txtOutput = this_species_txtOutput filename1 = webqtlUtil.genRandStr("Search_") #filename for tissue table object tissue_objfile = open('%s.obj' % (webqtlConfig.TMPDIR+filename1), 'wb') cPickle.dump(tissue_tblobj, tissue_objfile) tissue_objfile.close() tissue_sortby = self.getTissueSortByValue() # sets how the tissue table should be sorted by default tissue_div = HT.Div(webqtlUtil.genTableObj(tblobj=tissue_tblobj, file=filename1, sortby=tissue_sortby, tableID = "tissue_sort%s" % (i+1), addIndex = "1"), Id="tissue_sort%s" % (i+1)) tissuePageTable.append(HT.TR(HT.TD(" "))) tissuePageTable.append(HT.TR(HT.TD(tissue_div))) tissuePageTable.append(HT.TR(HT.TD(" "))) species_container_table.append(HT.TR(HT.TD(tissuePageTable)), HT.TR(HT.TD(" "))) filename2 = webqtlUtil.genRandStr("Search_") #filename for trait table object trait_objfile = open('%s.obj' % (webqtlConfig.TMPDIR+filename2), 'wb') cPickle.dump(trait_tblobj, trait_objfile) trait_objfile.close() trait_sortby = self.getTraitSortByValue() # sets how the trait table should be sorted by default trait_div = HT.Div(webqtlUtil.genTableObj(tblobj=trait_tblobj, file=filename2, sortby=trait_sortby, tableID = "results_sort%s" % (i+1), addIndex = "0"), Id="results_sort%s" % (i+1)) traitPageTable.append(HT.TR(HT.TD(" "))) traitPageTable.append(HT.TR(HT.TD(trait_div))) traitPageTable.append(HT.TR(HT.TD(" "))) species_container_table.append(HT.TR(HT.TD(traitPageTable)), HT.TR(HT.TD(" "))) if this_species_trait_count == 0: tab_container.append(HT.Div("No records retrieved for this species.", align="left", valign="top", style="font-size:42")) else: tab_container.append(species_container_table) all_species_dataset_count += this_species_dataset_count all_species_trait_count += this_species_trait_count div.append(tab_container) species_table.append(HT.TR(HT.TD(div))) species_container.append(species_table) if fd.returnFmt != 'text': #if the format is not 'text' self.dict['title'] = 'Search Results' TD_LR = HT.TD(height=100,width="100%",bgColor='#fafafa',valign="top") pageTable = HT.TableLite(cellSpacing=0,cellPadding=0,width="100%",border=0, align="Left") # Table containing all of the page's elements (opening text, form); in some browers the elements arrange themselves horizontally if you don't put them into a table, so this fixes that problem formTable = HT.TableLite(cellSpacing=2,cellPadding=0,width="100%",border=0) # Table containing all of the form's elements (tabs, option buttons); used to correct the same issue mentioned in pageTable's comment mainForm = HT.Form( cgi= os.path.join(webqtlConfig.CGIDIR, webqtlConfig.SCRIPTFILE), enctype='multipart/form-data', name='showDatabase', submit=HT.Input(type='hidden')) hddn = {'FormID':'showDatabase','ProbeSetID':'_','database':'_','CellID':'_','RISet':fd.RISet} hddn['incparentsf1']='ON' for key in hddn.keys(): mainForm.append(HT.Input(name=key, value=hddn[key], type='hidden')) #Add to collection, select all, invert selection, and deselect all ("reset") buttons addselect = HT.Href(url="#redirect", Class="add_traits") addselect_img = HT.Image("/images/add_collection1_final.jpg", name="addselect", alt="Add To Collection", title="Add To Collection", style="border:none;") addselect.append(addselect_img) selectall = HT.Href(url="#redirect", onClick="checkAll(document.getElementsByName('showDatabase')[0]);") selectall_img = HT.Image("/images/select_all2_final.jpg", name="selectall", alt="Select All", title="Select All", style="border:none;") selectall.append(selectall_img) selectinvert = HT.Href(url="#redirect", onClick="checkInvert(document.getElementsByName('showDatabase')[0];") selectinvert_img = HT.Image("/images/invert_selection2_final.jpg", name="selectinvert", alt="Invert Selection", title="Invert Selection", style="border:none;") selectinvert.append(selectinvert_img) reset = HT.Href(url="#redirect", onClick="checkNone(document.getElementsByName('showDatabase')[0]); return false;") reset_img = HT.Image("/images/select_none2_final.jpg", alt="Select None", title="Select None", style="border:none;") reset.append(reset_img) #Table with select, deselect, invert, etc. It is used for the results table. optionsTable = HT.TableLite(cellSpacing=2,cellPadding=0,width="20%",border=0) optionsRow = HT.TR(HT.TD(selectall, width="25%"), HT.TD(reset, width="25%"), HT.TD(selectinvert, width="25%"), HT.TD(addselect, width="25%")) labelsRow = HT.TR(HT.TD(" "*2,"Select", width="25%"), HT.TD(" ","Deselect", width="25%"), HT.TD(" "*3,"Invert", width="25%"), HT.TD(" "*4,"Add", width="25%")) optionsTable.append(HT.TR(HT.TD(" ")), optionsRow, labelsRow) if fd.geneName: searchType = "gene name " + fd.geneName elif fd.refseq: searchType = "RefSeq accession number " + fd.refseq elif fd.genbankid: searchType = "Genbank ID " + fd.genbankid elif fd.geneid: searchType = "Gene ID " + fd.geneid else: searchType = "" SearchText = HT.Span("You searched for the %s in GeneNetwork." % searchType, HT.BR(), "We queried %s expression datasets across %s species and listed the results" % (all_species_dataset_count, len(species_list)), HT.BR(), "below. A total of %s records that may be of interest to you were found. The" % all_species_trait_count, HT.BR(), "top table lists the number of results found for each relevant tissue, and the", HT.BR(), "bottom gives a basic summary of each result. To study one of the results, click", HT.BR(), "its Record ID. More detailed information is also available for each result's group", HT.BR() , "and dataset. To switch between species, click the tab with the corresponding", HT.BR(), "label.", HT.BR(), HT.BR(), "Please visit the links to the right to learn more about the variety of features", HT.BR(), "available within GeneNetwork.") LinkText = HT.Span() mainLink = HT.Href(url="/webqtl/main.py", text = "Main Search Page", target="_blank") homeLink = HT.Href(url="/home.html", text = "What is GeneNetwork?", target="_blank") tourLink = HT.Href(url="/tutorial/WebQTLTour/", text = "Tour of GeneNetwork (20-40 min)", target="_blank") faqLink = HT.Href(url="/faq.html", text = "Frequently Asked Questions", target="_blank") glossaryLink = HT.Href(url="/glossary.html", text = "Glossary of terms used throughout GeneNetwork", target="_blank") LinkText.append(mainLink, HT.BR(), homeLink, HT.BR(), tourLink, HT.BR(), faqLink, HT.BR(), glossaryLink) formTable.append(HT.TR(HT.TD(species_tabs, species_container)), HT.TR(HT.TD(optionsTable))) mainForm.append(formTable) if fd.geneName: SearchHeading = HT.Paragraph('Search Results for gene name ', fd.geneName) elif fd.refseq: SearchHeading = HT.Paragraph('Search Results for RefSeq accession number ', fd.refseq) elif fd.genbankid: SearchHeading = HT.Paragraph('Search Results for Genbank ID ', fd.genbankid) elif fd.geneid: SearchHeading = HT.Paragraph('Search Results for Gene ID ', fd.geneid) else: SearchHeading = HT.Paragraph('') SearchHeading.__setattr__("class","title") pageTable.append(HT.TR(HT.TD(SearchText, width=600), HT.TD(LinkText, align="left", valign="top")), HT.TR(HT.TD(" ", colspan=2)), HT.TR(HT.TD(mainForm, colspan=2))) TD_LR.append(SearchHeading, pageTable) self.dict['body'] = TD_LR else: if len(txtOutput) == 0: self.output = "##No records were found for this species. \n" else: self.output = "##A total of %d records were returned. \n" % all_species_trait_count newOutput = [] strainLists = {} for item in txtOutput: tissueGrp, thisTrait = item RISet = thisTrait.riset if strainLists.has_key(RISet): thisStrainlist = strainLists[RISet] else: thisGenotype = reaper.Dataset() thisGenotype.read(os.path.join(webqtlConfig.GENODIR, RISet + '.geno')) if thisGenotype.type == "riset": _f1, _f12, _mat, _pat = webqtlUtil.ParInfo[RISet] thisGenotype = thisGenotype.add(Mat=_mat, Pat=_pat, F1=_f1) thisStrainlist = list(thisGenotype.prgy) strainLists[RISet] = thisStrainlist thisTrait.retrieveData(strainlist=thisStrainlist) thisData = [] for item in thisStrainlist: if thisTrait.data.has_key(item): thisData.append(thisTrait.data[item].val) else: thisData.append(None) newOutput.append(["Structure", "Database", "ProbeSetID", "Cross"] + thisStrainlist) newOutput.append([tissueGrp, '"%s"' % thisTrait.db.fullname, thisTrait.name, RISet]+map(str,thisData)) newOutput = webqtlUtil.asymTranspose(newOutput) for item in newOutput: self.output += string.join(item, "\t") + "\n" def createTableBodies(self, fd, species, tissue, ADMIN_search_dbs): this_species_txtOutput = [] #priority GeneName > refseq > genbankid this_species_trait_count = 0 #count of all traits in this species this_species_dataset_count = 0 #Number of datasets in this species row_count = 0 #Index number used in the first row of the trait table trait_tblobj_body = [] #body of table with the results themselves; tissue_tblobj_body = [] #body of table with the number of results for each tissue group className = "fs12 fwn b1 c222" for i, tissueGrp in enumerate(ADMIN_search_dbs.keys()): if tissue and tissue.upper() != tissueGrp.upper(): continue dbNames = ADMIN_search_dbs[tissueGrp] tissue_tr = [] #Table row for tissue group tissue_tr.append(TDCell(HT.TD('', Class=className))) tissue_tr.append(TDCell(HT.TD(tissueGrp.capitalize(), Class=className), tissueGrp, tissueGrp)) #Append cell with tissue name to row this_tissue_record_count = 0 #Count of the results for each tissue for dbName in dbNames: this_species_dataset_count += 1 thisDB = webqtlDataset(dbName, self.cursor) if fd.geneName: if fd.searchAlias: self.cursor.execute("""SELECT ProbeSet.Name FROM ProbeSet, ProbeSetFreeze, ProbeSetXRef WHERE ProbeSetFreeze.Name = "%s" AND ProbeSetFreeze.Id = ProbeSetXRef.ProbeSetFreezeId AND MATCH (ProbeSet.symbol, alias) AGAINST ("+%s" IN BOOLEAN MODE) AND ProbeSet.Id = ProbeSetXRef.ProbeSetId""" % (dbName, fd.geneName)) else: self.cursor.execute("""SELECT ProbeSet.Name FROM ProbeSet, ProbeSetFreeze, ProbeSetXRef WHERE ProbeSetFreeze.Name = "%s" AND ProbeSetFreeze.Id = ProbeSetXRef.ProbeSetFreezeId AND ProbeSet.symbol = "%s" AND ProbeSet.Id = ProbeSetXRef.ProbeSetId""" % (dbName, fd.geneName)) elif fd.refseq: # XZ, Oct/08/2009: Search for RefSeq ID is kind of tricky. One probeset can have multiple RefseqIDs that are delimited by ' /// ' (currently). # So I have to use 'like' instead of '=' in SQL query. But user search with one short string, for example 'NM_1', it will return thousands of results. # To prevent this, I set the restriction that the length of input Refseq ID must be at least 9 characters. Otherwise, do NOT start searching. # Even with the restriction of input RefSeqID, I'm still worried about the 'like' in SQL query. My concern is in future, there might be RefSeqIDs with # 10 characters whose first 9 characters are the same as the existing ones. So I decide to further check the result. We should also consider that the # RefSeqID in database may have version number such as "NM_177938.2". If the input RefSeqID is 'NM_177938', it should be matched. I think we should get rid of the version number in database. if len(fd.refseq) < 9: if fd.returnFmt != 'text': heading = "Search Result" detail = ["The RefSeq ID that you inputed is less than 9 characters. GeneNetwork thinks it is not a legitimate RefSeq ID and did not do the search. Please try to use a RefSeq ID with at least 9 characters."] self.error(heading=heading,detail=detail,error="Not Found") else: self.output = "#The gene name or IDs you submitted did not match any record in the databases available. You may try different gene names or tissue type." return else: sqlString = """SELECT ProbeSet.Id, ProbeSet.RefSeq_TranscriptId FROM ProbeSet, ProbeSetFreeze, ProbeSetXRef WHERE ProbeSetFreeze.Name = "%s" AND ProbeSetFreeze.Id = ProbeSetXRef.ProbeSetFreezeId AND MATCH(ProbeSet.RefSeq_TranscriptId) AGAINST ("+%s" IN BOOLEAN MODE) AND ProbeSet.Id = ProbeSetXRef.ProbeSetId""" % (dbName, fd.refseq) self.cursor.execute(sqlString) results = self.cursor.fetchall() if results: Id_of_really_matched_probeset = [] for one_result in results: ProbeSet_Id, ProbeSet_RefSeq_TranscriptId = one_result multiple_RefSeqId = string.split(string.strip(ProbeSet_RefSeq_TranscriptId), '///') for one_RefSeqId in multiple_RefSeqId: tokens = string.split( one_RefSeqId, '.' ) one_RefSeqId_without_versionNum = string.strip(tokens[0]) if one_RefSeqId_without_versionNum == fd.refseq: Id_of_really_matched_probeset.append( ProbeSet_Id ) break if Id_of_really_matched_probeset: condition_string = " or ".join(["Id = %s" % one_ID for one_ID in Id_of_really_matched_probeset]) sqlString = """SELECT ProbeSet.Name from ProbeSet where (%s)""" % condition_string self.cursor.execute(sqlString) else: pass elif fd.genbankid: self.cursor.execute("""SELECT ProbeSet.Name FROM ProbeSet, ProbeSetFreeze, ProbeSetXRef WHERE ProbeSetFreeze.Name = "%s" AND ProbeSetFreeze.Id = ProbeSetXRef.ProbeSetFreezeId AND ProbeSet.GenbankId = "%s" AND ProbeSet.Id = ProbeSetXRef.ProbeSetId""" % (dbName, fd.genbankid)) elif fd.geneid: self.cursor.execute("""SELECT ProbeSet.Name FROM ProbeSet, ProbeSetFreeze, ProbeSetXRef WHERE ProbeSetFreeze.Name = "%s" AND ProbeSetFreeze.Id = ProbeSetXRef.ProbeSetFreezeId AND ProbeSet.GeneId = "%s" AND ProbeSet.Id = ProbeSetXRef.ProbeSetId""" % (dbName, fd.geneid)) else: continue results = self.cursor.fetchall() if len(results) > 0: this_tissue_record_count += len(results) this_species_trait_count += this_tissue_record_count for result in results: _ProbeSetID = result[0] thisTrait = webqtlTrait(db=thisDB, name=_ProbeSetID, cursor=self.cursor) results_tr = [] trId = str(thisTrait) _traitUrl = thisTrait.genHTML(dispFromDatabase=1) _traitName = str(thisTrait) #ZS: check box column results_tr.append(TDCell(HT.TD(str(row_count+1), HT.Input(type="checkbox", Class="checkallbox", name="searchResult",value=trId, onClick="highlight(this)"), nowrap="on", align="right", Class=className), str(row_count+1), row_count+1)) row_count += 1 #ZS: Tissue column results_tr.append(TDCell(HT.TD(tissueGrp.capitalize(), Class=className), tissueGrp, tissueGrp)) #ZS: Group column risetUrl = HT.Href(text=thisTrait.riset, url="http://www.genenetwork.org/%sCross.html#%s" % (species, thisTrait.riset), target="_blank", Class=className) results_tr.append(TDCell(HT.TD(risetUrl, Class=className), thisTrait.riset, thisTrait.riset)) #ZS: Dataset column results_tr.append(TDCell(HT.TD(HT.Href(text=thisTrait.db.fullname, url = webqtlConfig.INFOPAGEHREF % thisTrait.db.name, target='_blank', Class="fs13 fwn non_bold"), Class=className), thisTrait.db.name.upper(), thisTrait.db.name.upper())) #ZS: Trait ID column results_tr.append(TDCell(HT.TD(HT.Href(text=thisTrait.getGivenName(),url="javascript:showDatabase3('%s','%s','%s','')" % ('showDatabase', thisTrait.db.name, thisTrait.name), Class="fs12 fwn"), nowrap="yes",align="left", Class=className),str(thisTrait.name), thisTrait.name)) #ZS: Symbol column and Description column description_string = str(thisTrait.description).strip() if (thisTrait.db.type == "ProbeSet"): target_string = str(thisTrait.probe_target_description).strip() description_display = '' if len(description_string) > 1 and description_string != 'None': description_display = description_string else: description_display = thisTrait.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() description_string = description_display else: results_tr.append(TDCell(HT.TD("--", align="left", Class=className), "--", "Zz")) results_tr.append(TDCell(HT.TD(description_string, Class=className), description_string, description_string)) #XZ: trait_location_value is used for sorting trait_location_repr = "--" trait_location_value = 1000000 if hasattr(thisTrait, 'chr') and hasattr(thisTrait, 'mb') and thisTrait.chr and thisTrait.mb: try: trait_location_value = int(thisTrait.chr)*1000 + thisTrait.mb except: if thisTrait.chr.upper() == "X": trait_location_value = 20*1000 + thisTrait.mb else: trait_location_value = ord(str(thisTrait.chr).upper()[0])*1000 + thisTrait.mb trait_location_repr = "Chr%s: %.6f" % (thisTrait.chr, float(thisTrait.mb) ) results_tr.append(TDCell(HT.TD(trait_location_repr, nowrap='ON', Class=className), trait_location_repr, trait_location_value)) #ZS: Mean column self.cursor.execute(""" select ProbeSetXRef.mean from ProbeSetXRef, ProbeSet where ProbeSetXRef.ProbeSetFreezeId = %d and ProbeSet.Id = ProbeSetXRef.ProbeSetId and ProbeSet.Name = '%s' """ % (thisTrait.db.id, thisTrait.name)) result = self.cursor.fetchone() if result: if result[0]: mean = result[0] else: mean=0 else: mean = 0 repr = "%2.3f" % mean results_tr.append(TDCell(HT.TD(repr, Class=className, align='right', nowrap='ON'),repr, mean)) trait_tblobj_body.append(results_tr) this_species_txtOutput.append([tissueGrp, thisTrait]) tissue_tr.append(TDCell(HT.TD(str(this_tissue_record_count), Class=className), str(this_tissue_record_count), this_tissue_record_count)) tissue_tblobj_body.append(tissue_tr) self.output = "self.output" return tissue_tblobj_body, trait_tblobj_body, this_species_dataset_count, this_species_trait_count, this_species_txtOutput def getTissueSortByValue(self): sortby = ("tissue_group", "up") return sortby def getTraitSortByValue(self): sortby = ("tissue", "up") return sortby def getTissueTableHeader(self): tblobj_header = [] className = "fs13 fwb ffl b1 cw cbrb" tblobj_header = [[THCell(HT.TD(' ', Class=className, nowrap="on"), sort=0), THCell(HT.TD('Tissue',HT.BR(), HT.BR(), valign="top", Class=className, nowrap="on"), text="tissue_group", idx=1), THCell(HT.TD('Results', HT.BR(), HT.BR(), valign="top", Class=className, nowrap="on"), text="results", idx=2)]] return tblobj_header def getTraitTableHeader(self): tblobj_header = [] className = "fs13 fwb ffl b1 cw cbrb" tblobj_header = [[THCell(HT.TD('Index',HT.BR(), HT.BR(), valign="top", Class=className, nowrap="on"), text="index", idx=0), THCell(HT.TD('Tissue',HT.BR(), HT.BR(), valign="top", Class=className, nowrap="on"), text="tissue", idx=1), THCell(HT.TD('Group',HT.BR(), HT.BR(), valign="top", Class=className, nowrap="on"), text="group", idx=2), THCell(HT.TD('Dataset', HT.BR(), HT.BR(), valign="top", Class=className, nowrap="on"), text="dataset", idx=3), THCell(HT.TD('Record ID', HT.BR(), HT.BR(), valign="top", Class=className, nowrap="on"), text="name", idx=4), THCell(HT.TD('Description', HT.BR(), HT.BR(), valign="top", Class=className, nowrap="on"), text="desc", idx=5), THCell(HT.TD('Location', HT.BR(), 'Chr and Mb', HT.BR(), valign="top", Class=className, nowrap="on"), text="location", idx=6), THCell(HT.TD('Mean', HT.BR(), 'Expr', HT.BR(), valign="top", Class=className, nowrap="on"), text="mean", idx=7)]] return tblobj_header