Merge branch 'master' of git://git.assembla.com/fpdboz
This commit is contained in:
commit
6357f22f11
78
pyfpdb/AbsoluteToFpdb.py
Normal file → Executable file
78
pyfpdb/AbsoluteToFpdb.py
Normal file → Executable file
|
@ -26,6 +26,13 @@ from HandHistoryConverter import *
|
||||||
# Class for converting Absolute HH format.
|
# Class for converting Absolute HH format.
|
||||||
|
|
||||||
class Absolute(HandHistoryConverter):
|
class Absolute(HandHistoryConverter):
|
||||||
|
|
||||||
|
# Class Variables
|
||||||
|
sitename = "Absolute"
|
||||||
|
filetype = "text"
|
||||||
|
codepage = "cp1252"
|
||||||
|
siteid = 8
|
||||||
|
HORSEHand = False
|
||||||
|
|
||||||
# Static regexes
|
# Static regexes
|
||||||
re_SplitHands = re.compile(r"\n\n\n+")
|
re_SplitHands = re.compile(r"\n\n\n+")
|
||||||
|
@ -35,8 +42,11 @@ class Absolute(HandHistoryConverter):
|
||||||
#Seat 6 - FETS63 ($0.75 in chips)
|
#Seat 6 - FETS63 ($0.75 in chips)
|
||||||
#Board [10s 5d Kh Qh 8c]
|
#Board [10s 5d Kh Qh 8c]
|
||||||
|
|
||||||
re_GameInfo = re.compile(ur"^Stage #([0-9]+): (?P<GAME>Holdem|) (?P<LIMIT>No Limit|) (?P<CURRENCY>\$| €|)(?P<BB>[0-9]*[.0-9]+)", re.MULTILINE)
|
re_GameInfo = re.compile(ur"^Stage #([0-9]+): (?P<GAME>Holdem|HORSE)(?: \(1 on 1\)|)? ?(?P<LIMIT>No Limit|Pot Limit|Normal|)? ?(?P<CURRENCY>\$| €|)(?P<SB>[.0-9]+)/?(?:\$| €|)(?P<BB>[.0-9]+)?", re.MULTILINE)
|
||||||
re_HandInfo = re.compile(ur"^Stage #(?P<HID>[0-9]+): .*(?P<DATETIME>\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d).*\nTable: (?P<TABLE>.*) \(Real Money\)", re.MULTILINE)
|
re_HorseGameInfo = re.compile(ur"^Game Type: (?P<LIMIT>Limit) (?P<GAME>Holdem)", re.MULTILINE)
|
||||||
|
# TODO: can set max seats via (1 on 1) to a known 2 ..
|
||||||
|
re_HandInfo = re.compile(ur"^Stage #(?P<HID>[0-9]+): .*(?P<DATETIME>\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d).*\n(Table: (?P<TABLE>.*) \(Real Money\))?", re.MULTILINE)
|
||||||
|
re_TableFromFilename = re.compile(ur".*IHH([0-9]+) (?P<TABLE>.*) -") # on HORSE STUD games, the table name isn't in the hand info!
|
||||||
re_Button = re.compile(ur"Seat #(?P<BUTTON>[0-9]) is the ?[dead]* dealer$", re.MULTILINE) # TODO: that's not the right way to match for "dead" dealer is it?
|
re_Button = re.compile(ur"Seat #(?P<BUTTON>[0-9]) is the ?[dead]* dealer$", re.MULTILINE) # TODO: that's not the right way to match for "dead" dealer is it?
|
||||||
re_PlayerInfo = re.compile(ur"^Seat (?P<SEAT>[0-9]) - (?P<PNAME>.*) \((?:\$| €|)(?P<CASH>[0-9]*[.0-9]+) in chips\)", re.MULTILINE)
|
re_PlayerInfo = re.compile(ur"^Seat (?P<SEAT>[0-9]) - (?P<PNAME>.*) \((?:\$| €|)(?P<CASH>[0-9]*[.0-9]+) in chips\)", re.MULTILINE)
|
||||||
re_Board = re.compile(ur"\[(?P<CARDS>[^\]]*)\]? *$", re.MULTILINE)
|
re_Board = re.compile(ur"\[(?P<CARDS>[^\]]*)\]? *$", re.MULTILINE)
|
||||||
|
@ -48,24 +58,6 @@ class Absolute(HandHistoryConverter):
|
||||||
# re_Board = re.compile(ur"\[ (?P<CARDS>.+) \]")
|
# 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 = 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):
|
def compilePlayerRegexs(self, hand):
|
||||||
players = set([player[1] for player in hand.players])
|
players = set([player[1] for player in hand.players])
|
||||||
if not players <= self.compiledPlayers: # x <= y means 'x is subset of y'
|
if not players <= self.compiledPlayers: # x <= y means 'x is subset of y'
|
||||||
|
@ -79,13 +71,13 @@ debugging: if False, pass on partially supported game types. If true, have a go
|
||||||
# TODO: Absolute posting when coming in new: %s - Posts $0.02 .. should that be a new Post line? where do we need to add support for that? *confused*
|
# TODO: Absolute posting when coming in new: %s - Posts $0.02 .. should that be a new Post line? where do we need to add support for that? *confused*
|
||||||
self.re_PostBoth = re.compile(ur"^%s - Posts dead (?:\$| €|)(?P<SBBB>[0-9]*[.0-9]+)" % player_re, re.MULTILINE)
|
self.re_PostBoth = re.compile(ur"^%s - Posts dead (?:\$| €|)(?P<SBBB>[0-9]*[.0-9]+)" % player_re, re.MULTILINE)
|
||||||
self.re_Action = re.compile(ur"^%s - (?P<ATYPE>Bets |Raises |All-In |All-In\(Raise\) |Calls |Folds|Checks)?\$?(?P<BET>[0-9]*[.0-9]+)?" % player_re, re.MULTILINE)
|
self.re_Action = re.compile(ur"^%s - (?P<ATYPE>Bets |Raises |All-In |All-In\(Raise\) |Calls |Folds|Checks)?\$?(?P<BET>[0-9]*[.0-9]+)?" % player_re, re.MULTILINE)
|
||||||
print "^%s - (?P<ATYPE>Bets |Raises |All-In |All-In\(Raise\) |Calls |Folds|Checks)?\$?(?P<BET>[0-9]*[.0-9]+)?" % player_re
|
# print "^%s - (?P<ATYPE>Bets |Raises |All-In |All-In\(Raise\) |Calls |Folds|Checks)?\$?(?P<BET>[0-9]*[.0-9]+)?" % player_re
|
||||||
self.re_ShowdownAction = re.compile(ur"^%s - Shows \[(?P<CARDS>.*)\]" % player_re, re.MULTILINE)
|
self.re_ShowdownAction = re.compile(ur"^%s - Shows \[(?P<CARDS>.*)\]" % player_re, re.MULTILINE)
|
||||||
self.re_CollectPot = re.compile(ur"^Seat [0-9]: %s(?: \(dealer\)| \(big blind\)| \(small blind\)|) (?:won|collected) Total \((?:\$| €|)(?P<POT>[0-9]*[.0-9]+)\)" % player_re, re.MULTILINE)
|
self.re_CollectPot = re.compile(ur"^Seat [0-9]: %s(?: \(dealer\)|)(?: \(big blind\)| \(small blind\)|) (?:won|collected) Total \((?:\$| €|)(?P<POT>[0-9]*[.0-9]+)\)" % player_re, re.MULTILINE)
|
||||||
#self.re_PostSB = re.compile(ur"^%s: posts small blind \[(?:\$| €|) (?P<SB>[.0-9]+)" % player_re, re.MULTILINE)
|
#self.re_PostSB = re.compile(ur"^%s: posts small blind \[(?:\$| €|) (?P<SB>[.0-9]+)" % player_re, re.MULTILINE)
|
||||||
#self.re_PostBB = re.compile(ur"^%s: posts big blind \[(?:\$| €|) (?P<BB>[.0-9]+)" % player_re, re.MULTILINE)
|
#self.re_PostBB = re.compile(ur"^%s: posts big blind \[(?:\$| €|) (?P<BB>[.0-9]+)" % player_re, re.MULTILINE)
|
||||||
#self.re_PostBoth = re.compile(ur"^%s: posts both blinds \[(?:\$| €|) (?P<SBBB>[.0-9]+)" % player_re, re.MULTILINE)
|
#self.re_PostBoth = re.compile(ur"^%s: posts both blinds \[(?:\$| €|) (?P<SBBB>[.0-9]+)" % player_re, re.MULTILINE)
|
||||||
#self.re_Antes = re.compile(ur"^%s: posts ante \[(?:\$| €|) (?P<ANTE>[.0-9]+)" % player_re, re.MULTILINE)
|
self.re_Antes = re.compile(ur"^%s - Ante \[(?:\$| €|)(?P<ANTE>[.0-9]+)" % player_re, re.MULTILINE)
|
||||||
#self.re_BringIn = re.compile(ur"^%s posts bring-in (?:\$| €|)(?P<BRINGIN>[.0-9]+)\." % player_re, re.MULTILINE)
|
#self.re_BringIn = re.compile(ur"^%s posts bring-in (?:\$| €|)(?P<BRINGIN>[.0-9]+)\." % player_re, re.MULTILINE)
|
||||||
self.re_HeroCards = re.compile(ur"^Dealt to %s \[(?P<CARDS>.*)\]" % player_re, re.MULTILINE)
|
self.re_HeroCards = re.compile(ur"^Dealt to %s \[(?P<CARDS>.*)\]" % player_re, re.MULTILINE)
|
||||||
#self.re_Action = re.compile(ur"^%s(?P<ATYPE>: bets| checks| raises| calls| folds)(\s\[(?:\$| €|) (?P<BET>[.\d]+) (USD|EUR|)\])?" % player_re, re.MULTILINE)
|
#self.re_Action = re.compile(ur"^%s(?P<ATYPE>: bets| checks| raises| calls| folds)(\s\[(?:\$| €|) (?P<BET>[.\d]+) (USD|EUR|)\])?" % player_re, re.MULTILINE)
|
||||||
|
@ -124,7 +116,7 @@ or None if we fail to get the info """
|
||||||
mg = m.groupdict()
|
mg = m.groupdict()
|
||||||
|
|
||||||
# translations from captured groups to our info strings
|
# translations from captured groups to our info strings
|
||||||
limits = { 'No Limit':'nl', 'PL':'pl', '':'fl' }
|
limits = { 'No Limit':'nl', 'Pot Limit':'pl', 'Normal':'fl', 'Limit':'fl'}
|
||||||
games = { # base, category
|
games = { # base, category
|
||||||
"Holdem" : ('hold','holdem'),
|
"Holdem" : ('hold','holdem'),
|
||||||
'Omaha' : ('hold','omahahi'),
|
'Omaha' : ('hold','omahahi'),
|
||||||
|
@ -132,14 +124,26 @@ or None if we fail to get the info """
|
||||||
'7 Card Stud' : ('stud','studhi')
|
'7 Card Stud' : ('stud','studhi')
|
||||||
}
|
}
|
||||||
currencies = { u' €':'EUR', '$':'USD', '':'T$' }
|
currencies = { u' €':'EUR', '$':'USD', '':'T$' }
|
||||||
if 'LIMIT' in mg:
|
if 'GAME' in mg and mg['GAME'] == "HORSE": # if we're a HORSE game, the game type is on the next line
|
||||||
info['limitType'] = limits[mg['LIMIT']]
|
self.HORSEHand = True
|
||||||
|
m = self.re_HorseGameInfo.search(handText)
|
||||||
|
if not m:
|
||||||
|
return None # it's a HORSE game and we don't understand the game type
|
||||||
|
temp = m.groupdict()
|
||||||
|
#print "AP HORSE processing"
|
||||||
|
if 'GAME' not in temp or 'LIMIT' not in temp:
|
||||||
|
return None # sort of understood it but not really
|
||||||
|
#print "temp=", temp
|
||||||
|
mg['GAME'] = temp['GAME']
|
||||||
|
mg['LIMIT'] = temp['LIMIT']
|
||||||
if 'GAME' in mg:
|
if 'GAME' in mg:
|
||||||
(info['base'], info['category']) = games[mg['GAME']]
|
(info['base'], info['category']) = games[mg['GAME']]
|
||||||
|
if 'LIMIT' in mg:
|
||||||
|
info['limitType'] = limits[mg['LIMIT']]
|
||||||
if 'SB' in mg:
|
if 'SB' in mg:
|
||||||
info['sb'] = mg['SB']
|
info['sb'] = mg['SB']
|
||||||
else:
|
else:
|
||||||
info['sb'] = str(float(mg['BB']) * 0.5) # TODO: Apparently AP doesn't provide small blind info!? must search to see if it's posted, I guess
|
info['sb'] = str(float(mg['BB']) * 0.5) # TODO: Apparently AP doesn't provide small blind info!? must search to see if it's posted, I guess
|
||||||
if 'BB' in mg:
|
if 'BB' in mg:
|
||||||
info['bb'] = mg['BB']
|
info['bb'] = mg['BB']
|
||||||
if 'CURRENCY' in mg:
|
if 'CURRENCY' in mg:
|
||||||
|
@ -147,6 +151,11 @@ or None if we fail to get the info """
|
||||||
if info['currency'] == 'T$':
|
if info['currency'] == 'T$':
|
||||||
info['type'] = 'tour'
|
info['type'] = 'tour'
|
||||||
# NB: SB, BB must be interpreted as blinds or bets depending on limit type.
|
# NB: SB, BB must be interpreted as blinds or bets depending on limit type.
|
||||||
|
if info['bb'] is None:
|
||||||
|
info['bb'] = mg['SB']
|
||||||
|
info['sb'] = str(float(mg['SB']) * 0.5) # TODO: AP does provide Small BET for Limit .. I think? at least 1-on-1 limit they do.. sigh
|
||||||
|
|
||||||
|
#print info;
|
||||||
|
|
||||||
return info
|
return info
|
||||||
|
|
||||||
|
@ -159,9 +168,15 @@ or None if we fail to get the info """
|
||||||
return None
|
return None
|
||||||
logging.debug("HID %s, Table %s" % (m.group('HID'), m.group('TABLE')))
|
logging.debug("HID %s, Table %s" % (m.group('HID'), m.group('TABLE')))
|
||||||
hand.handid = m.group('HID')
|
hand.handid = m.group('HID')
|
||||||
hand.tablename = m.group('TABLE')
|
if m.group('TABLE'):
|
||||||
|
hand.tablename = m.group('TABLE')
|
||||||
|
else:
|
||||||
|
t = self.re_TableFromFilename.search(self.in_path)
|
||||||
|
hand.tablename = t.group('TABLE')
|
||||||
hand.maxseats = 6 # assume 6-max unless we have proof it's a larger/smaller game, since absolute doesn't give seat max info
|
hand.maxseats = 6 # assume 6-max unless we have proof it's a larger/smaller game, since absolute doesn't give seat max info
|
||||||
|
# TODO: (1-on-1) does have that info in the game type line
|
||||||
|
if self.HORSEHand:
|
||||||
|
hand.maxseats = 8
|
||||||
hand.starttime = datetime.datetime.strptime(m.group('DATETIME'), "%Y-%m-%d %H:%M:%S")
|
hand.starttime = datetime.datetime.strptime(m.group('DATETIME'), "%Y-%m-%d %H:%M:%S")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -248,8 +263,7 @@ or None if we fail to get the info """
|
||||||
else:
|
else:
|
||||||
#Not involved in hand
|
#Not involved in hand
|
||||||
hand.involved = False
|
hand.involved = False
|
||||||
|
|
||||||
|
|
||||||
def readStudPlayerCards(self, hand, street):
|
def readStudPlayerCards(self, hand, street):
|
||||||
# lol. see Plymouth.txt
|
# lol. see Plymouth.txt
|
||||||
logging.warning("Absolute readStudPlayerCards is only a stub.")
|
logging.warning("Absolute readStudPlayerCards is only a stub.")
|
||||||
|
@ -283,7 +297,7 @@ or None if we fail to get the info """
|
||||||
logging.debug("readShowdownActions")
|
logging.debug("readShowdownActions")
|
||||||
for shows in self.re_ShowdownAction.finditer(hand.handText):
|
for shows in self.re_ShowdownAction.finditer(hand.handText):
|
||||||
cards = shows.group('CARDS')
|
cards = shows.group('CARDS')
|
||||||
cards = [validCard(card) for card in cards.split(' ')]
|
cards = [validCard(card) for card in cards.split(' ')]
|
||||||
logging.debug("readShowdownActions %s %s" %(cards, shows.group('PNAME')))
|
logging.debug("readShowdownActions %s %s" %(cards, shows.group('PNAME')))
|
||||||
hand.addShownCards(cards, shows.group('PNAME'))
|
hand.addShownCards(cards, shows.group('PNAME'))
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,11 @@ from HandHistoryConverter import *
|
||||||
|
|
||||||
class Betfair(HandHistoryConverter):
|
class Betfair(HandHistoryConverter):
|
||||||
|
|
||||||
|
sitename = 'Betfair'
|
||||||
|
filetype = "text"
|
||||||
|
codepage = "cp1252"
|
||||||
|
siteId = 7 # Needs to match id entry in Sites database
|
||||||
|
|
||||||
# Static regexes
|
# 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_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+')
|
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_PlayerInfo = re.compile("Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*)\s\(\s(\$(?P<CASH>[.0-9]+)) \)")
|
||||||
re_Board = re.compile(ur"\[ (?P<CARDS>.+) \]")
|
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):
|
def compilePlayerRegexs(self, hand):
|
||||||
players = set([player[1] for player in hand.players])
|
players = set([player[1] for player in hand.players])
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
"""Configuration.py
|
"""Configuration.py
|
||||||
|
|
||||||
Handles HUD configuration files.
|
Handles HUD configuration files.
|
||||||
|
@ -32,6 +33,11 @@ import shutil
|
||||||
import xml.dom.minidom
|
import xml.dom.minidom
|
||||||
from xml.dom.minidom import Node
|
from xml.dom.minidom import Node
|
||||||
|
|
||||||
|
import logging, logging.config
|
||||||
|
logging.config.fileConfig(os.path.join(sys.path[0],"logging.conf"))
|
||||||
|
log = logging.getLogger("config")
|
||||||
|
log.debug("config logger initialised")
|
||||||
|
|
||||||
def fix_tf(x, default = True):
|
def fix_tf(x, default = True):
|
||||||
# The xml parser doesn't translate "True" to True. Therefore, we never get
|
# The xml parser doesn't translate "True" to True. Therefore, we never get
|
||||||
# True or False from the parser only "True" or "False". So translate the
|
# True or False from the parser only "True" or "False". So translate the
|
||||||
|
@ -73,11 +79,17 @@ class Layout:
|
||||||
|
|
||||||
class Site:
|
class Site:
|
||||||
def __init__(self, node):
|
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.site_name = node.getAttribute("site_name")
|
||||||
self.table_finder = node.getAttribute("table_finder")
|
self.table_finder = node.getAttribute("table_finder")
|
||||||
self.screen_name = node.getAttribute("screen_name")
|
self.screen_name = node.getAttribute("screen_name")
|
||||||
self.site_path = node.getAttribute("site_path")
|
self.site_path = normalizePath(node.getAttribute("site_path"))
|
||||||
self.HH_path = node.getAttribute("HH_path")
|
self.HH_path = normalizePath(node.getAttribute("HH_path"))
|
||||||
self.decoder = node.getAttribute("decoder")
|
self.decoder = node.getAttribute("decoder")
|
||||||
self.hudopacity = node.getAttribute("hudopacity")
|
self.hudopacity = node.getAttribute("hudopacity")
|
||||||
self.hudbgcolor = node.getAttribute("bgcolor")
|
self.hudbgcolor = node.getAttribute("bgcolor")
|
||||||
|
@ -91,6 +103,8 @@ class Site:
|
||||||
self.xpad = node.getAttribute("xpad")
|
self.xpad = node.getAttribute("xpad")
|
||||||
self.ypad = node.getAttribute("ypad")
|
self.ypad = node.getAttribute("ypad")
|
||||||
self.layout = {}
|
self.layout = {}
|
||||||
|
|
||||||
|
print self.site_name, self.HH_path
|
||||||
|
|
||||||
for layout_node in node.getElementsByTagName('layout'):
|
for layout_node in node.getElementsByTagName('layout'):
|
||||||
lo = Layout(layout_node)
|
lo = Layout(layout_node)
|
||||||
|
@ -192,6 +206,9 @@ class Database:
|
||||||
self.db_user = node.getAttribute("db_user")
|
self.db_user = node.getAttribute("db_user")
|
||||||
self.db_type = node.getAttribute("db_type")
|
self.db_type = node.getAttribute("db_type")
|
||||||
self.db_pass = node.getAttribute("db_pass")
|
self.db_pass = node.getAttribute("db_pass")
|
||||||
|
self.db_selected = fix_tf(node.getAttribute("default"),"False")
|
||||||
|
log.debug("Database db_name:'%(name)s' db_server:'%(server)s' db_ip:'%(ip)s' db_user:'%(user)s' db_type:'%(type)s' db_pass (not logged) selected:'%(sel)s'" \
|
||||||
|
% { 'name':self.db_name, 'server':self.db_server, 'ip':self.db_ip, 'user':self.db_user, 'type':self.db_type, 'sel':self.db_selected} )
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
temp = 'Database = ' + self.db_name + '\n'
|
temp = 'Database = ' + self.db_name + '\n'
|
||||||
|
@ -199,7 +216,7 @@ class Database:
|
||||||
if key.startswith('__'): continue
|
if key.startswith('__'): continue
|
||||||
value = getattr(self, key)
|
value = getattr(self, key)
|
||||||
if callable(value): continue
|
if callable(value): continue
|
||||||
temp = temp + ' ' + key + " = " + value + "\n"
|
temp = temp + ' ' + key + " = " + repr(value) + "\n"
|
||||||
return temp
|
return temp
|
||||||
|
|
||||||
class Aux_window:
|
class Aux_window:
|
||||||
|
@ -270,11 +287,10 @@ class Tv:
|
||||||
(self.combinedStealFold, self.combined2B3B, self.combinedPostflop) )
|
(self.combinedStealFold, self.combined2B3B, self.combinedPostflop) )
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
def __init__(self, file = None, dbname = 'fpdb'):
|
def __init__(self, file = None, dbname = ''):
|
||||||
|
|
||||||
# "file" is a path to an xml file with the fpdb/HUD configuration
|
# "file" is a path to an xml file with the fpdb/HUD configuration
|
||||||
# we check the existence of "file" and try to recover if it doesn't exist
|
# we check the existence of "file" and try to recover if it doesn't exist
|
||||||
self.dbname = dbname
|
|
||||||
|
|
||||||
self.default_config_path = self.get_default_config_path()
|
self.default_config_path = self.get_default_config_path()
|
||||||
if file != None: # configuration file path has been passed
|
if file != None: # configuration file path has been passed
|
||||||
|
@ -300,10 +316,10 @@ class Config:
|
||||||
# Parse even if there was no real config file found and we are using the example
|
# Parse even if there was no real config file found and we are using the example
|
||||||
# If using the example, we'll edit it later
|
# If using the example, we'll edit it later
|
||||||
try:
|
try:
|
||||||
print "Reading configuration file %s\n" % (file)
|
log.info("Reading configuration file %s" % (file))
|
||||||
doc = xml.dom.minidom.parse(file)
|
doc = xml.dom.minidom.parse(file)
|
||||||
except:
|
except:
|
||||||
print "Error parsing %s. See error log file." % (file)
|
log.error("Error parsing %s. See error log file." % (file))
|
||||||
traceback.print_exc(file=sys.stderr)
|
traceback.print_exc(file=sys.stderr)
|
||||||
print "press enter to continue"
|
print "press enter to continue"
|
||||||
sys.stdin.readline()
|
sys.stdin.readline()
|
||||||
|
@ -329,9 +345,21 @@ class Config:
|
||||||
self.supported_games[game.game_name] = game
|
self.supported_games[game.game_name] = game
|
||||||
|
|
||||||
# s_dbs = doc.getElementsByTagName("supported_databases")
|
# s_dbs = doc.getElementsByTagName("supported_databases")
|
||||||
|
if dbname and dbname in self.supported_databases:
|
||||||
|
self.db_selected = dbname
|
||||||
for db_node in doc.getElementsByTagName("database"):
|
for db_node in doc.getElementsByTagName("database"):
|
||||||
db = Database(node = db_node)
|
try:
|
||||||
self.supported_databases[db.db_name] = db
|
db = Database(node = db_node)
|
||||||
|
if db.db_name in self.supported_databases:
|
||||||
|
raise FpdbError("Database names must be unique")
|
||||||
|
# If there is only one Database node, or none are marked default, the first is selected
|
||||||
|
if len(self.supported_databases) == 0:
|
||||||
|
self.db_selected = db.db_name
|
||||||
|
self.supported_databases[db.db_name] = db
|
||||||
|
if db.db_selected:
|
||||||
|
self.db_selected = db.db_name
|
||||||
|
except:
|
||||||
|
raise
|
||||||
|
|
||||||
# s_dbs = doc.getElementsByTagName("mucked_windows")
|
# s_dbs = doc.getElementsByTagName("mucked_windows")
|
||||||
for aw_node in doc.getElementsByTagName("aw"):
|
for aw_node in doc.getElementsByTagName("aw"):
|
||||||
|
@ -498,7 +526,7 @@ class Config:
|
||||||
|
|
||||||
def get_db_parameters(self):
|
def get_db_parameters(self):
|
||||||
db = {}
|
db = {}
|
||||||
name = self.dbname
|
name = self.db_selected
|
||||||
try: db['db-databaseName'] = name
|
try: db['db-databaseName'] = name
|
||||||
except: pass
|
except: pass
|
||||||
|
|
||||||
|
@ -542,6 +570,13 @@ class Config:
|
||||||
if db_server != None: self.supported_databases[db_name].dp_server = db_server
|
if db_server != None: self.supported_databases[db_name].dp_server = db_server
|
||||||
if db_type != None: self.supported_databases[db_name].dp_type = db_type
|
if db_type != None: self.supported_databases[db_name].dp_type = db_type
|
||||||
return
|
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):
|
def get_tv_parameters(self):
|
||||||
tv = {}
|
tv = {}
|
||||||
|
@ -573,30 +608,32 @@ class Config:
|
||||||
except: imp['fastStoreHudCache'] = True
|
except: imp['fastStoreHudCache'] = True
|
||||||
return imp
|
return imp
|
||||||
|
|
||||||
def get_default_paths(self, site = "PokerStars"):
|
def get_default_paths(self, site = None):
|
||||||
|
if site is None: site = self.getDefaultSite()
|
||||||
paths = {}
|
paths = {}
|
||||||
try:
|
try:
|
||||||
paths['hud-defaultPath'] = os.path.expanduser(self.supported_sites[site].HH_path)
|
path = os.path.expanduser(self.supported_sites[site].HH_path)
|
||||||
paths['bulkImport-defaultPath'] = 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?
|
||||||
except:
|
paths['hud-defaultPath'] = paths['bulkImport-defaultPath'] = path
|
||||||
paths['hud-defaultPath'] = "default"
|
except AssertionError:
|
||||||
paths['bulkImport-defaultPath'] = "default"
|
paths['hud-defaultPath'] = paths['bulkImport-defaultPath'] = "** ERROR DEFAULT PATH IN CONFIG DOES NOT EXIST **"
|
||||||
return paths
|
return paths
|
||||||
|
|
||||||
def get_frames(self, site = "PokerStars"):
|
def get_frames(self, site = "PokerStars"):
|
||||||
|
if site not in self.supported_sites: return False
|
||||||
return self.supported_sites[site].use_frames == True
|
return self.supported_sites[site].use_frames == True
|
||||||
|
|
||||||
def get_default_colors(self, site = "PokerStars"):
|
def get_default_colors(self, site = "PokerStars"):
|
||||||
colors = {}
|
colors = {}
|
||||||
if self.supported_sites[site].hudopacity == "":
|
if site not in self.supported_sites or self.supported_sites[site].hudopacity == "":
|
||||||
colors['hudopacity'] = 0.90
|
colors['hudopacity'] = 0.90
|
||||||
else:
|
else:
|
||||||
colors['hudopacity'] = float(self.supported_sites[site].hudopacity)
|
colors['hudopacity'] = float(self.supported_sites[site].hudopacity)
|
||||||
if self.supported_sites[site].hudbgcolor == "":
|
if site not in self.supported_sites or self.supported_sites[site].hudbgcolor == "":
|
||||||
colors['hudbgcolor'] = "#FFFFFF"
|
colors['hudbgcolor'] = "#FFFFFF"
|
||||||
else:
|
else:
|
||||||
colors['hudbgcolor'] = self.supported_sites[site].hudbgcolor
|
colors['hudbgcolor'] = self.supported_sites[site].hudbgcolor
|
||||||
if self.supported_sites[site].hudfgcolor == "":
|
if site not in self.supported_sites or self.supported_sites[site].hudfgcolor == "":
|
||||||
colors['hudfgcolor'] = "#000000"
|
colors['hudfgcolor'] = "#000000"
|
||||||
else:
|
else:
|
||||||
colors['hudfgcolor'] = self.supported_sites[site].hudfgcolor
|
colors['hudfgcolor'] = self.supported_sites[site].hudfgcolor
|
||||||
|
@ -604,6 +641,8 @@ class Config:
|
||||||
|
|
||||||
def get_default_font(self, site = 'PokerStars'):
|
def get_default_font(self, site = 'PokerStars'):
|
||||||
(font, font_size) = ("Sans", "8")
|
(font, font_size) = ("Sans", "8")
|
||||||
|
if site not in self.supported_sites:
|
||||||
|
return ("Sans", "8")
|
||||||
if self.supported_sites[site].font == "":
|
if self.supported_sites[site].font == "":
|
||||||
font = "Sans"
|
font = "Sans"
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -24,13 +24,14 @@ Create and manage the database objects.
|
||||||
# postmaster -D /var/lib/pgsql/data
|
# postmaster -D /var/lib/pgsql/data
|
||||||
|
|
||||||
# Standard Library modules
|
# Standard Library modules
|
||||||
|
import os
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
from datetime import datetime, date, time, timedelta
|
from datetime import datetime, date, time, timedelta
|
||||||
from time import time, strftime, sleep
|
from time import time, strftime, sleep
|
||||||
|
from decimal import Decimal
|
||||||
import string
|
import string
|
||||||
import re
|
import re
|
||||||
import logging
|
|
||||||
import Queue
|
import Queue
|
||||||
|
|
||||||
# pyGTK modules
|
# pyGTK modules
|
||||||
|
@ -41,6 +42,12 @@ import fpdb_simple
|
||||||
import Configuration
|
import Configuration
|
||||||
import SQL
|
import SQL
|
||||||
import Card
|
import Card
|
||||||
|
import Tourney
|
||||||
|
from Exceptions import *
|
||||||
|
|
||||||
|
import logging, logging.config
|
||||||
|
logging.config.fileConfig(os.path.join(sys.path[0],"logging.conf"))
|
||||||
|
log = logging.getLogger('db')
|
||||||
|
|
||||||
class Database:
|
class Database:
|
||||||
|
|
||||||
|
@ -92,6 +99,14 @@ class Database:
|
||||||
, {'tab':'TourneyTypes', 'col':'siteId', 'drop':0}
|
, {'tab':'TourneyTypes', 'col':'siteId', 'drop':0}
|
||||||
]
|
]
|
||||||
, [ # indexes for sqlite (list index 4)
|
, [ # indexes for sqlite (list index 4)
|
||||||
|
{'tab':'Players', 'col':'name', 'drop':0}
|
||||||
|
, {'tab':'Hands', 'col':'siteHandNo', 'drop':0}
|
||||||
|
, {'tab':'Hands', 'col':'gametypeId', 'drop':0}
|
||||||
|
, {'tab':'HandsPlayers', 'col':'handId', 'drop':0}
|
||||||
|
, {'tab':'HandsPlayers', 'col':'playerId', 'drop':0}
|
||||||
|
, {'tab':'HandsPlayers', 'col':'tourneyTypeId', 'drop':0}
|
||||||
|
, {'tab':'HandsPlayers', 'col':'tourneysPlayersId', 'drop':0}
|
||||||
|
, {'tab':'Tourneys', 'col':'siteTourneyNo', 'drop':0}
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -160,8 +175,14 @@ class Database:
|
||||||
# CREATE INDEX idx ON tab(col)
|
# CREATE INDEX idx ON tab(col)
|
||||||
# DROP INDEX idx
|
# DROP INDEX idx
|
||||||
|
|
||||||
|
# SQLite notes:
|
||||||
|
|
||||||
|
# To add an index:
|
||||||
|
# create index indexname on tablename (col);
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, c, db_name = None, game = None, sql = None): # db_name and game not used any more
|
def __init__(self, c, db_name = None, game = None, sql = None): # db_name and game not used any more
|
||||||
print "\ncreating Database instance, sql =", sql
|
log.info("Creating Database instance, sql = %s" % sql)
|
||||||
self.fdb = fpdb_db.fpdb_db() # sets self.fdb.db self.fdb.cursor and self.fdb.sql
|
self.fdb = fpdb_db.fpdb_db() # sets self.fdb.db self.fdb.cursor and self.fdb.sql
|
||||||
self.fdb.do_connect(c)
|
self.fdb.do_connect(c)
|
||||||
self.connection = self.fdb.db
|
self.connection = self.fdb.db
|
||||||
|
@ -178,12 +199,17 @@ class Database:
|
||||||
#ISOLATION_LEVEL_READ_COMMITTED = 1
|
#ISOLATION_LEVEL_READ_COMMITTED = 1
|
||||||
#ISOLATION_LEVEL_SERIALIZABLE = 2
|
#ISOLATION_LEVEL_SERIALIZABLE = 2
|
||||||
|
|
||||||
|
|
||||||
# where possible avoid creating new SQL instance by using the global one passed in
|
# where possible avoid creating new SQL instance by using the global one passed in
|
||||||
if sql == None:
|
if sql == None:
|
||||||
self.sql = SQL.Sql(type = self.type, db_server = db_params['db-server'])
|
self.sql = SQL.Sql(type = self.type, db_server = db_params['db-server'])
|
||||||
else:
|
else:
|
||||||
self.sql = sql
|
self.sql = sql
|
||||||
|
|
||||||
|
if self.backend == self.SQLITE and db_params['db-databaseName'] == ':memory:' and self.fdb.wrongDbVersion:
|
||||||
|
log.info("sqlite/:memory: - creating")
|
||||||
|
self.recreate_tables()
|
||||||
|
|
||||||
self.pcache = None # PlayerId cache
|
self.pcache = None # PlayerId cache
|
||||||
self.cachemiss = 0 # Delete me later - using to count player cache misses
|
self.cachemiss = 0 # Delete me later - using to count player cache misses
|
||||||
self.cachehit = 0 # Delete me later - using to count player cache hits
|
self.cachehit = 0 # Delete me later - using to count player cache hits
|
||||||
|
@ -243,7 +269,7 @@ class Database:
|
||||||
elif self.backend==4:
|
elif self.backend==4:
|
||||||
return "SQLite"
|
return "SQLite"
|
||||||
else:
|
else:
|
||||||
raise fpdb_simple.FpdbError("invalid backend")
|
raise FpdbError("invalid backend")
|
||||||
|
|
||||||
def get_table_name(self, hand_id):
|
def get_table_name(self, hand_id):
|
||||||
c = self.connection.cursor()
|
c = self.connection.cursor()
|
||||||
|
@ -438,7 +464,7 @@ class Database:
|
||||||
if colnames[0].lower() == 'player_id':
|
if colnames[0].lower() == 'player_id':
|
||||||
playerid = row[0]
|
playerid = row[0]
|
||||||
else:
|
else:
|
||||||
print "ERROR: query %s result does not have player_id as first column" % (query,)
|
log.error("ERROR: query %s result does not have player_id as first column" % (query,))
|
||||||
break
|
break
|
||||||
|
|
||||||
for name, val in zip(colnames, row):
|
for name, val in zip(colnames, row):
|
||||||
|
@ -479,7 +505,7 @@ class Database:
|
||||||
if self.backend == self.MYSQL_INNODB:
|
if self.backend == self.MYSQL_INNODB:
|
||||||
ret = self.connection.insert_id()
|
ret = self.connection.insert_id()
|
||||||
if ret < 1 or ret > 999999999:
|
if ret < 1 or ret > 999999999:
|
||||||
print "getLastInsertId(): problem fetching insert_id? ret=", ret
|
log.warning("getLastInsertId(): problem fetching insert_id? ret=%d" % ret)
|
||||||
ret = -1
|
ret = -1
|
||||||
elif self.backend == self.PGSQL:
|
elif self.backend == self.PGSQL:
|
||||||
# some options:
|
# some options:
|
||||||
|
@ -491,14 +517,14 @@ class Database:
|
||||||
ret = c.execute ("SELECT lastval()")
|
ret = c.execute ("SELECT lastval()")
|
||||||
row = c.fetchone()
|
row = c.fetchone()
|
||||||
if not row:
|
if not row:
|
||||||
print "getLastInsertId(%s): problem fetching lastval? row=" % seq, row
|
log.warning("getLastInsertId(%s): problem fetching lastval? row=%d" % (seq, row))
|
||||||
ret = -1
|
ret = -1
|
||||||
else:
|
else:
|
||||||
ret = row[0]
|
ret = row[0]
|
||||||
elif self.backend == self.SQLITE:
|
elif self.backend == self.SQLITE:
|
||||||
ret = cursor.lastrowid
|
ret = cursor.lastrowid
|
||||||
else:
|
else:
|
||||||
print "getLastInsertId(): unknown backend ", self.backend
|
log.error("getLastInsertId(): unknown backend: %d" % self.backend)
|
||||||
ret = -1
|
ret = -1
|
||||||
except:
|
except:
|
||||||
ret = -1
|
ret = -1
|
||||||
|
@ -583,7 +609,7 @@ class Database:
|
||||||
hands_players_ids = self.store_hands_players_holdem_omaha_tourney(
|
hands_players_ids = self.store_hands_players_holdem_omaha_tourney(
|
||||||
self.backend, category, hands_id, player_ids, start_cashes, positions
|
self.backend, category, hands_id, player_ids, start_cashes, positions
|
||||||
, card_values, card_suits, winnings, rakes, seatNos, tourneys_players_ids
|
, card_values, card_suits, winnings, rakes, seatNos, tourneys_players_ids
|
||||||
, hudImportData)
|
, hudImportData, tourneyTypeId)
|
||||||
|
|
||||||
#print "tourney holdem, backend=%d" % backend
|
#print "tourney holdem, backend=%d" % backend
|
||||||
if 'dropHudCache' not in settings or settings['dropHudCache'] != 'drop':
|
if 'dropHudCache' not in settings or settings['dropHudCache'] != 'drop':
|
||||||
|
@ -611,7 +637,7 @@ class Database:
|
||||||
|
|
||||||
hands_players_ids = self.store_hands_players_stud_tourney(self.backend, hands_id
|
hands_players_ids = self.store_hands_players_stud_tourney(self.backend, hands_id
|
||||||
, playerIds, startCashes, antes, cardValues, cardSuits
|
, playerIds, startCashes, antes, cardValues, cardSuits
|
||||||
, winnings, rakes, seatNos, tourneys_players_ids)
|
, winnings, rakes, seatNos, tourneys_players_ids, tourneyTypeId)
|
||||||
|
|
||||||
if 'dropHudCache' not in settings or settings['dropHudCache'] != 'drop':
|
if 'dropHudCache' not in settings or settings['dropHudCache'] != 'drop':
|
||||||
self.storeHudCache(self.backend, base, category, gametypeId, hand_start_time, playerIds, hudImportData)
|
self.storeHudCache(self.backend, base, category, gametypeId, hand_start_time, playerIds, hudImportData)
|
||||||
|
@ -822,16 +848,16 @@ class Database:
|
||||||
self.create_tables()
|
self.create_tables()
|
||||||
self.createAllIndexes()
|
self.createAllIndexes()
|
||||||
self.commit()
|
self.commit()
|
||||||
print "Finished recreating tables"
|
log.info("Finished recreating tables")
|
||||||
#end def recreate_tables
|
#end def recreate_tables
|
||||||
|
|
||||||
def create_tables(self):
|
def create_tables(self):
|
||||||
#todo: should detect and fail gracefully if tables already exist.
|
#todo: should detect and fail gracefully if tables already exist.
|
||||||
try:
|
try:
|
||||||
logging.debug(self.sql.query['createSettingsTable'])
|
log.debug(self.sql.query['createSettingsTable'])
|
||||||
c = self.get_cursor()
|
c = self.get_cursor()
|
||||||
c.execute(self.sql.query['createSettingsTable'])
|
c.execute(self.sql.query['createSettingsTable'])
|
||||||
logging.debug(self.sql.query['createSitesTable'])
|
log.debug(self.sql.query['createSitesTable'])
|
||||||
c.execute(self.sql.query['createSitesTable'])
|
c.execute(self.sql.query['createSitesTable'])
|
||||||
c.execute(self.sql.query['createGametypesTable'])
|
c.execute(self.sql.query['createGametypesTable'])
|
||||||
c.execute(self.sql.query['createPlayersTable'])
|
c.execute(self.sql.query['createPlayersTable'])
|
||||||
|
@ -858,35 +884,51 @@ class Database:
|
||||||
|
|
||||||
def drop_tables(self):
|
def drop_tables(self):
|
||||||
"""Drops the fpdb tables from the current db"""
|
"""Drops the fpdb tables from the current db"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
c = self.get_cursor()
|
c = self.get_cursor()
|
||||||
if(self.get_backend_name() == 'MySQL InnoDB'):
|
|
||||||
#Databases with FOREIGN KEY support need this switched of before you can drop tables
|
|
||||||
self.drop_referential_integrity()
|
|
||||||
|
|
||||||
# Query the DB to see what tables exist
|
|
||||||
c.execute(self.sql.query['list_tables'])
|
|
||||||
for table in c:
|
|
||||||
c.execute(self.sql.query['drop_table'] + table[0])
|
|
||||||
elif(self.get_backend_name() == 'PostgreSQL'):
|
|
||||||
self.commit()# I have no idea why this makes the query work--REB 07OCT2008
|
|
||||||
c.execute(self.sql.query['list_tables'])
|
|
||||||
tables = c.fetchall()
|
|
||||||
for table in tables:
|
|
||||||
c.execute(self.sql.query['drop_table'] + table[0] + ' cascade')
|
|
||||||
elif(self.get_backend_name() == 'SQLite'):
|
|
||||||
c.execute(self.sql.query['list_tables'])
|
|
||||||
for table in c.fetchall():
|
|
||||||
logging.debug(self.sql.query['drop_table'] + table[0])
|
|
||||||
c.execute(self.sql.query['drop_table'] + table[0])
|
|
||||||
|
|
||||||
self.commit()
|
|
||||||
except:
|
except:
|
||||||
err = traceback.extract_tb(sys.exc_info()[2])[-1]
|
print "*** Error unable to get cursor"
|
||||||
print "***Error dropping tables: "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1])
|
else:
|
||||||
self.rollback()
|
backend = self.get_backend_name()
|
||||||
raise
|
if backend == 'MySQL InnoDB': # what happens if someone is using MyISAM?
|
||||||
|
try:
|
||||||
|
self.drop_referential_integrity() # needed to drop tables with foreign keys
|
||||||
|
c.execute(self.sql.query['list_tables'])
|
||||||
|
tables = c.fetchall()
|
||||||
|
for table in tables:
|
||||||
|
c.execute(self.sql.query['drop_table'] + table[0])
|
||||||
|
except:
|
||||||
|
err = traceback.extract_tb(sys.exc_info()[2])[-1]
|
||||||
|
print "***Error dropping tables: "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1])
|
||||||
|
self.rollback()
|
||||||
|
elif backend == 'PostgreSQL':
|
||||||
|
try:
|
||||||
|
self.commit()
|
||||||
|
c.execute(self.sql.query['list_tables'])
|
||||||
|
tables = c.fetchall()
|
||||||
|
for table in tables:
|
||||||
|
c.execute(self.sql.query['drop_table'] + table[0] + ' cascade')
|
||||||
|
except:
|
||||||
|
err = traceback.extract_tb(sys.exc_info()[2])[-1]
|
||||||
|
print "***Error dropping tables: "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1])
|
||||||
|
self.rollback()
|
||||||
|
elif backend == 'SQLite':
|
||||||
|
try:
|
||||||
|
c.execute(self.sql.query['list_tables'])
|
||||||
|
for table in c.fetchall():
|
||||||
|
log.debug(self.sql.query['drop_table'] + table[0])
|
||||||
|
c.execute(self.sql.query['drop_table'] + table[0])
|
||||||
|
except:
|
||||||
|
err = traceback.extract_tb(sys.exc_info()[2])[-1]
|
||||||
|
print "***Error dropping tables: "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1])
|
||||||
|
self.rollback()
|
||||||
|
try:
|
||||||
|
self.commit()
|
||||||
|
except:
|
||||||
|
print "*** Error in committing table drop"
|
||||||
|
err = traceback.extract_tb(sys.exc_info()[2])[-1]
|
||||||
|
print "***Error dropping tables: "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1])
|
||||||
|
self.rollback()
|
||||||
#end def drop_tables
|
#end def drop_tables
|
||||||
|
|
||||||
def createAllIndexes(self):
|
def createAllIndexes(self):
|
||||||
|
@ -911,14 +953,21 @@ class Database:
|
||||||
self.get_cursor().execute(s)
|
self.get_cursor().execute(s)
|
||||||
except:
|
except:
|
||||||
print " create idx failed: " + str(sys.exc_info())
|
print " create idx failed: " + str(sys.exc_info())
|
||||||
|
elif self.backend == self.SQLITE:
|
||||||
|
log.debug("Creating sqlite index %s %s" % (idx['tab'], idx['col']))
|
||||||
|
try:
|
||||||
|
s = "create index %s_%s_idx on %s(%s)" % (idx['tab'], idx['col'], idx['tab'], idx['col'])
|
||||||
|
self.get_cursor().execute(s)
|
||||||
|
except:
|
||||||
|
log.debug("Create idx failed: " + str(sys.exc_info()))
|
||||||
else:
|
else:
|
||||||
print "Only MySQL and Postgres supported so far"
|
print "Only MySQL, Postgres and SQLite supported so far"
|
||||||
return -1
|
return -1
|
||||||
if self.backend == self.PGSQL:
|
if self.backend == self.PGSQL:
|
||||||
self.connection.set_isolation_level(1) # go back to normal isolation level
|
self.connection.set_isolation_level(1) # go back to normal isolation level
|
||||||
except:
|
except:
|
||||||
print "Error creating indexes: " + str(sys.exc_value)
|
print "Error creating indexes: " + str(sys.exc_value)
|
||||||
raise fpdb_simple.FpdbError( "Error creating indexes " + str(sys.exc_value) )
|
raise FpdbError( "Error creating indexes " + str(sys.exc_value) )
|
||||||
#end def createAllIndexes
|
#end def createAllIndexes
|
||||||
|
|
||||||
def dropAllIndexes(self):
|
def dropAllIndexes(self):
|
||||||
|
@ -951,7 +1000,7 @@ class Database:
|
||||||
#end def dropAllIndexes
|
#end def dropAllIndexes
|
||||||
|
|
||||||
def fillDefaultData(self):
|
def fillDefaultData(self):
|
||||||
c = self.get_cursor()
|
c = self.get_cursor()
|
||||||
c.execute("INSERT INTO Settings (version) VALUES (118);")
|
c.execute("INSERT INTO Settings (version) VALUES (118);")
|
||||||
c.execute("INSERT INTO Sites (name,currency) VALUES ('Full Tilt Poker', 'USD')")
|
c.execute("INSERT INTO Sites (name,currency) VALUES ('Full Tilt Poker', 'USD')")
|
||||||
c.execute("INSERT INTO Sites (name,currency) VALUES ('PokerStars', 'USD')")
|
c.execute("INSERT INTO Sites (name,currency) VALUES ('PokerStars', 'USD')")
|
||||||
|
@ -963,12 +1012,10 @@ class Database:
|
||||||
c.execute("INSERT INTO Sites (name,currency) VALUES ('Absolute', 'USD')")
|
c.execute("INSERT INTO Sites (name,currency) VALUES ('Absolute', 'USD')")
|
||||||
c.execute("INSERT INTO Sites (name,currency) VALUES ('PartyPoker', 'USD')")
|
c.execute("INSERT INTO Sites (name,currency) VALUES ('PartyPoker', 'USD')")
|
||||||
if self.backend == self.SQLITE:
|
if self.backend == self.SQLITE:
|
||||||
c.execute("INSERT INTO TourneyTypes VALUES (NULL, 1, 0, 0, 0, 0);")
|
c.execute("INSERT INTO TourneyTypes (id, siteId, buyin, fee) VALUES (NULL, 1, 0, 0);")
|
||||||
else:
|
else:
|
||||||
c.execute("INSERT INTO TourneyTypes VALUES (DEFAULT, 1, 0, 0, 0, False);")
|
c.execute("insert into tourneytypes values (0,1,0,0,0,0,0,null,0,0,0);")
|
||||||
#c.execute("""INSERT INTO TourneyTypes
|
|
||||||
# (siteId,buyin,fee,knockout,rebuyOrAddon) VALUES
|
|
||||||
# (1,0,0,0,?)""",(False,) )
|
|
||||||
#end def fillDefaultData
|
#end def fillDefaultData
|
||||||
|
|
||||||
def rebuild_hudcache(self):
|
def rebuild_hudcache(self):
|
||||||
|
@ -1020,6 +1067,22 @@ class Database:
|
||||||
print "Error during fdb.lock_for_insert:", str(sys.exc_value)
|
print "Error during fdb.lock_for_insert:", str(sys.exc_value)
|
||||||
#end def lock_for_insert
|
#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):
|
def getSqlPlayerIDs(self, pnames, siteid):
|
||||||
result = {}
|
result = {}
|
||||||
if(self.pcache == None):
|
if(self.pcache == None):
|
||||||
|
@ -1040,10 +1103,15 @@ class Database:
|
||||||
def insertPlayer(self, name, site_id):
|
def insertPlayer(self, name, site_id):
|
||||||
result = None
|
result = None
|
||||||
c = self.get_cursor()
|
c = self.get_cursor()
|
||||||
c.execute ("SELECT id FROM Players WHERE name=%s".replace('%s',self.sql.query['placeholder'])
|
q = "SELECT name, id FROM Players WHERE siteid=%s and name=%s"
|
||||||
,(name,))
|
q = q.replace('%s', self.sql.query['placeholder'])
|
||||||
|
|
||||||
|
#print "DEBUG: name: %s site: %s" %(name, site_id)
|
||||||
|
|
||||||
|
c.execute (q, (site_id, name))
|
||||||
|
|
||||||
tmp = c.fetchone()
|
tmp = c.fetchone()
|
||||||
if (len(tmp)==0): #new player
|
if (tmp == None): #new player
|
||||||
c.execute ("INSERT INTO Players (name, siteId) VALUES (%s, %s)".replace('%s',self.sql.query['placeholder'])
|
c.execute ("INSERT INTO Players (name, siteId) VALUES (%s, %s)".replace('%s',self.sql.query['placeholder'])
|
||||||
,(name, site_id))
|
,(name, site_id))
|
||||||
#Get last id might be faster here.
|
#Get last id might be faster here.
|
||||||
|
@ -1085,7 +1153,7 @@ class Database:
|
||||||
, h.allIns, h.actionAmounts, h.actionNos, h.hudImportData, h.maxSeats
|
, h.allIns, h.actionAmounts, h.actionNos, h.hudImportData, h.maxSeats
|
||||||
, h.tableName, h.seatNos)
|
, h.tableName, h.seatNos)
|
||||||
else:
|
else:
|
||||||
raise fpdb_simple.FpdbError("unrecognised category")
|
raise FpdbError("unrecognised category")
|
||||||
else:
|
else:
|
||||||
if h.base == "hold":
|
if h.base == "hold":
|
||||||
result = self.ring_holdem_omaha(
|
result = self.ring_holdem_omaha(
|
||||||
|
@ -1103,7 +1171,7 @@ class Database:
|
||||||
, h.actionAmounts, h.actionNos, h.hudImportData, h.maxSeats, h.tableName
|
, h.actionAmounts, h.actionNos, h.hudImportData, h.maxSeats, h.tableName
|
||||||
, h.seatNos)
|
, h.seatNos)
|
||||||
else:
|
else:
|
||||||
raise fpdb_simple.FpdbError("unrecognised category")
|
raise FpdbError("unrecognised category")
|
||||||
except:
|
except:
|
||||||
print "Error storing hand: " + str(sys.exc_value)
|
print "Error storing hand: " + str(sys.exc_value)
|
||||||
self.rollback()
|
self.rollback()
|
||||||
|
@ -1116,71 +1184,76 @@ class Database:
|
||||||
|
|
||||||
def storeHand(self, p):
|
def storeHand(self, p):
|
||||||
#stores into table hands:
|
#stores into table hands:
|
||||||
self.cursor.execute ("""INSERT INTO Hands (
|
q = """INSERT INTO Hands (
|
||||||
tablename,
|
tablename,
|
||||||
sitehandno,
|
|
||||||
gametypeid,
|
gametypeid,
|
||||||
|
sitehandno,
|
||||||
handstart,
|
handstart,
|
||||||
importtime,
|
importtime,
|
||||||
seats,
|
seats,
|
||||||
maxseats,
|
maxseats,
|
||||||
boardcard1,
|
boardcard1,
|
||||||
boardcard2,
|
boardcard2,
|
||||||
boardcard3,
|
boardcard3,
|
||||||
boardcard4,
|
boardcard4,
|
||||||
boardcard5,
|
boardcard5,
|
||||||
-- texture,
|
street1Pot,
|
||||||
playersVpi,
|
street2Pot,
|
||||||
playersAtStreet1,
|
street3Pot,
|
||||||
playersAtStreet2,
|
street4Pot,
|
||||||
playersAtStreet3,
|
showdownPot
|
||||||
playersAtStreet4,
|
|
||||||
playersAtShowdown,
|
|
||||||
street0Raises,
|
|
||||||
street1Raises,
|
|
||||||
street2Raises,
|
|
||||||
street3Raises,
|
|
||||||
street4Raises,
|
|
||||||
-- street1Pot,
|
|
||||||
-- street2Pot,
|
|
||||||
-- street3Pot,
|
|
||||||
-- street4Pot,
|
|
||||||
-- showdownPot
|
|
||||||
)
|
)
|
||||||
VALUES
|
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, %s, %s, %s, %s, %s, %s)""",
|
%s, %s, %s, %s, %s, %s, %s)"""
|
||||||
(
|
#--- texture,
|
||||||
p['tablename'],
|
#-- playersVpi,
|
||||||
p['sitehandno'],
|
#-- playersAtStreet1,
|
||||||
p['gametypeid'],
|
#-- playersAtStreet2,
|
||||||
|
#-- playersAtStreet3,
|
||||||
|
#-- playersAtStreet4,
|
||||||
|
#-- playersAtShowdown,
|
||||||
|
#-- street0Raises,
|
||||||
|
#-- street1Raises,
|
||||||
|
#-- street2Raises,
|
||||||
|
#-- street3Raises,
|
||||||
|
#-- street4Raises,
|
||||||
|
#-- 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['handStart'],
|
p['handStart'],
|
||||||
datetime.datetime.today(),
|
datetime.today(), #importtime
|
||||||
len(p['names']),
|
# len(p['names']), #seats
|
||||||
p['maxSeats'],
|
p['maxSeats'],
|
||||||
|
p['seats'],
|
||||||
p['boardcard1'],
|
p['boardcard1'],
|
||||||
p['boardcard2'],
|
p['boardcard2'],
|
||||||
p['boardcard3'],
|
p['boardcard3'],
|
||||||
p['boardcard4'],
|
p['boardcard4'],
|
||||||
p['boardcard5'],
|
p['boardcard5'],
|
||||||
hudCache['playersVpi'],
|
# hudCache['playersVpi'],
|
||||||
hudCache['playersAtStreet1'],
|
# hudCache['playersAtStreet1'],
|
||||||
hudCache['playersAtStreet2'],
|
# hudCache['playersAtStreet2'],
|
||||||
hudCache['playersAtStreet3'],
|
# hudCache['playersAtStreet3'],
|
||||||
hudCache['playersAtStreet4'],
|
# hudCache['playersAtStreet4'],
|
||||||
hudCache['playersAtShowdown'],
|
# hudCache['playersAtShowdown'],
|
||||||
hudCache['street0Raises'],
|
# hudCache['street0Raises'],
|
||||||
hudCache['street1Raises'],
|
# hudCache['street1Raises'],
|
||||||
hudCache['street2Raises'],
|
# hudCache['street2Raises'],
|
||||||
hudCache['street3Raises'],
|
# hudCache['street3Raises'],
|
||||||
hudCache['street4Raises'],
|
# hudCache['street4Raises'],
|
||||||
hudCache['street1Pot'],
|
p['street1Pot'],
|
||||||
hudCache['street2Pot'],
|
p['street2Pot'],
|
||||||
hudCache['street3Pot'],
|
p['street3Pot'],
|
||||||
hudCache['street4Pot'],
|
p['street4Pot'],
|
||||||
hudCache['showdownPot']
|
p['showdownPot']
|
||||||
)
|
))
|
||||||
)
|
|
||||||
#return getLastInsertId(backend, conn, cursor)
|
#return getLastInsertId(backend, conn, cursor)
|
||||||
# def storeHand
|
# def storeHand
|
||||||
|
|
||||||
|
@ -1218,7 +1291,7 @@ class Database:
|
||||||
ret = self.get_last_insert_id(c)
|
ret = self.get_last_insert_id(c)
|
||||||
except:
|
except:
|
||||||
ret = -1
|
ret = -1
|
||||||
raise fpdb_simple.FpdbError( "storeHands error: " + str(sys.exc_value) )
|
raise FpdbError( "storeHands error: " + str(sys.exc_value) )
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
#end def storeHands
|
#end def storeHands
|
||||||
|
@ -1253,10 +1326,10 @@ class Database:
|
||||||
card3 = Card.cardFromValueSuit(card_values[i][2], card_suits[i][2])
|
card3 = Card.cardFromValueSuit(card_values[i][2], card_suits[i][2])
|
||||||
card4 = Card.cardFromValueSuit(card_values[i][3], card_suits[i][3])
|
card4 = Card.cardFromValueSuit(card_values[i][3], card_suits[i][3])
|
||||||
else:
|
else:
|
||||||
raise fpdb_simple.FpdbError("invalid category")
|
raise FpdbError("invalid category")
|
||||||
|
|
||||||
inserts.append( (
|
inserts.append( (
|
||||||
hands_id, player_ids[i], start_cashes[i], positions[i], 1, # tourneytypeid
|
hands_id, player_ids[i], start_cashes[i], positions[i],
|
||||||
card1, card2, card3, card4, startCards,
|
card1, card2, card3, card4, startCards,
|
||||||
winnings[i], rakes[i], seatNos[i], hudCache['totalProfit'][i],
|
winnings[i], rakes[i], seatNos[i], hudCache['totalProfit'][i],
|
||||||
hudCache['street0VPI'][i], hudCache['street0Aggr'][i],
|
hudCache['street0VPI'][i], hudCache['street0Aggr'][i],
|
||||||
|
@ -1287,7 +1360,7 @@ class Database:
|
||||||
c = self.get_cursor()
|
c = self.get_cursor()
|
||||||
c.executemany ("""
|
c.executemany ("""
|
||||||
INSERT INTO HandsPlayers
|
INSERT INTO HandsPlayers
|
||||||
(handId, playerId, startCash, position, tourneyTypeId,
|
(handId, playerId, startCash, position,
|
||||||
card1, card2, card3, card4, startCards, winnings, rake, seatNo, totalProfit,
|
card1, card2, card3, card4, startCards, winnings, rake, seatNo, totalProfit,
|
||||||
street0VPI, street0Aggr, street0_3BChance, street0_3BDone,
|
street0VPI, street0Aggr, street0_3BChance, street0_3BDone,
|
||||||
street1Seen, street2Seen, street3Seen, street4Seen, sawShowdown,
|
street1Seen, street2Seen, street3Seen, street4Seen, sawShowdown,
|
||||||
|
@ -1308,11 +1381,11 @@ class Database:
|
||||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
|
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, %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, %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, %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, %s, %s, %s, %s, %s, %s, %s,
|
||||||
%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""".replace('%s', self.sql.query['placeholder'])
|
%s, %s, %s, %s, %s, %s, %s, %s, %s)""".replace('%s', self.sql.query['placeholder'])
|
||||||
,inserts )
|
,inserts )
|
||||||
result.append( self.get_last_insert_id(c) ) # wrong? not used currently
|
result.append( self.get_last_insert_id(c) ) # wrong? not used currently
|
||||||
except:
|
except:
|
||||||
raise fpdb_simple.FpdbError( "store_hands_players_holdem_omaha error: " + str(sys.exc_value) )
|
raise FpdbError( "store_hands_players_holdem_omaha error: " + str(sys.exc_value) )
|
||||||
|
|
||||||
return result
|
return result
|
||||||
#end def store_hands_players_holdem_omaha
|
#end def store_hands_players_holdem_omaha
|
||||||
|
@ -1350,7 +1423,7 @@ class Database:
|
||||||
#result.append(cursor.fetchall()[0][0])
|
#result.append(cursor.fetchall()[0][0])
|
||||||
result.append( self.get_last_insert_id(c) )
|
result.append( self.get_last_insert_id(c) )
|
||||||
except:
|
except:
|
||||||
raise fpdb_simple.FpdbError( "store_hands_players_stud error: " + str(sys.exc_value) )
|
raise FpdbError( "store_hands_players_stud error: " + str(sys.exc_value) )
|
||||||
|
|
||||||
return result
|
return result
|
||||||
#end def store_hands_players_stud
|
#end def store_hands_players_stud
|
||||||
|
@ -1358,7 +1431,7 @@ class Database:
|
||||||
def store_hands_players_holdem_omaha_tourney(self, backend, category, hands_id, player_ids
|
def store_hands_players_holdem_omaha_tourney(self, backend, category, hands_id, player_ids
|
||||||
,start_cashes, positions, card_values, card_suits
|
,start_cashes, positions, card_values, card_suits
|
||||||
,winnings, rakes, seatNos, tourneys_players_ids
|
,winnings, rakes, seatNos, tourneys_players_ids
|
||||||
,hudCache):
|
,hudCache, tourneyTypeId):
|
||||||
#stores hands_players for tourney holdem/omaha hands
|
#stores hands_players for tourney holdem/omaha hands
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -1380,7 +1453,7 @@ class Database:
|
||||||
else:
|
else:
|
||||||
raise FpdbError ("invalid card_values length:"+str(len(card_values[0])))
|
raise FpdbError ("invalid card_values length:"+str(len(card_values[0])))
|
||||||
|
|
||||||
inserts.append( (hands_id, player_ids[i], start_cashes[i], positions[i], 1, # tourneytypeid
|
inserts.append( (hands_id, player_ids[i], start_cashes[i], positions[i], tourneyTypeId,
|
||||||
card1, card2, card3, card4, startCards,
|
card1, card2, card3, card4, startCards,
|
||||||
winnings[i], rakes[i], tourneys_players_ids[i], seatNos[i], hudCache['totalProfit'][i],
|
winnings[i], rakes[i], tourneys_players_ids[i], seatNos[i], hudCache['totalProfit'][i],
|
||||||
hudCache['street0VPI'][i], hudCache['street0Aggr'][i],
|
hudCache['street0VPI'][i], hudCache['street0Aggr'][i],
|
||||||
|
@ -1443,13 +1516,13 @@ class Database:
|
||||||
#cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i]))
|
#cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i]))
|
||||||
#result.append(cursor.fetchall()[0][0])
|
#result.append(cursor.fetchall()[0][0])
|
||||||
except:
|
except:
|
||||||
raise fpdb_simple.FpdbError( "store_hands_players_holdem_omaha_tourney error: " + str(sys.exc_value) )
|
raise FpdbError( "store_hands_players_holdem_omaha_tourney error: " + str(sys.exc_value) )
|
||||||
|
|
||||||
return result
|
return result
|
||||||
#end def store_hands_players_holdem_omaha_tourney
|
#end def store_hands_players_holdem_omaha_tourney
|
||||||
|
|
||||||
def store_hands_players_stud_tourney(self, backend, hands_id, player_ids, start_cashes,
|
def store_hands_players_stud_tourney(self, backend, hands_id, player_ids, start_cashes,
|
||||||
antes, card_values, card_suits, winnings, rakes, seatNos, tourneys_players_ids):
|
antes, card_values, card_suits, winnings, rakes, seatNos, tourneys_players_ids, tourneyTypeId):
|
||||||
#stores hands_players for tourney stud/razz hands
|
#stores hands_players for tourney stud/razz hands
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -1461,19 +1534,19 @@ class Database:
|
||||||
card1Value, card1Suit, card2Value, card2Suit,
|
card1Value, card1Suit, card2Value, card2Suit,
|
||||||
card3Value, card3Suit, card4Value, card4Suit,
|
card3Value, card3Suit, card4Value, card4Suit,
|
||||||
card5Value, card5Suit, card6Value, card6Suit,
|
card5Value, card5Suit, card6Value, card6Suit,
|
||||||
card7Value, card7Suit, winnings, rake, tourneysPlayersId, seatNo)
|
card7Value, card7Suit, winnings, rake, tourneysPlayersId, seatNo, tourneyTypeId)
|
||||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
|
||||||
%s, %s, %s, %s, %s, %s)""".replace('%s', self.sql.query['placeholder']),
|
%s, %s, %s, %s, %s, %s, %s)""".replace('%s', self.sql.query['placeholder']),
|
||||||
(hands_id, player_ids[i], start_cashes[i], antes[i],
|
(hands_id, player_ids[i], start_cashes[i], antes[i],
|
||||||
card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1],
|
card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1],
|
||||||
card_values[i][2], card_suits[i][2], card_values[i][3], card_suits[i][3],
|
card_values[i][2], card_suits[i][2], card_values[i][3], card_suits[i][3],
|
||||||
card_values[i][4], card_suits[i][4], card_values[i][5], card_suits[i][5],
|
card_values[i][4], card_suits[i][4], card_values[i][5], card_suits[i][5],
|
||||||
card_values[i][6], card_suits[i][6], winnings[i], rakes[i], tourneys_players_ids[i], seatNos[i]))
|
card_values[i][6], card_suits[i][6], winnings[i], rakes[i], tourneys_players_ids[i], seatNos[i], tourneyTypeId))
|
||||||
#cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i]))
|
#cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i]))
|
||||||
#result.append(cursor.fetchall()[0][0])
|
#result.append(cursor.fetchall()[0][0])
|
||||||
result.append( self.get_last_insert_id(c) )
|
result.append( self.get_last_insert_id(c) )
|
||||||
except:
|
except:
|
||||||
raise fpdb_simple.FpdbError( "store_hands_players_stud_tourney error: " + str(sys.exc_value) )
|
raise FpdbError( "store_hands_players_stud_tourney error: " + str(sys.exc_value) )
|
||||||
|
|
||||||
return result
|
return result
|
||||||
#end def store_hands_players_stud_tourney
|
#end def store_hands_players_stud_tourney
|
||||||
|
@ -1670,7 +1743,7 @@ class Database:
|
||||||
# print "todo: implement storeHudCache for stud base"
|
# print "todo: implement storeHudCache for stud base"
|
||||||
|
|
||||||
except:
|
except:
|
||||||
raise fpdb_simple.FpdbError( "storeHudCache error: " + str(sys.exc_value) )
|
raise FpdbError( "storeHudCache error: " + str(sys.exc_value) )
|
||||||
|
|
||||||
#end def storeHudCache
|
#end def storeHudCache
|
||||||
|
|
||||||
|
@ -1693,7 +1766,7 @@ class Database:
|
||||||
tmp=cursor.fetchone()
|
tmp=cursor.fetchone()
|
||||||
#print "created new tourneys.id:",tmp
|
#print "created new tourneys.id:",tmp
|
||||||
except:
|
except:
|
||||||
raise fpdb_simple.FpdbError( "store_tourneys error: " + str(sys.exc_value) )
|
raise FpdbError( "store_tourneys error: " + str(sys.exc_value) )
|
||||||
|
|
||||||
return tmp[0]
|
return tmp[0]
|
||||||
#end def store_tourneys
|
#end def store_tourneys
|
||||||
|
@ -1726,7 +1799,7 @@ class Database:
|
||||||
#print "created new tourneys_players.id:",tmp
|
#print "created new tourneys_players.id:",tmp
|
||||||
result.append(tmp[0])
|
result.append(tmp[0])
|
||||||
except:
|
except:
|
||||||
raise fpdb_simple.FpdbError( "store_tourneys_players error: " + str(sys.exc_value) )
|
raise FpdbError( "store_tourneys_players error: " + str(sys.exc_value) )
|
||||||
|
|
||||||
return result
|
return result
|
||||||
#end def store_tourneys_players
|
#end def store_tourneys_players
|
||||||
|
@ -1807,6 +1880,236 @@ class Database:
|
||||||
print "***Error sending finish: "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1])
|
print "***Error sending finish: "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1])
|
||||||
# end def send_finish_msg():
|
# end def send_finish_msg():
|
||||||
|
|
||||||
|
def tRecogniseTourneyType(self, tourney):
|
||||||
|
logging.debug("Database.tRecogniseTourneyType")
|
||||||
|
typeId = 1
|
||||||
|
# Check if Tourney exists, and if so retrieve TTypeId : in that case, check values of the ttype
|
||||||
|
cursor = self.get_cursor()
|
||||||
|
cursor.execute (self.sql.query['getTourneyTypeIdByTourneyNo'].replace('%s', self.sql.query['placeholder']),
|
||||||
|
(tourney.tourNo, tourney.siteId)
|
||||||
|
)
|
||||||
|
result=cursor.fetchone()
|
||||||
|
|
||||||
|
expectedValues = { 1 : "buyin", 2 : "fee", 4 : "isKO", 5 : "isRebuy", 6 : "speed",
|
||||||
|
7 : "isHU", 8 : "isShootout", 9 : "isMatrix" }
|
||||||
|
typeIdMatch = True
|
||||||
|
|
||||||
|
try:
|
||||||
|
len(result)
|
||||||
|
typeId = result[0]
|
||||||
|
logging.debug("Tourney found in db with Tourney_Type_ID = %d" % typeId)
|
||||||
|
for ev in expectedValues :
|
||||||
|
if ( getattr( tourney, expectedValues.get(ev) ) <> result[ev] ):
|
||||||
|
logging.debug("TypeId mismatch : wrong %s : Tourney=%s / db=%s" % (expectedValues.get(ev), getattr( tourney, expectedValues.get(ev)), result[ev]) )
|
||||||
|
typeIdMatch = False
|
||||||
|
#break
|
||||||
|
except:
|
||||||
|
# Tourney not found : a TourneyTypeId has to be found or created for that specific tourney
|
||||||
|
typeIdMatch = False
|
||||||
|
|
||||||
|
if typeIdMatch == False :
|
||||||
|
# Check for an existing TTypeId that matches tourney info (buyin/fee, knockout, rebuy, speed, matrix, shootout)
|
||||||
|
# if not found create it
|
||||||
|
logging.debug("Searching for a TourneyTypeId matching TourneyType data")
|
||||||
|
cursor.execute (self.sql.query['getTourneyTypeId'].replace('%s', self.sql.query['placeholder']),
|
||||||
|
(tourney.siteId, tourney.buyin, tourney.fee, tourney.isKO,
|
||||||
|
tourney.isRebuy, tourney.speed, tourney.isHU, tourney.isShootout, tourney.isMatrix)
|
||||||
|
)
|
||||||
|
result=cursor.fetchone()
|
||||||
|
|
||||||
|
try:
|
||||||
|
len(result)
|
||||||
|
typeId = result[0]
|
||||||
|
logging.debug("Existing Tourney Type Id found : %d" % typeId)
|
||||||
|
except TypeError: #this means we need to create a new entry
|
||||||
|
logging.debug("Tourney Type Id not found : create one")
|
||||||
|
cursor.execute (self.sql.query['insertTourneyTypes'].replace('%s', self.sql.query['placeholder']),
|
||||||
|
(tourney.siteId, tourney.buyin, tourney.fee, tourney.isKO, tourney.isRebuy,
|
||||||
|
tourney.speed, tourney.isHU, tourney.isShootout, tourney.isMatrix)
|
||||||
|
)
|
||||||
|
typeId = self.get_last_insert_id(cursor)
|
||||||
|
|
||||||
|
return typeId
|
||||||
|
#end def tRecogniseTourneyType
|
||||||
|
|
||||||
|
|
||||||
|
def tRecognizeTourney(self, tourney, dbTourneyTypeId):
|
||||||
|
logging.debug("Database.tRecognizeTourney")
|
||||||
|
tourneyID = 1
|
||||||
|
# Check if tourney exists in db (based on tourney.siteId and tourney.tourNo)
|
||||||
|
# If so retrieve all data to check for consistency
|
||||||
|
cursor = self.get_cursor()
|
||||||
|
cursor.execute (self.sql.query['getTourney'].replace('%s', self.sql.query['placeholder']),
|
||||||
|
(tourney.tourNo, tourney.siteId)
|
||||||
|
)
|
||||||
|
result=cursor.fetchone()
|
||||||
|
|
||||||
|
expectedValuesDecimal = { 2 : "entries", 3 : "prizepool", 6 : "buyInChips", 9 : "rebuyChips",
|
||||||
|
10 : "addOnChips", 11 : "rebuyAmount", 12 : "addOnAmount", 13 : "totalRebuys",
|
||||||
|
14 : "totalAddOns", 15 : "koBounty" }
|
||||||
|
expectedValues = { 7 : "tourneyName", 16 : "tourneyComment" }
|
||||||
|
|
||||||
|
tourneyDataMatch = True
|
||||||
|
tCommentTs = None
|
||||||
|
starttime = None
|
||||||
|
endtime = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
len(result)
|
||||||
|
tourneyID = result[0]
|
||||||
|
logging.debug("Tourney found in db with TourneyID = %d" % tourneyID)
|
||||||
|
if result[1] <> dbTourneyTypeId:
|
||||||
|
tourneyDataMatch = False
|
||||||
|
logging.debug("Tourney has wrong type ID (expected : %s - found : %s)" % (dbTourneyTypeId, result[1]))
|
||||||
|
if (tourney.starttime is None and result[4] is not None) or ( tourney.starttime is not None and fpdb_simple.parseHandStartTime("- %s" % tourney.starttime) <> result[4]) :
|
||||||
|
tourneyDataMatch = False
|
||||||
|
logging.debug("Tourney data mismatch : wrong starttime : Tourney=%s / db=%s" % (tourney.starttime, result[4]))
|
||||||
|
if (tourney.endtime is None and result[5] is not None) or ( tourney.endtime is not None and fpdb_simple.parseHandStartTime("- %s" % tourney.endtime) <> result[5]) :
|
||||||
|
tourneyDataMatch = False
|
||||||
|
logging.debug("Tourney data mismatch : wrong endtime : Tourney=%s / db=%s" % (tourney.endtime, result[5]))
|
||||||
|
|
||||||
|
for ev in expectedValues :
|
||||||
|
if ( getattr( tourney, expectedValues.get(ev) ) <> result[ev] ):
|
||||||
|
logging.debug("Tourney data mismatch : wrong %s : Tourney=%s / db=%s" % (expectedValues.get(ev), getattr( tourney, expectedValues.get(ev)), result[ev]) )
|
||||||
|
tourneyDataMatch = False
|
||||||
|
#break
|
||||||
|
for evD in expectedValuesDecimal :
|
||||||
|
if ( Decimal(getattr( tourney, expectedValuesDecimal.get(evD)) ) <> result[evD] ):
|
||||||
|
logging.debug("Tourney data mismatch : wrong %s : Tourney=%s / db=%s" % (expectedValuesDecimal.get(evD), getattr( tourney, expectedValuesDecimal.get(evD)), result[evD]) )
|
||||||
|
tourneyDataMatch = False
|
||||||
|
#break
|
||||||
|
|
||||||
|
# TO DO : Deal with matrix summary mutliple parsings
|
||||||
|
|
||||||
|
except:
|
||||||
|
# Tourney not found : create
|
||||||
|
logging.debug("Tourney is not found : create")
|
||||||
|
if tourney.tourneyComment is not None :
|
||||||
|
tCommentTs = datetime.today()
|
||||||
|
if tourney.starttime is not None :
|
||||||
|
starttime = fpdb_simple.parseHandStartTime("- %s" % tourney.starttime)
|
||||||
|
if tourney.endtime is not None :
|
||||||
|
endtime = fpdb_simple.parseHandStartTime("- %s" % tourney.endtime)
|
||||||
|
# TODO : deal with matrix Id processed
|
||||||
|
cursor.execute (self.sql.query['insertTourney'].replace('%s', self.sql.query['placeholder']),
|
||||||
|
(dbTourneyTypeId, tourney.tourNo, tourney.entries, tourney.prizepool, starttime,
|
||||||
|
endtime, tourney.buyInChips, tourney.tourneyName, 0, tourney.rebuyChips, tourney.addOnChips,
|
||||||
|
tourney.rebuyAmount, tourney.addOnAmount, tourney.totalRebuys, tourney.totalAddOns, tourney.koBounty,
|
||||||
|
tourney.tourneyComment, tCommentTs)
|
||||||
|
)
|
||||||
|
tourneyID = self.get_last_insert_id(cursor)
|
||||||
|
|
||||||
|
|
||||||
|
# Deal with inconsistent tourney in db
|
||||||
|
if tourneyDataMatch == False :
|
||||||
|
# Update Tourney
|
||||||
|
if result[16] <> tourney.tourneyComment :
|
||||||
|
tCommentTs = datetime.today()
|
||||||
|
if tourney.starttime is not None :
|
||||||
|
starttime = fpdb_simple.parseHandStartTime("- %s" % tourney.starttime)
|
||||||
|
if tourney.endtime is not None :
|
||||||
|
endtime = fpdb_simple.parseHandStartTime("- %s" % tourney.endtime)
|
||||||
|
|
||||||
|
cursor.execute (self.sql.query['updateTourney'].replace('%s', self.sql.query['placeholder']),
|
||||||
|
(dbTourneyTypeId, tourney.entries, tourney.prizepool, starttime,
|
||||||
|
endtime, tourney.buyInChips, tourney.tourneyName, 0, tourney.rebuyChips, tourney.addOnChips,
|
||||||
|
tourney.rebuyAmount, tourney.addOnAmount, tourney.totalRebuys, tourney.totalAddOns, tourney.koBounty,
|
||||||
|
tourney.tourneyComment, tCommentTs, tourneyID)
|
||||||
|
)
|
||||||
|
|
||||||
|
return tourneyID
|
||||||
|
#end def tRecognizeTourney
|
||||||
|
|
||||||
|
def tStoreTourneyPlayers(self, tourney, dbTourneyId):
|
||||||
|
logging.debug("Database.tStoreTourneyPlayers")
|
||||||
|
# First, get playerids for the players and specifically the one for hero :
|
||||||
|
playersIds = fpdb_simple.recognisePlayerIDs(self, tourney.players, tourney.siteId)
|
||||||
|
# hero may be None for matrix tourneys summaries
|
||||||
|
# hero = [ tourney.hero ]
|
||||||
|
# heroId = fpdb_simple.recognisePlayerIDs(self, hero , tourney.siteId)
|
||||||
|
# logging.debug("hero Id = %s - playersId = %s" % (heroId , playersIds))
|
||||||
|
|
||||||
|
tourneyPlayersIds=[]
|
||||||
|
try:
|
||||||
|
cursor = self.get_cursor()
|
||||||
|
|
||||||
|
for i in xrange(len(playersIds)):
|
||||||
|
cursor.execute(self.sql.query['getTourneysPlayers'].replace('%s', self.sql.query['placeholder'])
|
||||||
|
,(dbTourneyId, playersIds[i]))
|
||||||
|
result=cursor.fetchone()
|
||||||
|
#print "tried SELECTing tourneys_players.id:",tmp
|
||||||
|
|
||||||
|
try:
|
||||||
|
len(result)
|
||||||
|
# checking data
|
||||||
|
logging.debug("TourneysPlayers found : checking data")
|
||||||
|
expectedValuesDecimal = { 1 : "payinAmounts", 2 : "finishPositions", 3 : "winnings", 4 : "countRebuys",
|
||||||
|
5 : "countAddOns", 6 : "countKO" }
|
||||||
|
|
||||||
|
tourneyPlayersIds.append(result[0]);
|
||||||
|
|
||||||
|
tourneysPlayersDataMatch = True
|
||||||
|
for evD in expectedValuesDecimal :
|
||||||
|
if ( Decimal(getattr( tourney, expectedValuesDecimal.get(evD))[tourney.players[i]] ) <> result[evD] ):
|
||||||
|
logging.debug("TourneysPlayers data mismatch for TourneysPlayer id=%d, name=%s : wrong %s : Tourney=%s / db=%s" % (result[0], tourney.players[i], expectedValuesDecimal.get(evD), getattr( tourney, expectedValuesDecimal.get(evD))[tourney.players[i]], result[evD]) )
|
||||||
|
tourneysPlayersDataMatch = False
|
||||||
|
#break
|
||||||
|
|
||||||
|
if tourneysPlayersDataMatch == False:
|
||||||
|
logging.debug("TourneysPlayers data update needed")
|
||||||
|
cursor.execute (self.sql.query['updateTourneysPlayers'].replace('%s', self.sql.query['placeholder']),
|
||||||
|
(tourney.payinAmounts[tourney.players[i]], tourney.finishPositions[tourney.players[i]],
|
||||||
|
tourney.winnings[tourney.players[i]] , tourney.countRebuys[tourney.players[i]],
|
||||||
|
tourney.countAddOns[tourney.players[i]] , tourney.countKO[tourney.players[i]],
|
||||||
|
result[7], result[8], result[0])
|
||||||
|
)
|
||||||
|
|
||||||
|
except TypeError:
|
||||||
|
logging.debug("TourneysPlayers not found : need insert")
|
||||||
|
cursor.execute (self.sql.query['insertTourneysPlayers'].replace('%s', self.sql.query['placeholder']),
|
||||||
|
(dbTourneyId, playersIds[i],
|
||||||
|
tourney.payinAmounts[tourney.players[i]], tourney.finishPositions[tourney.players[i]],
|
||||||
|
tourney.winnings[tourney.players[i]] , tourney.countRebuys[tourney.players[i]],
|
||||||
|
tourney.countAddOns[tourney.players[i]] , tourney.countKO[tourney.players[i]],
|
||||||
|
None, None)
|
||||||
|
)
|
||||||
|
tourneyPlayersIds.append(self.get_last_insert_id(cursor))
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise fpdb_simple.FpdbError( "tStoreTourneyPlayers error: " + str(sys.exc_value) )
|
||||||
|
|
||||||
|
return tourneyPlayersIds
|
||||||
|
#end def tStoreTourneyPlayers
|
||||||
|
|
||||||
|
def tUpdateTourneysHandsPlayers(self, tourney, dbTourneysPlayersIds, dbTourneyTypeId):
|
||||||
|
logging.debug("Database.tCheckTourneysHandsPlayers")
|
||||||
|
try:
|
||||||
|
# Massive update seems to take quite some time ...
|
||||||
|
# query = self.sql.query['updateHandsPlayersForTTypeId2'] % (dbTourneyTypeId, self.sql.query['handsPlayersTTypeId_joiner'].join([self.sql.query['placeholder'] for id in dbTourneysPlayersIds]) )
|
||||||
|
# cursor = self.get_cursor()
|
||||||
|
# cursor.execute (query, dbTourneysPlayersIds)
|
||||||
|
|
||||||
|
query = self.sql.query['selectHandsPlayersWithWrongTTypeId'] % (dbTourneyTypeId, self.sql.query['handsPlayersTTypeId_joiner'].join([self.sql.query['placeholder'] for id in dbTourneysPlayersIds]) )
|
||||||
|
#print "query : %s" % query
|
||||||
|
cursor = self.get_cursor()
|
||||||
|
cursor.execute (query, dbTourneysPlayersIds)
|
||||||
|
result=cursor.fetchall()
|
||||||
|
|
||||||
|
if (len(result) > 0):
|
||||||
|
logging.debug("%d lines need update : %s" % (len(result), result) )
|
||||||
|
listIds = []
|
||||||
|
for i in result:
|
||||||
|
listIds.append(i[0])
|
||||||
|
|
||||||
|
query2 = self.sql.query['updateHandsPlayersForTTypeId'] % (dbTourneyTypeId, self.sql.query['handsPlayersTTypeId_joiner_id'].join([self.sql.query['placeholder'] for id in listIds]) )
|
||||||
|
cursor.execute (query2, listIds)
|
||||||
|
else:
|
||||||
|
logging.debug("No need to update, HandsPlayers are correct")
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise fpdb_simple.FpdbError( "tStoreTourneyPlayers error: " + str(sys.exc_value) )
|
||||||
|
#end def tUpdateTourneysHandsPlayers
|
||||||
|
|
||||||
|
|
||||||
# Class used to hold all the data needed to write a hand to the db
|
# Class used to hold all the data needed to write a hand to the db
|
||||||
# mainParser() in fpdb_parse_logic.py creates one of these and then passes it to
|
# mainParser() in fpdb_parse_logic.py creates one of these and then passes it to
|
||||||
|
|
|
@ -26,6 +26,11 @@ from HandHistoryConverter import *
|
||||||
|
|
||||||
class Everleaf(HandHistoryConverter):
|
class Everleaf(HandHistoryConverter):
|
||||||
|
|
||||||
|
sitename = 'Everleaf'
|
||||||
|
filetype = "text"
|
||||||
|
codepage = "cp1252"
|
||||||
|
siteId = 3 # Needs to match id entry in Sites database
|
||||||
|
|
||||||
# Static regexes
|
# Static regexes
|
||||||
re_SplitHands = re.compile(r"\n\n\n+")
|
re_SplitHands = re.compile(r"\n\n\n+")
|
||||||
re_TailSplitHands = 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>.+) \]")
|
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):
|
def compilePlayerRegexs(self, hand):
|
||||||
players = set([player[1] for player in hand.players])
|
players = set([player[1] for player in hand.players])
|
||||||
if not players <= self.compiledPlayers: # x <= y means 'x is subset of y'
|
if not players <= self.compiledPlayers: # x <= y means 'x is subset of y'
|
||||||
|
|
|
@ -1 +1,21 @@
|
||||||
class FpdbParseError(Exception): pass
|
class FpdbError(Exception):
|
||||||
|
def __init__(self, value):
|
||||||
|
self.value = value
|
||||||
|
def __str__(self):
|
||||||
|
return repr(self.value)
|
||||||
|
|
||||||
|
class FpdbParseError(FpdbError):
|
||||||
|
def __init__(self,value='',hid=''):
|
||||||
|
self.value = value
|
||||||
|
self.hid = hid
|
||||||
|
def __str__(self):
|
||||||
|
if hid:
|
||||||
|
return repr("HID:"+hid+", "+self.value)
|
||||||
|
else:
|
||||||
|
return repr(self.value)
|
||||||
|
|
||||||
|
class FpdbDatabaseError(FpdbError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class DuplicateError(FpdbError):
|
||||||
|
pass
|
||||||
|
|
|
@ -27,8 +27,14 @@ from HandHistoryConverter import *
|
||||||
|
|
||||||
class Fulltilt(HandHistoryConverter):
|
class Fulltilt(HandHistoryConverter):
|
||||||
|
|
||||||
|
sitename = "Fulltilt"
|
||||||
|
filetype = "text"
|
||||||
|
codepage = ["utf-16", "cp1252"]
|
||||||
|
siteId = 1 # Needs to match id entry in Sites database
|
||||||
|
|
||||||
# Static regexes
|
# 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>\$|)?
|
-\s(?P<CURRENCY>\$|)?
|
||||||
(?P<SB>[.0-9]+)/
|
(?P<SB>[.0-9]+)/
|
||||||
|
@ -39,39 +45,79 @@ class Fulltilt(HandHistoryConverter):
|
||||||
''', re.VERBOSE)
|
''', re.VERBOSE)
|
||||||
re_SplitHands = re.compile(r"\n\n+")
|
re_SplitHands = re.compile(r"\n\n+")
|
||||||
re_TailSplitHands = 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)?
|
(?:(?P<TOURNAMENT>.+)\s\((?P<TOURNO>\d+)\),\s)?
|
||||||
Table\s
|
(Table|Match)\s
|
||||||
(?P<PLAY>Play\sChip\s|PC)?
|
(?P<PLAY>Play\sChip\s|PC)?
|
||||||
(?P<TABLE>[-\s\da-zA-Z]+)\s
|
(?P<TABLE>[-\s\da-zA-Z]+)\s
|
||||||
(\((?P<TABLEATTRIBUTES>.+)\)\s)?-\s
|
(\((?P<TABLEATTRIBUTES>.+)\)\s)?-\s
|
||||||
\$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+)\s(Ante\s\$?(?P<ANTE>[.0-9]+)\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<GAMETYPE>[a-zA-Z\/\'\s]+)\s-\s
|
||||||
(?P<DATETIME>.*)
|
(?P<DATETIME>\d+:\d+:\d+\s\w+\s-\s\d+/\d+/\d+)\s?
|
||||||
''', re.VERBOSE)
|
(?P<PARTIAL>\(partial\))?\n
|
||||||
|
(?:.*?\n(?P<CANCELLED>Hand\s\#(?P=HID)\shas\sbeen\scanceled))?
|
||||||
|
''', re.VERBOSE|re.DOTALL)
|
||||||
|
re_TourneyExtraInfo = re.compile('''(((?P<TOURNEY_NAME>[^$]+)?
|
||||||
|
(?P<CURRENCY>\$)?(?P<BUYIN>[.0-9]+)?\s*\+\s*\$?(?P<FEE>[.0-9]+)?
|
||||||
|
(\s(?P<SPECIAL>(KO|Heads\sUp|Matrix\s\dx|Rebuy|Madness)))?
|
||||||
|
(\s(?P<SHOOTOUT>Shootout))?
|
||||||
|
(\s(?P<SNG>Sit\s&\sGo))?
|
||||||
|
(\s\((?P<TURBO>Turbo)\))?)|(?P<UNREADABLE_INFO>.+))
|
||||||
|
''', re.VERBOSE)
|
||||||
re_Button = re.compile('^The button is in seat #(?P<BUTTON>\d+)', re.MULTILINE)
|
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_PlayerInfo = re.compile('Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \(\$(?P<CASH>[,.0-9]+)\)$', re.MULTILINE)
|
||||||
|
re_TourneyPlayerInfo = re.compile('Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \(\$?(?P<CASH>[,.0-9]+)\)', re.MULTILINE)
|
||||||
re_Board = re.compile(r"\[(?P<CARDS>.+)\]")
|
re_Board = re.compile(r"\[(?P<CARDS>.+)\]")
|
||||||
|
|
||||||
|
#static regex for tourney purpose
|
||||||
|
re_TourneyInfo = re.compile('''Tournament\sSummary\s
|
||||||
|
(?P<TOURNAMENT_NAME>[^$(]+)?\s*
|
||||||
|
((?P<CURRENCY>\$|)?(?P<BUYIN>[.0-9]+)\s*\+\s*\$?(?P<FEE>[.0-9]+)\s)?
|
||||||
|
((?P<SPECIAL>(KO|Heads\sUp|Matrix\s\dx|Rebuy|Madness))\s)?
|
||||||
|
((?P<SHOOTOUT>Shootout)\s)?
|
||||||
|
((?P<SNG>Sit\s&\sGo)\s)?
|
||||||
|
(\((?P<TURBO1>Turbo)\)\s)?
|
||||||
|
\((?P<TOURNO>\d+)\)\s
|
||||||
|
((?P<MATCHNO>Match\s\d)\s)?
|
||||||
|
(?P<GAME>(Hold\'em|Omaha\sHi|Omaha\sH/L|7\sCard\sStud|Stud\sH/L|Razz|Stud\sHi))\s
|
||||||
|
(\((?P<TURBO2>Turbo)\)\s)?
|
||||||
|
(?P<LIMIT>(No\sLimit|Pot\sLimit|Limit))?
|
||||||
|
''', re.VERBOSE)
|
||||||
|
re_TourneyBuyInFee = re.compile("Buy-In: (?P<BUYIN_CURRENCY>\$|)?(?P<BUYIN>[.0-9]+) \+ \$?(?P<FEE>[.0-9]+)")
|
||||||
|
re_TourneyBuyInChips = re.compile("Buy-In Chips: (?P<BUYINCHIPS>\d+)")
|
||||||
|
re_TourneyEntries = re.compile("(?P<ENTRIES>\d+) Entries")
|
||||||
|
re_TourneyPrizePool = re.compile("Total Prize Pool: (?P<PRIZEPOOL_CURRENCY>\$|)?(?P<PRIZEPOOL>[.,0-9]+)")
|
||||||
|
re_TourneyRebuyAmount = re.compile("Rebuy: (?P<REBUY_CURRENCY>\$|)?(?P<REBUY_AMOUNT>[.,0-9]+)")
|
||||||
|
re_TourneyAddOnAmount = re.compile("Add-On: (?P<ADDON_CURRENCY>\$|)?(?P<ADDON_AMOUNT>[.,0-9]+)")
|
||||||
|
re_TourneyRebuyCount = re.compile("performed (?P<REBUY_COUNT>\d+) Rebuy")
|
||||||
|
re_TourneyAddOnCount = re.compile("performed (?P<ADDON_COUNT>\d+) Add-On")
|
||||||
|
re_TourneyRebuysTotal = re.compile("Total Rebuys: (?P<REBUY_TOTAL>\d+)")
|
||||||
|
re_TourneyAddOnsTotal = re.compile("Total Add-Ons: (?P<ADDONS_TOTAL>\d+)")
|
||||||
|
re_TourneyRebuyChips = re.compile("Rebuy Chips: (?P<REBUY_CHIPS>\d+)")
|
||||||
|
re_TourneyAddOnChips = re.compile("Add-On Chips: (?P<ADDON_CHIPS>\d+)")
|
||||||
|
re_TourneyKOBounty = re.compile("Knockout Bounty: (?P<KO_BOUNTY_CURRENCY>\$|)?(?P<KO_BOUNTY_AMOUNT>[.,0-9]+)")
|
||||||
|
re_TourneyCountKO = re.compile("received (?P<COUNT_KO>\d+) Knockout Bounty Award(s)?")
|
||||||
|
re_TourneyTimeInfo = re.compile("Tournament started: (?P<STARTTIME>.*)\nTournament ((?P<IN_PROGRESS>is still in progress)?|(finished:(?P<ENDTIME>.*))?)$")
|
||||||
|
|
||||||
|
re_TourneyPlayersSummary = re.compile("^(?P<RANK>(Still Playing|\d+))( - |: )(?P<PNAME>[^\n,]+)(, )?(?P<WINNING_CURRENCY>\$|)?(?P<WINNING>[.\d]+)?", re.MULTILINE)
|
||||||
|
re_TourneyHeroFinishingP = re.compile("(?P<HERO_NAME>.*) finished in (?P<HERO_FINISHING_POS>\d+)(st|nd|rd|th) place")
|
||||||
|
|
||||||
|
#TODO: See if we need to deal with play money tourney summaries -- Not right now (they shouldn't pass the re_TourneyInfo)
|
||||||
|
##Full Tilt Poker Tournament Summary 250 Play Money Sit & Go (102909471) Hold'em No Limit
|
||||||
|
##Buy-In: 250 Play Chips + 0 Play Chips
|
||||||
|
##Buy-In Chips: 1500
|
||||||
|
##6 Entries
|
||||||
|
##Total Prize Pool: 1,500 Play Chips
|
||||||
|
|
||||||
# These regexes are for FTP only
|
# These regexes are for FTP only
|
||||||
re_Mixed = re.compile(r'\s\-\s(?P<MIXED>HA|HORSE|HOSE)\s\-\s', re.VERBOSE)
|
re_Mixed = re.compile(r'\s\-\s(?P<MIXED>HA|HORSE|HOSE)\s\-\s', re.VERBOSE)
|
||||||
re_Max = re.compile("(?P<MAX>\d+)( max)?", re.MULTILINE)
|
re_Max = re.compile("(?P<MAX>\d+)( max)?", re.MULTILINE)
|
||||||
# NB: if we ever match "Full Tilt Poker" we should also match "FullTiltPoker", which PT Stud erroneously exports.
|
# NB: if we ever match "Full Tilt Poker" we should also match "FullTiltPoker", which PT Stud erroneously exports.
|
||||||
|
|
||||||
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)
|
mixes = { 'HORSE': 'horse', '7-Game': '7game', 'HOSE': 'hose', 'HA': 'ha'}
|
||||||
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):
|
def compilePlayerRegexs(self, hand):
|
||||||
|
@ -89,7 +135,7 @@ follow : whether to tail -f the input"""
|
||||||
self.re_HeroCards = re.compile(r"^Dealt to %s(?: \[(?P<OLDCARDS>.+?)\])?( \[(?P<NEWCARDS>.+?)\])" % player_re, re.MULTILINE)
|
self.re_HeroCards = re.compile(r"^Dealt to %s(?: \[(?P<OLDCARDS>.+?)\])?( \[(?P<NEWCARDS>.+?)\])" % player_re, re.MULTILINE)
|
||||||
self.re_Action = re.compile(r"^%s(?P<ATYPE> bets| checks| raises to| completes it to| calls| folds)( \$?(?P<BET>[.,\d]+))?" % player_re, re.MULTILINE)
|
self.re_Action = re.compile(r"^%s(?P<ATYPE> bets| checks| raises to| completes it to| calls| folds)( \$?(?P<BET>[.,\d]+))?" % player_re, re.MULTILINE)
|
||||||
self.re_ShowdownAction = re.compile(r"^%s shows \[(?P<CARDS>.*)\]" % player_re, re.MULTILINE)
|
self.re_ShowdownAction = re.compile(r"^%s shows \[(?P<CARDS>.*)\]" % player_re, re.MULTILINE)
|
||||||
self.re_CollectPot = re.compile(r"^Seat (?P<SEAT>[0-9]+): %s (\(button\) |\(small blind\) |\(big blind\) )?(collected|showed \[.*\] and won) \(\$(?P<POT>[.\d]+)\)(, mucked| with.*)" % player_re, re.MULTILINE)
|
self.re_CollectPot = re.compile(r"^Seat (?P<SEAT>[0-9]+): %s (\(button\) |\(small blind\) |\(big blind\) )?(collected|showed \[.*\] and won) \(\$?(?P<POT>[.,\d]+)\)(, mucked| with.*)" % player_re, re.MULTILINE)
|
||||||
self.re_SitsOut = re.compile(r"^%s sits out" % player_re, re.MULTILINE)
|
self.re_SitsOut = re.compile(r"^%s sits out" % player_re, re.MULTILINE)
|
||||||
self.re_ShownCards = re.compile(r"^Seat (?P<SEAT>[0-9]+): %s \(.*\) showed \[(?P<CARDS>.*)\].*" % player_re, re.MULTILINE)
|
self.re_ShownCards = re.compile(r"^Seat (?P<SEAT>[0-9]+): %s \(.*\) showed \[(?P<CARDS>.*)\].*" % player_re, re.MULTILINE)
|
||||||
|
|
||||||
|
@ -118,7 +164,6 @@ follow : whether to tail -f the input"""
|
||||||
if not m:
|
if not m:
|
||||||
return None
|
return None
|
||||||
mg = m.groupdict()
|
mg = m.groupdict()
|
||||||
|
|
||||||
# translations from captured groups to our info strings
|
# translations from captured groups to our info strings
|
||||||
limits = { 'No Limit':'nl', 'Pot Limit':'pl', 'Limit':'fl' }
|
limits = { 'No Limit':'nl', 'Pot Limit':'pl', 'Limit':'fl' }
|
||||||
games = { # base, category
|
games = { # base, category
|
||||||
|
@ -140,29 +185,11 @@ follow : whether to tail -f the input"""
|
||||||
if mg['TOURNO'] == None: info['type'] = "ring"
|
if mg['TOURNO'] == None: info['type'] = "ring"
|
||||||
else: info['type'] = "tour"
|
else: info['type'] = "tour"
|
||||||
# NB: SB, BB must be interpreted as blinds or bets depending on limit type.
|
# 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
|
return info
|
||||||
|
|
||||||
#Following function is a hack, we should be dealing with this in readFile (i think correct codepage....)
|
|
||||||
# Same function as parent class, removing the 2 end characters. - CG
|
|
||||||
def allHandsAsList(self):
|
|
||||||
"""Return a list of handtexts in the file at self.in_path"""
|
|
||||||
#TODO : any need for this to be generator? e.g. stars support can email one huge file of all hands in a year. Better to read bit by bit than all at once.
|
|
||||||
self.readFile()
|
|
||||||
|
|
||||||
# FIXME: it's a hack
|
|
||||||
if self.obs[:2] == u'\xff\xfe':
|
|
||||||
self.obs = self.obs[2:].replace('\x00', '')
|
|
||||||
|
|
||||||
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
|
|
||||||
return re.split(self.re_SplitHands, self.obs)
|
|
||||||
|
|
||||||
def readHandInfo(self, hand):
|
def readHandInfo(self, hand):
|
||||||
m = self.re_HandInfo.search(hand.handText,re.DOTALL)
|
m = self.re_HandInfo.search(hand.handText)
|
||||||
if(m == None):
|
if(m == None):
|
||||||
logging.info("Didn't match re_HandInfo")
|
logging.info("Didn't match re_HandInfo")
|
||||||
logging.info(hand.handText)
|
logging.info(hand.handText)
|
||||||
|
@ -170,6 +197,10 @@ follow : whether to tail -f the input"""
|
||||||
hand.handid = m.group('HID')
|
hand.handid = m.group('HID')
|
||||||
hand.tablename = m.group('TABLE')
|
hand.tablename = m.group('TABLE')
|
||||||
hand.starttime = datetime.datetime.strptime(m.group('DATETIME'), "%H:%M:%S ET - %Y/%m/%d")
|
hand.starttime = datetime.datetime.strptime(m.group('DATETIME'), "%H:%M:%S ET - %Y/%m/%d")
|
||||||
|
|
||||||
|
if m.group("CANCELLED") or m.group("PARTIAL"):
|
||||||
|
raise FpdbParseError(hid=m.group('HID'))
|
||||||
|
|
||||||
if m.group('TABLEATTRIBUTES'):
|
if m.group('TABLEATTRIBUTES'):
|
||||||
m2 = self.re_Max.search(m.group('TABLEATTRIBUTES'))
|
m2 = self.re_Max.search(m.group('TABLEATTRIBUTES'))
|
||||||
if m2: hand.maxseats = int(m2.group('MAX'))
|
if m2: hand.maxseats = int(m2.group('MAX'))
|
||||||
|
@ -178,7 +209,31 @@ follow : whether to tail -f the input"""
|
||||||
if m.group('PLAY') != None:
|
if m.group('PLAY') != None:
|
||||||
hand.gametype['currency'] = 'play'
|
hand.gametype['currency'] = 'play'
|
||||||
|
|
||||||
# TODO: if there's a way to figure these out, we should.. otherwise we have to stuff it with unknowns
|
# Done: if there's a way to figure these out, we should.. otherwise we have to stuff it with unknowns
|
||||||
|
if m.group('TOURNAMENT') is not None:
|
||||||
|
n = self.re_TourneyExtraInfo.search(m.group('TOURNAMENT'))
|
||||||
|
if n.group('UNREADABLE_INFO') is not None:
|
||||||
|
hand.tourneyComment = n.group('UNREADABLE_INFO')
|
||||||
|
else:
|
||||||
|
hand.tourneyComment = n.group('TOURNEY_NAME') # can be None
|
||||||
|
if (n.group('CURRENCY') is not None and n.group('BUYIN') is not None and n.group('FEE') is not None):
|
||||||
|
hand.buyin = "%s%s+%s%s" %(n.group('CURRENCY'), n.group('BUYIN'), n.group('CURRENCY'), n.group('FEE'))
|
||||||
|
if n.group('TURBO') is not None :
|
||||||
|
hand.speed = "Turbo"
|
||||||
|
if n.group('SPECIAL') is not None :
|
||||||
|
special = n.group('SPECIAL')
|
||||||
|
if special == "Rebuy":
|
||||||
|
hand.isRebuy = True
|
||||||
|
if special == "KO":
|
||||||
|
hand.isKO = True
|
||||||
|
if special == "Head's Up":
|
||||||
|
hand.isHU = True
|
||||||
|
if re.search("Matrix", special):
|
||||||
|
hand.isMatrix = True
|
||||||
|
if special == "Shootout":
|
||||||
|
hand.isShootout = True
|
||||||
|
|
||||||
|
|
||||||
if hand.buyin == None:
|
if hand.buyin == None:
|
||||||
hand.buyin = "$0.00+$0.00"
|
hand.buyin = "$0.00+$0.00"
|
||||||
if hand.level == None:
|
if hand.level == None:
|
||||||
|
@ -200,7 +255,11 @@ follow : whether to tail -f the input"""
|
||||||
#FIXME: hand.buttonpos = int(m.group('BUTTON'))
|
#FIXME: hand.buttonpos = int(m.group('BUTTON'))
|
||||||
|
|
||||||
def readPlayerStacks(self, hand):
|
def readPlayerStacks(self, hand):
|
||||||
m = self.re_PlayerInfo.finditer(hand.handText)
|
if hand.gametype['type'] == "ring" :
|
||||||
|
m = self.re_PlayerInfo.finditer(hand.handText)
|
||||||
|
else: #if hand.gametype['type'] == "tour"
|
||||||
|
m = self.re_TourneyPlayerInfo.finditer(hand.handText)
|
||||||
|
|
||||||
players = []
|
players = []
|
||||||
for a in m:
|
for a in m:
|
||||||
hand.addPlayer(int(a.group('SEAT')), a.group('PNAME'), a.group('CASH'))
|
hand.addPlayer(int(a.group('SEAT')), a.group('PNAME'), a.group('CASH'))
|
||||||
|
@ -322,7 +381,7 @@ follow : whether to tail -f the input"""
|
||||||
|
|
||||||
def readCollectPot(self,hand):
|
def readCollectPot(self,hand):
|
||||||
for m in self.re_CollectPot.finditer(hand.handText):
|
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=re.sub(u',',u'',m.group('POT')))
|
||||||
|
|
||||||
def readShownCards(self,hand):
|
def readShownCards(self,hand):
|
||||||
for m in self.re_ShownCards.finditer(hand.handText):
|
for m in self.re_ShownCards.finditer(hand.handText):
|
||||||
|
@ -355,6 +414,253 @@ follow : whether to tail -f the input"""
|
||||||
else:
|
else:
|
||||||
hand.mixed = self.mixes[m.groupdict()['MIXED']]
|
hand.mixed = self.mixes[m.groupdict()['MIXED']]
|
||||||
|
|
||||||
|
def readSummaryInfo(self, summaryInfoList):
|
||||||
|
starttime = time.time()
|
||||||
|
self.status = True
|
||||||
|
|
||||||
|
m = re.search("Tournament Summary", summaryInfoList[0])
|
||||||
|
if m:
|
||||||
|
# info list should be 2 lines : Tourney infos & Finsihing postions with winnings
|
||||||
|
if (len(summaryInfoList) != 2 ):
|
||||||
|
log.info("Too many lines (%d) in file '%s' : '%s'" % (len(summaryInfoList), self.in_path, summaryInfoList) )
|
||||||
|
self.status = False
|
||||||
|
else:
|
||||||
|
self.tourney = Tourney.Tourney(sitename = self.sitename, gametype = None, summaryText = summaryInfoList, builtFrom = "HHC")
|
||||||
|
self.status = self.getPlayersPositionsAndWinnings(self.tourney)
|
||||||
|
if self.status == True :
|
||||||
|
self.status = self.determineTourneyType(self.tourney)
|
||||||
|
#print self.tourney
|
||||||
|
else:
|
||||||
|
log.info("Parsing NOK : rejected")
|
||||||
|
else:
|
||||||
|
log.info( "This is not a summary file : '%s'" % (self.in_path) )
|
||||||
|
self.status = False
|
||||||
|
|
||||||
|
return self.status
|
||||||
|
|
||||||
|
def determineTourneyType(self, tourney):
|
||||||
|
info = {'type':'tour'}
|
||||||
|
tourneyText = tourney.summaryText[0]
|
||||||
|
#print "Examine : '%s'" %(tourneyText)
|
||||||
|
|
||||||
|
m = self.re_TourneyInfo.search(tourneyText)
|
||||||
|
if not m:
|
||||||
|
log.info( "determineTourneyType : Parsing NOK" )
|
||||||
|
return False
|
||||||
|
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
|
||||||
|
"Hold'em" : ('hold','holdem'),
|
||||||
|
'Omaha Hi' : ('hold','omahahi'),
|
||||||
|
'Omaha H/L' : ('hold','omahahilo'),
|
||||||
|
'Razz' : ('stud','razz'),
|
||||||
|
'Stud Hi' : ('stud','studhi'),
|
||||||
|
'Stud H/L' : ('stud','studhilo')
|
||||||
|
}
|
||||||
|
currencies = { u' €':'EUR', '$':'USD', '':'T$' }
|
||||||
|
info['limitType'] = limits[mg['LIMIT']]
|
||||||
|
if mg['GAME'] is not None:
|
||||||
|
(info['base'], info['category']) = games[mg['GAME']]
|
||||||
|
if mg['CURRENCY'] is not None:
|
||||||
|
info['currency'] = currencies[mg['CURRENCY']]
|
||||||
|
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.
|
||||||
|
|
||||||
|
# Info is now ready to be copied in the tourney object
|
||||||
|
tourney.gametype = info
|
||||||
|
|
||||||
|
# Additional info can be stored in the tourney object
|
||||||
|
if mg['BUYIN'] is not None:
|
||||||
|
tourney.buyin = 100*Decimal(re.sub(u',', u'', "%s" % mg['BUYIN']))
|
||||||
|
tourney.fee = 0
|
||||||
|
if mg['FEE'] is not None:
|
||||||
|
tourney.fee = 100*Decimal(re.sub(u',', u'', "%s" % mg['FEE']))
|
||||||
|
if mg['TOURNAMENT_NAME'] is not None:
|
||||||
|
# Tournament Name can have a trailing space at the end (depending on the tournament description)
|
||||||
|
tourney.tourneyName = mg['TOURNAMENT_NAME'].rstrip()
|
||||||
|
if mg['SPECIAL'] is not None:
|
||||||
|
special = mg['SPECIAL']
|
||||||
|
if special == "KO":
|
||||||
|
tourney.isKO = True
|
||||||
|
if special == "Heads Up":
|
||||||
|
tourney.isHU = True
|
||||||
|
tourney.maxseats = 2
|
||||||
|
if re.search("Matrix", special):
|
||||||
|
tourney.isMatrix = True
|
||||||
|
if special == "Rebuy":
|
||||||
|
tourney.isRebuy = True
|
||||||
|
if special == "Madness":
|
||||||
|
tourney.tourneyComment = "Madness"
|
||||||
|
if mg['SHOOTOUT'] is not None:
|
||||||
|
tourney.isShootout = True
|
||||||
|
if mg['TURBO1'] is not None or mg['TURBO2'] is not None :
|
||||||
|
tourney.speed = "Turbo"
|
||||||
|
if mg['TOURNO'] is not None:
|
||||||
|
tourney.tourNo = mg['TOURNO']
|
||||||
|
else:
|
||||||
|
log.info( "Unable to get a valid Tournament ID -- File rejected" )
|
||||||
|
return False
|
||||||
|
if tourney.isMatrix:
|
||||||
|
if mg['MATCHNO'] is not None:
|
||||||
|
tourney.matrixMatchId = mg['MATCHNO']
|
||||||
|
else:
|
||||||
|
tourney.matrixMatchId = 0
|
||||||
|
|
||||||
|
|
||||||
|
# Get BuyIn/Fee
|
||||||
|
# Try and deal with the different cases that can occur :
|
||||||
|
# - No buy-in/fee can be on the first line (freerolls, Satellites sometimes ?, ...) but appears in the rest of the description ==> use this one
|
||||||
|
# - Buy-In/Fee from the first line differs from the rest of the description :
|
||||||
|
# * OK in matrix tourneys (global buy-in dispatched between the different matches)
|
||||||
|
# * NOK otherwise ==> issue a warning and store specific data as if were a Matrix Tourney
|
||||||
|
# - If no buy-in/fee can be found : assume it's a freeroll
|
||||||
|
m = self.re_TourneyBuyInFee.search(tourneyText)
|
||||||
|
if m is not None:
|
||||||
|
mg = m.groupdict()
|
||||||
|
if tourney.isMatrix :
|
||||||
|
if mg['BUYIN'] is not None:
|
||||||
|
tourney.subTourneyBuyin = 100*Decimal(re.sub(u',', u'', "%s" % mg['BUYIN']))
|
||||||
|
tourney.subTourneyFee = 0
|
||||||
|
if mg['FEE'] is not None:
|
||||||
|
tourney.subTourneyFee = 100*Decimal(re.sub(u',', u'', "%s" % mg['FEE']))
|
||||||
|
else :
|
||||||
|
if mg['BUYIN'] is not None:
|
||||||
|
if tourney.buyin is None:
|
||||||
|
tourney.buyin = 100*Decimal(re.sub(u',', u'', "%s" % mg['BUYIN']))
|
||||||
|
else :
|
||||||
|
if 100*Decimal(re.sub(u',', u'', "%s" % mg['BUYIN'])) != tourney.buyin:
|
||||||
|
log.error( "Conflict between buyins read in topline (%s) and in BuyIn field (%s)" % (touney.buyin, 100*Decimal(re.sub(u',', u'', "%s" % mg['BUYIN']))) )
|
||||||
|
tourney.subTourneyBuyin = 100*Decimal(re.sub(u',', u'', "%s" % mg['BUYIN']))
|
||||||
|
if mg['FEE'] is not None:
|
||||||
|
if tourney.fee is None:
|
||||||
|
tourney.fee = 100*Decimal(re.sub(u',', u'', "%s" % mg['FEE']))
|
||||||
|
else :
|
||||||
|
if 100*Decimal(re.sub(u',', u'', "%s" % mg['FEE'])) != tourney.fee:
|
||||||
|
log.error( "Conflict between fees read in topline (%s) and in BuyIn field (%s)" % (touney.fee, 100*Decimal(re.sub(u',', u'', "%s" % mg['FEE']))) )
|
||||||
|
tourney.subTourneyFee = 100*Decimal(re.sub(u',', u'', "%s" % mg['FEE']))
|
||||||
|
|
||||||
|
if tourney.buyin is None:
|
||||||
|
log.info( "Unable to affect a buyin to this tournament : assume it's a freeroll" )
|
||||||
|
tourney.buyin = 0
|
||||||
|
tourney.fee = 0
|
||||||
|
else:
|
||||||
|
if tourney.fee is None:
|
||||||
|
#print "Couldn't initialize fee, even though buyin went OK : assume there are no fees"
|
||||||
|
tourney.fee = 0
|
||||||
|
|
||||||
|
#Get single line infos
|
||||||
|
dictRegex = { "BUYINCHIPS" : self.re_TourneyBuyInChips,
|
||||||
|
"ENTRIES" : self.re_TourneyEntries,
|
||||||
|
"PRIZEPOOL" : self.re_TourneyPrizePool,
|
||||||
|
"REBUY_AMOUNT" : self.re_TourneyRebuyAmount,
|
||||||
|
"ADDON_AMOUNT" : self.re_TourneyAddOnAmount,
|
||||||
|
"REBUY_TOTAL" : self.re_TourneyRebuysTotal,
|
||||||
|
"ADDONS_TOTAL" : self.re_TourneyAddOnsTotal,
|
||||||
|
"REBUY_CHIPS" : self.re_TourneyRebuyChips,
|
||||||
|
"ADDON_CHIPS" : self.re_TourneyAddOnChips,
|
||||||
|
"STARTTIME" : self.re_TourneyTimeInfo,
|
||||||
|
"KO_BOUNTY_AMOUNT" : self.re_TourneyKOBounty,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
dictHolders = { "BUYINCHIPS" : "buyInChips",
|
||||||
|
"ENTRIES" : "entries",
|
||||||
|
"PRIZEPOOL" : "prizepool",
|
||||||
|
"REBUY_AMOUNT" : "rebuyAmount",
|
||||||
|
"ADDON_AMOUNT" : "addOnAmount",
|
||||||
|
"REBUY_TOTAL" : "totalRebuys",
|
||||||
|
"ADDONS_TOTAL" : "totalAddOns",
|
||||||
|
"REBUY_CHIPS" : "rebuyChips",
|
||||||
|
"ADDON_CHIPS" : "addOnChips",
|
||||||
|
"STARTTIME" : "starttime",
|
||||||
|
"KO_BOUNTY_AMOUNT" : "koBounty"
|
||||||
|
}
|
||||||
|
|
||||||
|
mg = {} # After the loop, mg will contain all the matching groups, including the ones that have not been used, like ENDTIME and IN-PROGRESS
|
||||||
|
for data in dictRegex:
|
||||||
|
m = dictRegex.get(data).search(tourneyText)
|
||||||
|
if m is not None:
|
||||||
|
mg.update(m.groupdict())
|
||||||
|
setattr(tourney, dictHolders[data], mg[data])
|
||||||
|
|
||||||
|
if mg['IN_PROGRESS'] is not None or mg['ENDTIME'] is not None:
|
||||||
|
# Assign endtime to tourney (if None, that's ok, it's because the tourney wans't over over when the summary file was produced)
|
||||||
|
tourney.endtime = mg['ENDTIME']
|
||||||
|
|
||||||
|
# Deal with hero specific information
|
||||||
|
if tourney.hero is not None :
|
||||||
|
m = self.re_TourneyRebuyCount.search(tourneyText)
|
||||||
|
if m is not None:
|
||||||
|
mg = m.groupdict()
|
||||||
|
if mg['REBUY_COUNT'] is not None :
|
||||||
|
tourney.countRebuys.update( { tourney.hero : Decimal(mg['REBUY_COUNT']) } )
|
||||||
|
m = self.re_TourneyAddOnCount.search(tourneyText)
|
||||||
|
if m is not None:
|
||||||
|
mg = m.groupdict()
|
||||||
|
if mg['ADDON_COUNT'] is not None :
|
||||||
|
tourney.countAddOns.update( { tourney.hero : Decimal(mg['ADDON_COUNT']) } )
|
||||||
|
m = self.re_TourneyCountKO.search(tourneyText)
|
||||||
|
if m is not None:
|
||||||
|
mg = m.groupdict()
|
||||||
|
if mg['COUNT_KO'] is not None :
|
||||||
|
tourney.countKO.update( { tourney.hero : Decimal(mg['COUNT_KO']) } )
|
||||||
|
|
||||||
|
# Deal with money amounts
|
||||||
|
tourney.koBounty = 100*Decimal(re.sub(u',', u'', "%s" % tourney.koBounty))
|
||||||
|
tourney.prizepool = 100*Decimal(re.sub(u',', u'', "%s" % tourney.prizepool))
|
||||||
|
tourney.rebuyAmount = 100*Decimal(re.sub(u',', u'', "%s" % tourney.rebuyAmount))
|
||||||
|
tourney.addOnAmount = 100*Decimal(re.sub(u',', u'', "%s" % tourney.addOnAmount))
|
||||||
|
|
||||||
|
# Calculate payin amounts and update winnings -- not possible to take into account nb of rebuys, addons or Knockouts for other players than hero on FTP
|
||||||
|
for p in tourney.players :
|
||||||
|
tourney.payinAmounts[p] = tourney.buyin + tourney.fee + (tourney.rebuyAmount * tourney.countRebuys[p]) + (tourney.addOnAmount * tourney.countAddOns[p])
|
||||||
|
#print " player %s : payinAmount = %d" %( p, tourney.payinAmounts[p])
|
||||||
|
if tourney.isKO :
|
||||||
|
#tourney.incrementPlayerWinnings(tourney.players[p], Decimal(tourney.koBounty)*Decimal(tourney.countKO[p]))
|
||||||
|
tourney.winnings[p] += Decimal(tourney.koBounty)*Decimal(tourney.countKO[p])
|
||||||
|
#print "player %s : winnings %d" % (p, tourney.winnings[p])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#print mg
|
||||||
|
return True
|
||||||
|
|
||||||
|
def getPlayersPositionsAndWinnings(self, tourney):
|
||||||
|
playersText = tourney.summaryText[1]
|
||||||
|
#print "Examine : '%s'" %(playersText)
|
||||||
|
m = self.re_TourneyPlayersSummary.finditer(playersText)
|
||||||
|
|
||||||
|
for a in m:
|
||||||
|
if a.group('PNAME') is not None and a.group('RANK') is not None:
|
||||||
|
if a.group('RANK') == "Still Playing":
|
||||||
|
rank = -1
|
||||||
|
else:
|
||||||
|
rank = Decimal(a.group('RANK'))
|
||||||
|
|
||||||
|
if a.group('WINNING') is not None:
|
||||||
|
winnings = 100*Decimal(re.sub(u',', u'', "%s" % a.group('WINNING')))
|
||||||
|
else:
|
||||||
|
winnings = "0"
|
||||||
|
|
||||||
|
tourney.addPlayer(rank, a.group('PNAME'), winnings, 0, 0, 0, 0)
|
||||||
|
else:
|
||||||
|
print "Player finishing stats unreadable : %s" % a
|
||||||
|
|
||||||
|
# Find Hero
|
||||||
|
n = self.re_TourneyHeroFinishingP.search(playersText)
|
||||||
|
if n is not None:
|
||||||
|
heroName = n.group('HERO_NAME')
|
||||||
|
tourney.hero = heroName
|
||||||
|
# Is this really useful ?
|
||||||
|
if (tourney.finishPositions[heroName] != Decimal(n.group('HERO_FINISHING_POS'))):
|
||||||
|
print "Bad parsing : finish position incoherent : %s / %s" % (tourney.finishPositions[heroName], n.group('HERO_FINISHING_POS'))
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = OptionParser()
|
parser = OptionParser()
|
||||||
parser.add_option("-i", "--input", dest="ipath", help="parse input hand history", default="regression-test-files/fulltilt/razz/FT20090223 Danville - $0.50-$1 Ante $0.10 - Limit Razz.txt")
|
parser.add_option("-i", "--input", dest="ipath", help="parse input hand history", default="regression-test-files/fulltilt/razz/FT20090223 Danville - $0.50-$1 Ante $0.10 - Limit Razz.txt")
|
||||||
|
@ -369,7 +675,7 @@ if __name__ == "__main__":
|
||||||
|
|
||||||
(options, args) = parser.parse_args()
|
(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)
|
e = Fulltilt(in_path = options.ipath, out_path = options.opath, follow = options.follow)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
import threading
|
import threading
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import traceback
|
||||||
|
|
||||||
import pygtk
|
import pygtk
|
||||||
pygtk.require('2.0')
|
pygtk.require('2.0')
|
||||||
|
@ -135,12 +136,23 @@ class GuiAutoImport (threading.Thread):
|
||||||
def do_import(self):
|
def do_import(self):
|
||||||
"""Callback for timer to do an import iteration."""
|
"""Callback for timer to do an import iteration."""
|
||||||
if self.doAutoImportBool:
|
if self.doAutoImportBool:
|
||||||
|
self.startButton.set_label(u' I M P O R T I N G ')
|
||||||
self.importer.runUpdated()
|
self.importer.runUpdated()
|
||||||
sys.stdout.write(".")
|
sys.stdout.write(".")
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
|
gobject.timeout_add(1000, self.reset_startbutton)
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def reset_startbutton(self):
|
||||||
|
if self.pipe_to_hud is not None:
|
||||||
|
self.startButton.set_label(u' _Stop Autoimport ')
|
||||||
|
else:
|
||||||
|
self.startButton.set_label(u' _Start Autoimport ')
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def startClicked(self, widget, data):
|
def startClicked(self, widget, data):
|
||||||
"""runs when user clicks start on auto import tab"""
|
"""runs when user clicks start on auto import tab"""
|
||||||
|
@ -160,33 +172,32 @@ class GuiAutoImport (threading.Thread):
|
||||||
# - Ideally we want to release the lock if the auto-import is killed by some
|
# - Ideally we want to release the lock if the auto-import is killed by some
|
||||||
# kind of exception - is this possible?
|
# kind of exception - is this possible?
|
||||||
if self.settings['global_lock'].acquire(False): # returns false immediately if lock not acquired
|
if self.settings['global_lock'].acquire(False): # returns false immediately if lock not acquired
|
||||||
try:
|
print "\nGlobal lock taken ..."
|
||||||
print "\nGlobal lock taken ..."
|
self.doAutoImportBool = True
|
||||||
self.doAutoImportBool = True
|
widget.set_label(u' _Stop Autoimport ')
|
||||||
widget.set_label(u' _Stop Autoimport ')
|
if self.pipe_to_hud is None:
|
||||||
if self.pipe_to_hud is None:
|
if os.name == 'nt':
|
||||||
if os.name == 'nt':
|
command = "python HUD_main.py " + self.settings['cl_options']
|
||||||
command = "python HUD_main.py" + " " + self.settings['cl_options']
|
bs = 0
|
||||||
bs = 0 # windows is not happy with line buffing here
|
else:
|
||||||
self.pipe_to_hud = subprocess.Popen(command, bufsize = bs, stdin = subprocess.PIPE,
|
command = os.path.join(sys.path[0], 'HUD_main.py')
|
||||||
universal_newlines=True)
|
command = [command, ] + string.split(self.settings['cl_options'])
|
||||||
else:
|
bs = 1
|
||||||
command = os.path.join(sys.path[0], 'HUD_main.py')
|
try:
|
||||||
cl = [command, ] + string.split(self.settings['cl_options'])
|
self.pipe_to_hud = subprocess.Popen(command, bufsize = bs, stdin = subprocess.PIPE,
|
||||||
self.pipe_to_hud = subprocess.Popen(cl, bufsize = 1, stdin = subprocess.PIPE,
|
universal_newlines = True)
|
||||||
universal_newlines=True)
|
except:
|
||||||
|
err = traceback.extract_tb(sys.exc_info()[2])[-1]
|
||||||
# Add directories to importer object.
|
print "*** Error: " + err[2] + "(" + str(err[1]) + "): " + str(sys.exc_info()[1])
|
||||||
|
else:
|
||||||
for site in self.input_settings:
|
for site in self.input_settings:
|
||||||
self.importer.addImportDirectory(self.input_settings[site][0], True, site, self.input_settings[site][1])
|
self.importer.addImportDirectory(self.input_settings[site][0], True, site, self.input_settings[site][1])
|
||||||
print "Adding import directories - Site: " + site + " dir: "+ str(self.input_settings[site][0])
|
print "+Import directory - Site: " + site + " dir: " + str(self.input_settings[site][0])
|
||||||
self.do_import()
|
self.do_import()
|
||||||
|
|
||||||
interval=int(self.intervalEntry.get_text())
|
interval = int(self.intervalEntry.get_text())
|
||||||
gobject.timeout_add(interval*1000, self.do_import)
|
gobject.timeout_add(interval*1000, self.do_import)
|
||||||
except:
|
|
||||||
err = traceback.extract_tb(sys.exc_info()[2])[-1]
|
|
||||||
print "***Error: "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1])
|
|
||||||
else:
|
else:
|
||||||
print "auto-import aborted - global lock not available"
|
print "auto-import aborted - global lock not available"
|
||||||
else: # toggled off
|
else: # toggled off
|
||||||
|
|
|
@ -284,7 +284,7 @@ def main(argv=None):
|
||||||
parser.add_option("-q", "--quiet", action="store_false", dest="gui", default=True,
|
parser.add_option("-q", "--quiet", action="store_false", dest="gui", default=True,
|
||||||
help="don't start gui; deprecated (just give a filename with -f).")
|
help="don't start gui; deprecated (just give a filename with -f).")
|
||||||
parser.add_option("-c", "--convert", dest="filtername", default="PokerStars", metavar="FILTER",
|
parser.add_option("-c", "--convert", dest="filtername", default="PokerStars", metavar="FILTER",
|
||||||
help="Conversion filter (*Full Tilt Poker, PokerStars, Everleaf)")
|
help="Conversion filter (*Full Tilt Poker, PokerStars, Everleaf, Absolute)")
|
||||||
parser.add_option("-x", "--failOnError", action="store_true", default=False,
|
parser.add_option("-x", "--failOnError", action="store_true", default=False,
|
||||||
help="If this option is passed it quits when it encounters any error")
|
help="If this option is passed it quits when it encounters any error")
|
||||||
parser.add_option("-m", "--minPrint", "--status", dest="minPrint", default="0", type="int",
|
parser.add_option("-m", "--minPrint", "--status", dest="minPrint", default="0", type="int",
|
||||||
|
@ -319,6 +319,7 @@ def main(argv=None):
|
||||||
# importer.setDropIndexes("auto")
|
# importer.setDropIndexes("auto")
|
||||||
importer.setDropIndexes("don't drop")
|
importer.setDropIndexes("don't drop")
|
||||||
importer.setFailOnError(options.failOnError)
|
importer.setFailOnError(options.failOnError)
|
||||||
|
importer.setThreads(-1)
|
||||||
importer.addBulkImportImportFileOrDir(os.path.expanduser(options.filename), site=options.filtername)
|
importer.addBulkImportImportFileOrDir(os.path.expanduser(options.filename), site=options.filtername)
|
||||||
importer.setCallHud(False)
|
importer.setCallHud(False)
|
||||||
importer.runImport()
|
importer.runImport()
|
||||||
|
|
|
@ -20,6 +20,7 @@ import pygtk
|
||||||
pygtk.require('2.0')
|
pygtk.require('2.0')
|
||||||
import gtk
|
import gtk
|
||||||
import os
|
import os
|
||||||
|
import traceback
|
||||||
from time import *
|
from time import *
|
||||||
#import pokereval
|
#import pokereval
|
||||||
|
|
||||||
|
|
|
@ -132,7 +132,7 @@ class GuiPlayerStats (threading.Thread):
|
||||||
self.stats_vbox = gtk.VBox(False, 0)
|
self.stats_vbox = gtk.VBox(False, 0)
|
||||||
self.stats_vbox.show()
|
self.stats_vbox.show()
|
||||||
self.stats_frame.add(self.stats_vbox)
|
self.stats_frame.add(self.stats_vbox)
|
||||||
self.fillStatsFrame(self.stats_vbox)
|
# self.fillStatsFrame(self.stats_vbox)
|
||||||
|
|
||||||
self.main_hbox.pack_start(self.filters.get_vbox())
|
self.main_hbox.pack_start(self.filters.get_vbox())
|
||||||
self.main_hbox.pack_start(self.stats_frame, expand=True, fill=True)
|
self.main_hbox.pack_start(self.stats_frame, expand=True, fill=True)
|
||||||
|
@ -167,7 +167,9 @@ class GuiPlayerStats (threading.Thread):
|
||||||
for site in sites:
|
for site in sites:
|
||||||
if sites[site] == True:
|
if sites[site] == True:
|
||||||
sitenos.append(siteids[site])
|
sitenos.append(siteids[site])
|
||||||
self.cursor.execute(self.sql.query['getPlayerId'], (heroes[site],))
|
# Nasty hack to deal with multiple sites + same player name -Eric
|
||||||
|
que = self.sql.query['getPlayerId'] + " AND siteId=%d" % siteids[site]
|
||||||
|
self.cursor.execute(que, (heroes[site],))
|
||||||
result = self.db.cursor.fetchall()
|
result = self.db.cursor.fetchall()
|
||||||
if len(result) == 1:
|
if len(result) == 1:
|
||||||
playerids.append(result[0][0])
|
playerids.append(result[0][0])
|
||||||
|
|
|
@ -21,7 +21,11 @@ pygtk.require('2.0')
|
||||||
import gtk
|
import gtk
|
||||||
import os
|
import os
|
||||||
from time import time, strftime, localtime
|
from time import time, strftime, localtime
|
||||||
from numpy import diff, nonzero
|
try:
|
||||||
|
from numpy import diff, nonzero
|
||||||
|
except:
|
||||||
|
print """Failed to load numpy in Session Viewer"""
|
||||||
|
print """This is of no consequence as the module currently doesn't do anything."""
|
||||||
|
|
||||||
import Card
|
import Card
|
||||||
import fpdb_import
|
import fpdb_import
|
||||||
|
|
|
@ -22,9 +22,9 @@ import gtk
|
||||||
import os
|
import os
|
||||||
import fpdb_simple
|
import fpdb_simple
|
||||||
|
|
||||||
|
|
||||||
import fpdb_import
|
import fpdb_import
|
||||||
import fpdb_db
|
import fpdb_db
|
||||||
|
from Exceptions import *
|
||||||
|
|
||||||
|
|
||||||
class GuiTableViewer (threading.Thread):
|
class GuiTableViewer (threading.Thread):
|
||||||
|
@ -74,7 +74,7 @@ class GuiTableViewer (threading.Thread):
|
||||||
|
|
||||||
tmp+=("WtSD", "W$wsF", "W$SD")
|
tmp+=("WtSD", "W$wsF", "W$SD")
|
||||||
else:
|
else:
|
||||||
raise fpdb_simple.FpdbError("reimplement stud")
|
raise FpdbError("reimplement stud")
|
||||||
arr.append(tmp)
|
arr.append(tmp)
|
||||||
|
|
||||||
#then the data rows
|
#then the data rows
|
||||||
|
@ -94,7 +94,7 @@ class GuiTableViewer (threading.Thread):
|
||||||
elif seatCount==2 or seatCount==3:
|
elif seatCount==2 or seatCount==3:
|
||||||
minSeats,maxSeats=seatCount,seatCount
|
minSeats,maxSeats=seatCount,seatCount
|
||||||
else:
|
else:
|
||||||
fpdb_simple.FpdbError("invalid seatCount")
|
FpdbError("invalid seatCount")
|
||||||
|
|
||||||
self.cursor.execute("SELECT * FROM HudCache WHERE gametypeId=%s AND playerId=%s AND activeSeats>=%s AND activeSeats<=%s", (self.gametype_id, self.player_ids[player][0], minSeats, maxSeats))
|
self.cursor.execute("SELECT * FROM HudCache WHERE gametypeId=%s AND playerId=%s AND activeSeats>=%s AND activeSeats<=%s", (self.gametype_id, self.player_ids[player][0], minSeats, maxSeats))
|
||||||
rows=self.cursor.fetchall()
|
rows=self.cursor.fetchall()
|
||||||
|
|
|
@ -252,10 +252,10 @@
|
||||||
|
|
||||||
<site enabled="False"
|
<site enabled="False"
|
||||||
site_name="PartyPoker"
|
site_name="PartyPoker"
|
||||||
table_finder="PartyPoker.exe"
|
table_finder="PartyGaming.exe"
|
||||||
screen_name="YOUR SCREEN NAME HERE"
|
screen_name="YOUR SCREEN NAME HERE"
|
||||||
site_path=""
|
site_path="C:/Program Files/PartyGaming/PartyPoker"
|
||||||
HH_path=""
|
HH_path="C:/Program Files/PartyGaming/PartyPoker/HandHistory/YOUR SCREEN NAME HERE/"
|
||||||
decoder="everleaf_decode_table"
|
decoder="everleaf_decode_table"
|
||||||
converter="PartyPokerToFpdb"
|
converter="PartyPokerToFpdb"
|
||||||
supported_games="holdem">
|
supported_games="holdem">
|
||||||
|
@ -432,7 +432,8 @@
|
||||||
</hhcs>
|
</hhcs>
|
||||||
|
|
||||||
<supported_databases>
|
<supported_databases>
|
||||||
<database db_name="fpdb" db_server="mysql" db_ip="localhost" db_user="fpdb" db_pass="YOUR MYSQL PASSWORD" db_type="fpdb"></database>
|
<database db_name="fpdb" db_server="mysql" db_ip="localhost" db_user="fpdb" db_pass="YOUR MYSQL PASSWORD" db_type="fpdb"></database>
|
||||||
|
<!-- <database db_ip="localhost" db_name="fpdb" db_pass="fpdb" db_server="sqlite" db_type="fpdb" db_user="fpdb"/> -->
|
||||||
</supported_databases>
|
</supported_databases>
|
||||||
|
|
||||||
</FreePokerToolsConfig>
|
</FreePokerToolsConfig>
|
||||||
|
|
|
@ -37,7 +37,7 @@ import traceback
|
||||||
|
|
||||||
if not options.errorsToConsole:
|
if not options.errorsToConsole:
|
||||||
print "Note: error output is being diverted to fpdb-error-log.txt and HUD-error.txt. Any major error will be reported there _only_."
|
print "Note: error output is being diverted to fpdb-error-log.txt and HUD-error.txt. Any major error will be reported there _only_."
|
||||||
errorFile = open('fpdb-error-log.txt', 'w', 0)
|
errorFile = open('HUD-error.txt', 'w', 0)
|
||||||
sys.stderr = errorFile
|
sys.stderr = errorFile
|
||||||
|
|
||||||
import thread
|
import thread
|
||||||
|
@ -195,7 +195,7 @@ class HUD_main(object):
|
||||||
temp_key = tour_number
|
temp_key = tour_number
|
||||||
else: # tourney, but can't get number and table
|
else: # tourney, but can't get number and table
|
||||||
print "could not find tournament: skipping "
|
print "could not find tournament: skipping "
|
||||||
sys.stderr.write("Could not find tournament %d in hand %d. Skipping.\n" % (int(tour_number), int(new_hand_id)))
|
#sys.stderr.write("Could not find tournament %d in hand %d. Skipping.\n" % (int(tour_number), int(new_hand_id)))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
347
pyfpdb/Hand.py
347
pyfpdb/Hand.py
|
@ -1,15 +1,16 @@
|
||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#Copyright 2008 Carl Gherardi
|
#Copyright 2008 Carl Gherardi
|
||||||
#This program is free software: you can redistribute it and/or modify
|
#This program is free software: you can redistribute it and/or modify
|
||||||
#it under the terms of the GNU Affero General Public License as published by
|
#it under the terms of the GNU Affero General Public License as published by
|
||||||
#the Free Software Foundation, version 3 of the License.
|
#the Free Software Foundation, version 3 of the License.
|
||||||
#
|
#
|
||||||
#This program is distributed in the hope that it will be useful,
|
#This program is distributed in the hope that it will be useful,
|
||||||
#but WITHOUT ANY WARRANTY; without even the implied warranty of
|
#but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
#GNU General Public License for more details.
|
#GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
#You should have received a copy of the GNU Affero General Public License
|
#You should have received a copy of the GNU Affero General Public License
|
||||||
#along with this program. If not, see <http://www.gnu.org/licenses/>.
|
#along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#In the "official" distribution you can find the license in
|
#In the "official" distribution you can find the license in
|
||||||
|
@ -32,6 +33,8 @@ import pprint
|
||||||
import DerivedStats
|
import DerivedStats
|
||||||
import Card
|
import Card
|
||||||
|
|
||||||
|
log = logging.getLogger("parser")
|
||||||
|
|
||||||
class Hand(object):
|
class Hand(object):
|
||||||
|
|
||||||
###############################################################3
|
###############################################################3
|
||||||
|
@ -61,6 +64,15 @@ class Hand(object):
|
||||||
self.fee = None # the Database code is looking for this one .. ?
|
self.fee = None # the Database code is looking for this one .. ?
|
||||||
self.level = None
|
self.level = None
|
||||||
self.mixed = None
|
self.mixed = None
|
||||||
|
# Some attributes for hand from a tourney
|
||||||
|
self.speed = "Normal"
|
||||||
|
self.isRebuy = False
|
||||||
|
self.isKO = False
|
||||||
|
self.isHU = False
|
||||||
|
self.isMatrix = False
|
||||||
|
self.isShootout = False
|
||||||
|
self.tourneyComment = None
|
||||||
|
|
||||||
self.seating = []
|
self.seating = []
|
||||||
self.players = []
|
self.players = []
|
||||||
self.posted = []
|
self.posted = []
|
||||||
|
@ -118,7 +130,7 @@ class Hand(object):
|
||||||
("MIXED", self.mixed),
|
("MIXED", self.mixed),
|
||||||
("LASTBET", self.lastBet),
|
("LASTBET", self.lastBet),
|
||||||
("ACTION STREETS", self.actionStreets),
|
("ACTION STREETS", self.actionStreets),
|
||||||
("STREETS", self.streets),
|
("STREETS", self.streets),
|
||||||
("ALL STREETS", self.allStreets),
|
("ALL STREETS", self.allStreets),
|
||||||
("COMMUNITY STREETS", self.communityStreets),
|
("COMMUNITY STREETS", self.communityStreets),
|
||||||
("HOLE STREETS", self.holeStreets),
|
("HOLE STREETS", self.holeStreets),
|
||||||
|
@ -131,7 +143,7 @@ class Hand(object):
|
||||||
("RAKE", self.rake),
|
("RAKE", self.rake),
|
||||||
("START TIME", self.starttime),
|
("START TIME", self.starttime),
|
||||||
)
|
)
|
||||||
|
|
||||||
structs = ( ("PLAYERS", self.players),
|
structs = ( ("PLAYERS", self.players),
|
||||||
("STACKS", self.stacks),
|
("STACKS", self.stacks),
|
||||||
("POSTED", self.posted),
|
("POSTED", self.posted),
|
||||||
|
@ -162,7 +174,7 @@ shown whether they were revealed at showdown
|
||||||
mucked whether they were mucked at showdown
|
mucked whether they were mucked at showdown
|
||||||
dealt whether they were seen in a 'dealt to' line
|
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:
|
try:
|
||||||
self.checkPlayerExists(player)
|
self.checkPlayerExists(player)
|
||||||
except FpdbParseError, e:
|
except FpdbParseError, e:
|
||||||
|
@ -185,31 +197,31 @@ db: a connected fpdb_db object"""
|
||||||
# TODO:
|
# TODO:
|
||||||
# Players - base playerid and siteid tuple
|
# Players - base playerid and siteid tuple
|
||||||
sqlids = db.getSqlPlayerIDs([p[1] for p in self.players], self.siteId)
|
sqlids = db.getSqlPlayerIDs([p[1] for p in self.players], self.siteId)
|
||||||
|
|
||||||
|
#Gametypes
|
||||||
|
gtid = db.getGameTypeId(self.siteId, self.gametype)
|
||||||
|
|
||||||
# HudCache data to come from DerivedStats class
|
# HudCache data to come from DerivedStats class
|
||||||
# HandsActions - all actions for all players for all streets - self.actions
|
# HandsActions - all actions for all players for all streets - self.actions
|
||||||
# BoardCards - Skip - no longer necessary
|
|
||||||
# Hands - Summary information of hand indexed by handId - gameinfo
|
# Hands - Summary information of hand indexed by handId - gameinfo
|
||||||
#hh['siteHandNo'] = self.handid
|
#This should be moved to prepInsert
|
||||||
# gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
|
hh = {}
|
||||||
#
|
hh['siteHandNo'] = self.handid
|
||||||
#hh['handStart'] = self.starttime
|
hh['handStart'] = self.starttime
|
||||||
# seats TINYINT NOT NULL,
|
hh['gameTypeId'] = gtid
|
||||||
#
|
# seats TINYINT NOT NULL,
|
||||||
#hh['tableName'] = self.tablenam
|
hh['tableName'] = self.tablename
|
||||||
#hh['maxSeats'] = self.maxseats
|
hh['maxSeats'] = self.maxseats
|
||||||
# boardcard1 smallint, /* 0=none, 1-13=2-Ah 14-26=2-Ad 27-39=2-Ac 40-52=2-As */
|
hh['seats'] = len(sqlids)
|
||||||
# boardcard2 smallint,
|
# Flop turn and river may all be empty - add (likely) too many elements and trim with range
|
||||||
# boardcard3 smallint,
|
boardcards = self.board['FLOP'] + self.board['TURN'] + self.board['RIVER'] + [u'0x', u'0x', u'0x', u'0x', u'0x']
|
||||||
# boardcard4 smallint,
|
cards = [Card.encodeCard(c) for c in boardcards[0:5]]
|
||||||
# boardcard5 smallint,
|
hh['boardcard1'] = cards[0]
|
||||||
# Flop turn and river may all be empty - add (likely) too many elements and trim with range
|
hh['boardcard2'] = cards[1]
|
||||||
# boardcards = board['FLOP'] + board['TURN'] + board['RIVER'] + [u'0x', u'0x', u'0x', u'0x', u'0x']
|
hh['boardcard3'] = cards[2]
|
||||||
# cards = [Card.cardFromValueSuit(v,s) for v,s in boardcards[0:4]]
|
hh['boardcard4'] = cards[3]
|
||||||
# hh['boardcard1'] = cards[0]
|
hh['boardcard5'] = cards[4]
|
||||||
# hh['boardcard2'] = cards[1]
|
|
||||||
# hh['boardcard3'] = cards[2]
|
|
||||||
# hh['boardcard4'] = cards[3]
|
|
||||||
# hh['boardcard5'] = cards[4]
|
|
||||||
# texture smallint,
|
# texture smallint,
|
||||||
# playersVpi SMALLINT NOT NULL, /* num of players vpi */
|
# playersVpi SMALLINT NOT NULL, /* num of players vpi */
|
||||||
# Needs to be recorded
|
# Needs to be recorded
|
||||||
|
@ -233,18 +245,15 @@ db: a connected fpdb_db object"""
|
||||||
# Needs to be recorded
|
# Needs to be recorded
|
||||||
# street4Raises TINYINT NOT NULL, /* num big bets paid to see showdown */
|
# street4Raises TINYINT NOT NULL, /* num big bets paid to see showdown */
|
||||||
# Needs to be recorded
|
# Needs to be recorded
|
||||||
# street1Pot INT, /* pot size at flop/street4 */
|
|
||||||
# Needs to be recorded
|
#print "DEBUG: self.getStreetTotals = (%s, %s, %s, %s, %s)" % self.getStreetTotals()
|
||||||
# street2Pot INT, /* pot size at turn/street5 */
|
#FIXME: Pot size still in decimal, needs to be converted to cents
|
||||||
# Needs to be recorded
|
(hh['street1Pot'], hh['street2Pot'], hh['street3Pot'], hh['street4Pot'], hh['showdownPot']) = self.getStreetTotals()
|
||||||
# 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 */
|
|
||||||
# comment TEXT,
|
# comment TEXT,
|
||||||
# commentTs DATETIME
|
# commentTs DATETIME
|
||||||
# handid = db.storeHand(hh)
|
#print hh
|
||||||
|
handid = db.storeHand(hh)
|
||||||
# HandsPlayers - ? ... Do we fix winnings?
|
# HandsPlayers - ? ... Do we fix winnings?
|
||||||
# Tourneys ?
|
# Tourneys ?
|
||||||
# TourneysPlayers
|
# TourneysPlayers
|
||||||
|
@ -253,8 +262,8 @@ db: a connected fpdb_db object"""
|
||||||
|
|
||||||
def select(self, handId):
|
def select(self, handId):
|
||||||
""" Function to create Hand object from database """
|
""" Function to create Hand object from database """
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def addPlayer(self, seat, name, chips):
|
def addPlayer(self, seat, name, chips):
|
||||||
|
@ -264,7 +273,7 @@ seat (int) indicating the seat
|
||||||
name (string) player name
|
name (string) player name
|
||||||
chips (string) the chips the player has at the start of the hand (can be None)
|
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."""
|
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:
|
if chips is not None:
|
||||||
chips = re.sub(u',', u'', chips) #some sites have commas
|
chips = re.sub(u',', u'', chips) #some sites have commas
|
||||||
self.players.append([seat, name, chips])
|
self.players.append([seat, name, chips])
|
||||||
|
@ -280,9 +289,9 @@ If a player has None chips he won't be added."""
|
||||||
# go through m and initialise actions to empty list for each street.
|
# go through m and initialise actions to empty list for each street.
|
||||||
if match:
|
if match:
|
||||||
self.streets.update(match.groupdict())
|
self.streets.update(match.groupdict())
|
||||||
logging.debug("markStreets:\n"+ str(self.streets))
|
log.debug("markStreets:\n"+ str(self.streets))
|
||||||
else:
|
else:
|
||||||
logging.error("markstreets didn't match")
|
log.error("markstreets didn't match")
|
||||||
|
|
||||||
def checkPlayerExists(self,player):
|
def checkPlayerExists(self,player):
|
||||||
if player not in [p[1] for p in self.players]:
|
if player not in [p[1] for p in self.players]:
|
||||||
|
@ -292,7 +301,7 @@ If a player has None chips he won't be added."""
|
||||||
|
|
||||||
|
|
||||||
def setCommunityCards(self, street, cards):
|
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]
|
self.board[street] = [self.card(c) for c in cards]
|
||||||
# print "DEBUG: self.board: %s" % self.board
|
# print "DEBUG: self.board: %s" % self.board
|
||||||
|
|
||||||
|
@ -303,14 +312,13 @@ If a player has None chips he won't be added."""
|
||||||
return c
|
return c
|
||||||
|
|
||||||
def addAnte(self, player, ante):
|
def addAnte(self, player, ante):
|
||||||
logging.debug("%s %s antes %s" % ('ANTES', player, ante))
|
log.debug("%s %s antes %s" % ('BLINDSANTES', player, ante))
|
||||||
if player is not None:
|
if player is not None:
|
||||||
ante = re.sub(u',', u'', ante) #some sites have commas
|
ante = re.sub(u',', u'', ante) #some sites have commas
|
||||||
self.bets['ANTES'][player].append(Decimal(ante))
|
self.bets['BLINDSANTES'][player].append(Decimal(ante))
|
||||||
self.stacks[player] -= Decimal(ante)
|
self.stacks[player] -= Decimal(ante)
|
||||||
act = (player, 'posts', "ante", ante, self.stacks[player]==0)
|
act = (player, 'posts', "ante", ante, self.stacks[player]==0)
|
||||||
self.actions['ANTES'].append(act)
|
self.actions['BLINDSANTES'].append(act)
|
||||||
#~ self.lastBet['ANTES'] = Decimal(ante)
|
|
||||||
self.pot.addMoney(player, Decimal(ante))
|
self.pot.addMoney(player, Decimal(ante))
|
||||||
|
|
||||||
def addBlind(self, player, blindtype, amount):
|
def addBlind(self, player, blindtype, amount):
|
||||||
|
@ -322,29 +330,29 @@ If a player has None chips he won't be added."""
|
||||||
# - this is a call of 1 sb and a raise to 1 bb
|
# - 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:
|
if player is not None:
|
||||||
amount = re.sub(u',', u'', amount) #some sites have commas
|
amount = re.sub(u',', u'', amount) #some sites have commas
|
||||||
self.bets['PREFLOP'][player].append(Decimal(amount))
|
|
||||||
self.stacks[player] -= Decimal(amount)
|
self.stacks[player] -= Decimal(amount)
|
||||||
#print "DEBUG %s posts, stack %s" % (player, self.stacks[player])
|
|
||||||
act = (player, 'posts', blindtype, amount, self.stacks[player]==0)
|
act = (player, 'posts', blindtype, amount, self.stacks[player]==0)
|
||||||
self.actions['BLINDSANTES'].append(act)
|
self.actions['BLINDSANTES'].append(act)
|
||||||
|
|
||||||
|
if blindtype == 'both':
|
||||||
|
amount = self.bb
|
||||||
|
self.bets['BLINDSANTES'][player].append(Decimal(self.sb))
|
||||||
|
self.pot.addCommonMoney(Decimal(self.sb))
|
||||||
|
|
||||||
|
self.bets['PREFLOP'][player].append(Decimal(amount))
|
||||||
self.pot.addMoney(player, Decimal(amount))
|
self.pot.addMoney(player, Decimal(amount))
|
||||||
if blindtype == 'big blind':
|
self.lastBet['PREFLOP'] = Decimal(amount)
|
||||||
self.lastBet['PREFLOP'] = Decimal(amount)
|
|
||||||
elif blindtype == 'both':
|
|
||||||
# extra small blind is 'dead'
|
|
||||||
self.lastBet['PREFLOP'] = Decimal(self.bb)
|
|
||||||
self.posted = self.posted + [[player,blindtype]]
|
self.posted = self.posted + [[player,blindtype]]
|
||||||
#print "DEBUG: self.posted: %s" %(self.posted)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def addCall(self, street, player=None, amount=None):
|
def addCall(self, street, player=None, amount=None):
|
||||||
if amount:
|
if amount:
|
||||||
amount = re.sub(u',', u'', amount) #some sites have commas
|
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
|
# Potentially calculate the amount of the call if not supplied
|
||||||
# corner cases include if player would be all in
|
# corner cases include if player would be all in
|
||||||
if amount is not None:
|
if amount is not None:
|
||||||
|
@ -355,22 +363,22 @@ If a player has None chips he won't be added."""
|
||||||
act = (player, 'calls', amount, self.stacks[player]==0)
|
act = (player, 'calls', amount, self.stacks[player]==0)
|
||||||
self.actions[street].append(act)
|
self.actions[street].append(act)
|
||||||
self.pot.addMoney(player, Decimal(amount))
|
self.pot.addMoney(player, Decimal(amount))
|
||||||
|
|
||||||
def addRaiseBy(self, street, player, amountBy):
|
def addRaiseBy(self, street, player, amountBy):
|
||||||
"""\
|
"""\
|
||||||
Add a raise by amountBy on [street] by [player]
|
Add a raise by amountBy on [street] by [player]
|
||||||
"""
|
"""
|
||||||
#Given only the amount raised by, the amount of the raise can be calculated by
|
#Given only the amount raised by, the amount of the raise can be calculated by
|
||||||
# working out how much this player has already in the pot
|
# working out how much this player has already in the pot
|
||||||
# (which is the sum of self.bets[street][player])
|
# (which is the sum of self.bets[street][player])
|
||||||
# and how much he needs to call to match the previous player
|
# and how much he needs to call to match the previous player
|
||||||
# (which is tracked by self.lastBet)
|
# (which is tracked by self.lastBet)
|
||||||
# let Bp = previous bet
|
# let Bp = previous bet
|
||||||
# Bc = amount player has committed so far
|
# Bc = amount player has committed so far
|
||||||
# Rb = raise by
|
# Rb = raise by
|
||||||
# then: C = Bp - Bc (amount to call)
|
# then: C = Bp - Bc (amount to call)
|
||||||
# Rt = Bp + Rb (raise to)
|
# Rt = Bp + Rb (raise to)
|
||||||
#
|
#
|
||||||
amountBy = re.sub(u',', u'', amountBy) #some sites have commas
|
amountBy = re.sub(u',', u'', amountBy) #some sites have commas
|
||||||
self.checkPlayerExists(player)
|
self.checkPlayerExists(player)
|
||||||
Rb = Decimal(amountBy)
|
Rb = Decimal(amountBy)
|
||||||
|
@ -378,7 +386,7 @@ Add a raise by amountBy on [street] by [player]
|
||||||
Bc = reduce(operator.add, self.bets[street][player], 0)
|
Bc = reduce(operator.add, self.bets[street][player], 0)
|
||||||
C = Bp - Bc
|
C = Bp - Bc
|
||||||
Rt = Bp + Rb
|
Rt = Bp + Rb
|
||||||
|
|
||||||
self._addRaise(street, player, C, Rb, Rt)
|
self._addRaise(street, player, C, Rb, Rt)
|
||||||
#~ self.bets[street][player].append(C + Rb)
|
#~ self.bets[street][player].append(C + Rb)
|
||||||
#~ self.stacks[player] -= (C + Rb)
|
#~ self.stacks[player] -= (C + Rb)
|
||||||
|
@ -396,7 +404,7 @@ For sites which by "raises x" mean "calls and raises putting a total of x in the
|
||||||
C = Bp - Bc
|
C = Bp - Bc
|
||||||
Rb = CRb - C
|
Rb = CRb - C
|
||||||
Rt = Bp + Rb
|
Rt = Bp + Rb
|
||||||
|
|
||||||
self._addRaise(street, player, C, Rb, Rt)
|
self._addRaise(street, player, C, Rb, Rt)
|
||||||
|
|
||||||
def addRaiseTo(self, street, player, amountTo):
|
def addRaiseTo(self, street, player, amountTo):
|
||||||
|
@ -414,18 +422,18 @@ Add a raise on [street] by [player] to [amountTo]
|
||||||
self._addRaise(street, player, C, Rb, Rt)
|
self._addRaise(street, player, C, Rb, Rt)
|
||||||
|
|
||||||
def _addRaise(self, 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.bets[street][player].append(C + Rb)
|
||||||
self.stacks[player] -= (C + Rb)
|
self.stacks[player] -= (C + Rb)
|
||||||
act = (player, 'raises', Rb, Rt, C, self.stacks[player]==0)
|
act = (player, 'raises', Rb, Rt, C, self.stacks[player]==0)
|
||||||
self.actions[street].append(act)
|
self.actions[street].append(act)
|
||||||
self.lastBet[street] = Rt # TODO check this is correct
|
self.lastBet[street] = Rt # TODO check this is correct
|
||||||
self.pot.addMoney(player, C+Rb)
|
self.pot.addMoney(player, C+Rb)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def addBet(self, street, player, amount):
|
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
|
amount = re.sub(u',', u'', amount) #some sites have commas
|
||||||
self.checkPlayerExists(player)
|
self.checkPlayerExists(player)
|
||||||
self.bets[street][player].append(Decimal(amount))
|
self.bets[street][player].append(Decimal(amount))
|
||||||
|
@ -441,24 +449,25 @@ Add a raise on [street] by [player] to [amountTo]
|
||||||
self.checkPlayerExists(player)
|
self.checkPlayerExists(player)
|
||||||
act = (player, 'stands pat')
|
act = (player, 'stands pat')
|
||||||
self.actions[street].append(act)
|
self.actions[street].append(act)
|
||||||
|
|
||||||
|
|
||||||
def addFold(self, street, player):
|
def addFold(self, street, player):
|
||||||
logging.debug("%s %s folds" % (street, player))
|
log.debug("%s %s folds" % (street, player))
|
||||||
self.checkPlayerExists(player)
|
self.checkPlayerExists(player)
|
||||||
self.folded.add(player)
|
self.folded.add(player)
|
||||||
self.pot.addFold(player)
|
self.pot.addFold(player)
|
||||||
self.actions[street].append((player, 'folds'))
|
self.actions[street].append((player, 'folds'))
|
||||||
|
|
||||||
|
|
||||||
def addCheck(self, street, player):
|
def addCheck(self, street, player):
|
||||||
#print "DEBUG: %s %s checked" % (street, player)
|
#print "DEBUG: %s %s checked" % (street, player)
|
||||||
|
logging.debug("%s %s checks" % (street, player))
|
||||||
self.checkPlayerExists(player)
|
self.checkPlayerExists(player)
|
||||||
self.actions[street].append((player, 'checks'))
|
self.actions[street].append((player, 'checks'))
|
||||||
|
|
||||||
|
|
||||||
def addCollectPot(self,player, pot):
|
def addCollectPot(self,player, pot):
|
||||||
logging.debug("%s collected %s" % (player, pot))
|
log.debug("%s collected %s" % (player, pot))
|
||||||
self.checkPlayerExists(player)
|
self.checkPlayerExists(player)
|
||||||
self.collected = self.collected + [[player, pot]]
|
self.collected = self.collected + [[player, pot]]
|
||||||
if player not in self.collectees:
|
if player not in self.collectees:
|
||||||
|
@ -472,7 +481,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).
|
For when a player shows cards for any reason (for showdown or out of choice).
|
||||||
Card ranks will be uppercased
|
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:
|
if cards is not None:
|
||||||
self.addHoleCards(cards,player,shown, mucked)
|
self.addHoleCards(cards,player,shown, mucked)
|
||||||
elif holeandboard is not None:
|
elif holeandboard is not None:
|
||||||
|
@ -480,10 +489,9 @@ Card ranks will be uppercased
|
||||||
board = set([c for s in self.board.values() for c in s])
|
board = set([c for s in self.board.values() for c in s])
|
||||||
self.addHoleCards(holeandboard.difference(board),player,shown, mucked)
|
self.addHoleCards(holeandboard.difference(board),player,shown, mucked)
|
||||||
|
|
||||||
|
|
||||||
def totalPot(self):
|
def totalPot(self):
|
||||||
"""If all bets and blinds have been added, totals up the total pot size"""
|
"""If all bets and blinds have been added, totals up the total pot size"""
|
||||||
|
|
||||||
# This gives us the total amount put in the pot
|
# This gives us the total amount put in the pot
|
||||||
if self.totalpot is None:
|
if self.totalpot is None:
|
||||||
self.pot.end()
|
self.pot.end()
|
||||||
|
@ -522,7 +530,7 @@ Map the tuple self.gametype onto the pokerstars string describing it
|
||||||
"cp" : "Cap Pot Limit"
|
"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']])
|
retstring = "%s %s" %(gs[self.gametype['category']], ls[self.gametype['limitType']])
|
||||||
return retstring
|
return retstring
|
||||||
|
|
||||||
|
@ -553,6 +561,8 @@ Map the tuple self.gametype onto the pokerstars string describing it
|
||||||
return ("%s: posts big blind %s%s%s" %(act[0], self.sym, act[3], ' and is all-in' if act[4] else ''))
|
return ("%s: posts big blind %s%s%s" %(act[0], self.sym, act[3], ' and is all-in' if act[4] else ''))
|
||||||
elif(act[2] == "both"):
|
elif(act[2] == "both"):
|
||||||
return ("%s: posts small & big blinds %s%s%s" %(act[0], self.sym, act[3], ' and is all-in' if act[4] else ''))
|
return ("%s: posts small & big blinds %s%s%s" %(act[0], self.sym, act[3], ' and is all-in' if act[4] else ''))
|
||||||
|
elif(act[2] == "ante"):
|
||||||
|
return ("%s: posts the ante %s%s%s" %(act[0], self.sym, act[3], ' and is all-in' if act[4] else ''))
|
||||||
elif act[1] == 'bringin':
|
elif act[1] == 'bringin':
|
||||||
return ("%s: brings in for %s%s%s" %(act[0], self.sym, act[2], ' and is all-in' if act[3] else ''))
|
return ("%s: brings in for %s%s%s" %(act[0], self.sym, act[2], ' and is all-in' if act[3] else ''))
|
||||||
elif act[1] == 'discards':
|
elif act[1] == 'discards':
|
||||||
|
@ -564,23 +574,33 @@ Map the tuple self.gametype onto the pokerstars string describing it
|
||||||
"""Return a string of the stakes of the current hand."""
|
"""Return a string of the stakes of the current hand."""
|
||||||
return "%s%s/%s%s" % (self.sym, self.sb, self.sym, self.bb)
|
return "%s%s/%s%s" % (self.sym, self.sb, self.sym, self.bb)
|
||||||
|
|
||||||
|
def getStreetTotals(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def writeGameLine(self):
|
def writeGameLine(self):
|
||||||
"""Return the first HH line for the current hand."""
|
"""Return the first HH line for the current hand."""
|
||||||
gs = "PokerStars Game #%s: " % self.handid
|
gs = "PokerStars Game #%s: " % self.handid
|
||||||
|
|
||||||
if self.tourNo != None and self.mixed != None: # mixed tournament
|
if self.tourNo != None and self.mixed != None: # mixed tournament
|
||||||
gs = gs + "Tournament #%s, %s %s (%s) - Level %s (%s) - " % (self.tourNo, self.buyin, self.MS[self.mixed], self.getGameTypeAsString(), self.level, self.getStakesAsString())
|
gs = gs + "Tournament #%s, %s %s (%s) - Level %s (%s) - " % (self.tourNo, self.buyin, self.MS[self.mixed], self.getGameTypeAsString(), self.level, self.getStakesAsString())
|
||||||
elif self.tourNo != None: # all other tournaments
|
elif self.tourNo != None: # all other tournaments
|
||||||
gs = gs + "Tournament #%s, %s %s - Level %s (%s) - " % (self.tourNo,
|
gs = gs + "Tournament #%s, %s %s - Level %s (%s) - " % (self.tourNo,
|
||||||
self.buyin, self.getGameTypeAsString(), self.level, self.getStakesAsString())
|
self.buyin, self.getGameTypeAsString(), self.level, self.getStakesAsString())
|
||||||
elif self.mixed != None: # all other mixed games
|
elif self.mixed != None: # all other mixed games
|
||||||
gs = gs + " %s (%s, %s) - " % (self.MS[self.mixed],
|
gs = gs + " %s (%s, %s) - " % (self.MS[self.mixed],
|
||||||
self.getGameTypeAsString(), self.getStakesAsString())
|
self.getGameTypeAsString(), self.getStakesAsString())
|
||||||
else: # non-mixed cash games
|
else: # non-mixed cash games
|
||||||
gs = gs + " %s (%s) - " % (self.getGameTypeAsString(), self.getStakesAsString())
|
gs = gs + " %s (%s) - " % (self.getGameTypeAsString(), self.getStakesAsString())
|
||||||
|
|
||||||
return gs + datetime.datetime.strftime(self.starttime,'%Y/%m/%d %H:%M:%S ET')
|
try:
|
||||||
|
timestr = datetime.datetime.strftime(self.starttime, '%Y/%m/%d %H:%M:%S ET')
|
||||||
|
except TypeError:
|
||||||
|
print "*** ERROR - HAND: calling writeGameLine with unexpected STARTTIME value, expecting datetime.date object, received:", self.starttime
|
||||||
|
print "*** Make sure your HandHistoryConverter is setting hand.starttime properly!"
|
||||||
|
print "*** Game String:", gs
|
||||||
|
return gs
|
||||||
|
else:
|
||||||
|
return gs + timestr
|
||||||
|
|
||||||
def writeTableLine(self):
|
def writeTableLine(self):
|
||||||
table_string = "Table \'%s\' %s-max" % (self.tablename, self.maxseats)
|
table_string = "Table \'%s\' %s-max" % (self.tablename, self.maxseats)
|
||||||
|
@ -593,15 +613,15 @@ Map the tuple self.gametype onto the pokerstars string describing it
|
||||||
|
|
||||||
def writeHand(self, fh=sys.__stdout__):
|
def writeHand(self, fh=sys.__stdout__):
|
||||||
# PokerStars format.
|
# PokerStars format.
|
||||||
print >>fh, self.writeGameLine()
|
print >>fh, self.writeGameLine()
|
||||||
print >>fh, self.writeTableLine()
|
print >>fh, self.writeTableLine()
|
||||||
|
|
||||||
|
|
||||||
class HoldemOmahaHand(Hand):
|
class HoldemOmahaHand(Hand):
|
||||||
def __init__(self, hhc, sitename, gametype, handText, builtFrom = "HHC", handid=None):
|
def __init__(self, hhc, sitename, gametype, handText, builtFrom = "HHC", handid=None):
|
||||||
if gametype['base'] != 'hold':
|
if gametype['base'] != 'hold':
|
||||||
pass # or indeed don't pass and complain instead
|
pass # or indeed don't pass and complain instead
|
||||||
logging.debug("HoldemOmahaHand")
|
log.debug("HoldemOmahaHand")
|
||||||
self.allStreets = ['BLINDSANTES', 'PREFLOP','FLOP','TURN','RIVER']
|
self.allStreets = ['BLINDSANTES', 'PREFLOP','FLOP','TURN','RIVER']
|
||||||
self.holeStreets = ['PREFLOP']
|
self.holeStreets = ['PREFLOP']
|
||||||
self.communityStreets = ['FLOP', 'TURN', 'RIVER']
|
self.communityStreets = ['FLOP', 'TURN', 'RIVER']
|
||||||
|
@ -609,9 +629,9 @@ class HoldemOmahaHand(Hand):
|
||||||
Hand.__init__(self, sitename, gametype, handText, builtFrom = "HHC")
|
Hand.__init__(self, sitename, gametype, handText, builtFrom = "HHC")
|
||||||
self.sb = gametype['sb']
|
self.sb = gametype['sb']
|
||||||
self.bb = gametype['bb']
|
self.bb = gametype['bb']
|
||||||
|
|
||||||
#Populate a HoldemOmahaHand
|
#Populate a HoldemOmahaHand
|
||||||
#Generally, we call 'read' methods here, which get the info according to the particular filter (hhc)
|
#Generally, we call 'read' methods here, which get the info according to the particular filter (hhc)
|
||||||
# which then invokes a 'addXXX' callback
|
# which then invokes a 'addXXX' callback
|
||||||
if builtFrom == "HHC":
|
if builtFrom == "HHC":
|
||||||
hhc.readHandInfo(self)
|
hhc.readHandInfo(self)
|
||||||
|
@ -619,6 +639,7 @@ class HoldemOmahaHand(Hand):
|
||||||
hhc.compilePlayerRegexs(self)
|
hhc.compilePlayerRegexs(self)
|
||||||
hhc.markStreets(self)
|
hhc.markStreets(self)
|
||||||
hhc.readBlinds(self)
|
hhc.readBlinds(self)
|
||||||
|
hhc.readAntes(self)
|
||||||
hhc.readButton(self)
|
hhc.readButton(self)
|
||||||
hhc.readHeroCards(self)
|
hhc.readHeroCards(self)
|
||||||
hhc.readShowdownActions(self)
|
hhc.readShowdownActions(self)
|
||||||
|
@ -629,6 +650,7 @@ class HoldemOmahaHand(Hand):
|
||||||
for street in self.actionStreets:
|
for street in self.actionStreets:
|
||||||
if self.streets[street]:
|
if self.streets[street]:
|
||||||
hhc.readAction(self, street)
|
hhc.readAction(self, street)
|
||||||
|
self.pot.markTotal(street)
|
||||||
hhc.readCollectPot(self)
|
hhc.readCollectPot(self)
|
||||||
hhc.readShownCards(self)
|
hhc.readShownCards(self)
|
||||||
self.totalPot() # finalise it (total the pot)
|
self.totalPot() # finalise it (total the pot)
|
||||||
|
@ -640,11 +662,11 @@ class HoldemOmahaHand(Hand):
|
||||||
if handid is not None:
|
if handid is not None:
|
||||||
self.select(handid) # Will need a handId
|
self.select(handid) # Will need a handId
|
||||||
else:
|
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:
|
else:
|
||||||
logging.warning("HoldemOmahaHand.__init__:Neither HHC nor DB+handid provided")
|
log.warning("HoldemOmahaHand.__init__:Neither HHC nor DB+handid provided")
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def addShownCards(self, cards, player, shown=True, mucked=False, dealt=False):
|
def addShownCards(self, cards, player, shown=True, mucked=False, dealt=False):
|
||||||
if player == self.hero: # we have hero's cards just update shown/mucked
|
if player == self.hero: # we have hero's cards just update shown/mucked
|
||||||
|
@ -653,8 +675,20 @@ class HoldemOmahaHand(Hand):
|
||||||
else:
|
else:
|
||||||
self.addHoleCards('PREFLOP', player, open=[], closed=cards, shown=shown, mucked=mucked, dealt=dealt)
|
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__):
|
def writeHTMLHand(self):
|
||||||
from nevow import tags as T
|
from nevow import tags as T
|
||||||
from nevow import flat
|
from nevow import flat
|
||||||
players_who_act_preflop = (([x[0] for x in self.actions['PREFLOP']]+[x[0] for x in self.actions['BLINDSANTES']]))
|
players_who_act_preflop = (([x[0] for x in self.actions['PREFLOP']]+[x[0] for x in self.actions['BLINDSANTES']]))
|
||||||
|
@ -687,7 +721,7 @@ class HoldemOmahaHand(Hand):
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
if street in self.actionStreets and self.actions[street]:
|
if street in self.actionStreets and self.actions[street]:
|
||||||
lines.append(
|
lines.append(
|
||||||
T.ol(class_='actions', data=self.actions[street], render=render_action) [
|
T.ol(class_='actions', data=self.actions[street], render=render_action) [
|
||||||
T.li(pattern='list_item')[ T.slot(name='action') ]
|
T.li(pattern='list_item')[ T.slot(name='action') ]
|
||||||
]
|
]
|
||||||
|
@ -721,8 +755,8 @@ class HoldemOmahaHand(Hand):
|
||||||
context.tag[ pat().fillSlots('action', x)]
|
context.tag[ pat().fillSlots('action', x)]
|
||||||
return context.tag
|
return context.tag
|
||||||
|
|
||||||
s = T.p[
|
s = T.p[
|
||||||
T.h1[
|
T.h1[
|
||||||
T.span(class_='site')["%s Game #%s]" % ('PokerStars', self.handid)],
|
T.span(class_='site')["%s Game #%s]" % ('PokerStars', self.handid)],
|
||||||
T.span(class_='type_limit')[ "%s ($%s/$%s)" %(self.getGameTypeAsString(), self.sb, self.bb) ],
|
T.span(class_='type_limit')[ "%s ($%s/$%s)" %(self.getGameTypeAsString(), self.sb, self.bb) ],
|
||||||
T.span(class_='date')[ datetime.datetime.strftime(self.starttime,'%Y/%m/%d - %H:%M:%S ET') ]
|
T.span(class_='date')[ datetime.datetime.strftime(self.starttime,'%Y/%m/%d - %H:%M:%S ET') ]
|
||||||
|
@ -748,13 +782,13 @@ class HoldemOmahaHand(Hand):
|
||||||
|
|
||||||
return str(tidy.parseString(flat.flatten(s), **options))
|
return str(tidy.parseString(flat.flatten(s), **options))
|
||||||
|
|
||||||
|
|
||||||
def writeHand(self, fh=sys.__stdout__):
|
def writeHand(self, fh=sys.__stdout__):
|
||||||
# PokerStars format.
|
# PokerStars format.
|
||||||
super(HoldemOmahaHand, self).writeHand(fh)
|
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']]))
|
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]:
|
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
|
#Only print stacks of players who do something preflop
|
||||||
print >>fh, ("Seat %s: %s ($%s in chips) " %(player[0], player[1], player[2]))
|
print >>fh, ("Seat %s: %s ($%s in chips) " %(player[0], player[1], player[2]))
|
||||||
|
@ -762,7 +796,7 @@ class HoldemOmahaHand(Hand):
|
||||||
if self.actions['BLINDSANTES']:
|
if self.actions['BLINDSANTES']:
|
||||||
for act in self.actions['BLINDSANTES']:
|
for act in self.actions['BLINDSANTES']:
|
||||||
print >>fh, self.actionString(act)
|
print >>fh, self.actionString(act)
|
||||||
|
|
||||||
print >>fh, ("*** HOLE CARDS ***")
|
print >>fh, ("*** HOLE CARDS ***")
|
||||||
for player in self.dealt:
|
for player in self.dealt:
|
||||||
print >>fh, ("Dealt to %s [%s]" %(player, " ".join(self.holecards['PREFLOP'][player][1])))
|
print >>fh, ("Dealt to %s [%s]" %(player, " ".join(self.holecards['PREFLOP'][player][1])))
|
||||||
|
@ -808,7 +842,7 @@ class HoldemOmahaHand(Hand):
|
||||||
numOfHoleCardsNeeded = 2
|
numOfHoleCardsNeeded = 2
|
||||||
if len(self.holecards['PREFLOP'][name]) == numOfHoleCardsNeeded:
|
if len(self.holecards['PREFLOP'][name]) == numOfHoleCardsNeeded:
|
||||||
print >>fh, ("%s shows [%s] (a hand...)" % (name, " ".join(self.holecards['PREFLOP'][name][1])))
|
print >>fh, ("%s shows [%s] (a hand...)" % (name, " ".join(self.holecards['PREFLOP'][name][1])))
|
||||||
|
|
||||||
# Current PS format has the lines:
|
# Current PS format has the lines:
|
||||||
# Uncalled bet ($111.25) returned to s0rrow
|
# Uncalled bet ($111.25) returned to s0rrow
|
||||||
# s0rrow collected $5.15 from side pot
|
# s0rrow collected $5.15 from side pot
|
||||||
|
@ -850,7 +884,7 @@ class HoldemOmahaHand(Hand):
|
||||||
print >>fh, ("Seat %d: %s mucked" % (seatnum, name))
|
print >>fh, ("Seat %d: %s mucked" % (seatnum, name))
|
||||||
|
|
||||||
print >>fh, "\n\n"
|
print >>fh, "\n\n"
|
||||||
|
|
||||||
class DrawHand(Hand):
|
class DrawHand(Hand):
|
||||||
def __init__(self, hhc, sitename, gametype, handText, builtFrom = "HHC"):
|
def __init__(self, hhc, sitename, gametype, handText, builtFrom = "HHC"):
|
||||||
if gametype['base'] != 'draw':
|
if gametype['base'] != 'draw':
|
||||||
|
@ -870,6 +904,7 @@ class DrawHand(Hand):
|
||||||
hhc.compilePlayerRegexs(self)
|
hhc.compilePlayerRegexs(self)
|
||||||
hhc.markStreets(self)
|
hhc.markStreets(self)
|
||||||
hhc.readBlinds(self)
|
hhc.readBlinds(self)
|
||||||
|
hhc.readAntes(self)
|
||||||
hhc.readButton(self)
|
hhc.readButton(self)
|
||||||
hhc.readHeroCards(self)
|
hhc.readHeroCards(self)
|
||||||
hhc.readShowdownActions(self)
|
hhc.readShowdownActions(self)
|
||||||
|
@ -877,6 +912,7 @@ class DrawHand(Hand):
|
||||||
for street in self.streetList:
|
for street in self.streetList:
|
||||||
if self.streets[street]:
|
if self.streets[street]:
|
||||||
hhc.readAction(self, street)
|
hhc.readAction(self, street)
|
||||||
|
self.pot.markTotal(street)
|
||||||
hhc.readCollectPot(self)
|
hhc.readCollectPot(self)
|
||||||
hhc.readShownCards(self)
|
hhc.readShownCards(self)
|
||||||
self.totalPot() # finalise it (total the pot)
|
self.totalPot() # finalise it (total the pot)
|
||||||
|
@ -895,9 +931,9 @@ class DrawHand(Hand):
|
||||||
# - this is a bet of 1 sb, as yet uncalled.
|
# - this is a bet of 1 sb, as yet uncalled.
|
||||||
# Player in the big blind posts
|
# Player in the big blind posts
|
||||||
# - this is a call of 1 sb and a raise to 1 bb
|
# - 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:
|
if player is not None:
|
||||||
self.bets['DEAL'][player].append(Decimal(amount))
|
self.bets['DEAL'][player].append(Decimal(amount))
|
||||||
self.stacks[player] -= Decimal(amount)
|
self.stacks[player] -= Decimal(amount)
|
||||||
|
@ -906,7 +942,7 @@ class DrawHand(Hand):
|
||||||
self.actions['BLINDSANTES'].append(act)
|
self.actions['BLINDSANTES'].append(act)
|
||||||
self.pot.addMoney(player, Decimal(amount))
|
self.pot.addMoney(player, Decimal(amount))
|
||||||
if blindtype == 'big blind':
|
if blindtype == 'big blind':
|
||||||
self.lastBet['DEAL'] = Decimal(amount)
|
self.lastBet['DEAL'] = Decimal(amount)
|
||||||
elif blindtype == 'both':
|
elif blindtype == 'both':
|
||||||
# extra small blind is 'dead'
|
# extra small blind is 'dead'
|
||||||
self.lastBet['DEAL'] = Decimal(self.bb)
|
self.lastBet['DEAL'] = Decimal(self.bb)
|
||||||
|
@ -924,7 +960,7 @@ class DrawHand(Hand):
|
||||||
|
|
||||||
|
|
||||||
def discardDrawHoleCards(self, cards, player, street):
|
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])
|
self.discards[street][player] = set([cards])
|
||||||
|
|
||||||
|
|
||||||
|
@ -937,6 +973,14 @@ class DrawHand(Hand):
|
||||||
act = (player, 'discards', num)
|
act = (player, 'discards', num)
|
||||||
self.actions[street].append(act)
|
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__):
|
def writeHand(self, fh=sys.__stdout__):
|
||||||
# PokerStars format.
|
# PokerStars format.
|
||||||
|
@ -1018,17 +1062,17 @@ class StudHand(Hand):
|
||||||
if gametype['base'] != 'stud':
|
if gametype['base'] != 'stud':
|
||||||
pass # or indeed don't pass and complain instead
|
pass # or indeed don't pass and complain instead
|
||||||
|
|
||||||
self.allStreets = ['ANTES','THIRD','FOURTH','FIFTH','SIXTH','SEVENTH']
|
self.allStreets = ['BLINDSANTES','THIRD','FOURTH','FIFTH','SIXTH','SEVENTH']
|
||||||
self.communityStreets = []
|
self.communityStreets = []
|
||||||
self.actionStreets = ['ANTES','THIRD','FOURTH','FIFTH','SIXTH','SEVENTH']
|
self.actionStreets = ['BLINDSANTES','THIRD','FOURTH','FIFTH','SIXTH','SEVENTH']
|
||||||
|
|
||||||
self.streetList = ['ANTES','THIRD','FOURTH','FIFTH','SIXTH','SEVENTH'] # a list of the observed street names in order
|
self.streetList = ['BLINDSANTES','THIRD','FOURTH','FIFTH','SIXTH','SEVENTH'] # a list of the observed street names in order
|
||||||
self.holeStreets = ['THIRD','FOURTH','FIFTH','SIXTH','SEVENTH']
|
self.holeStreets = ['THIRD','FOURTH','FIFTH','SIXTH','SEVENTH']
|
||||||
Hand.__init__(self, sitename, gametype, handText)
|
Hand.__init__(self, sitename, gametype, handText)
|
||||||
self.sb = gametype['sb']
|
self.sb = gametype['sb']
|
||||||
self.bb = gametype['bb']
|
self.bb = gametype['bb']
|
||||||
#Populate the StudHand
|
#Populate the StudHand
|
||||||
#Generally, we call a 'read' method here, which gets the info according to the particular filter (hhc)
|
#Generally, we call a 'read' method here, which gets the info according to the particular filter (hhc)
|
||||||
# which then invokes a 'addXXX' callback
|
# which then invokes a 'addXXX' callback
|
||||||
if builtFrom == "HHC":
|
if builtFrom == "HHC":
|
||||||
hhc.readHandInfo(self)
|
hhc.readHandInfo(self)
|
||||||
|
@ -1040,11 +1084,11 @@ class StudHand(Hand):
|
||||||
hhc.readHeroCards(self)
|
hhc.readHeroCards(self)
|
||||||
# Read actions in street order
|
# Read actions in street order
|
||||||
for street in self.actionStreets:
|
for street in self.actionStreets:
|
||||||
if street == 'ANTES': continue # OMG--sometime someone folds in the ante round
|
if street == 'BLINDSANTES': continue # OMG--sometime someone folds in the ante round
|
||||||
if self.streets[street]:
|
if self.streets[street]:
|
||||||
logging.debug(street)
|
log.debug(street + self.streets[street])
|
||||||
logging.debug(self.streets[street])
|
|
||||||
hhc.readAction(self, street)
|
hhc.readAction(self, street)
|
||||||
|
self.pot.markTotal(street)
|
||||||
hhc.readCollectPot(self)
|
hhc.readCollectPot(self)
|
||||||
hhc.readShownCards(self) # not done yet
|
hhc.readShownCards(self) # not done yet
|
||||||
self.totalPot() # finalise it (total the pot)
|
self.totalPot() # finalise it (total the pot)
|
||||||
|
@ -1075,7 +1119,7 @@ street (string) the street name (in streetList)
|
||||||
open list of card bigrams e.g. ['2h','Jc'], dealt face up
|
open list of card bigrams e.g. ['2h','Jc'], dealt face up
|
||||||
closed likewise, but known only to player
|
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:
|
try:
|
||||||
self.checkPlayerExists(player)
|
self.checkPlayerExists(player)
|
||||||
self.holecards[street][player] = (open, closed)
|
self.holecards[street][player] = (open, closed)
|
||||||
|
@ -1089,7 +1133,7 @@ closed likewise, but known only to player
|
||||||
"""\
|
"""\
|
||||||
Add a complete on [street] by [player] to [amountTo]
|
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
|
amountTo = re.sub(u',', u'', amountTo) #some sites have commas
|
||||||
self.checkPlayerExists(player)
|
self.checkPlayerExists(player)
|
||||||
Bp = self.lastBet['THIRD']
|
Bp = self.lastBet['THIRD']
|
||||||
|
@ -1104,10 +1148,10 @@ Add a complete on [street] by [player] to [amountTo]
|
||||||
#~ self.actions[street].append(act)
|
#~ self.actions[street].append(act)
|
||||||
#~ self.lastBet[street] = Rt # TODO check this is correct
|
#~ self.lastBet[street] = Rt # TODO check this is correct
|
||||||
#~ self.pot.addMoney(player, C+Rb)
|
#~ self.pot.addMoney(player, C+Rb)
|
||||||
|
|
||||||
def addBringIn(self, player, bringin):
|
def addBringIn(self, player, bringin):
|
||||||
if player is not None:
|
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.bets['THIRD'][player].append(Decimal(bringin))
|
||||||
self.stacks[player] -= Decimal(bringin)
|
self.stacks[player] -= Decimal(bringin)
|
||||||
act = (player, 'bringin', bringin, self.stacks[player]==0)
|
act = (player, 'bringin', bringin, self.stacks[player]==0)
|
||||||
|
@ -1115,20 +1159,28 @@ Add a complete on [street] by [player] to [amountTo]
|
||||||
self.lastBet['THIRD'] = Decimal(bringin)
|
self.lastBet['THIRD'] = Decimal(bringin)
|
||||||
self.pot.addMoney(player, 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__):
|
def writeHand(self, fh=sys.__stdout__):
|
||||||
# PokerStars format.
|
# PokerStars format.
|
||||||
|
|
||||||
super(StudHand, self).writeHand(fh)
|
super(StudHand, self).writeHand(fh)
|
||||||
|
|
||||||
players_who_post_antes = set([x[0] for x in self.actions['ANTES']])
|
players_who_post_antes = set([x[0] for x in self.actions['BLINDSANTES']])
|
||||||
|
|
||||||
for player in [x for x in self.players if x[1] in players_who_post_antes]:
|
for player in [x for x in self.players if x[1] in players_who_post_antes]:
|
||||||
#Only print stacks of players who do something preflop
|
#Only print stacks of players who do something preflop
|
||||||
print >>fh, _("Seat %s: %s (%s%s in chips)" %(player[0], player[1], self.sym, player[2]))
|
print >>fh, _("Seat %s: %s (%s%s in chips)" %(player[0], player[1], self.sym, player[2]))
|
||||||
|
|
||||||
if 'ANTES' in self.actions:
|
if 'BLINDSANTES' in self.actions:
|
||||||
for act in self.actions['ANTES']:
|
for act in self.actions['BLINDSANTES']:
|
||||||
print >>fh, _("%s: posts the ante %s%s" %(act[0], self.sym, act[3]))
|
print >>fh, _("%s: posts the ante %s%s" %(act[0], self.sym, act[3]))
|
||||||
|
|
||||||
if 'THIRD' in self.actions:
|
if 'THIRD' in self.actions:
|
||||||
|
@ -1251,7 +1303,7 @@ Add a complete on [street] by [player] to [amountTo]
|
||||||
else:
|
else:
|
||||||
return hc + " ".join(self.holecards[street][player][0]) + ']'
|
return hc + " ".join(self.holecards[street][player][0]) + ']'
|
||||||
|
|
||||||
if street == 'SEVENTH' and player != self.hero: return # only write 7th st line for hero, LDO
|
if street == 'SEVENTH' and player != self.hero: return # only write 7th st line for hero, LDO
|
||||||
return hc + " ".join(self.holecards[street][player][1]) + "] [" + " ".join(self.holecards[street][player][0]) + "]"
|
return hc + " ".join(self.holecards[street][player][1]) + "] [" + " ".join(self.holecards[street][player][0]) + "]"
|
||||||
|
|
||||||
def join_holecards(self, player):
|
def join_holecards(self, player):
|
||||||
|
@ -1273,30 +1325,43 @@ class Pot(object):
|
||||||
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.contenders = set()
|
self.contenders = set()
|
||||||
self.committed = {}
|
self.committed = {}
|
||||||
self.total = None
|
self.streettotals = {}
|
||||||
self.returned = {}
|
self.common = Decimal(0)
|
||||||
self.sym = u'$' # this is the default currency symbol
|
self.total = None
|
||||||
|
self.returned = {}
|
||||||
|
self.sym = u'$' # this is the default currency symbol
|
||||||
|
|
||||||
def setSym(self, sym):
|
def setSym(self, sym):
|
||||||
self.sym = sym
|
self.sym = sym
|
||||||
|
|
||||||
def addPlayer(self,player):
|
def addPlayer(self,player):
|
||||||
self.committed[player] = Decimal(0)
|
self.committed[player] = Decimal(0)
|
||||||
|
|
||||||
def addFold(self, player):
|
def addFold(self, player):
|
||||||
# addFold must be called when a player folds
|
# addFold must be called when a player folds
|
||||||
self.contenders.discard(player)
|
self.contenders.discard(player)
|
||||||
|
|
||||||
|
def addCommonMoney(self, amount):
|
||||||
|
self.common += amount
|
||||||
|
|
||||||
def addMoney(self, player, amount):
|
def addMoney(self, player, amount):
|
||||||
# addMoney must be called for any actions that put money in the pot, in the order they occur
|
# addMoney must be called for any actions that put money in the pot, in the order they occur
|
||||||
self.contenders.add(player)
|
self.contenders.add(player)
|
||||||
self.committed[player] += amount
|
self.committed[player] += amount
|
||||||
|
|
||||||
|
def markTotal(self, street):
|
||||||
|
self.streettotals[street] = sum(self.committed.values()) + self.common
|
||||||
|
|
||||||
|
def getTotalAtStreet(self, street):
|
||||||
|
if street in self.streettotals:
|
||||||
|
return self.streettotals[street]
|
||||||
|
return 0
|
||||||
|
|
||||||
def end(self):
|
def end(self):
|
||||||
self.total = sum(self.committed.values())
|
self.total = sum(self.committed.values()) + self.common
|
||||||
|
|
||||||
# Return any uncalled bet.
|
# Return any uncalled bet.
|
||||||
committed = sorted([ (v,k) for (k,v) in self.committed.items()])
|
committed = sorted([ (v,k) for (k,v) in self.committed.items()])
|
||||||
lastbet = committed[-1][0] - committed[-2][0]
|
lastbet = committed[-1][0] - committed[-2][0]
|
||||||
|
@ -1314,7 +1379,7 @@ class Pot(object):
|
||||||
self.pots = []
|
self.pots = []
|
||||||
while len(commitsall) > 0:
|
while len(commitsall) > 0:
|
||||||
commitslive = [(v,k) for (v,k) in commitsall if k in self.contenders]
|
commitslive = [(v,k) for (v,k) in commitsall if k in self.contenders]
|
||||||
v1 = commitslive[0][0]
|
v1 = commitslive[0][0]
|
||||||
self.pots += [sum([min(v,v1) for (v,k) in commitsall])]
|
self.pots += [sum([min(v,v1) for (v,k) in commitsall])]
|
||||||
commitsall = [((v-v1),k) for (v,k) in commitsall if v-v1 >0]
|
commitsall = [((v-v1),k) for (v,k) in commitsall if v-v1 >0]
|
||||||
|
|
||||||
|
@ -1324,7 +1389,7 @@ class Pot(object):
|
||||||
# and y+z+r = x
|
# and y+z+r = x
|
||||||
# for example:
|
# for example:
|
||||||
# Total pot $124.30 Main pot $98.90. Side pot $23.40. | Rake $2
|
# Total pot $124.30 Main pot $98.90. Side pot $23.40. | Rake $2
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
if self.sym is None:
|
if self.sym is None:
|
||||||
self.sym = "C"
|
self.sym = "C"
|
||||||
|
@ -1332,17 +1397,17 @@ class Pot(object):
|
||||||
print "call Pot.end() before printing pot total"
|
print "call Pot.end() before printing pot total"
|
||||||
# NB if I'm sure end() is idempotent, call it here.
|
# NB if I'm sure end() is idempotent, call it here.
|
||||||
raise FpdbParseError
|
raise FpdbParseError
|
||||||
|
|
||||||
ret = "Total pot %s%.2f" % (self.sym, self.total)
|
ret = "Total pot %s%.2f" % (self.sym, self.total)
|
||||||
if len(self.pots) < 2:
|
if len(self.pots) < 2:
|
||||||
return ret;
|
return ret;
|
||||||
ret += " Main pot %s%.2f" % (self.sym, self.pots[0])
|
ret += " Main pot %s%.2f" % (self.sym, self.pots[0])
|
||||||
|
|
||||||
return ret + ''.join([ (" Side pot %s%.2f." % (self.sym, self.pots[x]) ) for x in xrange(1, len(self.pots)) ])
|
return ret + ''.join([ (" Side pot %s%.2f." % (self.sym, self.pots[x]) ) for x in xrange(1, len(self.pots)) ])
|
||||||
|
|
||||||
def assemble(cnxn, handid):
|
def assemble(cnxn, handid):
|
||||||
c = cnxn.cursor()
|
c = cnxn.cursor()
|
||||||
|
|
||||||
# We need at least sitename, gametype, handid
|
# We need at least sitename, gametype, handid
|
||||||
# for the Hand.__init__
|
# for the Hand.__init__
|
||||||
c.execute("""
|
c.execute("""
|
||||||
|
@ -1385,10 +1450,10 @@ limit 1""", {'handid':handid})
|
||||||
h.setCommunityCards('FLOP', cards[0:3])
|
h.setCommunityCards('FLOP', cards[0:3])
|
||||||
if cards[3]:
|
if cards[3]:
|
||||||
h.setCommunityCards('TURN', [cards[3]])
|
h.setCommunityCards('TURN', [cards[3]])
|
||||||
if cards[4]:
|
if cards[4]:
|
||||||
h.setCommunityCards('RIVER', [cards[4]])
|
h.setCommunityCards('RIVER', [cards[4]])
|
||||||
#[Card.valueSuitFromCard(x) for x in cards]
|
#[Card.valueSuitFromCard(x) for x in cards]
|
||||||
|
|
||||||
# HandInfo : HID, TABLE
|
# HandInfo : HID, TABLE
|
||||||
# BUTTON - why is this treated specially in Hand?
|
# BUTTON - why is this treated specially in Hand?
|
||||||
# answer: it is written out in hand histories
|
# answer: it is written out in hand histories
|
||||||
|
@ -1406,7 +1471,7 @@ WHERE h.id = %(handid)s
|
||||||
h.handid = res[0]
|
h.handid = res[0]
|
||||||
h.tablename = res[1]
|
h.tablename = res[1]
|
||||||
h.starttime = res[2] # automatically a datetime
|
h.starttime = res[2] # automatically a datetime
|
||||||
|
|
||||||
# PlayerStacks
|
# PlayerStacks
|
||||||
c.execute("""
|
c.execute("""
|
||||||
SELECT
|
SELECT
|
||||||
|
@ -1431,7 +1496,7 @@ and p.id = hp.playerid
|
||||||
h.addCollectPot(name, winnings)
|
h.addCollectPot(name, winnings)
|
||||||
if position == 'B':
|
if position == 'B':
|
||||||
h.buttonpos = seat
|
h.buttonpos = seat
|
||||||
|
|
||||||
|
|
||||||
# actions
|
# actions
|
||||||
c.execute("""
|
c.execute("""
|
||||||
|
@ -1479,7 +1544,7 @@ ORDER BY
|
||||||
#hc.readShownCards(self)
|
#hc.readShownCards(self)
|
||||||
h.totalPot()
|
h.totalPot()
|
||||||
h.rake = h.totalpot - h.totalcollected
|
h.rake = h.totalpot - h.totalcollected
|
||||||
|
|
||||||
|
|
||||||
return h
|
return h
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#Copyright 2008 Carl Gherardi
|
#Copyright 2008 Carl Gherardi
|
||||||
#This program is free software: you can redistribute it and/or modify
|
#This program is free software: you can redistribute it and/or modify
|
||||||
|
@ -16,10 +17,10 @@
|
||||||
#agpl-3.0.txt in the docs folder of the package.
|
#agpl-3.0.txt in the docs folder of the package.
|
||||||
|
|
||||||
import Hand
|
import Hand
|
||||||
|
import Tourney
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
import logging
|
|
||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
import os
|
import os
|
||||||
import os.path
|
import os.path
|
||||||
|
@ -30,25 +31,47 @@ import operator
|
||||||
from xml.dom.minidom import Node
|
from xml.dom.minidom import Node
|
||||||
import time
|
import time
|
||||||
import datetime
|
import datetime
|
||||||
|
from Exceptions import FpdbParseError
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
gettext.install('fpdb')
|
gettext.install('fpdb')
|
||||||
|
|
||||||
|
import logging, logging.config
|
||||||
|
logging.config.fileConfig(os.path.join(sys.path[0],"logging.conf"))
|
||||||
|
log = logging.getLogger("parser")
|
||||||
|
|
||||||
class HandHistoryConverter():
|
class HandHistoryConverter():
|
||||||
|
|
||||||
READ_CHUNK_SIZE = 10000 # bytes to read at a time from file (in tail mode)
|
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")
|
# 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.index = 0
|
||||||
|
|
||||||
self.in_path = in_path
|
self.in_path = in_path
|
||||||
self.out_path = out_path
|
self.out_path = out_path
|
||||||
|
|
||||||
self.processedHands = []
|
self.processedHands = []
|
||||||
|
|
||||||
|
# Tourney object used to store TourneyInfo when called to deal with a Summary file
|
||||||
|
self.tourney = None
|
||||||
|
|
||||||
if in_path == '-':
|
if in_path == '-':
|
||||||
self.in_fh = sys.stdin
|
self.in_fh = sys.stdin
|
||||||
|
@ -58,50 +81,91 @@ class HandHistoryConverter():
|
||||||
else:
|
else:
|
||||||
# TODO: out_path should be sanity checked.
|
# TODO: out_path should be sanity checked.
|
||||||
out_dir = os.path.dirname(self.out_path)
|
out_dir = os.path.dirname(self.out_path)
|
||||||
if not os.path.isdir(out_dir):
|
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)
|
os.makedirs(out_dir)
|
||||||
self.out_fh = open(self.out_path, 'w')
|
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.follow = follow
|
||||||
self.compiledPlayers = set()
|
self.compiledPlayers = set()
|
||||||
self.maxseats = 10
|
self.maxseats = 10
|
||||||
|
|
||||||
|
self.status = True
|
||||||
|
|
||||||
|
self.parsedObjectType = "HH" #default behaviour : parsing HH files, can be "Summary" if the parsing encounters a Summary File
|
||||||
|
|
||||||
|
if autostart:
|
||||||
|
self.start()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return """
|
return """
|
||||||
HandHistoryConverter: '%(sitename)s'
|
HandHistoryConverter: '%(sitename)s'
|
||||||
filetype: '%(filetype)s'
|
filetype '%(filetype)s'
|
||||||
in_path: '%(in_path)s'
|
in_path '%(in_path)s'
|
||||||
out_path: '%(out_path)s'
|
out_path '%(out_path)s'
|
||||||
""" % { 'sitename':self.sitename, 'filetype':self.filetype, 'in_path':self.in_path, 'out_path':self.out_path }
|
follow '%(follow)s'
|
||||||
|
""" % locals()
|
||||||
|
|
||||||
def start(self):
|
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.
|
If in follow mode, wait for more data to turn up.
|
||||||
Otherwise, finish at eof.
|
Otherwise, finish at EOF.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
starttime = time.time()
|
starttime = time.time()
|
||||||
if not self.sanityCheck():
|
if not self.sanityCheck():
|
||||||
print "Cowardly refusing to continue after failed sanity check"
|
log.warning("Failed sanity check")
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.follow:
|
try:
|
||||||
numHands = 0
|
numHands = 0
|
||||||
for handText in self.tailHands():
|
numErrors = 0
|
||||||
numHands+=1
|
if self.follow:
|
||||||
self.processHand(handText)
|
#TODO: See how summary files can be handled on the fly (here they should be rejected as before)
|
||||||
else:
|
log.info("Tailing '%s'" % self.in_path)
|
||||||
handsList = self.allHandsAsList()
|
for handText in self.tailHands():
|
||||||
logging.info("Parsing %d hands" % len(handsList))
|
try:
|
||||||
for handText in handsList:
|
self.processHand(handText)
|
||||||
self.processedHands.append(self.processHand(handText))
|
numHands+=1
|
||||||
numHands= len(handsList)
|
except FpdbParseError, e:
|
||||||
endtime = time.time()
|
numErrors+=1
|
||||||
print "read %d hands in %.3f seconds" % (numHands, endtime - starttime)
|
log.warning("Failed to convert hand %s" % e.hid)
|
||||||
if self.out_fh != sys.stdout:
|
log.debug(handText)
|
||||||
self.out_fh.close()
|
else:
|
||||||
|
handsList = self.allHandsAsList()
|
||||||
|
log.info("Parsing %d hands" % len(handsList))
|
||||||
|
# Determine if we're dealing with a HH file or a Summary file
|
||||||
|
# quick fix : empty files make the handsList[0] fail ==> If empty file, go on with HH parsing
|
||||||
|
if len(handsList) == 0 or self.isSummary(handsList[0]) == False:
|
||||||
|
self.parsedObjectType = "HH"
|
||||||
|
for handText in handsList:
|
||||||
|
try:
|
||||||
|
self.processedHands.append(self.processHand(handText))
|
||||||
|
except FpdbParseError, e:
|
||||||
|
numErrors+=1
|
||||||
|
log.warning("Failed to convert hand %s" % e.hid)
|
||||||
|
log.debug(handText)
|
||||||
|
numHands = len(handsList)
|
||||||
|
endtime = time.time()
|
||||||
|
log.info("Read %d hands (%d failed) in %.3f seconds" % (numHands, numErrors, endtime - starttime))
|
||||||
|
else:
|
||||||
|
self.parsedObjectType = "Summary"
|
||||||
|
summaryParsingStatus = self.readSummaryInfo(handsList)
|
||||||
|
endtime = time.time()
|
||||||
|
if summaryParsingStatus :
|
||||||
|
log.info("Summary file '%s' correctly parsed (took %.3f seconds)" % (self.in_path, endtime - starttime))
|
||||||
|
else :
|
||||||
|
log.warning("Error converting summary file '%s' (took %.3f seconds)" % (self.in_path, endtime - starttime))
|
||||||
|
|
||||||
|
except IOError, ioe:
|
||||||
|
log.exception("Error converting '%s'" % self.in_path)
|
||||||
|
finally:
|
||||||
|
if self.out_fh != sys.stdout:
|
||||||
|
self.out_fh.close()
|
||||||
|
|
||||||
|
|
||||||
def tailHands(self):
|
def tailHands(self):
|
||||||
|
@ -128,7 +192,7 @@ which it expects to find at self.re_TailSplitHands -- see for e.g. Everleaf.py.
|
||||||
time.sleep(interval)
|
time.sleep(interval)
|
||||||
fd.seek(where)
|
fd.seek(where)
|
||||||
else:
|
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 = codecs.open(self.in_path, 'r', self.codepage)
|
||||||
fd.seek(where)
|
fd.seek(where)
|
||||||
else:
|
else:
|
||||||
|
@ -173,13 +237,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.strip()
|
||||||
self.obs = self.obs.replace('\r\n', '\n')
|
self.obs = self.obs.replace('\r\n', '\n')
|
||||||
if self.obs == "" or self.obs == None:
|
if self.obs == "" or self.obs == None:
|
||||||
logging.info("Read no hands.")
|
log.info("Read no hands.")
|
||||||
return
|
return []
|
||||||
return re.split(self.re_SplitHands, self.obs)
|
return re.split(self.re_SplitHands, self.obs)
|
||||||
|
|
||||||
def processHand(self, handText):
|
def processHand(self, handText):
|
||||||
gametype = self.determineGameType(handText)
|
gametype = self.determineGameType(handText)
|
||||||
logging.debug("gametype %s" % gametype)
|
log.debug("gametype %s" % gametype)
|
||||||
hand = None
|
hand = None
|
||||||
if gametype is None:
|
if gametype is None:
|
||||||
l = None
|
l = None
|
||||||
|
@ -194,14 +258,14 @@ which it expects to find at self.re_TailSplitHands -- see for e.g. Everleaf.py.
|
||||||
l = [type] + [base] + [limit]
|
l = [type] + [base] + [limit]
|
||||||
if l in self.readSupportedGames():
|
if l in self.readSupportedGames():
|
||||||
if gametype['base'] == 'hold':
|
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)
|
hand = Hand.HoldemOmahaHand(self, self.sitename, gametype, handText)
|
||||||
elif gametype['base'] == 'stud':
|
elif gametype['base'] == 'stud':
|
||||||
hand = Hand.StudHand(self, self.sitename, gametype, handText)
|
hand = Hand.StudHand(self, self.sitename, gametype, handText)
|
||||||
elif gametype['base'] == 'draw':
|
elif gametype['base'] == 'draw':
|
||||||
hand = Hand.DrawHand(self, self.sitename, gametype, handText)
|
hand = Hand.DrawHand(self, self.sitename, gametype, handText)
|
||||||
else:
|
else:
|
||||||
logging.info("Unsupported game type: %s" % gametype)
|
log.info("Unsupported game type: %s" % gametype)
|
||||||
|
|
||||||
if hand:
|
if hand:
|
||||||
# uncomment these to calculate some stats
|
# uncomment these to calculate some stats
|
||||||
|
@ -210,7 +274,7 @@ which it expects to find at self.re_TailSplitHands -- see for e.g. Everleaf.py.
|
||||||
hand.writeHand(self.out_fh)
|
hand.writeHand(self.out_fh)
|
||||||
return hand
|
return hand
|
||||||
else:
|
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?
|
# 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 :)
|
# From the log we can deduce that it is the hand after the one before :)
|
||||||
|
|
||||||
|
@ -335,27 +399,36 @@ or None if we fail to get the info """
|
||||||
hands = hands + [Hand.Hand(self.sitename, self.gametype, l)]
|
hands = hands + [Hand.Hand(self.sitename, self.gametype, l)]
|
||||||
return hands
|
return hands
|
||||||
|
|
||||||
|
def __listof(self, x):
|
||||||
|
if isinstance(x, list) or isinstance(x, tuple): return x
|
||||||
|
else: return [x]
|
||||||
|
|
||||||
def readFile(self):
|
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.filetype == "text"):
|
||||||
if self.in_path == '-':
|
if self.in_path == '-':
|
||||||
# read from stdin
|
# 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)
|
in_fh = codecs.getreader('cp1252')(sys.stdin)
|
||||||
else:
|
else:
|
||||||
logging.debug("Opening %s with %s" % (self.in_path, self.codepage))
|
success = False
|
||||||
in_fh = codecs.open(self.in_path, 'r', self.codepage)
|
for kodec in self.__listof(self.codepage):
|
||||||
in_fh.seek(self.index)
|
if success: break
|
||||||
self.obs = in_fh.read()
|
print "trying", kodec
|
||||||
self.index = in_fh.tell()
|
try:
|
||||||
in_fh.close()
|
in_fh = codecs.open(self.in_path, 'r', kodec)
|
||||||
|
in_fh.seek(self.index)
|
||||||
|
log.debug("Opened in_path: '%s' with %s" % (self.in_path, kodec))
|
||||||
|
self.obs = in_fh.read()
|
||||||
|
self.index = in_fh.tell()
|
||||||
|
in_fh.close()
|
||||||
|
success = True
|
||||||
|
except:
|
||||||
|
pass
|
||||||
elif(self.filetype == "xml"):
|
elif(self.filetype == "xml"):
|
||||||
try:
|
doc = xml.dom.minidom.parse(filename)
|
||||||
doc = xml.dom.minidom.parse(filename)
|
self.doc = doc
|
||||||
self.doc = doc
|
|
||||||
except:
|
|
||||||
traceback.print_exc(file=sys.stderr)
|
|
||||||
|
|
||||||
def guessMaxSeats(self, hand):
|
def guessMaxSeats(self, hand):
|
||||||
"""Return a guess at max_seats when not specified in HH."""
|
"""Return a guess at max_seats when not specified in HH."""
|
||||||
|
@ -383,7 +456,7 @@ or None if we fail to get the info """
|
||||||
|
|
||||||
def getStatus(self):
|
def getStatus(self):
|
||||||
#TODO: Return a status of true if file processed ok
|
#TODO: Return a status of true if file processed ok
|
||||||
return True
|
return self.status
|
||||||
|
|
||||||
def getProcessedHands(self):
|
def getProcessedHands(self):
|
||||||
return self.processedHands
|
return self.processedHands
|
||||||
|
@ -393,3 +466,15 @@ or None if we fail to get the info """
|
||||||
|
|
||||||
def getLastCharacterRead(self):
|
def getLastCharacterRead(self):
|
||||||
return self.index
|
return self.index
|
||||||
|
|
||||||
|
def isSummary(self, topline):
|
||||||
|
return " Tournament Summary " in topline
|
||||||
|
|
||||||
|
def getParsedObjectType(self):
|
||||||
|
return self.parsedObjectType
|
||||||
|
|
||||||
|
#returns a status (True/False) indicating wether the parsing could be done correctly or not
|
||||||
|
def readSummaryInfo(self, summaryInfoList): abstract
|
||||||
|
|
||||||
|
def getTourney(self):
|
||||||
|
return self.tourney
|
||||||
|
|
282
pyfpdb/Hud.py
282
pyfpdb/Hud.py
|
@ -1,4 +1,5 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
"""Hud.py
|
"""Hud.py
|
||||||
|
|
||||||
Create and manage the hud overlays.
|
Create and manage the hud overlays.
|
||||||
|
@ -60,6 +61,8 @@ class Hud:
|
||||||
def __init__(self, parent, table, max, poker_game, config, db_connection):
|
def __init__(self, parent, table, max, poker_game, config, db_connection):
|
||||||
# __init__ is (now) intended to be called from the stdin thread, so it
|
# __init__ is (now) intended to be called from the stdin thread, so it
|
||||||
# cannot touch the gui
|
# cannot touch the gui
|
||||||
|
if parent == None: # running from cli ..
|
||||||
|
self.parent = self
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.table = table
|
self.table = table
|
||||||
self.config = config
|
self.config = config
|
||||||
|
@ -77,6 +80,10 @@ class Hud:
|
||||||
|
|
||||||
(font, font_size) = config.get_default_font(self.table.site)
|
(font, font_size) = config.get_default_font(self.table.site)
|
||||||
self.colors = config.get_default_colors(self.table.site)
|
self.colors = config.get_default_colors(self.table.site)
|
||||||
|
|
||||||
|
self.backgroundcolor = gtk.gdk.color_parse(self.colors['hudbgcolor'])
|
||||||
|
self.foregroundcolor = gtk.gdk.color_parse(self.colors['hudfgcolor'])
|
||||||
|
|
||||||
|
|
||||||
if font == None:
|
if font == None:
|
||||||
font = "Sans"
|
font = "Sans"
|
||||||
|
@ -93,91 +100,107 @@ class Hud:
|
||||||
if my_import == None:
|
if my_import == None:
|
||||||
continue
|
continue
|
||||||
self.aux_windows.append(my_import(self, config, aux_params))
|
self.aux_windows.append(my_import(self, config, aux_params))
|
||||||
|
|
||||||
|
self.creation_attrs = None
|
||||||
|
|
||||||
def create_mw(self):
|
def create_mw(self):
|
||||||
|
|
||||||
# Set up a main window for this this instance of the HUD
|
# Set up a main window for this this instance of the HUD
|
||||||
self.main_window = gtk.Window()
|
win = gtk.Window()
|
||||||
self.main_window.set_gravity(gtk.gdk.GRAVITY_STATIC)
|
win.set_gravity(gtk.gdk.GRAVITY_STATIC)
|
||||||
self.main_window.set_title("%s FPDBHUD" % (self.table.name))
|
win.set_title("%s FPDBHUD" % (self.table.name))
|
||||||
self.main_window.set_decorated(False)
|
win.set_decorated(False)
|
||||||
self.main_window.set_opacity(self.colors["hudopacity"])
|
win.set_opacity(self.colors["hudopacity"])
|
||||||
self.main_window.set_focus_on_map(False)
|
|
||||||
|
eventbox = gtk.EventBox()
|
||||||
self.ebox = gtk.EventBox()
|
label = gtk.Label("FPDB Menu - Right click\nLeft-Drag to Move")
|
||||||
self.label = gtk.Label("FPDB Menu (Right Click)\nLeft-drag to move")
|
|
||||||
|
win.add(eventbox)
|
||||||
self.backgroundcolor = gtk.gdk.color_parse(self.colors['hudbgcolor'])
|
eventbox.add(label)
|
||||||
self.foregroundcolor = gtk.gdk.color_parse(self.colors['hudfgcolor'])
|
|
||||||
|
label.modify_bg(gtk.STATE_NORMAL, self.backgroundcolor)
|
||||||
self.label.modify_bg(gtk.STATE_NORMAL, self.backgroundcolor)
|
label.modify_fg(gtk.STATE_NORMAL, self.foregroundcolor)
|
||||||
self.label.modify_fg(gtk.STATE_NORMAL, self.foregroundcolor)
|
|
||||||
|
eventbox.modify_bg(gtk.STATE_NORMAL, self.backgroundcolor)
|
||||||
self.main_window.add(self.ebox)
|
eventbox.modify_fg(gtk.STATE_NORMAL, self.foregroundcolor)
|
||||||
self.ebox.add(self.label)
|
|
||||||
|
|
||||||
self.ebox.modify_bg(gtk.STATE_NORMAL, self.backgroundcolor)
|
|
||||||
self.ebox.modify_fg(gtk.STATE_NORMAL, self.foregroundcolor)
|
|
||||||
|
|
||||||
|
self.main_window = win
|
||||||
self.main_window.move(self.table.x, self.table.y)
|
self.main_window.move(self.table.x, self.table.y)
|
||||||
|
|
||||||
# A popup menu for the main window
|
# A popup menu for the main window
|
||||||
self.menu = gtk.Menu()
|
menu = gtk.Menu()
|
||||||
self.item1 = gtk.MenuItem('Kill this HUD')
|
|
||||||
self.menu.append(self.item1)
|
|
||||||
self.item1.connect("activate", self.parent.kill_hud, self.table_name)
|
|
||||||
self.item1.show()
|
|
||||||
|
|
||||||
self.item2 = gtk.MenuItem('Save Layout')
|
killitem = gtk.MenuItem('Kill This HUD')
|
||||||
self.menu.append(self.item2)
|
menu.append(killitem)
|
||||||
self.item2.connect("activate", self.save_layout)
|
if self.parent != None:
|
||||||
self.item2.show()
|
killitem.connect("activate", self.parent.kill_hud, self.table_name)
|
||||||
|
|
||||||
self.item3 = gtk.MenuItem('Reposition Stats')
|
saveitem = gtk.MenuItem('Save HUD Layout')
|
||||||
self.menu.append(self.item3)
|
menu.append(saveitem)
|
||||||
self.item3.connect("activate", self.reposition_windows)
|
saveitem.connect("activate", self.save_layout)
|
||||||
self.item3.show()
|
|
||||||
|
|
||||||
self.item4 = gtk.MenuItem('Debug Stat Windows')
|
repositem = gtk.MenuItem('Reposition StatWindows')
|
||||||
self.menu.append(self.item4)
|
menu.append(repositem)
|
||||||
self.item4.connect("activate", self.debug_stat_windows)
|
repositem.connect("activate", self.reposition_windows)
|
||||||
self.item4.show()
|
|
||||||
|
|
||||||
self.ebox.connect_object("button-press-event", self.on_button_press, self.menu)
|
debugitem = gtk.MenuItem('Debug StatWindows')
|
||||||
|
menu.append(debugitem)
|
||||||
|
debugitem.connect("activate", self.debug_stat_windows)
|
||||||
|
|
||||||
|
item5 = gtk.MenuItem('Set max seats')
|
||||||
|
menu.append(item5)
|
||||||
|
maxSeatsMenu = gtk.Menu()
|
||||||
|
item5.set_submenu(maxSeatsMenu)
|
||||||
|
for i in range(2, 11, 1):
|
||||||
|
item = gtk.MenuItem('%d-max' % i)
|
||||||
|
item.ms = i
|
||||||
|
maxSeatsMenu.append(item)
|
||||||
|
item.connect("activate", self.change_max_seats)
|
||||||
|
setattr(self, 'maxSeatsMenuItem%d' % (i-1), item)
|
||||||
|
|
||||||
|
eventbox.connect_object("button-press-event", self.on_button_press, menu)
|
||||||
|
|
||||||
self.main_window.show_all()
|
|
||||||
self.mw_created = True
|
self.mw_created = True
|
||||||
|
self.label = label
|
||||||
# TODO: fold all uses of this type of 'topify' code into a single function, if the differences between the versions don't
|
menu.show_all()
|
||||||
# create adverse effects?
|
self.main_window.show_all()
|
||||||
|
self.topify_window(self.main_window)
|
||||||
if os.name == 'nt':
|
|
||||||
self.topify_window(self.main_window)
|
def change_max_seats(self, widget):
|
||||||
else:
|
if self.max != widget.ms:
|
||||||
self.main_window.parentgdkhandle = gtk.gdk.window_foreign_new(int(self.table.number)) # gets a gdk handle for poker client
|
print 'change_max_seats', widget.ms
|
||||||
self.main_window.gdkhandle = gtk.gdk.window_foreign_new(self.main_window.window.xid) # gets a gdk handle for the hud table window
|
self.max = widget.ms
|
||||||
self.main_window.gdkhandle.set_transient_for(self.main_window.parentgdkhandle) #
|
try:
|
||||||
|
self.kill()
|
||||||
self.update_table_position()
|
self.create(*self.creation_attrs)
|
||||||
|
self.update(self.hand, self.config)
|
||||||
|
except Exception, e:
|
||||||
|
print "Expcetion:",str(e)
|
||||||
|
pass
|
||||||
|
|
||||||
def update_table_position(self):
|
def update_table_position(self):
|
||||||
if os.name == 'nt':
|
if os.name == 'nt':
|
||||||
if not win32gui.IsWindow(self.table.number):
|
if not win32gui.IsWindow(self.table.number):
|
||||||
self.parent.kill_hud(self, self.table.name)
|
self.parent.kill_hud(self, self.table.name)
|
||||||
return False
|
return False
|
||||||
# anyone know how to do this in unix, or better yet, trap the X11 error that is triggered when executing the get_origin() for a closed window?
|
# anyone know how to do this in unix, or better yet, trap the X11 error that is triggered when executing the get_origin() for a closed window?
|
||||||
|
if self.table.gdkhandle is not None:
|
||||||
(x, y) = self.main_window.parentgdkhandle.get_origin()
|
(x, y) = self.table.gdkhandle.get_origin()
|
||||||
if self.table.x != x or self.table.y != y:
|
if self.table.x != x or self.table.y != y:
|
||||||
self.table.x = x
|
self.table.x = x
|
||||||
self.table.y = y
|
self.table.y = y
|
||||||
self.main_window.move(x, y)
|
self.main_window.move(x, y)
|
||||||
adj = self.adj_seats(self.hand, self.config)
|
adj = self.adj_seats(self.hand, self.config)
|
||||||
loc = self.config.get_locations(self.table.site, self.max)
|
loc = self.config.get_locations(self.table.site, self.max)
|
||||||
# TODO: is stat_windows getting converted somewhere from a list to a dict, for no good reason?
|
# TODO: is stat_windows getting converted somewhere from a list to a dict, for no good reason?
|
||||||
for i, w in enumerate(self.stat_windows.itervalues()):
|
for i, w in enumerate(self.stat_windows.itervalues()):
|
||||||
(x, y) = loc[adj[i+1]]
|
(x, y) = loc[adj[i+1]]
|
||||||
w.relocate(x, y)
|
w.relocate(x, y)
|
||||||
|
|
||||||
|
# While we're at it, fix the positions of mucked cards too
|
||||||
|
for aux in self.aux_windows:
|
||||||
|
aux.update_card_positions()
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def on_button_press(self, widget, event):
|
def on_button_press(self, widget, event):
|
||||||
|
@ -202,6 +225,7 @@ class Hud:
|
||||||
self.aux_windows = []
|
self.aux_windows = []
|
||||||
|
|
||||||
def reposition_windows(self, *args):
|
def reposition_windows(self, *args):
|
||||||
|
self.update_table_position()
|
||||||
for w in self.stat_windows.itervalues():
|
for w in self.stat_windows.itervalues():
|
||||||
if type(w) == int:
|
if type(w) == int:
|
||||||
# print "in reposition, w =", w
|
# print "in reposition, w =", w
|
||||||
|
@ -233,7 +257,7 @@ class Hud:
|
||||||
# Need range here, not xrange -> need the actual list
|
# Need range here, not xrange -> need the actual list
|
||||||
adj = range(0, self.max + 1) # default seat adjustments = no adjustment
|
adj = range(0, self.max + 1) # default seat adjustments = no adjustment
|
||||||
# does the user have a fav_seat?
|
# does the user have a fav_seat?
|
||||||
if int(config.supported_sites[self.table.site].layout[self.max].fav_seat) > 0:
|
if self.table.site != None and int(config.supported_sites[self.table.site].layout[self.max].fav_seat) > 0:
|
||||||
try:
|
try:
|
||||||
fav_seat = config.supported_sites[self.table.site].layout[self.max].fav_seat
|
fav_seat = config.supported_sites[self.table.site].layout[self.max].fav_seat
|
||||||
actual_seat = self.get_actual_seat(config.supported_sites[self.table.site].screen_name)
|
actual_seat = self.get_actual_seat(config.supported_sites[self.table.site].screen_name)
|
||||||
|
@ -261,6 +285,8 @@ class Hud:
|
||||||
#
|
#
|
||||||
# this method also manages the creating and destruction of stat
|
# this method also manages the creating and destruction of stat
|
||||||
# windows via calls to the Stat_Window class
|
# windows via calls to the Stat_Window class
|
||||||
|
self.creation_attrs = hand, config, stat_dict, cards
|
||||||
|
|
||||||
self.hand = hand
|
self.hand = hand
|
||||||
if not self.mw_created:
|
if not self.mw_created:
|
||||||
self.create_mw()
|
self.create_mw()
|
||||||
|
@ -333,30 +359,22 @@ class Hud:
|
||||||
Stats.do_tip(window.e_box[r][c], tip)
|
Stats.do_tip(window.e_box[r][c], tip)
|
||||||
|
|
||||||
def topify_window(self, window):
|
def topify_window(self, window):
|
||||||
# """Set the specified gtk window to stayontop in MS Windows."""
|
window.set_focus_on_map(False)
|
||||||
#
|
window.set_accept_focus(False)
|
||||||
# def windowEnumerationHandler(hwnd, resultList):
|
|
||||||
# '''Callback for win32gui.EnumWindows() to generate list of window handles.'''
|
if not self.table.gdkhandle:
|
||||||
# resultList.append((hwnd, win32gui.GetWindowText(hwnd)))
|
self.table.gdkhandle = gtk.gdk.window_foreign_new(int(self.table.number)) # gtk handle to poker window
|
||||||
# unique_name = 'unique name for finding this window'
|
# window.window.reparent(self.table.gdkhandle, 0, 0)
|
||||||
# real_name = window.get_title()
|
# window.map()
|
||||||
# window.set_title(unique_name)
|
# window.window.set_transient_for(self.table.gdkhandle)
|
||||||
# tl_windows = []
|
# if os.name == "nt":
|
||||||
# win32gui.EnumWindows(windowEnumerationHandler, tl_windows)
|
# print "window.window.handle=",window.window.handle
|
||||||
#
|
# oldparent = win32gui.SetParent(window.window.handle, self.table.number)
|
||||||
# for w in tl_windows:
|
# print "oldparent=",oldparent
|
||||||
# if w[1] == unique_name:
|
# win32gui.SendMessage(self.table.number, 0x0127) # WM_CHANGEUISTATE
|
||||||
self.main_window.parentgdkhandle = gtk.gdk.window_foreign_new(long(self.table.number))
|
# win32gui.SendMessage(self.table.number, 0x0128) # WM_UPDATEUISTATE
|
||||||
# self.main_window.gdkhandle = gtk.gdk.window_foreign_new(w[0])
|
# window.present()
|
||||||
self.main_window.gdkhandle = self.main_window.window
|
|
||||||
self.main_window.gdkhandle.set_transient_for(self.main_window.parentgdkhandle)
|
|
||||||
|
|
||||||
style = win32gui.GetWindowLong(self.table.number, win32con.GWL_EXSTYLE)
|
|
||||||
style |= win32con.WS_CLIPCHILDREN
|
|
||||||
win32gui.SetWindowLong(self.table.number, win32con.GWL_EXSTYLE, style)
|
|
||||||
# break
|
|
||||||
|
|
||||||
# window.set_title(real_name)
|
|
||||||
|
|
||||||
class Stat_Window:
|
class Stat_Window:
|
||||||
|
|
||||||
|
@ -366,21 +384,33 @@ class Stat_Window:
|
||||||
# and double-clicks.
|
# and double-clicks.
|
||||||
|
|
||||||
if event.button == 3: # right button event
|
if event.button == 3: # right button event
|
||||||
self.popups.append(Popup_window(widget, self))
|
newpopup = Popup_window(self.window, self)
|
||||||
|
#print "added popup", newpopup
|
||||||
|
# TODO: how should we go about making sure it doesn't open a dozen popups if you click?
|
||||||
|
self.popups.append(newpopup)
|
||||||
|
return True
|
||||||
|
|
||||||
if event.button == 2: # middle button event
|
if event.button == 2: # middle button event
|
||||||
self.window.hide()
|
self.window.hide()
|
||||||
|
return True
|
||||||
|
|
||||||
if event.button == 1: # left button event
|
if event.button == 1: # left button event
|
||||||
# TODO: make position saving save sizes as well?
|
# TODO: make position saving save sizes as well?
|
||||||
|
self.window.show_all()
|
||||||
if event.state & gtk.gdk.SHIFT_MASK:
|
if event.state & gtk.gdk.SHIFT_MASK:
|
||||||
self.window.begin_resize_drag(gtk.gdk.WINDOW_EDGE_SOUTH_EAST, event.button, int(event.x_root), int(event.y_root), event.time)
|
self.window.begin_resize_drag(gtk.gdk.WINDOW_EDGE_SOUTH_EAST, event.button, int(event.x_root), int(event.y_root), event.time)
|
||||||
else:
|
else:
|
||||||
self.window.begin_move_drag(event.button, int(event.x_root), int(event.y_root), event.time)
|
self.window.begin_move_drag(event.button, int(event.x_root), int(event.y_root), event.time)
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def noop(self, arga=None, argb=None): # i'm going to try to connect the focus-in and focus-out events here, to see if that fixes any of the focus problems.
|
||||||
|
return True
|
||||||
|
|
||||||
def kill_popup(self, popup):
|
def kill_popup(self, popup):
|
||||||
|
print "remove popup", popup
|
||||||
|
self.popups.remove(popup)
|
||||||
popup.window.destroy()
|
popup.window.destroy()
|
||||||
self.popups.remove(popup)
|
|
||||||
|
|
||||||
def kill_popups(self):
|
def kill_popups(self):
|
||||||
map(lambda x: x.window.destroy(), self.popups)
|
map(lambda x: x.window.destroy(), self.popups)
|
||||||
|
@ -410,7 +440,6 @@ class Stat_Window:
|
||||||
|
|
||||||
self.window.set_title("%s" % seat)
|
self.window.set_title("%s" % seat)
|
||||||
self.window.set_property("skip-taskbar-hint", True)
|
self.window.set_property("skip-taskbar-hint", True)
|
||||||
self.window.set_transient_for(parent.main_window)
|
|
||||||
self.window.set_focus_on_map(False)
|
self.window.set_focus_on_map(False)
|
||||||
|
|
||||||
grid = gtk.Table(rows = game.rows, columns = game.cols, homogeneous = False)
|
grid = gtk.Table(rows = game.rows, columns = game.cols, homogeneous = False)
|
||||||
|
@ -452,13 +481,35 @@ class Stat_Window:
|
||||||
|
|
||||||
e_box[r][c].add(self.label[r][c])
|
e_box[r][c].add(self.label[r][c])
|
||||||
e_box[r][c].connect("button_press_event", self.button_press_cb)
|
e_box[r][c].connect("button_press_event", self.button_press_cb)
|
||||||
|
e_box[r][c].connect("focus-in-event", self.noop)
|
||||||
|
e_box[r][c].connect("focus", self.noop)
|
||||||
|
e_box[r][c].connect("focus-out-event", self.noop)
|
||||||
label[r][c].modify_font(font)
|
label[r][c].modify_font(font)
|
||||||
|
|
||||||
self.window.set_opacity(parent.colors['hudopacity'])
|
self.window.set_opacity(parent.colors['hudopacity'])
|
||||||
|
self.window.connect("focus", self.noop)
|
||||||
|
self.window.connect("focus-in-event", self.noop)
|
||||||
|
self.window.connect("focus-out-event", self.noop)
|
||||||
|
self.window.connect("button_press_event", self.button_press_cb)
|
||||||
|
self.window.set_focus_on_map(False)
|
||||||
|
self.window.set_accept_focus(False)
|
||||||
|
|
||||||
|
|
||||||
self.window.move(self.x, self.y)
|
self.window.move(self.x, self.y)
|
||||||
|
self.window.realize() # window must be realized before it has a gdkwindow so we can attach it to the table window..
|
||||||
|
self.topify_window(self.window)
|
||||||
|
|
||||||
self.window.hide()
|
self.window.hide()
|
||||||
|
|
||||||
|
def topify_window(self, window):
|
||||||
|
window.set_focus_on_map(False)
|
||||||
|
window.set_accept_focus(False)
|
||||||
|
|
||||||
|
if not self.table.gdkhandle:
|
||||||
|
self.table.gdkhandle = gtk.gdk.window_foreign_new(int(self.table.number)) # gtk handle to poker window
|
||||||
|
# window.window.reparent(self.table.gdkhandle, 0, 0)
|
||||||
|
window.window.set_transient_for(self.table.gdkhandle)
|
||||||
|
# window.present()
|
||||||
|
|
||||||
def destroy(*args): # call back for terminating the main eventloop
|
def destroy(*args): # call back for terminating the main eventloop
|
||||||
gtk.main_quit()
|
gtk.main_quit()
|
||||||
|
@ -475,6 +526,8 @@ class Popup_window:
|
||||||
self.window.set_gravity(gtk.gdk.GRAVITY_STATIC)
|
self.window.set_gravity(gtk.gdk.GRAVITY_STATIC)
|
||||||
self.window.set_title("popup")
|
self.window.set_title("popup")
|
||||||
self.window.set_property("skip-taskbar-hint", True)
|
self.window.set_property("skip-taskbar-hint", True)
|
||||||
|
self.window.set_focus_on_map(False)
|
||||||
|
self.window.set_accept_focus(False)
|
||||||
self.window.set_transient_for(parent.get_toplevel())
|
self.window.set_transient_for(parent.get_toplevel())
|
||||||
|
|
||||||
self.window.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
|
self.window.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
|
||||||
|
@ -540,9 +593,6 @@ class Popup_window:
|
||||||
|
|
||||||
self.window.set_transient_for(stat_window.window)
|
self.window.set_transient_for(stat_window.window)
|
||||||
|
|
||||||
if os.name == 'nt':
|
|
||||||
self.topify_window(self.window)
|
|
||||||
|
|
||||||
def button_press_cb(self, widget, event, *args):
|
def button_press_cb(self, widget, event, *args):
|
||||||
# This handles all callbacks from button presses on the event boxes in
|
# This handles all callbacks from button presses on the event boxes in
|
||||||
# the popup windows. There is a bit of an ugly kludge to separate single-
|
# the popup windows. There is a bit of an ugly kludge to separate single-
|
||||||
|
@ -555,7 +605,9 @@ class Popup_window:
|
||||||
|
|
||||||
if event.button == 3: # right button event
|
if event.button == 3: # right button event
|
||||||
self.stat_window.kill_popup(self)
|
self.stat_window.kill_popup(self)
|
||||||
|
return True
|
||||||
# self.window.destroy()
|
# self.window.destroy()
|
||||||
|
return False
|
||||||
|
|
||||||
def toggle_decorated(self, widget):
|
def toggle_decorated(self, widget):
|
||||||
top = widget.get_toplevel()
|
top = widget.get_toplevel()
|
||||||
|
@ -569,27 +621,15 @@ class Popup_window:
|
||||||
top.move(x, y)
|
top.move(x, y)
|
||||||
|
|
||||||
def topify_window(self, window):
|
def topify_window(self, window):
|
||||||
"""Set the specified gtk window to stayontop in MS Windows."""
|
window.set_focus_on_map(False)
|
||||||
|
window.set_accept_focus(False)
|
||||||
# def windowEnumerationHandler(hwnd, resultList):
|
|
||||||
# '''Callback for win32gui.EnumWindows() to generate list of window handles.'''
|
if not self.table.gdkhandle:
|
||||||
# resultList.append((hwnd, win32gui.GetWindowText(hwnd)))
|
self.table.gdkhandle = gtk.gdk.window_foreign_new(int(self.table.number)) # gtk handle to poker window
|
||||||
|
# window.window.reparent(self.table.gdkhandle, 0, 0)
|
||||||
# unique_name = 'unique name for finding this window'
|
window.window.set_transient_for(self.table.gdkhandle)
|
||||||
# real_name = window.get_title()
|
# window.present()
|
||||||
# window.set_title(unique_name)
|
|
||||||
# tl_windows = []
|
|
||||||
# win32gui.EnumWindows(windowEnumerationHandler, tl_windows)
|
|
||||||
|
|
||||||
# for w in tl_windows:
|
|
||||||
# if w[1] == unique_name:
|
|
||||||
window.set_transient_for(self.parent.main_window)
|
|
||||||
style = win32gui.GetWindowLong(self.table.number, win32con.GWL_EXSTYLE)
|
|
||||||
style |= win32con.WS_CLIPCHILDREN
|
|
||||||
win32gui.SetWindowLong(self.table.number, win32con.GWL_EXSTYLE, style)
|
|
||||||
# break
|
|
||||||
|
|
||||||
# window.set_title(real_name)
|
|
||||||
|
|
||||||
if __name__== "__main__":
|
if __name__== "__main__":
|
||||||
main_window = gtk.Window()
|
main_window = gtk.Window()
|
||||||
|
@ -604,11 +644,13 @@ if __name__== "__main__":
|
||||||
if t is None:
|
if t is None:
|
||||||
print "Table not found."
|
print "Table not found."
|
||||||
db = Database.Database(c, 'fpdb', 'holdem')
|
db = Database.Database(c, 'fpdb', 'holdem')
|
||||||
|
|
||||||
|
stat_dict = db.get_stats_from_hand(1)
|
||||||
|
|
||||||
# for t in tables:
|
# for t in tables:
|
||||||
win = Hud(t, 10, 'holdem', c, db)
|
win = Hud(None, t, 10, 'holdem', c, db) # parent, table, max, poker_game, config, db_connection
|
||||||
win.create(1, c)
|
win.create(1, c, stat_dict, None) # hand, config, stat_dict, cards):
|
||||||
# t.get_details()
|
# t.get_details()
|
||||||
win.update(8300, db, c)
|
win.update(8300, c) # self, hand, config):
|
||||||
|
|
||||||
gtk.main()
|
gtk.main()
|
||||||
|
|
|
@ -345,6 +345,22 @@ class Aux_Seats(Aux_Window):
|
||||||
def create_contents(self): pass
|
def create_contents(self): pass
|
||||||
def update_contents(self): pass
|
def update_contents(self): pass
|
||||||
|
|
||||||
|
def update_card_positions(self):
|
||||||
|
# self.adj does not exist until .create() has been run
|
||||||
|
try:
|
||||||
|
adj = self.adj
|
||||||
|
except AttributeError:
|
||||||
|
return
|
||||||
|
loc = self.config.get_aux_locations(self.params['name'], int(self.hud.max))
|
||||||
|
for i in (range(1, self.hud.max + 1) + ['common']):
|
||||||
|
if i == 'common':
|
||||||
|
(x, y) = self.params['layout'][self.hud.max].common
|
||||||
|
else:
|
||||||
|
(x, y) = loc[adj[i]]
|
||||||
|
self.positions[i] = self.card_positions(x, self.hud.table.x, y, self.hud.table.y)
|
||||||
|
self.m_windows[i].move(self.positions[i][0], self.positions[i][1])
|
||||||
|
|
||||||
|
|
||||||
def create(self):
|
def create(self):
|
||||||
self.adj = self.hud.adj_seats(0, self.config) # move adj_seats to aux and get rid of it in Hud.py
|
self.adj = self.hud.adj_seats(0, self.config) # move adj_seats to aux and get rid of it in Hud.py
|
||||||
loc = self.config.get_aux_locations(self.params['name'], int(self.hud.max))
|
loc = self.config.get_aux_locations(self.params['name'], int(self.hud.max))
|
||||||
|
@ -362,7 +378,7 @@ class Aux_Seats(Aux_Window):
|
||||||
self.m_windows[i].set_transient_for(self.hud.main_window)
|
self.m_windows[i].set_transient_for(self.hud.main_window)
|
||||||
self.m_windows[i].set_focus_on_map(False)
|
self.m_windows[i].set_focus_on_map(False)
|
||||||
self.m_windows[i].connect("configure_event", self.configure_event_cb, i)
|
self.m_windows[i].connect("configure_event", self.configure_event_cb, i)
|
||||||
self.positions[i] = (int(x) + self.hud.table.x, int(y) + self.hud.table.y)
|
self.positions[i] = self.card_positions(x, self.hud.table.x, y, self.hud.table.y)
|
||||||
self.m_windows[i].move(self.positions[i][0], self.positions[i][1])
|
self.m_windows[i].move(self.positions[i][0], self.positions[i][1])
|
||||||
if self.params.has_key('opacity'):
|
if self.params.has_key('opacity'):
|
||||||
self.m_windows[i].set_opacity(float(self.params['opacity']))
|
self.m_windows[i].set_opacity(float(self.params['opacity']))
|
||||||
|
@ -374,6 +390,13 @@ class Aux_Seats(Aux_Window):
|
||||||
if self.uses_timer:
|
if self.uses_timer:
|
||||||
self.m_windows[i].hide()
|
self.m_windows[i].hide()
|
||||||
|
|
||||||
|
|
||||||
|
def card_positions(self, x, table_x, y, table_y):
|
||||||
|
_x = int(x) + int(table_x)
|
||||||
|
_y = int(y) + int(table_y)
|
||||||
|
return (_x, _y)
|
||||||
|
|
||||||
|
|
||||||
def update_gui(self, new_hand_id):
|
def update_gui(self, new_hand_id):
|
||||||
"""Update the gui, LDO."""
|
"""Update the gui, LDO."""
|
||||||
for i in self.m_windows.keys():
|
for i in self.m_windows.keys():
|
||||||
|
|
404
pyfpdb/PartyPokerToFpdb.py
Normal file → Executable file
404
pyfpdb/PartyPokerToFpdb.py
Normal file → Executable file
|
@ -2,17 +2,17 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright 2009, Grigorij Indigirkin
|
# Copyright 2009, Grigorij Indigirkin
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# This program is free software; you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
# (at your option) any later version.
|
# (at your option) any later version.
|
||||||
#
|
#
|
||||||
# This program is distributed in the hope that it will be useful,
|
# This program is distributed in the hope that it will be useful,
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
# GNU General Public License for more details.
|
# GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program; if not, write to the Free Software
|
# along with this program; if not, write to the Free Software
|
||||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
@ -21,43 +21,48 @@
|
||||||
import sys
|
import sys
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
|
from Exceptions import FpdbParseError
|
||||||
from HandHistoryConverter import *
|
from HandHistoryConverter import *
|
||||||
|
|
||||||
# PartyPoker HH Format
|
# PartyPoker HH Format
|
||||||
|
|
||||||
|
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 ("%(DELIMETER)s\n%(HH)s\n%(DELIMETER)s") % \
|
||||||
|
{'DELIMETER': '#'*50, 'HH': hh}
|
||||||
|
|
||||||
class PartyPoker(HandHistoryConverter):
|
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)
|
|
||||||
def wrapHh(self, hh):
|
|
||||||
return ("\n\nHand history attached below:\n"
|
|
||||||
"%(DELIMETER)s\n%(HH)s\n%(DELIMETER)s") % \
|
|
||||||
{'DELIMETER': '#'*50, 'HH': hh}
|
|
||||||
|
|
||||||
############################################################
|
############################################################
|
||||||
# Class Variables
|
# 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': "\$", }
|
sym = {'USD': "\$", }
|
||||||
|
|
||||||
# Static regexes
|
# Static regexes
|
||||||
# $5 USD NL Texas Hold'em - Saturday, July 25, 07:53:52 EDT 2009
|
# $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
|
# 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("""
|
re_GameInfoRing = re.compile("""
|
||||||
(?P<CURRENCY>\$|)\s*(?P<RINGLIMIT>\d+)\s*(?:USD)?\s*
|
(?P<CURRENCY>\$|)\s*(?P<RINGLIMIT>[0-9,]+)\s*(?:USD)?\s*
|
||||||
(?P<LIMIT>(NL))\s+
|
(?P<LIMIT>(NL|PL|))\s+
|
||||||
(?P<GAME>(Texas\ Hold\'em))
|
(?P<GAME>(Texas\ Hold\'em|Omaha))
|
||||||
\s*\-\s*
|
\s*\-\s*
|
||||||
(?P<DATETIME>.+)
|
(?P<DATETIME>.+)
|
||||||
""", re.VERBOSE)
|
""", re.VERBOSE)
|
||||||
re_GameInfoTrny = re.compile("""
|
re_GameInfoTrny = re.compile("""
|
||||||
(?P<LIMIT>(NL))\s+
|
(?P<LIMIT>(NL|PL|))\s+
|
||||||
(?P<GAME>(Texas\ Hold\'em))\s+
|
(?P<GAME>(Texas\ Hold\'em|Omaha))\s+
|
||||||
(?P<BUYIN>\$?[.0-9]+)\s*(?P<BUYIN_CURRENCY>USD)?\s*Buy-in\s+
|
(?P<BUYIN>\$?[.0-9]+)\s*(?P<BUYIN_CURRENCY>USD)?\s*Buy-in\s+
|
||||||
Trny:\s?(?P<TOURNO>\d+)\s+
|
Trny:\s?(?P<TOURNO>\d+)\s+
|
||||||
Level:\s*(?P<LEVEL>\d+)\s+
|
Level:\s*(?P<LEVEL>\d+)\s+
|
||||||
|
@ -70,37 +75,13 @@ class PartyPoker(HandHistoryConverter):
|
||||||
(?P<DATETIME>.+)
|
(?P<DATETIME>.+)
|
||||||
""", re.VERBOSE)
|
""", re.VERBOSE)
|
||||||
re_Hid = re.compile("^Game \#(?P<HID>\d+) starts.")
|
re_Hid = re.compile("^Game \#(?P<HID>\d+) starts.")
|
||||||
#re_GameInfo = re.compile("""
|
|
||||||
#PartyPoker\sGame\s\#(?P<HID>[0-9]+):\s+
|
|
||||||
#(Tournament\s\# # open paren of tournament info
|
|
||||||
#(?P<TOURNO>\d+),\s
|
|
||||||
#(?P<BUYIN>[%(LS)s\+\d\.]+ # here's how I plan to use LS
|
|
||||||
#\s?(?P<TOUR_ISO>%(LEGAL_ISO)s)?
|
|
||||||
#)\s)? # close paren of tournament info
|
|
||||||
#(?P<MIXED>HORSE|8\-Game|HOSE)?\s?\(?
|
|
||||||
#(?P<GAME>Hold\'em|Razz|7\sCard\sStud|7\sCard\sStud\sHi/Lo|Omaha|Omaha\sHi/Lo|Badugi|Triple\sDraw\s2\-7\sLowball)\s
|
|
||||||
#(?P<LIMIT>No\sLimit|Limit|Pot\sLimit)\)?,?\s
|
|
||||||
#(-\sLevel\s(?P<LEVEL>[IVXLC]+)\s)?
|
|
||||||
#\(? # open paren of the stakes
|
|
||||||
#(?P<CURRENCY>%(LS)s|)?
|
|
||||||
#(?P<SB>[.0-9]+)/(%(LS)s)?
|
|
||||||
#(?P<BB>[.0-9]+)
|
|
||||||
#\s?(?P<ISO>%(LEGAL_ISO)s)?
|
|
||||||
#\)\s-\s # close paren of the stakes
|
|
||||||
#(?P<DATETIME>.*$)""" % substitutions,
|
|
||||||
#re.MULTILINE|re.VERBOSE)
|
|
||||||
|
|
||||||
re_PlayerInfo = re.compile("""
|
re_PlayerInfo = re.compile("""
|
||||||
Seat\s(?P<SEAT>\d+):\s
|
Seat\s(?P<SEAT>\d+):\s
|
||||||
(?P<PNAME>.*)\s
|
(?P<PNAME>.*)\s
|
||||||
\(\s*\$?(?P<CASH>[0-9,.]+)\s*(?:USD|)\s*\)
|
\(\s*\$?(?P<CASH>[0-9,.]+)\s*(?:USD|)\s*\)
|
||||||
""" ,
|
""" ,
|
||||||
re.VERBOSE)
|
re.VERBOSE)
|
||||||
#re_PlayerInfo = re.compile("""
|
|
||||||
#^Seat\s(?P<SEAT>[0-9]+):\s
|
|
||||||
#(?P<PNAME>.*)\s
|
|
||||||
#\((%(LS)s)?(?P<CASH>[.0-9]+)\sin\schips\)""" % substitutions,
|
|
||||||
#re.MULTILINE|re.VERBOSE)
|
|
||||||
|
|
||||||
re_HandInfo = re.compile("""
|
re_HandInfo = re.compile("""
|
||||||
^Table\s+
|
^Table\s+
|
||||||
|
@ -109,110 +90,96 @@ class PartyPoker(HandHistoryConverter):
|
||||||
(?:[^ ]+\s+\#(?P<MTTTABLE>\d+).+)? # table number for mtt
|
(?:[^ ]+\s+\#(?P<MTTTABLE>\d+).+)? # table number for mtt
|
||||||
\((?P<PLAY>Real|Play)\s+Money\)\s+ # FIXME: check if play money is correct
|
\((?P<PLAY>Real|Play)\s+Money\)\s+ # FIXME: check if play money is correct
|
||||||
Seat\s+(?P<BUTTON>\d+)\sis\sthe\sbutton
|
Seat\s+(?P<BUTTON>\d+)\sis\sthe\sbutton
|
||||||
""",
|
""",
|
||||||
re.MULTILINE|re.VERBOSE)
|
re.MULTILINE|re.VERBOSE)
|
||||||
#re_HandInfo = re.compile("""
|
|
||||||
#^Table\s\'(?P<TABLE>[-\ a-zA-Z\d]+)\'\s
|
|
||||||
#((?P<MAX>\d+)-max\s)?
|
|
||||||
#(?P<PLAY>\(Play\sMoney\)\s)?
|
|
||||||
#(Seat\s\#(?P<BUTTON>\d+)\sis\sthe\sbutton)?""",
|
|
||||||
#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_SplitHands = re.compile('\x00+')
|
||||||
re_TailSplitHands = re.compile('(\x00+)')
|
re_TailSplitHands = re.compile('(\x00+)')
|
||||||
lineSplitter = '\n'
|
lineSplitter = '\n'
|
||||||
re_Button = re.compile('Seat (?P<BUTTON>\d+) is the button', re.MULTILINE)
|
re_Button = re.compile('Seat (?P<BUTTON>\d+) is the button', re.MULTILINE)
|
||||||
re_Board = re.compile(r"\[(?P<CARDS>.+)\]")
|
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(
|
||||||
# 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]+)')
|
'^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):
|
def allHandsAsList(self):
|
||||||
list = HandHistoryConverter.allHandsAsList(self)
|
list = HandHistoryConverter.allHandsAsList(self)
|
||||||
|
if list is None:
|
||||||
|
return []
|
||||||
return filter(lambda text: len(text.strip()), list)
|
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):
|
def compilePlayerRegexs(self, hand):
|
||||||
players = set([player[1] for player in hand.players])
|
players = set([player[1] for player in hand.players])
|
||||||
if not players <= self.compiledPlayers: # x <= y means 'x is subset of y'
|
if not players <= self.compiledPlayers: # x <= y means 'x is subset of y'
|
||||||
# we need to recompile the player regexs.
|
|
||||||
# TODO: should probably rename re_HeroCards and corresponding method,
|
|
||||||
# since they are used to find all cards on lines starting with "Dealt to:"
|
|
||||||
# They still identify the hero.
|
|
||||||
self.compiledPlayers = players
|
self.compiledPlayers = players
|
||||||
player_re = "(?P<PNAME>" + "|".join(map(re.escape, players)) + ")"
|
player_re = "(?P<PNAME>" + "|".join(map(re.escape, players)) + ")"
|
||||||
subst = {'PLYR': player_re, 'CUR_SYM': hand.SYMBOL[hand.gametype['currency']],
|
subst = {'PLYR': player_re, 'CUR_SYM': hand.SYMBOL[hand.gametype['currency']],
|
||||||
'CUR': hand.gametype['currency'] if hand.gametype['currency']!='T$' else ''}
|
'CUR': hand.gametype['currency'] if hand.gametype['currency']!='T$' else ''}
|
||||||
logging.debug("player_re: " + subst['PLYR'])
|
for key in ('CUR_SYM', 'CUR'):
|
||||||
logging.debug("CUR_SYM: " + subst['CUR_SYM'])
|
subst[key] = re.escape(subst[key])
|
||||||
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(
|
self.re_PostSB = re.compile(
|
||||||
r"^%(PLYR)s posts small blind \[%(CUR_SYM)s(?P<SB>[.0-9]+) ?%(CUR)s\]\." % subst,
|
r"^%(PLYR)s posts small blind \[%(CUR_SYM)s(?P<SB>[.0-9]+) ?%(CUR)s\]\." % subst,
|
||||||
re.MULTILINE)
|
re.MULTILINE)
|
||||||
self.re_PostBB = re.compile(
|
self.re_PostBB = re.compile(
|
||||||
r"^%(PLYR)s posts big blind \[%(CUR_SYM)s(?P<BB>[.0-9]+) ?%(CUR)s\]\." % subst,
|
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)
|
re.MULTILINE)
|
||||||
self.re_Antes = re.compile(
|
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)
|
re.MULTILINE)
|
||||||
#self.re_BringIn = re.compile(
|
|
||||||
#r"^%(PLYR)s: brings[- ]in( low|) for %(CUR)s(?P<BRINGIN>[.0-9]+)" % subst,
|
|
||||||
#re.MULTILINE)
|
|
||||||
#self.re_PostBoth = re.compile(
|
|
||||||
#r"^%(PLYR)s: posts small \& big blinds \[%(CUR)s (?P<SBBB>[.0-9]+)" % subst,
|
|
||||||
#re.MULTILINE)
|
|
||||||
self.re_HeroCards = re.compile(
|
self.re_HeroCards = re.compile(
|
||||||
r"^Dealt to %(PLYR)s \[\s*(?P<NEWCARDS>.+)\s*\]" % subst,
|
r"^Dealt to %(PLYR)s \[\s*(?P<NEWCARDS>.+)\s*\]" % subst,
|
||||||
re.MULTILINE)
|
re.MULTILINE)
|
||||||
self.re_Action = re.compile(r"""
|
self.re_Action = re.compile(r"""
|
||||||
^%(PLYR)s\s+(?P<ATYPE>bets|checks|raises|calls|folds|is\sall-In)
|
^%(PLYR)s\s+(?P<ATYPE>bets|checks|raises|calls|folds|is\sall-In)
|
||||||
(?:\s+\[%(CUR_SYM)s(?P<BET>[.,\d]+)\s*%(CUR)s\])?
|
(?:\s+\[%(CUR_SYM)s(?P<BET>[.,\d]+)\s*%(CUR)s\])?
|
||||||
""" % subst,
|
""" % subst,
|
||||||
re.MULTILINE|re.VERBOSE)
|
re.MULTILINE|re.VERBOSE)
|
||||||
self.re_ShownCards = re.compile(
|
self.re_ShownCards = re.compile(
|
||||||
r"^%s (?P<SHOWED>(?:doesn\'t )?shows?) " % player_re +
|
r"^%s (?P<SHOWED>(?:doesn\'t )?shows?) " % player_re +
|
||||||
r"\[ *(?P<CARDS>.+) *\](?P<COMBINATION>.+)\.",
|
r"\[ *(?P<CARDS>.+) *\](?P<COMBINATION>.+)\.",
|
||||||
re.MULTILINE)
|
re.MULTILINE)
|
||||||
self.re_CollectPot = re.compile(
|
self.re_CollectPot = re.compile(
|
||||||
r""""^%(PLYR)s \s+ wins \s+
|
r"""^%(PLYR)s \s+ wins \s+
|
||||||
%(CUR_SYM)s(?P<POT>[.\d]+)\s*%(CUR)s""" % subst,
|
%(CUR_SYM)s(?P<POT>[.,\d]+)\s*%(CUR)s""" % subst,
|
||||||
re.MULTILINE|re.VERBOSE)
|
re.MULTILINE|re.VERBOSE)
|
||||||
#self.re_sitsOut = re.compile("^%s sits out" % player_re, re.MULTILINE)
|
|
||||||
#self.re_ShownCards = re.compile(
|
|
||||||
#"^Seat (?P<SEAT>[0-9]+): %s (\(.*\) )?
|
|
||||||
#(?P<SHOWED>showed|mucked) \[(?P<CARDS>.*)\].*" % player_re,
|
|
||||||
#re.MULTILINE)
|
|
||||||
|
|
||||||
def readSupportedGames(self):
|
def readSupportedGames(self):
|
||||||
return [["ring", "hold", "nl"],
|
return [["ring", "hold", "nl"],
|
||||||
#["ring", "hold", "pl"],
|
["ring", "hold", "pl"],
|
||||||
#["ring", "hold", "fl"],
|
["ring", "hold", "fl"],
|
||||||
|
|
||||||
["tour", "hold", "nl"],
|
["tour", "hold", "nl"],
|
||||||
#["tour", "hold", "pl"],
|
["tour", "hold", "pl"],
|
||||||
#["tour", "hold", "fl"],
|
["tour", "hold", "fl"],
|
||||||
]
|
]
|
||||||
|
|
||||||
def _getGameType(self, handText):
|
def _getGameType(self, handText):
|
||||||
|
if not hasattr(self, '_gameType'):
|
||||||
|
self._gameType = None
|
||||||
if self._gameType is None:
|
if self._gameType is None:
|
||||||
# let's determine whether hand is trny
|
# let's determine whether hand is trny
|
||||||
# and whether 5-th line contains head line
|
# and whether 5-th line contains head line
|
||||||
headLine = handText.split(self.lineSplitter)[4]
|
headLine = handText.split(self.lineSplitter)[4]
|
||||||
#print headLine
|
|
||||||
#sys.exit(1)
|
|
||||||
for headLineContainer in headLine, handText:
|
for headLineContainer in headLine, handText:
|
||||||
for regexp in self.re_GameInfoTrny, self.re_GameInfoRing:
|
for regexp in self.re_GameInfoTrny, self.re_GameInfoRing:
|
||||||
m = regexp.search(headLineContainer)
|
m = regexp.search(headLineContainer)
|
||||||
|
@ -220,47 +187,45 @@ follow : whether to tail -f the input"""
|
||||||
self._gameType = m
|
self._gameType = m
|
||||||
return self._gameType
|
return self._gameType
|
||||||
return self._gameType
|
return self._gameType
|
||||||
|
|
||||||
def determineGameType(self, handText):
|
|
||||||
# inspect the handText and return the gametype dict
|
|
||||||
# gametype dict is:
|
|
||||||
# {'limitType': xxx, 'base': xxx, 'category': xxx}
|
|
||||||
|
|
||||||
print self.ParsingException().wrapHh( handText )
|
def determineGameType(self, handText):
|
||||||
|
"""inspect the handText and return the gametype dict
|
||||||
|
|
||||||
|
gametype dict is:
|
||||||
|
{'limitType': xxx, 'base': xxx, 'category': xxx}"""
|
||||||
|
|
||||||
|
log.debug(PartyPokerParseError().wrapHh( handText ))
|
||||||
|
|
||||||
info = {}
|
info = {}
|
||||||
m = self._getGameType(handText)
|
m = self._getGameType(handText)
|
||||||
if m is None:
|
if m is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
mg = m.groupdict()
|
mg = m.groupdict()
|
||||||
# translations from captured groups to fpdb info strings
|
# translations from captured groups to fpdb info strings
|
||||||
limits = { 'NL':'nl',
|
limits = { 'NL':'nl', 'PL':'pl', '':'fl' }
|
||||||
# 'Pot Limit':'pl', 'Limit':'fl'
|
|
||||||
}
|
|
||||||
games = { # base, category
|
games = { # base, category
|
||||||
"Texas Hold'em" : ('hold','holdem'),
|
"Texas Hold'em" : ('hold','holdem'),
|
||||||
#'Omaha' : ('hold','omahahi'),
|
'Omaha' : ('hold','omahahi'),
|
||||||
}
|
}
|
||||||
currencies = { '$':'USD', '':'T$' }
|
currencies = { '$':'USD', '':'T$' }
|
||||||
|
|
||||||
for expectedField in ['LIMIT', 'GAME']:
|
for expectedField in ['LIMIT', 'GAME']:
|
||||||
if mg[expectedField] is None:
|
if mg[expectedField] is None:
|
||||||
raise self.ParsingException(
|
raise PartyPokerParseError(
|
||||||
"Cannot fetch field '%s'" % expectedField,
|
"Cannot fetch field '%s'" % expectedField,
|
||||||
hh = handText)
|
hh = handText)
|
||||||
try:
|
try:
|
||||||
info['limitType'] = limits[mg['LIMIT']]
|
info['limitType'] = limits[mg['LIMIT'].strip()]
|
||||||
except:
|
except:
|
||||||
raise self.ParsingException(
|
raise PartyPokerParseError(
|
||||||
"Unknown limit '%s'" % mg['LIMIT'],
|
"Unknown limit '%s'" % mg['LIMIT'],
|
||||||
hh = handText)
|
hh = handText)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
(info['base'], info['category']) = games[mg['GAME']]
|
(info['base'], info['category']) = games[mg['GAME']]
|
||||||
except:
|
except:
|
||||||
raise self.ParsingException(
|
raise PartyPokerParseError(
|
||||||
"Unknown game type '%s'" % mg['GAME'],
|
"Unknown game type '%s'" % mg['GAME'],
|
||||||
hh = handText)
|
hh = handText)
|
||||||
|
|
||||||
|
@ -269,16 +234,16 @@ follow : whether to tail -f the input"""
|
||||||
info['type'] = 'tour'
|
info['type'] = 'tour'
|
||||||
else:
|
else:
|
||||||
info['type'] = 'ring'
|
info['type'] = 'ring'
|
||||||
|
|
||||||
if info['type'] == 'ring':
|
if info['type'] == 'ring':
|
||||||
info['sb'], info['bb'] = ringBlinds(mg['RINGLIMIT'])
|
info['sb'], info['bb'] = ringBlinds(mg['RINGLIMIT'])
|
||||||
# FIXME: there are only $ and play money availible for cash
|
# FIXME: there are only $ and play money availible for cash
|
||||||
info['currency'] = currencies(mg['CURRENCY'])
|
# to be honest, party doesn't save play money hh
|
||||||
|
info['currency'] = currencies[mg['CURRENCY']]
|
||||||
else:
|
else:
|
||||||
info['sb'] = renderTrnyMoney(mg['SB'])
|
info['sb'] = clearMoneyString(mg['SB'])
|
||||||
info['bb'] = renderTrnyMoney(mg['BB'])
|
info['bb'] = clearMoneyString(mg['BB'])
|
||||||
info['currency'] = 'T$'
|
info['currency'] = 'T$'
|
||||||
|
|
||||||
|
|
||||||
# NB: SB, BB must be interpreted as blinds or bets depending on limit type.
|
# NB: SB, BB must be interpreted as blinds or bets depending on limit type.
|
||||||
return info
|
return info
|
||||||
|
@ -286,22 +251,48 @@ follow : whether to tail -f the input"""
|
||||||
|
|
||||||
def readHandInfo(self, hand):
|
def readHandInfo(self, hand):
|
||||||
info = {}
|
info = {}
|
||||||
m = self.re_HandInfo.search(hand.handText,re.DOTALL)
|
try:
|
||||||
if m:
|
info.update(self.re_Hid.search(hand.handText).groupdict())
|
||||||
info.update(m.groupdict())
|
except:
|
||||||
else:
|
raise PartyPokerParseError("Cannot read HID for current hand", hh=hand.handText)
|
||||||
raise self.ParsingException("Cannot read Handinfo for current hand", hh=hand.handText)
|
|
||||||
m = self._getGameType(hand.handText)
|
try:
|
||||||
if m: info.update(m.groupdict())
|
info.update(self.re_HandInfo.search(hand.handText,re.DOTALL).groupdict())
|
||||||
m = self.re_Hid.search(hand.handText)
|
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())
|
if m: info.update(m.groupdict())
|
||||||
|
|
||||||
# FIXME: it's a hack cause party doesn't supply hand.maxseats info
|
|
||||||
#hand.maxseats = '9'
|
# FIXME: it's dirty hack
|
||||||
hand.mixed = None
|
# party doesnt subtract uncalled money from commited money
|
||||||
|
# so hand.totalPot calculation has to be redefined
|
||||||
# TODO : I rather like the idea of just having this dict as hand.info
|
from Hand import Pot, HoldemOmahaHand
|
||||||
logging.debug("readHandInfo: %s" % info)
|
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:
|
for key in info:
|
||||||
if key == 'DATETIME':
|
if key == 'DATETIME':
|
||||||
#Saturday, July 25, 07:53:52 EDT 2009
|
#Saturday, July 25, 07:53:52 EDT 2009
|
||||||
|
@ -313,9 +304,10 @@ follow : whether to tail -f the input"""
|
||||||
month = months.index(m2.group('M')) + 1
|
month = months.index(m2.group('M')) + 1
|
||||||
datetimestr = "%s/%s/%s %s:%s:%s" % (m2.group('Y'), month,m2.group('D'),m2.group('H'),m2.group('MIN'),m2.group('S'))
|
datetimestr = "%s/%s/%s %s:%s:%s" % (m2.group('Y'), month,m2.group('D'),m2.group('H'),m2.group('MIN'),m2.group('S'))
|
||||||
hand.starttime = datetime.datetime.strptime(datetimestr, "%Y/%m/%d %H:%M:%S")
|
hand.starttime = datetime.datetime.strptime(datetimestr, "%Y/%m/%d %H:%M:%S")
|
||||||
|
# FIXME: some timezone correction required
|
||||||
#tzShift = defaultdict(lambda:0, {'EDT': -5, 'EST': -6, 'MSKS': 3})
|
#tzShift = defaultdict(lambda:0, {'EDT': -5, 'EST': -6, 'MSKS': 3})
|
||||||
#hand.starttime -= datetime.timedelta(hours=tzShift[m2.group('TZ')])
|
#hand.starttime -= datetime.timedelta(hours=tzShift[m2.group('TZ')])
|
||||||
|
|
||||||
if key == 'HID':
|
if key == 'HID':
|
||||||
hand.handid = info[key]
|
hand.handid = info[key]
|
||||||
if key == 'TABLE':
|
if key == 'TABLE':
|
||||||
|
@ -325,14 +317,14 @@ follow : whether to tail -f the input"""
|
||||||
if key == 'TOURNO':
|
if key == 'TOURNO':
|
||||||
hand.tourNo = info[key]
|
hand.tourNo = info[key]
|
||||||
if key == 'BUYIN':
|
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 ''
|
cur = info[key][0] if info[key][0] not in '0123456789' else ''
|
||||||
hand.buyin = info[key] + '+%s0' % cur
|
hand.buyin = info[key] + '+%s0' % cur
|
||||||
if key == 'LEVEL':
|
if key == 'LEVEL':
|
||||||
hand.level = info[key]
|
hand.level = info[key]
|
||||||
if key == 'PLAY' and info['PLAY'] != 'Real':
|
if key == 'PLAY' and info['PLAY'] != 'Real':
|
||||||
# TODO: play money wasn't tested
|
# if realy there's no play money hh on party
|
||||||
# hand.currency = 'play' # overrides previously set value
|
|
||||||
hand.gametype['currency'] = 'play'
|
hand.gametype['currency'] = 'play'
|
||||||
|
|
||||||
def readButton(self, hand):
|
def readButton(self, hand):
|
||||||
|
@ -340,21 +332,17 @@ follow : whether to tail -f the input"""
|
||||||
if m:
|
if m:
|
||||||
hand.buttonpos = int(m.group('BUTTON'))
|
hand.buttonpos = int(m.group('BUTTON'))
|
||||||
else:
|
else:
|
||||||
logging.info('readButton: not found')
|
log.info('readButton: not found')
|
||||||
|
|
||||||
def readPlayerStacks(self, hand):
|
def readPlayerStacks(self, hand):
|
||||||
logging.debug("readPlayerStacks")
|
log.debug("readPlayerStacks")
|
||||||
m = self.re_PlayerInfo.finditer(hand.handText)
|
m = self.re_PlayerInfo.finditer(hand.handText)
|
||||||
players = []
|
players = []
|
||||||
for a in m:
|
for a in m:
|
||||||
hand.addPlayer(int(a.group('SEAT')), a.group('PNAME'),
|
hand.addPlayer(int(a.group('SEAT')), a.group('PNAME'),
|
||||||
renderTrnyMoney(a.group('CASH')))
|
clearMoneyString(a.group('CASH')))
|
||||||
|
|
||||||
def markStreets(self, hand):
|
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(
|
m = re.search(
|
||||||
r"\*{2} Dealing down cards \*{2}"
|
r"\*{2} Dealing down cards \*{2}"
|
||||||
r"(?P<PREFLOP>.+?)"
|
r"(?P<PREFLOP>.+?)"
|
||||||
|
@ -364,24 +352,17 @@ follow : whether to tail -f the input"""
|
||||||
, hand.handText,re.DOTALL)
|
, hand.handText,re.DOTALL)
|
||||||
hand.addStreets(m)
|
hand.addStreets(m)
|
||||||
|
|
||||||
def readCommunityCards(self, hand, street):
|
def readCommunityCards(self, hand, street):
|
||||||
if street in ('FLOP','TURN','RIVER'):
|
if street in ('FLOP','TURN','RIVER'):
|
||||||
m = self.re_Board.search(hand.streets[street])
|
m = self.re_Board.search(hand.streets[street])
|
||||||
hand.setCommunityCards(street, renderCards(m.group('CARDS')))
|
hand.setCommunityCards(street, renderCards(m.group('CARDS')))
|
||||||
|
|
||||||
def readAntes(self, hand):
|
def readAntes(self, hand):
|
||||||
logging.debug("reading antes")
|
log.debug("reading antes")
|
||||||
m = self.re_Antes.finditer(hand.handText)
|
m = self.re_Antes.finditer(hand.handText)
|
||||||
for player in m:
|
for player in m:
|
||||||
#~ logging.debug("hand.addAnte(%s,%s)" %(player.group('PNAME'), player.group('ANTE')))
|
|
||||||
hand.addAnte(player.group('PNAME'), player.group('ANTE'))
|
hand.addAnte(player.group('PNAME'), player.group('ANTE'))
|
||||||
|
|
||||||
def readBringIn(self, hand):
|
|
||||||
m = self.re_BringIn.search(hand.handText,re.DOTALL)
|
|
||||||
if m:
|
|
||||||
#~ logging.debug("readBringIn: %s for %s" %(m.group('PNAME'), m.group('BRINGIN')))
|
|
||||||
hand.addBringIn(m.group('PNAME'), m.group('BRINGIN'))
|
|
||||||
|
|
||||||
def readBlinds(self, hand):
|
def readBlinds(self, hand):
|
||||||
noSmallBlind = bool(self.re_NoSmallBlind.search(hand.handText))
|
noSmallBlind = bool(self.re_NoSmallBlind.search(hand.handText))
|
||||||
if hand.gametype['type'] == 'ring':
|
if hand.gametype['type'] == 'ring':
|
||||||
|
@ -391,50 +372,51 @@ follow : whether to tail -f the input"""
|
||||||
hand.addBlind(m.group('PNAME'), 'small blind', m.group('SB'))
|
hand.addBlind(m.group('PNAME'), 'small blind', m.group('SB'))
|
||||||
except: # no small blind
|
except: # no small blind
|
||||||
hand.addBlind(None, None, None)
|
hand.addBlind(None, None, None)
|
||||||
|
|
||||||
for a in self.re_PostBB.finditer(hand.handText):
|
for a in self.re_PostBB.finditer(hand.handText):
|
||||||
hand.addBlind(a.group('PNAME'), 'big blind', a.group('BB'))
|
hand.addBlind(a.group('PNAME'), 'big blind', a.group('BB'))
|
||||||
else:
|
|
||||||
|
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
|
# party doesn't track blinds for tournaments
|
||||||
# so there're some cra^Wcaclulations
|
# so there're some cra^Wcaclulations
|
||||||
if hand.buttonpos == 0:
|
if hand.buttonpos == 0:
|
||||||
self.readButton(hand)
|
self.readButton(hand)
|
||||||
# NOTE: code below depends on Hand's implementation
|
# NOTE: code below depends on Hand's implementation
|
||||||
# playersMap - dict {seat: (pname,stack)}
|
# playersMap - dict {seat: (pname,stack)}
|
||||||
playersMap = dict([(f[0], f[1:3]) for f in hand.players])
|
playersMap = dict([(f[0], f[1:3]) for f in hand.players])
|
||||||
maxSeat = max(playersMap)
|
maxSeat = max(playersMap)
|
||||||
|
|
||||||
def findFirstNonEmptySeat(startSeat):
|
def findFirstNonEmptySeat(startSeat):
|
||||||
while startSeat not in playersMap:
|
while startSeat not in playersMap:
|
||||||
if startSeat >= maxSeat:
|
if startSeat >= maxSeat:
|
||||||
startSeat = 0
|
startSeat = 0
|
||||||
startSeat += 1
|
startSeat += 1
|
||||||
return startSeat
|
return startSeat
|
||||||
smartMin = lambda A,B: A if float(A) <= float(B) else B
|
smartMin = lambda A,B: A if float(A) <= float(B) else B
|
||||||
|
|
||||||
if noSmallBlind:
|
if noSmallBlind:
|
||||||
hand.addBlind(None, None, None)
|
hand.addBlind(None, None, None)
|
||||||
|
smallBlindSeat = int(hand.buttonpos)
|
||||||
else:
|
else:
|
||||||
smallBlindSeat = findFirstNonEmptySeat(int(hand.buttonpos) + 1)
|
smallBlindSeat = findFirstNonEmptySeat(int(hand.buttonpos) + 1)
|
||||||
blind = smartMin(hand.sb, playersMap[smallBlindSeat][1])
|
blind = smartMin(hand.sb, playersMap[smallBlindSeat][1])
|
||||||
hand.addBlind(playersMap[smallBlindSeat][0], 'small blind', blind)
|
hand.addBlind(playersMap[smallBlindSeat][0], 'small blind', blind)
|
||||||
|
|
||||||
bigBlindSeat = findFirstNonEmptySeat(smallBlindSeat + 1)
|
bigBlindSeat = findFirstNonEmptySeat(smallBlindSeat + 1)
|
||||||
blind = smartMin(hand.bb, playersMap[bigBlindSeat][1])
|
blind = smartMin(hand.bb, playersMap[bigBlindSeat][1])
|
||||||
hand.addBlind(playersMap[bigBlindSeat][0], 'small blind', blind)
|
hand.addBlind(playersMap[bigBlindSeat][0], 'big blind', blind)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def readHeroCards(self, hand):
|
def readHeroCards(self, hand):
|
||||||
# streets PREFLOP, PREDRAW, and THIRD are special cases beacause
|
# we need to grab hero's cards
|
||||||
# we need to grab hero's cards
|
|
||||||
for street in ('PREFLOP',):
|
for street in ('PREFLOP',):
|
||||||
if street in hand.streets.keys():
|
if street in hand.streets.keys():
|
||||||
m = self.re_HeroCards.finditer(hand.streets[street])
|
m = self.re_HeroCards.finditer(hand.streets[street])
|
||||||
for found in m:
|
for found in m:
|
||||||
# if m == None:
|
|
||||||
# hand.involved = False
|
|
||||||
# else:
|
|
||||||
hand.hero = found.group('PNAME')
|
hand.hero = found.group('PNAME')
|
||||||
newcards = renderCards(found.group('NEWCARDS'))
|
newcards = renderCards(found.group('NEWCARDS'))
|
||||||
hand.addHoleCards(street, hand.hero, closed=newcards, shown=False, mucked=False, dealt=True)
|
hand.addHoleCards(street, hand.hero, closed=newcards, shown=False, mucked=False, dealt=True)
|
||||||
|
@ -444,35 +426,48 @@ follow : whether to tail -f the input"""
|
||||||
m = self.re_Action.finditer(hand.streets[street])
|
m = self.re_Action.finditer(hand.streets[street])
|
||||||
for action in m:
|
for action in m:
|
||||||
acts = action.groupdict()
|
acts = action.groupdict()
|
||||||
if action.group('ATYPE') in ('raises','is all-In'):
|
playerName = action.group('PNAME')
|
||||||
#print action.groupdict()
|
amount = clearMoneyString(action.group('BET')) if action.group('BET') else None
|
||||||
#sys.exit(1)
|
actionType = action.group('ATYPE')
|
||||||
hand.addRaiseBy( street, action.group('PNAME'), action.group('BET') )
|
|
||||||
elif action.group('ATYPE') == 'calls':
|
if actionType == 'is all-In':
|
||||||
hand.addCall( street, action.group('PNAME'), action.group('BET') )
|
# party's allin can mean either raise or bet or call
|
||||||
#print action.groupdict()
|
Bp = hand.lastBet[street]
|
||||||
#sys.exit(1)
|
if Bp == 0:
|
||||||
elif action.group('ATYPE') == 'bets':
|
actionType = 'bets'
|
||||||
hand.addBet( street, action.group('PNAME'), action.group('BET') )
|
elif Bp < Decimal(amount):
|
||||||
elif action.group('ATYPE') == 'folds':
|
actionType = 'raises'
|
||||||
hand.addFold( street, action.group('PNAME'))
|
else:
|
||||||
elif action.group('ATYPE') == 'checks':
|
actionType = 'calls'
|
||||||
hand.addCheck( street, action.group('PNAME'))
|
|
||||||
|
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:
|
else:
|
||||||
print "DEBUG: unimplemented readAction: '%s' '%s'" %(action.group('PNAME'),action.group('ATYPE'),)
|
raise PartyPokerParseError(
|
||||||
|
"Unimplemented readAction: '%s' '%s'" % (playerName,actionType,),
|
||||||
|
hid = hand.hid, hh = hand.handText )
|
||||||
|
|
||||||
|
|
||||||
def readShowdownActions(self, hand):
|
def readShowdownActions(self, hand):
|
||||||
# all action in readShownCards
|
# all action in readShownCards
|
||||||
pass
|
pass
|
||||||
## TODO: pick up mucks also??
|
|
||||||
#for shows in self.re_ShowdownAction.finditer(hand.handText):
|
|
||||||
#cards = shows.group('CARDS').split(' ')
|
|
||||||
#hand.addShownCards(cards, shows.group('PNAME'))
|
|
||||||
|
|
||||||
def readCollectPot(self,hand):
|
def readCollectPot(self,hand):
|
||||||
for m in self.re_CollectPot.finditer(hand.handText):
|
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):
|
def readShownCards(self,hand):
|
||||||
for m in self.re_ShownCards.finditer(hand.handText):
|
for m in self.re_ShownCards.finditer(hand.handText):
|
||||||
|
@ -484,26 +479,26 @@ follow : whether to tail -f the input"""
|
||||||
else: mucked = True
|
else: mucked = True
|
||||||
|
|
||||||
hand.addShownCards(cards=cards, player=m.group('PNAME'), shown=shown, mucked=mucked)
|
hand.addShownCards(cards=cards, player=m.group('PNAME'), shown=shown, mucked=mucked)
|
||||||
|
|
||||||
def ringBlinds(ringLimit):
|
def ringBlinds(ringLimit):
|
||||||
"Returns blinds for current limit"
|
"Returns blinds for current limit in cash games"
|
||||||
ringLimit = float(ringLimit)
|
ringLimit = float(clearMoneyString(ringLimit))
|
||||||
if ringLimit == 5.: ringLimit = 4.
|
if ringLimit == 5.: ringLimit = 4.
|
||||||
return ('%.2f' % (ringLimit/200.), '%.2f' % (ringLimit/100.) )
|
return ('%.2f' % (ringLimit/200.), '%.2f' % (ringLimit/100.) )
|
||||||
|
|
||||||
def renderTrnyMoney(money):
|
def clearMoneyString(money):
|
||||||
"renders 'numbers' like '1 200' and '2,000'"
|
"Renders 'numbers' like '1 200' and '2,000'"
|
||||||
return money.replace(' ', '').replace(',', '')
|
return money.replace(' ', '').replace(',', '')
|
||||||
|
|
||||||
def renderCards(string):
|
def renderCards(string):
|
||||||
"splits strings like ' Js, 4d '"
|
"Splits strings like ' Js, 4d '"
|
||||||
cards = string.strip().split(' ')
|
cards = string.strip().split(' ')
|
||||||
return filter(len, map(lambda x: x.strip(' ,'), cards))
|
return filter(len, map(lambda x: x.strip(' ,'), cards))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = OptionParser()
|
parser = OptionParser()
|
||||||
parser.add_option("-i", "--input", dest="ipath", help="parse input hand history", default="regression-test-files/stars/horse/HH20090226 Natalie V - $0.10-$0.20 - HORSE.txt")
|
parser.add_option("-i", "--input", dest="ipath", help="parse input hand history")
|
||||||
parser.add_option("-o", "--output", dest="opath", help="output translation to", default="-")
|
parser.add_option("-o", "--output", dest="opath", help="output translation to", default="-")
|
||||||
parser.add_option("-f", "--follow", dest="follow", help="follow (tail -f) the input", action="store_true", default=False)
|
parser.add_option("-f", "--follow", dest="follow", help="follow (tail -f) the input", action="store_true", default=False)
|
||||||
parser.add_option("-q", "--quiet",
|
parser.add_option("-q", "--quiet",
|
||||||
|
@ -515,7 +510,4 @@ if __name__ == "__main__":
|
||||||
|
|
||||||
(options, args) = parser.parse_args()
|
(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)
|
e = PartyPoker(in_path = options.ipath, out_path = options.opath, follow = options.follow)
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
########################################################################
|
########################################################################
|
||||||
|
|
||||||
# TODO: straighten out discards for draw games
|
# TODO: straighten out discards for draw games
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from HandHistoryConverter import *
|
from HandHistoryConverter import *
|
||||||
|
|
||||||
|
@ -26,13 +27,17 @@ from HandHistoryConverter import *
|
||||||
|
|
||||||
class PokerStars(HandHistoryConverter):
|
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
|
mixes = { 'HORSE': 'horse', '8-Game': '8game', 'HOSE': 'hose'} # Legal mixed games
|
||||||
sym = {'USD': "\$", 'CAD': "\$", 'T$': "", "EUR": "\x80", "GBP": "\xa3"} # ADD Euro, Sterling, etc HERE
|
sym = {'USD': "\$", 'CAD': "\$", 'T$': "", "EUR": "\x80", "GBP": "\xa3"} # ADD Euro, Sterling, etc HERE
|
||||||
substitutions = {
|
substitutions = {
|
||||||
'LEGAL_ISO' : "USD|EUR|GBP|CAD", # legal ISO currency codes
|
'LEGAL_ISO' : "USD|EUR|GBP|CAD|FPP", # legal ISO currency codes
|
||||||
'LS' : "\$|\x80|\xa3" # legal currency symbols ADD Euro, Sterling, etc HERE
|
'LS' : "\$|\x80|\xa3" # legal currency symbols 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]+)')
|
# 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):
|
def compilePlayerRegexs(self, hand):
|
||||||
players = set([player[1] for player in hand.players])
|
players = set([player[1] for player in hand.players])
|
||||||
if not players <= self.compiledPlayers: # x <= y means 'x is subset of y'
|
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()
|
(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)
|
e = PokerStars(in_path = options.ipath, out_path = options.opath, follow = options.follow)
|
||||||
|
|
5879
pyfpdb/SQL.py
5879
pyfpdb/SQL.py
File diff suppressed because it is too large
Load Diff
188
pyfpdb/SummaryEverleaf.py
Normal file
188
pyfpdb/SummaryEverleaf.py
Normal file
|
@ -0,0 +1,188 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
# Copyright (c) 2009 Eric Blade, and the FPDB team.
|
||||||
|
|
||||||
|
#This program is free software: you can redistribute it and/or modify
|
||||||
|
#it under the terms of the GNU Affero General Public License as published by
|
||||||
|
#the Free Software Foundation, version 3 of the License.
|
||||||
|
#
|
||||||
|
#This program is distributed in the hope that it will be useful,
|
||||||
|
#but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
#GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
#You should have received a copy of the GNU Affero General Public License
|
||||||
|
#along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#In the "official" distribution you can find the license in
|
||||||
|
#agpl-3.0.txt in the docs folder of the package.
|
||||||
|
|
||||||
|
import urllib, htmllib, formatter
|
||||||
|
|
||||||
|
class AppURLopener(urllib.FancyURLopener):
|
||||||
|
version = "Free Poker Database/0.12+"
|
||||||
|
|
||||||
|
urllib._urlopener = AppURLopener()
|
||||||
|
|
||||||
|
class SummaryParser(htmllib.HTMLParser): # derive new HTML parser
|
||||||
|
def get_attr(self, attrs, key):
|
||||||
|
#print attrs;
|
||||||
|
for a in attrs:
|
||||||
|
if a[0] == key:
|
||||||
|
# print key,"=",a[1]
|
||||||
|
return a[1]
|
||||||
|
return None
|
||||||
|
|
||||||
|
def __init__(self, formatter) : # class constructor
|
||||||
|
htmllib.HTMLParser.__init__(self, formatter) # base class constructor
|
||||||
|
self.nofill = True
|
||||||
|
self.SiteName = None
|
||||||
|
self.TourneyId = None
|
||||||
|
self.TourneyName = None
|
||||||
|
self.nextStartTime = False
|
||||||
|
self.TourneyStartTime = None
|
||||||
|
self.nextEndTime = False
|
||||||
|
self.TourneyEndTime = None
|
||||||
|
self.TourneyGameType = None
|
||||||
|
self.nextGameType = False
|
||||||
|
self.nextStructure = False
|
||||||
|
self.TourneyStructure = None
|
||||||
|
self.nextBuyIn = False
|
||||||
|
self.TourneyBuyIn = None
|
||||||
|
self.nextPool = False
|
||||||
|
self.TourneyPool = None
|
||||||
|
self.nextPlayers = False
|
||||||
|
self.TourneyPlayers = None
|
||||||
|
self.nextAllowRebuys = False
|
||||||
|
self.TourneyRebuys = None
|
||||||
|
self.parseResultsA = False
|
||||||
|
self.parseResultsB = False
|
||||||
|
self.TempResultStore = [0,0,0,0]
|
||||||
|
self.TempResultPos = 0
|
||||||
|
self.Results = {}
|
||||||
|
|
||||||
|
def start_meta(self, attrs):
|
||||||
|
x = self.get_attr(attrs, 'name')
|
||||||
|
if x == "author":
|
||||||
|
self.SiteName = self.get_attr(attrs, 'content')
|
||||||
|
|
||||||
|
def start_input(self, attrs):
|
||||||
|
x = self.get_attr(attrs, 'name')
|
||||||
|
#print "input name=",x
|
||||||
|
if x == "tid":
|
||||||
|
self.TourneyId = self.get_attr(attrs, 'value')
|
||||||
|
|
||||||
|
def start_h1(self, attrs):
|
||||||
|
if self.TourneyName is None:
|
||||||
|
self.save_bgn()
|
||||||
|
|
||||||
|
def end_h1(self):
|
||||||
|
if self.TourneyName is None:
|
||||||
|
self.TourneyName = self.save_end()
|
||||||
|
|
||||||
|
def start_div(self, attrs):
|
||||||
|
x = self.get_attr(attrs, 'id')
|
||||||
|
if x == "result":
|
||||||
|
self.parseResultsA = True
|
||||||
|
|
||||||
|
def end_div(self): # TODO: Can we get attrs in the END tag too? I don't know? Would be useful to make SURE we're closing the right div ..
|
||||||
|
if self.parseResultsA:
|
||||||
|
self.parseResultsA = False # TODO: Should probably just make sure everything is false at this point, since we're not going to be having anything in the middle of a DIV.. oh well
|
||||||
|
|
||||||
|
def start_td(self, attrs):
|
||||||
|
self.save_bgn()
|
||||||
|
|
||||||
|
def end_td(self):
|
||||||
|
x = self.save_end()
|
||||||
|
|
||||||
|
if not self.parseResultsA:
|
||||||
|
if not self.nextStartTime and x == "Start:":
|
||||||
|
self.nextStartTime = True
|
||||||
|
elif self.nextStartTime:
|
||||||
|
self.TourneyStartTime = x
|
||||||
|
self.nextStartTime = False
|
||||||
|
|
||||||
|
if not self.nextEndTime and x == "Finished:":
|
||||||
|
self.nextEndTime = True
|
||||||
|
elif self.nextEndTime:
|
||||||
|
self.TourneyEndTime = x
|
||||||
|
self.nextEndTime = False
|
||||||
|
|
||||||
|
if not self.nextGameType and x == "Game Type:":
|
||||||
|
self.nextGameType = True
|
||||||
|
elif self.nextGameType:
|
||||||
|
self.TourneyGameType = x
|
||||||
|
self.nextGameType = False
|
||||||
|
|
||||||
|
if not self.nextStructure and x == "Limit:":
|
||||||
|
self.nextStructure = True
|
||||||
|
elif self.nextStructure:
|
||||||
|
self.TourneyStructure = x
|
||||||
|
self.nextStructure = False
|
||||||
|
|
||||||
|
if not self.nextBuyIn and x == "Buy In / Fee:":
|
||||||
|
self.nextBuyIn = True
|
||||||
|
elif self.nextBuyIn:
|
||||||
|
self.TourneyBuyIn = x # TODO: Further parse the fee from this
|
||||||
|
self.nextBuyIn = False
|
||||||
|
|
||||||
|
if not self.nextPool and x == "Prize Money:":
|
||||||
|
self.nextPool = True
|
||||||
|
elif self.nextPool:
|
||||||
|
self.TourneyPool = x
|
||||||
|
self.nextPool = False
|
||||||
|
|
||||||
|
if not self.nextPlayers and x == "Player Count:":
|
||||||
|
self.nextPlayers = True
|
||||||
|
elif self.nextPlayers:
|
||||||
|
self.TourneyPlayers = x
|
||||||
|
self.nextPlayers = False
|
||||||
|
|
||||||
|
if not self.nextAllowRebuys and x == "Rebuys possible?:":
|
||||||
|
self.nextAllowRebuys = True
|
||||||
|
elif self.nextAllowRebuys:
|
||||||
|
self.TourneyRebuys = x
|
||||||
|
self.nextAllowRebuys = False
|
||||||
|
|
||||||
|
else: # parse results
|
||||||
|
if x == "Won Prize":
|
||||||
|
self.parseResultsB = True # ok, NOW we can start parsing the results
|
||||||
|
elif self.parseResultsB:
|
||||||
|
if x[0] == "$": # first char of the last of each row is the dollar sign, so now we can put it into a sane order
|
||||||
|
self.TempResultPos = 0
|
||||||
|
name = self.TempResultStore[1]
|
||||||
|
place = self.TempResultStore[0]
|
||||||
|
time = self.TempResultStore[2]
|
||||||
|
# print self.TempResultStore
|
||||||
|
|
||||||
|
self.Results[name] = {}
|
||||||
|
self.Results[name]['place'] = place
|
||||||
|
self.Results[name]['winamount'] = x
|
||||||
|
self.Results[name]['outtime'] = time
|
||||||
|
|
||||||
|
# self.Results[self.TempResultStore[1]] = {}
|
||||||
|
# self.Results[self.TempResultStore[1]]['place'] = self.TempResultStore[self.TempResultStore[0]]
|
||||||
|
# self.Results[self.TempResultStore[1]]['winamount'] = x
|
||||||
|
# self.Results[self.TempResultStore[1]]['outtime'] = self.TempResultStore[self.TempResultStore[2]]
|
||||||
|
else:
|
||||||
|
self.TempResultStore[self.TempResultPos] = x
|
||||||
|
self.TempResultPos += 1
|
||||||
|
|
||||||
|
class EverleafSummary:
|
||||||
|
def __init__(self):
|
||||||
|
if __name__ != "__main__":
|
||||||
|
self.main()
|
||||||
|
|
||||||
|
def main(self, id="785646"):
|
||||||
|
file = urllib.urlopen("http://www.poker4ever.com/en.tournaments.tournament-statistics?tid="+id)
|
||||||
|
self.parser = SummaryParser(formatter.NullFormatter())
|
||||||
|
self.parser.feed(file.read())
|
||||||
|
print "site=",self.parser.SiteName, "tourneyname=", self.parser.TourneyName, "tourneyid=", self.parser.TourneyId
|
||||||
|
print "start time=",self.parser.TourneyStartTime, "end time=",self.parser.TourneyEndTime
|
||||||
|
print "structure=", self.parser.TourneyStructure, "game type=",self.parser.TourneyGameType
|
||||||
|
print "buy-in=", self.parser.TourneyBuyIn, "rebuys=", self.parser.TourneyRebuys, "total players=", self.parser.TourneyPlayers, "pool=", self.parser.TourneyPool
|
||||||
|
print "results=", self.parser.Results
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
me = EverleafSummary()
|
||||||
|
me.main()
|
|
@ -39,6 +39,7 @@ if os.name == 'nt':
|
||||||
|
|
||||||
# FreePokerTools modules
|
# FreePokerTools modules
|
||||||
import Configuration
|
import Configuration
|
||||||
|
from fpdb_simple import LOCALE_ENCODING
|
||||||
|
|
||||||
# Each TableWindow object must have the following attributes correctly populated:
|
# Each TableWindow object must have the following attributes correctly populated:
|
||||||
# tw.name = the table name from the title bar, which must to match the table name
|
# tw.name = the table name from the title bar, which must to match the table name
|
||||||
|
@ -68,6 +69,7 @@ class Table_Window:
|
||||||
if 'site' in info: self.site = info['site']
|
if 'site' in info: self.site = info['site']
|
||||||
if 'title' in info: self.title = info['title']
|
if 'title' in info: self.title = info['title']
|
||||||
if 'name' in info: self.name = info['name']
|
if 'name' in info: self.name = info['name']
|
||||||
|
self.gdkhandle = None
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
# __str__ method for testing
|
# __str__ method for testing
|
||||||
|
@ -230,16 +232,21 @@ def discover_nt_by_name(c, tablename):
|
||||||
"""Finds poker client window with the given table name."""
|
"""Finds poker client window with the given table name."""
|
||||||
titles = {}
|
titles = {}
|
||||||
win32gui.EnumWindows(win_enum_handler, titles)
|
win32gui.EnumWindows(win_enum_handler, titles)
|
||||||
|
|
||||||
for hwnd in titles:
|
for hwnd in titles:
|
||||||
#print "Tables.py: tablename =", tablename, "title =", titles[hwnd]
|
#print "Tables.py: tablename =", tablename, "title =", titles[hwnd]
|
||||||
try:
|
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
|
# this can blow up in XP on some windows, eg firefox displaying http://docs.python.org/tutorial/classes.html
|
||||||
if not tablename in titles[hwnd]: continue
|
if not tablename.lower() in titles[hwnd].decode(LOCALE_ENCODING).lower(): continue
|
||||||
except:
|
except:
|
||||||
continue
|
continue
|
||||||
if 'History for table:' in titles[hwnd]: continue # Everleaf Network HH viewer window
|
if 'History for table:' in titles[hwnd]: continue # Everleaf Network HH viewer window
|
||||||
if 'HUD:' in titles[hwnd]: continue # FPDB HUD window
|
if 'HUD:' in titles[hwnd]: continue # FPDB HUD window
|
||||||
if 'Chat:' in titles[hwnd]: continue # Some sites (FTP? PS? Others?) have seperable or seperately constructed chat windows
|
if 'Chat:' in titles[hwnd]: continue # Some sites (FTP? PS? Others?) have seperable or seperately constructed chat windows
|
||||||
|
if ' - Table ' in titles[hwnd]: continue # Absolute table Chat window.. sigh. TODO: Can we tell what site we're trying to discover for somehow in here, so i can limit this check just to AP searches?
|
||||||
|
temp = decode_windows(c, titles[hwnd], hwnd)
|
||||||
|
#print "attach to window", temp
|
||||||
return decode_windows(c, titles[hwnd], hwnd)
|
return decode_windows(c, titles[hwnd], hwnd)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -302,7 +309,9 @@ def decode_windows(c, title, hwnd):
|
||||||
return info
|
return info
|
||||||
|
|
||||||
def win_enum_handler(hwnd, titles):
|
def win_enum_handler(hwnd, titles):
|
||||||
titles[hwnd] = win32gui.GetWindowText(hwnd)
|
str = win32gui.GetWindowText(hwnd)
|
||||||
|
if str != "":
|
||||||
|
titles[hwnd] = win32gui.GetWindowText(hwnd)
|
||||||
|
|
||||||
###################################################################
|
###################################################################
|
||||||
# Utility routines used by all the discoverers.
|
# Utility routines used by all the discoverers.
|
||||||
|
|
310
pyfpdb/TournamentTracker.py
Normal file
310
pyfpdb/TournamentTracker.py
Normal file
|
@ -0,0 +1,310 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
"""TourneyTracker.py
|
||||||
|
Based on HUD_main .. who knows if we want to actually use this or not
|
||||||
|
"""
|
||||||
|
# Copyright 2008, 2009, Eric Blade
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
|
||||||
|
# to do allow window resizing
|
||||||
|
# to do hud to echo, but ignore non numbers
|
||||||
|
# to do no stat window for hero
|
||||||
|
# to do things to add to config.xml
|
||||||
|
|
||||||
|
# Standard Library modules
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import Options
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
(options, sys.argv) = Options.fpdb_options()
|
||||||
|
|
||||||
|
if not options.errorsToConsole:
|
||||||
|
print "Note: error output is being diverted to fpdb-error-log.txt and HUD-error.txt. Any major error will be reported there _only_."
|
||||||
|
errorFile = open('tourneyerror.txt', 'w', 0)
|
||||||
|
sys.stderr = errorFile
|
||||||
|
|
||||||
|
import thread
|
||||||
|
import time
|
||||||
|
import string
|
||||||
|
import re
|
||||||
|
|
||||||
|
# pyGTK modules
|
||||||
|
import pygtk
|
||||||
|
import gtk
|
||||||
|
import gobject
|
||||||
|
|
||||||
|
# FreePokerTools modules
|
||||||
|
import Configuration
|
||||||
|
import Database
|
||||||
|
import SummaryEverleaf
|
||||||
|
|
||||||
|
class Tournament:
|
||||||
|
"""Tournament will hold the information about a tournament, I guess ? Remember I'm new to this language, so I don't know the best ways to do things"""
|
||||||
|
|
||||||
|
def __init__(self, parent, site, tid): # site should probably be something in the config object, but i don't know how the config object works right now, so we're going to make it a str ..
|
||||||
|
print "Tournament init"
|
||||||
|
self.parent = parent
|
||||||
|
self.window = None
|
||||||
|
self.site = site
|
||||||
|
self.id = tid
|
||||||
|
self.starttime = time.time()
|
||||||
|
self.endtime = None
|
||||||
|
self.game = None
|
||||||
|
self.structure = None
|
||||||
|
self.buyin = 0
|
||||||
|
self.fee = 0
|
||||||
|
self.rebuys = False
|
||||||
|
self.numrebuys = 0 # this should probably be attached to the players list...
|
||||||
|
self.numplayers = 0
|
||||||
|
self.prizepool = 0
|
||||||
|
self.players = {} # eventually i'd guess we'd probably want to fill this with playername:playerid's
|
||||||
|
self.results = {} # i'd guess we'd want to load this up with playerid's instead of playernames, too, as well, also
|
||||||
|
|
||||||
|
# if site == "Everleaf": # this should be attached to a button that says "retrieve tournament info" or something for sites that we know how to do it for
|
||||||
|
summary = SummaryEverleaf.EverleafSummary()
|
||||||
|
self.site = summary.parser.SiteName
|
||||||
|
self.id = summary.parser.TourneyId
|
||||||
|
self.starttime = summary.parser.TourneyStartTime
|
||||||
|
self.endtime = summary.parser.TourneyEndTime
|
||||||
|
self.game = summary.parser.TourneyGameType
|
||||||
|
self.structure = summary.parser.TourneyStructure
|
||||||
|
self.buyin = summary.parser.TourneyBuyIn # need to remember to parse the Fee out of this and move it to self.fee
|
||||||
|
self.rebuys = (summary.parser.TourneyRebuys == "yes")
|
||||||
|
self.prizepool = summary.parser.TourneyPool
|
||||||
|
self.numplayers = summary.parser.TourneyPlayers
|
||||||
|
|
||||||
|
self.openwindow() # let's start by getting any info we need.. meh
|
||||||
|
|
||||||
|
def openwindow(self, widget=None):
|
||||||
|
if self.window is not None:
|
||||||
|
self.window.show() # isn't there a better way to bring something to the front? not that GTK focus works right anyway, ever
|
||||||
|
else:
|
||||||
|
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
|
||||||
|
print "tournament edit window=", self.window
|
||||||
|
self.window.connect("delete_event", self.delete_event)
|
||||||
|
self.window.connect("destroy", self.destroy)
|
||||||
|
self.window.set_title("FPDB Tournament Entry")
|
||||||
|
self.window.set_border_width(1)
|
||||||
|
self.window.set_default_size(480,640)
|
||||||
|
self.window.set_resizable(True)
|
||||||
|
|
||||||
|
self.main_vbox = gtk.VBox(False, 1)
|
||||||
|
self.main_vbox.set_border_width(1)
|
||||||
|
self.window.add(self.main_vbox)
|
||||||
|
self.window.show()
|
||||||
|
|
||||||
|
def addrebuy(self, widget=None):
|
||||||
|
t = self
|
||||||
|
t.numrebuys += 1
|
||||||
|
t.mylabel.set_label("%s - %s - %s - %s - %s %s - %s - %s - %s - %s - %s" % (t.site, t.id, t.starttime, t.endtime, t.structure, t.game, t.buyin, t.fee, t.numrebuys, t.numplayers, t.prizepool))
|
||||||
|
|
||||||
|
def delete_event(self, widget, event, data=None):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def destroy(self, widget, data=None):
|
||||||
|
return False
|
||||||
|
#end def destroy
|
||||||
|
|
||||||
|
|
||||||
|
class ttracker_main(object):
|
||||||
|
"""A main() object to own both the read_stdin thread and the gui."""
|
||||||
|
# This class mainly provides state for controlling the multiple HUDs.
|
||||||
|
|
||||||
|
def __init__(self, db_name = 'fpdb'):
|
||||||
|
self.db_name = db_name
|
||||||
|
self.config = Configuration.Config(file=options.config, dbname=options.dbname)
|
||||||
|
self.tourney_list = []
|
||||||
|
|
||||||
|
# a thread to read stdin
|
||||||
|
gobject.threads_init() # this is required
|
||||||
|
thread.start_new_thread(self.read_stdin, ()) # starts the thread
|
||||||
|
|
||||||
|
# a main window
|
||||||
|
self.main_window = gtk.Window()
|
||||||
|
self.main_window.connect("destroy", self.destroy)
|
||||||
|
self.vb = gtk.VBox()
|
||||||
|
self.label = gtk.Label('Closing this window will stop the Tournament Tracker')
|
||||||
|
self.vb.add(self.label)
|
||||||
|
self.addbutton = gtk.Button(label="Enter Tournament")
|
||||||
|
self.addbutton.connect("clicked", self.addClicked, "add tournament")
|
||||||
|
self.vb.add(self.addbutton)
|
||||||
|
|
||||||
|
self.main_window.add(self.vb)
|
||||||
|
self.main_window.set_title("FPDB Tournament Tracker")
|
||||||
|
self.main_window.show_all()
|
||||||
|
|
||||||
|
def addClicked(self, widget, data): # what is "data"? i'm guessing anything i pass in after the function name in connect() but unsure because the documentation sucks
|
||||||
|
print "addClicked", widget, data
|
||||||
|
t = Tournament(self, None, None)
|
||||||
|
if t is not None:
|
||||||
|
print "new tournament=", t
|
||||||
|
self.tourney_list.append(t)
|
||||||
|
mylabel = gtk.Label("%s - %s - %s - %s - %s %s - %s - %s - %s - %s - %s" % (t.site, t.id, t.starttime, t.endtime, t.structure, t.game, t.buyin, t.fee, t.numrebuys, t.numplayers, t.prizepool))
|
||||||
|
print "new label=", mylabel
|
||||||
|
editbutton = gtk.Button(label="Edit")
|
||||||
|
print "new button=", editbutton
|
||||||
|
editbutton.connect("clicked", t.openwindow)
|
||||||
|
rebuybutton = gtk.Button(label="Rebuy")
|
||||||
|
rebuybutton.connect("clicked", t.addrebuy)
|
||||||
|
self.vb.add(rebuybutton)
|
||||||
|
self.vb.add(editbutton) # These should probably be put in.. a.. h-box? i don't know..
|
||||||
|
self.vb.add(mylabel)
|
||||||
|
self.main_window.resize_children()
|
||||||
|
self.main_window.show()
|
||||||
|
mylabel.show()
|
||||||
|
editbutton.show()
|
||||||
|
rebuybutton.show()
|
||||||
|
t.mylabel = mylabel
|
||||||
|
t.editbutton = editbutton
|
||||||
|
t.rebuybutton = rebuybutton
|
||||||
|
self.vb.show()
|
||||||
|
print self.tourney_list
|
||||||
|
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
# when we move the start command over to the main program, we can have the main program ask for the tourney id, and pipe it into the stdin here
|
||||||
|
# at least that was my initial thought on it
|
||||||
|
|
||||||
|
def destroy(*args): # call back for terminating the main eventloop
|
||||||
|
gtk.main_quit()
|
||||||
|
|
||||||
|
def create_HUD(self, new_hand_id, table, table_name, max, poker_game, stat_dict, cards):
|
||||||
|
|
||||||
|
def idle_func():
|
||||||
|
|
||||||
|
gtk.gdk.threads_enter()
|
||||||
|
try:
|
||||||
|
newlabel = gtk.Label("%s - %s" % (table.site, table_name))
|
||||||
|
self.vb.add(newlabel)
|
||||||
|
newlabel.show()
|
||||||
|
self.main_window.resize_children()
|
||||||
|
|
||||||
|
self.hud_dict[table_name].tablehudlabel = newlabel
|
||||||
|
self.hud_dict[table_name].create(new_hand_id, self.config, stat_dict, cards)
|
||||||
|
for m in self.hud_dict[table_name].aux_windows:
|
||||||
|
m.create()
|
||||||
|
m.update_gui(new_hand_id)
|
||||||
|
self.hud_dict[table_name].update(new_hand_id, self.config)
|
||||||
|
self.hud_dict[table_name].reposition_windows()
|
||||||
|
return False
|
||||||
|
finally:
|
||||||
|
gtk.gdk.threads_leave()
|
||||||
|
|
||||||
|
self.hud_dict[table_name] = Hud.Hud(self, table, max, poker_game, self.config, self.db_connection)
|
||||||
|
self.hud_dict[table_name].table_name = table_name
|
||||||
|
self.hud_dict[table_name].stat_dict = stat_dict
|
||||||
|
self.hud_dict[table_name].cards = cards
|
||||||
|
[aw.update_data(new_hand_id, self.db_connection) for aw in self.hud_dict[table_name].aux_windows]
|
||||||
|
gobject.idle_add(idle_func)
|
||||||
|
|
||||||
|
def update_HUD(self, new_hand_id, table_name, config):
|
||||||
|
"""Update a HUD gui from inside the non-gui read_stdin thread."""
|
||||||
|
# This is written so that only 1 thread can touch the gui--mainly
|
||||||
|
# for compatibility with Windows. This method dispatches the
|
||||||
|
# function idle_func() to be run by the gui thread, at its leisure.
|
||||||
|
def idle_func():
|
||||||
|
gtk.gdk.threads_enter()
|
||||||
|
try:
|
||||||
|
self.hud_dict[table_name].update(new_hand_id, config)
|
||||||
|
[aw.update_gui(new_hand_id) for aw in self.hud_dict[table_name].aux_windows]
|
||||||
|
return False
|
||||||
|
finally:
|
||||||
|
gtk.gdk.threads_leave()
|
||||||
|
gobject.idle_add(idle_func)
|
||||||
|
|
||||||
|
def read_stdin(self): # This is the thread function
|
||||||
|
"""Do all the non-gui heavy lifting for the HUD program."""
|
||||||
|
|
||||||
|
# This db connection is for the read_stdin thread only. It should not
|
||||||
|
# be passed to HUDs for use in the gui thread. HUD objects should not
|
||||||
|
# need their own access to the database, but should open their own
|
||||||
|
# if it is required.
|
||||||
|
self.db_connection = Database.Database(self.config, self.db_name, 'temp')
|
||||||
|
# self.db_connection.init_hud_stat_vars(hud_days)
|
||||||
|
tourny_finder = re.compile('(\d+) (\d+)')
|
||||||
|
|
||||||
|
while 1: # wait for a new hand number on stdin
|
||||||
|
new_hand_id = sys.stdin.readline()
|
||||||
|
new_hand_id = string.rstrip(new_hand_id)
|
||||||
|
if new_hand_id == "": # blank line means quit
|
||||||
|
self.destroy()
|
||||||
|
break # this thread is not always killed immediately with gtk.main_quit()
|
||||||
|
# get basic info about the new hand from the db
|
||||||
|
# if there is a db error, complain, skip hand, and proceed
|
||||||
|
try:
|
||||||
|
(table_name, max, poker_game, type) = self.db_connection.get_table_name(new_hand_id)
|
||||||
|
stat_dict = self.db_connection.get_stats_from_hand(new_hand_id, aggregate_stats[type]
|
||||||
|
,hud_style, agg_bb_mult)
|
||||||
|
|
||||||
|
cards = self.db_connection.get_cards(new_hand_id)
|
||||||
|
comm_cards = self.db_connection.get_common_cards(new_hand_id)
|
||||||
|
if comm_cards != {}: # stud!
|
||||||
|
cards['common'] = comm_cards['common']
|
||||||
|
except Exception, err:
|
||||||
|
err = traceback.extract_tb(sys.exc_info()[2])[-1]
|
||||||
|
print "db error: skipping "+str(new_hand_id)+" "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1])
|
||||||
|
if new_hand_id: # new_hand_id is none if we had an error prior to the store
|
||||||
|
sys.stderr.write("Database error %s in hand %d. Skipping.\n" % (err, int(new_hand_id)))
|
||||||
|
continue
|
||||||
|
|
||||||
|
if type == "tour": # hand is from a tournament
|
||||||
|
mat_obj = tourny_finder.search(table_name)
|
||||||
|
if mat_obj:
|
||||||
|
(tour_number, tab_number) = mat_obj.group(1, 2)
|
||||||
|
temp_key = tour_number
|
||||||
|
else: # tourney, but can't get number and table
|
||||||
|
print "could not find tournament: skipping "
|
||||||
|
sys.stderr.write("Could not find tournament %d in hand %d. Skipping.\n" % (int(tour_number), int(new_hand_id)))
|
||||||
|
continue
|
||||||
|
|
||||||
|
else:
|
||||||
|
temp_key = table_name
|
||||||
|
|
||||||
|
# Update an existing HUD
|
||||||
|
if temp_key in self.hud_dict:
|
||||||
|
self.hud_dict[temp_key].stat_dict = stat_dict
|
||||||
|
self.hud_dict[temp_key].cards = cards
|
||||||
|
[aw.update_data(new_hand_id, self.db_connection) for aw in self.hud_dict[temp_key].aux_windows]
|
||||||
|
self.update_HUD(new_hand_id, temp_key, self.config)
|
||||||
|
|
||||||
|
# Or create a new HUD
|
||||||
|
else:
|
||||||
|
if type == "tour":
|
||||||
|
tablewindow = Tables.discover_tournament_table(self.config, tour_number, tab_number)
|
||||||
|
else:
|
||||||
|
tablewindow = Tables.discover_table_by_name(self.config, table_name)
|
||||||
|
if tablewindow == None:
|
||||||
|
# If no client window is found on the screen, complain and continue
|
||||||
|
if type == "tour":
|
||||||
|
table_name = "%s %s" % (tour_number, tab_number)
|
||||||
|
sys.stderr.write("table name "+table_name+" not found, skipping.\n")
|
||||||
|
else:
|
||||||
|
self.create_HUD(new_hand_id, tablewindow, temp_key, max, poker_game, stat_dict, cards)
|
||||||
|
self.db_connection.connection.rollback()
|
||||||
|
|
||||||
|
if __name__== "__main__":
|
||||||
|
|
||||||
|
sys.stderr.write("tournament tracker starting\n")
|
||||||
|
sys.stderr.write("Using db name = %s\n" % (options.dbname))
|
||||||
|
|
||||||
|
# start the HUD_main object
|
||||||
|
hm = ttracker_main(db_name = options.dbname)
|
||||||
|
|
||||||
|
# start the event loop
|
||||||
|
gtk.main()
|
471
pyfpdb/Tourney.py
Normal file
471
pyfpdb/Tourney.py
Normal file
|
@ -0,0 +1,471 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
#Copyright 2009 Stephane Alessio
|
||||||
|
#This program is free software: you can redistribute it and/or modify
|
||||||
|
#it under the terms of the GNU Affero General Public License as published by
|
||||||
|
#the Free Software Foundation, version 3 of the License.
|
||||||
|
#
|
||||||
|
#This program is distributed in the hope that it will be useful,
|
||||||
|
#but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
#GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
#You should have received a copy of the GNU Affero General Public License
|
||||||
|
#along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#In the "official" distribution you can find the license in
|
||||||
|
#agpl-3.0.txt in the docs folder of the package.
|
||||||
|
|
||||||
|
# TODO: check to keep only the needed modules
|
||||||
|
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import traceback
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
from decimal import Decimal
|
||||||
|
import operator
|
||||||
|
import time,datetime
|
||||||
|
from copy import deepcopy
|
||||||
|
from Exceptions import *
|
||||||
|
import pprint
|
||||||
|
import DerivedStats
|
||||||
|
import Card
|
||||||
|
|
||||||
|
log = logging.getLogger("parser")
|
||||||
|
|
||||||
|
class Tourney(object):
|
||||||
|
|
||||||
|
################################################################
|
||||||
|
# Class Variables
|
||||||
|
UPS = {'a':'A', 't':'T', 'j':'J', 'q':'Q', 'k':'K', 'S':'s', 'C':'c', 'H':'h', 'D':'d'} # SAL- TO KEEP ??
|
||||||
|
LCS = {'H':'h', 'D':'d', 'C':'c', 'S':'s'} # SAL- TO KEEP ??
|
||||||
|
SYMBOL = {'USD': '$', 'EUR': u'$', 'T$': '', 'play': ''}
|
||||||
|
MS = {'horse' : 'HORSE', '8game' : '8-Game', 'hose' : 'HOSE', 'ha': 'HA'}
|
||||||
|
SITEIDS = {'Fulltilt':1, 'PokerStars':2, 'Everleaf':3, 'Win2day':4, 'OnGame':5, 'UltimateBet':6, 'Betfair':7, 'Absolute':8, 'PartyPoker':9 }
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, sitename, gametype, summaryText, builtFrom = "HHC"):
|
||||||
|
self.sitename = sitename
|
||||||
|
self.siteId = self.SITEIDS[sitename]
|
||||||
|
self.gametype = gametype
|
||||||
|
self.starttime = None
|
||||||
|
self.endtime = None
|
||||||
|
self.summaryText = summaryText
|
||||||
|
self.tourneyName = None
|
||||||
|
self.tourNo = None
|
||||||
|
self.buyin = None
|
||||||
|
self.fee = None # the Database code is looking for this one .. ?
|
||||||
|
self.hero = None
|
||||||
|
self.maxseats = None
|
||||||
|
self.entries = 0
|
||||||
|
self.speed = "Normal"
|
||||||
|
self.prizepool = None # Make it a dict in order to deal (eventually later) with non-money winnings : {'MONEY' : amount, 'OTHER' : Value ??}
|
||||||
|
self.buyInChips = None
|
||||||
|
self.mixed = None
|
||||||
|
self.isRebuy = False
|
||||||
|
self.isKO = False
|
||||||
|
self.isHU = False
|
||||||
|
self.isMatrix = False
|
||||||
|
self.isShootout = False
|
||||||
|
self.matrixMatchId = None # For Matrix tourneys : 1-4 => match tables (traditionnal), 0 => Positional winnings info
|
||||||
|
self.subTourneyBuyin = None
|
||||||
|
self.subTourneyFee = None
|
||||||
|
self.rebuyChips = 0
|
||||||
|
self.addOnChips = 0
|
||||||
|
self.rebuyAmount = 0
|
||||||
|
self.addOnAmount = 0
|
||||||
|
self.totalRebuys = 0
|
||||||
|
self.totalAddOns = 0
|
||||||
|
self.koBounty = 0
|
||||||
|
self.tourneyComment = None
|
||||||
|
self.players = []
|
||||||
|
|
||||||
|
# Collections indexed by player names
|
||||||
|
self.finishPositions = {}
|
||||||
|
self.winnings = {}
|
||||||
|
self.payinAmounts = {}
|
||||||
|
self.countRebuys = {}
|
||||||
|
self.countAddOns = {}
|
||||||
|
self.countKO = {}
|
||||||
|
|
||||||
|
# currency symbol for this summary
|
||||||
|
self.sym = None
|
||||||
|
#self.sym = self.SYMBOL[self.gametype['currency']] # save typing! delete this attr when done
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
#TODO : Update
|
||||||
|
vars = ( ("SITE", self.sitename),
|
||||||
|
("START TIME", self.starttime),
|
||||||
|
("END TIME", self.endtime),
|
||||||
|
("TOURNEY NAME", self.tourneyName),
|
||||||
|
("TOURNEY NO", self.tourNo),
|
||||||
|
("BUYIN", self.buyin),
|
||||||
|
("FEE", self.fee),
|
||||||
|
("HERO", self.hero),
|
||||||
|
("MAXSEATS", self.maxseats),
|
||||||
|
("ENTRIES", self.entries),
|
||||||
|
("SPEED", self.speed),
|
||||||
|
("PRIZE POOL", self.prizepool),
|
||||||
|
("STARTING CHIP COUNT", self.buyInChips),
|
||||||
|
("MIXED", self.mixed),
|
||||||
|
("REBUY ADDON", self.isRebuy),
|
||||||
|
("KO", self.isKO),
|
||||||
|
("HU", self.isHU),
|
||||||
|
("MATRIX", self.isMatrix),
|
||||||
|
("SHOOTOUT", self.isShootout),
|
||||||
|
("MATRIX MATCH ID", self.matrixMatchId),
|
||||||
|
("SUB TOURNEY BUY IN", self.subTourneyBuyin),
|
||||||
|
("SUB TOURNEY FEE", self.subTourneyFee),
|
||||||
|
("REBUY CHIPS", self.rebuyChips),
|
||||||
|
("ADDON CHIPS", self.addOnChips),
|
||||||
|
("REBUY AMOUNT", self.rebuyAmount),
|
||||||
|
("ADDON AMOUNT", self.addOnAmount),
|
||||||
|
("TOTAL REBUYS", self.totalRebuys),
|
||||||
|
("TOTAL ADDONS", self.totalAddOns),
|
||||||
|
("KO BOUNTY", self.koBounty),
|
||||||
|
("TOURNEY COMMENT", self.tourneyComment)
|
||||||
|
)
|
||||||
|
|
||||||
|
structs = ( ("GAMETYPE", self.gametype),
|
||||||
|
("PLAYERS", self.players),
|
||||||
|
("PAYIN AMOUNTS", self.payinAmounts),
|
||||||
|
("POSITIONS", self.finishPositions),
|
||||||
|
("WINNINGS", self.winnings),
|
||||||
|
("COUNT REBUYS", self.countRebuys),
|
||||||
|
("COUNT ADDONS", self.countAddOns),
|
||||||
|
("NB OF KO", self.countKO)
|
||||||
|
)
|
||||||
|
str = ''
|
||||||
|
for (name, var) in vars:
|
||||||
|
str = str + "\n%s = " % name + pprint.pformat(var)
|
||||||
|
|
||||||
|
for (name, struct) in structs:
|
||||||
|
str = str + "\n%s =\n" % name + pprint.pformat(struct, 4)
|
||||||
|
return str
|
||||||
|
|
||||||
|
def getSummaryText(self):
|
||||||
|
return self.summaryText
|
||||||
|
|
||||||
|
def prepInsert(self, db):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def insert(self, db):
|
||||||
|
# First : check all needed info is filled in the object, especially for the initial select
|
||||||
|
|
||||||
|
# Notes on DB Insert
|
||||||
|
# Some identified issues for tourneys already in the DB (which occurs when the HH file is parsed and inserted before the Summary)
|
||||||
|
# Be careful on updates that could make the HH import not match the tourney inserted from a previous summary import !!
|
||||||
|
# BuyIn/Fee can be at 0/0 => match may not be easy
|
||||||
|
# Only one existinf Tourney entry for Matrix Tourneys, but multiple Summary files
|
||||||
|
# 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
|
||||||
|
# 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 ??
|
||||||
|
|
||||||
|
dbTourneyTypeId = db.tRecogniseTourneyType(self)
|
||||||
|
logging.debug("Tourney Type ID = %d" % dbTourneyTypeId)
|
||||||
|
dbTourneyId = db.tRecognizeTourney(self, dbTourneyTypeId)
|
||||||
|
logging.debug("Tourney ID = %d" % dbTourneyId)
|
||||||
|
dbTourneysPlayersIds = db.tStoreTourneyPlayers(self, dbTourneyId)
|
||||||
|
logging.debug("TourneysPlayersId = %s" % dbTourneysPlayersIds)
|
||||||
|
db.tUpdateTourneysHandsPlayers(self, dbTourneysPlayersIds, dbTourneyTypeId)
|
||||||
|
logging.debug("tUpdateTourneysHandsPlayers done")
|
||||||
|
logging.debug("Tourney Insert done")
|
||||||
|
|
||||||
|
# TO DO : Return what has been done (tourney created, updated, nothing)
|
||||||
|
# ?? 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, tourneyPlayers and handsplayers)
|
||||||
|
# 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
|
||||||
|
stored = 0
|
||||||
|
duplicates = 0
|
||||||
|
partial = 0
|
||||||
|
errors = 0
|
||||||
|
ttime = 0
|
||||||
|
return (stored, duplicates, partial, errors, ttime)
|
||||||
|
|
||||||
|
|
||||||
|
def old_insert_from_Hand(self, db):
|
||||||
|
""" Function to insert Hand into database
|
||||||
|
Should not commit, and do minimal selects. Callers may want to cache commits
|
||||||
|
db: a connected fpdb_db object"""
|
||||||
|
# TODO:
|
||||||
|
# Players - base playerid and siteid tuple
|
||||||
|
sqlids = db.getSqlPlayerIDs([p[1] for p in self.players], self.siteId)
|
||||||
|
|
||||||
|
#Gametypes
|
||||||
|
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
|
||||||
|
# Hands - Summary information of hand indexed by handId - gameinfo
|
||||||
|
#This should be moved to prepInsert
|
||||||
|
hh = {}
|
||||||
|
hh['siteHandNo'] = self.handid
|
||||||
|
hh['handStart'] = self.starttime
|
||||||
|
hh['gameTypeId'] = gtid
|
||||||
|
# seats TINYINT NOT NULL,
|
||||||
|
hh['tableName'] = self.tablename
|
||||||
|
hh['maxSeats'] = self.maxseats
|
||||||
|
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]]
|
||||||
|
hh['boardcard1'] = cards[0]
|
||||||
|
hh['boardcard2'] = cards[1]
|
||||||
|
hh['boardcard3'] = cards[2]
|
||||||
|
hh['boardcard4'] = cards[3]
|
||||||
|
hh['boardcard5'] = cards[4]
|
||||||
|
|
||||||
|
# texture smallint,
|
||||||
|
# playersVpi SMALLINT NOT NULL, /* num of players vpi */
|
||||||
|
# Needs to be recorded
|
||||||
|
# playersAtStreet1 SMALLINT NOT NULL, /* num of players seeing flop/street4 */
|
||||||
|
# Needs to be recorded
|
||||||
|
# playersAtStreet2 SMALLINT NOT NULL,
|
||||||
|
# Needs to be recorded
|
||||||
|
# playersAtStreet3 SMALLINT NOT NULL,
|
||||||
|
# Needs to be recorded
|
||||||
|
# playersAtStreet4 SMALLINT NOT NULL,
|
||||||
|
# Needs to be recorded
|
||||||
|
# playersAtShowdown SMALLINT NOT NULL,
|
||||||
|
# Needs to be recorded
|
||||||
|
# street0Raises TINYINT NOT NULL, /* num small bets paid to see flop/street4, including blind */
|
||||||
|
# Needs to be recorded
|
||||||
|
# street1Raises TINYINT NOT NULL, /* num small bets paid to see turn/street5 */
|
||||||
|
# Needs to be recorded
|
||||||
|
# street2Raises TINYINT NOT NULL, /* num big bets paid to see river/street6 */
|
||||||
|
# Needs to be recorded
|
||||||
|
# street3Raises TINYINT NOT NULL, /* num big bets paid to see sd/street7 */
|
||||||
|
# Needs to be recorded
|
||||||
|
# street4Raises TINYINT NOT NULL, /* num big bets paid to see showdown */
|
||||||
|
# Needs to be recorded
|
||||||
|
|
||||||
|
#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 ?
|
||||||
|
# TourneysPlayers
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
def select(self, tourneyId):
|
||||||
|
""" Function to create Tourney object from database """
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def addPlayer(self, rank, name, winnings, payinAmount, nbRebuys, nbAddons, nbKO):
|
||||||
|
"""\
|
||||||
|
Adds a player to the tourney, and initialises data structures indexed by player.
|
||||||
|
rank (int) indicating the finishing rank (can be -1 if unknown)
|
||||||
|
name (string) player name
|
||||||
|
winnings (decimal) the money the player ended the tourney with (can be 0, or -1 if unknown)
|
||||||
|
"""
|
||||||
|
log.debug("addPlayer: rank:%s - name : '%s' - Winnings (%s)" % (rank, name, winnings))
|
||||||
|
self.players.append(name)
|
||||||
|
self.finishPositions.update( { name : Decimal(rank) } )
|
||||||
|
self.winnings.update( { name : Decimal(winnings) } )
|
||||||
|
self.payinAmounts.update( {name : Decimal(payinAmount) } )
|
||||||
|
self.countRebuys.update( {name: Decimal(nbRebuys) } )
|
||||||
|
self.countAddOns.update( {name: Decimal(nbAddons) } )
|
||||||
|
self.countKO.update( {name : Decimal(nbKO) } )
|
||||||
|
|
||||||
|
|
||||||
|
def incrementPlayerWinnings(self, name, additionnalWinnings):
|
||||||
|
log.debug("incrementPlayerWinnings: name : '%s' - Add Winnings (%s)" % (name, additionnalWinnings))
|
||||||
|
oldWins = 0
|
||||||
|
if self.winnings.has_key(name):
|
||||||
|
oldWins = self.winnings[name]
|
||||||
|
else:
|
||||||
|
self.players.append([-1, name, 0])
|
||||||
|
|
||||||
|
self.winnings[name] = oldWins + Decimal(additionnalWinnings)
|
||||||
|
|
||||||
|
def checkPlayerExists(self,player):
|
||||||
|
if player not in [p[1] for p in self.players]:
|
||||||
|
print "checkPlayerExists", player, "fail"
|
||||||
|
raise FpdbParseError
|
||||||
|
|
||||||
|
|
||||||
|
def getGameTypeAsString(self):
|
||||||
|
"""\
|
||||||
|
Map the tuple self.gametype onto the pokerstars string describing it
|
||||||
|
"""
|
||||||
|
# currently it appears to be something like ["ring", "hold", "nl", sb, bb]:
|
||||||
|
gs = {"holdem" : "Hold'em",
|
||||||
|
"omahahi" : "Omaha",
|
||||||
|
"omahahilo" : "Omaha Hi/Lo",
|
||||||
|
"razz" : "Razz",
|
||||||
|
"studhi" : "7 Card Stud",
|
||||||
|
"studhilo" : "7 Card Stud Hi/Lo",
|
||||||
|
"fivedraw" : "5 Card Draw",
|
||||||
|
"27_1draw" : "FIXME",
|
||||||
|
"27_3draw" : "Triple Draw 2-7 Lowball",
|
||||||
|
"badugi" : "Badugi"
|
||||||
|
}
|
||||||
|
ls = {"nl" : "No Limit",
|
||||||
|
"pl" : "Pot Limit",
|
||||||
|
"fl" : "Limit",
|
||||||
|
"cn" : "Cap No Limit",
|
||||||
|
"cp" : "Cap Pot Limit"
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug("gametype: %s" %(self.gametype))
|
||||||
|
retstring = "%s %s" %(gs[self.gametype['category']], ls[self.gametype['limitType']])
|
||||||
|
return retstring
|
||||||
|
|
||||||
|
|
||||||
|
def writeSummary(self, fh=sys.__stdout__):
|
||||||
|
print >>fh, "Override me"
|
||||||
|
|
||||||
|
def printSummary(self):
|
||||||
|
self.writeSummary(sys.stdout)
|
||||||
|
|
||||||
|
|
||||||
|
def assemble(cnxn, tourneyId):
|
||||||
|
# TODO !!
|
||||||
|
c = cnxn.cursor()
|
||||||
|
|
||||||
|
# We need at least sitename, gametype, handid
|
||||||
|
# for the Hand.__init__
|
||||||
|
c.execute("""
|
||||||
|
select
|
||||||
|
s.name,
|
||||||
|
g.category,
|
||||||
|
g.base,
|
||||||
|
g.type,
|
||||||
|
g.limitType,
|
||||||
|
g.hilo,
|
||||||
|
round(g.smallBlind / 100.0,2),
|
||||||
|
round(g.bigBlind / 100.0,2),
|
||||||
|
round(g.smallBet / 100.0,2),
|
||||||
|
round(g.bigBet / 100.0,2),
|
||||||
|
s.currency,
|
||||||
|
h.boardcard1,
|
||||||
|
h.boardcard2,
|
||||||
|
h.boardcard3,
|
||||||
|
h.boardcard4,
|
||||||
|
h.boardcard5
|
||||||
|
from
|
||||||
|
hands as h,
|
||||||
|
sites as s,
|
||||||
|
gametypes as g,
|
||||||
|
handsplayers as hp,
|
||||||
|
players as p
|
||||||
|
where
|
||||||
|
h.id = %(handid)s
|
||||||
|
and g.id = h.gametypeid
|
||||||
|
and hp.handid = h.id
|
||||||
|
and p.id = hp.playerid
|
||||||
|
and s.id = p.siteid
|
||||||
|
limit 1""", {'handid':handid})
|
||||||
|
#TODO: siteid should be in hands table - we took the scenic route through players here.
|
||||||
|
res = c.fetchone()
|
||||||
|
gametype = {'category':res[1],'base':res[2],'type':res[3],'limitType':res[4],'hilo':res[5],'sb':res[6],'bb':res[7], 'currency':res[10]}
|
||||||
|
h = HoldemOmahaHand(hhc = None, sitename=res[0], gametype = gametype, handText=None, builtFrom = "DB", handid=handid)
|
||||||
|
cards = map(Card.valueSuitFromCard, res[11:16] )
|
||||||
|
if cards[0]:
|
||||||
|
h.setCommunityCards('FLOP', cards[0:3])
|
||||||
|
if cards[3]:
|
||||||
|
h.setCommunityCards('TURN', [cards[3]])
|
||||||
|
if cards[4]:
|
||||||
|
h.setCommunityCards('RIVER', [cards[4]])
|
||||||
|
#[Card.valueSuitFromCard(x) for x in cards]
|
||||||
|
|
||||||
|
# HandInfo : HID, TABLE
|
||||||
|
# BUTTON - why is this treated specially in Hand?
|
||||||
|
# answer: it is written out in hand histories
|
||||||
|
# still, I think we should record all the active seat positions in a seat_order array
|
||||||
|
c.execute("""
|
||||||
|
SELECT
|
||||||
|
h.sitehandno as hid,
|
||||||
|
h.tablename as table,
|
||||||
|
h.handstart as starttime
|
||||||
|
FROM
|
||||||
|
hands as h
|
||||||
|
WHERE h.id = %(handid)s
|
||||||
|
""", {'handid':handid})
|
||||||
|
res = c.fetchone()
|
||||||
|
h.handid = res[0]
|
||||||
|
h.tablename = res[1]
|
||||||
|
h.starttime = res[2] # automatically a datetime
|
||||||
|
|
||||||
|
# PlayerStacks
|
||||||
|
c.execute("""
|
||||||
|
SELECT
|
||||||
|
hp.seatno,
|
||||||
|
round(hp.winnings / 100.0,2) as winnings,
|
||||||
|
p.name,
|
||||||
|
round(hp.startcash / 100.0,2) as chips,
|
||||||
|
hp.card1,hp.card2,
|
||||||
|
hp.position
|
||||||
|
FROM
|
||||||
|
handsplayers as hp,
|
||||||
|
players as p
|
||||||
|
WHERE
|
||||||
|
hp.handid = %(handid)s
|
||||||
|
and p.id = hp.playerid
|
||||||
|
""", {'handid':handid})
|
||||||
|
for (seat, winnings, name, chips, card1,card2, position) in c.fetchall():
|
||||||
|
h.addPlayer(seat,name,chips)
|
||||||
|
if card1 and card2:
|
||||||
|
h.addHoleCards(map(Card.valueSuitFromCard, (card1,card2)), name, dealt=True)
|
||||||
|
if winnings > 0:
|
||||||
|
h.addCollectPot(name, winnings)
|
||||||
|
if position == 'B':
|
||||||
|
h.buttonpos = seat
|
||||||
|
|
||||||
|
|
||||||
|
# actions
|
||||||
|
c.execute("""
|
||||||
|
SELECT
|
||||||
|
(ha.street,ha.actionno) as actnum,
|
||||||
|
p.name,
|
||||||
|
ha.street,
|
||||||
|
ha.action,
|
||||||
|
ha.allin,
|
||||||
|
round(ha.amount / 100.0,2)
|
||||||
|
FROM
|
||||||
|
handsplayers as hp,
|
||||||
|
handsactions as ha,
|
||||||
|
players as p
|
||||||
|
WHERE
|
||||||
|
hp.handid = %(handid)s
|
||||||
|
and ha.handsplayerid = hp.id
|
||||||
|
and p.id = hp.playerid
|
||||||
|
ORDER BY
|
||||||
|
ha.street,ha.actionno
|
||||||
|
""", {'handid':handid})
|
||||||
|
res = c.fetchall()
|
||||||
|
for (actnum,player, streetnum, act, allin, amount) in res:
|
||||||
|
act=act.strip()
|
||||||
|
street = h.allStreets[streetnum+1]
|
||||||
|
if act==u'blind':
|
||||||
|
h.addBlind(player, 'big blind', amount)
|
||||||
|
# TODO: The type of blind is not recorded in the DB.
|
||||||
|
# TODO: preflop street name anomalies in Hand
|
||||||
|
elif act==u'fold':
|
||||||
|
h.addFold(street,player)
|
||||||
|
elif act==u'call':
|
||||||
|
h.addCall(street,player,amount)
|
||||||
|
elif act==u'bet':
|
||||||
|
h.addBet(street,player,amount)
|
||||||
|
elif act==u'check':
|
||||||
|
h.addCheck(street,player)
|
||||||
|
elif act==u'unbet':
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
print act, player, streetnum, allin, amount
|
||||||
|
# TODO : other actions
|
||||||
|
|
||||||
|
#hhc.readShowdownActions(self)
|
||||||
|
#hc.readShownCards(self)
|
||||||
|
h.totalPot()
|
||||||
|
h.rake = h.totalpot - h.totalcollected
|
||||||
|
|
||||||
|
|
||||||
|
return h
|
||||||
|
|
|
@ -26,6 +26,11 @@ from HandHistoryConverter import *
|
||||||
|
|
||||||
class Win2day(HandHistoryConverter):
|
class Win2day(HandHistoryConverter):
|
||||||
|
|
||||||
|
sitename = "Win2day"
|
||||||
|
filetype = "text"
|
||||||
|
codepage = "cp1252"
|
||||||
|
siteID = 4
|
||||||
|
|
||||||
# Static regexes
|
# 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">
|
#<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_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)
|
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):
|
def compilePlayerRegexs(self, hand):
|
||||||
players = set([player[1] for player in hand.players])
|
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)
|
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>'
|
#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>'
|
#'^<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)
|
self.re_Action = re.compile(r'^<ACTION TYPE="(?P<ATYPE>[_A-Z]+)" PLAYER="%s"( VALUE="(?P<BET>[.0-9]+)")?></ACTION>' % player_re, re.MULTILINE)
|
||||||
|
|
|
@ -76,6 +76,7 @@ import SQL
|
||||||
import Database
|
import Database
|
||||||
import FpdbSQLQueries
|
import FpdbSQLQueries
|
||||||
import Configuration
|
import Configuration
|
||||||
|
from Exceptions import *
|
||||||
|
|
||||||
VERSION = "0.11"
|
VERSION = "0.11"
|
||||||
|
|
||||||
|
@ -98,7 +99,7 @@ class fpdb:
|
||||||
for i in self.tab_names: #todo: check this is valid
|
for i in self.tab_names: #todo: check this is valid
|
||||||
if i==new_tab_name:
|
if i==new_tab_name:
|
||||||
return # we depend on this to not create duplicate tabs, there's no reason to raise an error here?
|
return # we depend on this to not create duplicate tabs, there's no reason to raise an error here?
|
||||||
# raise fpdb_simple.FpdbError("duplicate tab_name not permitted")
|
# raise FpdbError("duplicate tab_name not permitted")
|
||||||
|
|
||||||
self.tabs.append(new_tab)
|
self.tabs.append(new_tab)
|
||||||
self.tab_names.append(new_tab_name)
|
self.tab_names.append(new_tab_name)
|
||||||
|
@ -120,7 +121,7 @@ class fpdb:
|
||||||
break
|
break
|
||||||
|
|
||||||
if tab_no == -1:
|
if tab_no == -1:
|
||||||
raise fpdb_simple.FpdbError("invalid tab_no")
|
raise FpdbError("invalid tab_no")
|
||||||
else:
|
else:
|
||||||
self.main_vbox.remove(self.current_tab)
|
self.main_vbox.remove(self.current_tab)
|
||||||
#self.current_tab.destroy()
|
#self.current_tab.destroy()
|
||||||
|
@ -243,30 +244,27 @@ class fpdb:
|
||||||
if self.obtain_global_lock(): # returns true if successful
|
if self.obtain_global_lock(): # returns true if successful
|
||||||
|
|
||||||
#lock_released = False
|
#lock_released = False
|
||||||
try:
|
dia_confirm = gtk.MessageDialog(parent=None, flags=0, type=gtk.MESSAGE_WARNING,
|
||||||
dia_confirm = gtk.MessageDialog(parent=None, flags=0, type=gtk.MESSAGE_WARNING,
|
buttons=(gtk.BUTTONS_YES_NO), message_format="Confirm deleting and recreating tables")
|
||||||
buttons=(gtk.BUTTONS_YES_NO), message_format="Confirm deleting and recreating tables")
|
diastring = "Please confirm that you want to (re-)create the tables. If there already are tables in the database " \
|
||||||
diastring = "Please confirm that you want to (re-)create the tables. If there already are tables in the database " \
|
+self.db.fdb.database+" on "+self.db.fdb.host+" they will be deleted."
|
||||||
+self.db.fdb.database+" on "+self.db.fdb.host+" they will be deleted."
|
dia_confirm.format_secondary_text(diastring)#todo: make above string with bold for db, host and deleted
|
||||||
dia_confirm.format_secondary_text(diastring)#todo: make above string with bold for db, host and deleted
|
|
||||||
|
|
||||||
response = dia_confirm.run()
|
response = dia_confirm.run()
|
||||||
dia_confirm.destroy()
|
dia_confirm.destroy()
|
||||||
if response == gtk.RESPONSE_YES:
|
if response == gtk.RESPONSE_YES:
|
||||||
#if self.db.fdb.backend == self.fdb_lock.fdb.MYSQL_INNODB:
|
#if self.db.fdb.backend == self.fdb_lock.fdb.MYSQL_INNODB:
|
||||||
# mysql requires locks on all tables or none - easier to release this lock
|
# mysql requires locks on all tables or none - easier to release this lock
|
||||||
# than lock all the other tables
|
# than lock all the other tables
|
||||||
# ToDo: lock all other tables so that lock doesn't have to be released
|
# ToDo: lock all other tables so that lock doesn't have to be released
|
||||||
# self.release_global_lock()
|
# self.release_global_lock()
|
||||||
# lock_released = True
|
# lock_released = True
|
||||||
self.db.recreate_tables()
|
self.db.recreate_tables()
|
||||||
#else:
|
#else:
|
||||||
# for other dbs use same connection as holds global lock
|
# for other dbs use same connection as holds global lock
|
||||||
# self.fdb_lock.fdb.recreate_tables()
|
# self.fdb_lock.fdb.recreate_tables()
|
||||||
elif response == gtk.RESPONSE_NO:
|
elif response == gtk.RESPONSE_NO:
|
||||||
print 'User cancelled recreating tables'
|
print 'User cancelled recreating tables'
|
||||||
except:
|
|
||||||
pass
|
|
||||||
#if not lock_released:
|
#if not lock_released:
|
||||||
self.release_global_lock()
|
self.release_global_lock()
|
||||||
#end def dia_recreate_tables
|
#end def dia_recreate_tables
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#Copyright 2008 Steffen Jobbagy-Felso
|
#Copyright 2008 Steffen Jobbagy-Felso
|
||||||
#This program is free software: you can redistribute it and/or modify
|
#This program is free software: you can redistribute it and/or modify
|
||||||
|
@ -21,8 +22,18 @@ import sys
|
||||||
import logging
|
import logging
|
||||||
from time import time, strftime
|
from time import time, strftime
|
||||||
|
|
||||||
|
use_pool = False
|
||||||
|
try:
|
||||||
|
import sqlalchemy.pool as pool
|
||||||
|
use_pool = True
|
||||||
|
except:
|
||||||
|
logging.info("Not using sqlalchemy connection pool.")
|
||||||
|
|
||||||
|
|
||||||
import fpdb_simple
|
import fpdb_simple
|
||||||
import FpdbSQLQueries
|
import FpdbSQLQueries
|
||||||
|
from Exceptions import *
|
||||||
|
|
||||||
|
|
||||||
class fpdb_db:
|
class fpdb_db:
|
||||||
MYSQL_INNODB = 2
|
MYSQL_INNODB = 2
|
||||||
|
@ -63,13 +74,17 @@ class fpdb_db:
|
||||||
self.database=database
|
self.database=database
|
||||||
if backend==fpdb_db.MYSQL_INNODB:
|
if backend==fpdb_db.MYSQL_INNODB:
|
||||||
import MySQLdb
|
import MySQLdb
|
||||||
|
if use_pool:
|
||||||
|
MySQLdb = pool.manage(MySQLdb, pool_size=5)
|
||||||
try:
|
try:
|
||||||
self.db = MySQLdb.connect(host = host, user = user, passwd = password, db = database, use_unicode=True, charset="utf8")
|
self.db = MySQLdb.connect(host = host, user = user, passwd = password, db = database, use_unicode=True)
|
||||||
except:
|
except:
|
||||||
raise fpdb_simple.FpdbError("MySQL connection failed")
|
raise FpdbError("MySQL connection failed")
|
||||||
elif backend==fpdb_db.PGSQL:
|
elif backend==fpdb_db.PGSQL:
|
||||||
import psycopg2
|
import psycopg2
|
||||||
import psycopg2.extensions
|
import psycopg2.extensions
|
||||||
|
if use_pool:
|
||||||
|
psycopg2 = pool.manage(psycopg2, pool_size=5)
|
||||||
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
|
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
|
||||||
# If DB connection is made over TCP, then the variables
|
# If DB connection is made over TCP, then the variables
|
||||||
# host, user and password are required
|
# host, user and password are required
|
||||||
|
@ -87,7 +102,7 @@ class fpdb_db:
|
||||||
pass
|
pass
|
||||||
#msg = "PostgreSQL direct connection to database (%s) failed, trying with user ..." % (database,)
|
#msg = "PostgreSQL direct connection to database (%s) failed, trying with user ..." % (database,)
|
||||||
#print msg
|
#print msg
|
||||||
#raise fpdb_simple.FpdbError(msg)
|
#raise FpdbError(msg)
|
||||||
if not connected:
|
if not connected:
|
||||||
try:
|
try:
|
||||||
self.db = psycopg2.connect(host = host,
|
self.db = psycopg2.connect(host = host,
|
||||||
|
@ -97,16 +112,19 @@ class fpdb_db:
|
||||||
except:
|
except:
|
||||||
msg = "PostgreSQL connection to database (%s) user (%s) failed." % (database, user)
|
msg = "PostgreSQL connection to database (%s) user (%s) failed." % (database, user)
|
||||||
print msg
|
print msg
|
||||||
raise fpdb_simple.FpdbError(msg)
|
raise FpdbError(msg)
|
||||||
elif backend==fpdb_db.SQLITE:
|
elif backend==fpdb_db.SQLITE:
|
||||||
logging.info("Connecting to SQLite:%(database)s" % {'database':database})
|
logging.info("Connecting to SQLite:%(database)s" % {'database':database})
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
if use_pool:
|
||||||
|
sqlite3 = pool.manage(sqlite3, pool_size=1)
|
||||||
|
else:
|
||||||
|
logging.warning("SQLite won't work well without 'sqlalchemy' installed.")
|
||||||
self.db = sqlite3.connect(database,detect_types=sqlite3.PARSE_DECLTYPES)
|
self.db = sqlite3.connect(database,detect_types=sqlite3.PARSE_DECLTYPES)
|
||||||
sqlite3.register_converter("bool", lambda x: bool(int(x)))
|
sqlite3.register_converter("bool", lambda x: bool(int(x)))
|
||||||
sqlite3.register_adapter(bool, lambda x: "1" if x else "0")
|
sqlite3.register_adapter(bool, lambda x: "1" if x else "0")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise fpdb_simple.FpdbError("unrecognised database backend:"+backend)
|
raise FpdbError("unrecognised database backend:"+backend)
|
||||||
self.cursor=self.db.cursor()
|
self.cursor=self.db.cursor()
|
||||||
# Set up query dictionary as early in the connection process as we can.
|
# Set up query dictionary as early in the connection process as we can.
|
||||||
self.sql = FpdbSQLQueries.FpdbSQLQueries(self.get_backend_name())
|
self.sql = FpdbSQLQueries.FpdbSQLQueries(self.get_backend_name())
|
||||||
|
@ -148,7 +166,7 @@ class fpdb_db:
|
||||||
elif self.backend==4:
|
elif self.backend==4:
|
||||||
return "SQLite"
|
return "SQLite"
|
||||||
else:
|
else:
|
||||||
raise fpdb_simple.FpdbError("invalid backend")
|
raise FpdbError("invalid backend")
|
||||||
#end def get_backend_name
|
#end def get_backend_name
|
||||||
|
|
||||||
def get_db_info(self):
|
def get_db_info(self):
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
import os # todo: remove this once import_dir is in fpdb_import
|
import os # todo: remove this once import_dir is in fpdb_import
|
||||||
import sys
|
import sys
|
||||||
from time import time, strftime, sleep
|
from time import time, strftime, sleep
|
||||||
import logging
|
|
||||||
import traceback
|
import traceback
|
||||||
import math
|
import math
|
||||||
import datetime
|
import datetime
|
||||||
|
@ -39,21 +38,26 @@ import Database
|
||||||
import fpdb_parse_logic
|
import fpdb_parse_logic
|
||||||
import Configuration
|
import Configuration
|
||||||
|
|
||||||
|
import logging, logging.config
|
||||||
|
logging.config.fileConfig(os.path.join(sys.path[0],"logging.conf"))
|
||||||
|
log = logging.getLogger('importer')
|
||||||
|
|
||||||
# database interface modules
|
# database interface modules
|
||||||
try:
|
try:
|
||||||
import MySQLdb
|
import MySQLdb
|
||||||
mysqlLibFound=True
|
mysqlLibFound=True
|
||||||
|
log.debug("Import module: MySQLdb")
|
||||||
except:
|
except:
|
||||||
pass
|
log.debug("Import module: MySQLdb not found")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import psycopg2
|
import psycopg2
|
||||||
pgsqlLibFound=True
|
pgsqlLibFound=True
|
||||||
import psycopg2.extensions
|
import psycopg2.extensions
|
||||||
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
|
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
|
||||||
|
log.debug("Import module: psycopg2")
|
||||||
except:
|
except:
|
||||||
pass
|
log.debug("Import module: psycopg2 not found")
|
||||||
|
|
||||||
class Importer:
|
class Importer:
|
||||||
|
|
||||||
|
@ -153,9 +157,9 @@ class Importer:
|
||||||
self.siteIds[site] = result[0][0]
|
self.siteIds[site] = result[0][0]
|
||||||
else:
|
else:
|
||||||
if len(result) == 0:
|
if len(result) == 0:
|
||||||
print "[ERROR] Database ID for %s not found" % site
|
log.error("Database ID for %s not found" % site)
|
||||||
else:
|
else:
|
||||||
print "[ERROR] More than 1 Database ID found for %s - Multiple currencies not implemented yet" % site
|
log.error("[ERROR] More than 1 Database ID found for %s - Multiple currencies not implemented yet" % site)
|
||||||
|
|
||||||
|
|
||||||
# Called from GuiBulkImport to add a file or directory.
|
# Called from GuiBulkImport to add a file or directory.
|
||||||
|
@ -168,7 +172,7 @@ class Importer:
|
||||||
if os.path.isdir(inputPath):
|
if os.path.isdir(inputPath):
|
||||||
for subdir in os.walk(inputPath):
|
for subdir in os.walk(inputPath):
|
||||||
for file in subdir[2]:
|
for file in subdir[2]:
|
||||||
self.addImportFile(os.path.join(inputPath, subdir[0], file), site=site, filter=filter)
|
self.addImportFile(os.path.join(subdir[0], file), site=site, filter=filter)
|
||||||
else:
|
else:
|
||||||
self.addImportFile(inputPath, site=site, filter=filter)
|
self.addImportFile(inputPath, site=site, filter=filter)
|
||||||
#Add a directory of files to filelist
|
#Add a directory of files to filelist
|
||||||
|
@ -189,7 +193,7 @@ class Importer:
|
||||||
#print " adding file ", file
|
#print " adding file ", file
|
||||||
self.addImportFile(os.path.join(dir, file), site, filter)
|
self.addImportFile(os.path.join(dir, file), site, filter)
|
||||||
else:
|
else:
|
||||||
print "Warning: Attempted to add non-directory: '" + str(dir) + "' as an import directory"
|
log.warning("Attempted to add non-directory: '" + str(dir) + "' as an import directory")
|
||||||
|
|
||||||
def runImport(self):
|
def runImport(self):
|
||||||
""""Run full import on self.filelist. This is called from GuiBulkImport.py"""
|
""""Run full import on self.filelist. This is called from GuiBulkImport.py"""
|
||||||
|
@ -199,7 +203,7 @@ class Importer:
|
||||||
# Initial setup
|
# Initial setup
|
||||||
start = datetime.datetime.now()
|
start = datetime.datetime.now()
|
||||||
starttime = time()
|
starttime = time()
|
||||||
print "Started at", start, "--", len(self.filelist), "files to import.", self.settings['dropIndexes']
|
log.info("Started at %s -- %d files to import. indexes: %s" % (start, len(self.filelist), self.settings['dropIndexes']))
|
||||||
if self.settings['dropIndexes'] == 'auto':
|
if self.settings['dropIndexes'] == 'auto':
|
||||||
self.settings['dropIndexes'] = self.calculate_auto2(self.database, 12.0, 500.0)
|
self.settings['dropIndexes'] = self.calculate_auto2(self.database, 12.0, 500.0)
|
||||||
if 'dropHudCache' in self.settings and self.settings['dropHudCache'] == 'auto':
|
if 'dropHudCache' in self.settings and self.settings['dropHudCache'] == 'auto':
|
||||||
|
@ -208,7 +212,7 @@ class Importer:
|
||||||
if self.settings['dropIndexes'] == 'drop':
|
if self.settings['dropIndexes'] == 'drop':
|
||||||
self.database.prepareBulkImport()
|
self.database.prepareBulkImport()
|
||||||
else:
|
else:
|
||||||
print "No need to drop indexes."
|
log.debug("No need to drop indexes.")
|
||||||
#print "dropInd =", self.settings['dropIndexes'], " dropHudCache =", self.settings['dropHudCache']
|
#print "dropInd =", self.settings['dropIndexes'], " dropHudCache =", self.settings['dropHudCache']
|
||||||
|
|
||||||
if self.settings['threads'] <= 0:
|
if self.settings['threads'] <= 0:
|
||||||
|
@ -380,12 +384,13 @@ class Importer:
|
||||||
conv = None
|
conv = None
|
||||||
(stored, duplicates, partial, errors, ttime) = (0, 0, 0, 0, 0)
|
(stored, duplicates, partial, errors, ttime) = (0, 0, 0, 0, 0)
|
||||||
|
|
||||||
|
file = file.decode(fpdb_simple.LOCALE_ENCODING)
|
||||||
|
|
||||||
# Load filter, process file, pass returned filename to import_fpdb_file
|
# Load filter, process file, pass returned filename to import_fpdb_file
|
||||||
|
|
||||||
if self.settings['threads'] > 0 and self.writeq != None:
|
if self.settings['threads'] > 0 and self.writeq != None:
|
||||||
print "\nConverting " + file + " (" + str(q.qsize()) + ")"
|
log.info("Converting " + file + " (" + str(q.qsize()) + ")")
|
||||||
else:
|
else:
|
||||||
print "\nConverting " + file
|
log.info("Converting " + file)
|
||||||
hhbase = self.config.get_import_parameters().get("hhArchiveBase")
|
hhbase = self.config.get_import_parameters().get("hhArchiveBase")
|
||||||
hhbase = os.path.expanduser(hhbase)
|
hhbase = os.path.expanduser(hhbase)
|
||||||
hhdir = os.path.join(hhbase,site)
|
hhdir = os.path.join(hhbase,site)
|
||||||
|
@ -413,10 +418,10 @@ class Importer:
|
||||||
else:
|
else:
|
||||||
# conversion didn't work
|
# conversion didn't work
|
||||||
# TODO: appropriate response?
|
# TODO: appropriate response?
|
||||||
return (0, 0, 0, 1, 0, -1)
|
return (0, 0, 0, 1, 0)
|
||||||
else:
|
else:
|
||||||
print "Unknown filter filter_name:'%s' in filter:'%s'" %(filter_name, filter)
|
log.warning("Unknown filter filter_name:'%s' in filter:'%s'" %(filter_name, filter))
|
||||||
return (0, 0, 0, 1, 0, -1)
|
return (0, 0, 0, 1, 0)
|
||||||
|
|
||||||
#This will barf if conv.getStatus != True
|
#This will barf if conv.getStatus != True
|
||||||
return (stored, duplicates, partial, errors, ttime)
|
return (stored, duplicates, partial, errors, ttime)
|
||||||
|
@ -458,7 +463,7 @@ class Importer:
|
||||||
db.commit()
|
db.commit()
|
||||||
ttime = time() - starttime
|
ttime = time() - starttime
|
||||||
if q == None:
|
if q == None:
|
||||||
print "\rTotal stored:", stored, " duplicates:", duplicates, "errors:", errors, " time:", ttime
|
log.info("Total stored: %(stored)d\tduplicates:%(duplicates)d\terrors:%(errors)d\ttime:%(ttime)s" % locals())
|
||||||
|
|
||||||
if not stored:
|
if not stored:
|
||||||
if duplicates:
|
if duplicates:
|
||||||
|
@ -533,7 +538,7 @@ class Importer:
|
||||||
if self.callHud:
|
if self.callHud:
|
||||||
#print "call to HUD here. handsId:",handsId
|
#print "call to HUD here. handsId:",handsId
|
||||||
#pipe the Hands.id out to the HUD
|
#pipe the Hands.id out to the HUD
|
||||||
print "sending hand to hud", handsId, "pipe =", self.caller.pipe_to_hud
|
#print "sending hand to hud", handsId, "pipe =", self.caller.pipe_to_hud
|
||||||
self.caller.pipe_to_hud.stdin.write("%s" % (handsId) + os.linesep)
|
self.caller.pipe_to_hud.stdin.write("%s" % (handsId) + os.linesep)
|
||||||
except fpdb_simple.DuplicateError:
|
except fpdb_simple.DuplicateError:
|
||||||
duplicates += 1
|
duplicates += 1
|
||||||
|
|
|
@ -22,6 +22,7 @@ import sys
|
||||||
import fpdb_simple
|
import fpdb_simple
|
||||||
import Database
|
import Database
|
||||||
from time import time, strftime
|
from time import time, strftime
|
||||||
|
from Exceptions import *
|
||||||
|
|
||||||
|
|
||||||
#parses a holdem hand
|
#parses a holdem hand
|
||||||
|
@ -67,7 +68,8 @@ def mainParser(settings, siteID, category, hand, config, db = None, writeq = Non
|
||||||
tourneyStartTime= handStartTime #todo: read tourney start time
|
tourneyStartTime= handStartTime #todo: read tourney start time
|
||||||
rebuyOrAddon = fpdb_simple.isRebuyOrAddon(hand[0])
|
rebuyOrAddon = fpdb_simple.isRebuyOrAddon(hand[0])
|
||||||
|
|
||||||
tourneyTypeId = fpdb_simple.recogniseTourneyTypeId(db.get_cursor(), siteID, buyin, fee, knockout, rebuyOrAddon)
|
## The tourney site id has to be searched because it may already be in db with a TourneyTypeId which is different from the one automatically calculated (Summary import first)
|
||||||
|
tourneyTypeId = fpdb_simple.recogniseTourneyTypeId(db, siteID, siteTourneyNo, buyin, fee, knockout, rebuyOrAddon)
|
||||||
else:
|
else:
|
||||||
siteTourneyNo = -1
|
siteTourneyNo = -1
|
||||||
buyin = -1
|
buyin = -1
|
||||||
|
@ -126,7 +128,7 @@ def mainParser(settings, siteID, category, hand, config, db = None, writeq = Non
|
||||||
elif lineTypes[i]=="table":
|
elif lineTypes[i]=="table":
|
||||||
tableResult=fpdb_simple.parseTableLine(base, line)
|
tableResult=fpdb_simple.parseTableLine(base, line)
|
||||||
else:
|
else:
|
||||||
raise fpdb_simple.FpdbError("unrecognised lineType:"+lineTypes[i])
|
raise FpdbError("unrecognised lineType:"+lineTypes[i])
|
||||||
|
|
||||||
maxSeats = tableResult['maxSeats']
|
maxSeats = tableResult['maxSeats']
|
||||||
tableName = tableResult['tableName']
|
tableName = tableResult['tableName']
|
||||||
|
|
|
@ -25,6 +25,8 @@ import datetime
|
||||||
import time
|
import time
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
from Exceptions import *
|
||||||
|
import locale
|
||||||
|
|
||||||
import Card
|
import Card
|
||||||
|
|
||||||
|
@ -37,18 +39,8 @@ MYSQL_INNODB = 2
|
||||||
PGSQL = 3
|
PGSQL = 3
|
||||||
SQLITE = 4
|
SQLITE = 4
|
||||||
|
|
||||||
class DuplicateError(Exception):
|
LOCALE_ENCODING = locale.getdefaultlocale()[1]
|
||||||
def __init__(self, value):
|
|
||||||
self.value = value
|
|
||||||
def __str__(self):
|
|
||||||
return repr(self.value)
|
|
||||||
|
|
||||||
class FpdbError(Exception):
|
|
||||||
def __init__(self, value):
|
|
||||||
self.value = value
|
|
||||||
def __str__(self):
|
|
||||||
return repr(self.value)
|
|
||||||
|
|
||||||
#returns an array of the total money paid. intending to add rebuys/addons here
|
#returns an array of the total money paid. intending to add rebuys/addons here
|
||||||
def calcPayin(count, buyin, fee):
|
def calcPayin(count, buyin, fee):
|
||||||
return [buyin + fee for i in xrange(count)]
|
return [buyin + fee for i in xrange(count)]
|
||||||
|
@ -224,7 +216,7 @@ def fillCardArrays(player_count, base, category, card_values, card_suits):
|
||||||
elif base=="stud":
|
elif base=="stud":
|
||||||
cardCount = 7
|
cardCount = 7
|
||||||
else:
|
else:
|
||||||
raise fpdb_simple.FpdbError("invalid category:", category)
|
raise FpdbError("invalid category:", category)
|
||||||
|
|
||||||
for i in xrange(player_count):
|
for i in xrange(player_count):
|
||||||
while (len(card_values[i]) < cardCount):
|
while (len(card_values[i]) < cardCount):
|
||||||
|
@ -543,7 +535,7 @@ def parseActionType(line):
|
||||||
#parses the ante out of the given line and checks which player paid it, updates antes accordingly.
|
#parses the ante out of the given line and checks which player paid it, updates antes accordingly.
|
||||||
def parseAnteLine(line, isTourney, names, antes):
|
def parseAnteLine(line, isTourney, names, antes):
|
||||||
for i, name in enumerate(names):
|
for i, name in enumerate(names):
|
||||||
if line.startswith(name.encode("latin-1")):
|
if line.startswith(name.encode(LOCALE_ENCODING)):
|
||||||
pos = line.rfind("$") + 1
|
pos = line.rfind("$") + 1
|
||||||
if not isTourney:
|
if not isTourney:
|
||||||
antes[i] += float2int(line[pos:])
|
antes[i] += float2int(line[pos:])
|
||||||
|
@ -705,7 +697,7 @@ def parseHandStartTime(topline):
|
||||||
def findName(line):
|
def findName(line):
|
||||||
pos1 = line.find(":") + 2
|
pos1 = line.find(":") + 2
|
||||||
pos2 = line.rfind("(") - 1
|
pos2 = line.rfind("(") - 1
|
||||||
return unicode(line[pos1:pos2], "latin-1")
|
return unicode(line[pos1:pos2], LOCALE_ENCODING)
|
||||||
|
|
||||||
def parseNames(lines):
|
def parseNames(lines):
|
||||||
return [findName(line) for line in lines]
|
return [findName(line) for line in lines]
|
||||||
|
@ -822,7 +814,7 @@ def parseTourneyNo(topline):
|
||||||
def parseWinLine(line, names, winnings, isTourney):
|
def parseWinLine(line, names, winnings, isTourney):
|
||||||
#print "parseWinLine: line:",line
|
#print "parseWinLine: line:",line
|
||||||
for i,n in enumerate(names):
|
for i,n in enumerate(names):
|
||||||
n = n.encode("latin-1")
|
n = n.encode(LOCALE_ENCODING)
|
||||||
if line.startswith(n):
|
if line.startswith(n):
|
||||||
if isTourney:
|
if isTourney:
|
||||||
pos1 = line.rfind("collected ") + 10
|
pos1 = line.rfind("collected ") + 10
|
||||||
|
@ -951,17 +943,28 @@ def recogniseGametypeID(backend, db, cursor, topline, smallBlindLine, site_id, c
|
||||||
return result[0]
|
return result[0]
|
||||||
#end def recogniseGametypeID
|
#end def recogniseGametypeID
|
||||||
|
|
||||||
def recogniseTourneyTypeId(cursor, siteId, buyin, fee, knockout, rebuyOrAddon):
|
def recogniseTourneyTypeId(db, siteId, tourneySiteId, buyin, fee, knockout, rebuyOrAddon):
|
||||||
cursor.execute ("SELECT id FROM TourneyTypes WHERE siteId=%s AND buyin=%s AND fee=%s AND knockout=%s AND rebuyOrAddon=%s", (siteId, buyin, fee, knockout, rebuyOrAddon))
|
cursor = db.get_cursor()
|
||||||
|
# First we try to find the tourney itself (by its tourneySiteId) in case it has already been inserted before (by a summary file for instance)
|
||||||
|
# The reason is that some tourneys may not be identified correctly in the HH toplines (especially Buy-In and Fee which are used to search/create the TourneyTypeId)
|
||||||
|
#TODO: When the summary file will be dumped to BD, if the tourney is already in, Buy-In/Fee may need an update (e.g. creation of a new type and link to the Tourney)
|
||||||
|
cursor.execute (db.sql.query['getTourneyTypeIdByTourneyNo'].replace('%s', db.sql.query['placeholder']), (tourneySiteId, siteId))
|
||||||
result=cursor.fetchone()
|
result=cursor.fetchone()
|
||||||
#print "tried SELECTing gametypes.id, result:",result
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
len(result)
|
len(result)
|
||||||
except TypeError:#this means we need to create a new entry
|
except:
|
||||||
cursor.execute("""INSERT INTO TourneyTypes (siteId, buyin, fee, knockout, rebuyOrAddon) VALUES (%s, %s, %s, %s, %s)""", (siteId, buyin, fee, knockout, rebuyOrAddon))
|
cursor.execute ("SELECT id FROM TourneyTypes WHERE siteId=%s AND buyin=%s AND fee=%s AND knockout=%s AND rebuyOrAddon=%s", (siteId, buyin, fee, knockout, rebuyOrAddon))
|
||||||
cursor.execute("SELECT id FROM TourneyTypes WHERE siteId=%s AND buyin=%s AND fee=%s AND knockout=%s AND rebuyOrAddon=%s", (siteId, buyin, fee, knockout, rebuyOrAddon))
|
|
||||||
result=cursor.fetchone()
|
result=cursor.fetchone()
|
||||||
|
#print "tried SELECTing gametypes.id, result:",result
|
||||||
|
|
||||||
|
try:
|
||||||
|
len(result)
|
||||||
|
except TypeError:#this means we need to create a new entry
|
||||||
|
cursor.execute("""INSERT INTO TourneyTypes (siteId, buyin, fee, knockout, rebuyOrAddon) VALUES (%s, %s, %s, %s, %s)""", (siteId, buyin, fee, knockout, rebuyOrAddon))
|
||||||
|
cursor.execute("SELECT id FROM TourneyTypes WHERE siteId=%s AND buyin=%s AND fee=%s AND knockout=%s AND rebuyOrAddon=%s", (siteId, buyin, fee, knockout, rebuyOrAddon))
|
||||||
|
result=cursor.fetchone()
|
||||||
|
|
||||||
return result[0]
|
return result[0]
|
||||||
#end def recogniseTourneyTypeId
|
#end def recogniseTourneyTypeId
|
||||||
|
|
||||||
|
@ -985,17 +988,17 @@ def recogniseTourneyTypeId(cursor, siteId, buyin, fee, knockout, rebuyOrAddon):
|
||||||
# return result
|
# return result
|
||||||
|
|
||||||
def recognisePlayerIDs(db, names, site_id):
|
def recognisePlayerIDs(db, names, site_id):
|
||||||
q = "SELECT name,id FROM Players WHERE name=" + " OR name=".join([db.sql.query['placeholder'] for n in names])
|
|
||||||
c = db.get_cursor()
|
c = db.get_cursor()
|
||||||
|
q = "SELECT name,id FROM Players WHERE siteid=%d and (name=%s)" %(site_id, " OR name=".join([db.sql.query['placeholder'] for n in names]))
|
||||||
c.execute(q, names) # get all playerids by the names passed in
|
c.execute(q, names) # get all playerids by the names passed in
|
||||||
ids = dict(c.fetchall()) # convert to dict
|
ids = dict(c.fetchall()) # convert to dict
|
||||||
if len(ids) != len(names):
|
if len(ids) != len(names):
|
||||||
notfound = [n for n in names if n not in ids] # make list of names not in database
|
notfound = [n for n in names if n not in ids] # make list of names not in database
|
||||||
if notfound: # insert them into database
|
if notfound: # insert them into database
|
||||||
#q_ins = "INSERT INTO Players (name, siteId) VALUES (%s, "+str(site_id)+")"
|
q_ins = "INSERT INTO Players (name, siteId) VALUES (%s, "+str(site_id)+")"
|
||||||
#q_ins = q_ins.replace('%s', db.sql.query['placeholder'])
|
q_ins = q_ins.replace('%s', db.sql.query['placeholder'])
|
||||||
c.executemany("INSERT INTO Players (name, siteId) VALUES (%s, "+str(site_id)+")", [(n,) for n in notfound])
|
c.executemany(q_ins, [(n,) for n in notfound])
|
||||||
q2 = "SELECT name,id FROM Players WHERE name=%s" % " OR name=".join(["%s" for n in notfound])
|
q2 = "SELECT name,id FROM Players WHERE siteid=%d and (name=%s)" % (site_id, " OR name=".join(["%s" for n in notfound]))
|
||||||
q2 = q2.replace('%s', db.sql.query['placeholder'])
|
q2 = q2.replace('%s', db.sql.query['placeholder'])
|
||||||
c.execute(q2, notfound) # get their new ids
|
c.execute(q2, notfound) # get their new ids
|
||||||
tmp = c.fetchall()
|
tmp = c.fetchall()
|
||||||
|
@ -1032,14 +1035,15 @@ def recognisePlayerIDs(db, names, site_id):
|
||||||
def recognisePlayerNo(line, names, atype):
|
def recognisePlayerNo(line, names, atype):
|
||||||
#print "recogniseplayerno, names:",names
|
#print "recogniseplayerno, names:",names
|
||||||
for i in xrange(len(names)):
|
for i in xrange(len(names)):
|
||||||
|
encodedName = names[i].encode(LOCALE_ENCODING)
|
||||||
if (atype=="unbet"):
|
if (atype=="unbet"):
|
||||||
if (line.endswith(names[i].encode("latin-1"))):
|
if (line.endswith(encodedName)):
|
||||||
return (i)
|
return (i)
|
||||||
elif (line.startswith("Dealt to ")):
|
elif (line.startswith("Dealt to ")):
|
||||||
#print "recognisePlayerNo, card precut, line:",line
|
#print "recognisePlayerNo, card precut, line:",line
|
||||||
tmp=line[9:]
|
tmp=line[9:]
|
||||||
#print "recognisePlayerNo, card postcut, tmp:",tmp
|
#print "recognisePlayerNo, card postcut, tmp:",tmp
|
||||||
if (tmp.startswith(names[i].encode("latin-1"))):
|
if (tmp.startswith(encodedName)):
|
||||||
return (i)
|
return (i)
|
||||||
elif (line.startswith("Seat ")):
|
elif (line.startswith("Seat ")):
|
||||||
if (line.startswith("Seat 10")):
|
if (line.startswith("Seat 10")):
|
||||||
|
@ -1047,10 +1051,10 @@ def recognisePlayerNo(line, names, atype):
|
||||||
else:
|
else:
|
||||||
tmp=line[8:]
|
tmp=line[8:]
|
||||||
|
|
||||||
if (tmp.startswith(names[i].encode("latin-1"))):
|
if (tmp.startswith(encodedName)):
|
||||||
return (i)
|
return (i)
|
||||||
else:
|
else:
|
||||||
if (line.startswith(names[i].encode("latin-1"))):
|
if (line.startswith(encodedName)):
|
||||||
return (i)
|
return (i)
|
||||||
#if we're here we mustve failed
|
#if we're here we mustve failed
|
||||||
raise FpdbError ("failed to recognise player in: "+line+" atype:"+atype)
|
raise FpdbError ("failed to recognise player in: "+line+" atype:"+atype)
|
||||||
|
|
|
@ -1,271 +1,271 @@
|
||||||
|
|
||||||
# Code from http://ender.snowburst.org:4747/~jjohns/interlocks.py
|
# Code from http://ender.snowburst.org:4747/~jjohns/interlocks.py
|
||||||
# Thanks JJ!
|
# Thanks JJ!
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os, os.path
|
import os, os.path
|
||||||
import subprocess
|
import subprocess
|
||||||
import time
|
import time
|
||||||
import signal
|
import signal
|
||||||
import base64
|
import base64
|
||||||
|
|
||||||
InterProcessLock = None
|
InterProcessLock = None
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Just use me like a thread lock. acquire() / release() / locked()
|
Just use me like a thread lock. acquire() / release() / locked()
|
||||||
|
|
||||||
Differences compared to thread locks:
|
Differences compared to thread locks:
|
||||||
1. By default, acquire()'s wait parameter is false.
|
1. By default, acquire()'s wait parameter is false.
|
||||||
2. When acquire fails, SingleInstanceError is thrown instead of simply returning false.
|
2. When acquire fails, SingleInstanceError is thrown instead of simply returning false.
|
||||||
3. acquire() can take a 3rd parameter retry_time, which, if wait is True, tells the locking
|
3. acquire() can take a 3rd parameter retry_time, which, if wait is True, tells the locking
|
||||||
mechanism how long to sleep between retrying the lock. Has no effect for unix/InterProcessLockFcntl.
|
mechanism how long to sleep between retrying the lock. Has no effect for unix/InterProcessLockFcntl.
|
||||||
|
|
||||||
Differences in fpdb version to JJ's original:
|
Differences in fpdb version to JJ's original:
|
||||||
1. Changed acquire() to return false like other locks
|
1. Changed acquire() to return false like other locks
|
||||||
2. Made acquire fail if same process already has the lock
|
2. Made acquire fail if same process already has the lock
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class SingleInstanceError(RuntimeError):
|
class SingleInstanceError(RuntimeError):
|
||||||
"Thrown when you try to acquire an InterProcessLock and another version of the process is already running."
|
"Thrown when you try to acquire an InterProcessLock and another version of the process is already running."
|
||||||
|
|
||||||
class InterProcessLockBase:
|
class InterProcessLockBase:
|
||||||
def __init__(self, name=None ):
|
def __init__(self, name=None ):
|
||||||
self._has_lock = False
|
self._has_lock = False
|
||||||
if not name:
|
if not name:
|
||||||
name = sys.argv[0]
|
name = sys.argv[0]
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
def getHashedName(self):
|
def getHashedName(self):
|
||||||
return base64.b64encode(self.name).replace('=','')
|
return base64.b64encode(self.name).replace('=','')
|
||||||
|
|
||||||
def acquire_impl(self, wait): abstract
|
def acquire_impl(self, wait): abstract
|
||||||
|
|
||||||
def acquire(self, wait=False, retry_time=1):
|
def acquire(self, wait=False, retry_time=1):
|
||||||
if self._has_lock: # make sure 2nd acquire in same process fails
|
if self._has_lock: # make sure 2nd acquire in same process fails
|
||||||
return False
|
return False
|
||||||
while not self._has_lock:
|
while not self._has_lock:
|
||||||
try:
|
try:
|
||||||
self.acquire_impl(wait)
|
self.acquire_impl(wait)
|
||||||
self._has_lock = True
|
self._has_lock = True
|
||||||
#print 'i have the lock'
|
#print 'i have the lock'
|
||||||
except SingleInstanceError:
|
except SingleInstanceError:
|
||||||
if not wait:
|
if not wait:
|
||||||
# raise # change back to normal acquire functionality, sorry JJ!
|
# raise # change back to normal acquire functionality, sorry JJ!
|
||||||
return False
|
return False
|
||||||
time.sleep(retry_time)
|
time.sleep(retry_time)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def release(self):
|
def release(self):
|
||||||
self.release_impl()
|
self.release_impl()
|
||||||
self._has_lock = False
|
self._has_lock = False
|
||||||
|
|
||||||
def locked(self):
|
def locked(self):
|
||||||
if self._has_lock:
|
if self._has_lock:
|
||||||
return True
|
return True
|
||||||
try:
|
try:
|
||||||
self.acquire()
|
self.acquire()
|
||||||
self.release()
|
self.release()
|
||||||
return False
|
return False
|
||||||
except SingleInstanceError:
|
except SingleInstanceError:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
LOCK_FILE_DIRECTORY = '/tmp'
|
LOCK_FILE_DIRECTORY = '/tmp'
|
||||||
|
|
||||||
class InterProcessLockFcntl(InterProcessLockBase):
|
class InterProcessLockFcntl(InterProcessLockBase):
|
||||||
def __init__(self, name=None):
|
def __init__(self, name=None):
|
||||||
InterProcessLockBase.__init__(self, name)
|
InterProcessLockBase.__init__(self, name)
|
||||||
self.lockfd = 0
|
self.lockfd = 0
|
||||||
self.lock_file_name = os.path.join(LOCK_FILE_DIRECTORY, self.getHashedName() + '.lck')
|
self.lock_file_name = os.path.join(LOCK_FILE_DIRECTORY, self.getHashedName() + '.lck')
|
||||||
assert(os.path.isdir(LOCK_FILE_DIRECTORY))
|
assert(os.path.isdir(LOCK_FILE_DIRECTORY))
|
||||||
|
|
||||||
# This is the suggested way to get a safe file name, but I like having a descriptively named lock file.
|
# This is the suggested way to get a safe file name, but I like having a descriptively named lock file.
|
||||||
def getHashedName(self):
|
def getHashedName(self):
|
||||||
import re
|
import re
|
||||||
bad_filename_character_re = re.compile(r'/\?<>\\\:;\*\|\'\"\^=\.\[\]')
|
bad_filename_character_re = re.compile(r'/\?<>\\\:;\*\|\'\"\^=\.\[\]')
|
||||||
return bad_filename_character_re.sub('_',self.name)
|
return bad_filename_character_re.sub('_',self.name)
|
||||||
|
|
||||||
def acquire_impl(self, wait):
|
def acquire_impl(self, wait):
|
||||||
self.lockfd = open(self.lock_file_name, 'w')
|
self.lockfd = open(self.lock_file_name, 'w')
|
||||||
fcntrl_options = fcntl.LOCK_EX
|
fcntrl_options = fcntl.LOCK_EX
|
||||||
if not wait:
|
if not wait:
|
||||||
fcntrl_options |= fcntl.LOCK_NB
|
fcntrl_options |= fcntl.LOCK_NB
|
||||||
try:
|
try:
|
||||||
fcntl.flock(self.lockfd, fcntrl_options)
|
fcntl.flock(self.lockfd, fcntrl_options)
|
||||||
except IOError:
|
except IOError:
|
||||||
self.lockfd.close()
|
self.lockfd.close()
|
||||||
self.lockfd = 0
|
self.lockfd = 0
|
||||||
raise SingleInstanceError('Could not acquire exclusive lock on '+self.lock_file_name)
|
raise SingleInstanceError('Could not acquire exclusive lock on '+self.lock_file_name)
|
||||||
|
|
||||||
def release_impl(self):
|
def release_impl(self):
|
||||||
fcntl.lockf(self.lockfd, fcntl.LOCK_UN)
|
fcntl.lockf(self.lockfd, fcntl.LOCK_UN)
|
||||||
self.lockfd.close()
|
self.lockfd.close()
|
||||||
self.lockfd = 0
|
self.lockfd = 0
|
||||||
try:
|
try:
|
||||||
os.unlink(self.lock_file_name)
|
os.unlink(self.lock_file_name)
|
||||||
except IOError:
|
except IOError:
|
||||||
# We don't care about the existence of the file too much here. It's the flock() we care about,
|
# We don't care about the existence of the file too much here. It's the flock() we care about,
|
||||||
# And that should just go away magically.
|
# And that should just go away magically.
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class InterProcessLockWin32(InterProcessLockBase):
|
class InterProcessLockWin32(InterProcessLockBase):
|
||||||
def __init__(self, name=None):
|
def __init__(self, name=None):
|
||||||
InterProcessLockBase.__init__(self, name)
|
InterProcessLockBase.__init__(self, name)
|
||||||
self.mutex = None
|
self.mutex = None
|
||||||
|
|
||||||
def acquire_impl(self,wait):
|
def acquire_impl(self,wait):
|
||||||
self.mutex = win32event.CreateMutex(None, 0, self.getHashedName())
|
self.mutex = win32event.CreateMutex(None, 0, self.getHashedName())
|
||||||
if win32api.GetLastError() == winerror.ERROR_ALREADY_EXISTS:
|
if win32api.GetLastError() == winerror.ERROR_ALREADY_EXISTS:
|
||||||
self.mutex.Close()
|
self.mutex.Close()
|
||||||
self.mutex = None
|
self.mutex = None
|
||||||
raise SingleInstanceError('Could not acquire exclusive lock on ' + self.name)
|
raise SingleInstanceError('Could not acquire exclusive lock on ' + self.name)
|
||||||
|
|
||||||
def release_impl(self):
|
def release_impl(self):
|
||||||
self.mutex.Close()
|
self.mutex.Close()
|
||||||
|
|
||||||
class InterProcessLockSocket(InterProcessLockBase):
|
class InterProcessLockSocket(InterProcessLockBase):
|
||||||
def __init__(self, name=None):
|
def __init__(self, name=None):
|
||||||
InterProcessLockBase.__init__(self, name)
|
InterProcessLockBase.__init__(self, name)
|
||||||
self.socket = None
|
self.socket = None
|
||||||
self.portno = 65530 - abs(self.getHashedName().__hash__()) % 32749
|
self.portno = 65530 - abs(self.getHashedName().__hash__()) % 32749
|
||||||
|
|
||||||
def acquire_impl(self, wait):
|
def acquire_impl(self, wait):
|
||||||
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
try:
|
try:
|
||||||
self.socket.bind(('127.0.0.1', self.portno))
|
self.socket.bind(('127.0.0.1', self.portno))
|
||||||
except socket.error:
|
except socket.error:
|
||||||
self.socket.close()
|
self.socket.close()
|
||||||
self.socket = None
|
self.socket = None
|
||||||
raise SingleInstanceError('Could not acquire exclusive lock on ' + self.name)
|
raise SingleInstanceError('Could not acquire exclusive lock on ' + self.name)
|
||||||
|
|
||||||
def release_impl(self):
|
def release_impl(self):
|
||||||
self.socket.close()
|
self.socket.close()
|
||||||
self.socket = None
|
self.socket = None
|
||||||
|
|
||||||
# Set InterProcessLock to the correct type given the sysem parameters available
|
# Set InterProcessLock to the correct type given the sysem parameters available
|
||||||
try:
|
try:
|
||||||
import fcntl
|
import fcntl
|
||||||
InterProcessLock = InterProcessLockFcntl
|
InterProcessLock = InterProcessLockFcntl
|
||||||
except ImportError:
|
except ImportError:
|
||||||
try:
|
try:
|
||||||
import win32event
|
import win32event
|
||||||
import win32api
|
import win32api
|
||||||
import winerror
|
import winerror
|
||||||
InterProcessLock = InterProcessLockWin32
|
InterProcessLock = InterProcessLockWin32
|
||||||
except ImportError:
|
except ImportError:
|
||||||
import socket
|
import socket
|
||||||
InterProcessLock = InterProcessLockSocket
|
InterProcessLock = InterProcessLockSocket
|
||||||
|
|
||||||
def test_construct():
|
def test_construct():
|
||||||
"""
|
"""
|
||||||
# Making the name of the test unique so it can be executed my multiple users on the same machine.
|
# Making the name of the test unique so it can be executed my multiple users on the same machine.
|
||||||
>>> test_name = 'InterProcessLockTest' +str(os.getpid()) + str(time.time())
|
>>> test_name = 'InterProcessLockTest' +str(os.getpid()) + str(time.time())
|
||||||
|
|
||||||
>>> lock1 = InterProcessLock(name=test_name)
|
>>> lock1 = InterProcessLock(name=test_name)
|
||||||
>>> lock1.acquire()
|
>>> lock1.acquire()
|
||||||
|
|
||||||
>>> lock2 = InterProcessLock(name=test_name)
|
>>> lock2 = InterProcessLock(name=test_name)
|
||||||
>>> lock3 = InterProcessLock(name=test_name)
|
>>> lock3 = InterProcessLock(name=test_name)
|
||||||
|
|
||||||
# Since lock1 is locked, other attempts to acquire it fail.
|
# Since lock1 is locked, other attempts to acquire it fail.
|
||||||
>>> lock2.acquire()
|
>>> lock2.acquire()
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
SingleInstanceError: Could not acquire exclusive lock on /tmp/test.lck
|
SingleInstanceError: Could not acquire exclusive lock on /tmp/test.lck
|
||||||
|
|
||||||
>>> lock3.acquire()
|
>>> lock3.acquire()
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
SingleInstanceError: Could not acquire exclusive lock on /tmp/test.lck
|
SingleInstanceError: Could not acquire exclusive lock on /tmp/test.lck
|
||||||
|
|
||||||
# Release the lock and let lock2 have it.
|
# Release the lock and let lock2 have it.
|
||||||
>>> lock1.release()
|
>>> lock1.release()
|
||||||
>>> lock2.acquire()
|
>>> lock2.acquire()
|
||||||
|
|
||||||
>>> lock3.acquire()
|
>>> lock3.acquire()
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
SingleInstanceError: Could not acquire exclusive lock on /tmp/test.lck
|
SingleInstanceError: Could not acquire exclusive lock on /tmp/test.lck
|
||||||
|
|
||||||
# Release it and give it back to lock1
|
# Release it and give it back to lock1
|
||||||
>>> lock2.release()
|
>>> lock2.release()
|
||||||
>>> lock1.acquire()
|
>>> lock1.acquire()
|
||||||
|
|
||||||
>>> lock2.acquire()
|
>>> lock2.acquire()
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
SingleInstanceError: Could not acquire exclusive lock on /tmp/test.lck
|
SingleInstanceError: Could not acquire exclusive lock on /tmp/test.lck
|
||||||
|
|
||||||
# Test lock status
|
# Test lock status
|
||||||
>>> lock2.locked()
|
>>> lock2.locked()
|
||||||
True
|
True
|
||||||
>>> lock3.locked()
|
>>> lock3.locked()
|
||||||
True
|
True
|
||||||
>>> lock1.locked()
|
>>> lock1.locked()
|
||||||
True
|
True
|
||||||
|
|
||||||
>>> lock1.release()
|
>>> lock1.release()
|
||||||
|
|
||||||
>>> lock2.locked()
|
>>> lock2.locked()
|
||||||
False
|
False
|
||||||
>>> lock3.locked()
|
>>> lock3.locked()
|
||||||
False
|
False
|
||||||
>>> lock1.locked()
|
>>> lock1.locked()
|
||||||
False
|
False
|
||||||
|
|
||||||
>>> if os.name == 'posix':
|
>>> if os.name == 'posix':
|
||||||
... def os_independent_kill(pid):
|
... def os_independent_kill(pid):
|
||||||
... import signal
|
... import signal
|
||||||
... os.kill(pid, signal.SIGKILL)
|
... os.kill(pid, signal.SIGKILL)
|
||||||
... else:
|
... else:
|
||||||
... assert(os.name == 'nt')
|
... assert(os.name == 'nt')
|
||||||
... def os_independent_kill(pid):
|
... def os_independent_kill(pid):
|
||||||
... ''' http://www.python.org/doc/faq/windows/#how-do-i-emulate-os-kill-in-windows '''
|
... ''' http://www.python.org/doc/faq/windows/#how-do-i-emulate-os-kill-in-windows '''
|
||||||
... import win32api
|
... import win32api
|
||||||
... import win32con
|
... import win32con
|
||||||
... import pywintypes
|
... import pywintypes
|
||||||
... handle = win32api.OpenProcess(win32con.PROCESS_TERMINATE , pywintypes.FALSE, pid)
|
... handle = win32api.OpenProcess(win32con.PROCESS_TERMINATE , pywintypes.FALSE, pid)
|
||||||
... return (0 != win32api.TerminateProcess(handle, 0))
|
... return (0 != win32api.TerminateProcess(handle, 0))
|
||||||
|
|
||||||
# Test to acquire the lock in another process.
|
# Test to acquire the lock in another process.
|
||||||
>>> def execute(cmd):
|
>>> def execute(cmd):
|
||||||
... cmd = 'import time;' + cmd + 'time.sleep(10);'
|
... cmd = 'import time;' + cmd + 'time.sleep(10);'
|
||||||
... process = subprocess.Popen([sys.executable, '-c', cmd])
|
... process = subprocess.Popen([sys.executable, '-c', cmd])
|
||||||
... pid = process.pid
|
... pid = process.pid
|
||||||
... time.sleep(2) # quick hack, but we test synchronization in the end
|
... time.sleep(2) # quick hack, but we test synchronization in the end
|
||||||
... return pid
|
... return pid
|
||||||
|
|
||||||
>>> pid = execute('import interlocks;a=interlocks.InterProcessLock(name=\\''+test_name+ '\\');a.acquire();')
|
>>> pid = execute('import interlocks;a=interlocks.InterProcessLock(name=\\''+test_name+ '\\');a.acquire();')
|
||||||
|
|
||||||
>>> lock1.acquire()
|
>>> lock1.acquire()
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
SingleInstanceError: Could not acquire exclusive lock on /tmp/test.lck
|
SingleInstanceError: Could not acquire exclusive lock on /tmp/test.lck
|
||||||
|
|
||||||
>>> os_independent_kill(pid)
|
>>> os_independent_kill(pid)
|
||||||
|
|
||||||
>>> time.sleep(1)
|
>>> time.sleep(1)
|
||||||
|
|
||||||
>>> lock1.acquire()
|
>>> lock1.acquire()
|
||||||
>>> lock1.release()
|
>>> lock1.release()
|
||||||
|
|
||||||
# Testing wait
|
# Testing wait
|
||||||
|
|
||||||
>>> pid = execute('import interlocks;a=interlocks.InterProcessLock(name=\\''+test_name+ '\\');a.acquire();')
|
>>> pid = execute('import interlocks;a=interlocks.InterProcessLock(name=\\''+test_name+ '\\');a.acquire();')
|
||||||
|
|
||||||
>>> lock1.acquire()
|
>>> lock1.acquire()
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
SingleInstanceError: Could not acquire exclusive lock on /tmp/test.lck
|
SingleInstanceError: Could not acquire exclusive lock on /tmp/test.lck
|
||||||
|
|
||||||
>>> os_independent_kill(pid)
|
>>> os_independent_kill(pid)
|
||||||
|
|
||||||
>>> lock1.acquire(True)
|
>>> lock1.acquire(True)
|
||||||
>>> lock1.release()
|
>>> lock1.release()
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if __name__=='__main__':
|
if __name__=='__main__':
|
||||||
import doctest
|
import doctest
|
||||||
doctest.testmod(optionflags=doctest.IGNORE_EXCEPTION_DETAIL)
|
doctest.testmod(optionflags=doctest.IGNORE_EXCEPTION_DETAIL)
|
||||||
|
|
57
pyfpdb/logging.conf
Normal file
57
pyfpdb/logging.conf
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
[loggers]
|
||||||
|
keys=root,parser,importer,config,db
|
||||||
|
|
||||||
|
[handlers]
|
||||||
|
keys=consoleHandler,fileHandler
|
||||||
|
|
||||||
|
[formatters]
|
||||||
|
keys=fileFormatter,stderrFormatter
|
||||||
|
|
||||||
|
[logger_root]
|
||||||
|
level=INFO
|
||||||
|
handlers=consoleHandler,fileHandler
|
||||||
|
|
||||||
|
[logger_parser]
|
||||||
|
level=INFO
|
||||||
|
handlers=consoleHandler,fileHandler
|
||||||
|
qualname=parser
|
||||||
|
propagate=0
|
||||||
|
|
||||||
|
[logger_importer]
|
||||||
|
level=DEBUG
|
||||||
|
handlers=consoleHandler,fileHandler
|
||||||
|
qualname=importer
|
||||||
|
propagate=0
|
||||||
|
|
||||||
|
[logger_config]
|
||||||
|
level=DEBUG
|
||||||
|
handlers=consoleHandler,fileHandler
|
||||||
|
qualname=config
|
||||||
|
propagate=0
|
||||||
|
|
||||||
|
[logger_db]
|
||||||
|
level=DEBUG
|
||||||
|
handlers=consoleHandler,fileHandler
|
||||||
|
qualname=db
|
||||||
|
propagate=0
|
||||||
|
|
||||||
|
[handler_consoleHandler]
|
||||||
|
class=StreamHandler
|
||||||
|
level=DEBUG
|
||||||
|
formatter=stderrFormatter
|
||||||
|
args=(sys.stderr,)
|
||||||
|
|
||||||
|
[handler_fileHandler]
|
||||||
|
class=FileHandler
|
||||||
|
level=DEBUG
|
||||||
|
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=
|
54
pyfpdb/py2exe_setup.py
Normal file
54
pyfpdb/py2exe_setup.py
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""setup.py
|
||||||
|
|
||||||
|
Py2exe script for fpdb.
|
||||||
|
"""
|
||||||
|
# Copyright 2009, Ray E. Barker
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
|
||||||
|
#TODO: change GuiAutoImport so that it knows to start HUD_main.exe, when appropriate
|
||||||
|
# include the lib needed to handle png files in mucked
|
||||||
|
# get rid of all the uneeded libraries (e.g., pyQT)
|
||||||
|
# think about an installer
|
||||||
|
|
||||||
|
from distutils.core import setup
|
||||||
|
import py2exe
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name = 'fpdb',
|
||||||
|
description = 'Free Poker DataBase',
|
||||||
|
version = '0.12',
|
||||||
|
|
||||||
|
console = [ {'script': 'fpdb.py', },
|
||||||
|
{'script': 'HUD_main.py', }
|
||||||
|
],
|
||||||
|
|
||||||
|
options = {'py2exe': {
|
||||||
|
'packages' :'encodings',
|
||||||
|
'includes' : 'cairo, pango, pangocairo, atk, gobject, PokerStarsToFpdb',
|
||||||
|
'excludes' : '_tkagg, _agg2, cocoaagg, fltkagg',
|
||||||
|
'dll_excludes': 'libglade-2.0-0.dll',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data_files = ['HUD_config.xml',
|
||||||
|
'Cards01.png'
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
|
@ -4,7 +4,12 @@ import py
|
||||||
|
|
||||||
# regression-test-files/fulltilt/nlhe/NLHE-6max-1.txt
|
# regression-test-files/fulltilt/nlhe/NLHE-6max-1.txt
|
||||||
# Sorrowful: start: $8.85 end: $14.70 total: $5.85
|
# Sorrowful: start: $8.85 end: $14.70 total: $5.85
|
||||||
|
|
||||||
|
# 'Canceled' hand
|
||||||
|
# regression-test-files/fulltilt/lh/Marlin.txt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def checkGameInfo(hhc, header, info):
|
def checkGameInfo(hhc, header, info):
|
||||||
assert hhc.determineGameType(header) == info
|
assert hhc.determineGameType(header) == info
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ text = ""
|
||||||
|
|
||||||
hhc = PokerStarsToFpdb.PokerStars(autostart=False)
|
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")
|
h.addPlayer("1", "s0rrow", "100000")
|
||||||
|
|
||||||
hhc.compilePlayerRegexs(h)
|
hhc.compilePlayerRegexs(h)
|
||||||
|
|
|
@ -1,27 +1,27 @@
|
||||||
|
|
||||||
# script to update indexes on mysql (+other?) database
|
# script to update indexes on mysql (+other?) database
|
||||||
|
|
||||||
select '1. Dropping indexes' as ' ';
|
select '1. Dropping indexes' as ' ';
|
||||||
select 'Can''t drop messages indicate index already gone' as ' ';
|
select 'Can''t drop messages indicate index already gone' as ' ';
|
||||||
|
|
||||||
ALTER TABLE `fpdb`.`Settings` DROP INDEX `id`;
|
ALTER TABLE `fpdb`.`Settings` DROP INDEX `id`;
|
||||||
ALTER TABLE `fpdb`.`Sites` DROP INDEX `id`;
|
ALTER TABLE `fpdb`.`Sites` DROP INDEX `id`;
|
||||||
ALTER TABLE `fpdb`.`Gametypes` DROP INDEX `id`;
|
ALTER TABLE `fpdb`.`Gametypes` DROP INDEX `id`;
|
||||||
ALTER TABLE `fpdb`.`Players` DROP INDEX `id`;
|
ALTER TABLE `fpdb`.`Players` DROP INDEX `id`;
|
||||||
ALTER TABLE `fpdb`.`Autorates` DROP INDEX `id`;
|
ALTER TABLE `fpdb`.`Autorates` DROP INDEX `id`;
|
||||||
ALTER TABLE `fpdb`.`Hands` DROP INDEX `id`;
|
ALTER TABLE `fpdb`.`Hands` DROP INDEX `id`;
|
||||||
ALTER TABLE `fpdb`.`BoardCards` DROP INDEX `id`;
|
ALTER TABLE `fpdb`.`BoardCards` DROP INDEX `id`;
|
||||||
ALTER TABLE `fpdb`.`TourneyTypes` DROP INDEX `id`;
|
ALTER TABLE `fpdb`.`TourneyTypes` DROP INDEX `id`;
|
||||||
ALTER TABLE `fpdb`.`Tourneys` DROP INDEX `id`;
|
ALTER TABLE `fpdb`.`Tourneys` DROP INDEX `id`;
|
||||||
ALTER TABLE `fpdb`.`TourneysPlayers` DROP INDEX `id`;
|
ALTER TABLE `fpdb`.`TourneysPlayers` DROP INDEX `id`;
|
||||||
ALTER TABLE `fpdb`.`HandsPlayers` DROP INDEX `id`;
|
ALTER TABLE `fpdb`.`HandsPlayers` DROP INDEX `id`;
|
||||||
ALTER TABLE `fpdb`.`HandsActions` DROP INDEX `id`;
|
ALTER TABLE `fpdb`.`HandsActions` DROP INDEX `id`;
|
||||||
ALTER TABLE `fpdb`.`HudCache` DROP INDEX `id`;
|
ALTER TABLE `fpdb`.`HudCache` DROP INDEX `id`;
|
||||||
|
|
||||||
select '2. Adding extra indexes on useful fields' as ' ';
|
select '2. Adding extra indexes on useful fields' as ' ';
|
||||||
select 'Duplicate key name messages indicate new indexes already there' as ' ';
|
select 'Duplicate key name messages indicate new indexes already there' as ' ';
|
||||||
|
|
||||||
ALTER TABLE `fpdb`.`tourneys` ADD INDEX `siteTourneyNo`(`siteTourneyNo`);
|
ALTER TABLE `fpdb`.`tourneys` ADD INDEX `siteTourneyNo`(`siteTourneyNo`);
|
||||||
ALTER TABLE `fpdb`.`hands` ADD INDEX `siteHandNo`(`siteHandNo`);
|
ALTER TABLE `fpdb`.`hands` ADD INDEX `siteHandNo`(`siteHandNo`);
|
||||||
ALTER TABLE `fpdb`.`players` ADD INDEX `name`(`name`);
|
ALTER TABLE `fpdb`.`players` ADD INDEX `name`(`name`);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user