1# Copyright 2017 The TensorFlow Authors. All Rights Reserved. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14# ============================================================================== 15"""Make HTML tables that report where TF and TOCO failed to convert models. 16 17This is primarily used by generate_examples.py. See it or 18`make_report_table` for more details on usage. 19""" 20from __future__ import absolute_import 21from __future__ import division 22from __future__ import print_function 23 24import html 25import json 26import re 27 28FAILED = "FAILED" 29SUCCESS = "SUCCESS" 30NOTRUN = "NOTRUN" 31 32 33def make_report_table(fp, title, reports): 34 """Make an HTML report of the success/failure reports. 35 36 Args: 37 fp: File-like object in which to put the html. 38 title: "Title of the zip file this pertains to." 39 reports: a list of conversion attempts. (report_args, report_vals) i.e. 40 ({"shape": [1,2,3], "type": "tf.float32"}, 41 {"tf": "SUCCESS", "converter": "FAILURE", 42 "converter_log": "Unsupported type.", "tf_log": ""}) 43 """ 44 # sort reports by if TOCO failure and then TF failure (reversed) 45 reports.sort(key=lambda x: x[1]["converter"], reverse=False) 46 reports.sort(key=lambda x: x[1]["tf"], reverse=True) 47 def result_cell(x, row, col): 48 """Produce a cell with the condition string `x`.""" 49 s = html.escape(repr(x), quote=True) 50 color = "#44ff44" if x == SUCCESS else ( 51 "#ff4444" if x == FAILED else "#eeeeee") 52 handler = "ShowLog(%d, %d)" % (row, col) 53 fp.write("<td style='background-color: %s' onclick='%s'>%s</td>\n" % ( 54 color, handler, s)) 55 56 fp.write("""<html> 57<head> 58<title>tflite report</title> 59<style> 60body { font-family: Arial; } 61th { background-color: #555555; color: #eeeeee; } 62td { vertical-align: top; } 63td.horiz {width: 50%;} 64pre { white-space: pre-wrap; word-break: keep-all; } 65table {width: 100%;} 66</style> 67</head> 68""") 69 # Write the log data to a javascript variable and also make a function 70 # in javascript to show the log when an item is clicked. 71 fp.write("<script> \n") 72 fp.write(""" 73function ShowLog(row, col) { 74 75var log = document.getElementById("log"); 76log.innerHTML = "<pre>" + data[row][col] + "</pre>"; 77} 78""") 79 fp.write("var data = \n") 80 logs = json.dumps([[escape_and_normalize(x[1]["tf_log"]), 81 escape_and_normalize(x[1]["converter_log"]) 82 ] for x in reports]) 83 fp.write(logs) 84 fp.write(";</script>\n") 85 86 # Write the main table and use onclick on the items that have log items. 87 fp.write(""" 88<body> 89<h1>TOCO Conversion</h1> 90<h2>%s</h2> 91""" % title) 92 93 # Get a list of keys that are in any of the records. 94 param_keys = {} 95 for params, _ in reports: 96 for k in params.keys(): 97 param_keys[k] = True 98 99 fp.write("<table>\n") 100 fp.write("<tr><td class='horiz'>\n") 101 fp.write("<div style='height:1000px; overflow:auto'>\n") 102 fp.write("<table>\n") 103 fp.write("<tr>\n") 104 for p in param_keys: 105 fp.write("<th>%s</th>\n" % html.escape(p, quote=True)) 106 fp.write("<th>TensorFlow</th>\n") 107 fp.write("<th>TOCO</th>\n") 108 fp.write("</tr>\n") 109 for idx, (params, vals) in enumerate(reports): 110 fp.write("<tr>\n") 111 for p in param_keys: 112 fp.write(" <td>%s</td>\n" % 113 html.escape(repr(params.get(p, None)), quote=True)) 114 115 result_cell(vals["tf"], idx, 0) 116 result_cell(vals["converter"], idx, 1) 117 fp.write("</tr>\n") 118 fp.write("</table>\n") 119 fp.write("</div>\n") 120 fp.write("</td>\n") 121 fp.write("<td class='horiz' id='log'></td></tr>\n") 122 fp.write("</table>\n") 123 fp.write("<script>\n") 124 fp.write("</script>\n") 125 fp.write(""" 126 </body> 127 </html> 128 """) 129 130 131def escape_and_normalize(log): 132 # These logs contain paths like /tmp/tmpgmypg3xa that are inconsistent between 133 # builds. This replaces these inconsistent paths with a consistent placeholder 134 # so the output is deterministic. 135 log = re.sub(r"/tmp/[^ ]+ ", "/NORMALIZED_TMP_FILE_PATH ", log) 136 log = re.sub(r"/build/work/[^/]+", "/NORMALIZED_BUILD_PATH", log) 137 return html.escape(log, quote=True) 138