C-Dash: fix latency graphs for iterative data
[csit.git] / csit.infra.dash / app / cdash / report / graphs.py
1 # Copyright (c) 2023 Cisco and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
5 #
6 #     http://www.apache.org/licenses/LICENSE-2.0
7 #
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
13
14 """Implementation of graphs for iterative data.
15 """
16
17 import plotly.graph_objects as go
18 import pandas as pd
19
20 from copy import deepcopy
21
22 from ..utils.constants import Constants as C
23 from ..utils.utils import get_color
24
25
26 def select_iterative_data(data: pd.DataFrame, itm:dict) -> pd.DataFrame:
27     """Select the data for graphs and tables from the provided data frame.
28
29     :param data: Data frame with data for graphs and tables.
30     :param itm: Item (in this case job name) which data will be selected from
31         the input data frame.
32     :type data: pandas.DataFrame
33     :type itm: str
34     :returns: A data frame with selected data.
35     :rtype: pandas.DataFrame
36     """
37
38     phy = itm["phy"].split("-")
39     if len(phy) == 4:
40         topo, arch, nic, drv = phy
41         if drv == "dpdk":
42             drv = ""
43         else:
44             drv += "-"
45             drv = drv.replace("_", "-")
46     else:
47         return None
48
49     if itm["testtype"] in ("ndr", "pdr"):
50         test_type = "ndrpdr"
51     elif itm["testtype"] == "mrr":
52         test_type = "mrr"
53     elif itm["area"] == "hoststack":
54         test_type = "hoststack"
55     df = data.loc[(
56         (data["release"] == itm["rls"]) &
57         (data["test_type"] == test_type) &
58         (data["passed"] == True)
59     )]
60
61     core = str() if itm["dut"] == "trex" else f"{itm['core']}"
62     ttype = "ndrpdr" if itm["testtype"] in ("ndr", "pdr") else itm["testtype"]
63     regex_test = \
64         f"^.*[.|-]{nic}.*{itm['framesize']}-{core}-{drv}{itm['test']}-{ttype}$"
65     df = df[
66         (df.job.str.endswith(f"{topo}-{arch}")) &
67         (df.dut_version.str.contains(itm["dutver"].replace(".r", "-r").\
68             replace("rls", "release"))) &
69         (df.test_id.str.contains(regex_test, regex=True))
70     ]
71
72     return df
73
74
75 def graph_iterative(data: pd.DataFrame, sel:dict, layout: dict,
76         normalize: bool) -> tuple:
77     """Generate the statistical box graph with iterative data (MRR, NDR and PDR,
78     for PDR also Latencies).
79
80     :param data: Data frame with iterative data.
81     :param sel: Selected tests.
82     :param layout: Layout of plot.ly graph.
83     :param normalize: If True, the data is normalized to CPU frquency
84         Constants.NORM_FREQUENCY.
85     :param data: pandas.DataFrame
86     :param sel: dict
87     :param layout: dict
88     :param normalize: bool
89     :returns: Tuple of graphs - throughput and latency.
90     :rtype: tuple(plotly.graph_objects.Figure, plotly.graph_objects.Figure)
91     """
92
93     fig_tput = None
94     fig_lat = None
95
96     tput_traces = list()
97     y_tput_max = 0
98     lat_traces = list()
99     y_lat_max = 0
100     x_lat = list()
101     y_units = set()
102     show_latency = False
103     show_tput = False
104     for idx, itm in enumerate(sel):
105
106         itm_data = select_iterative_data(data, itm)
107         if itm_data.empty:
108             continue
109
110         phy = itm["phy"].split("-")
111         topo_arch = f"{phy[0]}-{phy[1]}" if len(phy) == 4 else str()
112         norm_factor = (C.NORM_FREQUENCY / C.FREQUENCY[topo_arch]) \
113             if normalize else 1.0
114
115         if itm["area"] == "hoststack":
116             ttype = f"hoststack-{itm['testtype']}"
117         else:
118             ttype = itm["testtype"]
119
120         y_units.update(itm_data[C.UNIT[ttype]].unique().tolist())
121
122         if itm["testtype"] == "mrr":
123             y_data_raw = itm_data[C.VALUE_ITER[ttype]].to_list()[0]
124         else:
125             y_data_raw = itm_data[C.VALUE_ITER[ttype]].to_list()
126         y_data = [(y * norm_factor) for y in y_data_raw]
127         if y_data:
128             y_tput_max = max(max(y_data), y_tput_max)
129
130         nr_of_samples = len(y_data)
131         tput_kwargs = dict(
132             y=y_data,
133             name=(
134                 f"{idx + 1}. "
135                 f"({nr_of_samples:02d} "
136                 f"run{'s' if nr_of_samples > 1 else ''}) "
137                 f"{itm['id']}"
138             ),
139             hoverinfo=u"y+name",
140             boxpoints="all",
141             jitter=0.3,
142             marker=dict(color=get_color(idx))
143         )
144         tput_traces.append(go.Box(**tput_kwargs))
145         show_tput = True
146
147         if ttype == "pdr":
148             y_lat_row = itm_data[C.VALUE_ITER["pdr-lat"]].to_list()
149             y_lat = [(y / norm_factor) for y in y_lat_row]
150             if y_lat:
151                 try:
152                     y_lat_max = max(max(y_lat), y_lat_max)
153                 except TypeError:
154                     continue
155             nr_of_samples = len(y_lat)
156             lat_kwargs = dict(
157                 y=y_lat,
158                 name=(
159                     f"{idx + 1}. "
160                     f"({nr_of_samples:02d} "
161                     f"run{u's' if nr_of_samples > 1 else u''}) "
162                     f"{itm['id']}"
163                 ),
164                 hoverinfo="all",
165                 boxpoints="all",
166                 jitter=0.3,
167                 marker=dict(color=get_color(idx))
168             )
169             x_lat.append(idx + 1)
170             lat_traces.append(go.Box(**lat_kwargs))
171             show_latency = True
172         else:
173             lat_traces.append(go.Box())
174
175     if show_tput:
176         pl_tput = deepcopy(layout["plot-throughput"])
177         pl_tput["xaxis"]["tickvals"] = [i for i in range(len(sel))]
178         pl_tput["xaxis"]["ticktext"] = [str(i + 1) for i in range(len(sel))]
179         pl_tput["yaxis"]["title"] = f"Throughput [{'|'.join(sorted(y_units))}]"
180         if y_tput_max:
181             pl_tput["yaxis"]["range"] = [0, (int(y_tput_max / 1e6) + 2) * 1e6]
182         fig_tput = go.Figure(data=tput_traces, layout=pl_tput)
183
184     if show_latency:
185         pl_lat = deepcopy(layout["plot-latency"])
186         pl_lat["xaxis"]["tickvals"] = [i for i in range(len(x_lat))]
187         pl_lat["xaxis"]["ticktext"] = x_lat
188         if y_lat_max:
189             pl_lat["yaxis"]["range"] = [0, (int(y_lat_max / 10) + 1) * 10]
190         fig_lat = go.Figure(data=lat_traces, layout=pl_lat)
191
192     return fig_tput, fig_lat