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

Conflicts:
	pyfpdb/Database.py
	pyfpdb/fpdb_db.py

Reverted to upstream versions
This commit is contained in:
Mika Bostrom 2009-07-30 10:47:38 +03:00
commit 74d571a938
29 changed files with 4783 additions and 3699 deletions

View File

@ -34,12 +34,12 @@ 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): def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True, index=0):
"""\ """\
in_path (default '-' = sys.stdin) in_path (default '-' = sys.stdin)
out_path (default '-' = sys.stdout) out_path (default '-' = sys.stdout)
follow : whether to tail -f the input""" follow : whether to tail -f the input"""
HandHistoryConverter.__init__(self, in_path, out_path, sitename="Betfair", follow=follow) # Call super class init. HandHistoryConverter.__init__(self, in_path, out_path, sitename="Betfair", follow=follow, index) # Call super class init.
logging.info("Initialising Betfair converter class") logging.info("Initialising Betfair converter class")
self.filetype = "text" self.filetype = "text"
self.codepage = "cp1252" self.codepage = "cp1252"

View File

@ -24,25 +24,37 @@ def twoStartCards(value1, suit1, value2, suit2):
(y+2) represents rank of second card (2=2 .. 14=Ace) (y+2) represents rank of second card (2=2 .. 14=Ace)
If x > y then pair is suited, if x < y then unsuited""" If x > y then pair is suited, if x < y then unsuited"""
if value1 < 2 or value2 < 2: if value1 < 2 or value2 < 2:
return(0) ret = 0
if (suit1 == suit2 and value1 < value2) or (suit1 != suit2 and value2 > value1): if value1 == value2: # pairs
return(13 * (value2-2) + (value1-1)) ret = (13 * (value2-2) + (value2-1) )
elif suit1 == suit2:
if value1 > value2:
ret = 13 * (value1-2) + (value2-1)
else:
ret = 13 * (value2-2) + (value1-1)
else: else:
return(13 * (value1-2) + (value2-1)) if value1 > value2:
ret = 13 * (value2-2) + (value2-1)
else:
ret = 13 * (value1-2) + (value2-1)
# print "twoStartCards(", value1, suit1, value2, suit2, ")=", ret
return ret
def twoStartCardString(card): def twoStartCardString(card):
""" Function to convert an int representing 2 holdem hole cards (as created by twoStartCards) """ Function to convert an int representing 2 holdem hole cards (as created by twoStartCards)
into a string like AQo """ into a string like AQo """
if card <= 0: ret = 'xx'
return 'xx' if card > 0:
else:
card -= 1 card -= 1
s = ('2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K', 'A') s = ('2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K', 'A')
x = card/13 x = card / 13
y = card - 13*x y = card - 13 * x
if x == y: return(s[x] + s[y]) if x == y: ret = s[x] + s[y]
elif x > y: return(s[x] + s[y] + 's') elif x > y: ret = s[x] + s[y] + 's'
else: return(s[y] + s[x] + 'o') else: ret = s[y] + s[x] + 'o'
# print "twoStartCardString(", card ,") = " + ret
return ret
def fourStartCards(value1, suit1, value2, suit2, value3, suit3, value4, suit4): def fourStartCards(value1, suit1, value2, suit2, value3, suit3, value4, suit4):
""" Function to convert 4 value,suit pairs into a Omaha style starting hand, """ Function to convert 4 value,suit pairs into a Omaha style starting hand,
@ -94,5 +106,23 @@ def valueSuitFromCard(card):
, '2s', '3s', '4s', '5s', '6s', '7s', '8s', '9s', 'Ts', 'Js', 'Qs', 'Ks', 'As' , '2s', '3s', '4s', '5s', '6s', '7s', '8s', '9s', 'Ts', 'Js', 'Qs', 'Ks', 'As'
][card] ) ][card] )
def encodeCard(cardString):
"""Take a card string (Ah) and convert it to the db card code (1)."""
try:
return {'2h': 1, '3h': 2, '4h': 3, '5h': 4, '6h': 5, '7h': 6, '8h': 7, '9h': 8, 'Th': 9, 'Jh': 10, 'Qh': 11, 'Kh': 12, 'Ah': 13,
'2d': 14, '3d': 15, '4d': 16, '5d': 17, '6d': 18, '7d': 19, '8d': 20, '9d': 21, 'Td': 22, 'Jd': 23, 'Qd': 24, 'Kd': 25, 'Ad': 26,
'2c': 27, '3c': 28, '4c': 29, '5c': 30, '6c': 31, '7c': 32, '8c': 33, '9c': 34, 'Tc': 35, 'Jc': 36, 'Qc': 27, 'Kc': 38, 'Ac': 39,
'2s': 40, '3s': 41, '4s': 42, '5s': 43, '6s': 44, '7s': 45, '8s': 46, '9s': 47, 'Ts': 48, 'Js': 49, 'Qs': 50, 'Ks': 51, 'As': 52,
' ': 0
}[cardString]
except:
return 0 # everthing that isn't known is a unknown!
if __name__ == '__main__':
print "fpdb card encoding(same as pokersource)"
for i in xrange(1, 14):
print "card %2d = %s card %2d = %s card %2d = %s card %2d = %s" % \
(i, valueSuitFromCard(i), i+13, valueSuitFromCard(i+13), i+26, valueSuitFromCard(i+26), i+39, valueSuitFromCard(i+39))
print
print encodeCard('7c')

View File

@ -521,6 +521,8 @@ class Config:
db['db-backend'] = 2 db['db-backend'] = 2
elif string.lower(self.supported_databases[name].db_server) == 'postgresql': elif string.lower(self.supported_databases[name].db_server) == 'postgresql':
db['db-backend'] = 3 db['db-backend'] = 3
elif string.lower(self.supported_databases[name].db_server) == 'sqlite':
db['db-backend'] = 4
else: db['db-backend'] = None # this is big trouble else: db['db-backend'] = None # this is big trouble
return db return db

File diff suppressed because it is too large Load Diff

View File

@ -37,7 +37,7 @@ 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): def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True, debugging=False, index=0):
"""\ """\
in_path (default '-' = sys.stdin) in_path (default '-' = sys.stdin)
out_path (default '-' = sys.stdout) out_path (default '-' = sys.stdout)
@ -45,7 +45,7 @@ follow : whether to tail -f the input
autostart: whether to run the thread (or you can call start() yourself) 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...""" debugging: if False, pass on partially supported game types. If true, have a go and error..."""
print "DEBUG: XXXXXXXXXXXXXXX" print "DEBUG: XXXXXXXXXXXXXXX"
HandHistoryConverter.__init__(self, in_path, out_path, sitename="Everleaf", follow=follow) HandHistoryConverter.__init__(self, in_path, out_path, sitename="Everleaf", follow=follow, index=index)
logging.info("Initialising Everleaf converter class") logging.info("Initialising Everleaf converter class")
self.filetype = "text" self.filetype = "text"
self.codepage = "cp1252" self.codepage = "cp1252"
@ -237,11 +237,14 @@ or None if we fail to get the info """
# Also works with Omaha hands. # Also works with Omaha hands.
cards = m.group('CARDS') cards = m.group('CARDS')
cards = [card.strip() for card in cards.split(',')] cards = [card.strip() for card in cards.split(',')]
hand.addHoleCards(cards, m.group('PNAME')) # hand.addHoleCards(cards, m.group('PNAME'))
hand.addHoleCards('PREFLOP', hand.hero, closed=cards, shown=False, mucked=False, dealt=True)
else: else:
#Not involved in hand #Not involved in hand
hand.involved = False hand.involved = False
def readStudPlayerCards(self, hand, street): def readStudPlayerCards(self, hand, street):
# lol. see Plymouth.txt # lol. see Plymouth.txt
logging.warning("Everleaf readStudPlayerCards is only a stub.") logging.warning("Everleaf readStudPlayerCards is only a stub.")
@ -292,7 +295,8 @@ or None if we fail to get the info """
cards = cards.split(', ') cards = cards.split(', ')
player = m.group('PNAME') player = m.group('PNAME')
logging.debug("readShownCards %s cards=%s" % (player, cards)) logging.debug("readShownCards %s cards=%s" % (player, cards))
hand.addShownCards(cards=None, player=m.group('PNAME'), holeandboard=cards) # hand.addShownCards(cards=None, player=m.group('PNAME'), holeandboard=cards)
hand.addShownCards(cards=cards, player=m.group('PNAME'))

File diff suppressed because it is too large Load Diff

View File

@ -23,25 +23,49 @@ import logging
from HandHistoryConverter import * from HandHistoryConverter import *
# Fulltilt HH Format converter # Fulltilt HH Format converter
# TODO: cat tourno and table to make table name for tournaments
class Fulltilt(HandHistoryConverter): class Fulltilt(HandHistoryConverter):
# Static regexes # Static regexes
re_GameInfo = re.compile('- (?P<CURRENCY>\$|)?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) (Ante \$(?P<ANTE>[.0-9]+) )?- (?P<LIMIT>(No Limit|Pot Limit|Limit))? (?P<GAME>(Hold\'em|Omaha Hi|Razz))') re_GameInfo = re.compile('''(?:(?P<TOURNAMENT>.+)\s\((?P<TOURNO>\d+)\),\s)?
.+
-\s(?P<CURRENCY>\$|)?
(?P<SB>[.0-9]+)/
\$?(?P<BB>[.0-9]+)\s
(Ante\s\$?(?P<ANTE>[.0-9]+)\s)?-\s
(?P<LIMIT>(No\sLimit|Pot\sLimit|Limit))?\s
(?P<GAME>(Hold\'em|Omaha\sHi|Omaha\sH/L|7\sCard\sStud|Stud\sH/L|Razz|Stud\sHi))
''', 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]+): 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_HandInfo = re.compile('''.*\#(?P<HID>[0-9]+):\s
(?:(?P<TOURNAMENT>.+)\s\((?P<TOURNO>\d+)\),\s)?
Table\s
(?P<PLAY>Play\sChip\s|PC)?
(?P<TABLE>[-\s\da-zA-Z]+)\s
(\((?P<TABLEATTRIBUTES>.+)\)\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<DATETIME>.*)
''', re.VERBOSE)
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]+)\)\n') 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>.+)\]")
# These regexes are for FTP only
re_Mixed = re.compile(r'\s\-\s(?P<MIXED>HA|HORSE|HOSE)\s\-\s', re.VERBOSE)
re_Max = re.compile("(?P<MAX>\d+)( max)?", re.MULTILINE)
# NB: if we ever match "Full Tilt Poker" we should also match "FullTiltPoker", which PT Stud erroneously exports. # NB: if we ever match "Full Tilt Poker" we should also match "FullTiltPoker", which PT Stud erroneously exports.
def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True): 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) in_path (default '-' = sys.stdin)
out_path (default '-' = sys.stdout) out_path (default '-' = sys.stdout)
follow : whether to tail -f the input""" follow : whether to tail -f the input"""
HandHistoryConverter.__init__(self, in_path, out_path, sitename="Fulltilt", follow=follow) HandHistoryConverter.__init__(self, in_path, out_path, sitename="Fulltilt", follow=follow, index=index)
logging.info("Initialising Fulltilt converter class") logging.info("Initialising Fulltilt converter class")
self.filetype = "text" self.filetype = "text"
self.codepage = "cp1252" self.codepage = "cp1252"
@ -63,19 +87,24 @@ follow : whether to tail -f the input"""
self.re_BringIn = re.compile(r"^%s brings in for \$?(?P<BRINGIN>[.0-9]+)" % player_re, re.MULTILINE) self.re_BringIn = re.compile(r"^%s brings in for \$?(?P<BRINGIN>[.0-9]+)" % player_re, re.MULTILINE)
self.re_PostBoth = re.compile(r"^%s posts small \& big blinds \[\$? (?P<SBBB>[.0-9]+)" % player_re, re.MULTILINE) self.re_PostBoth = re.compile(r"^%s posts small \& big blinds \[\$? (?P<SBBB>[.0-9]+)" % player_re, re.MULTILINE)
self.re_HeroCards = re.compile(r"^Dealt to %s(?: \[(?P<OLDCARDS>.+?)\])?( \[(?P<NEWCARDS>.+?)\])" % player_re, re.MULTILINE) self.re_HeroCards = re.compile(r"^Dealt to %s(?: \[(?P<OLDCARDS>.+?)\])?( \[(?P<NEWCARDS>.+?)\])" % player_re, re.MULTILINE)
self.re_Action = re.compile(r"^%s(?P<ATYPE> bets| checks| raises to| completes it to| calls| folds)(\s\$(?P<BET>[.\d]+))?" % player_re, re.MULTILINE) self.re_Action = re.compile(r"^%s(?P<ATYPE> bets| checks| raises to| completes it to| calls| folds)( \$?(?P<BET>[.,\d]+))?" % player_re, re.MULTILINE)
self.re_ShowdownAction = re.compile(r"^%s shows \[(?P<CARDS>.*)\]" % player_re, re.MULTILINE) self.re_ShowdownAction = re.compile(r"^%s shows \[(?P<CARDS>.*)\]" % player_re, re.MULTILINE)
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.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.re_SitsOut = re.compile(r"^%s sits out" % player_re, re.MULTILINE) self.re_SitsOut = re.compile(r"^%s sits out" % player_re, re.MULTILINE)
self.re_ShownCards = re.compile(r"^Seat (?P<SEAT>[0-9]+): %s \(.*\) showed \[(?P<CARDS>.*)\].*" % player_re, re.MULTILINE) self.re_ShownCards = re.compile(r"^Seat (?P<SEAT>[0-9]+): %s \(.*\) showed \[(?P<CARDS>.*)\].*" % player_re, re.MULTILINE)
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"],
["ring", "stud", "fl"], ["ring", "stud", "fl"],
["ring", "omaha", "pl"]
["tour", "hold", "nl"],
["tour", "hold", "pl"],
["tour", "hold", "fl"],
["tour", "stud", "fl"],
] ]
def determineGameType(self, handText): def determineGameType(self, handText):
@ -88,7 +117,6 @@ follow : whether to tail -f the input"""
m = self.re_GameInfo.search(handText) m = self.re_GameInfo.search(handText)
if not m: if not m:
return None return None
mg = m.groupdict() mg = m.groupdict()
# translations from captured groups to our info strings # translations from captured groups to our info strings
@ -96,39 +124,41 @@ follow : whether to tail -f the input"""
games = { # base, category games = { # base, category
"Hold'em" : ('hold','holdem'), "Hold'em" : ('hold','holdem'),
'Omaha Hi' : ('hold','omahahi'), 'Omaha Hi' : ('hold','omahahi'),
'Omaha H/L' : ('hold','omahahilo'),
'Razz' : ('stud','razz'), 'Razz' : ('stud','razz'),
'7 Card Stud' : ('stud','studhi') 'Stud Hi' : ('stud','studhi'),
'Stud H/L' : ('stud','studhilo')
} }
currencies = { u'':'EUR', '$':'USD', '':'T$' } currencies = { u'':'EUR', '$':'USD', '':'T$' }
if 'LIMIT' in mg: info['limitType'] = limits[mg['LIMIT']]
info['limitType'] = limits[mg['LIMIT']] info['sb'] = mg['SB']
if 'GAME' in mg: info['bb'] = mg['BB']
if mg['GAME'] != None:
(info['base'], info['category']) = games[mg['GAME']] (info['base'], info['category']) = games[mg['GAME']]
if 'SB' in mg: if mg['CURRENCY'] != None:
info['sb'] = mg['SB']
if 'BB' in mg:
info['bb'] = mg['BB']
if 'CURRENCY' in mg:
info['currency'] = currencies[mg['CURRENCY']] info['currency'] = currencies[mg['CURRENCY']]
if mg['TOURNO'] == None: info['type'] = "ring"
else: info['type'] = "tour"
# 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.
return info return info
def readHandInfo(self, hand): def readHandInfo(self, hand):
m = self.re_HandInfo.search(hand.handText,re.DOTALL) m = self.re_HandInfo.search(hand.handText,re.DOTALL)
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)
return None return None
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")
hand.maxseats = 8 # assume 8-max until we see otherwise
if m.group('TABLEATTRIBUTES'): if m.group('TABLEATTRIBUTES'):
m2 = re.search("(deep )?(\d+)( max)?", m.group('TABLEATTRIBUTES')) m2 = self.re_Max.search(m.group('TABLEATTRIBUTES'))
hand.maxseats = int(m2.group(2)) if m2: hand.maxseats = int(m2.group('MAX'))
hand.tourNo = m.group('TOURNO')
if m.group('PLAY') != None:
hand.gametype['currency'] = 'play'
# 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')
@ -190,6 +220,7 @@ follow : whether to tail -f the input"""
m = self.re_Antes.finditer(hand.handText) m = self.re_Antes.finditer(hand.handText)
for player in m: for player in m:
logging.debug("hand.addAnte(%s,%s)" %(player.group('PNAME'), player.group('ANTE'))) logging.debug("hand.addAnte(%s,%s)" %(player.group('PNAME'), player.group('ANTE')))
# if player.group() !=
hand.addAnte(player.group('PNAME'), player.group('ANTE')) hand.addAnte(player.group('PNAME'), player.group('ANTE'))
def readBringIn(self, hand): def readBringIn(self, hand):
@ -198,79 +229,46 @@ follow : whether to tail -f the input"""
logging.debug("Player bringing in: %s for %s" %(m.group('PNAME'), m.group('BRINGIN'))) logging.debug("Player bringing in: %s for %s" %(m.group('PNAME'), m.group('BRINGIN')))
hand.addBringIn(m.group('PNAME'), m.group('BRINGIN')) hand.addBringIn(m.group('PNAME'), m.group('BRINGIN'))
else: else:
logging.warning("No bringin found") logging.warning("No bringin found, handid =%s" % hand.handid)
def readButton(self, hand): def readButton(self, hand):
hand.buttonpos = int(self.re_Button.search(hand.handText).group('BUTTON')) hand.buttonpos = int(self.re_Button.search(hand.handText).group('BUTTON'))
def readHeroCards(self, hand): def readHeroCards(self, hand):
m = self.re_HeroCards.search(hand.handText) # streets PREFLOP, PREDRAW, and THIRD are special cases beacause
if(m == None): # we need to grab hero's cards
#Not involved in hand for street in ('PREFLOP', 'DEAL'):
hand.involved = False if street in hand.streets.keys():
else: m = self.re_HeroCards.finditer(hand.streets[street])
hand.hero = m.group('PNAME') for found in m:
# "2c, qh" -> set(["2c","qc"]) # if m == None:
# Also works with Omaha hands. # hand.involved = False
cards = m.group('NEWCARDS') # else:
cards = [c.strip() for c in cards.split(' ')] hand.hero = found.group('PNAME')
hand.addHoleCards(cards, m.group('PNAME')) newcards = found.group('NEWCARDS').split(' ')
hand.addHoleCards(street, hand.hero, closed=newcards, shown=False, mucked=False, dealt=True)
for street, text in hand.streets.iteritems():
if not text or street in ('PREFLOP', 'DEAL'): continue # already done these
m = self.re_HeroCards.finditer(hand.streets[street])
for found in m:
player = found.group('PNAME')
if found.group('NEWCARDS') == None:
newcards = []
else:
newcards = found.group('NEWCARDS').split(' ')
if found.group('OLDCARDS') == None:
oldcards = []
else:
oldcards = found.group('OLDCARDS').split(' ')
if street == 'THIRD' and len(oldcards) == 2: # hero in stud game
hand.hero = player
hand.dealt.add(player) # need this for stud??
hand.addHoleCards(street, player, closed=oldcards, open=newcards, shown=False, mucked=False, dealt=False)
else:
hand.addHoleCards(street, player, open=newcards, closed=oldcards, shown=False, mucked=False, dealt=False)
def readStudPlayerCards(self, hand, street):
# This could be the most tricky one to get right.
# It looks for cards dealt in 'street',
# which may or may not be in the section of the hand designated 'street' by markStreets earlier.
# Here's an example at FTP of what 'THIRD' and 'FOURTH' look like to hero PokerAscetic
#
#"*** 3RD STREET ***
#Dealt to BFK23 [Th]
#Dealt to cutiepr1nnymaid [8c]
#Dealt to PokerAscetic [7c 8s] [3h]
#..."
#
#"*** 4TH STREET ***
#Dealt to cutiepr1nnymaid [8c] [2s]
#Dealt to PokerAscetic [7c 8s 3h] [5s]
#..."
#Note that hero's first two holecards are only reported at 3rd street as 'old' cards.
logging.debug("readStudPlayerCards")
m = self.re_HeroCards.finditer(hand.streets[street])
for player in m:
logging.debug(player.groupdict())
(pname, oldcards, newcards) = (player.group('PNAME'), player.group('OLDCARDS'), player.group('NEWCARDS'))
if oldcards:
oldcards = [c.strip() for c in oldcards.split(' ')]
if newcards:
newcards = [c.strip() for c in newcards.split(' ')]
# options here:
# (1) we trust the hand will know what to do -- probably check that the old cards match what it already knows, and add the newcards to this street.
# (2) we're the experts at this particular history format and we know how we're going to be called (once for each street in Hand.streetList)
# so call addPlayerCards with the appropriate information.
# I favour (2) here but I'm afraid it is rather stud7-specific.
# in the following, the final list of cards will be in 'newcards' whilst if the first list exists (most of the time it does) it will be in 'oldcards'
if street=='ANTES':
return
elif street=='THIRD':
# we'll have observed hero holecards in CARDS and thirdstreet open cards in 'NEWCARDS'
# hero: [xx][o]
# others: [o]
hand.addPlayerCards(player = player.group('PNAME'), street = street, closed = oldcards, open = newcards)
elif street in ('FOURTH', 'FIFTH', 'SIXTH'):
# 4th:
# hero: [xxo] [o]
# others: [o] [o]
# 5th:
# hero: [xxoo] [o]
# others: [oo] [o]
# 6th:
# hero: [xxooo] [o]
# others: [ooo] [o]
hand.addPlayerCards(player = player.group('PNAME'), street = street, open = newcards)
# we may additionally want to check the earlier streets tally with what we have but lets trust it for now.
elif street=='SEVENTH' and newcards:
# hero: [xxoooo] [x]
# others: not reported.
hand.addPlayerCards(player = player.group('PNAME'), street = street, closed = newcards)
def readAction(self, hand, street): def readAction(self, hand, street):
m = self.re_Action.finditer(hand.streets[street]) m = self.re_Action.finditer(hand.streets[street])
@ -308,6 +306,29 @@ follow : whether to tail -f the input"""
cards = cards.split(' ') cards = cards.split(' ')
hand.addShownCards(cards=cards, player=m.group('PNAME')) hand.addShownCards(cards=cards, player=m.group('PNAME'))
def guessMaxSeats(self, hand):
"""Return a guess at max_seats when not specified in HH."""
mo = self.maxOccSeat(hand)
if mo == 10: return 10 #that was easy
if hand.gametype['base'] == 'stud':
if mo <= 8: return 8
else: return mo
if hand.gametype['base'] == 'draw':
if mo <= 6: return 6
else: return mo
if mo == 2: return 2
if mo <= 6: return 6
return 9
def readOther(self, hand):
m = self.re_Mixed.search(self.in_path)
if m == None: hand.mixed = None
else:
hand.mixed = self.mixes[m.groupdict()['MIXED']]
if __name__ == "__main__": if __name__ == "__main__":
parser = OptionParser() parser = OptionParser()

View File

@ -154,31 +154,40 @@ class GuiAutoImport (threading.Thread):
# results to the same pipe. This means that self.path should be a a list of dirs # results to the same pipe. This means that self.path should be a a list of dirs
# to watch. # to watch.
if widget.get_active(): # toggled on if widget.get_active(): # toggled on
self.doAutoImportBool = True # - Does the lock acquisition need to be more sophisticated for multiple dirs?
widget.set_label(u' _Stop Autoimport ') # (see comment above about what to do if pipe already open)
if self.pipe_to_hud is None: # - Ideally we want to release the lock if the auto-import is killed by some
if os.name == 'nt': # kind of exception - is this possible?
command = "python HUD_main.py" + " " + self.settings['cl_options'] if self.settings['global_lock'].acquire(False): # returns false immediately if lock not acquired
bs = 0 # windows is not happy with line buffing here print "\nGlobal lock taken ..."
self.pipe_to_hud = subprocess.Popen(command, bufsize = bs, stdin = subprocess.PIPE, self.doAutoImportBool = True
universal_newlines=True) widget.set_label(u' _Stop Autoimport ')
else: if self.pipe_to_hud is None:
command = os.path.join(sys.path[0], 'HUD_main.py') if os.name == 'nt':
cl = [command, ] + string.split(self.settings['cl_options']) command = "python HUD_main.py" + " " + self.settings['cl_options']
self.pipe_to_hud = subprocess.Popen(cl, bufsize = 1, stdin = subprocess.PIPE, bs = 0 # windows is not happy with line buffing here
universal_newlines=True) self.pipe_to_hud = subprocess.Popen(command, bufsize = bs, stdin = subprocess.PIPE,
universal_newlines=True)
else:
command = os.path.join(sys.path[0], 'HUD_main.py')
cl = [command, ] + string.split(self.settings['cl_options'])
self.pipe_to_hud = subprocess.Popen(cl, bufsize = 1, stdin = subprocess.PIPE,
universal_newlines=True)
# Add directories to importer object. # Add directories to importer object.
for site in self.input_settings: for site in self.input_settings:
self.importer.addImportDirectory(self.input_settings[site][0], True, site, self.input_settings[site][1]) self.importer.addImportDirectory(self.input_settings[site][0], True, site, self.input_settings[site][1])
print "Adding import directories - Site: " + site + " dir: "+ str(self.input_settings[site][0]) print "Adding import directories - Site: " + site + " dir: "+ str(self.input_settings[site][0])
self.do_import() self.do_import()
interval=int(self.intervalEntry.get_text()) interval=int(self.intervalEntry.get_text())
gobject.timeout_add(interval*1000, self.do_import) gobject.timeout_add(interval*1000, self.do_import)
else:
print "auto-import aborted - global lock not available"
else: # toggled off else: # toggled off
self.settings['global_lock'].release()
self.doAutoImportBool = False # do_import will return this and stop the gobject callback timer self.doAutoImportBool = False # do_import will return this and stop the gobject callback timer
print "Stopping autoimport" print "Stopping autoimport - global lock released."
if self.pipe_to_hud.poll() is not None: if self.pipe_to_hud.poll() is not None:
print "HUD already terminated" print "HUD already terminated"
else: else:
@ -187,8 +196,6 @@ class GuiAutoImport (threading.Thread):
self.pipe_to_hud = None self.pipe_to_hud = None
self.startButton.set_label(u' _Start Autoimport ') self.startButton.set_label(u' _Start Autoimport ')
#end def GuiAutoImport.startClicked #end def GuiAutoImport.startClicked
def get_vbox(self): def get_vbox(self):

View File

@ -34,6 +34,7 @@ import Configuration
class GuiBulkImport(): class GuiBulkImport():
# not used
def import_dir(self): def import_dir(self):
"""imports a directory, non-recursive. todo: move this to fpdb_import so CLI can use it""" """imports a directory, non-recursive. todo: move this to fpdb_import so CLI can use it"""
@ -49,47 +50,57 @@ class GuiBulkImport():
self.importer.RunImportThreaded() self.importer.RunImportThreaded()
def load_clicked(self, widget, data=None): def load_clicked(self, widget, data=None):
# get the dir to import from the chooser # Does the lock acquisition need to be more sophisticated for multiple dirs?
self.inputFile = self.chooser.get_filename() # (see comment above about what to do if pipe already open)
if self.settings['global_lock'].acquire(False): # returns false immediately if lock not acquired
try:
print "\nGlobal lock taken ..."
# get the dir to import from the chooser
self.inputFile = self.chooser.get_filename()
# get the import settings from the gui and save in the importer # get the import settings from the gui and save in the importer
self.importer.setHandCount(int(self.spin_hands.get_text())) self.importer.setHandCount(int(self.spin_hands.get_text()))
self.importer.setMinPrint(int(self.spin_hands.get_text())) self.importer.setMinPrint(int(self.spin_hands.get_text()))
self.importer.setQuiet(self.chk_st_st.get_active()) self.importer.setQuiet(self.chk_st_st.get_active())
self.importer.setFailOnError(self.chk_fail.get_active()) self.importer.setFailOnError(self.chk_fail.get_active())
self.importer.setThreads(int(self.spin_threads.get_text())) self.importer.setThreads(int(self.spin_threads.get_text()))
self.importer.setHandsInDB(self.n_hands_in_db) self.importer.setHandsInDB(self.n_hands_in_db)
cb_model = self.cb_dropindexes.get_model() cb_model = self.cb_dropindexes.get_model()
cb_index = self.cb_dropindexes.get_active() cb_index = self.cb_dropindexes.get_active()
if cb_index: if cb_index:
self.importer.setDropIndexes(cb_model[cb_index][0]) self.importer.setDropIndexes(cb_model[cb_index][0])
else:
self.importer.setDropIndexes("auto")
sitename = self.cbfilter.get_model()[self.cbfilter.get_active()][0]
self.lab_info.set_text("Importing")
self.importer.addBulkImportImportFileOrDir(self.inputFile, site = sitename)
self.importer.setCallHud(False)
starttime = time()
(stored, dups, partial, errs, ttime) = self.importer.runImport()
ttime = time() - starttime
if ttime == 0:
ttime = 1
print 'GuiBulkImport.load done: Stored: %d \tDuplicates: %d \tPartial: %d \tErrors: %d in %s seconds - %d/sec'\
% (stored, dups, partial, errs, ttime, stored / ttime)
self.importer.clearFileList()
self.lab_info.set_text("Import finished")
except:
print "bulkimport.loadclicked error: "+str(sys.exc_value)
pass
self.settings['global_lock'].release()
else: else:
self.importer.setDropIndexes("auto") print "bulk-import aborted - global lock not available"
sitename = self.cbfilter.get_model()[self.cbfilter.get_active()][0]
self.lab_info.set_text("Importing")
self.importer.addBulkImportImportFileOrDir(self.inputFile, site = sitename)
self.importer.setCallHud(False)
starttime = time()
(stored, dups, partial, errs, ttime) = self.importer.runImport()
ttime = time() - starttime
if ttime == 0:
ttime = 1
print 'GuiBulkImport.import_dir done: Stored: %d \tDuplicates: %d \tPartial: %d \tErrors: %d in %s seconds - %d/sec'\
% (stored, dups, partial, errs, ttime, stored / ttime)
self.importer.clearFileList()
self.lab_info.set_text("Import finished")
def get_vbox(self): def get_vbox(self):
"""returns the vbox of this thread""" """returns the vbox of this thread"""
return self.vbox return self.vbox
def __init__(self, settings, config): def __init__(self, settings, config, sql = None):
self.settings = settings self.settings = settings
self.config = config self.config = config
self.importer = fpdb_import.Importer(self, self.settings, self.importer = fpdb_import.Importer(self, self.settings, config, sql)
config)
self.vbox = gtk.VBox(False, 0) self.vbox = gtk.VBox(False, 0)
self.vbox.show() self.vbox.show()
@ -196,10 +207,11 @@ class GuiBulkImport():
self.load_button.show() self.load_button.show()
# see how many hands are in the db and adjust accordingly # see how many hands are in the db and adjust accordingly
tcursor = self.importer.fdb.db.cursor() tcursor = self.importer.database.cursor
tcursor.execute("Select count(1) from Hands") tcursor.execute("Select count(1) from Hands")
row = tcursor.fetchone() row = tcursor.fetchone()
tcursor.close() tcursor.close()
self.importer.database.rollback()
self.n_hands_in_db = row[0] self.n_hands_in_db = row[0]
if self.n_hands_in_db == 0: if self.n_hands_in_db == 0:
self.cb_dropindexes.set_active(2) self.cb_dropindexes.set_active(2)
@ -252,7 +264,8 @@ def main(argv=None):
else: else:
#Do something useful #Do something useful
importer = fpdb_import.Importer(False,settings, config) importer = fpdb_import.Importer(False,settings, config)
importer.setDropIndexes("auto") # importer.setDropIndexes("auto")
importer.setDropIndexes("don't drop")
importer.setFailOnError(options.failOnError) importer.setFailOnError(options.failOnError)
importer.addBulkImportImportFileOrDir(os.path.expanduser(options.filename), site=options.filtername) importer.addBulkImportImportFileOrDir(os.path.expanduser(options.filename), site=options.filtername)
importer.setCallHud(False) importer.setCallHud(False)

View File

@ -38,20 +38,19 @@ except:
and HUD are NOT affected by this problem.""" and HUD are NOT affected by this problem."""
import fpdb_import import fpdb_import
import fpdb_db import Database
import Filters import Filters
class GuiGraphViewer (threading.Thread): class GuiGraphViewer (threading.Thread):
def __init__(self, querylist, config, debug=True): def __init__(self, querylist, config, debug=True):
"""Constructor for GraphViewer""" """Constructor for GraphViewer"""
self.debug=debug
#print "start of GraphViewer constructor"
self.db = fpdb_db.fpdb_db() # sets self.fdb.db self.fdb.cursor and self.fdb.sql
self.db.do_connect(config)
self.sql = querylist self.sql = querylist
self.conf = config self.conf = config
self.debug = debug
#print "start of GraphViewer constructor"
self.db = Database.Database(self.conf, sql=self.sql)
filters_display = { "Heroes" : True, filters_display = { "Heroes" : True,
"Sites" : True, "Sites" : True,
@ -63,7 +62,7 @@ class GuiGraphViewer (threading.Thread):
"Button2" : True "Button2" : True
} }
self.filters = Filters.Filters(self.db, config, querylist, display = filters_display) self.filters = Filters.Filters(self.db, self.conf, self.sql, display = filters_display)
self.filters.registerButton1Name("Refresh Graph") self.filters.registerButton1Name("Refresh Graph")
self.filters.registerButton1Callback(self.generateGraph) self.filters.registerButton1Callback(self.generateGraph)
self.filters.registerButton2Name("Export to File") self.filters.registerButton2Name("Export to File")
@ -90,7 +89,7 @@ class GuiGraphViewer (threading.Thread):
self.canvas = None self.canvas = None
self.db.db.rollback() self.db.rollback()
################################# #################################
# #
@ -126,7 +125,7 @@ class GuiGraphViewer (threading.Thread):
#end def get_vbox #end def get_vbox
def clearGraphData(self): def clearGraphData(self):
self.fig.clf() self.fig.clear()
if self.canvas is not None: if self.canvas is not None:
self.canvas.destroy() self.canvas.destroy()
@ -154,7 +153,7 @@ class GuiGraphViewer (threading.Thread):
if not sitenos: if not sitenos:
#Should probably pop up here. #Should probably pop up here.
print "No sites selected - defaulting to PokerStars" print "No sites selected - defaulting to PokerStars"
sitenos = [2] return
if not playerids: if not playerids:
print "No player ids found" print "No player ids found"
@ -197,6 +196,7 @@ class GuiGraphViewer (threading.Thread):
self.graphBox.add(self.canvas) self.graphBox.add(self.canvas)
self.canvas.show() self.canvas.show()
self.canvas.draw()
#self.exportButton.set_sensitive(True) #self.exportButton.set_sensitive(True)
#end of def showClicked #end of def showClicked
@ -205,7 +205,7 @@ class GuiGraphViewer (threading.Thread):
# print "DEBUG: getRingProfitGraph" # print "DEBUG: getRingProfitGraph"
start_date, end_date = self.filters.getDates() start_date, end_date = self.filters.getDates()
#Buggered if I can find a way to do this 'nicely' take a list of intergers and longs #Buggered if I can find a way to do this 'nicely' take a list of integers and longs
# and turn it into a tuple readale by sql. # and turn it into a tuple readale by sql.
# [5L] into (5) not (5,) and [5L, 2829L] into (5, 2829) # [5L] into (5) not (5,) and [5L, 2829L] into (5, 2829)
nametest = str(tuple(names)) nametest = str(tuple(names))
@ -229,7 +229,7 @@ class GuiGraphViewer (threading.Thread):
self.db.cursor.execute(tmp) self.db.cursor.execute(tmp)
#returns (HandId,Winnings,Costs,Profit) #returns (HandId,Winnings,Costs,Profit)
winnings = self.db.cursor.fetchall() winnings = self.db.cursor.fetchall()
self.db.db.rollback() self.db.rollback()
if(winnings == ()): if(winnings == ()):
return None return None

View File

@ -20,34 +20,35 @@ import pygtk
pygtk.require('2.0') pygtk.require('2.0')
import gtk import gtk
import os import os
import sys
from time import time, strftime from time import time, strftime
import Card import Card
import fpdb_import import fpdb_import
import Database
import fpdb_db import fpdb_db
import Filters import Filters
import FpdbSQLQueries
class GuiPlayerStats (threading.Thread): class GuiPlayerStats (threading.Thread):
def __init__(self, config, querylist, mainwin, debug=True): def __init__(self, config, querylist, mainwin, debug=True):
self.debug=debug self.debug = debug
self.conf=config self.conf = config
self.main_window=mainwin self.main_window = mainwin
self.sql = querylist
self.MYSQL_INNODB = 2 self.MYSQL_INNODB = 2
self.PGSQL = 3 self.PGSQL = 3
self.SQLITE = 4 self.SQLITE = 4
# create new db connection to avoid conflicts with other threads # create new db connection to avoid conflicts with other threads
self.db = fpdb_db.fpdb_db() self.db = Database.Database(self.conf, sql=self.sql)
self.db.do_connect(self.conf) self.cursor = self.db.cursor
self.cursor=self.db.cursor
self.sql = querylist
settings = {} settings = {}
settings.update(config.get_db_parameters()) settings.update(self.conf.get_db_parameters())
settings.update(config.get_tv_parameters()) settings.update(self.conf.get_tv_parameters())
settings.update(config.get_import_parameters()) settings.update(self.conf.get_import_parameters())
settings.update(config.get_default_paths()) settings.update(self.conf.get_default_paths())
# text used on screen stored here so that it can be configured # text used on screen stored here so that it can be configured
self.filterText = {'handhead':'Hand Breakdown for all levels listed above' self.filterText = {'handhead':'Hand Breakdown for all levels listed above'
@ -66,7 +67,7 @@ class GuiPlayerStats (threading.Thread):
"Button2" : True "Button2" : True
} }
self.filters = Filters.Filters(self.db, config, querylist, display = filters_display) self.filters = Filters.Filters(self.db, self.conf, self.sql, display = filters_display)
self.filters.registerButton1Name("_Filters") self.filters.registerButton1Name("_Filters")
self.filters.registerButton1Callback(self.showDetailFilter) self.filters.registerButton1Callback(self.showDetailFilter)
self.filters.registerButton2Name("_Refresh") self.filters.registerButton2Name("_Refresh")
@ -216,7 +217,7 @@ class GuiPlayerStats (threading.Thread):
flags = [True] flags = [True]
self.addTable(vbox1, 'playerDetailedStats', flags, playerids, sitenos, limits, seats, groups, dates) self.addTable(vbox1, 'playerDetailedStats', flags, playerids, sitenos, limits, seats, groups, dates)
self.db.db.commit() self.db.rollback()
print "Stats page displayed in %4.2f seconds" % (time() - starttime) print "Stats page displayed in %4.2f seconds" % (time() - starttime)
#end def fillStatsFrame(self, vbox): #end def fillStatsFrame(self, vbox):
@ -280,8 +281,10 @@ class GuiPlayerStats (threading.Thread):
if column[colalias] == 'plposition': if column[colalias] == 'plposition':
if value == 'B': if value == 'B':
value = 'BB' value = 'BB'
if value == 'S': elif value == 'S':
value = 'SB' value = 'SB'
elif value == '0':
value = 'Btn'
else: else:
if column[colalias] == 'game': if column[colalias] == 'game':
if holecards: if holecards:
@ -379,7 +382,8 @@ class GuiPlayerStats (threading.Thread):
# Group by position? # Group by position?
if groups['posn']: if groups['posn']:
query = query.replace("<position>", 'hp.position') #query = query.replace("<position>", "case hp.position when '0' then 'Btn' else hp.position end")
query = query.replace("<position>", "hp.position")
# set flag in self.columns to show posn column # set flag in self.columns to show posn column
[x for x in self.columns if x[0] == 'plposition'][0][1] = True [x for x in self.columns if x[0] == 'plposition'][0][1] = True
else: else:

View File

@ -23,29 +23,28 @@ import os
from time import time, strftime from time import time, strftime
import fpdb_import import fpdb_import
import fpdb_db import Database
import Filters import Filters
import FpdbSQLQueries import FpdbSQLQueries
class GuiPositionalStats (threading.Thread): class GuiPositionalStats (threading.Thread):
def __init__(self, config, querylist, debug=True): def __init__(self, config, querylist, debug=True):
self.debug=debug self.debug = debug
self.conf=config self.conf = config
self.sql = querylist
self.MYSQL_INNODB = 2 self.MYSQL_INNODB = 2
self.PGSQL = 3 self.PGSQL = 3
self.SQLITE = 4 self.SQLITE = 4
# create new db connection to avoid conflicts with other threads # create new db connection to avoid conflicts with other threads
self.db = fpdb_db.fpdb_db() self.db = Database.Database(self.conf, sql=self.sql)
self.db.do_connect(self.conf) self.cursor = self.db.cursor
self.cursor=self.db.cursor
self.sql = querylist
settings = {} settings = {}
settings.update(config.get_db_parameters()) settings.update(self.conf.get_db_parameters())
settings.update(config.get_tv_parameters()) settings.update(self.conf.get_tv_parameters())
settings.update(config.get_import_parameters()) settings.update(self.conf.get_import_parameters())
settings.update(config.get_default_paths()) settings.update(self.conf.get_default_paths())
filters_display = { "Heroes" : True, filters_display = { "Heroes" : True,
"Sites" : True, "Sites" : True,
@ -59,7 +58,7 @@ class GuiPositionalStats (threading.Thread):
"Button2" : False "Button2" : False
} }
self.filters = Filters.Filters(self.db, config, querylist, display = filters_display) self.filters = Filters.Filters(self.db, self.conf, self.sql, display = filters_display)
self.filters.registerButton1Name("Refresh") self.filters.registerButton1Name("Refresh")
self.filters.registerButton1Callback(self.refreshStats) self.filters.registerButton1Callback(self.refreshStats)
@ -319,7 +318,7 @@ class GuiPositionalStats (threading.Thread):
row = row + 1 row = row + 1
vbox.show_all() vbox.show_all()
self.db.db.rollback() self.db.rollback()
print "Positional Stats page displayed in %4.2f seconds" % (time() - starttime) print "Positional Stats page displayed in %4.2f seconds" % (time() - starttime)
#end def fillStatsFrame(self, vbox): #end def fillStatsFrame(self, vbox):

View File

@ -25,29 +25,28 @@ from numpy import diff, nonzero
import Card import Card
import fpdb_import import fpdb_import
import fpdb_db import Database
import Filters import Filters
import FpdbSQLQueries import FpdbSQLQueries
class GuiSessionViewer (threading.Thread): class GuiSessionViewer (threading.Thread):
def __init__(self, config, querylist, debug=True): def __init__(self, config, querylist, debug=True):
self.debug=debug self.debug = debug
self.conf=config self.conf = config
self.sql = querylist
self.MYSQL_INNODB = 2 self.MYSQL_INNODB = 2
self.PGSQL = 3 self.PGSQL = 3
self.SQLITE = 4 self.SQLITE = 4
# create new db connection to avoid conflicts with other threads # create new db connection to avoid conflicts with other threads
self.db = fpdb_db.fpdb_db() self.db = Database.Database(self.conf, sql=self.sql)
self.db.do_connect(self.conf) self.cursor = self.db.cursor
self.cursor=self.db.cursor
self.sql = querylist
settings = {} settings = {}
settings.update(config.get_db_parameters()) settings.update(self.conf.get_db_parameters())
settings.update(config.get_tv_parameters()) settings.update(self.conf.get_tv_parameters())
settings.update(config.get_import_parameters()) settings.update(self.conf.get_import_parameters())
settings.update(config.get_default_paths()) settings.update(self.conf.get_default_paths())
# text used on screen stored here so that it can be configured # text used on screen stored here so that it can be configured
self.filterText = {'handhead':'Hand Breakdown for all levels listed above' self.filterText = {'handhead':'Hand Breakdown for all levels listed above'
@ -66,7 +65,7 @@ class GuiSessionViewer (threading.Thread):
"Button2" : True "Button2" : True
} }
self.filters = Filters.Filters(self.db, settings, config, querylist, display = filters_display) self.filters = Filters.Filters(self.db, self.conf, self.sql, display = filters_display)
self.filters.registerButton2Name("_Refresh") self.filters.registerButton2Name("_Refresh")
self.filters.registerButton2Callback(self.refreshStats) self.filters.registerButton2Callback(self.refreshStats)
@ -195,7 +194,7 @@ class GuiSessionViewer (threading.Thread):
flags = [True] flags = [True]
self.addTable(vbox1, 'playerDetailedStats', flags, playerids, sitenos, limits, seats) self.addTable(vbox1, 'playerDetailedStats', flags, playerids, sitenos, limits, seats)
self.db.db.commit() self.db.rollback()
print "Stats page displayed in %4.2f seconds" % (time() - starttime) print "Stats page displayed in %4.2f seconds" % (time() - starttime)
#end def fillStatsFrame(self, vbox): #end def fillStatsFrame(self, vbox):

View File

@ -159,6 +159,50 @@
<location seat="9" x="70" y="53"> </location> <location seat="9" x="70" y="53"> </location>
</layout> </layout>
</site> </site>
<site enabled="False"
site_name="Win2day"
table_finder="Win2day.exe"
screen_name="YOUR SCREEN NAME HERE"
site_path=""
HH_path=""
decoder="everleaf_decode_table"
converter="Win2dayToFpdb"
supported_games="holdem">
<layout fav_seat="0" height="547" max="8" width="794">
<location seat="1" x="640" y="64"> </location>
<location seat="2" x="650" y="230"> </location>
<location seat="3" x="650" y="385"> </location>
<location seat="4" x="588" y="425"> </location>
<location seat="5" x="92" y="425"> </location>
<location seat="6" x="0" y="373"> </location>
<location seat="7" x="0" y="223"> </location>
<location seat="8" x="25" y="50"> </location>
</layout>
<layout fav_seat="0" height="547" max="6" width="794">
<location seat="1" x="640" y="58"> </location>
<location seat="2" x="654" y="288"> </location>
<location seat="3" x="615" y="424"> </location>
<location seat="4" x="70" y="421"> </location>
<location seat="5" x="0" y="280"> </location>
<location seat="6" x="70" y="58"> </location>
</layout>
<layout fav_seat="0" height="547" max="2" width="794">
<location seat="1" x="651" y="288"> </location>
<location seat="2" x="10" y="288"> </location>
</layout>
<layout fav_seat="0" height="547" max="9" width="794">
<location seat="1" x="634" y="38"> </location>
<location seat="2" x="667" y="184"> </location>
<location seat="3" x="667" y="321"> </location>
<location seat="4" x="667" y="445"> </location>
<location seat="5" x="337" y="459"> </location>
<location seat="6" x="0" y="400"> </location>
<location seat="7" x="0" y="322"> </location>
<location seat="8" x="0" y="181"> </location>
<location seat="9" x="70" y="53"> </location>
</layout>
</site>
</supported_sites> </supported_sites>
<supported_games> <supported_games>
@ -292,6 +336,7 @@
<hhc site="PokerStars" converter="PokerStarsToFpdb"/> <hhc site="PokerStars" converter="PokerStarsToFpdb"/>
<hhc site="Full Tilt Poker" converter="FulltiltToFpdb"/> <hhc site="Full Tilt Poker" converter="FulltiltToFpdb"/>
<hhc site="Everleaf" converter="EverleafToFpdb"/> <hhc site="Everleaf" converter="EverleafToFpdb"/>
<hhc site="Win2day" converter="Win2dayToFpdb"/>
</hhcs> </hhcs>
<supported_databases> <supported_databases>
@ -300,4 +345,3 @@
</FreePokerToolsConfig> </FreePokerToolsConfig>

View File

@ -32,14 +32,14 @@ import pprint
import DerivedStats import DerivedStats
import Card import Card
class Hand: class Hand(object):
###############################################################3 ###############################################################3
# Class Variables # Class Variables
UPS = {'a':'A', 't':'T', 'j':'J', 'q':'Q', 'k':'K', 'S':'s', 'C':'c', 'H':'h', 'D':'d'} UPS = {'a':'A', 't':'T', 'j':'J', 'q':'Q', 'k':'K', 'S':'s', 'C':'c', 'H':'h', 'D':'d'}
LCS = {'H':'h', 'D':'d', 'C':'c', 'S':'s'} LCS = {'H':'h', 'D':'d', 'C':'c', 'S':'s'}
SYMBOL = {'USD': '$', 'EUR': u'E', 'T$': '', 'play': ''} SYMBOL = {'USD': '$', 'EUR': u'$', 'T$': '', 'play': ''}
MS = {'horse' : 'HORSE', '8game' : '8-Game', 'hose' : 'HOSE'} MS = {'horse' : 'HORSE', '8game' : '8-Game', 'hose' : 'HOSE', 'ha': 'HA'}
def __init__(self, sitename, gametype, handText, builtFrom = "HHC"): def __init__(self, sitename, gametype, handText, builtFrom = "HHC"):
@ -51,7 +51,7 @@ class Hand:
self.handid = 0 self.handid = 0
self.tablename = "" self.tablename = ""
self.hero = "" self.hero = ""
self.maxseats = 10 self.maxseats = None
self.counted_seats = 0 self.counted_seats = 0
self.buttonpos = 0 self.buttonpos = 0
self.tourNo = None self.tourNo = None
@ -81,7 +81,6 @@ class Hand:
self.holecards[street] = {} # dict from player names to holecards self.holecards[street] = {} # dict from player names to holecards
self.discards[street] = {} # dict from player names to dicts by street ... of tuples ... of discarded holecards self.discards[street] = {} # dict from player names to dicts by street ... of tuples ... of discarded holecards
# Collections indexed by player names # Collections indexed by player names
# self.holecards = {} # dict from player names to dicts by street ... of tuples ... of holecards
self.stacks = {} self.stacks = {}
self.collected = [] #list of ? self.collected = [] #list of ?
self.collectees = {} # dict from player names to amounts collected (?) self.collectees = {} # dict from player names to amounts collected (?)
@ -92,12 +91,14 @@ class Hand:
self.shown = set() # cards were shown self.shown = set() # cards were shown
self.mucked = set() # cards were mucked at showdown self.mucked = set() # cards were mucked at showdown
# self.action = []
# Things to do with money # Things to do with money
self.pot = Pot() self.pot = Pot()
self.totalpot = None self.totalpot = None
self.totalcollected = None self.totalcollected = None
self.rake = None self.rake = None
# currency symbol for this hand
self.sym = self.SYMBOL[self.gametype['currency']] # save typing! delete this attr when done
self.pot.setSym(self.sym)
def __str__(self): def __str__(self):
vars = ( ("BB", self.bb), vars = ( ("BB", self.bb),
@ -171,6 +172,9 @@ dealt whether they were seen in a 'dealt to' line
self.holecards[street][player] = [open, closed] self.holecards[street][player] = [open, closed]
def prepInsert(self, db):
pass
def insert(self, db): def insert(self, db):
""" Function to insert Hand into database """ Function to insert Hand into database
Should not commit, and do minimal selects. Callers may want to cache commits Should not commit, and do minimal selects. Callers may want to cache commits
@ -259,6 +263,7 @@ chips (string) the chips the player has at the start of the hand (can be None)
If a player has None chips he won't be added.""" If a player has None chips he won't be added."""
logging.debug("addPlayer: %s %s (%s)" % (seat, name, chips)) logging.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
self.players.append([seat, name, chips]) self.players.append([seat, name, chips])
self.stacks[name] = Decimal(chips) self.stacks[name] = Decimal(chips)
self.pot.addPlayer(name) self.pot.addPlayer(name)
@ -297,6 +302,7 @@ If a player has None chips he won't be added."""
def addAnte(self, player, ante): def addAnte(self, player, ante):
logging.debug("%s %s antes %s" % ('ANTES', player, ante)) logging.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
self.bets['ANTES'][player].append(Decimal(ante)) self.bets['ANTES'][player].append(Decimal(ante))
self.stacks[player] -= Decimal(ante) self.stacks[player] -= Decimal(ante)
act = (player, 'posts', "ante", ante, self.stacks[player]==0) act = (player, 'posts', "ante", ante, self.stacks[player]==0)
@ -315,6 +321,7 @@ If a player has None chips he won't be added."""
logging.debug("addBlind: %s posts %s, %s" % (player, blindtype, amount)) logging.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
self.bets['PREFLOP'][player].append(Decimal(amount)) self.bets['PREFLOP'][player].append(Decimal(amount))
self.stacks[player] -= Decimal(amount) self.stacks[player] -= Decimal(amount)
#print "DEBUG %s posts, stack %s" % (player, self.stacks[player]) #print "DEBUG %s posts, stack %s" % (player, self.stacks[player])
@ -332,6 +339,8 @@ 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:
amount = re.sub(u',', u'', amount) #some sites have commas
logging.debug("%s %s calls %s" %(street, player, amount)) logging.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
@ -359,6 +368,7 @@ Add a raise by amountBy on [street] by [player]
# then: C = Bp - Bc (amount to call) # then: C = Bp - Bc (amount to call)
# Rt = Bp + Rb (raise to) # Rt = Bp + Rb (raise to)
# #
amountBy = re.sub(u',', u'', amountBy) #some sites have commas
self.checkPlayerExists(player) self.checkPlayerExists(player)
Rb = Decimal(amountBy) Rb = Decimal(amountBy)
Bp = self.lastBet[street] Bp = self.lastBet[street]
@ -376,6 +386,7 @@ Add a raise by amountBy on [street] by [player]
"""\ """\
For sites which by "raises x" mean "calls and raises putting a total of x in the por". """ For sites which by "raises x" mean "calls and raises putting a total of x in the por". """
self.checkPlayerExists(player) self.checkPlayerExists(player)
amount = re.sub(u',', u'', amount) #some sites have commas
CRb = Decimal(amount) CRb = Decimal(amount)
Bp = self.lastBet[street] Bp = self.lastBet[street]
Bc = reduce(operator.add, self.bets[street][player], 0) Bc = reduce(operator.add, self.bets[street][player], 0)
@ -391,6 +402,7 @@ Add a raise on [street] by [player] to [amountTo]
""" """
#CG - No idea if this function has been test/verified #CG - No idea if this function has been test/verified
self.checkPlayerExists(player) self.checkPlayerExists(player)
amountTo = re.sub(u',', u'', amountTo) #some sites have commas
Bp = self.lastBet[street] 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)
@ -411,6 +423,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)) logging.debug("%s %s bets %s" %(street, player, amount))
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))
self.stacks[player] -= Decimal(amount) self.stacks[player] -= Decimal(amount)
@ -523,55 +536,64 @@ Map the tuple self.gametype onto the pokerstars string describing it
elif act[1] == 'checks': elif act[1] == 'checks':
return ("%s: checks " %(act[0])) return ("%s: checks " %(act[0]))
elif act[1] == 'calls': elif act[1] == 'calls':
return ("%s: calls $%s%s" %(act[0], act[2], ' and is all-in' if act[3] else '')) return ("%s: calls %s%s%s" %(act[0], self.sym, act[2], ' and is all-in' if act[3] else ''))
elif act[1] == 'bets': elif act[1] == 'bets':
return ("%s: bets $%s%s" %(act[0], act[2], ' and is all-in' if act[3] else '')) return ("%s: bets %s%s%s" %(act[0], self.sym, act[2], ' and is all-in' if act[3] else ''))
elif act[1] == 'raises': elif act[1] == 'raises':
return ("%s: raises $%s to $%s%s" %(act[0], act[2], act[3], ' and is all-in' if act[5] else '')) return ("%s: raises %s%s to %s%s%s" %(act[0], self.sym, act[2], self.sym, act[3], ' and is all-in' if act[5] else ''))
elif act[1] == 'completea': elif act[1] == 'completea':
return ("%s: completes to $%s%s" %(act[0], act[2], ' and is all-in' if act[3] else '')) return ("%s: completes to %s%s%s" %(act[0], self.sym, act[2], ' and is all-in' if act[3] else ''))
elif act[1] == 'posts': elif act[1] == 'posts':
if(act[2] == "small blind"): if(act[2] == "small blind"):
return ("%s: posts small blind $%s%s" %(act[0], act[3], ' and is all-in' if act[4] else '')) return ("%s: posts small blind %s%s%s" %(act[0], self.sym, act[3], ' and is all-in' if act[4] else ''))
elif(act[2] == "big blind"): elif(act[2] == "big blind"):
return ("%s: posts big blind $%s%s" %(act[0], act[3], ' and is all-in' if act[4] else '')) return ("%s: posts big blind %s%s%s" %(act[0], self.sym, act[3], ' and is all-in' if act[4] else ''))
elif(act[2] == "both"): elif(act[2] == "both"):
return ("%s: posts small & big blinds $%s%s" %(act[0], act[3], ' and is all-in' if act[4] else '')) return ("%s: posts small & big blinds %s%s%s" %(act[0], self.sym, act[3], ' and is all-in' if act[4] else ''))
elif act[1] == 'bringin': elif act[1] == 'bringin':
return ("%s: brings in for $%s%s" %(act[0], act[2], ' and is all-in' if act[3] else '')) return ("%s: brings in for %s%s%s" %(act[0], self.sym, act[2], ' and is all-in' if act[3] else ''))
elif act[1] == 'discards': elif act[1] == 'discards':
return ("%s: discards %s %s%s" %(act[0], act[2], 'card' if act[2] == 1 else 'cards' , " [" + " ".join(self.discards[act[0]]['DRAWONE']) + "]" if self.hero == act[0] else '')) return ("%s: discards %s %s%s" %(act[0], act[2], 'card' if act[2] == 1 else 'cards' , " [" + " ".join(self.discards[act[0]]['DRAWONE']) + "]" if self.hero == act[0] else ''))
elif act[1] == 'stands pat': elif act[1] == 'stands pat':
return ("%s: stands pat" %(act[0])) return ("%s: stands pat" %(act[0]))
def getStakesAsString(self): def getStakesAsString(self):
retstring = "%s%s/%s%s" % (self.SYMBOL[self.gametype['currency']], self.sb, self.SYMBOL[self.gametype['currency']], self.bb) """Return a string of the stakes of the current hand."""
return retstring return "%s%s/%s%s" % (self.sym, self.sb, self.sym, self.bb)
def writeGameLine(self): def writeGameLine(self):
# print >>fh, ("%s Game #%s: %s ($%s/$%s) - %s" %("PokerStars", self.handid, self.getGameTypeAsString(), self.sb, self.bb, datetime.datetime.strftime(self.starttime,'%Y/%m/%d - %H:%M:%S ET'))) """Return the first HH line for the current hand."""
game_string = "PokerStars Game #%s: " % self.handid gs = "PokerStars Game #%s: " % self.handid
if self.tourNo != None:
game_string = game_string + "Tournament #%s, %s %s - Level %s (%s) - " % (self.tourNo, if self.tourNo != None and self.mixed != None: # mixed tournament
gs = gs + "Tournament #%s, %s %s (%s) - Level %s (%s) - " % (self.tourNo, self.buyin, self.MS[self.mixed], self.getGameTypeAsString(), self.level, self.getStakesAsString())
elif self.tourNo != None: # all other tournaments
gs = gs + "Tournament #%s, %s %s - Level %s (%s) - " % (self.tourNo,
self.buyin, self.getGameTypeAsString(), self.level, self.getStakesAsString()) self.buyin, self.getGameTypeAsString(), self.level, self.getStakesAsString())
elif self.mixed != None: elif self.mixed != None: # all other mixed games
game_string = game_string + " %s (%s, %s) - " % (self.MS[self.mixed], gs = gs + " %s (%s, %s) - " % (self.MS[self.mixed],
self.getGameTypeAsString(), self.getStakesAsString()) self.getGameTypeAsString(), self.getStakesAsString())
else: else: # non-mixed cash games
game_string = game_string + " %s (%s) - " % (self.getGameTypeAsString(), self.getStakesAsString()) gs = gs + " %s (%s) - " % (self.getGameTypeAsString(), self.getStakesAsString())
game_string = game_string + datetime.datetime.strftime(self.starttime,'%Y/%m/%d %H:%M:%S ET')
return game_string return gs + datetime.datetime.strftime(self.starttime,'%Y/%m/%d %H:%M:%S ET')
def writeTableLine(self): def writeTableLine(self):
table_string = "Table \'%s\' %s-max" % (self.tablename, self.maxseats) table_string = "Table \'%s\' %s-max" % (self.tablename, self.maxseats)
if self.gametype['currency'] == 'play': if self.gametype['currency'] == 'play':
table_string = table_string + " (Play Money)" table_string = table_string + " (Play Money)"
if self.buttonpos != None: if self.buttonpos != None and self.buttonpos != 0:
table_string = table_string + " Seat #%s is the button" % self.buttonpos table_string = table_string + " Seat #%s is the button" % self.buttonpos
return table_string return table_string
def writeHand(self, fh=sys.__stdout__):
# PokerStars format.
print >>fh, self.writeGameLine()
print >>fh, self.writeTableLine()
class HoldemOmahaHand(Hand): 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':
@ -608,6 +630,9 @@ class HoldemOmahaHand(Hand):
hhc.readShownCards(self) hhc.readShownCards(self)
self.totalPot() # finalise it (total the pot) self.totalPot() # finalise it (total the pot)
hhc.getRake(self) hhc.getRake(self)
if self.maxseats == None:
self.maxseats = hhc.guessMaxSeats(self)
hhc.readOther(self)
elif builtFrom == "DB": elif builtFrom == "DB":
if handid is not None: if handid is not None:
self.select(handid) # Will need a handId self.select(handid) # Will need a handId
@ -635,8 +660,8 @@ class HoldemOmahaHand(Hand):
def render_stack(context,data): def render_stack(context,data):
pat = context.tag.patternGenerator('list_item') pat = context.tag.patternGenerator('list_item')
for player in data: for player in data:
x = "Seat %s: %s ($%s in chips) " %(player[0], player[1], x = "Seat %s: %s (%s%s in chips) " %(player[0], player[1],
player[2]) self.sym, player[2])
context.tag[ pat().fillSlots('playerStack', x)] context.tag[ pat().fillSlots('playerStack', x)]
return context.tag return context.tag
@ -723,11 +748,7 @@ class HoldemOmahaHand(Hand):
def writeHand(self, fh=sys.__stdout__): def writeHand(self, fh=sys.__stdout__):
# PokerStars format. # PokerStars format.
# print >>fh, ("%s Game #%s: %s ($%s/$%s) - %s" %("PokerStars", self.handid, self.getGameTypeAsString(), self.sb, self.bb, datetime.datetime.strftime(self.starttime,'%Y/%m/%d - %H:%M:%S ET'))) super(HoldemOmahaHand, self).writeHand(fh)
print >>fh, self.writeGameLine()
print >>fh, self.writeTableLine()
# print >>fh, ("Table '%s' %d-max Seat #%s is the button" %(self.tablename, self.maxseats, self.buttonpos))
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']) logging.debug(self.actions['PREFLOP'])
@ -793,16 +814,16 @@ class HoldemOmahaHand(Hand):
# Immediately before the summary. # Immediately before the summary.
# The current importer uses those lines for importing winning rather than the summary # The current importer uses those lines for importing winning rather than the summary
for name in self.pot.returned: for name in self.pot.returned:
print >>fh, ("Uncalled bet ($%s) returned to %s" %(self.pot.returned[name],name)) print >>fh, ("Uncalled bet (%s%s) returned to %s" %(self.sym, self.pot.returned[name],name))
for entry in self.collected: for entry in self.collected:
print >>fh, ("%s collected $%s from x pot" %(entry[0], entry[1])) print >>fh, ("%s collected %s%s from x pot" %(entry[0], self.sym, entry[1]))
print >>fh, ("*** SUMMARY ***") print >>fh, ("*** SUMMARY ***")
print >>fh, "%s | Rake $%.2f" % (self.pot, self.rake) print >>fh, "%s | Rake %s%.2f" % (self.pot, self.sym, self.rake)
board = [] board = []
for s in self.board.values(): for street in ["FLOP", "TURN", "RIVER"]:
board += s board += self.board[street]
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)))
@ -810,9 +831,9 @@ class HoldemOmahaHand(Hand):
seatnum = player[0] seatnum = player[0]
name = player[1] name = player[1]
if name in self.collectees 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['PREFLOP'][name][1]), self.collectees[name])) print >>fh, ("Seat %d: %s showed [%s] and won (%s%s)" % (seatnum, name, " ".join(self.holecards['PREFLOP'][name][1]), self.sym, self.collectees[name]))
elif name in self.collectees: elif name in self.collectees:
print >>fh, ("Seat %d: %s collected ($%s)" % (seatnum, name, self.collectees[name])) print >>fh, ("Seat %d: %s collected (%s%s)" % (seatnum, name, self.sym, 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]['PREFLOP']))) #~ print >>fh, _("Seat %d: %s showed [%s]" % (seatnum, name, " ".join(self.holecards[name]['PREFLOP'])))
elif name in self.folded: elif name in self.folded:
@ -852,13 +873,14 @@ class DrawHand(Hand):
# Read actions in street order # Read actions in street order
for street in self.streetList: for street in self.streetList:
if self.streets[street]: if self.streets[street]:
# hhc.readCommunityCards(self, street)
# hhc.readDrawCards(self, street)
hhc.readAction(self, street) hhc.readAction(self, street)
hhc.readCollectPot(self) hhc.readCollectPot(self)
hhc.readShownCards(self) hhc.readShownCards(self)
self.totalPot() # finalise it (total the pot) self.totalPot() # finalise it (total the pot)
hhc.getRake(self) hhc.getRake(self)
if self.maxseats == None:
self.maxseats = hhc.guessMaxSeats(self)
hhc.readOther(self)
elif builtFrom == "DB": elif builtFrom == "DB":
self.select("dummy") # Will need a handId self.select("dummy") # Will need a handId
@ -898,21 +920,6 @@ class DrawHand(Hand):
self.addHoleCards('DRAWTHREE', player, open=[], closed=cards, shown=shown, mucked=mucked, dealt=dealt) self.addHoleCards('DRAWTHREE', player, open=[], closed=cards, shown=shown, mucked=mucked, dealt=dealt)
# def addDrawHoleCards(self, newcards, oldcards, player, street, shown=False):
# """\
#Assigns observed holecards to a player.
#cards list of card bigrams e.g. ['2h','Jc']
#player (string) name of player
#"""
# try:
# self.checkPlayerExists(player)
## if shown and len(cardset) > 0:
## self.shown.add(player)
# self.holecards[street][player] = (newcards,oldcards)
# except FpdbParseError, e:
# print "[ERROR] Tried to add holecards for unknown player: %s" % (player,)
def discardDrawHoleCards(self, cards, player, street): def discardDrawHoleCards(self, cards, player, street):
logging.debug("discardDrawHoleCards '%s' '%s' '%s'" % (cards, player, street)) logging.debug("discardDrawHoleCards '%s' '%s' '%s'" % (cards, player, street))
self.discards[street][player] = set([cards]) self.discards[street][player] = set([cards])
@ -928,64 +935,19 @@ class DrawHand(Hand):
self.actions[street].append(act) self.actions[street].append(act)
# def addShownCards(self, cards, player, holeandboard=None, shown=False, mucked=False):
# """\
#For when a player shows cards for any reason (for showdown or out of choice).
#Card ranks will be uppercased
#"""
# logging.debug("addShownCards %s hole=%s all=%s" % (player, cards, holeandboard))
# if cards is not None:
# self.shown.add(player)
# self.addHoleCards(cards,player)
# elif holeandboard is not None:
# holeandboard = set([self.card(c) for c in holeandboard])
# board = set([c for s in self.board.values() for c in s])
# self.addHoleCards(holeandboard.difference(board),player,shown=True)
# def addHoleCards(self, cards, player, shown, mucked, dealt=False):
# """\
#Assigns observed holecards to a player.
#cards list of card bigrams e.g. ['2h','Jc']
#player (string) name of player
#shown whether they were revealed at showdown
#mucked whether they were mucked at showdown
#dealt whether they were seen in a 'dealt to' line
#"""
## I think this only gets called for shown cards.
# logging.debug("addHoleCards %s %s" % (cards, player))
# try:
# self.checkPlayerExists(player)
# except FpdbParseError, e:
# print "[ERROR] Tried to add holecards for unknown player: %s" % (player,)
# return
#
# if dealt:
# self.dealt.add(player)
# if shown:
# self.shown.add(player)
# if mucked:
# self.mucked.add(player)
# if player != self.hero: #skip hero, we know his cards
# print "player, cards =", player, cards
# self.holecards[self.holeStreets[-1]][player] = (cards, set([]))
def writeHand(self, fh=sys.__stdout__): def writeHand(self, fh=sys.__stdout__):
# PokerStars format. # PokerStars format.
# 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))) super(DrawHand, self).writeHand(fh)
print >>fh, self.writeGameLine()
# print >>fh, _("Table '%s' %d-max Seat #%s is the button" %(self.tablename, self.maxseats, self.buttonpos))
print >>fh, self.writeTableLine()
players_who_act_ondeal = set(([x[0] for x in self.actions['DEAL']]+[x[0] for x in self.actions['BLINDSANTES']])) players_who_act_ondeal = set(([x[0] for x in self.actions['DEAL']]+[x[0] for x in self.actions['BLINDSANTES']]))
for player in [x for x in self.players if x[1] in players_who_act_ondeal]: for player in [x for x in self.players if x[1] in players_who_act_ondeal]:
#Only print stacks of players who do something on deal #Only print stacks of players who do something on deal
print >>fh, _("Seat %s: %s ($%s in chips) " %(player[0], player[1], player[2])) print >>fh, _("Seat %s: %s (%s%s in chips) " %(player[0], player[1], self.sym, player[2]))
if 'BLINDSANTES' in self.actions: if 'BLINDSANTES' in self.actions:
for act in self.actions['BLINDSANTES']: for act in self.actions['BLINDSANTES']:
print >>fh, _("%s: %s %s $%s" %(act[0], act[1], act[2], act[3])) print >>fh, _("%s: %s %s %s%s" %(act[0], act[1], act[2], self.sym, act[3]))
if 'DEAL' in self.actions: if 'DEAL' in self.actions:
print >>fh, _("*** DEALING HANDS ***") print >>fh, _("*** DEALING HANDS ***")
@ -1038,12 +1000,12 @@ class DrawHand(Hand):
# Immediately before the summary. # Immediately before the summary.
# The current importer uses those lines for importing winning rather than the summary # The current importer uses those lines for importing winning rather than the summary
for name in self.pot.returned: for name in self.pot.returned:
print >>fh, _("Uncalled bet ($%s) returned to %s" %(self.pot.returned[name],name)) print >>fh, _("Uncalled bet (%s%s) returned to %s" %(self.sym, self.pot.returned[name],name))
for entry in self.collected: for entry in self.collected:
print >>fh, _("%s collected $%s from x pot" %(entry[0], entry[1])) print >>fh, _("%s collected %s%s from x pot" %(entry[0], self.sym, entry[1]))
print >>fh, _("*** SUMMARY ***") print >>fh, _("*** SUMMARY ***")
print >>fh, "%s | Rake $%.2f" % (self.pot, self.rake) print >>fh, "%s | Rake %s%.2f" % (self.pot, self.sym, self.rake)
print >>fh, "\n\n" print >>fh, "\n\n"
@ -1058,7 +1020,7 @@ class StudHand(Hand):
self.actionStreets = ['ANTES','THIRD','FOURTH','FIFTH','SIXTH','SEVENTH'] self.actionStreets = ['ANTES','THIRD','FOURTH','FIFTH','SIXTH','SEVENTH']
self.streetList = ['ANTES','THIRD','FOURTH','FIFTH','SIXTH','SEVENTH'] # a list of the observed street names in order self.streetList = ['ANTES','THIRD','FOURTH','FIFTH','SIXTH','SEVENTH'] # a list of the observed street names in order
self.holeStreets = ['ANTES','THIRD','FOURTH','FIFTH','SIXTH','SEVENTH'] self.holeStreets = ['THIRD','FOURTH','FIFTH','SIXTH','SEVENTH']
Hand.__init__(self, sitename, gametype, handText) Hand.__init__(self, sitename, gametype, handText)
self.sb = gametype['sb'] self.sb = gametype['sb']
self.bb = gametype['bb'] self.bb = gametype['bb']
@ -1073,18 +1035,20 @@ class StudHand(Hand):
hhc.readAntes(self) hhc.readAntes(self)
hhc.readBringIn(self) hhc.readBringIn(self)
hhc.readHeroCards(self) hhc.readHeroCards(self)
#hhc.readShowdownActions(self) # not done yet
# Read actions in street order # Read actions in street order
for street in self.streetList: for street in self.actionStreets:
if street == 'ANTES': continue # OMG--sometime someone folds in the ante round
if self.streets[street]: if self.streets[street]:
logging.debug(street) logging.debug(street)
logging.debug(self.streets[street]) logging.debug(self.streets[street])
# hhc.readStudPlayerCards(self, 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
self.totalPot() # finalise it (total the pot) self.totalPot() # finalise it (total the pot)
hhc.getRake(self) hhc.getRake(self)
if self.maxseats == None:
self.maxseats = hhc.guessMaxSeats(self)
hhc.readOther(self)
elif builtFrom == "DB": elif builtFrom == "DB":
self.select("dummy") # Will need a handId self.select("dummy") # Will need a handId
@ -1093,11 +1057,10 @@ class StudHand(Hand):
if shown: self.shown.add(player) if shown: self.shown.add(player)
if mucked: self.mucked.add(player) if mucked: self.mucked.add(player)
else: else:
# self.addHoleCards('PREFLOP', player, open=[], closed=cards, shown=shown, mucked=mucked, dealt=dealt)
self.addHoleCards('THIRD', player, open=[cards[2]], closed=cards[0:2], shown=shown, mucked=mucked) self.addHoleCards('THIRD', player, open=[cards[2]], closed=cards[0:2], shown=shown, mucked=mucked)
self.addHoleCards('FOURTH', player, open=[cards[3]], closed=[], shown=shown, mucked=mucked) self.addHoleCards('FOURTH', player, open=[cards[3]], closed=[cards[2]], shown=shown, mucked=mucked)
self.addHoleCards('FIFTH', player, open=[cards[4]], closed=[], shown=shown, mucked=mucked) self.addHoleCards('FIFTH', player, open=[cards[4]], closed=cards[2:4], shown=shown, mucked=mucked)
self.addHoleCards('SIXTH', player, open=[cards[5]], closed=[], shown=shown, mucked=mucked) self.addHoleCards('SIXTH', player, open=[cards[5]], closed=cards[2:5], shown=shown, mucked=mucked)
self.addHoleCards('SEVENTH', player, open=[], closed=[cards[6]], shown=shown, mucked=mucked) self.addHoleCards('SEVENTH', player, open=[], closed=[cards[6]], shown=shown, mucked=mucked)
@ -1113,46 +1076,9 @@ closed likewise, but known only to player
try: try:
self.checkPlayerExists(player) self.checkPlayerExists(player)
self.holecards[street][player] = (open, closed) self.holecards[street][player] = (open, closed)
# cards = set([self.card(c) for c in cards])
# self.holecards[player].update(cards)
except FpdbParseError, e: except FpdbParseError, e:
print "[ERROR] Tried to add holecards for unknown player: %s" % (player,) print "[ERROR] Tried to add holecards for unknown player: %s" % (player,)
# def addHoleCards(self, cards, player, shown, mucked, dealt=False):
# """\
#Assigns observed holecards to a player.
#cards list of card bigrams e.g. ['2h','Jc']
#player (string) name of player
#shown whether they were revealed at showdown
#mucked whether they were mucked at showdown
#dealt whether they were seen in a 'dealt to' line
#"""
##
## For stud games we just need to do the routine setting of shown/mucked/etc
## and then update the cards 'THIRD' and 'SEVENTH'
# logging.debug("addHoleCards %s %s" % (cards, player))
# try:
# self.checkPlayerExists(player)
# except FpdbParseError, e:
# print "[ERROR] Tried to add holecards for unknown player: %s" % (player,)
# return
#
# if dealt:
# self.dealt.add(player)
# if shown:
# self.shown.add(player)
# if mucked:
# self.mucked.add(player)
# if player == self.hero:
# if len(cards) > 2:
# self.holecards['THIRD'][player] = ([cards[0:3]], [])
# if len(cards) > 6:
# self.holecards['SEVENTH'][player] = ([cards[6]], [])
# else:
# if len(cards) > 2:
# self.holecards['THIRD'][player] = ([cards[0]], cards[1:3])
# if len(cards) > 6:
# self.holecards['SEVENTH'][player] = ([], [cards[6]])
# TODO: def addComplete(self, player, amount): # TODO: def addComplete(self, player, amount):
def addComplete(self, street, player, amountTo): def addComplete(self, street, player, amountTo):
# assert street=='THIRD' # assert street=='THIRD'
@ -1161,6 +1087,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)) logging.debug("%s %s completes %s" % (street, player, amountTo))
amountTo = re.sub(u',', u'', amountTo) #some sites have commas
self.checkPlayerExists(player) self.checkPlayerExists(player)
Bp = self.lastBet['THIRD'] Bp = self.lastBet['THIRD']
Bc = reduce(operator.add, self.bets[street][player], 0) Bc = reduce(operator.add, self.bets[street][player], 0)
@ -1188,26 +1115,18 @@ Add a complete on [street] by [player] to [amountTo]
def writeHand(self, fh=sys.__stdout__): def writeHand(self, fh=sys.__stdout__):
# PokerStars format. # PokerStars format.
# 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)))
# TODO: super(StudHand, self).writeHand(fh)
# Hole cards are not currently correctly written. Currently the down cards for non-heros
# are shown in the "dealt to" lines. They should be hidden in those lines. I tried to fix
# but mind got boggled, will try again.
# print >>fh, _("%s Game #%s: %s ($%s/$%s) - %s" %("PokerStars", self.handid, self.getGameTypeAsString(), self.sb, self.bb, datetime.datetime.strftime(self.starttime,'%Y/%m/%d - %H:%M:%S ET')))
print >>fh, self.writeGameLine()
print >>fh, self.writeTableLine()
# 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']]) 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]: 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 #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%s in chips)" %(player[0], player[1], self.sym, player[2]))
if 'ANTES' in self.actions: if 'ANTES' in self.actions:
for act in self.actions['ANTES']: for act in self.actions['ANTES']:
print >>fh, _("%s: posts the ante $%s" %(act[0], act[3])) print >>fh, _("%s: posts the ante %s%s" %(act[0], self.sym, act[3]))
if 'THIRD' in self.actions: if 'THIRD' in self.actions:
dealt = 0 dealt = 0
@ -1218,7 +1137,8 @@ Add a complete on [street] by [player] to [amountTo]
dealt+=1 dealt+=1
if dealt==1: if dealt==1:
print >>fh, _("*** 3RD STREET ***") print >>fh, _("*** 3RD STREET ***")
print >>fh, _("Dealt to %s:%s%s") % (player, " [" + " ".join(closed) + "] " if closed else " ", "[" + " ".join(open) + "]" if open else "") # print >>fh, _("Dealt to %s:%s%s") % (player, " [" + " ".join(closed) + "] " if closed else " ", "[" + " ".join(open) + "]" if open else "")
print >>fh, self.writeHoleCards('THIRD', player)
for act in self.actions['THIRD']: for act in self.actions['THIRD']:
#FIXME: Need some logic here for bringin vs completes #FIXME: Need some logic here for bringin vs completes
print >>fh, self.actionString(act) print >>fh, self.actionString(act)
@ -1228,15 +1148,10 @@ Add a complete on [street] by [player] to [amountTo]
#~ print >>fh, _("*** 4TH STREET ***") #~ print >>fh, _("*** 4TH STREET ***")
for player in [x[1] for x in self.players if x[1] in players_who_post_antes]: for player in [x[1] for x in self.players if x[1] in players_who_post_antes]:
if player in self.holecards['FOURTH']: if player in self.holecards['FOURTH']:
old = []
(o,c) = self.holecards['THIRD'][player]
if o:old.extend(o)
if c:old.extend(c)
new = self.holecards['FOURTH'][player][0]
dealt+=1 dealt+=1
if dealt==1: if dealt==1:
print >>fh, _("*** 4TH STREET ***") print >>fh, _("*** 4TH STREET ***")
print >>fh, _("Dealt to %s:%s%s") % (player, " [" + " ".join(old) + "] " if old else " ", "[" + " ".join(new) + "]" if new else "") print >>fh, self.writeHoleCards('FOURTH', player)
for act in self.actions['FOURTH']: for act in self.actions['FOURTH']:
print >>fh, self.actionString(act) print >>fh, self.actionString(act)
@ -1245,16 +1160,10 @@ Add a complete on [street] by [player] to [amountTo]
#~ print >>fh, _("*** 5TH STREET ***") #~ print >>fh, _("*** 5TH STREET ***")
for player in [x[1] for x in self.players if x[1] in players_who_post_antes]: for player in [x[1] for x in self.players if x[1] in players_who_post_antes]:
if self.holecards['FIFTH'].has_key(player): if self.holecards['FIFTH'].has_key(player):
old = []
for street in ('THIRD','FOURTH'):
(o,c) = self.holecards[street][player]
if o:old.extend(o)
if c:old.extend(c)
new = self.holecards['FIFTH'][player][0]
dealt+=1 dealt+=1
if dealt==1: if dealt==1:
print >>fh, _("*** 5TH STREET ***") print >>fh, _("*** 5TH STREET ***")
print >>fh, _("Dealt to %s:%s%s") % (player, " [" + " ".join(old) + "] " if old else " ", "[" + " ".join(new) + "]" if new else "") print >>fh, self.writeHoleCards('FIFTH', player)
for act in self.actions['FIFTH']: for act in self.actions['FIFTH']:
print >>fh, self.actionString(act) print >>fh, self.actionString(act)
@ -1263,16 +1172,10 @@ Add a complete on [street] by [player] to [amountTo]
#~ print >>fh, _("*** 6TH STREET ***") #~ print >>fh, _("*** 6TH STREET ***")
for player in [x[1] for x in self.players if x[1] in players_who_post_antes]: for player in [x[1] for x in self.players if x[1] in players_who_post_antes]:
if self.holecards['SIXTH'].has_key(player): if self.holecards['SIXTH'].has_key(player):
old = []
for street in ('THIRD','FOURTH','FIFTH'):
(o,c) = self.holecards[street][player]
if o:old.extend(o)
if c:old.extend(c)
new = self.holecards['SIXTH'][player][0]
dealt += 1 dealt += 1
if dealt == 1: if dealt == 1:
print >>fh, _("*** 6TH STREET ***") print >>fh, _("*** 6TH STREET ***")
print >>fh, _("Dealt to %s:%s%s") % (player, " [" + " ".join(old) + "] " if old else " ", "[" + " ".join(new) + "]" if new else "") print >>fh, self.writeHoleCards('SIXTH', player)
for act in self.actions['SIXTH']: for act in self.actions['SIXTH']:
print >>fh, self.actionString(act) print >>fh, self.actionString(act)
@ -1281,17 +1184,11 @@ Add a complete on [street] by [player] to [amountTo]
# Then we have no 'dealt to' lines, no action lines, but still 7th street should appear. # Then we have no 'dealt to' lines, no action lines, but still 7th street should appear.
# The only way I can see to know whether to print this line is by knowing the state of the hand # The only way I can see to know whether to print this line is by knowing the state of the hand
# i.e. are all but one players folded; is there an allin showdown; and all that. # i.e. are all but one players folded; is there an allin showdown; and all that.
print >>fh, _("*** 7TH STREET ***") print >>fh, _("*** RIVER ***")
for player in [x[1] for x in self.players if x[1] in players_who_post_antes]: for player in [x[1] for x in self.players if x[1] in players_who_post_antes]:
if self.holecards['SEVENTH'].has_key(player): if self.holecards['SEVENTH'].has_key(player):
old = [] if self.writeHoleCards('SEVENTH', player):
for street in ('THIRD','FOURTH','FIFTH','SIXTH'): print >>fh, self.writeHoleCards('SEVENTH', player)
(o,c) = self.holecards[street][player]
if o:old.extend(o)
if c:old.extend(c)
new = self.holecards['SEVENTH'][player][0]
if new:
print >>fh, _("Dealt to %s:%s%s") % (player, " [" + " ".join(old) + "] " if old else " ", "[" + " ".join(new) + "]" if new else "")
for act in self.actions['SEVENTH']: for act in self.actions['SEVENTH']:
print >>fh, self.actionString(act) print >>fh, self.actionString(act)
@ -1310,13 +1207,13 @@ Add a complete on [street] by [player] to [amountTo]
# Immediately before the summary. # Immediately before the summary.
# The current importer uses those lines for importing winning rather than the summary # The current importer uses those lines for importing winning rather than the summary
for name in self.pot.returned: for name in self.pot.returned:
print >>fh, _("Uncalled bet ($%s) returned to %s" %(self.pot.returned[name],name)) print >>fh, _("Uncalled bet (%s%s) returned to %s" %(self.sym, self.pot.returned[name],name))
for entry in self.collected: for entry in self.collected:
print >>fh, _("%s collected $%s from x pot" %(entry[0], entry[1])) print >>fh, _("%s collected %s%s from x pot" %(entry[0], self.sym, entry[1]))
print >>fh, _("*** SUMMARY ***") print >>fh, _("*** SUMMARY ***")
print >>fh, "%s | Rake $%.2f" % (self.pot, self.rake) print >>fh, "%s | Rake %s%.2f" % (self.pot, self.sym, self.rake)
#print >>fh, _("Total pot $%s | Rake $%.2f" % (self.totalpot, self.rake)) # TODO: side pots # TODO: side pots
board = [] board = []
for s in self.board.values(): for s in self.board.values():
@ -1328,9 +1225,9 @@ Add a complete on [street] by [player] to [amountTo]
seatnum = player[0] seatnum = player[0]
name = player[1] name = player[1]
if name in self.collectees 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, self.join_holecards(name), self.collectees[name])) print >>fh, _("Seat %d: %s showed [%s] and won (%s%s)" % (seatnum, name, self.join_holecards(name), self.sym, self.collectees[name]))
elif name in self.collectees: elif name in self.collectees:
print >>fh, _("Seat %d: %s collected ($%s)" % (seatnum, name, self.collectees[name])) print >>fh, _("Seat %d: %s collected (%s%s)" % (seatnum, name, self.sym, self.collectees[name]))
elif name in self.shown: elif name in self.shown:
print >>fh, _("Seat %d: %s showed [%s]" % (seatnum, name, self.join_holecards(name))) print >>fh, _("Seat %d: %s showed [%s]" % (seatnum, name, self.join_holecards(name)))
elif name in self.mucked: elif name in self.mucked:
@ -1343,11 +1240,30 @@ Add a complete on [street] by [player] to [amountTo]
print >>fh, "\n\n" print >>fh, "\n\n"
def writeHoleCards(self, street, player):
hc = "Dealt to %s [" % player
if street == 'THIRD':
if player == self.hero:
return hc + " ".join(self.holecards[street][player][1]) + " " + " ".join(self.holecards[street][player][0]) + ']'
else:
return hc + " ".join(self.holecards[street][player][0]) + ']'
if street == 'SEVENTH' and player != self.hero: return # only write 7th st line for hero, LDO
return hc + " ".join(self.holecards[street][player][1]) + "] [" + " ".join(self.holecards[street][player][0]) + "]"
def join_holecards(self, player): def join_holecards(self, player):
holecards = [] holecards = []
for street in self.holeStreets: for street in self.holeStreets:
if self.holecards[street].has_key(player): if self.holecards[street].has_key(player):
holecards = holecards + self.holecards[street][player][0] if street == 'THIRD':
holecards = holecards + self.holecards[street][player][1] + self.holecards[street][player][0]
elif street == 'SEVENTH':
if player == self.hero:
holecards = holecards + self.holecards[street][player][0]
else:
holecards = holecards + self.holecards[street][player][1]
else:
holecards = holecards + self.holecards[street][player][0]
return " ".join(holecards) return " ".join(holecards)
class Pot(object): class Pot(object):
@ -1358,6 +1274,10 @@ class Pot(object):
self.committed = {} self.committed = {}
self.total = None self.total = None
self.returned = {} self.returned = {}
self.sym = u'$' # this is the default currency symbol
def setSym(self, sym):
self.sym = sym
def addPlayer(self,player): def addPlayer(self,player):
self.committed[player] = Decimal(0) self.committed[player] = Decimal(0)
@ -1403,31 +1323,19 @@ class Pot(object):
# Total pot $124.30 Main pot $98.90. Side pot $23.40. | Rake $2 # Total pot $124.30 Main pot $98.90. Side pot $23.40. | Rake $2
def __str__(self): def __str__(self):
if self.sym is None:
self.sym = "C"
if self.total is None: if self.total is None:
print "call Pot.end() before printing pot total" print "call Pot.end() before printing pot total"
# NB if I'm sure end() is idempotent, call it here. # NB if I'm sure end() is idempotent, call it here.
raise FpdbParseError raise FpdbParseError
ret = "Total pot %s%.2f" % (self.sym, self.total)
if len(self.pots) < 2:
return ret;
ret += " Main pot %s%.2f" % (self.sym, self.pots[0])
return ret + ''.join([ (" Side pot %s%.2f." % (self.sym, self.pots[x]) ) for x in xrange(1, len(self.pots)) ])
if len(self.pots) == 1: # (only use Total pot)
return "Total pot $%.2f" % (self.total,)
elif len(self.pots) == 2:
return "Total pot $%.2f Main pot $%.2f. Side pot $%2.f." % (self.total, self.pots[0], self.pots[1])
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])
elif len(self.pots) == 0:
# no small blind and walk in bb (hopefully)
return "Total pot $%.2f" % (self.total,)
else:
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.
# The thing to do here is raise a Hand error like fpdb import does and file it into errors.txt
def assemble(cnxn, handid): def assemble(cnxn, handid):
c = cnxn.cursor() c = cnxn.cursor()

View File

@ -28,91 +28,54 @@ 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
import time import time
import datetime import datetime
import gettext
#from pokerengine.pokercards import *
# provides letter2name{}, letter2names{}, visible_card(), not_visible_card(), is_visible(), card_value(), class PokerCards
# but it's probably not installed so here are the ones we may want:
letter2name = {
'A': 'Ace',
'K': 'King',
'Q': 'Queen',
'J': 'Jack',
'T': 'Ten',
'9': 'Nine',
'8': 'Eight',
'7': 'Seven',
'6': 'Six',
'5': 'Five',
'4': 'Four',
'3': 'Trey',
'2': 'Deuce'
}
letter2names = {
'A': 'Aces',
'K': 'Kings',
'Q': 'Queens',
'J': 'Jacks',
'T': 'Tens',
'9': 'Nines',
'8': 'Eights',
'7': 'Sevens',
'6': 'Sixes',
'5': 'Fives',
'4': 'Fours',
'3': 'Treys',
'2': 'Deuces'
}
import gettext import gettext
gettext.install('myapplication') gettext.install('fpdb')
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): def __init__(self, in_path = '-', out_path = '-', sitename = None, follow=False, index=0):
logging.info("HandHistory init called") logging.info("HandHistory init")
# default filetype and codepage. Subclasses should set these properly. # default filetype and codepage. Subclasses should set these properly.
self.filetype = "text" self.filetype = "text"
self.codepage = "utf8" self.codepage = "utf8"
self.index = 0
self.in_path = in_path self.in_path = in_path
self.out_path = out_path self.out_path = out_path
if self.out_path == '-':
# write to stdout self.processedHands = []
if in_path == '-':
self.in_fh = sys.stdin
if out_path == '-':
self.out_fh = sys.stdout self.out_fh = sys.stdout
else: else:
# TODO: out_path should be sanity checked before opening. Perhaps in fpdb_import? # TODO: out_path should be sanity checked.
# I'm not sure what we're looking for, although we don't want out_path==in_path!='-' self.out_fh = open(self.out_path, 'w')
self.out_fh = open(self.out_path, 'w') # doomswitch is now on :|
self.sitename = sitename self.sitename = sitename
self.follow = follow self.follow = follow
self.compiledPlayers = set() self.compiledPlayers = set()
self.maxseats = 10 self.maxseats = 10
def __str__(self): def __str__(self):
#TODO : I got rid of most of the hhdir stuff. return """
tmp = "HandHistoryConverter: '%s'\n" % (self.sitename) HandHistoryConverter: '%(sitename)s'
#tmp = tmp + "\thhbase: '%s'\n" % (self.hhbase) filetype: '%(filetype)s'
#tmp = tmp + "\thhdir: '%s'\n" % (self.hhdir) in_path: '%(in_path)s'
tmp = tmp + "\tfiletype: '%s'\n" % (self.filetype) out_path: '%(out_path)s'
tmp = tmp + "\tinfile: '%s'\n" % (self.in_path) """ % { 'sitename':self.sitename, 'filetype':self.filetype, 'in_path':self.in_path, 'out_path':self.out_path }
tmp = tmp + "\toutfile: '%s'\n" % (self.out_path)
#tmp = tmp + "\tgametype: '%s'\n" % (self.gametype[0])
#tmp = tmp + "\tgamebase: '%s'\n" % (self.gametype[1])
#tmp = tmp + "\tlimit: '%s'\n" % (self.gametype[2])
#tmp = tmp + "\tsb/bb: '%s/%s'\n" % (self.gametype[3], self.gametype[4])
return tmp
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()
@ -129,7 +92,7 @@ Otherwise, finish at eof...
handsList = self.allHandsAsList() handsList = self.allHandsAsList()
logging.info("Parsing %d hands" % len(handsList)) logging.info("Parsing %d hands" % len(handsList))
for handText in handsList: for handText in handsList:
self.processHand(handText) self.processedHands.append(self.processHand(handText))
numHands= len(handsList) numHands= len(handsList)
endtime = time.time() endtime = time.time()
print "read %d hands in %.3f seconds" % (numHands, endtime - starttime) print "read %d hands in %.3f seconds" % (numHands, endtime - starttime)
@ -213,6 +176,7 @@ which it expects to find at self.re_TailSplitHands -- see for e.g. Everleaf.py.
def processHand(self, handText): def processHand(self, handText):
gametype = self.determineGameType(handText) gametype = self.determineGameType(handText)
logging.debug("gametype %s" % gametype) logging.debug("gametype %s" % gametype)
hand = None
if gametype is None: if gametype is None:
l = None l = None
gametype = "unmatched" gametype = "unmatched"
@ -224,9 +188,7 @@ which it expects to find at self.re_TailSplitHands -- see for e.g. Everleaf.py.
base = gametype['base'] base = gametype['base']
limit = gametype['limitType'] limit = gametype['limitType']
l = [type] + [base] + [limit] l = [type] + [base] + [limit]
hand = None
if l in self.readSupportedGames(): if l in self.readSupportedGames():
hand = None
if gametype['base'] == 'hold': if gametype['base'] == 'hold':
logging.debug("hand = Hand.HoldemOmahaHand(self, self.sitename, gametype, handtext)") logging.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)
@ -240,6 +202,7 @@ which it expects to find at self.re_TailSplitHands -- see for e.g. Everleaf.py.
if hand: if hand:
# print hand # print hand
hand.writeHand(self.out_fh) hand.writeHand(self.out_fh)
return hand
else: else:
logging.info("Unsupported game type: %s" % gametype) logging.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?
@ -307,6 +270,11 @@ or None if we fail to get the info """
def readCollectPot(self, hand): abstract def readCollectPot(self, hand): abstract
def readShownCards(self, hand): abstract def readShownCards(self, hand): abstract
# Some sites do odd stuff that doesn't fall in to the normal HH parsing.
# e.g., FTP doesn't put mixed game info in the HH, but puts in in the
# file name. Use readOther() to clean up those messes.
def readOther(self, hand): pass
# Some sites don't report the rake. This will be called at the end of the hand after the pot total has been calculated # Some sites don't report the rake. This will be called at the end of the hand after the pot total has been calculated
# an inheriting class can calculate it for the specific site if need be. # an inheriting class can calculate it for the specific site if need be.
def getRake(self, hand): def getRake(self, hand):
@ -350,6 +318,7 @@ or None if we fail to get the info """
self.filetype = filetype self.filetype = filetype
self.codepage = codepage self.codepage = codepage
#This function doesn't appear to be used
def splitFileIntoHands(self): def splitFileIntoHands(self):
hands = [] hands = []
self.obs = self.obs.strip() self.obs = self.obs.strip()
@ -371,7 +340,9 @@ or None if we fail to get the info """
else: else:
logging.debug("Opening %s with %s" % (self.in_path, self.codepage)) 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)
self.obs = in_fh.read() self.obs = in_fh.read()
self.index = in_fh.tell()
in_fh.close() in_fh.close()
elif(self.filetype == "xml"): elif(self.filetype == "xml"):
try: try:
@ -380,10 +351,39 @@ or None if we fail to get the info """
except: except:
traceback.print_exc(file=sys.stderr) traceback.print_exc(file=sys.stderr)
def guessMaxSeats(self, hand):
"""Return a guess at max_seats when not specified in HH."""
mo = self.maxOccSeat(hand)
if mo == 10: return 10 #that was easy
if hand.gametype['base'] == 'stud':
if mo <= 8: return 8
else: return mo
if hand.gametype['base'] == 'draw':
if mo <= 6: return 6
else: return mo
if mo == 2: return 2
if mo <= 6: return 6
return 10
def maxOccSeat(self, hand):
max = 0
for player in hand.players:
if player[0] > max: max = player[0]
return max
def getStatus(self): def getStatus(self):
#TODO: Return a status of true if file processed ok #TODO: Return a status of true if file processed ok
return True return True
def getProcessedHands(self):
return self.processedHands
def getProcessedFile(self): def getProcessedFile(self):
return self.out_path return self.out_path
def getLastCharacterRead(self):
return self.index

View File

@ -235,12 +235,8 @@ class Hud:
# does the user have a fav_seat? # does the user have a fav_seat?
if int(config.supported_sites[self.table.site].layout[self.max].fav_seat) > 0: if int(config.supported_sites[self.table.site].layout[self.max].fav_seat) > 0:
try: try:
sys.stderr.write("site = %s, max = %d, fav seat = %d\n" % (self.table.site, self.max, config.supported_sites[self.table.site].layout[self.max].fav_seat))
fav_seat = config.supported_sites[self.table.site].layout[self.max].fav_seat fav_seat = config.supported_sites[self.table.site].layout[self.max].fav_seat
sys.stderr.write("found fav seat = %d\n" % fav_seat)
# actual_seat = self.db_connection.get_actual_seat(hand, config.supported_sites[self.table.site].screen_name)
actual_seat = self.get_actual_seat(config.supported_sites[self.table.site].screen_name) actual_seat = self.get_actual_seat(config.supported_sites[self.table.site].screen_name)
sys.stderr.write("found actual seat = %d\n" % actual_seat)
for i in xrange(0, self.max + 1): for i in xrange(0, self.max + 1):
j = actual_seat + i j = actual_seat + i
if j > self.max: if j > self.max:
@ -273,7 +269,6 @@ class Hud:
self.cards = cards self.cards = cards
sys.stderr.write("------------------------------------------------------------\nCreating hud from hand %s\n" % hand) sys.stderr.write("------------------------------------------------------------\nCreating hud from hand %s\n" % hand)
adj = self.adj_seats(hand, config) adj = self.adj_seats(hand, config)
sys.stderr.write("adj = %s\n" % adj)
loc = self.config.get_locations(self.table.site, self.max) loc = self.config.get_locations(self.table.site, self.max)
# create the stat windows # create the stat windows
@ -282,7 +277,6 @@ class Hud:
if i in self.stat_windows: if i in self.stat_windows:
self.stat_windows[i].relocate(x, y) self.stat_windows[i].relocate(x, y)
else: else:
sys.stderr.write("actual seat = %d, x = %d, y= %d\n" % (i, x, y))
self.stat_windows[i] = Stat_Window(game = config.supported_games[self.poker_game], self.stat_windows[i] = Stat_Window(game = config.supported_games[self.poker_game],
parent = self, parent = self,
table = self.table, table = self.table,

View File

@ -294,6 +294,7 @@ class Stud_cards:
def update_gui(self, new_hand_id): def update_gui(self, new_hand_id):
self.clear() self.clear()
for c, cards in self.parent.hud.cards.iteritems(): for c, cards in self.parent.hud.cards.iteritems():
if c == 'common': continue
self.grid_contents[(1, c - 1)].set_text(self.get_screen_name(c)) self.grid_contents[(1, c - 1)].set_text(self.get_screen_name(c))
for i in ((0, cards[0]), (1, cards[1]), (2, cards[2]), (3, cards[3]), for i in ((0, cards[0]), (1, cards[1]), (2, cards[2]), (3, cards[3]),
(4, cards[4]), (5, cards[5]), (6, cards[6])): (4, cards[4]), (5, cards[5]), (6, cards[6])):
@ -462,7 +463,6 @@ class Flop_Mucked(Aux_Seats):
if n_cards > 0 and i != 'common': if n_cards > 0 and i != 'common':
n_sd = n_sd + 1 n_sd = n_sd + 1
if n_sd < 2: if n_sd < 2:
print "skipping, n_sd =", n_sd
return return
super(Flop_Mucked, self).update_gui(new_hand_id) super(Flop_Mucked, self).update_gui(new_hand_id)

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright 2008, Carl Gherardi # Copyright 2008, Carl Gherardi
@ -55,12 +55,12 @@ class PokerStars(HandHistoryConverter):
mixes = { 'HORSE': 'horse', '8-Game': '8game', 'HOSE': 'hose'} mixes = { 'HORSE': 'horse', '8-Game': '8game', 'HOSE': 'hose'}
def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True): def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True, index=0):
"""\ """\
in_path (default '-' = sys.stdin) in_path (default '-' = sys.stdin)
out_path (default '-' = sys.stdout) out_path (default '-' = sys.stdout)
follow : whether to tail -f the input""" follow : whether to tail -f the input"""
HandHistoryConverter.__init__(self, in_path, out_path, sitename="PokerStars", follow=follow) HandHistoryConverter.__init__(self, in_path, out_path, sitename="PokerStars", follow=follow, index=index)
logging.info("Initialising PokerStars converter class") logging.info("Initialising PokerStars converter class")
self.filetype = "text" self.filetype = "text"
self.codepage = "cp1252" self.codepage = "cp1252"
@ -298,7 +298,7 @@ follow : whether to tail -f the input"""
hand.addHoleCards(street, hand.hero, closed=newcards, shown=False, mucked=False, dealt=True) hand.addHoleCards(street, hand.hero, closed=newcards, shown=False, mucked=False, dealt=True)
for street, text in hand.streets.iteritems(): for street, text in hand.streets.iteritems():
if street in ('PREFLOP', 'DEAL'): continue # already done these if not text or street in ('PREFLOP', 'DEAL'): continue # already done these
m = self.re_HeroCards.finditer(hand.streets[street]) m = self.re_HeroCards.finditer(hand.streets[street])
for found in m: for found in m:
player = found.group('PNAME') player = found.group('PNAME')
@ -398,7 +398,7 @@ follow : whether to tail -f the input"""
def readShowdownActions(self, hand): def readShowdownActions(self, hand):
# TODO: pick up mucks also # TODO: pick up mucks also??
for shows in self.re_ShowdownAction.finditer(hand.handText): for shows in self.re_ShowdownAction.finditer(hand.handText):
cards = shows.group('CARDS').split(' ') cards = shows.group('CARDS').split(' ')
hand.addShownCards(cards, shows.group('PNAME')) hand.addShownCards(cards, shows.group('PNAME'))
@ -421,7 +421,7 @@ follow : whether to tail -f the input"""
if __name__ == "__main__": if __name__ == "__main__":
parser = OptionParser() parser = OptionParser()
parser.add_option("-i", "--input", dest="ipath", help="parse input hand history", default="regression-test-files/pokerstars/HH20090226 Natalie V - $0.10-$0.20 - HORSE.txt") parser.add_option("-i", "--input", dest="ipath", help="parse input hand history", default="regression-test-files/stars/horse/HH20090226 Natalie V - $0.10-$0.20 - HORSE.txt")
parser.add_option("-o", "--output", dest="opath", help="output translation to", default="-") parser.add_option("-o", "--output", dest="opath", help="output translation to", default="-")
parser.add_option("-f", "--follow", dest="follow", help="follow (tail -f) the input", action="store_true", default=False) parser.add_option("-f", "--follow", dest="follow", help="follow (tail -f) the input", action="store_true", default=False)
parser.add_option("-q", "--quiet", parser.add_option("-q", "--quiet",

File diff suppressed because it is too large Load Diff

View File

@ -22,6 +22,8 @@
######################################################################## ########################################################################
# How to write a new stat: # How to write a new stat:
# 0 Do not use a name like "xyz_2". Names ending in _ and a single digit are
# used to indicate the number of decimal places the user wants to see in the Hud.
# 1 You can see a listing of all the raw stats (e.g., from the HudCache table) # 1 You can see a listing of all the raw stats (e.g., from the HudCache table)
# by running Database.py as a stand along program. You need to combine # by running Database.py as a stand along program. You need to combine
# those raw stats to get stats to present to the HUD. If you need more # those raw stats to get stats to present to the HUD. If you need more
@ -50,16 +52,34 @@
# pyGTK modules # pyGTK modules
import pygtk import pygtk
import gtk import gtk
import re
# FreePokerTools modules # FreePokerTools modules
import Configuration import Configuration
import Database import Database
re_Places = re.compile("_[0-9]$")
re_Percent = re.compile("%$")
def do_tip(widget, tip): def do_tip(widget, tip):
widget.set_tooltip_text(tip) widget.set_tooltip_text(tip)
def do_stat(stat_dict, player = 24, stat = 'vpip'): def do_stat(stat_dict, player = 24, stat = 'vpip'):
return eval("%(stat)s(stat_dict, %(player)d)" % {'stat': stat, 'player': player}) match = re_Places.search(stat)
if match == None:
result = eval("%(stat)s(stat_dict, %(player)d)" % {'stat': stat, 'player': player})
else:
base = stat[0:-2]
places = int(stat[-1:])
result = eval("%(stat)s(stat_dict, %(player)d)" % {'stat': base, 'player': player})
match = re_Percent.search(result[1])
if match == None:
result = (result[0], "%.*f" % (places, result[0]), result[2], result[3], result[4], result[5])
else:
result = (result[0], "%.*f%%" % (places, 100*result[0]), result[2], result[3], result[4], result[5])
return result
# OK, for reference the tuple returned by the stat is: # OK, for reference the tuple returned by the stat is:
# 0 - The stat, raw, no formating, eg 0.33333333 # 0 - The stat, raw, no formating, eg 0.33333333
@ -108,26 +128,6 @@ def vpip(stat_dict, player):
'Voluntarily Put In Pot %' 'Voluntarily Put In Pot %'
) )
def vpip_0(stat_dict, player):
""" Voluntarily put $ in the pot (no decimals)."""
stat = 0.0
try:
stat = float(stat_dict[player]['vpip'])/float(stat_dict[player]['n'])
return (stat,
'%2.0f' % (100*stat) + '%',
'v=%2.0f' % (100*stat) + '%',
'vpip=%2.0f' % (100*stat) + '%',
'(%d/%d)' % (stat_dict[player]['vpip'], stat_dict[player]['n']),
'vpip'
)
except: return (stat,
'%2.0f' % (0) + '%',
'w=%2.0f' % (0) + '%',
'wtsd=%2.0f' % (0) + '%',
'(%d/%d)' % (0, 0),
'wtsd'
)
def pfr(stat_dict, player): def pfr(stat_dict, player):
""" Preflop (3rd street) raise.""" """ Preflop (3rd street) raise."""
stat = 0.0 stat = 0.0
@ -149,27 +149,6 @@ def pfr(stat_dict, player):
'Pre-Flop Raise %' 'Pre-Flop Raise %'
) )
def pfr_0(stat_dict, player):
""" Preflop (3rd street) raise (no decimals)."""
stat = 0.0
try:
stat = float(stat_dict[player]['pfr'])/float(stat_dict[player]['n'])
return (stat,
'%2.0f' % (100*stat) + '%',
'p=%2.0f' % (100*stat) + '%',
'pfr=%2.0f' % (100*stat) + '%',
'(%d/%d)' % (stat_dict[player]['pfr'], stat_dict[player]['n']),
'pfr'
)
except:
return (stat,
'%2.0f' % (0) + '%',
'p=%2.0f' % (0) + '%',
'pfr=%2.0f' % (0) + '%',
'(%d/%d)' % (0, 0),
'pfr'
)
def wtsd(stat_dict, player): def wtsd(stat_dict, player):
""" Went to SD when saw flop/4th.""" """ Went to SD when saw flop/4th."""
stat = 0.0 stat = 0.0
@ -191,27 +170,6 @@ def wtsd(stat_dict, player):
'% went to showdown' '% went to showdown'
) )
def wtsd_0(stat_dict, player):
""" Went to SD when saw flop/4th."""
stat = 0.0
try:
stat = float(stat_dict[player]['sd'])/float(stat_dict[player]['saw_f'])
return (stat,
'%2.0f' % (100*stat) + '%',
'w=%2.0f' % (100*stat) + '%',
'wtsd=%2.0f' % (100*stat) + '%',
'(%d/%d)' % (stat_dict[player]['sd'], stat_dict[player]['saw_f']),
'% went to showdown'
)
except:
return (stat,
'%2.0f' % (0) + '%',
'w=%2.0f' % (0) + '%',
'wtsd=%2.0f' % (0) + '%',
'(%d/%d)' % (0, 0),
'% went to showdown'
)
def wmsd(stat_dict, player): def wmsd(stat_dict, player):
""" Won $ at showdown.""" """ Won $ at showdown."""
stat = 0.0 stat = 0.0
@ -233,28 +191,7 @@ def wmsd(stat_dict, player):
'% won money at showdown' '% won money at showdown'
) )
def wmsd_0(stat_dict, player): def profit100(stat_dict, player):
""" Won $ at showdown."""
stat = 0.0
try:
stat = float(stat_dict[player]['wmsd'])/float(stat_dict[player]['sd'])
return (stat,
'%2.0f' % (100*stat) + '%',
'w=%2.0f' % (100*stat) + '%',
'wmsd=%2.0f' % (100*stat) + '%',
'(%5.1f/%d)' % (float(stat_dict[player]['wmsd']), stat_dict[player]['sd']),
'% won money at showdown'
)
except:
return (stat,
'%2.0f' % (0) + '%',
'w=%2.0f' % (0) + '%',
'wmsd=%2.0f' % (0) + '%',
'(%d/%d)' % (0, 0),
'% won money at showdown'
)
def profit100_0(stat_dict, player):
""" Profit won per 100 hands (no decimal places).""" """ Profit won per 100 hands (no decimal places)."""
stat = 0.0 stat = 0.0
try: try:
@ -356,27 +293,6 @@ def steal(stat_dict, player):
except: except:
return (stat, 'NA', 'st=NA', 'steal=NA', '(0/0)', '% steal attempted') return (stat, 'NA', 'st=NA', 'steal=NA', '(0/0)', '% steal attempted')
def steal_0(stat_dict, player):
""" Steal %."""
stat = 0.0
try:
stat = float(stat_dict[player]['steal'])/float(stat_dict[player]['steal_opp'])
return (stat,
'%2.0f' % (100*stat) + '%',
'st=%2.0f' % (100*stat) + '%',
'steal=%2.0f' % (100*stat) + '%',
'(%d/%d)' % (stat_dict[player]['steal'], stat_dict[player]['steal_opp']),
'% steal attempted'
)
except:
return (stat,
'%2.0f' % (0) + '%',
'st=%2.0f' % (0) + '%',
'steal=%2.0f' % (0) + '%',
'(%d/%d)' % (0, 0),
'% steal attempted'
)
def f_SB_steal(stat_dict, player): def f_SB_steal(stat_dict, player):
""" Folded SB to steal.""" """ Folded SB to steal."""
stat = 0.0 stat = 0.0
@ -417,27 +333,7 @@ def f_BB_steal(stat_dict, player):
'(0/0)', '(0/0)',
'% folded BB to steal') '% folded BB to steal')
def f_BB_steal_0(stat_dict, player): def three_B(stat_dict, player):
""" Folded BB to steal."""
stat = 0.0
try:
stat = float(stat_dict[player]['bbnotdef'])/float(stat_dict[player]['bbstolen'])
return (stat,
'%2.0f' % (100*stat) + '%',
'fBB=%2.0f' % (100*stat) + '%',
'fBB_s=%2.0f' % (100*stat) + '%',
'(%d/%d)' % (stat_dict[player]['bbnotdef'], stat_dict[player]['bbstolen']),
'% folded BB to steal'
)
except:
return (stat,
'NA',
'fBB=NA',
'fBB_s=NA',
'(0/0)',
'% folded BB to steal')
def three_B_0(stat_dict, player):
""" Three bet preflop/3rd.""" """ Three bet preflop/3rd."""
stat = 0.0 stat = 0.0
try: try:
@ -479,7 +375,7 @@ def WMsF(stat_dict, player):
'% won$/saw flop/4th' '% won$/saw flop/4th'
) )
def a_freq_1(stat_dict, player): def a_freq1(stat_dict, player):
""" Flop/4th aggression frequency.""" """ Flop/4th aggression frequency."""
stat = 0.0 stat = 0.0
try: try:
@ -500,7 +396,7 @@ def a_freq_1(stat_dict, player):
'Aggression Freq flop/4th' 'Aggression Freq flop/4th'
) )
def a_freq_2(stat_dict, player): def a_freq2(stat_dict, player):
""" Turn/5th aggression frequency.""" """ Turn/5th aggression frequency."""
stat = 0.0 stat = 0.0
try: try:
@ -521,7 +417,7 @@ def a_freq_2(stat_dict, player):
'Aggression Freq turn/5th' 'Aggression Freq turn/5th'
) )
def a_freq_3(stat_dict, player): def a_freq3(stat_dict, player):
""" River/6th aggression frequency.""" """ River/6th aggression frequency."""
stat = 0.0 stat = 0.0
try: try:
@ -542,7 +438,7 @@ def a_freq_3(stat_dict, player):
'Aggression Freq river/6th' 'Aggression Freq river/6th'
) )
def a_freq_4(stat_dict, player): def a_freq4(stat_dict, player):
""" 7th street aggression frequency.""" """ 7th street aggression frequency."""
stat = 0.0 stat = 0.0
try: try:
@ -591,34 +487,7 @@ def a_freq_123(stat_dict, player):
'Post-Flop Aggression Freq' 'Post-Flop Aggression Freq'
) )
def a_freq_123_0(stat_dict, player): def cb1(stat_dict, player):
""" Post-Flop aggression frequency (no decimals)."""
stat = 0.0
try:
stat = float( stat_dict[player]['aggr_1'] + stat_dict[player]['aggr_2'] + stat_dict[player]['aggr_3']) / float( stat_dict[player]['saw_1'] + stat_dict[player]['saw_2'] + stat_dict[player]['saw_3']);
return (stat,
'%2.0f' % (100*stat) + '%',
'afq=%2.0f' % (100*stat) + '%',
'postf_aggfq=%2.0f' % (100*stat) + '%',
'(%d/%d)' % ( stat_dict[player]['aggr_1']
+ stat_dict[player]['aggr_2']
+ stat_dict[player]['aggr_3']
, stat_dict[player]['saw_1']
+ stat_dict[player]['saw_2']
+ stat_dict[player]['saw_3']
),
'Post-Flop Aggression Freq'
)
except:
return (stat,
'%2.0f' % (0) + '%',
'a3=%2.0f' % (0) + '%',
'a_fq_3=%2.0f' % (0) + '%',
'(%d/%d)' % (0, 0),
'Post-Flop Aggression Freq'
)
def cb_1(stat_dict, player):
""" Flop continuation bet.""" """ Flop continuation bet."""
stat = 0.0 stat = 0.0
try: try:
@ -639,7 +508,7 @@ def cb_1(stat_dict, player):
'% continuation bet flop/4th' '% continuation bet flop/4th'
) )
def cb_2(stat_dict, player): def cb2(stat_dict, player):
""" Turn continuation bet.""" """ Turn continuation bet."""
stat = 0.0 stat = 0.0
try: try:
@ -660,7 +529,7 @@ def cb_2(stat_dict, player):
'% continuation bet turn/5th' '% continuation bet turn/5th'
) )
def cb_3(stat_dict, player): def cb3(stat_dict, player):
""" River continuation bet.""" """ River continuation bet."""
stat = 0.0 stat = 0.0
try: try:
@ -681,7 +550,7 @@ def cb_3(stat_dict, player):
'% continuation bet river/6th' '% continuation bet river/6th'
) )
def cb_4(stat_dict, player): def cb4(stat_dict, player):
""" 7th street continuation bet.""" """ 7th street continuation bet."""
stat = 0.0 stat = 0.0
try: try:
@ -702,7 +571,7 @@ def cb_4(stat_dict, player):
'% continuation bet 7th' '% continuation bet 7th'
) )
def ffreq_1(stat_dict, player): def ffreq1(stat_dict, player):
""" Flop/4th fold frequency.""" """ Flop/4th fold frequency."""
stat = 0.0 stat = 0.0
try: try:
@ -723,7 +592,7 @@ def ffreq_1(stat_dict, player):
'% fold frequency flop/4th' '% fold frequency flop/4th'
) )
def ffreq_2(stat_dict, player): def ffreq2(stat_dict, player):
""" Turn/5th fold frequency.""" """ Turn/5th fold frequency."""
stat = 0.0 stat = 0.0
try: try:
@ -744,7 +613,7 @@ def ffreq_2(stat_dict, player):
'% fold frequency turn/5th' '% fold frequency turn/5th'
) )
def ffreq_3(stat_dict, player): def ffreq3(stat_dict, player):
""" River/6th fold frequency.""" """ River/6th fold frequency."""
stat = 0.0 stat = 0.0
try: try:
@ -765,7 +634,7 @@ def ffreq_3(stat_dict, player):
'% fold frequency river/6th' '% fold frequency river/6th'
) )
def ffreq_4(stat_dict, player): def ffreq4(stat_dict, player):
""" 7th fold frequency.""" """ 7th fold frequency."""
stat = 0.0 stat = 0.0
try: try:
@ -804,24 +673,24 @@ if __name__== "__main__":
print "player = ", player, do_stat(stat_dict, player = player, stat = 'fold_f') print "player = ", player, do_stat(stat_dict, player = player, stat = 'fold_f')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'wmsd') print "player = ", player, do_stat(stat_dict, player = player, stat = 'wmsd')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'steal') print "player = ", player, do_stat(stat_dict, player = player, stat = 'steal')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'f_sb_steal') print "player = ", player, do_stat(stat_dict, player = player, stat = 'f_SB_steal')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'f_bb_steal') print "player = ", player, do_stat(stat_dict, player = player, stat = 'f_BB_steal')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'three_b_0') print "player = ", player, do_stat(stat_dict, player = player, stat = 'three_B_0')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'wmsf') print "player = ", player, do_stat(stat_dict, player = player, stat = 'WMsF')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_1') print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq1')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_2') print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq2')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_3') print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq3')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_4') print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq4')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_123') print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_123')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_123_0') print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_123_0')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb_1') print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb1')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb_2') print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb2')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb_3') print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb3')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb_4') print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb4')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq_1') print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq1')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq_2') print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq2')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq_3') print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq3')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq_4') print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq4')
print "\n" print "\n"
print "\n\nLegal stats:" print "\n\nLegal stats:"

View File

@ -33,12 +33,12 @@ class UltimateBet(HandHistoryConverter):
re_Board = re.compile(r"\[(?P<CARDS>.+)\]") re_Board = re.compile(r"\[(?P<CARDS>.+)\]")
# 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): def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True, index=0):
"""\ """\
in_path (default '-' = sys.stdin) in_path (default '-' = sys.stdin)
out_path (default '-' = sys.stdout) out_path (default '-' = sys.stdout)
follow : whether to tail -f the input""" follow : whether to tail -f the input"""
HandHistoryConverter.__init__(self, in_path, out_path, sitename="UltimateBet", follow=follow) HandHistoryConverter.__init__(self, in_path, out_path, sitename="UltimateBet", follow=follow, index=index)
logging.info("Initialising UltimateBetconverter class") logging.info("Initialising UltimateBetconverter class")
self.filetype = "text" self.filetype = "text"
self.codepage = "cp1252" self.codepage = "cp1252"

381
pyfpdb/Win2dayToFpdb.py Executable file
View File

@ -0,0 +1,381 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright 2008, Carl Gherardi
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
########################################################################
import sys
import datetime
from HandHistoryConverter import *
# Win2day HH Format
class Win2day(HandHistoryConverter):
# 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="(?P<HID>[0-9]+)" SESSION="session[0-9]+\.xml" TABLE="(?P<TABLE>[- a-zA-Z0-9]+)" GAME="(?P<GAME>[_A-Z]+)" GAMETYPE="[_a-zA-Z]+" GAMEKIND="[_a-zA-Z]+" TABLECURRENCY="(?P<CURRENCY>[A-Z]+)" LIMIT="(?P<LIMIT>NL|PL)" STAKES="(?P<SB>[.0-9]+)/(?P<BB>[.0-9]+)" DATE="(?P<DATETIME>[0-9]+)" WIN="[.0-9]+" LOSS="[.0-9]+">$'
re_GameInfo = re.compile('^<HISTORY ID="(?P<HID>[0-9]+)" SESSION="session[0-9]+\.xml" TABLE="(?P<TABLE>[- a-zA-Z0-9]+)" GAME="(?P<GAME>[_A-Z]+)" GAMETYPE="[_a-zA-Z]+" GAMEKIND="[_a-zA-Z]+" TABLECURRENCY="(?P<CURRENCY>[A-Z]+)" LIMIT="(?P<LIMIT>NL|PL)" STAKES="(?P<SB>[.0-9]+)/(?P<BB>[.0-9]+)" DATE="(?P<DATETIME>[0-9]+)" WIN="[.0-9]+" LOSS="[.0-9]+">', re.MULTILINE)
re_SplitHands = re.compile('</HISTORY>')
re_HandInfo = re.compile("^Table \'(?P<TABLE>[- a-zA-Z]+)\'(?P<TABLEATTRIBUTES>.+?$)?", re.MULTILINE)
re_Button = re.compile('<ACTION TYPE="HAND_DEAL" PLAYER="(?P<BUTTON>[^"]+)">\n<CARD LINK="[0-9b]+"></CARD>\n<CARD LINK="[0-9b]+"></CARD></ACTION>\n<ACTION TYPE="ACTION_', re.MULTILINE)
#<PLAYER NAME="prato" SEAT="1" AMOUNT="61.29"></PLAYER>
re_PlayerInfo = re.compile('^<PLAYER NAME="(?P<PNAME>.*)" SEAT="(?P<SEAT>[0-9]+)" AMOUNT="(?P<CASH>[.0-9]+)"></PLAYER>', 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)
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):
players = set([player[1] for player in hand.players])
if not players <= self.compiledPlayers: # x <= y means 'x is subset of y'
# we need to recompile the player regexs.
self.compiledPlayers = players
player_re = "(?P<PNAME>" + "|".join(map(re.escape, players)) + ")"
logging.debug("player_re: " + player_re)
#<ACTION TYPE="HAND_BLINDS" PLAYER="prato" KIND="HAND_SB" VALUE="0.25"></ACTION>
self.re_PostSB = re.compile(r'^<ACTION TYPE="HAND_BLINDS" PLAYER="%s" KIND="HAND_SB" VALUE="(?P<SB>[.0-9]+)"></ACTION>' % player_re, re.MULTILINE)
self.re_PostBB = re.compile(r'^<ACTION TYPE="HAND_BLINDS" PLAYER="%s" KIND="HAND_BB" VALUE="(?P<BB>[.0-9]+)"></ACTION>' % player_re, re.MULTILINE)
self.re_Antes = re.compile(r"^%s: posts the ante \$?(?P<ANTE>[.0-9]+)" % player_re, re.MULTILINE)
self.re_BringIn = re.compile(r"^%s: brings[- ]in( low|) for \$?(?P<BRINGIN>[.0-9]+)" % 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>'
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>'
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_ShowdownAction = re.compile(r'<RESULT PLAYER="%s" WIN="[.0-9]+" HAND="(?P<HAND>\(\$STR_G_FOLD\)|[\$\(\)_ A-Z]+)">\n(?P<CARDS><CARD LINK="[0-9]+"></CARD>\n<CARD LINK="[0-9]+"></CARD>)</RESULT>' % player_re, re.MULTILINE)
#<RESULT PLAYER="wig0r" WIN="4.10" HAND="$(STR_G_WIN_TWOPAIR) $(STR_G_CARDS_TENS) $(STR_G_ANDTEXT) $(STR_G_CARDS_EIGHTS)">
#
self.re_CollectPot = re.compile(r'<RESULT PLAYER="%s" WIN="(?P<POT>[.\d]+)" HAND=".+">' % player_re, re.MULTILINE)
self.re_sitsOut = re.compile("^%s sits out" % player_re, re.MULTILINE)
self.re_ShownCards = re.compile("^Seat (?P<SEAT>[0-9]+): %s \(.*\) showed \[(?P<CARDS>.*)\].*" % player_re, re.MULTILINE)
def readSupportedGames(self):
return [["ring", "hold", "nl"],
["ring", "hold", "pl"],
["ring", "hold", "fl"],
["ring", "stud", "fl"],
["ring", "draw", "fl"],
["ring", "omaha", "pl"]
]
def determineGameType(self, handText):
info = {'type':'ring'}
m = self.re_GameInfo.search(handText)
if not m:
print "determineGameType:", handText
return None
mg = m.groupdict()
# translations from captured groups to our info strings
#limits = { 'NL':'nl', 'PL':'pl', 'Limit':'fl' }
limits = { 'NL':'nl', 'PL':'pl'}
games = { # base, category
"GAME_THM" : ('hold','holdem'),
# 'Omaha' : ('hold','omahahi'),
#'Omaha Hi/Lo' : ('hold','omahahilo'),
# 'Razz' : ('stud','razz'),
#'7 Card Stud' : ('stud','studhi'),
# 'Badugi' : ('draw','badugi')
}
if 'LIMIT' in mg:
info['limitType'] = limits[mg['LIMIT']]
if 'GAME' in mg:
(info['base'], info['category']) = games[mg['GAME']]
if 'SB' in mg:
info['sb'] = mg['SB']
if 'BB' in mg:
info['bb'] = mg['BB']
if 'CURRENCY' in mg:
info['currency'] = mg['CURRENCY']
# NB: SB, BB must be interpreted as blinds or bets depending on limit type.
return info
def readHandInfo(self, hand):
info = {}
m = self.re_HandInfo.search(hand.handText,re.DOTALL)
if m:
info.update(m.groupdict())
# TODO: Be less lazy and parse maxseats from the HandInfo regex
if m.group('TABLEATTRIBUTES'):
m2 = re.search("\s*(\d+)-max", m.group('TABLEATTRIBUTES'))
hand.maxseats = int(m2.group(1))
m = self.re_GameInfo.search(hand.handText)
if m: info.update(m.groupdict())
m = self.re_Button.search(hand.handText)
if m: info.update(m.groupdict())
# TODO : I rather like the idea of just having this dict as hand.info
logging.debug("readHandInfo: %s" % info)
for key in info:
if key == 'DATETIME':
# Win2day uses UTC timestamp
hand.starttime = datetime.datetime.fromtimestamp(int(info[key]))
if key == 'HID':
hand.handid = info[key]
if key == 'TABLE':
hand.tablename = info[key]
if key == 'BUTTON':
hand.buttonpos = info[key]
def readButton(self, hand):
m = self.re_Button.search(hand.handText)
if m:
for player in hand.players:
if player[1] == m.group('BUTTON'):
hand.buttonpos = player[0]
break
else:
logging.info('readButton: not found')
def readPlayerStacks(self, hand):
logging.debug("readPlayerStacks")
m = self.re_PlayerInfo.finditer(hand.handText)
players = []
for a in m:
hand.addPlayer(int(a.group('SEAT')), a.group('PNAME'), a.group('CASH'))
def markStreets(self, hand):
# PREFLOP = ** Dealing down cards **
# This re fails if, say, river is missing; then we don't get the ** that starts the river.
if hand.gametype['base'] in ("hold"):
#m = re.search(r"\*\*\* HOLE CARDS \*\*\*(?P<PREFLOP>.+(?=\*\*\* FLOP \*\*\*)|.+)"
# 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"(\*\*\* RIVER \*\*\* \[\S\S \S\S \S\S \S\S] (?P<RIVER>\[\S\S\].+))?", hand.handText,re.DOTALL)
m = re.search('<ACTION TYPE="HAND_BLINDS" PLAYER=".+" KIND="HAND_BB" VALUE="[.0-9]+"></ACTION>(?P<PREFLOP>.+(?=<ACTION TYPE="HAND_BOARD" VALUE="BOARD_FLOP")|.+)'
'((?P<FLOP><ACTION TYPE="HAND_BOARD" VALUE="BOARD_FLOP" POT="[.0-9]+">.+(?=<ACTION TYPE="HAND_BOARD" VALUE="BOARD_TURN")|.+))?'
'((?P<TURN><ACTION TYPE="HAND_BOARD" VALUE="BOARD_TURN" POT="[.0-9]+">.+(?=<ACTION TYPE="HAND_BOARD" VALUE="BOARD_RIVER")|.+))?'
'((?P<RIVER><ACTION TYPE="HAND_BOARD" VALUE="BOARD_RIVER" POT="[.0-9]+">.+(?=<SHOWDOWN NAME="HAND_SHOWDOWN")|.+))?', hand.handText,re.DOTALL)
hand.addStreets(m)
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)
#print "DEBUG readCommunityCards:", street, hand.streets.group(street)
boardCards = set([])
if street == 'FLOP':
m = self.re_Card.findall(hand.streets[street])
for card in m:
boardCards.add(self.convertWin2dayCards(card))
else:
m = self.re_BoardLast.search(hand.streets[street])
boardCards.add(self.convertWin2dayCards(m.group('CARD')))
hand.setCommunityCards(street, boardCards)
def readAntes(self, hand):
logging.debug("reading antes")
m = self.re_Antes.finditer(hand.handText)
for player in m:
#~ logging.debug("hand.addAnte(%s,%s)" %(player.group('PNAME'), player.group('ANTE')))
hand.addAnte(player.group('PNAME'), player.group('ANTE'))
def readBringIn(self, hand):
m = self.re_BringIn.search(hand.handText,re.DOTALL)
if m:
#~ logging.debug("readBringIn: %s for %s" %(m.group('PNAME'), m.group('BRINGIN')))
hand.addBringIn(m.group('PNAME'), m.group('BRINGIN'))
def readBlinds(self, hand):
try:
m = self.re_PostSB.search(hand.handText)
hand.addBlind(m.group('PNAME'), 'small blind', m.group('SB'))
except: # no small blind
hand.addBlind(None, None, None)
for a in self.re_PostBB.finditer(hand.handText):
hand.addBlind(a.group('PNAME'), 'big blind', a.group('BB'))
for a in self.re_PostBoth.finditer(hand.handText):
hand.addBlind(a.group('PNAME'), 'small & big blinds', a.group('SBBB'))
def readHeroCards(self, hand):
# streets PREFLOP, PREDRAW, and THIRD are special cases beacause
# we need to grab hero's cards
m = self.re_HeroCards.finditer(hand.streets['PREFLOP'])
newcards = []
for found in m:
hand.hero = found.group('PNAME')
for card in self.re_Card.finditer(found.group('CARDS')):
print self.convertWin2dayCards(card.group('CARD'))
newcards.append(self.convertWin2dayCards(card.group('CARD')))
#hand.addHoleCards(holeCards, m.group('PNAME'))
hand.addHoleCards('PREFLOP', hand.hero, closed=newcards, shown=False, mucked=False, dealt=True)
def convertWin2dayCards(self, card):
card = int(card)
retCard = ''
cardconvert = { 1:'A',
10:'T',
11:'J',
12:'Q',
13:'K'}
realNumber = card % 13 + 1
if(realNumber in cardconvert):
retCard += cardconvert[realNumber]
else:
retCard += str(realNumber)
if(card > 38):
retCard += 's'
elif(card > 25):
retCard += 'h'
elif(card > 12):
retCard += 'c'
else:
retCard += 'd'
return(retCard)
def readDrawCards(self, hand, street):
logging.debug("readDrawCards")
m = self.re_HeroCards.finditer(hand.streets[street])
if m == None:
hand.involved = False
else:
for player in m:
hand.hero = player.group('PNAME') # Only really need to do this once
newcards = player.group('NEWCARDS')
oldcards = player.group('OLDCARDS')
if newcards == None:
newcards = set()
else:
newcards = set(newcards.split(' '))
if oldcards == None:
oldcards = set()
else:
oldcards = set(oldcards.split(' '))
hand.addDrawHoleCards(newcards, oldcards, player.group('PNAME'), street)
def readStudPlayerCards(self, hand, street):
# See comments of reference implementation in FullTiltToFpdb.py
logging.debug("readStudPlayerCards")
m = self.re_HeroCards.finditer(hand.streets[street])
for player in m:
#~ logging.debug(player.groupdict())
(pname, oldcards, newcards) = (player.group('PNAME'), player.group('OLDCARDS'), player.group('NEWCARDS'))
if oldcards:
oldcards = [c.strip() for c in oldcards.split(' ')]
if newcards:
newcards = [c.strip() for c in newcards.split(' ')]
if street=='ANTES':
return
elif street=='THIRD':
# we'll have observed hero holecards in CARDS and thirdstreet open cards in 'NEWCARDS'
# hero: [xx][o]
# others: [o]
hand.addPlayerCards(player = player.group('PNAME'), street = street, closed = oldcards, open = newcards)
elif street in ('FOURTH', 'FIFTH', 'SIXTH'):
# 4th:
# hero: [xxo] [o]
# others: [o] [o]
# 5th:
# hero: [xxoo] [o]
# others: [oo] [o]
# 6th:
# hero: [xxooo] [o]
# others: [ooo] [o]
hand.addPlayerCards(player = player.group('PNAME'), street = street, open = newcards)
# we may additionally want to check the earlier streets tally with what we have but lets trust it for now.
elif street=='SEVENTH' and newcards:
# hero: [xxoooo] [x]
# others: not reported.
hand.addPlayerCards(player = player.group('PNAME'), street = street, closed = newcards)
def readAction(self, hand, street):
m = self.re_Action.finditer(hand.streets[street])
for action in m:
if action.group('ATYPE') == 'ACTION_RAISE':
hand.addRaiseBy( street, action.group('PNAME'), action.group('BET') )
elif action.group('ATYPE') == 'ACTION_CALL':
hand.addCall( street, action.group('PNAME'), action.group('BET') )
elif action.group('ATYPE') == 'ACTION_ALLIN':
hand.addRaiseBy( street, action.group('PNAME'), action.group('BET') )
elif action.group('ATYPE') == 'ACTION_BET':
hand.addBet( street, action.group('PNAME'), action.group('BET') )
elif action.group('ATYPE') == 'ACTION_FOLD':
hand.addFold( street, action.group('PNAME'))
elif action.group('ATYPE') == 'ACTION_CHECK':
hand.addCheck( street, action.group('PNAME'))
elif action.group('ATYPE') == 'ACTION_DISCARD':
hand.addDiscard(street, action.group('PNAME'), action.group('NODISCARDED'), action.group('DISCARDED'))
elif action.group('ATYPE') == 'ACTION_STAND':
hand.addStandsPat( street, action.group('PNAME'))
else:
print "DEBUG: unimplemented readAction: '%s' '%s'" %(action.group('PNAME'),action.group('ATYPE'),)
def readShowdownActions(self, hand):
for shows in self.re_ShowdownAction.finditer(hand.handText):
showdownCards = set([])
for card in self.re_Card.finditer(shows.group('CARDS')):
#print "DEBUG:", card, card.group('CARD'), self.convertWin2dayCards(card.group('CARD'))
showdownCards.add(self.convertWin2dayCards(card.group('CARD')))
hand.addShownCards(showdownCards, shows.group('PNAME'))
def readCollectPot(self,hand):
for m in self.re_CollectPot.finditer(hand.handText):
potcoll = Decimal(m.group('POT'))
if potcoll > 0:
hand.addCollectPot(player=m.group('PNAME'),pot=potcoll)
def readShownCards(self,hand):
for m in self.re_ShownCards.finditer(hand.handText):
if m.group('CARDS') is not None:
cards = m.group('CARDS')
cards = set(cards.split(' '))
hand.addShownCards(cards=cards, player=m.group('PNAME'))
if __name__ == "__main__":
parser = OptionParser()
parser.add_option("-i", "--input", dest="ipath", help="parse input hand history", default="-")
parser.add_option("-o", "--output", dest="opath", help="output translation to", default="-")
parser.add_option("-f", "--follow", dest="follow", help="follow (tail -f) the input", action="store_true", default=False)
parser.add_option("-q", "--quiet",
action="store_const", const=logging.CRITICAL, dest="verbosity", default=logging.INFO)
parser.add_option("-v", "--verbose",
action="store_const", const=logging.INFO, dest="verbosity")
parser.add_option("--vv",
action="store_const", const=logging.DEBUG, dest="verbosity")
(options, args) = parser.parse_args()
LOG_FILENAME = './logging.out'
logging.basicConfig(filename=LOG_FILENAME,level=options.verbosity)
e = Win2day(in_path = options.ipath, out_path = options.opath, follow = options.follow)

View File

@ -17,9 +17,9 @@
import os import os
import sys import sys
import threading
import Options import Options
import string import string
cl_options = string.join(sys.argv[1:]) cl_options = string.join(sys.argv[1:])
(options, sys.argv) = Options.fpdb_options() (options, sys.argv) = Options.fpdb_options()
@ -28,10 +28,14 @@ if not options.errorsToConsole:
errorFile = open('fpdb-error-log.txt', 'w', 0) errorFile = open('fpdb-error-log.txt', 'w', 0)
sys.stderr = errorFile sys.stderr = errorFile
import logging
import pygtk import pygtk
pygtk.require('2.0') pygtk.require('2.0')
import gtk import gtk
import interlocks
import fpdb_simple import fpdb_simple
import GuiBulkImport import GuiBulkImport
@ -115,11 +119,13 @@ class fpdb:
def dia_create_del_database(self, widget, data=None): def dia_create_del_database(self, widget, data=None):
print "todo: implement dia_create_del_database" print "todo: implement dia_create_del_database"
self.obtain_global_lock() self.obtain_global_lock()
self.release_global_lock()
#end def dia_create_del_database #end def dia_create_del_database
def dia_create_del_user(self, widget, data=None): def dia_create_del_user(self, widget, data=None):
print "todo: implement dia_create_del_user" print "todo: implement dia_create_del_user"
self.obtain_global_lock() self.obtain_global_lock()
self.release_global_lock()
#end def dia_create_del_user #end def dia_create_del_user
def dia_database_stats(self, widget, data=None): def dia_database_stats(self, widget, data=None):
@ -128,7 +134,7 @@ class fpdb:
#end def dia_database_stats #end def dia_database_stats
def dia_database_sessions(self, widget, data=None): def dia_database_sessions(self, widget, data=None):
new_sessions_thread=GuiSessionViewer.GuiSessionViewer(self.config, self.querydict) new_sessions_thread=GuiSessionViewer.GuiSessionViewer(self.config, self.sql)
self.threads.append(new_sessions_thread) self.threads.append(new_sessions_thread)
sessions_tab=new_sessions_thread.get_vbox() sessions_tab=new_sessions_thread.get_vbox()
self.add_and_display_tab(sessions_tab, "Sessions") self.add_and_display_tab(sessions_tab, "Sessions")
@ -136,16 +142,19 @@ class fpdb:
def dia_delete_db_parts(self, widget, data=None): def dia_delete_db_parts(self, widget, data=None):
print "todo: implement dia_delete_db_parts" print "todo: implement dia_delete_db_parts"
self.obtain_global_lock() self.obtain_global_lock()
self.release_global_lock()
#end def dia_delete_db_parts #end def dia_delete_db_parts
def dia_edit_profile(self, widget=None, data=None, create_default=False, path=None): def dia_edit_profile(self, widget=None, data=None, create_default=False, path=None):
print "todo: implement dia_edit_profile" print "todo: implement dia_edit_profile"
self.obtain_global_lock() self.obtain_global_lock()
self.release_global_lock()
#end def dia_edit_profile #end def dia_edit_profile
def dia_export_db(self, widget, data=None): def dia_export_db(self, widget, data=None):
print "todo: implement dia_export_db" print "todo: implement dia_export_db"
self.obtain_global_lock() self.obtain_global_lock()
self.release_global_lock()
#end def dia_export_db #end def dia_export_db
def dia_get_db_root_credentials(self): def dia_get_db_root_credentials(self):
@ -171,6 +180,7 @@ class fpdb:
def dia_import_db(self, widget, data=None): def dia_import_db(self, widget, data=None):
print "todo: implement dia_import_db" print "todo: implement dia_import_db"
self.obtain_global_lock() self.obtain_global_lock()
self.release_global_lock()
#end def dia_import_db #end def dia_import_db
def dia_licensing(self, widget, data=None): def dia_licensing(self, widget, data=None):
@ -179,7 +189,7 @@ class fpdb:
def dia_load_profile(self, widget, data=None): def dia_load_profile(self, widget, data=None):
"""Dialogue to select a file to load a profile from""" """Dialogue to select a file to load a profile from"""
if self.obtain_global_lock() == 0: # returns 0 if successful if self.obtain_global_lock(): # returns true if successful
#try: #try:
# chooser = gtk.FileChooserDialog(title="Please select a profile file to load", # chooser = gtk.FileChooserDialog(title="Please select a profile file to load",
# action=gtk.FILE_CHOOSER_ACTION_OPEN, # action=gtk.FILE_CHOOSER_ACTION_OPEN,
@ -194,15 +204,18 @@ class fpdb:
# print 'User cancelled loading profile' # print 'User cancelled loading profile'
#except: #except:
# pass # pass
self.load_profile() try:
self.load_profile()
except:
pass
self.release_global_lock() self.release_global_lock()
#end def dia_load_profile #end def dia_load_profile
def dia_recreate_tables(self, widget, data=None): def dia_recreate_tables(self, widget, data=None):
"""Dialogue that asks user to confirm that he wants to delete and recreate the tables""" """Dialogue that asks user to confirm that he wants to delete and recreate the tables"""
if self.obtain_global_lock() in (0,2): # returns 0 if successful, 2 if Hands table does not exist if self.obtain_global_lock(): # returns true if successful
lock_released = False #lock_released = False
try: try:
dia_confirm = gtk.MessageDialog(parent=None, flags=0, type=gtk.MESSAGE_WARNING, dia_confirm = gtk.MessageDialog(parent=None, flags=0, type=gtk.MESSAGE_WARNING,
buttons=(gtk.BUTTONS_YES_NO), message_format="Confirm deleting and recreating tables") buttons=(gtk.BUTTONS_YES_NO), message_format="Confirm deleting and recreating tables")
@ -213,27 +226,46 @@ class fpdb:
response = dia_confirm.run() response = dia_confirm.run()
dia_confirm.destroy() dia_confirm.destroy()
if response == gtk.RESPONSE_YES: if response == gtk.RESPONSE_YES:
if self.db.fdb.backend == self.fdb_lock.fdb.MYSQL_INNODB: #if self.db.fdb.backend == self.fdb_lock.fdb.MYSQL_INNODB:
# mysql requires locks on all tables or none - easier to release this lock # mysql requires locks on all tables or none - easier to release this lock
# than lock all the other tables # than lock all the other tables
# ToDo: lock all other tables so that lock doesn't have to be released # ToDo: lock all other tables so that lock doesn't have to be released
self.release_global_lock() # self.release_global_lock()
lock_released = True # lock_released = True
self.db.fdb.recreate_tables() self.db.recreate_tables()
else: #else:
# for other dbs use same connection as holds global lock # for other dbs use same connection as holds global lock
self.fdb_lock.fdb.recreate_tables() # self.fdb_lock.fdb.recreate_tables()
elif response == gtk.RESPONSE_NO: elif response == gtk.RESPONSE_NO:
print 'User cancelled recreating tables' print 'User cancelled recreating tables'
except: except:
pass pass
if not lock_released: #if not lock_released:
self.release_global_lock() self.release_global_lock()
#end def dia_recreate_tables #end def dia_recreate_tables
def dia_recreate_hudcache(self, widget, data=None):
if self.obtain_global_lock():
try:
dia_confirm = gtk.MessageDialog(parent=None, flags=0, type=gtk.MESSAGE_WARNING, buttons=(gtk.BUTTONS_YES_NO), message_format="Confirm recreating HUD cache")
diastring = "Please confirm that you want to re-create the HUD cache."
dia_confirm.format_secondary_text(diastring)
response = dia_confirm.run()
dia_confirm.destroy()
if response == gtk.RESPONSE_YES:
self.db.rebuild_hudcache()
elif response == gtk.REPSONSE_NO:
print 'User cancelled rebuilding hud cache'
except:
pass
self.release_global_lock()
def dia_regression_test(self, widget, data=None): def dia_regression_test(self, widget, data=None):
print "todo: implement dia_regression_test" print "todo: implement dia_regression_test"
self.obtain_global_lock() self.obtain_global_lock()
self.release_global_lock()
#end def dia_regression_test #end def dia_regression_test
def dia_save_profile(self, widget, data=None): def dia_save_profile(self, widget, data=None):
@ -292,6 +324,7 @@ class fpdb:
<menuitem action="createdb"/> <menuitem action="createdb"/>
<menuitem action="createuser"/> <menuitem action="createuser"/>
<menuitem action="createtabs"/> <menuitem action="createtabs"/>
<menuitem action="rebuildhudcache"/>
<menuitem action="stats"/> <menuitem action="stats"/>
<menuitem action="sessions"/> <menuitem action="sessions"/>
</menu> </menu>
@ -330,6 +363,7 @@ class fpdb:
('createdb', None, 'Create or Delete _Database (todo)', None, 'Create or Delete Database', self.dia_create_del_database), ('createdb', None, 'Create or Delete _Database (todo)', None, 'Create or Delete Database', self.dia_create_del_database),
('createuser', None, 'Create or Delete _User (todo)', None, 'Create or Delete User', self.dia_create_del_user), ('createuser', None, 'Create or Delete _User (todo)', None, 'Create or Delete User', self.dia_create_del_user),
('createtabs', None, 'Create or Recreate _Tables', None, 'Create or Recreate Tables ', self.dia_recreate_tables), ('createtabs', None, 'Create or Recreate _Tables', None, 'Create or Recreate Tables ', self.dia_recreate_tables),
('rebuildhudcache', None, 'Rebuild HUD Cache', None, 'Rebuild HUD Cache', self.dia_recreate_hudcache),
('stats', None, '_Statistics (todo)', None, 'View Database Statistics', self.dia_database_stats), ('stats', None, '_Statistics (todo)', None, 'View Database Statistics', self.dia_database_stats),
('sessions', None, 'Sessions', None, 'View Sessions', self.dia_database_sessions), ('sessions', None, 'Sessions', None, 'View Sessions', self.dia_database_sessions),
('help', None, '_Help'), ('help', None, '_Help'),
@ -352,6 +386,7 @@ class fpdb:
"""Loads profile from the provided path name.""" """Loads profile from the provided path name."""
self.config = Configuration.Config(file=options.config, dbname=options.dbname) self.config = Configuration.Config(file=options.config, dbname=options.dbname)
self.settings = {} self.settings = {}
self.settings['global_lock'] = self.lock
if (os.sep=="/"): if (os.sep=="/"):
self.settings['os']="linuxmac" self.settings['os']="linuxmac"
else: else:
@ -395,7 +430,6 @@ class fpdb:
self.status_bar.set_text("Status: Connected to %s database named %s on host %s" % (self.db.get_backend_name(),self.db.fdb.database, self.db.fdb.host)) self.status_bar.set_text("Status: Connected to %s database named %s on host %s" % (self.db.get_backend_name(),self.db.fdb.database, self.db.fdb.host))
# Database connected to successfully, load queries to pass on to other classes # Database connected to successfully, load queries to pass on to other classes
self.querydict = FpdbSQLQueries.FpdbSQLQueries(self.db.get_backend_name())
self.db.connection.rollback() self.db.connection.rollback()
#end def load_profile #end def load_profile
@ -404,10 +438,15 @@ class fpdb:
#end def not_implemented #end def not_implemented
def obtain_global_lock(self): def obtain_global_lock(self):
print "\nTaking global lock ..." ret = self.lock.acquire(False) # will return false if lock is already held
self.fdb_lock = Database.Database(self.config, sql = self.sql) if ret:
self.fdb_lock.do_connect(self.config) print "\nGlobal lock taken ..."
return self.fdb_lock.fdb.get_global_lock() else:
print "\nFailed to get global lock."
return ret
# need to release it later:
# self.lock.release()
#end def obtain_global_lock #end def obtain_global_lock
def quit(self, widget, data=None): def quit(self, widget, data=None):
@ -418,8 +457,7 @@ class fpdb:
#end def quit_cliecked #end def quit_cliecked
def release_global_lock(self): def release_global_lock(self):
self.fdb_lock.fdb.db.rollback() self.lock.release()
self.fdb_lock.fdb.disconnect()
print "Global lock released.\n" print "Global lock released.\n"
#end def release_global_lock #end def release_global_lock
@ -438,20 +476,20 @@ class fpdb:
def tab_bulk_import(self, widget, data=None): def tab_bulk_import(self, widget, data=None):
"""opens a tab for bulk importing""" """opens a tab for bulk importing"""
#print "start of tab_bulk_import" #print "start of tab_bulk_import"
new_import_thread=GuiBulkImport.GuiBulkImport(self.settings, self.config) new_import_thread = GuiBulkImport.GuiBulkImport(self.settings, self.config, self.sql)
self.threads.append(new_import_thread) self.threads.append(new_import_thread)
bulk_tab=new_import_thread.get_vbox() bulk_tab=new_import_thread.get_vbox()
self.add_and_display_tab(bulk_tab, "Bulk Import") self.add_and_display_tab(bulk_tab, "Bulk Import")
#end def tab_bulk_import #end def tab_bulk_import
def tab_player_stats(self, widget, data=None): def tab_player_stats(self, widget, data=None):
new_ps_thread=GuiPlayerStats.GuiPlayerStats(self.config, self.querydict, self.window) new_ps_thread=GuiPlayerStats.GuiPlayerStats(self.config, self.sql, self.window)
self.threads.append(new_ps_thread) self.threads.append(new_ps_thread)
ps_tab=new_ps_thread.get_vbox() ps_tab=new_ps_thread.get_vbox()
self.add_and_display_tab(ps_tab, "Player Stats") self.add_and_display_tab(ps_tab, "Player Stats")
def tab_positional_stats(self, widget, data=None): def tab_positional_stats(self, widget, data=None):
new_ps_thread=GuiPositionalStats.GuiPositionalStats(self.config, self.querydict) new_ps_thread = GuiPositionalStats.GuiPositionalStats(self.config, self.sql)
self.threads.append(new_ps_thread) self.threads.append(new_ps_thread)
ps_tab=new_ps_thread.get_vbox() ps_tab=new_ps_thread.get_vbox()
self.add_and_display_tab(ps_tab, "Positional Stats") self.add_and_display_tab(ps_tab, "Positional Stats")
@ -478,7 +516,7 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt")
def tabGraphViewer(self, widget, data=None): def tabGraphViewer(self, widget, data=None):
"""opens a graph viewer tab""" """opens a graph viewer tab"""
#print "start of tabGraphViewer" #print "start of tabGraphViewer"
new_gv_thread = GuiGraphViewer.GuiGraphViewer(self.querydict, self.config) new_gv_thread = GuiGraphViewer.GuiGraphViewer(self.sql, self.config)
self.threads.append(new_gv_thread) self.threads.append(new_gv_thread)
gv_tab=new_gv_thread.get_vbox() gv_tab=new_gv_thread.get_vbox()
self.add_and_display_tab(gv_tab, "Graphs") self.add_and_display_tab(gv_tab, "Graphs")
@ -486,6 +524,8 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt")
def __init__(self): def __init__(self):
self.threads = [] self.threads = []
# no more than 1 process can this lock at a time:
self.lock = interlocks.InterProcessLock(name="fpdb_global_lock")
self.db = None self.db = None
self.status_bar = None self.status_bar = None

View File

@ -18,124 +18,21 @@
import os import os
import re import re
import sys import sys
import logging
from time import time, strftime from time import time, strftime
import fpdb_simple import fpdb_simple
import FpdbSQLQueries import FpdbSQLQueries
class fpdb_db: class fpdb_db:
MYSQL_INNODB = 2
PGSQL = 3
SQLITE = 4
def __init__(self): def __init__(self):
"""Simple constructor, doesnt really do anything""" """Simple constructor, doesnt really do anything"""
self.db = None self.db = None
self.cursor = None self.cursor = None
self.sql = {} self.sql = {}
self.MYSQL_INNODB = 2
self.PGSQL = 3
self.SQLITE = 4
# Data Structures for index and foreign key creation
# drop_code is an int with possible values: 0 - don't drop for bulk import
# 1 - drop during bulk import
# db differences:
# - note that mysql automatically creates indexes on constrained columns when
# foreign keys are created, while postgres does not. Hence the much longer list
# of indexes is required for postgres.
# all primary keys are left on all the time
#
# table column drop_code
self.indexes = [
[ ] # no db with index 0
, [ ] # no db with index 1
, [ # indexes for mysql (list index 2)
{'tab':'Players', 'col':'name', 'drop':0}
, {'tab':'Hands', 'col':'siteHandNo', 'drop':0}
, {'tab':'Tourneys', 'col':'siteTourneyNo', 'drop':0}
]
, [ # indexes for postgres (list index 3)
{'tab':'Boardcards', 'col':'handId', 'drop':0}
, {'tab':'Gametypes', 'col':'siteId', 'drop':0}
, {'tab':'Hands', 'col':'gametypeId', 'drop':0} # mct 22/3/09
, {'tab':'Hands', 'col':'siteHandNo', 'drop':0}
, {'tab':'HandsActions', 'col':'handsPlayerId', 'drop':0}
, {'tab':'HandsPlayers', 'col':'handId', 'drop':1}
, {'tab':'HandsPlayers', 'col':'playerId', 'drop':1}
, {'tab':'HandsPlayers', 'col':'tourneysPlayersId', 'drop':0}
, {'tab':'HudCache', 'col':'gametypeId', 'drop':1}
, {'tab':'HudCache', 'col':'playerId', 'drop':0}
, {'tab':'HudCache', 'col':'tourneyTypeId', 'drop':0}
, {'tab':'Players', 'col':'siteId', 'drop':1}
, {'tab':'Players', 'col':'name', 'drop':0}
, {'tab':'Tourneys', 'col':'tourneyTypeId', 'drop':1}
, {'tab':'Tourneys', 'col':'siteTourneyNo', 'drop':0}
, {'tab':'TourneysPlayers', 'col':'playerId', 'drop':0}
, {'tab':'TourneysPlayers', 'col':'tourneyId', 'drop':0}
, {'tab':'TourneyTypes', 'col':'siteId', 'drop':0}
]
]
self.foreignKeys = [
[ ] # no db with index 0
, [ ] # no db with index 1
, [ # foreign keys for mysql
{'fktab':'Hands', 'fkcol':'gametypeId', 'rtab':'Gametypes', 'rcol':'id', 'drop':1}
, {'fktab':'HandsPlayers', 'fkcol':'handId', 'rtab':'Hands', 'rcol':'id', 'drop':1}
, {'fktab':'HandsPlayers', 'fkcol':'playerId', 'rtab':'Players', 'rcol':'id', 'drop':1}
, {'fktab':'HandsActions', 'fkcol':'handsPlayerId', 'rtab':'HandsPlayers', 'rcol':'id', 'drop':1}
, {'fktab':'HudCache', 'fkcol':'gametypeId', 'rtab':'Gametypes', 'rcol':'id', 'drop':1}
, {'fktab':'HudCache', 'fkcol':'playerId', 'rtab':'Players', 'rcol':'id', 'drop':0}
, {'fktab':'HudCache', 'fkcol':'tourneyTypeId', 'rtab':'TourneyTypes', 'rcol':'id', 'drop':1}
]
, [ # foreign keys for postgres
{'fktab':'Hands', 'fkcol':'gametypeId', 'rtab':'Gametypes', 'rcol':'id', 'drop':1}
, {'fktab':'HandsPlayers', 'fkcol':'handId', 'rtab':'Hands', 'rcol':'id', 'drop':1}
, {'fktab':'HandsPlayers', 'fkcol':'playerId', 'rtab':'Players', 'rcol':'id', 'drop':1}
, {'fktab':'HandsActions', 'fkcol':'handsPlayerId', 'rtab':'HandsPlayers', 'rcol':'id', 'drop':1}
, {'fktab':'HudCache', 'fkcol':'gametypeId', 'rtab':'Gametypes', 'rcol':'id', 'drop':1}
, {'fktab':'HudCache', 'fkcol':'playerId', 'rtab':'Players', 'rcol':'id', 'drop':0}
, {'fktab':'HudCache', 'fkcol':'tourneyTypeId', 'rtab':'TourneyTypes', 'rcol':'id', 'drop':1}
]
]
# MySQL Notes:
# "FOREIGN KEY (handId) REFERENCES Hands(id)" - requires index on Hands.id
# - creates index handId on <thistable>.handId
# alter table t drop foreign key fk
# alter table t add foreign key (fkcol) references tab(rcol)
# alter table t add constraint c foreign key (fkcol) references tab(rcol)
# (fkcol is used for foreigh key name)
# mysql to list indexes:
# SELECT table_name, index_name, non_unique, column_name
# FROM INFORMATION_SCHEMA.STATISTICS
# WHERE table_name = 'tbl_name'
# AND table_schema = 'db_name'
# ORDER BY table_name, index_name, seq_in_index
#
# ALTER TABLE Tourneys ADD INDEX siteTourneyNo(siteTourneyNo)
# ALTER TABLE tab DROP INDEX idx
# mysql to list fks:
# SELECT constraint_name, table_name, column_name, referenced_table_name, referenced_column_name
# FROM information_schema.KEY_COLUMN_USAGE
# WHERE REFERENCED_TABLE_SCHEMA = (your schema name here)
# AND REFERENCED_TABLE_NAME is not null
# ORDER BY TABLE_NAME, COLUMN_NAME;
# this may indicate missing object
# _mysql_exceptions.OperationalError: (1025, "Error on rename of '.\\fpdb\\hands' to '.\\fpdb\\#sql2-7f0-1b' (errno: 152)")
# PG notes:
# To add a foreign key constraint to a table:
# ALTER TABLE tab ADD CONSTRAINT c FOREIGN KEY (col) REFERENCES t2(col2) MATCH FULL;
# ALTER TABLE tab DROP CONSTRAINT zipchk
#
# Note: index names must be unique across a schema
# CREATE INDEX idx ON tab(col)
# DROP INDEX idx
#end def __init__ #end def __init__
def do_connect(self, config=None): def do_connect(self, config=None):
@ -146,12 +43,12 @@ class fpdb_db:
self.settings = {} self.settings = {}
self.settings['os'] = "linuxmac" if os.name != "nt" else "windows" self.settings['os'] = "linuxmac" if os.name != "nt" else "windows"
self.settings.update(config.get_db_parameters()) db = config.get_db_parameters()
self.connect(self.settings['db-backend'], self.connect(backend=db['db-backend'],
self.settings['db-host'], host=db['db-host'],
self.settings['db-databaseName'], database=db['db-databaseName'],
self.settings['db-user'], user=db['db-user'],
self.settings['db-password']) password=db['db-password'])
#end def do_connect #end def do_connect
def connect(self, backend=None, host=None, database=None, def connect(self, backend=None, host=None, database=None,
@ -164,13 +61,13 @@ class fpdb_db:
self.user=user self.user=user
self.password=password self.password=password
self.database=database self.database=database
if backend==self.MYSQL_INNODB: if backend==fpdb_db.MYSQL_INNODB:
import MySQLdb import MySQLdb
try: try:
self.db = MySQLdb.connect(host = host, user = user, passwd = password, db = database, use_unicode=True) self.db = MySQLdb.connect(host = host, user = user, passwd = password, db = database, use_unicode=True)
except: except:
raise fpdb_simple.FpdbError("MySQL connection failed") raise fpdb_simple.FpdbError("MySQL connection failed")
elif backend==self.PGSQL: elif backend==fpdb_db.PGSQL:
import psycopg2 import psycopg2
import psycopg2.extensions import psycopg2.extensions
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE) psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
@ -182,9 +79,7 @@ class fpdb_db:
# sqlcoder: This database only connect failed in my windows setup?? # sqlcoder: This database only connect failed in my windows setup??
# Modifed it to try the 4 parameter style if the first connect fails - does this work everywhere? # Modifed it to try the 4 parameter style if the first connect fails - does this work everywhere?
connected = False connected = False
if self.host == None or self.host == '' \ if self.host == "localhost" or self.host == "127.0.0.1":
or self.host == "localhost" \
or self.host == "127.0.0.1":
try: try:
self.db = psycopg2.connect(database = database) self.db = psycopg2.connect(database = database)
connected = True connected = True
@ -203,13 +98,19 @@ class fpdb_db:
msg = "PostgreSQL connection to database (%s) user (%s) failed." % (database, user) msg = "PostgreSQL connection to database (%s) user (%s) failed." % (database, user)
print msg print msg
raise fpdb_simple.FpdbError(msg) raise fpdb_simple.FpdbError(msg)
elif backend==fpdb_db.SQLITE:
logging.info("Connecting to SQLite:%(database)s" % {'database':database})
import sqlite3
self.db = sqlite3.connect(database,detect_types=sqlite3.PARSE_DECLTYPES)
sqlite3.register_converter("bool", lambda x: bool(int(x)))
sqlite3.register_adapter(bool, lambda x: "1" if x else "0")
else: else:
raise fpdb_simple.FpdbError("unrecognised database backend:"+backend) raise fpdb_simple.FpdbError("unrecognised database backend:"+backend)
self.cursor=self.db.cursor() self.cursor=self.db.cursor()
self.cursor.execute('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED')
self.db.commit()
# Set up query dictionary as early in the connection process as we can. # Set up query dictionary as early in the connection process as we can.
self.sql = FpdbSQLQueries.FpdbSQLQueries(self.get_backend_name()) self.sql = FpdbSQLQueries.FpdbSQLQueries(self.get_backend_name())
self.cursor.execute(self.sql.query['set tx level'])
self.wrongDbVersion=False self.wrongDbVersion=False
try: try:
self.cursor.execute("SELECT * FROM Settings") self.cursor.execute("SELECT * FROM Settings")
@ -238,77 +139,14 @@ class fpdb_db:
self.disconnect(due_to_error) self.disconnect(due_to_error)
self.connect(self.backend, self.host, self.database, self.user, self.password) self.connect(self.backend, self.host, self.database, self.user, self.password)
def create_tables(self):
#todo: should detect and fail gracefully if tables already exist.
self.cursor.execute(self.sql.query['createSettingsTable'])
self.cursor.execute(self.sql.query['createSitesTable'])
self.cursor.execute(self.sql.query['createGametypesTable'])
self.cursor.execute(self.sql.query['createPlayersTable'])
self.cursor.execute(self.sql.query['createAutoratesTable'])
self.cursor.execute(self.sql.query['createHandsTable'])
self.cursor.execute(self.sql.query['createTourneyTypesTable'])
self.cursor.execute(self.sql.query['createTourneysTable'])
self.cursor.execute(self.sql.query['createTourneysPlayersTable'])
self.cursor.execute(self.sql.query['createHandsPlayersTable'])
self.cursor.execute(self.sql.query['createHandsActionsTable'])
self.cursor.execute(self.sql.query['createHudCacheTable'])
#self.cursor.execute(self.sql.query['addTourneyIndex'])
#self.cursor.execute(self.sql.query['addHandsIndex'])
#self.cursor.execute(self.sql.query['addPlayersIndex'])
self.fillDefaultData()
self.db.commit()
#end def disconnect
def drop_tables(self):
"""Drops the fpdb tables from the current db"""
if(self.get_backend_name() == 'MySQL InnoDB'):
#Databases with FOREIGN KEY support need this switched of before you can drop tables
self.drop_referential_integrity()
# Query the DB to see what tables exist
self.cursor.execute(self.sql.query['list_tables'])
for table in self.cursor:
self.cursor.execute(self.sql.query['drop_table'] + table[0])
elif(self.get_backend_name() == 'PostgreSQL'):
self.db.commit()# I have no idea why this makes the query work--REB 07OCT2008
self.cursor.execute(self.sql.query['list_tables'])
tables = self.cursor.fetchall()
for table in tables:
self.cursor.execute(self.sql.query['drop_table'] + table[0] + ' cascade')
elif(self.get_backend_name() == 'SQLite'):
#todo: sqlite version here
print "Empty function here"
self.db.commit()
#end def drop_tables
def drop_referential_integrity(self):
"""Update all tables to remove foreign keys"""
self.cursor.execute(self.sql.query['list_tables'])
result = self.cursor.fetchall()
for i in range(len(result)):
self.cursor.execute("SHOW CREATE TABLE " + result[i][0])
inner = self.cursor.fetchall()
for j in range(len(inner)):
# result[i][0] - Table name
# result[i][1] - CREATE TABLE parameters
#Searching for CONSTRAINT `tablename_ibfk_1`
for m in re.finditer('(ibfk_[0-9]+)', inner[j][1]):
key = "`" + inner[j][0] + "_" + m.group() + "`"
self.cursor.execute("ALTER TABLE " + inner[j][0] + " DROP FOREIGN KEY " + key)
self.db.commit()
#end drop_referential_inegrity
def get_backend_name(self): def get_backend_name(self):
"""Returns the name of the currently used backend""" """Returns the name of the currently used backend"""
if self.backend==2: if self.backend==2:
return "MySQL InnoDB" return "MySQL InnoDB"
elif self.backend==3: elif self.backend==3:
return "PostgreSQL" return "PostgreSQL"
elif self.backend==4:
return "SQLite"
else: else:
raise fpdb_simple.FpdbError("invalid backend") raise fpdb_simple.FpdbError("invalid backend")
#end def get_backend_name #end def get_backend_name
@ -317,342 +155,107 @@ class fpdb_db:
return (self.host, self.database, self.user, self.password) return (self.host, self.database, self.user, self.password)
#end def get_db_info #end def get_db_info
def fillDefaultData(self):
self.cursor.execute("INSERT INTO Settings VALUES (118);")
self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'Full Tilt Poker', 'USD');")
self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'PokerStars', 'USD');")
self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'Everleaf', 'USD');")
self.cursor.execute("INSERT INTO TourneyTypes VALUES (DEFAULT, 1, 0, 0, 0, False);")
#end def fillDefaultData
def recreate_tables(self):
"""(Re-)creates the tables of the current DB"""
self.drop_tables()
self.create_tables()
self.createAllIndexes()
self.db.commit()
print "Finished recreating tables"
#end def recreate_tables
def prepareBulkImport(self):
"""Drop some indexes/foreign keys to prepare for bulk import.
Currently keeping the standalone indexes as needed to import quickly"""
stime = time()
if self.backend == self.PGSQL:
self.db.set_isolation_level(0) # allow table/index operations to work
for fk in self.foreignKeys[self.backend]:
if fk['drop'] == 1:
if self.backend == self.MYSQL_INNODB:
self.cursor.execute("SELECT constraint_name " +
"FROM information_schema.KEY_COLUMN_USAGE " +
#"WHERE REFERENCED_TABLE_SCHEMA = 'fpdb'
"WHERE 1=1 " +
"AND table_name = %s AND column_name = %s " +
"AND referenced_table_name = %s " +
"AND referenced_column_name = %s ",
(fk['fktab'], fk['fkcol'], fk['rtab'], fk['rcol']) )
cons = self.cursor.fetchone()
#print "preparebulk: cons=", cons
if cons:
print "dropping mysql fk", cons[0], fk['fktab'], fk['fkcol']
try:
self.cursor.execute("alter table " + fk['fktab'] + " drop foreign key " + cons[0])
except:
pass
elif self.backend == self.PGSQL:
# DON'T FORGET TO RECREATE THEM!!
print "dropping pg fk", fk['fktab'], fk['fkcol']
try:
# try to lock table to see if index drop will work:
# hmmm, tested by commenting out rollback in grapher. lock seems to work but
# then drop still hangs :-( does work in some tests though??
# will leave code here for now pending further tests/enhancement ...
self.cursor.execute( "lock table %s in exclusive mode nowait" % (fk['fktab'],) )
#print "after lock, status:", self.cursor.statusmessage
#print "alter table %s drop constraint %s_%s_fkey" % (fk['fktab'], fk['fktab'], fk['fkcol'])
try:
self.cursor.execute("alter table %s drop constraint %s_%s_fkey" % (fk['fktab'], fk['fktab'], fk['fkcol']))
print "dropped pg fk pg fk %s_%s_fkey, continuing ..." % (fk['fktab'], fk['fkcol'])
except:
if "does not exist" not in str(sys.exc_value):
print "warning: drop pg fk %s_%s_fkey failed: %s, continuing ..." \
% (fk['fktab'], fk['fkcol'], str(sys.exc_value).rstrip('\n') )
except:
print "warning: constraint %s_%s_fkey not dropped: %s, continuing ..." \
% (fk['fktab'],fk['fkcol'], str(sys.exc_value).rstrip('\n'))
else:
print "Only MySQL and Postgres supported so far"
return -1
for idx in self.indexes[self.backend]:
if idx['drop'] == 1:
if self.backend == self.MYSQL_INNODB:
print "dropping mysql index ", idx['tab'], idx['col']
try:
# apparently nowait is not implemented in mysql so this just hands if there are locks
# preventing the index drop :-(
self.cursor.execute( "alter table %s drop index %s", (idx['tab'],idx['col']) )
except:
pass
elif self.backend == self.PGSQL:
# DON'T FORGET TO RECREATE THEM!!
print "dropping pg index ", idx['tab'], idx['col']
try:
# try to lock table to see if index drop will work:
self.cursor.execute( "lock table %s in exclusive mode nowait" % (idx['tab'],) )
#print "after lock, status:", self.cursor.statusmessage
try:
# table locked ok so index drop should work:
#print "drop index %s_%s_idx" % (idx['tab'],idx['col'])
self.cursor.execute( "drop index if exists %s_%s_idx" % (idx['tab'],idx['col']) )
#print "dropped pg index ", idx['tab'], idx['col']
except:
if "does not exist" not in str(sys.exc_value):
print "warning: drop index %s_%s_idx failed: %s, continuing ..." \
% (idx['tab'],idx['col'], str(sys.exc_value).rstrip('\n'))
except:
print "warning: index %s_%s_idx not dropped %s, continuing ..." \
% (idx['tab'],idx['col'], str(sys.exc_value).rstrip('\n'))
else:
print "Error: Only MySQL and Postgres supported so far"
return -1
if self.backend == self.PGSQL:
self.db.set_isolation_level(1) # go back to normal isolation level
self.db.commit() # seems to clear up errors if there were any in postgres
ptime = time() - stime
print "prepare import took", ptime, "seconds"
#end def prepareBulkImport
def afterBulkImport(self):
"""Re-create any dropped indexes/foreign keys after bulk import"""
stime = time()
if self.backend == self.PGSQL:
self.db.set_isolation_level(0) # allow table/index operations to work
for fk in self.foreignKeys[self.backend]:
if fk['drop'] == 1:
if self.backend == self.MYSQL_INNODB:
self.cursor.execute("SELECT constraint_name " +
"FROM information_schema.KEY_COLUMN_USAGE " +
#"WHERE REFERENCED_TABLE_SCHEMA = 'fpdb'
"WHERE 1=1 " +
"AND table_name = %s AND column_name = %s " +
"AND referenced_table_name = %s " +
"AND referenced_column_name = %s ",
(fk['fktab'], fk['fkcol'], fk['rtab'], fk['rcol']) )
cons = self.cursor.fetchone()
print "afterbulk: cons=", cons
if cons:
pass
else:
print "creating fk ", fk['fktab'], fk['fkcol'], "->", fk['rtab'], fk['rcol']
try:
self.cursor.execute("alter table " + fk['fktab'] + " add foreign key ("
+ fk['fkcol'] + ") references " + fk['rtab'] + "("
+ fk['rcol'] + ")")
except:
pass
elif self.backend == self.PGSQL:
print "creating fk ", fk['fktab'], fk['fkcol'], "->", fk['rtab'], fk['rcol']
try:
self.cursor.execute("alter table " + fk['fktab'] + " add constraint "
+ fk['fktab'] + '_' + fk['fkcol'] + '_fkey'
+ " foreign key (" + fk['fkcol']
+ ") references " + fk['rtab'] + "(" + fk['rcol'] + ")")
except:
pass
else:
print "Only MySQL and Postgres supported so far"
return -1
for idx in self.indexes[self.backend]:
if idx['drop'] == 1:
if self.backend == self.MYSQL_INNODB:
print "creating mysql index ", idx['tab'], idx['col']
try:
self.cursor.execute( "alter table %s add index %s(%s)"
, (idx['tab'],idx['col'],idx['col']) )
except:
pass
elif self.backend == self.PGSQL:
# pass
# mod to use tab_col for index name?
print "creating pg index ", idx['tab'], idx['col']
try:
print "create index %s_%s_idx on %s(%s)" % (idx['tab'], idx['col'], idx['tab'], idx['col'])
self.cursor.execute( "create index %s_%s_idx on %s(%s)"
% (idx['tab'], idx['col'], idx['tab'], idx['col']) )
except:
print " ERROR! :-("
pass
else:
print "Only MySQL and Postgres supported so far"
return -1
if self.backend == self.PGSQL:
self.db.set_isolation_level(1) # go back to normal isolation level
self.db.commit() # seems to clear up errors if there were any in postgres
atime = time() - stime
print "after import took", atime, "seconds"
#end def afterBulkImport
def createAllIndexes(self):
"""Create new indexes"""
if self.backend == self.PGSQL:
self.db.set_isolation_level(0) # allow table/index operations to work
for idx in self.indexes[self.backend]:
if self.backend == self.MYSQL_INNODB:
print "creating mysql index ", idx['tab'], idx['col']
try:
self.cursor.execute( "alter table %s add index %s(%s)"
, (idx['tab'],idx['col'],idx['col']) )
except:
pass
elif self.backend == self.PGSQL:
# mod to use tab_col for index name?
print "creating pg index ", idx['tab'], idx['col']
try:
print "create index %s_%s_idx on %s(%s)" % (idx['tab'], idx['col'], idx['tab'], idx['col'])
self.cursor.execute( "create index %s_%s_idx on %s(%s)"
% (idx['tab'], idx['col'], idx['tab'], idx['col']) )
except:
print " ERROR! :-("
pass
else:
print "Only MySQL and Postgres supported so far"
return -1
if self.backend == self.PGSQL:
self.db.set_isolation_level(1) # go back to normal isolation level
#end def createAllIndexes
def dropAllIndexes(self):
"""Drop all standalone indexes (i.e. not including primary keys or foreign keys)
using list of indexes in indexes data structure"""
# maybe upgrade to use data dictionary?? (but take care to exclude PK and FK)
if self.backend == self.PGSQL:
self.db.set_isolation_level(0) # allow table/index operations to work
for idx in self.indexes[self.backend]:
if self.backend == self.MYSQL_INNODB:
print "dropping mysql index ", idx['tab'], idx['col']
try:
self.cursor.execute( "alter table %s drop index %s"
, (idx['tab'],idx['col']) )
except:
pass
elif self.backend == self.PGSQL:
print "dropping pg index ", idx['tab'], idx['col']
# mod to use tab_col for index name?
try:
self.cursor.execute( "drop index %s_%s_idx"
% (idx['tab'],idx['col']) )
except:
pass
else:
print "Only MySQL and Postgres supported so far"
return -1
if self.backend == self.PGSQL:
self.db.set_isolation_level(1) # go back to normal isolation level
#end def dropAllIndexes
def analyzeDB(self):
"""Do whatever the DB can offer to update index/table statistics"""
stime = time()
if self.backend == self.PGSQL:
self.db.set_isolation_level(0) # allow vacuum to work
try:
self.cursor.execute("vacuum analyze")
except:
print "Error during vacuum"
self.db.set_isolation_level(1) # go back to normal isolation level
self.db.commit()
atime = time() - stime
print "analyze took", atime, "seconds"
#end def analyzeDB
# Currently uses an exclusive lock on the Players table as a global lock
# ( Changed because Hands is used in Database.init() )
# Return values are Unix style, 0 for success, positive integers for errors
# 1 = generic error
# 2 = players table does not exist (error message is suppressed)
def get_global_lock(self):
if self.backend == self.MYSQL_INNODB:
try:
self.cursor.execute( "lock tables Players write" )
except:
# Table 'fpdb.players' doesn't exist
if str(sys.exc_value).find(".Players' doesn't exist") >= 0:
return(2)
print "Error! failed to obtain global lock. Close all programs accessing " \
+ "database (including fpdb) and try again (%s)." \
% ( str(sys.exc_value).rstrip('\n'), )
return(1)
elif self.backend == self.PGSQL:
try:
self.db.commit()
self.cursor.execute( "lock table Players in exclusive mode nowait" )
#print "... after lock table, status =", self.cursor.statusmessage
except:
# relation "players" does not exist
if str(sys.exc_value).find('does not exist') >= 0:
return(2)
print "Error! failed to obtain global lock. Close all programs accessing " \
+ "database (including fpdb) and try again (%s)." \
% ( str(sys.exc_value).rstrip('\n'), )
return(1)
return(0)
def getLastInsertId(self): def getLastInsertId(self):
if self.backend == self.MYSQL_INNODB: try:
ret = self.db.insert_id() if self.backend == self.MYSQL_INNODB:
if ret < 1 or ret > 999999999: ret = self.db.insert_id()
print "getLastInsertId(): problem fetching insert_id? ret=", ret if ret < 1 or ret > 999999999:
ret = -1 print "getLastInsertId(): problem fetching insert_id? ret=", ret
elif self.backend == self.PGSQL: ret = -1
# some options: elif self.backend == self.PGSQL:
# currval(hands_id_seq) - use name of implicit seq here # some options:
# lastval() - still needs sequences set up? # currval(hands_id_seq) - use name of implicit seq here
# insert ... returning is useful syntax (but postgres specific?) # lastval() - still needs sequences set up?
# see rules (fancy trigger type things) # insert ... returning is useful syntax (but postgres specific?)
self.cursor.execute ("SELECT lastval()") # see rules (fancy trigger type things)
row = self.cursor.fetchone() c = self.db.cursor()
if not row: ret = c.execute ("SELECT lastval()")
print "getLastInsertId(%s): problem fetching lastval? row=" % seq, row row = c.fetchone()
if not row:
print "getLastInsertId(%s): problem fetching lastval? row=" % seq, row
ret = -1
else:
ret = row[0]
elif self.backend == fpdb_db.SQLITE:
# don't know how to do this in sqlite
print "getLastInsertId(): not coded for sqlite yet"
ret = -1 ret = -1
else: else:
ret = row[0] print "getLastInsertId(): unknown backend ", self.backend
elif self.backend == self.SQLITE: ret = -1
# don't know how to do this in sqlite except:
print "getLastInsertId(): not coded for sqlite yet"
ret = -1
else:
print "getLastInsertId(): unknown backend ", self.backend
ret = -1 ret = -1
print "getLastInsertId error:", str(sys.exc_value), " ret =", ret
raise fpdb_simple.FpdbError( "getLastInsertId error: " + str(sys.exc_value) )
return ret return ret
def storeHand(self, p): def storeHand(self, p):
#stores into table hands: #stores into table hands:
self.cursor.execute ("""INSERT INTO Hands self.cursor.execute ("""INSERT INTO Hands (
(siteHandNo, gametypeId, handStart, seats, tableName, importTime, maxSeats tablename,
,boardcard1, boardcard2, boardcard3, boardcard4, boardcard5 sitehandno,
,playersVpi, playersAtStreet1, playersAtStreet2 gametypeid,
,playersAtStreet3, playersAtStreet4, playersAtShowdown handstart,
,street0Raises, street1Raises, street2Raises importtime,
,street3Raises, street4Raises, street1Pot seats,
,street2Pot, street3Pot, street4Pot maxseats,
,showdownPot boardcard1,
boardcard2,
boardcard3,
boardcard4,
boardcard5,
-- texture,
playersVpi,
playersAtStreet1,
playersAtStreet2,
playersAtStreet3,
playersAtStreet4,
playersAtShowdown,
street0Raises,
street1Raises,
street2Raises,
street3Raises,
street4Raises,
-- street1Pot,
-- street2Pot,
-- street3Pot,
-- street4Pot,
-- showdownPot
) )
VALUES VALUES
(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""" (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
,(p['siteHandNo'], gametype_id, p['handStart'], len(names), p['tableName'], datetime.datetime.today(), p['maxSeats'] %s, %s, %s, %s, %s, %s, %s)""",
,p['boardcard1'], ['boardcard2'], p['boardcard3'], ['boardcard4'], ['boardcard5'] (
,hudCache['playersVpi'], hudCache['playersAtStreet1'], hudCache['playersAtStreet2'] p['tablename'],
,hudCache['playersAtStreet3'], hudCache['playersAtStreet4'], hudCache['playersAtShowdown'] p['sitehandno'],
,hudCache['street0Raises'], hudCache['street1Raises'], hudCache['street2Raises'] p['gametypeid'],
,hudCache['street3Raises'], hudCache['street4Raises'], hudCache['street1Pot'] p['handStart'],
,hudCache['street2Pot'], hudCache['street3Pot'], hudCache['street4Pot'] datetime.datetime.today(),
,hudCache['showdownPot'] len(p['names']),
) p['maxSeats'],
) p['boardcard1'],
p['boardcard2'],
p['boardcard3'],
p['boardcard4'],
p['boardcard5'],
hudCache['playersVpi'],
hudCache['playersAtStreet1'],
hudCache['playersAtStreet2'],
hudCache['playersAtStreet3'],
hudCache['playersAtStreet4'],
hudCache['playersAtShowdown'],
hudCache['street0Raises'],
hudCache['street1Raises'],
hudCache['street2Raises'],
hudCache['street3Raises'],
hudCache['street4Raises'],
hudCache['street1Pot'],
hudCache['street2Pot'],
hudCache['street3Pot'],
hudCache['street4Pot'],
hudCache['showdownPot']
)
)
#return getLastInsertId(backend, conn, cursor) #return getLastInsertId(backend, conn, cursor)
#end class fpdb_db #end class fpdb_db

View File

@ -22,6 +22,7 @@
import os # todo: remove this once import_dir is in fpdb_import import os # todo: remove this once import_dir is in fpdb_import
import sys import sys
from time import time, strftime from time import time, strftime
import logging
import traceback import traceback
import math import math
import datetime import datetime
@ -53,14 +54,14 @@ except:
class Importer: class Importer:
def __init__(self, caller, settings, config): def __init__(self, caller, settings, config, sql = None):
"""Constructor""" """Constructor"""
self.settings = settings self.settings = settings
self.caller = caller self.caller = caller
self.config = config self.config = config
self.sql = sql
self.database = None # database will be the main db interface eventually self.database = None # database will be the main db interface eventually
self.fdb = None # fdb may disappear or just hold the simple db connection
self.cursor = None
self.filelist = {} self.filelist = {}
self.dirlist = {} self.dirlist = {}
self.siteIds = {} self.siteIds = {}
@ -77,10 +78,10 @@ class Importer:
self.settings.setdefault("minPrint", 30) self.settings.setdefault("minPrint", 30)
self.settings.setdefault("handCount", 0) self.settings.setdefault("handCount", 0)
self.database = Database.Database(self.config) # includes .connection and .sql variables self.database = Database.Database(self.config, sql = self.sql) # includes .connection and .sql variables
self.fdb = fpdb_db.fpdb_db() # sets self.fdb.db self.fdb.cursor and self.fdb.sql
self.fdb.do_connect(self.config) self.NEWIMPORT = False
self.fdb.db.rollback() self.allow_hudcache_rebuild = False
#Set functions #Set functions
def setCallHud(self, value): def setCallHud(self, value):
@ -119,8 +120,7 @@ class Importer:
self.filelist[filename] = [site] + [filter] self.filelist[filename] = [site] + [filter]
if site not in self.siteIds: if site not in self.siteIds:
# Get id from Sites table in DB # Get id from Sites table in DB
self.fdb.cursor.execute(self.fdb.sql.query['getSiteId'], (site,)) result = self.database.get_site_id(site)
result = self.fdb.cursor.fetchall()
if len(result) == 1: if len(result) == 1:
self.siteIds[site] = result[0][0] self.siteIds[site] = result[0][0]
else: else:
@ -165,13 +165,19 @@ class Importer:
def runImport(self): def runImport(self):
""""Run full import on self.filelist.""" """"Run full import on self.filelist."""
start = datetime.datetime.now() start = datetime.datetime.now()
print "started at", start, "--", len(self.filelist), "files to import.", self.settings['dropIndexes'] print "Started at", start, "--", len(self.filelist), "files to import.", self.settings['dropIndexes']
if self.settings['dropIndexes'] == 'auto': if self.settings['dropIndexes'] == 'auto':
self.settings['dropIndexes'] = self.calculate_auto2(10.0, 500.0) self.settings['dropIndexes'] = self.calculate_auto2(12.0, 500.0)
if self.allow_hudcache_rebuild:
self.settings['dropHudCache'] = self.calculate_auto2(25.0, 500.0) # returns "drop"/"don't drop"
if self.settings['dropIndexes'] == 'drop': if self.settings['dropIndexes'] == 'drop':
self.fdb.prepareBulkImport() self.database.prepareBulkImport()
#self.settings['updateHudCache'] = self.calculate_auto2(10.0, 500.0) else:
print "No need to drop indexes."
#print "dropInd =", self.settings['dropIndexes'], " dropHudCache =", self.settings['dropHudCache']
totstored = 0 totstored = 0
totdups = 0 totdups = 0
totpartial = 0 totpartial = 0
@ -186,8 +192,14 @@ class Importer:
toterrors += errors toterrors += errors
tottime += ttime tottime += ttime
if self.settings['dropIndexes'] == 'drop': if self.settings['dropIndexes'] == 'drop':
self.fdb.afterBulkImport() self.database.afterBulkImport()
self.fdb.analyzeDB() else:
print "No need to rebuild indexes."
if self.allow_hudcache_rebuild and self.settings['dropHudCache'] == 'drop':
self.database.rebuild_hudcache()
else:
print "No need to rebuild hudcache."
self.database.analyzeDB()
return (totstored, totdups, totpartial, toterrors, tottime) return (totstored, totdups, totpartial, toterrors, tottime)
# else: import threaded # else: import threaded
@ -196,7 +208,7 @@ class Importer:
if len(self.filelist) == 1: return "don't drop" if len(self.filelist) == 1: return "don't drop"
if 'handsInDB' not in self.settings: if 'handsInDB' not in self.settings:
try: try:
tmpcursor = self.fdb.db.cursor() tmpcursor = self.database.get_cursor()
tmpcursor.execute("Select count(1) from Hands;") tmpcursor.execute("Select count(1) from Hands;")
self.settings['handsInDB'] = tmpcursor.fetchone()[0] self.settings['handsInDB'] = tmpcursor.fetchone()[0]
except: except:
@ -219,7 +231,7 @@ class Importer:
# get number of hands in db # get number of hands in db
if 'handsInDB' not in self.settings: if 'handsInDB' not in self.settings:
try: try:
tmpcursor = self.fdb.db.cursor() tmpcursor = self.database.get_cursor()
tmpcursor.execute("Select count(1) from Hands;") tmpcursor.execute("Select count(1) from Hands;")
self.settings['handsInDB'] = tmpcursor.fetchone()[0] self.settings['handsInDB'] = tmpcursor.fetchone()[0]
except: except:
@ -234,11 +246,12 @@ class Importer:
# if hands_in_db is zero or very low, we want to drop indexes, otherwise compare # if hands_in_db is zero or very low, we want to drop indexes, otherwise compare
# import size with db size somehow: # import size with db size somehow:
#print "auto2: handsindb =", self.settings['handsInDB'], "total_size =", total_size, "size_per_hand =", \ ret = "don't drop"
# size_per_hand, "inc =", increment
if self.settings['handsInDB'] < scale * (total_size/size_per_hand) + increment: if self.settings['handsInDB'] < scale * (total_size/size_per_hand) + increment:
return "drop" ret = "drop"
return "don't drop" #print "auto2: handsindb =", self.settings['handsInDB'], "total_size =", total_size, "size_per_hand =", \
# size_per_hand, "inc =", increment, "return:", ret
return ret
#Run import on updated files, then store latest update time. #Run import on updated files, then store latest update time.
def runUpdated(self): def runUpdated(self):
@ -282,12 +295,13 @@ class Importer:
self.addToDirList = {} self.addToDirList = {}
self.removeFromFileList = {} self.removeFromFileList = {}
self.fdb.db.rollback() self.database.rollback()
#rulog.writelines(" finished\n") #rulog.writelines(" finished\n")
#rulog.close() #rulog.close()
# 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):
#print "import_file_dict"
if os.path.isdir(file): if os.path.isdir(file):
self.addToDirList[file] = [site] + [filter] self.addToDirList[file] = [site] + [filter]
return return
@ -309,9 +323,17 @@ class Importer:
mod = __import__(filter) mod = __import__(filter)
obj = getattr(mod, filter_name, None) obj = getattr(mod, filter_name, None)
if callable(obj): if callable(obj):
conv = obj(in_path = file, out_path = out_path) conv = obj(in_path = file, out_path = out_path, index = 0) # Index into file 0 until changeover
if(conv.getStatus()): if(conv.getStatus() and self.NEWIMPORT == False):
(stored, duplicates, partial, errors, ttime) = self.import_fpdb_file(out_path, site) (stored, duplicates, partial, errors, ttime) = self.import_fpdb_file(out_path, site)
elif (conv.getStatus() and self.NEWIMPORT == True):
#This code doesn't do anything yet
handlist = hhc.getProcessedHands()
self.pos_in_file[file] = hhc.getLastCharacterRead()
for hand in handlist:
hand.prepInsert()
hand.insert()
else: else:
# conversion didn't work # conversion didn't work
# TODO: appropriate response? # TODO: appropriate response?
@ -325,6 +347,7 @@ class Importer:
def import_fpdb_file(self, file, site): def import_fpdb_file(self, file, site):
#print "import_fpdb_file"
starttime = time() starttime = time()
last_read_hand = 0 last_read_hand = 0
loc = 0 loc = 0
@ -354,6 +377,8 @@ class Importer:
self.pos_in_file[file] = inputFile.tell() self.pos_in_file[file] = inputFile.tell()
inputFile.close() inputFile.close()
#self.database.lock_for_insert() # should be ok when using one thread
try: # sometimes we seem to be getting an empty self.lines, in which case, we just want to return. try: # sometimes we seem to be getting an empty self.lines, in which case, we just want to return.
firstline = self.lines[0] firstline = self.lines[0]
except: except:
@ -395,10 +420,10 @@ class Importer:
self.hand=hand self.hand=hand
try: try:
handsId = fpdb_parse_logic.mainParser( self.settings, self.fdb handsId = fpdb_parse_logic.mainParser( self.settings
, self.siteIds[site], category, hand , self.siteIds[site], category, hand
, self.config, self.database ) , self.config, self.database )
self.fdb.db.commit() self.database.commit()
stored += 1 stored += 1
if self.callHud: if self.callHud:
@ -408,23 +433,23 @@ class Importer:
self.caller.pipe_to_hud.stdin.write("%s" % (handsId) + os.linesep) self.caller.pipe_to_hud.stdin.write("%s" % (handsId) + os.linesep)
except fpdb_simple.DuplicateError: except fpdb_simple.DuplicateError:
duplicates += 1 duplicates += 1
self.fdb.db.rollback() self.database.rollback()
except (ValueError), fe: except (ValueError), fe:
errors += 1 errors += 1
self.printEmailErrorMessage(errors, file, hand) self.printEmailErrorMessage(errors, file, hand)
if (self.settings['failOnError']): if (self.settings['failOnError']):
self.fdb.db.commit() #dont remove this, in case hand processing was cancelled. self.database.commit() #dont remove this, in case hand processing was cancelled.
raise raise
else: else:
self.fdb.db.rollback() self.database.rollback()
except (fpdb_simple.FpdbError), fe: except (fpdb_simple.FpdbError), fe:
errors += 1 errors += 1
self.printEmailErrorMessage(errors, file, hand) self.printEmailErrorMessage(errors, file, hand)
self.fdb.db.rollback() self.database.rollback()
if self.settings['failOnError']: if self.settings['failOnError']:
self.fdb.db.commit() #dont remove this, in case hand processing was cancelled. self.database.commit() #dont remove this, in case hand processing was cancelled.
raise raise
if self.settings['minPrint']: if self.settings['minPrint']:
@ -451,7 +476,7 @@ class Importer:
print "failed to read a single hand from file:", inputFile print "failed to read a single hand from file:", inputFile
handsId=0 handsId=0
#todo: this will cause return of an unstored hand number if the last hand was error #todo: this will cause return of an unstored hand number if the last hand was error
self.fdb.db.commit() self.database.commit()
self.handsId=handsId self.handsId=handsId
return (stored, duplicates, partial, errors, ttime) return (stored, duplicates, partial, errors, ttime)

View File

@ -15,19 +15,25 @@
#In the "official" distribution you can find the license in #In the "official" distribution you can find the license in
#agpl-3.0.txt in the docs folder of the package. #agpl-3.0.txt in the docs folder of the package.
#methods that are specific to holdem but not trivial #parses an in-memory fpdb hand history and calls db routine to store it
import sys
import fpdb_simple import fpdb_simple
import Database import Database
from time import time, strftime
#parses a holdem hand #parses a holdem hand
def mainParser(settings, fdb, siteID, category, hand, config, db = None): def mainParser(settings, siteID, category, hand, config, db = None):
#print "mainparser"
# fdb is not used now - to be removed ...
t0 = time()
#print "mainparser"
backend = settings['db-backend'] backend = settings['db-backend']
if db == None: if db == None:
#This is redundant - hopefully fdb will be a Database object in an iteration soon
db = Database.Database(c = config, sql = None) db = Database.Database(c = config, sql = None)
else:
db = db
category = fpdb_simple.recogniseCategory(hand[0]) category = fpdb_simple.recogniseCategory(hand[0])
base = "hold" if category == "holdem" or category == "omahahi" or category == "omahahilo" else "stud" base = "hold" if category == "holdem" or category == "omahahi" or category == "omahahilo" else "stud"
@ -50,9 +56,8 @@ def mainParser(settings, fdb, siteID, category, hand, config, db = None):
if line[-2:] == "$0": continue if line[-2:] == "$0": continue
smallBlindLine = i smallBlindLine = i
break break
#print "small blind line:",smallBlindLine
gametypeID = fpdb_simple.recogniseGametypeID(backend, fdb.db, fdb.cursor, hand[0], hand[smallBlindLine], siteID, category, isTourney) gametypeID = fpdb_simple.recogniseGametypeID(backend, db, db.get_cursor(), hand[0], hand[smallBlindLine], siteID, category, isTourney)
if isTourney: if isTourney:
siteTourneyNo = fpdb_simple.parseTourneyNo(hand[0]) siteTourneyNo = fpdb_simple.parseTourneyNo(hand[0])
buyin = fpdb_simple.parseBuyin(hand[0]) buyin = fpdb_simple.parseBuyin(hand[0])
@ -63,9 +68,20 @@ def mainParser(settings, fdb, siteID, category, hand, config, db = None):
tourneyStartTime= handStartTime #todo: read tourney start time tourneyStartTime= handStartTime #todo: read tourney start time
rebuyOrAddon = fpdb_simple.isRebuyOrAddon(hand[0]) rebuyOrAddon = fpdb_simple.isRebuyOrAddon(hand[0])
tourneyTypeId = fpdb_simple.recogniseTourneyTypeId(fdb.cursor, siteID, buyin, fee, knockout, rebuyOrAddon) tourneyTypeId = fpdb_simple.recogniseTourneyTypeId(db.get_cursor(), siteID, buyin, fee, knockout, rebuyOrAddon)
else:
siteTourneyNo = -1
buyin = -1
fee = -1
entries = -1
prizepool = -1
knockout = 0
tourneyStartTime= None
rebuyOrAddon = -1
fpdb_simple.isAlreadyInDB(fdb.cursor, gametypeID, siteHandNo) tourneyTypeId = 1
fpdb_simple.isAlreadyInDB(db.get_cursor(), gametypeID, siteHandNo)
hand = fpdb_simple.filterCrap(hand, isTourney) hand = fpdb_simple.filterCrap(hand, isTourney)
@ -79,7 +95,7 @@ def mainParser(settings, fdb, siteID, category, hand, config, db = None):
seatLines.append(line) seatLines.append(line)
names = fpdb_simple.parseNames(seatLines) names = fpdb_simple.parseNames(seatLines)
playerIDs = fpdb_simple.recognisePlayerIDs(fdb.cursor, names, siteID) playerIDs = fpdb_simple.recognisePlayerIDs(db.get_cursor(), names, siteID) # inserts players as needed
tmp = fpdb_simple.parseCashesAndSeatNos(seatLines) tmp = fpdb_simple.parseCashesAndSeatNos(seatLines)
startCashes = tmp['startCashes'] startCashes = tmp['startCashes']
seatNos = tmp['seatNos'] seatNos = tmp['seatNos']
@ -126,8 +142,9 @@ def mainParser(settings, fdb, siteID, category, hand, config, db = None):
fpdb_simple.convertBlindBet(actionTypes, actionAmounts) fpdb_simple.convertBlindBet(actionTypes, actionAmounts)
fpdb_simple.checkPositions(positions) fpdb_simple.checkPositions(positions)
fdb.cursor.execute("SELECT limitType FROM Gametypes WHERE id=%s",(gametypeID, )) c = db.get_cursor()
limit_type = fdb.cursor.fetchone()[0] c.execute("SELECT limitType FROM Gametypes WHERE id=%s",(gametypeID, ))
limit_type = c.fetchone()[0]
fpdb_simple.convert3B4B(category, limit_type, actionTypes, actionAmounts) fpdb_simple.convert3B4B(category, limit_type, actionTypes, actionAmounts)
totalWinnings = sum(winnings) totalWinnings = sum(winnings)
@ -143,49 +160,27 @@ def mainParser(settings, fdb, siteID, category, hand, config, db = None):
, allIns, actionTypeByNo, winnings, totalWinnings, None , allIns, actionTypeByNo, winnings, totalWinnings, None
, actionTypes, actionAmounts, antes) , actionTypes, actionAmounts, antes)
if isTourney: #print "parse: hand data prepared" # only reads up to here apart from inserting new players
ranks = map(lambda x: 0, names) # create an array of 0's equal to the length of names try:
payin_amounts = fpdb_simple.calcPayin(len(names), buyin, fee) db.commit() # need to commit new players as different db connection used
# for other writes. maybe this will change maybe not ...
except:
print "parse: error during commit: " + str(sys.exc_value)
if base == "hold":
result = db.tourney_holdem_omaha( # save data structures in a HandToWrite instance and then insert into database:
config, settings, fdb.db, fdb.cursor, base, category, siteTourneyNo, buyin htw = Database.HandToWrite()
, fee, knockout, entries, prizepool, tourneyStartTime htw.set_all( config, settings, base, category, siteTourneyNo, buyin
, payin_amounts, ranks, tourneyTypeId, siteID, siteHandNo , fee, knockout, entries, prizepool, tourneyStartTime
, gametypeID, handStartTime, names, playerIDs, startCashes , isTourney, tourneyTypeId, siteID, siteHandNo
, positions, cardValues, cardSuits, boardValues, boardSuits , gametypeID, handStartTime, names, playerIDs, startCashes
, winnings, rakes, actionTypes, allIns, actionAmounts , positions, antes, cardValues, cardSuits, boardValues, boardSuits
, actionNos, hudImportData, maxSeats, tableName, seatNos) , winnings, rakes, actionTypes, allIns, actionAmounts
elif base == "stud": , actionNos, hudImportData, maxSeats, tableName, seatNos)
result = db.tourney_stud( result = db.store_the_hand(htw)
config, settings, fdb.db, fdb.cursor, base, category, siteTourneyNo
, buyin, fee, knockout, entries, prizepool, tourneyStartTime t9 = time()
, payin_amounts, ranks, tourneyTypeId, siteID, siteHandNo #print "parse and save=(%4.3f)" % (t9-t0)
, gametypeID, handStartTime, names, playerIDs, startCashes
, antes, cardValues, cardSuits, winnings, rakes, actionTypes
, allIns, actionAmounts, actionNos, hudImportData, maxSeats
, tableName, seatNos)
else:
raise fpdb_simple.FpdbError("unrecognised category")
else:
if base == "hold":
result = db.ring_holdem_omaha(
config, settings, fdb.db, fdb.cursor, base, category, siteHandNo
, gametypeID, handStartTime, names, playerIDs
, startCashes, positions, cardValues, cardSuits
, boardValues, boardSuits, winnings, rakes
, actionTypes, allIns, actionAmounts, actionNos
, hudImportData, maxSeats, tableName, seatNos)
elif base == "stud":
result = db.ring_stud(
config, settings, fdb.db, fdb.cursor, base, category, siteHandNo, gametypeID
, handStartTime, names, playerIDs, startCashes, antes
, cardValues, cardSuits, winnings, rakes, actionTypes, allIns
, actionAmounts, actionNos, hudImportData, maxSeats, tableName
, seatNos)
else:
raise fpdb_simple.FpdbError ("unrecognised category")
fdb.db.commit()
return result return result
#end def mainParser #end def mainParser

View File

@ -37,9 +37,6 @@ MYSQL_INNODB = 2
PGSQL = 3 PGSQL = 3
SQLITE = 4 SQLITE = 4
# config while trying out new hudcache mechanism
use_date_in_hudcache = True
class DuplicateError(Exception): class DuplicateError(Exception):
def __init__(self, value): def __init__(self, value):
self.value = value self.value = value
@ -986,7 +983,7 @@ def recogniseGametypeID(backend, db, cursor, topline, smallBlindLine, site_id, c
#AND limitType=%s AND smallBlind=%s AND bigBlind=%s", (site_id, type, category, limit_type, small_bet, big_bet)) #AND limitType=%s AND smallBlind=%s AND bigBlind=%s", (site_id, type, category, limit_type, small_bet, big_bet))
#result=(db.insert_id(),) #result=(db.insert_id(),)
result=(getLastInsertId(backend,db,cursor),) result=(db.get_last_insert_id(),)
return result[0] return result[0]
#end def recogniseGametypeID #end def recogniseGametypeID
@ -1122,282 +1119,6 @@ def splitRake(winnings, rakes, totalRake):
rakes[i]=totalRake*winPortion rakes[i]=totalRake*winPortion
#end def splitRake #end def splitRake
def storeActions(cursor, handsPlayersIds, actionTypes, allIns, actionAmounts, actionNos):
#stores into table hands_actions
#print "start of storeActions, actionNos:",actionNos
#print " action_amounts:",action_amounts
inserts = []
for i in xrange(len(actionTypes)): #iterate through streets
for j in xrange(len(actionTypes[i])): #iterate through names
for k in xrange(len(actionTypes[i][j])): #iterate through individual actions of that player on that street
# Add inserts into a list and let
inserts = inserts + [(handsPlayersIds[j], i, actionNos[i][j][k], actionTypes[i][j][k], allIns[i][j][k], actionAmounts[i][j][k])]
cursor.executemany("INSERT INTO HandsActions (handsPlayerId, street, actionNo, action, allIn, amount) VALUES (%s, %s, %s, %s, %s, %s)", inserts)
#end def storeActions
def storeHands(backend, conn, cursor, site_hand_no, gametype_id
,hand_start_time, names, tableName, maxSeats, hudCache,
board_values, board_suits):
cards = [Card.cardFromValueSuit(v,s) for v,s in zip(board_values,board_suits)]
#stores into table hands:
cursor.execute ("""INSERT INTO Hands
(siteHandNo, gametypeId, handStart, seats, tableName, importTime, maxSeats
,boardcard1,boardcard2,boardcard3,boardcard4,boardcard5
,playersVpi, playersAtStreet1, playersAtStreet2
,playersAtStreet3, playersAtStreet4, playersAtShowdown
,street0Raises, street1Raises, street2Raises
,street3Raises, street4Raises, street1Pot
,street2Pot, street3Pot, street4Pot
,showdownPot
)
VALUES
(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
"""
, (site_hand_no, gametype_id, hand_start_time, len(names), tableName, datetime.datetime.today(), maxSeats
,cards[0], cards[1], cards[2], cards[3], cards[4]
,hudCache['playersVpi'], hudCache['playersAtStreet1'], hudCache['playersAtStreet2']
,hudCache['playersAtStreet3'], hudCache['playersAtStreet4'], hudCache['playersAtShowdown']
,hudCache['street0Raises'], hudCache['street1Raises'], hudCache['street2Raises']
,hudCache['street3Raises'], hudCache['street4Raises'], hudCache['street1Pot']
,hudCache['street2Pot'], hudCache['street3Pot'], hudCache['street4Pot']
,hudCache['showdownPot']
))
return getLastInsertId(backend, conn, cursor)
#end def storeHands
def store_hands_players_holdem_omaha(backend, conn, cursor, category, hands_id, player_ids, start_cashes
,positions, card_values, card_suits, winnings, rakes, seatNos, hudCache):
result=[]
# postgres (and others?) needs the booleans converted to ints before saving:
# (or we could just save them as boolean ... but then we can't sum them so easily in sql ???)
# NO - storing booleans for now so don't need this
#hudCacheInt = {}
#for k,v in hudCache.iteritems():
# if k in ('wonWhenSeenStreet1', 'wonAtSD', 'totalProfit'):
# hudCacheInt[k] = v
# else:
# hudCacheInt[k] = map(lambda x: 1 if x else 0, v)
if (category=="holdem"):
for i in xrange(len(player_ids)):
startCards = Card.twoStartCards(card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1])
card1 = Card.cardFromValueSuit(card_values[i][0], card_suits[i][0])
card2 = Card.cardFromValueSuit(card_values[i][1], card_suits[i][1])
cursor.execute ("""
INSERT INTO HandsPlayers
(handId, playerId, startCash, position, tourneyTypeId,
card1, card2, startCards, winnings, rake, seatNo, totalProfit,
street0VPI, street0Aggr, street0_3BChance, street0_3BDone,
street1Seen, street2Seen, street3Seen, street4Seen, sawShowdown,
street1Aggr, street2Aggr, street3Aggr, street4Aggr,
otherRaisedStreet1, otherRaisedStreet2, otherRaisedStreet3, otherRaisedStreet4,
foldToOtherRaisedStreet1, foldToOtherRaisedStreet2, foldToOtherRaisedStreet3, foldToOtherRaisedStreet4,
wonWhenSeenStreet1, wonAtSD,
stealAttemptChance, stealAttempted, foldBbToStealChance, foldedBbToSteal, foldSbToStealChance, foldedSbToSteal,
street1CBChance, street1CBDone, street2CBChance, street2CBDone,
street3CBChance, street3CBDone, street4CBChance, street4CBDone,
foldToStreet1CBChance, foldToStreet1CBDone, foldToStreet2CBChance, foldToStreet2CBDone,
foldToStreet3CBChance, foldToStreet3CBDone, foldToStreet4CBChance, foldToStreet4CBDone,
street1CheckCallRaiseChance, street1CheckCallRaiseDone, street2CheckCallRaiseChance, street2CheckCallRaiseDone,
street3CheckCallRaiseChance, street3CheckCallRaiseDone, street4CheckCallRaiseChance, street4CheckCallRaiseDone,
street0Calls, street1Calls, street2Calls, street3Calls, street4Calls,
street0Bets, street1Bets, street2Bets, street3Bets, street4Bets
)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
%s, %s, %s, %s, %s, %s, %s, %s)""",
(hands_id, player_ids[i], start_cashes[i], positions[i], 1, # tourneytypeid
card1, card2, startCards,
winnings[i], rakes[i], seatNos[i], hudCache['totalProfit'][i],
hudCache['street0VPI'][i], hudCache['street0Aggr'][i],
hudCache['street0_3BChance'][i], hudCache['street0_3BDone'][i],
hudCache['street1Seen'][i], hudCache['street2Seen'][i], hudCache['street3Seen'][i],
hudCache['street4Seen'][i], hudCache['sawShowdown'][i],
hudCache['street1Aggr'][i], hudCache['street2Aggr'][i], hudCache['street3Aggr'][i], hudCache['street4Aggr'][i],
hudCache['otherRaisedStreet1'][i], hudCache['otherRaisedStreet2'][i],
hudCache['otherRaisedStreet3'][i], hudCache['otherRaisedStreet4'][i],
hudCache['foldToOtherRaisedStreet1'][i], hudCache['foldToOtherRaisedStreet2'][i],
hudCache['foldToOtherRaisedStreet3'][i], hudCache['foldToOtherRaisedStreet4'][i],
hudCache['wonWhenSeenStreet1'][i], hudCache['wonAtSD'][i],
hudCache['stealAttemptChance'][i], hudCache['stealAttempted'][i], hudCache['foldBbToStealChance'][i],
hudCache['foldedBbToSteal'][i], hudCache['foldSbToStealChance'][i], hudCache['foldedSbToSteal'][i],
hudCache['street1CBChance'][i], hudCache['street1CBDone'][i], hudCache['street2CBChance'][i], hudCache['street2CBDone'][i],
hudCache['street3CBChance'][i], hudCache['street3CBDone'][i], hudCache['street4CBChance'][i], hudCache['street4CBDone'][i],
hudCache['foldToStreet1CBChance'][i], hudCache['foldToStreet1CBDone'][i],
hudCache['foldToStreet2CBChance'][i], hudCache['foldToStreet2CBDone'][i],
hudCache['foldToStreet3CBChance'][i], hudCache['foldToStreet3CBDone'][i],
hudCache['foldToStreet4CBChance'][i], hudCache['foldToStreet4CBDone'][i],
hudCache['street1CheckCallRaiseChance'][i], hudCache['street1CheckCallRaiseDone'][i],
hudCache['street2CheckCallRaiseChance'][i], hudCache['street2CheckCallRaiseDone'][i],
hudCache['street3CheckCallRaiseChance'][i], hudCache['street3CheckCallRaiseDone'][i],
hudCache['street4CheckCallRaiseChance'][i], hudCache['street4CheckCallRaiseDone'][i],
hudCache['street0Calls'][i], hudCache['street1Calls'][i], hudCache['street2Calls'][i], hudCache['street3Calls'][i], hudCache['street4Calls'][i],
hudCache['street0Bets'][i], hudCache['street1Bets'][i], hudCache['street2Bets'][i], hudCache['street3Bets'][i], hudCache['street4Bets'][i]
) )
#cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId=%s", (hands_id, player_ids[i]))
#result.append(cursor.fetchall()[0][0])
result.append( getLastInsertId(backend, conn, cursor) )
elif (category=="omahahi" or category=="omahahilo"):
for i in xrange(len(player_ids)):
startCards = Card.fourStartCards(card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1], card_values[i][2], card_suits[i][2], card_values[i][3], card_suits[i][3])
card1 = Card.cardFromValueSuit(card_values[i][0], card_suits[i][0])
card2 = Card.cardFromValueSuit(card_values[i][1], card_suits[i][1])
card3 = Card.cardFromValueSuit(card_values[i][2], card_suits[i][2])
card4 = Card.cardFromValueSuit(card_values[i][3], card_suits[i][3])
cursor.execute ("""INSERT INTO HandsPlayers
(handId, playerId, startCash, position, tourneyTypeId,
card1, card2, card3, card4, winnings, rake, seatNo, totalProfit,
street0VPI, street0Aggr, street0_3BChance, street0_3BDone,
street1Seen, street2Seen, street3Seen, street4Seen, sawShowdown,
street1Aggr, street2Aggr, street3Aggr, street4Aggr,
otherRaisedStreet1, otherRaisedStreet2, otherRaisedStreet3, otherRaisedStreet4,
foldToOtherRaisedStreet1, foldToOtherRaisedStreet2, foldToOtherRaisedStreet3, foldToOtherRaisedStreet4,
wonWhenSeenStreet1, wonAtSD,
stealAttemptChance, stealAttempted, foldBbToStealChance, foldedBbToSteal, foldSbToStealChance, foldedSbToSteal,
street1CBChance, street1CBDone, street2CBChance, street2CBDone,
street3CBChance, street3CBDone, street4CBChance, street4CBDone,
foldToStreet1CBChance, foldToStreet1CBDone, foldToStreet2CBChance, foldToStreet2CBDone,
foldToStreet3CBChance, foldToStreet3CBDone, foldToStreet4CBChance, foldToStreet4CBDone,
street1CheckCallRaiseChance, street1CheckCallRaiseDone, street2CheckCallRaiseChance, street2CheckCallRaiseDone,
street3CheckCallRaiseChance, street3CheckCallRaiseDone, street4CheckCallRaiseChance, street4CheckCallRaiseDone,
street0Calls, street1Calls, street2Calls, street3Calls, street4Calls,
street0Bets, street1Bets, street2Bets, street3Bets, street4Bets
)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
%s, %s, %s, %s, %s, %s, %s, %s)""",
(hands_id, player_ids[i], start_cashes[i], positions[i], 1, # tourneytypeid
card1, card2, card3, card4,
winnings[i], rakes[i], seatNos[i], hudCache['totalProfit'][i],
hudCache['street0VPI'][i], hudCache['street0Aggr'][i],
hudCache['street0_3BChance'][i], hudCache['street0_3BDone'][i],
hudCache['street1Seen'][i], hudCache['street2Seen'][i], hudCache['street3Seen'][i],
hudCache['street4Seen'][i], hudCache['sawShowdown'][i],
hudCache['street1Aggr'][i], hudCache['street2Aggr'][i], hudCache['street3Aggr'][i], hudCache['street4Aggr'][i],
hudCache['otherRaisedStreet1'][i], hudCache['otherRaisedStreet2'][i],
hudCache['otherRaisedStreet3'][i], hudCache['otherRaisedStreet4'][i],
hudCache['foldToOtherRaisedStreet1'][i], hudCache['foldToOtherRaisedStreet2'][i],
hudCache['foldToOtherRaisedStreet3'][i], hudCache['foldToOtherRaisedStreet4'][i],
hudCache['wonWhenSeenStreet1'][i], hudCache['wonAtSD'][i],
hudCache['stealAttemptChance'][i], hudCache['stealAttempted'][i], hudCache['foldBbToStealChance'][i],
hudCache['foldedBbToSteal'][i], hudCache['foldSbToStealChance'][i], hudCache['foldedSbToSteal'][i],
hudCache['street1CBChance'][i], hudCache['street1CBDone'][i], hudCache['street2CBChance'][i], hudCache['street2CBDone'][i],
hudCache['street3CBChance'][i], hudCache['street3CBDone'][i], hudCache['street4CBChance'][i], hudCache['street4CBDone'][i],
hudCache['foldToStreet1CBChance'][i], hudCache['foldToStreet1CBDone'][i],
hudCache['foldToStreet2CBChance'][i], hudCache['foldToStreet2CBDone'][i],
hudCache['foldToStreet3CBChance'][i], hudCache['foldToStreet3CBDone'][i],
hudCache['foldToStreet4CBChance'][i], hudCache['foldToStreet4CBDone'][i],
hudCache['street1CheckCallRaiseChance'][i], hudCache['street1CheckCallRaiseDone'][i],
hudCache['street2CheckCallRaiseChance'][i], hudCache['street2CheckCallRaiseDone'][i],
hudCache['street3CheckCallRaiseChance'][i], hudCache['street3CheckCallRaiseDone'][i],
hudCache['street4CheckCallRaiseChance'][i], hudCache['street4CheckCallRaiseDone'][i],
hudCache['street0Calls'][i], hudCache['street1Calls'][i], hudCache['street2Calls'][i], hudCache['street3Calls'][i], hudCache['street4Calls'][i],
hudCache['street0Bets'][i], hudCache['street1Bets'][i], hudCache['street2Bets'][i], hudCache['street3Bets'][i], hudCache['street4Bets'][i]
) )
#cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i]))
#result.append(cursor.fetchall()[0][0])
result.append( getLastInsertId(backend, conn, cursor) )
else:
raise FpdbError("invalid category")
return result
#end def store_hands_players_holdem_omaha
def store_hands_players_stud(backend, conn, cursor, hands_id, player_ids, start_cashes, antes,
card_values, card_suits, winnings, rakes, seatNos):
#stores hands_players rows for stud/razz games. returns an array of the resulting IDs
result=[]
#print "before inserts in store_hands_players_stud, antes:", antes
for i in xrange(len(player_ids)):
card1 = Card.cardFromValueSuit(card_values[i][0], card_suits[i][0])
card2 = Card.cardFromValueSuit(card_values[i][1], card_suits[i][1])
card3 = Card.cardFromValueSuit(card_values[i][2], card_suits[i][2])
card4 = Card.cardFromValueSuit(card_values[i][3], card_suits[i][3])
card5 = Card.cardFromValueSuit(card_values[i][4], card_suits[i][4])
card6 = Card.cardFromValueSuit(card_values[i][5], card_suits[i][5])
card7 = Card.cardFromValueSuit(card_values[i][6], card_suits[i][6])
cursor.execute ("""INSERT INTO HandsPlayers
(handId, playerId, startCash, ante, tourneyTypeId,
card1, card2,
card3, card4,
card5, card6,
card7, winnings, rake, seatNo)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""",
(hands_id, player_ids[i], start_cashes[i], antes[i], 1,
card1, card2,
card3, card4,
card5, card6,
card7, winnings[i], rakes[i], seatNos[i]))
#cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i]))
#result.append(cursor.fetchall()[0][0])
result.append( getLastInsertId(backend, conn, cursor) )
return result
#end def store_hands_players_stud
def store_hands_players_holdem_omaha_tourney(backend, conn, cursor, category, hands_id, player_ids
,start_cashes, positions, card_values, card_suits
, winnings, rakes, seatNos, tourneys_players_ids):
#stores hands_players for tourney holdem/omaha hands
result=[]
for i in xrange(len(player_ids)):
if len(card_values[0])==2:
cursor.execute ("""INSERT INTO HandsPlayers
(handId, playerId, startCash, position,
card1Value, card1Suit, card2Value, card2Suit,
winnings, rake, tourneysPlayersId, seatNo)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""",
(hands_id, player_ids[i], start_cashes[i], positions[i],
card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1],
winnings[i], rakes[i], tourneys_players_ids[i], seatNos[i]))
elif len(card_values[0])==4:
cursor.execute ("""INSERT INTO HandsPlayers
(handId, playerId, startCash, position,
card1Value, card1Suit, card2Value, card2Suit,
card3Value, card3Suit, card4Value, card4Suit,
winnings, rake, tourneysPlayersId, seatNo)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""",
(hands_id, player_ids[i], start_cashes[i], positions[i],
card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1],
card_values[i][2], card_suits[i][2], card_values[i][3], card_suits[i][3],
winnings[i], rakes[i], tourneys_players_ids[i], seatNos[i]))
else:
raise FpdbError ("invalid card_values length:"+str(len(card_values[0])))
#cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i]))
#result.append(cursor.fetchall()[0][0])
result.append( getLastInsertId(backend, conn, cursor) )
return result
#end def store_hands_players_holdem_omaha_tourney
def store_hands_players_stud_tourney(backend, conn, cursor, hands_id, player_ids, start_cashes,
antes, card_values, card_suits, winnings, rakes, seatNos, tourneys_players_ids):
#stores hands_players for tourney stud/razz hands
result=[]
for i in xrange(len(player_ids)):
cursor.execute ("""INSERT INTO HandsPlayers
(handId, playerId, startCash, ante,
card1Value, card1Suit, card2Value, card2Suit,
card3Value, card3Suit, card4Value, card4Suit,
card5Value, card5Suit, card6Value, card6Suit,
card7Value, card7Suit, winnings, rake, tourneysPlayersId, seatNo)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
%s, %s, %s, %s, %s, %s)""",
(hands_id, player_ids[i], start_cashes[i], antes[i],
card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1],
card_values[i][2], card_suits[i][2], card_values[i][3], card_suits[i][3],
card_values[i][4], card_suits[i][4], card_values[i][5], card_suits[i][5],
card_values[i][6], card_suits[i][6], winnings[i], rakes[i], tourneys_players_ids[i], seatNos[i]))
#cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i]))
#result.append(cursor.fetchall()[0][0])
result.append( getLastInsertId(backend, conn, cursor) )
return result
#end def store_hands_players_stud_tourney
def generateHudCacheData(player_ids, base, category, action_types, allIns, actionTypeByNo def generateHudCacheData(player_ids, base, category, action_types, allIns, actionTypeByNo
,winnings, totalWinnings, positions, actionTypes, actionAmounts, antes): ,winnings, totalWinnings, positions, actionTypes, actionAmounts, antes):
"""calculates data for the HUD during import. IMPORTANT: if you change this method make """calculates data for the HUD during import. IMPORTANT: if you change this method make
@ -2105,230 +1826,3 @@ def generateFoldToCB(street, playerIDs, didStreetCB, streetCBDone, foldToStreetC
if action[1]=="fold": if action[1]=="fold":
foldToStreetCBDone[player]=True foldToStreetCBDone[player]=True
#end def generateFoldToCB #end def generateFoldToCB
def storeHudCache(backend, cursor, base, category, gametypeId, hand_start_time, playerIds, hudImportData):
"""Modified version aiming for more speed ..."""
# if (category=="holdem" or category=="omahahi" or category=="omahahilo"):
if use_date_in_hudcache:
#print "key =", "d%02d%02d%02d " % (hand_start_time.year-2000, hand_start_time.month, hand_start_time.day)
styleKey = "d%02d%02d%02d" % (hand_start_time.year-2000, hand_start_time.month, hand_start_time.day)
else:
# hard-code styleKey as 'A000000' (all-time cache, no key) for now
styleKey = 'A000000'
#print "storeHudCache2, len(playerIds)=", len(playerIds), " len(vpip)=" \
#, len(hudImportData['street0VPI']), " len(totprof)=", len(hudImportData['totalProfit'])
for player in xrange(len(playerIds)):
# Set up a clean row
row=[]
row.append(0)#blank for id
row.append(gametypeId)
row.append(playerIds[player])
row.append(len(playerIds))#seats
for i in xrange(len(hudImportData)+2):
row.append(0)
if base=="hold":
row[4]=hudImportData['position'][player]
else:
row[4]=0
row[5]=1 #tourneysGametypeId
row[6]+=1 #HDs
if hudImportData['street0VPI'][player]: row[7]+=1
if hudImportData['street0Aggr'][player]: row[8]+=1
if hudImportData['street0_3BChance'][player]: row[9]+=1
if hudImportData['street0_3BDone'][player]: row[10]+=1
if hudImportData['street1Seen'][player]: row[11]+=1
if hudImportData['street2Seen'][player]: row[12]+=1
if hudImportData['street3Seen'][player]: row[13]+=1
if hudImportData['street4Seen'][player]: row[14]+=1
if hudImportData['sawShowdown'][player]: row[15]+=1
if hudImportData['street1Aggr'][player]: row[16]+=1
if hudImportData['street2Aggr'][player]: row[17]+=1
if hudImportData['street3Aggr'][player]: row[18]+=1
if hudImportData['street4Aggr'][player]: row[19]+=1
if hudImportData['otherRaisedStreet1'][player]: row[20]+=1
if hudImportData['otherRaisedStreet2'][player]: row[21]+=1
if hudImportData['otherRaisedStreet3'][player]: row[22]+=1
if hudImportData['otherRaisedStreet4'][player]: row[23]+=1
if hudImportData['foldToOtherRaisedStreet1'][player]: row[24]+=1
if hudImportData['foldToOtherRaisedStreet2'][player]: row[25]+=1
if hudImportData['foldToOtherRaisedStreet3'][player]: row[26]+=1
if hudImportData['foldToOtherRaisedStreet4'][player]: row[27]+=1
if hudImportData['wonWhenSeenStreet1'][player]!=0.0: row[28]+=hudImportData['wonWhenSeenStreet1'][player]
if hudImportData['wonAtSD'][player]!=0.0: row[29]+=hudImportData['wonAtSD'][player]
if hudImportData['stealAttemptChance'][player]: row[30]+=1
if hudImportData['stealAttempted'][player]: row[31]+=1
if hudImportData['foldBbToStealChance'][player]: row[32]+=1
if hudImportData['foldedBbToSteal'][player]: row[33]+=1
if hudImportData['foldSbToStealChance'][player]: row[34]+=1
if hudImportData['foldedSbToSteal'][player]: row[35]+=1
if hudImportData['street1CBChance'][player]: row[36]+=1
if hudImportData['street1CBDone'][player]: row[37]+=1
if hudImportData['street2CBChance'][player]: row[38]+=1
if hudImportData['street2CBDone'][player]: row[39]+=1
if hudImportData['street3CBChance'][player]: row[40]+=1
if hudImportData['street3CBDone'][player]: row[41]+=1
if hudImportData['street4CBChance'][player]: row[42]+=1
if hudImportData['street4CBDone'][player]: row[43]+=1
if hudImportData['foldToStreet1CBChance'][player]: row[44]+=1
if hudImportData['foldToStreet1CBDone'][player]: row[45]+=1
if hudImportData['foldToStreet2CBChance'][player]: row[46]+=1
if hudImportData['foldToStreet2CBDone'][player]: row[47]+=1
if hudImportData['foldToStreet3CBChance'][player]: row[48]+=1
if hudImportData['foldToStreet3CBDone'][player]: row[49]+=1
if hudImportData['foldToStreet4CBChance'][player]: row[50]+=1
if hudImportData['foldToStreet4CBDone'][player]: row[51]+=1
#print "player=", player
#print "len(totalProfit)=", len(hudImportData['totalProfit'])
if hudImportData['totalProfit'][player]:
row[52]+=hudImportData['totalProfit'][player]
if hudImportData['street1CheckCallRaiseChance'][player]: row[53]+=1
if hudImportData['street1CheckCallRaiseDone'][player]: row[54]+=1
if hudImportData['street2CheckCallRaiseChance'][player]: row[55]+=1
if hudImportData['street2CheckCallRaiseDone'][player]: row[56]+=1
if hudImportData['street3CheckCallRaiseChance'][player]: row[57]+=1
if hudImportData['street3CheckCallRaiseDone'][player]: row[58]+=1
if hudImportData['street4CheckCallRaiseChance'][player]: row[59]+=1
if hudImportData['street4CheckCallRaiseDone'][player]: row[60]+=1
# Try to do the update first:
num = cursor.execute("""UPDATE HudCache
SET HDs=HDs+%s, street0VPI=street0VPI+%s, street0Aggr=street0Aggr+%s,
street0_3BChance=street0_3BChance+%s, street0_3BDone=street0_3BDone+%s,
street1Seen=street1Seen+%s, street2Seen=street2Seen+%s, street3Seen=street3Seen+%s,
street4Seen=street4Seen+%s, sawShowdown=sawShowdown+%s,
street1Aggr=street1Aggr+%s, street2Aggr=street2Aggr+%s, street3Aggr=street3Aggr+%s,
street4Aggr=street4Aggr+%s, otherRaisedStreet1=otherRaisedStreet1+%s,
otherRaisedStreet2=otherRaisedStreet2+%s, otherRaisedStreet3=otherRaisedStreet3+%s,
otherRaisedStreet4=otherRaisedStreet4+%s,
foldToOtherRaisedStreet1=foldToOtherRaisedStreet1+%s, foldToOtherRaisedStreet2=foldToOtherRaisedStreet2+%s,
foldToOtherRaisedStreet3=foldToOtherRaisedStreet3+%s, foldToOtherRaisedStreet4=foldToOtherRaisedStreet4+%s,
wonWhenSeenStreet1=wonWhenSeenStreet1+%s, wonAtSD=wonAtSD+%s, stealAttemptChance=stealAttemptChance+%s,
stealAttempted=stealAttempted+%s, foldBbToStealChance=foldBbToStealChance+%s,
foldedBbToSteal=foldedBbToSteal+%s,
foldSbToStealChance=foldSbToStealChance+%s, foldedSbToSteal=foldedSbToSteal+%s,
street1CBChance=street1CBChance+%s, street1CBDone=street1CBDone+%s, street2CBChance=street2CBChance+%s,
street2CBDone=street2CBDone+%s, street3CBChance=street3CBChance+%s,
street3CBDone=street3CBDone+%s, street4CBChance=street4CBChance+%s, street4CBDone=street4CBDone+%s,
foldToStreet1CBChance=foldToStreet1CBChance+%s, foldToStreet1CBDone=foldToStreet1CBDone+%s,
foldToStreet2CBChance=foldToStreet2CBChance+%s, foldToStreet2CBDone=foldToStreet2CBDone+%s,
foldToStreet3CBChance=foldToStreet3CBChance+%s,
foldToStreet3CBDone=foldToStreet3CBDone+%s, foldToStreet4CBChance=foldToStreet4CBChance+%s,
foldToStreet4CBDone=foldToStreet4CBDone+%s, totalProfit=totalProfit+%s,
street1CheckCallRaiseChance=street1CheckCallRaiseChance+%s,
street1CheckCallRaiseDone=street1CheckCallRaiseDone+%s, street2CheckCallRaiseChance=street2CheckCallRaiseChance+%s,
street2CheckCallRaiseDone=street2CheckCallRaiseDone+%s, street3CheckCallRaiseChance=street3CheckCallRaiseChance+%s,
street3CheckCallRaiseDone=street3CheckCallRaiseDone+%s, street4CheckCallRaiseChance=street4CheckCallRaiseChance+%s,
street4CheckCallRaiseDone=street4CheckCallRaiseDone+%s
WHERE gametypeId+0=%s
AND playerId=%s
AND activeSeats=%s
AND position=%s
AND tourneyTypeId+0=%s
AND styleKey=%s
""", (row[6], row[7], row[8], row[9], row[10],
row[11], row[12], row[13], row[14], row[15],
row[16], row[17], row[18], row[19], row[20],
row[21], row[22], row[23], row[24], row[25],
row[26], row[27], row[28], row[29], row[30],
row[31], row[32], row[33], row[34], row[35],
row[36], row[37], row[38], row[39], row[40],
row[41], row[42], row[43], row[44], row[45],
row[46], row[47], row[48], row[49], row[50],
row[51], row[52], row[53], row[54], row[55],
row[56], row[57], row[58], row[59], row[60],
row[1], row[2], row[3], str(row[4]), row[5], styleKey))
# Test statusmessage to see if update worked, do insert if not
#print "storehud2, upd num =", num
if ( (backend == PGSQL and cursor.statusmessage != "UPDATE 1")
or (backend == MYSQL_INNODB and num == 0) ):
#print "playerid before insert:",row[2]," num = ", num
cursor.execute("""INSERT INTO HudCache
(gametypeId, playerId, activeSeats, position, tourneyTypeId, styleKey,
HDs, street0VPI, street0Aggr, street0_3BChance, street0_3BDone,
street1Seen, street2Seen, street3Seen, street4Seen, sawShowdown,
street1Aggr, street2Aggr, street3Aggr, street4Aggr, otherRaisedStreet1,
otherRaisedStreet2, otherRaisedStreet3, otherRaisedStreet4, foldToOtherRaisedStreet1, foldToOtherRaisedStreet2,
foldToOtherRaisedStreet3, foldToOtherRaisedStreet4, wonWhenSeenStreet1, wonAtSD, stealAttemptChance,
stealAttempted, foldBbToStealChance, foldedBbToSteal, foldSbToStealChance, foldedSbToSteal,
street1CBChance, street1CBDone, street2CBChance, street2CBDone, street3CBChance,
street3CBDone, street4CBChance, street4CBDone, foldToStreet1CBChance, foldToStreet1CBDone,
foldToStreet2CBChance, foldToStreet2CBDone, foldToStreet3CBChance, foldToStreet3CBDone, foldToStreet4CBChance,
foldToStreet4CBDone, totalProfit, street1CheckCallRaiseChance, street1CheckCallRaiseDone, street2CheckCallRaiseChance,
street2CheckCallRaiseDone, street3CheckCallRaiseChance, street3CheckCallRaiseDone, street4CheckCallRaiseChance, street4CheckCallRaiseDone)
VALUES (%s, %s, %s, %s, %s, %s,
%s, %s, %s, %s, %s,
%s, %s, %s, %s, %s,
%s, %s, %s, %s, %s,
%s, %s, %s, %s, %s,
%s, %s, %s, %s, %s,
%s, %s, %s, %s, %s,
%s, %s, %s, %s, %s,
%s, %s, %s, %s, %s,
%s, %s, %s, %s, %s,
%s, %s, %s, %s, %s,
%s, %s, %s, %s, %s)"""
, (row[1], row[2], row[3], row[4], row[5], styleKey, row[6], row[7], row[8], row[9], row[10]
,row[11], row[12], row[13], row[14], row[15], row[16], row[17], row[18], row[19], row[20]
,row[21], row[22], row[23], row[24], row[25], row[26], row[27], row[28], row[29], row[30]
,row[31], row[32], row[33], row[34], row[35], row[36], row[37], row[38], row[39], row[40]
,row[41], row[42], row[43], row[44], row[45], row[46], row[47], row[48], row[49], row[50]
,row[51], row[52], row[53], row[54], row[55], row[56], row[57], row[58], row[59], row[60]) )
#print "hopefully inserted hud data line: ", cursor.statusmessage
# message seems to be "INSERT 0 1"
else:
#print "updated(2) hud data line"
pass
# else:
# print "todo: implement storeHudCache for stud base"
#end def storeHudCache2
def store_tourneys(cursor, tourneyTypeId, siteTourneyNo, entries, prizepool, startTime):
cursor.execute("SELECT id FROM Tourneys WHERE siteTourneyNo=%s AND tourneyTypeId+0=%s", (siteTourneyNo, tourneyTypeId))
tmp=cursor.fetchone()
#print "tried SELECTing tourneys.id, result:",tmp
try:
len(tmp)
except TypeError:#means we have to create new one
cursor.execute("""INSERT INTO Tourneys
(tourneyTypeId, siteTourneyNo, entries, prizepool, startTime)
VALUES (%s, %s, %s, %s, %s)""", (tourneyTypeId, siteTourneyNo, entries, prizepool, startTime))
cursor.execute("SELECT id FROM Tourneys WHERE siteTourneyNo=%s AND tourneyTypeId+0=%s", (siteTourneyNo, tourneyTypeId))
tmp=cursor.fetchone()
#print "created new tourneys.id:",tmp
return tmp[0]
#end def store_tourneys
def store_tourneys_players(cursor, tourney_id, player_ids, payin_amounts, ranks, winnings):
result=[]
#print "in store_tourneys_players. tourney_id:",tourney_id
#print "player_ids:",player_ids
#print "payin_amounts:",payin_amounts
#print "ranks:",ranks
#print "winnings:",winnings
for i in xrange(len(player_ids)):
cursor.execute("SELECT id FROM TourneysPlayers WHERE tourneyId=%s AND playerId+0=%s", (tourney_id, player_ids[i]))
tmp=cursor.fetchone()
#print "tried SELECTing tourneys_players.id:",tmp
try:
len(tmp)
except TypeError:
cursor.execute("""INSERT INTO TourneysPlayers
(tourneyId, playerId, payinAmount, rank, winnings) VALUES (%s, %s, %s, %s, %s)""",
(tourney_id, player_ids[i], payin_amounts[i], ranks[i], winnings[i]))
cursor.execute("SELECT id FROM TourneysPlayers WHERE tourneyId=%s AND playerId+0=%s",
(tourney_id, player_ids[i]))
tmp=cursor.fetchone()
#print "created new tourneys_players.id:",tmp
result.append(tmp[0])
return result
#end def store_tourneys_players

271
pyfpdb/interlocks.py Executable file
View File

@ -0,0 +1,271 @@
# Code from http://ender.snowburst.org:4747/~jjohns/interlocks.py
# Thanks JJ!
import sys
import os, os.path
import subprocess
import time
import signal
import base64
InterProcessLock = None
"""
Just use me like a thread lock. acquire() / release() / locked()
Differences compared to thread locks:
1. By default, acquire()'s wait parameter is false.
2. When acquire fails, SingleInstanceError is thrown instead of simply returning false.
3. acquire() can take a 3rd parameter retry_time, which, if wait is True, tells the locking
mechanism how long to sleep between retrying the lock. Has no effect for unix/InterProcessLockFcntl.
Differences in fpdb version to JJ's original:
1. Changed acquire() to return false like other locks
2. Made acquire fail if same process already has the lock
"""
class SingleInstanceError(RuntimeError):
"Thrown when you try to acquire an InterProcessLock and another version of the process is already running."
class InterProcessLockBase:
def __init__(self, name=None ):
self._has_lock = False
if not name:
name = sys.argv[0]
self.name = name
def getHashedName(self):
return base64.b64encode(self.name).replace('=','')
def acquire_impl(self, wait): abstract
def acquire(self, wait=False, retry_time=1):
if self._has_lock: # make sure 2nd acquire in same process fails
return False
while not self._has_lock:
try:
self.acquire_impl(wait)
self._has_lock = True
#print 'i have the lock'
except SingleInstanceError:
if not wait:
# raise # change back to normal acquire functionality, sorry JJ!
return False
time.sleep(retry_time)
return True
def release(self):
self.release_impl()
self._has_lock = False
def locked(self):
if self._has_lock:
return True
try:
self.acquire()
self.release()
return False
except SingleInstanceError:
return True
LOCK_FILE_DIRECTORY = '/tmp'
class InterProcessLockFcntl(InterProcessLockBase):
def __init__(self, name=None):
InterProcessLockBase.__init__(self, name)
self.lockfd = 0
self.lock_file_name = os.path.join(LOCK_FILE_DIRECTORY, self.getHashedName() + '.lck')
assert(os.path.isdir(LOCK_FILE_DIRECTORY))
# This is the suggested way to get a safe file name, but I like having a descriptively named lock file.
def getHashedName(self):
import re
bad_filename_character_re = re.compile(r'/\?<>\\\:;\*\|\'\"\^=\.\[\]')
return bad_filename_character_re.sub('_',self.name)
def acquire_impl(self, wait):
self.lockfd = open(self.lock_file_name, 'w')
fcntrl_options = fcntl.LOCK_EX
if not wait:
fcntrl_options |= fcntl.LOCK_NB
try:
fcntl.flock(self.lockfd, fcntrl_options)
except IOError:
self.lockfd.close()
self.lockfd = 0
raise SingleInstanceError('Could not acquire exclusive lock on '+self.lock_file_name)
def release_impl(self):
fcntl.lockf(self.lockfd, fcntl.LOCK_UN)
self.lockfd.close()
self.lockfd = 0
try:
os.unlink(self.lock_file_name)
except IOError:
# We don't care about the existence of the file too much here. It's the flock() we care about,
# And that should just go away magically.
pass
class InterProcessLockWin32(InterProcessLockBase):
def __init__(self, name=None):
InterProcessLockBase.__init__(self, name)
self.mutex = None
def acquire_impl(self,wait):
self.mutex = win32event.CreateMutex(None, 0, self.getHashedName())
if win32api.GetLastError() == winerror.ERROR_ALREADY_EXISTS:
self.mutex.Close()
self.mutex = None
raise SingleInstanceError('Could not acquire exclusive lock on ' + self.name)
def release_impl(self):
self.mutex.Close()
class InterProcessLockSocket(InterProcessLockBase):
def __init__(self, name=None):
InterProcessLockBase.__init__(self, name)
self.socket = None
self.portno = 65530 - abs(self.getHashedName().__hash__()) % 32749
def acquire_impl(self, wait):
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
self.socket.bind(('127.0.0.1', self.portno))
except socket.error:
self.socket.close()
self.socket = None
raise SingleInstanceError('Could not acquire exclusive lock on ' + self.name)
def release_impl(self):
self.socket.close()
self.socket = None
# Set InterProcessLock to the correct type given the sysem parameters available
try:
import fcntl
InterProcessLock = InterProcessLockFcntl
except ImportError:
try:
import win32event
import win32api
import winerror
InterProcessLock = InterProcessLockWin32
except ImportError:
import socket
InterProcessLock = InterProcessLockSocket
def test_construct():
"""
# Making the name of the test unique so it can be executed my multiple users on the same machine.
>>> test_name = 'InterProcessLockTest' +str(os.getpid()) + str(time.time())
>>> lock1 = InterProcessLock(name=test_name)
>>> lock1.acquire()
>>> lock2 = InterProcessLock(name=test_name)
>>> lock3 = InterProcessLock(name=test_name)
# Since lock1 is locked, other attempts to acquire it fail.
>>> lock2.acquire()
Traceback (most recent call last):
...
SingleInstanceError: Could not acquire exclusive lock on /tmp/test.lck
>>> lock3.acquire()
Traceback (most recent call last):
...
SingleInstanceError: Could not acquire exclusive lock on /tmp/test.lck
# Release the lock and let lock2 have it.
>>> lock1.release()
>>> lock2.acquire()
>>> lock3.acquire()
Traceback (most recent call last):
...
SingleInstanceError: Could not acquire exclusive lock on /tmp/test.lck
# Release it and give it back to lock1
>>> lock2.release()
>>> lock1.acquire()
>>> lock2.acquire()
Traceback (most recent call last):
...
SingleInstanceError: Could not acquire exclusive lock on /tmp/test.lck
# Test lock status
>>> lock2.locked()
True
>>> lock3.locked()
True
>>> lock1.locked()
True
>>> lock1.release()
>>> lock2.locked()
False
>>> lock3.locked()
False
>>> lock1.locked()
False
>>> if os.name == 'posix':
... def os_independent_kill(pid):
... import signal
... os.kill(pid, signal.SIGKILL)
... else:
... assert(os.name == 'nt')
... def os_independent_kill(pid):
... ''' http://www.python.org/doc/faq/windows/#how-do-i-emulate-os-kill-in-windows '''
... import win32api
... import win32con
... import pywintypes
... handle = win32api.OpenProcess(win32con.PROCESS_TERMINATE , pywintypes.FALSE, pid)
... return (0 != win32api.TerminateProcess(handle, 0))
# Test to acquire the lock in another process.
>>> def execute(cmd):
... cmd = 'import time;' + cmd + 'time.sleep(10);'
... process = subprocess.Popen([sys.executable, '-c', cmd])
... pid = process.pid
... time.sleep(2) # quick hack, but we test synchronization in the end
... return pid
>>> pid = execute('import interlocks;a=interlocks.InterProcessLock(name=\\''+test_name+ '\\');a.acquire();')
>>> lock1.acquire()
Traceback (most recent call last):
...
SingleInstanceError: Could not acquire exclusive lock on /tmp/test.lck
>>> os_independent_kill(pid)
>>> time.sleep(1)
>>> lock1.acquire()
>>> lock1.release()
# Testing wait
>>> pid = execute('import interlocks;a=interlocks.InterProcessLock(name=\\''+test_name+ '\\');a.acquire();')
>>> lock1.acquire()
Traceback (most recent call last):
...
SingleInstanceError: Could not acquire exclusive lock on /tmp/test.lck
>>> os_independent_kill(pid)
>>> lock1.acquire(True)
>>> lock1.release()
"""
pass
if __name__=='__main__':
import doctest
doctest.testmod(optionflags=doctest.IGNORE_EXCEPTION_DETAIL)