Link to pdf graphs for easier inclusion in the report (pdflatex)
[can-benchmark.git] / gw-tests / genhtml / genhtml.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 import os;
4 import dircache;
5 import sys;
6 import urllib
7 import traceback
8
9 class DimValue(object):
10     def __new__(cls, dim, value):
11         if value in dim:
12             return dim[value]
13         else:
14             return super(DimValue, cls).__new__(cls)
15     def __init__(self, dim, value):
16         self.dim = dim
17         self.value = value
18         self.dim.addValue(self)
19     def __str__(self):
20         return self.dim.val2str(self.value)
21     def __repr__(self):
22         return "DimValue(%s, %s)" % (repr(self.dim), repr(self.value))
23     def htmlTableHeading(self):
24         return self.dim.htmlTableHeading(self.value)
25
26 class DimValues(list):
27     def replace(self, val):
28         for i in xrange(len(self)):
29             if self[i].dim == val.dim:
30                 self[i] = val
31     def __add__(self, val):
32         ret = DimValues(self)
33         ret.append(val)
34         return ret
35     def __sub__(self, dim):
36         result = DimValues(self)
37         for v in self:
38             if v.dim == dim:
39                 result.remove(v)
40         return result
41     def key(self):
42         return tuple([v.value for v in self])
43
44 class Dimension(dict):
45     def __init__(self, atype, name=None):
46         self.type = atype
47         if (name):
48             self.name = name
49         else:
50             self.name = atype
51         self.sortedKeys = []
52
53     def __iter__(self):
54         for i in xrange(len(self)):
55             yield self.getValue(i)
56     def getValue(self, index):
57         return self[self.sortedKeys[index]]
58
59     def addValue(self, value):
60         if value not in self:
61             if isinstance(value, DimValue):
62                 self[value.value] = value
63             else:
64                 raise Exception("Unsupported usage of addValue")
65                 #self[value] = DimValue(self, value)
66             self.sortedKeys = self.keys()
67             self.sortedKeys.sort()
68     def val2str(self, v):
69         return str(v)
70     def htmlTableHeading(self, v):
71         return self.val2str(v)
72     def __str__(self):
73         return self.name
74     def __repr__(self):
75         return "Dimension(%s)"%self.type
76
77 class DimensionKern(Dimension):
78     def __init__(self):
79         Dimension.__init__(self, 'gwkern', 'GW kernel')
80     def htmlTableHeading(self, v):
81         i=v.find(":")
82         if i>0: kver=v[:i]
83         else: kver=v
84         return v+"<br><a href='config-%s'>config</a>"%(urllib.quote(kver))
85     def versions(self):
86         for v in self.values:
87             i=v.find(":")
88             if i>0: kver=v[:i]
89             else: kver=v
90             yield kver
91
92 class DimensionHostKern(Dimension):
93     def __init__(self):
94         Dimension.__init__(self, 'hostkern', 'Host kernel')
95     def val2str(self, v):
96         if v.find("host-") == 0:
97             v = v[5:]
98         return v
99     def htmlTableHeading(self, v):
100         v = self.val2str(v)
101         i = v.find(":")
102         if i>0: kver = v[:i]
103         else: kver = v
104         return v+"<br><a href='config-%s'>config</a>"%(urllib.quote(kver))
105     def versions(self):
106         for v in self.values:
107             i=v.find(":")
108             if i>0: kver=v[:i]
109             else: kver=v
110             yield kver
111
112 class DimensionTest(Dimension):
113     def __init__(self):
114         Dimension.__init__(self, 'test', 'Test')
115     def htmlTableHeading(self, v):
116         return v+"<br><a href='%s.sh.html'>source</a>"%(urllib.quote(v))
117
118 class DimensionLoad(Dimension):
119     def __init__(self):
120         Dimension.__init__(self, 'load', 'Load')
121
122 class DimensionTraffic(Dimension):
123     def __init__(self):
124         Dimension.__init__(self, 'traf', 'Traffic')
125     def val2str(self, v):
126         if v == "50":
127             return "50%"
128         elif v == "oneatatime":
129             return "one message at a time"
130         else:
131             return v
132     def htmlTableHeading(self, v):
133         return self.val2str(v)
134 class Test(object):
135     @classmethod
136     def isOnPath(cls, path):
137         f = os.path.join(path, 'plot.sh')
138         return os.path.isfile(f)
139     def __init__(self, path, values, tests=None):
140         self.path = path
141         self.name = os.path.basename(path)
142         self.values = values
143         self.tests = tests
144     def printThumbLink(self, file):
145         thumb = self.path+'/thumb'
146         try:
147             imgs = [img for img in dircache.listdir(thumb)]
148         except OSError:
149             imgs = [ self.name + ".png" ]
150         for img in imgs:
151             print >>file, "<a href='%s/results.html'><img src='%s/thumb/%s'></a>" % \
152                   (urllib.quote(self.path), urllib.quote(self.path), img)
153     def generateHtml(self):
154         html = open(os.path.join(self.path, 'results.html'), "w")
155         title = "CAN gateway timing analysis"
156         cdup = "../"*len(self.values)
157         print >> html, """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
158 <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
159 <head>
160 <meta http-equiv="content-type" content="text/html; charset=utf-8" />
161 <title>%s</title>
162 <link rel="stylesheet" href="%sstyle.css" type="text/css" /> 
163 </head>
164 <body>
165 <h1>%s</h1>"""  % (title, cdup, title)
166         params = ["%s %s" % (v.dim, v) for v in self.values]
167         print >>html, "Results for:", ", ".join(params)
168         print >>html, "<div class='otherview'><h4>Other results</h4><table>"
169         for d in self.tests.space:
170             links = []
171             for v in d:
172                 if v in self.values:
173                     links.append("<span class='value current'>%s</span>"%str(v))
174                 else:
175                     vv = DimValues(self.values)
176                     vv.replace(v)
177                     try:
178                         href = cdup + urllib.quote(self.tests[vv.key()].path+"/results.html")
179                         links.append("<span class='value other'><a href='%s'>%s</a></span>"%(href, str(v)))
180                     except KeyError:
181                         links.append("<span class='value missing'>%s</span>"%str(v))
182             print >>html, "<tr><th>%s</th><td>" % d, " ".join(links), "</td></tr>"
183
184         print >>html, "</table></div>"
185         print >>html, "<div><a href='%s'><img src='%s' /></a></div>" % (self.name+".pdf", self.name+".png")
186         print >>html, "<a href='./'>Raw data</a><br />"
187         print >>html, "<a href='%s'>Script source</a><br />" % (cdup+self.name+".sh.html")
188         print >>html, "<a href='%s'>Back to top</a><br />" % cdup
189         
190         html.close()
191
192 class Space(list):
193     """List of Dimensions()s (order matters)"""
194     def __init__(self, *dimensions):
195         self.extend(list(dimensions))
196     def path2dimValues(self, path):
197         coordinates = path.split("/")
198         if len(coordinates) != len(self):
199             raise KeyError("The number coordinates do not match the number of dimensions: " + str(coordinates))
200         
201         dv = DimValues([DimValue(self[i], coordinates[i]) \
202                             for i in xrange(len(coordinates))])
203         return dv
204
205     def iterValues(self):
206         idx = [0 for i in xrange(len(self))]
207         done=False
208         while not done:
209             values=DimValues()
210             for i in xrange(len(self)):
211                 values.append(self[i].values()[idx[i]])
212             yield values
213             done=True
214             for i in xrange(len(self)):
215                 idx[i] += 1
216                 if idx[i] < len(self[i]):
217                     done=False
218                     break
219                 idx[i] = 0
220     def reorder(self, dimValues):
221         reordered = DimValues()
222         for d in self:
223             for v in dimValues:
224                 if v.dim == d:
225                     reordered.append(v)
226         return reordered
227     def iterDimensionPairs(self):
228         for i in xrange(len(self)):
229             for j in xrange(i+1, len(self)):
230                 yield (self[i], self[j])
231                 yield (self[j], self[i])
232     def iterRemainingDimensions(self, dimensionPair):
233         for d in self:
234             if d not in dimensionPair:
235                 yield d
236
237
238 class Tests(dict):
239     """Represents all tests organized along several dimensions"""
240     def __init__(self, rootpath, space):
241         dict.__init__(self)
242         self.space = space
243         if (rootpath):
244             self.populate(rootpath)
245     def getTest(self, key):
246         if len(key) != len(self.space):
247             raise KeyError("The coordinates in key do not match the dimension of the space")
248         realkey = self.space.reorder(key)
249         return self[realkey.key()]
250
251     def addTest(self, test):
252         self[test.values.key()] = test
253         
254     def populate(self, rootpath):
255         for root, dirs, files in os.walk(rootpath):
256             if (root.find(rootpath) == 0):
257                 path = root[len(rootpath):]
258             else:
259                 path = rootpath
260             if Test.isOnPath(root):
261                 dv = self.space.path2dimValues(path)
262                 self.addTest(Test(root, dv, self))
263     def generateHtml(self):
264         for pair in self.space.iterDimensionPairs():
265             remDims = Space(*tuple([d for d in self.space.iterRemainingDimensions(pair)]))
266             for vals in remDims.iterValues():
267                 page = Page(pair, vals, self)
268                 print page.getName()
269                 page.generate()
270         try:
271             os.remove("index.html")
272         except OSError: pass
273         os.symlink(page.getName(), "index.html")
274         css = open("style.css", "w")
275         print >>css, """img { border: 0; }
276 table { border-collapse: collapse; }
277 th, td { border: 1px solid lightgray; padding: 4px;}
278 h4 { margin: 0; }
279 .otherview { margin: 1ex 0}
280 .otherview .value { color: black; padding: 0ex 1ex; -moz-border-radius: 1ex; border-radius: 1ex;}
281 .otherview .value a { color: inherit; text-decoration: none; }
282 .otherview .other:hover { background: #eee; }
283 .otherview .missing { color: gray; }
284 .otherview .current { background: #ccc; }
285 """
286         css.close()
287         for test in self.values():
288             print test.path
289             test.generateHtml()
290
291         os.system("source-highlight -d --output-dir=. ../*.sh > /dev/null")
292
293 class Page(object):
294     def __init__(self, dimPair, valsOther, tests):
295         self.dimy, self.dimx = dimPair
296         self.dimOther = [v.dim for v in valsOther]
297         self.valsOther = tests.space.reorder(valsOther)
298         self.tests = tests
299     def getName(self):
300         return "%s-vs-%s-for-%s.html"%(self.dimy.type, self.dimx.type,
301                                        "-".join([v.value for v in self.valsOther]))
302     def generate(self):
303         html = open(self.getName(), "w")
304         title = "CAN gateway timing analysis" 
305         print >> html, """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
306 <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
307 <head>
308 <meta http-equiv="content-type" content="text/html; charset=utf-8" />
309 <title>%s</title>
310 <link rel="stylesheet" href="style.css" type="text/css" /> 
311 </head>
312 <body>
313 <h1>%s</h1>"""  % (title, title)
314         params = ["%s %s" % (v.dim, v) for v in self.valsOther]
315         print >>html, "<h3>Results for ", ", ".join(params), "</h3>"
316         print >>html, "<div class='otherview'><h4>Other views</h4>"
317         print >>html, "<table><tr>"
318         for d in self.dimOther:
319             print >>html, "<th>%s</th>" % d
320             print >>html, "<td><a href='%s'>&rarr;</a> " % \
321                 Page((self.dimy, d), self.valsOther - d + self.dimx.getValue(0), self.tests).getName()
322             print >>html, "<a href='%s'>&darr;</a></td>" % \
323                 Page((d, self.dimx), self.valsOther - d + self.dimy.getValue(0), self.tests).getName()
324             links = []
325             print >>html, "<td>"
326             for v in d:
327                 if v in self.valsOther:
328                     links.append("<span class='value current'>%s</span>"%str(v))
329                 else:
330                     vv = DimValues(self.valsOther)
331                     vv.replace(v)
332                     links.append("<span class='value other'><a href='%s'>%s</a></span>"%(urllib.quote(Page((self.dimy, self.dimx), vv, self.tests).getName()), str(v)))
333             print >>html, " ".join(links)
334             print >>html, "</td></tr>"
335         print >>html, "</table></div>"
336
337         print >>html, "<table><thead><tr><td>%s &rarr; <br />%s &darr;</td>" % (self.dimx.name, self.dimy.name)
338         for x in self.dimx:
339             print >>html, "<th>%s</th>" % x.htmlTableHeading()
340         print >>html, "</tr></thead>"
341         for y in self.dimy:
342             print >>html, "<tr><th>%s</th>" % y.htmlTableHeading()
343
344             for x in self.dimx:
345                 print >>html, "<td>"
346                 idx = [x,y]
347                 idx.extend(self.valsOther)
348                 try:
349                     test = tests.getTest(idx)
350                     test.printThumbLink(html)
351                 except KeyError:
352                     print >>html, "N/A"
353                 print >>html, "</td>"
354             print >>html, "</tr>"
355         print >> html, """
356 </table>
357 <div style="font-size: small; color: gray; margin-top: 1em;">Authors: Michal Sojka, Pavel Píša, Copyright © 2010 Czech Technical University in Prague</div>
358 </body>
359 """
360
361
362 if __name__ == "__main__":
363     os.chdir(sys.argv[1])
364     os.system("rm *.html")
365     tests = Tests("./", Space(DimensionHostKern(), DimensionKern(), DimensionTraffic(), DimensionLoad(), DimensionTest()))
366     tests.generateHtml()
367     sys.exit(0)