2008-09-20 06:56:16 +02:00
|
|
|
#!/usr/bin/python
|
|
|
|
|
|
|
|
#Copyright 2008 Steffen Jobbagy-Felso
|
|
|
|
#This program is free software: you can redistribute it and/or modify
|
|
|
|
#it under the terms of the GNU Affero General Public License as published by
|
|
|
|
#the Free Software Foundation, version 3 of the License.
|
|
|
|
#
|
|
|
|
#This program is distributed in the hope that it will be useful,
|
|
|
|
#but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
#GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
#You should have received a copy of the GNU Affero General Public License
|
|
|
|
#along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
#In the "official" distribution you can find the license in
|
|
|
|
#agpl-3.0.txt in the docs folder of the package.
|
|
|
|
|
|
|
|
import threading
|
|
|
|
import pygtk
|
|
|
|
pygtk.require('2.0')
|
|
|
|
import gtk
|
|
|
|
import os
|
2009-01-21 17:40:05 +01:00
|
|
|
from time import *
|
2008-09-26 03:54:08 +02:00
|
|
|
#import pokereval
|
2008-09-20 06:56:16 +02:00
|
|
|
|
|
|
|
try:
|
2008-12-04 08:15:38 +01:00
|
|
|
import matplotlib
|
|
|
|
matplotlib.use('GTK')
|
|
|
|
from matplotlib.figure import Figure
|
|
|
|
from matplotlib.backends.backend_gtk import FigureCanvasGTK as FigureCanvas
|
|
|
|
from matplotlib.backends.backend_gtkagg import NavigationToolbar2GTKAgg as NavigationToolbar
|
|
|
|
from numpy import arange, cumsum
|
|
|
|
from pylab import *
|
2008-09-20 06:56:16 +02:00
|
|
|
except:
|
2008-12-04 08:15:38 +01:00
|
|
|
print """Failed to load libs for graphing, graphing will not function. Please in
|
2008-11-14 05:58:35 +01:00
|
|
|
stall numpy and matplotlib if you want to use graphs."""
|
2008-12-04 08:15:38 +01:00
|
|
|
print """This is of no consequence for other parts of the program, e.g. import
|
|
|
|
and HUD are NOT affected by this problem."""
|
2008-09-20 06:56:16 +02:00
|
|
|
|
|
|
|
import fpdb_import
|
2009-07-19 22:19:58 +02:00
|
|
|
import Database
|
2009-04-11 13:14:32 +02:00
|
|
|
import Filters
|
2008-09-20 06:56:16 +02:00
|
|
|
|
|
|
|
class GuiGraphViewer (threading.Thread):
|
2009-04-11 13:14:32 +02:00
|
|
|
|
2009-06-23 00:30:54 +02:00
|
|
|
def __init__(self, querylist, config, debug=True):
|
2009-04-11 13:14:32 +02:00
|
|
|
"""Constructor for GraphViewer"""
|
2009-06-23 00:30:54 +02:00
|
|
|
self.sql = querylist
|
2009-04-11 13:14:32 +02:00
|
|
|
self.conf = config
|
2009-07-19 22:19:58 +02:00
|
|
|
self.debug = debug
|
|
|
|
#print "start of GraphViewer constructor"
|
|
|
|
self.db = Database.Database(self.conf, sql=self.sql)
|
|
|
|
|
2009-04-11 13:14:32 +02:00
|
|
|
|
2009-04-17 06:44:43 +02:00
|
|
|
filters_display = { "Heroes" : True,
|
|
|
|
"Sites" : True,
|
|
|
|
"Games" : True,
|
|
|
|
"Limits" : True,
|
2009-05-10 23:30:26 +02:00
|
|
|
"Seats" : False,
|
2009-04-17 06:44:43 +02:00
|
|
|
"Dates" : True,
|
|
|
|
"Button1" : True,
|
|
|
|
"Button2" : True
|
|
|
|
}
|
|
|
|
|
2009-07-19 22:19:58 +02:00
|
|
|
self.filters = Filters.Filters(self.db, self.conf, self.sql, display = filters_display)
|
2009-04-17 06:44:43 +02:00
|
|
|
self.filters.registerButton1Name("Refresh Graph")
|
|
|
|
self.filters.registerButton1Callback(self.generateGraph)
|
|
|
|
self.filters.registerButton2Name("Export to File")
|
|
|
|
self.filters.registerButton2Callback(self.exportGraph)
|
2009-04-11 13:14:32 +02:00
|
|
|
|
|
|
|
self.mainHBox = gtk.HBox(False, 0)
|
|
|
|
self.mainHBox.show()
|
|
|
|
|
|
|
|
self.leftPanelBox = self.filters.get_vbox()
|
|
|
|
|
|
|
|
self.hpane = gtk.HPaned()
|
|
|
|
self.hpane.pack1(self.leftPanelBox)
|
2009-08-06 21:36:11 +02:00
|
|
|
self.mainHBox.add(self.hpane)
|
|
|
|
# hierarchy: self.mainHBox / self.hpane / self.graphBox / self.canvas / self.fig / self.ax
|
|
|
|
|
|
|
|
self.graphBox = gtk.VBox(False, 0)
|
|
|
|
self.graphBox.show()
|
2009-04-11 13:14:32 +02:00
|
|
|
self.hpane.pack2(self.graphBox)
|
2009-05-10 23:30:26 +02:00
|
|
|
self.hpane.show()
|
2009-04-11 13:14:32 +02:00
|
|
|
|
|
|
|
self.fig = None
|
2009-04-17 06:44:43 +02:00
|
|
|
#self.exportButton.set_sensitive(False)
|
2009-04-11 13:14:32 +02:00
|
|
|
self.canvas = None
|
|
|
|
|
2009-05-10 23:30:26 +02:00
|
|
|
|
2009-07-19 22:19:58 +02:00
|
|
|
self.db.rollback()
|
2009-04-11 13:14:32 +02:00
|
|
|
|
|
|
|
#################################
|
|
|
|
#
|
|
|
|
# self.db.cursor.execute("""select UNIX_TIMESTAMP(handStart) as time, id from Hands ORDER BY time""")
|
|
|
|
# THRESHOLD = 1800
|
|
|
|
# hands = self.db.cursor.fetchall()
|
|
|
|
#
|
|
|
|
# times = map(lambda x:long(x[0]), hands)
|
|
|
|
# handids = map(lambda x:int(x[1]), hands)
|
|
|
|
# print "DEBUG: len(times) %s" %(len(times))
|
|
|
|
# diffs = diff(times)
|
|
|
|
# print "DEBUG: len(diffs) %s" %(len(diffs))
|
|
|
|
# index = nonzero(diff(times) > THRESHOLD)
|
|
|
|
# print "DEBUG: len(index[0]) %s" %(len(index[0]))
|
|
|
|
# print "DEBUG: index %s" %(index)
|
|
|
|
# print "DEBUG: index[0][0] %s" %(index[0][0])
|
|
|
|
#
|
|
|
|
# total = 0
|
|
|
|
#
|
|
|
|
# last_idx = 0
|
|
|
|
# for i in range(len(index[0])):
|
|
|
|
# print "Hands in session %4s: %4s Start: %s End: %s Total: %s" %(i, index[0][i] - last_idx, strftime("%d/%m/%Y %H:%M", localtime(times[last_idx])), strftime("%d/%m/%Y %H:%M", localtime(times[index[0][i]])), times[index[0][i]] - times[last_idx])
|
|
|
|
# total = total + (index[0][i] - last_idx)
|
|
|
|
# last_idx = index[0][i] + 1
|
|
|
|
#
|
|
|
|
# print "Total: ", total
|
|
|
|
#################################
|
|
|
|
|
|
|
|
|
2008-12-04 08:15:38 +01:00
|
|
|
def get_vbox(self):
|
|
|
|
"""returns the vbox of this thread"""
|
|
|
|
return self.mainHBox
|
|
|
|
#end def get_vbox
|
|
|
|
|
2009-03-07 10:23:30 +01:00
|
|
|
def clearGraphData(self):
|
|
|
|
|
2009-08-06 21:36:11 +02:00
|
|
|
try:
|
|
|
|
try:
|
|
|
|
if self.canvas:
|
|
|
|
self.graphBox.remove(self.canvas)
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
|
|
|
|
if self.fig != None:
|
|
|
|
self.fig.clear()
|
|
|
|
self.fig = Figure(figsize=(5,4), dpi=100)
|
|
|
|
if self.canvas is not None:
|
|
|
|
self.canvas.destroy()
|
|
|
|
|
|
|
|
self.canvas = FigureCanvas(self.fig) # a gtk.DrawingArea
|
|
|
|
except:
|
|
|
|
err = traceback.extract_tb(sys.exc_info()[2])[-1]
|
|
|
|
print "***Error: "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1])
|
|
|
|
raise
|
2009-03-07 10:23:30 +01:00
|
|
|
|
2008-12-04 08:15:38 +01:00
|
|
|
def generateGraph(self, widget, data):
|
2009-08-06 21:36:11 +02:00
|
|
|
print "generateGraph: start"
|
|
|
|
try:
|
|
|
|
self.clearGraphData()
|
|
|
|
print "after cleardata"
|
|
|
|
|
|
|
|
sitenos = []
|
|
|
|
playerids = []
|
|
|
|
|
|
|
|
sites = self.filters.getSites()
|
|
|
|
heroes = self.filters.getHeroes()
|
|
|
|
siteids = self.filters.getSiteIds()
|
|
|
|
limits = self.filters.getLimits()
|
|
|
|
print "got filter data"
|
|
|
|
# Which sites are selected?
|
|
|
|
for site in sites:
|
|
|
|
if sites[site] == True:
|
|
|
|
sitenos.append(siteids[site])
|
|
|
|
self.db.cursor.execute(self.sql.query['getPlayerId'], (heroes[site],))
|
|
|
|
result = self.db.cursor.fetchall()
|
|
|
|
if len(result) == 1:
|
|
|
|
playerids.append(result[0][0])
|
|
|
|
|
|
|
|
if not sitenos:
|
|
|
|
#Should probably pop up here.
|
|
|
|
print "No sites selected - defaulting to PokerStars"
|
|
|
|
return
|
|
|
|
|
|
|
|
if not playerids:
|
|
|
|
print "No player ids found"
|
|
|
|
return
|
|
|
|
|
|
|
|
if not limits:
|
|
|
|
print "No limits found"
|
|
|
|
return
|
|
|
|
|
|
|
|
#Set graph properties
|
|
|
|
print "add_subplot"
|
|
|
|
self.ax = self.fig.add_subplot(111)
|
|
|
|
|
|
|
|
#Get graph data from DB
|
|
|
|
starttime = time()
|
|
|
|
print "get line: playerids =", playerids, "sitenos =", sitenos, "limits =", limits
|
|
|
|
line = self.getRingProfitGraph(playerids, sitenos, limits)
|
|
|
|
print "Graph generated in: %s" %(time() - starttime)
|
|
|
|
|
|
|
|
self.ax.set_title("Profit graph for ring games")
|
|
|
|
|
|
|
|
#Set axis labels and grid overlay properites
|
|
|
|
self.ax.set_xlabel("Hands", fontsize = 12)
|
|
|
|
self.ax.set_ylabel("$", fontsize = 12)
|
|
|
|
self.ax.grid(color='g', linestyle=':', linewidth=0.2)
|
|
|
|
if line == None or line == []:
|
|
|
|
|
|
|
|
#TODO: Do something useful like alert user
|
|
|
|
print "No hands returned by graph query"
|
|
|
|
else:
|
|
|
|
# text = "All Hands, " + sitename + str(name) + "\nProfit: $" + str(line[-1]) + "\nTotal Hands: " + str(len(line))
|
|
|
|
text = "All Hands, " + "\nProfit: $" + str(line[-1]) + "\nTotal Hands: " + str(len(line))
|
|
|
|
|
|
|
|
self.ax.annotate(text,
|
|
|
|
xy=(10, -10),
|
|
|
|
xycoords='axes points',
|
|
|
|
horizontalalignment='left', verticalalignment='top',
|
|
|
|
fontsize=10)
|
|
|
|
|
|
|
|
#Draw plot
|
|
|
|
self.ax.plot(line,)
|
|
|
|
|
|
|
|
self.graphBox.add(self.canvas)
|
|
|
|
self.canvas.show()
|
|
|
|
self.canvas.draw()
|
|
|
|
#self.exportButton.set_sensitive(True)
|
|
|
|
except:
|
|
|
|
err = traceback.extract_tb(sys.exc_info()[2])[-1]
|
|
|
|
print "***Error: "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1])
|
|
|
|
|
2008-12-04 08:15:38 +01:00
|
|
|
#end of def showClicked
|
|
|
|
|
2009-04-14 15:31:29 +02:00
|
|
|
def getRingProfitGraph(self, names, sites, limits):
|
2008-12-19 08:52:32 +01:00
|
|
|
tmp = self.sql.query['getRingProfitAllHandsPlayerIdSite']
|
|
|
|
# print "DEBUG: getRingProfitGraph"
|
2009-04-11 13:14:32 +02:00
|
|
|
start_date, end_date = self.filters.getDates()
|
2008-12-19 08:52:32 +01:00
|
|
|
|
2009-07-19 22:19:58 +02:00
|
|
|
#Buggered if I can find a way to do this 'nicely' take a list of integers and longs
|
2008-12-19 08:52:32 +01:00
|
|
|
# and turn it into a tuple readale by sql.
|
|
|
|
# [5L] into (5) not (5,) and [5L, 2829L] into (5, 2829)
|
|
|
|
nametest = str(tuple(names))
|
|
|
|
sitetest = str(tuple(sites))
|
2009-04-14 15:31:29 +02:00
|
|
|
limittest = str(tuple(limits))
|
2008-12-19 08:52:32 +01:00
|
|
|
nametest = nametest.replace("L", "")
|
|
|
|
nametest = nametest.replace(",)",")")
|
|
|
|
sitetest = sitetest.replace(",)",")")
|
2009-04-14 15:31:29 +02:00
|
|
|
limittest = limittest.replace("L", "")
|
|
|
|
limittest = limittest.replace(",)",")")
|
2008-12-19 08:52:32 +01:00
|
|
|
|
|
|
|
#Must be a nicer way to deal with tuples of size 1 ie. (2,) - which makes sql barf
|
|
|
|
tmp = tmp.replace("<player_test>", nametest)
|
|
|
|
tmp = tmp.replace("<site_test>", sitetest)
|
2008-12-19 09:21:58 +01:00
|
|
|
tmp = tmp.replace("<startdate_test>", start_date)
|
|
|
|
tmp = tmp.replace("<enddate_test>", end_date)
|
2009-04-14 15:31:29 +02:00
|
|
|
tmp = tmp.replace("<limit_test>", limittest)
|
2008-12-19 08:52:32 +01:00
|
|
|
|
2009-04-14 15:31:29 +02:00
|
|
|
#print "DEBUG: sql query:"
|
|
|
|
#print tmp
|
2009-06-23 00:30:54 +02:00
|
|
|
self.db.cursor.execute(tmp)
|
2008-12-04 08:15:38 +01:00
|
|
|
#returns (HandId,Winnings,Costs,Profit)
|
|
|
|
winnings = self.db.cursor.fetchall()
|
2009-07-19 22:19:58 +02:00
|
|
|
self.db.rollback()
|
2008-12-04 08:15:38 +01:00
|
|
|
|
2008-12-11 15:55:03 +01:00
|
|
|
if(winnings == ()):
|
|
|
|
return None
|
|
|
|
|
2008-12-04 08:15:38 +01:00
|
|
|
y=map(lambda x:float(x[3]), winnings)
|
|
|
|
line = cumsum(y)
|
|
|
|
return line/100
|
2008-10-08 13:53:25 +02:00
|
|
|
#end of def getRingProfitGraph
|
|
|
|
|
2008-12-06 12:51:36 +01:00
|
|
|
def exportGraph (self, widget, data):
|
2009-03-05 13:31:56 +01:00
|
|
|
if self.fig is None:
|
|
|
|
return # Might want to disable export button until something has been generated.
|
2008-12-06 12:51:36 +01:00
|
|
|
dia_chooser = gtk.FileChooserDialog(title="Please choose the directory you wish to export to:",
|
|
|
|
action=gtk.FILE_CHOOSER_ACTION_OPEN,
|
|
|
|
buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
|
2009-01-28 04:51:39 +01:00
|
|
|
#TODO: Suggest path and filename to start with
|
2008-12-06 12:51:36 +01:00
|
|
|
|
|
|
|
response = dia_chooser.run()
|
|
|
|
if response == gtk.RESPONSE_OK:
|
|
|
|
self.exportDir = dia_chooser.get_filename()
|
2009-01-28 04:51:39 +01:00
|
|
|
print "DEBUG: self.exportDir = %s" %(self.exportDir)
|
2008-12-06 12:51:36 +01:00
|
|
|
elif response == gtk.RESPONSE_CANCEL:
|
|
|
|
print 'Closed, no graph exported'
|
|
|
|
dia_chooser.destroy()
|
2009-01-28 04:51:39 +01:00
|
|
|
#TODO: Check to see if file exists
|
|
|
|
#NOTE: Dangerous - will happily overwrite any file we have write access too
|
2009-03-09 23:28:35 +01:00
|
|
|
#TODO: This asks for a directory but will take a filename and overwrite it.
|
2009-01-28 04:51:39 +01:00
|
|
|
self.fig.savefig(self.exportDir, format="png")
|
2008-12-06 12:51:36 +01:00
|
|
|
|
2009-01-21 17:40:05 +01:00
|
|
|
|