Merge branch 'master' of git://git.assembla.com/free_poker_tools
This commit is contained in:
		
						commit
						49022fcf83
					
				
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							|  | @ -1,2 +0,0 @@ | |||
| *.pyc | ||||
| *~ | ||||
|  | @ -27,6 +27,12 @@ from HandHistoryConverter import * | |||
| 
 | ||||
| class Absolute(HandHistoryConverter): | ||||
| 
 | ||||
|     # Class Variables | ||||
|     sitename = "Absolute" | ||||
|     filetype = "text" | ||||
|     codepage = "cp1252" | ||||
|     siteid   = 8 | ||||
|      | ||||
|     # Static regexes | ||||
|     re_SplitHands  = re.compile(r"\n\n\n+") | ||||
|     re_TailSplitHands  = re.compile(r"(\n\n\n+)") | ||||
|  | @ -48,24 +54,6 @@ class Absolute(HandHistoryConverter): | |||
| #    re_Board       = re.compile(ur"\[ (?P<CARDS>.+) \]") | ||||
|      | ||||
|      | ||||
|     def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True, debugging=False, index=0): | ||||
|         """\ | ||||
| in_path   (default '-' = sys.stdin) | ||||
| out_path  (default '-' = sys.stdout) | ||||
| follow :  whether to tail -f the input | ||||
| autostart: whether to run the thread (or you can call start() yourself) | ||||
| debugging: if False, pass on partially supported game types. If true, have a go and error...""" | ||||
|         #print "DEBUG: XXXXXXXXXXXXXXX" | ||||
|         HandHistoryConverter.__init__(self, in_path, out_path, sitename="Absolute", follow=follow, index=index) | ||||
|         logging.info("Initialising Absolute converter class") | ||||
|         self.filetype = "text" | ||||
|         self.codepage = "cp1252" | ||||
|         self.siteId   = 8 # Needs to match id entry in Sites database | ||||
|         self.debugging = debugging | ||||
|         if autostart: | ||||
|             self.start() | ||||
|             # otherwise you need to call start yourself. | ||||
| 
 | ||||
|     def compilePlayerRegexs(self, hand): | ||||
|         players = set([player[1] for player in hand.players]) | ||||
|         if not players <= self.compiledPlayers: # x <= y means 'x is subset of y' | ||||
|  |  | |||
|  | @ -26,6 +26,11 @@ from HandHistoryConverter import * | |||
| 
 | ||||
| class Betfair(HandHistoryConverter): | ||||
| 
 | ||||
|     sitename = 'Betfair' | ||||
|     filetype = "text" | ||||
|     codepage = "cp1252" | ||||
|     siteId   = 7 # Needs to match id entry in Sites database | ||||
| 
 | ||||
|     # Static regexes | ||||
|     re_GameInfo      = re.compile("^(?P<LIMIT>NL|PL|) (?P<CURRENCY>\$|)?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) (?P<GAME>(Texas Hold\'em|Omaha Hi|Razz))", re.MULTILINE) | ||||
|     re_SplitHands    = re.compile(r'\n\n+') | ||||
|  | @ -34,19 +39,6 @@ class Betfair(HandHistoryConverter): | |||
|     re_PlayerInfo    = re.compile("Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*)\s\(\s(\$(?P<CASH>[.0-9]+)) \)") | ||||
|     re_Board         = re.compile(ur"\[ (?P<CARDS>.+) \]") | ||||
| 
 | ||||
|     def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True, index=0): | ||||
|         """\ | ||||
| in_path   (default '-' = sys.stdin) | ||||
| out_path  (default '-' = sys.stdout) | ||||
| follow :  whether to tail -f the input""" | ||||
|         HandHistoryConverter.__init__(self, in_path, out_path, sitename="Betfair", follow=follow, index) # Call super class init. | ||||
|         logging.info("Initialising Betfair converter class") | ||||
|         self.filetype = "text" | ||||
|         self.codepage = "cp1252" | ||||
|         self.siteId   = 7 # Needs to match id entry in Sites database | ||||
|         if autostart: | ||||
|             self.start() | ||||
| 
 | ||||
| 
 | ||||
|     def compilePlayerRegexs(self,  hand): | ||||
|         players = set([player[1] for player in hand.players]) | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| #!/usr/bin/env python | ||||
| # -*- coding: utf-8 -*- | ||||
| """Configuration.py | ||||
| 
 | ||||
| Handles HUD configuration files. | ||||
|  | @ -73,11 +74,17 @@ class Layout: | |||
| 
 | ||||
| class Site: | ||||
|     def __init__(self, node): | ||||
|         def normalizePath(path): | ||||
|             "Normalized existing pathes" | ||||
|             if os.path.exists(path): | ||||
|                 return os.path.abspath(path) | ||||
|             return path | ||||
|          | ||||
|         self.site_name    = node.getAttribute("site_name") | ||||
|         self.table_finder = node.getAttribute("table_finder") | ||||
|         self.screen_name  = node.getAttribute("screen_name") | ||||
|         self.site_path    = node.getAttribute("site_path") | ||||
|         self.HH_path      = node.getAttribute("HH_path") | ||||
|         self.site_path    = normalizePath(node.getAttribute("site_path")) | ||||
|         self.HH_path      = normalizePath(node.getAttribute("HH_path")) | ||||
|         self.decoder      = node.getAttribute("decoder") | ||||
|         self.hudopacity   = node.getAttribute("hudopacity") | ||||
|         self.hudbgcolor   = node.getAttribute("bgcolor") | ||||
|  | @ -92,6 +99,8 @@ class Site: | |||
|         self.ypad         = node.getAttribute("ypad") | ||||
|         self.layout       = {} | ||||
|              | ||||
|         print self.site_name, self.HH_path | ||||
| 
 | ||||
|         for layout_node in node.getElementsByTagName('layout'): | ||||
|             lo = Layout(layout_node) | ||||
|             self.layout[lo.max] = lo | ||||
|  | @ -543,6 +552,13 @@ class Config: | |||
|             if db_type   != None: self.supported_databases[db_name].dp_type   = db_type | ||||
|         return | ||||
|      | ||||
|     def getDefaultSite(self): | ||||
|         "Returns first enabled site or None" | ||||
|         for site_name,site in self.supported_sites.iteritems(): | ||||
|             if site.enabled: | ||||
|                 return site_name | ||||
|         return None | ||||
| 
 | ||||
|     def get_tv_parameters(self): | ||||
|         tv = {} | ||||
|         try:    tv['combinedStealFold'] = self.tv.combinedStealFold | ||||
|  | @ -573,14 +589,15 @@ class Config: | |||
|         except:  imp['fastStoreHudCache'] = True | ||||
|         return imp | ||||
| 
 | ||||
|     def get_default_paths(self, site = "PokerStars"): | ||||
|     def get_default_paths(self, site = None): | ||||
|         if site is None: site = self.getDefaultSite() | ||||
|         paths = {} | ||||
|         try: | ||||
|             paths['hud-defaultPath']        = os.path.expanduser(self.supported_sites[site].HH_path) | ||||
|             paths['bulkImport-defaultPath'] = os.path.expanduser(self.supported_sites[site].HH_path) | ||||
|             path = os.path.expanduser(self.supported_sites[site].HH_path) | ||||
|             assert(os.path.isdir(path) or os.path.isfile(path)) # maybe it should try another site? | ||||
|             paths['hud-defaultPath'] = paths['bulkImport-defaultPath'] = path | ||||
|         except: | ||||
|             paths['hud-defaultPath']        = "default" | ||||
|             paths['bulkImport-defaultPath'] = "default" | ||||
|             paths['hud-defaultPath'] = paths['bulkImport-defaultPath'] = "default" | ||||
|         return paths | ||||
|      | ||||
|     def get_frames(self, site = "PokerStars"): | ||||
|  |  | |||
|  | @ -28,6 +28,7 @@ import sys | |||
| import traceback | ||||
| from datetime import datetime, date, time, timedelta | ||||
| from time import time, strftime, sleep | ||||
| from decimal import Decimal | ||||
| import string | ||||
| import re | ||||
| import logging | ||||
|  | @ -1020,6 +1021,22 @@ class Database: | |||
|             print "Error during fdb.lock_for_insert:", str(sys.exc_value) | ||||
|     #end def lock_for_insert | ||||
| 
 | ||||
|     def getGameTypeId(self, siteid, game): | ||||
|         c = self.get_cursor() | ||||
|         #FIXME: Fixed for NL at the moment | ||||
|         c.execute(self.sql.query['getGametypeNL'], (siteid, game['type'], game['category'], game['limitType'],  | ||||
|                         int(Decimal(game['sb'])*100), int(Decimal(game['bb'])*100))) | ||||
|         tmp = c.fetchone() | ||||
|         if (tmp == None): | ||||
|             hilo = "h" | ||||
|             if game['category'] in ['studhilo', 'omahahilo']: | ||||
|                 hilo = "s" | ||||
|             elif game['category'] in ['razz','27_3draw','badugi']: | ||||
|                 hilo = "l" | ||||
|             tmp  = self.insertGameTypes( (siteid, game['type'], game['base'], game['category'], game['limitType'], hilo, | ||||
|                                     int(Decimal(game['sb'])*100), int(Decimal(game['bb'])*100), 0, 0) ) | ||||
|         return tmp[0] | ||||
| 
 | ||||
|     def getSqlPlayerIDs(self, pnames, siteid): | ||||
|         result = {} | ||||
|         if(self.pcache == None): | ||||
|  | @ -1127,15 +1144,22 @@ class Database: | |||
|             sitehandno, | ||||
|             handstart,  | ||||
|             importtime, | ||||
|             seats, | ||||
|             maxseats, | ||||
|             boardcard1,  | ||||
|             boardcard2,  | ||||
|             boardcard3,  | ||||
|             boardcard4,  | ||||
|             boardcard5 | ||||
|             boardcard5, | ||||
|             street1Pot, | ||||
|             street2Pot, | ||||
|             street3Pot, | ||||
|             street4Pot, | ||||
|             showdownPot | ||||
|              )  | ||||
|              VALUES  | ||||
|               (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""" | ||||
|               (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, | ||||
|                %s, %s, %s, %s, %s, %s, %s)""" | ||||
| #---            texture, | ||||
| #--            playersVpi, | ||||
| #--            playersAtStreet1,  | ||||
|  | @ -1148,27 +1172,25 @@ class Database: | |||
| #--            street2Raises, | ||||
| #--            street3Raises, | ||||
| #--            street4Raises, | ||||
| #--            street1Pot, | ||||
| #--            street2Pot, | ||||
| #--            street3Pot, | ||||
| #--            street4Pot, | ||||
| #--            showdownPot | ||||
| #--            seats,  | ||||
| 
 | ||||
|         q = q.replace('%s', self.sql.query['placeholder']) | ||||
|         print "DEBUG: p: %s" %p | ||||
|         print "DEBUG: gtid: %s" % p['gameTypeId'] | ||||
|         self.cursor.execute(q, ( | ||||
|                 p['tableName'],  | ||||
|                 p['gameTypeId'],  | ||||
|                 p['siteHandNo'],  | ||||
|                 p['gametypeid'],  | ||||
|                 p['handStart'],  | ||||
|                 datetime.today(), #importtime | ||||
| #                len(p['names']), #seats | ||||
|                 p['maxSeats'], | ||||
|                 p['seats'], | ||||
|                 p['boardcard1'],  | ||||
|                 p['boardcard2'],  | ||||
|                 p['boardcard3'],  | ||||
|                 p['boardcard4'],  | ||||
|                 p['boardcard5']) | ||||
|                 p['boardcard5'], | ||||
| #                hudCache['playersVpi'],  | ||||
| #                hudCache['playersAtStreet1'],  | ||||
| #                hudCache['playersAtStreet2'], | ||||
|  | @ -1180,12 +1202,12 @@ class Database: | |||
| #                hudCache['street2Raises'], | ||||
| #                hudCache['street3Raises'],  | ||||
| #                hudCache['street4Raises'],  | ||||
| #                hudCache['street1Pot'], | ||||
| #                hudCache['street2Pot'],  | ||||
| #                hudCache['street3Pot'], | ||||
| #                hudCache['street4Pot'], | ||||
| #                hudCache['showdownPot'] | ||||
|         ) | ||||
|                 p['street1Pot'], | ||||
|                 p['street2Pot'], | ||||
|                 p['street3Pot'], | ||||
|                 p['street4Pot'], | ||||
|                 p['showdownPot'] | ||||
|         )) | ||||
|         #return getLastInsertId(backend, conn, cursor) | ||||
|     # def storeHand | ||||
| 
 | ||||
|  |  | |||
|  | @ -26,6 +26,11 @@ from HandHistoryConverter import * | |||
| 
 | ||||
| class Everleaf(HandHistoryConverter): | ||||
|      | ||||
|     sitename = 'Everleaf' | ||||
|     filetype = "text" | ||||
|     codepage = "cp1252" | ||||
|     siteId   = 3 # Needs to match id entry in Sites database | ||||
| 
 | ||||
|     # Static regexes | ||||
|     re_SplitHands  = re.compile(r"\n\n\n+") | ||||
|     re_TailSplitHands  = re.compile(r"(\n\n\n+)") | ||||
|  | @ -37,24 +42,6 @@ class Everleaf(HandHistoryConverter): | |||
|     re_Board       = re.compile(ur"\[ (?P<CARDS>.+) \]") | ||||
|      | ||||
|      | ||||
|     def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True, debugging=False, index=0): | ||||
|         """\ | ||||
| in_path   (default '-' = sys.stdin) | ||||
| out_path  (default '-' = sys.stdout) | ||||
| follow :  whether to tail -f the input | ||||
| autostart: whether to run the thread (or you can call start() yourself) | ||||
| debugging: if False, pass on partially supported game types. If true, have a go and error...""" | ||||
|         #print "DEBUG: XXXXXXXXXXXXXXX" | ||||
|         HandHistoryConverter.__init__(self, in_path, out_path, sitename="Everleaf", follow=follow, index=index) | ||||
|         logging.info("Initialising Everleaf converter class") | ||||
|         self.filetype = "text" | ||||
|         self.codepage = "cp1252" | ||||
|         self.siteId   = 3 # Needs to match id entry in Sites database | ||||
|         self.debugging = debugging | ||||
|         if autostart: | ||||
|             self.start() | ||||
|             # otherwise you need to call start yourself. | ||||
| 
 | ||||
|     def compilePlayerRegexs(self, hand): | ||||
|         players = set([player[1] for player in hand.players]) | ||||
|         if not players <= self.compiledPlayers: # x <= y means 'x is subset of y' | ||||
|  |  | |||
|  | @ -1 +1,4 @@ | |||
| class FpdbParseError(Exception): pass | ||||
| class FpdbParseError(Exception):  | ||||
|     def __init__(self,hid=None): | ||||
|         self.hid = hid | ||||
| 
 | ||||
|  |  | |||
|  | @ -27,8 +27,14 @@ from HandHistoryConverter import * | |||
| 
 | ||||
| class Fulltilt(HandHistoryConverter): | ||||
|      | ||||
|     sitename = "Fulltilt" | ||||
|     filetype = "text" | ||||
|     codepage = "cp1252" | ||||
|     siteId   = 1 # Needs to match id entry in Sites database | ||||
| 
 | ||||
|     # Static regexes | ||||
|     re_GameInfo     = re.compile('''(?:(?P<TOURNAMENT>.+)\s\((?P<TOURNO>\d+)\),\s)? | ||||
|     re_GameInfo     = re.compile('''.*\#(?P<HID>[0-9]+):\s | ||||
|                                     (?:(?P<TOURNAMENT>.+)\s\((?P<TOURNO>\d+)\),\s)? | ||||
|                                     .+ | ||||
|                                     -\s(?P<CURRENCY>\$|)? | ||||
|                                     (?P<SB>[.0-9]+)/ | ||||
|  | @ -39,7 +45,7 @@ class Fulltilt(HandHistoryConverter): | |||
|                                  ''', re.VERBOSE) | ||||
|     re_SplitHands   = re.compile(r"\n\n+") | ||||
|     re_TailSplitHands   = re.compile(r"(\n\n+)") | ||||
|     re_HandInfo     = re.compile('''.*\#(?P<HID>[0-9]+):\s | ||||
|     re_HandInfo     = re.compile(r'''.*\#(?P<HID>[0-9]+):\s | ||||
|                                     (?:(?P<TOURNAMENT>.+)\s\((?P<TOURNO>\d+)\),\s)? | ||||
|                                     Table\s | ||||
|                                     (?P<PLAY>Play\sChip\s|PC)? | ||||
|  | @ -47,8 +53,9 @@ class Fulltilt(HandHistoryConverter): | |||
|                                     (\((?P<TABLEATTRIBUTES>.+)\)\s)?-\s | ||||
|                                     \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+)\s(Ante\s\$?(?P<ANTE>[.0-9]+)\s)?-\s | ||||
|                                     (?P<GAMETYPE>[a-zA-Z\/\'\s]+)\s-\s | ||||
|                                     (?P<DATETIME>.*) | ||||
|                                  ''', re.VERBOSE) | ||||
|                                     (?P<DATETIME>.*?)\n | ||||
|                                     (?:.*?\n(?P<CANCELLED>Hand\s\#(?P=HID)\shas\sbeen\scanceled))? | ||||
|                                  ''', re.VERBOSE|re.DOTALL) | ||||
|     re_Button       = re.compile('^The button is in seat #(?P<BUTTON>\d+)', re.MULTILINE) | ||||
|     re_PlayerInfo   = re.compile('Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \(\$?(?P<CASH>[,.0-9]+)\)$', re.MULTILINE) | ||||
|     re_Board        = re.compile(r"\[(?P<CARDS>.+)\]") | ||||
|  | @ -60,20 +67,6 @@ class Fulltilt(HandHistoryConverter): | |||
| 
 | ||||
|     mixes = { 'HORSE': 'horse', '7-Game': '7game', 'HOSE': 'hose', 'HA': 'ha'} | ||||
| 
 | ||||
|     def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True, index=0): | ||||
|         """\ | ||||
| in_path   (default '-' = sys.stdin) | ||||
| out_path  (default '-' = sys.stdout) | ||||
| follow :  whether to tail -f the input""" | ||||
|         HandHistoryConverter.__init__(self, in_path, out_path, sitename="Fulltilt", follow=follow, index=index) | ||||
|         logging.info("Initialising Fulltilt converter class") | ||||
|         self.filetype = "text" | ||||
|         self.codepage = "cp1252" | ||||
|         self.siteId   = 1 # Needs to match id entry in Sites database | ||||
|         if autostart: | ||||
|             self.start() | ||||
| 
 | ||||
| 
 | ||||
|     def compilePlayerRegexs(self,  hand): | ||||
|         players = set([player[1] for player in hand.players]) | ||||
|         if not players <= self.compiledPlayers: # x <= y means 'x is subset of y' | ||||
|  | @ -118,7 +111,7 @@ follow :  whether to tail -f the input""" | |||
|         if not m:  | ||||
|             return None | ||||
|         mg = m.groupdict() | ||||
| 
 | ||||
|         print mg | ||||
|         # translations from captured groups to our info strings | ||||
|         limits = { 'No Limit':'nl', 'Pot Limit':'pl', 'Limit':'fl' } | ||||
|         games = {              # base, category | ||||
|  | @ -140,7 +133,7 @@ follow :  whether to tail -f the input""" | |||
|         if mg['TOURNO'] == None:  info['type'] = "ring" | ||||
|         else:                     info['type'] = "tour" | ||||
|         # NB: SB, BB must be interpreted as blinds or bets depending on limit type. | ||||
|         if info['type'] == "tour": return None # importer is screwed on tournies, pass on those hands so we don't interrupt other autoimporting | ||||
| #        if info['type'] == "tour": return None # importer is screwed on tournies, pass on those hands so we don't interrupt other autoimporting | ||||
|         return info | ||||
| 
 | ||||
|     #Following function is a hack, we should be dealing with this in readFile (i think correct codepage....) | ||||
|  | @ -158,11 +151,11 @@ follow :  whether to tail -f the input""" | |||
|         self.obs = self.obs.replace('\r\n', '\n') | ||||
|         if self.obs == "" or self.obs == None: | ||||
|             logging.info("Read no hands.") | ||||
|             return | ||||
|             return [] | ||||
|         return re.split(self.re_SplitHands,  self.obs) | ||||
| 
 | ||||
|     def readHandInfo(self, hand): | ||||
|         m =  self.re_HandInfo.search(hand.handText,re.DOTALL) | ||||
|         m =  self.re_HandInfo.search(hand.handText) | ||||
|         if(m == None): | ||||
|             logging.info("Didn't match re_HandInfo") | ||||
|             logging.info(hand.handText) | ||||
|  | @ -170,6 +163,10 @@ follow :  whether to tail -f the input""" | |||
|         hand.handid = m.group('HID') | ||||
|         hand.tablename = m.group('TABLE') | ||||
|         hand.starttime = datetime.datetime.strptime(m.group('DATETIME'), "%H:%M:%S ET - %Y/%m/%d") | ||||
| 
 | ||||
|         if m.group("CANCELLED"): | ||||
|             raise FpdbParseError(hid=m.group('HID')) | ||||
| 
 | ||||
|         if m.group('TABLEATTRIBUTES'): | ||||
|             m2 = self.re_Max.search(m.group('TABLEATTRIBUTES')) | ||||
|             if m2: hand.maxseats = int(m2.group('MAX')) | ||||
|  | @ -369,7 +366,4 @@ if __name__ == "__main__": | |||
| 
 | ||||
|     (options, args) = parser.parse_args() | ||||
| 
 | ||||
|     LOG_FILENAME = './logging.out' | ||||
|     logging.basicConfig(filename=LOG_FILENAME,level=options.verbosity) | ||||
| 
 | ||||
|     e = Fulltilt(in_path = options.ipath, out_path = options.opath, follow = options.follow) | ||||
|  |  | |||
|  | @ -252,10 +252,10 @@ | |||
| 
 | ||||
| 	    <site enabled="False" | ||||
| 	          site_name="PartyPoker" | ||||
| 	          table_finder="PartyPoker.exe" | ||||
| 	          table_finder="PartyGaming.exe" | ||||
| 	          screen_name="YOUR SCREEN NAME HERE" | ||||
| 	          site_path="" | ||||
| 	          HH_path="" | ||||
| 	          site_path="C:/Program Files/PartyGaming/PartyPoker" | ||||
| 	          HH_path="C:/Program Files/PartyGaming/PartyPoker/HandHistory/YOUR SCREEN NAME HERE/" | ||||
| 	          decoder="everleaf_decode_table" | ||||
|               converter="PartyPokerToFpdb" | ||||
| 	          supported_games="holdem"> | ||||
|  |  | |||
							
								
								
									
										134
									
								
								pyfpdb/Hand.py
									
									
									
									
									
								
							
							
						
						
									
										134
									
								
								pyfpdb/Hand.py
									
									
									
									
									
								
							|  | @ -32,6 +32,8 @@ import pprint | |||
| import DerivedStats | ||||
| import Card | ||||
| 
 | ||||
| log = logging.getLogger("parser") | ||||
| 
 | ||||
| class Hand(object): | ||||
| 
 | ||||
| ###############################################################3 | ||||
|  | @ -162,7 +164,7 @@ shown   whether they were revealed at showdown | |||
| mucked  whether they were mucked at showdown | ||||
| dealt   whether they were seen in a 'dealt to' line | ||||
| """ | ||||
| #        logging.debug("addHoleCards %s %s" % (open + closed, player)) | ||||
| #        log.debug("addHoleCards %s %s" % (open + closed, player)) | ||||
|         try: | ||||
|             self.checkPlayerExists(player) | ||||
|         except FpdbParseError, e: | ||||
|  | @ -187,21 +189,7 @@ db: a connected fpdb_db object""" | |||
|         sqlids = db.getSqlPlayerIDs([p[1] for p in self.players], self.siteId) | ||||
| 
 | ||||
|         #Gametypes | ||||
| 
 | ||||
|         print "DEBUG: self.gametype %s" % self.gametype | ||||
|         #Nice way to discover if the game is already listed in the db? | ||||
|         #Also this is using an old method from fpdb_simple - should probably conform to the rest of the inserts | ||||
| 
 | ||||
|         hilo = "h" | ||||
|         if self.gametype['category'] in ['studhilo', 'omahahilo']: | ||||
|             hilo = "s" | ||||
|         elif self.gametype['category'] in ['razz','27_3draw','badugi']: | ||||
|             hilo = "l" | ||||
|         #FIXME - the two zeros are small_bet and big_bet for limit | ||||
|         gtid = db.insertGameTypes( (self.siteId, self.gametype['type'], self.gametype['base'],  | ||||
|                                     self.gametype['category'], self.gametype['limitType'], hilo, | ||||
|                                     self.gametype['sb'], self.gametype['bb'], 0, 0) ) | ||||
|          | ||||
|         gtid = db.getGameTypeId(self.siteId, self.gametype) | ||||
| 
 | ||||
|         # HudCache data to come from DerivedStats class | ||||
|         # HandsActions - all actions for all players for all streets - self.actions | ||||
|  | @ -210,14 +198,11 @@ db: a connected fpdb_db object""" | |||
|         hh = {} | ||||
|         hh['siteHandNo'] =  self.handid | ||||
|         hh['handStart'] = self.starttime | ||||
|         hh['gameTypeId'] = gtid | ||||
|         # seats TINYINT NOT NULL, | ||||
|         hh['tableName'] = self.tablename | ||||
|         hh['maxSeats'] = self.maxseats | ||||
|              # boardcard1 smallint,  /* 0=none, 1-13=2-Ah 14-26=2-Ad 27-39=2-Ac 40-52=2-As */ | ||||
|              # boardcard2 smallint, | ||||
|              # boardcard3 smallint, | ||||
|              # boardcard4 smallint, | ||||
|              # boardcard5 smallint, | ||||
|         hh['seats'] = len(sqlids) | ||||
|         # Flop turn and river may all be empty - add (likely) too many elements and trim with range | ||||
|         boardcards = self.board['FLOP'] + self.board['TURN'] + self.board['RIVER'] + [u'0x', u'0x', u'0x', u'0x', u'0x'] | ||||
|         cards = [Card.encodeCard(c) for c in boardcards[0:5]] | ||||
|  | @ -227,7 +212,6 @@ db: a connected fpdb_db object""" | |||
|         hh['boardcard4'] = cards[3] | ||||
|         hh['boardcard5'] = cards[4] | ||||
| 
 | ||||
|         print hh | ||||
|              # texture smallint, | ||||
|              # playersVpi SMALLINT NOT NULL,         /* num of players vpi */ | ||||
|                 # Needs to be recorded | ||||
|  | @ -251,17 +235,14 @@ db: a connected fpdb_db object""" | |||
|                 # Needs to be recorded | ||||
|              # street4Raises TINYINT NOT NULL, /* num big bets paid to see showdown */ | ||||
|                 # Needs to be recorded | ||||
|              # street1Pot INT,                  /* pot size at flop/street4 */ | ||||
|                 # Needs to be recorded | ||||
|              # street2Pot INT,                  /* pot size at turn/street5 */ | ||||
|                 # Needs to be recorded | ||||
|              # street3Pot INT,                  /* pot size at river/street6 */ | ||||
|                 # Needs to be recorded | ||||
|              # street4Pot INT,                  /* pot size at sd/street7 */ | ||||
|                 # Needs to be recorded | ||||
|              # showdownPot INT,                 /* pot size at sd/street7 */ | ||||
| 
 | ||||
|         #print "DEBUG: self.getStreetTotals = (%s, %s, %s, %s, %s)" %  self.getStreetTotals() | ||||
|         #FIXME: Pot size still in decimal, needs to be converted to cents | ||||
|         (hh['street1Pot'], hh['street2Pot'], hh['street3Pot'], hh['street4Pot'], hh['showdownPot']) = self.getStreetTotals() | ||||
| 
 | ||||
|              # comment TEXT, | ||||
|              # commentTs DATETIME | ||||
|         #print hh | ||||
|         handid = db.storeHand(hh) | ||||
|         # HandsPlayers - ? ... Do we fix winnings? | ||||
|         # Tourneys ? | ||||
|  | @ -282,7 +263,7 @@ seat    (int) indicating the seat | |||
| name    (string) player name | ||||
| chips   (string) the chips the player has at the start of the hand (can be None) | ||||
| If a player has None chips he won't be added.""" | ||||
|         logging.debug("addPlayer: %s %s (%s)" % (seat, name, chips)) | ||||
|         log.debug("addPlayer: %s %s (%s)" % (seat, name, chips)) | ||||
|         if chips is not None: | ||||
|             chips = re.sub(u',', u'', chips) #some sites have commas | ||||
|             self.players.append([seat, name, chips]) | ||||
|  | @ -298,9 +279,9 @@ If a player has None chips he won't be added.""" | |||
|         # go through m and initialise actions to empty list for each street. | ||||
|         if match: | ||||
|             self.streets.update(match.groupdict()) | ||||
|             logging.debug("markStreets:\n"+ str(self.streets)) | ||||
|             log.debug("markStreets:\n"+ str(self.streets)) | ||||
|         else: | ||||
|             logging.error("markstreets didn't match") | ||||
|             log.error("markstreets didn't match") | ||||
| 
 | ||||
|     def checkPlayerExists(self,player): | ||||
|         if player not in [p[1] for p in self.players]: | ||||
|  | @ -310,7 +291,7 @@ If a player has None chips he won't be added.""" | |||
| 
 | ||||
| 
 | ||||
|     def setCommunityCards(self, street, cards): | ||||
|         logging.debug("setCommunityCards %s %s" %(street,  cards)) | ||||
|         log.debug("setCommunityCards %s %s" %(street,  cards)) | ||||
|         self.board[street] = [self.card(c) for c in cards] | ||||
| #        print "DEBUG: self.board: %s" % self.board | ||||
| 
 | ||||
|  | @ -321,7 +302,7 @@ If a player has None chips he won't be added.""" | |||
|         return c | ||||
| 
 | ||||
|     def addAnte(self, player, ante): | ||||
|         logging.debug("%s %s antes %s" % ('ANTES', player, ante)) | ||||
|         log.debug("%s %s antes %s" % ('ANTES', player, ante)) | ||||
|         if player is not None: | ||||
|             ante = re.sub(u',', u'', ante) #some sites have commas | ||||
|             self.bets['ANTES'][player].append(Decimal(ante)) | ||||
|  | @ -340,7 +321,7 @@ If a player has None chips he won't be added.""" | |||
|         #   - this is a call of 1 sb and a raise to 1 bb | ||||
|         # | ||||
| 
 | ||||
|         logging.debug("addBlind: %s posts %s, %s" % (player, blindtype, amount)) | ||||
|         log.debug("addBlind: %s posts %s, %s" % (player, blindtype, amount)) | ||||
|         if player is not None: | ||||
|             amount = re.sub(u',', u'', amount) #some sites have commas | ||||
|             self.bets['PREFLOP'][player].append(Decimal(amount)) | ||||
|  | @ -362,7 +343,7 @@ If a player has None chips he won't be added.""" | |||
|     def addCall(self, street, player=None, amount=None): | ||||
|         if amount: | ||||
|             amount = re.sub(u',', u'', amount) #some sites have commas | ||||
|         logging.debug("%s %s calls %s" %(street, player, amount)) | ||||
|         log.debug("%s %s calls %s" %(street, player, amount)) | ||||
|         # Potentially calculate the amount of the call if not supplied | ||||
|         # corner cases include if player would be all in | ||||
|         if amount is not None: | ||||
|  | @ -432,7 +413,7 @@ Add a raise on [street] by [player] to [amountTo] | |||
|         self._addRaise(street, player, C, Rb, Rt) | ||||
| 
 | ||||
|     def _addRaise(self, street, player, C, Rb, Rt): | ||||
|         logging.debug("%s %s raise %s" %(street, player, Rt)) | ||||
|         log.debug("%s %s raise %s" %(street, player, Rt)) | ||||
|         self.bets[street][player].append(C + Rb) | ||||
|         self.stacks[player] -= (C + Rb) | ||||
|         act = (player, 'raises', Rb, Rt, C, self.stacks[player]==0) | ||||
|  | @ -443,7 +424,7 @@ Add a raise on [street] by [player] to [amountTo] | |||
|          | ||||
|          | ||||
|     def addBet(self, street, player, amount): | ||||
|         logging.debug("%s %s bets %s" %(street, player, amount)) | ||||
|         log.debug("%s %s bets %s" %(street, player, amount)) | ||||
|         amount = re.sub(u',', u'', amount) #some sites have commas | ||||
|         self.checkPlayerExists(player) | ||||
|         self.bets[street][player].append(Decimal(amount)) | ||||
|  | @ -462,7 +443,7 @@ Add a raise on [street] by [player] to [amountTo] | |||
|          | ||||
| 
 | ||||
|     def addFold(self, street, player): | ||||
|         logging.debug("%s %s folds" % (street, player)) | ||||
|         log.debug("%s %s folds" % (street, player)) | ||||
|         self.checkPlayerExists(player) | ||||
|         self.folded.add(player) | ||||
|         self.pot.addFold(player) | ||||
|  | @ -477,7 +458,7 @@ Add a raise on [street] by [player] to [amountTo] | |||
| 
 | ||||
| 
 | ||||
|     def addCollectPot(self,player, pot): | ||||
|         logging.debug("%s collected %s" % (player, pot)) | ||||
|         log.debug("%s collected %s" % (player, pot)) | ||||
|         self.checkPlayerExists(player) | ||||
|         self.collected = self.collected + [[player, pot]] | ||||
|         if player not in self.collectees: | ||||
|  | @ -491,7 +472,7 @@ Add a raise on [street] by [player] to [amountTo] | |||
| For when a player shows cards for any reason (for showdown or out of choice). | ||||
| Card ranks will be uppercased | ||||
| """ | ||||
|         logging.debug("addShownCards %s hole=%s all=%s" % (player, cards,  holeandboard)) | ||||
|         log.debug("addShownCards %s hole=%s all=%s" % (player, cards,  holeandboard)) | ||||
|         if cards is not None: | ||||
|             self.addHoleCards(cards,player,shown, mucked) | ||||
|         elif holeandboard is not None: | ||||
|  | @ -499,7 +480,6 @@ Card ranks will be uppercased | |||
|             board = set([c for s in self.board.values() for c in s]) | ||||
|             self.addHoleCards(holeandboard.difference(board),player,shown, mucked) | ||||
| 
 | ||||
| 
 | ||||
|     def totalPot(self): | ||||
|         """If all bets and blinds have been added, totals up the total pot size""" | ||||
|          | ||||
|  | @ -541,7 +521,7 @@ Map the tuple self.gametype onto the pokerstars string describing it | |||
|               "cp"  : "Cap Pot Limit" | ||||
|              } | ||||
| 
 | ||||
|         logging.debug("gametype: %s" %(self.gametype)) | ||||
|         log.debug("gametype: %s" %(self.gametype)) | ||||
|         retstring = "%s %s" %(gs[self.gametype['category']], ls[self.gametype['limitType']]) | ||||
|         return retstring | ||||
| 
 | ||||
|  | @ -583,6 +563,9 @@ Map the tuple self.gametype onto the pokerstars string describing it | |||
|         """Return a string of the stakes of the current hand.""" | ||||
|         return "%s%s/%s%s" % (self.sym, self.sb, self.sym, self.bb) | ||||
| 
 | ||||
|     def getStreetTotals(self): | ||||
|         pass | ||||
| 
 | ||||
|     def writeGameLine(self): | ||||
|         """Return the first HH line for the current hand.""" | ||||
|         gs = "PokerStars Game #%s: " % self.handid | ||||
|  | @ -620,7 +603,7 @@ class HoldemOmahaHand(Hand): | |||
|     def __init__(self, hhc, sitename, gametype, handText, builtFrom = "HHC", handid=None): | ||||
|         if gametype['base'] != 'hold': | ||||
|             pass # or indeed don't pass and complain instead | ||||
|         logging.debug("HoldemOmahaHand") | ||||
|         log.debug("HoldemOmahaHand") | ||||
|         self.allStreets = ['BLINDSANTES', 'PREFLOP','FLOP','TURN','RIVER'] | ||||
|         self.holeStreets = ['PREFLOP'] | ||||
|         self.communityStreets = ['FLOP', 'TURN', 'RIVER'] | ||||
|  | @ -648,6 +631,7 @@ class HoldemOmahaHand(Hand): | |||
|             for street in self.actionStreets: | ||||
|                 if self.streets[street]: | ||||
|                     hhc.readAction(self, street) | ||||
|                     self.pot.markTotal(street) | ||||
|             hhc.readCollectPot(self) | ||||
|             hhc.readShownCards(self) | ||||
|             self.totalPot() # finalise it (total the pot) | ||||
|  | @ -659,9 +643,9 @@ class HoldemOmahaHand(Hand): | |||
|             if handid is not None: | ||||
|                 self.select(handid) # Will need a handId | ||||
|             else: | ||||
|                 logging.warning("HoldemOmahaHand.__init__:Can't assemble hand from db without a handid") | ||||
|                 log.warning("HoldemOmahaHand.__init__:Can't assemble hand from db without a handid") | ||||
|         else: | ||||
|             logging.warning("HoldemOmahaHand.__init__:Neither HHC nor DB+handid provided") | ||||
|             log.warning("HoldemOmahaHand.__init__:Neither HHC nor DB+handid provided") | ||||
|             pass | ||||
|                  | ||||
| 
 | ||||
|  | @ -672,6 +656,18 @@ class HoldemOmahaHand(Hand): | |||
|         else: | ||||
|             self.addHoleCards('PREFLOP', player, open=[], closed=cards, shown=shown, mucked=mucked, dealt=dealt) | ||||
| 
 | ||||
|     def getStreetTotals(self): | ||||
|         # street1Pot INT,                  /* pot size at flop/street4 */ | ||||
|         # street2Pot INT,                  /* pot size at turn/street5 */ | ||||
|         # street3Pot INT,                  /* pot size at river/street6 */ | ||||
|         # street4Pot INT,                  /* pot size at sd/street7 */ | ||||
|         # showdownPot INT,                 /* pot size at sd/street7 */ | ||||
|         tmp1 = self.pot.getTotalAtStreet('FLOP') | ||||
|         tmp2 = self.pot.getTotalAtStreet('TURN') | ||||
|         tmp3 = self.pot.getTotalAtStreet('RIVER') | ||||
|         tmp4 = 0 | ||||
|         tmp5 = 0 | ||||
|         return (tmp1,tmp2,tmp3,tmp4,tmp5) | ||||
| 
 | ||||
|     def writeHTMLHand(self, fh=sys.__stdout__): | ||||
|         from nevow import tags as T | ||||
|  | @ -773,7 +769,7 @@ class HoldemOmahaHand(Hand): | |||
|         super(HoldemOmahaHand, self).writeHand(fh) | ||||
| 
 | ||||
|         players_who_act_preflop = set(([x[0] for x in self.actions['PREFLOP']]+[x[0] for x in self.actions['BLINDSANTES']])) | ||||
|         logging.debug(self.actions['PREFLOP']) | ||||
|         log.debug(self.actions['PREFLOP']) | ||||
|         for player in [x for x in self.players if x[1] in players_who_act_preflop]: | ||||
|             #Only print stacks of players who do something preflop | ||||
|             print >>fh, ("Seat %s: %s ($%s in chips) " %(player[0], player[1], player[2])) | ||||
|  | @ -896,6 +892,7 @@ class DrawHand(Hand): | |||
|             for street in self.streetList: | ||||
|                 if self.streets[street]: | ||||
|                     hhc.readAction(self, street) | ||||
|                     self.pot.markTotal(street) | ||||
|             hhc.readCollectPot(self) | ||||
|             hhc.readShownCards(self) | ||||
|             self.totalPot() # finalise it (total the pot) | ||||
|  | @ -916,7 +913,7 @@ class DrawHand(Hand): | |||
|         #   - this is a call of 1 sb and a raise to 1 bb | ||||
|         #  | ||||
|          | ||||
|         logging.debug("addBlind: %s posts %s, %s" % (player, blindtype, amount)) | ||||
|         log.debug("addBlind: %s posts %s, %s" % (player, blindtype, amount)) | ||||
|         if player is not None: | ||||
|             self.bets['DEAL'][player].append(Decimal(amount)) | ||||
|             self.stacks[player] -= Decimal(amount) | ||||
|  | @ -943,7 +940,7 @@ class DrawHand(Hand): | |||
| 
 | ||||
| 
 | ||||
|     def discardDrawHoleCards(self, cards, player, street): | ||||
|         logging.debug("discardDrawHoleCards '%s' '%s' '%s'" % (cards, player, street)) | ||||
|         log.debug("discardDrawHoleCards '%s' '%s' '%s'" % (cards, player, street)) | ||||
|         self.discards[street][player] = set([cards]) | ||||
| 
 | ||||
| 
 | ||||
|  | @ -956,6 +953,14 @@ class DrawHand(Hand): | |||
|             act = (player, 'discards', num) | ||||
|         self.actions[street].append(act) | ||||
| 
 | ||||
|     def getStreetTotals(self): | ||||
|         # street1Pot INT,                  /* pot size at flop/street4 */ | ||||
|         # street2Pot INT,                  /* pot size at turn/street5 */ | ||||
|         # street3Pot INT,                  /* pot size at river/street6 */ | ||||
|         # street4Pot INT,                  /* pot size at sd/street7 */ | ||||
|         # showdownPot INT,                 /* pot size at sd/street7 */ | ||||
|         return (0,0,0,0,0) | ||||
| 
 | ||||
| 
 | ||||
|     def writeHand(self, fh=sys.__stdout__): | ||||
|         # PokerStars format. | ||||
|  | @ -1061,9 +1066,9 @@ class StudHand(Hand): | |||
|             for street in self.actionStreets: | ||||
|                 if street == 'ANTES': continue # OMG--sometime someone folds in the ante round | ||||
|                 if self.streets[street]: | ||||
|                     logging.debug(street) | ||||
|                     logging.debug(self.streets[street]) | ||||
|                     log.debug(street + self.streets[street]) | ||||
|                     hhc.readAction(self, street) | ||||
|                     self.pot.markTotal(street) | ||||
|             hhc.readCollectPot(self) | ||||
|             hhc.readShownCards(self) # not done yet | ||||
|             self.totalPot() # finalise it (total the pot) | ||||
|  | @ -1094,7 +1099,7 @@ street  (string) the street name (in streetList) | |||
| open  list of card bigrams e.g. ['2h','Jc'], dealt face up | ||||
| closed    likewise, but known only to player | ||||
| """ | ||||
|         logging.debug("addPlayerCards %s, o%s x%s" % (player,  open, closed)) | ||||
|         log.debug("addPlayerCards %s, o%s x%s" % (player,  open, closed)) | ||||
|         try: | ||||
|             self.checkPlayerExists(player) | ||||
|             self.holecards[street][player] = (open, closed) | ||||
|  | @ -1108,7 +1113,7 @@ closed    likewise, but known only to player | |||
|         """\ | ||||
| Add a complete on [street] by [player] to [amountTo] | ||||
| """ | ||||
|         logging.debug("%s %s completes %s" % (street, player, amountTo)) | ||||
|         log.debug("%s %s completes %s" % (street, player, amountTo)) | ||||
|         amountTo = re.sub(u',', u'', amountTo) #some sites have commas | ||||
|         self.checkPlayerExists(player) | ||||
|         Bp = self.lastBet['THIRD'] | ||||
|  | @ -1126,7 +1131,7 @@ Add a complete on [street] by [player] to [amountTo] | |||
|          | ||||
|     def addBringIn(self, player, bringin): | ||||
|         if player is not None: | ||||
|             logging.debug("Bringin: %s, %s" % (player , bringin)) | ||||
|             log.debug("Bringin: %s, %s" % (player , bringin)) | ||||
|             self.bets['THIRD'][player].append(Decimal(bringin)) | ||||
|             self.stacks[player] -= Decimal(bringin) | ||||
|             act = (player, 'bringin', bringin, self.stacks[player]==0) | ||||
|  | @ -1134,6 +1139,14 @@ Add a complete on [street] by [player] to [amountTo] | |||
|             self.lastBet['THIRD'] = Decimal(bringin) | ||||
|             self.pot.addMoney(player, Decimal(bringin)) | ||||
| 
 | ||||
|     def getStreetTotals(self): | ||||
|         # street1Pot INT,                  /* pot size at flop/street4 */ | ||||
|         # street2Pot INT,                  /* pot size at turn/street5 */ | ||||
|         # street3Pot INT,                  /* pot size at river/street6 */ | ||||
|         # street4Pot INT,                  /* pot size at sd/street7 */ | ||||
|         # showdownPot INT,                 /* pot size at sd/street7 */ | ||||
|         return (0,0,0,0,0) | ||||
| 
 | ||||
|      | ||||
|     def writeHand(self, fh=sys.__stdout__): | ||||
|         # PokerStars format. | ||||
|  | @ -1294,6 +1307,7 @@ class Pot(object): | |||
|     def __init__(self): | ||||
|         self.contenders   = set() | ||||
|         self.committed    = {} | ||||
|         self.streettotals = {} | ||||
|         self.total        = None | ||||
|         self.returned     = {} | ||||
|         self.sym          = u'$' # this is the default currency symbol | ||||
|  | @ -1313,6 +1327,14 @@ class Pot(object): | |||
|         self.contenders.add(player) | ||||
|         self.committed[player] += amount | ||||
| 
 | ||||
|     def markTotal(self, street): | ||||
|         self.streettotals[street] = sum(self.committed.values()) | ||||
| 
 | ||||
|     def getTotalAtStreet(self, street): | ||||
|         if street in self.streettotals: | ||||
|             return self.streettotals[street] | ||||
|         return 0 | ||||
| 
 | ||||
|     def end(self): | ||||
|         self.total = sum(self.committed.values()) | ||||
|          | ||||
|  |  | |||
|  | @ -20,7 +20,6 @@ import Hand | |||
| import re | ||||
| import sys | ||||
| import traceback | ||||
| import logging | ||||
| from optparse import OptionParser | ||||
| import os | ||||
| import os.path | ||||
|  | @ -31,19 +30,37 @@ import operator | |||
| from xml.dom.minidom import Node | ||||
| import time | ||||
| import datetime | ||||
| from Exceptions import FpdbParseError | ||||
| 
 | ||||
| import gettext | ||||
| gettext.install('fpdb') | ||||
| 
 | ||||
| import logging, logging.config | ||||
| logging.config.fileConfig("logging.conf") | ||||
| log = logging.getLogger("parser") | ||||
| 
 | ||||
| class HandHistoryConverter(): | ||||
| 
 | ||||
|     READ_CHUNK_SIZE = 10000 # bytes to read at a time from file (in tail mode) | ||||
|     def __init__(self, in_path = '-', out_path = '-', sitename = None, follow=False, index=0): | ||||
|         logging.info("HandHistory init") | ||||
|     READ_CHUNK_SIZE = 10000 # bytes to read at a time from file in tail mode | ||||
| 
 | ||||
|     # filetype can be "text" or "xml" | ||||
|     # so far always "text" | ||||
|     # subclass HHC_xml for xml parsing | ||||
|     filetype = "text" | ||||
| 
 | ||||
|     # codepage indicates the encoding of the text file. | ||||
|     # cp1252 is a safe default | ||||
|     # "utf_8" is more likely if there are funny characters | ||||
|     codepage = "cp1252" | ||||
| 
 | ||||
|     def __init__(self, in_path = '-', out_path = '-', follow=False, index=0, autostart=True): | ||||
|         """\ | ||||
| in_path   (default '-' = sys.stdin) | ||||
| out_path  (default '-' = sys.stdout) | ||||
| follow :  whether to tail -f the input""" | ||||
| 
 | ||||
|         log.info("HandHistory init - %s subclass, in_path '%s'; out_path '%s'" % (self.sitename, in_path, out_path) ) | ||||
|          | ||||
|         # default filetype and codepage. Subclasses should set these properly. | ||||
|         self.filetype  = "text" | ||||
|         self.codepage  = "utf8" | ||||
|         self.index     = 0 | ||||
| 
 | ||||
|         self.in_path = in_path | ||||
|  | @ -60,52 +77,70 @@ class HandHistoryConverter(): | |||
|             # TODO: out_path should be sanity checked. | ||||
|             out_dir = os.path.dirname(self.out_path) | ||||
|             if not os.path.isdir(out_dir) and out_dir != '': | ||||
|                 logging.info("Creatin directory '%s'" % out_dir) | ||||
|                 log.info("Creating directory '%s'" % out_dir) | ||||
|                 os.makedirs(out_dir) | ||||
|             try: | ||||
|                 self.out_fh = codecs.open(self.out_path, 'w', 'cp1252') | ||||
|                 log.debug("out_path %s opened as %s" % (self.out_path, self.out_fh)) | ||||
|             except: | ||||
|                 log.error("out_path %s couldn't be opened" % (self.out_path)) | ||||
| 
 | ||||
|         self.sitename  = sitename | ||||
|         self.follow = follow | ||||
|         self.compiledPlayers   = set() | ||||
|         self.maxseats  = 10 | ||||
| 
 | ||||
|         if autostart: | ||||
|             self.start() | ||||
| 
 | ||||
|     def __str__(self): | ||||
|         return """ | ||||
| HandHistoryConverter: '%(sitename)s' | ||||
|     filetype:   '%(filetype)s' | ||||
|     in_path:    '%(in_path)s' | ||||
|     out_path:   '%(out_path)s' | ||||
|     """ % { 'sitename':self.sitename, 'filetype':self.filetype, 'in_path':self.in_path, 'out_path':self.out_path } | ||||
|     filetype    '%(filetype)s' | ||||
|     in_path     '%(in_path)s' | ||||
|     out_path    '%(out_path)s' | ||||
|     follow      '%(follow)s' | ||||
|     """ %  locals()  | ||||
| 
 | ||||
|     def start(self): | ||||
|         """process a hand at a time from the input specified by in_path. | ||||
|         """Process a hand at a time from the input specified by in_path. | ||||
| If in follow mode, wait for more data to turn up. | ||||
| Otherwise, finish at eof. | ||||
| Otherwise, finish at EOF. | ||||
| 
 | ||||
| """ | ||||
|         starttime = time.time() | ||||
|         if not self.sanityCheck(): | ||||
|             print "Cowardly refusing to continue after failed sanity check" | ||||
|             log.warning("Failed sanity check") | ||||
|             return | ||||
| 
 | ||||
|         if self.follow: | ||||
|         try: | ||||
|             numHands = 0 | ||||
|             numErrors = 0 | ||||
|             if self.follow: | ||||
|                 log.info("Tailing '%s'" % self.in_path) | ||||
|                 for handText in self.tailHands(): | ||||
|                 numHands+=1 | ||||
|                     try: | ||||
|                         self.processHand(handText) | ||||
|                         numHands+=1 | ||||
|                     except FpdbParseError, e: | ||||
|                         numErrors+=1 | ||||
|                         log.warning("Failed to convert hand %s" % e.hid) | ||||
|                         log.debug(handText) | ||||
|             else: | ||||
|                 handsList = self.allHandsAsList() | ||||
|             logging.info("Parsing %d hands" % len(handsList)) | ||||
|             nBadHangs = 0 | ||||
|                 log.info("Parsing %d hands" % len(handsList)) | ||||
|                 for handText in handsList: | ||||
|                     try: | ||||
|                         self.processedHands.append(self.processHand(handText)) | ||||
|                 except Exception, e: # TODO: it's better to replace it with s-t like HhcEception | ||||
|                     nBadHangs += 1 | ||||
|                     logging.error("Caught exception while parsing hand: %s" % str(e)) | ||||
|             numHands =  len(handsList) - nBadHangs | ||||
|                     except FpdbParseError, e: | ||||
|                         numErrors+=1 | ||||
|                         log.warning("Failed to convert hand %s" % e.hid) | ||||
|                         log.debug(handText) | ||||
|                 numHands = len(handsList) | ||||
|             endtime = time.time() | ||||
|         print "read %d hands in %.3f seconds" % (numHands, endtime - starttime) | ||||
|             log.info("Read %d hands (%d failed) in %.3f seconds" % (numHands, numErrors, endtime - starttime)) | ||||
|         except IOError, ioe: | ||||
|             log.exception("Error converting '%s'" % self.in_path) | ||||
|         finally: | ||||
|             if self.out_fh != sys.stdout: | ||||
|                 self.out_fh.close() | ||||
| 
 | ||||
|  | @ -134,7 +169,7 @@ which it expects to find at self.re_TailSplitHands -- see for e.g. Everleaf.py. | |||
|                     time.sleep(interval) | ||||
|                     fd.seek(where) | ||||
|                 else: | ||||
|                     logging.debug("%s changed inode numbers from %d to %d" % (self.in_path, fd_results[1], st_results[1])) | ||||
|                     log.debug("%s changed inode numbers from %d to %d" % (self.in_path, fd_results[1], st_results[1])) | ||||
|                     fd = codecs.open(self.in_path, 'r', self.codepage) | ||||
|                     fd.seek(where) | ||||
|             else: | ||||
|  | @ -179,13 +214,13 @@ which it expects to find at self.re_TailSplitHands -- see for e.g. Everleaf.py. | |||
|         self.obs = self.obs.strip() | ||||
|         self.obs = self.obs.replace('\r\n', '\n') | ||||
|         if self.obs == "" or self.obs == None: | ||||
|             logging.info("Read no hands.") | ||||
|             return | ||||
|             log.info("Read no hands.") | ||||
|             return [] | ||||
|         return re.split(self.re_SplitHands,  self.obs) | ||||
|          | ||||
|     def processHand(self, handText): | ||||
|         gametype = self.determineGameType(handText) | ||||
|         logging.debug("gametype %s" % gametype) | ||||
|         log.debug("gametype %s" % gametype) | ||||
|         hand = None | ||||
|         if gametype is None:  | ||||
|             l = None | ||||
|  | @ -200,14 +235,14 @@ which it expects to find at self.re_TailSplitHands -- see for e.g. Everleaf.py. | |||
|             l = [type] + [base] + [limit] | ||||
|         if l in self.readSupportedGames(): | ||||
|             if gametype['base'] == 'hold': | ||||
|                 logging.debug("hand = Hand.HoldemOmahaHand(self, self.sitename, gametype, handtext)") | ||||
|                 log.debug("hand = Hand.HoldemOmahaHand(self, self.sitename, gametype, handtext)") | ||||
|                 hand = Hand.HoldemOmahaHand(self, self.sitename, gametype, handText) | ||||
|             elif gametype['base'] == 'stud': | ||||
|                 hand = Hand.StudHand(self, self.sitename, gametype, handText) | ||||
|             elif gametype['base'] == 'draw': | ||||
|                 hand = Hand.DrawHand(self, self.sitename, gametype, handText) | ||||
|         else: | ||||
|             logging.info("Unsupported game type: %s" % gametype) | ||||
|             log.info("Unsupported game type: %s" % gametype) | ||||
| 
 | ||||
|         if hand: | ||||
| #    uncomment these to calculate some stats | ||||
|  | @ -216,7 +251,7 @@ which it expects to find at self.re_TailSplitHands -- see for e.g. Everleaf.py. | |||
|             hand.writeHand(self.out_fh) | ||||
|             return hand | ||||
|         else: | ||||
|             logging.info("Unsupported game type: %s" % gametype) | ||||
|             log.info("Unsupported game type: %s" % gametype) | ||||
|             # TODO: pity we don't know the HID at this stage. Log the entire hand? | ||||
|             # From the log we can deduce that it is the hand after the one before :) | ||||
| 
 | ||||
|  | @ -342,26 +377,23 @@ or None if we fail to get the info """ | |||
|         return hands | ||||
| 
 | ||||
|     def readFile(self): | ||||
|         """open in_path according to self.codepage""" | ||||
|         """Open in_path according to self.codepage. Exceptions caught further up""" | ||||
|          | ||||
|         if(self.filetype == "text"): | ||||
|             if self.in_path == '-': | ||||
|                 # read from stdin | ||||
|                 logging.debug("Reading stdin with %s" % self.codepage) # is this necessary? or possible? or what? | ||||
|                 log.debug("Reading stdin with %s" % self.codepage) # is this necessary? or possible? or what? | ||||
|                 in_fh = codecs.getreader('cp1252')(sys.stdin) | ||||
|             else: | ||||
|                 logging.debug("Opening %s with %s" % (self.in_path, self.codepage)) | ||||
|                 in_fh = codecs.open(self.in_path, 'r', self.codepage) | ||||
|                 in_fh.seek(self.index) | ||||
|                 log.debug("Opened in_path: '%s' with %s" % (self.in_path, self.codepage)) | ||||
|                 self.obs = in_fh.read() | ||||
|                 self.index = in_fh.tell() | ||||
|                 in_fh.close() | ||||
|         elif(self.filetype == "xml"): | ||||
|             try: | ||||
|             doc = xml.dom.minidom.parse(filename) | ||||
|             self.doc = doc | ||||
|             except: | ||||
|                 traceback.print_exc(file=sys.stderr) | ||||
| 
 | ||||
|     def guessMaxSeats(self, hand): | ||||
|         """Return a guess at max_seats when not specified in HH.""" | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| #!/usr/bin/env python | ||||
| # -*- coding: utf-8 -*- | ||||
| """Hud.py | ||||
| 
 | ||||
| Create and manage the hud overlays. | ||||
|  | @ -96,6 +97,8 @@ class Hud: | |||
|                     continue | ||||
|                 self.aux_windows.append(my_import(self, config, aux_params)) | ||||
|          | ||||
|         self.creation_attrs = None | ||||
| 
 | ||||
|     def create_mw(self): | ||||
| 
 | ||||
| #	Set up a main window for this this instance of the HUD | ||||
|  | @ -146,6 +149,21 @@ class Hud: | |||
|         self.item4.connect("activate", self.debug_stat_windows) | ||||
|         self.item4.show() | ||||
|          | ||||
|         self.item5 = gtk.MenuItem('Set max seats') | ||||
|         self.menu.append(self.item5) | ||||
|         self.item5.show() | ||||
|         self.maxSeatsMenu = gtk.Menu() | ||||
|         self.item5.set_submenu(self.maxSeatsMenu) | ||||
|         for i in range(2, 11, 1): | ||||
|             item = gtk.MenuItem('%d-max' % i) | ||||
|             item.ms = i | ||||
|             self.maxSeatsMenu.append(item) | ||||
|             item.connect("activate", self.change_max_seats) | ||||
|             item.show() | ||||
|             setattr(self, 'maxSeatsMenuItem%d' % (i-1), item)  | ||||
|          | ||||
|              | ||||
|          | ||||
|         self.ebox.connect_object("button-press-event", self.on_button_press, self.menu) | ||||
| 
 | ||||
|         self.main_window.show_all() | ||||
|  | @ -163,6 +181,18 @@ class Hud: | |||
|              | ||||
|         self.update_table_position() | ||||
|             | ||||
|     def change_max_seats(self, widget): | ||||
|         if self.max != widget.ms: | ||||
|             print 'change_max_seats', widget.ms | ||||
|             self.max = widget.ms | ||||
|             try: | ||||
|                 self.kill() | ||||
|                 self.create(*self.creation_attrs) | ||||
|                 self.update(self.hand, self.config) | ||||
|             except Exception, e: | ||||
|                 print "Expcetion:",str(e) | ||||
|                 pass | ||||
|          | ||||
|     def update_table_position(self): | ||||
|         if os.name == 'nt': | ||||
|             if not win32gui.IsWindow(self.table.number): | ||||
|  | @ -270,6 +300,8 @@ class Hud: | |||
| # | ||||
| #    this method also manages the creating and destruction of stat | ||||
| #    windows via calls to the Stat_Window class | ||||
|         self.creation_attrs = hand, config, stat_dict, cards  | ||||
| 
 | ||||
|         self.hand = hand   | ||||
|         if not self.mw_created: | ||||
|             self.create_mw() | ||||
|  |  | |||
							
								
								
									
										257
									
								
								pyfpdb/PartyPokerToFpdb.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										257
									
								
								pyfpdb/PartyPokerToFpdb.py
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							|  | @ -21,43 +21,48 @@ | |||
| import sys | ||||
| from collections import defaultdict | ||||
| 
 | ||||
| from Exceptions import FpdbParseError | ||||
| from HandHistoryConverter import * | ||||
| 
 | ||||
| # PartyPoker HH Format | ||||
| 
 | ||||
| class PartyPoker(HandHistoryConverter): | ||||
|     class ParsingException(Exception): | ||||
|         "Usage: raise ParsingException(<msg>[, hh=<hh>])" | ||||
|         def __init__(self, *args, **kwargs): | ||||
|             if len(args)==0: args=[''] + list(args) | ||||
|             msg, args = args[0], args[1:] | ||||
|             if 'hh' in kwargs: | ||||
|                 msg += self.wrapHh(kwargs['hh']) | ||||
|                 del kwargs['hh'] | ||||
|             return Exception.__init__(self, msg, *args, **kwargs) | ||||
| class PartyPokerParseError(FpdbParseError): | ||||
|     "Usage: raise PartyPokerParseError(<msg>[, hh=<hh>][, hid=<hid>])" | ||||
|     def __init__(self, msg='', hh=None, hid=None): | ||||
|         if hh is not None: | ||||
|             msg += "\n\nHand history attached below:\n" + self.wrapHh(hh) | ||||
|         return super(PartyPokerParseError, self).__init__(hid=hid) | ||||
|         #return super(PartyPokerParseError, self).__init__(msg, hid=hid) | ||||
|     def wrapHh(self, hh): | ||||
|             return ("\n\nHand history attached below:\n" | ||||
|                     "%(DELIMETER)s\n%(HH)s\n%(DELIMETER)s") % \ | ||||
|         return ("%(DELIMETER)s\n%(HH)s\n%(DELIMETER)s") % \ | ||||
|                 {'DELIMETER': '#'*50, 'HH': hh} | ||||
| 
 | ||||
| class PartyPoker(HandHistoryConverter): | ||||
| 
 | ||||
| ############################################################ | ||||
| #    Class Variables | ||||
| 
 | ||||
|     sitename = "PartyPoker" | ||||
|     codepage = "cp1252" | ||||
|     siteId = 9 # TODO: automate; it's a class variable so shouldn't hit DB too often | ||||
|     filetype = "text" # "text" or "xml". I propose we subclass HHC to HHC_Text and HHC_XML. | ||||
| 
 | ||||
| 
 | ||||
|     sym = {'USD': "\$", } | ||||
| 
 | ||||
|     # Static regexes | ||||
|     # $5 USD NL Texas Hold'em - Saturday, July 25, 07:53:52 EDT 2009 | ||||
|     # NL Texas Hold'em $1 USD Buy-in Trny:45685440 Level:8  Blinds-Antes(600/1 200 -50) - Sunday, May 17, 11:25:07 MSKS 2009 | ||||
|     re_GameInfoRing     = re.compile(""" | ||||
|             (?P<CURRENCY>\$|)\s*(?P<RINGLIMIT>\d+)\s*(?:USD)?\s* | ||||
|             (?P<LIMIT>(NL))\s+ | ||||
|             (?P<GAME>(Texas\ Hold\'em)) | ||||
|             (?P<CURRENCY>\$|)\s*(?P<RINGLIMIT>[0-9,]+)\s*(?:USD)?\s* | ||||
|             (?P<LIMIT>(NL|PL|))\s+ | ||||
|             (?P<GAME>(Texas\ Hold\'em|Omaha)) | ||||
|             \s*\-\s* | ||||
|             (?P<DATETIME>.+) | ||||
|             """, re.VERBOSE) | ||||
|     re_GameInfoTrny     = re.compile(""" | ||||
|             (?P<LIMIT>(NL))\s+ | ||||
|             (?P<GAME>(Texas\ Hold\'em))\s+ | ||||
|             (?P<LIMIT>(NL|PL|))\s+ | ||||
|             (?P<GAME>(Texas\ Hold\'em|Omaha))\s+ | ||||
|             (?P<BUYIN>\$?[.0-9]+)\s*(?P<BUYIN_CURRENCY>USD)?\s*Buy-in\s+ | ||||
|             Trny:\s?(?P<TOURNO>\d+)\s+ | ||||
|             Level:\s*(?P<LEVEL>\d+)\s+ | ||||
|  | @ -88,52 +93,58 @@ class PartyPoker(HandHistoryConverter): | |||
|             """, | ||||
|           re.MULTILINE|re.VERBOSE) | ||||
| 
 | ||||
|     re_TotalPlayers = re.compile("^Total\s+number\s+of\s+players\s*:\s*(?P<MAXSEATS>\d+)", re.MULTILINE) | ||||
|     re_SplitHands   = re.compile('\x00+') | ||||
|     re_TailSplitHands   = re.compile('(\x00+)') | ||||
|     lineSplitter    = '\n' | ||||
|     re_Button       = re.compile('Seat (?P<BUTTON>\d+) is the button', re.MULTILINE) | ||||
|     re_Board        = re.compile(r"\[(?P<CARDS>.+)\]") | ||||
|     re_NoSmallBlind = re.compile('^There is no Small Blind in this hand as the Big Blind of the previous hand left the table') | ||||
|     re_NoSmallBlind = re.compile( | ||||
|                     '^There is no Small Blind in this hand as the Big Blind ' | ||||
|                     'of the previous hand left the table', re.MULTILINE) | ||||
| 
 | ||||
| 
 | ||||
|     def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True, index=0): | ||||
|         """\ | ||||
| in_path   (default '-' = sys.stdin) | ||||
| out_path  (default '-' = sys.stdout) | ||||
| follow :  whether to tail -f the input""" | ||||
|         HandHistoryConverter.__init__(self, in_path, out_path, sitename="PartyPoker", follow=follow, index=index) | ||||
|         logging.info("Initialising PartyPoker converter class") | ||||
|         self.filetype = "text" | ||||
|         self.codepage = "cp1252" # FIXME: wtf? | ||||
|         self.siteId   = 9 # Needs to match id entry in Sites database | ||||
|         self._gameType = None # cached reg-parse result | ||||
|         if autostart:  | ||||
|             self.start() | ||||
| 
 | ||||
|     def allHandsAsList(self): | ||||
|         list = HandHistoryConverter.allHandsAsList(self) | ||||
|         if list is None: | ||||
|             return [] | ||||
|         return filter(lambda text: len(text.strip()), list) | ||||
| 
 | ||||
|     def guessMaxSeats(self, hand): | ||||
|         """Return a guess at max_seats when not specified in HH.""" | ||||
|         mo = self.maxOccSeat(hand) | ||||
| 
 | ||||
|         if mo == 10: return mo | ||||
|         if mo == 2: return 2 | ||||
|         if mo <= 6: return 6 | ||||
|         # there are 9-max tables for cash and 10-max for tournaments | ||||
|         return 9 if hand.gametype['type']=='ring' else 10 | ||||
| 
 | ||||
|     def compilePlayerRegexs(self,  hand): | ||||
|         players = set([player[1] for player in hand.players]) | ||||
|         if not players <= self.compiledPlayers: # x <= y means 'x is subset of y' | ||||
| 
 | ||||
|             self.compiledPlayers = players | ||||
|             player_re = "(?P<PNAME>" + "|".join(map(re.escape, players)) + ")" | ||||
|             subst = {'PLYR': player_re, 'CUR_SYM': hand.SYMBOL[hand.gametype['currency']], | ||||
|                 'CUR': hand.gametype['currency'] if hand.gametype['currency']!='T$' else ''} | ||||
|             for key in ('CUR_SYM', 'CUR'): | ||||
|                 subst[key] = re.escape(subst[key]) | ||||
|             logging.debug("player_re: " + subst['PLYR']) | ||||
|             logging.debug("CUR_SYM: " + subst['CUR_SYM']) | ||||
|             logging.debug("CUR: " + subst['CUR']) | ||||
|             log.debug("player_re: '%s'" % subst['PLYR']) | ||||
|             log.debug("CUR_SYM: '%s'" % subst['CUR_SYM']) | ||||
|             log.debug("CUR: '%s'" % subst['CUR']) | ||||
|             self.re_PostSB = re.compile( | ||||
|                 r"^%(PLYR)s posts small blind \[%(CUR_SYM)s(?P<SB>[.0-9]+) ?%(CUR)s\]\." %  subst, | ||||
|                 re.MULTILINE) | ||||
|             self.re_PostBB = re.compile( | ||||
|                 r"^%(PLYR)s posts big blind \[%(CUR_SYM)s(?P<BB>[.0-9]+) ?%(CUR)s\]\." %  subst, | ||||
|                 re.MULTILINE) | ||||
|             # NOTE: comma is used as a fraction part delimeter in re below | ||||
|             self.re_PostDead = re.compile( | ||||
|                 r"^%(PLYR)s posts big blind \+ dead \[(?P<BBNDEAD>[.,0-9]+) ?%(CUR_SYM)s\]\." %  subst, | ||||
|                 re.MULTILINE) | ||||
|             self.re_Antes = re.compile( | ||||
|                 r"^%(PLYR)s posts ante \[%(CUR_SYM)s(?P<ANTE>[.0-9]+) ?%(CUR)s\]\." %  subst, | ||||
|                 r"^%(PLYR)s posts ante \[%(CUR_SYM)s(?P<ANTE>[.0-9]+) ?%(CUR)s\]" %  subst, | ||||
|                 re.MULTILINE) | ||||
|             self.re_HeroCards = re.compile( | ||||
|                 r"^Dealt to %(PLYR)s \[\s*(?P<NEWCARDS>.+)\s*\]" % subst, | ||||
|  | @ -148,21 +159,23 @@ follow :  whether to tail -f the input""" | |||
|                 r"\[ *(?P<CARDS>.+) *\](?P<COMBINATION>.+)\.", | ||||
|                 re.MULTILINE) | ||||
|             self.re_CollectPot = re.compile( | ||||
|                 r""""^%(PLYR)s \s+ wins \s+ | ||||
|                 %(CUR_SYM)s(?P<POT>[.\d]+)\s*%(CUR)s""" %  subst,  | ||||
|                 r"""^%(PLYR)s \s+ wins \s+ | ||||
|                 %(CUR_SYM)s(?P<POT>[.,\d]+)\s*%(CUR)s""" %  subst, | ||||
|                 re.MULTILINE|re.VERBOSE) | ||||
| 
 | ||||
|     def readSupportedGames(self): | ||||
|         return [["ring", "hold", "nl"], | ||||
|                 #["ring", "hold", "pl"], | ||||
|                 #["ring", "hold", "fl"], | ||||
|                 ["ring", "hold", "pl"], | ||||
|                 ["ring", "hold", "fl"], | ||||
| 
 | ||||
|                 ["tour", "hold", "nl"], | ||||
|                 #["tour", "hold", "pl"], | ||||
|                 #["tour", "hold", "fl"], | ||||
|                 ["tour", "hold", "pl"], | ||||
|                 ["tour", "hold", "fl"], | ||||
|                ] | ||||
| 
 | ||||
|     def _getGameType(self, handText): | ||||
|         if not hasattr(self, '_gameType'): | ||||
|             self._gameType = None | ||||
|         if self._gameType is None: | ||||
|             # let's determine whether hand is trny | ||||
|             # and whether 5-th line contains head line | ||||
|  | @ -181,7 +194,7 @@ follow :  whether to tail -f the input""" | |||
|         gametype dict is: | ||||
|         {'limitType': xxx, 'base': xxx, 'category': xxx}""" | ||||
| 
 | ||||
|         logging.debug(self.ParsingException().wrapHh( handText )) | ||||
|         log.debug(PartyPokerParseError().wrapHh( handText )) | ||||
| 
 | ||||
|         info = {} | ||||
|         m = self._getGameType(handText) | ||||
|  | @ -190,31 +203,29 @@ follow :  whether to tail -f the input""" | |||
| 
 | ||||
|         mg = m.groupdict() | ||||
|         # translations from captured groups to fpdb info strings | ||||
|         limits = { 'NL':'nl',  | ||||
| #            'Pot Limit':'pl', 'Limit':'fl'  | ||||
|             } | ||||
|         limits = { 'NL':'nl', 'PL':'pl', '':'fl' } | ||||
|         games = {                          # base, category | ||||
|                          "Texas Hold'em" : ('hold','holdem'), | ||||
|                                 #'Omaha' : ('hold','omahahi'), | ||||
|                                 'Omaha' : ('hold','omahahi'), | ||||
|                } | ||||
|         currencies = { '$':'USD', '':'T$' } | ||||
| 
 | ||||
|         for expectedField in ['LIMIT', 'GAME']: | ||||
|             if mg[expectedField] is None: | ||||
|                 raise self.ParsingException( | ||||
|                 raise PartyPokerParseError( | ||||
|                     "Cannot fetch field '%s'" % expectedField, | ||||
|                     hh = handText) | ||||
|         try: | ||||
|             info['limitType'] = limits[mg['LIMIT']] | ||||
|             info['limitType'] = limits[mg['LIMIT'].strip()] | ||||
|         except: | ||||
|             raise self.ParsingException( | ||||
|             raise PartyPokerParseError( | ||||
|                 "Unknown limit '%s'" % mg['LIMIT'], | ||||
|                 hh = handText) | ||||
| 
 | ||||
|         try: | ||||
|             (info['base'], info['category']) = games[mg['GAME']] | ||||
|         except: | ||||
|             raise self.ParsingException( | ||||
|             raise PartyPokerParseError( | ||||
|                 "Unknown game type '%s'" % mg['GAME'], | ||||
|                 hh = handText) | ||||
| 
 | ||||
|  | @ -227,10 +238,11 @@ follow :  whether to tail -f the input""" | |||
|         if info['type'] == 'ring': | ||||
|             info['sb'], info['bb'] = ringBlinds(mg['RINGLIMIT']) | ||||
|             # FIXME: there are only $ and play money availible for cash | ||||
|             # to be honest, party doesn't save play money hh | ||||
|             info['currency'] = currencies[mg['CURRENCY']] | ||||
|         else: | ||||
|             info['sb'] = renderTrnyMoney(mg['SB']) | ||||
|             info['bb'] = renderTrnyMoney(mg['BB']) | ||||
|             info['sb'] = clearMoneyString(mg['SB']) | ||||
|             info['bb'] = clearMoneyString(mg['BB']) | ||||
|             info['currency'] = 'T$' | ||||
| 
 | ||||
|         # NB: SB, BB must be interpreted as blinds or bets depending on limit type. | ||||
|  | @ -239,21 +251,48 @@ follow :  whether to tail -f the input""" | |||
| 
 | ||||
|     def readHandInfo(self, hand): | ||||
|         info = {} | ||||
|         m = self.re_HandInfo.search(hand.handText,re.DOTALL) | ||||
|         if m: | ||||
|             info.update(m.groupdict()) | ||||
|         else: | ||||
|             raise self.ParsingException("Cannot read Handinfo for current hand", hh=hand.handText) | ||||
|         m = self._getGameType(hand.handText) | ||||
|         if m: info.update(m.groupdict()) | ||||
|         m = self.re_Hid.search(hand.handText) | ||||
|         try: | ||||
|             info.update(self.re_Hid.search(hand.handText).groupdict()) | ||||
|         except: | ||||
|             raise PartyPokerParseError("Cannot read HID for current hand", hh=hand.handText) | ||||
| 
 | ||||
|         try: | ||||
|             info.update(self.re_HandInfo.search(hand.handText,re.DOTALL).groupdict()) | ||||
|         except: | ||||
|             raise PartyPokerParseError("Cannot read Handinfo for current hand", | ||||
|             hh=hand.handText, hid = info['HID']) | ||||
| 
 | ||||
|         try: | ||||
|             info.update(self._getGameType(hand.handText).groupdict()) | ||||
|         except: | ||||
|             raise PartyPokerParseError("Cannot read GameType for current hand", | ||||
|             hh=hand.handText, hid = info['HID']) | ||||
| 
 | ||||
| 
 | ||||
|         m = self.re_TotalPlayers.search(hand.handText) | ||||
|         if m: info.update(m.groupdict()) | ||||
| 
 | ||||
|         # FIXME: it's a hack cause party doesn't supply hand.maxseats info | ||||
|         #hand.maxseats = ??? | ||||
|         hand.mixed = None | ||||
| 
 | ||||
|         logging.debug("readHandInfo: %s" % info) | ||||
|         # FIXME: it's dirty hack | ||||
|         # party doesnt subtract uncalled money from commited money | ||||
|         # so hand.totalPot calculation has to be redefined | ||||
|         from Hand import Pot, HoldemOmahaHand | ||||
|         def getNewTotalPot(origTotalPot): | ||||
|             def totalPot(self): | ||||
|                 if self.totalpot is None: | ||||
|                     self.pot.end() | ||||
|                     self.totalpot = self.pot.total | ||||
|                 for i,v in enumerate(self.collected): | ||||
|                     if v[0] in self.pot.returned: | ||||
|                         self.collected[i][1] = Decimal(v[1]) - self.pot.returned[v[0]] | ||||
|                 return origTotalPot() | ||||
|             return totalPot | ||||
|         instancemethod = type(hand.totalPot) | ||||
|         hand.totalPot = instancemethod(getNewTotalPot(hand.totalPot), hand, HoldemOmahaHand) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|         log.debug("readHandInfo: %s" % info) | ||||
|         for key in info: | ||||
|             if key == 'DATETIME': | ||||
|                 #Saturday, July 25, 07:53:52 EDT 2009 | ||||
|  | @ -278,14 +317,14 @@ follow :  whether to tail -f the input""" | |||
|             if key == 'TOURNO': | ||||
|                 hand.tourNo = info[key] | ||||
|             if key == 'BUYIN': | ||||
|                 #FIXME: it's dirty hack T_T | ||||
|                 # FIXME: it's dirty hack T_T | ||||
|                 # code below assumes that rake is equal to zero | ||||
|                 cur = info[key][0] if info[key][0] not in '0123456789' else '' | ||||
|                 hand.buyin = info[key] + '+%s0' % cur | ||||
|             if key == 'LEVEL': | ||||
|                 hand.level = info[key] | ||||
|             if key == 'PLAY' and info['PLAY'] != 'Real': | ||||
|                 # TODO: play money wasn't tested | ||||
| #                hand.currency = 'play' # overrides previously set value | ||||
|                 # if realy there's no play money hh on party | ||||
|                 hand.gametype['currency'] = 'play' | ||||
| 
 | ||||
|     def readButton(self, hand): | ||||
|  | @ -293,21 +332,17 @@ follow :  whether to tail -f the input""" | |||
|         if m: | ||||
|             hand.buttonpos = int(m.group('BUTTON')) | ||||
|         else: | ||||
|             logging.info('readButton: not found') | ||||
|             log.info('readButton: not found') | ||||
| 
 | ||||
|     def readPlayerStacks(self, hand): | ||||
|         logging.debug("readPlayerStacks") | ||||
|         log.debug("readPlayerStacks") | ||||
|         m = self.re_PlayerInfo.finditer(hand.handText) | ||||
|         players = [] | ||||
|         for a in m: | ||||
|             hand.addPlayer(int(a.group('SEAT')), a.group('PNAME'), | ||||
|                            renderTrnyMoney(a.group('CASH'))) | ||||
|                            clearMoneyString(a.group('CASH'))) | ||||
| 
 | ||||
|     def markStreets(self, hand): | ||||
|         # PREFLOP = ** Dealing down cards ** | ||||
|         # This re fails if,  say, river is missing; then we don't get the ** that starts the river. | ||||
|         assert hand.gametype['base'] == "hold", \ | ||||
|             "wtf! There're no %s games on party" % hand.gametype['base'] | ||||
|         m =  re.search( | ||||
|             r"\*{2} Dealing down cards \*{2}" | ||||
|             r"(?P<PREFLOP>.+?)" | ||||
|  | @ -323,16 +358,11 @@ follow :  whether to tail -f the input""" | |||
|             hand.setCommunityCards(street, renderCards(m.group('CARDS'))) | ||||
| 
 | ||||
|     def readAntes(self, hand): | ||||
|         logging.debug("reading antes") | ||||
|         log.debug("reading antes") | ||||
|         m = self.re_Antes.finditer(hand.handText) | ||||
|         for player in m: | ||||
|             hand.addAnte(player.group('PNAME'), player.group('ANTE')) | ||||
| 
 | ||||
|     def readBringIn(self, hand): | ||||
|         m = self.re_BringIn.search(hand.handText,re.DOTALL) | ||||
|         if m: | ||||
|             hand.addBringIn(m.group('PNAME'),  m.group('BRINGIN')) | ||||
|          | ||||
|     def readBlinds(self, hand): | ||||
|         noSmallBlind = bool(self.re_NoSmallBlind.search(hand.handText)) | ||||
|         if hand.gametype['type'] == 'ring': | ||||
|  | @ -345,6 +375,10 @@ follow :  whether to tail -f the input""" | |||
| 
 | ||||
|             for a in self.re_PostBB.finditer(hand.handText): | ||||
|                 hand.addBlind(a.group('PNAME'), 'big blind', a.group('BB')) | ||||
| 
 | ||||
|             deadFilter = lambda s: s.replace(',', '.') | ||||
|             for a in self.re_PostDead.finditer(hand.handText): | ||||
|                 hand.addBlind(a.group('PNAME'), 'both', deadFilter(a.group('BBNDEAD'))) | ||||
|         else: | ||||
|             # party doesn't track blinds for tournaments | ||||
|             # so there're some cra^Wcaclulations | ||||
|  | @ -365,6 +399,7 @@ follow :  whether to tail -f the input""" | |||
| 
 | ||||
|             if noSmallBlind: | ||||
|                 hand.addBlind(None, None, None) | ||||
|                 smallBlindSeat = int(hand.buttonpos) | ||||
|             else: | ||||
|                 smallBlindSeat = findFirstNonEmptySeat(int(hand.buttonpos) + 1) | ||||
|                 blind = smartMin(hand.sb, playersMap[smallBlindSeat][1]) | ||||
|  | @ -372,7 +407,7 @@ follow :  whether to tail -f the input""" | |||
| 
 | ||||
|             bigBlindSeat = findFirstNonEmptySeat(smallBlindSeat + 1) | ||||
|             blind = smartMin(hand.bb, playersMap[bigBlindSeat][1]) | ||||
|             hand.addBlind(playersMap[bigBlindSeat][0], 'small blind', blind) | ||||
|             hand.addBlind(playersMap[bigBlindSeat][0], 'big blind', blind) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|  | @ -391,18 +426,39 @@ follow :  whether to tail -f the input""" | |||
|         m = self.re_Action.finditer(hand.streets[street]) | ||||
|         for action in m: | ||||
|             acts = action.groupdict() | ||||
|             if action.group('ATYPE') in ('raises','is all-In'): | ||||
|                 hand.addRaiseBy( street, action.group('PNAME'), action.group('BET') ) | ||||
|             elif action.group('ATYPE') == 'calls': | ||||
|                 hand.addCall( street, action.group('PNAME'), action.group('BET') ) | ||||
|             elif action.group('ATYPE') == 'bets': | ||||
|                 hand.addBet( street, action.group('PNAME'), action.group('BET') ) | ||||
|             elif action.group('ATYPE') == 'folds': | ||||
|                 hand.addFold( street, action.group('PNAME')) | ||||
|             elif action.group('ATYPE') == 'checks': | ||||
|                 hand.addCheck( street, action.group('PNAME')) | ||||
|             playerName = action.group('PNAME') | ||||
|             amount = clearMoneyString(action.group('BET')) if action.group('BET') else None | ||||
|             actionType = action.group('ATYPE') | ||||
| 
 | ||||
|             if actionType == 'is all-In': | ||||
|                 # party's allin can mean either raise or bet or call | ||||
|                 Bp = hand.lastBet[street] | ||||
|                 if Bp == 0: | ||||
|                     actionType = 'bets' | ||||
|                 elif Bp < Decimal(amount): | ||||
|                     actionType = 'raises' | ||||
|                 else: | ||||
|                 print "DEBUG: unimplemented readAction: '%s' '%s'" %(action.group('PNAME'),action.group('ATYPE'),) | ||||
|                     actionType = 'calls' | ||||
| 
 | ||||
|             if actionType == 'raises': | ||||
|                 if street == 'PREFLOP' and \ | ||||
|                     playerName in [item[0] for item in hand.actions['BLINDSANTES'] if item[2]!='ante']: | ||||
|                     # preflop raise from blind | ||||
|                     hand.addRaiseBy( street, playerName, amount ) | ||||
|                 else: | ||||
|                     hand.addCallandRaise( street, playerName, amount ) | ||||
|             elif actionType == 'calls': | ||||
|                 hand.addCall( street, playerName, amount ) | ||||
|             elif actionType == 'bets': | ||||
|                 hand.addBet( street, playerName, amount ) | ||||
|             elif actionType == 'folds': | ||||
|                 hand.addFold( street, playerName ) | ||||
|             elif actionType == 'checks': | ||||
|                 hand.addCheck( street, playerName ) | ||||
|             else: | ||||
|                 raise PartyPokerParseError( | ||||
|                     "Unimplemented readAction: '%s' '%s'" % (playerName,actionType,), | ||||
|                     hid = hand.hid, hh = hand.handText ) | ||||
| 
 | ||||
| 
 | ||||
|     def readShowdownActions(self, hand): | ||||
|  | @ -411,7 +467,7 @@ follow :  whether to tail -f the input""" | |||
| 
 | ||||
|     def readCollectPot(self,hand): | ||||
|         for m in self.re_CollectPot.finditer(hand.handText): | ||||
|             hand.addCollectPot(player=m.group('PNAME'),pot=m.group('POT')) | ||||
|             hand.addCollectPot(player=m.group('PNAME'),pot=clearMoneyString(m.group('POT'))) | ||||
| 
 | ||||
|     def readShownCards(self,hand): | ||||
|         for m in self.re_ShownCards.finditer(hand.handText): | ||||
|  | @ -425,17 +481,17 @@ follow :  whether to tail -f the input""" | |||
|                 hand.addShownCards(cards=cards, player=m.group('PNAME'), shown=shown, mucked=mucked) | ||||
| 
 | ||||
| def ringBlinds(ringLimit): | ||||
|     "Returns blinds for current limit" | ||||
|     ringLimit = float(ringLimit) | ||||
|     "Returns blinds for current limit in cash games" | ||||
|     ringLimit = float(clearMoneyString(ringLimit)) | ||||
|     if ringLimit == 5.: ringLimit = 4. | ||||
|     return ('%.2f' % (ringLimit/200.), '%.2f' % (ringLimit/100.)  ) | ||||
| 
 | ||||
| def renderTrnyMoney(money): | ||||
|     "renders 'numbers' like '1 200' and '2,000'" | ||||
| def clearMoneyString(money): | ||||
|     "Renders 'numbers' like '1 200' and '2,000'" | ||||
|     return money.replace(' ', '').replace(',', '') | ||||
| 
 | ||||
| def renderCards(string): | ||||
|     "splits strings like ' Js, 4d '" | ||||
|     "Splits strings like ' Js, 4d '" | ||||
|     cards = string.strip().split(' ') | ||||
|     return filter(len, map(lambda x: x.strip(' ,'), cards)) | ||||
| 
 | ||||
|  | @ -454,7 +510,4 @@ if __name__ == "__main__": | |||
| 
 | ||||
|     (options, args) = parser.parse_args() | ||||
| 
 | ||||
|     #LOG_FILENAME = './logging.out' | ||||
|     logging.basicConfig(level=options.verbosity) | ||||
| 
 | ||||
|     e = PartyPoker(in_path = options.ipath, out_path = options.opath, follow = options.follow) | ||||
|  |  | |||
|  | @ -19,6 +19,7 @@ | |||
| ######################################################################## | ||||
| 
 | ||||
| # TODO: straighten out discards for draw games | ||||
| 
 | ||||
| import sys | ||||
| from HandHistoryConverter import * | ||||
| 
 | ||||
|  | @ -26,8 +27,12 @@ from HandHistoryConverter import * | |||
| 
 | ||||
| class PokerStars(HandHistoryConverter): | ||||
| 
 | ||||
| ############################################################ | ||||
| #    Class Variables | ||||
|     # Class Variables | ||||
| 
 | ||||
|     sitename = "PokerStars" | ||||
|     filetype = "text" | ||||
|     codepage = "cp1252" | ||||
|     siteId   = 2 # Needs to match id entry in Sites database | ||||
| 
 | ||||
|     mixes = { 'HORSE': 'horse', '8-Game': '8game', 'HOSE': 'hose'} # Legal mixed games | ||||
|     sym = {'USD': "\$", 'CAD': "\$", 'T$': "", "EUR": "\x80", "GBP": "\xa3"}         # ADD Euro, Sterling, etc HERE | ||||
|  | @ -77,20 +82,6 @@ class PokerStars(HandHistoryConverter): | |||
| #        self.re_setHandInfoRegex('.*#(?P<HID>[0-9]+): Table (?P<TABLE>[ a-zA-Z]+) - \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) - (?P<GAMETYPE>.*) - (?P<HR>[0-9]+):(?P<MIN>[0-9]+) ET - (?P<YEAR>[0-9]+)/(?P<MON>[0-9]+)/(?P<DAY>[0-9]+)Table (?P<TABLE>[ a-zA-Z]+)\nSeat (?P<BUTTON>[0-9]+)')     | ||||
| 
 | ||||
| 
 | ||||
|     def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True, index=0): | ||||
|         """\ | ||||
| in_path   (default '-' = sys.stdin) | ||||
| out_path  (default '-' = sys.stdout) | ||||
| follow :  whether to tail -f the input""" | ||||
|         HandHistoryConverter.__init__(self, in_path, out_path, sitename="PokerStars", follow=follow, index=index) | ||||
|         logging.info("Initialising PokerStars converter class") | ||||
|         self.filetype = "text" | ||||
|         self.codepage = "cp1252" | ||||
|         self.siteId   = 2 # Needs to match id entry in Sites database | ||||
|         if autostart: | ||||
|             self.start() | ||||
| 
 | ||||
| 
 | ||||
|     def compilePlayerRegexs(self,  hand): | ||||
|         players = set([player[1] for player in hand.players]) | ||||
|         if not players <= self.compiledPlayers: # x <= y means 'x is subset of y' | ||||
|  | @ -388,7 +379,4 @@ if __name__ == "__main__": | |||
| 
 | ||||
|     (options, args) = parser.parse_args() | ||||
| 
 | ||||
|     LOG_FILENAME = './logging.out' | ||||
|     logging.basicConfig(filename=LOG_FILENAME,level=options.verbosity) | ||||
| 
 | ||||
|     e = PokerStars(in_path = options.ipath, out_path = options.opath, follow = options.follow) | ||||
|  |  | |||
|  | @ -230,11 +230,20 @@ def discover_nt_by_name(c, tablename): | |||
|     """Finds poker client window with the given table name.""" | ||||
|     titles = {} | ||||
|     win32gui.EnumWindows(win_enum_handler, titles) | ||||
|      | ||||
|     def getDefaultEncoding(): | ||||
|         # FIXME: if somebody know better place fot this function - move it | ||||
|         # FIXME: it's better to use GetCPInfo for windows http://msdn.microsoft.com/en-us/library/dd318078(VS.85).aspx | ||||
|         # but i have no idea, how to call it | ||||
|         import locale | ||||
|         return locale.getpreferredencoding() | ||||
|          | ||||
|     for hwnd in titles: | ||||
|         #print "Tables.py: tablename =", tablename, "title =", titles[hwnd] | ||||
|         try: | ||||
|             # maybe it's better to make global titles[hwnd] decoding? | ||||
|             # this can blow up in XP on some windows, eg firefox displaying http://docs.python.org/tutorial/classes.html | ||||
|             if not tablename.lower() in titles[hwnd].lower(): continue | ||||
|             if not tablename.lower() in titles[hwnd].decode(getDefaultEncoding()).lower(): continue | ||||
|         except: | ||||
|             continue | ||||
|         if 'History for table:' in titles[hwnd]: continue # Everleaf Network HH viewer window | ||||
|  |  | |||
|  | @ -26,6 +26,11 @@ from HandHistoryConverter import * | |||
| 
 | ||||
| class Win2day(HandHistoryConverter): | ||||
| 
 | ||||
|     sitename = "Win2day" | ||||
|     filetype = "text" | ||||
|     codepage = "cp1252" | ||||
|     siteID   = 4 | ||||
| 
 | ||||
|     # Static regexes | ||||
|     #<HISTORY ID="102271403" SESSION="session31237702.xml" TABLE="Innsbruck 3" GAME="GAME_THM" GAMETYPE="GAMETYPE_REAL" GAMEKIND="GAMEKIND_CASH" TABLECURRENCY="EUR" LIMIT="NL" STAKES="0.25/0.50" DATE="1246909773" WIN="0.00" LOSS="0.50"> | ||||
|      | ||||
|  | @ -39,15 +44,6 @@ class Win2day(HandHistoryConverter): | |||
|     re_Card        = re.compile('^<CARD LINK="(?P<CARD>[0-9]+)"></CARD>', re.MULTILINE) | ||||
|     re_BoardLast    = re.compile('^<CARD LINK="(?P<CARD>[0-9]+)"></CARD></ACTION>', re.MULTILINE) | ||||
|      | ||||
|     def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True, index=0): | ||||
|         HandHistoryConverter.__init__(self, in_path, out_path, sitename="Win2day", follow=follow, index=index) | ||||
|         logging.info("Initialising Win2day converter class") | ||||
|         self.filetype = "text" | ||||
|         self.codepage = "cp1252" | ||||
|         self.sideID   = 4 | ||||
|         if autostart: | ||||
|             self.start() | ||||
| 
 | ||||
| 
 | ||||
|     def compilePlayerRegexs(self,  hand): | ||||
|         players = set([player[1] for player in hand.players]) | ||||
|  | @ -65,7 +61,7 @@ class Win2day(HandHistoryConverter): | |||
|             self.re_PostBoth         = re.compile(r'^<ACTION TYPE="HAND_BLINDS" PLAYER="%s" KIND="HAND_AB" VALUE="(?P<SBBB>[.0-9]+)"></ACTION>' %  player_re, re.MULTILINE) | ||||
|      | ||||
|             #r'<ACTION TYPE="HAND_DEAL" PLAYER="%s">\n<CARD LINK="(?P<CARD1>[0-9]+)"></CARD>\n<CARD LINK="(?P<CARD2>[0-9]+)"></CARD></ACTION>' | ||||
|             self.re_HeroCards        = re.compile(r'<ACTION TYPE="HAND_DEAL" PLAYER="%s">\n(?P<CARDS><CARD LINK="[0-9]+"></CARD>\n<CARD LINK="[0-9]"></CARD>)</ACTION>' % player_re, re.MULTILINE) | ||||
|             self.re_HeroCards        = re.compile(r'<ACTION TYPE="HAND_DEAL" PLAYER="%s">\n(?P<CARDS><CARD LINK="[0-9]+"></CARD>\n<CARD LINK="[0-9]+"></CARD>)</ACTION>' % player_re, re.MULTILINE) | ||||
|              | ||||
|             #'^<ACTION TYPE="(?P<ATYPE>[_A-Z]+)" PLAYER="%s"( VALUE="(?P<BET>[.0-9]+)")?></ACTION>' | ||||
|             self.re_Action           = re.compile(r'^<ACTION TYPE="(?P<ATYPE>[_A-Z]+)" PLAYER="%s"( VALUE="(?P<BET>[.0-9]+)")?></ACTION>' %  player_re, re.MULTILINE) | ||||
|  |  | |||
							
								
								
									
										40
									
								
								pyfpdb/logging.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								pyfpdb/logging.conf
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,40 @@ | |||
| [loggers] | ||||
| keys=root,parser | ||||
| 
 | ||||
| [handlers] | ||||
| keys=consoleHandler,fileHandler | ||||
| 
 | ||||
| [formatters] | ||||
| keys=fileFormatter,stderrFormatter | ||||
| 
 | ||||
| [logger_root] | ||||
| level=INFO | ||||
| handlers=consoleHandler,fileHandler | ||||
| 
 | ||||
| [logger_parser] | ||||
| level=INFO | ||||
| # set to NOTSET or DEBUG to see everything the parser does | ||||
| handlers=consoleHandler,fileHandler | ||||
| qualname=parser | ||||
| propagate=0 | ||||
| 
 | ||||
| [handler_consoleHandler] | ||||
| class=StreamHandler | ||||
| level=INFO | ||||
| formatter=stderrFormatter | ||||
| args=(sys.stderr,) | ||||
| 
 | ||||
| [handler_fileHandler] | ||||
| class=FileHandler | ||||
| level=INFO | ||||
| formatter=fileFormatter | ||||
| args=('logging.out', 'a') | ||||
| 
 | ||||
| 
 | ||||
| [formatter_fileFormatter] | ||||
| format=%(asctime)s - %(name)-12s %(levelname)-8s %(message)s | ||||
| datefmt= | ||||
| 
 | ||||
| [formatter_stderrFormatter] | ||||
| format=%(name)-12s: %(levelname)-8s %(message)s | ||||
| datefmt= | ||||
|  | @ -5,6 +5,11 @@ import py | |||
| # regression-test-files/fulltilt/nlhe/NLHE-6max-1.txt | ||||
| #   Sorrowful: start: $8.85 end: $14.70 total: $5.85  | ||||
|    | ||||
| # 'Canceled' hand | ||||
| # regression-test-files/fulltilt/lh/Marlin.txt | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| def checkGameInfo(hhc, header, info): | ||||
|     assert hhc.determineGameType(header) == info | ||||
| 
 | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ text = "" | |||
| 
 | ||||
| hhc = PokerStarsToFpdb.PokerStars(autostart=False) | ||||
| 
 | ||||
| h = HoldemOmahaHand(None, "ASite", gametype, text, builtFrom = "Test") | ||||
| h = HoldemOmahaHand(None, "PokerStars", gametype, text, builtFrom = "Test") | ||||
| h.addPlayer("1", "s0rrow", "100000") | ||||
| 
 | ||||
| hhc.compilePlayerRegexs(h) | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user