p80 - a great many updates from Ray

This commit is contained in:
steffen123 2008-09-15 21:31:55 +01:00
parent 2c251d94f2
commit 7b13331331
16 changed files with 964 additions and 136 deletions

View File

@ -22,6 +22,8 @@ rm pyfpdb/*.pyc
mkdir fpdb-$1
cp -R docs fpdb-$1/
cp -R pyfpdb fpdb-$1/
rm fpdb-$1/HUD_config.*
cp pyfpdb/HUD_config.xml.example fpdb-$1/
cp -R regression-test fpdb-$1/
cp -R utils fpdb-$1/
cd fpdb-$1

View File

@ -2,9 +2,8 @@ todolist (db=database, imp=importer, tv=tableviewer)
Everything is subject to change and the order does not indicate priority. Patches for any of these or other features are very welcome, see readme-overview.txt for contacts.
Please also see db-todo.txt
alpha3 (release 1-2Sep?)
alpha3 (release 15Sep)
======
(fixed by ray) auto import only runs on one file per start
find correct sf logo link
windows integrated installer
@ -20,7 +19,7 @@ fill check-/call-raise cache fields
printhand each and the 2/3 relevant printplayerflags respectively on ps-lhe-ring-successful-steal-by-cutoff.txt and ps-lhe-ring-call-3B-preflop-cb-no2b.txt
alpha4 (release 8Sep?)
alpha4 (release 22-29Sep)
======
change to savannah?
implement steal and positions in stud
@ -33,9 +32,25 @@ change tabledesign VALIGN
finish updating filelist
finish todos in git instructions
debian/ubuntu package http://www.debian.org/doc/maint-guide/ch-start.en.html
howto remote DB
move all user docs to webpage
before beta
===========
No Full Tilt support in HUD
No river stats for stud games
hole/board cards are not correctly stored in the db for stud games
HORSE (and presumably other mixed games) hand history files not handled correctly
HUD stat windows are too big on Windows
HUD task bar entries on Windows won't go away
Some MTTs won't import (rebuys??)
Many STTs won't import
MTT/STT not tested in HUD
HUD stats not aggregated
Player names with non-Latin chars throw warnings in HUD
HUD doesn't start when fpdb is started from the Windows "Start Menu"
Exiting HUD on Windows doesn't properly clean up stat windows
finish bringing back tourney
ebuild: USE gtk, set permissions in it, copy docs to correct place, use games eclass or whatever to get games group notice, git-ebuild, get it into sunrise
make hud display W$SD etc as fraction.
@ -49,6 +64,7 @@ split hud data generation into separate for loops and make it more efficient
fix bug that sawFlop/Turn/River/CBChance/etc gets miscalculated if someone is allin - might as well add all-in recognition for this
make 3 default HUD configs (easy (4-5 fields), advanced (10ish fields), crazy (20 or so))
make it work with postgres
gentoo ebuild: USE postgresql
expand instructions for profile file
maybe remove siteId from gametypes
?change most cache fields to bigint to allow extremely big databases in excess of 2 or 4 million hands per stake and position?

72
pyfpdb/Configuration.py Normal file → Executable file
View File

@ -23,6 +23,7 @@ Handles HUD configuration files.
########################################################################
# Standard Library modules
import shutil
import xml.dom.minidom
from xml.dom.minidom import Node
@ -38,7 +39,7 @@ class Layout:
for i in range(1, len(self.location)):
temp = temp + "(%d,%d)" % self.location[i]
return temp
return temp + "\n"
class Site:
def __init__(self, node):
@ -81,7 +82,7 @@ class Stat:
pass
def __str__(self):
temp = " stat_name = %s, row = %d, col = %d, tip = %s, click = %s\n" % (self.stat_name, self.row, self.col, self.tip, self.click)
temp = " stat_name = %s, row = %d, col = %d, tip = %s, click = %s, popup = %s\n" % (self.stat_name, self.row, self.col, self.tip, self.click, self.popup)
return temp
class Game:
@ -99,6 +100,7 @@ class Game:
stat.col = int( stat_node.getAttribute("col") )
stat.tip = stat_node.getAttribute("tip")
stat.click = stat_node.getAttribute("click")
stat.popup = stat_node.getAttribute("popup")
self.stats[stat.stat_name] = stat
@ -150,15 +152,31 @@ class Mucked:
temp = temp + ' ' + key + " = " + value + "\n"
return temp
class Popup:
def __init__(self, node):
self.name = node.getAttribute("pu_name")
self.pu_stats = []
for stat_node in node.getElementsByTagName('pu_stat'):
self.pu_stats.append(stat_node.getAttribute("pu_stat_name"))
def __str__(self):
temp = "Popup = " + self.name + "\n"
for stat in self.pu_stats:
temp = temp + " " + stat
return temp + "\n"
class Config:
def __init__(self, file = 'HUD_config.xml'):
doc = xml.dom.minidom.parse(file)
self.doc = doc
self.file = file
self.supported_sites = {}
self.supported_games = {}
self.supported_databases = {}
self.mucked_windows = {}
self.popup_windows = {}
# s_sites = doc.getElementsByTagName("supported_sites")
for site_node in doc.getElementsByTagName("site"):
@ -180,6 +198,47 @@ class Config:
mw = Mucked(node = mw_node)
self.mucked_windows[mw.name] = mw
s_dbs = doc.getElementsByTagName("popup_windows")
for pu_node in doc.getElementsByTagName("pu"):
pu = Popup(node = pu_node)
self.popup_windows[pu.name] = pu
def get_site_node(self, site):
for site_node in self.doc.getElementsByTagName("site"):
if site_node.getAttribute("site_name") == site:
return site_node
def get_layout_node(self, site_node, layout):
for layout_node in site_node.getElementsByTagName("layout"):
if int( layout_node.getAttribute("max") ) == int( layout ):
return layout_node
def get_location_node(self, layout_node, seat):
for location_node in layout_node.getElementsByTagName("location"):
if int( location_node.getAttribute("seat") ) == int( seat ):
return location_node
def save(self, file = None):
if not file == None:
f = open(file, 'w')
self.doc.writexml(f)
f.close()
else:
shutil.move(self.file, self.file+".backup")
f = open(self.file, 'w')
self.doc.writexml(f)
f.close
def edit_layout(self, site_name, max, width = None, height = None,
fav_seat = None, locations = None):
site_node = self.get_site_node(site_name)
layout_node = self.get_layout_node(site_node, max)
for i in range(1, max + 1):
location_node = self.get_location_node(layout_node, i)
location_node.setAttribute("x", str( locations[i-1][0] ))
location_node.setAttribute("y", str( locations[i-1][1] ))
self.supported_sites[site_name].layout[max].location[i] = ( locations[i-1][0], locations[i-1][1] )
if __name__== "__main__":
c = Config()
@ -208,3 +267,12 @@ if __name__== "__main__":
print c.mucked_windows[w]
print "----------- END MUCKED WINDOW FORMATS -----------"
print "\n----------- POPUP WINDOW FORMATS -----------"
for w in c.popup_windows.keys():
print c.popup_windows[w]
print "----------- END MUCKED WINDOW FORMATS -----------"
c.edit_layout("PokerStars", 6, locations=( (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6) ))
c.save(file="testout.xml")

View File

@ -24,6 +24,7 @@ Create and manage the database objects.
# postmaster -D /var/lib/pgsql/data
# Standard Library modules
import sys
# pyGTK modules
@ -32,7 +33,7 @@ import Configuration
import SQL
# pgdb database module for posgres via DB-API
#import pgdb
import psycopg2
# pgdb uses pyformat. is that fixed or an option?
# mysql bindings
@ -41,7 +42,7 @@ import MySQLdb
class Database:
def __init__(self, c, db_name, game):
if c.supported_databases[db_name].db_server == 'postgresql':
self.connection = pgdb.connect(dsn = c.supported_databases[db_name].db_ip,
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)
@ -59,12 +60,12 @@ class Database:
self.type = c.supported_databases[db_name].db_type
self.sql = SQL.Sql(game = game, type = self.type)
def close(self):
self.connection.close
def close_connection(self):
self.connection.close()
def get_table_name(self, hand_id):
c = self.connection.cursor()
c.execute(self.sql.query['get_table_name'], (hand_id))
c.execute(self.sql.query['get_table_name'], (hand_id, ))
row = c.fetchone()
return row
@ -90,7 +91,21 @@ class Database:
c.execute(self.sql.query['get_hand_info'], new_hand_id)
return c.fetchall()
# def get_cards(self, hand):
# this version is for the PTrackSv2 db
# c = self.connection.cursor()
# c.execute(self.sql.query['get_cards'], hand)
# colnames = [desc[0] for desc in c.description]
# cards = {}
# for row in c.fetchall():
# s_dict = {}
# for name, val in zip(colnames, row):
# s_dict[name] = val
# cards[s_dict['seat_number']] = s_dict
# return (cards)
def get_cards(self, hand):
# this version is for the fpdb db
c = self.connection.cursor()
c.execute(self.sql.query['get_cards'], hand)
colnames = [desc[0] for desc in c.description]
@ -101,12 +116,14 @@ class Database:
s_dict[name] = val
cards[s_dict['seat_number']] = s_dict
return (cards)
def get_stats_from_hand(self, hand, hero):
def get_stats_from_hand(self, hand, player_id = False):
c = self.connection.cursor()
if not player_id: player_id = "%"
# get the players in the hand and their seats
c.execute(self.sql.query['get_players_from_hand'], (hand))
# c.execute(self.sql.query['get_players_from_hand'], (hand, player_id))
c.execute(self.sql.query['get_players_from_hand'], (hand, ))
names = {}
seats = {}
for row in c.fetchall():
@ -114,6 +131,7 @@ class Database:
seats[row[0]] = row[1]
# now get the stats
# c.execute(self.sql.query['get_stats_from_hand'], (hand, hand, player_id))
c.execute(self.sql.query['get_stats_from_hand'], (hand, hand))
colnames = [desc[0] for desc in c.description]
stat_dict = {}
@ -137,7 +155,8 @@ class Database:
if __name__=="__main__":
c = Configuration.Config()
db_connection = Database(c, 'fpdb', 'holdem') # mysql fpdb holdem
# db_connection = Database(c, 'fpdb', 'holdem') # mysql fpdb holdem
db_connection = Database(c, 'fpdb-p', 'test') # mysql fpdb holdem
# db_connection = Database(c, 'PTrackSv2', 'razz') # mysql razz
# db_connection = Database(c, 'ptracks', 'razz') # postgres
print "database connection object = ", db_connection.connection
@ -149,7 +168,16 @@ if __name__=="__main__":
hero = db_connection.get_player_id(c, 'PokerStars', 'nutOmatic')
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]
db_connection.close
db_connection.close_connection
print "press enter to continue"
sys.stdin.readline()

View File

@ -33,12 +33,12 @@ class GuiAutoImport (threading.Thread):
current_path=self.pathTBuffer.get_text(self.pathTBuffer.get_start_iter(), self.pathTBuffer.get_end_iter())
dia_chooser = gtk.FileChooserDialog(title="Please choose the path that you want to auto import",
action=gtk.FILE_CHOOSER_ACTION_OPEN,
action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
#dia_chooser.set_current_folder(pathname)
dia_chooser.set_filename(current_path)
#dia_chooser.set_select_multiple(select_multiple) #not in tv, but want this in bulk import
response = dia_chooser.run()
if response == gtk.RESPONSE_OK:
#print dia_chooser.get_filename(), 'selected'
@ -50,8 +50,18 @@ class GuiAutoImport (threading.Thread):
def do_import(self):
"""Callback for timer to do an import iteration."""
fpdb_import.import_file_dict(self, self.settings)
return(1)
for file in os.listdir(self.path):
if os.path.isdir(file):
print "AutoImport is not recursive - please select the final directory in which the history files are"
else:
self.inputFile = os.path.join(self.path, file)
stat_info = os.stat(self.inputFile)
if not self.import_files.has_key(self.inputFile) or stat_info.st_mtime > self.import_files[self.inputFile]:
fpdb_import.import_file_dict(self, self.settings, callHud = True)
self.import_files[self.inputFile] = stat_info.st_mtime
print "GuiAutoImport.import_dir done"
return True
def startClicked(self, widget, data):
"""runs when user clicks start on auto import tab"""
@ -59,24 +69,48 @@ class GuiAutoImport (threading.Thread):
# Check to see if we have an open file handle to the HUD and open one if we do not.
# bufsize = 1 means unbuffered
# We need to close this file handle sometime.
# TODO: Allow for importing from multiple dirs - REB 29AUG2008
# As presently written this function does nothing if there is already a pipe open.
# That is not correct. It should open another dir for importing while piping the
# results to the same pipe. This means that self.path should be a a list of dirs
# to watch.
try: #uhhh, I don't this this is the best way to check for the existence of an attr
getattr(self, "pipe_to_hud")
except AttributeError:
cwd = os.getcwd()
command = os.path.join(cwd, 'HUD_main.py')
self.pipe_to_hud = subprocess.Popen(command, bufsize = 1, stdin = subprocess.PIPE)
self.path=self.pathTBuffer.get_text(self.pathTBuffer.get_start_iter(), self.pathTBuffer.get_end_iter())
for file in os.listdir(self.path):
if os.path.isdir(file):
print "AutoImport is not recursive - please select the final directory in which the history files are"
if os.name == 'nt':
command = "python HUD_main.py" + " %s" % (self.database)
bs = 0 # windows is not happy with line buffing here
self.pipe_to_hud = subprocess.Popen(command, bufsize = bs, stdin = subprocess.PIPE,
universal_newlines=True)
else:
self.inputFile=self.path+os.sep+file
self.do_import()
print "GuiAutoImport.import_dir done"
cwd = os.getcwd()
command = os.path.join(cwd, 'HUD_main.py')
bs = 1
self.pipe_to_hud = subprocess.Popen((command, self.database), bufsize = bs, stdin = subprocess.PIPE,
universal_newlines=True)
# self.pipe_to_hud = subprocess.Popen((command, self.database), bufsize = bs, stdin = subprocess.PIPE,
# universal_newlines=True)
# command = command + " %s" % (self.database)
# print "command = ", command
# self.pipe_to_hud = os.popen(command, 'w')
self.path=self.pathTBuffer.get_text(self.pathTBuffer.get_start_iter(), self.pathTBuffer.get_end_iter())
# Iniitally populate the self.import_files dict, which keeps mtimes for the files watched
self.import_files = {}
for file in os.listdir(self.path):
if os.path.isdir(file):
pass # skip subdirs for now
else:
inputFile = os.path.join(self.path, file)
stat_info = os.stat(inputFile)
self.import_files[inputFile] = stat_info.st_mtime
self.do_import()
interval=int(self.intervalTBuffer.get_text(self.intervalTBuffer.get_start_iter(), self.intervalTBuffer.get_end_iter()))
gobject.timeout_add(interval*1000, self.do_import)
interval=int(self.intervalTBuffer.get_text(self.intervalTBuffer.get_start_iter(), self.intervalTBuffer.get_end_iter()))
gobject.timeout_add(interval*1000, self.do_import)
#end def GuiAutoImport.browseClicked
def get_vbox(self):
@ -140,3 +174,22 @@ class GuiAutoImport (threading.Thread):
self.mainVBox.add(self.startButton)
self.startButton.show()
#end of GuiAutoImport.__init__
if __name__== "__main__":
def destroy(*args): # call back for terminating the main eventloop
gtk.main_quit()
settings = {}
settings['db-host'] = "192.168.1.100"
settings['db-user'] = "mythtv"
settings['db-password'] = "mythtv"
settings['db-databaseName'] = "fpdb"
settings['hud-defaultInterval'] = 10
settings['hud-defaultPath'] = 'C:/Program Files/PokerStars/HandHistory/nutOmatic'
settings['imp-callFpdbHud'] = True
i = GuiAutoImport(settings)
main_window = gtk.Window()
main_window.connect("destroy", destroy)
main_window.add(i.mainVBox)
main_window.show()
gtk.main()

View File

@ -26,15 +26,17 @@ Main for FreePokerTools HUD.
# to do adjust for preferred seat
# to do allow window resizing
# to do hud to echo, but ignore non numbers
# to do kill a hud
# to do no hud window for hero
# to do single click to display detailed stats
# to do things to add to config.xml
# to do font and size
# to do bg and fg color
# to do opacity
# Standard Library modules
import sys
import os
import thread
import Queue
# pyGTK modules
import pygtk
@ -49,19 +51,23 @@ import Hud
# global dict for keeping the huds
hud_dict = {}
db_connection = 0;
config = 0;
def destroy(*args): # call back for terminating the main eventloop
gtk.main_quit()
def process_new_hand(source, condition):
def process_new_hand(new_hand_id, db_name):
# there is a new hand_id to be processed
# read the hand_id from stdin and strip whitespace
new_hand_id = sys.stdin.readline()
new_hand_id = new_hand_id.rstrip()
db_connection = Database.Database(config, 'fpdb', 'holdem')
global hud_dict
for h in hud_dict.keys():
if hud_dict[h].deleted:
del(hud_dict[h])
db_connection = Database.Database(config, db_name, 'temp')
(table_name, max, poker_game) = db_connection.get_table_name(new_hand_id)
# if a hud for this table exists, just update it
if hud_dict.has_key(table_name):
@ -71,29 +77,61 @@ def process_new_hand(source, condition):
table_windows = Tables.discover(config)
for t in table_windows.keys():
if table_windows[t].name == table_name:
hud_dict[table_name] = Hud.Hud(table_windows[t], max, poker_game, config, db_connection)
hud_dict[table_name] = Hud.Hud(table_windows[t], max, poker_game, config, db_name)
hud_dict[table_name].create(new_hand_id, config)
hud_dict[table_name].update(new_hand_id, db_connection, config)
break
# print "table name \"%s\" not identified, no hud created" % (table_name)
return(1)
db_connection.close_connection()
return(1)
def check_stdin(db_name):
try:
hand_no = dataQueue.get(block=False)
process_new_hand(hand_no, db_name)
except:
pass
return True
def read_stdin(source, condition, db_name):
new_hand_id = sys.stdin.readline()
process_new_hand(new_hand_id, db_name)
return True
def producer(): # This is the thread function
while True:
hand_no = sys.stdin.readline() # reads stdin
dataQueue.put(hand_no) # and puts result on the queue
if __name__== "__main__":
if not os.name == 'posix':
print "This version of the HUD only works with Linux or compatible.\nHUD exiting."
print "HUD_main starting"
try:
db_name = sys.argv[1]
except:
db_name = 'fpdb-p'
print "Using db name = ", db_name
config = Configuration.Config()
# db_connection = Database.Database(config, 'fpdb', 'holdem')
if os.name == 'posix':
s_id = gobject.io_add_watch(sys.stdin, gobject.IO_IN, read_stdin, db_name)
elif os.name == 'nt':
dataQueue = Queue.Queue() # shared global. infinite size
gobject.threads_init() # this is required
thread.start_new_thread(producer, ()) # starts the thread
gobject.timeout_add(1000, check_stdin, db_name)
else:
print "Sorry your operating system is not supported."
sys.exit()
main_window = gtk.Window()
main_window.connect("destroy", destroy)
label = gtk.Label('Fake main window, blah blah, blah\nblah, blah')
label = gtk.Label('Closing this window will exit from the HUD.')
main_window.add(label)
main_window.set_title("HUD Main Window")
main_window.show_all()
config = Configuration.Config()
db_connection = Database.Database(config, 'fpdb', 'holdem')
s_id = gobject.io_add_watch(sys.stdin, gobject.IO_IN, process_new_hand)
gtk.main()

301
pyfpdb/Hud.py Normal file → Executable file
View File

@ -22,6 +22,7 @@ Create and manage the hud overlays.
########################################################################
# Standard Library modules
import os
# pyGTK modules
import pygtk
@ -29,26 +30,85 @@ import gtk
import pango
import gobject
# win32 modules -- only imported on windows systems
if os.name == 'nt':
import win32gui
import win32con
# FreePokerTools modules
import Tables # needed for testing only
import Configuration
import Stats
import Mucked
import Database
import HUD_main
class Hud:
def __init__(self, table, max, poker_game, config, db_connection):
def __init__(self, table, max, poker_game, config, db_name):
self.table = table
self.config = config
self.poker_game = poker_game
self.max = max
self.db_connection = db_connection
self.db_name = db_name
self.deleted = False
self.stat_windows = {}
self.popup_windows = {}
self.font = pango.FontDescription("Sans 8")
# Set up a main window for this this instance of the HUD
self.main_window = gtk.Window()
# self.window.set_decorated(0)
self.main_window.set_gravity(gtk.gdk.GRAVITY_STATIC)
self.main_window.set_keep_above(1)
self.main_window.set_title(table.name)
self.main_window.connect("destroy", self.kill_hud)
self.ebox = gtk.EventBox()
self.label = gtk.Label("Close this window to\nkill the HUD for\n %s" % (table.name))
self.main_window.add(self.ebox)
self.ebox.add(self.label)
self.main_window.move(self.table.x, self.table.y)
# A popup window for the main window
self.menu = gtk.Menu()
self.item1 = gtk.MenuItem('Kill this HUD')
self.menu.append(self.item1)
self.item1.connect("activate", self.kill_hud)
self.item1.show()
self.item2 = gtk.MenuItem('Save Layout')
self.menu.append(self.item2)
self.item2.connect("activate", self.save_layout)
self.item2.show()
self.ebox.connect_object("button-press-event", self.on_button_press, self.menu)
self.main_window.show_all()
# set_keep_above(1) for windows
if os.name == 'nt': self.topify_window(self.main_window)
def on_button_press(self, widget, event):
if event.button == 3:
widget.popup(None, None, None, event.button, event.time)
return True
return False
def kill_hud(self, args):
for k in self.stat_windows.keys():
self.stat_windows[k].window.destroy()
self.main_window.destroy()
self.deleted = True
def save_layout(self, *args):
new_layout = []
for sw in self.stat_windows:
loc = self.stat_windows[sw].window.get_position()
new_loc = (loc[0] - self.table.x, loc[1] - self.table.y)
new_layout.append(new_loc)
print new_layout
self.config.edit_layout(self.table.site, self.table.max, locations = new_layout)
self.config.save()
def create(self, hand, config):
# update this hud, to the stats and players as of "hand"
# hand is the hand id of the most recent hand played at this table
@ -58,6 +118,7 @@ class Hud:
for i in range(1, self.max + 1):
(x, y) = config.supported_sites[self.table.site].layout[self.max].location[i]
self.stat_windows[i] = Stat_Window(game = config.supported_games[self.poker_game],
parent = self,
table = self.table,
x = x,
y = y,
@ -79,10 +140,10 @@ class Hud:
# self.mucked_window.show_all()
def update(self, hand, db, config):
stat_dict = db.get_stats_from_hand(hand, 3)
self.hand = hand # this is the last hand, so it is available later
stat_dict = db.get_stats_from_hand(hand)
for s in stat_dict.keys():
# for r in range(0, 2):
# for c in range(0, 3):
self.stat_windows[stat_dict[s]['seat']].player_id = stat_dict[s]['player_id']
for r in range(0, config.supported_games[self.poker_game].rows):
for c in range(0, config.supported_games[self.poker_game].cols):
number = Stats.do_stat(stat_dict, player = stat_dict[s]['player_id'], stat = self.stats[r][c])
@ -91,7 +152,33 @@ class Hud:
number[3] + ", " + number[4]
Stats.do_tip(self.stat_windows[stat_dict[s]['seat']].e_box[r][c], tip)
# self.m.update(hand)
def topify_window(self, 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:
win32gui.SetWindowPos(w[0], win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE|win32con.SWP_NOSIZE)
# notify_id = (w[0],
# 0,
# win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP,
# win32con.WM_USER+20,
# 0,
# '')
# win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, notify_id)
#
window.set_title(real_name)
class Stat_Window:
def button_press_cb(self, widget, event, *args):
@ -101,7 +188,7 @@ class Stat_Window:
if event.button == 1: # left button event
if event.type == gtk.gdk.BUTTON_PRESS: # left button single click
if self.sb_click > 0: return
self.sb_click = gobject.timeout_add(250, self.single_click)
self.sb_click = gobject.timeout_add(250, self.single_click, widget)
elif event.type == gtk.gdk._2BUTTON_PRESS: # left button double click
if self.sb_click > 0:
gobject.source_remove(self.sb_click)
@ -109,17 +196,20 @@ class Stat_Window:
self.double_click(widget, event, *args)
if event.button == 2: # middle button event
print "middle button clicked"
pass
# print "middle button clicked"
if event.button == 3: # right button event
print "right button clicked"
pass
# print "right button clicked"
def single_click(self):
def single_click(self, widget):
# Callback from the timeout in the single-click finding part of the
# button press call back. This needs to be modified to get all the
# arguments from the call.
print "left button clicked"
# print "left button clicked"
self.sb_click = 0
Popup_window(widget, self)
return False
def double_click(self, widget, event, *args):
@ -136,13 +226,14 @@ class Stat_Window:
top.set_decorated(1)
top.move(x, y)
def __init__(self, game, table, seat, x, y, player_id, font):
self.game = game
self.table = table
self.x = x + table.x
self.y = y + table.y
self.player_id = player_id
self.sb_click = 0
def __init__(self, parent, game, table, seat, x, y, player_id, font):
self.parent = parent # Hud object that this stat window belongs to
self.game = game # Configuration object for the curren
self.table = table # Table object where this is going
self.x = x + table.x # table.x and y are the location of the table
self.y = y + table.y # x and y are the location relative to table.x & y
self.player_id = player_id # looks like this isn't used ;)
self.sb_click = 0 # used to figure out button clicks
self.window = gtk.Window()
self.window.set_decorated(0)
@ -163,7 +254,7 @@ class Stat_Window:
for c in range(self.game.cols):
self.e_box[r].append( gtk.EventBox() )
Stats.do_tip(self.e_box[r][c], 'farts')
self.grid.attach(self.e_box[r][c], c, c+1, r, r+1, xpadding = 1, ypadding = 1)
self.grid.attach(self.e_box[r][c], c, c+1, r, r+1, xpadding = 0, ypadding = 0)
self.label[r].append( gtk.Label('xxx') )
self.e_box[r][c].add(self.label[r][c])
self.e_box[r][c].connect("button_press_event", self.button_press_cb)
@ -172,10 +263,173 @@ class Stat_Window:
self.window.realize
self.window.move(self.x, self.y)
self.window.show_all()
# set_keep_above(1) for windows
if os.name == 'nt': self.topify_window(self.window)
def topify_window(self, 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:
win32gui.SetWindowPos(w[0], win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE|win32con.SWP_NOSIZE)
# notify_id = (w[0],
# 0,
# win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP,
# win32con.WM_USER+20,
# 0,
# '')
# win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, notify_id)
#
window.set_title(real_name)
def destroy(*args): # call back for terminating the main eventloop
gtk.main_quit()
class Popup_window:
def __init__(self, parent, stat_window):
self.sb_click = 0
# create the popup window
self.window = gtk.Window()
self.window.set_decorated(0)
self.window.set_gravity(gtk.gdk.GRAVITY_STATIC)
self.window.set_keep_above(1)
self.window.set_title("popup")
self.window.set_property("skip-taskbar-hint", True)
self.window.set_transient_for(parent.get_toplevel())
self.window.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
self.ebox = gtk.EventBox()
self.ebox.connect("button_press_event", self.button_press_cb)
self.lab = gtk.Label("stuff\nstuff\nstuff")
# need an event box so we can respond to clicks
self.window.add(self.ebox)
self.ebox.add(self.lab)
self.window.realize
# figure out the row, col address of the click that activated the popup
row = 0
col = 0
for r in range(0, stat_window.game.rows):
for c in range(0, stat_window.game.cols):
if stat_window.e_box[r][c] == parent:
row = r
col = c
break
# figure out what popup format we're using
popup_format = "default"
for stat in stat_window.game.stats.keys():
if stat_window.game.stats[stat].row == row and stat_window.game.stats[stat].col == col:
popup_format = stat_window.game.stats[stat].popup
break
# get the list of stats to be presented from the config
stat_list = []
for w in stat_window.parent.config.popup_windows.keys():
if w == popup_format:
stat_list = stat_window.parent.config.popup_windows[w].pu_stats
break
# get a database connection
db_connection = Database.Database(stat_window.parent.config, stat_window.parent.db_name, 'temp')
# calculate the stat_dict and then create the text for the pu
# stat_dict = db_connection.get_stats_from_hand(stat_window.parent.hand, stat_window.player_id)
stat_dict = db_connection.get_stats_from_hand(stat_window.parent.hand)
db_connection.close_connection()
pu_text = ""
for s in stat_list:
number = Stats.do_stat(stat_dict, player = int(stat_window.player_id), stat = s)
pu_text += number[3] + "\n"
self.lab.set_text(pu_text)
self.window.show_all()
# set_keep_above(1) for windows
if os.name == 'nt': self.topify_window(self.window)
def button_press_cb(self, widget, event, *args):
# This handles all callbacks from button presses on the event boxes in
# the popup windows. There is a bit of an ugly kludge to separate single-
# and double-clicks. This is the same code as in the Stat_window class
if event.button == 1: # left button event
if event.type == gtk.gdk.BUTTON_PRESS: # left button single click
if self.sb_click > 0: return
self.sb_click = gobject.timeout_add(250, self.single_click, widget)
elif event.type == gtk.gdk._2BUTTON_PRESS: # left button double click
if self.sb_click > 0:
gobject.source_remove(self.sb_click)
self.sb_click = 0
self.double_click(widget, event, *args)
if event.button == 2: # middle button event
pass
# print "middle button clicked"
if event.button == 3: # right button event
pass
# print "right button clicked"
def single_click(self, widget):
# Callback from the timeout in the single-click finding part of the
# button press call back. This needs to be modified to get all the
# arguments from the call.
self.sb_click = 0
self.window.destroy()
return False
def double_click(self, widget, event, *args):
self.toggle_decorated(widget)
def toggle_decorated(self, widget):
top = widget.get_toplevel()
(x, y) = top.get_position()
if top.get_decorated():
top.set_decorated(0)
top.move(x, y)
else:
top.set_decorated(1)
top.move(x, y)
def topify_window(self, 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:
win32gui.SetWindowPos(w[0], win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE|win32con.SWP_NOSIZE)
# notify_id = (w[0],
# 0,
# win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP,
# win32con.WM_USER+20,
# 0,
# '')
# win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, notify_id)
#
window.set_title(real_name)
if __name__== "__main__":
main_window = gtk.Window()
main_window.connect("destroy", destroy)
@ -187,15 +441,6 @@ if __name__== "__main__":
tables = Tables.discover(c)
db = Database.Database(c, 'fpdb', 'holdem')
for attr in dir(Stats):
if attr.startswith('__'): continue
if attr == 'Configuration' or attr == 'Database': continue
if attr == 'GInitiallyUnowned': continue
# print Stats.attr.__doc__
print Stats.vpip.__doc__
for t in tables:
win = Hud(t, 8, c, db)
# t.get_details()

View File

@ -105,7 +105,8 @@ class MuckedList:
self.mucked_cards.update(model.get_value(iter, 0))
def update(self, new_hand_id):
info_row = self.db_connection.get_hand_info(new_hand_id)
# info_row = self.db_connection.get_hand_info(new_hand_id)
info_row = ((new_hand_id, "xxxx", 0), )
iter = self.liststore.append(info_row[0])
sel = self.treeview.get_selection()
sel.select_iter(iter)
@ -156,11 +157,15 @@ class MuckedCards:
self.grid.attach(self.grid_contents[(c, r)], c, c+1, r, r+1, xpadding = 1, ypadding = 1)
self.parent.add(self.grid)
def translate_cards(self, old_cards):
pass
def update(self, new_hand_id):
cards = self.db_connection.get_cards(new_hand_id)
self.clear()
cards = self.translate_cards(cards)
for c in cards.keys():
self.grid_contents[(1, cards[c]['seat_number'] - 1)].set_text(cards[c]['screen_name'])
@ -224,7 +229,7 @@ if __name__== "__main__":
return(True)
config = Configuration.Config()
db_connection = Database.Database(config, 'PTrackSv2', 'razz')
db_connection = Database.Database(config, 'fpdb', '')
main_window = gtk.Window()
main_window.set_keep_above(True)
@ -236,4 +241,3 @@ if __name__== "__main__":
s_id = gobject.io_add_watch(sys.stdin, gobject.IO_IN, process_new_hand)
gtk.main()

View File

@ -60,7 +60,7 @@ class Sql:
FROM game_players
WHERE game_id = %s AND player_id = 3
"""
self.query['get_cards'] = """
select
seat_number,
@ -173,10 +173,6 @@ class Sql:
self.query['get_stats_from_hand'] = """
SELECT HudCache.playerId AS player_id,
HudCache.gametypeId AS gametypeId,
activeSeats AS n_active,
position AS position,
HudCache.tourneyTypeId AS tourneyTypeId,
sum(HDs) AS n,
sum(street0VPI) AS vpip,
sum(street0Aggr) AS pfr,
@ -241,12 +237,18 @@ class Sql:
AND Hands.gametypeId = HudCache.gametypeId
GROUP BY HudCache.PlayerId
"""
# AND PlayerId LIKE %s
# HudCache.gametypeId AS gametypeId,
# activeSeats AS n_active,
# position AS position,
# HudCache.tourneyTypeId AS tourneyTypeId,
self.query['get_players_from_hand'] = """
SELECT HandsPlayers.playerId, seatNo, name
FROM HandsPlayers INNER JOIN Players ON (HandsPlayers.playerId = Players.id)
WHERE handId = %s
"""
# WHERE handId = %s AND Players.id LIKE %s
self.query['get_table_name'] = """
select tableName, maxSeats, category
@ -255,6 +257,31 @@ class Sql:
and Gametypes.id = Hands.gametypeId
"""
self.query['get_cards'] = """
select
seatNo AS seat_number,
name AS screen_name,
card1Value, card1Suit,
card2Value, card2Suit,
card3Value, card3Suit,
card4Value, card4Suit,
card5Value, card5Suit,
card6Value, card6Suit,
card7Value, card7Suit
from HandsPlayers, Players
where handID = %s and HandsPlayers.playerId = Players.id
order by seatNo
"""
# self.query['get_hand_info'] = """
# SELECT
# game_id,
# CONCAT(hole_card_1, hole_card_2, hole_card_3, hole_card_4, hole_card_5, hole_card_6, hole_card_7) AS hand,
# total_won-total_bet AS net
# FROM game_players
# WHERE game_id = %s AND player_id = 3
# """
if __name__== "__main__":
# just print the default queries and exit
s = Sql(game = 'razz', type = 'ptracks')

View File

@ -43,6 +43,7 @@
# 6 For each stat you make add a line to the __main__ function to test it.
# Standard Library modules
#import sys
# pyGTK modules
import pygtk
@ -55,10 +56,6 @@ import Database
def do_tip(widget, tip):
widget.set_tooltip_text(tip)
def list_stats():
for key in dir():
print key
def do_stat(stat_dict, player = 24, stat = 'vpip'):
return eval("%(stat)s(stat_dict, %(player)d)" % {'stat': stat, 'player': player})
# OK, for reference the tuple returned by the stat is:
@ -72,9 +69,7 @@ def do_stat(stat_dict, player = 24, stat = 'vpip'):
###########################################
# functions that return individual stats
def vpip(stat_dict, player):
"""
Voluntarily put $ in the pot
"""
""" Voluntarily put $ in the pot."""
stat = 0.0
try:
stat = float(stat_dict[player]['vpip'])/float(stat_dict[player]['n'])
@ -94,6 +89,7 @@ def vpip(stat_dict, player):
)
def pfr(stat_dict, player):
""" Preflop (3rd street) raise."""
stat = 0.0
try:
stat = float(stat_dict[player]['pfr'])/float(stat_dict[player]['n'])
@ -114,6 +110,7 @@ def pfr(stat_dict, player):
)
def wtsd(stat_dict, player):
""" Went to SD when saw flop/4th."""
stat = 0.0
try:
stat = float(stat_dict[player]['sd'])/float(stat_dict[player]['saw_f'])
@ -134,6 +131,7 @@ def wtsd(stat_dict, player):
)
def wmsd(stat_dict, player):
""" Won $ at showdown."""
stat = 0.0
try:
stat = float(stat_dict[player]['wmsd'])/float(stat_dict[player]['sd'])
@ -141,7 +139,7 @@ def wmsd(stat_dict, player):
'%3.1f' % (100*stat) + '%',
'w=%3.1f' % (100*stat) + '%',
'wmsd=%3.1f' % (100*stat) + '%',
'(%d/%d)' % (stat_dict[player]['wmsd'], stat_dict[player]['sd']),
'(%f5.0/%d)' % (stat_dict[player]['wmsd'], stat_dict[player]['sd']),
'% won money at showdown'
)
except:
@ -154,6 +152,7 @@ def wmsd(stat_dict, player):
)
def saw_f(stat_dict, player):
""" Saw flop/4th."""
try:
num = float(stat_dict[player]['saw_f'])
den = float(stat_dict[player]['n'])
@ -178,6 +177,7 @@ def saw_f(stat_dict, player):
)
def n(stat_dict, player):
""" Number of hands played."""
try:
return (stat_dict[player]['n'],
'%d' % (stat_dict[player]['n']),
@ -187,15 +187,16 @@ def n(stat_dict, player):
'number hands seen'
)
except:
return (stat_dict[player][0],
'%d' % (stat_dict[player][0]),
'n=%d' % (stat_dict[player][0]),
'n=%d' % (stat_dict[player][0]),
'(%d)' % (stat_dict[player][0]),
return (0,
'%d' % (0),
'n=%d' % (0),
'n=%d' % (0),
'(%d)' % (0),
'number hands seen'
)
def fold_f(stat_dict, player):
""" Folded flop/4th."""
stat = 0.0
try:
stat = float(stat_dict[player]['fold_2'])/fold(stat_dict[player]['saw_f'])
@ -216,6 +217,7 @@ def fold_f(stat_dict, player):
)
def steal(stat_dict, player):
""" Steal %."""
stat = 0.0
try:
stat = float(stat_dict[player]['steal'])/float(stat_dict[player]['steal_opp'])
@ -236,6 +238,7 @@ def steal(stat_dict, player):
)
def f_SB_steal(stat_dict, player):
""" Folded SB to steal."""
stat = 0.0
try:
stat = float(stat_dict[player]['SBnotDef'])/float(stat_dict[player]['SBstolen'])
@ -256,6 +259,7 @@ def f_SB_steal(stat_dict, player):
)
def f_BB_steal(stat_dict, player):
""" Folded BB to steal."""
stat = 0.0
try:
stat = float(stat_dict[player]['BBnotDef'])/float(stat_dict[player]['BBstolen'])
@ -276,6 +280,7 @@ def f_BB_steal(stat_dict, player):
)
def three_B_0(stat_dict, player):
""" Three bet preflop/3rd."""
stat = 0.0
try:
stat = float(stat_dict[player]['TB_0'])/float(stat_dict[player]['TB_opp_0'])
@ -296,6 +301,7 @@ def three_B_0(stat_dict, player):
)
def WMsF(stat_dict, player):
""" Won $ when saw flop/4th."""
stat = 0.0
try:
stat = float(stat_dict[player]['w_w_s_1'])/float(stat_dict[player]['saw_1'])
@ -316,6 +322,7 @@ def WMsF(stat_dict, player):
)
def a_freq_1(stat_dict, player):
""" Flop/4th aggression frequency."""
stat = 0.0
try:
stat = float(stat_dict[player]['aggr_1'])/float(stat_dict[player]['saw_f'])
@ -336,6 +343,7 @@ def a_freq_1(stat_dict, player):
)
def a_freq_2(stat_dict, player):
""" Turn/5th aggression frequency."""
stat = 0.0
try:
stat = float(stat_dict[player]['aggr_2'])/float(stat_dict[player]['saw_2'])
@ -356,6 +364,7 @@ def a_freq_2(stat_dict, player):
)
def a_freq_3(stat_dict, player):
""" River/6th aggression frequency."""
stat = 0.0
try:
stat = float(stat_dict[player]['aggr_3'])/float(stat_dict[player]['saw_3'])
@ -376,6 +385,7 @@ def a_freq_3(stat_dict, player):
)
def a_freq_4(stat_dict, player):
""" 7th street aggression frequency."""
stat = 0.0
try:
stat = float(stat_dict[player]['aggr_4'])/float(stat_dict[player]['saw_4'])
@ -389,13 +399,14 @@ def a_freq_4(stat_dict, player):
except:
return (stat,
'%3.1f' % (0) + '%',
'a1=%3.1f' % (0) + '%',
'a_fq_1=%3.1f' % (0) + '%',
'a4=%3.1f' % (0) + '%',
'a_fq_4=%3.1f' % (0) + '%',
'(%d/%d)' % (0, 0),
'Aggression Freq flop/4th'
)
def cb_1(stat_dict, player):
""" Flop continuation bet."""
stat = 0.0
try:
stat = float(stat_dict[player]['CB_1'])/float(stat_dict[player]['CB_opp_1'])
@ -416,6 +427,7 @@ def cb_1(stat_dict, player):
)
def cb_2(stat_dict, player):
""" Turn continuation bet."""
stat = 0.0
try:
stat = float(stat_dict[player]['CB_2'])/float(stat_dict[player]['CB_opp_2'])
@ -436,6 +448,7 @@ def cb_2(stat_dict, player):
)
def cb_3(stat_dict, player):
""" River continuation bet."""
stat = 0.0
try:
stat = float(stat_dict[player]['CB_3'])/float(stat_dict[player]['CB_opp_3'])
@ -456,6 +469,7 @@ def cb_3(stat_dict, player):
)
def cb_4(stat_dict, player):
""" 7th street continuation bet."""
stat = 0.0
try:
stat = float(stat_dict[player]['CB_4'])/float(stat_dict[player]['CB_opp_4'])
@ -476,6 +490,7 @@ def cb_4(stat_dict, player):
)
def ffreq_1(stat_dict, player):
""" Flop/4th fold frequency."""
stat = 0.0
try:
stat = float(stat_dict[player]['f_freq_1'])/float(stat_dict[player]['was_raised_1'])
@ -496,6 +511,7 @@ def ffreq_1(stat_dict, player):
)
def ffreq_2(stat_dict, player):
""" Turn/5th fold frequency."""
stat = 0.0
try:
stat = float(stat_dict[player]['f_freq_2'])/float(stat_dict[player]['was_raised_2'])
@ -516,6 +532,7 @@ def ffreq_2(stat_dict, player):
)
def ffreq_3(stat_dict, player):
""" River/6th fold frequency."""
stat = 0.0
try:
stat = float(stat_dict[player]['f_freq_3'])/float(stat_dict[player]['was_raised_3'])
@ -536,6 +553,7 @@ def ffreq_3(stat_dict, player):
)
def ffreq_4(stat_dict, player):
""" 7th fold frequency."""
stat = 0.0
try:
stat = float(stat_dict[player]['f_freq_4'])/float(stat_dict[player]['was_raised_4'])
@ -559,7 +577,7 @@ if __name__== "__main__":
c = Configuration.Config()
db_connection = Database.Database(c, 'fpdb', 'holdem')
h = db_connection.get_last_hand()
stat_dict = db_connection.get_stats_from_hand(h, 0)
stat_dict = db_connection.get_stats_from_hand(h)
for player in stat_dict.keys():
print "player = ", player, do_stat(stat_dict, player = player, stat = 'vpip')
@ -587,13 +605,14 @@ if __name__== "__main__":
print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq_3')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq_4')
# print "\n\nLegal stats:"
# for attr in dir():
# if attr.startswith('__'): continue
# if attr == 'Configuration' or attr == 'Database': continue
# if attr == 'GInitiallyUnowned': continue
# print attr.__doc__
#
# print vpip.__doc__
print "\n\nLegal stats:"
for attr in dir():
if attr.startswith('__'): continue
if attr in ("Configuration", "Database", "GInitiallyUnowned", "gtk", "pygtk",
"player", "c", "db_connection", "do_stat", "do_tip", "stat_dict",
"h"): continue
print attr, eval("%s.__doc__" % (attr))
# print " <pu_stat pu_stat_name = \"%s\"> </pu_stat>" % (attr)
db_connection.close

View File

@ -26,8 +26,14 @@ of Table_Window objects representing the windows found.
# Standard Library modules
import os
import sys
import re
# Win32 modules
if os.name == 'nt':
import win32gui
# FreePokerTools modules
import Configuration
@ -49,8 +55,24 @@ class Table_Window:
table.tournament = 0
def discover(c):
if os.name == 'posix':
tables = discover_posix(c)
return tables
elif os.name == 'nt':
tables = discover_nt(c)
return tables
elif ox.name == 'mac':
tables = discover_mac(c)
return tables
else: tables = {}
return(tables)
def discover_posix(c):
""" Poker client table window finder for posix/Linux = XWindows."""
tables = {}
for listing in os.popen('xwininfo -root -tree').readlines():
# xwininfo -root -tree -id 0xnnnnn gets the info on a single window
if re.search('Lobby', listing): continue
if re.search('Instant Hand History', listing): continue
if not re.search('Logged In as ', listing): continue
@ -75,6 +97,68 @@ def discover(c):
eval("%s(tw)" % c.supported_sites[s].decoder)
tables[tw.name] = tw
return tables
#
# The discover_xx functions query the system and report on the poker clients
# currently displayed on the screen. The discover_posix should give you
# some idea how to support other systems.
#
# discover_xx() returns a dict of TableWindow objects--one TableWindow
# object for each poker client table on the screen.
#
# Each TableWindow object must have the following attributes correctly populated:
# 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.
# 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.
def win_enum_handler(hwnd, titles):
titles[hwnd] = win32gui.GetWindowText(hwnd)
def child_enum_handler(hwnd, children):
print hwnd, win32.GetWindowRect(hwnd)
def discover_nt(c):
""" Poker client table window finder for Windows."""
#
# I cannot figure out how to get the inside dimensions of the poker table
# windows. So I just assume all borders are 3 thick and all title bars
# are 29 high. No doubt this will be off when used with certain themes.
#
b_width = 3
tb_height = 29
titles = {}
tables = {}
win32gui.EnumWindows(win_enum_handler, titles)
for hwnd in titles.keys():
if re.search('Logged In as', titles[hwnd]) and not re.search('Lobby', titles[hwnd]):
tw = Table_Window()
# tw.site = c.supported_sites[s].site_name
tw.number = hwnd
(x, y, width, height) = win32gui.GetWindowRect(hwnd)
tw.title = titles[hwnd]
tw.width = int( width ) - 2*b_width
tw.height = int( height ) - b_width - tb_height
tw.x = int( x ) + b_width
tw.y = int( y ) + tb_height
eval("%s(tw)" % "pokerstars_decode_table")
tw.site = "PokerStars"
tables[tw.name] = tw
return tables
def discover_mac(c):
""" Poker client table window finder for Macintosh."""
tables = {}
return tables
def pokerstars_decode_table(tw):
# extract the table name OR the tournament number and table name from the title
@ -95,18 +179,6 @@ def pokerstars_decode_table(tw):
mo = re.search('(Razz|Stud H/L|Stud|Omaha H/L|Omaha|Hold\'em|5-Card Draw|Triple Draw 2-7 Lowball)', tw.title)
#Traceback (most recent call last):
# File "/home/fatray/razz-poker-productio/HUD_main.py", line 41, in process_new_hand
# table_windows = Tables.discover(config)
# File "/home/fatray/razz-poker-productio/Tables.py", line 58, in discover
# eval("%s(tw)" % c.supported_sites[s].decoder)
# File "<string>", line 1, in <module>
# File "/home/fatray/razz-poker-productio/Tables.py", line 80, in pokerstars_decode_table
# tw.game = mo.group(1).lower()
#AttributeError: 'NoneType' object has no attribute 'group'
#
#This problem happens with observed windows!!
tw.game = mo.group(1).lower()
tw.game = re.sub('\'', '', tw.game)
tw.game = re.sub('h/l', 'hi/lo', tw.game)
@ -132,4 +204,8 @@ if __name__=="__main__":
tables = discover(c)
for t in tables.keys():
print tables[t]
print "t = ", t
print tables[t]
print "press enter to continue"
sys.stdin.readline()

View File

@ -407,7 +407,7 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt")
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.connect("delete_event", self.delete_event)
self.window.connect("destroy", self.destroy)
self.window.set_title("Free Poker DB - version: alpha2+, p78")
self.window.set_title("Free Poker DB - version: alpha3, p80")
self.window.set_border_width(1)
self.window.set_size_request(1020,400)
self.window.set_resizable(True)

View File

@ -25,6 +25,7 @@ class fpdb_db:
self.cursor=None
self.MYSQL_INNODB=2
self.PGSQL=3
self.SQLITE=4
#end def __init__
def connect(self, backend, host, database, user, password):
@ -39,8 +40,10 @@ class fpdb_db:
import MySQLdb
self.db=MySQLdb.connect(host = host, user = user, passwd = password, db = database)
elif backend==self.PGSQL:
import pgdb
self.db = pgdb.connect(dsn=host+":"+database, user='postgres', password=password)
# import pgdb
# self.db = pgdb.connect(dsn=host+":"+database, user='postgres', password=password)
import psycopg2
self.db = psycopg2.connect(host = host, user = user, password = password, database = database)
else:
raise fpdb_simple.FpdbError("unrecognised database backend:"+backend)
self.cursor=self.db.cursor()
@ -153,6 +156,24 @@ class fpdb_db:
def recreate_tables(self):
"""(Re-)creates the tables of the current DB"""
if self.backend == 3:
# postgresql
print "recreating tables in postgres db"
schema_file = open('schema.postgres.sql', 'r')
schema = schema_file.read()
schema_file.close()
curse = self.db.cursor()
# curse.executemany(schema, [1, 2])
for sql in schema.split(';'):
sql = sql.rstrip()
if sql == '':
continue
curse.execute(sql)
self.db.commit()
curse.close()
return
self.drop_tables()
self.create_table("""Settings (

21
pyfpdb/fpdb_import.py Executable file → Normal file
View File

@ -19,6 +19,7 @@
import sys
import MySQLdb
import psycopg2
#import pgdb
import math
import os
@ -37,7 +38,7 @@ def import_file(server, database, user, password, inputFile):
self.settings={'imp-callFpdbHud':False}
import_file_dict(self, settings)
def import_file_dict(options, settings, callHud=True):
def import_file_dict(options, settings, callHud=False):
last_read_hand=0
if (options.inputFile=="stdin"):
inputFile=sys.stdin
@ -45,8 +46,16 @@ def import_file_dict(options, settings, callHud=True):
inputFile=open(options.inputFile, "rU")
#connect to DB
db = MySQLdb.connect(host = options.server, user = options.user,
if options.settings['db-backend'] == 2:
db = MySQLdb.connect(host = options.server, user = options.user,
passwd = options.password, db = options.database)
elif options.settings['db-backend'] == 3:
db = psycopg2.connect(host = options.server, user = options.user,
password = options.password, database = options.database)
elif options.settings['db-backend'] == 4:
pass
else:
pass
cursor = db.cursor()
if (not options.quiet):
@ -104,11 +113,13 @@ def import_file_dict(options, settings, callHud=True):
db.commit()
stored+=1
if settings['imp-callFpdbHud'] and callHud and os.sep=='/':
db.commit()
# if settings['imp-callFpdbHud'] and callHud and os.sep=='/':
if settings['imp-callFpdbHud'] and callHud:
#print "call to HUD here. handsId:",handsId
#pipe the Hands.id out to the HUD
options.pipe_to_hud.stdin.write("%s\n" % (handsId))
db.commit()
# options.pipe_to_hud.write("%s" % (handsId) + os.linesep)
options.pipe_to_hud.stdin.write("%s" % (handsId) + os.linesep)
except fpdb_simple.DuplicateError:
duplicates+=1
except (ValueError), fe:

View File

@ -18,6 +18,7 @@
#This file contains simple functions for fpdb
import datetime
import re
PS=1
FTP=2
@ -781,8 +782,10 @@ def parseHandStartTime(topline, site):
#print "parsehandStartTime, tmp:", tmp
pos = tmp.find("-")+2
tmp = tmp[pos:]
#print "year:", tmp[0:4], "month", tmp[5:7], "day", tmp[8:10], "hour", tmp[13:15], "minute", tmp[16:18], "second", tmp[19:21]
result = datetime.datetime(int(tmp[0:4]), int(tmp[5:7]), int(tmp[8:10]), int(tmp[13:15]), int(tmp[16:18]), int(tmp[19:21]))
if re.search('\(ET\)', tmp): # the old datetime format at ps
result = datetime.datetime(int(tmp[0:4]), int(tmp[5:7]), int(tmp[8:10]), int(tmp[13:15]), int(tmp[16:18]), int(tmp[19:21]))
else: # new format
result = datetime.datetime(int(tmp[0:4]), int(tmp[5:7]), int(tmp[8:10]), int(tmp[11:13]), int(tmp[14:16]), int(tmp[17:19]))
else:
raise FpdbError("invalid site in parseHandStartTime")
@ -1951,7 +1954,7 @@ def storeHudCache(cursor, base, category, gametypeId, playerIds, hudImportData):
row[31], row[32], row[33], row[34], row[35], row[36], row[37], row[38], row[39], row[40],
row[41], row[42], row[43], row[44], row[45], row[46], row[47], row[48], row[49], row[50],
row[51], row[52], row[53], row[54], row[55], row[56], row[57], row[58], row[59], row[60],
row[1], row[2], row[3], row[4], row[5]))
row[1], row[2], row[3], str(row[4]), row[5]))
# else:
# print "todo: implement storeHudCache for stud base"
#end def storeHudCache

217
pyfpdb/schema.postgres.sql Normal file
View File

@ -0,0 +1,217 @@
DROP TABLE IF EXISTS Settings CASCADE;
CREATE TABLE Settings (version SMALLINT);
DROP TABLE IF EXISTS Sites CASCADE;
CREATE TABLE Sites (
id SERIAL UNIQUE, PRIMARY KEY (id),
name varchar(32),
currency char(3));
DROP TABLE IF EXISTS Gametypes CASCADE;
CREATE TABLE Gametypes (
id SERIAL UNIQUE, PRIMARY KEY (id),
siteId INTEGER, FOREIGN KEY (siteId) REFERENCES Sites(id),
type char(4),
base char(4),
category varchar(9),
limitType char(2),
hiLo char(1),
smallBlind int,
bigBlind int,
smallBet int,
bigBet int);
DROP TABLE IF EXISTS Players CASCADE;
CREATE TABLE Players (
id SERIAL UNIQUE, PRIMARY KEY (id),
name VARCHAR(32),
siteId INTEGER, FOREIGN KEY (siteId) REFERENCES Sites(id),
comment text,
commentTs timestamp without time zone);
DROP TABLE IF EXISTS Autorates CASCADE;
CREATE TABLE Autorates (
id BIGSERIAL UNIQUE, PRIMARY KEY (id),
playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id),
gametypeId INT, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
description varchar(50),
shortDesc char(8),
ratingTime timestamp without time zone,
handCount int);
DROP TABLE IF EXISTS Hands CASCADE;
CREATE TABLE Hands (
id BIGSERIAL UNIQUE, PRIMARY KEY (id),
tableName VARCHAR(20),
siteHandNo BIGINT,
gametypeId INT, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
handStart timestamp without time zone,
importTime timestamp without time zone,
seats SMALLINT,
maxSeats SMALLINT,
comment TEXT,
commentTs timestamp without time zone);
DROP TABLE IF EXISTS BoardCards CASCADE;
CREATE TABLE BoardCards (
id BIGSERIAL UNIQUE, PRIMARY KEY (id),
handId BIGINT, FOREIGN KEY (handId) REFERENCES Hands(id),
card1Value smallint,
card1Suit char(1),
card2Value smallint,
card2Suit char(1),
card3Value smallint,
card3Suit char(1),
card4Value smallint,
card4Suit char(1),
card5Value smallint,
card5Suit char(1));
DROP TABLE IF EXISTS TourneyTypes CASCADE;
CREATE TABLE TourneyTypes (
id SERIAL, PRIMARY KEY (id),
siteId INT, FOREIGN KEY (siteId) REFERENCES Sites(id),
buyin INT,
fee INT,
knockout INT,
rebuyOrAddon BOOLEAN);
DROP TABLE IF EXISTS Tourneys CASCADE;
CREATE TABLE Tourneys (
id SERIAL UNIQUE, PRIMARY KEY (id),
tourneyTypeId INT, FOREIGN KEY (tourneyTypeId) REFERENCES TourneyTypes(id),
siteTourneyNo BIGINT,
entries INT,
prizepool INT,
startTime timestamp without time zone,
comment TEXT,
commentTs timestamp without time zone);
DROP TABLE IF EXISTS TourneysPlayers CASCADE;
CREATE TABLE TourneysPlayers (
id BIGSERIAL UNIQUE, PRIMARY KEY (id),
tourneyId INT, FOREIGN KEY (tourneyId) REFERENCES Tourneys(id),
playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id),
payinAmount INT,
rank INT,
winnings INT,
comment TEXT,
commentTs timestamp without time zone);
DROP TABLE IF EXISTS HandsPlayers CASCADE;
CREATE TABLE HandsPlayers (
id BIGSERIAL UNIQUE, PRIMARY KEY (id),
handId BIGINT, FOREIGN KEY (handId) REFERENCES Hands(id),
playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id),
startCash INT,
position CHAR(1),
seatNo SMALLINT,
ante INT,
card1Value smallint,
card1Suit char(1),
card2Value smallint,
card2Suit char(1),
card3Value smallint,
card3Suit char(1),
card4Value smallint,
card4Suit char(1),
card5Value smallint,
card5Suit char(1),
card6Value smallint,
card6Suit char(1),
card7Value smallint,
card7Suit char(1),
winnings int,
rake int,
comment text,
commentTs timestamp without time zone,
tourneysPlayersId BIGINT, FOREIGN KEY (tourneysPlayersId) REFERENCES TourneysPlayers(id));
DROP TABLE IF EXISTS HandsActions CASCADE;
CREATE TABLE HandsActions (
id BIGSERIAL UNIQUE, PRIMARY KEY (id),
handPlayerId BIGINT, FOREIGN KEY (handPlayerId) REFERENCES HandsPlayers(id),
street SMALLINT,
actionNo SMALLINT,
action CHAR(5),
amount INT,
comment TEXT,
commentTs timestamp without time zone);
DROP TABLE IF EXISTS HudCache CASCADE;
CREATE TABLE HudCache (
id BIGSERIAL UNIQUE, PRIMARY KEY (id),
gametypeId INT, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id),
activeSeats SMALLINT,
position CHAR(1),
tourneyTypeId INT, FOREIGN KEY (tourneyTypeId) REFERENCES TourneyTypes(id),
HDs INT,
street0VPI INT,
street0Aggr INT,
street0_3B4BChance INT,
street0_3B4BDone INT,
street1Seen INT,
street2Seen INT,
street3Seen INT,
street4Seen INT,
sawShowdown INT,
street1Aggr INT,
street2Aggr INT,
street3Aggr INT,
street4Aggr INT,
otherRaisedStreet1 INT,
otherRaisedStreet2 INT,
otherRaisedStreet3 INT,
otherRaisedStreet4 INT,
foldToOtherRaisedStreet1 INT,
foldToOtherRaisedStreet2 INT,
foldToOtherRaisedStreet3 INT,
foldToOtherRaisedStreet4 INT,
wonWhenSeenStreet1 FLOAT,
wonAtSD FLOAT,
stealAttemptChance INT,
stealAttempted INT,
foldBbToStealChance INT,
foldedBbToSteal INT,
foldSbToStealChance INT,
foldedSbToSteal INT,
street1CBChance INT,
street1CBDone INT,
street2CBChance INT,
street2CBDone INT,
street3CBChance INT,
street3CBDone INT,
street4CBChance INT,
street4CBDone INT,
foldToStreet1CBChance INT,
foldToStreet1CBDone INT,
foldToStreet2CBChance INT,
foldToStreet2CBDone INT,
foldToStreet3CBChance INT,
foldToStreet3CBDone INT,
foldToStreet4CBChance INT,
foldToStreet4CBDone INT,
totalProfit INT,
street1CheckCallRaiseChance INT,
street1CheckCallRaiseDone INT,
street2CheckCallRaiseChance INT,
street2CheckCallRaiseDone INT,
street3CheckCallRaiseChance INT,
street3CheckCallRaiseDone INT,
street4CheckCallRaiseChance INT,
street4CheckCallRaiseDone INT);
INSERT INTO Settings VALUES (76);
INSERT INTO Sites ("name", currency) VALUES ('Full Tilt Poker', 'USD');
INSERT INTO Sites ("name", currency) VALUES ('PokerStars', 'USD');
INSERT INTO TourneyTypes (buyin, fee, knockout, rebuyOrAddon) VALUES (0, 0, 0, FALSE);