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

This commit is contained in:
eblade 2008-12-11 02:57:08 -05:00
commit 51b62392e1
21 changed files with 4101 additions and 3349 deletions

View File

@ -29,8 +29,8 @@ cp -R regression-test fpdb-$1/
cp -R utils fpdb-$1/ cp -R utils fpdb-$1/
cd fpdb-$1 cd fpdb-$1
zip -r ../../fpdb-1.0_$1.zip * zip -r releases/fpdb-1.0_$1.zip *
tar -cf - * | bzip2 >> ../../fpdb-1.0_$1.tar.bz2 tar -cf - * | bzip2 >> releases/fpdb-1.0_$1.tar.bz2
cd .. cd ..
rm -r fpdb-$1 rm -r fpdb-$1

View File

@ -312,11 +312,10 @@ The program itself is licensed under AGPLv3, see agpl-3.0.txt</p>
</TABLE> </TABLE>
<p><BR></P> <p><BR></P>
<p><B>Table HandsPlayers</B></P> <p><B>Table HandsPlayers</B></P>
<p>cardX: can be 1 through 20, one for each card. In holdem only 1-2 of these are used, in omaha 1-4, in stud/razz 1-7, in single draw games 1-10 is used and in badugi 1-16 (4*4) is used.</P> <p>cardX: can be 1 through 20, one for each card. In holdem only 1-2 of these are used, in omaha 1-4, in stud/razz 1-7, in single draw 1-10, in tripple draw all 20 and in badugi 1-16 (4*4).</P>
<p>For the draw games: the first 5 (badugi: 4) cards are the initial cards, the next 5 (badugi: 4) are after the first draw. If a player keeps some cards then those cards' spaces are filled with "k", short for "kept".<br> <p>For the draw games: the first 5 (badugi: 4) cards are the initial cards, the next 5 (badugi: 4) are after the first draw, etc.<br>
Example 1: If a player gets 2-6 spades for his first five cards and decides to throw away the 4 and then gets a 7 of spades then the first 10 fields of cardXValue would be as follows: 2, 3, 4, 5, 6, k, k, 7, k, k<br> Example 1: If a player gets 2-6 spades for his first five cards and decides to throw away the 4 and then gets a 7 of spades then the first 10 fields of cardXValue would be as follows: 2, 3, 4, 5, 6, 2, 3, 5, 6, 7<br>
Example 2: If a player gets 2, 3, 5, 8, J of spades for his first five cards and decides to throw away the 2 and the 3 and then gets a Q and K of spades then the first 10 fields of cardXValue would be as follows: 2, 3, 5, 8, J, Q, K, k, k, k<br> Example 2: If a player gets 2, 3, 5, 8, J of spades for his first five cards and decides to throw away the 2 and the 3 and then gets a Q and K of spades then the first 10 fields of cardXValue would be as follows: 2, 3, 5, 8, J, 5, 8, J, Q, K.</p>
Note that it will k in the space of which card was there previously, so in example 2 where the player kept the last 3 cards, the last 3 fields of the first draw (ie. card8-10Value) are replaced with k.</p>
<p>I did not separate this into an extra table because I felt the lost space is not sufficiently large. Also the benefit for searching is far less relevant.</P> <p>I did not separate this into an extra table because I felt the lost space is not sufficiently large. Also the benefit for searching is far less relevant.</P>
<TABLE BORDER=1 CELLPADDING=2 CELLSPACING=0> <TABLE BORDER=1 CELLPADDING=2 CELLSPACING=0>
<TR VALIGN=TOP> <TR VALIGN=TOP>
@ -370,6 +369,17 @@ Note that it will k in the space of which card was there previously, so in examp
<TD><P>char(1)</P></TD> <TD><P>char(1)</P></TD>
<TD><P>h=hearts, s=spades, d=diamonds, c=clubs, unknown/no card=x</P></TD> <TD><P>h=hearts, s=spades, d=diamonds, c=clubs, unknown/no card=x</P></TD>
</TR> </TR>
<TR VALIGN=TOP>
<TD><P>cardXDiscarded</P></TD>
<TD><P>boolean</P></TD>
<TD><P>Whether the card was discarded (this only applies to draw games, X can be 1 through 15 since the final cards can obviously not be discarded).</P></TD>
</TR>
<TR VALIGN=TOP>
<TD><P>DrawnX</P></TD>
<TD><P>smallint</P></TD>
<TD><p>X can be 1 through 3.<br>
This field denotes how many cards the player has drawn on each draw.</P></TD>
</TR>
<TR VALIGN=TOP> <TR VALIGN=TOP>
<TD><P>winnings</P></TD> <TD><P>winnings</P></TD>
<TD><P>int</P></TD> <TD><P>int</P></TD>

View File

@ -444,9 +444,9 @@ class Config:
def get_import_parameters(self): def get_import_parameters(self):
imp = {} imp = {}
try: try:
imp['callFpdbHud'] = self.callFpdbHud imp['callFpdbHud'] = self.imp.callFpdbHud
imp['interval'] = self.interval imp['interval'] = self.imp.interval
imp['hhArchiveBase'] = self.hhArchiveBase imp['hhArchiveBase'] = self.imp.hhArchiveBase
except: # Default params except: # Default params
imp['callFpdbHud'] = True imp['callFpdbHud'] = True
imp['interval'] = 10 imp['interval'] = 10
@ -613,9 +613,7 @@ if __name__== "__main__":
print "----------- END POPUP WINDOW FORMATS -----------" print "----------- END POPUP WINDOW FORMATS -----------"
print "\n----------- IMPORT -----------" print "\n----------- IMPORT -----------"
tmp = c.get_import_parameters() print c.imp
for param in tmp:
print " " + str(param) + ": " + str(tmp[param])
print "----------- END IMPORT -----------" print "----------- END IMPORT -----------"
print "\n----------- TABLE VIEW -----------" print "\n----------- TABLE VIEW -----------"

183
pyfpdb/EverleafToFpdb.py Normal file → Executable file
View File

@ -58,42 +58,46 @@ from HandHistoryConverter import *
# smaragdar calls [$ 34.50 USD] # smaragdar calls [$ 34.50 USD]
# ** Dealing Turn ** [ 2d ] # ** Dealing Turn ** [ 2d ]
# ** Dealing River ** [ 6c ] # ** 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 ] # smaragdar wins $ 102 USD from main pot with a pair of aces [ ad, ah, qs, 8h, 6c ]
class Everleaf(HandHistoryConverter): class Everleaf(HandHistoryConverter):
def __init__(self, config, file): def __init__(self, config, file):
print "Initialising Everleaf converter class" print "Initialising Everleaf converter class"
HandHistoryConverter.__init__(self, config, file, "Everleaf") # Call super class init. HandHistoryConverter.__init__(self, config, file, sitename="Everleaf") # Call super class init.
self.sitename = "Everleaf" self.sitename = "Everleaf"
self.setFileType("text") self.setFileType("text")
self.rexx.setGameInfoRegex('.*Blinds \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+)') self.rexx.setGameInfoRegex('.*Blinds \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+)')
self.rexx.setSplitHandRegex('\n\n\n\n') 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.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.setPlayerInfoRegex('Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \( \$ (?P<CASH>[.0-9]+) USD \)')
self.rexx.setPostSbRegex('.*\n(?P<PNAME>.*): posts small blind \[') self.rexx.setPostSbRegex('.*\n(?P<PNAME>.*): posts small blind \[\$? (?P<SB>[.0-9]+)')
self.rexx.setPostBbRegex('.*\n(?P<PNAME>.*): posts big blind \[') self.rexx.setPostBbRegex('.*\n(?P<PNAME>.*): posts big blind \[\$? (?P<BB>[.0-9]+)')
self.rexx.setHeroCardsRegex('.*\nDealt\sto\s(?P<PNAME>.*)\s\[ (?P<HOLECARDS>.*) \]') # mct : what about posting small & big blinds simultaneously?
self.rexx.setActionStepRegex('.*\n(?P<PNAME>.*) (?P<ATYPE>bets|checks|raises|calls|folds)(\s\[\$ (?P<BET>[.\d]+) USD\])?') self.rexx.setHeroCardsRegex('.*\nDealt\sto\s(?P<PNAME>.*)\s\[ (?P<HOLE1>\S\S), (?P<HOLE2>\S\S) \]')
self.rexx.compileRegexes() self.rexx.setActionStepRegex('.*\n(?P<PNAME>.*) (?P<ATYPE>bets|checks|raises|calls|folds)(\s\[\$ (?P<BET>[.\d]+) USD\])?')
self.rexx.setShowdownActionRegex('.*\n(?P<PNAME>.*) shows \[ (?P<CARDS>.*) \]')
self.rexx.compileRegexes()
def readSupportedGames(self): def readSupportedGames(self):
pass pass
def determineGameType(self): def determineGameType(self):
# Cheating with this regex, only support nlhe at the moment # Cheating with this regex, only support nlhe at the moment
gametype = ["ring", "hold", "nl"] gametype = ["ring", "hold", "nl"]
m = self.rexx.game_info_re.search(self.obs) m = self.rexx.game_info_re.search(self.obs)
gametype = gametype + [m.group('SB')] gametype = gametype + [m.group('SB')]
gametype = gametype + [m.group('BB')] gametype = gametype + [m.group('BB')]
return gametype return gametype
def readHandInfo(self, hand): def readHandInfo(self, hand):
m = self.rexx.hand_info_re.search(hand.string) m = self.rexx.hand_info_re.search(hand.string)
hand.handid = m.group('HID') hand.handid = m.group('HID')
hand.tablename = m.group('TABLE') hand.tablename = m.group('TABLE')
# These work, but the info is already in the Hand class - should be used for tourneys though. # These work, but the info is already in the Hand class - should be used for tourneys though.
# m.group('SB') # m.group('SB')
# m.group('BB') # m.group('BB')
@ -106,66 +110,87 @@ class Everleaf(HandHistoryConverter):
# 2008/11/10 3:58:52 ET # 2008/11/10 3:58:52 ET
#TODO: Do conversion from GMT to 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) #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')), 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'))) int(m.group('HR')), int(m.group('MIN')), int(m.group('SEC')))
hand.buttonpos = int(m.group('BUTTON')) hand.buttonpos = int(m.group('BUTTON'))
def readPlayerStacks(self, hand): def readPlayerStacks(self, hand):
m = self.rexx.player_info_re.finditer(hand.string) m = self.rexx.player_info_re.finditer(hand.string)
players = [] players = []
for a in m: for a in m:
players = players + [[a.group('SEAT'), a.group('PNAME'), a.group('CASH')]] hand.addPlayer(int(a.group('SEAT')), a.group('PNAME'), a.group('CASH'))
hand.players = players def markStreets(self, hand):
# PREFLOP = ** Dealing down cards **
def markStreets(self, hand): 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)
# PREFLOP = ** Dealing down cards **
m = re.search('(\*\* Dealing down cards \*\*\n)(?P<PREFLOP>.*?\n\*\*)?( Dealing Flop \*\*)?(?P<FLOP>.*?\*\*)?( Dealing Turn \*\*)?(?P<TURN>.*?\*\*)?( Dealing River \*\*)?(?P<RIVER>.*)', hand.string,re.DOTALL)
# for street in m.groupdict(): # for street in m.groupdict():
# print "DEBUG: Street: %s\tspan: %s" %(street, str(m.span(street))) # print "DEBUG: Street: %s\tspan: %s" %(street, str(m.span(street)))
hand.streets = m hand.streets = m
def readBlinds(self, hand): def readCommunityCards(self, hand):
try: # currently regex in wrong place pls fix my brain's fried
m = self.rexx.small_blind_re.search(hand.string) # what a mess!
hand.posted = [m.group('PNAME')] re_board = re.compile('\*\* Dealing (?P<STREET>.*) \*\* \[ (?P<CARDS>.*) \]')
except: m = re_board.finditer(hand.string)
hand.posted = ["FpdbNBP"] for street in m:
m = self.rexx.big_blind_re.finditer(hand.string) #print street.groups()
for a in m: re_card = re.compile('(?P<CARD>[0-9tjqka][schd])') # look that's weird, hole cards have a capital rank but board cards are lower case?
hand.posted = hand.posted + [a.group('PNAME')] cardsmatch = re_card.finditer(street.group('CARDS'))
hand.setCommunityCards(street.group('STREET'), [card.group('CARD') for card in cardsmatch])
def readHeroCards(self, hand): def readBlinds(self, hand):
m = self.rexx.hero_cards_re.search(hand.string) try:
if(m == None): m = self.rexx.small_blind_re.search(hand.string)
#Not involved in hand hand.addBlind(m.group('PNAME'), m.group('SB'))
hand.involved = False #hand.posted = [m.group('PNAME')]
else: except:
hand.hero = m.group('PNAME') hand.addBlind(None, 0)
hand.holecards = m.group('HOLECARDS') #hand.posted = ["FpdbNBP"]
hand.holecards = hand.holecards.replace(',','') m = self.rexx.big_blind_re.finditer(hand.string)
#Must be a better way to do the following tr akqjt AKQJT for a in m:
hand.holecards = hand.holecards.replace('a','A') hand.addBlind(a.group('PNAME'), a.group('BB'))
hand.holecards = hand.holecards.replace('k','K') #hand.posted = hand.posted + [a.group('PNAME')]
hand.holecards = hand.holecards.replace('q','Q')
hand.holecards = hand.holecards.replace('j','J')
hand.holecards = hand.holecards.replace('t','T')
def readAction(self, hand, street): def readHeroCards(self, hand):
m = self.rexx.action_re.finditer(hand.streets.group(street)) m = self.rexx.hero_cards_re.search(hand.string)
hand.actions[street] = [] if(m == None):
for action in m: #Not involved in hand
if action.group('ATYPE') == 'raises' or action.group('ATYPE') == 'calls': hand.involved = False
hand.actions[street] += [[action.group('PNAME'), action.group('ATYPE'), action.group('BET')]] else:
else: hand.hero = m.group('PNAME')
hand.actions[street] += [[action.group('PNAME'), action.group('ATYPE')]] hand.addHoleCards([m.group('HOLE1'), m.group('HOLE2')], m.group('PNAME'))
print "DEBUG: readAction: %s " %(hand.actions)
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 readShowdownActions(self, hand):
for shows in self.rexx.showdown_action_re.finditer(hand.string):
print shows.groups()
re_card = re.compile('(?P<CARD>[0-9tjqka][schd])') # copied from earlier
cards = [card.group('CARD') for card in re_card.finditer(shows.group('CARDS'))]
print cards
hand.addHoleCards(cards, shows.group('PNAME'))
def getRake(self, hand):
hand.rake = hand.totalpot * Decimal('0.05') # probably not quite right
if __name__ == "__main__": if __name__ == "__main__":
c = Configuration.Config() c = Configuration.Config()
e = Everleaf(c, "regression-test-files/everleaf/Speed_Kuala.txt") e = Everleaf(c, "Speed_Kuala.txt")
e.processFile() e.processFile()
print str(e) print str(e)

View File

@ -573,6 +573,27 @@ class FpdbSQLQueries:
elif(self.dbname == 'SQLite'): elif(self.dbname == 'SQLite'):
self.query['createHudCacheTable'] = """ """ self.query['createHudCacheTable'] = """ """
if(self.dbname == 'MySQL InnoDB'):
self.query['addTourneyIndex'] = """ALTER TABLE Tourneys ADD INDEX siteTourneyNo(siteTourneyNo)"""
elif(self.dbname == 'PostgreSQL'):
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'):
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'):
self.query['addPlayersIndex'] = """CREATE INDEX name ON Players (name)"""
elif(self.dbname == 'SQLite'):
self.query['addPlayersIndex'] = """ """
################################ ################################
# Queries used in GuiGraphViewer # Queries used in GuiGraphViewer
################################ ################################
@ -609,6 +630,11 @@ class FpdbSQLQueries:
WHERE Players.name = %s AND HandsPlayers.handId = %s WHERE Players.name = %s AND HandsPlayers.handId = %s
AND Players.siteId = %s AND (tourneysPlayersId IS NULL)""" AND Players.siteId = %s AND (tourneysPlayersId IS NULL)"""
if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL'):
self.query['getPlayerId'] = """SELECT id from Players where name = %s"""
elif(self.dbname == 'SQLite'):
self.query['getPlayerId'] = """SELECT id from Players where name = %s"""
if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL'): if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL'):
self.query['getRingProfitAllHandsPlayerIdSite'] = """ self.query['getRingProfitAllHandsPlayerIdSite'] = """
SELECT hp.handId, hp.winnings, SUM(ha.amount), hp.winnings - SUM(ha.amount) SELECT hp.handId, hp.winnings, SUM(ha.amount), hp.winnings - SUM(ha.amount)
@ -673,13 +699,11 @@ class FpdbSQLQueries:
,round(100*sum(street2Aggr)/sum(street2Seen)) AS TuAFq ,round(100*sum(street2Aggr)/sum(street2Seen)) AS TuAFq
,round(100*sum(street3Aggr)/sum(street3Seen)) AS RvAFq ,round(100*sum(street3Aggr)/sum(street3Seen)) AS RvAFq
,round(100*(sum(street1Aggr)+sum(street2Aggr)+sum(street3Aggr)) ,round(100*(sum(street1Aggr)+sum(street2Aggr)+sum(street3Aggr))
/(sum(street1Seen)+sum(street2Seen)+sum(street3Seen))) AS PFAFq /(sum(street1Seen)+sum(street2Seen)+sum(street3Seen))) AS PFAFq
from Gametypes gt from Gametypes gt
inner join Sites s on s.Id = gt.siteId inner join Sites s on s.Id = gt.siteId
inner join HudCache hc on hc.gameTypeId = gt.Id inner join HudCache hc on hc.gameTypeId = gt.Id
where gt.limittype = 'nl' where hc.playerId in <player_test>
and hc.playerId in (3) # use <player_test> here?
# use <gametype_test> here ? # use <gametype_test> here ?
group by hc.gametypeId group by hc.gametypeId
) stats ) stats
@ -692,7 +716,7 @@ class FpdbSQLQueries:
from HandsPlayers hp from HandsPlayers hp
inner join Hands h ON h.id = hp.handId inner join Hands h ON h.id = hp.handId
inner join HandsActions ha ON ha.handPlayerId = hp.id inner join HandsActions ha ON ha.handPlayerId = hp.id
where hp.playerId in (3) # use <player_test> here? where hp.playerId in <player_test>
# use <gametype_test> here ? # use <gametype_test> here ?
and hp.tourneysPlayersId IS NULL and hp.tourneysPlayersId IS NULL
group by hp.handId, h.gameTypeId, hp.position, hp.winnings group by hp.handId, h.gameTypeId, hp.position, hp.winnings

View File

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

165
pyfpdb/GuiPlayerStats.py Normal file
View File

@ -0,0 +1,165 @@
#!/usr/bin/python
#Copyright 2008 Steffen Jobbagy-Felso
#This program is free software: you can redistribute it and/or modify
#it under the terms of the GNU Affero General Public License as published by
#the Free Software Foundation, version 3 of the License.
#
#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#GNU General Public License for more details.
#
#You should have received a copy of the GNU Affero General Public License
#along with this program. If not, see <http://www.gnu.org/licenses/>.
#In the "official" distribution you can find the license in
#agpl-3.0.txt in the docs folder of the package.
import threading
import pygtk
pygtk.require('2.0')
import gtk
import os
import fpdb_import
import fpdb_db
import FpdbSQLQueries
class GuiPlayerStats (threading.Thread):
def get_vbox(self):
"""returns the vbox of this thread"""
return self.main_hbox
def toggleCallback(self, widget, data=None):
# print "%s was toggled %s" % (data, ("OFF", "ON")[widget.get_active()])
self.activesite = data
print "DEBUG: activesite set to %s" %(self.activesite)
def refreshStats(self, widget, data):
try: self.stats_table.destroy()
except AttributeError: pass
self.fillStatsFrame(self.stats_frame)
def fillStatsFrame(self, vbox):
# Get currently active site and grab playerid
tmp = self.sql.query['playerStats']
result = self.cursor.execute(self.sql.query['getPlayerId'], self.heroes[self.activesite])
result = self.db.cursor.fetchall()
pid = result[0][0]
tmp = tmp.replace("<player_test>", "(" + str(pid) + ")")
self.cursor.execute(tmp)
result = self.db.cursor.fetchall()
cols = 18
rows = len(result)+1 # +1 for title row
self.stats_table = gtk.Table(rows, cols, False)
self.stats_table.set_col_spacings(4)
self.stats_table.show()
vbox.add(self.stats_table)
# Create header row
titles = ("GID", "base", "Style", "Site", "$BB", "Hands", "VPIP", "PFR", "saw_f", "sawsd", "wtsdwsf", "wmsd", "FlAFq", "TuAFq", "RvAFq", "PFAFq", "Net($)", "BB/100")
col = 0
row = 0
for t in titles:
l = gtk.Label(titles[col])
l.show()
self.stats_table.attach(l, col, col+1, row, row+1)
col +=1
for row in range(rows-1):
for col in range(cols):
if(row%2 == 0):
bgcolor = "white"
else:
bgcolor = "lightgrey"
eb = gtk.EventBox()
eb.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(bgcolor))
l = gtk.Label(result[row-1][col])
eb.add(l)
self.stats_table.attach(eb, col, col+1, row+1, row+2)
l.show()
eb.show()
def fillPlayerFrame(self, vbox):
for site in self.conf.supported_sites.keys():
hbox = gtk.HBox(False, 0)
vbox.pack_start(hbox, False, True, 0)
hbox.show()
player = self.conf.supported_sites[site].screen_name
self.createPlayerLine(hbox, site, player)
hbox = gtk.HBox(False, 0)
button = gtk.Button("Refresh")
button.connect("clicked", self.refreshStats, False)
button.show()
hbox.add(button)
vbox.pack_start(hbox, False, True, 0)
hbox.show()
def createPlayerLine(self, hbox, site, player):
if(self.buttongroup == None):
button = gtk.RadioButton(None, site + " id:")
button.set_active(True)
self.buttongroup = button
self.activesite = site
else:
button = gtk.RadioButton(self.buttongroup, site + " id:")
hbox.pack_start(button, True, True, 0)
button.connect("toggled", self.toggleCallback, site)
button.show()
pname = gtk.Entry()
pname.set_text(player)
pname.set_width_chars(20)
hbox.pack_start(pname, False, True, 0)
pname.connect("changed", self.__set_hero_name, site)
#TODO: Look at GtkCompletion - to fill out usernames
pname.show()
self.__set_hero_name(pname, site)
def __set_hero_name(self, w, site):
self.heroes[site] = w.get_text()
print "DEBUG: settings heroes[%s]: %s"%(site, self.heroes[site])
def __init__(self, db, config, querylist, debug=True):
self.debug=debug
self.db=db
self.cursor=db.cursor
self.conf=config
self.sql = querylist
self.activesite = None
self.buttongroup = None
self.heroes = {}
self.stat_table = None
self.stats_frame = None
self.main_hbox = gtk.HBox(False, 0)
self.main_hbox.show()
playerFrame = gtk.Frame("Hero:")
playerFrame.set_label_align(0.0, 0.0)
playerFrame.show()
vbox = gtk.VBox(False, 0)
vbox.show()
self.fillPlayerFrame(vbox)
playerFrame.add(vbox)
statsFrame = gtk.Frame("Stats:")
statsFrame.set_label_align(0.0, 0.0)
statsFrame.show()
self.stats_frame = gtk.VBox(False, 0)
self.stats_frame.show()
self.fillStatsFrame(self.stats_frame)
statsFrame.add(self.stats_frame)
self.main_hbox.pack_start(playerFrame)
self.main_hbox.pack_start(statsFrame)

View File

@ -169,4 +169,3 @@ if __name__== "__main__":
main_window.show_all() main_window.show_all()
gtk.main() gtk.main()

View File

@ -23,254 +23,492 @@ import traceback
import os import os
import os.path import os.path
import xml.dom.minidom import xml.dom.minidom
from decimal import Decimal
import operator
from xml.dom.minidom import Node from xml.dom.minidom import Node
from pokereval import PokerEval
class HandHistoryConverter: class HandHistoryConverter:
def __init__(self, config, file, sitename): eval = PokerEval()
print "HandHistory init called" def __init__(self, config, file, sitename):
self.c = config print "HandHistory init called"
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): self.c = config
tmp = "HandHistoryConverter: '%s'\n" % (self.sitename) self.sitename = sitename
tmp = tmp + "\thhbase: '%s'\n" % (self.hhbase) self.obs = "" # One big string
tmp = tmp + "\thhdir: '%s'\n" % (self.hhdir) self.filetype = "text"
tmp = tmp + "\tfiletype: '%s'\n" % (self.filetype) self.doc = None # For XML based HH files
tmp = tmp + "\tinfile: '%s'\n" % (self.file) 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 + "\toutfile: '%s'\n" % (self.ofile)
# tmp = tmp + "\tgametype: '%s'\n" % (self.gametype[0]) # tmp = tmp + "\tgametype: '%s'\n" % (self.gametype[0])
# tmp = tmp + "\tgamebase: '%s'\n" % (self.gametype[1]) # tmp = tmp + "\tgamebase: '%s'\n" % (self.gametype[1])
# tmp = tmp + "\tlimit: '%s'\n" % (self.gametype[2]) # tmp = tmp + "\tlimit: '%s'\n" % (self.gametype[2])
# tmp = tmp + "\tsb/bb: '%s/%s'\n" % (self.gametype[3], self.gametype[4]) # tmp = tmp + "\tsb/bb: '%s/%s'\n" % (self.gametype[3], self.gametype[4])
return tmp return tmp
def processFile(self): def processFile(self):
if not self.sanityCheck(): if not self.sanityCheck():
print "Cowardly refusing to continue after failed sanity check" print "Cowardly refusing to continue after failed sanity check"
return return
self.readFile(self.file) self.readFile(self.file)
self.gametype = self.determineGameType() self.gametype = self.determineGameType()
self.hands = self.splitFileIntoHands() self.hands = self.splitFileIntoHands()
for hand in self.hands: for hand in self.hands:
self.readHandInfo(hand) self.readHandInfo(hand)
self.readPlayerStacks(hand) self.readPlayerStacks(hand)
self.markStreets(hand) self.markStreets(hand)
self.readBlinds(hand) self.readBlinds(hand)
self.readHeroCards(hand) self.readHeroCards(hand) # want to generalise to draw games
self.readCommunityCards(hand) # read community cards
self.readShowdownActions(hand)
# Read action (Note: no guarantee this is in hand order.
for street in hand.streets.groupdict():
self.readAction(hand, street)
# Read action (Note: no guarantee this is in hand order. # finalise it (total the pot)
for street in hand.streets.groupdict(): hand.totalPot()
self.readAction(hand, street) self.getRake(hand)
if(hand.involved == True): if(hand.involved == True):
self.writeHand("output file", hand) #self.writeHand("output file", hand)
else: hand.printHand()
pass #Don't write out observed hands else:
pass #Don't write out observed hands
# Functions to be implemented in the inheriting class #####
def readSupportedGames(self): abstract # These functions are parse actions that may be overridden by the inheriting class
#
# should return a list def readSupportedGames(self): abstract
# type base limit
# [ ring, hold, nl , sb, bb ]
# Valid types specified in docs/tabledesign.html in Gametypes
def determineGameType(self): abstract
#TODO: Comment # should return a list
def readHandInfo(self, hand): abstract # type base limit
# [ ring, hold, nl , sb, bb ]
# Valid types specified in docs/tabledesign.html in Gametypes
def determineGameType(self): abstract
# Needs to return a list of lists in the format # Read any of:
# [['seat#', 'player1name', 'stacksize'] ['seat#', 'player2name', 'stacksize'] [...]] # HID HandID
def readPlayerStacks(self, hand): abstract # 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 MatchObject with group names identifying the streets into the Hand object # Needs to return a list of lists in the format
def markStreets(self, hand): abstract # [['seat#', 'player1name', 'stacksize'] ['seat#', 'player2name', 'stacksize'] [...]]
def readPlayerStacks(self, hand): abstract
#Needs to return a list in the format # Needs to return a MatchObject with group names identifying the streets into the Hand object
# ['player1name', 'player2name', ...] where player1name is the sb and player2name is bb, # that is, pulls the chunks of preflop, flop, turn and river text into hand.streets MatchObject.
# addtional players are assumed to post a bb oop def markStreets(self, hand): abstract
def readBlinds(self, hand): abstract
def readHeroCards(self, hand): abstract
def readAction(self, hand, street): abstract
def sanityCheck(self): #Needs to return a list in the format
sane = False # ['player1name', 'player2name', ...] where player1name is the sb and player2name is bb,
base_w = False # addtional players are assumed to post a bb oop
#Check if hhbase exists and is writable def readBlinds(self, hand): abstract
#Note: Will not try to create the base HH directory def readHeroCards(self, hand): abstract
if not (os.access(self.hhbase, os.W_OK) and os.path.isdir(self.hhbase)): def readAction(self, hand, street): abstract
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 # 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
# Functions not necessary to implement in sub class def sanityCheck(self):
def setFileType(self, filetype = "text"): sane = True
self.filetype = filetype 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"
def splitFileIntoHands(self): return sane
hands = []
list = self.rexx.split_hand_re.split(self.obs) # Functions not necessary to implement in sub class
list.pop() #Last entry is empty def setFileType(self, filetype = "text"):
for l in list: 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 + "'" # print "'" + l + "'"
hands = hands + [Hand(self.sitename, self.gametype, l)] hands = hands + [Hand(self.sitename, self.gametype, l)]
return hands return hands
def readFile(self, filename): def readFile(self, filename):
"""Read file""" """Read file"""
print "Reading file: '%s'" %(filename) print "Reading file: '%s'" %(filename)
if(self.filetype == "text"): if(self.filetype == "text"):
infile=open(filename, "rU") infile=open(filename, "rU")
self.obs = infile.read() self.obs = infile.read()
infile.close() infile.close()
elif(self.filetype == "xml"): elif(self.filetype == "xml"):
try: try:
doc = xml.dom.minidom.parse(filename) doc = xml.dom.minidom.parse(filename)
self.doc = doc self.doc = doc
except: except:
traceback.print_exc(file=sys.stderr) traceback.print_exc(file=sys.stderr)
def writeHand(self, file, hand): def writeHand(self, file, hand):
"""Write out parsed data""" """Write out parsed data"""
print "DEBUG: *************************" print "DEBUG: *************************"
print "DEBUG: Start of print hand" print "DEBUG: Start of print hand"
print "DEBUG: *************************" print "DEBUG: *************************"
print "%s Game #%s: %s ($%s/$%s) - %s" %(hand.sitename, hand.handid, "XXXXhand.gametype", hand.sb, hand.bb, hand.starttime) 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) print "Table '%s' %d-max Seat #%s is the button" %(hand.tablename, hand.maxseats, hand.buttonpos)
for player in hand.players: for player in hand.players:
print "Seat %s: %s ($%s)" %(player[0], player[1], player[2]) print "Seat %s: %s ($%s)" %(player[0], player[1], player[2])
if(hand.posted[0] == "FpdbNBP"): if(hand.posted[0] == "FpdbNBP"):
print "No small blind posted" print "No small blind posted"
else: else:
print "%s: posts small blind $%s" %(hand.posted[0], hand.sb) print "%s: posts small blind $%s" %(hand.posted[0], hand.sb)
#May be more than 1 bb posting #May be more than 1 bb posting
print "%s: posts big blind $%s" %(hand.posted[1], hand.bb) print "%s: posts big blind $%s" %(hand.posted[1], hand.bb)
if(len(hand.posted) > 2): if(len(hand.posted) > 2):
# Need to loop on all remaining big blinds - lazy # Need to loop on all remaining big blinds - lazy
print "XXXXXXXXX FIXME XXXXXXXX" print "XXXXXXXXX FIXME XXXXXXXX"
print "*** HOLE CARDS ***" print "*** HOLE CARDS ***"
print "Dealt to %s [%s]" %(hand.hero ,hand.holecards) 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']: for act in hand.actions['PREFLOP']:
self.printActionLine(act, 0) self.printActionLine(act, 0)
if 'FLOP' in hand.actions: if 'PREFLOP' in hand.actions:
print "*** FLOP *** [%s]" %("XXXXX Flop cards XXXXXX") for act in hand.actions['PREFLOP']:
for act in hand.actions['FLOP']: print "PF action"
self.printActionLine(act, 0)
if 'TURN' in hand.actions: if 'FLOP' in hand.actions:
print "*** TURN *** [%s] [%s]" %("XXXXX Flop cards XXXXXX", "XXXXX Turn Card XXXXX") print "*** FLOP *** [%s %s %s]" %(hand.streets.group("FLOP1"), hand.streets.group("FLOP2"), hand.streets.group("FLOP3"))
for act in hand.actions['TURN']: for act in hand.actions['FLOP']:
self.printActionLine(act, 0) self.printActionLine(act, 0)
if 'RIVER' in hand.actions: if 'TURN' in hand.actions:
print "*** RIVER *** [%s %s] [%s]" %("XXXXX Flop cards XXXXXX", "XXXXX Turn Card XXXXX", "XXXXX River Card XXXXX") 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['RIVER']: for act in hand.actions['TURN']:
self.printActionLine(act, 0) self.printActionLine(act, 0)
print "*** SUMMARY ***" if 'RIVER' in hand.actions:
print "XXXXXXXXXXXX Need sumary info XXXXXXXXXXX" 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"))
# print "Total pot $%s | Rake $%s)" %(hand.totalpot $" + hand.rake) for act in hand.actions['RIVER']:
# print "Board [" + boardcards + "]" self.printActionLine(act, 0)
#
# SUMMARY STUFF print "*** SUMMARY ***"
print "XXXXXXXXXXXX Need sumary info XXXXXXXXXXX"
def printActionLine(self, act, pot): def printActionLine(self, act, pot):
if act[1] == 'folds' or act[1] == 'checks': if act[1] == 'folds' or act[1] == 'checks':
print "%s: %s" %(act[0], act[1]) print "%s: %s " %(act[0], act[1])
if act[1] == 'calls': if act[1] == 'calls':
print "%s: %s $%s" %(act[0], act[1], act[2]) print "%s: %s $%s" %(act[0], act[1], act[2])
if act[1] == 'raises': if act[1] == 'raises':
print "%s: %s $%s to XXXpottotalXXX" %(act[0], act[1], act[2]) 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 #takes a poker float (including , for thousand seperator and converts it to an int
def float2int (self, string): def float2int (self, string):
pos=string.find(",") pos=string.find(",")
if (pos!=-1): #remove , the thousand seperator if (pos!=-1): #remove , the thousand seperator
string=string[0:pos]+string[pos+1:] string=string[0:pos]+string[pos+1:]
pos=string.find(".") pos=string.find(".")
if (pos!=-1): #remove decimal point if (pos!=-1): #remove decimal point
string=string[0:pos]+string[pos+1:] string=string[0:pos]+string[pos+1:]
result = int(string) result = int(string)
if pos==-1: #no decimal point - was in full dollars - need to multiply with 100 if pos==-1: #no decimal point - was in full dollars - need to multiply with 100
result*=100 result*=100
return result return result
#end def float2int #end def float2int
class Hand: class Hand:
# def __init__(self, sitename, gametype, sb, bb, string): # def __init__(self, sitename, gametype, sb, bb, string):
UPS = {'a':'A', 't':'T', 'j':'J', 'q':'Q', 'k':'K'}
def __init__(self, sitename, gametype, string): def __init__(self, sitename, gametype, string):
self.sitename = sitename self.sitename = sitename
self.gametype = gametype self.gametype = gametype
self.string = string self.string = string
self.streets = None # A MatchObject using a groupnames to identify streets.
self.streetList = ['BLINDS','PREFLOP','FLOP','TURN','RIVER'] # a list of the observed street names in order
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 = {} # dict from player names to lists of hole cards
self.board = {} # dict from street names to community cards
self.action = []
self.totalpot = None
self.rake = None
self.bets = {}
self.lastBet = {}
for street in self.streetList:
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.holecards[name] = []
#self.startChips[name] = chips
#self.endChips[name] = chips
#self.winners[name] = 0
for street in self.streetList:
self.bets[street][name] = []
def addHoleCards(self, cards, player=None): # generalise to add hole cards for a specific seat or player
for c in cards:
self.holecards[player].append(self.card(c))
def discardHoleCards(self, cards, player=None):
if seat is None:
#raise something
pass
for card in cards:
try:
self.holecards[player].remove(card)
except ValueError:
print "tried to discard a card player apparently didn't have"
def setCommunityCards(self, street, cards):
self.board[street] = [self.card(c) for c in cards]
print self.board[street]
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 addFold(self, street, player=None):
## Called when a player folds.
#self.bets[street][player].append(None)
#def addCheck(self, street, player=None):
#self.bets[street][player].append(0)
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 all bets and blinds have been added, totals up the total pot size
Known bug: doesn't take into account side pots"""
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.streetList:
#print street, self.bets[street][player]
self.totalpot += reduce(operator.add, self.bets[street][player], 0)
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 = 0
self.rake = 0
def printHand(self): def printHand(self):
print self.sitename # PokerStars format.
print self.gametype print "### DEBUG ###"
print self.string print "%s Game #%s: %s ($%s/$%s) - %s" %(self.sitename, self.handid, "XXXXhand.gametype", self.sb, self.bb, self.starttime)
print self.handid print "Table '%s' %d-max Seat #%s is the button" %(self.tablename, self.maxseats, self.buttonpos)
print self.sb for player in self.players:
print self.bb print "Seat %s: %s ($%s)" %(player[0], player[1], player[2])
print self.tablename
print self.maxseats if(self.posted[0] is None):
print self.counted_seats print "No small blind posted"
print self.buttonpos else:
print self.seating print "%s: posts small blind $%s" %(self.posted[0], self.sb)
print self.players
print self.posted #May be more than 1 bb posting
print self.action for a in self.posted[1:]:
print self.involved print "%s: posts big blind $%s" %(self.posted[1], self.bb)
print self.hero
# What about big & small blinds?
print "*** HOLE CARDS ***"
print "Dealt to %s [%s %s]" %(self.hero , self.holecards[self.hero][0], self.holecards[self.hero][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"))
#print self.board
for player in self.players:
if self.holecards[player[1]]: # empty list default is false
hole = self.holecards[player[1]]
#print self.board.values()
board = []
for s in self.board.values():
board += s
playerhand = self.bestHand('hi', board+hole)
print "Seat %d: %s showed %s and won/lost with %s" % (player[0], player[1], hole, playerhand)
else:
print "Seat %d: %s mucked or folded" % (player[0], player[1])
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])
# going to use pokereval to figure out hands
# these functions are copied from pokergame.py
# im thinking perhaps its best to use all the functionality of pokergame instead
# of reinventing the wheel
def bestHand(self, side, cards):
#if self.variant == "omaha" or self.variant == "omaha8":
#hand = self.serial2player[serial].hand.tolist(True)
#board = self.board.tolist(True)
#else:
#hand = hand.tolist(True) + board.tolist(True)
#board = []
print cards
return HandHistoryConverter.eval.best('hi', cards, [])
def bestHandValue(self, side, serial):
(value, cards) = self.bestHand(side, serial)
return value
def readableHandValueLong(self, side, value, cards):
cards = self.eval.card2string(cards)
if value == "NoPair":
if side == "low":
if cards[0][0] == '5':
return _("The wheel")
else:
return join(map(lambda card: card[0], cards), ", ")
else:
return _("High card %(card)s") % { 'card' : _(letter2name[cards[0][0]]) }
elif value == "OnePair":
return _("A pair of %(card)s") % { 'card' : _(letter2names[cards[0][0]]) } + _(", %(card)s kicker") % { 'card' : _(letter2name[cards[2][0]]) }
elif value == "TwoPair":
return _("Two pairs %(card1)s and %(card2)s") % { 'card1' : _(letter2names[cards[0][0]]), 'card2' : _(letter2names[cards[2][0]]) } + _(", %(card)s kicker") % { 'card' : _(letter2name[cards[4][0]]) }
elif value == "Trips":
return _("Three of a kind %(card)s") % { 'card' : _(letter2names[cards[0][0]]) } + _(", %(card)s kicker") % { 'card' : _(letter2name[cards[3][0]]) }
elif value == "Straight":
return _("Straight %(card1)s to %(card2)s") % { 'card1' : _(letter2name[cards[0][0]]), 'card2' : _(letter2name[cards[4][0]]) }
elif value == "Flush":
return _("Flush %(card)s high") % { 'card' : _(letter2name[cards[0][0]]) }
elif value == "FlHouse":
return _("%(card1)ss full of %(card2)ss") % { 'card1' : _(letter2name[cards[0][0]]), 'card2' : _(letter2name[cards[3][0]]) }
elif value == "Quads":
return _("Four of a kind %(card)s") % { 'card' : _(letter2names[cards[0][0]]) } + _(", %(card)s kicker") % { 'card' : _(letter2name[cards[4][0]]) }
elif value == "StFlush":
if letter2name[cards[0][0]] == 'Ace':
return _("Royal flush")
else:
return _("Straight flush %(card)s high") % { 'card' : _(letter2name[cards[0][0]]) }
return value

3
pyfpdb/Mucked.py Normal file → Executable file
View File

@ -170,6 +170,7 @@ class Stud_cards:
for r in range(0, self.rows): for r in range(0, self.rows):
self.grid_contents[( 0, r)] = gtk.Label("%d" % (r + 1)) self.grid_contents[( 0, r)] = gtk.Label("%d" % (r + 1))
self.grid_contents[( 1, r)] = gtk.Label("player %d" % (r + 1)) self.grid_contents[( 1, r)] = gtk.Label("player %d" % (r + 1))
self.grid_contents[( 1, r)].set_property("width-chars", 12)
self.grid_contents[( 4, r)] = gtk.Label("-") self.grid_contents[( 4, r)] = gtk.Label("-")
self.grid_contents[( 9, r)] = gtk.Label("-") self.grid_contents[( 9, r)] = gtk.Label("-")
self.grid_contents[( 2, r)] = self.eb[( 0, r)] self.grid_contents[( 2, r)] = self.eb[( 0, r)]
@ -289,7 +290,7 @@ class Stud_cards:
def clear(self): def clear(self):
for r in range(0, self.rows): for r in range(0, self.rows):
self.grid_contents[(1, r)].set_text(" ") self.grid_contents[(1, r)].set_text(" ")
for c in range(0, 7): for c in range(0, 7):
self.seen_cards[(c, r)].set_from_pixbuf(self.card_images[('B', 'S')]) self.seen_cards[(c, r)].set_from_pixbuf(self.card_images[('B', 'S')])
self.eb[(c, r)].set_tooltip_text('') self.eb[(c, r)].set_tooltip_text('')

View File

@ -35,72 +35,72 @@ import unittest
class TestSequenceFunctions(unittest.TestCase): class TestSequenceFunctions(unittest.TestCase):
def setUp(self): def setUp(self):
"""Configure MySQL settings/database and establish connection""" """Configure MySQL settings/database and establish connection"""
self.c = Configuration.Config() self.c = Configuration.Config()
self.mysql_settings={ 'db-host':"localhost", self.mysql_settings={ 'db-host':"localhost",
'db-backend':2, 'db-backend':2,
'db-databaseName':"fpdbtest", 'db-databaseName':"fpdbtest",
'db-user':"fpdb", 'db-user':"fpdb",
'db-password':"fpdb"} 'db-password':"fpdb"}
self.mysql_db = fpdb_db.fpdb_db() self.mysql_db = fpdb_db.fpdb_db()
self.mysql_db.connect(self.mysql_settings['db-backend'], self.mysql_settings['db-host'], self.mysql_db.connect(self.mysql_settings['db-backend'], self.mysql_settings['db-host'],
self.mysql_settings['db-databaseName'], self.mysql_settings['db-user'], self.mysql_settings['db-databaseName'], self.mysql_settings['db-user'],
self.mysql_settings['db-password']) self.mysql_settings['db-password'])
self.mysqldict = FpdbSQLQueries.FpdbSQLQueries('MySQL InnoDB') self.mysqldict = FpdbSQLQueries.FpdbSQLQueries('MySQL InnoDB')
self.mysqlimporter = fpdb_import.Importer(self, self.mysql_settings, self.c) self.mysqlimporter = fpdb_import.Importer(self, self.mysql_settings, self.c)
self.mysqlimporter.setCallHud(False) self.mysqlimporter.setCallHud(False)
# """Configure Postgres settings/database and establish connection""" # """Configure Postgres settings/database and establish connection"""
# self.pg_settings={ 'db-host':"localhost", 'db-backend':3, 'db-databaseName':"fpdbtest", 'db-user':"fpdb", 'db-password':"fpdb"} # self.pg_settings={ 'db-host':"localhost", 'db-backend':3, 'db-databaseName':"fpdbtest", 'db-user':"fpdb", 'db-password':"fpdb"}
# self.pg_db = fpdb_db.fpdb_db() # self.pg_db = fpdb_db.fpdb_db()
# self.pg_db.connect(self.pg_settings['db-backend'], self.pg_settings['db-host'], # self.pg_db.connect(self.pg_settings['db-backend'], self.pg_settings['db-host'],
# self.pg_settings['db-databaseName'], self.pg_settings['db-user'], # self.pg_settings['db-databaseName'], self.pg_settings['db-user'],
# self.pg_settings['db-password']) # self.pg_settings['db-password'])
# self.pgdict = FpdbSQLQueries.FpdbSQLQueries('PostgreSQL') # self.pgdict = FpdbSQLQueries.FpdbSQLQueries('PostgreSQL')
def testDatabaseConnection(self): def testDatabaseConnection(self):
"""Test all supported DBs""" """Test all supported DBs"""
self.result = self.mysql_db.cursor.execute(self.mysqldict.query['list_tables']) self.result = self.mysql_db.cursor.execute(self.mysqldict.query['list_tables'])
self.failUnless(self.result==13, "Number of tables in database incorrect. Expected 13 got " + str(self.result)) self.failUnless(self.result==13, "Number of tables in database incorrect. Expected 13 got " + str(self.result))
# self.result = self.pg_db.cursor.execute(self.pgdict.query['list_tables']) # self.result = self.pg_db.cursor.execute(self.pgdict.query['list_tables'])
# self.failUnless(self.result==13, "Number of tables in database incorrect. Expected 13 got " + str(self.result)) # self.failUnless(self.result==13, "Number of tables in database incorrect. Expected 13 got " + str(self.result))
def testMySQLRecreateTables(self): def testMySQLRecreateTables(self):
"""Test droping then recreating fpdb table schema""" """Test droping then recreating fpdb table schema"""
self.mysql_db.recreate_tables() self.mysql_db.recreate_tables()
self.result = self.mysql_db.cursor.execute("SHOW TABLES") self.result = self.mysql_db.cursor.execute("SHOW TABLES")
self.failUnless(self.result==13, "Number of tables in database incorrect. Expected 13 got " + str(self.result)) self.failUnless(self.result==13, "Number of tables in database incorrect. Expected 13 got " + str(self.result))
def testPokerStarsHHDate(self): 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]" 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)" 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" 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") result = fpdb_simple.parseHandStartTime(older1, "ps")
self.failUnless(result==datetime.datetime(2008,9,7,11,23,14), self.failUnless(result==datetime.datetime(2008,9,7,11,23,14),
"Date incorrect, expected: 2008-09-07 11:23:14 got: " + str(result)) "Date incorrect, expected: 2008-09-07 11:23:14 got: " + str(result))
result = fpdb_simple.parseHandStartTime(latest, "ps") result = fpdb_simple.parseHandStartTime(latest, "ps")
self.failUnless(result==datetime.datetime(2008,11,12,15,00,48), self.failUnless(result==datetime.datetime(2008,11,12,15,00,48),
"Date incorrect, expected: 2008-11-12 15:00:48 got: " + str(result)) "Date incorrect, expected: 2008-11-12 15:00:48 got: " + str(result))
result = fpdb_simple.parseHandStartTime(previous, "ps") result = fpdb_simple.parseHandStartTime(previous, "ps")
self.failUnless(result==datetime.datetime(2008,8,17,6,14,43), self.failUnless(result==datetime.datetime(2008,8,17,6,14,43),
"Date incorrect, expected: 2008-08-17 01:14:43 got: " + str(result)) "Date incorrect, expected: 2008-08-17 01:14:43 got: " + str(result))
def testImportHandHistoryFiles(self): def testImportHandHistoryFiles(self):
"""Test import of single HH file""" """Test import of single HH file"""
self.mysqlimporter.addImportFile("regression-test-files/hand-histories/ps-lhe-ring-3hands.txt") self.mysqlimporter.addImportFile("regression-test-files/hand-histories/ps-lhe-ring-3hands.txt")
self.mysqlimporter.runImport() self.mysqlimporter.runImport()
self.mysqlimporter.addImportDirectory("regression-test-files/hand-histories") self.mysqlimporter.addImportDirectory("regression-test-files/hand-histories")
self.mysqlimporter.runImport() self.mysqlimporter.runImport()
# def testPostgresSQLRecreateTables(self): # def testPostgresSQLRecreateTables(self):
# """Test droping then recreating fpdb table schema""" # """Test droping then recreating fpdb table schema"""
# self.pg_db.recreate_tables() # self.pg_db.recreate_tables()
# self.result = self.pg_db.cursor.execute(self.pgdict.query['list_tables']) # self.result = self.pg_db.cursor.execute(self.pgdict.query['list_tables'])
# self.failUnless(self.result==13, "Number of tables in database incorrect. Expected 13 got " + str(self.result)) # self.failUnless(self.result==13, "Number of tables in database incorrect. Expected 13 got " + str(self.result))
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -229,12 +229,11 @@ class Sql:
sum(street3CheckCallRaiseDone) AS ccr_3, sum(street3CheckCallRaiseDone) AS ccr_3,
sum(street4CheckCallRaiseChance) AS ccr_opp_4, sum(street4CheckCallRaiseChance) AS ccr_opp_4,
sum(street4CheckCallRaiseDone) AS ccr_4 sum(street4CheckCallRaiseDone) AS ccr_4
FROM HudCache, Hands FROM Hands
WHERE HudCache.PlayerId in INNER JOIN HandsPlayers ON (HandsPlayers.handId = %s)
(SELECT PlayerId FROM HandsPlayers INNER JOIN HudCache ON ( HudCache.PlayerId = HandsPlayers.PlayerId+0
WHERE handId = %s) AND HudCache.gametypeId+0 = Hands.gametypeId+0)
AND Hands.id = %s WHERE Hands.id = %s
AND Hands.gametypeId = HudCache.gametypeId
GROUP BY HudCache.PlayerId GROUP BY HudCache.PlayerId
""" """

View File

@ -71,6 +71,7 @@ def do_stat(stat_dict, player = 24, stat = 'vpip'):
# functions that return individual stats # functions that return individual stats
def playername(stat_dict, player): def playername(stat_dict, player):
""" Player Name."""
return (stat_dict[player]['screen_name'], return (stat_dict[player]['screen_name'],
stat_dict[player]['screen_name'], stat_dict[player]['screen_name'],
stat_dict[player]['screen_name'], stat_dict[player]['screen_name'],
@ -98,6 +99,26 @@ def vpip(stat_dict, player):
'Voluntarily Put In Pot %' 'Voluntarily Put In Pot %'
) )
def vpip_0(stat_dict, player):
""" Voluntarily put $ in the pot (no decimals)."""
stat = 0.0
try:
stat = float(stat_dict[player]['vpip'])/float(stat_dict[player]['n'])
return (stat,
'%2.0f' % (100*stat) + '%',
'v=%2.0f' % (100*stat) + '%',
'vpip=%2.0f' % (100*stat) + '%',
'(%d/%d)' % (stat_dict[player]['vpip'], stat_dict[player]['n']),
'vpip'
)
except: return (stat,
'%2.0f' % (0) + '%',
'w=%2.0f' % (0) + '%',
'wtsd=%2.0f' % (0) + '%',
'(%d/%d)' % (0, 0),
'wtsd'
)
def pfr(stat_dict, player): def pfr(stat_dict, player):
""" Preflop (3rd street) raise.""" """ Preflop (3rd street) raise."""
stat = 0.0 stat = 0.0
@ -119,6 +140,27 @@ def pfr(stat_dict, player):
'Pre-Flop Raise %' 'Pre-Flop Raise %'
) )
def pfr_0(stat_dict, player):
""" Preflop (3rd street) raise (no decimals)."""
stat = 0.0
try:
stat = float(stat_dict[player]['pfr'])/float(stat_dict[player]['n'])
return (stat,
'%2.0f' % (100*stat) + '%',
'p=%2.0f' % (100*stat) + '%',
'pfr=%2.0f' % (100*stat) + '%',
'(%d/%d)' % (stat_dict[player]['pfr'], stat_dict[player]['n']),
'pfr'
)
except:
return (stat,
'%2.0f' % (0) + '%',
'p=%2.0f' % (0) + '%',
'pfr=%2.0f' % (0) + '%',
'(%d/%d)' % (0, 0),
'pfr'
)
def wtsd(stat_dict, player): def wtsd(stat_dict, player):
""" Went to SD when saw flop/4th.""" """ Went to SD when saw flop/4th."""
stat = 0.0 stat = 0.0
@ -149,7 +191,7 @@ def wmsd(stat_dict, player):
'%3.1f' % (100*stat) + '%', '%3.1f' % (100*stat) + '%',
'w=%3.1f' % (100*stat) + '%', 'w=%3.1f' % (100*stat) + '%',
'wmsd=%3.1f' % (100*stat) + '%', 'wmsd=%3.1f' % (100*stat) + '%',
'(%f5.0/%d)' % (stat_dict[player]['wmsd'], stat_dict[player]['sd']), '(%5.1f/%d)' % (float(stat_dict[player]['wmsd']), stat_dict[player]['sd']),
'% won money at showdown' '% won money at showdown'
) )
except: except:
@ -413,6 +455,61 @@ def a_freq_4(stat_dict, player):
'Aggression Freq 7th' 'Aggression Freq 7th'
) )
def a_freq_123(stat_dict, player):
""" Post-Flop aggression frequency."""
stat = 0.0
try:
stat = float( stat_dict[player]['aggr_1'] + stat_dict[player]['aggr_2'] + stat_dict[player]['aggr_3']
) / float( stat_dict[player]['saw_1'] + stat_dict[player]['saw_2'] + stat_dict[player]['saw_3']);
return (stat,
'%3.1f' % (100*stat) + '%',
'afq=%3.1f' % (100*stat) + '%',
'postf_aggfq=%3.1f' % (100*stat) + '%',
'(%d/%d)' % ( stat_dict[player]['aggr_1']
+ stat_dict[player]['aggr_2']
+ stat_dict[player]['aggr_3']
, stat_dict[player]['saw_1']
+ stat_dict[player]['saw_2']
+ stat_dict[player]['saw_3']
),
'Post-Flop Aggression Freq'
)
except:
return (stat,
'%2.0f' % (0) + '%',
'a3=%2.0f' % (0) + '%',
'a_fq_3=%2.0f' % (0) + '%',
'(%d/%d)' % (0, 0),
'Post-Flop Aggression Freq'
)
def a_freq_123_0(stat_dict, player):
""" Post-Flop aggression frequency (no decimals)."""
stat = 0.0
try:
stat = float( stat_dict[player]['aggr_1'] + stat_dict[player]['aggr_2'] + stat_dict[player]['aggr_3']) / float( stat_dict[player]['saw_1'] + stat_dict[player]['saw_2'] + stat_dict[player]['saw_3']);
return (stat,
'%2.0f' % (100*stat) + '%',
'afq=%2.0f' % (100*stat) + '%',
'postf_aggfq=%2.0f' % (100*stat) + '%',
'(%d/%d)' % ( stat_dict[player]['aggr_1']
+ stat_dict[player]['aggr_2']
+ stat_dict[player]['aggr_3']
, stat_dict[player]['saw_1']
+ stat_dict[player]['saw_2']
+ stat_dict[player]['saw_3']
),
'Post-Flop Aggression Freq'
)
except:
return (stat,
'%2.0f' % (0) + '%',
'a3=%2.0f' % (0) + '%',
'a_fq_3=%2.0f' % (0) + '%',
'(%d/%d)' % (0, 0),
'Post-Flop Aggression Freq'
)
def cb_1(stat_dict, player): def cb_1(stat_dict, player):
""" Flop continuation bet.""" """ Flop continuation bet."""
stat = 0.0 stat = 0.0
@ -589,7 +686,9 @@ if __name__== "__main__":
for player in stat_dict.keys(): for player in stat_dict.keys():
print "player = ", player, do_stat(stat_dict, player = player, stat = 'vpip') print "player = ", player, do_stat(stat_dict, player = player, stat = 'vpip')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'vpip_0')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'pfr') print "player = ", player, do_stat(stat_dict, player = player, stat = 'pfr')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'pfr_0')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'wtsd') print "player = ", player, do_stat(stat_dict, player = player, stat = 'wtsd')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'saw_f') print "player = ", player, do_stat(stat_dict, player = player, stat = 'saw_f')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'n') print "player = ", player, do_stat(stat_dict, player = player, stat = 'n')
@ -604,6 +703,8 @@ if __name__== "__main__":
print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_2') print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_2')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_3') print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_3')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_4') print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_4')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_123')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_123_0')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb_1') print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb_1')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb_2') print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb_2')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb_3') print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb_3')
@ -612,6 +713,7 @@ if __name__== "__main__":
print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq_2') print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq_2')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq_3') print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq_3')
print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq_4') print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq_4')
print "\n"
print "\n\nLegal stats:" print "\n\nLegal stats:"
for attr in dir(): for attr in dir():
@ -619,8 +721,8 @@ if __name__== "__main__":
if attr in ("Configuration", "Database", "GInitiallyUnowned", "gtk", "pygtk", if attr in ("Configuration", "Database", "GInitiallyUnowned", "gtk", "pygtk",
"player", "c", "db_connection", "do_stat", "do_tip", "stat_dict", "player", "c", "db_connection", "do_stat", "do_tip", "stat_dict",
"h"): continue "h"): continue
print attr, eval("%s.__doc__" % (attr)) print "%-14s %s" % (attr, eval("%s.__doc__" % (attr)))
# print " <pu_stat pu_stat_name = \"%s\"> </pu_stat>" % (attr) # print " <pu_stat pu_stat_name = \"%s\"> </pu_stat>" % (attr)
db_connection.close db_connection.close_connection

View File

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

276
pyfpdb/fpdb_db.py Executable file → Normal file
View File

@ -21,153 +21,171 @@ import fpdb_simple
import FpdbSQLQueries import FpdbSQLQueries
class fpdb_db: class fpdb_db:
def __init__(self): def __init__(self):
"""Simple constructor, doesnt really do anything""" """Simple constructor, doesnt really do anything"""
self.db=None self.db=None
self.cursor=None self.cursor=None
self.sql = {} self.sql = {}
self.MYSQL_INNODB=2 self.MYSQL_INNODB=2
self.PGSQL=3 self.PGSQL=3
self.SQLITE=4 self.SQLITE=4
#end def __init__ #end def __init__
def connect(self, backend, host, database, user, password): def connect(self, backend=None, host=None, database=None,
"""Connects a database with the given parameters""" user=None, password=None):
self.backend=backend """Connects a database with the given parameters"""
self.host=host if backend is None:
self.database=database raise FpdbError('Database backend not defined')
self.user=user self.backend=backend
self.password=password self.host=host
if backend==self.MYSQL_INNODB: self.user=user
import MySQLdb self.password=password
self.db=MySQLdb.connect(host = host, user = user, passwd = password, db = database) self.database=database
elif backend==self.PGSQL: if backend==self.MYSQL_INNODB:
import psycopg2 import MySQLdb
self.db = psycopg2.connect(host = host, user = user, password = password, database = database) self.db=MySQLdb.connect(host = host, user = user, passwd = password, db = database)
else: elif backend==self.PGSQL:
raise fpdb_simple.FpdbError("unrecognised database backend:"+backend) import psycopg2
self.cursor=self.db.cursor() # If DB connection is made over TCP, then the variables
self.cursor.execute('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED') # host, user and password are required
# Set up query dictionary as early in the connection process as we can. print "host=%s user=%s pass=%s." % (host, user, password)
self.sql = FpdbSQLQueries.FpdbSQLQueries(self.get_backend_name()) if self.host and self.user and self.password:
self.wrongDbVersion=False self.db = psycopg2.connect(host = host,
try: user = user,
self.cursor.execute("SELECT * FROM Settings") password = password,
settings=self.cursor.fetchone() database = database)
if settings[0]!=118: # For local domain-socket connections, only DB name is
print "outdated or too new database version - please recreate tables" # needed, and everything else is in fact undefined and/or
self.wrongDbVersion=True # flat out wrong
except:# _mysql_exceptions.ProgrammingError: else:
print "failed to read settings table - please recreate tables" self.db = psycopg2.connect(database = database)
self.wrongDbVersion=True else:
#end def connect 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
try:
self.cursor.execute("SELECT * FROM Settings")
settings=self.cursor.fetchone()
if settings[0]!=118:
print "outdated or too new database version - please recreate tables"
self.wrongDbVersion=True
except:# _mysql_exceptions.ProgrammingError:
print "failed to read settings table - please recreate tables"
self.wrongDbVersion=True
#end def connect
def disconnect(self, due_to_error=False): def disconnect(self, due_to_error=False):
"""Disconnects the DB""" """Disconnects the DB"""
if due_to_error: if due_to_error:
self.db.rollback() self.db.rollback()
else: else:
self.db.commit() self.db.commit()
self.cursor.close() self.cursor.close()
self.db.close() self.db.close()
#end def disconnect #end def disconnect
def reconnect(self, due_to_error=False): def reconnect(self, due_to_error=False):
"""Reconnects the DB""" """Reconnects the DB"""
#print "started fpdb_db.reconnect" #print "started fpdb_db.reconnect"
self.disconnect(due_to_error) self.disconnect(due_to_error)
self.connect(self.backend, self.host, self.database, self.user, self.password) self.connect(self.backend, self.host, self.database, self.user, self.password)
def create_tables(self): def create_tables(self):
#todo: should detect and fail gracefully if tables already exist. #todo: should detect and fail gracefully if tables already exist.
self.cursor.execute(self.sql.query['createSettingsTable']) self.cursor.execute(self.sql.query['createSettingsTable'])
self.cursor.execute(self.sql.query['createSitesTable']) self.cursor.execute(self.sql.query['createSitesTable'])
self.cursor.execute(self.sql.query['createGametypesTable']) self.cursor.execute(self.sql.query['createGametypesTable'])
self.cursor.execute(self.sql.query['createPlayersTable']) self.cursor.execute(self.sql.query['createPlayersTable'])
self.cursor.execute(self.sql.query['createAutoratesTable']) self.cursor.execute(self.sql.query['createAutoratesTable'])
self.cursor.execute(self.sql.query['createHandsTable']) self.cursor.execute(self.sql.query['createHandsTable'])
self.cursor.execute(self.sql.query['createBoardCardsTable']) self.cursor.execute(self.sql.query['createBoardCardsTable'])
self.cursor.execute(self.sql.query['createTourneyTypesTable']) self.cursor.execute(self.sql.query['createTourneyTypesTable'])
self.cursor.execute(self.sql.query['createTourneysTable']) self.cursor.execute(self.sql.query['createTourneysTable'])
self.cursor.execute(self.sql.query['createTourneysPlayersTable']) self.cursor.execute(self.sql.query['createTourneysPlayersTable'])
self.cursor.execute(self.sql.query['createHandsPlayersTable']) self.cursor.execute(self.sql.query['createHandsPlayersTable'])
self.cursor.execute(self.sql.query['createHandsActionsTable']) self.cursor.execute(self.sql.query['createHandsActionsTable'])
self.cursor.execute(self.sql.query['createHudCacheTable']) self.cursor.execute(self.sql.query['createHudCacheTable'])
self.fillDefaultData() self.cursor.execute(self.sql.query['addTourneyIndex'])
self.db.commit() self.cursor.execute(self.sql.query['addHandsIndex'])
self.cursor.execute(self.sql.query['addPlayersIndex'])
self.fillDefaultData()
self.db.commit()
#end def disconnect #end def disconnect
def drop_tables(self): def drop_tables(self):
"""Drops the fpdb tables from the current db""" """Drops the fpdb tables from the current db"""
if(self.get_backend_name() == 'MySQL InnoDB'): if(self.get_backend_name() == 'MySQL InnoDB'):
#Databases with FOREIGN KEY support need this switched of before you can drop tables #Databases with FOREIGN KEY support need this switched of before you can drop tables
self.drop_referencial_integrity() self.drop_referencial_integrity()
# Query the DB to see what tables exist # Query the DB to see what tables exist
self.cursor.execute(self.sql.query['list_tables']) self.cursor.execute(self.sql.query['list_tables'])
for table in self.cursor: for table in self.cursor:
self.cursor.execute(self.sql.query['drop_table'] + table[0]) self.cursor.execute(self.sql.query['drop_table'] + table[0])
elif(self.get_backend_name() == 'PostgreSQL'): elif(self.get_backend_name() == 'PostgreSQL'):
self.db.commit()# I have no idea why this makes the query work--REB 07OCT2008 self.db.commit()# I have no idea why this makes the query work--REB 07OCT2008
self.cursor.execute(self.sql.query['list_tables']) self.cursor.execute(self.sql.query['list_tables'])
tables = self.cursor.fetchall() tables = self.cursor.fetchall()
for table in tables: for table in tables:
self.cursor.execute(self.sql.query['drop_table'] + table[0] + ' cascade') self.cursor.execute(self.sql.query['drop_table'] + table[0] + ' cascade')
elif(self.get_backend_name() == 'SQLite'): elif(self.get_backend_name() == 'SQLite'):
#todo: sqlite version here #todo: sqlite version here
print "Empty function here" print "Empty function here"
self.db.commit() self.db.commit()
#end def drop_tables #end def drop_tables
def drop_referencial_integrity(self): def drop_referencial_integrity(self):
"""Update all tables to remove foreign keys""" """Update all tables to remove foreign keys"""
self.cursor.execute(self.sql.query['list_tables']) self.cursor.execute(self.sql.query['list_tables'])
result = self.cursor.fetchall() result = self.cursor.fetchall()
for i in range(len(result)): for i in range(len(result)):
self.cursor.execute("SHOW CREATE TABLE " + result[i][0]) self.cursor.execute("SHOW CREATE TABLE " + result[i][0])
inner = self.cursor.fetchall() inner = self.cursor.fetchall()
for j in range(len(inner)): for j in range(len(inner)):
# result[i][0] - Table name # result[i][0] - Table name
# result[i][1] - CREATE TABLE parameters # result[i][1] - CREATE TABLE parameters
#Searching for CONSTRAINT `tablename_ibfk_1` #Searching for CONSTRAINT `tablename_ibfk_1`
for m in re.finditer('(ibfk_[0-9]+)', inner[j][1]): for m in re.finditer('(ibfk_[0-9]+)', inner[j][1]):
key = "`" + inner[j][0] + "_" + m.group() + "`" key = "`" + inner[j][0] + "_" + m.group() + "`"
self.cursor.execute("ALTER TABLE " + inner[j][0] + " DROP FOREIGN KEY " + key) self.cursor.execute("ALTER TABLE " + inner[j][0] + " DROP FOREIGN KEY " + key)
self.db.commit() self.db.commit()
#end drop_referencial_inegrity #end drop_referencial_inegrity
def get_backend_name(self): def get_backend_name(self):
"""Returns the name of the currently used backend""" """Returns the name of the currently used backend"""
if self.backend==2: if self.backend==2:
return "MySQL InnoDB" return "MySQL InnoDB"
elif self.backend==3: elif self.backend==3:
return "PostgreSQL" return "PostgreSQL"
else: else:
raise fpdb_simple.FpdbError("invalid backend") raise fpdb_simple.FpdbError("invalid backend")
#end def get_backend_name #end def get_backend_name
def get_db_info(self): def get_db_info(self):
return (self.host, self.database, self.user, self.password) return (self.host, self.database, self.user, self.password)
#end def get_db_info #end def get_db_info
def fillDefaultData(self): def fillDefaultData(self):
self.cursor.execute("INSERT INTO Settings VALUES (118);") self.cursor.execute("INSERT INTO Settings VALUES (118);")
self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'Full Tilt Poker', 'USD');") self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'Full Tilt Poker', 'USD');")
self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'PokerStars', 'USD');") self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'PokerStars', 'USD');")
self.cursor.execute("INSERT INTO TourneyTypes VALUES (DEFAULT, 1, 0, 0, 0, False);") self.cursor.execute("INSERT INTO TourneyTypes VALUES (DEFAULT, 1, 0, 0, 0, False);")
#end def fillDefaultData #end def fillDefaultData
def recreate_tables(self): def recreate_tables(self):
"""(Re-)creates the tables of the current DB""" """(Re-)creates the tables of the current DB"""
self.drop_tables() self.drop_tables()
self.create_tables() self.create_tables()
self.db.commit() self.db.commit()
print "Finished recreating tables" print "Finished recreating tables"
#end def recreate_tables #end def recreate_tables
#end class fpdb_db #end class fpdb_db

482
pyfpdb/fpdb_import.py Executable file → Normal file
View File

@ -20,16 +20,16 @@
import sys import sys
try: try:
import MySQLdb import MySQLdb
mysqlLibFound=True mysqlLibFound=True
except: except:
pass pass
try: try:
import psycopg2 import psycopg2
pgsqlLibFound=True pgsqlLibFound=True
except: except:
pass pass
import traceback import traceback
import math import math
@ -42,274 +42,282 @@ from time import time
class Importer: class Importer:
def __init__(self, caller, settings, config): def __init__(self, caller, settings, config):
"""Constructor""" """Constructor"""
self.settings=settings self.settings=settings
self.caller=caller self.caller=caller
self.config = config self.config = config
self.db = None self.db = None
self.cursor = None self.cursor = None
self.filelist = {} self.filelist = {}
self.dirlist = {} self.dirlist = {}
self.monitor = False self.monitor = False
self.updated = {} #Time last import was run {file:mtime} self.updated = {} #Time last import was run {file:mtime}
self.lines = None self.lines = None
self.faobs = None #File as one big string self.faobs = None #File as one big string
self.pos_in_file = {} # dict to remember how far we have read in the file self.pos_in_file = {} # dict to remember how far we have read in the file
#Set defaults #Set defaults
self.callHud = self.config.get_import_parameters().get("callFpdbHud") self.callHud = self.config.get_import_parameters().get("callFpdbHud")
if not self.settings.has_key('minPrint'): if not self.settings.has_key('minPrint'):
self.settings['minPrint'] = 30 self.settings['minPrint'] = 30
self.dbConnect() self.dbConnect()
def dbConnect(self): # XXX: Why is this here, when fpdb_db.connect() already does the
#connect to DB # same?
if self.settings['db-backend'] == 2: def dbConnect(self):
if not mysqlLibFound: #connect to DB
raise fpdb_simple.FpdbError("interface library MySQLdb not found but MySQL selected as backend - please install the library or change the config file") if self.settings['db-backend'] == 2:
self.db = MySQLdb.connect(self.settings['db-host'], self.settings['db-user'], if not mysqlLibFound:
self.settings['db-password'], self.settings['db-databaseName']) raise fpdb_simple.FpdbError("interface library MySQLdb not found but MySQL selected as backend - please install the library or change the config file")
elif self.settings['db-backend'] == 3: self.db = MySQLdb.connect(self.settings['db-host'], self.settings['db-user'],
if not pgsqlLibFound: self.settings['db-password'], self.settings['db-databaseName'])
raise fpdb_simple.FpdbError("interface library psycopg2 not found but PostgreSQL selected as backend - please install the library or change the config file") elif self.settings['db-backend'] == 3:
print self.settings if not pgsqlLibFound:
self.db = psycopg2.connect(host = self.settings['db-host'], raise fpdb_simple.FpdbError("interface library psycopg2 not found but PostgreSQL selected as backend - please install the library or change the config file")
user = self.settings['db-user'], print self.settings
password = self.settings['db-password'], if self.settings.has_key('db-host') and \
database = self.settings['db-databaseName']) self.settings.has_key('db-user'):
elif self.settings['db-backend'] == 4: self.db = psycopg2.connect(host = self.settings['db-host'],
pass user = self.settings['db-user'],
else: password = self.settings['db-password'],
pass database = self.settings['db-databaseName'])
self.cursor = self.db.cursor() else:
dbname = self.settings['db-databaseName']
self.db = psycopg2.connect(database = dbname)
elif self.settings['db-backend'] == 4:
pass
else:
pass
self.cursor = self.db.cursor()
#Set functions #Set functions
def setCallHud(self, value): def setCallHud(self, value):
self.callHud = value self.callHud = value
def setMinPrint(self, value): def setMinPrint(self, value):
self.settings['minPrint'] = int(value) self.settings['minPrint'] = int(value)
def setHandCount(self, value): def setHandCount(self, value):
self.settings['handCount'] = int(value) self.settings['handCount'] = int(value)
def setQuiet(self, value): def setQuiet(self, value):
self.settings['quiet'] = value self.settings['quiet'] = value
def setFailOnError(self, value): def setFailOnError(self, value):
self.settings['failOnError'] = value self.settings['failOnError'] = value
# def setWatchTime(self): # def setWatchTime(self):
# self.updated = time() # self.updated = time()
def clearFileList(self): def clearFileList(self):
self.filelist = {} self.filelist = {}
#Add an individual file to filelist #Add an individual file to filelist
def addImportFile(self, filename, site = "default", filter = "passthrough"): def addImportFile(self, filename, site = "default", filter = "passthrough"):
#TODO: test it is a valid file #TODO: test it is a valid file
self.filelist[filename] = [site] + [filter] self.filelist[filename] = [site] + [filter]
#Add a directory of files to filelist #Add a directory of files to filelist
#Only one import directory per site supported. #Only one import directory per site supported.
#dirlist is a hash of lists: #dirlist is a hash of lists:
#dirlist{ 'PokerStars' => ["/path/to/import/", "filtername"] } #dirlist{ 'PokerStars' => ["/path/to/import/", "filtername"] }
def addImportDirectory(self,dir,monitor = False, site = "default", filter = "passthrough"): def addImportDirectory(self,dir,monitor = False, site = "default", filter = "passthrough"):
if os.path.isdir(dir): if os.path.isdir(dir):
if monitor == True: if monitor == True:
self.monitor = True self.monitor = True
self.dirlist[site] = [dir] + [filter] self.dirlist[site] = [dir] + [filter]
for file in os.listdir(dir): for file in os.listdir(dir):
self.addImportFile(os.path.join(dir, file), site, filter) self.addImportFile(os.path.join(dir, file), site, filter)
else: else:
print "Warning: Attempted to add: '" + str(dir) + "' as an import directory" print "Warning: Attempted to add: '" + str(dir) + "' as an import directory"
#Run full import on filelist #Run full import on filelist
def runImport(self): def runImport(self):
for file in self.filelist: for file in self.filelist:
self.import_file_dict(file, self.filelist[file][0], self.filelist[file][1]) self.import_file_dict(file, self.filelist[file][0], self.filelist[file][1])
#Run import on updated files, then store latest update time. #Run import on updated files, then store latest update time.
def runUpdated(self): def runUpdated(self):
#Check for new files in directory #Check for new files in directory
#todo: make efficient - always checks for new file, should be able to use mtime of directory #todo: make efficient - always checks for new file, should be able to use mtime of directory
# ^^ May not work on windows # ^^ May not work on windows
for site in self.dirlist: for site in self.dirlist:
self.addImportDirectory(self.dirlist[site][0], False, site, self.dirlist[site][1]) self.addImportDirectory(self.dirlist[site][0], False, site, self.dirlist[site][1])
for file in self.filelist: for file in self.filelist:
stat_info = os.stat(file) stat_info = os.stat(file)
try: try:
lastupdate = self.updated[file] lastupdate = self.updated[file]
if stat_info.st_mtime > lastupdate: if stat_info.st_mtime > lastupdate:
self.import_file_dict(file, self.filelist[file][0], self.filelist[file][1]) self.import_file_dict(file, self.filelist[file][0], self.filelist[file][1])
self.updated[file] = time() self.updated[file] = time()
except: except:
self.updated[file] = time() self.updated[file] = time()
# This codepath only runs first time the file is found, if modified in the last # This codepath only runs first time the file is found, if modified in the last
# minute run an immediate import. # minute run an immediate import.
if (time() - stat_info.st_mtime) < 60: if (time() - stat_info.st_mtime) < 60:
self.import_file_dict(file, self.filelist[file][0], self.filelist[file][1]) self.import_file_dict(file, self.filelist[file][0], self.filelist[file][1])
# This is now an internal function that should not be called directly. # This is now an internal function that should not be called directly.
def import_file_dict(self, file, site, filter): def import_file_dict(self, file, site, filter):
if(filter == "passthrough"): if(filter == "passthrough"):
self.import_fpdb_file(file, site) self.import_fpdb_file(file, site)
else: else:
#Load filter, and run filtered file though main importer #Load filter, and run filtered file though main importer
self.import_fpdb_file(file, site) self.import_fpdb_file(file, site)
def import_fpdb_file(self, file, site): def import_fpdb_file(self, file, site):
starttime = time() starttime = time()
last_read_hand=0 last_read_hand=0
loc = 0 loc = 0
if (file=="stdin"): if (file=="stdin"):
inputFile=sys.stdin inputFile=sys.stdin
else: else:
inputFile=open(file, "rU") inputFile=open(file, "rU")
try: loc = self.pos_in_file[file] try: loc = self.pos_in_file[file]
except: pass except: pass
# Read input file into class and close file # Read input file into class and close file
inputFile.seek(loc) inputFile.seek(loc)
self.lines=fpdb_simple.removeTrailingEOL(inputFile.readlines()) self.lines=fpdb_simple.removeTrailingEOL(inputFile.readlines())
self.pos_in_file[file] = inputFile.tell() self.pos_in_file[file] = inputFile.tell()
inputFile.close() inputFile.close()
try: # sometimes we seem to be getting an empty self.lines, in which case, we just want to return. try: # sometimes we seem to be getting an empty self.lines, in which case, we just want to return.
firstline = self.lines[0] firstline = self.lines[0]
except: except:
# print "import_fpdb_file", file, site, self.lines, "\n" # print "import_fpdb_file", file, site, self.lines, "\n"
return return
if firstline.find("Tournament Summary")!=-1: if firstline.find("Tournament Summary")!=-1:
print "TODO: implement importing tournament summaries" print "TODO: implement importing tournament summaries"
#self.faobs = readfile(inputFile) #self.faobs = readfile(inputFile)
#self.parseTourneyHistory() #self.parseTourneyHistory()
return 0 return 0
site=fpdb_simple.recogniseSite(firstline) site=fpdb_simple.recogniseSite(firstline)
category=fpdb_simple.recogniseCategory(firstline) category=fpdb_simple.recogniseCategory(firstline)
startpos=0 startpos=0
stored=0 #counter stored=0 #counter
duplicates=0 #counter duplicates=0 #counter
partial=0 #counter partial=0 #counter
errors=0 #counter errors=0 #counter
for i in range (len(self.lines)): #main loop, iterates through the lines of a file and calls the appropriate parser method for i in range (len(self.lines)): #main loop, iterates through the lines of a file and calls the appropriate parser method
if (len(self.lines[i])<2): if (len(self.lines[i])<2):
endpos=i endpos=i
hand=self.lines[startpos:endpos] hand=self.lines[startpos:endpos]
if (len(hand[0])<2): if (len(hand[0])<2):
hand=hand[1:] hand=hand[1:]
cancelled=False cancelled=False
damaged=False damaged=False
if (site=="ftp"): if (site=="ftp"):
for i in range (len(hand)): for i in range (len(hand)):
if (hand[i].endswith(" has been canceled")): #this is their typo. this is a typo, right? if (hand[i].endswith(" has been canceled")): #this is their typo. this is a typo, right?
cancelled=True cancelled=True
seat1=hand[i].find("Seat ") #todo: make this recover by skipping this line seat1=hand[i].find("Seat ") #todo: make this recover by skipping this line
if (seat1!=-1): if (seat1!=-1):
if (hand[i].find("Seat ", seat1+3)!=-1): if (hand[i].find("Seat ", seat1+3)!=-1):
damaged=True damaged=True
if (len(hand)<3): if (len(hand)<3):
pass pass
#todo: the above 2 lines are kind of a dirty hack, the mentioned circumstances should be handled elsewhere but that doesnt work with DOS/Win EOL. actually this doesnt work. #todo: the above 2 lines are kind of a dirty hack, the mentioned circumstances should be handled elsewhere but that doesnt work with DOS/Win EOL. actually this doesnt work.
elif (hand[0].endswith(" (partial)")): #partial hand - do nothing elif (hand[0].endswith(" (partial)")): #partial hand - do nothing
partial+=1 partial+=1
elif (hand[1].find("Seat")==-1 and hand[2].find("Seat")==-1 and hand[3].find("Seat")==-1):#todo: should this be or instead of and? elif (hand[1].find("Seat")==-1 and hand[2].find("Seat")==-1 and hand[3].find("Seat")==-1):#todo: should this be or instead of and?
partial+=1 partial+=1
elif (cancelled or damaged): elif (cancelled or damaged):
partial+=1 partial+=1
else: #normal processing else: #normal processing
isTourney=fpdb_simple.isTourney(hand[0]) isTourney=fpdb_simple.isTourney(hand[0])
if not isTourney: if not isTourney:
fpdb_simple.filterAnteBlindFold(site,hand) fpdb_simple.filterAnteBlindFold(site,hand)
hand=fpdb_simple.filterCrap(site, hand, isTourney) hand=fpdb_simple.filterCrap(site, hand, isTourney)
self.hand=hand self.hand=hand
try: try:
handsId=fpdb_parse_logic.mainParser(self.db, self.cursor, site, category, hand) handsId=fpdb_parse_logic.mainParser(self.settings['db-backend'], self.db
self.db.commit() ,self.cursor, site, category, hand)
self.db.commit()
stored+=1 stored+=1
self.db.commit() self.db.commit()
if self.callHud: if self.callHud:
#print "call to HUD here. handsId:",handsId #print "call to HUD here. handsId:",handsId
#pipe the Hands.id out to the HUD #pipe the Hands.id out to the HUD
self.caller.pipe_to_hud.stdin.write("%s" % (handsId) + os.linesep) self.caller.pipe_to_hud.stdin.write("%s" % (handsId) + os.linesep)
except fpdb_simple.DuplicateError: except fpdb_simple.DuplicateError:
duplicates+=1 duplicates+=1
except (ValueError), fe: except (ValueError), fe:
errors+=1 errors+=1
self.printEmailErrorMessage(errors, file, hand) self.printEmailErrorMessage(errors, file, hand)
if (self.settings['failOnError']): if (self.settings['failOnError']):
self.db.commit() #dont remove this, in case hand processing was cancelled. self.db.commit() #dont remove this, in case hand processing was cancelled.
raise raise
except (fpdb_simple.FpdbError), fe: except (fpdb_simple.FpdbError), fe:
errors+=1 errors+=1
self.printEmailErrorMessage(errors, file, hand) self.printEmailErrorMessage(errors, file, hand)
#fe.printStackTrace() #todo: get stacktrace #fe.printStackTrace() #todo: get stacktrace
self.db.rollback() self.db.rollback()
if (self.settings['failOnError']): if (self.settings['failOnError']):
self.db.commit() #dont remove this, in case hand processing was cancelled. self.db.commit() #dont remove this, in case hand processing was cancelled.
raise raise
if (self.settings['minPrint']!=0): if (self.settings['minPrint']!=0):
if ((stored+duplicates+partial+errors)%self.settings['minPrint']==0): if ((stored+duplicates+partial+errors)%self.settings['minPrint']==0):
print "stored:", stored, "duplicates:", duplicates, "partial:", partial, "errors:", errors print "stored:", stored, "duplicates:", duplicates, "partial:", partial, "errors:", errors
if (self.settings['handCount']!=0): if (self.settings['handCount']!=0):
if ((stored+duplicates+partial+errors)>=self.settings['handCount']): if ((stored+duplicates+partial+errors)>=self.settings['handCount']):
if (not self.settings['quiet']): if (not self.settings['quiet']):
print "quitting due to reaching the amount of hands to be imported" print "quitting due to reaching the amount of hands to be imported"
print "Total stored:", stored, "duplicates:", duplicates, "partial/damaged:", partial, "errors:", errors, " time:", (time() - starttime) print "Total stored:", stored, "duplicates:", duplicates, "partial/damaged:", partial, "errors:", errors, " time:", (time() - starttime)
sys.exit(0) sys.exit(0)
startpos=endpos startpos=endpos
print "Total stored:", stored, "duplicates:", duplicates, "partial:", partial, "errors:", errors, " time:", (time() - starttime) print "Total stored:", stored, "duplicates:", duplicates, "partial:", partial, "errors:", errors, " time:", (time() - starttime)
if stored==0: if stored==0:
if duplicates>0: if duplicates>0:
for line_no in range(len(self.lines)): for line_no in range(len(self.lines)):
if self.lines[line_no].find("Game #")!=-1: if self.lines[line_no].find("Game #")!=-1:
final_game_line=self.lines[line_no] final_game_line=self.lines[line_no]
handsId=fpdb_simple.parseSiteHandNo(final_game_line) handsId=fpdb_simple.parseSiteHandNo(final_game_line)
else: else:
print "failed to read a single hand from file:", inputFile print "failed to read a single hand from file:", inputFile
handsId=0 handsId=0
#todo: this will cause return of an unstored hand number if the last hand was error or partial #todo: this will cause return of an unstored hand number if the last hand was error or partial
self.db.commit() self.db.commit()
self.handsId=handsId self.handsId=handsId
return handsId return handsId
#end def import_file_dict #end def import_file_dict
def parseTourneyHistory(self): def parseTourneyHistory(self):
print "Tourney history parser stub" print "Tourney history parser stub"
#Find tournament boundaries. #Find tournament boundaries.
#print self.foabs #print self.foabs
def printEmailErrorMessage(self, errors, filename, line): def printEmailErrorMessage(self, errors, filename, line):
traceback.print_exc(file=sys.stderr) traceback.print_exc(file=sys.stderr)
print "Error No.",errors,", please send the hand causing this to steffen@sycamoretest.info so I can fix it." print "Error No.",errors,", please send the hand causing this to steffen@sycamoretest.info so I can fix it."
print "Filename:", filename print "Filename:", filename
print "Here is the first line so you can identify it. Please mention that the error was a ValueError:" print "Here is the first line so you can identify it. Please mention that the error was a ValueError:"
print self.hand[0] print self.hand[0]
print "Hand logged to hand-errors.txt" print "Hand logged to hand-errors.txt"
logfile = open('hand-errors.txt', 'a') logfile = open('hand-errors.txt', 'a')
for s in self.hand: for s in self.hand:
logfile.write(str(s) + "\n") logfile.write(str(s) + "\n")
logfile.write("\n") logfile.write("\n")
logfile.close() logfile.close()
if __name__ == "__main__": if __name__ == "__main__":
print "CLI for fpdb_import is now available as CliFpdb.py" print "CLI for fpdb_import is now available as CliFpdb.py"

View File

@ -21,146 +21,171 @@ import fpdb_simple
import fpdb_save_to_db import fpdb_save_to_db
#parses a holdem hand #parses a holdem hand
def mainParser(db, cursor, site, category, hand): def mainParser(backend, db, cursor, site, category, hand):
if (category=="holdem" or category=="omahahi" or category=="omahahilo"): if (category=="holdem" or category=="omahahi" or category=="omahahilo"):
base="hold" base="hold"
else: else:
base="stud" base="stud"
#part 0: create the empty arrays #part 0: create the empty arrays
lineTypes=[] #char, valid values: header, name, cards, action, win, rake, ignore lineTypes=[] #char, valid values: header, name, cards, action, win, rake, ignore
lineStreets=[] #char, valid values: (predeal, preflop, flop, turn, river) lineStreets=[] #char, valid values: (predeal, preflop, flop, turn, river)
cardValues, cardSuits, boardValues, boardSuits, antes, actionTypes, allIns, actionAmounts, actionNos, actionTypeByNo, seatLines, winnings, rakes=[],[],[],[],[],[],[],[],[],[],[],[],[] cardValues, cardSuits, boardValues, boardSuits, antes, actionTypes, allIns, actionAmounts, actionNos, actionTypeByNo, seatLines, winnings, rakes=[],[],[],[],[],[],[],[],[],[],[],[],[]
#part 1: read hand no and check for duplicate #part 1: read hand no and check for duplicate
siteHandNo=fpdb_simple.parseSiteHandNo(hand[0]) siteHandNo=fpdb_simple.parseSiteHandNo(hand[0])
handStartTime=fpdb_simple.parseHandStartTime(hand[0], site) handStartTime=fpdb_simple.parseHandStartTime(hand[0], site)
siteID=fpdb_simple.recogniseSiteID(cursor, site) siteID=fpdb_simple.recogniseSiteID(cursor, site)
#print "parse logic, siteID:",siteID,"site:",site #print "parse logic, siteID:",siteID,"site:",site
isTourney=fpdb_simple.isTourney(hand[0]) isTourney=fpdb_simple.isTourney(hand[0])
smallBlindLine=0 smallBlindLine=0
for i in range(len(hand)): for i in range(len(hand)):
if hand[i].find("posts small blind")!=-1 or hand[i].find("posts the small blind")!=-1: if hand[i].find("posts small blind")!=-1 or hand[i].find("posts the small blind")!=-1:
if hand[i][-2:] == "$0": if hand[i][-2:] == "$0":
continue continue
smallBlindLine=i smallBlindLine=i
#print "found small blind line:",smallBlindLine #print "found small blind line:",smallBlindLine
break break
#print "small blind line:",smallBlindLine #print "small blind line:",smallBlindLine
gametypeID=fpdb_simple.recogniseGametypeID(cursor, hand[0], hand[smallBlindLine], siteID, category, isTourney) gametypeID=fpdb_simple.recogniseGametypeID(backend, db, cursor, hand[0], hand[smallBlindLine], siteID, category, isTourney)
if isTourney: if isTourney:
if site!="ps": if site!="ps":
raise fpdb_simple.FpdbError("tourneys are only supported on PS right now") raise fpdb_simple.FpdbError("tourneys are only supported on PS right now")
siteTourneyNo=fpdb_simple.parseTourneyNo(hand[0]) siteTourneyNo=fpdb_simple.parseTourneyNo(hand[0])
buyin=fpdb_simple.parseBuyin(hand[0]) buyin=fpdb_simple.parseBuyin(hand[0])
fee=fpdb_simple.parseFee(hand[0]) fee=fpdb_simple.parseFee(hand[0])
entries=-1 #todo: parse this entries=-1 #todo: parse this
prizepool=-1 #todo: parse this prizepool=-1 #todo: parse this
knockout=0 knockout=0
tourneyStartTime=handStartTime #todo: read tourney start time tourneyStartTime=handStartTime #todo: read tourney start time
rebuyOrAddon=fpdb_simple.isRebuyOrAddon(hand[0]) rebuyOrAddon=fpdb_simple.isRebuyOrAddon(hand[0])
tourneyTypeId=fpdb_simple.recogniseTourneyTypeId(cursor, siteID, buyin, fee, knockout, rebuyOrAddon) tourneyTypeId=fpdb_simple.recogniseTourneyTypeId(cursor, siteID, buyin, fee, knockout, rebuyOrAddon)
fpdb_simple.isAlreadyInDB(cursor, gametypeID, siteHandNo) fpdb_simple.isAlreadyInDB(cursor, gametypeID, siteHandNo)
#part 2: classify lines by type (e.g. cards, action, win, sectionchange) and street #part 2: classify lines by type (e.g. cards, action, win, sectionchange) and street
fpdb_simple.classifyLines(hand, category, lineTypes, lineStreets) fpdb_simple.classifyLines(hand, category, lineTypes, lineStreets)
#part 3: read basic player info #part 3: read basic player info
#3a read player names, startcashes #3a read player names, startcashes
for i in range (len(hand)): #todo: use maxseats+1 here. for i in range (len(hand)): #todo: use maxseats+1 here.
if (lineTypes[i]=="name"): if (lineTypes[i]=="name"):
seatLines.append(hand[i]) seatLines.append(hand[i])
names=fpdb_simple.parseNames(seatLines) names=fpdb_simple.parseNames(seatLines)
playerIDs = fpdb_simple.recognisePlayerIDs(cursor, names, siteID) playerIDs = fpdb_simple.recognisePlayerIDs(cursor, names, siteID)
tmp=fpdb_simple.parseCashesAndSeatNos(seatLines, site) tmp=fpdb_simple.parseCashesAndSeatNos(seatLines, site)
startCashes=tmp['startCashes'] startCashes=tmp['startCashes']
seatNos=tmp['seatNos'] seatNos=tmp['seatNos']
fpdb_simple.createArrays(category, len(names), cardValues, cardSuits, antes, winnings, rakes, actionTypes, allIns, actionAmounts, actionNos, actionTypeByNo) fpdb_simple.createArrays(category, len(names), cardValues, cardSuits, antes, winnings, rakes, actionTypes, allIns, actionAmounts, actionNos, actionTypeByNo)
#3b read positions #3b read positions
if base=="hold": if base=="hold":
positions = fpdb_simple.parsePositions (hand, names) positions = fpdb_simple.parsePositions (hand, names)
#part 4: take appropriate action for each line based on linetype #part 4: take appropriate action for each line based on linetype
for i in range(len(hand)): for i in range(len(hand)):
if (lineTypes[i]=="cards"): if (lineTypes[i]=="cards"):
fpdb_simple.parseCardLine (site, category, lineStreets[i], hand[i], names, cardValues, cardSuits, boardValues, boardSuits) fpdb_simple.parseCardLine (site, category, lineStreets[i], hand[i], names, cardValues, cardSuits, boardValues, boardSuits)
#if category=="studhilo": #if category=="studhilo":
# print "hand[i]:", hand[i] # print "hand[i]:", hand[i]
# print "cardValues:", cardValues # print "cardValues:", cardValues
# print "cardSuits:", cardSuits # print "cardSuits:", cardSuits
elif (lineTypes[i]=="action"): elif (lineTypes[i]=="action"):
fpdb_simple.parseActionLine (site, base, isTourney, hand[i], lineStreets[i], playerIDs, names, actionTypes, allIns, actionAmounts, actionNos, actionTypeByNo) fpdb_simple.parseActionLine (site, base, isTourney, hand[i], lineStreets[i], playerIDs, names, actionTypes, allIns, actionAmounts, actionNos, actionTypeByNo)
elif (lineTypes[i]=="win"): elif (lineTypes[i]=="win"):
fpdb_simple.parseWinLine (hand[i], site, names, winnings, isTourney) fpdb_simple.parseWinLine (hand[i], site, names, winnings, isTourney)
elif (lineTypes[i]=="rake"): elif (lineTypes[i]=="rake"):
if isTourney: if isTourney:
totalRake=0 totalRake=0
else: else:
totalRake=fpdb_simple.parseRake(hand[i]) totalRake=fpdb_simple.parseRake(hand[i])
fpdb_simple.splitRake(winnings, rakes, totalRake) fpdb_simple.splitRake(winnings, rakes, totalRake)
elif (lineTypes[i]=="header" or lineTypes[i]=="rake" or lineTypes[i]=="name" or lineTypes[i]=="ignore"): elif (lineTypes[i]=="header" or lineTypes[i]=="rake" or lineTypes[i]=="name" or lineTypes[i]=="ignore"):
pass pass
elif (lineTypes[i]=="ante"): elif (lineTypes[i]=="ante"):
fpdb_simple.parseAnteLine(hand[i], site, isTourney, names, antes) fpdb_simple.parseAnteLine(hand[i], site, isTourney, names, antes)
elif (lineTypes[i]=="table"): elif (lineTypes[i]=="table"):
tableResult=fpdb_simple.parseTableLine(site, base, hand[i]) tableResult=fpdb_simple.parseTableLine(site, base, hand[i])
else: else:
raise fpdb_simple.FpdbError("unrecognised lineType:"+lineTypes[i]) raise fpdb_simple.FpdbError("unrecognised lineType:"+lineTypes[i])
if site=="ftp": if site=="ftp":
tableResult=fpdb_simple.parseTableLine(site, base, hand[0]) tableResult=fpdb_simple.parseTableLine(site, base, hand[0])
maxSeats=tableResult['maxSeats'] maxSeats=tableResult['maxSeats']
tableName=tableResult['tableName'] tableName=tableResult['tableName']
#print "before part5, antes:", antes #print "before part5, antes:", antes
#part 5: final preparations, then call fpdb_save_to_db.* with #part 5: final preparations, then call fpdb_save_to_db.* with
# the arrays as they are - that file will fill them. # the arrays as they are - that file will fill them.
fpdb_simple.convertCardValues(cardValues) fpdb_simple.convertCardValues(cardValues)
if base=="hold": if base=="hold":
fpdb_simple.convertCardValuesBoard(boardValues) fpdb_simple.convertCardValuesBoard(boardValues)
fpdb_simple.convertBlindBet(actionTypes, actionAmounts) fpdb_simple.convertBlindBet(actionTypes, actionAmounts)
fpdb_simple.checkPositions(positions) fpdb_simple.checkPositions(positions)
cursor.execute("SELECT limitType FROM Gametypes WHERE id=%s",(gametypeID, )) cursor.execute("SELECT limitType FROM Gametypes WHERE id=%s",(gametypeID, ))
limit_type=cursor.fetchone()[0] limit_type=cursor.fetchone()[0]
fpdb_simple.convert3B4B(site, category, limit_type, actionTypes, actionAmounts) fpdb_simple.convert3B4B(site, category, limit_type, actionTypes, actionAmounts)
totalWinnings=0 totalWinnings=0
for i in range(len(winnings)): for i in range(len(winnings)):
totalWinnings+=winnings[i] totalWinnings+=winnings[i]
if base=="hold": if base=="hold":
hudImportData=fpdb_simple.generateHudCacheData(playerIDs, base, category, actionTypes, allIns, actionTypeByNo, winnings, totalWinnings, positions) hudImportData=fpdb_simple.generateHudCacheData(playerIDs, base, category, actionTypes
else: , allIns, actionTypeByNo, winnings, totalWinnings, positions
hudImportData=fpdb_simple.generateHudCacheData(playerIDs, base, category, actionTypes, allIns, actionTypeByNo, winnings, totalWinnings, None) , actionTypes, actionAmounts)
else:
hudImportData=fpdb_simple.generateHudCacheData(playerIDs, base, category, actionTypes
, allIns, actionTypeByNo, winnings, totalWinnings, None
, actionTypes, actionAmounts)
if isTourney: if isTourney:
ranks=[] ranks=[]
for i in range (len(names)): for i in range (len(names)):
ranks.append(0) ranks.append(0)
payin_amounts=fpdb_simple.calcPayin(len(names), buyin, fee) payin_amounts=fpdb_simple.calcPayin(len(names), buyin, fee)
if base=="hold": if base=="hold":
result = fpdb_save_to_db.tourney_holdem_omaha(cursor, base, category, siteTourneyNo, buyin, fee, knockout, entries, prizepool, tourneyStartTime, payin_amounts, ranks, tourneyTypeId, siteID, result = fpdb_save_to_db.tourney_holdem_omaha(
siteHandNo, gametypeID, handStartTime, names, playerIDs, startCashes, positions, cardValues, cardSuits, boardValues, boardSuits, winnings, rakes, actionTypes, allIns, actionAmounts, actionNos, hudImportData, maxSeats, tableName, seatNos) backend, db, cursor, base, category, siteTourneyNo, buyin
elif base=="stud": , fee, knockout, entries, prizepool, tourneyStartTime
result = fpdb_save_to_db.tourney_stud(cursor, base, category, siteTourneyNo, buyin, fee, knockout, entries, prizepool, tourneyStartTime, payin_amounts, ranks, tourneyTypeId, siteID, , payin_amounts, ranks, tourneyTypeId, siteID, siteHandNo
siteHandNo, gametypeID, handStartTime, names, playerIDs, startCashes, antes, cardValues, cardSuits, winnings, rakes, actionTypes, allIns, actionAmounts, actionNos, hudImportData, maxSeats, tableName, seatNos) , gametypeID, handStartTime, names, playerIDs, startCashes
else: , positions, cardValues, cardSuits, boardValues, boardSuits
raise fpdb_simple.FpdbError ("unrecognised category") , winnings, rakes, actionTypes, allIns, actionAmounts
else: , actionNos, hudImportData, maxSeats, tableName, seatNos)
if base=="hold": elif base=="stud":
result = fpdb_save_to_db.ring_holdem_omaha(cursor, base, category, siteHandNo, gametypeID, handStartTime, names, playerIDs, startCashes, positions, cardValues, cardSuits, boardValues, boardSuits, winnings, rakes, actionTypes, allIns, actionAmounts, actionNos, hudImportData, maxSeats, tableName, seatNos) result = fpdb_save_to_db.tourney_stud(
elif base=="stud": backend, db, cursor, base, category, siteTourneyNo
result = fpdb_save_to_db.ring_stud(cursor, base, category, siteHandNo, gametypeID, , buyin, fee, knockout, entries, prizepool, tourneyStartTime
handStartTime, names, playerIDs, startCashes, antes, cardValues, , payin_amounts, ranks, tourneyTypeId, siteID, siteHandNo
cardSuits, winnings, rakes, actionTypes, allIns, actionAmounts, actionNos, hudImportData, maxSeats, tableName, seatNos) , gametypeID, handStartTime, names, playerIDs, startCashes
else: , antes, cardValues, cardSuits, winnings, rakes, actionTypes
raise fpdb_simple.FpdbError ("unrecognised category") , allIns, actionAmounts, actionNos, hudImportData, maxSeats
db.commit() , tableName, seatNos)
return result else:
raise fpdb_simple.FpdbError ("unrecognised category")
else:
if base=="hold":
result = fpdb_save_to_db.ring_holdem_omaha(
backend, db, cursor, base, category, siteHandNo
, gametypeID, handStartTime, names, playerIDs
, startCashes, positions, cardValues, cardSuits
, boardValues, boardSuits, winnings, rakes
, actionTypes, allIns, actionAmounts, actionNos
, hudImportData, maxSeats, tableName, seatNos)
elif base=="stud":
result = fpdb_save_to_db.ring_stud(
backend, db, cursor, base, category, siteHandNo, gametypeID
, handStartTime, names, playerIDs, startCashes, antes
, cardValues, cardSuits, winnings, rakes, actionTypes, allIns
, actionAmounts, actionNos, hudImportData, maxSeats, tableName
, seatNos)
else:
raise fpdb_simple.FpdbError ("unrecognised category")
db.commit()
return result
#end def mainParser #end def mainParser

View File

@ -18,77 +18,110 @@
#This file contains methods to store hands into the db. decides to move this #This file contains methods to store hands into the db. decides to move this
#into a seperate file since its ugly, fairly long and just generally in the way. #into a seperate file since its ugly, fairly long and just generally in the way.
from time import time
import fpdb_simple import fpdb_simple
#stores a stud/razz hand into the database #stores a stud/razz hand into the database
def ring_stud(cursor, base, category, site_hand_no, gametype_id, hand_start_time, names, player_ids, start_cashes, antes, card_values, card_suits, winnings, rakes, action_types, allIns, action_amounts, actionNos, hudImportData, maxSeats, tableName, seatNos): def ring_stud(backend, db, cursor, base, category, site_hand_no, gametype_id, hand_start_time
fpdb_simple.fillCardArrays(len(names), base, category, card_values, card_suits) ,names, player_ids, start_cashes, antes, card_values, card_suits, winnings, rakes
,action_types, allIns, action_amounts, actionNos, hudImportData, maxSeats, tableName
,seatNos):
fpdb_simple.fillCardArrays(len(names), base, category, card_values, card_suits)
hands_id=fpdb_simple.storeHands(cursor, site_hand_no, gametype_id, hand_start_time, names, tableName, maxSeats) hands_id=fpdb_simple.storeHands(backend, db, cursor, site_hand_no, gametype_id
,hand_start_time, names, tableName, maxSeats)
#print "before calling store_hands_players_stud, antes:", antes #print "before calling store_hands_players_stud, antes:", antes
hands_players_ids=fpdb_simple.store_hands_players_stud(cursor, hands_id, player_ids, hands_players_ids=fpdb_simple.store_hands_players_stud(backend, db, cursor, hands_id, player_ids
start_cashes, antes, card_values, card_suits, winnings, rakes, seatNos) ,start_cashes, antes, card_values
,card_suits, winnings, rakes, seatNos)
fpdb_simple.storeHudCache(cursor, base, category, gametype_id, player_ids, hudImportData) fpdb_simple.storeHudCache(cursor, base, category, gametype_id, player_ids, hudImportData)
fpdb_simple.storeActions(cursor, hands_players_ids, action_types, allIns, action_amounts, actionNos) fpdb_simple.storeActions(cursor, hands_players_ids, action_types
return hands_id ,allIns, action_amounts, actionNos)
return hands_id
#end def ring_stud #end def ring_stud
def ring_holdem_omaha(cursor, base, category, site_hand_no, gametype_id, hand_start_time, names, player_ids, start_cashes, positions, card_values, card_suits, board_values, board_suits, winnings, rakes, action_types, allIns, action_amounts, actionNos, hudImportData, maxSeats, tableName, seatNos): def ring_holdem_omaha(backend, db, cursor, base, category, site_hand_no, gametype_id
"""stores a holdem/omaha hand into the database""" ,hand_start_time, names, player_ids, start_cashes, positions, card_values
fpdb_simple.fillCardArrays(len(names), base, category, card_values, card_suits) ,card_suits, board_values, board_suits, winnings, rakes, action_types, allIns
fpdb_simple.fill_board_cards(board_values, board_suits) ,action_amounts, actionNos, hudImportData, maxSeats, tableName, seatNos):
"""stores a holdem/omaha hand into the database"""
t0 = time()
fpdb_simple.fillCardArrays(len(names), base, category, card_values, card_suits)
t1 = time()
fpdb_simple.fill_board_cards(board_values, board_suits)
t2 = time()
hands_id=fpdb_simple.storeHands(cursor, site_hand_no, gametype_id, hand_start_time, names, tableName, maxSeats) hands_id=fpdb_simple.storeHands(backend, db, cursor, site_hand_no, gametype_id
,hand_start_time, names, tableName, maxSeats)
hands_players_ids=fpdb_simple.store_hands_players_holdem_omaha(cursor, category, hands_id, player_ids, start_cashes, positions, card_values, card_suits, winnings, rakes, seatNos) t3 = time()
hands_players_ids=fpdb_simple.store_hands_players_holdem_omaha(
fpdb_simple.storeHudCache(cursor, base, category, gametype_id, player_ids, hudImportData) backend, db, cursor, category, hands_id, player_ids, start_cashes
, positions, card_values, card_suits, winnings, rakes, seatNos)
fpdb_simple.store_board_cards(cursor, hands_id, board_values, board_suits) t4 = time()
fpdb_simple.storeHudCache(cursor, base, category, gametype_id, player_ids, hudImportData)
fpdb_simple.storeActions(cursor, hands_players_ids, action_types, allIns, action_amounts, actionNos) t5 = time()
return hands_id fpdb_simple.store_board_cards(cursor, hands_id, board_values, board_suits)
t6 = time()
fpdb_simple.storeActions(cursor, hands_players_ids, action_types, allIns, action_amounts, actionNos)
t7 = time()
print "cards=%4.3f board=%4.3f hands=%4.3f plyrs=%4.3f hudcache=%4.3f board=%4.3f actions=%4.3f" \
% (t1-t0, t2-t1, t3-t2, t4-t3, t5-t4, t6-t5, t7-t6)
return hands_id
#end def ring_holdem_omaha #end def ring_holdem_omaha
def tourney_holdem_omaha(cursor, base, category, siteTourneyNo, buyin, fee, knockout, entries, prizepool, tourney_start, payin_amounts, ranks, tourneyTypeId, siteId, #end of tourney specific params def tourney_holdem_omaha(backend, db, cursor, base, category, siteTourneyNo, buyin, fee, knockout
site_hand_no, gametype_id, hand_start_time, names, player_ids, start_cashes, positions, card_values, card_suits, board_values, board_suits, winnings, rakes, action_types, allIns, action_amounts, actionNos, hudImportData, maxSeats, tableName, seatNos): ,entries, prizepool, tourney_start, payin_amounts, ranks, tourneyTypeId
"""stores a tourney holdem/omaha hand into the database""" ,siteId #end of tourney specific params
fpdb_simple.fillCardArrays(len(names), base, category, card_values, card_suits) ,site_hand_no, gametype_id, hand_start_time, names, player_ids
fpdb_simple.fill_board_cards(board_values, board_suits) ,start_cashes, positions, card_values, card_suits, board_values
,board_suits, winnings, rakes, action_types, allIns, action_amounts
,actionNos, hudImportData, maxSeats, tableName, seatNos):
"""stores a tourney holdem/omaha hand into the database"""
fpdb_simple.fillCardArrays(len(names), base, category, card_values, card_suits)
fpdb_simple.fill_board_cards(board_values, board_suits)
tourney_id=fpdb_simple.store_tourneys(cursor, tourneyTypeId, siteTourneyNo, entries, prizepool, tourney_start) tourney_id=fpdb_simple.store_tourneys(cursor, tourneyTypeId, siteTourneyNo, entries, prizepool, tourney_start)
tourneys_players_ids=fpdb_simple.store_tourneys_players(cursor, tourney_id, player_ids, payin_amounts, ranks, winnings) tourneys_players_ids=fpdb_simple.store_tourneys_players(cursor, tourney_id, player_ids, payin_amounts, ranks, winnings)
hands_id=fpdb_simple.storeHands(cursor, site_hand_no, gametype_id, hand_start_time, names, tableName, maxSeats) hands_id=fpdb_simple.storeHands(backend, db, cursor, site_hand_no, gametype_id
,hand_start_time, names, tableName, maxSeats)
hands_players_ids=fpdb_simple.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) hands_players_ids=fpdb_simple.store_hands_players_holdem_omaha_tourney(
backend, db, cursor, category, hands_id, player_ids, start_cashes, positions
, card_values, card_suits, winnings, rakes, seatNos, tourneys_players_ids)
fpdb_simple.storeHudCache(cursor, base, category, gametype_id, player_ids, hudImportData) fpdb_simple.storeHudCache(cursor, base, category, gametype_id, player_ids, hudImportData)
fpdb_simple.store_board_cards(cursor, hands_id, board_values, board_suits) fpdb_simple.store_board_cards(cursor, hands_id, board_values, board_suits)
fpdb_simple.storeActions(cursor, hands_players_ids, action_types, allIns, action_amounts, actionNos) fpdb_simple.storeActions(cursor, hands_players_ids, action_types, allIns, action_amounts, actionNos)
return hands_id return hands_id
#end def tourney_holdem_omaha #end def tourney_holdem_omaha
def tourney_stud(cursor, base, category, siteTourneyNo, buyin, fee, knockout, entries, prizepool, tourneyStartTime, payin_amounts, ranks, tourneyTypeId, siteId, def tourney_stud(backend, db, cursor, base, category, siteTourneyNo, buyin, fee, knockout, entries
siteHandNo, gametypeId, handStartTime, names, playerIds, startCashes, antes, cardValues, cardSuits, winnings, rakes, actionTypes, allIns, actionAmounts, actionNos, hudImportData, maxSeats, tableName, seatNos): ,prizepool, tourneyStartTime, payin_amounts, ranks, tourneyTypeId, siteId
,siteHandNo, gametypeId, handStartTime, names, playerIds, startCashes, antes
,cardValues, cardSuits, winnings, rakes, actionTypes, allIns, actionAmounts
,actionNos, hudImportData, maxSeats, tableName, seatNos):
#stores a tourney stud/razz hand into the database #stores a tourney stud/razz hand into the database
fpdb_simple.fillCardArrays(len(names), base, category, cardValues, cardSuits) fpdb_simple.fillCardArrays(len(names), base, category, cardValues, cardSuits)
tourney_id=fpdb_simple.store_tourneys(cursor, tourneyTypeId, siteTourneyNo, entries, prizepool, tourneyStartTime) tourney_id=fpdb_simple.store_tourneys(cursor, tourneyTypeId, siteTourneyNo, entries, prizepool, tourneyStartTime)
tourneys_players_ids=fpdb_simple.store_tourneys_players(cursor, tourney_id, playerIds, payin_amounts, ranks, winnings) tourneys_players_ids=fpdb_simple.store_tourneys_players(cursor, tourney_id, playerIds, payin_amounts, ranks, winnings)
hands_id=fpdb_simple.storeHands(cursor, siteHandNo, gametypeId, handStartTime, names, tableName, maxSeats) hands_id=fpdb_simple.storeHands(backend, db, cursor, siteHandNo, gametypeId, handStartTime, names, tableName, maxSeats)
hands_players_ids=fpdb_simple.store_hands_players_stud_tourney(cursor, hands_id, playerIds, startCashes, antes, cardValues, cardSuits, winnings, rakes, seatNos, tourneys_players_ids) hands_players_ids=fpdb_simple.store_hands_players_stud_tourney(backend, db, cursor, hands_id
, playerIds, startCashes, antes, cardValues, cardSuits
, winnings, rakes, seatNos, tourneys_players_ids)
fpdb_simple.storeHudCache(cursor, base, category, gametypeId, playerIds, hudImportData) fpdb_simple.storeHudCache(cursor, base, category, gametypeId, playerIds, hudImportData)
fpdb_simple.storeActions(cursor, hands_players_ids, actionTypes, allIns, actionAmounts, actionNos) fpdb_simple.storeActions(cursor, hands_players_ids, actionTypes, allIns, actionAmounts, actionNos)
return hands_id return hands_id
#end def tourney_stud #end def tourney_stud

3657
pyfpdb/fpdb_simple.py Normal file → Executable file

File diff suppressed because it is too large Load Diff

27
pyfpdb/upd_indexes.sql Executable file
View File

@ -0,0 +1,27 @@
# script to update indexes on mysql (+other?) database
select '1. Dropping indexes' as ' ';
select 'Can''t drop messages indicate index already gone' as ' ';
ALTER TABLE `fpdb`.`Settings` DROP INDEX `id`;
ALTER TABLE `fpdb`.`Sites` DROP INDEX `id`;
ALTER TABLE `fpdb`.`Gametypes` DROP INDEX `id`;
ALTER TABLE `fpdb`.`Players` DROP INDEX `id`;
ALTER TABLE `fpdb`.`Autorates` DROP INDEX `id`;
ALTER TABLE `fpdb`.`Hands` DROP INDEX `id`;
ALTER TABLE `fpdb`.`BoardCards` DROP INDEX `id`;
ALTER TABLE `fpdb`.`TourneyTypes` DROP INDEX `id`;
ALTER TABLE `fpdb`.`Tourneys` DROP INDEX `id`;
ALTER TABLE `fpdb`.`TourneysPlayers` DROP INDEX `id`;
ALTER TABLE `fpdb`.`HandsPlayers` DROP INDEX `id`;
ALTER TABLE `fpdb`.`HandsActions` DROP INDEX `id`;
ALTER TABLE `fpdb`.`HudCache` DROP INDEX `id`;
select '2. Adding extra indexes on useful fields' as ' ';
select 'Duplicate key name messages indicate new indexes already there' as ' ';
ALTER TABLE `fpdb`.`tourneys` ADD INDEX `siteTourneyNo`(`siteTourneyNo`);
ALTER TABLE `fpdb`.`hands` ADD INDEX `siteHandNo`(`siteHandNo`);
ALTER TABLE `fpdb`.`players` ADD INDEX `name`(`name`);

0
readme.txt Normal file
View File