From f55460341d22a29e31f1863d511199e7f281ef4b Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 21 May 2009 11:13:39 -0400 Subject: [PATCH 01/24] 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/24] 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/24] 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/24] 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/24] 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/24] 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


Table HandsPlayers

-

cardX: can be 1 through 20, one for each card. In holdem only 1-2 of these are used, in omaha 1-4, in stud/razz 1-7, in single draw 1-10, in tripple draw all 20 and in badugi 1-16 (4*4).

-

For the draw games: the first 5 (badugi: 4) cards are the initial cards, the next 5 (badugi: 4) are after the first draw, etc.
-Example 1: If a player gets 2-6 spades for his first five cards and decides to throw away the 4 and then gets a 7 of spades then the first 10 fields of cardXValue would be as follows: 2, 3, 4, 5, 6, 2, 3, 5, 6, 7
-Example 2: If a player gets 2, 3, 5, 8, J of spades for his first five cards and decides to throw away the 2 and the 3 and then gets a Q and K of spades then the first 10 fields of cardXValue would be as follows: 2, 3, 5, 8, J, 5, 8, J, Q, K.

+

cardX: can be 1 through 20, one for each card. In holdem only 1-2 of these are used, in omaha 1-4, in stud/razz 1-7, in single draw games 1-10 is used and in badugi 1-16 (4*4) is used.

+

For the draw games: the first 5 (badugi: 4) cards are the initial cards, the next 5 (badugi: 4) are after the first draw. If a player keeps some cards then those cards' spaces are filled with "k", short for "kept".
+Example 1: If a player gets 2-6 spades for his first five cards and decides to throw away the 4 and then gets a 7 of spades then the first 10 fields of cardXValue would be as follows: 2, 3, 4, 5, 6, k, k, 7, k, k
+Example 2: If a player gets 2, 3, 5, 8, J of spades for his first five cards and decides to throw away the 2 and the 3 and then gets a Q and K of spades then the first 10 fields of cardXValue would be as follows: 2, 3, 5, 8, J, Q, K, k, k, k
+Note that it will k in the space of which card was there previously, so in example 2 where the player kept the last 3 cards, the last 3 fields of the first draw (ie. card8-10Value) are replaced with k.

I did not separate this into an extra table because I felt the lost space is not sufficiently large. Also the benefit for searching is far less relevant.

+

ToDo: Original plan was to implement the many flags from hudcache as booleans - need to try this out as it will save space and may therefore be quicker.

@@ -353,33 +355,24 @@ Example 2: If a player gets 2, 3, 5, 8, J of spades for his first five cards and + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - @@ -388,7 +381,12 @@ Example 2: If a player gets 2, 3, 5, 8, J of spades for his first five cards and - + + + + + + @@ -405,6 +403,384 @@ Example 2: If a player gets 2, 3, 5, 8, J of spades for his first five cards and + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Field Name

smallint

The seat in which the person was sitting - necessary for HUD

card1(..7)

smallint

0=none/unknown, 1-13=2-Ah 14-26=2-Ad 27-39=2-Ac 40-52=2-As

startCards

smallint

int representing Holdem starting cards.
Hand is stored as an int 13 * x + y where x and y +are in range 0..12, and (x+2) and (y+2) represents rank of each card (2=2 .. 14=Ace).
+If x > y then pair is suited, if x < y then unsuited.
+Omaha and other games may need to use this as a key into another table. (to be decided ...)

ante

int

note: for cash this could be boolean, but in tourneys you may enter a hand with less than the full ante

cardXValue

smallint

2-10=2-10, J=11, Q=12, K=13, A=14 (even in razz), unknown/no card=x
- see note above table

cardXSuit

char(1)

h=hearts, s=spades, d=diamonds, c=clubs, unknown/no card=x

cardXDiscarded

boolean

Whether the card was discarded (this only applies to draw games, X can be 1 through 15 since the final cards can obviously not be discarded).

DrawnX

smallint

X can be 1 through 3.
- This field denotes how many cards the player has drawn on each draw.

winnings

int

rake

int

rake for this player for this hand

rake for this player for this hand (i.e. final pot(s) size = winnings + rake)

totalProfit

int

profit for this player for this hand ( i.e. winnings - (ante + bets) )

comment

bigint

references TourneysPlayers.id

tourneyTypeId

bigint

references TourneyTypes.id (maybe this should be on Hands?)

wonWhenSeenStreet1(..4)

float

How many hands the player won after seeing the flop/street4 - this can be a "partial win" if the pot is split.
+ To be completely clear, this stores a hand count, NOT a money amount.
+ (2/3/4: Same for turn/street5, river/street6, street7)

wonAtSD

float

As wonWhenSeenStreet1, but for showdown.

street0VPI

int

did player pay to see flop, 1 or 0

street0Aggr

int

did player raise before flop, 1 or 0

street0_3BChance

int

did player have chance to 3B, 1 or 0

street0_3BDone

int

did player 3bet before flop, 1 or 0

street0_4BChance

int

did player have chance to 4B, 1 or 0

street0_4BDone

int

did player 4bet before flop, 1 or 0

other_3BStreet0

int

did other player 3bet before flop, 1 or 0

other_4BStreet0

int

did other player 4bet before flop, 1 or 0

street1Seen(/2/3/4)

int

did player see flop/street4 (.. etc)

sawShowdown

int

did player see showdown

street1Aggr

int

number of hands where player raised flop/street4

street2Aggr

int

number of hands where player raised turn/street5

street3Aggr

int

number of hands where player raised river/street6

street4Aggr

int

number of hands where player raised street7

otherRaisedStreet0

int

number of hands where someone else raised pre-flop/street3

otherRaisedStreet1

int

number of hands where someone else raised flop/street4

otherRaisedStreet2

int

number of hands where someone else raised turn/street5

otherRaisedStreet3

int

number of hands where someone else raised river/street6

otherRaisedStreet4

int

number of hands where someone else raised street7

foldToOtherRaisedStreet0

int

number of hands where someone else raised flop/street4 and the player folded

foldToOtherRaisedStreet1

int

number of hands where someone else raised flop/street4 and the player folded

foldToOtherRaisedStreet2

int

number of hands where someone else raised Turn/street5 and the player folded

foldToOtherRaisedStreet3

int

number of hands where someone else raised River/street6 and the player folded

foldToOtherRaisedStreet4

int

number of hands where someone else raised street7 and the player folded

stealAttemptChance

int

Player was in CO, BTN or SB and nobody has called yet

stealAttempted

int

Player took a chance per the above condition

foldBbToStealChance

int

Somebody tried to steal BB from player

foldedBbToSteal

int

Player folded BB to steal attempt

foldSbToStealChance

int

Somebody tried to steal SB from player

foldedSbToSteal

int

Player folded SB to steal attempt

street1CBChance

int

Player had chance to make continuation bet on flop/street4

street1CBDone

int

Player used chance to make continuation bet on flop/street4

street2CBChance

int

Player had chance to make continuation bet on turn/street5

street2CBDone

int

Player used chance to make continuation bet on turn/street5

street3CBChance

int

Player had chance to make continuation bet on river/street6

street3CBDone

int

Player used chance to make continuation bet on river/street6

street4CBChance

int

Player had chance to make continuation bet on street7

street4CBDone

int

Player used chance to make continuation bet on street7

foldToStreet1CBChance

int

Player had chance to fold to continuation bet on this street

foldToStreet1CBDone

int

Player used chance to fold to continuation bet on this street

foldToStreet2CBChance

int

Player had chance to fold to continuation bet on this street

foldToStreet2CBDone

int

Player used chance to fold to continuation bet on this street

foldToStreet3CBChance

int

Player had chance to fold to continuation bet on this street

foldToStreet3CBDone

int

Player used chance to fold to continuation bet on this street

foldToStreet4CBChance

int

Player had chance to fold to continuation bet on this street

foldToStreet4CBDone

int

Player used chance to fold to continuation bet on this street

street1CheckCallRaiseChance

int

How often player had the chance to do a check-raise or a call-raise on this street

street1CheckCallRaiseDone

int

How often player used the chance to do a check-raise or a call-raise on this street

street2CheckCallRaiseChance

int

How often player had the chance to do a check-raise or a call-raise on this street

street2CheckCallRaiseDone

int

How often player used the chance to do a check-raise or a call-raise on this street

street3CheckCallRaiseChance

int

How often player had the chance to do a check-raise or a call-raise on this street

street3CheckCallRaiseDone

int

How often player used the chance to do a check-raise or a call-raise on this street

street4CheckCallRaiseChance

int

How often player had the chance to do a check-raise or a call-raise on this street

street4CheckCallRaiseDone

int

How often player used the chance to do a check-raise or a call-raise on this street

street0Calls

int

Number of times player called on this street

street1Calls

int

Number of times player called on this street

street2Calls

int

Number of times player called on this street

street3Calls

int

Number of times player called on this street

street4Calls

int

Number of times player called on this street

street0Bets

int

Number of times player bet on this street

street1Bets

int

Number of times player bet on this street

street2Bets

int

Number of times player bet on this street

street3Bets

int

Number of times player bet on this street

street4Bets

int

Number of times player bet on this street

street0Raises

int

Number of times player raised on this street

street1Raises

int

Number of times player raised on this street

street2Raises

int

Number of times player raised on this street

street3Raises

int

Number of times player raised on this street

street4Raises

int

Number of times player raised on this street

actionString

int

Experimental - idea is to store the action on this street as a string: e.g. kkBrcfC, with + player's own choices in upper case and other players in lower case. k=check, b=bet, c=call, + r=raise. (Perhaps NL would miss out bet sizes for this?) It would then be possible to do complex + ad-hoc queries using queries like: actionString like '%B%r%C% +


Table HudCache

@@ -444,12 +820,23 @@ Example 2: If a player gets 2, 3, 5, 8, J of spades for his first five cards and

smallint

References TourneyTypes.id

-

HDs

int

number of hands this player played in this gametype with this number of seats

+ +

wonWhenSeenStreet1(/2/3/4)

+

float

+

How many hands the player won after seeing the flop/street4 - this can be a "partial win" if the pot is split.
+ To be completely clear, this stores a hand count, NOT a money amount.
+ (/2/3/4: Same for turn/street5, river/street6, street7)

+ + +

wonAtSD

+

float

+

As wonWhenSeenStreet1, but for showdown.

+

street0VPI

int

@@ -463,14 +850,24 @@ Example 2: If a player gets 2, 3, 5, 8, J of spades for his first five cards and

number of hands where player raised before flop

-

street0_3B4BChance

+

street0_3BChance

int

-

number of hands where player had chance to 3B or 4B

+

number of hands where player had chance to 3B before flop

-

street0_3B4BDone

+

street0_3BDone

int

-

number of hands where player 3bet/4bet before flop

+

number of hands where player 3bet before flop

+ + +

street0_4BChance

+

int

+

number of hands where player had chance to 4B before flop

+ + +

street0_4BDone

+

int

+

number of hands where player 4bet before flop

street1Seen

@@ -517,6 +914,11 @@ Example 2: If a player gets 2, 3, 5, 8, J of spades for his first five cards and

int

number of hands where player raised street7

+ +

otherRaisedStreet0

+

int

+

number of hands where someone else raised pre-flop/street3

+

otherRaisedStreet1

int

@@ -537,6 +939,11 @@ Example 2: If a player gets 2, 3, 5, 8, J of spades for his first five cards and

int

number of hands where someone else raised street7

+ +

foldToOtherRaisedStreet0

+

int

+

number of hands where someone else raised pre-flop/street3 and the player folded

+

foldToOtherRaisedStreet1

int

@@ -557,18 +964,6 @@ Example 2: If a player gets 2, 3, 5, 8, J of spades for his first five cards and

int

number of hands where someone else raised street7 and the player folded

- -

wonWhenSeenStreet1

-

float

-

How many hands the player won after seeing the flop/street4 - this can be a "partial win" if the pot is split.
- To be completely clear, this stores a hand count, NOT a money amount.

- - -

wonAtSD

-

float

-

As wonWhenSeenStreet1, but for showdown.

- -

stealAttemptChance

int

@@ -729,6 +1124,84 @@ Example 2: If a player gets 2, 3, 5, 8, J of spades for his first five cards and

How often player used the chance to do a check-raise or a call-raise on this street

+ +

street0Calls

+

int

+

Number of times player called on this street

+ + +

street1Calls

+

int

+

Number of times player called on this street

+ + +

street2Calls

+

int

+

Number of times player called on this street

+ + +

street3Calls

+

int

+

Number of times player called on this street

+ + +

street4Calls

+

int

+

Number of times player called on this street

+ + + +

street0Bets

+

int

+

Number of times player bet on this street

+ + +

street1Bets

+

int

+

Number of times player bet on this street

+ + +

street2Bets

+

int

+

Number of times player bet on this street

+ + +

street3Bets

+

int

+

Number of times player bet on this street

+ + +

street4Bets

+

int

+

Number of times player bet on this street

+ + + +

street0Raises

+

int

+

Number of times player raised on this street

+ + +

street1Raises

+

int

+

Number of times player raised on this street

+ + +

street2Raises

+

int

+

Number of times player raised on this street

+ + +

street3Raises

+

int

+

Number of times player raised on this street

+ + +

street4Raises

+

int

+

Number of times player raised on this street

+ +

Table HandsActions

@@ -926,5 +1399,32 @@ Example 2: If a player gets 2, 3, 5, 8, J of spades for his first five cards and


+


+

Possible Changes

+ + + + + + + + + + + + + + + + + + + + + +

Table

Comment

BoardCards

Remove as these attributes are now stored on Hands

HandsActions

Remove if/when these attributes are stored on Hands or elsewhere

HandsPlayers

Move tourneyTypeId field to Hands table.

Comments

Comment fields on various tables should probably be moved to a single comment table. Aim + should be to where possible reduce tables to a list of fixed length not-null columns and have + the larger, sparser comment columns in a dedicated table. (May not be possible or practical but + something to aim at.)

From 0a7c3406563645315e9eb3a22e483ef94d25033c Mon Sep 17 00:00:00 2001 From: sqlcoder Date: Sun, 7 Jun 2009 22:21:58 +0100 Subject: [PATCH 18/24] Added timing info for index drop/create. Some small mods to output during import (obviously just change any you don't like) --- pyfpdb/HandHistoryConverter.py | 2 +- pyfpdb/fpdb_db.py | 14 +++++++++++--- pyfpdb/fpdb_import.py | 2 +- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/pyfpdb/HandHistoryConverter.py b/pyfpdb/HandHistoryConverter.py index 8b9eba63..f5c4c2a5 100644 --- a/pyfpdb/HandHistoryConverter.py +++ b/pyfpdb/HandHistoryConverter.py @@ -132,7 +132,7 @@ Otherwise, finish at eof... self.processHand(handText) numHands= len(handsList) endtime = time.time() - print "Processed %d hands in %.3f seconds" % (numHands, endtime - starttime) + print "read %d hands in %.3f seconds" % (numHands, endtime - starttime) if self.out_fh != sys.stdout: self.out_fh.close() diff --git a/pyfpdb/fpdb_db.py b/pyfpdb/fpdb_db.py index 474d694b..5ab62859 100644 --- a/pyfpdb/fpdb_db.py +++ b/pyfpdb/fpdb_db.py @@ -18,6 +18,7 @@ import os import re import sys +from time import time, strftime import fpdb_simple import FpdbSQLQueries @@ -175,7 +176,7 @@ 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 - print "host=%s user=%s pass=%s." % (host, user, password) + # print "host=%s user=%s pass=%s." % (host, user, password) if self.host and self.user and self.password: try: self.db = psycopg2.connect(host = host, @@ -327,7 +328,7 @@ class fpdb_db: def prepareBulkImport(self): """Drop some indexes/foreign keys to prepare for bulk import. Currently keeping the standalone indexes as needed to import quickly""" - # self is a fpdb_db object including backend, db, cursor, sql variables + stime = time() if self.backend == self.PGSQL: self.db.set_isolation_level(0) # allow table/index operations to work for fk in self.foreignKeys[self.backend]: @@ -410,11 +411,13 @@ class fpdb_db: if self.backend == self.PGSQL: self.db.set_isolation_level(1) # go back to normal isolation level self.db.commit() # seems to clear up errors if there were any in postgres + ptime = time() - stime + print "prepare import took", ptime, "seconds" #end def prepareBulkImport def afterBulkImport(self): """Re-create any dropped indexes/foreign keys after bulk import""" - # self is a fpdb_db object including backend, db, cursor, sql variables + stime = time() if self.backend == self.PGSQL: self.db.set_isolation_level(0) # allow table/index operations to work for fk in self.foreignKeys[self.backend]: @@ -480,6 +483,8 @@ class fpdb_db: if self.backend == self.PGSQL: self.db.set_isolation_level(1) # go back to normal isolation level self.db.commit() # seems to clear up errors if there were any in postgres + atime = time() - stime + print "after import took", atime, "seconds" #end def afterBulkImport def createAllIndexes(self): @@ -542,6 +547,7 @@ class fpdb_db: def analyzeDB(self): """Do whatever the DB can offer to update index/table statistics""" + stime = time() if self.backend == self.PGSQL: self.db.set_isolation_level(0) # allow vacuum to work try: @@ -550,6 +556,8 @@ class fpdb_db: print "Error during vacuum" self.db.set_isolation_level(1) # go back to normal isolation level self.db.commit() + atime = time() - stime + print "analyze took", atime, "seconds" #end def analyzeDB # Currently uses an exclusive lock on the Hands table as a global lock diff --git a/pyfpdb/fpdb_import.py b/pyfpdb/fpdb_import.py index a1e2a835..cffe8598 100644 --- a/pyfpdb/fpdb_import.py +++ b/pyfpdb/fpdb_import.py @@ -256,7 +256,7 @@ class Importer: conv = None # Load filter, process file, pass returned filename to import_fpdb_file - print "converting %s" % file + print "\nConverting %s" % file hhbase = self.config.get_import_parameters().get("hhArchiveBase") hhbase = os.path.expanduser(hhbase) hhdir = os.path.join(hhbase,site) From fce9941b0c761d237a5692daaef75f7d1aceace5 Mon Sep 17 00:00:00 2001 From: sqlcoder Date: Mon, 8 Jun 2009 00:17:48 +0100 Subject: [PATCH 19/24] add position capability to playerstats tab --- pyfpdb/Filters.py | 47 +++++++++++++++++++++- pyfpdb/FpdbSQLQueries.py | 18 +++++++-- pyfpdb/GuiPlayerStats.py | 86 +++++++++++++++++++++++++--------------- 3 files changed, 114 insertions(+), 37 deletions(-) diff --git a/pyfpdb/Filters.py b/pyfpdb/Filters.py index 3a5f0365..4846b998 100644 --- a/pyfpdb/Filters.py +++ b/pyfpdb/Filters.py @@ -44,6 +44,7 @@ class Filters(threading.Thread): self.games = {} self.limits = {} self.seats = {} + self.groups = {} self.siteid = {} self.heroes = {} self.boxes = {} @@ -52,6 +53,7 @@ class Filters(threading.Thread): self.filterText = {'limitsall':'All', 'limitsnone':'None', 'limitsshow':'Show _Limits' ,'seatsbetween':'Between:', 'seatsand':'And:', 'seatsshow':'Show Number of _Players' ,'limitstitle':'Limits:', 'seatstitle':'Number of Players:' + ,'groupstitle':'Grouping:', 'posnshow':'Show Position Stats:' } # For use in date ranges. @@ -109,6 +111,15 @@ class Filters(threading.Thread): self.fillSeatsFrame(vbox, self.display) seatsFrame.add(vbox) + # Groups + groupsFrame = gtk.Frame() + groupsFrame.show() + vbox = gtk.VBox(False, 0) + self.sbGroups = {} + + self.fillGroupsFrame(vbox, self.display) + groupsFrame.add(vbox) + # Date dateFrame = gtk.Frame("Date:") dateFrame.set_label_align(0.0, 0.0) @@ -131,6 +142,7 @@ class Filters(threading.Thread): self.mainVBox.add(gamesFrame) self.mainVBox.add(limitsFrame) self.mainVBox.add(seatsFrame) + self.mainVBox.add(groupsFrame) self.mainVBox.add(dateFrame) self.mainVBox.add(self.Button1) self.mainVBox.add(self.Button2) @@ -148,6 +160,8 @@ class Filters(threading.Thread): limitsFrame.hide() if "Seats" not in self.display or self.display["Seats"] == False: seatsFrame.hide() + if "Groups" not in self.display or self.display["Groups"] == False: + groupsFrame.hide() if "Dates" not in self.display or self.display["Dates"] == False: dateFrame.hide() if "Button1" not in self.display or self.display["Button1"] == False: @@ -183,6 +197,9 @@ class Filters(threading.Thread): self.seats['to'] = self.sbSeats['to'].get_value_as_int() return self.seats + def getGroups(self): + return self.groups + def getDates(self): return self.__get_dates() @@ -274,6 +291,11 @@ class Filters(threading.Thread): self.seats[seat] = w.get_active() print "self.seats[%s] set to %s" %(seat, self.seats[seat]) + def __set_group_select(self, w, group): + #print "__set_seat_select: seat =", seat, "active =", w.get_active() + self.groups[group] = w.get_active() + print "self.groups[%s] set to %s" %(group, self.groups[group]) + def fillPlayerFrame(self, vbox): for site in self.conf.get_supported_sites(): pathHBox = gtk.HBox(False, 0) @@ -389,10 +411,33 @@ class Filters(threading.Thread): self.sbSeats['show'] = cb self.seats['show'] = False - self.sbSeats['from'] = sb1 self.sbSeats['to'] = sb2 + def fillGroupsFrame(self, vbox, display): + hbox = gtk.HBox(False, 0) + vbox.pack_start(hbox, False, False, 0) + lbl_title = gtk.Label(self.filterText['groupstitle']) + lbl_title.set_alignment(xalign=0.0, yalign=0.5) + hbox.pack_start(lbl_title, expand=True, padding=3) + showb = gtk.Button(label="hide", stock=None, use_underline=True) + showb.set_alignment(xalign=1.0, yalign=0.5) + showb.connect('clicked', self.__toggle_box, 'groups') + hbox.pack_start(showb, expand=False, padding=1) + + vbox1 = gtk.VBox(False, 0) + vbox.pack_start(vbox1, False, False, 0) + self.boxes['groups'] = vbox1 + + hbox = gtk.HBox(False, 0) + vbox1.pack_start(hbox, False, True, 0) + + cb = gtk.CheckButton(self.filterText['posnshow']) + cb.connect('clicked', self.__set_group_select, 'posn') + hbox.pack_start(cb, False, False, 0) + self.sbGroups['posn'] = cb + self.groups['posn'] = False + def fillCardsFrame(self, vbox): hbox1 = gtk.HBox(True,0) hbox1.show() diff --git a/pyfpdb/FpdbSQLQueries.py b/pyfpdb/FpdbSQLQueries.py index 4445908e..598c0868 100644 --- a/pyfpdb/FpdbSQLQueries.py +++ b/pyfpdb/FpdbSQLQueries.py @@ -910,6 +910,7 @@ class FpdbSQLQueries: ,min(gt.bigBlind) AS minbigblind ,max(gt.bigBlind) AS maxbigblind /*, AS gtid*/ + , AS plposition ,count(1) AS n ,100.0*sum(cast(hp.street0VPI as integer))/count(1) AS vpip ,100.0*sum(cast(hp.street0Aggr as integer))/count(1) AS pfr @@ -963,12 +964,17 @@ class FpdbSQLQueries: ,gt.base ,gt.category + ,plposition ,upper(gt.limitType) ,s.name order by hp.playerId ,gt.base ,gt.category + ,case when 'B' then 'B' + when 'S' then 'S' + else concat('Z', ) + end ,maxbigblind desc ,upper(gt.limitType) @@ -983,7 +989,8 @@ class FpdbSQLQueries: ,s.name ,min(gt.bigBlind) AS minbigblind ,max(gt.bigBlind) AS maxbigblind - /*, AS gtid*/ + /*, AS gtid*/ + , AS plposition ,count(1) AS n ,100.0*sum(cast(hp.street0VPI as integer))/count(1) AS vpip ,100.0*sum(cast(hp.street0Aggr as integer))/count(1) AS pfr @@ -993,8 +1000,8 @@ class FpdbSQLQueries: ,case when sum(cast(hp.stealattemptchance as integer)) = 0 then -999 else 100.0*sum(cast(hp.stealattempted as integer))/sum(cast(hp.stealattemptchance as integer)) end AS steals - ,100.0*sum(cast(hp.street1Seen as integer))/count(1) AS saw_f - ,100.0*sum(cast(hp.sawShowdown as integer))/count(1) AS sawsd + ,100.0*sum(cast(hp.street1Seen as integer))/count(1) AS saw_f + ,100.0*sum(cast(hp.sawShowdown as integer))/count(1) AS sawsd ,case when sum(cast(hp.street1Seen as integer)) = 0 then -999 else 100.0*sum(cast(hp.sawShowdown as integer))/sum(cast(hp.street1Seen as integer)) end AS wtsdwsf @@ -1037,12 +1044,17 @@ class FpdbSQLQueries: ,gt.base ,gt.category + ,plposition ,upper(gt.limitType) ,s.name order by hp.playerId ,gt.base ,gt.category + ,case when 'B' then 'B' + when 'S' then 'S' + else 'Z'|| + end ,maxbigblind desc ,upper(gt.limitType) diff --git a/pyfpdb/GuiPlayerStats.py b/pyfpdb/GuiPlayerStats.py index 28990279..268aeab3 100644 --- a/pyfpdb/GuiPlayerStats.py +++ b/pyfpdb/GuiPlayerStats.py @@ -76,27 +76,28 @@ class GuiPlayerStats (threading.Thread): # ToDo: create popup to adjust column config # columns to display, keys match column name returned by sql, values in tuple are: # is column displayed, column heading, xalignment, formatting - self.columns = [ ("game", True, "Game", 0.0, "%s") - , ("hand", False, "Hand", 0.0, "%s") # true not allowed for this line - , ("n", True, "Hds", 1.0, "%d") - , ("avgseats", True, "Seats", 1.0, "%3.1f") - , ("vpip", True, "VPIP", 1.0, "%3.1f") - , ("pfr", True, "PFR", 1.0, "%3.1f") - , ("pf3", True, "PF3", 1.0, "%3.1f") - , ("steals", True, "Steals", 1.0, "%3.1f") - , ("saw_f", True, "Saw_F", 1.0, "%3.1f") - , ("sawsd", True, "SawSD", 1.0, "%3.1f") - , ("wtsdwsf", True, "WtSDwsF", 1.0, "%3.1f") - , ("wmsd", True, "W$SD", 1.0, "%3.1f") - , ("flafq", True, "FlAFq", 1.0, "%3.1f") - , ("tuafq", True, "TuAFq", 1.0, "%3.1f") - , ("rvafq", True, "RvAFq", 1.0, "%3.1f") - , ("pofafq", False, "PoFAFq", 1.0, "%3.1f") - , ("net", True, "Net($)", 1.0, "%6.2f") - , ("bbper100", True, "bb/100", 1.0, "%4.2f") - , ("rake", True, "Rake($)", 1.0, "%6.2f") - , ("bb100xr", True, "bbxr/100", 1.0, "%4.2f") - , ("variance", True, "Variance", 1.0, "%5.2f") + self.columns = [ ["game", True, "Game", 0.0, "%s"] + , ["hand", False, "Hand", 0.0, "%s"] # true not allowed for this line + , ["plposition", False, "Posn", 1.0, "%s"] # true not allowed for this line (set in code) + , ["n", True, "Hds", 1.0, "%d"] + , ["avgseats", True, "Seats", 1.0, "%3.1f"] + , ["vpip", True, "VPIP", 1.0, "%3.1f"] + , ["pfr", True, "PFR", 1.0, "%3.1f"] + , ["pf3", True, "PF3", 1.0, "%3.1f"] + , ["steals", True, "Steals", 1.0, "%3.1f"] + , ["saw_f", True, "Saw_F", 1.0, "%3.1f"] + , ["sawsd", True, "SawSD", 1.0, "%3.1f"] + , ["wtsdwsf", True, "WtSDwsF", 1.0, "%3.1f"] + , ["wmsd", True, "W$SD", 1.0, "%3.1f"] + , ["flafq", True, "FlAFq", 1.0, "%3.1f"] + , ["tuafq", True, "TuAFq", 1.0, "%3.1f"] + , ["rvafq", True, "RvAFq", 1.0, "%3.1f"] + , ["pofafq", False, "PoFAFq", 1.0, "%3.1f"] + , ["net", True, "Net($)", 1.0, "%6.2f"] + , ["bbper100", True, "bb/100", 1.0, "%4.2f"] + , ["rake", True, "Rake($)", 1.0, "%6.2f"] + , ["bb100xr", True, "bbxr/100", 1.0, "%4.2f"] + , ["variance", True, "Variance", 1.0, "%5.2f"] ] # Detail filters: This holds the data used in the popup window, extra values are @@ -136,7 +137,7 @@ class GuiPlayerStats (threading.Thread): self.main_hbox.pack_start(self.stats_frame, expand=True, fill=True) # make sure Hand column is not displayed - [x for x in self.columns if x[0] == 'hand'][0][1] == False + [x for x in self.columns if x[0] == 'hand'][0][1] = False def get_vbox(self): """returns the vbox of this thread""" @@ -156,6 +157,7 @@ class GuiPlayerStats (threading.Thread): siteids = self.filters.getSiteIds() limits = self.filters.getLimits() seats = self.filters.getSeats() + groups = self.filters.getGroups() dates = self.filters.getDates() sitenos = [] playerids = [] @@ -180,16 +182,16 @@ class GuiPlayerStats (threading.Thread): print "No limits found" return - self.createStatsTable(vbox, playerids, sitenos, limits, seats, dates) + self.createStatsTable(vbox, playerids, sitenos, limits, seats, groups, dates) - def createStatsTable(self, vbox, playerids, sitenos, limits, seats, dates): + def createStatsTable(self, vbox, playerids, sitenos, limits, seats, groups, dates): starttime = time() # Display summary table at top of page # 3rd parameter passes extra flags, currently includes: # holecards - whether to display card breakdown (True/False) flags = [False] - self.addTable(vbox, 'playerDetailedStats', flags, playerids, sitenos, limits, seats, dates) + self.addTable(vbox, 'playerDetailedStats', flags, playerids, sitenos, limits, seats, groups, dates) # Separator sep = gtk.HSeparator() @@ -212,13 +214,13 @@ class GuiPlayerStats (threading.Thread): # Detailed table flags = [True] - self.addTable(vbox1, 'playerDetailedStats', flags, playerids, sitenos, limits, seats, dates) + self.addTable(vbox1, 'playerDetailedStats', flags, playerids, sitenos, limits, seats, groups, dates) self.db.db.commit() print "Stats page displayed in %4.2f seconds" % (time() - starttime) #end def fillStatsFrame(self, vbox): - def addTable(self, vbox, query, flags, playerids, sitenos, limits, seats, dates): + def addTable(self, vbox, query, flags, playerids, sitenos, limits, seats, groups, dates): row = 0 sqlrow = 0 colalias,colshow,colheading,colxalign,colformat = 0,1,2,3,4 @@ -231,7 +233,7 @@ class GuiPlayerStats (threading.Thread): self.stats_table.show() tmp = self.sql.query[query] - tmp = self.refineQuery(tmp, flags, playerids, sitenos, limits, seats, dates) + tmp = self.refineQuery(tmp, flags, playerids, sitenos, limits, seats, groups, dates) self.cursor.execute(tmp) result = self.cursor.fetchall() colnames = [desc[0].lower() for desc in self.cursor.description] @@ -245,6 +247,8 @@ class GuiPlayerStats (threading.Thread): view.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_BOTH) vbox.pack_start(view, expand=False, padding=3) textcell = gtk.CellRendererText() + textcell50 = gtk.CellRendererText() + textcell50.set_property('xalign', 0.5) numcell = gtk.CellRendererText() numcell.set_property('xalign', 1.0) listcols = [] @@ -258,17 +262,18 @@ class GuiPlayerStats (threading.Thread): listcols.append(gtk.TreeViewColumn(s)) view.append_column(listcols[col]) if column[colformat] == '%s': - if col == 1 and holecards: + if column[colxalign] == 0.0: listcols[col].pack_start(textcell, expand=True) + listcols[col].add_attribute(textcell, 'text', col) else: - listcols[col].pack_start(textcell, expand=True) - listcols[col].add_attribute(textcell, 'text', col) + listcols[col].pack_start(textcell50, expand=True) + listcols[col].add_attribute(textcell50, 'text', col) listcols[col].set_expand(True) else: listcols[col].pack_start(numcell, expand=True) listcols[col].add_attribute(numcell, 'text', col) - listcols[col].set_alignment(1.0) listcols[col].set_expand(True) + #listcols[col].set_alignment(column[colxalign]) # no effect? rows = len(result) # +1 for title row @@ -281,6 +286,11 @@ class GuiPlayerStats (threading.Thread): for col,column in enumerate(cols_to_show): if column[colalias] in colnames: value = result[sqlrow][colnames.index(column[colalias])] + if column[colalias] == 'plposition': + if value == 'B': + value = 'BB' + if value == 'S': + value = 'SB' else: if column[colalias] == 'game': if holecards: @@ -313,7 +323,7 @@ class GuiPlayerStats (threading.Thread): #end def addTable(self, query, vars, playerids, sitenos, limits, seats): - def refineQuery(self, query, flags, playerids, sitenos, limits, seats, dates): + def refineQuery(self, query, flags, playerids, sitenos, limits, seats, groups, dates): if not flags: holecards = False else: holecards = flags[0] @@ -376,6 +386,16 @@ class GuiPlayerStats (threading.Thread): # Filter on dates query = query.replace("", " between '" + dates[0] + "' and '" + dates[1] + "'") + # Group by position? + if groups['posn']: + query = query.replace("", 'hp.position') + # set flag in self.columns to show posn column + [x for x in self.columns if x[0] == 'plposition'][0][1] = True + else: + query = query.replace("", "'1'") + # unset flag in self.columns to hide posn column + [x for x in self.columns if x[0] == 'plposition'][0][1] = False + #print "query =\n", query return(query) #end def refineQuery(self, query, playerids, sitenos, limits): From 4f555e2d8f8007c44b0edbb4b59bfe8e5a341bc6 Mon Sep 17 00:00:00 2001 From: Worros Date: Mon, 8 Jun 2009 18:19:55 +0800 Subject: [PATCH 20/24] Hand.insert() update - low hanging fruit --- pyfpdb/Hand.py | 11 +++++------ pyfpdb/fpdb_db.py | 24 ++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/pyfpdb/Hand.py b/pyfpdb/Hand.py index 879dad85..129eaede 100644 --- a/pyfpdb/Hand.py +++ b/pyfpdb/Hand.py @@ -92,16 +92,14 @@ db: a connected fpdb_db object""" # HandsActions - all actions for all players for all streets - self.actions # BoardCards - Skip - no longer necessary? # Hands - Summary information of hand indexed by handId - gameinfo - # self.tablename = tableName - # self.handid = siteHandNo + hh['siteHandNo'] = self.handid # gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id), # - # self.starttime = handStart - # importTime DATETIME NOT NULL, - # + hh['handStart'] = self.starttime # seats TINYINT NOT NULL, # - # self.maxseats = maxSeats + hh['tableName'] = self.tablename + hh['maxSeats'] = self.maxseats # boardcard1 smallint, /* 0=none, 1-13=2-Ah 14-26=2-Ad 27-39=2-Ac 40-52=2-As */ # boardcard2 smallint, # boardcard3 smallint, @@ -141,6 +139,7 @@ db: a connected fpdb_db object""" # showdownPot INT, /* pot size at sd/street7 */ # comment TEXT, # commentTs DATETIME + handid = db.storeHand(hh) # HandsPlayers - ? ... Do we fix winnings? # Tourneys ? # TourneysPlayers diff --git a/pyfpdb/fpdb_db.py b/pyfpdb/fpdb_db.py index ad599b13..44ea2352 100644 --- a/pyfpdb/fpdb_db.py +++ b/pyfpdb/fpdb_db.py @@ -241,3 +241,27 @@ class fpdb_db: #We proabably want to cache this return result + + def storeHand(self, p): + #stores into table hands: + self.cursor.execute ("""INSERT INTO Hands + (siteHandNo, gametypeId, handStart, seats, tableName, importTime, maxSeats + ,playersVpi, playersAtStreet1, playersAtStreet2 + ,playersAtStreet3, playersAtStreet4, playersAtShowdown + ,street0Raises, street1Raises, street2Raises + ,street3Raises, street4Raises, street1Pot + ,street2Pot, street3Pot, street4Pot + ,showdownPot + ) + VALUES + (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""" + ,(p['siteHandNo'], gametype_id, p['handStart'], len(names), p['tableName'], datetime.datetime.today(), p['maxSeats'] + ,hudCache['playersVpi'], hudCache['playersAtStreet1'], hudCache['playersAtStreet2'] + ,hudCache['playersAtStreet3'], hudCache['playersAtStreet4'], hudCache['playersAtShowdown'] + ,hudCache['street0Raises'], hudCache['street1Raises'], hudCache['street2Raises'] + ,hudCache['street3Raises'], hudCache['street4Raises'], hudCache['street1Pot'] + ,hudCache['street2Pot'], hudCache['street3Pot'], hudCache['street4Pot'] + ,hudCache['showdownPot'] + ) + ) + #return getLastInsertId(backend, conn, cursor) From 8e1404032b92e967c61861cd2cb6405fda2e14fb Mon Sep 17 00:00:00 2001 From: Worros Date: Mon, 8 Jun 2009 22:13:42 +0800 Subject: [PATCH 21/24] Fix last patch --- pyfpdb/Hand.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pyfpdb/Hand.py b/pyfpdb/Hand.py index ee6f36ab..c505a68a 100644 --- a/pyfpdb/Hand.py +++ b/pyfpdb/Hand.py @@ -92,14 +92,14 @@ db: a connected fpdb_db object""" # HandsActions - all actions for all players for all streets - self.actions # BoardCards - Skip - no longer necessary? # Hands - Summary information of hand indexed by handId - gameinfo - hh['siteHandNo'] = self.handid + #hh['siteHandNo'] = self.handid # gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id), # - hh['handStart'] = self.starttime + #hh['handStart'] = self.starttime # seats TINYINT NOT NULL, # - hh['tableName'] = self.tablename - hh['maxSeats'] = self.maxseats + #hh['tableName'] = self.tablenam + #hh['maxSeats'] = self.maxseats # boardcard1 smallint, /* 0=none, 1-13=2-Ah 14-26=2-Ad 27-39=2-Ac 40-52=2-As */ # boardcard2 smallint, # boardcard3 smallint, @@ -139,7 +139,7 @@ db: a connected fpdb_db object""" # showdownPot INT, /* pot size at sd/street7 */ # comment TEXT, # commentTs DATETIME - handid = db.storeHand(hh) + # handid = db.storeHand(hh) # HandsPlayers - ? ... Do we fix winnings? # Tourneys ? # TourneysPlayers From 1d6aed32500890ef55553d4d446d103896dc5a23 Mon Sep 17 00:00:00 2001 From: Worros Date: Tue, 9 Jun 2009 22:53:09 +0800 Subject: [PATCH 22/24] Fix datetime.strptime v time.strptime issue --- pyfpdb/EverleafToFpdb.py | 2 +- pyfpdb/FulltiltToFpdb.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyfpdb/EverleafToFpdb.py b/pyfpdb/EverleafToFpdb.py index 6ed2b6ba..ee4942f1 100755 --- a/pyfpdb/EverleafToFpdb.py +++ b/pyfpdb/EverleafToFpdb.py @@ -158,7 +158,7 @@ or None if we fail to get the info """ # 2008/11/10 3:58:52 ET #TODO: Do conversion from GMT to ET #TODO: Need some date functions to convert to different timezones (Date::Manip for perl rocked for this) - hand.starttime = time.strptime(m.group('DATETIME'), "%Y/%m/%d - %H:%M:%S") + hand.starttime = datetime.datetime.strptime(m.group('DATETIME'), "%Y/%m/%d - %H:%M:%S") return def readPlayerStacks(self, hand): diff --git a/pyfpdb/FulltiltToFpdb.py b/pyfpdb/FulltiltToFpdb.py index a84f683c..f7b834a6 100755 --- a/pyfpdb/FulltiltToFpdb.py +++ b/pyfpdb/FulltiltToFpdb.py @@ -124,7 +124,7 @@ follow : whether to tail -f the input""" hand.handid = m.group('HID') hand.tablename = m.group('TABLE') - hand.starttime = time.strptime(m.group('DATETIME'), "%H:%M:%S ET - %Y/%m/%d") + hand.starttime = datetime.datetime.strptime(m.group('DATETIME'), "%H:%M:%S ET - %Y/%m/%d") hand.maxseats = 8 # assume 8-max until we see otherwise if m.group('TABLEATTRIBUTES'): m2 = re.search("(deep )?(\d+)( max)?", m.group('TABLEATTRIBUTES')) From ed1d9cd229b13e9d81290aee36ccc636e76cd2da Mon Sep 17 00:00:00 2001 From: Worros Date: Tue, 9 Jun 2009 23:08:18 +0800 Subject: [PATCH 23/24] Fix tests --- pyfpdb/test_fpdb_simple.py | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/pyfpdb/test_fpdb_simple.py b/pyfpdb/test_fpdb_simple.py index 463e704e..3d9615cb 100755 --- a/pyfpdb/test_fpdb_simple.py +++ b/pyfpdb/test_fpdb_simple.py @@ -16,24 +16,12 @@ def testPokerStarsHHDate(): datetime.datetime(2008,9,7,11,23,14)) ) -def testFullTiltHHDate(): - sitngo1 = "Full Tilt Poker Game #10311865543: $1 + $0.25 Sit & Go (78057629), Table 1 - 25/50 - No Limit Hold'em - 0:07:45 ET - 2009/01/29" - cash1 = "Full Tilt Poker Game #9403951181: Table CR - tay - $0.05/$0.10 - No Limit Hold'em - 9:40:20 ET - 2008/12/09" - cash2 = "Full Tilt Poker Game #9468383505: Table Bike (deep 6) - $0.05/$0.10 - No Limit Hold'em - 5:09:36 ET - 2008/12/13" - - result = fpdb_simple.parseHandStartTime(sitngo1,"ftp") - assert result==datetime.datetime(2009,1,29,05,07,45) - result = fpdb_simple.parseHandStartTime(cash1,"ftp") - assert result==datetime.datetime(2008,12,9,14,40,20) - result = fpdb_simple.parseHandStartTime(cash2,"ftp") - assert result==datetime.datetime(2008,12,13,10,9,36) - - def testTableDetection(): - result = Tables.clean_title("French (deep)") - assert result == "French" - result = Tables.clean_title("French (deep) - $0.25/$0.50 - No Limit Hold'em - Logged In As xxxx") - assert result == "French" - - for (header, site, result) in tuples: - yield checkDateParse, header, site, result +#def testTableDetection(): +# result = Tables.clean_title("French (deep)") +# assert result == "French" +# result = Tables.clean_title("French (deep) - $0.25/$0.50 - No Limit Hold'em - Logged In As xxxx") +# assert result == "French" +# +# for (header, site, result) in tuples: +# yield checkDateParse, header, site, result From 8a55b2ebd9d231b6ce6339daf3dc602b4858ff04 Mon Sep 17 00:00:00 2001 From: Worros Date: Tue, 9 Jun 2009 23:10:11 +0800 Subject: [PATCH 24/24] Remove merge line --- pyfpdb/Database.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index 76052d54..9582354a 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -337,7 +337,6 @@ if __name__=="__main__": # stat_dict = db_connection.get_stats_from_hand(h, hero) # for p in stat_dict.keys(): # print p, " ", stat_dict[p] ->>>>>>> 7ef6a533ec6c73d5815ace42168067a6f8c26e3a:pyfpdb/Database.py print "cards =", db_connection.get_cards(u'1') db_connection.close_connection