C-Dash: Hover info and y-axis in trending and 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 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             y_data = [(y * norm_factor) for y in y_data_raw]
125             if len(y_data) > 0:
126                 y_tput_max = \
127                     max(y_data) if max(y_data) > y_tput_max else y_tput_max
128         else:
129             y_data_raw = itm_data[C.VALUE_ITER[ttype]].to_list()
130             y_data = [(y * norm_factor) for y in y_data_raw]
131             if y_data:
132                 y_tput_max = \
133                     max(y_data) if max(y_data) > y_tput_max else y_tput_max
134
135         nr_of_samples = len(y_data)
136         tput_kwargs = dict(
137             y=y_data,
138             name=(
139                 f"{idx + 1}. "
140                 f"({nr_of_samples:02d} "
141                 f"run{'s' if nr_of_samples > 1 else ''}) "
142                 f"{itm['id']}"
143             ),
144             hoverinfo=u"y+name",
145             boxpoints="all",
146             jitter=0.3,
147             marker=dict(color=get_color(idx))
148         )
149         tput_traces.append(go.Box(**tput_kwargs))
150         show_tput = True
151
152         if ttype == "pdr":
153             y_lat_row = itm_data[C.VALUE_ITER["pdr-lat"]].to_list()
154             y_lat = [(y / norm_factor) for y in y_lat_row]
155             if y_lat:
156                 y_lat_max = max(y_lat) if max(y_lat) > y_lat_max else y_lat_max
157             nr_of_samples = len(y_lat)
158             lat_kwargs = dict(
159                 y=y_lat,
160                 name=(
161                     f"{idx + 1}. "
162                     f"({nr_of_samples:02d} "
163                     f"run{u's' if nr_of_samples > 1 else u''}) "
164                     f"{itm['id']}"
165                 ),
166                 hoverinfo="all",
167                 boxpoints="all",
168                 jitter=0.3,
169                 marker=dict(color=get_color(idx))
170             )
171             x_lat.append(idx + 1)
172             lat_traces.append(go.Box(**lat_kwargs))
173             show_latency = True
174         else:
175             lat_traces.append(go.Box())
176
177     if show_tput:
178         pl_tput = deepcopy(layout["plot-throughput"])
179         pl_tput["xaxis"]["tickvals"] = [i for i in range(len(sel))]
180         pl_tput["xaxis"]["ticktext"] = [str(i + 1) for i in range(len(sel))]
181         pl_tput["yaxis"]["title"] = f"Throughput [{'|'.join(sorted(y_units))}]"
182         if y_tput_max:
183             pl_tput["yaxis"]["range"] = [0, (int(y_tput_max / 1e6) + 2) * 1e6]
184         fig_tput = go.Figure(data=tput_traces, layout=pl_tput)
185
186     if show_latency:
187         pl_lat = deepcopy(layout["plot-latency"])
188         pl_lat["xaxis"]["tickvals"] = [i for i in range(len(x_lat))]
189         pl_lat["xaxis"]["ticktext"] = x_lat
190         if y_lat_max:
191             pl_lat["yaxis"]["range"] = [0, (int(y_lat_max / 10) + 1) * 10]
192         fig_lat = go.Figure(data=lat_traces, layout=pl_lat)
193
194     return fig_tput, fig_lat