Coverage for testrail_api_reporter/engines/plotly_reporter.py: 72%
106 statements
« prev ^ index » next coverage.py v7.6.1, created at 2024-08-30 15:42 +0200
« prev ^ index » next coverage.py v7.6.1, created at 2024-08-30 15:42 +0200
1# -*- coding: utf-8 -*-
2""" Confluence sender module """
4from typing import Optional
6import plotly
8from ..utils.csv_parser import CSVParser
9from ..utils.logger_config import setup_logger, DEFAULT_LOGGING_LEVEL
11# Set path to orca for plotly
12plotly.io.orca.config.executable = "/usr/local/bin/orca"
15class PlotlyReporter:
16 """Class contains wrapper for generated reports (images) via plot charts"""
18 def __init__(
19 self,
20 pr_colors=None,
21 pr_labels=None,
22 ar_colors=None,
23 lines=None,
24 type_platforms=None,
25 logger=None,
26 log_level=DEFAULT_LOGGING_LEVEL,
27 ):
28 """
29 General init
31 :param pr_colors: default colors for different priorities, list with rgb, (usually 1-4 values), optional
32 :param pr_labels: default labels for different priorities, list with strings (usually 1-4 values), optional
33 :param ar_colors: default colors for different sections (platforms), list with rgb, optional
34 :param lines: default settings for lines, dict like {'color': 'rgb(0,0,51)', 'width': 1.5}, optional
35 :param type_platforms: list of dicts, with sections ids, where dict = {'name': 'UI',
36 'sections': [16276]}, optional
37 :param logger: logger object, optional
38 :param log_level: logging level, optional, by default is 'logging.DEBUG'
39 """
40 if not logger:
41 self.___logger = setup_logger(name="PlotlyReporter", log_file="PlotlyReporter.log", level=log_level)
42 else:
43 self.___logger = logger
44 self.___logger.debug("Initializing Plotly Reporter")
45 if not type_platforms:
46 raise ValueError("Platform types is not provided, Plotly Reporter cannot be initialized!")
47 self.__pr_labels = pr_labels if pr_labels else ["Low", "Medium", "High", "Critical"]
48 self.__pr_colors = (
49 pr_colors if pr_colors else ["rgb(173,216,230)", "rgb(34,139,34)", "rgb(255,255,51)", "rgb(255, 153, 153)"]
50 )
51 self.__ar_colors = (
52 ar_colors
53 if ar_colors
54 else [
55 "rgb(255, 153, 153)",
56 "rgb(255,255,51)",
57 "rgb(34,139,34)",
58 "rgb(173,216,230)",
59 "rgb(65,105,225)",
60 "rgb(192, 192, 192)",
61 ]
62 )
63 self.__lines = lines if lines else ({"color": "rgb(0,0,51)", "width": 1.5})
64 self.__type_platforms = type_platforms
66 def draw_automation_state_report(self, filename=None, reports=None, state_markers=None):
67 """
68 Generates an image file (png) with staked distribution (bar chart) with automation type coverage (or similar).
70 :param filename: output filename for image, png expected, required
71 :param reports: report with stacked distribution, usually it's output of
72 ATCoverageReporter().automation_state_report()
73 :param state_markers: list of dicts, contains settings for markers on chart like the following:
74 {
75 "marker": {"color": "rgb(34,139,34)", "line": {"color": "rgb(0,0,51)", "width": 1.5}},
76 "opacity": 0.6,
77 "textposition": "auto",
78 }
79 :return: none
80 """
81 if not reports:
82 raise ValueError("No TestRail reports are provided, report aborted!")
83 if not filename:
84 raise ValueError("No output filename is provided, report aborted!")
85 data = []
86 axis_x = []
87 axis_y_automated = []
88 axis_y_not_automated = []
89 axis_y_na = []
91 for report in reports:
92 axis_x.append(report.get_name())
93 axis_y_automated.append(report.get_automated())
94 axis_y_not_automated.append(report.get_not_automated())
95 axis_y_na.append(report.get_not_applicable())
97 if not state_markers:
98 state_markers = {
99 "Automated": {
100 "marker": {"color": "rgb(34,139,34)", "line": {"color": "rgb(0,0,51)", "width": 1.5}},
101 "opacity": 0.6,
102 "textposition": "auto",
103 },
104 "Not automated": {
105 "marker": {"color": "rgb(255, 153, 153)", "line": {"color": "rgb(0,0,51)", "width": 1.5}},
106 "opacity": 0.6,
107 "textposition": "auto",
108 },
109 "N/A": {
110 "marker": {"color": "rgb(192, 192, 192)", "line": {"color": "rgb(0,0,51)", "width": 1.5}},
111 "opacity": 0.6,
112 "textposition": "auto",
113 },
114 }
116 data.append(
117 plotly.graph_objs.Bar(
118 x=axis_x,
119 y=axis_y_automated,
120 text=axis_y_automated,
121 name="Automated",
122 textposition=state_markers["Automated"]["textposition"],
123 marker=state_markers["Automated"]["marker"],
124 opacity=state_markers["Automated"]["opacity"],
125 )
126 )
127 data.append(
128 plotly.graph_objs.Bar(
129 x=axis_x,
130 y=axis_y_not_automated,
131 text=axis_y_not_automated,
132 name="Not automated",
133 textposition=state_markers["Not automated"]["textposition"],
134 marker=state_markers["Not automated"]["marker"],
135 opacity=state_markers["Not automated"]["opacity"],
136 )
137 )
138 data.append(
139 plotly.graph_objs.Bar(
140 x=axis_x,
141 y=axis_y_na,
142 text=axis_y_na,
143 name="N/A",
144 textposition=state_markers["N/A"]["textposition"],
145 marker=state_markers["N/A"]["marker"],
146 opacity=state_markers["N/A"]["opacity"],
147 )
148 )
150 layout = plotly.graph_objs.Layout(barmode="stack")
151 self.___logger.debug("Drawing chart to file %s", filename)
152 fig = plotly.graph_objs.Figure(data=data, layout=layout)
153 plotly.io.write_image(fig, filename)
155 def draw_test_case_by_priority(self, filename=None, values=None, pr_labels=None, pr_colors=None, lines=None):
156 """
157 Generates an image file (png) with priority distribution (pie chart)
159 :param filename: output filename for image, png expected, required
160 :param values: list of values to draw report with priority distribution, usually it's output from
161 ATCoverageReporter().test_case_by_priority()
162 :param pr_labels: default labels for different priorities, list with strings (usually 1-4 values), optional
163 :param pr_colors: default colors for different priorities, list with rgb, (usually 1-4 values), optional
164 :param lines: default settings for lines, dict like {'color': 'rgb(0,0,51)', 'width': 1.5}, optional
165 :return: none
166 """
167 if not values:
168 raise ValueError("No TestRail values are provided, report aborted!")
169 if not filename:
170 raise ValueError("No output filename is provided, report aborted!")
171 pr_labels = pr_labels if pr_labels else self.__pr_labels
172 pr_colors = pr_colors if pr_colors else self.__pr_colors
173 lines = lines if lines else self.__lines
174 fig = {
175 "data": [
176 {
177 "values": values,
178 "labels": pr_labels,
179 "domain": {"column": 0},
180 "name": "Test cases by priority",
181 "hoverinfo": "label+percent+name",
182 "textinfo": "value+percent",
183 "type": "pie",
184 "marker": {"colors": pr_colors, "line": lines},
185 },
186 ]
187 }
188 self.___logger.debug("Drawing chart to file %s", filename)
189 plotly.io.write_image(fig, filename)
191 def draw_test_case_by_area(self, filename=None, cases=None, ar_colors=None, lines=None):
192 """
193 Generates an image file (png) with sections distribution (pie chart)
195 :param filename: output filename for image, png expected, required
196 :param cases: list of values to draw report with priority distribution, usually it's output from
197 ATCoverageReporter().test_case_by_type()
198 :param ar_colors: default colors for different sections (platforms), list with rgb, optional
199 :param lines: default settings for lines, dict like {'color': 'rgb(0,0,51)', 'width': 1.5}, optional
200 :return: none
201 """
202 if not cases:
203 raise ValueError("No TestRail cases are provided, report aborted!")
204 if not filename:
205 raise ValueError("No output filename is provided, report aborted!")
206 # priority distribution
207 ar_colors = ar_colors if ar_colors else self.__ar_colors
208 lines = lines if lines else self.__lines
209 # area distribution
210 ar_labels = []
211 ar_values = []
212 for case in cases:
213 ar_labels.append(case.get_name())
214 ar_values.append(case.get_total())
216 fig = {
217 "data": [
218 {
219 "values": ar_values,
220 "labels": ar_labels,
221 "domain": {"column": 0},
222 "name": "Test cases by area",
223 "hoverinfo": "label+percent+name",
224 "textinfo": "value+percent",
225 "type": "pie",
226 "marker": {"colors": ar_colors, "line": lines},
227 },
228 ]
229 }
231 self.___logger.debug("Drawing chart to file %s", filename)
232 plotly.io.write_image(fig, filename)
234 def draw_history_state_chart(
235 self,
236 chart_name: Optional[str] = None,
237 history_data=None,
238 filename=None,
239 trace1_decor=None,
240 trace2_decor=None,
241 filename_pattern="current_automation",
242 reverse_traces=False,
243 ):
244 """
245 Generates image file (png) with state distribution (staked line chart)
247 :param chart_name: chart name, string, required
248 :param history_data: history data, previously stored in CSV, by default it is CSVParser().load_history_data()
249 :param filename: output filename for image, png expected, optional
250 :param trace1_decor: decoration for distribution stack (1), dict like {"fill": "tonexty",
251 "line": {"width": 0.5,
252 "color": "rgb(255, 153, 153)"},
253 "mode": "none"}
254 :param trace2_decor: decoration for distribution stack (2), dict like {"fill": "tozeroy",
255 "line": {"width": 0.5,
256 "color": "rgb(34,139,34)"},
257 "mode": "none"}
258 :param filename_pattern: pattern, what is prefix will be for filename, string, optional
259 :param reverse_traces: reverse traces order
260 :return: none
261 """
262 if chart_name is None:
263 raise ValueError("No chart name is provided, report aborted!")
264 filename = filename if filename else f"{filename_pattern}_{chart_name.replace(' ', '_')}.csv"
265 trace1_decor = (
266 trace1_decor
267 if trace1_decor
268 else {"fill": "tonexty", "line": {"width": 0.5, "color": "rgb(255, 153, 153)"}, "mode": "none"}
269 )
270 trace2_decor = (
271 trace2_decor
272 if trace2_decor
273 else {"fill": "tozeroy", "line": {"width": 0.5, "color": "rgb(34,139,34)"}, "mode": "none"}
274 )
276 history_data = (
277 history_data
278 if history_data
279 else CSVParser(log_level=self.___logger.level, filename=filename).load_history_data()
280 )
281 trace1 = plotly.graph_objs.Scatter(
282 x=history_data[0],
283 y=history_data[1],
284 fill=trace1_decor["fill"],
285 name="Total",
286 line=trace1_decor["line"],
287 )
288 trace2 = plotly.graph_objs.Scatter(
289 x=history_data[0],
290 y=history_data[2],
291 fill=trace2_decor["fill"],
292 name="Automated",
293 line=trace2_decor["line"],
294 )
296 fig = plotly.graph_objs.Figure()
297 if reverse_traces:
298 fig.add_trace(trace2)
299 fig.add_trace(trace1)
300 else:
301 fig.add_trace(trace1)
302 fig.add_trace(trace2)
303 fig.update_layout(yaxis={"nticks": 30}, autotypenumbers="convert types")
304 fig.update_yaxes(range=[0, max((eval(i) for i in history_data[1]))]) # pylint: disable=eval-used
306 filename = f"{filename[:-3]}png"
307 self.___logger.debug("Drawing chart to file %s", filename)
308 plotly.io.write_image(fig, filename)
309 return filename
311 def draw_history_type_chart(
312 self,
313 filename=None,
314 type_platforms=None,
315 history_filename_pattern="current_area_distribution",
316 ar_colors=None,
317 lines=None,
318 ):
319 """
320 Generates an image file (png) with state distribution (staked line chart)
322 :param filename: output filename for image, png expected, required
323 :param type_platforms: list of dicts, with sections ids, where dict = {'name': 'UI',
324 'sections': [16276]}, optional
325 :param history_filename_pattern: pattern, what is prefix will be for filename, string, optional
326 :param ar_colors: default colors for different sections (platforms), list with rgb, optional
327 :param lines: default settings for lines, dict like {'color': 'rgb(0,0,51)', 'width': 1.5}, optional
328 :return: none
329 """
330 if not filename:
331 raise ValueError("No output filename is provided, report aborted!")
332 type_platforms = type_platforms if type_platforms else self.__type_platforms
333 ar_colors = ar_colors if ar_colors else self.__ar_colors
334 data = []
335 lines = lines if lines else self.__lines
336 index = 0
337 for platform in type_platforms:
338 type_name = platform["name"]
339 history_filename = f"{history_filename_pattern}_{type_name.replace(' ', '_')}.csv"
340 history_data = CSVParser(log_level=self.___logger.level, filename=history_filename).load_history_data()
341 data.append(
342 plotly.graph_objs.Scatter(
343 x=history_data[0],
344 y=history_data[1],
345 name=type_name,
346 marker={"color": ar_colors[index], "line": lines},
347 )
348 )
349 index += 1
350 fig = {"data": data}
351 self.___logger.debug("Drawing chart to file %s", filename)
352 plotly.io.write_image(fig, filename)