Coverage for PartyPokerToFpdb.py: 0%
486 statements
« prev ^ index » next coverage.py v7.6.4, created at 2024-11-07 02:19 +0000
« prev ^ index » next coverage.py v7.6.4, created at 2024-11-07 02:19 +0000
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#
4# Copyright 2009-2011, Grigorij Indigirkin
5#
6# This program is free software; you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation; either version 2 of the License, or
9# (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program; if not, write to the Free Software
18# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19########################################################################
21# import L10n
22# _ = L10n.get_translation()
25from HandHistoryConverter import HandHistoryConverter, FpdbParseError, FpdbHandPartial
26from decimal import Decimal
27import re
28import logging
29import datetime
30import time
32# PartyPoker HH Format
33log = logging.getLogger("parser")
36class PartyPoker(HandHistoryConverter):
37 sitename = "PartyPoker"
38 codepage = ("utf8", "cp1252")
39 siteId = 9
40 filetype = "text"
41 sym = {"USD": "\$", "EUR": "\u20ac", "T$": "", "play": "play"}
42 currencies = {"\$": "USD", "$": "USD", "\xe2\x82\xac": "EUR", "\u20ac": "EUR", "": "T$", "play": "play"}
43 substitutions = {
44 "LEGAL_ISO": "USD|EUR", # legal ISO currency codes
45 "LS": "\$|\u20ac|\xe2\x82\xac|", # Currency symbols - Euro(cp1252, utf-8)
46 "NUM": ".,'\dKMB",
47 }
48 limits = {"NL": "nl", "PL": "pl", "": "fl", "FL": "fl", "Limit": "fl"}
49 games = { # base, category
50 "Texas Hold'em": ("hold", "holdem"),
51 "Texas Holdem": ("hold", "holdem"),
52 "Hold'em": ("hold", "holdem"),
53 "Holdem": ("hold", "holdem"),
54 "Omaha": ("hold", "omahahi"),
55 "Omaha Hi": ("hold", "omahahi"),
56 "Omaha Hi-Lo": ("hold", "omahahilo"),
57 "7 Card Stud Hi-Lo": ("stud", "studhilo"),
58 "7 Card Stud": ("stud", "studhi"),
59 "Double Hold'em": ("hold", "2_holdem"),
60 "Double Holdem": ("hold", "2_holdem"),
61 "Short Deck": ("hold", "6_holdem"),
62 }
64 Lim_Blinds = {
65 "0.04": ("0.01", "0.02"),
66 "0.08": ("0.02", "0.04"),
67 "0.10": ("0.02", "0.05"),
68 "0.20": ("0.05", "0.10"),
69 "0.30": ("0.07", "0.15"),
70 "0.50": ("0.10", "0.25"),
71 "1.00": ("0.25", "0.50"),
72 "1": ("0.25", "0.50"),
73 "2.00": ("0.50", "1.00"),
74 "2": ("0.50", "1.00"),
75 "4.00": ("1.00", "2.00"),
76 "4": ("1.00", "2.00"),
77 "6.00": ("1.00", "3.00"),
78 "6": ("1.00", "3.00"),
79 "8.00": ("2.00", "4.00"),
80 "8": ("2.00", "4.00"),
81 "10.00": ("2.00", "5.00"),
82 "10": ("2.00", "5.00"),
83 "12.00": ("3.00", "6.00"),
84 "12": ("3.00", "6.00"),
85 "20.00": ("5.00", "10.00"),
86 "20": ("5.00", "10.00"),
87 "30.00": ("10.00", "15.00"),
88 "30": ("10.00", "15.00"),
89 "40.00": ("10.00", "20.00"),
90 "40": ("10.00", "20.00"),
91 "50.00": ("10.00", "25.00"),
92 "50": ("10.00", "25.00"),
93 "60.00": ("15.00", "30.00"),
94 "60": ("15.00", "30.00"),
95 "80.00": ("20.00", "40.00"),
96 "80": ("20.00", "40.00"),
97 "100.00": ("25.00", "50.00"),
98 "100": ("25.00", "50.00"),
99 "200.00": ("50.00", "100.00"),
100 "200": ("50.00", "100.00"),
101 "400.00": ("100.00", "200.00"),
102 "400": ("100.00", "200.00"),
103 "500.00": ("125.00", "250.00"),
104 "500": ("125.00", "250.00"),
105 "800.00": ("200.00", "400.00"),
106 "800": ("200.00", "400.00"),
107 }
108 NLim_Blinds_20bb = {
109 "0.80": ("0.01", "0.02"),
110 "1.60": ("0.02", "0.04"),
111 "4": ("0.05", "0.10"),
112 "10": ("0.10", "0.25"),
113 "20": ("0.25", "0.50"),
114 "40": ("0.50", "1.00"),
115 "240": ("3.00", "6.00"),
116 #'10': ('0.10', '0.25'),
117 #'10': ('0.10', '0.25'),
118 #'10': ('0.10', '0.25'),
119 #'10': ('0.10', '0.25'),
120 }
122 months = {
123 "January": 1,
124 "Jan": 1,
125 "February": 2,
126 "Feb": 2,
127 "March": 3,
128 "Mar": 3,
129 "April": 4,
130 "Apr": 4,
131 "May": 5,
132 "June": 6,
133 "Jun": 6,
134 "July": 7,
135 "Jul": 7,
136 "August": 8,
137 "Aug": 8,
138 "September": 9,
139 "Sep": 9,
140 "October": 10,
141 "Oct": 10,
142 "November": 11,
143 "Nov": 11,
144 "December": 12,
145 "Dec": 12,
146 }
148 sites = {
149 "Poker Stars": ("PokerStars", 2),
150 "PokerMaster": ("PokerMaster", 25),
151 "IPoker": ("iPoker", 14),
152 "Party": ("PartyPoker", 9),
153 #'PMU Poker' : ('PMU Poker', 31),
154 "Pacific": ("PacificPoker", 10),
155 "WPN": ("WinningPoker", 24),
156 "PokerBros": ("PokerBros", 29),
157 }
159 # Static regexes
160 re_GameInfo = re.compile(
161 """
162 \*{5}\sHand\sHistory\s(F|f)or\sGame\s(?P<HID>\w+)\s\*{5}(\s\((?P<SITE>Poker\sStars|PokerMaster|Party|PartyPoker|IPoker|Pacific|WPN|PokerBros)\))?\s+
163 (.+?\shas\sleft\sthe\stable\.\s+)*
164 (.+?\sfinished\sin\s\d+\splace\.\s+)*
165 ((?P<CURRENCY>[%(LS)s]))?\s*
166 (
167 ([%(LS)s]?(?P<SB>[%(NUM)s]+)/[%(LS)s]?(?P<BB>[%(NUM)s]+)\s*(?:%(LEGAL_ISO)s)?\s+(?P<FAST3>fastforward\s)?((?P<LIMIT3>NL|PL|FL|)\s+)?)|
168 ((?P<CASHBI>[%(NUM)s]+)\s*(?:%(LEGAL_ISO)s)?\s*)(?P<FAST2>fastforward\s)?(?P<LIMIT2>(NL|PL|FL|))?\s*
169 )
170 (Tourney\s*)?
171 (?P<GAME>(Texas\sHold\'?em|Hold\'?em|Omaha\sHi-Lo|Omaha(\sHi)?|7\sCard\sStud\sHi-Lo|7\sCard\sStud|Double\sHold\'?em|Short\sDeck))\s*
172 (Game\sTable\s*)?
173 (
174 (\((?P<LIMIT>(NL|PL|FL|Limit|))\)\s*)?
175 (\((?P<SNG>SNG|STT|MTT)(\sJackPot)?\sTournament\s\#(?P<TOURNO>\d+)\)\s*)?
176 )?
177 (?:\s\(Buyin\s(?P<BUYIN>[%(LS)s][%(NUM)s]+)\s\+\s(?P<FEE>[%(LS)s][%(NUM)s]+)\))?
178 \s*-\s*
179 (?P<DATETIME>.+)
180 """
181 % substitutions,
182 re.VERBOSE | re.UNICODE,
183 )
185 re_HandInfo = re.compile(
186 """
187 Table\s(?P<TABLE>.+?)?\s+
188 ((?: \#|\(|)(?P<TABLENO>\d+)\)?\s+)?
189 (\(No\sDP\)\s)?
190 \(\s?(?P<PLAY>Real|Play)\s+Money\s?\)\s+(--\s*)? # FIXME: check if play money is correct
191 Seat\s+(?P<BUTTON>\d+)\sis\sthe\sbutton
192 (\s+Total\s+number\s+of\s+players\s+\:\s+(?P<PLYRS>\d+)/?(?P<MAX>\d+)?)?
193 """,
194 re.VERBOSE | re.MULTILINE | re.DOTALL,
195 )
197 re_GameInfoTrny1 = re.compile(
198 """
199 \*{5}\sHand\sHistory\s(F|f)or\sGame\s(?P<HID>\w+)\s\*{5}\s+
200 (?P<LIMIT>(NL|PL|FL|))\s*
201 (?P<GAME>(Texas\sHold\'em|Hold\'?em|Omaha\sHi-Lo|Omaha(\sHi)?|7\sCard\sStud\sHi-Lo|7\sCard\sStud|Double\sHold\'em|Short\sDeck))\s+
202 (?:(?P<BUYIN>[%(LS)s]?\s?[%(NUM)s]+)\s*(?P<BUYIN_CURRENCY>%(LEGAL_ISO)s)?\s*Buy-in\s+)?
203 (\+\s(?P<FEE>[%(LS)s]?\s?[%(NUM)s]+)\sEntry\sFee\s+)?
204 Trny:\s?(?P<TOURNO>\d+)\s+
205 Level:\s*(?P<LEVEL>\d+)\s+
206 ((Blinds|Stakes)(?:-Antes)?)\(
207 (?P<SB>[%(NUM)s ]+)\s*
208 /(?P<BB>[%(NUM)s ]+)
209 (?:\s*-\s*(?P<ANTE>[%(NUM)s ]+)\$?)?
210 \)
211 \s*\-\s*
212 (?P<DATETIME>.+)
213 """
214 % substitutions,
215 re.VERBOSE | re.UNICODE,
216 )
218 re_GameInfoTrny2 = re.compile(
219 """
220 \*{5}\sHand\sHistory\s(F|f)or\sGame\s(?P<HID>\w+)\s\*{5}\s+
221 (?P<LIMIT>(NL|PL|FL|))\s*
222 (?P<GAME>(Texas\sHold\'em|Hold\'?em|Omaha\sHi-Lo|Omaha(\sHi)?|7\sCard\sStud\sHi-Lo|7\sCard\sStud|Double\sHold\'em|Short\sDeck))\s+
223 (?:(?P<BUYIN>[%(LS)s]?\s?[%(NUM)s]+)\s*(?P<BUYIN_CURRENCY>%(LEGAL_ISO)s)?\s*Buy-in\s+)?
224 (\+\s(?P<FEE>[%(LS)s]?\s?[%(NUM)s]+)\sEntry\sFee\s+)?
225 \s*\-\s*
226 (?P<DATETIME>.+)
227 """
228 % substitutions,
229 re.VERBOSE | re.UNICODE,
230 )
232 re_GameInfoTrny3 = re.compile(
233 """
234 \*{5}\sHand\sHistory\s(F|f)or\sGame\s(?P<HID>\w+)\s\*{5}\s\((?P<SITE>Poker\sStars|PokerMaster|Party|IPoker|Pacific|WPN|PokerBros)\)\s+
235 Tourney\sHand\s
236 (?P<LIMIT>(NL|PL|FL|))\s*
237 (?P<GAME>(Texas\sHold\'em|Hold\'?em|Omaha\sHi-Lo|Omaha(\sHi)?|7\sCard\sStud\sHi-Lo|7\sCard\sStud|Double\sHold\'em|Short\sDeck))\s+
238 \s*\-\s*
239 (?P<DATETIME>.+)
240 """
241 % substitutions,
242 re.VERBOSE | re.UNICODE,
243 )
245 re_Blinds = re.compile(
246 """
247 ^((Blinds|Stakes)(?:-Antes)?)\(
248 (?P<SB>[%(NUM)s ]+)\s*
249 /(?P<BB>[%(NUM)s ]+)
250 (?:\s*-\s*(?P<ANTE>[%(NUM)s ]+)\$?)?
251 \)$"""
252 % substitutions,
253 re.VERBOSE | re.MULTILINE,
254 )
256 re_TourNoLevel = re.compile(
257 """
258 Trny:\s?(?P<TOURNO>\d+)\s+
259 Level:\s*(?P<LEVEL>\d+)
260 """,
261 re.VERBOSE,
262 )
264 re_PlayerInfo = re.compile(
265 """
266 (S|s)eat\s?(?P<SEAT>\d+):\s
267 (?P<PNAME>.*)\s
268 \(\s*[%(LS)s]?(?P<CASH>[%(NUM)s]+)\s*(?:%(LEGAL_ISO)s|)\s*\)
269 """
270 % substitutions,
271 re.VERBOSE | re.UNICODE,
272 )
274 re_NewLevel = re.compile(
275 "Blinds(-Antes)?\((?P<SB>[%(NUM)s ]+)/(?P<BB>[%(NUM)s ]+)(?:\s*-\s*(?P<ANTE>[%(NUM)s ]+))?\)" % substitutions,
276 re.VERBOSE | re.MULTILINE | re.DOTALL,
277 )
278 re_CountedSeats = re.compile("Total\s+number\s+of\s+players\s*:\s*(?P<COUNTED_SEATS>\d+)", re.MULTILINE)
279 re_Identify = re.compile("\*{5}\sHand\sHistory\s[fF]or\sGame\s\d+\w+?\s")
280 re_SplitHands = re.compile("\n\n+")
281 re_TailSplitHands = re.compile("(\x00+)")
282 lineSplitter = "\n"
283 re_Button = re.compile("Seat (?P<BUTTON>\d+) is the button", re.MULTILINE)
284 re_Board = re.compile(r"\[(?P<CARDS>.+)\]")
285 re_NoSmallBlind = re.compile(
286 "^There is no Small Blind in this hand as the Big Blind " "of the previous hand left the table", re.MULTILINE
287 )
288 re_20BBmin = re.compile(r"Table 20BB Min")
289 re_Cancelled = re.compile("Table\sClosed\s?", re.MULTILINE)
290 re_Disconnected = re.compile("Connection\sLost\sdue\sto\ssome\sreason\s?", re.MULTILINE)
291 re_GameStartLine = re.compile("Game\s\#\d+\sstarts", re.MULTILINE)
292 re_emailedHand = re.compile(r"\*\*\sSummary\s\*\*")
294 def allHandsAsList(self):
295 list = HandHistoryConverter.allHandsAsList(self)
296 if list is None:
297 return []
298 return filter(lambda text: len(text.strip()), list)
300 def compilePlayerRegexs(self, hand):
301 players = set([player[1] for player in hand.players])
302 if not players <= self.compiledPlayers: # x <= y means 'x is subset of y'
303 self.compiledPlayers = players
304 player_re = "(?P<PNAME>" + "|".join(map(re.escape, players)) + ")"
305 subst = {
306 "PLYR": player_re,
307 "CUR_SYM": self.sym[hand.gametype["currency"]],
308 "CUR": hand.gametype["currency"] if hand.gametype["currency"] != "T$" else "(chips|)",
309 "BRAX": "\[\(\)\]",
310 }
311 self.re_PostSB = re.compile(
312 r"%(PLYR)s posts small blind [%(BRAX)s]?%(CUR_SYM)s?(?P<SB>[.,0-9]+)\s*(%(CUR)s)?[%(BRAX)s]?\.?\s*$"
313 % subst,
314 re.MULTILINE,
315 )
316 self.re_PostBB = re.compile(
317 r"%(PLYR)s posts big blind [%(BRAX)s]?%(CUR_SYM)s?(?P<BB>[.,0-9]+)\s*(%(CUR)s)?[%(BRAX)s]?\.?\s*$"
318 % subst,
319 re.MULTILINE,
320 )
321 self.re_PostDead = re.compile(
322 r"%(PLYR)s posts big blind \+ dead [%(BRAX)s]?%(CUR_SYM)s?(?P<BBNDEAD>[.,0-9]+)\s*%(CUR_SYM)s?[%(BRAX)s]?\.?\s*$"
323 % subst,
324 re.MULTILINE,
325 )
326 self.re_PostBUB = re.compile(
327 r"%(PLYR)s posts button blind ?[%(BRAX)s]?%(CUR_SYM)s?(?P<BUB>[.,0-9]+)\s*%(CUR)s?[%(BRAX)s]?\.?\s*$"
328 % subst,
329 re.MULTILINE,
330 )
331 self.re_Antes = re.compile(
332 r"%(PLYR)s posts ante( of)? [%(BRAX)s]?%(CUR_SYM)s?(?P<ANTE>[.,0-9]+)\s*(%(CUR)s)?[%(BRAX)s]?\.?\s*$"
333 % subst,
334 re.MULTILINE,
335 )
336 self.re_HeroCards = re.compile(r"Dealt to %(PLYR)s \[\s*(?P<NEWCARDS>.+)\s*\]" % subst, re.MULTILINE)
337 self.re_Action = re.compile(
338 r"""
339 (?P<PNAME>.+?)\s(?P<ATYPE>bets|checks|raises|completes|bring-ins|calls|folds|is\sall-In|double\sbets)
340 (?:\s*[%(BRAX)s]?\s?%(CUR_SYM)s?(?P<BET>[.,\d]+)\s*(%(CUR)s)?\s?[%(BRAX)s]?)?
341 (\sto\s[.,\d]+)?
342 \.?\s*$"""
343 % subst,
344 re.MULTILINE | re.VERBOSE,
345 )
346 if not hand.emailedHand:
347 self.re_ShownCards = re.compile(
348 r"%s (?P<SHOWED>(?:doesn\'t )?shows?) " % player_re + r"\[ *(?P<CARDS>.+) *\](?P<COMBINATION>.+)\.",
349 re.MULTILINE,
350 )
351 else:
352 # Michow111 balance $113, bet $50, collected $110.25, net +$60.25 [ 8h 9h ] [ a straight, seven to jack -- Jc,Td,9h,8h,7c ]
353 # babunchik balance $0, lost $50 [ Kd Js ] [ a pair of jacks -- Kd,Js,Jc,Td,7c ]
354 self.re_ShownCards = re.compile(
355 r"%(PLYR)s balance.*" % subst + r"\[ (?P<CARDS>.+) \] *\[ *(?P<COMBINATION>.+) \-\-", re.MULTILINE
356 )
357 if not hand.emailedHand:
358 self.re_CollectPot = re.compile(
359 r"""%(PLYR)s\s+wins\s+(Lo\s\()?%(CUR_SYM)s?(?P<POT>[.,\d]+)\s*(%(CUR)s)?\)?""" % subst,
360 re.MULTILINE | re.VERBOSE,
361 )
362 else:
363 self.re_CollectPot = re.compile(
364 r"""%(PLYR)s(\sbalance\s%(CUR_SYM)s?[.,\d]+,)(\sbet\s%(CUR_SYM)s?[.,\d]+,)(\scollected\s%(CUR_SYM)s?(?P<POT>[.,\d]+),)"""
365 % subst,
366 re.MULTILINE | re.VERBOSE,
367 )
369 def readSupportedGames(self):
370 return [
371 ["ring", "hold", "nl"],
372 ["ring", "hold", "pl"],
373 ["ring", "hold", "fl"],
374 ["ring", "stud", "fl"],
375 ["tour", "hold", "nl"],
376 ["tour", "hold", "pl"],
377 ["tour", "hold", "fl"],
378 ["tour", "stud", "fl"],
379 ]
381 def determineGameType(self, handText):
382 handText = handText.replace("\x00", "")
383 info, extra = {}, {}
384 m = self.re_GameInfo.search(handText)
385 if not m:
386 m = self.re_GameInfoTrny1.search(handText)
387 if not m:
388 m = self.re_GameInfoTrny2.search(handText)
389 m2 = self.re_TourNoLevel.search(handText)
390 m3 = self.re_Blinds.search(handText)
391 if m2 and m3:
392 extra.update(m2.groupdict())
393 extra.update(m3.groupdict())
394 else:
395 m = None
396 if not m:
397 m = self.re_GameInfoTrny3.search(handText)
398 if not m:
399 m = self.re_Disconnected.search(handText)
400 if m:
401 message = "Player Disconnected"
402 raise FpdbHandPartial("Partial hand history: %s" % message)
403 m = self.re_Cancelled.search(handText)
404 if m:
405 message = "Table Closed"
406 raise FpdbHandPartial("Partial hand history: %s" % message)
407 m = self.re_GameStartLine.match(handText)
408 if m and len(handText) < 50:
409 message = "Game start line"
410 raise FpdbHandPartial("Partial hand history: %s" % message)
411 tmp = handText[0:200]
412 log.error(("PartyPokerToFpdb.determineGameType: '%s'") % tmp)
413 raise FpdbParseError
415 mg = m.groupdict()
416 mg.update(extra)
417 # print "DEBUG: mg: %s" % mg
419 if "SITE" in mg and mg["SITE"] is not None:
420 self.sitename = self.sites[mg["SITE"]][0]
421 self.siteId = self.sites[mg["SITE"]][1] # Needs to match id entry in Sites database
422 print("self.siteId", self.siteId)
423 print("self.sitename", self.sitename)
424 if "LIMIT" in mg and mg["LIMIT"] is not None:
425 info["limitType"] = self.limits[mg["LIMIT"]]
426 if "LIMIT2" in mg and mg["LIMIT2"] is not None:
427 info["limitType"] = self.limits[mg["LIMIT2"]]
428 if "LIMIT3" in mg and mg["LIMIT3"] is not None:
429 info["limitType"] = self.limits[mg["LIMIT3"]]
430 if "FAST2" in mg and mg["FAST2"] is not None:
431 info["fast"] = True
432 elif "FAST3" in mg and mg["FAST3"] is not None:
433 info["fast"] = True
434 else:
435 info["fast"] = False
436 if mg["LIMIT"] is None and mg["LIMIT2"] is None and mg["LIMIT3"] is None:
437 info["limitType"] = "fl"
438 if "GAME" in mg:
439 (info["base"], info["category"]) = self.games[mg["GAME"]]
440 if "CASHBI" in mg and mg["CASHBI"] is not None:
441 # The summary is using buyin rather then listing the blinds
442 # Only with NL games?
443 mg["CASHBI"] = self.clearMoneyString(mg["CASHBI"])
444 m_20BBmin = self.re_20BBmin.search(handText)
445 if m_20BBmin is not None:
446 try:
447 info["sb"] = self.NLim_Blinds_20bb[mg["CASHBI"]][0]
448 info["bb"] = self.NLim_Blinds_20bb[mg["CASHBI"]][1]
449 info["buyinType"] = "shallow"
450 except KeyError:
451 tmp = handText[0:200]
452 log.error(
453 ("PartyPokerToFpdb.determineGameType: NLim_Blinds_20bb has no lookup for '%s' - '%s'")
454 % (mg["CASHBI"], tmp)
455 )
456 raise FpdbParseError
457 else:
458 try:
459 if Decimal(mg["CASHBI"]) >= 10000:
460 nl_bb = str((Decimal(mg["CASHBI"]) / 100).quantize(Decimal("0.01")))
461 info["buyinType"] = "deep"
462 else:
463 nl_bb = str((Decimal(mg["CASHBI"]) / 50).quantize(Decimal("0.01")))
464 info["buyinType"] = "regular"
465 info["sb"] = self.Lim_Blinds[nl_bb][0]
466 info["bb"] = self.Lim_Blinds[nl_bb][1]
467 except KeyError:
468 tmp = handText[0:200]
469 log.error(
470 ("PartyPokerToFpdb.determineGameType: Lim_Blinds has no lookup for '%s' - '%s'") % (nl_bb, tmp)
471 )
472 raise FpdbParseError
473 else:
474 if info["category"] == "6_holdem":
475 info["sb"] = "0"
476 info["bb"] = self.clearMoneyString(mg["SB"])
477 else:
478 m = self.re_NewLevel.search(handText)
479 if m:
480 mg["SB"] = m.group("SB")
481 mg["BB"] = m.group("BB")
482 if "SB" in mg:
483 info["sb"] = self.clearMoneyString(mg["SB"])
484 else:
485 info["sb"] = None
486 if "BB" in mg:
487 info["bb"] = self.clearMoneyString(mg["BB"])
488 else:
489 info["bb"] = None
490 info["buyinType"] = "regular"
491 if "CURRENCY" in mg:
492 if mg["CURRENCY"] is None:
493 info["currency"] = self.currencies["$"]
494 else:
495 info["currency"] = self.currencies[mg["CURRENCY"]]
496 if "MIXED" in mg:
497 if mg["MIXED"] is not None:
498 info["mix"] = self.mixes[mg["MIXED"]]
500 if "TOURNO" in mg and mg["TOURNO"] is None:
501 info["type"] = "ring"
502 else:
503 info["type"] = "tour"
504 info["currency"] = "T$"
506 if info["limitType"] == "fl" and info["bb"] is not None:
507 if info["type"] == "ring":
508 try:
509 info["sb"] = self.Lim_Blinds[mg["BB"]][0]
510 info["bb"] = self.Lim_Blinds[mg["BB"]][1]
511 except KeyError:
512 tmp = handText[0:200]
513 log.error(
514 ("PartyPokerToFpdb.determineGameType: Lim_Blinds has no lookup for '%s' - '%s'")
515 % (mg["BB"], tmp)
516 )
517 raise FpdbParseError
518 else:
519 info["sb"] = str((Decimal(mg["SB"]) / 2).quantize(Decimal("0.01")))
520 info["bb"] = str(Decimal(mg["SB"]).quantize(Decimal("0.01")))
521 # print "DEUBG: DGT.info: %s" % info
522 return info
524 def readHandInfo(self, hand):
525 info, m2, extra, type3 = {}, None, {}, False
526 hand.handText = hand.handText.replace("\x00", "")
527 if self.re_emailedHand.search(hand.handText):
528 hand.emailedHand = True
529 else:
530 hand.emailedHand = False
531 m = self.re_HandInfo.search(hand.handText, re.DOTALL)
532 if hand.gametype["type"] == "ring" or hand.emailedHand:
533 m2 = self.re_GameInfo.search(hand.handText)
534 else:
535 m2 = self.re_GameInfoTrny1.search(hand.handText)
536 if not m2:
537 m2 = self.re_GameInfoTrny2.search(hand.handText)
538 m3 = self.re_TourNoLevel.search(hand.handText)
539 m4 = self.re_Blinds.search(hand.handText)
540 if m3 and m4:
541 extra.update(m3.groupdict())
542 extra.update(m4.groupdict())
543 else:
544 m2 = self.re_GameInfoTrny3.search(hand.handText)
545 type3 = True
546 if m is None or m2 is None:
547 tmp = hand.handText[0:200]
548 log.error(("PartyPokerToFpdb.readHandInfo: '%s'") % tmp)
549 raise FpdbParseError
550 info.update(m.groupdict())
551 info.update(m2.groupdict())
552 info.update(extra)
554 for key in info:
555 if key == "DATETIME":
556 # Saturday, July 25, 07:53:52 EDT 2009
557 # Thursday, July 30, 21:40:41 MSKS 2009
558 # Sunday, October 25, 13:39:07 MSK 2009
559 # Mon Jul 12 13:38:32 EDT 2010
560 timezone = "ET"
561 m2 = re.search(
562 r"\w+?,?\s*?(?P<M>\w+)\s+(?P<D>\d+),?\s+(?P<H>\d+):(?P<MIN>\d+):(?P<S>\d+)\s+((?P<TZ>[A-Z]+)\s+)?(?P<Y>\d+)",
563 info[key],
564 re.UNICODE,
565 )
566 if m2.group("TZ"):
567 timezone = m2.group("TZ")
568 month = self.months[m2.group("M")]
569 datetimestr = "%s/%s/%s %s:%s:%s" % (
570 m2.group("Y"),
571 month,
572 m2.group("D"),
573 m2.group("H"),
574 m2.group("MIN"),
575 m2.group("S"),
576 )
577 hand.startTime = datetime.datetime.strptime(datetimestr, "%Y/%m/%d %H:%M:%S")
578 hand.startTime = HandHistoryConverter.changeTimezone(hand.startTime, timezone, "UTC")
579 # FIXME: some timezone correction required
580 # tzShift = defaultdict(lambda:0, {'EDT': -5, 'EST': -6, 'MSKS': 3})
581 # hand.starttime -= datetime.timedelta(hours=tzShift[m2.group('TZ')])
583 if key == "HID":
584 if str(info[key]) == "1111111111":
585 hand.handid = str(int(time.time() * 1000))
586 hand.roundPenny = True
587 else:
588 if re.search("[a-z]", info[key]):
589 hand.handid = info[key][:13]
590 hand.roundPenny = True
591 else:
592 hand.handid = info[key]
593 if key == "TABLE":
594 if "TOURNO" in info and info["TOURNO"] is None:
595 if info["TABLENO"] is not None:
596 hand.tablename = info[key] + " " + info["TABLENO"]
597 else:
598 hand.tablename = info[key]
599 else:
600 hand.tablename = info["TABLENO"]
601 if key == "BUTTON":
602 hand.buttonpos = info[key]
603 if key == "TOURNO":
604 hand.tourNo = info[key]
605 if hand.emailedHand:
606 hand.buyin = 0
607 hand.fee = 0
608 hand.buyinCurrency = "NA"
609 if key == "TABLE_ID_WRAPPER":
610 if info[key] == "#":
611 # FIXME: there is no such property in Hand class
612 self.isSNG = True
613 if key == "BUYIN":
614 if info.get("TABLE") is not None and "Freeroll" in info.get("TABLE"):
615 # Freeroll tourney
616 hand.buyin = 0
617 hand.fee = 0
618 hand.buyinCurrency = "FREE"
619 elif info[key] is None:
620 # Freeroll tourney
621 hand.buyin = 0
622 hand.fee = 0
623 hand.buyinCurrency = "NA"
624 elif hand.tourNo is not None:
625 hand.buyin = 0
626 hand.fee = 0
627 hand.buyinCurrency = "NA"
628 if info[key].find("$") != -1:
629 hand.buyinCurrency = "USD"
630 elif info[key].find("€") != -1:
631 hand.buyinCurrency = "EUR"
632 else:
633 log.error(
634 ("PartyPokerToFpdb.readHandInfo: Failed to detect currency Hand ID: '%s' - '%s'")
635 % (hand.handid, info[key])
636 )
637 raise FpdbParseError
638 info[key] = self.clearMoneyString(info[key].strip("$€"))
639 hand.buyin = int(100 * Decimal(info[key]))
640 if "FEE" in info and info["FEE"] is not None:
641 info["FEE"] = self.clearMoneyString(info["FEE"].strip("$€"))
642 hand.fee = int(100 * Decimal(info["FEE"]))
643 if key == "LEVEL":
644 hand.level = info[key]
645 if key == "PLAY" and info["PLAY"] != "Real":
646 # if realy party doesn's save play money hh
647 hand.gametype["currency"] = "play"
648 if key == "MAX" and info[key] is not None:
649 hand.maxseats = int(info[key])
651 if type3:
652 hand.tourNo = info["TABLE"]
653 hand.buyin = 0
654 hand.fee = 0
655 hand.buyinCurrency = "NA"
657 def readButton(self, hand):
658 m = self.re_Button.search(hand.handText)
659 if m:
660 hand.buttonpos = int(m.group("BUTTON"))
661 else:
662 log.info("readButton: " + ("not found"))
664 def readPlayerStacks(self, hand):
665 log.debug("readPlayerStacks")
666 m = self.re_PlayerInfo.finditer(hand.handText)
667 maxKnownStack = 0
668 zeroStackPlayers = []
669 self.playerMap = {}
670 for a in m:
671 pname = a.group("PNAME")
672 if hand.emailedHand:
673 subst = {"PLYR": re.escape(a.group("PNAME")), "SPACENAME": "\s(.+)? "}
674 re_PlayerName = re.compile(
675 r"""^%(PLYR)s(?P<PNAMEEXTRA>%(SPACENAME)s)balance\s""" % subst, re.MULTILINE | re.VERBOSE
676 )
677 m1 = re_PlayerName.search(hand.handText)
678 if m1 and len(m1.group("PNAMEEXTRA")) > 1:
679 pname = a.group("PNAME") + m1.group("PNAMEEXTRA")
680 pname = pname.strip()
681 self.playerMap[a.group("PNAME")] = pname
682 if a.group("CASH") > "0":
683 # record max known stack for use with players with unknown stack
684 maxKnownStack = max(a.group("CASH"), maxKnownStack)
685 hand.addPlayer(int(a.group("SEAT")), pname, self.clearMoneyString(a.group("CASH")))
686 else:
687 # zero stacked players are added later
688 zeroStackPlayers.append([int(a.group("SEAT")), pname, self.clearMoneyString(a.group("CASH"))])
689 if hand.gametype["type"] == "ring":
690 # finds first vacant seat after an exact seat
691 def findFirstEmptySeat(startSeat):
692 i = 0
693 while startSeat in occupiedSeats:
694 if (startSeat >= hand.maxseats and hand.maxseats is not None) or len(
695 occupiedSeats
696 ) >= hand.maxseats:
697 startSeat = 0
698 startSeat += 1
699 i += 1
700 if i > 10:
701 break
702 return startSeat
704 re_HoleCards = re.compile(r"\*{2} Dealing down cards \*{2}")
705 # re_SplitTest = re.compile(r"(joined the table|left the table|is sitting out)")
706 re_JoiningPlayers = re.compile(r"(?P<PLAYERNAME>.+?) has joined the table")
707 re_BBPostingPlayers = re.compile(r"(?P<PLAYERNAME>.+?) posts big blind", re.MULTILINE)
708 re_LeavingPlayers = re.compile(r"(?P<PLAYERNAME>.+?) has left the table")
709 re_PreDeal = re_HoleCards.split(hand.handText)[0]
711 match_JoiningPlayers = re_JoiningPlayers.findall(re_PreDeal)
712 match_LeavingPlayers = re_LeavingPlayers.findall(re_PreDeal)
713 match_BBPostingPlayers = []
714 m = re_BBPostingPlayers.finditer(re_PreDeal)
715 for player in m:
716 match_BBPostingPlayers.append(player.group("PLAYERNAME"))
718 # add every player with zero stack, but:
719 # if a zero stacked player is just joined the table in this very hand then set his stack to maxKnownStack
720 for p in zeroStackPlayers:
721 if p[1] in match_JoiningPlayers:
722 p[2] = self.clearMoneyString(str(maxKnownStack))
723 if not p[1] in match_LeavingPlayers:
724 hand.addPlayer(p[0], p[1], p[2])
726 seatedPlayers = list([(f[1]) for f in hand.players])
728 # it works for all known cases as of 2010-09-28
729 # should be refined with using match_ActivePlayers instead of match_BBPostingPlayers
730 # as a leaving and rejoining player could be active without posting a BB (sample HH needed)
731 unseatedActivePlayers = list(set(match_BBPostingPlayers) - set(seatedPlayers))
733 if unseatedActivePlayers:
734 for player in unseatedActivePlayers:
735 occupiedSeats = list([(f[0]) for f in hand.players])
736 occupiedSeats.sort()
737 # previousBBPoster = match_BBPostingPlayers[match_BBPostingPlayers.index(player)-1]
738 # previousBBPosterSeat = dict([(f[1], f[0]) for f in hand.players])[previousBBPoster]
739 # newPlayerSeat = findFirstEmptySeat(previousBBPosterSeat)
740 # The commented out code above is 'correct' unless the unseated player is the only BB
741 # I'm willing to live with the unseated player being placed in the lowest seat for now.
742 newPlayerSeat = findFirstEmptySeat(1)
743 hand.addPlayer(newPlayerSeat, player, self.clearMoneyString(str(maxKnownStack)))
745 def markStreets(self, hand):
746 if hand.gametype["base"] in ("hold"):
747 m = re.search(
748 r"\*{2} Dealing down cards \*{2}"
749 r"(?P<PREFLOP>.+?)"
750 r"(?:\*{2} Dealing Flop \*{2} (:?\s*)?(?P<FLOP>\[ \S\S, \S\S, \S\S \].+?))?"
751 r"(?:\*{2} Dealing Turn \*{2} (:?\s*)?(?P<TURN>\[ \S\S \].+?))?"
752 r"(?:\*{2} Dealing River \*{2} (:?\s*)?(?P<RIVER>\[ \S\S \].+?))?$",
753 hand.handText,
754 re.DOTALL,
755 )
756 elif hand.gametype["base"] in ("stud"):
757 m = re.search(
758 r"(?P<ANTES>.+(?=\*\* Dealing \*\*)|.+)"
759 r"(\*\* Dealing \*\*(?P<THIRD>.+(?=\*\* Dealing Fourth street \*\*)|.+))?"
760 r"(\*\* Dealing Fourth street \*\*(?P<FOURTH>.+(?=\*\* Dealing Fifth street \*\*)|.+))?"
761 r"(\*\* Dealing Fifth street \*\*(?P<FIFTH>.+(?=\*\* Dealing Sixth street \*\*)|.+))?"
762 r"(\*\* Dealing Sixth street \*\*(?P<SIXTH>.+(?=\*\* Dealing River \*\*)|.+))?"
763 r"(\*\* Dealing River \*\*(?P<SEVENTH>.+))?",
764 hand.handText,
765 re.DOTALL,
766 )
768 hand.addStreets(m)
770 def readCommunityCards(self, hand, street):
771 if street in ("FLOP", "TURN", "RIVER"):
772 m = self.re_Board.search(hand.streets[street])
773 hand.setCommunityCards(street, renderCards(m.group("CARDS")))
775 def readAntes(self, hand):
776 log.debug("reading antes")
777 m = self.re_Antes.finditer(hand.handText)
778 for player in m:
779 hand.addAnte(player.group("PNAME"), self.clearMoneyString(player.group("ANTE")))
781 def readBlinds(self, hand):
782 noSmallBlind = bool(self.re_NoSmallBlind.search(hand.handText))
783 if (
784 hand.gametype["type"] == "ring"
785 or hand.gametype["sb"] is None
786 or hand.gametype["bb"] is None
787 or hand.roundPenny
788 ):
789 try:
790 assert noSmallBlind is False
791 for m in self.re_PostSB.finditer(hand.handText):
792 hand.addBlind(m.group("PNAME"), "small blind", self.clearMoneyString(m.group("SB")))
793 if hand.gametype["sb"] is None:
794 hand.gametype["sb"] = self.clearMoneyString(m.group("SB"))
795 except (AttributeError, IndexError): # no small blind
796 hand.addBlind(None, None, None)
798 for a in self.re_PostBB.finditer(hand.handText):
799 hand.addBlind(a.group("PNAME"), "big blind", self.clearMoneyString(a.group("BB")))
800 if hand.gametype["bb"] is None:
801 hand.gametype["bb"] = self.clearMoneyString(a.group("BB"))
803 for a in self.re_PostBUB.finditer(hand.handText):
804 hand.addBlind(a.group("PNAME"), "button blind", self.clearMoneyString(a.group("BUB")))
806 for a in self.re_PostDead.finditer(hand.handText):
807 hand.addBlind(a.group("PNAME"), "both", self.clearMoneyString(a.group("BBNDEAD")))
808 else:
809 # party doesn't track blinds for tournaments
810 # so there're some cra^Wcaclulations
811 if hand.buttonpos == 0:
812 self.readButton(hand)
813 # NOTE: code below depends on Hand's implementation
814 # playersMap - dict {seat: (pname,stack)}
815 playersMap = dict([(f[0], f[1:3]) for f in hand.players if f[1] in hand.handText.split("Trny:")[-1]])
816 maxSeat = max(playersMap)
818 def findFirstNonEmptySeat(startSeat):
819 while startSeat not in playersMap:
820 if startSeat >= maxSeat:
821 startSeat = 0
822 startSeat += 1
823 return startSeat
825 smartMin = lambda A, B: A if float(A) <= float(B) else B
827 if noSmallBlind:
828 hand.addBlind(None, None, None)
829 smallBlindSeat = int(hand.buttonpos)
830 else:
831 if len(hand.players) == 2:
832 smallBlindSeat = int(hand.buttonpos)
833 else:
834 smallBlindSeat = findFirstNonEmptySeat(int(hand.buttonpos) + 1)
835 blind = smartMin(hand.sb, playersMap[smallBlindSeat][1])
836 hand.addBlind(playersMap[smallBlindSeat][0], "small blind", blind)
838 if hand.gametype["category"] == "6_holdem":
839 bigBlindSeat = findFirstNonEmptySeat(smallBlindSeat + 1)
840 blind = smartMin(hand.bb, playersMap[bigBlindSeat][1])
841 hand.addBlind(playersMap[bigBlindSeat][0], "button blind", blind)
842 else:
843 bigBlindSeat = findFirstNonEmptySeat(smallBlindSeat + 1)
844 blind = smartMin(hand.bb, playersMap[bigBlindSeat][1])
845 hand.addBlind(playersMap[bigBlindSeat][0], "big blind", blind)
847 def readBringIn(self, hand):
848 pass
849 # m = self.re_BringIn.search(hand.handText,re.DOTALL)
850 # if m:
851 # #~ logging.debug("readBringIn: %s for %s" %(m.group('PNAME'), m.group('BRINGIN')))
852 # hand.addBringIn(m.group('PNAME'), m.group('BRINGIN'))
854 def readHoleCards(self, hand):
855 # we need to grab hero's cards
856 for street in ("PREFLOP",):
857 if street in hand.streets.keys():
858 m = self.re_HeroCards.finditer(hand.streets[street])
859 for found in m:
860 hand.hero = found.group("PNAME")
861 newcards = renderCards(found.group("NEWCARDS"))
862 hand.addHoleCards(street, hand.hero, closed=newcards, shown=False, mucked=False, dealt=True)
864 for street, text in hand.streets.iteritems():
865 if not text or street in ("PREFLOP", "DEAL"):
866 continue # already done these
867 m = self.re_HeroCards.finditer(hand.streets[street])
868 for found in m:
869 player = found.group("PNAME")
870 if street != "SEVENTH":
871 newcards = renderCards(found.group("NEWCARDS"))
872 oldcards = []
873 else:
874 oldcards = renderCards(found.group("NEWCARDS"))
875 newcards = []
877 if street == "THIRD" and len(newcards) == 3: # hero in stud game
878 hand.hero = player
879 hand.dealt.add(player) # need this for stud??
880 hand.addHoleCards(
881 street, player, closed=newcards[0:2], open=[newcards[2]], shown=False, mucked=False, dealt=False
882 )
883 else:
884 hand.addHoleCards(
885 street, player, open=newcards, closed=oldcards, shown=False, mucked=False, dealt=False
886 )
888 def readAction(self, hand, street):
889 m = self.re_Action.finditer(hand.streets[street])
890 for action in m:
891 action.groupdict()
892 # print "DEBUG: acts: %s %s" % (street, acts)
893 playerName = action.group("PNAME")
894 if ":" in playerName:
895 continue # captures chat
896 if self.playerMap.get(playerName):
897 playerName = self.playerMap.get(playerName)
898 amount = self.clearMoneyString(action.group("BET")) if action.group("BET") else None
899 actionType = action.group("ATYPE")
901 if actionType == "folds":
902 hand.addFold(street, playerName)
903 elif actionType == "checks":
904 hand.addCheck(street, playerName)
905 elif actionType == "calls":
906 hand.addCall(street, playerName, amount)
907 elif actionType == "raises":
908 if street == "PREFLOP" and playerName in [
909 item[0] for item in hand.actions["BLINDSANTES"] if item[2] != "ante"
910 ]:
911 # preflop raise from blind
912 hand.addCallandRaise(street, playerName, amount)
913 else:
914 hand.addCallandRaise(street, playerName, amount)
915 elif actionType == "bets" or actionType == "double bets":
916 hand.addBet(street, playerName, amount)
917 elif actionType == "completes":
918 hand.addComplete(street, playerName, amount)
919 elif actionType == "bring-ins":
920 hand.addBringIn(playerName, amount)
921 elif actionType == "is all-In":
922 if amount:
923 hand.addAllIn(street, playerName, amount)
924 else:
925 log.error(
926 (("PartyPokerToFpdb: Unimplemented %s: '%s' '%s'") + " hid:%s")
927 % ("readAction", playerName, actionType, hand.handid)
928 )
929 raise FpdbParseError
931 def readShowdownActions(self, hand):
932 # all action in readShownCards
933 pass
935 def readCollectPot(self, hand):
936 hand.setUncalledBets(True)
937 for m in self.re_CollectPot.finditer(hand.handText):
938 hand.addCollectPot(player=m.group("PNAME"), pot=self.clearMoneyString(m.group("POT")))
940 def readShownCards(self, hand):
941 for m in self.re_ShownCards.finditer(hand.handText):
942 if m.group("CARDS") is not None:
943 cards = renderCards(m.group("CARDS"))
945 mucked = "SHOWED" in m.groupdict() and m.group("SHOWED") != "show"
947 hand.addShownCards(
948 cards=cards, player=m.group("PNAME"), shown=True, mucked=mucked, string=m.group("COMBINATION")
949 )
951 @staticmethod
952 def getTableTitleRe(type, table_name=None, tournament=None, table_number=None):
953 "Returns string to search in windows titles"
954 log.info(
955 "Party.getTableTitleRe: table_name='%s' tournament='%s' table_number='%s'"
956 % (table_name, tournament, table_number)
957 )
958 regex = "%s" % (table_name)
959 if type == "tour":
960 if table_name:
961 TableName = table_name.split(" ")
962 if len(TableName[1]) > 6:
963 regex = "#?%s" % (table_number)
964 else:
965 regex = "%s.+Table\s#?%s" % (TableName[0], table_number)
966 else:
967 #
968 # sng's seem to get passed in with:
969 # table_name = None
970 # tournament=8-digit tourney number
971 # table_number = 7 digit table number
972 # screen string is normally Turbo|Speed|(etc) #table_number
973 #
974 regex = "%s.*%s" % (tournament, table_number)
975 log.info("Party.getTableTitleRe: returns: '%s'" % (regex))
976 return regex
979def renderCards(string):
980 "Splits strings like ' Js, 4d '"
981 cards = string.strip().split(" ")
982 return filter(len, map(lambda x: x.strip(" ,"), cards))