Merge branch 'master' of git://git.assembla.com/fpdboz
Conflicts: pyfpdb/Hud.py
This commit is contained in:
commit
15e1461cca
|
@ -71,15 +71,17 @@ class Everleaf(HandHistoryConverter):
|
|||
self.setFileType("text", "cp1252")
|
||||
self.rexx.setGameInfoRegex('.*Blinds \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+)')
|
||||
self.rexx.setSplitHandRegex('\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<DATETIME>\d\d\d\d/\d\d/\d\d - \d\d:\d\d:\d\d)\nTable (?P<TABLE>[ a-zA-Z]+)\nSeat (?P<BUTTON>[0-9]+)')
|
||||
self.rexx.setPlayerInfoRegex('Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \(\s+(\$ (?P<CASH>[.0-9]+) USD|new player|All-in) \)')
|
||||
self.rexx.setPostSbRegex('.*\n(?P<PNAME>.*): posts small blind \[\$? (?P<SB>[.0-9]+)')
|
||||
self.rexx.setPostBbRegex('.*\n(?P<PNAME>.*): posts big blind \[\$? (?P<BB>[.0-9]+)')
|
||||
# mct : what about posting small & big blinds simultaneously?
|
||||
self.rexx.setHeroCardsRegex('.*\nDealt\sto\s(?P<PNAME>.*)\s\[ (?P<HOLE1>\S\S), (?P<HOLE2>\S\S) \]')
|
||||
self.rexx.setPostBothRegex('.*\n(?P<PNAME>.*): posts small \& big blinds \[\$? (?P<SBBB>[.0-9]+)')
|
||||
self.rexx.setHeroCardsRegex('.*\nDealt\sto\s(?P<PNAME>.*)\s\[ (?P<CARDS>.*) \]')
|
||||
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.setCollectPotRegex('.*\n(?P<PNAME>.*) wins \$ (?P<POT>[.\d]+) USD(.*\[ (?P<HAND>.*) \])?')
|
||||
self.rexx.setCollectPotRegex('.*\n(?P<PNAME>.*) wins \$ (?P<POT>[.\d]+) USD(.*?\[ (?P<CARDS>.*?) \])?')
|
||||
#self.rexx.setCollectPotRegex('.*\n(?P<PNAME>.*) wins \$ (?P<POT>[.\d]+) USD(.*\[ (?P<CARDS>) \S\S, \S\S, \S\S, \S\S, \S\S \])?')
|
||||
self.rexx.sits_out_re = re.compile('(?P<PNAME>.*) sits out')
|
||||
self.rexx.compileRegexes()
|
||||
|
||||
def readSupportedGames(self):
|
||||
|
@ -111,8 +113,7 @@ class Everleaf(HandHistoryConverter):
|
|||
# 2008/11/10 3:58:52 ET
|
||||
#TODO: Do conversion from GMT to ET
|
||||
#TODO: Need some date functions to convert to different timezones (Date::Manip for perl rocked for this)
|
||||
hand.starttime = "%d/%02d/%02d %d:%02d:%02d ET" %(int(m.group('YEAR')), int(m.group('MON')), int(m.group('DAY')),
|
||||
int(m.group('HR')), int(m.group('MIN')), int(m.group('SEC')))
|
||||
hand.starttime = time.strptime(m.group('DATETIME'), "%Y/%m/%d - %H:%M:%S")
|
||||
hand.buttonpos = int(m.group('BUTTON'))
|
||||
|
||||
def readPlayerStacks(self, hand):
|
||||
|
@ -127,34 +128,30 @@ class Everleaf(HandHistoryConverter):
|
|||
#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)
|
||||
|
||||
m = re.search(r"\*\* Dealing down cards \*\*(?P<PREFLOP>.+(?=\*\* Dealing Flop \*\*)|.+)"
|
||||
r"(\*\* Dealing Flop \*\* \[ \S\S, \S\S, \S\S \](?P<FLOP>.+(?=\*\* Dealing Turn \*\*)|.+))?"
|
||||
r"(\*\* Dealing Turn \*\* \[ \S\S \](?P<TURN>.+(?=\*\* Dealing River \*\*)|.+))?"
|
||||
r"(\*\* Dealing River \*\* \[ \S\S \](?P<RIVER>.+))?", hand.string,re.DOTALL)
|
||||
r"(\*\* Dealing Flop \*\*(?P<FLOP> \[ \S\S, \S\S, \S\S \].+(?=\*\* Dealing Turn \*\*)|.+))?"
|
||||
r"(\*\* Dealing Turn \*\*(?P<TURN> \[ \S\S \].+(?=\*\* Dealing River \*\*)|.+))?"
|
||||
r"(\*\* Dealing River \*\*(?P<RIVER> \[ \S\S \].+))?", hand.string,re.DOTALL)
|
||||
|
||||
hand.streets = m
|
||||
hand.addStreets(m)
|
||||
|
||||
def readCommunityCards(self, hand):
|
||||
# currently regex in wrong place pls fix my brain's fried
|
||||
re_board = re.compile('\*\* Dealing (?P<STREET>.*) \*\* \[ (?P<CARDS>.*) \]')
|
||||
m = re_board.finditer(hand.string)
|
||||
for street in m:
|
||||
#print street.groups()
|
||||
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?
|
||||
cardsmatch = re_card.finditer(street.group('CARDS'))
|
||||
hand.setCommunityCards(street.group('STREET'), [card.group('CARD') for card in cardsmatch])
|
||||
|
||||
def readCommunityCards(self, hand, street): # street has been matched by markStreets, so exists in this hand
|
||||
self.rexx.board_re = re.compile(r"\[ (?P<CARDS>.+) \]")
|
||||
print hand.streets.group(street)
|
||||
if street in ('FLOP','TURN','RIVER'): # a list of streets which get dealt community cards (i.e. all but PREFLOP)
|
||||
m = self.rexx.board_re.search(hand.streets.group(street))
|
||||
hand.setCommunityCards(street, m.group('CARDS').split(', '))
|
||||
|
||||
def readBlinds(self, hand):
|
||||
try:
|
||||
m = self.rexx.small_blind_re.search(hand.string)
|
||||
hand.addBlind(m.group('PNAME'), m.group('SB'))
|
||||
#hand.posted = [m.group('PNAME')]
|
||||
except:
|
||||
hand.addBlind(None, 0)
|
||||
#hand.posted = ["FpdbNBP"]
|
||||
m = self.rexx.big_blind_re.finditer(hand.string)
|
||||
for a in m:
|
||||
hand.addBlind(a.group('PNAME'), a.group('BB'))
|
||||
#hand.posted = hand.posted + [a.group('PNAME')]
|
||||
hand.addBlind(m.group('PNAME'), 'small blind', m.group('SB'))
|
||||
except: # no small blind
|
||||
hand.addBlind(None, None, None)
|
||||
for a in self.rexx.big_blind_re.finditer(hand.string):
|
||||
hand.addBlind(a.group('PNAME'), 'big blind', a.group('BB'))
|
||||
for a in self.rexx.both_blinds_re.finditer(hand.string):
|
||||
hand.addBlind(a.group('PNAME'), 'small & big blinds', a.group('SBBB'))
|
||||
|
||||
def readHeroCards(self, hand):
|
||||
m = self.rexx.hero_cards_re.search(hand.string)
|
||||
|
@ -163,14 +160,17 @@ class Everleaf(HandHistoryConverter):
|
|||
hand.involved = False
|
||||
else:
|
||||
hand.hero = m.group('PNAME')
|
||||
hand.addHoleCards([m.group('HOLE1'), m.group('HOLE2')], m.group('PNAME'))
|
||||
# "2c, qh" -> set(["2c","qc"])
|
||||
# Also works with Omaha hands.
|
||||
cards = m.group('CARDS')
|
||||
cards = set(cards.split(', '))
|
||||
hand.addHoleCards(cards, m.group('PNAME'))
|
||||
|
||||
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') )
|
||||
hand.addCallandRaise( 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':
|
||||
|
@ -181,34 +181,33 @@ class Everleaf(HandHistoryConverter):
|
|||
hand.addCheck( street, action.group('PNAME'))
|
||||
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
|
||||
cards = shows.group('CARDS')
|
||||
cards = set(cards.split(', '))
|
||||
hand.addShownCards(cards, shows.group('PNAME'))
|
||||
|
||||
def readCollectPot(self,hand):
|
||||
m = self.rexx.collect_pot_re.search(hand.string)
|
||||
if m is not None:
|
||||
if m.group('HAND') is not None:
|
||||
re_card = re.compile('(?P<CARD>[0-9tjqka][schd])') # copied from earlier
|
||||
cards = set([hand.card(card.group('CARD')) for card in re_card.finditer(m.group('HAND'))])
|
||||
hand.addShownCards(cards=None, player=m.group('PNAME'), holeandboard=cards)
|
||||
for m in self.rexx.collect_pot_re.finditer(hand.string):
|
||||
hand.addCollectPot(player=m.group('PNAME'),pot=m.group('POT'))
|
||||
else:
|
||||
print "WARNING: Unusual, no one collected; can happen if it's folded to big blind with a dead small blind."
|
||||
|
||||
def getRake(self, hand):
|
||||
hand.rake = hand.totalpot * Decimal('0.05') # probably not quite right
|
||||
def readShownCards(self,hand):
|
||||
for m in self.rexx.collect_pot_re.finditer(hand.string):
|
||||
if m.group('CARDS') is not None:
|
||||
cards = m.group('CARDS')
|
||||
cards = set(cards.split(', '))
|
||||
hand.addShownCards(cards=None, player=m.group('PNAME'), holeandboard=cards)
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
c = Configuration.Config()
|
||||
e = Everleaf(c, "regression-test-files/everleaf/Speed_Kuala_full.txt")
|
||||
if len(sys.argv) == 1:
|
||||
testfile = "regression-test-files/everleaf/Speed_Kuala_full.txt"
|
||||
else:
|
||||
testfile = sys.argv[1]
|
||||
e = Everleaf(c, testfile)
|
||||
e.processFile()
|
||||
print str(e)
|
||||
|
||||
|
|
|
@ -635,6 +635,11 @@ class FpdbSQLQueries:
|
|||
elif(self.dbname == 'SQLite'):
|
||||
self.query['getPlayerId'] = """SELECT id from Players where name = %s"""
|
||||
|
||||
if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL'):
|
||||
self.query['getSiteId'] = """SELECT id from Sites where name = %s"""
|
||||
elif(self.dbname == 'SQLite'):
|
||||
self.query['getSiteId'] = """SELECT id from Sites where name = %s"""
|
||||
|
||||
if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL'):
|
||||
self.query['getRingProfitAllHandsPlayerIdSite'] = """
|
||||
SELECT hp.handId, hp.winnings, coalesce(hp.ante,0) + SUM(ha.amount)
|
||||
|
@ -643,8 +648,10 @@ class FpdbSQLQueries:
|
|||
INNER JOIN Players pl ON hp.playerId = pl.id
|
||||
INNER JOIN Hands h ON h.id = hp.handId
|
||||
INNER JOIN HandsActions ha ON ha.handPlayerId = hp.id
|
||||
WHERE pl.name = %s
|
||||
AND pl.siteId = %s
|
||||
where pl.id in <player_test>
|
||||
AND pl.siteId in <site_test>
|
||||
AND h.handStart > '<startdate_test>'
|
||||
AND h.handStart < '<enddate_test>'
|
||||
AND hp.tourneysPlayersId IS NULL
|
||||
GROUP BY hp.handId, hp.winnings, h.handStart, hp.ante
|
||||
ORDER BY h.handStart"""
|
||||
|
@ -656,8 +663,10 @@ class FpdbSQLQueries:
|
|||
INNER JOIN Players pl ON hp.playerId = pl.id
|
||||
INNER JOIN Hands h ON h.id = hp.handId
|
||||
INNER JOIN HandsActions ha ON ha.handPlayerId = hp.id
|
||||
WHERE pl.name = %s
|
||||
AND pl.siteId = %s
|
||||
where pl.id in <player_test>
|
||||
AND pl.siteId in <site_test>
|
||||
AND h.handStart > '<startdate_test>'
|
||||
AND h.handStart < '<enddate_test>'
|
||||
AND hp.tourneysPlayersId IS NULL
|
||||
GROUP BY hp.handId, hp.winnings, h.handStart
|
||||
ORDER BY h.handStart"""
|
||||
|
@ -694,7 +703,7 @@ class FpdbSQLQueries:
|
|||
AS BBlPer100
|
||||
,hprof2.profitperhand AS Profitperhand
|
||||
*/
|
||||
,hprof2.variance as Variance
|
||||
,format(hprof2.variance,2) AS Variance
|
||||
FROM
|
||||
(select /* stats from hudcache */
|
||||
gt.base
|
||||
|
@ -793,7 +802,7 @@ class FpdbSQLQueries:
|
|||
AS BBper100
|
||||
,hprof2.profitperhand AS Profitperhand
|
||||
*/
|
||||
,hprof2.variance as Variance
|
||||
,round(hprof2.variance,2) AS Variance
|
||||
FROM
|
||||
(select gt.base
|
||||
,gt.category
|
||||
|
@ -825,10 +834,10 @@ class FpdbSQLQueries:
|
|||
else to_char(100.0*(sum(street1Aggr)+sum(street2Aggr)+sum(street3Aggr))
|
||||
/(sum(street1Seen)+sum(street2Seen)+sum(street3Seen)),'90D0')
|
||||
end AS PoFAFq
|
||||
,to_char(sum(totalProfit)/100.0,'9G999G990D00') AS Net
|
||||
,round(sum(totalProfit)/100.0,2) AS Net
|
||||
,to_char((sum(totalProfit)/(gt.bigBlind+0.0)) / (sum(HDs)/100.0), '990D00')
|
||||
AS BBper100
|
||||
,to_char(sum(totalProfit) / (sum(HDs)+0.0), '990D0000') AS Profitperhand
|
||||
,to_char(sum(totalProfit/100.0) / (sum(HDs)+0.0), '990D0000') AS Profitperhand
|
||||
from Gametypes gt
|
||||
inner join Sites s on s.Id = gt.siteId
|
||||
inner join HudCache hc on hc.gameTypeId = gt.Id
|
||||
|
@ -867,53 +876,130 @@ class FpdbSQLQueries:
|
|||
|
||||
if(self.dbname == 'MySQL InnoDB'):
|
||||
self.query['playerStatsByPosition'] = """
|
||||
select /* stats from hudcache */
|
||||
hc.position
|
||||
,sum(HDs) as n
|
||||
,format(round(100.0*sum(street0VPI)/sum(HDs)),1) AS vpip
|
||||
,format(round(100.0*sum(street0Aggr)/sum(HDs)),1) AS pfr
|
||||
,format(round(100.0*sum(street1Seen)/sum(HDs)),1) AS saw_f
|
||||
,format(round(100.0*sum(sawShowdown)/sum(HDs)),1) AS sawsd
|
||||
,case when sum(street1Seen) = 0 then 'oo'
|
||||
else format(round(100.0*sum(sawShowdown)/sum(street1Seen)),1)
|
||||
end AS wtsdwsf
|
||||
,case when sum(sawShowdown) = 0 then 'oo'
|
||||
else format(round(100.0*sum(wonAtSD)/sum(sawShowdown)),1)
|
||||
end AS wmsd
|
||||
,case when sum(street1Seen) = 0 then 'oo'
|
||||
else format(round(100.0*sum(street1Aggr)/sum(street1Seen)),1)
|
||||
end AS FlAFq
|
||||
,case when sum(street2Seen) = 0 then 'oo'
|
||||
else format(round(100.0*sum(street2Aggr)/sum(street2Seen)),1)
|
||||
end AS TuAFq
|
||||
,case when sum(street3Seen) = 0 then 'oo'
|
||||
else format(round(100.0*sum(street3Aggr)/sum(street3Seen)),1)
|
||||
end AS RvAFq
|
||||
,case when sum(street1Seen)+sum(street2Seen)+sum(street3Seen) = 0 then 'oo'
|
||||
else format(round(100.0*(sum(street1Aggr)+sum(street2Aggr)+sum(street3Aggr))
|
||||
/(sum(street1Seen)+sum(street2Seen)+sum(street3Seen))),1)
|
||||
end AS PoFAFq
|
||||
,format(sum(totalProfit)/100.0,2) AS Net
|
||||
,case when sum(HDs) = 0 then 'oo'
|
||||
else format((sum(totalProfit)/(gt.bigBlind+0.0)) / (sum(HDs)/100.0),2)
|
||||
end AS BBper100
|
||||
from Gametypes gt
|
||||
inner join Sites s on (s.Id = gt.siteId)
|
||||
inner join HudCache hc on (hc.gameTypeId = gt.Id)
|
||||
inner join Players p on (p.id = hc.playerId)
|
||||
where hc.playerId in <player_test>
|
||||
and gt.type = 'ring'
|
||||
and gt.id = <gametype_test> /* must specify gametypeid */
|
||||
/* and stats.n > 100 optional stat-based queries */
|
||||
group by hc.position, gt.bigBlind
|
||||
order by case when hc.position = 'B' then -2
|
||||
when hc.position = 'S' then -1
|
||||
when hc.position = 'D' then 0
|
||||
when hc.position = 'C' then 1
|
||||
when hc.position = 'M' then 2
|
||||
when hc.position = 'E' then 5
|
||||
else 9
|
||||
end
|
||||
SELECT
|
||||
concat(upper(stats.limitType), ' '
|
||||
,concat(upper(substring(stats.category,1,1)),substring(stats.category,2) ), ' '
|
||||
,stats.name, ' $'
|
||||
,cast(trim(leading ' ' from
|
||||
case when stats.bigBlind < 100 then format(stats.bigBlind/100.0,2)
|
||||
else format(stats.bigBlind/100.0,0)
|
||||
end ) as char)
|
||||
) AS Game
|
||||
,case when stats.PlPosition = -2 then 'BB'
|
||||
when stats.PlPosition = -1 then 'SB'
|
||||
when stats.PlPosition = 0 then 'Btn'
|
||||
when stats.PlPosition = 1 then 'CO'
|
||||
when stats.PlPosition = 2 then 'MP'
|
||||
when stats.PlPosition = 5 then 'EP'
|
||||
else '??'
|
||||
end AS PlPosition
|
||||
,stats.n
|
||||
,stats.vpip
|
||||
,stats.pfr
|
||||
,stats.saw_f
|
||||
,stats.sawsd
|
||||
,stats.wtsdwsf
|
||||
,stats.wmsd
|
||||
,stats.FlAFq
|
||||
,stats.TuAFq
|
||||
,stats.RvAFq
|
||||
,stats.PoFAFq
|
||||
/* if you have handsactions data the next 3 fields should give same answer as
|
||||
following 3 commented out fields */
|
||||
,stats.Net
|
||||
,stats.BBper100
|
||||
,stats.Profitperhand
|
||||
/*,format(hprof2.sum_profit/100.0,2) AS Net
|
||||
,format((hprof2.sum_profit/(stats.bigBlind+0.0)) / (stats.n/100.0),2)
|
||||
AS BBlPer100
|
||||
,hprof2.profitperhand AS Profitperhand
|
||||
*/
|
||||
,format(hprof2.variance,2) AS Variance
|
||||
FROM
|
||||
(select /* stats from hudcache */
|
||||
gt.base
|
||||
,gt.category
|
||||
,upper(gt.limitType) as limitType
|
||||
,s.name
|
||||
,gt.bigBlind
|
||||
,hc.gametypeId
|
||||
,case when hc.position = 'B' then -2
|
||||
when hc.position = 'S' then -1
|
||||
when hc.position = 'D' then 0
|
||||
when hc.position = 'C' then 1
|
||||
when hc.position = 'M' then 2
|
||||
when hc.position = 'E' then 5
|
||||
else 9
|
||||
end as PlPosition
|
||||
,sum(HDs) AS n
|
||||
,format(100.0*sum(street0VPI)/sum(HDs),1) AS vpip
|
||||
,format(100.0*sum(street0Aggr)/sum(HDs),1) AS pfr
|
||||
,format(100.0*sum(street1Seen)/sum(HDs),1) AS saw_f
|
||||
,format(100.0*sum(sawShowdown)/sum(HDs),1) AS sawsd
|
||||
,case when sum(street1Seen) = 0 then 'oo'
|
||||
else format(100.0*sum(sawShowdown)/sum(street1Seen),1)
|
||||
end AS wtsdwsf
|
||||
,case when sum(sawShowdown) = 0 then 'oo'
|
||||
else format(100.0*sum(wonAtSD)/sum(sawShowdown),1)
|
||||
end AS wmsd
|
||||
,case when sum(street1Seen) = 0 then 'oo'
|
||||
else format(100.0*sum(street1Aggr)/sum(street1Seen),1)
|
||||
end AS FlAFq
|
||||
,case when sum(street2Seen) = 0 then 'oo'
|
||||
else format(100.0*sum(street2Aggr)/sum(street2Seen),1)
|
||||
end AS TuAFq
|
||||
,case when sum(street3Seen) = 0 then 'oo'
|
||||
else format(100.0*sum(street3Aggr)/sum(street3Seen),1)
|
||||
end AS RvAFq
|
||||
,case when sum(street1Seen)+sum(street2Seen)+sum(street3Seen) = 0 then 'oo'
|
||||
else format(100.0*(sum(street1Aggr)+sum(street2Aggr)+sum(street3Aggr))
|
||||
/(sum(street1Seen)+sum(street2Seen)+sum(street3Seen)),1)
|
||||
end AS PoFAFq
|
||||
,format(sum(totalProfit)/100.0,2) AS Net
|
||||
,format((sum(totalProfit)/(gt.bigBlind+0.0)) / (sum(HDs)/100.0),2)
|
||||
AS BBper100
|
||||
,format( (sum(totalProfit)/100.0) / sum(HDs), 4) AS Profitperhand
|
||||
from Gametypes gt
|
||||
inner join Sites s on s.Id = gt.siteId
|
||||
inner join HudCache hc on hc.gameTypeId = gt.Id
|
||||
where hc.playerId in <player_test>
|
||||
# use <gametype_test> here ?
|
||||
group by gt.base
|
||||
,gt.category
|
||||
,upper(gt.limitType)
|
||||
,s.name
|
||||
,gt.bigBlind
|
||||
,hc.gametypeId
|
||||
,PlPosition
|
||||
) stats
|
||||
inner join
|
||||
( select # profit from handsplayers/handsactions
|
||||
hprof.gameTypeId,
|
||||
case when hprof.position = 'B' then -2
|
||||
when hprof.position = 'S' then -1
|
||||
when hprof.position in ('3','4') then 2
|
||||
when hprof.position in ('6','7') then 5
|
||||
else hprof.position
|
||||
end as PlPosition,
|
||||
sum(hprof.profit) as sum_profit,
|
||||
avg(hprof.profit/100.0) as profitperhand,
|
||||
variance(hprof.profit/100.0) as variance
|
||||
from
|
||||
(select hp.handId, h.gameTypeId, hp.position, hp.winnings, SUM(ha.amount)
|
||||
costs, hp.winnings - SUM(ha.amount) profit
|
||||
from HandsPlayers hp
|
||||
inner join Hands h ON h.id = hp.handId
|
||||
left join HandsActions ha ON ha.handPlayerId = hp.id
|
||||
where hp.playerId in <player_test>
|
||||
# use <gametype_test> here ?
|
||||
and hp.tourneysPlayersId IS NULL
|
||||
group by hp.handId, h.gameTypeId, hp.position, hp.winnings
|
||||
) hprof
|
||||
group by hprof.gameTypeId, PlPosition
|
||||
) hprof2
|
||||
on ( hprof2.gameTypeId = stats.gameTypeId
|
||||
and hprof2.PlPosition = stats.PlPosition)
|
||||
order by stats.category, stats.limittype, stats.bigBlind, cast(stats.PlPosition as signed)
|
||||
"""
|
||||
elif(self.dbname == 'PostgreSQL'):
|
||||
self.query['playerStatsByPosition'] = """
|
||||
|
|
220
pyfpdb/FulltiltToFpdb.py
Executable file
220
pyfpdb/FulltiltToFpdb.py
Executable file
|
@ -0,0 +1,220 @@
|
|||
#!/usr/bin/env python
|
||||
# Copyright 2008, Carl Gherardi
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
########################################################################
|
||||
|
||||
import sys
|
||||
import Configuration
|
||||
from HandHistoryConverter import *
|
||||
|
||||
# FullTilt HH Format
|
||||
|
||||
#Full Tilt Poker Game #9403951181: Table CR - tay - $0.05/$0.10 - No Limit Hold'em - 9:40:20 ET - 2008/12/09
|
||||
#Seat 1: rigoise ($15.95)
|
||||
#Seat 2: K2dream ($6.70)
|
||||
#Seat 4: ravens2216 ($10)
|
||||
#Seat 5: rizkouner ($4)
|
||||
#Seat 6: Sorrowful ($8.35)
|
||||
#rigoise posts the small blind of $0.05
|
||||
#K2dream posts the big blind of $0.10
|
||||
#5 seconds left to act
|
||||
#rizkouner posts $0.10
|
||||
#The button is in seat #6
|
||||
#*** HOLE CARDS ***
|
||||
#Dealt to Sorrowful [8h Qc]
|
||||
#ravens2216 folds
|
||||
#rizkouner checks
|
||||
#Sorrowful has 15 seconds left to act
|
||||
#Sorrowful folds
|
||||
#rigoise folds
|
||||
#K2dream checks
|
||||
#*** FLOP *** [9d Kc 5c]
|
||||
#K2dream checks
|
||||
#rizkouner checks
|
||||
#*** TURN *** [9d Kc 5c] [5h]
|
||||
#K2dream has 15 seconds left to act
|
||||
#K2dream bets $0.20
|
||||
#rizkouner calls $0.20
|
||||
#*** RIVER *** [9d Kc 5c 5h] [6h]
|
||||
#K2dream checks
|
||||
#rizkouner has 15 seconds left to act
|
||||
#rizkouner bets $0.20
|
||||
#K2dream folds
|
||||
#Uncalled bet of $0.20 returned to rizkouner
|
||||
#rizkouner mucks
|
||||
#rizkouner wins the pot ($0.60)
|
||||
#*** SUMMARY ***
|
||||
#Total pot $0.65 | Rake $0.05
|
||||
#Board: [9d Kc 5c 5h 6h]
|
||||
#Seat 1: rigoise (small blind) folded before the Flop
|
||||
#Seat 2: K2dream (big blind) folded on the River
|
||||
#Seat 4: ravens2216 didn't bet (folded)
|
||||
#Seat 5: rizkouner collected ($0.60), mucked
|
||||
#Seat 6: Sorrowful (button) didn't bet (folded)
|
||||
#Seat N: rizkouner (button) showed [Jh Ah] and won ($0.70) with a pair of Threes
|
||||
|
||||
class FullTilt(HandHistoryConverter):
|
||||
def __init__(self, config, file):
|
||||
print "Initialising FullTilt converter class"
|
||||
HandHistoryConverter.__init__(self, config, file, sitename="FullTilt") # Call super class init.
|
||||
self.sitename = "FullTilt"
|
||||
self.setFileType("text", "cp1252")
|
||||
self.rexx.setGameInfoRegex('- \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) -')
|
||||
self.rexx.setSplitHandRegex('\n\n+')
|
||||
self.rexx.setHandInfoRegex('.*#(?P<HID>[0-9]+): Table (?P<TABLE>[- a-zA-Z]+) (\((?P<TABLEATTRIBUTES>.+)\) )?- \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) - (?P<GAMETYPE>[a-zA-Z\' ]+) - (?P<DATETIME>.*)')
|
||||
# self.rexx.setHandInfoRegex('.*#(?P<HID>[0-9]+): Table (?P<TABLE>[ a-zA-Z]+) - \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) - (?P<GAMETYPE>.*) - (?P<HR>[0-9]+):(?P<MIN>[0-9]+) ET - (?P<YEAR>[0-9]+)/(?P<MON>[0-9]+)/(?P<DAY>[0-9]+)Table (?P<TABLE>[ a-zA-Z]+)\nSeat (?P<BUTTON>[0-9]+)')
|
||||
self.rexx.button_re = re.compile('The button is in seat #(?P<BUTTON>\d+)')
|
||||
self.rexx.setPlayerInfoRegex('Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \(\$(?P<CASH>[.0-9]+)\)\n')
|
||||
self.rexx.setPostSbRegex('.*\n(?P<PNAME>.*) posts the small blind of \$?(?P<SB>[.0-9]+)')
|
||||
self.rexx.setPostBbRegex('.*\n(?P<PNAME>.*) posts (the big blind of )?\$?(?P<BB>[.0-9]+)')
|
||||
self.rexx.setPostBothRegex('.*\n(?P<PNAME>.*) posts small \& big blinds \[\$? (?P<SBBB>[.0-9]+)')
|
||||
self.rexx.setHeroCardsRegex('.*\nDealt\sto\s(?P<PNAME>.*)\s\[(?P<CARDS>.*)\]')
|
||||
self.rexx.setActionStepRegex('.*\n(?P<PNAME>.*)(?P<ATYPE> bets| checks| raises to| calls| folds)(\s\$(?P<BET>[.\d]+))?')
|
||||
self.rexx.setShowdownActionRegex('.*\n(?P<PNAME>.*) shows \[(?P<CARDS>.*)\]')
|
||||
self.rexx.setCollectPotRegex(r"Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*?) (\(button\) |\(small blind\) |\(big blind\) )?(collected|showed \[.*\] and won) \(\$(?P<POT>[.\d]+)\)(, mucked| with.*)")
|
||||
self.rexx.shown_cards_re = re.compile('Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \(.*\) showed \[(?P<CARDS>.*)\].*')
|
||||
self.rexx.sits_out_re = re.compile('(?P<PNAME>.*) sits out')
|
||||
self.rexx.compileRegexes()
|
||||
|
||||
def readSupportedGames(self):
|
||||
pass
|
||||
|
||||
def determineGameType(self):
|
||||
# Cheating with this regex, only support nlhe at the moment
|
||||
gametype = ["ring", "hold", "nl"]
|
||||
|
||||
m = self.rexx.game_info_re.search(self.obs)
|
||||
gametype = gametype + [m.group('SB')]
|
||||
gametype = gametype + [m.group('BB')]
|
||||
|
||||
return gametype
|
||||
|
||||
def readHandInfo(self, hand):
|
||||
m = self.rexx.hand_info_re.search(hand.string,re.DOTALL)
|
||||
#print m.groups()
|
||||
hand.handid = m.group('HID')
|
||||
hand.tablename = m.group('TABLE')
|
||||
hand.buttonpos = int(self.rexx.button_re.search(hand.string).group('BUTTON'))
|
||||
hand.starttime = time.strptime(m.group('DATETIME'), "%H:%M:%S ET - %Y/%m/%d")
|
||||
# These work, but the info is already in the Hand class - should be used for tourneys though.
|
||||
# m.group('SB')
|
||||
# m.group('BB')
|
||||
# m.group('GAMETYPE')
|
||||
|
||||
# Stars format (Nov 10 2008): 2008/11/07 12:38:49 CET [2008/11/07 7:38:49 ET]
|
||||
# or : 2008/11/07 12:38:49 ET
|
||||
# Not getting it in my HH files yet, so using
|
||||
# 2008/11/10 3:58:52 ET
|
||||
#TODO: Do conversion from GMT to ET
|
||||
#TODO: Need some date functions to convert to different timezones (Date::Manip for perl rocked for this)
|
||||
#hand.starttime = "%d/%02d/%02d %d:%02d:%02d ET" %(int(m.group('YEAR')), int(m.group('MON')), int(m.group('DAY')),
|
||||
##int(m.group('HR')), int(m.group('MIN')), int(m.group('SEC')))
|
||||
#FIXME: hand.buttonpos = int(m.group('BUTTON'))
|
||||
|
||||
def readPlayerStacks(self, hand):
|
||||
m = self.rexx.player_info_re.finditer(hand.string)
|
||||
players = []
|
||||
for a in m:
|
||||
hand.addPlayer(int(a.group('SEAT')), a.group('PNAME'), a.group('CASH'))
|
||||
|
||||
def markStreets(self, hand):
|
||||
# PREFLOP = ** Dealing down cards **
|
||||
# This re fails if, say, river is missing; then we don't get the ** that starts the river.
|
||||
|
||||
m = re.search(r"\*\*\* HOLE CARDS \*\*\*(?P<PREFLOP>.+(?=\*\*\* FLOP \*\*\*)|.+)"
|
||||
r"(\*\*\* FLOP \*\*\*(?P<FLOP> \[\S\S \S\S \S\S\].+(?=\*\*\* TURN \*\*\*)|.+))?"
|
||||
r"(\*\*\* TURN \*\*\* \[\S\S \S\S \S\S] (?P<TURN>\[\S\S\].+(?=\*\*\* RIVER \*\*\*)|.+))?"
|
||||
r"(\*\*\* RIVER \*\*\* \[\S\S \S\S \S\S \S\S] (?P<RIVER>\[\S\S\].+))?", hand.string,re.DOTALL)
|
||||
|
||||
hand.addStreets(m)
|
||||
|
||||
def readCommunityCards(self, hand, street): # street has been matched by markStreets, so exists in this hand
|
||||
if street in ('FLOP','TURN','RIVER'): # a list of streets which get dealt community cards (i.e. all but PREFLOP)
|
||||
self.rexx.board_re = re.compile(r"\[(?P<CARDS>.+)\]")
|
||||
#print "DEBUG readCommunityCards:", street, hand.streets.group(street)
|
||||
m = self.rexx.board_re.search(hand.streets.group(street))
|
||||
hand.setCommunityCards(street, m.group('CARDS').split(' '))
|
||||
|
||||
|
||||
def readBlinds(self, hand):
|
||||
try:
|
||||
m = self.rexx.small_blind_re.search(hand.string)
|
||||
hand.addBlind(m.group('PNAME'), 'small blind', m.group('SB'))
|
||||
except: # no small blind
|
||||
hand.addBlind(None, None, None)
|
||||
for a in self.rexx.big_blind_re.finditer(hand.string):
|
||||
hand.addBlind(a.group('PNAME'), 'big blind', a.group('BB'))
|
||||
for a in self.rexx.both_blinds_re.finditer(hand.string):
|
||||
hand.addBlind(a.group('PNAME'), 'small & big blinds', a.group('SBBB'))
|
||||
|
||||
def readHeroCards(self, hand):
|
||||
m = self.rexx.hero_cards_re.search(hand.string)
|
||||
if(m == None):
|
||||
#Not involved in hand
|
||||
hand.involved = False
|
||||
else:
|
||||
hand.hero = m.group('PNAME')
|
||||
# "2c, qh" -> set(["2c","qc"])
|
||||
# Also works with Omaha hands.
|
||||
cards = m.group('CARDS')
|
||||
cards = set(cards.split(' '))
|
||||
hand.addHoleCards(cards, m.group('PNAME'))
|
||||
|
||||
def readAction(self, hand, street):
|
||||
m = self.rexx.action_re.finditer(hand.streets.group(street))
|
||||
for action in m:
|
||||
if action.group('ATYPE') == ' raises to':
|
||||
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') )
|
||||
elif action.group('ATYPE') == ' folds':
|
||||
hand.addFold( street, action.group('PNAME'))
|
||||
elif action.group('ATYPE') == ' checks':
|
||||
hand.addCheck( street, action.group('PNAME'))
|
||||
else:
|
||||
print "DEBUG: unimplemented readAction: %s %s" %(action.group('PNAME'),action.group('ATYPE'),)
|
||||
|
||||
|
||||
def readShowdownActions(self, hand):
|
||||
for shows in self.rexx.showdown_action_re.finditer(hand.string):
|
||||
cards = shows.group('CARDS')
|
||||
cards = set(cards.split(' '))
|
||||
hand.addShownCards(cards, shows.group('PNAME'))
|
||||
|
||||
def readCollectPot(self,hand):
|
||||
for m in self.rexx.collect_pot_re.finditer(hand.string):
|
||||
hand.addCollectPot(player=m.group('PNAME'),pot=m.group('POT'))
|
||||
|
||||
def readShownCards(self,hand):
|
||||
for m in self.rexx.shown_cards_re.finditer(hand.string):
|
||||
if m.group('CARDS') is not None:
|
||||
cards = m.group('CARDS')
|
||||
cards = set(cards.split(' '))
|
||||
hand.addShownCards(cards=cards, player=m.group('PNAME'))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
c = Configuration.Config()
|
||||
if len(sys.argv) == 1:
|
||||
testfile = "regression-test-files/FT20081209 CR - tay - $0.05-$0.10 - No Limit Hold'em.txt"
|
||||
else:
|
||||
testfile = sys.argv[1]
|
||||
print "Converting: ", testfile
|
||||
e = FullTilt(c, testfile)
|
||||
e.processFile()
|
||||
print str(e)
|
|
@ -29,166 +29,182 @@ import fpdb_import
|
|||
|
||||
|
||||
class GuiAutoImport (threading.Thread):
|
||||
def __init__(self, settings, config):
|
||||
"""Constructor for GuiAutoImport"""
|
||||
self.settings=settings
|
||||
self.config=config
|
||||
def __init__(self, settings, config):
|
||||
"""Constructor for GuiAutoImport"""
|
||||
self.settings=settings
|
||||
self.config=config
|
||||
|
||||
imp = self.config.get_import_parameters()
|
||||
imp = self.config.get_import_parameters()
|
||||
|
||||
print "Import parameters"
|
||||
print imp
|
||||
print "Import parameters"
|
||||
print imp
|
||||
|
||||
self.input_settings = {}
|
||||
self.input_settings = {}
|
||||
self.pipe_to_hud = None
|
||||
|
||||
self.importer = fpdb_import.Importer(self,self.settings, self.config)
|
||||
self.importer.setCallHud(True)
|
||||
self.importer.setMinPrint(30)
|
||||
self.importer.setQuiet(False)
|
||||
self.importer.setFailOnError(False)
|
||||
self.importer.setHandCount(0)
|
||||
# self.importer.setWatchTime()
|
||||
self.importer = fpdb_import.Importer(self,self.settings, self.config)
|
||||
self.importer.setCallHud(True)
|
||||
self.importer.setMinPrint(30)
|
||||
self.importer.setQuiet(False)
|
||||
self.importer.setFailOnError(False)
|
||||
self.importer.setHandCount(0)
|
||||
# self.importer.setWatchTime()
|
||||
|
||||
self.server=settings['db-host']
|
||||
self.user=settings['db-user']
|
||||
self.password=settings['db-password']
|
||||
self.database=settings['db-databaseName']
|
||||
self.server=settings['db-host']
|
||||
self.user=settings['db-user']
|
||||
self.password=settings['db-password']
|
||||
self.database=settings['db-databaseName']
|
||||
|
||||
self.mainVBox=gtk.VBox(False,1)
|
||||
self.mainVBox.show()
|
||||
self.mainVBox=gtk.VBox(False,1)
|
||||
self.mainVBox.show()
|
||||
|
||||
self.settingsHBox = gtk.HBox(False, 0)
|
||||
self.mainVBox.pack_start(self.settingsHBox, False, True, 0)
|
||||
self.settingsHBox.show()
|
||||
self.settingsHBox = gtk.HBox(False, 0)
|
||||
self.mainVBox.pack_start(self.settingsHBox, False, True, 0)
|
||||
self.settingsHBox.show()
|
||||
|
||||
self.intervalLabel = gtk.Label("Interval (ie. break) between imports in seconds:")
|
||||
self.settingsHBox.pack_start(self.intervalLabel)
|
||||
self.intervalLabel.show()
|
||||
self.intervalLabel = gtk.Label("Interval (ie. break) between imports in seconds:")
|
||||
self.settingsHBox.pack_start(self.intervalLabel)
|
||||
self.intervalLabel.show()
|
||||
|
||||
self.intervalEntry=gtk.Entry()
|
||||
self.intervalEntry.set_text(str(self.config.get_import_parameters().get("interval")))
|
||||
self.settingsHBox.pack_start(self.intervalEntry)
|
||||
self.intervalEntry.show()
|
||||
self.intervalEntry=gtk.Entry()
|
||||
self.intervalEntry.set_text(str(self.config.get_import_parameters().get("interval")))
|
||||
self.settingsHBox.pack_start(self.intervalEntry)
|
||||
self.intervalEntry.show()
|
||||
|
||||
self.addSites(self.mainVBox)
|
||||
self.addSites(self.mainVBox)
|
||||
|
||||
self.startButton=gtk.Button("Start Autoimport")
|
||||
self.startButton.connect("clicked", self.startClicked, "start clicked")
|
||||
self.mainVBox.add(self.startButton)
|
||||
self.startButton.show()
|
||||
self.doAutoImportBool = False
|
||||
self.startButton=gtk.ToggleButton("Start Autoimport")
|
||||
self.startButton.connect("clicked", self.startClicked, "start clicked")
|
||||
self.mainVBox.add(self.startButton)
|
||||
self.startButton.show()
|
||||
|
||||
|
||||
#end of GuiAutoImport.__init__
|
||||
def browseClicked(self, widget, data):
|
||||
"""runs when user clicks one of the browse buttons in the auto import tab"""
|
||||
current_path=data[1].get_text()
|
||||
#end of GuiAutoImport.__init__
|
||||
def browseClicked(self, widget, data):
|
||||
"""runs when user clicks one of the browse buttons in the auto import tab"""
|
||||
current_path=data[1].get_text()
|
||||
|
||||
dia_chooser = gtk.FileChooserDialog(title="Please choose the path that you want to auto import",
|
||||
action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
|
||||
buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
|
||||
#dia_chooser.set_current_folder(pathname)
|
||||
dia_chooser.set_filename(current_path)
|
||||
#dia_chooser.set_select_multiple(select_multiple) #not in tv, but want this in bulk import
|
||||
dia_chooser = gtk.FileChooserDialog(title="Please choose the path that you want to auto import",
|
||||
action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
|
||||
buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
|
||||
#dia_chooser.set_current_folder(pathname)
|
||||
dia_chooser.set_filename(current_path)
|
||||
#dia_chooser.set_select_multiple(select_multiple) #not in tv, but want this in bulk import
|
||||
|
||||
response = dia_chooser.run()
|
||||
if response == gtk.RESPONSE_OK:
|
||||
#print dia_chooser.get_filename(), 'selected'
|
||||
data[1].set_text(dia_chooser.get_filename())
|
||||
self.input_settings[data[0]][0] = dia_chooser.get_filename()
|
||||
elif response == gtk.RESPONSE_CANCEL:
|
||||
print 'Closed, no files selected'
|
||||
dia_chooser.destroy()
|
||||
#end def GuiAutoImport.browseClicked
|
||||
response = dia_chooser.run()
|
||||
if response == gtk.RESPONSE_OK:
|
||||
#print dia_chooser.get_filename(), 'selected'
|
||||
data[1].set_text(dia_chooser.get_filename())
|
||||
self.input_settings[data[0]][0] = dia_chooser.get_filename()
|
||||
elif response == gtk.RESPONSE_CANCEL:
|
||||
print 'Closed, no files selected'
|
||||
dia_chooser.destroy()
|
||||
#end def GuiAutoImport.browseClicked
|
||||
|
||||
def do_import(self):
|
||||
"""Callback for timer to do an import iteration."""
|
||||
self.importer.runUpdated()
|
||||
print "GuiAutoImport.import_dir done"
|
||||
return True
|
||||
def do_import(self):
|
||||
"""Callback for timer to do an import iteration."""
|
||||
if self.doAutoImportBool:
|
||||
self.importer.runUpdated()
|
||||
print "GuiAutoImport.import_dir done"
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def startClicked(self, widget, data):
|
||||
"""runs when user clicks start on auto import tab"""
|
||||
def startClicked(self, widget, data):
|
||||
"""runs when user clicks start on auto import tab"""
|
||||
|
||||
# Check to see if we have an open file handle to the HUD and open one if we do not.
|
||||
# bufsize = 1 means unbuffered
|
||||
# We need to close this file handle sometime.
|
||||
# Check to see if we have an open file handle to the HUD and open one if we do not.
|
||||
# bufsize = 1 means unbuffered
|
||||
# We need to close this file handle sometime.
|
||||
|
||||
# TODO: Allow for importing from multiple dirs - REB 29AUG2008
|
||||
# As presently written this function does nothing if there is already a pipe open.
|
||||
# That is not correct. It should open another dir for importing while piping the
|
||||
# results to the same pipe. This means that self.path should be a a list of dirs
|
||||
# to watch.
|
||||
try: #uhhh, I don't this this is the best way to check for the existence of an attr
|
||||
getattr(self, "pipe_to_hud")
|
||||
except AttributeError:
|
||||
if os.name == 'nt':
|
||||
command = "python HUD_main.py" + " %s" % (self.database)
|
||||
bs = 0 # windows is not happy with line buffing here
|
||||
self.pipe_to_hud = subprocess.Popen(command, bufsize = bs, stdin = subprocess.PIPE,
|
||||
universal_newlines=True)
|
||||
else:
|
||||
command = self.config.execution_path('HUD_main.py')
|
||||
bs = 1
|
||||
self.pipe_to_hud = subprocess.Popen((command, self.database), bufsize = bs, stdin = subprocess.PIPE,
|
||||
universal_newlines=True)
|
||||
# self.pipe_to_hud = subprocess.Popen((command, self.database), bufsize = bs, stdin = subprocess.PIPE,
|
||||
# universal_newlines=True)
|
||||
# command = command + " %s" % (self.database)
|
||||
# print "command = ", command
|
||||
# self.pipe_to_hud = os.popen(command, 'w')
|
||||
# TODO: Allow for importing from multiple dirs - REB 29AUG2008
|
||||
# As presently written this function does nothing if there is already a pipe open.
|
||||
# That is not correct. It should open another dir for importing while piping the
|
||||
# results to the same pipe. This means that self.path should be a a list of dirs
|
||||
# to watch.
|
||||
if widget.get_active(): # toggled on
|
||||
self.doAutoImportBool = True
|
||||
widget.set_label(u'Stop Autoimport')
|
||||
if self.pipe_to_hud is None:
|
||||
if os.name == 'nt':
|
||||
command = "python HUD_main.py" + " %s" % (self.database)
|
||||
bs = 0 # windows is not happy with line buffing here
|
||||
self.pipe_to_hud = subprocess.Popen(command, bufsize = bs, stdin = subprocess.PIPE,
|
||||
universal_newlines=True)
|
||||
else:
|
||||
command = self.config.execution_path('HUD_main.py')
|
||||
bs = 1
|
||||
self.pipe_to_hud = subprocess.Popen((command, self.database), bufsize = bs, stdin = subprocess.PIPE,
|
||||
universal_newlines=True)
|
||||
# self.pipe_to_hud = subprocess.Popen((command, self.database), bufsize = bs, stdin = subprocess.PIPE,
|
||||
# universal_newlines=True)
|
||||
# command = command + " %s" % (self.database)
|
||||
# print "command = ", command
|
||||
# self.pipe_to_hud = os.popen(command, 'w')
|
||||
|
||||
# Add directories to importer object.
|
||||
for site in self.input_settings:
|
||||
self.importer.addImportDirectory(self.input_settings[site][0], True, site, self.input_settings[site][1])
|
||||
print "Adding import directories - Site: " + site + " dir: "+ str(self.input_settings[site][0])
|
||||
self.do_import()
|
||||
# Add directories to importer object.
|
||||
for site in self.input_settings:
|
||||
self.importer.addImportDirectory(self.input_settings[site][0], True, site, self.input_settings[site][1])
|
||||
print "Adding import directories - Site: " + site + " dir: "+ str(self.input_settings[site][0])
|
||||
self.do_import()
|
||||
|
||||
interval=int(self.intervalEntry.get_text())
|
||||
gobject.timeout_add(interval*1000, self.do_import)
|
||||
#end def GuiAutoImport.startClicked
|
||||
interval=int(self.intervalEntry.get_text())
|
||||
gobject.timeout_add(interval*1000, self.do_import)
|
||||
else: # toggled off
|
||||
self.doAutoImportBool = False # do_import will return this and stop the gobject callback timer
|
||||
print "Stopping autoimport"
|
||||
print >>self.pipe_to_hud.stdin, "\n"
|
||||
#self.pipe_to_hud.communicate('\n') # waits for process to terminate
|
||||
self.pipe_to_hud = None
|
||||
self.startButton.set_label(u'Start Autoimport')
|
||||
|
||||
def get_vbox(self):
|
||||
"""returns the vbox of this thread"""
|
||||
return self.mainVBox
|
||||
#end def get_vbox
|
||||
|
||||
#Create the site line given required info and setup callbacks
|
||||
#enabling and disabling sites from this interface not possible
|
||||
#expects a box to layout the line horizontally
|
||||
def createSiteLine(self, hbox, site, iconpath, hhpath, filter_name, active = True):
|
||||
label = gtk.Label(site + " auto-import:")
|
||||
hbox.pack_start(label, False, False, 0)
|
||||
label.show()
|
||||
|
||||
dirPath=gtk.Entry()
|
||||
dirPath.set_text(hhpath)
|
||||
hbox.pack_start(dirPath, False, True, 0)
|
||||
dirPath.show()
|
||||
#end def GuiAutoImport.startClicked
|
||||
|
||||
browseButton=gtk.Button("Browse...")
|
||||
browseButton.connect("clicked", self.browseClicked, [site] + [dirPath])
|
||||
hbox.pack_start(browseButton, False, False, 0)
|
||||
browseButton.show()
|
||||
def get_vbox(self):
|
||||
"""returns the vbox of this thread"""
|
||||
return self.mainVBox
|
||||
#end def get_vbox
|
||||
|
||||
label = gtk.Label(site + " filter:")
|
||||
hbox.pack_start(label, False, False, 0)
|
||||
label.show()
|
||||
#Create the site line given required info and setup callbacks
|
||||
#enabling and disabling sites from this interface not possible
|
||||
#expects a box to layout the line horizontally
|
||||
def createSiteLine(self, hbox, site, iconpath, hhpath, filter_name, active = True):
|
||||
label = gtk.Label(site + " auto-import:")
|
||||
hbox.pack_start(label, False, False, 0)
|
||||
label.show()
|
||||
|
||||
filter=gtk.Entry()
|
||||
filter.set_text(filter_name)
|
||||
hbox.pack_start(filter, False, True, 0)
|
||||
filter.show()
|
||||
dirPath=gtk.Entry()
|
||||
dirPath.set_text(hhpath)
|
||||
hbox.pack_start(dirPath, False, True, 0)
|
||||
dirPath.show()
|
||||
|
||||
def addSites(self, vbox):
|
||||
for site in self.config.supported_sites.keys():
|
||||
pathHBox = gtk.HBox(False, 0)
|
||||
vbox.pack_start(pathHBox, False, True, 0)
|
||||
pathHBox.show()
|
||||
browseButton=gtk.Button("Browse...")
|
||||
browseButton.connect("clicked", self.browseClicked, [site] + [dirPath])
|
||||
hbox.pack_start(browseButton, False, False, 0)
|
||||
browseButton.show()
|
||||
|
||||
paths = self.config.get_default_paths(site)
|
||||
params = self.config.get_site_parameters(site)
|
||||
self.createSiteLine(pathHBox, site, False, paths['hud-defaultPath'], params['converter'], params['enabled'])
|
||||
self.input_settings[site] = [paths['hud-defaultPath']] + [params['converter']]
|
||||
label = gtk.Label(site + " filter:")
|
||||
hbox.pack_start(label, False, False, 0)
|
||||
label.show()
|
||||
|
||||
filter=gtk.Entry()
|
||||
filter.set_text(filter_name)
|
||||
hbox.pack_start(filter, False, True, 0)
|
||||
filter.show()
|
||||
|
||||
def addSites(self, vbox):
|
||||
for site in self.config.supported_sites.keys():
|
||||
pathHBox = gtk.HBox(False, 0)
|
||||
vbox.pack_start(pathHBox, False, True, 0)
|
||||
pathHBox.show()
|
||||
|
||||
paths = self.config.get_default_paths(site)
|
||||
params = self.config.get_site_parameters(site)
|
||||
self.createSiteLine(pathHBox, site, False, paths['hud-defaultPath'], params['converter'], params['enabled'])
|
||||
self.input_settings[site] = [paths['hud-defaultPath']] + [params['converter']]
|
||||
|
||||
if __name__== "__main__":
|
||||
def destroy(*args): # call back for terminating the main eventloop
|
||||
|
|
|
@ -50,22 +50,27 @@ class GuiGraphViewer (threading.Thread):
|
|||
try: self.canvas.destroy()
|
||||
except AttributeError: pass
|
||||
|
||||
# Whaich sites are selected?
|
||||
# TODO:
|
||||
# What hero names for the selected site?
|
||||
# TODO:
|
||||
sitenos = []
|
||||
playerids = []
|
||||
|
||||
name = self.heroes[self.sites]
|
||||
# Which sites are selected?
|
||||
for site in self.sites:
|
||||
if self.sites[site] == True:
|
||||
sitenos.append(self.siteid[site])
|
||||
self.cursor.execute(self.sql.query['getPlayerId'], (self.heroes[site],))
|
||||
result = self.db.cursor.fetchall()
|
||||
if len(result) == 1:
|
||||
playerids.append(result[0][0])
|
||||
|
||||
if self.sites == "PokerStars":
|
||||
site=2
|
||||
sitename="PokerStars: "
|
||||
elif self.sites=="Full Tilt":
|
||||
site=1
|
||||
sitename="Full Tilt: "
|
||||
else:
|
||||
print "invalid text in site selection in graph, defaulting to PS"
|
||||
site=2
|
||||
if sitenos == []:
|
||||
#Should probably pop up here.
|
||||
print "No sites selected - defaulting to PokerStars"
|
||||
sitenos = [2]
|
||||
|
||||
|
||||
if playerids == []:
|
||||
print "No player ids found"
|
||||
return
|
||||
|
||||
self.fig = Figure(figsize=(5,4), dpi=100)
|
||||
|
||||
|
@ -74,7 +79,7 @@ class GuiGraphViewer (threading.Thread):
|
|||
|
||||
#Get graph data from DB
|
||||
starttime = time()
|
||||
line = self.getRingProfitGraph(name, site)
|
||||
line = self.getRingProfitGraph(playerids, sitenos)
|
||||
print "Graph generated in: %s" %(time() - starttime)
|
||||
|
||||
self.ax.set_title("Profit graph for ring games")
|
||||
|
@ -87,7 +92,8 @@ class GuiGraphViewer (threading.Thread):
|
|||
#TODO: Do something useful like alert user
|
||||
print "No hands returned by graph query"
|
||||
else:
|
||||
text = "All Hands, " + sitename + str(name) + "\nProfit: $" + str(line[-1]) + "\nTotal Hands: " + str(len(line))
|
||||
# text = "All Hands, " + sitename + str(name) + "\nProfit: $" + str(line[-1]) + "\nTotal Hands: " + str(len(line))
|
||||
text = "All Hands, " + "\nProfit: $" + str(line[-1]) + "\nTotal Hands: " + str(len(line))
|
||||
|
||||
self.ax.annotate(text,
|
||||
xy=(10, -10),
|
||||
|
@ -103,8 +109,34 @@ class GuiGraphViewer (threading.Thread):
|
|||
self.canvas.show()
|
||||
#end of def showClicked
|
||||
|
||||
def getRingProfitGraph(self, name, site):
|
||||
self.cursor.execute(self.sql.query['getRingProfitAllHandsPlayerIdSite'], (name, site))
|
||||
def getRingProfitGraph(self, names, sites):
|
||||
tmp = self.sql.query['getRingProfitAllHandsPlayerIdSite']
|
||||
# print "DEBUG: getRingProfitGraph"
|
||||
start_date, end_date = self.__get_dates()
|
||||
|
||||
if start_date == '':
|
||||
start_date = '1970-01-01'
|
||||
if end_date == '':
|
||||
end_date = '2020-12-12'
|
||||
|
||||
#Buggered if I can find a way to do this 'nicely' take a list of intergers and longs
|
||||
# and turn it into a tuple readale by sql.
|
||||
# [5L] into (5) not (5,) and [5L, 2829L] into (5, 2829)
|
||||
nametest = str(tuple(names))
|
||||
sitetest = str(tuple(sites))
|
||||
nametest = nametest.replace("L", "")
|
||||
nametest = nametest.replace(",)",")")
|
||||
sitetest = sitetest.replace(",)",")")
|
||||
|
||||
#Must be a nicer way to deal with tuples of size 1 ie. (2,) - which makes sql barf
|
||||
tmp = tmp.replace("<player_test>", nametest)
|
||||
tmp = tmp.replace("<site_test>", sitetest)
|
||||
tmp = tmp.replace("<startdate_test>", start_date)
|
||||
tmp = tmp.replace("<enddate_test>", end_date)
|
||||
|
||||
# print "DEBUG: sql query:"
|
||||
# print tmp
|
||||
self.cursor.execute(tmp)
|
||||
#returns (HandId,Winnings,Costs,Profit)
|
||||
winnings = self.db.cursor.fetchall()
|
||||
|
||||
|
@ -125,7 +157,6 @@ class GuiGraphViewer (threading.Thread):
|
|||
pname.set_text(player)
|
||||
pname.set_width_chars(20)
|
||||
hbox.pack_start(pname, False, True, 0)
|
||||
#TODO: Need to connect a callback here
|
||||
pname.connect("changed", self.__set_hero_name, site)
|
||||
#TODO: Look at GtkCompletion - to fill out usernames
|
||||
pname.show()
|
||||
|
@ -134,7 +165,7 @@ class GuiGraphViewer (threading.Thread):
|
|||
|
||||
def __set_hero_name(self, w, site):
|
||||
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):
|
||||
cb = gtk.CheckButton(site)
|
||||
|
@ -144,8 +175,9 @@ class GuiGraphViewer (threading.Thread):
|
|||
|
||||
def __set_site_select(self, w, site):
|
||||
# This doesn't behave as intended - self.site only allows 1 site for the moment.
|
||||
self.sites = site
|
||||
print "self.sites set to %s" %(self.sites)
|
||||
print w.get_active()
|
||||
self.sites[site] = w.get_active()
|
||||
print "self.sites[%s] set to %s" %(site, self.sites[site])
|
||||
|
||||
def fillPlayerFrame(self, vbox):
|
||||
for site in self.conf.supported_sites.keys():
|
||||
|
@ -162,6 +194,13 @@ class GuiGraphViewer (threading.Thread):
|
|||
vbox.pack_start(hbox, False, True, 0)
|
||||
hbox.show()
|
||||
self.createSiteLine(hbox, site)
|
||||
#Get db site id for filtering later
|
||||
self.cursor.execute(self.sql.query['getSiteId'], (site,))
|
||||
result = self.db.cursor.fetchall()
|
||||
if len(result) == 1:
|
||||
self.siteid[site] = result[0][0]
|
||||
else:
|
||||
print "Either 0 or more than one site matched - EEK"
|
||||
|
||||
def fillDateFrame(self, vbox):
|
||||
# Hat tip to Mika Bostrom - calendar code comes from PokerStats
|
||||
|
@ -261,7 +300,8 @@ class GuiGraphViewer (threading.Thread):
|
|||
self.sql=querylist
|
||||
self.conf = config
|
||||
|
||||
self.sites = "PokerStars"
|
||||
self.sites = {}
|
||||
self.siteid = {}
|
||||
self.heroes = {}
|
||||
|
||||
# For use in date ranges.
|
||||
|
|
|
@ -60,7 +60,7 @@ class GuiPlayerStats (threading.Thread):
|
|||
vbox.add(self.stats_table)
|
||||
|
||||
# Create header row
|
||||
titles = ("Game", "Hands", "VPIP", "PFR", "saw_f", "sawsd", "wtsdwsf", "wmsd", "FlAFq", "TuAFq", "RvAFq", "PFAFq", "Net($)", "BB/100", "$/hand", "Variance")
|
||||
titles = ("Game", "Hands", "VPIP", "PFR", "Saw_F", "SawSD", "WtSDwsF", "W$SD", "FlAFq", "TuAFq", "RvAFq", "PoFAFq", "Net($)", "BB/100", "$/hand", "Variance")
|
||||
|
||||
col = 0
|
||||
row = 0
|
||||
|
@ -71,14 +71,17 @@ class GuiPlayerStats (threading.Thread):
|
|||
col +=1
|
||||
|
||||
for row in range(rows-1):
|
||||
if(row%2 == 0):
|
||||
bgcolor = "white"
|
||||
else:
|
||||
bgcolor = "lightgrey"
|
||||
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][col])
|
||||
if result[row][col]:
|
||||
l = gtk.Label(result[row][col])
|
||||
else:
|
||||
l = gtk.Label(' ')
|
||||
if col == 0:
|
||||
l.set_alignment(xalign=0.0, yalign=0.5)
|
||||
else:
|
||||
|
@ -127,7 +130,6 @@ class GuiPlayerStats (threading.Thread):
|
|||
|
||||
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
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
<location seat="2" x="10" y="288"> </location>
|
||||
</layout>
|
||||
</site>
|
||||
<site enabled="True" site_name="Full Tilt" table_finder="FullTiltPoker.exe" screen_name="ENTER HERO NAME" site_path="~/.wine/drive_c/Program Files/Full Tilt Poker/" HH_path="~/.wine/drive_c/Program Files/Full Tilt Poker/HandHistory/abc/" decoder="fulltilt_decode_table" converter="passthrough" supported_games="holdem,razz,omahahi,omahahilo,studhi,studhilo">
|
||||
<site enabled="True" site_name="Full Tilt Poker" table_finder="FullTiltPoker.exe" screen_name="ENTER HERO NAME" site_path="~/.wine/drive_c/Program Files/Full Tilt Poker/" HH_path="~/.wine/drive_c/Program Files/Full Tilt Poker/HandHistory/abc/" decoder="fulltilt_decode_table" converter="passthrough" supported_games="holdem,razz,omahahi,omahahilo,studhi,studhilo">
|
||||
<layout fav_seat="0" height="547" max="8" width="794">
|
||||
<location seat="1" x="640" y="64"> </location>
|
||||
<location seat="2" x="650" y="230"> </location>
|
||||
|
|
294
pyfpdb/Hand.py
294
pyfpdb/Hand.py
|
@ -27,18 +27,19 @@ import xml.dom.minidom
|
|||
import codecs
|
||||
from decimal import Decimal
|
||||
import operator
|
||||
from time import time
|
||||
import time
|
||||
from copy import deepcopy
|
||||
|
||||
class Hand:
|
||||
# def __init__(self, sitename, gametype, sb, bb, string):
|
||||
|
||||
UPS = {'a':'A', 't':'T', 'j':'J', 'q':'Q', 'k':'K'}
|
||||
UPS = {'a':'A', 't':'T', 'j':'J', 'q':'Q', 'k':'K', 'S':'s', 'C':'c', 'H':'h', 'D':'d'}
|
||||
def __init__(self, sitename, gametype, string):
|
||||
self.sitename = sitename
|
||||
self.gametype = gametype
|
||||
self.string = string
|
||||
|
||||
self.streetList = ['BLINDS','PREFLOP','FLOP','TURN','RIVER'] # a list of the observed street names in order
|
||||
self.streetList = ['PREFLOP','FLOP','TURN','RIVER'] # a list of the observed street names in order
|
||||
|
||||
self.handid = 0
|
||||
self.sb = gametype[3]
|
||||
|
@ -78,6 +79,8 @@ class Hand:
|
|||
# dict from player names to lists of hole cards
|
||||
self.holecards = {}
|
||||
|
||||
self.stacks = {}
|
||||
|
||||
# dict from player names to amounts collected
|
||||
self.collected = {}
|
||||
|
||||
|
@ -87,6 +90,8 @@ class Hand:
|
|||
|
||||
self.action = []
|
||||
self.totalpot = None
|
||||
self.totalcollected = None
|
||||
|
||||
self.rake = None
|
||||
|
||||
self.bets = {}
|
||||
|
@ -104,37 +109,49 @@ chips (string) the chips the player has at the start of the hand (can be None)
|
|||
If a player has None chips he won't be added."""
|
||||
if chips is not None:
|
||||
self.players.append([seat, name, chips])
|
||||
self.holecards[name] = []
|
||||
self.stacks[name] = Decimal(chips)
|
||||
self.holecards[name] = set()
|
||||
for street in self.streetList:
|
||||
self.bets[street][name] = []
|
||||
|
||||
|
||||
def addStreets(self, match):
|
||||
# go through m and initialise actions to empty list for each street.
|
||||
if match is not None:
|
||||
self.streets = match
|
||||
for street in match.groupdict():
|
||||
if match.group(street) is not None:
|
||||
self.actions[street] = []
|
||||
|
||||
else:
|
||||
print "empty markStreets match" # better to raise exception and put process hand in a try block
|
||||
|
||||
def addHoleCards(self, cards, player):
|
||||
"""\
|
||||
Assigns observed holecards to a player.
|
||||
cards list of card bigrams e.g. ['2h','jc']
|
||||
cards set of card bigrams e.g. set(['2h','Jc'])
|
||||
player (string) name of player
|
||||
hand
|
||||
Note, will automatically uppercase the rank letter.
|
||||
"""
|
||||
print "DEBUG: addHoleCards", cards,player
|
||||
try:
|
||||
self.checkPlayerExists(player)
|
||||
self.holecards[player] = set([self.card(c) for c in cards])
|
||||
cards = set([self.card(c) for c in cards])
|
||||
self.holecards[player].update(cards)
|
||||
except FpdbParseError, e:
|
||||
print "Tried to add holecards for unknown player: %s" % (player,)
|
||||
print "[ERROR] Tried to add holecards for unknown player: %s" % (player,)
|
||||
|
||||
def addShownCards(self, cards, player, holeandboard=None):
|
||||
"""\
|
||||
For when a player shows cards for any reason (for showdown or out of choice).
|
||||
Card ranks will be uppercased
|
||||
"""
|
||||
print "DEBUG: addShownCards", cards,player,holeandboard
|
||||
if cards is not None:
|
||||
self.shown.add(player)
|
||||
self.addHoleCards(cards,player)
|
||||
elif holeandboard is not None:
|
||||
holeandboard = set([self.card(c) for c in holeandboard])
|
||||
board = set([c for s in self.board.values() for c in s])
|
||||
#print board
|
||||
#print holeandboard
|
||||
#print holeandboard.difference(board)
|
||||
self.addHoleCards(holeandboard.difference(board),player)
|
||||
|
||||
|
||||
|
@ -150,7 +167,7 @@ For when a player shows cards for any reason (for showdown or out of choice).
|
|||
except FpdbParseError, e:
|
||||
pass
|
||||
except ValueError:
|
||||
print "tried to discard a card %s didn't have" % (player,)
|
||||
print "[ERROR] discardHoleCard tried to discard a card %s didn't have" % (player,)
|
||||
|
||||
def setCommunityCards(self, street, cards):
|
||||
self.board[street] = [self.card(c) for c in cards]
|
||||
|
@ -161,11 +178,19 @@ For when a player shows cards for any reason (for showdown or out of choice).
|
|||
c = c.replace(k,v)
|
||||
return c
|
||||
|
||||
def addBlind(self, player, amount):
|
||||
def addBlind(self, player, blindtype, amount):
|
||||
# if player is None, it's a missing small blind.
|
||||
print "DEBUG addBlind: %s posts %s, %s" % (player, blindtype, amount)
|
||||
if player is not None:
|
||||
self.bets['PREFLOP'][player].append(Decimal(amount))
|
||||
self.lastBet['PREFLOP'] = Decimal(amount)
|
||||
self.stacks[player] -= Decimal(amount)
|
||||
#print "DEBUG %s posts, stack %s" % (player, self.stacks[player])
|
||||
self.actions['PREFLOP'] += [(player, 'posts', blindtype, amount, self.stacks[player]==0)]
|
||||
if blindtype == 'big blind':
|
||||
self.lastBet['PREFLOP'] = Decimal(amount)
|
||||
elif blindtype == 'small & big blinds':
|
||||
# extra small blind is 'dead'
|
||||
self.lastBet['PREFLOP'] = Decimal(self.bb)
|
||||
self.posted += [player]
|
||||
|
||||
|
||||
|
@ -175,52 +200,99 @@ For when a player shows cards for any reason (for showdown or out of choice).
|
|||
if amount is not None:
|
||||
self.bets[street][player].append(Decimal(amount))
|
||||
#self.lastBet[street] = Decimal(amount)
|
||||
self.actions[street] += [[player, 'calls', amount]]
|
||||
self.stacks[player] -= Decimal(amount)
|
||||
print "DEBUG %s calls %s, stack %s" % (player, amount, self.stacks[player])
|
||||
self.actions[street] += [(player, 'calls', amount, self.stacks[player]==0)]
|
||||
|
||||
def addRaiseBy(self, street, player, amountBy):
|
||||
"""\
|
||||
Add a raise by amountBy on [street] by [player]
|
||||
"""
|
||||
#Given only the amount raised by, 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)
|
||||
# let Bp = previous bet
|
||||
# Bc = amount player has committed so far
|
||||
# Rb = raise by
|
||||
# then: C = Bp - Bc (amount to call)
|
||||
# Rt = Bp + Rb (raise to)
|
||||
#
|
||||
self.checkPlayerExists(player)
|
||||
Rb = Decimal(amountBy)
|
||||
Bp = self.lastBet[street]
|
||||
Bc = reduce(operator.add, self.bets[street][player], 0)
|
||||
C = Bp - Bc
|
||||
Rt = Bp + Rb
|
||||
|
||||
self.bets[street][player].append(C + Rb)
|
||||
self.stacks[player] -= (C + Rb)
|
||||
self.actions[street] += [(player, 'raises', Rb, Rt, C, self.stacks[player]==0)]
|
||||
self.lastBet[street] = Rt
|
||||
|
||||
def addCallandRaise(self, street, player, amount):
|
||||
"""\
|
||||
For sites which by "raises x" mean "calls and raises putting a total of x in the por". """
|
||||
self.checkPlayerExists(player)
|
||||
CRb = Decimal(amount)
|
||||
Bp = self.lastBet[street]
|
||||
Bc = reduce(operator.add, self.bets[street][player], 0)
|
||||
C = Bp - Bc
|
||||
Rb = CRb - C
|
||||
Rt = Bp + Rb
|
||||
|
||||
self._addRaise(street, player, C, Rb, Rt)
|
||||
|
||||
def _addRaise(self, street, player, C, Rb, Rt):
|
||||
self.bets[street][player].append(C + Rb)
|
||||
self.stacks[player] -= (C + Rb)
|
||||
self.actions[street] += [(player, 'raises', Rb, Rt, C, self.stacks[player]==0)]
|
||||
self.lastBet[street] = Rt
|
||||
|
||||
def addRaiseTo(self, street, player, amountTo):
|
||||
"""\
|
||||
Add a raise on [street] by [player] to [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)
|
||||
self.checkPlayerExists(player)
|
||||
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]]
|
||||
Bc = reduce(operator.add, self.bets[street][player], 0)
|
||||
Rt = Decimal(amountTo)
|
||||
C = Bp - Bc
|
||||
Rb = Rt - C
|
||||
self._addRaise(street, player, C, Rb, Rt)
|
||||
|
||||
|
||||
def addBet(self, street, player, amount):
|
||||
self.checkPlayerExists(player)
|
||||
self.bets[street][player].append(Decimal(amount))
|
||||
self.actions[street] += [[player, 'bets', amount]]
|
||||
self.stacks[player] -= Decimal(amount)
|
||||
print "DEBUG %s bets %s, stack %s" % (player, amount, self.stacks[player])
|
||||
self.actions[street] += [(player, 'bets', amount, self.stacks[player]==0)]
|
||||
self.lastBet[street] = Decimal(amount)
|
||||
|
||||
|
||||
def addFold(self, street, player):
|
||||
print "DEBUG: %s %s folded" % (street, player)
|
||||
self.checkPlayerExists(player)
|
||||
self.folded.add(player)
|
||||
self.actions[street] += [[player, 'folds']]
|
||||
self.actions[street] += [(player, 'folds')]
|
||||
|
||||
def addCheck(self, street, player):
|
||||
print "DEBUG: %s %s checked" % (street, player)
|
||||
self.checkPlayerExists(player)
|
||||
self.actions[street] += [[player, 'checks']]
|
||||
self.actions[street] += [(player, 'checks')]
|
||||
|
||||
def addCollectPot(self,player, pot):
|
||||
print "DEBUG: %s collected %s" % (player, pot)
|
||||
self.checkPlayerExists(player)
|
||||
if player not in self.collected:
|
||||
self.collected[player] = pot
|
||||
else:
|
||||
# possibly lines like "p collected $ from pot" appear during the showdown
|
||||
# but they are usually unique in the summary, so it's best to try to get them from there.
|
||||
print "%s collected pot more than once; avoidable by reading winnings only from summary lines?"
|
||||
print "[WARNING] %s collected pot more than once; avoidable by reading winnings only from summary lines?"
|
||||
|
||||
|
||||
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 all bets and blinds have been added, totals up the total pot size"""
|
||||
if self.totalpot is None:
|
||||
self.totalpot = 0
|
||||
|
||||
|
@ -231,6 +303,63 @@ Known bug: doesn't take into account side pots"""
|
|||
#print street, self.bets[street][player]
|
||||
self.totalpot += reduce(operator.add, self.bets[street][player], 0)
|
||||
|
||||
print "DEBUG conventional totalpot:", self.totalpot
|
||||
|
||||
|
||||
self.totalpot = 0
|
||||
|
||||
for street in self.actions:
|
||||
uncalled = 0
|
||||
calls = [0]
|
||||
for act in self.actions[street]:
|
||||
if act[1] == 'bets': # [name, 'bets', amount]
|
||||
self.totalpot += Decimal(act[2])
|
||||
uncalled = Decimal(act[2]) # only the last bet or raise can be uncalled
|
||||
calls = [0]
|
||||
print "uncalled: ", uncalled
|
||||
elif act[1] == 'raises': # [name, 'raises', amountby, amountto, amountcalled]
|
||||
print "calls %s and raises %s to %s" % (act[4],act[2],act[3])
|
||||
self.totalpot += Decimal(act[2]) + Decimal(act[4])
|
||||
calls = [0]
|
||||
uncalled = Decimal(act[2])
|
||||
print "uncalled: ", uncalled
|
||||
elif act[1] == 'calls': # [name, 'calls', amount]
|
||||
self.totalpot += Decimal(act[2])
|
||||
calls = calls + [Decimal(act[2])]
|
||||
print "calls:", calls
|
||||
elif act[1] == 'posts':
|
||||
self.totalpot += Decimal(act[3])
|
||||
if act[2] == 'big blind':
|
||||
# the bb gets called by out-of-blinds posts; but sb+bb only calls bb
|
||||
if uncalled == Decimal(act[3]): # a bb is already posted
|
||||
calls = calls + [Decimal(act[3])]
|
||||
elif 0 < uncalled < Decimal(act[3]): # a sb is already posted, btw wow python can do a<b<c.
|
||||
# treat this as tho called & raised
|
||||
calls = [0]
|
||||
uncalled = Decimal(act[3]) - uncalled
|
||||
else: # no blind yet posted.
|
||||
uncalled = Decimal(act[3])
|
||||
elif act[2] == 'small blind':
|
||||
uncalled = Decimal(act[3])
|
||||
calls = [0]
|
||||
pass
|
||||
|
||||
if uncalled > 0 and max(calls+[0]) < uncalled:
|
||||
|
||||
print "DEBUG returning some bet, calls:", calls
|
||||
print "DEBUG returned: %.2f from %.2f" % ((uncalled - max(calls)), self.totalpot,)
|
||||
self.totalpot -= (uncalled - max(calls))
|
||||
print "DEBUG new totalpot:", self.totalpot
|
||||
|
||||
|
||||
if self.totalcollected is None:
|
||||
self.totalcollected = 0;
|
||||
for amount in self.collected.values():
|
||||
self.totalcollected += Decimal(amount)
|
||||
|
||||
|
||||
|
||||
|
||||
def getGameTypeAsString(self):
|
||||
"""\
|
||||
Map the tuple self.gametype onto the pokerstars string describing it
|
||||
|
@ -258,80 +387,88 @@ Map the tuple self.gametype onto the pokerstars string describing it
|
|||
|
||||
return string
|
||||
|
||||
def printHand(self):
|
||||
def writeHand(self, fh=sys.__stdout__):
|
||||
# PokerStars format.
|
||||
print "\n### Pseudo stars format ###"
|
||||
print "%s Game #%s: %s ($%s/$%s) - %s" %(self.sitename, self.handid, self.getGameTypeAsString(), self.sb, self.bb, self.starttime)
|
||||
print "Table '%s' %d-max Seat #%s is the button" %(self.tablename, self.maxseats, self.buttonpos)
|
||||
for player in self.players:
|
||||
print "Seat %s: %s ($%s)" %(player[0], player[1], player[2])
|
||||
#print "\n### Pseudo stars format ###"
|
||||
#print >>fh, _("%s Game #%s: %s ($%s/$%s) - %s" %(self.sitename, self.handid, self.getGameTypeAsString(), self.sb, self.bb, self.starttime))
|
||||
print >>fh, _("%s Game #%s: %s ($%s/$%s) - %s" %("PokerStars", self.handid, self.getGameTypeAsString(), self.sb, self.bb, time.strftime('%Y/%m/%d - %H:%M:%S (ET)', self.starttime)))
|
||||
print >>fh, _("Table '%s' %d-max Seat #%s is the button" %(self.tablename, self.maxseats, self.buttonpos))
|
||||
|
||||
players_who_act_preflop = set([x[0] for x in self.actions['PREFLOP']])
|
||||
#print players_who_act_preflop
|
||||
#print [x[1] for x in self.players]
|
||||
#print [x for x in self.players if x[1] in players_who_act_preflop]
|
||||
for player in [x for x in self.players if x[1] in players_who_act_preflop]:
|
||||
#Only print stacks of players who do something preflop
|
||||
print >>fh, _("Seat %s: %s ($%s)" %(player[0], player[1], player[2]))
|
||||
|
||||
if(self.posted[0] is None):
|
||||
print "No small blind posted"
|
||||
#print >>fh, _("No small blind posted") # PS doesn't say this
|
||||
pass
|
||||
else:
|
||||
print "%s: posts small blind $%s" %(self.posted[0], self.sb)
|
||||
print >>fh, _("%s: posts small blind $%s" %(self.posted[0], self.sb))
|
||||
|
||||
#May be more than 1 bb posting
|
||||
for a in self.posted[1:]:
|
||||
print "%s: posts big blind $%s" %(self.posted[1], self.bb)
|
||||
print >>fh, _("%s: posts big blind $%s" %(self.posted[1], self.bb))
|
||||
|
||||
# What about big & small blinds?
|
||||
# TODO: What about big & small blinds?
|
||||
|
||||
print "*** HOLE CARDS ***"
|
||||
print >>fh, _("*** HOLE CARDS ***")
|
||||
if self.involved:
|
||||
print "Dealt to %s [%s]" %(self.hero , " ".join(self.holecards[self.hero]))
|
||||
print >>fh, _("Dealt to %s [%s]" %(self.hero , " ".join(self.holecards[self.hero])))
|
||||
|
||||
if 'PREFLOP' in self.actions:
|
||||
for act in self.actions['PREFLOP']:
|
||||
self.printActionLine(act)
|
||||
self.printActionLine(act, fh)
|
||||
|
||||
if 'FLOP' in self.actions:
|
||||
print "*** FLOP *** [%s]" %( " ".join(self.board['Flop']))
|
||||
print >>fh, _("*** FLOP *** [%s]" %( " ".join(self.board['FLOP'])))
|
||||
for act in self.actions['FLOP']:
|
||||
self.printActionLine(act)
|
||||
self.printActionLine(act, fh)
|
||||
|
||||
if 'TURN' in self.actions:
|
||||
print "*** TURN *** [%s] [%s]" %( " ".join(self.board['Flop']), " ".join(self.board['Turn']))
|
||||
print >>fh, _("*** TURN *** [%s] [%s]" %( " ".join(self.board['FLOP']), " ".join(self.board['TURN'])))
|
||||
for act in self.actions['TURN']:
|
||||
self.printActionLine(act)
|
||||
self.printActionLine(act, fh)
|
||||
|
||||
if 'RIVER' in self.actions:
|
||||
print "*** RIVER *** [%s] [%s]" %(" ".join(self.board['Flop']+self.board['Turn']), " ".join(self.board['River']) )
|
||||
print >>fh, _("*** RIVER *** [%s] [%s]" %(" ".join(self.board['FLOP']+self.board['TURN']), " ".join(self.board['RIVER']) ))
|
||||
for act in self.actions['RIVER']:
|
||||
self.printActionLine(act)
|
||||
self.printActionLine(act, fh)
|
||||
|
||||
|
||||
#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
|
||||
# we probably don't need a showdown section in pseudo stars format for our filtering purposes
|
||||
if 'SHOWDOWN' in self.actions:
|
||||
print "*** SHOW DOWN ***"
|
||||
print "what do they show"
|
||||
print >>fh, _("*** SHOW DOWN ***")
|
||||
print >>fh, "DEBUG: what do they show"
|
||||
|
||||
print >>fh, _("*** SUMMARY ***")
|
||||
print >>fh, _("Total pot $%s | Rake $%.2f" % (self.totalpot, self.rake)) # TODO: side pots
|
||||
|
||||
print "*** SUMMARY ***"
|
||||
print "Total pot $%s | Rake $%.2f)" % (self.totalpot, self.rake) # TODO side pots
|
||||
board = []
|
||||
for s in self.board.values():
|
||||
board += s
|
||||
if board: # sometimes hand ends preflop without a board
|
||||
print "Board [%s]" % (" ".join(board))
|
||||
print >>fh, _("Board [%s]" % (" ".join(board)))
|
||||
|
||||
|
||||
for player in self.players:
|
||||
for player in [x for x in self.players if x[1] in players_who_act_preflop]:
|
||||
seatnum = player[0]
|
||||
name = player[1]
|
||||
if name in self.collected and self.holecards[name]:
|
||||
print "Seat %d: %s showed [%s] and won ($%s)" % (seatnum, name, " ".join(self.holecards[name]), self.collected[name])
|
||||
if name in self.collected and name in self.shown:
|
||||
print >>fh, _("Seat %d: %s showed [%s] and won ($%s)" % (seatnum, name, " ".join(self.holecards[name]), self.collected[name]))
|
||||
elif name in self.collected:
|
||||
print "Seat %d: %s collected ($%s)" % (seatnum, name, self.collected[name])
|
||||
elif player[1] in self.shown:
|
||||
print "Seat %d: %s showed [%s]" % (seatnum, name, " ".join(self.holecards[name]))
|
||||
elif player[1] in self.folded:
|
||||
print "Seat %d: %s folded" % (seatnum, name)
|
||||
print >>fh, _("Seat %d: %s collected ($%s)" % (seatnum, name, self.collected[name]))
|
||||
elif name in self.shown:
|
||||
print >>fh, _("Seat %d: %s showed [%s]" % (seatnum, name, " ".join(self.holecards[name])))
|
||||
elif name in self.folded:
|
||||
print >>fh, _("Seat %d: %s folded" % (seatnum, name))
|
||||
else:
|
||||
print "Seat %d: %s mucked" % (seatnum, name)
|
||||
print >>fh, _("Seat %d: %s mucked" % (seatnum, name))
|
||||
|
||||
print
|
||||
print >>fh, "\n\n"
|
||||
# TODO:
|
||||
# logic for side pots
|
||||
# logic for which players get to showdown
|
||||
|
@ -348,15 +485,20 @@ Map the tuple self.gametype onto the pokerstars string describing it
|
|||
#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])
|
||||
def printHand(self):
|
||||
self.writeHand(sys.stdout)
|
||||
|
||||
def printActionLine(self, act, fh):
|
||||
if act[1] == 'folds':
|
||||
print >>fh, _("%s: folds " %(act[0]))
|
||||
elif act[1] == 'checks':
|
||||
print >>fh, _("%s: checks " %(act[0]))
|
||||
if act[1] == 'calls':
|
||||
print "%s: %s $%s" %(act[0], act[1], act[2])
|
||||
print >>fh, _("%s: calls $%s%s" %(act[0], act[2], ' and is all-in' if act[3] else ''))
|
||||
if act[1] == 'bets':
|
||||
print "%s: %s $%s" %(act[0], act[1], act[2])
|
||||
print >>fh, _("%s: bets $%s%s" %(act[0], act[2], ' and is all-in' if act[3] else ''))
|
||||
if act[1] == 'raises':
|
||||
print "%s: %s $%s to $%s" %(act[0], act[1], act[2], act[3])
|
||||
print >>fh, _("%s: raises $%s to $%s%s" %(act[0], act[2], act[3], ' and is all-in' if act[5] else ''))
|
||||
|
||||
# going to use pokereval to figure out hands at some point.
|
||||
# these functions are copied from pokergame.py
|
||||
|
|
|
@ -29,7 +29,10 @@ from decimal import Decimal
|
|||
import operator
|
||||
from xml.dom.minidom import Node
|
||||
from pokereval import PokerEval
|
||||
from time import time
|
||||
import time
|
||||
import datetime
|
||||
import gettext
|
||||
|
||||
#from pokerengine.pokercards import *
|
||||
# provides letter2name{}, letter2names{}, visible_card(), not_visible_card(), is_visible(), card_value(), class PokerCards
|
||||
# but it's probably not installed so here are the ones we may want:
|
||||
|
@ -65,6 +68,11 @@ letter2names = {
|
|||
'2': 'Deuces'
|
||||
}
|
||||
|
||||
import gettext
|
||||
gettext.install('myapplication')
|
||||
|
||||
|
||||
|
||||
class HandHistoryConverter:
|
||||
eval = PokerEval()
|
||||
def __init__(self, config, file, sitename):
|
||||
|
@ -97,7 +105,7 @@ class HandHistoryConverter:
|
|||
return tmp
|
||||
|
||||
def processFile(self):
|
||||
starttime = time()
|
||||
starttime = time.time()
|
||||
if not self.sanityCheck():
|
||||
print "Cowardly refusing to continue after failed sanity check"
|
||||
return
|
||||
|
@ -108,30 +116,35 @@ class HandHistoryConverter:
|
|||
print "\nInput:\n"+hand.string
|
||||
self.readHandInfo(hand)
|
||||
self.readPlayerStacks(hand)
|
||||
print "DEBUG stacks:", hand.stacks
|
||||
self.markStreets(hand)
|
||||
self.readBlinds(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():
|
||||
|
||||
# Read actions in street order
|
||||
for street in hand.streetList: # go through them in order
|
||||
if hand.streets.group(street) is not None:
|
||||
self.readCommunityCards(hand, street) # read community cards
|
||||
self.readAction(hand, street)
|
||||
|
||||
|
||||
self.readCollectPot(hand)
|
||||
self.readShownCards(hand)
|
||||
|
||||
# finalise it (total the pot)
|
||||
hand.totalPot()
|
||||
self.getRake(hand)
|
||||
|
||||
hand.printHand()
|
||||
hand.writeHand(sys.stderr)
|
||||
#if(hand.involved == True):
|
||||
#self.writeHand("output file", hand)
|
||||
#hand.printHand()
|
||||
#else:
|
||||
#pass #Don't write out observed hands
|
||||
|
||||
endtime = time()
|
||||
endtime = time.time()
|
||||
print "Processed %d hands in %d seconds" % (len(self.hands), endtime-starttime)
|
||||
|
||||
#####
|
||||
|
@ -161,7 +174,8 @@ class HandHistoryConverter:
|
|||
def readPlayerStacks(self, hand): abstract
|
||||
|
||||
# Needs to return a MatchObject with group names identifying the streets into the Hand object
|
||||
# that is, pulls the chunks of preflop, flop, turn and river text into hand.streets MatchObject.
|
||||
# so groups are called by street names 'PREFLOP', 'FLOP', 'STREET2' etc
|
||||
# blinds are done seperately
|
||||
def markStreets(self, hand): abstract
|
||||
|
||||
#Needs to return a list in the format
|
||||
|
@ -171,13 +185,16 @@ class HandHistoryConverter:
|
|||
def readHeroCards(self, hand): abstract
|
||||
def readAction(self, hand, street): abstract
|
||||
def readCollectPot(self, hand): abstract
|
||||
def readShownCards(self, hand): abstract
|
||||
|
||||
# Some sites don't report the rake. This will be called at the end of the hand after the pot total has been calculated
|
||||
# so that an inheriting class can calculate it for the specific site if need be.
|
||||
def getRake(self, hand): abstract
|
||||
# an inheriting class can calculate it for the specific site if need be.
|
||||
def getRake(self, hand):
|
||||
hand.rake = hand.totalpot - hand.totalcollected # * Decimal('0.05') # probably not quite right
|
||||
|
||||
|
||||
def sanityCheck(self):
|
||||
sane = True
|
||||
sane = False
|
||||
base_w = False
|
||||
#Check if hhbase exists and is writable
|
||||
#Note: Will not try to create the base HH directory
|
||||
|
|
|
@ -129,6 +129,13 @@ class Hud:
|
|||
self.main_window.set_destroy_with_parent(True)
|
||||
|
||||
def update_table_position(self):
|
||||
# self.main_window.parentgdkhandle = gtk.gdk.window_foreign_new(self.table.number)
|
||||
# if self.main_window.parentgdkhandle == None:
|
||||
if os.name == 'nt':
|
||||
if not win32gui.IsWindow(self.table.number):
|
||||
self.kill_hud()
|
||||
return False
|
||||
|
||||
(x, y) = self.main_window.parentgdkhandle.get_origin()
|
||||
if self.table.x != x or self.table.y != y:
|
||||
self.table.x = x
|
||||
|
@ -234,7 +241,8 @@ class Hud:
|
|||
aux_params = config.get_aux_parameters(game_params['aux'])
|
||||
self.aux_windows.append(eval("%s.%s(gtk.Window(), self, config, aux_params)" % (aux_params['module'], aux_params['class'])))
|
||||
|
||||
# gobject.timeout_add(500, self.update_table_position)
|
||||
if os.name == "nt":
|
||||
gobject.timeout_add(500, self.update_table_position)
|
||||
|
||||
def update(self, hand, config, stat_dict):
|
||||
self.hand = hand # this is the last hand, so it is available later
|
||||
|
|
239
pyfpdb/OnGameToFpdb.py
Executable file
239
pyfpdb/OnGameToFpdb.py
Executable file
|
@ -0,0 +1,239 @@
|
|||
#!/usr/bin/env python
|
||||
# Copyright 2008, Carl Gherardi
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
########################################################################
|
||||
|
||||
import sys
|
||||
import Configuration
|
||||
from HandHistoryConverter import *
|
||||
|
||||
# OnGame HH Format
|
||||
|
||||
#Texas Hold'em $.5-$1 NL (real money), hand #P4-76915775-797
|
||||
#Table Kuopio, 20 Sep 2008 11:59 PM
|
||||
|
||||
#Seat 1: .Lucchess ($4.17 in chips)
|
||||
#Seat 3: Gloff1 ($108 in chips)
|
||||
#Seat 4: far a ($13.54 in chips)
|
||||
#Seat 5: helander2222 ($49.77 in chips)
|
||||
#Seat 6: lopllopl ($62.06 in chips)
|
||||
#Seat 7: crazyhorse6 ($101.91 in chips)
|
||||
#Seat 8: peeci ($25.02 in chips)
|
||||
#Seat 9: Manuelhertz ($49 in chips)
|
||||
#Seat 10: Eurolll ($58.25 in chips)
|
||||
#ANTES/BLINDS
|
||||
#helander2222 posts blind ($0.25), lopllopl posts blind ($0.50).
|
||||
|
||||
#PRE-FLOP
|
||||
#crazyhorse6 folds, peeci folds, Manuelhertz folds, Eurolll calls $0.50, .Lucchess calls $0.50, Gloff1 folds, far a folds, helander2222 folds, lopllopl checks.
|
||||
|
||||
#FLOP [board cards AH,8H,KH ]
|
||||
#lopllopl checks, Eurolll checks, .Lucchess checks.
|
||||
|
||||
#TURN [board cards AH,8H,KH,6S ]
|
||||
#lopllopl checks, Eurolll checks, .Lucchess checks.
|
||||
|
||||
#RIVER [board cards AH,8H,KH,6S,8S ]
|
||||
#lopllopl checks, Eurolll bets $1.25, .Lucchess folds, lopllopl folds.
|
||||
|
||||
#SHOWDOWN
|
||||
#Eurolll wins $2.92.
|
||||
|
||||
#SUMMARY
|
||||
#Dealer: far a
|
||||
#Pot: $3, (including rake: $0.08)
|
||||
#.Lucchess, loses $0.50
|
||||
#Gloff1, loses $0
|
||||
#far a, loses $0
|
||||
#helander2222, loses $0.25
|
||||
#lopllopl, loses $0.50
|
||||
#crazyhorse6, loses $0
|
||||
#peeci, loses $0
|
||||
#Manuelhertz, loses $0
|
||||
#Eurolll, bets $1.75, collects $2.92, net $1.17
|
||||
|
||||
|
||||
class OnGame(HandHistoryConverter):
|
||||
def __init__(self, config, file):
|
||||
print "Initialising OnGame converter class"
|
||||
HandHistoryConverter.__init__(self, config, file, sitename="OnGame") # Call super class init.
|
||||
self.sitename = "OnGame"
|
||||
self.setFileType("text", "cp1252")
|
||||
#self.rexx.setGameInfoRegex('.*Blinds \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+)')
|
||||
self.rexx.setSplitHandRegex('\n\n\n+')
|
||||
|
||||
#Texas Hold'em $.5-$1 NL (real money), hand #P4-76915775-797
|
||||
#Table Kuopio, 20 Sep 2008 11:59 PM
|
||||
self.rexx.setHandInfoRegex(r"Texas Hold'em \$?(?P<SB>[.0-9]+)-\$?(?P<BB>[.0-9]+) NL \(real money\), hand #(?P<HID>[-A-Z\d]+)\nTable\ (?P<TABLE>[\' \w]+), (?P<DATETIME>\d\d \w+ \d\d\d\d \d\d:\d\d (AM|PM))")
|
||||
# SB BB HID TABLE DAY MON YEAR HR12 MIN AMPM
|
||||
|
||||
self.rexx.button_re = re.compile('#SUMMARY\nDealer: (?P<BUTTONPNAME>.*)\n')
|
||||
|
||||
#Seat 1: .Lucchess ($4.17 in chips)
|
||||
self.rexx.setPlayerInfoRegex('Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \((\$(?P<CASH>[.0-9]+) in chips)\)')
|
||||
|
||||
#ANTES/BLINDS
|
||||
#helander2222 posts blind ($0.25), lopllopl posts blind ($0.50).
|
||||
self.rexx.setPostSbRegex('(?P<PNAME>.*) posts blind \(\$?(?P<SB>[.0-9]+)\), ')
|
||||
self.rexx.setPostBbRegex('\), (?P<PNAME>.*) posts blind \(\$?(?P<BB>[.0-9]+)\).')
|
||||
self.rexx.setPostBothRegex('.*\n(?P<PNAME>.*): posts small \& big blinds \[\$? (?P<SBBB>[.0-9]+)')
|
||||
self.rexx.setHeroCardsRegex('.*\nDealt\sto\s(?P<PNAME>.*)\s\[ (?P<CARDS>.*) \]')
|
||||
|
||||
#lopllopl checks, Eurolll checks, .Lucchess checks.
|
||||
self.rexx.setActionStepRegex('(, )?(?P<PNAME>.*?)(?P<ATYPE> bets| checks| raises| calls| folds)( \$(?P<BET>\d*\.?\d*))?( and is all-in)?')
|
||||
|
||||
#Uchilka shows [ KC,JD ]
|
||||
self.rexx.setShowdownActionRegex('(?P<PNAME>.*) shows \[ (?P<CARDS>.+) \]')
|
||||
|
||||
# TODO: read SUMMARY correctly for collected pot stuff.
|
||||
#Uchilka, bets $11.75, collects $23.04, net $11.29
|
||||
self.rexx.setCollectPotRegex('(?P<PNAME>.*), bets.+, collects \$(?P<POT>\d*\.?\d*), net.* ')
|
||||
self.rexx.sits_out_re = re.compile('(?P<PNAME>.*) sits out')
|
||||
self.rexx.compileRegexes()
|
||||
|
||||
def readSupportedGames(self):
|
||||
pass
|
||||
|
||||
def determineGameType(self):
|
||||
# Cheating with this regex, only support nlhe at the moment
|
||||
gametype = ["ring", "hold", "nl"]
|
||||
|
||||
m = self.rexx.hand_info_re.search(self.obs)
|
||||
gametype = gametype + [m.group('SB')]
|
||||
gametype = gametype + [m.group('BB')]
|
||||
|
||||
return gametype
|
||||
|
||||
def readHandInfo(self, hand):
|
||||
m = self.rexx.hand_info_re.search(hand.string)
|
||||
hand.handid = m.group('HID')
|
||||
hand.tablename = m.group('TABLE')
|
||||
#hand.buttonpos = self.rexx.button_re.search(hand.string).group('BUTTONPNAME')
|
||||
# These work, but the info is already in the Hand class - should be used for tourneys though.
|
||||
# m.group('SB')
|
||||
# m.group('BB')
|
||||
# m.group('GAMETYPE')
|
||||
|
||||
# Believe Everleaf time is GMT/UTC, no transation necessary
|
||||
# Stars format (Nov 10 2008): 2008/11/07 12:38:49 CET [2008/11/07 7:38:49 ET]
|
||||
# or : 2008/11/07 12:38:49 ET
|
||||
# Not getting it in my HH files yet, so using
|
||||
# 2008/11/10 3:58:52 ET
|
||||
#TODO: Do conversion from GMT to ET
|
||||
#TODO: Need some date functions to convert to different timezones (Date::Manip for perl rocked for this)
|
||||
|
||||
hand.starttime = time.strptime(m.group('DATETIME'), "%d %b %Y %I:%M %p")
|
||||
#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')))
|
||||
|
||||
def readPlayerStacks(self, hand):
|
||||
m = self.rexx.player_info_re.finditer(hand.string)
|
||||
players = []
|
||||
for a in m:
|
||||
hand.addPlayer(int(a.group('SEAT')), a.group('PNAME'), a.group('CASH'))
|
||||
|
||||
def markStreets(self, hand):
|
||||
# PREFLOP = ** Dealing down cards **
|
||||
# This re fails if, say, river is missing; then we don't get the ** that starts the river.
|
||||
#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)
|
||||
|
||||
m = re.search(r"PRE-FLOP(?P<PREFLOP>.+(?=FLOP)|.+(?=SHOWDOWN))"
|
||||
r"(FLOP (?P<FLOP>\[board cards .+ \].+(?=TURN)|.+(?=SHOWDOWN)))?"
|
||||
r"(TURN (?P<TURN>\[board cards .+ \].+(?=RIVER)|.+(?=SHOWDOWN)))?"
|
||||
r"(RIVER (?P<RIVER>\[board cards .+ \].+(?=SHOWDOWN)))?", hand.string,re.DOTALL)
|
||||
|
||||
hand.addStreets(m)
|
||||
|
||||
|
||||
def readCommunityCards(self, hand, street):
|
||||
self.rexx.board_re = re.compile(r"\[board cards (?P<CARDS>.+) \]")
|
||||
print hand.streets.group(street)
|
||||
if street in ('FLOP','TURN','RIVER'): # a list of streets which get dealt community cards (i.e. all but PREFLOP)
|
||||
m = self.rexx.board_re.search(hand.streets.group(street))
|
||||
hand.setCommunityCards(street, m.group('CARDS').split(','))
|
||||
|
||||
def readBlinds(self, hand):
|
||||
try:
|
||||
m = self.rexx.small_blind_re.search(hand.string)
|
||||
hand.addBlind(m.group('PNAME'), 'small blind', m.group('SB'))
|
||||
except: # no small blind
|
||||
hand.addBlind(None, None, None)
|
||||
for a in self.rexx.big_blind_re.finditer(hand.string):
|
||||
hand.addBlind(a.group('PNAME'), 'big blind', a.group('BB'))
|
||||
for a in self.rexx.both_blinds_re.finditer(hand.string):
|
||||
hand.addBlind(a.group('PNAME'), 'small & big blinds', a.group('SBBB'))
|
||||
|
||||
def readHeroCards(self, hand):
|
||||
m = self.rexx.hero_cards_re.search(hand.string)
|
||||
if(m == None):
|
||||
#Not involved in hand
|
||||
hand.involved = False
|
||||
else:
|
||||
hand.hero = m.group('PNAME')
|
||||
# "2c, qh" -> set(["2c","qc"])
|
||||
# Also works with Omaha hands.
|
||||
cards = m.group('CARDS')
|
||||
cards = set(cards.split(','))
|
||||
hand.addHoleCards(cards, m.group('PNAME'))
|
||||
|
||||
def readAction(self, hand, street):
|
||||
m = self.rexx.action_re.finditer(hand.streets.group(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') )
|
||||
elif action.group('ATYPE') == ' folds':
|
||||
hand.addFold( street, action.group('PNAME'))
|
||||
elif action.group('ATYPE') == ' checks':
|
||||
hand.addCheck( street, action.group('PNAME'))
|
||||
else:
|
||||
print "DEBUG: unimplemented readAction: %s %s" %(action.group('PNAME'),action.group('ATYPE'),)
|
||||
#hand.actions[street] += [[action.group('PNAME'), action.group('ATYPE')]]
|
||||
# TODO: Everleaf does not record uncalled bets.
|
||||
|
||||
def readShowdownActions(self, hand):
|
||||
for shows in self.rexx.showdown_action_re.finditer(hand.string):
|
||||
cards = shows.group('CARDS')
|
||||
cards = set(cards.split(','))
|
||||
hand.addShownCards(cards, shows.group('PNAME'))
|
||||
|
||||
def readCollectPot(self,hand):
|
||||
for m in self.rexx.collect_pot_re.finditer(hand.string):
|
||||
hand.addCollectPot(player=m.group('PNAME'),pot=m.group('POT'))
|
||||
|
||||
def readShownCards(self,hand):
|
||||
return
|
||||
#for m in self.rexx.collect_pot_re.finditer(hand.string):
|
||||
#if m.group('CARDS') is not None:
|
||||
#cards = m.group('CARDS')
|
||||
#cards = set(cards.split(','))
|
||||
#hand.addShownCards(cards=None, player=m.group('PNAME'), holeandboard=cards)
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
c = Configuration.Config()
|
||||
if len(sys.argv) == 1:
|
||||
testfile = "regression-test-files/ongame/nlhe/ong NLH handhq_0.txt"
|
||||
else:
|
||||
testfile = sys.argv[1]
|
||||
e = OnGame(c, testfile)
|
||||
e.processFile()
|
||||
print str(e)
|
|
@ -108,9 +108,9 @@ class fpdb_db:
|
|||
self.cursor.execute(self.sql.query['createHandsPlayersTable'])
|
||||
self.cursor.execute(self.sql.query['createHandsActionsTable'])
|
||||
self.cursor.execute(self.sql.query['createHudCacheTable'])
|
||||
self.cursor.execute(self.sql.query['addTourneyIndex'])
|
||||
self.cursor.execute(self.sql.query['addHandsIndex'])
|
||||
self.cursor.execute(self.sql.query['addPlayersIndex'])
|
||||
#self.cursor.execute(self.sql.query['addTourneyIndex'])
|
||||
#self.cursor.execute(self.sql.query['addHandsIndex'])
|
||||
#self.cursor.execute(self.sql.query['addPlayersIndex'])
|
||||
self.fillDefaultData()
|
||||
self.db.commit()
|
||||
#end def disconnect
|
||||
|
@ -177,6 +177,7 @@ class fpdb_db:
|
|||
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, 'PokerStars', 'USD');")
|
||||
self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'Everleaf', 'USD');")
|
||||
self.cursor.execute("INSERT INTO TourneyTypes VALUES (DEFAULT, 1, 0, 0, 0, False);")
|
||||
#end def fillDefaultData
|
||||
|
||||
|
@ -185,6 +186,7 @@ class fpdb_db:
|
|||
|
||||
self.drop_tables()
|
||||
self.create_tables()
|
||||
fpdb_simple.createAllIndexes(self)
|
||||
self.db.commit()
|
||||
print "Finished recreating tables"
|
||||
#end def recreate_tables
|
||||
|
|
|
@ -116,8 +116,11 @@ class Importer:
|
|||
|
||||
#Run full import on filelist
|
||||
def runImport(self):
|
||||
fpdb_simple.prepareBulkImport(self.fdb)
|
||||
for file in self.filelist:
|
||||
self.import_file_dict(file, self.filelist[file][0], self.filelist[file][1])
|
||||
fpdb_simple.afterBulkImport(self.fdb)
|
||||
fpdb_simple.analyzeDB(self.fdb)
|
||||
|
||||
#Run import on updated files, then store latest update time.
|
||||
def runUpdated(self):
|
||||
|
|
|
@ -22,6 +22,11 @@ from time import time
|
|||
|
||||
import fpdb_simple
|
||||
|
||||
saveActions=True # set this to False to avoid storing action data
|
||||
# Pros: speeds up imports
|
||||
# Cons: no action data is saved, so you need to keep the hand histories
|
||||
# variance not available on stats page
|
||||
|
||||
#stores a stud/razz hand into the database
|
||||
def ring_stud(backend, db, cursor, base, category, site_hand_no, gametype_id, hand_start_time
|
||||
,names, player_ids, start_cashes, antes, card_values, card_suits, winnings, rakes
|
||||
|
@ -39,8 +44,9 @@ def ring_stud(backend, db, cursor, base, category, site_hand_no, gametype_id, ha
|
|||
|
||||
fpdb_simple.storeHudCache(cursor, base, category, gametype_id, player_ids, hudImportData)
|
||||
|
||||
fpdb_simple.storeActions(cursor, hands_players_ids, action_types
|
||||
,allIns, action_amounts, actionNos)
|
||||
if saveActions:
|
||||
fpdb_simple.storeActions(cursor, hands_players_ids, action_types
|
||||
,allIns, action_amounts, actionNos)
|
||||
return hands_id
|
||||
#end def ring_stud
|
||||
|
||||
|
@ -66,10 +72,10 @@ def ring_holdem_omaha(backend, db, cursor, base, category, site_hand_no, gametyp
|
|||
t5 = time()
|
||||
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)
|
||||
if saveActions:
|
||||
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)
|
||||
#print "fills=(%4.3f) saves=(%4.3f,%4.3f,%4.3f,%4.3f)" % (t2-t0, t3-t2, t4-t3, t5-t4, t6-t5)
|
||||
return hands_id
|
||||
#end def ring_holdem_omaha
|
||||
|
||||
|
@ -98,7 +104,8 @@ def tourney_holdem_omaha(backend, db, cursor, base, category, siteTourneyNo, buy
|
|||
|
||||
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)
|
||||
if saveActions:
|
||||
fpdb_simple.storeActions(cursor, hands_players_ids, action_types, allIns, action_amounts, actionNos)
|
||||
return hands_id
|
||||
#end def tourney_holdem_omaha
|
||||
|
||||
|
@ -122,6 +129,7 @@ def tourney_stud(backend, db, cursor, base, category, siteTourneyNo, buyin, fee,
|
|||
|
||||
fpdb_simple.storeHudCache(cursor, base, category, gametypeId, playerIds, hudImportData)
|
||||
|
||||
fpdb_simple.storeActions(cursor, hands_players_ids, actionTypes, allIns, actionAmounts, actionNos)
|
||||
if saveActions:
|
||||
fpdb_simple.storeActions(cursor, hands_players_ids, actionTypes, allIns, actionAmounts, actionNos)
|
||||
return hands_id
|
||||
#end def tourney_stud
|
||||
|
|
304
pyfpdb/fpdb_simple.py
Executable file → Normal file
304
pyfpdb/fpdb_simple.py
Executable file → Normal file
|
@ -26,6 +26,308 @@ FTP=2
|
|||
MYSQL_INNODB=2
|
||||
PGSQL=3
|
||||
SQLITE=4
|
||||
# Data Structures for index and foreign key creation
|
||||
# drop_code is an int with possible values: 0 - don't drop for bulk import
|
||||
# 1 - drop during bulk import
|
||||
# db differences:
|
||||
# - note that mysql automatically creates indexes on constrained columns when
|
||||
# foreign keys are created, while postgres does not. Hence the much longer list
|
||||
# of indexes is required for postgres.
|
||||
# all primary keys are left on all the time
|
||||
#
|
||||
# table column drop_code
|
||||
|
||||
indexes = [
|
||||
[ ] # no db with index 0
|
||||
, [ ] # no db with index 1
|
||||
, [ # indexes for mysql (list index 2)
|
||||
{'tab':'Players', 'col':'name', 'drop':0}
|
||||
, {'tab':'Hands', 'col':'siteHandNo', 'drop':0}
|
||||
, {'tab':'Tourneys', 'col':'siteTourneyNo', 'drop':0}
|
||||
]
|
||||
, [ # indexes for postgres (list index 3)
|
||||
{'tab':'Boardcards', 'col':'handId', 'drop':0}
|
||||
, {'tab':'Gametypes', 'col':'siteId', 'drop':0}
|
||||
, {'tab':'Hands', 'col':'gametypeId', 'drop':1}
|
||||
, {'tab':'Hands', 'col':'siteHandNo', 'drop':0}
|
||||
, {'tab':'HandsActions', 'col':'handplayerId', 'drop':0}
|
||||
, {'tab':'HandsPlayers', 'col':'handId', 'drop':1}
|
||||
, {'tab':'HandsPlayers', 'col':'playerId', 'drop':1}
|
||||
, {'tab':'HandsPlayers', 'col':'tourneysPlayersId', 'drop':0}
|
||||
, {'tab':'HudCache', 'col':'gametypeId', 'drop':1}
|
||||
, {'tab':'HudCache', 'col':'playerId', 'drop':0}
|
||||
, {'tab':'HudCache', 'col':'tourneyTypeId', 'drop':0}
|
||||
, {'tab':'Players', 'col':'siteId', 'drop':1}
|
||||
, {'tab':'Players', 'col':'name', 'drop':0}
|
||||
, {'tab':'Tourneys', 'col':'tourneyTypeId', 'drop':1}
|
||||
, {'tab':'Tourneys', 'col':'siteTourneyNo', 'drop':0}
|
||||
, {'tab':'TourneysPlayers', 'col':'playerId', 'drop':0}
|
||||
, {'tab':'TourneysPlayers', 'col':'tourneyId', 'drop':0}
|
||||
, {'tab':'TourneyTypes', 'col':'siteId', 'drop':0}
|
||||
]
|
||||
]
|
||||
|
||||
foreignKeys = [
|
||||
[ ] # no db with index 0
|
||||
, [ ] # no db with index 1
|
||||
, [ # foreign keys for mysql
|
||||
{'fktab':'Hands', 'fkcol':'gametypeId', 'rtab':'Gametypes', 'rcol':'id', 'drop':1}
|
||||
, {'fktab':'HandsPlayers', 'fkcol':'handId', 'rtab':'Hands', 'rcol':'id', 'drop':1}
|
||||
, {'fktab':'HandsPlayers', 'fkcol':'playerId', 'rtab':'Players', 'rcol':'id', 'drop':1}
|
||||
, {'fktab':'HandsActions', 'fkcol':'handPlayerId', 'rtab':'HandsPlayers', 'rcol':'id', 'drop':1}
|
||||
, {'fktab':'HudCache', 'fkcol':'gametypeId', 'rtab':'Gametypes', 'rcol':'id', 'drop':1}
|
||||
, {'fktab':'HudCache', 'fkcol':'playerId', 'rtab':'Players', 'rcol':'id', 'drop':0}
|
||||
, {'fktab':'HudCache', 'fkcol':'tourneyTypeId', 'rtab':'TourneyTypes', 'rcol':'id', 'drop':1}
|
||||
]
|
||||
, [ # foreign keys for postgres
|
||||
{'fktab':'Hands', 'fkcol':'gametypeId', 'rtab':'Gametypes', 'rcol':'id', 'drop':1}
|
||||
, {'fktab':'HandsPlayers', 'fkcol':'handId', 'rtab':'Hands', 'rcol':'id', 'drop':1}
|
||||
, {'fktab':'HandsPlayers', 'fkcol':'playerId', 'rtab':'Players', 'rcol':'id', 'drop':1}
|
||||
, {'fktab':'HandsActions', 'fkcol':'handPlayerId', 'rtab':'HandsPlayers', 'rcol':'id', 'drop':1}
|
||||
, {'fktab':'HudCache', 'fkcol':'gametypeId', 'rtab':'Gametypes', 'rcol':'id', 'drop':1}
|
||||
, {'fktab':'HudCache', 'fkcol':'playerId', 'rtab':'Players', 'rcol':'id', 'drop':0}
|
||||
, {'fktab':'HudCache', 'fkcol':'tourneyTypeId', 'rtab':'TourneyTypes', 'rcol':'id', 'drop':1}
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
# MySQL Notes:
|
||||
# "FOREIGN KEY (handId) REFERENCES Hands(id)" - requires index on Hands.id
|
||||
# - creates index handId on <thistable>.handId
|
||||
# alter table t drop foreign key fk
|
||||
# alter table t add foreign key (fkcol) references tab(rcol)
|
||||
# alter table t add constraint c foreign key (fkcol) references tab(rcol)
|
||||
# (fkcol is used for foreigh key name)
|
||||
|
||||
# mysql to list indexes:
|
||||
# SELECT table_name, index_name, non_unique, column_name
|
||||
# FROM INFORMATION_SCHEMA.STATISTICS
|
||||
# WHERE table_name = 'tbl_name'
|
||||
# AND table_schema = 'db_name'
|
||||
# ORDER BY table_name, index_name, seq_in_index
|
||||
#
|
||||
# ALTER TABLE Tourneys ADD INDEX siteTourneyNo(siteTourneyNo)
|
||||
# ALTER TABLE tab DROP INDEX idx
|
||||
|
||||
# mysql to list fks:
|
||||
# SELECT constraint_name, table_name, column_name, referenced_table_name, referenced_column_name
|
||||
# FROM information_schema.KEY_COLUMN_USAGE
|
||||
# WHERE REFERENCED_TABLE_SCHEMA = (your schema name here)
|
||||
# AND REFERENCED_TABLE_NAME is not null
|
||||
# ORDER BY TABLE_NAME, COLUMN_NAME;
|
||||
|
||||
# this may indicate missing object
|
||||
# _mysql_exceptions.OperationalError: (1025, "Error on rename of '.\\fpdb\\hands' to '.\\fpdb\\#sql2-7f0-1b' (errno: 152)")
|
||||
|
||||
|
||||
# PG notes:
|
||||
|
||||
# To add a foreign key constraint to a table:
|
||||
# ALTER TABLE tab ADD CONSTRAINT c FOREIGN KEY (col) REFERENCES t2(col2) MATCH FULL;
|
||||
# ALTER TABLE tab DROP CONSTRAINT zipchk
|
||||
#
|
||||
# Note: index names must be unique across a schema
|
||||
# CREATE INDEX idx ON tab(col)
|
||||
# DROP INDEX idx
|
||||
|
||||
def prepareBulkImport(fdb):
|
||||
"""Drop some indexes/foreign keys to prepare for bulk import.
|
||||
Currently keeping the standalone indexes as needed to import quickly"""
|
||||
# fdb is a fpdb_db object including backend, db, cursor, sql variables
|
||||
if fdb.backend == PGSQL:
|
||||
fdb.db.set_isolation_level(0) # allow table/index operations to work
|
||||
for fk in foreignKeys[fdb.backend]:
|
||||
if fk['drop'] == 1:
|
||||
if fdb.backend == MYSQL_INNODB:
|
||||
fdb.cursor.execute("SELECT constraint_name " +
|
||||
"FROM information_schema.KEY_COLUMN_USAGE " +
|
||||
#"WHERE REFERENCED_TABLE_SCHEMA = 'fpdb'
|
||||
"WHERE 1=1 " +
|
||||
"AND table_name = %s AND column_name = %s " +
|
||||
"AND referenced_table_name = %s " +
|
||||
"AND referenced_column_name = %s ",
|
||||
(fk['fktab'], fk['fkcol'], fk['rtab'], fk['rcol']) )
|
||||
cons = fdb.cursor.fetchone()
|
||||
print "preparebulk: cons=", cons
|
||||
if cons:
|
||||
print "dropping mysql fk", cons[0], fk['fktab'], fk['fkcol']
|
||||
try:
|
||||
fdb.cursor.execute("alter table " + fk['fktab'] + " drop foreign key " + cons[0])
|
||||
except:
|
||||
pass
|
||||
elif fdb.backend == PGSQL:
|
||||
print "dropping pg fk", fk['fktab'], fk['fkcol']
|
||||
try:
|
||||
fdb.cursor.execute("alter table " + fk['fktab'] + " drop constraint "
|
||||
+ fk['fktab'] + '_' + fk['fkcol'] + '_fkey')
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
print "Only MySQL and Postgres supported so far"
|
||||
return -1
|
||||
|
||||
for idx in indexes[fdb.backend]:
|
||||
if idx['drop'] == 1:
|
||||
if fdb.backend == MYSQL_INNODB:
|
||||
print "dropping mysql index ", idx['tab'], idx['col']
|
||||
try:
|
||||
fdb.cursor.execute( "alter table %s drop index %s", (idx['tab'],idx['col']) )
|
||||
except:
|
||||
pass
|
||||
elif fdb.backend == PGSQL:
|
||||
print "dropping pg index ", idx['tab'], idx['col']
|
||||
# mod to use tab_col for index name?
|
||||
try:
|
||||
fdb.cursor.execute( "drop index %s_%s_idx" % (idx['tab'],idx['col']) )
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
print "Only MySQL and Postgres supported so far"
|
||||
return -1
|
||||
|
||||
if fdb.backend == PGSQL:
|
||||
fdb.db.set_isolation_level(1) # go back to normal isolation level
|
||||
fdb.db.commit() # seems to clear up errors if there were any in postgres
|
||||
#end def prepareBulkImport
|
||||
|
||||
def afterBulkImport(fdb):
|
||||
"""Re-create any dropped indexes/foreign keys after bulk import"""
|
||||
# fdb is a fpdb_db object including backend, db, cursor, sql variables
|
||||
if fdb.backend == PGSQL:
|
||||
fdb.db.set_isolation_level(0) # allow table/index operations to work
|
||||
for fk in foreignKeys[fdb.backend]:
|
||||
if fk['drop'] == 1:
|
||||
if fdb.backend == MYSQL_INNODB:
|
||||
fdb.cursor.execute("SELECT constraint_name " +
|
||||
"FROM information_schema.KEY_COLUMN_USAGE " +
|
||||
#"WHERE REFERENCED_TABLE_SCHEMA = 'fpdb'
|
||||
"WHERE 1=1 " +
|
||||
"AND table_name = %s AND column_name = %s " +
|
||||
"AND referenced_table_name = %s " +
|
||||
"AND referenced_column_name = %s ",
|
||||
(fk['fktab'], fk['fkcol'], fk['rtab'], fk['rcol']) )
|
||||
cons = fdb.cursor.fetchone()
|
||||
print "afterbulk: cons=", cons
|
||||
if cons:
|
||||
pass
|
||||
else:
|
||||
print "creating fk ", fk['fktab'], fk['fkcol'], "->", fk['rtab'], fk['rcol']
|
||||
try:
|
||||
fdb.cursor.execute("alter table " + fk['fktab'] + " add foreign key ("
|
||||
+ fk['fkcol'] + ") references " + fk['rtab'] + "("
|
||||
+ fk['rcol'] + ")")
|
||||
except:
|
||||
pass
|
||||
elif fdb.backend == PGSQL:
|
||||
print "creating fk ", fk['fktab'], fk['fkcol'], "->", fk['rtab'], fk['rcol']
|
||||
try:
|
||||
fdb.cursor.execute("alter table " + fk['fktab'] + " add constraint "
|
||||
+ fk['fktab'] + '_' + fk['fkcol'] + '_fkey'
|
||||
+ " foreign key (" + fk['fkcol']
|
||||
+ ") references " + fk['rtab'] + "(" + fk['rcol'] + ")")
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
print "Only MySQL and Postgres supported so far"
|
||||
return -1
|
||||
|
||||
for idx in indexes[fdb.backend]:
|
||||
if idx['drop'] == 1:
|
||||
if fdb.backend == MYSQL_INNODB:
|
||||
print "creating mysql index ", idx['tab'], idx['col']
|
||||
try:
|
||||
fdb.cursor.execute( "alter table %s add index %s(%s)"
|
||||
, (idx['tab'],idx['col'],idx['col']) )
|
||||
except:
|
||||
pass
|
||||
elif fdb.backend == PGSQL:
|
||||
# mod to use tab_col for index name?
|
||||
print "creating pg index ", idx['tab'], idx['col']
|
||||
try:
|
||||
print "create index %s_%s_idx on %s(%s)" % (idx['tab'], idx['col'], idx['tab'], idx['col'])
|
||||
fdb.cursor.execute( "create index %s_%s_idx on %s(%s)"
|
||||
% (idx['tab'], idx['col'], idx['tab'], idx['col']) )
|
||||
except:
|
||||
print " ERROR! :-("
|
||||
pass
|
||||
else:
|
||||
print "Only MySQL and Postgres supported so far"
|
||||
return -1
|
||||
|
||||
if fdb.backend == PGSQL:
|
||||
fdb.db.set_isolation_level(1) # go back to normal isolation level
|
||||
fdb.db.commit() # seems to clear up errors if there were any in postgres
|
||||
#end def afterBulkImport
|
||||
|
||||
def createAllIndexes(fdb):
|
||||
"""Create new indexes"""
|
||||
if fdb.backend == PGSQL:
|
||||
fdb.db.set_isolation_level(0) # allow table/index operations to work
|
||||
for idx in indexes[fdb.backend]:
|
||||
if fdb.backend == MYSQL_INNODB:
|
||||
print "creating mysql index ", idx['tab'], idx['col']
|
||||
try:
|
||||
fdb.cursor.execute( "alter table %s add index %s(%s)"
|
||||
, (idx['tab'],idx['col'],idx['col']) )
|
||||
except:
|
||||
pass
|
||||
elif fdb.backend == PGSQL:
|
||||
# mod to use tab_col for index name?
|
||||
print "creating pg index ", idx['tab'], idx['col']
|
||||
try:
|
||||
print "create index %s_%s_idx on %s(%s)" % (idx['tab'], idx['col'], idx['tab'], idx['col'])
|
||||
fdb.cursor.execute( "create index %s_%s_idx on %s(%s)"
|
||||
% (idx['tab'], idx['col'], idx['tab'], idx['col']) )
|
||||
except:
|
||||
print " ERROR! :-("
|
||||
pass
|
||||
else:
|
||||
print "Only MySQL and Postgres supported so far"
|
||||
return -1
|
||||
if fdb.backend == PGSQL:
|
||||
fdb.db.set_isolation_level(1) # go back to normal isolation level
|
||||
#end def createAllIndexes
|
||||
|
||||
def dropAllIndexes(fdb):
|
||||
"""Drop all standalone indexes (i.e. not including primary keys or foreign keys)
|
||||
using list of indexes in indexes data structure"""
|
||||
# maybe upgrade to use data dictionary?? (but take care to exclude PK and FK)
|
||||
if fdb.backend == PGSQL:
|
||||
fdb.db.set_isolation_level(0) # allow table/index operations to work
|
||||
for idx in indexes[fdb.backend]:
|
||||
if fdb.backend == MYSQL_INNODB:
|
||||
print "dropping mysql index ", idx['tab'], idx['col']
|
||||
try:
|
||||
fdb.cursor.execute( "alter table %s drop index %s"
|
||||
, (idx['tab'],idx['col']) )
|
||||
except:
|
||||
pass
|
||||
elif fdb.backend == PGSQL:
|
||||
print "dropping pg index ", idx['tab'], idx['col']
|
||||
# mod to use tab_col for index name?
|
||||
try:
|
||||
fdb.cursor.execute( "drop index %s_%s_idx"
|
||||
% (idx['tab'],idx['col']) )
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
print "Only MySQL and Postgres supported so far"
|
||||
return -1
|
||||
if fdb.backend == PGSQL:
|
||||
fdb.db.set_isolation_level(1) # go back to normal isolation level
|
||||
#end def dropAllIndexes
|
||||
|
||||
def analyzeDB(fdb):
|
||||
"""Do whatever the DB can offer to update index/table statistics"""
|
||||
if fdb.backend == PGSQL:
|
||||
fdb.db.set_isolation_level(0) # allow vacuum to work
|
||||
try:
|
||||
fdb.cursor.execute("vacuum analyze")
|
||||
except:
|
||||
print "Error during vacuum"
|
||||
fdb.db.set_isolation_level(1) # go back to normal isolation level
|
||||
#end def analyzeDB
|
||||
|
||||
|
||||
|
||||
class DuplicateError(Exception):
|
||||
|
@ -90,7 +392,7 @@ def checkPositions(positions):
|
|||
pass
|
||||
|
||||
### RHH modified to allow for "position 9" here (pos==9 is when you're a dead hand before the BB
|
||||
if (pos!="B" and pos!="S" and pos!=0 and pos!=1 and pos!=2 and pos!=3 and pos!=4 and pos!=5 and pos!=6 and pos!=7 and pos!=9):
|
||||
if (pos!="B" and pos!="S" and pos!=0 and pos!=1 and pos!=2 and pos!=3 and pos!=4 and pos!=5 and pos!=6 and pos!=7 and pos != 8 and pos!=9):
|
||||
raise FpdbError("invalid position found in checkPositions. i: "+str(i)+" position: "+str(pos))
|
||||
#end def fpdb_simple.checkPositions
|
||||
|
||||
|
|
Binary file not shown.
Loading…
Reference in New Issue
Block a user