Coverage for GuiGraphViewer.py: 0%
194 statements
« prev ^ index » next coverage.py v7.6.7, created at 2024-11-18 00:10 +0000
« 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 -*-
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
46import logging
47from numpy import cumsum
48import Database
49import Filters
50# import Charset
52log = logging.getLogger("filter")
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)
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 }
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)
90 scroll = QScrollArea()
91 scroll.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Expanding)
92 scroll.setWidget(self.filters)
93 self.addWidget(scroll)
95 frame = QFrame()
96 frame.setStyleSheet(f'background-color: {self.colors["background"]}')
97 self.graphBox = QVBoxLayout()
99 frame.setLayout(self.graphBox)
100 self.addWidget(frame)
101 self.setStretchFactor(0, 0)
102 self.setStretchFactor(1, 1)
104 self.fig = None
105 self.canvas = None
106 self.exportFile = None
108 self.db.rollback()
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()
121 self.canvas = FigureCanvas(self.fig)
122 self.canvas.setParent(self)
124 def generateGraph(self, widget):
125 self.clearGraphData()
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 = ""
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
150 if not sitenos:
151 log.debug("No sites selected - defaulting to PokerStars")
152 self.db.rollback()
153 return
155 if not playerids:
156 log.debug("No player ids found")
157 self.db.rollback()
158 return
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())
167 self.ax = self.fig.add_subplot(111)
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}")
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")
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"])
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 )
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 )
219 handles, labels = self.ax.get_legend_handles_labels()
220 handles = handles[-1:] + handles[:-1]
221 labels = labels[-1:] + labels[:-1]
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)
235 self.graphBox.addWidget(self.canvas)
236 self.canvas.draw()
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 )
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"]
311 start_date, end_date = self.filters.getDates()
313 nametest = str(tuple(names))
314 sitetest = str(tuple(sites))
316 gametest = ""
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"
329 tmp = tmp.replace("<game_test>", gametest)
331 limittest = self.filters.get_limits_where_clause(limits)
333 currencytest = str(tuple(currencies))
334 currencytest = currencytest.replace(",)", ")")
335 currencytest = currencytest.replace("u'", "'")
336 currencytest = "AND gt.currency in %s" % currencytest
338 game_type = self.filters.getType()
340 if game_type == "ring":
341 limittest = limittest + " and gt.type = 'ring' "
342 elif game_type == "tour":
343 limittest = limittest + " and gt.type = 'tour' "
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(",)", ")")
353 # debug
354 # log.debug("Final SQL Request:")
355 # log.debug(tmp)
357 self.db.cursor.execute(tmp)
358 winnings = self.db.cursor.fetchall()
359 self.db.rollback()
361 # debug
362 # log.debug("winning data :")
363 # log.debug(winnings)
365 if len(winnings) == 0:
366 # log.debug("Aucune donnée de gains trouvée")
367 return (None, None, None, None)
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)
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])
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])
392 return (greenline / 100, blueline / 100, redline / 100, orangeline / 100)
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()