Merge branch 'master' of git://git.assembla.com/free_poker_tools

Conflicts:

	pyfpdb/Database.py
	pyfpdb/SQL.py
	pyfpdb/fpdb_db.py
	pyfpdb/fpdb_save_to_db.py

Also fix datetime issue with Everleaf
This commit is contained in:
Worros 2009-06-09 22:48:48 +08:00
commit c3d2c5a2be
17 changed files with 571 additions and 362 deletions

View File

@ -1,178 +0,0 @@
#!/usr/bin/pugs
#Copyright 2008 Steffen Jobbagy-Felso
#This program is free software: you can redistribute it and/or modify
#it under the terms of the GNU Affero General Public License as published by
#the Free Software Foundation, version 3 of the License.
#
#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 Affero General Public License
#along with this program. If not, see <http://www.gnu.org/licenses/>.
#In the "official" distribution you can find the license in
#agpl-3.0.txt in the docs folder of the package.
module LibFpdbImport;
use v6;
#use strict;
use LibFpdbShared;
#use LibFpdbImport2;
class Player {
has Str $name;
has Int $start_cash;
has Card @.cards;
has Char $position;
submethod BUILD (Str @strings) {
say "todo: implement Player.BUILD";
}#end Player.BUILD
our Player method find_players(@strings) {
#todo: i think this should be sub since its a class method not an instance method
say "todo: implement Player.find_players";
}
}#end class Player
class Line {
has Str $line;
has Bool $processed;
our protected submethod BUILD() {
say "todo: implement Line.BUILD?"
}#end Line.BUILD
our Line method recognise_and_parse(@strings) {
#todo: i think this should be sub since its a class method not an instance method
say "todo: implement Line.recognise_and_parse";
}#end Line.recognise_and_parse
}#end class Line
class ActionLine is Line {
has Player $player;
has Str $type;
has Int $amount;
has Bool $all_in;
has Int $action_no;
}#end class ActionLine
class WinLine is Line {
has Player $player;
has Int $amount;
}#end class WinLine
class RakeLine is Line {
has Int $amount;
}#end class RakeLine
class CardLine is Line {
has Bool $board_line;
has Player $player;
has Card @cards;
}#end class CardLine
#for useless lines
class CrapLine is Line {
has Str $type;
}#end class CrapLine
class Hand {
has Line @.lines;
#has Str @strings;
has Site $site;
has Str $currency;
has Str $type;
has Str $category;
has Str $limit_type;#todo: above ; missing causes error, but that doesnt list ; as a possibility
has Player @.players;
has Card @.board;
has Int $db_id;
submethod BUILD(Str @strings) {
Util.debug("running Hand.BUILD");
say "strings:",@strings;
#this contructor automatically parses the hand. call .store for storing
@.players=Player.find_players(@strings);
@.lines=Line.recognise_and_parse(@strings);
for @strings -> $line {
if class_of(line)==CardLine {
if line.board {
board=line.cards;
} else {
for player in players {
if line.player==player {
player.cards=line.cards;
}
}
}
}
}
}#end Hand.BUILD
our Bool method is_holdem(){
if category==("holdem"|"omahahi"|"omahahilo") {
return True;
} else {
return False;
}
}#end Hand.is_holdem
our Bool method is_stud(){
return not is_holdem();
}#end Hand.is_stud
our Bool method store($db) {
say "todo: Hand.store";
}#end Hand.store
}#end class Hand
class Importer {
#todo: be Thread?
submethod BUILD (Database $db, Str $filename) {
Util.debug("running Importer.BUILD");
if (not ($db.is_connected())) {
Util.fatal("not connected to DB");
}
my IO $?filehandle=$filename;
#for =$filehandle -> $line {say $line}
my Str @lines =$filehandle;
my Int $hand_start=0;
my Int $hand_end=0;
my Int $loopcount=0;
loop {#one loop of this per hand
$loopcount++;
say "loopcount", $loopcount;
my Int $current_line_index=$hand_end+1; #previous hand end is new hand start
for (my Int $i, $i<5, $i++) {#remove blank hands
if (@lines[$current_line_index].bytes) < 6 {
$current_line_index++;
} else {
$hand_start=$current_line_index;
break;
}
}
my Bool $continue=True; #todo: this is dumb, find out correct loop
while $continue {#loop through the lines to find end of hand
$current_line_index++;
if (@lines[$current_line_index].bytes) < 6 {
$hand_end=$current_line_index;
$continue=False;
}
}#end of find end of hand loop
my Str @handlines=@lines[$hand_start..$hand_end];
my Hand $hand .= new(:lines(@handlines));
$hand.store($db);
say "todo: record \$db_id";
say "todo: terminate on EOF";
}
}#end new Importer
}#end class Importer

View File

@ -1,84 +0,0 @@
#!/usr/bin/pugs
#Copyright 2008 Steffen Jobbagy-Felso
#This program is free software: you can redistribute it and/or modify
#it under the terms of the GNU Affero General Public License as published by
#the Free Software Foundation, version 3 of the License.
#
#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 Affero General Public License
#along with this program. If not, see <http://www.gnu.org/licenses/>.
#In the "official" distribution you can find the license in
#agpl-3.0.txt in the docs folder of the package.
module LibFpdbShared;
use v6;
#use strict;
class Util {
method debug(Str $string) {
#todo: i think this should be sub since its a class method not an instance method
say "debug notice: ", $string;
}#end debug_msg
sub warn(Str $string) {
say "todo: Util.warning";
}#end warning
sub fatal(Str $string, Database $db) {
say "todo: Util.fatal_error";
}#end fatal_error
}#end class Util
class Database {
has Str $backend;
has Str $host;
has Str $name;
has Str $user;
my Str $password;
submethod BUILD (Str $!backend, Str $!host, Str $!name, Str $!user, Str $!password) {
Util.debug("running Database.BUILD");
self.connect();
}#end new Database
our method connect() {
say "todo: db.connect";
}#end connect
method disconnect() {
say "todo: db.disconnect";
}#end disconnect
method cancel_import() {
say "todo: db.cancel_import";
}#end cancel_import
my method drop_tables() {
#todo: make this one private
say "todo: db.drop_tables";
}#end drop_tables
method recreate_tables() {
say "todo: db.recreate_tables";
}#end recreate_tables
#returns the id of the insert
our Int method insert(Str $sql_command) {
#todo: is it a bug that i need the "our" above?
say "todo: db.insert";
return 0;
}#end insert
our Str method fetch(Str $sql_command) {
say "todo: db.fetch";
}#end fetch
our Bool method is_connected() {
say "todo: db.is_connected";
}#end
}#end class Database

View File

@ -1,17 +0,0 @@
#!/usr/bin/pugs
#Copyright 2008 Steffen Jobbagy-Felso
#This program is free software: you can redistribute it and/or modify
#it under the terms of the GNU Affero General Public License as published by
#the Free Software Foundation, version 3 of the License.
#
#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 Affero General Public License
#along with this program. If not, see <http://www.gnu.org/licenses/>.
#In the "official" distribution you can find the license in
#agpl-3.0.txt in the docs folder of the package.

View File

@ -1,29 +0,0 @@
#!/usr/bin/pugs
#Copyright 2008 Steffen Jobbagy-Felso
#This program is free software: you can redistribute it and/or modify
#it under the terms of the GNU Affero General Public License as published by
#the Free Software Foundation, version 3 of the License.
#
#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 Affero General Public License
#along with this program. If not, see <http://www.gnu.org/licenses/>.
#In the "official" distribution you can find the license in
#agpl-3.0.txt in the docs folder of the package.
use v6;
#use strict;
use LibFpdbImport;
use LibFpdbShared;
my Database $db .= new(:backend<MySQL InnoDB>, :host<localhost>, :database<fpdb>, :user<fpdb>, :password<myPW>);
#todo: below doesnt work
my Importer $imp .= new(:db($db), :filename<HH-LHE1.txt>);
#perlbug?: adding another named argument that isnt listed in the constructor gave very weird error.
say $imp;

View File

@ -485,6 +485,9 @@ class Config:
try: db['db-server'] = self.supported_databases[name].db_server try: db['db-server'] = self.supported_databases[name].db_server
except: pass except: pass
try: db['db-type'] = self.supported_databases[name].db_type
except: pass
if string.lower(self.supported_databases[name].db_server) == 'mysql': if string.lower(self.supported_databases[name].db_server) == 'mysql':
db['db-backend'] = 2 db['db-backend'] = 2
elif string.lower(self.supported_databases[name].db_server) == 'postgresql': elif string.lower(self.supported_databases[name].db_server) == 'postgresql':

View File

@ -27,6 +27,7 @@ Create and manage the database objects.
import sys import sys
import traceback import traceback
from datetime import datetime, date, time, timedelta from datetime import datetime, date, time, timedelta
import string
# pyGTK modules # pyGTK modules
@ -37,15 +38,21 @@ import Card
class Database: class Database:
def __init__(self, c, db_name, game): def __init__(self, c, db_name, game):
if c.supported_databases[db_name].db_server == 'postgresql': db_params = c.get_db_parameters()
# psycopg2 database module for posgres via DB-API if (string.lower(db_params['db-server']) == 'postgresql' or
import psycopg2 string.lower(db_params['db-server']) == 'postgres'):
import psycopg2 # posgres via DB-API
import psycopg2.extensions
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
try: try:
self.connection = psycopg2.connect(host = c.supported_databases[db_name].db_ip, if db_params['db-host'] == 'localhost' or db_params['db-host'] == '127.0.0.1':
user = c.supported_databases[db_name].db_user, self.connection = psycopg2.connect(database = db_params['db-databaseName'])
password = c.supported_databases[db_name].db_pass, else:
database = c.supported_databases[db_name].db_name) 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: except:
print "Error opening database connection %s. See error log file." % (file) print "Error opening database connection %s. See error log file." % (file)
traceback.print_exc(file=sys.stderr) traceback.print_exc(file=sys.stderr)
@ -53,14 +60,13 @@ class Database:
sys.stdin.readline() sys.stdin.readline()
sys.exit() sys.exit()
elif c.supported_databases[db_name].db_server == 'mysql': elif string.lower(db_params['db-server']) == 'mysql':
# mysql bindings import MySQLdb # mysql bindings
import MySQLdb
try: try:
self.connection = MySQLdb.connect(host = c.supported_databases[db_name].db_ip, self.connection = MySQLdb.connect(host = db_params['db-host'],
user = c.supported_databases[db_name].db_user, user = db_params['db-user'],
passwd = c.supported_databases[db_name].db_pass, passwd = db_params['db-password'],
db = c.supported_databases[db_name].db_name) db = db_params['db-databaseName'])
cur_iso = self.connection.cursor() cur_iso = self.connection.cursor()
cur_iso.execute('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED') cur_iso.execute('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED')
cur_iso.close() cur_iso.close()
@ -78,9 +84,8 @@ class Database:
print "press enter to continue" print "press enter to continue"
sys.exit() sys.exit()
self.db_server = c.supported_databases[db_name].db_server self.type = db_params['db-type']
self.type = c.supported_databases[db_name].db_type self.sql = SQL.Sql(game = game, type = self.type)
self.sql = SQL.Sql(game = game, type = self.type, db_server = self.db_server)
self.connection.rollback() self.connection.rollback()
# To add to config: # To add to config:
@ -193,20 +198,20 @@ class Database:
# cards += "xx" # cards += "xx"
# else: # else:
# cards += ranks[d['card' + str(i) + 'Value']] + d['card' +str(i) + 'Suit'] # 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: if cv not in d or d[cv] == None:
break break
elif d[cv] == 0: elif d[cv] == 0:
cards += "xx" cards += "xx"
else: else:
cs = "card%dSuit" % i cs = "card%dsuit" % i
cards = "%s%s%s" % (cards, ranks[d[cv]], d[cs]) cards = "%s%s%s" % (cards, ranks[d[cv]], d[cs])
return cards return cards
def get_action_from_hand(self, hand_no): def get_action_from_hand(self, hand_no):
action = [ [], [], [], [], [] ] action = [ [], [], [], [], [] ]
c = self.connection.cursor() 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(): for row in c.fetchall():
street = row[0] street = row[0]
act = row[1:] act = row[1:]
@ -217,7 +222,7 @@ class Database:
"""Returns a hash of winners:amount won, given a hand number.""" """Returns a hash of winners:amount won, given a hand number."""
winners = {} winners = {}
c = self.connection.cursor() 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(): for row in c.fetchall():
winners[row[0]] = row[1] winners[row[0]] = row[1]
return winners return winners
@ -299,7 +304,6 @@ class Database:
return stat_dict return stat_dict
def get_player_id(self, config, site, player_name): def get_player_id(self, config, site, player_name):
print "site = %s, player name = %s" % (site, player_name)
c = self.connection.cursor() c = self.connection.cursor()
c.execute(self.sql.query['get_player_id'], {'player': player_name, 'site': site}) c.execute(self.sql.query['get_player_id'], {'player': player_name, 'site': site})
row = c.fetchone() row = c.fetchone()
@ -333,8 +337,9 @@ if __name__=="__main__":
# stat_dict = db_connection.get_stats_from_hand(h, hero) # stat_dict = db_connection.get_stats_from_hand(h, hero)
# for p in stat_dict.keys(): # for p in stat_dict.keys():
# print p, " ", stat_dict[p] # print p, " ", stat_dict[p]
>>>>>>> 7ef6a533ec6c73d5815ace42168067a6f8c26e3a:pyfpdb/Database.py
print "cards =", db_connection.get_cards(73525) print "cards =", db_connection.get_cards(u'1')
db_connection.close_connection db_connection.close_connection
print "press enter to continue" print "press enter to continue"

View File

@ -148,7 +148,6 @@ class HUD_main(object):
if new_hand_id == "": # blank line means quit if new_hand_id == "": # blank line means quit
self.destroy() self.destroy()
break # this thread is not always killed immediately with gtk.main_quit() break # this thread is not always killed immediately with gtk.main_quit()
# get basic info about the new hand from the db # get basic info about the new hand from the db
# if there is a db error, complain, skip hand, and proceed # if there is a db error, complain, skip hand, and proceed
try: try:

View File

@ -543,11 +543,16 @@ class Sql:
self.query['get_common_cards'] = """ self.query['get_common_cards'] = """
select select
card1Value, card1Suit, card1Value AS card1value,
card2Value, card2Suit, card1Suit AS card1suit,
card3Value, card3Suit, card2Value AS card2value,
card4Value, card4Suit, card2Suit AS card2suit,
card5Value, card5Suit card3Value AS card3value,
card3Suit AS card3suit,
card4Value AS card4value,
card4Suit AS card4suit,
card5Value AS card5value,
card5Suit AS card5suit
from BoardCards from BoardCards
where handId = %s where handId = %s
""" """

0
pyfpdb/Stats.py Normal file → Executable file
View File

154
pyfpdb/TableWindow.py Normal file
View File

@ -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

97
pyfpdb/Tables_Demo.py Normal file
View File

@ -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()

143
pyfpdb/WinTables.py Normal file
View File

@ -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)

101
pyfpdb/XTables.py Normal file
View File

@ -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)

View File

@ -176,23 +176,16 @@ class fpdb_db:
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE) psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
# If DB connection is made over TCP, then the variables # If DB connection is made over TCP, then the variables
# host, user and password are required # host, user and password are required
# print "host=%s user=%s pass=%s." % (host, user, password) # For local domain-socket connections, only DB name is
if self.host and self.user and self.password: # needed, and everything else is in fact undefined and/or
try: # 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, self.db = psycopg2.connect(host = host,
user = user, user = user,
password = password, password = password,
database = database) database = database)
except:
raise fpdb_simple.FpdbError("PostgreSQL connection failed")
# For local domain-socket connections, only DB name is
# needed, and everything else is in fact undefined and/or
# flat out wrong
else:
try:
self.db = psycopg2.connect(database = database)
except:
raise fpdb_simple.FpdbError("PostgreSQL connection failed")
else: else:
raise fpdb_simple.FpdbError("unrecognised database backend:"+backend) raise fpdb_simple.FpdbError("unrecognised database backend:"+backend)
self.cursor=self.db.cursor() self.cursor=self.db.cursor()

View File

@ -44,6 +44,9 @@ except:
try: try:
import psycopg2 import psycopg2
pgsqlLibFound=True pgsqlLibFound=True
import psycopg2.extensions
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
except: except:
pass pass

View File

@ -26,13 +26,14 @@ MYSQL_INNODB = 2
PGSQL = 3 PGSQL = 3
SQLITE = 4 SQLITE = 4
fastStoreHudCache = False # set this to True to test the new storeHudCache routine #fastStoreHudCache = False # set this to True to test the new storeHudCache routine
#
#saveActions = True # set this to False to avoid storing action data
# # Pros: speeds up imports
# # Cons: no action data is saved, so you need to keep the hand histories
# # variance not available on stats page
# # no graphs
saveActions = True # set this to False to avoid storing action data
# Pros: speeds up imports
# Cons: no action data is saved, so you need to keep the hand histories
# variance not available on stats page
# : No graphs
#stores a stud/razz hand into the database #stores a stud/razz hand into the database
def ring_stud(config, backend, db, cursor, base, category, site_hand_no, gametype_id, hand_start_time def ring_stud(config, backend, db, cursor, base, category, site_hand_no, gametype_id, hand_start_time
,names, player_ids, start_cashes, antes, card_values, card_suits, winnings, rakes ,names, player_ids, start_cashes, antes, card_values, card_suits, winnings, rakes

View File

@ -1,6 +1,7 @@
#!/bin/sh #!/bin/sh
#Copyright 2008 Steffen Jobbagy-Felso #Copyright 2008 Steffen Jobbagy-Felso
#Copyright 2009 Ray E. Barker
#This program is free software: you can redistribute it and/or modify #This program is free software: you can redistribute it and/or modify
#it under the terms of the GNU Affero General Public License as published by #it under the terms of the GNU Affero General Public License as published by
#the Free Software Foundation, version 3 of the License. #the Free Software Foundation, version 3 of the License.
@ -15,10 +16,22 @@
#In the "official" distribution you can find the license in #In the "official" distribution you can find the license in
#agpl-3.0.txt in the docs folder of the package. #agpl-3.0.txt in the docs folder of the package.
# This script prepares the compressed distribution files for
# uploading to sourceforge.
#
# Run from the root of your git repo (the folder that has .git in it)
# USAGE: $ utils/create-release.sh V
# where V is the current version. e.g. utils/create-release.sh 0.55
#get rid of extraneous stuff #get rid of extraneous stuff
rm regression-test/*.found.txt rm regression-test/*.found.txt
rm regression-test/*.pyc rm regression-test/*.pyc
rm pyfpdb/*.pyc rm pyfpdb/*.pyc
rm pyfpdb/*~
rm pyfpdb/fpdb-error-log.txt
rm pyfpdb/HUD-error.txt
rm pyfpdb/hand-errors.txt
# make the fpdb_$1.zip file for windows # make the fpdb_$1.zip file for windows
echo "*** making zip file" echo "*** making zip file"