pulled and merged from fpdboz
This commit is contained in:
commit
4dc15bfd94
109
pyfpdb/CarbonToFpdb.py
Normal file
109
pyfpdb/CarbonToFpdb.py
Normal 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)
|
||||
|
|
@ -53,7 +53,7 @@ if __name__ == "__main__":
|
|||
|
||||
(options, sys.argv) = parser.parse_args()
|
||||
|
||||
settings={'imp-callFpdbHud':False, 'db-backend':2}
|
||||
settings={'callFpdbHud':False, 'db-backend':2}
|
||||
settings['db-host']=options.server
|
||||
settings['db-user']=options.user
|
||||
settings['db-password']=options.password
|
||||
|
|
|
@ -114,6 +114,7 @@ class Game:
|
|||
stat.popup = stat_node.getAttribute("popup")
|
||||
stat.hudprefix = stat_node.getAttribute("hudprefix")
|
||||
stat.hudsuffix = stat_node.getAttribute("hudsuffix")
|
||||
stat.hudcolor = stat_node.getAttribute("hudcolor")
|
||||
|
||||
self.stats[stat.stat_name] = stat
|
||||
|
||||
|
@ -443,9 +444,9 @@ class Config:
|
|||
def get_import_parameters(self):
|
||||
imp = {}
|
||||
try:
|
||||
imp['callFpdbHud'] = self.callFpdbHud
|
||||
imp['interval'] = self.interval
|
||||
imp['hhArchiveBase'] = self.hhArchiveBase
|
||||
imp['callFpdbHud'] = self.imp.callFpdbHud
|
||||
imp['interval'] = self.imp.interval
|
||||
imp['hhArchiveBase'] = self.imp.hhArchiveBase
|
||||
except: # Default params
|
||||
imp['callFpdbHud'] = True
|
||||
imp['interval'] = 10
|
||||
|
@ -612,9 +613,7 @@ if __name__== "__main__":
|
|||
print "----------- END POPUP WINDOW FORMATS -----------"
|
||||
|
||||
print "\n----------- IMPORT -----------"
|
||||
tmp = c.get_import_parameters()
|
||||
for param in tmp:
|
||||
print " " + str(param) + ": " + str(tmp[param])
|
||||
print c.imp
|
||||
print "----------- END IMPORT -----------"
|
||||
|
||||
print "\n----------- TABLE VIEW -----------"
|
||||
|
|
|
@ -142,12 +142,27 @@ class Database:
|
|||
cards[s_dict['seat_number']] = s_dict
|
||||
return (cards)
|
||||
|
||||
def get_stats_from_hand(self, hand, player_id = False):
|
||||
def get_action_from_hand(self, hand_no):
|
||||
action = [ [], [], [], [], [] ]
|
||||
c = self.connection.cursor()
|
||||
c.execute(self.sql.query['get_action_from_hand'], (hand_no))
|
||||
for row in c.fetchall():
|
||||
street = row[0]
|
||||
act = row[1:]
|
||||
action[street].append(act)
|
||||
return action
|
||||
|
||||
def get_stats_from_hand(self, hand, aggregate = False):
|
||||
c = self.connection.cursor()
|
||||
|
||||
if not player_id: player_id = "%"
|
||||
if aggregate:
|
||||
query = 'get_stats_from_hand'
|
||||
subs = (hand, hand)
|
||||
else:
|
||||
query = 'get_stats_from_hand_aggregated'
|
||||
subs = (hand, hand, hand)
|
||||
|
||||
# get the players in the hand and their seats
|
||||
# c.execute(self.sql.query['get_players_from_hand'], (hand, player_id))
|
||||
c.execute(self.sql.query['get_players_from_hand'], (hand, ))
|
||||
names = {}
|
||||
seats = {}
|
||||
|
@ -156,8 +171,7 @@ class Database:
|
|||
seats[row[0]] = row[1]
|
||||
|
||||
# now get the stats
|
||||
# c.execute(self.sql.query['get_stats_from_hand'], (hand, hand, player_id))
|
||||
c.execute(self.sql.query['get_stats_from_hand'], (hand, hand))
|
||||
c.execute(self.sql.query[query], subs)
|
||||
colnames = [desc[0] for desc in c.description]
|
||||
stat_dict = {}
|
||||
for row in c.fetchall():
|
||||
|
|
176
pyfpdb/EverleafToFpdb.py
Executable file
176
pyfpdb/EverleafToFpdb.py
Executable 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
161
pyfpdb/FpdbRegex.py
Normal 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
|
||||
|
|
@ -73,13 +73,13 @@ class FpdbSQLQueries:
|
|||
|
||||
if(self.dbname == 'MySQL InnoDB'):
|
||||
self.query['createSitesTable'] = """CREATE TABLE Sites (
|
||||
id SMALLINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
|
||||
id SMALLINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
|
||||
name varchar(32) NOT NULL,
|
||||
currency char(3) NOT NULL)
|
||||
ENGINE=INNODB"""
|
||||
elif(self.dbname == 'PostgreSQL'):
|
||||
self.query['createSitesTable'] = """CREATE TABLE Sites (
|
||||
id SERIAL, PRIMARY KEY (id),
|
||||
id SERIAL UNIQUE, PRIMARY KEY (id),
|
||||
name varchar(32),
|
||||
currency char(3))"""
|
||||
elif(self.dbname == 'SQLite'):
|
||||
|
@ -92,7 +92,7 @@ class FpdbSQLQueries:
|
|||
|
||||
if(self.dbname == 'MySQL InnoDB'):
|
||||
self.query['createGametypesTable'] = """CREATE TABLE Gametypes (
|
||||
id SMALLINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
|
||||
id SMALLINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
|
||||
siteId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (siteId) REFERENCES Sites(id),
|
||||
type char(4) NOT NULL,
|
||||
base char(4) NOT NULL,
|
||||
|
@ -106,7 +106,7 @@ class FpdbSQLQueries:
|
|||
ENGINE=INNODB"""
|
||||
elif(self.dbname == 'PostgreSQL'):
|
||||
self.query['createGametypesTable'] = """CREATE TABLE Gametypes (
|
||||
id SERIAL, PRIMARY KEY (id),
|
||||
id SERIAL UNIQUE, PRIMARY KEY (id),
|
||||
siteId INTEGER, FOREIGN KEY (siteId) REFERENCES Sites(id),
|
||||
type char(4),
|
||||
base char(4),
|
||||
|
@ -127,7 +127,7 @@ class FpdbSQLQueries:
|
|||
|
||||
if(self.dbname == 'MySQL InnoDB'):
|
||||
self.query['createPlayersTable'] = """CREATE TABLE Players (
|
||||
id INT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
|
||||
id INT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
|
||||
name VARCHAR(32) CHARACTER SET utf8 NOT NULL,
|
||||
siteId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (siteId) REFERENCES Sites(id),
|
||||
comment text,
|
||||
|
@ -135,7 +135,7 @@ class FpdbSQLQueries:
|
|||
ENGINE=INNODB"""
|
||||
elif(self.dbname == 'PostgreSQL'):
|
||||
self.query['createPlayersTable'] = """CREATE TABLE Players (
|
||||
id SERIAL, PRIMARY KEY (id),
|
||||
id SERIAL UNIQUE, PRIMARY KEY (id),
|
||||
name VARCHAR(32),
|
||||
siteId INTEGER, FOREIGN KEY (siteId) REFERENCES Sites(id),
|
||||
comment text,
|
||||
|
@ -150,7 +150,7 @@ class FpdbSQLQueries:
|
|||
|
||||
if(self.dbname == 'MySQL InnoDB'):
|
||||
self.query['createAutoratesTable'] = """CREATE TABLE Autorates (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
|
||||
id BIGINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
|
||||
playerId INT UNSIGNED NOT NULL, FOREIGN KEY (playerId) REFERENCES Players(id),
|
||||
gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
|
||||
description varchar(50) NOT NULL,
|
||||
|
@ -160,7 +160,7 @@ class FpdbSQLQueries:
|
|||
ENGINE=INNODB"""
|
||||
elif(self.dbname == 'PostgreSQL'):
|
||||
self.query['createAutoratesTable'] = """CREATE TABLE Autorates (
|
||||
id BIGSERIAL, PRIMARY KEY (id),
|
||||
id BIGSERIAL UNIQUE, PRIMARY KEY (id),
|
||||
playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id),
|
||||
gametypeId INT, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
|
||||
description varchar(50),
|
||||
|
@ -177,7 +177,7 @@ class FpdbSQLQueries:
|
|||
|
||||
if(self.dbname == 'MySQL InnoDB'):
|
||||
self.query['createHandsTable'] = """CREATE TABLE Hands (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
|
||||
id BIGINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
|
||||
tableName VARCHAR(20) NOT NULL,
|
||||
siteHandNo BIGINT NOT NULL,
|
||||
gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
|
||||
|
@ -190,7 +190,7 @@ class FpdbSQLQueries:
|
|||
ENGINE=INNODB"""
|
||||
elif(self.dbname == 'PostgreSQL'):
|
||||
self.query['createHandsTable'] = """CREATE TABLE Hands (
|
||||
id BIGSERIAL, PRIMARY KEY (id),
|
||||
id BIGSERIAL UNIQUE, PRIMARY KEY (id),
|
||||
tableName VARCHAR(20),
|
||||
siteHandNo BIGINT,
|
||||
gametypeId INT, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
|
||||
|
@ -210,7 +210,7 @@ class FpdbSQLQueries:
|
|||
|
||||
if(self.dbname == 'MySQL InnoDB'):
|
||||
self.query['createBoardCardsTable'] = """CREATE TABLE BoardCards (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
|
||||
id BIGINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
|
||||
handId BIGINT UNSIGNED NOT NULL, FOREIGN KEY (handId) REFERENCES Hands(id),
|
||||
card1Value smallint NOT NULL,
|
||||
card1Suit char(1) NOT NULL,
|
||||
|
@ -225,7 +225,7 @@ class FpdbSQLQueries:
|
|||
ENGINE=INNODB"""
|
||||
elif(self.dbname == 'PostgreSQL'):
|
||||
self.query['createBoardCardsTable'] = """CREATE TABLE BoardCards (
|
||||
id BIGSERIAL, PRIMARY KEY (id),
|
||||
id BIGSERIAL UNIQUE, PRIMARY KEY (id),
|
||||
handId BIGINT, FOREIGN KEY (handId) REFERENCES Hands(id),
|
||||
card1Value smallint,
|
||||
card1Suit char(1),
|
||||
|
@ -247,7 +247,7 @@ class FpdbSQLQueries:
|
|||
|
||||
if(self.dbname == 'MySQL InnoDB'):
|
||||
self.query['createTourneyTypesTable'] = """CREATE TABLE TourneyTypes (
|
||||
id SMALLINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
|
||||
id SMALLINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
|
||||
siteId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (siteId) REFERENCES Sites(id),
|
||||
buyin INT NOT NULL,
|
||||
fee INT NOT NULL,
|
||||
|
@ -272,7 +272,7 @@ class FpdbSQLQueries:
|
|||
|
||||
if(self.dbname == 'MySQL InnoDB'):
|
||||
self.query['createTourneysTable'] = """CREATE TABLE Tourneys (
|
||||
id INT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
|
||||
id INT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
|
||||
tourneyTypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (tourneyTypeId) REFERENCES TourneyTypes(id),
|
||||
siteTourneyNo BIGINT NOT NULL,
|
||||
entries INT NOT NULL,
|
||||
|
@ -283,7 +283,7 @@ class FpdbSQLQueries:
|
|||
ENGINE=INNODB"""
|
||||
elif(self.dbname == 'PostgreSQL'):
|
||||
self.query['createTourneysTable'] = """CREATE TABLE Tourneys (
|
||||
id SERIAL, PRIMARY KEY (id),
|
||||
id SERIAL UNIQUE, PRIMARY KEY (id),
|
||||
tourneyTypeId INT, FOREIGN KEY (tourneyTypeId) REFERENCES TourneyTypes(id),
|
||||
siteTourneyNo BIGINT,
|
||||
entries INT,
|
||||
|
@ -300,7 +300,7 @@ class FpdbSQLQueries:
|
|||
|
||||
if(self.dbname == 'MySQL InnoDB'):
|
||||
self.query['createHandsPlayersTable'] = """CREATE TABLE HandsPlayers (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
|
||||
id BIGINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
|
||||
handId BIGINT UNSIGNED NOT NULL, FOREIGN KEY (handId) REFERENCES Hands(id),
|
||||
playerId INT UNSIGNED NOT NULL, FOREIGN KEY (playerId) REFERENCES Players(id),
|
||||
startCash INT NOT NULL,
|
||||
|
@ -332,7 +332,7 @@ class FpdbSQLQueries:
|
|||
ENGINE=INNODB"""
|
||||
elif(self.dbname == 'PostgreSQL'):
|
||||
self.query['createHandsPlayersTable'] = """CREATE TABLE HandsPlayers (
|
||||
id BIGSERIAL, PRIMARY KEY (id),
|
||||
id BIGSERIAL UNIQUE, PRIMARY KEY (id),
|
||||
handId BIGINT, FOREIGN KEY (handId) REFERENCES Hands(id),
|
||||
playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id),
|
||||
startCash INT,
|
||||
|
@ -370,7 +370,7 @@ class FpdbSQLQueries:
|
|||
|
||||
if(self.dbname == 'MySQL InnoDB'):
|
||||
self.query['createTourneysPlayersTable'] = """CREATE TABLE TourneysPlayers (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
|
||||
id BIGINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
|
||||
tourneyId INT UNSIGNED NOT NULL, FOREIGN KEY (tourneyId) REFERENCES Tourneys(id),
|
||||
playerId INT UNSIGNED NOT NULL, FOREIGN KEY (playerId) REFERENCES Players(id),
|
||||
payinAmount INT NOT NULL,
|
||||
|
@ -381,7 +381,7 @@ class FpdbSQLQueries:
|
|||
ENGINE=INNODB"""
|
||||
elif(self.dbname == 'PostgreSQL'):
|
||||
self.query['createTourneysPlayersTable'] = """CREATE TABLE TourneysPlayers (
|
||||
id BIGSERIAL, PRIMARY KEY (id),
|
||||
id BIGSERIAL UNIQUE, PRIMARY KEY (id),
|
||||
tourneyId INT, FOREIGN KEY (tourneyId) REFERENCES Tourneys(id),
|
||||
playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id),
|
||||
payinAmount INT,
|
||||
|
@ -399,7 +399,7 @@ class FpdbSQLQueries:
|
|||
|
||||
if(self.dbname == 'MySQL InnoDB'):
|
||||
self.query['createHandsActionsTable'] = """CREATE TABLE HandsActions (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
|
||||
id BIGINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
|
||||
handPlayerId BIGINT UNSIGNED NOT NULL, FOREIGN KEY (handPlayerId) REFERENCES HandsPlayers(id),
|
||||
street SMALLINT NOT NULL,
|
||||
actionNo SMALLINT NOT NULL,
|
||||
|
@ -411,7 +411,7 @@ class FpdbSQLQueries:
|
|||
ENGINE=INNODB"""
|
||||
elif(self.dbname == 'PostgreSQL'):
|
||||
self.query['createHandsActionsTable'] = """CREATE TABLE HandsActions (
|
||||
id BIGSERIAL, PRIMARY KEY (id),
|
||||
id BIGSERIAL UNIQUE, PRIMARY KEY (id),
|
||||
handPlayerId BIGINT, FOREIGN KEY (handPlayerId) REFERENCES HandsPlayers(id),
|
||||
street SMALLINT,
|
||||
actionNo SMALLINT,
|
||||
|
@ -430,7 +430,7 @@ class FpdbSQLQueries:
|
|||
|
||||
if(self.dbname == 'MySQL InnoDB'):
|
||||
self.query['createHudCacheTable'] = """CREATE TABLE HudCache (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
|
||||
id BIGINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
|
||||
gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
|
||||
playerId INT UNSIGNED NOT NULL, FOREIGN KEY (playerId) REFERENCES Players(id),
|
||||
activeSeats SMALLINT NOT NULL,
|
||||
|
@ -503,7 +503,7 @@ class FpdbSQLQueries:
|
|||
ENGINE=INNODB"""
|
||||
elif(self.dbname == 'PostgreSQL'):
|
||||
self.query['createHudCacheTable'] = """CREATE TABLE HudCache (
|
||||
id BIGSERIAL, PRIMARY KEY (id),
|
||||
id BIGSERIAL UNIQUE, PRIMARY KEY (id),
|
||||
gametypeId INT, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
|
||||
playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id),
|
||||
activeSeats SMALLINT,
|
||||
|
@ -576,26 +576,23 @@ class FpdbSQLQueries:
|
|||
if(self.dbname == 'MySQL InnoDB'):
|
||||
self.query['addTourneyIndex'] = """ALTER TABLE Tourneys ADD INDEX siteTourneyNo(siteTourneyNo)"""
|
||||
elif(self.dbname == 'PostgreSQL'):
|
||||
# FIXME: This query has a different syntax
|
||||
self.query['addTourneyIndex'] = """ALTER TABLE Tourneys ADD INDEX siteTourneyNo(siteTourneyNo)"""
|
||||
self.query['addTourneyIndex'] = """CREATE INDEX siteTourneyNo ON Tourneys (siteTourneyNo)"""
|
||||
elif(self.dbname == 'SQLite'):
|
||||
self.query['addHandsIndex'] = """ """
|
||||
|
||||
if(self.dbname == 'MySQL InnoDB'):
|
||||
self.query['addHandsIndex'] = """ALTER TABLE Hands ADD INDEX siteHandNo(siteHandNo)"""
|
||||
elif(self.dbname == 'PostgreSQL'):
|
||||
# FIXME: This query has a different syntax
|
||||
self.query['addHandsIndex'] = """ALTER TABLE Hands ADD INDEX siteHandNo(siteHandNo)"""
|
||||
self.query['addHandsIndex'] = """CREATE INDEX siteHandNo ON Hands (siteHandNo)"""
|
||||
elif(self.dbname == 'SQLite'):
|
||||
self.query['addHandsIndex'] = """ """
|
||||
|
||||
if(self.dbname == 'MySQL InnoDB'):
|
||||
self.query['addPlayersIndex'] = """ALTER TABLE Players ADD INDEX name(name)"""
|
||||
elif(self.dbname == 'PostgreSQL'):
|
||||
# FIXME: This query has a different syntax
|
||||
self.query['addHandsIndex'] = """ALTER TABLE Hands ADD INDEX siteHandNo(siteHandNo)"""
|
||||
self.query['addPlayersIndex'] = """CREATE INDEX name ON Players (name)"""
|
||||
elif(self.dbname == 'SQLite'):
|
||||
self.query['addHandsIndex'] = """ """
|
||||
self.query['addPlayersIndex'] = """ """
|
||||
|
||||
################################
|
||||
# Queries used in GuiGraphViewer
|
||||
|
@ -633,6 +630,11 @@ class FpdbSQLQueries:
|
|||
WHERE Players.name = %s AND HandsPlayers.handId = %s
|
||||
AND Players.siteId = %s AND (tourneysPlayersId IS NULL)"""
|
||||
|
||||
if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL'):
|
||||
self.query['getPlayerId'] = """SELECT id from Players where name = %s"""
|
||||
elif(self.dbname == 'SQLite'):
|
||||
self.query['getPlayerId'] = """SELECT id from Players where name = %s"""
|
||||
|
||||
if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL'):
|
||||
self.query['getRingProfitAllHandsPlayerIdSite'] = """
|
||||
SELECT hp.handId, hp.winnings, SUM(ha.amount), hp.winnings - SUM(ha.amount)
|
||||
|
@ -659,38 +661,6 @@ class FpdbSQLQueries:
|
|||
GROUP BY hp.handId, hp.winnings, h.handStart
|
||||
ORDER BY h.handStart"""
|
||||
|
||||
# Returns the profit for a given ring game handId, Total pot - money invested by playerId - WRONG, returns players costs
|
||||
if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL'):
|
||||
self.query['getRingProfitFromHandId'] = """SELECT SUM(amount) FROM HandsActions
|
||||
INNER JOIN HandsPlayers ON HandsActions.handPlayerId = HandsPlayers.id
|
||||
INNER JOIN Players ON HandsPlayers.playerId = Players.id
|
||||
WHERE Players.name = %s AND HandsPlayers.handId = %s
|
||||
AND Players.siteId = %s AND (tourneysPlayersId IS NULL)"""
|
||||
elif(self.dbname == 'SQLite'):
|
||||
#Probably doesn't work.
|
||||
self.query['getRingProfitFromHandId'] = """SELECT SUM(amount) FROM HandsActions
|
||||
INNER JOIN HandsPlayers ON HandsActions.handPlayerId = HandsPlayers.id
|
||||
INNER JOIN Players ON HandsPlayers.playerId = Players.id
|
||||
WHERE Players.name = %s AND HandsPlayers.handId = %s
|
||||
AND Players.siteId = %s AND (tourneysPlayersId IS NULL)"""
|
||||
|
||||
# Returns a list of the tables in the database
|
||||
if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL'):
|
||||
self.query['getTableList'] = """
|
||||
SELECT table_name
|
||||
FROM information_schema.tables
|
||||
WHERE table_type = 'BASE TABLE'
|
||||
AND table_schema = %s
|
||||
"""
|
||||
elif(self.dbname == 'getTableList'):
|
||||
#Probably doesn't work.
|
||||
self.query['getTableList'] = """
|
||||
SELECT table_name
|
||||
FROM information_schema.tables
|
||||
WHERE table_type = 'BASE TABLE'
|
||||
AND table_schema = %s
|
||||
"""
|
||||
|
||||
if(self.dbname == 'MySQL InnoDB'):
|
||||
self.query['playerStats'] = """
|
||||
SELECT stats.gametypeId
|
||||
|
@ -733,8 +703,7 @@ class FpdbSQLQueries:
|
|||
from Gametypes gt
|
||||
inner join Sites s on s.Id = gt.siteId
|
||||
inner join HudCache hc on hc.gameTypeId = gt.Id
|
||||
where gt.limittype = 'nl'
|
||||
and hc.playerId in (3) # use <player_test> here?
|
||||
where hc.playerId in <player_test>
|
||||
# use <gametype_test> here ?
|
||||
group by hc.gametypeId
|
||||
) stats
|
||||
|
@ -747,7 +716,7 @@ class FpdbSQLQueries:
|
|||
from HandsPlayers hp
|
||||
inner join Hands h ON h.id = hp.handId
|
||||
inner join HandsActions ha ON ha.handPlayerId = hp.id
|
||||
where hp.playerId in (3) # use <player_test> here?
|
||||
where hp.playerId in <player_test>
|
||||
# use <gametype_test> here ?
|
||||
and hp.tourneysPlayersId IS NULL
|
||||
group by hp.handId, h.gameTypeId, hp.position, hp.winnings
|
||||
|
|
|
@ -40,7 +40,7 @@ class GuiAutoImport (threading.Thread):
|
|||
|
||||
self.input_settings = {}
|
||||
|
||||
self.importer = fpdb_import.Importer(self, self.settings, self.config)
|
||||
self.importer = fpdb_import.Importer(self,self.settings, self.config)
|
||||
self.importer.setCallHud(True)
|
||||
self.importer.setMinPrint(30)
|
||||
self.importer.setQuiet(False)
|
||||
|
@ -60,7 +60,7 @@ class GuiAutoImport (threading.Thread):
|
|||
self.mainVBox.pack_start(self.settingsHBox, False, True, 0)
|
||||
self.settingsHBox.show()
|
||||
|
||||
self.intervalLabel = gtk.Label("Time between imports in seconds:")
|
||||
self.intervalLabel = gtk.Label("Interval (ie. break) between imports in seconds:")
|
||||
self.settingsHBox.pack_start(self.intervalLabel)
|
||||
self.intervalLabel.show()
|
||||
|
||||
|
|
|
@ -22,14 +22,17 @@ import pygtk
|
|||
pygtk.require('2.0')
|
||||
import gtk
|
||||
import os #todo: remove this once import_dir is in fpdb_import
|
||||
from time import time
|
||||
|
||||
class GuiBulkImport (threading.Thread):
|
||||
def import_dir(self):
|
||||
"""imports a directory, non-recursive. todo: move this to fpdb_import so CLI can use it"""
|
||||
self.path=self.inputFile
|
||||
self.importer.addImportDirectory(self.path)
|
||||
self.importer.setCallHud(False)
|
||||
starttime = time()
|
||||
self.importer.runImport()
|
||||
print "GuiBulkImport.import_dir done"
|
||||
print "GuiBulkImport.import_dir done in %s" %(time() - starttime)
|
||||
|
||||
def load_clicked(self, widget, data=None):
|
||||
self.inputFile=self.chooser.get_filename()
|
||||
|
@ -64,6 +67,7 @@ class GuiBulkImport (threading.Thread):
|
|||
self.import_dir()
|
||||
else:
|
||||
self.importer.addImportFile(self.inputFile)
|
||||
self.importer.setCallHud(False)
|
||||
self.importer.runImport()
|
||||
self.importer.clearFileList()
|
||||
|
||||
|
@ -80,7 +84,7 @@ class GuiBulkImport (threading.Thread):
|
|||
self.db=db
|
||||
self.settings=settings
|
||||
self.config=config
|
||||
self.importer = fpdb_import.Importer(self,self.settings)
|
||||
self.importer = fpdb_import.Importer(self,self.settings, config)
|
||||
|
||||
self.vbox=gtk.VBox(False,1)
|
||||
self.vbox.show()
|
||||
|
|
|
@ -20,17 +20,22 @@ import pygtk
|
|||
pygtk.require('2.0')
|
||||
import gtk
|
||||
import os
|
||||
from time import time
|
||||
#import pokereval
|
||||
|
||||
try:
|
||||
import matplotlib
|
||||
matplotlib.use('GTK')
|
||||
from matplotlib.figure import Figure
|
||||
from matplotlib.backends.backend_gtk import FigureCanvasGTK as FigureCanvas
|
||||
from matplotlib.backends.backend_gtkagg import NavigationToolbar2GTKAgg as NavigationToolbar
|
||||
from numpy import arange, cumsum
|
||||
from pylab import *
|
||||
except:
|
||||
print "Failed to load libs for graphing, graphing will not function. Please install numpy and matplotlib if you want to use graphs."
|
||||
print "This is of no consequence for other parts of the program, e.g. import and HUD are NOT affected by this problem."
|
||||
print """Failed to load libs for graphing, graphing will not function. Please in
|
||||
stall numpy and matplotlib if you want to use graphs."""
|
||||
print """This is of no consequence for other parts of the program, e.g. import
|
||||
and HUD are NOT affected by this problem."""
|
||||
|
||||
import fpdb_import
|
||||
import fpdb_db
|
||||
|
@ -38,21 +43,24 @@ import fpdb_db
|
|||
class GuiGraphViewer (threading.Thread):
|
||||
def get_vbox(self):
|
||||
"""returns the vbox of this thread"""
|
||||
return self.mainVBox
|
||||
return self.mainHBox
|
||||
#end def get_vbox
|
||||
|
||||
def showClicked(self, widget, data):
|
||||
def generateGraph(self, widget, data):
|
||||
try: self.canvas.destroy()
|
||||
except AttributeError: pass
|
||||
|
||||
name=self.nameEntry.get_text()
|
||||
# Whaich sites are selected?
|
||||
# TODO:
|
||||
# What hero names for the selected site?
|
||||
# TODO:
|
||||
|
||||
site=self.siteEntry.get_text()
|
||||
name = self.heroes[self.sites]
|
||||
|
||||
if site=="PS":
|
||||
if self.sites == "PokerStars":
|
||||
site=2
|
||||
sitename="PokerStars: "
|
||||
elif site=="FTP":
|
||||
elif self.sites=="Full Tilt":
|
||||
site=1
|
||||
sitename="Full Tilt: "
|
||||
else:
|
||||
|
@ -64,47 +72,180 @@ class GuiGraphViewer (threading.Thread):
|
|||
#Set graph properties
|
||||
self.ax = self.fig.add_subplot(111)
|
||||
|
||||
#
|
||||
#Get graph data from DB
|
||||
starttime = time()
|
||||
line = self.getRingProfitGraph(name, site)
|
||||
print "Graph generated in: %s" %(time() - starttime)
|
||||
|
||||
self.ax.set_title("Profit graph for ring games")
|
||||
|
||||
#Set axis labels and grid overlay properites
|
||||
self.ax.set_xlabel("Hands", fontsize = 12)
|
||||
self.ax.set_ylabel("$", fontsize = 12)
|
||||
self.ax.grid(color='g', linestyle=':', linewidth=0.2)
|
||||
text = "All Hands, " + sitename + str(name)
|
||||
#This line will crash if no hands exist in the query.
|
||||
text = "All Hands, " + sitename + str(name) + "\nProfit: $" + str(line[-1]) + "\nTotal Hands: " + str(len(line))
|
||||
|
||||
self.ax.annotate (text, (61,25), xytext =(0.1, 0.9) , textcoords ="axes fraction" ,)
|
||||
self.ax.annotate(text,
|
||||
xy=(10, -10),
|
||||
xycoords='axes points',
|
||||
horizontalalignment='left', verticalalignment='top',
|
||||
fontsize=10)
|
||||
|
||||
#Get graph data from DB
|
||||
line = self.getRingProfitGraph(name, site)
|
||||
|
||||
#Draw plot
|
||||
self.ax.plot(line,)
|
||||
|
||||
self.canvas = FigureCanvas(self.fig) # a gtk.DrawingArea
|
||||
self.mainVBox.pack_start(self.canvas)
|
||||
self.graphBox.add(self.canvas)
|
||||
self.canvas.show()
|
||||
#end of def showClicked
|
||||
|
||||
def getRingProfitGraph(self, name, site):
|
||||
#self.cursor.execute(self.sql.query['getRingWinningsAllGamesPlayerIdSite'], (name, site))
|
||||
self.cursor.execute(self.sql.query['getRingProfitAllHandsPlayerIdSite'], (name, site))
|
||||
# returns (HandId,Winnings,Costs,Profit)
|
||||
#returns (HandId,Winnings,Costs,Profit)
|
||||
winnings = self.db.cursor.fetchall()
|
||||
|
||||
#profit=range(len(winnings))
|
||||
#for i in profit:
|
||||
# self.cursor.execute(self.sql.query['getRingProfitFromHandId'], (name, winnings[i][0], site))
|
||||
# spent = self.db.cursor.fetchone()
|
||||
# profit[i]=(i, winnings[i][1]-spent[0])
|
||||
|
||||
#y=map(lambda x:float(x[1]), profit)
|
||||
y=map(lambda x:float(x[3]), winnings)
|
||||
|
||||
line = cumsum(y)
|
||||
return line/100
|
||||
#end of def getRingProfitGraph
|
||||
|
||||
def createPlayerLine(self, hbox, site, player):
|
||||
label = gtk.Label(site +" id:")
|
||||
hbox.pack_start(label, False, False, 0)
|
||||
label.show()
|
||||
|
||||
pname = gtk.Entry()
|
||||
pname.set_text(player)
|
||||
pname.set_width_chars(20)
|
||||
hbox.pack_start(pname, False, True, 0)
|
||||
#TODO: Need to connect a callback here
|
||||
pname.connect("changed", self.__set_hero_name, site)
|
||||
#TODO: Look at GtkCompletion - to fill out usernames
|
||||
pname.show()
|
||||
|
||||
self.__set_hero_name(pname, site)
|
||||
|
||||
def __set_hero_name(self, w, site):
|
||||
self.heroes[site] = w.get_text()
|
||||
print "DEBUG: settings heroes[%s]: %s"%(site, self.heroes[site])
|
||||
|
||||
def createSiteLine(self, hbox, site):
|
||||
cb = gtk.CheckButton(site)
|
||||
cb.connect('clicked', self.__set_site_select, site)
|
||||
hbox.pack_start(cb, False, False, 0)
|
||||
cb.show()
|
||||
|
||||
def __set_site_select(self, w, site):
|
||||
# This doesn't behave as intended - self.site only allows 1 site for the moment.
|
||||
self.sites = site
|
||||
print "self.sites set to %s" %(self.sites)
|
||||
|
||||
def fillPlayerFrame(self, vbox):
|
||||
for site in self.conf.supported_sites.keys():
|
||||
pathHBox = gtk.HBox(False, 0)
|
||||
vbox.pack_start(pathHBox, False, True, 0)
|
||||
pathHBox.show()
|
||||
|
||||
player = self.conf.supported_sites[site].screen_name
|
||||
self.createPlayerLine(pathHBox, site, player)
|
||||
|
||||
def fillSitesFrame(self, vbox):
|
||||
for site in self.conf.supported_sites.keys():
|
||||
hbox = gtk.HBox(False, 0)
|
||||
vbox.pack_start(hbox, False, True, 0)
|
||||
hbox.show()
|
||||
self.createSiteLine(hbox, site)
|
||||
|
||||
def fillDateFrame(self, vbox):
|
||||
# Hat tip to Mika Bostrom - calendar code comes from PokerStats
|
||||
hbox = gtk.HBox()
|
||||
vbox.pack_start(hbox, False, True, 0)
|
||||
hbox.show()
|
||||
|
||||
lbl_start = gtk.Label('From:')
|
||||
lbl_start.show()
|
||||
|
||||
btn_start = gtk.Button()
|
||||
btn_start.set_image(gtk.image_new_from_stock(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON))
|
||||
btn_start.connect('clicked', self.__calendar_dialog, self.start_date)
|
||||
btn_start.show()
|
||||
|
||||
hbox.pack_start(lbl_start, expand=False, padding=3)
|
||||
hbox.pack_start(btn_start, expand=False, padding=3)
|
||||
hbox.pack_start(self.start_date, expand=False, padding=2)
|
||||
self.start_date.show()
|
||||
|
||||
#New row for end date
|
||||
hbox = gtk.HBox()
|
||||
vbox.pack_start(hbox, False, True, 0)
|
||||
hbox.show()
|
||||
|
||||
lbl_end = gtk.Label(' To:')
|
||||
lbl_end.show()
|
||||
btn_end = gtk.Button()
|
||||
btn_end.set_image(gtk.image_new_from_stock(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON))
|
||||
btn_end.connect('clicked', self.__calendar_dialog, self.end_date)
|
||||
btn_end.show()
|
||||
|
||||
btn_clear = gtk.Button(label=' Clear Dates ')
|
||||
btn_clear.connect('clicked', self.__clear_dates)
|
||||
btn_clear.show()
|
||||
|
||||
hbox.pack_start(lbl_end, expand=False, padding=3)
|
||||
hbox.pack_start(btn_end, expand=False, padding=3)
|
||||
hbox.pack_start(self.end_date, expand=False, padding=2)
|
||||
self.end_date.show()
|
||||
|
||||
hbox.pack_start(btn_clear, expand=False, padding=15)
|
||||
|
||||
def __calendar_dialog(self, widget, entry):
|
||||
d = gtk.Window(gtk.WINDOW_TOPLEVEL)
|
||||
d.set_title('Pick a date')
|
||||
|
||||
vb = gtk.VBox()
|
||||
cal = gtk.Calendar()
|
||||
vb.pack_start(cal, expand=False, padding=0)
|
||||
|
||||
btn = gtk.Button('Done')
|
||||
btn.connect('clicked', self.__get_date, cal, entry, d)
|
||||
|
||||
vb.pack_start(btn, expand=False, padding=4)
|
||||
|
||||
d.add(vb)
|
||||
d.set_position(gtk.WIN_POS_MOUSE)
|
||||
d.show_all()
|
||||
|
||||
def __clear_dates(self, w):
|
||||
self.start_date.set_text('')
|
||||
self.end_date.set_text('')
|
||||
|
||||
def __get_dates(self):
|
||||
t1 = self.start_date.get_text()
|
||||
t2 = self.end_date.get_text()
|
||||
return (t1, t2)
|
||||
|
||||
def __get_date(self, widget, calendar, entry, win):
|
||||
# year and day are correct, month is 0..11
|
||||
(year, month, day) = calendar.get_date()
|
||||
month += 1
|
||||
ds = '%04d-%02d-%02d' % (year, month, day)
|
||||
entry.set_text(ds)
|
||||
win.destroy()
|
||||
|
||||
def exportGraph (self, widget, data):
|
||||
dia_chooser = gtk.FileChooserDialog(title="Please choose the directory you wish to export to:",
|
||||
action=gtk.FILE_CHOOSER_ACTION_OPEN,
|
||||
buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
|
||||
|
||||
response = dia_chooser.run()
|
||||
if response == gtk.RESPONSE_OK:
|
||||
self.exportDir = dia_chooser.get_filename()
|
||||
elif response == gtk.RESPONSE_CANCEL:
|
||||
print 'Closed, no graph exported'
|
||||
dia_chooser.destroy()
|
||||
|
||||
def __init__(self, db, settings, querylist, config, debug=True):
|
||||
"""Constructor for GraphViewer"""
|
||||
self.debug=debug
|
||||
|
@ -113,37 +254,70 @@ class GuiGraphViewer (threading.Thread):
|
|||
self.cursor=db.cursor
|
||||
self.settings=settings
|
||||
self.sql=querylist
|
||||
self.conf = config
|
||||
|
||||
self.mainVBox = gtk.VBox(False, 0)
|
||||
self.mainVBox.show()
|
||||
self.sites = "PokerStars"
|
||||
self.heroes = {}
|
||||
|
||||
self.settingsHBox = gtk.HBox(False, 0)
|
||||
self.mainVBox.pack_start(self.settingsHBox, False, True, 0)
|
||||
self.settingsHBox.show()
|
||||
# For use in date ranges.
|
||||
self.start_date = gtk.Entry(max=12)
|
||||
self.end_date = gtk.Entry(max=12)
|
||||
self.start_date.set_property('editable', False)
|
||||
self.end_date.set_property('editable', False)
|
||||
|
||||
self.nameLabel = gtk.Label("Name of the player to be graphed:")
|
||||
self.settingsHBox.pack_start(self.nameLabel)
|
||||
self.nameLabel.show()
|
||||
self.mainHBox = gtk.HBox(False, 0)
|
||||
self.mainHBox.show()
|
||||
|
||||
self.nameEntry=gtk.Entry()
|
||||
self.nameEntry.set_text("name")
|
||||
self.settingsHBox.pack_start(self.nameEntry)
|
||||
self.nameEntry.show()
|
||||
self.leftPanelBox = gtk.VBox(False, 0)
|
||||
self.graphBox = gtk.VBox(False, 0)
|
||||
|
||||
self.siteLabel = gtk.Label("Site (PS or FTP):")
|
||||
self.settingsHBox.pack_start(self.siteLabel)
|
||||
self.siteLabel.show()
|
||||
self.hpane = gtk.HPaned()
|
||||
self.hpane.pack1(self.leftPanelBox)
|
||||
self.hpane.pack2(self.graphBox)
|
||||
self.hpane.show()
|
||||
|
||||
self.siteEntry=gtk.Entry()
|
||||
self.siteEntry.set_text("PS")
|
||||
self.settingsHBox.pack_start(self.siteEntry)
|
||||
self.siteEntry.show()
|
||||
self.mainHBox.add(self.hpane)
|
||||
|
||||
#Note: Assumes PokerStars is in the config
|
||||
self.nameEntry.set_text(config.supported_sites["PokerStars"].screen_name)
|
||||
playerFrame = gtk.Frame("Hero:")
|
||||
playerFrame.set_label_align(0.0, 0.0)
|
||||
playerFrame.show()
|
||||
vbox = gtk.VBox(False, 0)
|
||||
vbox.show()
|
||||
|
||||
self.showButton=gtk.Button("Show/Refresh")
|
||||
self.showButton.connect("clicked", self.showClicked, "show clicked")
|
||||
self.settingsHBox.pack_start(self.showButton)
|
||||
self.showButton.show()
|
||||
#end of GuiGraphViewer.__init__
|
||||
self.fillPlayerFrame(vbox)
|
||||
playerFrame.add(vbox)
|
||||
|
||||
sitesFrame = gtk.Frame("Sites:")
|
||||
sitesFrame.set_label_align(0.0, 0.0)
|
||||
sitesFrame.show()
|
||||
vbox = gtk.VBox(False, 0)
|
||||
vbox.show()
|
||||
|
||||
self.fillSitesFrame(vbox)
|
||||
sitesFrame.add(vbox)
|
||||
|
||||
dateFrame = gtk.Frame("Date:")
|
||||
dateFrame.set_label_align(0.0, 0.0)
|
||||
dateFrame.show()
|
||||
vbox = gtk.VBox(False, 0)
|
||||
vbox.show()
|
||||
|
||||
self.fillDateFrame(vbox)
|
||||
dateFrame.add(vbox)
|
||||
|
||||
graphButton=gtk.Button("Generate Graph")
|
||||
graphButton.connect("clicked", self.generateGraph, "cliced data")
|
||||
graphButton.show()
|
||||
|
||||
self.exportButton=gtk.Button("Export to File")
|
||||
self.exportButton.connect("clicked", self.exportGraph, "show clicked")
|
||||
self.exportButton.show()
|
||||
|
||||
self.leftPanelBox.add(playerFrame)
|
||||
self.leftPanelBox.add(sitesFrame)
|
||||
self.leftPanelBox.add(dateFrame)
|
||||
self.leftPanelBox.add(graphButton)
|
||||
self.leftPanelBox.add(self.exportButton)
|
||||
|
||||
self.leftPanelBox.show()
|
||||
self.graphBox.show()
|
||||
|
|
165
pyfpdb/GuiPlayerStats.py
Normal file
165
pyfpdb/GuiPlayerStats.py
Normal 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)
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<FreePokerToolsConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FreePokerToolsConfig.xsd">
|
||||
<supported_sites>
|
||||
<site enabled="True" site_name="PokerStars" table_finder="PokerStars.exe" screen_name="DO NOT NEED THIS YET" site_path="~/.wine/drive_c/Program Files/PokerStars/" HH_path="~/.wine/drive_c/Program Files/PokerStars/HandHistory/abc/" decoder="pokerstars_decode_table" converter="passthrough" supported_games="holdem,razz,omahahi,omahahilo,studhi,studhilo">
|
||||
<site enabled="True" site_name="PokerStars" table_finder="PokerStars.exe" screen_name="ENTER HERO NAME" site_path="~/.wine/drive_c/Program Files/PokerStars/" HH_path="~/.wine/drive_c/Program Files/PokerStars/HandHistory/abc/" decoder="pokerstars_decode_table" converter="passthrough" supported_games="holdem,razz,omahahi,omahahilo,studhi,studhilo">
|
||||
<layout max="8" width="792" height="546" fav_seat="0">
|
||||
<location seat="1" x="684" y="61"> </location>
|
||||
<location seat="2" x="689" y="239"> </location>
|
||||
|
@ -49,7 +49,7 @@
|
|||
<location seat="2" x="10" y="288"> </location>
|
||||
</layout>
|
||||
</site>
|
||||
<site enabled="True" site_name="Full Tilt" table_finder="FullTiltPoker.exe" screen_name="DO NOT NEED THIS YET" site_path="~/.wine/drive_c/Program Files/Full Tilt Poker/" HH_path="~/.wine/drive_c/Program Files/Full Tilt Poker/HandHistory/abc/" decoder="fulltilt_decode_table" converter="passthrough" supported_games="holdem,razz,omahahi,omahahilo,studhi,studhilo">
|
||||
<site enabled="True" site_name="Full Tilt" table_finder="FullTiltPoker.exe" screen_name="ENTER HERO NAME" site_path="~/.wine/drive_c/Program Files/Full Tilt Poker/" HH_path="~/.wine/drive_c/Program Files/Full Tilt Poker/HandHistory/abc/" decoder="fulltilt_decode_table" converter="passthrough" supported_games="holdem,razz,omahahi,omahahilo,studhi,studhilo">
|
||||
<layout fav_seat="0" height="547" max="8" width="794">
|
||||
<location seat="1" x="640" y="64"> </location>
|
||||
<location seat="2" x="650" y="230"> </location>
|
||||
|
@ -84,7 +84,7 @@
|
|||
<location seat="9" x="70" y="53"> </location>
|
||||
</layout>
|
||||
</site>
|
||||
<site enabled="False" site_name="Everleaf" table_finder="Poker.exe" screen_name="DO NOT NEED THIS YET" site_path="" HH_path="" decoder="Unknown" converter="EverleafToFpdb" supported_games="holdem,razz,omahahi,omahahilo,studhi" fgcolor="#48D1CC" bgcolor="#000000">
|
||||
<site enabled="False" site_name="Everleaf" table_finder="Poker.exe" screen_name="ENTER HERO NAME" site_path="" HH_path="" decoder="Unknown" converter="EverleafToFpdb" supported_games="holdem,razz,omahahi,omahahilo,studhi" fgcolor="#48D1CC" bgcolor="#000000" opacity="0.75">
|
||||
<layout fav_seat="0" height="546" max="6" width="792">
|
||||
<location seat="1" x="581" y="109"> </location>
|
||||
<location seat="2" x="605" y="287"> </location>
|
||||
|
@ -187,7 +187,7 @@
|
|||
<pu_stat pu_stat_name="ffreq_4"> </pu_stat>
|
||||
</pu>
|
||||
</popup_windows>
|
||||
<import callFpdbHud = "True" interval = "10" ></import>
|
||||
<import callFpdbHud = "True" interval = "10" hhArchiveBase="~/.fpdb/HandHistories/"></import>
|
||||
<tv combinedStealFold = "True" combined2B3B = "True" combinedPostflop = "True"></tv>
|
||||
|
||||
<supported_databases>
|
||||
|
|
427
pyfpdb/HandHistoryConverter.py
Normal file
427
pyfpdb/HandHistoryConverter.py
Normal 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])
|
|
@ -60,7 +60,7 @@ class Hud:
|
|||
self.stat_windows = {}
|
||||
self.popup_windows = {}
|
||||
self.aux_windows = []
|
||||
self.font = pango.FontDescription("Sans 8")
|
||||
self.font = pango.FontDescription("Sans 7")
|
||||
|
||||
# Set up a main window for this this instance of the HUD
|
||||
self.main_window = gtk.Window()
|
||||
|
@ -92,14 +92,22 @@ class Hud:
|
|||
self.menu.append(self.item1)
|
||||
self.item1.connect("activate", self.kill_hud)
|
||||
self.item1.show()
|
||||
|
||||
self.item2 = gtk.MenuItem('Save Layout')
|
||||
self.menu.append(self.item2)
|
||||
self.item2.connect("activate", self.save_layout)
|
||||
self.item2.show()
|
||||
|
||||
self.item3 = gtk.MenuItem('Reposition Stats')
|
||||
self.menu.append(self.item3)
|
||||
self.item3.connect("activate", self.reposition_windows)
|
||||
self.item3.show()
|
||||
|
||||
self.item4 = gtk.MenuItem('Debug Stat Windows')
|
||||
self.menu.append(self.item4)
|
||||
self.item4.connect("activate", self.debug_stat_windows)
|
||||
self.item4.show()
|
||||
|
||||
self.ebox.connect_object("button-press-event", self.on_button_press, self.menu)
|
||||
|
||||
self.main_window.show_all()
|
||||
|
@ -132,6 +140,12 @@ class Hud:
|
|||
for w in self.stat_windows:
|
||||
self.stat_windows[w].window.move(self.stat_windows[w].x,
|
||||
self.stat_windows[w].y)
|
||||
|
||||
def debug_stat_windows(self, *args):
|
||||
print self.table, "\n", self.main_window.window.get_transient_for()
|
||||
for w in self.stat_windows:
|
||||
print self.stat_windows[w].window.window.get_transient_for()
|
||||
|
||||
def save_layout(self, *args):
|
||||
new_layout = [(0, 0)] * self.max
|
||||
# todo: have the hud track the poker table's window position regularly, don't forget to update table.x and table.y.
|
||||
|
@ -215,7 +229,14 @@ class Hud:
|
|||
this_stat = config.supported_games[self.poker_game].stats[self.stats[r][c]]
|
||||
number = Stats.do_stat(stat_dict, player = stat_dict[s]['player_id'], stat = self.stats[r][c])
|
||||
statstring = this_stat.hudprefix + str(number[1]) + this_stat.hudsuffix
|
||||
|
||||
if this_stat.hudcolor != "":
|
||||
self.label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(self.colors['hudfgcolor']))
|
||||
self.stat_windows[stat_dict[s]['seat']].label[r][c].modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(this_stat.hudcolor))
|
||||
|
||||
self.stat_windows[stat_dict[s]['seat']].label[r][c].set_text(statstring)
|
||||
if statstring != "xxx":
|
||||
self.stat_windows[stat_dict[s]['seat']].window.show_all()
|
||||
tip = stat_dict[s]['screen_name'] + "\n" + number[5] + "\n" + \
|
||||
number[3] + ", " + number[4]
|
||||
Stats.do_tip(self.stat_windows[stat_dict[s]['seat']].e_box[r][c], tip)
|
||||
|
@ -281,6 +302,7 @@ class Stat_Window:
|
|||
self.double_click(widget, event, *args)
|
||||
|
||||
if event.button == 2: # middle button event
|
||||
self.window.hide()
|
||||
# print "middle button clicked"
|
||||
pass
|
||||
|
||||
|
@ -361,42 +383,14 @@ class Stat_Window:
|
|||
|
||||
self.e_box[r][c].add(self.label[r][c])
|
||||
self.e_box[r][c].connect("button_press_event", self.button_press_cb)
|
||||
# font = pango.FontDescription("Sans 8")
|
||||
font = pango.FontDescription("Sans 7")
|
||||
self.label[r][c].modify_font(font)
|
||||
|
||||
# if not os.name == 'nt': # seems to be a bug in opacity on windows
|
||||
self.window.set_opacity(parent.colors['hudopacity'])
|
||||
|
||||
self.window.realize
|
||||
self.window.move(self.x, self.y)
|
||||
self.window.show_all()
|
||||
# set_keep_above(1) for windows
|
||||
if os.name == 'nt': self.topify_window(self.window)
|
||||
|
||||
def topify_window(self, window):
|
||||
"""Set the specified gtk window to stayontop in MS Windows."""
|
||||
|
||||
def windowEnumerationHandler(hwnd, resultList):
|
||||
'''Callback for win32gui.EnumWindows() to generate list of window handles.'''
|
||||
resultList.append((hwnd, win32gui.GetWindowText(hwnd)))
|
||||
|
||||
unique_name = 'unique name for finding this window'
|
||||
real_name = window.get_title()
|
||||
window.set_title(unique_name)
|
||||
tl_windows = []
|
||||
win32gui.EnumWindows(windowEnumerationHandler, tl_windows)
|
||||
|
||||
for w in tl_windows:
|
||||
if w[1] == unique_name:
|
||||
|
||||
#win32gui.SetWindowPos(w[0], win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE|win32con.SWP_NOSIZE)
|
||||
|
||||
# style = win32gui.GetWindowLong(w[0], win32con.GWL_EXSTYLE)
|
||||
# style |= win32con.WS_EX_TOOLWINDOW
|
||||
# style &= ~win32con.WS_EX_APPWINDOW
|
||||
# win32gui.SetWindowLong(w[0], win32con.GWL_EXSTYLE, style)
|
||||
win32gui.ShowWindow(w[0], win32con.SW_SHOW)
|
||||
window.set_title(real_name)
|
||||
self.window.hide()
|
||||
|
||||
def destroy(*args): # call back for terminating the main eventloop
|
||||
gtk.main_quit()
|
||||
|
@ -431,7 +425,7 @@ class Popup_window:
|
|||
self.lab.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(stat_window.parent.colors['hudbgcolor']))
|
||||
self.lab.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(stat_window.parent.colors['hudfgcolor']))
|
||||
|
||||
self.window.realize
|
||||
# self.window.realize()
|
||||
|
||||
# figure out the row, col address of the click that activated the popup
|
||||
row = 0
|
||||
|
@ -564,7 +558,7 @@ if __name__== "__main__":
|
|||
|
||||
c = Configuration.Config()
|
||||
#tables = Tables.discover(c)
|
||||
t = Tables.discover_table_by_name(c, "Chelsea")
|
||||
t = Tables.discover_table_by_name(c, "Motorway")
|
||||
if t is None:
|
||||
print "Table not found."
|
||||
db = Database.Database(c, 'fpdb', 'holdem')
|
||||
|
|
159
pyfpdb/Mucked.py
159
pyfpdb/Mucked.py
|
@ -41,30 +41,47 @@ import Configuration
|
|||
import Database
|
||||
import Tables
|
||||
import Hud
|
||||
import Mucked
|
||||
import HandHistory
|
||||
|
||||
class Mucked:
|
||||
def __init__(self, parent, db_connection):
|
||||
|
||||
class Aux_Window:
|
||||
def __init__(self, parent, config, db_name):
|
||||
self.config = config
|
||||
self.parent = parent #this is the parent of the mucked cards widget
|
||||
self.db_connection = db_connection
|
||||
self.db_name = db_name
|
||||
|
||||
self.vbox = gtk.VBox()
|
||||
self.parent.add(self.vbox)
|
||||
|
||||
self.mucked_list = MuckedList (self.vbox, db_connection)
|
||||
self.mucked_cards = MuckedCards(self.vbox, db_connection)
|
||||
def update(self):
|
||||
pass
|
||||
|
||||
class Stud_mucked(Aux_Window):
|
||||
def __init__(self, parent, config, db_name):
|
||||
|
||||
self.config = config
|
||||
self.parent = parent #this is the parent of the mucked cards widget
|
||||
self.db_name = db_name
|
||||
|
||||
self.vbox = gtk.VBox()
|
||||
self.parent.add(self.vbox)
|
||||
|
||||
self.mucked_list = Stud_list(self.vbox, config, db_name)
|
||||
self.mucked_cards = Stud_cards(self.vbox, config, db_name)
|
||||
self.mucked_list.mucked_cards = self.mucked_cards
|
||||
self.parent.show_all()
|
||||
|
||||
def update(self, new_hand_id):
|
||||
self.mucked_list.update(new_hand_id)
|
||||
def update_data(self, new_hand_id):
|
||||
self.mucked_list.update_data(new_hand_id)
|
||||
|
||||
class MuckedList:
|
||||
def __init__(self, parent, db_connection):
|
||||
def update_gui(self, new_hand_id):
|
||||
self.mucked_list.update_gui(new_hand_id)
|
||||
|
||||
class Stud_list:
|
||||
def __init__(self, parent, config, db_name):
|
||||
|
||||
self.parent = parent
|
||||
self.db_connection = db_connection
|
||||
self.config = config
|
||||
self.db_name = db_name
|
||||
|
||||
# set up a scrolled window to hold the listbox
|
||||
self.scrolled_window = gtk.ScrolledWindow()
|
||||
|
@ -115,11 +132,25 @@ class MuckedList:
|
|||
vadj.set_value(vadj.upper)
|
||||
self.mucked_cards.update(new_hand_id)
|
||||
|
||||
class MuckedCards:
|
||||
def __init__(self, parent, db_connection):
|
||||
def update_data(self, new_hand_id):
|
||||
self.info_row = ((new_hand_id, "xxxx", 0), )
|
||||
self.mucked_cards.update_data(new_hand_id)
|
||||
|
||||
def update_gui(self, new_hand_id):
|
||||
iter = self.liststore.append(self.info_row[0])
|
||||
sel = self.treeview.get_selection()
|
||||
sel.select_iter(iter)
|
||||
|
||||
vadj = self.scrolled_window.get_vadjustment()
|
||||
vadj.set_value(vadj.upper)
|
||||
self.mucked_cards.update_gui(new_hand_id)
|
||||
|
||||
class Stud_cards:
|
||||
def __init__(self, parent, config, db_name = 'fpdb'):
|
||||
|
||||
self.parent = parent #this is the parent of the mucked cards widget
|
||||
self.db_connection = db_connection
|
||||
self.config = config
|
||||
self.db_name = db_name
|
||||
|
||||
self.card_images = self.get_card_images()
|
||||
self.seen_cards = {}
|
||||
|
@ -159,37 +190,99 @@ class MuckedCards:
|
|||
self.parent.add(self.grid)
|
||||
|
||||
def translate_cards(self, old_cards):
|
||||
pass
|
||||
ranks = ('', '', '2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K', 'A')
|
||||
|
||||
for c in old_cards.keys():
|
||||
for i in range(1, 8):
|
||||
rank = 'card' + str(i) + 'Value'
|
||||
suit = 'card' + str(i) + 'Suit'
|
||||
key = 'hole_card_' + str(i)
|
||||
if old_cards[c][rank] == 0:
|
||||
old_cards[c][key] = 'xx'
|
||||
else:
|
||||
old_cards[c][key] = ranks[old_cards[c][rank]] + old_cards[c][suit]
|
||||
return old_cards
|
||||
|
||||
def update(self, new_hand_id):
|
||||
cards = self.db_connection.get_cards(new_hand_id)
|
||||
db_connection = Database.Database(self.config, 'fpdb', '')
|
||||
cards = db_connection.get_cards(new_hand_id)
|
||||
self.clear()
|
||||
|
||||
cards = self.translate_cards(cards)
|
||||
for c in cards.keys():
|
||||
self.grid_contents[(1, cards[c]['seat_number'] - 1)].set_text(cards[c]['screen_name'])
|
||||
|
||||
for i in ((0, 'hole_card_1'), (1, 'hole_card_2'), (2, 'hole_card_3'), (3, 'hole_card_4'),
|
||||
(4, 'hole_card_5'), (5, 'hole_card_6'), (6, 'hole_card_7')):
|
||||
if not cards[c][i[1]] == "":
|
||||
if not cards[c][i[1]] == "xx":
|
||||
self.seen_cards[(i[0], cards[c]['seat_number'] - 1)]. \
|
||||
set_from_pixbuf(self.card_images[self.split_cards(cards[c][i[1]])])
|
||||
|
||||
xml_text = self.db_connection.get_xml(new_hand_id)
|
||||
hh = HandHistory.HandHistory(xml_text, ('BETTING'))
|
||||
tips = []
|
||||
action = db_connection.get_action_from_hand(new_hand_id)
|
||||
for street in action:
|
||||
temp = ''
|
||||
for act in street:
|
||||
temp = temp + act[0] + " " + act[1] + "s "
|
||||
if act[2] > 0:
|
||||
if act[2]%100 > 0:
|
||||
temp = temp + "%4.2f\n" % (float(act[2])/100)
|
||||
else:
|
||||
temp = temp + "%d\n" % (act[2]/100)
|
||||
else:
|
||||
temp = temp + "\n"
|
||||
tips.append(temp)
|
||||
|
||||
# action in tool tips for 3rd street cards
|
||||
tip = "%s" % hh.BETTING.rounds[0]
|
||||
## action in tool tips for 3rd street cards
|
||||
for c in (0, 1, 2):
|
||||
for r in range(0, self.rows):
|
||||
self.eb[(c, r)].set_tooltip_text(tip)
|
||||
self.eb[(c, r)].set_tooltip_text(tips[0])
|
||||
|
||||
# action in tools tips for later streets
|
||||
round_to_col = (0, 3, 4, 5, 6)
|
||||
for round in range(1, len(hh.BETTING.rounds)):
|
||||
tip = "%s" % hh.BETTING.rounds[round]
|
||||
for round in range(1, len(tips)):
|
||||
for r in range(0, self.rows):
|
||||
self.eb[(round_to_col[round], r)].set_tooltip_text(tip)
|
||||
self.eb[(round_to_col[round], r)].set_tooltip_text(tips[round])
|
||||
db_connection.close_connection()
|
||||
|
||||
def update_data(self, new_hand_id):
|
||||
db_connection = Database.Database(self.config, 'fpdb', '')
|
||||
cards = db_connection.get_cards(new_hand_id)
|
||||
self.clear()
|
||||
self.cards = self.translate_cards(cards)
|
||||
|
||||
self.tips = []
|
||||
action = db_connection.get_action_from_hand(new_hand_id)
|
||||
for street in action:
|
||||
temp = ''
|
||||
for act in street:
|
||||
temp = temp + act[0] + " " + act[1] + "s "
|
||||
if act[2] > 0:
|
||||
if act[2]%100 > 0:
|
||||
temp = temp + "%4.2f\n" % (float(act[2])/100)
|
||||
else:
|
||||
temp = temp + "%d\n" % (act[2]/100)
|
||||
else:
|
||||
temp = temp + "\n"
|
||||
self.tips.append(temp)
|
||||
db_connection.close_connection()
|
||||
|
||||
def update_gui(self, new_hand_id):
|
||||
for c in self.cards.keys():
|
||||
self.grid_contents[(1, self.cards[c]['seat_number'] - 1)].set_text(self.cards[c]['screen_name'])
|
||||
for i in ((0, 'hole_card_1'), (1, 'hole_card_2'), (2, 'hole_card_3'), (3, 'hole_card_4'),
|
||||
(4, 'hole_card_5'), (5, 'hole_card_6'), (6, 'hole_card_7')):
|
||||
if not self.cards[c][i[1]] == "xx":
|
||||
self.seen_cards[(i[0], self.cards[c]['seat_number'] - 1)]. \
|
||||
set_from_pixbuf(self.card_images[self.split_cards(self.cards[c][i[1]])])
|
||||
## action in tool tips for 3rd street cards
|
||||
for c in (0, 1, 2):
|
||||
for r in range(0, self.rows):
|
||||
self.eb[(c, r)].set_tooltip_text(self.tips[0])
|
||||
|
||||
# action in tools tips for later streets
|
||||
round_to_col = (0, 3, 4, 5, 6)
|
||||
for round in range(1, len(self.tips)):
|
||||
for r in range(0, self.rows):
|
||||
self.eb[(round_to_col[round], r)].set_tooltip_text(self.tips[round])
|
||||
|
||||
def split_cards(self, card):
|
||||
return (card[0], card[1].upper())
|
||||
|
@ -225,19 +318,19 @@ if __name__== "__main__":
|
|||
# just read it and pass it to update
|
||||
new_hand_id = sys.stdin.readline()
|
||||
new_hand_id = new_hand_id.rstrip() # remove trailing whitespace
|
||||
m.update(new_hand_id)
|
||||
m.update_data(new_hand_id)
|
||||
m.update_gui(new_hand_id)
|
||||
return(True)
|
||||
|
||||
config = Configuration.Config()
|
||||
db_connection = Database.Database(config, 'fpdb', '')
|
||||
|
||||
# db_connection = Database.Database(config, 'fpdb', '')
|
||||
main_window = gtk.Window()
|
||||
main_window.set_keep_above(True)
|
||||
main_window.connect("destroy", destroy)
|
||||
|
||||
m = Mucked(main_window, db_connection)
|
||||
aux_to_call = "Stud_mucked"
|
||||
m = eval("%s(main_window, config, 'fpdb')" % aux_to_call)
|
||||
main_window.show_all()
|
||||
|
||||
s_id = gobject.io_add_watch(sys.stdin, gobject.IO_IN, process_new_hand)
|
||||
|
||||
gtk.main()
|
||||
|
|
|
@ -24,8 +24,11 @@
|
|||
import os
|
||||
import sys
|
||||
|
||||
import datetime
|
||||
import Configuration
|
||||
import fpdb_db
|
||||
import fpdb_import
|
||||
import fpdb_simple
|
||||
import FpdbSQLQueries
|
||||
|
||||
import unittest
|
||||
|
@ -34,6 +37,7 @@ class TestSequenceFunctions(unittest.TestCase):
|
|||
|
||||
def setUp(self):
|
||||
"""Configure MySQL settings/database and establish connection"""
|
||||
self.c = Configuration.Config()
|
||||
self.mysql_settings={ 'db-host':"localhost",
|
||||
'db-backend':2,
|
||||
'db-databaseName':"fpdbtest",
|
||||
|
@ -44,7 +48,8 @@ class TestSequenceFunctions(unittest.TestCase):
|
|||
self.mysql_settings['db-databaseName'], self.mysql_settings['db-user'],
|
||||
self.mysql_settings['db-password'])
|
||||
self.mysqldict = FpdbSQLQueries.FpdbSQLQueries('MySQL InnoDB')
|
||||
self.mysqlimporter = fpdb_import.Importer(self, self.mysql_settings)
|
||||
self.mysqlimporter = fpdb_import.Importer(self, self.mysql_settings, self.c)
|
||||
self.mysqlimporter.setCallHud(False)
|
||||
|
||||
# """Configure Postgres settings/database and establish connection"""
|
||||
# self.pg_settings={ 'db-host':"localhost", 'db-backend':3, 'db-databaseName':"fpdbtest", 'db-user':"fpdb", 'db-password':"fpdb"}
|
||||
|
@ -69,6 +74,21 @@ class TestSequenceFunctions(unittest.TestCase):
|
|||
self.result = self.mysql_db.cursor.execute("SHOW TABLES")
|
||||
self.failUnless(self.result==13, "Number of tables in database incorrect. Expected 13 got " + str(self.result))
|
||||
|
||||
def testPokerStarsHHDate(self):
|
||||
latest = "PokerStars Game #21969660557: Hold'em No Limit ($0.50/$1.00) - 2008/11/12 10:00:48 CET [2008/11/12 4:00:48 ET]"
|
||||
previous = "PokerStars Game #21969660557: Hold'em No Limit ($0.50/$1.00) - 2008/08/17 - 01:14:43 (ET)"
|
||||
older1 = "PokerStars Game #21969660557: Hold'em No Limit ($0.50/$1.00) - 2008/09/07 06:23:14 ET"
|
||||
|
||||
result = fpdb_simple.parseHandStartTime(older1, "ps")
|
||||
self.failUnless(result==datetime.datetime(2008,9,7,11,23,14),
|
||||
"Date incorrect, expected: 2008-09-07 11:23:14 got: " + str(result))
|
||||
result = fpdb_simple.parseHandStartTime(latest, "ps")
|
||||
self.failUnless(result==datetime.datetime(2008,11,12,15,00,48),
|
||||
"Date incorrect, expected: 2008-11-12 15:00:48 got: " + str(result))
|
||||
result = fpdb_simple.parseHandStartTime(previous, "ps")
|
||||
self.failUnless(result==datetime.datetime(2008,8,17,6,14,43),
|
||||
"Date incorrect, expected: 2008-08-17 01:14:43 got: " + str(result))
|
||||
|
||||
def testImportHandHistoryFiles(self):
|
||||
"""Test import of single HH file"""
|
||||
self.mysqlimporter.addImportFile("regression-test-files/hand-histories/ps-lhe-ring-3hands.txt")
|
||||
|
|
104
pyfpdb/SQL.py
104
pyfpdb/SQL.py
|
@ -237,18 +237,80 @@ class Sql:
|
|||
GROUP BY HudCache.PlayerId
|
||||
"""
|
||||
|
||||
# FROM HudCache, Hands
|
||||
# WHERE HudCache.PlayerId in
|
||||
# (SELECT PlayerId FROM HandsPlayers
|
||||
# WHERE handId = %s)
|
||||
# AND Hands.id = %s
|
||||
# AND Hands.gametypeId = HudCache.gametypeId
|
||||
|
||||
# AND PlayerId LIKE %s
|
||||
# HudCache.gametypeId AS gametypeId,
|
||||
# activeSeats AS n_active,
|
||||
# position AS position,
|
||||
# HudCache.tourneyTypeId AS tourneyTypeId,
|
||||
# same as above except stats are aggregated for all blind/limit levels
|
||||
self.query['get_stats_from_hand_aggregated'] = """
|
||||
SELECT HudCache.playerId AS player_id,
|
||||
sum(HDs) AS n,
|
||||
sum(street0VPI) AS vpip,
|
||||
sum(street0Aggr) AS pfr,
|
||||
sum(street0_3B4BChance) AS TB_opp_0,
|
||||
sum(street0_3B4BDone) AS TB_0,
|
||||
sum(street1Seen) AS saw_f,
|
||||
sum(street1Seen) AS saw_1,
|
||||
sum(street2Seen) AS saw_2,
|
||||
sum(street3Seen) AS saw_3,
|
||||
sum(street4Seen) AS saw_4,
|
||||
sum(sawShowdown) AS sd,
|
||||
sum(street1Aggr) AS aggr_1,
|
||||
sum(street2Aggr) AS aggr_2,
|
||||
sum(street3Aggr) AS aggr_3,
|
||||
sum(street4Aggr) AS aggr_4,
|
||||
sum(otherRaisedStreet1) AS was_raised_1,
|
||||
sum(otherRaisedStreet2) AS was_raised_2,
|
||||
sum(otherRaisedStreet3) AS was_raised_3,
|
||||
sum(otherRaisedStreet4) AS was_raised_4,
|
||||
sum(foldToOtherRaisedStreet1) AS f_freq_1,
|
||||
sum(foldToOtherRaisedStreet2) AS f_freq_2,
|
||||
sum(foldToOtherRaisedStreet3) AS f_freq_3,
|
||||
sum(foldToOtherRaisedStreet4) AS f_freq_4,
|
||||
sum(wonWhenSeenStreet1) AS w_w_s_1,
|
||||
sum(wonAtSD) AS wmsd,
|
||||
sum(stealAttemptChance) AS steal_opp,
|
||||
sum(stealAttempted) AS steal,
|
||||
sum(foldSbToStealChance) AS SBstolen,
|
||||
sum(foldedSbToSteal) AS SBnotDef,
|
||||
sum(foldBbToStealChance) AS BBstolen,
|
||||
sum(foldedBbToSteal) AS BBnotDef,
|
||||
sum(street1CBChance) AS CB_opp_1,
|
||||
sum(street1CBDone) AS CB_1,
|
||||
sum(street2CBChance) AS CB_opp_2,
|
||||
sum(street2CBDone) AS CB_2,
|
||||
sum(street3CBChance) AS CB_opp_3,
|
||||
sum(street3CBDone) AS CB_3,
|
||||
sum(street4CBChance) AS CB_opp_4,
|
||||
sum(street4CBDone) AS CB_4,
|
||||
sum(foldToStreet1CBChance) AS f_cb_opp_1,
|
||||
sum(foldToStreet1CBDone) AS f_cb_1,
|
||||
sum(foldToStreet2CBChance) AS f_cb_opp_2,
|
||||
sum(foldToStreet2CBDone) AS f_cb_2,
|
||||
sum(foldToStreet3CBChance) AS f_cb_opp_3,
|
||||
sum(foldToStreet3CBDone) AS f_cb_3,
|
||||
sum(foldToStreet4CBChance) AS f_cb_opp_4,
|
||||
sum(foldToStreet4CBDone) AS f_cb_4,
|
||||
sum(totalProfit) AS net,
|
||||
sum(street1CheckCallRaiseChance) AS ccr_opp_1,
|
||||
sum(street1CheckCallRaiseDone) AS ccr_1,
|
||||
sum(street2CheckCallRaiseChance) AS ccr_opp_2,
|
||||
sum(street2CheckCallRaiseDone) AS ccr_2,
|
||||
sum(street3CheckCallRaiseChance) AS ccr_opp_3,
|
||||
sum(street3CheckCallRaiseDone) AS ccr_3,
|
||||
sum(street4CheckCallRaiseChance) AS ccr_opp_4,
|
||||
sum(street4CheckCallRaiseDone) AS ccr_4
|
||||
FROM HudCache, Hands
|
||||
WHERE HudCache.PlayerId in
|
||||
(SELECT PlayerId FROM HandsPlayers
|
||||
WHERE handId = %s)
|
||||
AND Hands.id = %s
|
||||
AND HudCache.gametypeId in
|
||||
(SELECT gt1.id from Gametypes gt1, Gametypes gt2, Hands
|
||||
WHERE gt1.siteid = gt2.siteid
|
||||
AND gt1.type = gt2.type
|
||||
AND gt1.category = gt2.category
|
||||
AND gt1.limittype = gt2.limittype
|
||||
AND gt2.id = Hands.gametypeId
|
||||
AND Hands.id = %s)
|
||||
GROUP BY HudCache.PlayerId
|
||||
"""
|
||||
|
||||
self.query['get_players_from_hand'] = """
|
||||
SELECT HandsPlayers.playerId, seatNo, name
|
||||
|
@ -288,15 +350,15 @@ class Sql:
|
|||
order by seatNo
|
||||
"""
|
||||
|
||||
# self.query['get_hand_info'] = """
|
||||
# SELECT
|
||||
# game_id,
|
||||
# CONCAT(hole_card_1, hole_card_2, hole_card_3, hole_card_4, hole_card_5, hole_card_6, hole_card_7) AS hand,
|
||||
# total_won-total_bet AS net
|
||||
# FROM game_players
|
||||
# WHERE game_id = %s AND player_id = 3
|
||||
# """
|
||||
|
||||
self.query['get_action_from_hand'] = """
|
||||
SELECT street, Players.name, HandsActions.action, HandsActions.amount, actionno
|
||||
FROM Players, HandsActions, HandsPlayers
|
||||
WHERE HandsActions.action != 'blind'
|
||||
AND HandsPlayers.handid = %s
|
||||
AND HandsPlayers.playerid = Players.id
|
||||
AND HandsActions.handPlayerId = HandsPlayers.id
|
||||
ORDER BY street, actionno
|
||||
"""
|
||||
if __name__== "__main__":
|
||||
# just print the default queries and exit
|
||||
s = Sql(game = 'razz', type = 'ptracks')
|
||||
|
|
|
@ -89,14 +89,14 @@ def vpip(stat_dict, player):
|
|||
'v=%3.1f' % (100*stat) + '%',
|
||||
'vpip=%3.1f' % (100*stat) + '%',
|
||||
'(%d/%d)' % (stat_dict[player]['vpip'], stat_dict[player]['n']),
|
||||
'vpip'
|
||||
'Voluntarily Put In Pot %'
|
||||
)
|
||||
except: return (stat,
|
||||
'%3.1f' % (0) + '%',
|
||||
'w=%3.1f' % (0) + '%',
|
||||
'wtsd=%3.1f' % (0) + '%',
|
||||
'v=%3.1f' % (0) + '%',
|
||||
'vpip=%3.1f' % (0) + '%',
|
||||
'(%d/%d)' % (0, 0),
|
||||
'wtsd'
|
||||
'Voluntarily Put In Pot %'
|
||||
)
|
||||
|
||||
def vpip_0(stat_dict, player):
|
||||
|
@ -129,7 +129,7 @@ def pfr(stat_dict, player):
|
|||
'p=%3.1f' % (100*stat) + '%',
|
||||
'pfr=%3.1f' % (100*stat) + '%',
|
||||
'(%d/%d)' % (stat_dict[player]['pfr'], stat_dict[player]['n']),
|
||||
'pfr'
|
||||
'Pre-Flop Raise %'
|
||||
)
|
||||
except:
|
||||
return (stat,
|
||||
|
@ -137,7 +137,7 @@ def pfr(stat_dict, player):
|
|||
'p=%3.1f' % (0) + '%',
|
||||
'pfr=%3.1f' % (0) + '%',
|
||||
'(%d/%d)' % (0, 0),
|
||||
'pfr'
|
||||
'Pre-Flop Raise %'
|
||||
)
|
||||
|
||||
def pfr_0(stat_dict, player):
|
||||
|
@ -214,7 +214,7 @@ def saw_f(stat_dict, player):
|
|||
'sf=%3.1f' % (100*stat) + '%',
|
||||
'saw_f=%3.1f' % (100*stat) + '%',
|
||||
'(%d/%d)' % (stat_dict[player]['saw_f'], stat_dict[player]['n']),
|
||||
'saw_f'
|
||||
'Flop Seen %'
|
||||
)
|
||||
except:
|
||||
stat = 0.0
|
||||
|
@ -225,7 +225,7 @@ def saw_f(stat_dict, player):
|
|||
'sf=%3.1f' % (stat) + '%',
|
||||
'saw_f=%3.1f' % (stat) + '%',
|
||||
'(%d/%d)' % (num, den),
|
||||
'saw_f'
|
||||
'Flop Seen %'
|
||||
)
|
||||
|
||||
def n(stat_dict, player):
|
||||
|
@ -303,12 +303,11 @@ def f_SB_steal(stat_dict, player):
|
|||
)
|
||||
except:
|
||||
return (stat,
|
||||
'%3.1f' % (0) + '%',
|
||||
'fSB=%3.1f' % (0) + '%',
|
||||
'fSB_s=%3.1f' % (0) + '%',
|
||||
'(%d/%d)' % (0, 0),
|
||||
'% folded SB to steal'
|
||||
)
|
||||
'NA',
|
||||
'fSB=NA',
|
||||
'fSB_s=NA',
|
||||
'0/0',
|
||||
'% folded SB to steal')
|
||||
|
||||
def f_BB_steal(stat_dict, player):
|
||||
""" Folded BB to steal."""
|
||||
|
@ -324,12 +323,11 @@ def f_BB_steal(stat_dict, player):
|
|||
)
|
||||
except:
|
||||
return (stat,
|
||||
'%3.1f' % (0) + '%',
|
||||
'fBB=%3.1f' % (0) + '%',
|
||||
'fBB_s=%3.1f' % (0) + '%',
|
||||
'(%d/%d)' % (0, 0),
|
||||
'% folded BB to steal'
|
||||
)
|
||||
'NA',
|
||||
'fBB=NA',
|
||||
'fBB_s=NA',
|
||||
'0/0',
|
||||
'% folded BB to steal')
|
||||
|
||||
def three_B_0(stat_dict, player):
|
||||
""" Three bet preflop/3rd."""
|
||||
|
@ -454,7 +452,7 @@ def a_freq_4(stat_dict, player):
|
|||
'a4=%3.1f' % (0) + '%',
|
||||
'a_fq_4=%3.1f' % (0) + '%',
|
||||
'(%d/%d)' % (0, 0),
|
||||
'Aggression Freq flop/4th'
|
||||
'Aggression Freq 7th'
|
||||
)
|
||||
|
||||
def a_freq_123(stat_dict, player):
|
||||
|
|
403
pyfpdb/Tables.py
Normal file → Executable file
403
pyfpdb/Tables.py
Normal file → Executable file
|
@ -7,7 +7,6 @@ of Table_Window objects representing the windows found.
|
|||
"""
|
||||
# Copyright 2008, Ray E. Barker
|
||||
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
|
@ -40,7 +39,35 @@ if os.name == 'nt':
|
|||
# FreePokerTools modules
|
||||
import Configuration
|
||||
|
||||
# Each TableWindow object must have the following attributes correctly populated:
|
||||
# tw.name = the table name from the title bar, which must to match the table name
|
||||
# from the corresponding hand history.
|
||||
# tw.site = the site name, e.g. PokerStars, FullTilt. This must match the site
|
||||
# name specified in the config file.
|
||||
# tw.number = This is the system id number for the client table window in the
|
||||
# format that the system presents it. This is Xid in Xwindows and
|
||||
# hwnd in Microsoft Windows.
|
||||
# tw.title = The full title from the window title bar.
|
||||
# tw.width, tw.height = The width and height of the window in pixels. This is
|
||||
# the internal width and height, not including the title bar and
|
||||
# window borders.
|
||||
# tw.x, tw.y = The x, y (horizontal, vertical) location of the window relative
|
||||
# to the top left of the display screen. This also does not include the
|
||||
# title bar and window borders. To put it another way, this is the
|
||||
# screen location of (0, 0) in the working window.
|
||||
|
||||
class Table_Window:
|
||||
def __init__(self, info = {}):
|
||||
if info.has_key('number'): self.number = info['number']
|
||||
if info.has_key('exe'): self.exe = info['exe']
|
||||
if info.has_key('width'): self.width = info['width']
|
||||
if info.has_key('height'): self.height = info['height']
|
||||
if info.has_key('x'): self.x = info['x']
|
||||
if info.has_key('y'): self.y = info['y']
|
||||
if info.has_key('site'): self.site = info['site']
|
||||
if info.has_key('title'): self.title = info['title']
|
||||
if info.has_key('name'): self.name = info['name']
|
||||
|
||||
def __str__(self):
|
||||
# __str__ method for testing
|
||||
temp = 'TableWindow object\n'
|
||||
|
@ -51,13 +78,10 @@ class Table_Window:
|
|||
temp = temp + " tournament = %d\n table = %d" % (self.tournament, self.table)
|
||||
return temp
|
||||
|
||||
def get_details(table):
|
||||
table.game = 'razz'
|
||||
table.max = 8
|
||||
table.struture = 'limit'
|
||||
table.tournament = 0
|
||||
|
||||
############################################################################
|
||||
# Top-level discovery routines--these are the modules interface
|
||||
def discover(c):
|
||||
"""Dispatch routine for finding all potential poker client windows."""
|
||||
if os.name == 'posix':
|
||||
tables = discover_posix(c)
|
||||
elif os.name == 'nt':
|
||||
|
@ -66,86 +90,99 @@ def discover(c):
|
|||
tables = discover_mac(c)
|
||||
else:
|
||||
tables = {}
|
||||
|
||||
return(tables)
|
||||
return tables
|
||||
|
||||
def discover_table_by_name(c, tablename):
|
||||
"""Dispatch routine for finding poker client windows with the given name."""
|
||||
if os.name == 'posix':
|
||||
table = discover_posix_by_name(c, tablename)
|
||||
info = discover_posix_by_name(c, tablename)
|
||||
elif os.name == 'nt':
|
||||
table = discover_nt_by_name(c, tablename)
|
||||
info = discover_nt_by_name(c, tablename)
|
||||
elif os.name == 'mac':
|
||||
table = discover_mac_by_name(c, tablename)
|
||||
info = discover_mac_by_name(c, tablename)
|
||||
else:
|
||||
table = None
|
||||
return(table)
|
||||
return None
|
||||
if info == None:
|
||||
return None
|
||||
return Table_Window(info)
|
||||
|
||||
def discover_tournament_table(c, tour_number, tab_number):
|
||||
"""Dispatch routine for finding poker clients with tour and table number."""
|
||||
if os.name == 'posix':
|
||||
info = discover_posix_tournament(c, tour_number, tab_number)
|
||||
elif os.name == 'nt':
|
||||
info = discover_nt_tournament(c, tour_number, tab_number)
|
||||
elif os.name == 'mac':
|
||||
info = discover_mac_tournament(c, tour_number, tab_number)
|
||||
else:
|
||||
return None
|
||||
if info:
|
||||
return Table_Window(info)
|
||||
return None
|
||||
|
||||
#############################################################################
|
||||
# Posix (= XWindows) specific routines
|
||||
def discover_posix(c):
|
||||
""" Poker client table window finder for posix/Linux = XWindows."""
|
||||
"""Poker client table window finder for posix/Linux = XWindows."""
|
||||
tables = {}
|
||||
for listing in os.popen('xwininfo -root -tree').readlines():
|
||||
# xwininfo -root -tree -id 0xnnnnn gets the info on a single window
|
||||
for s in c.get_supported_sites():
|
||||
params = c.get_site_parameters(s)
|
||||
if re.search(params['table_finder'], listing):
|
||||
if re.search('Lobby', listing): continue
|
||||
if re.search('Instant Hand History', listing): continue
|
||||
if not re.search('Logged In as ', listing, re.IGNORECASE): continue
|
||||
if re.search('\"Full Tilt Poker\"', listing): continue # FTP Lobby
|
||||
for s in c.supported_sites.keys():
|
||||
if re.search(c.supported_sites[s].table_finder, listing):
|
||||
mo = re.match('\s+([\dxabcdef]+) (.+):.+ (\d+)x(\d+)\+\d+\+\d+ \+(\d+)\+(\d+)', listing)
|
||||
if mo.group(2) == '(has no name)': continue
|
||||
if re.match('[\(\)\d\s]+', mo.group(2)): continue # this is a popup
|
||||
tw = Table_Window()
|
||||
tw.site = c.supported_sites[s].site_name
|
||||
tw.number = int(mo.group(1), 0)
|
||||
tw.title = mo.group(2)
|
||||
tw.width = int( mo.group(3) )
|
||||
tw.height = int( mo.group(4) )
|
||||
tw.x = int (mo.group(5) )
|
||||
tw.y = int (mo.group(6) )
|
||||
tw.title = re.sub('\"', '', tw.title)
|
||||
|
||||
# use this eval thingie to call the title bar decoder specified in the config file
|
||||
eval("%s(tw)" % c.supported_sites[s].decoder)
|
||||
|
||||
if re.search('History for table:', listing): continue
|
||||
if re.search('has no name', listing): continue
|
||||
info = decode_xwininfo(c, listing)
|
||||
if info['site'] == None: continue
|
||||
if info['title'] == info['exe']: continue
|
||||
# this appears to be a poker client, so make a table object for it
|
||||
tw = Table_Window(info)
|
||||
eval("%s(tw)" % params['decoder'])
|
||||
tables[tw.name] = tw
|
||||
return tables
|
||||
|
||||
def discover_posix_by_name(c, tablename):
|
||||
tables = discover_posix(c)
|
||||
for t in tables:
|
||||
if tables[t].name.find(tablename) > -1:
|
||||
return tables[t]
|
||||
"""Find an XWindows poker client of the given name."""
|
||||
for listing in os.popen('xwininfo -root -tree').readlines():
|
||||
if re.search(tablename, listing):
|
||||
if re.search('History for table:', listing): continue
|
||||
info = decode_xwininfo(c, listing)
|
||||
if not info['name'] == tablename: continue
|
||||
return info
|
||||
return False
|
||||
|
||||
def discover_posix_tournament(c, t_number, s_number):
|
||||
"""Finds the X window for a client, given tournament and table nos."""
|
||||
search_string = "%s.+Table\s%s" % (t_number, s_number)
|
||||
for listing in os.popen('xwininfo -root -tree').readlines():
|
||||
if re.search(search_string, listing):
|
||||
return decode_xwininfo(c, listing)
|
||||
return False
|
||||
|
||||
def decode_xwininfo(c, info_string):
|
||||
"""Gets window parameters from xwinifo string--XWindows."""
|
||||
info = {}
|
||||
mo = re.match('\s+([\dxabcdef]+) (.+):\s\(\"([a-zA-Z.]+)\".+ (\d+)x(\d+)\+\d+\+\d+ \+(\d+)\+(\d+)', info_string)
|
||||
if not mo:
|
||||
return None
|
||||
else:
|
||||
info['number'] = int( mo.group(1), 0)
|
||||
info['exe'] = mo.group(3)
|
||||
info['width'] = int( mo.group(4) )
|
||||
info['height'] = int( mo.group(5) )
|
||||
info['x'] = int( mo.group(6) )
|
||||
info['y'] = int( mo.group(7) )
|
||||
info['site'] = get_site_from_exe(c, info['exe'])
|
||||
info['title'] = re.sub('\"', '', mo.group(2))
|
||||
title_bits = re.split(' - ', info['title'])
|
||||
info['name'] = clean_title(title_bits[0])
|
||||
return info
|
||||
|
||||
#
|
||||
# The discover_xx functions query the system and report on the poker clients
|
||||
# currently displayed on the screen. The discover_posix should give you
|
||||
# some idea how to support other systems.
|
||||
#
|
||||
# discover_xx() returns a dict of TableWindow objects--one TableWindow
|
||||
# object for each poker client table on the screen.
|
||||
#
|
||||
# Each TableWindow object must have the following attributes correctly populated:
|
||||
# tw.site = the site name, e.g. PokerStars, FullTilt. This must match the site
|
||||
# name specified in the config file.
|
||||
# tw.number = This is the system id number for the client table window in the
|
||||
# format that the system presents it.
|
||||
# tw.title = The full title from the window title bar.
|
||||
# tw.width, tw.height = The width and height of the window in pixels. This is
|
||||
# the internal width and height, not including the title bar and
|
||||
# window borders.
|
||||
# tw.x, tw.y = The x, y (horizontal, vertical) location of the window relative
|
||||
# to the top left of the display screen. This also does not include the
|
||||
# title bar and window borders. To put it another way, this is the
|
||||
# screen location of (0, 0) in the working window.
|
||||
|
||||
def win_enum_handler(hwnd, titles):
|
||||
titles[hwnd] = win32gui.GetWindowText(hwnd)
|
||||
|
||||
|
||||
def child_enum_handler(hwnd, children):
|
||||
print hwnd, win32.GetWindowRect(hwnd)
|
||||
|
||||
##############################################################################
|
||||
# NT (= Windows) specific routines
|
||||
def discover_nt(c):
|
||||
""" Poker client table window finder for Windows."""
|
||||
#
|
||||
|
@ -185,72 +222,88 @@ def discover_nt(c):
|
|||
return tables
|
||||
|
||||
def discover_nt_by_name(c, tablename):
|
||||
# this is pretty much identical to the 'search all windows for all poker sites' code, but made to dig just for a specific table name
|
||||
# it could be implemented a bunch better - and we need to not assume the width/height thing that (steffen?) assumed above, we should
|
||||
# be able to dig up the window's titlebar handle and get it's information, and such .. but.. for now, i guess this will work.
|
||||
# - eric
|
||||
b_width = 3
|
||||
tb_height = 29
|
||||
"""Finds poker client window with the given table name."""
|
||||
titles = {}
|
||||
# tables = discover_nt(c)
|
||||
win32gui.EnumWindows(win_enum_handler, titles)
|
||||
for s in c.supported_sites.keys():
|
||||
for hwnd in titles.keys():
|
||||
if titles[hwnd].find(tablename) == -1: continue
|
||||
if titles[hwnd].find("History for table:") > -1: continue
|
||||
if titles[hwnd].find("HUD:") > -1: continue
|
||||
if titles[hwnd].find("Chat:") > -1: continue
|
||||
return decode_windows(c, titles[hwnd], hwnd)
|
||||
return False
|
||||
|
||||
def discover_nt_tournament(c, tour_number, tab_number):
|
||||
"""Finds the Windows window handle for the given tournament/table."""
|
||||
search_string = "%s.+%s" % (tour_number, tab_number)
|
||||
|
||||
titles ={}
|
||||
win32gui.EnumWindows(win_enum_handler, titles)
|
||||
for hwnd in titles.keys():
|
||||
if re.search(search_string, titles[hwnd]):
|
||||
return decode_windows(c, titles[hwnd], hwnd)
|
||||
return False
|
||||
|
||||
def get_nt_exe(hwnd):
|
||||
"""Finds the name of the executable that the given window handle belongs to."""
|
||||
processid = win32process.GetWindowThreadProcessId(hwnd)
|
||||
pshandle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION | win32con.PROCESS_VM_READ, False, processid[1])
|
||||
exe = win32process.GetModuleFileNameEx(pshandle, 0)
|
||||
if exe.find(c.supported_sites[s].table_finder) == -1:
|
||||
continue
|
||||
if titles[hwnd].find(tablename) > -1:
|
||||
if titles[hwnd].find("History for table:") > -1 or titles[hwnd].find("FPDBHUD") > -1:
|
||||
continue
|
||||
tw = Table_Window()
|
||||
tw.number = hwnd
|
||||
(x, y, width, height) = win32gui.GetWindowRect(hwnd)
|
||||
tw.title = titles[hwnd]
|
||||
tw.width = int(width) - 2 * b_width
|
||||
tw.height = int(height) - b_width - tb_height
|
||||
tw.x = int(x) + b_width
|
||||
tw.y = int(y) + tb_height
|
||||
tw.site = c.supported_sites[s].site_name
|
||||
if not tw.site == "Unknown" and not c.supported_sites[tw.site].decoder == "Unknown":
|
||||
eval("%s(tw)" % c.supported_sites[tw.site].decoder)
|
||||
else:
|
||||
tw.name = tablename
|
||||
return tw
|
||||
return win32process.GetModuleFileNameEx(pshandle, 0)
|
||||
|
||||
# if we don't find anything by process name, let's search one more time, and call it Unknown ?
|
||||
for hwnd in titles.keys():
|
||||
if titles[hwnd].find(tablename) > -1:
|
||||
if titles[hwnd].find("History for table:") > -1 or titles[hwnd].find("FPDBHUD") > -1:
|
||||
continue
|
||||
tw = Table_Window()
|
||||
tw.number = hwnd
|
||||
(x, y, width, height) = win32gui.GetWindowRect(hwnd)
|
||||
tw.title = titles[hwnd]
|
||||
tw.width = int(width) - 2 * b_width
|
||||
tw.height = int(height) - b_width - tb_height
|
||||
tw.x = int(x) + b_width
|
||||
tw.y = int(y) + tb_height
|
||||
tw.site = "Unknown"
|
||||
tw.name = tablename
|
||||
return tw
|
||||
def decode_windows(c, title, hwnd):
|
||||
"""Gets window parameters from the window title and handle--Windows."""
|
||||
|
||||
# I cannot figure out how to get the inside dimensions of the poker table
|
||||
# windows. So I just assume all borders are 3 thick and all title bars
|
||||
# are 29 high. No doubt this will be off when used with certain themes.
|
||||
b_width = 3
|
||||
tb_height = 29
|
||||
|
||||
info = {}
|
||||
info['number'] = hwnd
|
||||
info['title'] = re.sub('\"', '', title)
|
||||
(x, y, width, height) = win32gui.GetWindowRect(hwnd)
|
||||
|
||||
info['x'] = int(x) + b_width
|
||||
info['y'] = int( y ) + tb_height
|
||||
info['width'] = int( width ) - 2*b_width
|
||||
info['height'] = int( height ) - b_width - tb_height
|
||||
info['exe'] = get_nt_exe(hwnd)
|
||||
|
||||
title_bits = re.split(' - ', info['title'])
|
||||
info['name'] = title_bits[0]
|
||||
info['site'] = get_site_from_exe(c, info['exe'])
|
||||
|
||||
return info
|
||||
|
||||
def win_enum_handler(hwnd, titles):
|
||||
titles[hwnd] = win32gui.GetWindowText(hwnd)
|
||||
|
||||
###################################################################
|
||||
# Utility routines used by all the discoverers.
|
||||
def get_site_from_exe(c, exe):
|
||||
"""Look up the site from config, given the exe."""
|
||||
for s in c.get_supported_sites():
|
||||
params = c.get_site_parameters(s)
|
||||
if re.search(params['table_finder'], exe):
|
||||
return params['site_name']
|
||||
return None
|
||||
|
||||
def discover_mac(c):
|
||||
""" Poker client table window finder for Macintosh."""
|
||||
tables = {}
|
||||
return tables
|
||||
|
||||
def discover_mac_by_name(c, tablename):
|
||||
# again, i have no mac to test this on, sorry -eric
|
||||
return discover_mac(c)
|
||||
|
||||
def clean_title(name):
|
||||
"""Clean the little info strings from the table name."""
|
||||
# these strings could go in a config file
|
||||
for pattern in [' \(6 max\)', ' \(heads up\)', ' \(deep\)',
|
||||
' \(deep hu\)', ' \(deep 6\)', ' \(2\)',
|
||||
' \(edu\)', ' \(edu, 6 max\)', ' \(6\)',
|
||||
' no all-in', ' fast', ',', ' 50BB min', '\s+$']:
|
||||
name = re.sub(pattern, '', name)
|
||||
name = name.rstrip()
|
||||
return name
|
||||
|
||||
def pokerstars_decode_table(tw):
|
||||
# extract the table name OR the tournament number and table name from the title
|
||||
# other info in title is redundant with data in the database
|
||||
# Extract poker information from the window title. This is not needed for
|
||||
# fpdb, since all that information is available in the db via new_hand_number.
|
||||
# This is needed only when using the HUD with a backend less integrated.
|
||||
title_bits = re.split(' - ', tw.title)
|
||||
name = title_bits[0]
|
||||
mo = re.search('Tournament (\d+) Table (\d+)', name)
|
||||
|
@ -260,12 +313,8 @@ def pokerstars_decode_table(tw):
|
|||
tw.name = name
|
||||
else:
|
||||
tw.tournament = None
|
||||
for pattern in [' no all-in', ' fast', ',', ' 50BB min']:
|
||||
name = re.sub(pattern, '', name)
|
||||
name = re.sub('\s+$', '', name)
|
||||
tw.name = name
|
||||
|
||||
mo = re.search('(Razz|Stud H/L|Stud|Omaha H/L|Omaha|Hold\'em|5-Card Draw|Triple Draw 2-7 Lowball)', tw.title)
|
||||
tw.name = clean_title(name)
|
||||
mo = re.search('(Razz|Stud H/L|Stud|Omaha H/L|Omaha|Hold\'em|5-Card Draw|Triple Draw 2-7 Lowball|Badugi)', tw.title)
|
||||
|
||||
tw.game = mo.group(1).lower()
|
||||
tw.game = re.sub('\'', '', tw.game)
|
||||
|
@ -288,25 +337,103 @@ def pokerstars_decode_table(tw):
|
|||
pass
|
||||
|
||||
def fulltilt_decode_table(tw):
|
||||
# extract the table name OR the tournament number and table name from the title
|
||||
# other info in title is redundant with data in the database
|
||||
# Extract poker information from the window title. This is not needed for
|
||||
# fpdb, since all that information is available in the db via new_hand_number.
|
||||
# This is needed only when using the HUD with a backend less integrated.
|
||||
title_bits = re.split(' - ', tw.title)
|
||||
name = title_bits[0]
|
||||
tw.tournament = None
|
||||
for pattern in [' (6 max)', ' (heads up)', ' (deep)',
|
||||
' (deep hu)', ' (deep 6)', ' (2)',
|
||||
' (edu)', ' (edu, 6 max)', ' (6)' ]:
|
||||
tw.name = clean_title(name)
|
||||
|
||||
def clean_title(name):
|
||||
"""Clean the little info strings from the table name."""
|
||||
# these strings could go in a config file
|
||||
for pattern in [' \(6 max\)', ' \(heads up\)', ' \(deep\)',
|
||||
' \(deep hu\)', ' \(deep 6\)', ' \(2\)',
|
||||
' \(edu\)', ' \(edu, 6 max\)', ' \(6\)',
|
||||
' no all-in', ' fast', ',', ' 50BB min', '\s+$']:
|
||||
name = re.sub(pattern, '', name)
|
||||
# (tw.name, trash) = name.split(r' (', 1)
|
||||
tw.name = name.rstrip()
|
||||
name = name.rstrip()
|
||||
return name
|
||||
|
||||
###########################################################################
|
||||
# Mac specific routines....all stubs for now
|
||||
def discover_mac_tournament(c, tour_number, tab_number):
|
||||
"""Mac users need help."""
|
||||
return None
|
||||
|
||||
def discover_mac(c):
|
||||
"""Poker client table window finder for Macintosh."""
|
||||
tables = {}
|
||||
return tables
|
||||
|
||||
def discover_mac_by_name(c, tablename):
|
||||
"""Oh, the humanity."""
|
||||
# again, i have no mac to test this on, sorry -eric
|
||||
return None
|
||||
|
||||
#def discover_nt_by_name(c, tablename):
|
||||
# # this is pretty much identical to the 'search all windows for all poker sites' code, but made to dig just for a specific table name
|
||||
# # it could be implemented a bunch better - and we need to not assume the width/height thing that (steffen?) assumed above, we should
|
||||
# # be able to dig up the window's titlebar handle and get it's information, and such .. but.. for now, i guess this will work.
|
||||
# # - eric
|
||||
# b_width = 3
|
||||
# tb_height = 29
|
||||
# titles = {}
|
||||
## tables = discover_nt(c)
|
||||
# win32gui.EnumWindows(win_enum_handler, titles)
|
||||
# for s in c.supported_sites.keys():
|
||||
# for hwnd in titles.keys():
|
||||
# processid = win32process.GetWindowThreadProcessId(hwnd)
|
||||
# pshandle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION | win32con.PROCESS_VM_READ, False, processid[1])
|
||||
# exe = win32process.GetModuleFileNameEx(pshandle, 0)
|
||||
# if exe.find(c.supported_sites[s].table_finder) == -1:
|
||||
# continue
|
||||
# if titles[hwnd].find(tablename) > -1:
|
||||
# if titles[hwnd].find("History for table:") > -1 or titles[hwnd].find("FPDBHUD") > -1:
|
||||
# continue
|
||||
# tw = Table_Window()
|
||||
# tw.number = hwnd
|
||||
# (x, y, width, height) = win32gui.GetWindowRect(hwnd)
|
||||
# tw.title = titles[hwnd]
|
||||
# tw.width = int(width) - 2 * b_width
|
||||
# tw.height = int(height) - b_width - tb_height
|
||||
# tw.x = int(x) + b_width
|
||||
# tw.y = int(y) + tb_height
|
||||
# tw.site = c.supported_sites[s].site_name
|
||||
# if not tw.site == "Unknown" and not c.supported_sites[tw.site].decoder == "Unknown":
|
||||
# eval("%s(tw)" % c.supported_sites[tw.site].decoder)
|
||||
# else:
|
||||
# tw.name = tablename
|
||||
# return tw
|
||||
#
|
||||
# # if we don't find anything by process name, let's search one more time, and call it Unknown ?
|
||||
# for hwnd in titles.keys():
|
||||
# if titles[hwnd].find(tablename) > -1:
|
||||
# if titles[hwnd].find("History for table:") > -1 or titles[hwnd].find("FPDBHUD") > -1:
|
||||
# continue
|
||||
# tw = Table_Window()
|
||||
# tw.number = hwnd
|
||||
# (x, y, width, height) = win32gui.GetWindowRect(hwnd)
|
||||
# tw.title = titles[hwnd]
|
||||
# tw.width = int(width) - 2 * b_width
|
||||
# tw.height = int(height) - b_width - tb_height
|
||||
# tw.x = int(x) + b_width
|
||||
# tw.y = int(y) + tb_height
|
||||
# tw.site = "Unknown"
|
||||
# tw.name = tablename
|
||||
# return tw
|
||||
#
|
||||
# return None
|
||||
|
||||
if __name__=="__main__":
|
||||
c = Configuration.Config()
|
||||
print discover_table_by_name(c, "Catacaos")
|
||||
tables = discover(c)
|
||||
|
||||
print discover_table_by_name(c, "Howard Lederer")
|
||||
print discover_tournament_table(c, "118942908", "3")
|
||||
|
||||
tables = discover(c)
|
||||
for t in tables.keys():
|
||||
print "t = ", t
|
||||
print tables[t]
|
||||
|
||||
print "press enter to continue"
|
||||
|
|
|
@ -37,6 +37,7 @@ import gtk
|
|||
import fpdb_db
|
||||
import fpdb_simple
|
||||
import GuiBulkImport
|
||||
import GuiPlayerStats
|
||||
import GuiTableViewer
|
||||
import GuiAutoImport
|
||||
import GuiGraphViewer
|
||||
|
@ -117,12 +118,12 @@ class fpdb:
|
|||
|
||||
def dia_create_del_database(self, widget, data):
|
||||
print "todo: implement dia_create_del_database"
|
||||
obtain_global_lock()
|
||||
self.obtain_global_lock()
|
||||
#end def dia_create_del_database
|
||||
|
||||
def dia_create_del_user(self, widget, data):
|
||||
print "todo: implement dia_create_del_user"
|
||||
obtain_global_lock()
|
||||
self.obtain_global_lock()
|
||||
#end def dia_create_del_user
|
||||
|
||||
def dia_database_stats(self, widget, data):
|
||||
|
@ -132,17 +133,17 @@ class fpdb:
|
|||
|
||||
def dia_delete_db_parts(self, widget, data):
|
||||
print "todo: implement dia_delete_db_parts"
|
||||
obtain_global_lock()
|
||||
self.obtain_global_lock()
|
||||
#end def dia_delete_db_parts
|
||||
|
||||
def dia_edit_profile(self, widget=None, data=None, create_default=False, path=None):
|
||||
print "todo: implement dia_edit_profile"
|
||||
obtain_global_lock()
|
||||
self.obtain_global_lock()
|
||||
#end def dia_edit_profile
|
||||
|
||||
def dia_export_db(self, widget, data):
|
||||
print "todo: implement dia_export_db"
|
||||
obtain_global_lock()
|
||||
self.obtain_global_lock()
|
||||
#end def dia_export_db
|
||||
|
||||
def dia_get_db_root_credentials(self):
|
||||
|
@ -167,7 +168,7 @@ class fpdb:
|
|||
|
||||
def dia_import_db(self, widget, data):
|
||||
print "todo: implement dia_import_db"
|
||||
obtain_global_lock()
|
||||
self.obtain_global_lock()
|
||||
#end def dia_import_db
|
||||
|
||||
def dia_licensing(self, widget, data):
|
||||
|
@ -263,7 +264,11 @@ class fpdb:
|
|||
|
||||
self.db = fpdb_db.fpdb_db()
|
||||
#print "end of fpdb.load_profile, databaseName:",self.settings['db-databaseName']
|
||||
self.db.connect(self.settings['db-backend'], self.settings['db-host'], self.settings['db-databaseName'], self.settings['db-user'], self.settings['db-password'])
|
||||
self.db.connect(self.settings['db-backend'],
|
||||
self.settings['db-host'],
|
||||
self.settings['db-databaseName'],
|
||||
self.settings['db-user'],
|
||||
self.settings['db-password'])
|
||||
if self.db.wrongDbVersion:
|
||||
diaDbVersionWarning = gtk.Dialog(title="Strong Warning - Invalid database version", parent=None, flags=0, buttons=(gtk.STOCK_OK,gtk.RESPONSE_OK))
|
||||
|
||||
|
@ -326,6 +331,13 @@ class fpdb:
|
|||
self.add_and_display_tab(bulk_tab, "Bulk Import")
|
||||
#end def tab_bulk_import
|
||||
|
||||
def tab_player_stats(self, widget, data):
|
||||
new_ps_thread=GuiPlayerStats.GuiPlayerStats(self.db, self.config, self.querydict)
|
||||
self.threads.append(new_ps_thread)
|
||||
ps_tab=new_ps_thread.get_vbox()
|
||||
self.add_and_display_tab(ps_tab, "Player Stats")
|
||||
|
||||
|
||||
def tab_main_help(self, widget, data):
|
||||
"""Displays a tab with the main fpdb help screen"""
|
||||
#print "start of tab_main_help"
|
||||
|
@ -384,7 +396,7 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt")
|
|||
("/Viewers/_Graphs", "<control>G", self.tabGraphViewer, 0, None ),
|
||||
("/Viewers/Hand _Replayer (todo)", None, self.not_implemented, 0, None ),
|
||||
("/Viewers/Player _Details (todo)", None, self.not_implemented, 0, None ),
|
||||
("/Viewers/_Player Stats (tabulated view) (todo)", None, self.not_implemented, 0, None ),
|
||||
("/Viewers/_Player Stats (tabulated view)", None, self.tab_player_stats, 0, None ),
|
||||
("/Viewers/Starting _Hands (todo)", None, self.not_implemented, 0, None ),
|
||||
("/Viewers/_Session Replayer (todo)", None, self.not_implemented, 0, None ),
|
||||
("/Viewers/Poker_table Viewer (mostly obselete)", "<control>T", self.tab_table_viewer, 0, None ),
|
||||
|
|
|
@ -31,24 +31,37 @@ class fpdb_db:
|
|||
self.SQLITE=4
|
||||
#end def __init__
|
||||
|
||||
def connect(self, backend, host, database, user, password):
|
||||
def connect(self, backend=None, host=None, database=None,
|
||||
user=None, password=None):
|
||||
"""Connects a database with the given parameters"""
|
||||
if backend is None:
|
||||
raise FpdbError('Database backend not defined')
|
||||
self.backend=backend
|
||||
self.host=host
|
||||
self.database=database
|
||||
self.user=user
|
||||
self.password=password
|
||||
self.database=database
|
||||
if backend==self.MYSQL_INNODB:
|
||||
import MySQLdb
|
||||
self.db=MySQLdb.connect(host = host, user = user, passwd = password, db = database)
|
||||
elif backend==self.PGSQL:
|
||||
import psycopg2
|
||||
self.db = psycopg2.connect(host = host, user = user, password = password, database = database)
|
||||
# If DB connection is made over TCP, then the variables
|
||||
# host, user and password are required
|
||||
if self.host or self.user:
|
||||
self.db = psycopg2.connect(host = host,
|
||||
user = user,
|
||||
password = password,
|
||||
database = database)
|
||||
# For local domain-socket connections, only DB name is
|
||||
# needed, and everything else is in fact undefined and/or
|
||||
# flat out wrong
|
||||
else:
|
||||
self.db = psycopg2.connect(database = database)
|
||||
else:
|
||||
raise fpdb_simple.FpdbError("unrecognised database backend:"+backend)
|
||||
self.cursor=self.db.cursor()
|
||||
self.cursor.execute('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED')
|
||||
|
||||
# Set up query dictionary as early in the connection process as we can.
|
||||
self.sql = FpdbSQLQueries.FpdbSQLQueries(self.get_backend_name())
|
||||
self.wrongDbVersion=False
|
||||
|
|
|
@ -31,6 +31,7 @@ try:
|
|||
except:
|
||||
pass
|
||||
|
||||
import traceback
|
||||
import math
|
||||
import os
|
||||
import datetime
|
||||
|
@ -61,6 +62,8 @@ class Importer:
|
|||
self.settings['minPrint'] = 30
|
||||
self.dbConnect()
|
||||
|
||||
# XXX: Why is this here, when fpdb_db.connect() already does the
|
||||
# same?
|
||||
def dbConnect(self):
|
||||
#connect to DB
|
||||
if self.settings['db-backend'] == 2:
|
||||
|
@ -72,10 +75,15 @@ class Importer:
|
|||
if not pgsqlLibFound:
|
||||
raise fpdb_simple.FpdbError("interface library psycopg2 not found but PostgreSQL selected as backend - please install the library or change the config file")
|
||||
print self.settings
|
||||
if not self.settings.has_key('db-host') or \
|
||||
not self.settings.has_key('db-user'):
|
||||
self.db = psycopg2.connect(host = self.settings['db-host'],
|
||||
user = self.settings['db-user'],
|
||||
password = self.settings['db-password'],
|
||||
database = self.settings['db-databaseName'])
|
||||
else:
|
||||
dbname = self.settings['db-databaseName']
|
||||
self.db = psycopg2.connect(database = dbname)
|
||||
elif self.settings['db-backend'] == 4:
|
||||
pass
|
||||
else:
|
||||
|
@ -113,8 +121,7 @@ class Importer:
|
|||
#Only one import directory per site supported.
|
||||
#dirlist is a hash of lists:
|
||||
#dirlist{ 'PokerStars' => ["/path/to/import/", "filtername"] }
|
||||
def addImportDirectory(self, dir, monitor = False, site = "default", filter = "passthrough"):
|
||||
if dir != "/dev/null" and dir.lower() != "none":
|
||||
def addImportDirectory(self,dir,monitor = False, site = "default", filter = "passthrough"):
|
||||
if os.path.isdir(dir):
|
||||
if monitor == True:
|
||||
self.monitor = True
|
||||
|
@ -123,7 +130,7 @@ class Importer:
|
|||
for file in os.listdir(dir):
|
||||
self.addImportFile(os.path.join(dir, file), site, filter)
|
||||
else:
|
||||
print "Warning: Attempted to add: '" + str(dir) + "' as an import directory\n"
|
||||
print "Warning: Attempted to add: '" + str(dir) + "' as an import directory"
|
||||
|
||||
#Run full import on filelist
|
||||
def runImport(self):
|
||||
|
@ -249,14 +256,14 @@ class Importer:
|
|||
duplicates+=1
|
||||
except (ValueError), fe:
|
||||
errors+=1
|
||||
self.printEmailErrorMessage(errors, file, hand[0])
|
||||
self.printEmailErrorMessage(errors, file, hand)
|
||||
|
||||
if (self.settings['failOnError']):
|
||||
self.db.commit() #dont remove this, in case hand processing was cancelled.
|
||||
raise
|
||||
except (fpdb_simple.FpdbError), fe:
|
||||
errors+=1
|
||||
self.printEmailErrorMessage(errors, file, hand[0])
|
||||
self.printEmailErrorMessage(errors, file, hand)
|
||||
|
||||
#fe.printStackTrace() #todo: get stacktrace
|
||||
self.db.rollback()
|
||||
|
@ -272,11 +279,10 @@ class Importer:
|
|||
if ((stored+duplicates+partial+errors)>=self.settings['handCount']):
|
||||
if (not self.settings['quiet']):
|
||||
print "quitting due to reaching the amount of hands to be imported"
|
||||
print "Total stored:", stored, "duplicates:", duplicates, "partial/damaged:",
|
||||
partial, "errors:", errors, " time: %5.3f" % (time() - starttime)
|
||||
print "Total stored:", stored, "duplicates:", duplicates, "partial/damaged:", partial, "errors:", errors, " time:", (time() - starttime)
|
||||
sys.exit(0)
|
||||
startpos=endpos
|
||||
print "Total stored:", stored, "duplicates:", duplicates, "partial:", partial, "errors:", errors, " time: %5.3f" % (time() - starttime)
|
||||
print "Total stored:", stored, "duplicates:", duplicates, "partial:", partial, "errors:", errors, " time:", (time() - starttime)
|
||||
|
||||
if stored==0:
|
||||
if duplicates>0:
|
||||
|
@ -300,11 +306,17 @@ class Importer:
|
|||
|
||||
|
||||
def printEmailErrorMessage(self, errors, filename, line):
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
print "Error No.",errors,", please send the hand causing this to steffen@sycamoretest.info so I can fix it."
|
||||
print "Filename:", filename
|
||||
print "Here is the first line so you can identify it. Please mention that the error was a ValueError:"
|
||||
print self.hand[0]
|
||||
|
||||
print "Hand logged to hand-errors.txt"
|
||||
logfile = open('hand-errors.txt', 'a')
|
||||
for s in self.hand:
|
||||
logfile.write(str(s) + "\n")
|
||||
logfile.write("\n")
|
||||
logfile.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
print "CLI for fpdb_import is now available as CliFpdb.py"
|
||||
|
|
|
@ -42,6 +42,8 @@ def mainParser(db, cursor, site, category, hand):
|
|||
smallBlindLine=0
|
||||
for i in range(len(hand)):
|
||||
if hand[i].find("posts small blind")!=-1 or hand[i].find("posts the small blind")!=-1:
|
||||
if hand[i][-2:] == "$0":
|
||||
continue
|
||||
smallBlindLine=i
|
||||
#print "found small blind line:",smallBlindLine
|
||||
break
|
||||
|
|
124
pyfpdb/fpdb_simple.py
Normal file → Executable file
124
pyfpdb/fpdb_simple.py
Normal file → Executable file
|
@ -54,7 +54,8 @@ def checkPositions(positions):
|
|||
pass
|
||||
|
||||
### RHH modified to allow for "position 9" here (pos==9 is when you're a dead hand before the BB
|
||||
if (pos!="B" and pos!="S" and pos!=0 and pos!=1 and pos!=2 and pos!=3 and pos!=4 and pos!=5 and pos!=6 and pos!=7 and pos!=9):
|
||||
### eric - position 8 could be valid - if only one blind is posted, but there's still 10 people, ie a sitout is present, and the small is dead...
|
||||
if not (pos == "B" or pos == "S" or (pos >= 0 and pos <= 9)):
|
||||
raise FpdbError("invalid position found in checkPositions. i: "+str(i)+" position: "+str(pos))
|
||||
#end def fpdb_simple.checkPositions
|
||||
|
||||
|
@ -484,7 +485,6 @@ def isActionLine(line):
|
|||
|
||||
#returns whether this is a duplicate
|
||||
def isAlreadyInDB(cursor, gametypeID, siteHandNo):
|
||||
#print "isAlreadyInDB gtid,shand:",gametypeID, siteHandNo
|
||||
cursor.execute ("SELECT id FROM Hands WHERE gametypeId=%s AND siteHandNo=%s", (gametypeID, siteHandNo))
|
||||
result=cursor.fetchall()
|
||||
if (len(result)>=1):
|
||||
|
@ -735,7 +735,7 @@ def parseCardLine(site, category, street, line, names, cardValues, cardSuits, bo
|
|||
print "line:",line,"cardValues[playerNo]:",cardValues[playerNo]
|
||||
raise FpdbError("read too many/too few holecards in parseCardLine")
|
||||
elif (category=="razz" or category=="studhi" or category=="studhilo"):
|
||||
if (line.find("shows")==-1):
|
||||
if (line.find("shows")==-1 and line.find("mucked") == -1):
|
||||
#print "parseCardLine(in stud if), street:", street
|
||||
if line[pos+2]=="]": #-> not (hero and 3rd street)
|
||||
cardValues[playerNo][street+2]=line[pos:pos+1]
|
||||
|
@ -832,14 +832,16 @@ def parseHandStartTime(topline, site):
|
|||
tmp=topline[pos1:pos2]
|
||||
isUTC=True
|
||||
else:
|
||||
tmp=topline[-30:]
|
||||
#print "parsehandStartTime, tmp:", tmp
|
||||
tmp=topline
|
||||
# print "parsehandStartTime, tmp:", tmp
|
||||
pos = tmp.find("-")+2
|
||||
tmp = tmp[pos:]
|
||||
#Need to match either
|
||||
# 2008/09/07 06:23:14 ET or
|
||||
# 2008/08/17 - 01:14:43 (ET)
|
||||
m = re.match('(?P<YEAR>[0-9]{4})\/(?P<MON>[0-9]{2})\/(?P<DAY>[0-9]{2})[\- ]+(?P<HR>[0-9]{2}):(?P<MIN>[0-9]{2}):(?P<SEC>[0-9]{2})',tmp)
|
||||
# 2008/08/17 - 01:14:43 (ET) or
|
||||
# 2008/11/12 9:33:31 CET [2008/11/12 3:33:31 ET]
|
||||
rexx = '(?P<YEAR>[0-9]{4})\/(?P<MON>[0-9]{2})\/(?P<DAY>[0-9]{2})[\- ]+(?P<HR>[0-9]+):(?P<MIN>[0-9]+):(?P<SEC>[0-9]+)'
|
||||
m = re.search(rexx,tmp)
|
||||
#print "year:", int(m.group('YEAR')), "month", int(m.group('MON')), "day", int(m.group('DAY')), "hour", int(m.group('HR')), "minute", int(m.group('MIN')), "second", int(m.group('SEC'))
|
||||
result = datetime.datetime(int(m.group('YEAR')), int(m.group('MON')), int(m.group('DAY')), int(m.group('HR')), int(m.group('MIN')), int(m.group('SEC')))
|
||||
else:
|
||||
|
@ -892,6 +894,11 @@ def parsePositions (hand, names):
|
|||
if (bb!=-1):
|
||||
bb=recognisePlayerNo(bb, names, "bet")
|
||||
|
||||
# print "sb = ", sb, "bb = ", bb
|
||||
if bb == sb:
|
||||
sbExists = False
|
||||
sb = -1
|
||||
|
||||
#write blinds into array
|
||||
if (sbExists):
|
||||
positions[sb]="S"
|
||||
|
@ -908,16 +915,25 @@ def parsePositions (hand, names):
|
|||
positions[arraypos]=distFromBtn
|
||||
arraypos-=1
|
||||
distFromBtn+=1
|
||||
|
||||
# eric - this takes into account dead seats between blinds
|
||||
if sbExists:
|
||||
i = bb - 1
|
||||
while positions[i] < 0 and i != sb:
|
||||
positions[i] = 9
|
||||
i -= 1
|
||||
### RHH - Changed to set the null seats before BB to "9"
|
||||
i=bb-1
|
||||
if sbExists:
|
||||
i = sb-1
|
||||
else:
|
||||
i = bb-1
|
||||
|
||||
while positions[i] < 0:
|
||||
positions[i]=9
|
||||
i-=1
|
||||
|
||||
arraypos=len(names)-1
|
||||
if (bb!=0 or (bb==0 and sbExists==False)):
|
||||
while (arraypos>bb):
|
||||
if (bb!=0 or (bb==0 and sbExists==False) or (bb == 1 and sb != arraypos) ):
|
||||
while (arraypos>bb and arraypos > sb):
|
||||
positions[arraypos]=distFromBtn
|
||||
arraypos-=1
|
||||
distFromBtn+=1
|
||||
|
@ -927,6 +943,7 @@ def parsePositions (hand, names):
|
|||
print "parsePositions names:",names
|
||||
print "result:",positions
|
||||
raise FpdbError ("failed to read positions")
|
||||
#print str(positions), "\n"
|
||||
return positions
|
||||
#end def parsePositions
|
||||
|
||||
|
@ -1022,7 +1039,7 @@ def recogniseCategory(line):
|
|||
#end def recogniseCategory
|
||||
|
||||
#returns the int for the gametype_id for the given line
|
||||
def recogniseGametypeID(db, cursor, topline, smallBlindLine, site_id, category, isTourney):#todo: this method is messy
|
||||
def recogniseGametypeID(cursor, topline, smallBlindLine, site_id, category, isTourney):#todo: this method is messy
|
||||
#if (topline.find("HORSE")!=-1):
|
||||
# raise FpdbError("recogniseGametypeID: HORSE is not yet supported.")
|
||||
|
||||
|
@ -1073,9 +1090,6 @@ def recogniseGametypeID(db, cursor, topline, smallBlindLine, site_id, category,
|
|||
else:
|
||||
cursor.execute ("SELECT id FROM Gametypes WHERE siteId=%s AND type=%s AND category=%s AND limitType=%s AND smallBlind=%s AND bigBlind=%s", (site_id, type, category, limit_type, small_bet, big_bet))
|
||||
result=cursor.fetchone()
|
||||
#print "recgt1 result=",result
|
||||
#ret=result[0]
|
||||
#print "recgt1 ret=",ret
|
||||
#print "tried SELECTing gametypes.id, result:",result
|
||||
|
||||
try:
|
||||
|
@ -1109,18 +1123,16 @@ def recogniseGametypeID(db, cursor, topline, smallBlindLine, site_id, category,
|
|||
cursor.execute("""INSERT INTO Gametypes
|
||||
(siteId, type, base, category, limitType, hiLo, smallBlind, bigBlind, smallBet, bigBet)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""", (site_id, type, base, category, limit_type, hiLo, small_blind, big_blind, small_bet, big_bet))
|
||||
#cursor.execute ("SELECT id FROM Gametypes WHERE siteId=%s AND type=%s AND category=%s AND limitType=%s AND smallBet=%s AND bigBet=%s", (site_id, type, category, limit_type, small_bet, big_bet))
|
||||
cursor.execute ("SELECT id FROM Gametypes WHERE siteId=%s AND type=%s AND category=%s AND limitType=%s AND smallBet=%s AND bigBet=%s", (site_id, type, category, limit_type, small_bet, big_bet))
|
||||
else:
|
||||
cursor.execute("""INSERT INTO Gametypes
|
||||
(siteId, type, base, category, limitType, hiLo, smallBlind, bigBlind, smallBet, bigBet)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""", (site_id, type, base, category, limit_type, hiLo, small_bet, big_bet, 0, 0))#remember, for these bet means blind
|
||||
#cursor.execute ("SELECT id FROM Gametypes WHERE siteId=%s AND type=%s AND category=%s AND limitType=%s AND smallBlind=%s AND bigBlind=%s", (site_id, type, category, limit_type, small_bet, big_bet))
|
||||
cursor.execute ("SELECT id FROM Gametypes WHERE siteId=%s AND type=%s AND category=%s AND limitType=%s AND smallBlind=%s AND bigBlind=%s", (site_id, type, category, limit_type, small_bet, big_bet))
|
||||
|
||||
result=(db.insert_id(),)
|
||||
#print "recgt2 result=",result
|
||||
result=cursor.fetchone()
|
||||
#print "created new gametypes.id:",result
|
||||
|
||||
#print "recgt3: result=", result
|
||||
return result[0]
|
||||
#end def recogniseGametypeID
|
||||
|
||||
|
@ -1241,8 +1253,7 @@ def storeActions(cursor, handsPlayersIds, actionTypes, allIns, actionAmounts, ac
|
|||
for i in range (len(actionTypes)): #iterate through streets
|
||||
for j in range (len(actionTypes[i])): #iterate through names
|
||||
for k in range (len(actionTypes[i][j])): #iterate through individual actions of that player on that street
|
||||
cursor.execute ("INSERT INTO HandsActions (handPlayerId, street, actionNo, action, allIn, amount) VALUES (%s, %s, %s, %s, %s, %s)"
|
||||
, (handsPlayersIds[j], i, actionNos[i][j][k], actionTypes[i][j][k], allIns[i][j][k], actionAmounts[i][j][k]))
|
||||
cursor.execute ("INSERT INTO HandsActions (handPlayerId, street, actionNo, action, allIn, amount) VALUES (%s, %s, %s, %s, %s, %s)", (handsPlayersIds[j], i, actionNos[i][j][k], actionTypes[i][j][k], allIns[i][j][k], actionAmounts[i][j][k]))
|
||||
#end def storeActions
|
||||
|
||||
def store_board_cards(cursor, hands_id, board_values, board_suits):
|
||||
|
@ -1255,16 +1266,15 @@ def store_board_cards(cursor, hands_id, board_values, board_suits):
|
|||
board_values[4], board_suits[4]))
|
||||
#end def store_board_cards
|
||||
|
||||
def storeHands(db, cursor, site_hand_no, gametype_id, hand_start_time, names, tableName, maxSeats):
|
||||
def storeHands(cursor, site_hand_no, gametype_id, hand_start_time, names, tableName, maxSeats):
|
||||
#stores into table hands
|
||||
cursor.execute ("INSERT INTO Hands (siteHandNo, gametypeId, handStart, seats, tableName, importTime, maxSeats) VALUES (%s, %s, %s, %s, %s, %s, %s)", (site_hand_no, gametype_id, hand_start_time, len(names), tableName, datetime.datetime.today(), maxSeats))
|
||||
#todo: find a better way of doing this...
|
||||
#cursor.execute("SELECT id FROM Hands WHERE siteHandNo=%s AND gametypeId=%s", (site_hand_no, gametype_id))
|
||||
#return cursor.fetchall()[0][0]
|
||||
return db.insert_id() # mysql only
|
||||
cursor.execute("SELECT id FROM Hands WHERE siteHandNo=%s AND gametypeId=%s", (site_hand_no, gametype_id))
|
||||
return cursor.fetchall()[0][0]
|
||||
#end def storeHands
|
||||
|
||||
def store_hands_players_holdem_omaha(db, cursor, category, hands_id, player_ids, start_cashes, positions, card_values, card_suits, winnings, rakes, seatNos):
|
||||
def store_hands_players_holdem_omaha(cursor, category, hands_id, player_ids, start_cashes, positions, card_values, card_suits, winnings, rakes, seatNos):
|
||||
result=[]
|
||||
if (category=="holdem"):
|
||||
for i in range (len(player_ids)):
|
||||
|
@ -1276,9 +1286,8 @@ def store_hands_players_holdem_omaha(db, cursor, category, hands_id, player_ids,
|
|||
(hands_id, player_ids[i], start_cashes[i], positions[i],
|
||||
card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1],
|
||||
winnings[i], rakes[i], seatNos[i]))
|
||||
#cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId=%s", (hands_id, player_ids[i]))
|
||||
#result.append(cursor.fetchall()[0][0])
|
||||
result.append( db.insert_id() ) # mysql only
|
||||
cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i]))
|
||||
result.append(cursor.fetchall()[0][0])
|
||||
elif (category=="omahahi" or category=="omahahilo"):
|
||||
for i in range (len(player_ids)):
|
||||
cursor.execute ("""INSERT INTO HandsPlayers
|
||||
|
@ -1290,15 +1299,14 @@ def store_hands_players_holdem_omaha(db, cursor, category, hands_id, player_ids,
|
|||
card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1],
|
||||
card_values[i][2], card_suits[i][2], card_values[i][3], card_suits[i][3],
|
||||
winnings[i], rakes[i], seatNos[i]))
|
||||
#cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i]))
|
||||
#result.append(cursor.fetchall()[0][0])
|
||||
result.append( db.insert_id() ) # mysql only
|
||||
cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i]))
|
||||
result.append(cursor.fetchall()[0][0])
|
||||
else:
|
||||
raise FpdbError("invalid category")
|
||||
return result
|
||||
#end def store_hands_players_holdem_omaha
|
||||
|
||||
def store_hands_players_stud(db, cursor, hands_id, player_ids, start_cashes, antes,
|
||||
def store_hands_players_stud(cursor, hands_id, player_ids, start_cashes, antes,
|
||||
card_values, card_suits, winnings, rakes, seatNos):
|
||||
#stores hands_players rows for stud/razz games. returns an array of the resulting IDs
|
||||
result=[]
|
||||
|
@ -1317,14 +1325,12 @@ def store_hands_players_stud(db, cursor, hands_id, player_ids, start_cashes, ant
|
|||
card_values[i][2], card_suits[i][2], card_values[i][3], card_suits[i][3],
|
||||
card_values[i][4], card_suits[i][4], card_values[i][5], card_suits[i][5],
|
||||
card_values[i][6], card_suits[i][6], winnings[i], rakes[i], seatNos[i]))
|
||||
#cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i]))
|
||||
#result.append(cursor.fetchall()[0][0])
|
||||
result.append( db.insert_id() ) # mysql only
|
||||
cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i]))
|
||||
result.append(cursor.fetchall()[0][0])
|
||||
return result
|
||||
#end def store_hands_players_stud
|
||||
|
||||
def store_hands_players_holdem_omaha_tourney(db, cursor, category, hands_id, player_ids,
|
||||
start_cashes, positions, card_values, card_suits, winnings, rakes, seatNos, tourneys_players_ids):
|
||||
def store_hands_players_holdem_omaha_tourney(cursor, category, hands_id, player_ids, start_cashes, positions, card_values, card_suits, winnings, rakes, seatNos, tourneys_players_ids):
|
||||
#stores hands_players for tourney holdem/omaha hands
|
||||
result=[]
|
||||
for i in range (len(player_ids)):
|
||||
|
@ -1350,14 +1356,13 @@ def store_hands_players_holdem_omaha_tourney(db, cursor, category, hands_id, pla
|
|||
winnings[i], rakes[i], tourneys_players_ids[i], seatNos[i]))
|
||||
else:
|
||||
raise FpdbError ("invalid card_values length:"+str(len(card_values[0])))
|
||||
#cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i]))
|
||||
#result.append(cursor.fetchall()[0][0])
|
||||
result.append( db.insert_id() ) # mysql only
|
||||
cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i]))
|
||||
result.append(cursor.fetchall()[0][0])
|
||||
|
||||
return result
|
||||
#end def store_hands_players_holdem_omaha_tourney
|
||||
|
||||
def store_hands_players_stud_tourney(db, cursor, hands_id, player_ids, start_cashes,
|
||||
def store_hands_players_stud_tourney(cursor, hands_id, player_ids, start_cashes,
|
||||
antes, card_values, card_suits, winnings, rakes, seatNos, tourneys_players_ids):
|
||||
#stores hands_players for tourney stud/razz hands
|
||||
result=[]
|
||||
|
@ -1375,18 +1380,13 @@ def store_hands_players_stud_tourney(db, cursor, hands_id, player_ids, start_cas
|
|||
card_values[i][2], card_suits[i][2], card_values[i][3], card_suits[i][3],
|
||||
card_values[i][4], card_suits[i][4], card_values[i][5], card_suits[i][5],
|
||||
card_values[i][6], card_suits[i][6], winnings[i], rakes[i], tourneys_players_ids[i], seatNos[i]))
|
||||
#cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i]))
|
||||
#result.append(cursor.fetchall()[0][0])
|
||||
result.append( db.insert_id() ) # mysql only
|
||||
cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId=%s", (hands_id, player_ids[i]))
|
||||
result.append(cursor.fetchall()[0][0])
|
||||
return result
|
||||
#end def store_hands_players_stud_tourney
|
||||
|
||||
def generateHudCacheData(player_ids, base, category, action_types, allIns, actionTypeByNo
|
||||
,winnings, totalWinnings, positions, actionTypes, actionAmounts):
|
||||
"""calculates data for the HUD during import. IMPORTANT: if you change this method make
|
||||
sure to also change the following storage method and table_viewer.prepare_data if necessary
|
||||
"""
|
||||
#print "generateHudCacheData, len(player_ids)=", len(player_ids)
|
||||
def generateHudCacheData(player_ids, base, category, action_types, allIns, actionTypeByNo, winnings, totalWinnings, positions):
|
||||
"""calculates data for the HUD during import. IMPORTANT: if you change this method make sure to also change the following storage method and table_viewer.prepare_data if necessary"""
|
||||
#setup subarrays of the result dictionary.
|
||||
street0VPI=[]
|
||||
street0Aggr=[]
|
||||
|
@ -1692,7 +1692,7 @@ def generateHudCacheData(player_ids, base, category, action_types, allIns, actio
|
|||
hudDataPositions.append('C')
|
||||
elif pos>=2 and pos<=4:
|
||||
hudDataPositions.append('M')
|
||||
elif pos>=5 and pos<=7:
|
||||
elif pos>=5 and pos<=8:
|
||||
hudDataPositions.append('E')
|
||||
### RHH Added this elif to handle being a dead hand before the BB (pos==9)
|
||||
elif pos==9:
|
||||
|
@ -1906,14 +1906,8 @@ def generateHudCacheData(player_ids, base, category, action_types, allIns, actio
|
|||
street3CheckCallRaiseDone=[]
|
||||
street4CheckCallRaiseChance=[]
|
||||
street4CheckCallRaiseDone=[]
|
||||
#print "b4 totprof calc, len(playerIds)=", len(player_ids)
|
||||
for pl in range (len(player_ids)):
|
||||
#print "pl=", pl
|
||||
myTotalProfit=winnings[pl] # still need to deduct costs
|
||||
for i in range (len(actionTypes)): #iterate through streets
|
||||
#for j in range (len(actionTypes[i])): #iterate through names (using pl loop above)
|
||||
for k in range (len(actionTypes[i][pl])): #iterate through individual actions of that player on that street
|
||||
myTotalProfit -= actionAmounts[i][pl][k]
|
||||
for player in range (len(player_ids)):
|
||||
myTotalProfit=0
|
||||
|
||||
myStreet1CheckCallRaiseChance=False
|
||||
myStreet1CheckCallRaiseDone=False
|
||||
|
@ -1924,9 +1918,7 @@ def generateHudCacheData(player_ids, base, category, action_types, allIns, actio
|
|||
myStreet4CheckCallRaiseChance=False
|
||||
myStreet4CheckCallRaiseDone=False
|
||||
|
||||
#print "myTotalProfit=", myTotalProfit
|
||||
totalProfit.append(myTotalProfit)
|
||||
#print "totalProfit[]=", totalProfit
|
||||
|
||||
street1CheckCallRaiseChance.append(myStreet1CheckCallRaiseChance)
|
||||
street1CheckCallRaiseDone.append(myStreet1CheckCallRaiseDone)
|
||||
|
@ -1938,7 +1930,6 @@ def generateHudCacheData(player_ids, base, category, action_types, allIns, actio
|
|||
street4CheckCallRaiseDone.append(myStreet4CheckCallRaiseDone)
|
||||
|
||||
result['totalProfit']=totalProfit
|
||||
#print "res[totalProfit]=", result['totalProfit']
|
||||
|
||||
result['street1CheckCallRaiseChance']=street1CheckCallRaiseChance
|
||||
result['street1CheckCallRaiseDone']=street1CheckCallRaiseDone
|
||||
|
@ -1974,8 +1965,6 @@ def generateFoldToCB(street, playerIDs, didStreetCB, streetCBDone, foldToStreetC
|
|||
def storeHudCache(cursor, base, category, gametypeId, playerIds, hudImportData):
|
||||
# if (category=="holdem" or category=="omahahi" or category=="omahahilo"):
|
||||
|
||||
#print "storeHudCache, len(playerIds)=", len(playerIds), " len(vpip)=" \
|
||||
#, len(hudImportData['street0VPI']), " len(totprof)=", len(hudImportData['totalProfit'])
|
||||
for player in range (len(playerIds)):
|
||||
if base=="hold":
|
||||
cursor.execute("SELECT * FROM HudCache WHERE gametypeId+0=%s AND playerId=%s AND activeSeats=%s AND position=%s", (gametypeId, playerIds[player], len(playerIds), hudImportData['position'][player]))
|
||||
|
@ -2060,9 +2049,6 @@ def storeHudCache(cursor, base, category, gametypeId, playerIds, hudImportData):
|
|||
if hudImportData['foldToStreet4CBChance'][player]: row[50]+=1
|
||||
if hudImportData['foldToStreet4CBDone'][player]: row[51]+=1
|
||||
|
||||
#print "player=", player
|
||||
#print "len(totalProfit)=", len(hudImportData['totalProfit'])
|
||||
if hudImportData['totalProfit'][player]:
|
||||
row[52]+=hudImportData['totalProfit'][player]
|
||||
|
||||
if hudImportData['street1CheckCallRaiseChance'][player]: row[53]+=1
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 48 KiB |
Loading…
Reference in New Issue
Block a user