2 # -*- coding: utf-8 -*-
4 #Copyright 2008-2011 Carl Gherardi
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.
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.
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.
19 _
= L10n
.get_translation()
29 from datetime
import datetime
38 calluse
= not 'matplotlib' in sys
.modules
41 matplotlib
.use('GTKCairo')
42 from matplotlib
.figure
import Figure
43 from matplotlib
.backends
.backend_gtk
import FigureCanvasGTK
as FigureCanvas
44 from matplotlib
.backends
.backend_gtkagg
import NavigationToolbar2GTKAgg
as NavigationToolbar
45 from matplotlib
.font_manager
import FontProperties
46 from numpy
import arange
, cumsum
48 except ImportError, inst
:
49 print _("""Failed to load libs for graphing, graphing will not function. Please install numpy and matplotlib if you want to use graphs.""")
50 print _("""This is of no consequence for other parts of the program, e.g. import and HUD are NOT affected by this problem.""")
51 print "ImportError: %s" % inst
.args
53 class GuiTourneyGraphViewer (threading
.Thread
):
55 def __init__(self
, querylist
, config
, parent
, debug
=True):
56 """Constructor for GraphViewer"""
61 #print "start of GraphViewer constructor"
62 self
.db
= Database
.Database(self
.conf
, sql
=self
.sql
)
65 filters_display
= { "Heroes" : True,
81 self
.filters
= Filters
.Filters(self
.db
, self
.conf
, self
.sql
, 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 self
.mainHBox
= gtk
.HBox(False, 0)
90 self
.leftPanelBox
= self
.filters
.get_vbox()
92 self
.hpane
= gtk
.HPaned()
93 self
.hpane
.pack1(self
.leftPanelBox
)
94 self
.mainHBox
.add(self
.hpane
)
95 # hierarchy: self.mainHBox / self.hpane / self.graphBox / self.canvas / self.fig / self.ax
97 self
.graphBox
= gtk
.VBox(False, 0)
99 self
.hpane
.pack2(self
.graphBox
)
103 #self.exportButton.set_sensitive(False)
110 """returns the vbox of this thread"""
114 def clearGraphData(self
):
119 self
.graphBox
.remove(self
.canvas
)
125 self
.fig
= Figure(figsize
=(5,4), dpi
=100)
126 if self
.canvas
is not None:
127 self
.canvas
.destroy()
129 self
.canvas
= FigureCanvas(self
.fig
) # a gtk.DrawingArea
131 err
= traceback
.extract_tb(sys
.exc_info()[2])[-1]
132 print _("Error:")+" "+err
[2]+"("+str(err
[1])+"): "+str(sys
.exc_info()[1])
135 def generateGraph(self
, widget
, data
):
137 self
.clearGraphData()
142 sites
= self
.filters
.getSites()
143 heroes
= self
.filters
.getHeroes()
144 siteids
= self
.filters
.getSiteIds()
146 # Which sites are selected?
148 if sites
[site
] == True:
149 sitenos
.append(siteids
[site
])
150 _hname
= Charset
.to_utf8(heroes
[site
])
151 result
= self
.db
.get_player_id(self
.conf
, site
, _hname
)
152 if result
is not None:
153 playerids
.append(int(result
))
156 #Should probably pop up here.
157 print _("No sites selected - defaulting to PokerStars")
162 print _("No player ids found")
166 #Set graph properties
167 self
.ax
= self
.fig
.add_subplot(111)
169 #Get graph data from DB
171 green
= self
.getData(playerids
, sitenos
)
172 print _("Graph generated in: %s") %(time() - starttime
)
175 #Set axis labels and grid overlay properites
176 self
.ax
.set_xlabel(_("Tournaments"), fontsize
= 12)
177 self
.ax
.set_ylabel("$", fontsize
= 12)
178 self
.ax
.grid(color
='g', linestyle
=':', linewidth
=0.2)
179 if green
== None or green
== []:
180 self
.ax
.set_title(_("No Data for Player(s) Found"))
181 green
= ([ 0., 0., 0., 0., 500., 1000., 900., 800.,
182 700., 600., 500., 400., 300., 200., 100., 0.,
183 500., 1000., 1000., 1000., 1000., 1000., 1000., 1000.,
184 1000., 1000., 1000., 1000., 1000., 1000., 875., 750.,
185 625., 500., 375., 250., 125., 0., 0., 0.,
186 0., 500., 1000., 900., 800., 700., 600., 500.,
187 400., 300., 200., 100., 0., 500., 1000., 1000.])
188 red
= ([ 0., 0., 0., 0., 500., 1000., 900., 800.,
189 700., 600., 500., 400., 300., 200., 100., 0.,
190 0., 0., 0., 0., 0., 0., 125., 250.,
191 375., 500., 500., 500., 500., 500., 500., 500.,
192 500., 500., 375., 250., 125., 0., 0., 0.,
193 0., 500., 1000., 900., 800., 700., 600., 500.,
194 400., 300., 200., 100., 0., 500., 1000., 1000.])
195 blue
= ([ 0., 0., 0., 0., 500., 1000., 900., 800.,
196 700., 600., 500., 400., 300., 200., 100., 0.,
197 0., 0., 0., 0., 0., 0., 125., 250.,
198 375., 500., 625., 750., 875., 1000., 875., 750.,
199 625., 500., 375., 250., 125., 0., 0., 0.,
200 0., 500., 1000., 900., 800., 700., 600., 500.,
201 400., 300., 200., 100., 0., 500., 1000., 1000.])
203 self
.ax
.plot(green
, color
='green', label
=_('Tournaments: %d\nProfit: $%.2f') %(len(green
), green
[-1]))
204 #self.ax.plot(blue, color='blue', label=_('Showdown: $%.2f') %(blue[-1]))
205 #self.ax.plot(red, color='red', label=_('Non-showdown: $%.2f') %(red[-1]))
206 self
.graphBox
.add(self
.canvas
)
210 #TODO: Do something useful like alert user
211 #print "No hands returned by graph query"
213 self
.ax
.set_title(_("Tournament Results"))
216 self
.ax
.plot(green
, color
='green', label
=_('Tournaments: %d\nProfit: $%.2f') %(len(green
), green
[-1]))
217 #self.ax.plot(blue, color='blue', label=_('Showdown: $%.2f') %(blue[-1]))
218 #self.ax.plot(red, color='red', label=_('Non-showdown: $%.2f') %(red[-1]))
219 if sys
.version
[0:3] == '2.5':
220 self
.ax
.legend(loc
='upper left', shadow
=True, prop
=FontProperties(size
='smaller'))
222 self
.ax
.legend(loc
='upper left', fancybox
=True, shadow
=True, prop
=FontProperties(size
='smaller'))
224 self
.graphBox
.add(self
.canvas
)
227 #self.exportButton.set_sensitive(True)
229 err
= traceback
.extract_tb(sys
.exc_info()[2])[-1]
230 print _("Error:")+" "+err
[2]+"("+str(err
[1])+"): "+str(sys
.exc_info()[1])
232 #end of def showClicked
234 def getData(self
, names
, sites
):
235 tmp
= self
.sql
.query
['tourneyResults']
236 print "DEBUG: getData"
237 start_date
, end_date
= self
.filters
.getDates()
239 #Buggered if I can find a way to do this 'nicely' take a list of integers and longs
240 # and turn it into a tuple readale by sql.
241 # [5L] into (5) not (5,) and [5L, 2829L] into (5, 2829)
242 nametest
= str(tuple(names
))
243 sitetest
= str(tuple(sites
))
245 #Must be a nicer way to deal with tuples of size 1 ie. (2,) - which makes sql barf
246 tmp
= tmp
.replace("<player_test>", nametest
)
247 tmp
= tmp
.replace("<site_test>", sitetest
)
248 tmp
= tmp
.replace("<startdate_test>", start_date
)
249 tmp
= tmp
.replace("<enddate_test>", end_date
)
250 tmp
= tmp
.replace(",)", ")")
252 print "DEBUG: sql query:"
254 self
.db
.cursor
.execute(tmp
)
255 #returns (HandId,Winnings,Costs,Profit)
256 winnings
= self
.db
.cursor
.fetchall()
259 if len(winnings
) == 0:
262 green
= map(lambda x
:float(x
[1]), winnings
)
263 #blue = map(lambda x: float(x[1]) if x[2] == True else 0.0, winnings)
264 #red = map(lambda x: float(x[1]) if x[2] == False else 0.0, winnings)
265 greenline
= cumsum(green
)
266 #blueline = cumsum(blue)
267 #redline = cumsum(red)
268 return (greenline
/100)
270 def exportGraph (self
, widget
, data
):
272 return # Might want to disable export button until something has been generated.
274 dia_chooser
= gtk
.FileChooserDialog(title
=_("Please choose the directory you wish to export to:"),
275 action
=gtk
.FILE_CHOOSER_ACTION_SELECT_FOLDER
,
276 buttons
=(gtk
.STOCK_CANCEL
,gtk
.RESPONSE_CANCEL
,gtk
.STOCK_OK
,gtk
.RESPONSE_OK
))
277 dia_chooser
.set_destroy_with_parent(True)
278 dia_chooser
.set_transient_for(self
.parent
)
280 dia_chooser
.set_filename(self
.exportFile
) # use previously chosen export path as default
284 response
= dia_chooser
.run()
286 if response
<> gtk
.RESPONSE_OK
:
287 print _('Closed, no graph exported')
288 dia_chooser
.destroy()
291 # generate a unique filename for export
293 now_formatted
= now
.strftime("%Y%m%d%H%M%S")
294 self
.exportFile
= dia_chooser
.get_filename() + "/fpdb" + now_formatted
+ ".png"
295 dia_chooser
.destroy()
297 #print "DEBUG: self.exportFile = %s" %(self.exportFile)
298 self
.fig
.savefig(self
.exportFile
, format
="png")
300 #display info box to confirm graph created
301 diainfo
= gtk
.MessageDialog(parent
=self
.parent
,
302 flags
=gtk
.DIALOG_DESTROY_WITH_PARENT
,
303 type=gtk
.MESSAGE_INFO
,
304 buttons
=gtk
.BUTTONS_OK
,
305 message_format
=_("Graph created"))
306 diainfo
.format_secondary_text(self
.exportFile
)
310 #end of def exportGraph