diff --git a/pyfpdb/Configuration.py b/pyfpdb/Configuration.py old mode 100755 new mode 100644 index 9093c22f..9c14d16f --- a/pyfpdb/Configuration.py +++ b/pyfpdb/Configuration.py @@ -59,28 +59,44 @@ def get_exec_path(): return sys.path[0] def get_config(file_name, fallback = True): - """Looks in exec dir and in self.default_config_path for a config file.""" - config_path = os.path.join(DIR_SELF, file_name) # look in exec dir - if os.path.exists(config_path) and os.path.isfile(config_path): - return config_path # there is a file in the exec dir so we use it - else: - config_path = os.path.join(DIR_CONFIG, file_name) # look in config dir - if os.path.exists(config_path) and os.path.isfile(config_path): + """Looks in cwd and in self.default_config_path for a config file.""" + exec_dir = get_exec_path() + config_path = os.path.join(exec_dir, file_name) +# print "config_path=", config_path + if os.path.exists(config_path): # there is a file in the cwd + return config_path # so we use it + else: # no file in the cwd, look where it should be in the first place + default_dir = get_default_config_path() + config_path = os.path.join(default_dir, file_name) +# print "config path 2=", config_path + if os.path.exists(config_path): return config_path # No file found if not fallback: return False -# OK, fall back to the .example file, should be in the exec dir - if os.path.exists(os.path.join(DIR_SELF, file_name + ".example")): +# OK, fall back to the .example file, should be in the start dir + if os.path.exists(file_name + ".example"): try: - shutil.copyfile(os.path.join(DIR_SELF, file_name + ".example"), os.path.join(DIR_CONFIG, file_name)) - print "No %s found, using %s.example.\n" % (file_name, file_name) - print "A %s file has been created. You will probably have to edit it." % os.path.join(DIR_CONFIG, file_name) - log.error("No %s found, using %s.example.\n" % (file_name, file_name) ) + print "" + if not os.path.isdir(default_dir): + msg = "Creating directory: '%s'" % (default_dir) + print msg + logging.info(msg) + os.mkdir(default_dir) + shutil.copyfile(file_name + ".example", config_path) + msg = "No %s found in %s or %s\n" % (file_name, exec_dir, default_dir) \ + + "Config file has been created at %s.\n" % config_path \ + + "Edit your screen_name and hand history path in the supported_sites "\ + + "section of the \nPreferences window (Main menu) before trying to import hands." + print msg + logging.info(msg) + file_name = config_path except: - print "No %s found, cannot fall back. Exiting.\n" % file_name + print "Error copying .example file, cannot fall back. Exiting.\n" + sys.stderr.write("Error copying .example file, cannot fall back. Exiting.\n") + sys.stderr.write( str(sys.exc_info()) ) sys.exit() else: print "No %s found, cannot fall back. Exiting.\n" % file_name @@ -94,26 +110,18 @@ def get_logger(file_name, config = "config", fallback = False): try: logging.config.fileConfig(conf) log = logging.getLogger(config) + log.debug("%s logger initialised" % config) return log except: pass log = logging.basicConfig() log = logging.getLogger() - log.error("basicConfig logger initialised") + log.debug("config logger initialised") return log -def check_dir(path, create = True): - """Check if a dir exists, optionally creates if not.""" - if os.path.exists(path): - if os.path.isdir(path): - return path - else: - return False - if create: - print "creating directory %s" % path - else: - return False +# find a logging.conf file and set up logging +log = get_logger("logging.conf") ######################################################################## # application wide consts @@ -121,32 +129,20 @@ def check_dir(path, create = True): APPLICATION_NAME_SHORT = 'fpdb' APPLICATION_VERSION = 'xx.xx.xx' -DIR_SELF = get_exec_path() -DIR_CONFIG = check_dir(get_default_config_path()) -DIR_DATABASE = check_dir(os.path.join(DIR_CONFIG, 'database')) -DIR_LOG = check_dir(os.path.join(DIR_CONFIG, 'log')) - DATABASE_TYPE_POSTGRESQL = 'postgresql' DATABASE_TYPE_SQLITE = 'sqlite' DATABASE_TYPE_MYSQL = 'mysql' -#TODO: should this be a tuple or a dict DATABASE_TYPES = ( DATABASE_TYPE_POSTGRESQL, DATABASE_TYPE_SQLITE, DATABASE_TYPE_MYSQL, ) -# find a logging.conf file and set up logging -log = get_logger("logging.conf", config = "config") -log.debug("config logger initialised") - -# and then log our consts -log.info("DIR SELF = %s" % DIR_SELF) -log.info("DIR CONFIG = %s" % DIR_CONFIG) -log.info("DIR DATABASE = %s" % DIR_DATABASE) -log.info("DIR LOG = %s" % DIR_LOG) -NEWIMPORT = True -LOCALE_ENCODING = locale.getdefaultlocale()[1] +#LOCALE_ENCODING = locale.getdefaultlocale()[1] +LOCALE_ENCODING = locale.getpreferredencoding() +if LOCALE_ENCODING == "US-ASCII": + print "Default encoding set to US-ASCII, defaulting to CP1252 instead -- If you're not on a Mac, please report this problem." + LOCALE_ENCODING = "cp1252" ######################################################################## def string_to_bool(string, default=True): @@ -428,7 +424,8 @@ class Config: if file is not None: # config file path passed in file = os.path.expanduser(file) if not os.path.exists(file): - log.error("Specified configuration file %s not found. Using defaults." % (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 is None: file = get_config("HUD_config.xml", True) diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index d1d24e92..a8456294 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -39,41 +39,38 @@ import string import re import Queue import codecs -import logging import math # pyGTK modules +# FreePokerTools modules +import SQL +import Card +import Tourney +import Charset +from Exceptions import * +import Configuration +log = Configuration.get_logger("logging.conf","db") + + # Other library modules try: import sqlalchemy.pool as pool use_pool = True except ImportError: - logging.info("Not using sqlalchemy connection pool.") + log.info("Not using sqlalchemy connection pool.") use_pool = False try: from numpy import var use_numpy = True except ImportError: - logging.info("Not using numpy to define variance in sqlite.") + log.info("Not using numpy to define variance in sqlite.") use_numpy = False -# FreePokerTools modules -import Configuration -import SQL -import Card -import Tourney -import Charset -from Exceptions import * - -log = Configuration.get_logger("logging.conf", config = "db") -log.debug("db logger initialized.") -encoder = codecs.lookup('utf-8') - DB_VERSION = 119 @@ -247,13 +244,14 @@ class Database: # connect to db self.do_connect(c) - print "connection =", self.connection + if self.backend == self.PGSQL: from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT, ISOLATION_LEVEL_READ_COMMITTED, ISOLATION_LEVEL_SERIALIZABLE #ISOLATION_LEVEL_AUTOCOMMIT = 0 #ISOLATION_LEVEL_READ_COMMITTED = 1 #ISOLATION_LEVEL_SERIALIZABLE = 2 + if self.backend == self.SQLITE and self.database == ':memory:' and self.wrongDbVersion: log.info("sqlite/:memory: - creating") self.recreate_tables() @@ -374,20 +372,20 @@ class Database: print msg raise FpdbError(msg) elif backend == Database.SQLITE: - logging.info("Connecting to SQLite: %(database)s" % {'database':database}) + log.info("Connecting to SQLite: %(database)s" % {'database':database}) import sqlite3 if use_pool: sqlite3 = pool.manage(sqlite3, pool_size=1) else: - logging.warning("SQLite won't work well without 'sqlalchemy' installed.") + log.warning("SQLite won't work well without 'sqlalchemy' installed.") if database != ":memory:": if not os.path.isdir(self.config.dir_databases): print "Creating directory: '%s'" % (self.config.dir_databases) - logging.info("Creating directory: '%s'" % (self.config.dir_databases)) + log.info("Creating directory: '%s'" % (self.config.dir_databases)) os.mkdir(self.config.dir_databases) database = os.path.join(self.config.dir_databases, database) - logging.info(" sqlite db: " + database) + log.info(" sqlite db: " + database) self.connection = sqlite3.connect(database, detect_types=sqlite3.PARSE_DECLTYPES ) sqlite3.register_converter("bool", lambda x: bool(int(x))) sqlite3.register_adapter(bool, lambda x: "1" if x else "0") @@ -397,7 +395,10 @@ class Database: if use_numpy: self.connection.create_aggregate("variance", 1, VARIANCE) else: - logging.warning("Some database functions will not work without NumPy support") + log.warning("Some database functions will not work without NumPy support") + self.cursor = self.connection.cursor() + self.cursor.execute('PRAGMA temp_store=2') # use memory for temp tables/indexes + self.cursor.execute('PRAGMA synchronous=0') # don't wait for file writes to finish else: raise FpdbError("unrecognised database backend:"+backend) @@ -412,7 +413,7 @@ class Database: self.cursor.execute("SELECT * FROM Settings") settings = self.cursor.fetchone() if settings[0] != DB_VERSION: - logging.error("outdated or too new database version (%s) - please recreate tables" + log.error("outdated or too new database version (%s) - please recreate tables" % (settings[0])) self.wrongDbVersion = True except:# _mysql_exceptions.ProgrammingError: @@ -422,11 +423,6 @@ class Database: log.info("failed to read settings table - recreating tables") self.recreate_tables() self.check_version(database=database, create=False) - if not self.wrongDbVersion: - msg = "Edit your screen_name and hand history path in the supported_sites "\ - +"section of the \nPreferences window (Main menu) before trying to import hands" - print "\n%s" % msg - log.warning(msg) else: print "Failed to read settings table - please recreate tables" log.info("failed to read settings table - please recreate tables") @@ -436,7 +432,27 @@ class Database: #end def connect def commit(self): - self.connection.commit() + if self.backend != self.SQLITE: + self.connection.commit() + else: + # sqlite commits can fail because of shared locks on the database (SQLITE_BUSY) + # re-try commit if it fails in case this happened + maxtimes = 5 + pause = 1 + ok = False + for i in xrange(maxtimes): + try: + ret = self.connection.commit() + log.debug("commit finished ok, i = "+str(i)) + ok = True + except: + log.debug("commit "+str(i)+" failed: info=" + str(sys.exc_info()) + + " value=" + str(sys.exc_value)) + sleep(pause) + if ok: break + if not ok: + log.debug("commit failed") + raise FpdbError('sqlite commit failed') def rollback(self): self.connection.rollback() @@ -1313,6 +1329,7 @@ class Database: c.execute("INSERT INTO Sites (name,currency) VALUES ('PartyPoker', 'USD')") c.execute("INSERT INTO Sites (name,currency) VALUES ('Partouche', 'EUR')") c.execute("INSERT INTO Sites (name,currency) VALUES ('Carbon', 'USD')") + c.execute("INSERT INTO Sites (name,currency) VALUES ('PKR', 'USD')") if self.backend == self.SQLITE: c.execute("INSERT INTO TourneyTypes (id, siteId, buyin, fee) VALUES (NULL, 1, 0, 0);") elif self.backend == self.PGSQL: @@ -1752,7 +1769,10 @@ class Database: def insertPlayer(self, name, site_id): result = None - _name = Charset.to_db_utf8(name) + if self.backend == self.SQLITE: + _name = name + else: + _name = Charset.to_db_utf8(name) c = self.get_cursor() q = "SELECT name, id FROM Players WHERE siteid=%s and name=%s" q = q.replace('%s', self.sql.query['placeholder']) @@ -1908,7 +1928,7 @@ class Database: # end def send_finish_msg(): def tRecogniseTourneyType(self, tourney): - logging.debug("Database.tRecogniseTourneyType") + log.debug("Database.tRecogniseTourneyType") typeId = 1 # Check if Tourney exists, and if so retrieve TTypeId : in that case, check values of the ttype cursor = self.get_cursor() @@ -1924,10 +1944,10 @@ class Database: try: len(result) typeId = result[0] - logging.debug("Tourney found in db with Tourney_Type_ID = %d" % typeId) + log.debug("Tourney found in db with Tourney_Type_ID = %d" % typeId) for ev in expectedValues : if ( getattr( tourney, expectedValues.get(ev) ) <> result[ev] ): - logging.debug("TypeId mismatch : wrong %s : Tourney=%s / db=%s" % (expectedValues.get(ev), getattr( tourney, expectedValues.get(ev)), result[ev]) ) + log.debug("TypeId mismatch : wrong %s : Tourney=%s / db=%s" % (expectedValues.get(ev), getattr( tourney, expectedValues.get(ev)), result[ev]) ) typeIdMatch = False #break except: @@ -1937,7 +1957,7 @@ class Database: if typeIdMatch == False : # Check for an existing TTypeId that matches tourney info (buyin/fee, knockout, rebuy, speed, matrix, shootout) # if not found create it - logging.debug("Searching for a TourneyTypeId matching TourneyType data") + log.debug("Searching for a TourneyTypeId matching TourneyType data") cursor.execute (self.sql.query['getTourneyTypeId'].replace('%s', self.sql.query['placeholder']), (tourney.siteId, tourney.buyin, tourney.fee, tourney.isKO, tourney.isRebuy, tourney.speed, tourney.isHU, tourney.isShootout, tourney.isMatrix) @@ -1947,9 +1967,9 @@ class Database: try: len(result) typeId = result[0] - logging.debug("Existing Tourney Type Id found : %d" % typeId) + log.debug("Existing Tourney Type Id found : %d" % typeId) except TypeError: #this means we need to create a new entry - logging.debug("Tourney Type Id not found : create one") + log.debug("Tourney Type Id not found : create one") cursor.execute (self.sql.query['insertTourneyTypes'].replace('%s', self.sql.query['placeholder']), (tourney.siteId, tourney.buyin, tourney.fee, tourney.isKO, tourney.isRebuy, tourney.speed, tourney.isHU, tourney.isShootout, tourney.isMatrix) diff --git a/pyfpdb/Exceptions.py b/pyfpdb/Exceptions.py index fd7c20e3..789c7b83 100644 --- a/pyfpdb/Exceptions.py +++ b/pyfpdb/Exceptions.py @@ -48,5 +48,11 @@ class FpdbPostgresqlNoDatabase(FpdbDatabaseError): def __str__(self): return repr(self.value +" " + self.errmsg) -class DuplicateError(FpdbError): +class FpdbHandError(FpdbError): + pass + +class FpdbHandDuplicate(FpdbHandError): + pass + +class FpdbHandPartial(FpdbHandError): pass diff --git a/pyfpdb/Filters.py b/pyfpdb/Filters.py index fa6d2400..7f75cc4e 100644 --- a/pyfpdb/Filters.py +++ b/pyfpdb/Filters.py @@ -42,6 +42,26 @@ class Filters(threading.Thread): self.conf = db.config self.display = display + # text used on screen stored here so that it can be configured + self.filterText = {'limitsall':'All', 'limitsnone':'None', 'limitsshow':'Show _Limits' + ,'seatsbetween':'Between:', 'seatsand':'And:', 'seatsshow':'Show Number of _Players' + ,'playerstitle':'Hero:', 'sitestitle':'Sites:', 'gamestitle':'Games:' + ,'limitstitle':'Limits:', 'seatstitle':'Number of Players:' + ,'groupstitle':'Grouping:', 'posnshow':'Show Position Stats:' + ,'datestitle':'Date:' + ,'groupsall':'All Players' + ,'limitsFL':'FL', 'limitsNL':'NL', 'limitsPL':'PL', 'ring':'Ring', 'tour':'Tourney' + } + + # Outer Packing box + self.mainVBox = gtk.VBox(False, 0) + + self.label = {} + self.callback = {} + + self.make_filter() + + def make_filter(self): self.sites = {} self.games = {} self.limits = {} @@ -60,16 +80,6 @@ class Filters(threading.Thread): else: print "Either 0 or more than one site matched - EEK" - - # text used on screen stored here so that it can be configured - self.filterText = {'limitsall':'All', 'limitsnone':'None', 'limitsshow':'Show _Limits' - ,'seatsbetween':'Between:', 'seatsand':'And:', 'seatsshow':'Show Number of _Players' - ,'limitstitle':'Limits:', 'seatstitle':'Number of Players:' - ,'groupstitle':'Grouping:', 'posnshow':'Show Position Stats:' - ,'groupsall':'All Players' - ,'limitsFL':'FL', 'limitsNL':'NL', 'limitsPL':'PL', 'ring':'Ring', 'tour':'Tourney' - } - # For use in date ranges. self.start_date = gtk.Entry(max=12) self.end_date = gtk.Entry(max=12) @@ -80,34 +90,28 @@ class Filters(threading.Thread): self.sbGroups = {} self.numHands = 0 - # Outer Packing box - self.mainVBox = gtk.VBox(False, 0) - - playerFrame = gtk.Frame("Hero:") + playerFrame = gtk.Frame() playerFrame.set_label_align(0.0, 0.0) vbox = gtk.VBox(False, 0) self.fillPlayerFrame(vbox, self.display) playerFrame.add(vbox) - self.boxes['player'] = vbox - sitesFrame = gtk.Frame("Sites:") + sitesFrame = gtk.Frame() sitesFrame.set_label_align(0.0, 0.0) vbox = gtk.VBox(False, 0) self.fillSitesFrame(vbox) sitesFrame.add(vbox) - self.boxes['sites'] = vbox # Game types - gamesFrame = gtk.Frame("Games:") + gamesFrame = gtk.Frame() gamesFrame.set_label_align(0.0, 0.0) gamesFrame.show() vbox = gtk.VBox(False, 0) self.fillGamesFrame(vbox) gamesFrame.add(vbox) - self.boxes['games'] = vbox # Limits limitsFrame = gtk.Frame() @@ -144,14 +148,13 @@ class Filters(threading.Thread): groupsFrame.add(vbox) # Date - dateFrame = gtk.Frame("Date:") + dateFrame = gtk.Frame() dateFrame.set_label_align(0.0, 0.0) dateFrame.show() vbox = gtk.VBox(False, 0) self.fillDateFrame(vbox) dateFrame.add(vbox) - self.boxes['date'] = vbox # Buttons self.Button1=gtk.Button("Unnamed 1") @@ -192,6 +195,17 @@ class Filters(threading.Thread): if "Button2" not in self.display or self.display["Button2"] == False: self.Button2.hide() + if 'button1' in self.label and self.label['button1']: + self.Button1.set_label( self.label['button1'] ) + if 'button2' in self.label and self.label['button2']: + self.Button2.set_label( self.label['button2'] ) + if 'button1' in self.callback and self.callback['button1']: + self.Button1.connect("clicked", self.callback['button1'], "clicked") + self.Button1.set_sensitive(True) + if 'button2' in self.callback and self.callback['button2']: + self.Button2.connect("clicked", self.callback['button2'], "clicked") + self.Button2.set_sensitive(True) + def get_vbox(self): """returns the vbox of this thread""" return self.mainVBox @@ -237,17 +251,21 @@ class Filters(threading.Thread): def registerButton1Name(self, title): self.Button1.set_label(title) + self.label['button1'] = title def registerButton1Callback(self, callback): self.Button1.connect("clicked", callback, "clicked") self.Button1.set_sensitive(True) + self.callback['button1'] = callback def registerButton2Name(self, title): self.Button2.set_label(title) + self.label['button2'] = title def registerButton2Callback(self, callback): self.Button2.connect("clicked", callback, "clicked") self.Button2.set_sensitive(True) + self.callback['button2'] = callback def cardCallback(self, widget, data=None): print "DEBUG: %s was toggled %s" % (data, ("OFF", "ON")[widget.get_active()]) @@ -255,7 +273,7 @@ class Filters(threading.Thread): def createPlayerLine(self, hbox, site, player): print 'DEBUG :: add:"%s"' % player label = gtk.Label(site +" id:") - hbox.pack_start(label, False, False, 0) + hbox.pack_start(label, False, False, 3) pname = gtk.Entry() pname.set_text(player) @@ -464,9 +482,22 @@ class Filters(threading.Thread): print "self.groups[%s] set to %s" %(group, self.groups[group]) def fillPlayerFrame(self, vbox, display): + top_hbox = gtk.HBox(False, 0) + vbox.pack_start(top_hbox, False, False, 0) + lbl_title = gtk.Label(self.filterText['playerstitle']) + lbl_title.set_alignment(xalign=0.0, yalign=0.5) + top_hbox.pack_start(lbl_title, expand=True, padding=3) + showb = gtk.Button(label="refresh", stock=None, use_underline=True) + showb.set_alignment(xalign=1.0, yalign=0.5) + showb.connect('clicked', self.__refresh, 'players') + + vbox1 = gtk.VBox(False, 0) + vbox.pack_start(vbox1, False, False, 0) + self.boxes['players'] = vbox1 + for site in self.conf.get_supported_sites(): hBox = gtk.HBox(False, 0) - vbox.pack_start(hBox, False, True, 0) + vbox1.pack_start(hBox, False, True, 0) player = self.conf.supported_sites[site].screen_name _pname = Charset.to_gui(player) @@ -474,7 +505,7 @@ class Filters(threading.Thread): if "GroupsAll" in display and display["GroupsAll"] == True: hbox = gtk.HBox(False, 0) - vbox.pack_start(hbox, False, False, 0) + vbox1.pack_start(hbox, False, False, 0) cb = gtk.CheckButton(self.filterText['groupsall']) cb.connect('clicked', self.__set_group_select, 'allplayers') hbox.pack_start(cb, False, False, 0) @@ -490,11 +521,30 @@ class Filters(threading.Thread): phands.set_width_chars(8) hbox.pack_start(phands, False, False, 0) phands.connect("changed", self.__set_num_hands, site) + top_hbox.pack_start(showb, expand=False, padding=1) def fillSitesFrame(self, vbox): + top_hbox = gtk.HBox(False, 0) + top_hbox.show() + vbox.pack_start(top_hbox, False, False, 0) + + lbl_title = gtk.Label(self.filterText['sitestitle']) + lbl_title.set_alignment(xalign=0.0, yalign=0.5) + top_hbox.pack_start(lbl_title, expand=True, padding=3) + + showb = gtk.Button(label="hide", stock=None, use_underline=True) + showb.set_alignment(xalign=1.0, yalign=0.5) + showb.connect('clicked', self.__toggle_box, 'sites') + showb.show() + top_hbox.pack_start(showb, expand=False, padding=1) + + vbox1 = gtk.VBox(False, 0) + self.boxes['sites'] = vbox1 + vbox.pack_start(vbox1, False, False, 0) + for site in self.conf.get_supported_sites(): hbox = gtk.HBox(False, 0) - vbox.pack_start(hbox, False, True, 0) + vbox1.pack_start(hbox, False, True, 0) self.createSiteLine(hbox, site) #Get db site id for filtering later #self.cursor.execute(self.sql.query['getSiteId'], (site,)) @@ -505,12 +555,26 @@ class Filters(threading.Thread): # print "Either 0 or more than one site matched - EEK" def fillGamesFrame(self, vbox): + top_hbox = gtk.HBox(False, 0) + vbox.pack_start(top_hbox, False, False, 0) + lbl_title = gtk.Label(self.filterText['gamestitle']) + lbl_title.set_alignment(xalign=0.0, yalign=0.5) + top_hbox.pack_start(lbl_title, expand=True, padding=3) + showb = gtk.Button(label="hide", stock=None, use_underline=True) + showb.set_alignment(xalign=1.0, yalign=0.5) + showb.connect('clicked', self.__toggle_box, 'games') + top_hbox.pack_start(showb, expand=False, padding=1) + + vbox1 = gtk.VBox(False, 0) + vbox.pack_start(vbox1, False, False, 0) + self.boxes['games'] = vbox1 + self.cursor.execute(self.sql.query['getGames']) result = self.db.cursor.fetchall() if len(result) >= 1: for line in result: hbox = gtk.HBox(False, 0) - vbox.pack_start(hbox, False, True, 0) + vbox1.pack_start(hbox, False, True, 0) self.createGameLine(hbox, line[0]) else: print "INFO: No games returned from database" @@ -705,8 +769,22 @@ class Filters(threading.Thread): def fillDateFrame(self, vbox): # Hat tip to Mika Bostrom - calendar code comes from PokerStats + top_hbox = gtk.HBox(False, 0) + vbox.pack_start(top_hbox, False, False, 0) + lbl_title = gtk.Label(self.filterText['datestitle']) + lbl_title.set_alignment(xalign=0.0, yalign=0.5) + top_hbox.pack_start(lbl_title, expand=True, padding=3) + showb = gtk.Button(label="hide", stock=None, use_underline=True) + showb.set_alignment(xalign=1.0, yalign=0.5) + showb.connect('clicked', self.__toggle_box, 'dates') + top_hbox.pack_start(showb, expand=False, padding=1) + + vbox1 = gtk.VBox(False, 0) + vbox.pack_start(vbox1, False, False, 0) + self.boxes['dates'] = vbox1 + hbox = gtk.HBox() - vbox.pack_start(hbox, False, True, 0) + vbox1.pack_start(hbox, False, True, 0) lbl_start = gtk.Label('From:') @@ -720,7 +798,7 @@ class Filters(threading.Thread): #New row for end date hbox = gtk.HBox() - vbox.pack_start(hbox, False, True, 0) + vbox1.pack_start(hbox, False, True, 0) lbl_end = gtk.Label(' To:') btn_end = gtk.Button() @@ -736,10 +814,13 @@ class Filters(threading.Thread): hbox.pack_start(btn_clear, expand=False, padding=15) + def __refresh(self, widget, entry): + for w in self.mainVBox.get_children(): + w.destroy() + self.make_filter() + def __toggle_box(self, widget, entry): - if "Limits" not in self.display or self.display["Limits"] == False: - self.boxes[entry].hide() - elif self.boxes[entry].props.visible: + if self.boxes[entry].props.visible: self.boxes[entry].hide() widget.set_label("show") else: diff --git a/pyfpdb/GuiAutoImport.py b/pyfpdb/GuiAutoImport.py index f9f1c335..628f7b7f 100755 --- a/pyfpdb/GuiAutoImport.py +++ b/pyfpdb/GuiAutoImport.py @@ -194,7 +194,7 @@ class GuiAutoImport (threading.Thread): widget.set_label(u' _Stop Autoimport ') if self.pipe_to_hud is None: if os.name == 'nt': - command = "python HUD_main.py " + self.settings['cl_options'] + command = "python "+sys.path[0]+"\\HUD_main.py " + self.settings['cl_options'] bs = 0 else: command = os.path.join(sys.path[0], 'HUD_main.py') diff --git a/pyfpdb/HUD_main.py b/pyfpdb/HUD_main.py index 2a1829ae..df144e92 100755 --- a/pyfpdb/HUD_main.py +++ b/pyfpdb/HUD_main.py @@ -212,6 +212,7 @@ class HUD_main(object): # get basic info about the new hand from the db # if there is a db error, complain, skip hand, and proceed + log.info("HUD_main.read_stdin: hand processing starting ...") try: (table_name, max, poker_game, type, site_id, site_name, num_seats, tour_number, tab_number) = \ self.db_connection.get_table_info(new_hand_id) @@ -237,6 +238,7 @@ class HUD_main(object): try: self.db_connection.init_hud_stat_vars( self.hud_dict[temp_key].hud_params['hud_days'] , self.hud_dict[temp_key].hud_params['h_hud_days']) + t4 = time.time() stat_dict = self.db_connection.get_stats_from_hand(new_hand_id, type, self.hud_dict[temp_key].hud_params, self.hero_ids[site_id]) self.hud_dict[temp_key].stat_dict = stat_dict except KeyError: # HUD instance has been killed off, key is stale @@ -245,6 +247,7 @@ class HUD_main(object): # Unlocks table, copied from end of function self.db_connection.connection.rollback() return + t5 = time.time() cards = self.db_connection.get_cards(new_hand_id) comm_cards = self.db_connection.get_common_cards(new_hand_id) if comm_cards != {}: # stud! @@ -257,10 +260,8 @@ class HUD_main(object): else: # get stats using default params--also get cards self.db_connection.init_hud_stat_vars( self.hud_params['hud_days'], self.hud_params['h_hud_days'] ) - t4 = time.time() stat_dict = self.db_connection.get_stats_from_hand(new_hand_id, type, self.hud_params ,self.hero_ids[site_id], num_seats) - t5 = time.time() cards = self.db_connection.get_cards(new_hand_id) comm_cards = self.db_connection.get_common_cards(new_hand_id) if comm_cards != {}: # stud! diff --git a/pyfpdb/Hand.py b/pyfpdb/Hand.py index 93661d70..9c7ea23f 100644 --- a/pyfpdb/Hand.py +++ b/pyfpdb/Hand.py @@ -229,8 +229,7 @@ db: a connected Database object""" # TourneysPlayers else: log.info("Hand.insert(): hid #: %s is a duplicate" % hh['siteHandNo']) - #Raise Duplicate exception? - pass + raise FpdbHandDuplicate(hh['siteHandNo']) def updateHudCache(self, db): db.storeHudCache(self.dbid_gt, self.dbid_pids, self.starttime, self.stats.getHandsPlayers()) @@ -636,7 +635,7 @@ class HoldemOmahaHand(Hand): if builtFrom == "HHC": hhc.readHandInfo(self) if self.gametype['type'] == 'tour': - self.tablename = "%s %s" % (self.tourNo, self.tablename) + self.tablename = "%s %s" % (self.tourNo, self.tablename) hhc.readPlayerStacks(self) hhc.compilePlayerRegexs(self) hhc.markStreets(self) @@ -932,7 +931,7 @@ class DrawHand(Hand): if builtFrom == "HHC": hhc.readHandInfo(self) if self.gametype['type'] == 'tour': - self.tablename = "%s %s" % (self.tourNo, self.tablename) + self.tablename = "%s %s" % (self.tourNo, self.tablename) hhc.readPlayerStacks(self) hhc.compilePlayerRegexs(self) hhc.markStreets(self) @@ -1128,7 +1127,7 @@ class StudHand(Hand): if builtFrom == "HHC": hhc.readHandInfo(self) if self.gametype['type'] == 'tour': - self.tablename = "%s %s" % (self.tourNo, self.tablename) + self.tablename = "%s %s" % (self.tourNo, self.tablename) hhc.readPlayerStacks(self) hhc.compilePlayerRegexs(self) hhc.markStreets(self) @@ -1220,7 +1219,7 @@ Add a complete on [street] by [player] to [amountTo] # showdownPot INT, /* pot size at sd/street7 */ return (0,0,0,0,0) - + def writeHand(self, fh=sys.__stdout__): # PokerStars format. @@ -1615,4 +1614,3 @@ ORDER BY return h - diff --git a/pyfpdb/Mucked.py b/pyfpdb/Mucked.py index d4c7d688..a6f48605 100755 --- a/pyfpdb/Mucked.py +++ b/pyfpdb/Mucked.py @@ -128,7 +128,8 @@ class Stud_mucked(Aux_Window): self.container.show_all() def update_data(self, new_hand_id, db_connection): - self.mucked_cards.update_data(new_hand_id, db_connection) +# uncomment next line when action is available in the db +# self.mucked_cards.update_data(new_hand_id, db_connection) self.mucked_list.update_data(new_hand_id, db_connection) def update_gui(self, new_hand_id): diff --git a/pyfpdb/fpdb.py b/pyfpdb/fpdb.py index 5136d56b..f202698d 100755 --- a/pyfpdb/fpdb.py +++ b/pyfpdb/fpdb.py @@ -769,7 +769,7 @@ class fpdb: self.status_bar.show() if self.db is not None and self.db.connected: - self.status_bar.set_text("Status: Connected to %s database named %s on host %s" + self.status_bar.set_text("Status: Connected to %s database named %s on host %s" % (self.db.get_backend_name(),self.db.database, self.db.host)) # rollback to make sure any locks are cleared: self.db.rollback() @@ -902,8 +902,8 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt") self.load_profile() self.statusIcon = gtk.StatusIcon() - if os.path.exists('../gfx/fpdb-cards.png'): - self.statusIcon.set_from_file('../gfx/fpdb-cards.png') + if os.path.exists(os.path.join(sys.path[0], '../gfx/fpdb-cards.png')): + self.statusIcon.set_from_file(os.path.join(sys.path[0], '../gfx/fpdb-cards.png')) elif os.path.exists('/usr/share/pixmaps/fpdb-cards.png'): self.statusIcon.set_from_file('/usr/share/pixmaps/fpdb-cards.png') else: diff --git a/pyfpdb/fpdb_import.py b/pyfpdb/fpdb_import.py index fe66fcda..859a6bc0 100644 --- a/pyfpdb/fpdb_import.py +++ b/pyfpdb/fpdb_import.py @@ -365,7 +365,7 @@ class Importer: pass (stored, duplicates, partial, errors, ttime) = self.import_file_dict(self.database, file, self.filelist[file][0], self.filelist[file][1], None) try: - if not os.path.isdir(file): + if not os.path.isdir(file): # Note: This assumes that whatever calls us has an "addText" func self.caller.addText(" %d stored, %d duplicates, %d partial, %d errors (time = %f)" % (stored, duplicates, partial, errors, ttime)) except KeyError: # TODO: Again, what error happens here? fix when we find out .. pass @@ -402,7 +402,7 @@ class Importer: return (0,0,0,0,0) conv = None - (stored, duplicates, partial, errors, ttime) = (0, 0, 0, 0, 0) + (stored, duplicates, partial, errors, ttime) = (0, 0, 0, 0, time()) file = file.decode(Configuration.LOCALE_ENCODING) @@ -437,12 +437,15 @@ class Importer: for hand in handlist: if hand is not None: - #try, except duplicates here? hand.prepInsert(self.database) - hand.insert(self.database) - if self.callHud and hand.dbid_hands != 0: - to_hud.append(hand.dbid_hands) - else: + try: + hand.insert(self.database) + except Exceptions.FpdbHandDuplicate: + duplicates += 1 + else: + if self.callHud and hand.dbid_hands != 0: + to_hud.append(hand.dbid_hands) + else: # TODO: Treat empty as an error, or just ignore? log.error("Hand processed but empty") self.database.commit() # Call hudcache update if not in bulk import mode @@ -460,13 +463,16 @@ class Importer: errors = getattr(hhc, 'numErrors') stored = getattr(hhc, 'numHands') + stored -= duplicates else: # conversion didn't work # TODO: appropriate response? - return (0, 0, 0, 1, 0) + return (0, 0, 0, 1, time() - ttime) else: log.warning("Unknown filter filter_name:'%s' in filter:'%s'" %(filter_name, filter)) - return (0, 0, 0, 1, 0) + return (0, 0, 0, 1, time() - ttime) + + ttime = time() - ttime #This will barf if conv.getStatus != True return (stored, duplicates, partial, errors, ttime) diff --git a/run_fpdb.py b/run_fpdb.py index 78e2803b..570b7dc5 100644 --- a/run_fpdb.py +++ b/run_fpdb.py @@ -24,7 +24,6 @@ sys.path[0] = sys.path[0]+os.sep+"pyfpdb" os.chdir(sys.path[0]) #print "sys.path[0] =", sys.path[0], "cwd =", os.getcwd() - import fpdb