From 93729a479806b59a02425b30ab77ced645e0e5a1 Mon Sep 17 00:00:00 2001 From: Eric Blade Date: Wed, 27 Jan 2010 13:42:03 -0500 Subject: [PATCH 01/12] merge --- run_fpdb.py | 68 ++++++++++++++++++++++++++--------------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/run_fpdb.py b/run_fpdb.py index 24f6a4dd..78e2803b 100755 --- a/run_fpdb.py +++ b/run_fpdb.py @@ -1,34 +1,34 @@ -#!/usr/bin/python - -#Copyright 2008 Carl Gherardi -#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 - -# sys.path[0] holds the directory run_fpdb.py is in -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 - - -if __name__ == "__main__": - me = fpdb.fpdb() - me.main() - exit() +#!/usr/bin/python + +#Copyright 2008 Carl Gherardi +#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 + +# sys.path[0] holds the directory run_fpdb.py is in +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 + + +if __name__ == "__main__": + me = fpdb.fpdb() + me.main() + exit() From 1df418c8e4cf761007c708f4092fa5dbea5c2d16 Mon Sep 17 00:00:00 2001 From: sqlcoder Date: Wed, 27 Jan 2010 23:48:39 +0000 Subject: [PATCH 02/12] create fpdb dir if required, improve sqlite performance :-) --- pyfpdb/Configuration.py | 22 ++++++++--- pyfpdb/Database.py | 85 +++++++++++++++++++++++++---------------- pyfpdb/HUD_main.py | 5 ++- 3 files changed, 72 insertions(+), 40 deletions(-) diff --git a/pyfpdb/Configuration.py b/pyfpdb/Configuration.py index 11d38bf4..09f7ca2e 100755 --- a/pyfpdb/Configuration.py +++ b/pyfpdb/Configuration.py @@ -60,12 +60,14 @@ def get_exec_path(): def get_config(file_name, fallback = True): """Looks in cwd and in self.default_config_path for a config file.""" - config_path = os.path.join(get_exec_path(), file_name) + 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 - config_path = os.path.join(get_default_config_path(), file_name) + 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 @@ -77,14 +79,24 @@ def get_config(file_name, fallback = True): # OK, fall back to the .example file, should be in the start dir if os.path.exists(file_name + ".example"): try: + 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) - print "No %s found, using %s.example.\n" % (file_name, file_name) - print "Config file has been created at %s.\nYou will probably have to edit it." % config_path - sys.stderr.write("No %s found, copying %s.example.\n" % (file_name, file_name) ) + 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 "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 diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index f17f1d33..54690979 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -39,40 +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") - - DB_VERSION = 119 @@ -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() @@ -1750,7 +1766,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']) @@ -1906,7 +1925,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() @@ -1922,10 +1941,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: @@ -1935,7 +1954,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) @@ -1945,9 +1964,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/HUD_main.py b/pyfpdb/HUD_main.py index 62025018..3fcd0fca 100755 --- a/pyfpdb/HUD_main.py +++ b/pyfpdb/HUD_main.py @@ -204,6 +204,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) @@ -230,6 +231,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 @@ -238,6 +240,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! @@ -250,10 +253,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! From 803fcec6dc85fb248a7d98c22d59646faab10a69 Mon Sep 17 00:00:00 2001 From: Eric Blade Date: Thu, 28 Jan 2010 04:45:33 -0500 Subject: [PATCH 03/12] make hud_main call with full path in windows, as has been done in unix --- pyfpdb/GuiAutoImport.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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') From 997483f909fb6ef27b798429a650fcfde1e7db1f Mon Sep 17 00:00:00 2001 From: Eric Blade Date: Thu, 28 Jan 2010 05:19:19 -0500 Subject: [PATCH 04/12] make import timer work again --- pyfpdb/fpdb_import.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/pyfpdb/fpdb_import.py b/pyfpdb/fpdb_import.py index fe66fcda..1df0ad7a 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) @@ -440,6 +440,7 @@ class Importer: #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: @@ -463,10 +464,12 @@ class Importer: 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) From 2be8b9d33be280e1f0edaa4f83e6a552dabcb2a8 Mon Sep 17 00:00:00 2001 From: Eric Blade Date: Thu, 28 Jan 2010 05:19:39 -0500 Subject: [PATCH 05/12] get fpdb icon from absolute path instead of relative to cwd --- pyfpdb/fpdb.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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: From d36ac3f92707fbfbf9c7c78ee65b8a9302304321 Mon Sep 17 00:00:00 2001 From: Eric Blade Date: Thu, 28 Jan 2010 05:55:06 -0500 Subject: [PATCH 06/12] rename DuplicateError to FpdbHandDuplicate, add matching FpdbHandError and FpdbHandPartial (not sure where to implement catching those though) --- pyfpdb/Exceptions.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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 From 3381527afc14f6a1fbfac49078f99bd41f294323 Mon Sep 17 00:00:00 2001 From: Eric Blade Date: Thu, 28 Jan 2010 05:56:17 -0500 Subject: [PATCH 07/12] raise FpdbHandDuplicate error on duplicate insert attempt, catch it in import_file_dict, increment duplicates count --- pyfpdb/Hand.py | 12 +++++------- pyfpdb/fpdb_import.py | 15 +++++++++------ 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/pyfpdb/Hand.py b/pyfpdb/Hand.py index 7023d50c..4fdc555e 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()) @@ -618,7 +617,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) @@ -914,7 +913,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) @@ -1110,7 +1109,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) @@ -1202,7 +1201,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. @@ -1597,4 +1596,3 @@ ORDER BY return h - diff --git a/pyfpdb/fpdb_import.py b/pyfpdb/fpdb_import.py index 1df0ad7a..859a6bc0 100644 --- a/pyfpdb/fpdb_import.py +++ b/pyfpdb/fpdb_import.py @@ -437,13 +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 @@ -461,6 +463,7 @@ class Importer: errors = getattr(hhc, 'numErrors') stored = getattr(hhc, 'numHands') + stored -= duplicates else: # conversion didn't work # TODO: appropriate response? From 7570db60f6ae673fc1f37990fd03265c5a0d5faa Mon Sep 17 00:00:00 2001 From: Eratosthenes Date: Thu, 28 Jan 2010 14:37:45 -0500 Subject: [PATCH 08/12] Make the StudMucked not crash with newimport. --- pyfpdb/Mucked.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyfpdb/Mucked.py b/pyfpdb/Mucked.py index dd424e63..c072fb54 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): From e29491c25cc3cfc32dff4dfc375c2a56400a21df Mon Sep 17 00:00:00 2001 From: Eric Blade Date: Thu, 28 Jan 2010 20:29:43 -0500 Subject: [PATCH 09/12] use getpreferredencoding() instead of getdefaultlocale() to get LOCALE_ENCODING, default to cp1252 if it comes back with US-ASCII (Mac) --- pyfpdb/Configuration.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pyfpdb/Configuration.py b/pyfpdb/Configuration.py index 09f7ca2e..9c14d16f 100644 --- a/pyfpdb/Configuration.py +++ b/pyfpdb/Configuration.py @@ -138,7 +138,11 @@ DATABASE_TYPES = ( DATABASE_TYPE_MYSQL, ) -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): From bacca69c4925dfd7e609f046586ec067fc525228 Mon Sep 17 00:00:00 2001 From: Gerko de Roo Date: Wed, 27 Jan 2010 18:37:23 +0100 Subject: [PATCH 10/12] Default graph created for empty data query --- pyfpdb/GuiGraphViewer.py | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/pyfpdb/GuiGraphViewer.py b/pyfpdb/GuiGraphViewer.py index f0eca2f1..ce9648ae 100644 --- a/pyfpdb/GuiGraphViewer.py +++ b/pyfpdb/GuiGraphViewer.py @@ -176,17 +176,46 @@ class GuiGraphViewer (threading.Thread): (green, blue, red) = self.getRingProfitGraph(playerids, sitenos, limits, games) 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) if green == None or green == []: + self.ax.set_title("No Data for Player(s) Found") + green = ([ 0., 0., 0., 0., 500., 1000., 900., 800., + 700., 600., 500., 400., 300., 200., 100., 0., + 500., 1000., 1000., 1000., 1000., 1000., 1000., 1000., + 1000., 1000., 1000., 1000., 1000., 1000., 875., 750., + 625., 500., 375., 250., 125., 0., 0., 0., + 0., 500., 1000., 900., 800., 700., 600., 500., + 400., 300., 200., 100., 0., 500., 1000., 1000.]) + red = ([ 0., 0., 0., 0., 500., 1000., 900., 800., + 700., 600., 500., 400., 300., 200., 100., 0., + 0., 0., 0., 0., 0., 0., 125., 250., + 375., 500., 500., 500., 500., 500., 500., 500., + 500., 500., 375., 250., 125., 0., 0., 0., + 0., 500., 1000., 900., 800., 700., 600., 500., + 400., 300., 200., 100., 0., 500., 1000., 1000.]) + blue = ([ 0., 0., 0., 0., 500., 1000., 900., 800., + 700., 600., 500., 400., 300., 200., 100., 0., + 0., 0., 0., 0., 0., 0., 125., 250., + 375., 500., 625., 750., 875., 1000., 875., 750., + 625., 500., 375., 250., 125., 0., 0., 0., + 0., 500., 1000., 900., 800., 700., 600., 500., + 400., 300., 200., 100., 0., 500., 1000., 1000.]) + + self.ax.plot(green, color='green', label='Hands: %d\nProfit: $%.2f' %(len(green), green[-1])) + self.ax.plot(blue, color='blue', label='Showdown: $%.2f' %(blue[-1])) + self.ax.plot(red, color='red', label='Non-showdown: $%.2f' %(red[-1])) + self.graphBox.add(self.canvas) + self.canvas.show() + self.canvas.draw() #TODO: Do something useful like alert user - print "No hands returned by graph query" + #print "No hands returned by graph query" else: + self.ax.set_title("Profit graph for ring games") #text = "Profit: $%.2f\nTotal Hands: %d" %(green[-1], len(green)) #self.ax.annotate(text, # xy=(10, -10), @@ -203,7 +232,6 @@ class GuiGraphViewer (threading.Thread): else: self.ax.legend(loc='best', fancybox=True, shadow=True, prop=FontProperties(size='smaller')) - self.graphBox.add(self.canvas) self.canvas.show() self.canvas.draw() @@ -292,7 +320,7 @@ class GuiGraphViewer (threading.Thread): self.db.rollback() if winnings == (): - return None + return (None, None, None) green = map(lambda x:float(x[1]), winnings) blue = map(lambda x: float(x[1]) if x[2] == True else 0.0, winnings) From a23d96e85d2a12902d43c4c5f6912fc664d9dfd6 Mon Sep 17 00:00:00 2001 From: Worros Date: Fri, 29 Jan 2010 17:00:37 +0800 Subject: [PATCH 11/12] Add Carbon back in as default data. Also add PKR --- pyfpdb/Database.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index ee43079c..a8456294 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -1328,6 +1328,8 @@ class Database: c.execute("INSERT INTO Sites (name,currency) VALUES ('Absolute', 'USD')") 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: From 52a176fd0a6ea3efb274120ddcf1c9ba2108eafb Mon Sep 17 00:00:00 2001 From: sqlcoder Date: Fri, 29 Jan 2010 09:12:08 +0000 Subject: [PATCH 12/12] tidy up filter, add ability to refresh (eg after importing more gametypes) --- pyfpdb/Filters.py | 143 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 112 insertions(+), 31 deletions(-) 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: