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

Conflicts:

	pyfpdb/GuiBulkImport.py
	pyfpdb/Hud.py
This commit is contained in:
Ray 2009-02-26 12:01:18 -05:00
commit 20514bd0cb
12 changed files with 651 additions and 267 deletions

View File

@ -62,6 +62,7 @@ class Site:
self.aux_window = node.getAttribute("aux_window") self.aux_window = node.getAttribute("aux_window")
self.font = node.getAttribute("font") self.font = node.getAttribute("font")
self.font_size = node.getAttribute("font_size") self.font_size = node.getAttribute("font_size")
self.use_frames = node.getAttribute("use_frames")
self.layout = {} self.layout = {}
for layout_node in node.getElementsByTagName('layout'): for layout_node in node.getElementsByTagName('layout'):
@ -189,7 +190,7 @@ class Import:
def __init__(self, node): def __init__(self, node):
self.interval = node.getAttribute("interval") self.interval = node.getAttribute("interval")
self.callFpdbHud = node.getAttribute("callFpdbHud") self.callFpdbHud = node.getAttribute("callFpdbHud")
self.hhArchiveBase = node.getAttribute("hhArchiveBase") self.hhArchiveBase = node.getAttribute("hhArchiveBase")
def __str__(self): def __str__(self):
return " interval = %s\n callFpdbHud = %s\n hhArchiveBase = %s" % (self.interval, self.callFpdbHud, self.hhArchiveBase) return " interval = %s\n callFpdbHud = %s\n hhArchiveBase = %s" % (self.interval, self.callFpdbHud, self.hhArchiveBase)
@ -465,6 +466,9 @@ class Config:
paths['hud-defaultPath'] = "default" paths['hud-defaultPath'] = "default"
paths['bulkImport-defaultPath'] = "default" paths['bulkImport-defaultPath'] = "default"
return paths return paths
def get_frames(self, site = "PokerStars"):
return self.supported_sites[site].use_frames == "True"
def get_default_colors(self, site = "PokerStars"): def get_default_colors(self, site = "PokerStars"):
colors = {} colors = {}
@ -665,4 +669,4 @@ if __name__== "__main__":
for game in c.get_supported_games(): for game in c.get_supported_games():
print c.get_game_parameters(game) print c.get_game_parameters(game)
print "start up path = ", c.execution_path("") print "start up path = ", c.execution_path("")

View File

@ -20,88 +20,94 @@
import sys import sys
import Configuration import Configuration
from HandHistoryConverter import * from HandHistoryConverter import *
from time import strftime
# Everleaf HH format # Class for converting Everleaf HH format.
# Everleaf Gaming Game #55208539
# ***** Hand history for game #55208539 *****
# Blinds $0.50/$1 NL Hold'em - 2008/09/01 - 13:35:01
# Table Speed Kuala
# Seat 1 is the button
# Total number of players: 9
# Seat 1: BadBeatBox ( $ 98.97 USD )
# Seat 3: EricBlade ( $ 73.96 USD )
# Seat 4: randy888 ( $ 196.50 USD )
# Seat 5: BaronSengir ( $ 182.80 USD )
# Seat 6: dogge ( $ 186.06 USD )
# Seat 7: wings ( $ 50 USD )
# Seat 8: schoffeltje ( $ 282.05 USD )
# Seat 9: harrydebeng ( $ 109.45 USD )
# Seat 10: smaragdar ( $ 96.50 USD )
# EricBlade: posts small blind [$ 0.50 USD]
# randy888: posts big blind [$ 1 USD]
# wings: posts big blind [$ 1 USD]
# ** Dealing down cards **
# Dealt to EricBlade [ qc, 3c ]
# BaronSengir folds
# dogge folds
# wings raises [$ 2.50 USD]
# schoffeltje folds
# harrydebeng calls [$ 3.50 USD]
# smaragdar raises [$ 15.50 USD]
# BadBeatBox folds
# EricBlade folds
# randy888 folds
# wings calls [$ 12 USD]
# harrydebeng folds
# ** Dealing Flop ** [ qs, 3d, 8h ]
# wings: bets [$ 34.50 USD]
# smaragdar calls [$ 34.50 USD]
# ** Dealing Turn ** [ 2d ]
# ** Dealing River ** [ 6c ]
# dogge shows [ 9h, 9c ]a pair of nines
# spicybum shows [ 5d, 6d ]a straight, eight high
# harrydebeng does not show cards
# smaragdar wins $ 102 USD from main pot with a pair of aces [ ad, ah, qs, 8h, 6c ]
class Everleaf(HandHistoryConverter): class Everleaf(HandHistoryConverter):
# Static regexes
re_SplitHands = re.compile(r"\n\n+")
re_GameInfo = re.compile(r".*Blinds \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) (?P<LTYPE>(NL|PL)) (?P<GAME>(Hold\'em|Omaha|7 Card Stud))")
re_HandInfo = re.compile(r".*#(?P<HID>[0-9]+)\n.*\nBlinds \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) (?P<GAMETYPE>.*) - (?P<DATETIME>\d\d\d\d/\d\d/\d\d - \d\d:\d\d:\d\d)\nTable (?P<TABLE>[- a-zA-Z]+)")
re_Button = re.compile(r"^Seat (?P<BUTTON>\d+) is the button", re.MULTILINE)
re_PlayerInfo = re.compile(r"^Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \(\s+(\$ (?P<CASH>[.0-9]+) USD|new player|All-in) \)", re.MULTILINE)
re_Board = re.compile(r"\[ (?P<CARDS>.+) \]")
def __init__(self, config, file): def __init__(self, config, file):
print "Initialising Everleaf converter class" print "Initialising Everleaf converter class"
HandHistoryConverter.__init__(self, config, file, sitename="Everleaf") # Call super class init. HandHistoryConverter.__init__(self, config, file, sitename="Everleaf") # Call super class init.
self.sitename = "Everleaf" self.sitename = "Everleaf"
self.setFileType("text", "cp1252") self.setFileType("text", "cp1252")
self.rexx.setGameInfoRegex('.*Blinds \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+)')
self.rexx.setSplitHandRegex('\n\n+')
self.rexx.setHandInfoRegex('.*#(?P<HID>[0-9]+)\n.*\nBlinds \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) (?P<GAMETYPE>.*) - (?P<DATETIME>\d\d\d\d/\d\d/\d\d - \d\d:\d\d:\d\d)\nTable (?P<TABLE>[ a-zA-Z]+)\nSeat (?P<BUTTON>[0-9]+)') try:
self.rexx.setPlayerInfoRegex('Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \(\s+(\$ (?P<CASH>[.0-9]+) USD|new player|All-in) \)') self.ofile = os.path.join(self.hhdir, file.split(os.path.sep)[-2]+"-"+os.path.basename(file))
self.rexx.setPostSbRegex('.*\n(?P<PNAME>.*): posts small blind \[\$? (?P<SB>[.0-9]+)') except:
self.rexx.setPostBbRegex('.*\n(?P<PNAME>.*): posts big blind \[\$? (?P<BB>[.0-9]+)') self.ofile = os.path.join(self.hhdir, "x"+strftime("%d-%m-%y")+os.path.basename(file))
self.rexx.setPostBothRegex('.*\n(?P<PNAME>.*): posts small \& big blinds \[\$? (?P<SBBB>[.0-9]+)')
self.rexx.setHeroCardsRegex('.*\nDealt\sto\s(?P<PNAME>.*)\s\[ (?P<CARDS>.*) \]') def compilePlayerRegexs(self):
self.rexx.setActionStepRegex('.*\n(?P<PNAME>.*)(?P<ATYPE>: bets| checks| raises| calls| folds)(\s\[\$ (?P<BET>[.\d]+) (USD|EUR)\])?') player_re = "(?P<PNAME>" + "|".join(map(re.escape, self.players)) + ")"
self.rexx.setShowdownActionRegex('.*\n(?P<PNAME>.*) shows \[ (?P<CARDS>.*) \]') #print "DEBUG player_re: " + player_re
self.rexx.setCollectPotRegex('.*\n(?P<PNAME>.*) wins \$ (?P<POT>[.\d]+) (USD|EUR)(.*?\[ (?P<CARDS>.*?) \])?') self.re_PostSB = re.compile(r"^%s: posts small blind \[\$? (?P<SB>[.0-9]+)" % player_re, re.MULTILINE)
#self.rexx.setCollectPotRegex('.*\n(?P<PNAME>.*) wins \$ (?P<POT>[.\d]+) USD(.*\[ (?P<CARDS>) \S\S, \S\S, \S\S, \S\S, \S\S \])?') self.re_PostBB = re.compile(r"^%s: posts big blind \[\$? (?P<BB>[.0-9]+)" % player_re, re.MULTILINE)
self.rexx.sits_out_re = re.compile('(?P<PNAME>.*) sits out') self.re_PostBoth = re.compile(r"^%s: posts both blinds \[\$? (?P<SBBB>[.0-9]+)" % player_re, re.MULTILINE)
self.rexx.compileRegexes() self.re_HeroCards = re.compile(r"^Dealt to %s \[ (?P<CARDS>.*) \]" % player_re, re.MULTILINE)
self.re_Action = re.compile(r"^%s(?P<ATYPE>: bets| checks| raises| calls| folds)(\s\[\$ (?P<BET>[.\d]+) (USD|EUR)\])?" % player_re, re.MULTILINE)
self.re_ShowdownAction = re.compile(r"^%s shows \[ (?P<CARDS>.*) \]" % player_re, re.MULTILINE)
self.re_CollectPot = re.compile(r"^%s wins \$ (?P<POT>[.\d]+) (USD|EUR)(.*?\[ (?P<CARDS>.*?) \])?" % player_re, re.MULTILINE)
self.re_SitsOut = re.compile(r"^%s sits out" % player_re, re.MULTILINE)
def readSupportedGames(self): def readSupportedGames(self):
pass return [["ring", "hold", "nl"],
["ring", "hold", "pl"],
["ring", "omaha", "pl"]
]
def determineGameType(self): def determineGameType(self):
# Cheating with this regex, only support nlhe at the moment # Cheating with this regex, only support nlhe at the moment
gametype = ["ring", "hold", "nl"] # Blinds $0.50/$1 PL Omaha - 2008/12/07 - 21:59:48
# Blinds $0.05/$0.10 NL Hold'em - 2009/02/21 - 11:21:57
m = self.rexx.game_info_re.search(self.obs) # $0.25/$0.50 7 Card Stud - 2008/12/05 - 21:43:59
gametype = gametype + [m.group('SB')]
gametype = gametype + [m.group('BB')]
# Tourney:
# Everleaf Gaming Game #75065769
# ***** Hand history for game #75065769 *****
# Blinds 10/20 NL Hold'em - 2009/02/25 - 17:30:32
# Table 2
structure = "" # nl, pl, cn, cp, fl
game = ""
m = self.re_GameInfo.search(self.obs)
if m == None:
return None
if m.group('LTYPE') == "NL":
structure = "nl"
elif m.group('LTYPE') == "PL":
structure = "pl"
else:
structure = "fl" # we don't support it, but there should be how to detect it at least.
if m.group('GAME') == "Hold\'em":
game = "hold"
elif m.group('GAME') == "Omaha":
game = "omahahi"
elif m.group('GAME') == "7 Card Stud":
game = "studhi" # Everleaf currently only does Hi stud
gametype = ["ring", game, structure, m.group('SB'), m.group('BB')]
return gametype return gametype
def readHandInfo(self, hand): def readHandInfo(self, hand):
m = self.rexx.hand_info_re.search(hand.string) m = self.re_HandInfo.search(hand.string)
if(m == None):
print "DEBUG: re_HandInfo.search failed: '%s'" %(hand.string)
hand.handid = m.group('HID') hand.handid = m.group('HID')
hand.tablename = m.group('TABLE') hand.tablename = m.group('TABLE')
hand.max_seats = 6 # assume 6-max unless we have proof it's a larger/smaller game, since everleaf doesn't give seat max info
# These work, but the info is already in the Hand class - should be used for tourneys though. # These work, but the info is already in the Hand class - should be used for tourneys though.
# m.group('SB') # m.group('SB')
# m.group('BB') # m.group('BB')
@ -115,14 +121,17 @@ class Everleaf(HandHistoryConverter):
#TODO: Do conversion from GMT to ET #TODO: Do conversion from GMT to ET
#TODO: Need some date functions to convert to different timezones (Date::Manip for perl rocked for this) #TODO: Need some date functions to convert to different timezones (Date::Manip for perl rocked for this)
hand.starttime = time.strptime(m.group('DATETIME'), "%Y/%m/%d - %H:%M:%S") hand.starttime = time.strptime(m.group('DATETIME'), "%Y/%m/%d - %H:%M:%S")
hand.buttonpos = int(m.group('BUTTON'))
def readPlayerStacks(self, hand): def readPlayerStacks(self, hand):
m = self.rexx.player_info_re.finditer(hand.string) m = self.re_PlayerInfo.finditer(hand.string)
players = []
for a in m: for a in m:
hand.addPlayer(int(a.group('SEAT')), a.group('PNAME'), a.group('CASH')) seatnum = int(a.group('SEAT'))
hand.addPlayer(seatnum, a.group('PNAME'), a.group('CASH'))
if seatnum > 6:
hand.max_seats = 10 # everleaf currently does 2/6/10 games, so if seats > 6 are in use, it must be 10-max.
# TODO: implement lookup list by table-name to determine maxes, then fall back to 6 default/10 here, if there's no entry in the list?
def markStreets(self, hand): def markStreets(self, hand):
# PREFLOP = ** Dealing down cards ** # PREFLOP = ** Dealing down cards **
# This re fails if, say, river is missing; then we don't get the ** that starts the river. # This re fails if, say, river is missing; then we don't get the ** that starts the river.
@ -137,25 +146,29 @@ class Everleaf(HandHistoryConverter):
def readCommunityCards(self, hand, street): # street has been matched by markStreets, so exists in this hand def readCommunityCards(self, hand, street): # street has been matched by markStreets, so exists in this hand
self.rexx.board_re = re.compile(r"\[ (?P<CARDS>.+) \]") #print "DEBUG " + street + ":"
print hand.streets.group(street) #print hand.streets.group(street) + "\n"
if street in ('FLOP','TURN','RIVER'): # a list of streets which get dealt community cards (i.e. all but PREFLOP) if street in ('FLOP','TURN','RIVER'): # a list of streets which get dealt community cards (i.e. all but PREFLOP)
m = self.rexx.board_re.search(hand.streets.group(street)) m = self.re_Board.search(hand.streets.group(street))
hand.setCommunityCards(street, m.group('CARDS').split(', ')) hand.setCommunityCards(street, m.group('CARDS').split(', '))
def readBlinds(self, hand): def readBlinds(self, hand):
try: try:
m = self.rexx.small_blind_re.search(hand.string) m = self.re_PostSB.search(hand.string)
hand.addBlind(m.group('PNAME'), 'small blind', m.group('SB')) hand.addBlind(m.group('PNAME'), 'small blind', m.group('SB'))
except: # no small blind except Exception, e: # no small blind
#print e
hand.addBlind(None, None, None) hand.addBlind(None, None, None)
for a in self.rexx.big_blind_re.finditer(hand.string): for a in self.re_PostBB.finditer(hand.string):
hand.addBlind(a.group('PNAME'), 'big blind', a.group('BB')) hand.addBlind(a.group('PNAME'), 'big blind', a.group('BB'))
for a in self.rexx.both_blinds_re.finditer(hand.string): for a in self.re_PostBoth.finditer(hand.string):
hand.addBlind(a.group('PNAME'), 'small & big blinds', a.group('SBBB')) hand.addBlind(a.group('PNAME'), 'both', a.group('SBBB'))
def readButton(self, hand):
hand.buttonpos = int(self.re_Button.search(hand.string).group('BUTTON'))
def readHeroCards(self, hand): def readHeroCards(self, hand):
m = self.rexx.hero_cards_re.search(hand.string) m = self.re_HeroCards.search(hand.string)
if(m == None): if(m == None):
#Not involved in hand #Not involved in hand
hand.involved = False hand.involved = False
@ -168,7 +181,7 @@ class Everleaf(HandHistoryConverter):
hand.addHoleCards(cards, m.group('PNAME')) hand.addHoleCards(cards, m.group('PNAME'))
def readAction(self, hand, street): def readAction(self, hand, street):
m = self.rexx.action_re.finditer(hand.streets.group(street)) m = self.re_Action.finditer(hand.streets.group(street))
for action in m: for action in m:
if action.group('ATYPE') == ' raises': if action.group('ATYPE') == ' raises':
hand.addCallandRaise( street, action.group('PNAME'), action.group('BET') ) hand.addCallandRaise( street, action.group('PNAME'), action.group('BET') )
@ -185,17 +198,17 @@ class Everleaf(HandHistoryConverter):
def readShowdownActions(self, hand): def readShowdownActions(self, hand):
for shows in self.rexx.showdown_action_re.finditer(hand.string): for shows in self.re_ShowdownAction.finditer(hand.string):
cards = shows.group('CARDS') cards = shows.group('CARDS')
cards = set(cards.split(', ')) cards = set(cards.split(', '))
hand.addShownCards(cards, shows.group('PNAME')) hand.addShownCards(cards, shows.group('PNAME'))
def readCollectPot(self,hand): def readCollectPot(self,hand):
for m in self.rexx.collect_pot_re.finditer(hand.string): for m in self.re_CollectPot.finditer(hand.string):
hand.addCollectPot(player=m.group('PNAME'),pot=m.group('POT')) hand.addCollectPot(player=m.group('PNAME'),pot=m.group('POT'))
def readShownCards(self,hand): def readShownCards(self,hand):
for m in self.rexx.collect_pot_re.finditer(hand.string): for m in self.re_CollectPot.finditer(hand.string):
if m.group('CARDS') is not None: if m.group('CARDS') is not None:
cards = m.group('CARDS') cards = m.group('CARDS')
cards = set(cards.split(', ')) cards = set(cards.split(', '))

View File

@ -20,94 +20,82 @@ import sys
import Configuration import Configuration
from HandHistoryConverter import * from HandHistoryConverter import *
# FullTilt HH Format # FullTilt HH Format converter
#Full Tilt Poker Game #9403951181: Table CR - tay - $0.05/$0.10 - No Limit Hold'em - 9:40:20 ET - 2008/12/09
#Seat 1: rigoise ($15.95)
#Seat 2: K2dream ($6.70)
#Seat 4: ravens2216 ($10)
#Seat 5: rizkouner ($4)
#Seat 6: Sorrowful ($8.35)
#rigoise posts the small blind of $0.05
#K2dream posts the big blind of $0.10
#5 seconds left to act
#rizkouner posts $0.10
#The button is in seat #6
#*** HOLE CARDS ***
#Dealt to Sorrowful [8h Qc]
#ravens2216 folds
#rizkouner checks
#Sorrowful has 15 seconds left to act
#Sorrowful folds
#rigoise folds
#K2dream checks
#*** FLOP *** [9d Kc 5c]
#K2dream checks
#rizkouner checks
#*** TURN *** [9d Kc 5c] [5h]
#K2dream has 15 seconds left to act
#K2dream bets $0.20
#rizkouner calls $0.20
#*** RIVER *** [9d Kc 5c 5h] [6h]
#K2dream checks
#rizkouner has 15 seconds left to act
#rizkouner bets $0.20
#K2dream folds
#Uncalled bet of $0.20 returned to rizkouner
#rizkouner mucks
#rizkouner wins the pot ($0.60)
#*** SUMMARY ***
#Total pot $0.65 | Rake $0.05
#Board: [9d Kc 5c 5h 6h]
#Seat 1: rigoise (small blind) folded before the Flop
#Seat 2: K2dream (big blind) folded on the River
#Seat 4: ravens2216 didn't bet (folded)
#Seat 5: rizkouner collected ($0.60), mucked
#Seat 6: Sorrowful (button) didn't bet (folded)
#Seat N: rizkouner (button) showed [Jh Ah] and won ($0.70) with a pair of Threes
class FullTilt(HandHistoryConverter): class FullTilt(HandHistoryConverter):
# Static regexes
re_GameInfo = re.compile('- \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) (Ante \$(?P<ANTE>[.0-9]+) )?- (?P<LTYPE>(No|Pot)? )?Limit (?P<GAME>(Hold\'em|Omaha|Razz))')
re_SplitHands = re.compile(r"\n\n+")
re_HandInfo = re.compile('.*#(?P<HID>[0-9]+): Table (?P<TABLE>[- a-zA-Z]+) (\((?P<TABLEATTRIBUTES>.+)\) )?- \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) (Ante \$(?P<ANTE>[.0-9]+) )?- (?P<GAMETYPE>[a-zA-Z\' ]+) - (?P<DATETIME>.*)')
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]+)\)\n')
re_Board = re.compile(r"\[(?P<CARDS>.+)\]")
def __init__(self, config, file): def __init__(self, config, file):
print "Initialising FullTilt converter class" print "Initialising FullTilt converter class"
HandHistoryConverter.__init__(self, config, file, sitename="FullTilt") # Call super class init. HandHistoryConverter.__init__(self, config, file, sitename="FullTilt") # Call super class init.
self.sitename = "FullTilt" self.sitename = "FullTilt"
self.setFileType("text", "cp1252") self.setFileType("text", "cp1252")
self.rexx.setGameInfoRegex('- \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) -')
self.rexx.setSplitHandRegex('\n\n+') def compilePlayerRegexs(self):
self.rexx.setHandInfoRegex('.*#(?P<HID>[0-9]+): Table (?P<TABLE>[- a-zA-Z]+) (\((?P<TABLEATTRIBUTES>.+)\) )?- \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) - (?P<GAMETYPE>[a-zA-Z\' ]+) - (?P<DATETIME>.*)') player_re = "(?P<PNAME>" + "|".join(map(re.escape, self.players)) + ")"
# self.rexx.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]+)') print "DEBUG player_re: " + player_re
self.rexx.button_re = re.compile('The button is in seat #(?P<BUTTON>\d+)') self.re_PostSB = re.compile(r"^%s posts the small blind of \$?(?P<SB>[.0-9]+)" % player_re, re.MULTILINE)
self.rexx.setPlayerInfoRegex('Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \(\$(?P<CASH>[.0-9]+)\)\n') self.re_PostBB = re.compile(r"^%s posts (the big blind of )?\$?(?P<BB>[.0-9]+)" % player_re, re.MULTILINE)
self.rexx.setPostSbRegex('.*\n(?P<PNAME>.*) posts the small blind of \$?(?P<SB>[.0-9]+)') self.re_Antes = re.compile(r"^%s antes \$?(?P<ANTE>[.0-9]+)" % player_re, re.MULTILINE)
self.rexx.setPostBbRegex('.*\n(?P<PNAME>.*) posts (the big blind of )?\$?(?P<BB>[.0-9]+)') self.re_BringIn = re.compile(r"^%s brings in for \$?(?P<BRINGIN>[.0-9]+)" % player_re, re.MULTILINE)
self.rexx.setPostBothRegex('.*\n(?P<PNAME>.*) posts small \& big blinds \[\$? (?P<SBBB>[.0-9]+)') self.re_PostBoth = re.compile(r"^%s posts small \& big blinds \[\$? (?P<SBBB>[.0-9]+)" % player_re, re.MULTILINE)
self.rexx.setHeroCardsRegex('.*\nDealt\sto\s(?P<PNAME>.*)\s\[(?P<CARDS>.*)\]') self.re_HeroCards = re.compile(r"^Dealt to %s \[(?P<CARDS>[AKQJT0-9hcsd ]+)\]( \[(?P<NEWCARD>[AKQJT0-9hcsd ]+)\])?" % player_re, re.MULTILINE)
self.rexx.setActionStepRegex('.*\n(?P<PNAME>.*)(?P<ATYPE> bets| checks| raises to| calls| folds)(\s\$(?P<BET>[.\d]+))?') self.re_Action = re.compile(r"^%s(?P<ATYPE> bets| checks| raises to| calls| folds)(\s\$(?P<BET>[.\d]+))?" % player_re, re.MULTILINE)
self.rexx.setShowdownActionRegex('.*\n(?P<PNAME>.*) shows \[(?P<CARDS>.*)\]') self.re_ShowdownAction = re.compile(r"^%s shows \[(?P<CARDS>.*)\]" % player_re, re.MULTILINE)
self.rexx.setCollectPotRegex(r"Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*?) (\(button\) |\(small blind\) |\(big blind\) )?(collected|showed \[.*\] and won) \(\$(?P<POT>[.\d]+)\)(, mucked| with.*)") self.re_CollectPot = re.compile(r"^Seat (?P<SEAT>[0-9]+): %s (\(button\) |\(small blind\) |\(big blind\) )?(collected|showed \[.*\] and won) \(\$(?P<POT>[.\d]+)\)(, mucked| with.*)" % player_re, re.MULTILINE)
self.rexx.shown_cards_re = re.compile('Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \(.*\) showed \[(?P<CARDS>.*)\].*') self.re_SitsOut = re.compile(r"^%s sits out" % player_re, re.MULTILINE)
self.rexx.sits_out_re = re.compile('(?P<PNAME>.*) sits out') self.re_ShownCards = re.compile(r"^Seat (?P<SEAT>[0-9]+): %s \(.*\) showed \[(?P<CARDS>.*)\].*" % player_re, re.MULTILINE)
self.rexx.compileRegexes()
def readSupportedGames(self): def readSupportedGames(self):
pass return [["ring", "hold", "nl"],
["ring", "hold", "pl"],
["ring", "razz", "fl"],
["ring", "omaha", "pl"]
]
def determineGameType(self): def determineGameType(self):
# Cheating with this regex, only support nlhe at the moment # Cheating with this regex, only support nlhe at the moment
gametype = ["ring", "hold", "nl"] # Full Tilt Poker Game #10777181585: Table Deerfly (deep 6) - $0.01/$0.02 - Pot Limit Omaha Hi - 2:24:44 ET - 2009/02/22
# Full Tilt Poker Game #10773265574: Table Butte (6 max) - $0.01/$0.02 - Pot Limit Hold'em - 21:33:46 ET - 2009/02/21
# Full Tilt Poker Game #9403951181: Table CR - tay - $0.05/$0.10 - No Limit Hold'em - 9:40:20 ET - 2008/12/09
structure = "" # nl, pl, cn, cp, fl
game = ""
m = self.rexx.game_info_re.search(self.obs)
gametype = gametype + [m.group('SB')] m = self.re_GameInfo.search(self.obs)
gametype = gametype + [m.group('BB')] if m.group('LTYPE') == "No ":
structure = "nl"
elif m.group('LTYPE') == "Pot ":
structure = "pl"
elif m.group('LTYPE') == None:
structure = "fl"
if m.group('GAME') == "Hold\'em":
game = "hold"
elif m.group('GAME') == "Omaha":
game = "omahahi"
elif m.group('GAME') == "Razz":
game = "razz"
print m.groups()
gametype = ["ring", game, structure, m.group('SB'), m.group('BB')]
return gametype return gametype
def readHandInfo(self, hand): def readHandInfo(self, hand):
m = self.rexx.hand_info_re.search(hand.string,re.DOTALL) m = self.re_HandInfo.search(hand.string,re.DOTALL)
#print m.groups() #print m.groups()
hand.handid = m.group('HID') hand.handid = m.group('HID')
hand.tablename = m.group('TABLE') hand.tablename = m.group('TABLE')
hand.buttonpos = int(self.rexx.button_re.search(hand.string).group('BUTTON'))
hand.starttime = time.strptime(m.group('DATETIME'), "%H:%M:%S ET - %Y/%m/%d") hand.starttime = time.strptime(m.group('DATETIME'), "%H:%M:%S ET - %Y/%m/%d")
# These work, but the info is already in the Hand class - should be used for tourneys though. # These work, but the info is already in the Hand class - should be used for tourneys though.
# m.group('SB') # m.group('SB')
@ -125,7 +113,7 @@ class FullTilt(HandHistoryConverter):
#FIXME: hand.buttonpos = int(m.group('BUTTON')) #FIXME: hand.buttonpos = int(m.group('BUTTON'))
def readPlayerStacks(self, hand): def readPlayerStacks(self, hand):
m = self.rexx.player_info_re.finditer(hand.string) m = self.re_PlayerInfo.finditer(hand.string)
players = [] players = []
for a in m: for a in m:
hand.addPlayer(int(a.group('SEAT')), a.group('PNAME'), a.group('CASH')) hand.addPlayer(int(a.group('SEAT')), a.group('PNAME'), a.group('CASH'))
@ -134,34 +122,57 @@ class FullTilt(HandHistoryConverter):
# PREFLOP = ** Dealing down cards ** # PREFLOP = ** Dealing down cards **
# This re fails if, say, river is missing; then we don't get the ** that starts the river. # This re fails if, say, river is missing; then we don't get the ** that starts the river.
m = re.search(r"\*\*\* HOLE CARDS \*\*\*(?P<PREFLOP>.+(?=\*\*\* FLOP \*\*\*)|.+)" if self.gametype[1] == "hold" or self.gametype[1] == "omaha":
m = re.search(r"\*\*\* HOLE CARDS \*\*\*(?P<PREFLOP>.+(?=\*\*\* FLOP \*\*\*)|.+)"
r"(\*\*\* FLOP \*\*\*(?P<FLOP> \[\S\S \S\S \S\S\].+(?=\*\*\* TURN \*\*\*)|.+))?" r"(\*\*\* FLOP \*\*\*(?P<FLOP> \[\S\S \S\S \S\S\].+(?=\*\*\* TURN \*\*\*)|.+))?"
r"(\*\*\* TURN \*\*\* \[\S\S \S\S \S\S] (?P<TURN>\[\S\S\].+(?=\*\*\* RIVER \*\*\*)|.+))?" r"(\*\*\* TURN \*\*\* \[\S\S \S\S \S\S] (?P<TURN>\[\S\S\].+(?=\*\*\* RIVER \*\*\*)|.+))?"
r"(\*\*\* RIVER \*\*\* \[\S\S \S\S \S\S \S\S] (?P<RIVER>\[\S\S\].+))?", hand.string,re.DOTALL) r"(\*\*\* RIVER \*\*\* \[\S\S \S\S \S\S \S\S] (?P<RIVER>\[\S\S\].+))?", hand.string,re.DOTALL)
elif self.gametype[1] == "razz":
m = re.search(r"(?P<ANTES>.+(?=\*\*\* 3RD STREET \*\*\*)|.+)"
r"(\*\*\* 3RD STREET \*\*\*(?P<THIRD>.+(?=\*\*\* 4TH STREET \*\*\*)|.+))?"
r"(\*\*\* 4TH STREET \*\*\*(?P<FOURTH>.+(?=\*\*\* 5TH STREET \*\*\*)|.+))?"
r"(\*\*\* 5TH STREET \*\*\*(?P<FIFTH>.+(?=\*\*\* 6TH STREET \*\*\*)|.+))?"
r"(\*\*\* 6TH STREET \*\*\*(?P<SIXTH>.+(?=\*\*\* 7TH STREET \*\*\*)|.+))?"
r"(\*\*\* 7TH STREET \*\*\*(?P<SEVENTH>.+))?", hand.string,re.DOTALL)
hand.addStreets(m) hand.addStreets(m)
def readCommunityCards(self, hand, street): # street has been matched by markStreets, so exists in this hand def readCommunityCards(self, hand, street): # street has been matched by markStreets, so exists in this hand
if street in ('FLOP','TURN','RIVER'): # a list of streets which get dealt community cards (i.e. all but PREFLOP) if street in ('FLOP','TURN','RIVER'): # a list of streets which get dealt community cards (i.e. all but PREFLOP)
self.rexx.board_re = re.compile(r"\[(?P<CARDS>.+)\]")
#print "DEBUG readCommunityCards:", street, hand.streets.group(street) #print "DEBUG readCommunityCards:", street, hand.streets.group(street)
m = self.rexx.board_re.search(hand.streets.group(street)) m = self.re_Board.search(hand.streets.group(street))
hand.setCommunityCards(street, m.group('CARDS').split(' ')) hand.setCommunityCards(street, m.group('CARDS').split(' '))
def readBlinds(self, hand): def readBlinds(self, hand):
try: try:
m = self.rexx.small_blind_re.search(hand.string) m = self.re_PostSB.search(hand.string)
hand.addBlind(m.group('PNAME'), 'small blind', m.group('SB')) hand.addBlind(m.group('PNAME'), 'small blind', m.group('SB'))
except: # no small blind except: # no small blind
hand.addBlind(None, None, None) hand.addBlind(None, None, None)
for a in self.rexx.big_blind_re.finditer(hand.string): for a in self.re_PostBB.finditer(hand.string):
hand.addBlind(a.group('PNAME'), 'big blind', a.group('BB')) hand.addBlind(a.group('PNAME'), 'big blind', a.group('BB'))
for a in self.rexx.both_blinds_re.finditer(hand.string): for a in self.re_PostBoth.finditer(hand.string):
hand.addBlind(a.group('PNAME'), 'small & big blinds', a.group('SBBB')) hand.addBlind(a.group('PNAME'), 'small & big blinds', a.group('SBBB'))
def readAntes(self, hand):
print "DEBUG: reading antes"
m = self.re_Antes.finditer(hand.string)
for player in m:
print "DEBUG: hand.addAnte(%s,%s)" %(player.group('PNAME'), player.group('ANTE'))
hand.addAnte(player.group('PNAME'), player.group('ANTE'))
def readBringIn(self, hand):
print "DEBUG: reading bring in"
# print hand.string
m = self.re_BringIn.search(hand.string,re.DOTALL)
print "DEBUG: Player bringing in: %s for %s" %(m.group('PNAME'), m.group('BRINGIN'))
hand.addBringIn(m.group('PNAME'), m.group('BRINGIN'))
def readButton(self, hand):
hand.buttonpos = int(self.re_Button.search(hand.string).group('BUTTON'))
def readHeroCards(self, hand): def readHeroCards(self, hand):
m = self.rexx.hero_cards_re.search(hand.string) m = self.re_HeroCards.search(hand.string)
if(m == None): if(m == None):
#Not involved in hand #Not involved in hand
hand.involved = False hand.involved = False
@ -173,8 +184,22 @@ class FullTilt(HandHistoryConverter):
cards = set(cards.split(' ')) cards = set(cards.split(' '))
hand.addHoleCards(cards, m.group('PNAME')) hand.addHoleCards(cards, m.group('PNAME'))
def readPlayerCards(self, hand, street):
#Used for stud hands - borrows the HeroCards regex for now.
m = self.re_HeroCards.finditer(hand.streets.group(street))
print "DEBUG: razz/stud readPlayerCards"
print hand.streets.group(street)
for player in m:
print player.groups()
cards = player.group('CARDS')
if player.group('NEWCARD') != None:
print cards
cards = cards + " " + player.group('NEWCARD')
cards = set(cards.split(' '))
hand.addPlayerCards(cards, player.group('PNAME'))
def readAction(self, hand, street): def readAction(self, hand, street):
m = self.rexx.action_re.finditer(hand.streets.group(street)) m = self.re_Action.finditer(hand.streets.group(street))
for action in m: for action in m:
if action.group('ATYPE') == ' raises to': if action.group('ATYPE') == ' raises to':
hand.addRaiseTo( street, action.group('PNAME'), action.group('BET') ) hand.addRaiseTo( street, action.group('PNAME'), action.group('BET') )
@ -191,17 +216,17 @@ class FullTilt(HandHistoryConverter):
def readShowdownActions(self, hand): def readShowdownActions(self, hand):
for shows in self.rexx.showdown_action_re.finditer(hand.string): for shows in self.re_ShowdownAction.finditer(hand.string):
cards = shows.group('CARDS') cards = shows.group('CARDS')
cards = set(cards.split(' ')) cards = set(cards.split(' '))
hand.addShownCards(cards, shows.group('PNAME')) hand.addShownCards(cards, shows.group('PNAME'))
def readCollectPot(self,hand): def readCollectPot(self,hand):
for m in self.rexx.collect_pot_re.finditer(hand.string): for m in self.re_CollectPot.finditer(hand.string):
hand.addCollectPot(player=m.group('PNAME'),pot=m.group('POT')) hand.addCollectPot(player=m.group('PNAME'),pot=m.group('POT'))
def readShownCards(self,hand): def readShownCards(self,hand):
for m in self.rexx.shown_cards_re.finditer(hand.string): for m in self.re_ShownCards.finditer(hand.string):
if m.group('CARDS') is not None: if m.group('CARDS') is not None:
cards = m.group('CARDS') cards = m.group('CARDS')
cards = set(cards.split(' ')) cards = set(cards.split(' '))
@ -211,7 +236,7 @@ class FullTilt(HandHistoryConverter):
if __name__ == "__main__": if __name__ == "__main__":
c = Configuration.Config() c = Configuration.Config()
if len(sys.argv) == 1: if len(sys.argv) == 1:
testfile = "regression-test-files/FT20081209 CR - tay - $0.05-$0.10 - No Limit Hold'em.txt" testfile = "regression-test-files/fulltilt/razz/FT20090223 Danville - $0.50-$1 Ante $0.10 - Limit Razz.txt"
else: else:
testfile = sys.argv[1] testfile = sys.argv[1]
print "Converting: ", testfile print "Converting: ", testfile

View File

@ -18,7 +18,9 @@
# Standard Library modules # Standard Library modules
import os import os
import sys
from time import time from time import time
from optparse import OptionParser
# pyGTK modules # pyGTK modules
import pygtk import pygtk
@ -64,15 +66,16 @@ class GuiBulkImport():
self.importer.setDropIndexes(cb_model[cb_index][0]) self.importer.setDropIndexes(cb_model[cb_index][0])
else: else:
self.importer.setDropIndexes("auto") self.importer.setDropIndexes("auto")
hhc=self.cbfilter.get_model()[self.cbfilter.get_active()][0]
self.lab_info.set_text("Importing") self.lab_info.set_text("Importing")
if os.path.isdir(self.inputFile):
self.import_dir() self.importer.addBulkImportImportFileOrDir(self.inputFile,filter=hhc)
else: self.importer.setCallHud(False)
self.importer.addImportFile(self.inputFile) starttime = time()
self.importer.setCallHud(False) (stored, dups, partial, errs, ttime) = self.importer.runImport()
self.importer.runImport() print 'GuiBulkImport.import_dir done: Stored: %d Duplicates: %d Partial: %d Errors: %d in %s seconds - %d/sec'\
self.importer.clearFileList() % (stored, dups, partial, errs, ttime, stored / ttime)
self.importer.clearFileList()
self.lab_info.set_text("Import finished") self.lab_info.set_text("Import finished")
@ -164,6 +167,20 @@ class GuiBulkImport():
self.table.attach(self.cb, 4, 5, 1, 2, xpadding = 10, ypadding = 0, yoptions=gtk.SHRINK) self.table.attach(self.cb, 4, 5, 1, 2, xpadding = 10, ypadding = 0, yoptions=gtk.SHRINK)
self.cb.show() self.cb.show()
# label - filter
self.lab_filter = gtk.Label("Site filter:")
self.table.attach(self.lab_filter, 2, 3, 2, 3, xpadding = 0, ypadding = 0, yoptions=gtk.SHRINK)
self.lab_filter.show()
self.lab_filter.set_justify(gtk.JUSTIFY_RIGHT)
# ComboBox - filter
self.cbfilter = gtk.combo_box_new_text()
self.cbfilter.append_text("passthrough")
self.cbfilter.append_text("Everleaf")
self.cbfilter.set_active(0)
self.table.attach(self.cbfilter, 3, 4, 2, 3, xpadding = 10, ypadding = 0, yoptions=gtk.SHRINK)
self.cbfilter.show()
# label - info # label - info
self.lab_info = gtk.Label() self.lab_info = gtk.Label()
self.table.attach(self.lab_info, 0, 4, 2, 3, xpadding = 0, ypadding = 0, yoptions=gtk.SHRINK) self.table.attach(self.lab_info, 0, 4, 2, 3, xpadding = 0, ypadding = 0, yoptions=gtk.SHRINK)
@ -199,6 +216,12 @@ if __name__ == '__main__':
def destroy(*args): # call back for terminating the main eventloop def destroy(*args): # call back for terminating the main eventloop
gtk.main_quit() gtk.main_quit()
parser = OptionParser()
parser.add_option("-f", "--file", dest="filename", help="Input file in quiet mode", metavar="FILE")
parser.add_option("-q", "--quiet", action="store_false", dest="gui", default=True, help="don't start gui")
(options, sys.argv) = parser.parse_args()
config = Configuration.Config() config = Configuration.Config()
db = fpdb_db.fpdb_db() db = fpdb_db.fpdb_db()
@ -211,9 +234,19 @@ if __name__ == '__main__':
settings.update(config.get_import_parameters()) settings.update(config.get_import_parameters())
settings.update(config.get_default_paths()) settings.update(config.get_default_paths())
i = GuiBulkImport(db, settings, config) if(options.gui == True):
main_window = gtk.Window() i = GuiBulkImport(db, settings, config)
main_window.connect('destroy', destroy) main_window = gtk.Window()
main_window.add(i.vbox) main_window.connect('destroy', destroy)
main_window.show() main_window.add(i.vbox)
gtk.main() main_window.show()
gtk.main()
else:
#Do something useful
importer = fpdb_import.Importer(False,settings, config)
importer.setDropIndexes("auto")
importer.setFailOnError(True)
importer.addImportFile(options.filename)
importer.setCallHud(False)
importer.runImport()
importer.clearFileList()

View File

@ -39,7 +39,10 @@ class Hand:
self.gametype = gametype self.gametype = gametype
self.string = string self.string = string
self.streetList = ['PREFLOP','FLOP','TURN','RIVER'] # a list of the observed street names in order if gametype[1] == "hold" or self.gametype[1] == "omaha":
self.streetList = ['PREFLOP','FLOP','TURN','RIVER'] # a list of the observed street names in order
elif self.gametype[1] == "razz" or self.gametype[1] == "stud" or self.gametype[1] == "stud8":
self.streetList = ['ANTES','THIRD','FOURTH','FIFTH','SIXTH','SEVENTH'] # a list of the observed street names in order
self.handid = 0 self.handid = 0
self.sb = gametype[3] self.sb = gametype[3]
@ -83,7 +86,8 @@ class Hand:
self.stacks = {} self.stacks = {}
# dict from player names to amounts collected # dict from player names to amounts collected
self.collected = {} self.collected = []
self.collectees = {}
# Sets of players # Sets of players
self.shown = set() self.shown = set()
@ -134,7 +138,23 @@ Assigns observed holecards to a player.
cards set of card bigrams e.g. set(['2h','Jc']) cards set of card bigrams e.g. set(['2h','Jc'])
player (string) name of player player (string) name of player
""" """
print "DEBUG: addHoleCards", cards,player #print "DEBUG: addHoleCards", cards,player
try:
self.checkPlayerExists(player)
cards = set([self.card(c) for c in cards])
self.holecards[player].update(cards)
except FpdbParseError, e:
print "[ERROR] Tried to add holecards for unknown player: %s" % (player,)
def addPlayerCards(self, cards, player):
"""\
Assigns observed cards to a player.
cards set of card bigrams e.g. set(['2h','Jc'])
player (string) name of player
Should probably be merged with addHoleCards
"""
print "DEBUG: addPlayerCards", cards,player
try: try:
self.checkPlayerExists(player) self.checkPlayerExists(player)
cards = set([self.card(c) for c in cards]) cards = set([self.card(c) for c in cards])
@ -147,7 +167,7 @@ player (string) name of player
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
""" """
print "DEBUG: addShownCards", cards,player,holeandboard #print "DEBUG: addShownCards", cards,player,holeandboard
if cards is not None: if cards is not None:
self.shown.add(player) self.shown.add(player)
self.addHoleCards(cards,player) self.addHoleCards(cards,player)
@ -159,6 +179,7 @@ Card ranks will be uppercased
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]:
print "checkPlayerExists", player, "fail"
raise FpdbParseError raise FpdbParseError
def discardHoleCards(self, cards, player): def discardHoleCards(self, cards, player):
@ -180,6 +201,14 @@ Card ranks will be uppercased
c = c.replace(k,v) c = c.replace(k,v)
return c return c
def addAnte(self, player, ante):
if player is not None:
self.bets['ANTES'][player].append(Decimal(ante))
self.stacks[player] -= Decimal(ante)
act = (player, 'posts', "ante", ante, self.stacks[player]==0)
self.actions['ANTES'].append(act)
self.pot.addMoney(player, Decimal(ante))
def addBlind(self, player, blindtype, amount): def addBlind(self, player, blindtype, amount):
# if player is None, it's a missing small blind. # if player is None, it's a missing small blind.
# TODO: # TODO:
@ -190,9 +219,9 @@ Card ranks will be uppercased
# - this is a bet of 1 bb and is the new uncalled # - this is a bet of 1 bb and is the new uncalled
# #
# If a player posts a big & small blind # If a player posts a big & small blind
# # - FIXME: We dont record this for later printing yet
print "DEBUG addBlind: %s posts %s, %s" % (player, blindtype, amount) #print "DEBUG addBlind: %s posts %s, %s" % (player, blindtype, amount)
if player is not None: if player is not None:
self.bets['PREFLOP'][player].append(Decimal(amount)) self.bets['PREFLOP'][player].append(Decimal(amount))
self.stacks[player] -= Decimal(amount) self.stacks[player] -= Decimal(amount)
@ -202,10 +231,19 @@ Card ranks will be uppercased
self.pot.addMoney(player, Decimal(amount)) self.pot.addMoney(player, Decimal(amount))
if blindtype == 'big blind': if blindtype == 'big blind':
self.lastBet['PREFLOP'] = Decimal(amount) self.lastBet['PREFLOP'] = Decimal(amount)
elif blindtype == 'small & big blinds': elif blindtype == 'both':
# extra small blind is 'dead' # extra small blind is 'dead'
self.lastBet['PREFLOP'] = Decimal(self.bb) self.lastBet['PREFLOP'] = Decimal(self.bb)
self.posted += [player] self.posted = self.posted + [[player,blindtype]]
#print "DEBUG: self.posted: %s" %(self.posted)
def addBringIn(self, player, ante):
if player is not None:
self.bets['THIRD'][player].append(Decimal(ante))
self.stacks[player] -= Decimal(ante)
act = (player, 'bringin', "bringin", ante, self.stacks[player]==0)
self.actions['THIRD'].append(act)
self.pot.addMoney(player, Decimal(ante))
def addCall(self, street, player=None, amount=None): def addCall(self, street, player=None, amount=None):
@ -215,7 +253,7 @@ Card ranks will be uppercased
self.bets[street][player].append(Decimal(amount)) self.bets[street][player].append(Decimal(amount))
#self.lastBet[street] = Decimal(amount) #self.lastBet[street] = Decimal(amount)
self.stacks[player] -= Decimal(amount) self.stacks[player] -= Decimal(amount)
print "DEBUG %s calls %s, stack %s" % (player, amount, self.stacks[player]) #print "DEBUG %s calls %s, stack %s" % (player, amount, self.stacks[player])
act = (player, 'calls', amount, self.stacks[player]==0) act = (player, 'calls', amount, self.stacks[player]==0)
self.actions[street].append(act) self.actions[street].append(act)
self.pot.addMoney(player, Decimal(amount)) self.pot.addMoney(player, Decimal(amount))
@ -264,7 +302,9 @@ For sites which by "raises x" mean "calls and raises putting a total of x in the
"""\ """\
Add a raise on [street] by [player] to [amountTo] Add a raise on [street] by [player] to [amountTo]
""" """
#CG - No idea if this function has been test/verified
self.checkPlayerExists(player) self.checkPlayerExists(player)
Bp = self.lastBet[street]
Bc = reduce(operator.add, self.bets[street][player], 0) Bc = reduce(operator.add, self.bets[street][player], 0)
Rt = Decimal(amountTo) Rt = Decimal(amountTo)
C = Bp - Bc C = Bp - Bc
@ -285,7 +325,7 @@ Add a raise on [street] by [player] to [amountTo]
self.checkPlayerExists(player) self.checkPlayerExists(player)
self.bets[street][player].append(Decimal(amount)) self.bets[street][player].append(Decimal(amount))
self.stacks[player] -= Decimal(amount) self.stacks[player] -= Decimal(amount)
print "DEBUG %s bets %s, stack %s" % (player, amount, self.stacks[player]) #print "DEBUG %s bets %s, stack %s" % (player, amount, self.stacks[player])
act = (player, 'bets', amount, self.stacks[player]==0) act = (player, 'bets', amount, self.stacks[player]==0)
self.actions[street].append(act) self.actions[street].append(act)
self.lastBet[street] = Decimal(amount) self.lastBet[street] = Decimal(amount)
@ -293,7 +333,7 @@ Add a raise on [street] by [player] to [amountTo]
def addFold(self, street, player): def addFold(self, street, player):
print "DEBUG: %s %s folded" % (street, player) #print "DEBUG: %s %s folded" % (street, player)
self.checkPlayerExists(player) self.checkPlayerExists(player)
self.folded.add(player) self.folded.add(player)
self.pot.addFold(player) self.pot.addFold(player)
@ -301,17 +341,19 @@ Add a raise on [street] by [player] to [amountTo]
def addCheck(self, street, player): def addCheck(self, street, player):
print "DEBUG: %s %s checked" % (street, player) #print "DEBUG: %s %s checked" % (street, player)
self.checkPlayerExists(player) self.checkPlayerExists(player)
self.actions[street].append((player, 'checks')) self.actions[street].append((player, 'checks'))
def addCollectPot(self,player, pot): def addCollectPot(self,player, pot):
print "DEBUG: %s collected %s" % (player, pot) #print "DEBUG: %s collected %s" % (player, pot)
self.checkPlayerExists(player) self.checkPlayerExists(player)
if player not in self.collected: self.collected = self.collected + [[player, pot]]
self.collected[player] = pot if player not in self.collectees:
self.collectees[player] = Decimal(pot)
else: else:
print "[WARNING] %s collected pot more than once; avoidable by reading winnings only from summary lines?" self.collectees[player] += Decimal(pot)
def totalPot(self): def totalPot(self):
@ -325,8 +367,9 @@ Add a raise on [street] by [player] to [amountTo]
# This gives us the amount collected, i.e. after rake # This gives us the amount collected, i.e. after rake
if self.totalcollected is None: if self.totalcollected is None:
self.totalcollected = 0; self.totalcollected = 0;
for amount in self.collected.values(): #self.collected looks like [[p1,amount][px,amount]]
self.totalcollected += Decimal(amount) for entry in self.collected:
self.totalcollected += Decimal(entry[1])
@ -337,7 +380,7 @@ Map the tuple self.gametype onto the pokerstars string describing it
""" """
# currently it appears to be something like ["ring", "hold", "nl", sb, bb]: # currently it appears to be something like ["ring", "hold", "nl", sb, bb]:
gs = {"hold" : "Hold'em", gs = {"hold" : "Hold'em",
"omahahi" : "FIXME", "omahahi" : "Omaha",
"omahahilo" : "FIXME", "omahahilo" : "FIXME",
"razz" : "Razz", "razz" : "Razz",
"studhi" : "FIXME", "studhi" : "FIXME",
@ -354,11 +397,19 @@ Map the tuple self.gametype onto the pokerstars string describing it
"cp" : "Cap Pot Limit" "cp" : "Cap Pot Limit"
} }
print "DEBUG: self.gametype: %s" %(self.gametype)
string = "%s %s" %(gs[self.gametype[1]], ls[self.gametype[2]]) string = "%s %s" %(gs[self.gametype[1]], ls[self.gametype[2]])
return string return string
def writeHand(self, fh=sys.__stdout__): def writeHand(self, fh=sys.__stdout__):
if self.gametype[1] == "hold" or self.gametype[1] == "omaha":
self.writeHoldemHand(fh)
else:
self.writeStudHand(fh)
def writeHoldemHand(self, fh=sys.__stdout__):
# PokerStars format. # PokerStars format.
#print "\n### Pseudo stars format ###" #print "\n### Pseudo stars format ###"
#print >>fh, _("%s Game #%s: %s ($%s/$%s) - %s" %(self.sitename, self.handid, self.getGameTypeAsString(), self.sb, self.bb, self.starttime)) #print >>fh, _("%s Game #%s: %s ($%s/$%s) - %s" %(self.sitename, self.handid, self.getGameTypeAsString(), self.sb, self.bb, self.starttime))
@ -371,17 +422,15 @@ Map the tuple self.gametype onto the pokerstars string describing it
#Only print stacks of players who do something preflop #Only print stacks of players who do something preflop
print >>fh, _("Seat %s: %s ($%s)" %(player[0], player[1], player[2])) print >>fh, _("Seat %s: %s ($%s)" %(player[0], player[1], player[2]))
if(self.posted[0] is None):
#print >>fh, _("No small blind posted") # PS doesn't say this
pass
else:
print >>fh, _("%s: posts small blind $%s" %(self.posted[0], self.sb))
#May be more than 1 bb posting #May be more than 1 bb posting
for a in self.posted[1:]: for a in self.posted:
print >>fh, _("%s: posts big blind $%s" %(self.posted[1], self.bb)) if(a[1] == "small blind"):
print >>fh, _("%s: posts small blind $%s" %(a[0], self.sb))
# TODO: What about big & small blinds? if(a[1] == "big blind"):
print >>fh, _("%s: posts big blind $%s" %(a[0], self.bb))
if(a[1] == "both"):
print >>fh, _("%s: posts small & big blinds $%.2f" %(a[0], (Decimal(self.sb) + Decimal(self.bb))))
print >>fh, _("*** HOLE CARDS ***") print >>fh, _("*** HOLE CARDS ***")
if self.involved: if self.involved:
@ -412,7 +461,123 @@ Map the tuple self.gametype onto the pokerstars string describing it
# we probably don't need a showdown section in pseudo stars format for our filtering purposes # we probably don't need a showdown section in pseudo stars format for our filtering purposes
if 'SHOWDOWN' in self.actions: if 'SHOWDOWN' in self.actions:
print >>fh, _("*** SHOW DOWN ***") print >>fh, _("*** SHOW DOWN ***")
print >>fh, "DEBUG: what do they show" #TODO: Complete SHOWDOWN
# Current PS format has the lines:
# Uncalled bet ($111.25) returned to s0rrow
# s0rrow collected $5.15 from side pot
# stervels: shows [Ks Qs] (two pair, Kings and Queens)
# stervels collected $45.35 from main pot
# Immediately before the summary.
# The current importer uses those lines for importing winning rather than the summary
for name in self.pot.returned:
print >>fh, _("Uncalled bet ($%s) returned to %s" %(self.pot.returned[name],name))
for entry in self.collected:
print >>fh, _("%s collected $%s from x pot" %(entry[0], entry[1]))
print >>fh, _("*** SUMMARY ***")
print >>fh, "%s | Rake $%.2f" % (self.pot, self.rake)
board = []
for s in self.board.values():
board += s
if board: # sometimes hand ends preflop without a board
print >>fh, _("Board [%s]" % (" ".join(board)))
for player in [x for x in self.players if x[1] in players_who_act_preflop]:
seatnum = player[0]
name = player[1]
if name in self.collectees and name in self.shown:
print >>fh, _("Seat %d: %s showed [%s] and won ($%s)" % (seatnum, name, " ".join(self.holecards[name]), self.collectees[name]))
elif name in self.collectees:
print >>fh, _("Seat %d: %s collected ($%s)" % (seatnum, name, self.collectees[name]))
elif name in self.shown:
print >>fh, _("Seat %d: %s showed [%s]" % (seatnum, name, " ".join(self.holecards[name])))
elif name in self.folded:
print >>fh, _("Seat %d: %s folded" % (seatnum, name))
else:
print >>fh, _("Seat %d: %s mucked" % (seatnum, name))
print >>fh, "\n\n"
# TODO:
# logic for side pots
# logic for which players get to showdown
# I'm just not sure we need to do this so heavily.. and if we do, it's probably better to use pokerlib
#if self.holecards[player[1]]: # empty list default is false
#hole = self.holecards[player[1]]
##board = []
##for s in self.board.values():
##board += s
##playerhand = self.bestHand('hi', board+hole)
##print "Seat %d: %s showed %s and won/lost with %s" % (player[0], player[1], hole, playerhand)
#print "Seat %d: %s showed %s" % (player[0], player[1], hole)
#else:
#print "Seat %d: %s mucked or folded" % (player[0], player[1])
def writeStudHand(self, fh=sys.__stdout__):
# PokerStars format.
#print "\n### Pseudo stars format ###"
#print >>fh, _("%s Game #%s: %s ($%s/$%s) - %s" %(self.sitename, self.handid, self.getGameTypeAsString(), self.sb, self.bb, self.starttime))
print >>fh, _("%s Game #%s: %s ($%s/$%s) - %s" %("PokerStars", self.handid, self.getGameTypeAsString(), self.sb, self.bb, time.strftime('%Y/%m/%d - %H:%M:%S (ET)', self.starttime)))
print >>fh, _("Table '%s' %d-max Seat #%s is the button" %(self.tablename, self.maxseats, self.buttonpos))
players_who_post_antes = set([x[0] for x in self.actions['ANTES']])
for player in [x for x in self.players if x[1] in players_who_post_antes]:
#Only print stacks of players who do something preflop
print >>fh, _("Seat %s: %s ($%s)" %(player[0], player[1], player[2]))
if 'ANTES' in self.actions:
for act in self.actions['ANTES']:
print >>fh, _("%s: posts the ante $%s" %(act[0], act[3]))
if 'THIRD' in self.actions:
print >>fh, _("*** 3RD STREET ***")
for player in [x for x in self.players if x[1] in players_who_post_antes]:
print >>fh, _("Dealt to ")
for act in self.actions['THIRD']:
#FIXME: Need some logic here for bringin vs completes
self.printActionLine(act, fh)
if 'FOURTH' in self.actions:
print >>fh, _("*** 4TH STREET ***")
for act in self.actions['FOURTH']:
self.printActionLine(act, fh)
if 'FIFTH' in self.actions:
print >>fh, _("*** 5TH STREET ***")
for act in self.actions['FIFTH']:
self.printActionLine(act, fh)
if 'SIXTH' in self.actions:
print >>fh, _("*** 6TH STREET ***")
for act in self.actions['SIXTH']:
self.printActionLine(act, fh)
if 'SEVENTH' in self.actions:
print >>fh, _("*** 7TH STREET ***")
for act in self.actions['SEVENTH']:
self.printActionLine(act, fh)
#Some sites don't have a showdown section so we have to figure out if there should be one
# The logic for a showdown is: at the end of river action there are at least two players in the hand
# we probably don't need a showdown section in pseudo stars format for our filtering purposes
if 'SHOWDOWN' in self.actions:
print >>fh, _("*** SHOW DOWN ***")
# print >>fh, "DEBUG: what do they show"
# Current PS format has the lines:
# Uncalled bet ($111.25) returned to s0rrow
# s0rrow collected $5.15 from side pot
# stervels: shows [Ks Qs] (two pair, Kings and Queens)
# stervels collected $45.35 from main pot
# Immediately before the summary.
# The current importer uses those lines for importing winning rather than the summary
for name in self.pot.returned:
print >>fh, _("Uncalled bet ($%s) returned to %s" %(self.pot.returned[name],name))
for entry in self.collected:
print >>fh, _("%s collected $%s from x pot" %(entry[0], entry[1]))
print >>fh, _("*** SUMMARY ***") print >>fh, _("*** SUMMARY ***")
print >>fh, "%s | Rake $%.2f" % (self.pot, self.rake) print >>fh, "%s | Rake $%.2f" % (self.pot, self.rake)
@ -424,13 +589,13 @@ Map the tuple self.gametype onto the pokerstars string describing it
if board: # sometimes hand ends preflop without a board if board: # sometimes hand ends preflop without a board
print >>fh, _("Board [%s]" % (" ".join(board))) print >>fh, _("Board [%s]" % (" ".join(board)))
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_post_antes]:
seatnum = player[0] seatnum = player[0]
name = player[1] name = player[1]
if name in self.collected and name in self.shown: if name in self.collectees and name in self.shown:
print >>fh, _("Seat %d: %s showed [%s] and won ($%s)" % (seatnum, name, " ".join(self.holecards[name]), self.collected[name])) print >>fh, _("Seat %d: %s showed [%s] and won ($%s)" % (seatnum, name, " ".join(self.holecards[name]), self.collectees[name]))
elif name in self.collected: elif name in self.collectees:
print >>fh, _("Seat %d: %s collected ($%s)" % (seatnum, name, self.collected[name])) print >>fh, _("Seat %d: %s collected ($%s)" % (seatnum, name, self.collectees[name]))
elif name in self.shown: elif name in self.shown:
print >>fh, _("Seat %d: %s showed [%s]" % (seatnum, name, " ".join(self.holecards[name]))) print >>fh, _("Seat %d: %s showed [%s]" % (seatnum, name, " ".join(self.holecards[name])))
elif name in self.folded: elif name in self.folded:
@ -518,6 +683,7 @@ class Pot(object):
self.contenders = set() self.contenders = set()
self.committed = {} self.committed = {}
self.total = None self.total = None
self.returned = {}
def addPlayer(self,player): def addPlayer(self,player):
self.committed[player] = Decimal(0) self.committed[player] = Decimal(0)
@ -539,14 +705,15 @@ class Pot(object):
lastbet = committed[-1][0] - committed[-2][0] lastbet = committed[-1][0] - committed[-2][0]
if lastbet > 0: # uncalled if lastbet > 0: # uncalled
returnto = committed[-1][1] returnto = committed[-1][1]
#print "returning %f to %s" % (lastbet, returnto) #print "DEBUG: returning %f to %s" % (lastbet, returnto)
self.total -= lastbet self.total -= lastbet
self.committed[returnto] -= lastbet self.committed[returnto] -= lastbet
self.returned[returnto] = lastbet
# Work out side pots # Work out side pots
commitsall = sorted([(v,k) for (k,v) in self.committed.items() if v >0]) commitsall = sorted([(v,k) for (k,v) in self.committed.items() if v >0])
self.pots = [] self.pots = []
while len(commitsall) > 0: while len(commitsall) > 0:
commitslive = [(v,k) for (v,k) in commitsall if k in self.contenders] commitslive = [(v,k) for (v,k) in commitsall if k in self.contenders]
@ -575,8 +742,11 @@ class Pot(object):
return "Total pot $%.2f Main pot $%.2f. Side pot $%2.f." % (self.total, self.pots[0], self.pots[1]) return "Total pot $%.2f Main pot $%.2f. Side pot $%2.f." % (self.total, self.pots[0], self.pots[1])
elif len(self.pots) == 3: elif len(self.pots) == 3:
return "Total pot $%.2f Main pot $%.2f. Side pot-1 $%2.2f. Side pot-2 $%.2f." % (self.total, self.pots[0], self.pots[1], self.pots[2]) return "Total pot $%.2f Main pot $%.2f. Side pot-1 $%2.2f. Side pot-2 $%.2f." % (self.total, self.pots[0], self.pots[1], self.pots[2])
elif len(self.pots) == 0:
# no small blind and walk in bb (hopefully)
return "Total pot $%.2f" % (self.total,)
else: else:
return "maybe no pot.. or too many pots.. no small blind and walk in bb?." return _("too many pots.. no small blind and walk in bb?. self.pots: %s" %(self.pots))
# I don't know stars format for a walk in the bb when sb doesn't post. # I don't know stars format for a walk in the bb when sb doesn't post.
# The thing to do here is raise a Hand error like fpdb import does and file it into errors.txt # The thing to do here is raise a Hand error like fpdb import does and file it into errors.txt

View File

@ -28,7 +28,7 @@ import codecs
from decimal import Decimal from decimal import Decimal
import operator import operator
from xml.dom.minidom import Node from xml.dom.minidom import Node
from pokereval import PokerEval # from pokereval import PokerEval
import time import time
import datetime import datetime
import gettext import gettext
@ -74,7 +74,7 @@ gettext.install('myapplication')
class HandHistoryConverter: class HandHistoryConverter:
eval = PokerEval() # eval = PokerEval()
def __init__(self, config, file, sitename): def __init__(self, config, file, sitename):
print "HandHistory init called" print "HandHistory init called"
self.c = config self.c = config
@ -88,8 +88,10 @@ class HandHistoryConverter:
self.hhbase = os.path.expanduser(self.hhbase) self.hhbase = os.path.expanduser(self.hhbase)
self.hhdir = os.path.join(self.hhbase,sitename) self.hhdir = os.path.join(self.hhbase,sitename)
self.gametype = [] self.gametype = []
# self.ofile = os.path.join(self.hhdir,file) self.ofile = os.path.join(self.hhdir, os.path.basename(file))
self.rexx = FpdbRegex.FpdbRegex() self.rexx = FpdbRegex.FpdbRegex()
self.players = set()
self.maxseats = 10
def __str__(self): def __str__(self):
tmp = "HandHistoryConverter: '%s'\n" % (self.sitename) tmp = "HandHistoryConverter: '%s'\n" % (self.sitename)
@ -97,11 +99,11 @@ class HandHistoryConverter:
tmp = tmp + "\thhdir: '%s'\n" % (self.hhdir) tmp = tmp + "\thhdir: '%s'\n" % (self.hhdir)
tmp = tmp + "\tfiletype: '%s'\n" % (self.filetype) tmp = tmp + "\tfiletype: '%s'\n" % (self.filetype)
tmp = tmp + "\tinfile: '%s'\n" % (self.file) tmp = tmp + "\tinfile: '%s'\n" % (self.file)
# tmp = tmp + "\toutfile: '%s'\n" % (self.ofile) tmp = tmp + "\toutfile: '%s'\n" % (self.ofile)
# tmp = tmp + "\tgametype: '%s'\n" % (self.gametype[0]) #tmp = tmp + "\tgametype: '%s'\n" % (self.gametype[0])
# tmp = tmp + "\tgamebase: '%s'\n" % (self.gametype[1]) #tmp = tmp + "\tgamebase: '%s'\n" % (self.gametype[1])
# tmp = tmp + "\tlimit: '%s'\n" % (self.gametype[2]) #tmp = tmp + "\tlimit: '%s'\n" % (self.gametype[2])
# tmp = tmp + "\tsb/bb: '%s/%s'\n" % (self.gametype[3], self.gametype[4]) #tmp = tmp + "\tsb/bb: '%s/%s'\n" % (self.gametype[3], self.gametype[4])
return tmp return tmp
def processFile(self): def processFile(self):
@ -110,23 +112,54 @@ class HandHistoryConverter:
print "Cowardly refusing to continue after failed sanity check" print "Cowardly refusing to continue after failed sanity check"
return return
self.readFile(self.file) self.readFile(self.file)
if self.obs == "" or self.obs == None:
print "Did not read anything from file."
return
self.obs = self.obs.replace('\r\n', '\n')
self.gametype = self.determineGameType() self.gametype = self.determineGameType()
if self.gametype == None:
print "Unknown game type from file, aborting on this file."
return
self.hands = self.splitFileIntoHands() self.hands = self.splitFileIntoHands()
outfile = open(self.ofile, 'w')
for hand in self.hands: for hand in self.hands:
print "\nInput:\n"+hand.string #print "\nDEBUG: Input:\n"+hand.string
self.readHandInfo(hand) self.readHandInfo(hand)
self.readPlayerStacks(hand) self.readPlayerStacks(hand)
print "DEBUG stacks:", hand.stacks #print "DEBUG stacks:", hand.stacks
# at this point we know the player names, they are in hand.players
playersThisHand = set([player[1] for player in hand.players])
if playersThisHand <= self.players: # x <= y means 'x is subset of y'
# we're ok; the regex should already cover them all.
pass
else:
# we need to recompile the player regexs.
self.players = playersThisHand
self.compilePlayerRegexs()
self.markStreets(hand) self.markStreets(hand)
self.readBlinds(hand) # Different calls if stud or holdem like
self.readHeroCards(hand) # want to generalise to draw games if self.gametype[1] == "hold" or self.gametype[1] == "omaha":
self.readBlinds(hand)
self.readButton(hand)
self.readHeroCards(hand) # want to generalise to draw games
elif self.gametype[1] == "razz" or self.gametype[1] == "stud" or self.gametype[1] == "stud8":
self.readAntes(hand)
self.readBringIn(hand)
self.readShowdownActions(hand) self.readShowdownActions(hand)
# Read actions in street order # Read actions in street order
for street in hand.streetList: # go through them in order for street in hand.streetList: # go through them in order
print "DEBUG: ", street
if hand.streets.group(street) is not None: if hand.streets.group(street) is not None:
self.readCommunityCards(hand, street) # read community cards if self.gametype[1] == "hold" or self.gametype[1] == "omaha":
self.readCommunityCards(hand, street) # read community cards
elif self.gametype[1] == "razz" or self.gametype[1] == "stud" or self.gametype[1] == "stud8":
self.readPlayerCards(hand, street)
self.readAction(hand, street) self.readAction(hand, street)
@ -137,19 +170,22 @@ class HandHistoryConverter:
hand.totalPot() hand.totalPot()
self.getRake(hand) self.getRake(hand)
hand.writeHand(sys.stderr) hand.writeHand(outfile)
#if(hand.involved == True): #if(hand.involved == True):
#self.writeHand("output file", hand) #self.writeHand("output file", hand)
#hand.printHand() #hand.printHand()
#else: #else:
#pass #Don't write out observed hands #pass #Don't write out observed hands
outfile.close()
endtime = time.time() endtime = time.time()
print "Processed %d hands in %d seconds" % (len(self.hands), endtime-starttime) print "Processed %d hands in %.3f seconds" % (len(self.hands), endtime - starttime)
##### #####
# These functions are parse actions that may be overridden by the inheriting class # These functions are parse actions that may be overridden by the inheriting class
# # This function should return a list of lists looking like:
# return [["ring", "hold", "nl"], ["tour", "hold", "nl"]]
# Showing all supported games limits and types
def readSupportedGames(self): abstract def readSupportedGames(self): abstract
@ -172,7 +208,10 @@ class HandHistoryConverter:
# Needs to return a list of lists in the format # Needs to return a list of lists in the format
# [['seat#', 'player1name', 'stacksize'] ['seat#', 'player2name', 'stacksize'] [...]] # [['seat#', 'player1name', 'stacksize'] ['seat#', 'player2name', 'stacksize'] [...]]
def readPlayerStacks(self, hand): abstract def readPlayerStacks(self, hand): abstract
def compilePlayerRegexs(self): abstract
"""Compile dynamic regexes -- these explicitly match known player names and must be updated if a new player joins"""
# Needs to return a MatchObject with group names identifying the streets into the Hand object # Needs to return a MatchObject with group names identifying the streets into the Hand object
# so groups are called by street names 'PREFLOP', 'FLOP', 'STREET2' etc # so groups are called by street names 'PREFLOP', 'FLOP', 'STREET2' etc
# blinds are done seperately # blinds are done seperately
@ -182,7 +221,11 @@ class HandHistoryConverter:
# ['player1name', 'player2name', ...] where player1name is the sb and player2name is bb, # ['player1name', 'player2name', ...] where player1name is the sb and player2name is bb,
# addtional players are assumed to post a bb oop # addtional players are assumed to post a bb oop
def readBlinds(self, hand): abstract def readBlinds(self, hand): abstract
def readAntes(self, hand): abstract
def readBringIn(self, hand): abstract
def readButton(self, hand): abstract
def readHeroCards(self, hand): abstract def readHeroCards(self, hand): abstract
def readPlayerCards(self, hand, street): abstract
def readAction(self, hand, street): abstract def readAction(self, hand, street): abstract
def readCollectPot(self, hand): abstract def readCollectPot(self, hand): abstract
def readShownCards(self, hand): abstract def readShownCards(self, hand): abstract
@ -212,6 +255,10 @@ class HandHistoryConverter:
else: else:
print "HH Sanity Check: Directory hhdir '" + self.hhdir + "' or its parent directory are not writable" print "HH Sanity Check: Directory hhdir '" + self.hhdir + "' or its parent directory are not writable"
# Make sure input and output files are different or we'll overwrite the source file
if(self.ofile == self.file):
print "HH Sanity Check: output and input files are the same, check config"
return sane return sane
# Functions not necessary to implement in sub class # Functions not necessary to implement in sub class
@ -222,7 +269,7 @@ class HandHistoryConverter:
def splitFileIntoHands(self): def splitFileIntoHands(self):
hands = [] hands = []
self.obs.strip() self.obs.strip()
list = self.rexx.split_hand_re.split(self.obs) list = self.re_SplitHands.split(self.obs)
list.pop() #Last entry is empty list.pop() #Last entry is empty
for l in list: for l in list:
# print "'" + l + "'" # print "'" + l + "'"
@ -233,7 +280,7 @@ class HandHistoryConverter:
"""Read file""" """Read file"""
print "Reading file: '%s'" %(filename) print "Reading file: '%s'" %(filename)
if(self.filetype == "text"): if(self.filetype == "text"):
infile=codecs.open(filename, "rU", self.codepage) infile=codecs.open(filename, "r", self.codepage)
self.obs = infile.read() self.obs = infile.read()
infile.close() infile.close()
elif(self.filetype == "xml"): elif(self.filetype == "xml"):
@ -244,18 +291,9 @@ class HandHistoryConverter:
traceback.print_exc(file=sys.stderr) traceback.print_exc(file=sys.stderr)
#takes a poker float (including , for thousand seperator and converts it to an int def getStatus(self):
def float2int (self, string): #TODO: Return a status of true if file processed ok
pos=string.find(",") return True
if (pos!=-1): #remove , the thousand seperator
string=string[0:pos]+string[pos+1:]
pos=string.find(".") def getProcessedFile(self):
if (pos!=-1): #remove decimal point return self.ofile
string=string[0:pos]+string[pos+1:]
result = int(string)
if pos==-1: #no decimal point - was in full dollars - need to multiply with 100
result*=100
return result
#end def float2int

View File

@ -364,6 +364,7 @@ class Stat_Window:
self.y = y + table.y # x and y are the location relative to table.x & y self.y = y + table.y # x and y are the location relative to table.x & y
self.player_id = player_id # looks like this isn't used ;) self.player_id = player_id # looks like this isn't used ;)
self.sb_click = 0 # used to figure out button clicks self.sb_click = 0 # used to figure out button clicks
self.useframes = parent.config.get_frames(parent.site)
self.window = gtk.Window() self.window = gtk.Window()
self.window.set_decorated(0) self.window.set_decorated(0)
@ -381,23 +382,28 @@ class Stat_Window:
self.frame = [] self.frame = []
self.label = [] self.label = []
for r in range(self.game.rows): for r in range(self.game.rows):
self.frame.append([]) if self.useframes:
self.frame.append([])
self.e_box.append([]) self.e_box.append([])
self.label.append([]) self.label.append([])
for c in range(self.game.cols): for c in range(self.game.cols):
self.frame[r].append( gtk.Frame() ) if self.useframes:
self.frame[r].append( gtk.Frame() )
self.e_box[r].append( gtk.EventBox() ) self.e_box[r].append( gtk.EventBox() )
self.e_box[r][c].modify_bg(gtk.STATE_NORMAL, parent.backgroundcolor) self.e_box[r][c].modify_bg(gtk.STATE_NORMAL, parent.backgroundcolor)
self.e_box[r][c].modify_fg(gtk.STATE_NORMAL, parent.foregroundcolor) self.e_box[r][c].modify_fg(gtk.STATE_NORMAL, parent.foregroundcolor)
Stats.do_tip(self.e_box[r][c], 'stuff') Stats.do_tip(self.e_box[r][c], 'stuff')
# self.grid.attach(self.e_box[r][c], c, c+1, r, r+1, xpadding = 0, ypadding = 0) if self.useframes:
self.grid.attach(self.frame[r][c], c, c+1, r, r+1, xpadding = 0, ypadding = 0) self.grid.attach(self.frame[r][c], c, c+1, r, r+1, xpadding = 0, ypadding = 0)
self.frame[r][c].add(self.e_box[r][c]) self.frame[r][c].add(self.e_box[r][c])
else:
self.grid.attach(self.e_box[r][c], c, c+1, r, r+1, xpadding = 0, ypadding = 0)
self.label[r].append( gtk.Label('xxx') ) self.label[r].append( gtk.Label('xxx') )
self.frame[r][c].modify_bg(gtk.STATE_NORMAL, parent.backgroundcolor) if self.useframes:
self.frame[r][c].modify_bg(gtk.STATE_NORMAL, parent.backgroundcolor)
self.label[r][c].modify_bg(gtk.STATE_NORMAL, parent.backgroundcolor) self.label[r][c].modify_bg(gtk.STATE_NORMAL, parent.backgroundcolor)
self.label[r][c].modify_fg(gtk.STATE_NORMAL, parent.foregroundcolor) self.label[r][c].modify_fg(gtk.STATE_NORMAL, parent.foregroundcolor)

15
pyfpdb/RegressionTest.py Normal file → Executable file
View File

@ -90,6 +90,21 @@ class TestSequenceFunctions(unittest.TestCase):
self.failUnless(result==datetime.datetime(2008,8,17,6,14,43), self.failUnless(result==datetime.datetime(2008,8,17,6,14,43),
"Date incorrect, expected: 2008-08-17 01:14:43 got: " + str(result)) "Date incorrect, expected: 2008-08-17 01:14:43 got: " + str(result))
def testFullTiltHHDate(self):
sitngo1 = "Full Tilt Poker Game #10311865543: $1 + $0.25 Sit & Go (78057629), Table 1 - 25/50 - No Limit Hold'em - 0:07:45 ET - 2009/01/29"
cash1 = "Full Tilt Poker Game #9403951181: Table CR - tay - $0.05/$0.10 - No Limit Hold'em - 9:40:20 ET - 2008/12/09"
cash2 = "Full Tilt Poker Game #9468383505: Table Bike (deep 6) - $0.05/$0.10 - No Limit Hold'em - 5:09:36 ET - 2008/12/13"
result = fpdb_simple.parseHandStartTime(sitngo1,"ftp")
self.failUnless(result==datetime.datetime(2009,1,29,05,07,45),
"Date incorrect, expected: 2009-01-29 05:07:45 got: " + str(result))
result = fpdb_simple.parseHandStartTime(cash1,"ftp")
self.failUnless(result==datetime.datetime(2008,12,9,14,40,20),
"Date incorrect, expected: 2008-12-09 14:40:20 got: " + str(result))
result = fpdb_simple.parseHandStartTime(cash2,"ftp")
self.failUnless(result==datetime.datetime(2008,12,13,10,9,36),
"Date incorrect, expected: 2008-12-13 10:09:36 got: " + str(result))
def testTableDetection(self): def testTableDetection(self):
result = Tables.clean_title("French (deep)") result = Tables.clean_title("French (deep)")
self.failUnless(result == "French", "French (deep) parsed incorrectly. Expected 'French' got: " + str(result)) self.failUnless(result == "French", "French (deep) parsed incorrectly. Expected 'French' got: " + str(result))

View File

@ -70,6 +70,13 @@ def do_stat(stat_dict, player = 24, stat = 'vpip'):
########################################### ###########################################
# functions that return individual stats # functions that return individual stats
def totalprofit(stat_dict, player):
""" Total Profit."""
if stat_dict[player]['net'] != 0:
stat = float(stat_dict[player]['net']) / 100
return (stat, '$%.2f' % stat, 'tp=$%.2f' % stat, 'totalprofit=$%.2f' % stat, str(stat), 'Total Profit')
return ('0', '0', '0', '0', 'Total Profit')
def playername(stat_dict, player): def playername(stat_dict, player):
""" Player Name.""" """ Player Name."""
return (stat_dict[player]['screen_name'], return (stat_dict[player]['screen_name'],

View File

@ -361,7 +361,7 @@ def clean_title(name):
' \(deep hu\)', ' \(deep 6\)', ' \(2\)', ' \(deep hu\)', ' \(deep 6\)', ' \(2\)',
' \(edu\)', ' \(edu, 6 max\)', ' \(6\)', ' \(edu\)', ' \(edu, 6 max\)', ' \(6\)',
' \(speed\)', ' \(speed\)',
' no all-in', ' fast', ',', ' 50BB min', '\s+$']: ' no all-in', ' fast', ',', ' 50BB min', '50bb min', '\s+$']:
name = re.sub(pattern, '', name) name = re.sub(pattern, '', name)
name = name.rstrip() name = name.rstrip()
return name return name

View File

@ -33,6 +33,8 @@ import fpdb_simple
import fpdb_db import fpdb_db
import fpdb_parse_logic import fpdb_parse_logic
import Configuration import Configuration
import EverleafToFpdb
import FulltiltToFpdb
# database interface modules # database interface modules
try: try:
@ -58,6 +60,8 @@ class Importer:
self.cursor = None self.cursor = None
self.filelist = {} self.filelist = {}
self.dirlist = {} self.dirlist = {}
self.addToDirList = {}
self.removeFromFileList = {} # to remove deleted files
self.monitor = False self.monitor = False
self.updated = {} #Time last import was run {file:mtime} self.updated = {} #Time last import was run {file:mtime}
self.lines = None self.lines = None
@ -66,8 +70,10 @@ class Importer:
#Set defaults #Set defaults
self.callHud = self.config.get_import_parameters().get("callFpdbHud") self.callHud = self.config.get_import_parameters().get("callFpdbHud")
if 'minPrint' not in self.settings: if 'minPrint' not in self.settings:
#TODO: Is this value in the xml file?
self.settings['minPrint'] = 30 self.settings['minPrint'] = 30
if 'handCount' not in self.settings: if 'handCount' not in self.settings:
#TODO: Is this value in the xml file?
self.settings['handCount'] = 0 self.settings['handCount'] = 0
self.fdb = fpdb_db.fpdb_db() # sets self.fdb.db self.fdb.cursor and self.fdb.sql self.fdb = fpdb_db.fpdb_db() # sets self.fdb.db self.fdb.cursor and self.fdb.sql
self.fdb.do_connect(self.config) self.fdb.do_connect(self.config)
@ -108,11 +114,27 @@ class Importer:
#TODO: test it is a valid file -> put that in config!! #TODO: test it is a valid file -> put that in config!!
self.filelist[filename] = [site] + [filter] self.filelist[filename] = [site] + [filter]
# Called from GuiBulkImport to add a file or directory.
def addBulkImportImportFileOrDir(self, inputPath,filter = "passthrough"):
"""Add a file or directory for bulk import"""
# Bulk import never monitors
# if directory, add all files in it. Otherwise add single file.
# TODO: only add sane files?
if os.path.isdir(inputPath):
for subdir in os.walk(inputPath):
for file in subdir[2]:
self.addImportFile(os.path.join(inputPath, subdir[0], file), site="default", filter=filter)
else:
self.addImportFile(inputPath, site="default", filter=filter)
#Add a directory of files to filelist #Add a directory of files to filelist
#Only one import directory per site supported. #Only one import directory per site supported.
#dirlist is a hash of lists: #dirlist is a hash of lists:
#dirlist{ 'PokerStars' => ["/path/to/import/", "filtername"] } #dirlist{ 'PokerStars' => ["/path/to/import/", "filtername"] }
def addImportDirectory(self,dir,monitor = False, site = "default", filter = "passthrough"): def addImportDirectory(self,dir,monitor = False, site = "default", filter = "passthrough"):
#This should really be using os.walk
#http://docs.python.org/library/os.html
if os.path.isdir(dir): if os.path.isdir(dir):
if monitor == True: if monitor == True:
self.monitor = True self.monitor = True
@ -177,16 +199,54 @@ class Importer:
self.updated[file] = time() self.updated[file] = time()
# This codepath only runs first time the file is found, if modified in the last # This codepath only runs first time the file is found, if modified in the last
# minute run an immediate import. # minute run an immediate import.
if (time() - stat_info.st_mtime) < 60: # TODO: figure out a way to dispatch this to the seperate thread so our main window doesn't lock up on initial import if (time() - stat_info.st_mtime) < 60 or os.path.isdir(file): # TODO: figure out a way to dispatch this to the seperate thread so our main window doesn't lock up on initial import
self.import_file_dict(file, self.filelist[file][0], self.filelist[file][1]) self.import_file_dict(file, self.filelist[file][0], self.filelist[file][1])
for dir in self.addToDirList:
self.addImportDirectory(dir, True, self.addToDirList[dir][0], self.addToDirList[dir][1])
for file in self.removeFromFileList:
if file in self.filelist:
del self.filelist[file]
self.addToDirList = {}
self.removeFromFileList = {}
# This is now an internal function that should not be called directly. # This is now an internal function that should not be called directly.
def import_file_dict(self, file, site, filter): def import_file_dict(self, file, site, filter):
if(filter == "passthrough"): if os.path.isdir(file):
self.addToDirList[file] = [site] + [filter]
return
if filter == "passthrough" or filter == "":
(stored, duplicates, partial, errors, ttime) = self.import_fpdb_file(file, site) (stored, duplicates, partial, errors, ttime) = self.import_fpdb_file(file, site)
else: else:
# TODO: Load filter, and run filtered file though main importer conv = None
(stored, duplicates, partial, errors, ttime) = self.import_fpdb_file(file, site) # Load filter, process file, pass returned filename to import_fpdb_file
# TODO: Shouldn't we be able to use some sort of lambda or something to just call a Python object by whatever name we specify? then we don't have to hardcode them,
# someone can just create their own python module for it
if filter == "EverleafToFpdb":
print "converting ", file
conv = EverleafToFpdb.Everleaf(self.config, file)
elif filter == "FulltiltToFpdb":
print "converting ", file
conv = FulltiltToFpdb.FullTilt(self.config, file)
else:
print "Unknown filter ", filter
return
supp = conv.readSupportedGames() # Should this be done by HHC on init?
#gt = conv.determineGameType()
# TODO: Check that gt is in supp - error appropriately if not
conv.processFile()
if(conv.getStatus()):
(stored, duplicates, partial, errors, ttime) = self.import_fpdb_file(conv.getProcessedFile(), site)
else:
# conversion didn't work
# TODO: appropriate response?
return (0, 0, 0, 1, 0)
#This will barf if conv.getStatus != True
return (stored, duplicates, partial, errors, ttime) return (stored, duplicates, partial, errors, ttime)
@ -197,9 +257,15 @@ class Importer:
if (file=="stdin"): if (file=="stdin"):
inputFile=sys.stdin inputFile=sys.stdin
else: else:
inputFile=open(file, "rU") if os.path.exists(file):
try: loc = self.pos_in_file[file] inputFile = open(file, "rU")
except: pass else:
self.removeFromFileList[file] = True
return (0, 0, 0, 1, 0)
try:
loc = self.pos_in_file[file]
except:
pass
# Read input file into class and close file # Read input file into class and close file
inputFile.seek(loc) inputFile.seek(loc)

View File

@ -1158,10 +1158,17 @@ def parseHandStartTime(topline, site):
isUTC=False isUTC=False
if site=="ftp": if site=="ftp":
# Full Tilt Sit'n'Go
# Full Tilt Poker Game #10311865543: $1 + $0.25 Sit & Go (78057629), Table 1 - 25/50 - No Limit Hold'em - 0:07:45 ET - 2009/01/29
# Cash Game:
# Full Tilt Poker Game #9403951181: Table CR - tay - $0.05/$0.10 - No Limit Hold'em - 9:40:20 ET - 2008/12/09
# Full Tilt Poker Game #9468383505: Table Bike (deep 6) - $0.05/$0.10 - No Limit Hold'em - 5:09:36 ET - 2008/12/13
pos = topline.find(" ", len(topline)-26)+1 pos = topline.find(" ", len(topline)-26)+1
tmp = topline[pos:] tmp = topline[pos:]
#print "year:", tmp[14:18], "month", tmp[19:21], "day", tmp[22:24], "hour", tmp[0:2], "minute", tmp[3:5], "second", tmp[6:8]
result = datetime.datetime(int(tmp[14:18]), int(tmp[19:21]), int(tmp[22:24]), int(tmp[0:2]), int(tmp[3:5]), int(tmp[6:8])) rexx = '(?P<HR>[0-9]+):(?P<MIN>[0-9]+):(?P<SEC>[0-9]+) ET [\- ]+(?P<YEAR>[0-9]{4})\/(?P<MON>[0-9]{2})\/(?P<DAY>[0-9]{2})'
m = re.search(rexx,tmp)
result = datetime.datetime(int(m.group('YEAR')), int(m.group('MON')), int(m.group('DAY')), int(m.group('HR')), int(m.group('MIN')), int(m.group('SEC')))
elif site=="ps": elif site=="ps":
if topline.find("UTC")!=-1: if topline.find("UTC")!=-1:
pos1 = topline.find("-")+2 pos1 = topline.find("-")+2