diff --git a/packaging/announce-0.21-rc2.txt b/packaging/announce-0.21-rc2.txt index 7dde9364..dd660ebd 100644 --- a/packaging/announce-0.21-rc2.txt +++ b/packaging/announce-0.21-rc2.txt @@ -27,8 +27,8 @@ Approx. 200 changesets (excl. merges) have gone in since 0.21-rc1. Some of the i Where to get it =============== -Please note that you will have to either recreate your database or use a new one if you're updating from 0.21-rc1 or older. -Config files from 0.20 and later should work. Please report if you have problems with config files from that version or later. +Please note that you will have to either recreate your database or use a new one if you're updating from 0.21-rc1 or older. Config files from 0.20 and later should work. Please report if you have problems with config files from that version or later. +You can find checksums (MD5 and SHA512) in the download folder. To download: http://sourceforge.net/projects/fpdb/files/fpdb/Snapshots/ To be notified by email of new versions you can subscribe to our announce mailing list here: https://lists.sourceforge.net/lists/listinfo/fpdb-announce diff --git a/pyfpdb/Aux_Hud.py b/pyfpdb/Aux_Hud.py new file mode 100644 index 00000000..01880708 --- /dev/null +++ b/pyfpdb/Aux_Hud.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +"""Mucked.py + +Mucked cards display for FreePokerTools HUD. +""" +# Copyright 2008-2010, Ray E. Barker +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# 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 General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +######################################################################## + +# to do + +# Standard Library modules + +# pyGTK modules +import gtk +import gobject + +# FreePokerTools modules +import Mucked +import Stats +class Stat_Window(Mucked.Seat_Window): + """Simple window class for stat windows.""" + + def create_contents(self, i): + self.grid = gtk.Table(rows = self.aw.nrows, columns = self.aw.ncols, homogeneous = False) + self.add(self.grid) + + self.stat_box = [ [None]*self.aw.ncols for i in range(self.aw.nrows) ] + for r in xrange(self.aw.nrows): + for c in xrange(self.aw.ncols): + self.stat_box[r][c] = Simple_stat(self.aw.stats[r][c]) + self.grid.attach(self.stat_box[r][c].widget, c, c+1, r, r+1, xpadding = self.aw.xpad, ypadding = self.aw.ypad) + + def update_contents(self, i): + if i == "common": return + player_id = self.aw.get_id_from_seat(i) + if player_id is None: return + for r in xrange(self.aw.nrows): + for c in xrange(self.aw.ncols): + self.stat_box[r][c].update(player_id, self.aw.hud.stat_dict) + +class Simple_HUD(Mucked.Aux_Seats): + """A simple HUD class based on the Aux_Window interface.""" + + def __init__(self, hud, config, params): + super(Simple_HUD, self).__init__(hud, config, params) +# Save everything you need to know about the hud as attrs. +# That way a subclass doesn't have to grab them. + self.poker_game = self.hud.poker_game + self.game_params = self.hud.config.get_game_parameters(self.hud.poker_game) + self.game = self.hud.config.supported_games[self.hud.poker_game] + self.max = self.hud.max + self.nrows = self.game_params['rows'] + self.ncols = self.game_params['cols'] + self.xpad = self.game_params['xpad'] + self.ypad = self.game_params['ypad'] + self.xshift = self.game_params['xshift'] + self.yshift = self.game_params['yshift'] + + self.aw_window_type = Stat_Window + +# layout is handled by superclass! + self.stats = [ [None]*self.ncols for i in range(self.nrows) ] + for stat in self.game.stats: + self.stats[self.config.supported_games[self.poker_game].stats[stat].row] \ + [self.config.supported_games[self.poker_game].stats[stat].col] = \ + self.config.supported_games[self.poker_game].stats[stat].stat_name + + def create_contents(self, container, i): + container.create_contents(i) + + def update_contents(self, container, i): + container.update_contents(i) + +class Simple_stat(object): + """A simple class for displaying a single stat.""" + def __init__(self, stat): + self.stat = stat + self.eb = Simple_eb(); + self.lab = Simple_label(self.stat) + self.eb.add(self.lab) + self.widget = self.eb + + def update(self, player_id, stat_dict): + self.lab.set_text( str(Stats.do_stat(stat_dict, player_id, self.stat)[1]) ) + +# Override thise methods to customize your eb or label +class Simple_eb(gtk.EventBox): pass +class Simple_label(gtk.Label): pass diff --git a/pyfpdb/Configuration.py b/pyfpdb/Configuration.py index a0ad4bad..365f7c4a 100644 --- a/pyfpdb/Configuration.py +++ b/pyfpdb/Configuration.py @@ -231,6 +231,8 @@ class Layout: self.max = int( node.getAttribute('max') ) if node.hasAttribute('fav_seat'): self.fav_seat = int( node.getAttribute('fav_seat') ) + if node.hasAttribute('name'): self.name = node.getAttribute('name') + else: self.name = None self.width = int( node.getAttribute('width') ) self.height = int( node.getAttribute('height') ) @@ -244,7 +246,11 @@ class Layout: self.common = (int( location_node.getAttribute('x') ), int( location_node.getAttribute('y'))) def __str__(self): - temp = " Layout = %d max, width= %d, height = %d" % (self.max, self.width, self.height) + if hasattr(self, 'name'): + name = self.name + ", " + else: + name = "" + temp = " Layout = %s%d max, width= %d, height = %d" % (name, self.max, self.width, self.height) if hasattr(self, 'fav_seat'): temp = temp + ", fav_seat = %d\n" % self.fav_seat else: temp = temp + "\n" if hasattr(self, "common"): @@ -477,6 +483,7 @@ class Import: self.interval = node.getAttribute("interval") self.callFpdbHud = node.getAttribute("callFpdbHud") self.hhArchiveBase = node.getAttribute("hhArchiveBase") + self.ResultsDirectory = node.getAttribute("ResultsDirectory") self.hhBulkPath = node.getAttribute("hhBulkPath") self.saveActions = string_to_bool(node.getAttribute("saveActions"), default=False) self.cacheSessions = string_to_bool(node.getAttribute("cacheSessions"), default=False) @@ -485,8 +492,8 @@ class Import: self.saveStarsHH = string_to_bool(node.getAttribute("saveStarsHH"), default=False) def __str__(self): - return " interval = %s\n callFpdbHud = %s\n hhArchiveBase = %s\n saveActions = %s\n fastStoreHudCache = %s\n" \ - % (self.interval, self.callFpdbHud, self.hhArchiveBase, self.saveActions, self.cacheSessions, self.sessionTimeout, self.fastStoreHudCache) + return " interval = %s\n callFpdbHud = %s\n hhArchiveBase = %s\n saveActions = %s\n fastStoreHudCache = %s\nResultsDirectory = %s" \ + % (self.interval, self.callFpdbHud, self.hhArchiveBase, self.saveActions, self.cacheSessions, self.sessionTimeout, self.fastStoreHudCache, self.ResultsDirectory) class HudUI: def __init__(self, node): @@ -1255,6 +1262,14 @@ class Config: try: imp['hhArchiveBase'] = self.imp.hhArchiveBase except: imp['hhArchiveBase'] = "~/.fpdb/HandHistories/" + # ResultsDirectory is the local cache for downloaded results + # NOTE: try: except: doesn'tseem to be triggering + # using if instead + if self.imp.ResultsDirectory != '': + imp['ResultsDirectory'] = self.imp.ResultsDirectory + else: + imp['ResultsDirectory'] = "~/.fpdb/Results/" + # hhBulkPath is the default location for bulk imports (if set) try: imp['hhBulkPath'] = self.imp.hhBulkPath except: imp['hhBulkPath'] = "" @@ -1485,7 +1500,7 @@ if __name__== "__main__": print "----------- END POPUP WINDOW FORMATS -----------" print "\n----------- IMPORT -----------" - print c.imp +# print c.imp # Need to add an str method for imp to print print "----------- END IMPORT -----------" c.edit_layout("PokerStars", 6, locations=( (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6) )) diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index e3a8e82e..a459c883 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -73,7 +73,7 @@ except ImportError: use_numpy = False -DB_VERSION = 147 +DB_VERSION = 149 # Variance created as sqlite has a bunch of undefined aggregate functions. @@ -125,7 +125,8 @@ class Database: {'tab':'Gametypes', 'col':'siteId', 'drop':0} , {'tab':'Hands', 'col':'gametypeId', 'drop':0} # mct 22/3/09 #, {'tab':'Hands', 'col':'siteHandNo', 'drop':0} unique indexes not dropped - , {'tab':'HandsActions', 'col':'handsPlayerId', 'drop':0} + , {'tab':'HandsActions', 'col':'handId', 'drop':1} + , {'tab':'HandsActions', 'col':'playerId', 'drop':1} , {'tab':'HandsActions', 'col':'actionId', 'drop':1} , {'tab':'HandsPlayers', 'col':'handId', 'drop':1} , {'tab':'HandsPlayers', 'col':'playerId', 'drop':1} @@ -150,7 +151,8 @@ class Database: , {'tab':'HandsPlayers', 'col':'handId', 'drop':0} , {'tab':'HandsPlayers', 'col':'playerId', 'drop':0} , {'tab':'HandsPlayers', 'col':'tourneysPlayersId', 'drop':0} - , {'tab':'HandsActions', 'col':'handsPlayerId', 'drop':0} + , {'tab':'HandsActions', 'col':'handId', 'drop':0} + , {'tab':'HandsActions', 'col':'playerId', 'drop':0} , {'tab':'HandsActions', 'col':'actionId', 'drop':1} , {'tab':'HudCache', 'col':'gametypeId', 'drop':1} , {'tab':'HudCache', 'col':'playerId', 'drop':0} @@ -174,7 +176,8 @@ class Database: , {'fktab':'HandsPlayers', 'fkcol':'handId', 'rtab':'Hands', 'rcol':'id', 'drop':1} , {'fktab':'HandsPlayers', 'fkcol':'playerId', 'rtab':'Players', 'rcol':'id', 'drop':1} , {'fktab':'HandsPlayers', 'fkcol':'tourneysPlayersId','rtab':'TourneysPlayers','rcol':'id', 'drop':1} - , {'fktab':'HandsActions', 'fkcol':'handsPlayerId', 'rtab':'HandsPlayers', 'rcol':'id', 'drop':1} + , {'fktab':'HandsActions', 'fkcol':'handId', 'rtab':'Hands', 'rcol':'id', 'drop':1} + , {'fktab':'HandsActions', 'fkcol':'playerId', 'rtab':'Players', 'rcol':'id', 'drop':1} , {'fktab':'HandsActions', 'fkcol':'actionId', 'rtab':'Actions', 'rcol':'id', 'drop':1} , {'fktab':'HudCache', 'fkcol':'gametypeId', 'rtab':'Gametypes', 'rcol':'id', 'drop':1} , {'fktab':'HudCache', 'fkcol':'playerId', 'rtab':'Players', 'rcol':'id', 'drop':0} @@ -184,7 +187,8 @@ class Database: {'fktab':'Hands', 'fkcol':'gametypeId', 'rtab':'Gametypes', 'rcol':'id', 'drop':1} , {'fktab':'HandsPlayers', 'fkcol':'handId', 'rtab':'Hands', 'rcol':'id', 'drop':1} , {'fktab':'HandsPlayers', 'fkcol':'playerId', 'rtab':'Players', 'rcol':'id', 'drop':1} - , {'fktab':'HandsActions', 'fkcol':'handsPlayerId', 'rtab':'HandsPlayers', 'rcol':'id', 'drop':1} + , {'fktab':'HandsActions', 'fkcol':'handId', 'rtab':'Hands', 'rcol':'id', 'drop':1} + , {'fktab':'HandsActions', 'fkcol':'playerId', 'rtab':'Players', 'rcol':'id', 'drop':1} , {'fktab':'HandsActions', 'fkcol':'actionId', 'rtab':'Actions', 'rcol':'id', 'drop':1} , {'fktab':'HudCache', 'fkcol':'gametypeId', 'rtab':'Gametypes', 'rcol':'id', 'drop':1} , {'fktab':'HudCache', 'fkcol':'playerId', 'rtab':'Players', 'rcol':'id', 'drop':0} @@ -450,7 +454,7 @@ class Database: self.connection = sqlite3.connect(self.db_path, detect_types=sqlite3.PARSE_DECLTYPES ) self.__connected = True sqlite3.register_converter("bool", lambda x: bool(int(x))) - sqlite3.register_adapter(bool, lambda x: "1" if x else "0") + sqlite3.register_adapter(bool, lambda x: 1 if x else 0) self.connection.create_function("floor", 1, math.floor) tmp = sqlitemath() self.connection.create_function("mod", 2, tmp.mod) @@ -634,6 +638,18 @@ class Database: return c.fetchone()[0] #end def getTourneyCount + def getSiteTourneyNos(self, site): + c = self.connection.cursor() + # FIXME: Take site and actually fetch siteId from that + # Fixed to Winamax atm + q = self.sql.query['getSiteTourneyNos'] + q = q.replace('%s', self.sql.query['placeholder']) + c.execute(q, (14,)) + alist = [] + for row in c.fetchall(): + alist.append(row) + return alist + def get_actual_seat(self, hand_id, name): c = self.connection.cursor() c.execute(self.sql.query['get_actual_seat'], (hand_id, name)) @@ -1507,6 +1523,7 @@ class Database: c.execute("INSERT INTO Sites (name,code) VALUES ('PKR', 'PK')") c.execute("INSERT INTO Sites (name,code) VALUES ('iPoker', 'IP')") c.execute("INSERT INTO Sites (name,code) VALUES ('Winamax', 'WM')") + c.execute("INSERT INTO Sites (name,code) VALUES ('Everest', 'EP')") #Fill Actions c.execute("INSERT INTO Actions (name,code) VALUES ('ante', 'A')") c.execute("INSERT INTO Actions (name,code) VALUES ('small blind', 'SB')") @@ -1714,10 +1731,11 @@ class Database: c.execute(q, ( p['tableName'], - p['gametypeId'], p['siteHandNo'], p['tourneyId'], - p['startTime'], + p['gametypeId'], + p['sessionId'], + p['startTime'], datetime.utcnow(), #importtime p['seats'], p['maxSeats'], @@ -1747,7 +1765,7 @@ class Database: return self.get_last_insert_id(c) # def storeHand - def storeHandsPlayers(self, hid, pids, pdata, printdata = False): + def storeHandsPlayers(self, hid, pids, pdata, hp_bulk = None, insert = False, printdata = False): #print "DEBUG: %s %s %s" %(hid, pids, pdata) if printdata: import pprint @@ -1755,7 +1773,6 @@ class Database: pp.pprint(pdata) inserts = [] - hpid = {} for p in pdata: inserts.append( (hid, pids[p], @@ -1813,8 +1830,15 @@ class Database: pdata[p]['street0_3BDone'], pdata[p]['street0_4BChance'], pdata[p]['street0_4BDone'], - pdata[p]['other3BStreet0'], - pdata[p]['other4BStreet0'], + pdata[p]['street0_C4BChance'], + pdata[p]['street0_C4BDone'], + pdata[p]['street0_FoldTo3BChance'], + pdata[p]['street0_FoldTo3BDone'], + pdata[p]['street0_FoldTo4BChance'], + pdata[p]['street0_FoldTo4BDone'], + pdata[p]['street0_SqueezeChance'], + pdata[p]['street0_SqueezeDone'], + pdata[p]['success_Steal'], pdata[p]['otherRaisedStreet0'], pdata[p]['otherRaisedStreet1'], pdata[p]['otherRaisedStreet2'], @@ -1854,23 +1878,16 @@ class Database: pdata[p]['street4Raises'] ) ) - q = self.sql.query['store_hands_players'] - q = q.replace('%s', self.sql.query['placeholder']) + if insert: + hp_bulk += inserts + q = self.sql.query['store_hands_players'] + q = q.replace('%s', self.sql.query['placeholder']) + c = self.get_cursor() + c.executemany(q, hp_bulk) + + return inserts - #print "DEBUG: inserts: %s" %inserts - #print "DEBUG: q: %s" % q - c = self.get_cursor() - - if self.import_options['saveActions']: - for r in inserts: - c.execute(q, r) - hpid[(r[0], r[1])] = self.get_last_insert_id(c) - else: - c.executemany(q, inserts) - - return hpid - - def storeHandsActions(self, hid, pids, hpid, adata, printdata = False): + def storeHandsActions(self, hid, pids, adata, ha_bulk = None, insert = False, printdata = False): #print "DEBUG: %s %s %s" %(hid, pids, adata) # This can be used to generate test data. Currently unused @@ -1881,8 +1898,8 @@ class Database: inserts = [] for a in adata: - inserts.append( (hpid[(hid, pids[adata[a]['player']])], - #self.getHandsPlayerId(self.hid, pids[adata[a]['player']]), + inserts.append( (hid, + pids[adata[a]['player']], adata[a]['street'], adata[a]['actionNo'], adata[a]['streetActionNo'], @@ -1895,11 +1912,14 @@ class Database: adata[a]['allIn'] ) ) - q = self.sql.query['store_hands_actions'] - q = q.replace('%s', self.sql.query['placeholder']) + if insert: + ha_bulk += inserts + q = self.sql.query['store_hands_actions'] + q = q.replace('%s', self.sql.query['placeholder']) + c = self.get_cursor() + c.executemany(q, ha_bulk) - c = self.get_cursor() - c.executemany(q, inserts) + return inserts def storeHudCache(self, gid, pids, starttime, pdata): """Update cached statistics. If update fails because no record exists, do an insert.""" @@ -1926,95 +1946,103 @@ class Database: #print "DEBUG: %s %s %s" %(hid, pids, pdata) inserts = [] for p in pdata: - line = [0]*85 + #NOTE: Insert new stats at right place because SQL needs strict order + line = [] - line[0] = 1 # HDs - if pdata[p]['street0VPI']: line[1] = 1 - if pdata[p]['street0Aggr']: line[2] = 1 - if pdata[p]['street0_3BChance']: line[3] = 1 - if pdata[p]['street0_3BDone']: line[4] = 1 - if pdata[p]['street0_4BChance']: line[5] = 1 - if pdata[p]['street0_4BDone']: line[6] = 1 - if pdata[p]['other3BStreet0']: line[7] = 1 - if pdata[p]['other4BStreet0']: line[8] = 1 - if pdata[p]['street1Seen']: line[9] = 1 - if pdata[p]['street2Seen']: line[10] = 1 - if pdata[p]['street3Seen']: line[11] = 1 - if pdata[p]['street4Seen']: line[12] = 1 - if pdata[p]['sawShowdown']: line[13] = 1 - if pdata[p]['street1Aggr']: line[14] = 1 - if pdata[p]['street2Aggr']: line[15] = 1 - if pdata[p]['street3Aggr']: line[16] = 1 - if pdata[p]['street4Aggr']: line[17] = 1 - if pdata[p]['otherRaisedStreet0']: line[18] = 1 - if pdata[p]['otherRaisedStreet1']: line[19] = 1 - if pdata[p]['otherRaisedStreet2']: line[20] = 1 - if pdata[p]['otherRaisedStreet3']: line[21] = 1 - if pdata[p]['otherRaisedStreet4']: line[22] = 1 - if pdata[p]['foldToOtherRaisedStreet0']: line[23] = 1 - if pdata[p]['foldToOtherRaisedStreet1']: line[24] = 1 - if pdata[p]['foldToOtherRaisedStreet2']: line[25] = 1 - if pdata[p]['foldToOtherRaisedStreet3']: line[26] = 1 - if pdata[p]['foldToOtherRaisedStreet4']: line[27] = 1 - line[28] = pdata[p]['wonWhenSeenStreet1'] - line[29] = pdata[p]['wonWhenSeenStreet2'] - line[30] = pdata[p]['wonWhenSeenStreet3'] - line[31] = pdata[p]['wonWhenSeenStreet4'] - line[32] = pdata[p]['wonAtSD'] - if pdata[p]['raiseFirstInChance']: line[33] = 1 - if pdata[p]['raisedFirstIn']: line[34] = 1 - if pdata[p]['foldBbToStealChance']: line[35] = 1 - if pdata[p]['foldedBbToSteal']: line[36] = 1 - if pdata[p]['foldSbToStealChance']: line[37] = 1 - if pdata[p]['foldedSbToSteal']: line[38] = 1 - if pdata[p]['street1CBChance']: line[39] = 1 - if pdata[p]['street1CBDone']: line[40] = 1 - if pdata[p]['street2CBChance']: line[41] = 1 - if pdata[p]['street2CBDone']: line[42] = 1 - if pdata[p]['street3CBChance']: line[43] = 1 - if pdata[p]['street3CBDone']: line[44] = 1 - if pdata[p]['street4CBChance']: line[45] = 1 - if pdata[p]['street4CBDone']: line[46] = 1 - if pdata[p]['foldToStreet1CBChance']: line[47] = 1 - if pdata[p]['foldToStreet1CBDone']: line[48] = 1 - if pdata[p]['foldToStreet2CBChance']: line[49] = 1 - if pdata[p]['foldToStreet2CBDone']: line[50] = 1 - if pdata[p]['foldToStreet3CBChance']: line[51] = 1 - if pdata[p]['foldToStreet3CBDone']: line[52] = 1 - if pdata[p]['foldToStreet4CBChance']: line[53] = 1 - if pdata[p]['foldToStreet4CBDone']: line[54] = 1 - line[55] = pdata[p]['totalProfit'] - if pdata[p]['street1CheckCallRaiseChance']: line[56] = 1 - if pdata[p]['street1CheckCallRaiseDone']: line[57] = 1 - if pdata[p]['street2CheckCallRaiseChance']: line[58] = 1 - if pdata[p]['street2CheckCallRaiseDone']: line[59] = 1 - if pdata[p]['street3CheckCallRaiseChance']: line[60] = 1 - if pdata[p]['street3CheckCallRaiseDone']: line[61] = 1 - if pdata[p]['street4CheckCallRaiseChance']: line[62] = 1 - if pdata[p]['street4CheckCallRaiseDone']: line[63] = 1 - if pdata[p]['street0Calls']: line[64] = 1 - if pdata[p]['street1Calls']: line[65] = 1 - if pdata[p]['street2Calls']: line[66] = 1 - if pdata[p]['street3Calls']: line[67] = 1 - if pdata[p]['street4Calls']: line[68] = 1 - if pdata[p]['street0Bets']: line[69] = 1 - if pdata[p]['street1Bets']: line[70] = 1 - if pdata[p]['street2Bets']: line[71] = 1 - if pdata[p]['street3Bets']: line[72] = 1 - if pdata[p]['street4Bets']: line[73] = 1 - if pdata[p]['street0Raises']: line[74] = 1 - if pdata[p]['street1Raises']: line[75] = 1 - if pdata[p]['street2Raises']: line[76] = 1 - if pdata[p]['street3Raises']: line[77] = 1 - if pdata[p]['street4Raises']: line[78] = 1 + line.append(1) # HDs + line.append(pdata[p]['street0VPI']) + line.append(pdata[p]['street0Aggr']) + line.append(pdata[p]['street0_3BChance']) + line.append(pdata[p]['street0_3BDone']) + line.append(pdata[p]['street0_4BChance']) + line.append(pdata[p]['street0_4BDone']) + line.append(pdata[p]['street0_C4BChance']) + line.append(pdata[p]['street0_C4BDone']) + line.append(pdata[p]['street0_FoldTo3BChance']) + line.append(pdata[p]['street0_FoldTo3BDone']) + line.append(pdata[p]['street0_FoldTo4BChance']) + line.append(pdata[p]['street0_FoldTo4BDone']) + line.append(pdata[p]['street0_SqueezeChance']) + line.append(pdata[p]['street0_SqueezeDone']) + line.append(pdata[p]['success_Steal']) + line.append(pdata[p]['street1Seen']) + line.append(pdata[p]['street2Seen']) + line.append(pdata[p]['street3Seen']) + line.append(pdata[p]['street4Seen']) + line.append(pdata[p]['sawShowdown']) + line.append(pdata[p]['street1Aggr']) + line.append(pdata[p]['street2Aggr']) + line.append(pdata[p]['street3Aggr']) + line.append(pdata[p]['street4Aggr']) + line.append(pdata[p]['otherRaisedStreet0']) + line.append(pdata[p]['otherRaisedStreet1']) + line.append(pdata[p]['otherRaisedStreet2']) + line.append(pdata[p]['otherRaisedStreet3']) + line.append(pdata[p]['otherRaisedStreet4']) + line.append(pdata[p]['foldToOtherRaisedStreet0']) + line.append(pdata[p]['foldToOtherRaisedStreet1']) + line.append(pdata[p]['foldToOtherRaisedStreet2']) + line.append(pdata[p]['foldToOtherRaisedStreet3']) + line.append(pdata[p]['foldToOtherRaisedStreet4']) + line.append(pdata[p]['wonWhenSeenStreet1']) + line.append(pdata[p]['wonWhenSeenStreet2']) + line.append(pdata[p]['wonWhenSeenStreet3']) + line.append(pdata[p]['wonWhenSeenStreet4']) + line.append(pdata[p]['wonAtSD']) + line.append(pdata[p]['raiseFirstInChance']) + line.append(pdata[p]['raisedFirstIn']) + line.append(pdata[p]['foldBbToStealChance']) + line.append(pdata[p]['foldedBbToSteal']) + line.append(pdata[p]['foldSbToStealChance']) + line.append(pdata[p]['foldedSbToSteal']) + line.append(pdata[p]['street1CBChance']) + line.append(pdata[p]['street1CBDone']) + line.append(pdata[p]['street2CBChance']) + line.append(pdata[p]['street2CBDone']) + line.append(pdata[p]['street3CBChance']) + line.append(pdata[p]['street3CBDone']) + line.append(pdata[p]['street4CBChance']) + line.append(pdata[p]['street4CBDone']) + line.append(pdata[p]['foldToStreet1CBChance']) + line.append(pdata[p]['foldToStreet1CBDone']) + line.append(pdata[p]['foldToStreet2CBChance']) + line.append(pdata[p]['foldToStreet2CBDone']) + line.append(pdata[p]['foldToStreet3CBChance']) + line.append(pdata[p]['foldToStreet3CBDone']) + line.append(pdata[p]['foldToStreet4CBChance']) + line.append(pdata[p]['foldToStreet4CBDone']) + line.append(pdata[p]['totalProfit']) + line.append(pdata[p]['street1CheckCallRaiseChance']) + line.append(pdata[p]['street1CheckCallRaiseDone']) + line.append(pdata[p]['street2CheckCallRaiseChance']) + line.append(pdata[p]['street2CheckCallRaiseDone']) + line.append(pdata[p]['street3CheckCallRaiseChance']) + line.append(pdata[p]['street3CheckCallRaiseDone']) + line.append(pdata[p]['street4CheckCallRaiseChance']) + line.append(pdata[p]['street4CheckCallRaiseDone']) + line.append(pdata[p]['street0Calls']) + line.append(pdata[p]['street1Calls']) + line.append(pdata[p]['street2Calls']) + line.append(pdata[p]['street3Calls']) + line.append(pdata[p]['street4Calls']) + line.append(pdata[p]['street0Bets']) + line.append(pdata[p]['street1Bets']) + line.append(pdata[p]['street2Bets']) + line.append(pdata[p]['street3Bets']) + line.append(pdata[p]['street4Bets']) + line.append(pdata[p]['street0Raises']) + line.append(pdata[p]['street1Raises']) + line.append(pdata[p]['street2Raises']) + line.append(pdata[p]['street3Raises']) + line.append(pdata[p]['street4Raises']) - line[79] = gid # gametypeId - line[80] = pids[p] # playerId - line[81] = len(pids) # activeSeats + line.append(gid) # gametypeId + line.append(pids[p]) # playerId + line.append(len(pids)) # activeSeats pos = {'B':'B', 'S':'S', 0:'D', 1:'C', 2:'M', 3:'M', 4:'M', 5:'E', 6:'E', 7:'E', 8:'E', 9:'E' } - line[82] = pos[pdata[p]['position']] - line[83] = pdata[p]['tourneyTypeId'] - line[84] = styleKey # styleKey + line.append(pos[pdata[p]['position']]) + line.append(pdata[p]['tourneyTypeId']) + line.append(styleKey) # styleKey inserts.append(line) @@ -2040,10 +2068,9 @@ class Database: pass def storeSessionsCache(self, pids, startTime, game, pdata): - """Update cached sessions. If update fails because no record exists, do an insert""" + """Update cached sessions. If no record exists, do an insert""" THRESHOLD = timedelta(seconds=int(self.sessionTimeout * 60)) - bigBet = int(Decimal(game['bb'])*200) select_sessionscache = self.sql.query['select_sessionscache'] select_sessionscache = select_sessionscache.replace('%s', self.sql.query['placeholder']) @@ -2066,6 +2093,9 @@ class Database: delete_sessions = self.sql.query['delete_sessions'] delete_sessions = delete_sessions.replace('%s', self.sql.query['placeholder']) + update_hands_sessionid = self.sql.query['update_hands_sessionid'] + update_hands_sessionid = update_hands_sessionid.replace('%s', self.sql.query['placeholder']) + #Grab playerIds using hero names in HUD_Config.xml try: # derive list of program owner's player ids @@ -2095,29 +2125,32 @@ class Database: if (game['type']=='ring'): line[0] = 1 # count ring hands if (game['type']=='tour'): line[1] = 1 # count tour hands - if (game['type']=='ring'): line[2] = pdata[p]['totalProfit'] #sum of profit - if (game['type']=='ring'): line[3] = 0 #float(Decimal(pdata[p]['totalProfit'])/Decimal(bigBet)) #sum of big bets won + if (game['type']=='ring' and game['currency']=='USD'): line[2] = pdata[p]['totalProfit'] #sum of ring profit in USD + if (game['type']=='ring' and game['currency']=='EUR'): line[3] = pdata[p]['totalProfit'] #sum of ring profit in EUR line[4] = startTime inserts.append(line) cursor = self.get_cursor() + id = None for row in inserts: threshold = [] threshold.append(row[-1]-THRESHOLD) threshold.append(row[-1]+THRESHOLD) cursor.execute(select_sessionscache, threshold) - num = cursor.rowcount + session_records = cursor.fetchall() + num = len(session_records) if (num == 1): + id = session_records[0][0] #grab the sessionId # Try to do the update first: #print "DEBUG: found 1 record to update" update_mid = row + row[-1:] cursor.execute(select_sessionscache_mid, update_mid[-2:]) - mid = cursor.rowcount + mid = len(cursor.fetchall()) if (mid == 0): update_startend = row[-1:] + row + threshold cursor.execute(select_sessionscache_start, update_startend[-3:]) - start = cursor.rowcount + start = len(cursor.fetchall()) if (start == 0): #print "DEBUG:", start, " start record found. Update stats and start time" cursor.execute(update_sessionscache_end, update_startend) @@ -2128,37 +2161,36 @@ class Database: #print "DEBUG: update stats mid-session" cursor.execute(update_sessionscache_mid, update_mid) elif (num > 1): + session_ids = [session_records[0][0], session_records[1][0]] + session_ids.sort() # Multiple matches found - merge them into one session and update: - #print "DEBUG:", num, "matches found" - cursor.execute(merge_sessionscache, threshold) + # - Obtain the session start and end times for the new combined session + cursor.execute(merge_sessionscache, session_ids) merge = cursor.fetchone() - cursor.execute(delete_sessions, threshold) + # - Delete the old records + for id in session_ids: + cursor.execute(delete_sessions, id) + # - Insert the new updated record cursor.execute(insert_sessionscache, merge) + # - Obtain the new sessionId and write over the old ids in Hands + id = self.get_last_insert_id(cursor) #grab the sessionId + update_hands = [id] + session_ids + cursor.execute(update_hands_sessionid, update_hands) + # - Update the newly combined record in SessionsCache with data from this hand update_mid = row + row[-1:] - cursor.execute(select_sessionscache_mid, update_mid[-2:]) - mid = cursor.rowcount - if (mid == 0): - update_startend = row[-1:] + row + threshold - cursor.execute(select_sessionscache_start, update_startend[-3:]) - start = cursor.rowcount - if (start == 0): - #print "DEBUG:", start, " start record found. Update stats and start time" - cursor.execute(update_sessionscache_end, update_startend) - else: - #print "DEBUG: 1 end record found. Update stats and end time time" - cursor.execute(update_sessionscache_start, update_startend) - else: - #print "DEBUG: update stats mid-session" - cursor.execute(update_sessionscache_mid, update_mid) + cursor.execute(update_sessionscache_mid, update_mid) elif (num == 0): # No matches found, insert new session: insert = row + row[-1:] insert = insert[-2:] + insert[:-2] #print "DEBUG: No matches found. Insert record", insert cursor.execute(insert_sessionscache, insert) + id = self.get_last_insert_id(cursor) #grab the sessionId else: # Something bad happened - pass + pass + + return id def isDuplicate(self, gametypeID, siteHandNo): dup = False diff --git a/pyfpdb/DerivedStats.py b/pyfpdb/DerivedStats.py index 8b1f4177..93c09571 100644 --- a/pyfpdb/DerivedStats.py +++ b/pyfpdb/DerivedStats.py @@ -30,62 +30,76 @@ class DerivedStats(): self.hands = {} self.handsplayers = {} self.handsactions = {} + self._initStats = DerivedStats._buildStatsInitializer() + + @staticmethod + def _buildStatsInitializer(): + init = {} + #Init vars that may not be used, but still need to be inserted. + # All stud street4 need this when importing holdem + init['winnings'] = 0 + init['rake'] = 0 + init['totalProfit'] = 0 + init['street4Aggr'] = False + init['wonWhenSeenStreet1'] = 0.0 + init['sawShowdown'] = False + init['wonAtSD'] = 0.0 + init['startCards'] = 0 + init['position'] = 2 + init['street0_3BChance'] = False + init['street0_3BDone'] = False + init['street0_4BChance'] = False + init['street0_4BDone'] = False + init['street0_C4BChance'] = False + init['street0_C4BDone'] = False + init['street0_FoldTo3BChance']= False + init['street0_FoldTo3BDone']= False + init['street0_FoldTo4BChance']= False + init['street0_FoldTo4BDone']= False + init['street0_SqueezeChance']= False + init['street0_SqueezeDone'] = False + init['success_Steal'] = False + init['raiseFirstInChance'] = False + init['raisedFirstIn'] = False + init['foldBbToStealChance'] = False + init['foldSbToStealChance'] = False + init['foldedSbToSteal'] = False + init['foldedBbToSteal'] = False + init['tourneyTypeId'] = None + init['street1Seen'] = False + init['street2Seen'] = False + init['street3Seen'] = False + init['street4Seen'] = False + + + for i in range(5): + init['street%dCalls' % i] = 0 + init['street%dBets' % i] = 0 + init['street%dRaises' % i] = 0 + for i in range(1,5): + init['street%dCBChance' %i] = False + init['street%dCBDone' %i] = False + init['street%dCheckCallRaiseChance' %i] = False + init['street%dCheckCallRaiseDone' %i] = False + init['otherRaisedStreet%d' %i] = False + init['foldToOtherRaisedStreet%d' %i] = False + + #FIXME - Everything below this point is incomplete. + init['other3BStreet0'] = False + init['other4BStreet0'] = False + init['otherRaisedStreet0'] = False + init['foldToOtherRaisedStreet0'] = False + for i in range(1,5): + init['foldToStreet%dCBChance' %i] = False + init['foldToStreet%dCBDone' %i] = False + init['wonWhenSeenStreet2'] = 0.0 + init['wonWhenSeenStreet3'] = 0.0 + init['wonWhenSeenStreet4'] = 0.0 + return init def getStats(self, hand): - for player in hand.players: - self.handsplayers[player[1]] = {} - #Init vars that may not be used, but still need to be inserted. - # All stud street4 need this when importing holdem - self.handsplayers[player[1]]['winnings'] = 0 - self.handsplayers[player[1]]['rake'] = 0 - self.handsplayers[player[1]]['totalProfit'] = 0 - self.handsplayers[player[1]]['street4Aggr'] = False - self.handsplayers[player[1]]['wonWhenSeenStreet1'] = 0.0 - self.handsplayers[player[1]]['sawShowdown'] = False - self.handsplayers[player[1]]['wonAtSD'] = 0.0 - self.handsplayers[player[1]]['startCards'] = 0 - self.handsplayers[player[1]]['position'] = 2 - self.handsplayers[player[1]]['street0_3BChance'] = False - self.handsplayers[player[1]]['street0_3BDone'] = False - self.handsplayers[player[1]]['street0_4BChance'] = False #FIXME: this might not actually be implemented - self.handsplayers[player[1]]['street0_4BDone'] = False #FIXME: this might not actually be implemented - self.handsplayers[player[1]]['raiseFirstInChance'] = False - self.handsplayers[player[1]]['raisedFirstIn'] = False - self.handsplayers[player[1]]['foldBbToStealChance'] = False - self.handsplayers[player[1]]['foldSbToStealChance'] = False - self.handsplayers[player[1]]['foldedSbToSteal'] = False - self.handsplayers[player[1]]['foldedBbToSteal'] = False - self.handsplayers[player[1]]['tourneyTypeId'] = None - self.handsplayers[player[1]]['street1Seen'] = False - self.handsplayers[player[1]]['street2Seen'] = False - self.handsplayers[player[1]]['street3Seen'] = False - self.handsplayers[player[1]]['street4Seen'] = False - - - for i in range(5): - self.handsplayers[player[1]]['street%dCalls' % i] = 0 - self.handsplayers[player[1]]['street%dBets' % i] = 0 - self.handsplayers[player[1]]['street%dRaises' % i] = 0 - for i in range(1,5): - self.handsplayers[player[1]]['street%dCBChance' %i] = False - self.handsplayers[player[1]]['street%dCBDone' %i] = False - self.handsplayers[player[1]]['street%dCheckCallRaiseChance' %i] = False - self.handsplayers[player[1]]['street%dCheckCallRaiseDone' %i] = False - self.handsplayers[player[1]]['otherRaisedStreet%d' %i] = False - self.handsplayers[player[1]]['foldToOtherRaisedStreet%d' %i] = False - - #FIXME - Everything below this point is incomplete. - self.handsplayers[player[1]]['other3BStreet0'] = False - self.handsplayers[player[1]]['other4BStreet0'] = False - self.handsplayers[player[1]]['otherRaisedStreet0'] = False - self.handsplayers[player[1]]['foldToOtherRaisedStreet0'] = False - for i in range(1,5): - self.handsplayers[player[1]]['foldToStreet%dCBChance' %i] = False - self.handsplayers[player[1]]['foldToStreet%dCBDone' %i] = False - self.handsplayers[player[1]]['wonWhenSeenStreet2'] = 0.0 - self.handsplayers[player[1]]['wonWhenSeenStreet3'] = 0.0 - self.handsplayers[player[1]]['wonWhenSeenStreet4'] = 0.0 + self.handsplayers[player[1]] = self._initStats.copy() self.assembleHands(self.hand) self.assembleHandsPlayers(self.hand) @@ -106,6 +120,7 @@ class DerivedStats(): self.hands['tableName'] = hand.tablename self.hands['siteHandNo'] = hand.handid self.hands['gametypeId'] = None # Leave None, handled later after checking db + self.hands['sessionId'] = None # Leave None, added later if caching sessions self.hands['startTime'] = hand.startTime # format this! self.hands['importTime'] = None self.hands['seats'] = self.countPlayers(hand) @@ -413,8 +428,10 @@ class DerivedStats(): #print "\naction:", action[0], posn, type(posn), steal_attempt, act if posn == 'B': #NOTE: Stud games will never hit this section - self.handsplayers[pname]['foldBbToStealChance'] = steal_attempt - self.handsplayers[pname]['foldedBbToSteal'] = steal_attempt and act == 'folds' + if steal_attempt: + self.handsplayers[pname]['foldBbToStealChance'] = True + self.handsplayers[pname]['foldedBbToSteal'] = act == 'folds' + self.handsplayers[stealer]['success_Steal'] = act == 'folds' break elif posn == 'S': self.handsplayers[pname]['foldSbToStealChance'] = steal_attempt @@ -430,6 +447,7 @@ class DerivedStats(): raised = True if posn in steal_positions: steal_attempt = True + stealer = pname if act == 'calls': break @@ -439,16 +457,48 @@ class DerivedStats(): def calc34BetStreet0(self, hand): """Fills street0_(3|4)B(Chance|Done), other(3|4)BStreet0""" bet_level = 1 # bet_level after 3-bet is equal to 3 + squeeze_chance = 0 for action in hand.actions[hand.actionStreets[1]]: - # FIXME: fill other(3|4)BStreet0 - i have no idea what does it mean - pname, aggr = action[0], action[1] in ('raises', 'bets') - self.handsplayers[pname]['street0_3BChance'] = self.handsplayers[pname]['street0_3BChance'] or bet_level == 2 - self.handsplayers[pname]['street0_4BChance'] = bet_level == 3 - self.handsplayers[pname]['street0_3BDone'] = self.handsplayers[pname]['street0_3BDone'] or (aggr and self.handsplayers[pname]['street0_3BChance']) - self.handsplayers[pname]['street0_4BDone'] = aggr and (self.handsplayers[pname]['street0_4BChance']) - if aggr: - bet_level += 1 - + pname, act, aggr = action[0], action[1], action[1] in ('raises', 'bets') + if bet_level == 1: + if aggr: + first_agressor = pname + bet_level += 1 + continue + elif bet_level == 2: + self.handsplayers[pname]['street0_3BChance'] = True + self.handsplayers[pname]['street0_SqueezeChance'] = squeeze_chance + if not squeeze_chance and act == 'calls': + squeeze_chance = 1 + continue + if aggr: + self.handsplayers[pname]['street0_3BDone'] = True + self.handsplayers[pname]['street0_SqueezeDone'] = squeeze_chance + second_agressor = pname + bet_level += 1 + continue + elif bet_level == 3: + if pname == first_agressor: + self.handsplayers[pname]['street0_4BChance'] = True + self.handsplayers[pname]['street0_FoldTo3BChance'] = True + if aggr: + self.handsplayers[pname]['street0_4BDone'] = True + bet_level += 1 + elif act == 'folds': + self.handsplayers[pname]['street0_FoldTo3BDone'] = True + break + else: + self.handsplayers[pname]['street0_C4BChance'] = True + if aggr: + self.handsplayers[pname]['street0_C4BDone'] = True + bet_level += 1 + continue + elif bet_level == 4: + if pname == second_agressor: + self.handsplayers[pname]['street0_FoldTo4BChance'] = True + if act == 'folds': + self.handsplayers[pname]['street0_FoldTo4BDone'] = True + break def calcCBets(self, hand): """Fill streetXCBChance, streetXCBDone, foldToStreetXCBDone, foldToStreetXCBChance diff --git a/pyfpdb/EverestToFpdb.py b/pyfpdb/EverestToFpdb.py new file mode 100644 index 00000000..f910ba2f --- /dev/null +++ b/pyfpdb/EverestToFpdb.py @@ -0,0 +1,254 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2011, Carl Gherardi +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# 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 General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +######################################################################## + +import L10n +_ = L10n.get_translation() + +import sys +import logging +from HandHistoryConverter import * +from decimal import Decimal + + +class Everest(HandHistoryConverter): + sitename = "Everest" + filetype = "text" + codepage = "utf8" + siteID = 15 + + substitutions = { + 'LS' : u"\$|\xe2\x82\xac|\u20ac|", + 'TAB' : u"-\u2013'\s\da-zA-Z", # legal characters for tablename + } + + # Static regexes + re_SplitHands = re.compile(r'\n+(?=)') + re_GameInfo = re.compile(u""" + """ % substitutions, re.VERBOSE|re.MULTILINE) + re_HandInfo = re.compile(r'[0-9]+)"\/>') + re_PlayerInfo = re.compile(r'', re.MULTILINE) + re_Board = re.compile(r'(?P.+)<\/COMMUNITY>', re.MULTILINE) + + # The following are also static regexes: there is no need to call + # compilePlayerRegexes (which does nothing), since players are identified + # not by name but by seat number + re_PostXB = re.compile(r'', re.MULTILINE) + #re_Antes = ??? + #re_BringIn = ??? + re_HeroCards = re.compile(r'FOLD|BET) position="(?P[0-9])"( amount="(?P[.0-9]+)")?\/>', re.MULTILINE) + re_ShowdownAction = re.compile(r'', re.MULTILINE) + re_CollectPot = re.compile(r'', re.MULTILINE) + re_ShownCards = re.compile(r'', re.MULTILINE) + + def compilePlayerRegexs(self, hand): + pass + + def playerNameFromSeatNo(self, seatNo, hand): + # Actions recorded by seat number, not by the player's name + for p in hand.players: + if p[0] == seatNo: + return p[1] + + def readSupportedGames(self): + return [ + ["ring", "hold", "nl"], + ["ring", "hold", "pl"], + #["tour", "hold", "nl"] + ] + + def determineGameType(self, handText): + m = self.re_GameInfo.search(handText) + m2 = self.re_HandInfo.search(handText) + if not m: + # Information about the game type appears only at the beginning of + # a hand history file; hence it is not supplied with the second + # and subsequent hands. In these cases we use the value previously + # stored. + try: + self.info + return self.info + except AttributeError: + tmp = handText[0:100] + log.error(_("determineGameType: Unable to recognise gametype from: '%s'") % tmp) + log.error(_("determineGameType: Raising FpdbParseError")) + raise FpdbParseError(_("Unable to recognise gametype from: '%s'") % tmp) + + if not m2: + tmp = handText[0:100] + raise FpdbParseError(_("Unable to recognise handinfo from: '%s'") % tmp) + + self.info = {} + mg = m.groupdict() + mg.update(m2.groupdict()) + print "DEBUG: mg: %s" % mg + + limits = { 'No Limit':'nl', 'No Limit ':'nl', 'Limit':'fl', 'pot-limit':'pl' } + games = { # base, category + 'Holdem' : ('hold','holdem'), + 'Holdem Tournament' : ('hold','holdem'), + 'omaha-hi' : ('hold','omahahi'), + } + + if 'LIMIT' in mg: + self.info['limitType'] = limits[mg['LIMIT']] + if 'GAME' in mg: + (self.info['base'], self.info['category']) = games[mg['GAME']] + if 'SB' in mg: + self.info['sb'] = mg['SB'] + if 'BB' in mg: + self.info['bb'] = mg['BB'] + + self.info['type'] = 'ring' + if mg['CURRENCY'] == u'\u20ac': + self.info['currency'] = 'EUR' + + # HACK - tablename not in every hand. + self.info['TABLENAME'] = mg['TABLE'] + + print "DEBUG: self.info: %s" % self.info + + return self.info + + def readHandInfo(self, hand): + m = self.re_HandInfo.search(hand.handText) + if m is None: + logging.info(_("Didn't match re_HandInfo")) + logging.info(hand.handText) + raise FpdbParseError(_("No match in readHandInfo.")) + hand.handid = m.group('HID') + hand.tablename = self.info['TABLENAME'] + hand.maxseats = None + #FIXME: u'DATETIME': u'1291155932' + hand.startTime = datetime.datetime.strptime('201102091158', '%Y%m%d%H%M') + #hand.startTime = datetime.datetime.strptime(m.group('DATETIME')[:12], '%Y%m%d%H%M') + + def readPlayerStacks(self, hand): + m = self.re_PlayerInfo.finditer(hand.handText) + for a in m: + hand.addPlayer(a.group('SEAT'), a.group('PNAME'), a.group('CASH')) + + def markStreets(self, hand): + #if hand.gametype['base'] == 'hold': + + m = re.search(r".+?(?=)|.+)" + r"((?P\S\S, \S\S, \S\S<\/COMMUNITY>.+?(?=)|.+))?" + r"((?P\S\S<\/COMMUNITY>.+?(?=)|.+))?" + r"((?P\S\S<\/COMMUNITY>.+))?", hand.handText,re.DOTALL) + #import pprint + #pp = pprint.PrettyPrinter(indent=4) + #pp.pprint(m.groupdict()) + hand.addStreets(m) + + def readCommunityCards(self, hand, street): + m = self.re_Board.search(hand.streets[street]) + if street == 'FLOP': + hand.setCommunityCards(street, m.group('CARDS').split(',')) + elif street in ('TURN','RIVER'): + hand.setCommunityCards(street, [m.group('CARDS').split(',')[-1]]) + + def readAntes(self, hand): + pass # ??? + + def readBringIn(self, hand): + pass # ??? + + def readBlinds(self, hand): + for a in self.re_PostXB.finditer(hand.handText): + amount = "%.2f" % float(int(a.group('XB'))/100) + print "DEBUG: readBlinds amount: %s" % amount + if Decimal(a.group('XB'))/100 == Decimal(self.info['sb']): + hand.addBlind(self.playerNameFromSeatNo(a.group('PSEAT'), hand),'small blind', amount) + elif Decimal(a.group('XB'))/100 == Decimal(self.info['bb']): + hand.addBlind(self.playerNameFromSeatNo(a.group('PSEAT'), hand),'big blind', amount) + + def readButton(self, hand): + hand.buttonpos = int(self.re_Button.search(hand.handText).group('BUTTON')) + + def readHeroCards(self, hand): + m = self.re_HeroCards.search(hand.handText) + if m: + hand.hero = self.playerNameFromSeatNo(m.group('PSEAT'), hand) + cards = m.group('CARDS').split(',') + hand.addHoleCards('PREFLOP', hand.hero, closed=cards, shown=False, + mucked=False, dealt=True) + + def readAction(self, hand, street): + print "DEBUG: readAction (%s)" % street + m = self.re_Action.finditer(hand.streets[street]) + curr_pot = Decimal('0') + for action in m: + print " DEBUG: %s %s" % (action.group('ATYPE'), action.groupdict()) + player = self.playerNameFromSeatNo(action.group('PSEAT'), hand) + if action.group('ATYPE') == 'BET': + amount = Decimal(action.group('BET')) + amountstr = "%.2f" % float(int(action.group('BET'))/100) + #Gah! BET can mean check, bet, call or raise... + if amount > 0 and curr_pot == 0: + # Open + curr_pot = amount + hand.addBet(street, player, amountstr) + elif Decimal(action.group('BET')) > 0 and curr_pot > 0: + # Raise or call + if amount > curr_pot: + # Raise + curr_pot = amount + hand.addCallandRaise(street, player, amountstr) + elif amount <= curr_pot: + # Call + hand.addCall(street, player, amountstr) + if action.group('BET') == '0': + hand.addCheck(street, player) + elif action.group('ATYPE') in ('FOLD', 'SIT_OUT'): + hand.addFold(street, player) + else: + print (_("Unimplemented readAction: %s %s" % (action.group('PSEAT'),action.group('ATYPE'),))) + logging.debug(_("Unimplemented readAction: %s %s" + % (action.group('PSEAT'),action.group('ATYPE'),))) + + def readShowdownActions(self, hand): + for shows in self.re_ShowdownAction.finditer(hand.handText): + cards = shows.group('CARDS').split(',') + hand.addShownCards(cards, + self.playerNameFromSeatNo(shows.group('PSEAT'), + hand)) + + def readCollectPot(self, hand): + for m in self.re_CollectPot.finditer(hand.handText): + player = self.playerNameFromSeatNo(m.group('PSEAT'), hand) + print "DEBUG: %s collects %s" % (player, m.group('POT')) + hand.addCollectPot(player, str(int(m.group('POT'))/100)) + + def readShownCards(self, hand): + for m in self.re_ShownCards.finditer(hand.handText): + cards = m.group('CARDS').split(',') + hand.addShownCards(cards=cards, player=self.playerNameFromSeatNo(m.group('PSEAT'), hand)) + diff --git a/pyfpdb/Filters.py b/pyfpdb/Filters.py index a4660eb2..f19774c7 100644 --- a/pyfpdb/Filters.py +++ b/pyfpdb/Filters.py @@ -57,7 +57,7 @@ class Filters(threading.Thread): ,'groupstitle':_('Grouping:'), 'posnshow':_('Show Position Stats') ,'datestitle':_('Date:') ,'groupsall':_('All Players') - ,'limitsFL':'FL', 'limitsNL':'NL', 'limitsPL':'PL', 'ring':_('Ring'), 'tour':_('Tourney') + ,'limitsFL':'FL', 'limitsNL':'NL', 'limitsPL':'PL', 'limitsCN':'CAP', 'ring':_('Ring'), 'tour':_('Tourney') } gen = self.conf.get_general_params() @@ -66,10 +66,20 @@ class Filters(threading.Thread): if 'day_start' in gen: self.day_start = float(gen['day_start']) + + self.sw = gtk.ScrolledWindow() + self.sw.set_border_width(0) + self.sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + self.sw.set_size_request(370, 300) + + # Outer Packing box self.mainVBox = gtk.VBox(False, 0) + self.sw.add_with_viewport(self.mainVBox) + self.sw.show() + print _("DEBUG: New packing box created!") - self.found = {'nl':False, 'fl':False, 'pl':False, 'ring':False, 'tour':False} + self.found = {'nl':False, 'fl':False, 'pl':False, 'cn':False, 'ring':False, 'tour':False} self.label = {} self.callback = {} @@ -144,10 +154,13 @@ class Filters(threading.Thread): self.cbFL = None self.cbNL = None self.cbPL = None + self.cbCN = None self.rb = {} # radio buttons for ring/tour self.type = None # ring/tour self.types = {} # list of all ring/tour values + self.num_limit_types = 0 + self.num_limit_types = 0 self.fillLimitsFrame(vbox, self.display) limitsFrame.add(vbox) @@ -245,7 +258,7 @@ class Filters(threading.Thread): def get_vbox(self): """returns the vbox of this thread""" - return self.mainVBox + return self.sw #end def get_vbox def getNumHands(self): @@ -419,13 +432,16 @@ class Filters(threading.Thread): #print "__set_limit_select: limit =", limit, w.get_active() self.limits[limit] = w.get_active() log.debug(_("self.limit[%s] set to %s") %(limit, self.limits[limit])) - if limit.isdigit() or (len(limit) > 2 and (limit[-2:] == 'nl' or limit[-2:] == 'fl' or limit[-2:] == 'pl')): + if limit.isdigit() or (len(limit) > 2 and (limit[-2:] == 'nl' or limit[-2:] == 'fl' or limit[-2:] == 'pl' or limit[-2:] == 'cn')): + # turning a leaf limit on with 'None' checked turns 'None' off if self.limits[limit]: if self.cbNoLimits is not None: self.cbNoLimits.set_active(False) + # turning a leaf limit off with 'All' checked turns 'All' off else: if self.cbAllLimits is not None: self.cbAllLimits.set_active(False) + # turning off a leaf limit turns off the corresponding fl. nl, cn or pl if not self.limits[limit]: if limit.isdigit(): if self.cbFL is not None: @@ -433,29 +449,44 @@ class Filters(threading.Thread): elif (len(limit) > 2 and (limit[-2:] == 'nl')): if self.cbNL is not None: self.cbNL.set_active(False) + elif (len(limit) > 2 and (limit[-2:] == 'cn')): + if self.cbCN is not None: + self.cbCN.set_active(False) else: if self.cbPL is not None: self.cbPL.set_active(False) elif limit == "all": if self.limits[limit]: - #for cb in self.cbLimits.values(): - # cb.set_active(True) - if self.cbFL is not None: - self.cbFL.set_active(True) - if self.cbNL is not None: - self.cbNL.set_active(True) - if self.cbPL is not None: - self.cbPL.set_active(True) + if self.num_limit_types == 1: + for cb in self.cbLimits.values(): + cb.set_active(True) + else: + if self.cbFL is not None: + self.cbFL.set_active(True) + if self.cbNL is not None: + self.cbNL.set_active(True) + if self.cbPL is not None: + self.cbPL.set_active(True) + if self.cbCN is not None: + self.cbCN.set_active(True) elif limit == "none": if self.limits[limit]: + if self.num_limit_types > 1: + if self.cbNL is not None: + self.cbNL.set_active(False) + if self.cbFL is not None: + self.cbFL.set_active(False) + if self.cbPL is not None: + self.cbPL.set_active(False) + if self.cbCN is not None: + self.cbCN.set_active(False) + # + # Finally, clean-up all individual limit checkboxes + # needed because the overall limit checkbox may + # not be set, or num_limit_types == 1 + # for cb in self.cbLimits.values(): - cb.set_active(False) - if self.cbNL is not None: - self.cbNL.set_active(False) - if self.cbFL is not None: - self.cbFL.set_active(False) - if self.cbPL is not None: - self.cbPL.set_active(False) + cb.set_active(False) elif limit == "fl": if not self.limits[limit]: # only toggle all fl limits off if they are all currently on @@ -535,6 +566,29 @@ class Filters(threading.Thread): elif self.type == 'tour': if 'ring' in self.rb: self.rb['ring'].set_active(True) + elif limit == "cn": + if not self.limits[limit]: + all_cn_on = True + for cb in self.cbLimits.values(): + t = cb.get_children()[0].get_text() + if "cn" in t and len(t) > 2: + if not cb.get_active(): + all_cn_on = False + found = {'ring':False, 'tour':False} + for cb in self.cbLimits.values(): + t = cb.get_children()[0].get_text() + if "cn" in t and len(t) > 2: + if self.limits[limit] or all_cn_on: + cb.set_active(self.limits[limit]) + found[self.types[t]] = True + if self.limits[limit]: + if not found[self.type]: + if self.type == 'ring': + if 'tour' in self.rb: + self.rb['tour'].set_active(True) + elif self.type == 'tour': + if 'ring' in self.rb: + self.rb['ring'].set_active(True) elif limit == "ring": log.debug("set", limit, "to", self.limits[limit]) if self.limits[limit]: @@ -708,15 +762,16 @@ class Filters(threading.Thread): showb = gtk.Button(label="hide", stock=None, use_underline=True) showb.set_alignment(xalign=1.0, yalign=0.5) showb.connect('clicked', self.__toggle_box, 'limits') + top_hbox.pack_start(showb, expand=False, padding=1) - vbox1 = gtk.VBox(False, 0) + vbox1 = gtk.VBox(False, 15) vbox.pack_start(vbox1, False, False, 0) self.boxes['limits'] = vbox1 self.cursor.execute(self.sql.query['getCashLimits']) # selects limitType, bigBlind result = self.db.cursor.fetchall() - self.found = {'nl':False, 'fl':False, 'pl':False, 'ring':False, 'tour':False} + self.found = {'nl':False, 'fl':False, 'pl':False, 'cn':False, 'ring':False, 'tour':False} if len(result) >= 1: hbox = gtk.HBox(True, 0) @@ -741,6 +796,9 @@ class Filters(threading.Thread): elif line[1] == 'pl': name = str(line[2])+line[1] self.found['pl'] = True + elif line[1] == 'cn': + name = str(line[2])+line[1] + self.found['cn'] = True else: name = str(line[2])+line[1] self.found['nl'] = True @@ -765,11 +823,12 @@ class Filters(threading.Thread): dest = vbox3 # for ring/tour buttons if "LimitType" in display and display["LimitType"] == True: - num_limit_types = 0 - if self.found['fl']: num_limit_types = num_limit_types + 1 - if self.found['pl']: num_limit_types = num_limit_types + 1 - if self.found['nl']: num_limit_types = num_limit_types + 1 - if num_limit_types > 1: + self.num_limit_types = 0 + if self.found['fl']: self.num_limit_types = self.num_limit_types + 1 + if self.found['pl']: self.num_limit_types = self.num_limit_types + 1 + if self.found['nl']: self.num_limit_types = self.num_limit_types + 1 + if self.found['cn']: self.num_limit_types = self.num_limit_types + 1 + if self.num_limit_types > 1: if self.found['fl']: hbox = gtk.HBox(False, 0) vbox3.pack_start(hbox, False, False, 0) @@ -782,6 +841,10 @@ class Filters(threading.Thread): hbox = gtk.HBox(False, 0) vbox3.pack_start(hbox, False, False, 0) self.cbPL = self.createLimitLine(hbox, 'pl', self.filterText['limitsPL']) + if self.found['cn']: + hbox = gtk.HBox(False, 0) + vbox3.pack_start(hbox, False, False, 0) + self.cbCN = self.createLimitLine(hbox, 'cn', self.filterText['limitsCN']) dest = vbox2 # for ring/tour buttons else: print _("INFO: No games returned from database") @@ -807,19 +870,24 @@ class Filters(threading.Thread): def fillGraphOpsFrame(self, vbox): top_hbox = gtk.HBox(False, 0) vbox.pack_start(top_hbox, False, False, 0) - title = gtk.Label("Graphing Options:") + title = gtk.Label(_("Graphing Options:")) title.set_alignment(xalign=0.0, yalign=0.5) top_hbox.pack_start(title, expand=True, padding=3) showb = gtk.Button(label="hide", stock=None, use_underline=True) showb.set_alignment(xalign=1.0, yalign=0.5) - showb.connect('clicked', self.__toggle_box, 'games') + showb.connect('clicked', self.__toggle_box, 'graphops') top_hbox.pack_start(showb, expand=False, padding=1) + vbox1 = gtk.VBox(False, 0) + vbox.pack_start(vbox1, False, False, 0) + vbox1.show() + self.boxes['graphops'] = vbox1 + hbox1 = gtk.HBox(False, 0) - vbox.pack_start(hbox1, False, False, 0) + vbox1.pack_start(hbox1, False, False, 0) hbox1.show() - label = gtk.Label("Show Graph In:") + label = gtk.Label(_("Show Graph In:")) label.set_alignment(xalign=0.0, yalign=0.5) hbox1.pack_start(label, True, True, 0) label.show() @@ -835,11 +903,7 @@ class Filters(threading.Thread): button.connect("toggled", self.__set_displayin_select, "BB") button.show() - vbox1 = gtk.VBox(False, 0) - vbox.pack_start(vbox1, False, False, 0) - vbox1.show() - - button = gtk.CheckButton("Showdown Winnings", False) + button = gtk.CheckButton(_("Showdown Winnings"), False) vbox1.pack_start(button, True, True, 0) # wouldn't it be awesome if there was a way to remember the state of things like # this and be able to set it to what it was last time? @@ -847,7 +911,7 @@ class Filters(threading.Thread): button.connect("toggled", self.__set_graphopscheck_select, "showdown") button.show() - button = gtk.CheckButton("Non-Showdown Winnings", False) + button = gtk.CheckButton(_("Non-Showdown Winnings"), False) vbox1.pack_start(button, True, True, 0) # ditto as 8 lines up :) #button.set_active(True) @@ -1092,3 +1156,4 @@ def main(argv=None): if __name__ == '__main__': sys.exit(main()) + diff --git a/pyfpdb/FullTiltPokerSummary.py b/pyfpdb/FullTiltPokerSummary.py index 79f50f01..2edcd81e 100644 --- a/pyfpdb/FullTiltPokerSummary.py +++ b/pyfpdb/FullTiltPokerSummary.py @@ -80,6 +80,7 @@ class FullTiltPokerSummary(TourneySummary): re_Currency = re.compile(u"""(?P[%(LS)s]|FPP)""" % substitutions) re_Player = re.compile(u"""(?P[\d]+):\s(?P[^,\r\n]{2,15})(,(\s)?[%(LS)s](?P[.\d]+))?""") + re_Finished = re.compile(u"""(?P[^,\r\n]{2,15}) finished in (?P[\d]+)\S\S place""") re_DateTime = re.compile("\[(?P[0-9]{4})\/(?P[0-9]{2})\/(?P[0-9]{2})[\- ]+(?P[0-9]+):(?P[0-9]+):(?P[0-9]+)") @@ -126,6 +127,7 @@ class FullTiltPokerSummary(TourneySummary): elif mg['CURRENCY'] == "FPP": self.currency="PSFP" m = self.re_Player.finditer(self.summaryText) + playercount = 0 for a in m: mg = a.groupdict() #print "DEBUG: a.groupdict(): %s" % mg @@ -136,4 +138,14 @@ class FullTiltPokerSummary(TourneySummary): if 'WINNINGS' in mg and mg['WINNINGS'] != None: winnings = int(100*Decimal(mg['WINNINGS'])) self.addPlayer(rank, name, winnings, self.currency, None, None, None) + playercount += 1 + # Some files dont contain the normals lines, and only contain the line + # finished in XXXXrd place + if playercount == 0: + m = self.re_Finished.finditer(self.summaryText) + for a in m: + winnings = 0 + name = a.group('NAME') + rank = a.group('RANK') + self.addPlayer(rank, name, winnings, self.currency, None, None, None) diff --git a/pyfpdb/FulltiltToFpdb.py b/pyfpdb/FulltiltToFpdb.py index d4d59473..20980de1 100755 --- a/pyfpdb/FulltiltToFpdb.py +++ b/pyfpdb/FulltiltToFpdb.py @@ -49,6 +49,7 @@ class Fulltilt(HandHistoryConverter): '6.00': ('1.00', '3.00'), '6': ('1.00', '3.00'), '8.00': ('2.00', '4.00'), '8': ('2.00', '4.00'), '10.00': ('2.00', '5.00'), '10': ('2.00', '5.00'), + '16.00': ('4.00', '8.00'), '16': ('4.00', '8.00'), '20.00': ('5.00', '10.00'), '20': ('5.00', '10.00'), '30.00': ('10.00', '15.00'), '30': ('10.00', '15.00'), '40.00': ('10.00', '20.00'), '40': ('10.00', '20.00'), @@ -77,17 +78,18 @@ class Fulltilt(HandHistoryConverter): ''' % substitutions, re.VERBOSE) re_SplitHands = re.compile(r"\n\n\n+") re_TailSplitHands = re.compile(r"(\n\n+)") - re_HandInfo = re.compile(r'''.*\#(?P[0-9]+):\s + re_HandInfo = re.compile(u'''.*\#(?P[0-9]+):\s (?:(?P.+)\s\((?P\d+)\),\s)? - (Table|Match)\s + ((Table|Match)\s)? (?PPlay\sChip\s|PC)? - (?P[%(TAB)s]+)\s + ((?P
[%(TAB)s]+)(\s|,)) + (?P\sEntry\s\#\d+\s)? (\((?P.+)\)\s)?-\s [%(LS)s]?(?P[%(NUM)s]+)/[%(LS)s]?(?P[%(NUM)s]+)\s(Ante\s[%(LS)s]?(?P[.0-9]+)\s)?-\s [%(LS)s]?(?P[.0-9]+\sCap\s)? (?P[-\da-zA-Z\/\'\s]+)\s-\s (?P.*$) - (?P\(partial\))?\n + (?P\(partial\))?\s (?:.*?\n(?PHand\s\#(?P=HID)\shas\sbeen\scanceled))? ''' % substitutions, re.MULTILINE|re.VERBOSE) re_TourneyExtraInfo = re.compile('''(((?P[^$]+)? @@ -99,7 +101,7 @@ class Fulltilt(HandHistoryConverter): ''' % substitutions, re.VERBOSE) re_Button = re.compile('^The button is in seat #(?P+ + + @@ -525,6 +528,18 @@ Left-Drag to Move" + + + + + + + + + + + + @@ -599,6 +614,42 @@ Left-Drag to Move" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -707,8 +758,14 @@ Left-Drag to Move" - + + + + + + + @@ -789,6 +846,7 @@ Left-Drag to Move" + diff --git a/pyfpdb/HUD_main.pyw b/pyfpdb/HUD_main.pyw index 4a8cbc0d..04476dae 100755 --- a/pyfpdb/HUD_main.pyw +++ b/pyfpdb/HUD_main.pyw @@ -23,6 +23,9 @@ Main for FreePokerTools HUD. """ +import L10n +_ = L10n.get_translation() + # Standard Library modules import sys import os @@ -51,21 +54,6 @@ elif sys.platform == 'darwin': else: # This is bad--figure out the values for the various windows flavors import WinTables as Tables -import locale -lang = locale.getdefaultlocale()[0][0:2] -print "lang:", lang -if lang == "en": - def _(string): - return string -else: - import gettext - try: - trans = gettext.translation("fpdb", localedir="locale", languages=[lang]) - trans.install() - except IOError: - def _(string): - return string - # get config and set up logger c = Configuration.Config(file=options.config, dbname=options.dbname) log = Configuration.get_logger("logging.conf", "hud", log_dir=c.dir_log, log_file='HUD-log.txt') @@ -128,7 +116,7 @@ class HUD_main(object): self.main_window.set_icon_stock(gtk.STOCK_HOME) if not options.hidden: self.main_window.show_all() -# gobject.timeout_add(100, self.check_tables) + gobject.timeout_add(800, self.check_tables) except: log.exception("Error initializing main_window") diff --git a/pyfpdb/Hand.py b/pyfpdb/Hand.py index 8c2d6bbe..727ca00d 100644 --- a/pyfpdb/Hand.py +++ b/pyfpdb/Hand.py @@ -57,6 +57,7 @@ class Hand(object): #log.debug( _("Hand.init(): handText is ") + str(handText) ) self.config = config self.saveActions = self.config.get_import_parameters().get('saveActions') + self.cacheSessions = self.config.get_import_parameters().get("cacheSessions") #log = Configuration.get_logger("logging.conf", "db", log_dir=self.config.dir_log) self.sitename = sitename self.siteId = self.config.get_site_id(sitename) @@ -258,7 +259,7 @@ dealt whether they were seen in a 'dealt to' line db.commit() #end def prepInsert - def insert(self, db, printtest = False): + def insert(self, db, hp_data = None, ha_data = None, insert_data=False, printtest = False): """ Function to insert Hand into database Should not commit, and do minimal selects. Callers may want to cache commits db: a connected Database object""" @@ -276,17 +277,26 @@ db: a connected Database object""" hh['gametypeId'] = self.dbid_gt # seats TINYINT NOT NULL, hh['seats'] = len(self.dbid_pids) + + hp = self.stats.getHandsPlayers() + + if self.cacheSessions: + hh['sessionId'] = db.storeSessionsCache(self.dbid_pids, self.startTime, self.gametype, hp) self.dbid_hands = db.storeHand(hh, printdata = printtest) - self.dbid_hpid = db.storeHandsPlayers(self.dbid_hands, self.dbid_pids, - self.stats.getHandsPlayers(), printdata = printtest) + + hp_inserts = db.storeHandsPlayers(self.dbid_hands, self.dbid_pids, hp, + insert=insert_data, hp_bulk = hp_data, printdata = printtest) + if self.saveActions: - db.storeHandsActions(self.dbid_hands, self.dbid_pids, self.dbid_hpid, - self.stats.getHandsActions(), printdata = printtest) + ha_inserts = db.storeHandsActions(self.dbid_hands, self.dbid_pids, self.stats.getHandsActions(), + insert=insert_data, ha_bulk = ha_data, printdata = printtest) else: log.info(_("Hand.insert(): hid #: %s is a duplicate") % hh['siteHandNo']) self.is_duplicate = True # i.e. don't update hudcache raise FpdbHandDuplicate(hh['siteHandNo']) + + return hp_inserts, ha_inserts def updateHudCache(self, db): db.storeHudCache(self.dbid_gt, self.dbid_pids, self.startTime, self.stats.getHandsPlayers()) @@ -349,15 +359,13 @@ db: a connected Database object""" # Need to find MySQL and Postgres equivalents # MySQL maybe: cursorclass=MySQLdb.cursors.DictCursor res = c.fetchone() + + #res['tourneyId'] #res['seats'] #res['rush'] self.tablename = res['tableName'] self.handid = res['siteHandNo'] self.startTime = datetime.datetime.strptime(res['startTime'], "%Y-%m-%d %H:%M:%S+00:00") - #res['tourneyId'] - #gametypeId - #res['importTime'] # Don't really care about this - #res['seats'] self.maxseats = res['maxSeats'] - #res['rush'] + cards = map(Card.valueSuitFromCard, [res['boardcard1'], res['boardcard2'], res['boardcard3'], res['boardcard4'], res['boardcard5']]) #print "DEBUG: res['boardcard1']: %s" % res['boardcard1'] #print "DEBUG: cards: %s" % cards @@ -437,7 +445,7 @@ chips (string) the chips the player has at the start of the hand (can be None) If a player has None chips he won't be added.""" log.debug("addPlayer: %s %s (%s)" % (seat, name, chips)) if chips is not None: - chips = re.sub(u',', u'', chips) #some sites have commas + chips = chips.replace(u',', u'') #some sites have commas self.players.append([seat, name, chips]) self.stacks[name] = Decimal(chips) self.pot.addPlayer(name) @@ -479,10 +487,10 @@ If a player has None chips he won't be added.""" For sites (currently only Carbon Poker) which record "all in" as a special action, which can mean either "calls and is all in" or "raises all in". """ self.checkPlayerExists(player) - amount = re.sub(u',', u'', amount) #some sites have commas + amount = amount.replace(u',', u'') #some sites have commas Ai = Decimal(amount) Bp = self.lastBet[street] - Bc = reduce(operator.add, self.bets[street][player], 0) + Bc = sum(self.bets[street][player]) C = Bp - Bc if Ai <= C: self.addCall(street, player, amount) @@ -495,13 +503,14 @@ For sites (currently only Carbon Poker) which record "all in" as a special actio def addAnte(self, player, ante): log.debug("%s %s antes %s" % ('BLINDSANTES', player, ante)) if player is not None: - ante = re.sub(u',', u'', ante) #some sites have commas - self.bets['BLINDSANTES'][player].append(Decimal(ante)) - self.stacks[player] -= Decimal(ante) - act = (player, 'ante', Decimal(ante), self.stacks[player]==0) + ante = ante.replace(u',', u'') #some sites have commas + ante = Decimal(ante) + self.bets['BLINDSANTES'][player].append(ante) + self.stacks[player] -= ante + act = (player, 'ante', ante, self.stacks[player]==0) self.actions['BLINDSANTES'].append(act) -# self.pot.addMoney(player, Decimal(ante)) - self.pot.addCommonMoney(player, Decimal(ante)) +# self.pot.addMoney(player, ante) + self.pot.addCommonMoney(player, ante) #I think the antes should be common money, don't have enough hand history to check def addBlind(self, player, blindtype, amount): @@ -514,22 +523,25 @@ For sites (currently only Carbon Poker) which record "all in" as a special actio # log.debug("addBlind: %s posts %s, %s" % (player, blindtype, amount)) if player is not None: - amount = re.sub(u',', u'', amount) #some sites have commas - self.stacks[player] -= Decimal(amount) - act = (player, blindtype, Decimal(amount), self.stacks[player]==0) + amount = amount.replace(u',', u'') #some sites have commas + amount = Decimal(amount) + self.stacks[player] -= amount + act = (player, blindtype, amount, self.stacks[player]==0) self.actions['BLINDSANTES'].append(act) if blindtype == 'both': # work with the real amount. limit games are listed as $1, $2, where # the SB 0.50 and the BB is $1, after the turn the minimum bet amount is $2.... - amount = self.bb - self.bets['BLINDSANTES'][player].append(Decimal(self.sb)) - self.pot.addCommonMoney(player, Decimal(self.sb)) + amount = Decimal(self.bb) + sb = Decimal(self.sb) + self.bets['BLINDSANTES'][player].append(sb) + self.pot.addCommonMoney(player, sb) if blindtype == 'secondsb': amount = Decimal(0) - self.bets['BLINDSANTES'][player].append(Decimal(self.sb)) - self.pot.addCommonMoney(player, Decimal(self.sb)) + sb = Decimal(self.sb) + self.bets['BLINDSANTES'][player].append(sb) + self.pot.addCommonMoney(player, sb) street = 'BLAH' @@ -538,27 +550,28 @@ For sites (currently only Carbon Poker) which record "all in" as a special actio elif self.gametype['base'] == 'draw': street = 'DEAL' - self.bets[street][player].append(Decimal(amount)) - self.pot.addMoney(player, Decimal(amount)) - self.lastBet[street] = Decimal(amount) + self.bets[street][player].append(amount) + self.pot.addMoney(player, amount) + self.lastBet[street] = amount self.posted = self.posted + [[player,blindtype]] def addCall(self, street, player=None, amount=None): if amount: - amount = re.sub(u',', u'', amount) #some sites have commas + amount = amount.replace(u',', u'') #some sites have commas log.debug(_("%s %s calls %s") %(street, player, amount)) # Potentially calculate the amount of the call if not supplied # corner cases include if player would be all in if amount is not None: - self.bets[street][player].append(Decimal(amount)) - #self.lastBet[street] = Decimal(amount) - self.stacks[player] -= Decimal(amount) + amount = Decimal(amount) + self.bets[street][player].append(amount) + #self.lastBet[street] = amount + self.stacks[player] -= amount #print "DEBUG %s calls %s, stack %s" % (player, amount, self.stacks[player]) - act = (player, 'calls', Decimal(amount), self.stacks[player]==0) + act = (player, 'calls', amount, self.stacks[player] == 0) self.actions[street].append(act) - self.pot.addMoney(player, Decimal(amount)) + self.pot.addMoney(player, amount) def addRaiseBy(self, street, player, amountBy): """\ @@ -575,11 +588,11 @@ Add a raise by amountBy on [street] by [player] # then: C = Bp - Bc (amount to call) # Rt = Bp + Rb (raise to) # - amountBy = re.sub(u',', u'', amountBy) #some sites have commas + amountBy = amountBy.replace(u',', u'') #some sites have commas self.checkPlayerExists(player) Rb = Decimal(amountBy) Bp = self.lastBet[street] - Bc = reduce(operator.add, self.bets[street][player], 0) + Bc = sum(self.bets[street][player]) C = Bp - Bc Rt = Bp + Rb @@ -593,10 +606,10 @@ Add a raise by amountBy on [street] by [player] """\ For sites which by "raises x" mean "calls and raises putting a total of x in the por". """ self.checkPlayerExists(player) - amount = re.sub(u',', u'', amount) #some sites have commas + amount = amount.replace(u',', u'') #some sites have commas CRb = Decimal(amount) Bp = self.lastBet[street] - Bc = reduce(operator.add, self.bets[street][player], 0) + Bc = sum(self.bets[street][player]) C = Bp - Bc Rb = CRb - C Rt = Bp + Rb @@ -609,9 +622,9 @@ Add a raise on [street] by [player] to [amountTo] """ #CG - No idea if this function has been test/verified self.checkPlayerExists(player) - amountTo = re.sub(u',', u'', amountTo) #some sites have commas + amountTo = amountTo.replace(u',', u'') #some sites have commas Bp = self.lastBet[street] - Bc = reduce(operator.add, self.bets[street][player], 0) + Bc = sum(self.bets[street][player]) Rt = Decimal(amountTo) C = Bp - Bc Rb = Rt - C - Bc @@ -630,15 +643,16 @@ Add a raise on [street] by [player] to [amountTo] def addBet(self, street, player, amount): log.debug(_("%s %s bets %s") %(street, player, amount)) - amount = re.sub(u',', u'', amount) #some sites have commas + amount = amount.replace(u',', u'') #some sites have commas + amount = Decimal(amount) self.checkPlayerExists(player) - self.bets[street][player].append(Decimal(amount)) - self.stacks[player] -= Decimal(amount) + self.bets[street][player].append(amount) + self.stacks[player] -= amount #print "DEBUG %s bets %s, stack %s" % (player, amount, self.stacks[player]) - act = (player, 'bets', Decimal(amount), self.stacks[player]==0) + act = (player, 'bets', amount, self.stacks[player]==0) self.actions[street].append(act) - self.lastBet[street] = Decimal(amount) - self.pot.addMoney(player, Decimal(amount)) + self.lastBet[street] = amount + self.pot.addMoney(player, amount) def addStandsPat(self, street, player): @@ -1140,6 +1154,9 @@ class DrawHand(Hand): hhc.readPlayerStacks(self) hhc.compilePlayerRegexs(self) hhc.markStreets(self) + # markStreets in Draw may match without dealing cards + if self.streets['DEAL'] == None: + raise FpdbParseError(_("DrawHand.__init__: street 'DEAL' is empty. Hand cancelled? HandID: '%s'" % self.handid)) hhc.readBlinds(self) hhc.readAntes(self) hhc.readButton(self) @@ -1366,10 +1383,10 @@ closed likewise, but known only to player Add a complete on [street] by [player] to [amountTo] """ log.debug(_("%s %s completes %s") % (street, player, amountTo)) - amountTo = re.sub(u',', u'', amountTo) #some sites have commas + amountTo = amountTo.replace(u',', u'') #some sites have commas self.checkPlayerExists(player) Bp = self.lastBet['THIRD'] - Bc = reduce(operator.add, self.bets[street][player], 0) + Bc = sum(self.bets[street][player]) Rt = Decimal(amountTo) C = Bp - Bc Rb = Rt - C @@ -1384,12 +1401,14 @@ Add a complete on [street] by [player] to [amountTo] def addBringIn(self, player, bringin): if player is not None: log.debug(_("Bringin: %s, %s") % (player , bringin)) - self.bets['THIRD'][player].append(Decimal(bringin)) - self.stacks[player] -= Decimal(bringin) - act = (player, 'bringin', Decimal(bringin), self.stacks[player]==0) + bringin = bringin.replace(u',', u'') #some sites have commas + bringin = Decimal(bringin) + self.bets['THIRD'][player].append(bringin) + self.stacks[player] -= bringin + act = (player, 'bringin', bringin, self.stacks[player]==0) self.actions['THIRD'].append(act) - self.lastBet['THIRD'] = Decimal(bringin) - self.pot.addMoney(player, Decimal(bringin)) + self.lastBet['THIRD'] = bringin + self.pot.addMoney(player, bringin) def getStreetTotals(self): # street1Pot INT, /* pot size at flop/street4 */ diff --git a/pyfpdb/HandHistoryConverter.py b/pyfpdb/HandHistoryConverter.py index 03202c5a..09bb328c 100644 --- a/pyfpdb/HandHistoryConverter.py +++ b/pyfpdb/HandHistoryConverter.py @@ -264,7 +264,7 @@ which it expects to find at self.re_TailSplitHands -- see for e.g. Everleaf.py. if self.ftpArchive == True: log.debug(_("Converting ftpArchive format to readable")) # Remove ******************** # 1 ************************* - m = re.compile('\*{20}\s#\s\d+\s\*{25}\s+', re.MULTILINE) + m = re.compile('\*{20}\s#\s\d+\s\*{20,25}\s+', re.MULTILINE) self.obs = m.sub('', self.obs) if self.obs is None or self.obs == "": diff --git a/pyfpdb/Mucked.py b/pyfpdb/Mucked.py index e642c6db..e592a471 100644 --- a/pyfpdb/Mucked.py +++ b/pyfpdb/Mucked.py @@ -335,6 +335,9 @@ class Stud_cards: class Seat_Window(gtk.Window): """Subclass gtk.Window for the seat windows.""" + def __init__(self, aw = None): + super(Seat_Window, self).__init__() + self.aw = aw class Aux_Seats(Aux_Window): """A super class to display an aux_window at each seat.""" @@ -348,6 +351,8 @@ class Aux_Seats(Aux_Window): self.uses_timer = False # the Aux_seats object uses a timer to control hiding self.timer_on = False # bool = Ture if the timeout for removing the cards is on + self.aw_window_type = Seat_Window + # placeholders that should be overridden--so we don't throw errors def create_contents(self): pass def update_contents(self): pass @@ -382,10 +387,10 @@ class Aux_Seats(Aux_Window): (x, y) = self.params['layout'][self.hud.max].common else: (x, y) = loc[self.adj[i]] - self.m_windows[i] = Seat_Window() + self.m_windows[i] = self.aw_window_type(self) self.m_windows[i].set_decorated(False) self.m_windows[i].set_property("skip-taskbar-hint", True) - self.m_windows[i].set_transient_for(self.hud.main_window) + self.m_windows[i].set_transient_for(self.hud.main_window) # FIXME: shouldn't this be the table window?? self.m_windows[i].set_focus_on_map(False) self.m_windows[i].connect("configure_event", self.configure_event_cb, i) self.positions[i] = self.card_positions((x * width) / 1000, self.hud.table.x, (y * height) /1000, self.hud.table.y) diff --git a/pyfpdb/OSXTables.py b/pyfpdb/OSXTables.py index 8baa6aec..75d7218a 100644 --- a/pyfpdb/OSXTables.py +++ b/pyfpdb/OSXTables.py @@ -52,8 +52,9 @@ class Table(Table_Window): if re.search(self.search_string, d.get(kCGWindowName, ""), re.I): title = d[kCGWindowName] if self.check_bad_words(title): continue - self.number = d[kCGWindowNumber] + self.number = int(d[kCGWindowNumber]) self.title = title + return self.title if self.number is None: return None @@ -63,11 +64,11 @@ class Table(Table_Window): WinListDict = CGWindowListCreateDescriptionFromArray(WinList) for d in WinListDict: - if d[CGWindowNumber] == self.number: - return {'x' : d[kCGWindowBounds][X], - 'y' : d[kCGWindowBounds][Y], - 'width' : d[kCGWindowBounds][Width], - 'height' : d[kCGWindowBounds][Height] + if d[kCGWindowNumber] == self.number: + return {'x' : int(d[kCGWindowBounds]['X']), + 'y' : int(d[kCGWindowBounds]['Y']), + 'width' : int(d[kCGWindowBounds]['Width']), + 'height' : int(d[kCGWindowBounds]['Height']) } return None @@ -86,5 +87,5 @@ class Table(Table_Window): # the hud window was a dialog belonging to the table. # This is the gdkhandle for the HUD window - gdkwindow = gtk.gdk.window_foreign_new(window.number) - gdkwindow.set_transient_for(self.gdkhandle) + gdkwindow = gtk.gdk.window_foreign_new(window.window.xid) + gdkwindow.set_transient_for(window.window) diff --git a/pyfpdb/OnGameToFpdb.py b/pyfpdb/OnGameToFpdb.py index cacd288a..889f2d24 100755 --- a/pyfpdb/OnGameToFpdb.py +++ b/pyfpdb/OnGameToFpdb.py @@ -49,7 +49,7 @@ class OnGame(HandHistoryConverter): } currencies = { u'\u20ac':'EUR', u'\xe2\x82\xac':'EUR', '$':'USD', '':'T$' } - limits = { 'NO_LIMIT':'nl', 'LIMIT':'fl'} + limits = { 'NO_LIMIT':'nl', 'POT_LIMIT':'pl', 'LIMIT':'fl'} games = { # base, category "TEXAS_HOLDEM" : ('hold','holdem'), @@ -68,17 +68,19 @@ class OnGame(HandHistoryConverter): # ***** End of hand R5-75443872-57 ***** re_SplitHands = re.compile(u'\*\*\*\*\*\sEnd\sof\shand\s[-A-Z\d]+.*\n(?=\*)') + #TODO: detect play money + # "Play money" rather than "Real money" and set currency accordingly re_HandInfo = re.compile(u""" \*\*\*\*\*\sHistory\sfor\shand\s(?P[-A-Z\d]+).* Start\shand:\s(?P.*) - Table:\s(?P
[-\'\w\s]+)\s\[\d+\]\s\( + Table:\s(\[SPEED\]\s)?(?P
[-\'\w\s]+)\s\[\d+\]\s\( ( - (?PNO_LIMIT|Limit|LIMIT|Pot\sLimit)\s + (?PNO_LIMIT|Limit|LIMIT|Pot\sLimit|POT_LIMIT)\s (?PTEXAS_HOLDEM|OMAHA_HI|SEVEN_CARD_STUD|SEVEN_CARD_STUD_HI_LO|RAZZ|FIVE_CARD_DRAW)\s (?P%(LS)s|)?(?P[.0-9]+)/ (%(LS)s)?(?P[.0-9]+) )? - """ % substitutions, re.MULTILINE|re.DOTALL|re.VERBOSE) #TODO: detect play money (identified by "Play money" rather than "Real money" and set currency accordingly + """ % substitutions, re.MULTILINE|re.DOTALL|re.VERBOSE) re_TailSplitHands = re.compile(u'(\*\*\*\*\*\sEnd\sof\shand\s[-A-Z\d]+.*\n)(?=\*)') re_Button = re.compile('Button: seat (?P""") + + re_Details = re.compile(u"""

(?P

""") + re_Prizepool = re.compile(u"""
.+: (?P[0-9,]+)""") + + re_DateTime = re.compile("\[(?P[0-9]{4})\/(?P[0-9]{2})\/(?P[0-9]{2})[\- ]+(?P[0-9]+):(?P[0-9]+):(?P[0-9]+)") + + codepage = ["utf-8"] + + def parseSummary(self): + self.currency = "EUR" + soup = BeautifulSoup(self.summaryText) + tl = soup.findAll('div', {"class":"left_content"}) + + ps = soup.findAll('p', {"class": "text"}) + for p in ps: + for m in self.re_Details.finditer(str(p)): + mg = m.groupdict() + #print mg + if mg['LABEL'] == 'Buy-in': + mg['VALUE'] = mg['VALUE'].replace(u"€", "") + mg['VALUE'] = mg['VALUE'].replace(u"+", "") + mg['VALUE'] = mg['VALUE'].strip(" $") + bi, fee = mg['VALUE'].split(" ") + self.buyin = int(100*Decimal(bi)) + self.fee = int(100*Decimal(fee)) + #print "DEBUG: bi: '%s' fee: '%s" % (self.buyin, self.fee) + if mg['LABEL'] == 'Nombre de joueurs inscrits': + self.entries = mg['VALUE'] + if mg['LABEL'] == 'D\xc3\xa9but du tournoi': + self.startTime = datetime.datetime.strptime(mg['VALUE'], "%d-%m-%Y %H:%M") + if mg['LABEL'] == 'Nombre de joueurs max': + # Max seats i think + pass + + div = soup.findAll('div', {"class": "title2"}) + for m in self.re_Prizepool.finditer(str(div)): + mg = m.groupdict() + #print mg + self.prizepool = mg['PRIZEPOOL'].replace(u',','.') + + + for m in self.re_GameType.finditer(str(tl[0])): + mg = m.groupdict() + #print mg + self.gametype['limitType'] = self.limits[mg['LIMIT']] + self.gametype['category'] = self.games[mg['GAME']][1] + + for m in self.re_Player.finditer(str(tl[0])): + mg = m.groupdict() + #print mg + winnings = mg['WINNINGS'].strip(u'€').replace(u',','.') + winnings = int(100*Decimal(winnings)) + rank = mg['RANK'] + name = mg['PNAME'] + #print "DEBUG: %s: %s" %(name, winnings) + self.addPlayer(rank, name, winnings, self.currency, None, None, None) + + + for m in self.re_TourNo.finditer(self.summaryText): + mg = m.groupdict() + #print mg + self.tourNo = mg['TOURNO'] diff --git a/pyfpdb/WinamaxToFpdb.py b/pyfpdb/WinamaxToFpdb.py index c6b49c4c..6aa6f51c 100644 --- a/pyfpdb/WinamaxToFpdb.py +++ b/pyfpdb/WinamaxToFpdb.py @@ -89,7 +89,7 @@ class Winamax(HandHistoryConverter): buyIn:\s(?P(?P[%(LS)s\d\,]+)?\s\+?\s(?P[%(LS)s\d\,]+)?\+?(?P[%(LS)s\d\.]+)?\s?(?P%(LEGAL_ISO)s)?|Gratuit|Ticket\suniquement)?\s (level:\s(?P\d+))? .*)? - \s-\sHandId:\s\#(?P\d+)-(?P\d+)-(?P\d+).*\s + \s-\sHandId:\s\#(?P\d+)-(?P\d+)-(?P\d+).*\s # REB says: HID3 is the correct hand number (?PHoldem|Omaha)\s (?Pno\slimit|pot\slimit)\s \( @@ -107,6 +107,7 @@ class Winamax(HandHistoryConverter): re_TailSplitHands = re.compile(r'\n\s*\n') re_Button = re.compile(r'Seat\s#(?P
(?P.+?)<\/td>(?P.+?)