Merge branch 'master' of git://git.assembla.com/fpdboz

This commit is contained in:
sqlcoder 2009-11-09 20:44:05 +00:00
commit 9bbca552ea
29 changed files with 4197 additions and 3601 deletions

View File

@ -191,7 +191,7 @@ class Betfair(HandHistoryConverter):
elif action.group('ATYPE') == 'checks':
hand.addCheck( street, action.group('PNAME'))
else:
print "DEBUG: unimplemented readAction: '%s' '%s'" %(action.group('PNAME'),action.group('ATYPE'),)
sys.stderr.write( "DEBUG: unimplemented readAction: '%s' '%s'" %(action.group('PNAME'),action.group('ATYPE'),))
def readShowdownActions(self, hand):

View File

@ -67,7 +67,7 @@ class CarbonPoker(HandHistoryConverter):
if(type == "Holdem"):
gametype = gametype + ["hold"]
else:
print "Unknown gametype: '%s'" % (type)
print "Carbon: Unknown gametype: '%s'" % (type)
stakes = desc_node[0].getAttribute("stakes")
#TODO: no examples of anything except nlhe

View File

@ -44,14 +44,35 @@ except ConfigParser.NoSectionError: # debian package path
log = logging.getLogger("config")
log.debug("config logger initialised")
########################################################################
# application wide consts
def fix_tf(x, default = True):
# The xml parser doesn't translate "True" to True. Therefore, we never get
# True or False from the parser only "True" or "False". So translate the
# string to the python boolean representation.
if x == "1" or x == 1 or string.lower(x) == "true" or string.lower(x) == "t":
APPLICATION_NAME_SHORT = 'fpdb'
APPLICATION_VERSION = 'xx.xx.xx'
DIR_SELF = os.path.dirname(os.path.abspath(__file__))
#TODO: imo no good idea to place 'database' in parent dir
DIR_DATABASES = os.path.join(os.path.dirname(DIR_SELF), 'database')
DATABASE_TYPE_POSTGRESQL = 'postgresql'
DATABASE_TYPE_SQLITE = 'sqlite'
DATABASE_TYPE_MYSQL = 'mysql'
DATABASE_TYPES = (
DATABASE_TYPE_POSTGRESQL,
DATABASE_TYPE_SQLITE,
DATABASE_TYPE_MYSQL,
)
########################################################################
def string_to_bool(string, default=True):
"""converts a string representation of a boolean value to boolean True or False
@param string: (str) the string to convert
@param default: value to return if the string can not be converted to a boolean value
"""
string = string.lower()
if string in ('1', 'true', 't'):
return True
if x == "0" or x == 0 or string.lower(x) == "false" or string.lower(x) == "f":
elif string in ('0', 'false', 'f'):
return False
return default
@ -106,29 +127,22 @@ class Site:
self.font = node.getAttribute("font")
self.font_size = node.getAttribute("font_size")
self.use_frames = node.getAttribute("use_frames")
self.enabled = fix_tf(node.getAttribute("enabled"), default = True)
self.enabled = string_to_bool(node.getAttribute("enabled"), default=True)
self.xpad = node.getAttribute("xpad")
self.ypad = node.getAttribute("ypad")
self.layout = {}
print self.site_name, self.HH_path
print "Loading site", self.site_name
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)
self.xpad = 1 if self.xpad == "" else int(self.xpad)
self.ypad = 0 if self.ypad == "" else int(self.ypad)
self.font_size = 7 if self.font_size == "" else int(self.font_size)
self.hudopacity = 1.0 if self.hudopacity == "" else float(self.hudopacity)
if self.use_frames == "": self.use_frames = False
if self.font == "": self.font = "Sans"
@ -208,14 +222,13 @@ class Game:
class Database:
def __init__(self, node):
self.db_name = node.getAttribute("db_name")
self.db_server = node.getAttribute("db_server")
self.db_server = node.getAttribute("db_server").lower()
self.db_ip = node.getAttribute("db_ip")
self.db_user = node.getAttribute("db_user")
self.db_type = node.getAttribute("db_type")
self.db_pass = node.getAttribute("db_pass")
self.db_selected = fix_tf(node.getAttribute("default"),"False")
log.debug("Database db_name:'%(name)s' db_server:'%(server)s' db_ip:'%(ip)s' db_user:'%(user)s' db_type:'%(type)s' db_pass (not logged) selected:'%(sel)s'" \
% { 'name':self.db_name, 'server':self.db_server, 'ip':self.db_ip, 'user':self.db_user, 'type':self.db_type, 'sel':self.db_selected} )
self.db_selected = string_to_bool(node.getAttribute("default"), default=False)
log.debug("Database db_name:'%(name)s' db_server:'%(server)s' db_ip:'%(ip)s' db_user:'%(user)s' db_pass (not logged) selected:'%(sel)s'" \
% { 'name':self.db_name, 'server':self.db_server, 'ip':self.db_ip, 'user':self.db_user, 'sel':self.db_selected} )
def __str__(self):
temp = 'Database = ' + self.db_name + '\n'
@ -277,8 +290,8 @@ class Import:
self.interval = node.getAttribute("interval")
self.callFpdbHud = node.getAttribute("callFpdbHud")
self.hhArchiveBase = node.getAttribute("hhArchiveBase")
self.saveActions = fix_tf(node.getAttribute("saveActions"), True)
self.fastStoreHudCache = fix_tf(node.getAttribute("fastStoreHudCache"), False)
self.saveActions = string_to_bool(node.getAttribute("saveActions"), default=True)
self.fastStoreHudCache = string_to_bool(node.getAttribute("fastStoreHudCache"), default=False)
def __str__(self):
return " interval = %s\n callFpdbHud = %s\n hhArchiveBase = %s\n saveActions = %s\n fastStoreHudCache = %s\n" \
@ -289,14 +302,14 @@ class HudUI:
self.node = node
self.label = node.getAttribute('label')
#
self.aggregate_ring = fix_tf(node.getAttribute('aggregate_ring_game_stats'))
self.aggregate_tour = fix_tf(node.getAttribute('aggregate_tourney_stats'))
self.aggregate_ring = string_to_bool(node.getAttribute('aggregate_ring_game_stats'))
self.aggregate_tour = string_to_bool(node.getAttribute('aggregate_tourney_stats'))
self.hud_style = node.getAttribute('stat_aggregation_range')
self.hud_days = node.getAttribute('aggregation_days')
self.agg_bb_mult = node.getAttribute('aggregation_level_multiplier')
#
self.h_aggregate_ring = fix_tf(node.getAttribute('aggregate_hero_ring_game_stats'))
self.h_aggregate_tour = fix_tf(node.getAttribute('aggregate_hero_tourney_stats'))
self.h_aggregate_ring = string_to_bool(node.getAttribute('aggregate_hero_ring_game_stats'))
self.h_aggregate_tour = string_to_bool(node.getAttribute('aggregate_hero_tourney_stats'))
self.h_hud_style = node.getAttribute('hero_stat_aggregation_range')
self.h_hud_days = node.getAttribute('hero_aggregation_days')
self.h_agg_bb_mult = node.getAttribute('hero_aggregation_level_multiplier')
@ -308,9 +321,9 @@ class HudUI:
class Tv:
def __init__(self, node):
self.combinedStealFold = node.getAttribute("combinedStealFold")
self.combined2B3B = node.getAttribute("combined2B3B")
self.combinedPostflop = node.getAttribute("combinedPostflop")
self.combinedStealFold = string_to_bool(node.getAttribute("combinedStealFold"), default=True)
self.combined2B3B = string_to_bool(node.getAttribute("combined2B3B"), default=True)
self.combinedPostflop = string_to_bool(node.getAttribute("combinedPostflop"), default=True)
def __str__(self):
return (" combinedStealFold = %s\n combined2B3B = %s\n combinedPostflop = %s\n" %
@ -323,22 +336,20 @@ class Config:
# we check the existence of "file" and try to recover if it doesn't exist
self.default_config_path = self.get_default_config_path()
if file != None: # configuration file path has been passed
if file is not None: # config file path passed in
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))
file = None
if file == None: # configuration file path not passed or invalid
if file is None: # configuration file path not passed or invalid
file = self.find_config() #Look for a config file in the normal places
if file == None: # no config file in the normal places
if file is None: # no config file in the normal places
file = self.find_example_config() #Look for an example file to edit
if file != None:
pass
if file == None: # that didn't work either, just die
if file is None: # that didn't work either, just die
print "No HUD_config_xml found after looking in current directory and "+self.default_config_path+"\nExiting"
sys.stderr.write("No HUD_config_xml found after looking in current directory and "+self.default_config_path+"\nExiting")
print "press enter to continue"
@ -367,10 +378,13 @@ class Config:
self.file = file
self.supported_sites = {}
self.supported_games = {}
self.supported_databases = {}
self.supported_databases = {} # databaseName --> Database instance
self.aux_windows = {}
self.hhcs = {}
self.popup_windows = {}
self.db_selected = None # database the user would like to use
self.tv = None
# s_sites = doc.getElementsByTagName("supported_sites")
for site_node in doc.getElementsByTagName("site"):
@ -382,29 +396,26 @@ class Config:
game = Game(node = game_node)
self.supported_games[game.game_name] = game
# parse databases defined by user in the <supported_databases> section
# the user may select the actual database to use via commandline or by setting the selected="bool"
# attribute of the tag. if no database is explicitely selected, we use the first one we come across
# s_dbs = doc.getElementsByTagName("supported_databases")
# select database from those defined in config by:
# 1) command line option
# or 2) selected="True" in config element
# or 3) just choose the first we come across
#TODO: do we want to take all <database> tags or all <database> tags contained in <supported_databases>
# ..this may break stuff for some users. so leave it unchanged for now untill there is a decission
for db_node in doc.getElementsByTagName("database"):
try:
db = Database(node=db_node)
except:
raise FpdbError("Unable to create database object")
else:
if db.db_name in self.supported_databases:
raise FpdbError("Database names must be unique")
# If there is only one Database node, or none are marked
# default, use first
if not self.supported_databases:
raise ValueError("Database names must be unique")
if self.db_selected is None or db.db_selected:
self.db_selected = db.db_name
self.supported_databases[db.db_name] = db
if db.db_selected:
self.db_selected = db.db_name
#TODO: if the user may passes '' (empty string) as database name via command line, his choice is ignored
# ..when we parse the xml we allow for ''. there has to be a decission if to allow '' or not
if dbname and dbname in self.supported_databases:
self.db_selected = dbname
#NOTE: fpdb can not handle the case when no database is defined in xml, so we throw an exception for now
if self.db_selected is None:
raise ValueError('There must be at least one database defined')
# s_dbs = doc.getElementsByTagName("mucked_windows")
for aw_node in doc.getElementsByTagName("aw"):
@ -430,13 +441,12 @@ class Config:
self.ui = hui
for tv_node in doc.getElementsByTagName("tv"):
tv = Tv(node = tv_node)
self.tv = tv
self.tv = Tv(node = tv_node)
db = self.get_db_parameters()
if db['db-password'] == 'YOUR MYSQL PASSWORD':
df_file = self.find_default_conf()
if df_file == None: # this is bad
if df_file is None: # this is bad
pass
else:
df_parms = self.read_default_conf(df_file)
@ -527,7 +537,7 @@ class Config:
def get_layout_node(self, site_node, layout):
for layout_node in site_node.getElementsByTagName("layout"):
if layout_node.getAttribute("max") == None:
if layout_node.getAttribute("max") is None:
return None
if int( layout_node.getAttribute("max") ) == int( layout ):
return layout_node
@ -543,19 +553,21 @@ class Config:
return location_node
def save(self, file = None):
if file != None:
if file is not None:
with open(file, 'w') as f:
self.doc.writexml(f)
else:
shutil.move(self.file, self.file+".backup")
with open(self.file, 'w') as f:
with open(file, 'w') as f:
self.doc.writexml(f)
def edit_layout(self, site_name, max, width = None, height = None,
fav_seat = None, locations = None):
site_node = self.get_site_node(site_name)
layout_node = self.get_layout_node(site_node, max)
if layout_node == None: return
# TODO: how do we support inserting new layouts?
if layout_node is None:
return
for i in range(1, max + 1):
location_node = self.get_location_node(layout_node, i)
location_node.setAttribute("x", str( locations[i-1][0] ))
@ -565,7 +577,7 @@ class Config:
def edit_aux_layout(self, aux_name, max, width = None, height = None, locations = None):
aux_node = self.get_aux_node(aux_name)
layout_node = self.get_layout_node(aux_node, max)
if layout_node == None:
if layout_node is None:
print "aux node not found"
return
print "editing locations =", locations
@ -578,6 +590,13 @@ class Config:
else:
self.aux_windows[aux_name].layout[max].location[i] = ( locations[i][0], locations[i][1] )
#NOTE: we got a nice Database class, so why map it again here?
# user input validation should be done when initializing the Database class. this allows to give appropriate feddback when something goes wrong
# try ..except is evil here. it swallows all kinds of errors. dont do this
# naming database types 2, 3, 4 on the fly is no good idea. i see this all over the code. better use some globally defined consts (see DATABASE_TYPE_*)
# i would like to drop this method entirely and replace it by get_selected_database() or better get_active_database(), returning one of our Database instances
# thus we can drop self.db_selected (holding database name) entirely and replace it with self._active_database = Database, avoiding to define the same
# thing multiple times
def get_db_parameters(self):
db = {}
name = self.db_selected
@ -597,33 +616,31 @@ class Config:
try: db['db-server'] = self.supported_databases[name].db_server
except: pass
try: db['db-type'] = self.supported_databases[name].db_type
except: pass
if string.lower(self.supported_databases[name].db_server) == 'mysql':
if self.supported_databases[name].db_server== DATABASE_TYPE_MYSQL:
db['db-backend'] = 2
elif string.lower(self.supported_databases[name].db_server) == 'postgresql':
elif self.supported_databases[name].db_server== DATABASE_TYPE_POSTGRESQL:
db['db-backend'] = 3
elif string.lower(self.supported_databases[name].db_server) == 'sqlite':
elif self.supported_databases[name].db_server== DATABASE_TYPE_SQLITE:
db['db-backend'] = 4
else: db['db-backend'] = None # this is big trouble
else:
raise ValueError('Unsupported database backend: %s' % self.supported_databases[name].db_server)
return db
def set_db_parameters(self, db_name = 'fpdb', db_ip = None, db_user = None,
db_pass = None, db_server = None, db_type = None):
db_pass = None, db_server = None):
db_node = self.get_db_node(db_name)
if db_node != None:
if db_ip != None: db_node.setAttribute("db_ip", db_ip)
if db_user != None: db_node.setAttribute("db_user", db_user)
if db_pass != None: db_node.setAttribute("db_pass", db_pass)
if db_server != None: db_node.setAttribute("db_server", db_server)
if db_type != None: db_node.setAttribute("db_type", db_type)
if db_ip is not None: db_node.setAttribute("db_ip", db_ip)
if db_user is not None: db_node.setAttribute("db_user", db_user)
if db_pass is not None: db_node.setAttribute("db_pass", db_pass)
if db_server is not None: db_node.setAttribute("db_server", db_server)
if db_type is not None: db_node.setAttribute("db_type", db_type)
if self.supported_databases.has_key(db_name):
if db_ip != None: self.supported_databases[db_name].dp_ip = db_ip
if db_user != None: self.supported_databases[db_name].dp_user = db_user
if db_pass != None: self.supported_databases[db_name].dp_pass = db_pass
if db_server != None: self.supported_databases[db_name].dp_server = db_server
if db_type != None: self.supported_databases[db_name].dp_type = db_type
if db_ip is not None: self.supported_databases[db_name].dp_ip = db_ip
if db_user is not None: self.supported_databases[db_name].dp_user = db_user
if db_pass is not None: self.supported_databases[db_name].dp_pass = db_pass
if db_server is not None: self.supported_databases[db_name].dp_server = db_server
if db_type is not None: self.supported_databases[db_name].dp_type = db_type
return
def getDefaultSite(self):
@ -634,16 +651,13 @@ class Config:
return None
def get_tv_parameters(self):
tv = {}
try: tv['combinedStealFold'] = self.tv.combinedStealFold
except: tv['combinedStealFold'] = True
try: tv['combined2B3B'] = self.tv.combined2B3B
except: tv['combined2B3B'] = True
try: tv['combinedPostflop'] = self.tv.combinedPostflop
except: tv['combinedPostflop'] = True
return tv
if self.tv is not None:
return {
'combinedStealFold': self.tv.combinedStealFold,
'combined2B3B': self.tv.combined2B3B,
'combinedPostflop': self.tv.combinedPostflop
}
return {}
# Allow to change the menu appearance
def get_hud_ui_parameters(self):
@ -742,29 +756,27 @@ class Config:
return colors
def get_default_font(self, site='PokerStars'):
(font, font_size) = ("Sans", "8")
if site not in self.supported_sites:
return ("Sans", "8")
if self.supported_sites[site].font == "":
font = "Sans"
else:
font = self.supported_sites[site].font
if self.supported_sites[site].font_size == "":
font_size = "8"
else:
font_size = self.supported_sites[site].font_size
return (font, font_size)
site = self.supported_sites.get(site, None)
if site is not None:
if site.font:
font = site.font
if site.font_size:
font_size = site.font_size
return font, font_size
def get_locations(self, site = "PokerStars", max = "8"):
try:
locations = self.supported_sites[site].layout[max].location
except:
locations = ( ( 0, 0), (684, 61), (689, 239), (692, 346),
def get_locations(self, site_name="PokerStars", max=8):
site = self.supported_sites.get(site_name, None)
if site is not None:
location = site.layout.get(max, None)
if location is not None:
return location.location
return (
( 0, 0), (684, 61), (689, 239), (692, 346),
(586, 393), (421, 440), (267, 440), ( 0, 361),
( 0, 280), (121, 280), ( 46, 30) )
return locations
( 0, 280), (121, 280), ( 46, 30)
)
def get_aux_locations(self, aux = "mucked", max = "9"):
@ -778,12 +790,10 @@ class Config:
def get_supported_sites(self, all=False):
"""Returns the list of supported sites."""
the_sites = []
for site in self.supported_sites.keys():
params = self.get_site_parameters(site)
if all or params['enabled']:
the_sites.append(site)
return the_sites
if all:
return self.supported_sites.keys()
else:
return [site_name for (site_name, site) in self.supported_sites.items() if site.enabled]
def get_site_parameters(self, site):
"""Returns a dict of the site parameters for the specified site"""
@ -814,27 +824,24 @@ class Config:
font = None, font_size = None):
"""Sets the specified site parameters for the specified site."""
site_node = self.get_site_node(site_name)
if db_node != None:
if converter != None: site_node.setAttribute("converter", converter)
if decoder != None: site_node.setAttribute("decoder", decoder)
if hudbgcolor != None: site_node.setAttribute("hudbgcolor", hudbgcolor)
if hudfgcolor != None: site_node.setAttribute("hudfgcolor", hudfgcolor)
if hudopacity != None: site_node.setAttribute("hudopacity", hudopacity)
if screen_name != None: site_node.setAttribute("screen_name", screen_name)
if site_path != None: site_node.setAttribute("site_path", site_path)
if table_finder != None: site_node.setAttribute("table_finder", table_finder)
if HH_path != None: site_node.setAttribute("HH_path", HH_path)
if enabled != None: site_node.setAttribute("enabled", enabled)
if font != None: site_node.setAttribute("font", font)
if font_size != None: site_node.setAttribute("font_size", font_size)
if db_node is not None:
if converter is not None: site_node.setAttribute("converter", converter)
if decoder is not None: site_node.setAttribute("decoder", decoder)
if hudbgcolor is not None: site_node.setAttribute("hudbgcolor", hudbgcolor)
if hudfgcolor is not None: site_node.setAttribute("hudfgcolor", hudfgcolor)
if hudopacity is not None: site_node.setAttribute("hudopacity", hudopacity)
if screen_name is not None: site_node.setAttribute("screen_name", screen_name)
if site_path is not None: site_node.setAttribute("site_path", site_path)
if table_finder is not None: site_node.setAttribute("table_finder", table_finder)
if HH_path is not None: site_node.setAttribute("HH_path", HH_path)
if enabled is not None: site_node.setAttribute("enabled", enabled)
if font is not None: site_node.setAttribute("font", font)
if font_size is not None: site_node.setAttribute("font_size", font_size)
return
def get_aux_windows(self):
"""Gets the list of mucked window formats in the configuration."""
mw = []
for w in self.aux_windows.keys():
mw.append(w)
return mw
return self.aux_windows.keys()
def get_aux_parameters(self, name):
"""Gets a dict of mucked window parameters from the named mw."""

View File

@ -205,7 +205,7 @@ class Database:
# where possible avoid creating new SQL instance by using the global one passed in
if sql is None:
self.sql = SQL.Sql(type = self.type, db_server = self.db_server)
self.sql = SQL.Sql(db_server = self.db_server)
else:
self.sql = sql
@ -249,7 +249,6 @@ class Database:
db_params = c.get_db_parameters()
self.import_options = c.get_import_parameters()
self.type = db_params['db-type']
self.backend = db_params['db-backend']
self.db_server = db_params['db-server']
self.database = db_params['db-databaseName']
@ -292,6 +291,21 @@ class Database:
row = c.fetchone()
return row
def get_table_info(self, hand_id):
c = self.connection.cursor()
c.execute(self.sql.query['get_table_name'], (hand_id, ))
row = c.fetchone()
l = list(row)
if row[3] == "ring": # cash game
l.append(None)
l.append(None)
return l
else: # tournament
tour_no, tab_no = re.split(" ", row[0])
l.append(tour_no)
l.append(tab_no)
return l
def get_last_hand(self):
c = self.connection.cursor()
c.execute(self.sql.query['get_last_hand'])
@ -353,7 +367,7 @@ class Database:
# else:
# cards += ranks[d['card' + str(i) + 'Value']] + d['card' +str(i) + 'Suit']
cv = "card%dvalue" % i
if cv not in d or d[cv] == None:
if cv not in d or d[cv] is None:
break
elif d[cv] == 0:
cards += "xx"
@ -395,7 +409,7 @@ class Database:
row = c.fetchone()
except: # TODO: what error is a database error?!
err = traceback.extract_tb(sys.exc_info()[2])[-1]
print "*** Error: " + err[2] + "(" + str(err[1]) + "): " + str(sys.exc_info()[1])
print "*** Database Error: " + err[2] + "(" + str(err[1]) + "): " + str(sys.exc_info()[1])
else:
if row and row[0]:
self.hand_1day_ago = int(row[0])
@ -421,10 +435,10 @@ class Database:
if row and row[0]:
self.date_nhands_ago[str(playerid)] = row[0]
c.close()
print "date n hands ago = " + self.date_nhands_ago[str(playerid)] + "(playerid "+str(playerid)+")"
print "Database: date n hands ago = " + self.date_nhands_ago[str(playerid)] + "(playerid "+str(playerid)+")"
except:
err = traceback.extract_tb(sys.exc_info()[2])[-1]
print "***Error: "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1])
print "*** Database Error: "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1])
def get_stats_from_hand( self, hand, type # type is "ring" or "tour"
, hud_params = {'aggregate_tour':False, 'aggregate_ring':False, 'hud_style':'A', 'hud_days':30, 'agg_bb_mult':100
@ -551,7 +565,7 @@ class Database:
def get_player_names(self, config, site_id=None, like_player_name="%"):
"""Fetch player names from players. Use site_id and like_player_name if provided"""
if site_id == None:
if site_id is None:
site_id = -1
c = self.get_cursor()
c.execute(self.sql.query['get_player_names'], (like_player_name, site_id, site_id))
@ -657,7 +671,7 @@ class Database:
except:
ret = -1
err = traceback.extract_tb(sys.exc_info()[2])
print "***get_last_insert_id error: " + str(sys.exc_info()[1])
print "*** Database get_last_insert_id error: " + str(sys.exc_info()[1])
print "\n".join( [e[0]+':'+str(e[1])+" "+e[2] for e in err] )
raise
return ret
@ -761,14 +775,18 @@ class Database:
hands_id = self.storeHands( self.backend, siteHandNo, gametypeId
, handStartTime, names, tableName, maxSeats
, hudImportData, board_values, board_suits )
, hudImportData, (None, None, None, None, None), (None, None, None, None, None) )
# changed board_values and board_suits to arrays of None, just like the
# cash game version of this function does - i don't believe this to be
# the correct thing to do (tell me if i'm wrong) but it should keep the
# importer from crashing
hands_players_ids = self.store_hands_players_stud_tourney(self.backend, hands_id
, playerIds, startCashes, antes, cardValues, cardSuits
, winnings, rakes, seatNos, tourneys_players_ids, tourneyTypeId)
if 'dropHudCache' not in settings or settings['dropHudCache'] != 'drop':
self.storeHudCache(self.backend, base, category, gametypeId, hand_start_time, playerIds, hudImportData)
self.storeHudCache(self.backend, base, category, gametypeId, handStartTime, playerIds, hudImportData)
return hands_id
#end def tourney_stud
@ -1179,7 +1197,7 @@ class Database:
if p_id:
self.hero_ids[site_id] = int(p_id)
if start == None:
if start is None:
start = self.hero_hudstart_def
if self.hero_ids == {}:
where = ""
@ -1375,6 +1393,12 @@ class Database:
pids[p],
pdata[p]['startCash'],
pdata[p]['seatNo'],
pdata[p]['winnings'],
pdata[p]['street0VPI'],
pdata[p]['street1Seen'],
pdata[p]['street2Seen'],
pdata[p]['street3Seen'],
pdata[p]['street4Seen'],
pdata[p]['street0Aggr'],
pdata[p]['street1Aggr'],
pdata[p]['street2Aggr'],
@ -1387,6 +1411,12 @@ class Database:
playerId,
startCash,
seatNo,
winnings,
street0VPI,
street1Seen,
street2Seen,
street3Seen,
street4Seen,
street0Aggr,
street1Aggr,
street2Aggr,
@ -1395,7 +1425,8 @@ class Database:
)
VALUES (
%s, %s, %s, %s, %s,
%s, %s, %s, %s
%s, %s, %s, %s, %s,
%s, %s, %s, %s, %s
)"""
# position,
@ -1405,16 +1436,10 @@ class Database:
# card3,
# card4,
# startCards,
# winnings,
# rake,
# totalProfit,
# street0VPI,
# street0_3BChance,
# street0_3BDone,
# street1Seen,
# street2Seen,
# street3Seen,
# street4Seen,
# sawShowdown,
# otherRaisedStreet1,
# otherRaisedStreet2,
@ -1946,7 +1971,7 @@ class Database:
def store_hands_players_stud_tourney(self, backend, hands_id, player_ids, start_cashes,
antes, card_values, card_suits, winnings, rakes, seatNos, tourneys_players_ids, tourneyTypeId):
#stores hands_players for tourney stud/razz hands
return # TODO: stubbed out until someone updates it for current database structuring
try:
result=[]
for i in xrange(len(player_ids)):

View File

@ -1,28 +1,85 @@
"""Database manager
@todo: (gtk) how to validate user input in gtk.Dialog? as soon as the user clicks ok the dialog is dead. we use a while loop as workaround. not nice
@todo: (fpdb) we need the application name 'fpdb' from somewhere to put it in dialog titles
@todo: (fpdb) config object should be initialized globally and accessible from all modules via Configuration.py
@todo: (all dialogs) save/restore size and pos
@todo: (WidgetDatabaseManager) give database status meaningful colors
@todo: (WidgetDatabaseManager) implement database purging
@todo: (WidgetDatabaseManager) implement database export
@todo: (WidgetDatabaseManager) what to do on database doubleclick?
@todo: (WidgetDatabaseManager) context menu for database tree
@todo: (WidgetDatabaseManager) initializing/validating databases may take a while. how to give feedback?
"""
import os
import pygtk
pygtk.require('2.0')
import gtk
import gobject
#*******************************************************************************************************
class DatabaseManager(object):
class DatabaseManager(gobject.GObject):
DatabaseTypes = {}
@classmethod
def from_fpdb(klass, data, defaultDatabaseType=None):
#TODO: parse whatever data is
#TODO: sort out unsupported databases passed by user and log
databases = (
DatabaseTypeSqLite(name='myDb'),
DatabaseTypeSqLite(name='myDb2'),
)
#NOTE: if no databases are present in config fpdb fails with
# Traceback (most recent call last):
# File "/home/me2/Scr/Repos/fpdb-mme/pyfpdb/DatabaseManager.py", line 783, in <module>
# databaseManager = DatabaseManager.from_fpdb('', defaultDatabaseType=DatabaseTypeSqLite)
# File "/home/me2/Scr/Repos/fpdb-mme/pyfpdb/DatabaseManager.py", line 36, in from_fpdb
# config = Configuration.Config(file=options.config, dbname=options.dbname)
# File "/home/me2/Scr/Repos/fpdb-mme/pyfpdb/Configuration.py", line 436, in __init__
# db = self.get_db_parameters()
# File "/home/me2/Scr/Repos/fpdb-mme/pyfpdb/Configuration.py", line 583, in get_db_parameters
# name = self.db_selected
# AttributeError: Config instance has no attribute 'db_selected'
import sys
import Options
import Configuration
#NOTE: fpdb should perform this globally
(options, sys.argv) = Options.fpdb_options()
config = Configuration.Config(file=options.config, dbname=options.dbname)
#TODO: handle no database present
defaultDatabaseName = config.get_db_parameters().get('db-databaseName', None)
#TODO: fpdb stores databases in no particular order. this has to be fixed somehow
databases = []
for name, fpdbDatabase in config.supported_databases.items():
databaseKlass = klass.DatabaseTypes.get(fpdbDatabase.db_server, None)
#NOTE: Config does not seem to validate user input, so anything may end up here
if databaseKlass is None:
raise ValueError('Unknown databasetype: %s' % fpdbDatabase.db_server)
database = databaseKlass()
if database.Type == 'sqlite':
database.name = fpdbDatabase.db_name
database.file = fpdbDatabase.db_server
else:
database.name = fpdbDatabase.db_name
database.host = fpdbDatabase.db_server
#NOTE: fpdbDatabase.db_ip is no is a string
database.port = int(fpdbDatabase.db_ip)
database.user = fpdbDatabase.db_user
database.password = fpdbDatabase.db_pass
databases.append(database)
return klass(databases=databases, defaultDatabaseType=defaultDatabaseType)
def to_fpdb(self):
pass
def __init__(self, databases=None, defaultDatabaseType=None):
gobject.GObject.__init__(self)
self._defaultDatabaseType = defaultDatabaseType
self._databases = [] if databases is None else list(databases)
self._activeDatabase = None
def __iter__(self):
return iter(self._databases)
def set_default_database_type(self, databaseType):
@ -31,7 +88,7 @@ class DatabaseManager(object):
return self._defaultDatabaseType
def database_from_id(self, idDatabase):
for database in self._databases:
if idDatabase == id(database):
if idDatabase == self.database_id(database):
return database
def database_id(self, database):
return id(database)
@ -41,8 +98,26 @@ class DatabaseManager(object):
self._databases.append(database)
def remove_database(self, database):
self._databases.remove(database)
def init_database(self, database):
pass
def activate_database(self, database):
if self._activeDatabase is not None:
self._activeDatabase.status = self._activeDatabase.StatusInactive
#TODO: finalize database
self.emit('database-deactivated', self.database_id(self._activeDatabase) )
database.status = database.StatusActive
#TODO: activate database
self._activeDatabase = database
self.emit('database-activated', self.database_id(database) )
def active_database(self):
return self._activeDatabase
# register DatabaseManager signals
gobject.type_register(DatabaseManager)
gobject.signal_new('database-activated', DatabaseManager, gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (int, ))
gobject.signal_new('database-deactivated', DatabaseManager, gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (int, ))
gobject.signal_new('database-error', DatabaseManager, gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (int, ))
class DatabaseTypeMeta(type):
def __new__(klass, name, bases, kws):
@ -56,10 +131,25 @@ class DatabaseTypeMeta(type):
class DatabaseTypeBase(object):
__metaclass__ = DatabaseTypeMeta
Type = None
Params = ()
StatusActive = 'active'
StatusInactive = 'inactive'
StatusError = 'error' #TODO: not implemented
#TODO: not happy with returning error string. just being too lazy to impl dozens of error codes for later translation
def init_new_database(self):
"""initializes a new empty database
@return: (str) error if something goes wrong, None otherwise
"""
raise NotImplementedError()
def validate_database(self):
"""checks if the database is valid
@return: (str) error if something goes wrong, None otherwise
"""
raise NotImplementedError()
class DatabaseTypePostgres(DatabaseTypeBase):
Type = 'postgres'
Type = 'postgresql'
@classmethod
def display_name(klass):
return 'Postgres'
@ -70,6 +160,15 @@ class DatabaseTypePostgres(DatabaseTypeBase):
self.user = user
self.password = password
self.database = database
self.status = self.StatusInactive
#TODO: implement
def init_new_database(self):
pass
#TODO: implement
def validate_database(self):
pass
class DatabaseTypeMysql(DatabaseTypeBase):
Type = 'mysql'
@ -83,6 +182,16 @@ class DatabaseTypeMysql(DatabaseTypeBase):
self.user = user
self.password = password
self.database = database
self.status = self.StatusInactive
#TODO: implement
def init_new_database(self):
pass
#TODO: implement
def validate_database(self):
pass
class DatabaseTypeSqLite(DatabaseTypeBase):
Type = 'sqlite'
@ -92,7 +201,26 @@ class DatabaseTypeSqLite(DatabaseTypeBase):
def __init__(self, name='', host='', file='', database='fpdb'):
self.name = name
self.file = file
self.database = database
self.status = self.StatusInactive
def init_new_database(self):
# make shure all attrs are specified
if not self.file:
return 'no database file specified'
# create file if necessary (this will truncate file if it exists)
try:
open(self.file, 'w').close()
except IOError:
return 'can not write file'
#TODO: init tables (...)
def validate_database(self):
pass
#TODO: check if tables (...) exist
#TODO: how do we want to handle unsupported database types?
# ..uncomment to remove unsupported database types
@ -104,6 +232,20 @@ class DatabaseTypeSqLite(DatabaseTypeBase):
#except ImportError: del DatabaseManager.DatabaseTypes['sqlite']
#***************************************************************************************************************************
#TODO: there is no title (on linux), wtf?
def DialogError(parent=None, msg=''):
dlg = gtk.MessageDialog(
parent=parent,
flags=gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
type=gtk.MESSAGE_ERROR,
buttons=gtk.BUTTONS_OK,
message_format=msg,
)
dlg.run()
dlg.destroy()
return None
#TODO: derrive from gtk.VBox?
class WidgetDatabaseProperties(gtk.VBox):
@ -158,7 +300,7 @@ class WidgetDatabaseProperties(gtk.VBox):
dlg.destroy()
#TODO: bit ugly this thingy. try to find a better way to map database attrs to gtk widgets
class FieldWidget(object):
def __init__(self, text='', attrDatabase='', widget=None, attrGet=None, attrSet=None, defaultValue=None, canEdit=False, tooltip=''):
"""
@ -212,16 +354,6 @@ class WidgetDatabaseProperties(gtk.VBox):
canEdit=True,
tooltip='Any name you like to name the database '
),
self.FieldWidget(
text='Db:',
attrDatabase='database',
widget=gtk.Entry(),
defaultValue='',
attrGet='get_text',
attrSet='set_text',
canEdit=False,
tooltip='Name of the database to create'
),
self.FieldWidget(
text='File:',
attrDatabase='file',
@ -272,6 +404,16 @@ class WidgetDatabaseProperties(gtk.VBox):
canEdit=False,
tooltip='Password used to login to the host'
),
self.FieldWidget(
text='Db:',
attrDatabase='database',
widget=gtk.Entry(),
defaultValue='',
attrGet='get_text',
attrSet='set_text',
canEdit=False,
tooltip='Name of the database'
),
)
# setup database type combo
@ -372,11 +514,22 @@ class DialogDatabaseProperties(gtk.Dialog):
#TODO: derrive from gtk.VBox?
# ..is there a way to derrive from gtk.Widget or similar? this would make parentWidget kw obsolete
class WidgetDatabaseManager(gtk.VBox):
"""
"""
def __init__(self, databaseManager, parentWidget=None):
gtk.VBox.__init__(self)
self.databaseManager = databaseManager
self.parentWidget = parentWidget
self.databaseManager = databaseManager
self.databaseManager.connect('database-activated', self.on_database_manager_database_activated)
self.databaseManager.connect('database-deactivated', self.on_database_manager_database_deactivated)
self.databaseStatusNames = {
DatabaseTypeBase.StatusActive: 'Active',
DatabaseTypeBase.StatusInactive: 'Inactive',
DatabaseTypeBase.StatusError: 'Error',
}
#TODO: dono how to make word wrap work as expected
self.labelInfo = gtk.Label('database management')
@ -389,6 +542,10 @@ class WidgetDatabaseManager(gtk.VBox):
#TODO: bit messy the distinction New/Add/Edit. we'd have to pass three flags to DialogDatabaseProperties
# to handle this. maybe drop Edit (is just a Remove + Add), to keep things simple
self.buttonDatabaseActivate = gtk.Button("Activate")
self.buttonDatabaseActivate.set_tooltip_text('activates the database')
self.buttonDatabaseActivate.connect('clicked', self.on_button_database_activate_clicked)
self.buttonDatabaseActivate.set_sensitive(False)
self.buttonDatabaseNew = gtk.Button("New..")
self.buttonDatabaseNew.set_tooltip_text('creates a new database')
self.buttonDatabaseNew.connect('clicked', self.on_button_database_new_clicked)
@ -402,31 +559,30 @@ class WidgetDatabaseManager(gtk.VBox):
self.buttonDatabaseRemove = gtk.Button("Remove")
self.buttonDatabaseRemove.set_tooltip_text('removes the database from the list')
self.buttonDatabaseRemove.set_sensitive(False)
self.buttonDatabaseRemove.connect('clicked', self.on_button_database_remove_clicked)
#TODO: i dont think we should do any real database management here. maybe drop it
self.buttonDatabaseDelete = gtk.Button("Delete")
self.buttonDatabaseDelete.set_tooltip_text('removes the database from the list and deletes it')
self.buttonDatabaseDelete.set_sensitive(False)
#self.buttonDatabaseDelete = gtk.Button("Delete")
#self.buttonDatabaseDelete.set_tooltip_text('removes the database from the list and deletes it')
#self.buttonDatabaseDelete.set_sensitive(False)
# init database tree
self.treeDatabases = gtk.TreeView()
self.treeDatabaseColumns = ( #NOTE: column names starting with '_' will be hidden
'Name',
'Status',
'Type',
'_id',
treeDatabaseColumns = ( # name, displayName, dataType
('name', 'Name', str),
('status', 'Status', str),
('type', 'Type', str),
('_id', '', int),
)
store = gtk.ListStore(str, str, str, int)
self.treeDatabaseColumns = {} # name --> index
store = gtk.ListStore( *[i[2] for i in treeDatabaseColumns] )
self.treeDatabases.set_model(store)
columns = ('Name', 'Status', 'Type', '_id')
for i, column in enumerate(columns):
col = gtk.TreeViewColumn(column, gtk.CellRendererText(), text=i)
for i, (name, displayName, dataType) in enumerate(treeDatabaseColumns):
col = gtk.TreeViewColumn(displayName, gtk.CellRendererText(), text=i)
self.treeDatabases.append_column(col)
if column.startswith('_'):
if name.startswith('_'):
col.set_visible(False)
self.treeDatabaseColumns = dict([(name, i) for (i, name) in enumerate(self.treeDatabaseColumns)])
self.treeDatabaseColumns[name] = i
self.treeDatabases.get_selection().connect('changed', self.on_tree_databases_selection_changed)
# layout widgets
@ -438,11 +594,12 @@ class WidgetDatabaseManager(gtk.VBox):
hbox.set_homogeneous(False)
vbox = gtk.VBox()
hbox.pack_start(vbox, False, False, 2)
vbox.pack_start(self.buttonDatabaseActivate, False, False, 2)
vbox.pack_start(self.buttonDatabaseNew, False, False, 2)
vbox.pack_start(self.buttonDatabaseAdd, False, False, 2)
vbox.pack_start(self.buttonDatabaseEdit, False, False, 2)
vbox.pack_start(self.buttonDatabaseRemove, False, False, 2)
vbox.pack_start(self.buttonDatabaseDelete, False, False, 2)
#vbox.pack_start(self.buttonDatabaseDelete, False, False, 2)
box = gtk.VBox()
vbox.pack_start(box, True, True, 0)
@ -452,48 +609,128 @@ class WidgetDatabaseManager(gtk.VBox):
self.show_all()
# init widget
model = self.treeDatabases.get_model()
for database in self.databaseManager:
self.treeDatabases.get_model().append( (database.name, 'foo', database.Type, self.databaseManager.database_id(database)) )
it = model.append()
model.set_value(it, self.treeDatabaseColumns['name'], database.name)
model.set_value(it, self.treeDatabaseColumns['status'], self.databaseStatusNames[database.status] )
model.set_value(it, self.treeDatabaseColumns['type'], database.display_name() )
model.set_value(it, self.treeDatabaseColumns['_id'], self.databaseManager.database_id(database))
def on_database_manager_database_activated(self, databaseManager, idDatabase):
database = self.databaseManager.database_from_id(idDatabase)
model = self.treeDatabases.get_model()
for row in iter(model):
if row[self.treeDatabaseColumns['_id']] == idDatabase:
row[self.treeDatabaseColumns['status']] = self.databaseStatusNames[database.StatusActive]
break
else:
raise ValueError('database not found')
def on_database_manager_database_deactivated(self, databaseManager, idDatabase):
database = self.databaseManager.database_from_id(idDatabase)
model = self.treeDatabases.get_model()
for row in iter(model):
if row[self.treeDatabaseColumns['_id']] == idDatabase:
row[self.treeDatabaseColumns['status']] = self.databaseStatusNames[database.StatusInactive]
break
else:
raise ValueError('database not found')
def on_button_database_activate_clicked(self, button):
selection = self.treeDatabases.get_selection()
if selection is None:
return
model, it = selection.get_selected()
idDatabase = model.get_value(it, self.treeDatabaseColumns['_id'])
database = self.databaseManager.database_from_id(idDatabase)
self.databaseManager.activate_database(database)
#TODO: for some reason i have to click OK/Cancel twice to close the dialog
def on_button_database_new_clicked(self, button):
databaseType = self.databaseManager.get_default_database_type()
if databaseType is None:
raise ValueError('no defult database type set')
databaseKlass = self.databaseManager.get_default_database_type()
if databaseKlass is None:
raise ValueError('no default database type set')
database = databaseKlass()
while True:
dlg = DialogDatabaseProperties(
self.databaseManager,
databaseType(),
database,
parent=self.parentWidget,
mode=WidgetDatabaseProperties.ModeNew,
title='[New database] - database properties'
title='New database'
)
if dlg.run() == gtk.RESPONSE_REJECT:
pass
if dlg.run() == gtk.RESPONSE_ACCEPT:
response = dlg.run()
if response == gtk.RESPONSE_ACCEPT:
database = dlg.get_widget_database_properties().get_database()
#TODO: sanity checks + init databse if necessary
self.databaseManager.add_database(database)
self.treeDatabases.get_model().append( (database.name, 'foo', database.Type, self.databaseManager.database_id(database)) )
#TODO: initing may or may not take a while. how to handle?
error = database.init_new_database()
if error:
DialogError(parent=dlg, msg=error)
dlg.destroy()
continue
else:
database = None
dlg.destroy()
break
if database is None:
return
self.databaseManager.add_database(database)
model = self.treeDatabases.get_model()
it = model.append()
model.set_value(it, self.treeDatabaseColumns['name'], database.name)
model.set_value(it, self.treeDatabaseColumns['status'], self.databaseStatusNames[database.status] )
model.set_value(it, self.treeDatabaseColumns['type'], database.display_name() )
model.set_value(it, self.treeDatabaseColumns['_id'], self.databaseManager.database_id(database))
def on_button_database_add_clicked(self, button):
databaseType = self.databaseManager.get_default_database_type()
if databaseType is None:
databaseKlass = self.databaseManager.get_default_database_type()
if databaseKlass is None:
raise ValueError('no defult database type set')
database = databaseKlass()
while True:
dlg = DialogDatabaseProperties(
self.databaseManager,
databaseType(),
database,
parent=self.parentWidget,
mode=WidgetDatabaseProperties.ModeAdd,
title='[Add database] - database properties'
title='Add database'
)
if dlg.run() == gtk.RESPONSE_REJECT:
pass
if dlg.run() == gtk.RESPONSE_ACCEPT:
response = dlg.run()
if response == gtk.RESPONSE_ACCEPT:
database = dlg.get_widget_database_properties().get_database()
#TODO: sanity checks
#TODO: validating may or may not take a while. how to handle?
error = database.validate_database()
if error:
DialogError(parent=self.parentWidget, msg=error)
dlg.destroy()
continue
else:
database = None
dlg.destroy()
break
if database is None:
return
self.databaseManager.add_database(database)
self.treeDatabases.get_model().append( (database.name, 'foo', database.Type, self.databaseManager.database_id(database)) )
model = self.treeDatabases.get_model()
it = model.append()
model.set_value(it, self.treeDatabaseColumns['name'], database.name)
model.set_value(it, self.treeDatabaseColumns['status'], self.databaseStatusNames[database.status] )
model.set_value(it, self.treeDatabaseColumns['type'], database.display_name() )
model.set_value(it, self.treeDatabaseColumns['_id'], self.databaseManager.database_id(database))
dlg.destroy()
def on_button_database_edit_clicked(self, button):
@ -501,39 +738,52 @@ class WidgetDatabaseManager(gtk.VBox):
if selection is None:
return
model, iter = selection.get_selected()
idDatabase = model.get_value(iter, self.treeDatabaseColumns['_id'])
model, it = selection.get_selected()
idDatabase = model.get_value(it, self.treeDatabaseColumns['_id'])
database = self.databaseManager.database_from_id(idDatabase)
dlg = DialogDatabaseProperties(
self.databaseManager,
database=database,
database,
parent=self.parentWidget,
mode=WidgetDatabaseProperties.ModeEdit,
title='[Edit database] - database properties'
title='Edit database'
)
if dlg.run() == gtk.RESPONSE_REJECT:
response = dlg.run()
if response == gtk.RESPONSE_REJECT:
pass
if dlg.run() == gtk.RESPONSE_ACCEPT:
elif response == gtk.RESPONSE_ACCEPT:
database = dlg.get_database()
selection = self.treeDatabases.get_selection()
if selection is not None:
model, iter = selection.get_selected()
model.set_value(iter, 0, database.name)
model, it = selection.get_selected()
model.set_value(it, self.treeDatabaseColumns['name'], database.name)
dlg.destroy()
def on_button_database_remove_clicked(self, button):
selection = self.treeDatabases.get_selection()
if selection is None:
return
model, it = selection.get_selected()
#TODO: finalize database
model.remove(it)
def on_tree_databases_selection_changed(self, treeSelection):
hasSelection = bool(treeSelection.count_selected_rows())
# enable/disable selection dependend widgets
self.buttonDatabaseActivate.set_sensitive(hasSelection)
self.buttonDatabaseEdit.set_sensitive(hasSelection)
self.buttonDatabaseRemove.set_sensitive(hasSelection)
self.buttonDatabaseDelete.set_sensitive(hasSelection)
#self.buttonDatabaseDelete.set_sensitive(hasSelection)
class DialogDatabaseManager(gtk.Dialog):
def __init__(self, databaseManager, parent=None):
gtk.Dialog.__init__(self,
title="My dialog",
title="Databases",
parent=parent,
flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
buttons=(

View File

@ -18,6 +18,13 @@
#fpdb modules
import Card
DEBUG = True
if DEBUG:
import pprint
pp = pprint.PrettyPrinter(indent=4)
class DerivedStats():
def __init__(self, hand):
self.hand = hand
@ -30,13 +37,19 @@ class DerivedStats():
for player in hand.players:
self.handsplayers[player[1]] = {}
#Init vars that may not be used, but still need to be inserted.
# All stud street4 need this when importing holdem
self.handsplayers[player[1]]['winnings'] = 0
self.handsplayers[player[1]]['street4Seen'] = False
self.handsplayers[player[1]]['street4Aggr'] = False
self.assembleHands(self.hand)
self.assembleHandsPlayers(self.hand)
print "hands =", self.hands
print "handsplayers =", self.handsplayers
if DEBUG:
print "Hands:"
pp.pprint(self.hands)
print "HandsPlayers:"
pp.pprint(self.handsplayers)
def getHands(self):
return self.hands
@ -85,11 +98,20 @@ class DerivedStats():
# commentTs DATETIME
def assembleHandsPlayers(self, hand):
#street0VPI/vpip already called in Hand
#hand.players = [[seat, name, chips],[seat, name, chips]]
for player in hand.players:
self.handsplayers[player[1]]['seatNo'] = player[0]
self.handsplayers[player[1]]['startCash'] = player[2]
# Winnings is a non-negative value of money collected from the pot, which already includes the
# rake taken out. hand.collectees is Decimal, database requires cents
for player in hand.collectees:
self.handsplayers[player]['winnings'] = int(100 * hand.collectees[player])
for i, street in enumerate(hand.actionStreets[2:]):
self.seen(self.hand, i+1)
for i, street in enumerate(hand.actionStreets[1:]):
self.aggr(self.hand, i)
@ -794,9 +816,9 @@ class DerivedStats():
for player in hand.players:
if player[1] in vpipers:
self.handsplayers[player[1]]['vpip'] = True
self.handsplayers[player[1]]['street0VPI'] = True
else:
self.handsplayers[player[1]]['vpip'] = False
self.handsplayers[player[1]]['street0VPI'] = False
def playersAtStreetX(self, hand):
""" playersAtStreet1 SMALLINT NOT NULL, /* num of players seeing flop/street4/draw1 */"""
@ -833,6 +855,17 @@ class DerivedStats():
self.hands['street3Raises'] = 0 # /* num big bets paid to see sd/street7 */
self.hands['street4Raises'] = 0 # /* num big bets paid to see showdown */
def seen(self, hand, i):
pas = set()
for act in hand.actions[hand.actionStreets[i+1]]:
pas.add(act[0])
for player in hand.players:
if player[1] in pas:
self.handsplayers[player[1]]['street%sSeen' % i] = True
else:
self.handsplayers[player[1]]['street%sSeen' % i] = False
def aggr(self, hand, i):
aggrers = set()
for act in hand.actions[hand.actionStreets[i]]:

View File

@ -305,10 +305,10 @@ class Filters(threading.Thread):
print "self.limit[%s] set to %s" %(limit, self.limits[limit])
if limit.isdigit() or (len(limit) > 2 and limit[-2:] == 'nl'):
if self.limits[limit]:
if self.cbNoLimits != None:
if self.cbNoLimits is not None:
self.cbNoLimits.set_active(False)
else:
if self.cbAllLimits != None:
if self.cbAllLimits is not None:
self.cbAllLimits.set_active(False)
if not self.limits[limit]:
if limit.isdigit():
@ -319,9 +319,9 @@ class Filters(threading.Thread):
if self.limits[limit]:
#for cb in self.cbLimits.values():
# cb.set_active(True)
if self.cbFL != None:
if self.cbFL is not None:
self.cbFL.set_active(True)
if self.cbNL != None:
if self.cbNL is not None:
self.cbNL.set_active(True)
elif limit == "none":
if self.limits[limit]:

View File

@ -184,11 +184,11 @@ class Fulltilt(HandHistoryConverter):
info['limitType'] = limits[mg['LIMIT']]
info['sb'] = mg['SB']
info['bb'] = mg['BB']
if mg['GAME'] != None:
if mg['GAME'] is not None:
(info['base'], info['category']) = games[mg['GAME']]
if mg['CURRENCY'] != None:
if mg['CURRENCY'] is not None:
info['currency'] = currencies[mg['CURRENCY']]
if mg['TOURNO'] == None: info['type'] = "ring"
if mg['TOURNO'] is None: info['type'] = "ring"
else: info['type'] = "tour"
# NB: SB, BB must be interpreted as blinds or bets depending on limit type.
# if info['type'] == "tour": return None # importer is screwed on tournies, pass on those hands so we don't interrupt other autoimporting
@ -196,7 +196,7 @@ class Fulltilt(HandHistoryConverter):
def readHandInfo(self, hand):
m = self.re_HandInfo.search(hand.handText)
if(m == None):
if m is None:
logging.info("Didn't match re_HandInfo")
logging.info(hand.handText)
return None
@ -212,7 +212,7 @@ class Fulltilt(HandHistoryConverter):
if m2: hand.maxseats = int(m2.group('MAX'))
hand.tourNo = m.group('TOURNO')
if m.group('PLAY') != None:
if m.group('PLAY') is not None:
hand.gametype['currency'] = 'play'
# Done: if there's a way to figure these out, we should.. otherwise we have to stuff it with unknowns
@ -240,9 +240,9 @@ class Fulltilt(HandHistoryConverter):
hand.isShootout = True
if hand.buyin == None:
if hand.buyin is None:
hand.buyin = "$0.00+$0.00"
if hand.level == None:
if hand.level is None:
hand.level = "0"
# These work, but the info is already in the Hand class - should be used for tourneys though.
@ -343,11 +343,11 @@ class Fulltilt(HandHistoryConverter):
m = self.re_HeroCards.finditer(hand.streets[street])
for found in m:
player = found.group('PNAME')
if found.group('NEWCARDS') == None:
if found.group('NEWCARDS') is None:
newcards = []
else:
newcards = found.group('NEWCARDS').split(' ')
if found.group('OLDCARDS') == None:
if found.group('OLDCARDS') is None:
oldcards = []
else:
oldcards = found.group('OLDCARDS').split(' ')
@ -376,7 +376,7 @@ class Fulltilt(HandHistoryConverter):
elif action.group('ATYPE') == ' checks':
hand.addCheck( street, action.group('PNAME'))
else:
print "DEBUG: unimplemented readAction: '%s' '%s'" %(action.group('PNAME'),action.group('ATYPE'),)
print "FullTilt: DEBUG: unimplemented readAction: '%s' '%s'" %(action.group('PNAME'),action.group('ATYPE'),)
def readShowdownActions(self, hand):
@ -416,7 +416,8 @@ class Fulltilt(HandHistoryConverter):
def readOther(self, hand):
m = self.re_Mixed.search(self.in_path)
if m == None: hand.mixed = None
if m is None:
hand.mixed = None
else:
hand.mixed = self.mixes[m.groupdict()['MIXED']]
@ -472,8 +473,10 @@ class Fulltilt(HandHistoryConverter):
(info['base'], info['category']) = games[mg['GAME']]
if mg['CURRENCY'] is not None:
info['currency'] = currencies[mg['CURRENCY']]
if mg['TOURNO'] == None: info['type'] = "ring"
else: info['type'] = "tour"
if mg['TOURNO'] is None:
info['type'] = "ring"
else:
info['type'] = "tour"
# NB: SB, BB must be interpreted as blinds or bets depending on limit type.
# Info is now ready to be copied in the tourney object
@ -654,7 +657,7 @@ class Fulltilt(HandHistoryConverter):
tourney.addPlayer(rank, a.group('PNAME'), winnings, 0, 0, 0, 0)
else:
print "Player finishing stats unreadable : %s" % a
print "FullTilt: Player finishing stats unreadable : %s" % a
# Find Hero
n = self.re_TourneyHeroFinishingP.search(playersText)
@ -663,9 +666,9 @@ class Fulltilt(HandHistoryConverter):
tourney.hero = heroName
# Is this really useful ?
if heroName not in tourney.finishPositions:
print heroName, "not found in tourney.finishPositions ..."
print "FullTilt:", heroName, "not found in tourney.finishPositions ..."
elif (tourney.finishPositions[heroName] != Decimal(n.group('HERO_FINISHING_POS'))):
print "Bad parsing : finish position incoherent : %s / %s" % (tourney.finishPositions[heroName], n.group('HERO_FINISHING_POS'))
print "FullTilt: Bad parsing : finish position incoherent : %s / %s" % (tourney.finishPositions[heroName], n.group('HERO_FINISHING_POS'))
return True

View File

@ -33,7 +33,6 @@ import string
class GuiAutoImport (threading.Thread):
def __init__(self, settings, config, sql):
"""Constructor for GuiAutoImport"""
self.importtimer = 0
self.settings = settings
self.config = config
@ -41,8 +40,8 @@ class GuiAutoImport (threading.Thread):
imp = self.config.get_import_parameters()
print "Import parameters"
print imp
# print "Import parameters"
# print imp
self.input_settings = {}
self.pipe_to_hud = None
@ -130,7 +129,8 @@ class GuiAutoImport (threading.Thread):
data[1].set_text(dia_chooser.get_filename())
self.input_settings[data[0]][0] = dia_chooser.get_filename()
elif response == gtk.RESPONSE_CANCEL:
print 'Closed, no files selected'
#print 'Closed, no files selected'
pass
dia_chooser.destroy()
#end def GuiAutoImport.browseClicked
@ -143,7 +143,6 @@ class GuiAutoImport (threading.Thread):
sys.stdout.flush()
gobject.timeout_add(1000, self.reset_startbutton)
return True
else:
return False
def reset_startbutton(self):
@ -184,18 +183,20 @@ class GuiAutoImport (threading.Thread):
command = os.path.join(sys.path[0], 'HUD_main.py')
command = [command, ] + string.split(self.settings['cl_options'])
bs = 1
try:
self.pipe_to_hud = subprocess.Popen(command, bufsize = bs, stdin = subprocess.PIPE,
self.pipe_to_hud = subprocess.Popen(command, bufsize=bs,
stdin=subprocess.PIPE,
universal_newlines=True)
except:
err = traceback.extract_tb(sys.exc_info()[2])[-1]
print "*** Error: " + err[2] + "(" + str(err[1]) + "): " + str(sys.exc_info()[1])
print "*** GuiAutoImport Error opening pipe: " + err[2] + "(" + str(err[1]) + "): " + str(sys.exc_info()[1])
else:
for site in self.input_settings:
self.importer.addImportDirectory(self.input_settings[site][0], True, site, self.input_settings[site][1])
print " * Add", site, " import directory", str(self.input_settings[site][0])
print "+Import directory - Site: " + site + " dir: " + str(self.input_settings[site][0])
self.do_import()
interval = int(self.intervalEntry.get_text())
if self.importtimer != 0:
gobject.source_remove(self.importtimer)
@ -209,7 +210,7 @@ class GuiAutoImport (threading.Thread):
self.doAutoImportBool = False # do_import will return this and stop the gobject callback timer
print "Stopping autoimport - global lock released."
if self.pipe_to_hud.poll() is not None:
print "HUD already terminated"
print " * Stop Autoimport: HUD already terminated"
else:
#print >>self.pipe_to_hud.stdin, "\n"
self.pipe_to_hud.communicate('\n') # waits for process to terminate
@ -227,7 +228,7 @@ class GuiAutoImport (threading.Thread):
#enabling and disabling sites from this interface not possible
#expects a box to layout the line horizontally
def createSiteLine(self, hbox1, hbox2, site, iconpath, hhpath, filter_name, active = True):
label = gtk.Label(site + " auto-import:")
label = gtk.Label("%s auto-import:" % site)
hbox1.pack_start(label, False, False, 3)
label.show()
@ -241,7 +242,7 @@ class GuiAutoImport (threading.Thread):
hbox2.pack_start(browseButton, False, False, 3)
browseButton.show()
label = gtk.Label(' ' + site + " filter:")
label = gtk.Label("%s filter:" % site)
hbox2.pack_start(label, False, False, 3)
label.show()

View File

@ -118,7 +118,7 @@ class GuiBulkImport():
self.progressbar.set_fraction(0)
except:
err = traceback.extract_tb(sys.exc_info()[2])[-1]
print "***Error: "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1])
print "*** BulkImport Error: "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1])
self.settings['global_lock'].release()
else:
print "bulk-import aborted - global lock not available"
@ -148,26 +148,32 @@ class GuiBulkImport():
# checkbox - print start/stop?
self.chk_st_st = gtk.CheckButton('Print Start/Stop Info')
self.table.attach(self.chk_st_st, 0, 1, 0, 1, xpadding = 10, ypadding = 0, yoptions=gtk.SHRINK)
self.table.attach(self.chk_st_st, 0, 1, 0, 1, xpadding=10, ypadding=0,
yoptions=gtk.SHRINK)
self.chk_st_st.show()
self.chk_st_st.set_active(True)
# label - status
self.lab_status = gtk.Label("Hands/status print:")
self.table.attach(self.lab_status, 1, 2, 0, 1, xpadding = 0, ypadding = 0, yoptions=gtk.SHRINK)
self.table.attach(self.lab_status, 1, 2, 0, 1, xpadding=0, ypadding=0,
yoptions=gtk.SHRINK)
self.lab_status.show()
self.lab_status.set_justify(gtk.JUSTIFY_RIGHT)
self.lab_status.set_alignment(1.0, 0.5)
# spin button - status
status_adj = gtk.Adjustment(value=100, lower=0, upper=300, step_incr=10, page_incr=1, page_size=0) #not sure what upper value should be!
self.spin_status = gtk.SpinButton(adjustment=status_adj, climb_rate=0.0, digits=0)
self.table.attach(self.spin_status, 2, 3, 0, 1, xpadding = 10, ypadding = 0, yoptions=gtk.SHRINK)
status_adj = gtk.Adjustment(value=100, lower=0, upper=300, step_incr=10,
page_incr=1, page_size=0) #not sure what upper value should be!
self.spin_status = gtk.SpinButton(adjustment=status_adj, climb_rate=0.0,
digits=0)
self.table.attach(self.spin_status, 2, 3, 0, 1, xpadding=10, ypadding=0,
yoptions=gtk.SHRINK)
self.spin_status.show()
# label - threads
self.lab_threads = gtk.Label("Number of threads:")
self.table.attach(self.lab_threads, 3, 4, 0, 1, xpadding = 0, ypadding = 0, yoptions=gtk.SHRINK)
self.table.attach(self.lab_threads, 3, 4, 0, 1, xpadding=0, ypadding=0,
yoptions=gtk.SHRINK)
self.lab_threads.show()
if not self.allowThreads:
self.lab_threads.set_sensitive(False)
@ -175,9 +181,11 @@ class GuiBulkImport():
self.lab_threads.set_alignment(1.0, 0.5)
# spin button - threads
threads_adj = gtk.Adjustment(value=0, lower=0, upper=32, step_incr=1, page_incr=1, page_size=0) #not sure what upper value should be!
threads_adj = gtk.Adjustment(value=0, lower=0, upper=32, step_incr=1,
page_incr=1, page_size=0) #not sure what upper value should be!
self.spin_threads = gtk.SpinButton(adjustment=threads_adj, climb_rate=0.0, digits=0)
self.table.attach(self.spin_threads, 4, 5, 0, 1, xpadding = 10, ypadding = 0, yoptions=gtk.SHRINK)
self.table.attach(self.spin_threads, 4, 5, 0, 1, xpadding=10, ypadding=0,
yoptions=gtk.SHRINK)
self.spin_threads.show()
if not self.allowThreads:
self.spin_threads.set_sensitive(False)
@ -195,14 +203,17 @@ class GuiBulkImport():
self.lab_hands.set_alignment(1.0, 0.5)
# spin button - hands to import
hands_adj = gtk.Adjustment(value=0, lower=0, upper=10, step_incr=1, page_incr=1, page_size=0) #not sure what upper value should be!
hands_adj = gtk.Adjustment(value=0, lower=0, upper=10, step_incr=1,
page_incr=1, page_size=0) #not sure what upper value should be!
self.spin_hands = gtk.SpinButton(adjustment=hands_adj, climb_rate=0.0, digits=0)
self.table.attach(self.spin_hands, 2, 3, 1, 2, xpadding = 10, ypadding = 0, yoptions=gtk.SHRINK)
self.table.attach(self.spin_hands, 2, 3, 1, 2, xpadding=10, ypadding=0,
yoptions=gtk.SHRINK)
self.spin_hands.show()
# label - drop indexes
self.lab_drop = gtk.Label("Drop indexes:")
self.table.attach(self.lab_drop, 3, 4, 1, 2, xpadding = 0, ypadding = 0, yoptions=gtk.SHRINK)
self.table.attach(self.lab_drop, 3, 4, 1, 2, xpadding=0, ypadding=0,
yoptions=gtk.SHRINK)
self.lab_drop.show()
self.lab_drop.set_justify(gtk.JUSTIFY_RIGHT)
self.lab_drop.set_alignment(1.0, 0.5)
@ -213,12 +224,14 @@ class GuiBulkImport():
self.cb_dropindexes.append_text("don't drop")
self.cb_dropindexes.append_text('drop')
self.cb_dropindexes.set_active(0)
self.table.attach(self.cb_dropindexes, 4, 5, 1, 2, xpadding = 10, ypadding = 0, yoptions=gtk.SHRINK)
self.table.attach(self.cb_dropindexes, 4, 5, 1, 2, xpadding=10,
ypadding=0, yoptions=gtk.SHRINK)
self.cb_dropindexes.show()
# label - filter
self.lab_filter = gtk.Label("Site filter:")
self.table.attach(self.lab_filter, 1, 2, 2, 3, xpadding = 0, ypadding = 0, yoptions=gtk.SHRINK)
self.table.attach(self.lab_filter, 1, 2, 2, 3, xpadding=0, ypadding=0,
yoptions=gtk.SHRINK)
self.lab_filter.show()
self.lab_filter.set_justify(gtk.JUSTIFY_RIGHT)
self.lab_filter.set_alignment(1.0, 0.5)
@ -229,12 +242,14 @@ class GuiBulkImport():
print w
self.cbfilter.append_text(w)
self.cbfilter.set_active(0)
self.table.attach(self.cbfilter, 2, 3, 2, 3, xpadding = 10, ypadding = 1, yoptions=gtk.SHRINK)
self.table.attach(self.cbfilter, 2, 3, 2, 3, xpadding=10, ypadding=1,
yoptions=gtk.SHRINK)
self.cbfilter.show()
# label - drop hudcache
self.lab_hdrop = gtk.Label("Drop HudCache:")
self.table.attach(self.lab_hdrop, 3, 4, 2, 3, xpadding = 0, ypadding = 0, yoptions=gtk.SHRINK)
self.table.attach(self.lab_hdrop, 3, 4, 2, 3, xpadding=0, ypadding=0,
yoptions=gtk.SHRINK)
self.lab_hdrop.show()
self.lab_hdrop.set_justify(gtk.JUSTIFY_RIGHT)
self.lab_hdrop.set_alignment(1.0, 0.5)
@ -245,19 +260,22 @@ class GuiBulkImport():
self.cb_drophudcache.append_text("don't drop")
self.cb_drophudcache.append_text('drop')
self.cb_drophudcache.set_active(0)
self.table.attach(self.cb_drophudcache, 4, 5, 2, 3, xpadding = 10, ypadding = 0, yoptions=gtk.SHRINK)
self.table.attach(self.cb_drophudcache, 4, 5, 2, 3, xpadding=10,
ypadding=0, yoptions=gtk.SHRINK)
self.cb_drophudcache.show()
# button - Import
self.load_button = gtk.Button('Import') # todo: rename variables to import too
self.load_button.connect('clicked', self.load_clicked,
'Import clicked')
self.table.attach(self.load_button, 2, 3, 4, 5, xpadding = 0, ypadding = 0, yoptions=gtk.SHRINK)
self.table.attach(self.load_button, 2, 3, 4, 5, xpadding=0, ypadding=0,
yoptions=gtk.SHRINK)
self.load_button.show()
# label - spacer (keeps rows 3 & 5 apart)
self.lab_spacer = gtk.Label()
self.table.attach(self.lab_spacer, 3, 5, 3, 4, xpadding = 0, ypadding = 0, yoptions=gtk.SHRINK)
self.table.attach(self.lab_spacer, 3, 5, 3, 4, xpadding=0, ypadding=0,
yoptions=gtk.SHRINK)
self.lab_spacer.show()
# label - info
@ -265,7 +283,8 @@ class GuiBulkImport():
# self.table.attach(self.lab_info, 3, 5, 4, 5, xpadding = 0, ypadding = 0, yoptions=gtk.SHRINK)
# self.lab_info.show()
self.progressbar = gtk.ProgressBar()
self.table.attach(self.progressbar, 3, 5, 4, 5, xpadding = 0, ypadding = 0, yoptions = gtk.SHRINK)
self.table.attach(self.progressbar, 3, 5, 4, 5, xpadding=0, ypadding=0,
yoptions=gtk.SHRINK)
self.progressbar.set_text("Waiting...")
self.progressbar.set_fraction(0)
self.progressbar.show()

View File

@ -310,7 +310,7 @@ class GuiSessionViewer (threading.Thread):
except:
pass
if self.fig != None:
if self.fig is not None:
self.fig.clear()
self.fig = Figure(figsize=(5,4), dpi=100)
if self.canvas is not None:

View File

@ -570,8 +570,8 @@ Left-Drag to Move"
</hhcs>
<supported_databases>
<database db_name="fpdb" db_server="mysql" db_ip="localhost" db_user="fpdb" db_pass="YOUR MYSQL PASSWORD" db_type="fpdb"></database>
<!-- <database db_ip="localhost" db_name="fpdb" db_pass="fpdb" db_server="sqlite" db_type="fpdb" db_user="fpdb"/> -->
<database db_name="fpdb" db_server="mysql" db_ip="localhost" db_user="fpdb" db_pass="YOUR MYSQL PASSWORD"></database>
<!-- <database db_ip="localhost" db_name="fpdb" db_pass="fpdb" db_server="sqlite" db_user="fpdb"/> -->
</supported_databases>
</FreePokerToolsConfig>

View File

@ -109,7 +109,7 @@ class HUD_main(object):
self.main_window.set_title("HUD Main Window")
self.main_window.show_all()
def destroy(*args): # call back for terminating the main eventloop
def destroy(self, *args): # call back for terminating the main eventloop
gtk.main_quit()
def kill_hud(self, event, table):
@ -190,7 +190,6 @@ class HUD_main(object):
# need their own access to the database, but should open their own
# if it is required.
self.db_connection = Database.Database(self.config)
tourny_finder = re.compile('(\d+) (\d+)')
# get hero's screen names and player ids
self.hero, self.hero_ids = {}, {}
@ -207,74 +206,56 @@ class HUD_main(object):
if new_hand_id == "": # blank line means quit
self.destroy()
break # this thread is not always killed immediately with gtk.main_quit()
# get basic info about the new hand from the db
# if there is a db error, complain, skip hand, and proceed
try:
(table_name, max, poker_game, type, site_id) = self.db_connection.get_table_name(new_hand_id)
cards = self.db_connection.get_cards(new_hand_id)
comm_cards = self.db_connection.get_common_cards(new_hand_id)
if comm_cards != {}: # stud!
cards['common'] = comm_cards['common']
(table_name, max, poker_game, type, site_id, tour_number, tab_number) = \
self.db_connection.get_table_info(new_hand_id)
except Exception, err:
err = traceback.extract_tb(sys.exc_info()[2])[-1]
print "db error: skipping "+str(new_hand_id)+" "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1])
if new_hand_id: # new_hand_id is none if we had an error prior to the store
sys.stderr.write("Database error %s in hand %d. Skipping.\n" % (err, int(new_hand_id)))
print "db error: skipping %s" % new_hand_id
sys.stderr.write("Database error: could not find hand %s.\n" % new_hand_id)
continue
if type == "tour": # hand is from a tournament
mat_obj = tourny_finder.search(table_name)
if mat_obj:
(tour_number, tab_number) = mat_obj.group(1, 2)
temp_key = tour_number
else: # tourney, but can't get number and table
print "could not find tournament: skipping "
#sys.stderr.write("Could not find tournament %d in hand %d. Skipping.\n" % (int(tour_number), int(new_hand_id)))
continue
else:
temp_key = table_name
# Update an existing HUD
if temp_key in self.hud_dict:
try:
# get stats using hud's specific params
# get stats using hud's specific params and get cards
self.db_connection.init_hud_stat_vars( self.hud_dict[temp_key].hud_params['hud_days']
, self.hud_dict[temp_key].hud_params['h_hud_days'])
stat_dict = self.db_connection.get_stats_from_hand(new_hand_id, type, self.hud_dict[temp_key].hud_params, self.hero_ids[site_id])
except:
err = traceback.extract_tb(sys.exc_info()[2])[-1]
print "db get_stats error: skipping "+str(new_hand_id)+" "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1])
if new_hand_id: # new_hand_id is none if we had an error prior to the store
sys.stderr.write("Database get_stats error %s in hand %d. Skipping.\n" % (err, int(new_hand_id)))
continue
self.hud_dict[temp_key].stat_dict = stat_dict
cards = self.db_connection.get_cards(new_hand_id)
comm_cards = self.db_connection.get_common_cards(new_hand_id)
if comm_cards != {}: # stud!
cards['common'] = comm_cards['common']
self.hud_dict[temp_key].cards = cards
[aw.update_data(new_hand_id, self.db_connection) for aw in self.hud_dict[temp_key].aux_windows]
self.update_HUD(new_hand_id, temp_key, self.config)
# Or create a new HUD
else:
try:
# get stats using default params
# get stats using default params--also get cards
self.db_connection.init_hud_stat_vars( self.hud_params['hud_days'], self.hud_params['h_hud_days'] )
stat_dict = self.db_connection.get_stats_from_hand(new_hand_id, type, self.hud_params, self.hero_ids[site_id])
except:
err = traceback.extract_tb(sys.exc_info()[2])[-1]
print "db get_stats error: skipping "+str(new_hand_id)+" "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1])
if new_hand_id: # new_hand_id is none if we had an error prior to the store
sys.stderr.write("Database get_stats error %s in hand %d. Skipping.\n" % (err, int(new_hand_id)))
continue
cards = self.db_connection.get_cards(new_hand_id)
comm_cards = self.db_connection.get_common_cards(new_hand_id)
if comm_cards != {}: # stud!
cards['common'] = comm_cards['common']
if type == "tour":
tablewindow = Tables.discover_tournament_table(self.config, tour_number, tab_number)
else:
tablewindow = Tables.discover_table_by_name(self.config, table_name)
if tablewindow == None:
if tablewindow is None:
# If no client window is found on the screen, complain and continue
if type == "tour":
table_name = "%s %s" % (tour_number, tab_number)
sys.stderr.write("table name "+table_name+" not found, skipping.\n")
sys.stderr.write("HUD create: table name "+table_name+" not found, skipping.\n")
else:
self.create_HUD(new_hand_id, tablewindow, temp_key, max, poker_game, type, stat_dict, cards)
self.db_connection.connection.rollback()

View File

@ -37,7 +37,8 @@ if __name__== "__main__":
HUD_main.config = Configuration.Config()
gobject.threads_init() # this is required
thread.start_new_thread(HUD_main.read_stdin, ()) # starts the thread
hud = HUD_main.HUD_main()
thread.start_new_thread(hud.read_stdin, ()) # starts the thread
HUD_main.main_window = gtk.Window()
HUD_main.main_window.connect("destroy", destroy)

View File

@ -550,12 +550,12 @@ Map the tuple self.gametype onto the pokerstars string describing it
"""Return the first HH line for the current hand."""
gs = "PokerStars Game #%s: " % self.handid
if self.tourNo != None and self.mixed != None: # mixed tournament
if self.tourNo is not None and self.mixed is not None: # mixed tournament
gs = gs + "Tournament #%s, %s %s (%s) - Level %s (%s) - " % (self.tourNo, self.buyin, self.MS[self.mixed], self.getGameTypeAsString(), self.level, self.getStakesAsString())
elif self.tourNo != None: # all other tournaments
elif self.tourNo is not None: # all other tournaments
gs = gs + "Tournament #%s, %s %s - Level %s (%s) - " % (self.tourNo,
self.buyin, self.getGameTypeAsString(), self.level, self.getStakesAsString())
elif self.mixed != None: # all other mixed games
elif self.mixed is not None: # all other mixed games
gs = gs + " %s (%s, %s) - " % (self.MS[self.mixed],
self.getGameTypeAsString(), self.getStakesAsString())
else: # non-mixed cash games
@ -628,7 +628,7 @@ class HoldemOmahaHand(Hand):
hhc.readShownCards(self)
self.totalPot() # finalise it (total the pot)
hhc.getRake(self)
if self.maxseats == None:
if self.maxseats is None:
self.maxseats = hhc.guessMaxSeats(self)
hhc.readOther(self)
elif builtFrom == "DB":
@ -897,7 +897,7 @@ class DrawHand(Hand):
hhc.readShownCards(self)
self.totalPot() # finalise it (total the pot)
hhc.getRake(self)
if self.maxseats == None:
if self.maxseats is None:
self.maxseats = hhc.guessMaxSeats(self)
hhc.readOther(self)
elif builtFrom == "DB":
@ -1073,7 +1073,7 @@ class StudHand(Hand):
hhc.readShownCards(self) # not done yet
self.totalPot() # finalise it (total the pot)
hhc.getRake(self)
if self.maxseats == None:
if self.maxseats is None:
self.maxseats = hhc.guessMaxSeats(self)
hhc.readOther(self)
elif builtFrom == "DB":

View File

@ -195,7 +195,8 @@ This requires a regex that greedily groups and matches the 'splitter' between ha
which it expects to find at self.re_TailSplitHands -- see for e.g. Everleaf.py.
"""
if self.in_path == '-': raise StopIteration
if self.in_path == '-':
raise StopIteration
interval = 1.0 # seconds to sleep between reads for new data
fd = codecs.open(self.in_path,'r', self.codepage)
data = ''
@ -256,7 +257,7 @@ which it expects to find at self.re_TailSplitHands -- see for e.g. Everleaf.py.
self.readFile()
self.obs = self.obs.strip()
self.obs = self.obs.replace('\r\n', '\n')
if self.obs == "" or self.obs == None:
if self.obs is None or self.obs == "":
log.info("Read no hands.")
return []
return re.split(self.re_SplitHands, self.obs)
@ -396,7 +397,7 @@ or None if we fail to get the info """
if True: # basically.. I don't know
sane = True
if(self.in_path != '-' and self.out_path == self.in_path):
if self.in_path != '-' and self.out_path == self.in_path:
print "HH Sanity Check: output and input files are the same, check config"
sane = False
@ -417,16 +418,19 @@ or None if we fail to get the info """
for l in list:
# print "'" + l + "'"
hands = hands + [Hand.Hand(self.sitename, self.gametype, l)]
# TODO: This looks like it could be replaced with a list comp.. ?
return hands
def __listof(self, x):
if isinstance(x, list) or isinstance(x, tuple): return x
else: return [x]
if isinstance(x, list) or isinstance(x, tuple):
return x
else:
return [x]
def readFile(self):
"""Open in_path according to self.codepage. Exceptions caught further up"""
if(self.filetype == "text"):
if self.filetype == "text":
if self.in_path == '-':
# read from stdin
log.debug("Reading stdin with %s" % self.codepage) # is this necessary? or possible? or what?
@ -446,12 +450,15 @@ or None if we fail to get the info """
pass
else:
print "unable to read file with any codec in list!", self.in_path
elif(self.filetype == "xml"):
elif self.filetype == "xml":
doc = xml.dom.minidom.parse(filename)
self.doc = doc
def guessMaxSeats(self, hand):
"""Return a guess at max_seats when not specified in HH."""
"""Return a guess at maxseats when not specified in HH."""
# if some other code prior to this has already set it, return it
if maxseats > 1 and maxseats < 11:
return maxseats
mo = self.maxOccSeat(hand)
if mo == 10: return 10 #that was easy
@ -471,7 +478,8 @@ or None if we fail to get the info """
def maxOccSeat(self, hand):
max = 0
for player in hand.players:
if player[0] > max: max = player[0]
if player[0] > max:
max = player[0]
return max
def getStatus(self):

View File

@ -61,7 +61,7 @@ class Hud:
def __init__(self, parent, table, max, poker_game, config, db_connection):
# __init__ is (now) intended to be called from the stdin thread, so it
# cannot touch the gui
if parent == None: # running from cli ..
if parent is None: # running from cli ..
self.parent = self
self.parent = parent
self.table = table
@ -87,11 +87,6 @@ class Hud:
self.backgroundcolor = gtk.gdk.color_parse(self.colors['hudbgcolor'])
self.foregroundcolor = gtk.gdk.color_parse(self.colors['hudfgcolor'])
if font == None:
font = "Sans"
if font_size == None:
font_size = "8"
self.font = pango.FontDescription("%s %s" % (font, font_size))
# do we need to add some sort of condition here for dealing with a request for a font that doesn't exist?
@ -136,7 +131,7 @@ class Hud:
killitem = gtk.MenuItem('Kill This HUD')
menu.append(killitem)
if self.parent != None:
if self.parent is not None:
killitem.connect("activate", self.parent.kill_hud, self.table_name)
saveitem = gtk.MenuItem('Save HUD Layout')
@ -455,6 +450,9 @@ class Hud:
# Need range here, not xrange -> need the actual list
adj = range(0, self.max + 1) # default seat adjustments = no adjustment
# does the user have a fav_seat?
if self.max not in config.supported_sites[self.table.site].layout:
sys.stderr.write("No layout found for %d-max games for site %s\n" % (self.max, self.table.site) )
return adj
if self.table.site != None and int(config.supported_sites[self.table.site].layout[self.max].fav_seat) > 0:
try:
fav_seat = config.supported_sites[self.table.site].layout[self.max].fav_seat
@ -494,6 +492,10 @@ class Hud:
sys.stderr.write("------------------------------------------------------------\nCreating hud from hand %s\n" % hand)
adj = self.adj_seats(hand, config)
loc = self.config.get_locations(self.table.site, self.max)
if loc is None and self.max != 10:
loc = self.config.get_locations(self.table.site, 10)
if loc is None and self.max != 9:
loc = self.config.get_locations(self.table.site, 9)
# create the stat windows
for i in xrange(1, self.max + 1):

View File

@ -479,7 +479,7 @@ class Flop_Mucked(Aux_Seats):
if i != "common":
id = self.get_id_from_seat(i)
# sc: had KeyError here with new table so added id != None test as a guess:
if id != None:
if id is not None:
self.m_windows[i].eb.set_tooltip_text(self.hud.stat_dict[id]['screen_name'])
def update_gui(self, new_hand_id):

View File

@ -164,7 +164,7 @@ class PokerStars(HandHistoryConverter):
if 'CURRENCY' in mg:
info['currency'] = currencies[mg['CURRENCY']]
if 'TOURNO' in mg and mg['TOURNO'] == None:
if 'TOURNO' in mg and mg['TOURNO'] is None:
info['type'] = 'ring'
else:
info['type'] = 'tour'
@ -172,7 +172,6 @@ class PokerStars(HandHistoryConverter):
# 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)
@ -182,7 +181,8 @@ class PokerStars(HandHistoryConverter):
else:
pass # throw an exception here, eh?
m = self.re_GameInfo.search(hand.handText)
if m: info.update(m.groupdict())
if m:
info.update(m.groupdict())
# m = self.re_Button.search(hand.handText)
# if m: info.update(m.groupdict())
# TODO : I rather like the idea of just having this dict as hand.info
@ -205,8 +205,7 @@ class PokerStars(HandHistoryConverter):
hand.maxseats = int(info[key])
if key == 'MIXED':
if info[key] == None: hand.mixed = None
else: hand.mixed = self.mixes[info[key]]
hand.mixed = self.mixes[info[key]] if info[key] is not None else None
if key == 'TOURNO':
hand.tourNo = info[key]
@ -214,7 +213,7 @@ class PokerStars(HandHistoryConverter):
hand.buyin = info[key]
if key == 'LEVEL':
hand.level = info[key]
if key == 'PLAY' and info['PLAY'] != None:
if key == 'PLAY' and info['PLAY'] is not None:
# hand.currency = 'play' # overrides previously set value
hand.gametype['currency'] = 'play'
@ -304,11 +303,11 @@ class PokerStars(HandHistoryConverter):
m = self.re_HeroCards.finditer(hand.streets[street])
for found in m:
player = found.group('PNAME')
if found.group('NEWCARDS') == None:
if found.group('NEWCARDS') is None:
newcards = []
else:
newcards = found.group('NEWCARDS').split(' ')
if found.group('OLDCARDS') == None:
if found.group('OLDCARDS') is None:
oldcards = []
else:
oldcards = found.group('OLDCARDS').split(' ')

View File

@ -33,12 +33,11 @@ import re
class Sql:
def __init__(self, game = 'holdem', type = 'fpdb', db_server = 'mysql'):
def __init__(self, game = 'holdem', db_server = 'mysql'):
self.query = {}
###############################################################################3
# Support for the Free Poker DataBase = fpdb http://fpdb.sourceforge.net/
#
if type == 'fpdb':
################################
# List tables
@ -213,7 +212,7 @@ class Sql:
if db_server == 'mysql':
self.query['createHandsTable'] = """CREATE TABLE Hands (
id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
tableName VARCHAR(20) NOT NULL,
tableName VARCHAR(22) NOT NULL,
siteHandNo BIGINT NOT NULL,
gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
handStart DATETIME NOT NULL,
@ -248,7 +247,7 @@ class Sql:
elif db_server == 'postgresql':
self.query['createHandsTable'] = """CREATE TABLE Hands (
id BIGSERIAL, PRIMARY KEY (id),
tableName VARCHAR(20) NOT NULL,
tableName VARCHAR(22) NOT NULL,
siteHandNo BIGINT NOT NULL,
gametypeId INT NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
handStart timestamp without time zone NOT NULL,
@ -282,7 +281,7 @@ class Sql:
elif db_server == 'sqlite':
self.query['createHandsTable'] = """CREATE TABLE Hands (
id INTEGER PRIMARY KEY,
tableName TEXT(20) NOT NULL,
tableName TEXT(22) NOT NULL,
siteHandNo INT NOT NULL,
gametypeId INT NOT NULL,
handStart REAL NOT NULL,
@ -1646,15 +1645,6 @@ class Sql:
where Id = %s
"""
self.query['get_action_from_hand'] = """
SELECT street, Players.name, HandsActions.action, HandsActions.amount, actionno
FROM Players, HandsActions, HandsPlayers
WHERE HandsPlayers.handid = %s
AND HandsPlayers.playerid = Players.id
AND HandsActions.handsPlayerId = HandsPlayers.id
ORDER BY street, actionno
"""
if db_server == 'mysql':
self.query['get_hand_1day_ago'] = """
select coalesce(max(id),0)
@ -1974,6 +1964,271 @@ class Sql:
,s.name
"""
if db_server == 'mysql':
self.query['playerStats'] = """
SELECT
concat(upper(stats.limitType), ' '
,concat(upper(substring(stats.category,1,1)),substring(stats.category,2) ), ' '
,stats.name, ' '
,cast(stats.bigBlindDesc as char)
) AS Game
,stats.n
,stats.vpip
,stats.pfr
,stats.pf3
,stats.steals
,stats.saw_f
,stats.sawsd
,stats.wtsdwsf
,stats.wmsd
,stats.FlAFq
,stats.TuAFq
,stats.RvAFq
,stats.PoFAFq
,stats.Net
,stats.BBper100
,stats.Profitperhand
,case when hprof2.variance = -999 then '-'
else format(hprof2.variance, 2)
end AS Variance
,stats.AvgSeats
FROM
(select /* stats from hudcache */
gt.base
,gt.category
,upper(gt.limitType) as limitType
,s.name
,<selectgt.bigBlind> AS bigBlindDesc
,<hcgametypeId> AS gtId
,sum(HDs) AS n
,format(100.0*sum(street0VPI)/sum(HDs),1) AS vpip
,format(100.0*sum(street0Aggr)/sum(HDs),1) AS pfr
,case when sum(street0_3Bchance) = 0 then '0'
else format(100.0*sum(street0_3Bdone)/sum(street0_3Bchance),1)
end AS pf3
,case when sum(stealattemptchance) = 0 then '-'
else format(100.0*sum(stealattempted)/sum(stealattemptchance),1)
end AS steals
,format(100.0*sum(street1Seen)/sum(HDs),1) AS saw_f
,format(100.0*sum(sawShowdown)/sum(HDs),1) AS sawsd
,case when sum(street1Seen) = 0 then '-'
else format(100.0*sum(sawShowdown)/sum(street1Seen),1)
end AS wtsdwsf
,case when sum(sawShowdown) = 0 then '-'
else format(100.0*sum(wonAtSD)/sum(sawShowdown),1)
end AS wmsd
,case when sum(street1Seen) = 0 then '-'
else format(100.0*sum(street1Aggr)/sum(street1Seen),1)
end AS FlAFq
,case when sum(street2Seen) = 0 then '-'
else format(100.0*sum(street2Aggr)/sum(street2Seen),1)
end AS TuAFq
,case when sum(street3Seen) = 0 then '-'
else format(100.0*sum(street3Aggr)/sum(street3Seen),1)
end AS RvAFq
,case when sum(street1Seen)+sum(street2Seen)+sum(street3Seen) = 0 then '-'
else format(100.0*(sum(street1Aggr)+sum(street2Aggr)+sum(street3Aggr))
/(sum(street1Seen)+sum(street2Seen)+sum(street3Seen)),1)
end AS PoFAFq
,format(sum(totalProfit)/100.0,2) AS Net
,format((sum(totalProfit/(gt.bigBlind+0.0))) / (sum(HDs)/100.0),2)
AS BBper100
,format( (sum(totalProfit)/100.0) / sum(HDs), 4) AS Profitperhand
,format( sum(activeSeats*HDs)/(sum(HDs)+0.0), 2) AS AvgSeats
from Gametypes gt
inner join Sites s on s.Id = gt.siteId
inner join HudCache hc on hc.gameTypeId = gt.Id
where hc.playerId in <player_test>
and <gtbigBlind_test>
and hc.activeSeats <seats_test>
and concat( '20', substring(hc.styleKey,2,2), '-', substring(hc.styleKey,4,2), '-'
, substring(hc.styleKey,6,2) ) <datestest>
group by gt.base
,gt.category
<groupbyseats>
,plposition
,upper(gt.limitType)
,s.name
having 1 = 1 <havingclause>
order by pname
,gt.base
,gt.category
<orderbyseats>
,case <position> when 'B' then 'B'
when 'S' then 'S'
else concat('Z', <position>)
end
<orderbyhgameTypeId>
,upper(gt.limitType) desc
,maxbigblind desc
,s.name
"""
elif db_server == 'postgresql':
self.query['playerDetailedStats'] = """
select <hgameTypeId> AS hgametypeid
,<playerName> AS pname
,gt.base
,gt.category
,upper(gt.limitType) AS limittype
,s.name
,min(gt.bigBlind) AS minbigblind
,max(gt.bigBlind) AS maxbigblind
/*,<hcgametypeId> AS gtid*/
,<position> AS plposition
,count(1) AS n
,100.0*sum(cast(hp.street0VPI as <signed>integer))/count(1) AS vpip
,100.0*sum(cast(hp.street0Aggr as <signed>integer))/count(1) AS pfr
,case when sum(cast(hp.street0_3Bchance as <signed>integer)) = 0 then -999
else 100.0*sum(cast(hp.street0_3Bdone as <signed>integer))/sum(cast(hp.street0_3Bchance as <signed>integer))
end AS pf3
,case when sum(cast(hp.stealattemptchance as <signed>integer)) = 0 then -999
else 100.0*sum(cast(hp.stealattempted as <signed>integer))/sum(cast(hp.stealattemptchance as <signed>integer))
end AS steals
,100.0*sum(cast(hp.street1Seen as <signed>integer))/count(1) AS saw_f
,100.0*sum(cast(hp.sawShowdown as <signed>integer))/count(1) AS sawsd
,case when sum(cast(hp.street1Seen as <signed>integer)) = 0 then -999
else 100.0*sum(cast(hp.sawShowdown as <signed>integer))/sum(cast(hp.street1Seen as <signed>integer))
end AS wtsdwsf
,case when sum(cast(hp.sawShowdown as <signed>integer)) = 0 then -999
else 100.0*sum(cast(hp.wonAtSD as <signed>integer))/sum(cast(hp.sawShowdown as <signed>integer))
end AS wmsd
,case when sum(cast(hp.street1Seen as <signed>integer)) = 0 then -999
else 100.0*sum(cast(hp.street1Aggr as <signed>integer))/sum(cast(hp.street1Seen as <signed>integer))
end AS flafq
,case when sum(cast(hp.street2Seen as <signed>integer)) = 0 then -999
else 100.0*sum(cast(hp.street2Aggr as <signed>integer))/sum(cast(hp.street2Seen as <signed>integer))
end AS tuafq
,case when sum(cast(hp.street3Seen as <signed>integer)) = 0 then -999
else 100.0*sum(cast(hp.street3Aggr as <signed>integer))/sum(cast(hp.street3Seen as <signed>integer))
end AS rvafq
,case when sum(cast(hp.street1Seen as <signed>integer))+sum(cast(hp.street2Seen as <signed>integer))+sum(cast(hp.street3Seen as <signed>integer)) = 0 then -999
else 100.0*(sum(cast(hp.street1Aggr as <signed>integer))+sum(cast(hp.street2Aggr as <signed>integer))+sum(cast(hp.street3Aggr as <signed>integer)))
/(sum(cast(hp.street1Seen as <signed>integer))+sum(cast(hp.street2Seen as <signed>integer))+sum(cast(hp.street3Seen as <signed>integer)))
end AS pofafq
,sum(hp.totalProfit)/100.0 AS net
,sum(hp.rake)/100.0 AS rake
,100.0*avg(hp.totalProfit/(gt.bigBlind+0.0)) AS bbper100
,avg(hp.totalProfit)/100.0 AS profitperhand
,100.0*avg((hp.totalProfit+hp.rake)/(gt.bigBlind+0.0)) AS bb100xr
,avg((hp.totalProfit+hp.rake)/100.0) AS profhndxr
,avg(h.seats+0.0) AS avgseats
,variance(hp.totalProfit/100.0) AS variance
from HandsPlayers hp
inner join Hands h on (h.id = hp.handId)
inner join Gametypes gt on (gt.Id = h.gameTypeId)
inner join Sites s on (s.Id = gt.siteId)
inner join Players p on (p.Id = hp.playerId)
where hp.playerId in <player_test>
/*and hp.tourneysPlayersId IS NULL*/
and h.seats <seats_test>
<flagtest>
<gtbigBlind_test>
and to_char(h.handStart, 'YYYY-MM-DD') <datestest>
group by hgameTypeId
,pname
,gt.base
,gt.category
<groupbyseats>
,plposition
,upper(gt.limitType)
,s.name
having 1 = 1 <havingclause>
order by pname
,gt.base
,gt.category
<orderbyseats>
,case <position> when 'B' then 'B'
when 'S' then 'S'
when '0' then 'Y'
else 'Z'||<position>
end
<orderbyhgameTypeId>
,upper(gt.limitType) desc
,maxbigblind desc
,s.name
"""
elif db_server == 'sqlite':
self.query['playerDetailedStats'] = """
select <hgameTypeId> AS hgametypeid
,gt.base
,gt.category
,upper(gt.limitType) AS limittype
,s.name
,min(gt.bigBlind) AS minbigblind
,max(gt.bigBlind) AS maxbigblind
/*,<hcgametypeId> AS gtid*/
,<position> AS plposition
,count(1) AS n
,100.0*sum(cast(hp.street0VPI as <signed>integer))/count(1) AS vpip
,100.0*sum(cast(hp.street0Aggr as <signed>integer))/count(1) AS pfr
,case when sum(cast(hp.street0_3Bchance as <signed>integer)) = 0 then -999
else 100.0*sum(cast(hp.street0_3Bdone as <signed>integer))/sum(cast(hp.street0_3Bchance as <signed>integer))
end AS pf3
,case when sum(cast(hp.stealattemptchance as <signed>integer)) = 0 then -999
else 100.0*sum(cast(hp.stealattempted as <signed>integer))/sum(cast(hp.stealattemptchance as <signed>integer))
end AS steals
,100.0*sum(cast(hp.street1Seen as <signed>integer))/count(1) AS saw_f
,100.0*sum(cast(hp.sawShowdown as <signed>integer))/count(1) AS sawsd
,case when sum(cast(hp.street1Seen as <signed>integer)) = 0 then -999
else 100.0*sum(cast(hp.sawShowdown as <signed>integer))/sum(cast(hp.street1Seen as <signed>integer))
end AS wtsdwsf
,case when sum(cast(hp.sawShowdown as <signed>integer)) = 0 then -999
else 100.0*sum(cast(hp.wonAtSD as <signed>integer))/sum(cast(hp.sawShowdown as <signed>integer))
end AS wmsd
,case when sum(cast(hp.street1Seen as <signed>integer)) = 0 then -999
else 100.0*sum(cast(hp.street1Aggr as <signed>integer))/sum(cast(hp.street1Seen as <signed>integer))
end AS flafq
,case when sum(cast(hp.street2Seen as <signed>integer)) = 0 then -999
else 100.0*sum(cast(hp.street2Aggr as <signed>integer))/sum(cast(hp.street2Seen as <signed>integer))
end AS tuafq
,case when sum(cast(hp.street3Seen as <signed>integer)) = 0 then -999
else 100.0*sum(cast(hp.street3Aggr as <signed>integer))/sum(cast(hp.street3Seen as <signed>integer))
end AS rvafq
,case when sum(cast(hp.street1Seen as <signed>integer))+sum(cast(hp.street2Seen as <signed>integer))+sum(cast(hp.street3Seen as <signed>integer)) = 0 then -999
else 100.0*(sum(cast(hp.street1Aggr as <signed>integer))+sum(cast(hp.street2Aggr as <signed>integer))+sum(cast(hp.street3Aggr as <signed>integer)))
/(sum(cast(hp.street1Seen as <signed>integer))+sum(cast(hp.street2Seen as <signed>integer))+sum(cast(hp.street3Seen as <signed>integer)))
end AS pofafq
,sum(hp.totalProfit)/100.0 AS net
,sum(hp.rake)/100.0 AS rake
,100.0*avg(hp.totalProfit/(gt.bigBlind+0.0)) AS bbper100
,avg(hp.totalProfit)/100.0 AS profitperhand
,100.0*avg((hp.totalProfit+hp.rake)/(gt.bigBlind+0.0)) AS bb100xr
,avg((hp.totalProfit+hp.rake)/100.0) AS profhndxr
,avg(h.seats+0.0) AS avgseats
,variance(hp.totalProfit/100.0) AS variance
from HandsPlayers hp
inner join Hands h on (h.id = hp.handId)
inner join Gametypes gt on (gt.Id = h.gameTypeId)
inner join Sites s on (s.Id = gt.siteId)
where hp.playerId in <player_test>
/*and hp.tourneysPlayersId IS NULL*/
and h.seats <seats_test>
<flagtest>
<gtbigBlind_test>
and to_char(h.handStart, 'YYYY-MM-DD') <datestest>
group by hgameTypeId
,hp.playerId
,gt.base
,gt.category
<groupbyseats>
,plposition
,upper(gt.limitType)
,s.name
order by hp.playerId
,gt.base
,gt.category
<orderbyseats>
,case <position> when 'B' then 'B'
when 'S' then 'S'
when '0' then 'Y'
else 'Z'||<position>
end
<orderbyhgameTypeId>
,upper(gt.limitType) desc
,maxbigblind desc
,s.name
"""
if db_server == 'mysql':
self.query['playerStats'] = """
SELECT
@ -2476,6 +2731,25 @@ class Sql:
GROUP BY h.handStart, hp.handId, hp.totalProfit
ORDER BY h.handStart"""
####################################
# Session stats query
####################################
if db_server == 'mysql':
self.query['sessionStats'] = """
SELECT UNIX_TIMESTAMP(h.handStart) as time, hp.handId, hp.startCash, hp.winnings, hp.totalProfit
FROM HandsPlayers hp
INNER JOIN Players pl ON (pl.id = hp.playerId)
INNER JOIN Hands h ON (h.id = hp.handId)
INNER JOIN Gametypes gt ON (gt.id = h.gametypeId)
WHERE pl.id in <player_test>
AND pl.siteId in <site_test>
AND h.handStart > '<startdate_test>'
AND h.handStart < '<enddate_test>'
<limit_test>
AND hp.tourneysPlayersId IS NULL
GROUP BY h.handStart, hp.handId, hp.totalProfit
ORDER BY h.handStart"""
####################################
# Session stats query
####################################
@ -2668,6 +2942,7 @@ class Sql:
,hc_position
,hp.tourneyTypeId
,date_format(h.handStart, 'd%y%m%d')
>>>>>>> 28ca49d592c8e706ad6ee58dd26655bcc33fc5fb:pyfpdb/SQL.py
"""
elif db_server == 'postgresql':
self.query['rebuildHudCache'] = """

View File

@ -68,14 +68,14 @@ def do_tip(widget, tip):
def do_stat(stat_dict, player = 24, stat = 'vpip'):
match = re_Places.search(stat)
if match == None:
if match is 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:
if match is 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])

View File

@ -95,12 +95,12 @@ gobject.signal_new("client_destroyed", gtk.Window,
class Table_Window(object):
def __init__(self, table_name = None, tournament = None, table_number = None):
if table_name != None:
if table_name is not None:
search_string = table_name
self.name = table_name
self.tournament = None
self.table = None
elif tournament != None and table_number != None:
elif tournament is not None and table_number is not None:
print "tournament %s, table %s" % (tournament, table_number)
self.tournament = int(tournament)
self.table = int(table_number)
@ -133,7 +133,7 @@ class Table_Window(object):
def check_geometry(self):
new_geo = self.get_geometry()
if new_geo == None: # window destroyed
if new_geo is None: # window destroyed
return "client_destroyed"
elif self.x != new_geo['x'] or self.y != new_geo['y']: # window moved

View File

@ -105,7 +105,7 @@ def discover_table_by_name(c, tablename):
info = discover_mac_by_name(c, tablename)
else:
return None
if info == None:
if info is None:
return None
return Table_Window(info)
@ -141,7 +141,7 @@ def discover_posix(c):
if 'History for table:' in listing: continue
if 'has no name' in listing: continue
info = decode_xwininfo(c, listing)
if info['site'] == None: continue
if info['site'] is None: continue
if info['title'] == info['exe']: continue
# this appears to be a poker client, so make a table object for it
tw = Table_Window(info)

View File

@ -289,7 +289,7 @@ class ttracker_main(object):
tablewindow = Tables.discover_tournament_table(self.config, tour_number, tab_number)
else:
tablewindow = Tables.discover_table_by_name(self.config, table_name)
if tablewindow == None:
if tablewindow is None:
# If no client window is found on the screen, complain and continue
if type == "tour":
table_name = "%s %s" % (tour_number, tab_number)

View File

@ -447,10 +447,10 @@ class fpdb:
self.settings.update(self.config.get_import_parameters())
self.settings.update(self.config.get_default_paths())
if self.db != None and self.db.fdb != None:
if self.db is not None and self.db.fdb is not None:
self.db.disconnect()
self.sql = SQL.Sql(type = self.settings['db-type'], db_server = self.settings['db-server'])
self.sql = SQL.Sql(db_server = self.settings['db-server'])
try:
self.db = Database.Database(self.config, sql = self.sql)
except FpdbMySQLFailedError:
@ -487,7 +487,7 @@ class fpdb:
response = diaDbVersionWarning.run()
diaDbVersionWarning.destroy()
if self.status_bar == None:
if self.status_bar is 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()
@ -513,10 +513,10 @@ class fpdb:
# self.lock.release()
def quit(self, widget, data=None):
# TODO: can we get some / all of the stuff done in this function to execute on any kind of abort?
print "Quitting normally"
#check if current settings differ from profile, if so offer to save or abort
# TODO: check if current settings differ from profile, if so offer to save or abort
self.db.disconnect()
# hide icon as it doesn't go away immediately in Windows - is this ok in Linux Eric?
self.statusIcon.set_visible(False)
gtk.main_quit()

View File

@ -33,12 +33,12 @@ except ImportError:
import fpdb_simple
import FpdbSQLQueries
import Configuration
class fpdb_db:
MYSQL_INNODB = 2
PGSQL = 3
SQLITE = 4
sqlite_db_dir = ".." + os.sep + "database"
def __init__(self):
"""Simple constructor, doesnt really do anything"""
@ -123,10 +123,10 @@ class fpdb_db:
else:
logging.warning("SQLite won't work well without 'sqlalchemy' installed.")
if not os.path.isdir(self.sqlite_db_dir):
print "Creating directory: '%s'" % (self.sqlite_db_dir)
os.mkdir(self.sqlite_db_dir)
self.db = sqlite3.connect( self.sqlite_db_dir + os.sep + database
if not os.path.isdir(Configuration.DIR_DATABASES):
print "Creating directory: '%s'" % (Configuration.DIR_DATABASES)
os.mkdir(Configuration.DIR_DATABASES)
self.db = sqlite3.connect( os.path.join(Configuration.DIR_DATABASES, 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")

View File

@ -182,7 +182,8 @@ class Importer:
if os.path.isdir(inputPath):
for subdir in os.walk(inputPath):
for file in subdir[2]:
self.addImportFile(os.path.join(subdir[0], file), site=site, filter=filter)
self.addImportFile(os.path.join(subdir[0], file), site=site,
filter=filter)
else:
self.addImportFile(inputPath, site=site, filter=filter)
#Add a directory of files to filelist
@ -203,7 +204,7 @@ class Importer:
#print " adding file ", file
self.addImportFile(os.path.join(dir, file), site, filter)
else:
log.warning("Attempted to add non-directory: '" + str(dir) + "' as an import directory")
log.warning("Attempted to add non-directory: '%s' as an import directory" % str(dir))
def runImport(self):
""""Run full import on self.filelist. This is called from GuiBulkImport.py"""
@ -250,6 +251,9 @@ class Importer:
#self.writeq.join()
#using empty() might be more reliable:
while not self.writeq.empty() and len(threading.enumerate()) > 1:
# TODO: Do we need to actually tell the progress indicator to move, or is it already moving, and we just need to process events...
while gtk.events_pending(): # see http://faq.pygtk.org/index.py?req=index for more hints (3.7)
gtk.main_iteration(False)
sleep(0.5)
print " ... writers finished"
@ -400,7 +404,7 @@ class Importer:
file = file.decode(fpdb_simple.LOCALE_ENCODING)
# Load filter, process file, pass returned filename to import_fpdb_file
if self.settings['threads'] > 0 and self.writeq != None:
if self.settings['threads'] > 0 and self.writeq is not None:
log.info("Converting " + file + " (" + str(q.qsize()) + ")")
else:
log.info("Converting " + file)
@ -418,9 +422,9 @@ class Importer:
obj = getattr(mod, filter_name, None)
if callable(obj):
hhc = obj(in_path = file, out_path = out_path, index = 0) # Index into file 0 until changeover
if(hhc.getStatus() and self.NEWIMPORT == False):
if hhc.getStatus() and self.NEWIMPORT == False:
(stored, duplicates, partial, errors, ttime) = self.import_fpdb_file(db, out_path, site, q)
elif (hhc.getStatus() and self.NEWIMPORT == True):
elif hhc.getStatus() and self.NEWIMPORT == True:
#This code doesn't do anything yet
handlist = hhc.getProcessedHands()
self.pos_in_file[file] = hhc.getLastCharacterRead()
@ -458,7 +462,7 @@ class Importer:
loc = self.pos_in_file[file]
#size = os.path.getsize(file)
#print "loc =", loc, 'size =', size
except:
except KeyError:
pass
# Read input file into class and close file
inputFile.seek(loc)
@ -475,7 +479,7 @@ class Importer:
db.commit()
ttime = time() - starttime
if q == None:
if q is None:
log.info("Total stored: %(stored)d\tduplicates:%(duplicates)d\terrors:%(errors)d\ttime:%(ttime)s" % locals())
if not stored:
@ -508,7 +512,7 @@ class Importer:
#print "DEBUG: import_fpdb_file: failed on lines[0]: '%s' '%s' '%s' '%s' " %( file, site, lines, loc)
return (0,0,0,1,0,0)
if firstline.find("Tournament Summary")!=-1:
if "Tournament Summary" in firstline:
print "TODO: implement importing tournament summaries"
#self.faobs = readfile(inputFile)
#self.parseTourneyHistory()
@ -525,15 +529,14 @@ class Importer:
handsId = 0
for i in xrange(len(lines)):
if (len(lines[i])<2): #Wierd way to detect for '\r\n' or '\n'
if len(lines[i]) < 2: #Wierd way to detect for '\r\n' or '\n'
endpos = i
hand = lines[startpos:endpos]
if (len(hand[0])<2):
if len(hand[0]) < 2:
hand=hand[1:]
if (len(hand)<3):
if len(hand) < 3:
pass
#TODO: This is ugly - we didn't actually find the start of the
# hand with the outer loop so we test again...
@ -553,7 +556,7 @@ class Importer:
if self.callHud:
#print "call to HUD here. handsId:",handsId
#pipe the Hands.id out to the HUD
print "sending hand to hud", handsId, "pipe =", self.caller.pipe_to_hud
print "fpdb_import: sending hand to hud", handsId, "pipe =", self.caller.pipe_to_hud
self.caller.pipe_to_hud.stdin.write("%s" % (handsId) + os.linesep)
except Exceptions.DuplicateError:
duplicates += 1

View File

@ -30,7 +30,7 @@ def mainParser(settings, siteID, category, hand, config, db = None, writeq = Non
backend = settings['db-backend']
# Ideally db connection is passed in, if not use sql list if passed in,
# otherwise start from scratch
if db == None:
if db is None:
db = Database.Database(c = config, sql = None)
category = fpdb_simple.recogniseCategory(hand[0])
@ -222,7 +222,7 @@ def mainParser(settings, siteID, category, hand, config, db = None, writeq = Non
, actionNos, hudImportData, maxSeats, tableName, seatNos)
# save hand in db via direct call or via q if in a thread
if writeq == None:
if writeq is None:
result = db.store_the_hand(htw)
else:
writeq.put(htw)

View File

@ -60,13 +60,9 @@ class InterProcessLockBase:
self._has_lock = False
def locked(self):
if self._has_lock:
return True
try:
self.acquire()
if self.acquire():
self.release()
return False
except SingleInstanceError:
return True
LOCK_FILE_DIRECTORY = '/tmp'
@ -162,38 +158,33 @@ def test_construct():
>>> lock1 = InterProcessLock(name=test_name)
>>> lock1.acquire()
True
>>> 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
False
>>> lock3.acquire()
Traceback (most recent call last):
...
SingleInstanceError: Could not acquire exclusive lock on /tmp/test.lck
False
# Release the lock and let lock2 have it.
>>> lock1.release()
>>> lock2.acquire()
True
>>> lock3.acquire()
Traceback (most recent call last):
...
SingleInstanceError: Could not acquire exclusive lock on /tmp/test.lck
False
# Release it and give it back to lock1
>>> lock2.release()
>>> lock1.acquire()
True
>>> lock2.acquire()
Traceback (most recent call last):
...
SingleInstanceError: Could not acquire exclusive lock on /tmp/test.lck
False
# Test lock status
>>> lock2.locked()
@ -224,7 +215,7 @@ def test_construct():
... import win32con
... import pywintypes
... handle = win32api.OpenProcess(win32con.PROCESS_TERMINATE , pywintypes.FALSE, pid)
... return (0 != win32api.TerminateProcess(handle, 0))
... #return (0 != win32api.TerminateProcess(handle, 0))
# Test to acquire the lock in another process.
>>> def execute(cmd):
@ -237,15 +228,14 @@ def test_construct():
>>> 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
False
>>> os_independent_kill(pid)
>>> time.sleep(1)
>>> lock1.acquire()
True
>>> lock1.release()
# Testing wait
@ -253,13 +243,12 @@ def test_construct():
>>> 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
False
>>> os_independent_kill(pid)
>>> lock1.acquire(True)
True
>>> lock1.release()
"""