Coverage for testrail_api_reporter/engines/plotly_reporter.py: 72%

1# -*- coding: utf-8 -*-

2""" Confluence sender module """

3

4 from typing import Optional

5

6 import plotly

7

8 from ..utils.csv_parser import CSVParser

9 from ..utils.logger_config import setup_logger, DEFAULT_LOGGING_LEVEL

10

11# Set path to orca for plotly

12 plotly.io.orca.config.executable = "/usr/local/bin/orca"

13

14

15 class PlotlyReporter:

16 """Class contains wrapper for generated reports (images) via plot charts"""

17

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

30

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

65

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).

69

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 = []

90

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())

96

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 }

115

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 )

149

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)

154

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)

158

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)

190

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)

194

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())

215

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 }

230

231 self.___logger.debug("Drawing chart to file %s", filename)

232 plotly.io.write_image(fig, filename)

233

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)

246

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 )

275

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 )

295

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

305

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

310

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)

321

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)