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