Home | History | Annotate | Download | only in tko
      1 import os, re, string, sys
      2 import frontend, reason_qualifier
      3 
      4 color_map = {
      5         'header'        : '#e5e5c0', # greyish yellow
      6         'blank'         : '#ffffff', # white
      7         'plain_text'    : '#e5e5c0', # greyish yellow
      8         'borders'       : '#bbbbbb', # grey
      9         'white'         : '#ffffff', # white
     10         'green'         : '#66ff66', # green
     11         'yellow'        : '#fffc00', # yellow
     12         'red'           : '#ff6666', # red
     13 
     14         #### additional keys for shaded color of a box
     15         #### depending on stats of GOOD/FAIL
     16         '100pct'  : '#32CD32', # green, 94% to 100% of success
     17         '95pct'   : '#c0ff80', # step twrds yellow, 88% to 94% of success
     18         '90pct'   : '#ffff00', # yellow, 82% to 88%
     19         '85pct'   : '#ffc040', # 76% to 82%
     20         '75pct'   : '#ff4040', # red, 1% to 76%
     21         '0pct'    : '#d080d0', # violet, <1% of success
     22 
     23 }
     24 
     25 _brief_mode = False
     26 
     27 
     28 def set_brief_mode():
     29     global _brief_mode
     30     _brief_mode = True
     31 
     32 
     33 def is_brief_mode():
     34     return _brief_mode
     35 
     36 
     37 def color_keys_row():
     38     """ Returns one row table with samples of 'NNpct' colors
     39             defined in the color_map
     40             and numbers of corresponding %%
     41     """
     42     ### This function does not require maintenance in case of
     43     ### color_map augmenting - as long as
     44     ### color keys for box shading have names that end with 'pct'
     45     keys = filter(lambda key: key.endswith('pct'), color_map.keys())
     46     def num_pct(key):
     47         return int(key.replace('pct',''))
     48     keys.sort(key=num_pct)
     49     html = ''
     50     for key in keys:
     51         html+= "\t\t\t<td bgcolor =%s>&nbsp;&nbsp;&nbsp;</td>\n"\
     52                         % color_map[key]
     53         hint = key.replace('pct',' %')
     54         if hint[0]<>'0': ## anything but 0 %
     55             hint = 'to ' + hint
     56         html+= "\t\t\t<td> %s </td>\n" % hint
     57 
     58     html = """
     59 <table width = "500" border="0" cellpadding="2" cellspacing="2">\n
     60     <tbody>\n
     61             <tr>\n
     62 %s
     63             </tr>\n
     64     </tbody>
     65 </table><br>
     66 """ % html
     67     return html
     68 
     69 
     70 def calculate_html(link, data, tooltip=None, row_label=None, column_label=None):
     71     if not is_brief_mode():
     72         hover_text = '%s:%s' % (row_label, column_label)
     73         if data:  ## cell is not empty
     74             hover_text += '<br>%s' % tooltip
     75         else:
     76             ## avoid "None" printed in empty cells
     77             data = '&nbsp;'
     78         html = ('<center><a class="info" href="%s">'
     79                 '%s<span>%s</span></a></center>' %
     80                 (link, data, hover_text))
     81         return html
     82     # no hover if embedded into AFE but links shall redirect to new window
     83     if data: ## cell is non empty
     84         html =  '<a href="%s" target="_blank">%s</a>' % (link, data)
     85         return html
     86     else: ## cell is empty
     87         return '&nbsp;'
     88 
     89 
     90 class box:
     91     def __init__(self, data, color_key = None, header = False, link = None,
     92                  tooltip = None, row_label = None, column_label = None):
     93 
     94         ## in brief mode we display grid table only and nothing more
     95         ## - mouse hovering feature is stubbed in brief mode
     96         ## - any link opens new window or tab
     97 
     98         redirect = ""
     99         if is_brief_mode():
    100             ## we are acting under AFE
    101             ## any link shall open new window
    102             redirect = " target=NEW"
    103 
    104         if data:
    105             data = "<tt>%s</tt>" % data
    106 
    107         if link and not tooltip:
    108             ## FlipAxis corner, column and row headers
    109             self.data = ('<a href="%s"%s>%s</a>' %
    110                          (link, redirect, data))
    111         else:
    112             self.data = calculate_html(link, data, tooltip,
    113                                        row_label, column_label)
    114 
    115         if color_map.has_key(color_key):
    116             self.color = color_map[color_key]
    117         elif header:
    118             self.color = color_map['header']
    119         elif data:
    120             self.color = color_map['plain_text']
    121         else:
    122             self.color = color_map['blank']
    123         self.header = header
    124 
    125 
    126     def html(self):
    127         if self.data:
    128             data = self.data
    129         else:
    130             data = '&nbsp'
    131 
    132         if self.header:
    133             box_html = 'th'
    134         else:
    135             box_html = 'td'
    136 
    137         return "<%s bgcolor=%s>%s</%s>" % \
    138                                 (box_html, self.color, data, box_html)
    139 
    140 
    141 def grade_from_status(status_idx, status):
    142     # % of goodness
    143     # GOOD (6)  -> 1
    144     # TEST_NA (8) is not counted
    145     # ##  If the test doesn't PASS, it FAILS
    146     # else -> 0
    147 
    148     if status == status_idx['GOOD']:
    149         return 1.0
    150     else:
    151         return 0.0
    152 
    153 
    154 def average_grade_from_status_count(status_idx, status_count):
    155     average_grade = 0
    156     total_count = 0
    157     for key in status_count.keys():
    158         if key not in (status_idx['TEST_NA'], status_idx['RUNNING']):
    159             average_grade += (grade_from_status(status_idx, key)
    160                                     * status_count[key])
    161             total_count += status_count[key]
    162     if total_count != 0:
    163         average_grade = average_grade / total_count
    164     else:
    165         average_grade = 0.0
    166     return average_grade
    167 
    168 
    169 def shade_from_status_count(status_idx, status_count):
    170     if not status_count:
    171         return None
    172 
    173     ## average_grade defines a shade of the box
    174     ## 0 -> violet
    175     ## 0.76 -> red
    176     ## 0.88-> yellow
    177     ## 1.0 -> green
    178     average_grade = average_grade_from_status_count(status_idx, status_count)
    179 
    180     ## find appropiate keyword from color_map
    181     if average_grade<0.01:
    182         shade = '0pct'
    183     elif average_grade<0.75:
    184         shade = '75pct'
    185     elif average_grade<0.85:
    186         shade = '85pct'
    187     elif average_grade<0.90:
    188         shade = '90pct'
    189     elif average_grade<0.95:
    190         shade = '95pct'
    191     else:
    192         shade = '100pct'
    193 
    194     return shade
    195 
    196 
    197 def status_html(db, box_data, shade):
    198     """
    199     status_count: dict mapping from status (integer key) to count
    200     eg. { 'GOOD' : 4, 'FAIL' : 1 }
    201     """
    202     status_count_subset = box_data.status_count.copy()
    203     test_na = db.status_idx['TEST_NA']
    204     running = db.status_idx['RUNNING']
    205     good = db.status_idx['GOOD']
    206 
    207     status_count_subset[test_na] = 0  # Don't count TEST_NA
    208     status_count_subset[running] = 0  # Don't count RUNNING
    209     html = "%d&nbsp;/&nbsp;%d " % (status_count_subset.get(good, 0),
    210                                    sum(status_count_subset.values()))
    211     if test_na in box_data.status_count.keys():
    212         html += ' (%d&nbsp;N/A)' % box_data.status_count[test_na]
    213     if running in box_data.status_count.keys():
    214         html += ' (%d&nbsp;running)' % box_data.status_count[running]
    215 
    216     if box_data.reasons_list:
    217         reasons_list = box_data.reasons_list
    218         aggregated_reasons_list = \
    219                 reason_qualifier.aggregate_reason_fields(reasons_list)
    220         for reason in aggregated_reasons_list:
    221             ## a bit of more postprocessing
    222             ## to look nicer in a cell
    223             ## in future: to do subtable within the cell
    224             reason = reason.replace('<br>','\n')
    225             reason = reason.replace('<','[').replace('>',']')
    226             reason = reason.replace('|','\n').replace('&',' AND ')
    227             reason = reason.replace('\n','<br>')
    228             html += '<br>' + reason
    229 
    230     tooltip = ""
    231     for status in sorted(box_data.status_count.keys(), reverse = True):
    232         status_word = db.status_word[status]
    233         tooltip += "%d %s " % (box_data.status_count[status], status_word)
    234     return (html,tooltip)
    235 
    236 
    237 def status_count_box(db, tests, link = None):
    238     """
    239     Display a ratio of total number of GOOD tests
    240     to total number of all tests in the group of tests.
    241     More info (e.g. 10 GOOD, 2 WARN, 3 FAIL) is in tooltips
    242     """
    243     if not tests:
    244         return box(None, None)
    245 
    246     status_count = {}
    247     for test in tests:
    248         count = status_count.get(test.status_num, 0)
    249         status_count[test.status_num] = count + 1
    250     return status_precounted_box(db, status_count, link)
    251 
    252 
    253 def status_precounted_box(db, box_data, link = None,
    254                                  x_label = None, y_label = None):
    255     """
    256     Display a ratio of total number of GOOD tests
    257     to total number of all tests in the group of tests.
    258     More info (e.g. 10 GOOD, 2 WARN, 3 FAIL) is in tooltips
    259     """
    260     status_count = box_data.status_count
    261     if not status_count:
    262         return box(None, None)
    263 
    264     shade = shade_from_status_count(db.status_idx, status_count)
    265     html,tooltip = status_html(db, box_data, shade)
    266     precounted_box = box(html, shade, False, link, tooltip,
    267                             x_label, y_label)
    268     return precounted_box
    269 
    270 
    271 def print_table(matrix):
    272     """
    273     matrix: list of lists of boxes, giving a matrix of data
    274     Each of the inner lists is a row, not a column.
    275 
    276     Display the given matrix of data as a table.
    277     """
    278 
    279     print ('<table bgcolor="%s" cellspacing="1" cellpadding="5" '
    280            'style="margin-right: 200px;">') % (
    281            color_map['borders'])
    282     for row in matrix:
    283         print '<tr>'
    284         for element in row:
    285             print element.html()
    286         print '</tr>'
    287     print '</table>'
    288 
    289 
    290 def sort_tests(tests):
    291     kernel_order = ['patch', 'config', 'build', 'mkinitrd', 'install']
    292 
    293     results = []
    294     for kernel_op in kernel_order:
    295         test = 'kernel.' + kernel_op
    296         if tests.count(test):
    297             results.append(test)
    298             tests.remove(test)
    299     if tests.count('boot'):
    300         results.append('boot')
    301         tests.remove('boot')
    302     return results + sorted(tests)
    303 
    304 
    305 def print_main_header():
    306     hover_css="""\
    307 a.info{
    308 position:relative; /*this is the key*/
    309 z-index:1
    310 color:#000;
    311 text-decoration:none}
    312 
    313 a.info:hover{z-index:25;}
    314 
    315 a.info span{display: none}
    316 
    317 a.info:hover span{ /*the span will display just on :hover state*/
    318 display:block;
    319 position:absolute;
    320 top:1em; left:1em;
    321 min-width: 100px;
    322 overflow: visible;
    323 border:1px solid #036;
    324 background-color:#fff; color:#000;
    325 text-align: left
    326 }
    327 """
    328     print '<head><style type="text/css">'
    329     print 'a { text-decoration: none }'
    330     print hover_css
    331     print '</style></head>'
    332     print '<h2>'
    333     print '<a href="compose_query.cgi">Functional</a>'
    334     print '&nbsp&nbsp&nbsp'
    335     print '<a href="machine_benchmark.cgi">Performance</a>'
    336     print '&nbsp&nbsp&nbsp'
    337     print '<a href="http://ossipedia.ipa.go.jp/crackerjack/compare_results.html">Crackerjack</a>'
    338     print '&nbsp&nbsp&nbsp'
    339     print '<a href="http://autotest.kernel.org">[About Page]</a>'
    340     print '</h2><p>'
    341 
    342 
    343 def group_name(group):
    344     name = re.sub('_', '<br>', group.name)
    345     if re.search('/', name):
    346         (owner, machine) = name.split('/', 1)
    347         name = owner + '<br>' + machine
    348     return name
    349 
    350 def print_add_test_form(available_params, attributes, cleared):
    351     print '<form method="post">'
    352     print '<input type="hidden" name="attributes" value="%s" />' % attributes
    353     print '<input type="hidden" name="cleared" value="%s" />' % cleared
    354     print '<select name="key">'
    355     for text in available_params:
    356         print '<option value="%s">%s</option>' % (text, text)
    357     print '</select>'
    358     print '<input type="submit" name="add" value="Add test" />'
    359     print '<input type="submit" name="clear" value="Clear all tests" />'
    360     print '<input type="submit" name="reset" value="Reset" />'
    361     print '</form>'
    362