Coverage for Configuration.py: 47%

1438 statements  

« 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 

20 

21######################################################################## 

22 

23# TODO fix / rethink edit stats - it is broken badly just now 

24 

25# Standard Library modules 

26from __future__ import with_statement 

27from __future__ import print_function 

28 

29 

30# import L10n 

31# _ = L10n.get_translation() 

32 

33import codecs 

34import os 

35import sys 

36import inspect 

37import shutil 

38import locale 

39import re 

40import xml.dom.minidom 

41 

42# # import Charset 

43import platform 

44import traceback 

45 

46 

47if platform.system() == "Windows": 

48 # import winpaths 

49 # winpaths_appdata = winpaths.get_appdata() 

50 import os 

51 

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 

59 

60import logging, logging.config 

61 

62 

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 

68 

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) 

83 

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" 

95 

96if INSTALL_METHOD == "exe": 

97 FPDB_ROOT_PATH = os.path.dirname(sys.executable) 

98 

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() 

112 

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 

125 

126# GRAPHICS_PATH = os.path.join(FPDB_ROOT_PATH, "gfx") 

127# PYFPDB_PATH = os.path.join(FPDB_ROOT_PATH, "pyfpdb") 

128 

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 

159 

160if os.name == "posix": 

161 POSIX = True 

162else: 

163 POSIX = False 

164 

165PYTHON_VERSION = sys.version[:3] 

166 

167# logging has been set up in fpdb.py or HUD_main.py, use their settings: 

168log = logging.getLogger("config") 

169 

170LOGLEVEL = { 

171 "DEBUG": logging.DEBUG, 

172 "INFO": logging.INFO, 

173 "WARNING": logging.WARNING, 

174 "ERROR": logging.ERROR, 

175 "CRITICAL": logging.CRITICAL, 

176} 

177 

178 

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 """ 

185 

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 

212 

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 

239 

240 # OK, fall back to the .example file, should be in the start dir 

241 

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 ) 

253 

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() 

262 

263 return (config_path, example_copy, example_path) 

264 

265 

266def set_logfile(file_name): 

267 (conf_file, copied, example_file) = get_config("logging.conf", fallback=False) 

268 

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}") 

286 

287 

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 

303 

304 

305def normalizePath(path): 

306 "Normalized existing pathes" 

307 if os.path.exists(path): 

308 return os.path.abspath(path) 

309 return path 

310 

311 

312######################################################################## 

313# application wide consts 

314 

315APPLICATION_NAME_SHORT = "fpdb" 

316APPLICATION_VERSION = "xx.xx.xx" 

317 

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) 

326 

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."))) 

332 

333# needs LOCALE_ENCODING (above), imported for sqlite setup in Config class below 

334 

335 

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 

348 

349 

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")) 

355 

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 

364 

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"))) 

381 

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" 

397 

398 

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") 

408 

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 ) 

414 

415 

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 = "" 

433 

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 

439 

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 

445 

446 self.emails = {} 

447 for email_node in node.getElementsByTagName("email"): 

448 email = Email(email_node) 

449 self.emails[email.fetchType] = email 

450 

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" 

466 

467 for fetchtype in self.emails: 

468 temp = temp + str(self.emails[fetchtype]) + "\n" 

469 

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]) 

472 

473 for max in self.fav_seat: 

474 temp = temp + " max = %s, fav_seat = %s\n" % (max, self.fav_seat[max]) 

475 

476 return temp 

477 

478 

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") 

497 

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" 

511 

512 return temp 

513 

514 

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 """ 

525 

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 # 

535 

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! 

540 

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 

547 

548 for stat in list(self.stats.keys()): 

549 temp = temp + "%s" % self.stats[stat] 

550 

551 return temp 

552 

553 

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 ) 

576 

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 

587 

588 

589class Aux_window(object): 

590 def __init__(self, node): 

591 for name, value in list(node.attributes.items()): 

592 setattr(self, name, value) 

593 

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" 

603 

604 return temp 

605 

606 

607class Supported_games(object): 

608 def __init__(self, node): 

609 for name, value in list(node.attributes.items()): 

610 setattr(self, name, value) 

611 

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 

616 

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" 

630 

631 for gs in self.game_stat_set: 

632 temp = temp + "%s" % str(self.game_stat_set[gs]) 

633 return temp 

634 

635 

636class Layout_set(object): 

637 def __init__(self, node): 

638 for name, value in list(node.attributes.items()): 

639 setattr(self, name, value) 

640 

641 self.layout = {} 

642 for layout_node in node.getElementsByTagName("layout"): 

643 lo = Layout(layout_node) 

644 self.layout[lo.max] = lo 

645 

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" 

659 

660 for layout in self.layout: 

661 temp = temp + "%s" % self.layout[layout] 

662 return temp 

663 

664 

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") 

669 

670 def __str__(self): 

671 return " Game Type: '%s' Stat Set: '%s'\n" % (self.game_type, self.stat_set) 

672 

673 

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") 

679 

680 def __str__(self): 

681 return "%s:\tconverter: '%s' summaryImporter: '%s'" % (self.site, self.converter, self.summaryImporter) 

682 

683 

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 = [] 

690 

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 ) 

697 

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" 

703 

704 

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" 

726 

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 ) 

741 

742 

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") 

781 

782 def __str__(self): 

783 return " label = %s\n" % self.label 

784 

785 

786class General(dict): 

787 def __init__(self): 

788 super(General, self).__init__() 

789 

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 

797 

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" 

804 

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" 

811 

812 def __str__(self): 

813 s = "" 

814 for k in self: 

815 s = s + " %s = %s\n" % (k, self[k]) 

816 return s 

817 

818 

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 """ 

825 

826 def __init__(self): 

827 super(GUICashStats, self).__init__() 

828 

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 ) 

842 

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")) 

861 

862 self.append([col_name, col_title, disp_all, disp_posn, field_format, field_type, xalignment]) 

863 

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) 

898 

899 

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 """ 

911 

912 def __init__(self): 

913 super(GUITourStats, self).__init__() 

914 

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 ) 

928 

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")) 

947 

948 self.append([col_name, col_title, disp_all, disp_posn, field_format, field_type, xalignment]) 

949 

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) 

959 

960 

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" 

974 

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" 

981 

982 # end def __init__ 

983 

984 def __str__(self): 

985 return " save= %s, compression= %s\n" % (self.save, self.compression) 

986 

987 

988# end class RawHands 

989 

990 

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" 

1004 

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" 

1011 

1012 # end def __init__ 

1013 

1014 def __str__(self): 

1015 return " save= %s, compression= %s\n" % (self.save, self.compression) 

1016 

1017 

1018# end class RawTourneys 

1019 

1020 

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 

1032 

1033 if not os.path.exists(CONFIG_PATH): 

1034 os.makedirs(CONFIG_PATH) 

1035 

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") 

1046 

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 

1049 

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 

1058 

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) 

1062 

1063 self.file = file 

1064 

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 

1080 

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 

1090 

1091 except (OSError, IOError, xml.parsers.expat.ExpatError) as e: 

1092 log.error(f"Error while processing XML: {traceback.format_exc()} Exception: {e}") 

1093 

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) 

1097 

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 

1102 

1103 if int(self.general["version"]) == CONFIG_VERSION: 

1104 self.wrongConfigVersion = False 

1105 else: 

1106 self.wrongConfigVersion = True 

1107 

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 

1112 

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 

1117 

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 

1122 

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 

1127 

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") 

1149 

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 

1154 

1155 for ls_node in doc.getElementsByTagName("ls"): 

1156 ls = Layout_set(node=ls_node) 

1157 self.layout_sets[ls.name] = ls 

1158 

1159 for ss_node in doc.getElementsByTagName("ss"): 

1160 ss = Stat_sets(node=ss_node) 

1161 self.stat_sets[ss.name] = ss 

1162 

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 

1167 

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 

1172 

1173 for imp_node in doc.getElementsByTagName("import"): 

1174 imp = Import(node=imp_node) 

1175 self.imp = imp 

1176 

1177 for hui_node in doc.getElementsByTagName("hud_ui"): 

1178 hui = HudUI(node=hui_node) 

1179 self.ui = hui 

1180 

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")) 

1197 

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) 

1202 

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) 

1207 

1208 # print "" 

1209 

1210 # end def __init__ 

1211 

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""" 

1216 

1217 nodes_added = 0 

1218 

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 

1224 

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 

1239 

1240 if nodes_added > 0: 

1241 print(("Added %d missing config sections" % nodes_added) + "\n") 

1242 self.save() 

1243 

1244 return nodes_added 

1245 

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 

1251 

1252 if config_file and os.path.exists(config_file): 

1253 file = config_file 

1254 else: 

1255 file = None 

1256 return file 

1257 

1258 def get_doc(self): 

1259 return self.doc 

1260 

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 

1265 

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 

1272 

1273 # end def getEmailNode 

1274 

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 

1281 

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 

1288 

1289 # end def getGameNode 

1290 

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 

1295 

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 

1300 

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 

1305 

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 

1310 

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 

1316 

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 

1323 

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 

1333 

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}") 

1341 

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())) 

1345 

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" 

1349 

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 

1355 

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 

1371 

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) 

1379 

1380 # end def editEmail 

1381 

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) 

1398 

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) 

1418 

1419 # end def 

1420 

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})" 

1439 

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 

1473 

1474 # end def 

1475 

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) 

1483 

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") 

1488 

1489 for node in statNodes: 

1490 statsetNode.removeChild(node) 

1491 

1492 statsetNode.setAttribute("rows", str(len(statArray))) 

1493 statsetNode.setAttribute("cols", str(len(statArray[0]))) 

1494 

1495 for rowNumber in range(len(statArray)): 

1496 for columnNumber in range(len(statArray[rowNumber])): 

1497 newStat = self.doc.createElement("stat") 

1498 

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 } 

1511 

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) 

1516 

1517 statsetNode.appendChild(newStat) 

1518 

1519 statNodes = statsetNode.getElementsByTagName("stat") # TODO remove this line? 

1520 

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) 

1526 

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 

1529 

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)) 

1537 

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 

1555 

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 

1566 

1567 if name not in self.supported_databases: 

1568 log.error(f"Database {name} not found in supported databases.") 

1569 return db 

1570 

1571 # Parameters are retrieved with default values 

1572 db["db-databaseName"] = name 

1573 

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) 

1582 

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 

1589 

1590 return db 

1591 

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 

1643 

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") 

1659 

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") 

1708 

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 

1726 

1727 if defaultb: 

1728 self.db_selected = db_name 

1729 return 

1730 

1731 def get_backend(self, name): 

1732 """Returns the number of the currently used backend""" 

1733 

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 } 

1740 

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}") 

1746 

1747 # Utilisation des constantes attendues 

1748 backends = {"DATABASE_TYPE_MYSQL": 2, "DATABASE_TYPE_POSTGRESQL": 3, "DATABASE_TYPE_SQLITE": 4} 

1749 

1750 return backends[name] 

1751 

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 

1758 

1759 # Allow to change the menu appearance 

1760 def get_hud_ui_parameters(self): 

1761 hui = {} 

1762 

1763 default_text = "FPDB Menu - Right click\nLeft-Drag to Move" 

1764 

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 

1772 

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 

1778 

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 

1784 

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" 

1790 

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" 

1796 

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) 

1802 

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 

1808 

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 

1814 

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 

1820 

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 

1826 

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 

1832 

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" 

1839 

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 

1845 

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 

1851 

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 

1857 

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 

1863 

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 

1869 

1870 return hui 

1871 

1872 def get_import_parameters(self): 

1873 imp = {} 

1874 

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 

1880 

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 

1886 

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/" 

1892 

1893 try: 

1894 imp["hhBulkPath"] = self.imp.hhBulkPath 

1895 except AttributeError as e: 

1896 log.error(f"Error getting 'hhBulkPath': {e}") 

1897 imp["hhBulkPath"] = "" 

1898 

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 

1904 

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 

1910 

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 

1916 

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 

1922 

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 

1928 

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 

1934 

1935 try: 

1936 imp["importFilters"] = self.imp.importFilters 

1937 except AttributeError as e: 

1938 log.error(f"Error getting 'importFilters': {e}") 

1939 imp["importFilters"] = [] 

1940 

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" 

1946 

1947 return imp 

1948 

1949 def set_timezone(self, timezone): 

1950 self.imp.timezone = timezone 

1951 

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 

1970 

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 

1974 

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 

1990 

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 

2001 

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 

2021 

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] 

2028 

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 

2046 

2047 return parms 

2048 

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 

2053 

2054 site_layouts = self.get_site_parameters(site)["layout_set"] 

2055 

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 

2062 

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 

2085 

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) 

2090 

2091 def set_site_ids(self, sites): 

2092 self.site_ids = dict(sites) 

2093 

2094 def get_site_id(self, site): 

2095 return self.site_ids[site] 

2096 

2097 def get_aux_windows(self): 

2098 """Gets the list of mucked window formats in the configuration.""" 

2099 return list(self.aux_windows.keys()) 

2100 

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 

2112 

2113 return param 

2114 return None 

2115 

2116 def get_stat_sets(self): 

2117 """Gets the list of stat block contents in the configuration.""" 

2118 return list(self.stat_sets.keys()) 

2119 

2120 def get_layout_sets(self): 

2121 """Gets the list of block layouts in the configuration.""" 

2122 return list(self.layout_sets.keys()) 

2123 

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 

2135 

2136 return param 

2137 return None 

2138 

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 

2145 

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 

2159 

2160 # some gymnastics now to load the correct Stats_sets instance 

2161 # into the game_stat_set key 

2162 

2163 game_stat_set = getattr(self.supported_games[name], "game_stat_set") 

2164 

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 

2171 

2172 return param 

2173 

2174 return None 

2175 

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) 

2179 

2180 def get_general_params(self): 

2181 return self.general 

2182 

2183 def get_gui_cash_stat_params(self): 

2184 # print(type(self.gui_cash_stats)) 

2185 return self.gui_cash_stats 

2186 

2187 def get_gui_tour_stat_params(self): 

2188 # print(type(self.gui_tour_stats)) 

2189 return self.gui_tour_stats 

2190 

2191 

2192if __name__ == "__main__": 

2193 set_logfile("fpdb-log.txt") 

2194 c = Config() 

2195 

2196 print("\n----------- GENERAL -----------") 

2197 print(c.general) 

2198 

2199 print("\n----------- SUPPORTED SITES -----------") 

2200 for s in list(c.supported_sites.keys()): 

2201 print(c.supported_sites[s]) 

2202 

2203 print("\n----------- SUPPORTED GAMES -----------") 

2204 for game in list(c.supported_games.keys()): 

2205 print(c.supported_games[game]) 

2206 

2207 print("\n----------- SUPPORTED DATABASES -----------") 

2208 for db in list(c.supported_databases.keys()): 

2209 print(c.supported_databases[db]) 

2210 

2211 print("\n----------- AUX WINDOW FORMATS -----------") 

2212 for w in list(c.aux_windows.keys()): 

2213 print(c.aux_windows[w]) 

2214 

2215 print("\n----------- LAYOUT SETS FORMATS -----------") 

2216 for w in list(c.layout_sets.keys()): 

2217 print(c.layout_sets[w]) 

2218 

2219 print("\n----------- STAT SETS FORMATS -----------") 

2220 for w in list(c.stat_sets.keys()): 

2221 print(c.stat_sets[w]) 

2222 

2223 print("\n----------- HAND HISTORY CONVERTERS -----------") 

2224 for w in list(c.hhcs.keys()): 

2225 print(c.hhcs[w]) 

2226 

2227 print("\n----------- POPUP WINDOW FORMATS -----------") 

2228 for w in list(c.popup_windows.keys()): 

2229 print(c.popup_windows[w]) 

2230 

2231 print("\n----------- DATABASE PARAMS -----------") 

2232 print("db = ", c.get_db_parameters()) 

2233 

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)) 

2238 

2239 print("\n----------- STARTUP PATH -----------") 

2240 print("start up path = ", c.execution_path("")) 

2241 

2242 print("\n----------- GUI CASH STATS -----------") 

2243 print("gui_cash_stats =", c.gui_cash_stats) 

2244 

2245 print("\n----------- Heroes -----------") 

2246 for s in list(c.supported_sites.keys()): 

2247 print(c.supported_sites[s].screen_name) 

2248 

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 -----------") 

2260 

2261 print("press enter to end") 

2262 sys.stdin.readline()