Coverage for PokerTrackerToFpdb.py: 0%

437 statements  

« prev     ^ index     » next       coverage.py v7.6.4, created at 2024-11-07 02:19 +0000

1#!/usr/bin/env python 

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

3# 

4# Copyright 2008-2012, Chaz Littlejohn 

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 

21from __future__ import division 

22 

23from past.utils import old_div 

24# import L10n 

25# _ = L10n.get_translation() 

26 

27# TODO: straighten out discards for draw games 

28 

29from HandHistoryConverter import HandHistoryConverter, FpdbParseError, FpdbHandPartial 

30from decimal import Decimal 

31import re 

32import logging 

33import datetime 

34 

35 

36import MergeStructures 

37 

38 

39# PokerTracker HH Format 

40log = logging.getLogger("parser") 

41 

42 

43class PokerTracker(HandHistoryConverter): 

44 # Class Variables 

45 Structures = None 

46 filetype = "text" 

47 codepage = ("utf8", "cp1252") 

48 sym = {"USD": "\$", "CAD": "\$", "T$": "", "EUR": "€", "GBP": "\£", "play": ""} # ADD Euro, Sterling, etc HERE 

49 substitutions = { 

50 "LEGAL_ISO": "USD|EUR|GBP|CAD|FPP", # legal ISO currency codes 

51 "LS": "\$|€|\£|", # legal currency symbols - Euro(cp1252, utf-8) 

52 "PLYR": r"(?P<PNAME>.+?)", 

53 "NUM": ".,\d", 

54 "CUR": "(\$|€||\£|)", 

55 "BRKTS": r"(\(button\) |\(small blind\) |\(big blind\) |\(button\) \(small blind\) |\(button\) \(big blind\) )?", 

56 } 

57 

58 # translations from captured groups to fpdb info strings 

59 Lim_Blinds = { 

60 "0.04": ("0.01", "0.02"), 

61 "0.08": ("0.02", "0.04"), 

62 "0.10": ("0.02", "0.05"), 

63 "0.20": ("0.05", "0.10"), 

64 "0.40": ("0.10", "0.20"), 

65 "0.50": ("0.10", "0.25"), 

66 "1.00": ("0.25", "0.50"), 

67 "1": ("0.25", "0.50"), 

68 "2.00": ("0.50", "1.00"), 

69 "2": ("0.50", "1.00"), 

70 "4.00": ("1.00", "2.00"), 

71 "4": ("1.00", "2.00"), 

72 "6.00": ("1.00", "3.00"), 

73 "6": ("1.00", "3.00"), 

74 "8.00": ("2.00", "4.00"), 

75 "8": ("2.00", "4.00"), 

76 "10.00": ("2.00", "5.00"), 

77 "10": ("2.00", "5.00"), 

78 "20.00": ("5.00", "10.00"), 

79 "20": ("5.00", "10.00"), 

80 "30.00": ("10.00", "15.00"), 

81 "30": ("10.00", "15.00"), 

82 "40.00": ("10.00", "20.00"), 

83 "40": ("10.00", "20.00"), 

84 "60.00": ("15.00", "30.00"), 

85 "60": ("15.00", "30.00"), 

86 "80.00": ("20.00", "40.00"), 

87 "80": ("20.00", "40.00"), 

88 "100.00": ("25.00", "50.00"), 

89 "100": ("25.00", "50.00"), 

90 "150.00": ("50.00", "75.00"), 

91 "150": ("50.00", "75.00"), 

92 "200.00": ("50.00", "100.00"), 

93 "200": ("50.00", "100.00"), 

94 "400.00": ("100.00", "200.00"), 

95 "400": ("100.00", "200.00"), 

96 "800.00": ("200.00", "400.00"), 

97 "800": ("200.00", "400.00"), 

98 "1000.00": ("250.00", "500.00"), 

99 "1000": ("250.00", "500.00"), 

100 "2000.00": ("500.00", "1000.00"), 

101 "2000": ("500.00", "1000.00"), 

102 } 

103 

104 limits = {"NL": "nl", "No Limit": "nl", "Pot Limit": "pl", "PL": "pl", "FL": "fl", "Limit": "fl", "LIMIT": "fl"} 

105 games = { # base, category 

106 "Hold'em": ("hold", "holdem"), 

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

108 "Holdem": ("hold", "holdem"), 

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

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

111 "Omaha Hi/Lo": ("hold", "omahahilo"), 

112 } 

113 sites = { 

114 "EverestPoker Game #": ("Everest", 16), 

115 "GAME #": ("iPoker", 14), 

116 "MERGE_GAME #": ("Merge", 12), 

117 "Merge Game #": ("Merge", 12), 

118 "** Game ID ": ("Microgaming", 20), 

119 "** Hand # ": ("Microgaming", 20), 

120 } 

121 currencies = {"€": "EUR", "$": "USD", "": "T$", "£": "GBP"} 

122 

123 re_Site = re.compile( 

124 "(?P<SITE>EverestPoker\sGame\s\#|GAME\s\#|MERGE_GAME\s\#|Merge\sGame\s\#|\*{2}\s(Game\sID|Hand\s\#)\s)\d+" 

125 ) 

126 # Static regexes 

127 re_GameInfo1 = re.compile( 

128 """ 

129 (?P<SITE>GAME\s\#|MERGE_GAME\s\#|Merge\sGame\s\#)(?P<HID>[0-9\-]+)(\sVersion:[\d\.]+\s(?P<UNCALLED>Uncalled:Y))?(:?\s+|\s\|\s) 

130 (?P<GAME>Holdem|Texas\sHold\'em|Omaha|Omaha\sHi|Omaha\sHi/Lo)\s\s? 

131 ((?P<LIMIT>PL|NL|FL|No\sLimit|Limit|LIMIT|Pot\sLimit)\s\s?)? 

132 (?P<TOUR>Tournament)? 

133 (\(? # open paren of the stakes 

134 (?P<CURRENCY>%(LS)s|)? 

135 (?P<SB>[%(NUM)s]+)/(%(LS)s)? 

136 (?P<BB>[%(NUM)s]+) 

137 (?P<BLAH>\s-\s[%(LS)s\d\.]+\sCap\s-\s)? # Optional Cap part 

138 \s?(?P<ISO>%(LEGAL_ISO)s)? 

139 \)? 

140 )?(\s|\s\|\s) # close paren of the stakes 

141 (?P<DATETIME>.*$) 

142 """ 

143 % substitutions, 

144 re.MULTILINE | re.VERBOSE, 

145 ) 

146 

147 re_GameInfo2 = re.compile( 

148 """ 

149 EverestPoker\sGame\s\#(?P<HID>[0-9]+):\s+ 

150 (?P<TOUR>Tourney\sID:\s(?P<TOURNO>\d+),\s)? 

151 Table\s(?P<TABLE>.+)? 

152 \s-\s 

153 ((?P<CURRENCY>%(LS)s|)? 

154 (?P<SB>[%(NUM)s]+)/(%(LS)s)? 

155 (?P<BB>[%(NUM)s]+))? 

156 \s-\s 

157 (?P<LIMIT>No\sLimit|Limit|Pot\sLimit)\s 

158 (?P<GAME>Hold\'em|Omaha|Omaha\sHi|Omaha\sHi/Lo)\s 

159 (-\s)? 

160 (?P<DATETIME>.*$) 

161 """ 

162 % substitutions, 

163 re.MULTILINE | re.VERBOSE, 

164 ) 

165 

166 re_GameInfo3 = re.compile( 

167 """ 

168 (?P<HID>[0-9]+)(\sVersion:\d)?\sstarting\s\-\s(?P<DATETIME>.*$)\s 

169 \*\*(?P<TOUR>.+(?P<SPEED>(Turbo|Hyper))?\((?P<TOURNO>\d+)\):Table)?\s(?P<TABLE>.+)\s 

170 \[((Multi|Single)\sTable\s)?(?P<GAME>Hold\'em|Omaha|Omaha\sHi|Omaha\sHi/Lo)\]\s 

171 \((?P<SB>[%(NUM)s]+)\|(?P<BB>[%(NUM)s]+)\s(?P<LIMIT>NL|FL|PL)\s\-\s(MTT|SNG|STT|(?P<CURRENCY>%(LS)s|)\s?Cash\sGame)(\sseats:(?P<MAX>\d+))?.*\)\s 

172 (?P<PLAY>Real|Play)\sMoney 

173 """ 

174 % substitutions, 

175 re.MULTILINE | re.VERBOSE, 

176 ) 

177 

178 re_PlayerInfo1 = re.compile( 

179 """ 

180 ^Seat\s(?P<SEAT>[0-9]+):\s 

181 (?P<PNAME>.*)\s 

182 \((?P<CURRENCY>%(LS)s)?(?P<CASH>[%(NUM)s]+)(\sin\schips)?\) 

183 (?P<BUTTON>\sDEALER)?""" 

184 % substitutions, 

185 re.MULTILINE | re.VERBOSE, 

186 ) 

187 

188 re_PlayerInfo2 = re.compile( 

189 """ 

190 ^(\-\s)?(?P<PNAME>.*)\s 

191 sitting\sin\sseat\s(?P<SEAT>[0-9]+)\swith\s 

192 (%(LS)s)?(?P<CASH>[%(NUM)s]+) 

193 (?P<BUTTON>\s?\[Dealer\])?""" 

194 % substitutions, 

195 re.MULTILINE | re.VERBOSE, 

196 ) 

197 

198 re_HandInfo_Tour = re.compile( 

199 """ 

200 ^Table\s(?P<TABLE>.*),\s(?P<TOURNO>\d+)(,\s\d+)?\s 

201 (?P<TOUR>\(Tournament:\s(.+)?\sBuy\-In:\s(?P<BUYIN>(?P<BIAMT>[%(LS)s\d\.]+)\s?\+?\s?(?P<BIRAKE>[%(LS)s\d\.]+))\)) 

202 """ 

203 % substitutions, 

204 re.MULTILINE | re.VERBOSE, 

205 ) 

206 

207 re_HandInfo_Cash = re.compile( 

208 """ 

209 ^Table\s(?P<TABLE>[^,]+?)(,\sSeats\s(?P<MAX>\d+))?$""" 

210 % substitutions, 

211 re.MULTILINE | re.VERBOSE, 

212 ) 

213 

214 re_Identify = re.compile( 

215 "(EverestPoker\sGame\s\#|GAME\s\#|MERGE_GAME\s\#|Merge\sGame\s\#|\*{2}\s(Game\sID|Hand\s\#)\s)\d+" 

216 ) 

217 re_SplitHands = re.compile("\n\n\n+?") 

218 re_TailSplitHands = re.compile("(\n\n\n+)") 

219 re_Button = re.compile("The button is in seat #(?P<BUTTON>\d+)", re.MULTILINE) 

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

221 re_Board2 = re.compile(r":\s(?P<CARDS>.+)\n") 

222 re_DateTime1 = re.compile( 

223 """(?P<Y>[0-9]{4})\-(?P<M>[0-9]{2})\-(?P<D>[0-9]{2})[\- ]+(?P<H>[0-9]+):(?P<MIN>[0-9]+):(?P<S>[0-9]+)""", 

224 re.MULTILINE, 

225 ) 

226 re_DateTime2 = re.compile( 

227 """(?P<M>[0-9]{2})\/(?P<D>[0-9]{2})\/(?P<Y>[0-9]{4})[\- ]+(?P<H>[0-9]+):(?P<MIN>[0-9]+):(?P<S>[0-9]+)""", 

228 re.MULTILINE, 

229 ) 

230 re_DateTime3 = re.compile( 

231 """(?P<H>[0-9]+):(?P<MIN>[0-9]+):(?P<S>[0-9]+)[\- ]+(?P<Y>[0-9]{4})\/(?P<M>[0-9]{2})\/(?P<D>[0-9]{2})""", 

232 re.MULTILINE, 

233 ) 

234 # revised re including timezone (not currently used): 

235 # re_DateTime = re.compile("""(?P<Y>[0-9]{4})\/(?P<M>[0-9]{2})\/(?P<D>[0-9]{2})[\- ]+(?P<H>[0-9]+):(?P<MIN>[0-9]+):(?P<S>[0-9]+) \(?(?P<TZ>[A-Z0-9]+)""", re.MULTILINE) 

236 

237 # These used to be compiled per player, but regression tests say 

238 # we don't have to, and it makes life faster. 

239 re_PostSB = re.compile( 

240 r"^%(PLYR)s:? ((posts|posted) the small blind( of)?|(Post )?SB) (\- )?%(CUR)s(?P<SB>[%(NUM)s]+)" 

241 % substitutions, 

242 re.MULTILINE, 

243 ) 

244 re_PostBB = re.compile( 

245 r"^%(PLYR)s:? ((posts|posted) the big blind( of)?|posts the dead blind of|(Post )?BB) (\- )?%(CUR)s(?P<BB>[%(NUM)s]+)" 

246 % substitutions, 

247 re.MULTILINE, 

248 ) 

249 re_Antes = re.compile( 

250 r"^%(PLYR)s:? ((posts|posted) (the )?ante( of)?|(Post )?Ante) (\- )?%(CUR)s(?P<ANTE>[%(NUM)s]+)" 

251 % substitutions, 

252 re.MULTILINE, 

253 ) 

254 re_PostBoth1 = re.compile( 

255 r"^%(PLYR)s:? (posts|Post|(Post )?DB) %(CUR)s(?P<SBBB>[%(NUM)s]+)" % substitutions, re.MULTILINE 

256 ) 

257 re_PostBoth2 = re.compile( 

258 r"^%(PLYR)s:? posted to play \- %(CUR)s(?P<SBBB>[%(NUM)s]+)" % substitutions, re.MULTILINE 

259 ) 

260 re_HeroCards1 = re.compile( 

261 r"^Dealt to %(PLYR)s(?: \[(?P<OLDCARDS>.+?)\])?( \[(?P<NEWCARDS>.+?)\])" % substitutions, re.MULTILINE 

262 ) 

263 re_HeroCards2 = re.compile( 

264 r"rd(s)? to %(PLYR)s: (?P<OLDCARDS>NONE)?(?P<NEWCARDS>.+)\n" % substitutions, re.MULTILINE 

265 ) 

266 re_Action1 = re.compile( 

267 r"""^%(PLYR)s:?(?P<ATYPE>\sbets|\schecks|\sraises|\scalls|\sfolds|\sBet|\sCheck|\sRaise(\sto)?|\sCall|\sFold|\sAllin)(?P<RAISETO>\s\(NF\))?(\sto)?(\s%(CUR)s(?P<BET>[%(NUM)s]+))?\s*(and\sis\sall.in)?(and\shas\sreached\sthe\s\[%(CUR)s\d\.,]+\scap)?(\son|\scards?)?(\s\[(?P<CARDS>.+?)\])?\s*$""" 

268 % substitutions, 

269 re.MULTILINE | re.VERBOSE, 

270 ) 

271 re_Action2 = re.compile( 

272 r""" 

273 ^%(PLYR)s(?P<ATYPE>\sbet|\schecked|\sraised(\sto)?|\scalled|\sfolded|\swent\sall\-in) 

274 (\s(\-\s)?%(CUR)s(?P<BET>[%(NUM)s]+))?\s*$""" 

275 % substitutions, 

276 re.MULTILINE | re.VERBOSE, 

277 ) 

278 re_ShownCards1 = re.compile( 

279 "^%(PLYR)s:? (?P<SHOWED>shows|Shows|mucked) \[(?P<CARDS>.*)\]" % substitutions, re.MULTILINE 

280 ) 

281 re_ShownCards2 = re.compile("^%(PLYR)s (?P<SHOWED>shows|mucks): (?P<CARDS>.+)\n" % substitutions, re.MULTILINE) 

282 re_CollectPot1 = re.compile(r"^%(PLYR)s:? (collects|wins) %(CUR)s(?P<POT>[%(NUM)s]+)" % substitutions, re.MULTILINE) 

283 re_CollectPot2 = re.compile(r"^%(PLYR)s wins %(CUR)s(?P<POT>[%(NUM)s]+)" % substitutions, re.MULTILINE) 

284 re_Cancelled = re.compile("Hand\scancelled", re.MULTILINE) 

285 re_Tournament = re.compile("\(Tournament:") 

286 re_Hole = re.compile(r"\*\*\sDealing\scard") 

287 re_Currency = re.compile(r"\s\-\s(?P<CURRENCY>%(CUR)s)[%(NUM)s]+\s(Max|Min)" % substitutions) 

288 re_Max = re.compile(r"(\s(?P<MAX>(HU|\d+\sSeat))\s)") 

289 re_FastFold = re.compile(r"^%(PLYR)s\sQuick\sFolded" % substitutions, re.MULTILINE) 

290 

291 def compilePlayerRegexs(self, hand): 

292 pass 

293 

294 def readSupportedGames(self): 

295 return [ 

296 ["ring", "hold", "nl"], 

297 ["ring", "hold", "pl"], 

298 ["ring", "hold", "fl"], 

299 ["tour", "hold", "nl"], 

300 ["tour", "hold", "pl"], 

301 ["tour", "hold", "fl"], 

302 ] 

303 

304 def determineGameType(self, handText): 

305 m = self.re_Site.search(handText) 

306 if not m: 

307 tmp = handText[0:200] 

308 log.error(("PokerTrackerToFpdb.determineGameType: '%s'") % tmp) 

309 raise FpdbParseError 

310 

311 self.sitename = self.sites[m.group("SITE")][0] 

312 self.siteId = self.sites[m.group("SITE")][1] # Needs to match id entry in Sites database 

313 

314 info = {} 

315 if self.sitename in ("iPoker", "Merge"): 

316 m = self.re_GameInfo1.search(handText) 

317 elif self.sitename == "Everest": 

318 m = self.re_GameInfo2.search(handText) 

319 elif self.sitename == "Microgaming": 

320 m = self.re_GameInfo3.search(handText) 

321 if not m: 

322 tmp = handText[0:200] 

323 log.error(("PokerTrackerToFpdb.determineGameType: '%s'") % tmp) 

324 raise FpdbParseError 

325 

326 mg = m.groupdict() 

327 # print 'DEBUG determineGameType', '%r' % mg 

328 if "LIMIT" in mg and mg["LIMIT"] is not None: 

329 info["limitType"] = self.limits[mg["LIMIT"]] 

330 if "GAME" in mg: 

331 (info["base"], info["category"]) = self.games[mg["GAME"]] 

332 if mg["LIMIT"] is None: 

333 if info["category"] == "omahahi": 

334 info["limitType"] = "pl" 

335 elif info["category"] == "holdem": 

336 info["limitType"] = "nl" 

337 if "SB" in mg: 

338 info["sb"] = self.clearMoneyString(mg["SB"]) 

339 if "BB" in mg: 

340 info["bb"] = self.clearMoneyString(mg["BB"]) 

341 if "CURRENCY" in mg and mg["CURRENCY"] is not None: 

342 if self.sitename == "Microgaming" and not mg["CURRENCY"]: 

343 m1 = self.re_Currency.search(mg["TABLE"]) 

344 if m1: 

345 mg["CURRENCY"] = m1.group("CURRENCY") 

346 if self.sitename == "iPoker" and not mg["CURRENCY"]: 

347 m1 = self.re_PlayerInfo1.search(handText) 

348 if m1: 

349 mg["CURRENCY"] = m1.group("CURRENCY") 

350 info["currency"] = self.currencies[mg["CURRENCY"]] 

351 if "MIXED" in mg: 

352 if mg["MIXED"] is not None: 

353 info["mix"] = self.mixes[mg["MIXED"]] 

354 

355 if "TOUR" in mg and mg["TOUR"] is not None: 

356 info["type"] = "tour" 

357 info["currency"] = "T$" 

358 else: 

359 info["type"] = "ring" 

360 

361 if info["limitType"] == "fl" and info["bb"] is not None: 

362 if info["type"] == "ring": 

363 try: 

364 bb = self.clearMoneyString(mg["BB"]) 

365 info["sb"] = self.Lim_Blinds[bb][0] 

366 info["bb"] = self.Lim_Blinds[bb][1] 

367 except KeyError: 

368 tmp = handText[0:200] 

369 log.error( 

370 ("PokerTrackerToFpdb.determineGameType: Lim_Blinds has no lookup for '%s' - '%s'") 

371 % (mg["BB"], tmp) 

372 ) 

373 raise FpdbParseError 

374 else: 

375 sb = self.clearMoneyString(mg["SB"]) 

376 info["sb"] = str((old_div(Decimal(sb), 2)).quantize(Decimal("0.01"))) 

377 info["bb"] = str(Decimal(sb).quantize(Decimal("0.01"))) 

378 

379 return info 

380 

381 def readHandInfo(self, hand): 

382 info, m = {}, None 

383 if self.sitename in ("iPoker", "Merge"): 

384 m3 = self.re_Tournament.search(hand.handText, re.DOTALL) 

385 if m3: 

386 m = self.re_HandInfo_Tour.search(hand.handText, re.DOTALL) 

387 else: 

388 m = self.re_HandInfo_Cash.search(hand.handText, re.DOTALL) 

389 m2 = self.re_GameInfo1.search(hand.handText) 

390 elif self.sitename == "Everest": 

391 m2 = self.re_GameInfo2.search(hand.handText) 

392 elif self.sitename == "Microgaming": 

393 m2 = self.re_GameInfo3.search(hand.handText) 

394 if (m is None and self.sitename not in ("Everest", "Microgaming")) or m2 is None: 

395 tmp = hand.handText[0:200] 

396 log.error(("PokerTrackerToFpdb.readHandInfo: '%s'") % tmp) 

397 raise FpdbParseError 

398 

399 if self.sitename not in ("Everest", "Microgaming"): 

400 info.update(m.groupdict()) 

401 info.update(m2.groupdict()) 

402 

403 if self.sitename != "Everest" and info.get("UNCALLED") is None: 

404 hand.setUncalledBets(True) 

405 

406 # print 'readHandInfo', info 

407 for key in info: 

408 if key == "DATETIME": 

409 # 2008/11/12 10:00:48 CET [2008/11/12 4:00:48 ET] # (both dates are parsed so ET date overrides the other) 

410 # 2008/08/17 - 01:14:43 (ET) 

411 # 2008/09/07 06:23:14 ET 

412 if self.sitename in ("iPoker", "Microgaming"): 

413 m1 = self.re_DateTime1.finditer(info[key]) 

414 elif self.sitename == "Merge": 

415 m1 = self.re_DateTime2.finditer(info[key]) 

416 elif self.sitename == "Everest": 

417 m1 = self.re_DateTime3.finditer(info[key]) 

418 datetimestr = "2000/01/01 00:00:00" # default used if time not found 

419 for a in m1: 

420 datetimestr = "%s/%s/%s %s:%s:%s" % ( 

421 a.group("Y"), 

422 a.group("M"), 

423 a.group("D"), 

424 a.group("H"), 

425 a.group("MIN"), 

426 a.group("S"), 

427 ) 

428 # tz = a.group('TZ') # just assume ET?? 

429 # print " tz = ", tz, " datetime =", datetimestr 

430 hand.startTime = datetime.datetime.strptime( 

431 datetimestr, "%Y/%m/%d %H:%M:%S" 

432 ) # also timezone at end, e.g. " ET" 

433 hand.startTime = HandHistoryConverter.changeTimezone(hand.startTime, "ET", "UTC") 

434 if key == "HID": 

435 if self.sitename == "Merge": 

436 hand.handid = info[key][:8] + info[key][9:] 

437 else: 

438 hand.handid = info[key] 

439 if key == "TOURNO": 

440 hand.tourNo = info[key] 

441 if key == "BUYIN": 

442 if hand.tourNo is not None: 

443 tourneyname = "" 

444 if self.sitename == "Merge": 

445 if self.Structures is None: 

446 self.Structures = MergeStructures.MergeStructures() 

447 tourneyname = re.split(",", m.group("TABLE"))[0].strip() 

448 structure = self.Structures.lookupSnG(tourneyname, hand.startTime) 

449 if structure is not None: 

450 hand.buyin = int(100 * structure["buyIn"]) 

451 hand.fee = int(100 * structure["fee"]) 

452 hand.buyinCurrency = structure["currency"] 

453 hand.maxseats = structure["seats"] 

454 hand.isSng = True 

455 else: 

456 # print 'DEBUG', 'no match for tourney %s tourNo %s' % (tourneyname, hand.tourNo) 

457 hand.buyin = 0 

458 hand.fee = 0 

459 hand.buyinCurrency = "NA" 

460 hand.maxseats = None 

461 if self.sitename != "Merge" or hand.buyin == 0: 

462 if info[key] == "Freeroll" or "Free" in tourneyname: 

463 hand.buyin = 0 

464 hand.fee = 0 

465 hand.buyinCurrency = "FREE" 

466 else: 

467 if info[key].find("$") != -1: 

468 hand.buyinCurrency = "USD" 

469 elif info[key].find("£") != -1: 

470 hand.buyinCurrency = "GBP" 

471 elif info[key].find("€") != -1: 

472 hand.buyinCurrency = "EUR" 

473 elif re.match("^[0-9+]*$", info[key]): 

474 hand.buyinCurrency = "play" 

475 else: 

476 # FIXME: handle other currencies, play money 

477 log.error( 

478 ("PokerTrackerToFpdb.readHandInfo: Failed to detect currency.") 

479 + " Hand ID: %s: '%s'" % (hand.handid, info[key]) 

480 ) 

481 raise FpdbParseError 

482 

483 info["BIAMT"] = info["BIAMT"].strip("$€£") 

484 info["BIRAKE"] = info["BIRAKE"].strip("$€£") 

485 

486 hand.buyin = int(100 * Decimal(info["BIAMT"])) 

487 hand.fee = int(100 * Decimal(info["BIRAKE"])) 

488 if key == "TABLE": 

489 if hand.gametype["type"] == "tour": 

490 hand.tablename = "0" 

491 elif hand.gametype["type"] == "tour" and self.sitename == "Microgaming": 

492 hand.tablename = info[key] 

493 else: 

494 hand.tablename = re.split(",", info[key])[0] 

495 hand.tablename = hand.tablename.strip() 

496 if "Blaze" in hand.tablename: 

497 hand.gametype["fast"] = True 

498 if self.sitename == "Microgaming": 

499 m3 = self.re_Max.search(hand.tablename) 

500 if m3 and m3.group("MAX"): 

501 if m3.group("MAX") == "HU": 

502 hand.maxseats = 2 

503 elif len(m3.group("MAX").split(" ")) == 2: 

504 hand.maxseats = int(m3.group("MAX").split(" ")[0]) 

505 if key == "BUTTON": 

506 hand.buttonpos = info[key] 

507 if key == "MAX" and info[key] is not None: 

508 seats = int(info[key]) 

509 if seats <= 10: 

510 hand.maxseats = int(info[key]) 

511 

512 if key == "PLAY" and info["PLAY"] is not None and info["PLAY"] == "Play": 

513 # hand.currency = 'play' # overrides previously set value 

514 hand.gametype["currency"] = "play" 

515 

516 if self.re_FastFold.search(hand.handText): 

517 hand.fastFold = True 

518 

519 if self.re_Cancelled.search(hand.handText): 

520 raise FpdbHandPartial(("Hand '%s' was cancelled.") % hand.handid) 

521 

522 def readButton(self, hand): 

523 m = self.re_Button.search(hand.handText) 

524 if m: 

525 hand.buttonpos = int(m.group("BUTTON")) 

526 else: 

527 log.info("readButton: " + ("not found")) 

528 

529 def readPlayerStacks(self, hand): 

530 if self.sitename != "Microgaming": 

531 m = self.re_PlayerInfo1.finditer(hand.handText) 

532 else: 

533 m = self.re_PlayerInfo2.finditer(hand.handText) 

534 for a in m: 

535 # print a.group('SEAT'), a.group('PNAME'), a.group('CASH') 

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

537 if a.group("BUTTON") is not None: 

538 hand.buttonpos = int(a.group("SEAT")) 

539 if len(hand.players) == 1: 

540 raise FpdbHandPartial(("Hand '%s' was cancelled.") % hand.handid) 

541 

542 def markStreets(self, hand): 

543 # PREFLOP = ** Dealing down cards ** 

544 # This re fails if, say, river is missing; then we don't get the ** that starts the river. 

545 if self.sitename == "Microgaming": 

546 m = re.search( 

547 r"\*\* Dealing ca(?P<PREFLOP>.+(?=\*\* Dealing the flop)|.+)" 

548 r"(\*\* Dealing the flop(?P<FLOP>:\s.+(?=\*\* Dealing the turn)|.+))?" 

549 r"(\*\* Dealing the turn(?P<TURN>:\s.+(?=\*\* Dealing the river)|.+))?" 

550 r"(\*\* Dealing the river(?P<RIVER>:\s.+))?", 

551 hand.handText, 

552 re.DOTALL, 

553 ) 

554 else: 

555 m = re.search( 

556 r"\*\*\* HOLE CARDS \*\*\*(?P<PREFLOP>.+(?=\*\*\* FLOP \*\*\*)|.+)" 

557 r"(\*\*\* FLOP \*\*\*(?P<FLOP> \[\S\S\S? \S\S\S? \S\S\S?\].+(?=\*\*\* TURN \*\*\*)|.+))?" 

558 r"(\*\*\* TURN \*\*\* (?P<TURN>\[\S\S\S?\].+(?=\*\*\* RIVER \*\*\*)|.+))?" 

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

560 hand.handText, 

561 re.DOTALL, 

562 ) 

563 hand.addStreets(m) 

564 

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

566 if street in ( 

567 "FLOP", 

568 "TURN", 

569 "RIVER", 

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

571 # print "DEBUG readCommunityCards:", street, hand.streets.group(street) 

572 if self.sitename == "Microgaming": 

573 m = self.re_Board2.search(hand.streets[street]) 

574 cards = [c.replace("10", "T").strip() for c in m.group("CARDS").replace(" of ", "").split(", ")] 

575 else: 

576 m = self.re_Board1.search(hand.streets[street]) 

577 if self.sitename == "iPoker": 

578 cards = [c[1:].replace("10", "T") + c[0].lower() for c in m.group("CARDS").split(" ")] 

579 else: 

580 cards = [c.replace("10", "T").strip() for c in m.group("CARDS").split(" ")] 

581 hand.setCommunityCards(street, cards) 

582 

583 def readAntes(self, hand): 

584 log.debug(("reading antes")) 

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

586 for player in m: 

587 # ~ logging.debug("hand.addAnte(%s,%s)" %(player.group('PNAME'), player.group('ANTE'))) 

588 self.adjustMergeTourneyStack(hand, player.group("PNAME"), player.group("ANTE")) 

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

590 

591 def readBlinds(self, hand): 

592 liveBlind, bb, sb = True, None, None 

593 for a in self.re_PostSB.finditer(hand.handText): 

594 sb = self.clearMoneyString(a.group("SB")) 

595 if liveBlind: 

596 self.adjustMergeTourneyStack(hand, a.group("PNAME"), a.group("SB")) 

597 hand.addBlind(a.group("PNAME"), "small blind", sb) 

598 if not hand.gametype["sb"]: 

599 hand.gametype["sb"] = sb 

600 liveBlind = False 

601 elif hand.gametype["type"] == "tour": 

602 self.adjustMergeTourneyStack(hand, a.group("PNAME"), a.group("SB")) 

603 if not hand.gametype["bb"]: 

604 hand.gametype["bb"] = sb 

605 hand.addBlind(a.group("PNAME"), "big blind", sb) 

606 else: 

607 # Post dead blinds as ante 

608 self.adjustMergeTourneyStack(hand, a.group("PNAME"), a.group("SB")) 

609 hand.addBlind(a.group("PNAME"), "secondsb", sb) 

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

611 bb = self.clearMoneyString(a.group("BB")) 

612 self.adjustMergeTourneyStack(hand, a.group("PNAME"), a.group("BB")) 

613 if not hand.gametype["bb"]: 

614 hand.gametype["bb"] = bb 

615 hand.addBlind(a.group("PNAME"), "big blind", bb) 

616 else: 

617 both = Decimal(hand.gametype["bb"]) + old_div(Decimal(hand.gametype["bb"]), 2) 

618 if both == Decimal(a.group("BB")): 

619 hand.addBlind(a.group("PNAME"), "both", bb) 

620 else: 

621 hand.addBlind(a.group("PNAME"), "big blind", bb) 

622 

623 if self.sitename == "Microgaming": 

624 for a in self.re_PostBoth2.finditer(hand.handText): 

625 if self.clearMoneyString(a.group("SBBB")) == hand.gametype["sb"]: 

626 hand.addBlind(a.group("PNAME"), "secondsb", self.clearMoneyString(a.group("SBBB"))) 

627 else: 

628 bet = self.clearMoneyString(a.group("SBBB")) 

629 amount = str(Decimal(bet) + old_div(Decimal(bet), 2)) 

630 hand.addBlind(a.group("PNAME"), "both", amount) 

631 for a in self.re_Action2.finditer(self.re_Hole.split(hand.handText)[0]): 

632 if a.group("ATYPE") == " went all-in": 

633 amount = Decimal(self.clearMoneyString(a.group("BET"))) 

634 player = a.group("PNAME") 

635 if bb is None: 

636 hand.addBlind(player, "big blind", self.clearMoneyString(a.group("BET"))) 

637 self.allInBlind(hand, "PREFLOP", a, "big blind") 

638 elif sb is None: 

639 hand.addBlind(player, "small blind", self.clearMoneyString(a.group("BET"))) 

640 self.allInBlind(hand, "PREFLOP", a, "small blind") 

641 else: 

642 for a in self.re_PostBoth1.finditer(hand.handText): 

643 self.adjustMergeTourneyStack(hand, a.group("PNAME"), a.group("SBBB")) 

644 if Decimal(str(hand.sb)) == Decimal(self.clearMoneyString(a.group("SBBB"))): 

645 hand.addBlind(a.group("PNAME"), "small blind", self.clearMoneyString(a.group("SBBB"))) 

646 else: 

647 hand.addBlind(a.group("PNAME"), "both", self.clearMoneyString(a.group("SBBB"))) 

648 

649 # FIXME 

650 # The following should only trigger when a small blind is missing in a tournament, or the sb/bb is ALL_IN 

651 # see http://sourceforge.net/apps/mantisbt/fpdb/view.php?id=115 

652 if hand.gametype["type"] == "tour" and self.sitename in ("Merge", "iPoker"): 

653 if hand.gametype["sb"] is None and hand.gametype["bb"] is None: 

654 hand.gametype["sb"] = "1" 

655 hand.gametype["bb"] = "2" 

656 elif hand.gametype["sb"] is None: 

657 hand.gametype["sb"] = str(old_div(int(Decimal(hand.gametype["bb"])), 2)) 

658 elif hand.gametype["bb"] is None: 

659 hand.gametype["bb"] = str(int(Decimal(hand.gametype["sb"])) * 2) 

660 if old_div(int(Decimal(hand.gametype["bb"])), 2) != int(Decimal(hand.gametype["sb"])): 

661 if old_div(int(Decimal(hand.gametype["bb"])), 2) < int(Decimal(hand.gametype["sb"])): 

662 hand.gametype["bb"] = str(int(Decimal(hand.gametype["sb"])) * 2) 

663 else: 

664 hand.gametype["sb"] = str(old_div(int(Decimal(hand.gametype["bb"])), 2)) 

665 

666 def readHoleCards(self, hand): 

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

668 # we need to grab hero's cards 

669 if self.sitename != "Microgaming": 

670 re_HeroCards = self.re_HeroCards1 

671 else: 

672 re_HeroCards = self.re_HeroCards2 

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

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

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

676 for found in m: 

677 # if m == None: 

678 # hand.involved = False 

679 # else: 

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

681 if self.sitename == "iPoker": 

682 newcards = [c[1:].replace("10", "T") + c[0].lower() for c in found.group("NEWCARDS").split(" ")] 

683 elif self.sitename == "Microgaming": 

684 newcards = [ 

685 c.replace("10", "T").strip() 

686 for c in found.group("NEWCARDS").replace(" of ", "").split(", ") 

687 ] 

688 else: 

689 newcards = [c.replace("10", "T").strip() for c in found.group("NEWCARDS").split(" ")] 

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

691 

692 for street, text in list(hand.streets.items()): 

693 if not text or street in ("PREFLOP", "DEAL"): 

694 continue # already done these 

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

696 for found in m: 

697 player = found.group("PNAME") 

698 if found.group("NEWCARDS") is None: 

699 newcards = [] 

700 else: 

701 if self.sitename == "iPoker": 

702 newcards = [c[1:].replace("10", "T") + c[0].lower() for c in found.group("NEWCARDS").split(" ")] 

703 elif self.sitename == "Microgaming": 

704 newcards = [ 

705 c.replace("10", "T").strip() 

706 for c in found.group("NEWCARDS").replace(" of ", "").split(", ") 

707 ] 

708 else: 

709 newcards = [c.replace("10", "T").strip() for c in found.group("NEWCARDS").split(" ")] 

710 if found.group("OLDCARDS") is None: 

711 oldcards = [] 

712 else: 

713 if self.sitename == "iPoker": 

714 oldcards = [c[1:].replace("10", "T") + c[0].lower() for c in found.group("OLDCARDS").split(" ")] 

715 else: 

716 oldcards = [c.replace("10", "T").strip() for c in found.group("OLDCARDS").split(" ")] 

717 

718 hand.addHoleCards( 

719 street, player, open=newcards, closed=oldcards, shown=False, mucked=False, dealt=False 

720 ) 

721 

722 def readAction(self, hand, street): 

723 if self.sitename != "Microgaming": 

724 m = self.re_Action1.finditer(hand.streets[street]) 

725 else: 

726 m = self.re_Action2.finditer(hand.streets[street]) 

727 curr_pot = Decimal("0") 

728 for action in m: 

729 action.groupdict() 

730 # print "DEBUG: acts: %s" %acts 

731 if action.group("ATYPE") in (" folds", " Fold", " folded"): 

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

733 elif action.group("ATYPE") in (" checks", " Check", " checked"): 

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

735 elif action.group("ATYPE") in (" calls", " Call", " called"): 

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

737 elif action.group("ATYPE") in (" raises", " Raise", " raised", " raised to", " Raise to"): 

738 amount = Decimal(self.clearMoneyString(action.group("BET"))) 

739 if self.sitename == "Merge": 

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

741 elif self.sitename == "Microgaming" or action.group("ATYPE") == " Raise to": 

742 hand.addCallandRaise(street, action.group("PNAME"), action.group("BET")) 

743 else: 

744 if curr_pot > amount: 

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

746 # elif not action.group('RAISETO') and action.group('ATYPE')==' Raise': 

747 # hand.addRaiseBy( street, action.group('PNAME'), action.group('BET') ) 

748 else: 

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

750 curr_pot = amount 

751 elif action.group("ATYPE") in (" bets", " Bet", " bet"): 

752 if self.sitename == "Microgaming" and street in ("PREFLOP", "THIRD", "DEAL"): 

753 hand.addCallandRaise(street, action.group("PNAME"), action.group("BET")) 

754 else: 

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

756 curr_pot = Decimal(self.clearMoneyString(action.group("BET"))) 

757 elif action.group("ATYPE") in (" Allin", " went all-in"): 

758 amount = Decimal(self.clearMoneyString(action.group("BET"))) 

759 hand.addAllIn(street, action.group("PNAME"), action.group("BET")) 

760 if curr_pot > amount and curr_pot > Decimal("0") and self.sitename == "Microgaming": 

761 hand.setUncalledBets(False) 

762 curr_pot = amount 

763 else: 

764 log.debug( 

765 ("DEBUG:") 

766 + " " 

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

768 ) 

769 

770 def allInBlind(self, hand, street, action, actiontype): 

771 if street in ("PREFLOP", "DEAL"): 

772 player = action.group("PNAME") 

773 if hand.stacks[player] == 0: 

774 hand.setUncalledBets(True) 

775 

776 def adjustMergeTourneyStack(self, hand, player, amount): 

777 if self.sitename == "Merge": 

778 amount = Decimal(self.clearMoneyString(amount)) 

779 if hand.gametype["type"] == "tour": 

780 for p in hand.players: 

781 if p[1] == player: 

782 stack = Decimal(p[2]) 

783 stack += amount 

784 p[2] = str(stack) 

785 hand.stacks[player] += amount 

786 

787 def readCollectPot(self, hand): 

788 if self.sitename == "Microgaming": 

789 for m in self.re_CollectPot2.finditer(hand.handText): 

790 hand.addCollectPot(player=m.group("PNAME"), pot=re.sub(",", "", m.group("POT"))) 

791 else: 

792 for m in self.re_CollectPot1.finditer(hand.handText): 

793 hand.addCollectPot(player=m.group("PNAME"), pot=re.sub(",", "", m.group("POT"))) 

794 

795 def readShowdownActions(self, hand): 

796 pass 

797 

798 def readShownCards(self, hand): 

799 found = [] 

800 if self.sitename == "Microgaming": 

801 re_ShownCards = self.re_ShownCards2 

802 else: 

803 re_ShownCards = self.re_ShownCards1 

804 for m in re_ShownCards.finditer(hand.handText): 

805 if m.group("CARDS") is not None and m.group("PNAME") not in found: 

806 if self.sitename == "iPoker": 

807 cards = [c[1:].replace("10", "T") + c[0].lower() for c in m.group("CARDS").split(" ")] 

808 elif self.sitename == "Microgaming": 

809 cards = [c.replace("10", "T").strip() for c in m.group("CARDS").replace(" of ", "").split(", ")] 

810 else: 

811 cards = [c.replace("10", "T").strip() for c in m.group("CARDS").split(" ")] 

812 

813 (shown, mucked) = (False, False) 

814 if m.group("SHOWED") in ("shows", "Shows"): 

815 shown = True 

816 elif m.group("SHOWED") in ("mucked", "mucks"): 

817 mucked = True 

818 found.append(m.group("PNAME")) 

819 

820 # print "DEBUG: hand.addShownCards(%s, %s, %s, %s)" %(cards, m.group('PNAME'), shown, mucked) 

821 hand.addShownCards(cards=cards, player=m.group("PNAME"), shown=shown, mucked=mucked)