pulled and merged from fpdboz

This commit is contained in:
sqlcoder 2008-12-07 23:38:33 +00:00
commit 4dc15bfd94
30 changed files with 7474 additions and 5957 deletions

109
pyfpdb/CarbonToFpdb.py Normal file
View File

@ -0,0 +1,109 @@
#!/usr/bin/env python
# Copyright 2008, Carl Gherardi
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
########################################################################
# Standard Library modules
import Configuration
import traceback
import sys
import re
import xml.dom.minidom
from xml.dom.minidom import Node
from HandHistoryConverter import HandHistoryConverter
# Carbon format looks like:
# 1) <description type="Holdem" stakes="No Limit ($0.25/$0.50)"/>
# 2) <game id="14902583-5578" starttime="20081006145401" numholecards="2" gametype="2" realmoney="true" data="20081006|Niagara Falls (14902583)|14902583|14902583-5578|false">
# 3) <players dealer="8">
# <player seat="3" nickname="PlayerInSeat3" balance="$43.29" dealtin="true" />
# ...
# 4) <round id="BLINDS" sequence="1">
# <event sequence="1" type="SMALL_BLIND" player="0" amount="0.25"/>
# <event sequence="2" type="BIG_BLIND" player="1" amount="0.50"/>
# 5) <round id="PREFLOP" sequence="2">
# <event sequence="3" type="CALL" player="2" amount="0.50"/>
# 6) <round id="POSTFLOP" sequence="3">
# <event sequence="16" type="BET" player="3" amount="1.00"/>
# ....
# <cards type="COMMUNITY" cards="7d,Jd,Jh"/>
# The full sequence for a NHLE cash game is:
# BLINDS, PREFLOP, POSTFLOP, POSTTURN, POSTRIVER, SHOWDOWN, END_OF_GAME
# This sequence can be terminated after BLINDS at any time by END_OF_FOLDED_GAME
class CarbonPoker(HandHistoryConverter):
def __init__(self, config, filename):
print "Initialising Carbon Poker converter class"
HandHistoryConverter.__init__(self, config, filename, "Carbon") # Call super class init
self.setFileType("xml")
def readSupportedGames(self):
pass
def determineGameType(self):
gametype = []
desc_node = self.doc.getElementsByTagName("description")
#TODO: no examples of non ring type yet
gametype = gametype + ["ring"]
type = desc_node[0].getAttribute("type")
if(type == "Holdem"):
gametype = gametype + ["hold"]
else:
print "Unknown gametype: '%s'" % (type)
stakes = desc_node[0].getAttribute("stakes")
#TODO: no examples of anything except nlhe
m = re.match('(?P<LIMIT>No Limit)\s\(\$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+)\)', stakes)
if(m.group('LIMIT') == "No Limit"):
gametype = gametype + ["nl"]
gametype = gametype + [self.float2int(m.group('SB'))]
gametype = gametype + [self.float2int(m.group('BB'))]
return gametype
def readPlayerStacks(self):
pass
def readBlinds(self):
pass
def readAction(self):
pass
# Override read function as xml.minidom barfs on the Carbon layout
# This is pretty dodgy
def readFile(self, filename):
print "Carbon: Reading file: '%s'" %(filename)
infile=open(filename, "rU")
self.obs = infile.read()
infile.close()
self.obs = "<CarbonHHFile>\n" + self.obs + "</CarbonHHFile>"
try:
doc = xml.dom.minidom.parseString(self.obs)
self.doc = doc
except:
traceback.print_exc(file=sys.stderr)
if __name__ == "__main__":
c = Configuration.Config()
e = CarbonPoker(c, "regression-test-files/carbon-poker/Niagara Falls (15245216).xml")
e.processFile()
print str(e)

View File

@ -53,7 +53,7 @@ if __name__ == "__main__":
(options, sys.argv) = parser.parse_args()
settings={'imp-callFpdbHud':False, 'db-backend':2}
settings={'callFpdbHud':False, 'db-backend':2}
settings['db-host']=options.server
settings['db-user']=options.user
settings['db-password']=options.password

View File

@ -114,6 +114,7 @@ class Game:
stat.popup = stat_node.getAttribute("popup")
stat.hudprefix = stat_node.getAttribute("hudprefix")
stat.hudsuffix = stat_node.getAttribute("hudsuffix")
stat.hudcolor = stat_node.getAttribute("hudcolor")
self.stats[stat.stat_name] = stat
@ -443,9 +444,9 @@ class Config:
def get_import_parameters(self):
imp = {}
try:
imp['callFpdbHud'] = self.callFpdbHud
imp['interval'] = self.interval
imp['hhArchiveBase'] = self.hhArchiveBase
imp['callFpdbHud'] = self.imp.callFpdbHud
imp['interval'] = self.imp.interval
imp['hhArchiveBase'] = self.imp.hhArchiveBase
except: # Default params
imp['callFpdbHud'] = True
imp['interval'] = 10
@ -612,9 +613,7 @@ if __name__== "__main__":
print "----------- END POPUP WINDOW FORMATS -----------"
print "\n----------- IMPORT -----------"
tmp = c.get_import_parameters()
for param in tmp:
print " " + str(param) + ": " + str(tmp[param])
print c.imp
print "----------- END IMPORT -----------"
print "\n----------- TABLE VIEW -----------"

View File

@ -142,12 +142,27 @@ class Database:
cards[s_dict['seat_number']] = s_dict
return (cards)
def get_stats_from_hand(self, hand, player_id = False):
def get_action_from_hand(self, hand_no):
action = [ [], [], [], [], [] ]
c = self.connection.cursor()
c.execute(self.sql.query['get_action_from_hand'], (hand_no))
for row in c.fetchall():
street = row[0]
act = row[1:]
action[street].append(act)
return action
def get_stats_from_hand(self, hand, aggregate = False):
c = self.connection.cursor()
if not player_id: player_id = "%"
if aggregate:
query = 'get_stats_from_hand'
subs = (hand, hand)
else:
query = 'get_stats_from_hand_aggregated'
subs = (hand, hand, hand)
# get the players in the hand and their seats
# c.execute(self.sql.query['get_players_from_hand'], (hand, player_id))
c.execute(self.sql.query['get_players_from_hand'], (hand, ))
names = {}
seats = {}
@ -156,8 +171,7 @@ class Database:
seats[row[0]] = row[1]
# now get the stats
# c.execute(self.sql.query['get_stats_from_hand'], (hand, hand, player_id))
c.execute(self.sql.query['get_stats_from_hand'], (hand, hand))
c.execute(self.sql.query[query], subs)
colnames = [desc[0] for desc in c.description]
stat_dict = {}
for row in c.fetchall():

176
pyfpdb/EverleafToFpdb.py Executable file
View File

@ -0,0 +1,176 @@
#!/usr/bin/env python
# Copyright 2008, Carl Gherardi
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
########################################################################
import sys
import Configuration
from HandHistoryConverter import *
# Everleaf HH format
# Everleaf Gaming Game #55208539
# ***** Hand history for game #55208539 *****
# Blinds $0.50/$1 NL Hold'em - 2008/09/01 - 13:35:01
# Table Speed Kuala
# Seat 1 is the button
# Total number of players: 9
# Seat 1: BadBeatBox ( $ 98.97 USD )
# Seat 3: EricBlade ( $ 73.96 USD )
# Seat 4: randy888 ( $ 196.50 USD )
# Seat 5: BaronSengir ( $ 182.80 USD )
# Seat 6: dogge ( $ 186.06 USD )
# Seat 7: wings ( $ 50 USD )
# Seat 8: schoffeltje ( $ 282.05 USD )
# Seat 9: harrydebeng ( $ 109.45 USD )
# Seat 10: smaragdar ( $ 96.50 USD )
# EricBlade: posts small blind [$ 0.50 USD]
# randy888: posts big blind [$ 1 USD]
# wings: posts big blind [$ 1 USD]
# ** Dealing down cards **
# Dealt to EricBlade [ qc, 3c ]
# BaronSengir folds
# dogge folds
# wings raises [$ 2.50 USD]
# schoffeltje folds
# harrydebeng calls [$ 3.50 USD]
# smaragdar raises [$ 15.50 USD]
# BadBeatBox folds
# EricBlade folds
# randy888 folds
# wings calls [$ 12 USD]
# harrydebeng folds
# ** Dealing Flop ** [ qs, 3d, 8h ]
# wings: bets [$ 34.50 USD]
# smaragdar calls [$ 34.50 USD]
# ** Dealing Turn ** [ 2d ]
# ** Dealing River ** [ 6c ]
# dogge shows [ 9h, 9c ]a pair of nines
# spicybum shows [ 5d, 6d ]a straight, eight high
# harrydebeng does not show cards
# smaragdar wins $ 102 USD from main pot with a pair of aces [ ad, ah, qs, 8h, 6c ]
class Everleaf(HandHistoryConverter):
def __init__(self, config, file):
print "Initialising Everleaf converter class"
HandHistoryConverter.__init__(self, config, file, "Everleaf") # Call super class init.
self.sitename = "Everleaf"
self.setFileType("text")
self.rexx.setGameInfoRegex('.*Blinds \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+)')
self.rexx.setSplitHandRegex('\n\n\n\n')
self.rexx.setHandInfoRegex('.*#(?P<HID>[0-9]+)\n.*\nBlinds \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) (?P<GAMETYPE>.*) - (?P<YEAR>[0-9]+)/(?P<MON>[0-9]+)/(?P<DAY>[0-9]+) - (?P<HR>[0-9]+):(?P<MIN>[0-9]+):(?P<SEC>[0-9]+)\nTable (?P<TABLE>[ a-zA-Z]+)\nSeat (?P<BUTTON>[0-9]+)')
self.rexx.setPlayerInfoRegex('Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \( \$ (?P<CASH>[.0-9]+) USD \)')
self.rexx.setPostSbRegex('.*\n(?P<PNAME>.*): posts small blind \[\$? (?P<SB>[.0-9]+)')
self.rexx.setPostBbRegex('.*\n(?P<PNAME>.*): posts big blind \[\$? (?P<BB>[.0-9]+)')
# mct : what about posting small & big blinds simultaneously?
self.rexx.setHeroCardsRegex('.*\nDealt\sto\s(?P<PNAME>.*)\s\[ (?P<HOLE1>\S\S), (?P<HOLE2>\S\S) \]')
self.rexx.setActionStepRegex('.*\n(?P<PNAME>.*) (?P<ATYPE>bets|checks|raises|calls|folds)(\s\[\$ (?P<BET>[.\d]+) USD\])?')
self.rexx.compileRegexes()
def readSupportedGames(self):
pass
def determineGameType(self):
# Cheating with this regex, only support nlhe at the moment
gametype = ["ring", "hold", "nl"]
m = self.rexx.game_info_re.search(self.obs)
gametype = gametype + [m.group('SB')]
gametype = gametype + [m.group('BB')]
return gametype
def readHandInfo(self, hand):
m = self.rexx.hand_info_re.search(hand.string)
hand.handid = m.group('HID')
hand.tablename = m.group('TABLE')
# These work, but the info is already in the Hand class - should be used for tourneys though.
# m.group('SB')
# m.group('BB')
# m.group('GAMETYPE')
# Believe Everleaf time is GMT/UTC, no transation necessary
# Stars format (Nov 10 2008): 2008/11/07 12:38:49 CET [2008/11/07 7:38:49 ET]
# or : 2008/11/07 12:38:49 ET
# Not getting it in my HH files yet, so using
# 2008/11/10 3:58:52 ET
#TODO: Do conversion from GMT to ET
#TODO: Need some date functions to convert to different timezones (Date::Manip for perl rocked for this)
hand.starttime = "%d/%02d/%02d %d:%02d:%02d ET" %(int(m.group('YEAR')), int(m.group('MON')), int(m.group('DAY')),
int(m.group('HR')), int(m.group('MIN')), int(m.group('SEC')))
hand.buttonpos = int(m.group('BUTTON'))
def readPlayerStacks(self, hand):
m = self.rexx.player_info_re.finditer(hand.string)
players = []
for a in m:
hand.addPlayer(a.group('SEAT'), a.group('PNAME'), a.group('CASH'))
def markStreets(self, hand):
# PREFLOP = ** Dealing down cards **
m = re.search('(\*\* Dealing down cards \*\*\n)(?P<PREFLOP>.*?\n\*\*)?( Dealing Flop \*\* \[ (?P<FLOP1>\S\S), (?P<FLOP2>\S\S), (?P<FLOP3>\S\S) \])?(?P<FLOP>.*?\*\*)?( Dealing Turn \*\* \[ (?P<TURN1>\S\S) \])?(?P<TURN>.*?\*\*)?( Dealing River \*\* \[ (?P<RIVER1>\S\S) \])?(?P<RIVER>.*)', hand.string,re.DOTALL)
# for street in m.groupdict():
# print "DEBUG: Street: %s\tspan: %s" %(street, str(m.span(street)))
hand.streets = m
def readBlinds(self, hand):
try:
m = self.rexx.small_blind_re.search(hand.string)
hand.addBlind(m.group('PNAME'), m.group('SB'))
#hand.posted = [m.group('PNAME')]
except:
hand.addBlind(None, 0)
#hand.posted = ["FpdbNBP"]
m = self.rexx.big_blind_re.finditer(hand.string)
for a in m:
hand.addBlind(a.group('PNAME'), a.group('BB'))
#hand.posted = hand.posted + [a.group('PNAME')]
def readHeroCards(self, hand):
m = self.rexx.hero_cards_re.search(hand.string)
if(m == None):
#Not involved in hand
hand.involved = False
else:
hand.hero = m.group('PNAME')
hand.addHoleCards(m.group('HOLE1'), m.group('HOLE2'))
def readAction(self, hand, street):
m = self.rexx.action_re.finditer(hand.streets.group(street))
hand.actions[street] = []
for action in m:
if action.group('ATYPE') == 'raises':
hand.addRaiseTo( street, action.group('PNAME'), action.group('BET') )
elif action.group('ATYPE') == 'calls':
hand.addCall( street, action.group('PNAME'), action.group('BET') )
elif action.group('ATYPE') == 'bets':
hand.addBet( street, action.group('PNAME'), action.group('BET') )
else:
print "DEBUG: unimplemented readAction: %s %s" %(action.group('PNAME'),action.group('ATYPE'),)
hand.actions[street] += [[action.group('PNAME'), action.group('ATYPE')]]
def getRake(self, hand):
hand.rake = hand.totalpot * Decimal('0.05') # probably not quite right
if __name__ == "__main__":
c = Configuration.Config()
e = Everleaf(c, "Speed_Kuala.txt")
e.processFile()
print str(e)

161
pyfpdb/FpdbRegex.py Normal file
View File

@ -0,0 +1,161 @@
# pokerstars_cash.py
# -*- coding: iso-8859-15
#
# PokerStats, an online poker statistics tracking software for Linux
# Copyright (C) 2007-2008 Mika Boström <bostik@iki.fi>
#
# 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, version 3 of the License.
#
# Modified for use in fpdb by Carl Gherardi
import re
# These are PokerStars specific;
# More importantly, they are currently valid for cash game only.
#####
# XXX: There was a weird problem with saved hand histories in PokerStars
# client 2.491; if a user was present on the table (and thus anywhere in
# the hand history), with non-standard characters in their username, the
# client would prepend a literal Ctrl-P (ASCII 16, 0x10) character to
# the hand history title line. Hence, to allow these strangely saved
# hands to be parsed and imported, there is a conditional "one extra
# character" allowed at the start of the new hand regex.
class FpdbRegex:
def __init__(self):
self.__GAME_INFO_REGEX=''
self.__SPLIT_HAND_REGEX='\n\n\n'
self.__NEW_HAND_REGEX='^.?PokerStars Game #\d+:\s+Hold\'em'
self.__HAND_INFO_REGEX='^.*#(\d+):\s+(\S+)\s([\s\S]+)\s\(\$?([.0-9]+)/\$?([.0-9]+)\)\s-\s(\S+)\s-?\s?(\S+)\s\(?(\w+)\)?'
self.__TABLE_INFO_REGEX='^\S+\s+\'.*\'\s+(\d+)-max\s+Seat\s#(\d+)'
self.__PLAYER_INFO_REGEX='^Seat\s(\d+):\s(.*)\s\(\$?([.\d]+)\s'
self.__POST_SB_REGEX='^(.*):\sposts small blind'
self.__POST_BB_REGEX='^(.*):\sposts big blind'
self.__POST_BOTH_REGEX='^(.*):\sposts small & big blinds'
self.__HAND_STAGE_REGEX='^\*{3}\s(.*)\s\*{3}'
self.__HOLE_CARD_REGEX='^\*{3}\sHOLE CARDS'
self.__FLOP_CARD_REGEX='^\*{3}\sFLOP\s\*{3}\s\[(\S{2})\s(\S{2})\s(\S{2})\]'
self.__TURN_CARD_REGEX='^\*{3}\sTURN\s\*{3}\s\[\S{2}\s\S{2}\s\S{2}\]\s\[(\S{2})\]'
self.__RIVER_CARD_REGEX='^\*{3}\sRIVER\s\*{3}\s\[\S{2}\s\S{2}\s\S{2}\s\S{2}\]\s\[(\S{2})\]'
self.__SHOWDOWN_REGEX='^\*{3}\sSHOW DOWN'
self.__SUMMARY_REGEX='^\*{3}\sSUMMARY'
self.__UNCALLED_BET_REGEX='^Uncalled bet \(\$([.\d]+)\) returned to (.*)'
self.__POT_AND_RAKE_REGEX='^Total\spot\s\$([.\d]+).*\|\sRake\s\$([.\d]+)'
self.__COLLECT_POT_REGEX='^(.*)\scollected\s\$([.\d]+)\sfrom\s((main|side)\s)?pot'
self.__HERO_CARDS_REGEX='^Dealt\sto\s(.*)\s\[(\S{2})\s(\S{2})\]'
self.__SHOWN_CARDS_REGEX='^(.*):\sshows\s\[(\S{2})\s(\S{2})\]'
self.__ACTION_STEP_REGEX='^(.*):\s(bets|checks|raises|calls|folds)((\s\$([.\d]+))?(\sto\s\$([.\d]+))?)?'
self.__SHOWDOWN_ACTION_REGEX='^(.*):\s(shows|mucks)'
self.__SUMMARY_CARDS_REGEX='^Seat\s\d+:\s(.*)\s(showed|mucked)\s\[(\S{2})\s(\S{2})\]'
self.__SUMMARY_CARDS_EXTRA_REGEX='^Seat\s\d+:\s(.*)\s(\(.*\)\s)(showed|mucked)\s\[(\S{2})\s(\S{2})\]'
def compileRegexes(self):
### Compile the regexes
self.game_info_re = re.compile(self.__GAME_INFO_REGEX)
self.split_hand_re = re.compile(self.__SPLIT_HAND_REGEX)
self.hand_start_re = re.compile(self.__NEW_HAND_REGEX)
self.hand_info_re = re.compile(self.__HAND_INFO_REGEX)
self.table_info_re = re.compile(self.__TABLE_INFO_REGEX)
self.player_info_re = re.compile(self.__PLAYER_INFO_REGEX)
self.small_blind_re = re.compile(self.__POST_SB_REGEX)
self.big_blind_re = re.compile(self.__POST_BB_REGEX)
self.both_blinds_re = re.compile(self.__POST_BOTH_REGEX)
self.hand_stage_re = re.compile(self.__HAND_STAGE_REGEX)
self.hole_cards_re = re.compile(self.__HOLE_CARD_REGEX)
self.flop_cards_re = re.compile(self.__FLOP_CARD_REGEX)
self.turn_card_re = re.compile(self.__TURN_CARD_REGEX)
self.river_card_re = re.compile(self.__RIVER_CARD_REGEX)
self.showdown_re = re.compile(self.__SHOWDOWN_REGEX)
self.summary_re = re.compile(self.__SUMMARY_REGEX)
self.uncalled_bet_re = re.compile(self.__UNCALLED_BET_REGEX)
self.collect_pot_re = re.compile(self.__COLLECT_POT_REGEX)
self.hero_cards_re = re.compile(self.__HERO_CARDS_REGEX)
self.cards_shown_re = re.compile(self.__SHOWN_CARDS_REGEX)
self.summary_cards_re = re.compile(self.__SUMMARY_CARDS_REGEX)
self.summary_cards_extra_re = re.compile(self.__SUMMARY_CARDS_EXTRA_REGEX)
self.action_re = re.compile(self.__ACTION_STEP_REGEX)
self.rake_re = re.compile(self.__POT_AND_RAKE_REGEX)
self.showdown_action_re = re.compile(self.__SHOWDOWN_ACTION_REGEX)
# Set methods for plugins to override
def setGameInfoRegex(self, string):
self.__GAME_INFO_REGEX = string
def setSplitHandRegex(self, string):
self.__SPLIT_HAND_REGEX = string
def setNewHandRegex(self, string):
self.__NEW_HAND_REGEX = string
def setHandInfoRegex(self, string):
self.__HAND_INFO_REGEX = string
def setTableInfoRegex(self, string):
self.__TABLE_INFO_REGEX = string
def setPlayerInfoRegex(self, string):
self.__PLAYER_INFO_REGEX = string
def setPostSbRegex(self, string):
self.__POST_SB_REGEX = string
def setPostBbRegex(self, string):
self.__POST_BB_REGEX = string
def setPostBothRegex(self, string):
self.__POST_BOTH_REGEX = string
def setHandStageRegex(self, string):
self.__HAND_STAGE_REGEX = string
def setHoleCardRegex(self, string):
self.__HOLE_CARD_REGEX = string
def setFlopCardRegex(self, string):
self.__FLOP_CARD_REGEX = string
def setTurnCardRegex(self, string):
self.__TURN_CARD_REGEX = string
def setRiverCardRegex(self, string):
self.__RIVER_CARD_REGEX = string
def setShowdownRegex(self, string):
self.__SHOWDOWN_REGEX = string
def setSummaryRegex(self, string):
self.__SUMMARY_REGEX = string
def setUncalledBetRegex(self, string):
self.__UNCALLED_BET_REGEX = string
def setCollectPotRegex(self, string):
self.__COLLECT_POT_REGEX = string
def setHeroCardsRegex(self, string):
self.__HERO_CARDS_REGEX = string
def setShownCardsRegex(self, string):
self.__SHOWN_CARDS_REGEX = string
def setSummaryCardsRegex(self, string):
self.__SUMMARY_CARDS_REGEX = string
def setSummaryCardsExtraRegex(self, string):
self.__SUMMARY_CARDS_EXTRA_REGEX = string
def setActionStepRegex(self, string):
self.__ACTION_STEP_REGEX = string
def setPotAndRakeRegex(self, string):
self.__POT_AND_RAKE_REGEX = string
def setShowdownActionRegex(self, string):
self.__SHOWDOWN_ACTION_REGEX = string

View File

@ -73,13 +73,13 @@ class FpdbSQLQueries:
if(self.dbname == 'MySQL InnoDB'):
self.query['createSitesTable'] = """CREATE TABLE Sites (
id SMALLINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
id SMALLINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
name varchar(32) NOT NULL,
currency char(3) NOT NULL)
ENGINE=INNODB"""
elif(self.dbname == 'PostgreSQL'):
self.query['createSitesTable'] = """CREATE TABLE Sites (
id SERIAL, PRIMARY KEY (id),
id SERIAL UNIQUE, PRIMARY KEY (id),
name varchar(32),
currency char(3))"""
elif(self.dbname == 'SQLite'):
@ -92,7 +92,7 @@ class FpdbSQLQueries:
if(self.dbname == 'MySQL InnoDB'):
self.query['createGametypesTable'] = """CREATE TABLE Gametypes (
id SMALLINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
id SMALLINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
siteId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (siteId) REFERENCES Sites(id),
type char(4) NOT NULL,
base char(4) NOT NULL,
@ -106,7 +106,7 @@ class FpdbSQLQueries:
ENGINE=INNODB"""
elif(self.dbname == 'PostgreSQL'):
self.query['createGametypesTable'] = """CREATE TABLE Gametypes (
id SERIAL, PRIMARY KEY (id),
id SERIAL UNIQUE, PRIMARY KEY (id),
siteId INTEGER, FOREIGN KEY (siteId) REFERENCES Sites(id),
type char(4),
base char(4),
@ -127,7 +127,7 @@ class FpdbSQLQueries:
if(self.dbname == 'MySQL InnoDB'):
self.query['createPlayersTable'] = """CREATE TABLE Players (
id INT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
id INT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
name VARCHAR(32) CHARACTER SET utf8 NOT NULL,
siteId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (siteId) REFERENCES Sites(id),
comment text,
@ -135,7 +135,7 @@ class FpdbSQLQueries:
ENGINE=INNODB"""
elif(self.dbname == 'PostgreSQL'):
self.query['createPlayersTable'] = """CREATE TABLE Players (
id SERIAL, PRIMARY KEY (id),
id SERIAL UNIQUE, PRIMARY KEY (id),
name VARCHAR(32),
siteId INTEGER, FOREIGN KEY (siteId) REFERENCES Sites(id),
comment text,
@ -150,7 +150,7 @@ class FpdbSQLQueries:
if(self.dbname == 'MySQL InnoDB'):
self.query['createAutoratesTable'] = """CREATE TABLE Autorates (
id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
id BIGINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
playerId INT UNSIGNED NOT NULL, FOREIGN KEY (playerId) REFERENCES Players(id),
gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
description varchar(50) NOT NULL,
@ -160,7 +160,7 @@ class FpdbSQLQueries:
ENGINE=INNODB"""
elif(self.dbname == 'PostgreSQL'):
self.query['createAutoratesTable'] = """CREATE TABLE Autorates (
id BIGSERIAL, PRIMARY KEY (id),
id BIGSERIAL UNIQUE, PRIMARY KEY (id),
playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id),
gametypeId INT, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
description varchar(50),
@ -177,7 +177,7 @@ class FpdbSQLQueries:
if(self.dbname == 'MySQL InnoDB'):
self.query['createHandsTable'] = """CREATE TABLE Hands (
id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
id BIGINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
tableName VARCHAR(20) NOT NULL,
siteHandNo BIGINT NOT NULL,
gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
@ -190,7 +190,7 @@ class FpdbSQLQueries:
ENGINE=INNODB"""
elif(self.dbname == 'PostgreSQL'):
self.query['createHandsTable'] = """CREATE TABLE Hands (
id BIGSERIAL, PRIMARY KEY (id),
id BIGSERIAL UNIQUE, PRIMARY KEY (id),
tableName VARCHAR(20),
siteHandNo BIGINT,
gametypeId INT, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
@ -210,7 +210,7 @@ class FpdbSQLQueries:
if(self.dbname == 'MySQL InnoDB'):
self.query['createBoardCardsTable'] = """CREATE TABLE BoardCards (
id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
id BIGINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
handId BIGINT UNSIGNED NOT NULL, FOREIGN KEY (handId) REFERENCES Hands(id),
card1Value smallint NOT NULL,
card1Suit char(1) NOT NULL,
@ -225,7 +225,7 @@ class FpdbSQLQueries:
ENGINE=INNODB"""
elif(self.dbname == 'PostgreSQL'):
self.query['createBoardCardsTable'] = """CREATE TABLE BoardCards (
id BIGSERIAL, PRIMARY KEY (id),
id BIGSERIAL UNIQUE, PRIMARY KEY (id),
handId BIGINT, FOREIGN KEY (handId) REFERENCES Hands(id),
card1Value smallint,
card1Suit char(1),
@ -247,7 +247,7 @@ class FpdbSQLQueries:
if(self.dbname == 'MySQL InnoDB'):
self.query['createTourneyTypesTable'] = """CREATE TABLE TourneyTypes (
id SMALLINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
id SMALLINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
siteId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (siteId) REFERENCES Sites(id),
buyin INT NOT NULL,
fee INT NOT NULL,
@ -272,7 +272,7 @@ class FpdbSQLQueries:
if(self.dbname == 'MySQL InnoDB'):
self.query['createTourneysTable'] = """CREATE TABLE Tourneys (
id INT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
id INT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
tourneyTypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (tourneyTypeId) REFERENCES TourneyTypes(id),
siteTourneyNo BIGINT NOT NULL,
entries INT NOT NULL,
@ -283,7 +283,7 @@ class FpdbSQLQueries:
ENGINE=INNODB"""
elif(self.dbname == 'PostgreSQL'):
self.query['createTourneysTable'] = """CREATE TABLE Tourneys (
id SERIAL, PRIMARY KEY (id),
id SERIAL UNIQUE, PRIMARY KEY (id),
tourneyTypeId INT, FOREIGN KEY (tourneyTypeId) REFERENCES TourneyTypes(id),
siteTourneyNo BIGINT,
entries INT,
@ -300,7 +300,7 @@ class FpdbSQLQueries:
if(self.dbname == 'MySQL InnoDB'):
self.query['createHandsPlayersTable'] = """CREATE TABLE HandsPlayers (
id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
id BIGINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
handId BIGINT UNSIGNED NOT NULL, FOREIGN KEY (handId) REFERENCES Hands(id),
playerId INT UNSIGNED NOT NULL, FOREIGN KEY (playerId) REFERENCES Players(id),
startCash INT NOT NULL,
@ -332,7 +332,7 @@ class FpdbSQLQueries:
ENGINE=INNODB"""
elif(self.dbname == 'PostgreSQL'):
self.query['createHandsPlayersTable'] = """CREATE TABLE HandsPlayers (
id BIGSERIAL, PRIMARY KEY (id),
id BIGSERIAL UNIQUE, PRIMARY KEY (id),
handId BIGINT, FOREIGN KEY (handId) REFERENCES Hands(id),
playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id),
startCash INT,
@ -370,7 +370,7 @@ class FpdbSQLQueries:
if(self.dbname == 'MySQL InnoDB'):
self.query['createTourneysPlayersTable'] = """CREATE TABLE TourneysPlayers (
id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
id BIGINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
tourneyId INT UNSIGNED NOT NULL, FOREIGN KEY (tourneyId) REFERENCES Tourneys(id),
playerId INT UNSIGNED NOT NULL, FOREIGN KEY (playerId) REFERENCES Players(id),
payinAmount INT NOT NULL,
@ -381,7 +381,7 @@ class FpdbSQLQueries:
ENGINE=INNODB"""
elif(self.dbname == 'PostgreSQL'):
self.query['createTourneysPlayersTable'] = """CREATE TABLE TourneysPlayers (
id BIGSERIAL, PRIMARY KEY (id),
id BIGSERIAL UNIQUE, PRIMARY KEY (id),
tourneyId INT, FOREIGN KEY (tourneyId) REFERENCES Tourneys(id),
playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id),
payinAmount INT,
@ -399,7 +399,7 @@ class FpdbSQLQueries:
if(self.dbname == 'MySQL InnoDB'):
self.query['createHandsActionsTable'] = """CREATE TABLE HandsActions (
id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
id BIGINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
handPlayerId BIGINT UNSIGNED NOT NULL, FOREIGN KEY (handPlayerId) REFERENCES HandsPlayers(id),
street SMALLINT NOT NULL,
actionNo SMALLINT NOT NULL,
@ -411,7 +411,7 @@ class FpdbSQLQueries:
ENGINE=INNODB"""
elif(self.dbname == 'PostgreSQL'):
self.query['createHandsActionsTable'] = """CREATE TABLE HandsActions (
id BIGSERIAL, PRIMARY KEY (id),
id BIGSERIAL UNIQUE, PRIMARY KEY (id),
handPlayerId BIGINT, FOREIGN KEY (handPlayerId) REFERENCES HandsPlayers(id),
street SMALLINT,
actionNo SMALLINT,
@ -430,7 +430,7 @@ class FpdbSQLQueries:
if(self.dbname == 'MySQL InnoDB'):
self.query['createHudCacheTable'] = """CREATE TABLE HudCache (
id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
id BIGINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
playerId INT UNSIGNED NOT NULL, FOREIGN KEY (playerId) REFERENCES Players(id),
activeSeats SMALLINT NOT NULL,
@ -503,7 +503,7 @@ class FpdbSQLQueries:
ENGINE=INNODB"""
elif(self.dbname == 'PostgreSQL'):
self.query['createHudCacheTable'] = """CREATE TABLE HudCache (
id BIGSERIAL, PRIMARY KEY (id),
id BIGSERIAL UNIQUE, PRIMARY KEY (id),
gametypeId INT, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id),
activeSeats SMALLINT,
@ -576,26 +576,23 @@ class FpdbSQLQueries:
if(self.dbname == 'MySQL InnoDB'):
self.query['addTourneyIndex'] = """ALTER TABLE Tourneys ADD INDEX siteTourneyNo(siteTourneyNo)"""
elif(self.dbname == 'PostgreSQL'):
# FIXME: This query has a different syntax
self.query['addTourneyIndex'] = """ALTER TABLE Tourneys ADD INDEX siteTourneyNo(siteTourneyNo)"""
self.query['addTourneyIndex'] = """CREATE INDEX siteTourneyNo ON Tourneys (siteTourneyNo)"""
elif(self.dbname == 'SQLite'):
self.query['addHandsIndex'] = """ """
if(self.dbname == 'MySQL InnoDB'):
self.query['addHandsIndex'] = """ALTER TABLE Hands ADD INDEX siteHandNo(siteHandNo)"""
elif(self.dbname == 'PostgreSQL'):
# FIXME: This query has a different syntax
self.query['addHandsIndex'] = """ALTER TABLE Hands ADD INDEX siteHandNo(siteHandNo)"""
self.query['addHandsIndex'] = """CREATE INDEX siteHandNo ON Hands (siteHandNo)"""
elif(self.dbname == 'SQLite'):
self.query['addHandsIndex'] = """ """
if(self.dbname == 'MySQL InnoDB'):
self.query['addPlayersIndex'] = """ALTER TABLE Players ADD INDEX name(name)"""
elif(self.dbname == 'PostgreSQL'):
# FIXME: This query has a different syntax
self.query['addHandsIndex'] = """ALTER TABLE Hands ADD INDEX siteHandNo(siteHandNo)"""
self.query['addPlayersIndex'] = """CREATE INDEX name ON Players (name)"""
elif(self.dbname == 'SQLite'):
self.query['addHandsIndex'] = """ """
self.query['addPlayersIndex'] = """ """
################################
# Queries used in GuiGraphViewer
@ -633,6 +630,11 @@ class FpdbSQLQueries:
WHERE Players.name = %s AND HandsPlayers.handId = %s
AND Players.siteId = %s AND (tourneysPlayersId IS NULL)"""
if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL'):
self.query['getPlayerId'] = """SELECT id from Players where name = %s"""
elif(self.dbname == 'SQLite'):
self.query['getPlayerId'] = """SELECT id from Players where name = %s"""
if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL'):
self.query['getRingProfitAllHandsPlayerIdSite'] = """
SELECT hp.handId, hp.winnings, SUM(ha.amount), hp.winnings - SUM(ha.amount)
@ -659,38 +661,6 @@ class FpdbSQLQueries:
GROUP BY hp.handId, hp.winnings, h.handStart
ORDER BY h.handStart"""
# Returns the profit for a given ring game handId, Total pot - money invested by playerId - WRONG, returns players costs
if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL'):
self.query['getRingProfitFromHandId'] = """SELECT SUM(amount) FROM HandsActions
INNER JOIN HandsPlayers ON HandsActions.handPlayerId = HandsPlayers.id
INNER JOIN Players ON HandsPlayers.playerId = Players.id
WHERE Players.name = %s AND HandsPlayers.handId = %s
AND Players.siteId = %s AND (tourneysPlayersId IS NULL)"""
elif(self.dbname == 'SQLite'):
#Probably doesn't work.
self.query['getRingProfitFromHandId'] = """SELECT SUM(amount) FROM HandsActions
INNER JOIN HandsPlayers ON HandsActions.handPlayerId = HandsPlayers.id
INNER JOIN Players ON HandsPlayers.playerId = Players.id
WHERE Players.name = %s AND HandsPlayers.handId = %s
AND Players.siteId = %s AND (tourneysPlayersId IS NULL)"""
# Returns a list of the tables in the database
if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL'):
self.query['getTableList'] = """
SELECT table_name
FROM information_schema.tables
WHERE table_type = 'BASE TABLE'
AND table_schema = %s
"""
elif(self.dbname == 'getTableList'):
#Probably doesn't work.
self.query['getTableList'] = """
SELECT table_name
FROM information_schema.tables
WHERE table_type = 'BASE TABLE'
AND table_schema = %s
"""
if(self.dbname == 'MySQL InnoDB'):
self.query['playerStats'] = """
SELECT stats.gametypeId
@ -733,8 +703,7 @@ class FpdbSQLQueries:
from Gametypes gt
inner join Sites s on s.Id = gt.siteId
inner join HudCache hc on hc.gameTypeId = gt.Id
where gt.limittype = 'nl'
and hc.playerId in (3) # use <player_test> here?
where hc.playerId in <player_test>
# use <gametype_test> here ?
group by hc.gametypeId
) stats
@ -747,7 +716,7 @@ class FpdbSQLQueries:
from HandsPlayers hp
inner join Hands h ON h.id = hp.handId
inner join HandsActions ha ON ha.handPlayerId = hp.id
where hp.playerId in (3) # use <player_test> here?
where hp.playerId in <player_test>
# use <gametype_test> here ?
and hp.tourneysPlayersId IS NULL
group by hp.handId, h.gameTypeId, hp.position, hp.winnings

View File

@ -40,7 +40,7 @@ class GuiAutoImport (threading.Thread):
self.input_settings = {}
self.importer = fpdb_import.Importer(self, self.settings, self.config)
self.importer = fpdb_import.Importer(self,self.settings, self.config)
self.importer.setCallHud(True)
self.importer.setMinPrint(30)
self.importer.setQuiet(False)
@ -60,7 +60,7 @@ class GuiAutoImport (threading.Thread):
self.mainVBox.pack_start(self.settingsHBox, False, True, 0)
self.settingsHBox.show()
self.intervalLabel = gtk.Label("Time between imports in seconds:")
self.intervalLabel = gtk.Label("Interval (ie. break) between imports in seconds:")
self.settingsHBox.pack_start(self.intervalLabel)
self.intervalLabel.show()

View File

@ -22,14 +22,17 @@ import pygtk
pygtk.require('2.0')
import gtk
import os #todo: remove this once import_dir is in fpdb_import
from time import time
class GuiBulkImport (threading.Thread):
def import_dir(self):
"""imports a directory, non-recursive. todo: move this to fpdb_import so CLI can use it"""
self.path=self.inputFile
self.importer.addImportDirectory(self.path)
self.importer.setCallHud(False)
starttime = time()
self.importer.runImport()
print "GuiBulkImport.import_dir done"
print "GuiBulkImport.import_dir done in %s" %(time() - starttime)
def load_clicked(self, widget, data=None):
self.inputFile=self.chooser.get_filename()
@ -64,6 +67,7 @@ class GuiBulkImport (threading.Thread):
self.import_dir()
else:
self.importer.addImportFile(self.inputFile)
self.importer.setCallHud(False)
self.importer.runImport()
self.importer.clearFileList()
@ -80,7 +84,7 @@ class GuiBulkImport (threading.Thread):
self.db=db
self.settings=settings
self.config=config
self.importer = fpdb_import.Importer(self,self.settings)
self.importer = fpdb_import.Importer(self,self.settings, config)
self.vbox=gtk.VBox(False,1)
self.vbox.show()

View File

@ -20,17 +20,22 @@ import pygtk
pygtk.require('2.0')
import gtk
import os
from time import time
#import pokereval
try:
import matplotlib
matplotlib.use('GTK')
from matplotlib.figure import Figure
from matplotlib.backends.backend_gtk import FigureCanvasGTK as FigureCanvas
from matplotlib.backends.backend_gtkagg import NavigationToolbar2GTKAgg as NavigationToolbar
from numpy import arange, cumsum
from pylab import *
except:
print "Failed to load libs for graphing, graphing will not function. Please install numpy and matplotlib if you want to use graphs."
print "This is of no consequence for other parts of the program, e.g. import and HUD are NOT affected by this problem."
print """Failed to load libs for graphing, graphing will not function. Please in
stall numpy and matplotlib if you want to use graphs."""
print """This is of no consequence for other parts of the program, e.g. import
and HUD are NOT affected by this problem."""
import fpdb_import
import fpdb_db
@ -38,21 +43,24 @@ import fpdb_db
class GuiGraphViewer (threading.Thread):
def get_vbox(self):
"""returns the vbox of this thread"""
return self.mainVBox
return self.mainHBox
#end def get_vbox
def showClicked(self, widget, data):
def generateGraph(self, widget, data):
try: self.canvas.destroy()
except AttributeError: pass
name=self.nameEntry.get_text()
# Whaich sites are selected?
# TODO:
# What hero names for the selected site?
# TODO:
site=self.siteEntry.get_text()
name = self.heroes[self.sites]
if site=="PS":
if self.sites == "PokerStars":
site=2
sitename="PokerStars: "
elif site=="FTP":
elif self.sites=="Full Tilt":
site=1
sitename="Full Tilt: "
else:
@ -64,47 +72,180 @@ class GuiGraphViewer (threading.Thread):
#Set graph properties
self.ax = self.fig.add_subplot(111)
#
#Get graph data from DB
starttime = time()
line = self.getRingProfitGraph(name, site)
print "Graph generated in: %s" %(time() - starttime)
self.ax.set_title("Profit graph for ring games")
#Set axis labels and grid overlay properites
self.ax.set_xlabel("Hands", fontsize = 12)
self.ax.set_ylabel("$", fontsize = 12)
self.ax.grid(color='g', linestyle=':', linewidth=0.2)
text = "All Hands, " + sitename + str(name)
#This line will crash if no hands exist in the query.
text = "All Hands, " + sitename + str(name) + "\nProfit: $" + str(line[-1]) + "\nTotal Hands: " + str(len(line))
self.ax.annotate (text, (61,25), xytext =(0.1, 0.9) , textcoords ="axes fraction" ,)
self.ax.annotate(text,
xy=(10, -10),
xycoords='axes points',
horizontalalignment='left', verticalalignment='top',
fontsize=10)
#Get graph data from DB
line = self.getRingProfitGraph(name, site)
#Draw plot
self.ax.plot(line,)
self.canvas = FigureCanvas(self.fig) # a gtk.DrawingArea
self.mainVBox.pack_start(self.canvas)
self.graphBox.add(self.canvas)
self.canvas.show()
#end of def showClicked
def getRingProfitGraph(self, name, site):
#self.cursor.execute(self.sql.query['getRingWinningsAllGamesPlayerIdSite'], (name, site))
self.cursor.execute(self.sql.query['getRingProfitAllHandsPlayerIdSite'], (name, site))
# returns (HandId,Winnings,Costs,Profit)
#returns (HandId,Winnings,Costs,Profit)
winnings = self.db.cursor.fetchall()
#profit=range(len(winnings))
#for i in profit:
# self.cursor.execute(self.sql.query['getRingProfitFromHandId'], (name, winnings[i][0], site))
# spent = self.db.cursor.fetchone()
# profit[i]=(i, winnings[i][1]-spent[0])
#y=map(lambda x:float(x[1]), profit)
y=map(lambda x:float(x[3]), winnings)
line = cumsum(y)
return line/100
#end of def getRingProfitGraph
def createPlayerLine(self, hbox, site, player):
label = gtk.Label(site +" id:")
hbox.pack_start(label, False, False, 0)
label.show()
pname = gtk.Entry()
pname.set_text(player)
pname.set_width_chars(20)
hbox.pack_start(pname, False, True, 0)
#TODO: Need to connect a callback here
pname.connect("changed", self.__set_hero_name, site)
#TODO: Look at GtkCompletion - to fill out usernames
pname.show()
self.__set_hero_name(pname, site)
def __set_hero_name(self, w, site):
self.heroes[site] = w.get_text()
print "DEBUG: settings heroes[%s]: %s"%(site, self.heroes[site])
def createSiteLine(self, hbox, site):
cb = gtk.CheckButton(site)
cb.connect('clicked', self.__set_site_select, site)
hbox.pack_start(cb, False, False, 0)
cb.show()
def __set_site_select(self, w, site):
# This doesn't behave as intended - self.site only allows 1 site for the moment.
self.sites = site
print "self.sites set to %s" %(self.sites)
def fillPlayerFrame(self, vbox):
for site in self.conf.supported_sites.keys():
pathHBox = gtk.HBox(False, 0)
vbox.pack_start(pathHBox, False, True, 0)
pathHBox.show()
player = self.conf.supported_sites[site].screen_name
self.createPlayerLine(pathHBox, site, player)
def fillSitesFrame(self, vbox):
for site in self.conf.supported_sites.keys():
hbox = gtk.HBox(False, 0)
vbox.pack_start(hbox, False, True, 0)
hbox.show()
self.createSiteLine(hbox, site)
def fillDateFrame(self, vbox):
# Hat tip to Mika Bostrom - calendar code comes from PokerStats
hbox = gtk.HBox()
vbox.pack_start(hbox, False, True, 0)
hbox.show()
lbl_start = gtk.Label('From:')
lbl_start.show()
btn_start = gtk.Button()
btn_start.set_image(gtk.image_new_from_stock(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON))
btn_start.connect('clicked', self.__calendar_dialog, self.start_date)
btn_start.show()
hbox.pack_start(lbl_start, expand=False, padding=3)
hbox.pack_start(btn_start, expand=False, padding=3)
hbox.pack_start(self.start_date, expand=False, padding=2)
self.start_date.show()
#New row for end date
hbox = gtk.HBox()
vbox.pack_start(hbox, False, True, 0)
hbox.show()
lbl_end = gtk.Label(' To:')
lbl_end.show()
btn_end = gtk.Button()
btn_end.set_image(gtk.image_new_from_stock(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON))
btn_end.connect('clicked', self.__calendar_dialog, self.end_date)
btn_end.show()
btn_clear = gtk.Button(label=' Clear Dates ')
btn_clear.connect('clicked', self.__clear_dates)
btn_clear.show()
hbox.pack_start(lbl_end, expand=False, padding=3)
hbox.pack_start(btn_end, expand=False, padding=3)
hbox.pack_start(self.end_date, expand=False, padding=2)
self.end_date.show()
hbox.pack_start(btn_clear, expand=False, padding=15)
def __calendar_dialog(self, widget, entry):
d = gtk.Window(gtk.WINDOW_TOPLEVEL)
d.set_title('Pick a date')
vb = gtk.VBox()
cal = gtk.Calendar()
vb.pack_start(cal, expand=False, padding=0)
btn = gtk.Button('Done')
btn.connect('clicked', self.__get_date, cal, entry, d)
vb.pack_start(btn, expand=False, padding=4)
d.add(vb)
d.set_position(gtk.WIN_POS_MOUSE)
d.show_all()
def __clear_dates(self, w):
self.start_date.set_text('')
self.end_date.set_text('')
def __get_dates(self):
t1 = self.start_date.get_text()
t2 = self.end_date.get_text()
return (t1, t2)
def __get_date(self, widget, calendar, entry, win):
# year and day are correct, month is 0..11
(year, month, day) = calendar.get_date()
month += 1
ds = '%04d-%02d-%02d' % (year, month, day)
entry.set_text(ds)
win.destroy()
def exportGraph (self, widget, data):
dia_chooser = gtk.FileChooserDialog(title="Please choose the directory you wish to export to:",
action=gtk.FILE_CHOOSER_ACTION_OPEN,
buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
response = dia_chooser.run()
if response == gtk.RESPONSE_OK:
self.exportDir = dia_chooser.get_filename()
elif response == gtk.RESPONSE_CANCEL:
print 'Closed, no graph exported'
dia_chooser.destroy()
def __init__(self, db, settings, querylist, config, debug=True):
"""Constructor for GraphViewer"""
self.debug=debug
@ -113,37 +254,70 @@ class GuiGraphViewer (threading.Thread):
self.cursor=db.cursor
self.settings=settings
self.sql=querylist
self.conf = config
self.mainVBox = gtk.VBox(False, 0)
self.mainVBox.show()
self.sites = "PokerStars"
self.heroes = {}
self.settingsHBox = gtk.HBox(False, 0)
self.mainVBox.pack_start(self.settingsHBox, False, True, 0)
self.settingsHBox.show()
# For use in date ranges.
self.start_date = gtk.Entry(max=12)
self.end_date = gtk.Entry(max=12)
self.start_date.set_property('editable', False)
self.end_date.set_property('editable', False)
self.nameLabel = gtk.Label("Name of the player to be graphed:")
self.settingsHBox.pack_start(self.nameLabel)
self.nameLabel.show()
self.mainHBox = gtk.HBox(False, 0)
self.mainHBox.show()
self.nameEntry=gtk.Entry()
self.nameEntry.set_text("name")
self.settingsHBox.pack_start(self.nameEntry)
self.nameEntry.show()
self.leftPanelBox = gtk.VBox(False, 0)
self.graphBox = gtk.VBox(False, 0)
self.siteLabel = gtk.Label("Site (PS or FTP):")
self.settingsHBox.pack_start(self.siteLabel)
self.siteLabel.show()
self.hpane = gtk.HPaned()
self.hpane.pack1(self.leftPanelBox)
self.hpane.pack2(self.graphBox)
self.hpane.show()
self.siteEntry=gtk.Entry()
self.siteEntry.set_text("PS")
self.settingsHBox.pack_start(self.siteEntry)
self.siteEntry.show()
self.mainHBox.add(self.hpane)
#Note: Assumes PokerStars is in the config
self.nameEntry.set_text(config.supported_sites["PokerStars"].screen_name)
playerFrame = gtk.Frame("Hero:")
playerFrame.set_label_align(0.0, 0.0)
playerFrame.show()
vbox = gtk.VBox(False, 0)
vbox.show()
self.showButton=gtk.Button("Show/Refresh")
self.showButton.connect("clicked", self.showClicked, "show clicked")
self.settingsHBox.pack_start(self.showButton)
self.showButton.show()
#end of GuiGraphViewer.__init__
self.fillPlayerFrame(vbox)
playerFrame.add(vbox)
sitesFrame = gtk.Frame("Sites:")
sitesFrame.set_label_align(0.0, 0.0)
sitesFrame.show()
vbox = gtk.VBox(False, 0)
vbox.show()
self.fillSitesFrame(vbox)
sitesFrame.add(vbox)
dateFrame = gtk.Frame("Date:")
dateFrame.set_label_align(0.0, 0.0)
dateFrame.show()
vbox = gtk.VBox(False, 0)
vbox.show()
self.fillDateFrame(vbox)
dateFrame.add(vbox)
graphButton=gtk.Button("Generate Graph")
graphButton.connect("clicked", self.generateGraph, "cliced data")
graphButton.show()
self.exportButton=gtk.Button("Export to File")
self.exportButton.connect("clicked", self.exportGraph, "show clicked")
self.exportButton.show()
self.leftPanelBox.add(playerFrame)
self.leftPanelBox.add(sitesFrame)
self.leftPanelBox.add(dateFrame)
self.leftPanelBox.add(graphButton)
self.leftPanelBox.add(self.exportButton)
self.leftPanelBox.show()
self.graphBox.show()

165
pyfpdb/GuiPlayerStats.py Normal file
View File

@ -0,0 +1,165 @@
#!/usr/bin/python
#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.
import threading
import pygtk
pygtk.require('2.0')
import gtk
import os
import fpdb_import
import fpdb_db
import FpdbSQLQueries
class GuiPlayerStats (threading.Thread):
def get_vbox(self):
"""returns the vbox of this thread"""
return self.main_hbox
def toggleCallback(self, widget, data=None):
# print "%s was toggled %s" % (data, ("OFF", "ON")[widget.get_active()])
self.activesite = data
print "DEBUG: activesite set to %s" %(self.activesite)
def refreshStats(self, widget, data):
try: self.stats_table.destroy()
except AttributeError: pass
self.fillStatsFrame(self.stats_frame)
def fillStatsFrame(self, vbox):
# Get currently active site and grab playerid
tmp = self.sql.query['playerStats']
result = self.cursor.execute(self.sql.query['getPlayerId'], self.heroes[self.activesite])
result = self.db.cursor.fetchall()
pid = result[0][0]
tmp = tmp.replace("<player_test>", "(" + str(pid) + ")")
self.cursor.execute(tmp)
result = self.db.cursor.fetchall()
cols = 18
rows = len(result)+1 # +1 for title row
self.stats_table = gtk.Table(rows, cols, False)
self.stats_table.set_col_spacings(4)
self.stats_table.show()
vbox.add(self.stats_table)
# Create header row
titles = ("GID", "base", "Style", "Site", "$BB", "Hands", "VPIP", "PFR", "saw_f", "sawsd", "wtsdwsf", "wmsd", "FlAFq", "TuAFq", "RvAFq", "PFAFq", "Net($)", "BB/100")
col = 0
row = 0
for t in titles:
l = gtk.Label(titles[col])
l.show()
self.stats_table.attach(l, col, col+1, row, row+1)
col +=1
for row in range(rows-1):
for col in range(cols):
if(row%2 == 0):
bgcolor = "white"
else:
bgcolor = "lightgrey"
eb = gtk.EventBox()
eb.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(bgcolor))
l = gtk.Label(result[row-1][col])
eb.add(l)
self.stats_table.attach(eb, col, col+1, row+1, row+2)
l.show()
eb.show()
def fillPlayerFrame(self, vbox):
for site in self.conf.supported_sites.keys():
hbox = gtk.HBox(False, 0)
vbox.pack_start(hbox, False, True, 0)
hbox.show()
player = self.conf.supported_sites[site].screen_name
self.createPlayerLine(hbox, site, player)
hbox = gtk.HBox(False, 0)
button = gtk.Button("Refresh")
button.connect("clicked", self.refreshStats, False)
button.show()
hbox.add(button)
vbox.pack_start(hbox, False, True, 0)
hbox.show()
def createPlayerLine(self, hbox, site, player):
if(self.buttongroup == None):
button = gtk.RadioButton(None, site + " id:")
button.set_active(True)
self.buttongroup = button
self.activesite = site
else:
button = gtk.RadioButton(self.buttongroup, site + " id:")
hbox.pack_start(button, True, True, 0)
button.connect("toggled", self.toggleCallback, site)
button.show()
pname = gtk.Entry()
pname.set_text(player)
pname.set_width_chars(20)
hbox.pack_start(pname, False, True, 0)
pname.connect("changed", self.__set_hero_name, site)
#TODO: Look at GtkCompletion - to fill out usernames
pname.show()
self.__set_hero_name(pname, site)
def __set_hero_name(self, w, site):
self.heroes[site] = w.get_text()
print "DEBUG: settings heroes[%s]: %s"%(site, self.heroes[site])
def __init__(self, db, config, querylist, debug=True):
self.debug=debug
self.db=db
self.cursor=db.cursor
self.conf=config
self.sql = querylist
self.activesite = None
self.buttongroup = None
self.heroes = {}
self.stat_table = None
self.stats_frame = None
self.main_hbox = gtk.HBox(False, 0)
self.main_hbox.show()
playerFrame = gtk.Frame("Hero:")
playerFrame.set_label_align(0.0, 0.0)
playerFrame.show()
vbox = gtk.VBox(False, 0)
vbox.show()
self.fillPlayerFrame(vbox)
playerFrame.add(vbox)
statsFrame = gtk.Frame("Stats:")
statsFrame.set_label_align(0.0, 0.0)
statsFrame.show()
self.stats_frame = gtk.VBox(False, 0)
self.stats_frame.show()
self.fillStatsFrame(self.stats_frame)
statsFrame.add(self.stats_frame)
self.main_hbox.pack_start(playerFrame)
self.main_hbox.pack_start(statsFrame)

View File

@ -2,7 +2,7 @@
<FreePokerToolsConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FreePokerToolsConfig.xsd">
<supported_sites>
<site enabled="True" site_name="PokerStars" table_finder="PokerStars.exe" screen_name="DO NOT NEED THIS YET" site_path="~/.wine/drive_c/Program Files/PokerStars/" HH_path="~/.wine/drive_c/Program Files/PokerStars/HandHistory/abc/" decoder="pokerstars_decode_table" converter="passthrough" supported_games="holdem,razz,omahahi,omahahilo,studhi,studhilo">
<site enabled="True" site_name="PokerStars" table_finder="PokerStars.exe" screen_name="ENTER HERO NAME" site_path="~/.wine/drive_c/Program Files/PokerStars/" HH_path="~/.wine/drive_c/Program Files/PokerStars/HandHistory/abc/" decoder="pokerstars_decode_table" converter="passthrough" supported_games="holdem,razz,omahahi,omahahilo,studhi,studhilo">
<layout max="8" width="792" height="546" fav_seat="0">
<location seat="1" x="684" y="61"> </location>
<location seat="2" x="689" y="239"> </location>
@ -49,7 +49,7 @@
<location seat="2" x="10" y="288"> </location>
</layout>
</site>
<site enabled="True" site_name="Full Tilt" table_finder="FullTiltPoker.exe" screen_name="DO NOT NEED THIS YET" site_path="~/.wine/drive_c/Program Files/Full Tilt Poker/" HH_path="~/.wine/drive_c/Program Files/Full Tilt Poker/HandHistory/abc/" decoder="fulltilt_decode_table" converter="passthrough" supported_games="holdem,razz,omahahi,omahahilo,studhi,studhilo">
<site enabled="True" site_name="Full Tilt" table_finder="FullTiltPoker.exe" screen_name="ENTER HERO NAME" site_path="~/.wine/drive_c/Program Files/Full Tilt Poker/" HH_path="~/.wine/drive_c/Program Files/Full Tilt Poker/HandHistory/abc/" decoder="fulltilt_decode_table" converter="passthrough" supported_games="holdem,razz,omahahi,omahahilo,studhi,studhilo">
<layout fav_seat="0" height="547" max="8" width="794">
<location seat="1" x="640" y="64"> </location>
<location seat="2" x="650" y="230"> </location>
@ -84,7 +84,7 @@
<location seat="9" x="70" y="53"> </location>
</layout>
</site>
<site enabled="False" site_name="Everleaf" table_finder="Poker.exe" screen_name="DO NOT NEED THIS YET" site_path="" HH_path="" decoder="Unknown" converter="EverleafToFpdb" supported_games="holdem,razz,omahahi,omahahilo,studhi" fgcolor="#48D1CC" bgcolor="#000000">
<site enabled="False" site_name="Everleaf" table_finder="Poker.exe" screen_name="ENTER HERO NAME" site_path="" HH_path="" decoder="Unknown" converter="EverleafToFpdb" supported_games="holdem,razz,omahahi,omahahilo,studhi" fgcolor="#48D1CC" bgcolor="#000000" opacity="0.75">
<layout fav_seat="0" height="546" max="6" width="792">
<location seat="1" x="581" y="109"> </location>
<location seat="2" x="605" y="287"> </location>
@ -187,7 +187,7 @@
<pu_stat pu_stat_name="ffreq_4"> </pu_stat>
</pu>
</popup_windows>
<import callFpdbHud = "True" interval = "10" ></import>
<import callFpdbHud = "True" interval = "10" hhArchiveBase="~/.fpdb/HandHistories/"></import>
<tv combinedStealFold = "True" combined2B3B = "True" combinedPostflop = "True"></tv>
<supported_databases>

View File

@ -0,0 +1,427 @@
#!/usr/bin/python
#Copyright 2008 Carl Gherardi
#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.
import Configuration
import FpdbRegex
import re
import sys
import traceback
import os
import os.path
import xml.dom.minidom
from decimal import Decimal
import operator
from xml.dom.minidom import Node
class HandHistoryConverter:
def __init__(self, config, file, sitename):
print "HandHistory init called"
self.c = config
self.sitename = sitename
self.obs = "" # One big string
self.filetype = "text"
self.doc = None # For XML based HH files
self.file = file
self.hhbase = self.c.get_import_parameters().get("hhArchiveBase")
self.hhbase = os.path.expanduser(self.hhbase)
self.hhdir = os.path.join(self.hhbase,sitename)
self.gametype = []
# self.ofile = os.path.join(self.hhdir,file)
self.rexx = FpdbRegex.FpdbRegex()
def __str__(self):
tmp = "HandHistoryConverter: '%s'\n" % (self.sitename)
tmp = tmp + "\thhbase: '%s'\n" % (self.hhbase)
tmp = tmp + "\thhdir: '%s'\n" % (self.hhdir)
tmp = tmp + "\tfiletype: '%s'\n" % (self.filetype)
tmp = tmp + "\tinfile: '%s'\n" % (self.file)
# tmp = tmp + "\toutfile: '%s'\n" % (self.ofile)
# tmp = tmp + "\tgametype: '%s'\n" % (self.gametype[0])
# tmp = tmp + "\tgamebase: '%s'\n" % (self.gametype[1])
# tmp = tmp + "\tlimit: '%s'\n" % (self.gametype[2])
# tmp = tmp + "\tsb/bb: '%s/%s'\n" % (self.gametype[3], self.gametype[4])
return tmp
def processFile(self):
if not self.sanityCheck():
print "Cowardly refusing to continue after failed sanity check"
return
self.readFile(self.file)
self.gametype = self.determineGameType()
self.hands = self.splitFileIntoHands()
for hand in self.hands:
self.readHandInfo(hand)
self.readPlayerStacks(hand)
self.markStreets(hand)
self.readBlinds(hand)
self.readHeroCards(hand)
# Read action (Note: no guarantee this is in hand order.
for street in hand.streets.groupdict():
self.readAction(hand, street)
# finalise it (total the pot)
hand.totalPot()
self.getRake(hand)
if(hand.involved == True):
#self.writeHand("output file", hand)
hand.printHand()
else:
pass #Don't write out observed hands
#####
# These functions are parse actions that may be overridden by the inheriting class
#
def readSupportedGames(self): abstract
# should return a list
# type base limit
# [ ring, hold, nl , sb, bb ]
# Valid types specified in docs/tabledesign.html in Gametypes
def determineGameType(self): abstract
# Read any of:
# HID HandID
# TABLE Table name
# SB small blind
# BB big blind
# GAMETYPE gametype
# YEAR MON DAY HR MIN SEC datetime
# BUTTON button seat number
def readHandInfo(self, hand): abstract
# Needs to return a list of lists in the format
# [['seat#', 'player1name', 'stacksize'] ['seat#', 'player2name', 'stacksize'] [...]]
def readPlayerStacks(self, hand): abstract
# Needs to return a MatchObject with group names identifying the streets into the Hand object
# that is, pulls the chunks of preflop, flop, turn and river text into hand.streets MatchObject.
def markStreets(self, hand): abstract
#Needs to return a list in the format
# ['player1name', 'player2name', ...] where player1name is the sb and player2name is bb,
# addtional players are assumed to post a bb oop
def readBlinds(self, hand): abstract
def readHeroCards(self, hand): abstract
def readAction(self, hand, street): abstract
# Some sites don't report the rake. This will be called at the end of the hand after the pot total has been calculated
# so that an inheriting class can calculate it for the specific site if need be.
def getRake(self, hand): abstract
def sanityCheck(self):
sane = True
base_w = False
#Check if hhbase exists and is writable
#Note: Will not try to create the base HH directory
if not (os.access(self.hhbase, os.W_OK) and os.path.isdir(self.hhbase)):
print "HH Sanity Check: Directory hhbase '" + self.hhbase + "' doesn't exist or is not writable"
else:
#Check if hhdir exists and is writable
if not os.path.isdir(self.hhdir):
# In first pass, dir may not exist. Attempt to create dir
print "Creating directory: '%s'" % (self.hhdir)
os.mkdir(self.hhdir)
sane = True
elif os.access(self.hhdir, os.W_OK):
sane = True
else:
print "HH Sanity Check: Directory hhdir '" + self.hhdir + "' or its parent directory are not writable"
return sane
# Functions not necessary to implement in sub class
def setFileType(self, filetype = "text"):
self.filetype = filetype
def splitFileIntoHands(self):
hands = []
list = self.rexx.split_hand_re.split(self.obs)
list.pop() #Last entry is empty
for l in list:
# print "'" + l + "'"
hands = hands + [Hand(self.sitename, self.gametype, l)]
return hands
def readFile(self, filename):
"""Read file"""
print "Reading file: '%s'" %(filename)
if(self.filetype == "text"):
infile=open(filename, "rU")
self.obs = infile.read()
infile.close()
elif(self.filetype == "xml"):
try:
doc = xml.dom.minidom.parse(filename)
self.doc = doc
except:
traceback.print_exc(file=sys.stderr)
def writeHand(self, file, hand):
"""Write out parsed data"""
print "DEBUG: *************************"
print "DEBUG: Start of print hand"
print "DEBUG: *************************"
print "%s Game #%s: %s ($%s/$%s) - %s" %(hand.sitename, hand.handid, "XXXXhand.gametype", hand.sb, hand.bb, hand.starttime)
print "Table '%s' %d-max Seat #%s is the button" %(hand.tablename, hand.maxseats, hand.buttonpos)
for player in hand.players:
print "Seat %s: %s ($%s)" %(player[0], player[1], player[2])
if(hand.posted[0] == "FpdbNBP"):
print "No small blind posted"
else:
print "%s: posts small blind $%s" %(hand.posted[0], hand.sb)
#May be more than 1 bb posting
print "%s: posts big blind $%s" %(hand.posted[1], hand.bb)
if(len(hand.posted) > 2):
# Need to loop on all remaining big blinds - lazy
print "XXXXXXXXX FIXME XXXXXXXX"
print "*** HOLE CARDS ***"
print "Dealt to %s [%s %s]" %(hand.hero , hand.holecards[0], hand.holecards[1])
#
## ACTION STUFF
# This is no limit only at the moment
for act in hand.actions['PREFLOP']:
self.printActionLine(act, 0)
if 'PREFLOP' in hand.actions:
for act in hand.actions['PREFLOP']:
print "PF action"
if 'FLOP' in hand.actions:
print "*** FLOP *** [%s %s %s]" %(hand.streets.group("FLOP1"), hand.streets.group("FLOP2"), hand.streets.group("FLOP3"))
for act in hand.actions['FLOP']:
self.printActionLine(act, 0)
if 'TURN' in hand.actions:
print "*** TURN *** [%s %s %s] [%s]" %(hand.streets.group("FLOP1"), hand.streets.group("FLOP2"), hand.streets.group("FLOP3"), hand.streets.group("TURN1"))
for act in hand.actions['TURN']:
self.printActionLine(act, 0)
if 'RIVER' in hand.actions:
print "*** RIVER *** [%s %s %s %s] [%s]" %(hand.streets.group("FLOP1"), hand.streets.group("FLOP2"), hand.streets.group("FLOP3"), hand.streets.group("TURN1"), hand.streets.group("RIVER1"))
for act in hand.actions['RIVER']:
self.printActionLine(act, 0)
print "*** SUMMARY ***"
print "XXXXXXXXXXXX Need sumary info XXXXXXXXXXX"
# print "Total pot $%s | Rake $%s)" %(hand.totalpot $" + hand.rake)
# print "Board [" + boardcards + "]"
#
# SUMMARY STUFF
def printActionLine(self, act, pot):
if act[1] == 'folds' or act[1] == 'checks':
print "%s: %s " %(act[0], act[1])
if act[1] == 'calls':
print "%s: %s $%s" %(act[0], act[1], act[2])
if act[1] == 'raises':
print "%s: %s $%s to XXXpottotalXXX" %(act[0], act[1], act[2])
#takes a poker float (including , for thousand seperator and converts it to an int
def float2int (self, string):
pos=string.find(",")
if (pos!=-1): #remove , the thousand seperator
string=string[0:pos]+string[pos+1:]
pos=string.find(".")
if (pos!=-1): #remove decimal point
string=string[0:pos]+string[pos+1:]
result = int(string)
if pos==-1: #no decimal point - was in full dollars - need to multiply with 100
result*=100
return result
#end def float2int
class Hand:
# def __init__(self, sitename, gametype, sb, bb, string):
UPS = {'a':'A', 't':'T', 'j':'J', 'q':'Q', 'k':'K'}
STREETS = ['BLINDS','PREFLOP','FLOP','TURN','RIVER']
def __init__(self, sitename, gametype, string):
self.sitename = sitename
self.gametype = gametype
self.string = string
self.streets = None # A MatchObject using a groupnames to identify streets.
self.actions = {}
self.handid = 0
self.sb = gametype[3]
self.bb = gametype[4]
self.tablename = "Slartibartfast"
self.maxseats = 10
self.counted_seats = 0
self.buttonpos = 0
self.seating = []
self.players = []
self.posted = []
self.involved = True
self.hero = "Hiro"
self.holecards = "Xx Xx"
self.action = []
self.totalpot = None
self.rake = None
self.bets = {}
self.lastBet = {}
for street in self.STREETS:
self.bets[street] = {}
self.lastBet[street] = 0
def addPlayer(self, seat, name, chips):
"""seat, an int indicating the seat
name, the player name
chips, the chips the player has at the start of the hand"""
#self.players.append(name)
self.players.append([seat, name, chips])
#self.startChips[name] = chips
#self.endChips[name] = chips
#self.winners[name] = 0
for street in self.STREETS:
self.bets[street][name] = []
def addHoleCards(self,h1,h2,seat=None): # generalise to add hole cards for a specific seat or player
self.holecards = [self.card(h1), self.card(h2)]
def card(self,c):
"""upper case the ranks but not suits, 'atjqk' => 'ATJQK'"""
# don't know how to make this 'static'
for k,v in self.UPS.items():
c = c.replace(k,v)
return c
def addBlind(self, player, amount):
# if player is None, it's a missing small blind.
if player is not None:
self.bets['PREFLOP'][player].append(Decimal(amount))
self.lastBet['PREFLOP'] = Decimal(amount)
self.posted += [player]
def addCall(self, street, player=None, amount=None):
# Potentially calculate the amount of the call if not supplied
# corner cases include if player would be all in
if amount is not None:
self.bets[street][player].append(Decimal(amount))
#self.lastBet[street] = Decimal(amount)
self.actions[street] += [[player, 'calls', amount]]
def addRaiseTo(self, street, player, amountTo):
# Given only the amount raised to, the amount of the raise can be calculated by
# working out how much this player has already in the pot
# (which is the sum of self.bets[street][player])
# and how much he needs to call to match the previous player
# (which is tracked by self.lastBet)
committedThisStreet = reduce(operator.add, self.bets[street][player], 0)
amountToCall = self.lastBet[street] - committedThisStreet
self.lastBet[street] = Decimal(amountTo)
amountBy = Decimal(amountTo) - amountToCall
self.bets[street][player].append(amountBy+amountToCall)
self.actions[street] += [[player, 'raises', amountBy, amountTo]]
def addBet(self, street, player=None, amount=0):
self.bets[street][name].append(Decimal(amount))
self.orderedBets[street].append(Decimal(amount))
self.actions[street] += [[player, 'bets', amount]]
def totalPot(self):
if self.totalpot is None:
self.totalpot = 0
# player names:
# print [x[1] for x in self.players]
for player in [x[1] for x in self.players]:
for street in self.STREETS:
print street, self.bets[street][player]
self.totalpot += reduce(operator.add, self.bets[street][player], 0)
def printHand(self):
# PokerStars format.
print "### DEBUG ###"
print "%s Game #%s: %s ($%s/$%s) - %s" %(self.sitename, self.handid, "XXXXhand.gametype", self.sb, self.bb, self.starttime)
print "Table '%s' %d-max Seat #%s is the button" %(self.tablename, self.maxseats, self.buttonpos)
for player in self.players:
print "Seat %s: %s ($%s)" %(player[0], player[1], player[2])
if(self.posted[0] is None):
print "No small blind posted"
else:
print "%s: posts small blind $%s" %(self.posted[0], self.sb)
#May be more than 1 bb posting
for a in self.posted[1:]:
print "%s: posts big blind $%s" %(self.posted[1], self.bb)
# What about big & small blinds?
print "*** HOLE CARDS ***"
print "Dealt to %s [%s %s]" %(self.hero , self.holecards[0], self.holecards[1])
if 'PREFLOP' in self.actions:
for act in self.actions['PREFLOP']:
self.printActionLine(act)
if 'FLOP' in self.actions:
print "*** FLOP *** [%s %s %s]" %(self.streets.group("FLOP1"), self.streets.group("FLOP2"), self.streets.group("FLOP3"))
for act in self.actions['FLOP']:
self.printActionLine(act)
if 'TURN' in self.actions:
print "*** TURN *** [%s %s %s] [%s]" %(self.streets.group("FLOP1"), self.streets.group("FLOP2"), self.streets.group("FLOP3"), self.streets.group("TURN1"))
for act in self.actions['TURN']:
self.printActionLine(act)
if 'RIVER' in self.actions:
print "*** RIVER *** [%s %s %s %s] [%s]" %(self.streets.group("FLOP1"), self.streets.group("FLOP2"), self.streets.group("FLOP3"), self.streets.group("TURN1"), self.streets.group("RIVER1"))
for act in self.actions['RIVER']:
self.printActionLine(act)
#Some sites don't have a showdown section so we have to figure out if there should be one
# The logic for a showdown is: at the end of river action there are at least two players in the hand
if 'SHOWDOWN' in self.actions:
print "*** SHOW DOWN ***"
print "what do they show"
print "*** SUMMARY ***"
print "Total pot $%s | Rake $%s)" % (self.totalpot, self.rake)
print "Board [%s %s %s %s %s]" % (self.streets.group("FLOP1"), self.streets.group("FLOP2"), self.streets.group("FLOP3"), self.streets.group("TURN1"), self.streets.group("RIVER1"))
def printActionLine(self, act):
if act[1] == 'folds' or act[1] == 'checks':
print "%s: %s " %(act[0], act[1])
if act[1] == 'calls':
print "%s: %s $%s" %(act[0], act[1], act[2])
if act[1] == 'raises':
print "%s: %s $%s to $%s" %(act[0], act[1], act[2], act[3])

View File

@ -60,7 +60,7 @@ class Hud:
self.stat_windows = {}
self.popup_windows = {}
self.aux_windows = []
self.font = pango.FontDescription("Sans 8")
self.font = pango.FontDescription("Sans 7")
# Set up a main window for this this instance of the HUD
self.main_window = gtk.Window()
@ -92,14 +92,22 @@ class Hud:
self.menu.append(self.item1)
self.item1.connect("activate", self.kill_hud)
self.item1.show()
self.item2 = gtk.MenuItem('Save Layout')
self.menu.append(self.item2)
self.item2.connect("activate", self.save_layout)
self.item2.show()
self.item3 = gtk.MenuItem('Reposition Stats')
self.menu.append(self.item3)
self.item3.connect("activate", self.reposition_windows)
self.item3.show()
self.item4 = gtk.MenuItem('Debug Stat Windows')
self.menu.append(self.item4)
self.item4.connect("activate", self.debug_stat_windows)
self.item4.show()
self.ebox.connect_object("button-press-event", self.on_button_press, self.menu)
self.main_window.show_all()
@ -132,6 +140,12 @@ class Hud:
for w in self.stat_windows:
self.stat_windows[w].window.move(self.stat_windows[w].x,
self.stat_windows[w].y)
def debug_stat_windows(self, *args):
print self.table, "\n", self.main_window.window.get_transient_for()
for w in self.stat_windows:
print self.stat_windows[w].window.window.get_transient_for()
def save_layout(self, *args):
new_layout = [(0, 0)] * self.max
# todo: have the hud track the poker table's window position regularly, don't forget to update table.x and table.y.
@ -215,7 +229,14 @@ class Hud:
this_stat = config.supported_games[self.poker_game].stats[self.stats[r][c]]
number = Stats.do_stat(stat_dict, player = stat_dict[s]['player_id'], stat = self.stats[r][c])
statstring = this_stat.hudprefix + str(number[1]) + this_stat.hudsuffix
if this_stat.hudcolor != "":
self.label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.colors['hudfgcolor']))
self.stat_windows[stat_dict[s]['seat']].label[r][c].modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(this_stat.hudcolor))
self.stat_windows[stat_dict[s]['seat']].label[r][c].set_text(statstring)
if statstring != "xxx":
self.stat_windows[stat_dict[s]['seat']].window.show_all()
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)
@ -281,6 +302,7 @@ class Stat_Window:
self.double_click(widget, event, *args)
if event.button == 2: # middle button event
self.window.hide()
# print "middle button clicked"
pass
@ -361,42 +383,14 @@ class Stat_Window:
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")
font = pango.FontDescription("Sans 7")
self.label[r][c].modify_font(font)
# if not os.name == 'nt': # seems to be a bug in opacity on windows
self.window.set_opacity(parent.colors['hudopacity'])
self.window.realize
self.window.move(self.x, self.y)
self.window.show_all()
# set_keep_above(1) for windows
if os.name == 'nt': self.topify_window(self.window)
def topify_window(self, window):
"""Set the specified gtk window to stayontop in MS Windows."""
def windowEnumerationHandler(hwnd, resultList):
'''Callback for win32gui.EnumWindows() to generate list of window handles.'''
resultList.append((hwnd, win32gui.GetWindowText(hwnd)))
unique_name = 'unique name for finding this window'
real_name = window.get_title()
window.set_title(unique_name)
tl_windows = []
win32gui.EnumWindows(windowEnumerationHandler, tl_windows)
for w in tl_windows:
if w[1] == unique_name:
#win32gui.SetWindowPos(w[0], win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE|win32con.SWP_NOSIZE)
# style = win32gui.GetWindowLong(w[0], win32con.GWL_EXSTYLE)
# style |= win32con.WS_EX_TOOLWINDOW
# style &= ~win32con.WS_EX_APPWINDOW
# win32gui.SetWindowLong(w[0], win32con.GWL_EXSTYLE, style)
win32gui.ShowWindow(w[0], win32con.SW_SHOW)
window.set_title(real_name)
self.window.hide()
def destroy(*args): # call back for terminating the main eventloop
gtk.main_quit()
@ -431,7 +425,7 @@ class Popup_window:
self.lab.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(stat_window.parent.colors['hudbgcolor']))
self.lab.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(stat_window.parent.colors['hudfgcolor']))
self.window.realize
# self.window.realize()
# figure out the row, col address of the click that activated the popup
row = 0
@ -564,7 +558,7 @@ if __name__== "__main__":
c = Configuration.Config()
#tables = Tables.discover(c)
t = Tables.discover_table_by_name(c, "Chelsea")
t = Tables.discover_table_by_name(c, "Motorway")
if t is None:
print "Table not found."
db = Database.Database(c, 'fpdb', 'holdem')

View File

@ -41,30 +41,47 @@ import Configuration
import Database
import Tables
import Hud
import Mucked
import HandHistory
class Mucked:
def __init__(self, parent, db_connection):
class Aux_Window:
def __init__(self, parent, config, db_name):
self.config = config
self.parent = parent #this is the parent of the mucked cards widget
self.db_connection = db_connection
self.db_name = db_name
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)
def update(self):
pass
class Stud_mucked(Aux_Window):
def __init__(self, parent, config, db_name):
self.config = config
self.parent = parent #this is the parent of the mucked cards widget
self.db_name = db_name
self.vbox = gtk.VBox()
self.parent.add(self.vbox)
self.mucked_list = Stud_list(self.vbox, config, db_name)
self.mucked_cards = Stud_cards(self.vbox, config, db_name)
self.mucked_list.mucked_cards = self.mucked_cards
self.parent.show_all()
def update(self, new_hand_id):
self.mucked_list.update(new_hand_id)
def update_data(self, new_hand_id):
self.mucked_list.update_data(new_hand_id)
class MuckedList:
def __init__(self, parent, db_connection):
def update_gui(self, new_hand_id):
self.mucked_list.update_gui(new_hand_id)
class Stud_list:
def __init__(self, parent, config, db_name):
self.parent = parent
self.db_connection = db_connection
self.config = config
self.db_name = db_name
# set up a scrolled window to hold the listbox
self.scrolled_window = gtk.ScrolledWindow()
@ -115,11 +132,25 @@ class MuckedList:
vadj.set_value(vadj.upper)
self.mucked_cards.update(new_hand_id)
class MuckedCards:
def __init__(self, parent, db_connection):
def update_data(self, new_hand_id):
self.info_row = ((new_hand_id, "xxxx", 0), )
self.mucked_cards.update_data(new_hand_id)
def update_gui(self, new_hand_id):
iter = self.liststore.append(self.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_gui(new_hand_id)
class Stud_cards:
def __init__(self, parent, config, db_name = 'fpdb'):
self.parent = parent #this is the parent of the mucked cards widget
self.db_connection = db_connection
self.config = config
self.db_name = db_name
self.card_images = self.get_card_images()
self.seen_cards = {}
@ -159,37 +190,99 @@ class MuckedCards:
self.parent.add(self.grid)
def translate_cards(self, old_cards):
pass
ranks = ('', '', '2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K', 'A')
for c in old_cards.keys():
for i in range(1, 8):
rank = 'card' + str(i) + 'Value'
suit = 'card' + str(i) + 'Suit'
key = 'hole_card_' + str(i)
if old_cards[c][rank] == 0:
old_cards[c][key] = 'xx'
else:
old_cards[c][key] = ranks[old_cards[c][rank]] + old_cards[c][suit]
return old_cards
def update(self, new_hand_id):
cards = self.db_connection.get_cards(new_hand_id)
db_connection = Database.Database(self.config, 'fpdb', '')
cards = db_connection.get_cards(new_hand_id)
self.clear()
cards = self.translate_cards(cards)
for c in cards.keys():
self.grid_contents[(1, cards[c]['seat_number'] - 1)].set_text(cards[c]['screen_name'])
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]] == "":
if not cards[c][i[1]] == "xx":
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'))
tips = []
action = db_connection.get_action_from_hand(new_hand_id)
for street in action:
temp = ''
for act in street:
temp = temp + act[0] + " " + act[1] + "s "
if act[2] > 0:
if act[2]%100 > 0:
temp = temp + "%4.2f\n" % (float(act[2])/100)
else:
temp = temp + "%d\n" % (act[2]/100)
else:
temp = temp + "\n"
tips.append(temp)
# action in tool tips for 3rd street cards
tip = "%s" % hh.BETTING.rounds[0]
## action in tool tips for 3rd street cards
for c in (0, 1, 2):
for r in range(0, self.rows):
self.eb[(c, r)].set_tooltip_text(tip)
self.eb[(c, r)].set_tooltip_text(tips[0])
# 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 round in range(1, len(tips)):
for r in range(0, self.rows):
self.eb[(round_to_col[round], r)].set_tooltip_text(tip)
self.eb[(round_to_col[round], r)].set_tooltip_text(tips[round])
db_connection.close_connection()
def update_data(self, new_hand_id):
db_connection = Database.Database(self.config, 'fpdb', '')
cards = db_connection.get_cards(new_hand_id)
self.clear()
self.cards = self.translate_cards(cards)
self.tips = []
action = db_connection.get_action_from_hand(new_hand_id)
for street in action:
temp = ''
for act in street:
temp = temp + act[0] + " " + act[1] + "s "
if act[2] > 0:
if act[2]%100 > 0:
temp = temp + "%4.2f\n" % (float(act[2])/100)
else:
temp = temp + "%d\n" % (act[2]/100)
else:
temp = temp + "\n"
self.tips.append(temp)
db_connection.close_connection()
def update_gui(self, new_hand_id):
for c in self.cards.keys():
self.grid_contents[(1, self.cards[c]['seat_number'] - 1)].set_text(self.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 self.cards[c][i[1]] == "xx":
self.seen_cards[(i[0], self.cards[c]['seat_number'] - 1)]. \
set_from_pixbuf(self.card_images[self.split_cards(self.cards[c][i[1]])])
## action in tool tips for 3rd street cards
for c in (0, 1, 2):
for r in range(0, self.rows):
self.eb[(c, r)].set_tooltip_text(self.tips[0])
# action in tools tips for later streets
round_to_col = (0, 3, 4, 5, 6)
for round in range(1, len(self.tips)):
for r in range(0, self.rows):
self.eb[(round_to_col[round], r)].set_tooltip_text(self.tips[round])
def split_cards(self, card):
return (card[0], card[1].upper())
@ -225,19 +318,19 @@ if __name__== "__main__":
# 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)
m.update_data(new_hand_id)
m.update_gui(new_hand_id)
return(True)
config = Configuration.Config()
db_connection = Database.Database(config, 'fpdb', '')
# db_connection = Database.Database(config, 'fpdb', '')
main_window = gtk.Window()
main_window.set_keep_above(True)
main_window.connect("destroy", destroy)
m = Mucked(main_window, db_connection)
aux_to_call = "Stud_mucked"
m = eval("%s(main_window, config, 'fpdb')" % aux_to_call)
main_window.show_all()
s_id = gobject.io_add_watch(sys.stdin, gobject.IO_IN, process_new_hand)
gtk.main()

View File

@ -24,8 +24,11 @@
import os
import sys
import datetime
import Configuration
import fpdb_db
import fpdb_import
import fpdb_simple
import FpdbSQLQueries
import unittest
@ -34,6 +37,7 @@ class TestSequenceFunctions(unittest.TestCase):
def setUp(self):
"""Configure MySQL settings/database and establish connection"""
self.c = Configuration.Config()
self.mysql_settings={ 'db-host':"localhost",
'db-backend':2,
'db-databaseName':"fpdbtest",
@ -44,7 +48,8 @@ class TestSequenceFunctions(unittest.TestCase):
self.mysql_settings['db-databaseName'], self.mysql_settings['db-user'],
self.mysql_settings['db-password'])
self.mysqldict = FpdbSQLQueries.FpdbSQLQueries('MySQL InnoDB')
self.mysqlimporter = fpdb_import.Importer(self, self.mysql_settings)
self.mysqlimporter = fpdb_import.Importer(self, self.mysql_settings, self.c)
self.mysqlimporter.setCallHud(False)
# """Configure Postgres settings/database and establish connection"""
# self.pg_settings={ 'db-host':"localhost", 'db-backend':3, 'db-databaseName':"fpdbtest", 'db-user':"fpdb", 'db-password':"fpdb"}
@ -69,6 +74,21 @@ class TestSequenceFunctions(unittest.TestCase):
self.result = self.mysql_db.cursor.execute("SHOW TABLES")
self.failUnless(self.result==13, "Number of tables in database incorrect. Expected 13 got " + str(self.result))
def testPokerStarsHHDate(self):
latest = "PokerStars Game #21969660557: Hold'em No Limit ($0.50/$1.00) - 2008/11/12 10:00:48 CET [2008/11/12 4:00:48 ET]"
previous = "PokerStars Game #21969660557: Hold'em No Limit ($0.50/$1.00) - 2008/08/17 - 01:14:43 (ET)"
older1 = "PokerStars Game #21969660557: Hold'em No Limit ($0.50/$1.00) - 2008/09/07 06:23:14 ET"
result = fpdb_simple.parseHandStartTime(older1, "ps")
self.failUnless(result==datetime.datetime(2008,9,7,11,23,14),
"Date incorrect, expected: 2008-09-07 11:23:14 got: " + str(result))
result = fpdb_simple.parseHandStartTime(latest, "ps")
self.failUnless(result==datetime.datetime(2008,11,12,15,00,48),
"Date incorrect, expected: 2008-11-12 15:00:48 got: " + str(result))
result = fpdb_simple.parseHandStartTime(previous, "ps")
self.failUnless(result==datetime.datetime(2008,8,17,6,14,43),
"Date incorrect, expected: 2008-08-17 01:14:43 got: " + str(result))
def testImportHandHistoryFiles(self):
"""Test import of single HH file"""
self.mysqlimporter.addImportFile("regression-test-files/hand-histories/ps-lhe-ring-3hands.txt")

View File

@ -237,18 +237,80 @@ class Sql:
GROUP BY HudCache.PlayerId
"""
# FROM HudCache, Hands
# WHERE HudCache.PlayerId in
# (SELECT PlayerId FROM HandsPlayers
# WHERE handId = %s)
# AND Hands.id = %s
# AND Hands.gametypeId = HudCache.gametypeId
# AND PlayerId LIKE %s
# HudCache.gametypeId AS gametypeId,
# activeSeats AS n_active,
# position AS position,
# HudCache.tourneyTypeId AS tourneyTypeId,
# same as above except stats are aggregated for all blind/limit levels
self.query['get_stats_from_hand_aggregated'] = """
SELECT HudCache.playerId AS player_id,
sum(HDs) AS n,
sum(street0VPI) AS vpip,
sum(street0Aggr) AS pfr,
sum(street0_3B4BChance) AS TB_opp_0,
sum(street0_3B4BDone) AS TB_0,
sum(street1Seen) AS saw_f,
sum(street1Seen) AS saw_1,
sum(street2Seen) AS saw_2,
sum(street3Seen) AS saw_3,
sum(street4Seen) AS saw_4,
sum(sawShowdown) AS sd,
sum(street1Aggr) AS aggr_1,
sum(street2Aggr) AS aggr_2,
sum(street3Aggr) AS aggr_3,
sum(street4Aggr) AS aggr_4,
sum(otherRaisedStreet1) AS was_raised_1,
sum(otherRaisedStreet2) AS was_raised_2,
sum(otherRaisedStreet3) AS was_raised_3,
sum(otherRaisedStreet4) AS was_raised_4,
sum(foldToOtherRaisedStreet1) AS f_freq_1,
sum(foldToOtherRaisedStreet2) AS f_freq_2,
sum(foldToOtherRaisedStreet3) AS f_freq_3,
sum(foldToOtherRaisedStreet4) AS f_freq_4,
sum(wonWhenSeenStreet1) AS w_w_s_1,
sum(wonAtSD) AS wmsd,
sum(stealAttemptChance) AS steal_opp,
sum(stealAttempted) AS steal,
sum(foldSbToStealChance) AS SBstolen,
sum(foldedSbToSteal) AS SBnotDef,
sum(foldBbToStealChance) AS BBstolen,
sum(foldedBbToSteal) AS BBnotDef,
sum(street1CBChance) AS CB_opp_1,
sum(street1CBDone) AS CB_1,
sum(street2CBChance) AS CB_opp_2,
sum(street2CBDone) AS CB_2,
sum(street3CBChance) AS CB_opp_3,
sum(street3CBDone) AS CB_3,
sum(street4CBChance) AS CB_opp_4,
sum(street4CBDone) AS CB_4,
sum(foldToStreet1CBChance) AS f_cb_opp_1,
sum(foldToStreet1CBDone) AS f_cb_1,
sum(foldToStreet2CBChance) AS f_cb_opp_2,
sum(foldToStreet2CBDone) AS f_cb_2,
sum(foldToStreet3CBChance) AS f_cb_opp_3,
sum(foldToStreet3CBDone) AS f_cb_3,
sum(foldToStreet4CBChance) AS f_cb_opp_4,
sum(foldToStreet4CBDone) AS f_cb_4,
sum(totalProfit) AS net,
sum(street1CheckCallRaiseChance) AS ccr_opp_1,
sum(street1CheckCallRaiseDone) AS ccr_1,
sum(street2CheckCallRaiseChance) AS ccr_opp_2,
sum(street2CheckCallRaiseDone) AS ccr_2,
sum(street3CheckCallRaiseChance) AS ccr_opp_3,
sum(street3CheckCallRaiseDone) AS ccr_3,
sum(street4CheckCallRaiseChance) AS ccr_opp_4,
sum(street4CheckCallRaiseDone) AS ccr_4
FROM HudCache, Hands
WHERE HudCache.PlayerId in
(SELECT PlayerId FROM HandsPlayers
WHERE handId = %s)
AND Hands.id = %s
AND HudCache.gametypeId in
(SELECT gt1.id from Gametypes gt1, Gametypes gt2, Hands
WHERE gt1.siteid = gt2.siteid
AND gt1.type = gt2.type
AND gt1.category = gt2.category
AND gt1.limittype = gt2.limittype
AND gt2.id = Hands.gametypeId
AND Hands.id = %s)
GROUP BY HudCache.PlayerId
"""
self.query['get_players_from_hand'] = """
SELECT HandsPlayers.playerId, seatNo, name
@ -288,15 +350,15 @@ class Sql:
order by seatNo
"""
# self.query['get_hand_info'] = """
# SELECT
# game_id,
# CONCAT(hole_card_1, hole_card_2, hole_card_3, hole_card_4, hole_card_5, hole_card_6, hole_card_7) AS hand,
# total_won-total_bet AS net
# FROM game_players
# WHERE game_id = %s AND player_id = 3
# """
self.query['get_action_from_hand'] = """
SELECT street, Players.name, HandsActions.action, HandsActions.amount, actionno
FROM Players, HandsActions, HandsPlayers
WHERE HandsActions.action != 'blind'
AND HandsPlayers.handid = %s
AND HandsPlayers.playerid = Players.id
AND HandsActions.handPlayerId = HandsPlayers.id
ORDER BY street, actionno
"""
if __name__== "__main__":
# just print the default queries and exit
s = Sql(game = 'razz', type = 'ptracks')

View File

@ -89,14 +89,14 @@ def vpip(stat_dict, player):
'v=%3.1f' % (100*stat) + '%',
'vpip=%3.1f' % (100*stat) + '%',
'(%d/%d)' % (stat_dict[player]['vpip'], stat_dict[player]['n']),
'vpip'
'Voluntarily Put In Pot %'
)
except: return (stat,
'%3.1f' % (0) + '%',
'w=%3.1f' % (0) + '%',
'wtsd=%3.1f' % (0) + '%',
'v=%3.1f' % (0) + '%',
'vpip=%3.1f' % (0) + '%',
'(%d/%d)' % (0, 0),
'wtsd'
'Voluntarily Put In Pot %'
)
def vpip_0(stat_dict, player):
@ -129,7 +129,7 @@ def pfr(stat_dict, player):
'p=%3.1f' % (100*stat) + '%',
'pfr=%3.1f' % (100*stat) + '%',
'(%d/%d)' % (stat_dict[player]['pfr'], stat_dict[player]['n']),
'pfr'
'Pre-Flop Raise %'
)
except:
return (stat,
@ -137,7 +137,7 @@ def pfr(stat_dict, player):
'p=%3.1f' % (0) + '%',
'pfr=%3.1f' % (0) + '%',
'(%d/%d)' % (0, 0),
'pfr'
'Pre-Flop Raise %'
)
def pfr_0(stat_dict, player):
@ -214,7 +214,7 @@ def saw_f(stat_dict, player):
'sf=%3.1f' % (100*stat) + '%',
'saw_f=%3.1f' % (100*stat) + '%',
'(%d/%d)' % (stat_dict[player]['saw_f'], stat_dict[player]['n']),
'saw_f'
'Flop Seen %'
)
except:
stat = 0.0
@ -225,7 +225,7 @@ def saw_f(stat_dict, player):
'sf=%3.1f' % (stat) + '%',
'saw_f=%3.1f' % (stat) + '%',
'(%d/%d)' % (num, den),
'saw_f'
'Flop Seen %'
)
def n(stat_dict, player):
@ -303,12 +303,11 @@ def f_SB_steal(stat_dict, player):
)
except:
return (stat,
'%3.1f' % (0) + '%',
'fSB=%3.1f' % (0) + '%',
'fSB_s=%3.1f' % (0) + '%',
'(%d/%d)' % (0, 0),
'% folded SB to steal'
)
'NA',
'fSB=NA',
'fSB_s=NA',
'0/0',
'% folded SB to steal')
def f_BB_steal(stat_dict, player):
""" Folded BB to steal."""
@ -324,12 +323,11 @@ def f_BB_steal(stat_dict, player):
)
except:
return (stat,
'%3.1f' % (0) + '%',
'fBB=%3.1f' % (0) + '%',
'fBB_s=%3.1f' % (0) + '%',
'(%d/%d)' % (0, 0),
'% folded BB to steal'
)
'NA',
'fBB=NA',
'fBB_s=NA',
'0/0',
'% folded BB to steal')
def three_B_0(stat_dict, player):
""" Three bet preflop/3rd."""
@ -454,7 +452,7 @@ def a_freq_4(stat_dict, player):
'a4=%3.1f' % (0) + '%',
'a_fq_4=%3.1f' % (0) + '%',
'(%d/%d)' % (0, 0),
'Aggression Freq flop/4th'
'Aggression Freq 7th'
)
def a_freq_123(stat_dict, player):

403
pyfpdb/Tables.py Normal file → Executable file
View File

@ -7,7 +7,6 @@ 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
@ -40,7 +39,35 @@ if os.name == 'nt':
# FreePokerTools modules
import Configuration
# 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:
def __init__(self, info = {}):
if info.has_key('number'): self.number = info['number']
if info.has_key('exe'): self.exe = info['exe']
if info.has_key('width'): self.width = info['width']
if info.has_key('height'): self.height = info['height']
if info.has_key('x'): self.x = info['x']
if info.has_key('y'): self.y = info['y']
if info.has_key('site'): self.site = info['site']
if info.has_key('title'): self.title = info['title']
if info.has_key('name'): self.name = info['name']
def __str__(self):
# __str__ method for testing
temp = 'TableWindow object\n'
@ -51,13 +78,10 @@ class Table_Window:
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
############################################################################
# Top-level discovery routines--these are the modules interface
def discover(c):
"""Dispatch routine for finding all potential poker client windows."""
if os.name == 'posix':
tables = discover_posix(c)
elif os.name == 'nt':
@ -66,86 +90,99 @@ def discover(c):
tables = discover_mac(c)
else:
tables = {}
return(tables)
return tables
def discover_table_by_name(c, tablename):
"""Dispatch routine for finding poker client windows with the given name."""
if os.name == 'posix':
table = discover_posix_by_name(c, tablename)
info = discover_posix_by_name(c, tablename)
elif os.name == 'nt':
table = discover_nt_by_name(c, tablename)
info = discover_nt_by_name(c, tablename)
elif os.name == 'mac':
table = discover_mac_by_name(c, tablename)
info = discover_mac_by_name(c, tablename)
else:
table = None
return(table)
return None
if info == None:
return None
return Table_Window(info)
def discover_tournament_table(c, tour_number, tab_number):
"""Dispatch routine for finding poker clients with tour and table number."""
if os.name == 'posix':
info = discover_posix_tournament(c, tour_number, tab_number)
elif os.name == 'nt':
info = discover_nt_tournament(c, tour_number, tab_number)
elif os.name == 'mac':
info = discover_mac_tournament(c, tour_number, tab_number)
else:
return None
if info:
return Table_Window(info)
return None
#############################################################################
# Posix (= XWindows) specific routines
def discover_posix(c):
""" Poker client table window finder for posix/Linux = XWindows."""
"""Poker client table window finder for posix/Linux = XWindows."""
tables = {}
for listing in os.popen('xwininfo -root -tree').readlines():
# xwininfo -root -tree -id 0xnnnnn gets the info on a single window
for s in c.get_supported_sites():
params = c.get_site_parameters(s)
if re.search(params['table_finder'], listing):
if re.search('Lobby', listing): continue
if re.search('Instant Hand History', listing): continue
if not re.search('Logged In as ', listing, re.IGNORECASE): continue
if re.search('\"Full Tilt Poker\"', listing): continue # FTP Lobby
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 = int(mo.group(1), 0)
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)
# use this eval thingie to call the title bar decoder specified in the config file
eval("%s(tw)" % c.supported_sites[s].decoder)
if re.search('History for table:', listing): continue
if re.search('has no name', listing): continue
info = decode_xwininfo(c, listing)
if info['site'] == None: continue
if info['title'] == info['exe']: continue
# this appears to be a poker client, so make a table object for it
tw = Table_Window(info)
eval("%s(tw)" % params['decoder'])
tables[tw.name] = tw
return tables
def discover_posix_by_name(c, tablename):
tables = discover_posix(c)
for t in tables:
if tables[t].name.find(tablename) > -1:
return tables[t]
"""Find an XWindows poker client of the given name."""
for listing in os.popen('xwininfo -root -tree').readlines():
if re.search(tablename, listing):
if re.search('History for table:', listing): continue
info = decode_xwininfo(c, listing)
if not info['name'] == tablename: continue
return info
return False
def discover_posix_tournament(c, t_number, s_number):
"""Finds the X window for a client, given tournament and table nos."""
search_string = "%s.+Table\s%s" % (t_number, s_number)
for listing in os.popen('xwininfo -root -tree').readlines():
if re.search(search_string, listing):
return decode_xwininfo(c, listing)
return False
def decode_xwininfo(c, info_string):
"""Gets window parameters from xwinifo string--XWindows."""
info = {}
mo = re.match('\s+([\dxabcdef]+) (.+):\s\(\"([a-zA-Z.]+)\".+ (\d+)x(\d+)\+\d+\+\d+ \+(\d+)\+(\d+)', info_string)
if not mo:
return None
else:
info['number'] = int( mo.group(1), 0)
info['exe'] = mo.group(3)
info['width'] = int( mo.group(4) )
info['height'] = int( mo.group(5) )
info['x'] = int( mo.group(6) )
info['y'] = int( mo.group(7) )
info['site'] = get_site_from_exe(c, info['exe'])
info['title'] = re.sub('\"', '', mo.group(2))
title_bits = re.split(' - ', info['title'])
info['name'] = clean_title(title_bits[0])
return info
#
# The discover_xx functions query the system and report on the poker clients
# currently displayed on the screen. The discover_posix should give you
# some idea how to support other systems.
#
# discover_xx() returns a dict of TableWindow objects--one TableWindow
# object for each poker client table on the screen.
#
# Each TableWindow object must have the following attributes correctly populated:
# tw.site = the site name, e.g. PokerStars, FullTilt. This must match the site
# name specified in the config file.
# tw.number = This is the system id number for the client table window in the
# format that the system presents it.
# tw.title = The full title from the window title bar.
# tw.width, tw.height = The width and height of the window in pixels. This is
# the internal width and height, not including the title bar and
# window borders.
# tw.x, tw.y = The x, y (horizontal, vertical) location of the window relative
# to the top left of the display screen. This also does not include the
# title bar and window borders. To put it another way, this is the
# screen location of (0, 0) in the working window.
def win_enum_handler(hwnd, titles):
titles[hwnd] = win32gui.GetWindowText(hwnd)
def child_enum_handler(hwnd, children):
print hwnd, win32.GetWindowRect(hwnd)
##############################################################################
# NT (= Windows) specific routines
def discover_nt(c):
""" Poker client table window finder for Windows."""
#
@ -185,72 +222,88 @@ def discover_nt(c):
return tables
def discover_nt_by_name(c, tablename):
# this is pretty much identical to the 'search all windows for all poker sites' code, but made to dig just for a specific table name
# it could be implemented a bunch better - and we need to not assume the width/height thing that (steffen?) assumed above, we should
# be able to dig up the window's titlebar handle and get it's information, and such .. but.. for now, i guess this will work.
# - eric
b_width = 3
tb_height = 29
"""Finds poker client window with the given table name."""
titles = {}
# tables = discover_nt(c)
win32gui.EnumWindows(win_enum_handler, titles)
for s in c.supported_sites.keys():
for hwnd in titles.keys():
if titles[hwnd].find(tablename) == -1: continue
if titles[hwnd].find("History for table:") > -1: continue
if titles[hwnd].find("HUD:") > -1: continue
if titles[hwnd].find("Chat:") > -1: continue
return decode_windows(c, titles[hwnd], hwnd)
return False
def discover_nt_tournament(c, tour_number, tab_number):
"""Finds the Windows window handle for the given tournament/table."""
search_string = "%s.+%s" % (tour_number, tab_number)
titles ={}
win32gui.EnumWindows(win_enum_handler, titles)
for hwnd in titles.keys():
if re.search(search_string, titles[hwnd]):
return decode_windows(c, titles[hwnd], hwnd)
return False
def get_nt_exe(hwnd):
"""Finds the name of the executable that the given window handle belongs to."""
processid = win32process.GetWindowThreadProcessId(hwnd)
pshandle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION | win32con.PROCESS_VM_READ, False, processid[1])
exe = win32process.GetModuleFileNameEx(pshandle, 0)
if exe.find(c.supported_sites[s].table_finder) == -1:
continue
if titles[hwnd].find(tablename) > -1:
if titles[hwnd].find("History for table:") > -1 or titles[hwnd].find("FPDBHUD") > -1:
continue
tw = Table_Window()
tw.number = hwnd
(x, y, width, height) = win32gui.GetWindowRect(hwnd)
tw.title = titles[hwnd]
tw.width = int(width) - 2 * b_width
tw.height = int(height) - b_width - tb_height
tw.x = int(x) + b_width
tw.y = int(y) + tb_height
tw.site = c.supported_sites[s].site_name
if not tw.site == "Unknown" and not c.supported_sites[tw.site].decoder == "Unknown":
eval("%s(tw)" % c.supported_sites[tw.site].decoder)
else:
tw.name = tablename
return tw
return win32process.GetModuleFileNameEx(pshandle, 0)
# if we don't find anything by process name, let's search one more time, and call it Unknown ?
for hwnd in titles.keys():
if titles[hwnd].find(tablename) > -1:
if titles[hwnd].find("History for table:") > -1 or titles[hwnd].find("FPDBHUD") > -1:
continue
tw = Table_Window()
tw.number = hwnd
(x, y, width, height) = win32gui.GetWindowRect(hwnd)
tw.title = titles[hwnd]
tw.width = int(width) - 2 * b_width
tw.height = int(height) - b_width - tb_height
tw.x = int(x) + b_width
tw.y = int(y) + tb_height
tw.site = "Unknown"
tw.name = tablename
return tw
def decode_windows(c, title, hwnd):
"""Gets window parameters from the window title and handle--Windows."""
# I cannot figure out how to get the inside dimensions of the poker table
# windows. So I just assume all borders are 3 thick and all title bars
# are 29 high. No doubt this will be off when used with certain themes.
b_width = 3
tb_height = 29
info = {}
info['number'] = hwnd
info['title'] = re.sub('\"', '', title)
(x, y, width, height) = win32gui.GetWindowRect(hwnd)
info['x'] = int(x) + b_width
info['y'] = int( y ) + tb_height
info['width'] = int( width ) - 2*b_width
info['height'] = int( height ) - b_width - tb_height
info['exe'] = get_nt_exe(hwnd)
title_bits = re.split(' - ', info['title'])
info['name'] = title_bits[0]
info['site'] = get_site_from_exe(c, info['exe'])
return info
def win_enum_handler(hwnd, titles):
titles[hwnd] = win32gui.GetWindowText(hwnd)
###################################################################
# Utility routines used by all the discoverers.
def get_site_from_exe(c, exe):
"""Look up the site from config, given the exe."""
for s in c.get_supported_sites():
params = c.get_site_parameters(s)
if re.search(params['table_finder'], exe):
return params['site_name']
return None
def discover_mac(c):
""" Poker client table window finder for Macintosh."""
tables = {}
return tables
def discover_mac_by_name(c, tablename):
# again, i have no mac to test this on, sorry -eric
return discover_mac(c)
def clean_title(name):
"""Clean the little info strings from the table name."""
# these strings could go in a config file
for pattern in [' \(6 max\)', ' \(heads up\)', ' \(deep\)',
' \(deep hu\)', ' \(deep 6\)', ' \(2\)',
' \(edu\)', ' \(edu, 6 max\)', ' \(6\)',
' no all-in', ' fast', ',', ' 50BB min', '\s+$']:
name = re.sub(pattern, '', name)
name = name.rstrip()
return name
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
# Extract poker information from the window title. This is not needed for
# fpdb, since all that information is available in the db via new_hand_number.
# This is needed only when using the HUD with a backend less integrated.
title_bits = re.split(' - ', tw.title)
name = title_bits[0]
mo = re.search('Tournament (\d+) Table (\d+)', name)
@ -260,12 +313,8 @@ def pokerstars_decode_table(tw):
tw.name = name
else:
tw.tournament = None
for pattern in [' no all-in', ' fast', ',', ' 50BB min']:
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)
tw.name = clean_title(name)
mo = re.search('(Razz|Stud H/L|Stud|Omaha H/L|Omaha|Hold\'em|5-Card Draw|Triple Draw 2-7 Lowball|Badugi)', tw.title)
tw.game = mo.group(1).lower()
tw.game = re.sub('\'', '', tw.game)
@ -288,25 +337,103 @@ def pokerstars_decode_table(tw):
pass
def fulltilt_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
# Extract poker information from the window title. This is not needed for
# fpdb, since all that information is available in the db via new_hand_number.
# This is needed only when using the HUD with a backend less integrated.
title_bits = re.split(' - ', tw.title)
name = title_bits[0]
tw.tournament = None
for pattern in [' (6 max)', ' (heads up)', ' (deep)',
' (deep hu)', ' (deep 6)', ' (2)',
' (edu)', ' (edu, 6 max)', ' (6)' ]:
tw.name = clean_title(name)
def clean_title(name):
"""Clean the little info strings from the table name."""
# these strings could go in a config file
for pattern in [' \(6 max\)', ' \(heads up\)', ' \(deep\)',
' \(deep hu\)', ' \(deep 6\)', ' \(2\)',
' \(edu\)', ' \(edu, 6 max\)', ' \(6\)',
' no all-in', ' fast', ',', ' 50BB min', '\s+$']:
name = re.sub(pattern, '', name)
# (tw.name, trash) = name.split(r' (', 1)
tw.name = name.rstrip()
name = name.rstrip()
return name
###########################################################################
# Mac specific routines....all stubs for now
def discover_mac_tournament(c, tour_number, tab_number):
"""Mac users need help."""
return None
def discover_mac(c):
"""Poker client table window finder for Macintosh."""
tables = {}
return tables
def discover_mac_by_name(c, tablename):
"""Oh, the humanity."""
# again, i have no mac to test this on, sorry -eric
return None
#def discover_nt_by_name(c, tablename):
# # this is pretty much identical to the 'search all windows for all poker sites' code, but made to dig just for a specific table name
# # it could be implemented a bunch better - and we need to not assume the width/height thing that (steffen?) assumed above, we should
# # be able to dig up the window's titlebar handle and get it's information, and such .. but.. for now, i guess this will work.
# # - eric
# b_width = 3
# tb_height = 29
# titles = {}
## tables = discover_nt(c)
# win32gui.EnumWindows(win_enum_handler, titles)
# for s in c.supported_sites.keys():
# for hwnd in titles.keys():
# processid = win32process.GetWindowThreadProcessId(hwnd)
# pshandle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION | win32con.PROCESS_VM_READ, False, processid[1])
# exe = win32process.GetModuleFileNameEx(pshandle, 0)
# if exe.find(c.supported_sites[s].table_finder) == -1:
# continue
# if titles[hwnd].find(tablename) > -1:
# if titles[hwnd].find("History for table:") > -1 or titles[hwnd].find("FPDBHUD") > -1:
# continue
# tw = Table_Window()
# tw.number = hwnd
# (x, y, width, height) = win32gui.GetWindowRect(hwnd)
# tw.title = titles[hwnd]
# tw.width = int(width) - 2 * b_width
# tw.height = int(height) - b_width - tb_height
# tw.x = int(x) + b_width
# tw.y = int(y) + tb_height
# tw.site = c.supported_sites[s].site_name
# if not tw.site == "Unknown" and not c.supported_sites[tw.site].decoder == "Unknown":
# eval("%s(tw)" % c.supported_sites[tw.site].decoder)
# else:
# tw.name = tablename
# return tw
#
# # if we don't find anything by process name, let's search one more time, and call it Unknown ?
# for hwnd in titles.keys():
# if titles[hwnd].find(tablename) > -1:
# if titles[hwnd].find("History for table:") > -1 or titles[hwnd].find("FPDBHUD") > -1:
# continue
# tw = Table_Window()
# tw.number = hwnd
# (x, y, width, height) = win32gui.GetWindowRect(hwnd)
# tw.title = titles[hwnd]
# tw.width = int(width) - 2 * b_width
# tw.height = int(height) - b_width - tb_height
# tw.x = int(x) + b_width
# tw.y = int(y) + tb_height
# tw.site = "Unknown"
# tw.name = tablename
# return tw
#
# return None
if __name__=="__main__":
c = Configuration.Config()
print discover_table_by_name(c, "Catacaos")
tables = discover(c)
print discover_table_by_name(c, "Howard Lederer")
print discover_tournament_table(c, "118942908", "3")
tables = discover(c)
for t in tables.keys():
print "t = ", t
print tables[t]
print "press enter to continue"

View File

@ -37,6 +37,7 @@ import gtk
import fpdb_db
import fpdb_simple
import GuiBulkImport
import GuiPlayerStats
import GuiTableViewer
import GuiAutoImport
import GuiGraphViewer
@ -117,12 +118,12 @@ class fpdb:
def dia_create_del_database(self, widget, data):
print "todo: implement dia_create_del_database"
obtain_global_lock()
self.obtain_global_lock()
#end def dia_create_del_database
def dia_create_del_user(self, widget, data):
print "todo: implement dia_create_del_user"
obtain_global_lock()
self.obtain_global_lock()
#end def dia_create_del_user
def dia_database_stats(self, widget, data):
@ -132,17 +133,17 @@ class fpdb:
def dia_delete_db_parts(self, widget, data):
print "todo: implement dia_delete_db_parts"
obtain_global_lock()
self.obtain_global_lock()
#end def dia_delete_db_parts
def dia_edit_profile(self, widget=None, data=None, create_default=False, path=None):
print "todo: implement dia_edit_profile"
obtain_global_lock()
self.obtain_global_lock()
#end def dia_edit_profile
def dia_export_db(self, widget, data):
print "todo: implement dia_export_db"
obtain_global_lock()
self.obtain_global_lock()
#end def dia_export_db
def dia_get_db_root_credentials(self):
@ -167,7 +168,7 @@ class fpdb:
def dia_import_db(self, widget, data):
print "todo: implement dia_import_db"
obtain_global_lock()
self.obtain_global_lock()
#end def dia_import_db
def dia_licensing(self, widget, data):
@ -263,7 +264,11 @@ class fpdb:
self.db = fpdb_db.fpdb_db()
#print "end of fpdb.load_profile, databaseName:",self.settings['db-databaseName']
self.db.connect(self.settings['db-backend'], self.settings['db-host'], self.settings['db-databaseName'], self.settings['db-user'], self.settings['db-password'])
self.db.connect(self.settings['db-backend'],
self.settings['db-host'],
self.settings['db-databaseName'],
self.settings['db-user'],
self.settings['db-password'])
if self.db.wrongDbVersion:
diaDbVersionWarning = gtk.Dialog(title="Strong Warning - Invalid database version", parent=None, flags=0, buttons=(gtk.STOCK_OK,gtk.RESPONSE_OK))
@ -326,6 +331,13 @@ class fpdb:
self.add_and_display_tab(bulk_tab, "Bulk Import")
#end def tab_bulk_import
def tab_player_stats(self, widget, data):
new_ps_thread=GuiPlayerStats.GuiPlayerStats(self.db, self.config, self.querydict)
self.threads.append(new_ps_thread)
ps_tab=new_ps_thread.get_vbox()
self.add_and_display_tab(ps_tab, "Player Stats")
def tab_main_help(self, widget, data):
"""Displays a tab with the main fpdb help screen"""
#print "start of tab_main_help"
@ -384,7 +396,7 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt")
("/Viewers/_Graphs", "<control>G", self.tabGraphViewer, 0, None ),
("/Viewers/Hand _Replayer (todo)", None, self.not_implemented, 0, None ),
("/Viewers/Player _Details (todo)", None, self.not_implemented, 0, None ),
("/Viewers/_Player Stats (tabulated view) (todo)", None, self.not_implemented, 0, None ),
("/Viewers/_Player Stats (tabulated view)", None, self.tab_player_stats, 0, None ),
("/Viewers/Starting _Hands (todo)", None, self.not_implemented, 0, None ),
("/Viewers/_Session Replayer (todo)", None, self.not_implemented, 0, None ),
("/Viewers/Poker_table Viewer (mostly obselete)", "<control>T", self.tab_table_viewer, 0, None ),

View File

@ -31,24 +31,37 @@ class fpdb_db:
self.SQLITE=4
#end def __init__
def connect(self, backend, host, database, user, password):
def connect(self, backend=None, host=None, database=None,
user=None, password=None):
"""Connects a database with the given parameters"""
if backend is None:
raise FpdbError('Database backend not defined')
self.backend=backend
self.host=host
self.database=database
self.user=user
self.password=password
self.database=database
if backend==self.MYSQL_INNODB:
import MySQLdb
self.db=MySQLdb.connect(host = host, user = user, passwd = password, db = database)
elif backend==self.PGSQL:
import psycopg2
self.db = psycopg2.connect(host = host, user = user, password = password, database = database)
# If DB connection is made over TCP, then the variables
# host, user and password are required
if self.host or self.user:
self.db = psycopg2.connect(host = host,
user = user,
password = password,
database = database)
# For local domain-socket connections, only DB name is
# needed, and everything else is in fact undefined and/or
# flat out wrong
else:
self.db = psycopg2.connect(database = database)
else:
raise fpdb_simple.FpdbError("unrecognised database backend:"+backend)
self.cursor=self.db.cursor()
self.cursor.execute('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED')
# Set up query dictionary as early in the connection process as we can.
self.sql = FpdbSQLQueries.FpdbSQLQueries(self.get_backend_name())
self.wrongDbVersion=False

View File

@ -31,6 +31,7 @@ try:
except:
pass
import traceback
import math
import os
import datetime
@ -61,6 +62,8 @@ class Importer:
self.settings['minPrint'] = 30
self.dbConnect()
# XXX: Why is this here, when fpdb_db.connect() already does the
# same?
def dbConnect(self):
#connect to DB
if self.settings['db-backend'] == 2:
@ -72,10 +75,15 @@ class Importer:
if not pgsqlLibFound:
raise fpdb_simple.FpdbError("interface library psycopg2 not found but PostgreSQL selected as backend - please install the library or change the config file")
print self.settings
if not self.settings.has_key('db-host') or \
not self.settings.has_key('db-user'):
self.db = psycopg2.connect(host = self.settings['db-host'],
user = self.settings['db-user'],
password = self.settings['db-password'],
database = self.settings['db-databaseName'])
else:
dbname = self.settings['db-databaseName']
self.db = psycopg2.connect(database = dbname)
elif self.settings['db-backend'] == 4:
pass
else:
@ -113,8 +121,7 @@ class Importer:
#Only one import directory per site supported.
#dirlist is a hash of lists:
#dirlist{ 'PokerStars' => ["/path/to/import/", "filtername"] }
def addImportDirectory(self, dir, monitor = False, site = "default", filter = "passthrough"):
if dir != "/dev/null" and dir.lower() != "none":
def addImportDirectory(self,dir,monitor = False, site = "default", filter = "passthrough"):
if os.path.isdir(dir):
if monitor == True:
self.monitor = True
@ -123,7 +130,7 @@ class Importer:
for file in os.listdir(dir):
self.addImportFile(os.path.join(dir, file), site, filter)
else:
print "Warning: Attempted to add: '" + str(dir) + "' as an import directory\n"
print "Warning: Attempted to add: '" + str(dir) + "' as an import directory"
#Run full import on filelist
def runImport(self):
@ -249,14 +256,14 @@ class Importer:
duplicates+=1
except (ValueError), fe:
errors+=1
self.printEmailErrorMessage(errors, file, hand[0])
self.printEmailErrorMessage(errors, file, hand)
if (self.settings['failOnError']):
self.db.commit() #dont remove this, in case hand processing was cancelled.
raise
except (fpdb_simple.FpdbError), fe:
errors+=1
self.printEmailErrorMessage(errors, file, hand[0])
self.printEmailErrorMessage(errors, file, hand)
#fe.printStackTrace() #todo: get stacktrace
self.db.rollback()
@ -272,11 +279,10 @@ class Importer:
if ((stored+duplicates+partial+errors)>=self.settings['handCount']):
if (not self.settings['quiet']):
print "quitting due to reaching the amount of hands to be imported"
print "Total stored:", stored, "duplicates:", duplicates, "partial/damaged:",
partial, "errors:", errors, " time: %5.3f" % (time() - starttime)
print "Total stored:", stored, "duplicates:", duplicates, "partial/damaged:", partial, "errors:", errors, " time:", (time() - starttime)
sys.exit(0)
startpos=endpos
print "Total stored:", stored, "duplicates:", duplicates, "partial:", partial, "errors:", errors, " time: %5.3f" % (time() - starttime)
print "Total stored:", stored, "duplicates:", duplicates, "partial:", partial, "errors:", errors, " time:", (time() - starttime)
if stored==0:
if duplicates>0:
@ -300,11 +306,17 @@ class Importer:
def printEmailErrorMessage(self, errors, filename, line):
traceback.print_exc(file=sys.stderr)
print "Error No.",errors,", please send the hand causing this to steffen@sycamoretest.info so I can fix it."
print "Filename:", filename
print "Here is the first line so you can identify it. Please mention that the error was a ValueError:"
print self.hand[0]
print "Hand logged to hand-errors.txt"
logfile = open('hand-errors.txt', 'a')
for s in self.hand:
logfile.write(str(s) + "\n")
logfile.write("\n")
logfile.close()
if __name__ == "__main__":
print "CLI for fpdb_import is now available as CliFpdb.py"

View File

@ -42,6 +42,8 @@ def mainParser(db, cursor, site, category, hand):
smallBlindLine=0
for i in range(len(hand)):
if hand[i].find("posts small blind")!=-1 or hand[i].find("posts the small blind")!=-1:
if hand[i][-2:] == "$0":
continue
smallBlindLine=i
#print "found small blind line:",smallBlindLine
break

124
pyfpdb/fpdb_simple.py Normal file → Executable file
View File

@ -54,7 +54,8 @@ def checkPositions(positions):
pass
### RHH modified to allow for "position 9" here (pos==9 is when you're a dead hand before the BB
if (pos!="B" and pos!="S" and pos!=0 and pos!=1 and pos!=2 and pos!=3 and pos!=4 and pos!=5 and pos!=6 and pos!=7 and pos!=9):
### eric - position 8 could be valid - if only one blind is posted, but there's still 10 people, ie a sitout is present, and the small is dead...
if not (pos == "B" or pos == "S" or (pos >= 0 and pos <= 9)):
raise FpdbError("invalid position found in checkPositions. i: "+str(i)+" position: "+str(pos))
#end def fpdb_simple.checkPositions
@ -484,7 +485,6 @@ def isActionLine(line):
#returns whether this is a duplicate
def isAlreadyInDB(cursor, gametypeID, siteHandNo):
#print "isAlreadyInDB gtid,shand:",gametypeID, siteHandNo
cursor.execute ("SELECT id FROM Hands WHERE gametypeId=%s AND siteHandNo=%s", (gametypeID, siteHandNo))
result=cursor.fetchall()
if (len(result)>=1):
@ -735,7 +735,7 @@ def parseCardLine(site, category, street, line, names, cardValues, cardSuits, bo
print "line:",line,"cardValues[playerNo]:",cardValues[playerNo]
raise FpdbError("read too many/too few holecards in parseCardLine")
elif (category=="razz" or category=="studhi" or category=="studhilo"):
if (line.find("shows")==-1):
if (line.find("shows")==-1 and line.find("mucked") == -1):
#print "parseCardLine(in stud if), street:", street
if line[pos+2]=="]": #-> not (hero and 3rd street)
cardValues[playerNo][street+2]=line[pos:pos+1]
@ -832,14 +832,16 @@ def parseHandStartTime(topline, site):
tmp=topline[pos1:pos2]
isUTC=True
else:
tmp=topline[-30:]
#print "parsehandStartTime, tmp:", tmp
tmp=topline
# print "parsehandStartTime, tmp:", tmp
pos = tmp.find("-")+2
tmp = tmp[pos:]
#Need to match either
# 2008/09/07 06:23:14 ET or
# 2008/08/17 - 01:14:43 (ET)
m = re.match('(?P<YEAR>[0-9]{4})\/(?P<MON>[0-9]{2})\/(?P<DAY>[0-9]{2})[\- ]+(?P<HR>[0-9]{2}):(?P<MIN>[0-9]{2}):(?P<SEC>[0-9]{2})',tmp)
# 2008/08/17 - 01:14:43 (ET) or
# 2008/11/12 9:33:31 CET [2008/11/12 3:33:31 ET]
rexx = '(?P<YEAR>[0-9]{4})\/(?P<MON>[0-9]{2})\/(?P<DAY>[0-9]{2})[\- ]+(?P<HR>[0-9]+):(?P<MIN>[0-9]+):(?P<SEC>[0-9]+)'
m = re.search(rexx,tmp)
#print "year:", int(m.group('YEAR')), "month", int(m.group('MON')), "day", int(m.group('DAY')), "hour", int(m.group('HR')), "minute", int(m.group('MIN')), "second", int(m.group('SEC'))
result = datetime.datetime(int(m.group('YEAR')), int(m.group('MON')), int(m.group('DAY')), int(m.group('HR')), int(m.group('MIN')), int(m.group('SEC')))
else:
@ -892,6 +894,11 @@ def parsePositions (hand, names):
if (bb!=-1):
bb=recognisePlayerNo(bb, names, "bet")
# print "sb = ", sb, "bb = ", bb
if bb == sb:
sbExists = False
sb = -1
#write blinds into array
if (sbExists):
positions[sb]="S"
@ -908,16 +915,25 @@ def parsePositions (hand, names):
positions[arraypos]=distFromBtn
arraypos-=1
distFromBtn+=1
# eric - this takes into account dead seats between blinds
if sbExists:
i = bb - 1
while positions[i] < 0 and i != sb:
positions[i] = 9
i -= 1
### RHH - Changed to set the null seats before BB to "9"
i=bb-1
if sbExists:
i = sb-1
else:
i = bb-1
while positions[i] < 0:
positions[i]=9
i-=1
arraypos=len(names)-1
if (bb!=0 or (bb==0 and sbExists==False)):
while (arraypos>bb):
if (bb!=0 or (bb==0 and sbExists==False) or (bb == 1 and sb != arraypos) ):
while (arraypos>bb and arraypos > sb):
positions[arraypos]=distFromBtn
arraypos-=1
distFromBtn+=1
@ -927,6 +943,7 @@ def parsePositions (hand, names):
print "parsePositions names:",names
print "result:",positions
raise FpdbError ("failed to read positions")
#print str(positions), "\n"
return positions
#end def parsePositions
@ -1022,7 +1039,7 @@ def recogniseCategory(line):
#end def recogniseCategory
#returns the int for the gametype_id for the given line
def recogniseGametypeID(db, cursor, topline, smallBlindLine, site_id, category, isTourney):#todo: this method is messy
def recogniseGametypeID(cursor, topline, smallBlindLine, site_id, category, isTourney):#todo: this method is messy
#if (topline.find("HORSE")!=-1):
# raise FpdbError("recogniseGametypeID: HORSE is not yet supported.")
@ -1073,9 +1090,6 @@ def recogniseGametypeID(db, cursor, topline, smallBlindLine, site_id, category,
else:
cursor.execute ("SELECT id FROM Gametypes WHERE siteId=%s AND type=%s AND category=%s AND limitType=%s AND smallBlind=%s AND bigBlind=%s", (site_id, type, category, limit_type, small_bet, big_bet))
result=cursor.fetchone()
#print "recgt1 result=",result
#ret=result[0]
#print "recgt1 ret=",ret
#print "tried SELECTing gametypes.id, result:",result
try:
@ -1109,18 +1123,16 @@ def recogniseGametypeID(db, cursor, topline, smallBlindLine, site_id, category,
cursor.execute("""INSERT INTO Gametypes
(siteId, type, base, category, limitType, hiLo, smallBlind, bigBlind, smallBet, bigBet)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""", (site_id, type, base, category, limit_type, hiLo, small_blind, big_blind, small_bet, big_bet))
#cursor.execute ("SELECT id FROM Gametypes WHERE siteId=%s AND type=%s AND category=%s AND limitType=%s AND smallBet=%s AND bigBet=%s", (site_id, type, category, limit_type, small_bet, big_bet))
cursor.execute ("SELECT id FROM Gametypes WHERE siteId=%s AND type=%s AND category=%s AND limitType=%s AND smallBet=%s AND bigBet=%s", (site_id, type, category, limit_type, small_bet, big_bet))
else:
cursor.execute("""INSERT INTO Gametypes
(siteId, type, base, category, limitType, hiLo, smallBlind, bigBlind, smallBet, bigBet)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""", (site_id, type, base, category, limit_type, hiLo, small_bet, big_bet, 0, 0))#remember, for these bet means blind
#cursor.execute ("SELECT id FROM Gametypes WHERE siteId=%s AND type=%s AND category=%s AND limitType=%s AND smallBlind=%s AND bigBlind=%s", (site_id, type, category, limit_type, small_bet, big_bet))
cursor.execute ("SELECT id FROM Gametypes WHERE siteId=%s AND type=%s AND category=%s AND limitType=%s AND smallBlind=%s AND bigBlind=%s", (site_id, type, category, limit_type, small_bet, big_bet))
result=(db.insert_id(),)
#print "recgt2 result=",result
result=cursor.fetchone()
#print "created new gametypes.id:",result
#print "recgt3: result=", result
return result[0]
#end def recogniseGametypeID
@ -1241,8 +1253,7 @@ def storeActions(cursor, handsPlayersIds, actionTypes, allIns, actionAmounts, ac
for i in range (len(actionTypes)): #iterate through streets
for j in range (len(actionTypes[i])): #iterate through names
for k in range (len(actionTypes[i][j])): #iterate through individual actions of that player on that street
cursor.execute ("INSERT INTO HandsActions (handPlayerId, street, actionNo, action, allIn, amount) VALUES (%s, %s, %s, %s, %s, %s)"
, (handsPlayersIds[j], i, actionNos[i][j][k], actionTypes[i][j][k], allIns[i][j][k], actionAmounts[i][j][k]))
cursor.execute ("INSERT INTO HandsActions (handPlayerId, street, actionNo, action, allIn, amount) VALUES (%s, %s, %s, %s, %s, %s)", (handsPlayersIds[j], i, actionNos[i][j][k], actionTypes[i][j][k], allIns[i][j][k], actionAmounts[i][j][k]))
#end def storeActions
def store_board_cards(cursor, hands_id, board_values, board_suits):
@ -1255,16 +1266,15 @@ def store_board_cards(cursor, hands_id, board_values, board_suits):
board_values[4], board_suits[4]))
#end def store_board_cards
def storeHands(db, cursor, site_hand_no, gametype_id, hand_start_time, names, tableName, maxSeats):
def storeHands(cursor, site_hand_no, gametype_id, hand_start_time, names, tableName, maxSeats):
#stores into table hands
cursor.execute ("INSERT INTO Hands (siteHandNo, gametypeId, handStart, seats, tableName, importTime, maxSeats) VALUES (%s, %s, %s, %s, %s, %s, %s)", (site_hand_no, gametype_id, hand_start_time, len(names), tableName, datetime.datetime.today(), maxSeats))
#todo: find a better way of doing this...
#cursor.execute("SELECT id FROM Hands WHERE siteHandNo=%s AND gametypeId=%s", (site_hand_no, gametype_id))
#return cursor.fetchall()[0][0]
return db.insert_id() # mysql only
cursor.execute("SELECT id FROM Hands WHERE siteHandNo=%s AND gametypeId=%s", (site_hand_no, gametype_id))
return cursor.fetchall()[0][0]
#end def storeHands
def store_hands_players_holdem_omaha(db, cursor, category, hands_id, player_ids, start_cashes, positions, card_values, card_suits, winnings, rakes, seatNos):
def store_hands_players_holdem_omaha(cursor, category, hands_id, player_ids, start_cashes, positions, card_values, card_suits, winnings, rakes, seatNos):
result=[]
if (category=="holdem"):
for i in range (len(player_ids)):
@ -1276,9 +1286,8 @@ def store_hands_players_holdem_omaha(db, cursor, category, hands_id, player_ids,
(hands_id, player_ids[i], start_cashes[i], positions[i],
card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1],
winnings[i], rakes[i], seatNos[i]))
#cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId=%s", (hands_id, player_ids[i]))
#result.append(cursor.fetchall()[0][0])
result.append( db.insert_id() ) # mysql only
cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i]))
result.append(cursor.fetchall()[0][0])
elif (category=="omahahi" or category=="omahahilo"):
for i in range (len(player_ids)):
cursor.execute ("""INSERT INTO HandsPlayers
@ -1290,15 +1299,14 @@ def store_hands_players_holdem_omaha(db, cursor, category, hands_id, player_ids,
card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1],
card_values[i][2], card_suits[i][2], card_values[i][3], card_suits[i][3],
winnings[i], rakes[i], seatNos[i]))
#cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i]))
#result.append(cursor.fetchall()[0][0])
result.append( db.insert_id() ) # mysql only
cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i]))
result.append(cursor.fetchall()[0][0])
else:
raise FpdbError("invalid category")
return result
#end def store_hands_players_holdem_omaha
def store_hands_players_stud(db, cursor, hands_id, player_ids, start_cashes, antes,
def store_hands_players_stud(cursor, hands_id, player_ids, start_cashes, antes,
card_values, card_suits, winnings, rakes, seatNos):
#stores hands_players rows for stud/razz games. returns an array of the resulting IDs
result=[]
@ -1317,14 +1325,12 @@ def store_hands_players_stud(db, cursor, hands_id, player_ids, start_cashes, ant
card_values[i][2], card_suits[i][2], card_values[i][3], card_suits[i][3],
card_values[i][4], card_suits[i][4], card_values[i][5], card_suits[i][5],
card_values[i][6], card_suits[i][6], winnings[i], rakes[i], seatNos[i]))
#cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i]))
#result.append(cursor.fetchall()[0][0])
result.append( db.insert_id() ) # mysql only
cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i]))
result.append(cursor.fetchall()[0][0])
return result
#end def store_hands_players_stud
def store_hands_players_holdem_omaha_tourney(db, cursor, category, hands_id, player_ids,
start_cashes, positions, card_values, card_suits, winnings, rakes, seatNos, tourneys_players_ids):
def store_hands_players_holdem_omaha_tourney(cursor, category, hands_id, player_ids, start_cashes, positions, card_values, card_suits, winnings, rakes, seatNos, tourneys_players_ids):
#stores hands_players for tourney holdem/omaha hands
result=[]
for i in range (len(player_ids)):
@ -1350,14 +1356,13 @@ def store_hands_players_holdem_omaha_tourney(db, cursor, category, hands_id, pla
winnings[i], rakes[i], tourneys_players_ids[i], seatNos[i]))
else:
raise FpdbError ("invalid card_values length:"+str(len(card_values[0])))
#cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i]))
#result.append(cursor.fetchall()[0][0])
result.append( db.insert_id() ) # mysql only
cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i]))
result.append(cursor.fetchall()[0][0])
return result
#end def store_hands_players_holdem_omaha_tourney
def store_hands_players_stud_tourney(db, cursor, hands_id, player_ids, start_cashes,
def store_hands_players_stud_tourney(cursor, hands_id, player_ids, start_cashes,
antes, card_values, card_suits, winnings, rakes, seatNos, tourneys_players_ids):
#stores hands_players for tourney stud/razz hands
result=[]
@ -1375,18 +1380,13 @@ def store_hands_players_stud_tourney(db, cursor, hands_id, player_ids, start_cas
card_values[i][2], card_suits[i][2], card_values[i][3], card_suits[i][3],
card_values[i][4], card_suits[i][4], card_values[i][5], card_suits[i][5],
card_values[i][6], card_suits[i][6], winnings[i], rakes[i], tourneys_players_ids[i], seatNos[i]))
#cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i]))
#result.append(cursor.fetchall()[0][0])
result.append( db.insert_id() ) # mysql only
cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId=%s", (hands_id, player_ids[i]))
result.append(cursor.fetchall()[0][0])
return result
#end def store_hands_players_stud_tourney
def generateHudCacheData(player_ids, base, category, action_types, allIns, actionTypeByNo
,winnings, totalWinnings, positions, actionTypes, actionAmounts):
"""calculates data for the HUD during import. IMPORTANT: if you change this method make
sure to also change the following storage method and table_viewer.prepare_data if necessary
"""
#print "generateHudCacheData, len(player_ids)=", len(player_ids)
def generateHudCacheData(player_ids, base, category, action_types, allIns, actionTypeByNo, winnings, totalWinnings, positions):
"""calculates data for the HUD during import. IMPORTANT: if you change this method make sure to also change the following storage method and table_viewer.prepare_data if necessary"""
#setup subarrays of the result dictionary.
street0VPI=[]
street0Aggr=[]
@ -1692,7 +1692,7 @@ def generateHudCacheData(player_ids, base, category, action_types, allIns, actio
hudDataPositions.append('C')
elif pos>=2 and pos<=4:
hudDataPositions.append('M')
elif pos>=5 and pos<=7:
elif pos>=5 and pos<=8:
hudDataPositions.append('E')
### RHH Added this elif to handle being a dead hand before the BB (pos==9)
elif pos==9:
@ -1906,14 +1906,8 @@ def generateHudCacheData(player_ids, base, category, action_types, allIns, actio
street3CheckCallRaiseDone=[]
street4CheckCallRaiseChance=[]
street4CheckCallRaiseDone=[]
#print "b4 totprof calc, len(playerIds)=", len(player_ids)
for pl in range (len(player_ids)):
#print "pl=", pl
myTotalProfit=winnings[pl] # still need to deduct costs
for i in range (len(actionTypes)): #iterate through streets
#for j in range (len(actionTypes[i])): #iterate through names (using pl loop above)
for k in range (len(actionTypes[i][pl])): #iterate through individual actions of that player on that street
myTotalProfit -= actionAmounts[i][pl][k]
for player in range (len(player_ids)):
myTotalProfit=0
myStreet1CheckCallRaiseChance=False
myStreet1CheckCallRaiseDone=False
@ -1924,9 +1918,7 @@ def generateHudCacheData(player_ids, base, category, action_types, allIns, actio
myStreet4CheckCallRaiseChance=False
myStreet4CheckCallRaiseDone=False
#print "myTotalProfit=", myTotalProfit
totalProfit.append(myTotalProfit)
#print "totalProfit[]=", totalProfit
street1CheckCallRaiseChance.append(myStreet1CheckCallRaiseChance)
street1CheckCallRaiseDone.append(myStreet1CheckCallRaiseDone)
@ -1938,7 +1930,6 @@ def generateHudCacheData(player_ids, base, category, action_types, allIns, actio
street4CheckCallRaiseDone.append(myStreet4CheckCallRaiseDone)
result['totalProfit']=totalProfit
#print "res[totalProfit]=", result['totalProfit']
result['street1CheckCallRaiseChance']=street1CheckCallRaiseChance
result['street1CheckCallRaiseDone']=street1CheckCallRaiseDone
@ -1974,8 +1965,6 @@ def generateFoldToCB(street, playerIDs, didStreetCB, streetCBDone, foldToStreetC
def storeHudCache(cursor, base, category, gametypeId, playerIds, hudImportData):
# if (category=="holdem" or category=="omahahi" or category=="omahahilo"):
#print "storeHudCache, len(playerIds)=", len(playerIds), " len(vpip)=" \
#, len(hudImportData['street0VPI']), " len(totprof)=", len(hudImportData['totalProfit'])
for player in range (len(playerIds)):
if base=="hold":
cursor.execute("SELECT * FROM HudCache WHERE gametypeId+0=%s AND playerId=%s AND activeSeats=%s AND position=%s", (gametypeId, playerIds[player], len(playerIds), hudImportData['position'][player]))
@ -2060,9 +2049,6 @@ def storeHudCache(cursor, base, category, gametypeId, playerIds, hudImportData):
if hudImportData['foldToStreet4CBChance'][player]: row[50]+=1
if hudImportData['foldToStreet4CBDone'][player]: row[51]+=1
#print "player=", player
#print "len(totalProfit)=", len(hudImportData['totalProfit'])
if hudImportData['totalProfit'][player]:
row[52]+=hudImportData['totalProfit'][player]
if hudImportData['street1CheckCallRaiseChance'][player]: row[53]+=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 48 KiB