From 8bf01a13e70435ceefd24fe4e7ec26f8b7e68c51 Mon Sep 17 00:00:00 2001 From: sqlcoder Date: Tue, 2 Dec 2008 00:00:44 +0000 Subject: [PATCH 01/35] Start of fpdb-sql repository --- readme.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 readme.txt diff --git a/readme.txt b/readme.txt new file mode 100644 index 00000000..e69de29b From a824814c0a34689f7ed36321f7217b5a66e61e81 Mon Sep 17 00:00:00 2001 From: sqlcoder Date: Tue, 2 Dec 2008 00:15:50 +0000 Subject: [PATCH 02/35] sqlcoder initial updates --- pyfpdb/CliFpdb.py | 0 pyfpdb/Configuration.py | 1219 +++++++++++++++++++----------------- pyfpdb/FpdbSQLQueries.py | 29 +- pyfpdb/GuiAutoImport.py | 427 +++++++------ pyfpdb/GuiGraphViewer.py | 18 +- pyfpdb/HUD_main.py | 337 +++++----- pyfpdb/Hud.py | 1140 ++++++++++++++++----------------- pyfpdb/SQL.py | 19 +- pyfpdb/Stats.py | 110 +++- pyfpdb/fpdb.py | 898 +++++++++++++------------- pyfpdb/fpdb_db.py | 0 pyfpdb/fpdb_import.py | 608 +++++++++--------- pyfpdb/fpdb_parse_logic.py | 10 +- pyfpdb/fpdb_save_to_db.py | 24 +- pyfpdb/fpdb_simple.py | 73 ++- 15 files changed, 2578 insertions(+), 2334 deletions(-) mode change 100755 => 100644 pyfpdb/CliFpdb.py mode change 100755 => 100644 pyfpdb/Configuration.py mode change 100755 => 100644 pyfpdb/HUD_main.py mode change 100755 => 100644 pyfpdb/fpdb.py mode change 100755 => 100644 pyfpdb/fpdb_db.py mode change 100755 => 100644 pyfpdb/fpdb_import.py diff --git a/pyfpdb/CliFpdb.py b/pyfpdb/CliFpdb.py old mode 100755 new mode 100644 diff --git a/pyfpdb/Configuration.py b/pyfpdb/Configuration.py old mode 100755 new mode 100644 index 5e599d4b..3b5c6d70 --- a/pyfpdb/Configuration.py +++ b/pyfpdb/Configuration.py @@ -1,578 +1,641 @@ -#!/usr/bin/env python -"""Configuration.py - -Handles HUD configuration files. -""" -# Copyright 2008, Ray E. Barker - -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -######################################################################## - -# Standard Library modules -import os -import sys -import string -import traceback -import shutil -import xml.dom.minidom -from xml.dom.minidom import Node - -class Layout: - def __init__(self, max): - self.max = int(max) - self.location = [] - for i in range(self.max + 1): self.location.append(None) - - def __str__(self): - temp = " Layout = %d max, width= %d, height = %d, fav_seat = %d\n" % (self.max, self.width, self.height, self.fav_seat) - temp = temp + " Locations = " - for i in range(1, len(self.location)): - temp = temp + "(%d,%d)" % self.location[i] - - return temp + "\n" - -class Site: - def __init__(self, node): - self.site_name = node.getAttribute("site_name") - self.table_finder = node.getAttribute("table_finder") - self.screen_name = node.getAttribute("screen_name") - self.site_path = node.getAttribute("site_path") - self.HH_path = node.getAttribute("HH_path") - self.decoder = node.getAttribute("decoder") - self.hudopacity = node.getAttribute("hudopacity") - self.hudbgcolor = node.getAttribute("bgcolor") - self.hudfgcolor = node.getAttribute("fgcolor") - self.converter = node.getAttribute("converter") - self.layout = {} - - for layout_node in node.getElementsByTagName('layout'): - max = int( layout_node.getAttribute('max') ) - lo = Layout(max) - lo.fav_seat = int( layout_node.getAttribute('fav_seat') ) - lo.width = int( layout_node.getAttribute('width') ) - lo.height = int( layout_node.getAttribute('height') ) - - for location_node in layout_node.getElementsByTagName('location'): - lo.location[int( location_node.getAttribute('seat') )] = (int( location_node.getAttribute('x') ), int( location_node.getAttribute('y'))) - - self.layout[lo.max] = lo - - def __str__(self): - temp = "Site = " + self.site_name + "\n" - for key in dir(self): - if key.startswith('__'): continue - if key == 'layout': continue - value = getattr(self, key) - if callable(value): continue - temp = temp + ' ' + key + " = " + str(value) + "\n" - - for layout in self.layout: - temp = temp + "%s" % self.layout[layout] - - return temp - -class Stat: - def __init__(self): - pass - - def __str__(self): - temp = " stat_name = %s, row = %d, col = %d, tip = %s, click = %s, popup = %s\n" % (self.stat_name, self.row, self.col, self.tip, self.click, self.popup) - return temp - -class Game: - def __init__(self, node): - self.game_name = node.getAttribute("game_name") - self.db = node.getAttribute("db") - self.rows = int( node.getAttribute("rows") ) - self.cols = int( node.getAttribute("cols") ) - - self.stats = {} - for stat_node in node.getElementsByTagName('stat'): - stat = Stat() - stat.stat_name = stat_node.getAttribute("stat_name") - stat.row = int( stat_node.getAttribute("row") ) - stat.col = int( stat_node.getAttribute("col") ) - stat.tip = stat_node.getAttribute("tip") - stat.click = stat_node.getAttribute("click") - stat.popup = stat_node.getAttribute("popup") - stat.hudprefix = stat_node.getAttribute("hudprefix") - stat.hudsuffix = stat_node.getAttribute("hudsuffix") - - self.stats[stat.stat_name] = stat - - def __str__(self): - temp = "Game = " + self.game_name + "\n" - temp = temp + " db = %s\n" % self.db - temp = temp + " rows = %d\n" % self.rows - temp = temp + " cols = %d\n" % self.cols - - for stat in self.stats.keys(): - temp = temp + "%s" % self.stats[stat] - - return temp - -class Database: - def __init__(self, node): - self.db_name = node.getAttribute("db_name") - self.db_server = node.getAttribute("db_server") - self.db_ip = node.getAttribute("db_ip") - self.db_user = node.getAttribute("db_user") - self.db_type = node.getAttribute("db_type") - self.db_pass = node.getAttribute("db_pass") - - def __str__(self): - temp = 'Database = ' + self.db_name + '\n' - for key in dir(self): - if key.startswith('__'): continue - value = getattr(self, key) - if callable(value): continue - temp = temp + ' ' + key + " = " + value + "\n" - return temp - -class Mucked: - def __init__(self, node): - self.name = node.getAttribute("mw_name") - self.cards = node.getAttribute("deck") - self.card_wd = node.getAttribute("card_wd") - self.card_ht = node.getAttribute("card_ht") - self.rows = node.getAttribute("rows") - self.cols = node.getAttribute("cols") - self.format = node.getAttribute("stud") - - def __str__(self): - temp = 'Mucked = ' + self.name + "\n" - for key in dir(self): - if key.startswith('__'): continue - value = getattr(self, key) - if callable(value): continue - temp = temp + ' ' + key + " = " + value + "\n" - return temp - -class Popup: - def __init__(self, node): - self.name = node.getAttribute("pu_name") - self.pu_stats = [] - for stat_node in node.getElementsByTagName('pu_stat'): - self.pu_stats.append(stat_node.getAttribute("pu_stat_name")) - - def __str__(self): - temp = "Popup = " + self.name + "\n" - for stat in self.pu_stats: - temp = temp + " " + stat - return temp + "\n" - -class Import: - def __init__(self, node): - self.interval = node.getAttribute("interval") - self.callFpdbHud = node.getAttribute("callFpdbHud") - - def __str__(self): - return " interval = %s\n callFpdbHud = %s\n" % (self.interval, self.callFpdbHud) - -class Tv: - def __init__(self, node): - self.combinedStealFold = node.getAttribute("combinedStealFold") - self.combined2B3B = node.getAttribute("combined2B3B") - self.combinedPostflop = node.getAttribute("combinedPostflop") - - def __str__(self): - return (" combinedStealFold = %s\n combined2B3B = %s\n combinedPostflop = %s\n" % - (self.combinedStealFold, self.combined2B3B, self.combinedPostflop) ) - -class Config: - def __init__(self, file = None): - -# "file" is a path to an xml file with the fpdb/HUD configuration -# we check the existence of "file" and try to recover if it doesn't exist - - self.default_config_path = self.get_default_config_path() - if not file == None: # configuration file path has been passed - if not os.path.exists(file): - print "Configuration file %s not found. Using defaults." % (file) - sys.stderr.write("Configuration file %s not found. Using defaults." % (file)) - file = None - - if file == None: # configuration file path not passed or invalid - file = self.find_config() #Look for a config file in the normal places - - if file == None: # no config file in the normal places - file = self.find_example_config() #Look for an example file to edit - if not file == None: - pass - - if file == None: # that didn't work either, just die - print "No HUD_config_xml found. Exiting" - sys.stderr.write("No HUD_config_xml found. Exiting") - sys.exit() - -# Parse even if there was no real config file found and we are using the example -# If using the example, we'll edit it later - try: - print "Reading configuration file %s\n" % (file) - doc = xml.dom.minidom.parse(file) - except: - print "Error parsing %s. See error log file." % (file) - traceback.print_exc(file=sys.stderr) - print "press enter to continue" - sys.stdin.readline() - sys.exit() - - self.doc = doc - self.file = file - self.supported_sites = {} - self.supported_games = {} - self.supported_databases = {} - self.mucked_windows = {} - self.popup_windows = {} - -# s_sites = doc.getElementsByTagName("supported_sites") - for site_node in doc.getElementsByTagName("site"): - site = Site(node = site_node) - self.supported_sites[site.site_name] = site - -# s_games = doc.getElementsByTagName("supported_games") - for game_node in doc.getElementsByTagName("game"): - game = Game(node = game_node) - self.supported_games[game.game_name] = game - -# s_dbs = doc.getElementsByTagName("supported_databases") - for db_node in doc.getElementsByTagName("database"): - db = Database(node = db_node) - self.supported_databases[db.db_name] = db - -# s_dbs = doc.getElementsByTagName("mucked_windows") - for mw_node in doc.getElementsByTagName("mw"): - mw = Mucked(node = mw_node) - self.mucked_windows[mw.name] = mw - -# s_dbs = doc.getElementsByTagName("popup_windows") - for pu_node in doc.getElementsByTagName("pu"): - pu = Popup(node = pu_node) - self.popup_windows[pu.name] = pu - - for imp_node in doc.getElementsByTagName("import"): - imp = Import(node = imp_node) - self.imp = imp - - for tv_node in doc.getElementsByTagName("tv"): - tv = Tv(node = tv_node) - self.tv = tv - - db = self.get_db_parameters('fpdb') - if db['db-password'] == 'YOUR MYSQL PASSWORD': - df_file = self.find_default_conf() - if df_file == None: # this is bad - pass - else: - df_parms = self.read_default_conf(df_file) - self.set_db_parameters(db_name = 'fpdb', db_ip = df_parms['db-host'], - db_user = df_parms['db-user'], - db_pass = df_parms['db-password']) - self.save(file=os.path.join(self.default_config_path, "HUD_config.xml")) - - - def find_config(self): - """Looks in cwd and in self.default_config_path for a config file.""" - if os.path.exists('HUD_config.xml'): # there is a HUD_config in the cwd - file = 'HUD_config.xml' # so we use it - else: # no HUD_config in the cwd, look where it should be in the first place - config_path = os.path.join(self.default_config_path, 'HUD_config.xml') - if os.path.exists(config_path): - file = config_path - else: - file = None - return file - - def get_default_config_path(self): - """Returns the path where the fpdb config file _should_ be stored.""" - if os.name == 'posix': - config_path = os.path.join(os.path.expanduser("~"), '.fpdb') - elif os.name == 'nt': - config_path = os.path.join(os.environ["APPDATA"], 'fpdb') - else: config_path = None - return config_path - - - def find_default_conf(self): - if os.name == 'posix': - config_path = os.path.join(os.path.expanduser("~"), '.fpdb', 'default.conf') - elif os.name == 'nt': - config_path = os.path.join(os.environ["APPDATA"], 'fpdb', 'default.conf') - else: config_path = False - - if config_path and os.path.exists(config_path): - file = config_path - else: - file = None - return file - - def read_default_conf(self, file): - parms = {} - fh = open(file, "r") - for line in fh: - line = string.strip(line) - (key, value) = line.split('=') - parms[key] = value - fh.close - return parms - - def find_example_config(self): - if os.path.exists('HUD_config.xml.example'): # there is a HUD_config in the cwd - file = 'HUD_config.xml.example' # so we use it - print "No HUD_config.xml found, using HUD_config.xml.example.\n", \ - "A HUD_config.xml will be written. You will probably have to edit it." - sys.stderr.write("No HUD_config.xml found, using HUD_config.xml.example.\n" + \ - "A HUD_config.xml will be written. You will probably have to edit it.") - else: - file = None - return file - - def get_site_node(self, site): - for site_node in self.doc.getElementsByTagName("site"): - if site_node.getAttribute("site_name") == site: - return site_node - - def get_db_node(self, db_name): - for db_node in self.doc.getElementsByTagName("database"): - if db_node.getAttribute("db_name") == db_name: - return db_node - return None - - def get_layout_node(self, site_node, layout): - for layout_node in site_node.getElementsByTagName("layout"): - if layout_node.getAttribute("max") == None: - return None - if int( layout_node.getAttribute("max") ) == int( layout ): - return layout_node - - def get_location_node(self, layout_node, seat): - for location_node in layout_node.getElementsByTagName("location"): - if int( location_node.getAttribute("seat") ) == int( seat ): - return location_node - - def save(self, file = None): - if not file == None: - f = open(file, 'w') - self.doc.writexml(f) - f.close() - else: - shutil.move(self.file, self.file+".backup") - f = open(self.file, 'w') - self.doc.writexml(f) - f.close - - def edit_layout(self, site_name, max, width = None, height = None, - fav_seat = None, locations = None): - print "max = ", max - site_node = self.get_site_node(site_name) - layout_node = self.get_layout_node(site_node, max) - if layout_node == None: return - for i in range(1, max + 1): - location_node = self.get_location_node(layout_node, i) - location_node.setAttribute("x", str( locations[i-1][0] )) - location_node.setAttribute("y", str( locations[i-1][1] )) - self.supported_sites[site_name].layout[max].location[i] = ( locations[i-1][0], locations[i-1][1] ) - - def get_db_parameters(self, name = None): - if name == None: name = 'fpdb' - db = {} - try: - db['db-databaseName'] = name - db['db-host'] = self.supported_databases[name].db_ip - db['db-user'] = self.supported_databases[name].db_user - db['db-password'] = self.supported_databases[name].db_pass - db['db-server'] = self.supported_databases[name].db_server - if string.lower(self.supported_databases[name].db_server) == 'mysql': - db['db-backend'] = 2 - elif string.lower(self.supported_databases[name].db_server) == 'postgresql': - db['db-backend'] = 3 - else: db['db-backend'] = None # this is big trouble - except: - pass - return db - - def set_db_parameters(self, db_name = 'fpdb', db_ip = None, db_user = None, - db_pass = None, db_server = None, db_type = None): - db_node = self.get_db_node(db_name) - if not db_node == None: - if not db_ip == None: db_node.setAttribute("db_ip", db_ip) - if not db_user == None: db_node.setAttribute("db_user", db_user) - if not db_pass == None: db_node.setAttribute("db_pass", db_pass) - if not db_server == None: db_node.setAttribute("db_server", db_server) - if not db_type == None: db_node.setAttribute("db_type", db_type) - if self.supported_databases.has_key(db_name): - if not db_ip == None: self.supported_databases[db_name].dp_ip = db_ip - if not db_user == None: self.supported_databases[db_name].dp_user = db_user - if not db_pass == None: self.supported_databases[db_name].dp_pass = db_pass - if not db_server == None: self.supported_databases[db_name].dp_server = db_server - if not db_type == None: self.supported_databases[db_name].dp_type = db_type - return - - def get_tv_parameters(self): - tv = {} - try: - tv['combinedStealFold'] = self.tv.combinedStealFold - tv['combined2B3B'] = self.tv.combined2B3B - tv['combinedPostflop'] = self.tv.combinedPostflop - except: # Default tv parameters - tv['combinedStealFold'] = True - tv['combined2B3B'] = True - tv['combinedPostflop'] = True - return tv - - def get_import_parameters(self): - imp = {} - try: - imp['imp-callFpdbHud'] = self.imp.callFpdbHud - imp['hud-defaultInterval'] = int(self.imp.interval) - except: # Default import parameters - imp['imp-callFpdbHud'] = True - imp['hud-defaultInterval'] = 10 - return imp - - def get_default_paths(self, site = "PokerStars"): - paths = {} - try: - paths['hud-defaultPath'] = os.path.expanduser(self.supported_sites[site].HH_path) - paths['bulkImport-defaultPath'] = os.path.expanduser(self.supported_sites[site].HH_path) - except: - paths['hud-defaultPath'] = "default" - paths['bulkImport-defaultPath'] = "default" - return paths - - def get_default_colors(self, site = "PokerStars"): - colors = {} - if self.supported_sites[site].hudopacity == "": - colors['hudopacity'] = 0.90 - else: - colors['hudopacity'] = float(self.supported_sites[site].hudopacity) - if self.supported_sites[site].hudbgcolor == "": - colors['hudbgcolor'] = "#FFFFFF" - else: - colors['hudbgcolor'] = self.supported_sites[site].hudbgcolor - if self.supported_sites[site].hudfgcolor == "": - colors['hudfgcolor'] = "#000000" - else: - colors['hudfgcolor'] = self.supported_sites[site].hudfgcolor - return colors - - def get_locations(self, site = "PokerStars", max = "8"): - - try: - locations = self.supported_sites[site].layout[max].location - except: - locations = ( ( 0, 0), (684, 61), (689, 239), (692, 346), - (586, 393), (421, 440), (267, 440), ( 0, 361), - ( 0, 280), (121, 280), ( 46, 30) ) - return locations - - def get_site_parameters(self, site): - """Returns a dict of the site parameters for the specified site""" - if not self.supported_sites.has_key(site): - return None - parms = {} - parms["converter"] = self.supported_sites[site].converter - parms["decoder"] = self.supported_sites[site].decoder - parms["hudbgcolor"] = self.supported_sites[site].hudbgcolor - parms["hudfgcolor"] = self.supported_sites[site].hudfgcolor - parms["hudopacity"] = self.supported_sites[site].hudopacity - parms["screen_name"] = self.supported_sites[site].screen_name - parms["site_path"] = self.supported_sites[site].site_path - parms["table_finder"] = self.supported_sites[site].table_finder - parms["HH_path"] = self.supported_sites[site].HH_path - return parms - - def set_site_parameters(self, site_name, converter = None, decoder = None, - hudbgcolor = None, hudfgcolor = None, - hudopacity = None, screen_name = None, - site_path = None, table_finder = None, - HH_path = None): - """Sets the specified site parameters for the specified site.""" - site_node = self.get_site_node(site_name) - if not db_node == None: - if not converter == None: site_node.setAttribute("converter", converter) - if not decoder == None: site_node.setAttribute("decoder", decoder) - if not hudbgcolor == None: site_node.setAttribute("hudbgcolor", hudbgcolor) - if not hudfgcolor == None: site_node.setAttribute("hudfgcolor", hudfgcolor) - if not hudopacity == None: site_node.setAttribute("hudopacity", hudopacity) - if not screen_name == None: site_node.setAttribute("screen_name", screen_name) - if not site_path == None: site_node.setAttribute("site_path", site_path) - if not table_finder == None: site_node.setAttribute("table_finder", table_finder) - if not HH_path == None: site_node.setAttribute("HH_path", HH_path) - - if self.supported_databases.has_key(db_name): - if not converter == None: self.supported_sites[site].converter = converter - if not decoder == None: self.supported_sites[site].decoder = decoder - if not hudbgcolor == None: self.supported_sites[site].hudbgcolor = hudbgcolor - if not hudfgcolor == None: self.supported_sites[site].hudfgcolor = hudfgcolor - if not hudopacity == None: self.supported_sites[site].hudopacity = hudopacity - if not screen_name == None: self.supported_sites[site].screen_name = screen_name - if not site_path == None: self.supported_sites[site].site_path = site_path - if not table_finder == None: self.supported_sites[site].table_finder = table_finder - if not HH_path == None: self.supported_sites[site].HH_path = HH_path - return - -if __name__== "__main__": - c = Config() - - print "\n----------- SUPPORTED SITES -----------" - for s in c.supported_sites.keys(): - print c.supported_sites[s] - print "----------- END SUPPORTED SITES -----------" - - - print "\n----------- SUPPORTED GAMES -----------" - for game in c.supported_games.keys(): - print c.supported_games[game] - print "----------- END SUPPORTED GAMES -----------" - - - print "\n----------- SUPPORTED DATABASES -----------" - for db in c.supported_databases.keys(): - print c.supported_databases[db] - print "----------- END SUPPORTED DATABASES -----------" - - print "\n----------- MUCKED WINDOW FORMATS -----------" - for w in c.mucked_windows.keys(): - print c.mucked_windows[w] - print "----------- END MUCKED WINDOW FORMATS -----------" - - print "\n----------- POPUP WINDOW FORMATS -----------" - for w in c.popup_windows.keys(): - print c.popup_windows[w] - print "----------- END MUCKED WINDOW FORMATS -----------" - - print "\n----------- IMPORT -----------" -# print c.imp - print "----------- END IMPORT -----------" - - print "\n----------- TABLE VIEW -----------" -# print c.tv - print "----------- END TABLE VIEW -----------" - - c.edit_layout("PokerStars", 6, locations=( (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6) )) - c.save(file="testout.xml") - - print "db = ", c.get_db_parameters() -# print "tv = ", c.get_tv_parameters() -# print "imp = ", c.get_import_parameters() - print "paths = ", c.get_default_paths("PokerStars") - print "colors = ", c.get_default_colors("PokerStars") - print "locs = ", c.get_locations("PokerStars", 8) - for site in c.supported_sites.keys(): - print "site = ", site, - print c.get_site_parameters(site) \ No newline at end of file +#!/usr/bin/env python +"""Configuration.py + +Handles HUD configuration files. +""" +# Copyright 2008, Ray E. Barker + +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +######################################################################## + +# Standard Library modules +import os +import sys +import string +import traceback +import shutil +import xml.dom.minidom +from xml.dom.minidom import Node + +class Layout: + def __init__(self, max): + self.max = int(max) + self.location = [] + for i in range(self.max + 1): self.location.append(None) + + def __str__(self): + temp = " Layout = %d max, width= %d, height = %d, fav_seat = %d\n" % (self.max, self.width, self.height, self.fav_seat) + temp = temp + " Locations = " + for i in range(1, len(self.location)): + temp = temp + "(%d,%d)" % self.location[i] + + return temp + "\n" + +class Site: + def __init__(self, node): + self.site_name = node.getAttribute("site_name") + self.table_finder = node.getAttribute("table_finder") + self.screen_name = node.getAttribute("screen_name") + self.site_path = node.getAttribute("site_path") + self.HH_path = node.getAttribute("HH_path") + self.decoder = node.getAttribute("decoder") + self.hudopacity = node.getAttribute("hudopacity") + self.hudbgcolor = node.getAttribute("bgcolor") + self.hudfgcolor = node.getAttribute("fgcolor") + self.converter = node.getAttribute("converter") + self.enabled = node.getAttribute("enabled") + self.aux_window = node.getAttribute("aux_window") + self.layout = {} + + for layout_node in node.getElementsByTagName('layout'): + max = int( layout_node.getAttribute('max') ) + lo = Layout(max) + lo.fav_seat = int( layout_node.getAttribute('fav_seat') ) + lo.width = int( layout_node.getAttribute('width') ) + lo.height = int( layout_node.getAttribute('height') ) + + for location_node in layout_node.getElementsByTagName('location'): + lo.location[int( location_node.getAttribute('seat') )] = (int( location_node.getAttribute('x') ), int( location_node.getAttribute('y'))) + + self.layout[lo.max] = lo + + def __str__(self): + temp = "Site = " + self.site_name + "\n" + for key in dir(self): + if key.startswith('__'): continue + if key == 'layout': continue + value = getattr(self, key) + if callable(value): continue + temp = temp + ' ' + key + " = " + str(value) + "\n" + + for layout in self.layout: + temp = temp + "%s" % self.layout[layout] + + return temp + +class Stat: + def __init__(self): + pass + + def __str__(self): + temp = " stat_name = %s, row = %d, col = %d, tip = %s, click = %s, popup = %s\n" % (self.stat_name, self.row, self.col, self.tip, self.click, self.popup) + return temp + +class Game: + def __init__(self, node): + self.game_name = node.getAttribute("game_name") + self.db = node.getAttribute("db") + self.rows = int( node.getAttribute("rows") ) + self.cols = int( node.getAttribute("cols") ) + self.aux = node.getAttribute("aux") + + self.stats = {} + for stat_node in node.getElementsByTagName('stat'): + stat = Stat() + stat.stat_name = stat_node.getAttribute("stat_name") + stat.row = int( stat_node.getAttribute("row") ) + stat.col = int( stat_node.getAttribute("col") ) + stat.tip = stat_node.getAttribute("tip") + stat.click = stat_node.getAttribute("click") + stat.popup = stat_node.getAttribute("popup") + stat.hudprefix = stat_node.getAttribute("hudprefix") + stat.hudsuffix = stat_node.getAttribute("hudsuffix") + + self.stats[stat.stat_name] = stat + + def __str__(self): + temp = "Game = " + self.game_name + "\n" + temp = temp + " db = %s\n" % self.db + temp = temp + " rows = %d\n" % self.rows + temp = temp + " cols = %d\n" % self.cols + temp = temp + " aux = %s\n" % self.aux + + for stat in self.stats.keys(): + temp = temp + "%s" % self.stats[stat] + + return temp + +class Database: + def __init__(self, node): + self.db_name = node.getAttribute("db_name") + self.db_server = node.getAttribute("db_server") + self.db_ip = node.getAttribute("db_ip") + self.db_user = node.getAttribute("db_user") + self.db_type = node.getAttribute("db_type") + self.db_pass = node.getAttribute("db_pass") + + def __str__(self): + temp = 'Database = ' + self.db_name + '\n' + for key in dir(self): + if key.startswith('__'): continue + value = getattr(self, key) + if callable(value): continue + temp = temp + ' ' + key + " = " + value + "\n" + return temp + +class Aux_window: + def __init__(self, node): + for (name, value) in node.attributes.items(): + setattr(self, name, value) +# self.name = node.getAttribute("mw_name") +# self.cards = node.getAttribute("deck") +# self.card_wd = node.getAttribute("card_wd") +# self.card_ht = node.getAttribute("card_ht") +# self.rows = node.getAttribute("rows") +# self.cols = node.getAttribute("cols") +# self.format = node.getAttribute("stud") + + def __str__(self): + temp = 'Aux = ' + self.name + "\n" + for key in dir(self): + if key.startswith('__'): continue + value = getattr(self, key) + if callable(value): continue + temp = temp + ' ' + key + " = " + value + "\n" + return temp + +class Popup: + def __init__(self, node): + self.name = node.getAttribute("pu_name") + self.pu_stats = [] + for stat_node in node.getElementsByTagName('pu_stat'): + self.pu_stats.append(stat_node.getAttribute("pu_stat_name")) + + def __str__(self): + temp = "Popup = " + self.name + "\n" + for stat in self.pu_stats: + temp = temp + " " + stat + return temp + "\n" + +class Import: + def __init__(self, node): + self.interval = node.getAttribute("interval") + self.callFpdbHud = node.getAttribute("callFpdbHud") + self.hhArchiveBase = node.getAttribute("hhArchiveBase") + + def __str__(self): + return " interval = %s\n callFpdbHud = %s\n hhArchiveBase = %s" % (self.interval, self.callFpdbHud, self.hhArchiveBase) + +class Tv: + def __init__(self, node): + self.combinedStealFold = node.getAttribute("combinedStealFold") + self.combined2B3B = node.getAttribute("combined2B3B") + self.combinedPostflop = node.getAttribute("combinedPostflop") + + def __str__(self): + return (" combinedStealFold = %s\n combined2B3B = %s\n combinedPostflop = %s\n" % + (self.combinedStealFold, self.combined2B3B, self.combinedPostflop) ) + +class Config: + def __init__(self, file = None): + +# "file" is a path to an xml file with the fpdb/HUD configuration +# we check the existence of "file" and try to recover if it doesn't exist + + self.default_config_path = self.get_default_config_path() + if not file == None: # configuration file path has been passed + if not os.path.exists(file): + print "Configuration file %s not found. Using defaults." % (file) + sys.stderr.write("Configuration file %s not found. Using defaults." % (file)) + file = None + + if file == None: # configuration file path not passed or invalid + file = self.find_config() #Look for a config file in the normal places + + if file == None: # no config file in the normal places + file = self.find_example_config() #Look for an example file to edit + if not file == None: + pass + + if file == None: # that didn't work either, just die + print "No HUD_config_xml found. Exiting" + sys.stderr.write("No HUD_config_xml found. Exiting") + sys.exit() + +# Parse even if there was no real config file found and we are using the example +# If using the example, we'll edit it later + try: + print "Reading configuration file %s\n" % (file) + doc = xml.dom.minidom.parse(file) + except: + print "Error parsing %s. See error log file." % (file) + traceback.print_exc(file=sys.stderr) + print "press enter to continue" + sys.stdin.readline() + sys.exit() + + self.doc = doc + self.file = file + self.supported_sites = {} + self.supported_games = {} + self.supported_databases = {} + self.aux_windows = {} + self.popup_windows = {} + +# s_sites = doc.getElementsByTagName("supported_sites") + for site_node in doc.getElementsByTagName("site"): + site = Site(node = site_node) + self.supported_sites[site.site_name] = site + +# s_games = doc.getElementsByTagName("supported_games") + for game_node in doc.getElementsByTagName("game"): + game = Game(node = game_node) + self.supported_games[game.game_name] = game + +# s_dbs = doc.getElementsByTagName("supported_databases") + for db_node in doc.getElementsByTagName("database"): + db = Database(node = db_node) + self.supported_databases[db.db_name] = db + +# s_dbs = doc.getElementsByTagName("mucked_windows") + for aw_node in doc.getElementsByTagName("aw"): + aw = Aux_window(node = aw_node) + self.aux_windows[aw.name] = aw + +# s_dbs = doc.getElementsByTagName("popup_windows") + for pu_node in doc.getElementsByTagName("pu"): + pu = Popup(node = pu_node) + self.popup_windows[pu.name] = pu + + for imp_node in doc.getElementsByTagName("import"): + imp = Import(node = imp_node) + self.imp = imp + + for tv_node in doc.getElementsByTagName("tv"): + tv = Tv(node = tv_node) + self.tv = tv + + db = self.get_db_parameters('fpdb') + if db['db-password'] == 'YOUR MYSQL PASSWORD': + df_file = self.find_default_conf() + if df_file == None: # this is bad + pass + else: + df_parms = self.read_default_conf(df_file) + self.set_db_parameters(db_name = 'fpdb', db_ip = df_parms['db-host'], + db_user = df_parms['db-user'], + db_pass = df_parms['db-password']) + self.save(file=os.path.join(self.default_config_path, "HUD_config.xml")) + + + def find_config(self): + """Looks in cwd and in self.default_config_path for a config file.""" + if os.path.exists('HUD_config.xml'): # there is a HUD_config in the cwd + file = 'HUD_config.xml' # so we use it + else: # no HUD_config in the cwd, look where it should be in the first place + config_path = os.path.join(self.default_config_path, 'HUD_config.xml') + if os.path.exists(config_path): + file = config_path + else: + file = None + return file + + def get_default_config_path(self): + """Returns the path where the fpdb config file _should_ be stored.""" + if os.name == 'posix': + config_path = os.path.join(os.path.expanduser("~"), '.fpdb') + elif os.name == 'nt': + config_path = os.path.join(os.environ["APPDATA"], 'fpdb') + else: config_path = None + return config_path + + + def find_default_conf(self): + if os.name == 'posix': + config_path = os.path.join(os.path.expanduser("~"), '.fpdb', 'default.conf') + elif os.name == 'nt': + config_path = os.path.join(os.environ["APPDATA"], 'fpdb', 'default.conf') + else: config_path = False + + if config_path and os.path.exists(config_path): + file = config_path + else: + file = None + return file + + def read_default_conf(self, file): + parms = {} + fh = open(file, "r") + for line in fh: + line = string.strip(line) + (key, value) = line.split('=') + parms[key] = value + fh.close + return parms + + def find_example_config(self): + if os.path.exists('HUD_config.xml.example'): # there is a HUD_config in the cwd + file = 'HUD_config.xml.example' # so we use it + print "No HUD_config.xml found, using HUD_config.xml.example.\n", \ + "A HUD_config.xml will be written. You will probably have to edit it." + sys.stderr.write("No HUD_config.xml found, using HUD_config.xml.example.\n" + \ + "A HUD_config.xml will be written. You will probably have to edit it.") + else: + file = None + return file + + def get_site_node(self, site): + for site_node in self.doc.getElementsByTagName("site"): + if site_node.getAttribute("site_name") == site: + return site_node + + def get_db_node(self, db_name): + for db_node in self.doc.getElementsByTagName("database"): + if db_node.getAttribute("db_name") == db_name: + return db_node + return None + + def get_layout_node(self, site_node, layout): + for layout_node in site_node.getElementsByTagName("layout"): + if layout_node.getAttribute("max") == None: + return None + if int( layout_node.getAttribute("max") ) == int( layout ): + return layout_node + + def get_location_node(self, layout_node, seat): + for location_node in layout_node.getElementsByTagName("location"): + if int( location_node.getAttribute("seat") ) == int( seat ): + return location_node + + def save(self, file = None): + if not file == None: + f = open(file, 'w') + self.doc.writexml(f) + f.close() + else: + shutil.move(self.file, self.file+".backup") + f = open(self.file, 'w') + self.doc.writexml(f) + f.close + + def edit_layout(self, site_name, max, width = None, height = None, + fav_seat = None, locations = None): + site_node = self.get_site_node(site_name) + layout_node = self.get_layout_node(site_node, max) + if layout_node == None: return + for i in range(1, max + 1): + location_node = self.get_location_node(layout_node, i) + location_node.setAttribute("x", str( locations[i-1][0] )) + location_node.setAttribute("y", str( locations[i-1][1] )) + self.supported_sites[site_name].layout[max].location[i] = ( locations[i-1][0], locations[i-1][1] ) + + def get_db_parameters(self, name = None): + if name == None: name = 'fpdb' + db = {} + try: + db['db-databaseName'] = name + db['db-host'] = self.supported_databases[name].db_ip + db['db-user'] = self.supported_databases[name].db_user + db['db-password'] = self.supported_databases[name].db_pass + db['db-server'] = self.supported_databases[name].db_server + if string.lower(self.supported_databases[name].db_server) == 'mysql': + db['db-backend'] = 2 + elif string.lower(self.supported_databases[name].db_server) == 'postgresql': + db['db-backend'] = 3 + else: db['db-backend'] = None # this is big trouble + except: + pass + return db + + def set_db_parameters(self, db_name = 'fpdb', db_ip = None, db_user = None, + db_pass = None, db_server = None, db_type = None): + db_node = self.get_db_node(db_name) + if not db_node == None: + if not db_ip == None: db_node.setAttribute("db_ip", db_ip) + if not db_user == None: db_node.setAttribute("db_user", db_user) + if not db_pass == None: db_node.setAttribute("db_pass", db_pass) + if not db_server == None: db_node.setAttribute("db_server", db_server) + if not db_type == None: db_node.setAttribute("db_type", db_type) + if self.supported_databases.has_key(db_name): + if not db_ip == None: self.supported_databases[db_name].dp_ip = db_ip + if not db_user == None: self.supported_databases[db_name].dp_user = db_user + if not db_pass == None: self.supported_databases[db_name].dp_pass = db_pass + if not db_server == None: self.supported_databases[db_name].dp_server = db_server + if not db_type == None: self.supported_databases[db_name].dp_type = db_type + return + + def get_tv_parameters(self): + tv = {} + try: + tv['combinedStealFold'] = self.tv.combinedStealFold + tv['combined2B3B'] = self.tv.combined2B3B + tv['combinedPostflop'] = self.tv.combinedPostflop + except: # Default tv parameters + tv['combinedStealFold'] = True + tv['combined2B3B'] = True + tv['combinedPostflop'] = True + return tv + + def get_import_parameters(self): + imp = {} + try: + imp['callFpdbHud'] = self.callFpdbHud + imp['interval'] = self.interval + imp['hhArchiveBase'] = self.hhArchiveBase + except: # Default params + imp['callFpdbHud'] = True + imp['interval'] = 10 + imp['hhArchiveBase'] = "~/.fpdb/HandHistories/" + return imp + + def get_default_paths(self, site = "PokerStars"): + paths = {} + try: + paths['hud-defaultPath'] = os.path.expanduser(self.supported_sites[site].HH_path) + paths['bulkImport-defaultPath'] = os.path.expanduser(self.supported_sites[site].HH_path) + except: + paths['hud-defaultPath'] = "default" + paths['bulkImport-defaultPath'] = "default" + return paths + + def get_default_colors(self, site = "PokerStars"): + colors = {} + if self.supported_sites[site].hudopacity == "": + colors['hudopacity'] = 0.90 + else: + colors['hudopacity'] = float(self.supported_sites[site].hudopacity) + if self.supported_sites[site].hudbgcolor == "": + colors['hudbgcolor'] = "#FFFFFF" + else: + colors['hudbgcolor'] = self.supported_sites[site].hudbgcolor + if self.supported_sites[site].hudfgcolor == "": + colors['hudfgcolor'] = "#000000" + else: + colors['hudfgcolor'] = self.supported_sites[site].hudfgcolor + return colors + + def get_locations(self, site = "PokerStars", max = "8"): + + try: + locations = self.supported_sites[site].layout[max].location + except: + locations = ( ( 0, 0), (684, 61), (689, 239), (692, 346), + (586, 393), (421, 440), (267, 440), ( 0, 361), + ( 0, 280), (121, 280), ( 46, 30) ) + return locations + + def get_supported_sites(self): + """Returns the list of supported sites.""" + return self.supported_sites.keys() + + def get_site_parameters(self, site): + """Returns a dict of the site parameters for the specified site""" + if not self.supported_sites.has_key(site): + return None + parms = {} + parms["converter"] = self.supported_sites[site].converter + parms["decoder"] = self.supported_sites[site].decoder + parms["hudbgcolor"] = self.supported_sites[site].hudbgcolor + parms["hudfgcolor"] = self.supported_sites[site].hudfgcolor + parms["hudopacity"] = self.supported_sites[site].hudopacity + parms["screen_name"] = self.supported_sites[site].screen_name + parms["site_path"] = self.supported_sites[site].site_path + parms["table_finder"] = self.supported_sites[site].table_finder + parms["HH_path"] = self.supported_sites[site].HH_path + parms["site_name"] = self.supported_sites[site].site_name + parms["enabled"] = self.supported_sites[site].enabled + parms["aux_window"] = self.supported_sites[site].aux_window + return parms + + def set_site_parameters(self, site_name, converter = None, decoder = None, + hudbgcolor = None, hudfgcolor = None, + hudopacity = None, screen_name = None, + site_path = None, table_finder = None, + HH_path = None, enabled = None): + """Sets the specified site parameters for the specified site.""" + site_node = self.get_site_node(site_name) + if not db_node == None: + if not converter == None: site_node.setAttribute("converter", converter) + if not decoder == None: site_node.setAttribute("decoder", decoder) + if not hudbgcolor == None: site_node.setAttribute("hudbgcolor", hudbgcolor) + if not hudfgcolor == None: site_node.setAttribute("hudfgcolor", hudfgcolor) + if not hudopacity == None: site_node.setAttribute("hudopacity", hudopacity) + if not screen_name == None: site_node.setAttribute("screen_name", screen_name) + if not site_path == None: site_node.setAttribute("site_path", site_path) + if not table_finder == None: site_node.setAttribute("table_finder", table_finder) + if not HH_path == None: site_node.setAttribute("HH_path", HH_path) + if not enabled == None: site_node.setAttribute("enabled", enabled) + + if self.supported_databases.has_key(db_name): + if not converter == None: self.supported_sites[site].converter = converter + if not decoder == None: self.supported_sites[site].decoder = decoder + if not hudbgcolor == None: self.supported_sites[site].hudbgcolor = hudbgcolor + if not hudfgcolor == None: self.supported_sites[site].hudfgcolor = hudfgcolor + if not hudopacity == None: self.supported_sites[site].hudopacity = hudopacity + if not screen_name == None: self.supported_sites[site].screen_name = screen_name + if not site_path == None: self.supported_sites[site].site_path = site_path + if not table_finder == None: self.supported_sites[site].table_finder = table_finder + if not HH_path == None: self.supported_sites[site].HH_path = HH_path + if not enabled == None: self.supported_sites[site].enabled = enabled + return + + def get_aux_windows(self): + """Gets the list of mucked window formats in the configuration.""" + mw = [] + for w in self.aux_windows.keys(): + mw.append(w) + return mw + + def get_aux_parameters(self, name): + """Gets a dict of mucked window parameters from the named mw.""" + param = {} + if self.aux_windows.has_key(name): + for key in dir(self.aux_windows[name]): + if key.startswith('__'): continue + value = getattr(self.aux_windows[name], key) + if callable(value): continue + param[key] = value + + return param + return None + + def get_game_parameters(self, name): + """Get the configuration parameters for the named game.""" + param = {} + if self.supported_games.has_key(name): + param['game_name'] = self.supported_games[name].game_name + param['db'] = self.supported_games[name].db + param['rows'] = self.supported_games[name].rows + param['cols'] = self.supported_games[name].cols + param['aux'] = self.supported_games[name].aux + return param + + def get_supported_games(self): + """Get the list of supported games.""" + sg = [] + for game in c.supported_games.keys(): + sg.append(c.supported_games[game].game_name) + return sg + +if __name__== "__main__": + c = Config() + + print "\n----------- SUPPORTED SITES -----------" + for s in c.supported_sites.keys(): + print c.supported_sites[s] + print "----------- END SUPPORTED SITES -----------" + + + print "\n----------- SUPPORTED GAMES -----------" + for game in c.supported_games.keys(): + print c.supported_games[game] + print "----------- END SUPPORTED GAMES -----------" + + + print "\n----------- SUPPORTED DATABASES -----------" + for db in c.supported_databases.keys(): + print c.supported_databases[db] + print "----------- END SUPPORTED DATABASES -----------" + + print "\n----------- AUX WINDOW FORMATS -----------" + for w in c.aux_windows.keys(): + print c.aux_windows[w] + print "----------- END AUX WINDOW FORMATS -----------" + + print "\n----------- POPUP WINDOW FORMATS -----------" + for w in c.popup_windows.keys(): + print c.popup_windows[w] + print "----------- END POPUP WINDOW FORMATS -----------" + + print "\n----------- IMPORT -----------" + tmp = c.get_import_parameters() + for param in tmp: + print " " + str(param) + ": " + str(tmp[param]) + print "----------- END IMPORT -----------" + + print "\n----------- TABLE VIEW -----------" +# print c.tv + print "----------- END TABLE VIEW -----------" + + c.edit_layout("PokerStars", 6, locations=( (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6) )) + c.save(file="testout.xml") + + print "db = ", c.get_db_parameters() +# print "tv = ", c.get_tv_parameters() +# print "imp = ", c.get_import_parameters() + print "paths = ", c.get_default_paths("PokerStars") + print "colors = ", c.get_default_colors("PokerStars") + print "locs = ", c.get_locations("PokerStars", 8) + for mw in c.get_aux_windows(): + print c.get_aux_parameters(mw) + + for site in c.supported_sites.keys(): + print "site = ", site, + print c.get_site_parameters(site) + + for game in c.get_supported_games(): + print c.get_game_parameters(game) diff --git a/pyfpdb/FpdbSQLQueries.py b/pyfpdb/FpdbSQLQueries.py index e582c681..9b791f5d 100644 --- a/pyfpdb/FpdbSQLQueries.py +++ b/pyfpdb/FpdbSQLQueries.py @@ -601,7 +601,34 @@ class FpdbSQLQueries: WHERE Players.name = %s AND Players.siteId = %s AND (tourneysPlayersId IS NULL) ORDER BY handStart""" - # Returns the profit for a given ring game handId, Total pot - money invested by playerId + # Returns the profit for all hands, Total pot - money invested by playerId + if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL'): + self.query['getRingProfitAllHandsPlayerIdSite'] = """ + SELECT hp.handId, hp.winnings, SUM(ha.amount) costs, hp.winnings - SUM(ha.amount) profit + FROM HandsPlayers hp + INNER JOIN Players pl ON hp.playerId = pl.id + INNER JOIN Hands h ON h.id = hp.handId + INNER JOIN HandsActions ha ON ha.handPlayerId = hp.id + WHERE pl.name = %s + AND pl.siteId = %s + AND hp.tourneysPlayersId IS NULL + group by hp.handId, hp.winnings, h.handStart + ORDER BY h.handStart""" + elif(self.dbname == 'SQLite'): + #May not work. + self.query['getRingProfitAllHandsPlayerIdSite'] = """ + SELECT hp.handId, hp.winnings, SUM(ha.amount) costs, hp.winnings - SUM(ha.amount) profit + FROM HandsPlayers hp + INNER JOIN Players pl ON hp.playerId = pl.id + INNER JOIN Hands h ON h.id = hp.handId + INNER JOIN HandsActions ha ON ha.handPlayerId = hp.id + WHERE pl.name = %s + AND pl.siteId = %s + AND hp.tourneysPlayersId IS NULL + group by hp.handId, hp.winnings, h.handStart + ORDER BY h.handStart""" + + # Returns the profit for a given ring game handId, Total pot - money invested by playerId - WRONG, returns players costs if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL'): self.query['getRingProfitFromHandId'] = """SELECT SUM(amount) FROM HandsActions INNER JOIN HandsPlayers ON HandsActions.handPlayerId = HandsPlayers.id diff --git a/pyfpdb/GuiAutoImport.py b/pyfpdb/GuiAutoImport.py index 916170f0..9f07910c 100644 --- a/pyfpdb/GuiAutoImport.py +++ b/pyfpdb/GuiAutoImport.py @@ -1,216 +1,211 @@ -#!/usr/bin/python - -#Copyright 2008 Steffen Jobbagy-Felso -#This program is free software: you can redistribute it and/or modify -#it under the terms of the GNU Affero General Public License as published by -#the Free Software Foundation, version 3 of the License. -# -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -# -#You should have received a copy of the GNU Affero General Public License -#along with this program. If not, see . -#In the "official" distribution you can find the license in -#agpl-3.0.txt in the docs folder of the package. - -import threading -import subprocess - -import pygtk -pygtk.require('2.0') -import gtk -import gobject -import os -import time -import fpdb_import - -class GuiAutoImport (threading.Thread): - def starsBrowseClicked(self, widget, data): - """runs when user clicks browse on auto import tab""" - #print "start of GuiAutoImport.starsBrowseClicked" - current_path=self.starsDirPath.get_text() - - dia_chooser = gtk.FileChooserDialog(title="Please choose the path that you want to auto import", - action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, - buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK)) - #dia_chooser.set_current_folder(pathname) - dia_chooser.set_filename(current_path) - #dia_chooser.set_select_multiple(select_multiple) #not in tv, but want this in bulk import - - response = dia_chooser.run() - if response == gtk.RESPONSE_OK: - #print dia_chooser.get_filename(), 'selected' - self.starsDirPath.set_text(dia_chooser.get_filename()) - elif response == gtk.RESPONSE_CANCEL: - print 'Closed, no files selected' - dia_chooser.destroy() - #end def GuiAutoImport.starsBrowseClicked - - def tiltBrowseClicked(self, widget, data): - """runs when user clicks browse on auto import tab""" - #print "start of GuiAutoImport.tiltBrowseClicked" - current_path=self.tiltDirPath.get_text() - - dia_chooser = gtk.FileChooserDialog(title="Please choose the path that you want to auto import", - action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, - buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK)) - #dia_chooser.set_current_folder(pathname) - dia_chooser.set_filename(current_path) - #dia_chooser.set_select_multiple(select_multiple) #not in tv, but want this in bulk import - - response = dia_chooser.run() - if response == gtk.RESPONSE_OK: - #print dia_chooser.get_filename(), 'selected' - self.tiltDirPath.set_text(dia_chooser.get_filename()) - elif response == gtk.RESPONSE_CANCEL: - print 'Closed, no files selected' - dia_chooser.destroy() - #end def GuiAutoImport.tiltBrowseClicked - - def do_import(self): - """Callback for timer to do an import iteration.""" - self.importer.runUpdated() - print "GuiAutoImport.import_dir done" - return True - - def startClicked(self, widget, data): - """runs when user clicks start on auto import tab""" - -# Check to see if we have an open file handle to the HUD and open one if we do not. -# bufsize = 1 means unbuffered -# We need to close this file handle sometime. - -# TODO: Allow for importing from multiple dirs - REB 29AUG2008 -# As presently written this function does nothing if there is already a pipe open. -# That is not correct. It should open another dir for importing while piping the -# results to the same pipe. This means that self.path should be a a list of dirs -# to watch. - try: #uhhh, I don't this this is the best way to check for the existence of an attr - getattr(self, "pipe_to_hud") - except AttributeError: - if os.name == 'nt': - command = "python HUD_main.py" + " %s" % (self.database) - bs = 0 # windows is not happy with line buffing here - self.pipe_to_hud = subprocess.Popen(command, bufsize = bs, stdin = subprocess.PIPE, - universal_newlines=True) - else: - cwd = os.getcwd() - command = os.path.join(cwd, 'HUD_main.py') - bs = 1 - self.pipe_to_hud = subprocess.Popen((command, self.database), bufsize = bs, stdin = subprocess.PIPE, - universal_newlines=True) -# self.pipe_to_hud = subprocess.Popen((command, self.database), bufsize = bs, stdin = subprocess.PIPE, -# universal_newlines=True) -# command = command + " %s" % (self.database) -# print "command = ", command -# self.pipe_to_hud = os.popen(command, 'w') - self.starspath=self.starsDirPath.get_text() - self.tiltpath=self.tiltDirPath.get_text() - -# Add directory to importer object. - self.importer.addImportDirectory(self.starspath, True, "PokerStars", "passthrough") - self.importer.addImportDirectory(self.tiltpath, True, "FullTilt", "passthrough") - self.do_import() - - interval=int(self.intervalEntry.get_text()) - gobject.timeout_add(interval*1000, self.do_import) - #end def GuiAutoImport.startClicked - - def get_vbox(self): - """returns the vbox of this thread""" - return self.mainVBox - #end def get_vbox - - def __init__(self, settings, config, debug=True): - """Constructor for GuiAutoImport""" - self.settings=settings - self.config=config - self.importer = fpdb_import.Importer(self,self.settings) - self.importer.setCallHud(True) - self.importer.setMinPrint(30) - self.importer.setQuiet(False) - self.importer.setFailOnError(False) - self.importer.setHandCount(0) -# self.importer.setWatchTime() - - self.server=settings['db-host'] - self.user=settings['db-user'] - self.password=settings['db-password'] - self.database=settings['db-databaseName'] - - self.mainVBox=gtk.VBox(False,1) - self.mainVBox.show() - - self.settingsHBox = gtk.HBox(False, 0) - self.mainVBox.pack_start(self.settingsHBox, False, True, 0) - self.settingsHBox.show() - - self.intervalLabel = gtk.Label("Interval (ie. break) between imports in seconds:") - self.settingsHBox.pack_start(self.intervalLabel) - self.intervalLabel.show() - - self.intervalEntry=gtk.Entry() - self.intervalEntry.set_text(str(self.settings['hud-defaultInterval'])) - self.settingsHBox.pack_start(self.intervalEntry) - self.intervalEntry.show() - - self.pathHBox = gtk.HBox(False, 0) - self.mainVBox.pack_start(self.pathHBox, False, True, 0) - self.pathHBox.show() - - self.pathStarsLabel = gtk.Label("Path to PokerStars auto-import:") - self.pathHBox.pack_start(self.pathStarsLabel, False, False, 0) - self.pathStarsLabel.show() - - self.starsDirPath=gtk.Entry() - paths = self.config.get_default_paths("PokerStars") - self.starsDirPath.set_text(paths['hud-defaultPath']) - self.pathHBox.pack_start(self.starsDirPath, False, True, 0) - self.starsDirPath.show() - - self.browseButton=gtk.Button("Browse...") - self.browseButton.connect("clicked", self.starsBrowseClicked, "Browse clicked") - self.pathHBox.pack_start(self.browseButton, False, False, 0) - self.browseButton.show() - - self.pathTiltLabel = gtk.Label("Path to Full Tilt auto-import:") - self.pathHBox.pack_start(self.pathTiltLabel, False, False, 0) - self.pathTiltLabel.show() - - self.tiltDirPath=gtk.Entry() - paths = self.config.get_default_paths("Full Tilt") - self.tiltDirPath.set_text(paths['hud-defaultPath']) - self.pathHBox.pack_start(self.tiltDirPath, False, True, 0) - self.tiltDirPath.show() - - self.browseButton=gtk.Button("Browse...") - self.browseButton.connect("clicked", self.tiltBrowseClicked, "Browse clicked") - self.pathHBox.pack_start(self.browseButton, False, False, 0) - self.browseButton.show() - - self.startButton=gtk.Button("Start Autoimport") - self.startButton.connect("clicked", self.startClicked, "start clicked") - self.mainVBox.add(self.startButton) - self.startButton.show() - #end of GuiAutoImport.__init__ -if __name__== "__main__": - def destroy(*args): # call back for terminating the main eventloop - gtk.main_quit() - - settings = {} - settings['db-host'] = "192.168.1.100" - settings['db-user'] = "mythtv" - settings['db-password'] = "mythtv" - settings['db-databaseName'] = "fpdb" - settings['hud-defaultInterval'] = 10 - settings['hud-defaultPath'] = 'C:/Program Files/PokerStars/HandHistory/nutOmatic' - settings['imp-callFpdbHud'] = True - - i = GuiAutoImport(settings) - main_window = gtk.Window() - main_window.connect("destroy", destroy) - main_window.add(i.mainVBox) - main_window.show() - gtk.main() +#!/usr/bin/python + +#Copyright 2008 Steffen Jobbagy-Felso +#This program is free software: you can redistribute it and/or modify +#it under the terms of the GNU Affero General Public License as published by +#the Free Software Foundation, version 3 of the License. +# +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +# +#You should have received a copy of the GNU Affero General Public License +#along with this program. If not, see . +#In the "official" distribution you can find the license in +#agpl-3.0.txt in the docs folder of the package. + +import threading +import subprocess + +import pygtk +pygtk.require('2.0') +import gtk +import gobject +import os +import time +import fpdb_import + + +class GuiAutoImport (threading.Thread): + def __init__(self, settings, config): + """Constructor for GuiAutoImport""" + self.settings=settings + self.config=config + + imp = self.config.get_import_parameters() + + print "Import parameters" + print imp + + self.input_settings = {} + + self.importer = fpdb_import.Importer(self, self.settings, self.config) + self.importer.setCallHud(True) + self.importer.setMinPrint(30) + self.importer.setQuiet(False) + self.importer.setFailOnError(False) + self.importer.setHandCount(0) +# self.importer.setWatchTime() + + self.server=settings['db-host'] + self.user=settings['db-user'] + self.password=settings['db-password'] + self.database=settings['db-databaseName'] + + self.mainVBox=gtk.VBox(False,1) + self.mainVBox.show() + + self.settingsHBox = gtk.HBox(False, 0) + self.mainVBox.pack_start(self.settingsHBox, False, True, 0) + self.settingsHBox.show() + + self.intervalLabel = gtk.Label("Time between imports in seconds:") + self.settingsHBox.pack_start(self.intervalLabel) + self.intervalLabel.show() + + self.intervalEntry=gtk.Entry() + self.intervalEntry.set_text(str(self.config.get_import_parameters().get("interval"))) + self.settingsHBox.pack_start(self.intervalEntry) + self.intervalEntry.show() + + self.addSites(self.mainVBox) + + self.startButton=gtk.Button("Start Autoimport") + self.startButton.connect("clicked", self.startClicked, "start clicked") + self.mainVBox.add(self.startButton) + self.startButton.show() + + + #end of GuiAutoImport.__init__ + def browseClicked(self, widget, data): + """runs when user clicks one of the browse buttons in the auto import tab""" + current_path=data[1].get_text() + + dia_chooser = gtk.FileChooserDialog(title="Please choose the path that you want to auto import", + action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, + buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK)) + #dia_chooser.set_current_folder(pathname) + dia_chooser.set_filename(current_path) + #dia_chooser.set_select_multiple(select_multiple) #not in tv, but want this in bulk import + + response = dia_chooser.run() + if response == gtk.RESPONSE_OK: + #print dia_chooser.get_filename(), 'selected' + data[1].set_text(dia_chooser.get_filename()) + self.input_settings[data[0]][0] = dia_chooser.get_filename() + elif response == gtk.RESPONSE_CANCEL: + print 'Closed, no files selected' + dia_chooser.destroy() + #end def GuiAutoImport.browseClicked + + def do_import(self): + """Callback for timer to do an import iteration.""" + self.importer.runUpdated() + print "GuiAutoImport.import_dir done" + return True + + def startClicked(self, widget, data): + """runs when user clicks start on auto import tab""" + +# Check to see if we have an open file handle to the HUD and open one if we do not. +# bufsize = 1 means unbuffered +# We need to close this file handle sometime. + +# TODO: Allow for importing from multiple dirs - REB 29AUG2008 +# As presently written this function does nothing if there is already a pipe open. +# That is not correct. It should open another dir for importing while piping the +# results to the same pipe. This means that self.path should be a a list of dirs +# to watch. + try: #uhhh, I don't this this is the best way to check for the existence of an attr + getattr(self, "pipe_to_hud") + except AttributeError: + if os.name == 'nt': + command = "python HUD_main.py" + " %s" % (self.database) + bs = 0 # windows is not happy with line buffing here + self.pipe_to_hud = subprocess.Popen(command, bufsize = bs, stdin = subprocess.PIPE, + universal_newlines=True) + else: + cwd = os.getcwd() + command = os.path.join(cwd, 'HUD_main.py') + bs = 1 + self.pipe_to_hud = subprocess.Popen((command, self.database), bufsize = bs, stdin = subprocess.PIPE, + universal_newlines=True) +# self.pipe_to_hud = subprocess.Popen((command, self.database), bufsize = bs, stdin = subprocess.PIPE, +# universal_newlines=True) +# command = command + " %s" % (self.database) +# print "command = ", command +# self.pipe_to_hud = os.popen(command, 'w') + +# Add directories to importer object. + for site in self.input_settings: + self.importer.addImportDirectory(self.input_settings[site][0], True, site, self.input_settings[site][1]) + print "Adding import directories - Site: " + site + " dir: "+ str(self.input_settings[site][0]) + self.do_import() + + interval=int(self.intervalEntry.get_text()) + gobject.timeout_add(interval*1000, self.do_import) + #end def GuiAutoImport.startClicked + + def get_vbox(self): + """returns the vbox of this thread""" + return self.mainVBox + #end def get_vbox + + #Create the site line given required info and setup callbacks + #enabling and disabling sites from this interface not possible + #expects a box to layout the line horizontally + def createSiteLine(self, hbox, site, iconpath, hhpath, filter_name, active = True): + label = gtk.Label(site + " auto-import:") + hbox.pack_start(label, False, False, 0) + label.show() + + dirPath=gtk.Entry() + dirPath.set_text(hhpath) + hbox.pack_start(dirPath, False, True, 0) + dirPath.show() + + browseButton=gtk.Button("Browse...") + browseButton.connect("clicked", self.browseClicked, [site] + [dirPath]) + hbox.pack_start(browseButton, False, False, 0) + browseButton.show() + + label = gtk.Label(site + " filter:") + hbox.pack_start(label, False, False, 0) + label.show() + + filter=gtk.Entry() + filter.set_text(filter_name) + hbox.pack_start(filter, False, True, 0) + filter.show() + + def addSites(self, vbox): + for site in self.config.supported_sites.keys(): + pathHBox = gtk.HBox(False, 0) + vbox.pack_start(pathHBox, False, True, 0) + pathHBox.show() + + paths = self.config.get_default_paths(site) + params = self.config.get_site_parameters(site) + self.createSiteLine(pathHBox, site, False, paths['hud-defaultPath'], params['converter'], params['enabled']) + self.input_settings[site] = [paths['hud-defaultPath']] + [params['converter']] + +if __name__== "__main__": + def destroy(*args): # call back for terminating the main eventloop + gtk.main_quit() + + settings = {} + settings['db-host'] = "192.168.1.100" + settings['db-user'] = "mythtv" + settings['db-password'] = "mythtv" + settings['db-databaseName'] = "fpdb" + settings['hud-defaultInterval'] = 10 + settings['hud-defaultPath'] = 'C:/Program Files/PokerStars/HandHistory/nutOmatic' + settings['callFpdbHud'] = True + + i = GuiAutoImport(settings) + main_window = gtk.Window() + main_window.connect("destroy", destroy) + main_window.add(i.mainVBox) + main_window.show() + gtk.main() diff --git a/pyfpdb/GuiGraphViewer.py b/pyfpdb/GuiGraphViewer.py index 7385e9fe..e4f9270e 100644 --- a/pyfpdb/GuiGraphViewer.py +++ b/pyfpdb/GuiGraphViewer.py @@ -87,16 +87,20 @@ class GuiGraphViewer (threading.Thread): #end of def showClicked def getRingProfitGraph(self, name, site): - self.cursor.execute(self.sql.query['getRingWinningsAllGamesPlayerIdSite'], (name, site)) + #self.cursor.execute(self.sql.query['getRingWinningsAllGamesPlayerIdSite'], (name, site)) + self.cursor.execute(self.sql.query['getRingProfitAllHandsPlayerIdSite'], (name, site)) + # returns (HandId,Winnings,Costs,Profit) winnings = self.db.cursor.fetchall() - profit=range(len(winnings)) - for i in profit: - self.cursor.execute(self.sql.query['getRingProfitFromHandId'], (name, winnings[i][0], site)) - spent = self.db.cursor.fetchone() - profit[i]=(i, winnings[i][1]-spent[0]) + #profit=range(len(winnings)) + #for i in profit: + # self.cursor.execute(self.sql.query['getRingProfitFromHandId'], (name, winnings[i][0], site)) + # spent = self.db.cursor.fetchone() + # profit[i]=(i, winnings[i][1]-spent[0]) + + #y=map(lambda x:float(x[1]), profit) + y=map(lambda x:float(x[3]), winnings) - y=map(lambda x:float(x[1]), profit) line = cumsum(y) return line/100 #end of def getRingProfitGraph diff --git a/pyfpdb/HUD_main.py b/pyfpdb/HUD_main.py old mode 100755 new mode 100644 index f54fa9af..c0244e8d --- a/pyfpdb/HUD_main.py +++ b/pyfpdb/HUD_main.py @@ -1,166 +1,171 @@ -#!/usr/bin/env python - -"""Hud_main.py - -Main for FreePokerTools HUD. -""" -# Copyright 2008, Ray E. Barker -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -######################################################################## - -# to do kill window on my seat -# to do adjust for preferred seat -# to do allow window resizing -# to do hud to echo, but ignore non numbers -# to do no hud window for hero -# to do things to add to config.xml -# to do font and size -# to do opacity - -# Standard Library modules -import sys -import os -import thread -import time -import string -import re - -errorfile = open('HUD-error.txt', 'w', 0) -sys.stderr = errorfile - -# pyGTK modules -import pygtk -import gtk -import gobject - -# FreePokerTools modules -import Configuration -import Database -import Tables -import Hud - -# global dict for keeping the huds -hud_dict = {} - -db_connection = 0; -config = 0; - -def destroy(*args): # call back for terminating the main eventloop - gtk.main_quit() - -def create_HUD(new_hand_id, table, db_name, table_name, max, poker_game, db_connection, config, stat_dict): - global hud_dict - def idle_func(): - global hud_dict - gtk.gdk.threads_enter() - try: - hud_dict[table_name] = Hud.Hud(table, max, poker_game, config, db_name) - hud_dict[table_name].create(new_hand_id, config) - hud_dict[table_name].update(new_hand_id, config, stat_dict) - hud_dict[table_name].reposition_windows() - return False - finally: - gtk.gdk.threads_leave() - gobject.idle_add(idle_func) - -def update_HUD(new_hand_id, table_name, config, stat_dict): - global hud_dict - def idle_func(): - gtk.gdk.threads_enter() - try: - hud_dict[table_name].update(new_hand_id, config, stat_dict) - return False - finally: - gtk.gdk.threads_leave() - gobject.idle_add(idle_func) - -def read_stdin(): # This is the thread function - global hud_dict - - db_connection = Database.Database(config, db_name, 'temp') -# tourny_finder = re.compile('(\d+) (\d+)') - - while True: # wait for a new hand number on stdin - new_hand_id = sys.stdin.readline() - new_hand_id = string.rstrip(new_hand_id) - if new_hand_id == "": # blank line means quit - destroy() - -# delete hud_dict entries for any HUD destroyed since last iteration - for h in hud_dict.keys(): - if hud_dict[h].deleted: - del(hud_dict[h]) - -# get basic info about the new hand from the db - (table_name, max, poker_game) = db_connection.get_table_name(new_hand_id) - -# find out if this hand is from a tournament - is_tournament = False -# (t_number, s_number) = (0, 0) -# mat_obj = tourny_finder(table_name) -# if len(mat_obj.groups) == 2: -# is_tournament = True -# (t_number, s_number) = mat_obj.group(1, 2) - - stat_dict = db_connection.get_stats_from_hand(new_hand_id) - -# if a hud for this CASH table exists, just update it - if hud_dict.has_key(table_name): - update_HUD(new_hand_id, table_name, config, stat_dict) -# if a hud for this TOURNAMENT table exists, just update it -# elif hud_dict.has_key(t_number): -# update_HUD(new_hand_id, t_number, config, stat_dict) -# otherwise create a new hud - else: - if is_tournament: - tablewindow = Tables.discover_tournament_table(config, t_number, s_number) - if tablewindow == None: - sys.stderr.write("table name "+table_name+" not found\n") - else: - create_HUD(new_hand_id, tablewindow, db_name, t_number, max, poker_game, db_connection, config, stat_dict) - else: - tablewindow = Tables.discover_table_by_name(config, table_name) - if tablewindow == None: - sys.stderr.write("table name "+table_name+" not found\n") - else: - create_HUD(new_hand_id, tablewindow, db_name, table_name, max, poker_game, db_connection, config, stat_dict) - -if __name__== "__main__": - sys.stderr.write("HUD_main starting\n") - - try: - db_name = sys.argv[1] - except: - db_name = 'fpdb' - sys.stderr.write("Using db name = %s\n" % (db_name)) - - config = Configuration.Config() - - gobject.threads_init() # this is required - thread.start_new_thread(read_stdin, ()) # starts the thread - - main_window = gtk.Window() - main_window.connect("destroy", destroy) - eb = gtk.EventBox() - label = gtk.Label('Closing this window will exit from the HUD.') - eb.add(label) - main_window.add(eb) - main_window.set_title("HUD Main Window") - main_window.show_all() - - gtk.main() - +#!/usr/bin/env python + +"""Hud_main.py + +Main for FreePokerTools HUD. +""" +# Copyright 2008, Ray E. Barker +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +######################################################################## + +# to do kill window on my seat +# to do adjust for preferred seat +# to do allow window resizing +# to do hud to echo, but ignore non numbers +# to do no hud window for hero +# to do things to add to config.xml +# to do font and size +# to do opacity + +# Standard Library modules +import sys +import os +import thread +import time +import string +import re + +errorfile = open('HUD-error.txt', 'w', 0) +sys.stderr = errorfile + +# pyGTK modules +import pygtk +import gtk +import gobject + +# FreePokerTools modules +import Configuration +import Database +import Tables +import Hud + +# global dict for keeping the huds +hud_dict = {} + +db_connection = 0; +config = 0; + +def destroy(*args): # call back for terminating the main eventloop + gtk.main_quit() + +def create_HUD(new_hand_id, table, db_name, table_name, max, poker_game, db_connection, config, stat_dict): + global hud_dict + def idle_func(): + global hud_dict + gtk.gdk.threads_enter() + try: + hud_dict[table_name] = Hud.Hud(table, max, poker_game, config, db_name) + hud_dict[table_name].create(new_hand_id, config) + hud_dict[table_name].update(new_hand_id, config, stat_dict) + hud_dict[table_name].reposition_windows() + return False + finally: + gtk.gdk.threads_leave() + gobject.idle_add(idle_func) + +def update_HUD(new_hand_id, table_name, config, stat_dict): + global hud_dict + def idle_func(): + gtk.gdk.threads_enter() + try: + hud_dict[table_name].update(new_hand_id, config, stat_dict) + for m in hud_dict[table_name].aux_windows: + m.update_gui(new_hand_id) + return False + finally: + gtk.gdk.threads_leave() + gobject.idle_add(idle_func) + +def read_stdin(): # This is the thread function + global hud_dict + + db_connection = Database.Database(config, db_name, 'temp') + tourny_finder = re.compile('(\d+) (\d+)') + + while True: # wait for a new hand number on stdin + new_hand_id = sys.stdin.readline() + new_hand_id = string.rstrip(new_hand_id) + if new_hand_id == "": # blank line means quit + destroy() + +# delete hud_dict entries for any HUD destroyed since last iteration + for h in hud_dict.keys(): + if hud_dict[h].deleted: + del(hud_dict[h]) + +# get basic info about the new hand from the db + (table_name, max, poker_game) = db_connection.get_table_name(new_hand_id) + +# find out if this hand is from a tournament + is_tournament = False + (tour_number, tab_number) = (0, 0) + mat_obj = tourny_finder.search(table_name) +# if len(mat_obj.groups) == 2: + if mat_obj: + is_tournament = True + (tour_number, tab_number) = mat_obj.group(1, 2) + + stat_dict = db_connection.get_stats_from_hand(new_hand_id) + +# if a hud for this CASH table exists, just update it + if hud_dict.has_key(table_name): +# update the data for the aux_windows + for aw in hud_dict[table_name].aux_windows: + aw.update_data(new_hand_id) + update_HUD(new_hand_id, table_name, config, stat_dict) +# if a hud for this TOURNAMENT table exists, just update it + elif hud_dict.has_key(tour_number): + update_HUD(new_hand_id, tour_number, config, stat_dict) +# otherwise create a new hud + else: + if is_tournament: + tablewindow = Tables.discover_tournament_table(config, tour_number, tab_number) + if tablewindow == None: + sys.stderr.write("tournament %s, table %s not found\n" % (tour_number, tab_number)) + else: + create_HUD(new_hand_id, tablewindow, db_name, tour_number, max, poker_game, db_connection, config, stat_dict) + else: + tablewindow = Tables.discover_table_by_name(config, table_name) + if tablewindow == None: + sys.stderr.write("table name "+table_name+" not found\n") + else: + create_HUD(new_hand_id, tablewindow, db_name, table_name, max, poker_game, db_connection, config, stat_dict) + +if __name__== "__main__": + sys.stderr.write("HUD_main starting\n") + + try: + db_name = sys.argv[1] + except: + db_name = 'fpdb' + sys.stderr.write("Using db name = %s\n" % (db_name)) + + config = Configuration.Config() + + gobject.threads_init() # this is required + thread.start_new_thread(read_stdin, ()) # starts the thread + + main_window = gtk.Window() + main_window.connect("destroy", destroy) + eb = gtk.EventBox() + label = gtk.Label('Closing this window will exit from the HUD.') + eb.add(label) + main_window.add(eb) + main_window.set_title("HUD Main Window") + main_window.show_all() + + gtk.main() diff --git a/pyfpdb/Hud.py b/pyfpdb/Hud.py index 2490ad91..1ad38ee0 100644 --- a/pyfpdb/Hud.py +++ b/pyfpdb/Hud.py @@ -1,562 +1,578 @@ -#!/usr/bin/env python -"""Hud.py - -Create and manage the hud overlays. -""" -# Copyright 2008, Ray E. Barker - -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -######################################################################## -# Standard Library modules -import os -import sys - -# pyGTK modules -import pygtk -import gtk -import pango -import gobject - -# win32 modules -- only imported on windows systems -if os.name == 'nt': - import win32gui - import win32con - import win32api - -# FreePokerTools modules -import Tables # needed for testing only -import Configuration -import Stats -import Mucked -import Database -import HUD_main - -class Hud: - - def __init__(self, table, max, poker_game, config, db_name): - self.table = table - self.config = config - self.poker_game = poker_game - self.max = max - self.db_name = db_name - self.deleted = False - self.stacked = True - self.colors = config.get_default_colors(self.table.site) - - self.stat_windows = {} - self.popup_windows = {} - self.font = pango.FontDescription("Sans 8") - -# Set up a main window for this this instance of the HUD - self.main_window = gtk.Window() -# self.window.set_decorated(0) - self.main_window.set_gravity(gtk.gdk.GRAVITY_STATIC) - self.main_window.set_title(table.name + " FPDBHUD") - self.main_window.connect("destroy", self.kill_hud) - self.main_window.set_decorated(False) - #self.main_window.set_transient_for(parent.get_toplevel()) - - self.ebox = gtk.EventBox() - self.label = gtk.Label("Right click to close HUD for %s\nor Save Stat Positions." % (table.name)) - - self.label.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.colors['hudbgcolor'])) - self.label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.colors['hudfgcolor'])) - - self.main_window.add(self.ebox) - self.ebox.add(self.label) - self.ebox.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.colors['hudbgcolor'])) - self.ebox.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.colors['hudfgcolor'])) - - self.main_window.move(self.table.x, self.table.y) - -# A popup window for the main window - self.menu = gtk.Menu() - self.item1 = gtk.MenuItem('Kill this HUD') - self.menu.append(self.item1) - self.item1.connect("activate", self.kill_hud) - self.item1.show() - self.item2 = gtk.MenuItem('Save Layout') - self.menu.append(self.item2) - self.item2.connect("activate", self.save_layout) - self.item2.show() - self.item3 = gtk.MenuItem('Reposition Stats') - self.menu.append(self.item3) - self.item3.connect("activate", self.reposition_windows) - self.item3.show() - self.ebox.connect_object("button-press-event", self.on_button_press, self.menu) - - self.main_window.show_all() -# set_keep_above(1) for windows - if os.name == 'nt': - self.topify_window(self.main_window) - else: - self.main_window.parentgdkhandle = gtk.gdk.window_foreign_new(self.table.number) # gets a gdk handle for poker client - self.main_window.gdkhandle = gtk.gdk.window_foreign_new(self.main_window.window.xid) # gets a gdk handle for the hud table window - self.main_window.gdkhandle.set_transient_for(self.main_window.parentgdkhandle) # - - self.main_window.set_destroy_with_parent(True) - - def on_button_press(self, widget, event): - if event.button == 3: - widget.popup(None, None, None, event.button, event.time) - return True - return False - - def kill_hud(self, args): - for k in self.stat_windows.keys(): - self.stat_windows[k].window.destroy() - self.main_window.destroy() - self.deleted = True - - def reposition_windows(self, args): - for w in self.stat_windows: - self.stat_windows[w].window.move(self.stat_windows[w].x, - self.stat_windows[w].y) - def save_layout(self, *args): - new_layout = [] -# todo: have the hud track the poker table's window position regularly, don't forget to update table.x and table.y. - for sw in self.stat_windows: - loc = self.stat_windows[sw].window.get_position() - new_loc = (loc[0] - self.table.x, loc[1] - self.table.y) - new_layout.append(new_loc) -# print new_layout - self.config.edit_layout(self.table.site, self.max, locations = new_layout) - self.config.save() - - def adj_seats(self, hand, config): - adj = range(0, self.max + 1) # default seat adjustments = no adjustment -# does the user have a fav_seat? - try: - if int(config.supported_sites[self.table.site].layout[self.max].fav_seat) > 0: - fav_seat = config.supported_sites[self.table.site].layout[self.max].fav_seat - db_connection = Database.Database(config, self.db_name, 'temp') - actual_seat = db_connection.get_actual_seat(hand, config.supported_sites[self.table.site].screen_name) - db_connection.close_connection() - for i in range(0, self.max + 1): - j = actual_seat + i - if j > self.max: j = j - self.max - adj[j] = fav_seat + i - if adj[j] > self.max: adj[j] = adj[j] - self.max - except: - pass - return adj - - def create(self, hand, config): -# update this hud, to the stats and players as of "hand" -# hand is the hand id of the most recent hand played at this table -# -# this method also manages the creating and destruction of stat -# windows via calls to the Stat_Window class - - adj = self.adj_seats(hand, config) - loc = self.config.get_locations(self.table.site, self.max) - -# create the stat windows - for i in range(1, self.max + 1): - (x, y) = loc[adj[i]] - if self.stat_windows.has_key(i): - self.stat_windows[i].relocate(x, y) - else: - self.stat_windows[i] = Stat_Window(game = config.supported_games[self.poker_game], - parent = self, - table = self.table, - x = x, - y = y, - seat = i, - player_id = 'fake', - font = self.font) - - self.stats = [] - for i in range(0, config.supported_games[self.poker_game].rows + 1): - row_list = [''] * config.supported_games[self.poker_game].cols - self.stats.append(row_list) - for stat in config.supported_games[self.poker_game].stats.keys(): - self.stats[config.supported_games[self.poker_game].stats[stat].row] \ - [config.supported_games[self.poker_game].stats[stat].col] = \ - config.supported_games[self.poker_game].stats[stat].stat_name -# self.mucked_window = gtk.Window() -# self.m = Mucked.Mucked(self.mucked_window, self.db_connection) -# self.mucked_window.show_all() - - def update(self, hand, config, stat_dict): - self.hand = hand # this is the last hand, so it is available later - for s in stat_dict.keys(): - try: - self.stat_windows[stat_dict[s]['seat']].player_id = stat_dict[s]['player_id'] - except: # omg, we have more seats than stat windows .. damn poker sites with incorrect max seating info .. let's force 10 here - self.max = 10 - self.create(hand, config) - self.stat_windows[stat_dict[s]['seat']].player_id = stat_dict[s]['player_id'] - - for r in range(0, config.supported_games[self.poker_game].rows): - for c in range(0, config.supported_games[self.poker_game].cols): - this_stat = config.supported_games[self.poker_game].stats[self.stats[r][c]] - number = Stats.do_stat(stat_dict, player = stat_dict[s]['player_id'], stat = self.stats[r][c]) - statstring = this_stat.hudprefix + str(number[1]) + this_stat.hudsuffix - self.stat_windows[stat_dict[s]['seat']].label[r][c].set_text(statstring) - tip = stat_dict[s]['screen_name'] + "\n" + number[5] + "\n" + \ - number[3] + ", " + number[4] - Stats.do_tip(self.stat_windows[stat_dict[s]['seat']].e_box[r][c], tip) -# self.m.update(hand) - - def topify_window(self, window): - """Set the specified gtk window to stayontop in MS Windows.""" - - def windowEnumerationHandler(hwnd, resultList): - '''Callback for win32gui.EnumWindows() to generate list of window handles.''' - resultList.append((hwnd, win32gui.GetWindowText(hwnd))) - - unique_name = 'unique name for finding this window' - real_name = window.get_title() - window.set_title(unique_name) - tl_windows = [] - win32gui.EnumWindows(windowEnumerationHandler, tl_windows) - - for w in tl_windows: - if w[1] == unique_name: - #win32gui.ShowWindow(w[0], win32con.SW_HIDE) - window.parentgdkhandle = gtk.gdk.window_foreign_new(long(self.table.number)) - self.main_window.gdkhandle = gtk.gdk.window_foreign_new(w[0]) - self.main_window.gdkhandle.set_transient_for(window.parentgdkhandle) - #win32gui.ShowWindow(w[0], win32con.SW_SHOW) - - style = win32gui.GetWindowLong(self.table.number, win32con.GWL_EXSTYLE) - #style |= win32con.WS_EX_TOOLWINDOW - #style &= ~win32con.WS_EX_APPWINDOW - style |= win32con.WS_CLIPCHILDREN - win32gui.SetWindowLong(self.table.number, win32con.GWL_EXSTYLE, style) - - - #win32gui.SetWindowPos(w[0], win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE|win32con.SWP_NOSIZE) - -# notify_id = (w[0], -# 0, -# win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP, -# win32con.WM_USER+20, -# 0, -# '') -# win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, notify_id) -# - window.set_title(real_name) - -class Stat_Window: - - def button_press_cb(self, widget, event, *args): -# This handles all callbacks from button presses on the event boxes in -# the stat windows. There is a bit of an ugly kludge to separate single- -# and double-clicks. - if event.button == 1: # left button event - if event.type == gtk.gdk.BUTTON_PRESS: # left button single click - if self.sb_click > 0: return - self.sb_click = gobject.timeout_add(250, self.single_click, widget) - elif event.type == gtk.gdk._2BUTTON_PRESS: # left button double click - if self.sb_click > 0: - gobject.source_remove(self.sb_click) - self.sb_click = 0 - self.double_click(widget, event, *args) - - if event.button == 2: # middle button event - pass -# print "middle button clicked" - - if event.button == 3: # right button event - pass -# print "right button clicked" - - def single_click(self, widget): -# Callback from the timeout in the single-click finding part of the -# button press call back. This needs to be modified to get all the -# arguments from the call. -# print "left button clicked" - self.sb_click = 0 - Popup_window(widget, self) - return False - - def double_click(self, widget, event, *args): - self.toggle_decorated(widget) - - def toggle_decorated(self, widget): - top = widget.get_toplevel() - (x, y) = top.get_position() - - if top.get_decorated(): - top.set_decorated(0) - top.move(x, y) - else: - top.set_decorated(1) - top.move(x, y) - - def relocate(self, x, y): - self.x = x + self.table.x - self.y = y + self.table.y - self.window.move(self.x, self.y) - - def __init__(self, parent, game, table, seat, x, y, player_id, font): - self.parent = parent # Hud object that this stat window belongs to - self.game = game # Configuration object for the curren - self.table = table # Table object where this is going - self.x = x + table.x # table.x and y are the location of the table - self.y = y + table.y # x and y are the location relative to table.x & y - self.player_id = player_id # looks like this isn't used ;) - self.sb_click = 0 # used to figure out button clicks - - self.window = gtk.Window() - self.window.set_decorated(0) - self.window.set_gravity(gtk.gdk.GRAVITY_STATIC) - - self.window.set_title("%s" % seat) - self.window.set_property("skip-taskbar-hint", True) - self.window.set_transient_for(parent.main_window) - - self.grid = gtk.Table(rows = self.game.rows, columns = self.game.cols, homogeneous = False) - self.window.add(self.grid) - - self.e_box = [] - self.frame = [] - self.label = [] - for r in range(self.game.rows): - self.e_box.append([]) - self.label.append([]) - for c in range(self.game.cols): - self.e_box[r].append( gtk.EventBox() ) - - self.e_box[r][c].modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(parent.colors['hudbgcolor'])) - self.e_box[r][c].modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(parent.colors['hudfgcolor'])) - - Stats.do_tip(self.e_box[r][c], 'farts') - self.grid.attach(self.e_box[r][c], c, c+1, r, r+1, xpadding = 0, ypadding = 0) - self.label[r].append( gtk.Label('xxx') ) - - self.label[r][c].modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(parent.colors['hudbgcolor'])) - self.label[r][c].modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(parent.colors['hudfgcolor'])) - - self.e_box[r][c].add(self.label[r][c]) - self.e_box[r][c].connect("button_press_event", self.button_press_cb) -# font = pango.FontDescription("Sans 8") - self.label[r][c].modify_font(font) - - if not os.name == 'nt': # seems to be a bug in opacity on windows - self.window.set_opacity(parent.colors['hudopacity']) - self.window.realize - self.window.move(self.x, self.y) - self.window.show_all() -# set_keep_above(1) for windows - if os.name == 'nt': self.topify_window(self.window) - - def topify_window(self, window): - """Set the specified gtk window to stayontop in MS Windows.""" - - def windowEnumerationHandler(hwnd, resultList): - '''Callback for win32gui.EnumWindows() to generate list of window handles.''' - resultList.append((hwnd, win32gui.GetWindowText(hwnd))) - - unique_name = 'unique name for finding this window' - real_name = window.get_title() - window.set_title(unique_name) - tl_windows = [] - win32gui.EnumWindows(windowEnumerationHandler, tl_windows) - - for w in tl_windows: - if w[1] == unique_name: - - #win32gui.SetWindowPos(w[0], win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE|win32con.SWP_NOSIZE) - -# style = win32gui.GetWindowLong(w[0], win32con.GWL_EXSTYLE) -# style |= win32con.WS_EX_TOOLWINDOW -# style &= ~win32con.WS_EX_APPWINDOW -# win32gui.SetWindowLong(w[0], win32con.GWL_EXSTYLE, style) - win32gui.ShowWindow(w[0], win32con.SW_SHOW) - window.set_title(real_name) - -def destroy(*args): # call back for terminating the main eventloop - gtk.main_quit() - -class Popup_window: - def __init__(self, parent, stat_window): - self.sb_click = 0 - -# create the popup window - self.window = gtk.Window() - self.window.set_decorated(0) - self.window.set_gravity(gtk.gdk.GRAVITY_STATIC) -# self.window.set_keep_above(1) - self.window.set_title("popup") - self.window.set_property("skip-taskbar-hint", True) - self.window.set_transient_for(parent.get_toplevel()) - - self.window.set_position(gtk.WIN_POS_CENTER_ON_PARENT) - - self.ebox = gtk.EventBox() - self.ebox.connect("button_press_event", self.button_press_cb) - self.lab = gtk.Label("stuff\nstuff\nstuff") - -# need an event box so we can respond to clicks - self.window.add(self.ebox) - self.ebox.add(self.lab) - - self.ebox.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(stat_window.parent.colors['hudbgcolor'])) - self.ebox.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(stat_window.parent.colors['hudfgcolor'])) - self.window.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(stat_window.parent.colors['hudbgcolor'])) - self.window.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(stat_window.parent.colors['hudfgcolor'])) - self.lab.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(stat_window.parent.colors['hudbgcolor'])) - self.lab.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(stat_window.parent.colors['hudfgcolor'])) - - self.window.realize - -# figure out the row, col address of the click that activated the popup - row = 0 - col = 0 - for r in range(0, stat_window.game.rows): - for c in range(0, stat_window.game.cols): - if stat_window.e_box[r][c] == parent: - row = r - col = c - break - -# figure out what popup format we're using - popup_format = "default" - for stat in stat_window.game.stats.keys(): - if stat_window.game.stats[stat].row == row and stat_window.game.stats[stat].col == col: - popup_format = stat_window.game.stats[stat].popup - break - -# get the list of stats to be presented from the config - stat_list = [] - for w in stat_window.parent.config.popup_windows.keys(): - if w == popup_format: - stat_list = stat_window.parent.config.popup_windows[w].pu_stats - break - -# get a database connection - db_connection = Database.Database(stat_window.parent.config, stat_window.parent.db_name, 'temp') - -# calculate the stat_dict and then create the text for the pu -# stat_dict = db_connection.get_stats_from_hand(stat_window.parent.hand, stat_window.player_id) - stat_dict = db_connection.get_stats_from_hand(stat_window.parent.hand) - db_connection.close_connection() - - pu_text = "" - for s in stat_list: - number = Stats.do_stat(stat_dict, player = int(stat_window.player_id), stat = s) - pu_text += number[3] + "\n" - - self.lab.set_text(pu_text) - self.window.show_all() - - self.window.set_transient_for(stat_window.main_window) - -# set_keep_above(1) for windows - if os.name == 'nt': self.topify_window(self.window) - - def button_press_cb(self, widget, event, *args): -# This handles all callbacks from button presses on the event boxes in -# the popup windows. There is a bit of an ugly kludge to separate single- -# and double-clicks. This is the same code as in the Stat_window class - if event.button == 1: # left button event - if event.type == gtk.gdk.BUTTON_PRESS: # left button single click - if self.sb_click > 0: return - self.sb_click = gobject.timeout_add(250, self.single_click, widget) - elif event.type == gtk.gdk._2BUTTON_PRESS: # left button double click - if self.sb_click > 0: - gobject.source_remove(self.sb_click) - self.sb_click = 0 - self.double_click(widget, event, *args) - - if event.button == 2: # middle button event - pass -# print "middle button clicked" - - if event.button == 3: # right button event - pass -# print "right button clicked" - - def single_click(self, widget): -# Callback from the timeout in the single-click finding part of the -# button press call back. This needs to be modified to get all the -# arguments from the call. - self.sb_click = 0 - self.window.destroy() - return False - - def double_click(self, widget, event, *args): - self.toggle_decorated(widget) - - def toggle_decorated(self, widget): - top = widget.get_toplevel() - (x, y) = top.get_position() - - if top.get_decorated(): - top.set_decorated(0) - top.move(x, y) - else: - top.set_decorated(1) - top.move(x, y) - - def topify_window(self, window): - """Set the specified gtk window to stayontop in MS Windows.""" - - def windowEnumerationHandler(hwnd, resultList): - '''Callback for win32gui.EnumWindows() to generate list of window handles.''' - resultList.append((hwnd, win32gui.GetWindowText(hwnd))) - - unique_name = 'unique name for finding this window' - real_name = window.get_title() - window.set_title(unique_name) - tl_windows = [] - win32gui.EnumWindows(windowEnumerationHandler, tl_windows) - - for w in tl_windows: - if w[1] == unique_name: -# win32gui.ShowWindow(w[0], win32con.SW_HIDE) -# style = win32gui.GetWindowLong(w[0], win32con.GWL_EXSTYLE) -# style |= win32con.WS_EX_TOOLWINDOW -# style &= ~win32con.WS_EX_APPWINDOW -# win32gui.SetWindowLong(w[0], win32con.GWL_EXSTYLE, style) -# win32gui.ShowWindow(w[0], win32con.SW_SHOW) - win32gui.SetWindowPos(w[0], win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE|win32con.SWP_NOSIZE) - -# notify_id = (w[0], -# 0, -# win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP, -# win32con.WM_USER+20, -# 0, -# '') -# win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, notify_id) -# - window.set_title(real_name) - -if __name__== "__main__": - main_window = gtk.Window() - main_window.connect("destroy", destroy) - label = gtk.Label('Fake main window, blah blah, blah\nblah, blah') - main_window.add(label) - main_window.show_all() - - c = Configuration.Config() - #tables = Tables.discover(c) - t = Tables.discover_table_by_name(c, "Chelsea") - if t is None: - print "Table not found." - db = Database.Database(c, 'fpdb', 'holdem') - -# for t in tables: - win = Hud(t, 10, 'holdem', c, db) - win.create(1, c) -# t.get_details() - win.update(8300, db, c) - - gtk.main() +#!/usr/bin/env python +"""Hud.py + +Create and manage the hud overlays. +""" +# Copyright 2008, Ray E. Barker + +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +######################################################################## +# Standard Library modules +import os +import sys + +# pyGTK modules +import pygtk +import gtk +import pango +import gobject + +# win32 modules -- only imported on windows systems +if os.name == 'nt': + import win32gui + import win32con + import win32api + +# FreePokerTools modules +import Tables # needed for testing only +import Configuration +import Stats +import Mucked +import Database +import HUD_main + +class Hud: + + def __init__(self, table, max, poker_game, config, db_name): + self.table = table + self.config = config + self.poker_game = poker_game + self.max = max + self.db_name = db_name + self.deleted = False + self.stacked = True + self.colors = config.get_default_colors(self.table.site) + + self.stat_windows = {} + self.popup_windows = {} + self.aux_windows = [] + self.font = pango.FontDescription("Sans 8") + +# Set up a main window for this this instance of the HUD + self.main_window = gtk.Window() +# self.window.set_decorated(0) + self.main_window.set_gravity(gtk.gdk.GRAVITY_STATIC) + self.main_window.set_title(table.name + " FPDBHUD") + self.main_window.connect("destroy", self.kill_hud) + self.main_window.set_decorated(False) + self.main_window.set_opacity(self.colors["hudopacity"]) + #self.main_window.set_transient_for(parent.get_toplevel()) + + self.ebox = gtk.EventBox() +# self.label = gtk.Label("Right click to close HUD for %s\nor Save Stat Positions." % (table.name)) + self.label = gtk.Label("FPDB Menu (Right Click)\nLeft-drag to move") + + self.label.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.colors['hudbgcolor'])) + self.label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.colors['hudfgcolor'])) + + self.main_window.add(self.ebox) + self.ebox.add(self.label) + self.ebox.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.colors['hudbgcolor'])) + self.ebox.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.colors['hudfgcolor'])) + + self.main_window.move(self.table.x, self.table.y) + +# A popup window for the main window + self.menu = gtk.Menu() + self.item1 = gtk.MenuItem('Kill this HUD') + self.menu.append(self.item1) + self.item1.connect("activate", self.kill_hud) + self.item1.show() + self.item2 = gtk.MenuItem('Save Layout') + self.menu.append(self.item2) + self.item2.connect("activate", self.save_layout) + self.item2.show() + self.item3 = gtk.MenuItem('Reposition Stats') + self.menu.append(self.item3) + self.item3.connect("activate", self.reposition_windows) + self.item3.show() + self.ebox.connect_object("button-press-event", self.on_button_press, self.menu) + + self.main_window.show_all() +# set_keep_above(1) for windows + if os.name == 'nt': + self.topify_window(self.main_window) + else: + self.main_window.parentgdkhandle = gtk.gdk.window_foreign_new(self.table.number) # gets a gdk handle for poker client + self.main_window.gdkhandle = gtk.gdk.window_foreign_new(self.main_window.window.xid) # gets a gdk handle for the hud table window + self.main_window.gdkhandle.set_transient_for(self.main_window.parentgdkhandle) # + + self.main_window.set_destroy_with_parent(True) + + def on_button_press(self, widget, event): + if event.button == 1: + self.main_window.begin_move_drag(event.button, int(event.x_root), int(event.y_root), event.time) + return True + if event.button == 3: + widget.popup(None, None, None, event.button, event.time) + return True + return False + + def kill_hud(self, *args): + for k in self.stat_windows.keys(): + self.stat_windows[k].window.destroy() + self.main_window.destroy() + self.deleted = True + + def reposition_windows(self, *args): + for w in self.stat_windows: + self.stat_windows[w].window.move(self.stat_windows[w].x, + self.stat_windows[w].y) + def save_layout(self, *args): + new_layout = [(0, 0)] * self.max +# todo: have the hud track the poker table's window position regularly, don't forget to update table.x and table.y. + for sw in self.stat_windows: + loc = self.stat_windows[sw].window.get_position() + new_loc = (loc[0] - self.table.x, loc[1] - self.table.y) + new_layout[self.stat_windows[sw].adj - 1] = new_loc + self.config.edit_layout(self.table.site, self.max, locations = new_layout) + self.config.save() + + def adj_seats(self, hand, config): + adj = range(0, self.max + 1) # default seat adjustments = no adjustment +# does the user have a fav_seat? + try: + if int(config.supported_sites[self.table.site].layout[self.max].fav_seat) > 0: + fav_seat = config.supported_sites[self.table.site].layout[self.max].fav_seat + db_connection = Database.Database(config, self.db_name, 'temp') + actual_seat = db_connection.get_actual_seat(hand, config.supported_sites[self.table.site].screen_name) + db_connection.close_connection() + for i in range(0, self.max + 1): + j = actual_seat + i + if j > self.max: j = j - self.max + adj[j] = fav_seat + i + if adj[j] > self.max: adj[j] = adj[j] - self.max + except: + pass + return adj + + def create(self, hand, config): +# update this hud, to the stats and players as of "hand" +# hand is the hand id of the most recent hand played at this table +# +# this method also manages the creating and destruction of stat +# windows via calls to the Stat_Window class + + adj = self.adj_seats(hand, config) + loc = self.config.get_locations(self.table.site, self.max) + +# create the stat windows + for i in range(1, self.max + 1): + (x, y) = loc[adj[i]] + if self.stat_windows.has_key(i): + self.stat_windows[i].relocate(x, y) + else: + self.stat_windows[i] = Stat_Window(game = config.supported_games[self.poker_game], + parent = self, + table = self.table, + x = x, + y = y, + seat = i, + adj = adj[i], + player_id = 'fake', + font = self.font) + + self.stats = [] + for i in range(0, config.supported_games[self.poker_game].rows + 1): + row_list = [''] * config.supported_games[self.poker_game].cols + self.stats.append(row_list) + for stat in config.supported_games[self.poker_game].stats.keys(): + self.stats[config.supported_games[self.poker_game].stats[stat].row] \ + [config.supported_games[self.poker_game].stats[stat].col] = \ + config.supported_games[self.poker_game].stats[stat].stat_name + + game_params = config.get_game_parameters(self.poker_game) + if not game_params['aux'] == "": + aux_params = config.get_aux_parameters(game_params['aux']) + self.aux_windows.append(eval("%s.%s(gtk.Window(), config, 'fpdb')" % (aux_params['module'], aux_params['class']))) + + def update(self, hand, config, stat_dict): + self.hand = hand # this is the last hand, so it is available later + for s in stat_dict.keys(): + try: + self.stat_windows[stat_dict[s]['seat']].player_id = stat_dict[s]['player_id'] + except: # omg, we have more seats than stat windows .. damn poker sites with incorrect max seating info .. let's force 10 here + self.max = 10 + self.create(hand, config) + self.stat_windows[stat_dict[s]['seat']].player_id = stat_dict[s]['player_id'] + + for r in range(0, config.supported_games[self.poker_game].rows): + for c in range(0, config.supported_games[self.poker_game].cols): + this_stat = config.supported_games[self.poker_game].stats[self.stats[r][c]] + number = Stats.do_stat(stat_dict, player = stat_dict[s]['player_id'], stat = self.stats[r][c]) + statstring = this_stat.hudprefix + str(number[1]) + this_stat.hudsuffix + self.stat_windows[stat_dict[s]['seat']].label[r][c].set_text(statstring) + tip = stat_dict[s]['screen_name'] + "\n" + number[5] + "\n" + \ + number[3] + ", " + number[4] + Stats.do_tip(self.stat_windows[stat_dict[s]['seat']].e_box[r][c], tip) +# for m in self.aux_windows: +# m.update_data(hand) +# m.update_gui(hand) + + def topify_window(self, window): + """Set the specified gtk window to stayontop in MS Windows.""" + + def windowEnumerationHandler(hwnd, resultList): + '''Callback for win32gui.EnumWindows() to generate list of window handles.''' + resultList.append((hwnd, win32gui.GetWindowText(hwnd))) + + unique_name = 'unique name for finding this window' + real_name = window.get_title() + window.set_title(unique_name) + tl_windows = [] + win32gui.EnumWindows(windowEnumerationHandler, tl_windows) + + for w in tl_windows: + if w[1] == unique_name: + #win32gui.ShowWindow(w[0], win32con.SW_HIDE) + window.parentgdkhandle = gtk.gdk.window_foreign_new(long(self.table.number)) + self.main_window.gdkhandle = gtk.gdk.window_foreign_new(w[0]) + self.main_window.gdkhandle.set_transient_for(window.parentgdkhandle) + #win32gui.ShowWindow(w[0], win32con.SW_SHOW) + + style = win32gui.GetWindowLong(self.table.number, win32con.GWL_EXSTYLE) + #style |= win32con.WS_EX_TOOLWINDOW + #style &= ~win32con.WS_EX_APPWINDOW + style |= win32con.WS_CLIPCHILDREN + win32gui.SetWindowLong(self.table.number, win32con.GWL_EXSTYLE, style) + + + #win32gui.SetWindowPos(w[0], win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE|win32con.SWP_NOSIZE) + +# notify_id = (w[0], +# 0, +# win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP, +# win32con.WM_USER+20, +# 0, +# '') +# win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, notify_id) +# + window.set_title(real_name) + +class Stat_Window: + + def button_press_cb(self, widget, event, *args): +# This handles all callbacks from button presses on the event boxes in +# the stat windows. There is a bit of an ugly kludge to separate single- +# and double-clicks. + + if event.button == 3: # right button event + if event.type == gtk.gdk.BUTTON_PRESS: # left button single click + if self.sb_click > 0: return + self.sb_click = gobject.timeout_add(250, self.single_click, widget) + elif event.type == gtk.gdk._2BUTTON_PRESS: # left button double click + if self.sb_click > 0: + gobject.source_remove(self.sb_click) + self.sb_click = 0 + self.double_click(widget, event, *args) + + if event.button == 2: # middle button event +# print "middle button clicked" + pass + + if event.button == 1: # left button event + if event.state & gtk.gdk.SHIFT_MASK: + self.window.begin_resize_drag(gtk.gdk.WINDOW_EDGE_SOUTH_EAST, event.button, int(event.x_root), int(event.y_root), event.time) + else: + self.window.begin_move_drag(event.button, int(event.x_root), int(event.y_root), event.time) + + def single_click(self, widget): +# Callback from the timeout in the single-click finding part of the +# button press call back. This needs to be modified to get all the +# arguments from the call. +# print "left button clicked" + self.sb_click = 0 + Popup_window(widget, self) + return False + + def double_click(self, widget, event, *args): + self.toggle_decorated(widget) + + def toggle_decorated(self, widget): + top = widget.get_toplevel() + (x, y) = top.get_position() + + if top.get_decorated(): + top.set_decorated(0) + top.move(x, y) + else: + top.set_decorated(1) + top.move(x, y) + + def relocate(self, x, y): + self.x = x + self.table.x + self.y = y + self.table.y + self.window.move(self.x, self.y) + + def __init__(self, parent, game, table, seat, adj, x, y, player_id, font): + self.parent = parent # Hud object that this stat window belongs to + self.game = game # Configuration object for the curren + self.table = table # Table object where this is going + self.seat = seat # seat number of his player + self.adj = adj # the adjusted seat number for this player + self.x = x + table.x # table.x and y are the location of the table + self.y = y + table.y # x and y are the location relative to table.x & y + self.player_id = player_id # looks like this isn't used ;) + self.sb_click = 0 # used to figure out button clicks + + self.window = gtk.Window() + self.window.set_decorated(0) + self.window.set_gravity(gtk.gdk.GRAVITY_STATIC) + + self.window.set_title("%s" % seat) + self.window.set_property("skip-taskbar-hint", True) + self.window.set_transient_for(parent.main_window) + + self.grid = gtk.Table(rows = self.game.rows, columns = self.game.cols, homogeneous = False) + self.window.add(self.grid) + + self.e_box = [] + self.frame = [] + self.label = [] + for r in range(self.game.rows): + self.e_box.append([]) + self.label.append([]) + for c in range(self.game.cols): + self.e_box[r].append( gtk.EventBox() ) + + self.e_box[r][c].modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(parent.colors['hudbgcolor'])) + self.e_box[r][c].modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(parent.colors['hudfgcolor'])) + + Stats.do_tip(self.e_box[r][c], 'farts') + self.grid.attach(self.e_box[r][c], c, c+1, r, r+1, xpadding = 0, ypadding = 0) + self.label[r].append( gtk.Label('xxx') ) + + self.label[r][c].modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(parent.colors['hudbgcolor'])) + self.label[r][c].modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(parent.colors['hudfgcolor'])) + + self.e_box[r][c].add(self.label[r][c]) + self.e_box[r][c].connect("button_press_event", self.button_press_cb) +# font = pango.FontDescription("Sans 8") + self.label[r][c].modify_font(font) + +# if not os.name == 'nt': # seems to be a bug in opacity on windows + self.window.set_opacity(parent.colors['hudopacity']) + + self.window.realize + self.window.move(self.x, self.y) + self.window.show_all() +# set_keep_above(1) for windows + if os.name == 'nt': self.topify_window(self.window) + + def topify_window(self, window): + """Set the specified gtk window to stayontop in MS Windows.""" + + def windowEnumerationHandler(hwnd, resultList): + '''Callback for win32gui.EnumWindows() to generate list of window handles.''' + resultList.append((hwnd, win32gui.GetWindowText(hwnd))) + + unique_name = 'unique name for finding this window' + real_name = window.get_title() + window.set_title(unique_name) + tl_windows = [] + win32gui.EnumWindows(windowEnumerationHandler, tl_windows) + + for w in tl_windows: + if w[1] == unique_name: + + #win32gui.SetWindowPos(w[0], win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE|win32con.SWP_NOSIZE) + +# style = win32gui.GetWindowLong(w[0], win32con.GWL_EXSTYLE) +# style |= win32con.WS_EX_TOOLWINDOW +# style &= ~win32con.WS_EX_APPWINDOW +# win32gui.SetWindowLong(w[0], win32con.GWL_EXSTYLE, style) + win32gui.ShowWindow(w[0], win32con.SW_SHOW) + window.set_title(real_name) + +def destroy(*args): # call back for terminating the main eventloop + gtk.main_quit() + +class Popup_window: + def __init__(self, parent, stat_window): + self.sb_click = 0 + +# create the popup window + self.window = gtk.Window() + self.window.set_decorated(0) + self.window.set_gravity(gtk.gdk.GRAVITY_STATIC) +# self.window.set_keep_above(1) + self.window.set_title("popup") + self.window.set_property("skip-taskbar-hint", True) + self.window.set_transient_for(parent.get_toplevel()) + + self.window.set_position(gtk.WIN_POS_CENTER_ON_PARENT) + + self.ebox = gtk.EventBox() + self.ebox.connect("button_press_event", self.button_press_cb) + self.lab = gtk.Label("stuff\nstuff\nstuff") + +# need an event box so we can respond to clicks + self.window.add(self.ebox) + self.ebox.add(self.lab) + + self.ebox.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(stat_window.parent.colors['hudbgcolor'])) + self.ebox.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(stat_window.parent.colors['hudfgcolor'])) + self.window.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(stat_window.parent.colors['hudbgcolor'])) + self.window.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(stat_window.parent.colors['hudfgcolor'])) + self.lab.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(stat_window.parent.colors['hudbgcolor'])) + self.lab.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(stat_window.parent.colors['hudfgcolor'])) + + self.window.realize + +# figure out the row, col address of the click that activated the popup + row = 0 + col = 0 + for r in range(0, stat_window.game.rows): + for c in range(0, stat_window.game.cols): + if stat_window.e_box[r][c] == parent: + row = r + col = c + break + +# figure out what popup format we're using + popup_format = "default" + for stat in stat_window.game.stats.keys(): + if stat_window.game.stats[stat].row == row and stat_window.game.stats[stat].col == col: + popup_format = stat_window.game.stats[stat].popup + break + +# get the list of stats to be presented from the config + stat_list = [] + for w in stat_window.parent.config.popup_windows.keys(): + if w == popup_format: + stat_list = stat_window.parent.config.popup_windows[w].pu_stats + break + +# get a database connection + db_connection = Database.Database(stat_window.parent.config, stat_window.parent.db_name, 'temp') + +# calculate the stat_dict and then create the text for the pu +# stat_dict = db_connection.get_stats_from_hand(stat_window.parent.hand, stat_window.player_id) + stat_dict = db_connection.get_stats_from_hand(stat_window.parent.hand) + db_connection.close_connection() + + pu_text = "" + for s in stat_list: + number = Stats.do_stat(stat_dict, player = int(stat_window.player_id), stat = s) + pu_text += number[3] + "\n" + + self.lab.set_text(pu_text) + self.window.show_all() + + self.window.set_transient_for(stat_window.window) + +# set_keep_above(1) for windows + if os.name == 'nt': self.topify_window(self.window) + + def button_press_cb(self, widget, event, *args): +# This handles all callbacks from button presses on the event boxes in +# the popup windows. There is a bit of an ugly kludge to separate single- +# and double-clicks. This is the same code as in the Stat_window class + if event.button == 1: # left button event + if event.type == gtk.gdk.BUTTON_PRESS: # left button single click + if self.sb_click > 0: return + self.sb_click = gobject.timeout_add(250, self.single_click, widget) + elif event.type == gtk.gdk._2BUTTON_PRESS: # left button double click + if self.sb_click > 0: + gobject.source_remove(self.sb_click) + self.sb_click = 0 + self.double_click(widget, event, *args) + + if event.button == 2: # middle button event + pass +# print "middle button clicked" + + if event.button == 3: # right button event + pass +# print "right button clicked" + + def single_click(self, widget): +# Callback from the timeout in the single-click finding part of the +# button press call back. This needs to be modified to get all the +# arguments from the call. + self.sb_click = 0 + self.window.destroy() + return False + + def double_click(self, widget, event, *args): + self.toggle_decorated(widget) + + def toggle_decorated(self, widget): + top = widget.get_toplevel() + (x, y) = top.get_position() + + if top.get_decorated(): + top.set_decorated(0) + top.move(x, y) + else: + top.set_decorated(1) + top.move(x, y) + + def topify_window(self, window): + """Set the specified gtk window to stayontop in MS Windows.""" + + def windowEnumerationHandler(hwnd, resultList): + '''Callback for win32gui.EnumWindows() to generate list of window handles.''' + resultList.append((hwnd, win32gui.GetWindowText(hwnd))) + + unique_name = 'unique name for finding this window' + real_name = window.get_title() + window.set_title(unique_name) + tl_windows = [] + win32gui.EnumWindows(windowEnumerationHandler, tl_windows) + + for w in tl_windows: + if w[1] == unique_name: +# win32gui.ShowWindow(w[0], win32con.SW_HIDE) +# style = win32gui.GetWindowLong(w[0], win32con.GWL_EXSTYLE) +# style |= win32con.WS_EX_TOOLWINDOW +# style &= ~win32con.WS_EX_APPWINDOW +# win32gui.SetWindowLong(w[0], win32con.GWL_EXSTYLE, style) +# win32gui.ShowWindow(w[0], win32con.SW_SHOW) + win32gui.SetWindowPos(w[0], win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE|win32con.SWP_NOSIZE) + +# notify_id = (w[0], +# 0, +# win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP, +# win32con.WM_USER+20, +# 0, +# '') +# win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, notify_id) +# + window.set_title(real_name) + +if __name__== "__main__": + main_window = gtk.Window() + main_window.connect("destroy", destroy) + label = gtk.Label('Fake main window, blah blah, blah\nblah, blah') + main_window.add(label) + main_window.show_all() + + c = Configuration.Config() + #tables = Tables.discover(c) + t = Tables.discover_table_by_name(c, "Chelsea") + if t is None: + print "Table not found." + db = Database.Database(c, 'fpdb', 'holdem') + +# for t in tables: + win = Hud(t, 10, 'holdem', c, db) + win.create(1, c) +# t.get_details() + win.update(8300, db, c) + + gtk.main() diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index 9bc125f6..7ff0b18c 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -229,14 +229,21 @@ class Sql: sum(street3CheckCallRaiseDone) AS ccr_3, sum(street4CheckCallRaiseChance) AS ccr_opp_4, sum(street4CheckCallRaiseDone) AS ccr_4 - FROM HudCache, Hands - WHERE HudCache.PlayerId in - (SELECT PlayerId FROM HandsPlayers - WHERE handId = %s) - AND Hands.id = %s - AND Hands.gametypeId = HudCache.gametypeId + FROM Hands + INNER JOIN HandsPlayers ON (HandsPlayers.handId = %s) + INNER JOIN HudCache ON ( HudCache.PlayerId = HandsPlayers.PlayerId+0 + AND HudCache.gametypeId+0 = Hands.gametypeId+0) + WHERE Hands.id = %s GROUP BY HudCache.PlayerId """ + +# FROM HudCache, Hands +# WHERE HudCache.PlayerId in +# (SELECT PlayerId FROM HandsPlayers +# WHERE handId = %s) +# AND Hands.id = %s +# AND Hands.gametypeId = HudCache.gametypeId + # AND PlayerId LIKE %s # HudCache.gametypeId AS gametypeId, # activeSeats AS n_active, diff --git a/pyfpdb/Stats.py b/pyfpdb/Stats.py index 7bb9a8c5..50a502e3 100644 --- a/pyfpdb/Stats.py +++ b/pyfpdb/Stats.py @@ -71,6 +71,7 @@ def do_stat(stat_dict, player = 24, stat = 'vpip'): # functions that return individual stats def playername(stat_dict, player): + """ Player Name.""" return (stat_dict[player]['screen_name'], stat_dict[player]['screen_name'], stat_dict[player]['screen_name'], @@ -98,6 +99,26 @@ def vpip(stat_dict, player): 'wtsd' ) +def vpip_0(stat_dict, player): + """ Voluntarily put $ in the pot (no decimals).""" + stat = 0.0 + try: + stat = float(stat_dict[player]['vpip'])/float(stat_dict[player]['n']) + return (stat, + '%2.0f' % (100*stat) + '%', + 'v=%2.0f' % (100*stat) + '%', + 'vpip=%2.0f' % (100*stat) + '%', + '(%d/%d)' % (stat_dict[player]['vpip'], stat_dict[player]['n']), + 'vpip' + ) + except: return (stat, + '%2.0f' % (0) + '%', + 'w=%2.0f' % (0) + '%', + 'wtsd=%2.0f' % (0) + '%', + '(%d/%d)' % (0, 0), + 'wtsd' + ) + def pfr(stat_dict, player): """ Preflop (3rd street) raise.""" stat = 0.0 @@ -119,6 +140,27 @@ def pfr(stat_dict, player): 'pfr' ) +def pfr_0(stat_dict, player): + """ Preflop (3rd street) raise (no decimals).""" + stat = 0.0 + try: + stat = float(stat_dict[player]['pfr'])/float(stat_dict[player]['n']) + return (stat, + '%2.0f' % (100*stat) + '%', + 'p=%2.0f' % (100*stat) + '%', + 'pfr=%2.0f' % (100*stat) + '%', + '(%d/%d)' % (stat_dict[player]['pfr'], stat_dict[player]['n']), + 'pfr' + ) + except: + return (stat, + '%2.0f' % (0) + '%', + 'p=%2.0f' % (0) + '%', + 'pfr=%2.0f' % (0) + '%', + '(%d/%d)' % (0, 0), + 'pfr' + ) + def wtsd(stat_dict, player): """ Went to SD when saw flop/4th.""" stat = 0.0 @@ -149,7 +191,7 @@ def wmsd(stat_dict, player): '%3.1f' % (100*stat) + '%', 'w=%3.1f' % (100*stat) + '%', 'wmsd=%3.1f' % (100*stat) + '%', - '(%f5.0/%d)' % (stat_dict[player]['wmsd'], stat_dict[player]['sd']), + '(%5.1f/%d)' % (float(stat_dict[player]['wmsd']), stat_dict[player]['sd']), '% won money at showdown' ) except: @@ -414,6 +456,61 @@ def a_freq_4(stat_dict, player): '(%d/%d)' % (0, 0), 'Aggression Freq flop/4th' ) + +def a_freq_123(stat_dict, player): + """ Post-Flop aggression frequency.""" + stat = 0.0 + try: + stat = float( stat_dict[player]['aggr_1'] + stat_dict[player]['aggr_2'] + stat_dict[player]['aggr_3'] + ) / float( stat_dict[player]['saw_1'] + stat_dict[player]['saw_2'] + stat_dict[player]['saw_3']); + return (stat, + '%3.1f' % (100*stat) + '%', + 'afq=%3.1f' % (100*stat) + '%', + 'postf_aggfq=%3.1f' % (100*stat) + '%', + '(%d/%d)' % ( stat_dict[player]['aggr_1'] + + stat_dict[player]['aggr_2'] + + stat_dict[player]['aggr_3'] + , stat_dict[player]['saw_1'] + + stat_dict[player]['saw_2'] + + stat_dict[player]['saw_3'] + ), + 'Post-Flop Aggression Freq' + ) + except: + return (stat, + '%2.0f' % (0) + '%', + 'a3=%2.0f' % (0) + '%', + 'a_fq_3=%2.0f' % (0) + '%', + '(%d/%d)' % (0, 0), + 'Post-Flop Aggression Freq' + ) + +def a_freq_123_0(stat_dict, player): + """ Post-Flop aggression frequency (no decimals).""" + stat = 0.0 + try: + stat = float( stat_dict[player]['aggr_1'] + stat_dict[player]['aggr_2'] + stat_dict[player]['aggr_3']) / float( stat_dict[player]['saw_1'] + stat_dict[player]['saw_2'] + stat_dict[player]['saw_3']); + return (stat, + '%2.0f' % (100*stat) + '%', + 'afq=%2.0f' % (100*stat) + '%', + 'postf_aggfq=%2.0f' % (100*stat) + '%', + '(%d/%d)' % ( stat_dict[player]['aggr_1'] + + stat_dict[player]['aggr_2'] + + stat_dict[player]['aggr_3'] + , stat_dict[player]['saw_1'] + + stat_dict[player]['saw_2'] + + stat_dict[player]['saw_3'] + ), + 'Post-Flop Aggression Freq' + ) + except: + return (stat, + '%2.0f' % (0) + '%', + 'a3=%2.0f' % (0) + '%', + 'a_fq_3=%2.0f' % (0) + '%', + '(%d/%d)' % (0, 0), + 'Post-Flop Aggression Freq' + ) def cb_1(stat_dict, player): """ Flop continuation bet.""" @@ -591,7 +688,9 @@ if __name__== "__main__": for player in stat_dict.keys(): print "player = ", player, do_stat(stat_dict, player = player, stat = 'vpip') + print "player = ", player, do_stat(stat_dict, player = player, stat = 'vpip_0') print "player = ", player, do_stat(stat_dict, player = player, stat = 'pfr') + print "player = ", player, do_stat(stat_dict, player = player, stat = 'pfr_0') print "player = ", player, do_stat(stat_dict, player = player, stat = 'wtsd') print "player = ", player, do_stat(stat_dict, player = player, stat = 'saw_f') print "player = ", player, do_stat(stat_dict, player = player, stat = 'n') @@ -606,6 +705,8 @@ if __name__== "__main__": print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_2') print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_3') print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_4') + print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_123') + print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_123_0') print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb_1') print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb_2') print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb_3') @@ -613,7 +714,8 @@ if __name__== "__main__": print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq_1') print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq_2') print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq_3') - print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq_4') + print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq_4') + print "\n" print "\n\nLegal stats:" for attr in dir(): @@ -621,8 +723,8 @@ if __name__== "__main__": if attr in ("Configuration", "Database", "GInitiallyUnowned", "gtk", "pygtk", "player", "c", "db_connection", "do_stat", "do_tip", "stat_dict", "h"): continue - print attr, eval("%s.__doc__" % (attr)) + print "%-14s %s" % (attr, eval("%s.__doc__" % (attr))) # print " " % (attr) - db_connection.close + db_connection.close_connection diff --git a/pyfpdb/fpdb.py b/pyfpdb/fpdb.py old mode 100755 new mode 100644 index bec9be78..8b4af06f --- a/pyfpdb/fpdb.py +++ b/pyfpdb/fpdb.py @@ -1,449 +1,449 @@ -#!/usr/bin/python - -#Copyright 2008 Steffen Jobbagy-Felso -#This program is free software: you can redistribute it and/or modify -#it under the terms of the GNU Affero General Public License as published by -#the Free Software Foundation, version 3 of the License. -# -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -# -#You should have received a copy of the GNU Affero General Public License -#along with this program. If not, see . -#In the "official" distribution you can find the license in -#agpl-3.0.txt in the docs folder of the package. - -import os -import sys -from optparse import OptionParser - - -parser = OptionParser() -parser.add_option("-x", "--errorsToConsole", action="store_true", - help="If passed error output will go to the console rather than .") -(options, sys.argv) = parser.parse_args() - -if not options.errorsToConsole: - print "Note: error output is being diverted to fpdb-error-log.txt and HUD-error.txt. Any major error will be reported there _only_." - errorFile = open('fpdb-error-log.txt', 'w', 0) - sys.stderr = errorFile - -import pygtk -pygtk.require('2.0') -import gtk - -import fpdb_db -import fpdb_simple -import GuiBulkImport -import GuiTableViewer -import GuiAutoImport -import GuiGraphViewer -import FpdbSQLQueries -import Configuration - -class fpdb: - def tab_clicked(self, widget, tab_name): - """called when a tab button is clicked to activate that tab""" - #print "start of tab_clicked" - self.display_tab(tab_name) - #end def tab_clicked - - def add_and_display_tab(self, new_tab, new_tab_name): - """just calls the component methods""" - self.add_tab(new_tab, new_tab_name) - self.display_tab(new_tab_name) - #end def add_and_display_tab - - def add_tab(self, new_tab, new_tab_name): - """adds a tab, namely creates the button and displays it and appends all the relevant arrays""" - #print "start of add_tab" - for i in self.tab_names: #todo: check this is valid - if i==new_tab_name: - raise fpdb_simple.FpdbError("duplicate tab_name not permitted") - - self.tabs.append(new_tab) - self.tab_names.append(new_tab_name) - - new_tab_sel_button=gtk.ToggleButton(new_tab_name) - new_tab_sel_button.connect("clicked", self.tab_clicked, new_tab_name) - self.tab_box.add(new_tab_sel_button) - new_tab_sel_button.show() - self.tab_buttons.append(new_tab_sel_button) - #end def add_tab - - def display_tab(self, new_tab_name): - """displays the indicated tab""" - #print "start of display_tab, len(self.tab_names):",len(self.tab_names) - tab_no=-1 - #if len(self.tab_names)>1: - for i in range(len(self.tab_names)): - #print "display_tab, new_tab_name:",new_tab_name," self.tab_names[i]:", self.tab_names[i] - if (new_tab_name==self.tab_names[i]): - tab_no=i - #self.tab_buttons[i].set_active(False) - #else: - # tab_no=0 - - #current_tab_no=-1 - for i in range(len(self.tab_names)): - if self.current_tab==self.tabs[i]: - #self.tab_buttons[i].set_active(False) - pass - - if tab_no==-1: - raise fpdb_simple.FpdbError("invalid tab_no") - else: - self.main_vbox.remove(self.current_tab) - #self.current_tab.destroy() - self.current_tab=self.tabs[tab_no] - self.main_vbox.add(self.current_tab) - self.tab_buttons[tab_no].set_active(True) - self.current_tab.show() - #end def display_tab - - def delete_event(self, widget, event, data=None): - return False - #end def delete_event - - def destroy(self, widget, data=None): - self.quit(widget, data) - #end def destroy - - def dia_about(self, widget, data): - print "todo: implement dia_about" - #end def dia_about - - def dia_create_del_database(self, widget, data): - print "todo: implement dia_create_del_database" - obtain_global_lock() - #end def dia_create_del_database - - def dia_create_del_user(self, widget, data): - print "todo: implement dia_create_del_user" - obtain_global_lock() - #end def dia_create_del_user - - def dia_database_stats(self, widget, data): - print "todo: implement dia_database_stats" - #string=fpdb_db.getDbStats(db, cursor) - #end def dia_database_stats - - def dia_delete_db_parts(self, widget, data): - print "todo: implement dia_delete_db_parts" - obtain_global_lock() - #end def dia_delete_db_parts - - def dia_edit_profile(self, widget=None, data=None, create_default=False, path=None): - print "todo: implement dia_edit_profile" - obtain_global_lock() - #end def dia_edit_profile - - def dia_export_db(self, widget, data): - print "todo: implement dia_export_db" - obtain_global_lock() - #end def dia_export_db - - def dia_get_db_root_credentials(self): - """obtains db root credentials from user""" - print "todo: implement dia_get_db_root_credentials" -# user, pw=None, None -# -# dialog=gtk.Dialog(title="DB Credentials needed", parent=None, flags=0, -# buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,"Connect and recreate",gtk.RESPONSE_OK)) -# -# label_warning1=gtk.Label("Please enter credentials for a database user for "+self.host+" that has permissions to create a database.") -# -# -# label_user=gtk.Label("Username") -# dialog.vbox.add(label_user) -# label_user.show() -# -# response=dialog.run() -# dialog.destroy() -# return (user, pw, response) - #end def dia_get_db_root_credentials - - def dia_import_db(self, widget, data): - print "todo: implement dia_import_db" - obtain_global_lock() - #end def dia_import_db - - def dia_licensing(self, widget, data): - print "todo: implement dia_licensing" - #end def dia_licensing - - def dia_load_profile(self, widget, data): - """Dialogue to select a file to load a profile from""" - self.obtain_global_lock() - chooser = gtk.FileChooserDialog(title="Please select a profile file to load", - action=gtk.FILE_CHOOSER_ACTION_OPEN, - buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK)) - chooser.set_filename(self.profile) - - response = chooser.run() - chooser.destroy() - if response == gtk.RESPONSE_OK: - self.load_profile(chooser.get_filename()) - elif response == gtk.RESPONSE_CANCEL: - print 'User cancelled loading profile' - #end def dia_load_profile - - def dia_recreate_tables(self, widget, data): - """Dialogue that asks user to confirm that he wants to delete and recreate the tables""" - self.obtain_global_lock() - dia_confirm=gtk.MessageDialog(parent=None, flags=0, type=gtk.MESSAGE_WARNING, - buttons=(gtk.BUTTONS_YES_NO), message_format="Confirm deleting and recreating tables") - diastring=("Please confirm that you want to (re-)create the tables. If there already are tables in the database "+self.db.database+" on "+self.db.host+" they will be deleted.") - dia_confirm.format_secondary_text(diastring)#todo: make above string with bold for db, host and deleted - - response=dia_confirm.run() - dia_confirm.destroy() - if response == gtk.RESPONSE_YES: - self.db.recreate_tables() - elif response == gtk.RESPONSE_NO: - print 'User cancelled recreating tables' - #end def dia_recreate_tables - - def dia_regression_test(self, widget, data): - print "todo: implement dia_regression_test" - self.obtain_global_lock() - #end def dia_regression_test - - def dia_save_profile(self, widget, data): - print "todo: implement dia_save_profile" - #end def dia_save_profile - - def diaSetupWizard(self, path): - print "todo: implement setup wizard" - print "setup wizard not implemented - please create the default configuration file:", path - diaSetupWizard = gtk.Dialog(title="Fatal Error - Config File Missing", parent=None, flags=0, buttons=(gtk.STOCK_QUIT,gtk.RESPONSE_OK)) - - label = gtk.Label("Please copy the config file from the docs folder to:") - diaSetupWizard.vbox.add(label) - label.show() - - label = gtk.Label(path) - diaSetupWizard.vbox.add(label) - label.show() - - label = gtk.Label("and edit it according to the install documentation at http://fpdb.sourceforge.net") - diaSetupWizard.vbox.add(label) - label.show() - - response = diaSetupWizard.run() - sys.exit(1) - #end def diaSetupWizard - - def get_menu(self, window): - """returns the menu for this program""" - accel_group = gtk.AccelGroup() - self.item_factory = gtk.ItemFactory(gtk.MenuBar, "
", accel_group) - self.item_factory.create_items(self.menu_items) - window.add_accel_group(accel_group) - return self.item_factory.get_widget("
") - #end def get_menu - - def load_profile(self): - """Loads profile from the provided path name.""" - self.settings = {} - if (os.sep=="/"): - self.settings['os']="linuxmac" - else: - self.settings['os']="windows" - - self.settings.update(self.config.get_db_parameters()) - self.settings.update(self.config.get_tv_parameters()) - self.settings.update(self.config.get_import_parameters()) - self.settings.update(self.config.get_default_paths()) - - if self.db!=None: - self.db.disconnect() - - self.db = fpdb_db.fpdb_db() - #print "end of fpdb.load_profile, databaseName:",self.settings['db-databaseName'] - self.db.connect(self.settings['db-backend'], self.settings['db-host'], self.settings['db-databaseName'], self.settings['db-user'], self.settings['db-password']) - if self.db.wrongDbVersion: - diaDbVersionWarning = gtk.Dialog(title="Strong Warning - Invalid database version", parent=None, flags=0, buttons=(gtk.STOCK_OK,gtk.RESPONSE_OK)) - - label = gtk.Label("An invalid DB version or missing tables have been detected.") - diaDbVersionWarning.vbox.add(label) - label.show() - - label = gtk.Label("This error is not necessarily fatal but it is strongly recommended that you recreate the tables by using the Database menu.") - diaDbVersionWarning.vbox.add(label) - label.show() - - label = gtk.Label("Not doing this will likely lead to misbehaviour including fpdb crashes, corrupt data etc.") - diaDbVersionWarning.vbox.add(label) - label.show() - - response = diaDbVersionWarning.run() - diaDbVersionWarning.destroy() - - # Database connected to successfully, load queries to pass on to other classes - self.querydict = FpdbSQLQueries.FpdbSQLQueries(self.db.get_backend_name()) - #end def load_profile - - def not_implemented(self): - print "todo: called unimplemented menu entry (users: pls ignore this)"#remove this once more entries are implemented - #end def not_implemented - - def obtain_global_lock(self): - print "todo: implement obtain_global_lock (users: pls ignore this)" - #end def obtain_global_lock - - def quit(self, widget, data): - print "Quitting normally" - #check if current settings differ from profile, if so offer to save or abort - self.db.disconnect() - gtk.main_quit() - #end def quit_cliecked - - def release_global_lock(self): - print "todo: implement release_global_lock" - #end def release_global_lock - - def tab_abbreviations(self, widget, data): - print "todo: implement tab_abbreviations" - #end def tab_abbreviations - - def tab_auto_import(self, widget, data): - """opens the auto import tab""" - new_aimp_thread=GuiAutoImport.GuiAutoImport(self.settings, self.config) - self.threads.append(new_aimp_thread) - aimp_tab=new_aimp_thread.get_vbox() - self.add_and_display_tab(aimp_tab, "Auto Import") - #end def tab_auto_import - - def tab_bulk_import(self, widget, data): - """opens a tab for bulk importing""" - #print "start of tab_bulk_import" - new_import_thread=GuiBulkImport.GuiBulkImport(self.db, self.settings, self.config) - self.threads.append(new_import_thread) - bulk_tab=new_import_thread.get_vbox() - self.add_and_display_tab(bulk_tab, "Bulk Import") - #end def tab_bulk_import - - def tab_main_help(self, widget, data): - """Displays a tab with the main fpdb help screen""" - #print "start of tab_main_help" - mh_tab=gtk.Label("""Welcome to Fpdb! -For documentation please visit our website at http://fpdb.sourceforge.net/ or check the docs directory in the fpdb folder. -Please note that default.conf is no longer needed nor used, all configuration now happens in HUD_config.xml -This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt") - self.add_and_display_tab(mh_tab, "Help") - #end def tab_main_help - - def tab_table_viewer(self, widget, data): - """opens a table viewer tab""" - #print "start of tab_table_viewer" - new_tv_thread=GuiTableViewer.GuiTableViewer(self.db, self.settings) - self.threads.append(new_tv_thread) - tv_tab=new_tv_thread.get_vbox() - self.add_and_display_tab(tv_tab, "Table Viewer") - #end def tab_table_viewer - - def tabGraphViewer(self, widget, data): - """opens a graph viewer tab""" - #print "start of tabGraphViewer" - new_gv_thread=GuiGraphViewer.GuiGraphViewer(self.db, self.settings, self.querydict, self.config) - self.threads.append(new_gv_thread) - gv_tab=new_gv_thread.get_vbox() - self.add_and_display_tab(gv_tab, "Graphs") - #end def tabGraphViewer - - def __init__(self): - self.threads=[] - self.db=None - self.config = Configuration.Config() - self.load_profile() - - self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) - self.window.connect("delete_event", self.delete_event) - self.window.connect("destroy", self.destroy) - self.window.set_title("Free Poker DB - version: alpha9+, p143 or higher") - self.window.set_border_width(1) - self.window.set_size_request(1020,400) - self.window.set_resizable(True) - - self.menu_items = ( - ( "/_Main", None, None, 0, "" ), - ( "/Main/_Load Profile (broken)", "L", self.dia_load_profile, 0, None ), - ( "/Main/_Edit Profile (todo)", "E", self.dia_edit_profile, 0, None ), - ( "/Main/_Save Profile (todo)", None, self.dia_save_profile, 0, None ), - ("/Main/sep1", None, None, 0, "" ), - ("/Main/_Quit", "Q", self.quit, 0, None ), - ("/_Import", None, None, 0, "" ), - ("/Import/_Bulk Import", "B", self.tab_bulk_import, 0, None ), - ("/Import/_Auto Import and HUD", "A", self.tab_auto_import, 0, None ), - ("/Import/Auto _Rating (todo)", "R", self.not_implemented, 0, None ), - ("/_Viewers", None, None, 0, "" ), - ("/_Viewers/_Auto Import and HUD", "A", self.tab_auto_import, 0, None ), - ("/Viewers/_Graphs", "G", self.tabGraphViewer, 0, None ), - ("/Viewers/Hand _Replayer (todo)", None, self.not_implemented, 0, None ), - ("/Viewers/Player _Details (todo)", None, self.not_implemented, 0, None ), - ("/Viewers/_Player Stats (tabulated view) (todo)", None, self.not_implemented, 0, None ), - ("/Viewers/Starting _Hands (todo)", None, self.not_implemented, 0, None ), - ("/Viewers/_Session Replayer (todo)", None, self.not_implemented, 0, None ), - ("/Viewers/Poker_table Viewer (mostly obselete)", "T", self.tab_table_viewer, 0, None ), - #( "/Viewers/Tourney Replayer - ( "/_Database", None, None, 0, "" ), - ( "/Database/Create or Delete _Database (todo)", None, self.dia_create_del_database, 0, None ), - ( "/Database/Create or Delete _User (todo)", None, self.dia_create_del_user, 0, None ), - ( "/Database/Create or Recreate _Tables", None, self.dia_recreate_tables, 0, None ), - ( "/Database/_Statistics (todo)", None, self.dia_database_stats, 0, None ), - ( "/D_ebugging", None, None, 0, "" ), - ( "/Debugging/_Delete Parts of Database (todo)", None, self.dia_delete_db_parts, 0, None ), - ( "/Debugging/_Export DB (todo)", None, self.dia_export_db, 0, None ), - ( "/Debugging/_Import DB (todo)", None, self.dia_import_db, 0, None ), - ( "/Debugging/_Regression test (todo)", None, self.dia_regression_test, 0, None ), - ( "/_Help", None, None, 0, "" ), - ( "/Help/_Main Help", "H", self.tab_main_help, 0, None ), - ( "/Help/_Abbrevations (todo)", None, self.tab_abbreviations, 0, None ), - ( "/Help/sep1", None, None, 0, "" ), - ( "/Help/A_bout (todo)", None, self.dia_about, 0, None ), - ( "/Help/_License and Copying (todo)", None, self.dia_licensing, 0, None ) - ) - - self.main_vbox = gtk.VBox(False, 1) - self.main_vbox.set_border_width(1) - self.window.add(self.main_vbox) - self.main_vbox.show() - - menubar = self.get_menu(self.window) - self.main_vbox.pack_start(menubar, False, True, 0) - menubar.show() - #done menubar - - self.tabs=[] - self.tab_names=[] - self.tab_buttons=[] - self.tab_box = gtk.HBox(False,1) - self.main_vbox.pack_start(self.tab_box, False, True, 0) - self.tab_box.show() - #done tab bar - - self.current_tab = gtk.VBox(False,1) - self.current_tab.set_border_width(1) - self.main_vbox.add(self.current_tab) - self.current_tab.show() - - self.tab_main_help(None, None) - - self.status_bar = gtk.Label("Status: Connected to "+self.db.get_backend_name()+" database named "+self.db.database+" on host "+self.db.host) - self.main_vbox.pack_end(self.status_bar, False, True, 0) - self.status_bar.show() - - self.window.show() - #end def __init__ - - def main(self): - gtk.main() - return 0 - #end def main - -if __name__ == "__main__": - me = fpdb() - me.main() +#!/usr/bin/python + +#Copyright 2008 Steffen Jobbagy-Felso +#This program is free software: you can redistribute it and/or modify +#it under the terms of the GNU Affero General Public License as published by +#the Free Software Foundation, version 3 of the License. +# +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +# +#You should have received a copy of the GNU Affero General Public License +#along with this program. If not, see . +#In the "official" distribution you can find the license in +#agpl-3.0.txt in the docs folder of the package. + +import os +import sys +from optparse import OptionParser + + +parser = OptionParser() +parser.add_option("-x", "--errorsToConsole", action="store_true", + help="If passed error output will go to the console rather than .") +(options, sys.argv) = parser.parse_args() + +if not options.errorsToConsole: + print "Note: error output is being diverted to fpdb-error-log.txt and HUD-error.txt. Any major error will be reported there _only_." + errorFile = open('fpdb-error-log.txt', 'w', 0) + sys.stderr = errorFile + +import pygtk +pygtk.require('2.0') +import gtk + +import fpdb_db +import fpdb_simple +import GuiBulkImport +import GuiTableViewer +import GuiAutoImport +import GuiGraphViewer +import FpdbSQLQueries +import Configuration + +class fpdb: + def tab_clicked(self, widget, tab_name): + """called when a tab button is clicked to activate that tab""" + #print "start of tab_clicked" + self.display_tab(tab_name) + #end def tab_clicked + + def add_and_display_tab(self, new_tab, new_tab_name): + """just calls the component methods""" + self.add_tab(new_tab, new_tab_name) + self.display_tab(new_tab_name) + #end def add_and_display_tab + + def add_tab(self, new_tab, new_tab_name): + """adds a tab, namely creates the button and displays it and appends all the relevant arrays""" + #print "start of add_tab" + for i in self.tab_names: #todo: check this is valid + if i==new_tab_name: + raise fpdb_simple.FpdbError("duplicate tab_name not permitted") + + self.tabs.append(new_tab) + self.tab_names.append(new_tab_name) + + new_tab_sel_button=gtk.ToggleButton(new_tab_name) + new_tab_sel_button.connect("clicked", self.tab_clicked, new_tab_name) + self.tab_box.add(new_tab_sel_button) + new_tab_sel_button.show() + self.tab_buttons.append(new_tab_sel_button) + #end def add_tab + + def display_tab(self, new_tab_name): + """displays the indicated tab""" + #print "start of display_tab, len(self.tab_names):",len(self.tab_names) + tab_no=-1 + #if len(self.tab_names)>1: + for i in range(len(self.tab_names)): + #print "display_tab, new_tab_name:",new_tab_name," self.tab_names[i]:", self.tab_names[i] + if (new_tab_name==self.tab_names[i]): + tab_no=i + #self.tab_buttons[i].set_active(False) + #else: + # tab_no=0 + + #current_tab_no=-1 + for i in range(len(self.tab_names)): + if self.current_tab==self.tabs[i]: + #self.tab_buttons[i].set_active(False) + pass + + if tab_no==-1: + raise fpdb_simple.FpdbError("invalid tab_no") + else: + self.main_vbox.remove(self.current_tab) + #self.current_tab.destroy() + self.current_tab=self.tabs[tab_no] + self.main_vbox.add(self.current_tab) + self.tab_buttons[tab_no].set_active(True) + self.current_tab.show() + #end def display_tab + + def delete_event(self, widget, event, data=None): + return False + #end def delete_event + + def destroy(self, widget, data=None): + self.quit(widget, data) + #end def destroy + + def dia_about(self, widget, data): + print "todo: implement dia_about" + #end def dia_about + + def dia_create_del_database(self, widget, data): + print "todo: implement dia_create_del_database" + obtain_global_lock() + #end def dia_create_del_database + + def dia_create_del_user(self, widget, data): + print "todo: implement dia_create_del_user" + obtain_global_lock() + #end def dia_create_del_user + + def dia_database_stats(self, widget, data): + print "todo: implement dia_database_stats" + #string=fpdb_db.getDbStats(db, cursor) + #end def dia_database_stats + + def dia_delete_db_parts(self, widget, data): + print "todo: implement dia_delete_db_parts" + obtain_global_lock() + #end def dia_delete_db_parts + + def dia_edit_profile(self, widget=None, data=None, create_default=False, path=None): + print "todo: implement dia_edit_profile" + obtain_global_lock() + #end def dia_edit_profile + + def dia_export_db(self, widget, data): + print "todo: implement dia_export_db" + obtain_global_lock() + #end def dia_export_db + + def dia_get_db_root_credentials(self): + """obtains db root credentials from user""" + print "todo: implement dia_get_db_root_credentials" +# user, pw=None, None +# +# dialog=gtk.Dialog(title="DB Credentials needed", parent=None, flags=0, +# buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,"Connect and recreate",gtk.RESPONSE_OK)) +# +# label_warning1=gtk.Label("Please enter credentials for a database user for "+self.host+" that has permissions to create a database.") +# +# +# label_user=gtk.Label("Username") +# dialog.vbox.add(label_user) +# label_user.show() +# +# response=dialog.run() +# dialog.destroy() +# return (user, pw, response) + #end def dia_get_db_root_credentials + + def dia_import_db(self, widget, data): + print "todo: implement dia_import_db" + obtain_global_lock() + #end def dia_import_db + + def dia_licensing(self, widget, data): + print "todo: implement dia_licensing" + #end def dia_licensing + + def dia_load_profile(self, widget, data): + """Dialogue to select a file to load a profile from""" + self.obtain_global_lock() + chooser = gtk.FileChooserDialog(title="Please select a profile file to load", + action=gtk.FILE_CHOOSER_ACTION_OPEN, + buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK)) + chooser.set_filename(self.profile) + + response = chooser.run() + chooser.destroy() + if response == gtk.RESPONSE_OK: + self.load_profile(chooser.get_filename()) + elif response == gtk.RESPONSE_CANCEL: + print 'User cancelled loading profile' + #end def dia_load_profile + + def dia_recreate_tables(self, widget, data): + """Dialogue that asks user to confirm that he wants to delete and recreate the tables""" + self.obtain_global_lock() + dia_confirm=gtk.MessageDialog(parent=None, flags=0, type=gtk.MESSAGE_WARNING, + buttons=(gtk.BUTTONS_YES_NO), message_format="Confirm deleting and recreating tables") + diastring=("Please confirm that you want to (re-)create the tables. If there already are tables in the database "+self.db.database+" on "+self.db.host+" they will be deleted.") + dia_confirm.format_secondary_text(diastring)#todo: make above string with bold for db, host and deleted + + response=dia_confirm.run() + dia_confirm.destroy() + if response == gtk.RESPONSE_YES: + self.db.recreate_tables() + elif response == gtk.RESPONSE_NO: + print 'User cancelled recreating tables' + #end def dia_recreate_tables + + def dia_regression_test(self, widget, data): + print "todo: implement dia_regression_test" + self.obtain_global_lock() + #end def dia_regression_test + + def dia_save_profile(self, widget, data): + print "todo: implement dia_save_profile" + #end def dia_save_profile + + def diaSetupWizard(self, path): + print "todo: implement setup wizard" + print "setup wizard not implemented - please create the default configuration file:", path + diaSetupWizard = gtk.Dialog(title="Fatal Error - Config File Missing", parent=None, flags=0, buttons=(gtk.STOCK_QUIT,gtk.RESPONSE_OK)) + + label = gtk.Label("Please copy the config file from the docs folder to:") + diaSetupWizard.vbox.add(label) + label.show() + + label = gtk.Label(path) + diaSetupWizard.vbox.add(label) + label.show() + + label = gtk.Label("and edit it according to the install documentation at http://fpdb.sourceforge.net") + diaSetupWizard.vbox.add(label) + label.show() + + response = diaSetupWizard.run() + sys.exit(1) + #end def diaSetupWizard + + def get_menu(self, window): + """returns the menu for this program""" + accel_group = gtk.AccelGroup() + self.item_factory = gtk.ItemFactory(gtk.MenuBar, "
", accel_group) + self.item_factory.create_items(self.menu_items) + window.add_accel_group(accel_group) + return self.item_factory.get_widget("
") + #end def get_menu + + def load_profile(self): + """Loads profile from the provided path name.""" + self.settings = {} + if (os.sep=="/"): + self.settings['os']="linuxmac" + else: + self.settings['os']="windows" + + self.settings.update(self.config.get_db_parameters()) + self.settings.update(self.config.get_tv_parameters()) + self.settings.update(self.config.get_import_parameters()) + self.settings.update(self.config.get_default_paths()) + + if self.db!=None: + self.db.disconnect() + + self.db = fpdb_db.fpdb_db() + #print "end of fpdb.load_profile, databaseName:",self.settings['db-databaseName'] + self.db.connect(self.settings['db-backend'], self.settings['db-host'], self.settings['db-databaseName'], self.settings['db-user'], self.settings['db-password']) + if self.db.wrongDbVersion: + diaDbVersionWarning = gtk.Dialog(title="Strong Warning - Invalid database version", parent=None, flags=0, buttons=(gtk.STOCK_OK,gtk.RESPONSE_OK)) + + label = gtk.Label("An invalid DB version or missing tables have been detected.") + diaDbVersionWarning.vbox.add(label) + label.show() + + label = gtk.Label("This error is not necessarily fatal but it is strongly recommended that you recreate the tables by using the Database menu.") + diaDbVersionWarning.vbox.add(label) + label.show() + + label = gtk.Label("Not doing this will likely lead to misbehaviour including fpdb crashes, corrupt data etc.") + diaDbVersionWarning.vbox.add(label) + label.show() + + response = diaDbVersionWarning.run() + diaDbVersionWarning.destroy() + + # Database connected to successfully, load queries to pass on to other classes + self.querydict = FpdbSQLQueries.FpdbSQLQueries(self.db.get_backend_name()) + #end def load_profile + + def not_implemented(self): + print "todo: called unimplemented menu entry (users: pls ignore this)"#remove this once more entries are implemented + #end def not_implemented + + def obtain_global_lock(self): + print "todo: implement obtain_global_lock (users: pls ignore this)" + #end def obtain_global_lock + + def quit(self, widget, data): + print "Quitting normally" + #check if current settings differ from profile, if so offer to save or abort + self.db.disconnect() + gtk.main_quit() + #end def quit_cliecked + + def release_global_lock(self): + print "todo: implement release_global_lock" + #end def release_global_lock + + def tab_abbreviations(self, widget, data): + print "todo: implement tab_abbreviations" + #end def tab_abbreviations + + def tab_auto_import(self, widget, data): + """opens the auto import tab""" + new_aimp_thread=GuiAutoImport.GuiAutoImport(self.settings, self.config) + self.threads.append(new_aimp_thread) + aimp_tab=new_aimp_thread.get_vbox() + self.add_and_display_tab(aimp_tab, "Auto Import") + #end def tab_auto_import + + def tab_bulk_import(self, widget, data): + """opens a tab for bulk importing""" + #print "start of tab_bulk_import" + new_import_thread=GuiBulkImport.GuiBulkImport(self.db, self.settings, self.config) + self.threads.append(new_import_thread) + bulk_tab=new_import_thread.get_vbox() + self.add_and_display_tab(bulk_tab, "Bulk Import") + #end def tab_bulk_import + + def tab_main_help(self, widget, data): + """Displays a tab with the main fpdb help screen""" + #print "start of tab_main_help" + mh_tab=gtk.Label("""Welcome to Fpdb! +For documentation please visit our website at http://fpdb.sourceforge.net/ or check the docs directory in the fpdb folder. +Please note that default.conf is no longer needed nor used, all configuration now happens in HUD_config.xml +This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt") + self.add_and_display_tab(mh_tab, "Help") + #end def tab_main_help + + def tab_table_viewer(self, widget, data): + """opens a table viewer tab""" + #print "start of tab_table_viewer" + new_tv_thread=GuiTableViewer.GuiTableViewer(self.db, self.settings) + self.threads.append(new_tv_thread) + tv_tab=new_tv_thread.get_vbox() + self.add_and_display_tab(tv_tab, "Table Viewer") + #end def tab_table_viewer + + def tabGraphViewer(self, widget, data): + """opens a graph viewer tab""" + #print "start of tabGraphViewer" + new_gv_thread=GuiGraphViewer.GuiGraphViewer(self.db, self.settings, self.querydict, self.config) + self.threads.append(new_gv_thread) + gv_tab=new_gv_thread.get_vbox() + self.add_and_display_tab(gv_tab, "Graphs") + #end def tabGraphViewer + + def __init__(self): + self.threads=[] + self.db=None + self.config = Configuration.Config() + self.load_profile() + + self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) + self.window.connect("delete_event", self.delete_event) + self.window.connect("destroy", self.destroy) + self.window.set_title("Free Poker DB - version: alpha9+, p143 or higher") + self.window.set_border_width(1) + self.window.set_size_request(1020,400) + self.window.set_resizable(True) + + self.menu_items = ( + ( "/_Main", None, None, 0, "" ), + ( "/Main/_Load Profile (broken)", "L", self.dia_load_profile, 0, None ), + ( "/Main/_Edit Profile (todo)", "E", self.dia_edit_profile, 0, None ), + ( "/Main/_Save Profile (todo)", None, self.dia_save_profile, 0, None ), + ("/Main/sep1", None, None, 0, "" ), + ("/Main/_Quit", "Q", self.quit, 0, None ), + ("/_Import", None, None, 0, "" ), + ("/Import/_Bulk Import", "B", self.tab_bulk_import, 0, None ), + ("/Import/_Auto Import and HUD", "A", self.tab_auto_import, 0, None ), + ("/Import/Auto _Rating (todo)", "R", self.not_implemented, 0, None ), + ("/_Viewers", None, None, 0, "" ), + ("/_Viewers/_Auto Import and HUD", "A", self.tab_auto_import, 0, None ), + ("/Viewers/_Graphs", "G", self.tabGraphViewer, 0, None ), + ("/Viewers/Hand _Replayer (todo)", None, self.not_implemented, 0, None ), + ("/Viewers/Player _Details (todo)", None, self.not_implemented, 0, None ), + ("/Viewers/_Player Stats (tabulated view) (todo)", None, self.not_implemented, 0, None ), + ("/Viewers/Starting _Hands (todo)", None, self.not_implemented, 0, None ), + ("/Viewers/_Session Replayer (todo)", None, self.not_implemented, 0, None ), + ("/Viewers/Poker_table Viewer (mostly obselete)", "T", self.tab_table_viewer, 0, None ), + #( "/Viewers/Tourney Replayer + ( "/_Database", None, None, 0, "" ), + ( "/Database/Create or Delete _Database (todo)", None, self.dia_create_del_database, 0, None ), + ( "/Database/Create or Delete _User (todo)", None, self.dia_create_del_user, 0, None ), + ( "/Database/Create or Recreate _Tables", None, self.dia_recreate_tables, 0, None ), + ( "/Database/_Statistics (todo)", None, self.dia_database_stats, 0, None ), + ( "/D_ebugging", None, None, 0, "" ), + ( "/Debugging/_Delete Parts of Database (todo)", None, self.dia_delete_db_parts, 0, None ), + ( "/Debugging/_Export DB (todo)", None, self.dia_export_db, 0, None ), + ( "/Debugging/_Import DB (todo)", None, self.dia_import_db, 0, None ), + ( "/Debugging/_Regression test (todo)", None, self.dia_regression_test, 0, None ), + ( "/_Help", None, None, 0, "" ), + ( "/Help/_Main Help", "H", self.tab_main_help, 0, None ), + ( "/Help/_Abbrevations (todo)", None, self.tab_abbreviations, 0, None ), + ( "/Help/sep1", None, None, 0, "" ), + ( "/Help/A_bout (todo)", None, self.dia_about, 0, None ), + ( "/Help/_License and Copying (todo)", None, self.dia_licensing, 0, None ) + ) + + self.main_vbox = gtk.VBox(False, 1) + self.main_vbox.set_border_width(1) + self.window.add(self.main_vbox) + self.main_vbox.show() + + menubar = self.get_menu(self.window) + self.main_vbox.pack_start(menubar, False, True, 0) + menubar.show() + #done menubar + + self.tabs=[] + self.tab_names=[] + self.tab_buttons=[] + self.tab_box = gtk.HBox(False,1) + self.main_vbox.pack_start(self.tab_box, False, True, 0) + self.tab_box.show() + #done tab bar + + self.current_tab = gtk.VBox(False,1) + self.current_tab.set_border_width(1) + self.main_vbox.add(self.current_tab) + self.current_tab.show() + + self.tab_main_help(None, None) + + self.status_bar = gtk.Label("Status: Connected to "+self.db.get_backend_name()+" database named "+self.db.database+" on host "+self.db.host) + self.main_vbox.pack_end(self.status_bar, False, True, 0) + self.status_bar.show() + + self.window.show() + #end def __init__ + + def main(self): + gtk.main() + return 0 + #end def main + +if __name__ == "__main__": + me = fpdb() + me.main() diff --git a/pyfpdb/fpdb_db.py b/pyfpdb/fpdb_db.py old mode 100755 new mode 100644 diff --git a/pyfpdb/fpdb_import.py b/pyfpdb/fpdb_import.py old mode 100755 new mode 100644 index 3b0fb983..e2a2779e --- a/pyfpdb/fpdb_import.py +++ b/pyfpdb/fpdb_import.py @@ -1,298 +1,310 @@ -#!/usr/bin/python - -#Copyright 2008 Steffen Jobbagy-Felso -#This program is free software: you can redistribute it and/or modify -#it under the terms of the GNU Affero General Public License as published by -#the Free Software Foundation, version 3 of the License. -# -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -# -#You should have received a copy of the GNU Affero General Public License -#along with this program. If not, see . -#In the "official" distribution you can find the license in -#agpl-3.0.txt in the docs folder of the package. - -#see status.txt for site/games support info - -import sys - -try: - import MySQLdb - mysqlLibFound=True -except: - pass - -try: - import psycopg2 - pgsqlLibFound=True -except: - pass - -import math -import os -import datetime -import re -import fpdb_simple -import fpdb_parse_logic -from time import time - -class Importer: - - def __init__(self, caller, settings): - """Constructor""" - self.settings=settings - self.caller=caller - self.db = None - self.cursor = None - self.filelist = {} - self.dirlist = {} - self.monitor = False - self.updated = {} #Time last import was run {file:mtime} - self.callHud = False - self.lines = None - self.faobs = None #File as one big string - self.pos_in_file = {} # dict to remember how far we have read in the file - #Set defaults - if not self.settings.has_key('imp-callFpdbHud'): - self.settings['imp-callFpdbHud'] = False - if not self.settings.has_key('minPrint'): - self.settings['minPrint'] = 30 - self.dbConnect() - - def dbConnect(self): - #connect to DB - if self.settings['db-backend'] == 2: - if not mysqlLibFound: - raise fpdb_simple.FpdbError("interface library MySQLdb not found but MySQL selected as backend - please install the library or change the config file") - self.db = MySQLdb.connect(self.settings['db-host'], self.settings['db-user'], - self.settings['db-password'], self.settings['db-databaseName']) - elif self.settings['db-backend'] == 3: - if not pgsqlLibFound: - raise fpdb_simple.FpdbError("interface library psycopg2 not found but PostgreSQL selected as backend - please install the library or change the config file") - print self.settings - self.db = psycopg2.connect(host = self.settings['db-host'], - user = self.settings['db-user'], - password = self.settings['db-password'], - database = self.settings['db-databaseName']) - elif self.settings['db-backend'] == 4: - pass - else: - pass - self.cursor = self.db.cursor() - - #Set functions - def setCallHud(self, value): - self.callHud = value - - def setMinPrint(self, value): - self.settings['minPrint'] = int(value) - - def setHandCount(self, value): - self.settings['handCount'] = int(value) - - def setQuiet(self, value): - self.settings['quiet'] = value - - def setFailOnError(self, value): - self.settings['failOnError'] = value - -# def setWatchTime(self): -# self.updated = time() - - def clearFileList(self): - self.filelist = {} - - #Add an individual file to filelist - def addImportFile(self, filename, site = "default", filter = "passthrough"): - #TODO: test it is a valid file - self.filelist[filename] = [site] + [filter] - - #Add a directory of files to filelist - #Only one import directory per site supported. - #dirlist is a hash of lists: - #dirlist{ 'PokerStars' => ["/path/to/import/", "filtername"] } - def addImportDirectory(self,dir,monitor = False, site = "default", filter = "passthrough"): - if os.path.isdir(dir): - if monitor == True: - self.monitor = True - self.dirlist[site] = [dir] + [filter] - - for file in os.listdir(dir): - self.addImportFile(os.path.join(dir, file), site, filter) - else: - print "Warning: Attempted to add: '" + str(dir) + "' as an import directory" - - #Run full import on filelist - def runImport(self): - for file in self.filelist: - self.import_file_dict(file) - - #Run import on updated files, then store latest update time. - def runUpdated(self): - #Check for new files in directory - #todo: make efficient - always checks for new file, should be able to use mtime of directory - # ^^ May not work on windows - for site in self.dirlist: - self.addImportDirectory(self.dirlist[site][0], False, site, self.dirlist[site][1]) - - for file in self.filelist: - stat_info = os.stat(file) - try: - lastupdate = self.updated[file] - if stat_info.st_mtime > lastupdate: - self.import_file_dict(file) - self.updated[file] = time() - except: - self.updated[file] = time() - # This codepath only runs first time the file is found, if modified in the last - # minute run an immediate import. - if (time() - stat_info.st_mtime) < 60: - self.import_file_dict(file) - - # This is now an internal function that should not be called directly. - def import_file_dict(self, file): - starttime = time() - last_read_hand=0 - loc = 0 - if (file=="stdin"): - inputFile=sys.stdin - else: - inputFile=open(file, "rU") - try: loc = self.pos_in_file[file] - except: pass - - # Read input file into class and close file - inputFile.seek(loc) - self.lines=fpdb_simple.removeTrailingEOL(inputFile.readlines()) - self.pos_in_file[file] = inputFile.tell() - inputFile.close() - - firstline = self.lines[0] - - if firstline.find("Tournament Summary")!=-1: - print "TODO: implement importing tournament summaries" - #self.faobs = readfile(inputFile) - #self.parseTourneyHistory() - return 0 - - site=fpdb_simple.recogniseSite(firstline) - category=fpdb_simple.recogniseCategory(firstline) - - startpos=0 - stored=0 #counter - duplicates=0 #counter - partial=0 #counter - errors=0 #counter - - for i in range (len(self.lines)): #main loop, iterates through the lines of a file and calls the appropriate parser method - if (len(self.lines[i])<2): - endpos=i - hand=self.lines[startpos:endpos] - - if (len(hand[0])<2): - hand=hand[1:] - - cancelled=False - damaged=False - if (site=="ftp"): - for i in range (len(hand)): - if (hand[i].endswith(" has been canceled")): #this is their typo. this is a typo, right? - cancelled=True - - seat1=hand[i].find("Seat ") #todo: make this recover by skipping this line - if (seat1!=-1): - if (hand[i].find("Seat ", seat1+3)!=-1): - damaged=True - - if (len(hand)<3): - pass - #todo: the above 2 lines are kind of a dirty hack, the mentioned circumstances should be handled elsewhere but that doesnt work with DOS/Win EOL. actually this doesnt work. - elif (hand[0].endswith(" (partial)")): #partial hand - do nothing - partial+=1 - elif (hand[1].find("Seat")==-1 and hand[2].find("Seat")==-1 and hand[3].find("Seat")==-1):#todo: should this be or instead of and? - partial+=1 - elif (cancelled or damaged): - partial+=1 - else: #normal processing - isTourney=fpdb_simple.isTourney(hand[0]) - if not isTourney: - fpdb_simple.filterAnteBlindFold(site,hand) - hand=fpdb_simple.filterCrap(site, hand, isTourney) - self.hand=hand - - try: - handsId=fpdb_parse_logic.mainParser(self.db, self.cursor, site, category, hand) - self.db.commit() - - stored+=1 - self.db.commit() -# if settings['imp-callFpdbHud'] and self.callHud and os.sep=='/': - if self.settings['imp-callFpdbHud'] and self.callHud: - #print "call to HUD here. handsId:",handsId - #pipe the Hands.id out to the HUD - self.caller.pipe_to_hud.stdin.write("%s" % (handsId) + os.linesep) - except fpdb_simple.DuplicateError: - duplicates+=1 - except (ValueError), fe: - errors+=1 - self.printEmailErrorMessage(errors, file, hand[0]) - - if (self.settings['failOnError']): - self.db.commit() #dont remove this, in case hand processing was cancelled. - raise - except (fpdb_simple.FpdbError), fe: - errors+=1 - self.printEmailErrorMessage(errors, file, hand[0]) - - #fe.printStackTrace() #todo: get stacktrace - self.db.rollback() - - if (self.settings['failOnError']): - self.db.commit() #dont remove this, in case hand processing was cancelled. - raise - if (self.settings['minPrint']!=0): - if ((stored+duplicates+partial+errors)%self.settings['minPrint']==0): - print "stored:", stored, "duplicates:", duplicates, "partial:", partial, "errors:", errors - - if (self.settings['handCount']!=0): - if ((stored+duplicates+partial+errors)>=self.settings['handCount']): - if (not self.settings['quiet']): - print "quitting due to reaching the amount of hands to be imported" - print "Total stored:", stored, "duplicates:", duplicates, "partial/damaged:", partial, "errors:", errors, " time:", (time() - starttime) - sys.exit(0) - startpos=endpos - print "Total stored:", stored, "duplicates:", duplicates, "partial:", partial, "errors:", errors, " time:", (time() - starttime) - - if stored==0: - if duplicates>0: - for line_no in range(len(self.lines)): - if self.lines[line_no].find("Game #")!=-1: - final_game_line=self.lines[line_no] - handsId=fpdb_simple.parseSiteHandNo(final_game_line) - else: - print "failed to read a single hand from file:", inputFile - handsId=0 - #todo: this will cause return of an unstored hand number if the last hand was error or partial - self.db.commit() - self.handsId=handsId - return handsId -#end def import_file_dict - - def parseTourneyHistory(self): - print "Tourney history parser stub" - #Find tournament boundaries. - #print self.foabs - - - def printEmailErrorMessage(self, errors, filename, line): - print "Error No.",errors,", please send the hand causing this to steffen@sycamoretest.info so I can fix it." - print "Filename:", filename - print "Here is the first line so you can identify it. Please mention that the error was a ValueError:" - print self.hand[0] - - -if __name__ == "__main__": - print "CLI for fpdb_import is now available as CliFpdb.py" +#!/usr/bin/python + +#Copyright 2008 Steffen Jobbagy-Felso +#This program is free software: you can redistribute it and/or modify +#it under the terms of the GNU Affero General Public License as published by +#the Free Software Foundation, version 3 of the License. +# +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +# +#You should have received a copy of the GNU Affero General Public License +#along with this program. If not, see . +#In the "official" distribution you can find the license in +#agpl-3.0.txt in the docs folder of the package. + +#see status.txt for site/games support info + +import sys + +try: + import MySQLdb + mysqlLibFound=True +except: + pass + +try: + import psycopg2 + pgsqlLibFound=True +except: + pass + +import math +import os +import datetime +import re +import fpdb_simple +import fpdb_parse_logic +from time import time + +class Importer: + + def __init__(self, caller, settings, config): + """Constructor""" + self.settings=settings + self.caller=caller + self.config = config + self.db = None + self.cursor = None + self.filelist = {} + self.dirlist = {} + self.monitor = False + self.updated = {} #Time last import was run {file:mtime} + self.lines = None + self.faobs = None #File as one big string + self.pos_in_file = {} # dict to remember how far we have read in the file + #Set defaults + self.callHud = self.config.get_import_parameters().get("callFpdbHud") + if not self.settings.has_key('minPrint'): + self.settings['minPrint'] = 30 + self.dbConnect() + + def dbConnect(self): + #connect to DB + if self.settings['db-backend'] == 2: + if not mysqlLibFound: + raise fpdb_simple.FpdbError("interface library MySQLdb not found but MySQL selected as backend - please install the library or change the config file") + self.db = MySQLdb.connect(self.settings['db-host'], self.settings['db-user'], + self.settings['db-password'], self.settings['db-databaseName']) + elif self.settings['db-backend'] == 3: + if not pgsqlLibFound: + raise fpdb_simple.FpdbError("interface library psycopg2 not found but PostgreSQL selected as backend - please install the library or change the config file") + print self.settings + self.db = psycopg2.connect(host = self.settings['db-host'], + user = self.settings['db-user'], + password = self.settings['db-password'], + database = self.settings['db-databaseName']) + elif self.settings['db-backend'] == 4: + pass + else: + pass + self.cursor = self.db.cursor() + + #Set functions + def setCallHud(self, value): + self.callHud = value + + def setMinPrint(self, value): + self.settings['minPrint'] = int(value) + + def setHandCount(self, value): + self.settings['handCount'] = int(value) + + def setQuiet(self, value): + self.settings['quiet'] = value + + def setFailOnError(self, value): + self.settings['failOnError'] = value + +# def setWatchTime(self): +# self.updated = time() + + def clearFileList(self): + self.filelist = {} + + #Add an individual file to filelist + def addImportFile(self, filename, site = "default", filter = "passthrough"): + #TODO: test it is a valid file + self.filelist[filename] = [site] + [filter] + + #Add a directory of files to filelist + #Only one import directory per site supported. + #dirlist is a hash of lists: + #dirlist{ 'PokerStars' => ["/path/to/import/", "filtername"] } + def addImportDirectory(self, dir, monitor = False, site = "default", filter = "passthrough"): + if dir != "/dev/null" and dir.lower() != "none": + if os.path.isdir(dir): + if monitor == True: + self.monitor = True + self.dirlist[site] = [dir] + [filter] + + for file in os.listdir(dir): + self.addImportFile(os.path.join(dir, file), site, filter) + else: + print "Warning: Attempted to add: '" + str(dir) + "' as an import directory\n" + + #Run full import on filelist + def runImport(self): + for file in self.filelist: + self.import_file_dict(file, self.filelist[file][0], self.filelist[file][1]) + + #Run import on updated files, then store latest update time. + def runUpdated(self): + #Check for new files in directory + #todo: make efficient - always checks for new file, should be able to use mtime of directory + # ^^ May not work on windows + for site in self.dirlist: + self.addImportDirectory(self.dirlist[site][0], False, site, self.dirlist[site][1]) + + for file in self.filelist: + stat_info = os.stat(file) + try: + lastupdate = self.updated[file] + if stat_info.st_mtime > lastupdate: + self.import_file_dict(file, self.filelist[file][0], self.filelist[file][1]) + self.updated[file] = time() + except: + self.updated[file] = time() + # This codepath only runs first time the file is found, if modified in the last + # minute run an immediate import. + if (time() - stat_info.st_mtime) < 60: + self.import_file_dict(file, self.filelist[file][0], self.filelist[file][1]) + + # This is now an internal function that should not be called directly. + def import_file_dict(self, file, site, filter): + if(filter == "passthrough"): + self.import_fpdb_file(file, site) + else: + #Load filter, and run filtered file though main importer + self.import_fpdb_file(file, site) + + + def import_fpdb_file(self, file, site): + starttime = time() + last_read_hand=0 + loc = 0 + if (file=="stdin"): + inputFile=sys.stdin + else: + inputFile=open(file, "rU") + try: loc = self.pos_in_file[file] + except: pass + + # Read input file into class and close file + inputFile.seek(loc) + self.lines=fpdb_simple.removeTrailingEOL(inputFile.readlines()) + self.pos_in_file[file] = inputFile.tell() + inputFile.close() + + try: # sometimes we seem to be getting an empty self.lines, in which case, we just want to return. + firstline = self.lines[0] + except: +# print "import_fpdb_file", file, site, self.lines, "\n" + return + + if firstline.find("Tournament Summary")!=-1: + print "TODO: implement importing tournament summaries" + #self.faobs = readfile(inputFile) + #self.parseTourneyHistory() + return 0 + + site=fpdb_simple.recogniseSite(firstline) + category=fpdb_simple.recogniseCategory(firstline) + + startpos=0 + stored=0 #counter + duplicates=0 #counter + partial=0 #counter + errors=0 #counter + + for i in range (len(self.lines)): #main loop, iterates through the lines of a file and calls the appropriate parser method + if (len(self.lines[i])<2): + endpos=i + hand=self.lines[startpos:endpos] + + if (len(hand[0])<2): + hand=hand[1:] + + cancelled=False + damaged=False + if (site=="ftp"): + for i in range (len(hand)): + if (hand[i].endswith(" has been canceled")): #this is their typo. this is a typo, right? + cancelled=True + + seat1=hand[i].find("Seat ") #todo: make this recover by skipping this line + if (seat1!=-1): + if (hand[i].find("Seat ", seat1+3)!=-1): + damaged=True + + if (len(hand)<3): + pass + #todo: the above 2 lines are kind of a dirty hack, the mentioned circumstances should be handled elsewhere but that doesnt work with DOS/Win EOL. actually this doesnt work. + elif (hand[0].endswith(" (partial)")): #partial hand - do nothing + partial+=1 + elif (hand[1].find("Seat")==-1 and hand[2].find("Seat")==-1 and hand[3].find("Seat")==-1):#todo: should this be or instead of and? + partial+=1 + elif (cancelled or damaged): + partial+=1 + else: #normal processing + isTourney=fpdb_simple.isTourney(hand[0]) + if not isTourney: + fpdb_simple.filterAnteBlindFold(site,hand) + hand=fpdb_simple.filterCrap(site, hand, isTourney) + self.hand=hand + + try: + handsId=fpdb_parse_logic.mainParser(self.db, self.cursor, site, category, hand) + self.db.commit() + + stored+=1 + self.db.commit() + if self.callHud: + #print "call to HUD here. handsId:",handsId + #pipe the Hands.id out to the HUD + self.caller.pipe_to_hud.stdin.write("%s" % (handsId) + os.linesep) + except fpdb_simple.DuplicateError: + duplicates+=1 + except (ValueError), fe: + errors+=1 + self.printEmailErrorMessage(errors, file, hand[0]) + + if (self.settings['failOnError']): + self.db.commit() #dont remove this, in case hand processing was cancelled. + raise + except (fpdb_simple.FpdbError), fe: + errors+=1 + self.printEmailErrorMessage(errors, file, hand[0]) + + #fe.printStackTrace() #todo: get stacktrace + self.db.rollback() + + if (self.settings['failOnError']): + self.db.commit() #dont remove this, in case hand processing was cancelled. + raise + if (self.settings['minPrint']!=0): + if ((stored+duplicates+partial+errors)%self.settings['minPrint']==0): + print "stored:", stored, "duplicates:", duplicates, "partial:", partial, "errors:", errors + + if (self.settings['handCount']!=0): + if ((stored+duplicates+partial+errors)>=self.settings['handCount']): + if (not self.settings['quiet']): + print "quitting due to reaching the amount of hands to be imported" + print "Total stored:", stored, "duplicates:", duplicates, "partial/damaged:", + partial, "errors:", errors, " time: %5.3f" % (time() - starttime) + sys.exit(0) + startpos=endpos + print "Total stored:", stored, "duplicates:", duplicates, "partial:", partial, "errors:", errors, " time: %5.3f" % (time() - starttime) + + if stored==0: + if duplicates>0: + for line_no in range(len(self.lines)): + if self.lines[line_no].find("Game #")!=-1: + final_game_line=self.lines[line_no] + handsId=fpdb_simple.parseSiteHandNo(final_game_line) + else: + print "failed to read a single hand from file:", inputFile + handsId=0 + #todo: this will cause return of an unstored hand number if the last hand was error or partial + self.db.commit() + self.handsId=handsId + return handsId +#end def import_file_dict + + def parseTourneyHistory(self): + print "Tourney history parser stub" + #Find tournament boundaries. + #print self.foabs + + + def printEmailErrorMessage(self, errors, filename, line): + print "Error No.",errors,", please send the hand causing this to steffen@sycamoretest.info so I can fix it." + print "Filename:", filename + print "Here is the first line so you can identify it. Please mention that the error was a ValueError:" + print self.hand[0] + + +if __name__ == "__main__": + print "CLI for fpdb_import is now available as CliFpdb.py" diff --git a/pyfpdb/fpdb_parse_logic.py b/pyfpdb/fpdb_parse_logic.py index ec9a53f6..d704d811 100644 --- a/pyfpdb/fpdb_parse_logic.py +++ b/pyfpdb/fpdb_parse_logic.py @@ -46,7 +46,7 @@ def mainParser(db, cursor, site, category, hand): #print "found small blind line:",smallBlindLine break #print "small blind line:",smallBlindLine - gametypeID=fpdb_simple.recogniseGametypeID(cursor, hand[0], hand[smallBlindLine], siteID, category, isTourney) + gametypeID=fpdb_simple.recogniseGametypeID(db, cursor, hand[0], hand[smallBlindLine], siteID, category, isTourney) if isTourney: if site!="ps": raise fpdb_simple.FpdbError("tourneys are only supported on PS right now") @@ -142,18 +142,18 @@ def mainParser(db, cursor, site, category, hand): payin_amounts=fpdb_simple.calcPayin(len(names), buyin, fee) if base=="hold": - result = fpdb_save_to_db.tourney_holdem_omaha(cursor, base, category, siteTourneyNo, buyin, fee, knockout, entries, prizepool, tourneyStartTime, payin_amounts, ranks, tourneyTypeId, siteID, + result = fpdb_save_to_db.tourney_holdem_omaha(db, cursor, base, category, siteTourneyNo, buyin, fee, knockout, entries, prizepool, tourneyStartTime, payin_amounts, ranks, tourneyTypeId, siteID, siteHandNo, gametypeID, handStartTime, names, playerIDs, startCashes, positions, cardValues, cardSuits, boardValues, boardSuits, winnings, rakes, actionTypes, allIns, actionAmounts, actionNos, hudImportData, maxSeats, tableName, seatNos) elif base=="stud": - result = fpdb_save_to_db.tourney_stud(cursor, base, category, siteTourneyNo, buyin, fee, knockout, entries, prizepool, tourneyStartTime, payin_amounts, ranks, tourneyTypeId, siteID, + result = fpdb_save_to_db.tourney_stud(db, cursor, base, category, siteTourneyNo, buyin, fee, knockout, entries, prizepool, tourneyStartTime, payin_amounts, ranks, tourneyTypeId, siteID, siteHandNo, gametypeID, handStartTime, names, playerIDs, startCashes, antes, cardValues, cardSuits, winnings, rakes, actionTypes, allIns, actionAmounts, actionNos, hudImportData, maxSeats, tableName, seatNos) else: raise fpdb_simple.FpdbError ("unrecognised category") else: if base=="hold": - result = fpdb_save_to_db.ring_holdem_omaha(cursor, base, category, siteHandNo, gametypeID, handStartTime, names, playerIDs, startCashes, positions, cardValues, cardSuits, boardValues, boardSuits, winnings, rakes, actionTypes, allIns, actionAmounts, actionNos, hudImportData, maxSeats, tableName, seatNos) + result = fpdb_save_to_db.ring_holdem_omaha(db, cursor, base, category, siteHandNo, gametypeID, handStartTime, names, playerIDs, startCashes, positions, cardValues, cardSuits, boardValues, boardSuits, winnings, rakes, actionTypes, allIns, actionAmounts, actionNos, hudImportData, maxSeats, tableName, seatNos) elif base=="stud": - result = fpdb_save_to_db.ring_stud(cursor, base, category, siteHandNo, gametypeID, + result = fpdb_save_to_db.ring_stud(db, cursor, base, category, siteHandNo, gametypeID, handStartTime, names, playerIDs, startCashes, antes, cardValues, cardSuits, winnings, rakes, actionTypes, allIns, actionAmounts, actionNos, hudImportData, maxSeats, tableName, seatNos) else: diff --git a/pyfpdb/fpdb_save_to_db.py b/pyfpdb/fpdb_save_to_db.py index af6ab09d..d548eca5 100644 --- a/pyfpdb/fpdb_save_to_db.py +++ b/pyfpdb/fpdb_save_to_db.py @@ -21,13 +21,13 @@ import fpdb_simple #stores a stud/razz hand into the database -def ring_stud(cursor, base, category, site_hand_no, gametype_id, hand_start_time, names, player_ids, start_cashes, antes, card_values, card_suits, winnings, rakes, action_types, allIns, action_amounts, actionNos, hudImportData, maxSeats, tableName, seatNos): +def ring_stud(db, cursor, base, category, site_hand_no, gametype_id, hand_start_time, names, player_ids, start_cashes, antes, card_values, card_suits, winnings, rakes, action_types, allIns, action_amounts, actionNos, hudImportData, maxSeats, tableName, seatNos): fpdb_simple.fillCardArrays(len(names), base, category, card_values, card_suits) - hands_id=fpdb_simple.storeHands(cursor, site_hand_no, gametype_id, hand_start_time, names, tableName, maxSeats) + hands_id=fpdb_simple.storeHands(db, cursor, site_hand_no, gametype_id, hand_start_time, names, tableName, maxSeats) #print "before calling store_hands_players_stud, antes:", antes - hands_players_ids=fpdb_simple.store_hands_players_stud(cursor, hands_id, player_ids, + hands_players_ids=fpdb_simple.store_hands_players_stud(db, cursor, hands_id, player_ids, start_cashes, antes, card_values, card_suits, winnings, rakes, seatNos) fpdb_simple.storeHudCache(cursor, base, category, gametype_id, player_ids, hudImportData) @@ -36,14 +36,14 @@ def ring_stud(cursor, base, category, site_hand_no, gametype_id, hand_start_time return hands_id #end def ring_stud -def ring_holdem_omaha(cursor, base, category, site_hand_no, gametype_id, hand_start_time, names, player_ids, start_cashes, positions, card_values, card_suits, board_values, board_suits, winnings, rakes, action_types, allIns, action_amounts, actionNos, hudImportData, maxSeats, tableName, seatNos): +def ring_holdem_omaha(db, cursor, base, category, site_hand_no, gametype_id, hand_start_time, names, player_ids, start_cashes, positions, card_values, card_suits, board_values, board_suits, winnings, rakes, action_types, allIns, action_amounts, actionNos, hudImportData, maxSeats, tableName, seatNos): """stores a holdem/omaha hand into the database""" fpdb_simple.fillCardArrays(len(names), base, category, card_values, card_suits) fpdb_simple.fill_board_cards(board_values, board_suits) - hands_id=fpdb_simple.storeHands(cursor, site_hand_no, gametype_id, hand_start_time, names, tableName, maxSeats) + hands_id=fpdb_simple.storeHands(db, cursor, site_hand_no, gametype_id, hand_start_time, names, tableName, maxSeats) - hands_players_ids=fpdb_simple.store_hands_players_holdem_omaha(cursor, category, hands_id, player_ids, start_cashes, positions, card_values, card_suits, winnings, rakes, seatNos) + hands_players_ids=fpdb_simple.store_hands_players_holdem_omaha(db, cursor, category, hands_id, player_ids, start_cashes, positions, card_values, card_suits, winnings, rakes, seatNos) fpdb_simple.storeHudCache(cursor, base, category, gametype_id, player_ids, hudImportData) @@ -53,7 +53,7 @@ def ring_holdem_omaha(cursor, base, category, site_hand_no, gametype_id, hand_st return hands_id #end def ring_holdem_omaha -def tourney_holdem_omaha(cursor, base, category, siteTourneyNo, buyin, fee, knockout, entries, prizepool, tourney_start, payin_amounts, ranks, tourneyTypeId, siteId, #end of tourney specific params +def tourney_holdem_omaha(db, cursor, base, category, siteTourneyNo, buyin, fee, knockout, entries, prizepool, tourney_start, payin_amounts, ranks, tourneyTypeId, siteId, #end of tourney specific params site_hand_no, gametype_id, hand_start_time, names, player_ids, start_cashes, positions, card_values, card_suits, board_values, board_suits, winnings, rakes, action_types, allIns, action_amounts, actionNos, hudImportData, maxSeats, tableName, seatNos): """stores a tourney holdem/omaha hand into the database""" fpdb_simple.fillCardArrays(len(names), base, category, card_values, card_suits) @@ -62,9 +62,9 @@ def tourney_holdem_omaha(cursor, base, category, siteTourneyNo, buyin, fee, knoc tourney_id=fpdb_simple.store_tourneys(cursor, tourneyTypeId, siteTourneyNo, entries, prizepool, tourney_start) tourneys_players_ids=fpdb_simple.store_tourneys_players(cursor, tourney_id, player_ids, payin_amounts, ranks, winnings) - hands_id=fpdb_simple.storeHands(cursor, site_hand_no, gametype_id, hand_start_time, names, tableName, maxSeats) + hands_id=fpdb_simple.storeHands(db, cursor, site_hand_no, gametype_id, hand_start_time, names, tableName, maxSeats) - hands_players_ids=fpdb_simple.store_hands_players_holdem_omaha_tourney(cursor, category, hands_id, player_ids, start_cashes, positions, card_values, card_suits, winnings, rakes, seatNos, tourneys_players_ids) + hands_players_ids=fpdb_simple.store_hands_players_holdem_omaha_tourney(db, cursor, category, hands_id, player_ids, start_cashes, positions, card_values, card_suits, winnings, rakes, seatNos, tourneys_players_ids) fpdb_simple.storeHudCache(cursor, base, category, gametype_id, player_ids, hudImportData) @@ -74,7 +74,7 @@ def tourney_holdem_omaha(cursor, base, category, siteTourneyNo, buyin, fee, knoc return hands_id #end def tourney_holdem_omaha -def tourney_stud(cursor, base, category, siteTourneyNo, buyin, fee, knockout, entries, prizepool, tourneyStartTime, payin_amounts, ranks, tourneyTypeId, siteId, +def tourney_stud(db, cursor, base, category, siteTourneyNo, buyin, fee, knockout, entries, prizepool, tourneyStartTime, payin_amounts, ranks, tourneyTypeId, siteId, siteHandNo, gametypeId, handStartTime, names, playerIds, startCashes, antes, cardValues, cardSuits, winnings, rakes, actionTypes, allIns, actionAmounts, actionNos, hudImportData, maxSeats, tableName, seatNos): #stores a tourney stud/razz hand into the database fpdb_simple.fillCardArrays(len(names), base, category, cardValues, cardSuits) @@ -83,9 +83,9 @@ def tourney_stud(cursor, base, category, siteTourneyNo, buyin, fee, knockout, en tourneys_players_ids=fpdb_simple.store_tourneys_players(cursor, tourney_id, playerIds, payin_amounts, ranks, winnings) - hands_id=fpdb_simple.storeHands(cursor, siteHandNo, gametypeId, handStartTime, names, tableName, maxSeats) + hands_id=fpdb_simple.storeHands(db, cursor, siteHandNo, gametypeId, handStartTime, names, tableName, maxSeats) - hands_players_ids=fpdb_simple.store_hands_players_stud_tourney(cursor, hands_id, playerIds, startCashes, antes, cardValues, cardSuits, winnings, rakes, seatNos, tourneys_players_ids) + hands_players_ids=fpdb_simple.store_hands_players_stud_tourney(db, cursor, hands_id, playerIds, startCashes, antes, cardValues, cardSuits, winnings, rakes, seatNos, tourneys_players_ids) fpdb_simple.storeHudCache(cursor, base, category, gametypeId, playerIds, hudImportData) diff --git a/pyfpdb/fpdb_simple.py b/pyfpdb/fpdb_simple.py index d1b191de..ecd9def8 100644 --- a/pyfpdb/fpdb_simple.py +++ b/pyfpdb/fpdb_simple.py @@ -484,7 +484,8 @@ def isActionLine(line): #returns whether this is a duplicate def isAlreadyInDB(cursor, gametypeID, siteHandNo): - cursor.execute ("SELECT id FROM Hands WHERE gametypeId=%s AND siteHandNo=%s", (gametypeID, siteHandNo)) + #print "isAlreadyInDB gtid,shand:",gametypeID, siteHandNo + cursor.execute ("SELECT id FROM Hands WHERE gametypeId=%s AND siteHandNo=%s", (gametypeID, siteHandNo)) result=cursor.fetchall() if (len(result)>=1): raise DuplicateError ("dupl") @@ -1021,7 +1022,7 @@ def recogniseCategory(line): #end def recogniseCategory #returns the int for the gametype_id for the given line -def recogniseGametypeID(cursor, topline, smallBlindLine, site_id, category, isTourney):#todo: this method is messy +def recogniseGametypeID(db, cursor, topline, smallBlindLine, site_id, category, isTourney):#todo: this method is messy #if (topline.find("HORSE")!=-1): # raise FpdbError("recogniseGametypeID: HORSE is not yet supported.") @@ -1072,7 +1073,10 @@ def recogniseGametypeID(cursor, topline, smallBlindLine, site_id, category, isTo else: cursor.execute ("SELECT id FROM Gametypes WHERE siteId=%s AND type=%s AND category=%s AND limitType=%s AND smallBlind=%s AND bigBlind=%s", (site_id, type, category, limit_type, small_bet, big_bet)) result=cursor.fetchone() - #print "tried SELECTing gametypes.id, result:",result + #print "recgt1 result=",result + #ret=result[0] + #print "recgt1 ret=",ret + #print "tried SELECTing gametypes.id, result:",result try: len(result) @@ -1105,17 +1109,19 @@ def recogniseGametypeID(cursor, topline, smallBlindLine, site_id, category, isTo cursor.execute("""INSERT INTO Gametypes (siteId, type, base, category, limitType, hiLo, smallBlind, bigBlind, smallBet, bigBet) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""", (site_id, type, base, category, limit_type, hiLo, small_blind, big_blind, small_bet, big_bet)) - cursor.execute ("SELECT id FROM Gametypes WHERE siteId=%s AND type=%s AND category=%s AND limitType=%s AND smallBet=%s AND bigBet=%s", (site_id, type, category, limit_type, small_bet, big_bet)) + #cursor.execute ("SELECT id FROM Gametypes WHERE siteId=%s AND type=%s AND category=%s AND limitType=%s AND smallBet=%s AND bigBet=%s", (site_id, type, category, limit_type, small_bet, big_bet)) else: cursor.execute("""INSERT INTO Gametypes (siteId, type, base, category, limitType, hiLo, smallBlind, bigBlind, smallBet, bigBet) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""", (site_id, type, base, category, limit_type, hiLo, small_bet, big_bet, 0, 0))#remember, for these bet means blind - cursor.execute ("SELECT id FROM Gametypes WHERE siteId=%s AND type=%s AND category=%s AND limitType=%s AND smallBlind=%s AND bigBlind=%s", (site_id, type, category, limit_type, small_bet, big_bet)) + #cursor.execute ("SELECT id FROM Gametypes WHERE siteId=%s AND type=%s AND category=%s AND limitType=%s AND smallBlind=%s AND bigBlind=%s", (site_id, type, category, limit_type, small_bet, big_bet)) - result=cursor.fetchone() + result=(db.insert_id(),) + #print "recgt2 result=",result #print "created new gametypes.id:",result - return result[0] + #print "recgt3: result=", result + return result[0] #end def recogniseGametypeID def recogniseTourneyTypeId(cursor, siteId, buyin, fee, knockout, rebuyOrAddon): @@ -1248,15 +1254,16 @@ def store_board_cards(cursor, hands_id, board_values, board_suits): board_values[4], board_suits[4])) #end def store_board_cards -def storeHands(cursor, site_hand_no, gametype_id, hand_start_time, names, tableName, maxSeats): +def storeHands(db, cursor, site_hand_no, gametype_id, hand_start_time, names, tableName, maxSeats): #stores into table hands cursor.execute ("INSERT INTO Hands (siteHandNo, gametypeId, handStart, seats, tableName, importTime, maxSeats) VALUES (%s, %s, %s, %s, %s, %s, %s)", (site_hand_no, gametype_id, hand_start_time, len(names), tableName, datetime.datetime.today(), maxSeats)) #todo: find a better way of doing this... - cursor.execute("SELECT id FROM Hands WHERE siteHandNo=%s AND gametypeId=%s", (site_hand_no, gametype_id)) - return cursor.fetchall()[0][0] + #cursor.execute("SELECT id FROM Hands WHERE siteHandNo=%s AND gametypeId=%s", (site_hand_no, gametype_id)) + #return cursor.fetchall()[0][0] + return db.insert_id() # mysql only #end def storeHands -def store_hands_players_holdem_omaha(cursor, category, hands_id, player_ids, start_cashes, positions, card_values, card_suits, winnings, rakes, seatNos): +def store_hands_players_holdem_omaha(db, cursor, category, hands_id, player_ids, start_cashes, positions, card_values, card_suits, winnings, rakes, seatNos): result=[] if (category=="holdem"): for i in range (len(player_ids)): @@ -1268,8 +1275,9 @@ def store_hands_players_holdem_omaha(cursor, category, hands_id, player_ids, sta (hands_id, player_ids[i], start_cashes[i], positions[i], card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1], winnings[i], rakes[i], seatNos[i])) - cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId=%s", (hands_id, player_ids[i])) - result.append(cursor.fetchall()[0][0]) + #cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId=%s", (hands_id, player_ids[i])) + #result.append(cursor.fetchall()[0][0]) + result.append( db.insert_id() ) # mysql only elif (category=="omahahi" or category=="omahahilo"): for i in range (len(player_ids)): cursor.execute ("""INSERT INTO HandsPlayers @@ -1281,14 +1289,15 @@ def store_hands_players_holdem_omaha(cursor, category, hands_id, player_ids, sta card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1], card_values[i][2], card_suits[i][2], card_values[i][3], card_suits[i][3], winnings[i], rakes[i], seatNos[i])) - cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId=%s", (hands_id, player_ids[i])) - result.append(cursor.fetchall()[0][0]) + #cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i])) + #result.append(cursor.fetchall()[0][0]) + result.append( db.insert_id() ) # mysql only else: raise FpdbError("invalid category") return result #end def store_hands_players_holdem_omaha -def store_hands_players_stud(cursor, hands_id, player_ids, start_cashes, antes, +def store_hands_players_stud(db, cursor, hands_id, player_ids, start_cashes, antes, card_values, card_suits, winnings, rakes, seatNos): #stores hands_players rows for stud/razz games. returns an array of the resulting IDs result=[] @@ -1307,12 +1316,14 @@ def store_hands_players_stud(cursor, hands_id, player_ids, start_cashes, antes, card_values[i][2], card_suits[i][2], card_values[i][3], card_suits[i][3], card_values[i][4], card_suits[i][4], card_values[i][5], card_suits[i][5], card_values[i][6], card_suits[i][6], winnings[i], rakes[i], seatNos[i])) - cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId=%s", (hands_id, player_ids[i])) - result.append(cursor.fetchall()[0][0]) + #cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i])) + #result.append(cursor.fetchall()[0][0]) + result.append( db.insert_id() ) # mysql only return result #end def store_hands_players_stud -def store_hands_players_holdem_omaha_tourney(cursor, category, hands_id, player_ids, start_cashes, positions, card_values, card_suits, winnings, rakes, seatNos, tourneys_players_ids): +def store_hands_players_holdem_omaha_tourney(db, cursor, category, hands_id, player_ids, + start_cashes, positions, card_values, card_suits, winnings, rakes, seatNos, tourneys_players_ids): #stores hands_players for tourney holdem/omaha hands result=[] for i in range (len(player_ids)): @@ -1338,13 +1349,14 @@ def store_hands_players_holdem_omaha_tourney(cursor, category, hands_id, player_ winnings[i], rakes[i], tourneys_players_ids[i], seatNos[i])) else: raise FpdbError ("invalid card_values length:"+str(len(card_values[0]))) - cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId=%s", (hands_id, player_ids[i])) - result.append(cursor.fetchall()[0][0]) + #cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i])) + #result.append(cursor.fetchall()[0][0]) + result.append( db.insert_id() ) # mysql only return result #end def store_hands_players_holdem_omaha_tourney -def store_hands_players_stud_tourney(cursor, hands_id, player_ids, start_cashes, +def store_hands_players_stud_tourney(db, cursor, hands_id, player_ids, start_cashes, antes, card_values, card_suits, winnings, rakes, seatNos, tourneys_players_ids): #stores hands_players for tourney stud/razz hands result=[] @@ -1362,8 +1374,9 @@ def store_hands_players_stud_tourney(cursor, hands_id, player_ids, start_cashes, card_values[i][2], card_suits[i][2], card_values[i][3], card_suits[i][3], card_values[i][4], card_suits[i][4], card_values[i][5], card_suits[i][5], card_values[i][6], card_suits[i][6], winnings[i], rakes[i], tourneys_players_ids[i], seatNos[i])) - cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId=%s", (hands_id, player_ids[i])) - result.append(cursor.fetchall()[0][0]) + #cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i])) + #result.append(cursor.fetchall()[0][0]) + result.append( db.insert_id() ) # mysql only return result #end def store_hands_players_stud_tourney @@ -1949,9 +1962,9 @@ def storeHudCache(cursor, base, category, gametypeId, playerIds, hudImportData): for player in range (len(playerIds)): if base=="hold": - cursor.execute("SELECT * FROM HudCache WHERE gametypeId=%s AND playerId=%s AND activeSeats=%s AND position=%s", (gametypeId, playerIds[player], len(playerIds), hudImportData['position'][player])) + cursor.execute("SELECT * FROM HudCache WHERE gametypeId+0=%s AND playerId=%s AND activeSeats=%s AND position=%s", (gametypeId, playerIds[player], len(playerIds), hudImportData['position'][player])) else: - cursor.execute("SELECT * FROM HudCache WHERE gametypeId=%s AND playerId=%s AND activeSeats=%s", (gametypeId, playerIds[player], len(playerIds))) + cursor.execute("SELECT * FROM HudCache WHERE gametypeId+0=%s AND playerId=%s AND activeSeats=%s", (gametypeId, playerIds[player], len(playerIds))) row=cursor.fetchone() #print "gametypeId:", gametypeId, "playerIds[player]",playerIds[player], "len(playerIds):",len(playerIds), "row:",row @@ -2097,7 +2110,7 @@ def storeHudCache(cursor, base, category, gametypeId, playerIds, hudImportData): #end def storeHudCache def store_tourneys(cursor, tourneyTypeId, siteTourneyNo, entries, prizepool, startTime): - cursor.execute("SELECT id FROM Tourneys WHERE siteTourneyNo=%s AND tourneyTypeId=%s", (siteTourneyNo, tourneyTypeId)) + cursor.execute("SELECT id FROM Tourneys WHERE siteTourneyNo=%s AND tourneyTypeId+0=%s", (siteTourneyNo, tourneyTypeId)) tmp=cursor.fetchone() #print "tried SELECTing tourneys.id, result:",tmp @@ -2107,7 +2120,7 @@ def store_tourneys(cursor, tourneyTypeId, siteTourneyNo, entries, prizepool, sta cursor.execute("""INSERT INTO Tourneys (tourneyTypeId, siteTourneyNo, entries, prizepool, startTime) VALUES (%s, %s, %s, %s, %s)""", (tourneyTypeId, siteTourneyNo, entries, prizepool, startTime)) - cursor.execute("SELECT id FROM Tourneys WHERE siteTourneyNo=%s AND tourneyTypeId=%s", (siteTourneyNo, tourneyTypeId)) + cursor.execute("SELECT id FROM Tourneys WHERE siteTourneyNo=%s AND tourneyTypeId+0=%s", (siteTourneyNo, tourneyTypeId)) tmp=cursor.fetchone() #print "created new tourneys.id:",tmp return tmp[0] @@ -2121,7 +2134,7 @@ def store_tourneys_players(cursor, tourney_id, player_ids, payin_amounts, ranks, #print "ranks:",ranks #print "winnings:",winnings for i in range (len(player_ids)): - cursor.execute("SELECT id FROM TourneysPlayers WHERE tourneyId=%s AND playerId=%s", (tourney_id, player_ids[i])) + cursor.execute("SELECT id FROM TourneysPlayers WHERE tourneyId=%s AND playerId+0=%s", (tourney_id, player_ids[i])) tmp=cursor.fetchone() #print "tried SELECTing tourneys_players.id:",tmp @@ -2132,7 +2145,7 @@ def store_tourneys_players(cursor, tourney_id, player_ids, payin_amounts, ranks, (tourneyId, playerId, payinAmount, rank, winnings) VALUES (%s, %s, %s, %s, %s)""", (tourney_id, player_ids[i], payin_amounts[i], ranks[i], winnings[i])) - cursor.execute("SELECT id FROM TourneysPlayers WHERE tourneyId=%s AND playerId=%s", + cursor.execute("SELECT id FROM TourneysPlayers WHERE tourneyId=%s AND playerId+0=%s", (tourney_id, player_ids[i])) tmp=cursor.fetchone() #print "created new tourneys_players.id:",tmp From 567d585a54af580aac54eb9faa0566ba8e52b116 Mon Sep 17 00:00:00 2001 From: sqlcoder Date: Tue, 2 Dec 2008 23:28:06 +0000 Subject: [PATCH 03/35] script to update index choices --- pyfpdb/upd_indexes.sql | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100755 pyfpdb/upd_indexes.sql diff --git a/pyfpdb/upd_indexes.sql b/pyfpdb/upd_indexes.sql new file mode 100755 index 00000000..e79f4bd4 --- /dev/null +++ b/pyfpdb/upd_indexes.sql @@ -0,0 +1,27 @@ + +# script to update indexes on mysql (+other?) database + +select '1. Dropping indexes' as ' '; +select 'Can''t drop messages indicate index already gone' as ' '; + +ALTER TABLE `fpdb`.`Settings` DROP INDEX `id`; +ALTER TABLE `fpdb`.`Sites` DROP INDEX `id`; +ALTER TABLE `fpdb`.`Gametypes` DROP INDEX `id`; +ALTER TABLE `fpdb`.`Players` DROP INDEX `id`; +ALTER TABLE `fpdb`.`Autorates` DROP INDEX `id`; +ALTER TABLE `fpdb`.`Hands` DROP INDEX `id`; +ALTER TABLE `fpdb`.`BoardCards` DROP INDEX `id`; +ALTER TABLE `fpdb`.`TourneyTypes` DROP INDEX `id`; +ALTER TABLE `fpdb`.`Tourneys` DROP INDEX `id`; +ALTER TABLE `fpdb`.`TourneysPlayers` DROP INDEX `id`; +ALTER TABLE `fpdb`.`HandsPlayers` DROP INDEX `id`; +ALTER TABLE `fpdb`.`HandsActions` DROP INDEX `id`; +ALTER TABLE `fpdb`.`HudCache` DROP INDEX `id`; + +select '2. Adding extra indexes on useful fields' as ' '; +select 'Duplicate key name messages indicate new indexes already there' as ' '; + +ALTER TABLE `fpdb`.`tourneys` ADD INDEX `siteTourneyNo`(`siteTourneyNo`); +ALTER TABLE `fpdb`.`hands` ADD INDEX `siteHandNo`(`siteHandNo`); +ALTER TABLE `fpdb`.`players` ADD INDEX `name`(`name`); + From 160db086897efe737700378065cef7f3a977dc6e Mon Sep 17 00:00:00 2001 From: sqlcoder Date: Wed, 3 Dec 2008 23:14:03 +0000 Subject: [PATCH 04/35] calc totalprofit for hud --- pyfpdb/fpdb_parse_logic.py | 15 ++++++++++++--- pyfpdb/fpdb_simple.py | 31 +++++++++++++++++++++++++------ 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/pyfpdb/fpdb_parse_logic.py b/pyfpdb/fpdb_parse_logic.py index d704d811..6652ba1f 100644 --- a/pyfpdb/fpdb_parse_logic.py +++ b/pyfpdb/fpdb_parse_logic.py @@ -131,9 +131,13 @@ def mainParser(db, cursor, site, category, hand): totalWinnings+=winnings[i] if base=="hold": - hudImportData=fpdb_simple.generateHudCacheData(playerIDs, base, category, actionTypes, allIns, actionTypeByNo, winnings, totalWinnings, positions) + hudImportData=fpdb_simple.generateHudCacheData(playerIDs, base, category, actionTypes + , allIns, actionTypeByNo, winnings, totalWinnings, positions + , actionTypes, actionAmounts) else: - hudImportData=fpdb_simple.generateHudCacheData(playerIDs, base, category, actionTypes, allIns, actionTypeByNo, winnings, totalWinnings, None) + hudImportData=fpdb_simple.generateHudCacheData(playerIDs, base, category, actionTypes + , allIns, actionTypeByNo, winnings, totalWinnings, None + , actionTypes, actionAmounts) if isTourney: ranks=[] @@ -151,7 +155,12 @@ def mainParser(db, cursor, site, category, hand): raise fpdb_simple.FpdbError ("unrecognised category") else: if base=="hold": - result = fpdb_save_to_db.ring_holdem_omaha(db, cursor, base, category, siteHandNo, gametypeID, handStartTime, names, playerIDs, startCashes, positions, cardValues, cardSuits, boardValues, boardSuits, winnings, rakes, actionTypes, allIns, actionAmounts, actionNos, hudImportData, maxSeats, tableName, seatNos) + result = fpdb_save_to_db.ring_holdem_omaha(db, cursor, base, category, siteHandNo + ,gametypeID, handStartTime, names, playerIDs + ,startCashes, positions, cardValues, cardSuits + ,boardValues, boardSuits, winnings, rakes + ,actionTypes, allIns, actionAmounts, actionNos + ,hudImportData, maxSeats, tableName, seatNos) elif base=="stud": result = fpdb_save_to_db.ring_stud(db, cursor, base, category, siteHandNo, gametypeID, handStartTime, names, playerIDs, startCashes, antes, cardValues, diff --git a/pyfpdb/fpdb_simple.py b/pyfpdb/fpdb_simple.py index ecd9def8..571223c4 100644 --- a/pyfpdb/fpdb_simple.py +++ b/pyfpdb/fpdb_simple.py @@ -1241,7 +1241,8 @@ def storeActions(cursor, handsPlayersIds, actionTypes, allIns, actionAmounts, ac for i in range (len(actionTypes)): #iterate through streets for j in range (len(actionTypes[i])): #iterate through names for k in range (len(actionTypes[i][j])): #iterate through individual actions of that player on that street - cursor.execute ("INSERT INTO HandsActions (handPlayerId, street, actionNo, action, allIn, amount) VALUES (%s, %s, %s, %s, %s, %s)", (handsPlayersIds[j], i, actionNos[i][j][k], actionTypes[i][j][k], allIns[i][j][k], actionAmounts[i][j][k])) + cursor.execute ("INSERT INTO HandsActions (handPlayerId, street, actionNo, action, allIn, amount) VALUES (%s, %s, %s, %s, %s, %s)" + , (handsPlayersIds[j], i, actionNos[i][j][k], actionTypes[i][j][k], allIns[i][j][k], actionAmounts[i][j][k])) #end def storeActions def store_board_cards(cursor, hands_id, board_values, board_suits): @@ -1380,8 +1381,12 @@ def store_hands_players_stud_tourney(db, cursor, hands_id, player_ids, start_cas return result #end def store_hands_players_stud_tourney -def generateHudCacheData(player_ids, base, category, action_types, allIns, actionTypeByNo, winnings, totalWinnings, positions): - """calculates data for the HUD during import. IMPORTANT: if you change this method make sure to also change the following storage method and table_viewer.prepare_data if necessary""" +def generateHudCacheData(player_ids, base, category, action_types, allIns, actionTypeByNo + ,winnings, totalWinnings, positions, actionTypes, actionAmounts): + """calculates data for the HUD during import. IMPORTANT: if you change this method make + sure to also change the following storage method and table_viewer.prepare_data if necessary + """ + #print "generateHudCacheData, len(player_ids)=", len(player_ids) #setup subarrays of the result dictionary. street0VPI=[] street0Aggr=[] @@ -1901,8 +1906,14 @@ def generateHudCacheData(player_ids, base, category, action_types, allIns, actio street3CheckCallRaiseDone=[] street4CheckCallRaiseChance=[] street4CheckCallRaiseDone=[] - for player in range (len(player_ids)): - myTotalProfit=0 + #print "b4 totprof calc, len(playerIds)=", len(player_ids) + for pl in range (len(player_ids)): + #print "pl=", pl + myTotalProfit=winnings[pl] # still need to deduct costs + for i in range (len(actionTypes)): #iterate through streets + #for j in range (len(actionTypes[i])): #iterate through names (using pl loop above) + for k in range (len(actionTypes[i][pl])): #iterate through individual actions of that player on that street + myTotalProfit -= actionAmounts[i][pl][k] myStreet1CheckCallRaiseChance=False myStreet1CheckCallRaiseDone=False @@ -1913,7 +1924,9 @@ def generateHudCacheData(player_ids, base, category, action_types, allIns, actio myStreet4CheckCallRaiseChance=False myStreet4CheckCallRaiseDone=False + #print "myTotalProfit=", myTotalProfit totalProfit.append(myTotalProfit) + #print "totalProfit[]=", totalProfit street1CheckCallRaiseChance.append(myStreet1CheckCallRaiseChance) street1CheckCallRaiseDone.append(myStreet1CheckCallRaiseDone) @@ -1925,6 +1938,7 @@ def generateHudCacheData(player_ids, base, category, action_types, allIns, actio street4CheckCallRaiseDone.append(myStreet4CheckCallRaiseDone) result['totalProfit']=totalProfit + #print "res[totalProfit]=", result['totalProfit'] result['street1CheckCallRaiseChance']=street1CheckCallRaiseChance result['street1CheckCallRaiseDone']=street1CheckCallRaiseDone @@ -1960,6 +1974,8 @@ def generateFoldToCB(street, playerIDs, didStreetCB, streetCBDone, foldToStreetC def storeHudCache(cursor, base, category, gametypeId, playerIds, hudImportData): # if (category=="holdem" or category=="omahahi" or category=="omahahilo"): + #print "storeHudCache, len(playerIds)=", len(playerIds), " len(vpip)=" \ + #, len(hudImportData['street0VPI']), " len(totprof)=", len(hudImportData['totalProfit']) for player in range (len(playerIds)): if base=="hold": cursor.execute("SELECT * FROM HudCache WHERE gametypeId+0=%s AND playerId=%s AND activeSeats=%s AND position=%s", (gametypeId, playerIds[player], len(playerIds), hudImportData['position'][player])) @@ -2044,7 +2060,10 @@ def storeHudCache(cursor, base, category, gametypeId, playerIds, hudImportData): if hudImportData['foldToStreet4CBChance'][player]: row[50]+=1 if hudImportData['foldToStreet4CBDone'][player]: row[51]+=1 - row[52]+=hudImportData['totalProfit'][player] + #print "player=", player + #print "len(totalProfit)=", len(hudImportData['totalProfit']) + if hudImportData['totalProfit'][player]: + row[52]+=hudImportData['totalProfit'][player] if hudImportData['street1CheckCallRaiseChance'][player]: row[53]+=1 if hudImportData['street1CheckCallRaiseDone'][player]: row[54]+=1 From 92656ae6a0317ccf84527cf15d1d85436dbcd23c Mon Sep 17 00:00:00 2001 From: Matt Turnbull Date: Wed, 3 Dec 2008 23:57:20 +0000 Subject: [PATCH 05/35] hole cards / community cards extracted and added to printout --- pyfpdb/EverleafToFpdb.py | 25 +++----- pyfpdb/HandHistoryConverter.py | 102 +++++++++++++++++++-------------- 2 files changed, 67 insertions(+), 60 deletions(-) mode change 100644 => 100755 pyfpdb/EverleafToFpdb.py diff --git a/pyfpdb/EverleafToFpdb.py b/pyfpdb/EverleafToFpdb.py old mode 100644 new mode 100755 index b9df28e2..6d050065 --- a/pyfpdb/EverleafToFpdb.py +++ b/pyfpdb/EverleafToFpdb.py @@ -73,14 +73,14 @@ class Everleaf(HandHistoryConverter): self.rexx.setPlayerInfoRegex('Seat (?P[0-9]+): (?P.*) \( \$ (?P[.0-9]+) USD \)') self.rexx.setPostSbRegex('.*\n(?P.*): posts small blind \[') self.rexx.setPostBbRegex('.*\n(?P.*): posts big blind \[') - self.rexx.setHeroCardsRegex('.*\nDealt\sto\s(?P.*)\s\[ (?P.*) \]') + self.rexx.setHeroCardsRegex('.*\nDealt\sto\s(?P.*)\s\[ (?P\S\S), (?P\S\S) \]') self.rexx.setActionStepRegex('.*\n(?P.*) (?Pbets|checks|raises|calls|folds)(\s\[\$ (?P[.\d]+) USD\])?') self.rexx.compileRegexes() - def readSupportedGames(self): + def readSupportedGames(self): pass - def determineGameType(self): + def determineGameType(self): # Cheating with this regex, only support nlhe at the moment gametype = ["ring", "hold", "nl"] @@ -110,7 +110,7 @@ class Everleaf(HandHistoryConverter): int(m.group('HR')), int(m.group('MIN')), int(m.group('SEC'))) hand.buttonpos = int(m.group('BUTTON')) - def readPlayerStacks(self, hand): + def readPlayerStacks(self, hand): m = self.rexx.player_info_re.finditer(hand.string) players = [] @@ -121,12 +121,12 @@ class Everleaf(HandHistoryConverter): def markStreets(self, hand): # PREFLOP = ** Dealing down cards ** - m = re.search('(\*\* Dealing down cards \*\*\n)(?P.*?\n\*\*)?( Dealing Flop \*\*)?(?P.*?\*\*)?( Dealing Turn \*\*)?(?P.*?\*\*)?( Dealing River \*\*)?(?P.*)', hand.string,re.DOTALL) + m = re.search('(\*\* Dealing down cards \*\*\n)(?P.*?\n\*\*)?( Dealing Flop \*\* \[ (?P\S\S), (?P\S\S), (?P\S\S) \])?(?P.*?\*\*)?( Dealing Turn \*\* \[ (?P\S\S) \])?(?P.*?\*\*)?( Dealing River \*\* \[ (?P\S\S) \])?(?P.*)', hand.string,re.DOTALL) # for street in m.groupdict(): # print "DEBUG: Street: %s\tspan: %s" %(street, str(m.span(street))) hand.streets = m - def readBlinds(self, hand): + def readBlinds(self, hand): try: m = self.rexx.small_blind_re.search(hand.string) hand.posted = [m.group('PNAME')] @@ -143,16 +143,9 @@ class Everleaf(HandHistoryConverter): hand.involved = False else: hand.hero = m.group('PNAME') - hand.holecards = m.group('HOLECARDS') - hand.holecards = hand.holecards.replace(',','') - #Must be a better way to do the following tr akqjt AKQJT - hand.holecards = hand.holecards.replace('a','A') - hand.holecards = hand.holecards.replace('k','K') - hand.holecards = hand.holecards.replace('q','Q') - hand.holecards = hand.holecards.replace('j','J') - hand.holecards = hand.holecards.replace('t','T') + hand.addHoleCards(m.group('HOLE1'), m.group('HOLE2')) - def readAction(self, hand, street): + def readAction(self, hand, street): m = self.rexx.action_re.finditer(hand.streets.group(street)) hand.actions[street] = [] for action in m: @@ -165,7 +158,7 @@ class Everleaf(HandHistoryConverter): if __name__ == "__main__": c = Configuration.Config() - e = Everleaf(c, "regression-test-files/everleaf/Speed_Kuala.txt") + e = Everleaf(c, "Speed_Kuala.txt") e.processFile() print str(e) diff --git a/pyfpdb/HandHistoryConverter.py b/pyfpdb/HandHistoryConverter.py index 3050b39e..85b28a94 100644 --- a/pyfpdb/HandHistoryConverter.py +++ b/pyfpdb/HandHistoryConverter.py @@ -104,7 +104,7 @@ class HandHistoryConverter: def readAction(self, hand, street): abstract def sanityCheck(self): - sane = False + sane = True base_w = False #Check if hhbase exists and is writable #Note: Will not try to create the base HH directory @@ -175,7 +175,7 @@ class HandHistoryConverter: print "XXXXXXXXX FIXME XXXXXXXX" print "*** HOLE CARDS ***" - print "Dealt to %s [%s]" %(hand.hero ,hand.holecards) + print "Dealt to %s [%s %s]" %(hand.hero , hand.holecards[0], hand.holecards[1]) # ## ACTION STUFF # This is no limit only at the moment @@ -184,17 +184,17 @@ class HandHistoryConverter: self.printActionLine(act, 0) if 'FLOP' in hand.actions: - print "*** FLOP *** [%s]" %("XXXXX Flop cards XXXXXX") + print "*** FLOP *** [%s %s %s]" %(hand.streets.group("FLOP1"), hand.streets.group("FLOP2"), hand.streets.group("FLOP3")) for act in hand.actions['FLOP']: self.printActionLine(act, 0) if 'TURN' in hand.actions: - print "*** TURN *** [%s] [%s]" %("XXXXX Flop cards XXXXXX", "XXXXX Turn Card XXXXX") + print "*** TURN *** [%s %s %s] [%s]" %(hand.streets.group("FLOP1"), hand.streets.group("FLOP2"), hand.streets.group("FLOP3"), hand.streets.group("TURN1")) for act in hand.actions['TURN']: self.printActionLine(act, 0) if 'RIVER' in hand.actions: - print "*** RIVER *** [%s %s] [%s]" %("XXXXX Flop cards XXXXXX", "XXXXX Turn Card XXXXX", "XXXXX River Card XXXXX") + print "*** RIVER *** [%s %s %s %s] [%s]" %(hand.streets.group("FLOP1"), hand.streets.group("FLOP2"), hand.streets.group("FLOP3"), hand.streets.group("TURN1"), hand.streets.group("RIVER1")) for act in hand.actions['RIVER']: self.printActionLine(act, 0) @@ -232,45 +232,59 @@ class HandHistoryConverter: class Hand: # def __init__(self, sitename, gametype, sb, bb, string): - def __init__(self, sitename, gametype, string): - self.sitename = sitename - self.gametype = gametype - self.string = string - self.streets = None # A MatchObject using a groupnames to identify streets. - self.actions = {} + ups = {'a':'A', 't':'T', 'j':'J', 'q':'Q', 'k':'K'} - self.handid = 0 - self.sb = gametype[3] - self.bb = gametype[4] - self.tablename = "Slartibartfast" - self.maxseats = 10 - self.counted_seats = 0 - self.buttonpos = 0 - self.seating = [] - self.players = [] - self.posted = [] - self.involved = True - self.hero = "Hiro" - self.holecards = "Xx Xx" - self.action = [] - self.totalpot = 0 - self.rake = 0 + def __init__(self, sitename, gametype, string): + self.sitename = sitename + self.gametype = gametype + self.string = string + + self.streets = None # A MatchObject using a groupnames to identify streets. + self.actions = {} + + self.handid = 0 + self.sb = gametype[3] + self.bb = gametype[4] + self.tablename = "Slartibartfast" + self.maxseats = 10 + self.counted_seats = 0 + self.buttonpos = 0 + self.seating = [] + self.players = [] + self.posted = [] + self.involved = True + self.hero = "Hiro" + self.holecards = "Xx Xx" + self.action = [] + self.totalpot = 0 + self.rake = 0 - def printHand(self): - print self.sitename - print self.gametype - print self.string - print self.handid - print self.sb - print self.bb - print self.tablename - print self.maxseats - print self.counted_seats - print self.buttonpos - print self.seating - print self.players - print self.posted - print self.action - print self.involved - print self.hero + def addHoleCards(self,h1,h2,seat=None): # generalise to add hole cards for a specific seat or player + self.holecards = [self.card(h1), self.card(h2)] + + + def card(self,c): + """upper case the ranks but not suits, 'atjqk' => 'ATJQK'""" + # don't know how to make this 'static' + for k,v in self.ups.items(): + c = c.replace(k,v) + return c + + def printHand(self): + print self.sitename + print self.gametype + print self.string + print self.handid + print self.sb + print self.bb + print self.tablename + print self.maxseats + print self.counted_seats + print self.buttonpos + print self.seating + print self.players + print self.posted + print self.action + print self.involved + print self.hero From fefbcfca65cf11e1408ec9e9572e7760e9544187 Mon Sep 17 00:00:00 2001 From: Worros Date: Thu, 4 Dec 2008 16:15:38 +0900 Subject: [PATCH 06/35] Whitespace churn on Graph viewer --- pyfpdb/GuiGraphViewer.py | 443 +++++++++++++++++++-------------------- 1 file changed, 221 insertions(+), 222 deletions(-) diff --git a/pyfpdb/GuiGraphViewer.py b/pyfpdb/GuiGraphViewer.py index 5b83775a..74e84df2 100644 --- a/pyfpdb/GuiGraphViewer.py +++ b/pyfpdb/GuiGraphViewer.py @@ -24,289 +24,288 @@ from time import time #import pokereval try: - import matplotlib - matplotlib.use('GTK') - from matplotlib.figure import Figure - from matplotlib.backends.backend_gtk import FigureCanvasGTK as FigureCanvas - from matplotlib.backends.backend_gtkagg import NavigationToolbar2GTKAgg as NavigationToolbar - from numpy import arange, cumsum - from pylab import * + import matplotlib + matplotlib.use('GTK') + from matplotlib.figure import Figure + from matplotlib.backends.backend_gtk import FigureCanvasGTK as FigureCanvas + from matplotlib.backends.backend_gtkagg import NavigationToolbar2GTKAgg as NavigationToolbar + from numpy import arange, cumsum + from pylab import * except: - print """Failed to load libs for graphing, graphing will not function. Please in + print """Failed to load libs for graphing, graphing will not function. Please in stall numpy and matplotlib if you want to use graphs.""" - print """This is of no consequence for other parts of the program, e.g. import - and HUD are NOT affected by this problem.""" + print """This is of no consequence for other parts of the program, e.g. import + and HUD are NOT affected by this problem.""" import fpdb_import import fpdb_db class GuiGraphViewer (threading.Thread): - def get_vbox(self): - """returns the vbox of this thread""" - return self.mainHBox - #end def get_vbox - - def generateGraph(self, widget, data): - try: self.canvas.destroy() - except AttributeError: pass + def get_vbox(self): + """returns the vbox of this thread""" + return self.mainHBox + #end def get_vbox - # Whaich sites are selected? - # TODO: - # What hero names for the selected site? - # TODO: + def generateGraph(self, widget, data): + try: self.canvas.destroy() + except AttributeError: pass - name = self.heroes[self.sites] - - if self.sites == "PokerStars": - site=2 - sitename="PokerStars: " - elif self.sites=="Full Tilt": - site=1 - sitename="Full Tilt: " - else: - print "invalid text in site selection in graph, defaulting to PS" - site=2 - - self.fig = Figure(figsize=(5,4), dpi=100) + # Whaich sites are selected? + # TODO: + # What hero names for the selected site? + # TODO: - #Set graph properties - self.ax = self.fig.add_subplot(111) + name = self.heroes[self.sites] - #Get graph data from DB - starttime = time() - line = self.getRingProfitGraph(name, site) - print "Graph generated in: %s" %(time() - starttime) + if self.sites == "PokerStars": + site=2 + sitename="PokerStars: " + elif self.sites=="Full Tilt": + site=1 + sitename="Full Tilt: " + else: + print "invalid text in site selection in graph, defaulting to PS" + site=2 - self.ax.set_title("Profit graph for ring games") + self.fig = Figure(figsize=(5,4), dpi=100) - #Set axis labels and grid overlay properites - self.ax.set_xlabel("Hands", fontsize = 12) - self.ax.set_ylabel("$", fontsize = 12) - self.ax.grid(color='g', linestyle=':', linewidth=0.2) - text = "All Hands, " + sitename + str(name) + "\nProfit: $" + str(line[-1]) + "\nTotal Hands: " + str(len(line)) + #Set graph properties + self.ax = self.fig.add_subplot(111) - self.ax.annotate(text, xy=(10, -10), - xycoords='axes points', - horizontalalignment='left', verticalalignment='top', - fontsize=10) + #Get graph data from DB + starttime = time() + line = self.getRingProfitGraph(name, site) + print "Graph generated in: %s" %(time() - starttime) + + self.ax.set_title("Profit graph for ring games") + + #Set axis labels and grid overlay properites + self.ax.set_xlabel("Hands", fontsize = 12) + self.ax.set_ylabel("$", fontsize = 12) + self.ax.grid(color='g', linestyle=':', linewidth=0.2) + text = "All Hands, " + sitename + str(name) + "\nProfit: $" + str(line[-1]) + "\nTotal Hands: " + str(len(line)) + + self.ax.annotate(text, + xy=(10, -10), + xycoords='axes points', + horizontalalignment='left', verticalalignment='top', + fontsize=10) - #Draw plot - self.ax.plot(line,) + #Draw plot + self.ax.plot(line,) - self.canvas = FigureCanvas(self.fig) # a gtk.DrawingArea - self.graphBox.add(self.canvas) - self.canvas.show() - #end of def showClicked + self.canvas = FigureCanvas(self.fig) # a gtk.DrawingArea + self.graphBox.add(self.canvas) + self.canvas.show() + #end of def showClicked - def getRingProfitGraph(self, name, site): - self.cursor.execute(self.sql.query['getRingProfitAllHandsPlayerIdSite'], (name, site)) - # returns (HandId,Winnings,Costs,Profit) - winnings = self.db.cursor.fetchall() + def getRingProfitGraph(self, name, site): + self.cursor.execute(self.sql.query['getRingProfitAllHandsPlayerIdSite'], (name, site)) + #returns (HandId,Winnings,Costs,Profit) + winnings = self.db.cursor.fetchall() - y=map(lambda x:float(x[3]), winnings) - line = cumsum(y) - return line/100 + y=map(lambda x:float(x[3]), winnings) + line = cumsum(y) + return line/100 #end of def getRingProfitGraph - def createPlayerLine(self, hbox, site, player): - label = gtk.Label(site +" id:") - hbox.pack_start(label, False, False, 0) - label.show() + def createPlayerLine(self, hbox, site, player): + label = gtk.Label(site +" id:") + hbox.pack_start(label, False, False, 0) + label.show() - pname = gtk.Entry() - pname.set_text(player) - pname.set_width_chars(20) - hbox.pack_start(pname, False, True, 0) - #TODO: Need to connect a callback here - pname.connect("changed", self.__set_hero_name, site) - #TODO: Look at GtkCompletion - to fill out usernames - pname.show() + pname = gtk.Entry() + pname.set_text(player) + pname.set_width_chars(20) + hbox.pack_start(pname, False, True, 0) + #TODO: Need to connect a callback here + pname.connect("changed", self.__set_hero_name, site) + #TODO: Look at GtkCompletion - to fill out usernames + pname.show() - self.__set_hero_name(pname, site) + self.__set_hero_name(pname, site) - def __set_hero_name(self, w, site): - self.heroes[site] = w.get_text() - print "DEBUG: settings heroes[%s]: %s"%(site, self.heroes[site]) + def __set_hero_name(self, w, site): + self.heroes[site] = w.get_text() + print "DEBUG: settings heroes[%s]: %s"%(site, self.heroes[site]) - def createSiteLine(self, hbox, site): - cb = gtk.CheckButton(site) - cb.connect('clicked', self.__set_site_select, site) - hbox.pack_start(cb, False, False, 0) - cb.show() + def createSiteLine(self, hbox, site): + cb = gtk.CheckButton(site) + cb.connect('clicked', self.__set_site_select, site) + hbox.pack_start(cb, False, False, 0) + cb.show() - def __set_site_select(self, w, site): - # This doesn't behave as intended - self.site only allows 1 site for the moment. - self.sites = site - print "self.sites set to %s" %(self.sites) + def __set_site_select(self, w, site): + # This doesn't behave as intended - self.site only allows 1 site for the moment. + self.sites = site + print "self.sites set to %s" %(self.sites) - def fillPlayerFrame(self, vbox): - for site in self.conf.supported_sites.keys(): - pathHBox = gtk.HBox(False, 0) - vbox.pack_start(pathHBox, False, True, 0) - pathHBox.show() + def fillPlayerFrame(self, vbox): + for site in self.conf.supported_sites.keys(): + pathHBox = gtk.HBox(False, 0) + vbox.pack_start(pathHBox, False, True, 0) + pathHBox.show() - player = self.conf.supported_sites[site].screen_name - self.createPlayerLine(pathHBox, site, player) + player = self.conf.supported_sites[site].screen_name + self.createPlayerLine(pathHBox, site, player) - def fillSitesFrame(self, vbox): - for site in self.conf.supported_sites.keys(): - hbox = gtk.HBox(False, 0) - vbox.pack_start(hbox, False, True, 0) - hbox.show() - self.createSiteLine(hbox, site) + def fillSitesFrame(self, vbox): + for site in self.conf.supported_sites.keys(): + hbox = gtk.HBox(False, 0) + vbox.pack_start(hbox, False, True, 0) + hbox.show() + self.createSiteLine(hbox, site) - def fillDateFrame(self, vbox): - # Hat tip to Mika Bostrom - calendar code comes from PokerStats - hbox = gtk.HBox() - vbox.pack_start(hbox, False, True, 0) - hbox.show() + def fillDateFrame(self, vbox): + # Hat tip to Mika Bostrom - calendar code comes from PokerStats + hbox = gtk.HBox() + vbox.pack_start(hbox, False, True, 0) + hbox.show() - lbl_start = gtk.Label('From:') - lbl_start.show() + lbl_start = gtk.Label('From:') + lbl_start.show() - btn_start = gtk.Button() - btn_start.set_image(gtk.image_new_from_stock(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)) - btn_start.connect('clicked', self.__calendar_dialog, self.start_date) - btn_start.show() + btn_start = gtk.Button() + btn_start.set_image(gtk.image_new_from_stock(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)) + btn_start.connect('clicked', self.__calendar_dialog, self.start_date) + btn_start.show() - hbox.pack_start(lbl_start, expand=False, padding=3) - hbox.pack_start(btn_start, expand=False, padding=3) - hbox.pack_start(self.start_date, expand=False, padding=2) - self.start_date.show() + hbox.pack_start(lbl_start, expand=False, padding=3) + hbox.pack_start(btn_start, expand=False, padding=3) + hbox.pack_start(self.start_date, expand=False, padding=2) + self.start_date.show() - #New row for end date - hbox = gtk.HBox() - vbox.pack_start(hbox, False, True, 0) - hbox.show() + #New row for end date + hbox = gtk.HBox() + vbox.pack_start(hbox, False, True, 0) + hbox.show() - lbl_end = gtk.Label(' To:') - lbl_end.show() - btn_end = gtk.Button() - btn_end.set_image(gtk.image_new_from_stock(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)) - btn_end.connect('clicked', self.__calendar_dialog, self.end_date) - btn_end.show() + lbl_end = gtk.Label(' To:') + lbl_end.show() + btn_end = gtk.Button() + btn_end.set_image(gtk.image_new_from_stock(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)) + btn_end.connect('clicked', self.__calendar_dialog, self.end_date) + btn_end.show() - btn_clear = gtk.Button(label=' Clear Dates ') - btn_clear.connect('clicked', self.__clear_dates) - btn_clear.show() + btn_clear = gtk.Button(label=' Clear Dates ') + btn_clear.connect('clicked', self.__clear_dates) + btn_clear.show() - hbox.pack_start(lbl_end, expand=False, padding=3) - hbox.pack_start(btn_end, expand=False, padding=3) - hbox.pack_start(self.end_date, expand=False, padding=2) - self.end_date.show() + hbox.pack_start(lbl_end, expand=False, padding=3) + hbox.pack_start(btn_end, expand=False, padding=3) + hbox.pack_start(self.end_date, expand=False, padding=2) + self.end_date.show() - hbox.pack_start(btn_clear, expand=False, padding=15) + hbox.pack_start(btn_clear, expand=False, padding=15) - def __calendar_dialog(self, widget, entry): - d = gtk.Window(gtk.WINDOW_TOPLEVEL) - d.set_title('Pick a date') + def __calendar_dialog(self, widget, entry): + d = gtk.Window(gtk.WINDOW_TOPLEVEL) + d.set_title('Pick a date') - vb = gtk.VBox() - cal = gtk.Calendar() - vb.pack_start(cal, expand=False, padding=0) + vb = gtk.VBox() + cal = gtk.Calendar() + vb.pack_start(cal, expand=False, padding=0) - btn = gtk.Button('Done') - btn.connect('clicked', self.__get_date, cal, entry, d) + btn = gtk.Button('Done') + btn.connect('clicked', self.__get_date, cal, entry, d) - vb.pack_start(btn, expand=False, padding=4) + vb.pack_start(btn, expand=False, padding=4) - d.add(vb) - d.set_position(gtk.WIN_POS_MOUSE) - d.show_all() + d.add(vb) + d.set_position(gtk.WIN_POS_MOUSE) + d.show_all() - def __clear_dates(self, w): - self.start_date.set_text('') - self.end_date.set_text('') + def __clear_dates(self, w): + self.start_date.set_text('') + self.end_date.set_text('') - def __get_dates(self): - t1 = self.start_date.get_text() - t2 = self.end_date.get_text() - return (t1, t2) + def __get_dates(self): + t1 = self.start_date.get_text() + t2 = self.end_date.get_text() + return (t1, t2) - def __get_date(self, widget, calendar, entry, win): + def __get_date(self, widget, calendar, entry, win): # year and day are correct, month is 0..11 - (year, month, day) = calendar.get_date() - month += 1 - ds = '%04d-%02d-%02d' % (year, month, day) - entry.set_text(ds) - win.destroy() + (year, month, day) = calendar.get_date() + month += 1 + ds = '%04d-%02d-%02d' % (year, month, day) + entry.set_text(ds) + win.destroy() - def __init__(self, db, settings, querylist, config, debug=True): - """Constructor for GraphViewer""" - self.debug=debug - #print "start of GraphViewer constructor" - self.db=db - self.cursor=db.cursor - self.settings=settings - self.sql=querylist - self.conf = config + def __init__(self, db, settings, querylist, config, debug=True): + """Constructor for GraphViewer""" + self.debug=debug + #print "start of GraphViewer constructor" + self.db=db + self.cursor=db.cursor + self.settings=settings + self.sql=querylist + self.conf = config - self.sites = "PokerStars" - self.heroes = {} + self.sites = "PokerStars" + self.heroes = {} - # For use in date ranges. - self.start_date = gtk.Entry(max=12) - self.end_date = gtk.Entry(max=12) - self.start_date.set_property('editable', False) - self.end_date.set_property('editable', False) + # For use in date ranges. + self.start_date = gtk.Entry(max=12) + self.end_date = gtk.Entry(max=12) + self.start_date.set_property('editable', False) + self.end_date.set_property('editable', False) - self.mainHBox = gtk.HBox(False, 0) - self.mainHBox.show() + self.mainHBox = gtk.HBox(False, 0) + self.mainHBox.show() - self.leftPanelBox = gtk.VBox(False, 0) - self.graphBox = gtk.VBox(False, 0) + self.leftPanelBox = gtk.VBox(False, 0) + self.graphBox = gtk.VBox(False, 0) - self.hpane = gtk.HPaned() - self.hpane.pack1(self.leftPanelBox) - self.hpane.pack2(self.graphBox) - self.hpane.show() + self.hpane = gtk.HPaned() + self.hpane.pack1(self.leftPanelBox) + self.hpane.pack2(self.graphBox) + self.hpane.show() - self.mainHBox.add(self.hpane) + self.mainHBox.add(self.hpane) - playerFrame = gtk.Frame("Hero:") - playerFrame.set_label_align(0.0, 0.0) - playerFrame.show() - vbox = gtk.VBox(False, 0) - vbox.show() + playerFrame = gtk.Frame("Hero:") + playerFrame.set_label_align(0.0, 0.0) + playerFrame.show() + vbox = gtk.VBox(False, 0) + vbox.show() - self.fillPlayerFrame(vbox) - playerFrame.add(vbox) + self.fillPlayerFrame(vbox) + playerFrame.add(vbox) - sitesFrame = gtk.Frame("Sites:") - sitesFrame.set_label_align(0.0, 0.0) - sitesFrame.show() - vbox = gtk.VBox(False, 0) - vbox.show() + sitesFrame = gtk.Frame("Sites:") + sitesFrame.set_label_align(0.0, 0.0) + sitesFrame.show() + vbox = gtk.VBox(False, 0) + vbox.show() - self.fillSitesFrame(vbox) - sitesFrame.add(vbox) + self.fillSitesFrame(vbox) + sitesFrame.add(vbox) - dateFrame = gtk.Frame("Date:") - dateFrame.set_label_align(0.0, 0.0) - dateFrame.show() - vbox = gtk.VBox(False, 0) - vbox.show() + dateFrame = gtk.Frame("Date:") + dateFrame.set_label_align(0.0, 0.0) + dateFrame.show() + vbox = gtk.VBox(False, 0) + vbox.show() - self.fillDateFrame(vbox) - dateFrame.add(vbox) + self.fillDateFrame(vbox) + dateFrame.add(vbox) - graphButton=gtk.Button("Generate Graph") - graphButton.connect("clicked", self.generateGraph, "cliced data") - graphButton.show() + graphButton=gtk.Button("Generate Graph") + graphButton.connect("clicked", self.generateGraph, "cliced data") + graphButton.show() - self.exportButton=gtk.Button("Export to File") -#@ self.exportButton.connect("clicked", self.exportGraph, "show clicked") - self.exportButton.show() + self.exportButton=gtk.Button("Export to File") +#@ self.exportButton.connect("clicked", self.exportGraph, "show clicked") + self.exportButton.show() - self.leftPanelBox.add(playerFrame) - self.leftPanelBox.add(sitesFrame) - self.leftPanelBox.add(dateFrame) - self.leftPanelBox.add(graphButton) - self.leftPanelBox.add(self.exportButton) + self.leftPanelBox.add(playerFrame) + self.leftPanelBox.add(sitesFrame) + self.leftPanelBox.add(dateFrame) + self.leftPanelBox.add(graphButton) + self.leftPanelBox.add(self.exportButton) + + self.leftPanelBox.show() + self.graphBox.show() - self.leftPanelBox.show() - self.graphBox.show() - - #Note: Assumes PokerStars is in the config - # self.nameEntry.set_text(self.conf.supported_sites["PokerStars"].screen_name) From 862502cdc0ab92cf62e9f254777fa0077fbaaa52 Mon Sep 17 00:00:00 2001 From: Matt Turnbull Date: Fri, 5 Dec 2008 02:40:04 +0000 Subject: [PATCH 07/35] Hand class keeping track of pot total / bet amounts Began to turn hand.printHand into the writeHand method but this may not be necessary after all --- pyfpdb/EverleafToFpdb.py | 33 ++++--- pyfpdb/HandHistoryConverter.py | 159 ++++++++++++++++++++++++++++----- 2 files changed, 159 insertions(+), 33 deletions(-) diff --git a/pyfpdb/EverleafToFpdb.py b/pyfpdb/EverleafToFpdb.py index 6d050065..0e03d0c3 100755 --- a/pyfpdb/EverleafToFpdb.py +++ b/pyfpdb/EverleafToFpdb.py @@ -60,7 +60,6 @@ from HandHistoryConverter import * # ** Dealing River ** [ 6c ] # smaragdar wins $ 102 USD from main pot with a pair of aces [ ad, ah, qs, 8h, 6c ] - class Everleaf(HandHistoryConverter): def __init__(self, config, file): print "Initialising Everleaf converter class" @@ -71,8 +70,9 @@ class Everleaf(HandHistoryConverter): self.rexx.setSplitHandRegex('\n\n\n\n') self.rexx.setHandInfoRegex('.*#(?P[0-9]+)\n.*\nBlinds \$?(?P[.0-9]+)/\$?(?P[.0-9]+) (?P.*) - (?P[0-9]+)/(?P[0-9]+)/(?P[0-9]+) - (?P
[0-9]+):(?P[0-9]+):(?P[0-9]+)\nTable (?P[ a-zA-Z]+)\nSeat (?P
[ a-zA-Z]+)\nSeat (?P
[ a-zA-Z]+)\nSeat (?P