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() (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-host']=options.server
settings['db-user']=options.user settings['db-user']=options.user
settings['db-password']=options.password settings['db-password']=options.password

View File

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

View File

@ -142,12 +142,27 @@ class Database:
cards[s_dict['seat_number']] = s_dict cards[s_dict['seat_number']] = s_dict
return (cards) 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() 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 # 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, )) c.execute(self.sql.query['get_players_from_hand'], (hand, ))
names = {} names = {}
seats = {} seats = {}
@ -156,8 +171,7 @@ class Database:
seats[row[0]] = row[1] seats[row[0]] = row[1]
# now get the stats # now get the stats
# c.execute(self.sql.query['get_stats_from_hand'], (hand, hand, player_id)) c.execute(self.sql.query[query], subs)
c.execute(self.sql.query['get_stats_from_hand'], (hand, hand))
colnames = [desc[0] for desc in c.description] colnames = [desc[0] for desc in c.description]
stat_dict = {} stat_dict = {}
for row in c.fetchall(): 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'): if(self.dbname == 'MySQL InnoDB'):
self.query['createSitesTable'] = """CREATE TABLE Sites ( 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, name varchar(32) NOT NULL,
currency char(3) NOT NULL) currency char(3) NOT NULL)
ENGINE=INNODB""" ENGINE=INNODB"""
elif(self.dbname == 'PostgreSQL'): elif(self.dbname == 'PostgreSQL'):
self.query['createSitesTable'] = """CREATE TABLE Sites ( self.query['createSitesTable'] = """CREATE TABLE Sites (
id SERIAL, PRIMARY KEY (id), id SERIAL UNIQUE, PRIMARY KEY (id),
name varchar(32), name varchar(32),
currency char(3))""" currency char(3))"""
elif(self.dbname == 'SQLite'): elif(self.dbname == 'SQLite'):
@ -92,7 +92,7 @@ class FpdbSQLQueries:
if(self.dbname == 'MySQL InnoDB'): if(self.dbname == 'MySQL InnoDB'):
self.query['createGametypesTable'] = """CREATE TABLE Gametypes ( 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), siteId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (siteId) REFERENCES Sites(id),
type char(4) NOT NULL, type char(4) NOT NULL,
base char(4) NOT NULL, base char(4) NOT NULL,
@ -106,7 +106,7 @@ class FpdbSQLQueries:
ENGINE=INNODB""" ENGINE=INNODB"""
elif(self.dbname == 'PostgreSQL'): elif(self.dbname == 'PostgreSQL'):
self.query['createGametypesTable'] = """CREATE TABLE Gametypes ( 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), siteId INTEGER, FOREIGN KEY (siteId) REFERENCES Sites(id),
type char(4), type char(4),
base char(4), base char(4),
@ -127,7 +127,7 @@ class FpdbSQLQueries:
if(self.dbname == 'MySQL InnoDB'): if(self.dbname == 'MySQL InnoDB'):
self.query['createPlayersTable'] = """CREATE TABLE Players ( 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, name VARCHAR(32) CHARACTER SET utf8 NOT NULL,
siteId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (siteId) REFERENCES Sites(id), siteId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (siteId) REFERENCES Sites(id),
comment text, comment text,
@ -135,7 +135,7 @@ class FpdbSQLQueries:
ENGINE=INNODB""" ENGINE=INNODB"""
elif(self.dbname == 'PostgreSQL'): elif(self.dbname == 'PostgreSQL'):
self.query['createPlayersTable'] = """CREATE TABLE Players ( self.query['createPlayersTable'] = """CREATE TABLE Players (
id SERIAL, PRIMARY KEY (id), id SERIAL UNIQUE, PRIMARY KEY (id),
name VARCHAR(32), name VARCHAR(32),
siteId INTEGER, FOREIGN KEY (siteId) REFERENCES Sites(id), siteId INTEGER, FOREIGN KEY (siteId) REFERENCES Sites(id),
comment text, comment text,
@ -150,7 +150,7 @@ class FpdbSQLQueries:
if(self.dbname == 'MySQL InnoDB'): if(self.dbname == 'MySQL InnoDB'):
self.query['createAutoratesTable'] = """CREATE TABLE Autorates ( 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), playerId INT UNSIGNED NOT NULL, FOREIGN KEY (playerId) REFERENCES Players(id),
gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id), gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
description varchar(50) NOT NULL, description varchar(50) NOT NULL,
@ -160,7 +160,7 @@ class FpdbSQLQueries:
ENGINE=INNODB""" ENGINE=INNODB"""
elif(self.dbname == 'PostgreSQL'): elif(self.dbname == 'PostgreSQL'):
self.query['createAutoratesTable'] = """CREATE TABLE Autorates ( 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), playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id),
gametypeId INT, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id), gametypeId INT, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
description varchar(50), description varchar(50),
@ -177,7 +177,7 @@ class FpdbSQLQueries:
if(self.dbname == 'MySQL InnoDB'): if(self.dbname == 'MySQL InnoDB'):
self.query['createHandsTable'] = """CREATE TABLE Hands ( 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, tableName VARCHAR(20) NOT NULL,
siteHandNo BIGINT NOT NULL, siteHandNo BIGINT NOT NULL,
gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id), gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
@ -190,7 +190,7 @@ class FpdbSQLQueries:
ENGINE=INNODB""" ENGINE=INNODB"""
elif(self.dbname == 'PostgreSQL'): elif(self.dbname == 'PostgreSQL'):
self.query['createHandsTable'] = """CREATE TABLE Hands ( self.query['createHandsTable'] = """CREATE TABLE Hands (
id BIGSERIAL, PRIMARY KEY (id), id BIGSERIAL UNIQUE, PRIMARY KEY (id),
tableName VARCHAR(20), tableName VARCHAR(20),
siteHandNo BIGINT, siteHandNo BIGINT,
gametypeId INT, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id), gametypeId INT, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
@ -210,7 +210,7 @@ class FpdbSQLQueries:
if(self.dbname == 'MySQL InnoDB'): if(self.dbname == 'MySQL InnoDB'):
self.query['createBoardCardsTable'] = """CREATE TABLE BoardCards ( 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), handId BIGINT UNSIGNED NOT NULL, FOREIGN KEY (handId) REFERENCES Hands(id),
card1Value smallint NOT NULL, card1Value smallint NOT NULL,
card1Suit char(1) NOT NULL, card1Suit char(1) NOT NULL,
@ -225,7 +225,7 @@ class FpdbSQLQueries:
ENGINE=INNODB""" ENGINE=INNODB"""
elif(self.dbname == 'PostgreSQL'): elif(self.dbname == 'PostgreSQL'):
self.query['createBoardCardsTable'] = """CREATE TABLE BoardCards ( 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), handId BIGINT, FOREIGN KEY (handId) REFERENCES Hands(id),
card1Value smallint, card1Value smallint,
card1Suit char(1), card1Suit char(1),
@ -247,7 +247,7 @@ class FpdbSQLQueries:
if(self.dbname == 'MySQL InnoDB'): if(self.dbname == 'MySQL InnoDB'):
self.query['createTourneyTypesTable'] = """CREATE TABLE TourneyTypes ( 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), siteId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (siteId) REFERENCES Sites(id),
buyin INT NOT NULL, buyin INT NOT NULL,
fee INT NOT NULL, fee INT NOT NULL,
@ -272,7 +272,7 @@ class FpdbSQLQueries:
if(self.dbname == 'MySQL InnoDB'): if(self.dbname == 'MySQL InnoDB'):
self.query['createTourneysTable'] = """CREATE TABLE Tourneys ( 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), tourneyTypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (tourneyTypeId) REFERENCES TourneyTypes(id),
siteTourneyNo BIGINT NOT NULL, siteTourneyNo BIGINT NOT NULL,
entries INT NOT NULL, entries INT NOT NULL,
@ -283,7 +283,7 @@ class FpdbSQLQueries:
ENGINE=INNODB""" ENGINE=INNODB"""
elif(self.dbname == 'PostgreSQL'): elif(self.dbname == 'PostgreSQL'):
self.query['createTourneysTable'] = """CREATE TABLE Tourneys ( 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), tourneyTypeId INT, FOREIGN KEY (tourneyTypeId) REFERENCES TourneyTypes(id),
siteTourneyNo BIGINT, siteTourneyNo BIGINT,
entries INT, entries INT,
@ -300,7 +300,7 @@ class FpdbSQLQueries:
if(self.dbname == 'MySQL InnoDB'): if(self.dbname == 'MySQL InnoDB'):
self.query['createHandsPlayersTable'] = """CREATE TABLE HandsPlayers ( 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), handId BIGINT UNSIGNED NOT NULL, FOREIGN KEY (handId) REFERENCES Hands(id),
playerId INT UNSIGNED NOT NULL, FOREIGN KEY (playerId) REFERENCES Players(id), playerId INT UNSIGNED NOT NULL, FOREIGN KEY (playerId) REFERENCES Players(id),
startCash INT NOT NULL, startCash INT NOT NULL,
@ -332,7 +332,7 @@ class FpdbSQLQueries:
ENGINE=INNODB""" ENGINE=INNODB"""
elif(self.dbname == 'PostgreSQL'): elif(self.dbname == 'PostgreSQL'):
self.query['createHandsPlayersTable'] = """CREATE TABLE HandsPlayers ( 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), handId BIGINT, FOREIGN KEY (handId) REFERENCES Hands(id),
playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id), playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id),
startCash INT, startCash INT,
@ -370,7 +370,7 @@ class FpdbSQLQueries:
if(self.dbname == 'MySQL InnoDB'): if(self.dbname == 'MySQL InnoDB'):
self.query['createTourneysPlayersTable'] = """CREATE TABLE TourneysPlayers ( 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), tourneyId INT UNSIGNED NOT NULL, FOREIGN KEY (tourneyId) REFERENCES Tourneys(id),
playerId INT UNSIGNED NOT NULL, FOREIGN KEY (playerId) REFERENCES Players(id), playerId INT UNSIGNED NOT NULL, FOREIGN KEY (playerId) REFERENCES Players(id),
payinAmount INT NOT NULL, payinAmount INT NOT NULL,
@ -381,7 +381,7 @@ class FpdbSQLQueries:
ENGINE=INNODB""" ENGINE=INNODB"""
elif(self.dbname == 'PostgreSQL'): elif(self.dbname == 'PostgreSQL'):
self.query['createTourneysPlayersTable'] = """CREATE TABLE TourneysPlayers ( 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), tourneyId INT, FOREIGN KEY (tourneyId) REFERENCES Tourneys(id),
playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id), playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id),
payinAmount INT, payinAmount INT,
@ -399,7 +399,7 @@ class FpdbSQLQueries:
if(self.dbname == 'MySQL InnoDB'): if(self.dbname == 'MySQL InnoDB'):
self.query['createHandsActionsTable'] = """CREATE TABLE HandsActions ( 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), handPlayerId BIGINT UNSIGNED NOT NULL, FOREIGN KEY (handPlayerId) REFERENCES HandsPlayers(id),
street SMALLINT NOT NULL, street SMALLINT NOT NULL,
actionNo SMALLINT NOT NULL, actionNo SMALLINT NOT NULL,
@ -411,7 +411,7 @@ class FpdbSQLQueries:
ENGINE=INNODB""" ENGINE=INNODB"""
elif(self.dbname == 'PostgreSQL'): elif(self.dbname == 'PostgreSQL'):
self.query['createHandsActionsTable'] = """CREATE TABLE HandsActions ( 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), handPlayerId BIGINT, FOREIGN KEY (handPlayerId) REFERENCES HandsPlayers(id),
street SMALLINT, street SMALLINT,
actionNo SMALLINT, actionNo SMALLINT,
@ -430,7 +430,7 @@ class FpdbSQLQueries:
if(self.dbname == 'MySQL InnoDB'): if(self.dbname == 'MySQL InnoDB'):
self.query['createHudCacheTable'] = """CREATE TABLE HudCache ( 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), gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
playerId INT UNSIGNED NOT NULL, FOREIGN KEY (playerId) REFERENCES Players(id), playerId INT UNSIGNED NOT NULL, FOREIGN KEY (playerId) REFERENCES Players(id),
activeSeats SMALLINT NOT NULL, activeSeats SMALLINT NOT NULL,
@ -503,7 +503,7 @@ class FpdbSQLQueries:
ENGINE=INNODB""" ENGINE=INNODB"""
elif(self.dbname == 'PostgreSQL'): elif(self.dbname == 'PostgreSQL'):
self.query['createHudCacheTable'] = """CREATE TABLE HudCache ( 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), gametypeId INT, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id), playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id),
activeSeats SMALLINT, activeSeats SMALLINT,
@ -576,26 +576,23 @@ class FpdbSQLQueries:
if(self.dbname == 'MySQL InnoDB'): if(self.dbname == 'MySQL InnoDB'):
self.query['addTourneyIndex'] = """ALTER TABLE Tourneys ADD INDEX siteTourneyNo(siteTourneyNo)""" self.query['addTourneyIndex'] = """ALTER TABLE Tourneys ADD INDEX siteTourneyNo(siteTourneyNo)"""
elif(self.dbname == 'PostgreSQL'): elif(self.dbname == 'PostgreSQL'):
# FIXME: This query has a different syntax self.query['addTourneyIndex'] = """CREATE INDEX siteTourneyNo ON Tourneys (siteTourneyNo)"""
self.query['addTourneyIndex'] = """ALTER TABLE Tourneys ADD INDEX siteTourneyNo(siteTourneyNo)"""
elif(self.dbname == 'SQLite'): elif(self.dbname == 'SQLite'):
self.query['addHandsIndex'] = """ """ self.query['addHandsIndex'] = """ """
if(self.dbname == 'MySQL InnoDB'): if(self.dbname == 'MySQL InnoDB'):
self.query['addHandsIndex'] = """ALTER TABLE Hands ADD INDEX siteHandNo(siteHandNo)""" self.query['addHandsIndex'] = """ALTER TABLE Hands ADD INDEX siteHandNo(siteHandNo)"""
elif(self.dbname == 'PostgreSQL'): elif(self.dbname == 'PostgreSQL'):
# FIXME: This query has a different syntax self.query['addHandsIndex'] = """CREATE INDEX siteHandNo ON Hands (siteHandNo)"""
self.query['addHandsIndex'] = """ALTER TABLE Hands ADD INDEX siteHandNo(siteHandNo)"""
elif(self.dbname == 'SQLite'): elif(self.dbname == 'SQLite'):
self.query['addHandsIndex'] = """ """ self.query['addHandsIndex'] = """ """
if(self.dbname == 'MySQL InnoDB'): if(self.dbname == 'MySQL InnoDB'):
self.query['addPlayersIndex'] = """ALTER TABLE Players ADD INDEX name(name)""" self.query['addPlayersIndex'] = """ALTER TABLE Players ADD INDEX name(name)"""
elif(self.dbname == 'PostgreSQL'): elif(self.dbname == 'PostgreSQL'):
# FIXME: This query has a different syntax self.query['addPlayersIndex'] = """CREATE INDEX name ON Players (name)"""
self.query['addHandsIndex'] = """ALTER TABLE Hands ADD INDEX siteHandNo(siteHandNo)"""
elif(self.dbname == 'SQLite'): elif(self.dbname == 'SQLite'):
self.query['addHandsIndex'] = """ """ self.query['addPlayersIndex'] = """ """
################################ ################################
# Queries used in GuiGraphViewer # Queries used in GuiGraphViewer
@ -633,6 +630,11 @@ class FpdbSQLQueries:
WHERE Players.name = %s AND HandsPlayers.handId = %s WHERE Players.name = %s AND HandsPlayers.handId = %s
AND Players.siteId = %s AND (tourneysPlayersId IS NULL)""" 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'): if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL'):
self.query['getRingProfitAllHandsPlayerIdSite'] = """ self.query['getRingProfitAllHandsPlayerIdSite'] = """
SELECT hp.handId, hp.winnings, SUM(ha.amount), hp.winnings - SUM(ha.amount) 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 GROUP BY hp.handId, hp.winnings, h.handStart
ORDER BY 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'): if(self.dbname == 'MySQL InnoDB'):
self.query['playerStats'] = """ self.query['playerStats'] = """
SELECT stats.gametypeId SELECT stats.gametypeId
@ -733,8 +703,7 @@ class FpdbSQLQueries:
from Gametypes gt from Gametypes gt
inner join Sites s on s.Id = gt.siteId inner join Sites s on s.Id = gt.siteId
inner join HudCache hc on hc.gameTypeId = gt.Id inner join HudCache hc on hc.gameTypeId = gt.Id
where gt.limittype = 'nl' where hc.playerId in <player_test>
and hc.playerId in (3) # use <player_test> here?
# use <gametype_test> here ? # use <gametype_test> here ?
group by hc.gametypeId group by hc.gametypeId
) stats ) stats
@ -747,7 +716,7 @@ class FpdbSQLQueries:
from HandsPlayers hp from HandsPlayers hp
inner join Hands h ON h.id = hp.handId inner join Hands h ON h.id = hp.handId
inner join HandsActions ha ON ha.handPlayerId = hp.id 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 ? # use <gametype_test> here ?
and hp.tourneysPlayersId IS NULL and hp.tourneysPlayersId IS NULL
group by hp.handId, h.gameTypeId, hp.position, hp.winnings group by hp.handId, h.gameTypeId, hp.position, hp.winnings

View File

@ -60,7 +60,7 @@ class GuiAutoImport (threading.Thread):
self.mainVBox.pack_start(self.settingsHBox, False, True, 0) self.mainVBox.pack_start(self.settingsHBox, False, True, 0)
self.settingsHBox.show() 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.settingsHBox.pack_start(self.intervalLabel)
self.intervalLabel.show() self.intervalLabel.show()

View File

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

View File

@ -20,17 +20,22 @@ import pygtk
pygtk.require('2.0') pygtk.require('2.0')
import gtk import gtk
import os import os
from time import time
#import pokereval #import pokereval
try: try:
import matplotlib
matplotlib.use('GTK')
from matplotlib.figure import Figure from matplotlib.figure import Figure
from matplotlib.backends.backend_gtk import FigureCanvasGTK as FigureCanvas from matplotlib.backends.backend_gtk import FigureCanvasGTK as FigureCanvas
from matplotlib.backends.backend_gtkagg import NavigationToolbar2GTKAgg as NavigationToolbar from matplotlib.backends.backend_gtkagg import NavigationToolbar2GTKAgg as NavigationToolbar
from numpy import arange, cumsum from numpy import arange, cumsum
from pylab import * from pylab import *
except: except:
print "Failed to load libs for graphing, graphing will not function. Please install numpy and matplotlib if you want to use graphs." print """Failed to load libs for graphing, graphing will not function. Please in
print "This is of no consequence for other parts of the program, e.g. import and HUD are NOT affected by this problem." 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_import
import fpdb_db import fpdb_db
@ -38,21 +43,24 @@ import fpdb_db
class GuiGraphViewer (threading.Thread): class GuiGraphViewer (threading.Thread):
def get_vbox(self): def get_vbox(self):
"""returns the vbox of this thread""" """returns the vbox of this thread"""
return self.mainVBox return self.mainHBox
#end def get_vbox #end def get_vbox
def showClicked(self, widget, data): def generateGraph(self, widget, data):
try: self.canvas.destroy() try: self.canvas.destroy()
except AttributeError: pass 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 site=2
sitename="PokerStars: " sitename="PokerStars: "
elif site=="FTP": elif self.sites=="Full Tilt":
site=1 site=1
sitename="Full Tilt: " sitename="Full Tilt: "
else: else:
@ -64,47 +72,180 @@ class GuiGraphViewer (threading.Thread):
#Set graph properties #Set graph properties
self.ax = self.fig.add_subplot(111) 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") self.ax.set_title("Profit graph for ring games")
#Set axis labels and grid overlay properites #Set axis labels and grid overlay properites
self.ax.set_xlabel("Hands", fontsize = 12) self.ax.set_xlabel("Hands", fontsize = 12)
self.ax.set_ylabel("$", fontsize = 12) self.ax.set_ylabel("$", fontsize = 12)
self.ax.grid(color='g', linestyle=':', linewidth=0.2) 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 #Draw plot
self.ax.plot(line,) self.ax.plot(line,)
self.canvas = FigureCanvas(self.fig) # a gtk.DrawingArea self.canvas = FigureCanvas(self.fig) # a gtk.DrawingArea
self.mainVBox.pack_start(self.canvas) self.graphBox.add(self.canvas)
self.canvas.show() self.canvas.show()
#end of def showClicked #end of def showClicked
def getRingProfitGraph(self, name, site): def getRingProfitGraph(self, name, site):
#self.cursor.execute(self.sql.query['getRingWinningsAllGamesPlayerIdSite'], (name, site))
self.cursor.execute(self.sql.query['getRingProfitAllHandsPlayerIdSite'], (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() 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) y=map(lambda x:float(x[3]), winnings)
line = cumsum(y) line = cumsum(y)
return line/100 return line/100
#end of def getRingProfitGraph #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): def __init__(self, db, settings, querylist, config, debug=True):
"""Constructor for GraphViewer""" """Constructor for GraphViewer"""
self.debug=debug self.debug=debug
@ -113,37 +254,70 @@ class GuiGraphViewer (threading.Thread):
self.cursor=db.cursor self.cursor=db.cursor
self.settings=settings self.settings=settings
self.sql=querylist self.sql=querylist
self.conf = config
self.mainVBox = gtk.VBox(False, 0) self.sites = "PokerStars"
self.mainVBox.show() self.heroes = {}
self.settingsHBox = gtk.HBox(False, 0) # For use in date ranges.
self.mainVBox.pack_start(self.settingsHBox, False, True, 0) self.start_date = gtk.Entry(max=12)
self.settingsHBox.show() 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.mainHBox = gtk.HBox(False, 0)
self.settingsHBox.pack_start(self.nameLabel) self.mainHBox.show()
self.nameLabel.show()
self.nameEntry=gtk.Entry() self.leftPanelBox = gtk.VBox(False, 0)
self.nameEntry.set_text("name") self.graphBox = gtk.VBox(False, 0)
self.settingsHBox.pack_start(self.nameEntry)
self.nameEntry.show()
self.siteLabel = gtk.Label("Site (PS or FTP):") self.hpane = gtk.HPaned()
self.settingsHBox.pack_start(self.siteLabel) self.hpane.pack1(self.leftPanelBox)
self.siteLabel.show() self.hpane.pack2(self.graphBox)
self.hpane.show()
self.siteEntry=gtk.Entry() self.mainHBox.add(self.hpane)
self.siteEntry.set_text("PS")
self.settingsHBox.pack_start(self.siteEntry)
self.siteEntry.show()
#Note: Assumes PokerStars is in the config playerFrame = gtk.Frame("Hero:")
self.nameEntry.set_text(config.supported_sites["PokerStars"].screen_name) playerFrame.set_label_align(0.0, 0.0)
playerFrame.show()
vbox = gtk.VBox(False, 0)
vbox.show()
self.showButton=gtk.Button("Show/Refresh") self.fillPlayerFrame(vbox)
self.showButton.connect("clicked", self.showClicked, "show clicked") playerFrame.add(vbox)
self.settingsHBox.pack_start(self.showButton)
self.showButton.show() sitesFrame = gtk.Frame("Sites:")
#end of GuiGraphViewer.__init__ 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"> <FreePokerToolsConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FreePokerToolsConfig.xsd">
<supported_sites> <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"> <layout max="8" width="792" height="546" fav_seat="0">
<location seat="1" x="684" y="61"> </location> <location seat="1" x="684" y="61"> </location>
<location seat="2" x="689" y="239"> </location> <location seat="2" x="689" y="239"> </location>
@ -49,7 +49,7 @@
<location seat="2" x="10" y="288"> </location> <location seat="2" x="10" y="288"> </location>
</layout> </layout>
</site> </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"> <layout fav_seat="0" height="547" max="8" width="794">
<location seat="1" x="640" y="64"> </location> <location seat="1" x="640" y="64"> </location>
<location seat="2" x="650" y="230"> </location> <location seat="2" x="650" y="230"> </location>
@ -84,7 +84,7 @@
<location seat="9" x="70" y="53"> </location> <location seat="9" x="70" y="53"> </location>
</layout> </layout>
</site> </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"> <layout fav_seat="0" height="546" max="6" width="792">
<location seat="1" x="581" y="109"> </location> <location seat="1" x="581" y="109"> </location>
<location seat="2" x="605" y="287"> </location> <location seat="2" x="605" y="287"> </location>
@ -187,7 +187,7 @@
<pu_stat pu_stat_name="ffreq_4"> </pu_stat> <pu_stat pu_stat_name="ffreq_4"> </pu_stat>
</pu> </pu>
</popup_windows> </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> <tv combinedStealFold = "True" combined2B3B = "True" combinedPostflop = "True"></tv>
<supported_databases> <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.stat_windows = {}
self.popup_windows = {} self.popup_windows = {}
self.aux_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 # Set up a main window for this this instance of the HUD
self.main_window = gtk.Window() self.main_window = gtk.Window()
@ -92,14 +92,22 @@ class Hud:
self.menu.append(self.item1) self.menu.append(self.item1)
self.item1.connect("activate", self.kill_hud) self.item1.connect("activate", self.kill_hud)
self.item1.show() self.item1.show()
self.item2 = gtk.MenuItem('Save Layout') self.item2 = gtk.MenuItem('Save Layout')
self.menu.append(self.item2) self.menu.append(self.item2)
self.item2.connect("activate", self.save_layout) self.item2.connect("activate", self.save_layout)
self.item2.show() self.item2.show()
self.item3 = gtk.MenuItem('Reposition Stats') self.item3 = gtk.MenuItem('Reposition Stats')
self.menu.append(self.item3) self.menu.append(self.item3)
self.item3.connect("activate", self.reposition_windows) self.item3.connect("activate", self.reposition_windows)
self.item3.show() 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.ebox.connect_object("button-press-event", self.on_button_press, self.menu)
self.main_window.show_all() self.main_window.show_all()
@ -132,6 +140,12 @@ class Hud:
for w in self.stat_windows: for w in self.stat_windows:
self.stat_windows[w].window.move(self.stat_windows[w].x, self.stat_windows[w].window.move(self.stat_windows[w].x,
self.stat_windows[w].y) 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): def save_layout(self, *args):
new_layout = [(0, 0)] * self.max 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. # 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]] 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]) 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 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) 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" + \ tip = stat_dict[s]['screen_name'] + "\n" + number[5] + "\n" + \
number[3] + ", " + number[4] number[3] + ", " + number[4]
Stats.do_tip(self.stat_windows[stat_dict[s]['seat']].e_box[r][c], tip) 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) self.double_click(widget, event, *args)
if event.button == 2: # middle button event if event.button == 2: # middle button event
self.window.hide()
# print "middle button clicked" # print "middle button clicked"
pass pass
@ -361,42 +383,14 @@ class Stat_Window:
self.e_box[r][c].add(self.label[r][c]) self.e_box[r][c].add(self.label[r][c])
self.e_box[r][c].connect("button_press_event", self.button_press_cb) 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) 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.set_opacity(parent.colors['hudopacity'])
self.window.realize
self.window.move(self.x, self.y) 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): self.window.hide()
"""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)
def destroy(*args): # call back for terminating the main eventloop def destroy(*args): # call back for terminating the main eventloop
gtk.main_quit() 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_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.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 # figure out the row, col address of the click that activated the popup
row = 0 row = 0
@ -564,7 +558,7 @@ if __name__== "__main__":
c = Configuration.Config() c = Configuration.Config()
#tables = Tables.discover(c) #tables = Tables.discover(c)
t = Tables.discover_table_by_name(c, "Chelsea") t = Tables.discover_table_by_name(c, "Motorway")
if t is None: if t is None:
print "Table not found." print "Table not found."
db = Database.Database(c, 'fpdb', 'holdem') db = Database.Database(c, 'fpdb', 'holdem')

View File

@ -41,30 +41,47 @@ import Configuration
import Database import Database
import Tables import Tables
import Hud import Hud
import Mucked
import HandHistory import HandHistory
class Mucked: class Aux_Window:
def __init__(self, parent, db_connection): def __init__(self, parent, config, db_name):
self.config = config
self.parent = parent #this is the parent of the mucked cards widget 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.vbox = gtk.VBox()
self.parent.add(self.vbox) self.parent.add(self.vbox)
self.mucked_list = MuckedList (self.vbox, db_connection) def update(self):
self.mucked_cards = MuckedCards(self.vbox, db_connection) 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.mucked_list.mucked_cards = self.mucked_cards
self.parent.show_all()
def update(self, new_hand_id): def update_data(self, new_hand_id):
self.mucked_list.update(new_hand_id) self.mucked_list.update_data(new_hand_id)
class MuckedList: def update_gui(self, new_hand_id):
def __init__(self, parent, db_connection): self.mucked_list.update_gui(new_hand_id)
class Stud_list:
def __init__(self, parent, config, db_name):
self.parent = parent 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 # set up a scrolled window to hold the listbox
self.scrolled_window = gtk.ScrolledWindow() self.scrolled_window = gtk.ScrolledWindow()
@ -115,11 +132,25 @@ class MuckedList:
vadj.set_value(vadj.upper) vadj.set_value(vadj.upper)
self.mucked_cards.update(new_hand_id) self.mucked_cards.update(new_hand_id)
class MuckedCards: def update_data(self, new_hand_id):
def __init__(self, parent, db_connection): 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.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.card_images = self.get_card_images()
self.seen_cards = {} self.seen_cards = {}
@ -159,37 +190,99 @@ class MuckedCards:
self.parent.add(self.grid) self.parent.add(self.grid)
def translate_cards(self, old_cards): 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): 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() self.clear()
cards = self.translate_cards(cards) cards = self.translate_cards(cards)
for c in cards.keys(): for c in cards.keys():
self.grid_contents[(1, cards[c]['seat_number'] - 1)].set_text(cards[c]['screen_name']) 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'), 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')): (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)]. \ self.seen_cards[(i[0], cards[c]['seat_number'] - 1)]. \
set_from_pixbuf(self.card_images[self.split_cards(cards[c][i[1]])]) set_from_pixbuf(self.card_images[self.split_cards(cards[c][i[1]])])
xml_text = self.db_connection.get_xml(new_hand_id) tips = []
hh = HandHistory.HandHistory(xml_text, ('BETTING')) 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 ## action in tool tips for 3rd street cards
tip = "%s" % hh.BETTING.rounds[0]
for c in (0, 1, 2): for c in (0, 1, 2):
for r in range(0, self.rows): 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 # action in tools tips for later streets
round_to_col = (0, 3, 4, 5, 6) round_to_col = (0, 3, 4, 5, 6)
for round in range(1, len(hh.BETTING.rounds)): for round in range(1, len(tips)):
tip = "%s" % hh.BETTING.rounds[round]
for r in range(0, self.rows): 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): def split_cards(self, card):
return (card[0], card[1].upper()) return (card[0], card[1].upper())
@ -225,19 +318,19 @@ if __name__== "__main__":
# just read it and pass it to update # just read it and pass it to update
new_hand_id = sys.stdin.readline() new_hand_id = sys.stdin.readline()
new_hand_id = new_hand_id.rstrip() # remove trailing whitespace 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) return(True)
config = Configuration.Config() config = Configuration.Config()
db_connection = Database.Database(config, 'fpdb', '') # db_connection = Database.Database(config, 'fpdb', '')
main_window = gtk.Window() main_window = gtk.Window()
main_window.set_keep_above(True) main_window.set_keep_above(True)
main_window.connect("destroy", destroy) 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() main_window.show_all()
s_id = gobject.io_add_watch(sys.stdin, gobject.IO_IN, process_new_hand) s_id = gobject.io_add_watch(sys.stdin, gobject.IO_IN, process_new_hand)
gtk.main() gtk.main()

View File

@ -24,8 +24,11 @@
import os import os
import sys import sys
import datetime
import Configuration
import fpdb_db import fpdb_db
import fpdb_import import fpdb_import
import fpdb_simple
import FpdbSQLQueries import FpdbSQLQueries
import unittest import unittest
@ -34,6 +37,7 @@ class TestSequenceFunctions(unittest.TestCase):
def setUp(self): def setUp(self):
"""Configure MySQL settings/database and establish connection""" """Configure MySQL settings/database and establish connection"""
self.c = Configuration.Config()
self.mysql_settings={ 'db-host':"localhost", self.mysql_settings={ 'db-host':"localhost",
'db-backend':2, 'db-backend':2,
'db-databaseName':"fpdbtest", '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-databaseName'], self.mysql_settings['db-user'],
self.mysql_settings['db-password']) self.mysql_settings['db-password'])
self.mysqldict = FpdbSQLQueries.FpdbSQLQueries('MySQL InnoDB') 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""" # """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"} # 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.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)) 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): def testImportHandHistoryFiles(self):
"""Test import of single HH file""" """Test import of single HH file"""
self.mysqlimporter.addImportFile("regression-test-files/hand-histories/ps-lhe-ring-3hands.txt") 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 GROUP BY HudCache.PlayerId
""" """
# FROM HudCache, Hands # same as above except stats are aggregated for all blind/limit levels
# WHERE HudCache.PlayerId in self.query['get_stats_from_hand_aggregated'] = """
# (SELECT PlayerId FROM HandsPlayers SELECT HudCache.playerId AS player_id,
# WHERE handId = %s) sum(HDs) AS n,
# AND Hands.id = %s sum(street0VPI) AS vpip,
# AND Hands.gametypeId = HudCache.gametypeId sum(street0Aggr) AS pfr,
sum(street0_3B4BChance) AS TB_opp_0,
# AND PlayerId LIKE %s sum(street0_3B4BDone) AS TB_0,
# HudCache.gametypeId AS gametypeId, sum(street1Seen) AS saw_f,
# activeSeats AS n_active, sum(street1Seen) AS saw_1,
# position AS position, sum(street2Seen) AS saw_2,
# HudCache.tourneyTypeId AS tourneyTypeId, 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'] = """ self.query['get_players_from_hand'] = """
SELECT HandsPlayers.playerId, seatNo, name SELECT HandsPlayers.playerId, seatNo, name
@ -288,15 +350,15 @@ class Sql:
order by seatNo order by seatNo
""" """
# self.query['get_hand_info'] = """ self.query['get_action_from_hand'] = """
# SELECT SELECT street, Players.name, HandsActions.action, HandsActions.amount, actionno
# game_id, FROM Players, HandsActions, HandsPlayers
# CONCAT(hole_card_1, hole_card_2, hole_card_3, hole_card_4, hole_card_5, hole_card_6, hole_card_7) AS hand, WHERE HandsActions.action != 'blind'
# total_won-total_bet AS net AND HandsPlayers.handid = %s
# FROM game_players AND HandsPlayers.playerid = Players.id
# WHERE game_id = %s AND player_id = 3 AND HandsActions.handPlayerId = HandsPlayers.id
# """ ORDER BY street, actionno
"""
if __name__== "__main__": if __name__== "__main__":
# just print the default queries and exit # just print the default queries and exit
s = Sql(game = 'razz', type = 'ptracks') s = Sql(game = 'razz', type = 'ptracks')

View File

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

401
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 # Copyright 2008, Ray E. Barker
#
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or # the Free Software Foundation; either version 2 of the License, or
@ -40,7 +39,35 @@ if os.name == 'nt':
# FreePokerTools modules # FreePokerTools modules
import Configuration 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: 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): def __str__(self):
# __str__ method for testing # __str__ method for testing
temp = 'TableWindow object\n' temp = 'TableWindow object\n'
@ -51,13 +78,10 @@ class Table_Window:
temp = temp + " tournament = %d\n table = %d" % (self.tournament, self.table) temp = temp + " tournament = %d\n table = %d" % (self.tournament, self.table)
return temp return temp
def get_details(table): ############################################################################
table.game = 'razz' # Top-level discovery routines--these are the modules interface
table.max = 8
table.struture = 'limit'
table.tournament = 0
def discover(c): def discover(c):
"""Dispatch routine for finding all potential poker client windows."""
if os.name == 'posix': if os.name == 'posix':
tables = discover_posix(c) tables = discover_posix(c)
elif os.name == 'nt': elif os.name == 'nt':
@ -66,86 +90,99 @@ def discover(c):
tables = discover_mac(c) tables = discover_mac(c)
else: else:
tables = {} tables = {}
return tables
return(tables)
def discover_table_by_name(c, tablename): def discover_table_by_name(c, tablename):
"""Dispatch routine for finding poker client windows with the given name."""
if os.name == 'posix': if os.name == 'posix':
table = discover_posix_by_name(c, tablename) info = discover_posix_by_name(c, tablename)
elif os.name == 'nt': elif os.name == 'nt':
table = discover_nt_by_name(c, tablename) info = discover_nt_by_name(c, tablename)
elif os.name == 'mac': elif os.name == 'mac':
table = discover_mac_by_name(c, tablename) info = discover_mac_by_name(c, tablename)
else: else:
table = None return None
return(table) 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): def discover_posix(c):
"""Poker client table window finder for posix/Linux = XWindows.""" """Poker client table window finder for posix/Linux = XWindows."""
tables = {} tables = {}
for listing in os.popen('xwininfo -root -tree').readlines(): for listing in os.popen('xwininfo -root -tree').readlines():
# xwininfo -root -tree -id 0xnnnnn gets the info on a single window # 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('Lobby', listing): continue
if re.search('Instant Hand History', 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 if re.search('\"Full Tilt Poker\"', listing): continue # FTP Lobby
for s in c.supported_sites.keys(): if re.search('History for table:', listing): continue
if re.search(c.supported_sites[s].table_finder, listing): if re.search('has no name', listing): continue
mo = re.match('\s+([\dxabcdef]+) (.+):.+ (\d+)x(\d+)\+\d+\+\d+ \+(\d+)\+(\d+)', listing) info = decode_xwininfo(c, listing)
if mo.group(2) == '(has no name)': continue if info['site'] == None: continue
if re.match('[\(\)\d\s]+', mo.group(2)): continue # this is a popup if info['title'] == info['exe']: continue
tw = Table_Window() # this appears to be a poker client, so make a table object for it
tw.site = c.supported_sites[s].site_name tw = Table_Window(info)
tw.number = int(mo.group(1), 0) eval("%s(tw)" % params['decoder'])
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)
tables[tw.name] = tw tables[tw.name] = tw
return tables return tables
def discover_posix_by_name(c, tablename): def discover_posix_by_name(c, tablename):
tables = discover_posix(c) """Find an XWindows poker client of the given name."""
for t in tables: for listing in os.popen('xwininfo -root -tree').readlines():
if tables[t].name.find(tablename) > -1: if re.search(tablename, listing):
return tables[t] 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 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 # NT (= Windows) specific routines
# currently displayed on the screen. The discover_posix should give you
# some idea how to support other systems.
#
# discover_xx() returns a dict of TableWindow objects--one TableWindow
# object for each poker client table on the screen.
#
# Each TableWindow object must have the following attributes correctly populated:
# tw.site = the site name, e.g. PokerStars, FullTilt. This must match the site
# name specified in the config file.
# tw.number = This is the system id number for the client table window in the
# format that the system presents it.
# tw.title = The full title from the window title bar.
# tw.width, tw.height = The width and height of the window in pixels. This is
# the internal width and height, not including the title bar and
# window borders.
# tw.x, tw.y = The x, y (horizontal, vertical) location of the window relative
# to the top left of the display screen. This also does not include the
# title bar and window borders. To put it another way, this is the
# screen location of (0, 0) in the working window.
def win_enum_handler(hwnd, titles):
titles[hwnd] = win32gui.GetWindowText(hwnd)
def child_enum_handler(hwnd, children):
print hwnd, win32.GetWindowRect(hwnd)
def discover_nt(c): def discover_nt(c):
""" Poker client table window finder for Windows.""" """ Poker client table window finder for Windows."""
# #
@ -185,72 +222,88 @@ def discover_nt(c):
return tables return tables
def discover_nt_by_name(c, tablename): 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 """Finds poker client window with the given 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 = {} titles = {}
# tables = discover_nt(c)
win32gui.EnumWindows(win_enum_handler, titles) win32gui.EnumWindows(win_enum_handler, titles)
for s in c.supported_sites.keys():
for hwnd in titles.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) processid = win32process.GetWindowThreadProcessId(hwnd)
pshandle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION | win32con.PROCESS_VM_READ, False, processid[1]) pshandle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION | win32con.PROCESS_VM_READ, False, processid[1])
exe = win32process.GetModuleFileNameEx(pshandle, 0) return 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 ? def decode_windows(c, title, hwnd):
for hwnd in titles.keys(): """Gets window parameters from the window title and handle--Windows."""
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
# 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 return None
def discover_mac(c): def clean_title(name):
""" Poker client table window finder for Macintosh.""" """Clean the little info strings from the table name."""
tables = {} # these strings could go in a config file
return tables for pattern in [' \(6 max\)', ' \(heads up\)', ' \(deep\)',
' \(deep hu\)', ' \(deep 6\)', ' \(2\)',
def discover_mac_by_name(c, tablename): ' \(edu\)', ' \(edu, 6 max\)', ' \(6\)',
# again, i have no mac to test this on, sorry -eric ' no all-in', ' fast', ',', ' 50BB min', '\s+$']:
return discover_mac(c) name = re.sub(pattern, '', name)
name = name.rstrip()
return name
def pokerstars_decode_table(tw): def pokerstars_decode_table(tw):
# extract the table name OR the tournament number and table name from the title # Extract poker information from the window title. This is not needed for
# other info in title is redundant with data in the database # 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) title_bits = re.split(' - ', tw.title)
name = title_bits[0] name = title_bits[0]
mo = re.search('Tournament (\d+) Table (\d+)', name) mo = re.search('Tournament (\d+) Table (\d+)', name)
@ -260,12 +313,8 @@ def pokerstars_decode_table(tw):
tw.name = name tw.name = name
else: else:
tw.tournament = None tw.tournament = None
for pattern in [' no all-in', ' fast', ',', ' 50BB min']: tw.name = clean_title(name)
name = re.sub(pattern, '', 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)
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.game = mo.group(1).lower() tw.game = mo.group(1).lower()
tw.game = re.sub('\'', '', tw.game) tw.game = re.sub('\'', '', tw.game)
@ -288,25 +337,103 @@ def pokerstars_decode_table(tw):
pass pass
def fulltilt_decode_table(tw): def fulltilt_decode_table(tw):
# extract the table name OR the tournament number and table name from the title # Extract poker information from the window title. This is not needed for
# other info in title is redundant with data in the database # 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) title_bits = re.split(' - ', tw.title)
name = title_bits[0] name = title_bits[0]
tw.tournament = None tw.tournament = None
for pattern in [' (6 max)', ' (heads up)', ' (deep)', tw.name = clean_title(name)
' (deep hu)', ' (deep 6)', ' (2)',
' (edu)', ' (edu, 6 max)', ' (6)' ]: 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 = re.sub(pattern, '', name)
# (tw.name, trash) = name.split(r' (', 1) name = name.rstrip()
tw.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__": if __name__=="__main__":
c = Configuration.Config() 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(): for t in tables.keys():
print "t = ", t
print tables[t] print tables[t]
print "press enter to continue" print "press enter to continue"

View File

@ -37,6 +37,7 @@ import gtk
import fpdb_db import fpdb_db
import fpdb_simple import fpdb_simple
import GuiBulkImport import GuiBulkImport
import GuiPlayerStats
import GuiTableViewer import GuiTableViewer
import GuiAutoImport import GuiAutoImport
import GuiGraphViewer import GuiGraphViewer
@ -117,12 +118,12 @@ class fpdb:
def dia_create_del_database(self, widget, data): def dia_create_del_database(self, widget, data):
print "todo: implement dia_create_del_database" print "todo: implement dia_create_del_database"
obtain_global_lock() self.obtain_global_lock()
#end def dia_create_del_database #end def dia_create_del_database
def dia_create_del_user(self, widget, data): def dia_create_del_user(self, widget, data):
print "todo: implement dia_create_del_user" print "todo: implement dia_create_del_user"
obtain_global_lock() self.obtain_global_lock()
#end def dia_create_del_user #end def dia_create_del_user
def dia_database_stats(self, widget, data): def dia_database_stats(self, widget, data):
@ -132,17 +133,17 @@ class fpdb:
def dia_delete_db_parts(self, widget, data): def dia_delete_db_parts(self, widget, data):
print "todo: implement dia_delete_db_parts" print "todo: implement dia_delete_db_parts"
obtain_global_lock() self.obtain_global_lock()
#end def dia_delete_db_parts #end def dia_delete_db_parts
def dia_edit_profile(self, widget=None, data=None, create_default=False, path=None): def dia_edit_profile(self, widget=None, data=None, create_default=False, path=None):
print "todo: implement dia_edit_profile" print "todo: implement dia_edit_profile"
obtain_global_lock() self.obtain_global_lock()
#end def dia_edit_profile #end def dia_edit_profile
def dia_export_db(self, widget, data): def dia_export_db(self, widget, data):
print "todo: implement dia_export_db" print "todo: implement dia_export_db"
obtain_global_lock() self.obtain_global_lock()
#end def dia_export_db #end def dia_export_db
def dia_get_db_root_credentials(self): def dia_get_db_root_credentials(self):
@ -167,7 +168,7 @@ class fpdb:
def dia_import_db(self, widget, data): def dia_import_db(self, widget, data):
print "todo: implement dia_import_db" print "todo: implement dia_import_db"
obtain_global_lock() self.obtain_global_lock()
#end def dia_import_db #end def dia_import_db
def dia_licensing(self, widget, data): def dia_licensing(self, widget, data):
@ -263,7 +264,11 @@ class fpdb:
self.db = fpdb_db.fpdb_db() self.db = fpdb_db.fpdb_db()
#print "end of fpdb.load_profile, databaseName:",self.settings['db-databaseName'] #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: if self.db.wrongDbVersion:
diaDbVersionWarning = gtk.Dialog(title="Strong Warning - Invalid database version", parent=None, flags=0, buttons=(gtk.STOCK_OK,gtk.RESPONSE_OK)) 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") self.add_and_display_tab(bulk_tab, "Bulk Import")
#end def 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): def tab_main_help(self, widget, data):
"""Displays a tab with the main fpdb help screen""" """Displays a tab with the main fpdb help screen"""
#print "start of tab_main_help" #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/_Graphs", "<control>G", self.tabGraphViewer, 0, None ),
("/Viewers/Hand _Replayer (todo)", None, self.not_implemented, 0, None ), ("/Viewers/Hand _Replayer (todo)", None, self.not_implemented, 0, None ),
("/Viewers/Player _Details (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/Starting _Hands (todo)", None, self.not_implemented, 0, None ),
("/Viewers/_Session Replayer (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 ), ("/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 self.SQLITE=4
#end def __init__ #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""" """Connects a database with the given parameters"""
if backend is None:
raise FpdbError('Database backend not defined')
self.backend=backend self.backend=backend
self.host=host self.host=host
self.database=database
self.user=user self.user=user
self.password=password self.password=password
self.database=database
if backend==self.MYSQL_INNODB: if backend==self.MYSQL_INNODB:
import MySQLdb import MySQLdb
self.db=MySQLdb.connect(host = host, user = user, passwd = password, db = database) self.db=MySQLdb.connect(host = host, user = user, passwd = password, db = database)
elif backend==self.PGSQL: elif backend==self.PGSQL:
import psycopg2 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: else:
raise fpdb_simple.FpdbError("unrecognised database backend:"+backend) raise fpdb_simple.FpdbError("unrecognised database backend:"+backend)
self.cursor=self.db.cursor() self.cursor=self.db.cursor()
self.cursor.execute('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED') self.cursor.execute('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED')
# Set up query dictionary as early in the connection process as we can. # Set up query dictionary as early in the connection process as we can.
self.sql = FpdbSQLQueries.FpdbSQLQueries(self.get_backend_name()) self.sql = FpdbSQLQueries.FpdbSQLQueries(self.get_backend_name())
self.wrongDbVersion=False self.wrongDbVersion=False

View File

@ -31,6 +31,7 @@ try:
except: except:
pass pass
import traceback
import math import math
import os import os
import datetime import datetime
@ -61,6 +62,8 @@ class Importer:
self.settings['minPrint'] = 30 self.settings['minPrint'] = 30
self.dbConnect() self.dbConnect()
# XXX: Why is this here, when fpdb_db.connect() already does the
# same?
def dbConnect(self): def dbConnect(self):
#connect to DB #connect to DB
if self.settings['db-backend'] == 2: if self.settings['db-backend'] == 2:
@ -72,10 +75,15 @@ class Importer:
if not pgsqlLibFound: 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") 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 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'], self.db = psycopg2.connect(host = self.settings['db-host'],
user = self.settings['db-user'], user = self.settings['db-user'],
password = self.settings['db-password'], password = self.settings['db-password'],
database = self.settings['db-databaseName']) database = self.settings['db-databaseName'])
else:
dbname = self.settings['db-databaseName']
self.db = psycopg2.connect(database = dbname)
elif self.settings['db-backend'] == 4: elif self.settings['db-backend'] == 4:
pass pass
else: else:
@ -114,7 +122,6 @@ class Importer:
#dirlist is a hash of lists: #dirlist is a hash of lists:
#dirlist{ 'PokerStars' => ["/path/to/import/", "filtername"] } #dirlist{ 'PokerStars' => ["/path/to/import/", "filtername"] }
def addImportDirectory(self,dir,monitor = False, site = "default", filter = "passthrough"): def addImportDirectory(self,dir,monitor = False, site = "default", filter = "passthrough"):
if dir != "/dev/null" and dir.lower() != "none":
if os.path.isdir(dir): if os.path.isdir(dir):
if monitor == True: if monitor == True:
self.monitor = True self.monitor = True
@ -123,7 +130,7 @@ class Importer:
for file in os.listdir(dir): for file in os.listdir(dir):
self.addImportFile(os.path.join(dir, file), site, filter) self.addImportFile(os.path.join(dir, file), site, filter)
else: 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 #Run full import on filelist
def runImport(self): def runImport(self):
@ -249,14 +256,14 @@ class Importer:
duplicates+=1 duplicates+=1
except (ValueError), fe: except (ValueError), fe:
errors+=1 errors+=1
self.printEmailErrorMessage(errors, file, hand[0]) self.printEmailErrorMessage(errors, file, hand)
if (self.settings['failOnError']): if (self.settings['failOnError']):
self.db.commit() #dont remove this, in case hand processing was cancelled. self.db.commit() #dont remove this, in case hand processing was cancelled.
raise raise
except (fpdb_simple.FpdbError), fe: except (fpdb_simple.FpdbError), fe:
errors+=1 errors+=1
self.printEmailErrorMessage(errors, file, hand[0]) self.printEmailErrorMessage(errors, file, hand)
#fe.printStackTrace() #todo: get stacktrace #fe.printStackTrace() #todo: get stacktrace
self.db.rollback() self.db.rollback()
@ -272,11 +279,10 @@ class Importer:
if ((stored+duplicates+partial+errors)>=self.settings['handCount']): if ((stored+duplicates+partial+errors)>=self.settings['handCount']):
if (not self.settings['quiet']): if (not self.settings['quiet']):
print "quitting due to reaching the amount of hands to be imported" print "quitting due to reaching the amount of hands to be imported"
print "Total stored:", stored, "duplicates:", duplicates, "partial/damaged:", print "Total stored:", stored, "duplicates:", duplicates, "partial/damaged:", partial, "errors:", errors, " time:", (time() - starttime)
partial, "errors:", errors, " time: %5.3f" % (time() - starttime)
sys.exit(0) sys.exit(0)
startpos=endpos 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 stored==0:
if duplicates>0: if duplicates>0:
@ -300,11 +306,17 @@ class Importer:
def printEmailErrorMessage(self, errors, filename, line): 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 "Error No.",errors,", please send the hand causing this to steffen@sycamoretest.info so I can fix it."
print "Filename:", filename print "Filename:", filename
print "Here is the first line so you can identify it. Please mention that the error was a ValueError:" print "Here is the first line so you can identify it. Please mention that the error was a ValueError:"
print self.hand[0] 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__": if __name__ == "__main__":
print "CLI for fpdb_import is now available as CliFpdb.py" 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 smallBlindLine=0
for i in range(len(hand)): 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].find("posts small blind")!=-1 or hand[i].find("posts the small blind")!=-1:
if hand[i][-2:] == "$0":
continue
smallBlindLine=i smallBlindLine=i
#print "found small blind line:",smallBlindLine #print "found small blind line:",smallBlindLine
break break

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

@ -54,7 +54,8 @@ def checkPositions(positions):
pass pass
### RHH modified to allow for "position 9" here (pos==9 is when you're a dead hand before the BB ### 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)) raise FpdbError("invalid position found in checkPositions. i: "+str(i)+" position: "+str(pos))
#end def fpdb_simple.checkPositions #end def fpdb_simple.checkPositions
@ -484,7 +485,6 @@ def isActionLine(line):
#returns whether this is a duplicate #returns whether this is a duplicate
def isAlreadyInDB(cursor, gametypeID, siteHandNo): 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)) cursor.execute ("SELECT id FROM Hands WHERE gametypeId=%s AND siteHandNo=%s", (gametypeID, siteHandNo))
result=cursor.fetchall() result=cursor.fetchall()
if (len(result)>=1): 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] print "line:",line,"cardValues[playerNo]:",cardValues[playerNo]
raise FpdbError("read too many/too few holecards in parseCardLine") raise FpdbError("read too many/too few holecards in parseCardLine")
elif (category=="razz" or category=="studhi" or category=="studhilo"): 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 #print "parseCardLine(in stud if), street:", street
if line[pos+2]=="]": #-> not (hero and 3rd street) if line[pos+2]=="]": #-> not (hero and 3rd street)
cardValues[playerNo][street+2]=line[pos:pos+1] cardValues[playerNo][street+2]=line[pos:pos+1]
@ -832,14 +832,16 @@ def parseHandStartTime(topline, site):
tmp=topline[pos1:pos2] tmp=topline[pos1:pos2]
isUTC=True isUTC=True
else: else:
tmp=topline[-30:] tmp=topline
# print "parsehandStartTime, tmp:", tmp # print "parsehandStartTime, tmp:", tmp
pos = tmp.find("-")+2 pos = tmp.find("-")+2
tmp = tmp[pos:] tmp = tmp[pos:]
#Need to match either #Need to match either
# 2008/09/07 06:23:14 ET or # 2008/09/07 06:23:14 ET or
# 2008/08/17 - 01:14:43 (ET) # 2008/08/17 - 01:14:43 (ET) or
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/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')) #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'))) 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: else:
@ -892,6 +894,11 @@ def parsePositions (hand, names):
if (bb!=-1): if (bb!=-1):
bb=recognisePlayerNo(bb, names, "bet") bb=recognisePlayerNo(bb, names, "bet")
# print "sb = ", sb, "bb = ", bb
if bb == sb:
sbExists = False
sb = -1
#write blinds into array #write blinds into array
if (sbExists): if (sbExists):
positions[sb]="S" positions[sb]="S"
@ -908,16 +915,25 @@ def parsePositions (hand, names):
positions[arraypos]=distFromBtn positions[arraypos]=distFromBtn
arraypos-=1 arraypos-=1
distFromBtn+=1 distFromBtn+=1
# eric - this takes into account dead seats between blinds
### RHH - Changed to set the null seats before BB to "9" if sbExists:
i = bb - 1 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"
if sbExists:
i = sb-1
else:
i = bb-1
while positions[i] < 0: while positions[i] < 0:
positions[i]=9 positions[i]=9
i-=1 i-=1
arraypos=len(names)-1 arraypos=len(names)-1
if (bb!=0 or (bb==0 and sbExists==False)): if (bb!=0 or (bb==0 and sbExists==False) or (bb == 1 and sb != arraypos) ):
while (arraypos>bb): while (arraypos>bb and arraypos > sb):
positions[arraypos]=distFromBtn positions[arraypos]=distFromBtn
arraypos-=1 arraypos-=1
distFromBtn+=1 distFromBtn+=1
@ -927,6 +943,7 @@ def parsePositions (hand, names):
print "parsePositions names:",names print "parsePositions names:",names
print "result:",positions print "result:",positions
raise FpdbError ("failed to read positions") raise FpdbError ("failed to read positions")
#print str(positions), "\n"
return positions return positions
#end def parsePositions #end def parsePositions
@ -1022,7 +1039,7 @@ def recogniseCategory(line):
#end def recogniseCategory #end def recogniseCategory
#returns the int for the gametype_id for the given line #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): #if (topline.find("HORSE")!=-1):
# raise FpdbError("recogniseGametypeID: HORSE is not yet supported.") # raise FpdbError("recogniseGametypeID: HORSE is not yet supported.")
@ -1073,9 +1090,6 @@ def recogniseGametypeID(db, cursor, topline, smallBlindLine, site_id, category,
else: 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)) 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() result=cursor.fetchone()
#print "recgt1 result=",result
#ret=result[0]
#print "recgt1 ret=",ret
#print "tried SELECTing gametypes.id, result:",result #print "tried SELECTing gametypes.id, result:",result
try: try:
@ -1109,18 +1123,16 @@ def recogniseGametypeID(db, cursor, topline, smallBlindLine, site_id, category,
cursor.execute("""INSERT INTO Gametypes cursor.execute("""INSERT INTO Gametypes
(siteId, type, base, category, limitType, hiLo, smallBlind, bigBlind, smallBet, bigBet) (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)) 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: else:
cursor.execute("""INSERT INTO Gametypes cursor.execute("""INSERT INTO Gametypes
(siteId, type, base, category, limitType, hiLo, smallBlind, bigBlind, smallBet, bigBet) (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 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(),) result=cursor.fetchone()
#print "recgt2 result=",result
#print "created new gametypes.id:",result #print "created new gametypes.id:",result
#print "recgt3: result=", result
return result[0] return result[0]
#end def recogniseGametypeID #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 i in range (len(actionTypes)): #iterate through streets
for j in range (len(actionTypes[i])): #iterate through names 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 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)" 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]))
, (handsPlayersIds[j], i, actionNos[i][j][k], actionTypes[i][j][k], allIns[i][j][k], actionAmounts[i][j][k]))
#end def storeActions #end def storeActions
def store_board_cards(cursor, hands_id, board_values, board_suits): 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])) board_values[4], board_suits[4]))
#end def store_board_cards #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 #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)) 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... #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)) cursor.execute("SELECT id FROM Hands WHERE siteHandNo=%s AND gametypeId=%s", (site_hand_no, gametype_id))
#return cursor.fetchall()[0][0] return cursor.fetchall()[0][0]
return db.insert_id() # mysql only
#end def storeHands #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=[] result=[]
if (category=="holdem"): if (category=="holdem"):
for i in range (len(player_ids)): 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], (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], card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1],
winnings[i], rakes[i], seatNos[i])) winnings[i], rakes[i], seatNos[i]))
#cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId=%s", (hands_id, player_ids[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(cursor.fetchall()[0][0])
result.append( db.insert_id() ) # mysql only
elif (category=="omahahi" or category=="omahahilo"): elif (category=="omahahi" or category=="omahahilo"):
for i in range (len(player_ids)): for i in range (len(player_ids)):
cursor.execute ("""INSERT INTO HandsPlayers 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][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], card_values[i][2], card_suits[i][2], card_values[i][3], card_suits[i][3],
winnings[i], rakes[i], seatNos[i])) winnings[i], rakes[i], seatNos[i]))
#cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[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(cursor.fetchall()[0][0])
result.append( db.insert_id() ) # mysql only
else: else:
raise FpdbError("invalid category") raise FpdbError("invalid category")
return result return result
#end def store_hands_players_holdem_omaha #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): card_values, card_suits, winnings, rakes, seatNos):
#stores hands_players rows for stud/razz games. returns an array of the resulting IDs #stores hands_players rows for stud/razz games. returns an array of the resulting IDs
result=[] 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][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][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])) 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])) 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(cursor.fetchall()[0][0])
result.append( db.insert_id() ) # mysql only
return result return result
#end def store_hands_players_stud #end def store_hands_players_stud
def store_hands_players_holdem_omaha_tourney(db, cursor, category, hands_id, player_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):
start_cashes, positions, card_values, card_suits, winnings, rakes, seatNos, tourneys_players_ids):
#stores hands_players for tourney holdem/omaha hands #stores hands_players for tourney holdem/omaha hands
result=[] result=[]
for i in range (len(player_ids)): 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])) winnings[i], rakes[i], tourneys_players_ids[i], seatNos[i]))
else: else:
raise FpdbError ("invalid card_values length:"+str(len(card_values[0]))) 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])) 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(cursor.fetchall()[0][0])
result.append( db.insert_id() ) # mysql only
return result return result
#end def store_hands_players_holdem_omaha_tourney #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): antes, card_values, card_suits, winnings, rakes, seatNos, tourneys_players_ids):
#stores hands_players for tourney stud/razz hands #stores hands_players for tourney stud/razz hands
result=[] 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][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][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])) 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])) 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(cursor.fetchall()[0][0])
result.append( db.insert_id() ) # mysql only
return result return result
#end def store_hands_players_stud_tourney #end def store_hands_players_stud_tourney
def generateHudCacheData(player_ids, base, category, action_types, allIns, actionTypeByNo def generateHudCacheData(player_ids, base, category, action_types, allIns, actionTypeByNo, winnings, totalWinnings, positions):
,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"""
"""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)
#setup subarrays of the result dictionary. #setup subarrays of the result dictionary.
street0VPI=[] street0VPI=[]
street0Aggr=[] street0Aggr=[]
@ -1692,7 +1692,7 @@ def generateHudCacheData(player_ids, base, category, action_types, allIns, actio
hudDataPositions.append('C') hudDataPositions.append('C')
elif pos>=2 and pos<=4: elif pos>=2 and pos<=4:
hudDataPositions.append('M') hudDataPositions.append('M')
elif pos>=5 and pos<=7: elif pos>=5 and pos<=8:
hudDataPositions.append('E') hudDataPositions.append('E')
### RHH Added this elif to handle being a dead hand before the BB (pos==9) ### RHH Added this elif to handle being a dead hand before the BB (pos==9)
elif pos==9: elif pos==9:
@ -1906,14 +1906,8 @@ def generateHudCacheData(player_ids, base, category, action_types, allIns, actio
street3CheckCallRaiseDone=[] street3CheckCallRaiseDone=[]
street4CheckCallRaiseChance=[] street4CheckCallRaiseChance=[]
street4CheckCallRaiseDone=[] street4CheckCallRaiseDone=[]
#print "b4 totprof calc, len(playerIds)=", len(player_ids) for player in range (len(player_ids)):
for pl in range (len(player_ids)): myTotalProfit=0
#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]
myStreet1CheckCallRaiseChance=False myStreet1CheckCallRaiseChance=False
myStreet1CheckCallRaiseDone=False myStreet1CheckCallRaiseDone=False
@ -1924,9 +1918,7 @@ def generateHudCacheData(player_ids, base, category, action_types, allIns, actio
myStreet4CheckCallRaiseChance=False myStreet4CheckCallRaiseChance=False
myStreet4CheckCallRaiseDone=False myStreet4CheckCallRaiseDone=False
#print "myTotalProfit=", myTotalProfit
totalProfit.append(myTotalProfit) totalProfit.append(myTotalProfit)
#print "totalProfit[]=", totalProfit
street1CheckCallRaiseChance.append(myStreet1CheckCallRaiseChance) street1CheckCallRaiseChance.append(myStreet1CheckCallRaiseChance)
street1CheckCallRaiseDone.append(myStreet1CheckCallRaiseDone) street1CheckCallRaiseDone.append(myStreet1CheckCallRaiseDone)
@ -1938,7 +1930,6 @@ def generateHudCacheData(player_ids, base, category, action_types, allIns, actio
street4CheckCallRaiseDone.append(myStreet4CheckCallRaiseDone) street4CheckCallRaiseDone.append(myStreet4CheckCallRaiseDone)
result['totalProfit']=totalProfit result['totalProfit']=totalProfit
#print "res[totalProfit]=", result['totalProfit']
result['street1CheckCallRaiseChance']=street1CheckCallRaiseChance result['street1CheckCallRaiseChance']=street1CheckCallRaiseChance
result['street1CheckCallRaiseDone']=street1CheckCallRaiseDone result['street1CheckCallRaiseDone']=street1CheckCallRaiseDone
@ -1974,8 +1965,6 @@ def generateFoldToCB(street, playerIDs, didStreetCB, streetCBDone, foldToStreetC
def storeHudCache(cursor, base, category, gametypeId, playerIds, hudImportData): def storeHudCache(cursor, base, category, gametypeId, playerIds, hudImportData):
# if (category=="holdem" or category=="omahahi" or category=="omahahilo"): # 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)): for player in range (len(playerIds)):
if base=="hold": 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])) 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['foldToStreet4CBChance'][player]: row[50]+=1
if hudImportData['foldToStreet4CBDone'][player]: row[51]+=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] row[52]+=hudImportData['totalProfit'][player]
if hudImportData['street1CheckCallRaiseChance'][player]: row[53]+=1 if hudImportData['street1CheckCallRaiseChance'][player]: row[53]+=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 48 KiB