From f55460341d22a29e31f1863d511199e7f281ef4b Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 21 May 2009 11:13:39 -0400 Subject: [PATCH 01/13] New table handling stuff and demo. Not working for windows and not currently used by HUD_main. --- pyfpdb/TableWindow.py | 154 ++++++++++++++++++++++++++++++++++++++++++ pyfpdb/Tables_Demo.py | 97 ++++++++++++++++++++++++++ pyfpdb/WinTables.py | 143 +++++++++++++++++++++++++++++++++++++++ pyfpdb/XTables.py | 101 +++++++++++++++++++++++++++ 4 files changed, 495 insertions(+) create mode 100644 pyfpdb/TableWindow.py create mode 100644 pyfpdb/Tables_Demo.py create mode 100644 pyfpdb/WinTables.py create mode 100644 pyfpdb/XTables.py diff --git a/pyfpdb/TableWindow.py b/pyfpdb/TableWindow.py new file mode 100644 index 00000000..bf62a69c --- /dev/null +++ b/pyfpdb/TableWindow.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python +"""Discover_TableWindow.py + +Inspects the currently open windows and finds those of interest to us--that is +poker table windows from supported sites. Returns a list +of Table_Window objects representing the windows found. +""" +# 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 +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +######################################################################## + +# Standard Library modules +import os +import sys + +# pyGTK modules +import pygtk +import gtk +import gobject + +# FreePokerTools modules +import Configuration +#if os.name == "posix": +# import XTables +#elif os.name == "nt": +# import WinTables + +# Global used for figuring out the current game being played from the title +# The dict key is the fpdb name for the game +# The list is the names for those games used by the supported poker sites +# This is currently only used for HORSE, so it only needs to support those +# games on PokerStars and Full Tilt. +game_names = { #fpdb name Stars Name FTP Name + "holdem" : ("Hold\'em" , ), + "omahahilo" : ("Omaha H/L" , ), + "studhilo" : ("Stud H/L" , ), + "razz" : ("Razz" , ), + "studhi" : ("Stud" , "Stud Hi") + } + +# A window title might have our table name + one of theses words/ +# phrases. If it has this word in the title, it is not a table. +bad_words = ('History for table:', 'HUD:', 'Chat:') + +# Here are the custom signals we define for allowing the 'client watcher' +# thread to communicate with the gui thread. Any time a poker client is +# is moved, resized, or closed on of these signals is emitted to the +# HUD main window. +gobject.signal_new("client_moved", gtk.Window, + gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT,)) + +gobject.signal_new("client_resized", gtk.Window, + gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT,)) + +gobject.signal_new("client_destroyed", gtk.Window, + gobject.SIGNAL_RUN_LAST, + gobject.TYPE_NONE, + (gobject.TYPE_PYOBJECT,)) + +# Each TableWindow object must have the following attributes correctly populated: +# tw.name = the table name from the title bar, which must to match the table name +# from the corresponding hand history. +# tw.site = the site name, e.g. PokerStars, FullTilt. This must match the site +# name specified in the config file. +# tw.number = This is the system id number for the client table window in the +# format that the system presents it. This is Xid in Xwindows and +# hwnd in Microsoft Windows. +# tw.title = The full title from the window title bar. +# tw.width, tw.height = The width and height of the window in pixels. This is +# the internal width and height, not including the title bar and +# window borders. +# tw.x, tw.y = The x, y (horizontal, vertical) location of the window relative +# to the top left of the display screen. This also does not include the +# title bar and window borders. To put it another way, this is the +# screen location of (0, 0) in the working window. + +class Table_Window(object): + def __init__(self, table_name = None, tournament = None, table_number = None): + + if table_name != None: + search_string = table_name + self.name = table_name + self.tournament = None + self.table = None + elif tournament != None and table_number != None: + print "tournament %s, table %s" % (tournament, table_number) + self.tournament = int(tournament) + self.table = int(table_number) + self.name = "%s - %s" % (self.tournament, self.table) + search_string = "%s.+Table\s%s" % (tournament, table_number) + else: + return None + + self.find_table_parameters(search_string) + + def __str__(self): +# __str__ method for testing + temp = 'TableWindow object\n' + temp = temp + " name = %s\n site = %s\n number = %s\n title = %s\n" % (self.name, self.site, self.number, self.title) +# temp = temp + " game = %s\n structure = %s\n max = %s\n" % (self.game, self.structure, self.max) + temp = temp + " width = %d\n height = %d\n x = %d\n y = %d\n" % (self.width, self.height, self.x, self.y) + if getattr(self, 'tournament', 0): + temp = temp + " tournament = %d\n table = %d" % (self.tournament, self.table) + return temp + + def get_game(self): + title = self.get_window_title() + print title + for game, names in game_names.iteritems(): + for name in names: + if name in title: + return game + return None + + def check_geometry(self): + new_geo = self.get_geometry() + + if new_geo == None: # window destroyed + return "client_destroyed" + + elif self.x != new_geo['x'] or self.y != new_geo['y']: # window moved + self.x = new_geo['x'] + self.y = new_geo['y'] + return "client_moved" + + elif self.width != new_geo['width'] or self.height != new_geo['height']: # window resized + self.width = new_geo['width'] + self.height = new_geo['height'] + return "client_resized" + + else: return False # window not changed + + def check_bad_words(self, title): + for word in bad_words: + if word in title: return True + return False \ No newline at end of file diff --git a/pyfpdb/Tables_Demo.py b/pyfpdb/Tables_Demo.py new file mode 100644 index 00000000..ffc8d412 --- /dev/null +++ b/pyfpdb/Tables_Demo.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python +"""Tables_Demo.py + +Main program module to test/demo the Tables subclasses. +""" +# 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 +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +######################################################################## + +# Standard Library modules +import sys +import os +import re + +# pyGTK modules +import pygtk +import gtk +import gobject + +# fpdb/free poker tools modules +# get the correct module for the current os +if os.name == 'posix': + import XTables as Tables +elif os.name == 'nt': + import WinTables as Tables + +# Main function used for testing +if __name__=="__main__": +# c = Configuration.Config() + + class fake_hud(object): + def __init__(self, table, dx = 100, dy = 100): + self.table = table + self.dx = dx + self.dy = dy + + self.main_window = gtk.Window() + self.main_window.connect("destroy", self.client_destroyed) + self.label = gtk.Label('Fake Fake Fake Fake\nFake\nFake\nFake') + self.main_window.add(self.label) + self.main_window.set_title("Fake HUD Main Window") + self.main_window.move(table.x + dx, table.y + dy) + self.main_window.show_all() + table.topify(self) + self.main_window.connect("client_moved", self.client_moved) + self.main_window.connect("client_resized", self.client_resized) + self.main_window.connect("client_destroyed", self.client_destroyed) + + def client_moved(self, widget, hud): + self.main_window.move(self.table.x + self.dx, self.table.y + self.dy) + + def client_resized(self, *args): + print "client resized" + + def client_destroyed(self, *args): # call back for terminating the main eventloop + gtk.main_quit() + + def check_on_table(table, hud): + result = table.check_geometry() + if result != False: + hud.main_window.emit(result, hud) + return True + + print "enter table name to find: ", + table_name = sys.stdin.readline() + if "," in table_name: # tournament + print "tournament" + (tour_no, tab_no) = table_name.split(",", 1) + tour_no = tour_no.rstrip() + tab_no = tab_no.rstrip() + table = Tables.Table(tournament = tour_no, table_number = tab_no) + else: # not a tournament + print "cash game" + table_name = table_name.rstrip() + table = Tables.Table(table_name = table_name) + + print "table =", table + print "game =", table.get_game() + + fake = fake_hud(table) + gobject.timeout_add(100, check_on_table, table, fake) + gtk.main() + diff --git a/pyfpdb/WinTables.py b/pyfpdb/WinTables.py new file mode 100644 index 00000000..1b064eef --- /dev/null +++ b/pyfpdb/WinTables.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python +"""WinTables.py + +Routines for detecting and handling poker client windows for MS Windows. +""" +# 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 +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +######################################################################## + +# Standard Library modules +import re + +# pyGTK modules +import pygtk +import gtk + +# Other Library modules +import win32gui +import win32process +import win32api +import win32con +import win32security + +# FreePokerTools modules +from TableWindow import Table_Window + +# We don't know the border width or title bar height +# so we guess here. We can probably get these from a windows call. +b_width = 3 +tb_height = 29 + +class Table(Table_Window): + + def find_table_parameters(self, search_string): + """Finds poker client window with the given table name.""" + titles = {} + win32gui.EnumWindows(win_enum_handler, titles) + for hwnd in titles: + if re.search(search_string, titles[hwnd]): + 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 + self.window = hwnd + break + + if self.window == None: + print "Window %s not found. Skipping." % search_string + return None + + (x, y, width, height) = win32gui.GetWindowRect(hwnd) + print "x = %s y = %s width = %s height = %s" % (x, y, width, height) + self.x = int(x) + b_width + self.y = int(y) + tb_height + self.height = int(height) - b_width - tb_height + self.width = int(width) - 2*b_width + + self.exe = self.get_nt_exe(hwnd) + self.title = titles[hwnd] + self.site = "" + self.hud = None + self.number = gtk.gdk.window_foreign_new(long(self.window)) + + def get_geometry(self): + + if not win32gui.IsWindow(self.window): # window closed + return None + + try: + (x, y, width, height) = win32gui.GetWindowRect(hwnd) + return {'x' : int(x) + b_width, + 'y' : int(y) + tb_height, + 'width' : int(height) - b_width - tb_height, + 'height' : int(width) - 2*b_width + } + except: + return None + + def get_window_title(self): + return win32gui.GetWindowText(self.window) + + def get_nt_exe(self, 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 win_enum_handler(hwnd, titles): + titles[hwnd] = win32gui.GetWindowText(hwnd) + +def topify_window(hud, window): + """Set the specified gtk window to stayontop in MS Windows.""" + + def windowEnumerationHandler(hwnd, resultList): + '''Callback for win32gui.EnumWindows() to generate list of window handles.''' + resultList.append((hwnd, win32gui.GetWindowText(hwnd))) + + unique_name = 'unique name for finding this window' + real_name = window.get_title() + window.set_title(unique_name) + tl_windows = [] + win32gui.EnumWindows(windowEnumerationHandler, tl_windows) + + for w in tl_windows: + if w[1] == unique_name: + hud.main_window.parentgdkhandle = gtk.gdk.window_foreign_new(long(hud.table.number)) + hud.main_window.gdkhandle = gtk.gdk.window_foreign_new(w[0]) + hud.main_window.gdkhandle.set_transient_for(hud.main_window.parentgdkhandle) + + style = win32gui.GetWindowLong(self.table.number, win32con.GWL_EXSTYLE) + style |= win32con.WS_CLIPCHILDREN + win32gui.SetWindowLong(hud.table.number, win32con.GWL_EXSTYLE, style) + break + + window.set_title(real_name) + diff --git a/pyfpdb/XTables.py b/pyfpdb/XTables.py new file mode 100644 index 00000000..1e692209 --- /dev/null +++ b/pyfpdb/XTables.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python +"""Discover_Tables.py + +Inspects the currently open windows and finds those of interest to us--that is +poker table windows from supported sites. Returns a list +of Table_Window objects representing the windows found. +""" +# 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 +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +######################################################################## + +# Standard Library modules +import re + +# pyGTK modules +import pygtk +import gtk + +# Other Library modules +import Xlib +import Xlib.display + +# FreePokerTools modules +from TableWindow import Table_Window + +# We might as well do this once and make them globals +disp = Xlib.display.Display() +root = disp.screen().root + +class Table(Table_Window): + + def find_table_parameters(self, search_string): + self.window = None + done_looping = False + for outside in root.query_tree().children: + for inside in outside.query_tree().children: + if done_looping: break + if inside.get_wm_name() and re.search(search_string, inside.get_wm_name()): + if self.check_bad_words(inside.get_wm_name()): continue + self.window = inside + self.parent = outside + done_looping = True + break + + if self.window == None or self.parent == None: + print "Window %s not found. Skipping." % search_string + return None + + my_geo = self.window.get_geometry() + pa_geo = self.parent.get_geometry() + + self.x = pa_geo.x + my_geo.x + self.y = pa_geo.y + my_geo.y + self.width = my_geo.width + self.height = my_geo.height + self.exe = self.window.get_wm_class()[0] + self.title = self.window.get_wm_name() + self.site = "" + self.hud = None + + window_string = str(self.window) + mo = re.match('Xlib\.display\.Window\(([\dxabcdef]+)', window_string) + if not mo: + print "Not matched" + self.gdk_handle = None + else: + self.number = int( mo.group(1), 0) + self.gdk_handle = gtk.gdk.window_foreign_new(int(self.number)) + + def get_geometry(self): + try: + my_geo = self.window.get_geometry() + pa_geo = self.parent.get_geometry() + return {'x' : pa_geo.x + my_geo.x, + 'y' : pa_geo.y + my_geo.y, + 'width' : my_geo.width, + 'height' : my_geo.height + } + except: + return None + + def get_window_title(self): + return self.window.get_wm_name() + + def topify(self, hud): + hud.main_window.gdkhandle = gtk.gdk.window_foreign_new(hud.main_window.window.xid) + hud.main_window.gdkhandle.set_transient_for(self.gdk_handle) From 069aa025e4881c8eec5c7f4c68f193bfee929ff2 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 31 May 2009 21:25:36 -0400 Subject: [PATCH 02/13] Fixes for importing and HUD with the postgres db. --- pyfpdb/Database.py | 36 +++++++++++++++++++----------------- pyfpdb/HUD_main.py | 3 +-- pyfpdb/SQL.py | 40 ++++++++++++++++++++++++++-------------- pyfpdb/fpdb_db.py | 4 +++- pyfpdb/fpdb_simple.py | 2 +- 5 files changed, 50 insertions(+), 35 deletions(-) diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index 3fba5c5e..26ee6ee3 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -38,12 +38,15 @@ class Database: if c.supported_databases[db_name].db_server == 'postgresql': # psycopg2 database module for posgres via DB-API import psycopg2 + import psycopg2.extensions + psycopg2.extensions.register_type(psycopg2.extensions.UNICODE) try: - self.connection = psycopg2.connect(host = c.supported_databases[db_name].db_ip, - user = c.supported_databases[db_name].db_user, - password = c.supported_databases[db_name].db_pass, - database = c.supported_databases[db_name].db_name) +# self.connection = psycopg2.connect(host = c.supported_databases[db_name].db_ip, +# user = c.supported_databases[db_name].db_user, +# password = c.supported_databases[db_name].db_pass, +# database = c.supported_databases[db_name].db_name) + self.connection = psycopg2.connect(database = c.supported_databases[db_name].db_name) except: print "Error opening database connection %s. See error log file." % (file) traceback.print_exc(file=sys.stderr) @@ -120,7 +123,7 @@ class Database: """Get and return the cards for each player in the hand.""" cards = {} # dict of cards, the key is the seat number example: {1: 'AcQd9hTs5d'} c = self.connection.cursor() - c.execute(self.sql.query['get_cards'], hand) + c.execute(self.sql.query['get_cards'], (hand, )) colnames = [desc[0] for desc in c.description] for row in c.fetchall(): s_dict = {} @@ -133,7 +136,7 @@ class Database: """Get and return the community cards for the specified hand.""" cards = {} c = self.connection.cursor() - c.execute(self.sql.query['get_common_cards'], hand) + c.execute(self.sql.query['get_common_cards'], (hand,)) colnames = [desc[0] for desc in c.description] for row in c.fetchall(): s_dict = {} @@ -154,20 +157,20 @@ class Database: # cards += "xx" # else: # cards += ranks[d['card' + str(i) + 'Value']] + d['card' +str(i) + 'Suit'] - cv = "card%dValue" % i + cv = "card%dvalue" % i if cv not in d or d[cv] == None: break elif d[cv] == 0: cards += "xx" else: - cs = "card%dSuit" % i + cs = "card%dsuit" % i cards = "%s%s%s" % (cards, ranks[d[cv]], d[cs]) return cards def get_action_from_hand(self, hand_no): action = [ [], [], [], [], [] ] c = self.connection.cursor() - c.execute(self.sql.query['get_action_from_hand'], (hand_no)) + c.execute(self.sql.query['get_action_from_hand'], (hand_no, )) for row in c.fetchall(): street = row[0] act = row[1:] @@ -178,7 +181,7 @@ class Database: """Returns a hash of winners:amount won, given a hand number.""" winners = {} c = self.connection.cursor() - c.execute(self.sql.query['get_winners_from_hand'], (hand)) + c.execute(self.sql.query['get_winners_from_hand'], (hand, )) for row in c.fetchall(): winners[row[0]] = row[1] return winners @@ -205,7 +208,6 @@ class Database: return stat_dict def get_player_id(self, config, site, player_name): - print "site = %s, player name = %s" % (site, player_name) c = self.connection.cursor() c.execute(self.sql.query['get_player_id'], {'player': player_name, 'site': site}) row = c.fetchone() @@ -224,19 +226,19 @@ if __name__=="__main__": h = db_connection.get_last_hand() print "last hand = ", h - hero = db_connection.get_player_id(c, 'PokerStars', 'nutOmatic') + hero = db_connection.get_player_id(c, 'Full Tilt Poker', 'PokerAscetic') print "nutOmatic is id_player = %d" % hero stat_dict = db_connection.get_stats_from_hand(h) for p in stat_dict.keys(): print p, " ", stat_dict[p] - print "nutOmatics stats:" - stat_dict = db_connection.get_stats_from_hand(h, hero) - for p in stat_dict.keys(): - print p, " ", stat_dict[p] +# print "nutOmatics stats:" +# stat_dict = db_connection.get_stats_from_hand(h, hero) +# for p in stat_dict.keys(): +# print p, " ", stat_dict[p] - print "cards =", db_connection.get_cards(73525) + print "cards =", db_connection.get_cards(u'1') db_connection.close_connection print "press enter to continue" diff --git a/pyfpdb/HUD_main.py b/pyfpdb/HUD_main.py index 4501821a..bce77cf1 100755 --- a/pyfpdb/HUD_main.py +++ b/pyfpdb/HUD_main.py @@ -147,7 +147,6 @@ class HUD_main(object): if new_hand_id == "": # blank line means quit self.destroy() break # this thread is not always killed immediately with gtk.main_quit() - # get basic info about the new hand from the db # if there is a db error, complain, skip hand, and proceed try: @@ -168,7 +167,7 @@ class HUD_main(object): (tour_number, tab_number) = mat_obj.group(1, 2) temp_key = tour_number else: # tourney, but can't get number and table - print "could not find tournamtne: skipping " + print "could not find tournament: skipping " sys.stderr.write("Could not find tournament %d in hand %d. Skipping.\n" % (int(tour_number), int(new_hand_id))) continue diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index c9314305..dcdac17d 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -237,7 +237,7 @@ class Sql: AND HudCache.gametypeId+0 = Hands.gametypeId+0) INNER JOIN Players ON (Players.id = HandsPlayers.PlayerId+0) WHERE Hands.id = %s - GROUP BY HudCache.PlayerId + GROUP BY HudCache.PlayerId, Players.name, seatNo """ # same as above except stats are aggregated for all blind/limit levels @@ -312,7 +312,7 @@ class Sql: AND gt1.limittype = gt2.limittype AND gt2.id = Hands.gametypeId AND Hands.id = %s) - GROUP BY HudCache.PlayerId + GROUP BY HudCache.PlayerId, Players.name, seatNo """ self.query['get_players_from_hand'] = """ @@ -349,13 +349,20 @@ class Sql: select seatNo AS seat_number, name AS screen_name, - card1Value, card1Suit, - card2Value, card2Suit, - card3Value, card3Suit, - card4Value, card4Suit, - card5Value, card5Suit, - card6Value, card6Suit, - card7Value, card7Suit + card1Value AS card1value, + card1Suit AS card1suit, + card2Value AS card2value, + card2Suit AS card2suit, + card3Value AS card3value, + card3Suit AS card3suit, + card4Value AS card4value, + card4Suit AS card4suit, + card5Value AS card5value, + card5Suit AS card5suit, + card6Value AS card6value, + card6Suit AS card6suit, + card7Value AS card7value, + card7Suit AS card7suit from HandsPlayers, Players where handID = %s and HandsPlayers.playerId = Players.id order by seatNo @@ -363,11 +370,16 @@ class Sql: self.query['get_common_cards'] = """ select - card1Value, card1Suit, - card2Value, card2Suit, - card3Value, card3Suit, - card4Value, card4Suit, - card5Value, card5Suit + card1Value AS card1value, + card1Suit AS card1suit, + card2Value AS card2value, + card2Suit AS card2suit, + card3Value AS card3value, + card3Suit AS card3suit, + card4Value AS card4value, + card4Suit AS card4suit, + card5Value AS card5value, + card5Suit AS card5suit from BoardCards where handId = %s """ diff --git a/pyfpdb/fpdb_db.py b/pyfpdb/fpdb_db.py index eba87f52..e8ff8109 100644 --- a/pyfpdb/fpdb_db.py +++ b/pyfpdb/fpdb_db.py @@ -62,9 +62,10 @@ class fpdb_db: self.db=MySQLdb.connect(host = host, user = user, passwd = password, db = database, use_unicode=True) elif backend==self.PGSQL: import psycopg2 + import psycopg2.extensions + psycopg2.extensions.register_type(psycopg2.extensions.UNICODE) # If DB connection is made over TCP, then the variables # host, user and password are required - print "host=%s user=%s pass=%s." % (host, user, password) if self.host and self.user and self.password: self.db = psycopg2.connect(host = host, user = user, @@ -75,6 +76,7 @@ class fpdb_db: # flat out wrong else: self.db = psycopg2.connect(database = database) +# self.db.set_client_encoding('UNICODE') else: raise fpdb_simple.FpdbError("unrecognised database backend:"+backend) self.cursor=self.db.cursor() diff --git a/pyfpdb/fpdb_simple.py b/pyfpdb/fpdb_simple.py index b2ef9d59..5978b806 100644 --- a/pyfpdb/fpdb_simple.py +++ b/pyfpdb/fpdb_simple.py @@ -1410,7 +1410,7 @@ def recognisePlayerIDs(cursor, names, site_id): if len(ids) != len(names): notfound = [n for n in names if n not in ids] # make list of names not in database if notfound: # insert them into database - cursor.executemany("INSERT INTO Players (name, siteId) VALUES (%s, "+str(site_id)+")", (notfound)) + cursor.executemany("INSERT INTO Players (name, siteId) VALUES (%s, "+str(site_id)+")", [(n,) for n in notfound]) q2 = "SELECT name,id FROM Players WHERE name=%s" % " OR name=".join(["%s" for n in notfound]) cursor.execute(q2, notfound) # get their new ids tmp = dict(cursor.fetchall()) From 441b300cff8f62db39b97ec8b98de4b927b19669 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 1 Jun 2009 18:26:20 -0400 Subject: [PATCH 03/13] Add db-type to get_db_parameters(). doh! --- pyfpdb/Configuration.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyfpdb/Configuration.py b/pyfpdb/Configuration.py index 47617023..e6395301 100755 --- a/pyfpdb/Configuration.py +++ b/pyfpdb/Configuration.py @@ -470,6 +470,9 @@ class Config: try: db['db-server'] = self.supported_databases[name].db_server except: pass + try: db['db-type'] = self.supported_databases[name].db_type + except: pass + if string.lower(self.supported_databases[name].db_server) == 'mysql': db['db-backend'] = 2 elif string.lower(self.supported_databases[name].db_server) == 'postgresql': From 98261387445313a79af9c7724ef7c25d483fe10c Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 1 Jun 2009 18:27:56 -0400 Subject: [PATCH 04/13] Correctly hand remote and local postgres dbs. Use get_db_parmeters(). --- pyfpdb/Database.py | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index 26ee6ee3..236a8dc2 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -26,6 +26,7 @@ Create and manage the database objects. # Standard Library modules import sys import traceback +import string # pyGTK modules @@ -35,18 +36,21 @@ import SQL class Database: def __init__(self, c, db_name, game): - if c.supported_databases[db_name].db_server == 'postgresql': - # psycopg2 database module for posgres via DB-API - import psycopg2 + db_params = c.get_db_parameters() + if (string.lower(db_params['db-server']) == 'postgresql' or + string.lower(db_params['db-server']) == 'postgres'): + import psycopg2 # posgres via DB-API import psycopg2.extensions psycopg2.extensions.register_type(psycopg2.extensions.UNICODE) try: -# self.connection = psycopg2.connect(host = c.supported_databases[db_name].db_ip, -# user = c.supported_databases[db_name].db_user, -# password = c.supported_databases[db_name].db_pass, -# database = c.supported_databases[db_name].db_name) - self.connection = psycopg2.connect(database = c.supported_databases[db_name].db_name) + if db_params['db-host'] == 'localhost' or db_params['db-host'] == '127.0.0.1': + self.connection = psycopg2.connect(database = db_params['db-databaseName']) + else: + self.connection = psycopg2.connect(host = db_params['db-host'], + user = db_params['db-user'], + password = db_params['db-password'], + database = db_params['db-databaseName']) except: print "Error opening database connection %s. See error log file." % (file) traceback.print_exc(file=sys.stderr) @@ -54,14 +58,13 @@ class Database: sys.stdin.readline() sys.exit() - elif c.supported_databases[db_name].db_server == 'mysql': - # mysql bindings - import MySQLdb + elif string.lower(db_params['db-server']) == 'mysql': + import MySQLdb # mysql bindings try: - self.connection = MySQLdb.connect(host = c.supported_databases[db_name].db_ip, - user = c.supported_databases[db_name].db_user, - passwd = c.supported_databases[db_name].db_pass, - db = c.supported_databases[db_name].db_name) + self.connection = MySQLdb.connect(host = db_params['db-host'], + user = db_params['db-user'], + passwd = db_params['db-password'], + db = db_params['db-databaseName']) cur_iso = self.connection.cursor() cur_iso.execute('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED') cur_iso.close() @@ -79,7 +82,7 @@ class Database: print "press enter to continue" sys.exit() - self.type = c.supported_databases[db_name].db_type + self.type = db_params['db-type'] self.sql = SQL.Sql(game = game, type = self.type) def close_connection(self): From 5013a07fc0022bbd0d36c506879dea8873e8b82f Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 2 Jun 2009 10:59:54 -0400 Subject: [PATCH 05/13] Minor change for postgres connection--not tested w/remote db. --- pyfpdb/fpdb_db.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/pyfpdb/fpdb_db.py b/pyfpdb/fpdb_db.py index e8ff8109..a0939b39 100644 --- a/pyfpdb/fpdb_db.py +++ b/pyfpdb/fpdb_db.py @@ -66,17 +66,16 @@ class fpdb_db: psycopg2.extensions.register_type(psycopg2.extensions.UNICODE) # If DB connection is made over TCP, then the variables # host, user and password are required - if self.host and self.user and self.password: + # For local domain-socket connections, only DB name is + # needed, and everything else is in fact undefined and/or + # flat out wrong + if self.host == "localhost" or self.host == "127.0.0.1": + self.db = psycopg2.connect(database = database) + else: self.db = psycopg2.connect(host = host, user = user, password = password, database = database) - # For local domain-socket connections, only DB name is - # needed, and everything else is in fact undefined and/or - # flat out wrong - else: - self.db = psycopg2.connect(database = database) -# self.db.set_client_encoding('UNICODE') else: raise fpdb_simple.FpdbError("unrecognised database backend:"+backend) self.cursor=self.db.cursor() From 7f7a298bcb3e172811af0e65bfbc1cf7e60c724f Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 4 Jun 2009 16:52:07 -0400 Subject: [PATCH 06/13] merging, I think --- pyfpdb/BetfairToFpdb.py | 241 ---------------------------------------- 1 file changed, 241 deletions(-) diff --git a/pyfpdb/BetfairToFpdb.py b/pyfpdb/BetfairToFpdb.py index bc1d17c9..e69de29b 100755 --- a/pyfpdb/BetfairToFpdb.py +++ b/pyfpdb/BetfairToFpdb.py @@ -1,241 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright 2008, Carl Gherardi -# -# 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 -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -######################################################################## - -import sys -import logging -from HandHistoryConverter import * - -# Betfair HH format - -class Betfair(HandHistoryConverter): - - # Static regexes - re_GameInfo = re.compile("^(?PNL|PL|) (?P\$|)?(?P[.0-9]+)/\$?(?P[.0-9]+) (?P(Texas Hold\'em|Omaha Hi|Razz))", re.MULTILINE) - re_SplitHands = re.compile(r'\n\n+') - re_HandInfo = re.compile("\*\*\*\*\* Betfair Poker Hand History for Game (?P[0-9]+) \*\*\*\*\*\n(?PNL|PL|) (?P\$|)?(?P[.0-9]+)/\$?(?P[.0-9]+) (?P(Texas Hold\'em|Omaha Hi|Razz)) - (?P[a-zA-Z]+, [a-zA-Z]+ \d+, \d\d:\d\d:\d\d GMT \d\d\d\d)\nTable (?P[ a-zA-Z0-9]+) \d-max \(Real Money\)\nSeat (?P
[ a-zA-Z0-9]+) \d-max \(Real Money\)\nSeat (?P