Coverage for GuiGraphViewer.py: 0%

192 statements  

« prev     ^ index     » next       coverage.py v7.6.3, created at 2024-10-15 19:33 +0000

1#!/usr/bin/env python 

2# -*- coding: utf-8 -*- 

3 

4# Copyright 2010-2011 Maxime Grandchamp 

5# This program is free software: you can redistribute it and/or modify 

6# it under the terms of the GNU Affero General Public License as published by 

7# the Free Software Foundation, version 3 of the License. 

8# 

9# This program is distributed in the hope that it will be useful, 

10# but WITHOUT ANY WARRANTY; without even the implied warranty of 

11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

12# GNU General Public License for more details. 

13# 

14# You should have received a copy of the GNU Affero General Public License 

15# along with this program. If not, see <http://www.gnu.org/licenses/>. 

16# In the "official" distribution you can find the license in agpl-3.0.txt. 

17# 

18 

19 

20# This code once was in GuiReplayer.py and was split up in this and the former by zarturo. 

21 

22 

23# import L10n 

24# _ = L10n.get_translation() 

25from __future__ import print_function 

26from __future__ import division 

27 

28import contextlib 

29import os 

30# import L10n 

31# _ = L10n.get_translation() 

32 

33from PyQt5.QtWidgets import ( 

34 QFrame, 

35 QScrollArea, 

36 QSizePolicy, 

37 QSplitter, 

38 QVBoxLayout, 

39 QMessageBox, 

40) 

41 

42from time import time 

43from matplotlib.figure import Figure 

44from matplotlib.backends.backend_qt5agg import FigureCanvas 

45from matplotlib.font_manager import FontProperties 

46from numpy import cumsum 

47import Database 

48import Filters 

49# import Charset 

50 

51 

52class GuiGraphViewer(QSplitter): 

53 def __init__(self, querylist, config, parent, colors, debug=True): 

54 QSplitter.__init__(self, parent) 

55 self.sql = querylist 

56 self.conf = config 

57 self.debug = debug 

58 self.parent = parent 

59 self.colors = colors 

60 self.db = Database.Database(self.conf, sql=self.sql) 

61 

62 filters_display = { 

63 "Heroes": True, 

64 "Sites": True, 

65 "Games": True, 

66 "Currencies": True, 

67 "Limits": True, 

68 "LimitSep": True, 

69 "LimitType": True, 

70 "Type": True, 

71 "UseType": "ring", 

72 "Seats": False, 

73 "SeatSep": False, 

74 "Dates": True, 

75 "GraphOps": True, 

76 "Groups": False, 

77 "Button1": True, 

78 "Button2": True, 

79 } 

80 

81 self.filters = Filters.Filters(self.db, display=filters_display) 

82 self.filters.registerButton1Name("Refresh Graph") 

83 self.filters.registerButton1Callback(self.generateGraph) 

84 self.filters.registerButton2Name("Export to File") 

85 self.filters.registerButton2Callback(self.exportGraph) 

86 

87 scroll = QScrollArea() 

88 scroll.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Expanding) 

89 scroll.setWidget(self.filters) 

90 self.addWidget(scroll) 

91 

92 frame = QFrame() 

93 frame.setStyleSheet(f'background-color: {self.colors["background"]}') 

94 self.graphBox = QVBoxLayout() 

95 

96 frame.setLayout(self.graphBox) 

97 self.addWidget(frame) 

98 self.setStretchFactor(0, 0) 

99 self.setStretchFactor(1, 1) 

100 

101 self.fig = None 

102 self.canvas = None 

103 self.exportFile = None 

104 

105 self.db.rollback() 

106 

107 def clearGraphData(self): 

108 with contextlib.suppress(Exception): 

109 if self.canvas: 

110 self.graphBox.removeWidget(self.canvas) 

111 if self.fig is not None: 

112 self.fig.clear() 

113 self.fig = Figure(figsize=(5.0, 4.0), dpi=100) 

114 self.fig.patch.set_facecolor(self.colors["background"]) 

115 if self.canvas is not None: 

116 self.canvas.destroy() 

117 

118 self.canvas = FigureCanvas(self.fig) 

119 self.canvas.setParent(self) 

120 

121 def generateGraph(self, widget): 

122 self.clearGraphData() 

123 

124 sitenos = [] 

125 playerids = [] 

126 # winnings = [] 

127 sites = self.filters.getSites() 

128 heroes = self.filters.getHeroes() 

129 siteids = self.filters.getSiteIds() 

130 limits = self.filters.getLimits() 

131 games = self.filters.getGames() 

132 currencies = self.filters.getCurrencies() 

133 graphops = self.filters.getGraphOps() 

134 display_in = "$" if "$" in graphops else "BB" 

135 names = "" 

136 

137 for site in sites: 

138 sitenos.append(siteids[site]) 

139 _hname = heroes.get(site, "") 

140 if not _hname: 

141 raise ValueError(f"Hero name not found for site {site}") 

142 result = self.db.get_player_id(self.conf, site, _hname) 

143 if result is not None: 

144 playerids.append(int(result)) 

145 names = names + "\n" + _hname + " on " + site 

146 

147 if not sitenos: 

148 print("No sites selected - defaulting to PokerStars") 

149 self.db.rollback() 

150 return 

151 

152 if not playerids: 

153 print("No player ids found") 

154 self.db.rollback() 

155 return 

156 

157 if not limits: 

158 print("No limits found") 

159 self.db.rollback() 

160 return 

161 # debug 

162 # print("currencies selcted:", self.filters.getCurrencies()) 

163 

164 self.ax = self.fig.add_subplot(111) 

165 

166 starttime = time() 

167 (green, blue, red, orange) = self.getRingProfitGraph(playerids, sitenos, limits, games, currencies, display_in) 

168 print(f"Graph generated in: {time() - starttime}") 

169 

170 self.ax.set_xlabel("Hands", color=self.colors["foreground"]) 

171 self.ax.set_facecolor(self.colors["background"]) 

172 self.ax.tick_params(axis="x", colors=self.colors["foreground"]) 

173 self.ax.tick_params(axis="y", colors=self.colors["foreground"]) 

174 self.ax.spines["left"].set_color(self.colors["foreground"]) 

175 self.ax.spines["right"].set_color(self.colors["foreground"]) 

176 self.ax.spines["top"].set_color(self.colors["foreground"]) 

177 self.ax.spines["bottom"].set_color(self.colors["foreground"]) 

178 self.ax.spines.left.set_position(("data", 0)) 

179 self.ax.spines.top.set_color("none") 

180 self.ax.spines.right.set_color("none") 

181 self.ax.spines.bottom.set_position(("data", 0)) 

182 self.ax.xaxis.set_ticks_position("bottom") 

183 self.ax.yaxis.set_ticks_position("left") 

184 

185 self.ax.set_ylabel(display_in, color=self.colors["foreground"]) 

186 self.ax.grid(color=self.colors["grid"], linestyle=":", linewidth=0.2) 

187 if green is None or len(green) == 0: 

188 self.plotGraph() 

189 else: 

190 self.ax.set_title(f"Profit graph for ring games{names}", color=self.colors["foreground"]) 

191 

192 if "showdown" in graphops: 

193 print("blue max:", blue.max()) 

194 self.ax.plot( 

195 blue, 

196 color=self.colors["line_showdown"], 

197 label=("Showdown") + " (%s): %.2f" % (display_in, blue[-1]), 

198 ) 

199 

200 if "nonshowdown" in graphops: 

201 self.ax.plot( 

202 red, 

203 color=self.colors["line_nonshowdown"], 

204 label=("Non-showdown") + " (%s): %.2f" % (display_in, red[-1]), 

205 ) 

206 if "ev" in graphops: 

207 self.ax.plot( 

208 orange, color=self.colors["line_ev"], label=("All-in EV") + " (%s): %.2f" % (display_in, orange[-1]) 

209 ) 

210 self.ax.plot( 

211 green, 

212 color=self.colors["line_hands"], 

213 label=("Hands") + ": %d\n" % len(green) + ("Profit") + ": (%s): %.2f" % (display_in, green[-1]), 

214 ) 

215 

216 handles, labels = self.ax.get_legend_handles_labels() 

217 handles = handles[-1:] + handles[:-1] 

218 labels = labels[-1:] + labels[:-1] 

219 

220 legend = self.ax.legend( 

221 handles, 

222 labels, 

223 loc="upper left", 

224 fancybox=True, 

225 shadow=True, 

226 prop=FontProperties(size="smaller"), 

227 facecolor=self.colors["background"], 

228 labelcolor=self.colors["foreground"], 

229 ) 

230 legend.set_draggable(state=1) 

231 

232 self.graphBox.addWidget(self.canvas) 

233 self.canvas.draw() 

234 

235 def plotGraph(self): 

236 self.ax.set_title("No Data for Player(s) Found") 

237 green = [ 

238 0.0, 

239 0.0, 

240 0.0, 

241 0.0, 

242 500.0, 

243 1000.0, 

244 900.0, 

245 800.0, 

246 700.0, 

247 600.0, 

248 500.0, 

249 400.0, 

250 300.0, 

251 200.0, 

252 100.0, 

253 0.0, 

254 500.0, 

255 1000.0, 

256 1000.0, 

257 1000.0, 

258 1000.0, 

259 1000.0, 

260 1000.0, 

261 1000.0, 

262 1000.0, 

263 1000.0, 

264 1000.0, 

265 1000.0, 

266 1000.0, 

267 1000.0, 

268 875.0, 

269 750.0, 

270 625.0, 

271 500.0, 

272 375.0, 

273 250.0, 

274 125.0, 

275 0.0, 

276 0.0, 

277 0.0, 

278 0.0, 

279 500.0, 

280 1000.0, 

281 900.0, 

282 800.0, 

283 700.0, 

284 600.0, 

285 500.0, 

286 400.0, 

287 300.0, 

288 200.0, 

289 100.0, 

290 0.0, 

291 500.0, 

292 1000.0, 

293 1000.0, 

294 ] 

295 self.ax.plot( 

296 green, 

297 color=self.colors["line_hands"], 

298 linewidth=0.5, 

299 label=("Hands") + ": %d\n" % len(green) + ("Profit") + ": %.2f" % green[-1], 

300 ) 

301 

302 def getRingProfitGraph(self, names, sites, limits, games, currencies, units): 

303 if units == "$": 

304 tmp = self.sql.query["getRingProfitAllHandsPlayerIdSiteInDollars"] 

305 elif units == "BB": 

306 tmp = self.sql.query["getRingProfitAllHandsPlayerIdSiteInBB"] 

307 

308 start_date, end_date = self.filters.getDates() 

309 

310 nametest = str(tuple(names)) 

311 sitetest = str(tuple(sites)) 

312 

313 gametest = "" 

314 

315 for m in list(self.filters.display.items()): 

316 if m[0] == "Games" and m[1]: 

317 if len(games) > 0: 

318 gametest = str(tuple(games)) 

319 gametest = gametest.replace("L", "") 

320 gametest = gametest.replace(",)", ")") 

321 gametest = gametest.replace("u'", "'") 

322 gametest = "and gt.category in %s" % gametest 

323 else: 

324 gametest = "and gt.category IS NULL" 

325 

326 tmp = tmp.replace("<game_test>", gametest) 

327 

328 limittest = self.filters.get_limits_where_clause(limits) 

329 

330 currencytest = str(tuple(currencies)) 

331 currencytest = currencytest.replace(",)", ")") 

332 currencytest = currencytest.replace("u'", "'") 

333 currencytest = "AND gt.currency in %s" % currencytest 

334 

335 game_type = self.filters.getType() 

336 

337 if game_type == "ring": 

338 limittest = limittest + " and gt.type = 'ring' " 

339 elif game_type == "tour": 

340 limittest = limittest + " and gt.type = 'tour' " 

341 

342 tmp = tmp.replace("<player_test>", nametest) 

343 tmp = tmp.replace("<site_test>", sitetest) 

344 tmp = tmp.replace("<startdate_test>", start_date) 

345 tmp = tmp.replace("<enddate_test>", end_date) 

346 tmp = tmp.replace("<limit_test>", limittest) 

347 tmp = tmp.replace("<currency_test>", currencytest) 

348 tmp = tmp.replace(",)", ")") 

349 

350 # debug 

351 # print("Final SQL Request:") 

352 # print(tmp) 

353 

354 self.db.cursor.execute(tmp) 

355 winnings = self.db.cursor.fetchall() 

356 self.db.rollback() 

357 

358 # debug 

359 # print("winning data :") 

360 # print(winnings) 

361 

362 if len(winnings) == 0: 

363 # print("Aucune donnée de gains trouvée") 

364 return (None, None, None, None) 

365 

366 green = [0, *[float(x[1]) for x in winnings]] 

367 blue = [0, *[float(x[1]) if x[2] is True else 0.0 for x in winnings]] 

368 red = [0] 

369 red.extend([float(x[1]) if x[2] is False else 0.0 for x in winnings]) 

370 orange = [0] 

371 orange.extend([float(x[3]) for x in winnings]) 

372 greenline = cumsum(green) 

373 blueline = cumsum(blue) 

374 redline = cumsum(red) 

375 orangeline = cumsum(orange) 

376 

377 # print("Data :") 

378 # print("Green:", green[:10]) # show only the first 10 results 

379 # print("Blue:", blue[:10]) 

380 # print("Red:", red[:10]) 

381 # print("Orange:", orange[:10]) 

382 

383 # print("sum :") 

384 # print("Greenline:", greenline[:10]) 

385 # print("Blueline:", blueline[:10]) 

386 # print("Redline:", redline[:10]) 

387 # print("Orangeline:", orangeline[:10]) 

388 

389 return (greenline / 100, blueline / 100, redline / 100, orangeline / 100) 

390 

391 def exportGraph(self): 

392 if self.fig is None: 

393 return 

394 path = f"{os.getcwd()}/graph.png" 

395 self.fig.savefig(path) 

396 msg = QMessageBox() 

397 msg.setWindowTitle("FPDB 3 info") 

398 mess = f"Your graph is saved in {path}" 

399 msg.setText(mess) 

400 msg.exec()