table_formatter.py 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818
  1. #!/usr/bin/env python
  2. from __future__ import print_function
  3. import sys, re, os.path, stat, math
  4. try:
  5. from html import escape
  6. except ImportError:
  7. from cgi import escape # Python 2.7
  8. from optparse import OptionParser
  9. from color import getColorizer, dummyColorizer
  10. class tblCell(object):
  11. def __init__(self, text, value = None, props = None):
  12. self.text = text
  13. self.value = value
  14. self.props = props
  15. class tblColumn(object):
  16. def __init__(self, caption, title = None, props = None):
  17. self.text = caption
  18. self.title = title
  19. self.props = props
  20. class tblRow(object):
  21. def __init__(self, colsNum, props = None):
  22. self.cells = [None] * colsNum
  23. self.props = props
  24. def htmlEncode(str):
  25. return '<br/>'.join([escape(s) for s in str])
  26. class table(object):
  27. def_align = "left"
  28. def_valign = "middle"
  29. def_color = None
  30. def_colspan = 1
  31. def_rowspan = 1
  32. def_bold = False
  33. def_italic = False
  34. def_text="-"
  35. def __init__(self, caption = None, format=None):
  36. self.format = format
  37. self.is_markdown = self.format == 'markdown'
  38. self.is_tabs = self.format == 'tabs'
  39. self.columns = {}
  40. self.rows = []
  41. self.ridx = -1;
  42. self.caption = caption
  43. pass
  44. def newRow(self, **properties):
  45. if len(self.rows) - 1 == self.ridx:
  46. self.rows.append(tblRow(len(self.columns), properties))
  47. else:
  48. self.rows[self.ridx + 1].props = properties
  49. self.ridx += 1
  50. return self.rows[self.ridx]
  51. def trimLastRow(self):
  52. if self.rows:
  53. self.rows.pop()
  54. if self.ridx >= len(self.rows):
  55. self.ridx = len(self.rows) - 1
  56. def newColumn(self, name, caption, title = None, **properties):
  57. if name in self.columns:
  58. index = self.columns[name].index
  59. else:
  60. index = len(self.columns)
  61. if isinstance(caption, tblColumn):
  62. caption.index = index
  63. self.columns[name] = caption
  64. return caption
  65. else:
  66. col = tblColumn(caption, title, properties)
  67. col.index = index
  68. self.columns[name] = col
  69. return col
  70. def getColumn(self, name):
  71. if isinstance(name, str):
  72. return self.columns.get(name, None)
  73. else:
  74. vals = [v for v in self.columns.values() if v.index == name]
  75. if vals:
  76. return vals[0]
  77. return None
  78. def newCell(self, col_name, text, value = None, **properties):
  79. if self.ridx < 0:
  80. self.newRow()
  81. col = self.getColumn(col_name)
  82. row = self.rows[self.ridx]
  83. if not col:
  84. return None
  85. if isinstance(text, tblCell):
  86. cl = text
  87. else:
  88. cl = tblCell(text, value, properties)
  89. row.cells[col.index] = cl
  90. return cl
  91. def layoutTable(self):
  92. columns = self.columns.values()
  93. columns = sorted(columns, key=lambda c: c.index)
  94. colspanned = []
  95. rowspanned = []
  96. self.headerHeight = 1
  97. rowsToAppend = 0
  98. for col in columns:
  99. self.measureCell(col)
  100. if col.height > self.headerHeight:
  101. self.headerHeight = col.height
  102. col.minwidth = col.width
  103. col.line = None
  104. for r in range(len(self.rows)):
  105. row = self.rows[r]
  106. row.minheight = 1
  107. for i in range(len(row.cells)):
  108. cell = row.cells[i]
  109. if row.cells[i] is None:
  110. continue
  111. cell.line = None
  112. self.measureCell(cell)
  113. colspan = int(self.getValue("colspan", cell))
  114. rowspan = int(self.getValue("rowspan", cell))
  115. if colspan > 1:
  116. colspanned.append((r,i))
  117. if i + colspan > len(columns):
  118. colspan = len(columns) - i
  119. cell.colspan = colspan
  120. #clear spanned cells
  121. for j in range(i+1, min(len(row.cells), i + colspan)):
  122. row.cells[j] = None
  123. elif columns[i].minwidth < cell.width:
  124. columns[i].minwidth = cell.width
  125. if rowspan > 1:
  126. rowspanned.append((r,i))
  127. rowsToAppend2 = r + colspan - len(self.rows)
  128. if rowsToAppend2 > rowsToAppend:
  129. rowsToAppend = rowsToAppend2
  130. cell.rowspan = rowspan
  131. #clear spanned cells
  132. for j in range(r+1, min(len(self.rows), r + rowspan)):
  133. if len(self.rows[j].cells) > i:
  134. self.rows[j].cells[i] = None
  135. elif row.minheight < cell.height:
  136. row.minheight = cell.height
  137. self.ridx = len(self.rows) - 1
  138. for r in range(rowsToAppend):
  139. self.newRow()
  140. self.rows[len(self.rows) - 1].minheight = 1
  141. while colspanned:
  142. colspanned_new = []
  143. for r, c in colspanned:
  144. cell = self.rows[r].cells[c]
  145. sum([col.minwidth for col in columns[c:c + cell.colspan]])
  146. cell.awailable = sum([col.minwidth for col in columns[c:c + cell.colspan]]) + cell.colspan - 1
  147. if cell.awailable < cell.width:
  148. colspanned_new.append((r,c))
  149. colspanned = colspanned_new
  150. if colspanned:
  151. r,c = colspanned[0]
  152. cell = self.rows[r].cells[c]
  153. cols = columns[c:c + cell.colspan]
  154. total = cell.awailable - cell.colspan + 1
  155. budget = cell.width - cell.awailable
  156. spent = 0
  157. s = 0
  158. for col in cols:
  159. s += col.minwidth
  160. addition = s * budget / total - spent
  161. spent += addition
  162. col.minwidth += addition
  163. while rowspanned:
  164. rowspanned_new = []
  165. for r, c in rowspanned:
  166. cell = self.rows[r].cells[c]
  167. cell.awailable = sum([row.minheight for row in self.rows[r:r + cell.rowspan]])
  168. if cell.awailable < cell.height:
  169. rowspanned_new.append((r,c))
  170. rowspanned = rowspanned_new
  171. if rowspanned:
  172. r,c = rowspanned[0]
  173. cell = self.rows[r].cells[c]
  174. rows = self.rows[r:r + cell.rowspan]
  175. total = cell.awailable
  176. budget = cell.height - cell.awailable
  177. spent = 0
  178. s = 0
  179. for row in rows:
  180. s += row.minheight
  181. addition = s * budget / total - spent
  182. spent += addition
  183. row.minheight += addition
  184. return columns
  185. def measureCell(self, cell):
  186. text = self.getValue("text", cell)
  187. cell.text = self.reformatTextValue(text)
  188. cell.height = len(cell.text)
  189. cell.width = len(max(cell.text, key = lambda line: len(line)))
  190. def reformatTextValue(self, value):
  191. if sys.version_info >= (2,7):
  192. unicode = str
  193. if isinstance(value, str):
  194. vstr = value
  195. elif isinstance(value, unicode):
  196. vstr = str(value)
  197. else:
  198. try:
  199. vstr = '\n'.join([str(v) for v in value])
  200. except TypeError:
  201. vstr = str(value)
  202. return vstr.splitlines()
  203. def adjustColWidth(self, cols, width):
  204. total = sum([c.minWidth for c in cols])
  205. if total + len(cols) - 1 >= width:
  206. return
  207. budget = width - len(cols) + 1 - total
  208. spent = 0
  209. s = 0
  210. for col in cols:
  211. s += col.minWidth
  212. addition = s * budget / total - spent
  213. spent += addition
  214. col.minWidth += addition
  215. def getValue(self, name, *elements):
  216. for el in elements:
  217. try:
  218. return getattr(el, name)
  219. except AttributeError:
  220. pass
  221. try:
  222. val = el.props[name]
  223. if val:
  224. return val
  225. except AttributeError:
  226. pass
  227. except KeyError:
  228. pass
  229. try:
  230. return getattr(self.__class__, "def_" + name)
  231. except AttributeError:
  232. return None
  233. def consolePrintTable(self, out):
  234. columns = self.layoutTable()
  235. colrizer = getColorizer(out) if not (self.is_markdown or self.is_tabs) else dummyColorizer(out)
  236. if self.caption:
  237. out.write("%s%s%s" % ( os.linesep, os.linesep.join(self.reformatTextValue(self.caption)), os.linesep * 2))
  238. headerRow = tblRow(len(columns), {"align": "center", "valign": "top", "bold": True, "header": True})
  239. headerRow.cells = columns
  240. headerRow.minheight = self.headerHeight
  241. self.consolePrintRow2(colrizer, headerRow, columns)
  242. for i in range(0, len(self.rows)):
  243. self.consolePrintRow2(colrizer, i, columns)
  244. def consolePrintRow2(self, out, r, columns):
  245. if isinstance(r, tblRow):
  246. row = r
  247. r = -1
  248. else:
  249. row = self.rows[r]
  250. #evaluate initial values for line numbers
  251. i = 0
  252. while i < len(row.cells):
  253. cell = row.cells[i]
  254. colspan = self.getValue("colspan", cell)
  255. if cell is not None:
  256. cell.wspace = sum([col.minwidth for col in columns[i:i + colspan]]) + colspan - 1
  257. if cell.line is None:
  258. if r < 0:
  259. rows = [row]
  260. else:
  261. rows = self.rows[r:r + self.getValue("rowspan", cell)]
  262. cell.line = self.evalLine(cell, rows, columns[i])
  263. if len(rows) > 1:
  264. for rw in rows:
  265. rw.cells[i] = cell
  266. i += colspan
  267. #print content
  268. if self.is_markdown:
  269. out.write("|")
  270. for c in row.cells:
  271. text = ' '.join(self.getValue('text', c) or [])
  272. out.write(text + "|")
  273. out.write(os.linesep)
  274. elif self.is_tabs:
  275. cols_to_join=[' '.join(self.getValue('text', c) or []) for c in row.cells]
  276. out.write('\t'.join(cols_to_join))
  277. out.write(os.linesep)
  278. else:
  279. for ln in range(row.minheight):
  280. i = 0
  281. while i < len(row.cells):
  282. if i > 0:
  283. out.write(" ")
  284. cell = row.cells[i]
  285. column = columns[i]
  286. if cell is None:
  287. out.write(" " * column.minwidth)
  288. i += 1
  289. else:
  290. self.consolePrintLine(cell, row, column, out)
  291. i += self.getValue("colspan", cell)
  292. if self.is_markdown:
  293. out.write("|")
  294. out.write(os.linesep)
  295. if self.is_markdown and row.props.get('header', False):
  296. out.write("|")
  297. for th in row.cells:
  298. align = self.getValue("align", th)
  299. if align == 'center':
  300. out.write(":-:|")
  301. elif align == 'right':
  302. out.write("--:|")
  303. else:
  304. out.write("---|")
  305. out.write(os.linesep)
  306. def consolePrintLine(self, cell, row, column, out):
  307. if cell.line < 0 or cell.line >= cell.height:
  308. line = ""
  309. else:
  310. line = cell.text[cell.line]
  311. width = cell.wspace
  312. align = self.getValue("align", ((None, cell)[isinstance(cell, tblCell)]), row, column)
  313. if align == "right":
  314. pattern = "%" + str(width) + "s"
  315. elif align == "center":
  316. pattern = "%" + str((width - len(line)) // 2 + len(line)) + "s" + " " * (width - len(line) - (width - len(line)) // 2)
  317. else:
  318. pattern = "%-" + str(width) + "s"
  319. out.write(pattern % line, color = self.getValue("color", cell, row, column))
  320. cell.line += 1
  321. def evalLine(self, cell, rows, column):
  322. height = cell.height
  323. valign = self.getValue("valign", cell, rows[0], column)
  324. space = sum([row.minheight for row in rows])
  325. if valign == "bottom":
  326. return height - space
  327. if valign == "middle":
  328. return (height - space + 1) // 2
  329. return 0
  330. def htmlPrintTable(self, out, embeedcss = False):
  331. columns = self.layoutTable()
  332. if embeedcss:
  333. out.write("<div style=\"font-family: Lucida Console, Courier New, Courier;font-size: 16px;color:#3e4758;\">\n<table style=\"background:none repeat scroll 0 0 #FFFFFF;border-collapse:collapse;font-family:'Lucida Sans Unicode','Lucida Grande',Sans-Serif;font-size:14px;margin:20px;text-align:left;width:480px;margin-left: auto;margin-right: auto;white-space:nowrap;\">\n")
  334. else:
  335. out.write("<div class=\"tableFormatter\">\n<table class=\"tbl\">\n")
  336. if self.caption:
  337. if embeedcss:
  338. out.write(" <caption style=\"font:italic 16px 'Trebuchet MS',Verdana,Arial,Helvetica,sans-serif;padding:0 0 5px;text-align:right;white-space:normal;\">%s</caption>\n" % htmlEncode(self.reformatTextValue(self.caption)))
  339. else:
  340. out.write(" <caption>%s</caption>\n" % htmlEncode(self.reformatTextValue(self.caption)))
  341. out.write(" <thead>\n")
  342. headerRow = tblRow(len(columns), {"align": "center", "valign": "top", "bold": True, "header": True})
  343. headerRow.cells = columns
  344. header_rows = [headerRow]
  345. header_rows.extend([row for row in self.rows if self.getValue("header")])
  346. last_row = header_rows[len(header_rows) - 1]
  347. for row in header_rows:
  348. out.write(" <tr>\n")
  349. for th in row.cells:
  350. align = self.getValue("align", ((None, th)[isinstance(th, tblCell)]), row, row)
  351. valign = self.getValue("valign", th, row)
  352. cssclass = self.getValue("cssclass", th)
  353. attr = ""
  354. if align:
  355. attr += " align=\"%s\"" % align
  356. if valign:
  357. attr += " valign=\"%s\"" % valign
  358. if cssclass:
  359. attr += " class=\"%s\"" % cssclass
  360. css = ""
  361. if embeedcss:
  362. css = " style=\"border:none;color:#003399;font-size:16px;font-weight:normal;white-space:nowrap;padding:3px 10px;\""
  363. if row == last_row:
  364. css = css[:-1] + "padding-bottom:5px;\""
  365. out.write(" <th%s%s>\n" % (attr, css))
  366. if th is not None:
  367. out.write(" %s\n" % htmlEncode(th.text))
  368. out.write(" </th>\n")
  369. out.write(" </tr>\n")
  370. out.write(" </thead>\n <tbody>\n")
  371. rows = [row for row in self.rows if not self.getValue("header")]
  372. for r in range(len(rows)):
  373. row = rows[r]
  374. rowattr = ""
  375. cssclass = self.getValue("cssclass", row)
  376. if cssclass:
  377. rowattr += " class=\"%s\"" % cssclass
  378. out.write(" <tr%s>\n" % (rowattr))
  379. i = 0
  380. while i < len(row.cells):
  381. column = columns[i]
  382. td = row.cells[i]
  383. if isinstance(td, int):
  384. i += td
  385. continue
  386. colspan = self.getValue("colspan", td)
  387. rowspan = self.getValue("rowspan", td)
  388. align = self.getValue("align", td, row, column)
  389. valign = self.getValue("valign", td, row, column)
  390. color = self.getValue("color", td, row, column)
  391. bold = self.getValue("bold", td, row, column)
  392. italic = self.getValue("italic", td, row, column)
  393. style = ""
  394. attr = ""
  395. if color:
  396. style += "color:%s;" % color
  397. if bold:
  398. style += "font-weight: bold;"
  399. if italic:
  400. style += "font-style: italic;"
  401. if align and align != "left":
  402. attr += " align=\"%s\"" % align
  403. if valign and valign != "middle":
  404. attr += " valign=\"%s\"" % valign
  405. if colspan > 1:
  406. attr += " colspan=\"%s\"" % colspan
  407. if rowspan > 1:
  408. attr += " rowspan=\"%s\"" % rowspan
  409. for q in range(r+1, min(r+rowspan, len(rows))):
  410. rows[q].cells[i] = colspan
  411. if style:
  412. attr += " style=\"%s\"" % style
  413. css = ""
  414. if embeedcss:
  415. css = " style=\"border:none;border-bottom:1px solid #CCCCCC;color:#666699;padding:6px 8px;white-space:nowrap;\""
  416. if r == 0:
  417. css = css[:-1] + "border-top:2px solid #6678B1;\""
  418. out.write(" <td%s%s>\n" % (attr, css))
  419. if td is not None:
  420. out.write(" %s\n" % htmlEncode(td.text))
  421. out.write(" </td>\n")
  422. i += colspan
  423. out.write(" </tr>\n")
  424. out.write(" </tbody>\n</table>\n</div>\n")
  425. def htmlPrintHeader(out, title = None):
  426. if title:
  427. titletag = "<title>%s</title>\n" % htmlEncode([str(title)])
  428. else:
  429. titletag = ""
  430. out.write("""<!DOCTYPE HTML>
  431. <html>
  432. <head>
  433. <meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
  434. %s<style type="text/css">
  435. html, body {font-family: Lucida Console, Courier New, Courier;font-size: 16px;color:#3e4758;}
  436. .tbl{background:none repeat scroll 0 0 #FFFFFF;border-collapse:collapse;font-family:"Lucida Sans Unicode","Lucida Grande",Sans-Serif;font-size:14px;margin:20px;text-align:left;width:480px;margin-left: auto;margin-right: auto;white-space:nowrap;}
  437. .tbl span{display:block;white-space:nowrap;}
  438. .tbl thead tr:last-child th {padding-bottom:5px;}
  439. .tbl tbody tr:first-child td {border-top:3px solid #6678B1;}
  440. .tbl th{border:none;color:#003399;font-size:16px;font-weight:normal;white-space:nowrap;padding:3px 10px;}
  441. .tbl td{border:none;border-bottom:1px solid #CCCCCC;color:#666699;padding:6px 8px;white-space:nowrap;}
  442. .tbl tbody tr:hover td{color:#000099;}
  443. .tbl caption{font:italic 16px "Trebuchet MS",Verdana,Arial,Helvetica,sans-serif;padding:0 0 5px;text-align:right;white-space:normal;}
  444. .firstingroup {border-top:2px solid #6678B1;}
  445. </style>
  446. <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
  447. <script type="text/javascript">
  448. function abs(val) { return val < 0 ? -val : val }
  449. $(function(){
  450. //generate filter rows
  451. $("div.tableFormatter table.tbl").each(function(tblIdx, tbl) {
  452. var head = $("thead", tbl)
  453. var filters = $("<tr></tr>")
  454. var hasAny = false
  455. $("tr:first th", head).each(function(colIdx, col) {
  456. col = $(col)
  457. var cell
  458. var id = "t" + tblIdx + "r" + colIdx
  459. if (col.hasClass("col_name")){
  460. cell = $("<th><input id='" + id + "' name='" + id + "' type='text' style='width:100%%' class='filter_col_name' title='Regular expression for name filtering (&quot;resize.*640x480&quot; - resize tests on VGA resolution)'></input></th>")
  461. hasAny = true
  462. }
  463. else if (col.hasClass("col_rel")){
  464. cell = $("<th><input id='" + id + "' name='" + id + "' type='text' style='width:100%%' class='filter_col_rel' title='Filter out lines with a x-factor of acceleration less than Nx'></input></th>")
  465. hasAny = true
  466. }
  467. else if (col.hasClass("col_cr")){
  468. cell = $("<th><input id='" + id + "' name='" + id + "' type='text' style='width:100%%' class='filter_col_cr' title='Filter out lines with a percentage of acceleration less than N%%'></input></th>")
  469. hasAny = true
  470. }
  471. else
  472. cell = $("<th></th>")
  473. cell.appendTo(filters)
  474. })
  475. if (hasAny){
  476. $(tbl).wrap("<form id='form_t" + tblIdx + "' method='get' action=''></form>")
  477. $("<input it='test' type='submit' value='Apply Filters' style='margin-left:10px;'></input>")
  478. .appendTo($("th:last", filters.appendTo(head)))
  479. }
  480. })
  481. //get filter values
  482. var vars = []
  483. var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&')
  484. for(var i = 0; i < hashes.length; ++i)
  485. {
  486. hash = hashes[i].split('=')
  487. vars.push(decodeURIComponent(hash[0]))
  488. vars[decodeURIComponent(hash[0])] = decodeURIComponent(hash[1]);
  489. }
  490. //set filter values
  491. for(var i = 0; i < vars.length; ++i)
  492. $("#" + vars[i]).val(vars[vars[i]])
  493. //apply filters
  494. $("div.tableFormatter table.tbl").each(function(tblIdx, tbl) {
  495. filters = $("input:text", tbl)
  496. var predicate = function(row) {return true;}
  497. var empty = true
  498. $.each($("input:text", tbl), function(i, flt) {
  499. flt = $(flt)
  500. var val = flt.val()
  501. var pred = predicate;
  502. if(val) {
  503. empty = false
  504. var colIdx = parseInt(flt.attr("id").slice(flt.attr("id").indexOf('r') + 1))
  505. if(flt.hasClass("filter_col_name")) {
  506. var re = new RegExp(val);
  507. predicate = function(row) {
  508. if (re.exec($(row.get(colIdx)).text()) == null)
  509. return false
  510. return pred(row)
  511. }
  512. } else if(flt.hasClass("filter_col_rel")) {
  513. var percent = parseFloat(val)
  514. if (percent < 0) {
  515. predicate = function(row) {
  516. var val = parseFloat($(row.get(colIdx)).text())
  517. if (!val || val >= 1 || val > 1+percent)
  518. return false
  519. return pred(row)
  520. }
  521. } else {
  522. predicate = function(row) {
  523. var val = parseFloat($(row.get(colIdx)).text())
  524. if (!val || val < percent)
  525. return false
  526. return pred(row)
  527. }
  528. }
  529. } else if(flt.hasClass("filter_col_cr")) {
  530. var percent = parseFloat(val)
  531. predicate = function(row) {
  532. var val = parseFloat($(row.get(colIdx)).text())
  533. if (!val || val < percent)
  534. return false
  535. return pred(row)
  536. }
  537. }
  538. }
  539. });
  540. if (!empty){
  541. $("tbody tr", tbl).each(function (i, tbl_row) {
  542. if(!predicate($("td", tbl_row)))
  543. $(tbl_row).remove()
  544. })
  545. if($("tbody tr", tbl).length == 0) {
  546. $("<tr><td colspan='"+$("thead tr:first th", tbl).length+"'>No results matching your search criteria</td></tr>")
  547. .appendTo($("tbody", tbl))
  548. }
  549. }
  550. })
  551. })
  552. </script>
  553. </head>
  554. <body>
  555. """ % titletag)
  556. def htmlPrintFooter(out):
  557. out.write("</body>\n</html>")
  558. def getStdoutFilename():
  559. try:
  560. if os.name == "nt":
  561. import msvcrt, ctypes
  562. handle = msvcrt.get_osfhandle(sys.stdout.fileno())
  563. size = ctypes.c_ulong(1024)
  564. nameBuffer = ctypes.create_string_buffer(size.value)
  565. ctypes.windll.kernel32.GetFinalPathNameByHandleA(handle, nameBuffer, size, 4)
  566. return nameBuffer.value
  567. else:
  568. return os.readlink('/proc/self/fd/1')
  569. except:
  570. return ""
  571. def detectHtmlOutputType(requestedType):
  572. if requestedType in ['txt', 'markdown']:
  573. return False
  574. elif requestedType in ["html", "moinwiki"]:
  575. return True
  576. else:
  577. if sys.stdout.isatty():
  578. return False
  579. else:
  580. outname = getStdoutFilename()
  581. if outname:
  582. if outname.endswith(".htm") or outname.endswith(".html"):
  583. return True
  584. else:
  585. return False
  586. else:
  587. return False
  588. def getRelativeVal(test, test0, metric):
  589. if not test or not test0:
  590. return None
  591. val0 = test0.get(metric, "s")
  592. if not val0:
  593. return None
  594. val = test.get(metric, "s")
  595. if not val or val == 0:
  596. return None
  597. return float(val0)/val
  598. def getCycleReduction(test, test0, metric):
  599. if not test or not test0:
  600. return None
  601. val0 = test0.get(metric, "s")
  602. if not val0 or val0 == 0:
  603. return None
  604. val = test.get(metric, "s")
  605. if not val:
  606. return None
  607. return (1.0-float(val)/val0)*100
  608. def getScore(test, test0, metric):
  609. if not test or not test0:
  610. return None
  611. m0 = float(test.get("gmean", None))
  612. m1 = float(test0.get("gmean", None))
  613. if m0 == 0 or m1 == 0:
  614. return None
  615. s0 = float(test.get("gstddev", None))
  616. s1 = float(test0.get("gstddev", None))
  617. s = math.sqrt(s0*s0 + s1*s1)
  618. m0 = math.log(m0)
  619. m1 = math.log(m1)
  620. if s == 0:
  621. return None
  622. return (m0-m1)/s
  623. metrix_table = \
  624. {
  625. "name": ("Name of Test", lambda test,test0,units: str(test)),
  626. "samples": ("Number of\ncollected samples", lambda test,test0,units: test.get("samples", units)),
  627. "outliers": ("Number of\noutliers", lambda test,test0,units: test.get("outliers", units)),
  628. "gmean": ("Geometric mean", lambda test,test0,units: test.get("gmean", units)),
  629. "mean": ("Mean", lambda test,test0,units: test.get("mean", units)),
  630. "min": ("Min", lambda test,test0,units: test.get("min", units)),
  631. "median": ("Median", lambda test,test0,units: test.get("median", units)),
  632. "stddev": ("Standard deviation", lambda test,test0,units: test.get("stddev", units)),
  633. "gstddev": ("Standard deviation of Ln(time)", lambda test,test0,units: test.get("gstddev")),
  634. "gmean%": ("Geometric mean (relative)", lambda test,test0,units: getRelativeVal(test, test0, "gmean")),
  635. "mean%": ("Mean (relative)", lambda test,test0,units: getRelativeVal(test, test0, "mean")),
  636. "min%": ("Min (relative)", lambda test,test0,units: getRelativeVal(test, test0, "min")),
  637. "median%": ("Median (relative)", lambda test,test0,units: getRelativeVal(test, test0, "median")),
  638. "stddev%": ("Standard deviation (relative)", lambda test,test0,units: getRelativeVal(test, test0, "stddev")),
  639. "gstddev%": ("Standard deviation of Ln(time) (relative)", lambda test,test0,units: getRelativeVal(test, test0, "gstddev")),
  640. "gmean$": ("Geometric mean (cycle reduction)", lambda test,test0,units: getCycleReduction(test, test0, "gmean")),
  641. "mean$": ("Mean (cycle reduction)", lambda test,test0,units: getCycleReduction(test, test0, "mean")),
  642. "min$": ("Min (cycle reduction)", lambda test,test0,units: getCycleReduction(test, test0, "min")),
  643. "median$": ("Median (cycle reduction)", lambda test,test0,units: getCycleReduction(test, test0, "median")),
  644. "stddev$": ("Standard deviation (cycle reduction)", lambda test,test0,units: getCycleReduction(test, test0, "stddev")),
  645. "gstddev$": ("Standard deviation of Ln(time) (cycle reduction)", lambda test,test0,units: getCycleReduction(test, test0, "gstddev")),
  646. "score": ("SCORE", lambda test,test0,units: getScore(test, test0, "gstddev")),
  647. }
  648. def formatValue(val, metric, units = None):
  649. if val is None:
  650. return "-"
  651. if metric.endswith("%"):
  652. return "%.2f" % val
  653. if metric.endswith("$"):
  654. return "%.2f%%" % val
  655. if metric.endswith("S"):
  656. if val > 3.5:
  657. return "SLOWER"
  658. if val < -3.5:
  659. return "FASTER"
  660. if val > -1.5 and val < 1.5:
  661. return " "
  662. if val < 0:
  663. return "faster"
  664. if val > 0:
  665. return "slower"
  666. #return "%.4f" % val
  667. if units:
  668. return "%.3f %s" % (val, units)
  669. else:
  670. return "%.3f" % val
  671. if __name__ == "__main__":
  672. if len(sys.argv) < 2:
  673. print("Usage:\n", os.path.basename(sys.argv[0]), "<log_name>.xml")
  674. exit(0)
  675. parser = OptionParser()
  676. parser.add_option("-o", "--output", dest="format", help="output results in text format (can be 'txt', 'html', 'markdown' or 'auto' - default)", metavar="FMT", default="auto")
  677. parser.add_option("-m", "--metric", dest="metric", help="output metric", metavar="NAME", default="gmean")
  678. parser.add_option("-u", "--units", dest="units", help="units for output values (s, ms (default), us, ns or ticks)", metavar="UNITS", default="ms")
  679. (options, args) = parser.parse_args()
  680. options.generateHtml = detectHtmlOutputType(options.format)
  681. if options.metric not in metrix_table:
  682. options.metric = "gmean"
  683. #print options
  684. #print args
  685. # tbl = table()
  686. # tbl.newColumn("first", "qqqq", align = "left")
  687. # tbl.newColumn("second", "wwww\nz\nx\n")
  688. # tbl.newColumn("third", "wwasdas")
  689. #
  690. # tbl.newCell(0, "ccc111", align = "right")
  691. # tbl.newCell(1, "dddd1")
  692. # tbl.newCell(2, "8768756754")
  693. # tbl.newRow()
  694. # tbl.newCell(0, "1\n2\n3\n4\n5\n6\n7", align = "center", colspan = 2, rowspan = 2)
  695. # tbl.newCell(2, "xxx\nqqq", align = "center", colspan = 1, valign = "middle")
  696. # tbl.newRow()
  697. # tbl.newCell(2, "+", align = "center", colspan = 1, valign = "middle")
  698. # tbl.newRow()
  699. # tbl.newCell(0, "vcvvbasdsadassdasdasv", align = "right", colspan = 2)
  700. # tbl.newCell(2, "dddd1")
  701. # tbl.newRow()
  702. # tbl.newCell(0, "vcvvbv")
  703. # tbl.newCell(1, "3445324", align = "right")
  704. # tbl.newCell(2, None)
  705. # tbl.newCell(1, "0000")
  706. # if sys.stdout.isatty():
  707. # tbl.consolePrintTable(sys.stdout)
  708. # else:
  709. # htmlPrintHeader(sys.stdout)
  710. # tbl.htmlPrintTable(sys.stdout)
  711. # htmlPrintFooter(sys.stdout)
  712. import testlog_parser
  713. if options.generateHtml:
  714. htmlPrintHeader(sys.stdout, "Tables demo")
  715. getter = metrix_table[options.metric][1]
  716. for arg in args:
  717. tests = testlog_parser.parseLogFile(arg)
  718. tbl = table(arg, format=options.format)
  719. tbl.newColumn("name", "Name of Test", align = "left")
  720. tbl.newColumn("value", metrix_table[options.metric][0], align = "center", bold = "true")
  721. for t in sorted(tests):
  722. tbl.newRow()
  723. tbl.newCell("name", str(t))
  724. status = t.get("status")
  725. if status != "run":
  726. tbl.newCell("value", status)
  727. else:
  728. val = getter(t, None, options.units)
  729. if val:
  730. if options.metric.endswith("%"):
  731. tbl.newCell("value", "%.2f" % val, val)
  732. else:
  733. tbl.newCell("value", "%.3f %s" % (val, options.units), val)
  734. else:
  735. tbl.newCell("value", "-")
  736. if options.generateHtml:
  737. tbl.htmlPrintTable(sys.stdout)
  738. else:
  739. tbl.consolePrintTable(sys.stdout)
  740. if options.generateHtml:
  741. htmlPrintFooter(sys.stdout)