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> </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 = ' ' 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 ' ' 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 = ' ' 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 / %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 N/A)' % box_data.status_count[test_na] 213 if running in box_data.status_count.keys(): 214 html += ' (%d 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 '   ' 335 print '<a href="machine_benchmark.cgi">Performance</a>' 336 print '   ' 337 print '<a href="http://ossipedia.ipa.go.jp/crackerjack/compare_results.html">Crackerjack</a>' 338 print '   ' 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