Report:data
[csit.git] / resources / tools / presentation / generator_tables.py
1 # Copyright (c) 2017 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 tables.
15 """
16
17
18 import logging
19 from string import replace
20
21 from errors import PresentationError
22 from utils import mean, stdev, relative_change
23
24
25 def generate_tables(spec, data):
26     """Generate all tables specified in the specification file.
27
28     :param spec: Specification read from the specification file.
29     :param data: Data to process.
30     :type spec: Specification
31     :type data: InputData
32     """
33
34     logging.info("Generating the tables ...")
35     for table in spec.tables:
36         try:
37             eval(table["algorithm"])(table, data)
38         except NameError:
39             logging.error("The algorithm '{0}' is not defined.".
40                           format(table["algorithm"]))
41     logging.info("Done.")
42
43
44 def table_details(table, input_data):
45     """Generate the table(s) with algorithm: table_detailed_test_results
46     specified in the specification file.
47
48     :param table: Table to generate.
49     :param input_data: Data to process.
50     :type table: pandas.Series
51     :type input_data: InputData
52     """
53
54     logging.info("  Generating the table {0} ...".
55                  format(table.get("title", "")))
56
57     # Transform the data
58     data = input_data.filter_data(table)
59
60     # Prepare the header of the tables
61     header = list()
62     for column in table["columns"]:
63         header.append('"{0}"'.format(str(column["title"]).replace('"', '""')))
64
65     # Generate the data for the table according to the model in the table
66     # specification
67
68     job = table["data"].keys()[0]
69     build = str(table["data"][job][0])
70     try:
71         suites = input_data.suites(job, build)
72     except KeyError:
73         logging.error("    No data available. The table will not be generated.")
74         return
75
76     for suite_longname, suite in suites.iteritems():
77         # Generate data
78         suite_name = suite["name"]
79         table_lst = list()
80         for test in data[job][build].keys():
81             if data[job][build][test]["parent"] in suite_name:
82                 row_lst = list()
83                 for column in table["columns"]:
84                     try:
85                         col_data = str(data[job][build][test][column["data"].
86                                        split(" ")[1]]).replace('"', '""')
87                         if column["data"].split(" ")[1] in ("vat-history",
88                                                             "show-run"):
89                             col_data = replace(col_data, " |br| ", "",
90                                                maxreplace=1)
91                             col_data = " |prein| {0} |preout| ".\
92                                 format(col_data[:-5])
93                         row_lst.append('"{0}"'.format(col_data))
94                     except KeyError:
95                         row_lst.append("No data")
96                 table_lst.append(row_lst)
97
98         # Write the data to file
99         if table_lst:
100             file_name = "{0}_{1}{2}".format(table["output-file"], suite_name,
101                                             table["output-file-ext"])
102             logging.info("      Writing file: '{}'".format(file_name))
103             with open(file_name, "w") as file_handler:
104                 file_handler.write(",".join(header) + "\n")
105                 for item in table_lst:
106                     file_handler.write(",".join(item) + "\n")
107
108     logging.info("  Done.")
109
110
111 def table_performance_improvements(table, input_data):
112     """Generate the table(s) with algorithm: table_performance_improvements
113     specified in the specification file.
114
115     :param table: Table to generate.
116     :param input_data: Data to process.
117     :type table: pandas.Series
118     :type input_data: InputData
119     """
120
121     def _write_line_to_file(file_handler, data):
122         """Write a line to the .csv file.
123
124         :param file_handler: File handler for the csv file. It must be open for
125          writing text.
126         :param data: Item to be written to the file.
127         :type file_handler: BinaryIO
128         :type data: list
129         """
130
131         line_lst = list()
132         for item in data:
133             if isinstance(item["data"], str):
134                 line_lst.append(item["data"])
135             elif isinstance(item["data"], float):
136                 line_lst.append("{:.1f}".format(item["data"]))
137             elif item["data"] is None:
138                 line_lst.append("")
139         file_handler.write(",".join(line_lst) + "\n")
140
141     logging.info("  Generating the table {0} ...".
142                  format(table.get("title", "")))
143
144     # Read the template
145     file_name = table.get("template", None)
146     if file_name:
147         try:
148             tmpl = _read_csv_template(file_name)
149         except PresentationError:
150             logging.error("  The template '{0}' does not exist. Skipping the "
151                           "table.".format(file_name))
152             return None
153     else:
154         logging.error("The template is not defined. Skipping the table.")
155         return None
156
157     # Transform the data
158     data = input_data.filter_data(table)
159
160     # Prepare the header of the tables
161     header = list()
162     for column in table["columns"]:
163         header.append(column["title"])
164
165     # Generate the data for the table according to the model in the table
166     # specification
167     tbl_lst = list()
168     for tmpl_item in tmpl:
169         tbl_item = list()
170         for column in table["columns"]:
171             cmd = column["data"].split(" ")[0]
172             args = column["data"].split(" ")[1:]
173             if cmd == "template":
174                 try:
175                     val = float(tmpl_item[int(args[0])])
176                 except ValueError:
177                     val = tmpl_item[int(args[0])]
178                 tbl_item.append({"data": val})
179             elif cmd == "data":
180                 job = args[0]
181                 operation = args[1]
182                 data_lst = list()
183                 for build in data[job]:
184                     try:
185                         data_lst.append(float(build[tmpl_item[0]]["throughput"]
186                                               ["value"]) / 1000000)
187                     except (KeyError, TypeError):
188                         # No data, ignore
189                         continue
190                 if data_lst:
191                     tbl_item.append({"data": eval(operation)(data_lst)})
192                 else:
193                     tbl_item.append({"data": None})
194             elif cmd == "operation":
195                 operation = args[0]
196                 try:
197                     nr1 = float(tbl_item[int(args[1])]["data"])
198                     nr2 = float(tbl_item[int(args[2])]["data"])
199                     if nr1 and nr2:
200                         tbl_item.append({"data": eval(operation)(nr1, nr2)})
201                     else:
202                         tbl_item.append({"data": None})
203                 except (IndexError, ValueError, TypeError):
204                     logging.error("No data for {0}".format(tbl_item[1]["data"]))
205                     tbl_item.append({"data": None})
206                     continue
207             else:
208                 logging.error("Not supported command {0}. Skipping the table.".
209                               format(cmd))
210                 return None
211         tbl_lst.append(tbl_item)
212
213     # Sort the table according to the relative change
214     tbl_lst.sort(key=lambda rel: rel[-1]["data"], reverse=True)
215
216     # Create the tables and write them to the files
217     file_names = [
218         "{0}_ndr_top{1}".format(table["output-file"], table["output-file-ext"]),
219         "{0}_pdr_top{1}".format(table["output-file"], table["output-file-ext"]),
220         "{0}_ndr_low{1}".format(table["output-file"], table["output-file-ext"]),
221         "{0}_pdr_low{1}".format(table["output-file"], table["output-file-ext"])
222     ]
223
224     for file_name in file_names:
225         logging.info("    Writing the file '{0}'".format(file_name))
226         with open(file_name, "w") as file_handler:
227             file_handler.write(",".join(header) + "\n")
228             for item in tbl_lst:
229                 if "ndr_top" in file_name \
230                         and "ndr" in item[1]["data"] \
231                         and item[-1]["data"] >= 10.0:
232                     _write_line_to_file(file_handler, item)
233                 elif "pdr_top" in file_name \
234                         and "pdr" in item[1]["data"] \
235                         and item[-1]["data"] >= 10.0:
236                     _write_line_to_file(file_handler, item)
237                 elif "ndr_low" in file_name \
238                         and "ndr" in item[1]["data"] \
239                         and item[-1]["data"] < 10.0:
240                     _write_line_to_file(file_handler, item)
241                 elif "pdr_low" in file_name \
242                         and "pdr" in item[1]["data"] \
243                         and item[-1]["data"] < 10.0:
244                     _write_line_to_file(file_handler, item)
245
246     logging.info("  Done.")
247
248
249 def _read_csv_template(file_name):
250     """Read the template from a .csv file.
251
252     :param file_name: Name / full path / relative path of the file to read.
253     :type file_name: str
254     :returns: Data from the template as list (lines) of lists (items on line).
255     :rtype: list
256     :raises: PresentationError if it is not possible to read the file.
257     """
258
259     try:
260         with open(file_name, 'r') as csv_file:
261             tmpl_data = list()
262             for line in csv_file:
263                 tmpl_data.append(line[:-1].split(","))
264         return tmpl_data
265     except IOError as err:
266         raise PresentationError(str(err), level="ERROR")