about summary refs log tree commit diff
path: root/wqflask/utility/webqtlUtil.py
diff options
context:
space:
mode:
Diffstat (limited to 'wqflask/utility/webqtlUtil.py')
-rwxr-xr-xwqflask/utility/webqtlUtil.py977
1 files changed, 977 insertions, 0 deletions
diff --git a/wqflask/utility/webqtlUtil.py b/wqflask/utility/webqtlUtil.py
new file mode 100755
index 00000000..6af7f846
--- /dev/null
+++ b/wqflask/utility/webqtlUtil.py
@@ -0,0 +1,977 @@
+# 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 time
+import re
+import math
+from math import *
+
+from htmlgen import HTMLgen2 as HT
+
+from base import webqtlConfig
+
+
+
+
+# NL, 07/27/2010. moved from webqtlForm.py
+#Dict of Parents and F1 information, In the order of [F1, Mat, Pat]
+ParInfo ={
+'BXH':['BHF1', 'HBF1',  'C57BL/6J', 'C3H/HeJ'],
+'AKXD':['AKF1', 'KAF1', 'AKR/J', 'DBA/2J'],
+'BXD':['B6D2F1', 'D2B6F1', 'C57BL/6J', 'DBA/2J'],
+'BXD300':['B6D2F1', 'D2B6F1', 'C57BL/6J', 'DBA/2J'],
+'B6BTBRF2':['B6BTBRF1', 'BTBRB6F1', 'C57BL/6J', 'BTBRT<+>tf/J'],
+'BHHBF2':['B6HF2','HB6F2','C57BL/6J','C3H/HeJ'],
+'BHF2':['B6HF2','HB6F2','C57BL/6J','C3H/HeJ'],
+'B6D2F2':['B6D2F1', 'D2B6F1', 'C57BL/6J', 'DBA/2J'],
+'BDF2-1999':['B6D2F2', 'D2B6F2', 'C57BL/6J', 'DBA/2J'],
+'BDF2-2005':['B6D2F1', 'D2B6F1', 'C57BL/6J', 'DBA/2J'],
+'CTB6F2':['CTB6F2','B6CTF2','C57BL/6J','Castaneous'],
+'CXB':['CBF1', 'BCF1', 'C57BL/6ByJ', 'BALB/cByJ'],
+'AXBXA':['ABF1', 'BAF1', 'C57BL/6J', 'A/J'],
+'AXB':['ABF1', 'BAF1', 'C57BL/6J', 'A/J'],
+'BXA':['BAF1', 'ABF1', 'C57BL/6J', 'A/J'],
+'LXS':['LSF1', 'SLF1', 'ISS', 'ILS'],
+'HXBBXH':['HSRBNF1', 'BNHSRF1', 'BN', 'HSR'],
+'BayXSha':['BayXShaF1', 'ShaXBayF1', 'Bay-0','Shahdara'],
+'ColXBur':['ColXBurF1', 'BurXColF1', 'Col-0','Bur-0'],
+'ColXCvi':['ColXCviF1', 'CviXColF1', 'Col-0','Cvi'],
+'SXM':['SMF1', 'MSF1', 'Steptoe','Morex']
+}
+
+
+# NL, 07/27/2010. moved from template.py
+IMGSTEP1 = HT.Image('/images/step1.gif', alt='STEP 1',border=0) #XZ, Only be used in inputPage.py
+IMGSTEP2 = HT.Image('/images/step2.gif', alt='STEP 2',border=0) #XZ, Only be used in inputPage.py
+IMGSTEP3 = HT.Image('/images/step3.gif', alt='STEP 3',border=0) #XZ, Only be used in inputPage.py
+IMGNEXT = HT.Image('/images/arrowdown.gif', alt='NEXT',border=0) #XZ, Only be used in inputPage.py
+
+IMGASC = HT.Image("/images/sortup.gif", border=0)
+IMGASCON = HT.Image("/images/sortupon.gif", border=0)
+IMGDESC = HT.Image("/images/sortdown.gif", border=0)
+IMGDESCON = HT.Image("/images/sortdownon.gif", border=0)
+
+"""
+IMGASC = HT.Image("/images/sortup_icon.gif", border=0)
+IMGASCON = HT.Image("/images/sortupon.gif", border=0)
+IMGDESC = HT.Image("/images/sortdown_icon.gif", border=0)
+IMGDESCON = HT.Image("/images/sortdownon.gif", border=0)
+IMG_UNSORTED = HT.Image("/images/unsorted_icon.gif", border=0)
+"""
+
+PROGRESSBAR = HT.Image('/images/waitAnima2.gif', alt='checkblue',align="middle",border=0)
+
+#########################################
+#      Accessory Functions
+#########################################
+
+def decodeEscape(str):
+	a = str
+	pattern = re.compile('(%[0-9A-Fa-f][0-9A-Fa-f])')
+	match = pattern.findall(a)
+	matched = []
+	for item in match:
+		if item not in matched:
+			a = a.replace(item, '%c' % eval("0x"+item[-2:]))
+		matched.append(item)
+	return a
+	
+def exportData(hddn, tdata, NP = None):
+	for key in tdata.keys():
+		_val, _var, _N = tdata[key].val, tdata[key].var, tdata[key].N
+		if _val != None:
+			hddn[key] = _val
+			if _var != None:
+				hddn['V'+key] = _var
+			if NP and _N != None:
+				hddn['N'+key] = _N
+	
+def genShortStrainName(RISet='', input_strainName=''):
+	#aliasStrainDict = {'C57BL/6J':'B6','DBA/2J':'D2'}
+	strainName = input_strainName
+	if RISet != 'AXBXA':
+		if RISet == 'BXD300':
+			this_RISet = 'BXD'
+		elif RISet == 'BDF2-2005':
+			this_RISet = 'CASE05_'
+		else:
+			this_RISet = RISet
+		strainName = string.replace(strainName,this_RISet,'')
+		strainName = string.replace(strainName,'CASE','')
+		try:
+			strainName = "%02d" % int(strainName)
+		except:
+			pass
+	else:
+		strainName = string.replace(strainName,'AXB','A')
+		strainName = string.replace(strainName,'BXA','B')
+		try:
+			strainName = strainName[0] + "%02d" % int(strainName[1:])
+		except:
+			pass
+	return strainName
+
+def toInt(in_str):
+	"Converts an arbitrary string to an unsigned integer"
+	start = -1
+	end = -1
+	for i, char in enumerate(in_str):
+		if char >= '0' and char <= '9':
+			if start < 0:
+				start = i
+			end = i+1
+		else:
+			if start >= 0:
+				break
+	if start < end:
+		return int(in_str[start:end])
+	else:
+		return  -1
+
+def transpose(m):
+	'transpose a matrix'
+	n = len(m)
+	return [[m[j][i] for i in range(len(m[0])) for j in range(n)][k*n:k*n+n] for k in range(len(m[0]))]
+
+def asymTranspose(m):
+	'transpose a matrix'
+	t = max(map(len, m))
+	n = len(m)
+	m2 = [["-"]]*n
+	for i in range(n):
+		m2[i] = m[i] + [""]*(t- len(m[i]))
+	return [[m2[j][i] for i in range(len(m2[0])) for j in range(n)][k*n:k*n+n] for k in range(len(m2[0]))]
+
+def genRandStr(prefix = "", length=8, chars=string.letters+string.digits):
+	from random import choice
+	_str = prefix[:]
+	for i in range(length):
+		_str += choice(chars)
+	return _str
+
+def generate_session():
+	import sha
+	return sha.new(str(time.time())).hexdigest()
+
+def cvt2Dict(x):
+	tmp = {}
+	for key in x.keys():
+		tmp[key] = x[key]
+	return tmp
+
+def dump_session(session_obj, filename):
+	"It seems mod python can only cPickle most basic data type"
+	import cPickle
+	session_file = open(filename, 'wb')
+	#try:
+	#	pass
+	#except:
+	#	pass
+	cPickle.dump(session_obj, session_file)
+	session_file.close()
+	
+def StringAsFloat(str):
+	'Converts string to float but catches any exception and returns None'
+	try:
+		return float(str)
+	except:
+		return None
+	
+def IntAsFloat(str):
+	'Converts string to Int but catches any exception and returns None'
+	try:
+		return int(str)
+	except:
+		return None
+		
+def FloatAsFloat(flt):
+	'Converts float to string but catches any exception and returns None'
+	try:
+		return float("%2.3f" % flt)
+	except:
+		return None
+
+def RemoveZero(flt):
+	'Converts string to float but catches any exception and returns None'
+	try:
+		if abs(flt) < 1e-6:
+			return None
+		else:
+			return flt
+	except:
+		return None
+
+
+def SciFloat(d):
+	'Converts string to float but catches any exception and returns None'
+
+	try:
+		if abs(d) <= 1.0e-4:	
+			return "%1.2e" % d
+		else:
+			return "%1.5f" % d
+	except:
+		return None
+
+###To be removed
+def FloatList2String(lst):
+	'Converts float list to string but catches any exception and returns None'
+	tt=''
+	try:
+		for item in lst:
+			if item == None:
+				tt += 'X '
+			else:
+				tt += '%f ' % item
+		return tt
+	except:
+		return ""
+
+def ListNotNull(lst):
+	'Determine if the elements in a list are all null'
+	for item in lst:
+		if item is not None:
+			return 1
+	return None
+	
+###To be removed
+def FileDataProcess(str):
+	'Remove the description text from the input file if theres any'
+	i=0
+	while i<len(str):
+		if str[i]<'\x7f' and str[i]>'\x20':
+			break
+		else:
+			i+=1
+	str=str[i:]
+	str=string.join(string.split(str,'\000'),'')
+	i=string.find(str,"*****")
+	if i>-1:
+		return str[i+5:]
+	else:
+		return str
+
+def rank(a,lst,offset=0):
+	"""Calculate the integer rank of a number in an array, can be used to calculate p-value"""
+	n = len(lst)
+	if n == 2:
+		if a <lst[0]:
+			return offset
+		elif a > lst[1]: 
+			return offset + 2 	
+		else:
+			return offset +1	
+	elif n == 1:
+		if a <lst[0]:
+			return offset
+		else:
+			return offset +1
+	elif n== 0:
+		return offset
+	else:
+		mid = n/2
+		if a < lst[mid]:
+			return rank(a,lst[:mid-1],offset)
+		else:
+			return rank(a,lst[mid:],offset+mid)
+
+def cmpScanResult(A,B):
+	try:
+		if A.LRS > B.LRS:
+			return 1
+		elif A.LRS == B.LRS:
+			return 0
+		else:
+			return -1	
+	except:
+		return 0
+
+	
+def cmpScanResult2(A,B):
+	try:
+		if A.LRS < B.LRS:
+			return 1
+		elif A.LRS == B.LRS:
+			return 0
+		else:
+			return -1	
+	except:
+		return 0
+
+def cmpOrder(A,B):
+	try:
+		if A[1] < B[1]:
+			return -1
+		elif A[1] == B[1]:
+			return 0
+		else:
+			return 1
+	except:
+		return 0
+
+def cmpOrder2(A,B):
+	try:
+		if A[-1] < B[-1]:
+			return -1
+		elif A[-1] == B[-1]:
+			return 0
+		else:
+			return 1	
+	except:
+		return 0
+
+
+
+
+def calRank(xVals, yVals, N): ###  Zach Sloan, February 4 2010
+	""" 
+	Returns a ranked set of X and Y values. These are used when generating
+	a Spearman scatterplot. Bear in mind that this sets values equal to each 
+	other as the same rank.
+	"""
+	XX = []
+	YY = []
+	X = [0]*len(xVals)
+	Y = [0]*len(yVals)
+	j = 0
+	
+	for i in range(len(xVals)):
+		
+		if xVals[i] != None and yVals[i] != None:
+			XX.append((j, xVals[i]))
+			YY.append((j, yVals[i]))
+			j = j + 1
+		
+	NN = len(XX)
+	
+	XX.sort(cmpOrder2)
+	YY.sort(cmpOrder2)
+
+	j = 1
+	rank = 0.0
+	
+	while j < NN:
+
+		if XX[j][1] != XX[j-1][1]:	
+			X[XX[j-1][0]] = j
+			j = j+1 
+		
+		else:
+			jt = j+1
+			ji = j
+			for jt in range(j+1, NN):
+				 if (XX[jt][1] != XX[j-1][1]):
+				 	break
+			rank = 0.5*(j+jt)
+			for ji in range(j-1, jt):
+				 X[XX[ji][0]] = rank
+			if (jt == NN-1):
+				if (XX[jt][1] == XX[j-1][1]):
+					X[XX[NN-1][0]] = rank
+			j = jt+1
+				 	
+	if j == NN:
+		if X[XX[NN-1][0]] == 0:
+			X[XX[NN-1][0]] = NN
+		
+	j = 1
+	rank = 0.0
+
+	while j < NN:
+
+		if YY[j][1] != YY[j-1][1]:	
+			Y[YY[j-1][0]] = j
+			j = j+1
+		else:
+			jt = j+1
+			ji = j
+			for jt in range(j+1, NN):
+				 if (YY[jt][1] != YY[j-1][1]):
+				 	break
+			rank = 0.5*(j+jt)
+			for ji in range(j-1, jt):
+				 Y[YY[ji][0]] = rank
+			if (jt == NN-1):
+				if (YY[jt][1] == YY[j-1][1]):
+					Y[YY[NN-1][0]] = rank
+			j = jt+1
+			
+	if j == NN:
+		if Y[YY[NN-1][0]] == 0:
+			Y[YY[NN-1][0]] = NN
+		
+	return (X,Y)	 
+
+def calCorrelationRank(xVals,yVals,N):
+	"""
+	Calculated Spearman Ranked Correlation. The algorithm works
+	by setting all tied ranks to the average of those ranks (for 
+	example, if ranks 5-10 all have the same value, each will be set
+	to rank 7.5).
+	"""
+	
+	XX = []
+	YY = []
+	j = 0
+	
+	for i in range(len(xVals)):
+		if xVals[i]!= None and yVals[i]!= None:
+			XX.append((j,xVals[i]))
+			YY.append((j,yVals[i]))
+			j = j+1
+			
+	NN = len(XX)
+	if NN <6:
+		return (0.0,NN)
+	XX.sort(cmpOrder2)
+	YY.sort(cmpOrder2)
+	X = [0]*NN
+	Y = [0]*NN
+	
+	j = 1
+	rank = 0.0
+	t = 0.0
+	sx = 0.0
+	
+	while j < NN:
+
+		if XX[j][1] != XX[j-1][1]:	
+			X[XX[j-1][0]] = j
+			j = j+1 
+		
+		else:
+			jt = j+1
+			ji = j
+			for jt in range(j+1, NN):
+				 if (XX[jt][1] != XX[j-1][1]):
+				 	break
+			rank = 0.5*(j+jt)
+			for ji in range(j-1, jt):
+				 X[XX[ji][0]] = rank
+			t = jt-j
+			sx = sx + (t*t*t-t)
+			if (jt == NN-1):
+				if (XX[jt][1] == XX[j-1][1]):
+					X[XX[NN-1][0]] = rank
+			j = jt+1
+				 	
+	if j == NN:
+		if X[XX[NN-1][0]] == 0:
+			X[XX[NN-1][0]] = NN
+		
+	j = 1
+	rank = 0.0
+	t = 0.0
+	sy = 0.0
+
+	while j < NN:
+
+		if YY[j][1] != YY[j-1][1]:	
+			Y[YY[j-1][0]] = j
+			j = j+1
+		else:
+			jt = j+1
+			ji = j
+			for jt in range(j+1, NN):
+				 if (YY[jt][1] != YY[j-1][1]):
+				 	break
+			rank = 0.5*(j+jt)
+			for ji in range(j-1, jt):
+				 Y[YY[ji][0]] = rank
+			t = jt - j
+			sy = sy + (t*t*t-t)
+			if (jt == NN-1):
+				if (YY[jt][1] == YY[j-1][1]):
+					Y[YY[NN-1][0]] = rank
+			j = jt+1
+			
+	if j == NN:
+		if Y[YY[NN-1][0]] == 0:
+			Y[YY[NN-1][0]] = NN
+				
+	D = 0.0
+	
+	for i in range(NN):
+		D += (X[i]-Y[i])*(X[i]-Y[i])
+		
+	fac = (1.0 -sx/(NN*NN*NN-NN))*(1.0-sy/(NN*NN*NN-NN))
+	
+	return ((1-(6.0/(NN*NN*NN-NN))*(D+(sx+sy)/12.0))/math.sqrt(fac),NN)
+	
+	
+def calCorrelationRankText(dbdata,userdata,N): ### dcrowell = David Crowell, July 2008
+        """Calculates correlation ranks with data formatted from the text file.
+        dbdata, userdata are lists of strings.  N is an int.  Returns a float.
+        Used by correlationPage"""
+        XX = []
+        YY = []
+        j = 0
+        for i in range(N):
+                if (dbdata[i]!= None and userdata[i]!=None) and (dbdata[i]!= 'None' and userdata[i]!='None'):
+                        XX.append((j,float(dbdata[i])))
+                        YY.append((j,float(userdata[i])))
+                        j += 1
+	NN = len(XX)
+	if NN <6:
+		return (0.0,NN)
+	XX.sort(cmpOrder2)
+	YY.sort(cmpOrder2)
+	X = [0]*NN
+	Y = [0]*NN
+	
+	j = 1
+	rank = 0.0
+	t = 0.0
+	sx = 0.0
+	
+	while j < NN:
+
+		if XX[j][1] != XX[j-1][1]:	
+			X[XX[j-1][0]] = j
+			j = j+1 
+		
+		else:
+			jt = j+1
+			ji = j
+			for jt in range(j+1, NN):
+				 if (XX[jt][1] != XX[j-1][1]):
+				 	break
+			rank = 0.5*(j+jt)
+			for ji in range(j-1, jt):
+				 X[XX[ji][0]] = rank
+			t = jt-j
+			sx = sx + (t*t*t-t)
+			if (jt == NN-1):
+				if (XX[jt][1] == XX[j-1][1]):
+					X[XX[NN-1][0]] = rank
+			j = jt+1
+				 	
+	if j == NN:
+		if X[XX[NN-1][0]] == 0:
+			X[XX[NN-1][0]] = NN
+		
+	j = 1
+	rank = 0.0
+	t = 0.0
+	sy = 0.0
+
+	while j < NN:
+
+		if YY[j][1] != YY[j-1][1]:	
+			Y[YY[j-1][0]] = j
+			j = j+1
+		else:
+			jt = j+1
+			ji = j
+			for jt in range(j+1, NN):
+				 if (YY[jt][1] != YY[j-1][1]):
+				 	break
+			rank = 0.5*(j+jt)
+			for ji in range(j-1, jt):
+				 Y[YY[ji][0]] = rank
+			t = jt - j
+			sy = sy + (t*t*t-t)
+			if (jt == NN-1):
+				if (YY[jt][1] == YY[j-1][1]):
+					Y[YY[NN-1][0]] = rank
+			j = jt+1
+			
+	if j == NN:
+		if Y[YY[NN-1][0]] == 0:
+			Y[YY[NN-1][0]] = NN
+				
+	D = 0.0
+	
+	for i in range(NN):
+		D += (X[i]-Y[i])*(X[i]-Y[i])
+		
+	fac = (1.0 -sx/(NN*NN*NN-NN))*(1.0-sy/(NN*NN*NN-NN))
+	
+	return ((1-(6.0/(NN*NN*NN-NN))*(D+(sx+sy)/12.0))/math.sqrt(fac),NN)
+
+
+
+def calCorrelation(dbdata,userdata,N):
+	X = []
+	Y = []
+	for i in range(N):
+		if dbdata[i]!= None and userdata[i]!= None:
+			X.append(dbdata[i])
+			Y.append(userdata[i])
+	NN = len(X)
+	if NN <6:
+		return (0.0,NN)
+	sx = reduce(lambda x,y:x+y,X,0.0)
+	sy = reduce(lambda x,y:x+y,Y,0.0)
+	meanx = sx/NN
+	meany = sy/NN
+	xyd = 0.0
+	sxd = 0.0
+	syd = 0.0
+	for i in range(NN):
+		xyd += (X[i] - meanx)*(Y[i]-meany)
+		sxd += (X[i] - meanx)*(X[i] - meanx)
+		syd += (Y[i] - meany)*(Y[i] - meany)
+	try:
+		corr = xyd/(sqrt(sxd)*sqrt(syd))
+	except:
+		corr = 0
+	return (corr,NN)
+
+def calCorrelationText(dbdata,userdata,N): ### dcrowell July 2008
+        """Calculates correlation coefficients with values formatted from text files. dbdata, userdata are lists of strings.  N is an int.  Returns a float
+        Used by correlationPage"""
+        X = []
+        Y = []
+        for i in range(N):
+                #if (dbdata[i]!= None and userdata[i]!= None) and (dbdata[i]!= 'None' and userdata[i]!= 'None'):
+                #               X.append(float(dbdata[i]))
+                #               Y.append(float(userdata[i]))
+                if dbdata[i] == None or dbdata[i] == 'None' or userdata[i] == None or userdata[i] == 'None':
+                        continue
+                else:
+                        X.append(float(dbdata[i]))
+                        Y.append(float(userdata[i]))
+        NN = len(X)
+        if NN <6:
+                return (0.0,NN)
+        sx = sum(X)
+        sy = sum(Y)
+        meanx = sx/float(NN)
+        meany = sy/float(NN)
+        xyd = 0.0
+        sxd = 0.0
+        syd = 0.0
+        for i in range(NN):
+                x1 = X[i]-meanx
+                y1 = Y[i]-meany
+                xyd += x1*y1
+                sxd += x1**2
+                syd += y1**2
+        try:
+                corr = xyd/(sqrt(sxd)*sqrt(syd))
+        except:
+                corr = 0
+        return (corr,NN)
+
+
+def readLineCSV(line): ### dcrowell July 2008
+        """Parses a CSV string of text and returns a list containing each element as a string.
+        Used by correlationPage"""
+        returnList = line.split('","')
+        returnList[-1]=returnList[-1][:-2]
+        returnList[0]=returnList[0][1:]
+        return returnList
+
+
+def cmpCorr(A,B):
+	try:
+		if abs(A[1]) < abs(B[1]):
+			return 1
+		elif abs(A[1]) == abs(B[1]):
+			return 0
+		else:
+			return -1	
+	except:
+		return 0
+
+def cmpLitCorr(A,B):
+	try:
+		if abs(A[3]) < abs(B[3]): return 1
+		elif abs(A[3]) == abs(B[3]):
+                     if abs(A[1]) < abs(B[1]): return 1
+		     elif abs(A[1]) == abs(B[1]): return 0
+                     else: return -1
+		else: return -1	
+	except:
+		return 0
+
+def cmpPValue(A,B):
+	try:
+		if A.corrPValue < B.corrPValue:
+			return -1
+		elif A.corrPValue == B.corrPValue:
+			if abs(A.corr) > abs(B.corr):
+				return -1
+			elif abs(A.corr) < abs(B.corr):
+				return 1
+			else:
+				return 0
+		else:
+			return 1	
+	except:
+		return 0
+
+def cmpEigenValue(A,B):
+	try:
+		if A[0] > B[0]:
+			return -1
+		elif A[0] == B[0]:
+			return 0
+		else:
+			return 1	
+	except:
+		return 0
+
+
+def cmpLRSFull(A,B):
+	try:
+		if A[0] < B[0]:
+			return -1
+		elif A[0] == B[0]:
+			return 0
+		else:
+			return 1	
+	except:
+		return 0
+
+def cmpLRSInteract(A,B):
+	try:
+		if A[1] < B[1]:
+			return -1
+		elif A[1] == B[1]:
+			return 0
+		else:
+			return 1	
+	except:
+		return 0
+
+	
+def cmpPos(A,B):
+	try:
+		try: 
+			AChr = int(A.chr)
+		except:
+			AChr = 20
+		try: 
+			BChr = int(B.chr)
+		except:
+			BChr = 20
+		if AChr > BChr:
+			return 1
+		elif AChr == BChr:
+			if A.mb > B.mb:
+				return 1
+			if A.mb == B.mb:
+				return 0
+			else:
+				return -1
+		else:
+			return -1	
+	except:
+		return 0
+	
+def cmpGenoPos(A,B):
+	try:
+		A1 = A.chr
+		B1 = B.chr
+		try:
+			A1 = int(A1)
+		except:
+			A1 = 25
+		try:
+			B1 = int(B1)
+		except:
+			B1 = 25
+		if A1 > B1:
+			return 1
+		elif A1 == B1:
+			if A.mb > B.mb:
+				return 1
+			if A.mb == B.mb:
+				return 0
+			else:
+				return -1
+		else:
+			return -1	
+	except:
+		return 0
+
+#XZhou: Must use "BINARY" to enable case sensitive comparison.
+def authUser(name,password,db, encrypt=None):
+	try:
+		if encrypt:
+			query = 'SELECT privilege, id,name,password, grpName FROM User WHERE name= BINARY \'%s\' and password= BINARY \'%s\'' % (name,password)
+		else:
+			query = 'SELECT privilege, id,name,password, grpName FROM User WHERE name= BINARY \'%s\' and password= BINARY SHA(\'%s\')' % (name,password)
+		db.execute(query)
+		records = db.fetchone()
+		if not records:
+			raise ValueError
+		return records#(privilege,id,name,password,grpName)
+	except:
+		return (None, None, None, None, None)
+
+
+def hasAccessToConfidentialPhenotypeTrait(privilege, userName, authorized_users):
+    access_to_confidential_phenotype_trait = 0
+    if webqtlConfig.USERDICT[privilege] > webqtlConfig.USERDICT['user']:
+        access_to_confidential_phenotype_trait = 1
+    else:
+        AuthorizedUsersList=map(string.strip, string.split(authorized_users, ','))
+        if AuthorizedUsersList.__contains__(userName):
+            access_to_confidential_phenotype_trait = 1
+    return access_to_confidential_phenotype_trait
+
+
+class VisualizeException(Exception):
+    def __init__(self, message):
+	        self.message = message	
+    def __str__(self):
+                return self.message
+
+# safeConvert : (string -> A) -> A -> A
+# to convert a string to type A, using the supplied default value
+# if the given conversion function doesn't work
+def safeConvert(f, value, default):
+    try:
+        return f(value)
+    except:
+        return default
+    
+# safeFloat : string -> float -> float
+# to convert a string to a float safely
+def safeFloat(value, default):
+    return safeConvert(float, value, default)
+    
+# safeInt: string -> int -> int
+# to convert a string to an int safely
+def safeInt(value, default):
+    return safeConvert(int, value, default)
+
+# safeString : string -> (arrayof string) -> string -> string
+# if a string is not in a list of strings to pick a default value
+# for that string
+def safeString(value, validChoices, default):
+    if value in validChoices:
+        return value
+    else:
+        return default
+
+# yesNoToInt: string -> int
+# map "yes" -> 1 and "no" -> 0
+def yesNoToInt(value):
+    if value == "yes":
+        return 1
+    elif value == "no":
+       	return 0
+    else:
+        return None
+
+# IntToYesNo: int -> string
+# map 1 -> "yes" and 0 -> "no"
+def intToYesNo(value):
+    if value == 1:
+        return "yes"
+    elif value == 0:
+        return "no"
+    else:
+        return None	
+       
+def formatField(name):
+	name = name.replace("_", " ")
+	name = name.title()
+        #name = name.replace("Mb Mm6", "Mb");  
+	return name.replace("Id", "ID")  
+
+#XZ, 03/27/2009: This function is very specific.
+#It is used by AJAX_table.py, correlationPage.py and dataPage.py
+
+
+def genTableObj(tblobj=None, file="", sortby = ("", ""), tableID = "sortable", addIndex = "1", hiddenColumns=[]):
+	header = tblobj['header']
+	body = tblobj['body']
+	field, order = sortby
+	
+	#ZAS 9/12/2011 - The hiddenColumns array needs to be converted into a string so they can be placed into the javascript of each up/down button
+	hiddenColumnsString = ",".join(hiddenColumns)
+
+	tbl = HT.TableLite(Class="collap b2", cellspacing=1, cellpadding=5)
+
+	hiddenColumnIdx = [] #indices of columns to hide
+	idx = -1
+	last_idx = 0 #ZS: This is the index of the last item in the regular table header (without any extra parameters). It is used to determine the index of each extra parameter.
+	for row in header:
+		hr = HT.TR()
+		for i, item in enumerate(row):
+			if (item.text == '') or (item.text not in hiddenColumns):
+				if item.sort and item.text:			
+					down = HT.Href("javascript:xmlhttpPost('%smain.py?FormID=AJAX_table', '%s', 'sort=%s&order=down&file=%s&tableID=%s&addIndex=%s&hiddenColumns=%s')" % (webqtlConfig.CGIDIR, tableID, item.text, file, tableID, addIndex, hiddenColumnsString),IMGDESC)		
+					up = HT.Href("javascript:xmlhttpPost('%smain.py?FormID=AJAX_table', '%s', 'sort=%s&order=up&file=%s&tableID=%s&addIndex=%s&hiddenColumns=%s')" % (webqtlConfig.CGIDIR, tableID, item.text, file, tableID, addIndex, hiddenColumnsString),IMGASC)
+					if item.text == field:	
+						idx = item.idx
+						last_idx = idx
+						if order == 'up':
+							up = IMGASCON
+						elif order == 'down':
+							down = IMGDESCON
+					item.html.append(HT.Div(up, down, style="float: bottom;"))
+				hr.append(item.html)
+			else:
+				hiddenColumnIdx.append(i)
+		tbl.append(hr)
+
+	for i, row in enumerate(body):
+		for j, item in enumerate(row):
+			if order == 'down':
+				if (item.val == '' or item.val == 'x' or item.val == 'None'):
+					item.val = 0 
+			if order == 'up':
+				if (item.val == '' or item.val == 'x' or item.val == 'None'):
+					item.val = 'zzzzz' 
+
+	if idx >= 0:
+		if order == 'down':
+			body.sort(lambda A, B: cmp(B[idx].val, A[idx].val), key=natsort_key)
+		elif order == 'up':
+			body.sort(lambda A, B: cmp(A[idx].val, B[idx].val), key=natsort_key)
+		else:
+			pass
+			
+	for i, row in enumerate(body):
+		hr = HT.TR(Id = row[0].text)
+		for j, item in enumerate(row):
+			if (j not in hiddenColumnIdx):
+				if j == 0:
+					if addIndex == "1":
+						item.html.contents = [i+1] + item.html.contents
+				hr.append(item.html)
+		tbl.append(hr)
+
+	return tbl	
+
+def natsort_key(string):
+    r = []
+    for c in string:
+        try:
+            c = int(c)
+            try: r[-1] = r[-1] * 10 + c
+            except: r.append(c)
+        except:
+            r.append(c)
+    return r
+