Coverage for iPokerSummary.py: 0%
187 statements
« prev ^ index » next coverage.py v7.6.3, created at 2024-10-14 11:07 +0000
« prev ^ index » next coverage.py v7.6.3, created at 2024-10-14 11:07 +0000
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
4# Copyright 2008-2011 Carl Gherardi
5# This program is free software: you can redistribute it and/or modify
6# it under the terms of the GNU Affero General Public License as published by
7# the Free Software Foundation, version 3 of the License.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU Affero General Public License
15# along with this program. If not, see <http://www.gnu.org/licenses/>.
16# In the "official" distribution you can find the license in agpl-3.0.txt.
18# import L10n
19# _ = L10n.get_translation()
20from HandHistoryConverter import FpdbParseError, FpdbHandPartial
21from decimal import Decimal
22import re
23import logging
24import datetime
25from TourneySummary import TourneySummary
27log = logging.getLogger("parser")
30class iPokerSummary(TourneySummary):
31 substitutions = {
32 "LS": "\$|\xe2\x82\xac|\xe2\u201a\xac|\u20ac|\xc2\xa3|\£|RSD|kr|",
33 "PLYR": r'(?P<PNAME>[^"]+)',
34 "NUM": r".,0-9",
35 "NUM2": r"\b((?:\d{1,3}(?:\s\d{3})*)|(?:\d+))\b", # Regex pattern for matching numbers with spaces
36 }
37 currencies = {"€": "EUR", "$": "USD", "": "T$", "£": "GBP", "RSD": "RSD", "kr": "SEK"}
39 months = {
40 "Jan": 1,
41 "Feb": 2,
42 "Mar": 3,
43 "Apr": 4,
44 "May": 5,
45 "Jun": 6,
46 "Jul": 7,
47 "Aug": 8,
48 "Sep": 9,
49 "Oct": 10,
50 "Nov": 11,
51 "Dec": 12,
52 }
54 limits = {
55 "No limit": "nl",
56 "Pot limit": "pl",
57 "Limit": "fl",
58 "NL": "nl",
59 "SL": "nl",
60 "БЛ": "nl",
61 "PL": "pl",
62 "LP": "pl",
63 "L": "fl",
64 "LZ": "fl",
65 }
66 games = { # base, category
67 "7 Card Stud": ("stud", "studhi"),
68 "7 Card Stud Hi-Lo": ("stud", "studhilo"),
69 "7 Card Stud HiLow": ("stud", "studhilo"),
70 "5 Card Stud": ("stud", "5_studhi"),
71 "Holdem": ("hold", "holdem"),
72 "Six Plus Holdem": ("hold", "6_holdem"),
73 "Omaha": ("hold", "omahahi"),
74 "Omaha Hi-Lo": ("hold", "omahahilo"),
75 }
77 re_Identify = re.compile("<game\sgamecode=")
78 re_client = re.compile(r"<client_version>(?P<CLIENT>.*?)</client_version>")
79 re_GameType = re.compile(
80 r"""
81 <gametype>(?P<GAME>((?P<CATEGORY>(5|7)\sCard\sStud(\sHi\-Lo)?(\sHiLow)?|(Six\sPlus\s)?Holdem|Omaha(\sHi\-Lo)?(\sHiLow)?)?\s?(?P<LIMIT>NL|SL|L|LZ|PL|БЛ|LP|No\slimit|Pot\slimit|Limit))|LH\s(?P<LSB>[%(NUM)s]+)(%(LS)s)?/(?P<LBB>[%(NUM)s]+)(%(LS)s)?.+?)
82 (\s(%(LS)s)?(?P<SB>[%(NUM)s]+)(%(LS)s)?/(%(LS)s)?(?P<BB>[%(NUM)s]+))?(%(LS)s)?(\sAnte\s(%(LS)s)?(?P<ANTE>[%(NUM)s]+)(%(LS)s)?)?</gametype>\s+?
83 <tablename>(?P<TABLE>.+)?</tablename>\s+?
84 (<(tablecurrency|tournamentcurrency)>.*</(tablecurrency|tournamentcurrency)>\s+?)?
85 (<smallblind>.+</smallblind>\s+?)?
86 (<bigblind>.+</bigblind>\s+?)?
87 <duration>.+</duration>\s+?
88 <gamecount>.+</gamecount>\s+?
89 <startdate>(?P<DATETIME>.+)</startdate>\s+?
90 <currency>(?P<CURRENCY>.+)</currency>\s+?
91 <nickname>(?P<HERO>.+)</nickname>
92 """
93 % substitutions,
94 re.MULTILINE | re.VERBOSE,
95 )
97 re_GameInfoTrny = re.compile(
98 r"""
99 (?:(<tour(?:nament)?code>(?P<TOURNO>\d+)</tour(?:nament)?code>))|
100 (?:(<tournamentname>(?P<NAME>[^<]*)</tournamentname>))|
101 (?:(<rewarddrawn>(?P<REWARD>[%(NUM2)s%(LS)s]+)</rewarddrawn>))|
102 (?:(<place>(?P<PLACE>.+?)</place>))|
103 (?:(<buyin>(?P<BIAMT>[%(NUM2)s%(LS)s]+)\s\+\s)?(?P<BIRAKE>[%(NUM2)s%(LS)s]+)\s\+\s(?P<BIRAKE2>[%(NUM2)s%(LS)s]+)</buyin>)|
104 (?:(<totalbuyin>(?P<TOTBUYIN>.*)</totalbuyin>))|
105 (?:(<win>(%(LS)s)?(?P<WIN>[%(NUM2)s%(LS)s]+)</win>))
106 """
107 % substitutions,
108 re.VERBOSE,
109 )
110 re_GameInfoTrny2 = re.compile(
111 r"""
112 (?:(<tour(?:nament)?code>(?P<TOURNO>\d+)</tour(?:nament)?code>))|
113 (?:(<tournamentname>(?P<NAME>[^<]*)</tournamentname>))|
114 (?:(<place>(?P<PLACE>.+?)</place>))|
115 (?:(<buyin>(?P<BIAMT>[%(NUM2)s%(LS)s]+)\s\+\s)?(?P<BIRAKE>[%(NUM2)s%(LS)s]+)</buyin>)|
116 (?:(<totalbuyin>(?P<TOTBUYIN>[%(NUM2)s%(LS)s]+)</totalbuyin>))|
117 (?:(<win>(%(LS)s)?(?P<WIN>.+?|[%(NUM2)s%(LS)s]+)</win>))
118 """
119 % substitutions,
120 re.VERBOSE,
121 )
122 re_Buyin = re.compile(r"""(?P<BUYIN>[%(NUM)s]+)""" % substitutions, re.MULTILINE | re.VERBOSE)
123 re_TourNo = re.compile(r"(?P<TOURNO>\d+)$", re.MULTILINE)
124 re_TotalBuyin = re.compile(
125 r"""(?P<BUYIN>(?P<BIAMT>[%(LS)s%(NUM)s]+)\s\+\s?(?P<BIRAKE>[%(LS)s%(NUM)s]+)?)""" % substitutions,
126 re.MULTILINE | re.VERBOSE,
127 )
128 re_DateTime1 = re.compile(
129 """(?P<D>[0-9]{2})\-(?P<M>[a-zA-Z]{3})\-(?P<Y>[0-9]{4})\s+(?P<H>[0-9]+):(?P<MIN>[0-9]+)(:(?P<S>[0-9]+))?""",
130 re.MULTILINE,
131 )
132 re_DateTime2 = re.compile(
133 """(?P<D>[0-9]{2})[\/\.](?P<M>[0-9]{2})[\/\.](?P<Y>[0-9]{4})\s+(?P<H>[0-9]+):(?P<MIN>[0-9]+)(:(?P<S>[0-9]+))?""",
134 re.MULTILINE,
135 )
136 re_DateTime3 = re.compile(
137 """(?P<Y>[0-9]{4})\/(?P<M>[0-9]{2})\/(?P<D>[0-9]{2})\s+(?P<H>[0-9]+):(?P<MIN>[0-9]+)(:(?P<S>[0-9]+))?""",
138 re.MULTILINE,
139 )
140 re_Place = re.compile(r"""(?:(<place>(?P<PLACE>.+?)</place>))""" % substitutions, re.VERBOSE)
141 re_FPP = re.compile(r"Pts\s")
143 codepage = ["utf8", "cp1252"]
145 @staticmethod
146 def getSplitRe(self, head):
147 re_SplitTourneys = re.compile("PokerStars Tournament ")
148 return re_SplitTourneys
150 def parseSummary(self):
151 m = self.re_GameType.search(self.summaryText)
152 if not m:
153 tmp = self.summaryText[0:200]
154 log.error(("iPokerSummary.determineGameType: '%s'") % tmp)
155 raise FpdbParseError
157 mg = m.groupdict()
158 # print "DEBUG: m.groupdict(): %s" % mg
160 if "SB" in mg and mg["SB"] is not None:
161 tmp = self.summaryText[0:200]
162 log.error(("iPokerSummary.parseSummary: Text does not appear to be a tournament '%s'") % tmp)
163 raise FpdbParseError
164 else:
165 tourney = True
166 # self.gametype['limitType'] =
167 if "GAME" in mg:
168 if mg["CATEGORY"] is None:
169 (self.info["base"], self.info["category"]) = ("hold", "5_omahahi")
170 else:
171 (self.gametype["base"], self.gametype["category"]) = self.games[mg["CATEGORY"]]
172 if "LIMIT" in mg:
173 self.gametype["limitType"] = self.limits[mg["LIMIT"]]
175 m2 = self.re_DateTime1.search(mg["DATETIME"])
176 if m2:
177 month = self.months[m2.group("M")]
178 sec = m2.group("S")
179 if m2.group("S") is None:
180 sec = "00"
181 datetimestr = "%s/%s/%s %s:%s:%s" % (
182 m2.group("Y"),
183 month,
184 m2.group("D"),
185 m2.group("H"),
186 m2.group("MIN"),
187 sec,
188 )
189 self.startTime = datetime.datetime.strptime(datetimestr, "%Y/%m/%d %H:%M:%S")
190 else:
191 try:
192 self.startTime = datetime.datetime.strptime(mg["DATETIME"], "%Y-%m-%d %H:%M:%S")
193 except ValueError:
194 date_match = self.re_DateTime2.search(mg["DATETIME"])
195 if date_match is not None:
196 datestr = "%d/%m/%Y %H:%M:%S" if "/" in mg["DATETIME"] else "%d.%m.%Y %H:%M:%S"
197 if date_match.group("S") is None:
198 datestr = "%d/%m/%Y %H:%M"
199 else:
200 date_match1 = self.re_DateTime3.search(mg["DATETIME"])
201 datestr = "%Y/%m/%d %H:%M:%S"
202 if date_match1 is None:
203 log.error(("iPokerSummary.parseSummary Could not read datetime"))
204 raise FpdbParseError
205 if date_match1.group("S") is None:
206 datestr = "%Y/%m/%d %H:%M"
207 self.startTime = datetime.datetime.strptime(mg["DATETIME"], datestr)
209 if not mg["CURRENCY"] or mg["CURRENCY"] == "fun":
210 self.buyinCurrency = "play"
211 else:
212 self.buyinCurrency = mg["CURRENCY"]
213 self.currency = self.buyinCurrency
215 mt = self.re_TourNo.search(mg["TABLE"])
216 if mt:
217 self.tourNo = mt.group("TOURNO")
218 else:
219 tourNo = mg["TABLE"].split(",")[-1].strip().split(" ")[0]
220 if tourNo.isdigit():
221 self.tourNo = tourNo
223 if tourney:
224 re_client_split = ".".join(self.re_client.split(".")[:2])
225 if re_client_split == "23.5": # betclic fr
226 matches = list(self.re_GameInfoTrny.finditer(self.summaryText))
227 if len(matches) > 0:
228 mg2 = {
229 "TOURNO": None,
230 "NAME": None,
231 "REWARD": None,
232 "PLACE": None,
233 "BIAMT": None,
234 "BIRAKE": None,
235 "BIRAKE2": None,
236 "TOTBUYIN": None,
237 "WIN": None,
238 }
239 mg2["TOURNO"] = matches[0].group("TOURNO")
240 mg2["NAME"] = matches[1].group("NAME")
241 mg2["REWARD"] = matches[2].group("REWARD")
242 mg2["PLACE"] = matches[3].group("PLACE")
243 mg2["BIAMT"] = matches[4].group("BIAMT")
244 mg2["BIRAKE"] = matches[4].group("BIRAKE")
245 mg2["BIRAKE2"] = matches[4].group("BIRAKE2")
246 mg2["TOTBUYIN"] = matches[5].group("TOTBUYIN")
247 mg2["WIN"] = matches[6].group("WIN")
249 self.buyin = 0
250 self.fee = 0
251 self.prizepool = None
252 self.entries = None
254 if mg2["TOURNO"]:
255 self.tourNo = mg2["TOURNO"]
256 # if mg2['CURRENCY']:
257 # self.currency = self.currencies[mg2['CURRENCY']]
258 rank, winnings = None, None
259 if "PLACE" in mg2 and self.re_Place.search(mg2["PLACE"]):
260 rank = int(mg2["PLACE"])
261 winnings = int(100 * self.convert_to_decimal(mg2["WIN"]))
263 self.tourneyName = mg2["NAME"].replace(" " + self.tourNo, "")
265 if not mg2["BIRAKE"] and mg2["TOTBUYIN"]:
266 m3 = self.re_TotalBuyin.search(mg2["TOTBUYIN"])
267 if m3:
268 mg2 = m3.groupdict()
269 elif mg2["BIAMT"]:
270 mg2["BIRAKE"] = "0"
271 if mg2["BIAMT"] and mg2["BIRAKE"]:
272 self.buyin = int(100 * self.convert_to_decimal(mg2["BIAMT"]))
273 self.fee = int(100 * self.convert_to_decimal(mg2["BIRAKE"]))
274 if "BIRAKE1" in mg2 and mg2["BIRAKE1"]:
275 self.buyin += int(100 * self.convert_to_decimal(mg2["BIRAKE1"]))
276 if self.re_FPP.match(mg2["BIAMT"]):
277 self.buyinCurrency = "FPP"
278 else:
279 self.buyin = 0
280 self.fee = 0
281 if self.buyin == 0:
282 self.buyinCurrency = "FREE"
283 hero = mg["HERO"]
284 self.addPlayer(rank, hero, winnings, self.currency, None, None, None)
285 else:
286 raise FpdbHandPartial(hid=self.tourNo)
287 if self.tourNo is None:
288 log.error(("iPokerSummary.parseSummary: Could Not Parse tourNo"))
289 raise FpdbParseError
291 else:
292 matches = list(self.re_GameInfoTrny2.finditer(self.summaryText))
293 if len(matches) > 0:
294 mg2 = {
295 "TOURNO": None,
296 "NAME": None,
297 "PLACE": None,
298 "BIAMT": None,
299 "BIRAKE": None,
300 "TOTBUYIN": None,
301 "WIN": None,
302 }
303 mg2["TOURNO"] = matches[0].group("TOURNO")
304 mg2["NAME"] = matches[1].group("NAME")
305 mg2["PLACE"] = matches[2].group("PLACE")
306 mg2["BIAMT"] = matches[3].group("BIAMT")
307 mg2["BIRAKE"] = matches[3].group("BIRAKE")
308 mg2["TOTBUYIN"] = matches[4].group("TOTBUYIN")
309 mg2["WIN"] = matches[5].group("WIN")
311 self.buyin = 0
312 self.fee = 0
313 self.prizepool = None
314 self.entries = None
316 if mg2["TOURNO"]:
317 self.tourNo = mg2["TOURNO"]
318 # if mg2['CURRENCY']:
319 # self.currency = self.currencies[mg2['CURRENCY']]
320 rank, winnings = None, None
321 if "PLACE" in mg2 and mg2["PLACE"] != "N/A":
322 rank = int(mg2["PLACE"])
323 if mg2["WIN"] and mg2["WIN"] != "N/A":
324 winnings = int(100 * self.convert_to_decimal(mg2["WIN"]))
326 self.tourneyName = mg2["NAME"].replace(" " + self.tourNo, "")
328 if not mg2["BIRAKE"] and mg2["TOTBUYIN"]:
329 m3 = self.re_TotalBuyin.search(mg2["TOTBUYIN"])
330 if m3:
331 mg2 = m3.groupdict()
332 elif mg2["BIAMT"]:
333 mg2["BIRAKE"] = "0"
334 if mg2["BIAMT"] and mg2["BIRAKE"]:
335 self.buyin = int(100 * self.convert_to_decimal(mg2["BIAMT"]))
336 self.fee = int(100 * self.convert_to_decimal(mg2["BIRAKE"]))
337 if "BIRAKE1" in mg2 and mg2["BIRAKE1"]:
338 self.buyin += int(100 * self.convert_to_decimal(mg2["BIRAKE1"]))
339 if self.re_FPP.match(mg2["BIAMT"]):
340 self.buyinCurrency = "FPP"
341 else:
342 self.buyin = 0
343 self.fee = 0
344 if self.buyin == 0:
345 self.buyinCurrency = "FREE"
346 hero = mg["HERO"]
347 self.addPlayer(rank, hero, winnings, self.currency, None, None, None)
348 else:
349 raise FpdbHandPartial(hid=self.tourNo)
350 if self.tourNo is None:
351 log.error(("iPokerSummary.parseSummary: Could Not Parse tourNo"))
352 raise FpdbParseError
353 else:
354 tmp = self.summaryText[0:200]
355 log.error(("iPokerSummary.determineGameType: Text does not appear to be a tournament '%s'") % tmp)
356 raise FpdbParseError
358 def convert_to_decimal(self, string):
359 dec = self.clearMoneyString(string)
360 m = self.re_Buyin.search(dec)
361 if m:
362 dec = Decimal(m.group("BUYIN"))
363 else:
364 dec = 0
365 return dec