From 37a418484fc3947155e2959da5798e3b18330e1c Mon Sep 17 00:00:00 2001 From: eblade Date: Thu, 8 Jan 2009 06:17:56 -0500 Subject: [PATCH 001/357] Replace all occurences in Tables.py where RegExps and the string 'find()' method were being used for simple string checks, with "in" operator --- pyfpdb/Tables.py | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/pyfpdb/Tables.py b/pyfpdb/Tables.py index f345d4ce..8346a37f 100755 --- a/pyfpdb/Tables.py +++ b/pyfpdb/Tables.py @@ -129,12 +129,14 @@ def discover_posix(c): # xwininfo -root -tree -id 0xnnnnn gets the info on a single window for s in c.get_supported_sites(): params = c.get_site_parameters(s) + +# TODO: We need to make a list of phrases, shared between the WIndows and Unix code!!!!!! if re.search(params['table_finder'], listing): - if re.search('Lobby', listing): continue - if re.search('Instant Hand History', listing): continue - if re.search('\"Full Tilt Poker\"', listing): continue # FTP Lobby - if re.search('History for table:', listing): continue - if re.search('has no name', listing): continue + if 'Lobby' in listing: continue + if 'Instant Hand History' in listing: continue + if '\"Full Tilt Poker\"' in listing: continue + if 'History for table:' in listing: continue + if 'has no name' in listing: continue: info = decode_xwininfo(c, listing) if info['site'] == None: continue if info['title'] == info['exe']: continue @@ -147,8 +149,8 @@ def discover_posix(c): def discover_posix_by_name(c, tablename): """Find an XWindows poker client of the given name.""" for listing in os.popen('xwininfo -root -tree').readlines(): - if re.search(tablename, listing): - if re.search('History for table:', listing): continue + if tablename in listing: + if 'History for table:' in listing: continue info = decode_xwininfo(c, listing) if not info['name'] == tablename: continue return info @@ -195,9 +197,9 @@ def discover_nt(c): titles = {} tables = {} win32gui.EnumWindows(win_enum_handler, titles) - for hwnd in titles.keys(): - if re.search('Logged In as', titles[hwnd], re.IGNORECASE) and not re.search('Lobby', titles[hwnd]): - if re.search('Full Tilt Poker', titles[hwnd]): + for hwnd in titles: + if 'Logged In as' in titles[hwnd] and not 'Lobby' in titles[hwnd]: + if 'Full Tilt Poker' in titles[hwnd]: continue tw = Table_Window() tw.number = hwnd @@ -207,9 +209,11 @@ def discover_nt(c): tw.height = int( height ) - b_width - tb_height tw.x = int( x ) + b_width tw.y = int( y ) + tb_height - if re.search('Logged In as', titles[hwnd]): + +# TODO: Isn't the site being determined by the EXE name it belongs to? is this section of code even useful? cleaning it anyway + if 'Logged In as' in titles[hwnd]: tw.site = "PokerStars" - elif re.search('Logged In As', titles[hwnd]): #wait, what??! + elif 'Logged In As' in titles[hwnd]: tw.site = "Full Tilt" else: tw.site = "Unknown" @@ -226,10 +230,10 @@ def discover_nt_by_name(c, tablename): titles = {} win32gui.EnumWindows(win_enum_handler, titles) for hwnd in titles: - if titles[hwnd].find(tablename) == -1: continue - if titles[hwnd].find("History for table:") > -1: continue - if titles[hwnd].find("HUD:") > -1: continue - if titles[hwnd].find("Chat:") > -1: continue + if not tablename in titles[hwnd]: continue + if 'History for table:' in titles[hwnd]: continue # Everleaf Network HH viewer window + if 'HUD:' in titles[hwnd]: continue # FPDB HUD window + if 'Chat:' in titles[hwnd]: continue # Some sites (FTP? PS? Others?) have seperable or seperately constructed chat windows return decode_windows(c, titles[hwnd], hwnd) return None From 0cd97db5486692e446dd34c8fd7385ba275d80a8 Mon Sep 17 00:00:00 2001 From: eblade Date: Thu, 8 Jan 2009 06:25:25 -0500 Subject: [PATCH 002/357] fix typo from last commit --- pyfpdb/Tables.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpdb/Tables.py b/pyfpdb/Tables.py index 8346a37f..ddd04fc0 100755 --- a/pyfpdb/Tables.py +++ b/pyfpdb/Tables.py @@ -136,7 +136,7 @@ def discover_posix(c): if 'Instant Hand History' in listing: continue if '\"Full Tilt Poker\"' in listing: continue if 'History for table:' in listing: continue - if 'has no name' in listing: continue: + if 'has no name' in listing: continue info = decode_xwininfo(c, listing) if info['site'] == None: continue if info['title'] == info['exe']: continue From 0fd6c4a9b20f1a6197eb51ccd00c7d5881e11626 Mon Sep 17 00:00:00 2001 From: eblade Date: Thu, 8 Jan 2009 10:40:18 -0500 Subject: [PATCH 003/357] fpdb_parse_logic: use 'in' instead of 'find' --- pyfpdb/fpdb_parse_logic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpdb/fpdb_parse_logic.py b/pyfpdb/fpdb_parse_logic.py index d30a7b3f..f13b1d6d 100644 --- a/pyfpdb/fpdb_parse_logic.py +++ b/pyfpdb/fpdb_parse_logic.py @@ -41,7 +41,7 @@ def mainParser(backend, db, cursor, site, category, hand): isTourney=fpdb_simple.isTourney(hand[0]) smallBlindLine=0 for i in range(len(hand)): - if hand[i].find("posts small blind")!=-1 or hand[i].find("posts the small blind")!=-1: + if 'posts small blind' in hand[i] or 'posts the small blind' in hand[i]: if hand[i][-2:] == "$0": continue smallBlindLine=i From 5db308e1b2c0f12d88cd892853762e33bc896aba Mon Sep 17 00:00:00 2001 From: Matt Turnbull Date: Sat, 10 Jan 2009 01:46:23 +0000 Subject: [PATCH 004/357] execute permission on HUD_run_me.py removed huge chunk of duplicate code in fpdb_simple.py (strange git problem -- first 300 lines or so were duplicated in a pull) --- pyfpdb/HUD_run_me.py | 0 pyfpdb/fpdb_simple.py | 303 ------------------------------------------ 2 files changed, 303 deletions(-) mode change 100644 => 100755 pyfpdb/HUD_run_me.py diff --git a/pyfpdb/HUD_run_me.py b/pyfpdb/HUD_run_me.py old mode 100644 new mode 100755 diff --git a/pyfpdb/fpdb_simple.py b/pyfpdb/fpdb_simple.py index 384b2eac..e97102dd 100644 --- a/pyfpdb/fpdb_simple.py +++ b/pyfpdb/fpdb_simple.py @@ -328,309 +328,6 @@ def analyzeDB(fdb): fdb.db.set_isolation_level(1) # go back to normal isolation level #end def analyzeDB - -# Data Structures for index and foreign key creation -# drop_code is an int with possible values: 0 - don't drop for bulk import -# 1 - drop during bulk import -# db differences: -# - note that mysql automatically creates indexes on constrained columns when -# foreign keys are created, while postgres does not. Hence the much longer list -# of indexes is required for postgres. -# all primary keys are left on all the time -# -# table column drop_code - -indexes = [ - [ ] # no db with index 0 - , [ ] # no db with index 1 - , [ # indexes for mysql (list index 2) - {'tab':'Players', 'col':'name', 'drop':0} - , {'tab':'Hands', 'col':'siteHandNo', 'drop':0} - , {'tab':'Tourneys', 'col':'siteTourneyNo', 'drop':0} - ] - , [ # indexes for postgres (list index 3) - {'tab':'Boardcards', 'col':'handId', 'drop':0} - , {'tab':'Gametypes', 'col':'siteId', 'drop':0} - , {'tab':'Hands', 'col':'gametypeId', 'drop':1} - , {'tab':'Hands', 'col':'siteHandNo', 'drop':0} - , {'tab':'HandsActions', 'col':'handplayerId', 'drop':0} - , {'tab':'HandsPlayers', 'col':'handId', 'drop':1} - , {'tab':'HandsPlayers', 'col':'playerId', 'drop':1} - , {'tab':'HandsPlayers', 'col':'tourneysPlayersId', 'drop':0} - , {'tab':'HudCache', 'col':'gametypeId', 'drop':1} - , {'tab':'HudCache', 'col':'playerId', 'drop':0} - , {'tab':'HudCache', 'col':'tourneyTypeId', 'drop':0} - , {'tab':'Players', 'col':'siteId', 'drop':1} - , {'tab':'Players', 'col':'name', 'drop':0} - , {'tab':'Tourneys', 'col':'tourneyTypeId', 'drop':1} - , {'tab':'Tourneys', 'col':'siteTourneyNo', 'drop':0} - , {'tab':'TourneysPlayers', 'col':'playerId', 'drop':0} - , {'tab':'TourneysPlayers', 'col':'tourneyId', 'drop':0} - , {'tab':'TourneyTypes', 'col':'siteId', 'drop':0} - ] - ] - -foreignKeys = [ - [ ] # no db with index 0 - , [ ] # no db with index 1 - , [ # foreign keys for mysql - {'fktab':'Hands', 'fkcol':'gametypeId', 'rtab':'Gametypes', 'rcol':'id', 'drop':1} - , {'fktab':'HandsPlayers', 'fkcol':'handId', 'rtab':'Hands', 'rcol':'id', 'drop':1} - , {'fktab':'HandsPlayers', 'fkcol':'playerId', 'rtab':'Players', 'rcol':'id', 'drop':1} - , {'fktab':'HandsActions', 'fkcol':'handPlayerId', 'rtab':'HandsPlayers', 'rcol':'id', 'drop':1} - , {'fktab':'HudCache', 'fkcol':'gametypeId', 'rtab':'Gametypes', 'rcol':'id', 'drop':1} - , {'fktab':'HudCache', 'fkcol':'playerId', 'rtab':'Players', 'rcol':'id', 'drop':0} - , {'fktab':'HudCache', 'fkcol':'tourneyTypeId', 'rtab':'TourneyTypes', 'rcol':'id', 'drop':1} - ] - , [ # foreign keys for postgres - {'fktab':'Hands', 'fkcol':'gametypeId', 'rtab':'Gametypes', 'rcol':'id', 'drop':1} - , {'fktab':'HandsPlayers', 'fkcol':'handId', 'rtab':'Hands', 'rcol':'id', 'drop':1} - , {'fktab':'HandsPlayers', 'fkcol':'playerId', 'rtab':'Players', 'rcol':'id', 'drop':1} - , {'fktab':'HandsActions', 'fkcol':'handPlayerId', 'rtab':'HandsPlayers', 'rcol':'id', 'drop':1} - , {'fktab':'HudCache', 'fkcol':'gametypeId', 'rtab':'Gametypes', 'rcol':'id', 'drop':1} - , {'fktab':'HudCache', 'fkcol':'playerId', 'rtab':'Players', 'rcol':'id', 'drop':0} - , {'fktab':'HudCache', 'fkcol':'tourneyTypeId', 'rtab':'TourneyTypes', 'rcol':'id', 'drop':1} - ] - ] - - -# MySQL Notes: -# "FOREIGN KEY (handId) REFERENCES Hands(id)" - requires index on Hands.id -# - creates index handId on .handId -# alter table t drop foreign key fk -# alter table t add foreign key (fkcol) references tab(rcol) -# alter table t add constraint c foreign key (fkcol) references tab(rcol) -# (fkcol is used for foreigh key name) - -# mysql to list indexes: -# SELECT table_name, index_name, non_unique, column_name -# FROM INFORMATION_SCHEMA.STATISTICS -# WHERE table_name = 'tbl_name' -# AND table_schema = 'db_name' -# ORDER BY table_name, index_name, seq_in_index -# -# ALTER TABLE Tourneys ADD INDEX siteTourneyNo(siteTourneyNo) -# ALTER TABLE tab DROP INDEX idx - -# mysql to list fks: -# SELECT constraint_name, table_name, column_name, referenced_table_name, referenced_column_name -# FROM information_schema.KEY_COLUMN_USAGE -# WHERE REFERENCED_TABLE_SCHEMA = (your schema name here) -# AND REFERENCED_TABLE_NAME is not null -# ORDER BY TABLE_NAME, COLUMN_NAME; - -# this may indicate missing object -# _mysql_exceptions.OperationalError: (1025, "Error on rename of '.\\fpdb\\hands' to '.\\fpdb\\#sql2-7f0-1b' (errno: 152)") - - -# PG notes: - -# To add a foreign key constraint to a table: -# ALTER TABLE tab ADD CONSTRAINT c FOREIGN KEY (col) REFERENCES t2(col2) MATCH FULL; -# ALTER TABLE tab DROP CONSTRAINT zipchk -# -# Note: index names must be unique across a schema -# CREATE INDEX idx ON tab(col) -# DROP INDEX idx - -def prepareBulkImport(fdb): - """Drop some indexes/foreign keys to prepare for bulk import. - Currently keeping the standalone indexes as needed to import quickly""" - # fdb is a fpdb_db object including backend, db, cursor, sql variables - if fdb.backend == PGSQL: - fdb.db.set_isolation_level(0) # allow table/index operations to work - for fk in foreignKeys[fdb.backend]: - if fk['drop'] == 1: - if fdb.backend == MYSQL_INNODB: - fdb.cursor.execute("SELECT constraint_name " + - "FROM information_schema.KEY_COLUMN_USAGE " + - #"WHERE REFERENCED_TABLE_SCHEMA = 'fpdb' - "WHERE 1=1 " + - "AND table_name = %s AND column_name = %s " + - "AND referenced_table_name = %s " + - "AND referenced_column_name = %s ", - (fk['fktab'], fk['fkcol'], fk['rtab'], fk['rcol']) ) - cons = fdb.cursor.fetchone() - print "preparebulk: cons=", cons - if cons: - print "dropping mysql fk", cons[0], fk['fktab'], fk['fkcol'] - try: - fdb.cursor.execute("alter table " + fk['fktab'] + " drop foreign key " + cons[0]) - except: - pass - elif fdb.backend == PGSQL: - print "dropping pg fk", fk['fktab'], fk['fkcol'] - try: - fdb.cursor.execute("alter table " + fk['fktab'] + " drop constraint " - + fk['fktab'] + '_' + fk['fkcol'] + '_fkey') - except: - pass - else: - print "Only MySQL and Postgres supported so far" - return -1 - - for idx in indexes[fdb.backend]: - if idx['drop'] == 1: - if fdb.backend == MYSQL_INNODB: - print "dropping mysql index ", idx['tab'], idx['col'] - try: - fdb.cursor.execute( "alter table %s drop index %s", (idx['tab'],idx['col']) ) - except: - pass - elif fdb.backend == PGSQL: - print "dropping pg index ", idx['tab'], idx['col'] - # mod to use tab_col for index name? - try: - fdb.cursor.execute( "drop index %s_%s_idx" % (idx['tab'],idx['col']) ) - except: - pass - else: - print "Only MySQL and Postgres supported so far" - return -1 - - if fdb.backend == PGSQL: - fdb.db.set_isolation_level(1) # go back to normal isolation level - fdb.db.commit() # seems to clear up errors if there were any in postgres -#end def prepareBulkImport - -def afterBulkImport(fdb): - """Re-create any dropped indexes/foreign keys after bulk import""" - # fdb is a fpdb_db object including backend, db, cursor, sql variables - if fdb.backend == PGSQL: - fdb.db.set_isolation_level(0) # allow table/index operations to work - for fk in foreignKeys[fdb.backend]: - if fk['drop'] == 1: - if fdb.backend == MYSQL_INNODB: - fdb.cursor.execute("SELECT constraint_name " + - "FROM information_schema.KEY_COLUMN_USAGE " + - #"WHERE REFERENCED_TABLE_SCHEMA = 'fpdb' - "WHERE 1=1 " + - "AND table_name = %s AND column_name = %s " + - "AND referenced_table_name = %s " + - "AND referenced_column_name = %s ", - (fk['fktab'], fk['fkcol'], fk['rtab'], fk['rcol']) ) - cons = fdb.cursor.fetchone() - print "afterbulk: cons=", cons - if cons: - pass - else: - print "creating fk ", fk['fktab'], fk['fkcol'], "->", fk['rtab'], fk['rcol'] - try: - fdb.cursor.execute("alter table " + fk['fktab'] + " add foreign key (" - + fk['fkcol'] + ") references " + fk['rtab'] + "(" - + fk['rcol'] + ")") - except: - pass - elif fdb.backend == PGSQL: - print "creating fk ", fk['fktab'], fk['fkcol'], "->", fk['rtab'], fk['rcol'] - try: - fdb.cursor.execute("alter table " + fk['fktab'] + " add constraint " - + fk['fktab'] + '_' + fk['fkcol'] + '_fkey' - + " foreign key (" + fk['fkcol'] - + ") references " + fk['rtab'] + "(" + fk['rcol'] + ")") - except: - pass - else: - print "Only MySQL and Postgres supported so far" - return -1 - - for idx in indexes[fdb.backend]: - if idx['drop'] == 1: - if fdb.backend == MYSQL_INNODB: - print "creating mysql index ", idx['tab'], idx['col'] - try: - fdb.cursor.execute( "alter table %s add index %s(%s)" - , (idx['tab'],idx['col'],idx['col']) ) - except: - pass - elif fdb.backend == PGSQL: - # mod to use tab_col for index name? - print "creating pg index ", idx['tab'], idx['col'] - try: - print "create index %s_%s_idx on %s(%s)" % (idx['tab'], idx['col'], idx['tab'], idx['col']) - fdb.cursor.execute( "create index %s_%s_idx on %s(%s)" - % (idx['tab'], idx['col'], idx['tab'], idx['col']) ) - except: - print " ERROR! :-(" - pass - else: - print "Only MySQL and Postgres supported so far" - return -1 - - if fdb.backend == PGSQL: - fdb.db.set_isolation_level(1) # go back to normal isolation level - fdb.db.commit() # seems to clear up errors if there were any in postgres -#end def afterBulkImport - -def createAllIndexes(fdb): - """Create new indexes""" - if fdb.backend == PGSQL: - fdb.db.set_isolation_level(0) # allow table/index operations to work - for idx in indexes[fdb.backend]: - if fdb.backend == MYSQL_INNODB: - print "creating mysql index ", idx['tab'], idx['col'] - try: - fdb.cursor.execute( "alter table %s add index %s(%s)" - , (idx['tab'],idx['col'],idx['col']) ) - except: - pass - elif fdb.backend == PGSQL: - # mod to use tab_col for index name? - print "creating pg index ", idx['tab'], idx['col'] - try: - print "create index %s_%s_idx on %s(%s)" % (idx['tab'], idx['col'], idx['tab'], idx['col']) - fdb.cursor.execute( "create index %s_%s_idx on %s(%s)" - % (idx['tab'], idx['col'], idx['tab'], idx['col']) ) - except: - print " ERROR! :-(" - pass - else: - print "Only MySQL and Postgres supported so far" - return -1 - if fdb.backend == PGSQL: - fdb.db.set_isolation_level(1) # go back to normal isolation level -#end def createAllIndexes - -def dropAllIndexes(fdb): - """Drop all standalone indexes (i.e. not including primary keys or foreign keys) - using list of indexes in indexes data structure""" - # maybe upgrade to use data dictionary?? (but take care to exclude PK and FK) - if fdb.backend == PGSQL: - fdb.db.set_isolation_level(0) # allow table/index operations to work - for idx in indexes[fdb.backend]: - if fdb.backend == MYSQL_INNODB: - print "dropping mysql index ", idx['tab'], idx['col'] - try: - fdb.cursor.execute( "alter table %s drop index %s" - , (idx['tab'],idx['col']) ) - except: - pass - elif fdb.backend == PGSQL: - print "dropping pg index ", idx['tab'], idx['col'] - # mod to use tab_col for index name? - try: - fdb.cursor.execute( "drop index %s_%s_idx" - % (idx['tab'],idx['col']) ) - except: - pass - else: - print "Only MySQL and Postgres supported so far" - return -1 - if fdb.backend == PGSQL: - fdb.db.set_isolation_level(1) # go back to normal isolation level -#end def dropAllIndexes - -def analyzeDB(fdb): - """Do whatever the DB can offer to update index/table statistics""" - if fdb.backend == PGSQL: - fdb.db.set_isolation_level(0) # allow vacuum to work - try: - fdb.cursor.execute("vacuum analyze") - except: - print "Error during vacuum" - fdb.db.set_isolation_level(1) # go back to normal isolation level -#end def analyzeDB - class DuplicateError(Exception): def __init__(self, value): self.value = value From d5597f7955c690d172238788b3edde475df4d922 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 12 Jan 2009 19:20:24 -0500 Subject: [PATCH 005/357] HUD_runme.py needs to be executable. --- pyfpdb/HUD_run_me.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 pyfpdb/HUD_run_me.py diff --git a/pyfpdb/HUD_run_me.py b/pyfpdb/HUD_run_me.py old mode 100644 new mode 100755 From fcd70293fbd89a3785bc0c1653fe90ff825e3f66 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 12 Jan 2009 19:21:43 -0500 Subject: [PATCH 006/357] minor formatting changes --- pyfpdb/Database.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 pyfpdb/Database.py diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py old mode 100644 new mode 100755 From cd1d4df96d6fabde92faad5a18d2e67a17b7fc35 Mon Sep 17 00:00:00 2001 From: eblade Date: Wed, 14 Jan 2009 23:05:08 -0500 Subject: [PATCH 007/357] HUD_main: Add site name to list of windows being HUDed, fix weird call convention to the "del" operator, which is not a function. call clean_title() on hud titles being killed, so we actually kill (deep) tables and such HUD: Add function "kill_hud_menu", which is now called from the Kill This HUD menu option, to avoid a potential loop in kill_hud() Record the handle of the HUD's destroy signal, use it to un-register, before performing a kill, also avoiding potential loop there Do not allow kill_hud() to be called twice in the same HUD object, return doing nothing if deleted is already set on it (that should totally solve the loop problem) --- pyfpdb/HUD_main.py | 7 +++++-- pyfpdb/Hud.py | 12 +++++++++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/pyfpdb/HUD_main.py b/pyfpdb/HUD_main.py index 2e89492d..50ec7068 100755 --- a/pyfpdb/HUD_main.py +++ b/pyfpdb/HUD_main.py @@ -65,7 +65,7 @@ def create_HUD(new_hand_id, table, db_name, table_name, max, poker_game, db_conn gtk.gdk.threads_enter() try: - newlabel = gtk.Label(table_name) + newlabel = gtk.Label(table.site + " - " + table_name) eb.add(newlabel) newlabel.show() @@ -98,9 +98,12 @@ def update_HUD(new_hand_id, table_name, config, stat_dict): def HUD_removed(tablename): global hud_dict, eb + tablename = Tables.clean_title(tablename) + # TODO: There's a potential problem here somewhere, that this hacks around .. the table_name as being passed to HUD_create is cleaned, + # but the table.name as being passed here is not cleaned. I don't know why. -eric if tablename in hud_dict and hud_dict[tablename].deleted: eb.remove(hud_dict[tablename].tablehudlabel) - del(hud_dict[tablename]) + del hud_dict[tablename] return False return True diff --git a/pyfpdb/Hud.py b/pyfpdb/Hud.py index 9911a61b..612cd9de 100644 --- a/pyfpdb/Hud.py +++ b/pyfpdb/Hud.py @@ -76,7 +76,7 @@ class Hud: self.main_window = gtk.Window() 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.destroyhandler = self.main_window.connect("destroy", self.kill_hud) self.main_window.set_decorated(False) self.main_window.set_opacity(self.colors["hudopacity"]) @@ -101,7 +101,7 @@ class Hud: 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.connect("activate", self.kill_hud_menu) self.item1.show() self.item2 = gtk.MenuItem('Save Layout') @@ -163,11 +163,17 @@ class Hud: return False def kill_hud(self, *args): + if self.deleted: + return # no killing self twice. for k in self.stat_windows: self.stat_windows[k].window.destroy() - self.main_window.destroy() self.deleted = True + self.main_window.disconnect(self.main_window.destroyhandler) # so we don't potentially infiniteloop in here, right + self.main_window.destroy() HUD_main.HUD_removed(self.table.name) + + def kill_hud_menu(self, *args): + self.main_window.destroy() def reposition_windows(self, *args): for w in self.stat_windows: From 9bf85513275d52e56eec5d64c0eeadab4fed584c Mon Sep 17 00:00:00 2001 From: eblade Date: Thu, 15 Jan 2009 11:09:30 -0500 Subject: [PATCH 008/357] update get_nt_exe() to elevate privileges before calling OpenProcess, bizarre. --- pyfpdb/Tables.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pyfpdb/Tables.py b/pyfpdb/Tables.py index ddd04fc0..6655b7a6 100755 --- a/pyfpdb/Tables.py +++ b/pyfpdb/Tables.py @@ -35,6 +35,7 @@ if os.name == 'nt': import win32process import win32api import win32con + import win32security # FreePokerTools modules import Configuration @@ -250,10 +251,23 @@ def discover_nt_tournament(c, tour_number, tab_number): def get_nt_exe(hwnd): """Finds the name of the executable that the given window handle belongs to.""" + + # Request privileges to enable "debug process", so we can later use PROCESS_VM_READ, retardedly required to GetModuleFileNameEx() + priv_flags = win32security.TOKEN_ADJUST_PRIVILEGES | win32security.TOKEN_QUERY + hToken = win32security.OpenProcessToken (win32api.GetCurrentProcess(), priv_flags) + # enable "debug process" + privilege_id = win32security.LookupPrivilegeValue (None, win32security.SE_DEBUG_NAME) + old_privs = win32security.AdjustTokenPrivileges (hToken, 0, [(privilege_id, win32security.SE_PRIVILEGE_ENABLED)]) + + # Open the process, and query it's filename processid = win32process.GetWindowThreadProcessId(hwnd) pshandle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION | win32con.PROCESS_VM_READ, False, processid[1]) exename = win32process.GetModuleFileNameEx(pshandle, 0) + + # clean up win32api.CloseHandle(pshandle) + win32api.CloseHandle(hToken) + return exename def decode_windows(c, title, hwnd): From a3ff47739580ad098caf6c5377d80e17682c0b39 Mon Sep 17 00:00:00 2001 From: Worros Date: Sat, 17 Jan 2009 00:24:01 +0900 Subject: [PATCH 009/357] Added comment/debug to damaged FTP file Doesn't fix issue, but documents what the bug is in the FTP software, and prints the file name and line number that it occurs in so user can fix. Ideally the parser itself would be able to deal with this. --- pyfpdb/fpdb_import.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/pyfpdb/fpdb_import.py b/pyfpdb/fpdb_import.py index 8b567032..9ff475e6 100644 --- a/pyfpdb/fpdb_import.py +++ b/pyfpdb/fpdb_import.py @@ -201,8 +201,13 @@ class Importer: 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 + + #FTP generates lines looking like: + #Seat 1: IOS Seat 2: kashman59 (big blind) showed [8c 9d] and won ($3.25) with a pair of Eights + #ie. Seat X multiple times on the same line in the summary section, when a new player sits down in the + #middle of the hand. + #TODO: Deal with this properly, either fix the file or make the parsing code work with this line. + seat1=hand[i].find("Seat ") if (seat1!=-1): if (hand[i].find("Seat ", seat1+3)!=-1): damaged=True @@ -216,6 +221,14 @@ class Importer: partial+=1 elif (cancelled or damaged): partial+=1 + if damaged: + print """ + DEBUG: Partial hand triggered by a line containing 'Seat X:' twice. This is a + bug in the FTP software when a player sits down in the middle of a hand. + Adding a newline after the player name will fix the issue + """ + print "File: %s" %(file) + print "Line: %s" %(startpos) else: #normal processing isTourney=fpdb_simple.isTourney(hand[0]) if not isTourney: From ffb037b1fe858f382969bb5a591420ff0be7bdb2 Mon Sep 17 00:00:00 2001 From: Worros Date: Sat, 17 Jan 2009 02:24:00 +0900 Subject: [PATCH 010/357] Added output totals for bulk import --- pyfpdb/GuiBulkImport.py | 4 ++-- pyfpdb/fpdb_import.py | 23 ++++++++++++++++++----- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/pyfpdb/GuiBulkImport.py b/pyfpdb/GuiBulkImport.py index e3bee81c..7aa33527 100644 --- a/pyfpdb/GuiBulkImport.py +++ b/pyfpdb/GuiBulkImport.py @@ -31,8 +31,8 @@ class GuiBulkImport (threading.Thread): self.importer.addImportDirectory(self.path) self.importer.setCallHud(False) starttime = time() - self.importer.runImport() - print "GuiBulkImport.import_dir done in %s" %(time() - starttime) + (stored, dups, partial, errs, ttime) = self.importer.runImport() + print "GuiBulkImport.import_dir done: Stored: %d Dupllicates: %d Partial: %d Errors: %d in %s" %(stored, dups, partial, errs, time() - starttime) def load_clicked(self, widget, data=None): self.inputFile=self.chooser.get_filename() diff --git a/pyfpdb/fpdb_import.py b/pyfpdb/fpdb_import.py index 9ff475e6..4c555a28 100644 --- a/pyfpdb/fpdb_import.py +++ b/pyfpdb/fpdb_import.py @@ -113,10 +113,21 @@ class Importer: #Run full import on filelist def runImport(self): fpdb_simple.prepareBulkImport(self.fdb) + totstored = 0 + totdups = 0 + totpartial = 0 + toterrors = 0 + tottime = 0 for file in self.filelist: - self.import_file_dict(file, self.filelist[file][0], self.filelist[file][1]) + (stored, duplicates, partial, errors, ttime) = self.import_file_dict(file, self.filelist[file][0], self.filelist[file][1]) + totstored += stored + totdups += duplicates + totpartial += partial + toterrors += errors + tottime += ttime fpdb_simple.afterBulkImport(self.fdb) fpdb_simple.analyzeDB(self.fdb) + return (totstored, totdups, totpartial, toterrors, tottime) #Run import on updated files, then store latest update time. def runUpdated(self): @@ -143,10 +154,11 @@ class Importer: # 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) + (stored, duplicates, partial, errors, ttime) = self.import_fpdb_file(file, site) else: #Load filter, and run filtered file though main importer - self.import_fpdb_file(file, site) + (stored, duplicates, partial, errors, ttime) = self.import_fpdb_file(file, site) + return (stored, duplicates, partial, errors, ttime) def import_fpdb_file(self, file, site): @@ -276,7 +288,8 @@ class Importer: 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) + ttime = time() - starttime + print "Total stored:", stored, "duplicates:", duplicates, "partial:", partial, "errors:", errors, " time:", ttime if stored==0: if duplicates>0: @@ -290,7 +303,7 @@ class Importer: #todo: this will cause return of an unstored hand number if the last hand was error or partial self.fdb.db.commit() self.handsId=handsId - return handsId + return (stored, duplicates, partial, errors, ttime) def parseTourneyHistory(self): print "Tourney history parser stub" From 8cf705240c545c7d7f876ce4d63a12d0019f0a7f Mon Sep 17 00:00:00 2001 From: Worros Date: Sat, 17 Jan 2009 02:41:18 +0900 Subject: [PATCH 011/357] Change bulk import output slightly --- pyfpdb/GuiBulkImport.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpdb/GuiBulkImport.py b/pyfpdb/GuiBulkImport.py index 7aa33527..250bd313 100644 --- a/pyfpdb/GuiBulkImport.py +++ b/pyfpdb/GuiBulkImport.py @@ -32,7 +32,7 @@ class GuiBulkImport (threading.Thread): self.importer.setCallHud(False) starttime = time() (stored, dups, partial, errs, ttime) = self.importer.runImport() - print "GuiBulkImport.import_dir done: Stored: %d Dupllicates: %d Partial: %d Errors: %d in %s" %(stored, dups, partial, errs, time() - starttime) + print "GuiBulkImport.import_dir done: Stored: %d Dupllicates: %d Partial: %d Errors: %d in %s seconds - %d/sec" %(stored, dups, partial, errs, ttime, (stored/ttime)) def load_clicked(self, widget, data=None): self.inputFile=self.chooser.get_filename() From b2c135ee12a70b12cf49b9e17dfc9da466691bb1 Mon Sep 17 00:00:00 2001 From: Worros Date: Mon, 19 Jan 2009 11:32:34 +0900 Subject: [PATCH 012/357] Possible fix for problem Eric is reporting in importer --- pyfpdb/fpdb_import.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpdb/fpdb_import.py b/pyfpdb/fpdb_import.py index 4c555a28..7b87dbc2 100644 --- a/pyfpdb/fpdb_import.py +++ b/pyfpdb/fpdb_import.py @@ -182,7 +182,7 @@ class Importer: firstline = self.lines[0] except: # print "import_fpdb_file", file, site, self.lines, "\n" - return + return (0,0,0,1,0) if firstline.find("Tournament Summary")!=-1: print "TODO: implement importing tournament summaries" From 3bc8327d1297fa3f5103bb906dd00395d3b8fbe5 Mon Sep 17 00:00:00 2001 From: Worros Date: Thu, 22 Jan 2009 01:31:39 +0900 Subject: [PATCH 013/357] Remove euro symbol is it barfs python --- pyfpdb/EverleafToFpdb.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyfpdb/EverleafToFpdb.py b/pyfpdb/EverleafToFpdb.py index 41052f66..4e51213f 100755 --- a/pyfpdb/EverleafToFpdb.py +++ b/pyfpdb/EverleafToFpdb.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +# -*- coding: iso-8859-15 -*- # Copyright 2008, Carl Gherardi # # This program is free software; you can redistribute it and/or modify @@ -72,7 +73,7 @@ class Everleaf(HandHistoryConverter): self.rexx.setGameInfoRegex('.*Blinds \$?(?P[.0-9]+)/\$?(?P[.0-9]+)') self.rexx.setSplitHandRegex('\n\n+') self.rexx.setHandInfoRegex('.*#(?P[0-9]+)\n.*\nBlinds \$?(?P[.0-9]+)/\$?(?P[.0-9]+) (?P.*) - (?P\d\d\d\d/\d\d/\d\d - \d\d:\d\d:\d\d)\nTable (?P[ a-zA-Z]+)\nSeat (?P
[ a-zA-Z]+)\nSeat (?P
[ a-zA-Z]+)\nSeat (?P
[ a-zA-Z]+)\nSeat (?P
[ a-zA-Z]+)\nSeat (?P
[ a-zA-Z]+)\nSeat (?P
[- a-zA-Z]+)\nSeat (?P
[- a-zA-Z]+) (\((?P.+)\) )?- \$?(?P[.0-9]+)/\$?(?P[.0-9]+) - (?P[a-zA-Z\' ]+) - (?P.*)') -# self.rexx.setHandInfoRegex('.*#(?P[0-9]+): Table (?P
[ a-zA-Z]+) - \$?(?P[.0-9]+)/\$?(?P[.0-9]+) - (?P.*) - (?P
[0-9]+):(?P[0-9]+) ET - (?P[0-9]+)/(?P[0-9]+)/(?P[0-9]+)Table (?P
[ a-zA-Z]+)\nSeat (?P
[- a-zA-Z]+) (\((?P.+)\) )?- \$?(?P[.0-9]+)/\$?(?P[.0-9]+) - (?P[a-zA-Z\' ]+) - (?P.*)') + self.re_Button = re.compile('The button is in seat #(?P
[- a-zA-Z]+) (\((?P.+)\) )?- \$?(?P[.0-9]+)/\$?(?P[.0-9]+) - (?P[a-zA-Z\' ]+) - (?P.*)') self.re_Button = re.compile('The button is in seat #(?P
[- a-zA-Z]+)\nSeat (?P
[- a-zA-Z]+) (\((?P.+)\) )?- \$?(?P[.0-9]+)/\$?(?P[.0-9]+) - (?P[a-zA-Z\' ]+) - (?P.*)') + self.re_HandInfo = re.compile('.*#(?P[0-9]+): Table (?P
[- a-zA-Z]+) (\((?P.+)\) )?- \$?(?P[.0-9]+)/\$?(?P[.0-9]+) (Ante \$(?P[.0-9]+) )?- (?P[a-zA-Z\' ]+) - (?P.*)') self.re_Button = re.compile('The button is in seat #(?P
[- a-zA-Z]+)\nSeat (?P
[- a-zA-Z]+)") + re_Button = re.compile(r"^Seat (?P
[- a-zA-Z]+)\nSeat (?P
[- a-zA-Z]+) (\((?P.+)\) )?- \$?(?P[.0-9]+)/\$?(?P[.0-9]+) (Ante \$(?P[.0-9]+) )?- (?P[a-zA-Z\' ]+) - (?P.*)') + re_Button = re.compile('^The button is in seat #(?P
[- a-zA-Z]+) (\((?P.+)\) )?- \$?(?P[.0-9]+)/\$?(?P[.0-9]+) (Ante \$(?P[.0-9]+) )?- (?P[a-zA-Z\' ]+) - (?P.*)') - self.re_Button = re.compile('The button is in seat #(?P
[- a-zA-Z]+)") + re_HandInfo = re.compile(r".*#(?P[0-9]+)\n.*\nBlinds \$?(?P[.0-9]+)/\$?(?P[.0-9]+) (?P.*) - (?P\d\d\d\d/\d\d/\d\d - \d\d:\d\d:\d\d)\nTable (?P
[- a-zA-Z]+)", re.MULTILINE) re_Button = re.compile(r"^Seat (?P
[- a-zA-Z]+) (\((?P.+)\) )?- \$?(?P[.0-9]+)/\$?(?P[.0-9]+) (Ante \$(?P[.0-9]+) )?- (?P[a-zA-Z\' ]+) - (?P.*)') - re_Button = re.compile('^The button is in seat #(?P
[- a-zA-Z]+)") re_Button = re.compile(r"^Seat (?P
[- a-zA-Z]+)") + re_HandInfo = re.compile(r".*#(?P[0-9]+)\n.*\n(Blinds )?\$?(?P[.0-9]+)/\$?(?P[.0-9]+) (?P.*) - (?P\d\d\d\d/\d\d/\d\d - \d\d:\d\d:\d\d)\nTable (?P
[- a-zA-Z]+)") re_Button = re.compile(r"^Seat (?P
[- a-zA-Z]+)\'(?P.+?$)?", re.MULTILINE) + re_Button = re.compile('Seat #(?P
[ a-zA-Z]+) - \$?(?P[.0-9]+)/\$?(?P[.0-9]+) - (?P.*) - (?P
[0-9]+):(?P[0-9]+) ET - (?P[0-9]+)/(?P[0-9]+)/(?P[0-9]+)Table (?P
[ a-zA-Z]+)\nSeat (?P
[- a-zA-Z]+)") + re_GameInfo = re.compile(u"^(Blinds )?(?P\$| €|)(?P[.0-9]+)/(?:\$| €)?(?P[.0-9]+) (?PNL|PL|) (?P(Hold\'em|Omaha|7 Card Stud))", re.MULTILINE) + re_HandInfo = re.compile(u".*#(?P[0-9]+)\n.*\n(Blinds )?(?:\$| €|)(?P[.0-9]+)/(?:\$| €|)(?P[.0-9]+) (?P.*) - (?P\d\d\d\d/\d\d/\d\d - \d\d:\d\d:\d\d)\nTable (?P
[- a-zA-Z]+)") # re_GameInfo = re.compile(r".*Blinds \$?(?P[.0-9]+)/\$?(?P[.0-9]+) (?P(NL|PL)) (?P(Hold\'em|Omaha|7 Card Stud))") #re_HandInfo = re.compile(r".*#(?P[0-9]+)\n.*\nBlinds \$?(?P[.0-9]+)/\$?(?P[.0-9]+) (?P.*) - (?P\d\d\d\d/\d\d/\d\d - \d\d:\d\d:\d\d)\nTable (?P
[- a-zA-Z]+)", re.MULTILINE) re_Button = re.compile(r"^Seat (?P
[ a-zA-Z]+) - \$?(?P[.0-9]+)/\$?(?P[.0-9]+) - (?P.*) - (?P
[0-9]+):(?P[0-9]+) ET - (?P[0-9]+)/(?P[0-9]+)/(?P[0-9]+)Table (?P
[ a-zA-Z]+)\nSeat (?P
[- a-zA-Z]+)") -# re_GameInfo = re.compile(r".*Blinds \$?(?P[.0-9]+)/\$?(?P[.0-9]+) (?P(NL|PL)) (?P(Hold\'em|Omaha|7 Card Stud))") - #re_HandInfo = re.compile(r".*#(?P[0-9]+)\n.*\nBlinds \$?(?P[.0-9]+)/\$?(?P[.0-9]+) (?P.*) - (?P\d\d\d\d/\d\d/\d\d - \d\d:\d\d:\d\d)\nTable (?P
[- a-zA-Z]+)", re.MULTILINE) - re_Button = re.compile(r"^Seat (?P
.+$)") + re_Button = re.compile(ur"^Seat (?P
[- a-zA-Z]+) (\((?P.+)\) )?- \$?(?P[.0-9]+)/\$?(?P[.0-9]+) (Ante \$(?P[.0-9]+) )?- (?P[a-zA-Z\' ]+) - (?P.*)') re_Button = re.compile('^The button is in seat #(?P
[- a-zA-Z]+)\'(?P.+?$)?", re.MULTILINE) re_Button = re.compile('Seat #(?P
[- a-zA-Z]+)") re_Button = re.compile(r"^Seat (?P
.+$)", re.MULTILINE) re_Button = re.compile(ur"^Seat (?P
[- a-zA-Z]+)\'(?P.+?$)?", re.MULTILINE) re_Button = re.compile('Seat #(?P
[ a-zA-Z]+)\nSeat (?P
[ a-zA-Z]+)\nSeat (?P
[ a-zA-Z]+)\nSeat (?P
[ a-zA-Z0-9]+) \d-max \(Real Money\)\nSeat (?P
[ a-zA-Z0-9]+) \d-max \(Real Money\)\nSeat (?P
[- a-zA-Z]+)\'(?P.+?$)?", re.MULTILINE) re_Button = re.compile('Seat #(?P
[- a-zA-Z]+)\'(?P.+?$)?", re.MULTILINE) re_Button = re.compile('Seat #(?P
.+$)", re.MULTILINE) diff --git a/pyfpdb/FulltiltToFpdb.py b/pyfpdb/FulltiltToFpdb.py index 6a897bad..f3d30229 100755 --- a/pyfpdb/FulltiltToFpdb.py +++ b/pyfpdb/FulltiltToFpdb.py @@ -28,7 +28,7 @@ class Fulltilt(HandHistoryConverter): # Static regexes re_GameInfo = re.compile('- (?P\$|)?(?P[.0-9]+)/\$?(?P[.0-9]+) (Ante \$(?P[.0-9]+) )?- (?P(No Limit|Pot Limit|Limit))? (?P(Hold\'em|Omaha Hi|Razz))') - re_SplitHands = re.compile(r"\n\n+") + re_SplitHands = re.compile(r"(\n\n+)") re_HandInfo = re.compile('.*#(?P[0-9]+): Table (?P
[- a-zA-Z]+) (\((?P.+)\) )?- \$?(?P[.0-9]+)/\$?(?P[.0-9]+) (Ante \$(?P[.0-9]+) )?- (?P[a-zA-Z\' ]+) - (?P.*)') re_Button = re.compile('^The button is in seat #(?P
[- a-zA-Z]+)\'(?P.+?$)?", re.MULTILINE) re_Button = re.compile('Seat #(?P