Coverage for PartyPokerToFpdb.py: 0%

481 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-09-28 16:41 +0000

1#!/usr/bin/env python 

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

3# 

4# Copyright 2009-2011, Grigorij Indigirkin 

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#import L10n 

22#_ = L10n.get_translation() 

23 

24import sys 

25from collections import defaultdict 

26 

27from Configuration import LOCALE_ENCODING 

28from Exceptions import FpdbParseError 

29from HandHistoryConverter import * 

30 

31# PartyPoker HH Format 

32 

33class PartyPoker(HandHistoryConverter): 

34 sitename = "PartyPoker" 

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

36 siteId = 9 

37 filetype = "text" 

38 sym = {'USD': "\$", 'EUR': u"\u20ac", 'T$': "", 'play':'play'} 

39 currencies = {"\$": "USD", "$": "USD", u"\xe2\x82\xac": "EUR", u"\u20ac": "EUR", '': "T$", 'play':'play'} 

40 substitutions = { 

41 'LEGAL_ISO' : "USD|EUR", # legal ISO currency codes 

42 'LS' : u"\$|\u20ac|\xe2\x82\xac|", # Currency symbols - Euro(cp1252, utf-8) 

43 'NUM' : u".,'\dKMB", 

44 } 

45 limits = { 'NL':'nl', 'PL':'pl', '':'fl', 'FL':'fl', 'Limit':'fl' } 

46 games = { # base, category 

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

48 "Texas Holdem" : ('hold','holdem'), 

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

50 "Holdem" : ('hold','holdem'), 

51 'Omaha' : ('hold','omahahi'), 

52 'Omaha Hi' : ('hold','omahahi'), 

53 'Omaha Hi-Lo' : ('hold','omahahilo'), 

54 "7 Card Stud Hi-Lo" : ('stud','studhilo'), 

55 "7 Card Stud" : ('stud','studhi'), 

56 "Double Hold'em" : ('hold','2_holdem'), 

57 "Double Holdem" : ('hold','2_holdem'), 

58 "Short Deck" : ('hold','6_holdem'), 

59 } 

60 

61 Lim_Blinds = { '0.04': ('0.01', '0.02'), '0.08': ('0.02', '0.04'), 

62 '0.10': ('0.02', '0.05'), '0.20': ('0.05', '0.10'), 

63 '0.30': ('0.07', '0.15'), '0.50': ('0.10', '0.25'), 

64 '1.00': ('0.25', '0.50'), '1': ('0.25', '0.50'), 

65 '2.00': ('0.50', '1.00'), '2': ('0.50', '1.00'), 

66 '4.00': ('1.00', '2.00'), '4': ('1.00', '2.00'), 

67 '6.00': ('1.00', '3.00'), '6': ('1.00', '3.00'), 

68 '8.00': ('2.00', '4.00'), '8': ('2.00', '4.00'), 

69 '10.00': ('2.00', '5.00'), '10': ('2.00', '5.00'), 

70 '12.00': ('3.00', '6.00'), '12': ('3.00', '6.00'), 

71 '20.00': ('5.00', '10.00'), '20': ('5.00', '10.00'), 

72 '30.00': ('10.00', '15.00'), '30': ('10.00', '15.00'), 

73 '40.00': ('10.00', '20.00'), '40': ('10.00', '20.00'), 

74 '50.00': ('10.00', '25.00'), '50': ('10.00', '25.00'), 

75 '60.00': ('15.00', '30.00'), '60': ('15.00', '30.00'), 

76 '80.00': ('20.00', '40.00'), '80': ('20.00', '40.00'), 

77 '100.00': ('25.00', '50.00'), '100': ('25.00', '50.00'), 

78 '200.00': ('50.00', '100.00'), '200': ('50.00', '100.00'), 

79 '400.00': ('100.00', '200.00'), '400': ('100.00', '200.00'), 

80 '500.00': ('125.00', '250.00'), '500': ('125.00', '250.00'), 

81 '800.00': ('200.00', '400.00'), '800': ('200.00', '400.00'), 

82 } 

83 NLim_Blinds_20bb = { '0.80': ('0.01', '0.02'), 

84 '1.60': ('0.02', '0.04'), 

85 '4': ('0.05', '0.10'), 

86 '10': ('0.10', '0.25'), 

87 '20': ('0.25', '0.50'), 

88 '40': ('0.50', '1.00'), 

89 '240': ('3.00', '6.00'), 

90 #'10': ('0.10', '0.25'), 

91 #'10': ('0.10', '0.25'), 

92 #'10': ('0.10', '0.25'), 

93 #'10': ('0.10', '0.25'), 

94 } 

95 

96 months = { 'January':1, 'Jan':1, 'February':2, 'Feb':2, 'March':3, 'Mar':3, 

97 'April':4, 'Apr':4, 'May':5, 'May':5, 'June':6, 'Jun':6, 

98 'July':7, 'Jul':7, 'August':8, 'Aug':8, 'September':9, 'Sep':9, 

99 'October':10, 'Oct':10, 'November':11, 'Nov':11, 'December':12, 'Dec':12} 

100 

101 sites = { 'Poker Stars' : ('PokerStars', 2), 

102 'PokerMaster' : ('PokerMaster', 25), 

103 'IPoker' : ('iPoker', 14), 

104 'Party' : ('PartyPoker', 9), 

105 #'PMU Poker' : ('PMU Poker', 31), 

106 'Pacific' : ('PacificPoker', 10), 

107 'WPN' : ('WinningPoker', 24), 

108 'PokerBros' : ('PokerBros', 29) 

109 } 

110 

111 # Static regexes 

112 re_GameInfo = re.compile(u""" 

113 \*{5}\sHand\sHistory\s(F|f)or\sGame\s(?P<HID>\w+)\s\*{5}(\s\((?P<SITE>Poker\sStars|PokerMaster|Party|PartyPoker|IPoker|Pacific|WPN|PokerBros)\))?\s+ 

114 (.+?\shas\sleft\sthe\stable\.\s+)* 

115 (.+?\sfinished\sin\s\d+\splace\.\s+)* 

116 ((?P<CURRENCY>[%(LS)s]))?\s* 

117 ( 

118 ([%(LS)s]?(?P<SB>[%(NUM)s]+)/[%(LS)s]?(?P<BB>[%(NUM)s]+)\s*(?:%(LEGAL_ISO)s)?\s+(?P<FAST3>fastforward\s)?((?P<LIMIT3>NL|PL|FL|)\s+)?)| 

119 ((?P<CASHBI>[%(NUM)s]+)\s*(?:%(LEGAL_ISO)s)?\s*)(?P<FAST2>fastforward\s)?(?P<LIMIT2>(NL|PL|FL|))?\s* 

120 ) 

121 (Tourney\s*)? 

122 (?P<GAME>(Texas\sHold\'?em|Hold\'?em|Omaha\sHi-Lo|Omaha(\sHi)?|7\sCard\sStud\sHi-Lo|7\sCard\sStud|Double\sHold\'?em|Short\sDeck))\s* 

123 (Game\sTable\s*)? 

124 ( 

125 (\((?P<LIMIT>(NL|PL|FL|Limit|))\)\s*)? 

126 (\((?P<SNG>SNG|STT|MTT)(\sJackPot)?\sTournament\s\#(?P<TOURNO>\d+)\)\s*)? 

127 )? 

128 (?:\s\(Buyin\s(?P<BUYIN>[%(LS)s][%(NUM)s]+)\s\+\s(?P<FEE>[%(LS)s][%(NUM)s]+)\))? 

129 \s*-\s* 

130 (?P<DATETIME>.+) 

131 """ % substitutions, re.VERBOSE | re.UNICODE) 

132 

133 re_HandInfo = re.compile(u""" 

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

135 ((?: \#|\(|)(?P<TABLENO>\d+)\)?\s+)? 

136 (\(No\sDP\)\s)? 

137 \(\s?(?P<PLAY>Real|Play)\s+Money\s?\)\s+(--\s*)? # FIXME: check if play money is correct 

138 Seat\s+(?P<BUTTON>\d+)\sis\sthe\sbutton 

139 (\s+Total\s+number\s+of\s+players\s+\:\s+(?P<PLYRS>\d+)/?(?P<MAX>\d+)?)? 

140 """, re.VERBOSE|re.MULTILINE|re.DOTALL) 

141 

142 re_GameInfoTrny1 = re.compile(u""" 

143 \*{5}\sHand\sHistory\s(F|f)or\sGame\s(?P<HID>\w+)\s\*{5}\s+ 

144 (?P<LIMIT>(NL|PL|FL|))\s* 

145 (?P<GAME>(Texas\sHold\'em|Hold\'?em|Omaha\sHi-Lo|Omaha(\sHi)?|7\sCard\sStud\sHi-Lo|7\sCard\sStud|Double\sHold\'em|Short\sDeck))\s+ 

146 (?:(?P<BUYIN>[%(LS)s]?\s?[%(NUM)s]+)\s*(?P<BUYIN_CURRENCY>%(LEGAL_ISO)s)?\s*Buy-in\s+)? 

147 (\+\s(?P<FEE>[%(LS)s]?\s?[%(NUM)s]+)\sEntry\sFee\s+)? 

148 Trny:\s?(?P<TOURNO>\d+)\s+ 

149 Level:\s*(?P<LEVEL>\d+)\s+ 

150 ((Blinds|Stakes)(?:-Antes)?)\( 

151 (?P<SB>[%(NUM)s ]+)\s* 

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

153 (?:\s*-\s*(?P<ANTE>[%(NUM)s ]+)\$?)? 

154 \) 

155 \s*\-\s* 

156 (?P<DATETIME>.+) 

157 """ % substitutions, re.VERBOSE | re.UNICODE) 

158 

159 re_GameInfoTrny2 = re.compile(u""" 

160 \*{5}\sHand\sHistory\s(F|f)or\sGame\s(?P<HID>\w+)\s\*{5}\s+ 

161 (?P<LIMIT>(NL|PL|FL|))\s* 

162 (?P<GAME>(Texas\sHold\'em|Hold\'?em|Omaha\sHi-Lo|Omaha(\sHi)?|7\sCard\sStud\sHi-Lo|7\sCard\sStud|Double\sHold\'em|Short\sDeck))\s+ 

163 (?:(?P<BUYIN>[%(LS)s]?\s?[%(NUM)s]+)\s*(?P<BUYIN_CURRENCY>%(LEGAL_ISO)s)?\s*Buy-in\s+)? 

164 (\+\s(?P<FEE>[%(LS)s]?\s?[%(NUM)s]+)\sEntry\sFee\s+)? 

165 \s*\-\s* 

166 (?P<DATETIME>.+) 

167 """ % substitutions, re.VERBOSE | re.UNICODE) 

168 

169 re_GameInfoTrny3 = re.compile(u""" 

170 \*{5}\sHand\sHistory\s(F|f)or\sGame\s(?P<HID>\w+)\s\*{5}\s\((?P<SITE>Poker\sStars|PokerMaster|Party|IPoker|Pacific|WPN|PokerBros)\)\s+ 

171 Tourney\sHand\s 

172 (?P<LIMIT>(NL|PL|FL|))\s* 

173 (?P<GAME>(Texas\sHold\'em|Hold\'?em|Omaha\sHi-Lo|Omaha(\sHi)?|7\sCard\sStud\sHi-Lo|7\sCard\sStud|Double\sHold\'em|Short\sDeck))\s+ 

174 \s*\-\s* 

175 (?P<DATETIME>.+) 

176 """ % substitutions, re.VERBOSE | re.UNICODE) 

177 

178 re_Blinds = re.compile(""" 

179 ^((Blinds|Stakes)(?:-Antes)?)\( 

180 (?P<SB>[%(NUM)s ]+)\s* 

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

182 (?:\s*-\s*(?P<ANTE>[%(NUM)s ]+)\$?)? 

183 \)$""" % substitutions, re.VERBOSE | re.MULTILINE) 

184 

185 re_TourNoLevel = re.compile(""" 

186 Trny:\s?(?P<TOURNO>\d+)\s+ 

187 Level:\s*(?P<LEVEL>\d+) 

188 """, re.VERBOSE) 

189 

190 re_PlayerInfo = re.compile(u""" 

191 (S|s)eat\s?(?P<SEAT>\d+):\s 

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

193 \(\s*[%(LS)s]?(?P<CASH>[%(NUM)s]+)\s*(?:%(LEGAL_ISO)s|)\s*\) 

194 """ % substitutions, re.VERBOSE| re.UNICODE) 

195 

196 re_NewLevel = re.compile(u"Blinds(-Antes)?\((?P<SB>[%(NUM)s ]+)/(?P<BB>[%(NUM)s ]+)(?:\s*-\s*(?P<ANTE>[%(NUM)s ]+))?\)" % substitutions, re.VERBOSE|re.MULTILINE|re.DOTALL) 

197 re_CountedSeats = re.compile("Total\s+number\s+of\s+players\s*:\s*(?P<COUNTED_SEATS>\d+)", re.MULTILINE) 

198 re_Identify = re.compile(u'\*{5}\sHand\sHistory\s[fF]or\sGame\s\d+\w+?\s') 

199 re_SplitHands = re.compile('\n\n+') 

200 re_TailSplitHands = re.compile('(\x00+)') 

201 lineSplitter = '\n' 

202 re_Button = re.compile('Seat (?P<BUTTON>\d+) is the button', re.MULTILINE) 

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

204 re_NoSmallBlind = re.compile( 

205 '^There is no Small Blind in this hand as the Big Blind ' 

206 'of the previous hand left the table', re.MULTILINE) 

207 re_20BBmin = re.compile(r"Table 20BB Min") 

208 re_Cancelled = re.compile('Table\sClosed\s?', re.MULTILINE) 

209 re_Disconnected = re.compile('Connection\sLost\sdue\sto\ssome\sreason\s?', re.MULTILINE) 

210 re_GameStartLine = re.compile('Game\s\#\d+\sstarts', re.MULTILINE) 

211 re_emailedHand = re.compile(r'\*\*\sSummary\s\*\*') 

212 

213 def allHandsAsList(self): 

214 list = HandHistoryConverter.allHandsAsList(self) 

215 if list is None: 

216 return [] 

217 return filter(lambda text: len(text.strip()), list) 

218 

219 def compilePlayerRegexs(self, hand): 

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

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

222 self.compiledPlayers = players 

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

224 subst = {'PLYR': player_re, 'CUR_SYM': self.sym[hand.gametype['currency']], 

225 'CUR': hand.gametype['currency'] if hand.gametype['currency']!='T$' else '(chips|)', 

226 'BRAX' : u"\[\(\)\]" 

227 } 

228 self.re_PostSB = re.compile( 

229 r"%(PLYR)s posts small blind [%(BRAX)s]?%(CUR_SYM)s?(?P<SB>[.,0-9]+)\s*(%(CUR)s)?[%(BRAX)s]?\.?\s*$" 

230 % subst, re.MULTILINE) 

231 self.re_PostBB = re.compile( 

232 r"%(PLYR)s posts big blind [%(BRAX)s]?%(CUR_SYM)s?(?P<BB>[.,0-9]+)\s*(%(CUR)s)?[%(BRAX)s]?\.?\s*$" 

233 % subst, re.MULTILINE) 

234 self.re_PostDead = re.compile( 

235 r"%(PLYR)s posts big blind \+ dead [%(BRAX)s]?%(CUR_SYM)s?(?P<BBNDEAD>[.,0-9]+)\s*%(CUR_SYM)s?[%(BRAX)s]?\.?\s*$" % subst, 

236 re.MULTILINE) 

237 self.re_PostBUB = re.compile( 

238 r"%(PLYR)s posts button blind ?[%(BRAX)s]?%(CUR_SYM)s?(?P<BUB>[.,0-9]+)\s*%(CUR)s?[%(BRAX)s]?\.?\s*$" % subst, 

239 re.MULTILINE) 

240 self.re_Antes = re.compile( 

241 r"%(PLYR)s posts ante( of)? [%(BRAX)s]?%(CUR_SYM)s?(?P<ANTE>[.,0-9]+)\s*(%(CUR)s)?[%(BRAX)s]?\.?\s*$" % subst, 

242 re.MULTILINE) 

243 self.re_HeroCards = re.compile( 

244 r"Dealt to %(PLYR)s \[\s*(?P<NEWCARDS>.+)\s*\]" % subst, 

245 re.MULTILINE) 

246 self.re_Action = re.compile(r""" 

247 (?P<PNAME>.+?)\s(?P<ATYPE>bets|checks|raises|completes|bring-ins|calls|folds|is\sall-In|double\sbets) 

248 (?:\s*[%(BRAX)s]?\s?%(CUR_SYM)s?(?P<BET>[.,\d]+)\s*(%(CUR)s)?\s?[%(BRAX)s]?)? 

249 (\sto\s[.,\d]+)? 

250 \.?\s*$""" % subst, re.MULTILINE|re.VERBOSE) 

251 if not hand.emailedHand: 

252 self.re_ShownCards = re.compile( 

253 r"%s (?P<SHOWED>(?:doesn\'t )?shows?) " % player_re + 

254 r"\[ *(?P<CARDS>.+) *\](?P<COMBINATION>.+)\.", 

255 re.MULTILINE) 

256 else: 

257 #Michow111 balance $113, bet $50, collected $110.25, net +$60.25 [ 8h 9h ] [ a straight, seven to jack -- Jc,Td,9h,8h,7c ] 

258 #babunchik balance $0, lost $50 [ Kd Js ] [ a pair of jacks -- Kd,Js,Jc,Td,7c ] 

259 self.re_ShownCards = re.compile( 

260 r"%(PLYR)s balance.*" % subst + 

261 r"\[ (?P<CARDS>.+) \] *\[ *(?P<COMBINATION>.+) \-\-", 

262 re.MULTILINE) 

263 if not hand.emailedHand: 

264 self.re_CollectPot = re.compile( 

265 r"""%(PLYR)s\s+wins\s+(Lo\s\()?%(CUR_SYM)s?(?P<POT>[.,\d]+)\s*(%(CUR)s)?\)?""" % subst, 

266 re.MULTILINE|re.VERBOSE) 

267 else: 

268 self.re_CollectPot = re.compile( 

269 r"""%(PLYR)s(\sbalance\s%(CUR_SYM)s?[.,\d]+,)(\sbet\s%(CUR_SYM)s?[.,\d]+,)(\scollected\s%(CUR_SYM)s?(?P<POT>[.,\d]+),)""" % subst, 

270 re.MULTILINE|re.VERBOSE) 

271 

272 def readSupportedGames(self): 

273 return [["ring", "hold", "nl"], 

274 ["ring", "hold", "pl"], 

275 ["ring", "hold", "fl"], 

276 

277 ["ring", "stud", "fl"], 

278 

279 ["tour", "hold", "nl"], 

280 ["tour", "hold", "pl"], 

281 ["tour", "hold", "fl"], 

282 

283 ["tour", "stud", "fl"], 

284 ] 

285 

286 def determineGameType(self, handText): 

287 handText = handText.replace(u'\x00', u'') 

288 info, extra = {}, {} 

289 m = self.re_GameInfo.search(handText) 

290 if not m: 

291 m = self.re_GameInfoTrny1.search(handText) 

292 if not m: 

293 m = self.re_GameInfoTrny2.search(handText) 

294 m2 = self.re_TourNoLevel.search(handText) 

295 m3 = self.re_Blinds.search(handText) 

296 if m2 and m3: 

297 extra.update(m2.groupdict()) 

298 extra.update(m3.groupdict()) 

299 else: 

300 m = None 

301 if not m: 

302 m = self.re_GameInfoTrny3.search(handText) 

303 if not m: 

304 m = self.re_Disconnected.search(handText) 

305 if m: 

306 message = ("Player Disconnected") 

307 raise FpdbHandPartial("Partial hand history: %s" % message) 

308 m = self.re_Cancelled.search(handText) 

309 if m: 

310 message = ("Table Closed") 

311 raise FpdbHandPartial("Partial hand history: %s" % message) 

312 m = self.re_GameStartLine.match(handText) 

313 if m and len(handText)<50: 

314 message = ("Game start line") 

315 raise FpdbHandPartial("Partial hand history: %s" % message) 

316 tmp = handText[0:200] 

317 log.error(("PartyPokerToFpdb.determineGameType: '%s'") % tmp) 

318 raise FpdbParseError 

319 

320 mg = m.groupdict() 

321 mg.update(extra) 

322 #print "DEBUG: mg: %s" % mg 

323 

324 if 'SITE' in mg and mg['SITE'] != None: 

325 self.sitename = self.sites[mg['SITE']][0] 

326 self.siteId = self.sites[mg['SITE']][1]# Needs to match id entry in Sites database 

327 print('self.siteId', self.siteId) 

328 print('self.sitename', self.sitename) 

329 if 'LIMIT' in mg and mg['LIMIT'] != None: 

330 info['limitType'] = self.limits[mg['LIMIT']] 

331 if 'LIMIT2' in mg and mg['LIMIT2'] != None: 

332 info['limitType'] = self.limits[mg['LIMIT2']] 

333 if 'LIMIT3' in mg and mg['LIMIT3'] != None: 

334 info['limitType'] = self.limits[mg['LIMIT3']] 

335 if 'FAST2' in mg and mg['FAST2'] != None: 

336 info['fast'] = True 

337 elif 'FAST3' in mg and mg['FAST3'] != None: 

338 info['fast'] = True 

339 else: 

340 info['fast'] = False 

341 if mg['LIMIT'] == None and mg['LIMIT2'] == None and mg['LIMIT3'] == None: 

342 info['limitType'] = 'fl' 

343 if 'GAME' in mg: 

344 (info['base'], info['category']) = self.games[mg['GAME']] 

345 if 'CASHBI' in mg and mg['CASHBI'] != None: 

346 # The summary is using buyin rather then listing the blinds 

347 # Only with NL games? 

348 mg['CASHBI'] = self.clearMoneyString(mg['CASHBI']) 

349 m_20BBmin = self.re_20BBmin.search(handText) 

350 if m_20BBmin is not None: 

351 try: 

352 info['sb'] = self.NLim_Blinds_20bb[mg['CASHBI']][0] 

353 info['bb'] = self.NLim_Blinds_20bb[mg['CASHBI']][1] 

354 info['buyinType'] = 'shallow' 

355 except KeyError: 

356 tmp = handText[0:200] 

357 log.error(("PartyPokerToFpdb.determineGameType: NLim_Blinds_20bb has no lookup for '%s' - '%s'") % (mg['CASHBI'], tmp)) 

358 raise FpdbParseError 

359 else: 

360 try: 

361 if Decimal(mg['CASHBI']) >= 10000: 

362 nl_bb = str((Decimal(mg['CASHBI'])/100).quantize(Decimal("0.01"))) 

363 info['buyinType'] = 'deep' 

364 else: 

365 nl_bb = str((Decimal(mg['CASHBI'])/50).quantize(Decimal("0.01"))) 

366 info['buyinType'] = 'regular' 

367 info['sb'] = self.Lim_Blinds[nl_bb][0] 

368 info['bb'] = self.Lim_Blinds[nl_bb][1] 

369 except KeyError: 

370 tmp = handText[0:200] 

371 log.error(("PartyPokerToFpdb.determineGameType: Lim_Blinds has no lookup for '%s' - '%s'") % (nl_bb, tmp)) 

372 raise FpdbParseError 

373 else: 

374 if info['category'] == '6_holdem': 

375 info['sb'] = '0' 

376 info['bb'] = self.clearMoneyString(mg['SB']) 

377 else: 

378 m = self.re_NewLevel.search(handText) 

379 if m: 

380 mg['SB'] = m.group('SB') 

381 mg['BB'] = m.group('BB') 

382 if 'SB' in mg: 

383 info['sb'] = self.clearMoneyString(mg['SB']) 

384 else: 

385 info['sb'] = None 

386 if 'BB' in mg: 

387 info['bb'] = self.clearMoneyString(mg['BB']) 

388 else: 

389 info['bb'] = None 

390 info['buyinType'] = 'regular' 

391 if 'CURRENCY' in mg: 

392 if mg['CURRENCY'] == None: 

393 info['currency'] = self.currencies['$'] 

394 else: 

395 info['currency'] = self.currencies[mg['CURRENCY']] 

396 if 'MIXED' in mg: 

397 if mg['MIXED'] is not None: info['mix'] = self.mixes[mg['MIXED']] 

398 

399 if 'TOURNO' in mg and mg['TOURNO'] is None: 

400 info['type'] = 'ring' 

401 else: 

402 info['type'] = 'tour' 

403 info['currency'] = "T$" 

404 

405 if info['limitType'] == 'fl' and info['bb'] is not None: 

406 if info['type'] == 'ring': 

407 try: 

408 info['sb'] = self.Lim_Blinds[mg['BB']][0] 

409 info['bb'] = self.Lim_Blinds[mg['BB']][1] 

410 except KeyError: 

411 tmp = handText[0:200] 

412 log.error(("PartyPokerToFpdb.determineGameType: Lim_Blinds has no lookup for '%s' - '%s'") % (mg['BB'], tmp)) 

413 raise FpdbParseError 

414 else: 

415 info['sb'] = str((old_div(Decimal(mg['SB']),2)).quantize(Decimal("0.01"))) 

416 info['bb'] = str(Decimal(mg['SB']).quantize(Decimal("0.01"))) 

417 #print "DEUBG: DGT.info: %s" % info 

418 return info 

419 

420 

421 def readHandInfo(self, hand): 

422 info, m2, extra, type3 = {}, None, {}, False 

423 hand.handText = hand.handText.replace(u'\x00', u'') 

424 if self.re_emailedHand.search(hand.handText): 

425 hand.emailedHand = True 

426 else: 

427 hand.emailedHand = False 

428 m = self.re_HandInfo.search(hand.handText,re.DOTALL) 

429 if hand.gametype['type'] == 'ring' or hand.emailedHand: 

430 m2 = self.re_GameInfo.search(hand.handText) 

431 else: 

432 m2 = self.re_GameInfoTrny1.search(hand.handText) 

433 if not m2: 

434 m2 = self.re_GameInfoTrny2.search(hand.handText) 

435 m3 = self.re_TourNoLevel.search(hand.handText) 

436 m4 = self.re_Blinds.search(hand.handText) 

437 if m3 and m4: 

438 extra.update(m3.groupdict()) 

439 extra.update(m4.groupdict()) 

440 else: 

441 m2 = self.re_GameInfoTrny3.search(hand.handText) 

442 type3 = True 

443 if m is None or m2 is None: 

444 tmp = hand.handText[0:200] 

445 log.error(("PartyPokerToFpdb.readHandInfo: '%s'") % tmp) 

446 raise FpdbParseError 

447 info.update(m.groupdict()) 

448 info.update(m2.groupdict()) 

449 info.update(extra) 

450 

451 for key in info: 

452 if key == 'DATETIME': 

453 #Saturday, July 25, 07:53:52 EDT 2009 

454 #Thursday, July 30, 21:40:41 MSKS 2009 

455 #Sunday, October 25, 13:39:07 MSK 2009 

456 #Mon Jul 12 13:38:32 EDT 2010 

457 timezone = "ET" 

458 m2 = re.search( 

459 r"\w+?,?\s*?(?P<M>\w+)\s+(?P<D>\d+),?\s+(?P<H>\d+):(?P<MIN>\d+):(?P<S>\d+)\s+((?P<TZ>[A-Z]+)\s+)?(?P<Y>\d+)", 

460 info[key], 

461 re.UNICODE 

462 ) 

463 if m2.group('TZ'): 

464 timezone = m2.group('TZ') 

465 month = self.months[m2.group('M')] 

466 datetimestr = "%s/%s/%s %s:%s:%s" % (m2.group('Y'), month,m2.group('D'),m2.group('H'),m2.group('MIN'),m2.group('S')) 

467 hand.startTime = datetime.datetime.strptime(datetimestr, "%Y/%m/%d %H:%M:%S") 

468 hand.startTime = HandHistoryConverter.changeTimezone(hand.startTime, timezone, "UTC") 

469 # FIXME: some timezone correction required 

470 #tzShift = defaultdict(lambda:0, {'EDT': -5, 'EST': -6, 'MSKS': 3}) 

471 #hand.starttime -= datetime.timedelta(hours=tzShift[m2.group('TZ')]) 

472 

473 if key == 'HID': 

474 if str(info[key]) == '1111111111': 

475 hand.handid = str(int(time.time()*1000)) 

476 hand.roundPenny = True 

477 else: 

478 if re.search('[a-z]', info[key]): 

479 hand.handid = info[key][:13] 

480 hand.roundPenny = True 

481 else: 

482 hand.handid = info[key] 

483 if key == 'TABLE': 

484 if 'TOURNO' in info and info['TOURNO'] is None: 

485 if info['TABLENO'] is not None: 

486 hand.tablename = info[key] + ' ' + info['TABLENO'] 

487 else: 

488 hand.tablename = info[key] 

489 else: 

490 hand.tablename = info['TABLENO'] 

491 if key == 'BUTTON': 

492 hand.buttonpos = info[key] 

493 if key == 'TOURNO': 

494 hand.tourNo = info[key] 

495 if hand.emailedHand: 

496 hand.buyin = 0 

497 hand.fee = 0 

498 hand.buyinCurrency = "NA" 

499 if key == 'TABLE_ID_WRAPPER': 

500 if info[key] == '#': 

501 # FIXME: there is no such property in Hand class 

502 self.isSNG = True 

503 if key == 'BUYIN': 

504 if info.get('TABLE')!= None and 'Freeroll' in info.get('TABLE'): 

505 # Freeroll tourney 

506 hand.buyin = 0 

507 hand.fee = 0 

508 hand.buyinCurrency = "FREE" 

509 elif info[key] == None: 

510 # Freeroll tourney 

511 hand.buyin = 0 

512 hand.fee = 0 

513 hand.buyinCurrency = "NA" 

514 elif hand.tourNo != None: 

515 hand.buyin = 0 

516 hand.fee = 0 

517 hand.buyinCurrency = "NA" 

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

519 hand.buyinCurrency="USD" 

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

521 hand.buyinCurrency="EUR" 

522 else: 

523 log.error(("PartyPokerToFpdb.readHandInfo: Failed to detect currency Hand ID: '%s' - '%s'") % (hand.handid, info[key])) 

524 raise FpdbParseError 

525 info[key] = self.clearMoneyString(info[key].strip(u'$€')) 

526 hand.buyin = int(100*Decimal(info[key])) 

527 if 'FEE' in info and info['FEE']!=None: 

528 info['FEE'] = self.clearMoneyString(info['FEE'].strip(u'$€')) 

529 hand.fee = int(100*Decimal(info['FEE'])) 

530 if key == 'LEVEL': 

531 hand.level = info[key] 

532 if key == 'PLAY' and info['PLAY'] != 'Real': 

533 # if realy party doesn's save play money hh 

534 hand.gametype['currency'] = 'play' 

535 if key == 'MAX' and info[key] is not None: 

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

537 

538 if type3: 

539 hand.tourNo = info['TABLE'] 

540 hand.buyin = 0 

541 hand.fee = 0 

542 hand.buyinCurrency = "NA" 

543 

544 def readButton(self, hand): 

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

546 if m: 

547 hand.buttonpos = int(m.group('BUTTON')) 

548 else: 

549 log.info('readButton: ' + ('not found')) 

550 

551 def readPlayerStacks(self, hand): 

552 log.debug("readPlayerStacks") 

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

554 maxKnownStack = 0 

555 zeroStackPlayers = [] 

556 self.playerMap = {} 

557 for a in m: 

558 pname = a.group('PNAME') 

559 if hand.emailedHand: 

560 

561 subst = {'PLYR': re.escape(a.group('PNAME')), 'SPACENAME': "\s(.+)? "} 

562 re_PlayerName = re.compile( 

563 r"""^%(PLYR)s(?P<PNAMEEXTRA>%(SPACENAME)s)balance\s""" % subst, 

564 re.MULTILINE|re.VERBOSE) 

565 m1 = re_PlayerName.search(hand.handText) 

566 if m1 and len(m1.group('PNAMEEXTRA'))>1: 

567 pname = a.group('PNAME') + m1.group('PNAMEEXTRA') 

568 pname = pname.strip() 

569 self.playerMap[a.group('PNAME')] = pname 

570 if a.group('CASH') > '0': 

571 #record max known stack for use with players with unknown stack 

572 maxKnownStack = max(a.group('CASH'),maxKnownStack) 

573 hand.addPlayer(int(a.group('SEAT')), pname, self.clearMoneyString(a.group('CASH'))) 

574 else: 

575 #zero stacked players are added later 

576 zeroStackPlayers.append([int(a.group('SEAT')), pname, self.clearMoneyString(a.group('CASH'))]) 

577 if hand.gametype['type'] == 'ring': 

578 #finds first vacant seat after an exact seat 

579 def findFirstEmptySeat(startSeat): 

580 i=0 

581 while startSeat in occupiedSeats: 

582 if (startSeat >= hand.maxseats and hand.maxseats!=None) or len(occupiedSeats)>=hand.maxseats: 

583 startSeat = 0 

584 startSeat += 1 

585 i+=1 

586 if i>10: break 

587 return startSeat 

588 

589 re_HoleCards = re.compile(r"\*{2} Dealing down cards \*{2}") 

590 re_SplitTest = re.compile(r"(joined the table|left the table|is sitting out)") 

591 re_JoiningPlayers = re.compile(r"(?P<PLAYERNAME>.+?) has joined the table") 

592 re_BBPostingPlayers = re.compile(r"(?P<PLAYERNAME>.+?) posts big blind", re.MULTILINE) 

593 re_LeavingPlayers = re.compile(r"(?P<PLAYERNAME>.+?) has left the table") 

594 re_PreDeal = re_HoleCards.split(hand.handText)[0] 

595 

596 match_JoiningPlayers = re_JoiningPlayers.findall(re_PreDeal) 

597 match_LeavingPlayers = re_LeavingPlayers.findall(re_PreDeal) 

598 match_BBPostingPlayers = [] 

599 m = re_BBPostingPlayers.finditer(re_PreDeal) 

600 for player in m: 

601 match_BBPostingPlayers.append(player.group('PLAYERNAME')) 

602 

603 #add every player with zero stack, but: 

604 #if a zero stacked player is just joined the table in this very hand then set his stack to maxKnownStack 

605 for p in zeroStackPlayers: 

606 if p[1] in match_JoiningPlayers: 

607 p[2] = self.clearMoneyString(str(maxKnownStack)) 

608 if not p[1] in match_LeavingPlayers: 

609 hand.addPlayer(p[0],p[1],p[2]) 

610 

611 seatedPlayers = list([(f[1]) for f in hand.players]) 

612 

613 #it works for all known cases as of 2010-09-28 

614 #should be refined with using match_ActivePlayers instead of match_BBPostingPlayers 

615 #as a leaving and rejoining player could be active without posting a BB (sample HH needed) 

616 unseatedActivePlayers = list(set(match_BBPostingPlayers) - set(seatedPlayers)) 

617 

618 if unseatedActivePlayers: 

619 for player in unseatedActivePlayers: 

620 occupiedSeats = list([(f[0]) for f in hand.players]) 

621 occupiedSeats.sort() 

622 #previousBBPoster = match_BBPostingPlayers[match_BBPostingPlayers.index(player)-1] 

623 #previousBBPosterSeat = dict([(f[1], f[0]) for f in hand.players])[previousBBPoster] 

624 #newPlayerSeat = findFirstEmptySeat(previousBBPosterSeat) 

625 # The commented out code above is 'correct' unless the unseated player is the only BB 

626 # I'm willing to live with the unseated player being placed in the lowest seat for now. 

627 newPlayerSeat = findFirstEmptySeat(1) 

628 hand.addPlayer(newPlayerSeat,player,self.clearMoneyString(str(maxKnownStack))) 

629 

630 def markStreets(self, hand): 

631 if hand.gametype['base'] in ("hold"): 

632 m = re.search(r"\*{2} Dealing down cards \*{2}" 

633 r"(?P<PREFLOP>.+?)" 

634 r"(?:\*{2} Dealing Flop \*{2} (:?\s*)?(?P<FLOP>\[ \S\S, \S\S, \S\S \].+?))?" 

635 r"(?:\*{2} Dealing Turn \*{2} (:?\s*)?(?P<TURN>\[ \S\S \].+?))?" 

636 r"(?:\*{2} Dealing River \*{2} (:?\s*)?(?P<RIVER>\[ \S\S \].+?))?$" 

637 , hand.handText,re.DOTALL) 

638 elif hand.gametype['base'] in ("stud"): 

639 m = re.search( 

640 r"(?P<ANTES>.+(?=\*\* Dealing \*\*)|.+)" 

641 r"(\*\* Dealing \*\*(?P<THIRD>.+(?=\*\* Dealing Fourth street \*\*)|.+))?" 

642 r"(\*\* Dealing Fourth street \*\*(?P<FOURTH>.+(?=\*\* Dealing Fifth street \*\*)|.+))?" 

643 r"(\*\* Dealing Fifth street \*\*(?P<FIFTH>.+(?=\*\* Dealing Sixth street \*\*)|.+))?" 

644 r"(\*\* Dealing Sixth street \*\*(?P<SIXTH>.+(?=\*\* Dealing River \*\*)|.+))?" 

645 r"(\*\* Dealing River \*\*(?P<SEVENTH>.+))?" 

646 , hand.handText,re.DOTALL) 

647 

648 hand.addStreets(m) 

649 

650 def readCommunityCards(self, hand, street): 

651 if street in ('FLOP','TURN','RIVER'): 

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

653 hand.setCommunityCards(street, renderCards(m.group('CARDS'))) 

654 

655 def readAntes(self, hand): 

656 log.debug("reading antes") 

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

658 for player in m: 

659 hand.addAnte(player.group('PNAME'), self.clearMoneyString(player.group('ANTE'))) 

660 

661 def readBlinds(self, hand): 

662 noSmallBlind = bool(self.re_NoSmallBlind.search(hand.handText)) 

663 if hand.gametype['type'] == 'ring' or hand.gametype['sb'] is None or hand.gametype['bb'] is None or hand.roundPenny: 

664 try: 

665 assert noSmallBlind==False 

666 for m in self.re_PostSB.finditer(hand.handText): 

667 hand.addBlind(m.group('PNAME'), 'small blind', self.clearMoneyString(m.group('SB'))) 

668 if hand.gametype['sb'] is None: 

669 hand.gametype['sb'] = self.clearMoneyString(m.group('SB')) 

670 except: # no small blind 

671 hand.addBlind(None, None, None) 

672 

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

674 hand.addBlind(a.group('PNAME'), 'big blind', self.clearMoneyString(a.group('BB'))) 

675 if hand.gametype['bb'] is None: 

676 hand.gametype['bb'] = self.clearMoneyString(a.group('BB')) 

677 

678 for a in self.re_PostBUB.finditer(hand.handText): 

679 hand.addBlind(a.group('PNAME'), 'button blind', self.clearMoneyString(a.group('BUB'))) 

680 

681 for a in self.re_PostDead.finditer(hand.handText): 

682 hand.addBlind(a.group('PNAME'), 'both', self.clearMoneyString(a.group('BBNDEAD'))) 

683 else: 

684 # party doesn't track blinds for tournaments 

685 # so there're some cra^Wcaclulations 

686 if hand.buttonpos == 0: 

687 self.readButton(hand) 

688 # NOTE: code below depends on Hand's implementation 

689 # playersMap - dict {seat: (pname,stack)} 

690 playersMap = dict([(f[0], f[1:3]) for f in hand.players if f[1] in hand.handText.split('Trny:')[-1]]) 

691 maxSeat = max(playersMap) 

692 

693 def findFirstNonEmptySeat(startSeat): 

694 while startSeat not in playersMap: 

695 if startSeat >= maxSeat: 

696 startSeat = 0 

697 startSeat += 1 

698 return startSeat 

699 smartMin = lambda A,B: A if float(A) <= float(B) else B 

700 

701 if noSmallBlind: 

702 hand.addBlind(None, None, None) 

703 smallBlindSeat = int(hand.buttonpos) 

704 else: 

705 if len(hand.players)==2: 

706 smallBlindSeat = int(hand.buttonpos) 

707 else: 

708 smallBlindSeat = findFirstNonEmptySeat(int(hand.buttonpos) + 1) 

709 blind = smartMin(hand.sb, playersMap[smallBlindSeat][1]) 

710 hand.addBlind(playersMap[smallBlindSeat][0], 'small blind', blind) 

711 

712 if hand.gametype['category'] == '6_holdem': 

713 bigBlindSeat = findFirstNonEmptySeat(smallBlindSeat + 1) 

714 blind = smartMin(hand.bb, playersMap[bigBlindSeat][1]) 

715 hand.addBlind(playersMap[bigBlindSeat][0], 'button blind', blind) 

716 else: 

717 bigBlindSeat = findFirstNonEmptySeat(smallBlindSeat + 1) 

718 blind = smartMin(hand.bb, playersMap[bigBlindSeat][1]) 

719 hand.addBlind(playersMap[bigBlindSeat][0], 'big blind', blind) 

720 

721 def readBringIn(self, hand): 

722 pass 

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

724 #if m: 

725 # #~ logging.debug("readBringIn: %s for %s" %(m.group('PNAME'), m.group('BRINGIN'))) 

726 # hand.addBringIn(m.group('PNAME'), m.group('BRINGIN')) 

727 

728 def readHoleCards(self, hand): 

729 # we need to grab hero's cards 

730 for street in ('PREFLOP',): 

731 if street in hand.streets.keys(): 

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

733 for found in m: 

734 hand.hero = found.group('PNAME') 

735 newcards = renderCards(found.group('NEWCARDS')) 

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

737 

738 for street, text in hand.streets.iteritems(): 

739 if not text or street in ('PREFLOP', 'DEAL'): continue # already done these 

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

741 for found in m: 

742 player = found.group('PNAME') 

743 if street != 'SEVENTH': 

744 newcards = renderCards(found.group('NEWCARDS')) 

745 oldcards = [] 

746 else: 

747 oldcards = renderCards(found.group('NEWCARDS')) 

748 newcards = [] 

749 

750 if street == 'THIRD' and len(newcards) == 3: # hero in stud game 

751 hand.hero = player 

752 hand.dealt.add(player) # need this for stud?? 

753 hand.addHoleCards(street, player, closed=newcards[0:2], open=[newcards[2]], shown=False, mucked=False, dealt=False) 

754 else: 

755 hand.addHoleCards(street, player, open=newcards, closed=oldcards, shown=False, mucked=False, dealt=False) 

756 

757 def readAction(self, hand, street): 

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

759 for action in m: 

760 acts = action.groupdict() 

761 #print "DEBUG: acts: %s %s" % (street, acts) 

762 playerName = action.group('PNAME') 

763 if ":" in playerName: continue #captures chat 

764 if self.playerMap.get(playerName): 

765 playerName = self.playerMap.get(playerName) 

766 amount = self.clearMoneyString(action.group('BET')) if action.group('BET') else None 

767 actionType = action.group('ATYPE') 

768 

769 if actionType == 'folds': 

770 hand.addFold( street, playerName ) 

771 elif actionType == 'checks': 

772 hand.addCheck( street, playerName ) 

773 elif actionType == 'calls': 

774 hand.addCall( street, playerName, amount ) 

775 elif actionType == 'raises': 

776 if street == 'PREFLOP' and \ 

777 playerName in [item[0] for item in hand.actions['BLINDSANTES'] if item[2]!='ante']: 

778 # preflop raise from blind 

779 hand.addCallandRaise( street, playerName, amount ) 

780 else: 

781 hand.addCallandRaise( street, playerName, amount ) 

782 elif actionType == 'bets' or actionType == 'double bets': 

783 hand.addBet( street, playerName, amount ) 

784 elif actionType == 'completes': 

785 hand.addComplete( street, playerName, amount ) 

786 elif actionType == 'bring-ins': 

787 hand.addBringIn( playerName, amount) 

788 elif actionType == 'is all-In': 

789 if amount: 

790 hand.addAllIn(street, playerName, amount) 

791 else: 

792 log.error((("PartyPokerToFpdb: Unimplemented %s: '%s' '%s'") + " hid:%s") % ("readAction", playerName, actionType, hand.handid)) 

793 raise FpdbParseError 

794 

795 def readShowdownActions(self, hand): 

796 # all action in readShownCards 

797 pass 

798 

799 def readCollectPot(self,hand): 

800 hand.setUncalledBets(True) 

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

802 hand.addCollectPot(player=m.group('PNAME'),pot=self.clearMoneyString(m.group('POT'))) 

803 

804 def readShownCards(self,hand): 

805 

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

807 if m.group('CARDS') is not None: 

808 cards = renderCards(m.group('CARDS')) 

809 

810 mucked = 'SHOWED' in m.groupdict() and m.group('SHOWED') != "show" 

811 

812 hand.addShownCards(cards=cards, player=m.group('PNAME'), shown=True, mucked=mucked, string=m.group('COMBINATION')) 

813 

814 @staticmethod 

815 def getTableTitleRe(type, table_name=None, tournament = None, table_number=None): 

816 "Returns string to search in windows titles" 

817 log.info("Party.getTableTitleRe: table_name='%s' tournament='%s' table_number='%s'" % (table_name, tournament, table_number)) 

818 regex = "%s" % (table_name) 

819 if type=="tour": 

820 if table_name: 

821 TableName = table_name.split(" ") 

822 if len(TableName[1]) > 6: 

823 regex = "#?%s" % (table_number) 

824 else: 

825 regex = "%s.+Table\s#?%s" % (TableName[0], table_number) 

826 else: 

827 # 

828 #sng's seem to get passed in with: 

829 # table_name = None 

830 # tournament=8-digit tourney number 

831 # table_number = 7 digit table number 

832 # screen string is normally Turbo|Speed|(etc) #table_number 

833 # 

834 regex = "%s.*%s" % (tournament, table_number) 

835 log.info("Party.getTableTitleRe: returns: '%s'" % (regex)) 

836 return regex 

837 

838def renderCards(string): 

839 "Splits strings like ' Js, 4d '" 

840 cards = string.strip().split(' ') 

841 return filter(len, map(lambda x: x.strip(' ,'), cards))