From 2d6412c8a797ffaa8bc85a998cb277b611e969d1 Mon Sep 17 00:00:00 2001 From: Matt Turnbull Date: Tue, 9 Dec 2008 15:32:37 +0000 Subject: [PATCH 01/14] pseudo stars! --- pyfpdb/EverleafToFpdb.py | 7 ++ pyfpdb/HandHistoryConverter.py | 114 +++++++++++---------------------- 2 files changed, 46 insertions(+), 75 deletions(-) diff --git a/pyfpdb/EverleafToFpdb.py b/pyfpdb/EverleafToFpdb.py index 1a1196c5..a5aa5514 100755 --- a/pyfpdb/EverleafToFpdb.py +++ b/pyfpdb/EverleafToFpdb.py @@ -79,6 +79,7 @@ class Everleaf(HandHistoryConverter): self.rexx.setHeroCardsRegex('.*\nDealt\sto\s(?P.*)\s\[ (?P\S\S), (?P\S\S) \]') self.rexx.setActionStepRegex('.*\n(?P.*) (?Pbets|checks|raises|calls|folds)(\s\[\$ (?P[.\d]+) USD\])?') self.rexx.setShowdownActionRegex('.*\n(?P.*) shows \[ (?P.*) \]') + self.rexx.setCollectPotRegex('.*\n(?P.*) wins \$ (?P[.\d]+) USD.*') self.rexx.compileRegexes() def readSupportedGames(self): @@ -184,6 +185,12 @@ class Everleaf(HandHistoryConverter): print cards hand.addHoleCards(cards, shows.group('PNAME')) + def readCollectPot(self,hand): + m = self.rexx.collect_pot_re.search(hand.string) + print m.groups() + print m.group('PNAME') + #for collection in m: + hand.addCollectPot(player=m.group('PNAME'),pot=m.group('POT')) def getRake(self, hand): hand.rake = hand.totalpot * Decimal('0.05') # probably not quite right diff --git a/pyfpdb/HandHistoryConverter.py b/pyfpdb/HandHistoryConverter.py index 06a59064..5e6804c5 100644 --- a/pyfpdb/HandHistoryConverter.py +++ b/pyfpdb/HandHistoryConverter.py @@ -78,6 +78,8 @@ class HandHistoryConverter: for street in hand.streets.groupdict(): self.readAction(hand, street) + self.readCollectPot(hand) + # finalise it (total the pot) hand.totalPot() self.getRake(hand) @@ -124,6 +126,7 @@ class HandHistoryConverter: def readBlinds(self, hand): abstract def readHeroCards(self, hand): abstract def readAction(self, hand, street): abstract + def readCollectPot(self, hand): abstract # Some sites don't report the rake. This will be called at the end of the hand after the pot total has been calculated # so that an inheriting class can calculate it for the specific site if need be. @@ -177,66 +180,6 @@ class HandHistoryConverter: except: traceback.print_exc(file=sys.stderr) - def writeHand(self, file, hand): - """Write out parsed data""" - print "DEBUG: *************************" - print "DEBUG: Start of print hand" - print "DEBUG: *************************" - - print "%s Game #%s: %s ($%s/$%s) - %s" %(hand.sitename, hand.handid, "XXXXhand.gametype", hand.sb, hand.bb, hand.starttime) - print "Table '%s' %d-max Seat #%s is the button" %(hand.tablename, hand.maxseats, hand.buttonpos) - - for player in hand.players: - print "Seat %s: %s ($%s)" %(player[0], player[1], player[2]) - - if(hand.posted[0] == "FpdbNBP"): - print "No small blind posted" - else: - print "%s: posts small blind $%s" %(hand.posted[0], hand.sb) - - #May be more than 1 bb posting - print "%s: posts big blind $%s" %(hand.posted[1], hand.bb) - if(len(hand.posted) > 2): - # Need to loop on all remaining big blinds - lazy - print "XXXXXXXXX FIXME XXXXXXXX" - - print "*** HOLE CARDS ***" - print "Dealt to %s [%s %s]" %(hand.hero , hand.holecards[0], hand.holecards[1]) - - for act in hand.actions['PREFLOP']: - self.printActionLine(act, 0) - - if 'PREFLOP' in hand.actions: - for act in hand.actions['PREFLOP']: - print "PF action" - - if 'FLOP' in hand.actions: - print "*** FLOP *** [%s %s %s]" %(hand.streets.group("FLOP1"), hand.streets.group("FLOP2"), hand.streets.group("FLOP3")) - for act in hand.actions['FLOP']: - self.printActionLine(act, 0) - - if 'TURN' in hand.actions: - print "*** TURN *** [%s %s %s] [%s]" %(hand.streets.group("FLOP1"), hand.streets.group("FLOP2"), hand.streets.group("FLOP3"), hand.streets.group("TURN1")) - for act in hand.actions['TURN']: - self.printActionLine(act, 0) - - if 'RIVER' in hand.actions: - print "*** RIVER *** [%s %s %s %s] [%s]" %(hand.streets.group("FLOP1"), hand.streets.group("FLOP2"), hand.streets.group("FLOP3"), hand.streets.group("TURN1"), hand.streets.group("RIVER1")) - for act in hand.actions['RIVER']: - self.printActionLine(act, 0) - - print "*** SUMMARY ***" - print "XXXXXXXXXXXX Need sumary info XXXXXXXXXXX" - - - def printActionLine(self, act, pot): - if act[1] == 'folds' or act[1] == 'checks': - print "%s: %s " %(act[0], act[1]) - if act[1] == 'calls': - print "%s: %s $%s" %(act[0], act[1], act[2]) - if act[1] == 'raises': - print "%s: %s $%s to XXXpottotalXXX" %(act[0], act[1], act[2]) - #takes a poker float (including , for thousand seperator and converts it to an int def float2int (self, string): @@ -282,6 +225,7 @@ class Hand: self.hero = "Hiro" self.holecards = {} # dict from player names to lists of hole cards self.board = {} # dict from street names to community cards + self.collected = {} # dict from player names to amounts collected self.action = [] self.totalpot = None @@ -373,7 +317,16 @@ class Hand: self.bets[street][name].append(Decimal(amount)) self.orderedBets[street].append(Decimal(amount)) self.actions[street] += [[player, 'bets', amount]] - + + def addCollectPot(self,player, pot): + if player not in self.collected: + self.collected[player] = pot + else: + # possibly lines like "p collected $ from pot" appear during the showdown + # but they are usually unique in the summary. + print "%s collected pot more than once; avoidable by reading winnings only from summary lines?" + + def totalPot(self): """If all bets and blinds have been added, totals up the total pot size Known bug: doesn't take into account side pots""" @@ -443,16 +396,29 @@ Known bug: doesn't take into account side pots""" #print self.board for player in self.players: - if self.holecards[player[1]]: # empty list default is false - hole = self.holecards[player[1]] - #print self.board.values() - board = [] - for s in self.board.values(): - board += s - playerhand = self.bestHand('hi', board+hole) - print "Seat %d: %s showed %s and won/lost with %s" % (player[0], player[1], hole, playerhand) + if player[1] in self.collected and self.holecards[player[1]]: + print "Seat %d: %s showed [%s %s] and won ($%s)" % (player[0], player[1], self.holecards[player[1]][0], self.holecards[player[1]][1], self.collected[player[1]]) + elif player[1] in self.collected: + print "Seat %d: %s collected ($%s)" % (player[0], player[1], self.collected[player[1]]) + elif self.holecards[player[1]]: + print "Seat %d: %s showed [%s %s]" % (player[0], player[1], self.holecards[player[1]][0], self.holecards[player[1]][1]) else: - print "Seat %d: %s mucked or folded" % (player[0], player[1]) + print "Seat %d: %s folded (or mucked..)" % (player[0], player[1]) + + # TODO: + # logic for side pots + # logic for which players get to showdown + # I'm just not sure we need to do this so heavily.. and if we do, it's probably better to use pokerlib + #if self.holecards[player[1]]: # empty list default is false + #hole = self.holecards[player[1]] + ##board = [] + ##for s in self.board.values(): + ##board += s + ##playerhand = self.bestHand('hi', board+hole) + ##print "Seat %d: %s showed %s and won/lost with %s" % (player[0], player[1], hole, playerhand) + #print "Seat %d: %s showed %s" % (player[0], player[1], hole) + #else: + #print "Seat %d: %s mucked or folded" % (player[0], player[1]) def printActionLine(self, act): @@ -463,10 +429,8 @@ Known bug: doesn't take into account side pots""" if act[1] == 'raises': print "%s: %s $%s to $%s" %(act[0], act[1], act[2], act[3]) - # going to use pokereval to figure out hands + # going to use pokereval to figure out hands at some point. # these functions are copied from pokergame.py - # im thinking perhaps its best to use all the functionality of pokergame instead - # of reinventing the wheel def bestHand(self, side, cards): #if self.variant == "omaha" or self.variant == "omaha8": #hand = self.serial2player[serial].hand.tolist(True) @@ -477,13 +441,13 @@ Known bug: doesn't take into account side pots""" print cards return HandHistoryConverter.eval.best('hi', cards, []) + # from pokergame.py def bestHandValue(self, side, serial): (value, cards) = self.bestHand(side, serial) return value - + # from pokergame.py def readableHandValueLong(self, side, value, cards): - cards = self.eval.card2string(cards) if value == "NoPair": if side == "low": if cards[0][0] == '5': From 7ac433fede89103c91b84447318b2c2f9a0f6105 Mon Sep 17 00:00:00 2001 From: Matt Turnbull Date: Tue, 9 Dec 2008 15:35:16 +0000 Subject: [PATCH 02/14] 2 decimal places for rake --- pyfpdb/HandHistoryConverter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpdb/HandHistoryConverter.py b/pyfpdb/HandHistoryConverter.py index 5e6804c5..63b51bb0 100644 --- a/pyfpdb/HandHistoryConverter.py +++ b/pyfpdb/HandHistoryConverter.py @@ -391,7 +391,7 @@ Known bug: doesn't take into account side pots""" print "what do they show" print "*** SUMMARY ***" - print "Total pot $%s | Rake $%s)" % (self.totalpot, self.rake) + print "Total pot $%s | Rake $%.2f)" % (self.totalpot, self.rake) # TODO side pots print "Board [%s %s %s %s %s]" % (self.streets.group("FLOP1"), self.streets.group("FLOP2"), self.streets.group("FLOP3"), self.streets.group("TURN1"), self.streets.group("RIVER1")) #print self.board From 6b0dcc37d42ecea4f1f23b337e74709cd5f34c1a Mon Sep 17 00:00:00 2001 From: Matt Turnbull Date: Tue, 9 Dec 2008 23:30:58 +0000 Subject: [PATCH 03/14] I set it up with Speed_Kuala_full.txt and now it's time to go through them all fixing the bugs. --- pyfpdb/EverleafToFpdb.py | 38 ++++++++++++---- pyfpdb/HandHistoryConverter.py | 82 ++++++++++++++++++++++++++-------- 2 files changed, 94 insertions(+), 26 deletions(-) diff --git a/pyfpdb/EverleafToFpdb.py b/pyfpdb/EverleafToFpdb.py index a5aa5514..e1f9bcbf 100755 --- a/pyfpdb/EverleafToFpdb.py +++ b/pyfpdb/EverleafToFpdb.py @@ -70,7 +70,7 @@ class Everleaf(HandHistoryConverter): self.sitename = "Everleaf" self.setFileType("text") self.rexx.setGameInfoRegex('.*Blinds \$?(?P[.0-9]+)/\$?(?P[.0-9]+)') - self.rexx.setSplitHandRegex('\n\n\n\n') + self.rexx.setSplitHandRegex('\n\n+') self.rexx.setHandInfoRegex('.*#(?P[0-9]+)\n.*\nBlinds \$?(?P[.0-9]+)/\$?(?P[.0-9]+) (?P.*) - (?P[0-9]+)/(?P[0-9]+)/(?P[0-9]+) - (?P
[0-9]+):(?P[0-9]+):(?P[0-9]+)\nTable (?P[ a-zA-Z]+)\nSeat (?P
[ a-zA-Z]+)\nSeat (?P


Table HandsPlayers

-

cardX: can be 1 through 20, one for each card. In holdem only 1-2 of these are used, in omaha 1-4, in stud/razz 1-7, in single draw games 1-10 is used and in badugi 1-16 (4*4) is used.

-

For the draw games: the first 5 (badugi: 4) cards are the initial cards, the next 5 (badugi: 4) are after the first draw. If a player keeps some cards then those cards' spaces are filled with "k", short for "kept".
-Example 1: If a player gets 2-6 spades for his first five cards and decides to throw away the 4 and then gets a 7 of spades then the first 10 fields of cardXValue would be as follows: 2, 3, 4, 5, 6, k, k, 7, k, k
-Example 2: If a player gets 2, 3, 5, 8, J of spades for his first five cards and decides to throw away the 2 and the 3 and then gets a Q and K of spades then the first 10 fields of cardXValue would be as follows: 2, 3, 5, 8, J, Q, K, k, k, k
-Note that it will k in the space of which card was there previously, so in example 2 where the player kept the last 3 cards, the last 3 fields of the first draw (ie. card8-10Value) are replaced with k.

+

cardX: can be 1 through 20, one for each card. In holdem only 1-2 of these are used, in omaha 1-4, in stud/razz 1-7, in single draw 1-10, in tripple draw all 20 and in badugi 1-16 (4*4).

+

For the draw games: the first 5 (badugi: 4) cards are the initial cards, the next 5 (badugi: 4) are after the first draw, etc.
+Example 1: If a player gets 2-6 spades for his first five cards and decides to throw away the 4 and then gets a 7 of spades then the first 10 fields of cardXValue would be as follows: 2, 3, 4, 5, 6, 2, 3, 5, 6, 7
+Example 2: If a player gets 2, 3, 5, 8, J of spades for his first five cards and decides to throw away the 2 and the 3 and then gets a Q and K of spades then the first 10 fields of cardXValue would be as follows: 2, 3, 5, 8, J, 5, 8, J, Q, K.

I did not separate this into an extra table because I felt the lost space is not sufficiently large. Also the benefit for searching is far less relevant.

@@ -370,6 +369,17 @@ Note that it will k in the space of which card was there previously, so in examp + + + + + + + + + + diff --git a/pyfpdb/CliFpdb.py b/pyfpdb/CliFpdb.py old mode 100644 new mode 100755 diff --git a/pyfpdb/Configuration.py b/pyfpdb/Configuration.py old mode 100644 new mode 100755 diff --git a/pyfpdb/HUD_main.py b/pyfpdb/HUD_main.py old mode 100644 new mode 100755 diff --git a/pyfpdb/fpdb.py b/pyfpdb/fpdb.py old mode 100644 new mode 100755 From 9ca0574d7854ec164c27ec0c36500e750d5bc0b0 Mon Sep 17 00:00:00 2001 From: Worros Date: Wed, 10 Dec 2008 21:21:43 +0900 Subject: [PATCH 11/14] Really basic check that username exists --- pyfpdb/GuiPlayerStats.py | 65 ++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/pyfpdb/GuiPlayerStats.py b/pyfpdb/GuiPlayerStats.py index 7d689228..a4baea41 100644 --- a/pyfpdb/GuiPlayerStats.py +++ b/pyfpdb/GuiPlayerStats.py @@ -46,41 +46,42 @@ class GuiPlayerStats (threading.Thread): result = self.cursor.execute(self.sql.query['getPlayerId'], self.heroes[self.activesite]) result = self.db.cursor.fetchall() - pid = result[0][0] - tmp = tmp.replace("", "(" + str(pid) + ")") - self.cursor.execute(tmp) - result = self.db.cursor.fetchall() - cols = 18 - rows = len(result)+1 # +1 for title row - self.stats_table = gtk.Table(rows, cols, False) - self.stats_table.set_col_spacings(4) - self.stats_table.show() - vbox.add(self.stats_table) + if not result == (): + pid = result[0][0] + tmp = tmp.replace("", "(" + str(pid) + ")") + self.cursor.execute(tmp) + result = self.db.cursor.fetchall() + cols = 18 + rows = len(result)+1 # +1 for title row + self.stats_table = gtk.Table(rows, cols, False) + self.stats_table.set_col_spacings(4) + self.stats_table.show() + vbox.add(self.stats_table) - # Create header row - titles = ("GID", "base", "Style", "Site", "$BB", "Hands", "VPIP", "PFR", "saw_f", "sawsd", "wtsdwsf", "wmsd", "FlAFq", "TuAFq", "RvAFq", "PFAFq", "Net($)", "BB/100") + # Create header row + titles = ("GID", "base", "Style", "Site", "$BB", "Hands", "VPIP", "PFR", "saw_f", "sawsd", "wtsdwsf", "wmsd", "FlAFq", "TuAFq", "RvAFq", "PFAFq", "Net($)", "BB/100") - col = 0 - row = 0 - for t in titles: - l = gtk.Label(titles[col]) - l.show() - self.stats_table.attach(l, col, col+1, row, row+1) - col +=1 + col = 0 + row = 0 + for t in titles: + l = gtk.Label(titles[col]) + l.show() + self.stats_table.attach(l, col, col+1, row, row+1) + col +=1 - for row in range(rows-1): - for col in range(cols): - if(row%2 == 0): - bgcolor = "white" - else: - bgcolor = "lightgrey" - eb = gtk.EventBox() - eb.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(bgcolor)) - l = gtk.Label(result[row-1][col]) - eb.add(l) - self.stats_table.attach(eb, col, col+1, row+1, row+2) - l.show() - eb.show() + for row in range(rows-1): + for col in range(cols): + if(row%2 == 0): + bgcolor = "white" + else: + bgcolor = "lightgrey" + eb = gtk.EventBox() + eb.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(bgcolor)) + l = gtk.Label(result[row-1][col]) + eb.add(l) + self.stats_table.attach(eb, col, col+1, row+1, row+2) + l.show() + eb.show() def fillPlayerFrame(self, vbox): From efc3e591eacabe1eceecb6bc2923ad56d98daac7 Mon Sep 17 00:00:00 2001 From: eblade Date: Wed, 10 Dec 2008 13:35:15 -0500 Subject: [PATCH 12/14] fix timeout callbacks for tracking table position --- pyfpdb/Hud.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pyfpdb/Hud.py b/pyfpdb/Hud.py index dcc7298c..c0804001 100644 --- a/pyfpdb/Hud.py +++ b/pyfpdb/Hud.py @@ -133,6 +133,8 @@ class Hud: (x, y) = loc[adj[i]] if self.stat_windows.has_key(i): self.stat_windows[i].relocate(x, y) + + return True def on_button_press(self, widget, event): if event.button == 1: @@ -151,6 +153,7 @@ class Hud: def reposition_windows(self, *args): self.update_table_position() + return True def debug_stat_windows(self, *args): print self.table, "\n", self.main_window.window.get_transient_for() @@ -225,7 +228,7 @@ class Hud: aux_params = config.get_aux_parameters(game_params['aux']) self.aux_windows.append(eval("%s.%s(gtk.Window(), config, 'fpdb')" % (aux_params['module'], aux_params['class']))) - gobject.timeout_add(0.5, self.update_table_position) + gobject.timeout_add(500, self.update_table_position) def update(self, hand, config, stat_dict): self.hand = hand # this is the last hand, so it is available later From 90803b297f0a6b820a684aa2155d43e988681cc9 Mon Sep 17 00:00:00 2001 From: Worros Date: Thu, 11 Dec 2008 23:42:33 +0900 Subject: [PATCH 13/14] PlayerStat update - Postgres fixes (still failing) Fixed parameter passing to execute - psycopg2 appears to be a bit fussy about how parameters are passed. Main query still doesn't work. psycopg2.DataError: division by zero - appears to be caused by ,round(100*sum(wonAtSD)/sum(sawShowdown)) AS wmsd Stuck there. --- pyfpdb/FpdbSQLQueries.py | 67 +++++++++++++++++++++++++++++++++++++++- pyfpdb/GuiPlayerStats.py | 2 +- 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/pyfpdb/FpdbSQLQueries.py b/pyfpdb/FpdbSQLQueries.py index f5b4f400..6443c1ed 100644 --- a/pyfpdb/FpdbSQLQueries.py +++ b/pyfpdb/FpdbSQLQueries.py @@ -726,7 +726,72 @@ class FpdbSQLQueries: on hprof2.gameTypeId = stats.gameTypeId order by stats.base, stats.limittype, stats.bigBlind""" elif(self.dbname == 'PostgreSQL'): - self.query['playerStats'] = """ """ + self.query['playerStats'] = """ + SELECT stats.gametypeId + ,stats.base + ,stats.limitType + ,stats.name + ,(stats.bigBlind/100) as BigBlind + ,stats.n + ,stats.vpip + ,stats.pfr + ,stats.saw_f + ,stats.sawsd + ,stats.wtsdwsf + ,stats.wmsd + ,stats.FlAFq + ,stats.TuAFq + ,stats.RvAFq + ,stats.PFAFq + ,hprof2.sum_profit/100 as Net + ,(hprof2.sum_profit/stats.bigBlind)/(stats.n/100) as BBlPer100 + FROM + (select gt.base + ,upper(gt.limitType) as limitType + ,s.name + ,gt.bigBlind + ,hc.gametypeId + ,sum(HDs) as n + ,round(100*sum(street0VPI)/sum(HDs)) as vpip + ,round(100*sum(street0Aggr)/sum(HDs)) as pfr + ,round(100*sum(street1Seen)/sum(HDs)) AS saw_f + ,round(100*sum(sawShowdown)/sum(HDs)) AS sawsd + ,round(100*sum(sawShowdown)/sum(street1Seen)) AS wtsdwsf + ,round(100*sum(wonAtSD)/sum(sawShowdown)) AS wmsd + ,round(100*sum(street1Aggr)/sum(street1Seen)) AS FlAFq + ,round(100*sum(street2Aggr)/sum(street2Seen)) AS TuAFq + ,round(100*sum(street3Aggr)/sum(street3Seen)) AS RvAFq + ,round(100*(sum(street1Aggr)+sum(street2Aggr)+sum(street3Aggr)) + /(sum(street1Seen)+sum(street2Seen)+sum(street3Seen))) AS PFAFq + from Gametypes gt + inner join Sites s on s.Id = gt.siteId + inner join HudCache hc on hc.gameTypeId = gt.Id + where hc.playerId in + group by gt.base + ,upper(gt.limitType) + ,s.name + ,gt.bigBlind + ,hc.gametypeId + ) stats + inner join + ( select hprof.gameTypeId, sum(hprof.profit) as sum_profit + from + (select hp.handId, + h.gameTypeId, + hp.winnings, + SUM(ha.amount) as costs, + hp.winnings - SUM(ha.amount) as profit + from HandsPlayers hp + inner join Hands h ON h.id = hp.handId + inner join HandsActions ha ON ha.handPlayerId = hp.id + where hp.playerId in + and hp.tourneysPlayersId IS NULL + group by hp.handId, h.gameTypeId, hp.position, hp.winnings + ) hprof + group by hprof.gameTypeId + ) hprof2 + on hprof2.gameTypeId = stats.gameTypeId + order by stats.base, stats.limittype, stats.bigBlind""" elif(self.dbname == 'SQLite'): self.query['playerStats'] = """ """ diff --git a/pyfpdb/GuiPlayerStats.py b/pyfpdb/GuiPlayerStats.py index a4baea41..c486a664 100644 --- a/pyfpdb/GuiPlayerStats.py +++ b/pyfpdb/GuiPlayerStats.py @@ -44,7 +44,7 @@ class GuiPlayerStats (threading.Thread): # Get currently active site and grab playerid tmp = self.sql.query['playerStats'] - result = self.cursor.execute(self.sql.query['getPlayerId'], self.heroes[self.activesite]) + result = self.cursor.execute(self.sql.query['getPlayerId'], (self.heroes[self.activesite],)) result = self.db.cursor.fetchall() if not result == (): pid = result[0][0] From 3a1ddad58ff7b06c154c2abce3cd9b110f480bcb Mon Sep 17 00:00:00 2001 From: Worros Date: Thu, 11 Dec 2008 23:55:03 +0900 Subject: [PATCH 14/14] Fix crasher in Grapher if no hands returned. Should alert user or at least draw something in right pane. --- pyfpdb/GuiGraphViewer.py | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/pyfpdb/GuiGraphViewer.py b/pyfpdb/GuiGraphViewer.py index d50f6b6a..94732295 100644 --- a/pyfpdb/GuiGraphViewer.py +++ b/pyfpdb/GuiGraphViewer.py @@ -83,22 +83,24 @@ class GuiGraphViewer (threading.Thread): self.ax.set_xlabel("Hands", fontsize = 12) self.ax.set_ylabel("$", fontsize = 12) self.ax.grid(color='g', linestyle=':', linewidth=0.2) - #This line will crash if no hands exist in the query. - text = "All Hands, " + sitename + str(name) + "\nProfit: $" + str(line[-1]) + "\nTotal Hands: " + str(len(line)) + if(line == None): + #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)) - self.ax.annotate(text, - xy=(10, -10), - xycoords='axes points', - horizontalalignment='left', verticalalignment='top', - fontsize=10) + self.ax.annotate(text, + xy=(10, -10), + xycoords='axes points', + horizontalalignment='left', verticalalignment='top', + fontsize=10) + #Draw plot + self.ax.plot(line,) - #Draw plot - self.ax.plot(line,) - - self.canvas = FigureCanvas(self.fig) # a gtk.DrawingArea - self.graphBox.add(self.canvas) - self.canvas.show() + self.canvas = FigureCanvas(self.fig) # a gtk.DrawingArea + self.graphBox.add(self.canvas) + self.canvas.show() #end of def showClicked def getRingProfitGraph(self, name, site): @@ -106,6 +108,9 @@ class GuiGraphViewer (threading.Thread): #returns (HandId,Winnings,Costs,Profit) winnings = self.db.cursor.fetchall() + if(winnings == ()): + return None + y=map(lambda x:float(x[3]), winnings) line = cumsum(y) return line/100

char(1)

h=hearts, s=spades, d=diamonds, c=clubs, unknown/no card=x

cardXDiscarded

boolean

Whether the card was discarded (this only applies to draw games, X can be 1 through 15 since the final cards can obviously not be discarded).

DrawnX

smallint

X can be 1 through 3.
+ This field denotes how many cards the player has drawn on each draw.

winnings

int