Merge branch 'master' of git://git.assembla.com/free_poker_tools
This commit is contained in:
commit
be116905c4
|
@ -73,7 +73,7 @@ class Absolute(HandHistoryConverter):
|
||||||
#self.re_PostSB = re.compile(ur"^%s: posts small blind \[(?:\$| €|) (?P<SB>[.0-9]+)" % player_re, re.MULTILINE)
|
#self.re_PostSB = re.compile(ur"^%s: posts small blind \[(?:\$| €|) (?P<SB>[.0-9]+)" % player_re, re.MULTILINE)
|
||||||
#self.re_PostBB = re.compile(ur"^%s: posts big blind \[(?:\$| €|) (?P<BB>[.0-9]+)" % player_re, re.MULTILINE)
|
#self.re_PostBB = re.compile(ur"^%s: posts big blind \[(?:\$| €|) (?P<BB>[.0-9]+)" % player_re, re.MULTILINE)
|
||||||
#self.re_PostBoth = re.compile(ur"^%s: posts both blinds \[(?:\$| €|) (?P<SBBB>[.0-9]+)" % player_re, re.MULTILINE)
|
#self.re_PostBoth = re.compile(ur"^%s: posts both blinds \[(?:\$| €|) (?P<SBBB>[.0-9]+)" % player_re, re.MULTILINE)
|
||||||
#self.re_Antes = re.compile(ur"^%s: posts ante \[(?:\$| €|) (?P<ANTE>[.0-9]+)" % player_re, re.MULTILINE)
|
self.re_Antes = re.compile(ur"^%s - Ante \[(?:\$| €|)(?P<ANTE>[.0-9]+)" % player_re, re.MULTILINE)
|
||||||
#self.re_BringIn = re.compile(ur"^%s posts bring-in (?:\$| €|)(?P<BRINGIN>[.0-9]+)\." % player_re, re.MULTILINE)
|
#self.re_BringIn = re.compile(ur"^%s posts bring-in (?:\$| €|)(?P<BRINGIN>[.0-9]+)\." % player_re, re.MULTILINE)
|
||||||
self.re_HeroCards = re.compile(ur"^Dealt to %s \[(?P<CARDS>.*)\]" % player_re, re.MULTILINE)
|
self.re_HeroCards = re.compile(ur"^Dealt to %s \[(?P<CARDS>.*)\]" % player_re, re.MULTILINE)
|
||||||
#self.re_Action = re.compile(ur"^%s(?P<ATYPE>: bets| checks| raises| calls| folds)(\s\[(?:\$| €|) (?P<BET>[.\d]+) (USD|EUR|)\])?" % player_re, re.MULTILINE)
|
#self.re_Action = re.compile(ur"^%s(?P<ATYPE>: bets| checks| raises| calls| folds)(\s\[(?:\$| €|) (?P<BET>[.\d]+) (USD|EUR|)\])?" % player_re, re.MULTILINE)
|
||||||
|
|
|
@ -47,13 +47,14 @@ 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
|
||||||
\$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+)\s(Ante\s\$?(?P<ANTE>[.0-9]+)\s)?-\s
|
\$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+)\s(Ante\s\$?(?P<ANTE>[.0-9]+)\s)?-\s
|
||||||
(?P<GAMETYPE>[a-zA-Z\/\'\s]+)\s-\s
|
(?P<GAMETYPE>[a-zA-Z\/\'\s]+)\s-\s
|
||||||
(?P<DATETIME>.*?)\n
|
(?P<DATETIME>\d+:\d+:\d+\s\w+\s-\s\d+/\d+/\d+)\s?
|
||||||
|
(?P<PARTIAL>\(partial\))?\n
|
||||||
(?:.*?\n(?P<CANCELLED>Hand\s\#(?P=HID)\shas\sbeen\scanceled))?
|
(?:.*?\n(?P<CANCELLED>Hand\s\#(?P=HID)\shas\sbeen\scanceled))?
|
||||||
''', re.VERBOSE|re.DOTALL)
|
''', re.VERBOSE|re.DOTALL)
|
||||||
re_Button = re.compile('^The button is in seat #(?P<BUTTON>\d+)', re.MULTILINE)
|
re_Button = re.compile('^The button is in seat #(?P<BUTTON>\d+)', re.MULTILINE)
|
||||||
|
@ -61,6 +62,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)
|
||||||
|
@ -116,7 +157,6 @@ class Fulltilt(HandHistoryConverter):
|
||||||
if not m:
|
if not m:
|
||||||
return None
|
return None
|
||||||
mg = m.groupdict()
|
mg = m.groupdict()
|
||||||
print mg
|
|
||||||
# translations from captured groups to our info strings
|
# translations from captured groups to our info strings
|
||||||
limits = { 'No Limit':'nl', 'Pot Limit':'pl', 'Limit':'fl' }
|
limits = { 'No Limit':'nl', 'Pot Limit':'pl', 'Limit':'fl' }
|
||||||
games = { # base, category
|
games = { # base, category
|
||||||
|
@ -151,7 +191,7 @@ class Fulltilt(HandHistoryConverter):
|
||||||
hand.tablename = m.group('TABLE')
|
hand.tablename = m.group('TABLE')
|
||||||
hand.starttime = datetime.datetime.strptime(m.group('DATETIME'), "%H:%M:%S ET - %Y/%m/%d")
|
hand.starttime = datetime.datetime.strptime(m.group('DATETIME'), "%H:%M:%S ET - %Y/%m/%d")
|
||||||
|
|
||||||
if m.group("CANCELLED"):
|
if m.group("CANCELLED") or m.group("PARTIAL"):
|
||||||
raise FpdbParseError(hid=m.group('HID'))
|
raise FpdbParseError(hid=m.group('HID'))
|
||||||
|
|
||||||
if m.group('TABLEATTRIBUTES'):
|
if m.group('TABLEATTRIBUTES'):
|
||||||
|
@ -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)
|
||||||
|
@ -68,6 +70,9 @@ follow : whether to tail -f the input"""
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
@ -89,6 +94,10 @@ follow : whether to tail -f the input"""
|
||||||
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
|
||||||
|
|
|
@ -168,15 +168,19 @@ class SummaryParser(htmllib.HTMLParser): # derive new HTML parser
|
||||||
self.TempResultPos += 1
|
self.TempResultPos += 1
|
||||||
|
|
||||||
class EverleafSummary:
|
class EverleafSummary:
|
||||||
def main(self):
|
def __init__(self):
|
||||||
file = urllib.urlopen("http://www.poker4ever.com/en.tournaments.tournament-statistics?tid=785119")
|
if __name__ != "__main__":
|
||||||
parser = SummaryParser(formatter.NullFormatter())
|
self.main()
|
||||||
parser.feed(file.read())
|
|
||||||
print "site=",parser.SiteName, "tourneyname=", parser.TourneyName, "tourneyid=", parser.TourneyId
|
def main(self, id="785646"):
|
||||||
print "start time=",parser.TourneyStartTime, "end time=",parser.TourneyEndTime
|
file = urllib.urlopen("http://www.poker4ever.com/en.tournaments.tournament-statistics?tid="+id)
|
||||||
print "structure=", parser.TourneyStructure, "game type=",parser.TourneyGameType
|
self.parser = SummaryParser(formatter.NullFormatter())
|
||||||
print "buy-in=", parser.TourneyBuyIn, "rebuys=", parser.TourneyRebuys, "total players=", parser.TourneyPlayers, "pool=", parser.TourneyPool
|
self.parser.feed(file.read())
|
||||||
print "results=", parser.Results
|
print "site=",self.parser.SiteName, "tourneyname=", self.parser.TourneyName, "tourneyid=", self.parser.TourneyId
|
||||||
|
print "start time=",self.parser.TourneyStartTime, "end time=",self.parser.TourneyEndTime
|
||||||
|
print "structure=", self.parser.TourneyStructure, "game type=",self.parser.TourneyGameType
|
||||||
|
print "buy-in=", self.parser.TourneyBuyIn, "rebuys=", self.parser.TourneyRebuys, "total players=", self.parser.TourneyPlayers, "pool=", self.parser.TourneyPool
|
||||||
|
print "results=", self.parser.Results
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
310
pyfpdb/TournamentTracker.py
Normal file
310
pyfpdb/TournamentTracker.py
Normal file
|
@ -0,0 +1,310 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
"""TourneyTracker.py
|
||||||
|
Based on HUD_main .. who knows if we want to actually use this or not
|
||||||
|
"""
|
||||||
|
# Copyright 2008, 2009, Eric Blade
|
||||||
|
#
|
||||||
|
# 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
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
|
||||||
|
# to do allow window resizing
|
||||||
|
# to do hud to echo, but ignore non numbers
|
||||||
|
# to do no stat window for hero
|
||||||
|
# to do things to add to config.xml
|
||||||
|
|
||||||
|
# Standard Library modules
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import Options
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
(options, sys.argv) = Options.fpdb_options()
|
||||||
|
|
||||||
|
if not options.errorsToConsole:
|
||||||
|
print "Note: error output is being diverted to fpdb-error-log.txt and HUD-error.txt. Any major error will be reported there _only_."
|
||||||
|
errorFile = open('tourneyerror.txt', 'w', 0)
|
||||||
|
sys.stderr = errorFile
|
||||||
|
|
||||||
|
import thread
|
||||||
|
import time
|
||||||
|
import string
|
||||||
|
import re
|
||||||
|
|
||||||
|
# pyGTK modules
|
||||||
|
import pygtk
|
||||||
|
import gtk
|
||||||
|
import gobject
|
||||||
|
|
||||||
|
# FreePokerTools modules
|
||||||
|
import Configuration
|
||||||
|
import Database
|
||||||
|
import SummaryEverleaf
|
||||||
|
|
||||||
|
class Tournament:
|
||||||
|
"""Tournament will hold the information about a tournament, I guess ? Remember I'm new to this language, so I don't know the best ways to do things"""
|
||||||
|
|
||||||
|
def __init__(self, parent, site, tid): # site should probably be something in the config object, but i don't know how the config object works right now, so we're going to make it a str ..
|
||||||
|
print "Tournament init"
|
||||||
|
self.parent = parent
|
||||||
|
self.window = None
|
||||||
|
self.site = site
|
||||||
|
self.id = tid
|
||||||
|
self.starttime = time.time()
|
||||||
|
self.endtime = None
|
||||||
|
self.game = None
|
||||||
|
self.structure = None
|
||||||
|
self.buyin = 0
|
||||||
|
self.fee = 0
|
||||||
|
self.rebuys = False
|
||||||
|
self.numrebuys = 0 # this should probably be attached to the players list...
|
||||||
|
self.numplayers = 0
|
||||||
|
self.prizepool = 0
|
||||||
|
self.players = {} # eventually i'd guess we'd probably want to fill this with playername:playerid's
|
||||||
|
self.results = {} # i'd guess we'd want to load this up with playerid's instead of playernames, too, as well, also
|
||||||
|
|
||||||
|
# if site == "Everleaf": # this should be attached to a button that says "retrieve tournament info" or something for sites that we know how to do it for
|
||||||
|
summary = SummaryEverleaf.EverleafSummary()
|
||||||
|
self.site = summary.parser.SiteName
|
||||||
|
self.id = summary.parser.TourneyId
|
||||||
|
self.starttime = summary.parser.TourneyStartTime
|
||||||
|
self.endtime = summary.parser.TourneyEndTime
|
||||||
|
self.game = summary.parser.TourneyGameType
|
||||||
|
self.structure = summary.parser.TourneyStructure
|
||||||
|
self.buyin = summary.parser.TourneyBuyIn # need to remember to parse the Fee out of this and move it to self.fee
|
||||||
|
self.rebuys = (summary.parser.TourneyRebuys == "yes")
|
||||||
|
self.prizepool = summary.parser.TourneyPool
|
||||||
|
self.numplayers = summary.parser.TourneyPlayers
|
||||||
|
|
||||||
|
self.openwindow() # let's start by getting any info we need.. meh
|
||||||
|
|
||||||
|
def openwindow(self, widget=None):
|
||||||
|
if self.window is not None:
|
||||||
|
self.window.show() # isn't there a better way to bring something to the front? not that GTK focus works right anyway, ever
|
||||||
|
else:
|
||||||
|
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
|
||||||
|
print "tournament edit window=", self.window
|
||||||
|
self.window.connect("delete_event", self.delete_event)
|
||||||
|
self.window.connect("destroy", self.destroy)
|
||||||
|
self.window.set_title("FPDB Tournament Entry")
|
||||||
|
self.window.set_border_width(1)
|
||||||
|
self.window.set_default_size(480,640)
|
||||||
|
self.window.set_resizable(True)
|
||||||
|
|
||||||
|
self.main_vbox = gtk.VBox(False, 1)
|
||||||
|
self.main_vbox.set_border_width(1)
|
||||||
|
self.window.add(self.main_vbox)
|
||||||
|
self.window.show()
|
||||||
|
|
||||||
|
def addrebuy(self, widget=None):
|
||||||
|
t = self
|
||||||
|
t.numrebuys += 1
|
||||||
|
t.mylabel.set_label("%s - %s - %s - %s - %s %s - %s - %s - %s - %s - %s" % (t.site, t.id, t.starttime, t.endtime, t.structure, t.game, t.buyin, t.fee, t.numrebuys, t.numplayers, t.prizepool))
|
||||||
|
|
||||||
|
def delete_event(self, widget, event, data=None):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def destroy(self, widget, data=None):
|
||||||
|
return False
|
||||||
|
#end def destroy
|
||||||
|
|
||||||
|
|
||||||
|
class ttracker_main(object):
|
||||||
|
"""A main() object to own both the read_stdin thread and the gui."""
|
||||||
|
# This class mainly provides state for controlling the multiple HUDs.
|
||||||
|
|
||||||
|
def __init__(self, db_name = 'fpdb'):
|
||||||
|
self.db_name = db_name
|
||||||
|
self.config = Configuration.Config(file=options.config, dbname=options.dbname)
|
||||||
|
self.tourney_list = []
|
||||||
|
|
||||||
|
# a thread to read stdin
|
||||||
|
gobject.threads_init() # this is required
|
||||||
|
thread.start_new_thread(self.read_stdin, ()) # starts the thread
|
||||||
|
|
||||||
|
# a main window
|
||||||
|
self.main_window = gtk.Window()
|
||||||
|
self.main_window.connect("destroy", self.destroy)
|
||||||
|
self.vb = gtk.VBox()
|
||||||
|
self.label = gtk.Label('Closing this window will stop the Tournament Tracker')
|
||||||
|
self.vb.add(self.label)
|
||||||
|
self.addbutton = gtk.Button(label="Enter Tournament")
|
||||||
|
self.addbutton.connect("clicked", self.addClicked, "add tournament")
|
||||||
|
self.vb.add(self.addbutton)
|
||||||
|
|
||||||
|
self.main_window.add(self.vb)
|
||||||
|
self.main_window.set_title("FPDB Tournament Tracker")
|
||||||
|
self.main_window.show_all()
|
||||||
|
|
||||||
|
def addClicked(self, widget, data): # what is "data"? i'm guessing anything i pass in after the function name in connect() but unsure because the documentation sucks
|
||||||
|
print "addClicked", widget, data
|
||||||
|
t = Tournament(self, None, None)
|
||||||
|
if t is not None:
|
||||||
|
print "new tournament=", t
|
||||||
|
self.tourney_list.append(t)
|
||||||
|
mylabel = gtk.Label("%s - %s - %s - %s - %s %s - %s - %s - %s - %s - %s" % (t.site, t.id, t.starttime, t.endtime, t.structure, t.game, t.buyin, t.fee, t.numrebuys, t.numplayers, t.prizepool))
|
||||||
|
print "new label=", mylabel
|
||||||
|
editbutton = gtk.Button(label="Edit")
|
||||||
|
print "new button=", editbutton
|
||||||
|
editbutton.connect("clicked", t.openwindow)
|
||||||
|
rebuybutton = gtk.Button(label="Rebuy")
|
||||||
|
rebuybutton.connect("clicked", t.addrebuy)
|
||||||
|
self.vb.add(rebuybutton)
|
||||||
|
self.vb.add(editbutton) # These should probably be put in.. a.. h-box? i don't know..
|
||||||
|
self.vb.add(mylabel)
|
||||||
|
self.main_window.resize_children()
|
||||||
|
self.main_window.show()
|
||||||
|
mylabel.show()
|
||||||
|
editbutton.show()
|
||||||
|
rebuybutton.show()
|
||||||
|
t.mylabel = mylabel
|
||||||
|
t.editbutton = editbutton
|
||||||
|
t.rebuybutton = rebuybutton
|
||||||
|
self.vb.show()
|
||||||
|
print self.tourney_list
|
||||||
|
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
# when we move the start command over to the main program, we can have the main program ask for the tourney id, and pipe it into the stdin here
|
||||||
|
# at least that was my initial thought on it
|
||||||
|
|
||||||
|
def destroy(*args): # call back for terminating the main eventloop
|
||||||
|
gtk.main_quit()
|
||||||
|
|
||||||
|
def create_HUD(self, new_hand_id, table, table_name, max, poker_game, stat_dict, cards):
|
||||||
|
|
||||||
|
def idle_func():
|
||||||
|
|
||||||
|
gtk.gdk.threads_enter()
|
||||||
|
try:
|
||||||
|
newlabel = gtk.Label("%s - %s" % (table.site, table_name))
|
||||||
|
self.vb.add(newlabel)
|
||||||
|
newlabel.show()
|
||||||
|
self.main_window.resize_children()
|
||||||
|
|
||||||
|
self.hud_dict[table_name].tablehudlabel = newlabel
|
||||||
|
self.hud_dict[table_name].create(new_hand_id, self.config, stat_dict, cards)
|
||||||
|
for m in self.hud_dict[table_name].aux_windows:
|
||||||
|
m.create()
|
||||||
|
m.update_gui(new_hand_id)
|
||||||
|
self.hud_dict[table_name].update(new_hand_id, self.config)
|
||||||
|
self.hud_dict[table_name].reposition_windows()
|
||||||
|
return False
|
||||||
|
finally:
|
||||||
|
gtk.gdk.threads_leave()
|
||||||
|
|
||||||
|
self.hud_dict[table_name] = Hud.Hud(self, table, max, poker_game, self.config, self.db_connection)
|
||||||
|
self.hud_dict[table_name].table_name = table_name
|
||||||
|
self.hud_dict[table_name].stat_dict = stat_dict
|
||||||
|
self.hud_dict[table_name].cards = cards
|
||||||
|
[aw.update_data(new_hand_id, self.db_connection) for aw in self.hud_dict[table_name].aux_windows]
|
||||||
|
gobject.idle_add(idle_func)
|
||||||
|
|
||||||
|
def update_HUD(self, new_hand_id, table_name, config):
|
||||||
|
"""Update a HUD gui from inside the non-gui read_stdin thread."""
|
||||||
|
# This is written so that only 1 thread can touch the gui--mainly
|
||||||
|
# for compatibility with Windows. This method dispatches the
|
||||||
|
# function idle_func() to be run by the gui thread, at its leisure.
|
||||||
|
def idle_func():
|
||||||
|
gtk.gdk.threads_enter()
|
||||||
|
try:
|
||||||
|
self.hud_dict[table_name].update(new_hand_id, config)
|
||||||
|
[aw.update_gui(new_hand_id) for aw in self.hud_dict[table_name].aux_windows]
|
||||||
|
return False
|
||||||
|
finally:
|
||||||
|
gtk.gdk.threads_leave()
|
||||||
|
gobject.idle_add(idle_func)
|
||||||
|
|
||||||
|
def read_stdin(self): # This is the thread function
|
||||||
|
"""Do all the non-gui heavy lifting for the HUD program."""
|
||||||
|
|
||||||
|
# This db connection is for the read_stdin thread only. It should not
|
||||||
|
# be passed to HUDs for use in the gui thread. HUD objects should not
|
||||||
|
# need their own access to the database, but should open their own
|
||||||
|
# if it is required.
|
||||||
|
self.db_connection = Database.Database(self.config, self.db_name, 'temp')
|
||||||
|
# self.db_connection.init_hud_stat_vars(hud_days)
|
||||||
|
tourny_finder = re.compile('(\d+) (\d+)')
|
||||||
|
|
||||||
|
while 1: # wait for a new hand number on stdin
|
||||||
|
new_hand_id = sys.stdin.readline()
|
||||||
|
new_hand_id = string.rstrip(new_hand_id)
|
||||||
|
if new_hand_id == "": # blank line means quit
|
||||||
|
self.destroy()
|
||||||
|
break # this thread is not always killed immediately with gtk.main_quit()
|
||||||
|
# get basic info about the new hand from the db
|
||||||
|
# if there is a db error, complain, skip hand, and proceed
|
||||||
|
try:
|
||||||
|
(table_name, max, poker_game, type) = self.db_connection.get_table_name(new_hand_id)
|
||||||
|
stat_dict = self.db_connection.get_stats_from_hand(new_hand_id, aggregate_stats[type]
|
||||||
|
,hud_style, agg_bb_mult)
|
||||||
|
|
||||||
|
cards = self.db_connection.get_cards(new_hand_id)
|
||||||
|
comm_cards = self.db_connection.get_common_cards(new_hand_id)
|
||||||
|
if comm_cards != {}: # stud!
|
||||||
|
cards['common'] = comm_cards['common']
|
||||||
|
except Exception, err:
|
||||||
|
err = traceback.extract_tb(sys.exc_info()[2])[-1]
|
||||||
|
print "db error: skipping "+str(new_hand_id)+" "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1])
|
||||||
|
if new_hand_id: # new_hand_id is none if we had an error prior to the store
|
||||||
|
sys.stderr.write("Database error %s in hand %d. Skipping.\n" % (err, int(new_hand_id)))
|
||||||
|
continue
|
||||||
|
|
||||||
|
if type == "tour": # hand is from a tournament
|
||||||
|
mat_obj = tourny_finder.search(table_name)
|
||||||
|
if mat_obj:
|
||||||
|
(tour_number, tab_number) = mat_obj.group(1, 2)
|
||||||
|
temp_key = tour_number
|
||||||
|
else: # tourney, but can't get number and table
|
||||||
|
print "could not find tournament: skipping "
|
||||||
|
sys.stderr.write("Could not find tournament %d in hand %d. Skipping.\n" % (int(tour_number), int(new_hand_id)))
|
||||||
|
continue
|
||||||
|
|
||||||
|
else:
|
||||||
|
temp_key = table_name
|
||||||
|
|
||||||
|
# Update an existing HUD
|
||||||
|
if temp_key in self.hud_dict:
|
||||||
|
self.hud_dict[temp_key].stat_dict = stat_dict
|
||||||
|
self.hud_dict[temp_key].cards = cards
|
||||||
|
[aw.update_data(new_hand_id, self.db_connection) for aw in self.hud_dict[temp_key].aux_windows]
|
||||||
|
self.update_HUD(new_hand_id, temp_key, self.config)
|
||||||
|
|
||||||
|
# Or create a new HUD
|
||||||
|
else:
|
||||||
|
if type == "tour":
|
||||||
|
tablewindow = Tables.discover_tournament_table(self.config, tour_number, tab_number)
|
||||||
|
else:
|
||||||
|
tablewindow = Tables.discover_table_by_name(self.config, table_name)
|
||||||
|
if tablewindow == None:
|
||||||
|
# If no client window is found on the screen, complain and continue
|
||||||
|
if type == "tour":
|
||||||
|
table_name = "%s %s" % (tour_number, tab_number)
|
||||||
|
sys.stderr.write("table name "+table_name+" not found, skipping.\n")
|
||||||
|
else:
|
||||||
|
self.create_HUD(new_hand_id, tablewindow, temp_key, max, poker_game, stat_dict, cards)
|
||||||
|
self.db_connection.connection.rollback()
|
||||||
|
|
||||||
|
if __name__== "__main__":
|
||||||
|
|
||||||
|
sys.stderr.write("tournament tracker starting\n")
|
||||||
|
sys.stderr.write("Using db name = %s\n" % (options.dbname))
|
||||||
|
|
||||||
|
# start the HUD_main object
|
||||||
|
hm = ttracker_main(db_name = options.dbname)
|
||||||
|
|
||||||
|
# start the event loop
|
||||||
|
gtk.main()
|
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,25 +396,41 @@ 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.getStatus() and self.NEWIMPORT == False):
|
if hhc.getParsedObjectType() == "HH":
|
||||||
(stored, duplicates, partial, errors, ttime) = self.import_fpdb_file(db, out_path, site, q)
|
if(hhc.getStatus() and self.NEWIMPORT == False):
|
||||||
elif (hhc.getStatus() and self.NEWIMPORT == True):
|
(stored, duplicates, partial, errors, ttime) = self.import_fpdb_file(db, out_path, site, q)
|
||||||
#This code doesn't do anything yet
|
elif (hhc.getStatus() and self.NEWIMPORT == True):
|
||||||
handlist = hhc.getProcessedHands()
|
#This code doesn't do anything yet
|
||||||
self.pos_in_file[file] = hhc.getLastCharacterRead()
|
handlist = hhc.getProcessedHands()
|
||||||
|
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:
|
||||||
|
# conversion didn't work
|
||||||
|
# TODO: appropriate response?
|
||||||
|
return (0, 0, 0, 1, 0, -1)
|
||||||
|
elif hhc.getParsedObjectType() == "Summary":
|
||||||
|
if(hhc.getStatus()):
|
||||||
|
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:
|
else:
|
||||||
# conversion didn't work
|
print "Unknown objects parsed by HHC :'%s'" %(hhc.getObjectTypeRead())
|
||||||
# TODO: appropriate response?
|
|
||||||
return (0, 0, 0, 1, 0, -1)
|
return (0, 0, 0, 1, 0, -1)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print "Unknown filter filter_name:'%s' in filter:'%s'" %(filter_name, filter)
|
print "Unknown filter filter_name:'%s' in filter:'%s'" %(filter_name, filter)
|
||||||
return (0, 0, 0, 1, 0, -1)
|
return (0, 0, 0, 1, 0, -1)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user