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_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)
|
||||
out_path (default '-' = sys.stdout)
|
||||
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")
|
||||
self.filetype = "text"
|
||||
self.codepage = "cp1252"
|
||||
|
|
|
@ -57,6 +57,21 @@ def fourStartCards(value1, suit1, value2, suit2, value3, suit3, value4, suit4):
|
|||
#AAKKs
|
||||
#AAKKr
|
||||
# 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)
|
||||
|
||||
def cardFromValueSuit(value, suit):
|
||||
|
@ -70,7 +85,7 @@ def cardFromValueSuit(value, suit):
|
|||
def valueSuitFromCard(card):
|
||||
""" Function to convert a card stored in the database (int 0-52) into value
|
||||
and suit like 9s, 4c etc """
|
||||
if card < 0 or card > 52:
|
||||
if card < 0 or card > 52 or not card:
|
||||
return('')
|
||||
else:
|
||||
return( ['', '2h', '3h', '4h', '5h', '6h', '7h', '8h', '9h', 'Th', 'Jh', 'Qh', 'Kh', 'Ah'
|
||||
|
@ -80,4 +95,7 @@ def valueSuitFromCard(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))
|
||||
|
|
|
@ -88,12 +88,32 @@ class Site:
|
|||
self.font_size = node.getAttribute("font_size")
|
||||
self.use_frames = node.getAttribute("use_frames")
|
||||
self.enabled = fix_tf(node.getAttribute("enabled"), default = True)
|
||||
self.xpad = node.getAttribute("xpad")
|
||||
self.ypad = node.getAttribute("ypad")
|
||||
self.layout = {}
|
||||
|
||||
for layout_node in node.getElementsByTagName('layout'):
|
||||
lo = Layout(layout_node)
|
||||
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):
|
||||
temp = "Site = " + self.site_name + "\n"
|
||||
for key in dir(self):
|
||||
|
@ -119,9 +139,16 @@ class Stat:
|
|||
class Game:
|
||||
def __init__(self, node):
|
||||
self.game_name = node.getAttribute("game_name")
|
||||
self.db = node.getAttribute("db")
|
||||
self.rows = int( node.getAttribute("rows") )
|
||||
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_list = aux_text.split(',')
|
||||
|
@ -146,9 +173,10 @@ class Game:
|
|||
|
||||
def __str__(self):
|
||||
temp = "Game = " + self.game_name + "\n"
|
||||
temp = temp + " db = %s\n" % self.db
|
||||
temp = temp + " rows = %d\n" % self.rows
|
||||
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
|
||||
|
||||
for stat in self.stats.keys():
|
||||
|
@ -250,6 +278,7 @@ class Config:
|
|||
|
||||
self.default_config_path = self.get_default_config_path()
|
||||
if file != None: # configuration file path has been passed
|
||||
file = os.path.expanduser(file)
|
||||
if not os.path.exists(file):
|
||||
print "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
|
||||
elif string.lower(self.supported_databases[name].db_server) == 'postgresql':
|
||||
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
|
||||
return db
|
||||
|
||||
|
@ -630,6 +661,8 @@ class Config:
|
|||
parms["font"] = self.supported_sites[site].font
|
||||
parms["font_size"] = self.supported_sites[site].font_size
|
||||
parms["enabled"] = self.supported_sites[site].enabled
|
||||
parms["xpad"] = self.supported_sites[site].xpad
|
||||
parms["ypad"] = self.supported_sites[site].ypad
|
||||
return parms
|
||||
|
||||
def set_site_parameters(self, site_name, converter = None, decoder = None,
|
||||
|
@ -680,9 +713,10 @@ class Config:
|
|||
param = {}
|
||||
if self.supported_games.has_key(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['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
|
||||
return param
|
||||
|
||||
|
|
|
@ -27,66 +27,47 @@ Create and manage the database objects.
|
|||
import sys
|
||||
import traceback
|
||||
from datetime import datetime, date, time, timedelta
|
||||
from time import time, strftime
|
||||
import string
|
||||
|
||||
# pyGTK modules
|
||||
|
||||
# FreePokerTools modules
|
||||
import fpdb_db
|
||||
import fpdb_simple
|
||||
import Configuration
|
||||
import SQL
|
||||
import Card
|
||||
|
||||
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()
|
||||
if (string.lower(db_params['db-server']) == 'postgresql' or
|
||||
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.import_options = c.get_import_parameters()
|
||||
self.type = db_params['db-type']
|
||||
self.sql = SQL.Sql(game = game, type = self.type)
|
||||
self.connection.rollback()
|
||||
self.backend = db_params['db-backend']
|
||||
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:
|
||||
self.hud_style = 'T' # A=All-time
|
||||
|
@ -95,31 +76,78 @@ class Database:
|
|||
# Future values may also include:
|
||||
# H=Hands (last n hands)
|
||||
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
|
||||
# (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
|
||||
# a new session)
|
||||
cur = self.connection.cursor()
|
||||
self.cursor = self.fdb.cursor
|
||||
|
||||
if self.fdb.wrongDbVersion == False:
|
||||
# self.hand_1day_ago used to fetch stats for current session (i.e. if hud_style = 'S')
|
||||
self.hand_1day_ago = 0
|
||||
cur.execute(self.sql.query['get_hand_1day_ago'])
|
||||
row = cur.fetchone()
|
||||
self.cursor.execute(self.sql.query['get_hand_1day_ago'])
|
||||
row = self.cursor.fetchone()
|
||||
if row and row[0]:
|
||||
self.hand_1day_ago = row[0]
|
||||
#print "hand 1day ago =", self.hand_1day_ago
|
||||
|
||||
# self.date_ndays_ago used if hud_style = 'T'
|
||||
d = timedelta(days=self.hud_days)
|
||||
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
|
||||
#cur.execute(self.sql.query['get_table_name'], (hand_id, ))
|
||||
#row = cur.fetchone()
|
||||
# self.hand_nhands_ago is used for fetching stats for last n hands (hud_style = 'H')
|
||||
# This option not used yet
|
||||
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):
|
||||
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):
|
||||
c = self.connection.cursor()
|
||||
c.execute(self.sql.query['get_table_name'], (hand_id, ))
|
||||
|
@ -156,21 +184,13 @@ class Database:
|
|||
|
||||
def get_cards(self, 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.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():
|
||||
cs = ['', '', '', '', '', '', '']
|
||||
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)
|
||||
cards[row[0]] = row[1:]
|
||||
return cards
|
||||
|
||||
def get_common_cards(self, hand):
|
||||
|
@ -178,12 +198,8 @@ class Database:
|
|||
cards = {}
|
||||
c = self.connection.cursor()
|
||||
c.execute(self.sql.query['get_common_cards'], [hand])
|
||||
colnames = [desc[0] for desc in c.description]
|
||||
for row in c.fetchall():
|
||||
s_dict = {}
|
||||
for name, val in zip(colnames, row):
|
||||
s_dict[name] = val
|
||||
cards['common'] = (self.convert_cards(s_dict))
|
||||
# row = c.fetchone()
|
||||
cards['common'] = c.fetchone()
|
||||
return cards
|
||||
|
||||
def convert_cards(self, d):
|
||||
|
@ -211,7 +227,7 @@ class Database:
|
|||
def get_action_from_hand(self, hand_no):
|
||||
action = [ [], [], [], [], [] ]
|
||||
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():
|
||||
street = row[0]
|
||||
act = row[1:]
|
||||
|
@ -222,7 +238,7 @@ class Database:
|
|||
"""Returns a hash of winners:amount won, given a hand number."""
|
||||
winners = {}
|
||||
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():
|
||||
winners[row[0]] = row[1]
|
||||
return winners
|
||||
|
@ -312,6 +328,159 @@ class Database:
|
|||
else:
|
||||
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__":
|
||||
c = Configuration.Config()
|
||||
|
||||
|
@ -333,10 +502,10 @@ if __name__=="__main__":
|
|||
for p in stat_dict.keys():
|
||||
print p, " ", stat_dict[p]
|
||||
|
||||
# print "nutOmatics stats:"
|
||||
# stat_dict = db_connection.get_stats_from_hand(h, hero)
|
||||
# for p in stat_dict.keys():
|
||||
# print p, " ", stat_dict[p]
|
||||
#print "nutOmatics stats:"
|
||||
#stat_dict = db_connection.get_stats_from_hand(h, hero)
|
||||
#for p in stat_dict.keys():
|
||||
# print p, " ", stat_dict[p]
|
||||
|
||||
print "cards =", db_connection.get_cards(u'1')
|
||||
db_connection.close_connection
|
||||
|
|
|
@ -37,7 +37,7 @@ class Everleaf(HandHistoryConverter):
|
|||
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)
|
||||
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)
|
||||
debugging: if False, pass on partially supported game types. If true, have a go and error..."""
|
||||
print "DEBUG: XXXXXXXXXXXXXXX"
|
||||
HandHistoryConverter.__init__(self, in_path, out_path, sitename="Everleaf", follow=follow)
|
||||
HandHistoryConverter.__init__(self, in_path, out_path, sitename="Everleaf", follow=follow, index=index)
|
||||
logging.info("Initialising Everleaf converter class")
|
||||
self.filetype = "text"
|
||||
self.codepage = "cp1252"
|
||||
|
@ -237,11 +237,14 @@ or None if we fail to get the info """
|
|||
# Also works with Omaha hands.
|
||||
cards = m.group('CARDS')
|
||||
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:
|
||||
#Not involved in hand
|
||||
hand.involved = False
|
||||
|
||||
|
||||
def readStudPlayerCards(self, hand, street):
|
||||
# lol. see Plymouth.txt
|
||||
logging.warning("Everleaf readStudPlayerCards is only a stub.")
|
||||
|
@ -292,7 +295,8 @@ or None if we fail to get the info """
|
|||
cards = cards.split(', ')
|
||||
player = m.group('PNAME')
|
||||
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
|
||||
|
||||
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
|
||||
#print "start of GraphViewer constructor"
|
||||
self.db=db
|
||||
self.cursor=db.cursor
|
||||
self.settings=settings
|
||||
self.sql=qdict
|
||||
self.conf = config
|
||||
self.display = display
|
||||
|
@ -235,7 +234,7 @@ class Filters(threading.Thread):
|
|||
|
||||
def __set_hero_name(self, w, site):
|
||||
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):
|
||||
cb = gtk.CheckButton(site)
|
||||
|
@ -556,23 +555,12 @@ def main(argv=None):
|
|||
config = Configuration.Config()
|
||||
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.connect(settings['db-backend'],
|
||||
settings['db-host'],
|
||||
settings['db-databaseName'],
|
||||
settings['db-user'],
|
||||
settings['db-password'])
|
||||
db.do_connect(config)
|
||||
|
||||
qdict = FpdbSQLQueries.FpdbSQLQueries(db.get_backend_name())
|
||||
|
||||
i = Filters(db, settings, config, qdict)
|
||||
i = Filters(db, config, qdict)
|
||||
main_window = gtk.Window()
|
||||
main_window.connect('destroy', destroy)
|
||||
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 *
|
||||
|
||||
# Fulltilt HH Format converter
|
||||
# TODO: cat tourno and table to make table name for tournaments
|
||||
|
||||
class Fulltilt(HandHistoryConverter):
|
||||
|
||||
# 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_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_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>.+)\]")
|
||||
|
||||
# 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.
|
||||
|
||||
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)
|
||||
out_path (default '-' = sys.stdout)
|
||||
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")
|
||||
self.filetype = "text"
|
||||
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_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_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_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_ShownCards = re.compile(r"^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", "omaha", "pl"]
|
||||
|
||||
["tour", "hold", "nl"],
|
||||
["tour", "hold", "pl"],
|
||||
["tour", "hold", "fl"],
|
||||
|
||||
["tour", "stud", "fl"],
|
||||
]
|
||||
|
||||
def determineGameType(self, handText):
|
||||
|
@ -88,7 +117,6 @@ follow : whether to tail -f the input"""
|
|||
m = self.re_GameInfo.search(handText)
|
||||
if not m:
|
||||
return None
|
||||
|
||||
mg = m.groupdict()
|
||||
|
||||
# translations from captured groups to our info strings
|
||||
|
@ -96,39 +124,41 @@ follow : whether to tail -f the input"""
|
|||
games = { # base, category
|
||||
"Hold'em" : ('hold','holdem'),
|
||||
'Omaha Hi' : ('hold','omahahi'),
|
||||
'Omaha H/L' : ('hold','omahahilo'),
|
||||
'Razz' : ('stud','razz'),
|
||||
'7 Card Stud' : ('stud','studhi')
|
||||
'Stud Hi' : ('stud','studhi'),
|
||||
'Stud H/L' : ('stud','studhilo')
|
||||
}
|
||||
currencies = { u' €':'EUR', '$':'USD', '':'T$' }
|
||||
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:
|
||||
if mg['GAME'] != None:
|
||||
(info['base'], info['category']) = games[mg['GAME']]
|
||||
if mg['CURRENCY'] != None:
|
||||
info['currency'] = currencies[mg['CURRENCY']]
|
||||
if mg['TOURNO'] == None: info['type'] = "ring"
|
||||
else: info['type'] = "tour"
|
||||
# NB: SB, BB must be interpreted as blinds or bets depending on limit type.
|
||||
|
||||
return info
|
||||
|
||||
def readHandInfo(self, hand):
|
||||
m = self.re_HandInfo.search(hand.handText,re.DOTALL)
|
||||
|
||||
if(m == None):
|
||||
logging.info("Didn't match re_HandInfo")
|
||||
logging.info(hand.handText)
|
||||
return None
|
||||
|
||||
hand.handid = m.group('HID')
|
||||
hand.tablename = m.group('TABLE')
|
||||
hand.starttime = datetime.datetime.strptime(m.group('DATETIME'), "%H:%M:%S ET - %Y/%m/%d")
|
||||
hand.maxseats = 8 # assume 8-max until we see otherwise
|
||||
if m.group('TABLEATTRIBUTES'):
|
||||
m2 = re.search("(deep )?(\d+)( max)?", m.group('TABLEATTRIBUTES'))
|
||||
hand.maxseats = int(m2.group(2))
|
||||
m2 = self.re_Max.search(m.group('TABLEATTRIBUTES'))
|
||||
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.
|
||||
# m.group('SB')
|
||||
# m.group('BB')
|
||||
|
@ -190,6 +220,7 @@ follow : whether to tail -f the input"""
|
|||
m = self.re_Antes.finditer(hand.handText)
|
||||
for player in m:
|
||||
logging.debug("hand.addAnte(%s,%s)" %(player.group('PNAME'), player.group('ANTE')))
|
||||
# if player.group() !=
|
||||
hand.addAnte(player.group('PNAME'), player.group('ANTE'))
|
||||
|
||||
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')))
|
||||
hand.addBringIn(m.group('PNAME'), m.group('BRINGIN'))
|
||||
else:
|
||||
logging.warning("No bringin found")
|
||||
logging.warning("No bringin found, handid =%s" % hand.handid)
|
||||
|
||||
def readButton(self, hand):
|
||||
hand.buttonpos = int(self.re_Button.search(hand.handText).group('BUTTON'))
|
||||
|
||||
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 = [c.strip() for c in cards.split(' ')]
|
||||
hand.addHoleCards(cards, m.group('PNAME'))
|
||||
|
||||
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")
|
||||
# streets PREFLOP, PREDRAW, and THIRD are special cases beacause
|
||||
# we need to grab hero's cards
|
||||
for street in ('PREFLOP', 'DEAL'):
|
||||
if street in hand.streets.keys():
|
||||
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)
|
||||
for found in m:
|
||||
# if m == None:
|
||||
# hand.involved = False
|
||||
# else:
|
||||
hand.hero = found.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 readAction(self, hand, street):
|
||||
m = self.re_Action.finditer(hand.streets[street])
|
||||
|
@ -308,6 +306,29 @@ follow : whether to tail -f the input"""
|
|||
cards = cards.split(' ')
|
||||
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__":
|
||||
parser = OptionParser()
|
||||
|
|
|
@ -28,6 +28,7 @@ import time
|
|||
import fpdb_import
|
||||
from optparse import OptionParser
|
||||
import Configuration
|
||||
import string
|
||||
|
||||
class GuiAutoImport (threading.Thread):
|
||||
def __init__(self, settings, config):
|
||||
|
@ -153,25 +154,25 @@ class GuiAutoImport (threading.Thread):
|
|||
# results to the same pipe. This means that self.path should be a a list of dirs
|
||||
# to watch.
|
||||
if widget.get_active(): # toggled on
|
||||
# - Does the lock acquisition need to be more sophisticated for multiple dirs?
|
||||
# (see comment above about what to do if pipe already open)
|
||||
# - Ideally we want to release the lock if the auto-import is killed by some
|
||||
# kind of exception - is this possible?
|
||||
if self.settings['global_lock'].acquire(False): # returns false immediately if lock not acquired
|
||||
print "\nGlobal lock taken ..."
|
||||
self.doAutoImportBool = True
|
||||
widget.set_label(u' _Stop Autoimport ')
|
||||
if self.pipe_to_hud is None:
|
||||
if os.name == 'nt':
|
||||
command = "python HUD_main.py" + " %s" % (self.database)
|
||||
command = "python HUD_main.py" + " " + self.settings['cl_options']
|
||||
bs = 0 # windows is not happy with line buffing here
|
||||
self.pipe_to_hud = subprocess.Popen(command, bufsize = bs, stdin = subprocess.PIPE,
|
||||
universal_newlines=True)
|
||||
else:
|
||||
command = os.path.join(sys.path[0], 'HUD_main.py')
|
||||
#command = self.config.execution_path('HUD_main.py') # Hi Ray. Sorry about this, kludging.
|
||||
bs = 1
|
||||
self.pipe_to_hud = subprocess.Popen((command, self.database), bufsize = bs, stdin = subprocess.PIPE,
|
||||
cl = [command, ] + string.split(self.settings['cl_options'])
|
||||
self.pipe_to_hud = subprocess.Popen(cl, bufsize = 1, stdin = subprocess.PIPE,
|
||||
universal_newlines=True)
|
||||
# self.pipe_to_hud = subprocess.Popen((command, self.database), bufsize = bs, stdin = subprocess.PIPE,
|
||||
# universal_newlines=True)
|
||||
# command = command + " %s" % (self.database)
|
||||
# print "command = ", command
|
||||
# self.pipe_to_hud = os.popen(command, 'w')
|
||||
|
||||
# Add directories to importer object.
|
||||
for site in self.input_settings:
|
||||
|
@ -181,9 +182,12 @@ class GuiAutoImport (threading.Thread):
|
|||
|
||||
interval=int(self.intervalEntry.get_text())
|
||||
gobject.timeout_add(interval*1000, self.do_import)
|
||||
else:
|
||||
print "auto-import aborted - global lock not available"
|
||||
else: # toggled off
|
||||
self.settings['global_lock'].release()
|
||||
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:
|
||||
print "HUD already terminated"
|
||||
else:
|
||||
|
@ -192,8 +196,6 @@ class GuiAutoImport (threading.Thread):
|
|||
self.pipe_to_hud = None
|
||||
self.startButton.set_label(u' _Start Autoimport ')
|
||||
|
||||
|
||||
|
||||
#end def GuiAutoImport.startClicked
|
||||
|
||||
def get_vbox(self):
|
||||
|
|
|
@ -49,10 +49,15 @@ class GuiBulkImport():
|
|||
self.importer.RunImportThreaded()
|
||||
|
||||
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?
|
||||
# (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.setMinPrint(int(self.spin_hands.get_text()))
|
||||
self.importer.setQuiet(self.chk_st_st.get_active())
|
||||
|
@ -80,6 +85,11 @@ class GuiBulkImport():
|
|||
self.importer.clearFileList()
|
||||
|
||||
self.lab_info.set_text("Import finished")
|
||||
except:
|
||||
pass
|
||||
self.settings['global_lock'].release()
|
||||
else:
|
||||
print "bulk-import aborted - global lock not available"
|
||||
|
||||
def get_vbox(self):
|
||||
"""returns the vbox of this thread"""
|
||||
|
@ -88,8 +98,7 @@ class GuiBulkImport():
|
|||
def __init__(self, settings, config):
|
||||
self.settings = settings
|
||||
self.config = config
|
||||
self.importer = fpdb_import.Importer(self, self.settings,
|
||||
config)
|
||||
self.importer = fpdb_import.Importer(self, self.settings, config)
|
||||
|
||||
self.vbox = gtk.VBox(False, 0)
|
||||
self.vbox.show()
|
||||
|
@ -196,10 +205,11 @@ class GuiBulkImport():
|
|||
self.load_button.show()
|
||||
|
||||
# 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")
|
||||
row = tcursor.fetchone()
|
||||
tcursor.close()
|
||||
self.importer.database.rollback()
|
||||
self.n_hands_in_db = row[0]
|
||||
if self.n_hands_in_db == 0:
|
||||
self.cb_dropindexes.set_active(2)
|
||||
|
|
|
@ -38,20 +38,19 @@ except:
|
|||
and HUD are NOT affected by this problem."""
|
||||
|
||||
import fpdb_import
|
||||
import fpdb_db
|
||||
import Database
|
||||
import Filters
|
||||
|
||||
class GuiGraphViewer (threading.Thread):
|
||||
|
||||
def __init__(self, db, settings, querylist, config, debug=True):
|
||||
def __init__(self, querylist, config, debug=True):
|
||||
"""Constructor for GraphViewer"""
|
||||
self.debug=debug
|
||||
#print "start of GraphViewer constructor"
|
||||
self.db=db
|
||||
self.cursor=db.cursor
|
||||
self.settings=settings
|
||||
self.sql=querylist
|
||||
self.sql = querylist
|
||||
self.conf = config
|
||||
self.debug = debug
|
||||
#print "start of GraphViewer constructor"
|
||||
self.db = Database.Database(self.conf, sql=self.sql)
|
||||
|
||||
|
||||
filters_display = { "Heroes" : True,
|
||||
"Sites" : True,
|
||||
|
@ -63,7 +62,7 @@ class GuiGraphViewer (threading.Thread):
|
|||
"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.registerButton1Callback(self.generateGraph)
|
||||
self.filters.registerButton2Name("Export to File")
|
||||
|
@ -90,7 +89,7 @@ class GuiGraphViewer (threading.Thread):
|
|||
self.canvas = None
|
||||
|
||||
|
||||
self.db.db.rollback()
|
||||
self.db.rollback()
|
||||
|
||||
#################################
|
||||
#
|
||||
|
@ -126,7 +125,7 @@ class GuiGraphViewer (threading.Thread):
|
|||
#end def get_vbox
|
||||
|
||||
def clearGraphData(self):
|
||||
self.fig.clf()
|
||||
self.fig.clear()
|
||||
if self.canvas is not None:
|
||||
self.canvas.destroy()
|
||||
|
||||
|
@ -146,7 +145,7 @@ class GuiGraphViewer (threading.Thread):
|
|||
for site in sites:
|
||||
if sites[site] == True:
|
||||
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()
|
||||
if len(result) == 1:
|
||||
playerids.append(result[0][0])
|
||||
|
@ -154,7 +153,7 @@ class GuiGraphViewer (threading.Thread):
|
|||
if not sitenos:
|
||||
#Should probably pop up here.
|
||||
print "No sites selected - defaulting to PokerStars"
|
||||
sitenos = [2]
|
||||
return
|
||||
|
||||
if not playerids:
|
||||
print "No player ids found"
|
||||
|
@ -197,6 +196,7 @@ class GuiGraphViewer (threading.Thread):
|
|||
|
||||
self.graphBox.add(self.canvas)
|
||||
self.canvas.show()
|
||||
self.canvas.draw()
|
||||
#self.exportButton.set_sensitive(True)
|
||||
#end of def showClicked
|
||||
|
||||
|
@ -205,7 +205,7 @@ class GuiGraphViewer (threading.Thread):
|
|||
# print "DEBUG: getRingProfitGraph"
|
||||
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.
|
||||
# [5L] into (5) not (5,) and [5L, 2829L] into (5, 2829)
|
||||
nametest = str(tuple(names))
|
||||
|
@ -226,10 +226,10 @@ class GuiGraphViewer (threading.Thread):
|
|||
|
||||
#print "DEBUG: sql query:"
|
||||
#print tmp
|
||||
self.cursor.execute(tmp)
|
||||
self.db.cursor.execute(tmp)
|
||||
#returns (HandId,Winnings,Costs,Profit)
|
||||
winnings = self.db.cursor.fetchall()
|
||||
self.db.db.rollback()
|
||||
self.db.rollback()
|
||||
|
||||
if(winnings == ()):
|
||||
return None
|
||||
|
|
|
@ -20,34 +20,35 @@ import pygtk
|
|||
pygtk.require('2.0')
|
||||
import gtk
|
||||
import os
|
||||
import sys
|
||||
from time import time, strftime
|
||||
|
||||
import Card
|
||||
import fpdb_import
|
||||
import Database
|
||||
import fpdb_db
|
||||
import Filters
|
||||
import FpdbSQLQueries
|
||||
|
||||
class GuiPlayerStats (threading.Thread):
|
||||
def __init__(self, config, querylist, mainwin, debug=True):
|
||||
self.debug=debug
|
||||
self.conf=config
|
||||
self.main_window=mainwin
|
||||
self.debug = debug
|
||||
self.conf = config
|
||||
self.main_window = mainwin
|
||||
self.sql = querylist
|
||||
|
||||
self.MYSQL_INNODB = 2
|
||||
self.PGSQL = 3
|
||||
self.SQLITE = 4
|
||||
|
||||
# create new db connection to avoid conflicts with other threads
|
||||
self.db = fpdb_db.fpdb_db()
|
||||
self.db.do_connect(self.conf)
|
||||
self.cursor=self.db.cursor
|
||||
self.sql = querylist
|
||||
self.db = Database.Database(self.conf, sql=self.sql)
|
||||
self.cursor = self.db.cursor
|
||||
|
||||
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())
|
||||
settings.update(self.conf.get_db_parameters())
|
||||
settings.update(self.conf.get_tv_parameters())
|
||||
settings.update(self.conf.get_import_parameters())
|
||||
settings.update(self.conf.get_default_paths())
|
||||
|
||||
# text used on screen stored here so that it can be configured
|
||||
self.filterText = {'handhead':'Hand Breakdown for all levels listed above'
|
||||
|
@ -66,7 +67,7 @@ class GuiPlayerStats (threading.Thread):
|
|||
"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.registerButton1Callback(self.showDetailFilter)
|
||||
self.filters.registerButton2Name("_Refresh")
|
||||
|
@ -216,7 +217,7 @@ class GuiPlayerStats (threading.Thread):
|
|||
flags = [True]
|
||||
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)
|
||||
#end def fillStatsFrame(self, vbox):
|
||||
|
||||
|
@ -227,11 +228,6 @@ class GuiPlayerStats (threading.Thread):
|
|||
if not flags: holecards = False
|
||||
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.refineQuery(tmp, flags, playerids, sitenos, limits, seats, groups, dates)
|
||||
self.cursor.execute(tmp)
|
||||
|
@ -279,18 +275,16 @@ class GuiPlayerStats (threading.Thread):
|
|||
|
||||
while sqlrow < rows:
|
||||
treerow = []
|
||||
if(row%2 == 0):
|
||||
bgcolor = "white"
|
||||
else:
|
||||
bgcolor = "lightgrey"
|
||||
for col,column in enumerate(cols_to_show):
|
||||
if column[colalias] in colnames:
|
||||
value = result[sqlrow][colnames.index(column[colalias])]
|
||||
if column[colalias] == 'plposition':
|
||||
if value == 'B':
|
||||
value = 'BB'
|
||||
if value == 'S':
|
||||
elif value == 'S':
|
||||
value = 'SB'
|
||||
elif value == '0':
|
||||
value = 'Btn'
|
||||
else:
|
||||
if column[colalias] == 'game':
|
||||
if holecards:
|
||||
|
@ -388,7 +382,8 @@ class GuiPlayerStats (threading.Thread):
|
|||
|
||||
# Group by position?
|
||||
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
|
||||
[x for x in self.columns if x[0] == 'plposition'][0][1] = True
|
||||
else:
|
||||
|
|
|
@ -20,31 +20,31 @@ import pygtk
|
|||
pygtk.require('2.0')
|
||||
import gtk
|
||||
import os
|
||||
from time import time, strftime
|
||||
|
||||
import fpdb_import
|
||||
import fpdb_db
|
||||
import Database
|
||||
import Filters
|
||||
import FpdbSQLQueries
|
||||
|
||||
class GuiPositionalStats (threading.Thread):
|
||||
def __init__(self, config, querylist, debug=True):
|
||||
self.debug=debug
|
||||
self.conf=config
|
||||
self.debug = debug
|
||||
self.conf = config
|
||||
self.sql = querylist
|
||||
self.MYSQL_INNODB = 2
|
||||
self.PGSQL = 3
|
||||
self.SQLITE = 4
|
||||
|
||||
# create new db connection to avoid conflicts with other threads
|
||||
self.db = fpdb_db.fpdb_db()
|
||||
self.db.do_connect(self.conf)
|
||||
self.cursor=self.db.cursor
|
||||
self.sql = querylist
|
||||
self.db = Database.Database(self.conf, sql=self.sql)
|
||||
self.cursor = self.db.cursor
|
||||
|
||||
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())
|
||||
settings.update(self.conf.get_db_parameters())
|
||||
settings.update(self.conf.get_tv_parameters())
|
||||
settings.update(self.conf.get_import_parameters())
|
||||
settings.update(self.conf.get_default_paths())
|
||||
|
||||
filters_display = { "Heroes" : True,
|
||||
"Sites" : True,
|
||||
|
@ -58,21 +58,50 @@ class GuiPositionalStats (threading.Thread):
|
|||
"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.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.stats_frame = None
|
||||
self.stats_vbox = None
|
||||
|
||||
self.main_hbox = gtk.HBox(False, 0)
|
||||
self.main_hbox.show()
|
||||
|
||||
statsFrame = gtk.Frame("Stats:")
|
||||
statsFrame.set_label_align(0.0, 0.0)
|
||||
statsFrame.show()
|
||||
self.stats_frame = gtk.VBox(False, 0)
|
||||
self.stats_frame = gtk.Frame()
|
||||
self.stats_frame.set_label_align(0.0, 0.0)
|
||||
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.
|
||||
# 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"
|
||||
)
|
||||
|
||||
self.fillStatsFrame(self.stats_frame)
|
||||
statsFrame.add(self.stats_frame)
|
||||
self.fillStatsFrame(self.stats_vbox)
|
||||
self.stats_frame.add(self.stats_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):
|
||||
|
@ -107,9 +136,12 @@ class GuiPositionalStats (threading.Thread):
|
|||
print "DEBUG: activesite set to %s" %(self.activesite)
|
||||
|
||||
def refreshStats(self, widget, data):
|
||||
try: self.stats_table.destroy()
|
||||
try: self.stats_vbox.destroy()
|
||||
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):
|
||||
sites = self.filters.getSites()
|
||||
|
@ -144,66 +176,104 @@ class GuiPositionalStats (threading.Thread):
|
|||
self.createStatsTable(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
|
||||
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.refineQuery(tmp, playerids, sitenos, limits, seats, dates)
|
||||
self.cursor.execute(tmp)
|
||||
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)
|
||||
colnames = [desc[0].lower() for desc in self.cursor.description]
|
||||
|
||||
last_game,last_seats,sqlrow = "","",0
|
||||
while sqlrow < rows:
|
||||
if(row%2 == 0):
|
||||
bgcolor = "white"
|
||||
else:
|
||||
bgcolor = "lightgrey"
|
||||
rowprinted=0
|
||||
treerow = []
|
||||
avgcol = colnames.index('avgseats')
|
||||
for col,colname in enumerate(self.posncols):
|
||||
if colname in colnames:
|
||||
sqlcol = colnames.index(colname)
|
||||
else:
|
||||
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 sqlrow == 0:
|
||||
l = gtk.Label(result[sqlrow][sqlcol])
|
||||
value = result[sqlrow][sqlcol]
|
||||
rowprinted=1
|
||||
elif result[sqlrow][0] != last_game:
|
||||
l = gtk.Label(' ')
|
||||
value = ' '
|
||||
elif 'show' in seats and seats['show'] and result[sqlrow][avgcol] != last_seats:
|
||||
l = gtk.Label(' ')
|
||||
value = ' '
|
||||
else:
|
||||
l = gtk.Label(result[sqlrow][sqlcol])
|
||||
value = result[sqlrow][sqlcol]
|
||||
rowprinted=1
|
||||
else:
|
||||
l = gtk.Label(' ')
|
||||
if col == 0:
|
||||
l.set_alignment(xalign=0.0, yalign=0.5)
|
||||
elif col == 1:
|
||||
l.set_alignment(xalign=0.5, yalign=0.5)
|
||||
value = ' '
|
||||
if value and value != -999:
|
||||
treerow.append(value)
|
||||
else:
|
||||
l.set_alignment(xalign=1.0, yalign=0.5)
|
||||
eb.add(l)
|
||||
self.stats_table.attach(eb, col, col+1, row+1, row+2, yoptions=gtk.SHRINK)
|
||||
l.show()
|
||||
eb.show()
|
||||
treerow.append(' ')
|
||||
iter = liststore.append(treerow)
|
||||
last_game = result[sqlrow][0]
|
||||
last_seats = result[sqlrow][avgcol]
|
||||
if rowprinted:
|
||||
|
@ -220,50 +290,36 @@ class GuiPositionalStats (threading.Thread):
|
|||
|
||||
# blank row between main stats and totals:
|
||||
col = 0
|
||||
if(row%2 == 0):
|
||||
bgcolor = "white"
|
||||
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()
|
||||
treerow = [' ' for x in self.posncols]
|
||||
iter = liststore.append(treerow)
|
||||
row = row + 1
|
||||
|
||||
for sqlrow in range(rows):
|
||||
if(row%2 == 0):
|
||||
bgcolor = "white"
|
||||
else:
|
||||
bgcolor = "lightgrey"
|
||||
treerow = []
|
||||
for col,colname in enumerate(self.posncols):
|
||||
if colname in colnames:
|
||||
sqlcol = colnames.index(colname)
|
||||
elif colname != "plposition":
|
||||
continue
|
||||
eb = gtk.EventBox()
|
||||
eb.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(bgcolor))
|
||||
if colname == 'plposition':
|
||||
l = gtk.Label('Totals')
|
||||
value = 'Totals'
|
||||
elif result[sqlrow][sqlcol]:
|
||||
l = gtk.Label(result[sqlrow][sqlcol])
|
||||
value = result[sqlrow][sqlcol]
|
||||
else:
|
||||
l = gtk.Label(' ')
|
||||
if col == 0:
|
||||
l.set_alignment(xalign=0.0, yalign=0.5)
|
||||
elif col == 1:
|
||||
l.set_alignment(xalign=0.5, yalign=0.5)
|
||||
value = ' '
|
||||
if value and value != -999:
|
||||
treerow.append(value)
|
||||
else:
|
||||
l.set_alignment(xalign=1.0, yalign=0.5)
|
||||
eb.add(l)
|
||||
self.stats_table.attach(eb, col, col+1, row+1, row+2, yoptions=gtk.SHRINK)
|
||||
l.show()
|
||||
eb.show()
|
||||
treerow.append(' ')
|
||||
iter = liststore.append(treerow)
|
||||
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):
|
||||
|
||||
def refineQuery(self, query, playerids, sitenos, limits, seats, dates):
|
||||
|
|
|
@ -25,29 +25,28 @@ from numpy import diff, nonzero
|
|||
|
||||
import Card
|
||||
import fpdb_import
|
||||
import fpdb_db
|
||||
import Database
|
||||
import Filters
|
||||
import FpdbSQLQueries
|
||||
|
||||
class GuiSessionViewer (threading.Thread):
|
||||
def __init__(self, config, querylist, debug=True):
|
||||
self.debug=debug
|
||||
self.conf=config
|
||||
self.debug = debug
|
||||
self.conf = config
|
||||
self.sql = querylist
|
||||
self.MYSQL_INNODB = 2
|
||||
self.PGSQL = 3
|
||||
self.SQLITE = 4
|
||||
|
||||
# create new db connection to avoid conflicts with other threads
|
||||
self.db = fpdb_db.fpdb_db()
|
||||
self.db.do_connect(self.conf)
|
||||
self.cursor=self.db.cursor
|
||||
self.sql = querylist
|
||||
self.db = Database.Database(self.conf, sql=self.sql)
|
||||
self.cursor = self.db.cursor
|
||||
|
||||
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())
|
||||
settings.update(self.conf.get_db_parameters())
|
||||
settings.update(self.conf.get_tv_parameters())
|
||||
settings.update(self.conf.get_import_parameters())
|
||||
settings.update(self.conf.get_default_paths())
|
||||
|
||||
# text used on screen stored here so that it can be configured
|
||||
self.filterText = {'handhead':'Hand Breakdown for all levels listed above'
|
||||
|
@ -66,7 +65,7 @@ class GuiSessionViewer (threading.Thread):
|
|||
"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.registerButton2Callback(self.refreshStats)
|
||||
|
||||
|
@ -195,7 +194,7 @@ class GuiSessionViewer (threading.Thread):
|
|||
flags = [True]
|
||||
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)
|
||||
#end def fillStatsFrame(self, vbox):
|
||||
|
||||
|
|
|
@ -159,6 +159,50 @@
|
|||
<location seat="9" x="70" y="53"> </location>
|
||||
</layout>
|
||||
</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_games>
|
||||
|
@ -292,6 +336,7 @@
|
|||
<hhc site="PokerStars" converter="PokerStarsToFpdb"/>
|
||||
<hhc site="Full Tilt Poker" converter="FulltiltToFpdb"/>
|
||||
<hhc site="Everleaf" converter="EverleafToFpdb"/>
|
||||
<hhc site="Win2day" converter="Win2dayToFpdb"/>
|
||||
</hhcs>
|
||||
|
||||
<supported_databases>
|
||||
|
@ -300,4 +345,3 @@
|
|||
|
||||
</FreePokerToolsConfig>
|
||||
|
||||
|
||||
|
|
|
@ -29,12 +29,16 @@ Main for FreePokerTools HUD.
|
|||
|
||||
# Standard Library modules
|
||||
import sys
|
||||
|
||||
# redirect the stderr
|
||||
errorfile = open('HUD-error.txt', 'w', 0)
|
||||
sys.stderr = errorfile
|
||||
|
||||
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 time
|
||||
import string
|
||||
|
@ -59,7 +63,7 @@ class HUD_main(object):
|
|||
|
||||
def __init__(self, db_name = 'fpdb'):
|
||||
self.db_name = db_name
|
||||
self.config = Configuration.Config()
|
||||
self.config = Configuration.Config(file=options.config, dbname=options.dbname)
|
||||
self.hud_dict = {}
|
||||
|
||||
# a thread to read stdin
|
||||
|
@ -198,17 +202,12 @@ class HUD_main(object):
|
|||
self.db_connection.connection.rollback()
|
||||
|
||||
if __name__== "__main__":
|
||||
sys.stderr.write("HUD_main starting\n")
|
||||
|
||||
# database name can be passed on command line
|
||||
try:
|
||||
db_name = sys.argv[1]
|
||||
except:
|
||||
db_name = 'fpdb'
|
||||
sys.stderr.write("Using db name = %s\n" % (db_name))
|
||||
sys.stderr.write("HUD_main starting\n")
|
||||
sys.stderr.write("Using db name = %s\n" % (options.dbname))
|
||||
|
||||
# start the HUD_main object
|
||||
hm = HUD_main(db_name = db_name)
|
||||
hm = HUD_main(db_name = options.dbname)
|
||||
|
||||
# start the event loop
|
||||
gtk.main()
|
||||
|
|
700
pyfpdb/Hand.py
700
pyfpdb/Hand.py
File diff suppressed because it is too large
Load Diff
|
@ -28,91 +28,54 @@ import codecs
|
|||
from decimal import Decimal
|
||||
import operator
|
||||
from xml.dom.minidom import Node
|
||||
# from pokereval import PokerEval
|
||||
import time
|
||||
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
|
||||
gettext.install('myapplication')
|
||||
gettext.install('fpdb')
|
||||
|
||||
class HandHistoryConverter():
|
||||
|
||||
READ_CHUNK_SIZE = 10000 # bytes to read at a time from file (in tail mode)
|
||||
def __init__(self, in_path = '-', out_path = '-', sitename = None, follow=False):
|
||||
logging.info("HandHistory init called")
|
||||
def __init__(self, in_path = '-', out_path = '-', sitename = None, follow=False, index=0):
|
||||
logging.info("HandHistory init")
|
||||
|
||||
# default filetype and codepage. Subclasses should set these properly.
|
||||
self.filetype = "text"
|
||||
self.codepage = "utf8"
|
||||
self.index = 0
|
||||
|
||||
self.in_path = in_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
|
||||
else:
|
||||
# TODO: out_path should be sanity checked before opening. Perhaps in fpdb_import?
|
||||
# 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') # doomswitch is now on :|
|
||||
# TODO: out_path should be sanity checked.
|
||||
self.out_fh = open(self.out_path, 'w')
|
||||
|
||||
self.sitename = sitename
|
||||
self.follow = follow
|
||||
self.compiledPlayers = set()
|
||||
self.maxseats = 10
|
||||
|
||||
def __str__(self):
|
||||
#TODO : I got rid of most of the hhdir stuff.
|
||||
tmp = "HandHistoryConverter: '%s'\n" % (self.sitename)
|
||||
#tmp = tmp + "\thhbase: '%s'\n" % (self.hhbase)
|
||||
#tmp = tmp + "\thhdir: '%s'\n" % (self.hhdir)
|
||||
tmp = tmp + "\tfiletype: '%s'\n" % (self.filetype)
|
||||
tmp = tmp + "\tinfile: '%s'\n" % (self.in_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
|
||||
return """
|
||||
HandHistoryConverter: '%(sitename)s'
|
||||
filetype: '%(filetype)s'
|
||||
in_path: '%(in_path)s'
|
||||
out_path: '%(out_path)s'
|
||||
""" % { 'sitename':self.sitename, 'filetype':self.filetype, 'in_path':self.in_path, 'out_path':self.out_path }
|
||||
|
||||
def start(self):
|
||||
"""process a hand at a time from the input specified by in_path.
|
||||
If in follow mode, wait for more data to turn up.
|
||||
Otherwise, finish at eof...
|
||||
Otherwise, finish at eof.
|
||||
|
||||
"""
|
||||
starttime = time.time()
|
||||
|
@ -129,7 +92,7 @@ Otherwise, finish at eof...
|
|||
handsList = self.allHandsAsList()
|
||||
logging.info("Parsing %d hands" % len(handsList))
|
||||
for handText in handsList:
|
||||
self.processHand(handText)
|
||||
self.processedHands.append(self.processHand(handText))
|
||||
numHands= len(handsList)
|
||||
endtime = time.time()
|
||||
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):
|
||||
gametype = self.determineGameType(handText)
|
||||
logging.debug("gametype %s" % gametype)
|
||||
hand = None
|
||||
if gametype is None:
|
||||
l = None
|
||||
gametype = "unmatched"
|
||||
|
@ -224,9 +188,7 @@ which it expects to find at self.re_TailSplitHands -- see for e.g. Everleaf.py.
|
|||
base = gametype['base']
|
||||
limit = gametype['limitType']
|
||||
l = [type] + [base] + [limit]
|
||||
hand = None
|
||||
if l in self.readSupportedGames():
|
||||
hand = None
|
||||
if gametype['base'] == 'hold':
|
||||
logging.debug("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)
|
||||
|
||||
if hand:
|
||||
# print hand
|
||||
hand.writeHand(self.out_fh)
|
||||
return hand
|
||||
else:
|
||||
logging.info("Unsupported game type: %s" % gametype)
|
||||
# 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 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
|
||||
# an inheriting class can calculate it for the specific site if need be.
|
||||
def getRake(self, hand):
|
||||
|
@ -349,6 +318,7 @@ or None if we fail to get the info """
|
|||
self.filetype = filetype
|
||||
self.codepage = codepage
|
||||
|
||||
#This function doesn't appear to be used
|
||||
def splitFileIntoHands(self):
|
||||
hands = []
|
||||
self.obs = self.obs.strip()
|
||||
|
@ -370,7 +340,9 @@ or None if we fail to get the info """
|
|||
else:
|
||||
logging.debug("Opening %s with %s" % (self.in_path, self.codepage))
|
||||
in_fh = codecs.open(self.in_path, 'r', self.codepage)
|
||||
in_fh.seek(self.index)
|
||||
self.obs = in_fh.read()
|
||||
self.index = in_fh.tell()
|
||||
in_fh.close()
|
||||
elif(self.filetype == "xml"):
|
||||
try:
|
||||
|
@ -379,10 +351,39 @@ or None if we fail to get the info """
|
|||
except:
|
||||
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):
|
||||
#TODO: Return a status of true if file processed ok
|
||||
return True
|
||||
|
||||
def getProcessedHands(self):
|
||||
return self.processedHands
|
||||
|
||||
def getProcessedFile(self):
|
||||
return self.out_path
|
||||
|
||||
def getLastCharacterRead(self):
|
||||
return self.index
|
||||
|
|
|
@ -34,10 +34,13 @@ import gobject
|
|||
|
||||
# FreePokerTools modules
|
||||
from Mucked import Aux_Window
|
||||
from Mucked import Seat_Window
|
||||
from Mucked import Aux_Seats
|
||||
|
||||
class Hello(Aux_Window):
|
||||
"""A 'Hello World' Aux_Window demo."""
|
||||
def create(self):
|
||||
print "creating Hello"
|
||||
# This demo simply creates a label in a window.
|
||||
self.container = gtk.Window()
|
||||
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.
|
||||
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):
|
||||
"""A 'Hello World' Aux_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()
|
||||
class Hello_Seats(Aux_Seats):
|
||||
"""A 'Hello World' Seat_Window demo."""
|
||||
|
||||
def print_cards(self, *args):
|
||||
# callback for the menu item
|
||||
print "cards =", self.hud.cards
|
||||
def create_contents(self, container, i):
|
||||
container.label = gtk.Label("empty")
|
||||
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 Mucked
|
||||
import Database
|
||||
import HUD_main
|
||||
#import HUD_main
|
||||
|
||||
def importName(module_name, 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')
|
||||
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])
|
||||
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') )
|
||||
|
||||
if usegtkframes:
|
||||
|
|
273
pyfpdb/Mucked.py
273
pyfpdb/Mucked.py
|
@ -35,28 +35,20 @@ import gobject
|
|||
# FreePokerTools modules
|
||||
import Configuration
|
||||
import Database
|
||||
import Card
|
||||
|
||||
class Aux_Window:
|
||||
class Aux_Window(object):
|
||||
def __init__(self, hud, params, config):
|
||||
self.hud = hud
|
||||
self.params = params
|
||||
self.config = config
|
||||
|
||||
def update_data(self, *args):
|
||||
pass
|
||||
|
||||
def update_gui(self, *args):
|
||||
pass
|
||||
|
||||
def create(self, *args):
|
||||
pass
|
||||
|
||||
def relocate(self, *args):
|
||||
pass
|
||||
|
||||
def save_layout(self, *args):
|
||||
pass
|
||||
|
||||
# Override these methods as needed
|
||||
def update_data(self, *args): pass
|
||||
def update_gui(self, *args): pass
|
||||
def create(self, *args): pass
|
||||
def relocate(self, *args): pass
|
||||
def save_layout(self, *args): pass
|
||||
def destroy(self):
|
||||
try:
|
||||
self.container.destroy()
|
||||
|
@ -67,27 +59,40 @@ class Aux_Window:
|
|||
# Some utility routines useful for Aux_Windows
|
||||
#
|
||||
def get_card_images(self):
|
||||
card_images = {}
|
||||
suits = ('S', 'H', 'D', 'C')
|
||||
ranks = ('A', 'K', 'Q', 'J', 'T', '9', '8', '7', '6', '5', '4', '3', '2', 'B')
|
||||
|
||||
card_images = 53 * [0]
|
||||
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']))
|
||||
|
||||
for j in range(0, 14):
|
||||
for j in range(0, 13):
|
||||
for i in range(0, 4):
|
||||
card_images[Card.cardFromValueSuit(ranks[j], suits[i])] = self.cropper(pb, i, j)
|
||||
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)
|
||||
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)
|
||||
# cards are 30 wide x 42 high
|
||||
|
||||
def split_cards(self, card):
|
||||
if card == 'xx': return ('B', 'S')
|
||||
return (card[0], card[1].upper())
|
||||
def cropper(self, pb, i, j):
|
||||
"""Crop out a card image given an FTP deck and the i, j position."""
|
||||
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):
|
||||
"""Returns the number of cards in the list."""
|
||||
n = 0
|
||||
for c in cards:
|
||||
if c in set('shdc'): return True
|
||||
return False
|
||||
if c != None and c > 0: n = n + 1
|
||||
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):
|
||||
def __init__(self, hud, config, params):
|
||||
|
@ -108,9 +113,10 @@ class Stud_mucked(Aux_Window):
|
|||
|
||||
def create(self):
|
||||
|
||||
self.container =gtk.Window()
|
||||
self.container = gtk.Window()
|
||||
self.vbox = gtk.VBox()
|
||||
self.container.add(self.vbox)
|
||||
self.container.set_title(self.hud.table.name)
|
||||
|
||||
self.mucked_list.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
|
||||
for stat in self.parent.hud.stat_dict.itervalues():
|
||||
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"
|
||||
|
||||
def update_gui(self, new_hand_id):
|
||||
|
@ -240,7 +248,7 @@ class Stud_cards:
|
|||
|
||||
for r in range(0, self.rows):
|
||||
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()
|
||||
|
||||
# set up the contents for the cells
|
||||
|
@ -287,11 +295,11 @@ class Stud_cards:
|
|||
self.clear()
|
||||
for c, cards in self.parent.hud.cards.iteritems():
|
||||
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]),
|
||||
(4, cards[8:10]), (5, cards[10:12]), (6, cards[12:14])):
|
||||
if not i[1] == "xx":
|
||||
for i in ((0, cards[0]), (1, cards[1]), (2, cards[2]), (3, cards[3]),
|
||||
(4, cards[4]), (5, cards[5]), (6, cards[6])):
|
||||
if not i[1] == 0:
|
||||
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
|
||||
for c in (0, 1, 2):
|
||||
for r in range(0, self.rows):
|
||||
|
@ -314,90 +322,155 @@ class Stud_cards:
|
|||
for r in range(0, self.rows):
|
||||
self.grid_contents[(1, r)].set_text(" ")
|
||||
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('')
|
||||
|
||||
class Flop_Mucked(Aux_Window):
|
||||
"""Aux_Window class for displaying mucked cards for flop games."""
|
||||
class Seat_Window(gtk.Window):
|
||||
"""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):
|
||||
self.hud = hud # hud object that this aux window supports
|
||||
self.config = config # configuration object for this aux window to use
|
||||
self.params = params # dict aux params from config
|
||||
self.positions = {} # dict of window positions
|
||||
# self.rel_positions = {} # dict of window positions, relative to the table origin
|
||||
self.displayed_cards = False
|
||||
self.displayed = False # the seat windows are displayed
|
||||
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.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):
|
||||
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))
|
||||
|
||||
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']):
|
||||
if i == 'common':
|
||||
(x, y) = self.params['layout'][self.hud.max].common
|
||||
else:
|
||||
(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_property("skip-taskbar-hint", True)
|
||||
self.m_windows[i].set_transient_for(self.hud.main_window)
|
||||
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].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.rel_positions[i] = (int(x), int(y))
|
||||
self.m_windows[i].move(self.positions[i][0], self.positions[i][1])
|
||||
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()
|
||||
if self.uses_timer:
|
||||
self.m_windows[i].hide()
|
||||
|
||||
def update_gui(self, new_hand_id):
|
||||
"""Prepare and show the mucked cards."""
|
||||
if self.displayed_cards:
|
||||
self.hide_mucked_cards()
|
||||
self.displayed_cards = False
|
||||
for (i, cards) in self.hud.cards.iteritems():
|
||||
if self.has_cards(cards):
|
||||
"""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'])*len(cards)/2,
|
||||
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[k:k+2] for k in xrange(0, len(cards), 2)]:
|
||||
for card in cards:
|
||||
# concatenate each card image to scratch
|
||||
self.card_images[self.split_cards(card)].copy_area(0, 0,
|
||||
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'])
|
||||
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
|
||||
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'])
|
||||
|
||||
for stats in self.hud.stat_dict.itervalues():
|
||||
self.eb[stats['seat']].set_tooltip_text(stats['screen_name'])
|
||||
def update_gui(self, new_hand_id):
|
||||
"""Prepare and show the mucked cards."""
|
||||
if self.displayed: self.hide()
|
||||
|
||||
if self.displayed_cards and float(self.params['timeout']) > 0:
|
||||
# See how many players showed a hand. Skip if only 1 shows (= hero)
|
||||
n_sd = 0
|
||||
for (i, cards) in self.hud.cards.iteritems():
|
||||
n_cards = self.has_cards(cards)
|
||||
if n_cards > 0 and i != 'common':
|
||||
n_sd = n_sd + 1
|
||||
if n_sd < 2:
|
||||
print "skipping, n_sd =", n_sd
|
||||
return
|
||||
|
||||
super(Flop_Mucked, self).update_gui(new_hand_id)
|
||||
|
||||
if self.displayed and float(self.params['timeout']) > 0:
|
||||
self.timer_on = True
|
||||
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):
|
||||
# this is the callback from the timeout
|
||||
|
||||
|
@ -406,15 +479,9 @@ class Flop_Mucked(Aux_Window):
|
|||
if not self.timer_on:
|
||||
return False
|
||||
else:
|
||||
self.hide_mucked_cards()
|
||||
self.hide()
|
||||
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):
|
||||
"""Handle button clicks in the event boxes."""
|
||||
|
||||
|
@ -432,56 +499,14 @@ class Flop_Mucked(Aux_Window):
|
|||
self.timer_on = False
|
||||
else:
|
||||
self.timer_on = False
|
||||
self.hide_mucked_cards()
|
||||
self.hide()
|
||||
|
||||
elif event.button == 1: # left button event
|
||||
window = widget.get_parent()
|
||||
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):
|
||||
for (i, cards) in self.hud.cards.iteritems():
|
||||
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
|
||||
|
||||
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()
|
||||
self.displayed = True
|
||||
|
|
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 -*-
|
||||
#
|
||||
# Copyright 2008, Carl Gherardi
|
||||
|
@ -18,6 +18,7 @@
|
|||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
########################################################################
|
||||
|
||||
# TODO: straighten out discards for draw games
|
||||
import sys
|
||||
from HandHistoryConverter import *
|
||||
|
||||
|
@ -25,22 +26,41 @@ from HandHistoryConverter import *
|
|||
|
||||
class PokerStars(HandHistoryConverter):
|
||||
|
||||
############################################################
|
||||
# Class Variables
|
||||
|
||||
# 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_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_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>.+)\]")
|
||||
# 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)
|
||||
out_path (default '-' = sys.stdout)
|
||||
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")
|
||||
self.filetype = "text"
|
||||
self.codepage = "cp1252"
|
||||
|
@ -53,6 +73,9 @@ follow : whether to tail -f the input"""
|
|||
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.
|
||||
# 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
|
||||
player_re = "(?P<PNAME>" + "|".join(map(re.escape, players)) + ")"
|
||||
logging.debug("player_re: " + player_re)
|
||||
|
@ -62,32 +85,43 @@ 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_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_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_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_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):
|
||||
return [["ring", "hold", "nl"],
|
||||
["ring", "hold", "pl"],
|
||||
["ring", "hold", "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):
|
||||
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)
|
||||
if not m:
|
||||
return None
|
||||
|
||||
mg = m.groupdict()
|
||||
|
||||
# translations from captured groups to our info strings
|
||||
# translations from captured groups to fpdb info strings
|
||||
limits = { 'No Limit':'nl', 'Pot Limit':'pl', 'Limit':'fl' }
|
||||
games = { # base, category
|
||||
"Hold'em" : ('hold','holdem'),
|
||||
|
@ -96,9 +130,14 @@ follow : whether to tail -f the input"""
|
|||
'Razz' : ('stud','razz'),
|
||||
'7 Card Stud' : ('stud','studhi'),
|
||||
'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$' }
|
||||
# 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:
|
||||
info['limitType'] = limits[mg['LIMIT']]
|
||||
if 'GAME' in mg:
|
||||
|
@ -109,8 +148,13 @@ follow : whether to tail -f the input"""
|
|||
info['bb'] = mg['BB']
|
||||
if 'CURRENCY' in mg:
|
||||
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
|
||||
|
||||
|
||||
|
@ -119,14 +163,13 @@ follow : whether to tail -f the input"""
|
|||
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))
|
||||
# hand.maxseats = int(m2.group(1))
|
||||
else:
|
||||
pass # throw an exception here, eh?
|
||||
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())
|
||||
# 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:
|
||||
|
@ -143,6 +186,22 @@ follow : whether to tail -f the input"""
|
|||
hand.tablename = info[key]
|
||||
if key == 'BUTTON':
|
||||
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):
|
||||
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):
|
||||
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'))
|
||||
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):
|
||||
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'))
|
||||
|
||||
def readDrawCards(self, hand, street):
|
||||
logging.debug("readDrawCards")
|
||||
# streets PREFLOP, PREDRAW, and THIRD are special cases beacause
|
||||
# we need to grab hero's cards
|
||||
for street in ('PREFLOP', 'DEAL'):
|
||||
if street in hand.streets.keys():
|
||||
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)
|
||||
for found in m:
|
||||
# if m == None:
|
||||
# hand.involved = False
|
||||
# else:
|
||||
hand.hero = found.group('PNAME')
|
||||
newcards = found.group('NEWCARDS').split(' ')
|
||||
hand.addHoleCards(street, hand.hero, closed=newcards, shown=False, mucked=False, dealt=True)
|
||||
|
||||
|
||||
def readStudPlayerCards(self, hand, street):
|
||||
# See comments of reference implementation in FullTiltToFpdb.py
|
||||
logging.debug("readStudPlayerCards")
|
||||
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 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)
|
||||
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(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 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:
|
||||
acts = action.groupdict()
|
||||
if action.group('ATYPE') == ' raises':
|
||||
hand.addRaiseBy( street, action.group('PNAME'), action.group('BET') )
|
||||
elif action.group('ATYPE') == ' calls':
|
||||
|
@ -294,7 +390,7 @@ follow : whether to tail -f the input"""
|
|||
elif action.group('ATYPE') == ' checks':
|
||||
hand.addCheck( street, action.group('PNAME'))
|
||||
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':
|
||||
hand.addStandsPat( street, action.group('PNAME'))
|
||||
else:
|
||||
|
@ -302,9 +398,9 @@ follow : whether to tail -f the input"""
|
|||
|
||||
|
||||
def readShowdownActions(self, hand):
|
||||
# TODO: pick up mucks also??
|
||||
for shows in self.re_ShowdownAction.finditer(hand.handText):
|
||||
cards = shows.group('CARDS')
|
||||
cards = set(cards.split(' '))
|
||||
cards = shows.group('CARDS').split(' ')
|
||||
hand.addShownCards(cards, shows.group('PNAME'))
|
||||
|
||||
def readCollectPot(self,hand):
|
||||
|
@ -315,12 +411,17 @@ follow : whether to tail -f the input"""
|
|||
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'))
|
||||
cards = cards.split(' ') # needs to be a list, not a set--stud needs the order
|
||||
|
||||
(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__":
|
||||
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("-f", "--follow", dest="follow", help="follow (tail -f) the input", action="store_true", default=False)
|
||||
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:
|
||||
# 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)
|
||||
# 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
|
||||
|
@ -50,16 +52,34 @@
|
|||
# pyGTK modules
|
||||
import pygtk
|
||||
import gtk
|
||||
import re
|
||||
|
||||
# FreePokerTools modules
|
||||
import Configuration
|
||||
import Database
|
||||
|
||||
|
||||
re_Places = re.compile("_[0-9]$")
|
||||
re_Percent = re.compile("%$")
|
||||
|
||||
|
||||
def do_tip(widget, tip):
|
||||
widget.set_tooltip_text(tip)
|
||||
|
||||
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:
|
||||
# 0 - The stat, raw, no formating, eg 0.33333333
|
||||
|
@ -108,26 +128,6 @@ def vpip(stat_dict, player):
|
|||
'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):
|
||||
""" Preflop (3rd street) raise."""
|
||||
stat = 0.0
|
||||
|
@ -149,27 +149,6 @@ def pfr(stat_dict, player):
|
|||
'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):
|
||||
""" Went to SD when saw flop/4th."""
|
||||
stat = 0.0
|
||||
|
@ -191,27 +170,6 @@ def wtsd(stat_dict, player):
|
|||
'% 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):
|
||||
""" Won $ at showdown."""
|
||||
stat = 0.0
|
||||
|
@ -233,28 +191,7 @@ def wmsd(stat_dict, player):
|
|||
'% won money at showdown'
|
||||
)
|
||||
|
||||
def wmsd_0(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):
|
||||
def profit100(stat_dict, player):
|
||||
""" Profit won per 100 hands (no decimal places)."""
|
||||
stat = 0.0
|
||||
try:
|
||||
|
@ -356,27 +293,6 @@ def steal(stat_dict, player):
|
|||
except:
|
||||
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):
|
||||
""" Folded SB to steal."""
|
||||
stat = 0.0
|
||||
|
@ -417,27 +333,7 @@ def f_BB_steal(stat_dict, player):
|
|||
'(0/0)',
|
||||
'% folded BB to steal')
|
||||
|
||||
def f_BB_steal_0(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):
|
||||
def three_B(stat_dict, player):
|
||||
""" Three bet preflop/3rd."""
|
||||
stat = 0.0
|
||||
try:
|
||||
|
@ -479,7 +375,7 @@ def WMsF(stat_dict, player):
|
|||
'% won$/saw flop/4th'
|
||||
)
|
||||
|
||||
def a_freq_1(stat_dict, player):
|
||||
def a_freq1(stat_dict, player):
|
||||
""" Flop/4th aggression frequency."""
|
||||
stat = 0.0
|
||||
try:
|
||||
|
@ -500,7 +396,7 @@ def a_freq_1(stat_dict, player):
|
|||
'Aggression Freq flop/4th'
|
||||
)
|
||||
|
||||
def a_freq_2(stat_dict, player):
|
||||
def a_freq2(stat_dict, player):
|
||||
""" Turn/5th aggression frequency."""
|
||||
stat = 0.0
|
||||
try:
|
||||
|
@ -521,7 +417,7 @@ def a_freq_2(stat_dict, player):
|
|||
'Aggression Freq turn/5th'
|
||||
)
|
||||
|
||||
def a_freq_3(stat_dict, player):
|
||||
def a_freq3(stat_dict, player):
|
||||
""" River/6th aggression frequency."""
|
||||
stat = 0.0
|
||||
try:
|
||||
|
@ -542,7 +438,7 @@ def a_freq_3(stat_dict, player):
|
|||
'Aggression Freq river/6th'
|
||||
)
|
||||
|
||||
def a_freq_4(stat_dict, player):
|
||||
def a_freq4(stat_dict, player):
|
||||
""" 7th street aggression frequency."""
|
||||
stat = 0.0
|
||||
try:
|
||||
|
@ -591,34 +487,7 @@ def a_freq_123(stat_dict, player):
|
|||
'Post-Flop Aggression Freq'
|
||||
)
|
||||
|
||||
def a_freq_123_0(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):
|
||||
def cb1(stat_dict, player):
|
||||
""" Flop continuation bet."""
|
||||
stat = 0.0
|
||||
try:
|
||||
|
@ -639,7 +508,7 @@ def cb_1(stat_dict, player):
|
|||
'% continuation bet flop/4th'
|
||||
)
|
||||
|
||||
def cb_2(stat_dict, player):
|
||||
def cb2(stat_dict, player):
|
||||
""" Turn continuation bet."""
|
||||
stat = 0.0
|
||||
try:
|
||||
|
@ -660,7 +529,7 @@ def cb_2(stat_dict, player):
|
|||
'% continuation bet turn/5th'
|
||||
)
|
||||
|
||||
def cb_3(stat_dict, player):
|
||||
def cb3(stat_dict, player):
|
||||
""" River continuation bet."""
|
||||
stat = 0.0
|
||||
try:
|
||||
|
@ -681,7 +550,7 @@ def cb_3(stat_dict, player):
|
|||
'% continuation bet river/6th'
|
||||
)
|
||||
|
||||
def cb_4(stat_dict, player):
|
||||
def cb4(stat_dict, player):
|
||||
""" 7th street continuation bet."""
|
||||
stat = 0.0
|
||||
try:
|
||||
|
@ -702,7 +571,7 @@ def cb_4(stat_dict, player):
|
|||
'% continuation bet 7th'
|
||||
)
|
||||
|
||||
def ffreq_1(stat_dict, player):
|
||||
def ffreq1(stat_dict, player):
|
||||
""" Flop/4th fold frequency."""
|
||||
stat = 0.0
|
||||
try:
|
||||
|
@ -723,7 +592,7 @@ def ffreq_1(stat_dict, player):
|
|||
'% fold frequency flop/4th'
|
||||
)
|
||||
|
||||
def ffreq_2(stat_dict, player):
|
||||
def ffreq2(stat_dict, player):
|
||||
""" Turn/5th fold frequency."""
|
||||
stat = 0.0
|
||||
try:
|
||||
|
@ -744,7 +613,7 @@ def ffreq_2(stat_dict, player):
|
|||
'% fold frequency turn/5th'
|
||||
)
|
||||
|
||||
def ffreq_3(stat_dict, player):
|
||||
def ffreq3(stat_dict, player):
|
||||
""" River/6th fold frequency."""
|
||||
stat = 0.0
|
||||
try:
|
||||
|
@ -765,7 +634,7 @@ def ffreq_3(stat_dict, player):
|
|||
'% fold frequency river/6th'
|
||||
)
|
||||
|
||||
def ffreq_4(stat_dict, player):
|
||||
def ffreq4(stat_dict, player):
|
||||
""" 7th fold frequency."""
|
||||
stat = 0.0
|
||||
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 = 'wmsd')
|
||||
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_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 = '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_freq_2')
|
||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_3')
|
||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_4')
|
||||
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 = 'three_B_0')
|
||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'WMsF')
|
||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq1')
|
||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq2')
|
||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq3')
|
||||
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_0')
|
||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb_1')
|
||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb_2')
|
||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb_3')
|
||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb_4')
|
||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq_1')
|
||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq_2')
|
||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq_3')
|
||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq_4')
|
||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb1')
|
||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb2')
|
||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb3')
|
||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb4')
|
||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq1')
|
||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq2')
|
||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq3')
|
||||
print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq4')
|
||||
print "\n"
|
||||
|
||||
print "\n\nLegal stats:"
|
||||
|
|
|
@ -231,7 +231,12 @@ def discover_nt_by_name(c, tablename):
|
|||
titles = {}
|
||||
win32gui.EnumWindows(win_enum_handler, titles)
|
||||
for hwnd in titles:
|
||||
#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 '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
|
||||
|
@ -364,7 +369,7 @@ def clean_title(name):
|
|||
for pattern in [' \(6 max\)', ' \(heads up\)', ' \(deep\)',
|
||||
' \(deep hu\)', ' \(deep 6\)', ' \(2\)',
|
||||
' \(edu\)', ' \(edu, 6 max\)', ' \(6\)',
|
||||
' \(speed\)',
|
||||
' \(speed\)', 'special', 'newVPP',
|
||||
' no all-in', ' fast', ',', ' 50BB min', '50bb min', '\s+$']:
|
||||
name = re.sub(pattern, '', name)
|
||||
name = name.rstrip()
|
||||
|
|
|
@ -33,12 +33,12 @@ class UltimateBet(HandHistoryConverter):
|
|||
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]+)')
|
||||
|
||||
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)
|
||||
out_path (default '-' = sys.stdout)
|
||||
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")
|
||||
self.filetype = "text"
|
||||
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)
|
152
pyfpdb/fpdb.py
152
pyfpdb/fpdb.py
|
@ -17,26 +17,26 @@
|
|||
|
||||
import os
|
||||
import sys
|
||||
from optparse import OptionParser
|
||||
|
||||
|
||||
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")
|
||||
(options, sys.argv) = parser.parse_args()
|
||||
import threading
|
||||
import Options
|
||||
import string
|
||||
cl_options = string.join(sys.argv[1:])
|
||||
(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 logging
|
||||
|
||||
import pygtk
|
||||
pygtk.require('2.0')
|
||||
import gtk
|
||||
|
||||
import fpdb_db
|
||||
import interlocks
|
||||
|
||||
|
||||
import fpdb_simple
|
||||
import GuiBulkImport
|
||||
import GuiPlayerStats
|
||||
|
@ -45,6 +45,8 @@ import GuiTableViewer
|
|||
import GuiAutoImport
|
||||
import GuiGraphViewer
|
||||
import GuiSessionViewer
|
||||
import SQL
|
||||
import Database
|
||||
import FpdbSQLQueries
|
||||
import Configuration
|
||||
|
||||
|
@ -117,11 +119,13 @@ class fpdb:
|
|||
def dia_create_del_database(self, widget, data=None):
|
||||
print "todo: implement dia_create_del_database"
|
||||
self.obtain_global_lock()
|
||||
self.release_global_lock()
|
||||
#end def dia_create_del_database
|
||||
|
||||
def dia_create_del_user(self, widget, data=None):
|
||||
print "todo: implement dia_create_del_user"
|
||||
self.obtain_global_lock()
|
||||
self.release_global_lock()
|
||||
#end def dia_create_del_user
|
||||
|
||||
def dia_database_stats(self, widget, data=None):
|
||||
|
@ -130,7 +134,7 @@ class fpdb:
|
|||
#end def dia_database_stats
|
||||
|
||||
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)
|
||||
sessions_tab=new_sessions_thread.get_vbox()
|
||||
self.add_and_display_tab(sessions_tab, "Sessions")
|
||||
|
@ -138,16 +142,19 @@ class fpdb:
|
|||
def dia_delete_db_parts(self, widget, data=None):
|
||||
print "todo: implement dia_delete_db_parts"
|
||||
self.obtain_global_lock()
|
||||
self.release_global_lock()
|
||||
#end def dia_delete_db_parts
|
||||
|
||||
def dia_edit_profile(self, widget=None, data=None, create_default=False, path=None):
|
||||
print "todo: implement dia_edit_profile"
|
||||
self.obtain_global_lock()
|
||||
self.release_global_lock()
|
||||
#end def dia_edit_profile
|
||||
|
||||
def dia_export_db(self, widget, data=None):
|
||||
print "todo: implement dia_export_db"
|
||||
self.obtain_global_lock()
|
||||
self.release_global_lock()
|
||||
#end def dia_export_db
|
||||
|
||||
def dia_get_db_root_credentials(self):
|
||||
|
@ -173,6 +180,7 @@ class fpdb:
|
|||
def dia_import_db(self, widget, data=None):
|
||||
print "todo: implement dia_import_db"
|
||||
self.obtain_global_lock()
|
||||
self.release_global_lock()
|
||||
#end def dia_import_db
|
||||
|
||||
def dia_licensing(self, widget, data=None):
|
||||
|
@ -181,19 +189,23 @@ class fpdb:
|
|||
|
||||
def dia_load_profile(self, widget, data=None):
|
||||
"""Dialogue to select a file to load a profile from"""
|
||||
if self.obtain_global_lock() == 0: # returns 0 if successful
|
||||
try:
|
||||
chooser = gtk.FileChooserDialog(title="Please select a profile file to load",
|
||||
action=gtk.FILE_CHOOSER_ACTION_OPEN,
|
||||
buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
|
||||
chooser.set_filename(self.profile)
|
||||
if self.obtain_global_lock(): # returns true if successful
|
||||
#try:
|
||||
# chooser = gtk.FileChooserDialog(title="Please select a profile file to load",
|
||||
# action=gtk.FILE_CHOOSER_ACTION_OPEN,
|
||||
# buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
|
||||
# chooser.set_filename(self.profile)
|
||||
|
||||
response = chooser.run()
|
||||
chooser.destroy()
|
||||
if response == gtk.RESPONSE_OK:
|
||||
self.load_profile(chooser.get_filename())
|
||||
elif response == gtk.RESPONSE_CANCEL:
|
||||
print 'User cancelled loading profile'
|
||||
# response = chooser.run()
|
||||
# chooser.destroy()
|
||||
# if response == gtk.RESPONSE_OK:
|
||||
# self.load_profile(chooser.get_filename())
|
||||
# elif response == gtk.RESPONSE_CANCEL:
|
||||
# print 'User cancelled loading profile'
|
||||
#except:
|
||||
# pass
|
||||
try:
|
||||
self.load_profile()
|
||||
except:
|
||||
pass
|
||||
self.release_global_lock()
|
||||
|
@ -201,39 +213,41 @@ class fpdb:
|
|||
|
||||
def dia_recreate_tables(self, widget, data=None):
|
||||
"""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:
|
||||
dia_confirm = gtk.MessageDialog(parent=None, flags=0, type=gtk.MESSAGE_WARNING,
|
||||
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
|
||||
|
||||
response = dia_confirm.run()
|
||||
dia_confirm.destroy()
|
||||
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
|
||||
# than lock all the other tables
|
||||
# ToDo: lock all other tables so that lock doesn't have to be released
|
||||
self.release_global_lock()
|
||||
lock_released = True
|
||||
self.db.recreate_tables()
|
||||
else:
|
||||
# self.release_global_lock()
|
||||
# lock_released = True
|
||||
self.db.fdb.recreate_tables()
|
||||
#else:
|
||||
# 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:
|
||||
print 'User cancelled recreating tables'
|
||||
except:
|
||||
pass
|
||||
if not lock_released:
|
||||
#if not lock_released:
|
||||
self.release_global_lock()
|
||||
#end def dia_recreate_tables
|
||||
|
||||
def dia_regression_test(self, widget, data=None):
|
||||
print "todo: implement dia_regression_test"
|
||||
self.obtain_global_lock()
|
||||
self.release_global_lock()
|
||||
#end def dia_regression_test
|
||||
|
||||
def dia_save_profile(self, widget, data=None):
|
||||
|
@ -350,28 +364,27 @@ class fpdb:
|
|||
|
||||
def load_profile(self):
|
||||
"""Loads profile from the provided path name."""
|
||||
self.config = Configuration.Config(file=options.config, dbname=options.dbname)
|
||||
self.settings = {}
|
||||
self.settings['global_lock'] = self.lock
|
||||
if (os.sep=="/"):
|
||||
self.settings['os']="linuxmac"
|
||||
else:
|
||||
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_tv_parameters())
|
||||
self.settings.update(self.config.get_import_parameters())
|
||||
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 = fpdb_db.fpdb_db()
|
||||
#print "end of fpdb.load_profile, databaseName:",self.settings['db-databaseName']
|
||||
self.db.connect(self.settings['db-backend'],
|
||||
self.settings['db-host'],
|
||||
self.settings['db-databaseName'],
|
||||
self.settings['db-user'],
|
||||
self.settings['db-password'])
|
||||
if self.db.wrongDbVersion:
|
||||
self.sql = SQL.Sql(type = self.settings['db-type'], db_server = self.settings['db-server'])
|
||||
self.db = Database.Database(self.config, sql = self.sql)
|
||||
|
||||
if self.db.fdb.wrongDbVersion:
|
||||
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.")
|
||||
|
@ -389,9 +402,16 @@ class fpdb:
|
|||
response = diaDbVersionWarning.run()
|
||||
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
|
||||
self.querydict = FpdbSQLQueries.FpdbSQLQueries(self.db.get_backend_name())
|
||||
self.db.db.rollback()
|
||||
self.db.connection.rollback()
|
||||
#end def load_profile
|
||||
|
||||
def not_implemented(self, widget, data=None):
|
||||
|
@ -399,17 +419,18 @@ class fpdb:
|
|||
#end def not_implemented
|
||||
|
||||
def obtain_global_lock(self):
|
||||
print "\nTaking global lock ..."
|
||||
self.fdb_lock = fpdb_db.fpdb_db()
|
||||
self.fdb_lock.connect(self.settings['db-backend'],
|
||||
self.settings['db-host'],
|
||||
self.settings['db-databaseName'],
|
||||
self.settings['db-user'],
|
||||
self.settings['db-password'])
|
||||
return self.fdb_lock.get_global_lock()
|
||||
ret = self.lock.acquire(False) # will return false if lock is already held
|
||||
if ret:
|
||||
print "\nGlobal lock taken ..."
|
||||
else:
|
||||
print "\nFailed to get global lock."
|
||||
return ret
|
||||
# need to release it later:
|
||||
# self.lock.release()
|
||||
|
||||
#end def obtain_global_lock
|
||||
|
||||
def quit(self, widget, data):
|
||||
def quit(self, widget, data=None):
|
||||
print "Quitting normally"
|
||||
#check if current settings differ from profile, if so offer to save or abort
|
||||
self.db.disconnect()
|
||||
|
@ -417,9 +438,8 @@ class fpdb:
|
|||
#end def quit_cliecked
|
||||
|
||||
def release_global_lock(self):
|
||||
self.fdb_lock.db.rollback()
|
||||
self.fdb_lock.disconnect()
|
||||
print "Global lock released."
|
||||
self.lock.release()
|
||||
print "Global lock released.\n"
|
||||
#end def release_global_lock
|
||||
|
||||
def tab_abbreviations(self, widget, data=None):
|
||||
|
@ -444,13 +464,13 @@ class fpdb:
|
|||
#end def tab_bulk_import
|
||||
|
||||
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)
|
||||
ps_tab=new_ps_thread.get_vbox()
|
||||
self.add_and_display_tab(ps_tab, "Player Stats")
|
||||
|
||||
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)
|
||||
ps_tab=new_ps_thread.get_vbox()
|
||||
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):
|
||||
"""opens a table viewer tab"""
|
||||
#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)
|
||||
tv_tab=new_tv_thread.get_vbox()
|
||||
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):
|
||||
"""opens a graph viewer tab"""
|
||||
#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)
|
||||
gv_tab=new_gv_thread.get_vbox()
|
||||
self.add_and_display_tab(gv_tab, "Graphs")
|
||||
#end def tabGraphViewer
|
||||
|
||||
def __init__(self):
|
||||
self.threads=[]
|
||||
self.db=None
|
||||
self.config = Configuration.Config(dbname=options.dbname)
|
||||
self.load_profile()
|
||||
self.threads = []
|
||||
# no more than 1 process can this lock at a time:
|
||||
self.lock = interlocks.InterProcessLock(name="fpdb_global_lock")
|
||||
self.db = None
|
||||
self.status_bar = None
|
||||
|
||||
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
|
||||
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.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.load_profile()
|
||||
sys.stderr.write("fpdb starting ...")
|
||||
#end def __init__
|
||||
|
||||
|
|
|
@ -18,20 +18,21 @@
|
|||
import os
|
||||
import re
|
||||
import sys
|
||||
import logging
|
||||
from time import time, strftime
|
||||
|
||||
import fpdb_simple
|
||||
import FpdbSQLQueries
|
||||
|
||||
class fpdb_db:
|
||||
MYSQL_INNODB = 2
|
||||
PGSQL = 3
|
||||
SQLITE = 4
|
||||
def __init__(self):
|
||||
"""Simple constructor, doesnt really do anything"""
|
||||
self.db = None
|
||||
self.cursor = None
|
||||
self.sql = {}
|
||||
self.MYSQL_INNODB = 2
|
||||
self.PGSQL = 3
|
||||
self.SQLITE = 4
|
||||
|
||||
# Data Structures for index and foreign key creation
|
||||
# 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':'TourneyTypes', 'col':'siteId', 'drop':0}
|
||||
]
|
||||
, [ # indexes for sqlite (list index 4)
|
||||
]
|
||||
]
|
||||
|
||||
self.foreignKeys = [
|
||||
|
@ -146,12 +149,12 @@ class fpdb_db:
|
|||
self.settings = {}
|
||||
self.settings['os'] = "linuxmac" if os.name != "nt" else "windows"
|
||||
|
||||
self.settings.update(config.get_db_parameters())
|
||||
self.connect(self.settings['db-backend'],
|
||||
self.settings['db-host'],
|
||||
self.settings['db-databaseName'],
|
||||
self.settings['db-user'],
|
||||
self.settings['db-password'])
|
||||
db = config.get_db_parameters()
|
||||
self.connect(backend=db['db-backend'],
|
||||
host=db['db-host'],
|
||||
database=db['db-databaseName'],
|
||||
user=db['db-user'],
|
||||
password=db['db-password'])
|
||||
#end def do_connect
|
||||
|
||||
def connect(self, backend=None, host=None, database=None,
|
||||
|
@ -164,13 +167,13 @@ class fpdb_db:
|
|||
self.user=user
|
||||
self.password=password
|
||||
self.database=database
|
||||
if backend==self.MYSQL_INNODB:
|
||||
if backend==fpdb_db.MYSQL_INNODB:
|
||||
import MySQLdb
|
||||
try:
|
||||
self.db = MySQLdb.connect(host = host, user = user, passwd = password, db = database, use_unicode=True)
|
||||
except:
|
||||
raise fpdb_simple.FpdbError("MySQL connection failed")
|
||||
elif backend==self.PGSQL:
|
||||
elif backend==fpdb_db.PGSQL:
|
||||
import psycopg2
|
||||
import psycopg2.extensions
|
||||
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
|
||||
|
@ -179,19 +182,41 @@ class fpdb_db:
|
|||
# For local domain-socket connections, only DB name is
|
||||
# needed, and everything else is in fact undefined and/or
|
||||
# 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":
|
||||
try:
|
||||
self.db = psycopg2.connect(database = database)
|
||||
else:
|
||||
connected = True
|
||||
except:
|
||||
pass
|
||||
#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:
|
||||
raise fpdb_simple.FpdbError("unrecognised database backend:"+backend)
|
||||
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.
|
||||
self.sql = FpdbSQLQueries.FpdbSQLQueries(self.get_backend_name())
|
||||
self.cursor.execute(self.sql.query['set tx level'])
|
||||
self.wrongDbVersion=False
|
||||
try:
|
||||
self.cursor.execute("SELECT * FROM Settings")
|
||||
|
@ -222,13 +247,14 @@ class fpdb_db:
|
|||
|
||||
def create_tables(self):
|
||||
#todo: should detect and fail gracefully if tables already exist.
|
||||
logging.debug(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['createGametypesTable'])
|
||||
self.cursor.execute(self.sql.query['createPlayersTable'])
|
||||
self.cursor.execute(self.sql.query['createAutoratesTable'])
|
||||
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['createTourneysTable'])
|
||||
self.cursor.execute(self.sql.query['createTourneysPlayersTable'])
|
||||
|
@ -260,8 +286,10 @@ class fpdb_db:
|
|||
for table in tables:
|
||||
self.cursor.execute(self.sql.query['drop_table'] + table[0] + ' cascade')
|
||||
elif(self.get_backend_name() == 'SQLite'):
|
||||
#todo: sqlite version here
|
||||
print "Empty function here"
|
||||
self.cursor.execute(self.sql.query['list_tables'])
|
||||
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()
|
||||
#end def drop_tables
|
||||
|
@ -292,6 +320,8 @@ class fpdb_db:
|
|||
return "MySQL InnoDB"
|
||||
elif self.backend==3:
|
||||
return "PostgreSQL"
|
||||
elif self.backend==4:
|
||||
return "SQLite"
|
||||
else:
|
||||
raise fpdb_simple.FpdbError("invalid backend")
|
||||
#end def get_backend_name
|
||||
|
@ -301,11 +331,15 @@ class fpdb_db:
|
|||
#end def get_db_info
|
||||
|
||||
def fillDefaultData(self):
|
||||
self.cursor.execute("INSERT INTO Settings VALUES (118);")
|
||||
self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'Full Tilt Poker', 'USD');")
|
||||
self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'PokerStars', 'USD');")
|
||||
self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'Everleaf', 'USD');")
|
||||
self.cursor.execute("INSERT INTO Settings (version) VALUES (118);")
|
||||
self.cursor.execute("INSERT INTO Sites (name,currency) VALUES ('Full Tilt Poker', 'USD')")
|
||||
self.cursor.execute("INSERT INTO Sites (name,currency) VALUES ('PokerStars', '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
|
||||
# (siteId,buyin,fee,knockout,rebuyOrAddon) VALUES
|
||||
# (1,0,0,0,?)""",(False,) )
|
||||
#end def fillDefaultData
|
||||
|
||||
def recreate_tables(self):
|
||||
|
@ -538,71 +572,99 @@ class fpdb_db:
|
|||
self.db.set_isolation_level(1) # go back to normal isolation level
|
||||
#end def dropAllIndexes
|
||||
|
||||
def analyzeDB(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):
|
||||
def getLastInsertId(self):
|
||||
if self.backend == self.MYSQL_INNODB:
|
||||
try:
|
||||
self.cursor.execute( "lock tables Hands write" )
|
||||
except:
|
||||
# Table 'fpdb.hands' doesn't exist
|
||||
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)
|
||||
ret = self.db.insert_id()
|
||||
if ret < 1 or ret > 999999999:
|
||||
print "getLastInsertId(): problem fetching insert_id? ret=", ret
|
||||
ret = -1
|
||||
elif self.backend == self.PGSQL:
|
||||
try:
|
||||
self.cursor.execute( "lock table Hands in exclusive mode nowait" )
|
||||
#print "... after lock table, status =", self.cursor.statusmessage
|
||||
except:
|
||||
# relation "hands" does not exist
|
||||
if str(sys.exc_value).find('relation "hands" does not 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)
|
||||
return(0)
|
||||
# some options:
|
||||
# currval(hands_id_seq) - use name of implicit seq here
|
||||
# lastval() - still needs sequences set up?
|
||||
# insert ... returning is useful syntax (but postgres specific?)
|
||||
# see rules (fancy trigger type things)
|
||||
self.cursor.execute ("SELECT lastval()")
|
||||
row = self.cursor.fetchone()
|
||||
if not row:
|
||||
print "getLastInsertId(%s): problem fetching lastval? row=" % seq, row
|
||||
ret = -1
|
||||
else:
|
||||
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):
|
||||
#stores into table hands:
|
||||
self.cursor.execute ("""INSERT INTO Hands
|
||||
(siteHandNo, gametypeId, handStart, seats, tableName, importTime, maxSeats
|
||||
,playersVpi, playersAtStreet1, playersAtStreet2
|
||||
,playersAtStreet3, playersAtStreet4, playersAtShowdown
|
||||
,street0Raises, street1Raises, street2Raises
|
||||
,street3Raises, street4Raises, street1Pot
|
||||
,street2Pot, street3Pot, street4Pot
|
||||
,showdownPot
|
||||
self.cursor.execute ("""INSERT INTO Hands (
|
||||
tablename,
|
||||
sitehandno,
|
||||
gametypeid,
|
||||
handstart,
|
||||
importtime,
|
||||
seats,
|
||||
maxseats,
|
||||
boardcard1,
|
||||
boardcard2,
|
||||
boardcard3,
|
||||
boardcard4,
|
||||
boardcard5,
|
||||
-- texture,
|
||||
playersVpi,
|
||||
playersAtStreet1,
|
||||
playersAtStreet2,
|
||||
playersAtStreet3,
|
||||
playersAtStreet4,
|
||||
playersAtShowdown,
|
||||
street0Raises,
|
||||
street1Raises,
|
||||
street2Raises,
|
||||
street3Raises,
|
||||
street4Raises,
|
||||
-- street1Pot,
|
||||
-- street2Pot,
|
||||
-- street3Pot,
|
||||
-- street4Pot,
|
||||
-- showdownPot
|
||||
)
|
||||
VALUES
|
||||
(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)"""
|
||||
,(p['siteHandNo'], gametype_id, p['handStart'], len(names), p['tableName'], datetime.datetime.today(), p['maxSeats']
|
||||
,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']
|
||||
(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
|
||||
%s, %s, %s, %s, %s, %s, %s)""",
|
||||
(
|
||||
p['tablename'],
|
||||
p['sitehandno'],
|
||||
p['gametypeid'],
|
||||
p['handStart'],
|
||||
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)
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
import os # todo: remove this once import_dir is in fpdb_import
|
||||
import sys
|
||||
from time import time, strftime
|
||||
import logging
|
||||
import traceback
|
||||
import math
|
||||
import datetime
|
||||
|
@ -31,6 +32,7 @@ import re
|
|||
|
||||
import fpdb_simple
|
||||
import fpdb_db
|
||||
import Database
|
||||
import fpdb_parse_logic
|
||||
import Configuration
|
||||
|
||||
|
@ -57,7 +59,8 @@ class Importer:
|
|||
self.settings = settings
|
||||
self.caller = caller
|
||||
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.filelist = {}
|
||||
self.dirlist = {}
|
||||
|
@ -75,9 +78,13 @@ class Importer:
|
|||
self.settings.setdefault("minPrint", 30)
|
||||
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.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
|
||||
def setCallHud(self, value):
|
||||
|
@ -162,12 +169,19 @@ class Importer:
|
|||
|
||||
def runImport(self):
|
||||
""""Run full import on self.filelist."""
|
||||
|
||||
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':
|
||||
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':
|
||||
self.fdb.prepareBulkImport()
|
||||
else:
|
||||
print "No need drop indexes."
|
||||
#print "dropInd =", self.settings['dropIndexes'], " dropHudCache =", self.settings['dropHudCache']
|
||||
totstored = 0
|
||||
totdups = 0
|
||||
totpartial = 0
|
||||
|
@ -183,7 +197,13 @@ class Importer:
|
|||
tottime += ttime
|
||||
if self.settings['dropIndexes'] == 'drop':
|
||||
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)
|
||||
# else: import threaded
|
||||
|
||||
|
@ -202,6 +222,41 @@ class Importer:
|
|||
if self.settings['handsInDB'] > 50000: return "don't 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.
|
||||
def runUpdated(self):
|
||||
#Check for new files in monitored directories
|
||||
|
@ -253,9 +308,7 @@ class Importer:
|
|||
if os.path.isdir(file):
|
||||
self.addToDirList[file] = [site] + [filter]
|
||||
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
|
||||
|
||||
|
@ -270,12 +323,38 @@ class Importer:
|
|||
|
||||
filter_name = filter.replace("ToFpdb", "")
|
||||
|
||||
# Example code for using threads & queues: (maybe for obj and import_fpdb_file??)
|
||||
#def worker():
|
||||
# while True:
|
||||
# item = q.get()
|
||||
# do_work(item)
|
||||
# q.task_done()
|
||||
#
|
||||
#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)
|
||||
if(conv.getStatus()):
|
||||
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:
|
||||
# conversion didn't work
|
||||
# TODO: appropriate response?
|
||||
|
@ -359,8 +438,9 @@ class Importer:
|
|||
self.hand=hand
|
||||
|
||||
try:
|
||||
handsId = fpdb_parse_logic.mainParser(self.settings['db-backend'], self.fdb.db
|
||||
,self.fdb.cursor, self.siteIds[site], category, hand, self.config)
|
||||
handsId = fpdb_parse_logic.mainParser( self.settings, self.fdb
|
||||
, self.siteIds[site], category, hand
|
||||
, self.config, self.database )
|
||||
self.fdb.db.commit()
|
||||
|
||||
stored += 1
|
||||
|
|
|
@ -18,10 +18,16 @@
|
|||
#methods that are specific to holdem but not trivial
|
||||
|
||||
import fpdb_simple
|
||||
import fpdb_save_to_db
|
||||
import Database
|
||||
|
||||
#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])
|
||||
|
||||
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
|
||||
siteHandNo = fpdb_simple.parseSiteHandNo(hand[0])
|
||||
#print "siteHandNo =", siteHandNo
|
||||
handStartTime = fpdb_simple.parseHandStartTime(hand[0])
|
||||
|
||||
isTourney = fpdb_simple.isTourney(hand[0])
|
||||
|
@ -45,7 +52,7 @@ def mainParser(backend, db, cursor, siteID, category, hand, config):
|
|||
break
|
||||
#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:
|
||||
siteTourneyNo = fpdb_simple.parseTourneyNo(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
|
||||
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)
|
||||
|
||||
|
@ -72,7 +79,7 @@ def mainParser(backend, db, cursor, siteID, category, hand, config):
|
|||
seatLines.append(line)
|
||||
|
||||
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)
|
||||
startCashes = tmp['startCashes']
|
||||
seatNos = tmp['seatNos']
|
||||
|
@ -111,7 +118,7 @@ def mainParser(backend, db, cursor, siteID, category, hand, config):
|
|||
tableName = tableResult['tableName']
|
||||
#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.
|
||||
fpdb_simple.convertCardValues(cardValues)
|
||||
if base == "hold":
|
||||
|
@ -119,13 +126,14 @@ def mainParser(backend, db, cursor, siteID, category, hand, config):
|
|||
fpdb_simple.convertBlindBet(actionTypes, actionAmounts)
|
||||
fpdb_simple.checkPositions(positions)
|
||||
|
||||
cursor.execute("SELECT limitType FROM Gametypes WHERE id=%s",(gametypeID, ))
|
||||
limit_type = cursor.fetchone()[0]
|
||||
fdb.cursor.execute("SELECT limitType FROM Gametypes WHERE id=%s",(gametypeID, ))
|
||||
limit_type = fdb.cursor.fetchone()[0]
|
||||
fpdb_simple.convert3B4B(category, limit_type, actionTypes, actionAmounts)
|
||||
|
||||
totalWinnings = sum(winnings)
|
||||
|
||||
# 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":
|
||||
hudImportData = fpdb_simple.generateHudCacheData(playerIDs, base, category, actionTypes
|
||||
, 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)
|
||||
|
||||
if base == "hold":
|
||||
result = fpdb_save_to_db.tourney_holdem_omaha(
|
||||
config, backend, db, cursor, base, category, siteTourneyNo, buyin
|
||||
result = db.tourney_holdem_omaha(
|
||||
config, settings, fdb.db, fdb.cursor, base, category, siteTourneyNo, buyin
|
||||
, fee, knockout, entries, prizepool, tourneyStartTime
|
||||
, payin_amounts, ranks, tourneyTypeId, siteID, siteHandNo
|
||||
, gametypeID, handStartTime, names, playerIDs, startCashes
|
||||
|
@ -149,8 +157,8 @@ def mainParser(backend, db, cursor, siteID, category, hand, config):
|
|||
, winnings, rakes, actionTypes, allIns, actionAmounts
|
||||
, actionNos, hudImportData, maxSeats, tableName, seatNos)
|
||||
elif base == "stud":
|
||||
result = fpdb_save_to_db.tourney_stud(
|
||||
config, backend, db, cursor, base, category, siteTourneyNo
|
||||
result = db.tourney_stud(
|
||||
config, settings, fdb.db, fdb.cursor, base, category, siteTourneyNo
|
||||
, buyin, fee, knockout, entries, prizepool, tourneyStartTime
|
||||
, payin_amounts, ranks, tourneyTypeId, siteID, siteHandNo
|
||||
, gametypeID, handStartTime, names, playerIDs, startCashes
|
||||
|
@ -161,23 +169,23 @@ def mainParser(backend, db, cursor, siteID, category, hand, config):
|
|||
raise fpdb_simple.FpdbError("unrecognised category")
|
||||
else:
|
||||
if base == "hold":
|
||||
result = fpdb_save_to_db.ring_holdem_omaha(
|
||||
config, backend, db, cursor, base, category, siteHandNo
|
||||
result = db.ring_holdem_omaha(
|
||||
config, settings, fdb.db, fdb.cursor, base, category, siteHandNo
|
||||
, gametypeID, handStartTime, names, playerIDs
|
||||
, startCashes, positions, cardValues, cardSuits
|
||||
, boardValues, boardSuits, winnings, rakes
|
||||
, actionTypes, allIns, actionAmounts, actionNos
|
||||
, hudImportData, maxSeats, tableName, seatNos)
|
||||
elif base == "stud":
|
||||
result = fpdb_save_to_db.ring_stud(
|
||||
config, backend, db, cursor, base, category, siteHandNo, gametypeID
|
||||
result = db.ring_stud(
|
||||
config, settings, fdb.db, fdb.cursor, base, category, siteHandNo, gametypeID
|
||||
, handStartTime, names, playerIDs, startCashes, antes
|
||||
, cardValues, cardSuits, winnings, rakes, actionTypes, allIns
|
||||
, actionAmounts, actionNos, hudImportData, maxSeats, tableName
|
||||
, seatNos)
|
||||
else:
|
||||
raise fpdb_simple.FpdbError ("unrecognised category")
|
||||
db.commit()
|
||||
fdb.db.commit()
|
||||
return result
|
||||
#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)
|
||||
#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
|
||||
,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:
|
||||
cursor.execute ("""INSERT INTO Hands
|
||||
(siteHandNo, gametypeId, handStart, seats, tableName, importTime, maxSeats
|
||||
,boardcard1,boardcard2,boardcard3,boardcard4,boardcard5
|
||||
,playersVpi, playersAtStreet1, playersAtStreet2
|
||||
,playersAtStreet3, playersAtStreet4, playersAtShowdown
|
||||
,street0Raises, street1Raises, street2Raises
|
||||
|
@ -1159,9 +1152,11 @@ def storeHands(backend, conn, cursor, site_hand_no, gametype_id
|
|||
,showdownPot
|
||||
)
|
||||
VALUES
|
||||
(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
||||
(%s, %s, %s, %s, %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
|
||||
,cards[0], cards[1], cards[2], cards[3], cards[4]
|
||||
,hudCache['playersVpi'], hudCache['playersAtStreet1'], hudCache['playersAtStreet2']
|
||||
,hudCache['playersAtStreet3'], hudCache['playersAtStreet4'], hudCache['playersAtShowdown']
|
||||
,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
|
||||
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'])
|
||||
for player in xrange(len(playerIds)):
|
||||
|
||||
|
@ -2292,189 +2287,6 @@ VALUES (%s, %s, %s, %s, %s, %s,
|
|||
pass
|
||||
# else:
|
||||
# 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
|
||||
|
||||
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