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