Coverage for BetfairToFpdb.py: 0%

143 statements  

« prev     ^ index     » next       coverage.py v7.6.3, created at 2024-10-15 19:33 +0000

1#!/usr/bin/env python 

2# -*- coding: utf-8 -*- 

3# 

4# Copyright 2008-2011, Carl Gherardi 

5# 

6# This program is free software; you can redistribute it and/or modify 

7# it under the terms of the GNU General Public License as published by 

8# the Free Software Foundation; either version 2 of the License, or 

9# (at your option) any later version. 

10# 

11# This program is distributed in the hope that it will be useful, 

12# but WITHOUT ANY WARRANTY; without even the implied warranty of 

13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

14# GNU General Public License for more details. 

15# 

16# You should have received a copy of the GNU General Public License 

17# along with this program; if not, write to the Free Software 

18# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 

19######################################################################## 

20 

21 

22# import L10n 

23# _ = L10n.get_translation() 

24 

25from HandHistoryConverter import HandHistoryConverter, FpdbParseError 

26import re 

27import logging 

28import datetime 

29 

30log = logging.getLogger("parser") 

31 

32# Betfair HH format 

33 

34 

35class Betfair(HandHistoryConverter): 

36 sitename = "Betfair" 

37 filetype = "text" 

38 codepage = "cp1252" 

39 siteId = 7 # Needs to match id entry in Sites database 

40 

41 # Static regexes 

42 re_GameInfo = re.compile( 

43 "^(?P<LIMIT>NL|PL|) (?P<CURRENCY>\$|)?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) (?P<GAME>(Texas Hold'em|Omaha Hi|Omaha|Razz))", 

44 re.MULTILINE, 

45 ) 

46 re_Identify = re.compile("\*{5}\sBetfair\sPoker\sHand\sHistory\sfor\sGame\s\d+\s") 

47 re_SplitHands = re.compile(r"\n\n+") 

48 re_HandInfo = re.compile( 

49 "\*\*\*\*\* Betfair Poker Hand History for Game (?P<HID>[0-9]+) \*\*\*\*\*\n(?P<LIMIT>NL|PL|) (?P<CURRENCY>\$|)?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) (?P<GAMETYPE>(Texas Hold'em|Omaha|Razz)) - (?P<DATETIME>[a-zA-Z]+, [a-zA-Z]+ \d+, \d\d:\d\d:\d\d GMT \d\d\d\d)\nTable (?P<TABLE>[ a-zA-Z0-9]+) \d-max \(Real Money\)\nSeat (?P<BUTTON>[0-9]+)", 

50 re.MULTILINE, 

51 ) 

52 re_Button = re.compile(r"^Seat (?P<BUTTON>\d+) is the button", re.MULTILINE) 

53 re_PlayerInfo = re.compile("Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*)\s\(\s(\$(?P<CASH>[.0-9]+)) \)") 

54 re_Board = re.compile(r"\[ (?P<CARDS>.+) \]") 

55 

56 def compilePlayerRegexs(self, hand): 

57 players = set([player[1] for player in hand.players]) 

58 if not players <= self.compiledPlayers: # x <= y means 'x is subset of y' 

59 # we need to recompile the player regexs. 

60 self.compiledPlayers = players 

61 player_re = "(?P<PNAME>" + "|".join(map(re.escape, players)) + ")" 

62 log.debug("player_re: " + player_re) 

63 self.re_PostSB = re.compile("^%s posts small blind \[\$?(?P<SB>[.0-9]+)" % player_re, re.MULTILINE) 

64 self.re_PostBB = re.compile("^%s posts big blind \[\$?(?P<BB>[.0-9]+)" % player_re, re.MULTILINE) 

65 self.re_Antes = re.compile("^%s antes asdf sadf sadf" % player_re, re.MULTILINE) 

66 self.re_BringIn = re.compile("^%s antes asdf sadf sadf" % player_re, re.MULTILINE) 

67 self.re_PostBoth = re.compile( 

68 "^%s posts small \& big blinds \[\$?(?P<SBBB>[.0-9]+)" % player_re, re.MULTILINE 

69 ) 

70 self.re_HeroCards = re.compile("^Dealt to %s \[ (?P<CARDS>.*) \]" % player_re, re.MULTILINE) 

71 self.re_Action = re.compile( 

72 "^%s (?P<ATYPE>bets|checks|raises to|raises|calls|folds)(\s\[\$(?P<BET>[.\d]+)\])?" % player_re, 

73 re.MULTILINE, 

74 ) 

75 self.re_ShowdownAction = re.compile("^%s shows \[ (?P<CARDS>.*) \]" % player_re, re.MULTILINE) 

76 self.re_CollectPot = re.compile( 

77 "^%s wins \$(?P<POT>[.\d]+) (.*?\[ (?P<CARDS>.*?) \])?" % player_re, re.MULTILINE 

78 ) 

79 self.re_SitsOut = re.compile("^%s sits out" % player_re, re.MULTILINE) 

80 self.re_ShownCards = re.compile(r"%s (?P<SEAT>[0-9]+) (?P<CARDS>adsfasdf)" % player_re, re.MULTILINE) 

81 

82 def readSupportedGames(self): 

83 return [["ring", "hold", "nl"], ["ring", "hold", "pl"]] 

84 

85 def determineGameType(self, handText): 

86 info = {"type": "ring"} 

87 

88 m = self.re_GameInfo.search(handText) 

89 if not m: 

90 tmp = handText[0:200] 

91 log.error(("BetfairToFpdb.determineGameType: '%s'") % tmp) 

92 raise FpdbParseError 

93 

94 mg = m.groupdict() 

95 

96 # translations from captured groups to our info strings 

97 limits = {"NL": "nl", "PL": "pl", "Limit": "fl"} 

98 games = { # base, category 

99 "Texas Hold'em": ("hold", "holdem"), 

100 "Omaha Hi": ("hold", "omahahi"), 

101 "Omaha": ("hold", "omahahi"), 

102 "Razz": ("stud", "razz"), 

103 "7 Card Stud": ("stud", "studhi"), 

104 } 

105 currencies = {" €": "EUR", "$": "USD", "": "T$"} 

106 if "LIMIT" in mg: 

107 info["limitType"] = limits[mg["LIMIT"]] 

108 if "GAME" in mg: 

109 (info["base"], info["category"]) = games[mg["GAME"]] 

110 if "SB" in mg: 

111 info["sb"] = mg["SB"] 

112 if "BB" in mg: 

113 info["bb"] = mg["BB"] 

114 if "CURRENCY" in mg: 

115 info["currency"] = currencies[mg["CURRENCY"]] 

116 

117 return info 

118 

119 def readHandInfo(self, hand): 

120 m = self.re_HandInfo.search(hand.handText) 

121 if m is None: 

122 tmp = hand.handText[0:200] 

123 log.error(("BetfairToFpdb.readHandInfo: '%s'") % tmp) 

124 raise FpdbParseError 

125 log.debug("HID %s, Table %s" % (m.group("HID"), m.group("TABLE"))) 

126 hand.handid = m.group("HID") 

127 hand.tablename = m.group("TABLE") 

128 hand.startTime = datetime.datetime.strptime(m.group("DATETIME"), "%A, %B %d, %H:%M:%S GMT %Y") 

129 # hand.buttonpos = int(m.group('BUTTON')) 

130 

131 def readPlayerStacks(self, hand): 

132 m = self.re_PlayerInfo.finditer(hand.handText) 

133 for a in m: 

134 hand.addPlayer(int(a.group("SEAT")), a.group("PNAME"), a.group("CASH")) 

135 

136 # Shouldn't really dip into the Hand object, but i've no idea how to tell the length of iter m 

137 if len(hand.players) < 2: 

138 log.info(("Less than 2 players found in hand %s.") % hand.handid) 

139 

140 def markStreets(self, hand): 

141 m = re.search( 

142 r"\*\* Dealing down cards \*\*(?P<PREFLOP>.+(?=\*\* Dealing Flop \*\*)|.+)" 

143 r"(\*\* Dealing Flop \*\*(?P<FLOP> \[ \S\S, \S\S, \S\S \].+(?=\*\* Dealing Turn \*\*)|.+))?" 

144 r"(\*\* Dealing Turn \*\*(?P<TURN> \[ \S\S \].+(?=\*\* Dealing River \*\*)|.+))?" 

145 r"(\*\* Dealing River \*\*(?P<RIVER> \[ \S\S \].+))?", 

146 hand.handText, 

147 re.DOTALL, 

148 ) 

149 

150 hand.addStreets(m) 

151 

152 def readCommunityCards(self, hand, street): # street has been matched by markStreets, so exists in this hand 

153 if street in ( 

154 "FLOP", 

155 "TURN", 

156 "RIVER", 

157 ): # a list of streets which get dealt community cards (i.e. all but PREFLOP) 

158 m = self.re_Board.search(hand.streets[street]) 

159 hand.setCommunityCards(street, m.group("CARDS").split(", ")) 

160 

161 def readBlinds(self, hand): 

162 try: 

163 m = self.re_PostSB.search(hand.handText) 

164 hand.addBlind(m.group("PNAME"), "small blind", m.group("SB")) 

165 except AttributeError: 

166 hand.addBlind(None, None, None) 

167 for a in self.re_PostBB.finditer(hand.handText): 

168 hand.addBlind(a.group("PNAME"), "big blind", a.group("BB")) 

169 for a in self.re_PostBoth.finditer(hand.handText): 

170 hand.addBlind(a.group("PNAME"), "small & big blinds", a.group("SBBB")) 

171 

172 def readAntes(self, hand): 

173 log.debug("reading antes") 

174 m = self.re_Antes.finditer(hand.handText) 

175 for player in m: 

176 log.debug("hand.addAnte(%s,%s)" % (player.group("PNAME"), player.group("ANTE"))) 

177 hand.addAnte(player.group("PNAME"), player.group("ANTE")) 

178 

179 def readBringIn(self, hand): 

180 m = self.re_BringIn.search(hand.handText, re.DOTALL) 

181 if m: 

182 log.debug(("Player bringing in: %s for %s") % (m.group("PNAME"), m.group("BRINGIN"))) 

183 hand.addBringIn(m.group("PNAME"), m.group("BRINGIN")) 

184 else: 

185 log.warning(("No bringin found")) 

186 

187 def readButton(self, hand): 

188 hand.buttonpos = int(self.re_Button.search(hand.handText).group("BUTTON")) 

189 

190 def readHoleCards(self, hand): 

191 # streets PREFLOP, PREDRAW, and THIRD are special cases beacause 

192 # we need to grab hero's cards 

193 for street in ("PREFLOP", "DEAL"): 

194 if street in list(hand.streets.keys()): 

195 m = self.re_HeroCards.finditer(hand.streets[street]) 

196 for found in m: 

197 hand.hero = found.group("PNAME") 

198 newcards = [c.strip() for c in found.group("CARDS").split(",")] 

199 hand.addHoleCards(street, hand.hero, closed=newcards, shown=False, mucked=False, dealt=True) 

200 

201 def readStudPlayerCards(self, hand, street): 

202 # balh blah blah 

203 pass 

204 

205 def readAction(self, hand, street): 

206 m = self.re_Action.finditer(hand.streets[street]) 

207 for action in m: 

208 if action.group("ATYPE") == "folds": 

209 hand.addFold(street, action.group("PNAME")) 

210 elif action.group("ATYPE") == "checks": 

211 hand.addCheck(street, action.group("PNAME")) 

212 elif action.group("ATYPE") == "calls": 

213 hand.addCall(street, action.group("PNAME"), action.group("BET")) 

214 elif action.group("ATYPE") == "bets": 

215 hand.addBet(street, action.group("PNAME"), action.group("BET")) 

216 elif action.group("ATYPE") == "raises to": 

217 hand.addRaiseTo(street, action.group("PNAME"), action.group("BET")) 

218 else: 

219 log.debug( 

220 ("DEBUG:") 

221 + " " 

222 + ("Unimplemented %s: '%s' '%s'") % ("readAction", action.group("PNAME"), action.group("ATYPE")) 

223 ) 

224 

225 def readShowdownActions(self, hand): 

226 for shows in self.re_ShowdownAction.finditer(hand.handText): 

227 cards = shows.group("CARDS") 

228 cards = cards.split(", ") 

229 hand.addShownCards(cards, shows.group("PNAME")) 

230 

231 def readCollectPot(self, hand): 

232 for m in self.re_CollectPot.finditer(hand.handText): 

233 hand.addCollectPot(player=m.group("PNAME"), pot=m.group("POT")) 

234 

235 def readShownCards(self, hand): 

236 for m in self.re_ShownCards.finditer(hand.handText): 

237 if m.group("CARDS") is not None: 

238 cards = m.group("CARDS") 

239 cards = cards.split(", ") 

240 hand.addShownCards(cards=None, player=m.group("PNAME"), holeandboard=cards)