CSIT-1041: Trending dashboard
[csit.git] / resources / tools / presentation / generator_plots.py
1 # Copyright (c) 2018 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 """Algorithms to generate plots.
15 """
16
17
18 import logging
19 import pandas as pd
20 import plotly.offline as ploff
21 import plotly.graph_objs as plgo
22
23 from plotly.exceptions import PlotlyError
24
25 from utils import mean
26
27
28 def generate_plots(spec, data):
29     """Generate all plots specified in the specification file.
30
31     :param spec: Specification read from the specification file.
32     :param data: Data to process.
33     :type spec: Specification
34     :type data: InputData
35     """
36
37     logging.info("Generating the plots ...")
38     for index, plot in enumerate(spec.plots):
39         try:
40             logging.info("  Plot nr {0}:".format(index + 1))
41             eval(plot["algorithm"])(plot, data)
42         except NameError:
43             logging.error("The algorithm '{0}' is not defined.".
44                           format(plot["algorithm"]))
45     logging.info("Done.")
46
47
48 def plot_performance_box(plot, input_data):
49     """Generate the plot(s) with algorithm: plot_performance_box
50     specified in the specification file.
51
52     :param plot: Plot to generate.
53     :param input_data: Data to process.
54     :type plot: pandas.Series
55     :type input_data: InputData
56     """
57
58     logging.info("  Generating the plot {0} ...".
59                  format(plot.get("title", "")))
60
61     # Transform the data
62     data = input_data.filter_data(plot)
63     if data is None:
64         logging.error("No data.")
65         return
66
67     # Prepare the data for the plot
68     y_vals = dict()
69     for job in data:
70         for build in job:
71             for test in build:
72                 if y_vals.get(test["parent"], None) is None:
73                     y_vals[test["parent"]] = list()
74                 try:
75                     y_vals[test["parent"]].append(test["throughput"]["value"])
76                 except (KeyError, TypeError):
77                     y_vals[test["parent"]].append(None)
78
79     # Add None to the lists with missing data
80     max_len = 0
81     for val in y_vals.values():
82         if len(val) > max_len:
83             max_len = len(val)
84     for key, val in y_vals.items():
85         if len(val) < max_len:
86             val.extend([None for _ in range(max_len - len(val))])
87
88     # Add plot traces
89     traces = list()
90     df = pd.DataFrame(y_vals)
91     df.head()
92     for i, col in enumerate(df.columns):
93         name = "{0}. {1}".format(i + 1, col.lower().replace('-ndrpdrdisc', ''))
94         traces.append(plgo.Box(x=[str(i + 1) + '.'] * len(df[col]),
95                                y=df[col],
96                                name=name,
97                                **plot["traces"]))
98
99     try:
100         # Create plot
101         plpl = plgo.Figure(data=traces, layout=plot["layout"])
102
103         # Export Plot
104         logging.info("    Writing file '{0}{1}'.".
105                      format(plot["output-file"], plot["output-file-type"]))
106         ploff.plot(plpl,
107                    show_link=False, auto_open=False,
108                    filename='{0}{1}'.format(plot["output-file"],
109                                             plot["output-file-type"]))
110     except PlotlyError as err:
111         logging.error("   Finished with error: {}".
112                       format(str(err).replace("\n", " ")))
113         return
114
115     logging.info("  Done.")
116
117
118 def plot_latency_box(plot, input_data):
119     """Generate the plot(s) with algorithm: plot_latency_box
120     specified in the specification file.
121
122     :param plot: Plot to generate.
123     :param input_data: Data to process.
124     :type plot: pandas.Series
125     :type input_data: InputData
126     """
127
128     logging.info("  Generating the plot {0} ...".
129                  format(plot.get("title", "")))
130
131     # Transform the data
132     data = input_data.filter_data(plot)
133     if data is None:
134         logging.error("No data.")
135         return
136
137     # Prepare the data for the plot
138     y_tmp_vals = dict()
139     for job in data:
140         for build in job:
141             for test in build:
142                 if y_tmp_vals.get(test["parent"], None) is None:
143                     y_tmp_vals[test["parent"]] = [
144                         list(),  # direction1, min
145                         list(),  # direction1, avg
146                         list(),  # direction1, max
147                         list(),  # direction2, min
148                         list(),  # direction2, avg
149                         list()   # direction2, max
150                     ]
151                 try:
152                     y_tmp_vals[test["parent"]][0].append(
153                         test["latency"]["direction1"]["50"]["min"])
154                     y_tmp_vals[test["parent"]][1].append(
155                         test["latency"]["direction1"]["50"]["avg"])
156                     y_tmp_vals[test["parent"]][2].append(
157                         test["latency"]["direction1"]["50"]["max"])
158                     y_tmp_vals[test["parent"]][3].append(
159                         test["latency"]["direction2"]["50"]["min"])
160                     y_tmp_vals[test["parent"]][4].append(
161                         test["latency"]["direction2"]["50"]["avg"])
162                     y_tmp_vals[test["parent"]][5].append(
163                         test["latency"]["direction2"]["50"]["max"])
164                 except (KeyError, TypeError):
165                     pass
166
167     y_vals = dict()
168     for key, values in y_tmp_vals.items():
169         y_vals[key] = list()
170         for val in values:
171             if val:
172                 average = mean(val)
173             else:
174                 average = None
175             y_vals[key].append(average)
176             y_vals[key].append(average)  # Twice for plot.ly
177
178     # Add plot traces
179     traces = list()
180     try:
181         df = pd.DataFrame(y_vals)
182         df.head()
183     except ValueError as err:
184         logging.error("   Finished with error: {}".
185                       format(str(err).replace("\n", " ")))
186         return
187
188     for i, col in enumerate(df.columns):
189         name = "{0}. {1}".format(i + 1, col.lower().replace('-ndrpdrdisc', ''))
190         traces.append(plgo.Box(x=['TGint1-to-SUT1-to-SUT2-to-TGint2',
191                                   'TGint1-to-SUT1-to-SUT2-to-TGint2',
192                                   'TGint1-to-SUT1-to-SUT2-to-TGint2',
193                                   'TGint1-to-SUT1-to-SUT2-to-TGint2',
194                                   'TGint1-to-SUT1-to-SUT2-to-TGint2',
195                                   'TGint1-to-SUT1-to-SUT2-to-TGint2',
196                                   'TGint2-to-SUT2-to-SUT1-to-TGint1',
197                                   'TGint2-to-SUT2-to-SUT1-to-TGint1',
198                                   'TGint2-to-SUT2-to-SUT1-to-TGint1',
199                                   'TGint2-to-SUT2-to-SUT1-to-TGint1',
200                                   'TGint2-to-SUT2-to-SUT1-to-TGint1',
201                                   'TGint2-to-SUT2-to-SUT1-to-TGint1'],
202                                y=df[col],
203                                name=name,
204                                **plot["traces"]))
205
206     try:
207         # Create plot
208         logging.info("    Writing file '{0}{1}'.".
209                      format(plot["output-file"], plot["output-file-type"]))
210         plpl = plgo.Figure(data=traces, layout=plot["layout"])
211
212         # Export Plot
213         ploff.plot(plpl,
214                    show_link=False, auto_open=False,
215                    filename='{0}{1}'.format(plot["output-file"],
216                                             plot["output-file-type"]))
217     except PlotlyError as err:
218         logging.error("   Finished with error: {}".
219                       format(str(err).replace("\n", " ")))
220         return
221
222     logging.info("  Done.")
223
224
225 def plot_throughput_speedup_analysis(plot, input_data):
226     """Generate the plot(s) with algorithm: plot_throughput_speedup_analysis
227     specified in the specification file.
228
229     :param plot: Plot to generate.
230     :param input_data: Data to process.
231     :type plot: pandas.Series
232     :type input_data: InputData
233     """
234
235     logging.info("  Generating the plot {0} ...".
236                  format(plot.get("title", "")))
237
238     # Transform the data
239     data = input_data.filter_data(plot)
240     if data is None:
241         logging.error("No data.")
242         return
243
244     throughput = dict()
245     for job in data:
246         for build in job:
247             for test in build:
248                 if throughput.get(test["parent"], None) is None:
249                     throughput[test["parent"]] = {"1": list(),
250                                                   "2": list(),
251                                                   "4": list()}
252                 try:
253                     if "1T1C" in test["tags"]:
254                         throughput[test["parent"]]["1"].\
255                             append(test["throughput"]["value"])
256                     elif "2T2C" in test["tags"]:
257                         throughput[test["parent"]]["2"]. \
258                             append(test["throughput"]["value"])
259                     elif "4T4C" in test["tags"]:
260                         throughput[test["parent"]]["4"]. \
261                             append(test["throughput"]["value"])
262                 except (KeyError, TypeError):
263                     pass
264
265     if not throughput:
266         logging.warning("No data for the plot '{}'".
267                         format(plot.get("title", "")))
268         return
269
270     for test_name, test_vals in throughput.items():
271         for key, test_val in test_vals.items():
272             if test_val:
273                 throughput[test_name][key] = sum(test_val) / len(test_val)
274
275     names = ['1 core', '2 cores', '4 cores']
276     x_vals = list()
277     y_vals_1 = list()
278     y_vals_2 = list()
279     y_vals_4 = list()
280
281     for test_name, test_vals in throughput.items():
282         if test_vals["1"]:
283             x_vals.append("-".join(test_name.split('-')[1:-1]))
284             y_vals_1.append(1)
285             if test_vals["2"]:
286                 y_vals_2.append(
287                     round(float(test_vals["2"]) / float(test_vals["1"]), 2))
288             else:
289                 y_vals_2.append(None)
290             if test_vals["4"]:
291                 y_vals_4.append(
292                     round(float(test_vals["4"]) / float(test_vals["1"]), 2))
293             else:
294                 y_vals_4.append(None)
295
296     y_vals = [y_vals_1, y_vals_2, y_vals_4]
297
298     y_vals_zipped = zip(names, y_vals)
299     traces = list()
300     for val in y_vals_zipped:
301         traces.append(plgo.Bar(x=x_vals,
302                                y=val[1],
303                                name=val[0]))
304
305     try:
306         # Create plot
307         logging.info("    Writing file '{0}{1}'.".
308                      format(plot["output-file"], plot["output-file-type"]))
309         plpl = plgo.Figure(data=traces, layout=plot["layout"])
310
311         # Export Plot
312         ploff.plot(plpl,
313                    show_link=False, auto_open=False,
314                    filename='{0}{1}'.format(plot["output-file"],
315                                             plot["output-file-type"]))
316     except PlotlyError as err:
317         logging.error("   Finished with error: {}".
318                       format(str(err).replace("\n", " ")))
319         return
320
321     logging.info("  Done.")
322
323
324 def plot_http_server_performance_box(plot, input_data):
325     """Generate the plot(s) with algorithm: plot_http_server_performance_box
326     specified in the specification file.
327
328     :param plot: Plot to generate.
329     :param input_data: Data to process.
330     :type plot: pandas.Series
331     :type input_data: InputData
332     """
333
334     logging.info("  Generating the plot {0} ...".
335                  format(plot.get("title", "")))
336
337     # Transform the data
338     data = input_data.filter_data(plot)
339     if data is None:
340         logging.error("No data.")
341         return
342
343     # Prepare the data for the plot
344     y_vals = dict()
345     for job in data:
346         for build in job:
347             for test in build:
348                 if y_vals.get(test["name"], None) is None:
349                     y_vals[test["name"]] = list()
350                 try:
351                     y_vals[test["name"]].append(test["result"]["value"])
352                 except (KeyError, TypeError):
353                     y_vals[test["name"]].append(None)
354
355     # Add None to the lists with missing data
356     max_len = 0
357     for val in y_vals.values():
358         if len(val) > max_len:
359             max_len = len(val)
360     for key, val in y_vals.items():
361         if len(val) < max_len:
362             val.extend([None for _ in range(max_len - len(val))])
363
364     # Add plot traces
365     traces = list()
366     df = pd.DataFrame(y_vals)
367     df.head()
368     for i, col in enumerate(df.columns):
369         name = "{0}. {1}".format(i + 1, col.lower().replace('-cps', '').
370                                  replace('-rps', ''))
371         traces.append(plgo.Box(x=[str(i + 1) + '.'] * len(df[col]),
372                                y=df[col],
373                                name=name,
374                                **plot["traces"]))
375     try:
376         # Create plot
377         plpl = plgo.Figure(data=traces, layout=plot["layout"])
378
379         # Export Plot
380         logging.info("    Writing file '{0}{1}'.".
381                      format(plot["output-file"], plot["output-file-type"]))
382         ploff.plot(plpl,
383                    show_link=False, auto_open=False,
384                    filename='{0}{1}'.format(plot["output-file"],
385                                             plot["output-file-type"]))
386     except PlotlyError as err:
387         logging.error("   Finished with error: {}".
388                       format(str(err).replace("\n", " ")))
389         return
390
391     logging.info("  Done.")