Coverage for TourneySummary.py: 0%

206 statements  

« 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 -*- 

3 

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. 

17 

18"""parses and stores summary sections from e.g. eMail or summary files""" 

19 

20# import L10n 

21# _ = L10n.get_translation() 

22 

23# TODO: check to keep only the needed modules 

24 

25import sys 

26import logging 

27 

28import codecs 

29 

30import pprint 

31import Database 

32from HandHistoryConverter import HandHistoryConverter 

33 

34 

35try: 

36 import xlrd 

37except ImportError: 

38 xlrd = None 

39 

40 

41log = logging.getLogger("parser") 

42 

43 

44class TourneySummary(object): 

45 ################################################################ 

46 # Class Variables 

47 UPS = {"a": "A", "t": "T", "j": "J", "q": "Q", "k": "K", "S": "s", "C": "c", "H": "h", "D": "d"} # SAL- TO KEEP ?? 

48 LCS = {"H": "h", "D": "d", "C": "c", "S": "s"} # SAL- TO KEEP ?? 

49 SYMBOL = {"USD": "$", "EUR": "$", "T$": "", "play": ""} 

50 MS = {"horse": "HORSE", "8game": "8-Game", "hose": "HOSE", "ha": "HA"} 

51 SITEIDS = { 

52 "Fulltilt": 1, 

53 "Full Tilt Poker": 1, 

54 "PokerStars": 2, 

55 "Everleaf": 3, 

56 "Boss": 4, 

57 "OnGame": 5, 

58 "UltimateBet": 6, 

59 "Betfair": 7, 

60 "Absolute": 8, 

61 "PartyPoker": 9, 

62 "PacificPoker": 10, 

63 "Partouche": 11, 

64 "Merge": 12, 

65 "PKR": 13, 

66 "iPoker": 14, 

67 "Winamax": 15, 

68 "Everest": 16, 

69 "Cake": 17, 

70 "Entraction": 18, 

71 "BetOnline": 19, 

72 "Microgaming": 20, 

73 "Bovada": 21, 

74 "Enet": 22, 

75 "SealsWithClubs": 23, 

76 "WinningPoker": 24, 

77 "Run It Once Poker": 26, 

78 } 

79 

80 def __init__(self, db, config, siteName, summaryText, in_path="-", builtFrom="HHC", header=""): 

81 self.db = db 

82 self.config = config 

83 self.import_parameters = self.config.get_import_parameters() 

84 self.siteName = siteName 

85 self.siteId = None 

86 if siteName in self.SITEIDS: 

87 self.siteId = self.SITEIDS[siteName] 

88 self.in_path = in_path 

89 self.header = header 

90 

91 self.summaryText = summaryText 

92 self.tourneyName = None 

93 self.tourneyTypeId = None 

94 self.tourneyId = None 

95 self.startTime = None 

96 self.endTime = None 

97 self.tourNo = None 

98 self.currency = None 

99 self.buyinCurrency = None 

100 self.buyin = 0 

101 self.fee = 0 

102 self.hero = None 

103 self.maxseats = 0 

104 self.entries = 0 

105 self.speed = "Normal" 

106 self.prizepool = 0 # Make it a dict in order to deal (eventually later) with non-money winnings : {'MONEY' : amount, 'OTHER' : Value ??} 

107 self.buyInChips = 0 

108 self.mixed = None 

109 self.isRebuy = False 

110 self.isAddOn = False 

111 self.isKO = False 

112 self.isProgressive = False 

113 self.isMatrix = False 

114 self.isShootout = False 

115 self.isFast = False 

116 self.rebuyChips = None 

117 self.addOnChips = None 

118 self.rebuyCost = 0 

119 self.addOnCost = 0 

120 self.totalRebuyCount = None 

121 self.totalAddOnCount = None 

122 self.koBounty = 0 

123 self.isSng = False 

124 self.stack = "Regular" 

125 self.isStep = False 

126 self.stepNo = 0 

127 self.isChance = False 

128 self.chanceCount = 0 

129 self.isMultiEntry = False 

130 self.isReEntry = False 

131 self.isNewToGame = False 

132 self.isHomeGame = False 

133 self.isSplit = False 

134 self.isFifty50 = False 

135 self.isTime = False 

136 self.timeAmt = 0 

137 self.isSatellite = False 

138 self.isDoubleOrNothing = False 

139 self.isCashOut = False 

140 self.isOnDemand = False 

141 self.isFlighted = False 

142 self.isGuarantee = False 

143 self.guaranteeAmt = 0 

144 self.added = None 

145 self.addedCurrency = None 

146 self.gametype = {"category": None, "limitType": None, "mix": "none"} 

147 self.players = {} 

148 self.comment = None 

149 self.commentTs = None 

150 

151 # Collections indexed by player names 

152 self.playerIds = {} 

153 self.tourneysPlayersIds = {} 

154 self.ranks = {} 

155 self.winnings = {} 

156 self.winningsCurrency = {} 

157 self.rebuyCounts = {} 

158 self.addOnCounts = {} 

159 self.koCounts = {} 

160 

161 # currency symbol for this summary 

162 self.sym = None 

163 

164 if builtFrom == "IMAP": 

165 # Fix line endings? 

166 pass 

167 if self.db is None: 

168 self.db = Database.Database(config) 

169 

170 self.parseSummary() 

171 

172 # end def __init__ 

173 

174 def __str__(self): 

175 # TODO : Update 

176 vars = ( 

177 (("SITE"), self.siteName), 

178 (("START TIME"), self.startTime), 

179 (("END TIME"), self.endTime), 

180 (("TOURNEY NAME"), self.tourneyName), 

181 (("TOURNEY NO"), self.tourNo), 

182 (("TOURNEY TYPE ID"), self.tourneyTypeId), 

183 (("TOURNEY ID"), self.tourneyId), 

184 (("BUYIN"), self.buyin), 

185 (("FEE"), self.fee), 

186 (("CURRENCY"), self.currency), 

187 (("HERO"), self.hero), 

188 (("MAX SEATS"), self.maxseats), 

189 (("ENTRIES"), self.entries), 

190 (("SPEED"), self.speed), 

191 (("PRIZE POOL"), self.prizepool), 

192 (("STARTING CHIP COUNT"), self.buyInChips), 

193 (("MIXED"), self.mixed), 

194 (("REBUY"), self.isRebuy), 

195 (("ADDON"), self.isAddOn), 

196 (("KO"), self.isKO), 

197 (("MATRIX"), self.isMatrix), 

198 (("SHOOTOUT"), self.isShootout), 

199 (("REBUY CHIPS"), self.rebuyChips), 

200 (("ADDON CHIPS"), self.addOnChips), 

201 (("REBUY COST"), self.rebuyCost), 

202 (("ADDON COST"), self.addOnCost), 

203 (("TOTAL REBUYS"), self.totalRebuyCount), 

204 (("TOTAL ADDONS"), self.totalAddOnCount), 

205 (("KO BOUNTY"), self.koBounty), 

206 (("SNG"), self.isSng), 

207 (("SATELLITE"), self.isSatellite), 

208 (("DOUBLE OR NOTHING"), self.isDoubleOrNothing), 

209 (("GUARANTEEAMT"), self.guaranteeAmt), 

210 (("ADDED"), self.added), 

211 (("ADDED CURRENCY"), self.addedCurrency), 

212 (("COMMENT"), self.comment), 

213 (("COMMENT TIMESTAMP"), self.commentTs), 

214 ) 

215 

216 structs = ( 

217 (("PLAYER IDS"), self.playerIds), 

218 (("PLAYERS"), self.players), 

219 (("TOURNEYS PLAYERS IDS"), self.tourneysPlayersIds), 

220 (("RANKS"), self.ranks), 

221 (("WINNINGS"), self.winnings), 

222 (("WINNINGS CURRENCY"), self.winningsCurrency), 

223 (("COUNT REBUYS"), self.rebuyCounts), 

224 (("COUNT ADDONS"), self.addOnCounts), 

225 (("COUNT KO"), self.koCounts), 

226 ) 

227 str = "" 

228 for name, var in vars: 

229 str = str + "\n%s = " % name + pprint.pformat(var) 

230 

231 for name, struct in structs: 

232 str = str + "\n%s =\n" % name + pprint.pformat(struct, 4) 

233 return str 

234 

235 # end def __str__ 

236 

237 def getSplitRe(self, head): 

238 pass 

239 

240 """Function to return a re object to split the summary text into separate tourneys, based on head of file""" 

241 

242 def parseSummary(self): 

243 pass 

244 

245 """should fill the class variables with the parsed information""" 

246 

247 def getSummaryText(self): 

248 return self.summaryText 

249 

250 @staticmethod 

251 def clearMoneyString(money): 

252 "Renders 'numbers' like '1 200' and '2,000'" 

253 money = money.strip("€&euro;\u20ac$ ") 

254 return HandHistoryConverter.clearMoneyString(money) 

255 

256 def insertOrUpdate(self, printtest=False): 

257 # First : check all needed info is filled in the object, especially for the initial select 

258 

259 # Notes on DB Insert 

260 # Some identified issues for tourneys already in the DB (which occurs when the HH file is parsed and inserted before the Summary) 

261 # Be careful on updates that could make the HH import not match the tourney inserted from a previous summary import !! 

262 # BuyIn/Fee can be at 0/0 => match may not be easy 

263 # Only one existinf Tourney entry for Matrix Tourneys, but multiple Summary files 

264 # 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 

265 # 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 ?? 

266 self.db.set_printdata(printtest) 

267 

268 self.playerIds = self.db.getSqlPlayerIDs(self.players.keys(), self.siteId, self.hero) 

269 # for player in self.players: 

270 # id=self.db.get_player_id(self.config, self.siteName, player) 

271 # if not id: 

272 # id=self.db.insertPlayer(unicode(player), self.siteId) 

273 # self.playerIds.update({player:id}) 

274 

275 # print "TS.insert players",self.players,"playerIds",self.playerIds 

276 self.dbid_pids = self.playerIds # TODO:rename this field in Hand so this silly renaming can be removed 

277 

278 # print "TS.self before starting insert",self 

279 self.tourneyTypeId = self.db.createOrUpdateTourneyType(self) 

280 self.tourneyId = self.db.createOrUpdateTourney(self) 

281 self.db.createOrUpdateTourneysPlayers(self) 

282 self.db.commit() 

283 

284 logging.debug(("Tourney Insert/Update done")) 

285 

286 # TO DO : Return what has been done (tourney created, updated, nothing) 

287 # ?? 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) 

288 # 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 

289 stored = 0 

290 duplicates = 0 

291 partial = 0 

292 errors = 0 

293 ttime = 0 

294 return (stored, duplicates, partial, errors, ttime) 

295 

296 def addPlayer(self, rank, name, winnings, winningsCurrency, rebuyCount, addOnCount, koCount, entryId=None): 

297 """\ 

298Adds a player to the tourney, and initialises data structures indexed by player. 

299rank (int) indicating the finishing rank (can be -1 if unknown) 

300name (string) player name 

301winnings (int) the money the player ended the tourney with (can be 0, or -1 if unknown) 

302""" 

303 log.debug("addPlayer: rank:%s - name : '%s' - Winnings (%s)" % (rank, name, winnings)) 

304 if self.players.get(name) is not None: 

305 if entryId is None: 

306 entries = self.players[name][-1] 

307 self.players[name].append(entries + 1) 

308 elif entryId in self.players[name]: 

309 return None 

310 else: 

311 self.players[name].append(entryId) 

312 if rank: 

313 self.ranks[name].append(rank) 

314 self.winnings[name].append(winnings) 

315 self.winningsCurrency[name].append(winningsCurrency) 

316 else: 

317 self.ranks[name].append(None) 

318 self.winnings[name].append(None) 

319 self.winningsCurrency[name].append(None) 

320 self.rebuyCounts[name].append(None) 

321 self.addOnCounts[name].append(None) 

322 self.koCounts[name].append(None) 

323 else: 

324 self.players[name] = [entryId if entryId is not None else 1] 

325 if rank: 

326 self.ranks.update({name: [rank]}) 

327 self.winnings.update({name: [winnings]}) 

328 self.winningsCurrency.update({name: [winningsCurrency]}) 

329 else: 

330 self.ranks.update({name: [None]}) 

331 self.winnings.update({name: [None]}) 

332 self.winningsCurrency.update({name: [None]}) 

333 self.rebuyCounts.update({name: [rebuyCount]}) 

334 self.addOnCounts.update({name: [addOnCount]}) 

335 self.koCounts.update({name: [koCount]}) 

336 

337 # end def addPlayer 

338 

339 def writeSummary(self, fh=sys.__stdout__): 

340 print("Override me", file=fh) 

341 

342 def printSummary(self): 

343 self.writeSummary(sys.stdout) 

344 

345 @staticmethod 

346 def summaries_from_excel(filenameXLS, tourNoField): 

347 wb = xlrd.open_workbook(filenameXLS) 

348 sh = wb.sheet_by_index(0) 

349 summaryTexts, rows, header, keys, entries = [], [], None, None, {} 

350 for rownum in range(sh.nrows): 

351 if rownum == 0: 

352 header = sh.row_values(rownum)[0] 

353 elif tourNoField in sh.row_values(rownum): 

354 keys = [str(c).encode("utf-8") for c in sh.row_values(rownum)] 

355 elif keys is not None: 

356 rows.append([str(c).encode("utf-8") for c in sh.row_values(rownum)]) 

357 for row in rows: 

358 data = dict(zip(keys, row)) 

359 data["header"] = header 

360 if len(data[tourNoField]) > 0: 

361 if entries.get(data[tourNoField]) is None: 

362 entries[data[tourNoField]] = [] 

363 entries[data[tourNoField]].append(data) 

364 for k, item in entries.iteritems(): 

365 summaryTexts.append(item) 

366 return summaryTexts 

367 

368 @staticmethod 

369 def readFile(self, filename): 

370 whole_file = None 

371 for kodec in self.codepage: 

372 try: 

373 in_fh = codecs.open(filename, "r", kodec) 

374 whole_file = in_fh.read() 

375 in_fh.close() 

376 break 

377 except UnicodeDecodeError as e: 

378 log.warning("TS.readFile: '%s' : '%s'" % (filename, e)) 

379 except UnicodeError as e: 

380 log.warning("TS.readFile: '%s' : '%s'" % (filename, e)) 

381 

382 return whole_file