Merge branch 'master' of git://git.assembla.com/fpdboz.git

Conflicts:
	pyfpdb/HandHistoryConverter.py
This commit is contained in:
Eric Blade 2009-08-12 04:24:04 -05:00
commit f31f2e442e
17 changed files with 313 additions and 229 deletions

2
.gitignore vendored
View File

@ -1,2 +0,0 @@
*.pyc
*~

View File

@ -26,6 +26,11 @@ from HandHistoryConverter import *
class Betfair(HandHistoryConverter): class Betfair(HandHistoryConverter):
sitename = 'Betfair'
filetype = "text"
codepage = "cp1252"
siteId = 7 # Needs to match id entry in Sites database
# Static regexes # Static regexes
re_GameInfo = re.compile("^(?P<LIMIT>NL|PL|) (?P<CURRENCY>\$|)?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) (?P<GAME>(Texas Hold\'em|Omaha Hi|Razz))", re.MULTILINE) re_GameInfo = re.compile("^(?P<LIMIT>NL|PL|) (?P<CURRENCY>\$|)?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) (?P<GAME>(Texas Hold\'em|Omaha Hi|Razz))", re.MULTILINE)
re_SplitHands = re.compile(r'\n\n+') re_SplitHands = re.compile(r'\n\n+')
@ -34,19 +39,6 @@ class Betfair(HandHistoryConverter):
re_PlayerInfo = re.compile("Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*)\s\(\s(\$(?P<CASH>[.0-9]+)) \)") re_PlayerInfo = re.compile("Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*)\s\(\s(\$(?P<CASH>[.0-9]+)) \)")
re_Board = re.compile(ur"\[ (?P<CARDS>.+) \]") re_Board = re.compile(ur"\[ (?P<CARDS>.+) \]")
def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True, index=0):
"""\
in_path (default '-' = sys.stdin)
out_path (default '-' = sys.stdout)
follow : whether to tail -f the input"""
HandHistoryConverter.__init__(self, in_path, out_path, sitename="Betfair", follow=follow, index) # Call super class init.
logging.info("Initialising Betfair converter class")
self.filetype = "text"
self.codepage = "cp1252"
self.siteId = 7 # Needs to match id entry in Sites database
if autostart:
self.start()
def compilePlayerRegexs(self, hand): def compilePlayerRegexs(self, hand):
players = set([player[1] for player in hand.players]) players = set([player[1] for player in hand.players])

View File

@ -1021,6 +1021,22 @@ class Database:
print "Error during fdb.lock_for_insert:", str(sys.exc_value) print "Error during fdb.lock_for_insert:", str(sys.exc_value)
#end def lock_for_insert #end def lock_for_insert
def getGameTypeId(self, siteid, game):
c = self.get_cursor()
#FIXME: Fixed for NL at the moment
c.execute(self.sql.query['getGametypeNL'], (siteid, game['type'], game['category'], game['limitType'],
int(Decimal(game['sb'])*100), int(Decimal(game['bb'])*100)))
tmp = c.fetchone()
if (tmp == None):
hilo = "h"
if game['category'] in ['studhilo', 'omahahilo']:
hilo = "s"
elif game['category'] in ['razz','27_3draw','badugi']:
hilo = "l"
tmp = self.insertGameTypes( (siteid, game['type'], game['base'], game['category'], game['limitType'], hilo,
int(Decimal(game['sb'])*100), int(Decimal(game['bb'])*100), 0, 0) )
return tmp[0]
def getSqlPlayerIDs(self, pnames, siteid): def getSqlPlayerIDs(self, pnames, siteid):
result = {} result = {}
if(self.pcache == None): if(self.pcache == None):

View File

@ -26,6 +26,11 @@ from HandHistoryConverter import *
class Everleaf(HandHistoryConverter): class Everleaf(HandHistoryConverter):
sitename = 'Everleaf'
filetype = "text"
codepage = "cp1252"
siteId = 3 # Needs to match id entry in Sites database
# Static regexes # Static regexes
re_SplitHands = re.compile(r"\n\n\n+") re_SplitHands = re.compile(r"\n\n\n+")
re_TailSplitHands = re.compile(r"(\n\n\n+)") re_TailSplitHands = re.compile(r"(\n\n\n+)")
@ -37,24 +42,6 @@ class Everleaf(HandHistoryConverter):
re_Board = re.compile(ur"\[ (?P<CARDS>.+) \]") re_Board = re.compile(ur"\[ (?P<CARDS>.+) \]")
def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True, debugging=False, index=0):
"""\
in_path (default '-' = sys.stdin)
out_path (default '-' = sys.stdout)
follow : whether to tail -f the input
autostart: whether to run the thread (or you can call start() yourself)
debugging: if False, pass on partially supported game types. If true, have a go and error..."""
#print "DEBUG: XXXXXXXXXXXXXXX"
HandHistoryConverter.__init__(self, in_path, out_path, sitename="Everleaf", follow=follow, index=index)
logging.info("Initialising Everleaf converter class")
self.filetype = "text"
self.codepage = "cp1252"
self.siteId = 3 # Needs to match id entry in Sites database
self.debugging = debugging
if autostart:
self.start()
# otherwise you need to call start yourself.
def compilePlayerRegexs(self, hand): def compilePlayerRegexs(self, hand):
players = set([player[1] for player in hand.players]) players = set([player[1] for player in hand.players])
if not players <= self.compiledPlayers: # x <= y means 'x is subset of y' if not players <= self.compiledPlayers: # x <= y means 'x is subset of y'

View File

@ -1 +1,4 @@
class FpdbParseError(Exception): pass class FpdbParseError(Exception):
def __init__(self,hid=None):
self.hid = hid

View File

@ -27,6 +27,11 @@ from HandHistoryConverter import *
class Fulltilt(HandHistoryConverter): class Fulltilt(HandHistoryConverter):
sitename = "Fulltilt"
filetype = "text"
codepage = "cp1252"
siteId = 1 # Needs to match id entry in Sites database
# Static regexes # Static regexes
re_GameInfo = re.compile('''(?:(?P<TOURNAMENT>.+)\s\((?P<TOURNO>\d+)\),\s)? re_GameInfo = re.compile('''(?:(?P<TOURNAMENT>.+)\s\((?P<TOURNO>\d+)\),\s)?
.+ .+
@ -39,7 +44,7 @@ class Fulltilt(HandHistoryConverter):
''', re.VERBOSE) ''', re.VERBOSE)
re_SplitHands = re.compile(r"\n\n+") re_SplitHands = re.compile(r"\n\n+")
re_TailSplitHands = re.compile(r"(\n\n+)") re_TailSplitHands = re.compile(r"(\n\n+)")
re_HandInfo = re.compile('''.*\#(?P<HID>[0-9]+):\s re_HandInfo = re.compile(r'''.*\#(?P<HID>[0-9]+):\s
(?:(?P<TOURNAMENT>.+)\s\((?P<TOURNO>\d+)\),\s)? (?:(?P<TOURNAMENT>.+)\s\((?P<TOURNO>\d+)\),\s)?
Table\s Table\s
(?P<PLAY>Play\sChip\s|PC)? (?P<PLAY>Play\sChip\s|PC)?
@ -47,8 +52,9 @@ class Fulltilt(HandHistoryConverter):
(\((?P<TABLEATTRIBUTES>.+)\)\s)?-\s (\((?P<TABLEATTRIBUTES>.+)\)\s)?-\s
\$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+)\s(Ante\s\$?(?P<ANTE>[.0-9]+)\s)?-\s \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+)\s(Ante\s\$?(?P<ANTE>[.0-9]+)\s)?-\s
(?P<GAMETYPE>[a-zA-Z\/\'\s]+)\s-\s (?P<GAMETYPE>[a-zA-Z\/\'\s]+)\s-\s
(?P<DATETIME>.*) (?P<DATETIME>.*?)\n
''', re.VERBOSE) (?:.*?\n(?P<CANCELLED>Hand\s\#(?P=HID)\shas\sbeen\scanceled))?
''', re.VERBOSE|re.DOTALL)
re_Button = re.compile('^The button is in seat #(?P<BUTTON>\d+)', re.MULTILINE) re_Button = re.compile('^The button is in seat #(?P<BUTTON>\d+)', re.MULTILINE)
re_PlayerInfo = re.compile('Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \(\$?(?P<CASH>[,.0-9]+)\)$', re.MULTILINE) re_PlayerInfo = re.compile('Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \(\$?(?P<CASH>[,.0-9]+)\)$', re.MULTILINE)
re_Board = re.compile(r"\[(?P<CARDS>.+)\]") re_Board = re.compile(r"\[(?P<CARDS>.+)\]")
@ -60,20 +66,6 @@ class Fulltilt(HandHistoryConverter):
mixes = { 'HORSE': 'horse', '7-Game': '7game', 'HOSE': 'hose', 'HA': 'ha'} mixes = { 'HORSE': 'horse', '7-Game': '7game', 'HOSE': 'hose', 'HA': 'ha'}
def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True, index=0):
"""\
in_path (default '-' = sys.stdin)
out_path (default '-' = sys.stdout)
follow : whether to tail -f the input"""
HandHistoryConverter.__init__(self, in_path, out_path, sitename="Fulltilt", follow=follow, index=index)
logging.info("Initialising Fulltilt converter class")
self.filetype = "text"
self.codepage = "cp1252"
self.siteId = 1 # Needs to match id entry in Sites database
if autostart:
self.start()
def compilePlayerRegexs(self, hand): def compilePlayerRegexs(self, hand):
players = set([player[1] for player in hand.players]) players = set([player[1] for player in hand.players])
if not players <= self.compiledPlayers: # x <= y means 'x is subset of y' if not players <= self.compiledPlayers: # x <= y means 'x is subset of y'
@ -162,7 +154,7 @@ follow : whether to tail -f the input"""
return re.split(self.re_SplitHands, self.obs) return re.split(self.re_SplitHands, self.obs)
def readHandInfo(self, hand): def readHandInfo(self, hand):
m = self.re_HandInfo.search(hand.handText,re.DOTALL) m = self.re_HandInfo.search(hand.handText)
if(m == None): if(m == None):
logging.info("Didn't match re_HandInfo") logging.info("Didn't match re_HandInfo")
logging.info(hand.handText) logging.info(hand.handText)
@ -170,6 +162,10 @@ follow : whether to tail -f the input"""
hand.handid = m.group('HID') hand.handid = m.group('HID')
hand.tablename = m.group('TABLE') hand.tablename = m.group('TABLE')
hand.starttime = datetime.datetime.strptime(m.group('DATETIME'), "%H:%M:%S ET - %Y/%m/%d") hand.starttime = datetime.datetime.strptime(m.group('DATETIME'), "%H:%M:%S ET - %Y/%m/%d")
if m.group("CANCELLED"):
raise FpdbParseError(hid=m.group('HID'))
if m.group('TABLEATTRIBUTES'): if m.group('TABLEATTRIBUTES'):
m2 = self.re_Max.search(m.group('TABLEATTRIBUTES')) m2 = self.re_Max.search(m.group('TABLEATTRIBUTES'))
if m2: hand.maxseats = int(m2.group('MAX')) if m2: hand.maxseats = int(m2.group('MAX'))
@ -369,7 +365,4 @@ if __name__ == "__main__":
(options, args) = parser.parse_args() (options, args) = parser.parse_args()
LOG_FILENAME = './logging.out'
logging.basicConfig(filename=LOG_FILENAME,level=options.verbosity)
e = Fulltilt(in_path = options.ipath, out_path = options.opath, follow = options.follow) e = Fulltilt(in_path = options.ipath, out_path = options.opath, follow = options.follow)

View File

@ -252,10 +252,10 @@
<site enabled="False" <site enabled="False"
site_name="PartyPoker" site_name="PartyPoker"
table_finder="PartyPoker.exe" table_finder="PartyGaming.exe"
screen_name="YOUR SCREEN NAME HERE" screen_name="YOUR SCREEN NAME HERE"
site_path="" site_path="C:/Program Files/PartyGaming/PartyPoker"
HH_path="" HH_path="C:/Program Files/PartyGaming/PartyPoker/HandHistory/YOUR SCREEN NAME HERE/"
decoder="everleaf_decode_table" decoder="everleaf_decode_table"
converter="PartyPokerToFpdb" converter="PartyPokerToFpdb"
supported_games="holdem"> supported_games="holdem">

View File

@ -32,6 +32,8 @@ import pprint
import DerivedStats import DerivedStats
import Card import Card
log = logging.getLogger("parser")
class Hand(object): class Hand(object):
###############################################################3 ###############################################################3
@ -162,7 +164,7 @@ shown whether they were revealed at showdown
mucked whether they were mucked at showdown mucked whether they were mucked at showdown
dealt whether they were seen in a 'dealt to' line dealt whether they were seen in a 'dealt to' line
""" """
# logging.debug("addHoleCards %s %s" % (open + closed, player)) # log.debug("addHoleCards %s %s" % (open + closed, player))
try: try:
self.checkPlayerExists(player) self.checkPlayerExists(player)
except FpdbParseError, e: except FpdbParseError, e:
@ -187,21 +189,7 @@ db: a connected fpdb_db object"""
sqlids = db.getSqlPlayerIDs([p[1] for p in self.players], self.siteId) sqlids = db.getSqlPlayerIDs([p[1] for p in self.players], self.siteId)
#Gametypes #Gametypes
gtid = db.getGameTypeId(self.siteId, self.gametype)
print "DEBUG: self.gametype %s" % self.gametype
#Nice way to discover if the game is already listed in the db?
#Also this is using an old method from fpdb_simple - should probably conform to the rest of the inserts
hilo = "h"
if self.gametype['category'] in ['studhilo', 'omahahilo']:
hilo = "s"
elif self.gametype['category'] in ['razz','27_3draw','badugi']:
hilo = "l"
gtid = db.insertGameTypes( (self.siteId, self.gametype['type'], self.gametype['base'],
self.gametype['category'], self.gametype['limitType'], hilo,
int(Decimal(self.gametype['sb'])*100), int(Decimal(self.gametype['bb'])*100), 0, 0) )
# HudCache data to come from DerivedStats class # HudCache data to come from DerivedStats class
# HandsActions - all actions for all players for all streets - self.actions # HandsActions - all actions for all players for all streets - self.actions
@ -210,7 +198,7 @@ db: a connected fpdb_db object"""
hh = {} hh = {}
hh['siteHandNo'] = self.handid hh['siteHandNo'] = self.handid
hh['handStart'] = self.starttime hh['handStart'] = self.starttime
hh['gameTypeId'] = gtid[0] hh['gameTypeId'] = gtid
# seats TINYINT NOT NULL, # seats TINYINT NOT NULL,
hh['tableName'] = self.tablename hh['tableName'] = self.tablename
hh['maxSeats'] = self.maxseats hh['maxSeats'] = self.maxseats
@ -284,7 +272,7 @@ seat (int) indicating the seat
name (string) player name name (string) player name
chips (string) the chips the player has at the start of the hand (can be None) 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.""" If a player has None chips he won't be added."""
logging.debug("addPlayer: %s %s (%s)" % (seat, name, chips)) log.debug("addPlayer: %s %s (%s)" % (seat, name, chips))
if chips is not None: if chips is not None:
chips = re.sub(u',', u'', chips) #some sites have commas chips = re.sub(u',', u'', chips) #some sites have commas
self.players.append([seat, name, chips]) self.players.append([seat, name, chips])
@ -300,9 +288,9 @@ If a player has None chips he won't be added."""
# go through m and initialise actions to empty list for each street. # go through m and initialise actions to empty list for each street.
if match: if match:
self.streets.update(match.groupdict()) self.streets.update(match.groupdict())
logging.debug("markStreets:\n"+ str(self.streets)) log.debug("markStreets:\n"+ str(self.streets))
else: else:
logging.error("markstreets didn't match") log.error("markstreets didn't match")
def checkPlayerExists(self,player): def checkPlayerExists(self,player):
if player not in [p[1] for p in self.players]: if player not in [p[1] for p in self.players]:
@ -312,7 +300,7 @@ If a player has None chips he won't be added."""
def setCommunityCards(self, street, cards): def setCommunityCards(self, street, cards):
logging.debug("setCommunityCards %s %s" %(street, cards)) log.debug("setCommunityCards %s %s" %(street, cards))
self.board[street] = [self.card(c) for c in cards] self.board[street] = [self.card(c) for c in cards]
# print "DEBUG: self.board: %s" % self.board # print "DEBUG: self.board: %s" % self.board
@ -323,7 +311,7 @@ If a player has None chips he won't be added."""
return c return c
def addAnte(self, player, ante): def addAnte(self, player, ante):
logging.debug("%s %s antes %s" % ('ANTES', player, ante)) log.debug("%s %s antes %s" % ('ANTES', player, ante))
if player is not None: if player is not None:
ante = re.sub(u',', u'', ante) #some sites have commas ante = re.sub(u',', u'', ante) #some sites have commas
self.bets['ANTES'][player].append(Decimal(ante)) self.bets['ANTES'][player].append(Decimal(ante))
@ -342,7 +330,7 @@ If a player has None chips he won't be added."""
# - this is a call of 1 sb and a raise to 1 bb # - this is a call of 1 sb and a raise to 1 bb
# #
logging.debug("addBlind: %s posts %s, %s" % (player, blindtype, amount)) log.debug("addBlind: %s posts %s, %s" % (player, blindtype, amount))
if player is not None: if player is not None:
amount = re.sub(u',', u'', amount) #some sites have commas amount = re.sub(u',', u'', amount) #some sites have commas
self.bets['PREFLOP'][player].append(Decimal(amount)) self.bets['PREFLOP'][player].append(Decimal(amount))
@ -364,7 +352,7 @@ If a player has None chips he won't be added."""
def addCall(self, street, player=None, amount=None): def addCall(self, street, player=None, amount=None):
if amount: if amount:
amount = re.sub(u',', u'', amount) #some sites have commas amount = re.sub(u',', u'', amount) #some sites have commas
logging.debug("%s %s calls %s" %(street, player, amount)) log.debug("%s %s calls %s" %(street, player, amount))
# Potentially calculate the amount of the call if not supplied # Potentially calculate the amount of the call if not supplied
# corner cases include if player would be all in # corner cases include if player would be all in
if amount is not None: if amount is not None:
@ -434,7 +422,7 @@ Add a raise on [street] by [player] to [amountTo]
self._addRaise(street, player, C, Rb, Rt) self._addRaise(street, player, C, Rb, Rt)
def _addRaise(self, street, player, C, Rb, Rt): def _addRaise(self, street, player, C, Rb, Rt):
logging.debug("%s %s raise %s" %(street, player, Rt)) log.debug("%s %s raise %s" %(street, player, Rt))
self.bets[street][player].append(C + Rb) self.bets[street][player].append(C + Rb)
self.stacks[player] -= (C + Rb) self.stacks[player] -= (C + Rb)
act = (player, 'raises', Rb, Rt, C, self.stacks[player]==0) act = (player, 'raises', Rb, Rt, C, self.stacks[player]==0)
@ -445,7 +433,7 @@ Add a raise on [street] by [player] to [amountTo]
def addBet(self, street, player, amount): def addBet(self, street, player, amount):
logging.debug("%s %s bets %s" %(street, player, amount)) log.debug("%s %s bets %s" %(street, player, amount))
amount = re.sub(u',', u'', amount) #some sites have commas amount = re.sub(u',', u'', amount) #some sites have commas
self.checkPlayerExists(player) self.checkPlayerExists(player)
self.bets[street][player].append(Decimal(amount)) self.bets[street][player].append(Decimal(amount))
@ -464,7 +452,7 @@ Add a raise on [street] by [player] to [amountTo]
def addFold(self, street, player): def addFold(self, street, player):
logging.debug("%s %s folds" % (street, player)) log.debug("%s %s folds" % (street, player))
self.checkPlayerExists(player) self.checkPlayerExists(player)
self.folded.add(player) self.folded.add(player)
self.pot.addFold(player) self.pot.addFold(player)
@ -479,7 +467,7 @@ Add a raise on [street] by [player] to [amountTo]
def addCollectPot(self,player, pot): def addCollectPot(self,player, pot):
logging.debug("%s collected %s" % (player, pot)) log.debug("%s collected %s" % (player, pot))
self.checkPlayerExists(player) self.checkPlayerExists(player)
self.collected = self.collected + [[player, pot]] self.collected = self.collected + [[player, pot]]
if player not in self.collectees: if player not in self.collectees:
@ -493,7 +481,7 @@ Add a raise on [street] by [player] to [amountTo]
For when a player shows cards for any reason (for showdown or out of choice). For when a player shows cards for any reason (for showdown or out of choice).
Card ranks will be uppercased Card ranks will be uppercased
""" """
logging.debug("addShownCards %s hole=%s all=%s" % (player, cards, holeandboard)) log.debug("addShownCards %s hole=%s all=%s" % (player, cards, holeandboard))
if cards is not None: if cards is not None:
self.addHoleCards(cards,player,shown, mucked) self.addHoleCards(cards,player,shown, mucked)
elif holeandboard is not None: elif holeandboard is not None:
@ -543,7 +531,7 @@ Map the tuple self.gametype onto the pokerstars string describing it
"cp" : "Cap Pot Limit" "cp" : "Cap Pot Limit"
} }
logging.debug("gametype: %s" %(self.gametype)) log.debug("gametype: %s" %(self.gametype))
retstring = "%s %s" %(gs[self.gametype['category']], ls[self.gametype['limitType']]) retstring = "%s %s" %(gs[self.gametype['category']], ls[self.gametype['limitType']])
return retstring return retstring
@ -622,7 +610,7 @@ class HoldemOmahaHand(Hand):
def __init__(self, hhc, sitename, gametype, handText, builtFrom = "HHC", handid=None): def __init__(self, hhc, sitename, gametype, handText, builtFrom = "HHC", handid=None):
if gametype['base'] != 'hold': if gametype['base'] != 'hold':
pass # or indeed don't pass and complain instead pass # or indeed don't pass and complain instead
logging.debug("HoldemOmahaHand") log.debug("HoldemOmahaHand")
self.allStreets = ['BLINDSANTES', 'PREFLOP','FLOP','TURN','RIVER'] self.allStreets = ['BLINDSANTES', 'PREFLOP','FLOP','TURN','RIVER']
self.holeStreets = ['PREFLOP'] self.holeStreets = ['PREFLOP']
self.communityStreets = ['FLOP', 'TURN', 'RIVER'] self.communityStreets = ['FLOP', 'TURN', 'RIVER']
@ -661,9 +649,9 @@ class HoldemOmahaHand(Hand):
if handid is not None: if handid is not None:
self.select(handid) # Will need a handId self.select(handid) # Will need a handId
else: else:
logging.warning("HoldemOmahaHand.__init__:Can't assemble hand from db without a handid") log.warning("HoldemOmahaHand.__init__:Can't assemble hand from db without a handid")
else: else:
logging.warning("HoldemOmahaHand.__init__:Neither HHC nor DB+handid provided") log.warning("HoldemOmahaHand.__init__:Neither HHC nor DB+handid provided")
pass pass
@ -775,7 +763,7 @@ class HoldemOmahaHand(Hand):
super(HoldemOmahaHand, self).writeHand(fh) super(HoldemOmahaHand, self).writeHand(fh)
players_who_act_preflop = set(([x[0] for x in self.actions['PREFLOP']]+[x[0] for x in self.actions['BLINDSANTES']])) players_who_act_preflop = set(([x[0] for x in self.actions['PREFLOP']]+[x[0] for x in self.actions['BLINDSANTES']]))
logging.debug(self.actions['PREFLOP']) log.debug(self.actions['PREFLOP'])
for player in [x for x in self.players if x[1] in players_who_act_preflop]: for player in [x for x in self.players if x[1] in players_who_act_preflop]:
#Only print stacks of players who do something preflop #Only print stacks of players who do something preflop
print >>fh, ("Seat %s: %s ($%s in chips) " %(player[0], player[1], player[2])) print >>fh, ("Seat %s: %s ($%s in chips) " %(player[0], player[1], player[2]))
@ -918,7 +906,7 @@ class DrawHand(Hand):
# - this is a call of 1 sb and a raise to 1 bb # - this is a call of 1 sb and a raise to 1 bb
# #
logging.debug("addBlind: %s posts %s, %s" % (player, blindtype, amount)) log.debug("addBlind: %s posts %s, %s" % (player, blindtype, amount))
if player is not None: if player is not None:
self.bets['DEAL'][player].append(Decimal(amount)) self.bets['DEAL'][player].append(Decimal(amount))
self.stacks[player] -= Decimal(amount) self.stacks[player] -= Decimal(amount)
@ -945,7 +933,7 @@ class DrawHand(Hand):
def discardDrawHoleCards(self, cards, player, street): def discardDrawHoleCards(self, cards, player, street):
logging.debug("discardDrawHoleCards '%s' '%s' '%s'" % (cards, player, street)) log.debug("discardDrawHoleCards '%s' '%s' '%s'" % (cards, player, street))
self.discards[street][player] = set([cards]) self.discards[street][player] = set([cards])
@ -1063,8 +1051,7 @@ class StudHand(Hand):
for street in self.actionStreets: for street in self.actionStreets:
if street == 'ANTES': continue # OMG--sometime someone folds in the ante round if street == 'ANTES': continue # OMG--sometime someone folds in the ante round
if self.streets[street]: if self.streets[street]:
logging.debug(street) log.debug(street + self.streets[street])
logging.debug(self.streets[street])
hhc.readAction(self, street) hhc.readAction(self, street)
hhc.readCollectPot(self) hhc.readCollectPot(self)
hhc.readShownCards(self) # not done yet hhc.readShownCards(self) # not done yet
@ -1096,7 +1083,7 @@ street (string) the street name (in streetList)
open list of card bigrams e.g. ['2h','Jc'], dealt face up open list of card bigrams e.g. ['2h','Jc'], dealt face up
closed likewise, but known only to player closed likewise, but known only to player
""" """
logging.debug("addPlayerCards %s, o%s x%s" % (player, open, closed)) log.debug("addPlayerCards %s, o%s x%s" % (player, open, closed))
try: try:
self.checkPlayerExists(player) self.checkPlayerExists(player)
self.holecards[street][player] = (open, closed) self.holecards[street][player] = (open, closed)
@ -1110,7 +1097,7 @@ closed likewise, but known only to player
"""\ """\
Add a complete on [street] by [player] to [amountTo] Add a complete on [street] by [player] to [amountTo]
""" """
logging.debug("%s %s completes %s" % (street, player, amountTo)) log.debug("%s %s completes %s" % (street, player, amountTo))
amountTo = re.sub(u',', u'', amountTo) #some sites have commas amountTo = re.sub(u',', u'', amountTo) #some sites have commas
self.checkPlayerExists(player) self.checkPlayerExists(player)
Bp = self.lastBet['THIRD'] Bp = self.lastBet['THIRD']
@ -1128,7 +1115,7 @@ Add a complete on [street] by [player] to [amountTo]
def addBringIn(self, player, bringin): def addBringIn(self, player, bringin):
if player is not None: if player is not None:
logging.debug("Bringin: %s, %s" % (player , bringin)) log.debug("Bringin: %s, %s" % (player , bringin))
self.bets['THIRD'][player].append(Decimal(bringin)) self.bets['THIRD'][player].append(Decimal(bringin))
self.stacks[player] -= Decimal(bringin) self.stacks[player] -= Decimal(bringin)
act = (player, 'bringin', bringin, self.stacks[player]==0) act = (player, 'bringin', bringin, self.stacks[player]==0)

View File

@ -20,7 +20,6 @@ import Hand
import re import re
import sys import sys
import traceback import traceback
import logging
from optparse import OptionParser from optparse import OptionParser
import os import os
import os.path import os.path
@ -31,19 +30,37 @@ import operator
from xml.dom.minidom import Node from xml.dom.minidom import Node
import time import time
import datetime import datetime
from Exceptions import FpdbParseError
import gettext import gettext
gettext.install('fpdb') gettext.install('fpdb')
import logging, logging.config
logging.config.fileConfig("logging.conf")
log = logging.getLogger("parser")
class HandHistoryConverter(): class HandHistoryConverter():
READ_CHUNK_SIZE = 10000 # bytes to read at a time from file (in tail mode) READ_CHUNK_SIZE = 10000 # bytes to read at a time from file in tail mode
def __init__(self, in_path = '-', out_path = '-', sitename = None, follow=False, index=0):
logging.info("HandHistory init") # filetype can be "text" or "xml"
# so far always "text"
# subclass HHC_xml for xml parsing
filetype = "text"
# codepage indicates the encoding of the text file.
# cp1252 is a safe default
# "utf_8" is more likely if there are funny characters
codepage = "cp1252"
def __init__(self, in_path = '-', out_path = '-', follow=False, index=0, autostart=True):
"""\
in_path (default '-' = sys.stdin)
out_path (default '-' = sys.stdout)
follow : whether to tail -f the input"""
log.info("HandHistory init - %s subclass, in_path '%s'; out_path '%s'" % (self.sitename, in_path, out_path) )
# default filetype and codepage. Subclasses should set these properly.
self.filetype = "text"
self.codepage = "utf8"
self.index = 0 self.index = 0
self.in_path = in_path self.in_path = in_path
@ -60,54 +77,72 @@ class HandHistoryConverter():
# TODO: out_path should be sanity checked. # TODO: out_path should be sanity checked.
out_dir = os.path.dirname(self.out_path) out_dir = os.path.dirname(self.out_path)
if not os.path.isdir(out_dir): if not os.path.isdir(out_dir):
logging.info("Creatin directory '%s'" % out_dir) log.info("Creating directory '%s'" % out_dir)
os.makedirs(out_dir) os.makedirs(out_dir)
self.out_fh = open(self.out_path, 'w') try:
self.out_fh = open(self.out_path, 'w')
log.debug("out_path %s opened as %s" % (self.out_path, self.out_fh))
except:
log.error("out_path %s couldn't be opened" % (self.out_path))
self.sitename = sitename
self.follow = follow self.follow = follow
self.compiledPlayers = set() self.compiledPlayers = set()
self.maxseats = 10 self.maxseats = 10
if autostart:
self.start()
def __str__(self): def __str__(self):
return """ return """
HandHistoryConverter: '%(sitename)s' HandHistoryConverter: '%(sitename)s'
filetype: '%(filetype)s' filetype '%(filetype)s'
in_path: '%(in_path)s' in_path '%(in_path)s'
out_path: '%(out_path)s' out_path '%(out_path)s'
""" % { 'sitename':self.sitename, 'filetype':self.filetype, 'in_path':self.in_path, 'out_path':self.out_path } follow '%(follow)s'
""" % locals()
def start(self): def start(self):
"""process a hand at a time from the input specified by in_path. """Process a hand at a time from the input specified by in_path.
If in follow mode, wait for more data to turn up. If in follow mode, wait for more data to turn up.
Otherwise, finish at eof. Otherwise, finish at EOF.
""" """
starttime = time.time() starttime = time.time()
if not self.sanityCheck(): if not self.sanityCheck():
print "Cowardly refusing to continue after failed sanity check" log.warning("Failed sanity check")
return return
if self.follow: try:
numHands = 0 numHands = 0
for handText in self.tailHands(): numErrors = 0
numHands+=1 if self.follow:
self.processHand(handText) log.info("Tailing '%s'" % self.in_path)
else: for handText in self.tailHands():
handsList = self.allHandsAsList() try:
logging.info("Parsing %d hands" % len(handsList)) self.processHand(handText)
nBadHands = 0 numHands+=1
for handText in handsList: except FpdbParseError, e:
try: numErrors+=1
self.processedHands.append(self.processHand(handText)) log.warning("Failed to convert hand %s" % e.hid)
except Exception, e: # TODO: it's better to replace it with s-t like HhcEception log.debug(handText)
nBadHands += 1 else:
logging.error("Caught exception while parsing hand: %s" % str(e)) handsList = self.allHandsAsList()
numHands = len(handsList) - nBadHands log.info("Parsing %d hands" % len(handsList))
endtime = time.time() for handText in handsList:
print "read %d hands in %.3f seconds" % (numHands, endtime - starttime) try:
if self.out_fh != sys.stdout: self.processedHands.append(self.processHand(handText))
self.out_fh.close() except FpdbParseError, e:
numErrors+=1
log.warning("Failed to convert hand %s" % e.hid)
log.debug(handText)
numHands = len(handsList)
endtime = time.time()
log.info("Read %d hands (%d failed) in %.3f seconds" % (numHands, numErrors, endtime - starttime))
except IOError, ioe:
log.exception("Error converting '%s'" % self.in_path)
finally:
if self.out_fh != sys.stdout:
self.out_fh.close()
def tailHands(self): def tailHands(self):
@ -134,7 +169,7 @@ which it expects to find at self.re_TailSplitHands -- see for e.g. Everleaf.py.
time.sleep(interval) time.sleep(interval)
fd.seek(where) fd.seek(where)
else: else:
logging.debug("%s changed inode numbers from %d to %d" % (self.in_path, fd_results[1], st_results[1])) log.debug("%s changed inode numbers from %d to %d" % (self.in_path, fd_results[1], st_results[1]))
fd = codecs.open(self.in_path, 'r', self.codepage) fd = codecs.open(self.in_path, 'r', self.codepage)
fd.seek(where) fd.seek(where)
else: else:
@ -179,13 +214,13 @@ which it expects to find at self.re_TailSplitHands -- see for e.g. Everleaf.py.
self.obs = self.obs.strip() self.obs = self.obs.strip()
self.obs = self.obs.replace('\r\n', '\n') self.obs = self.obs.replace('\r\n', '\n')
if self.obs == "" or self.obs == None: if self.obs == "" or self.obs == None:
logging.info("Read no hands.") log.info("Read no hands.")
return return
return re.split(self.re_SplitHands, self.obs) return re.split(self.re_SplitHands, self.obs)
def processHand(self, handText): def processHand(self, handText):
gametype = self.determineGameType(handText) gametype = self.determineGameType(handText)
logging.debug("gametype %s" % gametype) log.debug("gametype %s" % gametype)
hand = None hand = None
if gametype is None: if gametype is None:
l = None l = None
@ -200,14 +235,14 @@ which it expects to find at self.re_TailSplitHands -- see for e.g. Everleaf.py.
l = [type] + [base] + [limit] l = [type] + [base] + [limit]
if l in self.readSupportedGames(): if l in self.readSupportedGames():
if gametype['base'] == 'hold': if gametype['base'] == 'hold':
logging.debug("hand = Hand.HoldemOmahaHand(self, self.sitename, gametype, handtext)") log.debug("hand = Hand.HoldemOmahaHand(self, self.sitename, gametype, handtext)")
hand = Hand.HoldemOmahaHand(self, self.sitename, gametype, handText) hand = Hand.HoldemOmahaHand(self, self.sitename, gametype, handText)
elif gametype['base'] == 'stud': elif gametype['base'] == 'stud':
hand = Hand.StudHand(self, self.sitename, gametype, handText) hand = Hand.StudHand(self, self.sitename, gametype, handText)
elif gametype['base'] == 'draw': elif gametype['base'] == 'draw':
hand = Hand.DrawHand(self, self.sitename, gametype, handText) hand = Hand.DrawHand(self, self.sitename, gametype, handText)
else: else:
logging.info("Unsupported game type: %s" % gametype) log.info("Unsupported game type: %s" % gametype)
if hand: if hand:
# uncomment these to calculate some stats # uncomment these to calculate some stats
@ -216,7 +251,7 @@ which it expects to find at self.re_TailSplitHands -- see for e.g. Everleaf.py.
hand.writeHand(self.out_fh) hand.writeHand(self.out_fh)
return hand return hand
else: else:
logging.info("Unsupported game type: %s" % gametype) log.info("Unsupported game type: %s" % gametype)
# TODO: pity we don't know the HID at this stage. Log the entire hand? # TODO: pity we don't know the HID at this stage. Log the entire hand?
# From the log we can deduce that it is the hand after the one before :) # From the log we can deduce that it is the hand after the one before :)
@ -342,26 +377,23 @@ or None if we fail to get the info """
return hands return hands
def readFile(self): def readFile(self):
"""open in_path according to self.codepage""" """Open in_path according to self.codepage. Exceptions caught further up"""
if(self.filetype == "text"): if(self.filetype == "text"):
if self.in_path == '-': if self.in_path == '-':
# read from stdin # read from stdin
logging.debug("Reading stdin with %s" % self.codepage) # is this necessary? or possible? or what? log.debug("Reading stdin with %s" % self.codepage) # is this necessary? or possible? or what?
in_fh = codecs.getreader('cp1252')(sys.stdin) in_fh = codecs.getreader('cp1252')(sys.stdin)
else: else:
logging.debug("Opening %s with %s" % (self.in_path, self.codepage))
in_fh = codecs.open(self.in_path, 'r', self.codepage) in_fh = codecs.open(self.in_path, 'r', self.codepage)
in_fh.seek(self.index) in_fh.seek(self.index)
self.obs = in_fh.read() log.debug("Opened in_path: '%s' with %s" % (self.in_path, self.codepage))
self.index = in_fh.tell() self.obs = in_fh.read()
in_fh.close() self.index = in_fh.tell()
in_fh.close()
elif(self.filetype == "xml"): elif(self.filetype == "xml"):
try: doc = xml.dom.minidom.parse(filename)
doc = xml.dom.minidom.parse(filename) self.doc = doc
self.doc = doc
except:
traceback.print_exc(file=sys.stderr)
def guessMaxSeats(self, hand): def guessMaxSeats(self, hand):
"""Return a guess at max_seats when not specified in HH.""" """Return a guess at max_seats when not specified in HH."""

View File

@ -1,4 +1,5 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Hud.py """Hud.py
Create and manage the hud overlays. Create and manage the hud overlays.
@ -95,6 +96,8 @@ class Hud:
if my_import == None: if my_import == None:
continue continue
self.aux_windows.append(my_import(self, config, aux_params)) self.aux_windows.append(my_import(self, config, aux_params))
self.creation_attrs = None
def create_mw(self): def create_mw(self):
@ -146,6 +149,21 @@ class Hud:
self.item4.connect("activate", self.debug_stat_windows) self.item4.connect("activate", self.debug_stat_windows)
self.item4.show() self.item4.show()
self.item5 = gtk.MenuItem('Set max seats')
self.menu.append(self.item5)
self.item5.show()
self.maxSeatsMenu = gtk.Menu()
self.item5.set_submenu(self.maxSeatsMenu)
for i in range(2, 11, 1):
item = gtk.MenuItem('%d-max' % i)
item.ms = i
self.maxSeatsMenu.append(item)
item.connect("activate", self.change_max_seats)
item.show()
setattr(self, 'maxSeatsMenuItem%d' % (i-1), item)
self.ebox.connect_object("button-press-event", self.on_button_press, self.menu) self.ebox.connect_object("button-press-event", self.on_button_press, self.menu)
self.main_window.show_all() self.main_window.show_all()
@ -162,7 +180,19 @@ class Hud:
self.main_window.gdkhandle.set_transient_for(self.main_window.parentgdkhandle) # self.main_window.gdkhandle.set_transient_for(self.main_window.parentgdkhandle) #
self.update_table_position() self.update_table_position()
def change_max_seats(self, widget):
if self.max != widget.ms:
print 'change_max_seats', widget.ms
self.max = widget.ms
try:
self.kill()
self.create(*self.creation_attrs)
self.update(self.hand, self.config)
except Exception, e:
print "Expcetion:",str(e)
pass
def update_table_position(self): def update_table_position(self):
if os.name == 'nt': if os.name == 'nt':
if not win32gui.IsWindow(self.table.number): if not win32gui.IsWindow(self.table.number):
@ -264,6 +294,8 @@ class Hud:
# #
# this method also manages the creating and destruction of stat # this method also manages the creating and destruction of stat
# windows via calls to the Stat_Window class # windows via calls to the Stat_Window class
self.creation_attrs = hand, config, stat_dict, cards
self.hand = hand self.hand = hand
if not self.mw_created: if not self.mw_created:
self.create_mw() self.create_mw()

94
pyfpdb/PartyPokerToFpdb.py Normal file → Executable file
View File

@ -43,21 +43,27 @@ class PartyPoker(HandHistoryConverter):
############################################################ ############################################################
# Class Variables # Class Variables
sitename = "PartyPoker"
codepage = "cp1252"
siteId = 9 # TODO: automate; it's a class variable so shouldn't hit DB too often
filetype = "text" # "text" or "xml". I propose we subclass HHC to HHC_Text and HHC_XML.
sym = {'USD': "\$", } sym = {'USD': "\$", }
# Static regexes # Static regexes
# $5 USD NL Texas Hold'em - Saturday, July 25, 07:53:52 EDT 2009 # $5 USD NL Texas Hold'em - Saturday, July 25, 07:53:52 EDT 2009
# NL Texas Hold'em $1 USD Buy-in Trny:45685440 Level:8 Blinds-Antes(600/1 200 -50) - Sunday, May 17, 11:25:07 MSKS 2009 # NL Texas Hold'em $1 USD Buy-in Trny:45685440 Level:8 Blinds-Antes(600/1 200 -50) - Sunday, May 17, 11:25:07 MSKS 2009
re_GameInfoRing = re.compile(""" re_GameInfoRing = re.compile("""
(?P<CURRENCY>\$|)\s*(?P<RINGLIMIT>\d+)\s*(?:USD)?\s* (?P<CURRENCY>\$|)\s*(?P<RINGLIMIT>[0-9,]+)\s*(?:USD)?\s*
(?P<LIMIT>(NL))\s+ (?P<LIMIT>(NL|PL|))\s+
(?P<GAME>(Texas\ Hold\'em)) (?P<GAME>(Texas\ Hold\'em|Omaha))
\s*\-\s* \s*\-\s*
(?P<DATETIME>.+) (?P<DATETIME>.+)
""", re.VERBOSE) """, re.VERBOSE)
re_GameInfoTrny = re.compile(""" re_GameInfoTrny = re.compile("""
(?P<LIMIT>(NL))\s+ (?P<LIMIT>(NL|PL|))\s+
(?P<GAME>(Texas\ Hold\'em))\s+ (?P<GAME>(Texas\ Hold\'em|Omaha))\s+
(?P<BUYIN>\$?[.0-9]+)\s*(?P<BUYIN_CURRENCY>USD)?\s*Buy-in\s+ (?P<BUYIN>\$?[.0-9]+)\s*(?P<BUYIN_CURRENCY>USD)?\s*Buy-in\s+
Trny:\s?(?P<TOURNO>\d+)\s+ Trny:\s?(?P<TOURNO>\d+)\s+
Level:\s*(?P<LEVEL>\d+)\s+ Level:\s*(?P<LEVEL>\d+)\s+
@ -88,6 +94,7 @@ class PartyPoker(HandHistoryConverter):
""", """,
re.MULTILINE|re.VERBOSE) re.MULTILINE|re.VERBOSE)
re_TotalPlayers = re.compile("^Total\s+number\s+of\s+players\s*:\s*(?P<MAXSEATS>\d+)", re.MULTILINE)
re_SplitHands = re.compile('\x00+') re_SplitHands = re.compile('\x00+')
re_TailSplitHands = re.compile('(\x00+)') re_TailSplitHands = re.compile('(\x00+)')
lineSplitter = '\n' lineSplitter = '\n'
@ -96,23 +103,20 @@ class PartyPoker(HandHistoryConverter):
re_NoSmallBlind = re.compile('^There is no Small Blind in this hand as the Big Blind of the previous hand left the table') re_NoSmallBlind = re.compile('^There is no Small Blind in this hand as the Big Blind of the previous hand left the table')
def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True, index=0):
"""\
in_path (default '-' = sys.stdin)
out_path (default '-' = sys.stdout)
follow : whether to tail -f the input"""
HandHistoryConverter.__init__(self, in_path, out_path, sitename="PartyPoker", follow=follow, index=index)
logging.info("Initialising PartyPoker converter class")
self.filetype = "text"
self.codepage = "cp1252" # FIXME: wtf?
self.siteId = 9 # Needs to match id entry in Sites database
self._gameType = None # cached reg-parse result
if autostart:
self.start()
def allHandsAsList(self): def allHandsAsList(self):
list = HandHistoryConverter.allHandsAsList(self) list = HandHistoryConverter.allHandsAsList(self)
if list is None:
return []
return filter(lambda text: len(text.strip()), list) return filter(lambda text: len(text.strip()), list)
def guessMaxSeats(self, hand):
"""Return a guess at max_seats when not specified in HH."""
mo = self.maxOccSeat(hand)
if mo == 10: return mo
if mo == 2: return 2
if mo <= 6: return 6
return 9 if hand.gametype['type']=='ring' else 10
def compilePlayerRegexs(self, hand): def compilePlayerRegexs(self, hand):
players = set([player[1] for player in hand.players]) players = set([player[1] for player in hand.players])
@ -123,9 +127,9 @@ follow : whether to tail -f the input"""
'CUR': hand.gametype['currency'] if hand.gametype['currency']!='T$' else ''} 'CUR': hand.gametype['currency'] if hand.gametype['currency']!='T$' else ''}
for key in ('CUR_SYM', 'CUR'): for key in ('CUR_SYM', 'CUR'):
subst[key] = re.escape(subst[key]) subst[key] = re.escape(subst[key])
logging.debug("player_re: " + subst['PLYR']) log.debug("player_re: " + subst['PLYR'])
logging.debug("CUR_SYM: " + subst['CUR_SYM']) log.debug("CUR_SYM: " + subst['CUR_SYM'])
logging.debug("CUR: " + subst['CUR']) log.debug("CUR: " + subst['CUR'])
self.re_PostSB = re.compile( self.re_PostSB = re.compile(
r"^%(PLYR)s posts small blind \[%(CUR_SYM)s(?P<SB>[.0-9]+) ?%(CUR)s\]\." % subst, r"^%(PLYR)s posts small blind \[%(CUR_SYM)s(?P<SB>[.0-9]+) ?%(CUR)s\]\." % subst,
re.MULTILINE) re.MULTILINE)
@ -154,15 +158,17 @@ follow : whether to tail -f the input"""
def readSupportedGames(self): def readSupportedGames(self):
return [["ring", "hold", "nl"], return [["ring", "hold", "nl"],
#["ring", "hold", "pl"], ["ring", "hold", "pl"],
#["ring", "hold", "fl"], ["ring", "hold", "fl"],
["tour", "hold", "nl"], ["tour", "hold", "nl"],
#["tour", "hold", "pl"], ["tour", "hold", "pl"],
#["tour", "hold", "fl"], ["tour", "hold", "fl"],
] ]
def _getGameType(self, handText): def _getGameType(self, handText):
if not hasattr(self, '_gameType'):
self._gameType = None
if self._gameType is None: if self._gameType is None:
# let's determine whether hand is trny # let's determine whether hand is trny
# and whether 5-th line contains head line # and whether 5-th line contains head line
@ -181,7 +187,7 @@ follow : whether to tail -f the input"""
gametype dict is: gametype dict is:
{'limitType': xxx, 'base': xxx, 'category': xxx}""" {'limitType': xxx, 'base': xxx, 'category': xxx}"""
logging.debug(self.ParsingException().wrapHh( handText )) log.debug(self.ParsingException().wrapHh( handText ))
info = {} info = {}
m = self._getGameType(handText) m = self._getGameType(handText)
@ -190,12 +196,10 @@ follow : whether to tail -f the input"""
mg = m.groupdict() mg = m.groupdict()
# translations from captured groups to fpdb info strings # translations from captured groups to fpdb info strings
limits = { 'NL':'nl', limits = { 'NL':'nl', 'PL':'pl', '':'fl' }
# 'Pot Limit':'pl', 'Limit':'fl'
}
games = { # base, category games = { # base, category
"Texas Hold'em" : ('hold','holdem'), "Texas Hold'em" : ('hold','holdem'),
#'Omaha' : ('hold','omahahi'), 'Omaha' : ('hold','omahahi'),
} }
currencies = { '$':'USD', '':'T$' } currencies = { '$':'USD', '':'T$' }
@ -205,7 +209,7 @@ follow : whether to tail -f the input"""
"Cannot fetch field '%s'" % expectedField, "Cannot fetch field '%s'" % expectedField,
hh = handText) hh = handText)
try: try:
info['limitType'] = limits[mg['LIMIT']] info['limitType'] = limits[mg['LIMIT'].strip()]
except: except:
raise self.ParsingException( raise self.ParsingException(
"Unknown limit '%s'" % mg['LIMIT'], "Unknown limit '%s'" % mg['LIMIT'],
@ -229,8 +233,8 @@ follow : whether to tail -f the input"""
# FIXME: there are only $ and play money availible for cash # FIXME: there are only $ and play money availible for cash
info['currency'] = currencies[mg['CURRENCY']] info['currency'] = currencies[mg['CURRENCY']]
else: else:
info['sb'] = renderTrnyMoney(mg['SB']) info['sb'] = clearMoneyString(mg['SB'])
info['bb'] = renderTrnyMoney(mg['BB']) info['bb'] = clearMoneyString(mg['BB'])
info['currency'] = 'T$' info['currency'] = 'T$'
# NB: SB, BB must be interpreted as blinds or bets depending on limit type. # NB: SB, BB must be interpreted as blinds or bets depending on limit type.
@ -249,11 +253,14 @@ follow : whether to tail -f the input"""
m = self.re_Hid.search(hand.handText) m = self.re_Hid.search(hand.handText)
if m: info.update(m.groupdict()) if m: info.update(m.groupdict())
m = self.re_TotalPlayers.search(hand.handText)
if m: info.update(m.groupdict())
# FIXME: it's a hack cause party doesn't supply hand.maxseats info # FIXME: it's a hack cause party doesn't supply hand.maxseats info
#hand.maxseats = ??? #hand.maxseats = ???
hand.mixed = None hand.mixed = None
logging.debug("readHandInfo: %s" % info) log.debug("readHandInfo: %s" % info)
for key in info: for key in info:
if key == 'DATETIME': if key == 'DATETIME':
#Saturday, July 25, 07:53:52 EDT 2009 #Saturday, July 25, 07:53:52 EDT 2009
@ -281,6 +288,8 @@ follow : whether to tail -f the input"""
#FIXME: it's dirty hack T_T #FIXME: it's dirty hack T_T
cur = info[key][0] if info[key][0] not in '0123456789' else '' cur = info[key][0] if info[key][0] not in '0123456789' else ''
hand.buyin = info[key] + '+%s0' % cur hand.buyin = info[key] + '+%s0' % cur
#if key == 'MAXSEATS':
#hand.maxseats = int(info[key])
if key == 'LEVEL': if key == 'LEVEL':
hand.level = info[key] hand.level = info[key]
if key == 'PLAY' and info['PLAY'] != 'Real': if key == 'PLAY' and info['PLAY'] != 'Real':
@ -293,15 +302,15 @@ follow : whether to tail -f the input"""
if m: if m:
hand.buttonpos = int(m.group('BUTTON')) hand.buttonpos = int(m.group('BUTTON'))
else: else:
logging.info('readButton: not found') log.info('readButton: not found')
def readPlayerStacks(self, hand): def readPlayerStacks(self, hand):
logging.debug("readPlayerStacks") log.debug("readPlayerStacks")
m = self.re_PlayerInfo.finditer(hand.handText) m = self.re_PlayerInfo.finditer(hand.handText)
players = [] players = []
for a in m: for a in m:
hand.addPlayer(int(a.group('SEAT')), a.group('PNAME'), hand.addPlayer(int(a.group('SEAT')), a.group('PNAME'),
renderTrnyMoney(a.group('CASH'))) clearMoneyString(a.group('CASH')))
def markStreets(self, hand): def markStreets(self, hand):
# PREFLOP = ** Dealing down cards ** # PREFLOP = ** Dealing down cards **
@ -323,7 +332,7 @@ follow : whether to tail -f the input"""
hand.setCommunityCards(street, renderCards(m.group('CARDS'))) hand.setCommunityCards(street, renderCards(m.group('CARDS')))
def readAntes(self, hand): def readAntes(self, hand):
logging.debug("reading antes") log.debug("reading antes")
m = self.re_Antes.finditer(hand.handText) m = self.re_Antes.finditer(hand.handText)
for player in m: for player in m:
hand.addAnte(player.group('PNAME'), player.group('ANTE')) hand.addAnte(player.group('PNAME'), player.group('ANTE'))
@ -426,11 +435,11 @@ follow : whether to tail -f the input"""
def ringBlinds(ringLimit): def ringBlinds(ringLimit):
"Returns blinds for current limit" "Returns blinds for current limit"
ringLimit = float(ringLimit) ringLimit = float(clearMoneyString(ringLimit))
if ringLimit == 5.: ringLimit = 4. if ringLimit == 5.: ringLimit = 4.
return ('%.2f' % (ringLimit/200.), '%.2f' % (ringLimit/100.) ) return ('%.2f' % (ringLimit/200.), '%.2f' % (ringLimit/100.) )
def renderTrnyMoney(money): def clearMoneyString(money):
"renders 'numbers' like '1 200' and '2,000'" "renders 'numbers' like '1 200' and '2,000'"
return money.replace(' ', '').replace(',', '') return money.replace(' ', '').replace(',', '')
@ -454,7 +463,4 @@ if __name__ == "__main__":
(options, args) = parser.parse_args() (options, args) = parser.parse_args()
#LOG_FILENAME = './logging.out'
logging.basicConfig(level=options.verbosity)
e = PartyPoker(in_path = options.ipath, out_path = options.opath, follow = options.follow) e = PartyPoker(in_path = options.ipath, out_path = options.opath, follow = options.follow)

View File

@ -19,6 +19,7 @@
######################################################################## ########################################################################
# TODO: straighten out discards for draw games # TODO: straighten out discards for draw games
import sys import sys
from HandHistoryConverter import * from HandHistoryConverter import *
@ -26,8 +27,12 @@ from HandHistoryConverter import *
class PokerStars(HandHistoryConverter): class PokerStars(HandHistoryConverter):
############################################################ # Class Variables
# Class Variables
sitename = "PokerStars"
filetype = "text"
codepage = "cp1252"
siteId = 2 # Needs to match id entry in Sites database
mixes = { 'HORSE': 'horse', '8-Game': '8game', 'HOSE': 'hose'} # Legal mixed games mixes = { 'HORSE': 'horse', '8-Game': '8game', 'HOSE': 'hose'} # Legal mixed games
sym = {'USD': "\$", 'CAD': "\$", 'T$': "", "EUR": "\x80", "GBP": "\xa3"} # ADD Euro, Sterling, etc HERE sym = {'USD': "\$", 'CAD': "\$", 'T$': "", "EUR": "\x80", "GBP": "\xa3"} # ADD Euro, Sterling, etc HERE
@ -77,20 +82,6 @@ class PokerStars(HandHistoryConverter):
# self.re_setHandInfoRegex('.*#(?P<HID>[0-9]+): Table (?P<TABLE>[ a-zA-Z]+) - \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) - (?P<GAMETYPE>.*) - (?P<HR>[0-9]+):(?P<MIN>[0-9]+) ET - (?P<YEAR>[0-9]+)/(?P<MON>[0-9]+)/(?P<DAY>[0-9]+)Table (?P<TABLE>[ a-zA-Z]+)\nSeat (?P<BUTTON>[0-9]+)') # self.re_setHandInfoRegex('.*#(?P<HID>[0-9]+): Table (?P<TABLE>[ a-zA-Z]+) - \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) - (?P<GAMETYPE>.*) - (?P<HR>[0-9]+):(?P<MIN>[0-9]+) ET - (?P<YEAR>[0-9]+)/(?P<MON>[0-9]+)/(?P<DAY>[0-9]+)Table (?P<TABLE>[ a-zA-Z]+)\nSeat (?P<BUTTON>[0-9]+)')
def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True, index=0):
"""\
in_path (default '-' = sys.stdin)
out_path (default '-' = sys.stdout)
follow : whether to tail -f the input"""
HandHistoryConverter.__init__(self, in_path, out_path, sitename="PokerStars", follow=follow, index=index)
logging.info("Initialising PokerStars converter class")
self.filetype = "text"
self.codepage = "cp1252"
self.siteId = 2 # Needs to match id entry in Sites database
if autostart:
self.start()
def compilePlayerRegexs(self, hand): def compilePlayerRegexs(self, hand):
players = set([player[1] for player in hand.players]) players = set([player[1] for player in hand.players])
if not players <= self.compiledPlayers: # x <= y means 'x is subset of y' if not players <= self.compiledPlayers: # x <= y means 'x is subset of y'
@ -388,7 +379,4 @@ if __name__ == "__main__":
(options, args) = parser.parse_args() (options, args) = parser.parse_args()
LOG_FILENAME = './logging.out'
logging.basicConfig(filename=LOG_FILENAME,level=options.verbosity)
e = PokerStars(in_path = options.ipath, out_path = options.opath, follow = options.follow) e = PokerStars(in_path = options.ipath, out_path = options.opath, follow = options.follow)

View File

@ -230,11 +230,20 @@ def discover_nt_by_name(c, tablename):
"""Finds poker client window with the given table name.""" """Finds poker client window with the given table name."""
titles = {} titles = {}
win32gui.EnumWindows(win_enum_handler, titles) win32gui.EnumWindows(win_enum_handler, titles)
def getDefaultEncoding():
# FIXME: if somebody know better place fot this function - move it
# FIXME: it's better to use GetCPInfo for windows http://msdn.microsoft.com/en-us/library/dd318078(VS.85).aspx
# but i have no idea, how to call it
import locale
return locale.getpreferredencoding()
for hwnd in titles: for hwnd in titles:
#print "Tables.py: tablename =", tablename, "title =", titles[hwnd] #print "Tables.py: tablename =", tablename, "title =", titles[hwnd]
try: try:
# maybe it's better to make global titles[hwnd] decoding?
# this can blow up in XP on some windows, eg firefox displaying http://docs.python.org/tutorial/classes.html # this can blow up in XP on some windows, eg firefox displaying http://docs.python.org/tutorial/classes.html
if not tablename.lower() in titles[hwnd].lower(): continue if not tablename.lower() in titles[hwnd].decode(getDefaultEncoding()).lower(): continue
except: except:
continue continue
if 'History for table:' in titles[hwnd]: continue # Everleaf Network HH viewer window if 'History for table:' in titles[hwnd]: continue # Everleaf Network HH viewer window

View File

@ -26,6 +26,11 @@ from HandHistoryConverter import *
class Win2day(HandHistoryConverter): class Win2day(HandHistoryConverter):
sitename = "Win2day"
filetype = "text"
codepage = "cp1252"
siteID = 4
# Static regexes # Static regexes
#<HISTORY ID="102271403" SESSION="session31237702.xml" TABLE="Innsbruck 3" GAME="GAME_THM" GAMETYPE="GAMETYPE_REAL" GAMEKIND="GAMEKIND_CASH" TABLECURRENCY="EUR" LIMIT="NL" STAKES="0.25/0.50" DATE="1246909773" WIN="0.00" LOSS="0.50"> #<HISTORY ID="102271403" SESSION="session31237702.xml" TABLE="Innsbruck 3" GAME="GAME_THM" GAMETYPE="GAMETYPE_REAL" GAMEKIND="GAMEKIND_CASH" TABLECURRENCY="EUR" LIMIT="NL" STAKES="0.25/0.50" DATE="1246909773" WIN="0.00" LOSS="0.50">
@ -39,15 +44,6 @@ class Win2day(HandHistoryConverter):
re_Card = re.compile('^<CARD LINK="(?P<CARD>[0-9]+)"></CARD>', re.MULTILINE) re_Card = re.compile('^<CARD LINK="(?P<CARD>[0-9]+)"></CARD>', re.MULTILINE)
re_BoardLast = re.compile('^<CARD LINK="(?P<CARD>[0-9]+)"></CARD></ACTION>', re.MULTILINE) re_BoardLast = re.compile('^<CARD LINK="(?P<CARD>[0-9]+)"></CARD></ACTION>', re.MULTILINE)
def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True, index=0):
HandHistoryConverter.__init__(self, in_path, out_path, sitename="Win2day", follow=follow, index=index)
logging.info("Initialising Win2day converter class")
self.filetype = "text"
self.codepage = "cp1252"
self.sideID = 4
if autostart:
self.start()
def compilePlayerRegexs(self, hand): def compilePlayerRegexs(self, hand):
players = set([player[1] for player in hand.players]) players = set([player[1] for player in hand.players])
@ -65,7 +61,7 @@ class Win2day(HandHistoryConverter):
self.re_PostBoth = re.compile(r'^<ACTION TYPE="HAND_BLINDS" PLAYER="%s" KIND="HAND_AB" VALUE="(?P<SBBB>[.0-9]+)"></ACTION>' % player_re, re.MULTILINE) self.re_PostBoth = re.compile(r'^<ACTION TYPE="HAND_BLINDS" PLAYER="%s" KIND="HAND_AB" VALUE="(?P<SBBB>[.0-9]+)"></ACTION>' % player_re, re.MULTILINE)
#r'<ACTION TYPE="HAND_DEAL" PLAYER="%s">\n<CARD LINK="(?P<CARD1>[0-9]+)"></CARD>\n<CARD LINK="(?P<CARD2>[0-9]+)"></CARD></ACTION>' #r'<ACTION TYPE="HAND_DEAL" PLAYER="%s">\n<CARD LINK="(?P<CARD1>[0-9]+)"></CARD>\n<CARD LINK="(?P<CARD2>[0-9]+)"></CARD></ACTION>'
self.re_HeroCards = re.compile(r'<ACTION TYPE="HAND_DEAL" PLAYER="%s">\n(?P<CARDS><CARD LINK="[0-9]+"></CARD>\n<CARD LINK="[0-9]"></CARD>)</ACTION>' % player_re, re.MULTILINE) self.re_HeroCards = re.compile(r'<ACTION TYPE="HAND_DEAL" PLAYER="%s">\n(?P<CARDS><CARD LINK="[0-9]+"></CARD>\n<CARD LINK="[0-9]+"></CARD>)</ACTION>' % player_re, re.MULTILINE)
#'^<ACTION TYPE="(?P<ATYPE>[_A-Z]+)" PLAYER="%s"( VALUE="(?P<BET>[.0-9]+)")?></ACTION>' #'^<ACTION TYPE="(?P<ATYPE>[_A-Z]+)" PLAYER="%s"( VALUE="(?P<BET>[.0-9]+)")?></ACTION>'
self.re_Action = re.compile(r'^<ACTION TYPE="(?P<ATYPE>[_A-Z]+)" PLAYER="%s"( VALUE="(?P<BET>[.0-9]+)")?></ACTION>' % player_re, re.MULTILINE) self.re_Action = re.compile(r'^<ACTION TYPE="(?P<ATYPE>[_A-Z]+)" PLAYER="%s"( VALUE="(?P<BET>[.0-9]+)")?></ACTION>' % player_re, re.MULTILINE)

40
pyfpdb/logging.conf Normal file
View File

@ -0,0 +1,40 @@
[loggers]
keys=root,parser
[handlers]
keys=consoleHandler,fileHandler
[formatters]
keys=fileFormatter,stderrFormatter
[logger_root]
level=INFO
handlers=consoleHandler,fileHandler
[logger_parser]
level=INFO
# set to NOTSET or DEBUG to see everything the parser does
handlers=consoleHandler,fileHandler
qualname=parser
propagate=0
[handler_consoleHandler]
class=StreamHandler
level=INFO
formatter=stderrFormatter
args=(sys.stderr,)
[handler_fileHandler]
class=FileHandler
level=INFO
formatter=fileFormatter
args=('logging.out', 'a')
[formatter_fileFormatter]
format=%(asctime)s - %(name)-12s %(levelname)-8s %(message)s
datefmt=
[formatter_stderrFormatter]
format=%(name)-12s: %(levelname)-8s %(message)s
datefmt=

View File

@ -4,7 +4,12 @@ import py
# regression-test-files/fulltilt/nlhe/NLHE-6max-1.txt # regression-test-files/fulltilt/nlhe/NLHE-6max-1.txt
# Sorrowful: start: $8.85 end: $14.70 total: $5.85 # Sorrowful: start: $8.85 end: $14.70 total: $5.85
# 'Canceled' hand
# regression-test-files/fulltilt/lh/Marlin.txt
def checkGameInfo(hhc, header, info): def checkGameInfo(hhc, header, info):
assert hhc.determineGameType(header) == info assert hhc.determineGameType(header) == info

View File

@ -14,7 +14,7 @@ text = ""
hhc = PokerStarsToFpdb.PokerStars(autostart=False) hhc = PokerStarsToFpdb.PokerStars(autostart=False)
h = HoldemOmahaHand(None, "ASite", gametype, text, builtFrom = "Test") h = HoldemOmahaHand(None, "PokerStars", gametype, text, builtFrom = "Test")
h.addPlayer("1", "s0rrow", "100000") h.addPlayer("1", "s0rrow", "100000")
hhc.compilePlayerRegexs(h) hhc.compilePlayerRegexs(h)