+ rows = list()
+ for itm in tbl_lst:
+ rows.append([
+ itm[u"name"],
+ f"{len(itm[u'data'])}",
+ f"{itm[u'mean']} +- {itm[u'stdev']}"
+ if itm[u"stdev"] != u"" else f"{itm[u'mean']}"
+ ])
+
+ txt_table = prettytable.PrettyTable(
+ [u"Job Specification", u"Nr of Runs", u"Duration [HH:MM]"]
+ )
+ for row in rows:
+ txt_table.add_row(row)
+ txt_table.align = u"r"
+ txt_table.align[u"Job Specification"] = u"l"
+
+ file_name = f"{table.get(u'output-file', u'')}.txt"
+ with open(file_name, u"wt", encoding='utf-8') as txt_file:
+ txt_file.write(str(txt_table))
+
+
+def table_oper_data_html(table, input_data):
+ """Generate the table(s) with algorithm: html_table_oper_data
+ specified in the specification file.
+
+ :param table: Table to generate.
+ :param input_data: Data to process.
+ :type table: pandas.Series
+ :type input_data: InputData
+ """
+
+ logging.info(f" Generating the table {table.get(u'title', u'')} ...")
+ # Transform the data
+ logging.info(
+ f" Creating the data set for the {table.get(u'type', u'')} "
+ f"{table.get(u'title', u'')}."
+ )
+ data = input_data.filter_data(
+ table,
+ params=[u"name", u"parent", u"telemetry-show-run", u"type"],
+ continue_on_error=True
+ )
+ if data.empty:
+ return
+ data = input_data.merge_data(data)
+
+ sort_tests = table.get(u"sort", None)
+ if sort_tests:
+ args = dict(
+ inplace=True,
+ ascending=(sort_tests == u"ascending")
+ )
+ data.sort_index(**args)
+
+ suites = input_data.filter_data(
+ table,
+ continue_on_error=True,
+ data_set=u"suites"
+ )
+ if suites.empty:
+ return
+ suites = input_data.merge_data(suites)
+
+ def _generate_html_table(tst_data):
+ """Generate an HTML table with operational data for the given test.
+
+ :param tst_data: Test data to be used to generate the table.
+ :type tst_data: pandas.Series
+ :returns: HTML table with operational data.
+ :rtype: str
+ """
+
+ colors = {
+ u"header": u"#7eade7",
+ u"empty": u"#ffffff",
+ u"body": (u"#e9f1fb", u"#d4e4f7")
+ }
+
+ tbl = ET.Element(u"table", attrib=dict(width=u"100%", border=u"0"))
+
+ trow = ET.SubElement(tbl, u"tr", attrib=dict(bgcolor=colors[u"header"]))
+ thead = ET.SubElement(
+ trow, u"th", attrib=dict(align=u"left", colspan=u"6")
+ )
+ thead.text = tst_data[u"name"]
+
+ trow = ET.SubElement(tbl, u"tr", attrib=dict(bgcolor=colors[u"empty"]))
+ thead = ET.SubElement(
+ trow, u"th", attrib=dict(align=u"left", colspan=u"6")
+ )
+ thead.text = u"\t"
+
+ if tst_data.get(u"telemetry-show-run", None) is None or \
+ isinstance(tst_data[u"telemetry-show-run"], str):
+ trow = ET.SubElement(
+ tbl, u"tr", attrib=dict(bgcolor=colors[u"header"])
+ )
+ tcol = ET.SubElement(
+ trow, u"td", attrib=dict(align=u"left", colspan=u"6")
+ )
+ tcol.text = u"No Data"
+
+ trow = ET.SubElement(
+ tbl, u"tr", attrib=dict(bgcolor=colors[u"empty"])
+ )
+ thead = ET.SubElement(
+ trow, u"th", attrib=dict(align=u"left", colspan=u"6")
+ )
+ font = ET.SubElement(
+ thead, u"font", attrib=dict(size=u"12px", color=u"#ffffff")
+ )
+ font.text = u"."
+ return str(ET.tostring(tbl, encoding=u"unicode"))
+
+ tbl_hdr = (
+ u"Name",
+ u"Nr of Vectors",
+ u"Nr of Packets",
+ u"Suspends",
+ u"Cycles per Packet",
+ u"Average Vector Size"
+ )
+
+ for dut_data in tst_data[u"telemetry-show-run"].values():
+ trow = ET.SubElement(
+ tbl, u"tr", attrib=dict(bgcolor=colors[u"header"])
+ )
+ tcol = ET.SubElement(
+ trow, u"td", attrib=dict(align=u"left", colspan=u"6")
+ )
+ if dut_data.get(u"runtime", None) is None:
+ tcol.text = u"No Data"
+ continue
+
+ runtime = dict()
+ for item in dut_data[u"runtime"].get(u"data", tuple()):
+ tid = int(item[u"labels"][u"thread_id"])
+ if runtime.get(tid, None) is None:
+ runtime[tid] = dict()
+ gnode = item[u"labels"][u"graph_node"]
+ if runtime[tid].get(gnode, None) is None:
+ runtime[tid][gnode] = dict()
+ try:
+ runtime[tid][gnode][item[u"name"]] = float(item[u"value"])
+ except ValueError:
+ runtime[tid][gnode][item[u"name"]] = item[u"value"]
+
+ threads = dict({idx: list() for idx in range(len(runtime))})
+ for idx, run_data in runtime.items():
+ for gnode, gdata in run_data.items():
+ threads[idx].append([
+ gnode,
+ int(gdata[u"calls"]),
+ int(gdata[u"vectors"]),
+ int(gdata[u"suspends"]),
+ float(gdata[u"clocks"]),
+ float(gdata[u"vectors"] / gdata[u"calls"]) \
+ if gdata[u"calls"] else 0.0
+ ])
+
+ bold = ET.SubElement(tcol, u"b")
+ bold.text = (
+ f"Host IP: {dut_data.get(u'host', '')}, "
+ f"Socket: {dut_data.get(u'socket', '')}"
+ )
+ trow = ET.SubElement(
+ tbl, u"tr", attrib=dict(bgcolor=colors[u"empty"])
+ )
+ thead = ET.SubElement(
+ trow, u"th", attrib=dict(align=u"left", colspan=u"6")
+ )
+ thead.text = u"\t"
+
+ for thread_nr, thread in threads.items():
+ trow = ET.SubElement(
+ tbl, u"tr", attrib=dict(bgcolor=colors[u"header"])
+ )
+ tcol = ET.SubElement(
+ trow, u"td", attrib=dict(align=u"left", colspan=u"6")
+ )
+ bold = ET.SubElement(tcol, u"b")
+ bold.text = u"main" if thread_nr == 0 else f"worker_{thread_nr}"
+ trow = ET.SubElement(
+ tbl, u"tr", attrib=dict(bgcolor=colors[u"header"])
+ )
+ for idx, col in enumerate(tbl_hdr):
+ tcol = ET.SubElement(
+ trow, u"td",
+ attrib=dict(align=u"right" if idx else u"left")
+ )
+ font = ET.SubElement(
+ tcol, u"font", attrib=dict(size=u"2")
+ )
+ bold = ET.SubElement(font, u"b")
+ bold.text = col
+ for row_nr, row in enumerate(thread):
+ trow = ET.SubElement(
+ tbl, u"tr",
+ attrib=dict(bgcolor=colors[u"body"][row_nr % 2])
+ )
+ for idx, col in enumerate(row):
+ tcol = ET.SubElement(
+ trow, u"td",
+ attrib=dict(align=u"right" if idx else u"left")
+ )
+ font = ET.SubElement(
+ tcol, u"font", attrib=dict(size=u"2")
+ )
+ if isinstance(col, float):
+ font.text = f"{col:.2f}"
+ else:
+ font.text = str(col)
+ trow = ET.SubElement(
+ tbl, u"tr", attrib=dict(bgcolor=colors[u"empty"])
+ )
+ thead = ET.SubElement(
+ trow, u"th", attrib=dict(align=u"left", colspan=u"6")
+ )
+ thead.text = u"\t"
+
+ trow = ET.SubElement(tbl, u"tr", attrib=dict(bgcolor=colors[u"empty"]))
+ thead = ET.SubElement(
+ trow, u"th", attrib=dict(align=u"left", colspan=u"6")
+ )
+ font = ET.SubElement(
+ thead, u"font", attrib=dict(size=u"12px", color=u"#ffffff")
+ )
+ font.text = u"."
+
+ return str(ET.tostring(tbl, encoding=u"unicode"))
+
+ for suite in suites.values:
+ html_table = str()
+ for test_data in data.values:
+ if test_data[u"parent"] not in suite[u"name"]:
+ continue
+ html_table += _generate_html_table(test_data)
+ if not html_table:
+ continue
+ try:
+ file_name = f"{table[u'output-file']}{suite[u'name']}.rst"
+ with open(f"{file_name}", u'w') as html_file:
+ logging.info(f" Writing file: {file_name}")
+ html_file.write(u".. raw:: html\n\n\t")
+ html_file.write(html_table)
+ html_file.write(u"\n\t<p><br><br></p>\n")
+ except KeyError:
+ logging.warning(u"The output file is not defined.")
+ return
+ logging.info(u" Done.")
+
+
+def table_merged_details(table, input_data):
+ """Generate the table(s) with algorithm: table_merged_details
+ specified in the specification file.
+
+ :param table: Table to generate.
+ :param input_data: Data to process.
+ :type table: pandas.Series
+ :type input_data: InputData
+ """
+
+ logging.info(f" Generating the table {table.get(u'title', u'')} ...")
+
+ # Transform the data
+ logging.info(
+ f" Creating the data set for the {table.get(u'type', u'')} "
+ f"{table.get(u'title', u'')}."
+ )
+ data = input_data.filter_data(table, continue_on_error=True)
+ data = input_data.merge_data(data)
+
+ sort_tests = table.get(u"sort", None)
+ if sort_tests:
+ args = dict(
+ inplace=True,
+ ascending=(sort_tests == u"ascending")
+ )
+ data.sort_index(**args)
+
+ suites = input_data.filter_data(
+ table, continue_on_error=True, data_set=u"suites")
+ suites = input_data.merge_data(suites)
+
+ # Prepare the header of the tables
+ header = list()
+ for column in table[u"columns"]:
+ header.append(
+ u'"{0}"'.format(str(column[u"title"]).replace(u'"', u'""'))
+ )
+
+ for suite in suites.values: