Coverage for SealsWithClubsToFpdb.py: 0%

331 statements  

« prev     ^ index     » next       coverage.py v7.6.3, created at 2024-10-15 19:33 +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 

25from HandHistoryConverter import HandHistoryConverter, FpdbParseError, FpdbHandPartial 

26from decimal import Decimal 

27import re 

28import logging 

29import datetime 

30 

31 

32# SealsWithClubs HH Format 

33log = logging.getLogger("parser") 

34 

35 

36class SealsWithClubs(HandHistoryConverter): 

37 # Class Variables 

38 

39 sitename = "SealsWithClubs" 

40 filetype = "text" 

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

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

43 substitutions = { 

44 "PLYR": r"(?P<PNAME>\w+)", 

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

46 } 

47 

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

49 games = { # base, category 

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

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

52 "Omaha Hi-Lo": ("hold", "omahahilo"), 

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

54 "Omaha 5 Cards": ("hold", "5_omahahi"), 

55 } 

56 

57 # Static regexes 

58 re_GameInfo = re.compile( 

59 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>.*)""", 

60 re.VERBOSE, 

61 ) 

62 

63 re_PlayerInfo = re.compile( 

64 r"""^Seat\s+(?P<SEAT>\d+):\s+(?P<PNAME>\w+)\s+\((?P<CASH>\d{1,3}(,\d{3})*(\.\d+)?)\sin\schips\)""", 

65 re.MULTILINE | re.VERBOSE, 

66 ) 

67 

68 re_HandInfo = re.compile( 

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

70 re.MULTILINE, 

71 ) 

72 

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

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

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

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

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

78 re_DateTime = re.compile( 

79 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 

80 ) 

81 

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

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

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

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

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

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

88 re_HeroCards = re.compile( 

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

90 ) 

91 re_Action = re.compile( 

92 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*$""" 

93 % substitutions, 

94 re.MULTILINE | re.VERBOSE, 

95 ) 

96 

97 re_ShowdownAction = re.compile( 

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

99 ) 

100 re_CollectPot = re.compile( 

101 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\))" 

102 % substitutions, 

103 re.MULTILINE, 

104 ) 

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

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

107 re_Flop = re.compile("\*\*\* FLOP \*\*\*") 

108 re_Turn = re.compile("\*\*\* TURN \*\*\*") 

109 re_River = re.compile("\*\*\* RIVER \*\*\*") 

110 re_rake = re.compile( 

111 "Total pot (?P<TOTALPOT>\\d{1,3}(,\\d{3})*(\\.\\d+)?)\\s\\|\\sRake\\s(?P<RAKE>\\d{1,3}(,\\d{3})*(\\.\\d+)?)", 

112 re.MULTILINE, 

113 ) 

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

115 

116 def compilePlayerRegexs(self, hand): 

117 """ 

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

119 

120 Args: 

121 - self: instance of the class containing the method 

122 - hand: a Hand object representing the poker hand 

123 

124 Returns: None 

125 """ 

126 log.info("Compiling player regexes") 

127 # Get a set of player names in the hand 

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

129 

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

131 if not players <= self.compiledPlayers: 

132 # If not, update compiledPlayers 

133 self.compiledPlayers = players 

134 

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

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

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

138 

139 # Define substitutions for the regular expressions 

140 subst = { 

141 "PLYR": player_re, 

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

143 "CUR": "(\$|\xe2\x82\xac|\u20ac||\£|)", 

144 } 

145 

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

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

148 self.re_HeroCards = re.compile( 

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

150 ) 

151 

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

153 # 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>.*))?)?$" 

154 self.re_ShownCards = re.compile( 

155 "^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>.*))?)?$" 

156 % subst, 

157 re.MULTILINE, 

158 ) 

159 

160 def readSupportedGames(self): 

161 log.info("Reading supported games") 

162 return [ 

163 ["ring", "hold", "fl"], 

164 ["ring", "hold", "nl"], 

165 ["ring", "hold", "pl"], 

166 ["ring", "stud", "fl"], 

167 ["ring", "draw", "fl"], 

168 ["ring", "draw", "pl"], 

169 ["ring", "draw", "nl"], 

170 ["tour", "hold", "fl"], 

171 ["tour", "hold", "nl"], 

172 ["tour", "hold", "pl"], 

173 ["tour", "stud", "fl"], 

174 ["tour", "draw", "fl"], 

175 ["tour", "draw", "pl"], 

176 ["tour", "draw", "nl"], 

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

178 ] 

179 

180 def determineGameType(self, handText): 

181 log.info("Determining game type") 

182 info = {} 

183 m = self.re_GameInfo.search(handText) 

184 if not m: 

185 tmp = handText[:200] 

186 log.error(f"SealsWithClubsToFpdb.determineGameType: '{tmp}'") 

187 raise FpdbParseError 

188 

189 mg = m.groupdict() 

190 log.debug(f"Matched groups: {mg}") 

191 if "LIMIT" in mg: 

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

193 if "GAME" in mg: 

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

195 if "SB" in mg: 

196 if "," in mg["SB"]: 

197 mg["SB"] = mg["SB"].replace(",", "") 

198 info["sb"] = mg["SB"] 

199 if "BB" in mg: 

200 if "," in mg["BB"]: 

201 mg["BB"] = mg["BB"].replace(",", "") 

202 info["bb"] = mg["BB"] 

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

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

205 

206 info["type"] = "ring" if "TOURNO" in mg and mg["TOURNO"] is None else "tour" 

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

208 info["currency"] = "mBTC" 

209 else: 

210 info["currency"] = "mBTC" 

211 

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

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

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

215 

216 log.debug(f"Game info: {info}") 

217 return info 

218 

219 def readHandInfo(self, hand): 

220 log.info("Reading hand info") 

221 info = {} 

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

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

224 

225 if m is None or m2 is None: 

226 tmp = hand.handText[:200] 

227 log.error(f"SealsWithClubsToFpdb.readHandInfo: '{tmp}'") 

228 raise FpdbParseError 

229 

230 info.update(m.groupdict()) 

231 log.debug(f"HandInfo groups: {m.groupdict()}") 

232 info.update(m2.groupdict()) 

233 log.debug(f"GameInfo groups: {m2.groupdict()}") 

234 

235 if info["TOURNO"] is not None: 

236 words = m["TABLE"].split() 

237 new_string = words[1] 

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

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

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

241 else: 

242 # for cash game 

243 info["TABLE"] = m["TABLE"] 

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

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

246 

247 for key in info: 

248 if key == "DATETIME": 

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

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

251 for a in m1: 

252 datetimestr = "%s-%s-%s %s:%s:%s" % ( 

253 a.group("Y"), 

254 a.group("M"), 

255 a.group("D"), 

256 a.group("H"), 

257 a.group("MIN"), 

258 a.group("S"), 

259 ) 

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

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

262 if key == "HID": 

263 hand.handid = info[key] 

264 log.debug(f"Hand ID: {hand.handid}") 

265 if key == "TOURNO": 

266 hand.tourNo = info[key] 

267 if key == "BUYIN": 

268 if hand.tourNo is not None: 

269 if info[key] == "Freeroll": 

270 hand.buyin = 0 

271 hand.fee = 0 

272 hand.buyinCurrency = "FREE" 

273 else: 

274 hand.buyinCurrency = "mBTC" 

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

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

277 if key == "LEVEL": 

278 hand.level = info[key] 

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

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

281 if key == "HU" and info[key] is not None: 

282 hand.maxseats = 2 

283 

284 log.debug(f"Final hand info: {info}") 

285 

286 if not hand.handid: 

287 log.error("Hand ID not found, unable to process hand.") 

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

289 

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

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

292 

293 def readButton(self, hand): 

294 log.info("Reading button position") 

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

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

297 else: 

298 log.debug("readButton: not found") 

299 

300 def readPlayerStacks(self, hand): 

301 handsplit = hand.handText.split("*** SUMMARY ***") 

302 if len(handsplit) != 2: 

303 raise FpdbHandPartial(f"Hand is not cleanly split into pre and post Summary {hand.handid}.") 

304 pre, post = handsplit 

305 m = self.re_PlayerInfo.finditer(pre) 

306 plist = {} 

307 

308 for a in m: 

309 if plist.get(a.group("PNAME")) is None: 

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

311 plist[a.group("PNAME")] = [int(a.group("SEAT")), a.group("CASH")] 

312 

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

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

315 

316 def markStreets(self, hand): 

317 log.info("Marking streets") 

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

319 raise FpdbParseError 

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

321 raise FpdbParseError 

322 

323 m = re.search( 

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

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

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

327 r"(\*\*\* RIVER \*\*\*(?P<RIVER>[\s\S]*?(?=\*\*\* SHOW DOWN \*\*\*)|.+))?", 

328 hand.handText, 

329 re.DOTALL, 

330 ) 

331 

332 if not m: 

333 raise FpdbParseError 

334 

335 hand.addStreets(m) 

336 

337 def readCommunityCards(self, hand, street): 

338 log.debug(f"Reading community cards for street: {street}") 

339 if street in ("FLOP", "TURN", "RIVER"): 

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

341 hand.setCommunityCards(street, m.group("CARDS").split(" ")) 

342 

343 def readAntes(self, hand): 

344 log.info("Reading antes") 

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

346 for player in m: 

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

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

349 

350 def readBlinds(self, hand): 

351 log.debug("Reading blinds") 

352 liveBlind = True 

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

354 if liveBlind: 

355 hand.addBlind(a.group("PNAME"), "small blind", a.group("SB")) 

356 liveBlind = False 

357 else: 

358 hand.addBlind(a.group("PNAME"), "secondsb", a.group("SB")) 

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

360 hand.addBlind(a.group("PNAME"), "big blind", a.group("BB")) 

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

362 hand.addBlind(a.group("PNAME"), "both", a.group("SBBB")) 

363 

364 def readHoleCards(self, hand): 

365 log.debug("Reading hole cards") 

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

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

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

369 for found in m: 

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

371 newcards = found.group("NEWCARDS").split(" ") 

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

373 

374 def readAction(self, hand, street): 

375 log.debug(f"Reading actions for street: {street}") 

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

377 for action in m: 

378 acts = action.groupdict() 

379 log.debug(f"Action details: {acts}") 

380 if action.group("ATYPE") == " folds": 

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

382 elif action.group("ATYPE") == " checks": 

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

384 elif action.group("ATYPE") == " calls": 

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

386 elif action.group("ATYPE") == " raises": 

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

388 elif action.group("ATYPE") == " bets": 

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

390 else: 

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

392 

393 def readShownCards(self, hand): 

394 log.info("Reading shown cards") 

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

396 if m.group("CARDS") is not None: 

397 cards = m.group("CARDS").split(" ") 

398 string = m.group("STRING") 

399 if m.group("STRING2"): 

400 string += "|" + m.group("STRING2") 

401 (shown, mucked) = (False, False) 

402 if m.group("SHOWED") == "showed": 

403 shown = True 

404 elif m.group("SHOWED") == "mucked": 

405 mucked = True 

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

407 

408 def readShowdownActions(self, hand): 

409 log.info("Reading showdown actions") 

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

411 if shows.group("CARDS") is not None: 

412 cards = shows.group("CARDS").split(" ") 

413 hand.addShownCards(cards, shows.group("PNAME")) 

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

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

416 cards = mucks.group("CARDS").split(" ") 

417 hand.addShownCards(cards, mucks.group("PNAME")) 

418 

419 def readCollectPot(self, hand): 

420 log.info("Reading collected pot") 

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

422 rake = Decimal(0) 

423 totalpot = Decimal(0) 

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

425 if m.group("POT") is not None: 

426 hand.addCollectPot(player=m.group("PNAME"), pot=m.group("POT")) 

427 elif m.group("POT2") is not None: 

428 hand.addCollectPot(player=m.group("PNAME"), pot=m.group("POT2")) 

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

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

431 rake = rake + Decimal(m.group("RAKE")) 

432 if "," in m.group("TOTALPOT"): 

433 newtotalpot = m.group("TOTALPOT").replace(",", "") 

434 totalpot = totalpot + Decimal(newtotalpot) 

435 else: 

436 totalpot = totalpot + Decimal(m.group("TOTALPOT")) 

437 if hand.rake is None: 

438 hand.rake = rake 

439 elif hand.rakes.get("rake"): 

440 hand.rakes["rake"] += rake 

441 else: 

442 hand.rakes["rake"] = rake 

443 hand.totalpot = totalpot 

444 else: 

445 hand.setUncalledBets(True) 

446 rake = Decimal(0) 

447 totalpot = Decimal(0) 

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

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

450 rake = rake + Decimal(m.group("RAKE")) 

451 if "," in m.group("TOTALPOT"): 

452 newtotalpot = m.group("TOTALPOT").replace(",", "") 

453 totalpot = totalpot + Decimal(newtotalpot) 

454 else: 

455 totalpot = totalpot + Decimal(m.group("TOTALPOT")) 

456 if hand.rake is None: 

457 hand.rake = rake 

458 elif hand.rakes.get("rake"): 

459 hand.rakes["rake"] += rake 

460 else: 

461 hand.rakes["rake"] = rake 

462 hand.totalpot = totalpot 

463 total = rake + totalpot 

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

465 if m.group("POT") is not None: 

466 if totalpot == Decimal(m.group("POT")): 

467 uncalledpot = Decimal(0) 

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

469 if "," in m.group("BET"): 

470 newbet = m.group("BET").replace(",", "") 

471 uncalledpot = uncalledpot + Decimal(newbet) 

472 else: 

473 uncalledpot = uncalledpot + Decimal(m.group("BET")) 

474 collectpot = totalpot 

475 total = total + uncalledpot 

476 hand.totalpot = total 

477 hand.addCollectPot(player=m.group("PNAME"), pot=collectpot) 

478 else: 

479 hand.addCollectPot(player=m.group("PNAME"), pot=m.group("POT")) 

480 elif m.group("POT2") is not None: 

481 if totalpot == Decimal(m.group("POT2")): 

482 uncalledpot = Decimal(0) 

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

484 if "," in m.group("BET"): 

485 newbet = m.group("BET").replace(",", "") 

486 uncalledpot = uncalledpot + Decimal(newbet) 

487 else: 

488 uncalledpot = uncalledpot + Decimal(m.group("BET")) 

489 collectpot = totalpot 

490 total = total + uncalledpot 

491 hand.totalpot = total 

492 hand.addCollectPot(player=m.group("PNAME"), pot=collectpot) 

493 else: 

494 hand.addCollectPot(player=m.group("PNAME"), pot=m.group("POT2")) 

495 

496 @staticmethod 

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

498 log.debug( 

499 f"Seals.getTableTitleRe: table_name='{table_name}' tournament='{tournament}' table_number='{table_number}'" 

500 ) 

501 

502 if not table_name: 

503 log.debug("Seals.getTableTitleRe: no valid input provided") 

504 return "" 

505 

506 log.debug(f"Initial table_name: {table_name}") 

507 

508 regex = f"{table_name}" 

509 words = regex.split() 

510 

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

512 if len(words) > 2: 

513 regex = " ".join(words[1:-1]) 

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

515 return regex 

516 

517 if type == "tour": 

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

519 if match: 

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

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

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

523 return regex 

524 

525 regex = f"{table_name}" 

526 log.debug(f"Seals.getTableTitleRe: regex='{regex}'") 

527 return regex