Merge branch 'master' of git://git.assembla.com/free_poker_tools.git
This commit is contained in:
commit
308cfb1e56
|
@ -34,12 +34,12 @@ 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):
|
def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True, index=0):
|
||||||
"""\
|
"""\
|
||||||
in_path (default '-' = sys.stdin)
|
in_path (default '-' = sys.stdin)
|
||||||
out_path (default '-' = sys.stdout)
|
out_path (default '-' = sys.stdout)
|
||||||
follow : whether to tail -f the input"""
|
follow : whether to tail -f the input"""
|
||||||
HandHistoryConverter.__init__(self, in_path, out_path, sitename="Betfair", follow=follow) # Call super class init.
|
HandHistoryConverter.__init__(self, in_path, out_path, sitename="Betfair", follow=follow, index) # Call super class init.
|
||||||
logging.info("Initialising Betfair converter class")
|
logging.info("Initialising Betfair converter class")
|
||||||
self.filetype = "text"
|
self.filetype = "text"
|
||||||
self.codepage = "cp1252"
|
self.codepage = "cp1252"
|
||||||
|
|
|
@ -57,6 +57,21 @@ def fourStartCards(value1, suit1, value2, suit2, value3, suit3, value4, suit4):
|
||||||
#AAKKs
|
#AAKKs
|
||||||
#AAKKr
|
#AAKKr
|
||||||
# Is probably what we are looking for
|
# Is probably what we are looking for
|
||||||
|
|
||||||
|
# mct:
|
||||||
|
# my maths says there are 4 classes of suitedness
|
||||||
|
# SSSS SSSx SSxy SSHH
|
||||||
|
# encode them as follows:
|
||||||
|
# SSSS (K, J, 6, 3)
|
||||||
|
# - 13C4 = 715 possibilities
|
||||||
|
# SSSx (K, J, 6),(3)
|
||||||
|
# - 13C3 * 13 = 3718 possibilities
|
||||||
|
# SSxy (K, J),(6),(3)
|
||||||
|
# - 13C2 * 13*13 = 13182 possibilities
|
||||||
|
# SSHH (K, J),(6, 3)
|
||||||
|
# - 13C2 * 13C2 = 6084 possibilities
|
||||||
|
# Needless to say they won't fit on a 13x13 grid.
|
||||||
|
# The actual number of hands in each class is far greater
|
||||||
return(0)
|
return(0)
|
||||||
|
|
||||||
def cardFromValueSuit(value, suit):
|
def cardFromValueSuit(value, suit):
|
||||||
|
@ -70,7 +85,7 @@ def cardFromValueSuit(value, suit):
|
||||||
def valueSuitFromCard(card):
|
def valueSuitFromCard(card):
|
||||||
""" Function to convert a card stored in the database (int 0-52) into value
|
""" Function to convert a card stored in the database (int 0-52) into value
|
||||||
and suit like 9s, 4c etc """
|
and suit like 9s, 4c etc """
|
||||||
if card < 0 or card > 52:
|
if card < 0 or card > 52 or not card:
|
||||||
return('')
|
return('')
|
||||||
else:
|
else:
|
||||||
return( ['', '2h', '3h', '4h', '5h', '6h', '7h', '8h', '9h', 'Th', 'Jh', 'Qh', 'Kh', 'Ah'
|
return( ['', '2h', '3h', '4h', '5h', '6h', '7h', '8h', '9h', 'Th', 'Jh', 'Qh', 'Kh', 'Ah'
|
||||||
|
@ -80,4 +95,7 @@ def valueSuitFromCard(card):
|
||||||
][card] )
|
][card] )
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
for i in xrange(1, 14):
|
||||||
|
print "card %2d = %s card %2d = %s card %2d = %s card %2d = %s" % \
|
||||||
|
(i, valueSuitFromCard(i), i+13, valueSuitFromCard(i+13), i+26, valueSuitFromCard(i+26), i+39, valueSuitFromCard(i+39))
|
||||||
|
|
|
@ -86,14 +86,34 @@ class Site:
|
||||||
self.aux_window = node.getAttribute("aux_window")
|
self.aux_window = node.getAttribute("aux_window")
|
||||||
self.font = node.getAttribute("font")
|
self.font = node.getAttribute("font")
|
||||||
self.font_size = node.getAttribute("font_size")
|
self.font_size = node.getAttribute("font_size")
|
||||||
self.use_frames = node.getAttribute("use_frames")
|
self.use_frames = node.getAttribute("use_frames")
|
||||||
self.enabled = fix_tf(node.getAttribute("enabled"), default = True)
|
self.enabled = fix_tf(node.getAttribute("enabled"), default = True)
|
||||||
|
self.xpad = node.getAttribute("xpad")
|
||||||
|
self.ypad = node.getAttribute("ypad")
|
||||||
self.layout = {}
|
self.layout = {}
|
||||||
|
|
||||||
for layout_node in node.getElementsByTagName('layout'):
|
for layout_node in node.getElementsByTagName('layout'):
|
||||||
lo = Layout(layout_node)
|
lo = Layout(layout_node)
|
||||||
self.layout[lo.max] = lo
|
self.layout[lo.max] = lo
|
||||||
|
|
||||||
|
# Site defaults
|
||||||
|
if self.xpad == "": self.xpad = 1
|
||||||
|
else: self.xpad = int(self.xpad)
|
||||||
|
|
||||||
|
if self.ypad == "": self.ypad = 0
|
||||||
|
else: self.ypad = int(self.ypad)
|
||||||
|
|
||||||
|
if self.font_size == "": self.font_size = 7
|
||||||
|
else: self.font_size = int(self.font_size)
|
||||||
|
|
||||||
|
if self.hudopacity == "": self.hudopacity = 1.0
|
||||||
|
else: self.hudopacity = float(self.hudopacity)
|
||||||
|
|
||||||
|
if self.use_frames == "": self.use_frames = False
|
||||||
|
if self.font == "": self.font = "Sans"
|
||||||
|
if self.hudbgcolor == "": self.hudbgcolor = "000000"
|
||||||
|
if self.hudfgcolor == "": self.hudfgcolor = "FFFFFF"
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
temp = "Site = " + self.site_name + "\n"
|
temp = "Site = " + self.site_name + "\n"
|
||||||
for key in dir(self):
|
for key in dir(self):
|
||||||
|
@ -119,9 +139,16 @@ class Stat:
|
||||||
class Game:
|
class Game:
|
||||||
def __init__(self, node):
|
def __init__(self, node):
|
||||||
self.game_name = node.getAttribute("game_name")
|
self.game_name = node.getAttribute("game_name")
|
||||||
self.db = node.getAttribute("db")
|
|
||||||
self.rows = int( node.getAttribute("rows") )
|
self.rows = int( node.getAttribute("rows") )
|
||||||
self.cols = int( node.getAttribute("cols") )
|
self.cols = int( node.getAttribute("cols") )
|
||||||
|
self.xpad = node.getAttribute("xpad")
|
||||||
|
self.ypad = node.getAttribute("ypad")
|
||||||
|
|
||||||
|
# Defaults
|
||||||
|
if self.xpad == "": self.xpad = 1
|
||||||
|
else: self.xpad = int(self.xpad)
|
||||||
|
if self.ypad == "": self.ypad = 0
|
||||||
|
else: self.ypad = int(self.ypad)
|
||||||
|
|
||||||
aux_text = node.getAttribute("aux")
|
aux_text = node.getAttribute("aux")
|
||||||
aux_list = aux_text.split(',')
|
aux_list = aux_text.split(',')
|
||||||
|
@ -146,9 +173,10 @@ class Game:
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
temp = "Game = " + self.game_name + "\n"
|
temp = "Game = " + self.game_name + "\n"
|
||||||
temp = temp + " db = %s\n" % self.db
|
|
||||||
temp = temp + " rows = %d\n" % self.rows
|
temp = temp + " rows = %d\n" % self.rows
|
||||||
temp = temp + " cols = %d\n" % self.cols
|
temp = temp + " cols = %d\n" % self.cols
|
||||||
|
temp = temp + " xpad = %d\n" % self.xpad
|
||||||
|
temp = temp + " ypad = %d\n" % self.ypad
|
||||||
temp = temp + " aux = %s\n" % self.aux
|
temp = temp + " aux = %s\n" % self.aux
|
||||||
|
|
||||||
for stat in self.stats.keys():
|
for stat in self.stats.keys():
|
||||||
|
@ -250,6 +278,7 @@ class Config:
|
||||||
|
|
||||||
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
|
||||||
|
file = os.path.expanduser(file)
|
||||||
if not os.path.exists(file):
|
if not os.path.exists(file):
|
||||||
print "Configuration file %s not found. Using defaults." % (file)
|
print "Configuration file %s not found. Using defaults." % (file)
|
||||||
sys.stderr.write("Configuration file %s not found. Using defaults." % (file))
|
sys.stderr.write("Configuration file %s not found. Using defaults." % (file))
|
||||||
|
@ -492,6 +521,8 @@ class Config:
|
||||||
db['db-backend'] = 2
|
db['db-backend'] = 2
|
||||||
elif string.lower(self.supported_databases[name].db_server) == 'postgresql':
|
elif string.lower(self.supported_databases[name].db_server) == 'postgresql':
|
||||||
db['db-backend'] = 3
|
db['db-backend'] = 3
|
||||||
|
elif string.lower(self.supported_databases[name].db_server) == 'sqlite':
|
||||||
|
db['db-backend'] = 4
|
||||||
else: db['db-backend'] = None # this is big trouble
|
else: db['db-backend'] = None # this is big trouble
|
||||||
return db
|
return db
|
||||||
|
|
||||||
|
@ -630,6 +661,8 @@ class Config:
|
||||||
parms["font"] = self.supported_sites[site].font
|
parms["font"] = self.supported_sites[site].font
|
||||||
parms["font_size"] = self.supported_sites[site].font_size
|
parms["font_size"] = self.supported_sites[site].font_size
|
||||||
parms["enabled"] = self.supported_sites[site].enabled
|
parms["enabled"] = self.supported_sites[site].enabled
|
||||||
|
parms["xpad"] = self.supported_sites[site].xpad
|
||||||
|
parms["ypad"] = self.supported_sites[site].ypad
|
||||||
return parms
|
return parms
|
||||||
|
|
||||||
def set_site_parameters(self, site_name, converter = None, decoder = None,
|
def set_site_parameters(self, site_name, converter = None, decoder = None,
|
||||||
|
@ -680,9 +713,10 @@ class Config:
|
||||||
param = {}
|
param = {}
|
||||||
if self.supported_games.has_key(name):
|
if self.supported_games.has_key(name):
|
||||||
param['game_name'] = self.supported_games[name].game_name
|
param['game_name'] = self.supported_games[name].game_name
|
||||||
param['db'] = self.supported_games[name].db
|
|
||||||
param['rows'] = self.supported_games[name].rows
|
param['rows'] = self.supported_games[name].rows
|
||||||
param['cols'] = self.supported_games[name].cols
|
param['cols'] = self.supported_games[name].cols
|
||||||
|
param['xpad'] = self.supported_games[name].xpad
|
||||||
|
param['ypad'] = self.supported_games[name].ypad
|
||||||
param['aux'] = self.supported_games[name].aux
|
param['aux'] = self.supported_games[name].aux
|
||||||
return param
|
return param
|
||||||
|
|
||||||
|
|
|
@ -27,66 +27,47 @@ Create and manage the database objects.
|
||||||
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
|
||||||
import string
|
import string
|
||||||
|
|
||||||
# pyGTK modules
|
# pyGTK modules
|
||||||
|
|
||||||
# FreePokerTools modules
|
# FreePokerTools modules
|
||||||
|
import fpdb_db
|
||||||
|
import fpdb_simple
|
||||||
import Configuration
|
import Configuration
|
||||||
import SQL
|
import SQL
|
||||||
import Card
|
import Card
|
||||||
|
|
||||||
class Database:
|
class Database:
|
||||||
def __init__(self, c, db_name, game):
|
|
||||||
|
MYSQL_INNODB = 2
|
||||||
|
PGSQL = 3
|
||||||
|
SQLITE = 4
|
||||||
|
|
||||||
|
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
|
||||||
|
self.fdb = fpdb_db.fpdb_db() # sets self.fdb.db self.fdb.cursor and self.fdb.sql
|
||||||
|
self.fdb.do_connect(c)
|
||||||
|
self.connection = self.fdb.db
|
||||||
|
|
||||||
db_params = c.get_db_parameters()
|
db_params = c.get_db_parameters()
|
||||||
if (string.lower(db_params['db-server']) == 'postgresql' or
|
self.import_options = c.get_import_parameters()
|
||||||
string.lower(db_params['db-server']) == 'postgres'):
|
|
||||||
import psycopg2 # posgres via DB-API
|
|
||||||
import psycopg2.extensions
|
|
||||||
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
|
|
||||||
|
|
||||||
try:
|
|
||||||
if db_params['db-host'] == 'localhost' or db_params['db-host'] == '127.0.0.1':
|
|
||||||
self.connection = psycopg2.connect(database = db_params['db-databaseName'])
|
|
||||||
else:
|
|
||||||
self.connection = psycopg2.connect(host = db_params['db-host'],
|
|
||||||
user = db_params['db-user'],
|
|
||||||
password = db_params['db-password'],
|
|
||||||
database = db_params['db-databaseName'])
|
|
||||||
except:
|
|
||||||
print "Error opening database connection %s. See error log file." % (file)
|
|
||||||
traceback.print_exc(file=sys.stderr)
|
|
||||||
print "press enter to continue"
|
|
||||||
sys.stdin.readline()
|
|
||||||
sys.exit()
|
|
||||||
|
|
||||||
elif string.lower(db_params['db-server']) == 'mysql':
|
|
||||||
import MySQLdb # mysql bindings
|
|
||||||
try:
|
|
||||||
self.connection = MySQLdb.connect(host = db_params['db-host'],
|
|
||||||
user = db_params['db-user'],
|
|
||||||
passwd = db_params['db-password'],
|
|
||||||
db = db_params['db-databaseName'])
|
|
||||||
cur_iso = self.connection.cursor()
|
|
||||||
cur_iso.execute('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED')
|
|
||||||
cur_iso.close()
|
|
||||||
|
|
||||||
except:
|
|
||||||
print "Error opening database connection %s. See error log file." % (file)
|
|
||||||
traceback.print_exc(file=sys.stderr)
|
|
||||||
print "press enter to continue"
|
|
||||||
sys.stdin.readline()
|
|
||||||
sys.exit()
|
|
||||||
|
|
||||||
else:
|
|
||||||
print "Database = %s not recognized." % (c.supported_databases[db_name].db_server)
|
|
||||||
sys.stderr.write("Database not recognized, exiting.\n")
|
|
||||||
print "press enter to continue"
|
|
||||||
sys.exit()
|
|
||||||
|
|
||||||
self.type = db_params['db-type']
|
self.type = db_params['db-type']
|
||||||
self.sql = SQL.Sql(game = game, type = self.type)
|
self.backend = db_params['db-backend']
|
||||||
self.connection.rollback()
|
self.db_server = db_params['db-server']
|
||||||
|
|
||||||
|
if self.backend == self.PGSQL:
|
||||||
|
from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT, ISOLATION_LEVEL_READ_COMMITTED, ISOLATION_LEVEL_SERIALIZABLE
|
||||||
|
#ISOLATION_LEVEL_AUTOCOMMIT = 0
|
||||||
|
#ISOLATION_LEVEL_READ_COMMITTED = 1
|
||||||
|
#ISOLATION_LEVEL_SERIALIZABLE = 2
|
||||||
|
|
||||||
|
# where possible avoid creating new SQL instance by using the global one passed in
|
||||||
|
if sql == None:
|
||||||
|
self.sql = SQL.Sql(type = self.type, db_server = db_params['db-server'])
|
||||||
|
else:
|
||||||
|
self.sql = sql
|
||||||
|
|
||||||
# To add to config:
|
# To add to config:
|
||||||
self.hud_style = 'T' # A=All-time
|
self.hud_style = 'T' # A=All-time
|
||||||
|
@ -95,31 +76,78 @@ class Database:
|
||||||
# Future values may also include:
|
# Future values may also include:
|
||||||
# H=Hands (last n hands)
|
# H=Hands (last n hands)
|
||||||
self.hud_hands = 1000 # Max number of hands from each player to use for hud stats
|
self.hud_hands = 1000 # Max number of hands from each player to use for hud stats
|
||||||
self.hud_days = 90 # Max number of days from each player to use for hud stats
|
self.hud_days = 30 # Max number of days from each player to use for hud stats
|
||||||
self.hud_session_gap = 30 # Gap (minutes) between hands that indicates a change of session
|
self.hud_session_gap = 30 # Gap (minutes) between hands that indicates a change of session
|
||||||
# (hands every 2 mins for 1 hour = one session, if followed
|
# (hands every 2 mins for 1 hour = one session, if followed
|
||||||
# by a 40 minute gap and then more hands on same table that is
|
# by a 40 minute gap and then more hands on same table that is
|
||||||
# a new session)
|
# a new session)
|
||||||
cur = self.connection.cursor()
|
self.cursor = self.fdb.cursor
|
||||||
|
|
||||||
self.hand_1day_ago = 0
|
if self.fdb.wrongDbVersion == False:
|
||||||
cur.execute(self.sql.query['get_hand_1day_ago'])
|
# self.hand_1day_ago used to fetch stats for current session (i.e. if hud_style = 'S')
|
||||||
row = cur.fetchone()
|
self.hand_1day_ago = 0
|
||||||
if row and row[0]:
|
self.cursor.execute(self.sql.query['get_hand_1day_ago'])
|
||||||
self.hand_1day_ago = row[0]
|
row = self.cursor.fetchone()
|
||||||
#print "hand 1day ago =", self.hand_1day_ago
|
if row and row[0]:
|
||||||
|
self.hand_1day_ago = row[0]
|
||||||
|
#print "hand 1day ago =", self.hand_1day_ago
|
||||||
|
|
||||||
d = timedelta(days=self.hud_days)
|
# self.date_ndays_ago used if hud_style = 'T'
|
||||||
now = datetime.utcnow() - d
|
d = timedelta(days=self.hud_days)
|
||||||
self.date_ndays_ago = "d%02d%02d%02d" % (now.year-2000, now.month, now.day)
|
now = datetime.utcnow() - d
|
||||||
|
self.date_ndays_ago = "d%02d%02d%02d" % (now.year-2000, now.month, now.day)
|
||||||
|
|
||||||
self.hand_nhands_ago = 0 # todo
|
# self.hand_nhands_ago is used for fetching stats for last n hands (hud_style = 'H')
|
||||||
#cur.execute(self.sql.query['get_table_name'], (hand_id, ))
|
# This option not used yet
|
||||||
#row = cur.fetchone()
|
self.hand_nhands_ago = 0
|
||||||
|
# should use aggregated version of query if appropriate
|
||||||
|
self.cursor.execute(self.sql.query['get_hand_nhands_ago'], (self.hud_hands,self.hud_hands))
|
||||||
|
row = self.cursor.fetchone()
|
||||||
|
if row and row[0]:
|
||||||
|
self.hand_nhands_ago = row[0]
|
||||||
|
print "hand n hands ago =", self.hand_nhands_ago
|
||||||
|
|
||||||
|
#self.cursor.execute(self.sql.query['get_table_name'], (hand_id, ))
|
||||||
|
#row = self.cursor.fetchone()
|
||||||
|
else:
|
||||||
|
print "Bailing on DB query, not sure it exists yet"
|
||||||
|
|
||||||
|
self.saveActions = False if self.import_options['saveActions'] == False else True
|
||||||
|
|
||||||
|
self.connection.rollback() # make sure any locks taken so far are released
|
||||||
|
|
||||||
|
# could be used by hud to change hud style
|
||||||
|
def set_hud_style(self, style):
|
||||||
|
self.hud_style = style
|
||||||
|
|
||||||
|
def do_connect(self, c):
|
||||||
|
self.fdb.do_connect(c)
|
||||||
|
|
||||||
|
def commit(self):
|
||||||
|
self.fdb.db.commit()
|
||||||
|
|
||||||
|
def rollback(self):
|
||||||
|
self.fdb.db.rollback()
|
||||||
|
|
||||||
|
def get_cursor(self):
|
||||||
|
return self.connection.cursor()
|
||||||
|
|
||||||
def close_connection(self):
|
def close_connection(self):
|
||||||
self.connection.close()
|
self.connection.close()
|
||||||
|
|
||||||
|
def disconnect(self, due_to_error=False):
|
||||||
|
"""Disconnects the DB (rolls back if param is true, otherwise commits"""
|
||||||
|
self.fdb.disconnect(due_to_error)
|
||||||
|
|
||||||
|
def reconnect(self, due_to_error=False):
|
||||||
|
"""Reconnects the DB"""
|
||||||
|
self.fdb.reconnect(due_to_error=False)
|
||||||
|
|
||||||
|
def get_backend_name(self):
|
||||||
|
"""Reconnects the DB"""
|
||||||
|
return self.fdb.get_backend_name()
|
||||||
|
|
||||||
|
|
||||||
def get_table_name(self, hand_id):
|
def get_table_name(self, hand_id):
|
||||||
c = self.connection.cursor()
|
c = self.connection.cursor()
|
||||||
c.execute(self.sql.query['get_table_name'], (hand_id, ))
|
c.execute(self.sql.query['get_table_name'], (hand_id, ))
|
||||||
|
@ -156,21 +184,13 @@ class Database:
|
||||||
|
|
||||||
def get_cards(self, hand):
|
def get_cards(self, hand):
|
||||||
"""Get and return the cards for each player in the hand."""
|
"""Get and return the cards for each player in the hand."""
|
||||||
cards = {} # dict of cards, the key is the seat number example: {1: 'AcQd9hTs5d'}
|
cards = {} # dict of cards, the key is the seat number,
|
||||||
|
# the value is a tuple of the players cards
|
||||||
|
# example: {1: (0, 0, 20, 21, 22, 0 , 0)}
|
||||||
c = self.connection.cursor()
|
c = self.connection.cursor()
|
||||||
c.execute(self.sql.query['get_cards'], [hand])
|
c.execute(self.sql.query['get_cards'], [hand])
|
||||||
colnames = [desc[0] for desc in c.description]
|
|
||||||
cardnames = ['card1', 'card2', 'card3', 'card4', 'card5', 'card6', 'card7']
|
|
||||||
for row in c.fetchall():
|
for row in c.fetchall():
|
||||||
cs = ['', '', '', '', '', '', '']
|
cards[row[0]] = row[1:]
|
||||||
seat = -1
|
|
||||||
for col,name in enumerate(colnames):
|
|
||||||
if name in cardnames:
|
|
||||||
cs[cardnames.index(name)] = Card.valueSuitFromCard(row[col])
|
|
||||||
elif name == 'seat_number':
|
|
||||||
seat = row[col]
|
|
||||||
if seat != -1:
|
|
||||||
cards[seat] = ''.join(cs)
|
|
||||||
return cards
|
return cards
|
||||||
|
|
||||||
def get_common_cards(self, hand):
|
def get_common_cards(self, hand):
|
||||||
|
@ -178,12 +198,8 @@ class Database:
|
||||||
cards = {}
|
cards = {}
|
||||||
c = self.connection.cursor()
|
c = self.connection.cursor()
|
||||||
c.execute(self.sql.query['get_common_cards'], [hand])
|
c.execute(self.sql.query['get_common_cards'], [hand])
|
||||||
colnames = [desc[0] for desc in c.description]
|
# row = c.fetchone()
|
||||||
for row in c.fetchall():
|
cards['common'] = c.fetchone()
|
||||||
s_dict = {}
|
|
||||||
for name, val in zip(colnames, row):
|
|
||||||
s_dict[name] = val
|
|
||||||
cards['common'] = (self.convert_cards(s_dict))
|
|
||||||
return cards
|
return cards
|
||||||
|
|
||||||
def convert_cards(self, d):
|
def convert_cards(self, d):
|
||||||
|
@ -211,7 +227,7 @@ class Database:
|
||||||
def get_action_from_hand(self, hand_no):
|
def get_action_from_hand(self, hand_no):
|
||||||
action = [ [], [], [], [], [] ]
|
action = [ [], [], [], [], [] ]
|
||||||
c = self.connection.cursor()
|
c = self.connection.cursor()
|
||||||
c.execute(self.sql.query['get_action_from_hand'], (hand_no, ))
|
c.execute(self.sql.query['get_action_from_hand'], (hand_no,))
|
||||||
for row in c.fetchall():
|
for row in c.fetchall():
|
||||||
street = row[0]
|
street = row[0]
|
||||||
act = row[1:]
|
act = row[1:]
|
||||||
|
@ -222,7 +238,7 @@ class Database:
|
||||||
"""Returns a hash of winners:amount won, given a hand number."""
|
"""Returns a hash of winners:amount won, given a hand number."""
|
||||||
winners = {}
|
winners = {}
|
||||||
c = self.connection.cursor()
|
c = self.connection.cursor()
|
||||||
c.execute(self.sql.query['get_winners_from_hand'], (hand, ))
|
c.execute(self.sql.query['get_winners_from_hand'], (hand,))
|
||||||
for row in c.fetchall():
|
for row in c.fetchall():
|
||||||
winners[row[0]] = row[1]
|
winners[row[0]] = row[1]
|
||||||
return winners
|
return winners
|
||||||
|
@ -312,6 +328,159 @@ class Database:
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def get_last_insert_id(self):
|
||||||
|
return self.fdb.getLastInsertId()
|
||||||
|
|
||||||
|
|
||||||
|
#stores a stud/razz hand into the database
|
||||||
|
def ring_stud(self, config, settings, db, cursor, base, category, site_hand_no, gametype_id, hand_start_time
|
||||||
|
,names, player_ids, start_cashes, antes, card_values, card_suits, winnings, rakes
|
||||||
|
,action_types, allIns, action_amounts, actionNos, hudImportData, maxSeats, tableName
|
||||||
|
,seatNos):
|
||||||
|
|
||||||
|
fpdb_simple.fillCardArrays(len(names), base, category, card_values, card_suits)
|
||||||
|
|
||||||
|
hands_id = fpdb_simple.storeHands(self.backend, db, cursor, site_hand_no, gametype_id
|
||||||
|
,hand_start_time, names, tableName, maxSeats, hudImportData
|
||||||
|
,(None, None, None, None, None), (None, None, None, None, None))
|
||||||
|
|
||||||
|
#print "before calling store_hands_players_stud, antes:", antes
|
||||||
|
hands_players_ids = fpdb_simple.store_hands_players_stud(self.backend, db, cursor, hands_id, player_ids
|
||||||
|
,start_cashes, antes, card_values
|
||||||
|
,card_suits, winnings, rakes, seatNos)
|
||||||
|
|
||||||
|
if 'dropHudCache' not in settings or settings['dropHudCache'] != 'drop':
|
||||||
|
fpdb_simple.storeHudCache(self.backend, cursor, base, category, gametype_id, hand_start_time, player_ids, hudImportData)
|
||||||
|
|
||||||
|
if self.saveActions:
|
||||||
|
fpdb_simple.storeActions(cursor, hands_players_ids, action_types
|
||||||
|
,allIns, action_amounts, actionNos)
|
||||||
|
return hands_id
|
||||||
|
#end def ring_stud
|
||||||
|
|
||||||
|
def ring_holdem_omaha(self, config, settings, db, cursor, base, category, site_hand_no, gametype_id
|
||||||
|
,hand_start_time, names, player_ids, start_cashes, positions, card_values
|
||||||
|
,card_suits, board_values, board_suits, winnings, rakes, action_types, allIns
|
||||||
|
,action_amounts, actionNos, hudImportData, maxSeats, tableName, seatNos):
|
||||||
|
"""stores a holdem/omaha hand into the database"""
|
||||||
|
|
||||||
|
t0 = time()
|
||||||
|
fpdb_simple.fillCardArrays(len(names), base, category, card_values, card_suits)
|
||||||
|
t1 = time()
|
||||||
|
fpdb_simple.fill_board_cards(board_values, board_suits)
|
||||||
|
t2 = time()
|
||||||
|
|
||||||
|
hands_id = fpdb_simple.storeHands(self.backend, db, cursor, site_hand_no, gametype_id
|
||||||
|
,hand_start_time, names, tableName, maxSeats,
|
||||||
|
hudImportData, board_values, board_suits)
|
||||||
|
#TEMPORARY CALL! - Just until all functions are migrated
|
||||||
|
t3 = time()
|
||||||
|
hands_players_ids = fpdb_simple.store_hands_players_holdem_omaha(
|
||||||
|
self.backend, db, cursor, category, hands_id, player_ids, start_cashes
|
||||||
|
, positions, card_values, card_suits, winnings, rakes, seatNos, hudImportData)
|
||||||
|
t4 = time()
|
||||||
|
#print "ring holdem, backend=%d" % backend
|
||||||
|
if 'dropHudCache' not in settings or settings['dropHudCache'] != 'drop':
|
||||||
|
fpdb_simple.storeHudCache(self.backend, cursor, base, category, gametype_id, hand_start_time, player_ids, hudImportData)
|
||||||
|
t5 = time()
|
||||||
|
t6 = time()
|
||||||
|
if self.saveActions:
|
||||||
|
fpdb_simple.storeActions(cursor, hands_players_ids, action_types, allIns, action_amounts, actionNos)
|
||||||
|
t7 = time()
|
||||||
|
#print "fills=(%4.3f) saves=(%4.3f,%4.3f,%4.3f,%4.3f)" % (t2-t0, t3-t2, t4-t3, t5-t4, t6-t5)
|
||||||
|
return hands_id
|
||||||
|
#end def ring_holdem_omaha
|
||||||
|
|
||||||
|
def tourney_holdem_omaha(self, config, settings, db, cursor, base, category, siteTourneyNo, buyin, fee, knockout
|
||||||
|
,entries, prizepool, tourney_start, payin_amounts, ranks, tourneyTypeId
|
||||||
|
,siteId #end of tourney specific params
|
||||||
|
,site_hand_no, gametype_id, hand_start_time, names, player_ids
|
||||||
|
,start_cashes, positions, card_values, card_suits, board_values
|
||||||
|
,board_suits, winnings, rakes, action_types, allIns, action_amounts
|
||||||
|
,actionNos, hudImportData, maxSeats, tableName, seatNos):
|
||||||
|
"""stores a tourney holdem/omaha hand into the database"""
|
||||||
|
|
||||||
|
fpdb_simple.fillCardArrays(len(names), base, category, card_values, card_suits)
|
||||||
|
fpdb_simple.fill_board_cards(board_values, board_suits)
|
||||||
|
|
||||||
|
tourney_id = fpdb_simple.store_tourneys(cursor, tourneyTypeId, siteTourneyNo, entries, prizepool, tourney_start)
|
||||||
|
tourneys_players_ids = fpdb_simple.store_tourneys_players(cursor, tourney_id, player_ids, payin_amounts, ranks, winnings)
|
||||||
|
|
||||||
|
hands_id = fpdb_simple.storeHands(self.backend, db, cursor, site_hand_no, gametype_id
|
||||||
|
,hand_start_time, names, tableName, maxSeats)
|
||||||
|
|
||||||
|
hands_players_ids = fpdb_simple.store_hands_players_holdem_omaha_tourney(
|
||||||
|
self.backend, db, cursor, category, hands_id, player_ids, start_cashes, positions
|
||||||
|
, card_values, card_suits, winnings, rakes, seatNos, tourneys_players_ids)
|
||||||
|
|
||||||
|
#print "tourney holdem, backend=%d" % backend
|
||||||
|
if 'dropHudCache' not in settings or settings['dropHudCache'] != 'drop':
|
||||||
|
fpdb_simple.storeHudCache(self.backend, cursor, base, category, gametype_id, hand_start_time, player_ids, hudImportData)
|
||||||
|
|
||||||
|
if self.saveActions:
|
||||||
|
fpdb_simple.storeActions(cursor, hands_players_ids, action_types, allIns, action_amounts, actionNos)
|
||||||
|
return hands_id
|
||||||
|
#end def tourney_holdem_omaha
|
||||||
|
|
||||||
|
def tourney_stud(self, config, settings, db, cursor, base, category, siteTourneyNo, buyin, fee, knockout, entries
|
||||||
|
,prizepool, tourneyStartTime, payin_amounts, ranks, tourneyTypeId, siteId
|
||||||
|
,siteHandNo, gametypeId, handStartTime, names, playerIds, startCashes, antes
|
||||||
|
,cardValues, cardSuits, winnings, rakes, actionTypes, allIns, actionAmounts
|
||||||
|
,actionNos, hudImportData, maxSeats, tableName, seatNos):
|
||||||
|
#stores a tourney stud/razz hand into the database
|
||||||
|
|
||||||
|
fpdb_simple.fillCardArrays(len(names), base, category, cardValues, cardSuits)
|
||||||
|
|
||||||
|
tourney_id = fpdb_simple.store_tourneys(cursor, tourneyTypeId, siteTourneyNo, entries, prizepool, tourneyStartTime)
|
||||||
|
|
||||||
|
tourneys_players_ids = fpdb_simple.store_tourneys_players(cursor, tourney_id, playerIds, payin_amounts, ranks, winnings)
|
||||||
|
|
||||||
|
hands_id = fpdb_simple.storeHands(self.backend, db, cursor, siteHandNo, gametypeId, handStartTime, names, tableName, maxSeats)
|
||||||
|
|
||||||
|
hands_players_ids = fpdb_simple.store_hands_players_stud_tourney(self.backend, db, cursor, hands_id
|
||||||
|
, playerIds, startCashes, antes, cardValues, cardSuits
|
||||||
|
, winnings, rakes, seatNos, tourneys_players_ids)
|
||||||
|
|
||||||
|
if 'dropHudCache' not in settings or settings['dropHudCache'] != 'drop':
|
||||||
|
fpdb_simple.storeHudCache(self.backend, cursor, base, category, gametypeId, hand_start_time, playerIds, hudImportData)
|
||||||
|
|
||||||
|
if self.saveActions:
|
||||||
|
fpdb_simple.storeActions(cursor, hands_players_ids, actionTypes, allIns, actionAmounts, actionNos)
|
||||||
|
return hands_id
|
||||||
|
#end def tourney_stud
|
||||||
|
|
||||||
|
def rebuild_hudcache(self):
|
||||||
|
"""clears hudcache and rebuilds from the individual handsplayers records"""
|
||||||
|
|
||||||
|
stime = time()
|
||||||
|
self.connection.cursor().execute(self.sql.query['clearHudCache'])
|
||||||
|
self.connection.cursor().execute(self.sql.query['rebuildHudCache'])
|
||||||
|
self.commit()
|
||||||
|
print "Rebuild hudcache took %.1f seconds" % (time() - stime,)
|
||||||
|
#end def rebuild_hudcache
|
||||||
|
|
||||||
|
|
||||||
|
def analyzeDB(self):
|
||||||
|
"""Do whatever the DB can offer to update index/table statistics"""
|
||||||
|
stime = time()
|
||||||
|
if self.backend == self.MYSQL_INNODB:
|
||||||
|
try:
|
||||||
|
self.cursor.execute(self.sql.query['analyze'])
|
||||||
|
except:
|
||||||
|
print "Error during analyze"
|
||||||
|
elif self.backend == self.PGSQL:
|
||||||
|
self.connection.set_isolation_level(0) # allow vacuum to work
|
||||||
|
try:
|
||||||
|
self.cursor = self.get_cursor()
|
||||||
|
self.cursor.execute(self.sql.query['analyze'])
|
||||||
|
except:
|
||||||
|
print "Error during analyze:", str(sys.exc_value)
|
||||||
|
self.connection.set_isolation_level(1) # go back to normal isolation level
|
||||||
|
self.commit()
|
||||||
|
atime = time() - stime
|
||||||
|
print "Analyze took %.1f seconds" % (atime,)
|
||||||
|
#end def analyzeDB
|
||||||
|
|
||||||
if __name__=="__main__":
|
if __name__=="__main__":
|
||||||
c = Configuration.Config()
|
c = Configuration.Config()
|
||||||
|
|
||||||
|
@ -333,10 +502,10 @@ if __name__=="__main__":
|
||||||
for p in stat_dict.keys():
|
for p in stat_dict.keys():
|
||||||
print p, " ", stat_dict[p]
|
print p, " ", stat_dict[p]
|
||||||
|
|
||||||
# print "nutOmatics stats:"
|
#print "nutOmatics stats:"
|
||||||
# stat_dict = db_connection.get_stats_from_hand(h, hero)
|
#stat_dict = db_connection.get_stats_from_hand(h, hero)
|
||||||
# for p in stat_dict.keys():
|
#for p in stat_dict.keys():
|
||||||
# print p, " ", stat_dict[p]
|
# print p, " ", stat_dict[p]
|
||||||
|
|
||||||
print "cards =", db_connection.get_cards(u'1')
|
print "cards =", db_connection.get_cards(u'1')
|
||||||
db_connection.close_connection
|
db_connection.close_connection
|
||||||
|
|
|
@ -37,7 +37,7 @@ 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):
|
def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True, debugging=False, index=0):
|
||||||
"""\
|
"""\
|
||||||
in_path (default '-' = sys.stdin)
|
in_path (default '-' = sys.stdin)
|
||||||
out_path (default '-' = sys.stdout)
|
out_path (default '-' = sys.stdout)
|
||||||
|
@ -45,7 +45,7 @@ follow : whether to tail -f the input
|
||||||
autostart: whether to run the thread (or you can call start() yourself)
|
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..."""
|
debugging: if False, pass on partially supported game types. If true, have a go and error..."""
|
||||||
print "DEBUG: XXXXXXXXXXXXXXX"
|
print "DEBUG: XXXXXXXXXXXXXXX"
|
||||||
HandHistoryConverter.__init__(self, in_path, out_path, sitename="Everleaf", follow=follow)
|
HandHistoryConverter.__init__(self, in_path, out_path, sitename="Everleaf", follow=follow, index=index)
|
||||||
logging.info("Initialising Everleaf converter class")
|
logging.info("Initialising Everleaf converter class")
|
||||||
self.filetype = "text"
|
self.filetype = "text"
|
||||||
self.codepage = "cp1252"
|
self.codepage = "cp1252"
|
||||||
|
@ -237,11 +237,14 @@ or None if we fail to get the info """
|
||||||
# Also works with Omaha hands.
|
# Also works with Omaha hands.
|
||||||
cards = m.group('CARDS')
|
cards = m.group('CARDS')
|
||||||
cards = [card.strip() for card in cards.split(',')]
|
cards = [card.strip() for card in cards.split(',')]
|
||||||
hand.addHoleCards(cards, m.group('PNAME'))
|
# hand.addHoleCards(cards, m.group('PNAME'))
|
||||||
|
hand.addHoleCards('PREFLOP', hand.hero, closed=cards, shown=False, mucked=False, dealt=True)
|
||||||
|
|
||||||
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("Everleaf readStudPlayerCards is only a stub.")
|
logging.warning("Everleaf readStudPlayerCards is only a stub.")
|
||||||
|
@ -292,7 +295,8 @@ or None if we fail to get the info """
|
||||||
cards = cards.split(', ')
|
cards = cards.split(', ')
|
||||||
player = m.group('PNAME')
|
player = m.group('PNAME')
|
||||||
logging.debug("readShownCards %s cards=%s" % (player, cards))
|
logging.debug("readShownCards %s cards=%s" % (player, cards))
|
||||||
hand.addShownCards(cards=None, player=m.group('PNAME'), holeandboard=cards)
|
# hand.addShownCards(cards=None, player=m.group('PNAME'), holeandboard=cards)
|
||||||
|
hand.addShownCards(cards=cards, player=m.group('PNAME'))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -30,12 +30,11 @@ import fpdb_db
|
||||||
import FpdbSQLQueries
|
import FpdbSQLQueries
|
||||||
|
|
||||||
class Filters(threading.Thread):
|
class Filters(threading.Thread):
|
||||||
def __init__(self, db, settings, config, qdict, display = {},debug=True):
|
def __init__(self, db, config, qdict, display = {}, debug=True):
|
||||||
self.debug=debug
|
self.debug=debug
|
||||||
#print "start of GraphViewer constructor"
|
#print "start of GraphViewer constructor"
|
||||||
self.db=db
|
self.db=db
|
||||||
self.cursor=db.cursor
|
self.cursor=db.cursor
|
||||||
self.settings=settings
|
|
||||||
self.sql=qdict
|
self.sql=qdict
|
||||||
self.conf = config
|
self.conf = config
|
||||||
self.display = display
|
self.display = display
|
||||||
|
@ -235,7 +234,7 @@ class Filters(threading.Thread):
|
||||||
|
|
||||||
def __set_hero_name(self, w, site):
|
def __set_hero_name(self, w, site):
|
||||||
self.heroes[site] = w.get_text()
|
self.heroes[site] = w.get_text()
|
||||||
# print "DEBUG: settings heroes[%s]: %s"%(site, self.heroes[site])
|
# print "DEBUG: setting heroes[%s]: %s"%(site, self.heroes[site])
|
||||||
|
|
||||||
def createSiteLine(self, hbox, site):
|
def createSiteLine(self, hbox, site):
|
||||||
cb = gtk.CheckButton(site)
|
cb = gtk.CheckButton(site)
|
||||||
|
@ -556,23 +555,12 @@ def main(argv=None):
|
||||||
config = Configuration.Config()
|
config = Configuration.Config()
|
||||||
db = None
|
db = None
|
||||||
|
|
||||||
settings = {}
|
|
||||||
|
|
||||||
settings.update(config.get_db_parameters())
|
|
||||||
settings.update(config.get_tv_parameters())
|
|
||||||
settings.update(config.get_import_parameters())
|
|
||||||
settings.update(config.get_default_paths())
|
|
||||||
|
|
||||||
db = fpdb_db.fpdb_db()
|
db = fpdb_db.fpdb_db()
|
||||||
db.connect(settings['db-backend'],
|
db.do_connect(config)
|
||||||
settings['db-host'],
|
|
||||||
settings['db-databaseName'],
|
|
||||||
settings['db-user'],
|
|
||||||
settings['db-password'])
|
|
||||||
|
|
||||||
qdict = FpdbSQLQueries.FpdbSQLQueries(db.get_backend_name())
|
qdict = FpdbSQLQueries.FpdbSQLQueries(db.get_backend_name())
|
||||||
|
|
||||||
i = Filters(db, settings, config, qdict)
|
i = Filters(db, config, qdict)
|
||||||
main_window = gtk.Window()
|
main_window = gtk.Window()
|
||||||
main_window.connect('destroy', destroy)
|
main_window.connect('destroy', destroy)
|
||||||
main_window.add(i.get_vbox())
|
main_window.add(i.get_vbox())
|
||||||
|
|
|
@ -1,161 +0,0 @@
|
||||||
# pokerstars_cash.py
|
|
||||||
# -*- coding: iso-8859-15
|
|
||||||
#
|
|
||||||
# PokerStats, an online poker statistics tracking software for Linux
|
|
||||||
# Copyright (C) 2007-2008 Mika Boström <bostik@iki.fi>
|
|
||||||
#
|
|
||||||
# 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, version 3 of the License.
|
|
||||||
#
|
|
||||||
|
|
||||||
# Modified for use in fpdb by Carl Gherardi
|
|
||||||
|
|
||||||
import re
|
|
||||||
|
|
||||||
# These are PokerStars specific;
|
|
||||||
# More importantly, they are currently valid for cash game only.
|
|
||||||
#####
|
|
||||||
# XXX: There was a weird problem with saved hand histories in PokerStars
|
|
||||||
# client 2.491; if a user was present on the table (and thus anywhere in
|
|
||||||
# the hand history), with non-standard characters in their username, the
|
|
||||||
# client would prepend a literal Ctrl-P (ASCII 16, 0x10) character to
|
|
||||||
# the hand history title line. Hence, to allow these strangely saved
|
|
||||||
# hands to be parsed and imported, there is a conditional "one extra
|
|
||||||
# character" allowed at the start of the new hand regex.
|
|
||||||
|
|
||||||
|
|
||||||
class FpdbRegex:
|
|
||||||
def __init__(self):
|
|
||||||
self.__GAME_INFO_REGEX=''
|
|
||||||
self.__SPLIT_HAND_REGEX='\n\n\n'
|
|
||||||
self.__NEW_HAND_REGEX='^.?PokerStars Game #\d+:\s+Hold\'em'
|
|
||||||
self.__HAND_INFO_REGEX='^.*#(\d+):\s+(\S+)\s([\s\S]+)\s\(\$?([.0-9]+)/\$?([.0-9]+)\)\s-\s(\S+)\s-?\s?(\S+)\s\(?(\w+)\)?'
|
|
||||||
self.__TABLE_INFO_REGEX='^\S+\s+\'.*\'\s+(\d+)-max\s+Seat\s#(\d+)'
|
|
||||||
self.__PLAYER_INFO_REGEX='^Seat\s(\d+):\s(.*)\s\(\$?([.\d]+)\s'
|
|
||||||
self.__POST_SB_REGEX='^(.*):\sposts small blind'
|
|
||||||
self.__POST_BB_REGEX='^(.*):\sposts big blind'
|
|
||||||
self.__POST_BOTH_REGEX='^(.*):\sposts small & big blinds'
|
|
||||||
self.__HAND_STAGE_REGEX='^\*{3}\s(.*)\s\*{3}'
|
|
||||||
self.__HOLE_CARD_REGEX='^\*{3}\sHOLE CARDS'
|
|
||||||
self.__FLOP_CARD_REGEX='^\*{3}\sFLOP\s\*{3}\s\[(\S{2})\s(\S{2})\s(\S{2})\]'
|
|
||||||
self.__TURN_CARD_REGEX='^\*{3}\sTURN\s\*{3}\s\[\S{2}\s\S{2}\s\S{2}\]\s\[(\S{2})\]'
|
|
||||||
self.__RIVER_CARD_REGEX='^\*{3}\sRIVER\s\*{3}\s\[\S{2}\s\S{2}\s\S{2}\s\S{2}\]\s\[(\S{2})\]'
|
|
||||||
self.__SHOWDOWN_REGEX='^\*{3}\sSHOW DOWN'
|
|
||||||
self.__SUMMARY_REGEX='^\*{3}\sSUMMARY'
|
|
||||||
self.__UNCALLED_BET_REGEX='^Uncalled bet \(\$([.\d]+)\) returned to (.*)'
|
|
||||||
self.__POT_AND_RAKE_REGEX='^Total\spot\s\$([.\d]+).*\|\sRake\s\$([.\d]+)'
|
|
||||||
self.__COLLECT_POT_REGEX='^(.*)\scollected\s\$([.\d]+)\sfrom\s((main|side)\s)?pot'
|
|
||||||
self.__HERO_CARDS_REGEX='^Dealt\sto\s(.*)\s\[(\S{2})\s(\S{2})\]'
|
|
||||||
self.__SHOWN_CARDS_REGEX='^(.*):\sshows\s\[(\S{2})\s(\S{2})\]'
|
|
||||||
self.__ACTION_STEP_REGEX='^(.*):\s(bets|checks|raises|calls|folds)((\s\$([.\d]+))?(\sto\s\$([.\d]+))?)?'
|
|
||||||
|
|
||||||
self.__SHOWDOWN_ACTION_REGEX='^(.*):\s(shows|mucks)'
|
|
||||||
self.__SUMMARY_CARDS_REGEX='^Seat\s\d+:\s(.*)\s(showed|mucked)\s\[(\S{2})\s(\S{2})\]'
|
|
||||||
self.__SUMMARY_CARDS_EXTRA_REGEX='^Seat\s\d+:\s(.*)\s(\(.*\)\s)(showed|mucked)\s\[(\S{2})\s(\S{2})\]'
|
|
||||||
|
|
||||||
def compileRegexes(self):
|
|
||||||
### Compile the regexes
|
|
||||||
self.game_info_re = re.compile(self.__GAME_INFO_REGEX)
|
|
||||||
self.split_hand_re = re.compile(self.__SPLIT_HAND_REGEX)
|
|
||||||
self.hand_start_re = re.compile(self.__NEW_HAND_REGEX)
|
|
||||||
self.hand_info_re = re.compile(self.__HAND_INFO_REGEX)
|
|
||||||
self.table_info_re = re.compile(self.__TABLE_INFO_REGEX)
|
|
||||||
self.player_info_re = re.compile(self.__PLAYER_INFO_REGEX)
|
|
||||||
self.small_blind_re = re.compile(self.__POST_SB_REGEX)
|
|
||||||
self.big_blind_re = re.compile(self.__POST_BB_REGEX)
|
|
||||||
self.both_blinds_re = re.compile(self.__POST_BOTH_REGEX)
|
|
||||||
self.hand_stage_re = re.compile(self.__HAND_STAGE_REGEX)
|
|
||||||
self.hole_cards_re = re.compile(self.__HOLE_CARD_REGEX)
|
|
||||||
self.flop_cards_re = re.compile(self.__FLOP_CARD_REGEX)
|
|
||||||
self.turn_card_re = re.compile(self.__TURN_CARD_REGEX)
|
|
||||||
self.river_card_re = re.compile(self.__RIVER_CARD_REGEX)
|
|
||||||
self.showdown_re = re.compile(self.__SHOWDOWN_REGEX)
|
|
||||||
self.summary_re = re.compile(self.__SUMMARY_REGEX)
|
|
||||||
self.uncalled_bet_re = re.compile(self.__UNCALLED_BET_REGEX)
|
|
||||||
self.collect_pot_re = re.compile(self.__COLLECT_POT_REGEX)
|
|
||||||
self.hero_cards_re = re.compile(self.__HERO_CARDS_REGEX)
|
|
||||||
self.cards_shown_re = re.compile(self.__SHOWN_CARDS_REGEX)
|
|
||||||
self.summary_cards_re = re.compile(self.__SUMMARY_CARDS_REGEX)
|
|
||||||
self.summary_cards_extra_re = re.compile(self.__SUMMARY_CARDS_EXTRA_REGEX)
|
|
||||||
self.action_re = re.compile(self.__ACTION_STEP_REGEX)
|
|
||||||
self.rake_re = re.compile(self.__POT_AND_RAKE_REGEX)
|
|
||||||
self.showdown_action_re = re.compile(self.__SHOWDOWN_ACTION_REGEX)
|
|
||||||
|
|
||||||
# Set methods for plugins to override
|
|
||||||
|
|
||||||
def setGameInfoRegex(self, string):
|
|
||||||
self.__GAME_INFO_REGEX = string
|
|
||||||
|
|
||||||
def setSplitHandRegex(self, string):
|
|
||||||
self.__SPLIT_HAND_REGEX = string
|
|
||||||
|
|
||||||
def setNewHandRegex(self, string):
|
|
||||||
self.__NEW_HAND_REGEX = string
|
|
||||||
|
|
||||||
def setHandInfoRegex(self, string):
|
|
||||||
self.__HAND_INFO_REGEX = string
|
|
||||||
|
|
||||||
def setTableInfoRegex(self, string):
|
|
||||||
self.__TABLE_INFO_REGEX = string
|
|
||||||
|
|
||||||
def setPlayerInfoRegex(self, string):
|
|
||||||
self.__PLAYER_INFO_REGEX = string
|
|
||||||
|
|
||||||
def setPostSbRegex(self, string):
|
|
||||||
self.__POST_SB_REGEX = string
|
|
||||||
|
|
||||||
def setPostBbRegex(self, string):
|
|
||||||
self.__POST_BB_REGEX = string
|
|
||||||
|
|
||||||
def setPostBothRegex(self, string):
|
|
||||||
self.__POST_BOTH_REGEX = string
|
|
||||||
|
|
||||||
def setHandStageRegex(self, string):
|
|
||||||
self.__HAND_STAGE_REGEX = string
|
|
||||||
|
|
||||||
def setHoleCardRegex(self, string):
|
|
||||||
self.__HOLE_CARD_REGEX = string
|
|
||||||
|
|
||||||
def setFlopCardRegex(self, string):
|
|
||||||
self.__FLOP_CARD_REGEX = string
|
|
||||||
|
|
||||||
def setTurnCardRegex(self, string):
|
|
||||||
self.__TURN_CARD_REGEX = string
|
|
||||||
|
|
||||||
def setRiverCardRegex(self, string):
|
|
||||||
self.__RIVER_CARD_REGEX = string
|
|
||||||
|
|
||||||
def setShowdownRegex(self, string):
|
|
||||||
self.__SHOWDOWN_REGEX = string
|
|
||||||
|
|
||||||
def setSummaryRegex(self, string):
|
|
||||||
self.__SUMMARY_REGEX = string
|
|
||||||
|
|
||||||
def setUncalledBetRegex(self, string):
|
|
||||||
self.__UNCALLED_BET_REGEX = string
|
|
||||||
|
|
||||||
def setCollectPotRegex(self, string):
|
|
||||||
self.__COLLECT_POT_REGEX = string
|
|
||||||
|
|
||||||
def setHeroCardsRegex(self, string):
|
|
||||||
self.__HERO_CARDS_REGEX = string
|
|
||||||
|
|
||||||
def setShownCardsRegex(self, string):
|
|
||||||
self.__SHOWN_CARDS_REGEX = string
|
|
||||||
|
|
||||||
def setSummaryCardsRegex(self, string):
|
|
||||||
self.__SUMMARY_CARDS_REGEX = string
|
|
||||||
|
|
||||||
def setSummaryCardsExtraRegex(self, string):
|
|
||||||
self.__SUMMARY_CARDS_EXTRA_REGEX = string
|
|
||||||
|
|
||||||
def setActionStepRegex(self, string):
|
|
||||||
self.__ACTION_STEP_REGEX = string
|
|
||||||
|
|
||||||
def setPotAndRakeRegex(self, string):
|
|
||||||
self.__POT_AND_RAKE_REGEX = string
|
|
||||||
|
|
||||||
def setShowdownActionRegex(self, string):
|
|
||||||
self.__SHOWDOWN_ACTION_REGEX = string
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -23,25 +23,49 @@ import logging
|
||||||
from HandHistoryConverter import *
|
from HandHistoryConverter import *
|
||||||
|
|
||||||
# Fulltilt HH Format converter
|
# Fulltilt HH Format converter
|
||||||
|
# TODO: cat tourno and table to make table name for tournaments
|
||||||
|
|
||||||
class Fulltilt(HandHistoryConverter):
|
class Fulltilt(HandHistoryConverter):
|
||||||
|
|
||||||
# Static regexes
|
# Static regexes
|
||||||
re_GameInfo = re.compile('- (?P<CURRENCY>\$|)?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) (Ante \$(?P<ANTE>[.0-9]+) )?- (?P<LIMIT>(No Limit|Pot Limit|Limit))? (?P<GAME>(Hold\'em|Omaha Hi|Razz))')
|
re_GameInfo = re.compile('''(?:(?P<TOURNAMENT>.+)\s\((?P<TOURNO>\d+)\),\s)?
|
||||||
|
.+
|
||||||
|
-\s(?P<CURRENCY>\$|)?
|
||||||
|
(?P<SB>[.0-9]+)/
|
||||||
|
\$?(?P<BB>[.0-9]+)\s
|
||||||
|
(Ante\s\$?(?P<ANTE>[.0-9]+)\s)?-\s
|
||||||
|
(?P<LIMIT>(No\sLimit|Pot\sLimit|Limit))?\s
|
||||||
|
(?P<GAME>(Hold\'em|Omaha\sHi|Omaha\sH/L|7\sCard\sStud|Stud\sH/L|Razz|Stud\sHi))
|
||||||
|
''', 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]+): Table (?P<TABLE>[- a-zA-Z]+) (\((?P<TABLEATTRIBUTES>.+)\) )?- \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) (Ante \$(?P<ANTE>[.0-9]+) )?- (?P<GAMETYPE>[a-zA-Z\' ]+) - (?P<DATETIME>.*)')
|
re_HandInfo = re.compile('''.*\#(?P<HID>[0-9]+):\s
|
||||||
|
(?:(?P<TOURNAMENT>.+)\s\((?P<TOURNO>\d+)\),\s)?
|
||||||
|
Table\s
|
||||||
|
(?P<PLAY>Play\sChip\s|PC)?
|
||||||
|
(?P<TABLE>[-\s\da-zA-Z]+)\s
|
||||||
|
(\((?P<TABLEATTRIBUTES>.+)\)\s)?-\s
|
||||||
|
\$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+)\s(Ante\s\$?(?P<ANTE>[.0-9]+)\s)?-\s
|
||||||
|
(?P<GAMETYPE>[a-zA-Z\/\'\s]+)\s-\s
|
||||||
|
(?P<DATETIME>.*)
|
||||||
|
''', re.VERBOSE)
|
||||||
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]+)\)\n')
|
re_PlayerInfo = 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>.+)\]")
|
||||||
|
|
||||||
|
# These regexes are for FTP only
|
||||||
|
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)
|
||||||
# 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.
|
||||||
|
|
||||||
def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True):
|
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)
|
in_path (default '-' = sys.stdin)
|
||||||
out_path (default '-' = sys.stdout)
|
out_path (default '-' = sys.stdout)
|
||||||
follow : whether to tail -f the input"""
|
follow : whether to tail -f the input"""
|
||||||
HandHistoryConverter.__init__(self, in_path, out_path, sitename="Fulltilt", follow=follow)
|
HandHistoryConverter.__init__(self, in_path, out_path, sitename="Fulltilt", follow=follow, index=index)
|
||||||
logging.info("Initialising Fulltilt converter class")
|
logging.info("Initialising Fulltilt converter class")
|
||||||
self.filetype = "text"
|
self.filetype = "text"
|
||||||
self.codepage = "cp1252"
|
self.codepage = "cp1252"
|
||||||
|
@ -63,19 +87,24 @@ follow : whether to tail -f the input"""
|
||||||
self.re_BringIn = re.compile(r"^%s brings in for \$?(?P<BRINGIN>[.0-9]+)" % player_re, re.MULTILINE)
|
self.re_BringIn = re.compile(r"^%s brings in for \$?(?P<BRINGIN>[.0-9]+)" % player_re, re.MULTILINE)
|
||||||
self.re_PostBoth = re.compile(r"^%s posts small \& big blinds \[\$? (?P<SBBB>[.0-9]+)" % player_re, re.MULTILINE)
|
self.re_PostBoth = re.compile(r"^%s posts small \& big blinds \[\$? (?P<SBBB>[.0-9]+)" % player_re, re.MULTILINE)
|
||||||
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)(\s\$(?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)
|
||||||
|
|
||||||
|
|
||||||
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"],
|
||||||
|
|
||||||
["ring", "stud", "fl"],
|
["ring", "stud", "fl"],
|
||||||
["ring", "omaha", "pl"]
|
|
||||||
|
["tour", "hold", "nl"],
|
||||||
|
["tour", "hold", "pl"],
|
||||||
|
["tour", "hold", "fl"],
|
||||||
|
|
||||||
|
["tour", "stud", "fl"],
|
||||||
]
|
]
|
||||||
|
|
||||||
def determineGameType(self, handText):
|
def determineGameType(self, handText):
|
||||||
|
@ -88,7 +117,6 @@ follow : whether to tail -f the input"""
|
||||||
m = self.re_GameInfo.search(handText)
|
m = self.re_GameInfo.search(handText)
|
||||||
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
|
||||||
|
@ -96,39 +124,41 @@ follow : whether to tail -f the input"""
|
||||||
games = { # base, category
|
games = { # base, category
|
||||||
"Hold'em" : ('hold','holdem'),
|
"Hold'em" : ('hold','holdem'),
|
||||||
'Omaha Hi' : ('hold','omahahi'),
|
'Omaha Hi' : ('hold','omahahi'),
|
||||||
|
'Omaha H/L' : ('hold','omahahilo'),
|
||||||
'Razz' : ('stud','razz'),
|
'Razz' : ('stud','razz'),
|
||||||
'7 Card Stud' : ('stud','studhi')
|
'Stud Hi' : ('stud','studhi'),
|
||||||
|
'Stud H/L' : ('stud','studhilo')
|
||||||
}
|
}
|
||||||
currencies = { u' €':'EUR', '$':'USD', '':'T$' }
|
currencies = { u' €':'EUR', '$':'USD', '':'T$' }
|
||||||
if 'LIMIT' in mg:
|
info['limitType'] = limits[mg['LIMIT']]
|
||||||
info['limitType'] = limits[mg['LIMIT']]
|
info['sb'] = mg['SB']
|
||||||
if 'GAME' in mg:
|
info['bb'] = mg['BB']
|
||||||
|
if mg['GAME'] != None:
|
||||||
(info['base'], info['category']) = games[mg['GAME']]
|
(info['base'], info['category']) = games[mg['GAME']]
|
||||||
if 'SB' in mg:
|
if mg['CURRENCY'] != None:
|
||||||
info['sb'] = mg['SB']
|
|
||||||
if 'BB' in mg:
|
|
||||||
info['bb'] = mg['BB']
|
|
||||||
if 'CURRENCY' in mg:
|
|
||||||
info['currency'] = currencies[mg['CURRENCY']]
|
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.
|
# NB: SB, BB must be interpreted as blinds or bets depending on limit type.
|
||||||
|
|
||||||
return info
|
return info
|
||||||
|
|
||||||
def readHandInfo(self, hand):
|
def readHandInfo(self, hand):
|
||||||
m = self.re_HandInfo.search(hand.handText,re.DOTALL)
|
m = self.re_HandInfo.search(hand.handText,re.DOTALL)
|
||||||
|
|
||||||
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)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
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")
|
||||||
hand.maxseats = 8 # assume 8-max until we see otherwise
|
|
||||||
if m.group('TABLEATTRIBUTES'):
|
if m.group('TABLEATTRIBUTES'):
|
||||||
m2 = re.search("(deep )?(\d+)( max)?", m.group('TABLEATTRIBUTES'))
|
m2 = self.re_Max.search(m.group('TABLEATTRIBUTES'))
|
||||||
hand.maxseats = int(m2.group(2))
|
if m2: hand.maxseats = int(m2.group('MAX'))
|
||||||
|
|
||||||
|
hand.tourNo = m.group('TOURNO')
|
||||||
|
if m.group('PLAY') != None:
|
||||||
|
hand.gametype['currency'] = 'play'
|
||||||
|
|
||||||
# These work, but the info is already in the Hand class - should be used for tourneys though.
|
# These work, but the info is already in the Hand class - should be used for tourneys though.
|
||||||
# m.group('SB')
|
# m.group('SB')
|
||||||
# m.group('BB')
|
# m.group('BB')
|
||||||
|
@ -190,6 +220,7 @@ follow : whether to tail -f the input"""
|
||||||
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')))
|
logging.debug("hand.addAnte(%s,%s)" %(player.group('PNAME'), player.group('ANTE')))
|
||||||
|
# if player.group() !=
|
||||||
hand.addAnte(player.group('PNAME'), player.group('ANTE'))
|
hand.addAnte(player.group('PNAME'), player.group('ANTE'))
|
||||||
|
|
||||||
def readBringIn(self, hand):
|
def readBringIn(self, hand):
|
||||||
|
@ -198,79 +229,46 @@ follow : whether to tail -f the input"""
|
||||||
logging.debug("Player bringing in: %s for %s" %(m.group('PNAME'), m.group('BRINGIN')))
|
logging.debug("Player bringing in: %s for %s" %(m.group('PNAME'), m.group('BRINGIN')))
|
||||||
hand.addBringIn(m.group('PNAME'), m.group('BRINGIN'))
|
hand.addBringIn(m.group('PNAME'), m.group('BRINGIN'))
|
||||||
else:
|
else:
|
||||||
logging.warning("No bringin found")
|
logging.warning("No bringin found, handid =%s" % hand.handid)
|
||||||
|
|
||||||
def readButton(self, hand):
|
def readButton(self, hand):
|
||||||
hand.buttonpos = int(self.re_Button.search(hand.handText).group('BUTTON'))
|
hand.buttonpos = int(self.re_Button.search(hand.handText).group('BUTTON'))
|
||||||
|
|
||||||
def readHeroCards(self, hand):
|
def readHeroCards(self, hand):
|
||||||
m = self.re_HeroCards.search(hand.handText)
|
# streets PREFLOP, PREDRAW, and THIRD are special cases beacause
|
||||||
if(m == None):
|
# we need to grab hero's cards
|
||||||
#Not involved in hand
|
for street in ('PREFLOP', 'DEAL'):
|
||||||
hand.involved = False
|
if street in hand.streets.keys():
|
||||||
else:
|
m = self.re_HeroCards.finditer(hand.streets[street])
|
||||||
hand.hero = m.group('PNAME')
|
for found in m:
|
||||||
# "2c, qh" -> set(["2c","qc"])
|
# if m == None:
|
||||||
# Also works with Omaha hands.
|
# hand.involved = False
|
||||||
cards = m.group('NEWCARDS')
|
# else:
|
||||||
cards = [c.strip() for c in cards.split(' ')]
|
hand.hero = found.group('PNAME')
|
||||||
hand.addHoleCards(cards, m.group('PNAME'))
|
newcards = found.group('NEWCARDS').split(' ')
|
||||||
|
hand.addHoleCards(street, hand.hero, closed=newcards, shown=False, mucked=False, dealt=True)
|
||||||
|
|
||||||
|
for street, text in hand.streets.iteritems():
|
||||||
|
if not text or street in ('PREFLOP', 'DEAL'): continue # already done these
|
||||||
|
m = self.re_HeroCards.finditer(hand.streets[street])
|
||||||
|
for found in m:
|
||||||
|
player = found.group('PNAME')
|
||||||
|
if found.group('NEWCARDS') == None:
|
||||||
|
newcards = []
|
||||||
|
else:
|
||||||
|
newcards = found.group('NEWCARDS').split(' ')
|
||||||
|
if found.group('OLDCARDS') == None:
|
||||||
|
oldcards = []
|
||||||
|
else:
|
||||||
|
oldcards = found.group('OLDCARDS').split(' ')
|
||||||
|
|
||||||
|
if street == 'THIRD' and len(oldcards) == 2: # hero in stud game
|
||||||
|
hand.hero = player
|
||||||
|
hand.dealt.add(player) # need this for stud??
|
||||||
|
hand.addHoleCards(street, player, closed=oldcards, open=newcards, shown=False, mucked=False, dealt=False)
|
||||||
|
else:
|
||||||
|
hand.addHoleCards(street, player, open=newcards, closed=oldcards, shown=False, mucked=False, dealt=False)
|
||||||
|
|
||||||
def readStudPlayerCards(self, hand, street):
|
|
||||||
# This could be the most tricky one to get right.
|
|
||||||
# It looks for cards dealt in 'street',
|
|
||||||
# which may or may not be in the section of the hand designated 'street' by markStreets earlier.
|
|
||||||
# Here's an example at FTP of what 'THIRD' and 'FOURTH' look like to hero PokerAscetic
|
|
||||||
#
|
|
||||||
#"*** 3RD STREET ***
|
|
||||||
#Dealt to BFK23 [Th]
|
|
||||||
#Dealt to cutiepr1nnymaid [8c]
|
|
||||||
#Dealt to PokerAscetic [7c 8s] [3h]
|
|
||||||
#..."
|
|
||||||
#
|
|
||||||
#"*** 4TH STREET ***
|
|
||||||
#Dealt to cutiepr1nnymaid [8c] [2s]
|
|
||||||
#Dealt to PokerAscetic [7c 8s 3h] [5s]
|
|
||||||
#..."
|
|
||||||
#Note that hero's first two holecards are only reported at 3rd street as 'old' cards.
|
|
||||||
logging.debug("readStudPlayerCards")
|
|
||||||
m = self.re_HeroCards.finditer(hand.streets[street])
|
|
||||||
for player in m:
|
|
||||||
logging.debug(player.groupdict())
|
|
||||||
(pname, oldcards, newcards) = (player.group('PNAME'), player.group('OLDCARDS'), player.group('NEWCARDS'))
|
|
||||||
if oldcards:
|
|
||||||
oldcards = [c.strip() for c in oldcards.split(' ')]
|
|
||||||
if newcards:
|
|
||||||
newcards = [c.strip() for c in newcards.split(' ')]
|
|
||||||
# options here:
|
|
||||||
# (1) we trust the hand will know what to do -- probably check that the old cards match what it already knows, and add the newcards to this street.
|
|
||||||
# (2) we're the experts at this particular history format and we know how we're going to be called (once for each street in Hand.streetList)
|
|
||||||
# so call addPlayerCards with the appropriate information.
|
|
||||||
# I favour (2) here but I'm afraid it is rather stud7-specific.
|
|
||||||
# in the following, the final list of cards will be in 'newcards' whilst if the first list exists (most of the time it does) it will be in 'oldcards'
|
|
||||||
if street=='ANTES':
|
|
||||||
return
|
|
||||||
elif street=='THIRD':
|
|
||||||
# we'll have observed hero holecards in CARDS and thirdstreet open cards in 'NEWCARDS'
|
|
||||||
# hero: [xx][o]
|
|
||||||
# others: [o]
|
|
||||||
hand.addPlayerCards(player = player.group('PNAME'), street = street, closed = oldcards, open = newcards)
|
|
||||||
elif street in ('FOURTH', 'FIFTH', 'SIXTH'):
|
|
||||||
# 4th:
|
|
||||||
# hero: [xxo] [o]
|
|
||||||
# others: [o] [o]
|
|
||||||
# 5th:
|
|
||||||
# hero: [xxoo] [o]
|
|
||||||
# others: [oo] [o]
|
|
||||||
# 6th:
|
|
||||||
# hero: [xxooo] [o]
|
|
||||||
# others: [ooo] [o]
|
|
||||||
hand.addPlayerCards(player = player.group('PNAME'), street = street, open = newcards)
|
|
||||||
# we may additionally want to check the earlier streets tally with what we have but lets trust it for now.
|
|
||||||
elif street=='SEVENTH' and newcards:
|
|
||||||
# hero: [xxoooo] [x]
|
|
||||||
# others: not reported.
|
|
||||||
hand.addPlayerCards(player = player.group('PNAME'), street = street, closed = newcards)
|
|
||||||
|
|
||||||
def readAction(self, hand, street):
|
def readAction(self, hand, street):
|
||||||
m = self.re_Action.finditer(hand.streets[street])
|
m = self.re_Action.finditer(hand.streets[street])
|
||||||
|
@ -308,6 +306,29 @@ follow : whether to tail -f the input"""
|
||||||
cards = cards.split(' ')
|
cards = cards.split(' ')
|
||||||
hand.addShownCards(cards=cards, player=m.group('PNAME'))
|
hand.addShownCards(cards=cards, player=m.group('PNAME'))
|
||||||
|
|
||||||
|
def guessMaxSeats(self, hand):
|
||||||
|
"""Return a guess at max_seats when not specified in HH."""
|
||||||
|
mo = self.maxOccSeat(hand)
|
||||||
|
|
||||||
|
if mo == 10: return 10 #that was easy
|
||||||
|
|
||||||
|
if hand.gametype['base'] == 'stud':
|
||||||
|
if mo <= 8: return 8
|
||||||
|
else: return mo
|
||||||
|
|
||||||
|
if hand.gametype['base'] == 'draw':
|
||||||
|
if mo <= 6: return 6
|
||||||
|
else: return mo
|
||||||
|
|
||||||
|
if mo == 2: return 2
|
||||||
|
if mo <= 6: return 6
|
||||||
|
return 9
|
||||||
|
|
||||||
|
def readOther(self, hand):
|
||||||
|
m = self.re_Mixed.search(self.in_path)
|
||||||
|
if m == None: hand.mixed = None
|
||||||
|
else:
|
||||||
|
hand.mixed = self.mixes[m.groupdict()['MIXED']]
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = OptionParser()
|
parser = OptionParser()
|
||||||
|
|
|
@ -28,6 +28,7 @@ import time
|
||||||
import fpdb_import
|
import fpdb_import
|
||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
import Configuration
|
import Configuration
|
||||||
|
import string
|
||||||
|
|
||||||
class GuiAutoImport (threading.Thread):
|
class GuiAutoImport (threading.Thread):
|
||||||
def __init__(self, settings, config):
|
def __init__(self, settings, config):
|
||||||
|
@ -153,37 +154,40 @@ class GuiAutoImport (threading.Thread):
|
||||||
# results to the same pipe. This means that self.path should be a a list of dirs
|
# results to the same pipe. This means that self.path should be a a list of dirs
|
||||||
# to watch.
|
# to watch.
|
||||||
if widget.get_active(): # toggled on
|
if widget.get_active(): # toggled on
|
||||||
self.doAutoImportBool = True
|
# - Does the lock acquisition need to be more sophisticated for multiple dirs?
|
||||||
widget.set_label(u' _Stop Autoimport ')
|
# (see comment above about what to do if pipe already open)
|
||||||
if self.pipe_to_hud is None:
|
# - Ideally we want to release the lock if the auto-import is killed by some
|
||||||
if os.name == 'nt':
|
# kind of exception - is this possible?
|
||||||
command = "python HUD_main.py" + " %s" % (self.database)
|
if self.settings['global_lock'].acquire(False): # returns false immediately if lock not acquired
|
||||||
bs = 0 # windows is not happy with line buffing here
|
print "\nGlobal lock taken ..."
|
||||||
self.pipe_to_hud = subprocess.Popen(command, bufsize = bs, stdin = subprocess.PIPE,
|
self.doAutoImportBool = True
|
||||||
universal_newlines=True)
|
widget.set_label(u' _Stop Autoimport ')
|
||||||
else:
|
if self.pipe_to_hud is None:
|
||||||
command = os.path.join(sys.path[0], 'HUD_main.py')
|
if os.name == 'nt':
|
||||||
#command = self.config.execution_path('HUD_main.py') # Hi Ray. Sorry about this, kludging.
|
command = "python HUD_main.py" + " " + self.settings['cl_options']
|
||||||
bs = 1
|
bs = 0 # windows is not happy with line buffing here
|
||||||
self.pipe_to_hud = subprocess.Popen((command, self.database), bufsize = bs, stdin = subprocess.PIPE,
|
self.pipe_to_hud = subprocess.Popen(command, bufsize = bs, stdin = subprocess.PIPE,
|
||||||
universal_newlines=True)
|
universal_newlines=True)
|
||||||
# self.pipe_to_hud = subprocess.Popen((command, self.database), bufsize = bs, stdin = subprocess.PIPE,
|
else:
|
||||||
# universal_newlines=True)
|
command = os.path.join(sys.path[0], 'HUD_main.py')
|
||||||
# command = command + " %s" % (self.database)
|
cl = [command, ] + string.split(self.settings['cl_options'])
|
||||||
# print "command = ", command
|
self.pipe_to_hud = subprocess.Popen(cl, bufsize = 1, stdin = subprocess.PIPE,
|
||||||
# self.pipe_to_hud = os.popen(command, 'w')
|
universal_newlines=True)
|
||||||
|
|
||||||
# Add directories to importer object.
|
# Add directories to importer object.
|
||||||
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 "Adding import directories - 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)
|
||||||
|
else:
|
||||||
|
print "auto-import aborted - global lock not available"
|
||||||
else: # toggled off
|
else: # toggled off
|
||||||
|
self.settings['global_lock'].release()
|
||||||
self.doAutoImportBool = False # do_import will return this and stop the gobject callback timer
|
self.doAutoImportBool = False # do_import will return this and stop the gobject callback timer
|
||||||
print "Stopping autoimport"
|
print "Stopping autoimport - global lock released."
|
||||||
if self.pipe_to_hud.poll() is not None:
|
if self.pipe_to_hud.poll() is not None:
|
||||||
print "HUD already terminated"
|
print "HUD already terminated"
|
||||||
else:
|
else:
|
||||||
|
@ -192,8 +196,6 @@ class GuiAutoImport (threading.Thread):
|
||||||
self.pipe_to_hud = None
|
self.pipe_to_hud = None
|
||||||
self.startButton.set_label(u' _Start Autoimport ')
|
self.startButton.set_label(u' _Start Autoimport ')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#end def GuiAutoImport.startClicked
|
#end def GuiAutoImport.startClicked
|
||||||
|
|
||||||
def get_vbox(self):
|
def get_vbox(self):
|
||||||
|
|
|
@ -49,37 +49,47 @@ class GuiBulkImport():
|
||||||
self.importer.RunImportThreaded()
|
self.importer.RunImportThreaded()
|
||||||
|
|
||||||
def load_clicked(self, widget, data=None):
|
def load_clicked(self, widget, data=None):
|
||||||
# get the dir to import from the chooser
|
# Does the lock acquisition need to be more sophisticated for multiple dirs?
|
||||||
self.inputFile = self.chooser.get_filename()
|
# (see comment above about what to do if pipe already open)
|
||||||
|
if self.settings['global_lock'].acquire(False): # returns false immediately if lock not acquired
|
||||||
|
try:
|
||||||
|
print "\nGlobal lock taken ..."
|
||||||
|
# get the dir to import from the chooser
|
||||||
|
self.inputFile = self.chooser.get_filename()
|
||||||
|
|
||||||
# get the import settings from the gui and save in the importer
|
# get the import settings from the gui and save in the importer
|
||||||
self.importer.setHandCount(int(self.spin_hands.get_text()))
|
self.importer.setHandCount(int(self.spin_hands.get_text()))
|
||||||
self.importer.setMinPrint(int(self.spin_hands.get_text()))
|
self.importer.setMinPrint(int(self.spin_hands.get_text()))
|
||||||
self.importer.setQuiet(self.chk_st_st.get_active())
|
self.importer.setQuiet(self.chk_st_st.get_active())
|
||||||
self.importer.setFailOnError(self.chk_fail.get_active())
|
self.importer.setFailOnError(self.chk_fail.get_active())
|
||||||
self.importer.setThreads(int(self.spin_threads.get_text()))
|
self.importer.setThreads(int(self.spin_threads.get_text()))
|
||||||
self.importer.setHandsInDB(self.n_hands_in_db)
|
self.importer.setHandsInDB(self.n_hands_in_db)
|
||||||
cb_model = self.cb_dropindexes.get_model()
|
cb_model = self.cb_dropindexes.get_model()
|
||||||
cb_index = self.cb_dropindexes.get_active()
|
cb_index = self.cb_dropindexes.get_active()
|
||||||
if cb_index:
|
if cb_index:
|
||||||
self.importer.setDropIndexes(cb_model[cb_index][0])
|
self.importer.setDropIndexes(cb_model[cb_index][0])
|
||||||
|
else:
|
||||||
|
self.importer.setDropIndexes("auto")
|
||||||
|
sitename = self.cbfilter.get_model()[self.cbfilter.get_active()][0]
|
||||||
|
self.lab_info.set_text("Importing")
|
||||||
|
|
||||||
|
self.importer.addBulkImportImportFileOrDir(self.inputFile, site = sitename)
|
||||||
|
self.importer.setCallHud(False)
|
||||||
|
starttime = time()
|
||||||
|
(stored, dups, partial, errs, ttime) = self.importer.runImport()
|
||||||
|
ttime = time() - starttime
|
||||||
|
if ttime == 0:
|
||||||
|
ttime = 1
|
||||||
|
print 'GuiBulkImport.import_dir done: Stored: %d \tDuplicates: %d \tPartial: %d \tErrors: %d in %s seconds - %d/sec'\
|
||||||
|
% (stored, dups, partial, errs, ttime, stored / ttime)
|
||||||
|
self.importer.clearFileList()
|
||||||
|
|
||||||
|
self.lab_info.set_text("Import finished")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
self.settings['global_lock'].release()
|
||||||
else:
|
else:
|
||||||
self.importer.setDropIndexes("auto")
|
print "bulk-import aborted - global lock not available"
|
||||||
sitename = self.cbfilter.get_model()[self.cbfilter.get_active()][0]
|
|
||||||
self.lab_info.set_text("Importing")
|
|
||||||
|
|
||||||
self.importer.addBulkImportImportFileOrDir(self.inputFile, site = sitename)
|
|
||||||
self.importer.setCallHud(False)
|
|
||||||
starttime = time()
|
|
||||||
(stored, dups, partial, errs, ttime) = self.importer.runImport()
|
|
||||||
ttime = time() - starttime
|
|
||||||
if ttime == 0:
|
|
||||||
ttime = 1
|
|
||||||
print 'GuiBulkImport.import_dir done: Stored: %d \tDuplicates: %d \tPartial: %d \tErrors: %d in %s seconds - %d/sec'\
|
|
||||||
% (stored, dups, partial, errs, ttime, stored / ttime)
|
|
||||||
self.importer.clearFileList()
|
|
||||||
|
|
||||||
self.lab_info.set_text("Import finished")
|
|
||||||
|
|
||||||
def get_vbox(self):
|
def get_vbox(self):
|
||||||
"""returns the vbox of this thread"""
|
"""returns the vbox of this thread"""
|
||||||
|
@ -88,8 +98,7 @@ class GuiBulkImport():
|
||||||
def __init__(self, settings, config):
|
def __init__(self, settings, config):
|
||||||
self.settings = settings
|
self.settings = settings
|
||||||
self.config = config
|
self.config = config
|
||||||
self.importer = fpdb_import.Importer(self, self.settings,
|
self.importer = fpdb_import.Importer(self, self.settings, config)
|
||||||
config)
|
|
||||||
|
|
||||||
self.vbox = gtk.VBox(False, 0)
|
self.vbox = gtk.VBox(False, 0)
|
||||||
self.vbox.show()
|
self.vbox.show()
|
||||||
|
@ -196,10 +205,11 @@ class GuiBulkImport():
|
||||||
self.load_button.show()
|
self.load_button.show()
|
||||||
|
|
||||||
# see how many hands are in the db and adjust accordingly
|
# see how many hands are in the db and adjust accordingly
|
||||||
tcursor = self.importer.fdb.db.cursor()
|
tcursor = self.importer.database.cursor
|
||||||
tcursor.execute("Select count(1) from Hands")
|
tcursor.execute("Select count(1) from Hands")
|
||||||
row = tcursor.fetchone()
|
row = tcursor.fetchone()
|
||||||
tcursor.close()
|
tcursor.close()
|
||||||
|
self.importer.database.rollback()
|
||||||
self.n_hands_in_db = row[0]
|
self.n_hands_in_db = row[0]
|
||||||
if self.n_hands_in_db == 0:
|
if self.n_hands_in_db == 0:
|
||||||
self.cb_dropindexes.set_active(2)
|
self.cb_dropindexes.set_active(2)
|
||||||
|
|
|
@ -38,20 +38,19 @@ except:
|
||||||
and HUD are NOT affected by this problem."""
|
and HUD are NOT affected by this problem."""
|
||||||
|
|
||||||
import fpdb_import
|
import fpdb_import
|
||||||
import fpdb_db
|
import Database
|
||||||
import Filters
|
import Filters
|
||||||
|
|
||||||
class GuiGraphViewer (threading.Thread):
|
class GuiGraphViewer (threading.Thread):
|
||||||
|
|
||||||
def __init__(self, db, settings, querylist, config, debug=True):
|
def __init__(self, querylist, config, debug=True):
|
||||||
"""Constructor for GraphViewer"""
|
"""Constructor for GraphViewer"""
|
||||||
self.debug=debug
|
self.sql = querylist
|
||||||
#print "start of GraphViewer constructor"
|
|
||||||
self.db=db
|
|
||||||
self.cursor=db.cursor
|
|
||||||
self.settings=settings
|
|
||||||
self.sql=querylist
|
|
||||||
self.conf = config
|
self.conf = config
|
||||||
|
self.debug = debug
|
||||||
|
#print "start of GraphViewer constructor"
|
||||||
|
self.db = Database.Database(self.conf, sql=self.sql)
|
||||||
|
|
||||||
|
|
||||||
filters_display = { "Heroes" : True,
|
filters_display = { "Heroes" : True,
|
||||||
"Sites" : True,
|
"Sites" : True,
|
||||||
|
@ -63,7 +62,7 @@ class GuiGraphViewer (threading.Thread):
|
||||||
"Button2" : True
|
"Button2" : True
|
||||||
}
|
}
|
||||||
|
|
||||||
self.filters = Filters.Filters(db, settings, config, querylist, display = filters_display)
|
self.filters = Filters.Filters(self.db, self.conf, self.sql, display = filters_display)
|
||||||
self.filters.registerButton1Name("Refresh Graph")
|
self.filters.registerButton1Name("Refresh Graph")
|
||||||
self.filters.registerButton1Callback(self.generateGraph)
|
self.filters.registerButton1Callback(self.generateGraph)
|
||||||
self.filters.registerButton2Name("Export to File")
|
self.filters.registerButton2Name("Export to File")
|
||||||
|
@ -90,7 +89,7 @@ class GuiGraphViewer (threading.Thread):
|
||||||
self.canvas = None
|
self.canvas = None
|
||||||
|
|
||||||
|
|
||||||
self.db.db.rollback()
|
self.db.rollback()
|
||||||
|
|
||||||
#################################
|
#################################
|
||||||
#
|
#
|
||||||
|
@ -126,7 +125,7 @@ class GuiGraphViewer (threading.Thread):
|
||||||
#end def get_vbox
|
#end def get_vbox
|
||||||
|
|
||||||
def clearGraphData(self):
|
def clearGraphData(self):
|
||||||
self.fig.clf()
|
self.fig.clear()
|
||||||
if self.canvas is not None:
|
if self.canvas is not None:
|
||||||
self.canvas.destroy()
|
self.canvas.destroy()
|
||||||
|
|
||||||
|
@ -146,7 +145,7 @@ class GuiGraphViewer (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],))
|
self.db.cursor.execute(self.sql.query['getPlayerId'], (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])
|
||||||
|
@ -154,7 +153,7 @@ class GuiGraphViewer (threading.Thread):
|
||||||
if not sitenos:
|
if not sitenos:
|
||||||
#Should probably pop up here.
|
#Should probably pop up here.
|
||||||
print "No sites selected - defaulting to PokerStars"
|
print "No sites selected - defaulting to PokerStars"
|
||||||
sitenos = [2]
|
return
|
||||||
|
|
||||||
if not playerids:
|
if not playerids:
|
||||||
print "No player ids found"
|
print "No player ids found"
|
||||||
|
@ -197,6 +196,7 @@ class GuiGraphViewer (threading.Thread):
|
||||||
|
|
||||||
self.graphBox.add(self.canvas)
|
self.graphBox.add(self.canvas)
|
||||||
self.canvas.show()
|
self.canvas.show()
|
||||||
|
self.canvas.draw()
|
||||||
#self.exportButton.set_sensitive(True)
|
#self.exportButton.set_sensitive(True)
|
||||||
#end of def showClicked
|
#end of def showClicked
|
||||||
|
|
||||||
|
@ -205,7 +205,7 @@ class GuiGraphViewer (threading.Thread):
|
||||||
# print "DEBUG: getRingProfitGraph"
|
# print "DEBUG: getRingProfitGraph"
|
||||||
start_date, end_date = self.filters.getDates()
|
start_date, end_date = self.filters.getDates()
|
||||||
|
|
||||||
#Buggered if I can find a way to do this 'nicely' take a list of intergers and longs
|
#Buggered if I can find a way to do this 'nicely' take a list of integers and longs
|
||||||
# and turn it into a tuple readale by sql.
|
# and turn it into a tuple readale by sql.
|
||||||
# [5L] into (5) not (5,) and [5L, 2829L] into (5, 2829)
|
# [5L] into (5) not (5,) and [5L, 2829L] into (5, 2829)
|
||||||
nametest = str(tuple(names))
|
nametest = str(tuple(names))
|
||||||
|
@ -226,10 +226,10 @@ class GuiGraphViewer (threading.Thread):
|
||||||
|
|
||||||
#print "DEBUG: sql query:"
|
#print "DEBUG: sql query:"
|
||||||
#print tmp
|
#print tmp
|
||||||
self.cursor.execute(tmp)
|
self.db.cursor.execute(tmp)
|
||||||
#returns (HandId,Winnings,Costs,Profit)
|
#returns (HandId,Winnings,Costs,Profit)
|
||||||
winnings = self.db.cursor.fetchall()
|
winnings = self.db.cursor.fetchall()
|
||||||
self.db.db.rollback()
|
self.db.rollback()
|
||||||
|
|
||||||
if(winnings == ()):
|
if(winnings == ()):
|
||||||
return None
|
return None
|
||||||
|
|
|
@ -20,34 +20,35 @@ import pygtk
|
||||||
pygtk.require('2.0')
|
pygtk.require('2.0')
|
||||||
import gtk
|
import gtk
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
from time import time, strftime
|
from time import time, strftime
|
||||||
|
|
||||||
import Card
|
import Card
|
||||||
import fpdb_import
|
import fpdb_import
|
||||||
|
import Database
|
||||||
import fpdb_db
|
import fpdb_db
|
||||||
import Filters
|
import Filters
|
||||||
import FpdbSQLQueries
|
|
||||||
|
|
||||||
class GuiPlayerStats (threading.Thread):
|
class GuiPlayerStats (threading.Thread):
|
||||||
def __init__(self, config, querylist, mainwin, debug=True):
|
def __init__(self, config, querylist, mainwin, debug=True):
|
||||||
self.debug=debug
|
self.debug = debug
|
||||||
self.conf=config
|
self.conf = config
|
||||||
self.main_window=mainwin
|
self.main_window = mainwin
|
||||||
|
self.sql = querylist
|
||||||
|
|
||||||
self.MYSQL_INNODB = 2
|
self.MYSQL_INNODB = 2
|
||||||
self.PGSQL = 3
|
self.PGSQL = 3
|
||||||
self.SQLITE = 4
|
self.SQLITE = 4
|
||||||
|
|
||||||
# create new db connection to avoid conflicts with other threads
|
# create new db connection to avoid conflicts with other threads
|
||||||
self.db = fpdb_db.fpdb_db()
|
self.db = Database.Database(self.conf, sql=self.sql)
|
||||||
self.db.do_connect(self.conf)
|
self.cursor = self.db.cursor
|
||||||
self.cursor=self.db.cursor
|
|
||||||
self.sql = querylist
|
|
||||||
|
|
||||||
settings = {}
|
settings = {}
|
||||||
settings.update(config.get_db_parameters())
|
settings.update(self.conf.get_db_parameters())
|
||||||
settings.update(config.get_tv_parameters())
|
settings.update(self.conf.get_tv_parameters())
|
||||||
settings.update(config.get_import_parameters())
|
settings.update(self.conf.get_import_parameters())
|
||||||
settings.update(config.get_default_paths())
|
settings.update(self.conf.get_default_paths())
|
||||||
|
|
||||||
# text used on screen stored here so that it can be configured
|
# text used on screen stored here so that it can be configured
|
||||||
self.filterText = {'handhead':'Hand Breakdown for all levels listed above'
|
self.filterText = {'handhead':'Hand Breakdown for all levels listed above'
|
||||||
|
@ -66,7 +67,7 @@ class GuiPlayerStats (threading.Thread):
|
||||||
"Button2" : True
|
"Button2" : True
|
||||||
}
|
}
|
||||||
|
|
||||||
self.filters = Filters.Filters(self.db, settings, config, querylist, display = filters_display)
|
self.filters = Filters.Filters(self.db, self.conf, self.sql, display = filters_display)
|
||||||
self.filters.registerButton1Name("_Filters")
|
self.filters.registerButton1Name("_Filters")
|
||||||
self.filters.registerButton1Callback(self.showDetailFilter)
|
self.filters.registerButton1Callback(self.showDetailFilter)
|
||||||
self.filters.registerButton2Name("_Refresh")
|
self.filters.registerButton2Name("_Refresh")
|
||||||
|
@ -216,7 +217,7 @@ class GuiPlayerStats (threading.Thread):
|
||||||
flags = [True]
|
flags = [True]
|
||||||
self.addTable(vbox1, 'playerDetailedStats', flags, playerids, sitenos, limits, seats, groups, dates)
|
self.addTable(vbox1, 'playerDetailedStats', flags, playerids, sitenos, limits, seats, groups, dates)
|
||||||
|
|
||||||
self.db.db.commit()
|
self.db.rollback()
|
||||||
print "Stats page displayed in %4.2f seconds" % (time() - starttime)
|
print "Stats page displayed in %4.2f seconds" % (time() - starttime)
|
||||||
#end def fillStatsFrame(self, vbox):
|
#end def fillStatsFrame(self, vbox):
|
||||||
|
|
||||||
|
@ -227,11 +228,6 @@ class GuiPlayerStats (threading.Thread):
|
||||||
if not flags: holecards = False
|
if not flags: holecards = False
|
||||||
else: holecards = flags[0]
|
else: holecards = flags[0]
|
||||||
|
|
||||||
|
|
||||||
self.stats_table = gtk.Table(1, 1, False)
|
|
||||||
self.stats_table.set_col_spacings(4)
|
|
||||||
self.stats_table.show()
|
|
||||||
|
|
||||||
tmp = self.sql.query[query]
|
tmp = self.sql.query[query]
|
||||||
tmp = self.refineQuery(tmp, flags, playerids, sitenos, limits, seats, groups, dates)
|
tmp = self.refineQuery(tmp, flags, playerids, sitenos, limits, seats, groups, dates)
|
||||||
self.cursor.execute(tmp)
|
self.cursor.execute(tmp)
|
||||||
|
@ -279,18 +275,16 @@ class GuiPlayerStats (threading.Thread):
|
||||||
|
|
||||||
while sqlrow < rows:
|
while sqlrow < rows:
|
||||||
treerow = []
|
treerow = []
|
||||||
if(row%2 == 0):
|
|
||||||
bgcolor = "white"
|
|
||||||
else:
|
|
||||||
bgcolor = "lightgrey"
|
|
||||||
for col,column in enumerate(cols_to_show):
|
for col,column in enumerate(cols_to_show):
|
||||||
if column[colalias] in colnames:
|
if column[colalias] in colnames:
|
||||||
value = result[sqlrow][colnames.index(column[colalias])]
|
value = result[sqlrow][colnames.index(column[colalias])]
|
||||||
if column[colalias] == 'plposition':
|
if column[colalias] == 'plposition':
|
||||||
if value == 'B':
|
if value == 'B':
|
||||||
value = 'BB'
|
value = 'BB'
|
||||||
if value == 'S':
|
elif value == 'S':
|
||||||
value = 'SB'
|
value = 'SB'
|
||||||
|
elif value == '0':
|
||||||
|
value = 'Btn'
|
||||||
else:
|
else:
|
||||||
if column[colalias] == 'game':
|
if column[colalias] == 'game':
|
||||||
if holecards:
|
if holecards:
|
||||||
|
@ -388,7 +382,8 @@ class GuiPlayerStats (threading.Thread):
|
||||||
|
|
||||||
# Group by position?
|
# Group by position?
|
||||||
if groups['posn']:
|
if groups['posn']:
|
||||||
query = query.replace("<position>", 'hp.position')
|
#query = query.replace("<position>", "case hp.position when '0' then 'Btn' else hp.position end")
|
||||||
|
query = query.replace("<position>", "hp.position")
|
||||||
# set flag in self.columns to show posn column
|
# set flag in self.columns to show posn column
|
||||||
[x for x in self.columns if x[0] == 'plposition'][0][1] = True
|
[x for x in self.columns if x[0] == 'plposition'][0][1] = True
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -20,31 +20,31 @@ import pygtk
|
||||||
pygtk.require('2.0')
|
pygtk.require('2.0')
|
||||||
import gtk
|
import gtk
|
||||||
import os
|
import os
|
||||||
|
from time import time, strftime
|
||||||
|
|
||||||
import fpdb_import
|
import fpdb_import
|
||||||
import fpdb_db
|
import Database
|
||||||
import Filters
|
import Filters
|
||||||
import FpdbSQLQueries
|
import FpdbSQLQueries
|
||||||
|
|
||||||
class GuiPositionalStats (threading.Thread):
|
class GuiPositionalStats (threading.Thread):
|
||||||
def __init__(self, config, querylist, debug=True):
|
def __init__(self, config, querylist, debug=True):
|
||||||
self.debug=debug
|
self.debug = debug
|
||||||
self.conf=config
|
self.conf = config
|
||||||
|
self.sql = querylist
|
||||||
self.MYSQL_INNODB = 2
|
self.MYSQL_INNODB = 2
|
||||||
self.PGSQL = 3
|
self.PGSQL = 3
|
||||||
self.SQLITE = 4
|
self.SQLITE = 4
|
||||||
|
|
||||||
# create new db connection to avoid conflicts with other threads
|
# create new db connection to avoid conflicts with other threads
|
||||||
self.db = fpdb_db.fpdb_db()
|
self.db = Database.Database(self.conf, sql=self.sql)
|
||||||
self.db.do_connect(self.conf)
|
self.cursor = self.db.cursor
|
||||||
self.cursor=self.db.cursor
|
|
||||||
self.sql = querylist
|
|
||||||
|
|
||||||
settings = {}
|
settings = {}
|
||||||
settings.update(config.get_db_parameters())
|
settings.update(self.conf.get_db_parameters())
|
||||||
settings.update(config.get_tv_parameters())
|
settings.update(self.conf.get_tv_parameters())
|
||||||
settings.update(config.get_import_parameters())
|
settings.update(self.conf.get_import_parameters())
|
||||||
settings.update(config.get_default_paths())
|
settings.update(self.conf.get_default_paths())
|
||||||
|
|
||||||
filters_display = { "Heroes" : True,
|
filters_display = { "Heroes" : True,
|
||||||
"Sites" : True,
|
"Sites" : True,
|
||||||
|
@ -58,21 +58,50 @@ class GuiPositionalStats (threading.Thread):
|
||||||
"Button2" : False
|
"Button2" : False
|
||||||
}
|
}
|
||||||
|
|
||||||
self.filters = Filters.Filters(self.db, settings, config, querylist, display = filters_display)
|
self.filters = Filters.Filters(self.db, self.conf, self.sql, display = filters_display)
|
||||||
self.filters.registerButton1Name("Refresh")
|
self.filters.registerButton1Name("Refresh")
|
||||||
self.filters.registerButton1Callback(self.refreshStats)
|
self.filters.registerButton1Callback(self.refreshStats)
|
||||||
|
|
||||||
|
# ToDo: store in config
|
||||||
|
# ToDo: create popup to adjust column config
|
||||||
|
# columns to display, keys match column name returned by sql, values in tuple are:
|
||||||
|
# is column displayed, column heading, xalignment, formatting
|
||||||
|
self.columns = [ ["game", True, "Game", 0.0, "%s"]
|
||||||
|
, ["hand", False, "Hand", 0.0, "%s"] # true not allowed for this line
|
||||||
|
, ["plposition", False, "Posn", 1.0, "%s"] # true not allowed for this line (set in code)
|
||||||
|
, ["n", True, "Hds", 1.0, "%d"]
|
||||||
|
, ["avgseats", True, "Seats", 1.0, "%3.1f"]
|
||||||
|
, ["vpip", True, "VPIP", 1.0, "%3.1f"]
|
||||||
|
, ["pfr", True, "PFR", 1.0, "%3.1f"]
|
||||||
|
, ["pf3", True, "PF3", 1.0, "%3.1f"]
|
||||||
|
, ["steals", True, "Steals", 1.0, "%3.1f"]
|
||||||
|
, ["saw_f", True, "Saw_F", 1.0, "%3.1f"]
|
||||||
|
, ["sawsd", True, "SawSD", 1.0, "%3.1f"]
|
||||||
|
, ["wtsdwsf", True, "WtSDwsF", 1.0, "%3.1f"]
|
||||||
|
, ["wmsd", True, "W$SD", 1.0, "%3.1f"]
|
||||||
|
, ["flafq", True, "FlAFq", 1.0, "%3.1f"]
|
||||||
|
, ["tuafq", True, "TuAFq", 1.0, "%3.1f"]
|
||||||
|
, ["rvafq", True, "RvAFq", 1.0, "%3.1f"]
|
||||||
|
, ["pofafq", False, "PoFAFq", 1.0, "%3.1f"]
|
||||||
|
, ["net", True, "Net($)", 1.0, "%6.2f"]
|
||||||
|
, ["bbper100", True, "bb/100", 1.0, "%4.2f"]
|
||||||
|
, ["rake", True, "Rake($)", 1.0, "%6.2f"]
|
||||||
|
, ["bb100xr", True, "bbxr/100", 1.0, "%4.2f"]
|
||||||
|
, ["variance", True, "Variance", 1.0, "%5.2f"]
|
||||||
|
]
|
||||||
|
|
||||||
self.stat_table = None
|
self.stat_table = None
|
||||||
self.stats_frame = None
|
self.stats_frame = None
|
||||||
|
self.stats_vbox = None
|
||||||
|
|
||||||
self.main_hbox = gtk.HBox(False, 0)
|
self.main_hbox = gtk.HBox(False, 0)
|
||||||
self.main_hbox.show()
|
self.main_hbox.show()
|
||||||
|
|
||||||
statsFrame = gtk.Frame("Stats:")
|
self.stats_frame = gtk.Frame()
|
||||||
statsFrame.set_label_align(0.0, 0.0)
|
self.stats_frame.set_label_align(0.0, 0.0)
|
||||||
statsFrame.show()
|
|
||||||
self.stats_frame = gtk.VBox(False, 0)
|
|
||||||
self.stats_frame.show()
|
self.stats_frame.show()
|
||||||
|
self.stats_vbox = gtk.VBox(False, 0)
|
||||||
|
self.stats_vbox.show()
|
||||||
|
|
||||||
# This could be stored in config eventually, or maybe configured in this window somehow.
|
# This could be stored in config eventually, or maybe configured in this window somehow.
|
||||||
# Each posncols element is the name of a column returned by the sql
|
# Each posncols element is the name of a column returned by the sql
|
||||||
|
@ -90,11 +119,11 @@ class GuiPositionalStats (threading.Thread):
|
||||||
, "PoFAFq", "Net($)", "bb/100", "$/hand", "Variance", "Hds"
|
, "PoFAFq", "Net($)", "bb/100", "$/hand", "Variance", "Hds"
|
||||||
)
|
)
|
||||||
|
|
||||||
self.fillStatsFrame(self.stats_frame)
|
self.fillStatsFrame(self.stats_vbox)
|
||||||
statsFrame.add(self.stats_frame)
|
self.stats_frame.add(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(statsFrame)
|
self.main_hbox.pack_start(self.stats_frame)
|
||||||
|
|
||||||
|
|
||||||
def get_vbox(self):
|
def get_vbox(self):
|
||||||
|
@ -107,9 +136,12 @@ class GuiPositionalStats (threading.Thread):
|
||||||
print "DEBUG: activesite set to %s" %(self.activesite)
|
print "DEBUG: activesite set to %s" %(self.activesite)
|
||||||
|
|
||||||
def refreshStats(self, widget, data):
|
def refreshStats(self, widget, data):
|
||||||
try: self.stats_table.destroy()
|
try: self.stats_vbox.destroy()
|
||||||
except AttributeError: pass
|
except AttributeError: pass
|
||||||
self.fillStatsFrame(self.stats_frame)
|
self.stats_vbox = gtk.VBox(False, 0)
|
||||||
|
self.stats_vbox.show()
|
||||||
|
self.stats_frame.add(self.stats_vbox)
|
||||||
|
self.fillStatsFrame(self.stats_vbox)
|
||||||
|
|
||||||
def fillStatsFrame(self, vbox):
|
def fillStatsFrame(self, vbox):
|
||||||
sites = self.filters.getSites()
|
sites = self.filters.getSites()
|
||||||
|
@ -144,66 +176,104 @@ class GuiPositionalStats (threading.Thread):
|
||||||
self.createStatsTable(vbox, playerids, sitenos, limits, seats, dates)
|
self.createStatsTable(vbox, playerids, sitenos, limits, seats, dates)
|
||||||
|
|
||||||
def createStatsTable(self, vbox, playerids, sitenos, limits, seats, dates):
|
def createStatsTable(self, vbox, playerids, sitenos, limits, seats, dates):
|
||||||
self.stats_table = gtk.Table(1, 1, False) # gtk table expands as required
|
|
||||||
self.stats_table.set_col_spacings(4)
|
|
||||||
self.stats_table.show()
|
|
||||||
vbox.add(self.stats_table)
|
|
||||||
|
|
||||||
|
starttime = time()
|
||||||
|
colalias,colshow,colheading,colxalign,colformat = 0,1,2,3,4
|
||||||
row = 0
|
row = 0
|
||||||
col = 0
|
col = 0
|
||||||
for t in self.posnheads:
|
|
||||||
l = gtk.Label(self.posnheads[col])
|
|
||||||
l.show()
|
|
||||||
self.stats_table.attach(l, col, col+1, row, row+1, yoptions=gtk.SHRINK)
|
|
||||||
col +=1
|
|
||||||
|
|
||||||
tmp = self.sql.query['playerStatsByPosition']
|
tmp = self.sql.query['playerStatsByPosition']
|
||||||
tmp = self.refineQuery(tmp, playerids, sitenos, limits, seats, dates)
|
tmp = self.refineQuery(tmp, playerids, sitenos, limits, seats, dates)
|
||||||
self.cursor.execute(tmp)
|
self.cursor.execute(tmp)
|
||||||
result = self.cursor.fetchall()
|
result = self.cursor.fetchall()
|
||||||
|
colnames = [desc[0].lower() for desc in self.cursor.description]
|
||||||
|
|
||||||
|
liststore = gtk.ListStore(*([str] * len(colnames)))
|
||||||
|
view = gtk.TreeView(model=liststore)
|
||||||
|
view.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_BOTH)
|
||||||
|
vbox.pack_start(view, expand=False, padding=3)
|
||||||
|
# left-aligned cells:
|
||||||
|
textcell = gtk.CellRendererText()
|
||||||
|
# centred cells:
|
||||||
|
textcell50 = gtk.CellRendererText()
|
||||||
|
textcell50.set_property('xalign', 0.5)
|
||||||
|
# right-aligned cells:
|
||||||
|
numcell = gtk.CellRendererText()
|
||||||
|
numcell.set_property('xalign', 1.0)
|
||||||
|
listcols = []
|
||||||
|
|
||||||
|
for t in self.posnheads:
|
||||||
|
listcols.append(gtk.TreeViewColumn(self.posnheads[col]))
|
||||||
|
view.append_column(listcols[col])
|
||||||
|
if col == 0:
|
||||||
|
listcols[col].pack_start(textcell, expand=True)
|
||||||
|
listcols[col].add_attribute(textcell, 'text', col)
|
||||||
|
listcols[col].set_expand(True)
|
||||||
|
elif col in (1, 2):
|
||||||
|
listcols[col].pack_start(textcell50, expand=True)
|
||||||
|
listcols[col].add_attribute(textcell50, 'text', col)
|
||||||
|
listcols[col].set_expand(True)
|
||||||
|
else:
|
||||||
|
listcols[col].pack_start(numcell, expand=True)
|
||||||
|
listcols[col].add_attribute(numcell, 'text', col)
|
||||||
|
listcols[col].set_expand(True)
|
||||||
|
col +=1
|
||||||
|
|
||||||
|
# Code below to be used when full column data structures implemented like in player stats:
|
||||||
|
|
||||||
|
# Create header row eg column: ("game", True, "Game", 0.0, "%s")
|
||||||
|
#for col, column in enumerate(cols_to_show):
|
||||||
|
# if column[colalias] == 'game' and holecards:
|
||||||
|
# s = [x for x in self.columns if x[colalias] == 'hand'][0][colheading]
|
||||||
|
# else:
|
||||||
|
# s = column[colheading]
|
||||||
|
# listcols.append(gtk.TreeViewColumn(s))
|
||||||
|
# view.append_column(listcols[col])
|
||||||
|
# if column[colformat] == '%s':
|
||||||
|
# if column[colxalign] == 0.0:
|
||||||
|
# listcols[col].pack_start(textcell, expand=True)
|
||||||
|
# listcols[col].add_attribute(textcell, 'text', col)
|
||||||
|
# else:
|
||||||
|
# listcols[col].pack_start(textcell50, expand=True)
|
||||||
|
# listcols[col].add_attribute(textcell50, 'text', col)
|
||||||
|
# listcols[col].set_expand(True)
|
||||||
|
# else:
|
||||||
|
# listcols[col].pack_start(numcell, expand=True)
|
||||||
|
# listcols[col].add_attribute(numcell, 'text', col)
|
||||||
|
# listcols[col].set_expand(True)
|
||||||
|
# #listcols[col].set_alignment(column[colxalign]) # no effect?
|
||||||
|
|
||||||
rows = len(result)
|
rows = len(result)
|
||||||
colnames = [desc[0].lower() for desc in self.cursor.description]
|
|
||||||
|
|
||||||
last_game,last_seats,sqlrow = "","",0
|
last_game,last_seats,sqlrow = "","",0
|
||||||
while sqlrow < rows:
|
while sqlrow < rows:
|
||||||
if(row%2 == 0):
|
|
||||||
bgcolor = "white"
|
|
||||||
else:
|
|
||||||
bgcolor = "lightgrey"
|
|
||||||
rowprinted=0
|
rowprinted=0
|
||||||
|
treerow = []
|
||||||
avgcol = colnames.index('avgseats')
|
avgcol = colnames.index('avgseats')
|
||||||
for col,colname in enumerate(self.posncols):
|
for col,colname in enumerate(self.posncols):
|
||||||
if colname in colnames:
|
if colname in colnames:
|
||||||
sqlcol = colnames.index(colname)
|
sqlcol = colnames.index(colname)
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
eb = gtk.EventBox()
|
|
||||||
eb.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(bgcolor))
|
|
||||||
# print blank row between levels:
|
|
||||||
if result[sqlrow][sqlcol]:
|
if result[sqlrow][sqlcol]:
|
||||||
if sqlrow == 0:
|
if sqlrow == 0:
|
||||||
l = gtk.Label(result[sqlrow][sqlcol])
|
value = result[sqlrow][sqlcol]
|
||||||
rowprinted=1
|
rowprinted=1
|
||||||
elif result[sqlrow][0] != last_game:
|
elif result[sqlrow][0] != last_game:
|
||||||
l = gtk.Label(' ')
|
value = ' '
|
||||||
elif 'show' in seats and seats['show'] and result[sqlrow][avgcol] != last_seats:
|
elif 'show' in seats and seats['show'] and result[sqlrow][avgcol] != last_seats:
|
||||||
l = gtk.Label(' ')
|
value = ' '
|
||||||
else:
|
else:
|
||||||
l = gtk.Label(result[sqlrow][sqlcol])
|
value = result[sqlrow][sqlcol]
|
||||||
rowprinted=1
|
rowprinted=1
|
||||||
else:
|
else:
|
||||||
l = gtk.Label(' ')
|
l = gtk.Label(' ')
|
||||||
if col == 0:
|
value = ' '
|
||||||
l.set_alignment(xalign=0.0, yalign=0.5)
|
if value and value != -999:
|
||||||
elif col == 1:
|
treerow.append(value)
|
||||||
l.set_alignment(xalign=0.5, yalign=0.5)
|
|
||||||
else:
|
else:
|
||||||
l.set_alignment(xalign=1.0, yalign=0.5)
|
treerow.append(' ')
|
||||||
eb.add(l)
|
iter = liststore.append(treerow)
|
||||||
self.stats_table.attach(eb, col, col+1, row+1, row+2, yoptions=gtk.SHRINK)
|
|
||||||
l.show()
|
|
||||||
eb.show()
|
|
||||||
last_game = result[sqlrow][0]
|
last_game = result[sqlrow][0]
|
||||||
last_seats = result[sqlrow][avgcol]
|
last_seats = result[sqlrow][avgcol]
|
||||||
if rowprinted:
|
if rowprinted:
|
||||||
|
@ -220,50 +290,36 @@ class GuiPositionalStats (threading.Thread):
|
||||||
|
|
||||||
# blank row between main stats and totals:
|
# blank row between main stats and totals:
|
||||||
col = 0
|
col = 0
|
||||||
if(row%2 == 0):
|
treerow = [' ' for x in self.posncols]
|
||||||
bgcolor = "white"
|
iter = liststore.append(treerow)
|
||||||
else:
|
|
||||||
bgcolor = "lightgrey"
|
|
||||||
eb = gtk.EventBox()
|
|
||||||
eb.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(bgcolor))
|
|
||||||
l = gtk.Label(' ')
|
|
||||||
eb.add(l)
|
|
||||||
self.stats_table.attach(eb, col, col+1, row+1, row+2, yoptions=gtk.SHRINK)
|
|
||||||
l.show()
|
|
||||||
eb.show()
|
|
||||||
row = row + 1
|
row = row + 1
|
||||||
|
|
||||||
for sqlrow in range(rows):
|
for sqlrow in range(rows):
|
||||||
if(row%2 == 0):
|
treerow = []
|
||||||
bgcolor = "white"
|
|
||||||
else:
|
|
||||||
bgcolor = "lightgrey"
|
|
||||||
for col,colname in enumerate(self.posncols):
|
for col,colname in enumerate(self.posncols):
|
||||||
if colname in colnames:
|
if colname in colnames:
|
||||||
sqlcol = colnames.index(colname)
|
sqlcol = colnames.index(colname)
|
||||||
elif colname != "plposition":
|
elif colname != "plposition":
|
||||||
continue
|
continue
|
||||||
eb = gtk.EventBox()
|
|
||||||
eb.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(bgcolor))
|
|
||||||
if colname == 'plposition':
|
if colname == 'plposition':
|
||||||
l = gtk.Label('Totals')
|
l = gtk.Label('Totals')
|
||||||
|
value = 'Totals'
|
||||||
elif result[sqlrow][sqlcol]:
|
elif result[sqlrow][sqlcol]:
|
||||||
l = gtk.Label(result[sqlrow][sqlcol])
|
l = gtk.Label(result[sqlrow][sqlcol])
|
||||||
|
value = result[sqlrow][sqlcol]
|
||||||
else:
|
else:
|
||||||
l = gtk.Label(' ')
|
l = gtk.Label(' ')
|
||||||
if col == 0:
|
value = ' '
|
||||||
l.set_alignment(xalign=0.0, yalign=0.5)
|
if value and value != -999:
|
||||||
elif col == 1:
|
treerow.append(value)
|
||||||
l.set_alignment(xalign=0.5, yalign=0.5)
|
|
||||||
else:
|
else:
|
||||||
l.set_alignment(xalign=1.0, yalign=0.5)
|
treerow.append(' ')
|
||||||
eb.add(l)
|
iter = liststore.append(treerow)
|
||||||
self.stats_table.attach(eb, col, col+1, row+1, row+2, yoptions=gtk.SHRINK)
|
|
||||||
l.show()
|
|
||||||
eb.show()
|
|
||||||
row = row + 1
|
row = row + 1
|
||||||
|
vbox.show_all()
|
||||||
|
|
||||||
self.db.db.rollback()
|
self.db.rollback()
|
||||||
|
print "Positional Stats page displayed in %4.2f seconds" % (time() - starttime)
|
||||||
#end def fillStatsFrame(self, vbox):
|
#end def fillStatsFrame(self, vbox):
|
||||||
|
|
||||||
def refineQuery(self, query, playerids, sitenos, limits, seats, dates):
|
def refineQuery(self, query, playerids, sitenos, limits, seats, dates):
|
||||||
|
|
|
@ -25,29 +25,28 @@ from numpy import diff, nonzero
|
||||||
|
|
||||||
import Card
|
import Card
|
||||||
import fpdb_import
|
import fpdb_import
|
||||||
import fpdb_db
|
import Database
|
||||||
import Filters
|
import Filters
|
||||||
import FpdbSQLQueries
|
import FpdbSQLQueries
|
||||||
|
|
||||||
class GuiSessionViewer (threading.Thread):
|
class GuiSessionViewer (threading.Thread):
|
||||||
def __init__(self, config, querylist, debug=True):
|
def __init__(self, config, querylist, debug=True):
|
||||||
self.debug=debug
|
self.debug = debug
|
||||||
self.conf=config
|
self.conf = config
|
||||||
|
self.sql = querylist
|
||||||
self.MYSQL_INNODB = 2
|
self.MYSQL_INNODB = 2
|
||||||
self.PGSQL = 3
|
self.PGSQL = 3
|
||||||
self.SQLITE = 4
|
self.SQLITE = 4
|
||||||
|
|
||||||
# create new db connection to avoid conflicts with other threads
|
# create new db connection to avoid conflicts with other threads
|
||||||
self.db = fpdb_db.fpdb_db()
|
self.db = Database.Database(self.conf, sql=self.sql)
|
||||||
self.db.do_connect(self.conf)
|
self.cursor = self.db.cursor
|
||||||
self.cursor=self.db.cursor
|
|
||||||
self.sql = querylist
|
|
||||||
|
|
||||||
settings = {}
|
settings = {}
|
||||||
settings.update(config.get_db_parameters())
|
settings.update(self.conf.get_db_parameters())
|
||||||
settings.update(config.get_tv_parameters())
|
settings.update(self.conf.get_tv_parameters())
|
||||||
settings.update(config.get_import_parameters())
|
settings.update(self.conf.get_import_parameters())
|
||||||
settings.update(config.get_default_paths())
|
settings.update(self.conf.get_default_paths())
|
||||||
|
|
||||||
# text used on screen stored here so that it can be configured
|
# text used on screen stored here so that it can be configured
|
||||||
self.filterText = {'handhead':'Hand Breakdown for all levels listed above'
|
self.filterText = {'handhead':'Hand Breakdown for all levels listed above'
|
||||||
|
@ -66,7 +65,7 @@ class GuiSessionViewer (threading.Thread):
|
||||||
"Button2" : True
|
"Button2" : True
|
||||||
}
|
}
|
||||||
|
|
||||||
self.filters = Filters.Filters(self.db, settings, config, querylist, display = filters_display)
|
self.filters = Filters.Filters(self.db, self.conf, self.sql, display = filters_display)
|
||||||
self.filters.registerButton2Name("_Refresh")
|
self.filters.registerButton2Name("_Refresh")
|
||||||
self.filters.registerButton2Callback(self.refreshStats)
|
self.filters.registerButton2Callback(self.refreshStats)
|
||||||
|
|
||||||
|
@ -195,7 +194,7 @@ class GuiSessionViewer (threading.Thread):
|
||||||
flags = [True]
|
flags = [True]
|
||||||
self.addTable(vbox1, 'playerDetailedStats', flags, playerids, sitenos, limits, seats)
|
self.addTable(vbox1, 'playerDetailedStats', flags, playerids, sitenos, limits, seats)
|
||||||
|
|
||||||
self.db.db.commit()
|
self.db.rollback()
|
||||||
print "Stats page displayed in %4.2f seconds" % (time() - starttime)
|
print "Stats page displayed in %4.2f seconds" % (time() - starttime)
|
||||||
#end def fillStatsFrame(self, vbox):
|
#end def fillStatsFrame(self, vbox):
|
||||||
|
|
||||||
|
|
|
@ -159,6 +159,50 @@
|
||||||
<location seat="9" x="70" y="53"> </location>
|
<location seat="9" x="70" y="53"> </location>
|
||||||
</layout>
|
</layout>
|
||||||
</site>
|
</site>
|
||||||
|
|
||||||
|
<site enabled="False"
|
||||||
|
site_name="Win2day"
|
||||||
|
table_finder="Win2day.exe"
|
||||||
|
screen_name="YOUR SCREEN NAME HERE"
|
||||||
|
site_path=""
|
||||||
|
HH_path=""
|
||||||
|
decoder="everleaf_decode_table"
|
||||||
|
converter="Win2dayToFpdb"
|
||||||
|
supported_games="holdem">
|
||||||
|
<layout fav_seat="0" height="547" max="8" width="794">
|
||||||
|
<location seat="1" x="640" y="64"> </location>
|
||||||
|
<location seat="2" x="650" y="230"> </location>
|
||||||
|
<location seat="3" x="650" y="385"> </location>
|
||||||
|
<location seat="4" x="588" y="425"> </location>
|
||||||
|
<location seat="5" x="92" y="425"> </location>
|
||||||
|
<location seat="6" x="0" y="373"> </location>
|
||||||
|
<location seat="7" x="0" y="223"> </location>
|
||||||
|
<location seat="8" x="25" y="50"> </location>
|
||||||
|
</layout>
|
||||||
|
<layout fav_seat="0" height="547" max="6" width="794">
|
||||||
|
<location seat="1" x="640" y="58"> </location>
|
||||||
|
<location seat="2" x="654" y="288"> </location>
|
||||||
|
<location seat="3" x="615" y="424"> </location>
|
||||||
|
<location seat="4" x="70" y="421"> </location>
|
||||||
|
<location seat="5" x="0" y="280"> </location>
|
||||||
|
<location seat="6" x="70" y="58"> </location>
|
||||||
|
</layout>
|
||||||
|
<layout fav_seat="0" height="547" max="2" width="794">
|
||||||
|
<location seat="1" x="651" y="288"> </location>
|
||||||
|
<location seat="2" x="10" y="288"> </location>
|
||||||
|
</layout>
|
||||||
|
<layout fav_seat="0" height="547" max="9" width="794">
|
||||||
|
<location seat="1" x="634" y="38"> </location>
|
||||||
|
<location seat="2" x="667" y="184"> </location>
|
||||||
|
<location seat="3" x="667" y="321"> </location>
|
||||||
|
<location seat="4" x="667" y="445"> </location>
|
||||||
|
<location seat="5" x="337" y="459"> </location>
|
||||||
|
<location seat="6" x="0" y="400"> </location>
|
||||||
|
<location seat="7" x="0" y="322"> </location>
|
||||||
|
<location seat="8" x="0" y="181"> </location>
|
||||||
|
<location seat="9" x="70" y="53"> </location>
|
||||||
|
</layout>
|
||||||
|
</site>
|
||||||
</supported_sites>
|
</supported_sites>
|
||||||
|
|
||||||
<supported_games>
|
<supported_games>
|
||||||
|
@ -292,6 +336,7 @@
|
||||||
<hhc site="PokerStars" converter="PokerStarsToFpdb"/>
|
<hhc site="PokerStars" converter="PokerStarsToFpdb"/>
|
||||||
<hhc site="Full Tilt Poker" converter="FulltiltToFpdb"/>
|
<hhc site="Full Tilt Poker" converter="FulltiltToFpdb"/>
|
||||||
<hhc site="Everleaf" converter="EverleafToFpdb"/>
|
<hhc site="Everleaf" converter="EverleafToFpdb"/>
|
||||||
|
<hhc site="Win2day" converter="Win2dayToFpdb"/>
|
||||||
</hhcs>
|
</hhcs>
|
||||||
|
|
||||||
<supported_databases>
|
<supported_databases>
|
||||||
|
@ -300,4 +345,3 @@
|
||||||
|
|
||||||
</FreePokerToolsConfig>
|
</FreePokerToolsConfig>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -29,12 +29,16 @@ Main for FreePokerTools HUD.
|
||||||
|
|
||||||
# Standard Library modules
|
# Standard Library modules
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
# redirect the stderr
|
|
||||||
errorfile = open('HUD-error.txt', 'w', 0)
|
|
||||||
sys.stderr = errorfile
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import Options
|
||||||
|
|
||||||
|
(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('fpdb-error-log.txt', 'w', 0)
|
||||||
|
sys.stderr = errorFile
|
||||||
|
|
||||||
import thread
|
import thread
|
||||||
import time
|
import time
|
||||||
import string
|
import string
|
||||||
|
@ -59,7 +63,7 @@ class HUD_main(object):
|
||||||
|
|
||||||
def __init__(self, db_name = 'fpdb'):
|
def __init__(self, db_name = 'fpdb'):
|
||||||
self.db_name = db_name
|
self.db_name = db_name
|
||||||
self.config = Configuration.Config()
|
self.config = Configuration.Config(file=options.config, dbname=options.dbname)
|
||||||
self.hud_dict = {}
|
self.hud_dict = {}
|
||||||
|
|
||||||
# a thread to read stdin
|
# a thread to read stdin
|
||||||
|
@ -198,17 +202,12 @@ class HUD_main(object):
|
||||||
self.db_connection.connection.rollback()
|
self.db_connection.connection.rollback()
|
||||||
|
|
||||||
if __name__== "__main__":
|
if __name__== "__main__":
|
||||||
sys.stderr.write("HUD_main starting\n")
|
|
||||||
|
|
||||||
# database name can be passed on command line
|
sys.stderr.write("HUD_main starting\n")
|
||||||
try:
|
sys.stderr.write("Using db name = %s\n" % (options.dbname))
|
||||||
db_name = sys.argv[1]
|
|
||||||
except:
|
|
||||||
db_name = 'fpdb'
|
|
||||||
sys.stderr.write("Using db name = %s\n" % (db_name))
|
|
||||||
|
|
||||||
# start the HUD_main object
|
# start the HUD_main object
|
||||||
hm = HUD_main(db_name = db_name)
|
hm = HUD_main(db_name = options.dbname)
|
||||||
|
|
||||||
# start the event loop
|
# start the event loop
|
||||||
gtk.main()
|
gtk.main()
|
||||||
|
|
704
pyfpdb/Hand.py
704
pyfpdb/Hand.py
File diff suppressed because it is too large
Load Diff
|
@ -28,91 +28,54 @@ import codecs
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
import operator
|
import operator
|
||||||
from xml.dom.minidom import Node
|
from xml.dom.minidom import Node
|
||||||
# from pokereval import PokerEval
|
|
||||||
import time
|
import time
|
||||||
import datetime
|
import datetime
|
||||||
import gettext
|
|
||||||
|
|
||||||
#from pokerengine.pokercards import *
|
|
||||||
# provides letter2name{}, letter2names{}, visible_card(), not_visible_card(), is_visible(), card_value(), class PokerCards
|
|
||||||
# but it's probably not installed so here are the ones we may want:
|
|
||||||
letter2name = {
|
|
||||||
'A': 'Ace',
|
|
||||||
'K': 'King',
|
|
||||||
'Q': 'Queen',
|
|
||||||
'J': 'Jack',
|
|
||||||
'T': 'Ten',
|
|
||||||
'9': 'Nine',
|
|
||||||
'8': 'Eight',
|
|
||||||
'7': 'Seven',
|
|
||||||
'6': 'Six',
|
|
||||||
'5': 'Five',
|
|
||||||
'4': 'Four',
|
|
||||||
'3': 'Trey',
|
|
||||||
'2': 'Deuce'
|
|
||||||
}
|
|
||||||
|
|
||||||
letter2names = {
|
|
||||||
'A': 'Aces',
|
|
||||||
'K': 'Kings',
|
|
||||||
'Q': 'Queens',
|
|
||||||
'J': 'Jacks',
|
|
||||||
'T': 'Tens',
|
|
||||||
'9': 'Nines',
|
|
||||||
'8': 'Eights',
|
|
||||||
'7': 'Sevens',
|
|
||||||
'6': 'Sixes',
|
|
||||||
'5': 'Fives',
|
|
||||||
'4': 'Fours',
|
|
||||||
'3': 'Treys',
|
|
||||||
'2': 'Deuces'
|
|
||||||
}
|
|
||||||
|
|
||||||
import gettext
|
import gettext
|
||||||
gettext.install('myapplication')
|
gettext.install('fpdb')
|
||||||
|
|
||||||
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):
|
def __init__(self, in_path = '-', out_path = '-', sitename = None, follow=False, index=0):
|
||||||
logging.info("HandHistory init called")
|
logging.info("HandHistory init")
|
||||||
|
|
||||||
# default filetype and codepage. Subclasses should set these properly.
|
# default filetype and codepage. Subclasses should set these properly.
|
||||||
self.filetype = "text"
|
self.filetype = "text"
|
||||||
self.codepage = "utf8"
|
self.codepage = "utf8"
|
||||||
|
self.index = 0
|
||||||
|
|
||||||
self.in_path = in_path
|
self.in_path = in_path
|
||||||
self.out_path = out_path
|
self.out_path = out_path
|
||||||
if self.out_path == '-':
|
|
||||||
# write to stdout
|
self.processedHands = []
|
||||||
|
|
||||||
|
if in_path == '-':
|
||||||
|
self.in_fh = sys.stdin
|
||||||
|
|
||||||
|
if out_path == '-':
|
||||||
self.out_fh = sys.stdout
|
self.out_fh = sys.stdout
|
||||||
else:
|
else:
|
||||||
# TODO: out_path should be sanity checked before opening. Perhaps in fpdb_import?
|
# TODO: out_path should be sanity checked.
|
||||||
# I'm not sure what we're looking for, although we don't want out_path==in_path!='-'
|
self.out_fh = open(self.out_path, 'w')
|
||||||
self.out_fh = open(self.out_path, 'w') # doomswitch is now on :|
|
|
||||||
self.sitename = sitename
|
self.sitename = sitename
|
||||||
self.follow = follow
|
self.follow = follow
|
||||||
self.compiledPlayers = set()
|
self.compiledPlayers = set()
|
||||||
self.maxseats = 10
|
self.maxseats = 10
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
#TODO : I got rid of most of the hhdir stuff.
|
return """
|
||||||
tmp = "HandHistoryConverter: '%s'\n" % (self.sitename)
|
HandHistoryConverter: '%(sitename)s'
|
||||||
#tmp = tmp + "\thhbase: '%s'\n" % (self.hhbase)
|
filetype: '%(filetype)s'
|
||||||
#tmp = tmp + "\thhdir: '%s'\n" % (self.hhdir)
|
in_path: '%(in_path)s'
|
||||||
tmp = tmp + "\tfiletype: '%s'\n" % (self.filetype)
|
out_path: '%(out_path)s'
|
||||||
tmp = tmp + "\tinfile: '%s'\n" % (self.in_path)
|
""" % { 'sitename':self.sitename, 'filetype':self.filetype, 'in_path':self.in_path, 'out_path':self.out_path }
|
||||||
tmp = tmp + "\toutfile: '%s'\n" % (self.out_path)
|
|
||||||
#tmp = tmp + "\tgametype: '%s'\n" % (self.gametype[0])
|
|
||||||
#tmp = tmp + "\tgamebase: '%s'\n" % (self.gametype[1])
|
|
||||||
#tmp = tmp + "\tlimit: '%s'\n" % (self.gametype[2])
|
|
||||||
#tmp = tmp + "\tsb/bb: '%s/%s'\n" % (self.gametype[3], self.gametype[4])
|
|
||||||
return tmp
|
|
||||||
|
|
||||||
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()
|
||||||
|
@ -129,7 +92,7 @@ Otherwise, finish at eof...
|
||||||
handsList = self.allHandsAsList()
|
handsList = self.allHandsAsList()
|
||||||
logging.info("Parsing %d hands" % len(handsList))
|
logging.info("Parsing %d hands" % len(handsList))
|
||||||
for handText in handsList:
|
for handText in handsList:
|
||||||
self.processHand(handText)
|
self.processedHands.append(self.processHand(handText))
|
||||||
numHands= len(handsList)
|
numHands= len(handsList)
|
||||||
endtime = time.time()
|
endtime = time.time()
|
||||||
print "read %d hands in %.3f seconds" % (numHands, endtime - starttime)
|
print "read %d hands in %.3f seconds" % (numHands, endtime - starttime)
|
||||||
|
@ -213,6 +176,7 @@ which it expects to find at self.re_TailSplitHands -- see for e.g. Everleaf.py.
|
||||||
def processHand(self, handText):
|
def processHand(self, handText):
|
||||||
gametype = self.determineGameType(handText)
|
gametype = self.determineGameType(handText)
|
||||||
logging.debug("gametype %s" % gametype)
|
logging.debug("gametype %s" % gametype)
|
||||||
|
hand = None
|
||||||
if gametype is None:
|
if gametype is None:
|
||||||
l = None
|
l = None
|
||||||
gametype = "unmatched"
|
gametype = "unmatched"
|
||||||
|
@ -224,9 +188,7 @@ which it expects to find at self.re_TailSplitHands -- see for e.g. Everleaf.py.
|
||||||
base = gametype['base']
|
base = gametype['base']
|
||||||
limit = gametype['limitType']
|
limit = gametype['limitType']
|
||||||
l = [type] + [base] + [limit]
|
l = [type] + [base] + [limit]
|
||||||
hand = None
|
|
||||||
if l in self.readSupportedGames():
|
if l in self.readSupportedGames():
|
||||||
hand = None
|
|
||||||
if gametype['base'] == 'hold':
|
if gametype['base'] == 'hold':
|
||||||
logging.debug("hand = Hand.HoldemOmahaHand(self, self.sitename, gametype, handtext)")
|
logging.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)
|
||||||
|
@ -238,7 +200,9 @@ which it expects to find at self.re_TailSplitHands -- see for e.g. Everleaf.py.
|
||||||
logging.info("Unsupported game type: %s" % gametype)
|
logging.info("Unsupported game type: %s" % gametype)
|
||||||
|
|
||||||
if hand:
|
if hand:
|
||||||
|
# print hand
|
||||||
hand.writeHand(self.out_fh)
|
hand.writeHand(self.out_fh)
|
||||||
|
return hand
|
||||||
else:
|
else:
|
||||||
logging.info("Unsupported game type: %s" % gametype)
|
logging.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?
|
||||||
|
@ -306,6 +270,11 @@ or None if we fail to get the info """
|
||||||
def readCollectPot(self, hand): abstract
|
def readCollectPot(self, hand): abstract
|
||||||
def readShownCards(self, hand): abstract
|
def readShownCards(self, hand): abstract
|
||||||
|
|
||||||
|
# Some sites do odd stuff that doesn't fall in to the normal HH parsing.
|
||||||
|
# e.g., FTP doesn't put mixed game info in the HH, but puts in in the
|
||||||
|
# file name. Use readOther() to clean up those messes.
|
||||||
|
def readOther(self, hand): pass
|
||||||
|
|
||||||
# Some sites don't report the rake. This will be called at the end of the hand after the pot total has been calculated
|
# Some sites don't report the rake. This will be called at the end of the hand after the pot total has been calculated
|
||||||
# an inheriting class can calculate it for the specific site if need be.
|
# an inheriting class can calculate it for the specific site if need be.
|
||||||
def getRake(self, hand):
|
def getRake(self, hand):
|
||||||
|
@ -349,6 +318,7 @@ or None if we fail to get the info """
|
||||||
self.filetype = filetype
|
self.filetype = filetype
|
||||||
self.codepage = codepage
|
self.codepage = codepage
|
||||||
|
|
||||||
|
#This function doesn't appear to be used
|
||||||
def splitFileIntoHands(self):
|
def splitFileIntoHands(self):
|
||||||
hands = []
|
hands = []
|
||||||
self.obs = self.obs.strip()
|
self.obs = self.obs.strip()
|
||||||
|
@ -370,7 +340,9 @@ or None if we fail to get the info """
|
||||||
else:
|
else:
|
||||||
logging.debug("Opening %s with %s" % (self.in_path, self.codepage))
|
logging.debug("Opening %s with %s" % (self.in_path, self.codepage))
|
||||||
in_fh = codecs.open(self.in_path, 'r', self.codepage)
|
in_fh = codecs.open(self.in_path, 'r', self.codepage)
|
||||||
|
in_fh.seek(self.index)
|
||||||
self.obs = in_fh.read()
|
self.obs = in_fh.read()
|
||||||
|
self.index = in_fh.tell()
|
||||||
in_fh.close()
|
in_fh.close()
|
||||||
elif(self.filetype == "xml"):
|
elif(self.filetype == "xml"):
|
||||||
try:
|
try:
|
||||||
|
@ -379,10 +351,39 @@ or None if we fail to get the info """
|
||||||
except:
|
except:
|
||||||
traceback.print_exc(file=sys.stderr)
|
traceback.print_exc(file=sys.stderr)
|
||||||
|
|
||||||
|
def guessMaxSeats(self, hand):
|
||||||
|
"""Return a guess at max_seats when not specified in HH."""
|
||||||
|
mo = self.maxOccSeat(hand)
|
||||||
|
|
||||||
|
if mo == 10: return 10 #that was easy
|
||||||
|
|
||||||
|
if hand.gametype['base'] == 'stud':
|
||||||
|
if mo <= 8: return 8
|
||||||
|
else: return mo
|
||||||
|
|
||||||
|
if hand.gametype['base'] == 'draw':
|
||||||
|
if mo <= 6: return 6
|
||||||
|
else: return mo
|
||||||
|
|
||||||
|
if mo == 2: return 2
|
||||||
|
if mo <= 6: return 6
|
||||||
|
return 10
|
||||||
|
|
||||||
|
def maxOccSeat(self, hand):
|
||||||
|
max = 0
|
||||||
|
for player in hand.players:
|
||||||
|
if player[0] > max: max = player[0]
|
||||||
|
return max
|
||||||
|
|
||||||
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 True
|
||||||
|
|
||||||
|
def getProcessedHands(self):
|
||||||
|
return self.processedHands
|
||||||
|
|
||||||
def getProcessedFile(self):
|
def getProcessedFile(self):
|
||||||
return self.out_path
|
return self.out_path
|
||||||
|
|
||||||
|
def getLastCharacterRead(self):
|
||||||
|
return self.index
|
||||||
|
|
|
@ -34,10 +34,13 @@ import gobject
|
||||||
|
|
||||||
# FreePokerTools modules
|
# FreePokerTools modules
|
||||||
from Mucked import Aux_Window
|
from Mucked import Aux_Window
|
||||||
|
from Mucked import Seat_Window
|
||||||
|
from Mucked import Aux_Seats
|
||||||
|
|
||||||
class Hello(Aux_Window):
|
class Hello(Aux_Window):
|
||||||
"""A 'Hello World' Aux_Window demo."""
|
"""A 'Hello World' Aux_Window demo."""
|
||||||
def create(self):
|
def create(self):
|
||||||
|
print "creating Hello"
|
||||||
# This demo simply creates a label in a window.
|
# This demo simply creates a label in a window.
|
||||||
self.container = gtk.Window()
|
self.container = gtk.Window()
|
||||||
self.container.add(gtk.Label("Hello World"))
|
self.container.add(gtk.Label("Hello World"))
|
||||||
|
@ -99,15 +102,18 @@ class Hello_plus(Aux_Window):
|
||||||
# hands played that was updated in the "update_data()" function.
|
# hands played that was updated in the "update_data()" function.
|
||||||
self.label.set_text("Hello %s\nYou have played %d hands\n on %s." % (self.hero, self.hands_played, self.site))
|
self.label.set_text("Hello %s\nYou have played %d hands\n on %s." % (self.hero, self.hands_played, self.site))
|
||||||
|
|
||||||
class Hello_Menu(Aux_Window):
|
class Hello_Seats(Aux_Seats):
|
||||||
"""A 'Hello World' Aux_Window demo."""
|
"""A 'Hello World' Seat_Window demo."""
|
||||||
def create(self):
|
|
||||||
# This demo puts a menu item on the HUD mainwindow.
|
|
||||||
self.item = gtk.MenuItem('Print cards')
|
|
||||||
self.hud.menu.append(self.item)
|
|
||||||
self.item.connect("activate", self.print_cards)
|
|
||||||
self.item.show()
|
|
||||||
|
|
||||||
def print_cards(self, *args):
|
def create_contents(self, container, i):
|
||||||
# callback for the menu item
|
container.label = gtk.Label("empty")
|
||||||
print "cards =", self.hud.cards
|
container.add(container.label)
|
||||||
|
container.show_all()
|
||||||
|
|
||||||
|
def update_contents(self, container, i):
|
||||||
|
if i == "common": return
|
||||||
|
id = self.get_id_from_seat(i)
|
||||||
|
if id == None:
|
||||||
|
container.label.set_text("empty")
|
||||||
|
else:
|
||||||
|
container.label.set_text("player = %s" % self.hud.stat_dict[id]['screen_name'])
|
||||||
|
|
|
@ -43,7 +43,7 @@ import Configuration
|
||||||
import Stats
|
import Stats
|
||||||
import Mucked
|
import Mucked
|
||||||
import Database
|
import Database
|
||||||
import HUD_main
|
#import HUD_main
|
||||||
|
|
||||||
def importName(module_name, name):
|
def importName(module_name, name):
|
||||||
"""Import a named object 'name' from module 'module_name'."""
|
"""Import a named object 'name' from module 'module_name'."""
|
||||||
|
@ -445,10 +445,10 @@ class Stat_Window:
|
||||||
|
|
||||||
Stats.do_tip(e_box[r][c], 'stuff')
|
Stats.do_tip(e_box[r][c], 'stuff')
|
||||||
if usegtkframes:
|
if usegtkframes:
|
||||||
grid.attach(self.frame[r][c], c, c+1, r, r+1, xpadding = 0, ypadding = 0)
|
grid.attach(self.frame[r][c], c, c+1, r, r+1, xpadding = game.xpad, ypadding = game.ypad)
|
||||||
self.frame[r][c].add(e_box[r][c])
|
self.frame[r][c].add(e_box[r][c])
|
||||||
else:
|
else:
|
||||||
grid.attach(e_box[r][c], c, c+1, r, r+1, xpadding = 0, ypadding = 0)
|
grid.attach(e_box[r][c], c, c+1, r, r+1, xpadding = game.xpad, ypadding = game.ypad)
|
||||||
label[r].append( gtk.Label('xxx') )
|
label[r].append( gtk.Label('xxx') )
|
||||||
|
|
||||||
if usegtkframes:
|
if usegtkframes:
|
||||||
|
|
291
pyfpdb/Mucked.py
291
pyfpdb/Mucked.py
|
@ -35,28 +35,20 @@ import gobject
|
||||||
# FreePokerTools modules
|
# FreePokerTools modules
|
||||||
import Configuration
|
import Configuration
|
||||||
import Database
|
import Database
|
||||||
|
import Card
|
||||||
|
|
||||||
class Aux_Window:
|
class Aux_Window(object):
|
||||||
def __init__(self, hud, params, config):
|
def __init__(self, hud, params, config):
|
||||||
self.hud = hud
|
self.hud = hud
|
||||||
self.params = params
|
self.params = params
|
||||||
self.config = config
|
self.config = config
|
||||||
|
|
||||||
def update_data(self, *args):
|
# Override these methods as needed
|
||||||
pass
|
def update_data(self, *args): pass
|
||||||
|
def update_gui(self, *args): pass
|
||||||
def update_gui(self, *args):
|
def create(self, *args): pass
|
||||||
pass
|
def relocate(self, *args): pass
|
||||||
|
def save_layout(self, *args): pass
|
||||||
def create(self, *args):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def relocate(self, *args):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def save_layout(self, *args):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def destroy(self):
|
def destroy(self):
|
||||||
try:
|
try:
|
||||||
self.container.destroy()
|
self.container.destroy()
|
||||||
|
@ -67,27 +59,40 @@ class Aux_Window:
|
||||||
# Some utility routines useful for Aux_Windows
|
# Some utility routines useful for Aux_Windows
|
||||||
#
|
#
|
||||||
def get_card_images(self):
|
def get_card_images(self):
|
||||||
card_images = {}
|
|
||||||
suits = ('S', 'H', 'D', 'C')
|
card_images = 53 * [0]
|
||||||
ranks = ('A', 'K', 'Q', 'J', 'T', '9', '8', '7', '6', '5', '4', '3', '2', 'B')
|
suits = ('s', 'h', 'd', 'c')
|
||||||
|
ranks = (14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2)
|
||||||
pb = gtk.gdk.pixbuf_new_from_file(self.config.execution_path(self.params['deck']))
|
pb = gtk.gdk.pixbuf_new_from_file(self.config.execution_path(self.params['deck']))
|
||||||
|
|
||||||
for j in range(0, 14):
|
for j in range(0, 13):
|
||||||
for i in range(0, 4):
|
for i in range(0, 4):
|
||||||
temp_pb = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, pb.get_has_alpha(), pb.get_bits_per_sample(), 30, 42)
|
card_images[Card.cardFromValueSuit(ranks[j], suits[i])] = self.cropper(pb, i, j)
|
||||||
pb.copy_area(30*j, 42*i, 30, 42, temp_pb, 0, 0)
|
temp_pb = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, pb.get_has_alpha(), pb.get_bits_per_sample(), 30, 42)
|
||||||
card_images[(ranks[j], suits[i])] = temp_pb
|
# also pick out a card back and store in [0]
|
||||||
|
card_images[0] = self.cropper(pb, 2, 13)
|
||||||
return(card_images)
|
return(card_images)
|
||||||
# cards are 30 wide x 42 high
|
# cards are 30 wide x 42 high
|
||||||
|
|
||||||
def split_cards(self, card):
|
def cropper(self, pb, i, j):
|
||||||
if card == 'xx': return ('B', 'S')
|
"""Crop out a card image given an FTP deck and the i, j position."""
|
||||||
return (card[0], card[1].upper())
|
temp_pb = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, pb.get_has_alpha(), pb.get_bits_per_sample(), 30, 42)
|
||||||
|
pb.copy_area(30*j, 42*i, 30, 42, temp_pb, 0, 0)
|
||||||
|
return temp_pb
|
||||||
|
|
||||||
def has_cards(self, cards):
|
def has_cards(self, cards):
|
||||||
|
"""Returns the number of cards in the list."""
|
||||||
|
n = 0
|
||||||
for c in cards:
|
for c in cards:
|
||||||
if c in set('shdc'): return True
|
if c != None and c > 0: n = n + 1
|
||||||
return False
|
return n
|
||||||
|
|
||||||
|
def get_id_from_seat(self, seat):
|
||||||
|
"""Determine player id from seat number, given stat_dict."""
|
||||||
|
for id, dict in self.hud.stat_dict.iteritems():
|
||||||
|
if seat == dict['seat']:
|
||||||
|
return id
|
||||||
|
return None
|
||||||
|
|
||||||
class Stud_mucked(Aux_Window):
|
class Stud_mucked(Aux_Window):
|
||||||
def __init__(self, hud, config, params):
|
def __init__(self, hud, config, params):
|
||||||
|
@ -108,9 +113,10 @@ class Stud_mucked(Aux_Window):
|
||||||
|
|
||||||
def create(self):
|
def create(self):
|
||||||
|
|
||||||
self.container =gtk.Window()
|
self.container = gtk.Window()
|
||||||
self.vbox = gtk.VBox()
|
self.vbox = gtk.VBox()
|
||||||
self.container.add(self.vbox)
|
self.container.add(self.vbox)
|
||||||
|
self.container.set_title(self.hud.table.name)
|
||||||
|
|
||||||
self.mucked_list.create(self.vbox)
|
self.mucked_list.create(self.vbox)
|
||||||
self.mucked_cards.create(self.vbox)
|
self.mucked_cards.create(self.vbox)
|
||||||
|
@ -207,7 +213,9 @@ class Stud_list:
|
||||||
# find the hero's seat from the stat_dict
|
# find the hero's seat from the stat_dict
|
||||||
for stat in self.parent.hud.stat_dict.itervalues():
|
for stat in self.parent.hud.stat_dict.itervalues():
|
||||||
if stat['screen_name'] == hero:
|
if stat['screen_name'] == hero:
|
||||||
return self.parent.hud.cards[stat['seat']][0:6]
|
return Card.valueSuitFromCard(self.parent.hud.cards[stat['seat']][0]) +\
|
||||||
|
Card.valueSuitFromCard(self.parent.hud.cards[stat['seat']][1]) +\
|
||||||
|
Card.valueSuitFromCard(self.parent.hud.cards[stat['seat']][2])
|
||||||
return "xxxxxx"
|
return "xxxxxx"
|
||||||
|
|
||||||
def update_gui(self, new_hand_id):
|
def update_gui(self, new_hand_id):
|
||||||
|
@ -240,7 +248,7 @@ class Stud_cards:
|
||||||
|
|
||||||
for r in range(0, self.rows):
|
for r in range(0, self.rows):
|
||||||
for c in range(0, self.cols):
|
for c in range(0, self.cols):
|
||||||
self.seen_cards[(c, r)] = gtk.image_new_from_pixbuf(self.card_images[('B', 'S')])
|
self.seen_cards[(c, r)] = gtk.image_new_from_pixbuf(self.card_images[(0)])
|
||||||
self.eb[(c, r)]= gtk.EventBox()
|
self.eb[(c, r)]= gtk.EventBox()
|
||||||
|
|
||||||
# set up the contents for the cells
|
# set up the contents for the cells
|
||||||
|
@ -287,11 +295,11 @@ class Stud_cards:
|
||||||
self.clear()
|
self.clear()
|
||||||
for c, cards in self.parent.hud.cards.iteritems():
|
for c, cards in self.parent.hud.cards.iteritems():
|
||||||
self.grid_contents[(1, c - 1)].set_text(self.get_screen_name(c))
|
self.grid_contents[(1, c - 1)].set_text(self.get_screen_name(c))
|
||||||
for i in ((0, cards[0:2]), (1, cards[2:4]), (2, cards[4:6]), (3, cards[6:8]),
|
for i in ((0, cards[0]), (1, cards[1]), (2, cards[2]), (3, cards[3]),
|
||||||
(4, cards[8:10]), (5, cards[10:12]), (6, cards[12:14])):
|
(4, cards[4]), (5, cards[5]), (6, cards[6])):
|
||||||
if not i[1] == "xx":
|
if not i[1] == 0:
|
||||||
self.seen_cards[(i[0], c - 1)]. \
|
self.seen_cards[(i[0], c - 1)]. \
|
||||||
set_from_pixbuf(self.card_images[self.parent.split_cards(i[1])])
|
set_from_pixbuf(self.card_images[i[1]])
|
||||||
## action in tool tips for 3rd street cards
|
## action in tool tips for 3rd street cards
|
||||||
for c in (0, 1, 2):
|
for c in (0, 1, 2):
|
||||||
for r in range(0, self.rows):
|
for r in range(0, self.rows):
|
||||||
|
@ -314,90 +322,155 @@ class Stud_cards:
|
||||||
for r in range(0, self.rows):
|
for r in range(0, self.rows):
|
||||||
self.grid_contents[(1, r)].set_text(" ")
|
self.grid_contents[(1, r)].set_text(" ")
|
||||||
for c in range(0, 7):
|
for c in range(0, 7):
|
||||||
self.seen_cards[(c, r)].set_from_pixbuf(self.card_images[('B', 'S')])
|
self.seen_cards[(c, r)].set_from_pixbuf(self.card_images[0])
|
||||||
self.eb[(c, r)].set_tooltip_text('')
|
self.eb[(c, r)].set_tooltip_text('')
|
||||||
|
|
||||||
class Flop_Mucked(Aux_Window):
|
class Seat_Window(gtk.Window):
|
||||||
"""Aux_Window class for displaying mucked cards for flop games."""
|
"""Subclass gtk.Window for the seat windows."""
|
||||||
|
|
||||||
|
class Aux_Seats(Aux_Window):
|
||||||
|
"""A super class to display an aux_window at each seat."""
|
||||||
|
|
||||||
def __init__(self, hud, config, params):
|
def __init__(self, hud, config, params):
|
||||||
self.hud = hud # hud object that this aux window supports
|
self.hud = hud # hud object that this aux window supports
|
||||||
self.config = config # configuration object for this aux window to use
|
self.config = config # configuration object for this aux window to use
|
||||||
self.params = params # dict aux params from config
|
self.params = params # dict aux params from config
|
||||||
self.positions = {} # dict of window positions
|
self.positions = {} # dict of window positions
|
||||||
# self.rel_positions = {} # dict of window positions, relative to the table origin
|
self.displayed = False # the seat windows are displayed
|
||||||
self.displayed_cards = False
|
self.uses_timer = False # the Aux_seats object uses a timer to control hiding
|
||||||
self.timer_on = False # bool = Ture if the timeout for removing the cards is on
|
self.timer_on = False # bool = Ture if the timeout for removing the cards is on
|
||||||
self.card_images = self.get_card_images()
|
|
||||||
|
# placeholders that should be overridden--so we don't throw errors
|
||||||
|
def create_contents(self): pass
|
||||||
|
def update_contents(self): pass
|
||||||
|
|
||||||
def create(self):
|
def create(self):
|
||||||
self.adj = self.hud.adj_seats(0, self.config)
|
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))
|
||||||
|
|
||||||
self.m_windows = {} # windows to put the card images in
|
self.m_windows = {} # windows to put the card images in
|
||||||
self.eb = {} # event boxes so we can interact with the mucked cards
|
|
||||||
self.seen_cards = {} # image objects to stash the cards in
|
|
||||||
|
|
||||||
for i in (range(1, self.hud.max + 1) + ['common']):
|
for i in (range(1, self.hud.max + 1) + ['common']):
|
||||||
if i == 'common':
|
if i == 'common':
|
||||||
(x, y) = self.params['layout'][self.hud.max].common
|
(x, y) = self.params['layout'][self.hud.max].common
|
||||||
else:
|
else:
|
||||||
(x, y) = loc[self.adj[i]]
|
(x, y) = loc[self.adj[i]]
|
||||||
self.m_windows[i] = gtk.Window()
|
self.m_windows[i] = Seat_Window()
|
||||||
self.m_windows[i].set_decorated(False)
|
self.m_windows[i].set_decorated(False)
|
||||||
self.m_windows[i].set_property("skip-taskbar-hint", True)
|
self.m_windows[i].set_property("skip-taskbar-hint", True)
|
||||||
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.eb[i] = gtk.EventBox()
|
|
||||||
self.eb[i].connect("button_press_event", self.button_press_cb)
|
|
||||||
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.m_windows[i].add(self.eb[i])
|
|
||||||
self.seen_cards[i] = gtk.image_new_from_pixbuf(self.card_images[('B', 'H')])
|
|
||||||
self.eb[i].add(self.seen_cards[i])
|
|
||||||
self.positions[i] = (int(x) + self.hud.table.x, int(y) + self.hud.table.y)
|
self.positions[i] = (int(x) + self.hud.table.x, int(y) + self.hud.table.y)
|
||||||
# self.rel_positions[i] = (int(x), int(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])
|
||||||
self.m_windows[i].set_opacity(float(self.params['opacity']))
|
if self.params.has_key('opacity'):
|
||||||
|
self.m_windows[i].set_opacity(float(self.params['opacity']))
|
||||||
|
|
||||||
|
# the create_contents method is supplied by the subclass
|
||||||
|
self.create_contents(self.m_windows[i], i)
|
||||||
|
|
||||||
self.m_windows[i].show_all()
|
self.m_windows[i].show_all()
|
||||||
self.m_windows[i].hide()
|
if self.uses_timer:
|
||||||
|
self.m_windows[i].hide()
|
||||||
|
|
||||||
|
def update_gui(self, new_hand_id):
|
||||||
|
"""Update the gui, LDO."""
|
||||||
|
for i in self.m_windows.keys():
|
||||||
|
self.update_contents(self.m_windows[i], i)
|
||||||
|
|
||||||
|
# Methods likely to be of use for any Seat_Window implementation
|
||||||
|
def destroy(self):
|
||||||
|
"""Destroy all of the seat windows."""
|
||||||
|
for i in self.m_windows.keys():
|
||||||
|
self.m_windows[i].destroy()
|
||||||
|
del(self.m_windows[i])
|
||||||
|
|
||||||
|
# Methods likely to be useful for mucked card windows (or similar) only
|
||||||
|
def hide(self):
|
||||||
|
"""Hide the seat windows."""
|
||||||
|
for (i, w) in self.m_windows.iteritems():
|
||||||
|
w.hide()
|
||||||
|
self.displayed = False
|
||||||
|
|
||||||
|
def save_layout(self, *args):
|
||||||
|
"""Save new layout back to the aux element in the config file."""
|
||||||
|
new_locs = {}
|
||||||
|
# print "adj =", self.adj
|
||||||
|
for (i, pos) in self.positions.iteritems():
|
||||||
|
if i != 'common':
|
||||||
|
new_locs[self.adj[int(i)]] = (pos[0] - self.hud.table.x, pos[1] - self.hud.table.y)
|
||||||
|
else:
|
||||||
|
new_locs[i] = (pos[0] - self.hud.table.x, pos[1] - self.hud.table.y)
|
||||||
|
self.config.edit_aux_layout(self.params['name'], self.hud.max, locations = new_locs)
|
||||||
|
|
||||||
|
def configure_event_cb(self, widget, event, i, *args):
|
||||||
|
self.positions[i] = widget.get_position()
|
||||||
|
# self.rel_positions[i] = (self.positions[i][0] - self.hud.table.x, self.positions[i][1] - self.hud.table.y)
|
||||||
|
|
||||||
|
class Flop_Mucked(Aux_Seats):
|
||||||
|
"""Aux_Window class for displaying mucked cards for flop games."""
|
||||||
|
|
||||||
|
def __init__(self, hud, config, params):
|
||||||
|
super(Flop_Mucked, self).__init__(hud, config, params)
|
||||||
|
self.card_images = self.get_card_images()
|
||||||
|
self.uses_timer = True # this Aux_seats object uses a timer to control hiding
|
||||||
|
|
||||||
|
def create_contents(self, container, i):
|
||||||
|
"""Create the widgets for showing the contents of the Aux_seats window."""
|
||||||
|
container.eb = gtk.EventBox()
|
||||||
|
container.eb.connect("button_press_event", self.button_press_cb)
|
||||||
|
container.add(container.eb)
|
||||||
|
container.seen_cards = gtk.image_new_from_pixbuf(self.card_images[0])
|
||||||
|
container.eb.add(container.seen_cards)
|
||||||
|
|
||||||
|
def update_contents(self, container, i):
|
||||||
|
if not self.hud.cards.has_key(i): return
|
||||||
|
cards = self.hud.cards[i]
|
||||||
|
n_cards = self.has_cards(cards)
|
||||||
|
if n_cards > 1:
|
||||||
|
|
||||||
|
# scratch is a working pixbuf, used to assemble the image
|
||||||
|
scratch = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8,
|
||||||
|
int(self.params['card_wd'])*n_cards,
|
||||||
|
int(self.params['card_ht']))
|
||||||
|
x = 0 # x coord where the next card starts in scratch
|
||||||
|
for card in cards:
|
||||||
|
# concatenate each card image to scratch
|
||||||
|
if card == None or card ==0:
|
||||||
|
break
|
||||||
|
self.card_images[card].copy_area(0, 0,
|
||||||
|
int(self.params['card_wd']), int(self.params['card_ht']),
|
||||||
|
scratch, x, 0)
|
||||||
|
x = x + int(self.params['card_wd'])
|
||||||
|
container.seen_cards.set_from_pixbuf(scratch)
|
||||||
|
container.resize(1,1)
|
||||||
|
container.show()
|
||||||
|
container.move(self.positions[i][0], self.positions[i][1]) # here is where I move back
|
||||||
|
self.displayed = True
|
||||||
|
if i != "common":
|
||||||
|
id = self.get_id_from_seat(i)
|
||||||
|
self.m_windows[i].eb.set_tooltip_text(self.hud.stat_dict[id]['screen_name'])
|
||||||
|
|
||||||
def update_gui(self, new_hand_id):
|
def update_gui(self, new_hand_id):
|
||||||
"""Prepare and show the mucked cards."""
|
"""Prepare and show the mucked cards."""
|
||||||
if self.displayed_cards:
|
if self.displayed: self.hide()
|
||||||
self.hide_mucked_cards()
|
|
||||||
self.displayed_cards = False
|
# See how many players showed a hand. Skip if only 1 shows (= hero)
|
||||||
|
n_sd = 0
|
||||||
for (i, cards) in self.hud.cards.iteritems():
|
for (i, cards) in self.hud.cards.iteritems():
|
||||||
if self.has_cards(cards):
|
n_cards = self.has_cards(cards)
|
||||||
# scratch is a working pixbuf, used to assemble the image
|
if n_cards > 0 and i != 'common':
|
||||||
scratch = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8,
|
n_sd = n_sd + 1
|
||||||
int(self.params['card_wd'])*len(cards)/2,
|
if n_sd < 2:
|
||||||
int(self.params['card_ht']))
|
print "skipping, n_sd =", n_sd
|
||||||
x = 0 # x coord where the next card starts in scratch
|
return
|
||||||
for card in [cards[k:k+2] for k in xrange(0, len(cards), 2)]:
|
|
||||||
# concatenate each card image to scratch
|
|
||||||
self.card_images[self.split_cards(card)].copy_area(0, 0,
|
|
||||||
int(self.params['card_wd']), int(self.params['card_ht']),
|
|
||||||
scratch, x, 0)
|
|
||||||
x = x + int(self.params['card_wd'])
|
|
||||||
self.seen_cards[i].set_from_pixbuf(scratch)
|
|
||||||
# self.m_windows[i].show_all()
|
|
||||||
self.m_windows[i].resize(1,1)
|
|
||||||
self.m_windows[i].show()
|
|
||||||
self.m_windows[i].move(self.positions[i][0], self.positions[i][1]) # here is where I move back
|
|
||||||
self.displayed_cards = True
|
|
||||||
|
|
||||||
for stats in self.hud.stat_dict.itervalues():
|
super(Flop_Mucked, self).update_gui(new_hand_id)
|
||||||
self.eb[stats['seat']].set_tooltip_text(stats['screen_name'])
|
|
||||||
|
|
||||||
if self.displayed_cards and float(self.params['timeout']) > 0:
|
if self.displayed and float(self.params['timeout']) > 0:
|
||||||
self.timer_on = True
|
self.timer_on = True
|
||||||
gobject.timeout_add(int(1000*float(self.params['timeout'])), self.timed_out)
|
gobject.timeout_add(int(1000*float(self.params['timeout'])), self.timed_out)
|
||||||
|
|
||||||
def destroy(self):
|
|
||||||
"""Destroy all of the mucked windows."""
|
|
||||||
for w in self.m_windows.values():
|
|
||||||
w.destroy()
|
|
||||||
|
|
||||||
def timed_out(self):
|
def timed_out(self):
|
||||||
# this is the callback from the timeout
|
# this is the callback from the timeout
|
||||||
|
|
||||||
|
@ -406,15 +479,9 @@ class Flop_Mucked(Aux_Window):
|
||||||
if not self.timer_on:
|
if not self.timer_on:
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
self.hide_mucked_cards()
|
self.hide()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def hide_mucked_cards(self):
|
|
||||||
"""Hide the mucked card windows."""
|
|
||||||
for (i, w) in self.m_windows.iteritems():
|
|
||||||
w.hide()
|
|
||||||
self.displayed_cards = False
|
|
||||||
|
|
||||||
def button_press_cb(self, widget, event, *args):
|
def button_press_cb(self, widget, event, *args):
|
||||||
"""Handle button clicks in the event boxes."""
|
"""Handle button clicks in the event boxes."""
|
||||||
|
|
||||||
|
@ -432,56 +499,14 @@ class Flop_Mucked(Aux_Window):
|
||||||
self.timer_on = False
|
self.timer_on = False
|
||||||
else:
|
else:
|
||||||
self.timer_on = False
|
self.timer_on = False
|
||||||
self.hide_mucked_cards()
|
self.hide()
|
||||||
|
|
||||||
elif event.button == 1: # left button event
|
elif event.button == 1: # left button event
|
||||||
window = widget.get_parent()
|
window = widget.get_parent()
|
||||||
window.begin_move_drag(event.button, int(event.x_root), int(event.y_root), event.time)
|
window.begin_move_drag(event.button, int(event.x_root), int(event.y_root), event.time)
|
||||||
|
|
||||||
def configure_event_cb(self, widget, event, i, *args):
|
|
||||||
self.positions[i] = widget.get_position()
|
|
||||||
# self.rel_positions[i] = (self.positions[i][0] - self.hud.table.x, self.positions[i][1] - self.hud.table.y)
|
|
||||||
|
|
||||||
def expose_all(self):
|
def expose_all(self):
|
||||||
for (i, cards) in self.hud.cards.iteritems():
|
for (i, cards) in self.hud.cards.iteritems():
|
||||||
self.m_windows[i].show()
|
self.m_windows[i].show()
|
||||||
self.m_windows[i].move(self.positions[i][0], self.positions[i][1]) # here is where I move back
|
self.m_windows[i].move(self.positions[i][0], self.positions[i][1]) # here is where I move back
|
||||||
self.displayed_cards = True
|
self.displayed = True
|
||||||
|
|
||||||
def save_layout(self, *args):
|
|
||||||
"""Save new layout back to the aux element in the config file."""
|
|
||||||
new_locs = {}
|
|
||||||
# print "adj =", self.adj
|
|
||||||
for (i, pos) in self.positions.iteritems():
|
|
||||||
if i != 'common':
|
|
||||||
new_locs[self.adj[int(i)]] = (pos[0] - self.hud.table.x, pos[1] - self.hud.table.y)
|
|
||||||
else:
|
|
||||||
new_locs[i] = (pos[0] - self.hud.table.x, pos[1] - self.hud.table.y)
|
|
||||||
self.config.edit_aux_layout(self.params['name'], self.hud.max, locations = new_locs)
|
|
||||||
|
|
||||||
if __name__== "__main__":
|
|
||||||
|
|
||||||
def destroy(*args): # call back for terminating the main eventloop
|
|
||||||
gtk.main_quit() # used only for testing
|
|
||||||
|
|
||||||
def process_new_hand(source, condition, db_connection): #callback from stdin watch -- testing only
|
|
||||||
# there is a new hand_id to be processed
|
|
||||||
# just read it and pass it to update
|
|
||||||
new_hand_id = sys.stdin.readline()
|
|
||||||
new_hand_id = new_hand_id.rstrip() # remove trailing whitespace
|
|
||||||
m.update_data(new_hand_id, db_connection)
|
|
||||||
m.update_gui(new_hand_id)
|
|
||||||
return(True)
|
|
||||||
|
|
||||||
config = Configuration.Config()
|
|
||||||
db_connection = Database.Database(config, 'fpdb', '')
|
|
||||||
main_window = gtk.Window()
|
|
||||||
main_window.set_keep_above(True)
|
|
||||||
main_window.connect("destroy", destroy)
|
|
||||||
|
|
||||||
aux_to_call = "stud_mucked"
|
|
||||||
aux_params = config.get_aux_parameters(aux_to_call)
|
|
||||||
m = eval("%s(main_window, None, config, aux_params)" % aux_params['class'])
|
|
||||||
|
|
||||||
s_id = gobject.io_add_watch(sys.stdin, gobject.IO_IN, process_new_hand, db_connection)
|
|
||||||
gtk.main()
|
|
||||||
|
|
45
pyfpdb/Options.py
Normal file
45
pyfpdb/Options.py
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
#Copyright 2008 Ray E. Barker
|
||||||
|
#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 os
|
||||||
|
import sys
|
||||||
|
from optparse import OptionParser
|
||||||
|
|
||||||
|
def fpdb_options():
|
||||||
|
|
||||||
|
"""Process command line options for fpdb and HUD_main."""
|
||||||
|
parser = OptionParser()
|
||||||
|
parser.add_option("-x", "--errorsToConsole",
|
||||||
|
action="store_true",
|
||||||
|
help="If passed error output will go to the console rather than .")
|
||||||
|
parser.add_option("-d", "--databaseName",
|
||||||
|
dest="dbname", default="fpdb",
|
||||||
|
help="Overrides the default database name")
|
||||||
|
parser.add_option("-c", "--configFile",
|
||||||
|
dest="config", default=None,
|
||||||
|
help="Specifies a configuration file.")
|
||||||
|
(options, sys.argv) = parser.parse_args()
|
||||||
|
return (options, sys.argv)
|
||||||
|
|
||||||
|
if __name__== "__main__":
|
||||||
|
(options, sys.argv) = fpdb_options()
|
||||||
|
print "errorsToConsole =", options.errorsToConsole
|
||||||
|
print "database name =", options.dbname
|
||||||
|
print "config file =", options.config
|
||||||
|
|
||||||
|
print "press enter to end"
|
||||||
|
sys.stdin.readline()
|
|
@ -1,4 +1,4 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Copyright 2008, Carl Gherardi
|
# Copyright 2008, Carl Gherardi
|
||||||
|
@ -18,6 +18,7 @@
|
||||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
########################################################################
|
########################################################################
|
||||||
|
|
||||||
|
# TODO: straighten out discards for draw games
|
||||||
import sys
|
import sys
|
||||||
from HandHistoryConverter import *
|
from HandHistoryConverter import *
|
||||||
|
|
||||||
|
@ -25,22 +26,41 @@ from HandHistoryConverter import *
|
||||||
|
|
||||||
class PokerStars(HandHistoryConverter):
|
class PokerStars(HandHistoryConverter):
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
# Class Variables
|
||||||
|
|
||||||
# Static regexes
|
# Static regexes
|
||||||
re_GameInfo = re.compile("PokerStars Game #(?P<HID>[0-9]+):\s+(HORSE)? \(?(?P<GAME>Hold\'em|Razz|7 Card Stud|7 Card Stud Hi/Lo|Omaha|Omaha Hi/Lo|Badugi) (?P<LIMIT>No Limit|Limit|Pot Limit),? \(?(?P<CURRENCY>\$|)?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+)\) - (?P<DATETIME>.*$)", re.MULTILINE)
|
re_GameInfo = re.compile("""PokerStars\sGame\s\#(?P<HID>[0-9]+):\s+
|
||||||
|
(Tournament\s\#(?P<TOURNO>\d+),\s(?P<BUYIN>[\$\+\d\.]+)\s)?
|
||||||
|
(?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)?\(?
|
||||||
|
(?P<CURRENCY>\$|)?
|
||||||
|
(?P<SB>[.0-9]+)/\$?
|
||||||
|
(?P<BB>[.0-9]+)\)\s-\s
|
||||||
|
(?P<DATETIME>.*$)""",
|
||||||
|
re.MULTILINE|re.VERBOSE)
|
||||||
re_SplitHands = re.compile('\n\n+')
|
re_SplitHands = re.compile('\n\n+')
|
||||||
re_TailSplitHands = re.compile('(\n\n\n+)')
|
re_TailSplitHands = re.compile('(\n\n\n+)')
|
||||||
re_HandInfo = re.compile("^Table \'(?P<TABLE>[- a-zA-Z]+)\'(?P<TABLEATTRIBUTES>.+?$)?", re.MULTILINE)
|
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_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_PlayerInfo = re.compile('^Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \(\$?(?P<CASH>[.0-9]+) in chips\)', re.MULTILINE)
|
re_PlayerInfo = re.compile('^Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \(\$?(?P<CASH>[.0-9]+) in chips\)', re.MULTILINE)
|
||||||
re_Board = re.compile(r"\[(?P<CARDS>.+)\]")
|
re_Board = re.compile(r"\[(?P<CARDS>.+)\]")
|
||||||
# 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):
|
mixes = { 'HORSE': 'horse', '8-Game': '8game', 'HOSE': 'hose'}
|
||||||
|
|
||||||
|
def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True, index=0):
|
||||||
"""\
|
"""\
|
||||||
in_path (default '-' = sys.stdin)
|
in_path (default '-' = sys.stdin)
|
||||||
out_path (default '-' = sys.stdout)
|
out_path (default '-' = sys.stdout)
|
||||||
follow : whether to tail -f the input"""
|
follow : whether to tail -f the input"""
|
||||||
HandHistoryConverter.__init__(self, in_path, out_path, sitename="PokerStars", follow=follow)
|
HandHistoryConverter.__init__(self, in_path, out_path, sitename="PokerStars", follow=follow, index=index)
|
||||||
logging.info("Initialising PokerStars converter class")
|
logging.info("Initialising PokerStars converter class")
|
||||||
self.filetype = "text"
|
self.filetype = "text"
|
||||||
self.codepage = "cp1252"
|
self.codepage = "cp1252"
|
||||||
|
@ -53,6 +73,9 @@ follow : whether to tail -f the input"""
|
||||||
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.
|
# 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)) + ")"
|
||||||
logging.debug("player_re: " + player_re)
|
logging.debug("player_re: " + player_re)
|
||||||
|
@ -62,43 +85,59 @@ follow : whether to tail -f the input"""
|
||||||
self.re_BringIn = re.compile(r"^%s: brings[- ]in( low|) for \$?(?P<BRINGIN>[.0-9]+)" % player_re, re.MULTILINE)
|
self.re_BringIn = re.compile(r"^%s: brings[- ]in( low|) for \$?(?P<BRINGIN>[.0-9]+)" % player_re, re.MULTILINE)
|
||||||
self.re_PostBoth = re.compile(r"^%s: posts small \& big blinds \[\$? (?P<SBBB>[.0-9]+)" % player_re, re.MULTILINE)
|
self.re_PostBoth = re.compile(r"^%s: posts small \& big blinds \[\$? (?P<SBBB>[.0-9]+)" % player_re, re.MULTILINE)
|
||||||
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| calls| folds| discards| stands pat)( \$(?P<BET>[.\d]+))?( to \$(?P<BETTO>[.\d]+))?( (?P<NODISCARDED>\d) cards?( \[(?P<DISCARDED>.+?)\])?)?" % player_re, re.MULTILINE)
|
self.re_Action = re.compile(r"""^%s:(?P<ATYPE>\sbets|\schecks|\sraises|\scalls|\sfolds|\sdiscards|\sstands\spat)
|
||||||
|
(\s\$?(?P<BET>[.\d]+))?(\sto\s\$?(?P<BETTO>[.\d]+))? # the number discarded goes in <BET>
|
||||||
|
(\scards?(\s\[(?P<DISCARDED>.+?)\])?)?"""
|
||||||
|
% player_re, re.MULTILINE|re.VERBOSE)
|
||||||
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("^%s sits out" % player_re, re.MULTILINE)
|
self.re_sitsOut = re.compile("^%s sits out" % player_re, re.MULTILINE)
|
||||||
self.re_ShownCards = re.compile("^Seat (?P<SEAT>[0-9]+): %s \(.*\) showed \[(?P<CARDS>.*)\].*" % 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"],
|
||||||
|
|
||||||
["ring", "stud", "fl"],
|
["ring", "stud", "fl"],
|
||||||
#["ring", "draw", "fl"],
|
|
||||||
["ring", "omaha", "pl"]
|
["ring", "draw", "fl"],
|
||||||
|
|
||||||
|
["tour", "hold", "nl"],
|
||||||
|
["tour", "hold", "pl"],
|
||||||
|
["tour", "hold", "fl"],
|
||||||
|
|
||||||
|
["tour", "stud", "fl"],
|
||||||
]
|
]
|
||||||
|
|
||||||
def determineGameType(self, handText):
|
def determineGameType(self, handText):
|
||||||
info = {'type':'ring'}
|
# inspect the handText and return the gametype dict
|
||||||
|
# gametype dict is:
|
||||||
|
# {'limitType': xxx, 'base': xxx, 'category': xxx}
|
||||||
|
|
||||||
|
info = {}
|
||||||
m = self.re_GameInfo.search(handText)
|
m = self.re_GameInfo.search(handText)
|
||||||
if not m:
|
if not m:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
mg = m.groupdict()
|
mg = m.groupdict()
|
||||||
|
# translations from captured groups to fpdb 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
|
||||||
"Hold'em" : ('hold','holdem'),
|
"Hold'em" : ('hold','holdem'),
|
||||||
'Omaha' : ('hold','omahahi'),
|
'Omaha' : ('hold','omahahi'),
|
||||||
'Omaha Hi/Lo' : ('hold','omahahilo'),
|
'Omaha Hi/Lo' : ('hold','omahahilo'),
|
||||||
'Razz' : ('stud','razz'),
|
'Razz' : ('stud','razz'),
|
||||||
'7 Card Stud' : ('stud','studhi'),
|
'7 Card Stud' : ('stud','studhi'),
|
||||||
'7 Card Stud Hi/Lo' : ('stud','studhilo'),
|
'7 Card Stud Hi/Lo' : ('stud','studhilo'),
|
||||||
'Badugi' : ('draw','badugi')
|
'Badugi' : ('draw','badugi'),
|
||||||
|
'Triple Draw 2-7 Lowball' : ('draw','27_3draw'),
|
||||||
}
|
}
|
||||||
currencies = { u'€':'EUR', '$':'USD', '':'T$' }
|
currencies = { u'€':'EUR', '$':'USD', '':'T$' }
|
||||||
|
# I don't think this is doing what we think. mg will always have all
|
||||||
|
# the expected keys, but the ones that didn't match in the regex will
|
||||||
|
# have a value of None. It is OK if it throws an exception when it
|
||||||
|
# runs across an unknown game or limit or whatever.
|
||||||
if 'LIMIT' in mg:
|
if 'LIMIT' in mg:
|
||||||
info['limitType'] = limits[mg['LIMIT']]
|
info['limitType'] = limits[mg['LIMIT']]
|
||||||
if 'GAME' in mg:
|
if 'GAME' in mg:
|
||||||
|
@ -109,8 +148,13 @@ follow : whether to tail -f the input"""
|
||||||
info['bb'] = mg['BB']
|
info['bb'] = mg['BB']
|
||||||
if 'CURRENCY' in mg:
|
if 'CURRENCY' in mg:
|
||||||
info['currency'] = currencies[mg['CURRENCY']]
|
info['currency'] = currencies[mg['CURRENCY']]
|
||||||
# NB: SB, BB must be interpreted as blinds or bets depending on limit type.
|
|
||||||
|
|
||||||
|
if 'TOURNO' in mg and mg['TOURNO'] == None:
|
||||||
|
info['type'] = 'ring'
|
||||||
|
else:
|
||||||
|
info['type'] = 'tour'
|
||||||
|
|
||||||
|
# NB: SB, BB must be interpreted as blinds or bets depending on limit type.
|
||||||
return info
|
return info
|
||||||
|
|
||||||
|
|
||||||
|
@ -119,14 +163,13 @@ follow : whether to tail -f the input"""
|
||||||
m = self.re_HandInfo.search(hand.handText,re.DOTALL)
|
m = self.re_HandInfo.search(hand.handText,re.DOTALL)
|
||||||
if m:
|
if m:
|
||||||
info.update(m.groupdict())
|
info.update(m.groupdict())
|
||||||
# TODO: Be less lazy and parse maxseats from the HandInfo regex
|
# hand.maxseats = int(m2.group(1))
|
||||||
if m.group('TABLEATTRIBUTES'):
|
else:
|
||||||
m2 = re.search("\s*(\d+)-max", m.group('TABLEATTRIBUTES'))
|
pass # throw an exception here, eh?
|
||||||
hand.maxseats = int(m2.group(1))
|
|
||||||
m = self.re_GameInfo.search(hand.handText)
|
m = self.re_GameInfo.search(hand.handText)
|
||||||
if m: info.update(m.groupdict())
|
if m: info.update(m.groupdict())
|
||||||
m = self.re_Button.search(hand.handText)
|
# m = self.re_Button.search(hand.handText)
|
||||||
if m: info.update(m.groupdict())
|
# if m: info.update(m.groupdict())
|
||||||
# TODO : I rather like the idea of just having this dict as hand.info
|
# TODO : I rather like the idea of just having this dict as hand.info
|
||||||
logging.debug("readHandInfo: %s" % info)
|
logging.debug("readHandInfo: %s" % info)
|
||||||
for key in info:
|
for key in info:
|
||||||
|
@ -143,6 +186,22 @@ follow : whether to tail -f the input"""
|
||||||
hand.tablename = info[key]
|
hand.tablename = info[key]
|
||||||
if key == 'BUTTON':
|
if key == 'BUTTON':
|
||||||
hand.buttonpos = info[key]
|
hand.buttonpos = info[key]
|
||||||
|
if key == 'MAX':
|
||||||
|
hand.maxseats = int(info[key])
|
||||||
|
|
||||||
|
if key == 'MIXED':
|
||||||
|
if info[key] == None: hand.mixed = None
|
||||||
|
else: hand.mixed = self.mixes[info[key]]
|
||||||
|
|
||||||
|
if key == 'TOURNO':
|
||||||
|
hand.tourNo = info[key]
|
||||||
|
if key == 'BUYIN':
|
||||||
|
hand.buyin = info[key]
|
||||||
|
if key == 'LEVEL':
|
||||||
|
hand.level = info[key]
|
||||||
|
if key == 'PLAY' and info['PLAY'] != None:
|
||||||
|
# hand.currency = 'play' # overrides previously set value
|
||||||
|
hand.gametype['currency'] = 'play'
|
||||||
|
|
||||||
def readButton(self, hand):
|
def readButton(self, hand):
|
||||||
m = self.re_Button.search(hand.handText)
|
m = self.re_Button.search(hand.handText)
|
||||||
|
@ -209,80 +268,117 @@ follow : whether to tail -f the input"""
|
||||||
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'))
|
||||||
for a in self.re_PostBoth.finditer(hand.handText):
|
for a in self.re_PostBoth.finditer(hand.handText):
|
||||||
hand.addBlind(a.group('PNAME'), 'small & big blinds', a.group('SBBB'))
|
hand.addBlind(a.group('PNAME'), 'both', a.group('SBBB'))
|
||||||
|
|
||||||
|
# def readHeroCards(self, hand):
|
||||||
|
# m = self.re_HeroCards.search(hand.handText)
|
||||||
|
# if(m == None):
|
||||||
|
# #Not involved in hand
|
||||||
|
# hand.involved = False
|
||||||
|
# else:
|
||||||
|
# hand.hero = m.group('PNAME')
|
||||||
|
# # "2c, qh" -> set(["2c","qc"])
|
||||||
|
# # Also works with Omaha hands.
|
||||||
|
# cards = m.group('NEWCARDS')
|
||||||
|
# cards = set(cards.split(' '))
|
||||||
|
# hand.addHoleCards(cards, m.group('PNAME'), shown=False, mucked=False, dealt=True)
|
||||||
|
|
||||||
def readHeroCards(self, hand):
|
def readHeroCards(self, hand):
|
||||||
m = self.re_HeroCards.search(hand.handText)
|
# streets PREFLOP, PREDRAW, and THIRD are special cases beacause
|
||||||
if(m == None):
|
# we need to grab hero's cards
|
||||||
#Not involved in hand
|
for street in ('PREFLOP', 'DEAL'):
|
||||||
hand.involved = False
|
if street in hand.streets.keys():
|
||||||
else:
|
m = self.re_HeroCards.finditer(hand.streets[street])
|
||||||
hand.hero = m.group('PNAME')
|
for found in m:
|
||||||
# "2c, qh" -> set(["2c","qc"])
|
# if m == None:
|
||||||
# Also works with Omaha hands.
|
# hand.involved = False
|
||||||
cards = m.group('NEWCARDS')
|
# else:
|
||||||
cards = set(cards.split(' '))
|
hand.hero = found.group('PNAME')
|
||||||
hand.addHoleCards(cards, m.group('PNAME'))
|
newcards = found.group('NEWCARDS').split(' ')
|
||||||
|
hand.addHoleCards(street, hand.hero, closed=newcards, shown=False, mucked=False, dealt=True)
|
||||||
|
|
||||||
def readDrawCards(self, hand, street):
|
for street, text in hand.streets.iteritems():
|
||||||
logging.debug("readDrawCards")
|
if not text or street in ('PREFLOP', 'DEAL'): continue # already done these
|
||||||
m = self.re_HeroCards.finditer(hand.streets[street])
|
m = self.re_HeroCards.finditer(hand.streets[street])
|
||||||
if m == None:
|
for found in m:
|
||||||
hand.involved = False
|
player = found.group('PNAME')
|
||||||
else:
|
if found.group('NEWCARDS') == None:
|
||||||
for player in m:
|
newcards = []
|
||||||
hand.hero = player.group('PNAME') # Only really need to do this once
|
|
||||||
newcards = player.group('NEWCARDS')
|
|
||||||
oldcards = player.group('OLDCARDS')
|
|
||||||
if newcards == None:
|
|
||||||
newcards = set()
|
|
||||||
else:
|
else:
|
||||||
newcards = set(newcards.split(' '))
|
newcards = found.group('NEWCARDS').split(' ')
|
||||||
if oldcards == None:
|
if found.group('OLDCARDS') == None:
|
||||||
oldcards = set()
|
oldcards = []
|
||||||
else:
|
else:
|
||||||
oldcards = set(oldcards.split(' '))
|
oldcards = found.group('OLDCARDS').split(' ')
|
||||||
hand.addDrawHoleCards(newcards, oldcards, player.group('PNAME'), street)
|
|
||||||
|
if street == 'THIRD' and len(newcards) == 3: # hero in stud game
|
||||||
|
hand.hero = player
|
||||||
|
hand.dealt.add(player) # need this for stud??
|
||||||
|
hand.addHoleCards(street, player, closed=newcards[0:2], open=[newcards[2]], shown=False, mucked=False, dealt=False)
|
||||||
|
else:
|
||||||
|
hand.addHoleCards(street, player, open=newcards, closed=oldcards, shown=False, mucked=False, dealt=False)
|
||||||
|
|
||||||
|
|
||||||
def readStudPlayerCards(self, hand, street):
|
# def readDrawCards(self, hand, street):
|
||||||
# See comments of reference implementation in FullTiltToFpdb.py
|
# logging.debug("readDrawCards")
|
||||||
logging.debug("readStudPlayerCards")
|
# m = self.re_HeroCards.finditer(hand.streets[street])
|
||||||
m = self.re_HeroCards.finditer(hand.streets[street])
|
# if m == None:
|
||||||
for player in m:
|
# hand.involved = False
|
||||||
#~ logging.debug(player.groupdict())
|
# else:
|
||||||
(pname, oldcards, newcards) = (player.group('PNAME'), player.group('OLDCARDS'), player.group('NEWCARDS'))
|
# for player in m:
|
||||||
if oldcards:
|
# hand.hero = player.group('PNAME') # Only really need to do this once
|
||||||
oldcards = [c.strip() for c in oldcards.split(' ')]
|
# newcards = player.group('NEWCARDS')
|
||||||
if newcards:
|
# oldcards = player.group('OLDCARDS')
|
||||||
newcards = [c.strip() for c in newcards.split(' ')]
|
# if newcards == None:
|
||||||
if street=='ANTES':
|
# newcards = set()
|
||||||
return
|
# else:
|
||||||
elif street=='THIRD':
|
# newcards = set(newcards.split(' '))
|
||||||
# we'll have observed hero holecards in CARDS and thirdstreet open cards in 'NEWCARDS'
|
# if oldcards == None:
|
||||||
# hero: [xx][o]
|
# oldcards = set()
|
||||||
# others: [o]
|
# else:
|
||||||
hand.addPlayerCards(player = player.group('PNAME'), street = street, closed = oldcards, open = newcards)
|
# oldcards = set(oldcards.split(' '))
|
||||||
elif street in ('FOURTH', 'FIFTH', 'SIXTH'):
|
# hand.addDrawHoleCards(newcards, oldcards, player.group('PNAME'), street)
|
||||||
# 4th:
|
|
||||||
# hero: [xxo] [o]
|
|
||||||
# others: [o] [o]
|
# def readStudPlayerCards(self, hand, street):
|
||||||
# 5th:
|
# # See comments of reference implementation in FullTiltToFpdb.py
|
||||||
# hero: [xxoo] [o]
|
# logging.debug("readStudPlayerCards")
|
||||||
# others: [oo] [o]
|
# m = self.re_HeroCards.finditer(hand.streets[street])
|
||||||
# 6th:
|
# for player in m:
|
||||||
# hero: [xxooo] [o]
|
# #~ logging.debug(player.groupdict())
|
||||||
# others: [ooo] [o]
|
# (pname, oldcards, newcards) = (player.group('PNAME'), player.group('OLDCARDS'), player.group('NEWCARDS'))
|
||||||
hand.addPlayerCards(player = player.group('PNAME'), street = street, open = newcards)
|
# if oldcards:
|
||||||
# we may additionally want to check the earlier streets tally with what we have but lets trust it for now.
|
# oldcards = [c.strip() for c in oldcards.split(' ')]
|
||||||
elif street=='SEVENTH' and newcards:
|
# if newcards:
|
||||||
# hero: [xxoooo] [x]
|
# newcards = [c.strip() for c in newcards.split(' ')]
|
||||||
# others: not reported.
|
# if street=='ANTES':
|
||||||
hand.addPlayerCards(player = player.group('PNAME'), street = street, closed = newcards)
|
# return
|
||||||
|
# elif street=='THIRD':
|
||||||
|
# # we'll have observed hero holecards in CARDS and thirdstreet open cards in 'NEWCARDS'
|
||||||
|
# # hero: [xx][o]
|
||||||
|
# # others: [o]
|
||||||
|
# hand.addPlayerCards(player = player.group('PNAME'), street = street, closed = oldcards, open = newcards)
|
||||||
|
# elif street in ('FOURTH', 'FIFTH', 'SIXTH'):
|
||||||
|
# # 4th:
|
||||||
|
# # hero: [xxo] [o]
|
||||||
|
# # others: [o] [o]
|
||||||
|
# # 5th:
|
||||||
|
# # hero: [xxoo] [o]
|
||||||
|
# # others: [oo] [o]
|
||||||
|
# # 6th:
|
||||||
|
# # hero: [xxooo] [o]
|
||||||
|
# # others: [ooo] [o]
|
||||||
|
# hand.addPlayerCards(player = player.group('PNAME'), street = street, open = newcards)
|
||||||
|
# # we may additionally want to check the earlier streets tally with what we have but lets trust it for now.
|
||||||
|
# elif street=='SEVENTH' and newcards:
|
||||||
|
# # hero: [xxoooo] [x]
|
||||||
|
# # others: not reported.
|
||||||
|
# hand.addPlayerCards(player = player.group('PNAME'), street = street, closed = newcards)
|
||||||
|
|
||||||
def readAction(self, hand, street):
|
def readAction(self, hand, street):
|
||||||
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()
|
||||||
if action.group('ATYPE') == ' raises':
|
if action.group('ATYPE') == ' raises':
|
||||||
hand.addRaiseBy( street, action.group('PNAME'), action.group('BET') )
|
hand.addRaiseBy( street, action.group('PNAME'), action.group('BET') )
|
||||||
elif action.group('ATYPE') == ' calls':
|
elif action.group('ATYPE') == ' calls':
|
||||||
|
@ -294,7 +390,7 @@ follow : whether to tail -f the input"""
|
||||||
elif action.group('ATYPE') == ' checks':
|
elif action.group('ATYPE') == ' checks':
|
||||||
hand.addCheck( street, action.group('PNAME'))
|
hand.addCheck( street, action.group('PNAME'))
|
||||||
elif action.group('ATYPE') == ' discards':
|
elif action.group('ATYPE') == ' discards':
|
||||||
hand.addDiscard(street, action.group('PNAME'), action.group('NODISCARDED'), action.group('DISCARDED'))
|
hand.addDiscard(street, action.group('PNAME'), action.group('BET'), action.group('DISCARDED'))
|
||||||
elif action.group('ATYPE') == ' stands pat':
|
elif action.group('ATYPE') == ' stands pat':
|
||||||
hand.addStandsPat( street, action.group('PNAME'))
|
hand.addStandsPat( street, action.group('PNAME'))
|
||||||
else:
|
else:
|
||||||
|
@ -302,9 +398,9 @@ follow : whether to tail -f the input"""
|
||||||
|
|
||||||
|
|
||||||
def readShowdownActions(self, hand):
|
def readShowdownActions(self, hand):
|
||||||
|
# TODO: pick up mucks also??
|
||||||
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').split(' ')
|
||||||
cards = set(cards.split(' '))
|
|
||||||
hand.addShownCards(cards, shows.group('PNAME'))
|
hand.addShownCards(cards, shows.group('PNAME'))
|
||||||
|
|
||||||
def readCollectPot(self,hand):
|
def readCollectPot(self,hand):
|
||||||
|
@ -315,12 +411,17 @@ follow : whether to tail -f the input"""
|
||||||
for m in self.re_ShownCards.finditer(hand.handText):
|
for m in self.re_ShownCards.finditer(hand.handText):
|
||||||
if m.group('CARDS') is not None:
|
if m.group('CARDS') is not None:
|
||||||
cards = m.group('CARDS')
|
cards = m.group('CARDS')
|
||||||
cards = set(cards.split(' '))
|
cards = cards.split(' ') # needs to be a list, not a set--stud needs the order
|
||||||
hand.addShownCards(cards=cards, player=m.group('PNAME'))
|
|
||||||
|
(shown, mucked) = (False, False)
|
||||||
|
if m.group('SHOWED') == "showed": shown = True
|
||||||
|
elif m.group('SHOWED') == "mucked": mucked = True
|
||||||
|
|
||||||
|
hand.addShownCards(cards=cards, player=m.group('PNAME'), shown=shown, mucked=mucked)
|
||||||
|
|
||||||
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/pokerstars/HH20090226 Natalie V - $0.10-$0.20 - HORSE.txt")
|
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("-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",
|
||||||
|
|
1011
pyfpdb/SQL.py
1011
pyfpdb/SQL.py
File diff suppressed because it is too large
Load Diff
233
pyfpdb/Stats.py
233
pyfpdb/Stats.py
|
@ -22,6 +22,8 @@
|
||||||
########################################################################
|
########################################################################
|
||||||
|
|
||||||
# How to write a new stat:
|
# How to write a new stat:
|
||||||
|
# 0 Do not use a name like "xyz_2". Names ending in _ and a single digit are
|
||||||
|
# used to indicate the number of decimal places the user wants to see in the Hud.
|
||||||
# 1 You can see a listing of all the raw stats (e.g., from the HudCache table)
|
# 1 You can see a listing of all the raw stats (e.g., from the HudCache table)
|
||||||
# by running Database.py as a stand along program. You need to combine
|
# by running Database.py as a stand along program. You need to combine
|
||||||
# those raw stats to get stats to present to the HUD. If you need more
|
# those raw stats to get stats to present to the HUD. If you need more
|
||||||
|
@ -50,16 +52,34 @@
|
||||||
# pyGTK modules
|
# pyGTK modules
|
||||||
import pygtk
|
import pygtk
|
||||||
import gtk
|
import gtk
|
||||||
|
import re
|
||||||
|
|
||||||
# FreePokerTools modules
|
# FreePokerTools modules
|
||||||
import Configuration
|
import Configuration
|
||||||
import Database
|
import Database
|
||||||
|
|
||||||
|
|
||||||
|
re_Places = re.compile("_[0-9]$")
|
||||||
|
re_Percent = re.compile("%$")
|
||||||
|
|
||||||
|
|
||||||
def do_tip(widget, tip):
|
def do_tip(widget, tip):
|
||||||
widget.set_tooltip_text(tip)
|
widget.set_tooltip_text(tip)
|
||||||
|
|
||||||
def do_stat(stat_dict, player = 24, stat = 'vpip'):
|
def do_stat(stat_dict, player = 24, stat = 'vpip'):
|
||||||
return eval("%(stat)s(stat_dict, %(player)d)" % {'stat': stat, 'player': player})
|
match = re_Places.search(stat)
|
||||||
|
if match == None:
|
||||||
|
result = eval("%(stat)s(stat_dict, %(player)d)" % {'stat': stat, 'player': player})
|
||||||
|
else:
|
||||||
|
base = stat[0:-2]
|
||||||
|
places = int(stat[-1:])
|
||||||
|
result = eval("%(stat)s(stat_dict, %(player)d)" % {'stat': base, 'player': player})
|
||||||
|
match = re_Percent.search(result[1])
|
||||||
|
if match == None:
|
||||||
|
result = (result[0], "%.*f" % (places, result[0]), result[2], result[3], result[4], result[5])
|
||||||
|
else:
|
||||||
|
result = (result[0], "%.*f%%" % (places, 100*result[0]), result[2], result[3], result[4], result[5])
|
||||||
|
return result
|
||||||
|
|
||||||
# OK, for reference the tuple returned by the stat is:
|
# OK, for reference the tuple returned by the stat is:
|
||||||
# 0 - The stat, raw, no formating, eg 0.33333333
|
# 0 - The stat, raw, no formating, eg 0.33333333
|
||||||
|
@ -108,26 +128,6 @@ def vpip(stat_dict, player):
|
||||||
'Voluntarily Put In Pot %'
|
'Voluntarily Put In Pot %'
|
||||||
)
|
)
|
||||||
|
|
||||||
def vpip_0(stat_dict, player):
|
|
||||||
""" Voluntarily put $ in the pot (no decimals)."""
|
|
||||||
stat = 0.0
|
|
||||||
try:
|
|
||||||
stat = float(stat_dict[player]['vpip'])/float(stat_dict[player]['n'])
|
|
||||||
return (stat,
|
|
||||||
'%2.0f' % (100*stat) + '%',
|
|
||||||
'v=%2.0f' % (100*stat) + '%',
|
|
||||||
'vpip=%2.0f' % (100*stat) + '%',
|
|
||||||
'(%d/%d)' % (stat_dict[player]['vpip'], stat_dict[player]['n']),
|
|
||||||
'vpip'
|
|
||||||
)
|
|
||||||
except: return (stat,
|
|
||||||
'%2.0f' % (0) + '%',
|
|
||||||
'w=%2.0f' % (0) + '%',
|
|
||||||
'wtsd=%2.0f' % (0) + '%',
|
|
||||||
'(%d/%d)' % (0, 0),
|
|
||||||
'wtsd'
|
|
||||||
)
|
|
||||||
|
|
||||||
def pfr(stat_dict, player):
|
def pfr(stat_dict, player):
|
||||||
""" Preflop (3rd street) raise."""
|
""" Preflop (3rd street) raise."""
|
||||||
stat = 0.0
|
stat = 0.0
|
||||||
|
@ -149,27 +149,6 @@ def pfr(stat_dict, player):
|
||||||
'Pre-Flop Raise %'
|
'Pre-Flop Raise %'
|
||||||
)
|
)
|
||||||
|
|
||||||
def pfr_0(stat_dict, player):
|
|
||||||
""" Preflop (3rd street) raise (no decimals)."""
|
|
||||||
stat = 0.0
|
|
||||||
try:
|
|
||||||
stat = float(stat_dict[player]['pfr'])/float(stat_dict[player]['n'])
|
|
||||||
return (stat,
|
|
||||||
'%2.0f' % (100*stat) + '%',
|
|
||||||
'p=%2.0f' % (100*stat) + '%',
|
|
||||||
'pfr=%2.0f' % (100*stat) + '%',
|
|
||||||
'(%d/%d)' % (stat_dict[player]['pfr'], stat_dict[player]['n']),
|
|
||||||
'pfr'
|
|
||||||
)
|
|
||||||
except:
|
|
||||||
return (stat,
|
|
||||||
'%2.0f' % (0) + '%',
|
|
||||||
'p=%2.0f' % (0) + '%',
|
|
||||||
'pfr=%2.0f' % (0) + '%',
|
|
||||||
'(%d/%d)' % (0, 0),
|
|
||||||
'pfr'
|
|
||||||
)
|
|
||||||
|
|
||||||
def wtsd(stat_dict, player):
|
def wtsd(stat_dict, player):
|
||||||
""" Went to SD when saw flop/4th."""
|
""" Went to SD when saw flop/4th."""
|
||||||
stat = 0.0
|
stat = 0.0
|
||||||
|
@ -191,27 +170,6 @@ def wtsd(stat_dict, player):
|
||||||
'% went to showdown'
|
'% went to showdown'
|
||||||
)
|
)
|
||||||
|
|
||||||
def wtsd_0(stat_dict, player):
|
|
||||||
""" Went to SD when saw flop/4th."""
|
|
||||||
stat = 0.0
|
|
||||||
try:
|
|
||||||
stat = float(stat_dict[player]['sd'])/float(stat_dict[player]['saw_f'])
|
|
||||||
return (stat,
|
|
||||||
'%2.0f' % (100*stat) + '%',
|
|
||||||
'w=%2.0f' % (100*stat) + '%',
|
|
||||||
'wtsd=%2.0f' % (100*stat) + '%',
|
|
||||||
'(%d/%d)' % (stat_dict[player]['sd'], stat_dict[player]['saw_f']),
|
|
||||||
'% went to showdown'
|
|
||||||
)
|
|
||||||
except:
|
|
||||||
return (stat,
|
|
||||||
'%2.0f' % (0) + '%',
|
|
||||||
'w=%2.0f' % (0) + '%',
|
|
||||||
'wtsd=%2.0f' % (0) + '%',
|
|
||||||
'(%d/%d)' % (0, 0),
|
|
||||||
'% went to showdown'
|
|
||||||
)
|
|
||||||
|
|
||||||
def wmsd(stat_dict, player):
|
def wmsd(stat_dict, player):
|
||||||
""" Won $ at showdown."""
|
""" Won $ at showdown."""
|
||||||
stat = 0.0
|
stat = 0.0
|
||||||
|
@ -233,28 +191,7 @@ def wmsd(stat_dict, player):
|
||||||
'% won money at showdown'
|
'% won money at showdown'
|
||||||
)
|
)
|
||||||
|
|
||||||
def wmsd_0(stat_dict, player):
|
def profit100(stat_dict, player):
|
||||||
""" Won $ at showdown."""
|
|
||||||
stat = 0.0
|
|
||||||
try:
|
|
||||||
stat = float(stat_dict[player]['wmsd'])/float(stat_dict[player]['sd'])
|
|
||||||
return (stat,
|
|
||||||
'%2.0f' % (100*stat) + '%',
|
|
||||||
'w=%2.0f' % (100*stat) + '%',
|
|
||||||
'wmsd=%2.0f' % (100*stat) + '%',
|
|
||||||
'(%5.1f/%d)' % (float(stat_dict[player]['wmsd']), stat_dict[player]['sd']),
|
|
||||||
'% won money at showdown'
|
|
||||||
)
|
|
||||||
except:
|
|
||||||
return (stat,
|
|
||||||
'%2.0f' % (0) + '%',
|
|
||||||
'w=%2.0f' % (0) + '%',
|
|
||||||
'wmsd=%2.0f' % (0) + '%',
|
|
||||||
'(%d/%d)' % (0, 0),
|
|
||||||
'% won money at showdown'
|
|
||||||
)
|
|
||||||
|
|
||||||
def profit100_0(stat_dict, player):
|
|
||||||
""" Profit won per 100 hands (no decimal places)."""
|
""" Profit won per 100 hands (no decimal places)."""
|
||||||
stat = 0.0
|
stat = 0.0
|
||||||
try:
|
try:
|
||||||
|
@ -356,27 +293,6 @@ def steal(stat_dict, player):
|
||||||
except:
|
except:
|
||||||
return (stat, 'NA', 'st=NA', 'steal=NA', '(0/0)', '% steal attempted')
|
return (stat, 'NA', 'st=NA', 'steal=NA', '(0/0)', '% steal attempted')
|
||||||
|
|
||||||
def steal_0(stat_dict, player):
|
|
||||||
""" Steal %."""
|
|
||||||
stat = 0.0
|
|
||||||
try:
|
|
||||||
stat = float(stat_dict[player]['steal'])/float(stat_dict[player]['steal_opp'])
|
|
||||||
return (stat,
|
|
||||||
'%2.0f' % (100*stat) + '%',
|
|
||||||
'st=%2.0f' % (100*stat) + '%',
|
|
||||||
'steal=%2.0f' % (100*stat) + '%',
|
|
||||||
'(%d/%d)' % (stat_dict[player]['steal'], stat_dict[player]['steal_opp']),
|
|
||||||
'% steal attempted'
|
|
||||||
)
|
|
||||||
except:
|
|
||||||
return (stat,
|
|
||||||
'%2.0f' % (0) + '%',
|
|
||||||
'st=%2.0f' % (0) + '%',
|
|
||||||
'steal=%2.0f' % (0) + '%',
|
|
||||||
'(%d/%d)' % (0, 0),
|
|
||||||
'% steal attempted'
|
|
||||||
)
|
|
||||||
|
|
||||||
def f_SB_steal(stat_dict, player):
|
def f_SB_steal(stat_dict, player):
|
||||||
""" Folded SB to steal."""
|
""" Folded SB to steal."""
|
||||||
stat = 0.0
|
stat = 0.0
|
||||||
|
@ -417,27 +333,7 @@ def f_BB_steal(stat_dict, player):
|
||||||
'(0/0)',
|
'(0/0)',
|
||||||
'% folded BB to steal')
|
'% folded BB to steal')
|
||||||
|
|
||||||
def f_BB_steal_0(stat_dict, player):
|
def three_B(stat_dict, player):
|
||||||
""" Folded BB to steal."""
|
|
||||||
stat = 0.0
|
|
||||||
try:
|
|
||||||
stat = float(stat_dict[player]['bbnotdef'])/float(stat_dict[player]['bbstolen'])
|
|
||||||
return (stat,
|
|
||||||
'%2.0f' % (100*stat) + '%',
|
|
||||||
'fBB=%2.0f' % (100*stat) + '%',
|
|
||||||
'fBB_s=%2.0f' % (100*stat) + '%',
|
|
||||||
'(%d/%d)' % (stat_dict[player]['bbnotdef'], stat_dict[player]['bbstolen']),
|
|
||||||
'% folded BB to steal'
|
|
||||||
)
|
|
||||||
except:
|
|
||||||
return (stat,
|
|
||||||
'NA',
|
|
||||||
'fBB=NA',
|
|
||||||
'fBB_s=NA',
|
|
||||||
'(0/0)',
|
|
||||||
'% folded BB to steal')
|
|
||||||
|
|
||||||
def three_B_0(stat_dict, player):
|
|
||||||
""" Three bet preflop/3rd."""
|
""" Three bet preflop/3rd."""
|
||||||
stat = 0.0
|
stat = 0.0
|
||||||
try:
|
try:
|
||||||
|
@ -479,7 +375,7 @@ def WMsF(stat_dict, player):
|
||||||
'% won$/saw flop/4th'
|
'% won$/saw flop/4th'
|
||||||
)
|
)
|
||||||
|
|
||||||
def a_freq_1(stat_dict, player):
|
def a_freq1(stat_dict, player):
|
||||||
""" Flop/4th aggression frequency."""
|
""" Flop/4th aggression frequency."""
|
||||||
stat = 0.0
|
stat = 0.0
|
||||||
try:
|
try:
|
||||||
|
@ -500,7 +396,7 @@ def a_freq_1(stat_dict, player):
|
||||||
'Aggression Freq flop/4th'
|
'Aggression Freq flop/4th'
|
||||||
)
|
)
|
||||||
|
|
||||||
def a_freq_2(stat_dict, player):
|
def a_freq2(stat_dict, player):
|
||||||
""" Turn/5th aggression frequency."""
|
""" Turn/5th aggression frequency."""
|
||||||
stat = 0.0
|
stat = 0.0
|
||||||
try:
|
try:
|
||||||
|
@ -521,7 +417,7 @@ def a_freq_2(stat_dict, player):
|
||||||
'Aggression Freq turn/5th'
|
'Aggression Freq turn/5th'
|
||||||
)
|
)
|
||||||
|
|
||||||
def a_freq_3(stat_dict, player):
|
def a_freq3(stat_dict, player):
|
||||||
""" River/6th aggression frequency."""
|
""" River/6th aggression frequency."""
|
||||||
stat = 0.0
|
stat = 0.0
|
||||||
try:
|
try:
|
||||||
|
@ -542,7 +438,7 @@ def a_freq_3(stat_dict, player):
|
||||||
'Aggression Freq river/6th'
|
'Aggression Freq river/6th'
|
||||||
)
|
)
|
||||||
|
|
||||||
def a_freq_4(stat_dict, player):
|
def a_freq4(stat_dict, player):
|
||||||
""" 7th street aggression frequency."""
|
""" 7th street aggression frequency."""
|
||||||
stat = 0.0
|
stat = 0.0
|
||||||
try:
|
try:
|
||||||
|
@ -591,34 +487,7 @@ def a_freq_123(stat_dict, player):
|
||||||
'Post-Flop Aggression Freq'
|
'Post-Flop Aggression Freq'
|
||||||
)
|
)
|
||||||
|
|
||||||
def a_freq_123_0(stat_dict, player):
|
def cb1(stat_dict, player):
|
||||||
""" Post-Flop aggression frequency (no decimals)."""
|
|
||||||
stat = 0.0
|
|
||||||
try:
|
|
||||||
stat = float( stat_dict[player]['aggr_1'] + stat_dict[player]['aggr_2'] + stat_dict[player]['aggr_3']) / float( stat_dict[player]['saw_1'] + stat_dict[player]['saw_2'] + stat_dict[player]['saw_3']);
|
|
||||||
return (stat,
|
|
||||||
'%2.0f' % (100*stat) + '%',
|
|
||||||
'afq=%2.0f' % (100*stat) + '%',
|
|
||||||
'postf_aggfq=%2.0f' % (100*stat) + '%',
|
|
||||||
'(%d/%d)' % ( stat_dict[player]['aggr_1']
|
|
||||||
+ stat_dict[player]['aggr_2']
|
|
||||||
+ stat_dict[player]['aggr_3']
|
|
||||||
, stat_dict[player]['saw_1']
|
|
||||||
+ stat_dict[player]['saw_2']
|
|
||||||
+ stat_dict[player]['saw_3']
|
|
||||||
),
|
|
||||||
'Post-Flop Aggression Freq'
|
|
||||||
)
|
|
||||||
except:
|
|
||||||
return (stat,
|
|
||||||
'%2.0f' % (0) + '%',
|
|
||||||
'a3=%2.0f' % (0) + '%',
|
|
||||||
'a_fq_3=%2.0f' % (0) + '%',
|
|
||||||
'(%d/%d)' % (0, 0),
|
|
||||||
'Post-Flop Aggression Freq'
|
|
||||||
)
|
|
||||||
|
|
||||||
def cb_1(stat_dict, player):
|
|
||||||
""" Flop continuation bet."""
|
""" Flop continuation bet."""
|
||||||
stat = 0.0
|
stat = 0.0
|
||||||
try:
|
try:
|
||||||
|
@ -639,7 +508,7 @@ def cb_1(stat_dict, player):
|
||||||
'% continuation bet flop/4th'
|
'% continuation bet flop/4th'
|
||||||
)
|
)
|
||||||
|
|
||||||
def cb_2(stat_dict, player):
|
def cb2(stat_dict, player):
|
||||||
""" Turn continuation bet."""
|
""" Turn continuation bet."""
|
||||||
stat = 0.0
|
stat = 0.0
|
||||||
try:
|
try:
|
||||||
|
@ -660,7 +529,7 @@ def cb_2(stat_dict, player):
|
||||||
'% continuation bet turn/5th'
|
'% continuation bet turn/5th'
|
||||||
)
|
)
|
||||||
|
|
||||||
def cb_3(stat_dict, player):
|
def cb3(stat_dict, player):
|
||||||
""" River continuation bet."""
|
""" River continuation bet."""
|
||||||
stat = 0.0
|
stat = 0.0
|
||||||
try:
|
try:
|
||||||
|
@ -681,7 +550,7 @@ def cb_3(stat_dict, player):
|
||||||
'% continuation bet river/6th'
|
'% continuation bet river/6th'
|
||||||
)
|
)
|
||||||
|
|
||||||
def cb_4(stat_dict, player):
|
def cb4(stat_dict, player):
|
||||||
""" 7th street continuation bet."""
|
""" 7th street continuation bet."""
|
||||||
stat = 0.0
|
stat = 0.0
|
||||||
try:
|
try:
|
||||||
|
@ -702,7 +571,7 @@ def cb_4(stat_dict, player):
|
||||||
'% continuation bet 7th'
|
'% continuation bet 7th'
|
||||||
)
|
)
|
||||||
|
|
||||||
def ffreq_1(stat_dict, player):
|
def ffreq1(stat_dict, player):
|
||||||
""" Flop/4th fold frequency."""
|
""" Flop/4th fold frequency."""
|
||||||
stat = 0.0
|
stat = 0.0
|
||||||
try:
|
try:
|
||||||
|
@ -723,7 +592,7 @@ def ffreq_1(stat_dict, player):
|
||||||
'% fold frequency flop/4th'
|
'% fold frequency flop/4th'
|
||||||
)
|
)
|
||||||
|
|
||||||
def ffreq_2(stat_dict, player):
|
def ffreq2(stat_dict, player):
|
||||||
""" Turn/5th fold frequency."""
|
""" Turn/5th fold frequency."""
|
||||||
stat = 0.0
|
stat = 0.0
|
||||||
try:
|
try:
|
||||||
|
@ -744,7 +613,7 @@ def ffreq_2(stat_dict, player):
|
||||||
'% fold frequency turn/5th'
|
'% fold frequency turn/5th'
|
||||||
)
|
)
|
||||||
|
|
||||||
def ffreq_3(stat_dict, player):
|
def ffreq3(stat_dict, player):
|
||||||
""" River/6th fold frequency."""
|
""" River/6th fold frequency."""
|
||||||
stat = 0.0
|
stat = 0.0
|
||||||
try:
|
try:
|
||||||
|
@ -765,7 +634,7 @@ def ffreq_3(stat_dict, player):
|
||||||
'% fold frequency river/6th'
|
'% fold frequency river/6th'
|
||||||
)
|
)
|
||||||
|
|
||||||
def ffreq_4(stat_dict, player):
|
def ffreq4(stat_dict, player):
|
||||||
""" 7th fold frequency."""
|
""" 7th fold frequency."""
|
||||||
stat = 0.0
|
stat = 0.0
|
||||||
try:
|
try:
|
||||||
|
@ -804,24 +673,24 @@ if __name__== "__main__":
|
||||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'fold_f')
|
print "player = ", player, do_stat(stat_dict, player = player, stat = 'fold_f')
|
||||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'wmsd')
|
print "player = ", player, do_stat(stat_dict, player = player, stat = 'wmsd')
|
||||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'steal')
|
print "player = ", player, do_stat(stat_dict, player = player, stat = 'steal')
|
||||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'f_sb_steal')
|
print "player = ", player, do_stat(stat_dict, player = player, stat = 'f_SB_steal')
|
||||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'f_bb_steal')
|
print "player = ", player, do_stat(stat_dict, player = player, stat = 'f_BB_steal')
|
||||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'three_b_0')
|
print "player = ", player, do_stat(stat_dict, player = player, stat = 'three_B_0')
|
||||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'wmsf')
|
print "player = ", player, do_stat(stat_dict, player = player, stat = 'WMsF')
|
||||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_1')
|
print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq1')
|
||||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_2')
|
print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq2')
|
||||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_3')
|
print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq3')
|
||||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_4')
|
print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq4')
|
||||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_123')
|
print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_123')
|
||||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_123_0')
|
print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_123_0')
|
||||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb_1')
|
print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb1')
|
||||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb_2')
|
print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb2')
|
||||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb_3')
|
print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb3')
|
||||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb_4')
|
print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb4')
|
||||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq_1')
|
print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq1')
|
||||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq_2')
|
print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq2')
|
||||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq_3')
|
print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq3')
|
||||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq_4')
|
print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq4')
|
||||||
print "\n"
|
print "\n"
|
||||||
|
|
||||||
print "\n\nLegal stats:"
|
print "\n\nLegal stats:"
|
||||||
|
|
|
@ -231,7 +231,12 @@ def discover_nt_by_name(c, tablename):
|
||||||
titles = {}
|
titles = {}
|
||||||
win32gui.EnumWindows(win_enum_handler, titles)
|
win32gui.EnumWindows(win_enum_handler, titles)
|
||||||
for hwnd in titles:
|
for hwnd in titles:
|
||||||
if not tablename in titles[hwnd]: continue
|
#print "Tables.py: tablename =", tablename, "title =", titles[hwnd]
|
||||||
|
try:
|
||||||
|
# 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
|
||||||
|
except:
|
||||||
|
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
|
||||||
|
@ -364,7 +369,7 @@ def clean_title(name):
|
||||||
for pattern in [' \(6 max\)', ' \(heads up\)', ' \(deep\)',
|
for pattern in [' \(6 max\)', ' \(heads up\)', ' \(deep\)',
|
||||||
' \(deep hu\)', ' \(deep 6\)', ' \(2\)',
|
' \(deep hu\)', ' \(deep 6\)', ' \(2\)',
|
||||||
' \(edu\)', ' \(edu, 6 max\)', ' \(6\)',
|
' \(edu\)', ' \(edu, 6 max\)', ' \(6\)',
|
||||||
' \(speed\)',
|
' \(speed\)', 'special', 'newVPP',
|
||||||
' no all-in', ' fast', ',', ' 50BB min', '50bb min', '\s+$']:
|
' no all-in', ' fast', ',', ' 50BB min', '50bb min', '\s+$']:
|
||||||
name = re.sub(pattern, '', name)
|
name = re.sub(pattern, '', name)
|
||||||
name = name.rstrip()
|
name = name.rstrip()
|
||||||
|
|
|
@ -33,12 +33,12 @@ class UltimateBet(HandHistoryConverter):
|
||||||
re_Board = re.compile(r"\[(?P<CARDS>.+)\]")
|
re_Board = re.compile(r"\[(?P<CARDS>.+)\]")
|
||||||
# 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):
|
def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True, index=0):
|
||||||
"""\
|
"""\
|
||||||
in_path (default '-' = sys.stdin)
|
in_path (default '-' = sys.stdin)
|
||||||
out_path (default '-' = sys.stdout)
|
out_path (default '-' = sys.stdout)
|
||||||
follow : whether to tail -f the input"""
|
follow : whether to tail -f the input"""
|
||||||
HandHistoryConverter.__init__(self, in_path, out_path, sitename="UltimateBet", follow=follow)
|
HandHistoryConverter.__init__(self, in_path, out_path, sitename="UltimateBet", follow=follow, index=index)
|
||||||
logging.info("Initialising UltimateBetconverter class")
|
logging.info("Initialising UltimateBetconverter class")
|
||||||
self.filetype = "text"
|
self.filetype = "text"
|
||||||
self.codepage = "cp1252"
|
self.codepage = "cp1252"
|
||||||
|
|
381
pyfpdb/Win2dayToFpdb.py
Executable file
381
pyfpdb/Win2dayToFpdb.py
Executable file
|
@ -0,0 +1,381 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Copyright 2008, Carl Gherardi
|
||||||
|
#
|
||||||
|
# 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
|
||||||
|
########################################################################
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import datetime
|
||||||
|
from HandHistoryConverter import *
|
||||||
|
|
||||||
|
# Win2day HH Format
|
||||||
|
|
||||||
|
class Win2day(HandHistoryConverter):
|
||||||
|
|
||||||
|
# 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="(?P<HID>[0-9]+)" SESSION="session[0-9]+\.xml" TABLE="(?P<TABLE>[- a-zA-Z0-9]+)" GAME="(?P<GAME>[_A-Z]+)" GAMETYPE="[_a-zA-Z]+" GAMEKIND="[_a-zA-Z]+" TABLECURRENCY="(?P<CURRENCY>[A-Z]+)" LIMIT="(?P<LIMIT>NL|PL)" STAKES="(?P<SB>[.0-9]+)/(?P<BB>[.0-9]+)" DATE="(?P<DATETIME>[0-9]+)" WIN="[.0-9]+" LOSS="[.0-9]+">$'
|
||||||
|
re_GameInfo = re.compile('^<HISTORY ID="(?P<HID>[0-9]+)" SESSION="session[0-9]+\.xml" TABLE="(?P<TABLE>[- a-zA-Z0-9]+)" GAME="(?P<GAME>[_A-Z]+)" GAMETYPE="[_a-zA-Z]+" GAMEKIND="[_a-zA-Z]+" TABLECURRENCY="(?P<CURRENCY>[A-Z]+)" LIMIT="(?P<LIMIT>NL|PL)" STAKES="(?P<SB>[.0-9]+)/(?P<BB>[.0-9]+)" DATE="(?P<DATETIME>[0-9]+)" WIN="[.0-9]+" LOSS="[.0-9]+">', re.MULTILINE)
|
||||||
|
re_SplitHands = re.compile('</HISTORY>')
|
||||||
|
re_HandInfo = re.compile("^Table \'(?P<TABLE>[- a-zA-Z]+)\'(?P<TABLEATTRIBUTES>.+?$)?", re.MULTILINE)
|
||||||
|
re_Button = re.compile('<ACTION TYPE="HAND_DEAL" PLAYER="(?P<BUTTON>[^"]+)">\n<CARD LINK="[0-9b]+"></CARD>\n<CARD LINK="[0-9b]+"></CARD></ACTION>\n<ACTION TYPE="ACTION_', re.MULTILINE)
|
||||||
|
#<PLAYER NAME="prato" SEAT="1" AMOUNT="61.29"></PLAYER>
|
||||||
|
re_PlayerInfo = re.compile('^<PLAYER NAME="(?P<PNAME>.*)" SEAT="(?P<SEAT>[0-9]+)" AMOUNT="(?P<CASH>[.0-9]+)"></PLAYER>', 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)
|
||||||
|
|
||||||
|
def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True, index=0):
|
||||||
|
HandHistoryConverter.__init__(self, in_path, out_path, sitename="Win2day", follow=follow, index=index)
|
||||||
|
logging.info("Initialising Win2day converter class")
|
||||||
|
self.filetype = "text"
|
||||||
|
self.codepage = "cp1252"
|
||||||
|
self.sideID = 4
|
||||||
|
if autostart:
|
||||||
|
self.start()
|
||||||
|
|
||||||
|
|
||||||
|
def compilePlayerRegexs(self, hand):
|
||||||
|
players = set([player[1] for player in hand.players])
|
||||||
|
if not players <= self.compiledPlayers: # x <= y means 'x is subset of y'
|
||||||
|
# we need to recompile the player regexs.
|
||||||
|
self.compiledPlayers = players
|
||||||
|
player_re = "(?P<PNAME>" + "|".join(map(re.escape, players)) + ")"
|
||||||
|
logging.debug("player_re: " + player_re)
|
||||||
|
#<ACTION TYPE="HAND_BLINDS" PLAYER="prato" KIND="HAND_SB" VALUE="0.25"></ACTION>
|
||||||
|
|
||||||
|
self.re_PostSB = re.compile(r'^<ACTION TYPE="HAND_BLINDS" PLAYER="%s" KIND="HAND_SB" VALUE="(?P<SB>[.0-9]+)"></ACTION>' % player_re, re.MULTILINE)
|
||||||
|
self.re_PostBB = re.compile(r'^<ACTION TYPE="HAND_BLINDS" PLAYER="%s" KIND="HAND_BB" VALUE="(?P<BB>[.0-9]+)"></ACTION>' % player_re, re.MULTILINE)
|
||||||
|
self.re_Antes = re.compile(r"^%s: posts the ante \$?(?P<ANTE>[.0-9]+)" % player_re, re.MULTILINE)
|
||||||
|
self.re_BringIn = re.compile(r"^%s: brings[- ]in( low|) for \$?(?P<BRINGIN>[.0-9]+)" % 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>'
|
||||||
|
self.re_HeroCards = re.compile(r'<ACTION TYPE="HAND_DEAL" PLAYER="%s">\n(?P<CARDS><CARD LINK="[0-9]+"></CARD>\n<CARD LINK="[0-9]"></CARD>)</ACTION>' % player_re, re.MULTILINE)
|
||||||
|
|
||||||
|
#'^<ACTION TYPE="(?P<ATYPE>[_A-Z]+)" PLAYER="%s"( VALUE="(?P<BET>[.0-9]+)")?></ACTION>'
|
||||||
|
self.re_Action = re.compile(r'^<ACTION TYPE="(?P<ATYPE>[_A-Z]+)" PLAYER="%s"( VALUE="(?P<BET>[.0-9]+)")?></ACTION>' % player_re, re.MULTILINE)
|
||||||
|
|
||||||
|
self.re_ShowdownAction = re.compile(r'<RESULT PLAYER="%s" WIN="[.0-9]+" HAND="(?P<HAND>\(\$STR_G_FOLD\)|[\$\(\)_ A-Z]+)">\n(?P<CARDS><CARD LINK="[0-9]+"></CARD>\n<CARD LINK="[0-9]+"></CARD>)</RESULT>' % player_re, re.MULTILINE)
|
||||||
|
#<RESULT PLAYER="wig0r" WIN="4.10" HAND="$(STR_G_WIN_TWOPAIR) $(STR_G_CARDS_TENS) $(STR_G_ANDTEXT) $(STR_G_CARDS_EIGHTS)">
|
||||||
|
#
|
||||||
|
self.re_CollectPot = re.compile(r'<RESULT PLAYER="%s" WIN="(?P<POT>[.\d]+)" HAND=".+">' % player_re, re.MULTILINE)
|
||||||
|
self.re_sitsOut = re.compile("^%s sits out" % player_re, re.MULTILINE)
|
||||||
|
self.re_ShownCards = re.compile("^Seat (?P<SEAT>[0-9]+): %s \(.*\) showed \[(?P<CARDS>.*)\].*" % player_re, re.MULTILINE)
|
||||||
|
|
||||||
|
|
||||||
|
def readSupportedGames(self):
|
||||||
|
return [["ring", "hold", "nl"],
|
||||||
|
["ring", "hold", "pl"],
|
||||||
|
["ring", "hold", "fl"],
|
||||||
|
["ring", "stud", "fl"],
|
||||||
|
["ring", "draw", "fl"],
|
||||||
|
["ring", "omaha", "pl"]
|
||||||
|
]
|
||||||
|
|
||||||
|
def determineGameType(self, handText):
|
||||||
|
info = {'type':'ring'}
|
||||||
|
|
||||||
|
m = self.re_GameInfo.search(handText)
|
||||||
|
if not m:
|
||||||
|
print "determineGameType:", handText
|
||||||
|
return None
|
||||||
|
|
||||||
|
mg = m.groupdict()
|
||||||
|
|
||||||
|
# translations from captured groups to our info strings
|
||||||
|
#limits = { 'NL':'nl', 'PL':'pl', 'Limit':'fl' }
|
||||||
|
limits = { 'NL':'nl', 'PL':'pl'}
|
||||||
|
games = { # base, category
|
||||||
|
"GAME_THM" : ('hold','holdem'),
|
||||||
|
# 'Omaha' : ('hold','omahahi'),
|
||||||
|
#'Omaha Hi/Lo' : ('hold','omahahilo'),
|
||||||
|
# 'Razz' : ('stud','razz'),
|
||||||
|
#'7 Card Stud' : ('stud','studhi'),
|
||||||
|
# 'Badugi' : ('draw','badugi')
|
||||||
|
}
|
||||||
|
if 'LIMIT' in mg:
|
||||||
|
info['limitType'] = limits[mg['LIMIT']]
|
||||||
|
if 'GAME' in mg:
|
||||||
|
(info['base'], info['category']) = games[mg['GAME']]
|
||||||
|
if 'SB' in mg:
|
||||||
|
info['sb'] = mg['SB']
|
||||||
|
if 'BB' in mg:
|
||||||
|
info['bb'] = mg['BB']
|
||||||
|
if 'CURRENCY' in mg:
|
||||||
|
info['currency'] = mg['CURRENCY']
|
||||||
|
# NB: SB, BB must be interpreted as blinds or bets depending on limit type.
|
||||||
|
|
||||||
|
return info
|
||||||
|
|
||||||
|
|
||||||
|
def readHandInfo(self, hand):
|
||||||
|
info = {}
|
||||||
|
m = self.re_HandInfo.search(hand.handText,re.DOTALL)
|
||||||
|
if m:
|
||||||
|
info.update(m.groupdict())
|
||||||
|
# TODO: Be less lazy and parse maxseats from the HandInfo regex
|
||||||
|
if m.group('TABLEATTRIBUTES'):
|
||||||
|
m2 = re.search("\s*(\d+)-max", m.group('TABLEATTRIBUTES'))
|
||||||
|
hand.maxseats = int(m2.group(1))
|
||||||
|
m = self.re_GameInfo.search(hand.handText)
|
||||||
|
if m: info.update(m.groupdict())
|
||||||
|
m = self.re_Button.search(hand.handText)
|
||||||
|
if m: info.update(m.groupdict())
|
||||||
|
# TODO : I rather like the idea of just having this dict as hand.info
|
||||||
|
logging.debug("readHandInfo: %s" % info)
|
||||||
|
for key in info:
|
||||||
|
if key == 'DATETIME':
|
||||||
|
# Win2day uses UTC timestamp
|
||||||
|
hand.starttime = datetime.datetime.fromtimestamp(int(info[key]))
|
||||||
|
if key == 'HID':
|
||||||
|
hand.handid = info[key]
|
||||||
|
if key == 'TABLE':
|
||||||
|
hand.tablename = info[key]
|
||||||
|
if key == 'BUTTON':
|
||||||
|
hand.buttonpos = info[key]
|
||||||
|
|
||||||
|
def readButton(self, hand):
|
||||||
|
m = self.re_Button.search(hand.handText)
|
||||||
|
if m:
|
||||||
|
for player in hand.players:
|
||||||
|
if player[1] == m.group('BUTTON'):
|
||||||
|
hand.buttonpos = player[0]
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
logging.info('readButton: not found')
|
||||||
|
|
||||||
|
def readPlayerStacks(self, hand):
|
||||||
|
logging.debug("readPlayerStacks")
|
||||||
|
m = self.re_PlayerInfo.finditer(hand.handText)
|
||||||
|
players = []
|
||||||
|
for a in m:
|
||||||
|
hand.addPlayer(int(a.group('SEAT')), a.group('PNAME'), a.group('CASH'))
|
||||||
|
|
||||||
|
def markStreets(self, hand):
|
||||||
|
# PREFLOP = ** Dealing down cards **
|
||||||
|
# This re fails if, say, river is missing; then we don't get the ** that starts the river.
|
||||||
|
if hand.gametype['base'] in ("hold"):
|
||||||
|
#m = re.search(r"\*\*\* HOLE CARDS \*\*\*(?P<PREFLOP>.+(?=\*\*\* FLOP \*\*\*)|.+)"
|
||||||
|
# r"(\*\*\* FLOP \*\*\*(?P<FLOP> \[\S\S \S\S \S\S\].+(?=\*\*\* TURN \*\*\*)|.+))?"
|
||||||
|
# r"(\*\*\* TURN \*\*\* \[\S\S \S\S \S\S] (?P<TURN>\[\S\S\].+(?=\*\*\* RIVER \*\*\*)|.+))?"
|
||||||
|
# r"(\*\*\* RIVER \*\*\* \[\S\S \S\S \S\S \S\S] (?P<RIVER>\[\S\S\].+))?", hand.handText,re.DOTALL)
|
||||||
|
|
||||||
|
m = re.search('<ACTION TYPE="HAND_BLINDS" PLAYER=".+" KIND="HAND_BB" VALUE="[.0-9]+"></ACTION>(?P<PREFLOP>.+(?=<ACTION TYPE="HAND_BOARD" VALUE="BOARD_FLOP")|.+)'
|
||||||
|
'((?P<FLOP><ACTION TYPE="HAND_BOARD" VALUE="BOARD_FLOP" POT="[.0-9]+">.+(?=<ACTION TYPE="HAND_BOARD" VALUE="BOARD_TURN")|.+))?'
|
||||||
|
'((?P<TURN><ACTION TYPE="HAND_BOARD" VALUE="BOARD_TURN" POT="[.0-9]+">.+(?=<ACTION TYPE="HAND_BOARD" VALUE="BOARD_RIVER")|.+))?'
|
||||||
|
'((?P<RIVER><ACTION TYPE="HAND_BOARD" VALUE="BOARD_RIVER" POT="[.0-9]+">.+(?=<SHOWDOWN NAME="HAND_SHOWDOWN")|.+))?', hand.handText,re.DOTALL)
|
||||||
|
|
||||||
|
hand.addStreets(m)
|
||||||
|
|
||||||
|
def readCommunityCards(self, hand, street): # street has been matched by markStreets, so exists in this hand
|
||||||
|
if street in ('FLOP','TURN','RIVER'): # a list of streets which get dealt community cards (i.e. all but PREFLOP)
|
||||||
|
#print "DEBUG readCommunityCards:", street, hand.streets.group(street)
|
||||||
|
|
||||||
|
boardCards = set([])
|
||||||
|
if street == 'FLOP':
|
||||||
|
m = self.re_Card.findall(hand.streets[street])
|
||||||
|
for card in m:
|
||||||
|
boardCards.add(self.convertWin2dayCards(card))
|
||||||
|
else:
|
||||||
|
m = self.re_BoardLast.search(hand.streets[street])
|
||||||
|
boardCards.add(self.convertWin2dayCards(m.group('CARD')))
|
||||||
|
|
||||||
|
hand.setCommunityCards(street, boardCards)
|
||||||
|
|
||||||
|
def readAntes(self, hand):
|
||||||
|
logging.debug("reading antes")
|
||||||
|
m = self.re_Antes.finditer(hand.handText)
|
||||||
|
for player in m:
|
||||||
|
#~ logging.debug("hand.addAnte(%s,%s)" %(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):
|
||||||
|
try:
|
||||||
|
m = self.re_PostSB.search(hand.handText)
|
||||||
|
hand.addBlind(m.group('PNAME'), 'small blind', m.group('SB'))
|
||||||
|
except: # no small blind
|
||||||
|
hand.addBlind(None, None, None)
|
||||||
|
for a in self.re_PostBB.finditer(hand.handText):
|
||||||
|
hand.addBlind(a.group('PNAME'), 'big blind', a.group('BB'))
|
||||||
|
for a in self.re_PostBoth.finditer(hand.handText):
|
||||||
|
hand.addBlind(a.group('PNAME'), 'small & big blinds', a.group('SBBB'))
|
||||||
|
|
||||||
|
def readHeroCards(self, hand):
|
||||||
|
# streets PREFLOP, PREDRAW, and THIRD are special cases beacause
|
||||||
|
# we need to grab hero's cards
|
||||||
|
m = self.re_HeroCards.finditer(hand.streets['PREFLOP'])
|
||||||
|
newcards = []
|
||||||
|
for found in m:
|
||||||
|
hand.hero = found.group('PNAME')
|
||||||
|
for card in self.re_Card.finditer(found.group('CARDS')):
|
||||||
|
print self.convertWin2dayCards(card.group('CARD'))
|
||||||
|
newcards.append(self.convertWin2dayCards(card.group('CARD')))
|
||||||
|
|
||||||
|
#hand.addHoleCards(holeCards, m.group('PNAME'))
|
||||||
|
hand.addHoleCards('PREFLOP', hand.hero, closed=newcards, shown=False, mucked=False, dealt=True)
|
||||||
|
|
||||||
|
def convertWin2dayCards(self, card):
|
||||||
|
card = int(card)
|
||||||
|
retCard = ''
|
||||||
|
cardconvert = { 1:'A',
|
||||||
|
10:'T',
|
||||||
|
11:'J',
|
||||||
|
12:'Q',
|
||||||
|
13:'K'}
|
||||||
|
realNumber = card % 13 + 1
|
||||||
|
if(realNumber in cardconvert):
|
||||||
|
retCard += cardconvert[realNumber]
|
||||||
|
else:
|
||||||
|
retCard += str(realNumber)
|
||||||
|
|
||||||
|
if(card > 38):
|
||||||
|
retCard += 's'
|
||||||
|
elif(card > 25):
|
||||||
|
retCard += 'h'
|
||||||
|
elif(card > 12):
|
||||||
|
retCard += 'c'
|
||||||
|
else:
|
||||||
|
retCard += 'd'
|
||||||
|
|
||||||
|
return(retCard)
|
||||||
|
|
||||||
|
def readDrawCards(self, hand, street):
|
||||||
|
logging.debug("readDrawCards")
|
||||||
|
m = self.re_HeroCards.finditer(hand.streets[street])
|
||||||
|
if m == None:
|
||||||
|
hand.involved = False
|
||||||
|
else:
|
||||||
|
for player in m:
|
||||||
|
hand.hero = player.group('PNAME') # Only really need to do this once
|
||||||
|
newcards = player.group('NEWCARDS')
|
||||||
|
oldcards = player.group('OLDCARDS')
|
||||||
|
if newcards == None:
|
||||||
|
newcards = set()
|
||||||
|
else:
|
||||||
|
newcards = set(newcards.split(' '))
|
||||||
|
if oldcards == None:
|
||||||
|
oldcards = set()
|
||||||
|
else:
|
||||||
|
oldcards = set(oldcards.split(' '))
|
||||||
|
hand.addDrawHoleCards(newcards, oldcards, player.group('PNAME'), street)
|
||||||
|
|
||||||
|
|
||||||
|
def readStudPlayerCards(self, hand, street):
|
||||||
|
# See comments of reference implementation in FullTiltToFpdb.py
|
||||||
|
logging.debug("readStudPlayerCards")
|
||||||
|
m = self.re_HeroCards.finditer(hand.streets[street])
|
||||||
|
for player in m:
|
||||||
|
#~ logging.debug(player.groupdict())
|
||||||
|
(pname, oldcards, newcards) = (player.group('PNAME'), player.group('OLDCARDS'), player.group('NEWCARDS'))
|
||||||
|
if oldcards:
|
||||||
|
oldcards = [c.strip() for c in oldcards.split(' ')]
|
||||||
|
if newcards:
|
||||||
|
newcards = [c.strip() for c in newcards.split(' ')]
|
||||||
|
if street=='ANTES':
|
||||||
|
return
|
||||||
|
elif street=='THIRD':
|
||||||
|
# we'll have observed hero holecards in CARDS and thirdstreet open cards in 'NEWCARDS'
|
||||||
|
# hero: [xx][o]
|
||||||
|
# others: [o]
|
||||||
|
hand.addPlayerCards(player = player.group('PNAME'), street = street, closed = oldcards, open = newcards)
|
||||||
|
elif street in ('FOURTH', 'FIFTH', 'SIXTH'):
|
||||||
|
# 4th:
|
||||||
|
# hero: [xxo] [o]
|
||||||
|
# others: [o] [o]
|
||||||
|
# 5th:
|
||||||
|
# hero: [xxoo] [o]
|
||||||
|
# others: [oo] [o]
|
||||||
|
# 6th:
|
||||||
|
# hero: [xxooo] [o]
|
||||||
|
# others: [ooo] [o]
|
||||||
|
hand.addPlayerCards(player = player.group('PNAME'), street = street, open = newcards)
|
||||||
|
# we may additionally want to check the earlier streets tally with what we have but lets trust it for now.
|
||||||
|
elif street=='SEVENTH' and newcards:
|
||||||
|
# hero: [xxoooo] [x]
|
||||||
|
# others: not reported.
|
||||||
|
hand.addPlayerCards(player = player.group('PNAME'), street = street, closed = newcards)
|
||||||
|
|
||||||
|
def readAction(self, hand, street):
|
||||||
|
m = self.re_Action.finditer(hand.streets[street])
|
||||||
|
for action in m:
|
||||||
|
if action.group('ATYPE') == 'ACTION_RAISE':
|
||||||
|
hand.addRaiseBy( street, action.group('PNAME'), action.group('BET') )
|
||||||
|
elif action.group('ATYPE') == 'ACTION_CALL':
|
||||||
|
hand.addCall( street, action.group('PNAME'), action.group('BET') )
|
||||||
|
elif action.group('ATYPE') == 'ACTION_ALLIN':
|
||||||
|
hand.addRaiseBy( street, action.group('PNAME'), action.group('BET') )
|
||||||
|
elif action.group('ATYPE') == 'ACTION_BET':
|
||||||
|
hand.addBet( street, action.group('PNAME'), action.group('BET') )
|
||||||
|
elif action.group('ATYPE') == 'ACTION_FOLD':
|
||||||
|
hand.addFold( street, action.group('PNAME'))
|
||||||
|
elif action.group('ATYPE') == 'ACTION_CHECK':
|
||||||
|
hand.addCheck( street, action.group('PNAME'))
|
||||||
|
elif action.group('ATYPE') == 'ACTION_DISCARD':
|
||||||
|
hand.addDiscard(street, action.group('PNAME'), action.group('NODISCARDED'), action.group('DISCARDED'))
|
||||||
|
elif action.group('ATYPE') == 'ACTION_STAND':
|
||||||
|
hand.addStandsPat( street, action.group('PNAME'))
|
||||||
|
else:
|
||||||
|
print "DEBUG: unimplemented readAction: '%s' '%s'" %(action.group('PNAME'),action.group('ATYPE'),)
|
||||||
|
|
||||||
|
|
||||||
|
def readShowdownActions(self, hand):
|
||||||
|
for shows in self.re_ShowdownAction.finditer(hand.handText):
|
||||||
|
showdownCards = set([])
|
||||||
|
for card in self.re_Card.finditer(shows.group('CARDS')):
|
||||||
|
#print "DEBUG:", card, card.group('CARD'), self.convertWin2dayCards(card.group('CARD'))
|
||||||
|
showdownCards.add(self.convertWin2dayCards(card.group('CARD')))
|
||||||
|
|
||||||
|
hand.addShownCards(showdownCards, shows.group('PNAME'))
|
||||||
|
|
||||||
|
def readCollectPot(self,hand):
|
||||||
|
for m in self.re_CollectPot.finditer(hand.handText):
|
||||||
|
potcoll = Decimal(m.group('POT'))
|
||||||
|
if potcoll > 0:
|
||||||
|
hand.addCollectPot(player=m.group('PNAME'),pot=potcoll)
|
||||||
|
|
||||||
|
def readShownCards(self,hand):
|
||||||
|
for m in self.re_ShownCards.finditer(hand.handText):
|
||||||
|
if m.group('CARDS') is not None:
|
||||||
|
cards = m.group('CARDS')
|
||||||
|
cards = set(cards.split(' '))
|
||||||
|
hand.addShownCards(cards=cards, player=m.group('PNAME'))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = OptionParser()
|
||||||
|
parser.add_option("-i", "--input", dest="ipath", help="parse input hand history", 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("-q", "--quiet",
|
||||||
|
action="store_const", const=logging.CRITICAL, dest="verbosity", default=logging.INFO)
|
||||||
|
parser.add_option("-v", "--verbose",
|
||||||
|
action="store_const", const=logging.INFO, dest="verbosity")
|
||||||
|
parser.add_option("--vv",
|
||||||
|
action="store_const", const=logging.DEBUG, dest="verbosity")
|
||||||
|
|
||||||
|
(options, args) = parser.parse_args()
|
||||||
|
|
||||||
|
LOG_FILENAME = './logging.out'
|
||||||
|
logging.basicConfig(filename=LOG_FILENAME,level=options.verbosity)
|
||||||
|
|
||||||
|
e = Win2day(in_path = options.ipath, out_path = options.opath, follow = options.follow)
|
154
pyfpdb/fpdb.py
154
pyfpdb/fpdb.py
|
@ -17,26 +17,26 @@
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from optparse import OptionParser
|
import threading
|
||||||
|
import Options
|
||||||
|
import string
|
||||||
parser = OptionParser()
|
cl_options = string.join(sys.argv[1:])
|
||||||
parser.add_option("-x", "--errorsToConsole", action="store_true",
|
(options, sys.argv) = Options.fpdb_options()
|
||||||
help="If passed error output will go to the console rather than .")
|
|
||||||
parser.add_option("-d", "--databaseName", dest="dbname", default="fpdb",
|
|
||||||
help="Overrides the default database name")
|
|
||||||
(options, sys.argv) = parser.parse_args()
|
|
||||||
|
|
||||||
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('fpdb-error-log.txt', 'w', 0)
|
||||||
sys.stderr = errorFile
|
sys.stderr = errorFile
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
import pygtk
|
import pygtk
|
||||||
pygtk.require('2.0')
|
pygtk.require('2.0')
|
||||||
import gtk
|
import gtk
|
||||||
|
|
||||||
import fpdb_db
|
import interlocks
|
||||||
|
|
||||||
|
|
||||||
import fpdb_simple
|
import fpdb_simple
|
||||||
import GuiBulkImport
|
import GuiBulkImport
|
||||||
import GuiPlayerStats
|
import GuiPlayerStats
|
||||||
|
@ -45,6 +45,8 @@ import GuiTableViewer
|
||||||
import GuiAutoImport
|
import GuiAutoImport
|
||||||
import GuiGraphViewer
|
import GuiGraphViewer
|
||||||
import GuiSessionViewer
|
import GuiSessionViewer
|
||||||
|
import SQL
|
||||||
|
import Database
|
||||||
import FpdbSQLQueries
|
import FpdbSQLQueries
|
||||||
import Configuration
|
import Configuration
|
||||||
|
|
||||||
|
@ -117,11 +119,13 @@ class fpdb:
|
||||||
def dia_create_del_database(self, widget, data=None):
|
def dia_create_del_database(self, widget, data=None):
|
||||||
print "todo: implement dia_create_del_database"
|
print "todo: implement dia_create_del_database"
|
||||||
self.obtain_global_lock()
|
self.obtain_global_lock()
|
||||||
|
self.release_global_lock()
|
||||||
#end def dia_create_del_database
|
#end def dia_create_del_database
|
||||||
|
|
||||||
def dia_create_del_user(self, widget, data=None):
|
def dia_create_del_user(self, widget, data=None):
|
||||||
print "todo: implement dia_create_del_user"
|
print "todo: implement dia_create_del_user"
|
||||||
self.obtain_global_lock()
|
self.obtain_global_lock()
|
||||||
|
self.release_global_lock()
|
||||||
#end def dia_create_del_user
|
#end def dia_create_del_user
|
||||||
|
|
||||||
def dia_database_stats(self, widget, data=None):
|
def dia_database_stats(self, widget, data=None):
|
||||||
|
@ -130,7 +134,7 @@ class fpdb:
|
||||||
#end def dia_database_stats
|
#end def dia_database_stats
|
||||||
|
|
||||||
def dia_database_sessions(self, widget, data=None):
|
def dia_database_sessions(self, widget, data=None):
|
||||||
new_sessions_thread=GuiSessionViewer.GuiSessionViewer(self.config, self.querydict)
|
new_sessions_thread=GuiSessionViewer.GuiSessionViewer(self.config, self.sql)
|
||||||
self.threads.append(new_sessions_thread)
|
self.threads.append(new_sessions_thread)
|
||||||
sessions_tab=new_sessions_thread.get_vbox()
|
sessions_tab=new_sessions_thread.get_vbox()
|
||||||
self.add_and_display_tab(sessions_tab, "Sessions")
|
self.add_and_display_tab(sessions_tab, "Sessions")
|
||||||
|
@ -138,16 +142,19 @@ class fpdb:
|
||||||
def dia_delete_db_parts(self, widget, data=None):
|
def dia_delete_db_parts(self, widget, data=None):
|
||||||
print "todo: implement dia_delete_db_parts"
|
print "todo: implement dia_delete_db_parts"
|
||||||
self.obtain_global_lock()
|
self.obtain_global_lock()
|
||||||
|
self.release_global_lock()
|
||||||
#end def dia_delete_db_parts
|
#end def dia_delete_db_parts
|
||||||
|
|
||||||
def dia_edit_profile(self, widget=None, data=None, create_default=False, path=None):
|
def dia_edit_profile(self, widget=None, data=None, create_default=False, path=None):
|
||||||
print "todo: implement dia_edit_profile"
|
print "todo: implement dia_edit_profile"
|
||||||
self.obtain_global_lock()
|
self.obtain_global_lock()
|
||||||
|
self.release_global_lock()
|
||||||
#end def dia_edit_profile
|
#end def dia_edit_profile
|
||||||
|
|
||||||
def dia_export_db(self, widget, data=None):
|
def dia_export_db(self, widget, data=None):
|
||||||
print "todo: implement dia_export_db"
|
print "todo: implement dia_export_db"
|
||||||
self.obtain_global_lock()
|
self.obtain_global_lock()
|
||||||
|
self.release_global_lock()
|
||||||
#end def dia_export_db
|
#end def dia_export_db
|
||||||
|
|
||||||
def dia_get_db_root_credentials(self):
|
def dia_get_db_root_credentials(self):
|
||||||
|
@ -173,6 +180,7 @@ class fpdb:
|
||||||
def dia_import_db(self, widget, data=None):
|
def dia_import_db(self, widget, data=None):
|
||||||
print "todo: implement dia_import_db"
|
print "todo: implement dia_import_db"
|
||||||
self.obtain_global_lock()
|
self.obtain_global_lock()
|
||||||
|
self.release_global_lock()
|
||||||
#end def dia_import_db
|
#end def dia_import_db
|
||||||
|
|
||||||
def dia_licensing(self, widget, data=None):
|
def dia_licensing(self, widget, data=None):
|
||||||
|
@ -181,19 +189,23 @@ class fpdb:
|
||||||
|
|
||||||
def dia_load_profile(self, widget, data=None):
|
def dia_load_profile(self, widget, data=None):
|
||||||
"""Dialogue to select a file to load a profile from"""
|
"""Dialogue to select a file to load a profile from"""
|
||||||
if self.obtain_global_lock() == 0: # returns 0 if successful
|
if self.obtain_global_lock(): # returns true if successful
|
||||||
try:
|
#try:
|
||||||
chooser = gtk.FileChooserDialog(title="Please select a profile file to load",
|
# chooser = gtk.FileChooserDialog(title="Please select a profile file to load",
|
||||||
action=gtk.FILE_CHOOSER_ACTION_OPEN,
|
# action=gtk.FILE_CHOOSER_ACTION_OPEN,
|
||||||
buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
|
# buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
|
||||||
chooser.set_filename(self.profile)
|
# chooser.set_filename(self.profile)
|
||||||
|
|
||||||
response = chooser.run()
|
# response = chooser.run()
|
||||||
chooser.destroy()
|
# chooser.destroy()
|
||||||
if response == gtk.RESPONSE_OK:
|
# if response == gtk.RESPONSE_OK:
|
||||||
self.load_profile(chooser.get_filename())
|
# self.load_profile(chooser.get_filename())
|
||||||
elif response == gtk.RESPONSE_CANCEL:
|
# elif response == gtk.RESPONSE_CANCEL:
|
||||||
print 'User cancelled loading profile'
|
# print 'User cancelled loading profile'
|
||||||
|
#except:
|
||||||
|
# pass
|
||||||
|
try:
|
||||||
|
self.load_profile()
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
self.release_global_lock()
|
self.release_global_lock()
|
||||||
|
@ -201,39 +213,41 @@ class fpdb:
|
||||||
|
|
||||||
def dia_recreate_tables(self, widget, data=None):
|
def dia_recreate_tables(self, widget, data=None):
|
||||||
"""Dialogue that asks user to confirm that he wants to delete and recreate the tables"""
|
"""Dialogue that asks user to confirm that he wants to delete and recreate the tables"""
|
||||||
if self.obtain_global_lock() in (0,2): # returns 0 if successful, 2 if Hands table does not exist
|
if self.obtain_global_lock(): # returns true if successful
|
||||||
|
|
||||||
lock_released = False
|
#lock_released = False
|
||||||
try:
|
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 "+self.db.database+" on "+self.db.host+" they will be deleted."
|
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."
|
||||||
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.backend == self.fdb_lock.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.fdb.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.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:
|
except:
|
||||||
pass
|
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
|
||||||
|
|
||||||
def dia_regression_test(self, widget, data=None):
|
def dia_regression_test(self, widget, data=None):
|
||||||
print "todo: implement dia_regression_test"
|
print "todo: implement dia_regression_test"
|
||||||
self.obtain_global_lock()
|
self.obtain_global_lock()
|
||||||
|
self.release_global_lock()
|
||||||
#end def dia_regression_test
|
#end def dia_regression_test
|
||||||
|
|
||||||
def dia_save_profile(self, widget, data=None):
|
def dia_save_profile(self, widget, data=None):
|
||||||
|
@ -350,28 +364,27 @@ class fpdb:
|
||||||
|
|
||||||
def load_profile(self):
|
def load_profile(self):
|
||||||
"""Loads profile from the provided path name."""
|
"""Loads profile from the provided path name."""
|
||||||
|
self.config = Configuration.Config(file=options.config, dbname=options.dbname)
|
||||||
self.settings = {}
|
self.settings = {}
|
||||||
|
self.settings['global_lock'] = self.lock
|
||||||
if (os.sep=="/"):
|
if (os.sep=="/"):
|
||||||
self.settings['os']="linuxmac"
|
self.settings['os']="linuxmac"
|
||||||
else:
|
else:
|
||||||
self.settings['os']="windows"
|
self.settings['os']="windows"
|
||||||
|
|
||||||
|
self.settings.update({'cl_options': cl_options})
|
||||||
self.settings.update(self.config.get_db_parameters())
|
self.settings.update(self.config.get_db_parameters())
|
||||||
self.settings.update(self.config.get_tv_parameters())
|
self.settings.update(self.config.get_tv_parameters())
|
||||||
self.settings.update(self.config.get_import_parameters())
|
self.settings.update(self.config.get_import_parameters())
|
||||||
self.settings.update(self.config.get_default_paths())
|
self.settings.update(self.config.get_default_paths())
|
||||||
|
|
||||||
if self.db!=None:
|
if self.db != None and self.db.fdb != None:
|
||||||
self.db.disconnect()
|
self.db.disconnect()
|
||||||
|
|
||||||
self.db = fpdb_db.fpdb_db()
|
self.sql = SQL.Sql(type = self.settings['db-type'], db_server = self.settings['db-server'])
|
||||||
#print "end of fpdb.load_profile, databaseName:",self.settings['db-databaseName']
|
self.db = Database.Database(self.config, sql = self.sql)
|
||||||
self.db.connect(self.settings['db-backend'],
|
|
||||||
self.settings['db-host'],
|
if self.db.fdb.wrongDbVersion:
|
||||||
self.settings['db-databaseName'],
|
|
||||||
self.settings['db-user'],
|
|
||||||
self.settings['db-password'])
|
|
||||||
if self.db.wrongDbVersion:
|
|
||||||
diaDbVersionWarning = gtk.Dialog(title="Strong Warning - Invalid database version", parent=None, flags=0, buttons=(gtk.STOCK_OK,gtk.RESPONSE_OK))
|
diaDbVersionWarning = gtk.Dialog(title="Strong Warning - Invalid database version", parent=None, flags=0, buttons=(gtk.STOCK_OK,gtk.RESPONSE_OK))
|
||||||
|
|
||||||
label = gtk.Label("An invalid DB version or missing tables have been detected.")
|
label = gtk.Label("An invalid DB version or missing tables have been detected.")
|
||||||
|
@ -389,9 +402,16 @@ class fpdb:
|
||||||
response = diaDbVersionWarning.run()
|
response = diaDbVersionWarning.run()
|
||||||
diaDbVersionWarning.destroy()
|
diaDbVersionWarning.destroy()
|
||||||
|
|
||||||
|
if self.status_bar == None:
|
||||||
|
self.status_bar = gtk.Label("Status: Connected to %s database named %s on host %s"%(self.db.get_backend_name(),self.db.fdb.database, self.db.fdb.host))
|
||||||
|
self.main_vbox.pack_end(self.status_bar, False, True, 0)
|
||||||
|
self.status_bar.show()
|
||||||
|
else:
|
||||||
|
self.status_bar.set_text("Status: Connected to %s database named %s on host %s" % (self.db.get_backend_name(),self.db.fdb.database, self.db.fdb.host))
|
||||||
|
|
||||||
# Database connected to successfully, load queries to pass on to other classes
|
# Database connected to successfully, load queries to pass on to other classes
|
||||||
self.querydict = FpdbSQLQueries.FpdbSQLQueries(self.db.get_backend_name())
|
self.querydict = FpdbSQLQueries.FpdbSQLQueries(self.db.get_backend_name())
|
||||||
self.db.db.rollback()
|
self.db.connection.rollback()
|
||||||
#end def load_profile
|
#end def load_profile
|
||||||
|
|
||||||
def not_implemented(self, widget, data=None):
|
def not_implemented(self, widget, data=None):
|
||||||
|
@ -399,17 +419,18 @@ class fpdb:
|
||||||
#end def not_implemented
|
#end def not_implemented
|
||||||
|
|
||||||
def obtain_global_lock(self):
|
def obtain_global_lock(self):
|
||||||
print "\nTaking global lock ..."
|
ret = self.lock.acquire(False) # will return false if lock is already held
|
||||||
self.fdb_lock = fpdb_db.fpdb_db()
|
if ret:
|
||||||
self.fdb_lock.connect(self.settings['db-backend'],
|
print "\nGlobal lock taken ..."
|
||||||
self.settings['db-host'],
|
else:
|
||||||
self.settings['db-databaseName'],
|
print "\nFailed to get global lock."
|
||||||
self.settings['db-user'],
|
return ret
|
||||||
self.settings['db-password'])
|
# need to release it later:
|
||||||
return self.fdb_lock.get_global_lock()
|
# self.lock.release()
|
||||||
|
|
||||||
#end def obtain_global_lock
|
#end def obtain_global_lock
|
||||||
|
|
||||||
def quit(self, widget, data):
|
def quit(self, widget, data=None):
|
||||||
print "Quitting normally"
|
print "Quitting normally"
|
||||||
#check if current settings differ from profile, if so offer to save or abort
|
#check if current settings differ from profile, if so offer to save or abort
|
||||||
self.db.disconnect()
|
self.db.disconnect()
|
||||||
|
@ -417,9 +438,8 @@ class fpdb:
|
||||||
#end def quit_cliecked
|
#end def quit_cliecked
|
||||||
|
|
||||||
def release_global_lock(self):
|
def release_global_lock(self):
|
||||||
self.fdb_lock.db.rollback()
|
self.lock.release()
|
||||||
self.fdb_lock.disconnect()
|
print "Global lock released.\n"
|
||||||
print "Global lock released."
|
|
||||||
#end def release_global_lock
|
#end def release_global_lock
|
||||||
|
|
||||||
def tab_abbreviations(self, widget, data=None):
|
def tab_abbreviations(self, widget, data=None):
|
||||||
|
@ -444,13 +464,13 @@ class fpdb:
|
||||||
#end def tab_bulk_import
|
#end def tab_bulk_import
|
||||||
|
|
||||||
def tab_player_stats(self, widget, data=None):
|
def tab_player_stats(self, widget, data=None):
|
||||||
new_ps_thread=GuiPlayerStats.GuiPlayerStats(self.config, self.querydict, self.window)
|
new_ps_thread=GuiPlayerStats.GuiPlayerStats(self.config, self.sql, self.window)
|
||||||
self.threads.append(new_ps_thread)
|
self.threads.append(new_ps_thread)
|
||||||
ps_tab=new_ps_thread.get_vbox()
|
ps_tab=new_ps_thread.get_vbox()
|
||||||
self.add_and_display_tab(ps_tab, "Player Stats")
|
self.add_and_display_tab(ps_tab, "Player Stats")
|
||||||
|
|
||||||
def tab_positional_stats(self, widget, data=None):
|
def tab_positional_stats(self, widget, data=None):
|
||||||
new_ps_thread=GuiPositionalStats.GuiPositionalStats(self.config, self.querydict)
|
new_ps_thread = GuiPositionalStats.GuiPositionalStats(self.config, self.sql)
|
||||||
self.threads.append(new_ps_thread)
|
self.threads.append(new_ps_thread)
|
||||||
ps_tab=new_ps_thread.get_vbox()
|
ps_tab=new_ps_thread.get_vbox()
|
||||||
self.add_and_display_tab(ps_tab, "Positional Stats")
|
self.add_and_display_tab(ps_tab, "Positional Stats")
|
||||||
|
@ -468,7 +488,7 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt")
|
||||||
def tab_table_viewer(self, widget, data=None):
|
def tab_table_viewer(self, widget, data=None):
|
||||||
"""opens a table viewer tab"""
|
"""opens a table viewer tab"""
|
||||||
#print "start of tab_table_viewer"
|
#print "start of tab_table_viewer"
|
||||||
new_tv_thread=GuiTableViewer.GuiTableViewer(self.db, self.settings)
|
new_tv_thread=GuiTableViewer.GuiTableViewer(self.db.fdb, self.settings)
|
||||||
self.threads.append(new_tv_thread)
|
self.threads.append(new_tv_thread)
|
||||||
tv_tab=new_tv_thread.get_vbox()
|
tv_tab=new_tv_thread.get_vbox()
|
||||||
self.add_and_display_tab(tv_tab, "Table Viewer")
|
self.add_and_display_tab(tv_tab, "Table Viewer")
|
||||||
|
@ -477,17 +497,18 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt")
|
||||||
def tabGraphViewer(self, widget, data=None):
|
def tabGraphViewer(self, widget, data=None):
|
||||||
"""opens a graph viewer tab"""
|
"""opens a graph viewer tab"""
|
||||||
#print "start of tabGraphViewer"
|
#print "start of tabGraphViewer"
|
||||||
new_gv_thread=GuiGraphViewer.GuiGraphViewer(self.db, self.settings, self.querydict, self.config)
|
new_gv_thread = GuiGraphViewer.GuiGraphViewer(self.sql, self.config)
|
||||||
self.threads.append(new_gv_thread)
|
self.threads.append(new_gv_thread)
|
||||||
gv_tab=new_gv_thread.get_vbox()
|
gv_tab=new_gv_thread.get_vbox()
|
||||||
self.add_and_display_tab(gv_tab, "Graphs")
|
self.add_and_display_tab(gv_tab, "Graphs")
|
||||||
#end def tabGraphViewer
|
#end def tabGraphViewer
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.threads=[]
|
self.threads = []
|
||||||
self.db=None
|
# no more than 1 process can this lock at a time:
|
||||||
self.config = Configuration.Config(dbname=options.dbname)
|
self.lock = interlocks.InterProcessLock(name="fpdb_global_lock")
|
||||||
self.load_profile()
|
self.db = None
|
||||||
|
self.status_bar = None
|
||||||
|
|
||||||
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
|
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
|
||||||
self.window.connect("delete_event", self.delete_event)
|
self.window.connect("delete_event", self.delete_event)
|
||||||
|
@ -522,11 +543,8 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt")
|
||||||
|
|
||||||
self.tab_main_help(None, None)
|
self.tab_main_help(None, None)
|
||||||
|
|
||||||
self.status_bar = gtk.Label("Status: Connected to %s database named %s on host %s"%(self.db.get_backend_name(),self.db.database, self.db.host))
|
|
||||||
self.main_vbox.pack_end(self.status_bar, False, True, 0)
|
|
||||||
self.status_bar.show()
|
|
||||||
|
|
||||||
self.window.show()
|
self.window.show()
|
||||||
|
self.load_profile()
|
||||||
sys.stderr.write("fpdb starting ...")
|
sys.stderr.write("fpdb starting ...")
|
||||||
#end def __init__
|
#end def __init__
|
||||||
|
|
||||||
|
|
|
@ -18,20 +18,21 @@
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
import logging
|
||||||
from time import time, strftime
|
from time import time, strftime
|
||||||
|
|
||||||
import fpdb_simple
|
import fpdb_simple
|
||||||
import FpdbSQLQueries
|
import FpdbSQLQueries
|
||||||
|
|
||||||
class fpdb_db:
|
class fpdb_db:
|
||||||
|
MYSQL_INNODB = 2
|
||||||
|
PGSQL = 3
|
||||||
|
SQLITE = 4
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""Simple constructor, doesnt really do anything"""
|
"""Simple constructor, doesnt really do anything"""
|
||||||
self.db = None
|
self.db = None
|
||||||
self.cursor = None
|
self.cursor = None
|
||||||
self.sql = {}
|
self.sql = {}
|
||||||
self.MYSQL_INNODB = 2
|
|
||||||
self.PGSQL = 3
|
|
||||||
self.SQLITE = 4
|
|
||||||
|
|
||||||
# Data Structures for index and foreign key creation
|
# Data Structures for index and foreign key creation
|
||||||
# drop_code is an int with possible values: 0 - don't drop for bulk import
|
# drop_code is an int with possible values: 0 - don't drop for bulk import
|
||||||
|
@ -72,6 +73,8 @@ class fpdb_db:
|
||||||
, {'tab':'TourneysPlayers', 'col':'tourneyId', 'drop':0}
|
, {'tab':'TourneysPlayers', 'col':'tourneyId', 'drop':0}
|
||||||
, {'tab':'TourneyTypes', 'col':'siteId', 'drop':0}
|
, {'tab':'TourneyTypes', 'col':'siteId', 'drop':0}
|
||||||
]
|
]
|
||||||
|
, [ # indexes for sqlite (list index 4)
|
||||||
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
self.foreignKeys = [
|
self.foreignKeys = [
|
||||||
|
@ -146,12 +149,12 @@ class fpdb_db:
|
||||||
self.settings = {}
|
self.settings = {}
|
||||||
self.settings['os'] = "linuxmac" if os.name != "nt" else "windows"
|
self.settings['os'] = "linuxmac" if os.name != "nt" else "windows"
|
||||||
|
|
||||||
self.settings.update(config.get_db_parameters())
|
db = config.get_db_parameters()
|
||||||
self.connect(self.settings['db-backend'],
|
self.connect(backend=db['db-backend'],
|
||||||
self.settings['db-host'],
|
host=db['db-host'],
|
||||||
self.settings['db-databaseName'],
|
database=db['db-databaseName'],
|
||||||
self.settings['db-user'],
|
user=db['db-user'],
|
||||||
self.settings['db-password'])
|
password=db['db-password'])
|
||||||
#end def do_connect
|
#end def do_connect
|
||||||
|
|
||||||
def connect(self, backend=None, host=None, database=None,
|
def connect(self, backend=None, host=None, database=None,
|
||||||
|
@ -164,13 +167,13 @@ class fpdb_db:
|
||||||
self.user=user
|
self.user=user
|
||||||
self.password=password
|
self.password=password
|
||||||
self.database=database
|
self.database=database
|
||||||
if backend==self.MYSQL_INNODB:
|
if backend==fpdb_db.MYSQL_INNODB:
|
||||||
import MySQLdb
|
import MySQLdb
|
||||||
try:
|
try:
|
||||||
self.db = MySQLdb.connect(host = host, user = user, passwd = password, db = database, use_unicode=True)
|
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 fpdb_simple.FpdbError("MySQL connection failed")
|
||||||
elif backend==self.PGSQL:
|
elif backend==fpdb_db.PGSQL:
|
||||||
import psycopg2
|
import psycopg2
|
||||||
import psycopg2.extensions
|
import psycopg2.extensions
|
||||||
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
|
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
|
||||||
|
@ -179,19 +182,41 @@ class fpdb_db:
|
||||||
# For local domain-socket connections, only DB name is
|
# For local domain-socket connections, only DB name is
|
||||||
# needed, and everything else is in fact undefined and/or
|
# needed, and everything else is in fact undefined and/or
|
||||||
# flat out wrong
|
# flat out wrong
|
||||||
|
# sqlcoder: This database only connect failed in my windows setup??
|
||||||
|
# Modifed it to try the 4 parameter style if the first connect fails - does this work everywhere?
|
||||||
|
connected = False
|
||||||
if self.host == "localhost" or self.host == "127.0.0.1":
|
if self.host == "localhost" or self.host == "127.0.0.1":
|
||||||
self.db = psycopg2.connect(database = database)
|
try:
|
||||||
else:
|
self.db = psycopg2.connect(database = database)
|
||||||
self.db = psycopg2.connect(host = host,
|
connected = True
|
||||||
user = user,
|
except:
|
||||||
password = password,
|
pass
|
||||||
database = database)
|
#msg = "PostgreSQL direct connection to database (%s) failed, trying with user ..." % (database,)
|
||||||
|
#print msg
|
||||||
|
#raise fpdb_simple.FpdbError(msg)
|
||||||
|
if not connected:
|
||||||
|
try:
|
||||||
|
self.db = psycopg2.connect(host = host,
|
||||||
|
user = user,
|
||||||
|
password = password,
|
||||||
|
database = database)
|
||||||
|
except:
|
||||||
|
msg = "PostgreSQL connection to database (%s) user (%s) failed." % (database, user)
|
||||||
|
print msg
|
||||||
|
raise fpdb_simple.FpdbError(msg)
|
||||||
|
elif backend==fpdb_db.SQLITE:
|
||||||
|
logging.info("Connecting to SQLite:%(database)s" % {'database':database})
|
||||||
|
import sqlite3
|
||||||
|
self.db = sqlite3.connect(database,detect_types=sqlite3.PARSE_DECLTYPES)
|
||||||
|
sqlite3.register_converter("bool", lambda x: bool(int(x)))
|
||||||
|
sqlite3.register_adapter(bool, lambda x: "1" if x else "0")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise fpdb_simple.FpdbError("unrecognised database backend:"+backend)
|
raise fpdb_simple.FpdbError("unrecognised database backend:"+backend)
|
||||||
self.cursor=self.db.cursor()
|
self.cursor=self.db.cursor()
|
||||||
self.cursor.execute('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED')
|
|
||||||
# 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())
|
||||||
|
self.cursor.execute(self.sql.query['set tx level'])
|
||||||
self.wrongDbVersion=False
|
self.wrongDbVersion=False
|
||||||
try:
|
try:
|
||||||
self.cursor.execute("SELECT * FROM Settings")
|
self.cursor.execute("SELECT * FROM Settings")
|
||||||
|
@ -222,13 +247,14 @@ class fpdb_db:
|
||||||
|
|
||||||
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.
|
||||||
|
logging.debug(self.sql.query['createSettingsTable'])
|
||||||
self.cursor.execute(self.sql.query['createSettingsTable'])
|
self.cursor.execute(self.sql.query['createSettingsTable'])
|
||||||
|
logging.debug(self.sql.query['createSitesTable'])
|
||||||
self.cursor.execute(self.sql.query['createSitesTable'])
|
self.cursor.execute(self.sql.query['createSitesTable'])
|
||||||
self.cursor.execute(self.sql.query['createGametypesTable'])
|
self.cursor.execute(self.sql.query['createGametypesTable'])
|
||||||
self.cursor.execute(self.sql.query['createPlayersTable'])
|
self.cursor.execute(self.sql.query['createPlayersTable'])
|
||||||
self.cursor.execute(self.sql.query['createAutoratesTable'])
|
self.cursor.execute(self.sql.query['createAutoratesTable'])
|
||||||
self.cursor.execute(self.sql.query['createHandsTable'])
|
self.cursor.execute(self.sql.query['createHandsTable'])
|
||||||
self.cursor.execute(self.sql.query['createBoardCardsTable'])
|
|
||||||
self.cursor.execute(self.sql.query['createTourneyTypesTable'])
|
self.cursor.execute(self.sql.query['createTourneyTypesTable'])
|
||||||
self.cursor.execute(self.sql.query['createTourneysTable'])
|
self.cursor.execute(self.sql.query['createTourneysTable'])
|
||||||
self.cursor.execute(self.sql.query['createTourneysPlayersTable'])
|
self.cursor.execute(self.sql.query['createTourneysPlayersTable'])
|
||||||
|
@ -260,10 +286,12 @@ class fpdb_db:
|
||||||
for table in tables:
|
for table in tables:
|
||||||
self.cursor.execute(self.sql.query['drop_table'] + table[0] + ' cascade')
|
self.cursor.execute(self.sql.query['drop_table'] + table[0] + ' cascade')
|
||||||
elif(self.get_backend_name() == 'SQLite'):
|
elif(self.get_backend_name() == 'SQLite'):
|
||||||
#todo: sqlite version here
|
self.cursor.execute(self.sql.query['list_tables'])
|
||||||
print "Empty function here"
|
for table in self.cursor.fetchall():
|
||||||
|
logging.debug(self.sql.query['drop_table'] + table[0])
|
||||||
|
self.cursor.execute(self.sql.query['drop_table'] + table[0])
|
||||||
|
|
||||||
self.db.commit()
|
self.db.commit()
|
||||||
#end def drop_tables
|
#end def drop_tables
|
||||||
|
|
||||||
def drop_referential_integrity(self):
|
def drop_referential_integrity(self):
|
||||||
|
@ -292,6 +320,8 @@ class fpdb_db:
|
||||||
return "MySQL InnoDB"
|
return "MySQL InnoDB"
|
||||||
elif self.backend==3:
|
elif self.backend==3:
|
||||||
return "PostgreSQL"
|
return "PostgreSQL"
|
||||||
|
elif self.backend==4:
|
||||||
|
return "SQLite"
|
||||||
else:
|
else:
|
||||||
raise fpdb_simple.FpdbError("invalid backend")
|
raise fpdb_simple.FpdbError("invalid backend")
|
||||||
#end def get_backend_name
|
#end def get_backend_name
|
||||||
|
@ -301,11 +331,15 @@ class fpdb_db:
|
||||||
#end def get_db_info
|
#end def get_db_info
|
||||||
|
|
||||||
def fillDefaultData(self):
|
def fillDefaultData(self):
|
||||||
self.cursor.execute("INSERT INTO Settings VALUES (118);")
|
self.cursor.execute("INSERT INTO Settings (version) VALUES (118);")
|
||||||
self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'Full Tilt Poker', 'USD');")
|
self.cursor.execute("INSERT INTO Sites (name,currency) VALUES ('Full Tilt Poker', 'USD')")
|
||||||
self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'PokerStars', 'USD');")
|
self.cursor.execute("INSERT INTO Sites (name,currency) VALUES ('PokerStars', 'USD')")
|
||||||
self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'Everleaf', 'USD');")
|
self.cursor.execute("INSERT INTO Sites (name,currency) VALUES ('Everleaf', 'USD')")
|
||||||
|
self.cursor.execute("INSERT INTO Sites (name,currency) VALUES ('Win2day', 'USD')")
|
||||||
self.cursor.execute("INSERT INTO TourneyTypes VALUES (DEFAULT, 1, 0, 0, 0, False);")
|
self.cursor.execute("INSERT INTO TourneyTypes VALUES (DEFAULT, 1, 0, 0, 0, False);")
|
||||||
|
#self.cursor.execute("""INSERT INTO TourneyTypes
|
||||||
|
# (siteId,buyin,fee,knockout,rebuyOrAddon) VALUES
|
||||||
|
# (1,0,0,0,?)""",(False,) )
|
||||||
#end def fillDefaultData
|
#end def fillDefaultData
|
||||||
|
|
||||||
def recreate_tables(self):
|
def recreate_tables(self):
|
||||||
|
@ -538,72 +572,100 @@ class fpdb_db:
|
||||||
self.db.set_isolation_level(1) # go back to normal isolation level
|
self.db.set_isolation_level(1) # go back to normal isolation level
|
||||||
#end def dropAllIndexes
|
#end def dropAllIndexes
|
||||||
|
|
||||||
def analyzeDB(self):
|
def getLastInsertId(self):
|
||||||
"""Do whatever the DB can offer to update index/table statistics"""
|
|
||||||
stime = time()
|
|
||||||
if self.backend == self.PGSQL:
|
|
||||||
self.db.set_isolation_level(0) # allow vacuum to work
|
|
||||||
try:
|
|
||||||
self.cursor.execute("vacuum analyze")
|
|
||||||
except:
|
|
||||||
print "Error during vacuum"
|
|
||||||
self.db.set_isolation_level(1) # go back to normal isolation level
|
|
||||||
self.db.commit()
|
|
||||||
atime = time() - stime
|
|
||||||
print "analyze took", atime, "seconds"
|
|
||||||
#end def analyzeDB
|
|
||||||
|
|
||||||
# Currently uses an exclusive lock on the Hands table as a global lock
|
|
||||||
# Return values are Unix style, 0 for success, positive integers for errors
|
|
||||||
# 1 = generic error
|
|
||||||
# 2 = hands table does not exist (error message is suppressed)
|
|
||||||
def get_global_lock(self):
|
|
||||||
if self.backend == self.MYSQL_INNODB:
|
if self.backend == self.MYSQL_INNODB:
|
||||||
try:
|
ret = self.db.insert_id()
|
||||||
self.cursor.execute( "lock tables Hands write" )
|
if ret < 1 or ret > 999999999:
|
||||||
except:
|
print "getLastInsertId(): problem fetching insert_id? ret=", ret
|
||||||
# Table 'fpdb.hands' doesn't exist
|
ret = -1
|
||||||
if str(sys.exc_value).find(".hands' doesn't exist") >= 0:
|
|
||||||
return(2)
|
|
||||||
print "Error! failed to obtain global lock. Close all programs accessing " \
|
|
||||||
+ "database (including fpdb) and try again (%s)." \
|
|
||||||
% ( str(sys.exc_value).rstrip('\n'), )
|
|
||||||
return(1)
|
|
||||||
elif self.backend == self.PGSQL:
|
elif self.backend == self.PGSQL:
|
||||||
try:
|
# some options:
|
||||||
self.cursor.execute( "lock table Hands in exclusive mode nowait" )
|
# currval(hands_id_seq) - use name of implicit seq here
|
||||||
#print "... after lock table, status =", self.cursor.statusmessage
|
# lastval() - still needs sequences set up?
|
||||||
except:
|
# insert ... returning is useful syntax (but postgres specific?)
|
||||||
# relation "hands" does not exist
|
# see rules (fancy trigger type things)
|
||||||
if str(sys.exc_value).find('relation "hands" does not exist') >= 0:
|
self.cursor.execute ("SELECT lastval()")
|
||||||
return(2)
|
row = self.cursor.fetchone()
|
||||||
print "Error! failed to obtain global lock. Close all programs accessing " \
|
if not row:
|
||||||
+ "database (including fpdb) and try again (%s)." \
|
print "getLastInsertId(%s): problem fetching lastval? row=" % seq, row
|
||||||
% ( str(sys.exc_value).rstrip('\n'), )
|
ret = -1
|
||||||
return(1)
|
else:
|
||||||
return(0)
|
ret = row[0]
|
||||||
|
elif self.backend == fpdb_db.SQLITE:
|
||||||
|
# don't know how to do this in sqlite
|
||||||
|
print "getLastInsertId(): not coded for sqlite yet"
|
||||||
|
ret = -1
|
||||||
|
else:
|
||||||
|
print "getLastInsertId(): unknown backend ", self.backend
|
||||||
|
ret = -1
|
||||||
|
return ret
|
||||||
|
|
||||||
def storeHand(self, p):
|
def storeHand(self, p):
|
||||||
#stores into table hands:
|
#stores into table hands:
|
||||||
self.cursor.execute ("""INSERT INTO Hands
|
self.cursor.execute ("""INSERT INTO Hands (
|
||||||
(siteHandNo, gametypeId, handStart, seats, tableName, importTime, maxSeats
|
tablename,
|
||||||
,playersVpi, playersAtStreet1, playersAtStreet2
|
sitehandno,
|
||||||
,playersAtStreet3, playersAtStreet4, playersAtShowdown
|
gametypeid,
|
||||||
,street0Raises, street1Raises, street2Raises
|
handstart,
|
||||||
,street3Raises, street4Raises, street1Pot
|
importtime,
|
||||||
,street2Pot, street3Pot, street4Pot
|
seats,
|
||||||
,showdownPot
|
maxseats,
|
||||||
|
boardcard1,
|
||||||
|
boardcard2,
|
||||||
|
boardcard3,
|
||||||
|
boardcard4,
|
||||||
|
boardcard5,
|
||||||
|
-- texture,
|
||||||
|
playersVpi,
|
||||||
|
playersAtStreet1,
|
||||||
|
playersAtStreet2,
|
||||||
|
playersAtStreet3,
|
||||||
|
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,
|
||||||
,(p['siteHandNo'], gametype_id, p['handStart'], len(names), p['tableName'], datetime.datetime.today(), p['maxSeats']
|
%s, %s, %s, %s, %s, %s, %s)""",
|
||||||
,hudCache['playersVpi'], hudCache['playersAtStreet1'], hudCache['playersAtStreet2']
|
(
|
||||||
,hudCache['playersAtStreet3'], hudCache['playersAtStreet4'], hudCache['playersAtShowdown']
|
p['tablename'],
|
||||||
,hudCache['street0Raises'], hudCache['street1Raises'], hudCache['street2Raises']
|
p['sitehandno'],
|
||||||
,hudCache['street3Raises'], hudCache['street4Raises'], hudCache['street1Pot']
|
p['gametypeid'],
|
||||||
,hudCache['street2Pot'], hudCache['street3Pot'], hudCache['street4Pot']
|
p['handStart'],
|
||||||
,hudCache['showdownPot']
|
datetime.datetime.today(),
|
||||||
)
|
len(p['names']),
|
||||||
)
|
p['maxSeats'],
|
||||||
|
p['boardcard1'],
|
||||||
|
p['boardcard2'],
|
||||||
|
p['boardcard3'],
|
||||||
|
p['boardcard4'],
|
||||||
|
p['boardcard5'],
|
||||||
|
hudCache['playersVpi'],
|
||||||
|
hudCache['playersAtStreet1'],
|
||||||
|
hudCache['playersAtStreet2'],
|
||||||
|
hudCache['playersAtStreet3'],
|
||||||
|
hudCache['playersAtStreet4'],
|
||||||
|
hudCache['playersAtShowdown'],
|
||||||
|
hudCache['street0Raises'],
|
||||||
|
hudCache['street1Raises'],
|
||||||
|
hudCache['street2Raises'],
|
||||||
|
hudCache['street3Raises'],
|
||||||
|
hudCache['street4Raises'],
|
||||||
|
hudCache['street1Pot'],
|
||||||
|
hudCache['street2Pot'],
|
||||||
|
hudCache['street3Pot'],
|
||||||
|
hudCache['street4Pot'],
|
||||||
|
hudCache['showdownPot']
|
||||||
|
)
|
||||||
|
)
|
||||||
#return getLastInsertId(backend, conn, cursor)
|
#return getLastInsertId(backend, conn, cursor)
|
||||||
#end class fpdb_db
|
#end class fpdb_db
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
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
|
from time import time, strftime
|
||||||
|
import logging
|
||||||
import traceback
|
import traceback
|
||||||
import math
|
import math
|
||||||
import datetime
|
import datetime
|
||||||
|
@ -31,6 +32,7 @@ import re
|
||||||
|
|
||||||
import fpdb_simple
|
import fpdb_simple
|
||||||
import fpdb_db
|
import fpdb_db
|
||||||
|
import Database
|
||||||
import fpdb_parse_logic
|
import fpdb_parse_logic
|
||||||
import Configuration
|
import Configuration
|
||||||
|
|
||||||
|
@ -57,7 +59,8 @@ class Importer:
|
||||||
self.settings = settings
|
self.settings = settings
|
||||||
self.caller = caller
|
self.caller = caller
|
||||||
self.config = config
|
self.config = config
|
||||||
self.fdb = None
|
self.database = None # database will be the main db interface eventually
|
||||||
|
self.fdb = None # fdb may disappear or just hold the simple db connection
|
||||||
self.cursor = None
|
self.cursor = None
|
||||||
self.filelist = {}
|
self.filelist = {}
|
||||||
self.dirlist = {}
|
self.dirlist = {}
|
||||||
|
@ -75,9 +78,13 @@ class Importer:
|
||||||
self.settings.setdefault("minPrint", 30)
|
self.settings.setdefault("minPrint", 30)
|
||||||
self.settings.setdefault("handCount", 0)
|
self.settings.setdefault("handCount", 0)
|
||||||
|
|
||||||
|
self.database = Database.Database(self.config) # includes .connection and .sql variables
|
||||||
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(self.config)
|
self.fdb.do_connect(self.config)
|
||||||
self.fdb.db.rollback()
|
self.fdb.db.rollback() # make sure all locks are released
|
||||||
|
|
||||||
|
self.NEWIMPORT = False
|
||||||
|
self.allow_hudcache_rebuild = True;
|
||||||
|
|
||||||
#Set functions
|
#Set functions
|
||||||
def setCallHud(self, value):
|
def setCallHud(self, value):
|
||||||
|
@ -162,12 +169,19 @@ class Importer:
|
||||||
|
|
||||||
def runImport(self):
|
def runImport(self):
|
||||||
""""Run full import on self.filelist."""
|
""""Run full import on self.filelist."""
|
||||||
|
|
||||||
start = datetime.datetime.now()
|
start = datetime.datetime.now()
|
||||||
print "started at", start, "--", len(self.filelist), "files to import.", self.settings['dropIndexes']
|
print "Started at", start, "--", len(self.filelist), "files to import.", self.settings['dropIndexes']
|
||||||
if self.settings['dropIndexes'] == 'auto':
|
if self.settings['dropIndexes'] == 'auto':
|
||||||
self.settings['dropIndexes'] = self.calculate_auto()
|
self.settings['dropIndexes'] = self.calculate_auto2(12.0, 500.0)
|
||||||
|
if self.allow_hudcache_rebuild:
|
||||||
|
self.settings['dropHudCache'] = self.calculate_auto2(25.0, 500.0) # returns "drop"/"don't drop"
|
||||||
|
|
||||||
if self.settings['dropIndexes'] == 'drop':
|
if self.settings['dropIndexes'] == 'drop':
|
||||||
self.fdb.prepareBulkImport()
|
self.fdb.prepareBulkImport()
|
||||||
|
else:
|
||||||
|
print "No need drop indexes."
|
||||||
|
#print "dropInd =", self.settings['dropIndexes'], " dropHudCache =", self.settings['dropHudCache']
|
||||||
totstored = 0
|
totstored = 0
|
||||||
totdups = 0
|
totdups = 0
|
||||||
totpartial = 0
|
totpartial = 0
|
||||||
|
@ -183,7 +197,13 @@ class Importer:
|
||||||
tottime += ttime
|
tottime += ttime
|
||||||
if self.settings['dropIndexes'] == 'drop':
|
if self.settings['dropIndexes'] == 'drop':
|
||||||
self.fdb.afterBulkImport()
|
self.fdb.afterBulkImport()
|
||||||
self.fdb.analyzeDB()
|
else:
|
||||||
|
print "No need rebuild indexes."
|
||||||
|
if self.settings['dropHudCache'] == 'drop':
|
||||||
|
self.database.rebuild_hudcache()
|
||||||
|
else:
|
||||||
|
print "No need to rebuild hudcache."
|
||||||
|
self.database.analyzeDB()
|
||||||
return (totstored, totdups, totpartial, toterrors, tottime)
|
return (totstored, totdups, totpartial, toterrors, tottime)
|
||||||
# else: import threaded
|
# else: import threaded
|
||||||
|
|
||||||
|
@ -202,6 +222,41 @@ class Importer:
|
||||||
if self.settings['handsInDB'] > 50000: return "don't drop"
|
if self.settings['handsInDB'] > 50000: return "don't drop"
|
||||||
return "drop"
|
return "drop"
|
||||||
|
|
||||||
|
def calculate_auto2(self, scale, increment):
|
||||||
|
"""A second heuristic to determine a reasonable value of drop/don't drop
|
||||||
|
This one adds up size of files to import to guess number of hands in them
|
||||||
|
Example values of scale and increment params might be 10 and 500 meaning
|
||||||
|
roughly: drop if importing more than 10% (100/scale) of hands in db or if
|
||||||
|
less than 500 hands in db"""
|
||||||
|
size_per_hand = 1300.0 # wag based on a PS 6-up FLHE file. Actual value not hugely important
|
||||||
|
# as values of scale and increment compensate for it anyway.
|
||||||
|
# decimal used to force float arithmetic
|
||||||
|
|
||||||
|
# get number of hands in db
|
||||||
|
if 'handsInDB' not in self.settings:
|
||||||
|
try:
|
||||||
|
tmpcursor = self.fdb.db.cursor()
|
||||||
|
tmpcursor.execute("Select count(1) from Hands;")
|
||||||
|
self.settings['handsInDB'] = tmpcursor.fetchone()[0]
|
||||||
|
except:
|
||||||
|
pass # if this fails we're probably doomed anyway
|
||||||
|
|
||||||
|
# add up size of import files
|
||||||
|
total_size = 0.0
|
||||||
|
for file in self.filelist:
|
||||||
|
if os.path.exists(file):
|
||||||
|
stat_info = os.stat(file)
|
||||||
|
total_size += stat_info.st_size
|
||||||
|
|
||||||
|
# if hands_in_db is zero or very low, we want to drop indexes, otherwise compare
|
||||||
|
# import size with db size somehow:
|
||||||
|
ret = "don't drop"
|
||||||
|
if self.settings['handsInDB'] < scale * (total_size/size_per_hand) + increment:
|
||||||
|
ret = "drop"
|
||||||
|
#print "auto2: handsindb =", self.settings['handsInDB'], "total_size =", total_size, "size_per_hand =", \
|
||||||
|
# size_per_hand, "inc =", increment, "return:", ret
|
||||||
|
return ret
|
||||||
|
|
||||||
#Run import on updated files, then store latest update time.
|
#Run import on updated files, then store latest update time.
|
||||||
def runUpdated(self):
|
def runUpdated(self):
|
||||||
#Check for new files in monitored directories
|
#Check for new files in monitored directories
|
||||||
|
@ -253,36 +308,60 @@ class Importer:
|
||||||
if os.path.isdir(file):
|
if os.path.isdir(file):
|
||||||
self.addToDirList[file] = [site] + [filter]
|
self.addToDirList[file] = [site] + [filter]
|
||||||
return
|
return
|
||||||
if filter == "passthrough" or filter == "":
|
|
||||||
(stored, duplicates, partial, errors, ttime) = self.import_fpdb_file(file, site)
|
|
||||||
else:
|
|
||||||
conv = None
|
|
||||||
# Load filter, process file, pass returned filename to import_fpdb_file
|
|
||||||
|
|
||||||
print "\nConverting %s" % file
|
conv = None
|
||||||
hhbase = self.config.get_import_parameters().get("hhArchiveBase")
|
# Load filter, process file, pass returned filename to import_fpdb_file
|
||||||
hhbase = os.path.expanduser(hhbase)
|
|
||||||
hhdir = os.path.join(hhbase,site)
|
|
||||||
try:
|
|
||||||
out_path = os.path.join(hhdir, file.split(os.path.sep)[-2]+"-"+os.path.basename(file))
|
|
||||||
except:
|
|
||||||
out_path = os.path.join(hhdir, "x"+strftime("%d-%m-%y")+os.path.basename(file))
|
|
||||||
|
|
||||||
filter_name = filter.replace("ToFpdb", "")
|
print "\nConverting %s" % file
|
||||||
|
hhbase = self.config.get_import_parameters().get("hhArchiveBase")
|
||||||
|
hhbase = os.path.expanduser(hhbase)
|
||||||
|
hhdir = os.path.join(hhbase,site)
|
||||||
|
try:
|
||||||
|
out_path = os.path.join(hhdir, file.split(os.path.sep)[-2]+"-"+os.path.basename(file))
|
||||||
|
except:
|
||||||
|
out_path = os.path.join(hhdir, "x"+strftime("%d-%m-%y")+os.path.basename(file))
|
||||||
|
|
||||||
mod = __import__(filter)
|
filter_name = filter.replace("ToFpdb", "")
|
||||||
obj = getattr(mod, filter_name, None)
|
|
||||||
if callable(obj):
|
# Example code for using threads & queues: (maybe for obj and import_fpdb_file??)
|
||||||
conv = obj(in_path = file, out_path = out_path)
|
#def worker():
|
||||||
if(conv.getStatus()):
|
# while True:
|
||||||
(stored, duplicates, partial, errors, ttime) = self.import_fpdb_file(out_path, site)
|
# item = q.get()
|
||||||
else:
|
# do_work(item)
|
||||||
# conversion didn't work
|
# q.task_done()
|
||||||
# TODO: appropriate response?
|
#
|
||||||
return (0, 0, 0, 1, 0)
|
#q = Queue()
|
||||||
|
#for i in range(num_worker_threads):
|
||||||
|
# t = Thread(target=worker)
|
||||||
|
# t.setDaemon(True)
|
||||||
|
# t.start()
|
||||||
|
#
|
||||||
|
#for item in source():
|
||||||
|
# q.put(item)
|
||||||
|
#
|
||||||
|
#q.join() # block until all tasks are done
|
||||||
|
|
||||||
|
mod = __import__(filter)
|
||||||
|
obj = getattr(mod, filter_name, None)
|
||||||
|
if callable(obj):
|
||||||
|
conv = obj(in_path = file, out_path = out_path, index = 0) # Index into file 0 until changeover
|
||||||
|
if(conv.getStatus() and self.NEWIMPORT == False):
|
||||||
|
(stored, duplicates, partial, errors, ttime) = self.import_fpdb_file(out_path, site)
|
||||||
|
elif (conv.getStatus() and self.NEWIMPORT == True):
|
||||||
|
#This code doesn't do anything yet
|
||||||
|
handlist = hhc.getProcessedHands()
|
||||||
|
self.pos_in_file[file] = hhc.getLastCharacterRead()
|
||||||
|
|
||||||
|
for hand in handlist:
|
||||||
|
hand.prepInsert()
|
||||||
|
hand.insert()
|
||||||
else:
|
else:
|
||||||
print "Unknown filter filter_name:'%s' in filter:'%s'" %(filter_name, filter)
|
# conversion didn't work
|
||||||
return
|
# TODO: appropriate response?
|
||||||
|
return (0, 0, 0, 1, 0)
|
||||||
|
else:
|
||||||
|
print "Unknown filter filter_name:'%s' in filter:'%s'" %(filter_name, filter)
|
||||||
|
return
|
||||||
|
|
||||||
#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)
|
||||||
|
@ -359,8 +438,9 @@ class Importer:
|
||||||
self.hand=hand
|
self.hand=hand
|
||||||
|
|
||||||
try:
|
try:
|
||||||
handsId = fpdb_parse_logic.mainParser(self.settings['db-backend'], self.fdb.db
|
handsId = fpdb_parse_logic.mainParser( self.settings, self.fdb
|
||||||
,self.fdb.cursor, self.siteIds[site], category, hand, self.config)
|
, self.siteIds[site], category, hand
|
||||||
|
, self.config, self.database )
|
||||||
self.fdb.db.commit()
|
self.fdb.db.commit()
|
||||||
|
|
||||||
stored += 1
|
stored += 1
|
||||||
|
|
|
@ -18,10 +18,16 @@
|
||||||
#methods that are specific to holdem but not trivial
|
#methods that are specific to holdem but not trivial
|
||||||
|
|
||||||
import fpdb_simple
|
import fpdb_simple
|
||||||
import fpdb_save_to_db
|
import Database
|
||||||
|
|
||||||
#parses a holdem hand
|
#parses a holdem hand
|
||||||
def mainParser(backend, db, cursor, siteID, category, hand, config):
|
def mainParser(settings, fdb, siteID, category, hand, config, db = None):
|
||||||
|
backend = settings['db-backend']
|
||||||
|
if db == None:
|
||||||
|
#This is redundant - hopefully fdb will be a Database object in an iteration soon
|
||||||
|
db = Database.Database(c = config, sql = None)
|
||||||
|
else:
|
||||||
|
db = db
|
||||||
category = fpdb_simple.recogniseCategory(hand[0])
|
category = fpdb_simple.recogniseCategory(hand[0])
|
||||||
|
|
||||||
base = "hold" if category == "holdem" or category == "omahahi" or category == "omahahilo" else "stud"
|
base = "hold" if category == "holdem" or category == "omahahi" or category == "omahahilo" else "stud"
|
||||||
|
@ -34,6 +40,7 @@ def mainParser(backend, db, cursor, siteID, category, hand, config):
|
||||||
|
|
||||||
#part 1: read hand no and check for duplicate
|
#part 1: read hand no and check for duplicate
|
||||||
siteHandNo = fpdb_simple.parseSiteHandNo(hand[0])
|
siteHandNo = fpdb_simple.parseSiteHandNo(hand[0])
|
||||||
|
#print "siteHandNo =", siteHandNo
|
||||||
handStartTime = fpdb_simple.parseHandStartTime(hand[0])
|
handStartTime = fpdb_simple.parseHandStartTime(hand[0])
|
||||||
|
|
||||||
isTourney = fpdb_simple.isTourney(hand[0])
|
isTourney = fpdb_simple.isTourney(hand[0])
|
||||||
|
@ -45,7 +52,7 @@ def mainParser(backend, db, cursor, siteID, category, hand, config):
|
||||||
break
|
break
|
||||||
#print "small blind line:",smallBlindLine
|
#print "small blind line:",smallBlindLine
|
||||||
|
|
||||||
gametypeID = fpdb_simple.recogniseGametypeID(backend, db, cursor, hand[0], hand[smallBlindLine], siteID, category, isTourney)
|
gametypeID = fpdb_simple.recogniseGametypeID(backend, fdb.db, fdb.cursor, hand[0], hand[smallBlindLine], siteID, category, isTourney)
|
||||||
if isTourney:
|
if isTourney:
|
||||||
siteTourneyNo = fpdb_simple.parseTourneyNo(hand[0])
|
siteTourneyNo = fpdb_simple.parseTourneyNo(hand[0])
|
||||||
buyin = fpdb_simple.parseBuyin(hand[0])
|
buyin = fpdb_simple.parseBuyin(hand[0])
|
||||||
|
@ -56,9 +63,9 @@ def mainParser(backend, db, cursor, siteID, category, hand, config):
|
||||||
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(cursor, siteID, buyin, fee, knockout, rebuyOrAddon)
|
tourneyTypeId = fpdb_simple.recogniseTourneyTypeId(fdb.cursor, siteID, buyin, fee, knockout, rebuyOrAddon)
|
||||||
|
|
||||||
fpdb_simple.isAlreadyInDB(cursor, gametypeID, siteHandNo)
|
fpdb_simple.isAlreadyInDB(fdb.cursor, gametypeID, siteHandNo)
|
||||||
|
|
||||||
hand = fpdb_simple.filterCrap(hand, isTourney)
|
hand = fpdb_simple.filterCrap(hand, isTourney)
|
||||||
|
|
||||||
|
@ -72,7 +79,7 @@ def mainParser(backend, db, cursor, siteID, category, hand, config):
|
||||||
seatLines.append(line)
|
seatLines.append(line)
|
||||||
|
|
||||||
names = fpdb_simple.parseNames(seatLines)
|
names = fpdb_simple.parseNames(seatLines)
|
||||||
playerIDs = fpdb_simple.recognisePlayerIDs(cursor, names, siteID)
|
playerIDs = fpdb_simple.recognisePlayerIDs(fdb.cursor, names, siteID)
|
||||||
tmp = fpdb_simple.parseCashesAndSeatNos(seatLines)
|
tmp = fpdb_simple.parseCashesAndSeatNos(seatLines)
|
||||||
startCashes = tmp['startCashes']
|
startCashes = tmp['startCashes']
|
||||||
seatNos = tmp['seatNos']
|
seatNos = tmp['seatNos']
|
||||||
|
@ -111,7 +118,7 @@ def mainParser(backend, db, cursor, siteID, category, hand, config):
|
||||||
tableName = tableResult['tableName']
|
tableName = tableResult['tableName']
|
||||||
#print "before part5, antes:", antes
|
#print "before part5, antes:", antes
|
||||||
|
|
||||||
#part 5: final preparations, then call fpdb_save_to_db.* with
|
#part 5: final preparations, then call Database.* with
|
||||||
# the arrays as they are - that file will fill them.
|
# the arrays as they are - that file will fill them.
|
||||||
fpdb_simple.convertCardValues(cardValues)
|
fpdb_simple.convertCardValues(cardValues)
|
||||||
if base == "hold":
|
if base == "hold":
|
||||||
|
@ -119,13 +126,14 @@ def mainParser(backend, db, cursor, siteID, category, hand, config):
|
||||||
fpdb_simple.convertBlindBet(actionTypes, actionAmounts)
|
fpdb_simple.convertBlindBet(actionTypes, actionAmounts)
|
||||||
fpdb_simple.checkPositions(positions)
|
fpdb_simple.checkPositions(positions)
|
||||||
|
|
||||||
cursor.execute("SELECT limitType FROM Gametypes WHERE id=%s",(gametypeID, ))
|
fdb.cursor.execute("SELECT limitType FROM Gametypes WHERE id=%s",(gametypeID, ))
|
||||||
limit_type = cursor.fetchone()[0]
|
limit_type = fdb.cursor.fetchone()[0]
|
||||||
fpdb_simple.convert3B4B(category, limit_type, actionTypes, actionAmounts)
|
fpdb_simple.convert3B4B(category, limit_type, actionTypes, actionAmounts)
|
||||||
|
|
||||||
totalWinnings = sum(winnings)
|
totalWinnings = sum(winnings)
|
||||||
|
|
||||||
# if hold'em, use positions and not antes, if stud do not use positions, use antes
|
# if hold'em, use positions and not antes, if stud do not use positions, use antes
|
||||||
|
# this is used for handsplayers inserts, so still needed even if hudcache update is being skipped
|
||||||
if base == "hold":
|
if base == "hold":
|
||||||
hudImportData = fpdb_simple.generateHudCacheData(playerIDs, base, category, actionTypes
|
hudImportData = fpdb_simple.generateHudCacheData(playerIDs, base, category, actionTypes
|
||||||
, allIns, actionTypeByNo, winnings, totalWinnings, positions
|
, allIns, actionTypeByNo, winnings, totalWinnings, positions
|
||||||
|
@ -140,8 +148,8 @@ def mainParser(backend, db, cursor, siteID, category, hand, config):
|
||||||
payin_amounts = fpdb_simple.calcPayin(len(names), buyin, fee)
|
payin_amounts = fpdb_simple.calcPayin(len(names), buyin, fee)
|
||||||
|
|
||||||
if base == "hold":
|
if base == "hold":
|
||||||
result = fpdb_save_to_db.tourney_holdem_omaha(
|
result = db.tourney_holdem_omaha(
|
||||||
config, backend, db, cursor, base, category, siteTourneyNo, buyin
|
config, settings, fdb.db, fdb.cursor, base, category, siteTourneyNo, buyin
|
||||||
, fee, knockout, entries, prizepool, tourneyStartTime
|
, fee, knockout, entries, prizepool, tourneyStartTime
|
||||||
, payin_amounts, ranks, tourneyTypeId, siteID, siteHandNo
|
, payin_amounts, ranks, tourneyTypeId, siteID, siteHandNo
|
||||||
, gametypeID, handStartTime, names, playerIDs, startCashes
|
, gametypeID, handStartTime, names, playerIDs, startCashes
|
||||||
|
@ -149,8 +157,8 @@ def mainParser(backend, db, cursor, siteID, category, hand, config):
|
||||||
, winnings, rakes, actionTypes, allIns, actionAmounts
|
, winnings, rakes, actionTypes, allIns, actionAmounts
|
||||||
, actionNos, hudImportData, maxSeats, tableName, seatNos)
|
, actionNos, hudImportData, maxSeats, tableName, seatNos)
|
||||||
elif base == "stud":
|
elif base == "stud":
|
||||||
result = fpdb_save_to_db.tourney_stud(
|
result = db.tourney_stud(
|
||||||
config, backend, db, cursor, base, category, siteTourneyNo
|
config, settings, fdb.db, fdb.cursor, base, category, siteTourneyNo
|
||||||
, buyin, fee, knockout, entries, prizepool, tourneyStartTime
|
, buyin, fee, knockout, entries, prizepool, tourneyStartTime
|
||||||
, payin_amounts, ranks, tourneyTypeId, siteID, siteHandNo
|
, payin_amounts, ranks, tourneyTypeId, siteID, siteHandNo
|
||||||
, gametypeID, handStartTime, names, playerIDs, startCashes
|
, gametypeID, handStartTime, names, playerIDs, startCashes
|
||||||
|
@ -161,23 +169,23 @@ def mainParser(backend, db, cursor, siteID, category, hand, config):
|
||||||
raise fpdb_simple.FpdbError("unrecognised category")
|
raise fpdb_simple.FpdbError("unrecognised category")
|
||||||
else:
|
else:
|
||||||
if base == "hold":
|
if base == "hold":
|
||||||
result = fpdb_save_to_db.ring_holdem_omaha(
|
result = db.ring_holdem_omaha(
|
||||||
config, backend, db, cursor, base, category, siteHandNo
|
config, settings, fdb.db, fdb.cursor, base, category, siteHandNo
|
||||||
, gametypeID, handStartTime, names, playerIDs
|
, gametypeID, handStartTime, names, playerIDs
|
||||||
, startCashes, positions, cardValues, cardSuits
|
, startCashes, positions, cardValues, cardSuits
|
||||||
, boardValues, boardSuits, winnings, rakes
|
, boardValues, boardSuits, winnings, rakes
|
||||||
, actionTypes, allIns, actionAmounts, actionNos
|
, actionTypes, allIns, actionAmounts, actionNos
|
||||||
, hudImportData, maxSeats, tableName, seatNos)
|
, hudImportData, maxSeats, tableName, seatNos)
|
||||||
elif base == "stud":
|
elif base == "stud":
|
||||||
result = fpdb_save_to_db.ring_stud(
|
result = db.ring_stud(
|
||||||
config, backend, db, cursor, base, category, siteHandNo, gametypeID
|
config, settings, fdb.db, fdb.cursor, base, category, siteHandNo, gametypeID
|
||||||
, handStartTime, names, playerIDs, startCashes, antes
|
, handStartTime, names, playerIDs, startCashes, antes
|
||||||
, cardValues, cardSuits, winnings, rakes, actionTypes, allIns
|
, cardValues, cardSuits, winnings, rakes, actionTypes, allIns
|
||||||
, actionAmounts, actionNos, hudImportData, maxSeats, tableName
|
, actionAmounts, actionNos, hudImportData, maxSeats, tableName
|
||||||
, seatNos)
|
, seatNos)
|
||||||
else:
|
else:
|
||||||
raise fpdb_simple.FpdbError ("unrecognised category")
|
raise fpdb_simple.FpdbError ("unrecognised category")
|
||||||
db.commit()
|
fdb.db.commit()
|
||||||
return result
|
return result
|
||||||
#end def mainParser
|
#end def mainParser
|
||||||
|
|
||||||
|
|
|
@ -1,173 +0,0 @@
|
||||||
#!/usr/bin/python
|
|
||||||
|
|
||||||
#Copyright 2008 Steffen Jobbagy-Felso
|
|
||||||
#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.
|
|
||||||
|
|
||||||
#This file contains methods to store hands into the db. decides to move this
|
|
||||||
#into a seperate file since its ugly, fairly long and just generally in the way.
|
|
||||||
|
|
||||||
from time import time
|
|
||||||
|
|
||||||
import fpdb_simple
|
|
||||||
|
|
||||||
MYSQL_INNODB = 2
|
|
||||||
PGSQL = 3
|
|
||||||
SQLITE = 4
|
|
||||||
|
|
||||||
fastStoreHudCache = False # set this to True to test the new storeHudCache routine
|
|
||||||
|
|
||||||
saveActions = True # set this to False to avoid storing action data
|
|
||||||
# Pros: speeds up imports
|
|
||||||
# Cons: no action data is saved, so you need to keep the hand histories
|
|
||||||
# variance not available on stats page
|
|
||||||
# : No graphs
|
|
||||||
#stores a stud/razz hand into the database
|
|
||||||
def ring_stud(config, backend, db, cursor, base, category, site_hand_no, gametype_id, hand_start_time
|
|
||||||
,names, player_ids, start_cashes, antes, card_values, card_suits, winnings, rakes
|
|
||||||
,action_types, allIns, action_amounts, actionNos, hudImportData, maxSeats, tableName
|
|
||||||
,seatNos):
|
|
||||||
|
|
||||||
import_options = config.get_import_parameters()
|
|
||||||
|
|
||||||
saveActions = False if import_options['saveActions'] == False else True
|
|
||||||
fastStoreHudCache = True if import_options['fastStoreHudCache'] == True else False
|
|
||||||
|
|
||||||
fpdb_simple.fillCardArrays(len(names), base, category, card_values, card_suits)
|
|
||||||
|
|
||||||
hands_id = fpdb_simple.storeHands(backend, db, cursor, site_hand_no, gametype_id
|
|
||||||
,hand_start_time, names, tableName, maxSeats, hudImportData)
|
|
||||||
|
|
||||||
#print "before calling store_hands_players_stud, antes:", antes
|
|
||||||
hands_players_ids = fpdb_simple.store_hands_players_stud(backend, db, cursor, hands_id, player_ids
|
|
||||||
,start_cashes, antes, card_values
|
|
||||||
,card_suits, winnings, rakes, seatNos)
|
|
||||||
|
|
||||||
fpdb_simple.storeHudCache(backend, cursor, base, category, gametype_id, hand_start_time, player_ids, hudImportData)
|
|
||||||
|
|
||||||
if saveActions:
|
|
||||||
fpdb_simple.storeActions(cursor, hands_players_ids, action_types
|
|
||||||
,allIns, action_amounts, actionNos)
|
|
||||||
return hands_id
|
|
||||||
#end def ring_stud
|
|
||||||
|
|
||||||
def ring_holdem_omaha(config, backend, db, cursor, base, category, site_hand_no, gametype_id
|
|
||||||
,hand_start_time, names, player_ids, start_cashes, positions, card_values
|
|
||||||
,card_suits, board_values, board_suits, winnings, rakes, action_types, allIns
|
|
||||||
,action_amounts, actionNos, hudImportData, maxSeats, tableName, seatNos):
|
|
||||||
"""stores a holdem/omaha hand into the database"""
|
|
||||||
|
|
||||||
import_options = config.get_import_parameters()
|
|
||||||
saveActions = False if import_options['saveActions'] == False else True
|
|
||||||
fastStoreHudCache = True if import_options['fastStoreHudCache'] == True else False
|
|
||||||
|
|
||||||
# print "DEBUG: saveActions = '%s' fastStoreHudCache = '%s'"%(saveActions, fastStoreHudCache)
|
|
||||||
# print "DEBUG: import_options = ", import_options
|
|
||||||
|
|
||||||
t0 = time()
|
|
||||||
fpdb_simple.fillCardArrays(len(names), base, category, card_values, card_suits)
|
|
||||||
t1 = time()
|
|
||||||
fpdb_simple.fill_board_cards(board_values, board_suits)
|
|
||||||
t2 = time()
|
|
||||||
|
|
||||||
hands_id = fpdb_simple.storeHands(backend, db, cursor, site_hand_no, gametype_id
|
|
||||||
,hand_start_time, names, tableName, maxSeats, hudImportData)
|
|
||||||
t3 = time()
|
|
||||||
hands_players_ids = fpdb_simple.store_hands_players_holdem_omaha(
|
|
||||||
backend, db, cursor, category, hands_id, player_ids, start_cashes
|
|
||||||
, positions, card_values, card_suits, winnings, rakes, seatNos, hudImportData)
|
|
||||||
t4 = time()
|
|
||||||
#print "ring holdem, backend=%d" % backend
|
|
||||||
if fastStoreHudCache:
|
|
||||||
fpdb_simple.storeHudCache2(backend, cursor, base, category, gametype_id, hand_start_time, player_ids, hudImportData)
|
|
||||||
else:
|
|
||||||
fpdb_simple.storeHudCache(backend, cursor, base, category, gametype_id, hand_start_time, player_ids, hudImportData)
|
|
||||||
t5 = time()
|
|
||||||
fpdb_simple.store_board_cards(cursor, hands_id, board_values, board_suits)
|
|
||||||
t6 = time()
|
|
||||||
if saveActions:
|
|
||||||
fpdb_simple.storeActions(cursor, hands_players_ids, action_types, allIns, action_amounts, actionNos)
|
|
||||||
t7 = time()
|
|
||||||
#print "fills=(%4.3f) saves=(%4.3f,%4.3f,%4.3f,%4.3f)" % (t2-t0, t3-t2, t4-t3, t5-t4, t6-t5)
|
|
||||||
return hands_id
|
|
||||||
#end def ring_holdem_omaha
|
|
||||||
|
|
||||||
def tourney_holdem_omaha(config, backend, db, cursor, base, category, siteTourneyNo, buyin, fee, knockout
|
|
||||||
,entries, prizepool, tourney_start, payin_amounts, ranks, tourneyTypeId
|
|
||||||
,siteId #end of tourney specific params
|
|
||||||
,site_hand_no, gametype_id, hand_start_time, names, player_ids
|
|
||||||
,start_cashes, positions, card_values, card_suits, board_values
|
|
||||||
,board_suits, winnings, rakes, action_types, allIns, action_amounts
|
|
||||||
,actionNos, hudImportData, maxSeats, tableName, seatNos):
|
|
||||||
"""stores a tourney holdem/omaha hand into the database"""
|
|
||||||
|
|
||||||
import_options = config.get_import_parameters()
|
|
||||||
saveActions = True if import_options['saveActions'] == True else False
|
|
||||||
fastStoreHudCache = True if import_options['fastStoreHudCache'] == True else False
|
|
||||||
|
|
||||||
fpdb_simple.fillCardArrays(len(names), base, category, card_values, card_suits)
|
|
||||||
fpdb_simple.fill_board_cards(board_values, board_suits)
|
|
||||||
|
|
||||||
tourney_id = fpdb_simple.store_tourneys(cursor, tourneyTypeId, siteTourneyNo, entries, prizepool, tourney_start)
|
|
||||||
tourneys_players_ids = fpdb_simple.store_tourneys_players(cursor, tourney_id, player_ids, payin_amounts, ranks, winnings)
|
|
||||||
|
|
||||||
hands_id = fpdb_simple.storeHands(backend, db, cursor, site_hand_no, gametype_id
|
|
||||||
,hand_start_time, names, tableName, maxSeats)
|
|
||||||
|
|
||||||
hands_players_ids = fpdb_simple.store_hands_players_holdem_omaha_tourney(
|
|
||||||
backend, db, cursor, category, hands_id, player_ids, start_cashes, positions
|
|
||||||
, card_values, card_suits, winnings, rakes, seatNos, tourneys_players_ids)
|
|
||||||
|
|
||||||
#print "tourney holdem, backend=%d" % backend
|
|
||||||
if fastStoreHudCache:
|
|
||||||
fpdb_simple.storeHudCache2(backend, cursor, base, category, gametype_id, hand_start_time, player_ids, hudImportData)
|
|
||||||
else:
|
|
||||||
fpdb_simple.storeHudCache(backend, cursor, base, category, gametype_id, hand_start_time, player_ids, hudImportData)
|
|
||||||
|
|
||||||
fpdb_simple.store_board_cards(cursor, hands_id, board_values, board_suits)
|
|
||||||
|
|
||||||
if saveActions:
|
|
||||||
fpdb_simple.storeActions(cursor, hands_players_ids, action_types, allIns, action_amounts, actionNos)
|
|
||||||
return hands_id
|
|
||||||
#end def tourney_holdem_omaha
|
|
||||||
|
|
||||||
def tourney_stud(config, backend, db, cursor, base, category, siteTourneyNo, buyin, fee, knockout, entries
|
|
||||||
,prizepool, tourneyStartTime, payin_amounts, ranks, tourneyTypeId, siteId
|
|
||||||
,siteHandNo, gametypeId, handStartTime, names, playerIds, startCashes, antes
|
|
||||||
,cardValues, cardSuits, winnings, rakes, actionTypes, allIns, actionAmounts
|
|
||||||
,actionNos, hudImportData, maxSeats, tableName, seatNos):
|
|
||||||
#stores a tourney stud/razz hand into the database
|
|
||||||
|
|
||||||
import_options = config.get_import_parameters()
|
|
||||||
saveActions = True if import_options['saveActions'] == True else False
|
|
||||||
fastStoreHudCache = True if import_options['fastStoreHudCache'] == True else False
|
|
||||||
|
|
||||||
fpdb_simple.fillCardArrays(len(names), base, category, cardValues, cardSuits)
|
|
||||||
|
|
||||||
tourney_id = fpdb_simple.store_tourneys(cursor, tourneyTypeId, siteTourneyNo, entries, prizepool, tourneyStartTime)
|
|
||||||
|
|
||||||
tourneys_players_ids = fpdb_simple.store_tourneys_players(cursor, tourney_id, playerIds, payin_amounts, ranks, winnings)
|
|
||||||
|
|
||||||
hands_id = fpdb_simple.storeHands(backend, db, cursor, siteHandNo, gametypeId, handStartTime, names, tableName, maxSeats)
|
|
||||||
|
|
||||||
hands_players_ids = fpdb_simple.store_hands_players_stud_tourney(backend, db, cursor, hands_id
|
|
||||||
, playerIds, startCashes, antes, cardValues, cardSuits
|
|
||||||
, winnings, rakes, seatNos, tourneys_players_ids)
|
|
||||||
|
|
||||||
fpdb_simple.storeHudCache(backend, cursor, base, category, gametypeId, hand_start_time, playerIds, hudImportData)
|
|
||||||
|
|
||||||
if saveActions:
|
|
||||||
fpdb_simple.storeActions(cursor, hands_players_ids, actionTypes, allIns, actionAmounts, actionNos)
|
|
||||||
return hands_id
|
|
||||||
#end def tourney_stud
|
|
|
@ -1136,21 +1136,14 @@ def storeActions(cursor, handsPlayersIds, actionTypes, allIns, actionAmounts, ac
|
||||||
cursor.executemany("INSERT INTO HandsActions (handsPlayerId, street, actionNo, action, allIn, amount) VALUES (%s, %s, %s, %s, %s, %s)", inserts)
|
cursor.executemany("INSERT INTO HandsActions (handsPlayerId, street, actionNo, action, allIn, amount) VALUES (%s, %s, %s, %s, %s, %s)", inserts)
|
||||||
#end def storeActions
|
#end def storeActions
|
||||||
|
|
||||||
def store_board_cards(cursor, hands_id, board_values, board_suits):
|
|
||||||
#stores into table board_cards
|
|
||||||
cursor.execute ("""INSERT INTO BoardCards (handId, card1Value, card1Suit,
|
|
||||||
card2Value, card2Suit, card3Value, card3Suit, card4Value, card4Suit,
|
|
||||||
card5Value, card5Suit) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""",
|
|
||||||
(hands_id, board_values[0], board_suits[0], board_values[1], board_suits[1],
|
|
||||||
board_values[2], board_suits[2], board_values[3], board_suits[3],
|
|
||||||
board_values[4], board_suits[4]))
|
|
||||||
#end def store_board_cards
|
|
||||||
|
|
||||||
def storeHands(backend, conn, cursor, site_hand_no, gametype_id
|
def storeHands(backend, conn, cursor, site_hand_no, gametype_id
|
||||||
,hand_start_time, names, tableName, maxSeats, hudCache):
|
,hand_start_time, names, tableName, maxSeats, hudCache,
|
||||||
|
board_values, board_suits):
|
||||||
|
cards = [Card.cardFromValueSuit(v,s) for v,s in zip(board_values,board_suits)]
|
||||||
#stores into table hands:
|
#stores into table hands:
|
||||||
cursor.execute ("""INSERT INTO Hands
|
cursor.execute ("""INSERT INTO Hands
|
||||||
(siteHandNo, gametypeId, handStart, seats, tableName, importTime, maxSeats
|
(siteHandNo, gametypeId, handStart, seats, tableName, importTime, maxSeats
|
||||||
|
,boardcard1,boardcard2,boardcard3,boardcard4,boardcard5
|
||||||
,playersVpi, playersAtStreet1, playersAtStreet2
|
,playersVpi, playersAtStreet1, playersAtStreet2
|
||||||
,playersAtStreet3, playersAtStreet4, playersAtShowdown
|
,playersAtStreet3, playersAtStreet4, playersAtShowdown
|
||||||
,street0Raises, street1Raises, street2Raises
|
,street0Raises, street1Raises, street2Raises
|
||||||
|
@ -1159,9 +1152,11 @@ def storeHands(backend, conn, cursor, site_hand_no, gametype_id
|
||||||
,showdownPot
|
,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, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
||||||
"""
|
"""
|
||||||
, (site_hand_no, gametype_id, hand_start_time, len(names), tableName, datetime.datetime.today(), maxSeats
|
, (site_hand_no, gametype_id, hand_start_time, len(names), tableName, datetime.datetime.today(), maxSeats
|
||||||
|
,cards[0], cards[1], cards[2], cards[3], cards[4]
|
||||||
,hudCache['playersVpi'], hudCache['playersAtStreet1'], hudCache['playersAtStreet2']
|
,hudCache['playersVpi'], hudCache['playersAtStreet1'], hudCache['playersAtStreet2']
|
||||||
,hudCache['playersAtStreet3'], hudCache['playersAtStreet4'], hudCache['playersAtShowdown']
|
,hudCache['playersAtStreet3'], hudCache['playersAtStreet4'], hudCache['playersAtShowdown']
|
||||||
,hudCache['street0Raises'], hudCache['street1Raises'], hudCache['street2Raises']
|
,hudCache['street0Raises'], hudCache['street1Raises'], hudCache['street2Raises']
|
||||||
|
@ -2121,7 +2116,7 @@ def storeHudCache(backend, cursor, base, category, gametypeId, hand_start_time,
|
||||||
# hard-code styleKey as 'A000000' (all-time cache, no key) for now
|
# hard-code styleKey as 'A000000' (all-time cache, no key) for now
|
||||||
styleKey = 'A000000'
|
styleKey = 'A000000'
|
||||||
|
|
||||||
#print "storeHudCache, len(playerIds)=", len(playerIds), " len(vpip)=" \
|
#print "storeHudCache2, len(playerIds)=", len(playerIds), " len(vpip)=" \
|
||||||
#, len(hudImportData['street0VPI']), " len(totprof)=", len(hudImportData['totalProfit'])
|
#, len(hudImportData['street0VPI']), " len(totprof)=", len(hudImportData['totalProfit'])
|
||||||
for player in xrange(len(playerIds)):
|
for player in xrange(len(playerIds)):
|
||||||
|
|
||||||
|
@ -2292,189 +2287,6 @@ VALUES (%s, %s, %s, %s, %s, %s,
|
||||||
pass
|
pass
|
||||||
# else:
|
# else:
|
||||||
# print "todo: implement storeHudCache for stud base"
|
# print "todo: implement storeHudCache for stud base"
|
||||||
#end def storeHudCache
|
|
||||||
|
|
||||||
def storeHudCache2(backend, cursor, base, category, gametypeId, hand_start_time, playerIds, hudImportData):
|
|
||||||
"""Modified version aiming for more speed ..."""
|
|
||||||
# if (category=="holdem" or category=="omahahi" or category=="omahahilo"):
|
|
||||||
if use_date_in_hudcache:
|
|
||||||
#print "key =", "d%02d%02d%02d " % (hand_start_time.year-2000, hand_start_time.month, hand_start_time.day)
|
|
||||||
styleKey = "d%02d%02d%02d" % (hand_start_time.year-2000, hand_start_time.month, hand_start_time.day)
|
|
||||||
else:
|
|
||||||
# hard-code styleKey as 'A000000' (all-time cache, no key) for now
|
|
||||||
styleKey = 'A000000'
|
|
||||||
|
|
||||||
#print "storeHudCache2, len(playerIds)=", len(playerIds), " len(vpip)=" \
|
|
||||||
#, len(hudImportData['street0VPI']), " len(totprof)=", len(hudImportData['totalProfit'])
|
|
||||||
for player in xrange(len(playerIds)):
|
|
||||||
|
|
||||||
# Set up a clean row
|
|
||||||
row=[]
|
|
||||||
row.append(0)#blank for id
|
|
||||||
row.append(gametypeId)
|
|
||||||
row.append(playerIds[player])
|
|
||||||
row.append(len(playerIds))#seats
|
|
||||||
for i in xrange(len(hudImportData)+2):
|
|
||||||
row.append(0)
|
|
||||||
|
|
||||||
if base=="hold":
|
|
||||||
row[4]=hudImportData['position'][player]
|
|
||||||
else:
|
|
||||||
row[4]=0
|
|
||||||
row[5]=1 #tourneysGametypeId
|
|
||||||
row[6]+=1 #HDs
|
|
||||||
if hudImportData['street0VPI'][player]: row[7]+=1
|
|
||||||
if hudImportData['street0Aggr'][player]: row[8]+=1
|
|
||||||
if hudImportData['street0_3BChance'][player]: row[9]+=1
|
|
||||||
if hudImportData['street0_3BDone'][player]: row[10]+=1
|
|
||||||
if hudImportData['street1Seen'][player]: row[11]+=1
|
|
||||||
if hudImportData['street2Seen'][player]: row[12]+=1
|
|
||||||
if hudImportData['street3Seen'][player]: row[13]+=1
|
|
||||||
if hudImportData['street4Seen'][player]: row[14]+=1
|
|
||||||
if hudImportData['sawShowdown'][player]: row[15]+=1
|
|
||||||
if hudImportData['street1Aggr'][player]: row[16]+=1
|
|
||||||
if hudImportData['street2Aggr'][player]: row[17]+=1
|
|
||||||
if hudImportData['street3Aggr'][player]: row[18]+=1
|
|
||||||
if hudImportData['street4Aggr'][player]: row[19]+=1
|
|
||||||
if hudImportData['otherRaisedStreet1'][player]: row[20]+=1
|
|
||||||
if hudImportData['otherRaisedStreet2'][player]: row[21]+=1
|
|
||||||
if hudImportData['otherRaisedStreet3'][player]: row[22]+=1
|
|
||||||
if hudImportData['otherRaisedStreet4'][player]: row[23]+=1
|
|
||||||
if hudImportData['foldToOtherRaisedStreet1'][player]: row[24]+=1
|
|
||||||
if hudImportData['foldToOtherRaisedStreet2'][player]: row[25]+=1
|
|
||||||
if hudImportData['foldToOtherRaisedStreet3'][player]: row[26]+=1
|
|
||||||
if hudImportData['foldToOtherRaisedStreet4'][player]: row[27]+=1
|
|
||||||
if hudImportData['wonWhenSeenStreet1'][player]!=0.0: row[28]+=hudImportData['wonWhenSeenStreet1'][player]
|
|
||||||
if hudImportData['wonAtSD'][player]!=0.0: row[29]+=hudImportData['wonAtSD'][player]
|
|
||||||
if hudImportData['stealAttemptChance'][player]: row[30]+=1
|
|
||||||
if hudImportData['stealAttempted'][player]: row[31]+=1
|
|
||||||
if hudImportData['foldBbToStealChance'][player]: row[32]+=1
|
|
||||||
if hudImportData['foldedBbToSteal'][player]: row[33]+=1
|
|
||||||
if hudImportData['foldSbToStealChance'][player]: row[34]+=1
|
|
||||||
if hudImportData['foldedSbToSteal'][player]: row[35]+=1
|
|
||||||
|
|
||||||
if hudImportData['street1CBChance'][player]: row[36]+=1
|
|
||||||
if hudImportData['street1CBDone'][player]: row[37]+=1
|
|
||||||
if hudImportData['street2CBChance'][player]: row[38]+=1
|
|
||||||
if hudImportData['street2CBDone'][player]: row[39]+=1
|
|
||||||
if hudImportData['street3CBChance'][player]: row[40]+=1
|
|
||||||
if hudImportData['street3CBDone'][player]: row[41]+=1
|
|
||||||
if hudImportData['street4CBChance'][player]: row[42]+=1
|
|
||||||
if hudImportData['street4CBDone'][player]: row[43]+=1
|
|
||||||
|
|
||||||
if hudImportData['foldToStreet1CBChance'][player]: row[44]+=1
|
|
||||||
if hudImportData['foldToStreet1CBDone'][player]: row[45]+=1
|
|
||||||
if hudImportData['foldToStreet2CBChance'][player]: row[46]+=1
|
|
||||||
if hudImportData['foldToStreet2CBDone'][player]: row[47]+=1
|
|
||||||
if hudImportData['foldToStreet3CBChance'][player]: row[48]+=1
|
|
||||||
if hudImportData['foldToStreet3CBDone'][player]: row[49]+=1
|
|
||||||
if hudImportData['foldToStreet4CBChance'][player]: row[50]+=1
|
|
||||||
if hudImportData['foldToStreet4CBDone'][player]: row[51]+=1
|
|
||||||
|
|
||||||
#print "player=", player
|
|
||||||
#print "len(totalProfit)=", len(hudImportData['totalProfit'])
|
|
||||||
if hudImportData['totalProfit'][player]:
|
|
||||||
row[52]+=hudImportData['totalProfit'][player]
|
|
||||||
|
|
||||||
if hudImportData['street1CheckCallRaiseChance'][player]: row[53]+=1
|
|
||||||
if hudImportData['street1CheckCallRaiseDone'][player]: row[54]+=1
|
|
||||||
if hudImportData['street2CheckCallRaiseChance'][player]: row[55]+=1
|
|
||||||
if hudImportData['street2CheckCallRaiseDone'][player]: row[56]+=1
|
|
||||||
if hudImportData['street3CheckCallRaiseChance'][player]: row[57]+=1
|
|
||||||
if hudImportData['street3CheckCallRaiseDone'][player]: row[58]+=1
|
|
||||||
if hudImportData['street4CheckCallRaiseChance'][player]: row[59]+=1
|
|
||||||
if hudImportData['street4CheckCallRaiseDone'][player]: row[60]+=1
|
|
||||||
|
|
||||||
# Try to do the update first:
|
|
||||||
num = cursor.execute("""UPDATE HudCache
|
|
||||||
SET HDs=HDs+%s, street0VPI=street0VPI+%s, street0Aggr=street0Aggr+%s,
|
|
||||||
street0_3B4BChance=street0_3B4BChance+%s, street0_3B4BDone=street0_3B4BDone+%s,
|
|
||||||
street1Seen=street1Seen+%s, street2Seen=street2Seen+%s, street3Seen=street3Seen+%s,
|
|
||||||
street4Seen=street4Seen+%s, sawShowdown=sawShowdown+%s,
|
|
||||||
street1Aggr=street1Aggr+%s, street2Aggr=street2Aggr+%s, street3Aggr=street3Aggr+%s,
|
|
||||||
street4Aggr=street4Aggr+%s, otherRaisedStreet1=otherRaisedStreet1+%s,
|
|
||||||
otherRaisedStreet2=otherRaisedStreet2+%s, otherRaisedStreet3=otherRaisedStreet3+%s,
|
|
||||||
otherRaisedStreet4=otherRaisedStreet4+%s,
|
|
||||||
foldToOtherRaisedStreet1=foldToOtherRaisedStreet1+%s, foldToOtherRaisedStreet2=foldToOtherRaisedStreet2+%s,
|
|
||||||
foldToOtherRaisedStreet3=foldToOtherRaisedStreet3+%s, foldToOtherRaisedStreet4=foldToOtherRaisedStreet4+%s,
|
|
||||||
wonWhenSeenStreet1=wonWhenSeenStreet1+%s, wonAtSD=wonAtSD+%s, stealAttemptChance=stealAttemptChance+%s,
|
|
||||||
stealAttempted=stealAttempted+%s, foldBbToStealChance=foldBbToStealChance+%s,
|
|
||||||
foldedBbToSteal=foldedBbToSteal+%s,
|
|
||||||
foldSbToStealChance=foldSbToStealChance+%s, foldedSbToSteal=foldedSbToSteal+%s,
|
|
||||||
street1CBChance=street1CBChance+%s, street1CBDone=street1CBDone+%s, street2CBChance=street2CBChance+%s,
|
|
||||||
street2CBDone=street2CBDone+%s, street3CBChance=street3CBChance+%s,
|
|
||||||
street3CBDone=street3CBDone+%s, street4CBChance=street4CBChance+%s, street4CBDone=street4CBDone+%s,
|
|
||||||
foldToStreet1CBChance=foldToStreet1CBChance+%s, foldToStreet1CBDone=foldToStreet1CBDone+%s,
|
|
||||||
foldToStreet2CBChance=foldToStreet2CBChance+%s, foldToStreet2CBDone=foldToStreet2CBDone+%s,
|
|
||||||
foldToStreet3CBChance=foldToStreet3CBChance+%s,
|
|
||||||
foldToStreet3CBDone=foldToStreet3CBDone+%s, foldToStreet4CBChance=foldToStreet4CBChance+%s,
|
|
||||||
foldToStreet4CBDone=foldToStreet4CBDone+%s, totalProfit=totalProfit+%s,
|
|
||||||
street1CheckCallRaiseChance=street1CheckCallRaiseChance+%s,
|
|
||||||
street1CheckCallRaiseDone=street1CheckCallRaiseDone+%s, street2CheckCallRaiseChance=street2CheckCallRaiseChance+%s,
|
|
||||||
street2CheckCallRaiseDone=street2CheckCallRaiseDone+%s, street3CheckCallRaiseChance=street3CheckCallRaiseChance+%s,
|
|
||||||
street3CheckCallRaiseDone=street3CheckCallRaiseDone+%s, street4CheckCallRaiseChance=street4CheckCallRaiseChance+%s,
|
|
||||||
street4CheckCallRaiseDone=street4CheckCallRaiseDone+%s
|
|
||||||
WHERE gametypeId+0=%s
|
|
||||||
AND playerId=%s
|
|
||||||
AND activeSeats=%s
|
|
||||||
AND position=%s
|
|
||||||
AND tourneyTypeId+0=%s
|
|
||||||
AND styleKey=%s
|
|
||||||
""", (row[6], row[7], row[8], row[9], row[10],
|
|
||||||
row[11], row[12], row[13], row[14], row[15],
|
|
||||||
row[16], row[17], row[18], row[19], row[20],
|
|
||||||
row[21], row[22], row[23], row[24], row[25],
|
|
||||||
row[26], row[27], row[28], row[29], row[30],
|
|
||||||
row[31], row[32], row[33], row[34], row[35],
|
|
||||||
row[36], row[37], row[38], row[39], row[40],
|
|
||||||
row[41], row[42], row[43], row[44], row[45],
|
|
||||||
row[46], row[47], row[48], row[49], row[50],
|
|
||||||
row[51], row[52], row[53], row[54], row[55],
|
|
||||||
row[56], row[57], row[58], row[59], row[60],
|
|
||||||
row[1], row[2], row[3], str(row[4]), row[5], styleKey))
|
|
||||||
# Test statusmessage to see if update worked, do insert if not
|
|
||||||
#print "storehud2, upd num =", num
|
|
||||||
if ( (backend == PGSQL and cursor.statusmessage != "UPDATE 1")
|
|
||||||
or (backend == MYSQL_INNODB and num == 0) ):
|
|
||||||
#print "playerid before insert:",row[2]," num = ", num
|
|
||||||
cursor.execute("""INSERT INTO HudCache
|
|
||||||
(gametypeId, playerId, activeSeats, position, tourneyTypeId, styleKey,
|
|
||||||
HDs, street0VPI, street0Aggr, street0_3BChance, street0_3BDone,
|
|
||||||
street1Seen, street2Seen, street3Seen, street4Seen, sawShowdown,
|
|
||||||
street1Aggr, street2Aggr, street3Aggr, street4Aggr, otherRaisedStreet1,
|
|
||||||
otherRaisedStreet2, otherRaisedStreet3, otherRaisedStreet4, foldToOtherRaisedStreet1, foldToOtherRaisedStreet2,
|
|
||||||
foldToOtherRaisedStreet3, foldToOtherRaisedStreet4, wonWhenSeenStreet1, wonAtSD, stealAttemptChance,
|
|
||||||
stealAttempted, foldBbToStealChance, foldedBbToSteal, foldSbToStealChance, foldedSbToSteal,
|
|
||||||
street1CBChance, street1CBDone, street2CBChance, street2CBDone, street3CBChance,
|
|
||||||
street3CBDone, street4CBChance, street4CBDone, foldToStreet1CBChance, foldToStreet1CBDone,
|
|
||||||
foldToStreet2CBChance, foldToStreet2CBDone, foldToStreet3CBChance, foldToStreet3CBDone, foldToStreet4CBChance,
|
|
||||||
foldToStreet4CBDone, totalProfit, street1CheckCallRaiseChance, street1CheckCallRaiseDone, street2CheckCallRaiseChance,
|
|
||||||
street2CheckCallRaiseDone, street3CheckCallRaiseChance, street3CheckCallRaiseDone, street4CheckCallRaiseChance, street4CheckCallRaiseDone)
|
|
||||||
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)"""
|
|
||||||
, (row[1], row[2], row[3], row[4], row[5], styleKey, row[6], row[7], row[8], row[9], row[10]
|
|
||||||
,row[11], row[12], row[13], row[14], row[15], row[16], row[17], row[18], row[19], row[20]
|
|
||||||
,row[21], row[22], row[23], row[24], row[25], row[26], row[27], row[28], row[29], row[30]
|
|
||||||
,row[31], row[32], row[33], row[34], row[35], row[36], row[37], row[38], row[39], row[40]
|
|
||||||
,row[41], row[42], row[43], row[44], row[45], row[46], row[47], row[48], row[49], row[50]
|
|
||||||
,row[51], row[52], row[53], row[54], row[55], row[56], row[57], row[58], row[59], row[60]) )
|
|
||||||
#print "hopefully inserted hud data line: ", cursor.statusmessage
|
|
||||||
# message seems to be "INSERT 0 1"
|
|
||||||
else:
|
|
||||||
#print "updated(2) hud data line"
|
|
||||||
pass
|
|
||||||
# else:
|
|
||||||
# print "todo: implement storeHudCache for stud base"
|
|
||||||
#end def storeHudCache2
|
#end def storeHudCache2
|
||||||
|
|
||||||
def store_tourneys(cursor, tourneyTypeId, siteTourneyNo, entries, prizepool, startTime):
|
def store_tourneys(cursor, tourneyTypeId, siteTourneyNo, entries, prizepool, startTime):
|
||||||
|
|
271
pyfpdb/interlocks.py
Executable file
271
pyfpdb/interlocks.py
Executable file
|
@ -0,0 +1,271 @@
|
||||||
|
|
||||||
|
# Code from http://ender.snowburst.org:4747/~jjohns/interlocks.py
|
||||||
|
# Thanks JJ!
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os, os.path
|
||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
import signal
|
||||||
|
import base64
|
||||||
|
|
||||||
|
InterProcessLock = None
|
||||||
|
|
||||||
|
"""
|
||||||
|
Just use me like a thread lock. acquire() / release() / locked()
|
||||||
|
|
||||||
|
Differences compared to thread locks:
|
||||||
|
1. By default, acquire()'s wait parameter is 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
|
||||||
|
mechanism how long to sleep between retrying the lock. Has no effect for unix/InterProcessLockFcntl.
|
||||||
|
|
||||||
|
Differences in fpdb version to JJ's original:
|
||||||
|
1. Changed acquire() to return false like other locks
|
||||||
|
2. Made acquire fail if same process already has the lock
|
||||||
|
"""
|
||||||
|
|
||||||
|
class SingleInstanceError(RuntimeError):
|
||||||
|
"Thrown when you try to acquire an InterProcessLock and another version of the process is already running."
|
||||||
|
|
||||||
|
class InterProcessLockBase:
|
||||||
|
def __init__(self, name=None ):
|
||||||
|
self._has_lock = False
|
||||||
|
if not name:
|
||||||
|
name = sys.argv[0]
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
def getHashedName(self):
|
||||||
|
return base64.b64encode(self.name).replace('=','')
|
||||||
|
|
||||||
|
def acquire_impl(self, wait): abstract
|
||||||
|
|
||||||
|
def acquire(self, wait=False, retry_time=1):
|
||||||
|
if self._has_lock: # make sure 2nd acquire in same process fails
|
||||||
|
return False
|
||||||
|
while not self._has_lock:
|
||||||
|
try:
|
||||||
|
self.acquire_impl(wait)
|
||||||
|
self._has_lock = True
|
||||||
|
#print 'i have the lock'
|
||||||
|
except SingleInstanceError:
|
||||||
|
if not wait:
|
||||||
|
# raise # change back to normal acquire functionality, sorry JJ!
|
||||||
|
return False
|
||||||
|
time.sleep(retry_time)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def release(self):
|
||||||
|
self.release_impl()
|
||||||
|
self._has_lock = False
|
||||||
|
|
||||||
|
def locked(self):
|
||||||
|
if self._has_lock:
|
||||||
|
return True
|
||||||
|
try:
|
||||||
|
self.acquire()
|
||||||
|
self.release()
|
||||||
|
return False
|
||||||
|
except SingleInstanceError:
|
||||||
|
return True
|
||||||
|
|
||||||
|
LOCK_FILE_DIRECTORY = '/tmp'
|
||||||
|
|
||||||
|
class InterProcessLockFcntl(InterProcessLockBase):
|
||||||
|
def __init__(self, name=None):
|
||||||
|
InterProcessLockBase.__init__(self, name)
|
||||||
|
self.lockfd = 0
|
||||||
|
self.lock_file_name = os.path.join(LOCK_FILE_DIRECTORY, self.getHashedName() + '.lck')
|
||||||
|
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.
|
||||||
|
def getHashedName(self):
|
||||||
|
import re
|
||||||
|
bad_filename_character_re = re.compile(r'/\?<>\\\:;\*\|\'\"\^=\.\[\]')
|
||||||
|
return bad_filename_character_re.sub('_',self.name)
|
||||||
|
|
||||||
|
def acquire_impl(self, wait):
|
||||||
|
self.lockfd = open(self.lock_file_name, 'w')
|
||||||
|
fcntrl_options = fcntl.LOCK_EX
|
||||||
|
if not wait:
|
||||||
|
fcntrl_options |= fcntl.LOCK_NB
|
||||||
|
try:
|
||||||
|
fcntl.flock(self.lockfd, fcntrl_options)
|
||||||
|
except IOError:
|
||||||
|
self.lockfd.close()
|
||||||
|
self.lockfd = 0
|
||||||
|
raise SingleInstanceError('Could not acquire exclusive lock on '+self.lock_file_name)
|
||||||
|
|
||||||
|
def release_impl(self):
|
||||||
|
fcntl.lockf(self.lockfd, fcntl.LOCK_UN)
|
||||||
|
self.lockfd.close()
|
||||||
|
self.lockfd = 0
|
||||||
|
try:
|
||||||
|
os.unlink(self.lock_file_name)
|
||||||
|
except IOError:
|
||||||
|
# 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.
|
||||||
|
pass
|
||||||
|
|
||||||
|
class InterProcessLockWin32(InterProcessLockBase):
|
||||||
|
def __init__(self, name=None):
|
||||||
|
InterProcessLockBase.__init__(self, name)
|
||||||
|
self.mutex = None
|
||||||
|
|
||||||
|
def acquire_impl(self,wait):
|
||||||
|
self.mutex = win32event.CreateMutex(None, 0, self.getHashedName())
|
||||||
|
if win32api.GetLastError() == winerror.ERROR_ALREADY_EXISTS:
|
||||||
|
self.mutex.Close()
|
||||||
|
self.mutex = None
|
||||||
|
raise SingleInstanceError('Could not acquire exclusive lock on ' + self.name)
|
||||||
|
|
||||||
|
def release_impl(self):
|
||||||
|
self.mutex.Close()
|
||||||
|
|
||||||
|
class InterProcessLockSocket(InterProcessLockBase):
|
||||||
|
def __init__(self, name=None):
|
||||||
|
InterProcessLockBase.__init__(self, name)
|
||||||
|
self.socket = None
|
||||||
|
self.portno = 65530 - abs(self.getHashedName().__hash__()) % 32749
|
||||||
|
|
||||||
|
def acquire_impl(self, wait):
|
||||||
|
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
try:
|
||||||
|
self.socket.bind(('127.0.0.1', self.portno))
|
||||||
|
except socket.error:
|
||||||
|
self.socket.close()
|
||||||
|
self.socket = None
|
||||||
|
raise SingleInstanceError('Could not acquire exclusive lock on ' + self.name)
|
||||||
|
|
||||||
|
def release_impl(self):
|
||||||
|
self.socket.close()
|
||||||
|
self.socket = None
|
||||||
|
|
||||||
|
# Set InterProcessLock to the correct type given the sysem parameters available
|
||||||
|
try:
|
||||||
|
import fcntl
|
||||||
|
InterProcessLock = InterProcessLockFcntl
|
||||||
|
except ImportError:
|
||||||
|
try:
|
||||||
|
import win32event
|
||||||
|
import win32api
|
||||||
|
import winerror
|
||||||
|
InterProcessLock = InterProcessLockWin32
|
||||||
|
except ImportError:
|
||||||
|
import socket
|
||||||
|
InterProcessLock = InterProcessLockSocket
|
||||||
|
|
||||||
|
def test_construct():
|
||||||
|
"""
|
||||||
|
# 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())
|
||||||
|
|
||||||
|
>>> lock1 = InterProcessLock(name=test_name)
|
||||||
|
>>> lock1.acquire()
|
||||||
|
|
||||||
|
>>> lock2 = InterProcessLock(name=test_name)
|
||||||
|
>>> lock3 = InterProcessLock(name=test_name)
|
||||||
|
|
||||||
|
# Since lock1 is locked, other attempts to acquire it fail.
|
||||||
|
>>> lock2.acquire()
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
SingleInstanceError: Could not acquire exclusive lock on /tmp/test.lck
|
||||||
|
|
||||||
|
>>> lock3.acquire()
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
SingleInstanceError: Could not acquire exclusive lock on /tmp/test.lck
|
||||||
|
|
||||||
|
# Release the lock and let lock2 have it.
|
||||||
|
>>> lock1.release()
|
||||||
|
>>> lock2.acquire()
|
||||||
|
|
||||||
|
>>> lock3.acquire()
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
SingleInstanceError: Could not acquire exclusive lock on /tmp/test.lck
|
||||||
|
|
||||||
|
# Release it and give it back to lock1
|
||||||
|
>>> lock2.release()
|
||||||
|
>>> lock1.acquire()
|
||||||
|
|
||||||
|
>>> lock2.acquire()
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
SingleInstanceError: Could not acquire exclusive lock on /tmp/test.lck
|
||||||
|
|
||||||
|
# Test lock status
|
||||||
|
>>> lock2.locked()
|
||||||
|
True
|
||||||
|
>>> lock3.locked()
|
||||||
|
True
|
||||||
|
>>> lock1.locked()
|
||||||
|
True
|
||||||
|
|
||||||
|
>>> lock1.release()
|
||||||
|
|
||||||
|
>>> lock2.locked()
|
||||||
|
False
|
||||||
|
>>> lock3.locked()
|
||||||
|
False
|
||||||
|
>>> lock1.locked()
|
||||||
|
False
|
||||||
|
|
||||||
|
>>> if os.name == 'posix':
|
||||||
|
... def os_independent_kill(pid):
|
||||||
|
... import signal
|
||||||
|
... os.kill(pid, signal.SIGKILL)
|
||||||
|
... else:
|
||||||
|
... assert(os.name == 'nt')
|
||||||
|
... def os_independent_kill(pid):
|
||||||
|
... ''' http://www.python.org/doc/faq/windows/#how-do-i-emulate-os-kill-in-windows '''
|
||||||
|
... import win32api
|
||||||
|
... import win32con
|
||||||
|
... import pywintypes
|
||||||
|
... handle = win32api.OpenProcess(win32con.PROCESS_TERMINATE , pywintypes.FALSE, pid)
|
||||||
|
... return (0 != win32api.TerminateProcess(handle, 0))
|
||||||
|
|
||||||
|
# Test to acquire the lock in another process.
|
||||||
|
>>> def execute(cmd):
|
||||||
|
... cmd = 'import time;' + cmd + 'time.sleep(10);'
|
||||||
|
... process = subprocess.Popen([sys.executable, '-c', cmd])
|
||||||
|
... pid = process.pid
|
||||||
|
... time.sleep(2) # quick hack, but we test synchronization in the end
|
||||||
|
... return pid
|
||||||
|
|
||||||
|
>>> pid = execute('import interlocks;a=interlocks.InterProcessLock(name=\\''+test_name+ '\\');a.acquire();')
|
||||||
|
|
||||||
|
>>> lock1.acquire()
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
SingleInstanceError: Could not acquire exclusive lock on /tmp/test.lck
|
||||||
|
|
||||||
|
>>> os_independent_kill(pid)
|
||||||
|
|
||||||
|
>>> time.sleep(1)
|
||||||
|
|
||||||
|
>>> lock1.acquire()
|
||||||
|
>>> lock1.release()
|
||||||
|
|
||||||
|
# Testing wait
|
||||||
|
|
||||||
|
>>> pid = execute('import interlocks;a=interlocks.InterProcessLock(name=\\''+test_name+ '\\');a.acquire();')
|
||||||
|
|
||||||
|
>>> lock1.acquire()
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
SingleInstanceError: Could not acquire exclusive lock on /tmp/test.lck
|
||||||
|
|
||||||
|
>>> os_independent_kill(pid)
|
||||||
|
|
||||||
|
>>> lock1.acquire(True)
|
||||||
|
>>> lock1.release()
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
if __name__=='__main__':
|
||||||
|
import doctest
|
||||||
|
doctest.testmod(optionflags=doctest.IGNORE_EXCEPTION_DETAIL)
|
Loading…
Reference in New Issue
Block a user