8d78e11a4f
In case someone needs to connect to a DB engine in non-standard port, defining the TCP port is required. This is a rare corner case which should only arise when a given DB host runs multiple versions of the same DB engine. However, the support is relatively straightforward to implement and it shouldn't conflict with any other options.
1541 lines
66 KiB
Python
1541 lines
66 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
"""Configuration.py
|
|
|
|
Handles HUD configuration files.
|
|
"""
|
|
# Copyright 2008-2010, Ray E. Barker
|
|
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
########################################################################
|
|
|
|
|
|
# Standard Library modules
|
|
from __future__ import with_statement
|
|
|
|
import L10n
|
|
_ = L10n.get_translation()
|
|
|
|
import os
|
|
import sys
|
|
import inspect
|
|
import string
|
|
import traceback
|
|
import shutil
|
|
import locale
|
|
import re
|
|
import xml.dom.minidom
|
|
from xml.dom.minidom import Node
|
|
|
|
import logging, logging.config
|
|
import ConfigParser
|
|
|
|
# logging has been set up in fpdb.py or HUD_main.py, use their settings:
|
|
log = logging.getLogger("config")
|
|
|
|
|
|
##############################################################################
|
|
# Functions for finding config files and setting up logging
|
|
# Also used in other modules that use logging.
|
|
|
|
def get_default_config_path():
|
|
"""Returns the path where the fpdb config file _should_ be stored."""
|
|
if os.name == 'posix':
|
|
config_path = os.path.join(os.path.expanduser("~"), '.fpdb')
|
|
elif os.name == 'nt':
|
|
config_path = os.path.join(unicode(os.environ[u"APPDATA"], "latin-1"), u"fpdb")
|
|
#print u"path after joining in get_default_config_path:",config_path
|
|
else: config_path = False
|
|
|
|
try: os.mkdir(config_path)
|
|
except: pass
|
|
|
|
return config_path
|
|
|
|
def get_exec_path():
|
|
"""Returns the path to the fpdb(dir|.exe) file we are executing"""
|
|
if hasattr(sys, "frozen"): # compiled by py2exe
|
|
return os.path.dirname(sys.executable)
|
|
else:
|
|
return os.path.dirname(sys.path[0]) # should be path to /fpdb
|
|
|
|
def get_config(file_name, fallback = True):
|
|
"""Looks in cwd and in self.default_config_path for a config file."""
|
|
|
|
# look for example file even if not used here, path is returned to caller
|
|
config_found,example_found,example_copy = False,False,False
|
|
config_path, example_path = None,None
|
|
|
|
exec_dir = get_exec_path()
|
|
if file_name == 'logging.conf' and not hasattr(sys, "frozen"):
|
|
config_path = os.path.join(exec_dir, 'pyfpdb', file_name)
|
|
else:
|
|
config_path = os.path.join(exec_dir, file_name)
|
|
# print "config_path=", config_path
|
|
if os.path.exists(config_path): # there is a file in the cwd
|
|
config_found = True # so we use it
|
|
else: # no file in the cwd, look where it should be in the first place
|
|
default_dir = get_default_config_path()
|
|
config_path = os.path.join(default_dir, file_name)
|
|
# print "config path 2=", config_path
|
|
if os.path.exists(config_path):
|
|
config_found = True
|
|
|
|
# Example configuration for debian package
|
|
if os.name == 'posix':
|
|
# If we're on linux, try to copy example from the place
|
|
# debian package puts it; get_default_config_path() creates
|
|
# the config directory for us so there's no need to check it
|
|
# again
|
|
example_path = '/usr/share/python-fpdb/' + file_name + '.example'
|
|
if not config_found and fallback:
|
|
try:
|
|
shutil.copyfile(example_path, config_path)
|
|
example_copy = True
|
|
msg = _("Config file has been created at %s.\n") % config_path
|
|
logging.info(msg)
|
|
except IOError:
|
|
pass
|
|
|
|
# OK, fall back to the .example file, should be in the start dir
|
|
elif os.path.exists(file_name + ".example"):
|
|
try:
|
|
print ""
|
|
example_path = file_name + ".example"
|
|
check_dir(default_dir)
|
|
if not config_found and fallback:
|
|
shutil.copyfile(example_path, config_path)
|
|
example_copy = True
|
|
msg = _("No %s found\n in %s\n or %s\n") % (file_name, exec_dir, default_dir) \
|
|
+ _("Config file has been created at %s.\n") % config_path
|
|
print msg
|
|
logging.info(msg)
|
|
except:
|
|
print _("Error copying .example config file, cannot fall back. Exiting.\n")
|
|
sys.stderr.write(_("Error copying .example config file, cannot fall back. Exiting.\n"))
|
|
sys.stderr.write( str(sys.exc_info()) )
|
|
sys.exit()
|
|
elif fallback:
|
|
print _("No %s found, cannot fall back. Exiting.\n") % file_name
|
|
sys.stderr.write(_("No %s found, cannot fall back. Exiting.\n") % file_name)
|
|
sys.exit()
|
|
|
|
#print "get_config: returning "+str( (config_path,example_copy,example_path) )
|
|
return (config_path,example_copy,example_path)
|
|
|
|
def get_logger(file_name, config = "config", fallback = False, log_dir=None, log_file=None):
|
|
(conf_file,copied,example_file) = get_config(file_name, fallback = fallback)
|
|
|
|
if log_dir is None:
|
|
log_dir = os.path.join(get_exec_path(), u'log')
|
|
#print "\nget_logger: checking log_dir:", log_dir
|
|
check_dir(log_dir)
|
|
if log_file is None:
|
|
file = os.path.join(log_dir, 'fpdb-log.txt')
|
|
else:
|
|
file = os.path.join(log_dir, log_file)
|
|
|
|
if conf_file:
|
|
try:
|
|
file = file.replace('\\', '\\\\') # replace each \ with \\
|
|
# print " ="+file+" "+ str(type(file))+" len="+str(len(file))+"\n"
|
|
logging.config.fileConfig(conf_file, {"logFile":file})
|
|
log = logging.getLogger(config)
|
|
log.debug("%s logger initialised" % config)
|
|
return log
|
|
except:
|
|
pass
|
|
|
|
log = logging.basicConfig(filename=file, level=logging.INFO)
|
|
log = logging.getLogger()
|
|
# but it looks like default is no output :-( maybe because all the calls name a module?
|
|
log.debug(_("Default logger initialised for ")+file)
|
|
print _("Default logger intialised for ")+file
|
|
return log
|
|
|
|
def check_dir(path, create = True):
|
|
"""Check if a dir exists, optionally creates if not."""
|
|
if os.path.exists(path):
|
|
if os.path.isdir(path):
|
|
return path
|
|
else:
|
|
return False
|
|
if create:
|
|
msg = _("Creating directory: '%s'") % (path)
|
|
print msg
|
|
log.info(msg)
|
|
os.mkdir(path)#, "utf-8"))
|
|
else:
|
|
return False
|
|
|
|
|
|
########################################################################
|
|
# application wide consts
|
|
|
|
APPLICATION_NAME_SHORT = 'fpdb'
|
|
APPLICATION_VERSION = 'xx.xx.xx'
|
|
|
|
DATABASE_TYPE_POSTGRESQL = 'postgresql'
|
|
DATABASE_TYPE_SQLITE = 'sqlite'
|
|
DATABASE_TYPE_MYSQL = 'mysql'
|
|
DATABASE_TYPES = (
|
|
DATABASE_TYPE_POSTGRESQL,
|
|
DATABASE_TYPE_SQLITE,
|
|
DATABASE_TYPE_MYSQL,
|
|
)
|
|
|
|
#LOCALE_ENCODING = locale.getdefaultlocale()[1]
|
|
LOCALE_ENCODING = locale.getpreferredencoding()
|
|
if LOCALE_ENCODING == "US-ASCII":
|
|
print _("Default encoding set to US-ASCII, defaulting to CP1252 instead -- If you're not on a Mac, please report this problem.")
|
|
LOCALE_ENCODING = "cp1252"
|
|
|
|
|
|
# needs LOCALE_ENCODING (above), imported for sqlite setup in Config class below
|
|
|
|
FROZEN = hasattr(sys, "frozen")
|
|
EXEC_PATH = get_exec_path()
|
|
|
|
import Charset
|
|
|
|
|
|
########################################################################
|
|
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
|
|
elif string in ('0', 'false', 'f'):
|
|
return False
|
|
return default
|
|
|
|
class Layout:
|
|
def __init__(self, node):
|
|
|
|
self.max = int( node.getAttribute('max') )
|
|
if node.hasAttribute('fav_seat'): self.fav_seat = int( node.getAttribute('fav_seat') )
|
|
self.width = int( node.getAttribute('width') )
|
|
self.height = int( node.getAttribute('height') )
|
|
|
|
self.location = []
|
|
self.location = map(lambda x: None, range(self.max+1)) # fill array with max seats+1 empty entries
|
|
|
|
for location_node in node.getElementsByTagName('location'):
|
|
if location_node.getAttribute('seat') != "":
|
|
self.location[int( location_node.getAttribute('seat') )] = (int( location_node.getAttribute('x') ), int( location_node.getAttribute('y')))
|
|
elif location_node.getAttribute('common') != "":
|
|
self.common = (int( location_node.getAttribute('x') ), int( location_node.getAttribute('y')))
|
|
|
|
def __str__(self):
|
|
temp = " Layout = %d max, width= %d, height = %d" % (self.max, self.width, self.height)
|
|
if hasattr(self, 'fav_seat'): temp = temp + ", fav_seat = %d\n" % self.fav_seat
|
|
else: temp = temp + "\n"
|
|
if hasattr(self, "common"):
|
|
temp = temp + " Common = (%d, %d)\n" % (self.common[0], self.common[1])
|
|
temp = temp + " Locations = "
|
|
for i in range(1, len(self.location)):
|
|
temp = temp + "(%d,%d)" % self.location[i]
|
|
|
|
return temp + "\n"
|
|
|
|
class Email:
|
|
def __init__(self, node):
|
|
self.node = node
|
|
self.host= node.getAttribute("host")
|
|
self.username = node.getAttribute("username")
|
|
self.password = node.getAttribute("password")
|
|
self.useSsl = node.getAttribute("useSsl")
|
|
self.folder = node.getAttribute("folder")
|
|
self.fetchType = node.getAttribute("fetchType")
|
|
|
|
def __str__(self):
|
|
return " fetchType=%s\n host = %s\n username = %s\n password = %s\n useSsl = %s\n folder = %s\n" \
|
|
% (self.fetchType, self.host, self.username, self.password, self.useSsl, self.folder)
|
|
|
|
class Site:
|
|
def __init__(self, node):
|
|
def normalizePath(path):
|
|
"Normalized existing pathes"
|
|
if os.path.exists(path):
|
|
return os.path.abspath(path)
|
|
return path
|
|
|
|
self.site_name = node.getAttribute("site_name")
|
|
self.table_finder = node.getAttribute("table_finder")
|
|
self.screen_name = node.getAttribute("screen_name")
|
|
self.site_path = normalizePath(node.getAttribute("site_path"))
|
|
self.HH_path = normalizePath(node.getAttribute("HH_path"))
|
|
self.decoder = node.getAttribute("decoder")
|
|
self.hudopacity = node.getAttribute("hudopacity")
|
|
self.hudbgcolor = node.getAttribute("bgcolor")
|
|
self.hudfgcolor = node.getAttribute("fgcolor")
|
|
self.converter = node.getAttribute("converter")
|
|
self.aux_window = node.getAttribute("aux_window")
|
|
self.font = node.getAttribute("font")
|
|
self.font_size = node.getAttribute("font_size")
|
|
self.use_frames = node.getAttribute("use_frames")
|
|
self.enabled = string_to_bool(node.getAttribute("enabled"), default=True)
|
|
self.xpad = node.getAttribute("xpad")
|
|
self.ypad = node.getAttribute("ypad")
|
|
self.xshift = node.getAttribute("xshift")
|
|
self.yshift = node.getAttribute("yshift")
|
|
self.layout = {}
|
|
self.emails = {}
|
|
|
|
print _("Loading site"), self.site_name
|
|
|
|
for layout_node in node.getElementsByTagName('layout'):
|
|
lo = Layout(layout_node)
|
|
self.layout[lo.max] = lo
|
|
|
|
for email_node in node.getElementsByTagName('email'):
|
|
email = Email(email_node)
|
|
self.emails[email.fetchType] = email
|
|
|
|
# Site defaults
|
|
self.xpad = 1 if self.xpad == "" else int(self.xpad)
|
|
self.ypad = 0 if self.ypad == "" else int(self.ypad)
|
|
self.xshift = 1 if self.xshift == "" else int(self.xshift)
|
|
self.yshift = 0 if self.yshift == "" else int(self.yshift)
|
|
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"
|
|
if self.hudbgcolor == "": self.hudbgcolor = "#000000"
|
|
if self.hudfgcolor == "": self.hudfgcolor = "#FFFFFF"
|
|
|
|
def __str__(self):
|
|
temp = "Site = " + self.site_name + "\n"
|
|
for key in dir(self):
|
|
if key.startswith('__'): continue
|
|
if key == 'layout': continue
|
|
value = getattr(self, key)
|
|
if callable(value): continue
|
|
temp = temp + ' ' + key + " = " + str(value) + "\n"
|
|
|
|
for layout in self.layout:
|
|
temp = temp + "%s" % self.layout[layout]
|
|
|
|
return temp
|
|
|
|
class Stat:
|
|
def __init__(self):
|
|
pass
|
|
|
|
def __str__(self):
|
|
temp = " stat_name = %s, row = %d, col = %d, tip = %s, click = %s, popup = %s\n" % (self.stat_name, self.row, self.col, self.tip, self.click, self.popup)
|
|
return temp
|
|
|
|
class Game:
|
|
def __init__(self, node):
|
|
self.game_name = node.getAttribute("game_name")
|
|
self.rows = int( node.getAttribute("rows") )
|
|
self.cols = int( node.getAttribute("cols") )
|
|
self.xpad = node.getAttribute("xpad")
|
|
self.ypad = node.getAttribute("ypad")
|
|
self.xshift = node.getAttribute("xshift")
|
|
self.yshift = node.getAttribute("yshift")
|
|
|
|
# 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.xshift == "": self.xshift = 1
|
|
else: self.xshift = int(self.xshift)
|
|
if self.yshift == "": self.yshift = 0
|
|
else: self.yshift = int(self.yshift)
|
|
|
|
aux_text = node.getAttribute("aux")
|
|
aux_list = aux_text.split(',')
|
|
for i in range(0, len(aux_list)):
|
|
aux_list[i] = aux_list[i].strip()
|
|
self.aux = aux_list
|
|
|
|
self.stats = {}
|
|
for stat_node in node.getElementsByTagName('stat'):
|
|
stat = Stat()
|
|
stat.stat_name = stat_node.getAttribute("stat_name")
|
|
stat.row = int( stat_node.getAttribute("row") )
|
|
stat.col = int( stat_node.getAttribute("col") )
|
|
stat.tip = stat_node.getAttribute("tip")
|
|
stat.click = stat_node.getAttribute("click")
|
|
stat.popup = stat_node.getAttribute("popup")
|
|
stat.hudprefix = stat_node.getAttribute("hudprefix")
|
|
stat.hudsuffix = stat_node.getAttribute("hudsuffix")
|
|
stat.hudcolor = stat_node.getAttribute("hudcolor")
|
|
stat.stat_loth = stat_node.getAttribute("stat_loth")
|
|
stat.stat_hith = stat_node.getAttribute("stat_hith")
|
|
stat.stat_locolor = stat_node.getAttribute("stat_locolor")
|
|
stat.stat_hicolor = stat_node.getAttribute("stat_hicolor")
|
|
|
|
self.stats[stat.stat_name] = stat
|
|
|
|
def __str__(self):
|
|
temp = "Game = " + self.game_name + "\n"
|
|
temp = temp + " rows = %d\n" % self.rows
|
|
temp = temp + " cols = %d\n" % self.cols
|
|
temp = temp + " xpad = %d\n" % self.xpad
|
|
temp = temp + " ypad = %d\n" % self.ypad
|
|
temp = temp + " xshift = %d\n" % self.xshift
|
|
temp = temp + " yshift = %d\n" % self.yshift
|
|
temp = temp + " aux = %s\n" % self.aux
|
|
|
|
for stat in self.stats.keys():
|
|
temp = temp + "%s" % self.stats[stat]
|
|
|
|
return temp
|
|
|
|
class Database:
|
|
def __init__(self, node):
|
|
self.db_name = node.getAttribute("db_name")
|
|
self.db_desc = node.getAttribute("db_desc")
|
|
self.db_server = node.getAttribute("db_server").lower()
|
|
self.db_ip = node.getAttribute("db_ip")
|
|
self.db_port = node.getAttribute("db_port")
|
|
self.db_user = node.getAttribute("db_user")
|
|
self.db_pass = node.getAttribute("db_pass")
|
|
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_port:'%(port)s' db_user:'%(user)s' db_pass (not logged) selected:'%(sel)s'" \
|
|
% { 'name':self.db_name, 'server':self.db_server, 'ip':self.db_ip, 'port':self.db_port, 'user':self.db_user, 'sel':self.db_selected} )
|
|
|
|
def __str__(self):
|
|
temp = 'Database = ' + self.db_name + '\n'
|
|
for key in dir(self):
|
|
if key.startswith('__'): continue
|
|
value = getattr(self, key)
|
|
if callable(value): continue
|
|
temp = temp + ' ' + key + " = " + repr(value) + "\n"
|
|
return temp
|
|
|
|
class Aux_window:
|
|
def __init__(self, node):
|
|
for (name, value) in node.attributes.items():
|
|
setattr(self, name, value)
|
|
|
|
self.layout = {}
|
|
for layout_node in node.getElementsByTagName('layout'):
|
|
lo = Layout(layout_node)
|
|
self.layout[lo.max] = lo
|
|
|
|
def __str__(self):
|
|
temp = 'Aux = ' + self.name + "\n"
|
|
for key in dir(self):
|
|
if key.startswith('__'): continue
|
|
if key == 'layout': continue
|
|
value = getattr(self, key)
|
|
if callable(value): continue
|
|
temp = temp + ' ' + key + " = " + value + "\n"
|
|
|
|
for layout in self.layout:
|
|
temp = temp + "%s" % self.layout[layout]
|
|
return temp
|
|
|
|
class HHC:
|
|
def __init__(self, node):
|
|
self.site = node.getAttribute("site")
|
|
self.converter = node.getAttribute("converter")
|
|
self.summaryImporter = node.getAttribute("summaryImporter")
|
|
|
|
def __str__(self):
|
|
return "%s:\tconverter: '%s' summaryImporter: '%s'" % (self.site, self.converter, self.summaryImporter)
|
|
|
|
|
|
class Popup:
|
|
def __init__(self, node):
|
|
self.name = node.getAttribute("pu_name")
|
|
self.pu_stats = []
|
|
for stat_node in node.getElementsByTagName('pu_stat'):
|
|
self.pu_stats.append(stat_node.getAttribute("pu_stat_name"))
|
|
|
|
def __str__(self):
|
|
temp = "Popup = " + self.name + "\n"
|
|
for stat in self.pu_stats:
|
|
temp = temp + " " + stat
|
|
return temp + "\n"
|
|
|
|
class Import:
|
|
def __init__(self, node):
|
|
self.node = node
|
|
self.interval = node.getAttribute("interval")
|
|
self.callFpdbHud = node.getAttribute("callFpdbHud")
|
|
self.hhArchiveBase = node.getAttribute("hhArchiveBase")
|
|
self.hhBulkPath = node.getAttribute("hhBulkPath")
|
|
self.saveActions = string_to_bool(node.getAttribute("saveActions"), default=False)
|
|
self.cacheSessions = string_to_bool(node.getAttribute("cacheSessions"), default=False)
|
|
self.sessionTimeout = string_to_bool(node.getAttribute("sessionTimeout"), default=30)
|
|
self.fastStoreHudCache = string_to_bool(node.getAttribute("fastStoreHudCache"), default=False)
|
|
self.saveStarsHH = string_to_bool(node.getAttribute("saveStarsHH"), default=False)
|
|
|
|
def __str__(self):
|
|
return " interval = %s\n callFpdbHud = %s\n hhArchiveBase = %s\n saveActions = %s\n fastStoreHudCache = %s\n" \
|
|
% (self.interval, self.callFpdbHud, self.hhArchiveBase, self.saveActions, self.cacheSessions, self.sessionTimeout, self.fastStoreHudCache)
|
|
|
|
class HudUI:
|
|
def __init__(self, node):
|
|
self.node = node
|
|
self.label = node.getAttribute('label')
|
|
#
|
|
self.hud_style = node.getAttribute('stat_range')
|
|
self.hud_days = node.getAttribute('stat_days')
|
|
self.aggregate_ring = string_to_bool(node.getAttribute('aggregate_ring_game_stats'))
|
|
self.aggregate_tour = string_to_bool(node.getAttribute('aggregate_tourney_stats'))
|
|
self.agg_bb_mult = node.getAttribute('aggregation_level_multiplier')
|
|
#
|
|
self.h_hud_style = node.getAttribute('hero_stat_range')
|
|
self.h_hud_days = node.getAttribute('hero_stat_days')
|
|
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_agg_bb_mult = node.getAttribute('hero_aggregation_level_multiplier')
|
|
|
|
|
|
def __str__(self):
|
|
return " label = %s\n" % self.label
|
|
|
|
|
|
class General(dict):
|
|
def __init__(self):
|
|
super(General, self).__init__()
|
|
|
|
def add_elements(self, node):
|
|
# day_start - number n where 0.0 <= n < 24.0 representing start of day for user
|
|
# e.g. user could set to 4.0 for day to start at 4am local time
|
|
# [ HH_bulk_path was here - now moved to import section ]
|
|
for (name, value) in node.attributes.items():
|
|
log.debug(_("config.general: adding %s = %s") % (name,value))
|
|
self[name] = value
|
|
|
|
try:
|
|
self["version"]=int(self["version"])
|
|
except KeyError:
|
|
self["version"]=0
|
|
self["ui_language"]="system"
|
|
self["config_difficulty"]="expert"
|
|
|
|
def get_defaults(self):
|
|
self["version"]=0
|
|
self["ui_language"]="system"
|
|
self["config_difficulty"]="expert"
|
|
self["config_wrap_len"]="-1"
|
|
self["day_start"]="5"
|
|
|
|
def __str__(self):
|
|
s = ""
|
|
for k in self:
|
|
s = s + " %s = %s\n" % (k, self[k])
|
|
return(s)
|
|
|
|
class GUICashStats(list):
|
|
"""<gui_cash_stats>
|
|
<col col_name="game" col_title="Game" disp_all="True" disp_posn="True" field_format="%s" field_type="str" xalignment="0.0" />
|
|
...
|
|
</gui_cash_stats>
|
|
"""
|
|
def __init__(self):
|
|
super(GUICashStats, self).__init__()
|
|
|
|
def add_elements(self, node):
|
|
# is this needed?
|
|
for child in node.childNodes:
|
|
if child.nodeType == child.ELEMENT_NODE:
|
|
col_name, col_title, disp_all, disp_posn, field_format, field_type, xalignment=None, None, True, True, "%s", "str", 0.0
|
|
|
|
if child.hasAttribute('col_name'): col_name = child.getAttribute('col_name')
|
|
if child.hasAttribute('col_title'): col_title = child.getAttribute('col_title')
|
|
if child.hasAttribute('disp_all'): disp_all = string_to_bool(child.getAttribute('disp_all'))
|
|
if child.hasAttribute('disp_posn'): disp_posn = string_to_bool(child.getAttribute('disp_posn'))
|
|
if child.hasAttribute('field_format'): field_format = child.getAttribute('field_format')
|
|
if child.hasAttribute('field_type'): field_type = child.getAttribute('field_type')
|
|
try:
|
|
if child.hasAttribute('xalignment'): xalignment = float(child.getAttribute('xalignment'))
|
|
except ValueError:
|
|
print _("bad number in xalignment was ignored")
|
|
log.info(_("bad number in xalignment was ignored"))
|
|
|
|
self.append( [col_name, col_title, disp_all, disp_posn, field_format, field_type, xalignment] )
|
|
|
|
def get_defaults(self):
|
|
"""A list of defaults to be called, should there be no entry in config"""
|
|
# SQL column name, display title, display all, display positional, format, type, alignment
|
|
defaults = [ [u'game', u'Game', True, True, u'%s', u'str', 0.0],
|
|
[u'hand', u'Hand', False, False, u'%s', u'str', 0.0],
|
|
[u'plposition', u'Posn', False, False, u'%s', u'str', 1.0],
|
|
[u'pname', u'Name', False, False, u'%s', u'str', 0.0],
|
|
[u'n', u'Hds', True, True, u'%1.0f', u'str', 1.0],
|
|
[u'avgseats', u'Seats', False, False, u'%3.1f', u'str', 1.0],
|
|
[u'vpip', u'VPIP', True, True, u'%3.1f', u'str', 1.0],
|
|
[u'pfr', u'PFR', True, True, u'%3.1f', u'str', 1.0],
|
|
[u'pf3', u'PF3', True, True, u'%3.1f', u'str', 1.0],
|
|
[u'aggfac', u'AggFac', True, True, u'%2.2f', u'str', 1.0],
|
|
[u'aggfrq', u'AggFreq', True, True, u'%3.1f', u'str', 1.0],
|
|
[u'conbet', u'ContBet', True, True, u'%3.1f', u'str', 1.0],
|
|
[u'rfi', u'RFI', True, True, u'%3.1f', u'str', 1.0],
|
|
[u'steals', u'Steals', True, True, u'%3.1f', u'str', 1.0],
|
|
[u'saw_f', u'Saw_F', True, True, u'%3.1f', u'str', 1.0],
|
|
[u'sawsd', u'SawSD', True, True, u'%3.1f', u'str', 1.0],
|
|
[u'wtsdwsf', u'WtSDwsF', True, True, u'%3.1f', u'str', 1.0],
|
|
[u'wmsd', u'W$SD', True, True, u'%3.1f', u'str', 1.0],
|
|
[u'flafq', u'FlAFq', True, True, u'%3.1f', u'str', 1.0],
|
|
[u'tuafq', u'TuAFq', True, True, u'%3.1f', u'str', 1.0],
|
|
[u'rvafq', u'RvAFq', True, True, u'%3.1f', u'str', 1.0],
|
|
[u'pofafq', u'PoFAFq', False, False, u'%3.1f', u'str', 1.0],
|
|
[u'net', u'Net($)', True, True, u'%6.2f', u'cash', 1.0],
|
|
[u'bbper100', u'bb/100', True, True, u'%4.2f', u'str', 1.0],
|
|
[u'rake', u'Rake($)', True, True, u'%6.2f', u'cash', 1.0],
|
|
[u'bb100xr', u'bbxr/100', True, True, u'%4.2f', u'str', 1.0],
|
|
[u'variance', u'Variance', True, True, u'%5.2f', u'str', 1.0]
|
|
]
|
|
for col in defaults:
|
|
self.append (col)
|
|
|
|
# def __str__(self):
|
|
# s = ""
|
|
# for l in self:
|
|
# s = s + " %s = %s\n" % (k, self[k])
|
|
# return(s)
|
|
|
|
class RawHands:
|
|
def __init__(self, node=None):
|
|
if node==None:
|
|
self.save="error"
|
|
self.compression="none"
|
|
print _("missing config section raw_hands")
|
|
else:
|
|
save=node.getAttribute("save")
|
|
if save in ("none", "error", "all"):
|
|
self.save=save
|
|
else:
|
|
print _("Invalid config value for raw_hands.save, defaulting to \"error\"")
|
|
self.save="error"
|
|
|
|
compression=node.getAttribute("compression")
|
|
if save in ("none", "gzip", "bzip2"):
|
|
self.compression=compression
|
|
else:
|
|
print _("Invalid config value for raw_hands.compression, defaulting to \"none\"")
|
|
self.compression="none"
|
|
#end def __init__
|
|
|
|
def __str__(self):
|
|
return " save= %s, compression= %s\n" % (self.save, self.compression)
|
|
#end class RawHands
|
|
|
|
class RawTourneys:
|
|
def __init__(self, node=None):
|
|
if node==None:
|
|
self.save="error"
|
|
self.compression="none"
|
|
print _("missing config section raw_tourneys")
|
|
else:
|
|
save=node.getAttribute("save")
|
|
if save in ("none", "error", "all"):
|
|
self.save=save
|
|
else:
|
|
print _("Invalid config value for raw_tourneys.save, defaulting to \"error\"")
|
|
self.save="error"
|
|
|
|
compression=node.getAttribute("compression")
|
|
if save in ("none", "gzip", "bzip2"):
|
|
self.compression=compression
|
|
else:
|
|
print _("Invalid config value for raw_tourneys.compression, defaulting to \"none\"")
|
|
self.compression="none"
|
|
#end def __init__
|
|
|
|
def __str__(self):
|
|
return " save= %s, compression= %s\n" % (self.save, self.compression)
|
|
#end class RawTourneys
|
|
|
|
class Config:
|
|
def __init__(self, file = None, dbname = ''):
|
|
# "file" is a path to an xml file with the fpdb/HUD configuration
|
|
# we check the existence of "file" and try to recover if it doesn't exist
|
|
|
|
# self.default_config_path = self.get_default_config_path()
|
|
self.example_copy = False
|
|
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
|
|
|
|
self.example_copy,example_file = True,None
|
|
if file is None: (file,self.example_copy,example_file) = get_config("HUD_config.xml", True)
|
|
|
|
self.file = file
|
|
self.dir_self = get_exec_path()
|
|
# self.dir_config = os.path.dirname(self.file)
|
|
self.dir_config = get_default_config_path()
|
|
self.dir_log = os.path.join(self.dir_config, u'log')
|
|
self.dir_database = os.path.join(self.dir_config, u'database')
|
|
self.log_file = os.path.join(self.dir_log, u'fpdb-log.txt')
|
|
log = get_logger("logging.conf", "config", log_dir=self.dir_log)
|
|
|
|
self.supported_sites = {}
|
|
self.supported_games = {}
|
|
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.general = General()
|
|
self.emails = {}
|
|
self.gui_cash_stats = GUICashStats()
|
|
self.site_ids = {} # site ID list from the database
|
|
|
|
added,n = 1,0 # use n to prevent infinite loop if add_missing_elements() fails somehow
|
|
while added > 0 and n < 2:
|
|
n = n + 1
|
|
log.info(_("Reading configuration file %s") % file)
|
|
print _("\nReading configuration file %s\n") % file
|
|
try:
|
|
doc = xml.dom.minidom.parse(file)
|
|
self.doc = doc
|
|
self.file_error = None
|
|
except:
|
|
log.error(_("Error parsing %s. See error log file.") % (file))
|
|
traceback.print_exc(file=sys.stderr)
|
|
self.file_error = sys.exc_info()[1]
|
|
# we could add a parameter to decide whether to return or read a line and exit?
|
|
return
|
|
#print "press enter to continue"
|
|
#sys.stdin.readline()
|
|
#sys.exit()
|
|
#ExpatError: not well-formed (invalid token): line 511, column 4
|
|
#sys.exc_info = (<class 'xml.parsers.expat.ExpatError'>, ExpatError('not well-formed (invalid token): line 511,
|
|
# column 4',), <traceback object at 0x024503A0>)
|
|
|
|
if not self.example_copy and example_file is not None:
|
|
# reads example file and adds missing elements into current config
|
|
added = self.add_missing_elements(doc, example_file)
|
|
|
|
if doc.getElementsByTagName("general") == []:
|
|
self.general.get_defaults()
|
|
for gen_node in doc.getElementsByTagName("general"):
|
|
self.general.add_elements(node=gen_node) # add/overwrite elements in self.general
|
|
|
|
if doc.getElementsByTagName("gui_cash_stats") == []:
|
|
self.gui_cash_stats.get_defaults()
|
|
for gcs_node in doc.getElementsByTagName("gui_cash_stats"):
|
|
self.gui_cash_stats.add_elements(node=gcs_node) # add/overwrite elements in self.gui_cash_stats
|
|
|
|
# s_sites = doc.getElementsByTagName("supported_sites")
|
|
for site_node in doc.getElementsByTagName("site"):
|
|
site = Site(node = site_node)
|
|
self.supported_sites[site.site_name] = site
|
|
|
|
# s_games = doc.getElementsByTagName("supported_games")
|
|
for game_node in doc.getElementsByTagName("game"):
|
|
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")
|
|
#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"):
|
|
db = Database(node=db_node)
|
|
if db.db_name in 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
|
|
db_node.setAttribute("default", "True")
|
|
self.supported_databases[db.db_name] = db
|
|
#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"):
|
|
aw = Aux_window(node = aw_node)
|
|
self.aux_windows[aw.name] = aw
|
|
|
|
# s_dbs = doc.getElementsByTagName("mucked_windows")
|
|
for hhc_node in doc.getElementsByTagName("hhc"):
|
|
hhc = HHC(node = hhc_node)
|
|
self.hhcs[hhc.site] = hhc
|
|
|
|
# s_dbs = doc.getElementsByTagName("popup_windows")
|
|
for pu_node in doc.getElementsByTagName("pu"):
|
|
pu = Popup(node = pu_node)
|
|
self.popup_windows[pu.name] = pu
|
|
|
|
for imp_node in doc.getElementsByTagName("import"):
|
|
imp = Import(node = imp_node)
|
|
self.imp = imp
|
|
|
|
for hui_node in doc.getElementsByTagName('hud_ui'):
|
|
hui = HudUI(node = hui_node)
|
|
self.ui = hui
|
|
|
|
db = self.get_db_parameters()
|
|
if db['db-password'] == 'YOUR MYSQL PASSWORD':
|
|
df_file = self.find_default_conf()
|
|
if df_file is None: # this is bad
|
|
pass
|
|
else:
|
|
df_parms = self.read_default_conf(df_file)
|
|
self.set_db_parameters(db_name = 'fpdb', db_ip = df_parms['db-host'],
|
|
db_user = df_parms['db-user'],
|
|
db_pass = df_parms['db-password'])
|
|
self.save(file=os.path.join(self.dir_config, "HUD_config.xml"))
|
|
|
|
if doc.getElementsByTagName("raw_hands") == []:
|
|
self.raw_hands = RawHands()
|
|
for raw_hands_node in doc.getElementsByTagName('raw_hands'):
|
|
self.raw_hands = RawHands(raw_hands_node)
|
|
|
|
if doc.getElementsByTagName("raw_tourneys") == []:
|
|
self.raw_tourneys = RawTourneys()
|
|
for raw_tourneys_node in doc.getElementsByTagName('raw_tourneys'):
|
|
self.raw_tourneys = RawTourneys(raw_tourneys_node)
|
|
|
|
print ""
|
|
#end def __init__
|
|
|
|
def add_missing_elements(self, doc, example_file):
|
|
""" Look through example config file and add any elements that are not in the config
|
|
May need to add some 'enabled' attributes to turn things off - can't just delete a
|
|
config section now because this will add it back in"""
|
|
|
|
nodes_added = 0
|
|
|
|
try:
|
|
example_doc = xml.dom.minidom.parse(example_file)
|
|
except:
|
|
log.error(_("Error parsing example file %s. See error log file.") % (example_file))
|
|
return nodes_added
|
|
|
|
for cnode in doc.getElementsByTagName("FreePokerToolsConfig"):
|
|
for example_cnode in example_doc.childNodes:
|
|
if example_cnode.localName == "FreePokerToolsConfig":
|
|
for e in example_cnode.childNodes:
|
|
#print "nodetype", e.nodeType, "name", e.localName, "found", len(doc.getElementsByTagName(e.localName))
|
|
if e.nodeType == e.ELEMENT_NODE and doc.getElementsByTagName(e.localName) == []:
|
|
new = doc.importNode(e, True) # True means do deep copy
|
|
t_node = self.doc.createTextNode(" ")
|
|
cnode.appendChild(t_node)
|
|
cnode.appendChild(new)
|
|
t_node = self.doc.createTextNode("\r\n\r\n")
|
|
cnode.appendChild(t_node)
|
|
print "... adding missing config section: " + e.localName
|
|
nodes_added = nodes_added + 1
|
|
|
|
if nodes_added > 0:
|
|
print "Added %d missing config sections\n" % nodes_added
|
|
self.save()
|
|
|
|
return nodes_added
|
|
|
|
def set_hhArchiveBase(self, path):
|
|
self.imp.node.setAttribute("hhArchiveBase", path)
|
|
|
|
def find_default_conf(self):
|
|
if os.name == 'posix':
|
|
config_path = os.path.join(os.path.expanduser("~"), '.fpdb', 'default.conf')
|
|
elif os.name == 'nt':
|
|
config_path = os.path.join(os.environ["APPDATA"], 'fpdb', 'default.conf')
|
|
else: config_path = False
|
|
|
|
if config_path and os.path.exists(config_path):
|
|
file = config_path
|
|
else:
|
|
file = None
|
|
return file
|
|
|
|
def get_doc(self):
|
|
return self.doc
|
|
|
|
def get_site_node(self, site):
|
|
for site_node in self.doc.getElementsByTagName("site"):
|
|
if site_node.getAttribute("site_name") == site:
|
|
return site_node
|
|
|
|
def getEmailNode(self, siteName, fetchType):
|
|
siteNode = self.get_site_node(siteName)
|
|
for emailNode in siteNode.getElementsByTagName("email"):
|
|
if emailNode.getAttribute("fetchType") == fetchType:
|
|
print "found emailNode"
|
|
return emailNode
|
|
break
|
|
#end def getEmailNode
|
|
|
|
def getGameNode(self,gameName):
|
|
"""returns DOM game node for a given game"""
|
|
for gameNode in self.doc.getElementsByTagName("game"):
|
|
#print "getGameNode gameNode:",gameNode
|
|
if gameNode.getAttribute("game_name") == gameName:
|
|
return gameNode
|
|
#end def getGameNode
|
|
|
|
def get_aux_node(self, aux):
|
|
for aux_node in self.doc.getElementsByTagName("aw"):
|
|
if aux_node.getAttribute("name") == aux:
|
|
return aux_node
|
|
|
|
def get_db_node(self, db_name):
|
|
for db_node in self.doc.getElementsByTagName("database"):
|
|
if db_node.getAttribute("db_name") == db_name:
|
|
return db_node
|
|
return None
|
|
|
|
def get_layout_node(self, site_node, layout):
|
|
for layout_node in site_node.getElementsByTagName("layout"):
|
|
if layout_node.getAttribute("max") is None:
|
|
return None
|
|
if int( layout_node.getAttribute("max") ) == int( layout ):
|
|
return layout_node
|
|
|
|
def get_location_node(self, layout_node, seat):
|
|
if seat == "common":
|
|
for location_node in layout_node.getElementsByTagName("location"):
|
|
if location_node.hasAttribute("common"):
|
|
return location_node
|
|
else:
|
|
for location_node in layout_node.getElementsByTagName("location"):
|
|
if int( location_node.getAttribute("seat") ) == int( seat ):
|
|
return location_node
|
|
|
|
def save(self, file = None):
|
|
if file is None:
|
|
file = self.file
|
|
try:
|
|
shutil.move(file, file+".backup")
|
|
except:
|
|
pass
|
|
|
|
with open(file, 'w') as f:
|
|
#self.doc.writexml(f)
|
|
f.write( self.wrap_long_lines( self.doc.toxml() ) )
|
|
|
|
def wrap_long_lines(self, s):
|
|
lines = [ self.wrap_long_line(l) for l in s.splitlines() ]
|
|
return('\n'.join(lines) + '\n')
|
|
|
|
def wrap_long_line(self, l):
|
|
if 'config_wrap_len' in self.general:
|
|
wrap_len = int(self.general['config_wrap_len'])
|
|
else:
|
|
wrap_len = -1 # < 0 means no wrap
|
|
|
|
if wrap_len >= 0 and len(l) > wrap_len:
|
|
m = re.compile('\s+\S+\s+')
|
|
mo = m.match(l)
|
|
if mo:
|
|
indent_len = mo.end()
|
|
#print "indent = %s (%s)" % (indent_len, l[0:indent_len])
|
|
indent = '\n' + ' ' * indent_len
|
|
m = re.compile('(\S+="[^"]+"\s+)')
|
|
parts = [x for x in m.split(l[indent_len:]) if x]
|
|
if len(parts) > 1:
|
|
#print "parts =", parts
|
|
l = l[0:indent_len] + indent.join(parts)
|
|
return(l)
|
|
else:
|
|
return(l)
|
|
|
|
def editEmail(self, siteName, fetchType, newEmail):
|
|
emailNode = self.getEmailNode(siteName, fetchType)
|
|
emailNode.setAttribute("host", newEmail.host)
|
|
emailNode.setAttribute("username", newEmail.username)
|
|
emailNode.setAttribute("password", newEmail.password)
|
|
emailNode.setAttribute("folder", newEmail.folder)
|
|
emailNode.setAttribute("useSsl", newEmail.useSsl)
|
|
#end def editEmail
|
|
|
|
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)
|
|
# 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] ))
|
|
location_node.setAttribute("y", str( locations[i-1][1] ))
|
|
self.supported_sites[site_name].layout[max].location[i] = ( locations[i-1][0], locations[i-1][1] )
|
|
|
|
def editStats(self, gameName, statArray):
|
|
"""replaces stat selection for the given gameName with the given statArray"""
|
|
gameNode = self.getGameNode(gameName)
|
|
statNodes = gameNode.getElementsByTagName("stat")
|
|
|
|
for node in statNodes:
|
|
gameNode.removeChild(node)
|
|
|
|
gameNode.setAttribute("rows", str(len(statArray)))
|
|
gameNode.setAttribute("cols", str(len(statArray[0])))
|
|
|
|
for rowNumber in range(len(statArray)):
|
|
for columnNumber in range(len(statArray[rowNumber])):
|
|
newStat=self.doc.createElement("stat")
|
|
|
|
newAttrStatName=self.doc.createAttribute("stat_name")
|
|
newStat.setAttributeNode(newAttrStatName)
|
|
newStat.setAttribute("stat_name", statArray[rowNumber][columnNumber])
|
|
|
|
newAttrStatName=self.doc.createAttribute("row")
|
|
newStat.setAttributeNode(newAttrStatName)
|
|
newStat.setAttribute("row", str(rowNumber))
|
|
|
|
newAttrStatName=self.doc.createAttribute("col")
|
|
newStat.setAttributeNode(newAttrStatName)
|
|
newStat.setAttribute("col", str(columnNumber))
|
|
|
|
newAttrStatName=self.doc.createAttribute("click")
|
|
newStat.setAttributeNode(newAttrStatName)
|
|
newStat.setAttribute("click", "tog_decorate")
|
|
|
|
newAttrStatName=self.doc.createAttribute("popup")
|
|
newStat.setAttributeNode(newAttrStatName)
|
|
newStat.setAttribute("popup", "default")
|
|
|
|
newAttrStatName=self.doc.createAttribute("tip")
|
|
newStat.setAttributeNode(newAttrStatName)
|
|
newStat.setAttribute("tip", "tip1")
|
|
|
|
gameNode.appendChild(newStat)
|
|
statNodes = gameNode.getElementsByTagName("stat")
|
|
#end def editStats
|
|
|
|
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 is None:
|
|
print "aux node not found"
|
|
return
|
|
print "editing locations =", locations
|
|
for (i, pos) in locations.iteritems():
|
|
location_node = self.get_location_node(layout_node, i)
|
|
location_node.setAttribute("x", str( locations[i][0] ))
|
|
location_node.setAttribute("y", str( locations[i][1] ))
|
|
if i == "common":
|
|
self.aux_windows[aux_name].layout[max].common = ( locations[i][0], locations[i][1] )
|
|
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
|
|
# TODO: What's up with all the exception handling here?!
|
|
try: db['db-databaseName'] = name
|
|
except: pass
|
|
|
|
try: db['db-desc'] = self.supported_databases[name].db_desc
|
|
except: pass
|
|
|
|
try: db['db-host'] = self.supported_databases[name].db_ip
|
|
except: pass
|
|
|
|
try: db['db-port'] = self.supported_databases[name].db_port
|
|
except KeyError: pass
|
|
|
|
try: db['db-user'] = self.supported_databases[name].db_user
|
|
except: pass
|
|
|
|
try: db['db-password'] = self.supported_databases[name].db_pass
|
|
except: pass
|
|
|
|
try: db['db-server'] = self.supported_databases[name].db_server
|
|
except: pass
|
|
|
|
db['db-backend'] = self.get_backend(self.supported_databases[name].db_server)
|
|
|
|
return db
|
|
|
|
def set_db_parameters(self, db_name = 'fpdb', db_ip = None, db_port = None,
|
|
db_user = None, db_pass = None, db_desc = None,
|
|
db_server = None, default = "False"):
|
|
db_node = self.get_db_node(db_name)
|
|
default = default.lower()
|
|
defaultb = string_to_bool(default, False)
|
|
if db_node != None:
|
|
if db_desc is not None: db_node.setAttribute("db_desc", db_desc)
|
|
if db_ip is not None: db_node.setAttribute("db_ip", db_ip)
|
|
if db_port is not None: db_node.setAttribute("db_port", db_port)
|
|
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 defaultb or self.db_selected == db_name:
|
|
db_node.setAttribute("default", "True")
|
|
for dbn in self.doc.getElementsByTagName("database"):
|
|
if dbn.getAttribute('db_name') != db_name and dbn.hasAttribute("default"):
|
|
dbn.removeAttribute("default")
|
|
elif db_node.hasAttribute("default"):
|
|
db_node.removeAttribute("default")
|
|
if self.supported_databases.has_key(db_name):
|
|
if db_desc is not None: self.supported_databases[db_name].dp_desc = db_desc
|
|
if db_ip is not None: self.supported_databases[db_name].dp_ip = db_ip
|
|
if db_port is not None: self.supported_databases[db_name].dp_port = db_port
|
|
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
|
|
self.supported_databases[db_name].db_selected = defaultb
|
|
if defaultb:
|
|
self.db_selected = db_name
|
|
return
|
|
|
|
def add_db_parameters(self, db_name = 'fpdb', db_ip = None, db_user = None,
|
|
db_pass = None, db_desc = None, db_server = None,
|
|
default = "False"):
|
|
default = default.lower()
|
|
defaultb = string_to_bool(default, False)
|
|
if db_name in self.supported_databases:
|
|
raise ValueError("Database names must be unique")
|
|
|
|
db_node = self.get_db_node(db_name)
|
|
if db_node is None:
|
|
for db_node in self.doc.getElementsByTagName("supported_databases"):
|
|
# should only be one supported_databases element, use last one if there are several
|
|
suppdb_node = db_node
|
|
t_node = self.doc.createTextNode(" ")
|
|
suppdb_node.appendChild(t_node)
|
|
db_node = self.doc.createElement("database")
|
|
suppdb_node.appendChild(db_node)
|
|
t_node = self.doc.createTextNode("\r\n ")
|
|
suppdb_node.appendChild(t_node)
|
|
db_node.setAttribute("db_name", db_name)
|
|
if db_desc is not None: db_node.setAttribute("db_desc", db_desc)
|
|
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 defaultb:
|
|
db_node.setAttribute("default", "True")
|
|
for dbn in self.doc.getElementsByTagName("database"):
|
|
if dbn.getAttribute('db_name') != db_name and dbn.hasAttribute("default"):
|
|
dbn.removeAttribute("default")
|
|
elif db_node.hasAttribute("default"):
|
|
db_node.removeAttribute("default")
|
|
else:
|
|
if db_desc is not None: db_node.setAttribute("db_desc", db_desc)
|
|
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 defaultb or self.db_selected == db_name:
|
|
db_node.setAttribute("default", "True")
|
|
elif db_node.hasAttribute("default"):
|
|
db_node.removeAttribute("default")
|
|
|
|
if self.supported_databases.has_key(db_name):
|
|
if db_desc is not None: self.supported_databases[db_name].dp_desc = db_desc
|
|
if db_ip is not None: self.supported_databases[db_name].dp_ip = db_ip
|
|
if db_port is not None: self.supported_databases[db_name].dp_port = db_port
|
|
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
|
|
self.supported_databases[db_name].db_selected = defaultb
|
|
else:
|
|
db = Database(node=db_node)
|
|
self.supported_databases[db.db_name] = db
|
|
|
|
if defaultb:
|
|
self.db_selected = db_name
|
|
return
|
|
|
|
def get_backend(self, name):
|
|
"""Returns the number of the currently used backend"""
|
|
if name == DATABASE_TYPE_MYSQL:
|
|
ret = 2
|
|
elif name == DATABASE_TYPE_POSTGRESQL:
|
|
ret = 3
|
|
elif name == DATABASE_TYPE_SQLITE:
|
|
ret = 4
|
|
# sqlcoder: this assignment fixes unicode problems for me with sqlite (windows, cp1252)
|
|
# feel free to remove or improve this if you understand the problems
|
|
# better than me (not hard!)
|
|
Charset.not_needed1, Charset.not_needed2, Charset.not_needed3 = True, True, True
|
|
else:
|
|
raise ValueError('Unsupported database backend: %s' % self.supported_databases[name].db_server)
|
|
|
|
return ret
|
|
|
|
def getDefaultSite(self):
|
|
"Returns first enabled site or None"
|
|
for site_name,site in self.supported_sites.iteritems():
|
|
if site.enabled:
|
|
return site_name
|
|
return None
|
|
|
|
# Allow to change the menu appearance
|
|
def get_hud_ui_parameters(self):
|
|
hui = {}
|
|
|
|
default_text = 'FPDB Menu - Right click\nLeft-Drag to Move'
|
|
try:
|
|
hui['label'] = self.ui.label
|
|
if self.ui.label == '': # Empty menu label is a big no-no
|
|
hui['label'] = default_text
|
|
except:
|
|
hui['label'] = default_text
|
|
|
|
try: hui['hud_style'] = self.ui.hud_style
|
|
except: hui['hud_style'] = 'A' # default is show stats for All-time, also S(session) and T(ime)
|
|
|
|
try: hui['hud_days'] = int(self.ui.hud_days)
|
|
except: hui['hud_days'] = 90
|
|
|
|
try: hui['aggregate_ring'] = self.ui.aggregate_ring
|
|
except: hui['aggregate_ring'] = False
|
|
|
|
try: hui['aggregate_tour'] = self.ui.aggregate_tour
|
|
except: hui['aggregate_tour'] = True
|
|
|
|
try: hui['agg_bb_mult'] = self.ui.agg_bb_mult
|
|
except: hui['agg_bb_mult'] = 1
|
|
|
|
try: hui['seats_style'] = self.ui.seats_style
|
|
except: hui['seats_style'] = 'A' # A / C / E, use A(ll) / C(ustom) / E(xact) seat numbers
|
|
|
|
try: hui['seats_cust_nums'] = self.ui.seats_cust_nums
|
|
except: hui['seats_cust_nums'] = ['n/a', 'n/a', (2,2), (3,4), (3,5), (4,6), (5,7), (6,8), (7,9), (8,10), (8,10)]
|
|
|
|
# Hero specific
|
|
|
|
try: hui['h_hud_style'] = self.ui.h_hud_style
|
|
except: hui['h_hud_style'] = 'S'
|
|
|
|
try: hui['h_hud_days'] = int(self.ui.h_hud_days)
|
|
except: hui['h_hud_days'] = 30
|
|
|
|
try: hui['h_aggregate_ring'] = self.ui.h_aggregate_ring
|
|
except: hui['h_aggregate_ring'] = False
|
|
|
|
try: hui['h_aggregate_tour'] = self.ui.h_aggregate_tour
|
|
except: hui['h_aggregate_tour'] = True
|
|
|
|
try: hui['h_agg_bb_mult'] = self.ui.h_agg_bb_mult
|
|
except: hui['h_agg_bb_mult'] = 1
|
|
|
|
try: hui['h_seats_style'] = self.ui.h_seats_style
|
|
except: hui['h_seats_style'] = 'A' # A / C / E, use A(ll) / C(ustom) / E(xact) seat numbers
|
|
|
|
try: hui['h_seats_cust_nums'] = self.ui.h_seats_cust_nums
|
|
except: hui['h_seats_cust_nums'] = ['n/a', 'n/a', (2,2), (3,4), (3,5), (4,6), (5,7), (6,8), (7,9), (8,10), (8,10)]
|
|
|
|
return hui
|
|
|
|
|
|
def get_import_parameters(self):
|
|
imp = {}
|
|
try: imp['callFpdbHud'] = self.imp.callFpdbHud
|
|
except: imp['callFpdbHud'] = True
|
|
|
|
try: imp['interval'] = self.imp.interval
|
|
except: imp['interval'] = 10
|
|
|
|
# hhArchiveBase is the temp store for part-processed hand histories - should be redundant eventually
|
|
try: imp['hhArchiveBase'] = self.imp.hhArchiveBase
|
|
except: imp['hhArchiveBase'] = "~/.fpdb/HandHistories/"
|
|
|
|
# hhBulkPath is the default location for bulk imports (if set)
|
|
try: imp['hhBulkPath'] = self.imp.hhBulkPath
|
|
except: imp['hhBulkPath'] = ""
|
|
|
|
try: imp['saveActions'] = self.imp.saveActions
|
|
except: imp['saveActions'] = False
|
|
|
|
try: imp['cacheSessions'] = self.imp.cacheSessions
|
|
except: imp['cacheSessions'] = False
|
|
|
|
try: imp['sessionTimeout'] = self.imp.sessionTimeout
|
|
except: imp['sessionTimeout'] = 30
|
|
|
|
try: imp['saveStarsHH'] = self.imp.saveStarsHH
|
|
except: imp['saveStarsHH'] = False
|
|
|
|
try: imp['fastStoreHudCache'] = self.imp.fastStoreHudCache
|
|
except: imp['fastStoreHudCache'] = True
|
|
|
|
return imp
|
|
|
|
def get_default_paths(self, site = None):
|
|
if site is None: site = self.getDefaultSite()
|
|
paths = {}
|
|
try:
|
|
path = os.path.expanduser(self.supported_sites[site].HH_path)
|
|
assert(os.path.isdir(path) or os.path.isfile(path)) # maybe it should try another site?
|
|
paths['hud-defaultPath'] = paths['bulkImport-defaultPath'] = path
|
|
if self.imp.hhBulkPath:
|
|
paths['bulkImport-defaultPath'] = self.imp.hhBulkPath
|
|
except AssertionError:
|
|
paths['hud-defaultPath'] = paths['bulkImport-defaultPath'] = "** ERROR DEFAULT PATH IN CONFIG DOES NOT EXIST **"
|
|
return paths
|
|
|
|
def get_frames(self, site = "PokerStars"):
|
|
if site not in self.supported_sites: return False
|
|
return self.supported_sites[site].use_frames == True
|
|
|
|
def get_default_colors(self, site = "PokerStars"):
|
|
colors = {}
|
|
if site not in self.supported_sites or self.supported_sites[site].hudopacity == "":
|
|
colors['hudopacity'] = 0.90
|
|
else:
|
|
colors['hudopacity'] = float(self.supported_sites[site].hudopacity)
|
|
if site not in self.supported_sites or self.supported_sites[site].hudbgcolor == "":
|
|
colors['hudbgcolor'] = "#FFFFFF"
|
|
else:
|
|
colors['hudbgcolor'] = self.supported_sites[site].hudbgcolor
|
|
if site not in self.supported_sites or self.supported_sites[site].hudfgcolor == "":
|
|
colors['hudfgcolor'] = "#000000"
|
|
else:
|
|
colors['hudfgcolor'] = self.supported_sites[site].hudfgcolor
|
|
return colors
|
|
|
|
def get_default_font(self, site='PokerStars'):
|
|
font = "Sans"
|
|
font_size = "8"
|
|
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_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)
|
|
)
|
|
|
|
def get_aux_locations(self, aux = "mucked", max = "9"):
|
|
|
|
try:
|
|
locations = self.aux_windows[aux].layout[max].location
|
|
except:
|
|
locations = ( ( 0, 0), (684, 61), (689, 239), (692, 346),
|
|
(586, 393), (421, 440), (267, 440), ( 0, 361),
|
|
( 0, 280), (121, 280), ( 46, 30) )
|
|
return locations
|
|
|
|
def get_supported_sites(self, all=False):
|
|
"""Returns the list of supported 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"""
|
|
parms = {}
|
|
parms["converter"] = self.supported_sites[site].converter
|
|
parms["decoder"] = self.supported_sites[site].decoder
|
|
parms["hudbgcolor"] = self.supported_sites[site].hudbgcolor
|
|
parms["hudfgcolor"] = self.supported_sites[site].hudfgcolor
|
|
parms["hudopacity"] = self.supported_sites[site].hudopacity
|
|
parms["screen_name"] = self.supported_sites[site].screen_name
|
|
parms["site_path"] = self.supported_sites[site].site_path
|
|
parms["table_finder"] = self.supported_sites[site].table_finder
|
|
parms["HH_path"] = self.supported_sites[site].HH_path
|
|
parms["site_name"] = self.supported_sites[site].site_name
|
|
parms["aux_window"] = self.supported_sites[site].aux_window
|
|
parms["font"] = self.supported_sites[site].font
|
|
parms["font_size"] = self.supported_sites[site].font_size
|
|
parms["enabled"] = self.supported_sites[site].enabled
|
|
parms["xpad"] = self.supported_sites[site].xpad
|
|
parms["ypad"] = self.supported_sites[site].ypad
|
|
parms["xshift"] = self.supported_sites[site].xshift
|
|
parms["yshift"] = self.supported_sites[site].yshift
|
|
return parms
|
|
|
|
def set_site_parameters(self, site_name, converter = None, decoder = None,
|
|
hudbgcolor = None, hudfgcolor = None,
|
|
hudopacity = None, screen_name = None,
|
|
site_path = None, table_finder = None,
|
|
HH_path = None, enabled = None,
|
|
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 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 set_site_ids(self, sites):
|
|
self.site_ids = dict(sites)
|
|
|
|
def get_site_id(self, site):
|
|
return( self.site_ids[site] )
|
|
|
|
def get_aux_windows(self):
|
|
"""Gets the list of mucked window formats in the configuration."""
|
|
return self.aux_windows.keys()
|
|
|
|
def get_aux_parameters(self, name):
|
|
"""Gets a dict of mucked window parameters from the named mw."""
|
|
param = {}
|
|
if self.aux_windows.has_key(name):
|
|
for key in dir(self.aux_windows[name]):
|
|
if key.startswith('__'): continue
|
|
value = getattr(self.aux_windows[name], key)
|
|
if callable(value): continue
|
|
param[key] = value
|
|
|
|
return param
|
|
return None
|
|
|
|
def get_game_parameters(self, name):
|
|
"""Get the configuration parameters for the named game."""
|
|
param = {}
|
|
if self.supported_games.has_key(name):
|
|
param['game_name'] = self.supported_games[name].game_name
|
|
param['rows'] = self.supported_games[name].rows
|
|
param['cols'] = self.supported_games[name].cols
|
|
param['xpad'] = self.supported_games[name].xpad
|
|
param['ypad'] = self.supported_games[name].ypad
|
|
param['xshift'] = self.supported_games[name].xshift
|
|
param['yshift'] = self.supported_games[name].yshift
|
|
param['aux'] = self.supported_games[name].aux
|
|
return param
|
|
|
|
def get_supported_games(self):
|
|
"""Get the list of supported games."""
|
|
sg = []
|
|
for game in self.supported_games.keys():
|
|
sg.append(self.supported_games[game].game_name)
|
|
return sg
|
|
|
|
def execution_path(self, filename):
|
|
"""Join the fpdb path to filename."""
|
|
return os.path.join(os.path.dirname(inspect.getfile(sys._getframe(0))), filename)
|
|
|
|
def get_general_params(self):
|
|
return( self.general )
|
|
|
|
def get_gui_cash_stat_params(self):
|
|
return( self.gui_cash_stats )
|
|
|
|
if __name__== "__main__":
|
|
c = Config()
|
|
|
|
print "\n----------- SUPPORTED SITES -----------"
|
|
for s in c.supported_sites.keys():
|
|
print c.supported_sites[s]
|
|
print "----------- END SUPPORTED SITES -----------"
|
|
|
|
|
|
print "\n----------- SUPPORTED GAMES -----------"
|
|
for game in c.supported_games.keys():
|
|
print c.supported_games[game]
|
|
print "----------- END SUPPORTED GAMES -----------"
|
|
|
|
|
|
print "\n----------- SUPPORTED DATABASES -----------"
|
|
for db in c.supported_databases.keys():
|
|
print c.supported_databases[db]
|
|
print "----------- END SUPPORTED DATABASES -----------"
|
|
|
|
print "\n----------- AUX WINDOW FORMATS -----------"
|
|
for w in c.aux_windows.keys():
|
|
print c.aux_windows[w]
|
|
print "----------- END AUX WINDOW FORMATS -----------"
|
|
|
|
print "\n----------- HAND HISTORY CONVERTERS -----------"
|
|
for w in c.hhcs.keys():
|
|
print c.hhcs[w]
|
|
print "----------- END HAND HISTORY CONVERTERS -----------"
|
|
|
|
print "\n----------- POPUP WINDOW FORMATS -----------"
|
|
for w in c.popup_windows.keys():
|
|
print c.popup_windows[w]
|
|
print "----------- END POPUP WINDOW FORMATS -----------"
|
|
|
|
print "\n----------- IMPORT -----------"
|
|
print c.imp
|
|
print "----------- END IMPORT -----------"
|
|
|
|
c.edit_layout("PokerStars", 6, locations=( (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6) ))
|
|
c.save(file="testout.xml")
|
|
|
|
print "db = ", c.get_db_parameters()
|
|
# print "imp = ", c.get_import_parameters()
|
|
print "paths = ", c.get_default_paths("PokerStars")
|
|
print "colors = ", c.get_default_colors("PokerStars")
|
|
print "locs = ", c.get_locations("PokerStars", 8)
|
|
for mw in c.get_aux_windows():
|
|
print c.get_aux_parameters(mw)
|
|
|
|
print "mucked locations =", c.get_aux_locations('mucked', 9)
|
|
# c.edit_aux_layout('mucked', 9, locations = [(487, 113), (555, 469), (572, 276), (522, 345),
|
|
# (333, 354), (217, 341), (150, 273), (150, 169), (230, 115)])
|
|
# print "mucked locations =", c.get_aux_locations('mucked', 9)
|
|
|
|
for site in c.supported_sites.keys():
|
|
print "site = ", site,
|
|
print c.get_site_parameters(site)
|
|
print c.get_default_font(site)
|
|
|
|
for game in c.get_supported_games():
|
|
print c.get_game_parameters(game)
|
|
|
|
for hud_param, value in c.get_hud_ui_parameters().iteritems():
|
|
print "hud param %s = %s" % (hud_param, value)
|
|
|
|
print "start up path = ", c.execution_path("")
|
|
|
|
print "gui_cash_stats =", c.gui_cash_stats
|
|
|
|
try:
|
|
from xml.dom.ext import PrettyPrint
|
|
for site_node in c.doc.getElementsByTagName("site"):
|
|
PrettyPrint(site_node, stream=sys.stdout, encoding="utf-8")
|
|
except:
|
|
print "xml.dom.ext needs PyXML to be installed!"
|
|
|
|
print "FROZEN =", FROZEN
|
|
print "EXEC_PATH =", EXEC_PATH
|
|
|
|
print "press enter to end"
|
|
sys.stdin.readline()
|