Coverage for PokerStarsToFpdb.py: 0%

466 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-09-27 18:50 +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#import L10n 

22#_ = L10n.get_translation() 

23 

24# TODO: straighten out discards for draw games 

25 

26import sys 

27from HandHistoryConverter import * 

28from decimal_wrapper import Decimal 

29 

30# PokerStars HH Format 

31 

32class PokerStars(HandHistoryConverter): 

33 

34 # Class Variables 

35 

36 sitename = "PokerStars" 

37 filetype = "text" 

38 codepage = ("utf8", "cp1252", "ISO-8859-1") 

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

40 sym = {'USD': "\$", 'CAD': "\$", 'T$': "", "EUR": "\xe2\x82\xac", "GBP": "\£", "play": "", "INR": "\₹", "CNY": "\¥"} # ADD Euro, Sterling, etc HERE 

41 substitutions = { 

42 'LEGAL_ISO' : "USD|EUR|GBP|CAD|FPP|SC|INR|CNY", # legal ISO currency codes 

43 'LS' : r"\$|\xe2\x82\xac|\u20ac|\£|\u20b9|\¥|Rs\.\s|", # legal currency symbols - Euro(cp1252, utf-8) 

44 'PLYR': r'\s?(?P<PNAME>.+?)', 

45 'CUR': r"(\$|\xe2\x82\xac|\u20ac||\£|\u20b9|\¥|Rs\.\s|)", 

46 'BRKTS': r'(\(button\) |\(small blind\) |\(big blind\) |\(button blind\) |\(button\) \(small blind\) |\(small blind\) \(button\) |\(big blind\) \(button\) |\(small blind/button\) |\(button\) \(big blind\) )?', 

47 } 

48 

49 # translations from captured groups to fpdb info strings 

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

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

52 '0.40': ('0.10', '0.20'), '0.50': ('0.10', '0.25'), 

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

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

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

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

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

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

59 '16.00': ('4.00', '8.00'), '16': ('4.00', '8.00'), 

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

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

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

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

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

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

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

67 '150.00': ('50.00', '75.00'), '150': ('50.00', '75.00'), 

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

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

70 '500.00': ('100.00', '250.00'), '500': ('100.00', '250.00'), 

71 '600.00': ('150.00', '300.00'), '600': ('150.00', '300.00'), 

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

73 '1000.00': ('250.00', '500.00'), '1000': ('250.00', '500.00'), 

74 '2000.00': ('500.00', '1000.00'), '2000': ('500.00', '1000.00'), 

75 '4000.00': ('1000.00', '2000.00'), '4000': ('1000.00', '2000.00'), 

76 '10000.00': ('2500.00', '5000.00'), '10000': ('2500.00', '5000.00'), 

77 '20000.00': ('5000.00', '10000.00'),'20000': ('5000.00', '10000.00'), 

78 '40000.00': ('10000.00', '20000.00'),'40000': ('10000.00', '20000.00'), 

79 } 

80 

81 limits = { 

82 'No Limit':'nl', 

83 'NO LIMIT':'nl', 

84 'Pot Limit':'pl', 

85 'POT LIMIT':'pl', 

86 'Fixed Limit':'fl', 

87 'Limit':'fl', 

88 'LIMIT':'fl' , 

89 'Pot Limit Pre-Flop, No Limit Post-Flop': 'pn' 

90 } 

91 games = { # base, category 

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

93 "HOLD'EM" : ('hold','holdem'), 

94 "6+ Hold'em" : ('hold','6_holdem'), 

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

96 'Fusion': ('hold', 'fusion'), 

97 'OMAHA' : ('hold','omahahi'), 

98 'Omaha Hi/Lo' : ('hold','omahahilo'), 

99 'OMAHA HI/LO' : ('hold','omahahilo'), 

100 '5 Card Omaha' : ('hold', '5_omahahi'), 

101 'Omaha 5 Cards': ('hold', '5_omahahi'), 

102 '5 Card Omaha Hi/Lo' : ('hold', '5_omaha8'), 

103 '6 Card Omaha' : ('hold', '6_omahahi'), 

104 '6 Card Omaha Hi/Lo': ('hold', '6_omaha8'), 

105 'Omaha 6 Cards': ('hold', '6_omahahi'), 

106 'Courchevel' : ('hold', 'cour_hi'), 

107 'Courchevel Hi/Lo' : ('hold', 'cour_hilo'), 

108 'Razz' : ('stud','razz'), 

109 'RAZZ' : ('stud','razz'), 

110 '7 Card Stud' : ('stud','studhi'), 

111 '7 CARD STUD' : ('stud','studhi'), 

112 '7 Card Stud Hi/Lo' : ('stud','studhilo'), 

113 '7 CARD STUD HI/LO' : ('stud','studhilo'), 

114 'Badugi' : ('draw','badugi'), 

115 'Triple Draw 2-7 Lowball' : ('draw','27_3draw'), 

116 'Single Draw 2-7 Lowball' : ('draw','27_1draw'), 

117 '5 Card Draw' : ('draw','fivedraw') 

118 } 

119 mixes = { 

120 'HORSE': 'horse', 

121 '8-Game': '8game', 

122 '8-GAME': '8game', 

123 'HOSE': 'hose', 

124 'Mixed PLH/PLO': 'plh_plo', 

125 'Mixed NLH/PLO': 'nlh_plo', 

126 'Mixed Omaha H/L': 'plo_lo', 

127 'Mixed Hold\'em': 'mholdem', 

128 'Mixed Omaha': 'momaha', 

129 'Triple Stud': '3stud' 

130 } # Legal mixed games 

131 currencies = { u'€':'EUR', '$':'USD', '':'T$', u'£':'GBP', u'¥':'CNY', u'₹':'INR', 'Rs. ':'INR'} 

132 

133 # Static regexes 

134 re_GameInfo = re.compile(u""" 

135 (?P<SITE>PokerStars|POKERSTARS|Hive\sPoker|Full\sTilt|PokerMaster|Run\sIt\sOnce\sPoker|BetOnline|PokerBros|MPLPoker|SupremaPoker)(?P<TITLE>\sGame|\sHand|\sHome\sGame|\sHome\sGame\sHand|Game|\s(Zoom|Rush)\sHand|\sGAME)\s\#(?P<HID>[0-9]+):\s+ 

136 (\{.*\}\s+)?((?P<TOUR>((Zoom|Rush)\s)?(Tournament|TOURNAMENT))\s\# # open paren of tournament info 

137 (?P<TOURNO>\d+),\s(Table\s\#(?P<HIVETABLE>\d+),\s)? 

138 # here's how I plan to use LS 

139 (?P<BUYIN>(?P<BIAMT>[%(LS)s\d\.]+)?\+?(?P<BIRAKE>[%(LS)s\d\.]+)?\+?(?P<BOUNTY>[%(LS)s\d\.]+)?\s?(?P<TOUR_ISO>%(LEGAL_ISO)s)?|Freeroll|)(\s+)?(-\s)? 

140 (\s.+?,)? 

141 )? 

142 # close paren of tournament info 

143 (?P<MIXED>HORSE|8\-Game|8\-GAME|HOSE|Mixed\sOmaha\sH/L|Mixed\sHold\'em|Mixed\sPLH/PLO|Mixed\sNLH/PLO|Mixed\sOmaha|Triple\sStud)?\s?\(? 

144 (?P<SPLIT>Split)?\s? 

145 (?P<GAME>Hold\'em|HOLD\'EM|Hold\'em|6\+\sHold\'em|Razz|RAZZ|Fusion|7\sCard\sStud|7\sCARD\sSTUD|7\sCard\sStud\sHi/Lo|7\sCARD\sSTUD\sHI/LO|Omaha|OMAHA|Omaha\sHi/Lo|OMAHA\sHI/LO|Badugi|Triple\sDraw\s2\-7\sLowball|Single\sDraw\s2\-7\sLowball|5\sCard\sDraw|(5|6)\sCard\sOmaha(\sHi/Lo)?|Omaha\s(5|6)\sCards|Courchevel(\sHi/Lo)?)\s 

146 (?P<LIMIT>No\sLimit|NO\sLIMIT|Fixed\sLimit|Limit|LIMIT|Pot\sLimit|POT\sLIMIT|Pot\sLimit\sPre\-Flop,\sNo\sLimit\sPost\-Flop)\)?,?\s 

147 (-\s)? 

148 (?P<SHOOTOUT>Match.*,\s)? 

149 ((Level|LEVEL)\s(?P<LEVEL>[IVXLC\d]+)\s)? 

150 \(? # open paren of the stakes 

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

152 (ante\s\d+,\s)? 

153 ((?P<SB>[.0-9]+)/(%(LS)s)?(?P<BB>[.0-9]+)|Button\sBlind\s(?P<CURRENCY1>%(LS)s|)(?P<BUB>[.0-9]+)\s\-\sAnte\s(%(LS)s)?[.0-9]+\s) 

154 (?P<CAP>\s-\s[%(LS)s]?(?P<CAPAMT>[.0-9]+)\sCap\s-\s)? # Optional Cap part 

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

156 \) # close paren of the stakes 

157 (?P<BLAH2>\s\[(ADM|AAMS)\sID:\s[A-Z0-9]+\])? # AAMS/ADM ID: in .it HH's 

158 \s-\s 

159 (?P<DATETIME>.*$) 

160 """ % substitutions, re.MULTILINE|re.VERBOSE) 

161 

162 re_PlayerInfo = re.compile(u""" 

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

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

165 \((%(LS)s)?(?P<CASH>[,.0-9]+)\sin\schips 

166 (,\s(%(LS)s)?(?P<BOUNTY>[,.0-9]+)\sbounty)? 

167 \) 

168 (?P<SITOUT>\sis\ssitting\sout)?""" % substitutions, 

169 re.MULTILINE|re.VERBOSE) 

170 

171 re_HandInfo = re.compile(""" 

172 \s?Table\s(ID\s)?\'(?P<TABLE>.+?)\'(\(\d+\))?\s 

173 ((?P<MAX>\d+)-[Mm]ax\s)? 

174 (?P<PLAY>\(Play\sMoney\)\s)? 

175 (\(Real\sMoney\)\s)? 

176 (Seat\s\#(?P<BUTTON>\d+)\sis\sthe\sbutton)?""", 

177 re.MULTILINE|re.VERBOSE) 

178 

179 re_Identify = re.compile(u'(PokerStars|POKERSTARS|Hive\sPoker|Full\sTilt|PokerMaster|Run\sIt\sOnce\sPoker|BetOnline|PokerBros|MPLPoker|SupremaPoker)(\sGame|\sHand|\sHome\sGame|\sHome\sGame\sHand|Game|\s(Zoom|Rush)\sHand|\sGAME)\s\#\d+:') 

180 re_SplitHands = re.compile('(?:\s?\n){2,}') 

181 re_TailSplitHands = re.compile('(\n\n\n+)') 

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

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

184 re_Board2 = re.compile(r"\[(?P<C1>\S\S)\] \[(\S\S)?(?P<C2>\S\S) (?P<C3>\S\S)\]") 

185 re_DateTime1 = 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]+)""", re.MULTILINE) 

186 re_DateTime2 = 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]+)""", re.MULTILINE) 

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

188 #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) 

189 

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

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

192 re_PostSB = re.compile(r"%(PLYR)s: posts small blind %(CUR)s(?P<SB>[,.0-9]+)" % substitutions, re.MULTILINE) 

193 re_PostBB = re.compile(r"%(PLYR)s: posts big blind %(CUR)s(?P<BB>[,.0-9]+)" % substitutions, re.MULTILINE) 

194 re_PostBUB = re.compile(r"%(PLYR)s: posts button blind %(CUR)s(?P<BUB>[,.0-9]+)" % substitutions, re.MULTILINE) 

195 re_Antes = re.compile(r"%(PLYR)s: posts the ante %(CUR)s(?P<ANTE>[,.0-9]+)" % substitutions, re.MULTILINE) 

196 re_BringIn = re.compile(r"%(PLYR)s: brings[- ]in( low|) for %(CUR)s(?P<BRINGIN>[,.0-9]+)" % substitutions, re.MULTILINE) 

197 re_PostBoth = re.compile(r"%(PLYR)s: posts small \& big blinds %(CUR)s(?P<SBBB>[,.0-9]+)" % substitutions, re.MULTILINE) 

198 re_PostStraddle = re.compile(r"%(PLYR)s: posts straddle %(CUR)s(?P<STRADDLE>[,.0-9]+)" % substitutions, re.MULTILINE) 

199 try: 

200 re_Action = re.compile(r"""%(PLYR)s:(?P<ATYPE>\sbets|\schecks|\sraises|\scalls|\sfolds|\sdiscards|\sstands\spat)(\s%(CUR)s(?P<BET>[,.\d]+))?(\sto\s%(CUR)s(?P<BETTO>[,.\d]+))?\s*(and\sis\sall.in)?(and\shas\sreached\sthe\s[%(CUR)s\d\.,]+\scap)?(\son|\scards?)?(\s\(disconnect\))?(\s\[(?P<CARDS>.+?)\])?\s*$""" % substitutions, re.MULTILINE|re.VERBOSE) 

201 except Exception as e: 

202 print(f"Error compiling re_Action: {e}") 

203 

204 re_ShowdownAction = re.compile(r"%s: (shows|mucks|mucked|showed) \[(?P<CARDS>.*)\]" % substitutions['PLYR'], re.MULTILINE) 

205 re_sitsOut = re.compile("%s sits out" % substitutions['PLYR'], re.MULTILINE) 

206 #re_ShownCards = re.compile("^Seat (?P<SEAT>[0-9]+): %(PLYR)s %(BRKTS)s(?P<SHOWED>showed|mucked) \[(?P<CARDS>.*)\]( and (lost|(won|collected) \(%(CUR)s(?P<POT>[.\d]+)\)) with (?P<STRING>.+?)(,\sand\s(won\s\(%(CUR)s[.\d]+\)|lost)\swith\s(?P<STRING2>.*))?)?$" % substitutions, re.MULTILINE) 

207 re_CollectPot = re.compile(r"Seat (?P<SEAT>[0-9]+): %(PLYR)s %(BRKTS)s(collected|showed \[.*\] and (won|collected)) \(?%(CUR)s(?P<POT>[,.\d]+)\)?(, mucked| with.*|)" % substitutions, re.MULTILINE) 

208 #Vinsand88 cashed out the hand for $2.19 | Cash Out Fee $0.02 

209 re_CollectPot2 = re.compile(r"%(PLYR)s (collected|cashed out the hand for) %(CUR)s(?P<POT>[,.\d]+)" % substitutions, re.MULTILINE) 

210 re_CashedOut = re.compile(r"cashed\sout\sthe\shand") 

211 re_WinningRankOne = re.compile(r"%(PLYR)s wins the tournament and receives %(CUR)s(?P<AMT>[,\.0-9]+) - congratulations!$" % substitutions, re.MULTILINE) 

212 re_WinningRankOther = re.compile(r"%(PLYR)s finished the tournament in (?P<RANK>[0-9]+)(st|nd|rd|th) place and received %(CUR)s(?P<AMT>[,.0-9]+)\.$" % substitutions, re.MULTILINE) 

213 re_RankOther = re.compile(r"%(PLYR)s finished the tournament in (?P<RANK>[0-9]+)(st|nd|rd|th) place$" % substitutions, re.MULTILINE) 

214 re_Cancelled = re.compile('Hand\scancelled', re.MULTILINE) 

215 re_Uncalled = re.compile('Uncalled bet \(%(CUR)s(?P<BET>[,.\d]+)\) returned to %(PLYR)s$' % substitutions, re.MULTILINE) 

216 re_EmptyCard = re.compile("\[\]", re.MULTILINE) 

217 #APTEM-89 wins the $0.27 bounty for eliminating Hero 

218 #ChazDazzle wins the 22000 bounty for eliminating berkovich609 

219 #JKuzja, vecenta split the $50 bounty for eliminating ODYSSES 

220 re_Bounty = re.compile(r"%(PLYR)s (?P<SPLIT>split|wins) the %(CUR)s(?P<AMT>[,\.0-9]+) bounty for eliminating (?P<ELIMINATED>.+?)$" % substitutions, re.MULTILINE) 

221 #Amsterdam71 wins $19.90 for eliminating MuKoJla and their own bounty increases by $19.89 to $155.32 

222 #Amsterdam71 wins $4.60 for splitting the elimination of Frimble11 and their own bounty increases by $4.59 to $41.32  

223 #Amsterdam71 wins the tournament and receives $230.36 - congratulations! 

224 re_Progressive = re.compile(r""" 

225 %(PLYR)s\swins\s%(CUR)s(?P<AMT>[,\.0-9]+)\s 

226 for\s(splitting\sthe\selimination\sof|eliminating)\s(?P<ELIMINATED>.+?)\s 

227 and\stheir\sown\sbounty\sincreases\sby\s%(CUR)s(?P<INCREASE>[\.0-9]+)\sto\s%(CUR)s(?P<ENDAMT>[\.0-9]+)$""" 

228 % substitutions, re.MULTILINE|re.VERBOSE) 

229 re_Rake = re.compile(r""" 

230 Total\spot\s%(CUR)s(?P<POT>[,\.0-9]+)(.+?)?\s\|\sRake\s%(CUR)s(?P<RAKE>[,\.0-9]+)""" 

231 % substitutions, re.MULTILINE|re.VERBOSE) 

232 

233 re_STP = re.compile(r""" 

234 STP\sadded:\s%(CUR)s(?P<AMOUNT>[,\.0-9]+)""" 

235 % substitutions, re.MULTILINE|re.VERBOSE) 

236 

237 def compilePlayerRegexs(self, hand): 

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

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

240 self.compiledPlayers = players 

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

242 subst = { 

243 'PLYR': player_re, 

244 'BRKTS': r'(\(button\) |\(small blind\) |\(big blind\) |\(button\) \(small blind\) |\(button\) \(big blind\) )?', 

245 'CUR': u"(\$|\xe2\x82\xac|\u20ac||\£|)" 

246 } 

247 if self.siteId == 26: 

248 self.re_HeroCards = re.compile(r"Dealt\sto\s(?P<PNAME>(?![A-Z][a-z]+\s[A-Z]).+?)(?: \[(?P<OLDCARDS>.+?)\])?( \[(?P<NEWCARDS>.+?)\])" % subst, re.MULTILINE) 

249 self.re_ShownCards = re.compile("Seat (?P<SEAT>[0-9]+): %(PLYR)s %(BRKTS)s(?P<SHOWED>showed|mucked) \[(?P<CARDS>.*)\]( and (lost|(won|collected) %(CUR)s(?P<POT>[,\.\d]+) with (?P<STRING>.+?))(,\sand\s(lost|won\s%(CUR)s[\.\d]+\swith\s(?P<STRING2>.*)))?)?$" % subst, re.MULTILINE) 

250 else: 

251 self.re_HeroCards = re.compile(r"Dealt to %(PLYR)s(?: \[(?P<OLDCARDS>.+?)\])?( \[(?P<NEWCARDS>.+?)\])" % subst, re.MULTILINE) 

252 self.re_ShownCards = re.compile("Seat (?P<SEAT>[0-9]+): %(PLYR)s %(BRKTS)s(?P<SHOWED>showed|mucked) \[(?P<CARDS>.*)\]( and (lost|(won|collected) \(%(CUR)s(?P<POT>[,\.\d]+)\)) with (?P<STRING>.+?)(,\sand\s(won\s\(%(CUR)s[\.\d]+\)|lost)\swith\s(?P<STRING2>.*))?)?$" % subst, re.MULTILINE) 

253 

254 def readSupportedGames(self): 

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

256 ["ring", "hold", "pl"], 

257 ["ring", "hold", "fl"], 

258 ["ring", "hold", "pn"], 

259 

260 ["ring", "stud", "fl"], 

261 

262 ["ring", "draw", "fl"], 

263 ["ring", "draw", "pl"], 

264 ["ring", "draw", "nl"], 

265 

266 ["tour", "hold", "nl"], 

267 ["tour", "hold", "pl"], 

268 ["tour", "hold", "fl"], 

269 ["tour", "hold", "pn"], 

270 

271 ["tour", "stud", "fl"], 

272 

273 ["tour", "draw", "fl"], 

274 ["tour", "draw", "pl"], 

275 ["tour", "draw", "nl"], 

276 ] 

277 

278 def determineGameType(self, handText): 

279 info = {} 

280 m = self.re_GameInfo.search(handText) 

281 if not m: 

282 tmp = handText[0:200] 

283 log.error(("PokerStarsToFpdb.determineGameType: '%s'") % tmp) 

284 raise FpdbParseError 

285 

286 mg = m.groupdict() 

287 if 'LIMIT' in mg: 

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

289 if 'GAME' in mg: 

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

291 if 'SB' in mg and mg['SB'] is not None: 

292 info['sb'] = mg['SB'] 

293 if 'BB' in mg and mg['BB'] is not None: 

294 info['bb'] = mg['BB'] 

295 if 'BUB' in mg and mg['BUB'] is not None: 

296 info['sb'] = '0' 

297 info['bb'] = mg['BUB'] 

298 if 'CURRENCY1' in mg and mg['CURRENCY1'] is not None: 

299 info['currency'] = self.currencies[mg['CURRENCY1']] 

300 elif 'CURRENCY' in mg: 

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

302 if 'MIXED' in mg: 

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

304 if 'Zoom' in mg['TITLE'] or 'Rush' in mg['TITLE']: 

305 info['fast'] = True 

306 else: 

307 info['fast'] = False 

308 if 'Home' in mg['TITLE']: 

309 info['homeGame'] = True 

310 else: 

311 info['homeGame'] = False 

312 if 'CAP' in mg and mg['CAP'] is not None: 

313 info['buyinType'] = 'cap' 

314 else: 

315 info['buyinType'] = 'regular' 

316 if 'SPLIT' in mg and mg['SPLIT'] == 'Split': 

317 info['split'] = True 

318 else: 

319 info['split'] = False 

320 if 'SITE' in mg: 

321 if mg['SITE'] == 'PokerMaster': 

322 self.sitename = "PokerMaster" 

323 self.siteId = 25 

324 m1 = self.re_HandInfo.search(handText,re.DOTALL) 

325 if m1 and '_5Cards_' in m1.group('TABLE'): 

326 info['category'] = '5_omahahi' 

327 elif mg['SITE'] == 'Run It Once Poker': 

328 self.sitename = "Run It Once Poker" 

329 self.siteId = 26 

330 elif mg['SITE'] == 'BetOnline': 

331 self.sitename = 'BetOnline' 

332 self.siteId = 19 

333 elif mg['SITE'] == 'PokerBros': 

334 self.sitename = 'PokerBros' 

335 self.siteId = 29 

336 

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

338 info['type'] = 'ring' 

339 else: 

340 info['type'] = 'tour' 

341 if 'ZOOM' in mg['TOUR']: 

342 info['fast'] = True 

343 

344 if info.get('currency') in ('T$', None) and info['type']=='ring': 

345 info['currency'] = 'play' 

346 

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

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

349 try: 

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

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

352 except KeyError: 

353 tmp = handText[0:200] 

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

355 raise FpdbParseError 

356 else: 

357 info['sb'] = str((float(mg['SB'])/2).quantize(float("0.01"))) 

358 info['bb'] = str(float(mg['SB']).quantize(float("0.01"))) 

359 

360 return info 

361 

362 def readHandInfo(self, hand): 

363 #First check if partial 

364 if hand.handText.count('*** SUMMARY ***')!=1: 

365 raise FpdbHandPartial(("Hand is not cleanly split into pre and post Summary")) 

366 

367 info = {} 

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

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

370 if m is None or m2 is None: 

371 tmp = hand.handText[0:200] 

372 log.error(("PokerStarsToFpdb.readHandInfo: '%s'") % tmp) 

373 raise FpdbParseError 

374 

375 info.update(m.groupdict()) 

376 info.update(m2.groupdict()) 

377 

378 #log.debug("readHandInfo: %s" % info) 

379 for key in info: 

380 if key == 'DATETIME': 

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

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

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

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

385 if self.siteId == 26: 

386 m2 = self.re_DateTime2.finditer(info[key]) 

387 for a in m2: 

388 datetimestr = "%s/%s/%s %s:%s:%s" % (a.group('Y'), a.group('M'),a.group('D'),a.group('H'),a.group('MIN'),'00') 

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

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

391 hand.startTime = datetime.datetime.strptime(datetimestr, "%Y/%m/%d %H:%M:%S") # also timezone at end, e.g. " ET" 

392 else: 

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

394 for a in m1: 

395 datetimestr = "%s/%s/%s %s:%s:%s" % (a.group('Y'), a.group('M'),a.group('D'),a.group('H'),a.group('MIN'),a.group('S')) 

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

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

398 hand.startTime = datetime.datetime.strptime(datetimestr, "%Y/%m/%d %H:%M:%S") # also timezone at end, e.g. " ET" 

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

400 

401 if key == 'HID': 

402 hand.handid = info[key] 

403 if key == 'TOURNO' and info[key]!=None: 

404 hand.tourNo = info[key][-18:] 

405 if key == 'BUYIN': 

406 if hand.tourNo!=None: 

407 #print "DEBUG: info['BUYIN']: %s" % info['BUYIN'] 

408 #print "DEBUG: info['BIAMT']: %s" % info['BIAMT'] 

409 #print "DEBUG: info['BIRAKE']: %s" % info['BIRAKE'] 

410 #print "DEBUG: info['BOUNTY']: %s" % info['BOUNTY'] 

411 if info[key].strip() == 'Freeroll': 

412 hand.buyin = 0 

413 hand.fee = 0 

414 hand.buyinCurrency = "FREE" 

415 elif info[key].strip() == '': 

416 hand.buyin = 0 

417 hand.fee = 0 

418 hand.buyinCurrency = "NA" 

419 else: 

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

421 hand.buyinCurrency="USD" 

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

423 hand.buyinCurrency="GBP" 

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

425 hand.buyinCurrency="EUR" 

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

427 hand.buyinCurrency="INR" 

428 elif info[key].find("Rs. ")!=-1: 

429 hand.buyinCurrency="INR" 

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

431 hand.buyinCurrency="CNY" 

432 elif info[key].find("FPP")!=-1: 

433 hand.buyinCurrency="PSFP" 

434 elif info[key].find("SC")!=-1: 

435 hand.buyinCurrency="PSFP" 

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

437 hand.buyinCurrency="play" 

438 else: 

439 #FIXME: handle other currencies, play money 

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

441 raise FpdbParseError 

442 

443 info['BIAMT'] = info['BIAMT'].strip(u'$€£FPPSC₹') 

444 

445 if hand.buyinCurrency!="PSFP": 

446 if info['BOUNTY'] != None: 

447 # There is a bounty, Which means we need to switch BOUNTY and BIRAKE values 

448 tmp = info['BOUNTY'] 

449 info['BOUNTY'] = info['BIRAKE'] 

450 info['BIRAKE'] = tmp 

451 info['BOUNTY'] = info['BOUNTY'].strip(u'$€£₹') # Strip here where it isn't 'None' 

452 hand.koBounty = int(100*float(info['BOUNTY'])) 

453 hand.isKO = True 

454 else: 

455 hand.isKO = False 

456 

457 info['BIRAKE'] = info['BIRAKE'].strip(u'$€£₹') 

458 

459 hand.buyin = int(100*float(info['BIAMT'])) + hand.koBounty 

460 hand.fee = int(100*float(info['BIRAKE'])) 

461 else: 

462 hand.buyin = int(100*float(info['BIAMT'])) 

463 hand.fee = 0 

464 if 'Zoom' in info['TITLE'] or 'Rush' in info['TITLE']: 

465 hand.isFast = True 

466 else: 

467 hand.isFast = False 

468 if 'Home' in info['TITLE']: 

469 hand.isHomeGame = True 

470 else: 

471 hand.isHomeGame = False 

472 if key == 'LEVEL': 

473 hand.level = info[key] 

474 if key == 'SHOOTOUT' and info[key] != None: 

475 hand.isShootout = True 

476 if key == 'TABLE': 

477 tablesplit = re.split(" ", info[key]) 

478 if info['TOURNO'] is not None and info['HIVETABLE'] is not None: 

479 hand.tablename = info['HIVETABLE'] 

480 elif hand.tourNo != None and len(tablesplit)>1: 

481 hand.tablename = tablesplit[1] 

482 else: 

483 hand.tablename = info[key] 

484 if key == 'BUTTON': 

485 hand.buttonpos = info[key] 

486 if key == 'MAX' and info[key] != None: 

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

488 

489 if 'Zoom' in self.in_path or 'Rush' in self.in_path: 

490 (hand.gametype['fast'], hand.isFast) = (True, True) 

491 

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

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

494 

495 def readButton(self, hand): 

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

497 if m: 

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

499 else: 

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

501 

502 def readPlayerStacks(self, hand): 

503 pre, post = hand.handText.split('*** SUMMARY ***') 

504 m = self.re_PlayerInfo.finditer(pre) 

505 for a in m: 

506 hand.addPlayer( 

507 int(a.group('SEAT')), 

508 a.group('PNAME'), 

509 self.clearMoneyString(a.group('CASH')), 

510 None, 

511 a.group('SITOUT'), 

512 self.clearMoneyString(a.group('BOUNTY')) 

513 ) 

514 

515 def markStreets(self, hand): 

516 

517 # There is no marker between deal and draw in Stars single draw games 

518 # this upsets the accounting, incorrectly sets handsPlayers.cardxx and  

519 # in consequence the mucked-display is incorrect. 

520 # Attempt to fix by inserting a DRAW marker into the hand text attribute 

521 

522 if hand.gametype['category'] in ('27_1draw', 'fivedraw'): 

523 # isolate the first discard/stand pat line (thanks Carl for the regex) 

524 discard_split = re.split(r"(?:(.+(?: stands pat|: discards).+))", hand.handText,re.DOTALL) 

525 if len(hand.handText) == len(discard_split[0]): 

526 # handText was not split, no DRAW street occurred 

527 pass 

528 else: 

529 # DRAW street found, reassemble, with DRAW marker added 

530 discard_split[0] += "*** DRAW ***\r\n" 

531 hand.handText = "" 

532 for i in discard_split: 

533 hand.handText += i 

534 

535 # PREFLOP = ** Dealing down cards ** 

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

537 if hand.gametype['split']: 

538 m = re.search(r"\*\*\* HOLE CARDS \*\*\*(?P<PREFLOP>.+(?=\*\*\* FIRST\sFLOP \*\*\*)|.+)" 

539 r"(\*\*\* FIRST FLOP \*\*\* (?P<FLOP1>\[(\S\S ?)?\S\S \S\S\].+(?=\*\*\* SECOND\sFLOP \*\*\*)|.+))?" 

540 r"(\*\*\* SECOND FLOP \*\*\* (?P<FLOP2>\[(\S\S ?)?\S\S \S\S\].+(?=\*\*\* FIRST\sTURN \*\*\*)|.+))?" 

541 r"(\*\*\* FIRST TURN \*\*\* \[\S\S \S\S \S\S] (?P<TURN1>\[\S\S\].+(?=\*\*\* SECOND TURN \*\*\*)|.+))?" 

542 r"(\*\*\* SECOND TURN \*\*\* \[\S\S \S\S \S\S] (?P<TURN2>\[\S\S\].+(?=\*\*\* FIRST RIVER \*\*\*)|.+))?" 

543 r"(\*\*\* FIRST RIVER \*\*\* \[\S\S \S\S \S\S \S\S] (?P<RIVER1>\[\S\S\].+?(?=\*\*\* SECOND RIVER \*\*\*)|.+))?" 

544 r"(\*\*\* SECOND RIVER \*\*\* \[\S\S \S\S \S\S \S\S] (?P<RIVER2>\[\S\S\].+))?", hand.handText,re.DOTALL) 

545 elif hand.gametype['base'] in ("hold"): 

546 if self.siteId == 19: 

547 m = re.search(r"\*\*\* HOLE CARDS \*\*\*(?P<PREFLOP>(.+(?P<FLOPET>\[\S\S\]))?.+(?=\*\*\* FLOP \*\*\*)|.+)" 

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

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

550 r"(\*\*\* RIVER \*\*\* \[\S\S \S\S \S\S\]? \[?\S\S\] (?P<RIVER>\[\S\S\].+))?", hand.handText,re.DOTALL) 

551 else: 

552 m = re.search(r"\*\*\* HOLE CARDS \*\*\*(?P<PREFLOP>(.+(?P<FLOPET>\[\S\S\]))?.+(?=\*\*\* (FIRST\s)?FLOP \*\*\*)|.+)" 

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

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

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

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

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

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

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

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

561 r"(\*\*\* SECOND RIVER \*\*\* \[\S\S \S\S \S\S \S\S] (?P<RIVER2>\[\S\S\].+))?", hand.handText,re.DOTALL) 

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

563 m = re.search(r"(?P<ANTES>.+(?=\*\*\* 3rd STREET \*\*\*)|.+)" 

564 r"(\*\*\* 3rd STREET \*\*\*(?P<THIRD>.+(?=\*\*\* 4th STREET \*\*\*)|.+))?" 

565 r"(\*\*\* 4th STREET \*\*\*(?P<FOURTH>.+(?=\*\*\* 5th STREET \*\*\*)|.+))?" 

566 r"(\*\*\* 5th STREET \*\*\*(?P<FIFTH>.+(?=\*\*\* 6th STREET \*\*\*)|.+))?" 

567 r"(\*\*\* 6th STREET \*\*\*(?P<SIXTH>.+(?=\*\*\* RIVER \*\*\*)|.+))?" 

568 r"(\*\*\* RIVER \*\*\*(?P<SEVENTH>.+))?", hand.handText,re.DOTALL) 

569 elif hand.gametype['base'] in ("draw"): 

570 if hand.gametype['category'] in ('27_1draw', 'fivedraw'): 

571 m = re.search(r"(?P<PREDEAL>.+(?=\*\*\* DEALING HANDS \*\*\*)|.+)" 

572 r"(\*\*\* DEALING HANDS \*\*\*(?P<DEAL>.+(?=\*\*\* DRAW \*\*\*)|.+))?" 

573 r"(\*\*\* DRAW \*\*\*(?P<DRAWONE>.+))?", hand.handText,re.DOTALL) 

574 else: 

575 m = re.search(r"(?P<PREDEAL>.+(?=\*\*\* DEALING HANDS \*\*\*)|.+)" 

576 r"(\*\*\* DEALING HANDS \*\*\*(?P<DEAL>.+(?=\*\*\* FIRST DRAW \*\*\*)|.+))?" 

577 r"(\*\*\* FIRST DRAW \*\*\*(?P<DRAWONE>.+(?=\*\*\* SECOND DRAW \*\*\*)|.+))?" 

578 r"(\*\*\* SECOND DRAW \*\*\*(?P<DRAWTWO>.+(?=\*\*\* THIRD DRAW \*\*\*)|.+))?" 

579 r"(\*\*\* THIRD DRAW \*\*\*(?P<DRAWTHREE>.+))?", hand.handText,re.DOTALL) 

580 print("type",type(m), m) 

581 mg = m.groupdict() 

582 print("type mg" ,type(mg), mg) 

583 hand.addStreets(m) 

584 

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

586 if self.re_EmptyCard.search(hand.streets[street]): 

587 raise FpdbHandPartial(("Blank community card")) 

588 if street!='FLOPET' or hand.streets.get('FLOP')==None: # a list of streets which get dealt community cards (i.e. all but PREFLOP) 

589 m2 = self.re_Board2.search(hand.streets[street]) 

590 if m2: 

591 hand.setCommunityCards(street, [m2.group('C1'),m2.group('C2'),m2.group('C3')]) 

592 else: 

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

594 hand.setCommunityCards(street, m.group('CARDS').split(' ')) 

595 if street in ('FLOP1', 'TURN1', 'RIVER1', 'FLOP2', 'TURN2', 'RIVER2'): 

596 hand.runItTimes = 2 

597 

598 def readSTP(self, hand): 

599 #log.debug(_("read Splash the Pot")) 

600 m = self.re_STP.search(hand.handText) 

601 if m: 

602 hand.addSTP(m.group('AMOUNT')) 

603 

604 def readAntes(self, hand): 

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

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

607 for player in m: 

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

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

610 

611 def readBringIn(self, hand): 

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

613 if m: 

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

615 hand.addBringIn(m.group('PNAME'), self.clearMoneyString(m.group('BRINGIN'))) 

616 

617 def readBlinds(self, hand): 

618 liveBlind = True 

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

620 if liveBlind: 

621 hand.addBlind(a.group('PNAME'), 'small blind', self.clearMoneyString(a.group('SB'))) 

622 liveBlind = False 

623 else: 

624 names = [p[1] for p in hand.players] 

625 if "Big Blind" in names or "Small Blind" in names or "Dealer" in names: 

626 hand.addBlind(a.group('PNAME'), 'small blind', self.clearMoneyString(a.group('SB'))) 

627 else: 

628 # Post dead blinds as ante 

629 hand.addBlind(a.group('PNAME'), 'secondsb', self.clearMoneyString(a.group('SB'))) 

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

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

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

633 hand.addBlind(a.group('PNAME'), 'both', self.clearMoneyString(a.group('SBBB'))) 

634 for a in self.re_PostStraddle.finditer(hand.handText): 

635 hand.addBlind(a.group('PNAME'), 'straddle', self.clearMoneyString(a.group('STRADDLE'))) 

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

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

638 

639 def readHoleCards(self, hand): 

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

641# we need to grab hero's cards 

642 for street in ('PREFLOP', 'DEAL'): 

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

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

645 for found in m: 

646# if m == None: 

647# hand.involved = False 

648# else: 

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

650 if 'cards' not in found.group('NEWCARDS'): 

651 newcards = found.group('NEWCARDS').split(' ') 

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

653 

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

655 print('text',text) 

656 print(list(hand.streets.items())) 

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

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

659 for found in m: 

660 player = found.group('PNAME') 

661 if found.group('NEWCARDS') is None: 

662 newcards = [] 

663 else: 

664 newcards = found.group('NEWCARDS').split(' ') 

665 if found.group('OLDCARDS') is None: 

666 oldcards = [] 

667 else: 

668 oldcards = found.group('OLDCARDS').split(' ') 

669 

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

671 hand.hero = player 

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

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

674 else: 

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

676 

677 

678 def readAction(self, hand, street): 

679 if hand.gametype['split'] and street in hand.communityStreets: 

680 s = street + '2' 

681 else: 

682 s = street 

683 if not hand.streets[s]: 

684 return 

685 m = self.re_Action.finditer(hand.streets[s]) 

686 for action in m: 

687 acts = action.groupdict() 

688 #log.error("DEBUG: %s acts: %s" % (street, acts)) 

689 if action.group('ATYPE') == ' folds': 

690 hand.addFold( street, action.group('PNAME')) 

691 elif action.group('ATYPE') == ' checks': 

692 hand.addCheck( street, action.group('PNAME')) 

693 elif action.group('ATYPE') == ' calls': 

694 hand.addCall( street, action.group('PNAME'), self.clearMoneyString(action.group('BET')) ) 

695 elif action.group('ATYPE') == ' raises': 

696 if action.group('BETTO') is not None: 

697 hand.addRaiseTo( street, action.group('PNAME'), self.clearMoneyString(action.group('BETTO')) ) 

698 elif action.group('BET') is not None: 

699 hand.addCallandRaise( street, action.group('PNAME'), self.clearMoneyString(action.group('BET')) ) 

700 elif action.group('ATYPE') == ' bets': 

701 hand.addBet( street, action.group('PNAME'), self.clearMoneyString(action.group('BET')) ) 

702 elif action.group('ATYPE') == ' discards': 

703 hand.addDiscard(street, action.group('PNAME'), action.group('BET'), action.group('CARDS')) 

704 elif action.group('ATYPE') == ' stands pat': 

705 hand.addStandsPat( street, action.group('PNAME'), action.group('CARDS')) 

706 else: 

707 log.debug(("DEBUG:") + " " + ("Unimplemented %s: '%s' '%s'") % ("readAction", action.group('PNAME'), action.group('ATYPE'))) 

708 m = self.re_Uncalled.search(hand.streets[s]) 

709 if (m): 

710 hand.addUncalled( street, m.group('PNAME'), m.group('BET')) 

711 

712 

713 def readShowdownActions(self, hand): 

714# TODO: pick up mucks also?? 

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

716 cards = shows.group('CARDS').split(' ') 

717 hand.addShownCards(cards, shows.group('PNAME')) 

718 

719 def readTourneyResults(self, hand): 

720 """Reads knockout bounties and add them to the koCounts dict""" 

721 if self.re_Bounty.search(hand.handText) == None: 

722 koAmounts = {} 

723 winner = None 

724 #%(PLYR)s wins %(CUR)s(?P<AMT>[\.0-9]+) for eliminating (?P<ELIMINATED>.+?) and their own bounty increases by %(CUR)s(?P<INCREASE>[\.0-9]+) to %(CUR)s(?P<ENDAMT>[\.0-9]+) 

725 #re_WinningRankOne = re.compile(u"^%(PLYR)s wins the tournament and receives %(CUR)s(?P<AMT>[\.0-9]+) - congratulations!$" % substitutions, re.MULTILINE) 

726 for a in self.re_Progressive.finditer(hand.handText): 

727 if a.group('PNAME') not in koAmounts: 

728 koAmounts[a.group('PNAME')] = 0 

729 koAmounts[a.group('PNAME')] += 100*float(a.group('AMT')) 

730 hand.endBounty[a.group('PNAME')] = 100*float(a.group('ENDAMT')) 

731 hand.isProgressive = True 

732 

733 m = self.re_WinningRankOne.search(hand.handText) 

734 if m: winner = m.group('PNAME') 

735 

736 if hand.koBounty > 0: 

737 for pname, amount in list(koAmounts.items()): 

738 if pname == winner: 

739 end = (amount + hand.endBounty[pname]) 

740 hand.koCounts[pname] = (amount + hand.endBounty[pname]) / float(hand.koBounty) 

741 else: 

742 end = 0 

743 hand.koCounts[pname] = amount / float(hand.koBounty) 

744 else: 

745 for a in self.re_Bounty.finditer(hand.handText): 

746 if a.group('SPLIT') == 'split': 

747 pnames = a.group('PNAME').split(', ') 

748 for pname in pnames: 

749 if pname not in hand.koCounts: 

750 hand.koCounts[pname] = 0 

751 hand.koCounts[pname] += (1 / float(len(pnames))) 

752 else: 

753 if a.group('PNAME') not in hand.koCounts: 

754 hand.koCounts[a.group('PNAME')] = 0 

755 hand.koCounts[a.group('PNAME')] += 1 

756 

757 def readCollectPot(self,hand): 

758 #Bovada walks are calculated incorrectly in converted PokerStars hands 

759 acts, bovadaUncalled_v1, bovadaUncalled_v2, blindsantes, adjustment = hand.actions.get('PREFLOP'), False, False, 0, 0 

760 names = [p[1] for p in hand.players] 

761 if "Big Blind" in names or "Small Blind" in names or "Dealer" in names or self.siteId == 26: 

762 if acts != None and len([a for a in acts if a[1] != 'folds']) == 0: 

763 m0 = self.re_Uncalled.search(hand.handText) 

764 if m0 and float(m0.group('BET')) == float(hand.bb): 

765 bovadaUncalled_v2 = True 

766 elif m0 == None: 

767 bovadaUncalled_v1 = True 

768 has_sb = len([a[2] for a in hand.actions.get('BLINDSANTES') if a[1] == 'small blind']) > 0 

769 adjustment = (float(hand.bb) - float(hand.sb)) if has_sb else float(hand.bb) 

770 blindsantes = sum([a[2] for a in hand.actions.get('BLINDSANTES')]) 

771 i=0 

772 pre, post = hand.handText.split('*** SUMMARY ***') 

773 hand.cashedOut = self.re_CashedOut.search(pre) != None 

774 if hand.runItTimes==0 and hand.cashedOut == False: 

775 for m in self.re_CollectPot.finditer(post): 

776 pot = self.clearMoneyString(m.group('POT')) 

777 if bovadaUncalled_v1 and float(pot) == (blindsantes + hand.pot.stp): 

778 hand.addCollectPot(player=m.group('PNAME'),pot=str(float(pot) - adjustment)) 

779 elif bovadaUncalled_v2: 

780 hand.addCollectPot(player=m.group('PNAME'),pot=str(float(pot)*2)) 

781 else: 

782 hand.addCollectPot(player=m.group('PNAME'),pot=pot) 

783 i+=1 

784 if i==0: 

785 for m in self.re_CollectPot2.finditer(pre): 

786 pot = self.clearMoneyString(m.group('POT')) 

787 if bovadaUncalled_v1 and float(pot) == (blindsantes + hand.pot.stp): 

788 hand.addCollectPot(player=m.group('PNAME'),pot=str(float(pot) - adjustment)) 

789 elif bovadaUncalled_v2: 

790 hand.addCollectPot(player=m.group('PNAME'),pot=str(float(pot)*2)) 

791 else: 

792 hand.addCollectPot(player=m.group('PNAME'),pot=pot) 

793 

794 def readShownCards(self,hand): 

795 if self.siteId == 26: 

796 re_RevealedCards = re.compile(r"Dealt to %(PLYR)s(?: \[(?P<OLDCARDS>.+?)\])?( \[(?P<NEWCARDS>.+?)\])" % self.substitutions, re.MULTILINE) 

797 m = re_RevealedCards.finditer(hand.handText) 

798 for found in m: 

799 cards = found.group('NEWCARDS').split(' ') 

800 hand.addShownCards(cards=cards, player=found.group('PNAME'), shown=True, mucked=False) 

801 

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

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

804 cards = m.group('CARDS') 

805 cards = cards.split(' ') # needs to be a list, not a set--stud needs the order 

806 string = m.group('STRING') 

807 if m.group('STRING2'): 

808 string += '|' + m.group('STRING2') 

809 

810 (shown, mucked) = (False, False) 

811 if m.group('SHOWED') == "showed": shown = True 

812 elif m.group('SHOWED') == "mucked": mucked = True 

813 

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

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

816 

817 

818 @staticmethod 

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

820 "Returns string to search in windows titles" 

821 regex = re.escape(str(table_name)) 

822 print("regex cash ", regex) 

823 if type=="tour": 

824 regex = re.escape(str(tournament)) + " (Table|Tisch) " + re.escape(str(table_number)) 

825 print("regex tour: ", regex) 

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

827 log.info("Stars.getTableTitleRe: returns: '%s'" % (regex)) 

828 print('regex:') 

829 print(regex) 

830 return regex 

831