C-Dash: Add latency hdrh for iterative data 23/38623/3
authorTibor Frank <tifrank@cisco.com>
Tue, 11 Apr 2023 10:56:56 +0000 (12:56 +0200)
committerTibor Frank <tifrank@cisco.com>
Wed, 12 Apr 2023 05:49:41 +0000 (05:49 +0000)
Signed-off-by: Tibor Frank <tifrank@cisco.com>
Change-Id: I4557c724d1d2b2eab355d03f0339c56660253360

csit.infra.dash/app/cdash/data/data.yaml
csit.infra.dash/app/cdash/report/graphs.py
csit.infra.dash/app/cdash/report/layout.py
csit.infra.dash/app/cdash/trending/graphs.py
csit.infra.dash/app/cdash/trending/layout.py
csit.infra.dash/app/cdash/utils/utils.py

index 7a03e13..4e78323 100644 (file)
     - result_pdr_lower_rate_value
     - result_ndr_lower_rate_unit
     - result_ndr_lower_rate_value
+    - result_latency_reverse_pdr_90_hdrh
+    - result_latency_reverse_pdr_50_hdrh
+    - result_latency_reverse_pdr_10_hdrh
+    - result_latency_reverse_pdr_0_hdrh
+    - result_latency_forward_pdr_90_hdrh
     - result_latency_forward_pdr_50_avg
+    - result_latency_forward_pdr_50_hdrh
     - result_latency_forward_pdr_50_unit
+    - result_latency_forward_pdr_10_hdrh
+    - result_latency_forward_pdr_0_hdrh
   categories:
     - job
     - build
     - result_pdr_lower_rate_value
     - result_ndr_lower_rate_unit
     - result_ndr_lower_rate_value
+    - result_latency_reverse_pdr_90_hdrh
+    - result_latency_reverse_pdr_50_hdrh
+    - result_latency_reverse_pdr_10_hdrh
+    - result_latency_reverse_pdr_0_hdrh
+    - result_latency_forward_pdr_90_hdrh
     - result_latency_forward_pdr_50_avg
+    - result_latency_forward_pdr_50_hdrh
     - result_latency_forward_pdr_50_unit
+    - result_latency_forward_pdr_10_hdrh
+    - result_latency_forward_pdr_0_hdrh
   categories:
     - job
     - build
     - result_pdr_lower_rate_value
     - result_ndr_lower_rate_unit
     - result_ndr_lower_rate_value
+    - result_latency_reverse_pdr_90_hdrh
+    - result_latency_reverse_pdr_50_hdrh
+    - result_latency_reverse_pdr_10_hdrh
+    - result_latency_reverse_pdr_0_hdrh
+    - result_latency_forward_pdr_90_hdrh
     - result_latency_forward_pdr_50_avg
+    - result_latency_forward_pdr_50_hdrh
     - result_latency_forward_pdr_50_unit
+    - result_latency_forward_pdr_10_hdrh
+    - result_latency_forward_pdr_0_hdrh
   categories:
     - job
     - build
index 544fdf0..f45e17f 100644 (file)
@@ -20,7 +20,7 @@ import pandas as pd
 from copy import deepcopy
 
 from ..utils.constants import Constants as C
-from ..utils.utils import get_color
+from ..utils.utils import get_color, get_hdrh_latencies
 
 
 def select_iterative_data(data: pd.DataFrame, itm:dict) -> pd.DataFrame:
@@ -145,6 +145,12 @@ def graph_iterative(data: pd.DataFrame, sel:dict, layout: dict,
         show_tput = True
 
         if ttype == "pdr":
+            customdata = list()
+            for _, row in itm_data.iterrows():
+                customdata.append(
+                    get_hdrh_latencies(row, f"{row['job']}/{row['build']}")
+                )
+
             y_lat_row = itm_data[C.VALUE_ITER["latency"]].to_list()
             y_lat = [(y / norm_factor) for y in y_lat_row]
             if y_lat:
@@ -164,7 +170,8 @@ def graph_iterative(data: pd.DataFrame, sel:dict, layout: dict,
                 hoverinfo="all",
                 boxpoints="all",
                 jitter=0.3,
-                marker=dict(color=get_color(idx))
+                marker=dict(color=get_color(idx)),
+                customdata=customdata
             )
             x_lat.append(idx + 1)
             lat_traces.append(go.Box(**lat_kwargs))
index 2f954b6..8dbaea3 100644 (file)
@@ -31,7 +31,7 @@ from ..utils.constants import Constants as C
 from ..utils.control_panel import ControlPanel
 from ..utils.trigger import Trigger
 from ..utils.utils import show_tooltip, label, sync_checklists, gen_new_url, \
-    generate_options, get_list_group_items
+    generate_options, get_list_group_items, graph_hdrh_latency
 from ..utils.url_processing import url_decode
 from .graphs import graph_iterative, select_iterative_data
 
@@ -279,6 +279,20 @@ class Layout:
                             self._add_ctrl_col(),
                             self._add_plotting_col()
                         ]
+                    ),
+                    dbc.Spinner(
+                        dbc.Offcanvas(
+                            class_name="w-50",
+                            id="offcanvas-metadata",
+                            title="Throughput And Latency",
+                            placement="end",
+                            is_open=False,
+                            children=[
+                                dbc.Row(id="metadata-tput-lat"),
+                                dbc.Row(id="metadata-hdrh-graph")
+                            ]
+                        ),
+                        delay_show=C.SPINNER_DELAY
                     )
                 ]
             )
@@ -1320,3 +1334,112 @@ class Layout:
                 df = pd.concat([df, sel_data], ignore_index=True)
 
             return dcc.send_data_frame(df.to_csv, C.REPORT_DOWNLOAD_FILE_NAME)
+
+        @app.callback(
+            Output("metadata-tput-lat", "children"),
+            Output("metadata-hdrh-graph", "children"),
+            Output("offcanvas-metadata", "is_open"),
+            Input({"type": "graph", "index": ALL}, "clickData"),
+            prevent_initial_call=True
+        )
+        def _show_metadata_from_graphs(graph_data: dict) -> tuple:
+            """Generates the data for the offcanvas displayed when a particular
+            point in a graph is clicked on.
+
+            :param graph_data: The data from the clicked point in the graph.
+            :type graph_data: dict
+            :returns: The data to be displayed on the offcanvas and the
+                information to show the offcanvas.
+            :rtype: tuple(list, list, bool)
+            """
+
+            trigger = Trigger(callback_context.triggered)
+
+            try:
+                idx = 0 if trigger.idx == "tput" else 1
+                graph_data = graph_data[idx]["points"]
+            except (IndexError, KeyError, ValueError, TypeError):
+                raise PreventUpdate
+
+            def _process_stats(data: list, param: str) -> list:
+                """Process statistical data provided by plot.ly box graph.
+
+                :param data: Statistical data provided by plot.ly box graph.
+                :param param: Parameter saying if the data come from "tput" or
+                    "lat" graph.
+                :type data: list
+                :type param: str
+                :returns: Listo of tuples where the first value is the
+                    statistic's name and the secont one it's value.
+                :rtype: list
+                """
+                if len(data) == 7:
+                    stats = ("max", "upper fence", "q3", "median", "q1",
+                            "lower fence", "min")
+                elif len(data) == 9:
+                    stats = ("outlier", "max", "upper fence", "q3", "median",
+                            "q1", "lower fence", "min", "outlier")
+                elif len(data) == 1:
+                    if param == "lat":
+                        stats = ("Average Latency at 50% PDR", )
+                    else:
+                        stats = ("Throughput", )
+                else:
+                    return list()
+                unit = " [us]" if param == "lat" else str()
+                return [(f"{stat}{unit}", f"{value['y']:,.0f}")
+                        for stat, value in zip(stats, data)]
+
+            graph = list()
+            if trigger.idx == "tput":
+                title = "Throughput"
+            elif trigger.idx == "lat":
+                title = "Latency"
+                if len(graph_data) == 1:
+                    hdrh_data = graph_data[0].get("customdata", None)
+                    if hdrh_data:
+                        graph = [dbc.Card(
+                            class_name="gy-2 p-0",
+                            children=[
+                                dbc.CardHeader(hdrh_data.pop("name")),
+                                dbc.CardBody(children=[
+                                    dcc.Graph(
+                                        id="hdrh-latency-graph",
+                                        figure=graph_hdrh_latency(
+                                            hdrh_data, self._graph_layout
+                                        )
+                                    )
+                                ])
+                            ])
+                        ]
+            else:
+                raise PreventUpdate
+            metadata = [
+                dbc.Card(
+                    class_name="gy-2 p-0",
+                    children=[
+                        dbc.CardHeader(children=[
+                            dcc.Clipboard(
+                                target_id="tput-lat-metadata",
+                                title="Copy",
+                                style={"display": "inline-block"}
+                            ),
+                            title
+                        ]),
+                        dbc.CardBody(
+                            id="tput-lat-metadata",
+                            class_name="p-0",
+                            children=[dbc.ListGroup(
+                                [
+                                    dbc.ListGroupItem([dbc.Badge(k), v])
+                                        for k, v in _process_stats(
+                                            graph_data, trigger.idx)
+                                ],
+                                flush=True)
+                            ]
+                        )
+                    ]
+                )
+            ]
+
+            return metadata, graph, True
index 10ad745..fc26f8b 100644 (file)
 import plotly.graph_objects as go
 import pandas as pd
 
-import hdrh.histogram
-import hdrh.codec
-
 from ..utils.constants import Constants as C
-from ..utils.utils import classify_anomalies, get_color
-
-
-def _get_hdrh_latencies(row: pd.Series, name: str) -> dict:
-    """Get the HDRH latencies from the test data.
-
-    :param row: A row fron the data frame with test data.
-    :param name: The test name to be displayed as the graph title.
-    :type row: pandas.Series
-    :type name: str
-    :returns: Dictionary with HDRH latencies.
-    :rtype: dict
-    """
-
-    latencies = {"name": name}
-    for key in C.LAT_HDRH:
-        try:
-            latencies[key] = row[key]
-        except KeyError:
-            return None
-
-    return latencies
+from ..utils.utils import classify_anomalies, get_color, get_hdrh_latencies
 
 
 def select_trending_data(data: pd.DataFrame, itm: dict) -> pd.DataFrame:
@@ -189,7 +165,7 @@ def graph_trending(
             ).replace("<stdev>", stdev).replace("<additional-info>", add_info)
             hover.append(hover_itm)
             if ttype == "latency":
-                customdata_samples.append(_get_hdrh_latencies(row, name))
+                customdata_samples.append(get_hdrh_latencies(row, name))
                 customdata.append({"name": name})
             else:
                 customdata_samples.append(
@@ -367,83 +343,6 @@ def graph_trending(
     return fig_tput, fig_lat
 
 
-def graph_hdrh_latency(data: dict, layout: dict) -> go.Figure:
-    """Generate HDR Latency histogram graphs.
-
-    :param data: HDRH data.
-    :param layout: Layout of plot.ly graph.
-    :type data: dict
-    :type layout: dict
-    :returns: HDR latency Histogram.
-    :rtype: plotly.graph_objects.Figure
-    """
-
-    fig = None
-
-    traces = list()
-    for idx, (lat_name, lat_hdrh) in enumerate(data.items()):
-        try:
-            decoded = hdrh.histogram.HdrHistogram.decode(lat_hdrh)
-        except (hdrh.codec.HdrLengthException, TypeError):
-            continue
-        previous_x = 0.0
-        prev_perc = 0.0
-        xaxis = list()
-        yaxis = list()
-        hovertext = list()
-        for item in decoded.get_recorded_iterator():
-            # The real value is "percentile".
-            # For 100%, we cut that down to "x_perc" to avoid
-            # infinity.
-            percentile = item.percentile_level_iterated_to
-            x_perc = min(percentile, C.PERCENTILE_MAX)
-            xaxis.append(previous_x)
-            yaxis.append(item.value_iterated_to)
-            hovertext.append(
-                f"<b>{C.GRAPH_LAT_HDRH_DESC[lat_name]}</b><br>"
-                f"Direction: {('W-E', 'E-W')[idx % 2]}<br>"
-                f"Percentile: {prev_perc:.5f}-{percentile:.5f}%<br>"
-                f"Latency: {item.value_iterated_to}uSec"
-            )
-            next_x = 100.0 / (100.0 - x_perc)
-            xaxis.append(next_x)
-            yaxis.append(item.value_iterated_to)
-            hovertext.append(
-                f"<b>{C.GRAPH_LAT_HDRH_DESC[lat_name]}</b><br>"
-                f"Direction: {('W-E', 'E-W')[idx % 2]}<br>"
-                f"Percentile: {prev_perc:.5f}-{percentile:.5f}%<br>"
-                f"Latency: {item.value_iterated_to}uSec"
-            )
-            previous_x = next_x
-            prev_perc = percentile
-
-        traces.append(
-            go.Scatter(
-                x=xaxis,
-                y=yaxis,
-                name=C.GRAPH_LAT_HDRH_DESC[lat_name],
-                mode="lines",
-                legendgroup=C.GRAPH_LAT_HDRH_DESC[lat_name],
-                showlegend=bool(idx % 2),
-                line=dict(
-                    color=get_color(int(idx/2)),
-                    dash="solid",
-                    width=1 if idx % 2 else 2
-                ),
-                hovertext=hovertext,
-                hoverinfo="text"
-            )
-        )
-    if traces:
-        fig = go.Figure()
-        fig.add_traces(traces)
-        layout_hdrh = layout.get("plot-hdrh-latency", None)
-        if lat_hdrh:
-            fig.update_layout(layout_hdrh)
-
-    return fig
-
-
 def graph_tm_trending(data: pd.DataFrame, layout: dict) -> list:
     """Generates one trending graph per test, each graph includes all selected
     metrics.
index e4b7094..4110614 100644 (file)
@@ -34,10 +34,9 @@ from ..utils.control_panel import ControlPanel
 from ..utils.trigger import Trigger
 from ..utils.telemetry_data import TelemetryData
 from ..utils.utils import show_tooltip, label, sync_checklists, gen_new_url, \
-    generate_options, get_list_group_items
+    generate_options, get_list_group_items, graph_hdrh_latency
 from ..utils.url_processing import url_decode
-from .graphs import graph_trending, graph_hdrh_latency, select_trending_data, \
-    graph_tm_trending
+from .graphs import graph_trending, select_trending_data, graph_tm_trending
 
 
 # Control panel partameters and their default values.
index 6809998..d9347b1 100644 (file)
 """
 
 import pandas as pd
+import plotly.graph_objects as go
 import dash_bootstrap_components as dbc
 
+import hdrh.histogram
+import hdrh.codec
+
 from math import sqrt
 from numpy import isnan
 from dash import dcc
@@ -393,6 +397,7 @@ def get_list_group_items(
 
     return children
 
+
 def relative_change_stdev(mean1, mean2, std1, std2):
     """Compute relative standard deviation of change of two values.
 
@@ -417,3 +422,101 @@ def relative_change_stdev(mean1, mean2, std1, std2):
     second = std2 / mean2
     std = quotient * sqrt(first * first + second * second)
     return (quotient - 1) * 100, std * 100
+
+
+def get_hdrh_latencies(row: pd.Series, name: str) -> dict:
+    """Get the HDRH latencies from the test data.
+
+    :param row: A row fron the data frame with test data.
+    :param name: The test name to be displayed as the graph title.
+    :type row: pandas.Series
+    :type name: str
+    :returns: Dictionary with HDRH latencies.
+    :rtype: dict
+    """
+
+    latencies = {"name": name}
+    for key in C.LAT_HDRH:
+        try:
+            latencies[key] = row[key]
+        except KeyError:
+            return None
+
+    return latencies
+
+
+def graph_hdrh_latency(data: dict, layout: dict) -> go.Figure:
+    """Generate HDR Latency histogram graphs.
+
+    :param data: HDRH data.
+    :param layout: Layout of plot.ly graph.
+    :type data: dict
+    :type layout: dict
+    :returns: HDR latency Histogram.
+    :rtype: plotly.graph_objects.Figure
+    """
+
+    fig = None
+
+    traces = list()
+    for idx, (lat_name, lat_hdrh) in enumerate(data.items()):
+        try:
+            decoded = hdrh.histogram.HdrHistogram.decode(lat_hdrh)
+        except (hdrh.codec.HdrLengthException, TypeError):
+            continue
+        previous_x = 0.0
+        prev_perc = 0.0
+        xaxis = list()
+        yaxis = list()
+        hovertext = list()
+        for item in decoded.get_recorded_iterator():
+            # The real value is "percentile".
+            # For 100%, we cut that down to "x_perc" to avoid
+            # infinity.
+            percentile = item.percentile_level_iterated_to
+            x_perc = min(percentile, C.PERCENTILE_MAX)
+            xaxis.append(previous_x)
+            yaxis.append(item.value_iterated_to)
+            hovertext.append(
+                f"<b>{C.GRAPH_LAT_HDRH_DESC[lat_name]}</b><br>"
+                f"Direction: {('W-E', 'E-W')[idx % 2]}<br>"
+                f"Percentile: {prev_perc:.5f}-{percentile:.5f}%<br>"
+                f"Latency: {item.value_iterated_to}uSec"
+            )
+            next_x = 100.0 / (100.0 - x_perc)
+            xaxis.append(next_x)
+            yaxis.append(item.value_iterated_to)
+            hovertext.append(
+                f"<b>{C.GRAPH_LAT_HDRH_DESC[lat_name]}</b><br>"
+                f"Direction: {('W-E', 'E-W')[idx % 2]}<br>"
+                f"Percentile: {prev_perc:.5f}-{percentile:.5f}%<br>"
+                f"Latency: {item.value_iterated_to}uSec"
+            )
+            previous_x = next_x
+            prev_perc = percentile
+
+        traces.append(
+            go.Scatter(
+                x=xaxis,
+                y=yaxis,
+                name=C.GRAPH_LAT_HDRH_DESC[lat_name],
+                mode="lines",
+                legendgroup=C.GRAPH_LAT_HDRH_DESC[lat_name],
+                showlegend=bool(idx % 2),
+                line=dict(
+                    color=get_color(int(idx/2)),
+                    dash="solid",
+                    width=1 if idx % 2 else 2
+                ),
+                hovertext=hovertext,
+                hoverinfo="text"
+            )
+        )
+    if traces:
+        fig = go.Figure()
+        fig.add_traces(traces)
+        layout_hdrh = layout.get("plot-hdrh-latency", None)
+        if lat_hdrh:
+            fig.update_layout(layout_hdrh)
+
+    return fig