Merge branch 'master' of git://git.assembla.com/fpdboz
Conflicts: pyfpdb/SQL.py
This commit is contained in:
commit
efb0768b97
|
@ -44,14 +44,35 @@ except ConfigParser.NoSectionError: # debian package path
|
||||||
|
|
||||||
log = logging.getLogger("config")
|
log = logging.getLogger("config")
|
||||||
log.debug("config logger initialised")
|
log.debug("config logger initialised")
|
||||||
|
########################################################################
|
||||||
|
# application wide consts
|
||||||
|
|
||||||
def fix_tf(x, default = True):
|
APPLICATION_NAME_SHORT = 'fpdb'
|
||||||
# The xml parser doesn't translate "True" to True. Therefore, we never get
|
APPLICATION_VERSION = 'xx.xx.xx'
|
||||||
# True or False from the parser only "True" or "False". So translate the
|
|
||||||
# string to the python boolean representation.
|
DIR_SELF = os.path.dirname(os.path.abspath(__file__))
|
||||||
if x == "1" or x == 1 or string.lower(x) == "true" or string.lower(x) == "t":
|
#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
|
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 False
|
||||||
return default
|
return default
|
||||||
|
|
||||||
|
@ -106,7 +127,7 @@ class Site:
|
||||||
self.font = node.getAttribute("font")
|
self.font = node.getAttribute("font")
|
||||||
self.font_size = node.getAttribute("font_size")
|
self.font_size = node.getAttribute("font_size")
|
||||||
self.use_frames = node.getAttribute("use_frames")
|
self.use_frames = node.getAttribute("use_frames")
|
||||||
self.enabled = fix_tf(node.getAttribute("enabled"), default = True)
|
self.enabled = string_to_bool(node.getAttribute("enabled"), default=True)
|
||||||
self.xpad = node.getAttribute("xpad")
|
self.xpad = node.getAttribute("xpad")
|
||||||
self.ypad = node.getAttribute("ypad")
|
self.ypad = node.getAttribute("ypad")
|
||||||
self.layout = {}
|
self.layout = {}
|
||||||
|
@ -201,14 +222,13 @@ class Game:
|
||||||
class Database:
|
class Database:
|
||||||
def __init__(self, node):
|
def __init__(self, node):
|
||||||
self.db_name = node.getAttribute("db_name")
|
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_ip = node.getAttribute("db_ip")
|
||||||
self.db_user = node.getAttribute("db_user")
|
self.db_user = node.getAttribute("db_user")
|
||||||
self.db_type = node.getAttribute("db_type")
|
|
||||||
self.db_pass = node.getAttribute("db_pass")
|
self.db_pass = node.getAttribute("db_pass")
|
||||||
self.db_selected = fix_tf(node.getAttribute("default"),"False")
|
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_type:'%(type)s' db_pass (not logged) selected:'%(sel)s'" \
|
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, 'type':self.db_type, 'sel':self.db_selected} )
|
% { 'name':self.db_name, 'server':self.db_server, 'ip':self.db_ip, 'user':self.db_user, 'sel':self.db_selected} )
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
temp = 'Database = ' + self.db_name + '\n'
|
temp = 'Database = ' + self.db_name + '\n'
|
||||||
|
@ -270,8 +290,8 @@ class Import:
|
||||||
self.interval = node.getAttribute("interval")
|
self.interval = node.getAttribute("interval")
|
||||||
self.callFpdbHud = node.getAttribute("callFpdbHud")
|
self.callFpdbHud = node.getAttribute("callFpdbHud")
|
||||||
self.hhArchiveBase = node.getAttribute("hhArchiveBase")
|
self.hhArchiveBase = node.getAttribute("hhArchiveBase")
|
||||||
self.saveActions = fix_tf(node.getAttribute("saveActions"), True)
|
self.saveActions = string_to_bool(node.getAttribute("saveActions"), default=True)
|
||||||
self.fastStoreHudCache = fix_tf(node.getAttribute("fastStoreHudCache"), False)
|
self.fastStoreHudCache = string_to_bool(node.getAttribute("fastStoreHudCache"), default=False)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return " interval = %s\n callFpdbHud = %s\n hhArchiveBase = %s\n saveActions = %s\n fastStoreHudCache = %s\n" \
|
return " interval = %s\n callFpdbHud = %s\n hhArchiveBase = %s\n saveActions = %s\n fastStoreHudCache = %s\n" \
|
||||||
|
@ -282,14 +302,14 @@ class HudUI:
|
||||||
self.node = node
|
self.node = node
|
||||||
self.label = node.getAttribute('label')
|
self.label = node.getAttribute('label')
|
||||||
#
|
#
|
||||||
self.aggregate_ring = fix_tf(node.getAttribute('aggregate_ring_game_stats'))
|
self.aggregate_ring = string_to_bool(node.getAttribute('aggregate_ring_game_stats'))
|
||||||
self.aggregate_tour = fix_tf(node.getAttribute('aggregate_tourney_stats'))
|
self.aggregate_tour = string_to_bool(node.getAttribute('aggregate_tourney_stats'))
|
||||||
self.hud_style = node.getAttribute('stat_aggregation_range')
|
self.hud_style = node.getAttribute('stat_aggregation_range')
|
||||||
self.hud_days = node.getAttribute('aggregation_days')
|
self.hud_days = node.getAttribute('aggregation_days')
|
||||||
self.agg_bb_mult = node.getAttribute('aggregation_level_multiplier')
|
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_ring = string_to_bool(node.getAttribute('aggregate_hero_ring_game_stats'))
|
||||||
self.h_aggregate_tour = fix_tf(node.getAttribute('aggregate_hero_tourney_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_style = node.getAttribute('hero_stat_aggregation_range')
|
||||||
self.h_hud_days = node.getAttribute('hero_aggregation_days')
|
self.h_hud_days = node.getAttribute('hero_aggregation_days')
|
||||||
self.h_agg_bb_mult = node.getAttribute('hero_aggregation_level_multiplier')
|
self.h_agg_bb_mult = node.getAttribute('hero_aggregation_level_multiplier')
|
||||||
|
@ -301,9 +321,9 @@ class HudUI:
|
||||||
|
|
||||||
class Tv:
|
class Tv:
|
||||||
def __init__(self, node):
|
def __init__(self, node):
|
||||||
self.combinedStealFold = node.getAttribute("combinedStealFold")
|
self.combinedStealFold = string_to_bool(node.getAttribute("combinedStealFold"), default=True)
|
||||||
self.combined2B3B = node.getAttribute("combined2B3B")
|
self.combined2B3B = string_to_bool(node.getAttribute("combined2B3B"), default=True)
|
||||||
self.combinedPostflop = node.getAttribute("combinedPostflop")
|
self.combinedPostflop = string_to_bool(node.getAttribute("combinedPostflop"), default=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return (" combinedStealFold = %s\n combined2B3B = %s\n combinedPostflop = %s\n" %
|
return (" combinedStealFold = %s\n combined2B3B = %s\n combinedPostflop = %s\n" %
|
||||||
|
@ -358,10 +378,13 @@ class Config:
|
||||||
self.file = file
|
self.file = file
|
||||||
self.supported_sites = {}
|
self.supported_sites = {}
|
||||||
self.supported_games = {}
|
self.supported_games = {}
|
||||||
self.supported_databases = {}
|
self.supported_databases = {} # databaseName --> Database instance
|
||||||
self.aux_windows = {}
|
self.aux_windows = {}
|
||||||
self.hhcs = {}
|
self.hhcs = {}
|
||||||
self.popup_windows = {}
|
self.popup_windows = {}
|
||||||
|
self.db_selected = None # database the user would like to use
|
||||||
|
self.tv = None
|
||||||
|
|
||||||
|
|
||||||
# s_sites = doc.getElementsByTagName("supported_sites")
|
# s_sites = doc.getElementsByTagName("supported_sites")
|
||||||
for site_node in doc.getElementsByTagName("site"):
|
for site_node in doc.getElementsByTagName("site"):
|
||||||
|
@ -373,29 +396,26 @@ class Config:
|
||||||
game = Game(node = game_node)
|
game = Game(node = game_node)
|
||||||
self.supported_games[game.game_name] = game
|
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")
|
# s_dbs = doc.getElementsByTagName("supported_databases")
|
||||||
# select database from those defined in config by:
|
#TODO: do we want to take all <database> tags or all <database> tags contained in <supported_databases>
|
||||||
# 1) command line option
|
# ..this may break stuff for some users. so leave it unchanged for now untill there is a decission
|
||||||
# or 2) selected="True" in config element
|
|
||||||
# or 3) just choose the first we come across
|
|
||||||
for db_node in doc.getElementsByTagName("database"):
|
for db_node in doc.getElementsByTagName("database"):
|
||||||
try:
|
db = Database(node=db_node)
|
||||||
db = Database(node = db_node)
|
|
||||||
except:
|
|
||||||
raise FpdbError("Unable to create database object")
|
|
||||||
else:
|
|
||||||
if db.db_name in self.supported_databases:
|
if db.db_name in self.supported_databases:
|
||||||
raise FpdbError("Database names must be unique")
|
raise ValueError("Database names must be unique")
|
||||||
# If there is only one Database node, or none are marked
|
if self.db_selected is None or db.db_selected:
|
||||||
# default, use first
|
|
||||||
if not self.supported_databases:
|
|
||||||
self.db_selected = db.db_name
|
self.db_selected = db.db_name
|
||||||
self.supported_databases[db.db_name] = db
|
self.supported_databases[db.db_name] = db
|
||||||
if db.db_selected:
|
#TODO: if the user may passes '' (empty string) as database name via command line, his choice is ignored
|
||||||
self.db_selected = db.db_name
|
# ..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:
|
if dbname and dbname in self.supported_databases:
|
||||||
self.db_selected = dbname
|
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")
|
# s_dbs = doc.getElementsByTagName("mucked_windows")
|
||||||
for aw_node in doc.getElementsByTagName("aw"):
|
for aw_node in doc.getElementsByTagName("aw"):
|
||||||
|
@ -421,8 +441,7 @@ class Config:
|
||||||
self.ui = hui
|
self.ui = hui
|
||||||
|
|
||||||
for tv_node in doc.getElementsByTagName("tv"):
|
for tv_node in doc.getElementsByTagName("tv"):
|
||||||
tv = Tv(node = tv_node)
|
self.tv = Tv(node = tv_node)
|
||||||
self.tv = tv
|
|
||||||
|
|
||||||
db = self.get_db_parameters()
|
db = self.get_db_parameters()
|
||||||
if db['db-password'] == 'YOUR MYSQL PASSWORD':
|
if db['db-password'] == 'YOUR MYSQL PASSWORD':
|
||||||
|
@ -539,7 +558,7 @@ class Config:
|
||||||
self.doc.writexml(f)
|
self.doc.writexml(f)
|
||||||
else:
|
else:
|
||||||
shutil.move(self.file, self.file+".backup")
|
shutil.move(self.file, self.file+".backup")
|
||||||
with open(self.file, 'w') as f:
|
with open(file, 'w') as f:
|
||||||
self.doc.writexml(f)
|
self.doc.writexml(f)
|
||||||
|
|
||||||
def edit_layout(self, site_name, max, width = None, height = None,
|
def edit_layout(self, site_name, max, width = None, height = None,
|
||||||
|
@ -571,6 +590,13 @@ class Config:
|
||||||
else:
|
else:
|
||||||
self.aux_windows[aux_name].layout[max].location[i] = ( locations[i][0], locations[i][1] )
|
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):
|
def get_db_parameters(self):
|
||||||
db = {}
|
db = {}
|
||||||
name = self.db_selected
|
name = self.db_selected
|
||||||
|
@ -590,20 +616,18 @@ class Config:
|
||||||
try: db['db-server'] = self.supported_databases[name].db_server
|
try: db['db-server'] = self.supported_databases[name].db_server
|
||||||
except: pass
|
except: pass
|
||||||
|
|
||||||
try: db['db-type'] = self.supported_databases[name].db_type
|
if self.supported_databases[name].db_server== DATABASE_TYPE_MYSQL:
|
||||||
except: pass
|
|
||||||
|
|
||||||
if string.lower(self.supported_databases[name].db_server) == 'mysql':
|
|
||||||
db['db-backend'] = 2
|
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
|
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
|
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
|
return db
|
||||||
|
|
||||||
def set_db_parameters(self, db_name = 'fpdb', db_ip = None, db_user = None,
|
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)
|
db_node = self.get_db_node(db_name)
|
||||||
if db_node != None:
|
if db_node != None:
|
||||||
if db_ip is not None: db_node.setAttribute("db_ip", db_ip)
|
if db_ip is not None: db_node.setAttribute("db_ip", db_ip)
|
||||||
|
@ -627,16 +651,13 @@ class Config:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_tv_parameters(self):
|
def get_tv_parameters(self):
|
||||||
tv = {}
|
if self.tv is not None:
|
||||||
try: tv['combinedStealFold'] = self.tv.combinedStealFold
|
return {
|
||||||
except: tv['combinedStealFold'] = True
|
'combinedStealFold': self.tv.combinedStealFold,
|
||||||
|
'combined2B3B': self.tv.combined2B3B,
|
||||||
try: tv['combined2B3B'] = self.tv.combined2B3B
|
'combinedPostflop': self.tv.combinedPostflop
|
||||||
except: tv['combined2B3B'] = True
|
}
|
||||||
|
return {}
|
||||||
try: tv['combinedPostflop'] = self.tv.combinedPostflop
|
|
||||||
except: tv['combinedPostflop'] = True
|
|
||||||
return tv
|
|
||||||
|
|
||||||
# Allow to change the menu appearance
|
# Allow to change the menu appearance
|
||||||
def get_hud_ui_parameters(self):
|
def get_hud_ui_parameters(self):
|
||||||
|
@ -734,30 +755,28 @@ class Config:
|
||||||
colors['hudfgcolor'] = self.supported_sites[site].hudfgcolor
|
colors['hudfgcolor'] = self.supported_sites[site].hudfgcolor
|
||||||
return colors
|
return colors
|
||||||
|
|
||||||
def get_default_font(self, site = 'PokerStars'):
|
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"
|
font = "Sans"
|
||||||
else:
|
|
||||||
font = self.supported_sites[site].font
|
|
||||||
|
|
||||||
if self.supported_sites[site].font_size == "":
|
|
||||||
font_size = "8"
|
font_size = "8"
|
||||||
else:
|
site = self.supported_sites.get(site, None)
|
||||||
font_size = self.supported_sites[site].font_size
|
if site is not None:
|
||||||
return (font, font_size)
|
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"):
|
def get_locations(self, site_name="PokerStars", max=8):
|
||||||
|
site = self.supported_sites.get(site_name, None)
|
||||||
try:
|
if site is not None:
|
||||||
locations = self.supported_sites[site].layout[max].location
|
location = site.layout.get(max, None)
|
||||||
except:
|
if location is not None:
|
||||||
locations = ( ( 0, 0), (684, 61), (689, 239), (692, 346),
|
return location.location
|
||||||
|
return (
|
||||||
|
( 0, 0), (684, 61), (689, 239), (692, 346),
|
||||||
(586, 393), (421, 440), (267, 440), ( 0, 361),
|
(586, 393), (421, 440), (267, 440), ( 0, 361),
|
||||||
( 0, 280), (121, 280), ( 46, 30) )
|
( 0, 280), (121, 280), ( 46, 30)
|
||||||
return locations
|
)
|
||||||
|
|
||||||
def get_aux_locations(self, aux = "mucked", max = "9"):
|
def get_aux_locations(self, aux = "mucked", max = "9"):
|
||||||
|
|
||||||
|
@ -769,14 +788,12 @@ class Config:
|
||||||
( 0, 280), (121, 280), ( 46, 30) )
|
( 0, 280), (121, 280), ( 46, 30) )
|
||||||
return locations
|
return locations
|
||||||
|
|
||||||
def get_supported_sites(self, all = False):
|
def get_supported_sites(self, all=False):
|
||||||
"""Returns the list of supported sites."""
|
"""Returns the list of supported sites."""
|
||||||
the_sites = []
|
if all:
|
||||||
for site in self.supported_sites.keys():
|
return self.supported_sites.keys()
|
||||||
params = self.get_site_parameters(site)
|
else:
|
||||||
if all or params['enabled']:
|
return [site_name for (site_name, site) in self.supported_sites.items() if site.enabled]
|
||||||
the_sites.append(site)
|
|
||||||
return the_sites
|
|
||||||
|
|
||||||
def get_site_parameters(self, site):
|
def get_site_parameters(self, site):
|
||||||
"""Returns a dict of the site parameters for the specified site"""
|
"""Returns a dict of the site parameters for the specified site"""
|
||||||
|
@ -824,10 +841,7 @@ class Config:
|
||||||
|
|
||||||
def get_aux_windows(self):
|
def get_aux_windows(self):
|
||||||
"""Gets the list of mucked window formats in the configuration."""
|
"""Gets the list of mucked window formats in the configuration."""
|
||||||
mw = []
|
return self.aux_windows.keys()
|
||||||
for w in self.aux_windows.keys():
|
|
||||||
mw.append(w)
|
|
||||||
return mw
|
|
||||||
|
|
||||||
def get_aux_parameters(self, name):
|
def get_aux_parameters(self, name):
|
||||||
"""Gets a dict of mucked window parameters from the named mw."""
|
"""Gets a dict of mucked window parameters from the named mw."""
|
||||||
|
|
|
@ -205,7 +205,7 @@ class Database:
|
||||||
|
|
||||||
# where possible avoid creating new SQL instance by using the global one passed in
|
# where possible avoid creating new SQL instance by using the global one passed in
|
||||||
if sql is None:
|
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:
|
else:
|
||||||
self.sql = sql
|
self.sql = sql
|
||||||
|
|
||||||
|
@ -249,7 +249,6 @@ class Database:
|
||||||
|
|
||||||
db_params = c.get_db_parameters()
|
db_params = c.get_db_parameters()
|
||||||
self.import_options = c.get_import_parameters()
|
self.import_options = c.get_import_parameters()
|
||||||
self.type = db_params['db-type']
|
|
||||||
self.backend = db_params['db-backend']
|
self.backend = db_params['db-backend']
|
||||||
self.db_server = db_params['db-server']
|
self.db_server = db_params['db-server']
|
||||||
self.database = db_params['db-databaseName']
|
self.database = db_params['db-databaseName']
|
||||||
|
@ -1394,6 +1393,12 @@ class Database:
|
||||||
pids[p],
|
pids[p],
|
||||||
pdata[p]['startCash'],
|
pdata[p]['startCash'],
|
||||||
pdata[p]['seatNo'],
|
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]['street0Aggr'],
|
||||||
pdata[p]['street1Aggr'],
|
pdata[p]['street1Aggr'],
|
||||||
pdata[p]['street2Aggr'],
|
pdata[p]['street2Aggr'],
|
||||||
|
@ -1406,6 +1411,12 @@ class Database:
|
||||||
playerId,
|
playerId,
|
||||||
startCash,
|
startCash,
|
||||||
seatNo,
|
seatNo,
|
||||||
|
winnings,
|
||||||
|
street0VPI,
|
||||||
|
street1Seen,
|
||||||
|
street2Seen,
|
||||||
|
street3Seen,
|
||||||
|
street4Seen,
|
||||||
street0Aggr,
|
street0Aggr,
|
||||||
street1Aggr,
|
street1Aggr,
|
||||||
street2Aggr,
|
street2Aggr,
|
||||||
|
@ -1414,7 +1425,8 @@ class Database:
|
||||||
)
|
)
|
||||||
VALUES (
|
VALUES (
|
||||||
%s, %s, %s, %s, %s,
|
%s, %s, %s, %s, %s,
|
||||||
%s, %s, %s, %s
|
%s, %s, %s, %s, %s,
|
||||||
|
%s, %s, %s, %s, %s
|
||||||
)"""
|
)"""
|
||||||
|
|
||||||
# position,
|
# position,
|
||||||
|
@ -1424,16 +1436,10 @@ class Database:
|
||||||
# card3,
|
# card3,
|
||||||
# card4,
|
# card4,
|
||||||
# startCards,
|
# startCards,
|
||||||
# winnings,
|
|
||||||
# rake,
|
# rake,
|
||||||
# totalProfit,
|
# totalProfit,
|
||||||
# street0VPI,
|
|
||||||
# street0_3BChance,
|
# street0_3BChance,
|
||||||
# street0_3BDone,
|
# street0_3BDone,
|
||||||
# street1Seen,
|
|
||||||
# street2Seen,
|
|
||||||
# street3Seen,
|
|
||||||
# street4Seen,
|
|
||||||
# sawShowdown,
|
# sawShowdown,
|
||||||
# otherRaisedStreet1,
|
# otherRaisedStreet1,
|
||||||
# otherRaisedStreet2,
|
# otherRaisedStreet2,
|
||||||
|
|
|
@ -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 os
|
||||||
import pygtk
|
import pygtk
|
||||||
pygtk.require('2.0')
|
pygtk.require('2.0')
|
||||||
import gtk
|
import gtk
|
||||||
|
import gobject
|
||||||
|
|
||||||
#*******************************************************************************************************
|
#*******************************************************************************************************
|
||||||
class DatabaseManager(object):
|
class DatabaseManager(gobject.GObject):
|
||||||
DatabaseTypes = {}
|
DatabaseTypes = {}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_fpdb(klass, data, defaultDatabaseType=None):
|
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)
|
return klass(databases=databases, defaultDatabaseType=defaultDatabaseType)
|
||||||
|
|
||||||
|
def to_fpdb(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, databases=None, defaultDatabaseType=None):
|
def __init__(self, databases=None, defaultDatabaseType=None):
|
||||||
|
gobject.GObject.__init__(self)
|
||||||
|
|
||||||
self._defaultDatabaseType = defaultDatabaseType
|
self._defaultDatabaseType = defaultDatabaseType
|
||||||
self._databases = [] if databases is None else list(databases)
|
self._databases = [] if databases is None else list(databases)
|
||||||
|
self._activeDatabase = None
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return iter(self._databases)
|
return iter(self._databases)
|
||||||
def set_default_database_type(self, databaseType):
|
def set_default_database_type(self, databaseType):
|
||||||
|
@ -31,7 +88,7 @@ class DatabaseManager(object):
|
||||||
return self._defaultDatabaseType
|
return self._defaultDatabaseType
|
||||||
def database_from_id(self, idDatabase):
|
def database_from_id(self, idDatabase):
|
||||||
for database in self._databases:
|
for database in self._databases:
|
||||||
if idDatabase == id(database):
|
if idDatabase == self.database_id(database):
|
||||||
return database
|
return database
|
||||||
def database_id(self, database):
|
def database_id(self, database):
|
||||||
return id(database)
|
return id(database)
|
||||||
|
@ -41,8 +98,26 @@ class DatabaseManager(object):
|
||||||
self._databases.append(database)
|
self._databases.append(database)
|
||||||
def remove_database(self, database):
|
def remove_database(self, database):
|
||||||
self._databases.remove(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):
|
class DatabaseTypeMeta(type):
|
||||||
def __new__(klass, name, bases, kws):
|
def __new__(klass, name, bases, kws):
|
||||||
|
@ -56,10 +131,25 @@ class DatabaseTypeMeta(type):
|
||||||
class DatabaseTypeBase(object):
|
class DatabaseTypeBase(object):
|
||||||
__metaclass__ = DatabaseTypeMeta
|
__metaclass__ = DatabaseTypeMeta
|
||||||
Type = None
|
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):
|
class DatabaseTypePostgres(DatabaseTypeBase):
|
||||||
Type = 'postgres'
|
Type = 'postgresql'
|
||||||
@classmethod
|
@classmethod
|
||||||
def display_name(klass):
|
def display_name(klass):
|
||||||
return 'Postgres'
|
return 'Postgres'
|
||||||
|
@ -70,6 +160,15 @@ class DatabaseTypePostgres(DatabaseTypeBase):
|
||||||
self.user = user
|
self.user = user
|
||||||
self.password = password
|
self.password = password
|
||||||
self.database = database
|
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):
|
class DatabaseTypeMysql(DatabaseTypeBase):
|
||||||
Type = 'mysql'
|
Type = 'mysql'
|
||||||
|
@ -83,6 +182,16 @@ class DatabaseTypeMysql(DatabaseTypeBase):
|
||||||
self.user = user
|
self.user = user
|
||||||
self.password = password
|
self.password = password
|
||||||
self.database = database
|
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):
|
class DatabaseTypeSqLite(DatabaseTypeBase):
|
||||||
Type = 'sqlite'
|
Type = 'sqlite'
|
||||||
|
@ -92,7 +201,26 @@ class DatabaseTypeSqLite(DatabaseTypeBase):
|
||||||
def __init__(self, name='', host='', file='', database='fpdb'):
|
def __init__(self, name='', host='', file='', database='fpdb'):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.file = file
|
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?
|
#TODO: how do we want to handle unsupported database types?
|
||||||
# ..uncomment to remove unsupported database types
|
# ..uncomment to remove unsupported database types
|
||||||
|
@ -104,6 +232,20 @@ class DatabaseTypeSqLite(DatabaseTypeBase):
|
||||||
#except ImportError: del DatabaseManager.DatabaseTypes['sqlite']
|
#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?
|
#TODO: derrive from gtk.VBox?
|
||||||
class WidgetDatabaseProperties(gtk.VBox):
|
class WidgetDatabaseProperties(gtk.VBox):
|
||||||
|
|
||||||
|
@ -158,7 +300,7 @@ class WidgetDatabaseProperties(gtk.VBox):
|
||||||
dlg.destroy()
|
dlg.destroy()
|
||||||
|
|
||||||
|
|
||||||
|
#TODO: bit ugly this thingy. try to find a better way to map database attrs to gtk widgets
|
||||||
class FieldWidget(object):
|
class FieldWidget(object):
|
||||||
def __init__(self, text='', attrDatabase='', widget=None, attrGet=None, attrSet=None, defaultValue=None, canEdit=False, tooltip=''):
|
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,
|
canEdit=True,
|
||||||
tooltip='Any name you like to name the database '
|
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(
|
self.FieldWidget(
|
||||||
text='File:',
|
text='File:',
|
||||||
attrDatabase='file',
|
attrDatabase='file',
|
||||||
|
@ -272,6 +404,16 @@ class WidgetDatabaseProperties(gtk.VBox):
|
||||||
canEdit=False,
|
canEdit=False,
|
||||||
tooltip='Password used to login to the host'
|
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
|
# setup database type combo
|
||||||
|
@ -372,11 +514,22 @@ class DialogDatabaseProperties(gtk.Dialog):
|
||||||
#TODO: derrive from gtk.VBox?
|
#TODO: derrive from gtk.VBox?
|
||||||
# ..is there a way to derrive from gtk.Widget or similar? this would make parentWidget kw obsolete
|
# ..is there a way to derrive from gtk.Widget or similar? this would make parentWidget kw obsolete
|
||||||
class WidgetDatabaseManager(gtk.VBox):
|
class WidgetDatabaseManager(gtk.VBox):
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, databaseManager, parentWidget=None):
|
def __init__(self, databaseManager, parentWidget=None):
|
||||||
gtk.VBox.__init__(self)
|
gtk.VBox.__init__(self)
|
||||||
|
|
||||||
self.databaseManager = databaseManager
|
|
||||||
self.parentWidget = parentWidget
|
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
|
#TODO: dono how to make word wrap work as expected
|
||||||
self.labelInfo = gtk.Label('database management')
|
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
|
#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
|
# 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 = gtk.Button("New..")
|
||||||
self.buttonDatabaseNew.set_tooltip_text('creates a new database')
|
self.buttonDatabaseNew.set_tooltip_text('creates a new database')
|
||||||
self.buttonDatabaseNew.connect('clicked', self.on_button_database_new_clicked)
|
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 = gtk.Button("Remove")
|
||||||
self.buttonDatabaseRemove.set_tooltip_text('removes the database from the list')
|
self.buttonDatabaseRemove.set_tooltip_text('removes the database from the list')
|
||||||
self.buttonDatabaseRemove.set_sensitive(False)
|
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
|
#TODO: i dont think we should do any real database management here. maybe drop it
|
||||||
self.buttonDatabaseDelete = gtk.Button("Delete")
|
#self.buttonDatabaseDelete = gtk.Button("Delete")
|
||||||
self.buttonDatabaseDelete.set_tooltip_text('removes the database from the list and deletes it')
|
#self.buttonDatabaseDelete.set_tooltip_text('removes the database from the list and deletes it')
|
||||||
self.buttonDatabaseDelete.set_sensitive(False)
|
#self.buttonDatabaseDelete.set_sensitive(False)
|
||||||
|
|
||||||
# init database tree
|
# init database tree
|
||||||
self.treeDatabases = gtk.TreeView()
|
self.treeDatabases = gtk.TreeView()
|
||||||
self.treeDatabaseColumns = ( #NOTE: column names starting with '_' will be hidden
|
treeDatabaseColumns = ( # name, displayName, dataType
|
||||||
'Name',
|
('name', 'Name', str),
|
||||||
'Status',
|
('status', 'Status', str),
|
||||||
'Type',
|
('type', 'Type', str),
|
||||||
'_id',
|
('_id', '', int),
|
||||||
)
|
)
|
||||||
|
self.treeDatabaseColumns = {} # name --> index
|
||||||
store = gtk.ListStore(str, str, str, int)
|
store = gtk.ListStore( *[i[2] for i in treeDatabaseColumns] )
|
||||||
self.treeDatabases.set_model(store)
|
self.treeDatabases.set_model(store)
|
||||||
columns = ('Name', 'Status', 'Type', '_id')
|
for i, (name, displayName, dataType) in enumerate(treeDatabaseColumns):
|
||||||
for i, column in enumerate(columns):
|
col = gtk.TreeViewColumn(displayName, gtk.CellRendererText(), text=i)
|
||||||
col = gtk.TreeViewColumn(column, gtk.CellRendererText(), text=i)
|
|
||||||
self.treeDatabases.append_column(col)
|
self.treeDatabases.append_column(col)
|
||||||
if column.startswith('_'):
|
if name.startswith('_'):
|
||||||
col.set_visible(False)
|
col.set_visible(False)
|
||||||
|
self.treeDatabaseColumns[name] = i
|
||||||
self.treeDatabaseColumns = dict([(name, i) for (i, name) in enumerate(self.treeDatabaseColumns)])
|
|
||||||
self.treeDatabases.get_selection().connect('changed', self.on_tree_databases_selection_changed)
|
self.treeDatabases.get_selection().connect('changed', self.on_tree_databases_selection_changed)
|
||||||
|
|
||||||
# layout widgets
|
# layout widgets
|
||||||
|
@ -438,11 +594,12 @@ class WidgetDatabaseManager(gtk.VBox):
|
||||||
hbox.set_homogeneous(False)
|
hbox.set_homogeneous(False)
|
||||||
vbox = gtk.VBox()
|
vbox = gtk.VBox()
|
||||||
hbox.pack_start(vbox, False, False, 2)
|
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.buttonDatabaseNew, False, False, 2)
|
||||||
vbox.pack_start(self.buttonDatabaseAdd, False, False, 2)
|
vbox.pack_start(self.buttonDatabaseAdd, False, False, 2)
|
||||||
vbox.pack_start(self.buttonDatabaseEdit, False, False, 2)
|
vbox.pack_start(self.buttonDatabaseEdit, False, False, 2)
|
||||||
vbox.pack_start(self.buttonDatabaseRemove, 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()
|
box = gtk.VBox()
|
||||||
vbox.pack_start(box, True, True, 0)
|
vbox.pack_start(box, True, True, 0)
|
||||||
|
|
||||||
|
@ -452,48 +609,128 @@ class WidgetDatabaseManager(gtk.VBox):
|
||||||
self.show_all()
|
self.show_all()
|
||||||
|
|
||||||
# init widget
|
# init widget
|
||||||
|
model = self.treeDatabases.get_model()
|
||||||
for database in self.databaseManager:
|
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
|
#TODO: for some reason i have to click OK/Cancel twice to close the dialog
|
||||||
def on_button_database_new_clicked(self, button):
|
def on_button_database_new_clicked(self, button):
|
||||||
databaseType = self.databaseManager.get_default_database_type()
|
databaseKlass = self.databaseManager.get_default_database_type()
|
||||||
if databaseType is None:
|
if databaseKlass is None:
|
||||||
raise ValueError('no defult database type set')
|
raise ValueError('no default database type set')
|
||||||
|
database = databaseKlass()
|
||||||
|
|
||||||
|
while True:
|
||||||
dlg = DialogDatabaseProperties(
|
dlg = DialogDatabaseProperties(
|
||||||
self.databaseManager,
|
self.databaseManager,
|
||||||
databaseType(),
|
database,
|
||||||
parent=self.parentWidget,
|
parent=self.parentWidget,
|
||||||
mode=WidgetDatabaseProperties.ModeNew,
|
mode=WidgetDatabaseProperties.ModeNew,
|
||||||
title='[New database] - database properties'
|
title='New database'
|
||||||
)
|
)
|
||||||
if dlg.run() == gtk.RESPONSE_REJECT:
|
response = dlg.run()
|
||||||
pass
|
if response == gtk.RESPONSE_ACCEPT:
|
||||||
if dlg.run() == gtk.RESPONSE_ACCEPT:
|
|
||||||
database = dlg.get_widget_database_properties().get_database()
|
database = dlg.get_widget_database_properties().get_database()
|
||||||
#TODO: sanity checks + init databse if necessary
|
#TODO: initing may or may not take a while. how to handle?
|
||||||
self.databaseManager.add_database(database)
|
error = database.init_new_database()
|
||||||
self.treeDatabases.get_model().append( (database.name, 'foo', database.Type, self.databaseManager.database_id(database)) )
|
if error:
|
||||||
|
DialogError(parent=dlg, msg=error)
|
||||||
dlg.destroy()
|
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):
|
def on_button_database_add_clicked(self, button):
|
||||||
databaseType = self.databaseManager.get_default_database_type()
|
databaseKlass = self.databaseManager.get_default_database_type()
|
||||||
if databaseType is None:
|
if databaseKlass is None:
|
||||||
raise ValueError('no defult database type set')
|
raise ValueError('no defult database type set')
|
||||||
|
database = databaseKlass()
|
||||||
|
|
||||||
|
while True:
|
||||||
dlg = DialogDatabaseProperties(
|
dlg = DialogDatabaseProperties(
|
||||||
self.databaseManager,
|
self.databaseManager,
|
||||||
databaseType(),
|
database,
|
||||||
parent=self.parentWidget,
|
parent=self.parentWidget,
|
||||||
mode=WidgetDatabaseProperties.ModeAdd,
|
mode=WidgetDatabaseProperties.ModeAdd,
|
||||||
title='[Add database] - database properties'
|
title='Add database'
|
||||||
)
|
)
|
||||||
if dlg.run() == gtk.RESPONSE_REJECT:
|
response = dlg.run()
|
||||||
pass
|
if response == gtk.RESPONSE_ACCEPT:
|
||||||
if dlg.run() == gtk.RESPONSE_ACCEPT:
|
|
||||||
database = dlg.get_widget_database_properties().get_database()
|
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.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()
|
dlg.destroy()
|
||||||
|
|
||||||
def on_button_database_edit_clicked(self, button):
|
def on_button_database_edit_clicked(self, button):
|
||||||
|
@ -501,39 +738,52 @@ class WidgetDatabaseManager(gtk.VBox):
|
||||||
if selection is None:
|
if selection is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
model, iter = selection.get_selected()
|
model, it = selection.get_selected()
|
||||||
idDatabase = model.get_value(iter, self.treeDatabaseColumns['_id'])
|
idDatabase = model.get_value(it, self.treeDatabaseColumns['_id'])
|
||||||
database = self.databaseManager.database_from_id(idDatabase)
|
database = self.databaseManager.database_from_id(idDatabase)
|
||||||
dlg = DialogDatabaseProperties(
|
dlg = DialogDatabaseProperties(
|
||||||
self.databaseManager,
|
self.databaseManager,
|
||||||
database=database,
|
database,
|
||||||
parent=self.parentWidget,
|
parent=self.parentWidget,
|
||||||
mode=WidgetDatabaseProperties.ModeEdit,
|
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
|
pass
|
||||||
if dlg.run() == gtk.RESPONSE_ACCEPT:
|
elif response == gtk.RESPONSE_ACCEPT:
|
||||||
database = dlg.get_database()
|
database = dlg.get_database()
|
||||||
selection = self.treeDatabases.get_selection()
|
selection = self.treeDatabases.get_selection()
|
||||||
if selection is not None:
|
if selection is not None:
|
||||||
model, iter = selection.get_selected()
|
model, it = selection.get_selected()
|
||||||
model.set_value(iter, 0, database.name)
|
model.set_value(it, self.treeDatabaseColumns['name'], database.name)
|
||||||
dlg.destroy()
|
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):
|
def on_tree_databases_selection_changed(self, treeSelection):
|
||||||
hasSelection = bool(treeSelection.count_selected_rows())
|
hasSelection = bool(treeSelection.count_selected_rows())
|
||||||
|
|
||||||
# enable/disable selection dependend widgets
|
# enable/disable selection dependend widgets
|
||||||
|
self.buttonDatabaseActivate.set_sensitive(hasSelection)
|
||||||
self.buttonDatabaseEdit.set_sensitive(hasSelection)
|
self.buttonDatabaseEdit.set_sensitive(hasSelection)
|
||||||
self.buttonDatabaseRemove.set_sensitive(hasSelection)
|
self.buttonDatabaseRemove.set_sensitive(hasSelection)
|
||||||
self.buttonDatabaseDelete.set_sensitive(hasSelection)
|
#self.buttonDatabaseDelete.set_sensitive(hasSelection)
|
||||||
|
|
||||||
|
|
||||||
class DialogDatabaseManager(gtk.Dialog):
|
class DialogDatabaseManager(gtk.Dialog):
|
||||||
def __init__(self, databaseManager, parent=None):
|
def __init__(self, databaseManager, parent=None):
|
||||||
gtk.Dialog.__init__(self,
|
gtk.Dialog.__init__(self,
|
||||||
title="My dialog",
|
title="Databases",
|
||||||
parent=parent,
|
parent=parent,
|
||||||
flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
|
flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
|
||||||
buttons=(
|
buttons=(
|
||||||
|
|
|
@ -18,6 +18,13 @@
|
||||||
#fpdb modules
|
#fpdb modules
|
||||||
import Card
|
import Card
|
||||||
|
|
||||||
|
DEBUG = True
|
||||||
|
|
||||||
|
if DEBUG:
|
||||||
|
import pprint
|
||||||
|
pp = pprint.PrettyPrinter(indent=4)
|
||||||
|
|
||||||
|
|
||||||
class DerivedStats():
|
class DerivedStats():
|
||||||
def __init__(self, hand):
|
def __init__(self, hand):
|
||||||
self.hand = hand
|
self.hand = hand
|
||||||
|
@ -30,13 +37,19 @@ class DerivedStats():
|
||||||
for player in hand.players:
|
for player in hand.players:
|
||||||
self.handsplayers[player[1]] = {}
|
self.handsplayers[player[1]] = {}
|
||||||
#Init vars that may not be used, but still need to be inserted.
|
#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.handsplayers[player[1]]['street4Aggr'] = False
|
||||||
|
|
||||||
self.assembleHands(self.hand)
|
self.assembleHands(self.hand)
|
||||||
self.assembleHandsPlayers(self.hand)
|
self.assembleHandsPlayers(self.hand)
|
||||||
|
|
||||||
print "hands =", self.hands
|
if DEBUG:
|
||||||
print "handsplayers =", self.handsplayers
|
print "Hands:"
|
||||||
|
pp.pprint(self.hands)
|
||||||
|
print "HandsPlayers:"
|
||||||
|
pp.pprint(self.handsplayers)
|
||||||
|
|
||||||
def getHands(self):
|
def getHands(self):
|
||||||
return self.hands
|
return self.hands
|
||||||
|
@ -85,11 +98,20 @@ class DerivedStats():
|
||||||
# commentTs DATETIME
|
# commentTs DATETIME
|
||||||
|
|
||||||
def assembleHandsPlayers(self, hand):
|
def assembleHandsPlayers(self, hand):
|
||||||
|
#street0VPI/vpip already called in Hand
|
||||||
#hand.players = [[seat, name, chips],[seat, name, chips]]
|
#hand.players = [[seat, name, chips],[seat, name, chips]]
|
||||||
for player in hand.players:
|
for player in hand.players:
|
||||||
self.handsplayers[player[1]]['seatNo'] = player[0]
|
self.handsplayers[player[1]]['seatNo'] = player[0]
|
||||||
self.handsplayers[player[1]]['startCash'] = player[2]
|
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:]):
|
for i, street in enumerate(hand.actionStreets[1:]):
|
||||||
self.aggr(self.hand, i)
|
self.aggr(self.hand, i)
|
||||||
|
|
||||||
|
@ -794,9 +816,9 @@ class DerivedStats():
|
||||||
|
|
||||||
for player in hand.players:
|
for player in hand.players:
|
||||||
if player[1] in vpipers:
|
if player[1] in vpipers:
|
||||||
self.handsplayers[player[1]]['vpip'] = True
|
self.handsplayers[player[1]]['street0VPI'] = True
|
||||||
else:
|
else:
|
||||||
self.handsplayers[player[1]]['vpip'] = False
|
self.handsplayers[player[1]]['street0VPI'] = False
|
||||||
|
|
||||||
def playersAtStreetX(self, hand):
|
def playersAtStreetX(self, hand):
|
||||||
""" playersAtStreet1 SMALLINT NOT NULL, /* num of players seeing flop/street4/draw1 */"""
|
""" 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['street3Raises'] = 0 # /* num big bets paid to see sd/street7 */
|
||||||
self.hands['street4Raises'] = 0 # /* num big bets paid to see showdown */
|
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):
|
def aggr(self, hand, i):
|
||||||
aggrers = set()
|
aggrers = set()
|
||||||
for act in hand.actions[hand.actionStreets[i]]:
|
for act in hand.actions[hand.actionStreets[i]]:
|
||||||
|
|
|
@ -570,8 +570,8 @@ Left-Drag to Move"
|
||||||
</hhcs>
|
</hhcs>
|
||||||
|
|
||||||
<supported_databases>
|
<supported_databases>
|
||||||
<database db_name="fpdb" db_server="mysql" db_ip="localhost" db_user="fpdb" db_pass="YOUR MYSQL PASSWORD" db_type="fpdb"></database>
|
<database db_name="fpdb" db_server="mysql" db_ip="localhost" db_user="fpdb" db_pass="YOUR MYSQL PASSWORD"></database>
|
||||||
<!-- <database db_ip="localhost" db_name="fpdb" db_pass="fpdb" db_server="sqlite" db_type="fpdb" db_user="fpdb"/> -->
|
<!-- <database db_ip="localhost" db_name="fpdb" db_pass="fpdb" db_server="sqlite" db_user="fpdb"/> -->
|
||||||
</supported_databases>
|
</supported_databases>
|
||||||
|
|
||||||
</FreePokerToolsConfig>
|
</FreePokerToolsConfig>
|
||||||
|
|
|
@ -37,7 +37,8 @@ if __name__== "__main__":
|
||||||
HUD_main.config = Configuration.Config()
|
HUD_main.config = Configuration.Config()
|
||||||
|
|
||||||
gobject.threads_init() # this is required
|
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 = gtk.Window()
|
||||||
HUD_main.main_window.connect("destroy", destroy)
|
HUD_main.main_window.connect("destroy", destroy)
|
||||||
|
|
303
pyfpdb/SQL.py
303
pyfpdb/SQL.py
|
@ -33,12 +33,11 @@ import re
|
||||||
|
|
||||||
class Sql:
|
class Sql:
|
||||||
|
|
||||||
def __init__(self, game = 'holdem', type = 'fpdb', db_server = 'mysql'):
|
def __init__(self, game = 'holdem', db_server = 'mysql'):
|
||||||
self.query = {}
|
self.query = {}
|
||||||
###############################################################################3
|
###############################################################################3
|
||||||
# Support for the Free Poker DataBase = fpdb http://fpdb.sourceforge.net/
|
# Support for the Free Poker DataBase = fpdb http://fpdb.sourceforge.net/
|
||||||
#
|
#
|
||||||
if type == 'fpdb':
|
|
||||||
|
|
||||||
################################
|
################################
|
||||||
# List tables
|
# List tables
|
||||||
|
@ -213,7 +212,7 @@ class Sql:
|
||||||
if db_server == 'mysql':
|
if db_server == 'mysql':
|
||||||
self.query['createHandsTable'] = """CREATE TABLE Hands (
|
self.query['createHandsTable'] = """CREATE TABLE Hands (
|
||||||
id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
|
id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
|
||||||
tableName VARCHAR(20) NOT NULL,
|
tableName VARCHAR(22) NOT NULL,
|
||||||
siteHandNo BIGINT NOT NULL,
|
siteHandNo BIGINT NOT NULL,
|
||||||
gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
|
gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
|
||||||
handStart DATETIME NOT NULL,
|
handStart DATETIME NOT NULL,
|
||||||
|
@ -248,7 +247,7 @@ class Sql:
|
||||||
elif db_server == 'postgresql':
|
elif db_server == 'postgresql':
|
||||||
self.query['createHandsTable'] = """CREATE TABLE Hands (
|
self.query['createHandsTable'] = """CREATE TABLE Hands (
|
||||||
id BIGSERIAL, PRIMARY KEY (id),
|
id BIGSERIAL, PRIMARY KEY (id),
|
||||||
tableName VARCHAR(20) NOT NULL,
|
tableName VARCHAR(22) NOT NULL,
|
||||||
siteHandNo BIGINT NOT NULL,
|
siteHandNo BIGINT NOT NULL,
|
||||||
gametypeId INT NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
|
gametypeId INT NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
|
||||||
handStart timestamp without time zone NOT NULL,
|
handStart timestamp without time zone NOT NULL,
|
||||||
|
@ -282,7 +281,7 @@ class Sql:
|
||||||
elif db_server == 'sqlite':
|
elif db_server == 'sqlite':
|
||||||
self.query['createHandsTable'] = """CREATE TABLE Hands (
|
self.query['createHandsTable'] = """CREATE TABLE Hands (
|
||||||
id INTEGER PRIMARY KEY,
|
id INTEGER PRIMARY KEY,
|
||||||
tableName TEXT(20) NOT NULL,
|
tableName TEXT(22) NOT NULL,
|
||||||
siteHandNo INT NOT NULL,
|
siteHandNo INT NOT NULL,
|
||||||
gametypeId INT NOT NULL,
|
gametypeId INT NOT NULL,
|
||||||
handStart REAL NOT NULL,
|
handStart REAL NOT NULL,
|
||||||
|
@ -1646,15 +1645,6 @@ class Sql:
|
||||||
where Id = %s
|
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':
|
if db_server == 'mysql':
|
||||||
self.query['get_hand_1day_ago'] = """
|
self.query['get_hand_1day_ago'] = """
|
||||||
select coalesce(max(id),0)
|
select coalesce(max(id),0)
|
||||||
|
@ -1974,6 +1964,271 @@ class Sql:
|
||||||
,s.name
|
,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':
|
if db_server == 'mysql':
|
||||||
self.query['playerStats'] = """
|
self.query['playerStats'] = """
|
||||||
SELECT
|
SELECT
|
||||||
|
@ -2476,6 +2731,25 @@ class Sql:
|
||||||
GROUP BY h.handStart, hp.handId, hp.totalProfit
|
GROUP BY h.handStart, hp.handId, hp.totalProfit
|
||||||
ORDER BY h.handStart"""
|
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
|
# Session stats query
|
||||||
####################################
|
####################################
|
||||||
|
@ -2668,6 +2942,7 @@ class Sql:
|
||||||
,hc_position
|
,hc_position
|
||||||
,hp.tourneyTypeId
|
,hp.tourneyTypeId
|
||||||
,date_format(h.handStart, 'd%y%m%d')
|
,date_format(h.handStart, 'd%y%m%d')
|
||||||
|
>>>>>>> 28ca49d592c8e706ad6ee58dd26655bcc33fc5fb:pyfpdb/SQL.py
|
||||||
"""
|
"""
|
||||||
elif db_server == 'postgresql':
|
elif db_server == 'postgresql':
|
||||||
self.query['rebuildHudCache'] = """
|
self.query['rebuildHudCache'] = """
|
||||||
|
|
|
@ -450,7 +450,7 @@ class fpdb:
|
||||||
if self.db is not None and self.db.fdb is not None:
|
if self.db is not None and self.db.fdb is not None:
|
||||||
self.db.disconnect()
|
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:
|
try:
|
||||||
self.db = Database.Database(self.config, sql = self.sql)
|
self.db = Database.Database(self.config, sql = self.sql)
|
||||||
except FpdbMySQLFailedError:
|
except FpdbMySQLFailedError:
|
||||||
|
|
|
@ -33,12 +33,12 @@ except ImportError:
|
||||||
|
|
||||||
import fpdb_simple
|
import fpdb_simple
|
||||||
import FpdbSQLQueries
|
import FpdbSQLQueries
|
||||||
|
import Configuration
|
||||||
|
|
||||||
class fpdb_db:
|
class fpdb_db:
|
||||||
MYSQL_INNODB = 2
|
MYSQL_INNODB = 2
|
||||||
PGSQL = 3
|
PGSQL = 3
|
||||||
SQLITE = 4
|
SQLITE = 4
|
||||||
sqlite_db_dir = ".." + os.sep + "database"
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""Simple constructor, doesnt really do anything"""
|
"""Simple constructor, doesnt really do anything"""
|
||||||
|
@ -123,10 +123,10 @@ class fpdb_db:
|
||||||
else:
|
else:
|
||||||
logging.warning("SQLite won't work well without 'sqlalchemy' installed.")
|
logging.warning("SQLite won't work well without 'sqlalchemy' installed.")
|
||||||
|
|
||||||
if not os.path.isdir(self.sqlite_db_dir):
|
if not os.path.isdir(Configuration.DIR_DATABASES):
|
||||||
print "Creating directory: '%s'" % (self.sqlite_db_dir)
|
print "Creating directory: '%s'" % (Configuration.DIR_DATABASES)
|
||||||
os.mkdir(self.sqlite_db_dir)
|
os.mkdir(Configuration.DIR_DATABASES)
|
||||||
self.db = sqlite3.connect( self.sqlite_db_dir + os.sep + database
|
self.db = sqlite3.connect( os.path.join(Configuration.DIR_DATABASES, database)
|
||||||
, detect_types=sqlite3.PARSE_DECLTYPES )
|
, detect_types=sqlite3.PARSE_DECLTYPES )
|
||||||
sqlite3.register_converter("bool", lambda x: bool(int(x)))
|
sqlite3.register_converter("bool", lambda x: bool(int(x)))
|
||||||
sqlite3.register_adapter(bool, lambda x: "1" if x else "0")
|
sqlite3.register_adapter(bool, lambda x: "1" if x else "0")
|
||||||
|
|
|
@ -60,13 +60,9 @@ class InterProcessLockBase:
|
||||||
self._has_lock = False
|
self._has_lock = False
|
||||||
|
|
||||||
def locked(self):
|
def locked(self):
|
||||||
if self._has_lock:
|
if self.acquire():
|
||||||
return True
|
|
||||||
try:
|
|
||||||
self.acquire()
|
|
||||||
self.release()
|
self.release()
|
||||||
return False
|
return False
|
||||||
except SingleInstanceError:
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
LOCK_FILE_DIRECTORY = '/tmp'
|
LOCK_FILE_DIRECTORY = '/tmp'
|
||||||
|
@ -162,38 +158,33 @@ def test_construct():
|
||||||
|
|
||||||
>>> lock1 = InterProcessLock(name=test_name)
|
>>> lock1 = InterProcessLock(name=test_name)
|
||||||
>>> lock1.acquire()
|
>>> lock1.acquire()
|
||||||
|
True
|
||||||
|
|
||||||
>>> lock2 = InterProcessLock(name=test_name)
|
>>> lock2 = InterProcessLock(name=test_name)
|
||||||
>>> lock3 = InterProcessLock(name=test_name)
|
>>> lock3 = InterProcessLock(name=test_name)
|
||||||
|
|
||||||
# Since lock1 is locked, other attempts to acquire it fail.
|
# Since lock1 is locked, other attempts to acquire it fail.
|
||||||
>>> lock2.acquire()
|
>>> lock2.acquire()
|
||||||
Traceback (most recent call last):
|
False
|
||||||
...
|
|
||||||
SingleInstanceError: Could not acquire exclusive lock on /tmp/test.lck
|
|
||||||
|
|
||||||
>>> lock3.acquire()
|
>>> lock3.acquire()
|
||||||
Traceback (most recent call last):
|
False
|
||||||
...
|
|
||||||
SingleInstanceError: Could not acquire exclusive lock on /tmp/test.lck
|
|
||||||
|
|
||||||
# Release the lock and let lock2 have it.
|
# Release the lock and let lock2 have it.
|
||||||
>>> lock1.release()
|
>>> lock1.release()
|
||||||
>>> lock2.acquire()
|
>>> lock2.acquire()
|
||||||
|
True
|
||||||
|
|
||||||
>>> lock3.acquire()
|
>>> lock3.acquire()
|
||||||
Traceback (most recent call last):
|
False
|
||||||
...
|
|
||||||
SingleInstanceError: Could not acquire exclusive lock on /tmp/test.lck
|
|
||||||
|
|
||||||
# Release it and give it back to lock1
|
# Release it and give it back to lock1
|
||||||
>>> lock2.release()
|
>>> lock2.release()
|
||||||
>>> lock1.acquire()
|
>>> lock1.acquire()
|
||||||
|
True
|
||||||
|
|
||||||
>>> lock2.acquire()
|
>>> lock2.acquire()
|
||||||
Traceback (most recent call last):
|
False
|
||||||
...
|
|
||||||
SingleInstanceError: Could not acquire exclusive lock on /tmp/test.lck
|
|
||||||
|
|
||||||
# Test lock status
|
# Test lock status
|
||||||
>>> lock2.locked()
|
>>> lock2.locked()
|
||||||
|
@ -224,7 +215,7 @@ def test_construct():
|
||||||
... import win32con
|
... import win32con
|
||||||
... import pywintypes
|
... import pywintypes
|
||||||
... handle = win32api.OpenProcess(win32con.PROCESS_TERMINATE , pywintypes.FALSE, pid)
|
... handle = win32api.OpenProcess(win32con.PROCESS_TERMINATE , pywintypes.FALSE, pid)
|
||||||
... return (0 != win32api.TerminateProcess(handle, 0))
|
... #return (0 != win32api.TerminateProcess(handle, 0))
|
||||||
|
|
||||||
# Test to acquire the lock in another process.
|
# Test to acquire the lock in another process.
|
||||||
>>> def execute(cmd):
|
>>> def execute(cmd):
|
||||||
|
@ -237,15 +228,14 @@ def test_construct():
|
||||||
>>> pid = execute('import interlocks;a=interlocks.InterProcessLock(name=\\''+test_name+ '\\');a.acquire();')
|
>>> pid = execute('import interlocks;a=interlocks.InterProcessLock(name=\\''+test_name+ '\\');a.acquire();')
|
||||||
|
|
||||||
>>> lock1.acquire()
|
>>> lock1.acquire()
|
||||||
Traceback (most recent call last):
|
False
|
||||||
...
|
|
||||||
SingleInstanceError: Could not acquire exclusive lock on /tmp/test.lck
|
|
||||||
|
|
||||||
>>> os_independent_kill(pid)
|
>>> os_independent_kill(pid)
|
||||||
|
|
||||||
>>> time.sleep(1)
|
>>> time.sleep(1)
|
||||||
|
|
||||||
>>> lock1.acquire()
|
>>> lock1.acquire()
|
||||||
|
True
|
||||||
>>> lock1.release()
|
>>> lock1.release()
|
||||||
|
|
||||||
# Testing wait
|
# Testing wait
|
||||||
|
@ -253,13 +243,12 @@ def test_construct():
|
||||||
>>> pid = execute('import interlocks;a=interlocks.InterProcessLock(name=\\''+test_name+ '\\');a.acquire();')
|
>>> pid = execute('import interlocks;a=interlocks.InterProcessLock(name=\\''+test_name+ '\\');a.acquire();')
|
||||||
|
|
||||||
>>> lock1.acquire()
|
>>> lock1.acquire()
|
||||||
Traceback (most recent call last):
|
False
|
||||||
...
|
|
||||||
SingleInstanceError: Could not acquire exclusive lock on /tmp/test.lck
|
|
||||||
|
|
||||||
>>> os_independent_kill(pid)
|
>>> os_independent_kill(pid)
|
||||||
|
|
||||||
>>> lock1.acquire(True)
|
>>> lock1.acquire(True)
|
||||||
|
True
|
||||||
>>> lock1.release()
|
>>> lock1.release()
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in New Issue
Block a user