Coverage for Configuration.py: 47%
1438 statements
« prev ^ index » next coverage.py v7.6.3, created at 2024-10-15 19:33 +0000
« prev ^ index » next coverage.py v7.6.3, created at 2024-10-15 19:33 +0000
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3# Configuration####
4# Handles fpdb/fpdb-hud configuration files.
5# Copyright 2008-2012, Ray E. Barker
6#
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation; either version 2 of the License, or
10# (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program; if not, write to the Free Software
19# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21########################################################################
23# TODO fix / rethink edit stats - it is broken badly just now
25# Standard Library modules
26from __future__ import with_statement
27from __future__ import print_function
30# import L10n
31# _ = L10n.get_translation()
33import codecs
34import os
35import sys
36import inspect
37import shutil
38import locale
39import re
40import xml.dom.minidom
42# # import Charset
43import platform
44import traceback
47if platform.system() == "Windows":
48 # import winpaths
49 # winpaths_appdata = winpaths.get_appdata()
50 import os
52 winpaths_appdata = os.getenv("APPDATA")
53 # winpaths_appdata = os.getcwd()
54 winpaths_appdata = winpaths_appdata.replace("\\", "/")
55 print("winpaths_appdata:") # debug
56 print(winpaths_appdata) # debug
57else:
58 winpaths_appdata = False
60import logging, logging.config
63# config version is used to flag a warning at runtime if the users config is
64# out of date.
65# The CONFIG_VERSION should be incremented __ONLY__ if the add_missing_elements()
66# method cannot update existing standard configurations
67CONFIG_VERSION = 83
69#
70# Setup constants
71# code is centralised here to ensure uniform handling of path names
72# especially important when user directory includes non-ascii chars
73#
74# INSTALL_METHOD ("source" or "exe")
75# FPDB_ROOT_PATH (path to the root fpdb installation dir root (normally ...../fpdb)
76# APPDATA_PATH (root path for appdata eg /~ or appdata)
77# CONFIG_PATH (path to the directory holding logs, sqlite db's and config)
78# GRAPHICS_PATH (path to graphics assets (normally .gfx)
79# PYFPDB_PATH (path to py's)
80# OS_FAMILY (OS Family for installed system (Linux, Mac, XP, Win7)
81# POSIX (True=Linux or Mac platform, False=Windows platform)
82# PYTHON_VERSION (n.n)
84if hasattr(sys, "frozen"):
85 if platform.system() == "Windows":
86 INSTALL_METHOD = "exe"
87 elif platform.system() == "Darwin":
88 INSTALL_METHOD = "app"
89 elif "APPDIR" in os.environ:
90 INSTALL_METHOD = "appimage"
91 else:
92 INSTALL_METHOD = "unknown"
93else:
94 INSTALL_METHOD = "source"
96if INSTALL_METHOD == "exe":
97 FPDB_ROOT_PATH = os.path.dirname(sys.executable)
99 FPDB_ROOT_PATH = FPDB_ROOT_PATH.replace("\\", "/")
100# should be exe path to \fpdbroot\pyfpdb
101elif INSTALL_METHOD == "app":
102 FPDB_ROOT_PATH = os.path.dirname(sys.executable)
103elif INSTALL_METHOD == "appimage":
104 FPDB_ROOT_PATH = os.environ["APPDIR"]
105elif sys.path[0] == "": # we are probably running directly (>>>import Configuration)
106 temp = os.getcwd() # should be ./pyfpdb
107 print(temp)
108 FPDB_ROOT_PATH = os.path.join(temp, os.pardir) # go up one level (to fpdbroot)
109else: # all other cases
110 # FPDB_ROOT_PATH = os.path.dirname(sys.path[0]) # should be source path to /fpdbroot
111 FPDB_ROOT_PATH = os.getcwd()
113sysPlatform = platform.system() # Linux, Windows, Darwin
114if sysPlatform[0:5] == "Linux":
115 OS_FAMILY = "Linux"
116elif sysPlatform == "Darwin":
117 OS_FAMILY = "Mac"
118elif sysPlatform == "Windows":
119 if platform.release() != "XP":
120 OS_FAMILY = "Win7" # Vista and win7
121 else:
122 OS_FAMILY = "XP"
123else:
124 OS_FAMILY = False
126# GRAPHICS_PATH = os.path.join(FPDB_ROOT_PATH, "gfx")
127# PYFPDB_PATH = os.path.join(FPDB_ROOT_PATH, "pyfpdb")
129if OS_FAMILY in ["XP", "Win7"]:
130 APPDATA_PATH = winpaths_appdata
131 CONFIG_PATH = os.path.join(APPDATA_PATH, "fpdb")
132 CONFIG_PATH = CONFIG_PATH.replace("\\", "/")
133 FPDB_ROOT_PATH = os.path.dirname(sys.executable)
134 FPDB_ROOT_PATH = FPDB_ROOT_PATH.replace("\\", "/")
135 if INSTALL_METHOD == "source":
136 script = os.path.realpath(__file__)
137 print("SCript path:", script)
138 script = script.replace("\\", "/")
139 script = script.rsplit("/", 1)[0]
140 GRAPHICS_PATH = script + "/gfx"
141 else:
142 GRAPHICS_PATH = os.path.join(FPDB_ROOT_PATH, "gfx")
143 GRAPHICS_PATH = GRAPHICS_PATH.replace("\\", "/")
144 PYFPDB_PATH = os.path.join(FPDB_ROOT_PATH, "pyfpdb")
145 PYFPDB_PATH = PYFPDB_PATH.replace("\\", "/")
146elif OS_FAMILY == "Mac":
147 APPDATA_PATH = os.getenv("HOME")
148 CONFIG_PATH = os.path.join(APPDATA_PATH, ".fpdb")
149 GRAPHICS_PATH = os.path.join(FPDB_ROOT_PATH, "gfx")
150 PYFPDB_PATH = os.path.join(FPDB_ROOT_PATH, "pyfpdb")
151elif OS_FAMILY == "Linux":
152 APPDATA_PATH = os.path.expanduser("~")
153 CONFIG_PATH = os.path.join(APPDATA_PATH, ".fpdb")
154 GRAPHICS_PATH = os.path.join(FPDB_ROOT_PATH, "gfx")
155 PYFPDB_PATH = os.path.join(FPDB_ROOT_PATH)
156else:
157 APPDATA_PATH = False
158 CONFIG_PATH = False
160if os.name == "posix":
161 POSIX = True
162else:
163 POSIX = False
165PYTHON_VERSION = sys.version[:3]
167# logging has been set up in fpdb.py or HUD_main.py, use their settings:
168log = logging.getLogger("config")
170LOGLEVEL = {
171 "DEBUG": logging.DEBUG,
172 "INFO": logging.INFO,
173 "WARNING": logging.WARNING,
174 "ERROR": logging.ERROR,
175 "CRITICAL": logging.CRITICAL,
176}
179def get_config(file_name, fallback=True):
180 """Looks in cwd and in self.default_config_path for a config file.
181 -- FIXME --
182 This function has become difficult to understand, plus it no-longer
183 just looks for a config file, it actually does file copying
184 """
186 # look for example file even if not used here, path is returned to caller
187 config_found, example_copy = False, False
188 config_path, example_path = None, None
189 if sysPlatform == "Windows":
190 # print('-> windows')
191 if platform.release() != "XP":
192 OS_FAMILY = "Win7" # Vista and win7
193 # print('-> windows Win7')
194 else:
195 OS_FAMILY = "XP"
196 # print('-> windows XP')
197 if OS_FAMILY == "XP" or "Win7":
198 # print('-> windows XP or Win7')
199 config_path = os.path.join(CONFIG_PATH, file_name)
200 config_path = config_path.replace("\\", "/")
201 else:
202 config_path = os.path.join(CONFIG_PATH, file_name)
203 if os.path.exists(config_path): # there is a file in the cwd
204 config_found = True
205 fallback = False
206 else: # no file in the cwd, look where it should be in the first place
207 config_path = os.path.join(CONFIG_PATH, file_name)
208 config_path = config_path.replace("\\", "/")
209 if os.path.exists(config_path):
210 config_found = True
211 fallback = False
213 if POSIX:
214 # If we're on linux, try to copy example from the place
215 # debian package puts it; get_default_config_path() creates
216 # the config directory for us so there's no need to check it
217 # again
218 example_path = "/usr/share/python-fpdb/" + file_name + ".example"
219 if not os.path.exists(example_path):
220 if os.path.exists(file_name + ".example"):
221 example_path = file_name + ".example"
222 else:
223 example_path = os.path.join(PYFPDB_PATH, file_name + ".example")
224 if not config_found and fallback:
225 try:
226 shutil.copyfile(example_path, config_path)
227 example_copy = True
228 msg = ("Config file has been created at %s.") % (config_path)
229 log.info(msg)
230 except IOError:
231 try:
232 example_path = file_name + ".example"
233 shutil.copyfile(example_path, config_path)
234 example_copy = True
235 msg = ("Config file has been created at %s.") % (config_path)
236 log.info(msg)
237 except IOError:
238 pass
240 # OK, fall back to the .example file, should be in the start dir
242 elif os.path.exists(os.path.join(CONFIG_PATH, file_name + ".example").replace("\\", "/")):
243 try:
244 example_path = os.path.join(CONFIG_PATH, file_name + ".example").replace("\\", "/")
245 if not config_found and fallback:
246 shutil.copyfile(example_path, config_path)
247 example_copy = True
248 log.info(
249 ('No %r found in "%r" or "%r".') % (file_name, FPDB_ROOT_PATH, CONFIG_PATH)
250 + " "
251 + ("Config file has been created at %r.") % (config_path + "\n")
252 )
254 except IOError:
255 print((("Error copying .example config file, cannot fall back. Exiting."), "\n"))
256 sys.stderr.write(("Error copying .example config file, cannot fall back. Exiting.") + "\n")
257 sys.stderr.write(str(sys.exc_info()))
258 sys.exit()
259 elif fallback:
260 sys.stderr.write((("No %s found, cannot fall back. Exiting.") % file_name) + "\n")
261 sys.exit()
263 return (config_path, example_copy, example_path)
266def set_logfile(file_name):
267 (conf_file, copied, example_file) = get_config("logging.conf", fallback=False)
269 log_dir = os.path.join(CONFIG_PATH, "log").replace("\\", "/")
270 check_dir(log_dir)
271 log_file = os.path.join(log_dir, file_name).replace("\\", "/")
272 if os.path.isfile(conf_file):
273 print("logging.conf file already exists")
274 else:
275 # create a file
276 # FIME: why printing that a file is going to be copied but not doing anything ?
277 # print('copying logging.conf file in appdata rooming folder')
278 pass
279 if conf_file:
280 try:
281 log_file = log_file.replace("\\", "/") # replace each \ with \\
282 print(f"Using logging configfile: {conf_file}")
283 logging.config.fileConfig(conf_file, {"logFile": log_file})
284 except Exception:
285 sys.stderr.write(f"Could not setup log file {file_name}")
288def check_dir(path, create=True):
289 """Check if a dir exists, optionally creates if not."""
290 if os.path.exists(path):
291 if os.path.isdir(path):
292 return path
293 else:
294 return False
295 if create:
296 path = path.replace("\\", "/")
297 msg = ("Creating directory: '%s'") % (path)
298 print(msg)
299 log.info(msg)
300 os.makedirs(path) # , "utf-8"))
301 else:
302 return False
305def normalizePath(path):
306 "Normalized existing pathes"
307 if os.path.exists(path):
308 return os.path.abspath(path)
309 return path
312########################################################################
313# application wide consts
315APPLICATION_NAME_SHORT = "fpdb"
316APPLICATION_VERSION = "xx.xx.xx"
318DATABASE_TYPE_POSTGRESQL = "postgresql"
319DATABASE_TYPE_SQLITE = "sqlite"
320DATABASE_TYPE_MYSQL = "mysql"
321DATABASE_TYPES = (
322 DATABASE_TYPE_POSTGRESQL,
323 DATABASE_TYPE_SQLITE,
324 DATABASE_TYPE_MYSQL,
325)
327LOCALE_ENCODING = locale.getpreferredencoding()
328if LOCALE_ENCODING in ("US-ASCII", "", None):
329 LOCALE_ENCODING = "cp1252"
330 if os.uname()[0] != "Darwin":
331 print((("Default encoding set to US-ASCII, defaulting to CP1252 instead."), ("Please report this problem.")))
333# needs LOCALE_ENCODING (above), imported for sqlite setup in Config class below
336########################################################################
337def string_to_bool(string, default=True):
338 """converts a string representation of a boolean value to boolean True or False
339 @param string: (str) the string to convert
340 @param default: value to return if the string can not be converted to a boolean value
341 """
342 string = string.lower()
343 if string in ("1", "true", "t"):
344 return True
345 elif string in ("0", "false", "f"):
346 return False
347 return default
350class Layout(object):
351 def __init__(self, node):
352 self.max = int(node.getAttribute("max"))
353 self.width = int(node.getAttribute("width"))
354 self.height = int(node.getAttribute("height"))
356 self.location = []
357 self.hh_seats = []
358 self.location = [None for x in range(self.max + 1)] # fill array with max seats+1 empty entries
359 # hh_seats is used to map the seat numbers specified in hand history files (and stored in db) onto
360 # the contiguous integerss, 1 to self.max, used to index hud stat_windows (and aw seat_windows) for display
361 # For most sites these numbers are the same, but some sites (e.g. iPoker) omit seat numbers in hand histories
362 # for tables smaller than 10-max.
363 self.hh_seats = [None for x in range(self.max + 1)] # fill array with max seats+1 empty entries
365 for location_node in node.getElementsByTagName("location"):
366 hud_seat = location_node.getAttribute("seat")
367 if hud_seat != "":
368 # if hist_seat for this seat number is specified in the layout, then store it in the hh_seats list
369 hist_seat = location_node.getAttribute("hist_seat") # XXX
370 if hist_seat:
371 self.hh_seats[int(hud_seat)] = int(hist_seat)
372 else:
373 # .. otherwise just store the original seat number in the hh_seats list
374 self.hh_seats[int(hud_seat)] = int(hud_seat)
375 self.location[int(hud_seat)] = (
376 int(location_node.getAttribute("x")),
377 int(location_node.getAttribute("y")),
378 )
379 elif location_node.getAttribute("common") != "":
380 self.common = (int(location_node.getAttribute("x")), int(location_node.getAttribute("y")))
382 def __str__(self):
383 if hasattr(self, "name"):
384 name = str(self.name)
385 log.info(f"attribut {name} exists")
386 temp = " Layout = %d max, width= %d, height = %d" % (self.max, self.width, self.height)
387 if hasattr(self, "fav_seat"):
388 temp = temp + ", fav_seat = %d\n" % self.fav_seat
389 else:
390 temp = temp + "\n"
391 if hasattr(self, "common"):
392 temp = temp + " Common = (%d, %d)\n" % (self.common[0], self.common[1])
393 temp = temp + " Locations = "
394 for i in range(1, len(self.location)):
395 temp = temp + "%s:(%d,%d) " % (self.hh_seats[i], self.location[i][0], self.location[i][1])
396 return temp + "\n"
399class Email(object):
400 def __init__(self, node):
401 self.node = node
402 self.host = node.getAttribute("host")
403 self.username = node.getAttribute("username")
404 self.password = node.getAttribute("password")
405 self.useSsl = node.getAttribute("useSsl")
406 self.folder = node.getAttribute("folder")
407 self.fetchType = node.getAttribute("fetchType")
409 def __str__(self):
410 return (
411 " email\n fetchType = %s host = %s\n username = %s password = %s\n useSsl = %s folder = %s"
412 % (self.fetchType, self.host, self.username, self.password, self.useSsl, self.folder)
413 )
416class Site(object):
417 def __init__(self, node):
418 self.site_name = node.getAttribute("site_name")
419 self.screen_name = node.getAttribute("screen_name")
420 self.site_path = normalizePath(node.getAttribute("site_path"))
421 self.HH_path = normalizePath(node.getAttribute("HH_path"))
422 self.TS_path = normalizePath(node.getAttribute("TS_path"))
423 self.enabled = string_to_bool(node.getAttribute("enabled"), default=True)
424 self.aux_enabled = string_to_bool(node.getAttribute("aux_enabled"), default=True)
425 self.hud_menu_xshift = node.getAttribute("hud_menu_xshift")
426 self.hud_menu_xshift = 1 if self.hud_menu_xshift == "" else int(self.hud_menu_xshift)
427 self.hud_menu_yshift = node.getAttribute("hud_menu_yshift")
428 self.hud_menu_yshift = 1 if self.hud_menu_yshift == "" else int(self.hud_menu_yshift)
429 if node.hasAttribute("TS_path"):
430 self.TS_path = normalizePath(node.getAttribute("TS_path"))
431 else:
432 self.TS_path = ""
434 self.fav_seat = {}
435 for fav_node in node.getElementsByTagName("fav"):
436 max = int(fav_node.getAttribute("max"))
437 fav = int(fav_node.getAttribute("fav_seat"))
438 self.fav_seat[max] = fav
440 self.layout_set = {}
441 for site_layout_node in node.getElementsByTagName("layout_set"):
442 gt = site_layout_node.getAttribute("game_type")
443 ls = site_layout_node.getAttribute("ls")
444 self.layout_set[gt] = ls
446 self.emails = {}
447 for email_node in node.getElementsByTagName("email"):
448 email = Email(email_node)
449 self.emails[email.fetchType] = email
451 def __str__(self):
452 temp = "Site = " + self.site_name + "\n"
453 for key in dir(self):
454 if key.startswith("__"):
455 continue
456 if key == "layout_set":
457 continue
458 if key == "fav_seat":
459 continue
460 if key == "emails":
461 continue
462 value = getattr(self, key)
463 if callable(value):
464 continue
465 temp = temp + " " + key + " = " + str(value) + "\n"
467 for fetchtype in self.emails:
468 temp = temp + str(self.emails[fetchtype]) + "\n"
470 for game_type in self.layout_set:
471 temp = temp + " game_type = %s, layout_set = %s\n" % (game_type, self.layout_set[game_type])
473 for max in self.fav_seat:
474 temp = temp + " max = %s, fav_seat = %s\n" % (max, self.fav_seat[max])
476 return temp
479class Stat(object):
480 def __init__(self, node):
481 rowcol = node.getAttribute("_rowcol") # human string "(r,c)" values >0)
482 self.rows = node.getAttribute("rows")
483 self.cols = node.getAttribute("cols")
484 self.rowcol = tuple(int(s) - 1 for s in rowcol[1:-1].split(",")) # tuple (r-1,c-1)
485 self.stat_name = node.getAttribute("_stat_name")
486 self.tip = node.getAttribute("tip")
487 self.click = node.getAttribute("click")
488 self.popup = node.getAttribute("popup")
489 self.hudprefix = node.getAttribute("hudprefix")
490 self.hudsuffix = node.getAttribute("hudsuffix")
491 self.hudcolor = node.getAttribute("hudcolor")
492 self.stat_loth = node.getAttribute("stat_loth")
493 self.stat_hith = node.getAttribute("stat_hith")
494 self.stat_locolor = node.getAttribute("stat_locolor")
495 self.stat_hicolor = node.getAttribute("stat_hicolor")
496 self.stat_midcolor = node.getAttribute("stat_midcolor")
498 def __str__(self):
499 temp = " _rowcol = %s, _stat_name = %s, \n" % (self.rowcol, self.stat_name)
500 for key in dir(self):
501 if key.startswith("__"):
502 continue
503 if key == "_stat_name":
504 continue
505 if key == "_rowcol":
506 continue
507 value = getattr(self, key)
508 if callable(value):
509 continue
510 temp = temp + " " + key + " = " + str(value) + "\n"
512 return temp
515class Stat_sets(object):
516 """Representation of a HUD display configuration
517 - stats: Dict of Tuples (position in HUD) -> Configuration.Stat
518 Exemple: {
519 (0,0): Stat(stat_name = 'vpip', stat_hicolor ='#F44336', ...),
520 (0,1): Stat(stat_name = 'pfr', stat_hicolor ='#F44336', ...),
521 ...
522 }
523 - rows, cols: siez of the HUD
524 """
526 def __init__(self, node):
527 self.name = node.getAttribute("name")
528 self.rows = int(node.getAttribute("rows"))
529 self.cols = int(node.getAttribute("cols"))
530 self.xpad = node.getAttribute("xpad")
531 self.xpad = 0 if self.xpad == "" else int(self.xpad)
532 self.ypad = node.getAttribute("ypad")
533 self.ypad = 0 if self.ypad == "" else int(self.ypad)
534 self.stats = None #
536 self.stats = {}
537 for stat_node in node.getElementsByTagName("stat"):
538 stat = Stat(stat_node)
539 self.stats[stat.rowcol] = stat # this is the key!
541 def __str__(self):
542 temp = "Name = " + self.name + "\n"
543 temp = temp + " rows = %d" % self.rows
544 temp = temp + " cols = %d" % self.cols
545 temp = temp + " xpad = %d" % self.xpad
546 temp = temp + " ypad = %d\n" % self.ypad
548 for stat in list(self.stats.keys()):
549 temp = temp + "%s" % self.stats[stat]
551 return temp
554class Database(object):
555 def __init__(self, node):
556 self.db_name = node.getAttribute("db_name")
557 self.db_desc = node.getAttribute("db_desc")
558 self.db_server = node.getAttribute("db_server").lower()
559 self.db_ip = node.getAttribute("db_ip")
560 self.db_port = node.getAttribute("db_port")
561 self.db_user = node.getAttribute("db_user")
562 self.db_pass = node.getAttribute("db_pass")
563 self.db_path = node.getAttribute("db_path")
564 self.db_selected = string_to_bool(node.getAttribute("default"), default=False)
565 log.debug(
566 "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'"
567 % {
568 "name": self.db_name,
569 "server": self.db_server,
570 "ip": self.db_ip,
571 "port": self.db_port,
572 "user": self.db_user,
573 "sel": self.db_selected,
574 }
575 )
577 def __str__(self):
578 temp = "Database = " + self.db_name + "\n"
579 for key in dir(self):
580 if key.startswith("__"):
581 continue
582 value = getattr(self, key)
583 if callable(value):
584 continue
585 temp = temp + " " + key + " = " + repr(value) + "\n"
586 return temp
589class Aux_window(object):
590 def __init__(self, node):
591 for name, value in list(node.attributes.items()):
592 setattr(self, name, value)
594 def __str__(self):
595 temp = "Aux = " + self.name + "\n"
596 for key in dir(self):
597 if key.startswith("__"):
598 continue
599 value = getattr(self, key)
600 if callable(value):
601 continue
602 temp = temp + " " + key + " = " + value + "\n"
604 return temp
607class Supported_games(object):
608 def __init__(self, node):
609 for name, value in list(node.attributes.items()):
610 setattr(self, name, value)
612 self.game_stat_set = {}
613 for game_stat_set_node in node.getElementsByTagName("game_stat_set"):
614 gss = Game_stat_set(game_stat_set_node)
615 self.game_stat_set[gss.game_type] = gss
617 def __str__(self):
618 temp = "Supported_games = " + self.game_name + "\n"
619 for key in dir(self):
620 if key.startswith("__"):
621 continue
622 if key == "game_stat_set":
623 continue
624 if key == "game_name":
625 continue
626 value = getattr(self, key)
627 if callable(value):
628 continue
629 temp = temp + " " + key + " = " + value + "\n"
631 for gs in self.game_stat_set:
632 temp = temp + "%s" % str(self.game_stat_set[gs])
633 return temp
636class Layout_set(object):
637 def __init__(self, node):
638 for name, value in list(node.attributes.items()):
639 setattr(self, name, value)
641 self.layout = {}
642 for layout_node in node.getElementsByTagName("layout"):
643 lo = Layout(layout_node)
644 self.layout[lo.max] = lo
646 def __str__(self):
647 temp = "Layout set = " + self.name + "\n"
648 for key in dir(self):
649 if key.startswith("__"):
650 continue
651 if key == "layout":
652 continue
653 if key == "name":
654 continue
655 value = getattr(self, key)
656 if callable(value):
657 continue
658 temp = temp + " " + key + " = " + value + "\n"
660 for layout in self.layout:
661 temp = temp + "%s" % self.layout[layout]
662 return temp
665class Game_stat_set(object):
666 def __init__(self, node):
667 self.game_type = node.getAttribute("game_type")
668 self.stat_set = node.getAttribute("stat_set")
670 def __str__(self):
671 return " Game Type: '%s' Stat Set: '%s'\n" % (self.game_type, self.stat_set)
674class HHC(object):
675 def __init__(self, node):
676 self.site = node.getAttribute("site")
677 self.converter = node.getAttribute("converter")
678 self.summaryImporter = node.getAttribute("summaryImporter")
680 def __str__(self):
681 return "%s:\tconverter: '%s' summaryImporter: '%s'" % (self.site, self.converter, self.summaryImporter)
684class Popup(object):
685 def __init__(self, node):
686 self.name = node.getAttribute("pu_name")
687 self.pu_class = node.getAttribute("pu_class")
688 self.pu_stats = []
689 self.pu_stats_submenu = []
691 for stat_node in node.getElementsByTagName("pu_stat"):
692 self.pu_stats.append(stat_node.getAttribute("pu_stat_name"))
693 # if stat_node.getAttribute("pu_stat_submenu"):
694 self.pu_stats_submenu.append(
695 tuple((stat_node.getAttribute("pu_stat_name"), stat_node.getAttribute("pu_stat_submenu")))
696 )
698 def __str__(self):
699 temp = "Popup = " + self.name + " Class = " + self.pu_class + "\n"
700 for stat in self.pu_stats:
701 temp = temp + " " + stat
702 return temp + "\n"
705class Import(object):
706 def __init__(self, node):
707 self.node = node
708 self.interval = node.getAttribute("interval")
709 self.sessionTimeout = string_to_bool(node.getAttribute("sessionTimeout"), default=30)
710 self.ResultsDirectory = node.getAttribute("ResultsDirectory")
711 self.hhBulkPath = node.getAttribute("hhBulkPath")
712 self.saveActions = string_to_bool(node.getAttribute("saveActions"), default=False)
713 self.cacheSessions = string_to_bool(node.getAttribute("cacheSessions"), default=False)
714 self.publicDB = string_to_bool(node.getAttribute("publicDB"), default=False)
715 self.callFpdbHud = string_to_bool(node.getAttribute("callFpdbHud"), default=False)
716 self.fastStoreHudCache = string_to_bool(node.getAttribute("fastStoreHudCache"), default=False)
717 self.saveStarsHH = string_to_bool(node.getAttribute("saveStarsHH"), default=False)
718 if node.getAttribute("importFilters"):
719 self.importFilters = node.getAttribute("importFilters").split(",")
720 else:
721 self.importFilters = []
722 if node.getAttribute("timezone"):
723 self.timezone = node.getAttribute("timezone")
724 else:
725 self.timezone = "America/New_York"
727 def __str__(self):
728 return (
729 " interval = %s\n callFpdbHud = %s\n saveActions = %s\n cacheSessions = %s\n publicDB = %s\n sessionTimeout = %s\n fastStoreHudCache = %s\n ResultsDirectory = %s"
730 % (
731 self.interval,
732 self.callFpdbHud,
733 self.saveActions,
734 self.cacheSessions,
735 self.publicDB,
736 self.sessionTimeout,
737 self.fastStoreHudCache,
738 self.ResultsDirectory,
739 )
740 )
743class HudUI(object):
744 def __init__(self, node):
745 self.node = node
746 self.label = node.getAttribute("label")
747 if node.hasAttribute("card_ht"):
748 self.card_ht = node.getAttribute("card_ht")
749 if node.hasAttribute("card_wd"):
750 self.card_wd = node.getAttribute("card_wd")
751 if node.hasAttribute("deck_type"):
752 self.deck_type = node.getAttribute("deck_type")
753 if node.hasAttribute("card_back"):
754 self.card_back = node.getAttribute("card_back")
755 #
756 if node.hasAttribute("stat_range"):
757 self.stat_range = node.getAttribute("stat_range")
758 if node.hasAttribute("stat_days"):
759 self.hud_days = node.getAttribute("stat_days")
760 if node.hasAttribute("aggregation_level_multiplier"):
761 self.agg_bb_mult = node.getAttribute("aggregation_level_multiplier")
762 if node.hasAttribute("seats_style"):
763 self.seats_style = node.getAttribute("seats_style")
764 if node.hasAttribute("seats_cust_nums_low"):
765 self.seats_cust_nums_low = node.getAttribute("seats_cust_nums_low")
766 if node.hasAttribute("seats_cust_nums_high"):
767 self.seats_cust_nums_high = node.getAttribute("seats_cust_nums_high")
768 #
769 if node.hasAttribute("hero_stat_range"):
770 self.h_stat_range = node.getAttribute("hero_stat_range")
771 if node.hasAttribute("hero_stat_days"):
772 self.h_hud_days = node.getAttribute("hero_stat_days")
773 if node.hasAttribute("hero_aggregation_level_multiplier"):
774 self.h_agg_bb_mult = node.getAttribute("hero_aggregation_level_multiplier")
775 if node.hasAttribute("hero_seats_style"):
776 self.h_seats_style = node.getAttribute("hero_seats_style")
777 if node.hasAttribute("hero_seats_cust_nums_low"):
778 self.h_seats_cust_nums_low = node.getAttribute("hero_seats_cust_nums_low")
779 if node.hasAttribute("hero_seats_cust_nums_high"):
780 self.h_seats_cust_nums_high = node.getAttribute("hero_seats_cust_nums_high")
782 def __str__(self):
783 return " label = %s\n" % self.label
786class General(dict):
787 def __init__(self):
788 super(General, self).__init__()
790 def add_elements(self, node):
791 # day_start - number n where 0.0 <= n < 24.0 representing start of day for user
792 # e.g. user could set to 4.0 for day to start at 4am local time
793 # [ HH_bulk_path was here - now moved to import section ]
794 for name, value in list(node.attributes.items()):
795 log.debug("config.general: adding %s = %s" % (name, value))
796 self[name] = value
798 try:
799 self["version"] = int(self["version"])
800 except KeyError:
801 self["version"] = 0
802 self["ui_language"] = "system"
803 self["config_difficulty"] = "expert"
805 def get_defaults(self):
806 self["version"] = 0
807 self["ui_language"] = "system"
808 self["config_difficulty"] = "expert"
809 self["config_wrap_len"] = "-1"
810 self["day_start"] = "5"
812 def __str__(self):
813 s = ""
814 for k in self:
815 s = s + " %s = %s\n" % (k, self[k])
816 return s
819class GUICashStats(list):
820 """<gui_cash_stats>
821 <col col_name="game" col_title="Game" disp_all="True" disp_posn="True" field_format="%s" field_type="str" xalignment="0.0" />
822 ...
823 </gui_cash_stats>
824 """
826 def __init__(self):
827 super(GUICashStats, self).__init__()
829 def add_elements(self, node):
830 # is this needed?
831 for child in node.childNodes:
832 if child.nodeType == child.ELEMENT_NODE:
833 col_name, col_title, disp_all, disp_posn, field_format, field_type, xalignment = (
834 None,
835 None,
836 True,
837 True,
838 "%s",
839 "str",
840 0.0,
841 )
843 if child.hasAttribute("col_name"):
844 col_name = child.getAttribute("col_name")
845 if child.hasAttribute("col_title"):
846 col_title = child.getAttribute("col_title")
847 if child.hasAttribute("disp_all"):
848 disp_all = string_to_bool(child.getAttribute("disp_all"))
849 if child.hasAttribute("disp_posn"):
850 disp_posn = string_to_bool(child.getAttribute("disp_posn"))
851 if child.hasAttribute("field_format"):
852 field_format = child.getAttribute("field_format")
853 if child.hasAttribute("field_type"):
854 field_type = child.getAttribute("field_type")
855 try:
856 if child.hasAttribute("xalignment"):
857 xalignment = float(child.getAttribute("xalignment"))
858 except ValueError:
859 print(("bad number in xalignment was ignored"))
860 log.info(("bad number in xalignment was ignored"))
862 self.append([col_name, col_title, disp_all, disp_posn, field_format, field_type, xalignment])
864 def get_defaults(self):
865 """A list of defaults to be called, should there be no entry in config"""
866 # SQL column name, display title, display all, display positional, format, type, alignment
867 defaults = [
868 ["game", "Game", True, True, "%s", "str", 0.0],
869 ["hand", "Hand", False, False, "%s", "str", 0.0],
870 ["plposition", "Posn", False, False, "%s", "str", 1.0],
871 ["pname", "Name", False, False, "%s", "str", 0.0],
872 ["n", "Hds", True, True, "%1.0f", "str", 1.0],
873 ["avgseats", "Seats", False, False, "%3.1f", "str", 1.0],
874 ["vpip", "VPIP", True, True, "%3.1f", "str", 1.0],
875 ["pfr", "PFR", True, True, "%3.1f", "str", 1.0],
876 ["pf3", "PF3", True, True, "%3.1f", "str", 1.0],
877 ["aggfac", "AggFac", True, True, "%2.2f", "str", 1.0],
878 ["aggfrq", "AggFreq", True, True, "%3.1f", "str", 1.0],
879 ["conbet", "ContBet", True, True, "%3.1f", "str", 1.0],
880 ["rfi", "RFI", True, True, "%3.1f", "str", 1.0],
881 ["steals", "Steals", True, True, "%3.1f", "str", 1.0],
882 ["saw_f", "Saw_F", True, True, "%3.1f", "str", 1.0],
883 ["sawsd", "SawSD", True, True, "%3.1f", "str", 1.0],
884 ["wtsdwsf", "WtSDwsF", True, True, "%3.1f", "str", 1.0],
885 ["wmsd", "W$SD", True, True, "%3.1f", "str", 1.0],
886 ["flafq", "FlAFq", True, True, "%3.1f", "str", 1.0],
887 ["tuafq", "TuAFq", True, True, "%3.1f", "str", 1.0],
888 ["rvafq", "RvAFq", True, True, "%3.1f", "str", 1.0],
889 ["pofafq", "PoFAFq", False, False, "%3.1f", "str", 1.0],
890 ["net", "Net($)", True, True, "%6.2f", "cash", 1.0],
891 ["bbper100", "bb/100", True, True, "%4.2f", "str", 1.0],
892 ["rake", "Rake($)", True, True, "%6.2f", "cash", 1.0],
893 ["bb100xr", "bbxr/100", True, True, "%4.2f", "str", 1.0],
894 ["stddev", "Standard Deviation", True, True, "%5.2f", "str", 1.0],
895 ]
896 for col in defaults:
897 self.append(col)
900# def __str__(self):
901# s = ""
902# for l in self:
903# s = s + " %s = %s\n" % (k, self[k])
904# return(s)
905class GUITourStats(list):
906 """<gui_tour_stats>
907 <col col_name="game" col_title="Game" disp_all="True" disp_posn="True" field_format="%s" field_type="str" xalignment="0.0" />
908 ...
909 </gui_tour_stats>
910 """
912 def __init__(self):
913 super(GUITourStats, self).__init__()
915 def add_elements(self, node):
916 # is this needed?
917 for child in node.childNodes:
918 if child.nodeType == child.ELEMENT_NODE:
919 col_name, col_title, disp_all, disp_posn, field_format, field_type, xalignment = (
920 None,
921 None,
922 True,
923 True,
924 "%s",
925 "str",
926 0.0,
927 )
929 if child.hasAttribute("col_name"):
930 col_name = child.getAttribute("col_name")
931 if child.hasAttribute("col_title"):
932 col_title = child.getAttribute("col_title")
933 if child.hasAttribute("disp_all"):
934 disp_all = string_to_bool(child.getAttribute("disp_all"))
935 if child.hasAttribute("disp_posn"):
936 disp_posn = string_to_bool(child.getAttribute("disp_posn"))
937 if child.hasAttribute("field_format"):
938 field_format = child.getAttribute("field_format")
939 if child.hasAttribute("field_type"):
940 field_type = child.getAttribute("field_type")
941 try:
942 if child.hasAttribute("xalignment"):
943 xalignment = float(child.getAttribute("xalignment"))
944 except ValueError:
945 print(("bad number in xalignment was ignored"))
946 log.info(("bad number in xalignment was ignored"))
948 self.append([col_name, col_title, disp_all, disp_posn, field_format, field_type, xalignment])
950 def get_defaults(self):
951 """A list of defaults to be called, should there be no entry in config"""
952 # SQL column name, display title, display all, display positional, format, type, alignment
953 defaults = [
954 ["game", "Game", True, True, "%s", "str", 0.0],
955 ["hand", "Hand", False, False, "%s", "str", 0.0],
956 ]
957 for col in defaults:
958 self.append(col)
961class RawHands(object):
962 def __init__(self, node=None):
963 if node is None:
964 self.save = "error"
965 self.compression = "none"
966 # print ("missing config section raw_hands")
967 else:
968 save = node.getAttribute("save")
969 if save in ("none", "error", "all"):
970 self.save = save
971 else:
972 print(("Invalid config value for %s, defaulting to %s") % (self.raw_hands.save, '"error"'))
973 self.save = "error"
975 compression = node.getAttribute("compression")
976 if save in ("none", "gzip", "bzip2"):
977 self.compression = compression
978 else:
979 print(("Invalid config value for %s, defaulting to %s") % (self.raw_hands.compression, '"none"'))
980 self.compression = "none"
982 # end def __init__
984 def __str__(self):
985 return " save= %s, compression= %s\n" % (self.save, self.compression)
988# end class RawHands
991class RawTourneys(object):
992 def __init__(self, node=None):
993 if node is None:
994 self.save = "error"
995 self.compression = "none"
996 # print ("missing config section raw_tourneys")
997 else:
998 save = node.getAttribute("save")
999 if save in ("none", "error", "all"):
1000 self.save = save
1001 else:
1002 print(("Invalid config value for %s, defaulting to %s") % (self.raw_tourneys.save, '"error"'))
1003 self.save = "error"
1005 compression = node.getAttribute("compression")
1006 if save in ("none", "gzip", "bzip2"):
1007 self.compression = compression
1008 else:
1009 print(("Invalid config value for %s, defaulting to %s") % (self.raw_tourneys.compression, '"none"'))
1010 self.compression = "none"
1012 # end def __init__
1014 def __str__(self):
1015 return " save= %s, compression= %s\n" % (self.save, self.compression)
1018# end class RawTourneys
1021class Config(object):
1022 def __init__(self, file=None, dbname="", custom_log_dir="", lvl="INFO"):
1023 self.install_method = INSTALL_METHOD
1024 self.fpdb_root_path = FPDB_ROOT_PATH
1025 self.appdata_path = APPDATA_PATH
1026 self.config_path = CONFIG_PATH
1027 self.pyfpdb_path = PYFPDB_PATH
1028 self.graphics_path = GRAPHICS_PATH
1029 self.os_family = OS_FAMILY
1030 self.posix = POSIX
1031 self.python_version = PYTHON_VERSION
1033 if not os.path.exists(CONFIG_PATH):
1034 os.makedirs(CONFIG_PATH)
1036 if custom_log_dir and os.path.exists(custom_log_dir):
1037 self.dir_log = str(custom_log_dir, "utf8")
1038 else:
1039 if OS_FAMILY == "XP" or "Win7":
1040 self.dir_log = os.path.join(CONFIG_PATH, "log")
1041 self.dir_log = self.dir_log.replace("\\", "/")
1042 else:
1043 self.dir_log = os.path.join(CONFIG_PATH, "log")
1044 self.log_file = os.path.join(self.dir_log, "fpdb-log.txt")
1045 log = logging.getLogger("config")
1047 # "file" is a path to an xml file with the fpdb/HUD configuration
1048 # we check the existence of "file" and try to recover if it doesn't exist
1050 # self.default_config_path = self.get_default_config_path()
1051 self.example_copy = False
1052 if file is not None: # config file path passed in
1053 file = os.path.expanduser(file)
1054 if not os.path.exists(file):
1055 print(("Configuration file %s not found. Using defaults.") % (file))
1056 sys.stderr.write(("Configuration file %s not found. Using defaults.") % (file))
1057 file = None
1059 self.example_copy, example_file = True, None
1060 if file is None:
1061 (file, self.example_copy, example_file) = get_config("HUD_config.xml", True)
1063 self.file = file
1065 self.supported_sites = {}
1066 self.supported_games = {}
1067 self.supported_databases = {} # databaseName --> Database instance
1068 self.aux_windows = {}
1069 self.layout_sets = {}
1070 self.stat_sets = {}
1071 self.hhcs = {}
1072 self.popup_windows = {}
1073 self.db_selected = None # database the user would like to use
1074 self.general = General()
1075 self.emails = {}
1076 self.gui_cash_stats = GUICashStats()
1077 self.gui_tour_stats = GUITourStats()
1078 self.site_ids = {} # site ID list from the database
1079 self.doc = None # Root of XML tree
1081 added, n = 1, 0 # use n to prevent infinite loop if add_missing_elements() fails somehow
1082 while added > 0 and n < 2:
1083 n = n + 1
1084 log.info("Reading configuration file %s" % file)
1085 print(("\n" + ("Reading configuration file %s") + "\n") % file)
1086 try:
1087 doc = xml.dom.minidom.parse(file)
1088 self.doc = doc # Root of XML tree
1089 self.file_error = None
1091 except (OSError, IOError, xml.parsers.expat.ExpatError) as e:
1092 log.error(f"Error while processing XML: {traceback.format_exc()} Exception: {e}")
1094 if (not self.example_copy) and (example_file is not None):
1095 # reads example file and adds missing elements into current config
1096 added = self.add_missing_elements(doc, example_file)
1098 if doc.getElementsByTagName("general") == []:
1099 self.general.get_defaults()
1100 for gen_node in doc.getElementsByTagName("general"):
1101 self.general.add_elements(node=gen_node) # add/overwrite elements in self.general
1103 if int(self.general["version"]) == CONFIG_VERSION:
1104 self.wrongConfigVersion = False
1105 else:
1106 self.wrongConfigVersion = True
1108 if doc.getElementsByTagName("gui_cash_stats") == []:
1109 self.gui_cash_stats.get_defaults()
1110 for gcs_node in doc.getElementsByTagName("gui_cash_stats"):
1111 self.gui_cash_stats.add_elements(node=gcs_node) # add/overwrite elements in self.gui_cash_stats
1113 if doc.getElementsByTagName("gui_tour_stats") == []:
1114 self.gui_tour_stats.get_defaults()
1115 for gcs_node in doc.getElementsByTagName("gui_tour_stats"):
1116 self.gui_tour_stats.add_elements(node=gcs_node) # add/overwrite elements in self.gui_cash_stats
1118 # s_sites = doc.getElementsByTagName("supported_sites")
1119 for site_node in doc.getElementsByTagName("site"):
1120 site = Site(node=site_node)
1121 self.supported_sites[site.site_name] = site
1123 # s_games = doc.getElementsByTagName("supported_games")
1124 for supported_game_node in doc.getElementsByTagName("game"):
1125 supported_game = Supported_games(supported_game_node)
1126 self.supported_games[supported_game.game_name] = supported_game
1128 # parse databases defined by user in the <supported_databases> section
1129 # the user may select the actual database to use via commandline or by setting the selected="bool"
1130 # attribute of the tag. if no database is explicitely selected, we use the first one we come across
1131 # s_dbs = doc.getElementsByTagName("supported_databases")
1132 # TODO: do we want to take all <database> tags or all <database> tags contained in <supported_databases>
1133 # ..this may break stuff for some users. so leave it unchanged for now untill there is a decission
1134 for db_node in doc.getElementsByTagName("database"):
1135 db = Database(node=db_node)
1136 if db.db_name in self.supported_databases:
1137 raise ValueError("Database names must be unique")
1138 if self.db_selected is None or db.db_selected:
1139 self.db_selected = db.db_name
1140 db_node.setAttribute("default", "True")
1141 self.supported_databases[db.db_name] = db
1142 # TODO: if the user may passes '' (empty string) as database name via command line, his choice is ignored
1143 # ..when we parse the xml we allow for ''. there has to be a decission if to allow '' or not
1144 if dbname and dbname in self.supported_databases:
1145 self.db_selected = dbname
1146 # NOTE: fpdb can not handle the case when no database is defined in xml, so we throw an exception for now
1147 if self.db_selected is None:
1148 raise ValueError("There must be at least one database defined")
1150 # s_dbs = doc.getElementsByTagName("mucked_windows")
1151 for aw_node in doc.getElementsByTagName("aw"):
1152 aw = Aux_window(node=aw_node)
1153 self.aux_windows[aw.name] = aw
1155 for ls_node in doc.getElementsByTagName("ls"):
1156 ls = Layout_set(node=ls_node)
1157 self.layout_sets[ls.name] = ls
1159 for ss_node in doc.getElementsByTagName("ss"):
1160 ss = Stat_sets(node=ss_node)
1161 self.stat_sets[ss.name] = ss
1163 # s_dbs = doc.getElementsByTagName("mucked_windows")
1164 for hhc_node in doc.getElementsByTagName("hhc"):
1165 hhc = HHC(node=hhc_node)
1166 self.hhcs[hhc.site] = hhc
1168 # s_dbs = doc.getElementsByTagName("popup_windows")
1169 for pu_node in doc.getElementsByTagName("pu"):
1170 pu = Popup(node=pu_node)
1171 self.popup_windows[pu.name] = pu
1173 for imp_node in doc.getElementsByTagName("import"):
1174 imp = Import(node=imp_node)
1175 self.imp = imp
1177 for hui_node in doc.getElementsByTagName("hud_ui"):
1178 hui = HudUI(node=hui_node)
1179 self.ui = hui
1181 db = self.get_db_parameters()
1182 # Set the db path if it's defined in HUD_config.xml (sqlite only), otherwise place in config path.
1183 self.dir_database = db["db-path"] if db["db-path"] else os.path.join(CONFIG_PATH, "database")
1184 if db["db-password"] == "YOUR MYSQL PASSWORD":
1185 df_file = self.find_default_conf()
1186 if df_file is None: # this is bad
1187 pass
1188 else:
1189 df_parms = self.read_default_conf(df_file)
1190 self.set_db_parameters(
1191 db_name="fpdb",
1192 db_ip=df_parms["db-host"],
1193 db_user=df_parms["db-user"],
1194 db_pass=df_parms["db-password"],
1195 )
1196 self.save(file=os.path.join(CONFIG_PATH, "HUD_config.xml"))
1198 if doc.getElementsByTagName("raw_hands") == []:
1199 self.raw_hands = RawHands()
1200 for raw_hands_node in doc.getElementsByTagName("raw_hands"):
1201 self.raw_hands = RawHands(raw_hands_node)
1203 if doc.getElementsByTagName("raw_tourneys") == []:
1204 self.raw_tourneys = RawTourneys()
1205 for raw_tourneys_node in doc.getElementsByTagName("raw_tourneys"):
1206 self.raw_tourneys = RawTourneys(raw_tourneys_node)
1208 # print ""
1210 # end def __init__
1212 def add_missing_elements(self, doc, example_file):
1213 """Look through example config file and add any elements that are not in the config
1214 May need to add some 'enabled' attributes to turn things off - can't just delete a
1215 config section now because this will add it back in"""
1217 nodes_added = 0
1219 try:
1220 example_doc = xml.dom.minidom.parse(example_file)
1221 except (OSError, IOError, xml.parsers.expat.ExpatError) as e:
1222 log.error(f"Error parsing example configuration file {example_file}. See error log file. Exception: {e}")
1223 return nodes_added
1225 for cnode in doc.getElementsByTagName("FreePokerToolsConfig"):
1226 for example_cnode in example_doc.childNodes:
1227 if example_cnode.localName == "FreePokerToolsConfig":
1228 for e in example_cnode.childNodes:
1229 # print "nodetype", e.nodeType, "name", e.localName, "found", len(doc.getElementsByTagName(e.localName))
1230 if e.nodeType == e.ELEMENT_NODE and doc.getElementsByTagName(e.localName) == []:
1231 new = doc.importNode(e, True) # True means do deep copy
1232 t_node = self.doc.createTextNode(" ")
1233 cnode.appendChild(t_node)
1234 cnode.appendChild(new)
1235 t_node = self.doc.createTextNode("\r\n\r\n")
1236 cnode.appendChild(t_node)
1237 print("... adding missing config section: " + e.localName)
1238 nodes_added = nodes_added + 1
1240 if nodes_added > 0:
1241 print(("Added %d missing config sections" % nodes_added) + "\n")
1242 self.save()
1244 return nodes_added
1246 def find_default_conf(self):
1247 if CONFIG_PATH:
1248 config_file = os.path.join(CONFIG_PATH, "default.conf")
1249 else:
1250 config_file = False
1252 if config_file and os.path.exists(config_file):
1253 file = config_file
1254 else:
1255 file = None
1256 return file
1258 def get_doc(self):
1259 return self.doc
1261 def get_site_node(self, site):
1262 for site_node in self.doc.getElementsByTagName("site"):
1263 if site_node.getAttribute("site_name") == site:
1264 return site_node
1266 def getEmailNode(self, siteName, fetchType):
1267 siteNode = self.get_site_node(siteName)
1268 for emailNode in siteNode.getElementsByTagName("email"):
1269 if emailNode.getAttribute("fetchType") == fetchType:
1270 return emailNode
1271 break
1273 # end def getEmailNode
1275 def getStatSetNode(self, statsetName):
1276 """returns DOM game node for a given game"""
1277 for statsetNode in self.doc.getElementsByTagName("ss"):
1278 # print "getStatSetNode statsetNode:",statsetNode
1279 if statsetNode.getAttribute("name") == statsetName:
1280 return statsetNode
1282 def getGameNode(self, gameName):
1283 """returns DOM game node for a given game"""
1284 for gameNode in self.doc.getElementsByTagName("game"):
1285 # print "getGameNode gameNode:",gameNode
1286 if gameNode.getAttribute("game_name") == gameName:
1287 return gameNode
1289 # end def getGameNode
1291 def get_aux_node(self, aux):
1292 for aux_node in self.doc.getElementsByTagName("aw"):
1293 if aux_node.getAttribute("name") == aux:
1294 return aux_node
1296 def get_layout_set_node(self, ls):
1297 for layout_set_node in self.doc.getElementsByTagName("ls"):
1298 if layout_set_node.getAttribute("name") == ls:
1299 return layout_set_node
1301 def get_layout_node(self, ls, max):
1302 for layout_node in ls.getElementsByTagName("layout"):
1303 if layout_node.getAttribute("max") == str(max):
1304 return layout_node
1306 def get_stat_set_node(self, ss):
1307 for stat_set_node in self.doc.getElementsByTagName("ss"):
1308 if os.ST_NODEV.getAttribute("name") == ss:
1309 return stat_set_node
1311 def get_db_node(self, db_name):
1312 for db_node in self.doc.getElementsByTagName("database"):
1313 if db_node.getAttribute("db_name") == db_name:
1314 return db_node
1315 return None
1317 # def get_layout_node(self, site_node, layout):
1318 # for layout_node in site_node.getElementsByTagName("layout"):
1319 # if layout_node.getAttribute("max") is None:
1320 # return None
1321 # if int( layout_node.getAttribute("max") ) == int( layout ):
1322 # return layout_node
1324 def get_location_node(self, layout_node, seat):
1325 if seat == "common":
1326 for location_node in layout_node.getElementsByTagName("location"):
1327 if location_node.hasAttribute("common"):
1328 return location_node
1329 else:
1330 for location_node in layout_node.getElementsByTagName("location"):
1331 if int(location_node.getAttribute("seat")) == int(seat):
1332 return location_node
1334 def save(self, file=None):
1335 if file is None:
1336 file = self.file
1337 try:
1338 shutil.move(file, f"{file}.backup")
1339 except OSError as e:
1340 log.error(f"Failed to move file {file} to backup. Exception: {e}")
1342 with codecs.open(file, "w", "utf-8") as f:
1343 # self.doc.writexml(f)
1344 f.write(self.wrap_long_lines(self.doc.toxml()))
1346 def wrap_long_lines(self, s):
1347 lines = [self.wrap_long_line(line) for line in s.splitlines()]
1348 return "\n".join(lines) + "\n"
1350 def wrap_long_line(self, line):
1351 if "config_wrap_len" in self.general:
1352 wrap_len = int(self.general["config_wrap_len"])
1353 else:
1354 wrap_len = -1 # < 0 means no wrap
1356 if wrap_len >= 0 and len(line) > wrap_len:
1357 m = re.compile("\s+\S+\s+")
1358 mo = m.match(line)
1359 if mo:
1360 indent_len = mo.end()
1361 # print "indent = %s (%s)" % (indent_len, l[0:indent_len])
1362 indent = "\n" + " " * indent_len
1363 m = re.compile('(\S+="[^"]+"\s+)')
1364 parts = [x for x in m.split(line[indent_len:]) if x]
1365 if len(parts) > 1:
1366 # print "parts =", parts
1367 line = line[0:indent_len] + indent.join(parts)
1368 return line
1369 else:
1370 return line
1372 def editEmail(self, siteName, fetchType, newEmail):
1373 emailNode = self.getEmailNode(siteName, fetchType)
1374 emailNode.setAttribute("host", newEmail.host)
1375 emailNode.setAttribute("username", newEmail.username)
1376 emailNode.setAttribute("password", newEmail.password)
1377 emailNode.setAttribute("folder", newEmail.folder)
1378 emailNode.setAttribute("useSsl", newEmail.useSsl)
1380 # end def editEmail
1382 def edit_fav_seat(
1383 self,
1384 site_name,
1385 enabled,
1386 seat2_dict,
1387 seat3_dict,
1388 seat4_dict,
1389 seat5_dict,
1390 seat6_dict,
1391 seat7_dict,
1392 seat8_dict,
1393 seat9_dict,
1394 seat10_dict,
1395 ):
1396 site_node = self.get_site_node(site_name)
1397 site_node.setAttribute("enabled", enabled)
1399 for fav_seat in site_node.getElementsByTagName("fav"):
1400 if fav_seat.getAttribute("max") == "2":
1401 fav_seat.setAttribute("fav_seat", seat2_dict)
1402 elif fav_seat.getAttribute("max") == "3":
1403 fav_seat.setAttribute("fav_seat", seat3_dict)
1404 elif fav_seat.getAttribute("max") == "4":
1405 fav_seat.setAttribute("fav_seat", seat4_dict)
1406 elif fav_seat.getAttribute("max") == "5":
1407 fav_seat.setAttribute("fav_seat", seat5_dict)
1408 elif fav_seat.getAttribute("max") == "6":
1409 fav_seat.setAttribute("fav_seat", seat6_dict)
1410 elif fav_seat.getAttribute("max") == "7":
1411 fav_seat.setAttribute("fav_seat", seat7_dict)
1412 elif fav_seat.getAttribute("max") == "8":
1413 fav_seat.setAttribute("fav_seat", seat8_dict)
1414 elif fav_seat.getAttribute("max") == "9":
1415 fav_seat.setAttribute("fav_seat", seat9_dict)
1416 elif fav_seat.getAttribute("max") == "10":
1417 fav_seat.setAttribute("fav_seat", seat10_dict)
1419 # end def
1421 def increment_position(self, position: str) -> str:
1422 # Adapt defined logic for hus stats form config file
1423 # TODO: Probably adapt hud logic instead
1424 """
1425 >>> self.increment_position('(0,0)')
1426 "(1,1)"
1427 >>> self.increment_position('(0, 0)')
1428 "(1,1)"
1429 >>> self.increment_position('(2,3)')
1430 "(3,4)"
1431 """
1432 assert position.startswith("(") and position.endswith(")"), position.__repr__()
1433 # Remove parentheses and split by comma
1434 row, col = map(int, position[1:-1].split(","))
1435 # Check that row and collar are not negative
1436 assert row >= 0 and col >= 0, f"Negative values detected: row={row}, col={col}"
1437 # Increment both row and column by 1
1438 return f"({row + 1},{col + 1})"
1440 def edit_hud(
1441 self,
1442 hud_name,
1443 position,
1444 stat_name,
1445 click,
1446 hudcolor,
1447 hudprefix,
1448 hudsuffix,
1449 popup,
1450 stat_hicolor,
1451 stat_hith,
1452 stat_locolor,
1453 stat_loth,
1454 tip,
1455 ):
1456 """Replace given values onto self.doc (XML root node)"""
1457 for statsetNode in self.doc.getElementsByTagName("ss"):
1458 if statsetNode.getAttribute("name") == hud_name:
1459 for fav_stat in statsetNode.getElementsByTagName("stat"):
1460 if fav_stat.getAttribute("_rowcol") == self.increment_position(position):
1461 fav_stat.setAttribute("_stat_name", stat_name)
1462 fav_stat.setAttribute("click", click)
1463 fav_stat.setAttribute("hudcolor", hudcolor)
1464 fav_stat.setAttribute("hudprefix", hudprefix)
1465 fav_stat.setAttribute("hudsuffix", hudsuffix)
1466 fav_stat.setAttribute("popup", popup)
1467 fav_stat.setAttribute("stat_hicolor", stat_hicolor)
1468 fav_stat.setAttribute("stat_hith", stat_hith)
1469 fav_stat.setAttribute("stat_locolor", stat_locolor)
1470 fav_stat.setAttribute("stat_loth", stat_loth)
1471 fav_stat.setAttribute("tip", tip)
1472 # fav_stat.setAttribute("stat_midcolor", stat_midcolor) # not returned by UI
1474 # end def
1476 def edit_site(self, site_name, enabled, screen_name, history_path, summary_path):
1477 site_node = self.get_site_node(site_name)
1478 site_node.setAttribute("enabled", enabled)
1479 site_node.setAttribute("screen_name", screen_name)
1480 site_node.setAttribute("HH_path", history_path)
1481 if summary_path:
1482 site_node.setAttribute("TS_path", summary_path)
1484 def editStats(self, statsetName, statArray):
1485 """replaces stat selection for the given gameName with the given statArray"""
1486 statsetNode = self.getStatSetNode(statsetName)
1487 statNodes = statsetNode.getElementsByTagName("stat")
1489 for node in statNodes:
1490 statsetNode.removeChild(node)
1492 statsetNode.setAttribute("rows", str(len(statArray)))
1493 statsetNode.setAttribute("cols", str(len(statArray[0])))
1495 for rowNumber in range(len(statArray)):
1496 for columnNumber in range(len(statArray[rowNumber])):
1497 newStat = self.doc.createElement("stat")
1499 attributes = {
1500 "_stat_name": statArray[rowNumber][columnNumber],
1501 "_rowcol": f"({rowNumber+1},{columnNumber+1})",
1502 "click": "",
1503 "popup": "default",
1504 "tip": "",
1505 "stat_locolor": "",
1506 "stat_loth": "",
1507 "stat_midcolor": "", # Add stat_midcolor
1508 "stat_hicolor": "",
1509 "stat_hith": "",
1510 }
1512 for attr_name, attr_value in attributes.items():
1513 newAttr = self.doc.createAttribute(attr_name)
1514 newStat.setAttributeNode(newAttr)
1515 newStat.setAttribute(attr_name, attr_value)
1517 statsetNode.appendChild(newStat)
1519 statNodes = statsetNode.getElementsByTagName("stat") # TODO remove this line?
1521 # end def editStats
1522 def editImportFilters(self, games):
1523 self.imp.importFilters = games
1524 imp_node = self.doc.getElementsByTagName("import")[-1]
1525 imp_node.setAttribute("importFilters", games)
1527 def save_layout_set(self, ls, max, locations, width=None, height=None):
1528 # wid/height normally not specified when saving common from the mucked display
1530 print("saving layout =", ls.name, " ", str(max), "Max ", str(locations), "size:", str(width), "x", str(height))
1531 ls_node = self.get_layout_set_node(ls.name)
1532 layout_node = self.get_layout_node(ls_node, max)
1533 if width:
1534 layout_node.setAttribute("width", str(width))
1535 if height:
1536 layout_node.setAttribute("height", str(height))
1538 for i, pos in list(locations.items()):
1539 location_node = self.get_location_node(layout_node, i)
1540 location_node.setAttribute("x", str(locations[i][0]))
1541 location_node.setAttribute("y", str(locations[i][1]))
1542 # now refresh the live instance of the layout set with the new locations
1543 # this is needed because any future windows created after a save layout
1544 # MUST pickup the new layout
1545 # fixme - this is horrid
1546 if i == "common":
1547 self.layout_sets[ls.name].layout[max].common = (locations[i][0], locations[i][1])
1548 else:
1549 self.layout_sets[ls.name].layout[max].location[i] = (locations[i][0], locations[i][1])
1550 # more horridness below, fixme
1551 if height:
1552 self.layout_sets[ls.name].layout[max].height = height
1553 if width:
1554 self.layout_sets[ls.name].layout[max].width = width
1556 # NOTE: we got a nice Database class, so why map it again here?
1557 # user input validation should be done when initializing the Database class. this allows to give appropriate feddback when something goes wrong
1558 # try ..except is evil here. it swallows all kinds of errors. dont do this
1559 # 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_*)
1560 # 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
1561 # thus we can drop self.db_selected (holding database name) entirely and replace it with self._active_database = Database, avoiding to define the same
1562 # thing multiple times
1563 def get_db_parameters(self):
1564 db = {}
1565 name = self.db_selected
1567 if name not in self.supported_databases:
1568 log.error(f"Database {name} not found in supported databases.")
1569 return db
1571 # Parameters are retrieved with default values
1572 db["db-databaseName"] = name
1574 # use getattr
1575 db["db-desc"] = getattr(self.supported_databases[name], "db_desc", None)
1576 db["db-host"] = getattr(self.supported_databases[name], "db_ip", None)
1577 db["db-port"] = getattr(self.supported_databases[name], "db_port", None)
1578 db["db-user"] = getattr(self.supported_databases[name], "db_user", None)
1579 db["db-password"] = getattr(self.supported_databases[name], "db_pass", None)
1580 db["db-server"] = getattr(self.supported_databases[name], "db_server", None)
1581 db["db-path"] = getattr(self.supported_databases[name], "db_path", None)
1583 # add backend
1584 try:
1585 db["db-backend"] = self.get_backend(self.supported_databases[name].db_server)
1586 except (AttributeError, KeyError) as e:
1587 log.error(f"Error retrieving backend for {name}: {str(e)}")
1588 db["db-backend"] = None
1590 return db
1592 def set_db_parameters(
1593 self,
1594 db_name="fpdb",
1595 db_ip=None,
1596 db_port=None,
1597 db_user=None,
1598 db_pass=None,
1599 db_desc=None,
1600 db_server=None,
1601 default="False",
1602 ):
1603 db_node = self.get_db_node(db_name)
1604 default = default.lower()
1605 defaultb = string_to_bool(default, False)
1606 if db_node is not None:
1607 if db_desc is not None:
1608 db_node.setAttribute("db_desc", db_desc)
1609 if db_ip is not None:
1610 db_node.setAttribute("db_ip", db_ip)
1611 if db_port is not None:
1612 db_node.setAttribute("db_port", db_port)
1613 if db_user is not None:
1614 db_node.setAttribute("db_user", db_user)
1615 if db_pass is not None:
1616 db_node.setAttribute("db_pass", db_pass)
1617 if db_server is not None:
1618 db_node.setAttribute("db_server", db_server)
1619 if defaultb or self.db_selected == db_name:
1620 db_node.setAttribute("default", "True")
1621 for dbn in self.doc.getElementsByTagName("database"):
1622 if dbn.getAttribute("db_name") != db_name and dbn.hasAttribute("default"):
1623 dbn.removeAttribute("default")
1624 elif db_node.hasAttribute("default"):
1625 db_node.removeAttribute("default")
1626 if db_name in self.supported_databases:
1627 if db_desc is not None:
1628 self.supported_databases[db_name].dp_desc = db_desc
1629 if db_ip is not None:
1630 self.supported_databases[db_name].dp_ip = db_ip
1631 if db_port is not None:
1632 self.supported_databases[db_name].dp_port = db_port
1633 if db_user is not None:
1634 self.supported_databases[db_name].dp_user = db_user
1635 if db_pass is not None:
1636 self.supported_databases[db_name].dp_pass = db_pass
1637 if db_server is not None:
1638 self.supported_databases[db_name].dp_server = db_server
1639 self.supported_databases[db_name].db_selected = defaultb
1640 if defaultb:
1641 self.db_selected = db_name
1642 return
1644 def add_db_parameters(
1645 self,
1646 db_name="fpdb",
1647 db_ip=None,
1648 db_port=None,
1649 db_user=None,
1650 db_pass=None,
1651 db_desc=None,
1652 db_server=None,
1653 default="False",
1654 ):
1655 default = default.lower()
1656 defaultb = string_to_bool(default, False)
1657 if db_name in self.supported_databases:
1658 raise ValueError("Database names must be unique")
1660 db_node = self.get_db_node(db_name)
1661 if db_node is None:
1662 for db_node in self.doc.getElementsByTagName("supported_databases"):
1663 # should only be one supported_databases element, use last one if there are several
1664 suppdb_node = db_node
1665 t_node = self.doc.createTextNode(" ")
1666 suppdb_node.appendChild(t_node)
1667 db_node = self.doc.createElement("database")
1668 suppdb_node.appendChild(db_node)
1669 t_node = self.doc.createTextNode("\r\n ")
1670 suppdb_node.appendChild(t_node)
1671 db_node.setAttribute("db_name", db_name)
1672 if db_desc is not None:
1673 db_node.setAttribute("db_desc", db_desc)
1674 if db_ip is not None:
1675 db_node.setAttribute("db_ip", db_ip)
1676 if db_port is not None:
1677 db_node.setAttribute("db_port", db_port)
1678 if db_user is not None:
1679 db_node.setAttribute("db_user", db_user)
1680 if db_pass is not None:
1681 db_node.setAttribute("db_pass", db_pass)
1682 if db_server is not None:
1683 db_node.setAttribute("db_server", db_server)
1684 if defaultb:
1685 db_node.setAttribute("default", "True")
1686 for dbn in self.doc.getElementsByTagName("database"):
1687 if dbn.getAttribute("db_name") != db_name and dbn.hasAttribute("default"):
1688 dbn.removeAttribute("default")
1689 elif db_node.hasAttribute("default"):
1690 db_node.removeAttribute("default")
1691 else:
1692 if db_desc is not None:
1693 db_node.setAttribute("db_desc", db_desc)
1694 if db_ip is not None:
1695 db_node.setAttribute("db_ip", db_ip)
1696 if db_port is not None:
1697 db_node.setAttribute("db_port", db_port)
1698 if db_user is not None:
1699 db_node.setAttribute("db_user", db_user)
1700 if db_pass is not None:
1701 db_node.setAttribute("db_pass", db_pass)
1702 if db_server is not None:
1703 db_node.setAttribute("db_server", db_server)
1704 if defaultb or self.db_selected == db_name:
1705 db_node.setAttribute("default", "True")
1706 elif db_node.hasAttribute("default"):
1707 db_node.removeAttribute("default")
1709 if db_name in self.supported_databases:
1710 if db_desc is not None:
1711 self.supported_databases[db_name].dp_desc = db_desc
1712 if db_ip is not None:
1713 self.supported_databases[db_name].dp_ip = db_ip
1714 if db_port is not None:
1715 self.supported_databases[db_name].dp_port = db_port
1716 if db_user is not None:
1717 self.supported_databases[db_name].dp_user = db_user
1718 if db_pass is not None:
1719 self.supported_databases[db_name].dp_pass = db_pass
1720 if db_server is not None:
1721 self.supported_databases[db_name].dp_server = db_server
1722 self.supported_databases[db_name].db_selected = defaultb
1723 else:
1724 db = Database(node=db_node)
1725 self.supported_databases[db.db_name] = db
1727 if defaultb:
1728 self.db_selected = db_name
1729 return
1731 def get_backend(self, name):
1732 """Returns the number of the currently used backend"""
1734 # Mapper les chaînes de caractères reçues aux constantes attendues
1735 name_mapping = {
1736 "sqlite": "DATABASE_TYPE_SQLITE",
1737 "mysql": "DATABASE_TYPE_MYSQL",
1738 "postgresql": "DATABASE_TYPE_POSTGRESQL",
1739 }
1741 # Convertir le nom en majuscules en utilisant le mapping
1742 if name in name_mapping:
1743 name = name_mapping[name]
1744 else:
1745 raise ValueError(f"Unsupported database backend: {name}")
1747 # Utilisation des constantes attendues
1748 backends = {"DATABASE_TYPE_MYSQL": 2, "DATABASE_TYPE_POSTGRESQL": 3, "DATABASE_TYPE_SQLITE": 4}
1750 return backends[name]
1752 def getDefaultSite(self):
1753 "Returns first enabled site or None"
1754 for site_name, site in list(self.supported_sites.items()):
1755 if site.enabled:
1756 return site_name
1757 return None
1759 # Allow to change the menu appearance
1760 def get_hud_ui_parameters(self):
1761 hui = {}
1763 default_text = "FPDB Menu - Right click\nLeft-Drag to Move"
1765 try:
1766 hui["label"] = self.ui.label
1767 if self.ui.label == "": # Empty menu label is a big no-no
1768 hui["label"] = default_text
1769 except AttributeError as e:
1770 log.error(f"Error getting label: {e}")
1771 hui["label"] = default_text
1773 try:
1774 hui["card_ht"] = int(self.ui.card_ht)
1775 except (AttributeError, ValueError) as e:
1776 log.error(f"Error getting card height: {e}")
1777 hui["card_ht"] = 42
1779 try:
1780 hui["card_wd"] = int(self.ui.card_wd)
1781 except (AttributeError, ValueError) as e:
1782 log.error(f"Error getting card width: {e}")
1783 hui["card_wd"] = 30
1785 try:
1786 hui["deck_type"] = str(self.ui.deck_type)
1787 except AttributeError as e:
1788 log.error(f"Error getting deck type: {e}")
1789 hui["deck_type"] = "colour"
1791 try:
1792 hui["card_back"] = str(self.ui.card_back)
1793 except AttributeError as e:
1794 log.error(f"Error getting card back: {e}")
1795 hui["card_back"] = "back04"
1797 try:
1798 hui["stat_range"] = self.ui.stat_range
1799 except AttributeError as e:
1800 log.error(f"Error getting stat range: {e}")
1801 hui["stat_range"] = "A" # default is show stats for All-time, also S(session) and T(ime)
1803 try:
1804 hui["hud_days"] = int(self.ui.hud_days)
1805 except (AttributeError, ValueError) as e:
1806 log.error(f"Error getting HUD days: {e}")
1807 hui["hud_days"] = 90
1809 try:
1810 hui["agg_bb_mult"] = int(self.ui.agg_bb_mult)
1811 except (AttributeError, ValueError) as e:
1812 log.error(f"Error getting aggregate BB multiplier: {e}")
1813 hui["agg_bb_mult"] = 1
1815 try:
1816 hui["seats_style"] = self.ui.seats_style
1817 except AttributeError as e:
1818 log.error(f"Error getting seats style: {e}")
1819 hui["seats_style"] = "A" # A / C / E, use A(ll) / C(ustom) / E(xact) seat numbers
1821 try:
1822 hui["seats_cust_nums_low"] = int(self.ui.seats_cust_nums_low)
1823 except (AttributeError, ValueError) as e:
1824 log.error(f"Error getting custom seat numbers low: {e}")
1825 hui["seats_cust_nums_low"] = 1
1827 try:
1828 hui["seats_cust_nums_high"] = int(self.ui.seats_cust_nums_high)
1829 except (AttributeError, ValueError) as e:
1830 log.error(f"Error getting custom seat numbers high: {e}")
1831 hui["seats_cust_nums_high"] = 10
1833 # Hero specific
1834 try:
1835 hui["h_stat_range"] = self.ui.h_stat_range
1836 except AttributeError as e:
1837 log.error(f"Error getting hero stat range: {e}")
1838 hui["h_stat_range"] = "S"
1840 try:
1841 hui["h_hud_days"] = int(self.ui.h_hud_days)
1842 except (AttributeError, ValueError) as e:
1843 log.error(f"Error getting hero HUD days: {e}")
1844 hui["h_hud_days"] = 30
1846 try:
1847 hui["h_agg_bb_mult"] = int(self.ui.h_agg_bb_mult)
1848 except (AttributeError, ValueError) as e:
1849 log.error(f"Error getting hero aggregate BB multiplier: {e}")
1850 hui["h_agg_bb_mult"] = 1
1852 try:
1853 hui["h_seats_style"] = self.ui.h_seats_style
1854 except AttributeError as e:
1855 log.error(f"Error getting hero seats style: {e}")
1856 hui["h_seats_style"] = "A" # A / C / E, use A(ll) / C(ustom) / E(xact) seat numbers
1858 try:
1859 hui["h_seats_cust_nums_low"] = int(self.ui.h_seats_cust_nums_low)
1860 except (AttributeError, ValueError) as e:
1861 log.error(f"Error getting hero custom seat numbers low: {e}")
1862 hui["h_seats_cust_nums_low"] = 1
1864 try:
1865 hui["h_seats_cust_nums_high"] = int(self.ui.h_seats_cust_nums_high)
1866 except (AttributeError, ValueError) as e:
1867 log.error(f"Error getting hero custom seat numbers high: {e}")
1868 hui["h_seats_cust_nums_high"] = 10
1870 return hui
1872 def get_import_parameters(self):
1873 imp = {}
1875 try:
1876 imp["callFpdbHud"] = self.imp.callFpdbHud
1877 except AttributeError as e:
1878 log.error(f"Error getting 'callFpdbHud': {e}")
1879 imp["callFpdbHud"] = True
1881 try:
1882 imp["interval"] = self.imp.interval
1883 except AttributeError as e:
1884 log.error(f"Error getting 'interval': {e}")
1885 imp["interval"] = 10
1887 # Use if instead of try/except for ResultsDirectory
1888 if self.imp.ResultsDirectory != "":
1889 imp["ResultsDirectory"] = self.imp.ResultsDirectory
1890 else:
1891 imp["ResultsDirectory"] = "~/.fpdb/Results/"
1893 try:
1894 imp["hhBulkPath"] = self.imp.hhBulkPath
1895 except AttributeError as e:
1896 log.error(f"Error getting 'hhBulkPath': {e}")
1897 imp["hhBulkPath"] = ""
1899 try:
1900 imp["saveActions"] = self.imp.saveActions
1901 except AttributeError as e:
1902 log.error(f"Error getting 'saveActions': {e}")
1903 imp["saveActions"] = False
1905 try:
1906 imp["cacheSessions"] = self.imp.cacheSessions
1907 except AttributeError as e:
1908 log.error(f"Error getting 'cacheSessions': {e}")
1909 imp["cacheSessions"] = False
1911 try:
1912 imp["publicDB"] = self.imp.publicDB
1913 except AttributeError as e:
1914 log.error(f"Error getting 'publicDB': {e}")
1915 imp["publicDB"] = False
1917 try:
1918 imp["sessionTimeout"] = self.imp.sessionTimeout
1919 except AttributeError as e:
1920 log.error(f"Error getting 'sessionTimeout': {e}")
1921 imp["sessionTimeout"] = 30
1923 try:
1924 imp["saveStarsHH"] = self.imp.saveStarsHH
1925 except AttributeError as e:
1926 log.error(f"Error getting 'saveStarsHH': {e}")
1927 imp["saveStarsHH"] = False
1929 try:
1930 imp["fastStoreHudCache"] = self.imp.fastStoreHudCache
1931 except AttributeError as e:
1932 log.error(f"Error getting 'fastStoreHudCache': {e}")
1933 imp["fastStoreHudCache"] = False
1935 try:
1936 imp["importFilters"] = self.imp.importFilters
1937 except AttributeError as e:
1938 log.error(f"Error getting 'importFilters': {e}")
1939 imp["importFilters"] = []
1941 try:
1942 imp["timezone"] = self.imp.timezone
1943 except AttributeError as e:
1944 log.error(f"Error getting 'timezone': {e}")
1945 imp["timezone"] = "America/New_York"
1947 return imp
1949 def set_timezone(self, timezone):
1950 self.imp.timezone = timezone
1952 def get_default_paths(self, site=None):
1953 if site is None:
1954 site = self.getDefaultSite()
1955 paths = {}
1956 try:
1957 path = os.path.expanduser(self.supported_sites[site].HH_path)
1958 assert os.path.isdir(path) or os.path.isfile(path) # maybe it should try another site?
1959 paths["hud-defaultPath"] = paths["bulkImport-defaultPath"] = path
1960 if self.imp.hhBulkPath:
1961 paths["bulkImport-defaultPath"] = self.imp.hhBulkPath
1962 if self.supported_sites[site].TS_path != "":
1963 tspath = os.path.expanduser(self.supported_sites[site].TS_path)
1964 paths["hud-defaultTSPath"] = tspath
1965 except AssertionError:
1966 paths["hud-defaultPath"] = paths["bulkImport-defaultPath"] = (
1967 "** ERROR DEFAULT PATH IN CONFIG DOES NOT EXIST **"
1968 )
1969 return paths
1971 # def get_frames(self, site = "PokerStars"):
1972 # if site not in self.supported_sites: return False
1973 # return self.supported_sites[site].use_frames == True
1975 # def get_default_colors(self, site = "PokerStars"):
1976 # colors = {}
1977 # if site not in self.supported_sites or self.supported_sites[site].hudopacity == "":
1978 # colors['hudopacity'] = 0.90
1979 # else:
1980 # colors['hudopacity'] = float(self.supported_sites[site].hudopacity)
1981 # if site not in self.supported_sites or self.supported_sites[site].hudbgcolor == "":
1982 # colors['hudbgcolor'] = "#FFFFFF"
1983 # else:
1984 # colors['hudbgcolor'] = self.supported_sites[site].hudbgcolor
1985 # if site not in self.supported_sites or self.supported_sites[site].hudfgcolor == "":
1986 # colors['hudfgcolor'] = "#000000"
1987 # else:
1988 # colors['hudfgcolor'] = self.supported_sites[site].hudfgcolor
1989 # return colors
1991 # def get_default_font(self, site='PokerStars'):
1992 # font = "Sans"
1993 # font_size = "8"
1994 # site = self.supported_sites.get(site, None)
1995 # if site is not None:
1996 # if site.font:
1997 # font = site.font
1998 # if site.font_size:
1999 # font_size = site.font_size
2000 # return font, font_size
2002 def get_layout_set_locations(self, set="mucked", max="9"):
2003 try:
2004 locations = self.layout_sets[set].layout[max].location
2005 except (KeyError, AttributeError) as e:
2006 log.error(f"Error retrieving layout set locations for set='{set}', max='{max}': {e}")
2007 locations = (
2008 (0, 0),
2009 (684, 61),
2010 (689, 239),
2011 (692, 346),
2012 (586, 393),
2013 (421, 440),
2014 (267, 440),
2015 (0, 361),
2016 (0, 280),
2017 (121, 280),
2018 (46, 30),
2019 )
2020 return locations
2022 def get_supported_sites(self, all=False):
2023 """Returns the list of supported sites."""
2024 if all:
2025 return list(self.supported_sites.keys())
2026 else:
2027 return [site_name for (site_name, site) in list(self.supported_sites.items()) if site.enabled]
2029 def get_site_parameters(self, site):
2030 """Returns a dict of the site parameters for the specified site"""
2031 parms = {}
2032 parms["converter"] = self.hhcs[site].converter
2033 parms["summaryImporter"] = self.hhcs[site].summaryImporter
2034 parms["screen_name"] = self.supported_sites[site].screen_name
2035 parms["site_path"] = self.supported_sites[site].site_path
2036 parms["HH_path"] = self.supported_sites[site].HH_path
2037 parms["TS_path"] = self.supported_sites[site].TS_path
2038 parms["site_name"] = self.supported_sites[site].site_name
2039 parms["enabled"] = self.supported_sites[site].enabled
2040 parms["aux_enabled"] = self.supported_sites[site].aux_enabled
2041 parms["hud_menu_xshift"] = self.supported_sites[site].hud_menu_xshift
2042 parms["hud_menu_yshift"] = self.supported_sites[site].hud_menu_yshift
2043 parms["layout_set"] = self.supported_sites[site].layout_set
2044 parms["emails"] = self.supported_sites[site].emails
2045 parms["fav_seat"] = self.supported_sites[site].fav_seat
2047 return parms
2049 def get_layout(self, site, game_type):
2050 # find layouts used at site
2051 # locate the one used for this game_type
2052 # return that Layout-set() instance
2054 site_layouts = self.get_site_parameters(site)["layout_set"]
2056 if game_type in site_layouts:
2057 return self.layout_sets[site_layouts[game_type]]
2058 elif "all" in site_layouts:
2059 return self.layout_sets[site_layouts["all"]]
2060 else:
2061 return None
2063 # def set_site_parameters(self, site_name, converter = None, decoder = None,
2064 # hudbgcolor = None, hudfgcolor = None,
2065 # hudopacity = None, screen_name = None,
2066 # site_path = None, table_finder = None,
2067 # HH_path = None, enabled = None,
2068 # font = None, font_size = None):
2069 # """Sets the specified site parameters for the specified site."""
2070 # site_node = self.get_site_node(site_name)
2071 # if db_node is not None:
2072 # if converter is not None: site_node.setAttribute("converter", converter)
2073 # if decoder is not None: site_node.setAttribute("decoder", decoder)
2074 # if hudbgcolor is not None: site_node.setAttribute("hudbgcolor", hudbgcolor)
2075 # if hudfgcolor is not None: site_node.setAttribute("hudfgcolor", hudfgcolor)
2076 # if hudopacity is not None: site_node.setAttribute("hudopacity", hudopacity)
2077 # if screen_name is not None: site_node.setAttribute("screen_name", screen_name)
2078 # if site_path is not None: site_node.setAttribute("site_path", site_path)
2079 # if table_finder is not None: site_node.setAttribute("table_finder", table_finder)
2080 # if HH_path is not None: site_node.setAttribute("HH_path", HH_path)
2081 # if enabled is not None: site_node.setAttribute("enabled", enabled)
2082 # if font is not None: site_node.setAttribute("font", font)
2083 # if font_size is not None: site_node.setAttribute("font_size", font_size)
2084 # return
2086 def set_general(self, lang=None):
2087 for general_node in self.doc.getElementsByTagName("general"):
2088 if lang:
2089 general_node.setAttribute("ui_language", lang)
2091 def set_site_ids(self, sites):
2092 self.site_ids = dict(sites)
2094 def get_site_id(self, site):
2095 return self.site_ids[site]
2097 def get_aux_windows(self):
2098 """Gets the list of mucked window formats in the configuration."""
2099 return list(self.aux_windows.keys())
2101 def get_aux_parameters(self, name):
2102 """Gets a dict of mucked window parameters from the named mw."""
2103 param = {}
2104 if name in self.aux_windows:
2105 for key in dir(self.aux_windows[name]):
2106 if key.startswith("__"):
2107 continue
2108 value = getattr(self.aux_windows[name], key)
2109 if callable(value):
2110 continue
2111 param[key] = value
2113 return param
2114 return None
2116 def get_stat_sets(self):
2117 """Gets the list of stat block contents in the configuration."""
2118 return list(self.stat_sets.keys())
2120 def get_layout_sets(self):
2121 """Gets the list of block layouts in the configuration."""
2122 return list(self.layout_sets.keys())
2124 def get_layout_set_parameters(self, name):
2125 """Gets a dict of parameters from the named ls."""
2126 param = {}
2127 if name in self.layout_sets:
2128 for key in dir(self.layout_sets[name]):
2129 if key.startswith("__"):
2130 continue
2131 value = getattr(self.layout_sets[name], key)
2132 if callable(value):
2133 continue
2134 param[key] = value
2136 return param
2137 return None
2139 def get_supported_games(self):
2140 """Get the list of supported games."""
2141 sg = []
2142 for game in list(self.supported_games.keys()):
2143 sg.append(self.supported_games[game].game_name)
2144 return sg
2146 def get_supported_games_parameters(self, name, game_type):
2147 """Gets a dict of parameters from the named gametype."""
2148 param = {}
2149 if name in self.supported_games:
2150 for key in dir(self.supported_games[name]):
2151 if key.startswith("__"):
2152 continue
2153 if key == ("game_stat_set"):
2154 continue
2155 value = getattr(self.supported_games[name], key)
2156 if callable(value):
2157 continue
2158 param[key] = value
2160 # some gymnastics now to load the correct Stats_sets instance
2161 # into the game_stat_set key
2163 game_stat_set = getattr(self.supported_games[name], "game_stat_set")
2165 if game_type in game_stat_set:
2166 param["game_stat_set"] = self.stat_sets[game_stat_set[game_type].stat_set]
2167 elif "all" in game_stat_set:
2168 param["game_stat_set"] = self.stat_sets[game_stat_set["all"].stat_set]
2169 else:
2170 return None
2172 return param
2174 return None
2176 def execution_path(self, filename):
2177 """Join the fpdb path to filename."""
2178 return os.path.join(os.path.dirname(inspect.getfile(sys._getframe(0))), filename)
2180 def get_general_params(self):
2181 return self.general
2183 def get_gui_cash_stat_params(self):
2184 # print(type(self.gui_cash_stats))
2185 return self.gui_cash_stats
2187 def get_gui_tour_stat_params(self):
2188 # print(type(self.gui_tour_stats))
2189 return self.gui_tour_stats
2192if __name__ == "__main__":
2193 set_logfile("fpdb-log.txt")
2194 c = Config()
2196 print("\n----------- GENERAL -----------")
2197 print(c.general)
2199 print("\n----------- SUPPORTED SITES -----------")
2200 for s in list(c.supported_sites.keys()):
2201 print(c.supported_sites[s])
2203 print("\n----------- SUPPORTED GAMES -----------")
2204 for game in list(c.supported_games.keys()):
2205 print(c.supported_games[game])
2207 print("\n----------- SUPPORTED DATABASES -----------")
2208 for db in list(c.supported_databases.keys()):
2209 print(c.supported_databases[db])
2211 print("\n----------- AUX WINDOW FORMATS -----------")
2212 for w in list(c.aux_windows.keys()):
2213 print(c.aux_windows[w])
2215 print("\n----------- LAYOUT SETS FORMATS -----------")
2216 for w in list(c.layout_sets.keys()):
2217 print(c.layout_sets[w])
2219 print("\n----------- STAT SETS FORMATS -----------")
2220 for w in list(c.stat_sets.keys()):
2221 print(c.stat_sets[w])
2223 print("\n----------- HAND HISTORY CONVERTERS -----------")
2224 for w in list(c.hhcs.keys()):
2225 print(c.hhcs[w])
2227 print("\n----------- POPUP WINDOW FORMATS -----------")
2228 for w in list(c.popup_windows.keys()):
2229 print(c.popup_windows[w])
2231 print("\n----------- DATABASE PARAMS -----------")
2232 print("db = ", c.get_db_parameters())
2234 print("\n----------- HUD PARAMS -----------")
2235 print("hud params =")
2236 for hud_param, value in list(c.get_hud_ui_parameters().items()):
2237 print(" %s = %s" % (hud_param, value))
2239 print("\n----------- STARTUP PATH -----------")
2240 print("start up path = ", c.execution_path(""))
2242 print("\n----------- GUI CASH STATS -----------")
2243 print("gui_cash_stats =", c.gui_cash_stats)
2245 print("\n----------- Heroes -----------")
2246 for s in list(c.supported_sites.keys()):
2247 print(c.supported_sites[s].screen_name)
2249 print("\n----------- ENVIRONMENT CONSTANTS -----------")
2250 print("Configuration.install_method {source,exe,app} =", INSTALL_METHOD)
2251 print("Configuration.fpdb_root_path =", FPDB_ROOT_PATH, type(FPDB_ROOT_PATH))
2252 print("Configuration.graphics_path =", GRAPHICS_PATH, type(GRAPHICS_PATH))
2253 print("Configuration.appdata_path =", APPDATA_PATH, type(APPDATA_PATH))
2254 print("Configuration.config_path =", CONFIG_PATH, type(CONFIG_PATH))
2255 print("Configuration.pyfpdb_path =", PYFPDB_PATH, type(PYFPDB_PATH))
2256 print("Configuration.os_family {Linux,Mac,XP,Win7} =", OS_FAMILY)
2257 print("Configuration.posix {True/False} =", POSIX)
2258 print("Configuration.python_version =", PYTHON_VERSION)
2259 print("\n\n----------- END OF CONFIG REPORT -----------")
2261 print("press enter to end")
2262 sys.stdin.readline()