Coverage for GuiGraphViewer.py: 0%

193 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-09-27 18:50 +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 

30from past.utils import old_div 

31#import L10n 

32#_ = L10n.get_translation() 

33 

34from PyQt5.QtCore import Qt 

35from PyQt5.QtWidgets import (QFrame, QHBoxLayout, QLabel, QScrollArea, QSizePolicy, 

36 QSplitter, QVBoxLayout, QWidget, QFileDialog, QMessageBox) 

37import contextlib 

38from time import time 

39from matplotlib.figure import Figure 

40from matplotlib.backends.backend_qt5agg import FigureCanvas 

41from matplotlib.font_manager import FontProperties 

42from numpy import cumsum 

43import Database 

44import Filters 

45import Charset 

46 

47class GuiGraphViewer(QSplitter): 

48 

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

50 QSplitter.__init__(self, parent) 

51 self.sql = querylist 

52 self.conf = config 

53 self.debug = debug 

54 self.parent = parent 

55 self.colors = colors 

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

57 

58 filters_display = { 

59 "Heroes": True, 

60 "Sites": True, 

61 "Games": True, 

62 "Currencies": True, 

63 "Limits": True, 

64 "LimitSep": True, 

65 "LimitType": True, 

66 "Type": True, 

67 "UseType": 'ring', 

68 "Seats": False, 

69 "SeatSep": False, 

70 "Dates": True, 

71 "GraphOps": True, 

72 "Groups": False, 

73 "Button1": True, 

74 "Button2": True 

75 } 

76 

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

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

79 self.filters.registerButton1Callback(self.generateGraph) 

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

81 self.filters.registerButton2Callback(self.exportGraph) 

82 

83 scroll = QScrollArea() 

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

85 scroll.setWidget(self.filters) 

86 self.addWidget(scroll) 

87 

88 frame = QFrame() 

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

90 self.graphBox = QVBoxLayout() 

91 

92 frame.setLayout(self.graphBox) 

93 self.addWidget(frame) 

94 self.setStretchFactor(0, 0) 

95 self.setStretchFactor(1, 1) 

96 

97 self.fig = None 

98 self.canvas = None 

99 self.exportFile = None 

100 

101 self.db.rollback() 

102 

103 def clearGraphData(self): 

104 with contextlib.suppress(Exception): 

105 if self.canvas: 

106 self.graphBox.removeWidget(self.canvas) 

107 if self.fig is not None: 

108 self.fig.clear() 

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

110 self.fig.patch.set_facecolor(self.colors['background']) 

111 if self.canvas is not None: 

112 self.canvas.destroy() 

113 

114 self.canvas = FigureCanvas(self.fig) 

115 self.canvas.setParent(self) 

116 

117 def generateGraph(self, widget): 

118 self.clearGraphData() 

119 

120 sitenos = [] 

121 playerids = [] 

122 winnings = [] 

123 sites = self.filters.getSites() 

124 heroes = self.filters.getHeroes() 

125 siteids = self.filters.getSiteIds() 

126 limits = self.filters.getLimits() 

127 games = self.filters.getGames() 

128 currencies = self.filters.getCurrencies() 

129 graphops = self.filters.getGraphOps() 

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

131 names = "" 

132 

133 

134 

135 for site in sites: 

136 sitenos.append(siteids[site]) 

137 _hname = Charset.to_utf8(heroes[site]) 

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

139 if result is not None: 

140 playerids.append(int(result)) 

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

142 

143 if not sitenos: 

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

145 self.db.rollback() 

146 return 

147 

148 if not playerids: 

149 print("No player ids found") 

150 self.db.rollback() 

151 return 

152 

153 if not limits: 

154 print("No limits found") 

155 self.db.rollback() 

156 return 

157 #debug 

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

159 

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

161 

162 starttime = time() 

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

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

165 

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

167 self.ax.set_facecolor(self.colors['background']) 

168 self.ax.tick_params(axis='x', colors=self.colors['foreground']) 

169 self.ax.tick_params(axis='y', colors=self.colors['foreground']) 

170 self.ax.spines['left'].set_color(self.colors['foreground']) 

171 self.ax.spines['right'].set_color(self.colors['foreground']) 

172 self.ax.spines['top'].set_color(self.colors['foreground']) 

173 self.ax.spines['bottom'].set_color(self.colors['foreground']) 

174 self.ax.spines.left.set_position(('data', 0)) 

175 self.ax.spines.top.set_color('none') 

176 self.ax.spines.right.set_color('none') 

177 self.ax.spines.bottom.set_position(('data', 0)) 

178 self.ax.xaxis.set_ticks_position('bottom') 

179 self.ax.yaxis.set_ticks_position('left') 

180 

181 self.ax.set_ylabel(display_in, color=self.colors['foreground']) 

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

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

184 self.plotGraph() 

185 else: 

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

187 

188 if 'showdown' in graphops: 

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

190 self.ax.plot(blue, color=self.colors['line_showdown'], label=('Showdown') + ' (%s): %.2f' % (display_in, blue[-1])) 

191 

192 if 'nonshowdown' in graphops: 

193 self.ax.plot(red, color=self.colors['line_nonshowdown'], label=('Non-showdown') + ' (%s): %.2f' % (display_in, red[-1])) 

194 if 'ev' in graphops: 

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

196 self.ax.plot(green, color=self.colors['line_hands'], label=('Hands') + ': %d\n' % len(green) + ('Profit') + ': (%s): %.2f' % (display_in, green[-1])) 

197 

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

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

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

201 

202 legend = self.ax.legend(handles, labels, loc='upper left', fancybox=True, shadow=True, prop=FontProperties(size='smaller'), facecolor=self.colors['background'], labelcolor=self.colors['foreground']) 

203 legend.set_draggable(state=1) 

204 

205 self.graphBox.addWidget(self.canvas) 

206 self.canvas.draw() 

207 

208 def plotGraph(self): 

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

210 green = [0., 0., 0., 0., 500., 1000., 900., 800., 

211 700., 600., 500., 400., 300., 200., 100., 0., 

212 500., 1000., 1000., 1000., 1000., 1000., 1000., 1000., 

213 1000., 1000., 1000., 1000., 1000., 1000., 875., 750., 

214 625., 500., 375., 250., 125., 0., 0., 0., 

215 0., 500., 1000., 900., 800., 700., 600., 500., 

216 400., 300., 200., 100., 0., 500., 1000., 1000.] 

217 self.ax.plot(green, color=self.colors['line_hands'], linewidth=0.5, label=('Hands') + ': %d\n' % len(green) + ('Profit') + ': %.2f' % green[-1]) 

218 

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

220 if units == '$': 

221 tmp = self.sql.query['getRingProfitAllHandsPlayerIdSiteInDollars'] 

222 elif units == 'BB': 

223 tmp = self.sql.query['getRingProfitAllHandsPlayerIdSiteInBB'] 

224 

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

226 

227 nametest = str(tuple(names)) 

228 sitetest = str(tuple(sites)) 

229 

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

231 if m[0] == 'Games' and m[1]: 

232 if len(games) > 0: 

233 gametest = str(tuple(games)) 

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

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

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

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

238 else: 

239 gametest = "and gt.category IS NULL" 

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

241 

242 limittest = self.filters.get_limits_where_clause(limits) 

243 

244 currencytest = str(tuple(currencies)) 

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

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

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

248 

249 if type == 'ring': 

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

251 elif type == 'tour': 

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

253 

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

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

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

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

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

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

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

261 

262 #debug 

263 #print("Final SQL Request:") 

264 #print(tmp) 

265 

266 self.db.cursor.execute(tmp) 

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

268 self.db.rollback() 

269 

270 #debug 

271 #print("winning data :") 

272 #print(winnings) 

273 

274 if len(winnings) == 0: 

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

276 return (None, None, None, None) 

277 

278 

279 

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

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

282 red = [0] 

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

284 orange = [0] 

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

286 greenline = cumsum(green) 

287 blueline = cumsum(blue) 

288 redline = cumsum(red) 

289 orangeline = cumsum(orange) 

290 

291 # print("Data :") 

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

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

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

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

296 

297 # print("sum :") 

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

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

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

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

302 

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

304 

305 def exportGraph(self): 

306 if self.fig is None: 

307 return 

308 path = f'{os.getcwd()}/graph.png' 

309 self.fig.savefig(path) 

310 msg = QMessageBox() 

311 msg.setWindowTitle("FPDB 3 info") 

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

313 msg.setText(mess) 

314 msg.exec()