Merge branch 'master' of git://git.assembla.com/fpdb-eric

This commit is contained in:
sqlcoder 2009-07-28 21:25:07 +01:00
commit 3cc5ed8024
8 changed files with 118 additions and 54 deletions

View File

@ -24,25 +24,37 @@ def twoStartCards(value1, suit1, value2, suit2):
(y+2) represents rank of second card (2=2 .. 14=Ace)
If x > y then pair is suited, if x < y then unsuited"""
if value1 < 2 or value2 < 2:
return(0)
if (suit1 == suit2 and value1 < value2) or (suit1 != suit2 and value2 > value1):
return(13 * (value2-2) + (value1-1))
ret = 0
if value1 == value2: # pairs
ret = (13 * (value2-2) + (value2-1) )
elif suit1 == suit2:
if value1 > value2:
ret = 13 * (value1-2) + (value2-1)
else:
return(13 * (value1-2) + (value2-1))
ret = 13 * (value2-2) + (value1-1)
else:
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):
""" Function to convert an int representing 2 holdem hole cards (as created by twoStartCards)
into a string like AQo """
if card <= 0:
return 'xx'
else:
ret = 'xx'
if card > 0:
card -= 1
s = ('2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K', 'A')
x = card / 13
y = card - 13 * x
if x == y: return(s[x] + s[y])
elif x > y: return(s[x] + s[y] + 's')
else: return(s[y] + s[x] + 'o')
if x == y: ret = s[x] + s[y]
elif x > y: ret = s[x] + s[y] + 's'
else: ret = s[y] + s[x] + 'o'
# print "twoStartCardString(", card ,") = " + ret
return ret
def fourStartCards(value1, suit1, value2, suit2, value3, suit3, value4, suit4):
""" Function to convert 4 value,suit pairs into a Omaha style starting hand,
@ -95,4 +107,7 @@ def valueSuitFromCard(card):
][card] )
if __name__ == '__main__':
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))

View File

@ -237,11 +237,14 @@ or None if we fail to get the info """
# Also works with Omaha hands.
cards = m.group('CARDS')
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:
#Not involved in hand
hand.involved = False
def readStudPlayerCards(self, hand, street):
# lol. see Plymouth.txt
logging.warning("Everleaf readStudPlayerCards is only a stub.")
@ -292,7 +295,8 @@ or None if we fail to get the info """
cards = cards.split(', ')
player = m.group('PNAME')
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'))

View File

@ -33,25 +33,33 @@ class Fulltilt(HandHistoryConverter):
-\s(?P<CURRENCY>\$|)?
(?P<SB>[.0-9]+)/
\$?(?P<BB>[.0-9]+)\s
(Ante\s\$(?P<ANTE>[.0-9]+)\s)?-\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))
(?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_TailSplitHands = re.compile(r"(\n\n+)")
re_HandInfo = re.compile('''.*\#(?P<HID>[0-9]+):\s
(?:(?P<TOURNAMENT>.+)\s\((?P<TOURNO>\d+)\),\s)?
Table\s(?P<TABLE>[-\s\da-zA-Z]+)\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<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_PlayerInfo = re.compile('Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \(\$?(?P<CASH>[,.0-9]+)\)$', re.MULTILINE)
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.
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)
@ -109,7 +117,6 @@ follow : whether to tail -f the input"""
m = self.re_GameInfo.search(handText)
if not m:
return None
mg = m.groupdict()
# translations from captured groups to our info strings
@ -119,7 +126,7 @@ follow : whether to tail -f the input"""
'Omaha Hi' : ('hold','omahahi'),
'Omaha H/L' : ('hold','omahahilo'),
'Razz' : ('stud','razz'),
'7 Card Stud' : ('stud','studhi'),
'Stud Hi' : ('stud','studhi'),
'Stud H/L' : ('stud','studhilo')
}
currencies = { u'':'EUR', '$':'USD', '':'T$' }
@ -137,7 +144,6 @@ follow : whether to tail -f the input"""
def readHandInfo(self, hand):
m = self.re_HandInfo.search(hand.handText,re.DOTALL)
if(m == None):
logging.info("Didn't match re_HandInfo")
logging.info(hand.handText)
@ -146,12 +152,12 @@ follow : whether to tail -f the input"""
hand.tablename = m.group('TABLE')
hand.starttime = datetime.datetime.strptime(m.group('DATETIME'), "%H:%M:%S ET - %Y/%m/%d")
if m.group('TABLEATTRIBUTES'):
m2 = re.search("(deep )?(\d+)( max)?", m.group('TABLEATTRIBUTES'))
hand.maxseats = int(m2.group(2))
m2 = self.re_Max.search(m.group('TABLEATTRIBUTES'))
if m2: hand.maxseats = int(m2.group('MAX'))
hand.tourNo = m.group('TOURNO')
# if key == 'PLAY' and info['PLAY'] != None:
# hand.gametype['currency'] = 'play'
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.
# m.group('SB')
@ -214,6 +220,7 @@ follow : whether to tail -f the input"""
m = self.re_Antes.finditer(hand.handText)
for player in m:
logging.debug("hand.addAnte(%s,%s)" %(player.group('PNAME'), player.group('ANTE')))
# if player.group() !=
hand.addAnte(player.group('PNAME'), player.group('ANTE'))
def readBringIn(self, hand):
@ -222,7 +229,7 @@ follow : whether to tail -f the input"""
logging.debug("Player bringing in: %s for %s" %(m.group('PNAME'), m.group('BRINGIN')))
hand.addBringIn(m.group('PNAME'), m.group('BRINGIN'))
else:
logging.warning("No bringin found")
logging.warning("No bringin found, handid =%s" % hand.handid)
def readButton(self, hand):
hand.buttonpos = int(self.re_Button.search(hand.handText).group('BUTTON'))
@ -255,10 +262,10 @@ follow : whether to tail -f the input"""
else:
oldcards = found.group('OLDCARDS').split(' ')
if street == 'THIRD' and len(newcards) == 3: # hero in stud game
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=newcards[0:2], open=[newcards[2]], shown=False, mucked=False, dealt=False)
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)
@ -317,6 +324,11 @@ follow : whether to tail -f the input"""
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__":
parser = OptionParser()

View File

@ -262,7 +262,8 @@ def main(argv=None):
else:
#Do something useful
importer = fpdb_import.Importer(False,settings, config)
importer.setDropIndexes("auto")
# importer.setDropIndexes("auto")
importer.setDropIndexes("don't drop")
importer.setFailOnError(options.failOnError)
importer.addBulkImportImportFileOrDir(os.path.expanduser(options.filename), site=options.filtername)
importer.setCallHud(False)

View File

@ -39,7 +39,7 @@ class Hand(object):
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'}
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"):
@ -583,7 +583,7 @@ Map the tuple self.gametype onto the pokerstars string describing it
table_string = "Table \'%s\' %s-max" % (self.tablename, self.maxseats)
if self.gametype['currency'] == 'play':
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
return table_string
@ -632,6 +632,7 @@ class HoldemOmahaHand(Hand):
hhc.getRake(self)
if self.maxseats == None:
self.maxseats = hhc.guessMaxSeats(self)
hhc.readOther(self)
elif builtFrom == "DB":
if handid is not None:
self.select(handid) # Will need a handId
@ -877,6 +878,9 @@ class DrawHand(Hand):
hhc.readShownCards(self)
self.totalPot() # finalise it (total the pot)
hhc.getRake(self)
if self.maxseats == None:
self.maxseats = hhc.guessMaxSeats(self)
hhc.readOther(self)
elif builtFrom == "DB":
self.select("dummy") # Will need a handId
@ -1032,7 +1036,8 @@ class StudHand(Hand):
hhc.readBringIn(self)
hhc.readHeroCards(self)
# 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]:
logging.debug(street)
logging.debug(self.streets[street])
@ -1041,6 +1046,9 @@ class StudHand(Hand):
hhc.readShownCards(self) # not done yet
self.totalPot() # finalise it (total the pot)
hhc.getRake(self)
if self.maxseats == None:
self.maxseats = hhc.guessMaxSeats(self)
hhc.readOther(self)
elif builtFrom == "DB":
self.select("dummy") # Will need a handId
@ -1079,6 +1087,7 @@ closed likewise, but known only to player
Add a complete on [street] by [player] to [amountTo]
"""
logging.debug("%s %s completes %s" % (street, player, amountTo))
amountTo = re.sub(u',', u'', amountTo) #some sites have commas
self.checkPlayerExists(player)
Bp = self.lastBet['THIRD']
Bc = reduce(operator.add, self.bets[street][player], 0)
@ -1314,31 +1323,19 @@ class Pot(object):
# Total pot $124.30 Main pot $98.90. Side pot $23.40. | Rake $2
def __str__(self):
if self.sym is None:
self.sym = "C"
if self.total is None:
print "call Pot.end() before printing pot total"
# NB if I'm sure end() is idempotent, call it here.
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])
# TODO: This really neeads to be a loop to handle multiple side pots
if len(self.pots) == 1: # (only use Total pot)
return "Total pot %s%.2f" % (self.sym, self.total,)
elif len(self.pots) == 2:
return "Total pot %s%.2f Main pot %s%.2f. Side pot %s%2.f." % (self.sym, self.total, self.sym, self.pots[0], self.sym, self.pots[1])
elif len(self.pots) == 3:
return "Total pot %s%.2f Main pot %s%.2f. Side pot-1 %s%2.2f. Side pot-2 %s%.2f." % (self.sym, self.total, self.sym, self.pots[0], self.sym, self.pots[1], self.sym, self.pots[2])
elif len(self.pots) == 0:
# no small blind and walk in bb (hopefully)
return "Total pot %s%.2f" % (self.sym, 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
return ret + ''.join([ (" Side pot %s%.2f." % (self.sym, self.pots[x]) ) for x in xrange(1, len(self.pots)) ])
def assemble(cnxn, handid):
c = cnxn.cursor()

View File

@ -270,6 +270,11 @@ or None if we fail to get the info """
def readCollectPot(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
# an inheriting class can calculate it for the specific site if need be.
def getRake(self, hand):

View File

@ -244,6 +244,24 @@ class fpdb:
self.release_global_lock()
#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):
print "todo: implement dia_regression_test"
self.obtain_global_lock()
@ -306,6 +324,7 @@ class fpdb:
<menuitem action="createdb"/>
<menuitem action="createuser"/>
<menuitem action="createtabs"/>
<menuitem action="rebuildhudcache"/>
<menuitem action="stats"/>
<menuitem action="sessions"/>
</menu>
@ -344,6 +363,7 @@ class fpdb:
('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),
('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),
('sessions', None, 'Sessions', None, 'View Sessions', self.dia_database_sessions),
('help', None, '_Help'),

View File

@ -361,6 +361,10 @@ class fpdb_db:
"""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.MYSQL_INNODB:
self.cursor.execute("SET foreign_key_checks=0")
self.cursor.execute("SET autocommit=0")
return
if self.backend == self.PGSQL:
self.db.set_isolation_level(0) # allow table/index operations to work
for fk in self.foreignKeys[self.backend]:
@ -450,6 +454,12 @@ class fpdb_db:
def afterBulkImport(self):
"""Re-create any dropped indexes/foreign keys after bulk import"""
stime = time()
if self.backend == self.MYSQL_INNODB:
self.cursor.execute("SET foreign_key_checks=1")
self.cursor.execute("SET autocommit=1")
return
if self.backend == self.PGSQL:
self.db.set_isolation_level(0) # allow table/index operations to work
for fk in self.foreignKeys[self.backend]: