diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index bb9739ec..1533c05c 100755 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -427,7 +427,8 @@ class Database: print "***Error: "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1]) def get_stats_from_hand( self, hand, type # type is "ring" or "tour" - , hud_params = {'aggregate_tour':False, 'aggregate_ring':False, 'hud_style':'A', 'agg_bb_mult':100} + , hud_params = {'aggregate_tour':False, 'aggregate_ring':False, 'hud_style':'A', 'hud_days':30, 'agg_bb_mult':100 + ,'h_aggregate_tour':False, 'h_aggregate_ring':False, 'h_hud_style':'S', 'h_hud_days':30, 'h_agg_bb_mult':100} , hero_id = -1 ): aggregate = hud_params['aggregate_tour'] if type == "tour" else hud_params['aggregate_ring'] diff --git a/pyfpdb/GuiGraphViewer.py b/pyfpdb/GuiGraphViewer.py index d148d841..9387bdb7 100644 --- a/pyfpdb/GuiGraphViewer.py +++ b/pyfpdb/GuiGraphViewer.py @@ -98,34 +98,6 @@ class GuiGraphViewer (threading.Thread): self.db.rollback() -################################# -# -# 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 -################################# - - def get_vbox(self): """returns the vbox of this thread""" return self.mainHBox diff --git a/pyfpdb/GuiPlayerStats.py b/pyfpdb/GuiPlayerStats.py index dd3db0be..1faa40c5 100644 --- a/pyfpdb/GuiPlayerStats.py +++ b/pyfpdb/GuiPlayerStats.py @@ -15,6 +15,7 @@ #In the "official" distribution you can find the license in #agpl-3.0.txt in the docs folder of the package. +import traceback import threading import pygtk pygtk.require('2.0') @@ -29,7 +30,11 @@ import Database import fpdb_db import Filters +colalias,colshow,colheading,colxalign,colformat,coltype = 0,1,2,3,4,5 +ranks = {'x':0, '2':2, '3':3, '4':4, '5':5, '6':6, '7':7, '8':8, '9':9, 'T':10, 'J':11, 'Q':12, 'K':13, 'A':14} + class GuiPlayerStats (threading.Thread): + def __init__(self, config, querylist, mainwin, debug=True): self.debug = debug self.conf = config @@ -210,8 +215,8 @@ class GuiPlayerStats (threading.Thread): # Display summary table at top of page # 3rd parameter passes extra flags, currently includes: - # holecards - whether to display card breakdown (True/False) - # numhands - min number hands required when displaying all players + # holecards - whether to display card breakdown (True/False) + # numhands - min number hands required when displaying all players flags = [False, self.filters.getNumHands()] self.addTable(swin, 'playerDetailedStats', flags, playerids, sitenos, limits, type, seats, groups, dates) @@ -248,26 +253,65 @@ class GuiPlayerStats (threading.Thread): cell.set_property('text', str) cell.set_property('foreground', 'red') else: - cell.set_property('foreground', 'green') + cell.set_property('foreground', 'darkgreen') return + def sortnums(self, model, iter1, iter2, n): + try: + ret = 0 + a = self.liststore.get_value(iter1, n) + b = self.liststore.get_value(iter2, n) + if 'f' in self.cols_to_show[n][4]: + try: a = float(a) + except: a = 0.0 + try: b = float(b) + except: b = 0.0 + if n == 0: + a1,a2,a3 = ranks[a[0]], ranks[a[1]], (a+'o')[2] + b1,b2,b3 = ranks[b[0]], ranks[b[1]], (b+'o')[2] + if a1 > b1 or ( a1 == b1 and (a2 > b2 or (a2 == b2 and a3 > b3) ) ): + ret = 1 + else: + ret = -1 + else: + if a < b: + ret = -1 + elif a == b: + ret = 0 + else: + ret = 1 + #print "n =", n, "iter1[n] =", self.liststore.get_value(iter1,n), "iter2[n] =", self.liststore.get_value(iter2,n), "ret =", ret + except: + err = traceback.extract_tb(sys.exc_info()[2]) + print "***sortnums error: " + str(sys.exc_info()[1]) + print "\n".join( [e[0]+':'+str(e[1])+" "+e[2] for e in err] ) + + return(ret) + def sortcols(self, col, n): - #This doesn't actually work yet - if n == 0: - # Card values can stay the same for the moment. - return - if col.get_sort_order() == gtk.SORT_ASCENDING: - col.set_sort_order(gtk.SORT_DESCENDING) - else: - col.set_sort_order(gtk.SORT_ASCENDING) - self.liststore.set_sort_column_id(n, col.get_sort_order()) + try: + #This doesn't actually work yet - clicking heading in top section sorts bottom section :-( + if col.get_sort_order() == gtk.SORT_ASCENDING: + col.set_sort_order(gtk.SORT_DESCENDING) + else: + col.set_sort_order(gtk.SORT_ASCENDING) + self.liststore.set_sort_column_id(n, col.get_sort_order()) + self.liststore.set_sort_func(n, self.sortnums, n) + for i in xrange(len(self.listcols)): + self.listcols[i].set_sort_indicator(False) + self.listcols[n].set_sort_indicator(True) + # use this listcols[col].set_sort_indicator(True) + # to turn indicator off for other cols + except: + err = traceback.extract_tb(sys.exc_info()[2]) + print "***sortcols error: " + str(sys.exc_info()[1]) + print "\n".join( [e[0]+':'+str(e[1])+" "+e[2] for e in err] ) def addTable(self, vbox, query, flags, playerids, sitenos, limits, type, seats, groups, dates): counter = 0 row = 0 sqlrow = 0 - colalias,colshow,colheading,colxalign,colformat,coltype = 0,1,2,3,4,5 if not flags: holecards = False else: holecards = flags[0] @@ -278,10 +322,10 @@ class GuiPlayerStats (threading.Thread): colnames = [desc[0].lower() for desc in self.cursor.description] # pre-fetch some constant values: - cols_to_show = [x for x in self.columns if x[colshow]] + self.cols_to_show = [x for x in self.columns if x[colshow]] hgametypeid_idx = colnames.index('hgametypeid') - self.liststore = gtk.ListStore(*([str] * len(cols_to_show))) + self.liststore = gtk.ListStore(*([str] * len(self.cols_to_show))) view = gtk.TreeView(model=self.liststore) view.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_BOTH) #vbox.pack_start(view, expand=False, padding=3) @@ -291,44 +335,45 @@ class GuiPlayerStats (threading.Thread): textcell50.set_property('xalign', 0.5) numcell = gtk.CellRendererText() numcell.set_property('xalign', 1.0) - listcols = [] - idx = 0 + self.listcols = [] # Create header row eg column: ("game", True, "Game", 0.0, "%s") - for col, column in enumerate(cols_to_show): + for col, column in enumerate(self.cols_to_show): if column[colalias] == 'game' and holecards: s = [x for x in self.columns if x[colalias] == 'hand'][0][colheading] else: s = column[colheading] - listcols.append(gtk.TreeViewColumn(s)) - view.append_column(listcols[col]) - #listcols[col].set_clickable(True) - #listcols[col].set_sort_indicator(True) - #listcols[col].connect("clicked", self.sortcols, idx) + self.listcols.append(gtk.TreeViewColumn(s)) + view.append_column(self.listcols[col]) if column[colformat] == '%s': if column[colxalign] == 0.0: - listcols[col].pack_start(textcell, expand=True) - listcols[col].add_attribute(textcell, 'text', col) + self.listcols[col].pack_start(textcell, expand=True) + self.listcols[col].add_attribute(textcell, 'text', col) else: - listcols[col].pack_start(textcell50, expand=True) - listcols[col].add_attribute(textcell50, 'text', col) - listcols[col].set_expand(True) + self.listcols[col].pack_start(textcell50, expand=True) + self.listcols[col].add_attribute(textcell50, 'text', col) + self.listcols[col].set_expand(True) else: - listcols[col].pack_start(numcell, expand=True) - listcols[col].add_attribute(numcell, 'text', col) - listcols[col].set_expand(True) - #listcols[col].set_alignment(column[colxalign]) # no effect? + self.listcols[col].pack_start(numcell, expand=True) + self.listcols[col].add_attribute(numcell, 'text', col) + self.listcols[col].set_expand(True) + #self.listcols[col].set_alignment(column[colxalign]) # no effect? + if holecards: + self.listcols[col].set_clickable(True) + self.listcols[col].connect("clicked", self.sortcols, col) + if col == 0: + self.listcols[col].set_sort_order(gtk.SORT_DESCENDING) + self.listcols[col].set_sort_indicator(True) if column[coltype] == 'cash': - listcols[col].set_cell_data_func(numcell, self.ledger_style_render_func) + self.listcols[col].set_cell_data_func(numcell, self.ledger_style_render_func) else: - listcols[col].set_cell_data_func(numcell, self.reset_style_render_func) - idx = idx+1 + self.listcols[col].set_cell_data_func(numcell, self.reset_style_render_func) rows = len(result) # +1 for title row while sqlrow < rows: treerow = [] - for col,column in enumerate(cols_to_show): + for col,column in enumerate(self.cols_to_show): if column[colalias] in colnames: value = result[sqlrow][colnames.index(column[colalias])] if column[colalias] == 'plposition': diff --git a/pyfpdb/GuiSessionViewer.py b/pyfpdb/GuiSessionViewer.py index 76ad682b..2013f8a2 100755 --- a/pyfpdb/GuiSessionViewer.py +++ b/pyfpdb/GuiSessionViewer.py @@ -21,10 +21,17 @@ import pygtk pygtk.require('2.0') import gtk import os +import traceback from time import time, strftime, localtime try: + 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 matplotlib.finance import candlestick2 + from numpy import diff, nonzero, sum, cumsum, max, mina - import matplotlib.finance # from matplotlib.dates import DateFormatter, WeekdayLocator, HourLocator, \ # DayLocator, MONDAY, timezone @@ -49,6 +56,11 @@ class GuiSessionViewer (threading.Thread): self.MYSQL_INNODB = 2 self.PGSQL = 3 self.SQLITE = 4 + + self.fig = None + self.canvas = None + self.ax = None + self.graphBox = None # create new db connection to avoid conflicts with other threads self.db = Database.Database(self.conf, sql=self.sql) @@ -67,17 +79,17 @@ class GuiSessionViewer (threading.Thread): filters_display = { "Heroes" : True, "Sites" : True, "Games" : False, - "Limits" : True, - "LimitSep" : True, - "LimitType" : True, + "Limits" : False, + "LimitSep" : False, + "LimitType" : False, "Type" : True, - "Seats" : True, - "SeatSep" : True, + "Seats" : False, + "SeatSep" : False, "Dates" : True, - "Groups" : True, - "GroupsAll" : True, + "Groups" : False, + "GroupsAll" : False, "Button1" : True, - "Button2" : True + "Button2" : False } self.filters = Filters.Filters(self.db, self.conf, self.sql, display = filters_display) @@ -182,17 +194,20 @@ class GuiSessionViewer (threading.Thread): print "No limits found" return - self.createStatsTable(vbox, playerids, sitenos, limits, seats) + self.createStatsPane(vbox, playerids, sitenos, limits, seats) - def createStatsTable(self, vbox, playerids, sitenos, limits, seats): + def createStatsPane(self, vbox, playerids, sitenos, limits, seats): starttime = time() - # Display summary table at top of page - # 3rd parameter passes extra flags, currently includes: - # holecards - whether to display card breakdown (True/False) - flags = [False] - self.addTable(vbox, 'playerDetailedStats', flags, playerids, sitenos, limits, seats) + (results, opens, closes, highs, lows) = self.generateDatasets(playerids, sitenos, limits, seats) + + + self.graphBox = gtk.VBox(False, 0) + self.graphBox.show() + self.generateGraph(opens, closes, highs, lows) + + vbox.pack_start(self.graphBox) # Separator sep = gtk.HSeparator() vbox.pack_start(sep, expand=False, padding=3) @@ -212,92 +227,24 @@ class GuiSessionViewer (threading.Thread): vbox1.show() swin.add_with_viewport(vbox1) - # Detailed table - flags = [True] - self.addTable(vbox1, 'playerDetailedStats', flags, playerids, sitenos, limits, seats) + self.addTable(vbox1, results) self.db.rollback() print "Stats page displayed in %4.2f seconds" % (time() - starttime) #end def fillStatsFrame(self, vbox): - def generateGraph(self, vbox, data): - fig = figure() - fig.subplots_adjust(bottom=0.2) - ax = fig.add_subplot(111) - ax.xaxis.set_major_locator(mondays) - ax.xaxis.set_minor_locator(alldays) - ax.xaxis.set_major_formatter(weekFormatter) - #ax.xaxis.set_minor_formatter(dayFormatter) - #plot_day_summary(ax, quotes, ticksize=3) -# candlestick(ax, quotes, width=0.6) -# candlestick2(ax, opens, closes, highs, lows, width=4, colorup='k', colordown='r', alpha=0.75) -# Represent the open, close as a bar line and high low range as a vertical line. -# ax : an Axes instance to plot to -# width : the bar width in points -# colorup : the color of the lines where close >= open -# colordown : the color of the lines where close < open -# alpha : bar transparency -# return value is lineCollection, barCollection - ax.xaxis_date() - ax.autoscale_view() - setp( gca().get_xticklabels(), rotation=45, horizontalalignment='right') - - show() - - - def addTable(self, vbox, query, flags, playerids, sitenos, limits, seats): - row = 0 - sqlrow = 0 - colalias,colshow,colheading,colxalign,colformat = 0,1,2,3,4 - if not flags: holecards = False - else: holecards = flags[0] - - # pre-fetch some constant values: - cols_to_show = [x for x in self.columns if x[colshow]] - - self.liststore = gtk.ListStore(*([str] * len(cols_to_show))) - - view = gtk.TreeView(model=self.liststore) - view.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_BOTH) - vbox.add(view) - textcell = gtk.CellRendererText() - textcell50 = gtk.CellRendererText() - textcell50.set_property('xalign', 0.5) - numcell = gtk.CellRendererText() - numcell.set_property('xalign', 1.0) - listcols = [] - - # Create header row eg column: ("game", True, "Game", 0.0, "%s") - for col, column in enumerate(cols_to_show): - s = column[colheading] - listcols.append(gtk.TreeViewColumn(s)) - view.append_column(listcols[col]) - if column[colformat] == '%s': - if column[colxalign] == 0.0: - listcols[col].pack_start(textcell, expand=True) - listcols[col].add_attribute(textcell, 'text', col) - else: - listcols[col].pack_start(textcell50, expand=True) - listcols[col].add_attribute(textcell50, 'text', col) - listcols[col].set_expand(True) - else: - listcols[col].pack_start(numcell, expand=True) - listcols[col].add_attribute(numcell, 'text', col) - listcols[col].set_expand(True) - + def generateDatasets(self, playerids, sitenos, limits, seats): # Get a list of all handids and their timestampts - # FIXME: Will probably want to be able to filter this list eventually - # FIXME: Join on handsplayers for Hero to get other useful stuff like total profit? - q = """ -select UNIX_TIMESTAMP(h.handStart) as time, hp.handId, hp.startCash, hp.winnings, hp.totalProfit -from HandsPlayers hp - inner join Hands h on (h.id = hp.handId) - inner join Gametypes gt on (gt.Id = h.gameTypeId) - inner join Sites s on (s.Id = gt.siteId) - inner join Players p on (p.Id = hp.playerId) -where hp.playerId in (2) -order by time -""" + #FIXME: Query still need to filter on blind levels + + q = self.sql.query['sessionStats'] + start_date, end_date = self.filters.getDates() + q = q.replace("", " between '" + start_date + "' and '" + end_date + "'") + + nametest = str(tuple(playerids)) + nametest = nametest.replace("L", "") + nametest = nametest.replace(",)",")") + q = q.replace("", nametest) self.db.cursor.execute(q) THRESHOLD = 1800 hands = self.db.cursor.fetchall() @@ -329,30 +276,121 @@ order by time for i in range(len(index[0])): sid = i # Session id hds = index[0][i] - last_idx # Number of hands in session - stime = strftime("%d/%m/%Y %H:%M", localtime(times[last_idx])) # Formatted start time - etime = strftime("%d/%m/%Y %H:%M", localtime(times[index[0][i]])) # Formatted end time - hph = (times[index[0][i]] - times[last_idx])/60 # Hands per hour - won = sum(winnings[last_idx:index[0][i]])/100.0 - hwm = max(cum_sum[last_idx:index[0][i]]) - lwm = min(cum_sum[last_idx:index[0][i]]) - print "DEBUG: range: (%s, %s) - (min, max): (%s, %s)" %(last_idx, index[0][i], hwm, lwm) + if hds > 0: + stime = strftime("%d/%m/%Y %H:%M", localtime(times[last_idx])) # Formatted start time + etime = strftime("%d/%m/%Y %H:%M", localtime(times[index[0][i]])) # Formatted end time + hph = (times[index[0][i]] - times[last_idx])/60 # Hands per hour + won = sum(winnings[last_idx:index[0][i]])/100.0 + hwm = max(cum_sum[last_idx:index[0][i]]) + lwm = min(cum_sum[last_idx:index[0][i]]) + #print "DEBUG: range: (%s, %s) - (min, max): (%s, %s)" %(last_idx, index[0][i], hwm, lwm) - results.append([sid, hds, stime, etime, hph, won]) - opens.append((sum(winnings[:last_idx]))/100) - closes.append((sum(winnings[:index[0][i]]))/100) - highs.append(hwm) - lows.append(lwm) - print "Hands in session %4s: %4s Start: %s End: %s HPH: %s Profit: %s" %(sid, hds, stime, etime, hph, won) - total = total + (index[0][i] - last_idx) - last_idx = index[0][i] + 1 + results.append([sid, hds, stime, etime, hph, won]) + opens.append((sum(winnings[:last_idx]))/100) + closes.append((sum(winnings[:index[0][i]]))/100) + highs.append(hwm) + lows.append(lwm) + #print "Hands in session %4s: %4s Start: %s End: %s HPH: %s Profit: %s" %(sid, hds, stime, etime, hph, won) + total = total + (index[0][i] - last_idx) + last_idx = index[0][i] + 1 + return (results, opens, closes, highs, lows) + + def clearGraphData(self): + + 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 + + + def generateGraph(self, opens, closes, highs, lows): + self.clearGraphData() + + #FIXME: Weird - first data entry is crashing this for me + opens = opens[1:] + closes = closes[1:] + highs = highs[1:] + lows = lows[1:] +# print "DEBUG:" +# print "highs = %s" % highs +# print "lows = %s" % lows +# print "opens = %s" % opens +# print "closes = %s" % closes +# print "len(highs): %s == len(lows): %s" %(len(highs), len(lows)) +# print "len(opens): %s == len(closes): %s" %(len(opens), len(closes)) +# +# for i in range(len(highs)): +# print "DEBUG: (%s, %s, %s, %s)" %(lows[i], opens[i], closes[i], highs[i]) +# print "DEBUG: diffs h/l: %s o/c: %s" %(lows[i] - highs[i], opens[i] - closes[i]) + + self.ax = self.fig.add_subplot(111) + + self.ax.set_title("Session candlestick graph") + + #Set axis labels and grid overlay properites + self.ax.set_xlabel("Sessions", fontsize = 12) + self.ax.set_ylabel("$", fontsize = 12) + self.ax.grid(color='g', linestyle=':', linewidth=0.2) + + candlestick2(self.ax, opens, closes, highs, lows, width=0.50, colordown='r', colorup='g', alpha=1.00) + self.graphBox.add(self.canvas) + self.canvas.show() + self.canvas.draw() + + def addTable(self, vbox, results): + row = 0 + sqlrow = 0 + colalias,colshow,colheading,colxalign,colformat = 0,1,2,3,4 + + # pre-fetch some constant values: + cols_to_show = [x for x in self.columns if x[colshow]] + + self.liststore = gtk.ListStore(*([str] * len(cols_to_show))) for row in results: iter = self.liststore.append(row) - print "DEBUG: highs = %s" % highs - print "DEBUG: lows = %s" % lows - print "DEBUG: opens = %s" % opens - print "DEBUG: closes = %s" % closes + view = gtk.TreeView(model=self.liststore) + view.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_BOTH) + vbox.add(view) + textcell = gtk.CellRendererText() + textcell50 = gtk.CellRendererText() + textcell50.set_property('xalign', 0.5) + numcell = gtk.CellRendererText() + numcell.set_property('xalign', 1.0) + listcols = [] + + # Create header row eg column: ("game", True, "Game", 0.0, "%s") + for col, column in enumerate(cols_to_show): + s = column[colheading] + listcols.append(gtk.TreeViewColumn(s)) + view.append_column(listcols[col]) + if column[colformat] == '%s': + if column[colxalign] == 0.0: + listcols[col].pack_start(textcell, expand=True) + listcols[col].add_attribute(textcell, 'text', col) + else: + listcols[col].pack_start(textcell50, expand=True) + listcols[col].add_attribute(textcell50, 'text', col) + listcols[col].set_expand(True) + else: + listcols[col].pack_start(numcell, expand=True) + listcols[col].add_attribute(numcell, 'text', col) + listcols[col].set_expand(True) vbox.show_all() diff --git a/pyfpdb/HUD_main.py b/pyfpdb/HUD_main.py index 77d8312d..021d1ff2 100755 --- a/pyfpdb/HUD_main.py +++ b/pyfpdb/HUD_main.py @@ -116,11 +116,7 @@ class HUD_main(object): # called by an event in the HUD, to kill this specific HUD if table in self.hud_dict: self.hud_dict[table].kill() - try: - # throws exception in windows sometimes (when closing using main_window menu?) - self.hud_dict[table].main_window.destroy() - except: - pass + self.hud_dict[table].main_window.destroy() self.vb.remove(self.hud_dict[table].tablehudlabel) del(self.hud_dict[table]) self.main_window.resize(1,1) @@ -131,7 +127,7 @@ class HUD_main(object): def idle_func(): gtk.gdk.threads_enter() - try: + try: # TODO: seriously need to decrease the scope of this block.. what are we expecting to error? newlabel = gtk.Label("%s - %s" % (table.site, table_name)) self.vb.add(newlabel) newlabel.show() @@ -174,12 +170,13 @@ class HUD_main(object): # function idle_func() to be run by the gui thread, at its leisure. def idle_func(): gtk.gdk.threads_enter() - try: - self.hud_dict[table_name].update(new_hand_id, config) - [aw.update_gui(new_hand_id) for aw in self.hud_dict[table_name].aux_windows] - return False - finally: - gtk.gdk.threads_leave() +# try: + self.hud_dict[table_name].update(new_hand_id, config) + [aw.update_gui(new_hand_id) for aw in self.hud_dict[table_name].aux_windows] +# finally: + gtk.gdk.threads_leave() + return False + gobject.idle_add(idle_func) def read_stdin(self): # This is the thread function diff --git a/pyfpdb/Mucked.py b/pyfpdb/Mucked.py index c4a6e4f1..6da35f6d 100755 --- a/pyfpdb/Mucked.py +++ b/pyfpdb/Mucked.py @@ -63,7 +63,12 @@ class Aux_Window(object): card_images = 53 * [0] suits = ('s', 'h', 'd', 'c') ranks = (14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2) - pb = gtk.gdk.pixbuf_new_from_file(self.config.execution_path(self.params['deck'])) + deckimg = self.params['deck'] + try: + pb = gtk.gdk.pixbuf_new_from_file(self.config.execution_path(deckimg)) + except: + stockpath = '/usr/share/python-fpdb/' + deckimg + pb = gtk.gdk.pixbuf_new_from_file(stockpath) for j in range(0, 13): for i in range(0, 4): diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index 251a56fa..3c25963f 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -2475,6 +2475,33 @@ class Sql: GROUP BY h.handStart, hp.handId, hp.totalProfit ORDER BY h.handStart""" + #################################### + # Session stats query + #################################### + if db_server == 'mysql': + self.query['sessionStats'] = """ + SELECT UNIX_TIMESTAMP(h.handStart) as time, hp.handId, hp.startCash, hp.winnings, hp.totalProfit + FROM HandsPlayers hp + INNER JOIN Hands h on (h.id = hp.handId) + INNER JOIN Gametypes gt on (gt.Id = h.gameTypeId) + INNER JOIN Sites s on (s.Id = gt.siteId) + INNER JOIN Players p on (p.Id = hp.playerId) + WHERE hp.playerId in + AND date_format(h.handStart, '%Y-%m-%d') + ORDER by time""" + elif db_server == 'postgresql': + self.query['sessionStats'] = """ + SELECT EXTRACT(epoch from h.handStart) as time, hp.handId, hp.startCash, hp.winnings, hp.totalProfit + FROM HandsPlayers hp + INNER JOIN Hands h on (h.id = hp.handId) + INNER JOIN Gametypes gt on (gt.Id = h.gameTypeId) + INNER JOIN Sites s on (s.Id = gt.siteId) + INNER JOIN Players p on (p.Id = hp.playerId) + WHERE hp.playerId in + AND h.handStart + ORDER by time""" + elif db_server == 'sqlite': + self.query['sessionStats'] = """ """ #################################### # Queries to rebuild/modify hudcache diff --git a/pyfpdb/Stats.py b/pyfpdb/Stats.py index 614f48ed..d54ccf12 100755 --- a/pyfpdb/Stats.py +++ b/pyfpdb/Stats.py @@ -93,7 +93,7 @@ def do_stat(stat_dict, player = 24, stat = 'vpip'): # functions that return individual stats def totalprofit(stat_dict, player): - """ Total Profit.""" + """ Total Profit.""" if stat_dict[player]['net'] != 0: stat = float(stat_dict[player]['net']) / 100 return (stat, '$%.2f' % stat, 'tp=$%.2f' % stat, 'totalprofit=$%.2f' % stat, str(stat), 'Total Profit') @@ -657,17 +657,15 @@ def ffreq4(stat_dict, player): if __name__== "__main__": c = Configuration.Config() - db_connection = Database.Database(c, 'fpdb', 'holdem') + db_connection = Database.Database(c) h = db_connection.get_last_hand() - stat_dict = db_connection.get_stats_from_hand(h) + stat_dict = db_connection.get_stats_from_hand(h, "ring") for player in stat_dict.keys(): print "player = ", player, do_stat(stat_dict, player = player, stat = 'vpip') - print "player = ", player, do_stat(stat_dict, player = player, stat = 'vpip_0') print "player = ", player, do_stat(stat_dict, player = player, stat = 'pfr') - print "player = ", player, do_stat(stat_dict, player = player, stat = 'pfr_0') print "player = ", player, do_stat(stat_dict, player = player, stat = 'wtsd') - print "player = ", player, do_stat(stat_dict, player = player, stat = 'profit100_0') + print "player = ", player, do_stat(stat_dict, player = player, stat = 'profit100') print "player = ", player, do_stat(stat_dict, player = player, stat = 'saw_f') print "player = ", player, do_stat(stat_dict, player = player, stat = 'n') print "player = ", player, do_stat(stat_dict, player = player, stat = 'fold_f') @@ -675,14 +673,13 @@ if __name__== "__main__": print "player = ", player, do_stat(stat_dict, player = player, stat = 'steal') print "player = ", player, do_stat(stat_dict, player = player, stat = 'f_SB_steal') print "player = ", player, do_stat(stat_dict, player = player, stat = 'f_BB_steal') - print "player = ", player, do_stat(stat_dict, player = player, stat = 'three_B_0') + print "player = ", player, do_stat(stat_dict, player = player, stat = 'three_B') print "player = ", player, do_stat(stat_dict, player = player, stat = 'WMsF') print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq1') print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq2') print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq3') print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq4') print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_123') - print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_123_0') print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb1') print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb2') print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb3') @@ -694,13 +691,15 @@ if __name__== "__main__": print "\n" print "\n\nLegal stats:" + print "(add _0 to name to display with 0 decimal places, _1 to display with 1, etc)\n" for attr in dir(): if attr.startswith('__'): continue if attr in ("Configuration", "Database", "GInitiallyUnowned", "gtk", "pygtk", "player", "c", "db_connection", "do_stat", "do_tip", "stat_dict", - "h"): continue + "h", "re", "re_Percent", "re_Places"): continue print "%-14s %s" % (attr, eval("%s.__doc__" % (attr))) # print " " % (attr) + print db_connection.close_connection diff --git a/pyfpdb/fpdb.py b/pyfpdb/fpdb.py index 4f3210f4..fe2ddd39 100755 --- a/pyfpdb/fpdb.py +++ b/pyfpdb/fpdb.py @@ -41,6 +41,10 @@ if os.name == 'nt' and sys.version[0:3] not in ('2.5', '2.6') and '-r' not in sy else: pass #print "debug - not changing path" + +if os.name == 'nt': + import win32api + import win32con print "Python " + sys.version[0:3] + '...\n' @@ -78,7 +82,7 @@ import FpdbSQLQueries import Configuration from Exceptions import * -VERSION = "0.11" +VERSION = "0.12" class fpdb: def tab_clicked(self, widget, tab_name): @@ -618,7 +622,57 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt") self.window.show() self.load_profile() + + self.statusIcon = gtk.StatusIcon() + self.statusIcon.set_from_stock(gtk.STOCK_HOME) + self.statusIcon.set_tooltip("Free Poker Database") + self.statusIcon.connect('activate', self.statusicon_activate) + self.statusMenu = gtk.Menu() + menuItem = gtk.ImageMenuItem(gtk.STOCK_ABOUT) + menuItem.connect('activate', self.dia_about) + self.statusMenu.append(menuItem) + menuItem = gtk.ImageMenuItem(gtk.STOCK_QUIT) + menuItem.connect('activate', self.quit) + self.statusMenu.append(menuItem) + self.statusIcon.connect('popup-menu', self.statusicon_menu, self.statusMenu) + self.statusIcon.set_visible(True) + + self.window.connect('window-state-event', self.window_state_event_cb) sys.stderr.write("fpdb starting ...") + + def window_state_event_cb(self, window, event): + print "window_state_event", event + if event.changed_mask & gtk.gdk.WINDOW_STATE_ICONIFIED: + # -20 = GWL_EXSTYLE can't find it in the pywin32 libs + #bits = win32api.GetWindowLong(self.window.window.handle, -20) + #bits = bits ^ (win32con.WS_EX_TOOLWINDOW | win32con.WS_EX_APPWINDOW) + + #win32api.SetWindowLong(self.window.window.handle, -20, bits) + + if event.new_window_state & gtk.gdk.WINDOW_STATE_ICONIFIED: + self.window.hide() + self.window.set_skip_taskbar_hint(True) + self.window.set_skip_pager_hint(True) + else: + self.window.set_skip_taskbar_hint(False) + self.window.set_skip_pager_hint(False) + # Tell GTK not to propagate this signal any further + return True + + def statusicon_menu(self, widget, button, time, data = None): + # we don't need to pass data here, since we do keep track of most all + # our variables .. the example code that i looked at for this + # didn't use any long scope variables.. which might be an alright + # idea too sometime + if button == 3: + if data: + data.show_all() + data.popup(None, None, None, 3, time) + pass + + def statusicon_activate(self, widget, data = None): + self.window.show() + self.window.present() def warning_box(self, str, diatitle="FPDB WARNING"): diaWarning = gtk.Dialog(title=diatitle, parent=None, flags=0, buttons=(gtk.STOCK_OK,gtk.RESPONSE_OK)) diff --git a/setup.py b/setup.py index 737ad18e..0ba21bf0 100644 --- a/setup.py +++ b/setup.py @@ -19,6 +19,8 @@ setup(name = 'fpdb', ('/usr/share/applications', ['files/fpdb.desktop']), ('/usr/share/python-fpdb', - ['pyfpdb/logging.conf']) + ['pyfpdb/logging.conf', 'pyfpdb/Cards01.png', + 'pyfpdb/HUD_config.xml.example' + ]) ] )