Coverage for SealsWithClubsToFpdb.py: 0%

332 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 2008-2013, Carl Gherardi 

5#  

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

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

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

9# (at your option) any later version. 

10#  

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

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

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

14# GNU General Public License for more details. 

15#  

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

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

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

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

20 

21 

22#import L10n 

23#_ = L10n.get_translation() 

24 

25import sys 

26import re 

27import datetime 

28import logging 

29from HandHistoryConverter import * 

30from decimal_wrapper import Decimal 

31 

32# Set up debug logging 

33logging.basicConfig(level=logging.DEBUG) 

34 

35# SealsWithClubs HH Format 

36 

37class SealsWithClubs(HandHistoryConverter): 

38 

39 # Class Variables 

40 

41 sitename = "SealsWithClubs" 

42 filetype = "text" 

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

44 siteId = 23 # Needs to match id entry in Sites database 

45 substitutions = { 

46 'PLYR': r'(?P<PNAME>\w+)', 

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

48 } 

49 

50 limits = { "NL":'nl',"No Limit":'nl', 'PL': 'pl', 'Limit':'fl', 'Fixed Limit':'fl', 'Pot Limit':'pl' } 

51 games = { # base, category 

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

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

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

55 "Short Deck Hold'em" : ('hold','6_holdem'), 

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

57 } 

58 

59 # Static regexes 

60 re_GameInfo = re.compile(r"""SwCPoker\sHand\s*\#(?P<HID>\d+):\s((Tournament|Cashgame|sitngo)\s\(((?P<TABLE2>.*?))\)\#(?P<TOURNO>\d+),\s(?P<BUYIN>(?P<BIAMT>\d+(\.\d+)?))\+(?P<BIRAKE>\d+(\.\d+)?)\s|\s)(?P<GAME>(Hold\'em|Omaha|Omaha\s5\sCards|Short\sDeck\sHold\'em))\s(?P<LIMIT>(NL|Fixed\sLimit|PL|Limit|Pot\sLimit|No\sLimit))\s((-\sLevel\s\w+\s)|)\((?P<SB>\d+(\.\d+)?(\,\d+)?)/(?P<BB>\d+(\.\d+)?(\,\d+)?)\)\s-\s(?P<DATETIME>.*)""",re.VERBOSE) 

61 

62 re_PlayerInfo = re.compile(r"""^Seat\s+(?P<SEAT>\d+):\s+(?P<PNAME>\w+)\s+\((?P<CASH>\d{1,3}(,\d{3})*(\.\d+)?)\sin\schips\)""" , re.MULTILINE|re.VERBOSE) 

63 

64 re_HandInfo = re.compile(r"""^Table\s'(?P<TABLE>.*?)'\(\d+\)\s(?P<MAX>\d+)-max\s(?:\(Real Money\)\s)?Seat\s\#\d+\sis\sthe\sbutton""",re.MULTILINE) 

65 

66 re_Identify = re.compile(r"SwCPoker\sHand\s") 

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

68 re_ButtonName = re.compile(r"""^(?P<BUTTONNAME>.*) has the dealer button""",re.MULTILINE) 

69 re_ButtonPos = re.compile(r"""Seat\s+\#(?P<BUTTON>\d+)\sis\sthe\sbutton""",re.MULTILINE) 

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

71 re_DateTime = re.compile(r"""(?P<Y>\d{4})-(?P<M>\d{2})-(?P<D>\d{2})[\-\s]+(?P<H>\d+):(?P<MIN>\d+):(?P<S>\d+)""", re.MULTILINE) 

72 

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

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

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

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

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

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

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

80 re_Action = re.compile(r"""^%(PLYR)s:(?P<ATYPE>\sbets|\schecks|\sraises|\scalls|\sfolds|\sdiscards|\sstands\spat)(?:\s(?P<BET>\d{1,3}(,\d{3})*(\.\d+)?))?(?:\sto\s(?P<POT>\d{1,3}(,\d{3})*(\.\d+)?))?\s*$""" % substitutions, re.MULTILINE|re.VERBOSE) 

81 

82 re_ShowdownAction = re.compile(r"^(?P<PNAME>\w+): (shows \[(?P<CARDS>.*)\]\s\((?P<FHAND>.*?)\)|doesn't show hand|mucks hand)", re.MULTILINE) 

83 re_CollectPot = re.compile(r"^Seat (?P<SEAT>[0-9]+): %(PLYR)s ((%(BRKTS)s(((((?P<SHOWED>showed|mucked) \[(?P<CARDS>.*)\]( and (lost|(won|collected) \((?P<POT>[.\d]+)\)) with (?P<STRING>.+?)(\s\sand\s(won\s\([.\d]+\)|lost)\swith\s(?P<STRING2>.*))?)?$)|collected\s\((?P<POT2>[.\d]+)\)))|folded ((on the (Flop|Turn|River))|before Flop)))|folded before Flop \(didn't bet\))" % substitutions, re.MULTILINE) 

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

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

86 re_Flop = re.compile('\*\*\* FLOP \*\*\*') 

87 re_Turn = re.compile('\*\*\* TURN \*\*\*') 

88 re_River = re.compile('\*\*\* RIVER \*\*\*') 

89 re_rake = re.compile('Total pot (?P<TOTALPOT>\\d{1,3}(,\\d{3})*(\\.\\d+)?)\\s\\|\\sRake\\s(?P<RAKE>\\d{1,3}(,\\d{3})*(\\.\\d+)?)', re.MULTILINE) 

90 re_Mucked = re.compile("^%(PLYR)s: mucks hand" % substitutions, re.MULTILINE) 

91 

92 def compilePlayerRegexs(self, hand): 

93 """ 

94 Compiles regular expressions to match player names and cards shown in a poker hand. 

95 

96 Args: 

97 - self: instance of the class containing the method 

98 - hand: a Hand object representing the poker hand 

99 

100 Returns: None 

101 """ 

102 logging.debug("Compiling player regexes") 

103 # Get a set of player names in the hand 

104 players = {player[1] for player in hand.players} 

105 

106 # Check if the set of players is a subset of compiledPlayers 

107 if not players <= self.compiledPlayers: 

108 # If not, update compiledPlayers 

109 self.compiledPlayers = players 

110 

111 # Compile a regular expression to match the player's name 

112 # The regular expression is of the form "(?P<PNAME>player1|player2|player3)" 

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

114 

115 # Define substitutions for the regular expressions 

116 subst = { 

117 'PLYR': player_re, 

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

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

120 } 

121 

122 # Compile a regular expression to match the cards dealt to the player 

123 # The regular expression is of the form "^Dealt to %(PLYR)s(?: \[(?P<OLDCARDS>.+?)\])?( \[(?P<NEWCARDS>.+?)\])" 

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

125 

126 # Compile a regular expression to match the cards shown by the player 

127 # The regular expression is of the form "^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>.*))?)?$" 

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

129 

130 def readSupportedGames(self): 

131 logging.debug("Reading supported games") 

132 return [ 

133 ["ring", "hold", "fl"], 

134 ["ring", "hold", "nl"], 

135 ["ring", "hold", "pl"], 

136 

137 ["ring", "stud", "fl"], 

138 

139 ["ring", "draw", "fl"], 

140 ["ring", "draw", "pl"], 

141 ["ring", "draw", "nl"], 

142 

143 ["tour", "hold", "fl"], 

144 ["tour", "hold", "nl"], 

145 ["tour", "hold", "pl"], 

146 

147 ["tour", "stud", "fl"], 

148 

149 ["tour", "draw", "fl"], 

150 ["tour", "draw", "pl"], 

151 ["tour", "draw", "nl"], 

152 ["tour", "hold", "6_holdem"], 

153 ] 

154 

155 def determineGameType(self, handText): 

156 logging.debug("Determining game type") 

157 info = {} 

158 m = self.re_GameInfo.search(handText) 

159 if not m: 

160 tmp = handText[:200] 

161 logging.error(f"SealsWithClubsToFpdb.determineGameType: '{tmp}'") 

162 raise FpdbParseError 

163 

164 mg = m.groupdict() 

165 logging.debug(f"Matched groups: {mg}") 

166 if 'LIMIT' in mg: 

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

168 if 'GAME' in mg: 

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

170 if 'SB' in mg: 

171 if ',' in mg['SB']: 

172 mg['SB'] = mg['SB'].replace(',', '') 

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

174 if 'BB' in mg: 

175 if ',' in mg['BB']: 

176 mg['BB'] = mg['BB'].replace(',', '') 

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

178 if 'CURRENCY' in mg and mg['CURRENCY'] is not None: 

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

180 

181 info['type'] = 'ring' if 'TOURNO' in mg and mg['TOURNO'] is None else 'tour' 

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

183 info['currency'] = 'mBTC' 

184 else: 

185 info['currency'] = 'mBTC' 

186 

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

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

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

190 

191 logging.debug(f"Game info: {info}") 

192 return info 

193 

194 def readHandInfo(self, hand): 

195 logging.debug("Reading hand info") 

196 info = {} 

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

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

199 

200 if m is None or m2 is None: 

201 tmp = hand.handText[:200] 

202 logging.error(f"SealsWithClubsToFpdb.readHandInfo: '{tmp}'") 

203 raise FpdbParseError 

204 

205 info.update(m.groupdict()) 

206 logging.debug(f"HandInfo groups: {m.groupdict()}") 

207 info.update(m2.groupdict()) 

208 logging.debug(f"GameInfo groups: {m2.groupdict()}") 

209 

210 if info['TOURNO'] is not None: 

211 words = m['TABLE'].split() 

212 new_string = words[1] 

213 info['TABLE'] = f"{m2['TABLE2']} {new_string}" 

214 logging.debug(f"Table name updated to: {info['TABLE']}") 

215 hand.tablename = f"{info['TABLE']}" 

216 else: 

217 # for cash game 

218 info['TABLE'] = m['TABLE'] 

219 logging.debug(f"Table name for cash game: {info['TABLE']}") 

220 hand.tablename = f"{info['TABLE']}" 

221 

222 for key in info: 

223 if key == 'DATETIME': 

224 m1 = self.re_DateTime.finditer(info[key]) 

225 datetimestr = "2000-01-01 00:00:00" 

226 for a in m1: 

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

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

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

230 if key == 'HID': 

231 hand.handid = info[key] 

232 logging.debug(f"Hand ID: {hand.handid}") 

233 if key == 'TOURNO': 

234 hand.tourNo = info[key] 

235 if key == 'BUYIN': 

236 if hand.tourNo is not None: 

237 if info[key] == 'Freeroll': 

238 hand.buyin = 0 

239 hand.fee = 0 

240 hand.buyinCurrency = "FREE" 

241 else: 

242 hand.buyinCurrency = "mBTC" 

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

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

245 if key == 'LEVEL': 

246 hand.level = info[key] 

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

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

249 if key == 'HU' and info[key] is not None: 

250 hand.maxseats = 2 

251 

252 logging.debug(f"Final hand info: {info}") 

253 

254 if not hand.handid: 

255 logging.error("Hand ID not found, unable to process hand.") 

256 raise FpdbParseError("Hand ID not found.") 

257 

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

259 raise FpdbHandPartial(f"Hand '{hand.handid}' was cancelled.") 

260 

261 

262 def readButton(self, hand): 

263 logging.debug("Reading button position") 

264 if m := self.re_ButtonPos.search(hand.handText): 

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

266 else: 

267 logging.debug('readButton: not found') 

268 

269 def readPlayerStacks(self, hand): 

270 handsplit = hand.handText.split('*** SUMMARY ***') 

271 if len(handsplit) != 2: 

272 raise FpdbHandPartial( 

273 f"Hand is not cleanly split into pre and post Summary {hand.handid}." 

274 ) 

275 pre, post = handsplit 

276 m = self.re_PlayerInfo.finditer(pre) 

277 plist = {} 

278 

279 for a in m: 

280 if plist.get(a.group('PNAME')) is None: 

281 hand.addPlayer(int(a.group('SEAT')), a.group('PNAME'), a.group('CASH')) 

282 plist[a.group('PNAME')] = [int(a.group('SEAT')), a.group('CASH')] 

283 

284 if len(plist.keys()) < 2: 

285 raise FpdbHandPartial(f"Less than 2 players in hand! {hand.handid}.") 

286 

287 def markStreets(self, hand): 

288 logging.debug("Marking streets") 

289 if self.re_Turn.search(hand.handText) and not self.re_Flop.search(hand.handText): 

290 raise FpdbParseError 

291 if self.re_River.search(hand.handText) and not self.re_Turn.search(hand.handText): 

292 raise FpdbParseError 

293 

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

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

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

297 r"(\*\*\* RIVER \*\*\*(?P<RIVER>[\s\S]*?(?=\*\*\* SHOW DOWN \*\*\*)|.+))?", hand.handText,re.DOTALL) 

298 

299 if not m: 

300 raise FpdbParseError 

301 

302 hand.addStreets(m) 

303 

304 def readCommunityCards(self, hand, street): 

305 logging.debug(f"Reading community cards for street: {street}") 

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

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

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

309 

310 def readAntes(self, hand): 

311 logging.debug("Reading antes") 

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

313 for player in m: 

314 logging.debug(f"hand.addAnte({player.group('PNAME')},{player.group('ANTE')})") 

315 hand.addAnte(player.group('PNAME'), player.group('ANTE')) 

316 

317 def readBlinds(self, hand): 

318 logging.debug("Reading blinds") 

319 liveBlind = True 

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

321 if liveBlind: 

322 hand.addBlind(a.group('PNAME'), 'small blind', a.group('SB')) 

323 liveBlind = False 

324 else: 

325 hand.addBlind(a.group('PNAME'), 'secondsb', a.group('SB')) 

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

327 hand.addBlind(a.group('PNAME'), 'big blind', a.group('BB')) 

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

329 hand.addBlind(a.group('PNAME'), 'both', a.group('SBBB')) 

330 

331 def readHoleCards(self, hand): 

332 logging.debug("Reading hole cards") 

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

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

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

336 for found in m: 

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

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

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

340 

341 def readAction(self, hand, street): 

342 logging.debug(f"Reading actions for street: {street}") 

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

344 for action in m: 

345 acts = action.groupdict() 

346 logging.debug(f"Action details: {acts}") 

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

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

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

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

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

352 hand.addCall(street, action.group('PNAME'), action.group('BET')) 

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

354 hand.addRaiseTo(street, action.group('PNAME'), action.group('BET')) 

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

356 hand.addBet(street, action.group('PNAME'), action.group('BET')) 

357 else: 

358 logging.debug(f"DEBUG: Unimplemented {action.group('ATYPE')}: '{action.group('PNAME')}'") 

359 

360 def readShownCards(self, hand): 

361 logging.debug("Reading shown cards") 

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

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

364 cards = m.group('CARDS').split(' ') 

365 string = m.group('STRING') 

366 if m.group('STRING2'): 

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

368 (shown, mucked) = (False, False) 

369 if m.group('SHOWED') == "showed": 

370 shown = True 

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

372 mucked = True 

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

374 

375 def readShowdownActions(self, hand): 

376 logging.debug("Reading showdown actions") 

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

378 if shows.group('CARDS') is not None: 

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

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

381 for mucks in self.re_CollectPot.finditer(hand.handText): 

382 if mucks.group('SHOWED') == "mucked" and mucks.group('CARDS') is not None: 

383 cards = mucks.group('CARDS').split(' ') 

384 hand.addShownCards(cards, mucks.group('PNAME')) 

385 

386 def readCollectPot(self, hand): 

387 logging.debug("Reading collected pot") 

388 if self.re_Uncalled.search(hand.handText) is None: 

389 rake = Decimal(0) 

390 totalpot = Decimal(0) 

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

392 if m.group('POT') is not None: 

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

394 elif m.group('POT2') is not None: 

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

396 if self.re_rake.search(hand.handText) is not None: 

397 for m in self.re_rake.finditer(hand.handText): 

398 rake = rake + Decimal(m.group('RAKE')) 

399 if ',' in m.group('TOTALPOT'): 

400 newtotalpot = m.group('TOTALPOT').replace(',', '') 

401 totalpot = totalpot + Decimal(newtotalpot) 

402 else: 

403 totalpot = totalpot + Decimal(m.group('TOTALPOT')) 

404 if hand.rake is None: 

405 hand.rake = rake 

406 elif hand.rakes.get('rake'): 

407 hand.rakes['rake'] += rake 

408 else: 

409 hand.rakes['rake'] = rake 

410 hand.totalpot = totalpot 

411 else: 

412 hand.setUncalledBets(True) 

413 rake = Decimal(0) 

414 totalpot = Decimal(0) 

415 if self.re_rake.search(hand.handText) is not None: 

416 for m in self.re_rake.finditer(hand.handText): 

417 rake = rake + Decimal(m.group('RAKE')) 

418 if ',' in m.group('TOTALPOT'): 

419 newtotalpot = m.group('TOTALPOT').replace(',', '') 

420 totalpot = totalpot + Decimal(newtotalpot) 

421 else: 

422 totalpot = totalpot + Decimal(m.group('TOTALPOT')) 

423 if hand.rake is None: 

424 hand.rake = rake 

425 elif hand.rakes.get('rake'): 

426 hand.rakes['rake'] += rake 

427 else: 

428 hand.rakes['rake'] = rake 

429 hand.totalpot = totalpot 

430 total = rake + totalpot 

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

432 if m.group('POT') is not None: 

433 if totalpot == Decimal(m.group('POT')): 

434 uncalledpot = Decimal(0) 

435 for m in self.re_Uncalled.finditer(hand.handText): 

436 if ',' in m.group('BET'): 

437 newbet = m.group('BET').replace(',', '') 

438 uncalledpot = uncalledpot + Decimal(newbet) 

439 else: 

440 uncalledpot = uncalledpot + Decimal(m.group('BET')) 

441 collectpot = totalpot 

442 total = total + uncalledpot 

443 hand.totalpot = total 

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

445 else: 

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

447 elif m.group('POT2') is not None: 

448 if totalpot == Decimal(m.group('POT2')): 

449 uncalledpot = Decimal(0) 

450 for m in self.re_Uncalled.finditer(hand.handText): 

451 if ',' in m.group('BET'): 

452 newbet = m.group('BET').replace(',', '') 

453 uncalledpot = uncalledpot + Decimal(newbet) 

454 else: 

455 uncalledpot = uncalledpot + Decimal(m.group('BET')) 

456 collectpot = totalpot 

457 total = total + uncalledpot 

458 hand.totalpot = total 

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

460 else: 

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

462 

463 @staticmethod 

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

465 logging.debug(f"Seals.getTableTitleRe: table_name='{table_name}' tournament='{tournament}' table_number='{table_number}'") 

466 

467 if not table_name: 

468 logging.debug("Seals.getTableTitleRe: no valid input provided") 

469 return "" 

470 

471 logging.debug(f"Initial table_name: {table_name}") 

472 

473 regex = f"{table_name}" 

474 words = regex.split() 

475 

476 if type in ["tour", "ring"]: 

477 if len(words) > 2: 

478 regex = ' '.join(words[1:-1]) 

479 logging.debug(f"Seals.getTableTitleRe: regex after processing='{regex}'") 

480 return regex 

481 

482 if type == "tour": 

483 match = re.match(r"(\d+)\s(.+)\s(\[\d+\sChips\])\s(\d+)", table_name) 

484 if match: 

485 tournament_id, game_type, chips_info, table_number = match.groups() 

486 regex = f"{tournament_id} {game_type} {chips_info} {table_number}" 

487 logging.debug(f"Seals.getTableTitleRe: regex for tour='{regex}'") 

488 return regex 

489 

490 regex = f"{table_name}" 

491 logging.debug(f"Seals.getTableTitleRe: regex='{regex}'") 

492 return regex