Coverage for UnibetToFpdb_old.py: 0%

310 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# Unibet HH Format 

31 

32class Unibet(HandHistoryConverter): 

33 

34 # Class Variables 

35 

36 sitename = "Unibet" 

37 filetype = "text" 

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

39 siteId = 30 # 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' : u"\$|\xe2\x82\xac|\u20ac|\£|\u20b9|\¥|", # legal currency symbols - Euro(cp1252, utf-8) 

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

45 'CUR': u"(\$|\xe2\x82\xac|\u20ac||\£|\u20b9|\¥|)", 

46 'BRKTS': r'(\(button\) |\(small blind\) |\(big blind\) |\(button blind\) |\(button\) \(small blind\) |\(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 'Pot Limit':'pl', 

84 'Fixed Limit':'fl', 

85 'Limit':'fl' 

86 } 

87 games = { # base, category 

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

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

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

91 } 

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

93 

94 # Static regexes 

95 re_GameInfo = re.compile(u""" 

96 Game\s\#(?P<HID>[0-9]+):\s+Table 

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

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

99 # here's how I plan to use LS 

100 (?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)? 

101 (\s.+?,)? 

102 )? 

103 # close paren of tournament info 

104 (?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?\(? 

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

106 (?P<GAME>Hold\'em|HOLD\'EM|Hold\'em|6\+\sHold\'em|Razz|RAZZ|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)?|Courchevel(\sHi/Lo)?)\s 

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

108 (-\s)? 

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

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

111 \(? # open paren of the stakes 

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

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

114 ((?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) 

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

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

117 \) # close paren of the stakes 

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

119 \s-\s 

120 (?P<DATETIME>.*$) 

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

122 

123 re_PlayerInfo = re.compile(u""" 

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

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

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

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

128 \) 

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

130 re.MULTILINE|re.VERBOSE) 

131 

132 re_HandInfo = re.compile(""" 

133 ^\s?Table\s(ID\s)?\'(?P<TABLE>.+?)\'\s 

134 ((?P<MAX>\d+)-max\s)? 

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

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

137 re.MULTILINE|re.VERBOSE) 

138 

139 re_Identify = re.compile(u'^Game\s\#\d+:\sTable') 

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

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

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

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

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

145 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) 

146 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) 

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

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

149 

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

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

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

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

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

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

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

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

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

159 re_Action = re.compile(r""" 

160 ^%(PLYR)s:(?P<ATYPE>\sbets|\schecks|\sraises|\scalls|\sfolds|\sdiscards|\sstands\spat) 

161 (\s%(CUR)s(?P<BET>[,.\d]+))?(\sto\s%(CUR)s(?P<BETTO>[,.\d]+))? # the number discarded goes in <BET> 

162 \s*(and\sis\sall.in)? 

163 (and\shas\sreached\sthe\s\[%(CUR)s\d\.,]+\scap)? 

164 (\son|\scards?)? 

165 (\s\(disconnect\))? 

166 (\s\[(?P<CARDS>.+?)\])?\s*$""" 

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

168 re_ShowdownAction = re.compile(r"^%s: shows \[(?P<CARDS>.*)\]" % substitutions['PLYR'], re.MULTILINE) 

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

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

171 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) 

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

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

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

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

176 re_WinningRankOther = re.compile(u"^%(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) 

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

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

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

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

181 #ChazDazzle wins the 22000 bounty for eliminating berkovich609 

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

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

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

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

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

187 re_Progressive = re.compile(u""" 

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

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

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

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

192 re_Rake = re.compile(u""" 

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

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

195 

196 re_STP = re.compile(u""" 

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

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

199 

200 def compilePlayerRegexs(self, hand): 

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

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

203 self.compiledPlayers = players 

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

205 subst = { 

206 'PLYR': player_re, 

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

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

209 } 

210 if self.siteId == 26: 

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

212 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) 

213 else: 

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

215 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) 

216 

217 def readSupportedGames(self): 

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

219 ["ring", "hold", "pl"], 

220 ["ring", "hold", "fl"], 

221 ["ring", "hold", "pn"], 

222 

223 ["ring", "stud", "fl"], 

224 

225 ["ring", "draw", "fl"], 

226 ["ring", "draw", "pl"], 

227 ["ring", "draw", "nl"], 

228 

229 ["tour", "hold", "nl"], 

230 ["tour", "hold", "pl"], 

231 ["tour", "hold", "fl"], 

232 ["tour", "hold", "pn"], 

233 

234 ["tour", "stud", "fl"], 

235 

236 ["tour", "draw", "fl"], 

237 ["tour", "draw", "pl"], 

238 ["tour", "draw", "nl"], 

239 ] 

240 

241 def determineGameType(self, handText): 

242 info = {} 

243 m = self.re_GameInfo.search(handText) 

244 if not m: 

245 tmp = handText[0:200] 

246 log.error(("UnibetToFpdb.determineGameType: '%s'") % tmp) 

247 raise FpdbParseError 

248 

249 mg = m.groupdict() 

250 if 'LIMIT' in mg: 

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

252 if 'GAME' in mg: 

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

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

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

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

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

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

259 info['sb'] = '0' 

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

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

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

263 elif 'CURRENCY' in mg: 

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

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

266 info['fast'] = True 

267 else: 

268 info['fast'] = False 

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

270 info['homeGame'] = True 

271 else: 

272 info['homeGame'] = False 

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

274 info['buyinType'] = 'cap' 

275 else: 

276 info['buyinType'] = 'regular' 

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

278 info['split'] = True 

279 else: 

280 info['split'] = False 

281 if 'SITE' in mg: 

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

283 self.sitename = "PokerMaster" 

284 self.siteId = 25 

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

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

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

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

289 self.sitename = "Run It Once Poker" 

290 self.siteId = 26 

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

292 self.sitename = 'BetOnline' 

293 self.siteId = 19 

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

295 self.sitename = 'PokerBros' 

296 self.siteId = 29 

297 

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

299 info['type'] = 'ring' 

300 else: 

301 info['type'] = 'tour' 

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

303 info['fast'] = True 

304 

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

306 info['currency'] = 'play' 

307 

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

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

310 try: 

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

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

313 except KeyError: 

314 tmp = handText[0:200] 

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

316 raise FpdbParseError 

317 else: 

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

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

320 

321 return info 

322 

323 def readHandInfo(self, hand): 

324 #First check if partial 

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

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

327 

328 info = {} 

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

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

331 if m is None or m2 is None: 

332 tmp = hand.handText[0:200] 

333 log.error(_("UnibetToFpdb.readHandInfo: '%s'") % tmp) 

334 raise FpdbParseError 

335 

336 info.update(m.groupdict()) 

337 info.update(m2.groupdict()) 

338 

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

340 for key in info: 

341 if key == 'DATETIME': 

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

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

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

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

346 if self.siteId == 26: 

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

348 for a in m2: 

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

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

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

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

353 else: 

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

355 for a in m1: 

356 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')) 

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

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

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

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

361 

362 if key == 'HID': 

363 hand.handid = info[key] 

364 if key == 'TOURNO': 

365 hand.tourNo = info[key] 

366 if key == 'BUYIN': 

367 if hand.tourNo!=None: 

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

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

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

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

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

373 hand.buyin = 0 

374 hand.fee = 0 

375 hand.buyinCurrency = "FREE" 

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

377 hand.buyin = 0 

378 hand.fee = 0 

379 hand.buyinCurrency = "NA" 

380 else: 

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

382 hand.buyinCurrency="USD" 

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

384 hand.buyinCurrency="GBP" 

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

386 hand.buyinCurrency="EUR" 

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

388 hand.buyinCurrency="INR" 

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

390 hand.buyinCurrency="CNY" 

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

392 hand.buyinCurrency="PSFP" 

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

394 hand.buyinCurrency="PSFP" 

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

396 hand.buyinCurrency="play" 

397 else: 

398 #FIXME: handle other currencies, play money 

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

400 raise FpdbParseError 

401 

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

403 

404 if hand.buyinCurrency!="PSFP": 

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

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

407 tmp = info['BOUNTY'] 

408 info['BOUNTY'] = info['BIRAKE'] 

409 info['BIRAKE'] = tmp 

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

411 hand.koBounty = int(100*Decimal(info['BOUNTY'])) 

412 hand.isKO = True 

413 else: 

414 hand.isKO = False 

415 

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

417 

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

419 hand.fee = int(100*Decimal(info['BIRAKE'])) 

420 else: 

421 hand.buyin = int(100*Decimal(info['BIAMT'])) 

422 hand.fee = 0 

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

424 hand.isFast = True 

425 else: 

426 hand.isFast = False 

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

428 hand.isHomeGame = True 

429 else: 

430 hand.isHomeGame = False 

431 if key == 'LEVEL': 

432 hand.level = info[key] 

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

434 hand.isShootout = True 

435 if key == 'TABLE': 

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

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

438 hand.tablename = info['HIVETABLE'] 

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

440 hand.tablename = tablesplit[1] 

441 else: 

442 hand.tablename = info[key] 

443 if key == 'BUTTON': 

444 hand.buttonpos = info[key] 

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

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

447 

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

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

450 

451 def readButton(self, hand): 

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

453 if m: 

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

455 else: 

456 log.info('readButton: ' + _('not found')) 

457 

458 def readPlayerStacks(self, hand): 

459 pre, post = hand.handText.split('*** Summary ***') 

460 m = self.re_PlayerInfo.finditer(pre) 

461 for a in m: 

462 hand.addPlayer( 

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

464 a.group('PNAME'), 

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

466 None, 

467 a.group('SITOUT'), 

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

469 ) 

470 

471 def markStreets(self, hand): 

472 

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

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

475 # in consequence the mucked-display is incorrect. 

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

477 # PREFLOP = ** Dealing down cards ** 

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

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

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

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

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

483 hand.addStreets(m) 

484 

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

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

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

488 

489 def readAntes(self, hand): 

490 log.debug(_("reading antes")) 

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

492 for player in m: 

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

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

495 

496 def readBringIn(self, hand): 

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

498 if m: 

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

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

501 

502 def readBlinds(self, hand): 

503 liveBlind = True 

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

505 if liveBlind: 

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

507 liveBlind = False 

508 else: 

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

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

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

512 else: 

513 # Post dead blinds as ante 

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

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

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

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

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

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

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

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

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

523 

524 def readHoleCards(self, hand): 

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

526# we need to grab hero's cards 

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

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

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

530 for found in m: 

531# if m == None: 

532# hand.involved = False 

533# else: 

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

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

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

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

538 

539 def readAction(self, hand, street): 

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

541 for action in m: 

542 acts = action.groupdict() 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

561 else: 

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

563 

564 

565 def readShowdownActions(self, hand): 

566 pass 

567 

568 def readTourneyResults(self, hand): 

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

570 pass 

571 

572 def readCollectPot(self,hand): 

573 pass 

574 

575 def readShownCards(self,hand): 

576 pass 

577 

578 

579 @staticmethod 

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

581 pass 

582