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