Coverage for GuiGraphViewer.py: 0%

194 statements  

« prev     ^ index     » next       coverage.py v7.6.7, created at 2024-11-18 00:10 +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 

46import logging 

47from numpy import cumsum 

48import Database 

49import Filters 

50# import Charset 

51 

52log = logging.getLogger("filter") 

53 

54 

55class GuiGraphViewer(QSplitter): 

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

57 QSplitter.__init__(self, parent) 

58 self.sql = querylist 

59 self.conf = config 

60 self.debug = debug 

61 self.parent = parent 

62 self.colors = colors 

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

64 

65 filters_display = { 

66 "Heroes": True, 

67 "Sites": True, 

68 "Games": True, 

69 "Currencies": True, 

70 "Limits": True, 

71 "LimitSep": True, 

72 "LimitType": True, 

73 "Type": True, 

74 "UseType": "ring", 

75 "Seats": False, 

76 "SeatSep": False, 

77 "Dates": True, 

78 "GraphOps": True, 

79 "Groups": False, 

80 "Button1": True, 

81 "Button2": True, 

82 } 

83 

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

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

86 self.filters.registerButton1Callback(self.generateGraph) 

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

88 self.filters.registerButton2Callback(self.exportGraph) 

89 

90 scroll = QScrollArea() 

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

92 scroll.setWidget(self.filters) 

93 self.addWidget(scroll) 

94 

95 frame = QFrame() 

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

97 self.graphBox = QVBoxLayout() 

98 

99 frame.setLayout(self.graphBox) 

100 self.addWidget(frame) 

101 self.setStretchFactor(0, 0) 

102 self.setStretchFactor(1, 1) 

103 

104 self.fig = None 

105 self.canvas = None 

106 self.exportFile = None 

107 

108 self.db.rollback() 

109 

110 def clearGraphData(self): 

111 with contextlib.suppress(Exception): 

112 if self.canvas: 

113 self.graphBox.removeWidget(self.canvas) 

114 if self.fig is not None: 

115 self.fig.clear() 

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

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

118 if self.canvas is not None: 

119 self.canvas.destroy() 

120 

121 self.canvas = FigureCanvas(self.fig) 

122 self.canvas.setParent(self) 

123 

124 def generateGraph(self, widget): 

125 self.clearGraphData() 

126 

127 sitenos = [] 

128 playerids = [] 

129 # winnings = [] 

130 sites = self.filters.getSites() 

131 heroes = self.filters.getHeroes() 

132 siteids = self.filters.getSiteIds() 

133 limits = self.filters.getLimits() 

134 games = self.filters.getGames() 

135 currencies = self.filters.getCurrencies() 

136 graphops = self.filters.getGraphOps() 

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

138 names = "" 

139 

140 for site in sites: 

141 sitenos.append(siteids[site]) 

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

143 if not _hname: 

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

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

146 if result is not None: 

147 playerids.append(int(result)) 

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

149 

150 if not sitenos: 

151 log.debug("No sites selected - defaulting to PokerStars") 

152 self.db.rollback() 

153 return 

154 

155 if not playerids: 

156 log.debug("No player ids found") 

157 self.db.rollback() 

158 return 

159 

160 if not limits: 

161 log.debug("No limits found") 

162 self.db.rollback() 

163 return 

164 # debug 

165 # log.debug("currencies selcted:", self.filters.getCurrencies()) 

166 

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

168 

169 starttime = time() 

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

171 log.debug(f"Graph generated in: {time() - starttime}") 

172 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

187 

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

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

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

191 self.plotGraph() 

192 else: 

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

194 

195 if "showdown" in graphops: 

196 log.debug("blue max:", blue.max()) 

197 self.ax.plot( 

198 blue, 

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

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

201 ) 

202 

203 if "nonshowdown" in graphops: 

204 self.ax.plot( 

205 red, 

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

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

208 ) 

209 if "ev" in graphops: 

210 self.ax.plot( 

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

212 ) 

213 self.ax.plot( 

214 green, 

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

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

217 ) 

218 

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

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

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

222 

223 legend = self.ax.legend( 

224 handles, 

225 labels, 

226 loc="upper left", 

227 fancybox=True, 

228 shadow=True, 

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

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

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

232 ) 

233 legend.set_draggable(state=1) 

234 

235 self.graphBox.addWidget(self.canvas) 

236 self.canvas.draw() 

237 

238 def plotGraph(self): 

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

240 green = [ 

241 0.0, 

242 0.0, 

243 0.0, 

244 0.0, 

245 500.0, 

246 1000.0, 

247 900.0, 

248 800.0, 

249 700.0, 

250 600.0, 

251 500.0, 

252 400.0, 

253 300.0, 

254 200.0, 

255 100.0, 

256 0.0, 

257 500.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 1000.0, 

269 1000.0, 

270 1000.0, 

271 875.0, 

272 750.0, 

273 625.0, 

274 500.0, 

275 375.0, 

276 250.0, 

277 125.0, 

278 0.0, 

279 0.0, 

280 0.0, 

281 0.0, 

282 500.0, 

283 1000.0, 

284 900.0, 

285 800.0, 

286 700.0, 

287 600.0, 

288 500.0, 

289 400.0, 

290 300.0, 

291 200.0, 

292 100.0, 

293 0.0, 

294 500.0, 

295 1000.0, 

296 1000.0, 

297 ] 

298 self.ax.plot( 

299 green, 

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

301 linewidth=0.5, 

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

303 ) 

304 

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

306 if units == "$": 

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

308 elif units == "BB": 

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

310 

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

312 

313 nametest = str(tuple(names)) 

314 sitetest = str(tuple(sites)) 

315 

316 gametest = "" 

317 

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

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

320 if len(games) > 0: 

321 gametest = str(tuple(games)) 

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

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

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

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

326 else: 

327 gametest = "and gt.category IS NULL" 

328 

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

330 

331 limittest = self.filters.get_limits_where_clause(limits) 

332 

333 currencytest = str(tuple(currencies)) 

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

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

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

337 

338 game_type = self.filters.getType() 

339 

340 if game_type == "ring": 

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

342 elif game_type == "tour": 

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

344 

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

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

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

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

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

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

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

352 

353 # debug 

354 # log.debug("Final SQL Request:") 

355 # log.debug(tmp) 

356 

357 self.db.cursor.execute(tmp) 

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

359 self.db.rollback() 

360 

361 # debug 

362 # log.debug("winning data :") 

363 # log.debug(winnings) 

364 

365 if len(winnings) == 0: 

366 # log.debug("Aucune donnée de gains trouvée") 

367 return (None, None, None, None) 

368 

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

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

371 red = [0] 

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

373 orange = [0] 

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

375 greenline = cumsum(green) 

376 blueline = cumsum(blue) 

377 redline = cumsum(red) 

378 orangeline = cumsum(orange) 

379 

380 # log.debug("Data :") 

381 # log.debug("Green:", green[:10]) # show only the first 10 results 

382 # log.debug("Blue:", blue[:10]) 

383 # log.debug("Red:", red[:10]) 

384 # log.debug("Orange:", orange[:10]) 

385 

386 # log.debug("sum :") 

387 # log.debug("Greenline:", greenline[:10]) 

388 # log.debug("Blueline:", blueline[:10]) 

389 # log.debug("Redline:", redline[:10]) 

390 # log.debug("Orangeline:", orangeline[:10]) 

391 

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

393 

394 def exportGraph(self): 

395 if self.fig is None: 

396 return 

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

398 self.fig.savefig(path) 

399 msg = QMessageBox() 

400 msg.setWindowTitle("FPDB 3 info") 

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

402 msg.setText(mess) 

403 msg.exec()