Merge branch 'master' of git://git.assembla.com/fpt_fpdb
This commit is contained in:
commit
4065eebabe
|
@ -47,7 +47,7 @@ class Fulltilt(HandHistoryConverter):
|
||||||
re_TailSplitHands = re.compile(r"(\n\n+)")
|
re_TailSplitHands = re.compile(r"(\n\n+)")
|
||||||
re_HandInfo = re.compile(r'''.*\#(?P<HID>[0-9]+):\s
|
re_HandInfo = re.compile(r'''.*\#(?P<HID>[0-9]+):\s
|
||||||
(?:(?P<TOURNAMENT>.+)\s\((?P<TOURNO>\d+)\),\s)?
|
(?:(?P<TOURNAMENT>.+)\s\((?P<TOURNO>\d+)\),\s)?
|
||||||
Table\s
|
(Table|Match)\s
|
||||||
(?P<PLAY>Play\sChip\s|PC)?
|
(?P<PLAY>Play\sChip\s|PC)?
|
||||||
(?P<TABLE>[-\s\da-zA-Z]+)\s
|
(?P<TABLE>[-\s\da-zA-Z]+)\s
|
||||||
(\((?P<TABLEATTRIBUTES>.+)\)\s)?-\s
|
(\((?P<TABLEATTRIBUTES>.+)\)\s)?-\s
|
||||||
|
@ -61,6 +61,46 @@ class Fulltilt(HandHistoryConverter):
|
||||||
re_TourneyPlayerInfo = re.compile('Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \(\$?(?P<CASH>[,.0-9]+)\)', re.MULTILINE)
|
re_TourneyPlayerInfo = 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>.+)\]")
|
||||||
|
|
||||||
|
#static regex for tourney purpose
|
||||||
|
re_TourneyInfo = re.compile('''Tournament\sSummary\s
|
||||||
|
(?P<TOURNAMENT_NAME>[^$(]+)?\s*
|
||||||
|
((?P<CURRENCY>\$|)?(?P<BUYIN>[.0-9]+)\s*\+\s*\$?(?P<FEE>[.0-9]+)\s)?
|
||||||
|
((?P<SPECIAL>(KO|Heads\sUp|Matrix\s\dx|Rebuy))\s)?
|
||||||
|
((?P<SHOOTOUT>Shootout)\s)?
|
||||||
|
((?P<SNG>Sit\s&\sGo)\s)?
|
||||||
|
(\((?P<TURBO1>Turbo)\)\s)?
|
||||||
|
\((?P<TOURNO>\d+)\)\s
|
||||||
|
((?P<MATCHNO>Match\s\d)\s)?
|
||||||
|
(?P<GAME>(Hold\'em|Omaha\sHi|Omaha\sH/L|7\sCard\sStud|Stud\sH/L|Razz|Stud\sHi))\s
|
||||||
|
(\((?P<TURBO2>Turbo)\)\s)?
|
||||||
|
(?P<LIMIT>(No\sLimit|Pot\sLimit|Limit))?
|
||||||
|
''', re.VERBOSE)
|
||||||
|
re_TourneyBuyInFee = re.compile("Buy-In: (?P<BUYIN_CURRENCY>\$|)?(?P<BUYIN>[.0-9]+) \+ \$?(?P<FEE>[.0-9]+)")
|
||||||
|
re_TourneyBuyInChips = re.compile("Buy-In Chips: (?P<BUYINCHIPS>\d+)")
|
||||||
|
re_TourneyEntries = re.compile("(?P<ENTRIES>\d+) Entries")
|
||||||
|
re_TourneyPrizePool = re.compile("Total Prize Pool: (?P<PRIZEPOOL_CURRENCY>\$|)?(?P<PRIZEPOOL>[.,0-9]+)")
|
||||||
|
re_TourneyRebuyAmount = re.compile("Rebuy: (?P<REBUY_CURRENCY>\$|)?(?P<REBUY_AMOUNT>[.,0-9]+)")
|
||||||
|
re_TourneyAddOnAmount = re.compile("Add-On: (?P<ADDON_CURRENCY>\$|)?(?P<ADDON_AMOUNT>[.,0-9]+)")
|
||||||
|
re_TourneyRebuyCount = re.compile("performed (?P<REBUY_COUNT>\d+) Rebuy")
|
||||||
|
re_TourneyAddOnCount = re.compile("performed (?P<ADDON_COUNT>\d+) Add-On")
|
||||||
|
re_TourneyRebuysTotal = re.compile("Total Rebuys: (?P<REBUY_TOTAL>\d+)")
|
||||||
|
re_TourneyAddOnsTotal = re.compile("Total Add-Ons: (?P<ADDONS_TOTAL>\d+)")
|
||||||
|
re_TourneyRebuyChips = re.compile("Rebuy Chips: (?P<REBUY_CHIPS>\d+)")
|
||||||
|
re_TourneyAddOnChips = re.compile("Add-On Chips: (?P<ADDON_CHIPS>\d+)")
|
||||||
|
re_TourneyKOBounty = re.compile("Knockout Bounty: (?P<KO_BOUNTY_CURRENCY>\$|)?(?P<KO_BOUNTY_AMOUNT>[.,0-9]+)")
|
||||||
|
re_TourneyCountKO = re.compile("received (?P<COUNT_KO>\d+) Knockout Bounty Award(s)?")
|
||||||
|
re_TourneyTimeInfo = re.compile("Tournament started: (?P<STARTTIME>.*)\nTournament ((?P<IN_PROGRESS>is still in progress)?|(finished:(?P<ENDTIME>.*))?)$")
|
||||||
|
|
||||||
|
re_TourneyPlayersSummary = re.compile("^(?P<RANK>(Still Playing|\d+))( - |: )(?P<PNAME>[^\n,]+)(, )?(?P<WINNING_CURRENCY>\$|)?(?P<WINNING>[.\d]+)?", re.MULTILINE)
|
||||||
|
re_TourneyHeroFinishingP = re.compile("(?P<HERO_NAME>.*) finished in (?P<HERO_FINISHING_POS>\d+)(st|nd|rd|th) place")
|
||||||
|
|
||||||
|
#TODO: See if we need to deal with play money tourney summaries -- Not right now (they shouldn't pass the re_TourneyInfo)
|
||||||
|
##Full Tilt Poker Tournament Summary 250 Play Money Sit & Go (102909471) Hold'em No Limit
|
||||||
|
##Buy-In: 250 Play Chips + 0 Play Chips
|
||||||
|
##Buy-In Chips: 1500
|
||||||
|
##6 Entries
|
||||||
|
##Total Prize Pool: 1,500 Play Chips
|
||||||
|
|
||||||
# These regexes are for FTP only
|
# These regexes are for FTP only
|
||||||
re_Mixed = re.compile(r'\s\-\s(?P<MIXED>HA|HORSE|HOSE)\s\-\s', re.VERBOSE)
|
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)
|
re_Max = re.compile("(?P<MAX>\d+)( max)?", re.MULTILINE)
|
||||||
|
@ -353,6 +393,225 @@ class Fulltilt(HandHistoryConverter):
|
||||||
else:
|
else:
|
||||||
hand.mixed = self.mixes[m.groupdict()['MIXED']]
|
hand.mixed = self.mixes[m.groupdict()['MIXED']]
|
||||||
|
|
||||||
|
def readSummaryInfo(self, summaryInfoList):
|
||||||
|
starttime = time.time()
|
||||||
|
self.status = True
|
||||||
|
|
||||||
|
m = re.search("Tournament Summary", summaryInfoList[0])
|
||||||
|
if m:
|
||||||
|
# info list should be 2 lines : Tourney infos & Finsihing postions with winnings
|
||||||
|
if (len(summaryInfoList) != 2 ):
|
||||||
|
log.info("Too many lines (%d) in file '%s' : '%s'" % (len(summaryInfoList), self.in_path, summaryInfoList) )
|
||||||
|
self.status = False
|
||||||
|
else:
|
||||||
|
self.tourney = Tourney.Tourney(sitename = self.sitename, gametype = None, summaryText = summaryInfoList, builtFrom = "HHC")
|
||||||
|
self.status = self.determineTourneyType(self.tourney)
|
||||||
|
if self.status == True :
|
||||||
|
self.status = status = self.getPlayersPositionsAndWinnings(self.tourney)
|
||||||
|
#print self.tourney
|
||||||
|
else:
|
||||||
|
log.info("Parsing NOK : rejected")
|
||||||
|
else:
|
||||||
|
log.info( "This is not a summary file : '%s'" % (self.in_path) )
|
||||||
|
self.status = False
|
||||||
|
|
||||||
|
return self.status
|
||||||
|
|
||||||
|
def determineTourneyType(self, tourney):
|
||||||
|
info = {'type':'tour'}
|
||||||
|
tourneyText = tourney.summaryText[0]
|
||||||
|
#print "Examine : '%s'" %(tourneyText)
|
||||||
|
|
||||||
|
m = self.re_TourneyInfo.search(tourneyText)
|
||||||
|
if not m:
|
||||||
|
log.info( "determineTourneyType : Parsing NOK" )
|
||||||
|
return False
|
||||||
|
mg = m.groupdict()
|
||||||
|
#print mg
|
||||||
|
|
||||||
|
# translations from captured groups to our info strings
|
||||||
|
limits = { 'No Limit':'nl', 'Pot Limit':'pl', 'Limit':'fl' }
|
||||||
|
games = { # base, category
|
||||||
|
"Hold'em" : ('hold','holdem'),
|
||||||
|
'Omaha Hi' : ('hold','omahahi'),
|
||||||
|
'Omaha H/L' : ('hold','omahahilo'),
|
||||||
|
'Razz' : ('stud','razz'),
|
||||||
|
'Stud Hi' : ('stud','studhi'),
|
||||||
|
'Stud H/L' : ('stud','studhilo')
|
||||||
|
}
|
||||||
|
currencies = { u' €':'EUR', '$':'USD', '':'T$' }
|
||||||
|
info['limitType'] = limits[mg['LIMIT']]
|
||||||
|
if mg['GAME'] is not None:
|
||||||
|
(info['base'], info['category']) = games[mg['GAME']]
|
||||||
|
if mg['CURRENCY'] is not None:
|
||||||
|
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.
|
||||||
|
|
||||||
|
# Info is now ready to be copied in the tourney object
|
||||||
|
tourney.gametype = info
|
||||||
|
|
||||||
|
# Additional info can be stored in the tourney object
|
||||||
|
if mg['BUYIN'] is not None:
|
||||||
|
tourney.buyin = mg['BUYIN']
|
||||||
|
tourney.fee = 0
|
||||||
|
if mg['FEE'] is not None:
|
||||||
|
tourney.fee = mg['FEE']
|
||||||
|
if mg['TOURNAMENT_NAME'] is not None:
|
||||||
|
# Tournament Name can have a trailing space at the end (depending on the tournament description)
|
||||||
|
tourney.tourneyName = mg['TOURNAMENT_NAME'].rstrip()
|
||||||
|
if mg['SPECIAL'] is not None:
|
||||||
|
special = mg['SPECIAL']
|
||||||
|
if special == "KO":
|
||||||
|
tourney.isKO = True
|
||||||
|
if special == "Heads Up":
|
||||||
|
tourney.isHU = True
|
||||||
|
tourney.maxseats = 2
|
||||||
|
if re.search("Matrix", special):
|
||||||
|
tourney.isMatrix = True
|
||||||
|
if special == "Rebuy":
|
||||||
|
tourney.isRebuy = True
|
||||||
|
if mg['SHOOTOUT'] is not None:
|
||||||
|
tourney.isShootout = True
|
||||||
|
if mg['TURBO1'] is not None or mg['TURBO2'] is not None :
|
||||||
|
tourney.speed = "Turbo"
|
||||||
|
if mg['TOURNO'] is not None:
|
||||||
|
tourney.tourNo = mg['TOURNO']
|
||||||
|
else:
|
||||||
|
log.info( "Unable to get a valid Tournament ID -- File rejected" )
|
||||||
|
return False
|
||||||
|
if tourney.isMatrix:
|
||||||
|
if mg['MATCHNO'] is not None:
|
||||||
|
tourney.matrixMatchId = mg['MATCHNO']
|
||||||
|
else:
|
||||||
|
tourney.matrixMatchId = 0
|
||||||
|
|
||||||
|
|
||||||
|
# Get BuyIn/Fee
|
||||||
|
# Try and deal with the different cases that can occur :
|
||||||
|
# - No buy-in/fee can be on the first line (freerolls, Satellites sometimes ?, ...) but appears in the rest of the description ==> use this one
|
||||||
|
# - Buy-In/Fee from the first line differs from the rest of the description :
|
||||||
|
# * OK in matrix tourneys (global buy-in dispatched between the different matches)
|
||||||
|
# * NOK otherwise ==> issue a warning and store specific data as if were a Matrix Tourney
|
||||||
|
# - If no buy-in/fee can be found : assume it's a freeroll
|
||||||
|
m = self.re_TourneyBuyInFee.search(tourneyText)
|
||||||
|
if m is not None:
|
||||||
|
mg = m.groupdict()
|
||||||
|
if tourney.isMatrix :
|
||||||
|
if mg['BUYIN'] is not None:
|
||||||
|
tourney.subTourneyBuyin = mg['BUYIN']
|
||||||
|
tourney.subTourneyFee = 0
|
||||||
|
if mg['FEE'] is not None:
|
||||||
|
tourney.subTourneyFee = mg['FEE']
|
||||||
|
else :
|
||||||
|
if mg['BUYIN'] is not None:
|
||||||
|
if tourney.buyin is None:
|
||||||
|
tourney.buyin = mg['BUYIN']
|
||||||
|
else :
|
||||||
|
if mg['BUYIN'] != tourney.buyin:
|
||||||
|
log.error( "Conflict between buyins read in topline (%s) and in BuyIn field (%s)" % (touney.buyin, mg['BUYIN']) )
|
||||||
|
tourney.subTourneyBuyin = mg['BUYIN']
|
||||||
|
if mg['FEE'] is not None:
|
||||||
|
if tourney.fee is None:
|
||||||
|
tourney.fee = mg['FEE']
|
||||||
|
else :
|
||||||
|
if mg['FEE'] != tourney.fee:
|
||||||
|
log.error( "Conflict between fees read in topline (%s) and in BuyIn field (%s)" % (touney.fee, mg['FEE']) )
|
||||||
|
tourney.subTourneyFee = mg['FEE']
|
||||||
|
|
||||||
|
if tourney.buyin is None:
|
||||||
|
log.info( "Unable to affect a buyin to this tournament : assume it's a freeroll" )
|
||||||
|
tourney.buyin = 0
|
||||||
|
tourney.fee = 0
|
||||||
|
else:
|
||||||
|
if tourney.fee is None:
|
||||||
|
#print "Couldn't initialize fee, even though buyin went OK : assume there are no fees"
|
||||||
|
tourney.fee = 0
|
||||||
|
|
||||||
|
#Get single line infos
|
||||||
|
dictRegex = { "BUYINCHIPS" : self.re_TourneyBuyInChips,
|
||||||
|
"ENTRIES" : self.re_TourneyEntries,
|
||||||
|
"PRIZEPOOL" : self.re_TourneyPrizePool,
|
||||||
|
"REBUY_AMOUNT" : self.re_TourneyRebuyAmount,
|
||||||
|
"ADDON_AMOUNT" : self.re_TourneyAddOnAmount,
|
||||||
|
"REBUY_COUNT" : self.re_TourneyRebuyCount,
|
||||||
|
"ADDON_COUNT" : self.re_TourneyAddOnCount,
|
||||||
|
"REBUY_TOTAL" : self.re_TourneyRebuysTotal,
|
||||||
|
"ADDONS_TOTAL" : self.re_TourneyAddOnsTotal,
|
||||||
|
"REBUY_CHIPS" : self.re_TourneyRebuyChips,
|
||||||
|
"ADDON_CHIPS" : self.re_TourneyAddOnChips,
|
||||||
|
"STARTTIME" : self.re_TourneyTimeInfo,
|
||||||
|
"KO_BOUNTY_AMOUNT" : self.re_TourneyKOBounty,
|
||||||
|
"COUNT_KO" : self.re_TourneyCountKO
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
dictHolders = { "BUYINCHIPS" : "buyInChips",
|
||||||
|
"ENTRIES" : "entries",
|
||||||
|
"PRIZEPOOL" : "prizepool",
|
||||||
|
"REBUY_AMOUNT" : "rebuyAmount",
|
||||||
|
"ADDON_AMOUNT" : "addOnAmount",
|
||||||
|
"REBUY_COUNT" : "countRebuys",
|
||||||
|
"ADDON_COUNT" : "countAddOns",
|
||||||
|
"REBUY_TOTAL" : "totalRebuys",
|
||||||
|
"ADDONS_TOTAL" : "totalAddOns",
|
||||||
|
"REBUY_CHIPS" : "rebuyChips",
|
||||||
|
"ADDON_CHIPS" : "addOnChips",
|
||||||
|
"STARTTIME" : "starttime",
|
||||||
|
"KO_BOUNTY_AMOUNT" : "koBounty",
|
||||||
|
"COUNT_KO" : "countKO"
|
||||||
|
}
|
||||||
|
|
||||||
|
mg = {} # After the loop, mg will contain all the matching groups, including the ones that have not been used, like ENDTIME and IN-PROGRESS
|
||||||
|
for data in dictRegex:
|
||||||
|
m = dictRegex.get(data).search(tourneyText)
|
||||||
|
if m is not None:
|
||||||
|
mg.update(m.groupdict())
|
||||||
|
setattr(tourney, dictHolders[data], mg[data])
|
||||||
|
|
||||||
|
if mg['IN_PROGRESS'] is not None or mg['ENDTIME'] is not None:
|
||||||
|
# Assign endtime to tourney (if None, that's ok, it's because the tourney wans't over over when the summary file was produced)
|
||||||
|
tourney.endtime = mg['ENDTIME']
|
||||||
|
#print mg
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def getPlayersPositionsAndWinnings(self, tourney):
|
||||||
|
playersText = tourney.summaryText[1]
|
||||||
|
#print "Examine : '%s'" %(playersText)
|
||||||
|
m = self.re_TourneyPlayersSummary.finditer(playersText)
|
||||||
|
|
||||||
|
for a in m:
|
||||||
|
if a.group('PNAME') is not None and a.group('RANK') is not None:
|
||||||
|
if a.group('RANK') == "Still Playing":
|
||||||
|
rank = -1
|
||||||
|
else:
|
||||||
|
rank = Decimal(a.group('RANK'))
|
||||||
|
|
||||||
|
if a.group('WINNING') is not None:
|
||||||
|
winnings = a.group('WINNING')
|
||||||
|
else:
|
||||||
|
winnings = "0"
|
||||||
|
|
||||||
|
tourney.addPlayer(rank, a.group('PNAME'), winnings)
|
||||||
|
else:
|
||||||
|
print "Player finishing stats unreadable : %s" % a
|
||||||
|
|
||||||
|
# Deal with KO tournaments for hero winnings calculation
|
||||||
|
n = self.re_TourneyHeroFinishingP.search(playersText)
|
||||||
|
if n is not None:
|
||||||
|
heroName = n.group('HERO_NAME')
|
||||||
|
tourney.hero = heroName
|
||||||
|
# Is this really useful ?
|
||||||
|
if (tourney.finishPositions[heroName] != Decimal(n.group('HERO_FINISHING_POS'))):
|
||||||
|
print "Bad parsing : finish position incoherent : %s / %s" % (tourney.finishPositions[heroName], n.group('HERO_FINISHING_POS'))
|
||||||
|
if tourney.isKO:
|
||||||
|
#Update the winnings with the (KO amount) * (# of KO)
|
||||||
|
tourney.incrementPlayerWinnings(n.group('HERO_NAME'), Decimal(tourney.koBounty)*Decimal(tourney.countKO))
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
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/fulltilt/razz/FT20090223 Danville - $0.50-$1 Ante $0.10 - Limit Razz.txt")
|
parser.add_option("-i", "--input", dest="ipath", help="parse input hand history", default="regression-test-files/fulltilt/razz/FT20090223 Danville - $0.50-$1 Ante $0.10 - Limit Razz.txt")
|
||||||
|
@ -368,3 +627,6 @@ if __name__ == "__main__":
|
||||||
(options, args) = parser.parse_args()
|
(options, args) = parser.parse_args()
|
||||||
|
|
||||||
e = Fulltilt(in_path = options.ipath, out_path = options.opath, follow = options.follow)
|
e = Fulltilt(in_path = options.ipath, out_path = options.opath, follow = options.follow)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#agpl-3.0.txt in the docs folder of the package.
|
#agpl-3.0.txt in the docs folder of the package.
|
||||||
|
|
||||||
import Hand
|
import Hand
|
||||||
|
import Tourney
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
|
@ -53,6 +54,7 @@ class HandHistoryConverter():
|
||||||
# "utf_8" is more likely if there are funny characters
|
# "utf_8" is more likely if there are funny characters
|
||||||
codepage = "cp1252"
|
codepage = "cp1252"
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, in_path = '-', out_path = '-', follow=False, index=0, autostart=True):
|
def __init__(self, in_path = '-', out_path = '-', follow=False, index=0, autostart=True):
|
||||||
"""\
|
"""\
|
||||||
in_path (default '-' = sys.stdin)
|
in_path (default '-' = sys.stdin)
|
||||||
|
@ -67,6 +69,9 @@ follow : whether to tail -f the input"""
|
||||||
self.out_path = out_path
|
self.out_path = out_path
|
||||||
|
|
||||||
self.processedHands = []
|
self.processedHands = []
|
||||||
|
|
||||||
|
# Tourney object used to store TourneyInfo when called to deal with a Summary file
|
||||||
|
self.tourney = None
|
||||||
|
|
||||||
if in_path == '-':
|
if in_path == '-':
|
||||||
self.in_fh = sys.stdin
|
self.in_fh = sys.stdin
|
||||||
|
@ -88,6 +93,10 @@ follow : whether to tail -f the input"""
|
||||||
self.follow = follow
|
self.follow = follow
|
||||||
self.compiledPlayers = set()
|
self.compiledPlayers = set()
|
||||||
self.maxseats = 10
|
self.maxseats = 10
|
||||||
|
|
||||||
|
self.status = True
|
||||||
|
|
||||||
|
self.parsedObjectType = "HH" #default behaviour : parsing HH files, can be "Summary" if the parsing encounters a Summary File
|
||||||
|
|
||||||
if autostart:
|
if autostart:
|
||||||
self.start()
|
self.start()
|
||||||
|
@ -116,6 +125,7 @@ Otherwise, finish at EOF.
|
||||||
numHands = 0
|
numHands = 0
|
||||||
numErrors = 0
|
numErrors = 0
|
||||||
if self.follow:
|
if self.follow:
|
||||||
|
#TODO: See how summary files can be handled on the fly (here they should be rejected as before)
|
||||||
log.info("Tailing '%s'" % self.in_path)
|
log.info("Tailing '%s'" % self.in_path)
|
||||||
for handText in self.tailHands():
|
for handText in self.tailHands():
|
||||||
try:
|
try:
|
||||||
|
@ -128,16 +138,28 @@ Otherwise, finish at EOF.
|
||||||
else:
|
else:
|
||||||
handsList = self.allHandsAsList()
|
handsList = self.allHandsAsList()
|
||||||
log.info("Parsing %d hands" % len(handsList))
|
log.info("Parsing %d hands" % len(handsList))
|
||||||
for handText in handsList:
|
# Determine if we're dealing with a HH file or a Summary file
|
||||||
try:
|
if self.isSummary(handsList[0]) == False:
|
||||||
self.processedHands.append(self.processHand(handText))
|
self.parsedObjectType = "HH"
|
||||||
except FpdbParseError, e:
|
for handText in handsList:
|
||||||
numErrors+=1
|
try:
|
||||||
log.warning("Failed to convert hand %s" % e.hid)
|
self.processedHands.append(self.processHand(handText))
|
||||||
log.debug(handText)
|
except FpdbParseError, e:
|
||||||
numHands = len(handsList)
|
numErrors+=1
|
||||||
endtime = time.time()
|
log.warning("Failed to convert hand %s" % e.hid)
|
||||||
log.info("Read %d hands (%d failed) in %.3f seconds" % (numHands, numErrors, endtime - starttime))
|
log.debug(handText)
|
||||||
|
numHands = len(handsList)
|
||||||
|
endtime = time.time()
|
||||||
|
log.info("Read %d hands (%d failed) in %.3f seconds" % (numHands, numErrors, endtime - starttime))
|
||||||
|
else:
|
||||||
|
self.parsedObjectType = "Summary"
|
||||||
|
summaryParsingStatus = self.readSummaryInfo(handsList)
|
||||||
|
endtime = time.time()
|
||||||
|
if summaryParsingStatus :
|
||||||
|
log.info("Summary file '%s' correctly parsed (took %.3f seconds)" % (self.in_path, endtime - starttime))
|
||||||
|
else :
|
||||||
|
log.warning("Error converting summary file '%s' (took %.3f seconds)" % (self.in_path, endtime - starttime))
|
||||||
|
|
||||||
except IOError, ioe:
|
except IOError, ioe:
|
||||||
log.exception("Error converting '%s'" % self.in_path)
|
log.exception("Error converting '%s'" % self.in_path)
|
||||||
finally:
|
finally:
|
||||||
|
@ -421,7 +443,7 @@ or None if we fail to get the info """
|
||||||
|
|
||||||
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 self.status
|
||||||
|
|
||||||
def getProcessedHands(self):
|
def getProcessedHands(self):
|
||||||
return self.processedHands
|
return self.processedHands
|
||||||
|
@ -431,3 +453,15 @@ or None if we fail to get the info """
|
||||||
|
|
||||||
def getLastCharacterRead(self):
|
def getLastCharacterRead(self):
|
||||||
return self.index
|
return self.index
|
||||||
|
|
||||||
|
def isSummary(self, topline):
|
||||||
|
return " Tournament Summary " in topline
|
||||||
|
|
||||||
|
def getParsedObjectType(self):
|
||||||
|
return self.parsedObjectType
|
||||||
|
|
||||||
|
#returns a status (True/False) indicating wether the parsing could be done correctly or not
|
||||||
|
def readSummaryInfo(self, summaryInfoList): abstract
|
||||||
|
|
||||||
|
def getTourney(self):
|
||||||
|
return self.tourney
|
||||||
|
|
462
pyfpdb/Tourney.py
Normal file
462
pyfpdb/Tourney.py
Normal file
|
@ -0,0 +1,462 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
#Copyright 2009 Stephane Alessio
|
||||||
|
#This program is free software: you can redistribute it and/or modify
|
||||||
|
#it under the terms of the GNU Affero General Public License as published by
|
||||||
|
#the Free Software Foundation, version 3 of the License.
|
||||||
|
#
|
||||||
|
#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 Affero General Public License
|
||||||
|
#along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#In the "official" distribution you can find the license in
|
||||||
|
#agpl-3.0.txt in the docs folder of the package.
|
||||||
|
|
||||||
|
# TODO: check to keep only the needed modules
|
||||||
|
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import traceback
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
from decimal import Decimal
|
||||||
|
import operator
|
||||||
|
import time,datetime
|
||||||
|
from copy import deepcopy
|
||||||
|
from Exceptions import *
|
||||||
|
import pprint
|
||||||
|
import DerivedStats
|
||||||
|
import Card
|
||||||
|
|
||||||
|
log = logging.getLogger("parser")
|
||||||
|
|
||||||
|
class Tourney(object):
|
||||||
|
|
||||||
|
################################################################
|
||||||
|
# Class Variables
|
||||||
|
UPS = {'a':'A', 't':'T', 'j':'J', 'q':'Q', 'k':'K', 'S':'s', 'C':'c', 'H':'h', 'D':'d'} # SAL- TO KEEP ??
|
||||||
|
LCS = {'H':'h', 'D':'d', 'C':'c', 'S':'s'} # SAL- TO KEEP ??
|
||||||
|
SYMBOL = {'USD': '$', 'EUR': u'$', 'T$': '', 'play': ''}
|
||||||
|
MS = {'horse' : 'HORSE', '8game' : '8-Game', 'hose' : 'HOSE', 'ha': 'HA'}
|
||||||
|
SITEIDS = {'Fulltilt':1, 'PokerStars':2, 'Everleaf':3, 'Win2day':4, 'OnGame':5, 'UltimateBet':6, 'Betfair':7, 'Absolute':8, 'PartyPoker':9 }
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, sitename, gametype, summaryText, builtFrom = "HHC"):
|
||||||
|
print "Tourney.__init__"
|
||||||
|
self.sitename = sitename
|
||||||
|
self.siteId = self.SITEIDS[sitename]
|
||||||
|
self.gametype = gametype
|
||||||
|
self.starttime = None
|
||||||
|
self.endtime = None
|
||||||
|
self.summaryText = summaryText
|
||||||
|
self.tourneyName = None
|
||||||
|
self.tourNo = None
|
||||||
|
self.buyin = None
|
||||||
|
self.fee = None # the Database code is looking for this one .. ?
|
||||||
|
self.hero = None
|
||||||
|
self.maxseats = None
|
||||||
|
self.entries = 0
|
||||||
|
self.speed = "Normal"
|
||||||
|
self.prizepool = None # Make it a dict in order to deal (eventually later) with non-money winnings : {'MONEY' : amount, 'OTHER' : Value ??}
|
||||||
|
self.buyInChips = None
|
||||||
|
self.mixed = None
|
||||||
|
self.isRebuy = False
|
||||||
|
self.isKO = False
|
||||||
|
self.isHU = False
|
||||||
|
self.isMatrix = False
|
||||||
|
self.isShootout = False
|
||||||
|
self.matrixMatchId = None # For Matrix tourneys : 1-4 => match tables (traditionnal), 0 => Positional winnings info
|
||||||
|
self.subTourneyBuyin = None
|
||||||
|
self.subTourneyFee = None
|
||||||
|
self.rebuyChips = 0
|
||||||
|
self.addOnChips = 0
|
||||||
|
self.countRebuys = 0
|
||||||
|
self.countAddOns = 0
|
||||||
|
self.rebuyAmount = 0
|
||||||
|
self.addOnAmount = 0
|
||||||
|
self.totalRebuys = 0
|
||||||
|
self.totalAddOns = 0
|
||||||
|
self.koBounty = 0
|
||||||
|
self.countKO = 0 #To use for winnings calculation which is not counted in the rest of the summary file
|
||||||
|
self.players = []
|
||||||
|
|
||||||
|
# Collections indexed by player names
|
||||||
|
self.finishPositions = {}
|
||||||
|
self.winnings = {}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# currency symbol for this summary
|
||||||
|
self.sym = None
|
||||||
|
#self.sym = self.SYMBOL[self.gametype['currency']] # save typing! delete this attr when done
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
#TODO : Update
|
||||||
|
vars = ( ("SITE", self.sitename),
|
||||||
|
("START TIME", self.starttime),
|
||||||
|
("END TIME", self.endtime),
|
||||||
|
("TOURNEY NAME", self.tourneyName),
|
||||||
|
("TOURNEY NO", self.tourNo),
|
||||||
|
("BUYIN", self.buyin),
|
||||||
|
("FEE", self.fee),
|
||||||
|
("HERO", self.hero),
|
||||||
|
("MAXSEATS", self.maxseats),
|
||||||
|
("ENTRIES", self.entries),
|
||||||
|
("SPEED", self.speed),
|
||||||
|
("PRIZE POOL", self.prizepool),
|
||||||
|
("STARTING CHIP COUNT", self.buyInChips),
|
||||||
|
("MIXED", self.mixed),
|
||||||
|
("REBUY ADDON", self.isRebuy),
|
||||||
|
("KO", self.isKO),
|
||||||
|
("HU", self.isHU),
|
||||||
|
("MATRIX", self.isMatrix),
|
||||||
|
("SHOOTOUT", self.isShootout),
|
||||||
|
("MATRIX MATCH ID", self.matrixMatchId),
|
||||||
|
("SUB TOURNEY BUY IN", self.subTourneyBuyin),
|
||||||
|
("SUB TOURNEY FEE", self.subTourneyFee),
|
||||||
|
("REBUY CHIPS", self.rebuyChips),
|
||||||
|
("ADDON CHIPS", self.addOnChips),
|
||||||
|
("REBUY AMOUNT", self.rebuyAmount),
|
||||||
|
("ADDON AMOUNT", self.addOnAmount),
|
||||||
|
("COUNT REBUYS", self.countRebuys),
|
||||||
|
("COUNT ADDONS", self.countAddOns),
|
||||||
|
("NB REBUYS", self.countRebuys),
|
||||||
|
("NB ADDONS", self.countAddOns),
|
||||||
|
("TOTAL REBUYS", self.totalRebuys),
|
||||||
|
("TOTAL ADDONS", self.totalAddOns),
|
||||||
|
("KO BOUNTY", self.koBounty),
|
||||||
|
("NB OF KO", self.countKO)
|
||||||
|
)
|
||||||
|
|
||||||
|
structs = ( ("GAMETYPE", self.gametype),
|
||||||
|
("PLAYERS", self.players),
|
||||||
|
("POSITIONS", self.finishPositions),
|
||||||
|
("WINNINGS", self.winnings),
|
||||||
|
)
|
||||||
|
str = ''
|
||||||
|
for (name, var) in vars:
|
||||||
|
str = str + "\n%s = " % name + pprint.pformat(var)
|
||||||
|
|
||||||
|
for (name, struct) in structs:
|
||||||
|
str = str + "\n%s =\n" % name + pprint.pformat(struct, 4)
|
||||||
|
return str
|
||||||
|
|
||||||
|
def getSummaryText(self):
|
||||||
|
return self.summaryText
|
||||||
|
|
||||||
|
def prepInsert(self, db):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def insert(self, db):
|
||||||
|
print "TODO: Insert Tourney in DB"
|
||||||
|
# First : check all needed info is filled in the object, especially for the initial select
|
||||||
|
|
||||||
|
# Notes on DB Insert
|
||||||
|
# Some identified issues for tourneys already in the DB (which occurs when the HH file is parsed and inserted before the Summary)
|
||||||
|
# Be careful on updates that could make the HH import not match the tourney inserted from a previous summary import !!
|
||||||
|
# BuyIn/Fee can be at 0/0 => match may not be easy
|
||||||
|
# Only one existinf Tourney entry for Matrix Tourneys, but multiple Summary files
|
||||||
|
# Starttime may not match the one in the Summary file : HH = time of the first Hand / could be slighltly different from the one in the summary file
|
||||||
|
# Note: If the TourneyNo could be a unique id .... this would really be a relief to deal with matrix matches ==> Ask on the IRC / Ask Fulltilt ??
|
||||||
|
|
||||||
|
stored = 0
|
||||||
|
duplicates = 0
|
||||||
|
partial = 0
|
||||||
|
errors = 0
|
||||||
|
ttime = 0
|
||||||
|
return (stored, duplicates, partial, errors, ttime)
|
||||||
|
|
||||||
|
|
||||||
|
def old_insert_from_Hand(self, db):
|
||||||
|
""" Function to insert Hand into database
|
||||||
|
Should not commit, and do minimal selects. Callers may want to cache commits
|
||||||
|
db: a connected fpdb_db object"""
|
||||||
|
# TODO:
|
||||||
|
# Players - base playerid and siteid tuple
|
||||||
|
sqlids = db.getSqlPlayerIDs([p[1] for p in self.players], self.siteId)
|
||||||
|
|
||||||
|
#Gametypes
|
||||||
|
gtid = db.getGameTypeId(self.siteId, self.gametype)
|
||||||
|
|
||||||
|
# HudCache data to come from DerivedStats class
|
||||||
|
# HandsActions - all actions for all players for all streets - self.actions
|
||||||
|
# Hands - Summary information of hand indexed by handId - gameinfo
|
||||||
|
#This should be moved to prepInsert
|
||||||
|
hh = {}
|
||||||
|
hh['siteHandNo'] = self.handid
|
||||||
|
hh['handStart'] = self.starttime
|
||||||
|
hh['gameTypeId'] = gtid
|
||||||
|
# seats TINYINT NOT NULL,
|
||||||
|
hh['tableName'] = self.tablename
|
||||||
|
hh['maxSeats'] = self.maxseats
|
||||||
|
hh['seats'] = len(sqlids)
|
||||||
|
# Flop turn and river may all be empty - add (likely) too many elements and trim with range
|
||||||
|
boardcards = self.board['FLOP'] + self.board['TURN'] + self.board['RIVER'] + [u'0x', u'0x', u'0x', u'0x', u'0x']
|
||||||
|
cards = [Card.encodeCard(c) for c in boardcards[0:5]]
|
||||||
|
hh['boardcard1'] = cards[0]
|
||||||
|
hh['boardcard2'] = cards[1]
|
||||||
|
hh['boardcard3'] = cards[2]
|
||||||
|
hh['boardcard4'] = cards[3]
|
||||||
|
hh['boardcard5'] = cards[4]
|
||||||
|
|
||||||
|
# texture smallint,
|
||||||
|
# playersVpi SMALLINT NOT NULL, /* num of players vpi */
|
||||||
|
# Needs to be recorded
|
||||||
|
# playersAtStreet1 SMALLINT NOT NULL, /* num of players seeing flop/street4 */
|
||||||
|
# Needs to be recorded
|
||||||
|
# playersAtStreet2 SMALLINT NOT NULL,
|
||||||
|
# Needs to be recorded
|
||||||
|
# playersAtStreet3 SMALLINT NOT NULL,
|
||||||
|
# Needs to be recorded
|
||||||
|
# playersAtStreet4 SMALLINT NOT NULL,
|
||||||
|
# Needs to be recorded
|
||||||
|
# playersAtShowdown SMALLINT NOT NULL,
|
||||||
|
# Needs to be recorded
|
||||||
|
# street0Raises TINYINT NOT NULL, /* num small bets paid to see flop/street4, including blind */
|
||||||
|
# Needs to be recorded
|
||||||
|
# street1Raises TINYINT NOT NULL, /* num small bets paid to see turn/street5 */
|
||||||
|
# Needs to be recorded
|
||||||
|
# street2Raises TINYINT NOT NULL, /* num big bets paid to see river/street6 */
|
||||||
|
# Needs to be recorded
|
||||||
|
# street3Raises TINYINT NOT NULL, /* num big bets paid to see sd/street7 */
|
||||||
|
# Needs to be recorded
|
||||||
|
# street4Raises TINYINT NOT NULL, /* num big bets paid to see showdown */
|
||||||
|
# Needs to be recorded
|
||||||
|
|
||||||
|
#print "DEBUG: self.getStreetTotals = (%s, %s, %s, %s, %s)" % self.getStreetTotals()
|
||||||
|
#FIXME: Pot size still in decimal, needs to be converted to cents
|
||||||
|
(hh['street1Pot'], hh['street2Pot'], hh['street3Pot'], hh['street4Pot'], hh['showdownPot']) = self.getStreetTotals()
|
||||||
|
|
||||||
|
# comment TEXT,
|
||||||
|
# commentTs DATETIME
|
||||||
|
#print hh
|
||||||
|
handid = db.storeHand(hh)
|
||||||
|
# HandsPlayers - ? ... Do we fix winnings?
|
||||||
|
# Tourneys ?
|
||||||
|
# TourneysPlayers
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
def select(self, tourneyId):
|
||||||
|
""" Function to create Tourney object from database """
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def addPlayer(self, rank, name, winnings):
|
||||||
|
"""\
|
||||||
|
Adds a player to the tourney, and initialises data structures indexed by player.
|
||||||
|
rank (int) indicating the finishing rank (can be -1 if unknown)
|
||||||
|
name (string) player name
|
||||||
|
winnings (string) the money the player ended the tourney with (can be 0, or -1 if unknown)
|
||||||
|
"""
|
||||||
|
log.debug("addPlayer: rank:%s - name : '%s' - Winnings (%s)" % (rank, name, winnings))
|
||||||
|
winnings = re.sub(u',', u'', winnings) #some sites have commas
|
||||||
|
self.players.append(name)
|
||||||
|
self.finishPositions.update( { name : Decimal(rank) } )
|
||||||
|
self.winnings.update( { name : Decimal(winnings) } )
|
||||||
|
|
||||||
|
|
||||||
|
def incrementPlayerWinnings(self, name, additionnalWinnings):
|
||||||
|
log.debug("incrementPlayerWinnings: name : '%s' - Add Winnings (%s)" % (name, additionnalWinnings))
|
||||||
|
oldWins = 0
|
||||||
|
if self.winnings.has_key(name):
|
||||||
|
oldWins = self.winnings[name]
|
||||||
|
else:
|
||||||
|
self.players.append([-1, name, 0])
|
||||||
|
|
||||||
|
self.winnings[name] = oldWins + Decimal(additionnalWinnings)
|
||||||
|
|
||||||
|
|
||||||
|
def calculatePayinAmount(self):
|
||||||
|
return self.buyin + self.fee + (self.rebuyAmount * self.countRebuys) + (self.addOnAmount * self.countAddOns )
|
||||||
|
|
||||||
|
|
||||||
|
def checkPlayerExists(self,player):
|
||||||
|
if player not in [p[1] for p in self.players]:
|
||||||
|
print "checkPlayerExists", player, "fail"
|
||||||
|
raise FpdbParseError
|
||||||
|
|
||||||
|
|
||||||
|
def getGameTypeAsString(self):
|
||||||
|
"""\
|
||||||
|
Map the tuple self.gametype onto the pokerstars string describing it
|
||||||
|
"""
|
||||||
|
# currently it appears to be something like ["ring", "hold", "nl", sb, bb]:
|
||||||
|
gs = {"holdem" : "Hold'em",
|
||||||
|
"omahahi" : "Omaha",
|
||||||
|
"omahahilo" : "Omaha Hi/Lo",
|
||||||
|
"razz" : "Razz",
|
||||||
|
"studhi" : "7 Card Stud",
|
||||||
|
"studhilo" : "7 Card Stud Hi/Lo",
|
||||||
|
"fivedraw" : "5 Card Draw",
|
||||||
|
"27_1draw" : "FIXME",
|
||||||
|
"27_3draw" : "Triple Draw 2-7 Lowball",
|
||||||
|
"badugi" : "Badugi"
|
||||||
|
}
|
||||||
|
ls = {"nl" : "No Limit",
|
||||||
|
"pl" : "Pot Limit",
|
||||||
|
"fl" : "Limit",
|
||||||
|
"cn" : "Cap No Limit",
|
||||||
|
"cp" : "Cap Pot Limit"
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug("gametype: %s" %(self.gametype))
|
||||||
|
retstring = "%s %s" %(gs[self.gametype['category']], ls[self.gametype['limitType']])
|
||||||
|
return retstring
|
||||||
|
|
||||||
|
|
||||||
|
def writeSummary(self, fh=sys.__stdout__):
|
||||||
|
print >>fh, "Override me"
|
||||||
|
|
||||||
|
def printSummary(self):
|
||||||
|
self.writeSummary(sys.stdout)
|
||||||
|
|
||||||
|
|
||||||
|
def assemble(cnxn, tourneyId):
|
||||||
|
# TODO !!
|
||||||
|
c = cnxn.cursor()
|
||||||
|
|
||||||
|
# We need at least sitename, gametype, handid
|
||||||
|
# for the Hand.__init__
|
||||||
|
c.execute("""
|
||||||
|
select
|
||||||
|
s.name,
|
||||||
|
g.category,
|
||||||
|
g.base,
|
||||||
|
g.type,
|
||||||
|
g.limitType,
|
||||||
|
g.hilo,
|
||||||
|
round(g.smallBlind / 100.0,2),
|
||||||
|
round(g.bigBlind / 100.0,2),
|
||||||
|
round(g.smallBet / 100.0,2),
|
||||||
|
round(g.bigBet / 100.0,2),
|
||||||
|
s.currency,
|
||||||
|
h.boardcard1,
|
||||||
|
h.boardcard2,
|
||||||
|
h.boardcard3,
|
||||||
|
h.boardcard4,
|
||||||
|
h.boardcard5
|
||||||
|
from
|
||||||
|
hands as h,
|
||||||
|
sites as s,
|
||||||
|
gametypes as g,
|
||||||
|
handsplayers as hp,
|
||||||
|
players as p
|
||||||
|
where
|
||||||
|
h.id = %(handid)s
|
||||||
|
and g.id = h.gametypeid
|
||||||
|
and hp.handid = h.id
|
||||||
|
and p.id = hp.playerid
|
||||||
|
and s.id = p.siteid
|
||||||
|
limit 1""", {'handid':handid})
|
||||||
|
#TODO: siteid should be in hands table - we took the scenic route through players here.
|
||||||
|
res = c.fetchone()
|
||||||
|
gametype = {'category':res[1],'base':res[2],'type':res[3],'limitType':res[4],'hilo':res[5],'sb':res[6],'bb':res[7], 'currency':res[10]}
|
||||||
|
h = HoldemOmahaHand(hhc = None, sitename=res[0], gametype = gametype, handText=None, builtFrom = "DB", handid=handid)
|
||||||
|
cards = map(Card.valueSuitFromCard, res[11:16] )
|
||||||
|
if cards[0]:
|
||||||
|
h.setCommunityCards('FLOP', cards[0:3])
|
||||||
|
if cards[3]:
|
||||||
|
h.setCommunityCards('TURN', [cards[3]])
|
||||||
|
if cards[4]:
|
||||||
|
h.setCommunityCards('RIVER', [cards[4]])
|
||||||
|
#[Card.valueSuitFromCard(x) for x in cards]
|
||||||
|
|
||||||
|
# HandInfo : HID, TABLE
|
||||||
|
# BUTTON - why is this treated specially in Hand?
|
||||||
|
# answer: it is written out in hand histories
|
||||||
|
# still, I think we should record all the active seat positions in a seat_order array
|
||||||
|
c.execute("""
|
||||||
|
SELECT
|
||||||
|
h.sitehandno as hid,
|
||||||
|
h.tablename as table,
|
||||||
|
h.handstart as starttime
|
||||||
|
FROM
|
||||||
|
hands as h
|
||||||
|
WHERE h.id = %(handid)s
|
||||||
|
""", {'handid':handid})
|
||||||
|
res = c.fetchone()
|
||||||
|
h.handid = res[0]
|
||||||
|
h.tablename = res[1]
|
||||||
|
h.starttime = res[2] # automatically a datetime
|
||||||
|
|
||||||
|
# PlayerStacks
|
||||||
|
c.execute("""
|
||||||
|
SELECT
|
||||||
|
hp.seatno,
|
||||||
|
round(hp.winnings / 100.0,2) as winnings,
|
||||||
|
p.name,
|
||||||
|
round(hp.startcash / 100.0,2) as chips,
|
||||||
|
hp.card1,hp.card2,
|
||||||
|
hp.position
|
||||||
|
FROM
|
||||||
|
handsplayers as hp,
|
||||||
|
players as p
|
||||||
|
WHERE
|
||||||
|
hp.handid = %(handid)s
|
||||||
|
and p.id = hp.playerid
|
||||||
|
""", {'handid':handid})
|
||||||
|
for (seat, winnings, name, chips, card1,card2, position) in c.fetchall():
|
||||||
|
h.addPlayer(seat,name,chips)
|
||||||
|
if card1 and card2:
|
||||||
|
h.addHoleCards(map(Card.valueSuitFromCard, (card1,card2)), name, dealt=True)
|
||||||
|
if winnings > 0:
|
||||||
|
h.addCollectPot(name, winnings)
|
||||||
|
if position == 'B':
|
||||||
|
h.buttonpos = seat
|
||||||
|
|
||||||
|
|
||||||
|
# actions
|
||||||
|
c.execute("""
|
||||||
|
SELECT
|
||||||
|
(ha.street,ha.actionno) as actnum,
|
||||||
|
p.name,
|
||||||
|
ha.street,
|
||||||
|
ha.action,
|
||||||
|
ha.allin,
|
||||||
|
round(ha.amount / 100.0,2)
|
||||||
|
FROM
|
||||||
|
handsplayers as hp,
|
||||||
|
handsactions as ha,
|
||||||
|
players as p
|
||||||
|
WHERE
|
||||||
|
hp.handid = %(handid)s
|
||||||
|
and ha.handsplayerid = hp.id
|
||||||
|
and p.id = hp.playerid
|
||||||
|
ORDER BY
|
||||||
|
ha.street,ha.actionno
|
||||||
|
""", {'handid':handid})
|
||||||
|
res = c.fetchall()
|
||||||
|
for (actnum,player, streetnum, act, allin, amount) in res:
|
||||||
|
act=act.strip()
|
||||||
|
street = h.allStreets[streetnum+1]
|
||||||
|
if act==u'blind':
|
||||||
|
h.addBlind(player, 'big blind', amount)
|
||||||
|
# TODO: The type of blind is not recorded in the DB.
|
||||||
|
# TODO: preflop street name anomalies in Hand
|
||||||
|
elif act==u'fold':
|
||||||
|
h.addFold(street,player)
|
||||||
|
elif act==u'call':
|
||||||
|
h.addCall(street,player,amount)
|
||||||
|
elif act==u'bet':
|
||||||
|
h.addBet(street,player,amount)
|
||||||
|
elif act==u'check':
|
||||||
|
h.addCheck(street,player)
|
||||||
|
elif act==u'unbet':
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
print act, player, streetnum, allin, amount
|
||||||
|
# TODO : other actions
|
||||||
|
|
||||||
|
#hhc.readShowdownActions(self)
|
||||||
|
#hc.readShownCards(self)
|
||||||
|
h.totalPot()
|
||||||
|
h.rake = h.totalpot - h.totalcollected
|
||||||
|
|
||||||
|
|
||||||
|
return h
|
||||||
|
|
|
@ -396,29 +396,45 @@ class Importer:
|
||||||
out_path = os.path.join(hhdir, "x"+strftime("%d-%m-%y")+os.path.basename(file))
|
out_path = os.path.join(hhdir, "x"+strftime("%d-%m-%y")+os.path.basename(file))
|
||||||
|
|
||||||
filter_name = filter.replace("ToFpdb", "")
|
filter_name = filter.replace("ToFpdb", "")
|
||||||
|
mod = __import__(filter)
|
||||||
mod = __import__(filter)
|
obj = getattr(mod, filter_name, None)
|
||||||
obj = getattr(mod, filter_name, None)
|
if callable(obj):
|
||||||
if callable(obj):
|
hhc = obj(in_path = file, out_path = out_path, index = 0) # Index into file 0 until changeover
|
||||||
hhc = obj(in_path = file, out_path = out_path, index = 0) # Index into file 0 until changeover
|
if hhc.getParsedObjectType() == "HH":
|
||||||
if(hhc.getStatus() and self.NEWIMPORT == False):
|
if(hhc.getStatus() and self.NEWIMPORT == False):
|
||||||
(stored, duplicates, partial, errors, ttime) = self.import_fpdb_file(db, out_path, site, q)
|
(stored, duplicates, partial, errors, ttime) = self.import_fpdb_file(db, out_path, site, q)
|
||||||
elif (hhc.getStatus() and self.NEWIMPORT == True):
|
elif (hhc.getStatus() and self.NEWIMPORT == True):
|
||||||
#This code doesn't do anything yet
|
#This code doesn't do anything yet
|
||||||
handlist = hhc.getProcessedHands()
|
handlist = hhc.getProcessedHands()
|
||||||
self.pos_in_file[file] = hhc.getLastCharacterRead()
|
self.pos_in_file[file] = hhc.getLastCharacterRead()
|
||||||
|
|
||||||
for hand in handlist:
|
for hand in handlist:
|
||||||
#hand.prepInsert()
|
#hand.prepInsert()
|
||||||
hand.insert(self.database)
|
hand.insert(self.database)
|
||||||
else:
|
else:
|
||||||
# conversion didn't work
|
# conversion didn't work
|
||||||
# TODO: appropriate response?
|
# TODO: appropriate response?
|
||||||
return (0, 0, 0, 1, 0, -1)
|
return (0, 0, 0, 1, 0, -1)
|
||||||
else:
|
elif hhc.getParsedObjectType() == "Summary":
|
||||||
print "Unknown filter filter_name:'%s' in filter:'%s'" %(filter_name, filter)
|
if(hhc.getStatus()):
|
||||||
return (0, 0, 0, 1, 0, -1)
|
tourney = hhc.getTourney()
|
||||||
|
#print tourney
|
||||||
|
#tourney.prepInsert()
|
||||||
|
(stored, duplicates, partial, errors, ttime) = tourney.insert(self.database)
|
||||||
|
return (stored, duplicates, partial, errors, ttime)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# conversion didn't work
|
||||||
|
# Could just be the parsing of a non summary file (classic HH file)
|
||||||
|
return (0, 0, 0, 0, 0)
|
||||||
|
else:
|
||||||
|
print "Unknown objects parsed by HHC :'%s'" %(hhc.getObjectTypeRead())
|
||||||
|
return (0, 0, 0, 1, 0, -1)
|
||||||
|
|
||||||
|
else:
|
||||||
|
print "Unknown filter filter_name:'%s' in filter:'%s'" %(filter_name, filter)
|
||||||
|
return (0, 0, 0, 1, 0, -1)
|
||||||
|
|
||||||
#This will barf if conv.getStatus != True
|
#This will barf if conv.getStatus != True
|
||||||
return (stored, duplicates, partial, errors, ttime)
|
return (stored, duplicates, partial, errors, ttime)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user