about summary refs log tree commit diff
path: root/web/webqtl/utility
diff options
context:
space:
mode:
authorroot2012-05-08 18:39:56 -0500
committerroot2012-05-08 18:39:56 -0500
commitea46f42ee640928b92947bfb204c41a482d80937 (patch)
tree9b27a4eb852d12539b543c3efee9d2a47ef470f3 /web/webqtl/utility
parent056b5253fc3857b0444382aa39944f6344dc1ceb (diff)
downloadgenenetwork2-ea46f42ee640928b92947bfb204c41a482d80937.tar.gz
Add all the source codes into the github.
Diffstat (limited to 'web/webqtl/utility')
-rwxr-xr-xweb/webqtl/utility/AJAX_table.py153
-rwxr-xr-xweb/webqtl/utility/Plot.py1283
-rwxr-xr-xweb/webqtl/utility/TDCell.py42
-rwxr-xr-xweb/webqtl/utility/THCell.py44
-rwxr-xr-xweb/webqtl/utility/__init__.py0
-rwxr-xr-xweb/webqtl/utility/svg.py1069
-rwxr-xr-xweb/webqtl/utility/webqtlUtil.py977
7 files changed, 3568 insertions, 0 deletions
diff --git a/web/webqtl/utility/AJAX_table.py b/web/webqtl/utility/AJAX_table.py
new file mode 100755
index 00000000..963a530e
--- /dev/null
+++ b/web/webqtl/utility/AJAX_table.py
@@ -0,0 +1,153 @@
+# 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 cPickle
+import os
+import MySQLdb
+import time
+import pyXLWriter as xl
+
+from htmlgen import HTMLgen2 as HT
+
+from base import webqtlConfig
+from THCell import THCell
+from TDCell import TDCell
+import webqtlUtil
+
+
+class AJAX_table:
+	def __init__(self, fd):
+		file = fd.formdata.getfirst("file", "")
+		sort = fd.formdata.getfirst("sort", "")
+		order = fd.formdata.getfirst("order", "up")
+		cmd = fd.formdata.getfirst("cmd", "")
+		tableID = fd.formdata.getfirst("tableID", "")
+		addIndex = fd.formdata.getfirst("addIndex", "1")
+		hiddenColumnsString = fd.formdata.getfirst("hiddenColumns", "")
+		hiddenColumns = hiddenColumnsString.split(',')
+
+		try:
+			fp = open(os.path.join(webqtlConfig.TMPDIR, file + '.obj'), 'rb')
+			tblobj = cPickle.load(fp)
+			fp.close()
+		
+			if cmd == 'addCorr':
+				dbId = int(fd.formdata.getfirst("db"))
+				dbFullName = fd.formdata.getfirst("dbname")
+				trait = fd.formdata.getfirst("trait")
+				form = fd.formdata.getfirst("form")
+				ids = fd.formdata.getfirst("ids")
+				vals = fd.formdata.getfirst("vals")
+				ids = eval(ids) 
+				nnCorr = len(ids)
+				vals = eval(vals)
+	
+				workbook = xl.Writer('%s.xls' % (webqtlConfig.TMPDIR+file))
+				worksheet = workbook.add_worksheet()
+	
+				con = MySQLdb.Connect(db=webqtlConfig.DB_NAME,host=webqtlConfig.MYSQL_SERVER, user=webqtlConfig.DB_USER,passwd=webqtlConfig.DB_PASSWD)
+				cursor = con.cursor()
+			
+				cursor.execute("Select name, ShortName from ProbeSetFreeze where Id = %s", dbId)
+				dbName, dbShortName = cursor.fetchone()
+			
+				tblobj['header'][0].append(
+					THCell(HT.TD(dbShortName, Class="fs11 ffl b1 cw cbrb"), 
+					text="%s" % dbShortName, idx=tblobj['header'][0][-1].idx + 1),
+				)
+	            
+				headingStyle = workbook.add_format(align = 'center', bold = 1, border = 1, size=13, fg_color = 0x1E, color="white")
+				for i, item in enumerate(tblobj['header'][0]):
+                    			if (i > 0):
+						worksheet.write([8, i-1], item.text, headingStyle)
+						worksheet.set_column([i-1, i-1], 2*len(item.text))  
+			
+				for i, row in enumerate(tblobj['body']):
+					ProbeSetId = row[1].text
+					#XZ, 03/02/2009: Xiaodong changed Data to ProbeSetData
+					cursor.execute("""
+						Select ProbeSetData.StrainId, ProbeSetData.Value
+						From ProbeSetData, ProbeSetXRef, ProbeSet
+						where ProbeSetXRef.ProbeSetFreezeId = %d AND
+							ProbeSetXRef.DataId = ProbeSetData.Id AND
+							ProbeSetXRef.ProbeSetId = ProbeSet.Id AND
+							ProbeSet.Name = '%s'
+					""" % (dbId, ProbeSetId))
+					results = cursor.fetchall()
+					vdict = {}
+					for item in results:
+						vdict[item[0]] = item[1]
+					newvals = []
+					for id in ids:
+						if vdict.has_key(id):
+							newvals.append(vdict[id])
+						else:
+							newvals.append(None)
+					corr,nOverlap= webqtlUtil.calCorrelation(newvals,vals,nnCorr)
+					repr = '%0.4f' % corr
+					row.append(
+						TDCell(HT.TD(HT.Href(text=repr, url="javascript:showCorrPlotThird('%s', '%s', '%s')" % (form, dbName, ProbeSetId), Class="fs11 fwn ffl"), " / ", nOverlap, Class="fs11 fwn ffl b1 c222", align="middle"),repr,abs(corr))
+					)
+	                
+					last_row=0
+					for j, item in enumerate(tblobj['body'][i]):
+						if (j > 0):
+							worksheet.write([9+i, j-1], item.text)
+							last_row = 9+i
+					last_row += 1
+	                              
+				titleStyle = workbook.add_format(align = 'left', bold = 0, size=14, border = 1, border_color="gray")
+				##Write title Info
+				# Modified by Hongqiang Li 
+				worksheet.write([0, 0], "Citations: Please see %s/reference.html" % webqtlConfig.PORTADDR, titleStyle)
+				worksheet.write([1, 0], "Trait : %s" % trait, titleStyle)
+				worksheet.write([2, 0], "Database : %s" % dbFullName, titleStyle)
+				worksheet.write([3, 0], "Date : %s" % time.strftime("%B %d, %Y", time.gmtime()), titleStyle)
+				worksheet.write([4, 0], "Time : %s GMT" % time.strftime("%H:%M ", time.gmtime()), titleStyle)
+				worksheet.write([5, 0], "Status of data ownership: Possibly unpublished data; please see %s/statusandContact.html for details on sources, ownership, and usage of these data." % webqtlConfig.PORTADDR, titleStyle)
+				#Write footer info
+				worksheet.write([1 + last_row, 0], "Funding for The GeneNetwork: NIAAA (U01AA13499, U24AA13513), NIDA, NIMH, and NIAAA (P20-DA21131), NCI MMHCC (U01CA105417), and NCRR (U01NR 105417)", titleStyle)
+				worksheet.write([2 + last_row, 0], "PLEASE RETAIN DATA SOURCE INFORMATION WHENEVER POSSIBLE", titleStyle)    
+	                    
+				cursor.close()
+				workbook.close()
+	            
+				objfile = open(os.path.join(webqtlConfig.TMPDIR, file + '.obj'), 'wb')
+				cPickle.dump(tblobj, objfile)
+				objfile.close()
+			else:
+				pass
+
+			self.value = str(webqtlUtil.genTableObj(tblobj=tblobj, file=file, sortby=(sort, order), tableID = tableID, addIndex = addIndex, hiddenColumns = hiddenColumns))
+
+		except:
+			self.value = "<span class='fs16 fwb cr ffl'>The table is no longer available on this server</span>"
+
+	def __str__(self):
+		return self.value
+
+	def write(self):
+		return str(self)
diff --git a/web/webqtl/utility/Plot.py b/web/webqtl/utility/Plot.py
new file mode 100755
index 00000000..2401c85c
--- /dev/null
+++ b/web/webqtl/utility/Plot.py
@@ -0,0 +1,1283 @@
+# 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 piddle as pid
+from math import *
+import random
+import sys, os
+from numarray import linear_algebra as la
+from numarray import ones, array, dot, swapaxes
+
+import reaper
+
+import svg
+import webqtlUtil
+from base import webqtlConfig
+
+
+def cformat(d, rank=0):
+	'custom string format'
+	strD = "%2.6f" % d
+	
+	if rank == 0:
+		while strD[-1] in ('0','.'):
+			if strD[-1] == '0' and strD[-2] == '.' and len(strD) <= 4:
+				break
+			elif strD[-1] == '.':
+				strD = strD[:-1]
+				break
+			else:
+				strD = strD[:-1]
+	
+	else:
+		strD = strD.split(".")[0]		
+
+	if strD == '-0.0':
+		strD = '0.0'
+	return strD
+	
+def frange(start, end=None, inc=1.0): 
+	"A faster range-like function that does accept float increments..." 
+	if end == None: 
+		end = start + 0.0 
+		start = 0.0 
+	else: 
+		start += 0.0 # force it to be a float 
+	count = int((end - start) / inc) 
+	if start + count * inc != end: 
+	# Need to adjust the count. AFAICT, it always comes up one short. 
+		count += 1 
+	L = [start] * count 
+	for i in xrange(1, count): 
+		L[i] = start + i * inc 
+	return L 
+
+
+def gammln(xx):
+	cof=[76.18009173,-86.50532033,24.01409822,-1.231739516,0.120858003e-2,-0.536382e-5]
+	x=xx-1.0
+	tmp=x+5.5
+	tmp -=(x+0.5)*log(tmp)
+	ser=1.0
+	for item in cof:
+		x+=1.0
+		ser+=item/x
+	
+	return -tmp+log(2.50662827465*ser)
+	
+
+def gser(a,x):
+	gln=gammln(a)
+	ITMAX=100
+	EPS=3.0e-7
+	
+	if x<=0.0:
+		gamser=0.0
+		return [gamser,gln]
+	else:
+		ap=a
+		sum=1.0/a
+		dele=sum
+		for i in range(1,ITMAX+1):
+			ap+=1.0
+			dele*=x/ap
+			sum+=dele
+			if abs(dele)<abs(sum)*EPS:
+				gamser=sum*exp(-x+a*log(x)-gln)
+				return [gamser,gln]
+	return None
+	
+def gcf(a,x):
+	ITMAX=100
+	EPS=3.0e-7
+	gold=0.0
+	fac=1
+	b1=1.0
+	b0=0.0
+	a0=1.0
+	gln=gammln(a)
+	
+	a1=x
+	for n in range(1,ITMAX+1):
+		an=n+0.0
+		ana=an-a
+		a0=(a1+a0*ana)*fac
+		b0=(b1+b0*ana)*fac
+		anf=an*fac
+		a1=x*a0+anf*a1
+		b1=x*b0+anf*b1
+		if (a1):
+			fac=1.0/a1
+			g=b1*fac
+			if abs((g-gold)/g)<EPS:
+				gammcf=exp(-x+a*log(x)-gln)*g
+				return [gammcf,gln]
+			gold=g
+	return None
+	
+def gammp(a,x):
+	if x<0.0 or a<=0.0:
+		return None
+	if x<(a+1.0):
+		a=gser(a,x)[0]
+		return a
+	else:
+		a=gcf(a,x)[0]
+		return 1.0-a
+def U(n):
+	x=pow(0.5,1.0/n)
+	m=[1-x]
+	for i in range(2,n):
+		a=(i-0.3175)/(n+0.365)
+		m.append(a)
+	m.append(x)
+	return m
+		
+def erf(x):
+	if x<0.0:
+		return -gammp(0.5,x*x)
+	else:
+		return gammp(0.5,x*x)
+
+def erfcc(x):
+	z=abs(x)
+	t=1.0/(1.0+0.5*z)
+	ans=t*exp(-z*z-1.26551223+t*(1.00002368+t*(0.37409196+t*(0.09678418+t*(-0.18628806+t*(0.27886807+t*(-1.13520398+t*(1.48851587+t*(-0.82215223+t*0.17087277)))))))))
+	if x>=0.0:
+		return ans
+	else:
+		return 2.0-ans
+		
+def calMeanVar(data):
+	n=len(data)
+	if n<2:
+		return None
+	else:
+		sum=reduce(lambda x,y:x+y,data,0.0)
+		mean=sum/n
+		z=data[:]
+		for i in range(n):
+			z[i]=z[i]-mean
+		variance=reduce(lambda x,y:x+y*y,z,0.0)
+		variance /= n-1
+		variance =sqrt(variance)
+		for i in range(n):
+			z[i]=z[i]/variance
+		return z
+		
+def inverseCumul(p):
+	#Coefficients in rational approximations.
+	a = [-3.969683028665376e+01,2.209460984245205e+02,-2.759285104469687e+02,1.383577518672690e+02,-3.066479806614716e+01,2.506628277459239e+00]
+
+	b = [-5.447609879822406e+01,1.615858368580409e+02,-1.556989798598866e+02,6.680131188771972e+01,-1.328068155288572e+01]
+
+	c = [-7.784894002430293e-03,-3.223964580411365e-01,-2.400758277161838e+00,-2.549732539343734e+00,4.374664141464968e+00,2.938163982698783e+00]
+
+	d =  [7.784695709041462e-03,3.224671290700398e-01,2.445134137142996e+00,3.754408661907416e+00]
+
+	#Define break-points.
+
+	p_low  = 0.02425
+	p_high = 1 - p_low
+
+	#Rational approximation for lower region.
+
+	if p > 0 and p < p_low:
+   		q = sqrt(-2*log(p))
+   		x = (((((c[0]*q+c[1])*q+c[2])*q+c[3])*q+c[4])*q+c[5]) / ((((d[0]*q+d[1])*q+d[2])*q+d[3])*q+1)
+
+
+	#Rational approximation for central region.
+
+	elif p>= p_low and p <= p_high:
+   		q = p - 0.5
+   		r = q*q
+   		x = (((((a[0]*r+a[1])*r+a[2])*r+a[3])*r+a[4])*r+a[5])*q /(((((b[0]*r+b[1])*r+b[2])*r+b[3])*r+b[4])*r+1)
+
+	#Rational approximation for upper region.
+
+	elif p>p_high and  p < 1:
+   		q = sqrt(-2*log(1-p))
+   		x = -(((((c[0]*q+c[1])*q+c[2])*q+c[3])*q+c[4])*q+c[5]) /((((d[0]*q+d[1])*q+d[2])*q+d[3])*q+1)
+
+	else:
+		return None
+
+	if p>0 and p < 1:
+   		e = 0.5 * erfcc(-x/sqrt(2)) - p 
+   		u = e * sqrt(2*pi) * exp(x*x/2)
+   		x = x - u/(1 + x*u/2)
+		return x
+	else:
+		return None
+
+def gmean(lst):
+	N = len(lst)
+	if N == 0:
+		return 0
+	else:
+		return (reduce(lambda x,y: x+y, lst, 0.0))/N
+
+def gmedian(lst2):
+	lst = lst2[:]
+	N = len(lst)
+	if N == 0:
+		return 0
+	else:
+		lst.sort()
+		if N % 2 == 0:
+			return (lst[N/2]+lst[(N-2)/2])/2.0
+		else:
+			return lst[(N-1)/2]
+			
+def gpercentile(lst2, np):
+	lst = lst2[:]
+	N = len(lst)
+	if N == 0 or np > 100 or np < 0:
+		return None
+	else:
+		lst.sort()
+		pNadd1 = (np/100.0)*N
+		k = int(pNadd1)
+		d = pNadd1 - k
+		if k == 0:
+			return lst[0]
+		elif k >= N-1:
+			return lst[N-1]
+		else:
+			return lst[k-1] + d*(lst[k] - lst[k-1])
+		
+def findOutliers(vals):
+	
+	valsOnly = []
+	dataXZ = vals[:]
+	for i in range(len(dataXZ)):
+		valsOnly.append(dataXZ[i][1])
+		
+	data = [('', valsOnly[:])]	
+	
+	for item in data:
+		itemvalue = item[1]
+		nValue = len(itemvalue)
+		catValue = []
+		
+		for item2 in itemvalue:
+			try:
+				tstrain, tvalue = item2
+			except:
+				tvalue = item2
+			if nValue <= 4:
+				continue
+			else:
+				catValue.append(tvalue)
+				
+		if catValue != []:
+			lowHinge = gpercentile(catValue, 25)
+			upHinge = gpercentile(catValue, 75)
+			Hstep = 1.5*(upHinge - lowHinge)
+			
+			outlier = []
+			extreme = []
+			
+			upperBound = upHinge + Hstep
+			lowerBound = lowHinge - Hstep
+			
+			for item in catValue:
+				if item >= upHinge + 2*Hstep:
+					extreme.append(item)
+				elif item >= upHinge + Hstep:
+					outlier.append(item)
+				else:
+					pass
+				
+			for item in catValue:
+				if item <= lowHinge - 2*Hstep:
+					extreme.append(item)
+				elif item <= lowHinge - Hstep:
+					outlier.append(item)
+				else:
+					pass
+		else:
+			upperBound = 1000
+			lowerBound = -1000
+				
+	return upperBound, lowerBound
+					
+					
+def plotBoxPlot(canvas, data, offset= (40, 40, 40, 40), XLabel="Category", YLabel="Value"):
+	xLeftOffset, xRightOffset, yTopOffset, yBottomOffset = offset
+	plotWidth = canvas.size[0] - xLeftOffset - xRightOffset
+	plotHeight = canvas.size[1] - yTopOffset - yBottomOffset
+	iValues = []
+	for item in data:
+		for item2 in item[1]:
+			try:
+				iValues.append(item2[1])
+			except:
+				iValues.append(item2)
+	
+	#draw frame		
+	max_Y = max(iValues)
+	min_Y = min(iValues)
+	scaleY = detScale(min_Y, max_Y)
+	Yll = scaleY[0]
+	Yur = scaleY[1]
+	nStep = scaleY[2]
+	stepY = (Yur - Yll)/nStep
+	stepYPixel = plotHeight/(nStep)
+	canvas.drawRect(plotWidth+xLeftOffset, plotHeight + yTopOffset, xLeftOffset, yTopOffset)
+				
+	##draw Y Scale
+	YYY = Yll
+	YCoord = plotHeight + yTopOffset
+	scaleFont=pid.Font(ttf="cour",size=11,bold=1)
+	for i in range(nStep+1):
+		strY = cformat(d=YYY, rank=0)
+		YCoord = max(YCoord, yTopOffset)
+		canvas.drawLine(xLeftOffset,YCoord,xLeftOffset-5,YCoord)
+		canvas.drawString(strY,	xLeftOffset -30,YCoord +5,font=scaleFont)
+		YYY += stepY
+		YCoord -= stepYPixel
+				
+	##draw X Scale
+	stepX = plotWidth/len(data)
+	XCoord = xLeftOffset + 0.5*stepX
+	YCoord = plotHeight + yTopOffset
+	scaleFont = pid.Font(ttf="tahoma",size=12,bold=0)
+	labelFont = pid.Font(ttf="tahoma",size=13,bold=0)
+	for item in data:
+		itemname, itemvalue = item
+		canvas.drawLine(XCoord, YCoord,XCoord, YCoord+5, color=pid.black)
+		canvas.drawString(itemname, XCoord - canvas.stringWidth(itemname,font=labelFont)/2.0,\
+		YCoord +20,font=labelFont)
+		
+		nValue = len(itemvalue)
+		catValue = []
+		for item2 in itemvalue:
+			try:
+				tstrain, tvalue = item2
+			except:
+				tvalue = item2
+			if nValue <= 4:
+				canvas.drawCross(XCoord, plotHeight + yTopOffset - (tvalue-Yll)*plotHeight/(Yur - Yll), color=pid.red,size=5)
+			else:
+				catValue.append(tvalue)
+		if catValue != []:
+			catMean = gmean(catValue)
+			catMedian = gmedian(catValue)
+			lowHinge = gpercentile(catValue, 25)
+			upHinge = gpercentile(catValue, 75)
+			Hstep = 1.5*(upHinge - lowHinge)
+			
+			outlier = []
+			extrem = []
+			
+			upperAdj = None
+			for item in catValue:
+				if item >= upHinge + 2*Hstep:
+					extrem.append(item)
+				elif item >= upHinge + Hstep:
+					outlier.append(item)
+				elif item > upHinge and  item < upHinge + Hstep:
+					if upperAdj == None or item > upperAdj:
+						upperAdj = item
+				else:
+					pass
+			lowerAdj = None
+			for item in catValue:
+				if item <= lowHinge - 2*Hstep:
+					extrem.append(item)
+				elif item <= lowHinge - Hstep:
+					outlier.append(item)
+				if item < lowHinge and  item > lowHinge - Hstep:
+					if lowerAdj == None or item < lowerAdj:
+						lowerAdj = item
+					else:
+						pass
+			canvas.drawRect(XCoord-20, plotHeight + yTopOffset - (lowHinge-Yll)*plotHeight/(Yur - Yll), \
+				XCoord+20, plotHeight + yTopOffset - (upHinge-Yll)*plotHeight/(Yur - Yll))
+			canvas.drawLine(XCoord-20, plotHeight + yTopOffset - (catMedian-Yll)*plotHeight/(Yur - Yll), \
+				XCoord+20, plotHeight + yTopOffset - (catMedian-Yll)*plotHeight/(Yur - Yll))
+			if upperAdj != None:
+				canvas.drawLine(XCoord, plotHeight + yTopOffset - (upHinge-Yll)*plotHeight/(Yur - Yll), \
+				XCoord, plotHeight + yTopOffset - (upperAdj-Yll)*plotHeight/(Yur - Yll))
+				canvas.drawLine(XCoord-20, plotHeight + yTopOffset - (upperAdj-Yll)*plotHeight/(Yur - Yll), \
+				XCoord+20, plotHeight + yTopOffset - (upperAdj-Yll)*plotHeight/(Yur - Yll))
+			if lowerAdj != None:
+				canvas.drawLine(XCoord, plotHeight + yTopOffset - (lowHinge-Yll)*plotHeight/(Yur - Yll), \
+				XCoord, plotHeight + yTopOffset - (lowerAdj-Yll)*plotHeight/(Yur - Yll))
+				canvas.drawLine(XCoord-20, plotHeight + yTopOffset - (lowerAdj-Yll)*plotHeight/(Yur - Yll), \
+				XCoord+20, plotHeight + yTopOffset - (lowerAdj-Yll)*plotHeight/(Yur - Yll))
+			
+			outlierFont = pid.Font(ttf="cour",size=12,bold=0)
+			if outlier != []:
+				for item in outlier:
+					yc = plotHeight + yTopOffset - (item-Yll)*plotHeight/(Yur - Yll)
+					#canvas.drawEllipse(XCoord-3, yc-3, XCoord+3, yc+3)
+					canvas.drawString('o', XCoord-3, yc+5, font=outlierFont, color=pid.orange)
+			if extrem != []:
+				for item in extrem:
+					yc = plotHeight + yTopOffset - (item-Yll)*plotHeight/(Yur - Yll)
+					#canvas.drawEllipse(XCoord-3, yc-3, XCoord+3, yc+3)
+					canvas.drawString('*', XCoord-3, yc+6, font=outlierFont, color=pid.red)
+			
+			canvas.drawCross(XCoord, plotHeight + yTopOffset - (catMean-Yll)*plotHeight/(Yur - Yll), \
+				color=pid.blue,size=3)
+			#print (catMean, catMedian, cat25per, cat75per)
+			pass
+		
+		XCoord += stepX
+	
+	labelFont=pid.Font(ttf="verdana",size=18,bold=0)
+	canvas.drawString(XLabel, xLeftOffset + (plotWidth -canvas.stringWidth(XLabel,font=labelFont))/2.0, \
+	YCoord +40, font=labelFont)
+	canvas.drawString(YLabel,xLeftOffset-40, YCoord-(plotHeight -canvas.stringWidth(YLabel,font=labelFont))/2.0,\
+	 font=labelFont, angle =90)
+
+def plotSecurity(canvas, text="12345"):
+	if not text:
+		return
+
+	plotWidth = canvas.size[0]
+	plotHeight = canvas.size[1]
+	if plotHeight<=0 or plotWidth<=0:
+		return
+		
+	bgColor = pid.Color(0.6+0.4*random.random(), 0.6+0.4*random.random(), 0.6+0.4*random.random())
+	canvas.drawRect(0,0,plotWidth,plotHeight, edgeColor=bgColor, fillColor=bgColor)
+	
+	for i in range(30):
+		randomColor = pid.Color(0.6+0.4*random.random(), 0.6+0.4*random.random(), 0.6+0.4*random.random())
+		scaleFont=pid.Font(ttf="cour",size=random.choice(range(20, 50)))
+		canvas.drawString(random.choice('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'), 
+			int(random.random()*plotWidth), int(random.random()*plotHeight), font=scaleFont, 
+			color=randomColor, angle=random.choice(range(-45, 50)))
+			
+	step = (plotWidth-20)/len(text)
+	startX = 20
+	for item in text:
+		randomColor = pid.Color(0.6*random.random(),0.6*random.random(), 0.6*random.random())
+		scaleFont=pid.Font(ttf="verdana",size=random.choice(range(50, 60)),bold=1)
+		canvas.drawString(item, startX, plotHeight/2-10, font=scaleFont, 
+			color=randomColor, angle=random.choice(range(-45, 50)))
+		startX += step
+ 
+# parameter: data is either object returned by reaper permutation function (called by MarkerRegressionPage.py)
+# or the first object returned by direct (pair-scan) permu function (called by DirectPlotPage.py)	 
+def plotBar(canvas, data, barColor=pid.blue, axesColor=pid.black, labelColor=pid.black, XLabel=None, YLabel=None, title=None, offset= (60, 20, 40, 40), zoom = 1):
+	
+	xLeftOffset, xRightOffset, yTopOffset, yBottomOffset = offset
+	
+	plotWidth = canvas.size[0] - xLeftOffset - xRightOffset
+	plotHeight = canvas.size[1] - yTopOffset - yBottomOffset
+	if plotHeight<=0 or plotWidth<=0:
+		return
+		
+	if len(data) < 2:
+		return
+		
+	max_D = max(data)
+	min_D = min(data)
+	#add by NL 06-20-2011: fix the error: when max_D is infinite, log function in detScale will go wrong
+	if max_D == float('inf') or max_D>webqtlConfig.MAXLRS:
+		max_D=webqtlConfig.MAXLRS #maximum LRS value
+		
+	xLow, xTop, stepX = detScale(min_D, max_D)
+	
+	#reduce data
+	step = ceil((xTop-xLow)/50.0)
+	j = xLow
+	dataXY = []
+	Count = []
+	while j <= xTop:
+		dataXY.append(j)
+		Count.append(0)
+		j += step 
+	
+	for i, item in enumerate(data):
+		if item == float('inf') or item>webqtlConfig.MAXLRS:
+			item = webqtlConfig.MAXLRS #maximum LRS value
+		j = int((item-xLow)/step)
+		Count[j] += 1	
+		
+	yLow, yTop, stepY=detScale(0,max(Count))
+	
+	#draw data
+	xScale = plotWidth/(xTop-xLow)
+	yScale = plotHeight/(yTop-yLow)
+	barWidth = xScale*step
+	
+	for i, count in enumerate(Count):
+		if count:
+			xc = (dataXY[i]-xLow)*xScale+xLeftOffset
+			yc =-(count-yLow)*yScale+yTopOffset+plotHeight
+			canvas.drawRect(xc+2,yc,xc+barWidth-2,yTopOffset+plotHeight,edgeColor=barColor,fillColor=barColor)
+	
+	#draw drawing region
+	canvas.drawRect(xLeftOffset, yTopOffset, xLeftOffset+plotWidth, yTopOffset+plotHeight)
+
+	#draw scale
+	scaleFont=pid.Font(ttf="cour",size=11,bold=1)
+	x=xLow
+	for i in range(stepX+1):
+		xc=xLeftOffset+(x-xLow)*xScale
+		canvas.drawLine(xc,yTopOffset+plotHeight,xc,yTopOffset+plotHeight+5, color=axesColor)
+		strX = cformat(d=x, rank=0)
+		canvas.drawString(strX,xc-canvas.stringWidth(strX,font=scaleFont)/2,yTopOffset+plotHeight+14,font=scaleFont)
+		x+= (xTop - xLow)/stepX
+		
+	y=yLow
+	for i in range(stepY+1):
+		yc=yTopOffset+plotHeight-(y-yLow)*yScale
+		canvas.drawLine(xLeftOffset,yc,xLeftOffset-5,yc, color=axesColor)
+		strY = "%d" %y
+		canvas.drawString(strY,xLeftOffset-canvas.stringWidth(strY,font=scaleFont)-6,yc+5,font=scaleFont)
+		y+= (yTop - yLow)/stepY
+	
+	#draw label
+	labelFont=pid.Font(ttf="tahoma",size=17,bold=0)
+	if XLabel:
+		canvas.drawString(XLabel,xLeftOffset+(plotWidth-canvas.stringWidth(XLabel,font=labelFont))/2.0,
+			yTopOffset+plotHeight+yBottomOffset-10,font=labelFont,color=labelColor)
+	
+	if YLabel:
+		canvas.drawString(YLabel, 19, yTopOffset+plotHeight-(plotHeight-canvas.stringWidth(YLabel,font=labelFont))/2.0,
+			font=labelFont,color=labelColor,angle=90)
+
+	labelFont=pid.Font(ttf="verdana",size=16,bold=0)
+	if title:
+		canvas.drawString(title,xLeftOffset+(plotWidth-canvas.stringWidth(title,font=labelFont))/2.0,
+			20,font=labelFont,color=labelColor)
+
+def plotBarText(canvas, data, label, variance=None, barColor=pid.blue, axesColor=pid.black, labelColor=pid.black, XLabel=None, YLabel=None, title=None, sLabel = None, offset= (80, 20, 40, 100), barSpace = 2, zoom = 1):
+	xLeftOffset, xRightOffset, yTopOffset, yBottomOffset = offset
+	plotWidth = canvas.size[0] - xLeftOffset - xRightOffset
+	plotHeight = canvas.size[1] - yTopOffset - yBottomOffset
+	if plotHeight<=0 or plotWidth<=0:
+		return
+	
+	NNN = len(data)
+	if NNN < 2 or NNN != len(label):
+		return
+	if variance and len(variance)!=NNN:
+		variance = []
+	
+	Y2 = data[:]
+	if variance:
+		for i in range(NNN):
+			if variance[i]:
+				Y2 += [data[i]-variance[i]]
+				
+	#Y axis
+	YLow, YTop, stepY = detScale(min(Y2), max(Y2))
+	YScale = plotHeight/(YTop - YLow)
+	
+	if YLow < 0  and  YTop > 0:
+		drawZero = 1
+	else:
+		drawZero = 0
+		
+	#X axis
+	X = range(NNN)
+	Xll= 0 
+	Xur= NNN-1
+	
+	
+	if drawZero:
+		YZero = yTopOffset+plotHeight-YScale*(0-YLow)
+		canvas.drawLine(xLeftOffset, YZero, xLeftOffset+plotWidth, YZero)
+	else:
+		YZero = yTopOffset+plotHeight
+	#draw data
+	spaceWidth = barSpace
+	if spaceWidth < 1:
+		spaceWidth = 1
+	barWidth = int((plotWidth - (NNN-1.0)*spaceWidth)/NNN)
+	
+	xc= xLeftOffset
+	scaleFont=pid.Font(ttf="verdana",size=11,bold=0)	
+	for i in range(NNN):
+		yc = yTopOffset+plotHeight-(data[i]-YLow)*YScale
+		canvas.drawRect(xc,YZero,xc+barWidth-1, yc, edgeColor=barColor,fillColor=barColor)
+		if variance and variance[i]:
+			varlen = variance[i]*YScale
+			if yc-varlen < yTopOffset:
+				topYd = yTopOffset
+			else:
+				topYd = yc-varlen
+				canvas.drawLine(xc+barWidth/2-2,yc-varlen,xc+barWidth/2+2,yc-varlen,color=pid.red)
+			canvas.drawLine(xc+barWidth/2,yc+varlen,xc+barWidth/2,topYd,color=pid.red)
+			canvas.drawLine(xc+barWidth/2-2,yc+varlen,xc+barWidth/2+2,yc+varlen,color=pid.red)
+		strX = label[i]
+		canvas.drawString(strX,xc+barWidth/2.0+2,yTopOffset+plotHeight+2+canvas.stringWidth(strX,font=scaleFont),font=scaleFont,angle=90)
+		xc += barWidth + spaceWidth	
+		
+	#draw drawing region
+	canvas.drawRect(xLeftOffset, yTopOffset, xLeftOffset+plotWidth, yTopOffset+plotHeight)
+	
+	#draw Y scale
+	scaleFont=pid.Font(ttf="cour",size=16,bold=1)		
+	y=YLow
+	for i in range(stepY+1):
+		yc=yTopOffset+plotHeight-(y-YLow)*YScale
+		canvas.drawLine(xLeftOffset,yc,xLeftOffset-5,yc, color=axesColor)
+		strY = cformat(d=y, rank=0)
+		canvas.drawString(strY,xLeftOffset-canvas.stringWidth(strY,font=scaleFont)-6,yc+5,font=scaleFont)
+		y+= (YTop - YLow)/stepY
+	
+	#draw label
+	labelFont=pid.Font(ttf="verdana",size=17,bold=0)
+	if XLabel:
+		canvas.drawString(XLabel,xLeftOffset+(plotWidth-canvas.stringWidth(XLabel,font=labelFont))/2.0,yTopOffset+plotHeight+65,font=labelFont,color=labelColor)
+	
+	if YLabel:
+		canvas.drawString(YLabel,xLeftOffset-50, yTopOffset+plotHeight-(plotHeight-canvas.stringWidth(YLabel,font=labelFont))/2.0,font=labelFont,color=labelColor,angle=90)
+	
+	labelFont=pid.Font(ttf="verdana",size=18,bold=0)
+	if title:
+		canvas.drawString(title,xLeftOffset,yTopOffset-15,font=labelFont,color=labelColor)
+		
+	return
+		
+def plotXY(canvas, dataX, dataY, rank=0, dataLabel=[], plotColor = pid.black, axesColor=pid.black, labelColor=pid.black, lineSize="thin", lineColor=pid.grey, idFont="arial", idColor=pid.blue, idSize="14", symbolColor=pid.black, symbolType="circle", filled="yes", symbolSize="tiny", XLabel=None, YLabel=None, title=None, fitcurve=None, connectdot=1, displayR=None, loadingPlot = 0, offset= (80, 20, 40, 60), zoom = 1, specialCases=[], showLabel = 1, bufferSpace = 15):
+	'displayR : correlation scatter plot, loadings : loading plot'
+	
+	dataXRanked, dataYRanked = webqtlUtil.calRank(dataX, dataY, len(dataX))
+		
+	#get ID font size
+	idFontSize = int(idSize)	
+	
+	#If filled is yes, set fill color
+	if filled == "yes":
+		fillColor = symbolColor
+	else:
+		fillColor = None	
+	
+	if symbolSize == "large":
+		sizeModifier = 7
+		fontModifier = 12
+	elif symbolSize == "medium":
+		sizeModifier = 5
+		fontModifier = 8
+	elif symbolSize == "small":
+		sizeModifier = 3
+		fontModifier = 3
+	else:
+		sizeModifier = 1
+		fontModifier = -1	
+			
+	if rank == 0:    # Pearson correlation
+		bufferSpace = 0
+		dataXPrimary = dataX
+		dataYPrimary = dataY
+		dataXAlt = dataXRanked    #Values used just for printing the other corr type to the graph image
+		dataYAlt = dataYRanked    #Values used just for printing the other corr type to the graph image
+	else:    # Spearman correlation: Switching Ranked and Unranked X and Y values
+		dataXPrimary = dataXRanked
+		dataYPrimary = dataYRanked
+		dataXAlt = dataX    #Values used just for printing the other corr type to the graph image
+		dataYAlt = dataY    #Values used just for printing the other corr type to the graph image
+	
+	xLeftOffset, xRightOffset, yTopOffset, yBottomOffset = offset
+	plotWidth = canvas.size[0] - xLeftOffset - xRightOffset
+	plotHeight = canvas.size[1] - yTopOffset - yBottomOffset
+	if plotHeight<=0 or plotWidth<=0:
+		return
+	if len(dataXPrimary) < 1 or  len(dataXPrimary) != len(dataYPrimary) or (dataLabel and len(dataXPrimary) != len(dataLabel)):
+		return
+	
+	max_X=max(dataXPrimary)
+	min_X=min(dataXPrimary)
+	max_Y=max(dataYPrimary)
+	min_Y=min(dataYPrimary)
+	
+	#for some reason I forgot why I need to do this
+	if loadingPlot:
+		min_X = min(-0.1,min_X)
+		max_X = max(0.1,max_X)
+		min_Y = min(-0.1,min_Y)
+		max_Y = max(0.1,max_Y)
+	
+	xLow, xTop, stepX=detScale(min_X,max_X)
+	yLow, yTop, stepY=detScale(min_Y,max_Y)
+	xScale = plotWidth/(xTop-xLow)
+	yScale = plotHeight/(yTop-yLow)
+	
+	#draw drawing region
+	canvas.drawRect(xLeftOffset-bufferSpace, yTopOffset, xLeftOffset+plotWidth, yTopOffset+plotHeight+bufferSpace)
+	canvas.drawRect(xLeftOffset-bufferSpace+1, yTopOffset, xLeftOffset+plotWidth, yTopOffset+plotHeight+bufferSpace-1)
+
+	#calculate data points	
+	data = map(lambda X, Y: (X, Y), dataXPrimary, dataYPrimary)
+	xCoord = map(lambda X, Y: ((X-xLow)*xScale + xLeftOffset, yTopOffset+plotHeight-(Y-yLow)*yScale), dataXPrimary, dataYPrimary)
+
+	labelFont=pid.Font(ttf=idFont,size=idFontSize,bold=0)
+
+	if loadingPlot:
+		xZero = -xLow*xScale+xLeftOffset
+		yZero = yTopOffset+plotHeight+yLow*yScale
+		for point in xCoord:
+			canvas.drawLine(xZero,yZero,point[0],point[1],color=pid.red)
+	else:
+		if connectdot:
+			canvas.drawPolygon(xCoord,edgeColor=plotColor,closed=0)
+		else:
+			pass
+
+	symbolFont = pid.Font(ttf="fnt_bs", size=12+fontModifier,bold=0)
+
+	for i, item in enumerate(xCoord):
+		if dataLabel and dataLabel[i] in specialCases:
+			canvas.drawRect(item[0]-3, item[1]-3, item[0]+3, item[1]+3, edgeColor=pid.green)
+			#canvas.drawCross(item[0],item[1],color=pid.blue,size=5)
+		else:
+			if symbolType == "vertRect":
+				canvas.drawRect(x1=item[0]-sizeModifier+2,y1=item[1]-sizeModifier-2, x2=item[0]+sizeModifier-1,y2=item[1]+sizeModifier+2, edgeColor=symbolColor, edgeWidth=1, fillColor=fillColor)
+			elif (symbolType == "circle" and filled != "yes"):
+				canvas.drawString(":", item[0]-canvas.stringWidth(":",font=symbolFont)/2+1,item[1]+2,color=symbolColor, font=symbolFont)
+			elif (symbolType == "circle" and filled == "yes"):
+				canvas.drawString("5", item[0]-canvas.stringWidth("5",font=symbolFont)/2+1,item[1]+2,color=symbolColor, font=symbolFont)
+			elif symbolType == "horiRect":
+				canvas.drawRect(x1=item[0]-sizeModifier-1,y1=item[1]-sizeModifier+3, x2=item[0]+sizeModifier+3,y2=item[1]+sizeModifier-2, edgeColor=symbolColor, edgeWidth=1, fillColor=fillColor)
+			elif (symbolType == "square"):
+				canvas.drawRect(x1=item[0]-sizeModifier+1,y1=item[1]-sizeModifier-4, x2=item[0]+sizeModifier+2,y2=item[1]+sizeModifier-3, edgeColor=symbolColor, edgeWidth=1, fillColor=fillColor)
+			elif (symbolType == "diamond" and filled != "yes"):
+				canvas.drawString(",", item[0]-canvas.stringWidth(",",font=symbolFont)/2+2, item[1]+6, font=symbolFont, color=symbolColor)
+			elif (symbolType == "diamond" and filled == "yes"):
+				canvas.drawString("D", item[0]-canvas.stringWidth("D",font=symbolFont)/2+2, item[1]+6, font=symbolFont, color=symbolColor)	
+			elif symbolType == "4-star":
+				canvas.drawString("l", item[0]-canvas.stringWidth("l",font=symbolFont)/2+1, item[1]+3, font=symbolFont, color=symbolColor)	
+			elif symbolType == "3-star":
+				canvas.drawString("k", item[0]-canvas.stringWidth("k",font=symbolFont)/2+1, item[1]+3, font=symbolFont, color=symbolColor)	
+			else:	
+				canvas.drawCross(item[0],item[1]-2,color=symbolColor, size=sizeModifier+2)
+
+		if showLabel and dataLabel:
+			if (symbolType == "vertRect" or symbolType == "diamond"):
+				labelGap = 15
+			elif (symbolType == "4-star" or symbolType == "3-star"):
+				labelGap = 12
+			else:
+			    labelGap = 11
+			canvas.drawString(dataLabel[i], item[0]- canvas.stringWidth(dataLabel[i],
+				font=labelFont)/2 + 1, item[1]+(labelGap+sizeModifier+(idFontSize-12)), font=labelFont, color=idColor)
+                        	
+	#draw scale
+	scaleFont=pid.Font(ttf="cour",size=16,bold=1)
+	
+
+	x=xLow
+	for i in range(stepX+1):
+		xc=xLeftOffset+(x-xLow)*xScale
+		if ((x == 0) & (rank == 1)):
+			pass
+		else:
+			canvas.drawLine(xc,yTopOffset+plotHeight + bufferSpace,xc,yTopOffset+plotHeight+5 + bufferSpace, color=axesColor)		
+		strX = cformat(d=x, rank=rank)
+		if ((strX == "0") & (rank == 1)):
+			pass
+		else:
+			canvas.drawString(strX,xc-canvas.stringWidth(strX,font=scaleFont)/2,yTopOffset+plotHeight+20 + bufferSpace,font=scaleFont)
+		x+= (xTop - xLow)/stepX
+	
+	y=yLow
+	for i in range(stepY+1):
+		yc=yTopOffset+plotHeight-(y-yLow)*yScale
+		if ((y == 0) & (rank == 1)):
+			pass
+		else:
+			canvas.drawLine(xLeftOffset - bufferSpace,yc,xLeftOffset-5 - bufferSpace,yc, color=axesColor)
+		strY = cformat(d=y, rank=rank)
+		if ((strY == "0") & (rank == 1)):
+			pass
+		else:
+			canvas.drawString(strY,xLeftOffset-canvas.stringWidth(strY,font=scaleFont)- 10 - bufferSpace,yc+4,font=scaleFont)
+		y+= (yTop - yLow)/stepY
+			
+	#draw label
+
+	labelFont=pid.Font(ttf="verdana",size=canvas.size[0]/45,bold=0)
+	titleFont=pid.Font(ttf="verdana",size=canvas.size[0]/40,bold=0)
+		
+	if (rank == 1 and not title):
+		canvas.drawString("Spearman Rank Correlation", xLeftOffset-canvas.size[0]*.025+(plotWidth-canvas.stringWidth("Spearman Rank Correlation",font=titleFont))/2.0,
+						  25,font=titleFont,color=labelColor)
+	elif (rank == 0 and not title):
+		canvas.drawString("Pearson Correlation", xLeftOffset-canvas.size[0]*.025+(plotWidth-canvas.stringWidth("Pearson Correlation",font=titleFont))/2.0,
+						  25,font=titleFont,color=labelColor)
+		
+	if XLabel:
+		canvas.drawString(XLabel,xLeftOffset+(plotWidth-canvas.stringWidth(XLabel,font=labelFont))/2.0,
+			yTopOffset+plotHeight+yBottomOffset-25,font=labelFont,color=labelColor)
+	
+	if YLabel:
+		canvas.drawString(YLabel, xLeftOffset-65, yTopOffset+plotHeight- (plotHeight-canvas.stringWidth(YLabel,font=labelFont))/2.0,
+			font=labelFont,color=labelColor,angle=90)
+
+	labelFont=pid.Font(ttf="verdana",size=20,bold=0)
+	if title:
+		canvas.drawString(title,xLeftOffset+(plotWidth-canvas.stringWidth(title,font=labelFont))/2.0,
+			20,font=labelFont,color=labelColor)
+	
+	if fitcurve:
+		import sys
+		sys.argv = [ "mod_python" ]
+		#from numarray import linear_algebra as la
+		#from numarray import ones, array, dot, swapaxes
+		fitYY = array(dataYPrimary)
+		fitXX = array([ones(len(dataXPrimary)),dataXPrimary])
+		AA = dot(fitXX,swapaxes(fitXX,0,1))
+		BB = dot(fitXX,fitYY)
+		bb = la.linear_least_squares(AA,BB)[0]
+		
+		xc1 = xLeftOffset
+		yc1 = yTopOffset+plotHeight-(bb[0]+bb[1]*xLow-yLow)*yScale 
+		if yc1 > yTopOffset+plotHeight:
+			yc1 = yTopOffset+plotHeight
+			xc1 = (yLow-bb[0])/bb[1]
+			xc1=(xc1-xLow)*xScale+xLeftOffset
+		elif yc1 < yTopOffset:
+			yc1 = yTopOffset
+			xc1 = (yTop-bb[0])/bb[1]
+			xc1=(xc1-xLow)*xScale+xLeftOffset
+		else:
+			pass
+		
+		xc2 = xLeftOffset + plotWidth 
+		yc2 = yTopOffset+plotHeight-(bb[0]+bb[1]*xTop-yLow)*yScale
+		if yc2 > yTopOffset+plotHeight:
+			yc2 = yTopOffset+plotHeight
+			xc2 = (yLow-bb[0])/bb[1]
+			xc2=(xc2-xLow)*xScale+xLeftOffset
+		elif yc2 < yTopOffset:
+			yc2 = yTopOffset
+			xc2 = (yTop-bb[0])/bb[1]
+			xc2=(xc2-xLow)*xScale+xLeftOffset
+		else:
+			pass
+
+		canvas.drawLine(xc1 - bufferSpace,yc1 + bufferSpace,xc2,yc2,color=lineColor)
+		if lineSize == "medium":
+			canvas.drawLine(xc1 - bufferSpace,yc1 + bufferSpace+1,xc2,yc2+1,color=lineColor)
+		if lineSize == "thick":
+			canvas.drawLine(xc1 - bufferSpace,yc1 + bufferSpace+1,xc2,yc2+1,color=lineColor)
+			canvas.drawLine(xc1 - bufferSpace,yc1 + bufferSpace-1,xc2,yc2-1,color=lineColor)
+		
+		
+	if displayR:
+		labelFont=pid.Font(ttf="trebuc",size=canvas.size[0]/60,bold=0)
+		NNN = len(dataX)
+		corr = webqtlUtil.calCorrelation(dataXPrimary,dataYPrimary,NNN)[0]
+
+		if NNN < 3:
+			corrPValue = 1.0
+		else:
+			if abs(corr) >= 1.0:
+				corrPValue = 0.0
+			else:
+				ZValue = 0.5*log((1.0+corr)/(1.0-corr))
+				ZValue = ZValue*sqrt(NNN-3)
+				corrPValue = 2.0*(1.0 - reaper.normp(abs(ZValue)))
+				
+		NStr = "N = %d" % NNN
+		strLenN = canvas.stringWidth(NStr,font=labelFont)
+
+		if rank == 1:
+		    if corrPValue < 0.0000000000000001:
+				corrStr = "Rho = %1.3f P < 1.00 E-16" % (corr)
+		    else:
+				corrStr = "Rho = %1.3f P = %3.2E" % (corr, corrPValue)
+		else:
+		    if corrPValue < 0.0000000000000001:
+				corrStr = "r = %1.3f P < 1.00 E-16" % (corr)
+		    else:
+				corrStr = "r = %1.3f P = %3.2E" % (corr, corrPValue)
+		strLen = canvas.stringWidth(corrStr,font=labelFont)
+
+		canvas.drawString(NStr,xLeftOffset,yTopOffset-10,font=labelFont,color=labelColor)
+		canvas.drawString(corrStr,xLeftOffset+plotWidth-strLen,yTopOffset-10,font=labelFont,color=labelColor)
+
+	return xCoord
+
+def plotXYSVG(drawSpace, dataX, dataY, rank=0, dataLabel=[], plotColor = "black", axesColor="black", labelColor="black", symbolColor="red", XLabel=None, YLabel=None, title=None, fitcurve=None, connectdot=1, displayR=None, loadingPlot = 0, offset= (80, 20, 40, 60), zoom = 1, specialCases=[], showLabel = 1):
+	'displayR : correlation scatter plot, loadings : loading plot'
+	
+	dataXRanked, dataYRanked = webqtlUtil.calRank(dataX, dataY, len(dataX))
+			
+	# Switching Ranked and Unranked X and Y values if a Spearman Rank Correlation			
+	if rank == 0:
+		dataXPrimary = dataX
+		dataYPrimary = dataY
+		dataXAlt = dataXRanked
+		dataYAlt = dataYRanked		
+	
+	else:	
+		dataXPrimary = dataXRanked
+		dataYPrimary = dataYRanked
+		dataXAlt = dataX
+		dataYAlt = dataY
+
+
+	
+	xLeftOffset, xRightOffset, yTopOffset, yBottomOffset = offset
+	plotWidth = drawSpace.attributes['width'] - xLeftOffset - xRightOffset
+	plotHeight = drawSpace.attributes['height'] - yTopOffset - yBottomOffset
+	if plotHeight<=0 or plotWidth<=0:
+		return
+	if len(dataXPrimary) < 1 or  len(dataXPrimary) != len(dataYPrimary) or (dataLabel and len(dataXPrimary) != len(dataLabel)):
+		return
+	
+	max_X=max(dataXPrimary)
+	min_X=min(dataXPrimary)
+	max_Y=max(dataYPrimary)
+	min_Y=min(dataYPrimary)
+	
+	#for some reason I forgot why I need to do this
+	if loadingPlot:
+		min_X = min(-0.1,min_X)
+		max_X = max(0.1,max_X)
+		min_Y = min(-0.1,min_Y)
+		max_Y = max(0.1,max_Y)
+	
+	xLow, xTop, stepX=detScale(min_X,max_X)
+	yLow, yTop, stepY=detScale(min_Y,max_Y)
+	xScale = plotWidth/(xTop-xLow)
+	yScale = plotHeight/(yTop-yLow)
+	
+	#draw drawing region
+	r = svg.rect(xLeftOffset, yTopOffset, plotWidth,  plotHeight, 'none', axesColor, 1)
+	drawSpace.addElement(r)
+	
+	#calculate data points	
+	data = map(lambda X, Y: (X, Y), dataXPrimary, dataYPrimary)
+	xCoord = map(lambda X, Y: ((X-xLow)*xScale + xLeftOffset, yTopOffset+plotHeight-(Y-yLow)*yScale), dataXPrimary, dataYPrimary)
+	labelFontF = "verdana"
+	labelFontS = 11
+	
+	if loadingPlot:
+		xZero = -xLow*xScale+xLeftOffset
+		yZero = yTopOffset+plotHeight+yLow*yScale
+		for point in xCoord:
+			drawSpace.addElement(svg.line(xZero,yZero,point[0],point[1], "red", 1))
+	else:
+		if connectdot:
+			pass
+			#drawSpace.drawPolygon(xCoord,edgeColor=plotColor,closed=0)
+		else:
+			pass
+			
+	for i, item in enumerate(xCoord):
+		if dataLabel and dataLabel[i] in specialCases:
+			drawSpace.addElement(svg.rect(item[0]-3, item[1]-3, 6, 6, "none", "green", 0.5))
+			#drawSpace.drawCross(item[0],item[1],color=pid.blue,size=5)
+		else:
+			drawSpace.addElement(svg.line(item[0],item[1]+5,item[0],item[1]-5,symbolColor,1))
+			drawSpace.addElement(svg.line(item[0]+5,item[1],item[0]-5,item[1],symbolColor,1))
+		if showLabel and dataLabel:
+			pass
+			drawSpace.addElement(svg.text(item[0], item[1]+14, dataLabel[i], labelFontS, 
+				labelFontF, text_anchor="middle", style="stroke:blue;stroke-width:0.5;"))
+			#canvas.drawString(, item[0]- canvas.stringWidth(dataLabel[i],
+			#	font=labelFont)/2, item[1]+14, font=labelFont, color=pid.blue)
+	
+	#draw scale
+	#scaleFont=pid.Font(ttf="cour",size=14,bold=1)
+	x=xLow
+	for i in range(stepX+1):
+		xc=xLeftOffset+(x-xLow)*xScale
+		drawSpace.addElement(svg.line(xc,yTopOffset+plotHeight,xc,yTopOffset+plotHeight+5, axesColor, 1))
+		strX = cformat(d=x, rank=rank)
+		drawSpace.addElement(svg.text(xc,yTopOffset+plotHeight+20,strX,13, "courier", text_anchor="middle"))
+		x+= (xTop - xLow)/stepX
+		
+	y=yLow
+	for i in range(stepY+1):
+		yc=yTopOffset+plotHeight-(y-yLow)*yScale
+		drawSpace.addElement(svg.line(xLeftOffset,yc,xLeftOffset-5,yc, axesColor, 1))
+		strY = cformat(d=y, rank=rank)
+		drawSpace.addElement(svg.text(xLeftOffset-10,yc+5,strY,13, "courier", text_anchor="end"))
+		y+= (yTop - yLow)/stepY
+			
+	#draw label
+	labelFontF = "verdana"
+	labelFontS = 17
+	if XLabel:
+		drawSpace.addElement(svg.text(xLeftOffset+plotWidth/2.0,
+			yTopOffset+plotHeight+yBottomOffset-10,XLabel,
+			labelFontS, labelFontF, text_anchor="middle"))
+	
+	if YLabel:
+		drawSpace.addElement(svg.text(xLeftOffset-50,
+			 yTopOffset+plotHeight/2,YLabel,
+			labelFontS, labelFontF, text_anchor="middle", style="writing-mode:tb-rl", transform="rotate(270 %d %d)" % (xLeftOffset-50,  yTopOffset+plotHeight/2)))
+		#drawSpace.drawString(YLabel, xLeftOffset-50, yTopOffset+plotHeight- (plotHeight-drawSpace.stringWidth(YLabel,font=labelFont))/2.0,
+		#	font=labelFont,color=labelColor,angle=90)
+
+
+	if fitcurve:
+		sys.argv = [ "mod_python" ]
+                #from numarray import linear_algebra as la
+		#from numarray import ones, array, dot, swapaxes
+		fitYY = array(dataYPrimary)
+		fitXX = array([ones(len(dataXPrimary)),dataXPrimary])
+		AA = dot(fitXX,swapaxes(fitXX,0,1))
+		BB = dot(fitXX,fitYY)
+		bb = la.linear_least_squares(AA,BB)[0]
+		
+		xc1 = xLeftOffset
+		yc1 = yTopOffset+plotHeight-(bb[0]+bb[1]*xLow-yLow)*yScale 
+		if yc1 > yTopOffset+plotHeight:
+			yc1 = yTopOffset+plotHeight
+			xc1 = (yLow-bb[0])/bb[1]
+			xc1=(xc1-xLow)*xScale+xLeftOffset
+		elif yc1 < yTopOffset:
+			yc1 = yTopOffset
+			xc1 = (yTop-bb[0])/bb[1]
+			xc1=(xc1-xLow)*xScale+xLeftOffset
+		else:
+			pass
+		
+		xc2 = xLeftOffset + plotWidth 
+		yc2 = yTopOffset+plotHeight-(bb[0]+bb[1]*xTop-yLow)*yScale
+		if yc2 > yTopOffset+plotHeight:
+			yc2 = yTopOffset+plotHeight
+			xc2 = (yLow-bb[0])/bb[1]
+			xc2=(xc2-xLow)*xScale+xLeftOffset
+		elif yc2 < yTopOffset:
+			yc2 = yTopOffset
+			xc2 = (yTop-bb[0])/bb[1]
+			xc2=(xc2-xLow)*xScale+xLeftOffset
+		else:
+			pass
+
+		drawSpace.addElement(svg.line(xc1,yc1,xc2,yc2,"green", 1))
+	
+	if displayR:		
+		labelFontF = "trebuc"
+		labelFontS = 14
+		NNN = len(dataX)
+
+		corr = webqtlUtil.calCorrelation(dataXPrimary,dataYPrimary,NNN)[0]
+
+		if NNN < 3:
+			corrPValue = 1.0
+		else:
+			if abs(corr) >= 1.0:
+				corrPValue = 0.0
+			else:
+				ZValue = 0.5*log((1.0+corr)/(1.0-corr))
+				ZValue = ZValue*sqrt(NNN-3)
+				corrPValue = 2.0*(1.0 - reaper.normp(abs(ZValue)))
+				
+		NStr = "N of Cases=%d" % NNN
+		
+		if rank == 1:
+			corrStr = "Spearman's r=%1.3f P=%3.2E" % (corr, corrPValue)
+		else:
+			corrStr = "Pearson's r=%1.3f P=%3.2E" % (corr, corrPValue)
+		
+		drawSpace.addElement(svg.text(xLeftOffset,yTopOffset-10,NStr,
+			labelFontS, labelFontF, text_anchor="start"))
+		drawSpace.addElement(svg.text(xLeftOffset+plotWidth,yTopOffset-25,corrStr,
+			labelFontS, labelFontF, text_anchor="end"))
+	"""	
+	"""
+	return
+
+
+# This function determines the scale of the plot	
+def detScaleOld(min,max):
+	if min>=max:
+		return None
+	elif min == -1.0 and max == 1.0:
+		return [-1.2,1.2,12]
+	else:
+		a=max-min
+		b=floor(log10(a))
+		c=pow(10.0,b)
+		if a < c*5.0:
+			c/=2.0
+		#print a,b,c
+		low=c*floor(min/c)
+		high=c*ceil(max/c)
+		return [low,high,round((high-low)/c)]
+
+def detScale(min=0,max=0,bufferSpace=3):
+
+	if min>=max:
+		return None
+	elif min == -1.0 and max == 1.0:
+		return [-1.2,1.2,12]
+	else:
+		a=max-min
+		if max != 0:
+			max += 0.1*a
+		if min != 0:
+			if min > 0 and min < 0.1*a:
+				min = 0.0
+			else:
+				min -= 0.1*a
+		a=max-min
+		b=floor(log10(a))
+		c=pow(10.0,b)
+		low=c*floor(min/c)
+		high=c*ceil(max/c)
+		n = round((high-low)/c)
+		div = 2.0
+		while n < 5 or n > 15:
+			if n < 5:
+				c /= div
+			else:
+				c *= div
+			if div == 2.0:
+				div =5.0
+			else:
+				div =2.0
+			low=c*floor(min/c)
+			high=c*ceil(max/c)
+			n = round((high-low)/c)
+
+		return [low,high,n]
+
+
+
+def colorSpectrumOld(n):
+	if n == 1:
+		return [pid.Color(1,0,0)]
+	elif n == 2:
+		return [pid.Color(1,0,0),pid.Color(0,0,1)]
+	elif n == 3:
+		return [pid.Color(1,0,0),pid.Color(0,1,0),pid.Color(0,0,1)]
+	else:
+		step = 2.0/(n-1)
+		red = 1.0
+		green = 0.0
+		blue = 0.0
+		colors = [pid.Color(red,green,blue)]
+		i = 1
+		greenpeak = 0
+		while i < n:
+			if red >= step:
+				red -= step
+				green += step
+				if green >= 1.0:
+					greenpeak = 1
+					blue += green -1.0
+					green = 1.0
+			else:
+				red = 0.0
+				if greenpeak:
+					green -= step
+					blue += step
+				else:
+					green += step
+				if green >= 1.0:
+					greenpeak = 1
+					blue += green -1.0
+					green = 2.0 -green
+				elif green < 0.0:
+					green = 0.0
+				else:
+					pass
+			colors.append(pid.Color(red,green,blue))
+			i += 1
+		return colors
+		
+		
+
+
+def bluefunc(x):
+	return 1.0 / (1.0 + exp(-10*(x-0.6)))
+
+
+def redfunc(x):
+	return 1.0 / (1.0 + exp(10*(x-0.5)))
+
+def greenfunc(x):
+	return 1 - pow(redfunc(x+0.2),2) - bluefunc(x-0.3)
+
+def colorSpectrum(n=100):
+	multiple = 10
+	if n == 1:
+		return [pid.Color(1,0,0)]
+	elif n == 2:
+		return [pid.Color(1,0,0),pid.Color(0,0,1)]
+	elif n == 3:
+		return [pid.Color(1,0,0),pid.Color(0,1,0),pid.Color(0,0,1)]
+	N = n*multiple
+	out = [None]*N;
+	for i in range(N):
+		x = float(i)/N
+		out[i] = pid.Color(redfunc(x), greenfunc(x), bluefunc(x));
+	out2 = [out[0]]
+	step = N/float(n-1)
+	j = 0
+	for i in range(n-2):
+		j += step
+		out2.append(out[int(j)])
+	out2.append(out[-1])
+	return out2
+
+
+def colorSpectrumSVG(n=100):
+	multiple = 10
+	if n == 1:
+		return ["rgb(255,0,0)"]
+	elif n == 2:
+		return ["rgb(255,0,0)","rgb(0,0,255)"]
+	elif n == 3:
+		return ["rgb(255,0,0)","rgb(0,255,0)","rgb(0,0,255)"]
+	N = n*multiple
+	out = [None]*N;
+	for i in range(N):
+		x = float(i)/N
+		out[i] = "rgb(%d, %d, %d)" % (redfunc(x)*255, greenfunc(x)*255, bluefunc(x)*255);
+	out2 = [out[0]]
+	step = N/float(n-1)
+	j = 0
+	for i in range(n-2):
+		j += step
+		out2.append(out[int(j)])
+	out2.append(out[-1])
+	return out2
+
+
+def BWSpectrum(n=100):
+	multiple = 10
+	if n == 1:
+		return [pid.Color(0,0,0)]
+	elif n == 2:
+		return [pid.Color(0,0,0),pid.Color(1,1,1)]
+	elif n == 3:
+		return [pid.Color(0,0,0),pid.Color(0.5,0.5,0.5),pid.Color(1,1,1)]
+	
+	step = 1.0/n
+	x = 0.0
+	out = []
+	for i in range(n):
+		out.append(pid.Color(x,x,x));
+		x += step
+	return out
diff --git a/web/webqtl/utility/TDCell.py b/web/webqtl/utility/TDCell.py
new file mode 100755
index 00000000..76b9c5db
--- /dev/null
+++ b/web/webqtl/utility/TDCell.py
@@ -0,0 +1,42 @@
+# 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
+
+##########################################################
+#
+#    Table Cell Class
+#
+##########################################################
+
+		
+class TDCell:
+	def __init__(self, html="", text="", val=0.0):
+		self.html = html	#html, for web page
+		self.text = text	#text value, for output to a text file
+		self.val = val		#sort by value
+	
+	def __str__(self):
+		return self.text
+
diff --git a/web/webqtl/utility/THCell.py b/web/webqtl/utility/THCell.py
new file mode 100755
index 00000000..a96b9e49
--- /dev/null
+++ b/web/webqtl/utility/THCell.py
@@ -0,0 +1,44 @@
+# 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
+
+##########################################################
+#
+#    Table Header Class
+#
+##########################################################
+
+
+class THCell:
+	def __init__(self, html="", text="", sort=1, idx=-1):
+		self.html = html	#html, for web page
+		self.text = text	#Column text value
+		self.sort = sort	#0: not sortable, 1: yes
+		self.idx = idx		#sort by value
+	
+	def __str__(self):
+		return self.text
+		
+
diff --git a/web/webqtl/utility/__init__.py b/web/webqtl/utility/__init__.py
new file mode 100755
index 00000000..e69de29b
--- /dev/null
+++ b/web/webqtl/utility/__init__.py
diff --git a/web/webqtl/utility/svg.py b/web/webqtl/utility/svg.py
new file mode 100755
index 00000000..e49a6c3c
--- /dev/null
+++ b/web/webqtl/utility/svg.py
@@ -0,0 +1,1069 @@
+# 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
+
+#!/usr/bin/env python
+##Copyright (c) 2002, Fedor Baart & Hans de Wit (Stichting Farmaceutische Kengetallen)
+##All rights reserved.
+##
+##Redistribution and use in source and binary forms, with or without modification,
+##are permitted provided that the following conditions are met:
+##
+##Redistributions of source code must retain the above copyright notice, this
+##list of conditions and the following disclaimer.
+##
+##Redistributions in binary form must reproduce the above copyright notice,
+##this list of conditions and the following disclaimer in the documentation and/or
+##other materials provided with the distribution.
+##
+##Neither the name of the Stichting Farmaceutische Kengetallen nor the names of
+##its contributors may be used to endorse or promote products derived from this
+##software without specific prior written permission.
+##
+##THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+##AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+##IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+##DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+##FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+##DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+##SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+##CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+##OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+##OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+##Thanks to Gerald Rosennfellner for his help and useful comments.
+
+__doc__="""Use SVGdraw to generate your SVGdrawings.
+
+SVGdraw uses an object model drawing and a method toXML to create SVG graphics
+by using easy to use classes and methods usualy you start by creating a drawing eg
+
+    d=drawing()
+    #then you create a SVG root element
+    s=svg()
+    #then you add some elements eg a circle and add it to the svg root element
+    c=circle()
+    #you can supply attributes by using named arguments.
+    c=circle(fill='red',stroke='blue')
+    #or by updating the attributes attribute:
+    c.attributes['stroke-width']=1
+    s.addElement(c)
+    #then you add the svg root element to the drawing
+    d.setSVG(s)
+    #and finaly you xmlify the drawing
+    d.toXml()
+    
+
+this results in the svg source of the drawing, which consists of a circle
+on a white background. Its as easy as that;)
+This module was created using the SVG specification of www.w3c.org and the
+O'Reilly (www.oreilly.com) python books as information sources. A svg viewer
+is available from www.adobe.com"""
+
+__version__="1.0"
+
+# there are two possibilities to generate svg:
+# via a dom implementation and directly using <element>text</element> strings
+# the latter is way faster (and shorter in coding)
+# the former is only used in debugging svg programs
+# maybe it will be removed alltogether after a while
+# with the following variable you indicate whether to use the dom implementation
+# Note that PyXML is required for using the dom implementation.
+# It is also possible to use the standard minidom. But I didn't try that one.
+# Anyway the text based approach is about 60 times faster than using the full dom implementation.
+use_dom_implementation=0
+
+
+import exceptions
+if use_dom_implementation<>0:
+    try:
+        from xml.dom import implementation
+        from xml.dom.ext import PrettyPrint
+    except:
+        raise exceptions.ImportError, "PyXML is required for using the dom implementation"
+#The implementation is used for the creating the XML document.
+#The prettyprint module is used for converting the xml document object to a xml file
+
+import sys
+assert sys.version_info[0]>=2
+if sys.version_info[1]<2:
+    True=1
+    False=0
+    file=open
+    
+sys.setrecursionlimit=50
+#The recursion limit is set conservative so mistakes like s=svg() s.addElement(s)
+#won't eat up too much processor time.
+
+#the following code is pasted form xml.sax.saxutils
+#it makes it possible to run the code without the xml sax package installed
+#To make it possible to have <rubbish> in your text elements, it is necessary to escape the texts
+def _escape(data, entities={}):
+    """Escape &, <, and > in a string of data.
+
+    You can escape other strings of data by passing a dictionary as
+    the optional entities parameter.  The keys and values must all be
+    strings; each key will be replaced with its corresponding value.
+    """
+    #data = data.replace("&", "&amp;")
+    data = data.replace("<", "&lt;")
+    data = data.replace(">", "&gt;")
+    for chars, entity in entities.items():
+        data = data.replace(chars, entity)
+    return data
+
+def _quoteattr(data, entities={}):
+    """Escape and quote an attribute value.
+
+    Escape &, <, and > in a string of data, then quote it for use as
+    an attribute value.  The \" character will be escaped as well, if
+    necessary.
+
+    You can escape other strings of data by passing a dictionary as
+    the optional entities parameter.  The keys and values must all be
+    strings; each key will be replaced with its corresponding value.
+    """
+    data = _escape(data, entities)
+    if '"' in data:
+        if "'" in data:
+            data = '"%s"' % data.replace('"', "&quot;")
+        else:
+            data = "'%s'" % data
+    else:
+        data = '"%s"' % data
+    return data
+
+
+
+def _xypointlist(a):
+    """formats a list of xy pairs"""
+    s=''
+    for e in a: #this could be done more elegant
+        s+=str(e)[1:-1] +'  '
+    return s
+
+def _viewboxlist(a):
+    """formats a tuple"""
+    s=''
+    for e in a: 
+        s+=str(e)+' '
+    return s
+
+def _pointlist(a):
+    """formats a list of numbers"""
+    return str(a)[1:-1]
+
+class pathdata:
+    """class used to create a pathdata object which can be used for a path.
+    although most methods are pretty straightforward it might be useful to look at the SVG specification."""
+    #I didn't test the methods below. 
+    def __init__(self,x=None,y=None):
+        self.path=[]
+        if x is not None and y is not None:
+            self.path.append('M '+str(x)+' '+str(y))
+    def closepath(self):
+        """ends the path"""
+        self.path.append('z')
+    def move(self,x,y):
+        """move to absolute"""
+        self.path.append('M '+str(x)+' '+str(y))
+    def relmove(self,x,y):
+        """move to relative"""
+        self.path.append('m '+str(x)+' '+str(y))
+    def line(self,x,y):
+        """line to absolute"""
+        self.path.append('L '+str(x)+' '+str(y))
+    def relline(self,x,y):
+        """line to relative"""
+        self.path.append('l '+str(x)+' '+str(y))
+    def hline(self,x):
+        """horizontal line to absolute"""
+        self.path.append('H'+str(x))
+    def relhline(self,x):
+        """horizontal line to relative"""
+        self.path.append('h'+str(x))
+    def vline(self,y):
+        """verical line to absolute"""
+        self.path.append('V'+str(y))
+    def relvline(self,y):
+        """vertical line to relative"""
+        self.path.append('v'+str(y))
+    def bezier(self,x1,y1,x2,y2,x,y):
+        """bezier with xy1 and xy2 to xy absolut"""
+        self.path.append('C'+str(x1)+','+str(y1)+' '+str(x2)+','+str(y2)+' '+str(x)+','+str(y))
+    def relbezier(self,x1,y1,x2,y2,x,y):
+        """bezier with xy1 and xy2 to xy relative"""
+        self.path.append('c'+str(x1)+','+str(y1)+' '+str(x2)+','+str(y2)+' '+str(x)+','+str(y))
+    def smbezier(self,x2,y2,x,y):
+        """smooth bezier with xy2 to xy absolut"""
+        self.path.append('S'+str(x2)+','+str(y2)+' '+str(x)+','+str(y))
+    def relsmbezier(self,x2,y2,x,y):
+        """smooth bezier with xy2 to xy relative"""
+        self.path.append('s'+str(x2)+','+str(y2)+' '+str(x)+','+str(y))
+    def qbezier(self,x1,y1,x,y):
+        """quadratic bezier with xy1 to xy absolut"""
+        self.path.append('Q'+str(x1)+','+str(y1)+' '+str(x)+','+str(y))
+    def relqbezier(self,x1,y1,x,y):
+        """quadratic bezier with xy1 to xy relative"""
+        self.path.append('q'+str(x1)+','+str(y1)+' '+str(x)+','+str(y))
+    def smqbezier(self,x,y):
+        """smooth quadratic bezier to xy absolut"""
+        self.path.append('T'+str(x)+','+str(y))
+    def relsmqbezier(self,x,y):
+        """smooth quadratic bezier to xy relative"""
+        self.path.append('t'+str(x)+','+str(y))
+    def ellarc(self,rx,ry,xrot,laf,sf,x,y):
+        """elliptival arc with rx and ry rotating with xrot using large-arc-flag and sweep-flag  to xy absolut"""
+        self.path.append('A'+str(rx)+','+str(ry)+' '+str(xrot)+' '+str(laf)+' '+str(sf)+' '+str(x)+' '+str(y))
+    def relellarc(self,rx,ry,xrot,laf,sf,x,y):
+        """elliptival arc with rx and ry rotating with xrot using large-arc-flag and sweep-flag  to xy relative"""
+        self.path.append('a'+str(rx)+','+str(ry)+' '+str(xrot)+' '+str(laf)+' '+str(sf)+' '+str(x)+' '+str(y))
+    def __repr__(self):
+        return ' '.join(self.path)
+    
+
+
+      
+class SVGelement:
+    """SVGelement(type,attributes,elements,text,namespace,**args)
+    Creates a arbitrary svg element and is intended to be subclassed not used on its own.
+    This element is the base of every svg element it defines a class which resembles
+    a xml-element. The main advantage of this kind of implementation is that you don't
+    have to create a toXML method for every different graph object. Every element
+    consists of a type, attribute, optional subelements, optional text and an optional
+    namespace. Note the elements==None, if elements = None:self.elements=[] construction.
+    This is done because if you default to elements=[] every object has a reference
+    to the same empty list."""
+    def __init__(self,type='',attributes=None,elements=None,text='',namespace='',cdata=None, **args):
+        self.type=type
+        if attributes==None:
+            self.attributes={}
+        else:
+            self.attributes=attributes
+        if elements==None:
+            self.elements=[]
+        else:
+            self.elements=elements
+        self.text=text
+        self.namespace=namespace
+        self.cdata=cdata
+        for arg in args.keys():
+            arg2 = arg.replace("__", ":")
+            arg2 = arg2.replace("_", "-")
+            self.attributes[arg2]=args[arg]
+    def addElement(self,SVGelement):
+        """adds an element to a SVGelement
+
+        SVGelement.addElement(SVGelement)
+        """
+        self.elements.append(SVGelement)
+
+    def toXml(self,level,f):
+        f.write('\t'*level)
+        f.write('<'+self.type)
+        for attkey in self.attributes.keys():
+            f.write(' '+_escape(str(attkey))+'='+_quoteattr(str(self.attributes[attkey])))
+        if self.namespace:
+            f.write(' xmlns="'+ _escape(str(self.namespace))+'" xmlns:xlink="http://www.w3.org/1999/xlink"')
+        if self.elements or self.text or self.cdata:
+            f.write('>')
+        if self.elements:
+            f.write('\n')
+        for element in self.elements:
+            element.toXml(level+1,f)
+        if self.cdata:
+            f.write('\n'+'\t'*(level+1)+'<![CDATA[')
+            for line in self.cdata.splitlines():
+               f.write('\n'+'\t'*(level+2)+line)
+            f.write('\n'+'\t'*(level+1)+']]>\n')
+        if self.text:
+            if type(self.text)==type(''): #If the text is only text
+                f.write(_escape(str(self.text)))
+            else:                         #If the text is a spannedtext class
+                f.write(str(self.text))
+        if self.elements:
+            f.write('\t'*level+'</'+self.type+'>\n')
+        elif self.text: 
+            f.write('</'+self.type+'>\n')
+        elif self.cdata:
+            f.write('\t'*level+'</'+self.type+'>\n')
+        else:
+            f.write('/>\n')
+            
+class tspan(SVGelement):
+    """ts=tspan(text='',**args)
+
+    a tspan element can be used for applying formatting to a textsection
+    usage:
+    ts=tspan('this text is bold')
+    ts.attributes['font-weight']='bold'
+    st=spannedtext()
+    st.addtspan(ts)
+    t=text(3,5,st)
+    """
+    def __init__(self,text=None,**args):
+        SVGelement.__init__(self,'tspan',**args)
+        if self.text<>None:
+            self.text=text
+    def __repr__(self):
+        s="<tspan"
+        for key,value in self.attributes.items():
+         s+= ' %s="%s"' % (key,value)
+        s+='>'
+        s+=self.text
+        s+='</tspan>'
+        return s
+    
+class tref(SVGelement):
+    """tr=tref(link='',**args)
+
+    a tref element can be used for referencing text by a link to its id.
+    usage:
+    tr=tref('#linktotext')
+    st=spannedtext()
+    st.addtref(tr)
+    t=text(3,5,st)
+    """
+    def __init__(self,link,**args):
+        SVGelement.__init__(self,'tref',{'xlink:href':link},**args)
+    def __repr__(self):
+        s="<tref"
+        
+        for key,value in self.attributes.items():
+         s+= ' %s="%s"' % (key,value)
+        s+='/>'
+        return s
+    
+class spannedtext:
+    """st=spannedtext(textlist=[])
+
+    a spannedtext can be used for text which consists of text, tspan's and tref's
+    You can use it to add to a text element or path element. Don't add it directly
+    to a svg or a group element.
+    usage:
+    
+    ts=tspan('this text is bold')
+    ts.attributes['font-weight']='bold'
+    tr=tref('#linktotext')
+    tr.attributes['fill']='red'
+    st=spannedtext()
+    st.addtspan(ts)
+    st.addtref(tr)
+    st.addtext('This text is not bold')
+    t=text(3,5,st)
+    """
+    def __init__(self,textlist=None):
+        if textlist==None:
+            self.textlist=[]
+        else:
+            self.textlist=textlist
+    def addtext(self,text=''):
+        self.textlist.append(text)
+    def addtspan(self,tspan):
+        self.textlist.append(tspan)
+    def addtref(self,tref):
+        self.textlist.append(tref)
+    def __repr__(self):
+        s=""
+        for element in self.textlist:
+            s+=str(element)
+        return s
+    
+class rect(SVGelement):
+    """r=rect(width,height,x,y,fill,stroke,stroke_width,**args)
+    
+    a rectangle is defined by a width and height and a xy pair 
+    """
+    def __init__(self,x=None,y=None,width=None,height=None,fill=None,stroke=None,stroke_width=None,**args):
+        if width==None or height==None:
+            if width<>None:
+                raise ValueError, 'height is required'
+            if height<>None:
+                raise ValueError, 'width is required'
+            else:
+                raise ValueError, 'both height and width are required'
+        SVGelement.__init__(self,'rect',{'width':width,'height':height},**args)
+        if x<>None:
+            self.attributes['x']=x
+        if y<>None:
+            self.attributes['y']=y
+        if fill<>None:
+            self.attributes['fill']=fill
+        if stroke<>None:
+            self.attributes['stroke']=stroke
+        if stroke_width<>None:
+            self.attributes['stroke-width']=stroke_width
+            
+class ellipse(SVGelement):
+    """e=ellipse(rx,ry,x,y,fill,stroke,stroke_width,**args)
+
+    an ellipse is defined as a center and a x and y radius.
+    """
+    def __init__(self,cx=None,cy=None,rx=None,ry=None,fill=None,stroke=None,stroke_width=None,**args):
+        if rx==None or ry== None:
+            if rx<>None:
+                raise ValueError, 'rx is required'
+            if ry<>None:
+                raise ValueError, 'ry is required'
+            else:
+                raise ValueError, 'both rx and ry are required'
+        SVGelement.__init__(self,'ellipse',{'rx':rx,'ry':ry},**args)
+        if cx<>None:
+            self.attributes['cx']=cx
+        if cy<>None:
+            self.attributes['cy']=cy
+        if fill<>None:
+            self.attributes['fill']=fill
+        if stroke<>None:
+            self.attributes['stroke']=stroke
+        if stroke_width<>None:
+            self.attributes['stroke-width']=stroke_width
+        
+   
+class circle(SVGelement):
+    """c=circle(x,y,radius,fill,stroke,stroke_width,**args)
+
+    The circle creates an element using a x, y and radius values eg
+    """
+    def __init__(self,cx=None,cy=None,r=None,fill=None,stroke=None,stroke_width=None,**args):
+        if r==None:
+            raise ValueError, 'r is required'
+        SVGelement.__init__(self,'circle',{'r':r},**args)
+        if cx<>None:
+            self.attributes['cx']=cx
+        if cy<>None:
+            self.attributes['cy']=cy
+        if fill<>None:
+            self.attributes['fill']=fill
+        if stroke<>None:
+            self.attributes['stroke']=stroke
+        if stroke_width<>None:
+            self.attributes['stroke-width']=stroke_width
+
+class point(circle):
+    """p=point(x,y,color)
+    
+    A point is defined as a circle with a size 1 radius. It may be more efficient to use a
+    very small rectangle if you use many points because a circle is difficult to render.
+    """
+    def __init__(self,x,y,fill='black',**args):
+        circle.__init__(self,x,y,1,fill,**args)
+
+class line(SVGelement):
+    """l=line(x1,y1,x2,y2,stroke,stroke_width,**args)
+    
+    A line is defined by a begin x,y pair and an end x,y pair
+    """
+    def __init__(self,x1=None,y1=None,x2=None,y2=None,stroke=None,stroke_width=None,**args):
+        SVGelement.__init__(self,'line',**args)
+        if x1<>None:
+            self.attributes['x1']=x1
+        if y1<>None:
+            self.attributes['y1']=y1
+        if x2<>None:
+            self.attributes['x2']=x2
+        if y2<>None:
+            self.attributes['y2']=y2
+        if stroke_width<>None:
+            self.attributes['stroke-width']=stroke_width
+        if stroke<>None:
+            self.attributes['stroke']=stroke
+            
+class polyline(SVGelement):
+    """pl=polyline([[x1,y1],[x2,y2],...],fill,stroke,stroke_width,**args)
+    
+    a polyline is defined by a list of xy pairs
+    """
+    def __init__(self,points,fill=None,stroke=None,stroke_width=None,**args):
+        SVGelement.__init__(self,'polyline',{'points':_xypointlist(points)},**args)
+        if fill<>None:
+            self.attributes['fill']=fill
+        if stroke_width<>None:
+            self.attributes['stroke-width']=stroke_width
+        if stroke<>None:
+            self.attributes['stroke']=stroke
+
+class polygon(SVGelement):
+    """pl=polyline([[x1,y1],[x2,y2],...],fill,stroke,stroke_width,**args)
+    
+    a polygon is defined by a list of xy pairs
+    """
+    def __init__(self,points,fill=None,stroke=None,stroke_width=None,**args):
+        SVGelement.__init__(self,'polygon',{'points':_xypointlist(points)},**args)
+        if fill<>None:
+            self.attributes['fill']=fill
+        if stroke_width<>None:
+            self.attributes['stroke-width']=stroke_width
+        if stroke<>None:
+            self.attributes['stroke']=stroke
+
+class path(SVGelement):
+    """p=path(path,fill,stroke,stroke_width,**args)
+
+    a path is defined by a path object and optional width, stroke and fillcolor
+    """
+    def __init__(self,pathdata,fill=None,stroke=None,stroke_width=None,id=None,**args):
+        SVGelement.__init__(self,'path',{'d':str(pathdata)},**args)
+        if stroke<>None:
+            self.attributes['stroke']=stroke
+        if fill<>None:
+            self.attributes['fill']=fill
+        if stroke_width<>None:
+            self.attributes['stroke-width']=stroke_width
+        if id<>None:
+            self.attributes['id']=id
+        
+        
+class text(SVGelement):
+    """t=text(x,y,text,font_size,font_family,**args)
+    
+    a text element can bge used for displaying text on the screen
+    """
+    def __init__(self,x=None,y=None,text=None,font_size=None,font_family=None,text_anchor=None,**args):
+        SVGelement.__init__(self,'text',**args)
+        if x<>None:
+            self.attributes['x']=x
+        if y<>None:
+            self.attributes['y']=y
+        if font_size<>None:
+            self.attributes['font-size']=font_size
+        if font_family<>None:
+            self.attributes['font-family']=font_family
+        if text<>None:
+            self.text=text
+        if text_anchor<>None:
+            self.attributes['text-anchor']=text_anchor
+
+
+class textpath(SVGelement):
+    """tp=textpath(text,link,**args)
+
+    a textpath places a text on a path which is referenced by a link.   
+    """
+    def __init__(self,link,text=None,**args):
+        SVGelement.__init__(self,'textPath',{'xlink:href':link},**args)
+        if text<>None:
+            self.text=text
+
+class pattern(SVGelement):
+    """p=pattern(x,y,width,height,patternUnits,**args)
+
+    A pattern is used to fill or stroke an object using a pre-defined
+    graphic object which can be replicated ("tiled") at fixed intervals
+    in x and y to cover the areas to be painted.
+    """
+    def __init__(self,x=None,y=None,width=None,height=None,patternUnits=None,**args):
+        SVGelement.__init__(self,'pattern',**args)
+        if x<>None:
+            self.attributes['x']=x
+        if y<>None:
+            self.attributes['y']=y
+        if width<>None:
+            self.attributes['width']=width
+        if height<>None:
+            self.attributes['height']=height
+        if patternUnits<>None:
+            self.attributes['patternUnits']=patternUnits
+
+class title(SVGelement):
+    """t=title(text,**args)
+    
+    a title is a text element. The text is displayed in the title bar
+    add at least one to the root svg element
+    """
+    def __init__(self,text=None,**args):
+        SVGelement.__init__(self,'title',**args)
+        if text<>None:
+            self.text=text
+
+class description(SVGelement):
+    """d=description(text,**args)
+    
+    a description can be added to any element and is used for a tooltip
+    Add this element before adding other elements.
+    """
+    def __init__(self,text=None,**args):
+        SVGelement.__init__(self,'desc',**args)
+        if text<>None:
+            self.text=text
+
+class lineargradient(SVGelement):
+    """lg=lineargradient(x1,y1,x2,y2,id,**args)
+
+    defines a lineargradient using two xy pairs.
+    stop elements van be added to define the gradient colors.
+    """
+    def __init__(self,x1=None,y1=None,x2=None,y2=None,id=None,**args):
+        SVGelement.__init__(self,'linearGradient',**args)
+        if x1<>None:
+            self.attributes['x1']=x1
+        if y1<>None:
+            self.attributes['y1']=y1
+        if x2<>None:
+            self.attributes['x2']=x2
+        if y2<>None:
+            self.attributes['y2']=y2
+        if id<>None:
+            self.attributes['id']=id
+
+class radialgradient(SVGelement):
+    """rg=radialgradient(cx,cy,r,fx,fy,id,**args)
+
+    defines a radial gradient using a outer circle which are defined by a cx,cy and r and by using a focalpoint.
+    stop elements van be added to define the gradient colors.
+    """
+    def __init__(self,cx=None,cy=None,r=None,fx=None,fy=None,id=None,**args):
+        SVGelement.__init__(self,'radialGradient',**args)
+        if cx<>None:
+            self.attributes['cx']=cx
+        if cy<>None:
+            self.attributes['cy']=cy
+        if r<>None:
+            self.attributes['r']=r
+        if fx<>None:
+            self.attributes['fx']=fx
+        if fy<>None:
+            self.attributes['fy']=fy
+        if id<>None:
+            self.attributes['id']=id
+            
+class stop(SVGelement):
+    """st=stop(offset,stop_color,**args)
+
+    Puts a stop color at the specified radius
+    """
+    def __init__(self,offset,stop_color=None,**args):
+        SVGelement.__init__(self,'stop',{'offset':offset},**args)
+        if stop_color<>None:
+            self.attributes['stop-color']=stop_color
+            
+class style(SVGelement):
+    """st=style(type,cdata=None,**args)
+
+    Add a CDATA element to this element for defing in line stylesheets etc..
+    """
+    def __init__(self,type,cdata=None,**args):
+        SVGelement.__init__(self,'style',{'type':type},cdata=cdata, **args)
+        
+            
+class image(SVGelement):
+    """im=image(url,width,height,x,y,**args)
+
+    adds an image to the drawing. Supported formats are .png, .jpg and .svg.
+    """
+    def __init__(self,url,x=None,y=None,width=None,height=None,**args):
+        if width==None or height==None:
+            if width<>None:
+                raise ValueError, 'height is required'
+            if height<>None:
+                raise ValueError, 'width is required'
+            else:
+                raise ValueError, 'both height and width are required'
+        SVGelement.__init__(self,'image',{'xlink:href':url,'width':width,'height':height},**args)
+        if x<>None:
+            self.attributes['x']=x
+        if y<>None:
+            self.attributes['y']=y
+ 
+class cursor(SVGelement):
+    """c=cursor(url,**args)
+
+    defines a custom cursor for a element or a drawing
+    """
+    def __init__(self,url,**args):
+        SVGelement.__init__(self,'cursor',{'xlink:href':url},**args)
+
+    
+class marker(SVGelement):
+    """m=marker(id,viewbox,refX,refY,markerWidth,markerHeight,**args)
+    
+    defines a marker which can be used as an endpoint for a line or other pathtypes
+    add an element to it which should be used as a marker.
+    """
+    def __init__(self,id=None,viewBox=None,refx=None,refy=None,markerWidth=None,markerHeight=None,**args):
+        SVGelement.__init__(self,'marker',**args)
+        if id<>None:
+            self.attributes['id']=id
+        if viewBox<>None:
+            self.attributes['viewBox']=_viewboxlist(viewBox)
+        if refx<>None:
+            self.attributes['refX']=refx
+        if refy<>None:
+            self.attributes['refY']=refy
+        if markerWidth<>None:
+            self.attributes['markerWidth']=markerWidth
+        if markerHeight<>None:
+            self.attributes['markerHeight']=markerHeight
+        
+class group(SVGelement):
+    """g=group(id,**args)
+    
+    a group is defined by an id and is used to contain elements
+    g.addElement(SVGelement)
+    """
+    def __init__(self,id=None,**args):
+        SVGelement.__init__(self,'g',**args)
+        if id<>None:
+            self.attributes['id']=id
+
+class symbol(SVGelement):
+    """sy=symbol(id,viewbox,**args)
+
+    defines a symbol which can be used on different places in your graph using
+    the use element. A symbol is not rendered but you can use 'use' elements to
+    display it by referencing its id.
+    sy.addElement(SVGelement)
+    """
+    
+    def __init__(self,id=None,viewBox=None,**args):
+        SVGelement.__init__(self,'symbol',**args)
+        if id<>None:
+            self.attributes['id']=id
+        if viewBox<>None:
+            self.attributes['viewBox']=_viewboxlist(viewBox)
+            
+class defs(SVGelement):
+    """d=defs(**args)
+
+    container for defining elements
+    """
+    def __init__(self,**args):
+        SVGelement.__init__(self,'defs',**args)
+
+class switch(SVGelement):
+    """sw=switch(**args)
+
+    Elements added to a switch element which are "switched" by the attributes
+    requiredFeatures, requiredExtensions and systemLanguage.
+    Refer to the SVG specification for details.
+    """
+    def __init__(self,**args):
+        SVGelement.__init__(self,'switch',**args)
+
+        
+class use(SVGelement):
+    """u=use(link,x,y,width,height,**args)
+    
+    references a symbol by linking to its id and its position, height and width
+    """
+    def __init__(self,link,x=None,y=None,width=None,height=None,**args):
+        SVGelement.__init__(self,'use',{'xlink:href':link},**args)
+        if x<>None:
+            self.attributes['x']=x
+        if y<>None:
+            self.attributes['y']=y
+
+        if width<>None:
+            self.attributes['width']=width
+        if height<>None:
+            self.attributes['height']=height
+            
+            
+class link(SVGelement):
+    """a=link(url,**args)
+
+    a link  is defined by a hyperlink. add elements which have to be linked
+    a.addElement(SVGelement)
+    """
+    def __init__(self,link='',**args):
+        SVGelement.__init__(self,'a',{'xlink:href':link},**args)
+        
+class view(SVGelement):
+    """v=view(id,**args)
+
+    a view can be used to create a view with different attributes"""
+    def __init__(self,id=None,**args):
+        SVGelement.__init__(self,'view',**args)
+        if id<>None:
+            self.attributes['id']=id
+
+class script(SVGelement):
+    """sc=script(type,type,cdata,**args)
+
+    adds a script element which contains CDATA to the SVG drawing
+
+    """
+    def __init__(self,type,cdata=None,**args):
+        SVGelement.__init__(self,'script',{'type':type},cdata=cdata,**args)
+        
+class animate(SVGelement):
+    """an=animate(attribute,from,to,during,**args)
+
+    animates an attribute.    
+    """
+    def __init__(self,attribute,fr=None,to=None,dur=None,**args):
+        SVGelement.__init__(self,'animate',{'attributeName':attribute},**args)
+        if fr<>None:
+            self.attributes['from']=fr
+        if to<>None:
+            self.attributes['to']=to
+        if dur<>None:
+            self.attributes['dur']=dur
+        
+class animateMotion(SVGelement):
+    """an=animateMotion(pathdata,dur,**args)
+
+    animates a SVGelement over the given path in dur seconds
+    """
+    def __init__(self,pathdata,dur,**args):
+        SVGelement.__init__(self,'animateMotion',**args)
+        if pathdata<>None:
+            self.attributes['path']=str(pathdata)
+        if dur<>None:
+            self.attributes['dur']=dur
+
+class animateTransform(SVGelement):
+    """antr=animateTransform(type,from,to,dur,**args)
+    
+    transform an element from and to a value.
+    """
+    def __init__(self,type=None,fr=None,to=None,dur=None,**args):
+        SVGelement.__init__(self,'animateTransform',{'attributeName':'transform'},**args)
+        #As far as I know the attributeName is always transform
+        if type<>None:
+            self.attributes['type']=type
+        if fr<>None:
+            self.attributes['from']=fr
+        if to<>None:
+            self.attributes['to']=to
+        if dur<>None:
+            self.attributes['dur']=dur
+class animateColor(SVGelement):
+    """ac=animateColor(attribute,type,from,to,dur,**args)
+
+    Animates the color of a element
+    """
+    def __init__(self,attribute,type=None,fr=None,to=None,dur=None,**args):
+        SVGelement.__init__(self,'animateColor',{'attributeName':attribute},**args)
+        if type<>None:
+            self.attributes['type']=type
+        if fr<>None:
+            self.attributes['from']=fr
+        if to<>None:
+            self.attributes['to']=to
+        if dur<>None:
+            self.attributes['dur']=dur        
+class set(SVGelement):
+    """st=set(attribute,to,during,**args)
+    
+    sets an attribute to a value for a
+    """
+    def __init__(self,attribute,to=None,dur=None,**args):
+        SVGelement.__init__(self,'set',{'attributeName':attribute},**args)
+        if to<>None:
+            self.attributes['to']=to
+        if dur<>None:
+            self.attributes['dur']=dur
+
+
+            
+class svg(SVGelement):
+    """s=svg(viewbox,width,height,**args)
+    
+    a svg or element is the root of a drawing add all elements to a svg element.
+    You can have different svg elements in one svg file
+    s.addElement(SVGelement)
+
+    eg
+    d=drawing()
+    s=svg((0,0,100,100),'100%','100%')
+    c=circle(50,50,20)
+    s.addElement(c)
+    d.setSVG(s)
+    d.toXml()
+    """
+    def __init__(self,viewBox=None, width=None, height=None,**args):
+        SVGelement.__init__(self,'svg',**args)
+        if viewBox<>None:
+            self.attributes['viewBox']=_viewboxlist(viewBox)
+        if width<>None:
+            self.attributes['width']=width
+        if height<>None:
+            self.attributes['height']=height
+        self.namespace="http://www.w3.org/2000/svg"
+        
+class drawing:
+    """d=drawing()
+
+    this is the actual SVG document. It needs a svg element as a root.
+    Use the addSVG method to set the svg to the root. Use the toXml method to write the SVG
+    source to the screen or to a file
+    d=drawing()
+    d.addSVG(svg)
+    d.toXml(optionalfilename)
+    """
+
+    def __init__(self, entity={}):
+        self.svg=None
+        self.entity = entity
+    def setSVG(self,svg):
+        self.svg=svg
+        #Voeg een element toe aan de grafiek toe.
+    if use_dom_implementation==0:      
+        def toXml(self, filename='',compress=False):
+            import cStringIO
+            xml=cStringIO.StringIO()
+            xml.write("<?xml version='1.0' encoding='UTF-8'?>\n")
+            xml.write("<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\" \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\"")
+            if self.entity:
+            	xml.write(" [\n")
+            	for item in self.entity.keys():
+            		xml.write("<!ENTITY %s \"%s\">\n" % (item, self.entity[item]))
+            	xml.write("]")
+            xml.write(">\n")
+            self.svg.toXml(0,xml)
+            if not filename:
+                if compress:
+                    import gzip
+                    f=cStringIO.StringIO()
+                    zf=gzip.GzipFile(fileobj=f,mode='wb')
+                    zf.write(xml.getvalue())
+                    zf.close()
+                    f.seek(0)
+                    return f.read()
+                else:
+                    return xml.getvalue()
+            else:
+                if filename[-4:]=='svgz':
+                    import gzip
+                    f=gzip.GzipFile(filename=filename,mode="wb", compresslevel=9)
+                    f.write(xml.getvalue())
+                    f.close()
+                else:
+                    f=file(filename,'w')
+                    f.write(xml.getvalue())
+                    f.close()
+
+    else:
+        def toXml(self,filename='',compress=False):
+            """drawing.toXml()        ---->to the screen
+            drawing.toXml(filename)---->to the file
+            writes a svg drawing to the screen or to a file
+            compresses if filename ends with svgz or if compress is true
+            """
+            doctype = implementation.createDocumentType('svg',"-//W3C//DTD SVG 1.0//EN""",'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd ')
+        
+            global root
+            #root is defined global so it can be used by the appender. Its also possible to use it as an arugument but
+            #that is a bit messy.
+            root=implementation.createDocument(None,None,doctype)
+            #Create the xml document.
+            global appender
+            def appender(element,elementroot):
+                """This recursive function appends elements to an element and sets the attributes
+                and type. It stops when alle elements have been appended"""
+                if element.namespace:
+                    e=root.createElementNS(element.namespace,element.type)
+                else:
+                    e=root.createElement(element.type)
+                if element.text:
+                    textnode=root.createTextNode(element.text)
+                    e.appendChild(textnode)
+                for attribute in element.attributes.keys():   #in element.attributes is supported from python 2.2
+                    e.setAttribute(attribute,str(element.attributes[attribute]))
+                if element.elements:
+                    for el in element.elements:
+                        e=appender(el,e)
+                elementroot.appendChild(e)
+                return elementroot
+            root=appender(self.svg,root)
+            if not filename:
+                import cStringIO
+                xml=cStringIO.StringIO()
+                PrettyPrint(root,xml)
+                if compress:
+                    import gzip
+                    f=cStringIO.StringIO()
+                    zf=gzip.GzipFile(fileobj=f,mode='wb')
+                    zf.write(xml.getvalue())
+                    zf.close()
+                    f.seek(0)
+                    return f.read()
+                else:
+                    return xml.getvalue()
+            else:
+                try:
+                    if filename[-4:]=='svgz':
+                        import gzip
+                        import cStringIO
+                        xml=cStringIO.StringIO()
+                        PrettyPrint(root,xml)
+                        f=gzip.GzipFile(filename=filename,mode='wb',compresslevel=9)
+                        f.write(xml.getvalue())
+                        f.close()
+                    else:
+                        f=open(filename,'w')
+                        PrettyPrint(root,f)
+                        f.close()
+                except:
+                    print "Cannot write SVG file: " + filename
+    def validate(self):
+        try:
+            import xml.parsers.xmlproc.xmlval
+        except:
+            raise exceptions.ImportError,'PyXml is required for validating SVG'
+        svg=self.toXml()
+        xv=xml.parsers.xmlproc.xmlval.XMLValidator()
+        try:
+            xv.feed(svg)
+        except:
+            raise "SVG is not well formed, see messages above"
+        else:
+            print "SVG well formed"
+if __name__=='__main__':
+
+    
+    d=drawing()
+    s=svg((0,0,100,100))
+    r=rect(-100,-100,300,300,'cyan')
+    s.addElement(r)
+    
+    t=title('SVGdraw Demo')
+    s.addElement(t)
+    g=group('animations')
+    e=ellipse(0,0,5,2)
+    g.addElement(e)
+    c=circle(0,0,1,'red')
+    g.addElement(c)
+    pd=pathdata(0,-10)
+    for i in range(6):
+        pd.relsmbezier(10,5,0,10)
+        pd.relsmbezier(-10,5,0,10)
+    an=animateMotion(pd,10)
+    an.attributes['rotate']='auto-reverse'
+    an.attributes['repeatCount']="indefinite"
+    g.addElement(an)
+    s.addElement(g)
+    for i in range(20,120,20):
+        u=use('#animations',i,0)
+        s.addElement(u)
+    for i in range(0,120,20):
+        for j in range(5,105,10):
+            c=circle(i,j,1,'red','black',.5)
+            s.addElement(c)
+    d.setSVG(s)
+     
+    print d.toXml()
+
diff --git a/web/webqtl/utility/webqtlUtil.py b/web/webqtl/utility/webqtlUtil.py
new file mode 100755
index 00000000..6af7f846
--- /dev/null
+++ b/web/webqtl/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
+