Source code for fvm.toolchains.questa_pkg.parsers.parse_reports

# Copyright 2024-2026 Universidad de Sevilla
# SPDX-License-Identifier: Apache-2.0

"""Parsers for coverage reports and convert them to HTML."""
import re

[docs] def parse_formal_reachability_report_to_html(input_file, output_file="report.html"): """Parses a formal reachability report text file and converts it to an HTML file with styling and interactivity.""" with open(input_file, "r", encoding="utf-8") as file: lines = file.readlines() html_content = [] assumptions = [] index_items = [] tables = [] current_section = None legend = [] cover_table = False cover_type_table = [] table_title = "" report_generated = "" for line in lines: stripped_line = line.strip() if stripped_line.startswith("Report Generated :"): report_generated = stripped_line continue if re.search(r"-{2,}", stripped_line): continue if "Legend" in stripped_line: current_section = "Legend" legend = [] continue if current_section == "Legend": if stripped_line == "": current_section = None else: if "Reachability Percentage" in stripped_line: text = "Reachable/(Total-User Excluded)" legend.append(f"<strong>Reachability Percentage</strong>: {text}") else: parts = stripped_line.split(" ", 1) if len(parts) == 2: legend.append(f"<strong>{parts[0]}</strong>: {parts[1]}") if "Assumptions" in stripped_line: current_section = "Assumptions" assumptions = [] match = re.search(r"Assumptions \((\d+)\)", stripped_line) if match: assumptions_count = match.group(1) assumptions.append(f"<h2>Assumptions ({assumptions_count})</h2><ul>") continue if current_section == "Assumptions": if stripped_line == "": current_section = None else: assumptions.append(stripped_line) if re.match(r"Reachability Summary for Design:", stripped_line): table_title = stripped_line current_section = "Design" index_items.append((current_section, table_title)) continue if re.match(r"Reachability Summary for Instance:", stripped_line): table_title = stripped_line current_section = "Instance" index_items.append((current_section, table_title)) continue if "Cover Type" in stripped_line and not cover_table: cover_table = True cover_type_table = [["Cover Type","Total","Unreachable","Inconclusive","Reachable"]] continue if cover_table: if stripped_line == "" or "Total" in stripped_line: if cover_type_table: tables.append((table_title, cover_type_table)) cover_table = False continue row = re.split(r"\s{3,}", stripped_line) if len(row) >= 5: row[4] = re.sub(r"\s*\)$", r" )", row[4]) row = row[:5] if row: cover_type_table.append(row) html_content.append("""<!DOCTYPE html> <html lang='en'> <head> <meta charset='UTF-8'> <meta name='viewport' content='width=device-width, initial-scale=1.0'> <title>Reachability Report</title> <link href='https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap' rel='stylesheet'> <style> body { font-family: 'Poppins', sans-serif; margin: 0; padding: 15px; background-color: #f4f4f9; color: #333; line-height: 1.4; font-size: 14px; } h1, h2, h3 { text-align: center; margin-bottom: 8px; font-weight: 600; font-size: 1.5em; } .container { display: flex; height: 100h; overflow: hidden; } .index { flex: 1; max-width: 25%; padding: 15px; background-color: #fff; box-shadow: 0 4px 6px rgba(0,0,0,0.1); border-radius: 8px; overflow-y: scroll; height: 100%; } .content { flex: 4; padding: 15px; background-color: #fff; box-shadow: 0 4px 6px rgba(0,0,0,0.1); border-radius: 8px; overflow-y: scroll; height: 100%; } table { width: 80%; border-collapse: collapse; margin: 20px auto; background-color: #fff; border-radius: 8px; overflow: hidden; box-shadow: 0 4px 8px rgba(0,0,0,0.1); font-size: 14px; } table th, table td { padding: 10px 12px; text-align: center; } table th { background-color: #1976D2; color: white; font-weight: 600; } table tr:nth-child(even) { background-color: #f9f9f9; } table tr:hover { background-color: #f1f1f1; } .legend, .assumptions, .assertions, .index { margin: 20px auto; padding: 15px; width: 75%; text-align: left; background-color: #fff; box-shadow: 0 4px 6px rgba(0,0,0,0.1); border-radius: 8px; font-size: 14px; } .legend ul, .assumptions ul, .assertions ul, .index ul { list-style-type: none; padding: 0; margin: 0; } .legend ul li, .assumptions ul li, .assertions ul li, .index ul li { margin: 8px 0; font-size: 1em; } .legend h2, .assumptions h2, .assertions h2, .index h2 { font-weight: 600; font-size: 1.2em; } .index ul li { margin: 8px 0; font-size: 1em; } .index ul li a { color: #0000EE; text-decoration: none; } .index ul li a:visited { color: #0000EE; } .index ul li a:hover { color: #0000EE; } .index ul li a:active { color: #0000EE; } .index ul li.index_instance { padding-left: 20px; font-style: italic; } .toggle-btn { font-size: 18px; font-weight: bold; color: #333; background: none; border: none; padding: 10px 0; cursor: pointer; text-align: center; width: 100%; margin-bottom: 10px; } .tables-container { display: flex; flex-direction: column; gap: 20px; width: 80%; margin: 0 auto; background-color: transparent; } table { width: 80%; border-collapse: collapse; margin: 20px auto; background-color: #fff; border-radius: 8px; overflow: hidden; box-shadow: 0 4px 8px rgba(0,0,0,0.1); font-size: 14px; } .content .legend, .content .assumptions, .content .tables-container { margin-bottom: 20px; } </style> </head> <body> <h1>Reachability Report</h1>""") if report_generated: match = re.match(r"(Report Generated :)(.*)", report_generated) if match: bold_text = match.group(1).strip() normal_text = match.group(2).strip() html_content.append( f"<p style='text-align: center;'><strong>{bold_text}</strong> {normal_text}</p>" ) html_content.append("<div class='container'>") if index_items: html_content.append("<div class='index'>") html_content.append("<h2>Index</h2>") html_content.append("<ul>") for idx, (section_type, item) in enumerate(index_items): link_id = f"table_title_{idx}" if section_type == "Design": item_text = item.split(":")[-1].strip() html_content.append( f"<li><a href='#{link_id}'>{section_type}: {item_text}</a></li>" ) if section_type == "Instance": item_text = item.split(":")[-1].strip() html_content.append( f"<li class='index_instance'><a href='#{link_id}'>" f"{section_type}: {item_text}</a></li>" ) html_content.append("</ul>") html_content.append("</div>") html_content.append("<div class='content'>") if assumptions: html_content.append("<div class='assumptions'><h2></h2><ul>") for item in assumptions: html_content.append(f"<li>{item}</li>") html_content.append("</ul></div>") if tables: html_content.append("<div class='tables-container'>") for idx, (table_title, table) in enumerate(tables): title_id = f"table_title_{idx}" html_content.append( f"<button class='toggle-btn' id='{title_id}' " f"onclick='toggleTable(\"table_{idx}\")'>{table_title}</button>" ) html_content.append( f"<div id='table_{idx}' class='table-content' " f"style='display: block; margin-bottom: 20px;'>" ) html_content.append("<table><thead><tr>") for header in table[0]: html_content.append(f"<th>{header}</th>") html_content.append("</tr></thead><tbody>") for i, row in enumerate(table[1:]): row_color = "#f9f9f9" if i % 2 == 0 else "#e0e0e0" html_content.append(f"<tr style='background-color: {row_color};'>") for idx, cell in enumerate(row): if idx == 4: match = re.search(r"(\d+(\.\d+)?)%", cell) if match: percentage = float(match.group(1)) if 80 <= percentage < 90: html_content.append( f"<td style=background-color:#A5D6A7;color:black>{cell}</td>" ) elif 90 <= percentage <= 100: html_content.append( f"<td style=background-color:#66bb6a;color:black>{cell}</td>" ) elif 60 <= percentage < 80: html_content.append( f"<td style=background-color:#FFF59D;color:black>{cell}</td>" ) elif 30 <= percentage < 60: html_content.append( f"<td style=background-color:#FFEB3B;color:black>{cell}</td>" ) else: html_content.append( f"<td style=background-color:#FF7043;color:black>{cell}</td>" ) else: html_content.append(f"<td>{cell}</td>") else: html_content.append(f"<td>{cell}</td>") html_content.append("</tr>") html_content.append("</tbody></table>") html_content.append("</div>") html_content.append("</div>") html_content.append("<script>") html_content.append("function toggleTable(tableId) {") html_content.append(" var table = document.getElementById(tableId);") html_content.append(" if (table.style.display === 'none') {") html_content.append(" table.style.display = 'block';") html_content.append(" } else {") html_content.append(" table.style.display = 'none';") html_content.append(" }") html_content.append("}") html_content.append("</script>") html_content.append("</div>") html_content.append("</div>") html_content.append("</body>") html_content.append("</html>") with open(output_file, "w", encoding="utf-8") as output: output.write("\n".join(html_content))
[docs] def parse_formal_observability_report_to_html(input_file, output_file="report.html"): """Parses a formal observability report text file and converts it to an HTML file with styling and interactivity.""" with open(input_file, "r", encoding="utf-8") as file: lines = file.readlines() html_content = [] assumptions = [] assertions = [] index_items = [] tables = [] current_section = None legend = [] cover_table = False cover_type_table = [] table_title = "" report_generated = "" for line in lines: stripped_line = line.strip() if stripped_line.startswith("Report Generated :"): report_generated = stripped_line continue if re.search(r"-{2,}", stripped_line): continue if "Legend" in stripped_line: current_section = "Legend" legend = [] continue if current_section == "Legend": if stripped_line == "": current_section = None else: if "Observability Percentage" in stripped_line: text = "Observable/(Total-User Excluded)" legend.append(f"<strong>Observability Percentage</strong>: {text}") else: parts = stripped_line.split(" ", 1) if len(parts) == 2: legend.append(f"<strong>{parts[0]}</strong>: {parts[1]}") if "Assumptions" in stripped_line: current_section = "Assumptions" assumptions = [] match = re.search(r"Assumptions \((\d+)\)", stripped_line) if match: assumptions_count = match.group(1) assumptions.append(f"<h2>Assumptions ({assumptions_count})</h2><ul>") continue if current_section == "Assumptions": if stripped_line == "": current_section = None else: assumptions.append(stripped_line) if "Formal Coverage Report Generated for Proven Targets" in stripped_line: current_section = "Assertions" assertions = [] match = re.search( r"Formal Coverage Report Generated for Proven Targets \((\d+)\)", stripped_line ) if match: assertions_count = match.group(1) assertions.append( f"<h2>Formal Coverage Report Generated for Proven Targets " f"({assertions_count})</h2><ul>" ) if current_section == "Assertions": if stripped_line == "": current_section = None else: assertions.append(stripped_line) if re.match(r"Observability Summary for Design:", stripped_line): table_title = stripped_line current_section = "Design" index_items.append((current_section, table_title)) continue if re.match(r"Observability Summary for Instance:", stripped_line): table_title = stripped_line current_section = "Instance" index_items.append((current_section, table_title)) continue if "Cover Type" in stripped_line and not cover_table: cover_table = True cover_type_table = [["Cover Type", "Total", "Unobservable", "Observable (P)"]] continue if cover_table: if stripped_line == "" or "Total" in stripped_line: if cover_type_table: tables.append((table_title, cover_type_table)) cover_table = False continue row = re.split(r"\s{3,}", stripped_line) if len(row) >= 4: row[3] = re.sub(r"\s*\)$", r" )", row[3]) row = row[:4] if row: cover_type_table.append(row) html_content.append("""<!DOCTYPE html> <html lang='en'> <head> <meta charset='UTF-8'> <meta name='viewport' content='width=device-width, initial-scale=1.0'> <title>Observability Report</title> <link href='https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap' rel='stylesheet'> <style> body { font-family: 'Poppins', sans-serif; margin: 0; padding: 15px; background-color: #f4f4f9; color: #333; line-height: 1.4; font-size: 14px; } h1, h2, h3 { text-align: center; margin-bottom: 8px; font-weight: 600; font-size: 1.5em; } .container { display: flex; height: 100h; overflow: hidden; } .index { flex: 1; max-width: 25%; padding: 15px; background-color: #fff; box-shadow: 0 4px 6px rgba(0,0,0,0.1); border-radius: 8px; overflow-y: scroll; height: 100%; } .content { flex: 4; padding: 15px; background-color: #fff; box-shadow: 0 4px 6px rgba(0,0,0,0.1); border-radius: 8px; overflow-y: scroll; height: 100%; } table { width: 80%; border-collapse: collapse; margin: 20px auto; background-color: #fff; border-radius: 8px; overflow: hidden; box-shadow: 0 4px 8px rgba(0,0,0,0.1); font-size: 14px; } table th, table td { padding: 10px 12px; text-align: center; } table th { background-color: #1976D2; color: white; font-weight: 600; } table tr:nth-child(even) { background-color: #f9f9f9; } table tr:hover { background-color: #f1f1f1; } .legend, .assumptions, .assertions, .index { margin: 20px auto; padding: 15px; width: 75%; text-align: left; background-color: #fff; box-shadow: 0 4px 6px rgba(0,0,0,0.1); border-radius: 8px; font-size: 14px; } .legend ul, .assumptions ul, .assertions ul, .index ul { list-style-type: none; padding: 0; margin: 0; } .legend ul li, .assumptions ul li, .assertions ul li, .index ul li { margin: 8px 0; font-size: 1em; } .legend h2, .assumptions h2, .assertions h2, .index h2 { font-weight: 600; font-size: 1.2em; } .index ul li { margin: 8px 0; font-size: 1em; } .index ul li a { color: #0000EE; text-decoration: none; } .index ul li a:visited { color: #0000EE; } .index ul li a:hover { color: #0000EE; } .index ul li a:active { color: #0000EE; } .index ul li.index_instance { padding-left: 20px; font-style: italic; } .toggle-btn { font-size: 18px; font-weight: bold; color: #333; background: none; border: none; padding: 10px 0; cursor: pointer; text-align: center; width: 100%; margin-bottom: 10px; } .tables-container { display: flex; flex-direction: column; gap: 20px; width: 80%; margin: 0 auto; background-color: transparent; } table { width: 80%; border-collapse: collapse; margin: 20px auto; background-color: #fff; border-radius: 8px; overflow: hidden; box-shadow: 0 4px 8px rgba(0,0,0,0.1); font-size: 14px; } .content .legend, .content .assumptions, .content .tables-container { margin-bottom: 20px; } </style> </head> <body> <h1>Observability Report</h1>""") if report_generated: match = re.match(r"(Report Generated :)(.*)", report_generated) if match: bold_text = match.group(1).strip() normal_text = match.group(2).strip() html_content.append( f"<p style='text-align: center;'><strong>{bold_text}</strong> {normal_text}</p>" ) html_content.append("<div class='container'>") if index_items: html_content.append("<div class='index'>") html_content.append("<h2>Index</h2>") html_content.append("<ul>") for idx, (section_type, item) in enumerate(index_items): link_id = f"table_title_{idx}" if section_type == "Design": item_text = item.split(":")[-1].strip() html_content.append( f"<li><a href='#{link_id}'>{section_type}: {item_text}</a></li>" ) if section_type == "Instance": item_text = item.split(":")[-1].strip() html_content.append( f"<li class='index_instance'><a href='#{link_id}'>" f"{section_type}: {item_text}</a></li>" ) html_content.append("</ul>") html_content.append("</div>") html_content.append("<div class='content'>") if assumptions: html_content.append("<div class='assumptions'><h2></h2><ul>") for item in assumptions: html_content.append(f"<li>{item}</li>") html_content.append("</ul></div>") if assertions: html_content.append("<div class='assertions'><h2></h2><ul>") for item in assertions: html_content.append(f"<li>{item}</li>") html_content.append("</ul></div>") if tables: html_content.append("<div class='tables-container'>") for idx, (table_title, table) in enumerate(tables): title_id = f"table_title_{idx}" html_content.append( f"<button class='toggle-btn' id='{title_id}' " f"onclick='toggleTable(\"table_{idx}\")'>{table_title}</button>" ) html_content.append( f"<div id='table_{idx}' class='table-content' " f"style='display: block; margin-bottom: 20px;'>" ) html_content.append("<table><thead><tr>") for header in table[0]: html_content.append(f"<th>{header}</th>") html_content.append("</tr></thead><tbody>") for i, row in enumerate(table[1:]): row_color = "#f9f9f9" if i % 2 == 0 else "#e0e0e0" html_content.append(f"<tr style='background-color: {row_color};'>") for idx, cell in enumerate(row): if idx == 3: match = re.search(r"(\d+(\.\d+)?)%", cell) if match: percentage = float(match.group(1)) if 80 <= percentage < 90: html_content.append( f"<td style=background-color:#A5D6A7;color:black>{cell}</td>" ) elif 90 <= percentage <= 100: html_content.append( f"<td style=background-color:#66bb6a;color:black>{cell}</td>" ) elif 60 <= percentage < 80: html_content.append( f"<td style=background-color:#FFF59D;color:black>{cell}</td>" ) elif 30 <= percentage < 60: html_content.append( f"<td style=background-color:#FFEB3B;color:black>{cell}</td>" ) else: html_content.append( f"<td style=background-color:#FF7043;color:black>{cell}</td>" ) else: html_content.append(f"<td>{cell}</td>") else: html_content.append(f"<td>{cell}</td>") html_content.append("</tr>") html_content.append("</tbody></table>") html_content.append("</div>") html_content.append("</div>") html_content.append("<script>") html_content.append("function toggleTable(tableId) {") html_content.append(" var table = document.getElementById(tableId);") html_content.append(" if (table.style.display === 'none') {") html_content.append(" table.style.display = 'block';") html_content.append(" } else {") html_content.append(" table.style.display = 'none';") html_content.append(" }") html_content.append("}") html_content.append("</script>") html_content.append("</div>") html_content.append("</div>") html_content.append("</body></html>") with open(output_file, "w", encoding="utf-8") as output: output.write("\n".join(html_content))
[docs] def parse_reachability_report_to_html(input_file, output_file="report.html"): """Parses a reachability report text file and converts it to an HTML file with styling and interactivity.""" with open(input_file, "r", encoding="utf-8") as file: lines = file.readlines() html_content = [] tables = [] cover_table = False cover_type_table = [] table_title = "" report_generated = "" for line in lines: stripped_line = line.strip() if stripped_line.startswith("Report Generated :"): report_generated = stripped_line continue if re.search(r"-{2,}", stripped_line): continue if re.match(r"Summary", stripped_line): table_title = stripped_line continue if "Coverage Type" in stripped_line and not cover_table: cover_table = True cover_type_table = [["Coverage Type","Active","Witness","Inconclusive","Unreachable"]] continue if cover_table: if stripped_line == "" or "Total" in stripped_line: if cover_type_table: tables.append((table_title, cover_type_table)) cover_table = False continue row = re.split(r"\s{3,}", stripped_line) if len(row) >= 5: row[4] = re.sub(r"\s*\)$", r" )", row[4]) row = row[:5] if row: cover_type_table.append(row) html_content.append("""<!DOCTYPE html> <html lang='en'> <head> <meta charset='UTF-8'> <meta name='viewport' content='width=device-width, initial-scale=1.0'> <title>CoverCheck Reachability Report</title> <link href='https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap' rel='stylesheet'> <style> body { font-family: 'Poppins', sans-serif; margin: 0; padding: 15px; background-color: #f4f4f9; color: #333; line-height: 1.4; font-size: 14px; } h1, h2, h3 { text-align: center; margin-bottom: 8px; font-weight: 600; font-size: 1.5em; } table { width: 80%; border-collapse: collapse; margin: 20px auto; background-color: #fff; border-radius: 8px; overflow: hidden; box-shadow: 0 4px 8px rgba(0,0,0,0.1); font-size: 14px; } table th, table td { padding: 10px 12px; text-align: center; } table th { background-color: #1976D2; color: white; font-weight: 600; } table tr:nth-child(even) { background-color: #f9f9f9; } table tr:hover { background-color: #f1f1f1; } .legend, .assumptions { margin: 20px auto; padding: 15px; width: 75%; text-align: left; background-color: #fff; box-shadow: 0 4px 6px rgba(0,0,0,0.1); border-radius: 8px; font-size: 14px; } .legend ul, .assumptions ul { list-style-type: none; padding: 0; margin: 0; } .legend ul li, .assumptions ul li { margin: 8px 0; font-size: 1em; } .legend h2, .assumptions h2 { font-weight: 600; font-size: 1.2em; } </style> </head> <body> <h1>CoverCheck Reachability Report</h1>""") if report_generated: match = re.match(r"(Report Generated :)(.*)", report_generated) if match: bold_text = match.group(1).strip() normal_text = match.group(2).strip() html_content.append( f"<p style='text-align: center;'><strong>{bold_text}</strong> {normal_text}</p>" ) for table_title, table in tables: for line in table_title.split("\n"): html_content.append(f"<h2>{line}</h2>") html_content.append("<table><thead><tr>") for header in table[0]: html_content.append(f"<th>{header}</th>") html_content.append("</tr></thead><tbody>") for i, row in enumerate(table[1:]): row_color = "#f9f9f9" if i % 2 == 0 else "#e0e0e0" html_content.append(f"<tr style='background-color: {row_color};'>") for idx, cell in enumerate(row): if idx == 4: match = re.search(r"(\d+(\.\d+)?)%", cell) if match: percentage = float(match.group(1)) if 80 <= percentage < 90: html_content.append( f"<td style=background-color:#A5D6A7;color:black>{cell}</td>" ) elif 90 <= percentage <= 100: html_content.append( f"<td style=background-color:#66bb6a;color:black>{cell}</td>" ) elif 60 <= percentage < 80: html_content.append( f"<td style=background-color:#FFF59D;color:black>{cell}</td>" ) elif 30 <= percentage < 60: html_content.append( f"<td style=background-color:#FFEB3B;color:black>{cell}</td>" ) else: html_content.append( f"<td style=background-color:#FF7043;color:black>{cell}</td>" ) else: html_content.append(f"<td>{cell}</td>") else: html_content.append(f"<td>{cell}</td>") html_content.append("</tr>") html_content.append("</tbody></table>") html_content.append("</body>") html_content.append("</html>") with open(output_file, "w", encoding="utf-8") as output: output.write("\n".join(html_content))
[docs] def parse_formal_signoff_report_to_html(input_file, output_file="report.html"): """Parses a formal signoff report text file and converts it to an HTML file with styling and interactivity.""" with open(input_file, "r", encoding="utf-8") as file: lines = file.readlines() html_content = [] assumptions = [] index_items = [] assertions = [] tables = [] current_section = None legend = [] cover_table = False cover_type_table = [] table_title = "" report_generated = "" for line in lines: stripped_line = line.strip() if stripped_line.startswith("Report Generated :"): report_generated = stripped_line continue if re.search(r"-{2,}", stripped_line): continue if "Legend" in stripped_line: current_section = "Legend" legend = [] continue if current_section == "Legend": if stripped_line == "": current_section = None else: if "Covered Percentage" in stripped_line: text = "Covered / (Total - Excluded)" legend.append(f"<strong>Covered Percentage</strong>: {text}") else: parts = stripped_line.split(" ", 1) if len(parts) == 2: legend.append(f"<strong>{parts[0]}</strong>: {parts[1]}") if "Assumptions" in stripped_line: current_section = "Assumptions" assumptions = [] match = re.search(r"Assumptions \((\d+)\)", stripped_line) if match: assumptions_count = match.group(1) assumptions.append(f"<h2>Assumptions ({assumptions_count})</h2><ul>") continue if current_section == "Assumptions": if stripped_line == "": current_section = None else: assumptions.append(stripped_line) if re.match(r"Formal Coverage Summary for Design:", stripped_line): table_title = stripped_line current_section = "Design" index_items.append((current_section, table_title)) continue if re.match(r"Formal Coverage Summary for Instance:", stripped_line): table_title = stripped_line current_section = "Instance" index_items.append((current_section, table_title)) continue if "Formal Coverage Report Generated for Proven Targets" in stripped_line: current_section = "Assertions" assertions = [] match = re.search( r"Formal Coverage Report Generated for Proven Targets \((\d+)\)", stripped_line ) if match: assertions_count = match.group(1) assertions.append( f"<h2>Formal Coverage Report Generated for Proven Targets " f"({assertions_count})</h2><ul>" ) if current_section == "Assertions": if stripped_line == "": current_section = None else: assertions.append(stripped_line) if "Coverage Type" in stripped_line and not cover_table: cover_table = True cover_type_table = [] continue if cover_table: if stripped_line == "" or "Total" in stripped_line: if cover_type_table: tables.append((table_title, cover_type_table)) cover_table = False continue row = re.split(r"\s{3,}", stripped_line) if len(row) >= 4: if not cover_type_table: if len(row) == 5: cover_type_table.append([ "Coverage Type", "Total", "Uncovered", "Excluded", "Covered (P)" ]) elif len(row) == 4: cover_type_table.append([ "Coverage Type", "Total", "Uncovered", "Covered (P)" ]) cover_type_table.append(row) html_content.append("""<!DOCTYPE html> <html lang='en'> <head> <meta charset='UTF-8'> <meta name='viewport' content='width=device-width, initial-scale=1.0'> <title>Signoff Report</title> <link href='https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap' rel='stylesheet'> <style> body { font-family: 'Poppins', sans-serif; margin: 0; padding: 15px; background-color: #f4f4f9; color: #333; line-height: 1.4; font-size: 14px; } h1, h2, h3 { text-align: center; margin-bottom: 8px; font-weight: 600; font-size: 1.5em; } .container { display: flex; height: 100h; overflow: hidden; } .index { flex: 1; max-width: 25%; padding: 15px; background-color: #fff; box-shadow: 0 4px 6px rgba(0,0,0,0.1); border-radius: 8px; overflow-y: scroll; height: 100%; } .content { flex: 4; padding: 15px; background-color: #fff; box-shadow: 0 4px 6px rgba(0,0,0,0.1); border-radius: 8px; overflow-y: scroll; height: 100%; } table { width: 80%; border-collapse: collapse; margin: 20px auto; background-color: #fff; border-radius: 8px; overflow: hidden; box-shadow: 0 4px 8px rgba(0,0,0,0.1); font-size: 14px; } table th, table td { padding: 10px 12px; text-align: center; } table th { background-color: #1976D2; color: white; font-weight: 600; } table tr:nth-child(even) { background-color: #f9f9f9; } table tr:hover { background-color: #f1f1f1; } .legend, .assumptions, .assertions, .index { margin: 20px auto; padding: 15px; width: 75%; text-align: left; background-color: #fff; box-shadow: 0 4px 6px rgba(0,0,0,0.1); border-radius: 8px; font-size: 14px; } .legend ul, .assumptions ul, .assertions ul, .index ul { list-style-type: none; padding: 0; margin: 0; } .legend ul li, .assumptions ul li, .assertions ul li, .index ul li { margin: 8px 0; font-size: 1em; } .legend h2, .assumptions h2, .assertions h2, .index h2 { font-weight: 600; font-size: 1.2em; } .index ul li { margin: 8px 0; font-size: 1em; } .index ul li a { color: #0000EE; text-decoration: none; } .index ul li a:visited { color: #0000EE; } .index ul li a:hover { color: #0000EE; } .index ul li a:active { color: #0000EE; } .index ul li.index_instance { padding-left: 20px; font-style: italic; } .toggle-btn { font-size: 18px; font-weight: bold; color: #333; background: none; border: none; padding: 10px 0; cursor: pointer; text-align: center; width: 100%; margin-bottom: 10px; } .tables-container { display: flex; flex-direction: column; gap: 20px; width: 80%; margin: 0 auto; background-color: transparent; } .content .legend, .content .assumptions, .content .tables-container { margin-bottom: 20px; } </style> </head> <body> <h1>Signoff Report</h1>""") if report_generated: match = re.match(r"(Report Generated :)(.*)", report_generated) if match: bold_text = match.group(1).strip() normal_text = match.group(2).strip() html_content.append( f"<p style='text-align: center;'><strong>{bold_text}</strong> {normal_text}</p>" ) html_content.append("<div class='container'>") if index_items: html_content.append("<div class='index'>") html_content.append("<h2>Index</h2>") html_content.append("<ul>") for idx, (section_type, item) in enumerate(index_items): link_id = f"table_title_{idx}" if section_type == "Design": item_text = item.split(":")[-1].strip() html_content.append( f"<li><a href='#{link_id}'>{section_type}: {item_text}</a></li>" ) if section_type == "Instance": item_text = item.split(":")[-1].strip() html_content.append( f"<li class='index_instance'><a href='#{link_id}'>" f"{section_type}: {item_text}</a></li>" ) html_content.append("</ul>") html_content.append("</div>") html_content.append("<div class='content'>") if assumptions: html_content.append("<div class='assumptions'><h2></h2><ul>") for item in assumptions: html_content.append(f"<li>{item}</li>") html_content.append("</ul></div>") if assertions: html_content.append("<div class='assertions'><h2></h2><ul>") for item in assertions: html_content.append(f"<li>{item}</li>") html_content.append("</ul></div>") if tables: html_content.append("<div class='tables-container'>") for idx, (table_title, table) in enumerate(tables): title_id = f"table_title_{idx}" html_content.append( f"<button class='toggle-btn' id='{title_id}' " f"onclick='toggleTable(\"table_{idx}\")'>{table_title}</button>" ) html_content.append( f"<div id='table_{idx}' class='table-content' " f"style='display: block; margin-bottom: 20px;'>" ) html_content.append("<table><thead><tr>") for header in table[0]: html_content.append(f"<th>{header}</th>") html_content.append("</tr></thead><tbody>") for i, row in enumerate(table[1:]): row_color = "#f9f9f9" if i % 2 == 0 else "#e0e0e0" html_content.append(f"<tr style='background-color: {row_color};'>") for idx, cell in enumerate(row): if idx in (3, 4): match = re.search(r"(\d+(\.\d+)?)%", cell) if match: percentage = float(match.group(1)) if 80 <= percentage < 90: html_content.append( f"<td style=background-color:#A5D6A7;color:black>{cell}</td>" ) elif 90 <= percentage <= 100: html_content.append( f"<td style=background-color:#66bb6a;color:black>{cell}</td>" ) elif 60 <= percentage < 80: html_content.append( f"<td style=background-color:#FFF59D;color:black>{cell}</td>" ) elif 30 <= percentage < 60: html_content.append( f"<td style=background-color:#FFEB3B;color:black>{cell}</td>" ) else: html_content.append( f"<td style=background-color:#FF7043;color:black>{cell}</td>" ) else: html_content.append(f"<td>{cell}</td>") else: html_content.append(f"<td>{cell}</td>") html_content.append("</tr>") html_content.append("</tbody></table>") html_content.append("</div>") html_content.append("</div>") html_content.append("<script>") html_content.append("function toggleTable(tableId) {") html_content.append(" var table = document.getElementById(tableId);") html_content.append(" if (table.style.display === 'none') {") html_content.append(" table.style.display = 'block';") html_content.append(" } else {") html_content.append(" table.style.display = 'none';") html_content.append(" }") html_content.append("}") html_content.append("</script>") html_content.append("</div>") html_content.append("</div>") html_content.append("</body>") html_content.append("</html>") with open(output_file, "w", encoding="utf-8") as output: output.write("\n".join(html_content))