Add dynamic graphing capability to TKO.

Signed-off-by: Martin J. Bligh <mbligh@google.com>



git-svn-id: http://test.kernel.org/svn/autotest/trunk@680 592f7852-d20e-0410-864c-8624ca9c26a4
diff --git a/tko/frontend.py b/tko/frontend.py
index 85124c2..55ec215 100755
--- a/tko/frontend.py
+++ b/tko/frontend.py
@@ -42,18 +42,24 @@
 
 	def __init__(self, db, test_idx, job_idx, testname, subdir, kernel_idx, status_num, reason, machine):
 		self.idx = test_idx
-		self.job = None 
 		self.job = job(db, job_idx)
 		# self.machine = self.job.machine
 		self.test = testname
 		self.subdir = subdir
-		self.kernel = None
-		# self.kernel = kernel.select(db, {'kernel_idx' : kernel_idx})
+		self.kernel = kernel.select(db, {'kernel_idx' : kernel_idx})[0]
 		self.status_num = status_num
 		self.status_word = db.status_word[status_num]
 		self.reason = reason
 		self.url = html_root + self.job.tag + '/' + self.subdir
-		
+
+		self.iterations = {}
+		# Create a dictionary - dict{key} = [value1, value2, ....]
+		for i in iteration.select(db, {'test_idx' : test_idx}):
+			if self.iterations.has_key(i.key):
+				self.iterations[i.key].append(i.value)
+			else:
+				self.iterations[i.key] = [i.value]
+				
 
 class job:
 	def __init__(self, db, job_idx):
@@ -64,12 +70,22 @@
 		(self.tag, self.machine) = rows[0]
 
  
+class iteration:
+	@classmethod
+	def select(klass, db, where):
+		fields = ['iteration', 'attribute', 'value']
+		iterations = []
+		rows = db.select(','.join(fields), 'iteration_result', where)
+		for row in rows:
+			iterations.append(klass(*row))
+		return iterations
+
+
+	def __init__(self, iteration, key, value):
+ 		self.iteration = iteration
+		self.key = key
+		self.value = value
+
 # class patch:
 # 	def __init__(self):
 # 		self.spec = None
-# 
-# 
-# class iteration:
-# 	def __init__(self):
-# 		self.a = None
-# 
diff --git a/tko/machine_test_attribute_graph.cgi b/tko/machine_test_attribute_graph.cgi
index 75fb9c8..8bbe51e 100755
--- a/tko/machine_test_attribute_graph.cgi
+++ b/tko/machine_test_attribute_graph.cgi
@@ -16,20 +16,27 @@
 db = db.db()
 
 def main():
-#	form = cgi.FieldStorage()
-#	if not form.has_key("machine") and form.has_key("kernel"):
-#		raise
-#
-#	machine = form["machine"].value
-#	kernel_version = form["kernel"].value
-#	draw_graph("A test graph", "Kernel", "Elapsed time (seconds)")
-	print "Content-type: text/html\n"
-	sys.stdout.flush()
-	machine = 'cagg4'
-	where = { 'subdir' : 'kernbench', 'machine' : machine }
-	tests = frontend.test.select(db, where)
-	kernels.sort(key = kernel_encode)
+	form = cgi.FieldStorage()
+	machine = form["machine"].value
+	benchmark = form["benchmark"].value
+	key = form["key"].value
 
+	data = {}
+	where = { 'subdir' : benchmark, 'machine' : machine }
+	for test in frontend.test.select(db, where):
+		if test.iterations.has_key(key):
+			data[test.kernel.printable] = test.iterations[key]
+
+	# for kernel in sort_kernels(data.keys()):
+	#	print "%s %s" % (kernel, str(data[kernel]))
+	title = "%s on %s" % (benchmark, machine)
+	graph = plotgraph.gnuplot(title, 'Kernel', key, xsort = sort_kernels)
+	graph.add_dataset('all kernels', data)
+	graph.plot(cgi_header = True)
+
+
+def sort_kernels(kernels):
+	return sorted(kernels, key = kernel_versions.version_encode)
 
 
 def draw_graph(title, xlabel, ylabel):
diff --git a/tko/plotgraph.py b/tko/plotgraph.py
index 579af8b..a7de429 100755
--- a/tko/plotgraph.py
+++ b/tko/plotgraph.py
@@ -7,33 +7,53 @@
 """
 
 import subprocess, sys, os
+from math import sqrt
 Popen = subprocess.Popen
 
+def avg_dev(values):
+	if len(values) == 0:
+		return (0,0)
+	average = sum(values) / len(values)
+	sum_sq_dev = sum( [(x - average) ** 2 for x in values] )
+        std_dev = sqrt(sum_sq_dev / float(len(values)));
+        return (average, std_dev);
+
+
 class gnuplot:
-	def __init__(self, title, xlabel, ylabel):
+	def __init__(self, title, xlabel, ylabel, xsort = sorted):
 		self.title = title
 		self.xlabel = xlabel
 		self.ylabel = ylabel
 		self.data_titles = []
 		self.datasets = []
+		self.xsort = xsort
+		self.xvalues = set([])
 
 
-	def set_xlabels(self, labels):
+	def xtics(self):
 		count = 1
-		self.xtics = []
-		for label in labels:
-			self.xtics.append('"%s" %d' % (label, count))
+		tics = []
+		for label in self.xsort(self.xlabels):
+			tics.append('"%s" %d' % (label, count))
 			count += 1
+		return tics
 
 
-	def add_dataset(self, title, values):
+	def add_dataset(self, title, labeled_values):
 		"""
 		Add a data line
 
-		For yerrorbars, values should be "<value> <error>" as a string
+		title: title of the dataset
+		labeled_values: dictionary of lists
+				{ label : [value1, value2, ... ] , ... }
 		"""
 		self.data_titles.append(title)
-		self.datasets.append(values)
+		data_points = {}
+		for label in labeled_values:
+			point = "%s %s" % avg_dev(labeled_values[label])
+			data_points[label] = point
+			self.xvalues.add(label)
+		self.datasets.append(data_points)
 
 
 	def plot(self, cgi_header = False, output = None, test = None):
@@ -55,16 +75,19 @@
 		g.write('set style data yerrorlines\n')
 		g.write('set grid\n')
 
-		g.write('set xtics rotate (%s)\n' % ','.join(self.xtics))
+		self.xlabels = self.xsort(list(self.xvalues))
+		g.write('set xtics rotate (%s)\n' % ','.join(self.xtics()))
 
 		plot_lines = ['"-" title "%s"' % t for t in self.data_titles]
 		g.write('plot ' + ', '.join(plot_lines) + '\n')
 
 		for dataset in self.datasets:
 			count = 1
-			for data in dataset:
+			for label in self.xlabels:
+				data = dataset[label]
 				g.write("%d %s\n" % (count, str(data)))
 				count += 1
+			sys.stdout.flush()
 			g.write('e\n')
 
 		g.close()