diff --git a/docs/known-bugs-and-planned-features.txt b/docs/known-bugs-and-planned-features.txt index fec10458..f6ae60dc 100644 --- a/docs/known-bugs-and-planned-features.txt +++ b/docs/known-bugs-and-planned-features.txt @@ -4,6 +4,7 @@ Everything is subject to change and especially the order will often change. Patc alpha2 (release by 17Aug) ====== make windows use correct language version of Appdata, e.g. Anwendungdaten +stop bulk importer from executing HUD hook seperate and improve instructions for update update status or make a support matrix table for website add instructions for mailing list to contacts diff --git a/pyfpdb/Cards01.png b/pyfpdb/Cards01.png new file mode 100644 index 00000000..2c64fc9e Binary files /dev/null and b/pyfpdb/Cards01.png differ diff --git a/pyfpdb/Configuration.py b/pyfpdb/Configuration.py new file mode 100644 index 00000000..2ade6bee --- /dev/null +++ b/pyfpdb/Configuration.py @@ -0,0 +1,211 @@ +#!/usr/bin/env python +"""Configuration.py + +Handles HUD configuration files. +""" +# Copyright 2008, 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 xml.dom.minidom +from xml.dom.minidom import Node + +class Layout: + def __init__(self, max): + self.max = int(max) + self.location = [] + for i in range(self.max + 1): self.location.append(None) + + def __str__(self): + temp = " Layout = %d max, width= %d, height = %d, fav_seat = %d\n" % (self.max, self.width, self.height, self.fav_seat) + temp = temp + " Locations = " + for i in range(1, len(self.location)): + temp = temp + "(%d,%d)" % self.location[i] + + return temp + +class Site: + def __init__(self, node): + self.site_name = node.getAttribute("site_name") + self.table_finder = node.getAttribute("table_finder") + self.screen_name = node.getAttribute("screen_name") + self.site_path = node.getAttribute("site_path") + self.HH_path = node.getAttribute("HH_path") + self.decoder = node.getAttribute("decoder") + self.layout = {} + + for layout_node in node.getElementsByTagName('layout'): + max = int( layout_node.getAttribute('max') ) + lo = Layout(max) + lo.fav_seat = int( layout_node.getAttribute('fav_seat') ) + lo.width = int( layout_node.getAttribute('width') ) + lo.height = int( layout_node.getAttribute('height') ) + + for location_node in layout_node.getElementsByTagName('location'): + lo.location[int( location_node.getAttribute('seat') )] = (int( location_node.getAttribute('x') ), int( location_node.getAttribute('y'))) + + self.layout[lo.max] = lo + + def __str__(self): + temp = "Site = " + self.site_name + "\n" + for key in dir(self): + if key.startswith('__'): continue + if key == 'layout': continue + value = getattr(self, key) + if callable(value): continue + temp = temp + ' ' + key + " = " + value + "\n" + + for layout in self.layout: + temp = temp + "%s" % self.layout[layout] + + return temp + +class Stat: + def __init__(self): + 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) + return temp + +class Game: + def __init__(self, node): + self.game_name = node.getAttribute("game_name") + self.db = node.getAttribute("db") + self.rows = int( node.getAttribute("rows") ) + self.cols = int( node.getAttribute("cols") ) + + self.stats = {} + for stat_node in node.getElementsByTagName('stat'): + stat = Stat() + stat.stat_name = stat_node.getAttribute("stat_name") + stat.row = int( stat_node.getAttribute("row") ) + stat.col = int( stat_node.getAttribute("col") ) + stat.tip = stat_node.getAttribute("tip") + stat.click = stat_node.getAttribute("click") + + self.stats[stat.stat_name] = stat + + def __str__(self): + temp = "Game = " + self.game_name + "\n" + temp = temp + " db = %s\n" % self.db + temp = temp + " rows = %d\n" % self.rows + temp = temp + " cols = %d\n" % self.cols + + for stat in self.stats.keys(): + temp = temp + "%s" % self.stats[stat] + + return temp + +class Database: + def __init__(self, node): + self.db_name = node.getAttribute("db_name") + self.db_server = node.getAttribute("db_server") + self.db_ip = node.getAttribute("db_ip") + self.db_user = node.getAttribute("db_user") + self.db_type = node.getAttribute("db_type") + self.db_pass = node.getAttribute("db_pass") + + def __str__(self): + temp = 'Database = ' + self.db_name + '\n' + for key in dir(self): + if key.startswith('__'): continue + value = getattr(self, key) + if callable(value): continue + temp = temp + ' ' + key + " = " + value + "\n" + return temp + +class Mucked: + def __init__(self, node): + self.name = node.getAttribute("mw_name") + self.cards = node.getAttribute("deck") + self.card_wd = node.getAttribute("card_wd") + self.card_ht = node.getAttribute("card_ht") + self.rows = node.getAttribute("rows") + self.cols = node.getAttribute("cols") + self.format = node.getAttribute("stud") + print "mw name = " + self.name + + def __str__(self): + temp = 'Mucked = ' + self.name + "\n" + for key in dir(self): + if key.startswith('__'): continue + value = getattr(self, key) + if callable(value): continue + temp = temp + ' ' + key + " = " + value + "\n" + return temp + +class Config: + def __init__(self, file = 'HUD_config.xml'): + + doc = xml.dom.minidom.parse(file) + + self.supported_sites = {} + self.supported_games = {} + self.supported_databases = {} + self.mucked_windows = {} + +# s_sites = doc.getElementsByTagName("supported_sites") + for site_node in doc.getElementsByTagName("site"): + site = Site(node = site_node) + self.supported_sites[site.site_name] = site + + s_games = doc.getElementsByTagName("supported_games") + for game_node in doc.getElementsByTagName("game"): + game = Game(node = game_node) + self.supported_games[game.game_name] = game + + s_dbs = doc.getElementsByTagName("supported_databases") + for db_node in doc.getElementsByTagName("database"): + db = Database(node = db_node) + self.supported_databases[db.db_name] = db + + s_dbs = doc.getElementsByTagName("mucked_windows") + for mw_node in doc.getElementsByTagName("mw"): + mw = Mucked(node = mw_node) + self.mucked_windows[mw.name] = mw + +if __name__== "__main__": + c = Config() + + print "\n----------- SUPPORTED SITES -----------" + for s in c.supported_sites.keys(): + print c.supported_sites[s] + + print "----------- END SUPPORTED SITES -----------" + + + print "\n----------- SUPPORTED GAMES -----------" + for game in c.supported_games.keys(): + print c.supported_games[game] + + print "----------- END SUPPORTED GAMES -----------" + + + print "\n----------- SUPPORTED DATABASES -----------" + for db in c.supported_databases.keys(): + print c.supported_databases[db] + + print "----------- END SUPPORTED DATABASES -----------" + + print "\n----------- MUCKED WINDOW FORMATS -----------" + for w in c.mucked_windows.keys(): + print c.mucked_windows[w] + + print "----------- END MUCKED WINDOW FORMATS -----------" diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py new file mode 100644 index 00000000..0d6db697 --- /dev/null +++ b/pyfpdb/Database.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python +"""Database.py + +Create and manage the database objects. +""" +# Copyright 2008, 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 + +######################################################################## + +# postmaster -D /var/lib/pgsql/data + +# Standard Library modules + +# pyGTK modules + +# FreePokerTools modules +import Configuration +import SQL + +# pgdb database module for posgres via DB-API +#import pgdb +# pgdb uses pyformat. is that fixed or an option? + +# mysql bindings +import MySQLdb + +class Database: + def __init__(self, c, db_name, game): + print "db_name = " + db_name + if c.supported_databases[db_name].db_server == 'postgresql': + self.connection = pgdb.connect(dsn = 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) + + elif c.supported_databases[db_name].db_server == 'mysql': + self.connection = MySQLdb.connect(host = c.supported_databases[db_name].db_ip, + user = c.supported_databases[db_name].db_user, + passwd = c.supported_databases[db_name].db_pass, + db = c.supported_databases[db_name].db_name) + + else: + print "Database not recognized." + return(0) + + 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 get_table_name(self, hand_id): + c = self.connection.cursor() + c.execute(self.sql.query['get_table_name'], (hand_id)) + row = c.fetchone() + return row + + def get_last_hand(self): + c = self.connection.cursor() + c.execute(self.sql.query['get_last_hand']) + row = c.fetchone() + return row[0] + + def get_xml(self, hand_id): + c = self.connection.cursor() + c.execute(self.sql.query['get_xml'], (hand_id)) + row = c.fetchone() + return row[0] + + def get_recent_hands(self, last_hand): + c = self.connection.cursor() + c.execute(self.sql.query['get_recent_hands'], {'last_hand': last_hand}) + return c.fetchall() + + def get_hand_info(self, new_hand_id): + c = self.connection.cursor() + c.execute(self.sql.query['get_hand_info'], new_hand_id) + return c.fetchall() + + def get_cards(self, hand): + 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_stats_from_hand(self, hand, hero): + c = self.connection.cursor() + +# get the players in the hand and their seats + c.execute(self.sql.query['get_players_from_hand'], (hand)) + names = {} + seats = {} + for row in c.fetchall(): + names[row[0]] = row[2] + seats[row[0]] = row[1] + +# now get the stats + c.execute(self.sql.query['get_stats_from_hand'], (hand, hand)) + colnames = [desc[0] for desc in c.description] + stat_dict = {} + for row in c.fetchall(): + t_dict = {} + for name, val in zip(colnames, row): + t_dict[name] = val +# print t_dict + t_dict['screen_name'] = names[t_dict['player_id']] + t_dict['seat'] = seats[t_dict['player_id']] + stat_dict[t_dict['player_id']] = t_dict + return stat_dict + + def get_player_id(self, config, site, player_name): + print "site = %s, player name = %s" % (site, player_name) + c = self.connection.cursor() + c.execute(self.sql.query['get_player_id'], {'player': player_name, 'site': site}) + row = c.fetchone() + return row[0] + +if __name__=="__main__": + c = Configuration.Config() + + db_connection = Database(c, 'fpdb', 'holdem') # 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 + print "database type = ", db_connection.type + + h = db_connection.get_last_hand() + print "last hand = ", h + + 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, hero) + for p in stat_dict.keys(): + print p, " ", stat_dict[p] + db_connection.close diff --git a/pyfpdb/GuiAutoImport.py b/pyfpdb/GuiAutoImport.py index 3d345c29..af34ad58 100644 --- a/pyfpdb/GuiAutoImport.py +++ b/pyfpdb/GuiAutoImport.py @@ -55,7 +55,7 @@ class GuiAutoImport (threading.Thread): else: self.inputFile=self.path+os.sep+file fpdb_import.import_file_dict(self, self.settings) - print "GuiBulkImport.import_dir done" + print "GuiAutoImport.import_dir done" interval=int(self.intervalTBuffer.get_text(self.intervalTBuffer.get_start_iter(), self.intervalTBuffer.get_end_iter())) time.sleep(interval) diff --git a/pyfpdb/HUD_config.xml.example b/pyfpdb/HUD_config.xml.example new file mode 100644 index 00000000..7d76688f --- /dev/null +++ b/pyfpdb/HUD_config.xml.example @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pyfpdb/HUD_main.py b/pyfpdb/HUD_main.py new file mode 100755 index 00000000..1f71625b --- /dev/null +++ b/pyfpdb/HUD_main.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python + +"""Hud_main.py + +Main for FreePokerTools HUD. +""" +# Copyright 2008, 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 + +######################################################################## + +# to do kill window on my seat +# 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 + +# Standard Library modules +import sys +import os + +# pyGTK modules +import pygtk +import gtk +import gobject + +# FreePokerTools modules +import Configuration +import Database +import Tables +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): +# 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') + + (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): + hud_dict[table_name].update(new_hand_id, db_connection, config) +# otherwise create a new hud + else: + 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].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) + +if __name__== "__main__": + main_window = gtk.Window() + main_window.connect("destroy", destroy) + label = gtk.Label('Fake main window, blah blah, blah\nblah, blah') + main_window.add(label) + 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() diff --git a/pyfpdb/HandHistory.py b/pyfpdb/HandHistory.py new file mode 100644 index 00000000..dd2fa427 --- /dev/null +++ b/pyfpdb/HandHistory.py @@ -0,0 +1,194 @@ +#!/usr/bin/env python +"""HandHistory.py + +Parses HandHistory xml files and returns requested objects. +""" +# Copyright 2008, 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 xml.dom.minidom +from xml.dom.minidom import Node + +class HandHistory: + def __init__(self, xml_string, elements = ('ALL')): + + doc = xml.dom.minidom.parseString(xml_string) + if elements == ('ALL'): + elements = ('BETTING', 'AWARDS', 'POSTS', 'PLAYERS', 'GAME') + + if 'BETTING' in elements: + self.BETTING = Betting(doc.getElementsByTagName('BETTING')[0]) + if 'AWARDS' in elements: + self.AWARDS = Awards (doc.getElementsByTagName('AWARDS')[0]) + if 'POSTS' in elements: + self.POSTS = Posts (doc.getElementsByTagName('POSTS')[0]) + if 'GAME' in elements: + self.GAME = Game (doc.getElementsByTagName('GAME')[0]) + if 'PLAYERS' in elements: + self.PLAYERS = {} + p_n = doc.getElementsByTagName('PLAYERS')[0] + for p in p_n.getElementsByTagName('PLAYER'): + a_player = Player(p) + self.PLAYERS[a_player.name] = a_player + +class Player: + def __init__(self, node): + self.name = node.getAttribute('NAME') + self.seat = node.getAttribute('SEAT') + self.stack = node.getAttribute('STACK') + self.showed_hand = node.getAttribute('SHOWED_HAND') + self.cards = node.getAttribute('CARDS') + self.allin = node.getAttribute('ALLIN') + self.sitting_out = node.getAttribute('SITTING_OUT') + self.hand = node.getAttribute('HAND') + self.start_cards = node.getAttribute('START_CARDS') + + if self.allin == '' or \ + self.allin == '0' or \ + self.allin.upper() == 'FALSE': self.allin = False + else: self.allin = True + + if self.sitting_out == '' or \ + self.sitting_out == '0' or \ + self.sitting_out.upper() == 'FALSE': self.sitting_out = False + else: self.sitting_out = True + + def __str__(self): + temp = "%s\n seat = %s\n stack = %s\n cards = %s\n" % \ + (self.name, self.seat, self.stack, self.cards) + temp = temp + " showed_hand = %s\n allin = %s\n" % \ + (self.showed_hand, self.allin) + temp = temp + " hand = %s\n start_cards = %s\n" % \ + (self.hand, self.start_cards) + return temp + +class Awards: + def __init__(self, node): + self.awards = [] # just an array of award objects + for a in node.getElementsByTagName('AWARD'): + self.awards.append(Award(a)) + + def __str__(self): + temp = "" + for a in self.awards: + temp = temp + "%s\n" % (a) + return temp + +class Award: + def __init__(self, node): + self.player = node.getAttribute('PLAYER') + self.amount = node.getAttribute('AMOUNT') + self.pot = node.getAttribute('POT') + + def __str__(self): + return self.player + " won " + self.amount + " from " + self.pot + +class Game: + def __init__(self, node): + print node + self.tags = {} + for tag in ( ('GAME_NAME', 'game_name'), ('MAX', 'max'), ('HIGHLOW', 'high_low'), + ('STRUCTURE', 'structure'), ('MIXED', 'mixed') ): + L = node.getElementsByTagName(tag[0]) + if (not L): continue + print L + for node2 in L: + title = "" + for node3 in node2.childNodes: + if (node3.nodeType == Node.TEXT_NODE): + title +=node3.data + self.tags[tag[1]] = title + + def __str__(self): + return "%s %s %s, (%s max), %s" % (self.tags['structure'], + self.tags['game_name'], + self.tags['game_name'], + self.tags['max'], + self.tags['game_name']) + +class Posts: + def __init__(self, node): + self.posts = [] # just an array of post objects + for p in node.getElementsByTagName('POST'): + self.posts.append(Post(p)) + + def __str__(self): + temp = "" + for p in self.posts: + temp = temp + "%s\n" % (p) + return temp + +class Post: + def __init__(self, node): + self.player = node.getAttribute('PLAYER') + self.amount = node.getAttribute('AMOUNT') + self.posted = node.getAttribute('POSTED') + self.live = node.getAttribute('LIVE') + + def __str__(self): + return ("%s posted %s %s %s") % (self.player, self.amount, self.posted, self.live) + +class Betting: + def __init__(self, node): + self.rounds = [] # a Betting object is just an array of rounds + for r in node.getElementsByTagName('ROUND'): + self.rounds.append(Round(r)) + + def __str__(self): + temp = "" + for r in self.rounds: + temp = temp + "%s\n" % (r) + return temp + +class Round: + def __init__(self, node): + self.name = node.getAttribute('ROUND_NAME') + self.action = [] + for a in node.getElementsByTagName('ACTION'): + self.action.append(Action(a)) + + def __str__(self): + temp = self.name + "\n" + for a in self.action: + temp = temp + " %s\n" % (a) + return temp + +class Action: + def __init__(self, node): + self.player = node.getAttribute('PLAYER') + self.action = node.getAttribute('ACT') + self.amount = node.getAttribute('AMOUNT') + self.allin = node.getAttribute('ALLIN') + + def __str__(self): + return self.player + " " + self.action + " " + self.amount + " " + self.allin + +if __name__== "__main__": + file = open('test.xml', 'r') + xml_string = file.read() + file.close() + + print xml_string + "\n\n\n" + h = HandHistory(xml_string, ('ALL')) + print h.GAME + print h.POSTS + print h.BETTING + print h.AWARDS + + for p in h.PLAYERS.keys(): + print h.PLAYERS[p] \ No newline at end of file diff --git a/pyfpdb/Hud.py b/pyfpdb/Hud.py new file mode 100644 index 00000000..9b35f283 --- /dev/null +++ b/pyfpdb/Hud.py @@ -0,0 +1,189 @@ +#!/usr/bin/env python +"""Hud.py + +Create and manage the hud overlays. +""" +# Copyright 2008, 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 + +# pyGTK modules +import pygtk +import gtk +import pango +import gobject + +# FreePokerTools modules +import Tables # needed for testing only +import Configuration +import Stats +import Mucked +import Database + +class Hud: + + def __init__(self, table, max, poker_game, config, db_connection): + self.table = table + self.config = config + self.poker_game = poker_game + self.max = max + self.db_connection = db_connection + + self.stat_windows = {} + self.font = pango.FontDescription("Sans 8") + + + 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 +# +# this method also manages the creating and destruction of stat +# windows via calls to the Stat_Window class + 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], + table = self.table, + x = x, + y = y, + seat = i, + player_id = 'fake', + font = self.font) + + self.stats = [['', '', ''], ['', '', '']] + for stat in config.supported_games[self.poker_game].stats.keys(): + self.stats[config.supported_games[self.poker_game].stats[stat].row] \ + [config.supported_games[self.poker_game].stats[stat].col] = \ + config.supported_games[self.poker_game].stats[stat].stat_name + +# self.mucked_window = gtk.Window() +# self.m = Mucked.Mucked(self.mucked_window, self.db_connection) +# self.mucked_window.show_all() + + def update(self, hand, db, config): + stat_dict = db.get_stats_from_hand(hand, 3) + for s in stat_dict.keys(): + for r in range(0, 2): + for c in range(0, 3): + number = Stats.do_stat(stat_dict, player = stat_dict[s]['player_id'], stat = self.stats[r][c]) + self.stat_windows[stat_dict[s]['seat']].label[r][c].set_text(number[1]) + tip = stat_dict[s]['screen_name'] + "\n" + number[5] + "\n" + \ + number[3] + ", " + number[4] + Stats.do_tip(self.stat_windows[stat_dict[s]['seat']].e_box[r][c], tip) +# self.m.update(hand) + +class Stat_Window: + + def button_press_cb(self, widget, event, *args): +# This handles all callbacks from button presses on the event boxes in +# the stat windows. There is a bit of an ugly kludge to separate single- +# and double-clicks. + 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) + 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 + print "middle button clicked" + + if event.button == 3: # right button event + print "right button clicked" + + def single_click(self): +# 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" + self.sb_click = 0 + 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 __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 + + self.window = gtk.Window() + self.window.set_decorated(0) + self.window.set_gravity(gtk.gdk.GRAVITY_STATIC) + self.window.set_title("%s" % seat) + + self.grid = gtk.Table(rows = self.game.rows, columns = self.game.cols, homogeneous = False) + self.window.add(self.grid) + + self.e_box = [] + self.frame = [] + self.label = [] + for r in range(self.game.rows): + self.e_box.append([]) + self.label.append([]) + 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.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) +# font = pango.FontDescription("Sans 8") + self.label[r][c].modify_font(font) + self.window.realize + self.window.move(self.x, self.y) + self.window.set_keep_above(1) + self.window.show_all() + +def destroy(*args): # call back for terminating the main eventloop + gtk.main_quit() + +if __name__== "__main__": + main_window = gtk.Window() + main_window.connect("destroy", destroy) + label = gtk.Label('Fake main window, blah blah, blah\nblah, blah') + main_window.add(label) + main_window.show_all() + + c = Configuration.Config() + tables = Tables.discover(c) + db = Database.Database(c, 'PTrackSv2', 'razz') + + for t in tables: + win = Hud(t, 8, c, db) +# t.get_details() + win.update(8300, db, c) + + gtk.main() diff --git a/pyfpdb/Mucked.py b/pyfpdb/Mucked.py new file mode 100644 index 00000000..a1f76f87 --- /dev/null +++ b/pyfpdb/Mucked.py @@ -0,0 +1,241 @@ +#!/usr/bin/env python +"""Mucked.py + +Mucked cards display for FreePokerTools HUD. +""" +# Copyright 2008, 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 + +######################################################################## + +# to do +# problem with hand 30586 + +# Standard Library modules +import sys +import os +import string +import xml.dom.minidom +from xml.dom.minidom import Node + +# pyGTK modules +import pygtk +import gtk +import gobject + +# FreePokerTools modules +import Configuration +import Database +import Tables +import Hud +import Mucked +import HandHistory + +class Mucked: + def __init__(self, parent, db_connection): + + self.parent = parent #this is the parent of the mucked cards widget + self.db_connection = db_connection + + self.vbox = gtk.VBox() + self.parent.add(self.vbox) + + self.mucked_list = MuckedList (self.vbox, db_connection) + self.mucked_cards = MuckedCards(self.vbox, db_connection) + self.mucked_list.mucked_cards = self.mucked_cards + + def update(self, new_hand_id): + self.mucked_list.update(new_hand_id) + +class MuckedList: + def __init__(self, parent, db_connection): + + self.parent = parent + self.db_connection = db_connection + +# set up a scrolled window to hold the listbox + self.scrolled_window = gtk.ScrolledWindow() + self.scrolled_window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS) + self.parent.add(self.scrolled_window) + +# create a ListStore to use as the model + self.liststore = gtk.ListStore(str, str, str) + self.treeview = gtk.TreeView(self.liststore) + self.tvcolumn0 = gtk.TreeViewColumn('HandID') + self.tvcolumn1 = gtk.TreeViewColumn('Cards') + self.tvcolumn2 = gtk.TreeViewColumn('Net') + +# add tvcolumn to treeview + self.treeview.append_column(self.tvcolumn0) + self.treeview.append_column(self.tvcolumn1) + self.treeview.append_column(self.tvcolumn2) + +# create a CellRendererText to render the data + self.cell = gtk.CellRendererText() + + # add the cell to the tvcolumn and allow it to expand + self.tvcolumn0.pack_start(self.cell, True) + self.tvcolumn1.pack_start(self.cell, True) + self.tvcolumn2.pack_start(self.cell, True) + self.tvcolumn0.add_attribute(self.cell, 'text', 0) + self.tvcolumn1.add_attribute(self.cell, 'text', 1) + self.tvcolumn2.add_attribute(self.cell, 'text', 2) +# resize the cols if nec + self.tvcolumn0.set_resizable(True) + self.treeview.connect("row-activated", self.activated_event) + + self.scrolled_window.add_with_viewport(self.treeview) + + def activated_event(self, path, column, data=None): + sel = self.treeview.get_selection() + (model, iter) = sel.get_selected() + 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) + iter = self.liststore.append(info_row[0]) + sel = self.treeview.get_selection() + sel.select_iter(iter) + + vadj = self.scrolled_window.get_vadjustment() + vadj.set_value(vadj.upper) + self.mucked_cards.update(new_hand_id) + +class MuckedCards: + def __init__(self, parent, db_connection): + + self.parent = parent #this is the parent of the mucked cards widget + self.db_connection = db_connection + + self.card_images = self.get_card_images() + self.seen_cards = {} + self.grid_contents = {} + self.eb = {} + + self.rows = 8 + self.cols = 7 + self.grid = gtk.Table(self.rows, self.cols + 4, homogeneous = False) + + for r in range(0, self.rows): + for c in range(0, self.cols): + self.seen_cards[(c, r)] = gtk.image_new_from_pixbuf(self.card_images[('B', 'S')]) + self.eb[(c, r)]= gtk.EventBox() + +# set up the contents for the cells + for r in range(0, self.rows): + self.grid_contents[( 0, r)] = gtk.Label("%d" % (r + 1)) + self.grid_contents[( 1, r)] = gtk.Label("player %d" % (r + 1)) + self.grid_contents[( 4, r)] = gtk.Label("-") + self.grid_contents[( 9, r)] = gtk.Label("-") + self.grid_contents[( 2, r)] = self.eb[( 0, r)] + self.grid_contents[( 3, r)] = self.eb[( 1, r)] + self.grid_contents[( 5, r)] = self.eb[( 2, r)] + self.grid_contents[( 6, r)] = self.eb[( 3, r)] + self.grid_contents[( 7, r)] = self.eb[( 4, r)] + self.grid_contents[( 8, r)] = self.eb[( 5, r)] + self.grid_contents[(10, r)] = self.eb[( 6, r)] + for c in range(0, self.cols): + self.eb[(c, r)].add(self.seen_cards[(c, r)]) + +# add the cell contents to the table + for c in range(0, self.cols + 4): + for r in range(0, self.rows): + self.grid.attach(self.grid_contents[(c, r)], c, c+1, r, r+1, xpadding = 1, ypadding = 1) + + self.parent.add(self.grid) + + def update(self, new_hand_id): + cards = self.db_connection.get_cards(new_hand_id) + self.clear() + + for c in cards.keys(): + self.grid_contents[(1, cards[c]['seat_number'] - 1)].set_text(cards[c]['screen_name']) + + for i in ((0, 'hole_card_1'), (1, 'hole_card_2'), (2, 'hole_card_3'), (3, 'hole_card_4'), + (4, 'hole_card_5'), (5, 'hole_card_6'), (6, 'hole_card_7')): + if not cards[c][i[1]] == "": + self.seen_cards[(i[0], cards[c]['seat_number'] - 1)]. \ + set_from_pixbuf(self.card_images[self.split_cards(cards[c][i[1]])]) + + xml_text = self.db_connection.get_xml(new_hand_id) + hh = HandHistory.HandHistory(xml_text, ('BETTING')) + +# action in tool tips for 3rd street cards + tip = "%s" % hh.BETTING.rounds[0] + for c in (0, 1, 2): + for r in range(0, self.rows): + self.eb[(c, r)].set_tooltip_text(tip) + +# action in tools tips for later streets + round_to_col = (0, 3, 4, 5, 6) + for round in range(1, len(hh.BETTING.rounds)): + tip = "%s" % hh.BETTING.rounds[round] + for r in range(0, self.rows): + self.eb[(round_to_col[round], r)].set_tooltip_text(tip) + + def split_cards(self, card): + return (card[0], card[1].upper()) + + def clear(self): + for r in range(0, self.rows): + self.grid_contents[(1, r)].set_text(" ") + for c in range(0, 7): + self.seen_cards[(c, r)].set_from_pixbuf(self.card_images[('B', 'S')]) + self.eb[(c, r)].set_tooltip_text('') + def get_card_images(self): + card_images = {} + suits = ('S', 'H', 'D', 'C') + ranks = ('A', 'K', 'Q', 'J', 'T', '9', '8', '7', '6', '5', '4', '3', '2', 'B') + pb = gtk.gdk.pixbuf_new_from_file("Cards01.png") + + for j in range(0, 14): + for i in range(0, 4): + temp_pb = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, pb.get_has_alpha(), pb.get_bits_per_sample(), 30, 42) + pb.copy_area(30*j, 42*i, 30, 42, temp_pb, 0, 0) + card_images[(ranks[j], suits[i])] = temp_pb + return(card_images) + +# cards are 30 wide x 42 high + +if __name__== "__main__": + + def destroy(*args): # call back for terminating the main eventloop + gtk.main_quit() # used only for testing + + def process_new_hand(source, condition): #callback from stdin watch -- testing only +# there is a new hand_id to be processed +# just read it and pass it to update + new_hand_id = sys.stdin.readline() + new_hand_id = new_hand_id.rstrip() # remove trailing whitespace + m.update(new_hand_id) + return(True) + + print "system = %s" % (os.name) + + config = Configuration.Config() + db_connection = Database.Database(config, 'PTrackSv2', 'razz') + + main_window = gtk.Window() + main_window.set_keep_above(True) + main_window.connect("destroy", destroy) + + m = Mucked(main_window, db_connection) + main_window.show_all() + + s_id = gobject.io_add_watch(sys.stdin, gobject.IO_IN, process_new_hand) + + gtk.main() + diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py new file mode 100644 index 00000000..ffd02ea6 --- /dev/null +++ b/pyfpdb/SQL.py @@ -0,0 +1,262 @@ +#!/usr/bin/env python +"""SQL.py + +Set up all of the SQL statements for a given game and database type. +""" +# Copyright 2008, 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 + +# pyGTK modules + +# FreePokerTools modules + +class Sql: + + def __init__(self, game = 'holdem', type = 'PT3'): + self.query = {} + + if game == 'razz' and type == 'ptracks': + + self.query['get_table_name'] = "select table_name from game where game_id = %s" + + self.query['get_last_hand'] = "select max(game_id) from game" + + self.query['get_recent_hands'] = "select game_id from game where game_id > %(last_hand)d" + + self.query['get_xml'] = "select xml from hand_history where game_id = %s" + + self.query['get_player_id'] = """ + select player_id from players + where screen_name = %(player)s + """ + + self.query['get_hand_info'] = """ + SELECT + game_id, + CONCAT(hole_card_1, hole_card_2, hole_card_3, hole_card_4, hole_card_5, hole_card_6, hole_card_7) AS hand, + total_won-total_bet AS net + FROM game_players + WHERE game_id = %s AND player_id = 3 + """ + + self.query['get_cards'] = """ + select + seat_number, + screen_name, + hole_card_1, + hole_card_2, + hole_card_3, + hole_card_4, + hole_card_5, + hole_card_6, + hole_card_7 + from game_players, players + where game_id = %s and game_players.player_id = players.player_id + order by seat_number + """ + + self.query['get_stats_from_hand'] = """ + SELECT player_id, + count(*) AS n, + sum(pre_fourth_raise_n) AS pfr, + sum(fourth_raise_n) AS raise_n_2, + sum(fourth_ck_raise_n) AS cr_n_2, + sum(fifth_bet_raise_n) AS br_n_3, + sum(fifth_bet_ck_raise_n) AS cr_n_3, + sum(sixth_bet_raise_n) AS br_n_4, + sum(sixth_bet_ck_raise_n) AS cr_n_4, + sum(river_bet_raise_n) AS br_n_5, + sum(river_bet_ck_raise_n) AS cr_n_5, + sum(went_to_showdown_n) AS sd, + sum(saw_fourth_n) AS saw_f, + sum(raised_first_pf) AS first_pfr, + sum(vol_put_money_in_pot) AS vpip, + sum(limp_with_prev_callers) AS limp_w_callers, + + sum(ppossible_actions) AS poss_a_pf, + sum(pfold) AS fold_pf, + sum(pcheck) AS check_pf, + sum(praise) AS raise_pf, + sum(pcall) AS raise_pf, + sum(limp_call_reraise_pf) AS limp_call_pf, + + sum(pfr_check) AS check_after_raise, + sum(pfr_call) AS call_after_raise, + sum(pfr_fold) AS fold_after_raise, + sum(pfr_bet) AS bet_after_raise, + sum(pfr_raise) AS raise_after_raise, + sum(folded_to_river_bet) AS fold_to_r_bet, + + sum(fpossible_actions) AS poss_a_2, + sum(ffold) AS fold_2, + sum(fcheck) AS check_2, + sum(fbet) AS bet_2, + sum(fraise) AS raise_2, + sum(fcall) AS raise_2, + + sum(fifpossible_actions) AS poss_a_3, + sum(fiffold) AS fold_3, + sum(fifcheck) AS check_3, + sum(fifbet) AS bet_3, + sum(fifraise) AS raise_3, + sum(fifcall) AS call_3, + + sum(spossible_actions) AS poss_a_4, + sum(sfold) AS fold_4, + sum(scheck) AS check_4, + sum(sbet) AS bet_4, + sum(sraise) AS raise_4, + sum(scall) AS call_4, + + sum(rpossible_actions) AS poss_a_5, + sum(rfold) AS fold_5, + sum(rcheck) AS check_5, + sum(rbet) AS bet_5, + sum(rraise) AS raise_5, + sum(rcall) AS call_5, + + sum(cold_call_pf) AS cc_pf, + sum(saw_fifth_n) AS saw_3, + sum(saw_sixth_n) AS saw_4, + sum(saw_river_n) AS saw_5 + FROM game_players + WHERE player_id in + (SELECT player_id FROM game_players + WHERE game_id = %s AND NOT player_id = %s) + GROUP BY player_id + """ +# alternate form of WHERE for above +# WHERE game_id = %(hand)d AND NOT player_id = %(hero)d) +# WHERE game_id = %s AND NOT player_id = %s) + + self.query['get_players_from_hand'] = """ + SELECT game_players.player_id, seat_number, screen_name + FROM game_players INNER JOIN players ON (game_players.player_id = players.player_id) + WHERE game_id = %s + """ + + if game == 'holdem' and type == 'PT3': + + self.query['get_last_hand'] = "select max(id_hand) from holdem_hand_summary" + + self.query['get_player_id'] = """ + select id_player from player, lookup_sites + where player_name = %(player)s + and site_name = %(site)s + and player.id_site = lookup_sites.id_site + """ + + self.query['get_stats_from_hand'] = """ + select id_player AS player_id, + count(*) AS n, + sum(CAST (flg_vpip AS integer)) as vpip, + sum(CAST (flg_p_first_raise AS integer)) as p_first_raise, + sum(CAST (flg_f_saw AS integer)) as f_saw, + sum(CAST (flg_p_open AS integer)) as p_open, + sum(CAST (flg_p_limp AS integer)) as p_limp, + sum(CAST (flg_p_fold AS integer)) as p_fold, + sum(CAST (flg_p_ccall AS integer)) as p_ccall, + sum(CAST (flg_f_bet AS integer)) as f_bet, + sum(CAST (flg_f_first_raise AS integer)) as f_first_raise, + sum(CAST (flg_f_check AS integer)) as f_check, + sum(CAST (flg_f_check_raise AS integer)) as f_check_raise, + sum(CAST (flg_f_fold AS integer)) as f_fold, + sum(CAST (flg_f_saw AS integer)) as f_saw, + sum(CAST (flg_t_bet AS integer)) as t_bet, + sum(CAST (flg_t_first_raise AS integer)) as t_first_raise, + sum(CAST (flg_t_check AS integer)) as t_check, + sum(CAST (flg_t_check_raise AS integer)) as t_check_raise, + sum(CAST (flg_t_fold AS integer)) as t_fold, + sum(CAST (flg_t_saw AS integer)) as t_saw, + sum(CAST (flg_r_bet AS integer)) as r_bet, + sum(CAST (flg_r_first_raise AS integer)) as r_first_raise, + sum(CAST (flg_r_check AS integer)) as r_check, + sum(CAST (flg_r_check_raise AS integer)) as r_check_raise, + sum(CAST (flg_r_fold AS integer)) as r_fold, + sum(CAST (flg_r_saw AS integer)) as r_saw, + sum(CAST (flg_sb_steal_fold AS integer)) as sb_steal_fold, + sum(CAST (flg_bb_steal_fold AS integer)) as bb_steal_fold, + sum(CAST (flg_blind_def_opp AS integer)) as blind_def_opp, + sum(CAST (flg_steal_att AS integer)) as steal_att, + sum(CAST (flg_steal_opp AS integer)) as steal_opp, + sum(CAST (flg_blind_k AS integer)) as blind_k, + sum(CAST (flg_showdown AS integer)) as showdown, + + sum(CAST (flg_p_squeeze AS integer)) as p_squeeze, + sum(CAST (flg_p_squeeze_opp AS integer)) as p_squeeze_opp, + sum(CAST (flg_p_squeeze_def_opp AS integer)) as p_squeeze_def_opp, + + sum(CAST (flg_f_cbet AS integer)) as f_cbet, + sum(CAST (flg_f_cbet_opp AS integer)) as f_cbet_opp, + sum(CAST (flg_f_cbet_def_opp AS integer)) as f_cbet_def_opp + + from holdem_hand_player_statistics + where id_hand = %(hand)d and not id_player = %(hero)d + group by id_player + """ + + if game == 'holdem' and type == 'fpdb': + + self.query['get_last_hand'] = "select max(id) from Hands" + + self.query['get_player_id'] = """ + select Players.id AS player_id from Players, Sites + where Players.name = %(player)s + and Sites.name = %(site)s + and Players.SiteId = Sites.id + """ + + self.query['get_stats_from_hand'] = """ + SELECT HudCache.playerId AS player_id, + sum(HDs) AS n, + sum(street0Aggr) AS pfr, + sum(street0VPI) AS vpip, + sum(sawShowdown) AS sd, + sum(wonAtSD) AS wmsd, + sum(street1Seen) AS saw_f, + sum(totalProfit) AS net + FROM HudCache, Hands + WHERE HudCache.PlayerId in + (SELECT PlayerId FROM HandsPlayers + WHERE handId = %s) + AND Hands.id = %s + AND Hands.gametypeId = HudCache.gametypeId + GROUP BY HudCache.PlayerId + """ + + self.query['get_players_from_hand'] = """ + SELECT HandsPlayers.playerId, seatNo, name + FROM HandsPlayers INNER JOIN Players ON (HandsPlayers.playerId = Players.id) + WHERE handId = %s + """ + + self.query['get_table_name'] = """ + select tableName, maxSeats, category + from Hands,Gametypes + where Hands.id = %s + and Gametypes.id = Hands.gametypeId + """ + +if __name__== "__main__": +# just print the default queries and exit + s = Sql(game = 'razz', type = 'ptracks') + for key in s.query: + print "For query " + key + ", sql =" + print s.query[key] diff --git a/pyfpdb/Stats.py b/pyfpdb/Stats.py new file mode 100644 index 00000000..edde911d --- /dev/null +++ b/pyfpdb/Stats.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python + +"""Manage collecting and formatting of stats and tooltips. +""" +# Copyright 2008, 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 + +# pyGTK modules +import pygtk +import gtk + +# FreePokerTools modules +import Configuration +import Database + +def do_tip(widget, tip): + widget.set_tooltip_text(tip) + +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: +# 0 - The stat, raw, no formating, eg 0.33333333 +# 1 - formatted stat with appropriate precision and punctuation, eg 33% +# 2 - formatted stat with appropriate precision, punctuation and a hint, eg v=33% +# 3 - same as #2 except name of stat instead of hint, eg vpip=33% +# 4 - the calculation that got the stat, eg 9/27 +# 5 - the name of the stat, useful for a tooltip, eg vpip + +########################################### +# functions that return individual stats +def vpip(stat_dict, player): + stat = 0.0 + try: + stat = float(stat_dict[player]['vpip'])/float(stat_dict[player]['n']) + return (stat, + '%3.1f' % (100*stat) + '%', + 'v=%3.1f' % (100*stat) + '%', + 'vpip=%3.1f' % (100*stat) + '%', + '(%d/%d)' % (stat_dict[player]['vpip'], stat_dict[player]['n']), + 'vpip' + ) + except: return (stat, + '%3.1f' % (0) + '%', + 'w=%3.1f' % (0) + '%', + 'wtsd=%3.1f' % (0) + '%', + '(%d/%d)' % (0, 0), + 'wtsd' + ) + +def pfr(stat_dict, player): + stat = 0.0 + try: + stat = float(stat_dict[player]['pfr'])/float(stat_dict[player]['n']) + return (stat, + '%3.1f' % (100*stat) + '%', + 'p=%3.1f' % (100*stat) + '%', + 'pfr=%3.1f' % (100*stat) + '%', + '(%d/%d)' % (stat_dict[player]['pfr'], stat_dict[player]['n']), + 'pfr' + ) + except: + return (stat, + '%3.1f' % (0) + '%', + 'w=%3.1f' % (0) + '%', + 'wtsd=%3.1f' % (0) + '%', + '(%d/%d)' % (0, 0), + 'wtsd' + ) + +def wtsd(stat_dict, player): + stat = 0.0 + try: + stat = float(stat_dict[player]['sd'])/float(stat_dict[player]['saw_f']) + return (stat, + '%3.1f' % (100*stat) + '%', + 'w=%3.1f' % (100*stat) + '%', + 'wtsd=%3.1f' % (100*stat) + '%', + '(%d/%d)' % (stat_dict[player]['sd'], stat_dict[player]['saw_f']), + 'wtsd' + ) + except: + return (stat, + '%3.1f' % (0) + '%', + 'w=%3.1f' % (0) + '%', + 'wtsd=%3.1f' % (0) + '%', + '(%d/%d)' % (0, 0), + 'wtsd' + ) + + +def saw_f(stat_dict, player): + try: + num = float(stat_dict[player]['saw_f']) + den = float(stat_dict[player]['n']) + stat = num/den + return (stat, + '%3.1f' % (100*stat) + '%', + 'sf=%3.1f' % (100*stat) + '%', + 'saw_f=%3.1f' % (100*stat) + '%', + '(%d/%d)' % (stat_dict[player]['saw_f'], stat_dict[player]['n']), + 'saw_f' + ) + except: + stat = 0.0 + num = 0 + den = 0 + return (stat, + '%3.1f' % (stat) + '%', + 'sf=%3.1f' % (stat) + '%', + 'saw_f=%3.1f' % (stat) + '%', + '(%d/%d)' % (num, den), + 'saw_f' + ) + +def n(stat_dict, player): + try: + return (stat_dict[player]['n'], + '%d' % (stat_dict[player]['n']), + 'n=%d' % (stat_dict[player]['n']), + 'n=%d' % (stat_dict[player]['n']), + '(%d)' % (stat_dict[player]['n']), + '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]), + 'number hands seen' + ) + +def fold_f(stat_dict, player): + stat = 0.0 + try: + stat = stat_dict[player]['fold_2']/stat_dict[player]['saw_f'] + return (stat, + '%3.1f' % (100*stat) + '%', + 'ff=%3.1f' % (100*stat) + '%', + 'fold_f=%3.1f' % (100*stat) + '%', + '(%d/%d)' % (stat_dict[player]['fold_2'], stat_dict[player]['saw_f']), + 'folded fourth' + ) + except: + return (stat, + '%3.1f' % (0) + '%', + 'ff=%3.1f' % (0) + '%', + 'fold_f=%3.1f' % (0) + '%', + '(%d/%d)' % (0, 0), + 'folded fourth' + ) + + +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) + + for player in stat_dict.keys(): + print "player = ", player, do_stat(stat_dict, player = player, stat = 'vpip') + print "player = ", player, do_stat(stat_dict, player = player, stat = 'pfr') + print "player = ", player, do_stat(stat_dict, player = player, stat = 'wtsd') + print "player = ", player, do_stat(stat_dict, player = player, stat = 'saw_f') + print "player = ", player, do_stat(stat_dict, player = player, stat = 'n') + print "player = ", player, do_stat(stat_dict, player = player, stat = 'fold_f') + + db_connection.close + diff --git a/pyfpdb/Tables.py b/pyfpdb/Tables.py new file mode 100644 index 00000000..6c53a590 --- /dev/null +++ b/pyfpdb/Tables.py @@ -0,0 +1,135 @@ +#!/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, 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 re + +# FreePokerTools modules +import Configuration + +class Table_Window: + 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_details(table): + table.game = 'razz' + table.max = 8 + table.struture = 'limit' + table.tournament = 0 + +def discover(c): + tables = {} + for listing in os.popen('xwininfo -root -tree').readlines(): + if re.search('Lobby', listing): continue + if re.search('Instant Hand History', listing): continue + if not re.search('Logged In as ', listing): continue + for s in c.supported_sites.keys(): + if re.search(c.supported_sites[s].table_finder, listing): + mo = re.match('\s+([\dxabcdef]+) (.+):.+ (\d+)x(\d+)\+\d+\+\d+ \+(\d+)\+(\d+)', listing) + if mo.group(2) == '(has no name)': continue + if re.match('[\(\)\d\s]+', mo.group(2)): continue # this is a popup + tw = Table_Window() + tw.site = c.supported_sites[s].site_name + tw.number = mo.group(1) + tw.title = mo.group(2) + tw.width = int( mo.group(3) ) + tw.height = int( mo.group(4) ) + tw.x = int (mo.group(5) ) + tw.y = int (mo.group(6) ) + tw.title = re.sub('\"', '', tw.title) +# this rather ugly hack makes my fake table used for debugging work + if tw.title == "PokerStars.py": continue + +# use this eval thingie to call the title bar decoder specified in the config file + eval("%s(tw)" % c.supported_sites[s].decoder) + tables[tw.name] = tw + return tables + +def pokerstars_decode_table(tw): +# extract the table name OR the tournament number and table name from the title +# other info in title is redundant with data in the database + title_bits = re.split(' - ', tw.title) + name = title_bits[0] + mo = re.search('Tournament (\d+) Table (\d+)', name) + if mo: + tw.tournament = int( mo.group(1) ) + tw.table = int( mo.group(2) ) + tw.name = name + else: + tw.tournament = None + for pattern in [' no all-in', ' fast', ',']: + name = re.sub(pattern, '', name) + name = re.sub('\s+$', '', name) + tw.name = name + + 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 "", line 1, in +# 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) + + mo = re.search('(No Limit|Pot Limit)', tw.title) + if mo: + tw.structure = mo.group(1).lower() + else: + tw.structure = 'limit' + + tw.max = None + if tw.game in ('razz', 'stud', 'stud hi/lo'): + tw.max = 8 + elif tw.game in ('5-card draw', 'triple draw 2-7 lowball'): + tw.max = 6 + elif tw.game == 'holdem': + pass + elif tw.game in ('omaha', 'omaha hi/lo'): + pass + +if __name__=="__main__": + c = Configuration.Config() + tables = discover(c) + + for t in tables.keys(): + print tables[t] \ No newline at end of file diff --git a/pyfpdb/fpdb_import.py b/pyfpdb/fpdb_import.py index 38f2cf04..6bee055c 100755 --- a/pyfpdb/fpdb_import.py +++ b/pyfpdb/fpdb_import.py @@ -102,12 +102,12 @@ def import_file_dict(options, settings): try: if (category=="razz" or category=="studhi" or category=="studhilo"): raise fpdb_simple.FpdbError ("stud/razz currently out of order") - last_read_hand=fpdb_parse_logic.mainParser(db, cursor, site, category, hand) + handsId=fpdb_parse_logic.mainParser(db, cursor, site, category, hand) db.commit() stored+=1 if settings['imp-callFpdbHud']: - print "call to HUD here" + print "call to HUD here. handsId:",handsId db.commit() except fpdb_simple.DuplicateError: duplicates+=1 @@ -156,13 +156,13 @@ def import_file_dict(options, settings): for line_no in range(len(lines)): if lines[line_no].find("Game #")!=-1: final_game_line=lines[line_no] - last_read_hand=fpdb_simple.parseSiteHandNo(final_game_line) + handsId=fpdb_simple.parseSiteHandNo(final_game_line) #todo: this will cause return of an unstored hand number if the last hadn was error or partial db.commit() inputFile.close() cursor.close() db.close() - return last_read_hand + return handsId if __name__ == "__main__": diff --git a/pyfpdb/fpdb_save_to_db.py b/pyfpdb/fpdb_save_to_db.py index a5016904..a3ec95a5 100644 --- a/pyfpdb/fpdb_save_to_db.py +++ b/pyfpdb/fpdb_save_to_db.py @@ -34,7 +34,7 @@ def ring_stud(cursor, category, site_hand_no, gametype_id, hand_start_time, fpdb_simple.storeHudData(cursor, category, player_ids, hudImportData) fpdb_simple.storeActions(cursor, hands_players_ids, action_types, action_amounts) - return site_hand_no + return hands_id #end def ring_stud def ring_holdem_omaha(cursor, category, site_hand_no, gametype_id, hand_start_time, names, player_ids, start_cashes, positions, card_values, card_suits, board_values, board_suits, winnings, rakes, action_types, action_amounts, actionNos, hudImportData, maxSeats, tableName, seatNos): @@ -51,7 +51,7 @@ def ring_holdem_omaha(cursor, category, site_hand_no, gametype_id, hand_start_ti fpdb_simple.store_board_cards(cursor, hands_id, board_values, board_suits) fpdb_simple.storeActions(cursor, hands_players_ids, action_types, action_amounts, actionNos) - return site_hand_no + return hands_id #end def ring_holdem_omaha def tourney_holdem_omaha(cursor, category, siteTourneyNo, buyin, fee, knockout, entries, prizepool, tourney_start, payin_amounts, ranks, tourneyTypeId, siteId, #end of tourney specific params @@ -72,7 +72,7 @@ def tourney_holdem_omaha(cursor, category, siteTourneyNo, buyin, fee, knockout, fpdb_simple.store_board_cards(cursor, hands_id, board_values, board_suits) fpdb_simple.storeActions(cursor, hands_players_ids, action_types, action_amounts, actionNos) - return site_hand_no + return hands_id #end def tourney_holdem_omaha def tourney_stud(cursor, category, site_tourney_no, buyin, fee, knockout, entries, prizepool, @@ -95,6 +95,5 @@ def tourney_stud(cursor, category, site_tourney_no, buyin, fee, knockout, entrie fpdb_simple.storeHudData(cursor, category, player_ids, hudImportData) fpdb_simple.storeActions(cursor, hands_players_ids, action_types, action_amounts) - return site_hand_no + return hands_id #end def tourney_stud -