Coverage for GuiGraphViewer.py: 0%
193 statements
« prev ^ index » next coverage.py v7.6.1, created at 2024-09-27 18:50 +0000
« 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 -*-
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#
20# This code once was in GuiReplayer.py and was split up in this and the former by zarturo.
23# import L10n
24# _ = L10n.get_translation()
25from __future__ import print_function
26from __future__ import division
28import contextlib
29import os
30from past.utils import old_div
31#import L10n
32#_ = L10n.get_translation()
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
47class GuiGraphViewer(QSplitter):
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)
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 }
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)
83 scroll = QScrollArea()
84 scroll.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Expanding)
85 scroll.setWidget(self.filters)
86 self.addWidget(scroll)
88 frame = QFrame()
89 frame.setStyleSheet(f'background-color: {self.colors["background"]}')
90 self.graphBox = QVBoxLayout()
92 frame.setLayout(self.graphBox)
93 self.addWidget(frame)
94 self.setStretchFactor(0, 0)
95 self.setStretchFactor(1, 1)
97 self.fig = None
98 self.canvas = None
99 self.exportFile = None
101 self.db.rollback()
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()
114 self.canvas = FigureCanvas(self.fig)
115 self.canvas.setParent(self)
117 def generateGraph(self, widget):
118 self.clearGraphData()
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 = ""
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
143 if not sitenos:
144 print("No sites selected - defaulting to PokerStars")
145 self.db.rollback()
146 return
148 if not playerids:
149 print("No player ids found")
150 self.db.rollback()
151 return
153 if not limits:
154 print("No limits found")
155 self.db.rollback()
156 return
157 #debug
158 #print("currencies selcted:", self.filters.getCurrencies())
160 self.ax = self.fig.add_subplot(111)
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}")
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')
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'])
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]))
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]))
198 handles, labels = self.ax.get_legend_handles_labels()
199 handles = handles[-1:] + handles[:-1]
200 labels = labels[-1:] + labels[:-1]
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)
205 self.graphBox.addWidget(self.canvas)
206 self.canvas.draw()
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])
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']
225 start_date, end_date = self.filters.getDates()
227 nametest = str(tuple(names))
228 sitetest = str(tuple(sites))
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)
242 limittest = self.filters.get_limits_where_clause(limits)
244 currencytest = str(tuple(currencies))
245 currencytest = currencytest.replace(",)",")")
246 currencytest = currencytest.replace("u'","'")
247 currencytest = "AND gt.currency in %s" % currencytest
249 if type == 'ring':
250 limittest = limittest + " and gt.type = 'ring' "
251 elif type == 'tour':
252 limittest = limittest + " and gt.type = 'tour' "
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(",)", ")")
262 #debug
263 #print("Final SQL Request:")
264 #print(tmp)
266 self.db.cursor.execute(tmp)
267 winnings = self.db.cursor.fetchall()
268 self.db.rollback()
270 #debug
271 #print("winning data :")
272 #print(winnings)
274 if len(winnings) == 0:
275 #print("Aucune donnée de gains trouvée")
276 return (None, None, None, None)
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)
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])
297 # print("sum :")
298 # print("Greenline:", greenline[:10])
299 # print("Blueline:", blueline[:10])
300 # print("Redline:", redline[:10])
301 # print("Orangeline:", orangeline[:10])
303 return (greenline / 100, blueline / 100, redline / 100, orangeline / 100)
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()