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