Coverage for TourneySummary.py: 0%
215 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 -*-
4#Copyright 2009-2011 Stephane Alessio
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"""parses and stores summary sections from e.g. eMail or summary files"""
20#import L10n
21#_ = L10n.get_translation()
23# TODO: check to keep only the needed modules
25import re
26import sys
27import logging
28import os
29import os.path
30from decimal_wrapper import Decimal
31import operator
32import time, datetime
33from copy import deepcopy
34from Exceptions import *
35import codecs
37import pprint
38import DerivedStats
39import Card
40import Database
41from HandHistoryConverter import HandHistoryConverter
43log = logging.getLogger("parser")
45try:
46 import xlrd
47except:
48 xlrd = None
49 log.info(("xlrd not found. Required for importing Excel tourney results files"))
51class TourneySummary(object):
53################################################################
54# Class Variables
55 UPS = {'a':'A', 't':'T', 'j':'J', 'q':'Q', 'k':'K', 'S':'s', 'C':'c', 'H':'h', 'D':'d'} # SAL- TO KEEP ??
56 LCS = {'H':'h', 'D':'d', 'C':'c', 'S':'s'} # SAL- TO KEEP ??
57 SYMBOL = {'USD': '$', 'EUR': u'$', 'T$': '', 'play': ''}
58 MS = {'horse' : 'HORSE', '8game' : '8-Game', 'hose' : 'HOSE', 'ha': 'HA'}
59 SITEIDS = {'Fulltilt':1, 'Full Tilt Poker':1, 'PokerStars':2, 'Everleaf':3, 'Boss':4, 'OnGame':5,
60 'UltimateBet':6, 'Betfair':7, 'Absolute':8, 'PartyPoker':9, 'PacificPoker':10,
61 'Partouche':11, 'Merge':12, 'PKR':13, 'iPoker':14, 'Winamax':15, 'Everest':16,
62 'Cake':17, 'Entraction':18, 'BetOnline':19, 'Microgaming':20, 'Bovada':21, 'Enet':22,
63 'SealsWithClubs': 23, 'WinningPoker': 24, 'Run It Once Poker': 26}
66 def __init__(self, db, config, siteName, summaryText, in_path='-', builtFrom="HHC", header=""):
67 self.db = db
68 self.config = config
69 self.import_parameters = self.config.get_import_parameters()
70 self.siteName = siteName
71 self.siteId = None
72 if siteName in self.SITEIDS:
73 self.siteId = self.SITEIDS[siteName]
74 self.in_path = in_path
75 self.header = header
77 self.summaryText = summaryText
78 self.tourneyName = None
79 self.tourneyTypeId = None
80 self.tourneyId = None
81 self.startTime = None
82 self.endTime = None
83 self.tourNo = None
84 self.currency = None
85 self.buyinCurrency = None
86 self.buyin = 0
87 self.fee = 0
88 self.hero = None
89 self.maxseats = 0
90 self.entries = 0
91 self.speed = "Normal"
92 self.prizepool = 0 # Make it a dict in order to deal (eventually later) with non-money winnings : {'MONEY' : amount, 'OTHER' : Value ??}
93 self.buyInChips = 0
94 self.mixed = None
95 self.isRebuy = False
96 self.isAddOn = False
97 self.isKO = False
98 self.isProgressive = False
99 self.isMatrix = False
100 self.isShootout = False
101 self.isFast = False
102 self.rebuyChips = None
103 self.addOnChips = None
104 self.rebuyCost = 0
105 self.addOnCost = 0
106 self.totalRebuyCount = None
107 self.totalAddOnCount = None
108 self.koBounty = 0
109 self.isSng = False
110 self.stack = "Regular"
111 self.isStep = False
112 self.stepNo = 0
113 self.isChance = False
114 self.chanceCount = 0
115 self.isMultiEntry = False
116 self.isReEntry = False
117 self.isNewToGame = False
118 self.isHomeGame = False
119 self.isSplit = False
120 self.isFifty50 = False
121 self.isTime = False
122 self.timeAmt = 0
123 self.isSatellite = False
124 self.isDoubleOrNothing = False
125 self.isCashOut = False
126 self.isOnDemand = False
127 self.isFlighted = False
128 self.isGuarantee = False
129 self.guaranteeAmt = 0
130 self.added = None
131 self.addedCurrency = None
132 self.gametype = {'category':None, 'limitType':None, 'mix':'none'}
133 self.players = {}
134 self.comment = None
135 self.commentTs = None
137 # Collections indexed by player names
138 self.playerIds = {}
139 self.tourneysPlayersIds = {}
140 self.ranks = {}
141 self.winnings = {}
142 self.winningsCurrency = {}
143 self.rebuyCounts = {}
144 self.addOnCounts = {}
145 self.koCounts = {}
147 # currency symbol for this summary
148 self.sym = None
150 if builtFrom == "IMAP":
151 # Fix line endings?
152 pass
153 if self.db == None:
154 self.db = Database.Database(config)
156 self.parseSummary()
157 #end def __init__
159 def __str__(self):
160 #TODO : Update
161 vars = ((("SITE"), self.siteName),
162 (("START TIME"), self.startTime),
163 (("END TIME"), self.endTime),
164 (("TOURNEY NAME"), self.tourneyName),
165 (("TOURNEY NO"), self.tourNo),
166 (("TOURNEY TYPE ID"), self.tourneyTypeId),
167 (("TOURNEY ID"), self.tourneyId),
168 (("BUYIN"), self.buyin),
169 (("FEE"), self.fee),
170 (("CURRENCY"), self.currency),
171 (("HERO"), self.hero),
172 (("MAX SEATS"), self.maxseats),
173 (("ENTRIES"), self.entries),
174 (("SPEED"), self.speed),
175 (("PRIZE POOL"), self.prizepool),
176 (("STARTING CHIP COUNT"), self.buyInChips),
177 (("MIXED"), self.mixed),
178 (("REBUY"), self.isRebuy),
179 (("ADDON"), self.isAddOn),
180 (("KO"), self.isKO),
181 (("MATRIX"), self.isMatrix),
182 (("SHOOTOUT"), self.isShootout),
183 (("REBUY CHIPS"), self.rebuyChips),
184 (("ADDON CHIPS"), self.addOnChips),
185 (("REBUY COST"), self.rebuyCost),
186 (("ADDON COST"), self.addOnCost),
187 (("TOTAL REBUYS"), self.totalRebuyCount),
188 (("TOTAL ADDONS"), self.totalAddOnCount),
189 (("KO BOUNTY"), self.koBounty),
190 (("SNG"), self.isSng),
191 (("SATELLITE"), self.isSatellite),
192 (("DOUBLE OR NOTHING"), self.isDoubleOrNothing),
193 (("GUARANTEEAMT"), self.guaranteeAmt),
194 (("ADDED"), self.added),
195 (("ADDED CURRENCY"), self.addedCurrency),
196 (("COMMENT"), self.comment),
197 (("COMMENT TIMESTAMP"), self.commentTs)
198 )
200 structs = ((("PLAYER IDS"), self.playerIds),
201 (("PLAYERS"), self.players),
202 (("TOURNEYS PLAYERS IDS"), self.tourneysPlayersIds),
203 (("RANKS"), self.ranks),
204 (("WINNINGS"), self.winnings),
205 (("WINNINGS CURRENCY"), self.winningsCurrency),
206 (("COUNT REBUYS"), self.rebuyCounts),
207 (("COUNT ADDONS"), self.addOnCounts),
208 (("COUNT KO"), self.koCounts)
209 )
210 str = ''
211 for (name, var) in vars:
212 str = str + "\n%s = " % name + pprint.pformat(var)
214 for (name, struct) in structs:
215 str = str + "\n%s =\n" % name + pprint.pformat(struct, 4)
216 return str
217 #end def __str__
219 def getSplitRe(self, head): abstract
220 """Function to return a re object to split the summary text into separate tourneys, based on head of file"""
222 def parseSummary(self): abstract
223 """should fill the class variables with the parsed information"""
225 def getSummaryText(self):
226 return self.summaryText
228 @staticmethod
229 def clearMoneyString(money):
230 "Renders 'numbers' like '1 200' and '2,000'"
231 money = money.strip(u'€€\u20ac$ ')
232 return HandHistoryConverter.clearMoneyString(money)
234 def insertOrUpdate(self, printtest=False):
235 # First : check all needed info is filled in the object, especially for the initial select
237 # Notes on DB Insert
238 # Some identified issues for tourneys already in the DB (which occurs when the HH file is parsed and inserted before the Summary)
239 # Be careful on updates that could make the HH import not match the tourney inserted from a previous summary import !!
240 # BuyIn/Fee can be at 0/0 => match may not be easy
241 # Only one existinf Tourney entry for Matrix Tourneys, but multiple Summary files
242 # Starttime may not match the one in the Summary file : HH = time of the first Hand / could be slighltly different from the one in the summary file
243 # Note: If the TourneyNo could be a unique id .... this would really be a relief to deal with matrix matches ==> Ask on the IRC / Ask Fulltilt ??
244 self.db.set_printdata(printtest)
246 self.playerIds = self.db.getSqlPlayerIDs(self.players.keys(), self.siteId, self.hero)
247 #for player in self.players:
248 # id=self.db.get_player_id(self.config, self.siteName, player)
249 # if not id:
250 # id=self.db.insertPlayer(unicode(player), self.siteId)
251 # self.playerIds.update({player:id})
253 #print "TS.insert players",self.players,"playerIds",self.playerIds
254 self.dbid_pids = self.playerIds #TODO:rename this field in Hand so this silly renaming can be removed
256 #print "TS.self before starting insert",self
257 self.tourneyTypeId = self.db.createOrUpdateTourneyType(self)
258 self.tourneyId = self.db.createOrUpdateTourney(self)
259 self.db.createOrUpdateTourneysPlayers(self)
260 self.db.commit()
262 logging.debug(("Tourney Insert/Update done"))
264 # TO DO : Return what has been done (tourney created, updated, nothing)
265 # ?? stored = 1 if tourney is fully created / duplicates = 1, if everything was already here and correct / partial=1 if some things were already here (between tourney, tourneysPlayers and handsPlayers)
266 # if so, prototypes may need changes to know what has been done or make some kind of dict in Tourney object that could be updated during the insert process to store that information
267 stored = 0
268 duplicates = 0
269 partial = 0
270 errors = 0
271 ttime = 0
272 return (stored, duplicates, partial, errors, ttime)
275 def addPlayer(self, rank, name, winnings, winningsCurrency, rebuyCount, addOnCount, koCount, entryId=None):
276 """\
277Adds a player to the tourney, and initialises data structures indexed by player.
278rank (int) indicating the finishing rank (can be -1 if unknown)
279name (string) player name
280winnings (int) the money the player ended the tourney with (can be 0, or -1 if unknown)
281"""
282 log.debug("addPlayer: rank:%s - name : '%s' - Winnings (%s)" % (rank, name, winnings))
283 if self.players.get(name) != None:
284 if entryId is None:
285 entries = self.players[name][-1]
286 self.players[name].append(entries + 1)
287 elif entryId in self.players[name]:
288 return None
289 else:
290 self.players[name].append(entryId)
291 if rank:
292 self.ranks[name].append(rank)
293 self.winnings[name].append(winnings)
294 self.winningsCurrency[name].append(winningsCurrency)
295 else:
296 self.ranks[name].append(None)
297 self.winnings[name].append(None)
298 self.winningsCurrency[name].append(None)
299 self.rebuyCounts[name].append(None)
300 self.addOnCounts[name].append(None)
301 self.koCounts[name].append(None)
302 else:
303 self.players[name] = [entryId if entryId is not None else 1]
304 if rank:
305 self.ranks.update({ name : [rank] })
306 self.winnings.update({ name : [winnings] })
307 self.winningsCurrency.update({ name : [winningsCurrency] })
308 else:
309 self.ranks.update({ name : [None] })
310 self.winnings.update({ name : [None] })
311 self.winningsCurrency.update({ name : [None] })
312 self.rebuyCounts.update({name: [rebuyCount] })
313 self.addOnCounts.update({name: [addOnCount] })
314 self.koCounts.update({name : [koCount] })
315 #end def addPlayer
317 def writeSummary(self, fh=sys.__stdout__):
318 print >> fh, "Override me"
320 def printSummary(self):
321 self.writeSummary(sys.stdout)
323 @staticmethod
324 def summaries_from_excel(filenameXLS, tourNoField):
325 wb = xlrd.open_workbook(filenameXLS)
326 sh = wb.sheet_by_index(0)
327 summaryTexts, rows, header, keys, entries = [], [], None, None, {}
328 for rownum in range(sh.nrows):
329 if rownum==0:
330 header = sh.row_values(rownum)[0]
331 elif tourNoField in sh.row_values(rownum):
332 keys = [str(c).encode('utf-8') for c in sh.row_values(rownum)]
333 elif keys!=None:
334 rows.append([str(c).encode('utf-8') for c in sh.row_values(rownum)])
335 for row in rows:
336 data = dict(zip(keys, row))
337 data['header'] = header
338 if len(data[tourNoField])>0:
339 if entries.get(data[tourNoField])==None:
340 entries[data[tourNoField]] = []
341 entries[data[tourNoField]].append(data)
342 for k, item in entries.iteritems():
343 summaryTexts.append(item)
344 return summaryTexts
346 @staticmethod
347 def readFile(self, filename):
348 whole_file = None
349 for kodec in self.codepage:
350 try:
351 in_fh = codecs.open(filename, 'r', kodec)
352 whole_file = in_fh.read()
353 in_fh.close()
354 break
355 except UnicodeDecodeError as e:
356 log.warning("TS.readFile: '%s' : '%s'" % (filename, e))
357 except UnicodeError as e:
358 log.warning("TS.readFile: '%s' : '%s'" % (filename, e))
360 return whole_file