C-Dash: Add detailed info to iterative graphs
[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
18 import plotly.graph_objects as go
19 import pandas as pd
20
21 from copy import deepcopy
22
23 from ..utils.constants import Constants as C
24 from ..utils.utils import get_color, get_hdrh_latencies
25
26
27 def select_iterative_data(data: pd.DataFrame, itm:dict) -> pd.DataFrame:
28     """Select the data for graphs and tables from the provided data frame.
29
30     :param data: Data frame with data for graphs and tables.
31     :param itm: Item (in this case job name) which data will be selected from
32         the input data frame.
33     :type data: pandas.DataFrame
34     :type itm: str
35     :returns: A data frame with selected data.
36     :rtype: pandas.DataFrame
37     """
38
39     phy = itm["phy"].split("-")
40     if len(phy) == 4:
41         topo, arch, nic, drv = phy
42         if drv == "dpdk":
43             drv = ""
44         else:
45             drv += "-"
46             drv = drv.replace("_", "-")
47     else:
48         return None
49
50     if itm["testtype"] in ("ndr", "pdr"):
51         test_type = "ndrpdr"
52     elif itm["testtype"] == "mrr":
53         test_type = "mrr"
54     elif itm["area"] == "hoststack":
55         test_type = "hoststack"
56     df = data.loc[(
57         (data["release"] == itm["rls"]) &
58         (data["test_type"] == test_type) &
59         (data["passed"] == True)
60     )]
61
62     core = str() if itm["dut"] == "trex" else f"{itm['core']}"
63     ttype = "ndrpdr" if itm["testtype"] in ("ndr", "pdr") else itm["testtype"]
64     regex_test = \
65         f"^.*[.|-]{nic}.*{itm['framesize']}-{core}-{drv}{itm['test']}-{ttype}$"
66     df = df[
67         (df.job.str.endswith(f"{topo}-{arch}")) &
68         (df.dut_version.str.contains(itm["dutver"].replace(".r", "-r").\
69             replace("rls", "release"))) &
70         (df.test_id.str.contains(regex_test, regex=True))
71     ]
72
73     return df
74
75
76 def graph_iterative(data: pd.DataFrame, sel:dict, layout: dict,
77         normalize: bool) -> tuple:
78     """Generate the statistical box graph with iterative data (MRR, NDR and PDR,
79     for PDR also Latencies).
80
81     :param data: Data frame with iterative data.
82     :param sel: Selected tests.
83     :param layout: Layout of plot.ly graph.
84     :param normalize: If True, the data is normalized to CPU frequency
85         Constants.NORM_FREQUENCY.
86     :param data: pandas.DataFrame
87     :param sel: dict
88     :param layout: dict
89     :param normalize: bool
90     :returns: Tuple of graphs - throughput and latency.
91     :rtype: tuple(plotly.graph_objects.Figure, plotly.graph_objects.Figure)
92     """
93
94     fig_tput = None
95     fig_lat = None
96
97     tput_traces = list()
98     y_tput_max = 0
99     lat_traces = list()
100     y_lat_max = 0
101     x_lat = list()
102     y_units = set()
103     show_latency = False
104     show_tput = False
105     for idx, itm in enumerate(sel):
106
107         itm_data = select_iterative_data(data, itm)
108         if itm_data.empty:
109             continue
110
111         phy = itm["phy"].split("-")
112         topo_arch = f"{phy[0]}-{phy[1]}" if len(phy) == 4 else str()
113         norm_factor = (C.NORM_FREQUENCY / C.FREQUENCY[topo_arch]) \
114             if normalize else 1.0
115
116         if itm["area"] == "hoststack":
117             ttype = f"hoststack-{itm['testtype']}"
118         else:
119             ttype = itm["testtype"]
120
121         y_units.update(itm_data[C.UNIT[ttype]].unique().tolist())
122
123         if itm["testtype"] == "mrr":
124             y_data_raw = itm_data[C.VALUE_ITER[ttype]].to_list()[0]
125         else:
126             y_data_raw = itm_data[C.VALUE_ITER[ttype]].to_list()
127         y_data = [(y * norm_factor) for y in y_data_raw]
128         if y_data:
129             y_tput_max = max(max(y_data), y_tput_max)
130
131         nr_of_samples = len(y_data)
132
133         customdata = list()
134         metadata = {
135             "csit release": itm["rls"],
136             "dut": itm["dut"],
137             "dut version": itm["dutver"],
138             "infra": itm["phy"],
139             "test": (
140                 f"{itm['area']}-{itm['framesize']}-{itm['core']}-"
141                 f"{itm['test']}-{itm['testtype']}"
142             )
143         }
144
145         if itm["testtype"] == "mrr":
146             metadata["csit-ref"] = (
147                 f"{itm_data['job'].to_list()[0]}/",
148                 f"{itm_data['build'].to_list()[0]}"
149             )
150             customdata = [{"metadata": metadata}, ] * nr_of_samples
151         else:
152             for _, row in itm_data.iterrows():
153                 metadata["csit-ref"] = f"{row['job']}/{row['build']}"
154                 customdata.append({"metadata": deepcopy(metadata)})
155         tput_kwargs = dict(
156             y=y_data,
157             name=(
158                 f"{idx + 1}. "
159                 f"({nr_of_samples:02d} "
160                 f"run{'s' if nr_of_samples > 1 else ''}) "
161                 f"{itm['id']}"
162             ),
163             hoverinfo=u"y+name",
164             boxpoints="all",
165             jitter=0.3,
166             marker=dict(color=get_color(idx)),
167             customdata=customdata
168         )
169         tput_traces.append(go.Box(**tput_kwargs))
170         show_tput = True
171
172         if ttype == "pdr":
173             customdata = list()
174             for _, row in itm_data.iterrows():
175                 hdrh = get_hdrh_latencies(
176                     row,
177                     f"{metadata['infra']}-{metadata['test']}"
178                 )
179                 metadata["csit-ref"] = f"{row['job']}/{row['build']}"
180                 customdata.append({
181                     "metadata": deepcopy(metadata),
182                     "hdrh": hdrh
183                 })
184
185             y_lat_row = itm_data[C.VALUE_ITER["latency"]].to_list()
186             y_lat = [(y / norm_factor) for y in y_lat_row]
187             if y_lat:
188                 try:
189                     y_lat_max = max(max(y_lat), y_lat_max)
190                 except TypeError:
191                     continue
192             nr_of_samples = len(y_lat)
193             lat_kwargs = dict(
194                 y=y_lat,
195                 name=(
196                     f"{idx + 1}. "
197                     f"({nr_of_samples:02d} "
198                     f"run{u's' if nr_of_samples > 1 else u''}) "
199                     f"{itm['id']}"
200                 ),
201                 hoverinfo="all",
202                 boxpoints="all",
203                 jitter=0.3,
204                 marker=dict(color=get_color(idx)),
205                 customdata=customdata
206             )
207             x_lat.append(idx + 1)
208             lat_traces.append(go.Box(**lat_kwargs))
209             show_latency = True
210         else:
211             lat_traces.append(go.Box())
212
213     if show_tput:
214         pl_tput = deepcopy(layout["plot-throughput"])
215         pl_tput["xaxis"]["tickvals"] = [i for i in range(len(sel))]
216         pl_tput["xaxis"]["ticktext"] = [str(i + 1) for i in range(len(sel))]
217         pl_tput["yaxis"]["title"] = f"Throughput [{'|'.join(sorted(y_units))}]"
218         if y_tput_max:
219             pl_tput["yaxis"]["range"] = [0, (int(y_tput_max / 1e6) + 2) * 1e6]
220         fig_tput = go.Figure(data=tput_traces, layout=pl_tput)
221
222     if show_latency:
223         pl_lat = deepcopy(layout["plot-latency"])
224         pl_lat["xaxis"]["tickvals"] = [i for i in range(len(x_lat))]
225         pl_lat["xaxis"]["ticktext"] = x_lat
226         if y_lat_max:
227             pl_lat["yaxis"]["range"] = [0, (int(y_lat_max / 10) + 1) * 10]
228         fig_lat = go.Figure(data=lat_traces, layout=pl_lat)
229
230     return fig_tput, fig_lat