From 105e8688641c0a98f7020a81e7a15744f25cc991 Mon Sep 17 00:00:00 2001 From: Mika Bostrom Date: Sun, 5 Jul 2009 23:33:09 +0300 Subject: [PATCH 01/45] Catch local connection config If database backend is Postgres and the connection is over domain socket, the only values in node are: * db_name * db_server * db_type Now, for some reason the config reader unconditionally creates "tidy" string representations for all possible keys. This means that host, user and password are all empty strings (''), and not even NoneType entities. To catch the case for postgres, simply treat empty host the same as undefined host. --- pyfpdb/fpdb_db.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pyfpdb/fpdb_db.py b/pyfpdb/fpdb_db.py index f89b5d6d..669ba4f0 100644 --- a/pyfpdb/fpdb_db.py +++ b/pyfpdb/fpdb_db.py @@ -182,7 +182,9 @@ class fpdb_db: # sqlcoder: This database only connect failed in my windows setup?? # Modifed it to try the 4 parameter style if the first connect fails - does this work everywhere? connected = False - if self.host == "localhost" or self.host == "127.0.0.1": + if self.host == None or self.host == '' \ + or self.host == "localhost" \ + or self.host == "127.0.0.1": try: self.db = psycopg2.connect(database = database) connected = True From d3373add8b5e1f3cb948282990be24cd7bafb572 Mon Sep 17 00:00:00 2001 From: Mika Bostrom Date: Mon, 6 Jul 2009 00:00:32 +0300 Subject: [PATCH 02/45] Fix most annoying typo in Postgres case The error message from postgres contains the said table as written in original command. 'Players' != 'players', indeed. Now software can at least start with postgres and an empty database. --- pyfpdb/fpdb_db.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyfpdb/fpdb_db.py b/pyfpdb/fpdb_db.py index 669ba4f0..e3bf3d2a 100644 --- a/pyfpdb/fpdb_db.py +++ b/pyfpdb/fpdb_db.py @@ -207,6 +207,7 @@ class fpdb_db: raise fpdb_simple.FpdbError("unrecognised database backend:"+backend) self.cursor=self.db.cursor() self.cursor.execute('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED') + self.db.commit() # Set up query dictionary as early in the connection process as we can. self.sql = FpdbSQLQueries.FpdbSQLQueries(self.get_backend_name()) self.wrongDbVersion=False @@ -592,7 +593,7 @@ class fpdb_db: #print "... after lock table, status =", self.cursor.statusmessage except: # relation "players" does not exist - if str(sys.exc_value).find('relation "players" does not exist') >= 0: + if str(sys.exc_value).find('relation "Players" does not exist') >= 0: return(2) print "Error! failed to obtain global lock. Close all programs accessing " \ + "database (including fpdb) and try again (%s)." \ From 14dc35ef815da8da40e0de7542a3cd2549280f3d Mon Sep 17 00:00:00 2001 From: Mika Bostrom Date: Mon, 6 Jul 2009 00:15:37 +0300 Subject: [PATCH 03/45] Fix initial run with postgres Simplify error check, so that regardless of how the table name is mangled, we now catch just the meaningful part. When trying to obtain the lock, make sure that there is no transaction block open (which tends to happen on error-paths); flush the database connection before requesting exclusive lock. --- pyfpdb/fpdb_db.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyfpdb/fpdb_db.py b/pyfpdb/fpdb_db.py index e3bf3d2a..2ec96580 100644 --- a/pyfpdb/fpdb_db.py +++ b/pyfpdb/fpdb_db.py @@ -589,11 +589,12 @@ class fpdb_db: return(1) elif self.backend == self.PGSQL: try: + self.db.commit() self.cursor.execute( "lock table Players in exclusive mode nowait" ) #print "... after lock table, status =", self.cursor.statusmessage except: # relation "players" does not exist - if str(sys.exc_value).find('relation "Players" does not exist') >= 0: + if str(sys.exc_value).find('does not exist') >= 0: return(2) print "Error! failed to obtain global lock. Close all programs accessing " \ + "database (including fpdb) and try again (%s)." \ From dd4aeffa4fe55ea75f7a342b7a2aa9c28e0015e0 Mon Sep 17 00:00:00 2001 From: Mika Bostrom Date: Mon, 6 Jul 2009 00:18:46 +0300 Subject: [PATCH 04/45] Remove obnoxious error window Do we really need that annoying error note about table viewer not working any longer? It's useless with postgres backend anyhow, so might as well kill it off entirely. --- pyfpdb/GuiTableViewer.py | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/pyfpdb/GuiTableViewer.py b/pyfpdb/GuiTableViewer.py index 35dbb797..3693481f 100644 --- a/pyfpdb/GuiTableViewer.py +++ b/pyfpdb/GuiTableViewer.py @@ -24,19 +24,11 @@ import fpdb_simple try: import MySQLdb -except: - diaSQLLibMissing = gtk.Dialog(title="Fatal Error - SQL interface library missing", parent=None, flags=0, buttons=(gtk.STOCK_QUIT,gtk.RESPONSE_OK)) - - label = gtk.Label("Please note that the table viewer only works with MySQL, if you use PostgreSQL this error is expected.") - diaSQLLibMissing.vbox.add(label) - label.show() - - label = gtk.Label("Since the HUD now runs on all supported plattforms I do not see any point in table viewer anymore, if you disagree please send a message to steffen@sycamoretest.info") - diaSQLLibMissing.vbox.add(label) - label.show() - - response = diaSQLLibMissing.run() - #sys.exit(1) +except: # Try to make postgres case cleaner, and kill the error note + try: + import psycopg2 + except ImportError: + pass import fpdb_import import fpdb_db From b7ca7a77a7940373fba45004382dace12544d0a5 Mon Sep 17 00:00:00 2001 From: Mika Bostrom Date: Mon, 6 Jul 2009 09:01:49 +0300 Subject: [PATCH 05/45] Work around error with no database Instead of blindly trusting that SELECT ... works, use try-catch to get around errors when the database doesn't have the tables yet. --- pyfpdb/Database.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index 0f354054..243f9aa3 100755 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -72,19 +72,22 @@ class Database: cur = self.connection.cursor() self.hand_1day_ago = 0 - cur.execute(self.sql.query['get_hand_1day_ago']) - row = cur.fetchone() - if row and row[0]: - self.hand_1day_ago = row[0] - #print "hand 1day ago =", self.hand_1day_ago + try: + cur.execute(self.sql.query['get_hand_1day_ago']) + row = cur.fetchone() + if row and row[0]: + self.hand_1day_ago = row[0] + #print "hand 1day ago =", self.hand_1day_ago - d = timedelta(days=self.hud_days) - now = datetime.utcnow() - d - self.date_ndays_ago = "d%02d%02d%02d" % (now.year-2000, now.month, now.day) + d = timedelta(days=self.hud_days) + now = datetime.utcnow() - d + self.date_ndays_ago = "d%02d%02d%02d" % (now.year-2000, now.month, now.day) - self.hand_nhands_ago = 0 # todo - #cur.execute(self.sql.query['get_table_name'], (hand_id, )) - #row = cur.fetchone() + self.hand_nhands_ago = 0 # todo + #cur.execute(self.sql.query['get_table_name'], (hand_id, )) + #row = cur.fetchone() + except: # no tables created yet + pass self.saveActions = False if self.import_options['saveActions'] == False else True def do_connect(self, c): From 71aaf62c40d52306523101353ad8901dfc3c87ef Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 5 Aug 2009 23:25:43 -0400 Subject: [PATCH 06/45] Fix my last fix of previous commit. --- pyfpdb/HandHistoryConverter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpdb/HandHistoryConverter.py b/pyfpdb/HandHistoryConverter.py index bb30e705..76a98e7b 100644 --- a/pyfpdb/HandHistoryConverter.py +++ b/pyfpdb/HandHistoryConverter.py @@ -58,7 +58,7 @@ class HandHistoryConverter(): else: # TODO: out_path should be sanity checked. out_dir = os.path.dirname(self.out_path) - if not os.path.isdir(out_dir): + if not os.path.isdir(out_dir) and out_dir != '': logging.info("Creatin directory '%s'" % out_dir) os.makedirs(out_dir) self.out_fh = open(self.out_path, 'w') From 26016bffb79b8d2ab4d4d2992bbf06d58db1d92c Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 6 Aug 2009 19:27:52 -0400 Subject: [PATCH 07/45] Correctly parse fpp tournaments. --- pyfpdb/PokerStarsToFpdb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpdb/PokerStarsToFpdb.py b/pyfpdb/PokerStarsToFpdb.py index fcfecb84..84653ba0 100755 --- a/pyfpdb/PokerStarsToFpdb.py +++ b/pyfpdb/PokerStarsToFpdb.py @@ -32,7 +32,7 @@ class PokerStars(HandHistoryConverter): mixes = { 'HORSE': 'horse', '8-Game': '8game', 'HOSE': 'hose'} # Legal mixed games sym = {'USD': "\$", 'CAD': "\$", 'T$': "", "EUR": "\x80", "GBP": "\xa3"} # ADD Euro, Sterling, etc HERE substitutions = { - 'LEGAL_ISO' : "USD|EUR|GBP|CAD", # legal ISO currency codes + 'LEGAL_ISO' : "USD|EUR|GBP|CAD|FPP", # legal ISO currency codes 'LS' : "\$|\x80|\xa3" # legal currency symbols ADD Euro, Sterling, etc HERE } From 5c468d0a3877fc4ab09344bb9bc6d5221f35dd96 Mon Sep 17 00:00:00 2001 From: grindi Date: Sat, 8 Aug 2009 15:47:58 +0400 Subject: [PATCH 08/45] Added party hh path into HUD_config.xml.example --- pyfpdb/HUD_config.xml.example | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyfpdb/HUD_config.xml.example b/pyfpdb/HUD_config.xml.example index bd8cb7df..adc8141c 100644 --- a/pyfpdb/HUD_config.xml.example +++ b/pyfpdb/HUD_config.xml.example @@ -252,10 +252,10 @@ From 4074092f322dff53f1ce4bbe619a542e80f83c11 Mon Sep 17 00:00:00 2001 From: grindi Date: Sat, 8 Aug 2009 17:36:48 +0400 Subject: [PATCH 09/45] Removed .gitignore --- .gitignore | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 .gitignore diff --git a/.gitignore b/.gitignore deleted file mode 100644 index f3d74a9a..00000000 --- a/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.pyc -*~ From f431a6307093d88fb7c19182b998af5eace508e5 Mon Sep 17 00:00:00 2001 From: eblade Date: Sat, 8 Aug 2009 15:58:30 -0400 Subject: [PATCH 10/45] badHangs -> badHands --- pyfpdb/HandHistoryConverter.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyfpdb/HandHistoryConverter.py b/pyfpdb/HandHistoryConverter.py index fb7311ab..7d943ada 100644 --- a/pyfpdb/HandHistoryConverter.py +++ b/pyfpdb/HandHistoryConverter.py @@ -96,14 +96,14 @@ Otherwise, finish at eof. else: handsList = self.allHandsAsList() logging.info("Parsing %d hands" % len(handsList)) - nBadHangs = 0 + nBadHands = 0 for handText in handsList: try: self.processedHands.append(self.processHand(handText)) except Exception, e: # TODO: it's better to replace it with s-t like HhcEception - nBadHangs += 1 + nBadHands += 1 logging.error("Caught exception while parsing hand: %s" % str(e)) - numHands = len(handsList) - nBadHangs + numHands = len(handsList) - nBadHands endtime = time.time() print "read %d hands in %.3f seconds" % (numHands, endtime - starttime) if self.out_fh != sys.stdout: From c042bea1817a99a6639c4ecbd66be20d54e76d28 Mon Sep 17 00:00:00 2001 From: grindi Date: Sun, 9 Aug 2009 16:24:31 +0400 Subject: [PATCH 11/45] Improved table-by-name recognition for non-latin window titles What i mean: u'1464739' in 'Speed #1464739 - \xc1\xcb \xd5\xee\xeb\xe4\xe5\xec' -> Exception u'1464739' in 'Speed #1464739 - \xc1\xcb \xd5\xee\xeb\xe4\xe5\xec'.decode('cp1251') -> True --- pyfpdb/Tables.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/pyfpdb/Tables.py b/pyfpdb/Tables.py index 97a897ca..9fdc8630 100755 --- a/pyfpdb/Tables.py +++ b/pyfpdb/Tables.py @@ -230,11 +230,20 @@ def discover_nt_by_name(c, tablename): """Finds poker client window with the given table name.""" titles = {} win32gui.EnumWindows(win_enum_handler, titles) + + def getDefaultEncoding(): + # FIXME: if somebody know better place fot this function - move it + # FIXME: it's better to use GetCPInfo for windows http://msdn.microsoft.com/en-us/library/dd318078(VS.85).aspx + # but i have no idea, how to call it + import locale + return locale.getpreferredencoding() + for hwnd in titles: #print "Tables.py: tablename =", tablename, "title =", titles[hwnd] try: + # maybe it's better to make global titles[hwnd] decoding? # this can blow up in XP on some windows, eg firefox displaying http://docs.python.org/tutorial/classes.html - if not tablename.lower() in titles[hwnd].lower(): continue + if not tablename.lower() in titles[hwnd].decode(getDefaultEncoding()).lower(): continue except: continue if 'History for table:' in titles[hwnd]: continue # Everleaf Network HH viewer window From 730c82bc95613030559925cbac30e225ff7c0cc1 Mon Sep 17 00:00:00 2001 From: grindi Date: Sun, 9 Aug 2009 16:38:55 +0400 Subject: [PATCH 12/45] Party hhc: added max seats recognition --- pyfpdb/PartyPokerToFpdb.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pyfpdb/PartyPokerToFpdb.py b/pyfpdb/PartyPokerToFpdb.py index af1081df..81044e78 100644 --- a/pyfpdb/PartyPokerToFpdb.py +++ b/pyfpdb/PartyPokerToFpdb.py @@ -88,6 +88,7 @@ class PartyPoker(HandHistoryConverter): """, re.MULTILINE|re.VERBOSE) + re_TotalPlayers = re.compile("^Total\s+number\s+of\s+players\s*:\s*(?P\d+)", re.MULTILINE) re_SplitHands = re.compile('\x00+') re_TailSplitHands = re.compile('(\x00+)') lineSplitter = '\n' @@ -112,6 +113,8 @@ follow : whether to tail -f the input""" def allHandsAsList(self): list = HandHistoryConverter.allHandsAsList(self) + if list is None: + return None return filter(lambda text: len(text.strip()), list) def compilePlayerRegexs(self, hand): @@ -249,6 +252,9 @@ follow : whether to tail -f the input""" m = self.re_Hid.search(hand.handText) if m: info.update(m.groupdict()) + m = self.re_TotalPlayers.search(hand.handText) + if m: info.update(m.groupdict()) + # FIXME: it's a hack cause party doesn't supply hand.maxseats info #hand.maxseats = ??? hand.mixed = None @@ -281,6 +287,8 @@ follow : whether to tail -f the input""" #FIXME: it's dirty hack T_T cur = info[key][0] if info[key][0] not in '0123456789' else '' hand.buyin = info[key] + '+%s0' % cur + if key == 'MAXSEATS': + hand.maxseats = int(info[key]) if key == 'LEVEL': hand.level = info[key] if key == 'PLAY' and info['PLAY'] != 'Real': From 7882e735aa5792f6b4371a0aa7d5ceda6ba1ad0d Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 9 Aug 2009 09:29:44 -0400 Subject: [PATCH 13/45] Removed support for the ptrackS database type. File is big enough. ptrackS is the PokerTracker Stud database. An early version of the HUD supported that db. --- pyfpdb/SQL.py | 136 ++------------------------------------------------ 1 file changed, 4 insertions(+), 132 deletions(-) diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index 18eaad5c..25440720 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -1,9 +1,7 @@ #!/usr/bin/env python -"""SQL.py - -Set up all of the SQL statements for a given game and database type. +"""Returns a dict of SQL statements used in fpdb. """ -# Copyright 2008, Ray E. Barker +# Copyright 2008-2009, Ray E. Barker # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -35,134 +33,8 @@ import re class Sql: - def __init__(self, game = 'holdem', type = 'PT3', db_server = 'mysql'): + def __init__(self, game = 'holdem', type = 'fpdb', db_server = 'mysql'): self.query = {} - -############################################################################ -# -# Support for the ptracks database, a cut down PT2 stud database. -# You can safely ignore this unless you are me. -# - if game == 'razz' and type == 'ptracks': - - self.query['get_table_name'] = "select table_name from game where game_id = %s" - - self.query['get_last_hand'] = "select max(game_id) from game" - - self.query['get_recent_hands'] = "select game_id from game where game_id > %(last_hand)d" - - self.query['get_xml'] = "select xml from hand_history where game_id = %s" - - self.query['get_player_id'] = """ - select player_id from players - where screen_name = %(player)s - """ - - self.query['get_hand_info'] = """ - SELECT - game_id, - CONCAT(hole_card_1, hole_card_2, hole_card_3, hole_card_4, hole_card_5, hole_card_6, hole_card_7) AS hand, - total_won-total_bet AS net - FROM game_players - WHERE game_id = %s AND player_id = 3 - """ - - self.query['get_cards'] = """ - select - seat_number, - screen_name, - hole_card_1, - hole_card_2, - hole_card_3, - hole_card_4, - hole_card_5, - hole_card_6, - hole_card_7 - from game_players, players - where game_id = %s and game_players.player_id = players.player_id - order by seat_number - """ - - self.query['get_stats_from_hand'] = """ - SELECT player_id, - count(*) AS n, - sum(pre_fourth_raise_n) AS pfr, - sum(fourth_raise_n) AS raise_n_2, - sum(fourth_ck_raise_n) AS cr_n_2, - sum(fifth_bet_raise_n) AS br_n_3, - sum(fifth_bet_ck_raise_n) AS cr_n_3, - sum(sixth_bet_raise_n) AS br_n_4, - sum(sixth_bet_ck_raise_n) AS cr_n_4, - sum(river_bet_raise_n) AS br_n_5, - sum(river_bet_ck_raise_n) AS cr_n_5, - sum(went_to_showdown_n) AS sd, - sum(saw_fourth_n) AS saw_f, - sum(raised_first_pf) AS first_pfr, - sum(vol_put_money_in_pot) AS vpip, - sum(limp_with_prev_callers) AS limp_w_callers, - - sum(ppossible_actions) AS poss_a_pf, - sum(pfold) AS fold_pf, - sum(pcheck) AS check_pf, - sum(praise) AS raise_pf, - sum(pcall) AS raise_pf, - sum(limp_call_reraise_pf) AS limp_call_pf, - - sum(pfr_check) AS check_after_raise, - sum(pfr_call) AS call_after_raise, - sum(pfr_fold) AS fold_after_raise, - sum(pfr_bet) AS bet_after_raise, - sum(pfr_raise) AS raise_after_raise, - sum(folded_to_river_bet) AS fold_to_r_bet, - - sum(fpossible_actions) AS poss_a_2, - sum(ffold) AS fold_2, - sum(fcheck) AS check_2, - sum(fbet) AS bet_2, - sum(fraise) AS raise_2, - sum(fcall) AS raise_2, - - sum(fifpossible_actions) AS poss_a_3, - sum(fiffold) AS fold_3, - sum(fifcheck) AS check_3, - sum(fifbet) AS bet_3, - sum(fifraise) AS raise_3, - sum(fifcall) AS call_3, - - sum(spossible_actions) AS poss_a_4, - sum(sfold) AS fold_4, - sum(scheck) AS check_4, - sum(sbet) AS bet_4, - sum(sraise) AS raise_4, - sum(scall) AS call_4, - - sum(rpossible_actions) AS poss_a_5, - sum(rfold) AS fold_5, - sum(rcheck) AS check_5, - sum(rbet) AS bet_5, - sum(rraise) AS raise_5, - sum(rcall) AS call_5, - - sum(cold_call_pf) AS cc_pf, - sum(saw_fifth_n) AS saw_3, - sum(saw_sixth_n) AS saw_4, - sum(saw_river_n) AS saw_5 - FROM game_players - WHERE player_id in - (SELECT player_id FROM game_players - WHERE game_id = %s AND NOT player_id = %s) - GROUP BY player_id - """ -# alternate form of WHERE for above -# WHERE game_id = %(hand)d AND NOT player_id = %(hero)d) -# WHERE game_id = %s AND NOT player_id = %s) - - self.query['get_players_from_hand'] = """ - SELECT game_players.player_id, seat_number, screen_name - FROM game_players INNER JOIN players ON (game_players.player_id = players.player_id) - WHERE game_id = %s - """ - ###############################################################################3 # Support for the Free Poker DataBase = fpdb http://fpdb.sourceforge.net/ # @@ -2899,7 +2771,7 @@ class Sql: if __name__== "__main__": # just print the default queries and exit - s = Sql(game = 'razz', type = 'ptracks') + s = Sql() for key in s.query: print "For query " + key + ", sql =" print s.query[key] From 8ef03b0697aa3c04ac858b19074bba75232f9de3 Mon Sep 17 00:00:00 2001 From: grindi Date: Sun, 9 Aug 2009 19:54:35 +0400 Subject: [PATCH 14/45] Party hhc: added nl, pl and omaha hi support --- pyfpdb/PartyPokerToFpdb.py | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/pyfpdb/PartyPokerToFpdb.py b/pyfpdb/PartyPokerToFpdb.py index 81044e78..70f14b3d 100644 --- a/pyfpdb/PartyPokerToFpdb.py +++ b/pyfpdb/PartyPokerToFpdb.py @@ -49,15 +49,15 @@ class PartyPoker(HandHistoryConverter): # $5 USD NL Texas Hold'em - Saturday, July 25, 07:53:52 EDT 2009 # NL Texas Hold'em $1 USD Buy-in Trny:45685440 Level:8 Blinds-Antes(600/1 200 -50) - Sunday, May 17, 11:25:07 MSKS 2009 re_GameInfoRing = re.compile(""" - (?P\$|)\s*(?P\d+)\s*(?:USD)?\s* - (?P(NL))\s+ - (?P(Texas\ Hold\'em)) + (?P\$|)\s*(?P[0-9,]+)\s*(?:USD)?\s* + (?P(NL|PL|))\s+ + (?P(Texas\ Hold\'em|Omaha)) \s*\-\s* (?P.+) """, re.VERBOSE) re_GameInfoTrny = re.compile(""" - (?P(NL))\s+ - (?P(Texas\ Hold\'em))\s+ + (?P(NL|PL|))\s+ + (?P(Texas\ Hold\'em|Omaha))\s+ (?P\$?[.0-9]+)\s*(?PUSD)?\s*Buy-in\s+ Trny:\s?(?P\d+)\s+ Level:\s*(?P\d+)\s+ @@ -157,12 +157,12 @@ follow : whether to tail -f the input""" def readSupportedGames(self): return [["ring", "hold", "nl"], - #["ring", "hold", "pl"], - #["ring", "hold", "fl"], + ["ring", "hold", "pl"], + ["ring", "hold", "fl"], ["tour", "hold", "nl"], - #["tour", "hold", "pl"], - #["tour", "hold", "fl"], + ["tour", "hold", "pl"], + ["tour", "hold", "fl"], ] def _getGameType(self, handText): @@ -193,12 +193,10 @@ follow : whether to tail -f the input""" mg = m.groupdict() # translations from captured groups to fpdb info strings - limits = { 'NL':'nl', -# 'Pot Limit':'pl', 'Limit':'fl' - } + limits = { 'NL':'nl', 'PL':'pl', '':'fl' } games = { # base, category "Texas Hold'em" : ('hold','holdem'), - #'Omaha' : ('hold','omahahi'), + 'Omaha' : ('hold','omahahi'), } currencies = { '$':'USD', '':'T$' } @@ -208,7 +206,7 @@ follow : whether to tail -f the input""" "Cannot fetch field '%s'" % expectedField, hh = handText) try: - info['limitType'] = limits[mg['LIMIT']] + info['limitType'] = limits[mg['LIMIT'].strip()] except: raise self.ParsingException( "Unknown limit '%s'" % mg['LIMIT'], @@ -232,8 +230,8 @@ follow : whether to tail -f the input""" # FIXME: there are only $ and play money availible for cash info['currency'] = currencies[mg['CURRENCY']] else: - info['sb'] = renderTrnyMoney(mg['SB']) - info['bb'] = renderTrnyMoney(mg['BB']) + info['sb'] = clearMoneyString(mg['SB']) + info['bb'] = clearMoneyString(mg['BB']) info['currency'] = 'T$' # NB: SB, BB must be interpreted as blinds or bets depending on limit type. @@ -309,7 +307,7 @@ follow : whether to tail -f the input""" players = [] for a in m: hand.addPlayer(int(a.group('SEAT')), a.group('PNAME'), - renderTrnyMoney(a.group('CASH'))) + clearMoneyString(a.group('CASH'))) def markStreets(self, hand): # PREFLOP = ** Dealing down cards ** @@ -434,11 +432,11 @@ follow : whether to tail -f the input""" def ringBlinds(ringLimit): "Returns blinds for current limit" - ringLimit = float(ringLimit) + ringLimit = float(clearMoneyString(ringLimit)) if ringLimit == 5.: ringLimit = 4. return ('%.2f' % (ringLimit/200.), '%.2f' % (ringLimit/100.) ) -def renderTrnyMoney(money): +def clearMoneyString(money): "renders 'numbers' like '1 200' and '2,000'" return money.replace(' ', '').replace(',', '') From f00a6ebfd2bbd86d0e18b664348c53cce4035ff8 Mon Sep 17 00:00:00 2001 From: grindi Date: Sun, 9 Aug 2009 21:24:48 +0400 Subject: [PATCH 15/45] Party hhc: removed "max seats" recognition --- pyfpdb/PartyPokerToFpdb.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/pyfpdb/PartyPokerToFpdb.py b/pyfpdb/PartyPokerToFpdb.py index 70f14b3d..7abea321 100644 --- a/pyfpdb/PartyPokerToFpdb.py +++ b/pyfpdb/PartyPokerToFpdb.py @@ -114,8 +114,17 @@ follow : whether to tail -f the input""" def allHandsAsList(self): list = HandHistoryConverter.allHandsAsList(self) if list is None: - return None + return [] return filter(lambda text: len(text.strip()), list) + + def guessMaxSeats(self, hand): + """Return a guess at max_seats when not specified in HH.""" + mo = self.maxOccSeat(hand) + + if mo == 10: return mo + if mo == 2: return 2 + if mo <= 6: return 6 + return 9 if hand.gametype['type']=='ring' else 10 def compilePlayerRegexs(self, hand): players = set([player[1] for player in hand.players]) @@ -285,8 +294,8 @@ follow : whether to tail -f the input""" #FIXME: it's dirty hack T_T cur = info[key][0] if info[key][0] not in '0123456789' else '' hand.buyin = info[key] + '+%s0' % cur - if key == 'MAXSEATS': - hand.maxseats = int(info[key]) + #if key == 'MAXSEATS': + #hand.maxseats = int(info[key]) if key == 'LEVEL': hand.level = info[key] if key == 'PLAY' and info['PLAY'] != 'Real': From 4c449059edcff6e06c66cb32243258c24cf7a17b Mon Sep 17 00:00:00 2001 From: grindi Date: Mon, 10 Aug 2009 00:26:24 +0400 Subject: [PATCH 16/45] HUD: added max seats submenu --- pyfpdb/Hud.py | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/pyfpdb/Hud.py b/pyfpdb/Hud.py index 8a7aa740..4be9061f 100644 --- a/pyfpdb/Hud.py +++ b/pyfpdb/Hud.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +# -*- coding: utf-8 -*- """Hud.py Create and manage the hud overlays. @@ -95,6 +96,8 @@ class Hud: if my_import == None: continue self.aux_windows.append(my_import(self, config, aux_params)) + + self.creation_attrs = None def create_mw(self): @@ -146,6 +149,21 @@ class Hud: self.item4.connect("activate", self.debug_stat_windows) self.item4.show() + self.item5 = gtk.MenuItem('Set max seats') + self.menu.append(self.item5) + self.item5.show() + self.maxSeatsMenu = gtk.Menu() + self.item5.set_submenu(self.maxSeatsMenu) + for i in range(2, 11, 1): + item = gtk.MenuItem('%d-max' % i) + item.ms = i + self.maxSeatsMenu.append(item) + item.connect("activate", self.change_max_seats) + item.show() + setattr(self, 'maxSeatsMenuItem%d' % (i-1), item) + + + self.ebox.connect_object("button-press-event", self.on_button_press, self.menu) self.main_window.show_all() @@ -162,7 +180,19 @@ class Hud: self.main_window.gdkhandle.set_transient_for(self.main_window.parentgdkhandle) # self.update_table_position() - + + def change_max_seats(self, widget): + if self.max != widget.ms: + print 'change_max_seats', widget.ms + self.max = widget.ms + try: + self.kill() + self.create(*self.creation_attrs) + self.update(self.hand, self.config) + except Exception, e: + print "Expcetion:",str(e) + pass + def update_table_position(self): if os.name == 'nt': if not win32gui.IsWindow(self.table.number): @@ -264,6 +294,8 @@ class Hud: # # this method also manages the creating and destruction of stat # windows via calls to the Stat_Window class + self.creation_attrs = hand, config, stat_dict, cards + self.hand = hand if not self.mw_created: self.create_mw() From 3d227a42cdb87d84e646a416aa17a9c20198562c Mon Sep 17 00:00:00 2001 From: grindi Date: Mon, 10 Aug 2009 01:45:55 +0400 Subject: [PATCH 17/45] Party hhc: fixed after Matt's refactoring ^_^ --- pyfpdb/PartyPokerToFpdb.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyfpdb/PartyPokerToFpdb.py b/pyfpdb/PartyPokerToFpdb.py index 38206845..1000c9b6 100755 --- a/pyfpdb/PartyPokerToFpdb.py +++ b/pyfpdb/PartyPokerToFpdb.py @@ -167,6 +167,8 @@ class PartyPoker(HandHistoryConverter): ] def _getGameType(self, handText): + if not hasattr(self, '_gameType'): + self._gameType = None if self._gameType is None: # let's determine whether hand is trny # and whether 5-th line contains head line From dc3c721884d40b790b8597708847e301e402159a Mon Sep 17 00:00:00 2001 From: grindi Date: Mon, 10 Aug 2009 18:13:39 +0400 Subject: [PATCH 18/45] Party hhc: fixes and refactoring --- pyfpdb/PartyPokerToFpdb.py | 133 +++++++++++++++++++++---------------- 1 file changed, 75 insertions(+), 58 deletions(-) diff --git a/pyfpdb/PartyPokerToFpdb.py b/pyfpdb/PartyPokerToFpdb.py index 1000c9b6..149dd3d6 100755 --- a/pyfpdb/PartyPokerToFpdb.py +++ b/pyfpdb/PartyPokerToFpdb.py @@ -21,24 +21,23 @@ import sys from collections import defaultdict +from Exceptions import FpdbParseError from HandHistoryConverter import * # PartyPoker HH Format +class PartyPokerParseError(FpdbParseError): + "Usage: raise PartyPokerParseError([, hh=][, hid=])" + def __init__(self, msg='', hh=None, hid=None): + if hh is not None: + msg += "\n\nHand history attached below:\n" + self.wrapHh(hh) + return super(PartyPokerParseError, self).__init__(hid=hid) + #return super(PartyPokerParseError, self).__init__(msg, hid=hid) + def wrapHh(self, hh): + return ("%(DELIMETER)s\n%(HH)s\n%(DELIMETER)s") % \ + {'DELIMETER': '#'*50, 'HH': hh} + class PartyPoker(HandHistoryConverter): - class ParsingException(Exception): - "Usage: raise ParsingException([, hh=])" - def __init__(self, *args, **kwargs): - if len(args)==0: args=[''] + list(args) - msg, args = args[0], args[1:] - if 'hh' in kwargs: - msg += self.wrapHh(kwargs['hh']) - del kwargs['hh'] - return Exception.__init__(self, msg, *args, **kwargs) - def wrapHh(self, hh): - return ("\n\nHand history attached below:\n" - "%(DELIMETER)s\n%(HH)s\n%(DELIMETER)s") % \ - {'DELIMETER': '#'*50, 'HH': hh} ############################################################ # Class Variables @@ -116,6 +115,7 @@ class PartyPoker(HandHistoryConverter): if mo == 10: return mo if mo == 2: return 2 if mo <= 6: return 6 + # there are 9-max tables for cash and 10-max for tournaments return 9 if hand.gametype['type']=='ring' else 10 def compilePlayerRegexs(self, hand): @@ -152,7 +152,7 @@ class PartyPoker(HandHistoryConverter): r"\[ *(?P.+) *\](?P.+)\.", re.MULTILINE) self.re_CollectPot = re.compile( - r""""^%(PLYR)s \s+ wins \s+ + r"""^%(PLYR)s \s+ wins \s+ %(CUR_SYM)s(?P[.\d]+)\s*%(CUR)s""" % subst, re.MULTILINE|re.VERBOSE) @@ -187,7 +187,7 @@ class PartyPoker(HandHistoryConverter): gametype dict is: {'limitType': xxx, 'base': xxx, 'category': xxx}""" - log.debug(self.ParsingException().wrapHh( handText )) + log.debug(PartyPokerParseError().wrapHh( handText )) info = {} m = self._getGameType(handText) @@ -205,20 +205,20 @@ class PartyPoker(HandHistoryConverter): for expectedField in ['LIMIT', 'GAME']: if mg[expectedField] is None: - raise self.ParsingException( + raise PartyPokerParseError( "Cannot fetch field '%s'" % expectedField, hh = handText) try: info['limitType'] = limits[mg['LIMIT'].strip()] except: - raise self.ParsingException( + raise PartyPokerParseError( "Unknown limit '%s'" % mg['LIMIT'], hh = handText) try: (info['base'], info['category']) = games[mg['GAME']] except: - raise self.ParsingException( + raise PartyPokerParseError( "Unknown game type '%s'" % mg['GAME'], hh = handText) @@ -231,6 +231,7 @@ class PartyPoker(HandHistoryConverter): if info['type'] == 'ring': info['sb'], info['bb'] = ringBlinds(mg['RINGLIMIT']) # FIXME: there are only $ and play money availible for cash + # to be honest, party doesn't save play money hh info['currency'] = currencies[mg['CURRENCY']] else: info['sb'] = clearMoneyString(mg['SB']) @@ -243,21 +244,27 @@ class PartyPoker(HandHistoryConverter): def readHandInfo(self, hand): info = {} - m = self.re_HandInfo.search(hand.handText,re.DOTALL) - if m: - info.update(m.groupdict()) - else: - raise self.ParsingException("Cannot read Handinfo for current hand", hh=hand.handText) - m = self._getGameType(hand.handText) - if m: info.update(m.groupdict()) - m = self.re_Hid.search(hand.handText) - if m: info.update(m.groupdict()) + try: + info.update(self.re_Hid.search(hand.handText).groupdict()) + except: + raise PartyPokerParseError("Cannot read HID for current hand", hh=hand.handText) + + try: + info.update(self.re_HandInfo.search(hand.handText,re.DOTALL).groupdict()) + except: + raise PartyPokerParseError("Cannot read Handinfo for current hand", + hh=hand.handText, hid = info['HID']) + + try: + info.update(self._getGameType(hand.handText).groupdict()) + except: + raise PartyPokerParseError("Cannot read GameType for current hand", + hh=hand.handText, hid = info['HID']) + m = self.re_TotalPlayers.search(hand.handText) if m: info.update(m.groupdict()) - # FIXME: it's a hack cause party doesn't supply hand.maxseats info - #hand.maxseats = ??? hand.mixed = None log.debug("readHandInfo: %s" % info) @@ -285,16 +292,14 @@ class PartyPoker(HandHistoryConverter): if key == 'TOURNO': hand.tourNo = info[key] if key == 'BUYIN': - #FIXME: it's dirty hack T_T + # FIXME: it's dirty hack T_T + # code below assumes that rake is equal to zero cur = info[key][0] if info[key][0] not in '0123456789' else '' hand.buyin = info[key] + '+%s0' % cur - #if key == 'MAXSEATS': - #hand.maxseats = int(info[key]) if key == 'LEVEL': hand.level = info[key] if key == 'PLAY' and info['PLAY'] != 'Real': - # TODO: play money wasn't tested -# hand.currency = 'play' # overrides previously set value + # if realy there's no play money hh on party hand.gametype['currency'] = 'play' def readButton(self, hand): @@ -313,10 +318,6 @@ class PartyPoker(HandHistoryConverter): clearMoneyString(a.group('CASH'))) def markStreets(self, hand): - # PREFLOP = ** Dealing down cards ** - # This re fails if, say, river is missing; then we don't get the ** that starts the river. - assert hand.gametype['base'] == "hold", \ - "wtf! There're no %s games on party" % hand.gametype['base'] m = re.search( r"\*{2} Dealing down cards \*{2}" r"(?P.+?)" @@ -337,11 +338,6 @@ class PartyPoker(HandHistoryConverter): for player in m: hand.addAnte(player.group('PNAME'), player.group('ANTE')) - def readBringIn(self, hand): - m = self.re_BringIn.search(hand.handText,re.DOTALL) - if m: - hand.addBringIn(m.group('PNAME'), m.group('BRINGIN')) - def readBlinds(self, hand): noSmallBlind = bool(self.re_NoSmallBlind.search(hand.handText)) if hand.gametype['type'] == 'ring': @@ -381,7 +377,7 @@ class PartyPoker(HandHistoryConverter): bigBlindSeat = findFirstNonEmptySeat(smallBlindSeat + 1) blind = smartMin(hand.bb, playersMap[bigBlindSeat][1]) - hand.addBlind(playersMap[bigBlindSeat][0], 'small blind', blind) + hand.addBlind(playersMap[bigBlindSeat][0], 'big blind', blind) @@ -400,18 +396,39 @@ class PartyPoker(HandHistoryConverter): m = self.re_Action.finditer(hand.streets[street]) for action in m: acts = action.groupdict() - if action.group('ATYPE') in ('raises','is all-In'): - hand.addRaiseBy( street, action.group('PNAME'), action.group('BET') ) - elif action.group('ATYPE') == 'calls': - hand.addCall( street, action.group('PNAME'), action.group('BET') ) - elif action.group('ATYPE') == 'bets': - hand.addBet( street, action.group('PNAME'), action.group('BET') ) - elif action.group('ATYPE') == 'folds': - hand.addFold( street, action.group('PNAME')) - elif action.group('ATYPE') == 'checks': - hand.addCheck( street, action.group('PNAME')) + playerName = action.group('PNAME') + amount = clearMoneyString(action.group('BET')) if action.group('BET') else None + actionType = action.group('ATYPE') + + if actionType == 'is all-In': + # party's allin can mean either raise or bet or call + Bp = hand.lastBet[street] + if Bp == 0: + actionType = 'bets' + elif Bp < Decimal(amount): + actionType = 'raises' + else: + actionType = 'calls' + + if actionType == 'raises': + if street == 'PREFLOP' and \ + playerName in [item[0] for item in hand.actions['BLINDSANTES']]: + # preflop raise from blind + hand.addRaiseBy( street, playerName, amount ) + else: + hand.addCallandRaise( street, playerName, amount ) + elif actionType == 'calls': + hand.addCall( street, playerName, amount ) + elif actionType == 'bets': + hand.addBet( street, playerName, amount ) + elif actionType == 'folds': + hand.addFold( street, playerName ) + elif actionType == 'checks': + hand.addCheck( street, playerName ) else: - print "DEBUG: unimplemented readAction: '%s' '%s'" %(action.group('PNAME'),action.group('ATYPE'),) + raise PartyPokerParseError( + "Unimplemented readAction: '%s' '%s'" % (playerName,actionType,), + hid = hand.hid, hh = hand.handText ) def readShowdownActions(self, hand): @@ -434,17 +451,17 @@ class PartyPoker(HandHistoryConverter): hand.addShownCards(cards=cards, player=m.group('PNAME'), shown=shown, mucked=mucked) def ringBlinds(ringLimit): - "Returns blinds for current limit" + "Returns blinds for current limit in cash games" ringLimit = float(clearMoneyString(ringLimit)) if ringLimit == 5.: ringLimit = 4. return ('%.2f' % (ringLimit/200.), '%.2f' % (ringLimit/100.) ) def clearMoneyString(money): - "renders 'numbers' like '1 200' and '2,000'" + "Renders 'numbers' like '1 200' and '2,000'" return money.replace(' ', '').replace(',', '') def renderCards(string): - "splits strings like ' Js, 4d '" + "Splits strings like ' Js, 4d '" cards = string.strip().split(' ') return filter(len, map(lambda x: x.strip(' ,'), cards)) From a67368cc5ad89fbb16bff01f3724093b64670a5f Mon Sep 17 00:00:00 2001 From: grindi Date: Mon, 10 Aug 2009 20:10:39 +0400 Subject: [PATCH 19/45] Added hh path normalization Slashes '/' in hh path can lead to import errors on non-posix systems Path normalization eliminates this problem --- pyfpdb/Configuration.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/pyfpdb/Configuration.py b/pyfpdb/Configuration.py index b90040d7..f0277bca 100755 --- a/pyfpdb/Configuration.py +++ b/pyfpdb/Configuration.py @@ -74,11 +74,17 @@ class Layout: class Site: def __init__(self, node): + def normalizePath(path): + "Normalized existing pathes" + if os.path.exists(path): + return os.path.abspath(path) + return path + self.site_name = node.getAttribute("site_name") self.table_finder = node.getAttribute("table_finder") self.screen_name = node.getAttribute("screen_name") - self.site_path = node.getAttribute("site_path") - self.HH_path = node.getAttribute("HH_path") + self.site_path = normalizePath(node.getAttribute("site_path")) + self.HH_path = normalizePath(node.getAttribute("HH_path")) self.decoder = node.getAttribute("decoder") self.hudopacity = node.getAttribute("hudopacity") self.hudbgcolor = node.getAttribute("bgcolor") @@ -92,6 +98,8 @@ class Site: self.xpad = node.getAttribute("xpad") self.ypad = node.getAttribute("ypad") self.layout = {} + + print self.site_name, self.HH_path for layout_node in node.getElementsByTagName('layout'): lo = Layout(layout_node) From 24e5c2252df01b7b70136aa54a831daa53b389cf Mon Sep 17 00:00:00 2001 From: Worros Date: Tue, 11 Aug 2009 20:52:07 +0800 Subject: [PATCH 20/45] Fix inserting into Gametypes --- pyfpdb/Database.py | 16 ++++++++++++++++ pyfpdb/Hand.py | 18 ++---------------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index 6fb96507..45de48e0 100755 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -1021,6 +1021,22 @@ class Database: print "Error during fdb.lock_for_insert:", str(sys.exc_value) #end def lock_for_insert + def getGameTypeId(self, siteid, game): + c = self.get_cursor() + #FIXME: Fixed for NL at the moment + c.execute(self.sql.query['getGametypeNL'], (siteid, game['type'], game['category'], game['limitType'], + int(Decimal(game['sb'])*100), int(Decimal(game['bb'])*100))) + tmp = c.fetchone() + if (tmp == None): + hilo = "h" + if game['category'] in ['studhilo', 'omahahilo']: + hilo = "s" + elif game['category'] in ['razz','27_3draw','badugi']: + hilo = "l" + tmp = self.insertGameTypes( (siteid, game['type'], game['base'], game['category'], game['limitType'], hilo, + int(Decimal(game['sb'])*100), int(Decimal(game['bb'])*100), 0, 0) ) + return tmp[0] + def getSqlPlayerIDs(self, pnames, siteid): result = {} if(self.pcache == None): diff --git a/pyfpdb/Hand.py b/pyfpdb/Hand.py index 2c5045a2..97a68b18 100644 --- a/pyfpdb/Hand.py +++ b/pyfpdb/Hand.py @@ -189,21 +189,7 @@ db: a connected fpdb_db object""" sqlids = db.getSqlPlayerIDs([p[1] for p in self.players], self.siteId) #Gametypes - - print "DEBUG: self.gametype %s" % self.gametype - #Nice way to discover if the game is already listed in the db? - #Also this is using an old method from fpdb_simple - should probably conform to the rest of the inserts - - hilo = "h" - if self.gametype['category'] in ['studhilo', 'omahahilo']: - hilo = "s" - elif self.gametype['category'] in ['razz','27_3draw','badugi']: - hilo = "l" - - gtid = db.insertGameTypes( (self.siteId, self.gametype['type'], self.gametype['base'], - self.gametype['category'], self.gametype['limitType'], hilo, - int(Decimal(self.gametype['sb'])*100), int(Decimal(self.gametype['bb'])*100), 0, 0) ) - + gtid = db.getGameTypeId(self.siteId, self.gametype) # HudCache data to come from DerivedStats class # HandsActions - all actions for all players for all streets - self.actions @@ -212,7 +198,7 @@ db: a connected fpdb_db object""" hh = {} hh['siteHandNo'] = self.handid hh['handStart'] = self.starttime - hh['gameTypeId'] = gtid[0] + hh['gameTypeId'] = gtid # seats TINYINT NOT NULL, hh['tableName'] = self.tablename hh['maxSeats'] = self.maxseats From 2e60586e6568a84babb8b2ebdc0e2bdbc41f98de Mon Sep 17 00:00:00 2001 From: grindi Date: Tue, 11 Aug 2009 18:02:58 +0400 Subject: [PATCH 21/45] Party hhc: fixes and one fat hack --- pyfpdb/PartyPokerToFpdb.py | 46 +++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/pyfpdb/PartyPokerToFpdb.py b/pyfpdb/PartyPokerToFpdb.py index 149dd3d6..1b108d2f 100755 --- a/pyfpdb/PartyPokerToFpdb.py +++ b/pyfpdb/PartyPokerToFpdb.py @@ -99,7 +99,9 @@ class PartyPoker(HandHistoryConverter): lineSplitter = '\n' re_Button = re.compile('Seat (?P