From 105e8688641c0a98f7020a81e7a15744f25cc991 Mon Sep 17 00:00:00 2001 From: Mika Bostrom Date: Sun, 5 Jul 2009 23:33:09 +0300 Subject: [PATCH 01/11] 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/11] 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/11] 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/11] 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/11] 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/11] 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/11] 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 7882e735aa5792f6b4371a0aa7d5ceda6ba1ad0d Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 9 Aug 2009 09:29:44 -0400 Subject: [PATCH 08/11] 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 d69ab83ee5d528c2849660906e5ff43bda3046fd Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 11 Aug 2009 17:43:28 -0400 Subject: [PATCH 09/11] Fix encoding problem in the written Stars HHs. --- pyfpdb/HandHistoryConverter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpdb/HandHistoryConverter.py b/pyfpdb/HandHistoryConverter.py index bcb523c9..ea43d635 100644 --- a/pyfpdb/HandHistoryConverter.py +++ b/pyfpdb/HandHistoryConverter.py @@ -62,7 +62,7 @@ class HandHistoryConverter(): 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') + self.out_fh = codecs.open(self.out_path, 'w', 'cp1252') self.sitename = sitename self.follow = follow From 35cd58545a8f2b6d2049fad1dd0c7fc8fc9b25ae Mon Sep 17 00:00:00 2001 From: Mika Bostrom Date: Wed, 12 Aug 2009 08:26:06 +0300 Subject: [PATCH 10/11] More generic locale use, I hope Instead of assuming conversion is from latin1, use locale information --- pyfpdb/fpdb_simple.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pyfpdb/fpdb_simple.py b/pyfpdb/fpdb_simple.py index d91becb8..781ef290 100644 --- a/pyfpdb/fpdb_simple.py +++ b/pyfpdb/fpdb_simple.py @@ -25,6 +25,7 @@ import datetime import time import re import sys +import locale import Card @@ -37,6 +38,8 @@ MYSQL_INNODB = 2 PGSQL = 3 SQLITE = 4 +(localename, encoding) = locale.getdefaultlocale() + class DuplicateError(Exception): def __init__(self, value): self.value = value @@ -704,7 +707,7 @@ def parseHandStartTime(topline): def findName(line): pos1 = line.find(":") + 2 pos2 = line.rfind("(") - 1 - return unicode(line[pos1:pos2], "latin-1") + return unicode(line[pos1:pos2], encoding) def parseNames(lines): return [findName(line) for line in lines] From 185f9660c5535d6da03fb3aa44de38d208d47a2d Mon Sep 17 00:00:00 2001 From: Mika Bostrom Date: Wed, 12 Aug 2009 10:00:23 +0300 Subject: [PATCH 11/11] Use same locale conversion everywhere It is not enough to use actual system locale in just one spot if all the other encodings are hard-coded to latin1. Now that we have the real locale available, do all string conversions [.encode($locale)] with that. --- pyfpdb/fpdb_simple.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pyfpdb/fpdb_simple.py b/pyfpdb/fpdb_simple.py index 1c3e7bc4..c12d38ca 100644 --- a/pyfpdb/fpdb_simple.py +++ b/pyfpdb/fpdb_simple.py @@ -546,7 +546,7 @@ def parseActionType(line): #parses the ante out of the given line and checks which player paid it, updates antes accordingly. def parseAnteLine(line, isTourney, names, antes): for i, name in enumerate(names): - if line.startswith(name.encode("latin-1")): + if line.startswith(name.encode(encoding)): pos = line.rfind("$") + 1 if not isTourney: antes[i] += float2int(line[pos:]) @@ -825,7 +825,7 @@ def parseTourneyNo(topline): def parseWinLine(line, names, winnings, isTourney): #print "parseWinLine: line:",line for i,n in enumerate(names): - n = n.encode("latin-1") + n = n.encode(encoding) if line.startswith(n): if isTourney: pos1 = line.rfind("collected ") + 10 @@ -1036,13 +1036,13 @@ def recognisePlayerNo(line, names, atype): #print "recogniseplayerno, names:",names for i in xrange(len(names)): if (atype=="unbet"): - if (line.endswith(names[i].encode("latin-1"))): + if (line.endswith(names[i].encode(encoding))): return (i) elif (line.startswith("Dealt to ")): #print "recognisePlayerNo, card precut, line:",line tmp=line[9:] #print "recognisePlayerNo, card postcut, tmp:",tmp - if (tmp.startswith(names[i].encode("latin-1"))): + if (tmp.startswith(names[i].encode(encoding))): return (i) elif (line.startswith("Seat ")): if (line.startswith("Seat 10")): @@ -1050,10 +1050,10 @@ def recognisePlayerNo(line, names, atype): else: tmp=line[8:] - if (tmp.startswith(names[i].encode("latin-1"))): + if (tmp.startswith(names[i].encode(encoding))): return (i) else: - if (line.startswith(names[i].encode("latin-1"))): + if (line.startswith(names[i].encode(encoding))): return (i) #if we're here we mustve failed raise FpdbError ("failed to recognise player in: "+line+" atype:"+atype)