Add 'Currencies' filter to the ring player stats viewer.
[fpdb-dooglus.git] / pyfpdb / Configuration.py
blob18ab3df22f33cd8f11c9f6b73a4263b532e380d6
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 """Configuration.py
5 Handles fpdb/fpdb-hud configuration files.
6 """
7 # Copyright 2008-2011, Ray E. Barker
10 # This program is free software; you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 2 of the License, or
13 # (at your option) any later version.
15 # This program is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
20 # You should have received a copy of the GNU General Public License
21 # along with this program; if not, write to the Free Software
22 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 ########################################################################
27 # Standard Library modules
28 from __future__ import with_statement
30 import L10n
31 _ = L10n.get_translation()
33 import os
34 import sys
35 import inspect
36 import string
37 import traceback
38 import shutil
39 import locale
40 import re
41 import xml.dom.minidom
42 from xml.dom.minidom import Node
44 import platform
45 if platform.system() == 'Windows':
46 import winpaths
47 winpaths_appdata = winpaths.get_appdata()
48 else:
49 winpaths_appdata = False
51 import logging, logging.config
52 import ConfigParser
55 # Setup constants
56 # code is centralised here to ensure uniform handling of path names
57 # especially important when user directory includes non-ascii chars
59 # INSTALL_METHOD ("source" or "exe")
60 # FPDB_PROGRAM_PATH (path to the root fpdb installation dir root (normally ...../fpdb)
61 # APPDATA_PATH (root path for appdata eg /~ or appdata)
62 # CONFIG_PATH (path to the directory holding logs, sqlite db's and config)
63 # OS_FAMILY (OS Family for installed system (Linux, Mac, XP, Win7)
64 # POSIX (True=Linux or Mac platform, False=Windows platform)
65 # PYTHON_VERSION (n.n)
67 if hasattr(sys, "frozen"):
68 INSTALL_METHOD = "exe"
69 else:
70 INSTALL_METHOD = "source"
72 if INSTALL_METHOD == "exe":
73 FPDB_PROGRAM_PATH = os.path.dirname(sys.executable) # should be exe path to /fpdb
74 else:
75 FPDB_PROGRAM_PATH = os.path.dirname(sys.path[0]) # should be source path to /fpdb
77 sysPlatform = platform.system() #Linux, Windows, Darwin
78 if sysPlatform[0:5] == 'Linux':
79 OS_FAMILY = 'Linux'
80 elif sysPlatform == 'Darwin':
81 OS_FAMILY = 'Mac'
82 elif sysPlatform == 'Windows':
83 if platform.release() <> 'XP':
84 OS_FAMILY = 'Win7' #Vista and win7
85 else:
86 OS_FAMILY = 'XP'
87 else:
88 OS_FAMILY = False
90 if OS_FAMILY in ['XP', 'Win7']:
91 APPDATA_PATH = winpaths_appdata
92 CONFIG_PATH = os.path.join(APPDATA_PATH, u"fpdb")
93 elif OS_FAMILY == 'Mac':
94 APPDATA_PATH = os.getenv("HOME")
95 CONFIG_PATH = os.path.join(APPDATA_PATH, u".fpdb")
96 elif OS_FAMILY == 'Linux':
97 APPDATA_PATH = os.path.expanduser("~")
98 CONFIG_PATH = os.path.join(APPDATA_PATH, u".fpdb")
99 else:
100 APPDATA_PATH = False
101 CONFIG_PATH = False
103 if os.name == 'posix':
104 POSIX = True
105 else:
106 POSIX = False
108 PYTHON_VERSION = sys.version[:3]
110 # logging has been set up in fpdb.py or HUD_main.py, use their settings:
111 log = logging.getLogger("config")
113 def get_config(file_name, fallback = True):
114 """Looks in cwd and in self.default_config_path for a config file."""
115 # look for example file even if not used here, path is returned to caller
116 config_found,example_found,example_copy = False,False,False
117 config_path, example_path = None,None
119 if file_name == 'logging.conf' and INSTALL_METHOD == "source":
120 config_path = os.path.join(FPDB_PROGRAM_PATH, 'pyfpdb', file_name)
121 else:
122 config_path = os.path.join(FPDB_PROGRAM_PATH, file_name)
123 #print "config_path=", config_path
124 if os.path.exists(config_path): # there is a file in the cwd
125 config_found = True # so we use it
126 else: # no file in the cwd, look where it should be in the first place
127 config_path = os.path.join(CONFIG_PATH, file_name)
128 #print "config path 2=", config_path
129 if os.path.exists(config_path):
130 config_found = True
132 #TODO: clean up the example path loading to ensure it behaves the same on all OSs
133 # Example configuration for debian package
134 if POSIX:
135 # If we're on linux, try to copy example from the place
136 # debian package puts it; get_default_config_path() creates
137 # the config directory for us so there's no need to check it
138 # again
139 example_path = '/usr/share/python-fpdb/' + file_name + '.example'
140 if not os.path.exists(example_path):
141 if os.path.exists(file_name + '.example'):
142 example_path = file_name + '.example'
143 else:
144 example_path = "pyfpdb/" + file_name + '.example'
145 if not config_found and fallback:
146 try:
147 shutil.copyfile(example_path, config_path)
148 example_copy = True
149 msg = _("Config file has been created at %s.") % (config_path+"\n")
150 logging.info(msg)
151 except IOError:
152 try:
153 example_path = file_name + '.example'
154 shutil.copyfile(example_path, config_path)
155 example_copy = True
156 msg = _("Config file has been created at %s.") % (config_path+"\n")
157 logging.info(msg)
158 except IOError:
159 pass
161 # OK, fall back to the .example file, should be in the start dir
162 elif os.path.exists(file_name + ".example"):
163 try:
164 #print ""
165 example_path = file_name + ".example"
166 check_dir(CONFIG_PATH)
167 if not config_found and fallback:
168 shutil.copyfile(example_path, config_path)
169 example_copy = True
170 msg = _("No %s found\n in %s\n or %s") % (file_name, FPDB_PROGRAM_PATH, CONFIG_PATH) \
171 + " " + _("Config file has been created at %s.") % (config_path+"\n")
172 print msg
173 logging.info(msg)
174 except:
175 print _("Error copying .example config file, cannot fall back. Exiting."), "\n"
176 sys.stderr.write(_("Error copying .example config file, cannot fall back. Exiting.")+"\n")
177 sys.stderr.write( str(sys.exc_info()) )
178 sys.exit()
179 elif fallback:
180 print _("No %s found, cannot fall back. Exiting.") % file_name, "\n"
181 sys.stderr.write((_("No %s found, cannot fall back. Exiting.") % file_name) + "\n")
182 sys.exit()
184 #print "get_config: returning "+str( (config_path,example_copy,example_path) )
185 return (config_path,example_copy,example_path)
187 def get_logger(file_name, config = "config", fallback = False, log_dir=None, log_file=None):
188 (conf_file,copied,example_file) = get_config(file_name, fallback = fallback)
190 if log_dir is None:
191 log_dir = os.path.join(FPDB_PROGRAM_PATH, u'log')
192 #print "\nget_logger: checking log_dir:", log_dir
193 check_dir(log_dir)
194 if log_file is None:
195 file = os.path.join(log_dir, u'fpdb-log.txt')
196 else:
197 file = os.path.join(log_dir, log_file)
199 if conf_file:
200 try:
201 file = file.replace('\\', '\\\\') # replace each \ with \\
202 # print " ="+file+" "+ str(type(file))+" len="+str(len(file))+"\n"
203 logging.config.fileConfig(conf_file, {"logFile":file})
204 log = logging.getLogger(config)
205 log.debug("%s logger initialised" % config)
206 return log
207 except:
208 pass
210 log = logging.basicConfig(filename=file, level=logging.INFO)
211 log = logging.getLogger()
212 # but it looks like default is no output :-( maybe because all the calls name a module?
213 log.debug(_("Default logger initialised for %s") % file)
214 #print(_("Default logger initialised for %s") % file)
215 return log
217 def check_dir(path, create = True):
218 """Check if a dir exists, optionally creates if not."""
219 if os.path.exists(path):
220 if os.path.isdir(path):
221 return path
222 else:
223 return False
224 if create:
225 msg = _("Creating directory: '%s'") % (path)
226 print msg
227 log.info(msg)
228 os.mkdir(path)#, "utf-8"))
229 else:
230 return False
233 ########################################################################
234 # application wide consts
236 APPLICATION_NAME_SHORT = 'fpdb'
237 APPLICATION_VERSION = 'xx.xx.xx'
239 DATABASE_TYPE_POSTGRESQL = 'postgresql'
240 DATABASE_TYPE_SQLITE = 'sqlite'
241 DATABASE_TYPE_MYSQL = 'mysql'
242 DATABASE_TYPES = (
243 DATABASE_TYPE_POSTGRESQL,
244 DATABASE_TYPE_SQLITE,
245 DATABASE_TYPE_MYSQL,
248 LOCALE_ENCODING = locale.getpreferredencoding()
249 if LOCALE_ENCODING in ("US-ASCII", "", None):
250 LOCALE_ENCODING = "cp1252"
251 if (os.uname()[0]!="Darwin"):
252 print _("Default encoding set to US-ASCII, defaulting to CP1252 instead."), _("Please report this problem.")
254 # needs LOCALE_ENCODING (above), imported for sqlite setup in Config class below
255 import Charset
259 ########################################################################
260 def string_to_bool(string, default=True):
261 """converts a string representation of a boolean value to boolean True or False
262 @param string: (str) the string to convert
263 @param default: value to return if the string can not be converted to a boolean value
265 string = string.lower()
266 if string in ('1', 'true', 't'):
267 return True
268 elif string in ('0', 'false', 'f'):
269 return False
270 return default
272 class Layout:
273 def __init__(self, node):
275 self.max = int( node.getAttribute('max') )
276 if node.hasAttribute('fav_seat'): self.fav_seat = int( node.getAttribute('fav_seat') )
277 if node.hasAttribute('name'): self.name = node.getAttribute('name')
278 else: self.name = None
279 self.width = int( node.getAttribute('width') )
280 self.height = int( node.getAttribute('height') )
282 self.location = []
283 self.location = map(lambda x: None, range(self.max+1)) # fill array with max seats+1 empty entries
285 for location_node in node.getElementsByTagName('location'):
286 if location_node.getAttribute('seat') != "":
287 self.location[int( location_node.getAttribute('seat') )] = (int( location_node.getAttribute('x') ), int( location_node.getAttribute('y')))
288 elif location_node.getAttribute('common') != "":
289 self.common = (int( location_node.getAttribute('x') ), int( location_node.getAttribute('y')))
291 def __str__(self):
292 if hasattr(self, 'name'):
293 name = str(self.name) + ", "
294 else:
295 name = ""
296 temp = " Layout = %s%d max, width= %d, height = %d" % (name, self.max, self.width, self.height)
297 if hasattr(self, 'fav_seat'): temp = temp + ", fav_seat = %d\n" % self.fav_seat
298 else: temp = temp + "\n"
299 if hasattr(self, "common"):
300 temp = temp + " Common = (%d, %d)\n" % (self.common[0], self.common[1])
301 temp = temp + " Locations = "
302 for i in range(1, len(self.location)):
303 temp = temp + "(%d,%d)" % self.location[i]
305 return temp + "\n"
307 class Email:
308 def __init__(self, node):
309 self.node = node
310 self.host= node.getAttribute("host")
311 self.username = node.getAttribute("username")
312 self.password = node.getAttribute("password")
313 self.useSsl = node.getAttribute("useSsl")
314 self.folder = node.getAttribute("folder")
315 self.fetchType = node.getAttribute("fetchType")
317 def __str__(self):
318 return " fetchType=%s\n host = %s\n username = %s\n password = %s\n useSsl = %s\n folder = %s\n" \
319 % (self.fetchType, self.host, self.username, self.password, self.useSsl, self.folder)
321 class Site:
322 def __init__(self, node):
323 def normalizePath(path):
324 "Normalized existing pathes"
325 if os.path.exists(path):
326 return os.path.abspath(path)
327 return path
329 self.site_name = node.getAttribute("site_name")
330 self.table_finder = node.getAttribute("table_finder")
331 self.screen_name = node.getAttribute("screen_name")
332 self.site_path = normalizePath(node.getAttribute("site_path"))
333 self.HH_path = normalizePath(node.getAttribute("HH_path"))
334 self.decoder = node.getAttribute("decoder")
335 self.hudopacity = node.getAttribute("hudopacity")
336 self.hudbgcolor = node.getAttribute("bgcolor")
337 self.hudfgcolor = node.getAttribute("fgcolor")
338 self.converter = node.getAttribute("converter")
339 self.aux_window = node.getAttribute("aux_window")
340 self.font = node.getAttribute("font")
341 self.font_size = node.getAttribute("font_size")
342 self.use_frames = node.getAttribute("use_frames")
343 self.enabled = string_to_bool(node.getAttribute("enabled"), default=True)
344 self.xpad = node.getAttribute("xpad")
345 self.ypad = node.getAttribute("ypad")
346 self.xshift = node.getAttribute("xshift")
347 self.yshift = node.getAttribute("yshift")
348 self.layout = {}
349 self.emails = {}
351 for layout_node in node.getElementsByTagName('layout'):
352 lo = Layout(layout_node)
353 self.layout[lo.max] = lo
355 for email_node in node.getElementsByTagName('email'):
356 email = Email(email_node)
357 self.emails[email.fetchType] = email
359 # Site defaults
360 self.xpad = 1 if self.xpad == "" else int(self.xpad)
361 self.ypad = 0 if self.ypad == "" else int(self.ypad)
362 self.xshift = 1 if self.xshift == "" else int(self.xshift)
363 self.yshift = 0 if self.yshift == "" else int(self.yshift)
364 self.font_size = 7 if self.font_size == "" else int(self.font_size)
365 self.hudopacity = 1.0 if self.hudopacity == "" else float(self.hudopacity)
367 if self.use_frames == "": self.use_frames = False
368 if self.font == "": self.font = "Sans"
369 if self.hudbgcolor == "": self.hudbgcolor = "#000000"
370 if self.hudfgcolor == "": self.hudfgcolor = "#FFFFFF"
372 def __str__(self):
373 temp = "Site = " + self.site_name + "\n"
374 for key in dir(self):
375 if key.startswith('__'): continue
376 if key == 'layout': continue
377 value = getattr(self, key)
378 if callable(value): continue
379 temp = temp + ' ' + key + " = " + str(value) + "\n"
381 for layout in self.layout:
382 temp = temp + "%s" % self.layout[layout]
384 return temp
386 class Stat:
387 def __init__(self):
388 pass
390 def __str__(self):
391 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)
392 return temp
394 class Game:
395 def __init__(self, node):
396 self.game_name = node.getAttribute("game_name")
397 self.rows = int( node.getAttribute("rows") )
398 self.cols = int( node.getAttribute("cols") )
399 self.xpad = node.getAttribute("xpad")
400 self.ypad = node.getAttribute("ypad")
401 self.xshift = node.getAttribute("xshift")
402 self.yshift = node.getAttribute("yshift")
404 # Defaults
405 if self.xpad == "": self.xpad = 1
406 else: self.xpad = int(self.xpad)
407 if self.ypad == "": self.ypad = 0
408 else: self.ypad = int(self.ypad)
409 if self.xshift == "": self.xshift = 1
410 else: self.xshift = int(self.xshift)
411 if self.yshift == "": self.yshift = 0
412 else: self.yshift = int(self.yshift)
414 aux_text = node.getAttribute("aux")
415 aux_list = aux_text.split(',')
416 for i in range(0, len(aux_list)):
417 aux_list[i] = aux_list[i].strip()
418 self.aux = aux_list
420 self.stats = {}
421 for stat_node in node.getElementsByTagName('stat'):
422 stat = Stat()
423 stat.stat_name = stat_node.getAttribute("stat_name")
424 stat.row = int( stat_node.getAttribute("row") )
425 stat.col = int( stat_node.getAttribute("col") )
426 stat.tip = stat_node.getAttribute("tip")
427 stat.click = stat_node.getAttribute("click")
428 stat.popup = stat_node.getAttribute("popup")
429 stat.hudprefix = stat_node.getAttribute("hudprefix")
430 stat.hudsuffix = stat_node.getAttribute("hudsuffix")
431 stat.hudcolor = stat_node.getAttribute("hudcolor")
432 stat.stat_loth = stat_node.getAttribute("stat_loth")
433 stat.stat_hith = stat_node.getAttribute("stat_hith")
434 stat.stat_locolor = stat_node.getAttribute("stat_locolor")
435 stat.stat_hicolor = stat_node.getAttribute("stat_hicolor")
437 self.stats[stat.stat_name] = stat
439 def __str__(self):
440 temp = "Game = " + self.game_name + "\n"
441 temp = temp + " rows = %d\n" % self.rows
442 temp = temp + " cols = %d\n" % self.cols
443 temp = temp + " xpad = %d\n" % self.xpad
444 temp = temp + " ypad = %d\n" % self.ypad
445 temp = temp + " xshift = %d\n" % self.xshift
446 temp = temp + " yshift = %d\n" % self.yshift
447 temp = temp + " aux = %s\n" % self.aux
449 for stat in self.stats.keys():
450 temp = temp + "%s" % self.stats[stat]
452 return temp
454 class Database:
455 def __init__(self, node):
456 self.db_name = node.getAttribute("db_name")
457 self.db_desc = node.getAttribute("db_desc")
458 self.db_server = node.getAttribute("db_server").lower()
459 self.db_ip = node.getAttribute("db_ip")
460 self.db_user = node.getAttribute("db_user")
461 self.db_pass = node.getAttribute("db_pass")
462 self.db_selected = string_to_bool(node.getAttribute("default"), default=False)
463 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'" \
464 % { 'name':self.db_name, 'server':self.db_server, 'ip':self.db_ip, 'user':self.db_user, 'sel':self.db_selected} )
466 def __str__(self):
467 temp = 'Database = ' + self.db_name + '\n'
468 for key in dir(self):
469 if key.startswith('__'): continue
470 value = getattr(self, key)
471 if callable(value): continue
472 temp = temp + ' ' + key + " = " + repr(value) + "\n"
473 return temp
475 class Aux_window:
476 def __init__(self, node):
477 for (name, value) in node.attributes.items():
478 setattr(self, name, value)
480 self.layout = {}
481 for layout_node in node.getElementsByTagName('layout'):
482 lo = Layout(layout_node)
483 self.layout[lo.max] = lo
485 def __str__(self):
486 temp = 'Aux = ' + self.name + "\n"
487 for key in dir(self):
488 if key.startswith('__'): continue
489 if key == 'layout': continue
490 value = getattr(self, key)
491 if callable(value): continue
492 temp = temp + ' ' + key + " = " + value + "\n"
494 for layout in self.layout:
495 temp = temp + "%s" % self.layout[layout]
496 return temp
498 class HHC:
499 def __init__(self, node):
500 self.site = node.getAttribute("site")
501 self.converter = node.getAttribute("converter")
502 self.summaryImporter = node.getAttribute("summaryImporter")
504 def __str__(self):
505 return "%s:\tconverter: '%s' summaryImporter: '%s'" % (self.site, self.converter, self.summaryImporter)
508 class Popup:
509 def __init__(self, node):
510 self.name = node.getAttribute("pu_name")
511 self.pu_stats = []
512 for stat_node in node.getElementsByTagName('pu_stat'):
513 self.pu_stats.append(stat_node.getAttribute("pu_stat_name"))
515 def __str__(self):
516 temp = "Popup = " + self.name + "\n"
517 for stat in self.pu_stats:
518 temp = temp + " " + stat
519 return temp + "\n"
521 class Import:
522 def __init__(self, node):
523 self.node = node
524 self.interval = node.getAttribute("interval")
525 self.sessionTimeout = string_to_bool(node.getAttribute("sessionTimeout") , default=30)
526 self.ResultsDirectory = node.getAttribute("ResultsDirectory")
527 self.hhBulkPath = node.getAttribute("hhBulkPath")
528 self.saveActions = string_to_bool(node.getAttribute("saveActions") , default=False)
529 self.cacheSessions = string_to_bool(node.getAttribute("cacheSessions") , default=False)
530 self.callFpdbHud = string_to_bool(node.getAttribute("callFpdbHud") , default=False)
531 self.fastStoreHudCache = string_to_bool(node.getAttribute("fastStoreHudCache"), default=False)
532 self.saveStarsHH = string_to_bool(node.getAttribute("saveStarsHH") , default=False)
534 def __str__(self):
535 return " interval = %s\n callFpdbHud = %s\n saveActions = %s\n fastStoreHudCache = %s\nResultsDirectory = %s" \
536 % (self.interval, self.callFpdbHud, self.saveActions, self.cacheSessions, self.sessionTimeout, self.fastStoreHudCache, self.ResultsDirectory)
538 class HudUI:
539 def __init__(self, node):
540 self.node = node
541 self.label = node.getAttribute('label')
543 self.hud_style = node.getAttribute('stat_range')
544 self.hud_days = node.getAttribute('stat_days')
545 self.aggregate_ring = string_to_bool(node.getAttribute('aggregate_ring_game_stats'))
546 self.aggregate_tour = string_to_bool(node.getAttribute('aggregate_tourney_stats'))
547 self.agg_bb_mult = node.getAttribute('aggregation_level_multiplier')
549 self.h_hud_style = node.getAttribute('hero_stat_range')
550 self.h_hud_days = node.getAttribute('hero_stat_days')
551 self.h_aggregate_ring = string_to_bool(node.getAttribute('aggregate_hero_ring_game_stats'))
552 self.h_aggregate_tour = string_to_bool(node.getAttribute('aggregate_hero_tourney_stats'))
553 self.h_agg_bb_mult = node.getAttribute('hero_aggregation_level_multiplier')
556 def __str__(self):
557 return " label = %s\n" % self.label
560 class General(dict):
561 def __init__(self):
562 super(General, self).__init__()
564 def add_elements(self, node):
565 # day_start - number n where 0.0 <= n < 24.0 representing start of day for user
566 # e.g. user could set to 4.0 for day to start at 4am local time
567 # [ HH_bulk_path was here - now moved to import section ]
568 for (name, value) in node.attributes.items():
569 log.debug(unicode(_("config.general: adding %s = %s"), "utf8") % (name,value))
570 self[name] = value
572 try:
573 self["version"]=int(self["version"])
574 except KeyError:
575 self["version"]=0
576 self["ui_language"]="system"
577 self["config_difficulty"]="expert"
579 def get_defaults(self):
580 self["version"]=0
581 self["ui_language"]="system"
582 self["config_difficulty"]="expert"
583 self["config_wrap_len"]="-1"
584 self["day_start"]="5"
586 def __str__(self):
587 s = ""
588 for k in self:
589 s = s + " %s = %s\n" % (k, self[k])
590 return(s)
592 class GUICashStats(list):
593 """<gui_cash_stats>
594 <col col_name="game" col_title="Game" disp_all="True" disp_posn="True" field_format="%s" field_type="str" xalignment="0.0" />
596 </gui_cash_stats>
598 def __init__(self):
599 super(GUICashStats, self).__init__()
601 def add_elements(self, node):
602 # is this needed?
603 for child in node.childNodes:
604 if child.nodeType == child.ELEMENT_NODE:
605 col_name, col_title, disp_all, disp_posn, field_format, field_type, xalignment=None, None, True, True, "%s", "str", 0.0
607 if child.hasAttribute('col_name'): col_name = child.getAttribute('col_name')
608 if child.hasAttribute('col_title'): col_title = child.getAttribute('col_title')
609 if child.hasAttribute('disp_all'): disp_all = string_to_bool(child.getAttribute('disp_all'))
610 if child.hasAttribute('disp_posn'): disp_posn = string_to_bool(child.getAttribute('disp_posn'))
611 if child.hasAttribute('field_format'): field_format = child.getAttribute('field_format')
612 if child.hasAttribute('field_type'): field_type = child.getAttribute('field_type')
613 try:
614 if child.hasAttribute('xalignment'): xalignment = float(child.getAttribute('xalignment'))
615 except ValueError:
616 print _("bad number in xalignment was ignored")
617 log.info(_("bad number in xalignment was ignored"))
619 self.append( [col_name, col_title, disp_all, disp_posn, field_format, field_type, xalignment] )
621 def get_defaults(self):
622 """A list of defaults to be called, should there be no entry in config"""
623 # SQL column name, display title, display all, display positional, format, type, alignment
624 defaults = [ [u'game', u'Game', True, True, u'%s', u'str', 0.0],
625 [u'hand', u'Hand', False, False, u'%s', u'str', 0.0],
626 [u'plposition', u'Posn', False, False, u'%s', u'str', 1.0],
627 [u'pname', u'Name', False, False, u'%s', u'str', 0.0],
628 [u'n', u'Hds', True, True, u'%1.0f', u'str', 1.0],
629 [u'avgseats', u'Seats', False, False, u'%3.1f', u'str', 1.0],
630 [u'vpip', u'VPIP', True, True, u'%3.1f', u'str', 1.0],
631 [u'pfr', u'PFR', True, True, u'%3.1f', u'str', 1.0],
632 [u'pf3', u'PF3', True, True, u'%3.1f', u'str', 1.0],
633 [u'aggfac', u'AggFac', True, True, u'%2.2f', u'str', 1.0],
634 [u'aggfrq', u'AggFreq', True, True, u'%3.1f', u'str', 1.0],
635 [u'conbet', u'ContBet', True, True, u'%3.1f', u'str', 1.0],
636 [u'rfi', u'RFI', True, True, u'%3.1f', u'str', 1.0],
637 [u'steals', u'Steals', True, True, u'%3.1f', u'str', 1.0],
638 [u'saw_f', u'Saw_F', True, True, u'%3.1f', u'str', 1.0],
639 [u'sawsd', u'SawSD', True, True, u'%3.1f', u'str', 1.0],
640 [u'wtsdwsf', u'WtSDwsF', True, True, u'%3.1f', u'str', 1.0],
641 [u'wmsd', u'W$SD', True, True, u'%3.1f', u'str', 1.0],
642 [u'flafq', u'FlAFq', True, True, u'%3.1f', u'str', 1.0],
643 [u'tuafq', u'TuAFq', True, True, u'%3.1f', u'str', 1.0],
644 [u'rvafq', u'RvAFq', True, True, u'%3.1f', u'str', 1.0],
645 [u'pofafq', u'PoFAFq', False, False, u'%3.1f', u'str', 1.0],
646 [u'net', u'Net($)', True, True, u'%6.2f', u'cash', 1.0],
647 [u'bbper100', u'bb/100', True, True, u'%4.2f', u'str', 1.0],
648 [u'rake', u'Rake($)', True, True, u'%6.2f', u'cash', 1.0],
649 [u'bb100xr', u'bbxr/100', True, True, u'%4.2f', u'str', 1.0],
650 [u'variance', u'Variance', True, True, u'%5.2f', u'str', 1.0]
652 for col in defaults:
653 self.append (col)
655 # def __str__(self):
656 # s = ""
657 # for l in self:
658 # s = s + " %s = %s\n" % (k, self[k])
659 # return(s)
661 class RawHands:
662 def __init__(self, node=None):
663 if node==None:
664 self.save="error"
665 self.compression="none"
666 #print _("missing config section raw_hands")
667 else:
668 save=node.getAttribute("save")
669 if save in ("none", "error", "all"):
670 self.save=save
671 else:
672 print (_("Invalid config value for %s, defaulting to %s") % (raw_hands.save, "\"error\""))
673 self.save="error"
675 compression=node.getAttribute("compression")
676 if save in ("none", "gzip", "bzip2"):
677 self.compression=compression
678 else:
679 print (_("Invalid config value for %s, defaulting to %s") % (raw_hands.compression, "\"none\""))
680 self.compression="none"
681 #end def __init__
683 def __str__(self):
684 return " save= %s, compression= %s\n" % (self.save, self.compression)
685 #end class RawHands
687 class RawTourneys:
688 def __init__(self, node=None):
689 if node==None:
690 self.save="error"
691 self.compression="none"
692 #print _("missing config section raw_tourneys")
693 else:
694 save=node.getAttribute("save")
695 if save in ("none", "error", "all"):
696 self.save=save
697 else:
698 print (_("Invalid config value for %s, defaulting to %s") % (raw_tourneys.save, "\"error\""))
699 self.save="error"
701 compression=node.getAttribute("compression")
702 if save in ("none", "gzip", "bzip2"):
703 self.compression=compression
704 else:
705 print (_("Invalid config value for %s, defaulting to %s") % (raw_tourneys.compression, "\"none\""))
706 self.compression="none"
707 #end def __init__
709 def __str__(self):
710 return " save= %s, compression= %s\n" % (self.save, self.compression)
711 #end class RawTourneys
713 class Config:
714 def __init__(self, file = None, dbname = ''):
715 # "file" is a path to an xml file with the fpdb/HUD configuration
716 # we check the existence of "file" and try to recover if it doesn't exist
718 # self.default_config_path = self.get_default_config_path()
720 self.example_copy = False
721 if file is not None: # config file path passed in
722 file = os.path.expanduser(file)
723 if not os.path.exists(file):
724 print _("Configuration file %s not found. Using defaults.") % (file)
725 sys.stderr.write(_("Configuration file %s not found. Using defaults.") % (file))
726 file = None
728 self.example_copy,example_file = True,None
729 if file is None: (file,self.example_copy,example_file) = get_config("HUD_config.xml", True)
731 self.file = file
733 self.install_method = INSTALL_METHOD
734 self.fpdb_program_path = FPDB_PROGRAM_PATH
735 self.appdata_path = APPDATA_PATH
736 self.config_path = CONFIG_PATH
737 self.os_family = OS_FAMILY
738 self.posix = POSIX
739 self.python_version = PYTHON_VERSION
741 if not os.path.exists(CONFIG_PATH):
742 os.mkdir(CONFIG_PATH)
744 self.dir_log = os.path.join(CONFIG_PATH, u'log')
745 self.dir_database = os.path.join(CONFIG_PATH, u'database')
746 self.log_file = os.path.join(self.dir_log, u'fpdb-log.txt')
747 log = get_logger(u"logging.conf", "config", log_dir=self.dir_log)
749 self.supported_sites = {}
750 self.supported_games = {}
751 self.supported_databases = {} # databaseName --> Database instance
752 self.aux_windows = {}
753 self.hhcs = {}
754 self.popup_windows = {}
755 self.db_selected = None # database the user would like to use
756 self.general = General()
757 self.emails = {}
758 self.gui_cash_stats = GUICashStats()
759 self.site_ids = {} # site ID list from the database
762 added,n = 1,0 # use n to prevent infinite loop if add_missing_elements() fails somehow
763 while added > 0 and n < 2:
764 n = n + 1
765 log.info(unicode(_("Reading configuration file %s"), "utf8") % file)
766 #print (("\n"+_("Reading configuration file %s")+"\n") % file)
767 try:
768 doc = xml.dom.minidom.parse(file)
769 self.doc = doc
770 self.file_error = None
771 except:
772 log.error((_("Error parsing %s.") % (file)) + _("See error log file."))
773 traceback.print_exc(file=sys.stderr)
774 self.file_error = sys.exc_info()[1]
775 # we could add a parameter to decide whether to return or read a line and exit?
776 return
777 #print "press enter to continue"
778 #sys.stdin.readline()
779 #sys.exit()
780 #ExpatError: not well-formed (invalid token): line 511, column 4
781 #sys.exc_info = (<class 'xml.parsers.expat.ExpatError'>, ExpatError('not well-formed (invalid token): line 511,
782 # column 4',), <traceback object at 0x024503A0>)
784 if (not self.example_copy) and (example_file is not None):
785 # reads example file and adds missing elements into current config
786 added = self.add_missing_elements(doc, example_file)
788 if doc.getElementsByTagName("general") == []:
789 self.general.get_defaults()
790 for gen_node in doc.getElementsByTagName("general"):
791 self.general.add_elements(node=gen_node) # add/overwrite elements in self.general
793 if doc.getElementsByTagName("gui_cash_stats") == []:
794 self.gui_cash_stats.get_defaults()
795 for gcs_node in doc.getElementsByTagName("gui_cash_stats"):
796 self.gui_cash_stats.add_elements(node=gcs_node) # add/overwrite elements in self.gui_cash_stats
798 # s_sites = doc.getElementsByTagName("supported_sites")
799 for site_node in doc.getElementsByTagName("site"):
800 site = Site(node = site_node)
801 self.supported_sites[site.site_name] = site
803 # s_games = doc.getElementsByTagName("supported_games")
804 for game_node in doc.getElementsByTagName("game"):
805 game = Game(node = game_node)
806 self.supported_games[game.game_name] = game
808 # parse databases defined by user in the <supported_databases> section
809 # the user may select the actual database to use via commandline or by setting the selected="bool"
810 # attribute of the tag. if no database is explicitely selected, we use the first one we come across
811 # s_dbs = doc.getElementsByTagName("supported_databases")
812 #TODO: do we want to take all <database> tags or all <database> tags contained in <supported_databases>
813 # ..this may break stuff for some users. so leave it unchanged for now untill there is a decission
814 for db_node in doc.getElementsByTagName("database"):
815 db = Database(node=db_node)
816 if db.db_name in self.supported_databases:
817 raise ValueError("Database names must be unique")
818 if self.db_selected is None or db.db_selected:
819 self.db_selected = db.db_name
820 db_node.setAttribute("default", "True")
821 self.supported_databases[db.db_name] = db
822 #TODO: if the user may passes '' (empty string) as database name via command line, his choice is ignored
823 # ..when we parse the xml we allow for ''. there has to be a decission if to allow '' or not
824 if dbname and dbname in self.supported_databases:
825 self.db_selected = dbname
826 #NOTE: fpdb can not handle the case when no database is defined in xml, so we throw an exception for now
827 if self.db_selected is None:
828 raise ValueError('There must be at least one database defined')
830 # s_dbs = doc.getElementsByTagName("mucked_windows")
831 for aw_node in doc.getElementsByTagName("aw"):
832 aw = Aux_window(node = aw_node)
833 self.aux_windows[aw.name] = aw
835 # s_dbs = doc.getElementsByTagName("mucked_windows")
836 for hhc_node in doc.getElementsByTagName("hhc"):
837 hhc = HHC(node = hhc_node)
838 self.hhcs[hhc.site] = hhc
840 # s_dbs = doc.getElementsByTagName("popup_windows")
841 for pu_node in doc.getElementsByTagName("pu"):
842 pu = Popup(node = pu_node)
843 self.popup_windows[pu.name] = pu
845 for imp_node in doc.getElementsByTagName("import"):
846 imp = Import(node = imp_node)
847 self.imp = imp
849 for hui_node in doc.getElementsByTagName('hud_ui'):
850 hui = HudUI(node = hui_node)
851 self.ui = hui
853 db = self.get_db_parameters()
854 if db['db-password'] == 'YOUR MYSQL PASSWORD':
855 df_file = self.find_default_conf()
856 if df_file is None: # this is bad
857 pass
858 else:
859 df_parms = self.read_default_conf(df_file)
860 self.set_db_parameters(db_name = 'fpdb', db_ip = df_parms['db-host'],
861 db_user = df_parms['db-user'],
862 db_pass = df_parms['db-password'])
863 self.save(file=os.path.join(CONFIG_PATH, u"HUD_config.xml"))
865 if doc.getElementsByTagName("raw_hands") == []:
866 self.raw_hands = RawHands()
867 for raw_hands_node in doc.getElementsByTagName('raw_hands'):
868 self.raw_hands = RawHands(raw_hands_node)
870 if doc.getElementsByTagName("raw_tourneys") == []:
871 self.raw_tourneys = RawTourneys()
872 for raw_tourneys_node in doc.getElementsByTagName('raw_tourneys'):
873 self.raw_tourneys = RawTourneys(raw_tourneys_node)
875 #print ""
876 #end def __init__
878 def add_missing_elements(self, doc, example_file):
879 """ Look through example config file and add any elements that are not in the config
880 May need to add some 'enabled' attributes to turn things off - can't just delete a
881 config section now because this will add it back in"""
883 nodes_added = 0
885 try:
886 example_doc = xml.dom.minidom.parse(example_file)
887 except:
888 log.error((_("Error parsing example configuration file %s.") % (example_file)) + _("See error log file."))
889 return nodes_added
891 for cnode in doc.getElementsByTagName("FreePokerToolsConfig"):
892 for example_cnode in example_doc.childNodes:
893 if example_cnode.localName == "FreePokerToolsConfig":
894 for e in example_cnode.childNodes:
895 #print "nodetype", e.nodeType, "name", e.localName, "found", len(doc.getElementsByTagName(e.localName))
896 if e.nodeType == e.ELEMENT_NODE and doc.getElementsByTagName(e.localName) == []:
897 new = doc.importNode(e, True) # True means do deep copy
898 t_node = self.doc.createTextNode(" ")
899 cnode.appendChild(t_node)
900 cnode.appendChild(new)
901 t_node = self.doc.createTextNode("\r\n\r\n")
902 cnode.appendChild(t_node)
903 print "... adding missing config section: " + e.localName
904 nodes_added = nodes_added + 1
906 if nodes_added > 0:
907 print "Added %d missing config sections\n" % nodes_added
908 self.save()
910 return nodes_added
912 def find_default_conf(self):
913 if CONFIG_PATH:
914 config_file = os.path.join(CONFIG_PATH, u'default.conf')
915 else: config_file = False
917 if config_file and os.path.exists(config_file):
918 file = config_file
919 else:
920 file = None
921 return file
923 def get_doc(self):
924 return self.doc
926 def get_site_node(self, site):
927 for site_node in self.doc.getElementsByTagName("site"):
928 if site_node.getAttribute("site_name") == site:
929 return site_node
931 def getEmailNode(self, siteName, fetchType):
932 siteNode = self.get_site_node(siteName)
933 for emailNode in siteNode.getElementsByTagName("email"):
934 if emailNode.getAttribute("fetchType") == fetchType:
935 print "found emailNode"
936 return emailNode
937 break
938 #end def getEmailNode
940 def getGameNode(self,gameName):
941 """returns DOM game node for a given game"""
942 for gameNode in self.doc.getElementsByTagName("game"):
943 #print "getGameNode gameNode:",gameNode
944 if gameNode.getAttribute("game_name") == gameName:
945 return gameNode
946 #end def getGameNode
948 def get_aux_node(self, aux):
949 for aux_node in self.doc.getElementsByTagName("aw"):
950 if aux_node.getAttribute("name") == aux:
951 return aux_node
953 def get_db_node(self, db_name):
954 for db_node in self.doc.getElementsByTagName("database"):
955 if db_node.getAttribute("db_name") == db_name:
956 return db_node
957 return None
959 def get_layout_node(self, site_node, layout):
960 for layout_node in site_node.getElementsByTagName("layout"):
961 if layout_node.getAttribute("max") is None:
962 return None
963 if int( layout_node.getAttribute("max") ) == int( layout ):
964 return layout_node
966 def get_location_node(self, layout_node, seat):
967 if seat == "common":
968 for location_node in layout_node.getElementsByTagName("location"):
969 if location_node.hasAttribute("common"):
970 return location_node
971 else:
972 for location_node in layout_node.getElementsByTagName("location"):
973 if int( location_node.getAttribute("seat") ) == int( seat ):
974 return location_node
976 def save(self, file = None):
977 if file is None:
978 file = self.file
979 try:
980 shutil.move(file, file+".backup")
981 except:
982 pass
984 with open(file, 'w') as f:
985 #self.doc.writexml(f)
986 f.write( self.wrap_long_lines( self.doc.toxml() ) )
988 def wrap_long_lines(self, s):
989 lines = [ self.wrap_long_line(l) for l in s.splitlines() ]
990 return('\n'.join(lines) + '\n')
992 def wrap_long_line(self, l):
993 if 'config_wrap_len' in self.general:
994 wrap_len = int(self.general['config_wrap_len'])
995 else:
996 wrap_len = -1 # < 0 means no wrap
998 if wrap_len >= 0 and len(l) > wrap_len:
999 m = re.compile('\s+\S+\s+')
1000 mo = m.match(l)
1001 if mo:
1002 indent_len = mo.end()
1003 #print "indent = %s (%s)" % (indent_len, l[0:indent_len])
1004 indent = '\n' + ' ' * indent_len
1005 m = re.compile('(\S+="[^"]+"\s+)')
1006 parts = [x for x in m.split(l[indent_len:]) if x]
1007 if len(parts) > 1:
1008 #print "parts =", parts
1009 l = l[0:indent_len] + indent.join(parts)
1010 return(l)
1011 else:
1012 return(l)
1014 def editEmail(self, siteName, fetchType, newEmail):
1015 emailNode = self.getEmailNode(siteName, fetchType)
1016 emailNode.setAttribute("host", newEmail.host)
1017 emailNode.setAttribute("username", newEmail.username)
1018 emailNode.setAttribute("password", newEmail.password)
1019 emailNode.setAttribute("folder", newEmail.folder)
1020 emailNode.setAttribute("useSsl", newEmail.useSsl)
1021 #end def editEmail
1023 def edit_layout(self, site_name, max, width = None, height = None,
1024 fav_seat = None, locations = None):
1025 site_node = self.get_site_node(site_name)
1026 layout_node = self.get_layout_node(site_node, max)
1027 # TODO: how do we support inserting new layouts?
1028 if layout_node is None:
1029 return
1030 for i in range(1, max + 1):
1031 location_node = self.get_location_node(layout_node, i)
1032 location_node.setAttribute("x", str( locations[i-1][0] ))
1033 location_node.setAttribute("y", str( locations[i-1][1] ))
1034 self.supported_sites[site_name].layout[max].location[i] = ( locations[i-1][0], locations[i-1][1] )
1036 def edit_site(self, site_name, enabled, screen_name, history_path):
1037 site_node = self.get_site_node(site_name)
1038 site_node.setAttribute("enabled", enabled)
1039 site_node.setAttribute("screen_name", screen_name)
1040 site_node.setAttribute("HH_path", history_path)
1042 def editStats(self, gameName, statArray):
1043 """replaces stat selection for the given gameName with the given statArray"""
1044 gameNode = self.getGameNode(gameName)
1045 statNodes = gameNode.getElementsByTagName("stat")
1047 for node in statNodes:
1048 gameNode.removeChild(node)
1050 gameNode.setAttribute("rows", str(len(statArray)))
1051 gameNode.setAttribute("cols", str(len(statArray[0])))
1053 for rowNumber in range(len(statArray)):
1054 for columnNumber in range(len(statArray[rowNumber])):
1055 newStat=self.doc.createElement("stat")
1057 newAttrStatName=self.doc.createAttribute("stat_name")
1058 newStat.setAttributeNode(newAttrStatName)
1059 newStat.setAttribute("stat_name", statArray[rowNumber][columnNumber])
1061 newAttrStatName=self.doc.createAttribute("row")
1062 newStat.setAttributeNode(newAttrStatName)
1063 newStat.setAttribute("row", str(rowNumber))
1065 newAttrStatName=self.doc.createAttribute("col")
1066 newStat.setAttributeNode(newAttrStatName)
1067 newStat.setAttribute("col", str(columnNumber))
1069 newAttrStatName=self.doc.createAttribute("click")
1070 newStat.setAttributeNode(newAttrStatName)
1071 newStat.setAttribute("click", "tog_decorate")
1073 newAttrStatName=self.doc.createAttribute("popup")
1074 newStat.setAttributeNode(newAttrStatName)
1075 newStat.setAttribute("popup", "default")
1077 newAttrStatName=self.doc.createAttribute("tip")
1078 newStat.setAttributeNode(newAttrStatName)
1079 newStat.setAttribute("tip", "tip1")
1081 gameNode.appendChild(newStat)
1082 statNodes = gameNode.getElementsByTagName("stat") #TODO remove this line?
1083 #end def editStats
1085 def edit_aux_layout(self, aux_name, max, width = None, height = None, locations = None):
1086 aux_node = self.get_aux_node(aux_name)
1087 layout_node = self.get_layout_node(aux_node, max)
1088 if layout_node is None:
1089 print "aux node not found"
1090 return
1091 print "editing locations =", locations
1092 for (i, pos) in locations.iteritems():
1093 location_node = self.get_location_node(layout_node, i)
1094 location_node.setAttribute("x", str( locations[i][0] ))
1095 location_node.setAttribute("y", str( locations[i][1] ))
1096 if i == "common":
1097 self.aux_windows[aux_name].layout[max].common = ( locations[i][0], locations[i][1] )
1098 else:
1099 self.aux_windows[aux_name].layout[max].location[i] = ( locations[i][0], locations[i][1] )
1101 #NOTE: we got a nice Database class, so why map it again here?
1102 # user input validation should be done when initializing the Database class. this allows to give appropriate feddback when something goes wrong
1103 # try ..except is evil here. it swallows all kinds of errors. dont do this
1104 # 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_*)
1105 # 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
1106 # thus we can drop self.db_selected (holding database name) entirely and replace it with self._active_database = Database, avoiding to define the same
1107 # thing multiple times
1108 def get_db_parameters(self):
1109 db = {}
1110 name = self.db_selected
1111 # TODO: What's up with all the exception handling here?!
1112 try: db['db-databaseName'] = name
1113 except: pass
1115 try: db['db-desc'] = self.supported_databases[name].db_desc
1116 except: pass
1118 try: db['db-host'] = self.supported_databases[name].db_ip
1119 except: pass
1121 try: db['db-user'] = self.supported_databases[name].db_user
1122 except: pass
1124 try: db['db-password'] = self.supported_databases[name].db_pass
1125 except: pass
1127 try: db['db-server'] = self.supported_databases[name].db_server
1128 except: pass
1130 db['db-backend'] = self.get_backend(self.supported_databases[name].db_server)
1132 return db
1134 def set_db_parameters(self, db_name = 'fpdb', db_ip = None, db_user = None,
1135 db_pass = None, db_desc = None, db_server = None,
1136 default = "False"):
1137 db_node = self.get_db_node(db_name)
1138 default = default.lower()
1139 defaultb = string_to_bool(default, False)
1140 if db_node != None:
1141 if db_desc is not None: db_node.setAttribute("db_desc", db_desc)
1142 if db_ip is not None: db_node.setAttribute("db_ip", db_ip)
1143 if db_user is not None: db_node.setAttribute("db_user", db_user)
1144 if db_pass is not None: db_node.setAttribute("db_pass", db_pass)
1145 if db_server is not None: db_node.setAttribute("db_server", db_server)
1146 if defaultb or self.db_selected == db_name:
1147 db_node.setAttribute("default", "True")
1148 for dbn in self.doc.getElementsByTagName("database"):
1149 if dbn.getAttribute('db_name') != db_name and dbn.hasAttribute("default"):
1150 dbn.removeAttribute("default")
1151 elif db_node.hasAttribute("default"):
1152 db_node.removeAttribute("default")
1153 if self.supported_databases.has_key(db_name):
1154 if db_desc is not None: self.supported_databases[db_name].dp_desc = db_desc
1155 if db_ip is not None: self.supported_databases[db_name].dp_ip = db_ip
1156 if db_user is not None: self.supported_databases[db_name].dp_user = db_user
1157 if db_pass is not None: self.supported_databases[db_name].dp_pass = db_pass
1158 if db_server is not None: self.supported_databases[db_name].dp_server = db_server
1159 self.supported_databases[db_name].db_selected = defaultb
1160 if defaultb:
1161 self.db_selected = db_name
1162 return
1164 def add_db_parameters(self, db_name = 'fpdb', db_ip = None, db_user = None,
1165 db_pass = None, db_desc = None, db_server = None,
1166 default = "False"):
1167 default = default.lower()
1168 defaultb = string_to_bool(default, False)
1169 if db_name in self.supported_databases:
1170 raise ValueError("Database names must be unique")
1172 db_node = self.get_db_node(db_name)
1173 if db_node is None:
1174 for db_node in self.doc.getElementsByTagName("supported_databases"):
1175 # should only be one supported_databases element, use last one if there are several
1176 suppdb_node = db_node
1177 t_node = self.doc.createTextNode(" ")
1178 suppdb_node.appendChild(t_node)
1179 db_node = self.doc.createElement("database")
1180 suppdb_node.appendChild(db_node)
1181 t_node = self.doc.createTextNode("\r\n ")
1182 suppdb_node.appendChild(t_node)
1183 db_node.setAttribute("db_name", db_name)
1184 if db_desc is not None: db_node.setAttribute("db_desc", db_desc)
1185 if db_ip is not None: db_node.setAttribute("db_ip", db_ip)
1186 if db_user is not None: db_node.setAttribute("db_user", db_user)
1187 if db_pass is not None: db_node.setAttribute("db_pass", db_pass)
1188 if db_server is not None: db_node.setAttribute("db_server", db_server)
1189 if defaultb:
1190 db_node.setAttribute("default", "True")
1191 for dbn in self.doc.getElementsByTagName("database"):
1192 if dbn.getAttribute('db_name') != db_name and dbn.hasAttribute("default"):
1193 dbn.removeAttribute("default")
1194 elif db_node.hasAttribute("default"):
1195 db_node.removeAttribute("default")
1196 else:
1197 if db_desc is not None: db_node.setAttribute("db_desc", db_desc)
1198 if db_ip is not None: db_node.setAttribute("db_ip", db_ip)
1199 if db_user is not None: db_node.setAttribute("db_user", db_user)
1200 if db_pass is not None: db_node.setAttribute("db_pass", db_pass)
1201 if db_server is not None: db_node.setAttribute("db_server", db_server)
1202 if defaultb or self.db_selected == db_name:
1203 db_node.setAttribute("default", "True")
1204 elif db_node.hasAttribute("default"):
1205 db_node.removeAttribute("default")
1207 if self.supported_databases.has_key(db_name):
1208 if db_desc is not None: self.supported_databases[db_name].dp_desc = db_desc
1209 if db_ip is not None: self.supported_databases[db_name].dp_ip = db_ip
1210 if db_user is not None: self.supported_databases[db_name].dp_user = db_user
1211 if db_pass is not None: self.supported_databases[db_name].dp_pass = db_pass
1212 if db_server is not None: self.supported_databases[db_name].dp_server = db_server
1213 self.supported_databases[db_name].db_selected = defaultb
1214 else:
1215 db = Database(node=db_node)
1216 self.supported_databases[db.db_name] = db
1218 if defaultb:
1219 self.db_selected = db_name
1220 return
1222 def get_backend(self, name):
1223 """Returns the number of the currently used backend"""
1224 if name == DATABASE_TYPE_MYSQL:
1225 ret = 2
1226 elif name == DATABASE_TYPE_POSTGRESQL:
1227 ret = 3
1228 elif name == DATABASE_TYPE_SQLITE:
1229 ret = 4
1230 # sqlcoder: this assignment fixes unicode problems for me with sqlite (windows, cp1252)
1231 # feel free to remove or improve this if you understand the problems
1232 # better than me (not hard!)
1233 Charset.not_needed1, Charset.not_needed2, Charset.not_needed3 = True, True, True
1234 else:
1235 raise ValueError('Unsupported database backend: %s' % self.supported_databases[name].db_server)
1237 return ret
1239 def getDefaultSite(self):
1240 "Returns first enabled site or None"
1241 for site_name,site in self.supported_sites.iteritems():
1242 if site.enabled:
1243 return site_name
1244 return None
1246 # Allow to change the menu appearance
1247 def get_hud_ui_parameters(self):
1248 hui = {}
1250 default_text = 'FPDB Menu - Right click\nLeft-Drag to Move'
1251 try:
1252 hui['label'] = self.ui.label
1253 if self.ui.label == '': # Empty menu label is a big no-no
1254 hui['label'] = default_text
1255 except:
1256 hui['label'] = default_text
1258 try: hui['hud_style'] = self.ui.hud_style
1259 except: hui['hud_style'] = 'A' # default is show stats for All-time, also S(session) and T(ime)
1261 try: hui['hud_days'] = int(self.ui.hud_days)
1262 except: hui['hud_days'] = 90
1264 try: hui['aggregate_ring'] = self.ui.aggregate_ring
1265 except: hui['aggregate_ring'] = False
1267 try: hui['aggregate_tour'] = self.ui.aggregate_tour
1268 except: hui['aggregate_tour'] = True
1270 try: hui['agg_bb_mult'] = self.ui.agg_bb_mult
1271 except: hui['agg_bb_mult'] = 1
1273 try: hui['seats_style'] = self.ui.seats_style
1274 except: hui['seats_style'] = 'A' # A / C / E, use A(ll) / C(ustom) / E(xact) seat numbers
1276 try: hui['seats_cust_nums'] = self.ui.seats_cust_nums
1277 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)]
1279 # Hero specific
1281 try: hui['h_hud_style'] = self.ui.h_hud_style
1282 except: hui['h_hud_style'] = 'S'
1284 try: hui['h_hud_days'] = int(self.ui.h_hud_days)
1285 except: hui['h_hud_days'] = 30
1287 try: hui['h_aggregate_ring'] = self.ui.h_aggregate_ring
1288 except: hui['h_aggregate_ring'] = False
1290 try: hui['h_aggregate_tour'] = self.ui.h_aggregate_tour
1291 except: hui['h_aggregate_tour'] = True
1293 try: hui['h_agg_bb_mult'] = self.ui.h_agg_bb_mult
1294 except: hui['h_agg_bb_mult'] = 1
1296 try: hui['h_seats_style'] = self.ui.h_seats_style
1297 except: hui['h_seats_style'] = 'A' # A / C / E, use A(ll) / C(ustom) / E(xact) seat numbers
1299 try: hui['h_seats_cust_nums'] = self.ui.h_seats_cust_nums
1300 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)]
1302 return hui
1305 def get_import_parameters(self):
1306 imp = {}
1307 try: imp['callFpdbHud'] = self.imp.callFpdbHud
1308 except: imp['callFpdbHud'] = True
1310 try: imp['interval'] = self.imp.interval
1311 except: imp['interval'] = 10
1313 # ResultsDirectory is the local cache for downloaded results
1314 # NOTE: try: except: doesn'tseem to be triggering
1315 # using if instead
1316 if self.imp.ResultsDirectory != '':
1317 imp['ResultsDirectory'] = self.imp.ResultsDirectory
1318 else:
1319 imp['ResultsDirectory'] = "~/.fpdb/Results/"
1321 # hhBulkPath is the default location for bulk imports (if set)
1322 try: imp['hhBulkPath'] = self.imp.hhBulkPath
1323 except: imp['hhBulkPath'] = ""
1325 try: imp['saveActions'] = self.imp.saveActions
1326 except: imp['saveActions'] = False
1328 try: imp['cacheSessions'] = self.imp.cacheSessions
1329 except: imp['cacheSessions'] = False
1331 try: imp['sessionTimeout'] = self.imp.sessionTimeout
1332 except: imp['sessionTimeout'] = 30
1334 try: imp['saveStarsHH'] = self.imp.saveStarsHH
1335 except: imp['saveStarsHH'] = False
1337 try: imp['fastStoreHudCache'] = self.imp.fastStoreHudCache
1338 except: imp['fastStoreHudCache'] = True
1340 return imp
1342 def get_default_paths(self, site = None):
1343 if site is None: site = self.getDefaultSite()
1344 paths = {}
1345 try:
1346 path = os.path.expanduser(self.supported_sites[site].HH_path)
1347 assert(os.path.isdir(path) or os.path.isfile(path)) # maybe it should try another site?
1348 paths['hud-defaultPath'] = paths['bulkImport-defaultPath'] = path
1349 if self.imp.hhBulkPath:
1350 paths['bulkImport-defaultPath'] = self.imp.hhBulkPath
1351 except AssertionError:
1352 paths['hud-defaultPath'] = paths['bulkImport-defaultPath'] = "** ERROR DEFAULT PATH IN CONFIG DOES NOT EXIST **"
1353 return paths
1355 def get_frames(self, site = "PokerStars"):
1356 if site not in self.supported_sites: return False
1357 return self.supported_sites[site].use_frames == True
1359 def get_default_colors(self, site = "PokerStars"):
1360 colors = {}
1361 if site not in self.supported_sites or self.supported_sites[site].hudopacity == "":
1362 colors['hudopacity'] = 0.90
1363 else:
1364 colors['hudopacity'] = float(self.supported_sites[site].hudopacity)
1365 if site not in self.supported_sites or self.supported_sites[site].hudbgcolor == "":
1366 colors['hudbgcolor'] = "#FFFFFF"
1367 else:
1368 colors['hudbgcolor'] = self.supported_sites[site].hudbgcolor
1369 if site not in self.supported_sites or self.supported_sites[site].hudfgcolor == "":
1370 colors['hudfgcolor'] = "#000000"
1371 else:
1372 colors['hudfgcolor'] = self.supported_sites[site].hudfgcolor
1373 return colors
1375 def get_default_font(self, site='PokerStars'):
1376 font = "Sans"
1377 font_size = "8"
1378 site = self.supported_sites.get(site, None)
1379 if site is not None:
1380 if site.font:
1381 font = site.font
1382 if site.font_size:
1383 font_size = site.font_size
1384 return font, font_size
1386 def get_locations(self, site_name="PokerStars", max=8):
1387 site = self.supported_sites.get(site_name, None)
1388 if site is not None:
1389 location = site.layout.get(max, None)
1390 if location is not None:
1391 return location.location
1392 return (
1393 ( 0, 0), (684, 61), (689, 239), (692, 346),
1394 (586, 393), (421, 440), (267, 440), ( 0, 361),
1395 ( 0, 280), (121, 280), ( 46, 30)
1398 def get_aux_locations(self, aux = "mucked", max = "9"):
1400 try:
1401 locations = self.aux_windows[aux].layout[max].location
1402 except:
1403 locations = ( ( 0, 0), (684, 61), (689, 239), (692, 346),
1404 (586, 393), (421, 440), (267, 440), ( 0, 361),
1405 ( 0, 280), (121, 280), ( 46, 30) )
1406 return locations
1408 def get_supported_sites(self, all=False):
1409 """Returns the list of supported sites."""
1410 if all:
1411 return self.supported_sites.keys()
1412 else:
1413 return [site_name for (site_name, site) in self.supported_sites.items() if site.enabled]
1415 def get_site_parameters(self, site):
1416 """Returns a dict of the site parameters for the specified site"""
1417 parms = {}
1418 parms["converter"] = self.supported_sites[site].converter
1419 parms["decoder"] = self.supported_sites[site].decoder
1420 parms["hudbgcolor"] = self.supported_sites[site].hudbgcolor
1421 parms["hudfgcolor"] = self.supported_sites[site].hudfgcolor
1422 parms["hudopacity"] = self.supported_sites[site].hudopacity
1423 parms["screen_name"] = self.supported_sites[site].screen_name
1424 parms["site_path"] = self.supported_sites[site].site_path
1425 parms["table_finder"] = self.supported_sites[site].table_finder
1426 parms["HH_path"] = self.supported_sites[site].HH_path
1427 parms["site_name"] = self.supported_sites[site].site_name
1428 parms["aux_window"] = self.supported_sites[site].aux_window
1429 parms["font"] = self.supported_sites[site].font
1430 parms["font_size"] = self.supported_sites[site].font_size
1431 parms["enabled"] = self.supported_sites[site].enabled
1432 parms["xpad"] = self.supported_sites[site].xpad
1433 parms["ypad"] = self.supported_sites[site].ypad
1434 parms["xshift"] = self.supported_sites[site].xshift
1435 parms["yshift"] = self.supported_sites[site].yshift
1436 return parms
1438 def set_site_parameters(self, site_name, converter = None, decoder = None,
1439 hudbgcolor = None, hudfgcolor = None,
1440 hudopacity = None, screen_name = None,
1441 site_path = None, table_finder = None,
1442 HH_path = None, enabled = None,
1443 font = None, font_size = None):
1444 """Sets the specified site parameters for the specified site."""
1445 site_node = self.get_site_node(site_name)
1446 if db_node is not None:
1447 if converter is not None: site_node.setAttribute("converter", converter)
1448 if decoder is not None: site_node.setAttribute("decoder", decoder)
1449 if hudbgcolor is not None: site_node.setAttribute("hudbgcolor", hudbgcolor)
1450 if hudfgcolor is not None: site_node.setAttribute("hudfgcolor", hudfgcolor)
1451 if hudopacity is not None: site_node.setAttribute("hudopacity", hudopacity)
1452 if screen_name is not None: site_node.setAttribute("screen_name", screen_name)
1453 if site_path is not None: site_node.setAttribute("site_path", site_path)
1454 if table_finder is not None: site_node.setAttribute("table_finder", table_finder)
1455 if HH_path is not None: site_node.setAttribute("HH_path", HH_path)
1456 if enabled is not None: site_node.setAttribute("enabled", enabled)
1457 if font is not None: site_node.setAttribute("font", font)
1458 if font_size is not None: site_node.setAttribute("font_size", font_size)
1459 return
1461 def set_site_ids(self, sites):
1462 self.site_ids = dict(sites)
1464 def get_site_id(self, site):
1465 return( self.site_ids[site] )
1467 def get_aux_windows(self):
1468 """Gets the list of mucked window formats in the configuration."""
1469 return self.aux_windows.keys()
1471 def get_aux_parameters(self, name):
1472 """Gets a dict of mucked window parameters from the named mw."""
1473 param = {}
1474 if self.aux_windows.has_key(name):
1475 for key in dir(self.aux_windows[name]):
1476 if key.startswith('__'): continue
1477 value = getattr(self.aux_windows[name], key)
1478 if callable(value): continue
1479 param[key] = value
1481 return param
1482 return None
1484 def get_game_parameters(self, name):
1485 """Get the configuration parameters for the named game."""
1486 param = {}
1487 if self.supported_games.has_key(name):
1488 param['game_name'] = self.supported_games[name].game_name
1489 param['rows'] = self.supported_games[name].rows
1490 param['cols'] = self.supported_games[name].cols
1491 param['xpad'] = self.supported_games[name].xpad
1492 param['ypad'] = self.supported_games[name].ypad
1493 param['xshift'] = self.supported_games[name].xshift
1494 param['yshift'] = self.supported_games[name].yshift
1495 param['aux'] = self.supported_games[name].aux
1496 return param
1498 def get_supported_games(self):
1499 """Get the list of supported games."""
1500 sg = []
1501 for game in self.supported_games.keys():
1502 sg.append(self.supported_games[game].game_name)
1503 return sg
1505 def execution_path(self, filename):
1506 """Join the fpdb path to filename."""
1507 return os.path.join(os.path.dirname(inspect.getfile(sys._getframe(0))), filename)
1509 def get_general_params(self):
1510 return( self.general )
1512 def get_gui_cash_stat_params(self):
1513 return( self.gui_cash_stats )
1515 if __name__== "__main__":
1516 c = Config()
1518 print "\n----------- SUPPORTED SITES -----------"
1519 for s in c.supported_sites.keys():
1520 print c.supported_sites[s]
1521 print "----------- END SUPPORTED SITES -----------"
1524 print "\n----------- SUPPORTED GAMES -----------"
1525 for game in c.supported_games.keys():
1526 print c.supported_games[game]
1527 print "----------- END SUPPORTED GAMES -----------"
1530 print "\n----------- SUPPORTED DATABASES -----------"
1531 for db in c.supported_databases.keys():
1532 print c.supported_databases[db]
1533 print "----------- END SUPPORTED DATABASES -----------"
1535 print "\n----------- AUX WINDOW FORMATS -----------"
1536 for w in c.aux_windows.keys():
1537 print c.aux_windows[w]
1538 print "----------- END AUX WINDOW FORMATS -----------"
1540 print "\n----------- HAND HISTORY CONVERTERS -----------"
1541 for w in c.hhcs.keys():
1542 print c.hhcs[w]
1543 print "----------- END HAND HISTORY CONVERTERS -----------"
1545 print "\n----------- POPUP WINDOW FORMATS -----------"
1546 for w in c.popup_windows.keys():
1547 print c.popup_windows[w]
1548 print "----------- END POPUP WINDOW FORMATS -----------"
1550 print "\n----------- IMPORT -----------"
1551 # print c.imp # Need to add an str method for imp to print
1552 print "----------- END IMPORT -----------"
1554 c.edit_layout("PokerStars", 6, locations=( (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6) ))
1555 c.save(file="testout.xml")
1557 print "db = ", c.get_db_parameters()
1558 # print "imp = ", c.get_import_parameters()
1559 print "paths = ", c.get_default_paths("PokerStars")
1560 print "colors = ", c.get_default_colors("PokerStars")
1561 print "locs = ", c.get_locations("PokerStars", 8)
1562 for mw in c.get_aux_windows():
1563 print c.get_aux_parameters(mw)
1565 print "mucked locations =", c.get_aux_locations('mucked', 9)
1566 # c.edit_aux_layout('mucked', 9, locations = [(487, 113), (555, 469), (572, 276), (522, 345),
1567 # (333, 354), (217, 341), (150, 273), (150, 169), (230, 115)])
1568 # print "mucked locations =", c.get_aux_locations('mucked', 9)
1570 for site in c.supported_sites.keys():
1571 print "site = ", site,
1572 print c.get_site_parameters(site)
1573 print c.get_default_font(site)
1575 for game in c.get_supported_games():
1576 print c.get_game_parameters(game)
1578 for hud_param, value in c.get_hud_ui_parameters().iteritems():
1579 print "hud param %s = %s" % (hud_param, value)
1581 print "start up path = ", c.execution_path("")
1583 print "gui_cash_stats =", c.gui_cash_stats
1585 try:
1586 from xml.dom.ext import PrettyPrint
1587 for site_node in c.doc.getElementsByTagName("site"):
1588 PrettyPrint(site_node, stream=sys.stdout, encoding="utf-8")
1589 except:
1590 print "xml.dom.ext needs PyXML to be installed!"
1592 print "\n----------- ENVIRONMENT CONSTANTS -----------"
1593 print "Configuration.install_method {source,exe} =", INSTALL_METHOD
1594 print "Configuration.fpdb_program_path =", FPDB_PROGRAM_PATH
1595 print "Configuration.appdata_path =", APPDATA_PATH
1596 print "Configuration.config_path =", CONFIG_PATH
1597 print "Configuration.os_family {Linux,Mac,XP,Win7} =", OS_FAMILY
1598 print "Configuration.posix {True/False} =", POSIX
1599 print "Configuration.python_version =", PYTHON_VERSION
1600 print "----------- END ENVIRONMENT CONSTANTS -----------"
1602 print "press enter to end"
1603 sys.stdin.readline()