Coverage for GuiGraphViewer.py: 0%
192 statements
« prev ^ index » next coverage.py v7.6.3, created at 2024-10-15 19:33 +0000
« 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 -*-
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
30# import L10n
31# _ = L10n.get_translation()
33from PyQt5.QtWidgets import (
34 QFrame,
35 QScrollArea,
36 QSizePolicy,
37 QSplitter,
38 QVBoxLayout,
39 QMessageBox,
40)
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
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)
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 }
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)
87 scroll = QScrollArea()
88 scroll.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Expanding)
89 scroll.setWidget(self.filters)
90 self.addWidget(scroll)
92 frame = QFrame()
93 frame.setStyleSheet(f'background-color: {self.colors["background"]}')
94 self.graphBox = QVBoxLayout()
96 frame.setLayout(self.graphBox)
97 self.addWidget(frame)
98 self.setStretchFactor(0, 0)
99 self.setStretchFactor(1, 1)
101 self.fig = None
102 self.canvas = None
103 self.exportFile = None
105 self.db.rollback()
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()
118 self.canvas = FigureCanvas(self.fig)
119 self.canvas.setParent(self)
121 def generateGraph(self, widget):
122 self.clearGraphData()
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 = ""
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
147 if not sitenos:
148 print("No sites selected - defaulting to PokerStars")
149 self.db.rollback()
150 return
152 if not playerids:
153 print("No player ids found")
154 self.db.rollback()
155 return
157 if not limits:
158 print("No limits found")
159 self.db.rollback()
160 return
161 # debug
162 # print("currencies selcted:", self.filters.getCurrencies())
164 self.ax = self.fig.add_subplot(111)
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}")
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")
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"])
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 )
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 )
216 handles, labels = self.ax.get_legend_handles_labels()
217 handles = handles[-1:] + handles[:-1]
218 labels = labels[-1:] + labels[:-1]
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)
232 self.graphBox.addWidget(self.canvas)
233 self.canvas.draw()
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 )
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"]
308 start_date, end_date = self.filters.getDates()
310 nametest = str(tuple(names))
311 sitetest = str(tuple(sites))
313 gametest = ""
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"
326 tmp = tmp.replace("<game_test>", gametest)
328 limittest = self.filters.get_limits_where_clause(limits)
330 currencytest = str(tuple(currencies))
331 currencytest = currencytest.replace(",)", ")")
332 currencytest = currencytest.replace("u'", "'")
333 currencytest = "AND gt.currency in %s" % currencytest
335 game_type = self.filters.getType()
337 if game_type == "ring":
338 limittest = limittest + " and gt.type = 'ring' "
339 elif game_type == "tour":
340 limittest = limittest + " and gt.type = 'tour' "
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(",)", ")")
350 # debug
351 # print("Final SQL Request:")
352 # print(tmp)
354 self.db.cursor.execute(tmp)
355 winnings = self.db.cursor.fetchall()
356 self.db.rollback()
358 # debug
359 # print("winning data :")
360 # print(winnings)
362 if len(winnings) == 0:
363 # print("Aucune donnée de gains trouvée")
364 return (None, None, None, None)
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)
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])
383 # print("sum :")
384 # print("Greenline:", greenline[:10])
385 # print("Blueline:", blueline[:10])
386 # print("Redline:", redline[:10])
387 # print("Orangeline:", orangeline[:10])
389 return (greenline / 100, blueline / 100, redline / 100, orangeline / 100)
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()