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

This commit is contained in:
Eric Blade 2011-01-14 03:38:39 -05:00
commit 793244fb6b
29 changed files with 1374 additions and 164 deletions

View File

@ -33,6 +33,7 @@ psycopg2 ... http://www.stickpeople.com/projects/python/win-psycopg/psycopg2-2.2
(py)pokereval v138 ... http://sourceforge.net/projects/fpdb/files/fpdb/pypoker-eval-win32/pokereval-138.win32.exe/download
(Note: There are no official windows builds, this installer is built from source. A walkthrough is in the same directory as this walkthrough.
1.2/ MySQL
Install the following file:
@ -67,6 +68,26 @@ with this line:
1.3.4/ Save and exit
1.4/ Patch py2exe to stop popup runtime error message
see http://www.py2exe.org/index.cgi/StderrLog for technical info.
1.4.1/
dos> write C:\Python26\Lib\site-packages\py2exe\boot_common.py
replace:
atexit.register(alert, 0,
"See the logfile '%s' for details" % fname,
"Errors occurred")
with:
#atexit.register(alert, 0,
# "See the logfile '%s' for details" % fname,
# "Errors occurred")
1.4.2/ save and exit
Step 2 Setup GTK
-----------------

View File

@ -2169,7 +2169,7 @@ class Database:
dup = True
return dup
def getGameTypeId(self, siteid, game):
def getGameTypeId(self, siteid, game, printdata = False):
c = self.get_cursor()
#FIXME: Fixed for NL at the moment
c.execute(self.sql.query['getGametypeNL'], (siteid, game['type'], game['category'], game['limitType'], game['currency'],
@ -2181,14 +2181,22 @@ class Database:
hilo = "s"
elif game['category'] in ['razz','27_3draw','badugi', '27_1draw']:
hilo = "l"
#FIXME: recognise currency
#TODO: this wont work for non-standard structures
tmp = self.insertGameTypes( (siteid, game['currency'], game['type'], game['base'], game['category'], game['limitType'], hilo,
int(Decimal(game['sb'])*100), int(Decimal(game['bb'])*100),
int(Decimal(game['bb'])*100), int(Decimal(game['bb'])*200)) ) #TODO: this wont work for non-standard structures
#FIXME: recognise currency
int(Decimal(game['bb'])*100), int(Decimal(game['bb'])*200)), printdata = printdata)
return tmp[0]
def insertGameTypes(self, row):
def insertGameTypes(self, row, printdata = False):
if printdata:
print _("######## Gametype ##########")
import pprint
pp = pprint.PrettyPrinter(indent=4)
pp.pprint(row)
print _("###### End Gametype ########")
c = self.get_cursor()
c.execute( self.sql.query['insertGameTypes'], row )
return [self.get_last_insert_id(c)]

View File

@ -40,7 +40,6 @@ class DerivedStats():
self.handsplayers[player[1]]['winnings'] = 0
self.handsplayers[player[1]]['rake'] = 0
self.handsplayers[player[1]]['totalProfit'] = 0
self.handsplayers[player[1]]['street4Seen'] = False
self.handsplayers[player[1]]['street4Aggr'] = False
self.handsplayers[player[1]]['wonWhenSeenStreet1'] = 0.0
self.handsplayers[player[1]]['sawShowdown'] = False
@ -58,6 +57,11 @@ class DerivedStats():
self.handsplayers[player[1]]['foldedSbToSteal'] = False
self.handsplayers[player[1]]['foldedBbToSteal'] = False
self.handsplayers[player[1]]['tourneyTypeId'] = None
self.handsplayers[player[1]]['street1Seen'] = False
self.handsplayers[player[1]]['street2Seen'] = False
self.handsplayers[player[1]]['street3Seen'] = False
self.handsplayers[player[1]]['street4Seen'] = False
for i in range(5):
self.handsplayers[player[1]]['street%dCalls' % i] = 0
@ -152,10 +156,11 @@ class DerivedStats():
else:
self.handsplayers[player[1]]['tourneysPlayersIds'] = None
#### seen now processed in playersAtStreetX()
# XXX: enumerate(list, start=x) is python 2.6 syntax; 'start'
#for i, street in enumerate(hand.actionStreets[2:], start=1):
for i, street in enumerate(hand.actionStreets[2:]):
self.seen(self.hand, i+1)
#for i, street in enumerate(hand.actionStreets[2:]):
# self.seen(self.hand, i+1)
for i, street in enumerate(hand.actionStreets[1:]):
self.aggr(self.hand, i)
@ -298,6 +303,7 @@ class DerivedStats():
# FIXME?? - This isn't couting people that are all in - at least showdown needs to reflect this
# ... new code below hopefully fixes this
# partly fixed, allins are now set as seeing streets because they never do a fold action
self.hands['playersAtStreet1'] = 0
self.hands['playersAtStreet2'] = 0
@ -324,18 +330,58 @@ class DerivedStats():
# actionStreets[1] is 'DEAL', 'THIRD', 'PREFLOP', so any player dealt cards
# must act on this street if dealt cards. Almost certainly broken for the 'all-in blind' case
# and right now i don't care - CG
p_in = set([x[0] for x in hand.actions[hand.actionStreets[1]]])
#
# discover who folded on each street and remove them from p_in
#
# i values as follows 0=BLINDSANTES 1=PREFLOP 2=FLOP 3=TURN 4=RIVER
# (for flop games)
#
# At the beginning of the loop p_in contains the players with cards
# at the start of that street.
# p_in is reduced each street to become a list of players still-in
# e.g. when i=1 (preflop) all players who folded during preflop
# are found by pfba() and eliminated from p_in.
# Therefore at the end of the loop, p_in contains players remaining
# at the end of the action on that street, and can therefore be set
# as the value for the number of players who saw the next street
#
# note that i is 1 in advance of the actual street numbers in the db
#
# if p_in reduces to 1 player, we must bomb-out immediately
# because the hand is over, this will ensure playersAtStreetx
# is accurate.
#
for (i, street) in enumerate(hand.actionStreets):
if (i-1) in (1,2,3,4):
# p_in stores players with cards at start of this street,
# so can set streetxSeen & playersAtStreetx with this information
# This hard-coded for i-1 =1,2,3,4 because those are the only columns
# in the db! this code section also replaces seen() - more info log 66
# nb i=2=flop=street1Seen, hence i-1 term needed
self.hands['playersAtStreet%d' % (i-1)] = len(p_in)
for player_with_cards in p_in:
self.handsplayers[player_with_cards]['street%sSeen' % (i-1)] = True
#
# find out who folded, and eliminate them from p_in
#
actions = hand.actions[street]
p_in = p_in - self.pfba(actions, l=('folds',))
self.hands['playersAtStreet%d' % (i-1)] = len(p_in)
self.hands['playersAtShowdown'] = len(p_in)
#
# if everyone folded, we are done, so exit this method immediately
#
if len(p_in) == 1: return None
if self.hands['playersAtShowdown'] > 1:
for player in p_in:
self.handsplayers[player]['sawShowdown'] = True
#
# The remaining players in p_in reached showdown (including all-ins
# because they never did a "fold" action in pfba() above)
#
self.hands['playersAtShowdown'] = len(p_in)
for showdown_player in p_in:
self.handsplayers[showdown_player]['sawShowdown'] = True
def streetXRaises(self, hand):
# self.actions[street] is a list of all actions in a tuple, contining the action as the second element
@ -447,17 +493,6 @@ class DerivedStats():
self.handsplayers[pname]['street%dCheckCallRaiseChance' % (i+1)] = True
self.handsplayers[pname]['street%dCheckCallRaiseDone' % (i+1)] = act!='folds'
def seen(self, hand, i):
pas = set()
for act in hand.actions[hand.actionStreets[i+1]]:
pas.add(act[0])
for player in hand.players:
if player[1] in pas:
self.handsplayers[player[1]]['street%sSeen' % i] = True
else:
self.handsplayers[player[1]]['street%sSeen' % i] = False
def aggr(self, hand, i):
aggrers = set()
others = set()

View File

@ -76,8 +76,8 @@ class Fulltilt(HandHistoryConverter):
(\s\((?P<TURBO>Turbo)\))?)|(?P<UNREADABLE_INFO>.+))
''' % substitutions, re.VERBOSE)
re_Button = re.compile('^The button is in seat #(?P<BUTTON>\d+)', re.MULTILINE)
re_PlayerInfo = re.compile('Seat (?P<SEAT>[0-9]+): (?P<PNAME>.{2,15}) \([%(LS)s](?P<CASH>[,.0-9]+)\)$' % substitutions, re.MULTILINE)
re_TourneysPlayerInfo = re.compile('Seat (?P<SEAT>[0-9]+): (?P<PNAME>.{2,15}) \([%(LS)s]?(?P<CASH>[,.0-9]+)\)(, is sitting out)?$' % substitutions, re.MULTILINE)
re_PlayerInfo = re.compile('Seat (?P<SEAT>[0-9]+): (?P<PNAME>.{2,15}) \([%(LS)s]?(?P<CASH>[%(NUM)s]+)\)(?P<SITOUT>, is sitting out)?$' % substitutions, re.MULTILINE)
re_SummarySitout = re.compile('Seat (?P<SEAT>[0-9]+): (?P<PNAME>.{2,15}) is sitting out?$' % substitutions, re.MULTILINE)
re_Board = re.compile(r"\[(?P<CARDS>.+)\]")
#static regex for tourney purpose
@ -150,7 +150,7 @@ class Fulltilt(HandHistoryConverter):
self.re_HeroCards = re.compile(r"^Dealt to %s(?: \[(?P<OLDCARDS>.+?)\])?( \[(?P<NEWCARDS>.+?)\])" % player_re, re.MULTILINE)
self.re_Action = re.compile(r"^%(PLAYERS)s(?P<ATYPE> bets| checks| raises to| completes it to| calls| folds)( [%(LS)s]?(?P<BET>[%(NUM)s]+))?" % self.substitutions, re.MULTILINE)
self.re_ShowdownAction = re.compile(r"^%s shows \[(?P<CARDS>.*)\]" % player_re, re.MULTILINE)
self.re_CollectPot = re.compile(r"^Seat (?P<SEAT>[0-9]+): %(PLAYERS)s (\(button\) |\(small blind\) |\(big blind\) )?(collected|showed \[.*\] and won) \([%(LS)s]?(?P<POT>[%(NUM)s]+)\)(, mucked| with.*)" % self.substitutions, re.MULTILINE)
self.re_CollectPot = re.compile(r"^Seat (?P<SEAT>[0-9]+): %(PLAYERS)s (\(button\) |\(small blind\) |\(big blind\) )?(collected|showed \[.*\] and won) \([%(LS)s]?(?P<POT>[%(NUM)s]+)\)(, mucked| with.*)?" % self.substitutions, re.MULTILINE)
self.re_SitsOut = re.compile(r"^%s sits out" % player_re, re.MULTILINE)
self.re_ShownCards = re.compile(r"^Seat (?P<SEAT>[0-9]+): %s (\(button\) |\(small blind\) |\(big blind\) )?(?P<ACT>showed|mucked) \[(?P<CARDS>.*)\].*" % player_re, re.MULTILINE)
@ -298,13 +298,24 @@ class Fulltilt(HandHistoryConverter):
# Split hand text for FTP, as the regex matches the player names incorrectly
# in the summary section
pre, post = hand.handText.split('SUMMARY')
if hand.gametype['type'] == "ring" :
m = self.re_PlayerInfo.finditer(pre)
else: #if hand.gametype['type'] == "tour"
m = self.re_TourneysPlayerInfo.finditer(pre)
m = self.re_PlayerInfo.finditer(pre)
plist = {}
# Get list of players in header.
for a in m:
hand.addPlayer(int(a.group('SEAT')), a.group('PNAME'), a.group('CASH'))
plist[a.group('PNAME')] = [int(a.group('SEAT')), a.group('CASH')]
if hand.gametype['type'] == "ring" :
# Remove any listed as sitting out in the summary as start of hand info unreliable
n = self.re_SummarySitout.finditer(post)
for b in n:
del plist[b.group('PNAME')]
print "DEBUG: Deleting '%s' from player dict" %(b.group('PNAME'))
# Add remaining players
for a in plist:
seat, stack = plist[a]
hand.addPlayer(seat, a, stack)
def markStreets(self, hand):
@ -367,7 +378,11 @@ class Fulltilt(HandHistoryConverter):
logging.warning(_("No bringin found, handid =%s") % hand.handid)
def readButton(self, hand):
hand.buttonpos = int(self.re_Button.search(hand.handText).group('BUTTON'))
try:
hand.buttonpos = int(self.re_Button.search(hand.handText).group('BUTTON'))
except AttributeError, e:
# FTP has no indication that a hand is cancelled.
raise FpdbParseError(_("FTP: readButton: Failed to detect button (hand #%s cancelled?)") % hand.handid)
def readHeroCards(self, hand):
# streets PREFLOP, PREDRAW, and THIRD are special cases beacause

View File

@ -59,7 +59,6 @@ class GuiAutoImport (threading.Thread):
self.importer = fpdb_import.Importer(self, self.settings, self.config, self.sql)
self.importer.setCallHud(True)
self.importer.setMinPrint(settings['minPrint'])
self.importer.setQuiet(False)
self.importer.setFailOnError(False)
self.importer.setHandCount(0)
@ -338,14 +337,11 @@ if __name__== "__main__":
parser = OptionParser()
parser.add_option("-q", "--quiet", action="store_false", dest="gui", default=True, help="don't start gui")
parser.add_option("-m", "--minPrint", "--status", dest="minPrint", default="0", type="int",
help=_("How often to print a one-line status report (0 (default) means never)"))
(options, argv) = parser.parse_args()
config = Configuration.Config()
settings = {}
settings['minPrint'] = options.minPrint
if os.name == 'nt': settings['os'] = 'windows'
else: settings['os'] = 'linuxmac'

View File

@ -68,7 +68,6 @@ class GuiBulkImport():
# get the import settings from the gui and save in the importer
self.importer.setHandCount(int(self.spin_hands.get_text()))
self.importer.setMinPrint(int(self.spin_hands.get_text()))
self.importer.setQuiet(self.chk_st_st.get_active())
self.importer.setFailOnError(self.chk_fail.get_active())
self.importer.setThreads(int(self.spin_threads.get_text()))
@ -344,8 +343,6 @@ def main(argv=None):
help=_("Conversion filter (*Full Tilt Poker, PokerStars, Everleaf, Absolute)"))
parser.add_option("-x", "--failOnError", action="store_true", default=False,
help=_("If this option is passed it quits when it encounters any error"))
parser.add_option("-m", "--minPrint", "--status", dest="minPrint", default="0", type="int",
help=_("How often to print a one-line status report (0 (default) means never)"))
parser.add_option("-u", "--usage", action="store_true", dest="usage", default=False,
help=_("Print some useful one liners"))
parser.add_option("-s", "--starsarchive", action="store_true", dest="starsArchive", default=False,
@ -369,7 +366,6 @@ def main(argv=None):
config = Configuration.Config()
settings = {}
settings['minPrint'] = options.minPrint
if os.name == 'nt': settings['os'] = 'windows'
else: settings['os'] = 'linuxmac'

View File

@ -224,6 +224,9 @@ class GuiGraphViewer (threading.Thread):
self.graphBox.add(self.canvas)
self.canvas.show()
self.canvas.draw()
#TODO: Do something useful like alert user
#print "No hands returned by graph query"
else:
self.ax.set_title(_("Profit graph for ring games"+names),fontsize=12)
@ -337,10 +340,7 @@ class GuiGraphViewer (threading.Thread):
if len(winnings) == 0:
return (None, None, None)
#Insert a 0th entry into winnings so graph starts 'zerod'
winnings.insert(0, (0,0,0))
green = map(lambda x: float(x[1]), winnings)
green = map(lambda x:float(x[1]), winnings)
blue = map(lambda x: float(x[1]) if x[2] == True else 0.0, winnings)
red = map(lambda x: float(x[1]) if x[2] == False else 0.0, winnings)
greenline = cumsum(green)

View File

@ -58,7 +58,7 @@ onlinehelp = {'Game':_('Type of Game'),
'Saw_F':_('% Saw Flop vs hands dealt'),
'SawSD':_('Saw Show Down / River'),
'WtSDwsF':_('Went To Show Down When Saw Flop'),
'W$SD':_('Amount Won when Show Down seen'),
'W$SD':_('% Won some money at showdown'),
'FlAFq':_('Flop Aggression\n% Bet or Raise after seeing Flop'),
'TuAFq':_('Turn Aggression\n% Bet or Raise after seeing Turn'),
'RvAFq':_('River Aggression\n% Bet or Raise after seeing River'),

View File

@ -226,7 +226,7 @@ dealt whether they were seen in a 'dealt to' line
self.holecards[street][player] = [open, closed]
def prepInsert(self, db):
def prepInsert(self, db, printtest = False):
#####
# Players, Gametypes, TourneyTypes are all shared functions that are needed for additional tables
# These functions are intended for prep insert eventually
@ -235,7 +235,19 @@ dealt whether they were seen in a 'dealt to' line
self.dbid_pids = db.getSqlPlayerIDs([p[1] for p in self.players], self.siteId)
#Gametypes
self.dbid_gt = db.getGameTypeId(self.siteId, self.gametype)
hilo = "h"
if self.gametype['category'] in ['studhilo', 'omahahilo']:
hilo = "s"
elif self.gametype['category'] in ['razz','27_3draw','badugi', '27_1draw']:
hilo = "l"
self.gametyperow = (self.siteId, self.gametype['currency'], self.gametype['type'], self.gametype['base'],
self.gametype['category'], self.gametype['limitType'], hilo,
int(Decimal(self.gametype['sb'])*100), int(Decimal(self.gametype['bb'])*100),
int(Decimal(self.gametype['bb'])*100), int(Decimal(self.gametype['bb'])*200))
# Note: the above data is calculated in db.getGameTypeId
# Only being calculated above so we can grab the testdata
self.dbid_gt = db.getGameTypeId(self.siteId, self.gametype, printdata = printtest)
if self.tourNo!=None:
self.tourneyTypeId = db.createTourneyType(self)
@ -500,7 +512,6 @@ For sites (currently only Carbon Poker) which record "all in" as a special actio
# Player in the big blind posts
# - this is a call of 1 sb and a raise to 1 bb
#
log.debug("addBlind: %s posts %s, %s" % (player, blindtype, amount))
if player is not None:
amount = re.sub(u',', u'', amount) #some sites have commas
@ -520,9 +531,16 @@ For sites (currently only Carbon Poker) which record "all in" as a special actio
self.bets['BLINDSANTES'][player].append(Decimal(self.sb))
self.pot.addCommonMoney(player, Decimal(self.sb))
self.bets['PREFLOP'][player].append(Decimal(amount))
street = 'BLAH'
if self.gametype['base'] == 'hold':
street = 'PREFLOP'
elif self.gametype['base'] == 'draw':
street = 'DEAL'
self.bets[street][player].append(Decimal(amount))
self.pot.addMoney(player, Decimal(amount))
self.lastBet['PREFLOP'] = Decimal(amount)
self.lastBet[street] = Decimal(amount)
self.posted = self.posted + [[player,blindtype]]
@ -1142,34 +1160,6 @@ class DrawHand(Hand):
elif builtFrom == "DB":
self.select("dummy") # Will need a handId
# Draw games (at least Badugi has blinds - override default Holdem addBlind
def addBlind(self, player, blindtype, amount):
# if player is None, it's a missing small blind.
# The situation we need to cover are:
# Player in small blind posts
# - this is a bet of 1 sb, as yet uncalled.
# Player in the big blind posts
# - this is a call of 1 sb and a raise to 1 bb
#
log.debug("addBlind: %s posts %s, %s" % (player, blindtype, amount))
if player is not None:
self.bets['DEAL'][player].append(Decimal(amount))
self.stacks[player] -= Decimal(amount)
#print "DEBUG %s posts, stack %s" % (player, self.stacks[player])
act = (player, blindtype, Decimal(amount), self.stacks[player]==0)
self.actions['BLINDSANTES'].append(act)
self.pot.addMoney(player, Decimal(amount))
if blindtype == 'big blind':
self.lastBet['DEAL'] = Decimal(amount)
elif blindtype == 'both':
# extra small blind is 'dead'
amount = Decimal(amount)/3
amount += amount
self.lastBet['DEAL'] = Decimal(amount)
self.posted = self.posted + [[player,blindtype]]
#print "DEBUG: self.posted: %s" %(self.posted)
def addShownCards(self, cards, player, shown=True, mucked=False, dealt=False):
if player == self.hero: # we have hero's cards just update shown/mucked
if shown: self.shown.add(player)

View File

@ -56,6 +56,7 @@ class PokerStars(HandHistoryConverter):
'10.00': ('2.00', '5.00'), '10': ('2.00', '5.00'),
'20.00': ('5.00', '10.00'), '20': ('5.00', '10.00'),
'30.00': ('10.00', '15.00'), '30': ('10.00', '15.00'),
'40.00': ('10.00', '20.00'), '40': ('10.00', '20.00'),
'60.00': ('15.00', '30.00'), '60': ('15.00', '30.00'),
'80.00': ('20.00', '40.00'), '80': ('20.00', '40.00'),
'100.00': ('25.00', '50.00'), '100': ('25.00', '50.00'),

333
pyfpdb/RushNotesAux.py Normal file
View File

@ -0,0 +1,333 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""RushNotesAux.py
EXPERIMENTAL - USE WITH CARE
Auxilliary process to push HUD data into the FullTilt player notes XML
This will allow a rudimentary "HUD" in rush games
The existing notes file will be altered by this function
"""
# Copyright 2010, "Gimick" of the FPDB project fpdb.sourceforge.net
#
#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.
########################################################################
##########for each hand processed, attempts to create update for player notes in FullTilt
##########based upon the AW howto notes written by Ray E. Barker (nutomatic) at fpdb.sourceforge.net
##########Huge thanks to Ray for his guidance and encouragement to create this !!
#
#debugmode will write logfiles for the __init__ and update_data methods
# writes into ./pyfpdb/~Rushdebug.*
#
debugmode = False
# Standard Library modules
import os
import sys
from xml.dom import minidom
from datetime import datetime
from time import *
# FreePokerDatabase modules
from Mucked import Aux_Window
from Mucked import Seat_Window
from Mucked import Aux_Seats
import Stats
import Card
#
# overload minidom methods to fix bug where \n is parsed as " ".
# described here: http://bugs.python.org/issue7139
#
def _write_data(writer, data, isAttrib=False):
"Writes datachars to writer."
if isAttrib:
data = data.replace("\r", "&#xD;").replace("\n", "&#xA;")
data = data.replace("\t", "&#x9;")
writer.write(data)
minidom._write_data = _write_data
def writexml(self, writer, indent="", addindent="", newl=""):
# indent = current indentation
# addindent = indentation to add to higher levels
# newl = newline string
writer.write(indent+"<" + self.tagName)
attrs = self._get_attributes()
a_names = attrs.keys()
a_names.sort()
for a_name in a_names:
writer.write(" %s=\"" % a_name)
_write_data(writer, attrs[a_name].value, isAttrib=True)
writer.write("\"")
if self.childNodes:
writer.write(">%s"%(newl))
for node in self.childNodes:
node.writexml(writer,indent+addindent,addindent,newl)
writer.write("%s</%s>%s" % (indent,self.tagName,newl))
else:
writer.write("/>%s"%(newl))
# For an introduction to overriding instance methods, see
# http://irrepupavel.com/documents/python/instancemethod/
instancemethod = type(minidom.Element.writexml)
minidom.Element.writexml = instancemethod(
writexml, None, minidom.Element)
class RushNotes(Aux_Window):
def __init__(self, hud, config, params):
self.hud = hud
self.config = config
#
# following line makes all the site params magically available (thanks Ray!)
#
site_params_dict = self.hud.config.get_site_parameters(self.hud.site)
heroname = site_params_dict['screen_name']
sitename = site_params_dict['site_name']
notepath = site_params_dict['site_path'] # this is a temporary hijack of site-path
self.heroid = self.hud.db_connection.get_player_id(self.config, sitename, heroname)
self.notefile = notepath + "/" + heroname + ".xml"
self.rushtables = ("Mach 10", "Lightning", "Celerity", "Flash", "Zoom")
if not os.path.isfile(self.notefile):
self.active = False
return
else:
self.active = True
#
# read in existing notefile and backup with date/time in name
# todo change to not use dom
#
now = datetime.now()
notefilebackup = self.notefile + ".backup." + now.strftime("%Y%m%d%H%M%S")
xmlnotefile = minidom.parse(self.notefile)
outputfile = open(notefilebackup, 'w')
xmlnotefile.writexml(outputfile)
outputfile.close()
xmlnotefile.unlink
#
# Create a fresh queue file with skeleton XML
#
self.queuefile = self.notefile + ".queue"
queuedom = minidom.Document()
pld=queuedom.createElement("PLAYERDATA")
queuedom.appendChild(pld)
nts=queuedom.createElement("NOTES")
pld.appendChild(nts)
nte = queuedom.createElement("NOTE")
nte = queuedom.createTextNode("\n")
nts.insertBefore(nte,None)
outputfile = open(self.queuefile, 'w')
queuedom.writexml(outputfile)
outputfile.close()
queuedom.unlink
if (debugmode):
#initialise logfiles
debugfile=open("~Rushdebug.init", "w")
debugfile.write("conf="+str(config)+"\n")
debugfile.write("spdi="+str(site_params_dict)+"\n")
debugfile.write("para="+str(params)+"\n")
debugfile.write("hero="+heroname+" "+str(self.heroid)+"\n")
debugfile.write("back="+notefilebackup+"\n")
debugfile.write("queu="+self.queuefile+"\n")
debugfile.close()
open("~Rushdebug.data", "w").close()
def update_data(self, new_hand_id, db_connection):
#this method called once for every hand processed
# self.hud.stat_dict contains the stats information for this hand
if not self.active:
return
if (debugmode):
debugfile=open("~Rushdebug.data", "a")
debugfile.write(new_hand_id+"\n")
now = datetime.now()
debugfile.write(now.strftime("%Y%m%d%H%M%S")+ " update_data begins"+ "\n")
debugfile.write("hero="+str(self.heroid)+"\n")
#debugfile.write(str(self.hud.stat_dict)+"\n")
debugfile.write("table="+self.hud.table.name+"\n")
debugfile.write("players="+str(self.hud.stat_dict.keys())+"\n")
debugfile.write("db="+str(db_connection)+"\n")
if self.hud.table.name not in self.rushtables:
return
#
# Grab a list of player id's
#
handplayers = self.hud.stat_dict.keys()
#
# build a dictionary of stats text for each player in the hand (excluding the hero)
# xmlqueuedict contains {playername : stats text}
#
xmlqueuedict = {}
for playerid in handplayers:
# ignore hero, no notes available for hero at Full Tilt
if playerid == self.heroid: continue
playername=unicode(str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'playername')[1]))
# Use index[3] which is a short description
n=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'n')[3] + " ")
vpip=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'vpip')[3] + " ")
pfr=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'pfr')[3] + " ")
three_B=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'three_B')[3] + " ")
cbet=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'cbet')[3] + " ")
fbbsteal=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'f_BB_steal')[3] + " ")
steal=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'steal')[3] + " ")
ffreq1=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'ffreq1')[3] + " ")
agg_freq=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'agg_freq')[3] + " ")
BBper100=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'BBper100')[3])
if BBper100[6] == "-": BBper100=BBper100[0:6] + "(" + BBper100[7:] + ")"
#
# grab villain known starting hands
# only those where they VPIP'd, so limp in the BB will not be shown
# sort by hand strength. Output will show position too,
# so KK.1 is KK from late posn etc.
# ignore non-rush hands (check against known rushtablenames)
# cards decoding is hard-coded for holdem, so that's tuff atm
# three categories of known hands are shown:
# agression preflop hands
# non-aggression preflop hands
# bigblind called to defend hands
#
# This isn't perfect, but it isn't too bad a starting point
#
PFcall="PFcall"
PFaggr="PFaggr"
PFdefend="PFdefend"
c = db_connection.get_cursor()
c.execute(("SELECT handId, position, startCards, street0Aggr, tableName " +
"FROM hands, handsPlayers " +
"WHERE handsplayers.handId = hands.id " +
"AND street0VPI = 1 " +
"AND startCards > 0 " +
"AND playerId = %d " +
"ORDER BY startCards DESC " +
";")
% int(playerid))
for (qid, qposition, qstartcards, qstreet0Aggr, qtablename) in c.fetchall():
if (debugmode):
debugfile.write("pid, hid, pos, cards, aggr, table player"+
str(playerid)+"/"+str(qid)+"/"+str(qposition)+"/"+
str(qstartcards)+"/"+str(qstreet0Aggr)+"/"+
str(qtablename)+"/"+str(playername)+
"\n")
humancards = Card.decodeStartHandValue("holdem", qstartcards)
if qtablename not in self.rushtables:
pass
elif qposition == "B" and qstreet0Aggr == False:
PFdefend=PFdefend+"/"+humancards
elif qstreet0Aggr == True:
PFaggr=PFaggr+"/"+humancards+"."+qposition
else:
PFcall=PFcall+"/"+humancards+"."+qposition
c.close
#
# build up final text package (top/tail with ~fpdb~ ~ends~
# for later search/replace by Merge module
#
xmlqueuedict[playername] = ("~fpdb~" + "\n" +
n + vpip + pfr + three_B + fbbsteal + "\n" +
steal + cbet + ffreq1 + "\n" +
agg_freq + BBper100 + "\n" +
PFcall+"\n"+PFaggr+"\n"+PFdefend +"\n"
"~ends~")
if (debugmode):
now = datetime.now()
debugfile.write(now.strftime("%Y%m%d%H%M%S")+" villain data has been processed" + "\n")
debugfile.write(str(xmlqueuedict)+"\n")
#
# delaying processing of xml until now. Grab current queuefile contents and
# read each existing NOTE element in turn, if matched to a player in xmlqueuedict
# update their text in the xml and delete the dictionary item
#
xmlnotefile = minidom.parse(self.queuefile)
notelist = xmlnotefile.getElementsByTagName('NOTE')
for noteentry in notelist: #for each note in turn
noteplayer = noteentry.getAttribute("PlayerId") #extract the playername from xml
if noteplayer in xmlqueuedict: # does that player exist in the queue?
noteentry.setAttribute("Text",xmlqueuedict[noteplayer])
del xmlqueuedict[noteplayer] #remove from list, does not need to be added later on
#
#create entries for new players (those remaining in the dictionary)
#
if len(xmlqueuedict) > 0:
playerdata=xmlnotefile.lastChild #move to the PLAYERDATA node (assume last one in the list)
notesnode=playerdata.childNodes[0] #Find NOTES node
for newplayer in xmlqueuedict:
newentry = xmlnotefile.createElement("NOTE")
newentry.setAttribute("PlayerId", newplayer)
newentry.setAttribute("Text", xmlqueuedict[newplayer])
notesnode.insertBefore(newentry,None)
newentry = xmlnotefile.createTextNode("\n")
notesnode.insertBefore(newentry,None)
if (debugmode):
now = datetime.now()
debugfile.write(now.strftime("%Y%m%d%H%M%S")+" xml pre-processing complete"+ "\n")
#
# OverWrite existing xml file with updated DOM and cleanup
#
updatednotes = open(self.queuefile, 'w')
xmlnotefile.writexml(updatednotes)
updatednotes.close()
xmlnotefile.unlink
if (debugmode):
now = datetime.now()
debugfile.write(now.strftime("%Y%m%d%H%M%S")+" dom written, process finished"+ "\n")
debugfile.close()

183
pyfpdb/RushNotesMerge.py Executable file
View File

@ -0,0 +1,183 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""RushNotesMerge.py
EXPERIMENTAL - USE WITH CARE
Merge .queue file with hero's note to generate fresh .merge file
normal usage
$> ./pyfpdb/RushNotesMerge.py "/home/foo/.wine/drive_c/Program Files/Full Tilt Poker/heroname.xml"
The generated file can then replace heroname.xml (if all is well).
"""
# Copyright 2010, "Gimick" of the FPDB project fpdb.sourceforge.net
#
#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.
########################################################################
# Standard Library modules
import os
import sys
from xml.dom import minidom
#
# overload minidom methods to fix bug where \n is parsed as " ".
# described here: http://bugs.python.org/issue7139
#
def _write_data(writer, data, isAttrib=False):
"Writes datachars to writer."
if isAttrib:
data = data.replace("\r", "&#xD;").replace("\n", "&#xA;")
data = data.replace("\t", "&#x9;")
writer.write(data)
minidom._write_data = _write_data
def writexml(self, writer, indent="", addindent="", newl=""):
# indent = current indentation
# addindent = indentation to add to higher levels
# newl = newline string
writer.write(indent+"<" + self.tagName)
attrs = self._get_attributes()
a_names = attrs.keys()
a_names.sort()
for a_name in a_names:
writer.write(" %s=\"" % a_name)
_write_data(writer, attrs[a_name].value, isAttrib=True)
writer.write("\"")
if self.childNodes:
writer.write(">%s"%(newl))
for node in self.childNodes:
node.writexml(writer,indent+addindent,addindent,newl)
writer.write("%s</%s>%s" % (indent,self.tagName,newl))
else:
writer.write("/>%s"%(newl))
# For an introduction to overriding instance methods, see
# http://irrepupavel.com/documents/python/instancemethod/
instancemethod = type(minidom.Element.writexml)
minidom.Element.writexml = instancemethod(
writexml, None, minidom.Element)
statqueue=0
statupdated=0
statadded=0
def cleannote(textin):
if textin.find("~fpdb~") == -1: return textin
if textin.find("~ends~") == -1: return textin
if textin.find("~fpdb~") > textin.find("~ends~"): return textin
return textin[0:textin.find("~fpdb~")] + textin[textin.find("~ends~")+6:]
# get out now if parameter not passed
try:
sys.argv[1] <> ""
except:
print "A parameter is required, quitting now"
print "normal usage is something like:"
print '$> ./pyfpdb/RushNotesMerge.py "/home/foo/.wine/drive_c/Program Files/Full Tilt Poker/myhero.xml"'
quit()
if not os.path.isfile(sys.argv[1]):
print "Hero notes file not found, quitting"
print "normal usage is something like:"
print '$> ./pyfpdb/RushNotesMerge.py "/home/foo/.wine/drive_c/Program Files/Full Tilt Poker/myhero.xml"'
quit()
if not os.path.isfile((sys.argv[1]+".queue")):
print "Nothing found to merge, quitting"
print "Did the HUD not get started during the last session?"
print "Has the HUD been stopped and started without merging?"
quit()
print "***************************************************************"
print "IMPORTANT: *** Before running this merge: ***"
print "Closedown the FullTiltClient and wait for it to completely stop"
print "If FullTiltClient was running, run the merge again once it"
print "has stopped completely"
print "***************************************************************"
print
print "read from: ", sys.argv[1]
print "merge with: ", sys.argv[1]+".queue"
#read queue and turn into a dict
queuedict = {}
xmlqueue = minidom.parse(sys.argv[1]+".queue")
notelist = xmlqueue.getElementsByTagName('NOTE')
for noteentry in notelist:
noteplayer = noteentry.getAttribute("PlayerId")
notetext = noteentry.getAttribute("Text")
queuedict[noteplayer] = notetext
statqueue = statqueue + 1
#read existing player note file
xmlnotefile = minidom.parse(sys.argv[1])
notelist = xmlnotefile.getElementsByTagName('NOTE')
#
#for existing players, empty out existing fpdbtext and refill
#
for noteentry in notelist:
noteplayer = noteentry.getAttribute("PlayerId")
if noteplayer in queuedict:
existingnote = noteentry.getAttribute("Text")
newnote=cleannote(existingnote)
newnote = newnote + queuedict[noteplayer]
noteentry.setAttribute("Text",newnote)
statupdated = statupdated + 1
del queuedict[noteplayer]
#
#create entries for new players (those remaining in the dictionary)
#
if len(queuedict) > 0:
playerdata=xmlnotefile.lastChild #move to the PLAYERDATA node (assume last one in the list)
notesnode=playerdata.childNodes[1] #Find NOTES node
for newplayer in queuedict:
newentry = xmlnotefile.createElement("NOTE")
newentry.setAttribute("PlayerId", newplayer)
newentry.setAttribute("Text", queuedict[newplayer])
notesnode.insertBefore(newentry,None)
newentry = xmlnotefile.createTextNode("\n")
notesnode.insertBefore(newentry,None)
statadded=statadded+1
#print xmlnotefile.toprettyxml()
mergednotes = open(sys.argv[1]+".merged", 'w')
xmlnotefile.writexml(mergednotes)
mergednotes.close()
xmlnotefile.unlink
print "Merged file has been written to: ", sys.argv[1]+".merged"
print ""
print "number in queue: ", statqueue
print "existing players updated: ", statupdated
print "new players added: ", statadded
print "\n"
print "Use a viewer to check the contents of the merge file."
print "If you are happy, carry out the following steps:"
print "1 Rename or delete the existing notes file (normally <heroname>.xml"
print "2 Rename the .merged file to become the new notes file"

194
pyfpdb/RushNotesReadMe.txt Normal file
View File

@ -0,0 +1,194 @@
aux to write fpdb data to player notes on Full Tilt
---------------------------------------------------
by Gimick 30th Dec 2010
RushNotesAux - auxillary processed attached to the full tilt hud
builds up fpdb notes "queue" for each villain met while the autoimport is running
uses HUD aggregation stats to do this
RushNotesMerge - stand alone process to merge the existing ftp notes, together with queue
produced by Aux.
the output file can then be renamed to become the new ftp notes file
Important info:
The Merge process can only be run when ftp client is shutdown
- otherwise ftp overwrites the xml on exit.
Restarting the autoimport will empty the notes"queue" so avoid restarting
autoimport until the previous notes "queue" has been merged. You will
lose all the queued notes, but these will be regenerated the next time
the villian is at your table, so it isn't the end of the world.
Existing ftp notes _SHOULD_ be preserved, but this isn't guaranteed,
you have been warned!
Existing colour codings should be preserved,
this process does not change or set colourcodings.
Copies of the live ftp notes file should be preserved everytime
RushNotesAux (i.e. the HUD is started)
The AW is hard-coded with just the table names of Micro Rush Poker,
and should ignore all other hands.
What might not work?
--------------------
This isn't tested with Windows, and probably won't work, feedback welcome.
Hasn't been tested for co-existance with other sites, feedback welcome.
Whenever FTP change their notes file format, this will all break rather spectacularly,
you have been warned!
Getting started:
---------------
1. Set the Hero aggregation to alltime. hero_stat_range="A"
This overcomes a sqlite "bug" which has nothing to do with auxillary windows
not doing this will slow processing down to about 1 hand per minute.
2. Set the site_path to be the folder containing the FTP notes xml file
(on wine this is normally site_path="/home/blah/.wine/Program Files/Full Tilt Poker/")
Wire-up the aux process:
-----------------------
<aw class="RushNotes" module="RushNotesAux" name="Rush1"> </aw>
<game aux="Rush1" cols="3" db="fpdb" game_name="holdem" rows="3">
or whatever works for you.
Play some poker
---------------
Start Autoimport, and rearrange the on-screen stats out of the way
(the full HUD must run, killing the HUD kills the AW updates)
Play whatever you want
Stop the autoimport
Exit the Full tilt poker client (ensure it has fully stopped with ps -A)
execute the following:
./pyfpdb/RushNotesMerge.py "/home/foo/.wine/drive_c/Program Files/Full Tilt Poker/myname.xml"
A revised notes file (blah.merge) should automagically appear in the full tilt root directory.
If you are happy with it, replace the existing (myname.xml file)
Since the updates aren't real time, it would be ok to play the rush
session with fpdb inactive, but before quitting any of the tables,
start the HUD and wait for it to catch-up processing all the hands played.
Summary
-------
This is very rough and ready, but it does what I set-out to achieve.
All feedback welcome, and if this is useful as a basis for general notes
processing in future, then thats great.
As I find bugs and make improvements, I will push to git.
Much more information below:
----------------------------
Background
----------
When playing rush poker, some sort of rudimentary HUD would answer simple questions
like "is this allin overbet being made by a nit, or a maniac". Although some
notes may have been made previously, some statistics would help to backup the decision.
Currently fpdb cannot support rush because the HUD is always 1 hand or more
behind the current action.
The only way to do this at the moment is to switch to GuiPlayerStats and make a quick
enquiry by player name. However, this effectively times you out of all other
action if multitabling.
Full Tilt situation
-------------------
Full Tilt notes are stored in xml format ("hero.xml"). Previously these could
be updated while the game was in progress, however, FullTilt now cache the
notes and write them out when the application exits. This makes it impossible
to use the notes as a real-time HUD, and therefore real-time huds are now
forced to screen-scrape or poke around in the client memory.
Accepting this a limitation, this implementation updates the notes only once
the FullTilt client has been closed. Obviously, the villain HUD stats are only
as at the end of the last session, however, it is hoped this is significantly
better than having nothing at all. As the hero's hand history increases, the
notes should progressively mature in accuracy.
Preamble
--------
Note that this implementation was written purely to be "good enough" to work
for the author, and is not intended as package or production quality. It
is contributed as a starting point for others, or for experimental use.
Thanks to Ray Barker who gave a great deal of help throughout.
The implementation
-------------------
RushNotesAux is an fpdb auxilliary process, and is called for every hand
processed by autoimport. Each villain has a note prepared based on the current
fpdb data, and this note (in XML format) is stored in a queue file.
Auxilliary windows were chosen because
a) the author has limited fpdb and programming skill
b) the auxillary windows handler is well documented and supported
c) any code created has access to the full range of stats with little or no extra work
d) runs within the HUD, so the aggregation parameters are already available
Limitations
-----------
The notes are only regenerated if a hand is played against the villain. The
process does not "bulk load" notes based upon all the player stats in FPDB.
It is hoped that due to the relatively large hand volume and relatively small
player pools, this limitation will be largely overcome after a few sessions
although there will obviously be a number of players with no fpdb note.
The aggregation parameters used for the notes are based upon the HUD parameters.
(with the exception of the hand-ranges, which uses its' own criteria (see source)
Stopping and starting the HUD will erase the previously created notes queue file.
The HUD must run, so the individual player popups need to be manually relocated.
Although hard-coded for micro RUSH tablenames, the auxilliary window could
probably happily update notes of all cash and tournament players.
Process overview
----------------
1/ The HUD process is started.
1.1/ when the first hand is received, h fresh holding file is created, and
a copy of the current live xml note file is created as a security backup.
2/ For every hand played, the auxillary window is called
3/ Based upon the players in the hand, fpdb will be interrogated
and key stats are formatted in xml-style and written out to a holding file.
4/ At the end of the session, the HUD is stopped and the poker client closed
5/ The user can then review the contents of the holding file.
6/ A process is begun to "merge" the holding file into the existing player notes
7/ A new "merged" file is created. The process attempts to preserve any
existing notes, but this cannot be guaranteed.
8/ The user can review this merged file, and if they are happy,
they replace the existing note file.
9/ Note that this process never updates the live notes file in situ, but
there is a risk that something goes wrong, and that existing notes could be destroyed.

View File

@ -62,6 +62,43 @@ class FpdbError:
idx = f.find('regression')
print "(%3d) : %s" %(self.histogram[f], f[idx:])
def compare_gametypes_file(filename, importer, errors):
hashfilename = filename + '.gt'
in_fh = codecs.open(hashfilename, 'r', 'utf8')
whole_file = in_fh.read()
in_fh.close()
testhash = eval(whole_file)
hhc = importer.getCachedHHC()
handlist = hhc.getProcessedHands()
lookup = {
0:'siteId',
1:'currency',
2:'type',
3:'base',
4:'game',
5:'limit',
6:'hilo',
7:'Small Blind',
8:'Big Blind',
9:'Small Bet',
10:'Big Bet',
}
for hand in handlist:
ghash = hand.gametyperow
for i in range(len(ghash)):
print "DEBUG: about to compare: '%s' and '%s'" %(ghash[i], testhash[i])
if ghash[i] == testhash[i]:
# The stats match - continue
pass
else:
errors.error_report(filename, hand, lookup[i], ghash, testhash, None)
pass
def compare_handsplayers_file(filename, importer, errors):
hashfilename = filename + '.hp'
@ -142,6 +179,8 @@ def compare(leaf, importer, errors, site):
compare_handsplayers_file(filename, importer, errors)
if os.path.isfile(filename + '.hands'):
compare_hands_file(filename, importer, errors)
if os.path.isfile(filename + '.gt'):
compare_gametypes_file(filename, importer, errors)
importer.clearFileList()

View File

@ -86,7 +86,6 @@ class Importer:
self.cacheSessions = self.config.get_import_parameters().get("cacheSessions")
# CONFIGURATION OPTIONS
self.settings.setdefault("minPrint", 30)
self.settings.setdefault("handCount", 0)
#self.settings.setdefault("allowHudcacheRebuild", True) # NOT USED NOW
#self.settings.setdefault("forceThreads", 2) # NOT USED NOW
@ -115,9 +114,6 @@ class Importer:
def setCacheSessions(self, value):
self.cacheSessions = value
def setMinPrint(self, value):
self.settings['minPrint'] = int(value)
def setHandCount(self, value):
self.settings['handCount'] = int(value)
@ -477,7 +473,7 @@ class Importer:
for hand in handlist:
if hand is not None:
hand.prepInsert(self.database)
hand.prepInsert(self.database, printtest = self.settings['testData'])
try:
hand.insert(self.database, printtest = self.settings['testData'])
except Exceptions.FpdbHandDuplicate:

View File

@ -167,7 +167,7 @@
'street3CheckCallRaiseChance': False,
'street3CheckCallRaiseDone': False,
'street3Raises': 0,
'street3Seen': False,
'street3Seen': True,
'street4Aggr': False,
'street4Bets': 0,
'street4CBChance': False,
@ -184,7 +184,7 @@
'wonAtSD': 1.0,
'wonWhenSeenStreet1': 1.0,
'wonWhenSeenStreet2': 1.0,
'wonWhenSeenStreet3': 0.0,
'wonWhenSeenStreet3': 1.0,
'wonWhenSeenStreet4': 0.0},
u'Player2': { 'card1': 0,
'card2': 0,
@ -355,7 +355,7 @@
'street3CheckCallRaiseChance': False,
'street3CheckCallRaiseDone': False,
'street3Raises': 0,
'street3Seen': False,
'street3Seen': True,
'street4Aggr': False,
'street4Bets': 0,
'street4CBChance': False,

View File

@ -0,0 +1,27 @@
FullTiltPoker Game #25314791161: Table Squaw Tea (6 max) - $150/$300 - Limit 2-7 Triple Draw - 8:19:00 ET - 2010/11/06
Seat 1: Hero ($5,208), is sitting out
Seat 2: Player1 ($6,795.50), is sitting out
Seat 3: Player2 ($4,368)
Seat 5: Player3 ($3,625)
Seat 6: Player4 ($12,705)
Player4 posts the small blind of $75
Player2 posts the big blind of $150
The button is in seat #5
*** HOLE CARDS ***
Player3 folds
Player4 has 15 seconds left to act
Player4 folds
Uncalled bet of $75 returned to Player2
Player2 mucks
Player2 wins the pot ($150)
*** SUMMARY ***
Duration 24s
Total pot $150 | Rake $0
Seat 1: Hero is sitting out
Seat 2: Player1 is sitting out
Seat 3: Player2 (big blind) collected ($150)
Seat 5: Player3 (button) didn't bet (folded)
Seat 6: Player4 (small blind) folded before the draw

View File

@ -7,8 +7,6 @@
'importTime': None,
'maxSeats': 6,
'playersAtShowdown': 2,
'playersAtStreet-1': 5,
'playersAtStreet0': 2,
'playersAtStreet1': 2,
'playersAtStreet2': 2,
'playersAtStreet3': 2,

View File

@ -0,0 +1,28 @@
FullTiltPoker Game #25585334444: Table Rocky Cliff (6 max) - $250/$500 - Limit 2-7 Triple Draw - 18:08:00 ET - 2010/11/15
Seat 2: Player5 ($1,201)
Seat 3: Player6 ($4,108.50)
Seat 4: Player4 ($13,124.50)
Seat 5: Player3 ($5,245)
Seat 6: Hero ($5,000)
Player4 posts the small blind of $125
Player3 posts the big blind of $250
The button is in seat #3
*** HOLE CARDS ***
Player5 has 15 seconds left to act
Player5 raises to $500
Player6 folds
Player4 folds
Player3 folds
Uncalled bet of $250 returned to Player5
Player5 mucks
Player5 wins the pot ($625)
*** SUMMARY ***
Duration 18s
Total pot $625 | Rake $0
Seat 2: Player5 collected ($625)
Seat 3: Player6 (button) didn't bet (folded)
Seat 4: Player4 (small blind) folded before the draw
Seat 5: Player3 (big blind) folded before the draw
Seat 6: Hero is sitting out

View File

@ -0,0 +1 @@
(1, 'USD', 'ring', 'draw', '27_3draw', 'fl', 'l', 12500, 25000, 25000, 50000)

View File

@ -0,0 +1,32 @@
{ 'boardcard1': 0,
'boardcard2': 0,
'boardcard3': 0,
'boardcard4': 0,
'boardcard5': 0,
'gametypeId': 1,
'importTime': None,
'maxSeats': 6,
'playersAtShowdown': 1,
'playersAtStreet-1': 4,
'playersAtStreet0': 1,
'playersAtStreet1': 1,
'playersAtStreet2': 1,
'playersAtStreet3': 1,
'playersAtStreet4': 0,
'playersVpi': 1,
'seats': 4,
'showdownPot': 0,
'siteHandNo': u'25585334444',
'startTime': datetime.datetime(2010, 11, 15, 23, 8, tzinfo=pytz.utc),
'street0Raises': 1,
'street1Pot': 0,
'street1Raises': 0,
'street2Pot': 0,
'street2Raises': 0,
'street3Pot': 0,
'street3Raises': 0,
'street4Pot': 0,
'street4Raises': 0,
'tableName': u'Rocky Cliff',
'texture': None,
'tourneyId': None}

View File

@ -0,0 +1,376 @@
{ u'Player3': { 'card1': 0,
'card2': 0,
'card3': 0,
'card4': 0,
'card5': 0,
'card6': 0,
'card7': 0,
'foldBbToStealChance': True,
'foldSbToStealChance': False,
'foldToOtherRaisedStreet0': False,
'foldToOtherRaisedStreet1': False,
'foldToOtherRaisedStreet2': False,
'foldToOtherRaisedStreet3': False,
'foldToOtherRaisedStreet4': False,
'foldToStreet1CBChance': False,
'foldToStreet1CBDone': False,
'foldToStreet2CBChance': False,
'foldToStreet2CBDone': False,
'foldToStreet3CBChance': False,
'foldToStreet3CBDone': False,
'foldToStreet4CBChance': False,
'foldToStreet4CBDone': False,
'foldedBbToSteal': True,
'foldedSbToSteal': False,
'other3BStreet0': False,
'other4BStreet0': False,
'otherRaisedStreet0': False,
'otherRaisedStreet1': False,
'otherRaisedStreet2': False,
'otherRaisedStreet3': False,
'otherRaisedStreet4': False,
'position': 'B',
'raiseFirstInChance': False,
'raisedFirstIn': False,
'rake': 0,
'sawShowdown': False,
'seatNo': 5,
'sitout': False,
'startCards': 0,
'startCash': 524500,
'street0Aggr': False,
'street0Bets': 0,
'street0Calls': 0,
'street0Raises': 0,
'street0VPI': False,
'street0_3BChance': True,
'street0_3BDone': False,
'street0_4BChance': False,
'street0_4BDone': False,
'street1Aggr': False,
'street1Bets': 0,
'street1CBChance': False,
'street1CBDone': False,
'street1Calls': 0,
'street1CheckCallRaiseChance': False,
'street1CheckCallRaiseDone': False,
'street1Raises': 0,
'street1Seen': False,
'street2Aggr': False,
'street2Bets': 0,
'street2CBChance': False,
'street2CBDone': False,
'street2Calls': 0,
'street2CheckCallRaiseChance': False,
'street2CheckCallRaiseDone': False,
'street2Raises': 0,
'street2Seen': False,
'street3Aggr': False,
'street3Bets': 0,
'street3CBChance': False,
'street3CBDone': False,
'street3Calls': 0,
'street3CheckCallRaiseChance': False,
'street3CheckCallRaiseDone': False,
'street3Raises': 0,
'street3Seen': False,
'street4Aggr': False,
'street4Bets': 0,
'street4CBChance': False,
'street4CBDone': False,
'street4Calls': 0,
'street4CheckCallRaiseChance': False,
'street4CheckCallRaiseDone': False,
'street4Raises': 0,
'street4Seen': False,
'totalProfit': -25000,
'tourneyTypeId': None,
'tourneysPlayersIds': None,
'winnings': 0,
'wonAtSD': 0.0,
'wonWhenSeenStreet1': 0.0,
'wonWhenSeenStreet2': 0.0,
'wonWhenSeenStreet3': 0.0,
'wonWhenSeenStreet4': 0.0},
u'Player4': { 'card1': 0,
'card2': 0,
'card3': 0,
'card4': 0,
'card5': 0,
'card6': 0,
'card7': 0,
'foldBbToStealChance': False,
'foldSbToStealChance': True,
'foldToOtherRaisedStreet0': False,
'foldToOtherRaisedStreet1': False,
'foldToOtherRaisedStreet2': False,
'foldToOtherRaisedStreet3': False,
'foldToOtherRaisedStreet4': False,
'foldToStreet1CBChance': False,
'foldToStreet1CBDone': False,
'foldToStreet2CBChance': False,
'foldToStreet2CBDone': False,
'foldToStreet3CBChance': False,
'foldToStreet3CBDone': False,
'foldToStreet4CBChance': False,
'foldToStreet4CBDone': False,
'foldedBbToSteal': False,
'foldedSbToSteal': True,
'other3BStreet0': False,
'other4BStreet0': False,
'otherRaisedStreet0': False,
'otherRaisedStreet1': False,
'otherRaisedStreet2': False,
'otherRaisedStreet3': False,
'otherRaisedStreet4': False,
'position': 'S',
'raiseFirstInChance': False,
'raisedFirstIn': False,
'rake': 0,
'sawShowdown': False,
'seatNo': 4,
'sitout': False,
'startCards': 0,
'startCash': 1312450,
'street0Aggr': False,
'street0Bets': 0,
'street0Calls': 0,
'street0Raises': 0,
'street0VPI': False,
'street0_3BChance': True,
'street0_3BDone': False,
'street0_4BChance': False,
'street0_4BDone': False,
'street1Aggr': False,
'street1Bets': 0,
'street1CBChance': False,
'street1CBDone': False,
'street1Calls': 0,
'street1CheckCallRaiseChance': False,
'street1CheckCallRaiseDone': False,
'street1Raises': 0,
'street1Seen': False,
'street2Aggr': False,
'street2Bets': 0,
'street2CBChance': False,
'street2CBDone': False,
'street2Calls': 0,
'street2CheckCallRaiseChance': False,
'street2CheckCallRaiseDone': False,
'street2Raises': 0,
'street2Seen': False,
'street3Aggr': False,
'street3Bets': 0,
'street3CBChance': False,
'street3CBDone': False,
'street3Calls': 0,
'street3CheckCallRaiseChance': False,
'street3CheckCallRaiseDone': False,
'street3Raises': 0,
'street3Seen': False,
'street4Aggr': False,
'street4Bets': 0,
'street4CBChance': False,
'street4CBDone': False,
'street4Calls': 0,
'street4CheckCallRaiseChance': False,
'street4CheckCallRaiseDone': False,
'street4Raises': 0,
'street4Seen': False,
'totalProfit': -12500,
'tourneyTypeId': None,
'tourneysPlayersIds': None,
'winnings': 0,
'wonAtSD': 0.0,
'wonWhenSeenStreet1': 0.0,
'wonWhenSeenStreet2': 0.0,
'wonWhenSeenStreet3': 0.0,
'wonWhenSeenStreet4': 0.0},
u'Player5': { 'card1': 0,
'card2': 0,
'card3': 0,
'card4': 0,
'card5': 0,
'card6': 0,
'card7': 0,
'foldBbToStealChance': False,
'foldSbToStealChance': False,
'foldToOtherRaisedStreet0': False,
'foldToOtherRaisedStreet1': False,
'foldToOtherRaisedStreet2': False,
'foldToOtherRaisedStreet3': False,
'foldToOtherRaisedStreet4': False,
'foldToStreet1CBChance': False,
'foldToStreet1CBDone': False,
'foldToStreet2CBChance': False,
'foldToStreet2CBDone': False,
'foldToStreet3CBChance': False,
'foldToStreet3CBDone': False,
'foldToStreet4CBChance': False,
'foldToStreet4CBDone': False,
'foldedBbToSteal': False,
'foldedSbToSteal': False,
'other3BStreet0': False,
'other4BStreet0': False,
'otherRaisedStreet0': False,
'otherRaisedStreet1': False,
'otherRaisedStreet2': False,
'otherRaisedStreet3': False,
'otherRaisedStreet4': False,
'position': 1,
'raiseFirstInChance': True,
'raisedFirstIn': True,
'rake': 0,
'sawShowdown': False,
'seatNo': 2,
'sitout': False,
'startCards': 0,
'startCash': 120100,
'street0Aggr': True,
'street0Bets': 0,
'street0Calls': 0,
'street0Raises': 0,
'street0VPI': True,
'street0_3BChance': False,
'street0_3BDone': False,
'street0_4BChance': False,
'street0_4BDone': False,
'street1Aggr': False,
'street1Bets': 0,
'street1CBChance': False,
'street1CBDone': False,
'street1Calls': 0,
'street1CheckCallRaiseChance': False,
'street1CheckCallRaiseDone': False,
'street1Raises': 0,
'street1Seen': False,
'street2Aggr': False,
'street2Bets': 0,
'street2CBChance': False,
'street2CBDone': False,
'street2Calls': 0,
'street2CheckCallRaiseChance': False,
'street2CheckCallRaiseDone': False,
'street2Raises': 0,
'street2Seen': False,
'street3Aggr': False,
'street3Bets': 0,
'street3CBChance': False,
'street3CBDone': False,
'street3Calls': 0,
'street3CheckCallRaiseChance': False,
'street3CheckCallRaiseDone': False,
'street3Raises': 0,
'street3Seen': False,
'street4Aggr': False,
'street4Bets': 0,
'street4CBChance': False,
'street4CBDone': False,
'street4Calls': 0,
'street4CheckCallRaiseChance': False,
'street4CheckCallRaiseDone': False,
'street4Raises': 0,
'street4Seen': False,
'totalProfit': -25000,
'tourneyTypeId': None,
'tourneysPlayersIds': None,
'winnings': 0,
'wonAtSD': 0.0,
'wonWhenSeenStreet1': 0.0,
'wonWhenSeenStreet2': 0.0,
'wonWhenSeenStreet3': 0.0,
'wonWhenSeenStreet4': 0.0},
u'Player6': { 'card1': 0,
'card2': 0,
'card3': 0,
'card4': 0,
'card5': 0,
'card6': 0,
'card7': 0,
'foldBbToStealChance': False,
'foldSbToStealChance': False,
'foldToOtherRaisedStreet0': False,
'foldToOtherRaisedStreet1': False,
'foldToOtherRaisedStreet2': False,
'foldToOtherRaisedStreet3': False,
'foldToOtherRaisedStreet4': False,
'foldToStreet1CBChance': False,
'foldToStreet1CBDone': False,
'foldToStreet2CBChance': False,
'foldToStreet2CBDone': False,
'foldToStreet3CBChance': False,
'foldToStreet3CBDone': False,
'foldToStreet4CBChance': False,
'foldToStreet4CBDone': False,
'foldedBbToSteal': False,
'foldedSbToSteal': False,
'other3BStreet0': False,
'other4BStreet0': False,
'otherRaisedStreet0': False,
'otherRaisedStreet1': False,
'otherRaisedStreet2': False,
'otherRaisedStreet3': False,
'otherRaisedStreet4': False,
'position': 0,
'raiseFirstInChance': False,
'raisedFirstIn': False,
'rake': 0,
'sawShowdown': False,
'seatNo': 3,
'sitout': False,
'startCards': 0,
'startCash': 410850,
'street0Aggr': False,
'street0Bets': 0,
'street0Calls': 0,
'street0Raises': 0,
'street0VPI': False,
'street0_3BChance': True,
'street0_3BDone': False,
'street0_4BChance': False,
'street0_4BDone': False,
'street1Aggr': False,
'street1Bets': 0,
'street1CBChance': False,
'street1CBDone': False,
'street1Calls': 0,
'street1CheckCallRaiseChance': False,
'street1CheckCallRaiseDone': False,
'street1Raises': 0,
'street1Seen': False,
'street2Aggr': False,
'street2Bets': 0,
'street2CBChance': False,
'street2CBDone': False,
'street2Calls': 0,
'street2CheckCallRaiseChance': False,
'street2CheckCallRaiseDone': False,
'street2Raises': 0,
'street2Seen': False,
'street3Aggr': False,
'street3Bets': 0,
'street3CBChance': False,
'street3CBDone': False,
'street3Calls': 0,
'street3CheckCallRaiseChance': False,
'street3CheckCallRaiseDone': False,
'street3Raises': 0,
'street3Seen': False,
'street4Aggr': False,
'street4Bets': 0,
'street4CBChance': False,
'street4CBDone': False,
'street4Calls': 0,
'street4CheckCallRaiseChance': False,
'street4CheckCallRaiseDone': False,
'street4Raises': 0,
'street4Seen': False,
'totalProfit': 0,
'tourneyTypeId': None,
'tourneysPlayersIds': None,
'winnings': 0,
'wonAtSD': 0.0,
'wonWhenSeenStreet1': 0.0,
'wonWhenSeenStreet2': 0.0,
'wonWhenSeenStreet3': 0.0,
'wonWhenSeenStreet4': 0.0}}

View File

@ -6,12 +6,10 @@
'gametypeId': 7,
'importTime': None,
'maxSeats': 6,
'playersAtShowdown': 1,
'playersAtStreet-1': 6,
'playersAtStreet0': 1,
'playersAtStreet1': 1,
'playersAtStreet2': 1,
'playersAtStreet3': 1,
'playersAtShowdown': 0,
'playersAtStreet1': 0,
'playersAtStreet2': 0,
'playersAtStreet3': 0,
'playersAtStreet4': 0,
'playersVpi': 1,
'seats': 6,

View File

@ -7,8 +7,6 @@
'importTime': None,
'maxSeats': 9,
'playersAtShowdown': 0,
'playersAtStreet-1': 8,
'playersAtStreet0': 0,
'playersAtStreet1': 0,
'playersAtStreet2': 0,
'playersAtStreet3': 0,

View File

@ -7,11 +7,9 @@
'importTime': None,
'maxSeats': 6,
'playersAtShowdown': 2,
'playersAtStreet-1': 6,
'playersAtStreet0': 4,
'playersAtStreet1': 3,
'playersAtStreet1': 4,
'playersAtStreet2': 3,
'playersAtStreet3': 2,
'playersAtStreet3': 3,
'playersAtStreet4': 0,
'playersVpi': 3,
'seats': 6,

View File

@ -6,13 +6,11 @@
'gametypeId': 5,
'importTime': None,
'maxSeats': 8,
'playersAtShowdown': 1,
'playersAtStreet-1': 7,
'playersAtStreet0': 3,
'playersAtShowdown': 0,
'playersAtStreet1': 3,
'playersAtStreet2': 2,
'playersAtStreet3': 1,
'playersAtStreet4': 1,
'playersAtStreet2': 3,
'playersAtStreet3': 2,
'playersAtStreet4': 0,
'playersVpi': 3,
'seats': 7,
'showdownPot': 0,

View File

@ -6,15 +6,13 @@
'gametypeId': 1,
'importTime': None,
'maxSeats': 8,
'playersAtShowdown': 1,
'playersAtStreet-1': 7,
'playersAtStreet0': 3,
'playersAtStreet1': 2,
'playersAtStreet2': 1,
'playersAtStreet3': 1,
'playersAtStreet4': 1,
'playersAtShowdown': 0,
'playersAtStreet1': 3,
'playersAtStreet2': 2,
'playersAtStreet3': 0,
'playersAtStreet4': 0,
'playersVpi': 2,
'seats': 8,
'seats': 7,
'showdownPot': 0,
'siteHandNo': u'26190500040',
'startTime': datetime.datetime(2010, 12, 7, 9, 19, tzinfo=pytz.utc),

View File

@ -1,49 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#Copyright 2009-2010 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.
import BetfairToFpdb
from Hand import *
import py
import Configuration
import Database
import SQL
import fpdb_import
config = Configuration.Config(file = "HUD_config.test.xml")
db = Database.Database(config)
sql = SQL.Sql(db_server = 'sqlite')
settings = {}
settings.update(config.get_db_parameters())
settings.update(config.get_import_parameters())
settings.update(config.get_default_paths())
def testFlopImport():
db.recreate_tables()
importer = fpdb_import.Importer(False, settings, config)
importer.setDropIndexes("don't drop")
importer.setFailOnError(True)
importer.setThreads(-1)
importer.addBulkImportImportFileOrDir(
"""regression-test-files/cash/Betfair/Flop/PLO-6max-USD-0.05-0.10-200909.All.in.river.splitpot.txt""", site="Betfair")
importer.setCallHud(False)
(stored, dups, partial, errs, ttime) = importer.runImport()
importer.clearFileList()
# Should actually do some testing here
assert 1 == 1