Merge from ray, minor conflicts

This commit is contained in:
Matt Turnbull 2009-07-15 00:20:26 +01:00
commit 79ca83c95a
22 changed files with 1011 additions and 764 deletions

View File

@ -88,12 +88,32 @@ class Site:
self.font_size = node.getAttribute("font_size")
self.use_frames = node.getAttribute("use_frames")
self.enabled = fix_tf(node.getAttribute("enabled"), default = True)
self.xpad = node.getAttribute("xpad")
self.ypad = node.getAttribute("ypad")
self.layout = {}
for layout_node in node.getElementsByTagName('layout'):
lo = Layout(layout_node)
self.layout[lo.max] = lo
# Site defaults
if self.xpad == "": self.xpad = 1
else: self.xpad = int(self.xpad)
if self.ypad == "": self.ypad = 0
else: self.ypad = int(self.ypad)
if self.font_size == "": self.font_size = 7
else: self.font_size = int(self.font_size)
if self.hudopacity == "": self.hudopacity = 1.0
else: self.hudopacity = float(self.hudopacity)
if self.use_frames == "": self.use_frames = False
if self.font == "": self.font = "Sans"
if self.hudbgcolor == "": self.hudbgcolor = "000000"
if self.hudfgcolor == "": self.hudfgcolor = "FFFFFF"
def __str__(self):
temp = "Site = " + self.site_name + "\n"
for key in dir(self):
@ -119,9 +139,16 @@ class Stat:
class Game:
def __init__(self, node):
self.game_name = node.getAttribute("game_name")
self.db = node.getAttribute("db")
self.rows = int( node.getAttribute("rows") )
self.cols = int( node.getAttribute("cols") )
self.xpad = node.getAttribute("xpad")
self.ypad = node.getAttribute("ypad")
# Defaults
if self.xpad == "": self.xpad = 1
else: self.xpad = int(self.xpad)
if self.ypad == "": self.ypad = 0
else: self.ypad = int(self.ypad)
aux_text = node.getAttribute("aux")
aux_list = aux_text.split(',')
@ -146,9 +173,10 @@ class Game:
def __str__(self):
temp = "Game = " + self.game_name + "\n"
temp = temp + " db = %s\n" % self.db
temp = temp + " rows = %d\n" % self.rows
temp = temp + " cols = %d\n" % self.cols
temp = temp + " xpad = %d\n" % self.xpad
temp = temp + " ypad = %d\n" % self.ypad
temp = temp + " aux = %s\n" % self.aux
for stat in self.stats.keys():
@ -633,6 +661,8 @@ class Config:
parms["font"] = self.supported_sites[site].font
parms["font_size"] = self.supported_sites[site].font_size
parms["enabled"] = self.supported_sites[site].enabled
parms["xpad"] = self.supported_sites[site].xpad
parms["ypad"] = self.supported_sites[site].ypad
return parms
def set_site_parameters(self, site_name, converter = None, decoder = None,
@ -683,9 +713,10 @@ class Config:
param = {}
if self.supported_games.has_key(name):
param['game_name'] = self.supported_games[name].game_name
param['db'] = self.supported_games[name].db
param['rows'] = self.supported_games[name].rows
param['cols'] = self.supported_games[name].cols
param['xpad'] = self.supported_games[name].xpad
param['ypad'] = self.supported_games[name].ypad
param['aux'] = self.supported_games[name].aux
return param

View File

@ -39,7 +39,8 @@ import SQL
import Card
class Database:
def __init__(self, c, db_name, game):
def __init__(self, c, db_name = None, game = None, sql = None): # db_name and game not used any more
print "\ncreating Database instance, sql =", sql
self.fdb = fpdb_db.fpdb_db() # sets self.fdb.db self.fdb.cursor and self.fdb.sql
self.fdb.do_connect(c)
self.connection = self.fdb.db
@ -48,7 +49,12 @@ class Database:
self.import_options = c.get_import_parameters()
self.type = db_params['db-type']
self.backend = db_params['db-backend']
self.sql = SQL.Sql(game = game, type = self.type, db_server = db_params['db-server'])
self.db_server = db_params['db-server']
# where possible avoid creating new SQL instance by using the global one passed in
if sql == None:
self.sql = SQL.Sql(type = self.type, db_server = db_params['db-server'])
else:
self.sql = sql
self.connection.rollback()
# To add to config:
@ -81,6 +87,8 @@ class Database:
#row = cur.fetchone()
self.saveActions = False if self.import_options['saveActions'] == False else True
def do_connect(self, c):
self.fdb.do_connect(c)
def commit(self):
self.fdb.db.commit()
@ -88,6 +96,19 @@ class Database:
def close_connection(self):
self.connection.close()
def disconnect(self, due_to_error=False):
"""Disconnects the DB (rolls back if param is true, otherwise commits"""
self.fdb.disconnect(due_to_error)
def reconnect(self, due_to_error=False):
"""Reconnects the DB"""
self.fdb.reconnect(due_to_error=False)
def get_backend_name(self):
"""Reconnects the DB"""
return self.fdb.get_backend_name()
def get_table_name(self, hand_id):
c = self.connection.cursor()
c.execute(self.sql.query['get_table_name'], (hand_id, ))
@ -138,12 +159,8 @@ class Database:
cards = {}
c = self.connection.cursor()
c.execute(self.sql.query['get_common_cards'], [hand])
colnames = [desc[0] for desc in c.description]
for row in c.fetchall():
s_dict = {}
for name, val in zip(colnames, row):
s_dict[name] = val
cards['common'] = (self.convert_cards(s_dict))
# row = c.fetchone()
cards['common'] = c.fetchone()
return cards
def convert_cards(self, d):
@ -285,7 +302,8 @@ class Database:
fpdb_simple.fillCardArrays(len(names), base, category, card_values, card_suits)
hands_id = fpdb_simple.storeHands(self.backend, db, cursor, site_hand_no, gametype_id
,hand_start_time, names, tableName, maxSeats, hudImportData)
,hand_start_time, names, tableName, maxSeats, hudImportData
,(None, None, None, None, None), (None, None, None, None, None))
#print "before calling store_hands_players_stud, antes:", antes
hands_players_ids = fpdb_simple.store_hands_players_stud(self.backend, db, cursor, hands_id, player_ids
@ -326,7 +344,6 @@ class Database:
if 'updateHudCache' not in settings or settings['updateHudCache'] != 'drop':
fpdb_simple.storeHudCache(self.backend, cursor, base, category, gametype_id, hand_start_time, player_ids, hudImportData)
t5 = time()
fpdb_simple.store_board_cards(cursor, hands_id, board_values, board_suits)
t6 = time()
if self.saveActions:
fpdb_simple.storeActions(cursor, hands_players_ids, action_types, allIns, action_amounts, actionNos)
@ -361,8 +378,6 @@ class Database:
if 'updateHudCache' not in settings or settings['updateHudCache'] != 'drop':
fpdb_simple.storeHudCache(self.backend, cursor, base, category, gametype_id, hand_start_time, player_ids, hudImportData)
fpdb_simple.store_board_cards(cursor, hands_id, board_values, board_suits)
if self.saveActions:
fpdb_simple.storeActions(cursor, hands_players_ids, action_types, allIns, action_amounts, actionNos)
return hands_id

View File

@ -30,12 +30,11 @@ import fpdb_db
import FpdbSQLQueries
class Filters(threading.Thread):
def __init__(self, db, settings, config, qdict, display = {},debug=True):
def __init__(self, db, config, qdict, display = {}, debug=True):
self.debug=debug
#print "start of GraphViewer constructor"
self.db=db
self.cursor=db.cursor
self.settings=settings
self.sql=qdict
self.conf = config
self.display = display
@ -235,7 +234,7 @@ class Filters(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: setting heroes[%s]: %s"%(site, self.heroes[site])
def createSiteLine(self, hbox, site):
cb = gtk.CheckButton(site)
@ -556,23 +555,12 @@ def main(argv=None):
config = Configuration.Config()
db = None
settings = {}
settings.update(config.get_db_parameters())
settings.update(config.get_tv_parameters())
settings.update(config.get_import_parameters())
settings.update(config.get_default_paths())
db = fpdb_db.fpdb_db()
db.connect(settings['db-backend'],
settings['db-host'],
settings['db-databaseName'],
settings['db-user'],
settings['db-password'])
db.do_connect(config)
qdict = FpdbSQLQueries.FpdbSQLQueries(db.get_backend_name())
i = Filters(db, settings, config, qdict)
i = Filters(db, config, qdict)
main_window = gtk.Window()
main_window.connect('destroy', destroy)
main_window.add(i.get_vbox())

View File

@ -282,43 +282,6 @@ class FpdbSQLQueries:
FOREIGN KEY(gametypeId) REFERENCES Gametypes(id) ON DELETE CASCADE)"""
################################
# Create BoardCards
################################
if(self.dbname == 'MySQL InnoDB'):
self.query['createBoardCardsTable'] = """CREATE TABLE BoardCards (
id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
handId BIGINT UNSIGNED NOT NULL, FOREIGN KEY (handId) REFERENCES Hands(id),
card1Value smallint NOT NULL,
card1Suit char(1) NOT NULL,
card2Value smallint NOT NULL,
card2Suit char(1) NOT NULL,
card3Value smallint NOT NULL,
card3Suit char(1) NOT NULL,
card4Value smallint NOT NULL,
card4Suit char(1) NOT NULL,
card5Value smallint NOT NULL,
card5Suit char(1) NOT NULL)
ENGINE=INNODB"""
elif(self.dbname == 'PostgreSQL'):
self.query['createBoardCardsTable'] = """CREATE TABLE BoardCards (
id BIGSERIAL, PRIMARY KEY (id),
handId BIGINT, FOREIGN KEY (handId) REFERENCES Hands(id),
card1Value smallint,
card1Suit char(1),
card2Value smallint,
card2Suit char(1),
card3Value smallint,
card3Suit char(1),
card4Value smallint,
card4Suit char(1),
card5Value smallint,
card5Suit char(1))"""
elif(self.dbname == 'SQLite'):
self.query['createBoardCardsTable'] = """ """
################################
# Create TourneyTypes
################################
@ -1598,136 +1561,6 @@ class FpdbSQLQueries:
elif(self.dbname == 'SQLite'):
self.query['playerStatsByPosition'] = """ """
if(self.dbname == 'MySQL InnoDB'):
self.query['playerStatsByPositionAndHoleCards'] = """
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*/,hprof2.n
/*,stats.vpip*/,0
/*,stats.pfr*/,0
/*,stats.saw_f*/,0
/*,stats.sawsd*/,0
/*,stats.wtsdwsf*/,0
/*,stats.wmsd*/,0
/*,stats.FlAFq*/,0
/*,stats.TuAFq*/,0
/*,stats.RvAFq*/,0
/*,stats.PoFAFq*/,0
/* 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)*/,0
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 '-'
else format(100.0*sum(sawShowdown)/sum(street1Seen),1)
end AS wtsdwsf
,case when sum(sawShowdown) = 0 then '-'
end AS wtsdwsf
,case when sum(sawShowdown) = 0 then '-'
else format(100.0*sum(wonAtSD)/sum(sawShowdown),1)
end AS wmsd
,case when sum(street1Seen) = 0 then '-'
else format(100.0*sum(street1Aggr)/sum(street1Seen),1)
end AS FlAFq
,case when sum(street2Seen) = 0 then '-'
else format(100.0*sum(street2Aggr)/sum(street2Seen),1)
end AS TuAFq
,case when sum(street3Seen) = 0 then '-'
else format(100.0*sum(street3Aggr)/sum(street3Seen),1)
end AS RvAFq
,case when sum(street1Seen)+sum(street2Seen)+sum(street3Seen) = 0 then '-'
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,
count(*) as n
from
(select hp.handId, h.gameTypeId, hp.position, hp.winnings, SUM(ha.amount) as costs
, hp.winnings - SUM(ha.amount) as profit
from HandsPlayers hp
inner join Hands h ON h.id = hp.handId
left join HandsActions ha ON ha.handsPlayerId = hp.id
where hp.playerId in <player_test>
# use <gametype_test> here ?
and hp.tourneysPlayersId IS NULL
and ((hp.card1Value = <first_card> and hp.card2Value = <second_card>) or (hp.card1Value = <second_card> and hp.card2Value = <first_card>))
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)
"""
if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL') or (self.dbname == 'SQLite'):
self.query['getGames'] = """SELECT DISTINCT category from Gametypes"""

View File

@ -43,13 +43,13 @@ import Filters
class GuiGraphViewer (threading.Thread):
def __init__(self, db, settings, querylist, config, debug=True):
def __init__(self, querylist, config, debug=True):
"""Constructor for GraphViewer"""
self.debug=debug
#print "start of GraphViewer constructor"
self.db=db
self.cursor=db.cursor
self.settings=settings
self.db = fpdb_db.fpdb_db() # sets self.fdb.db self.fdb.cursor and self.fdb.sql
self.db.do_connect(config)
self.sql = querylist
self.conf = config
@ -63,7 +63,7 @@ class GuiGraphViewer (threading.Thread):
"Button2" : True
}
self.filters = Filters.Filters(db, settings, config, querylist, display = filters_display)
self.filters = Filters.Filters(self.db, config, querylist, display = filters_display)
self.filters.registerButton1Name("Refresh Graph")
self.filters.registerButton1Callback(self.generateGraph)
self.filters.registerButton2Name("Export to File")
@ -146,7 +146,7 @@ class GuiGraphViewer (threading.Thread):
for site in sites:
if sites[site] == True:
sitenos.append(siteids[site])
self.cursor.execute(self.sql.query['getPlayerId'], (heroes[site],))
self.db.cursor.execute(self.sql.query['getPlayerId'], (heroes[site],))
result = self.db.cursor.fetchall()
if len(result) == 1:
playerids.append(result[0][0])
@ -226,7 +226,7 @@ class GuiGraphViewer (threading.Thread):
#print "DEBUG: sql query:"
#print tmp
self.cursor.execute(tmp)
self.db.cursor.execute(tmp)
#returns (HandId,Winnings,Costs,Profit)
winnings = self.db.cursor.fetchall()
self.db.db.rollback()

View File

@ -66,7 +66,7 @@ class GuiPlayerStats (threading.Thread):
"Button2" : True
}
self.filters = Filters.Filters(self.db, settings, config, querylist, display = filters_display)
self.filters = Filters.Filters(self.db, config, querylist, display = filters_display)
self.filters.registerButton1Name("_Filters")
self.filters.registerButton1Callback(self.showDetailFilter)
self.filters.registerButton2Name("_Refresh")
@ -227,11 +227,6 @@ class GuiPlayerStats (threading.Thread):
if not flags: holecards = False
else: holecards = flags[0]
self.stats_table = gtk.Table(1, 1, False)
self.stats_table.set_col_spacings(4)
self.stats_table.show()
tmp = self.sql.query[query]
tmp = self.refineQuery(tmp, flags, playerids, sitenos, limits, seats, groups, dates)
self.cursor.execute(tmp)
@ -279,10 +274,6 @@ class GuiPlayerStats (threading.Thread):
while sqlrow < rows:
treerow = []
if(row%2 == 0):
bgcolor = "white"
else:
bgcolor = "lightgrey"
for col,column in enumerate(cols_to_show):
if column[colalias] in colnames:
value = result[sqlrow][colnames.index(column[colalias])]

View File

@ -20,6 +20,7 @@ import pygtk
pygtk.require('2.0')
import gtk
import os
from time import time, strftime
import fpdb_import
import fpdb_db
@ -58,21 +59,50 @@ class GuiPositionalStats (threading.Thread):
"Button2" : False
}
self.filters = Filters.Filters(self.db, settings, config, querylist, display = filters_display)
self.filters = Filters.Filters(self.db, config, querylist, display = filters_display)
self.filters.registerButton1Name("Refresh")
self.filters.registerButton1Callback(self.refreshStats)
# ToDo: store in config
# ToDo: create popup to adjust column config
# columns to display, keys match column name returned by sql, values in tuple are:
# is column displayed, column heading, xalignment, formatting
self.columns = [ ["game", True, "Game", 0.0, "%s"]
, ["hand", False, "Hand", 0.0, "%s"] # true not allowed for this line
, ["plposition", False, "Posn", 1.0, "%s"] # true not allowed for this line (set in code)
, ["n", True, "Hds", 1.0, "%d"]
, ["avgseats", True, "Seats", 1.0, "%3.1f"]
, ["vpip", True, "VPIP", 1.0, "%3.1f"]
, ["pfr", True, "PFR", 1.0, "%3.1f"]
, ["pf3", True, "PF3", 1.0, "%3.1f"]
, ["steals", True, "Steals", 1.0, "%3.1f"]
, ["saw_f", True, "Saw_F", 1.0, "%3.1f"]
, ["sawsd", True, "SawSD", 1.0, "%3.1f"]
, ["wtsdwsf", True, "WtSDwsF", 1.0, "%3.1f"]
, ["wmsd", True, "W$SD", 1.0, "%3.1f"]
, ["flafq", True, "FlAFq", 1.0, "%3.1f"]
, ["tuafq", True, "TuAFq", 1.0, "%3.1f"]
, ["rvafq", True, "RvAFq", 1.0, "%3.1f"]
, ["pofafq", False, "PoFAFq", 1.0, "%3.1f"]
, ["net", True, "Net($)", 1.0, "%6.2f"]
, ["bbper100", True, "bb/100", 1.0, "%4.2f"]
, ["rake", True, "Rake($)", 1.0, "%6.2f"]
, ["bb100xr", True, "bbxr/100", 1.0, "%4.2f"]
, ["variance", True, "Variance", 1.0, "%5.2f"]
]
self.stat_table = None
self.stats_frame = None
self.stats_vbox = None
self.main_hbox = gtk.HBox(False, 0)
self.main_hbox.show()
statsFrame = gtk.Frame("Stats:")
statsFrame.set_label_align(0.0, 0.0)
statsFrame.show()
self.stats_frame = gtk.VBox(False, 0)
self.stats_frame = gtk.Frame()
self.stats_frame.set_label_align(0.0, 0.0)
self.stats_frame.show()
self.stats_vbox = gtk.VBox(False, 0)
self.stats_vbox.show()
# This could be stored in config eventually, or maybe configured in this window somehow.
# Each posncols element is the name of a column returned by the sql
@ -90,11 +120,11 @@ class GuiPositionalStats (threading.Thread):
, "PoFAFq", "Net($)", "bb/100", "$/hand", "Variance", "Hds"
)
self.fillStatsFrame(self.stats_frame)
statsFrame.add(self.stats_frame)
self.fillStatsFrame(self.stats_vbox)
self.stats_frame.add(self.stats_vbox)
self.main_hbox.pack_start(self.filters.get_vbox())
self.main_hbox.pack_start(statsFrame)
self.main_hbox.pack_start(self.stats_frame)
def get_vbox(self):
@ -107,9 +137,12 @@ class GuiPositionalStats (threading.Thread):
print "DEBUG: activesite set to %s" %(self.activesite)
def refreshStats(self, widget, data):
try: self.stats_table.destroy()
try: self.stats_vbox.destroy()
except AttributeError: pass
self.fillStatsFrame(self.stats_frame)
self.stats_vbox = gtk.VBox(False, 0)
self.stats_vbox.show()
self.stats_frame.add(self.stats_vbox)
self.fillStatsFrame(self.stats_vbox)
def fillStatsFrame(self, vbox):
sites = self.filters.getSites()
@ -144,66 +177,104 @@ class GuiPositionalStats (threading.Thread):
self.createStatsTable(vbox, playerids, sitenos, limits, seats, dates)
def createStatsTable(self, vbox, playerids, sitenos, limits, seats, dates):
self.stats_table = gtk.Table(1, 1, False) # gtk table expands as required
self.stats_table.set_col_spacings(4)
self.stats_table.show()
vbox.add(self.stats_table)
starttime = time()
colalias,colshow,colheading,colxalign,colformat = 0,1,2,3,4
row = 0
col = 0
for t in self.posnheads:
l = gtk.Label(self.posnheads[col])
l.show()
self.stats_table.attach(l, col, col+1, row, row+1, yoptions=gtk.SHRINK)
col +=1
tmp = self.sql.query['playerStatsByPosition']
tmp = self.refineQuery(tmp, playerids, sitenos, limits, seats, dates)
self.cursor.execute(tmp)
result = self.cursor.fetchall()
colnames = [desc[0].lower() for desc in self.cursor.description]
liststore = gtk.ListStore(*([str] * len(colnames)))
view = gtk.TreeView(model=liststore)
view.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_BOTH)
vbox.pack_start(view, expand=False, padding=3)
# left-aligned cells:
textcell = gtk.CellRendererText()
# centred cells:
textcell50 = gtk.CellRendererText()
textcell50.set_property('xalign', 0.5)
# right-aligned cells:
numcell = gtk.CellRendererText()
numcell.set_property('xalign', 1.0)
listcols = []
for t in self.posnheads:
listcols.append(gtk.TreeViewColumn(self.posnheads[col]))
view.append_column(listcols[col])
if col == 0:
listcols[col].pack_start(textcell, expand=True)
listcols[col].add_attribute(textcell, 'text', col)
listcols[col].set_expand(True)
elif col in (1, 2):
listcols[col].pack_start(textcell50, expand=True)
listcols[col].add_attribute(textcell50, 'text', col)
listcols[col].set_expand(True)
else:
listcols[col].pack_start(numcell, expand=True)
listcols[col].add_attribute(numcell, 'text', col)
listcols[col].set_expand(True)
col +=1
# Code below to be used when full column data structures implemented like in player stats:
# Create header row eg column: ("game", True, "Game", 0.0, "%s")
#for col, column in enumerate(cols_to_show):
# if column[colalias] == 'game' and holecards:
# s = [x for x in self.columns if x[colalias] == 'hand'][0][colheading]
# else:
# s = column[colheading]
# listcols.append(gtk.TreeViewColumn(s))
# view.append_column(listcols[col])
# if column[colformat] == '%s':
# if column[colxalign] == 0.0:
# listcols[col].pack_start(textcell, expand=True)
# listcols[col].add_attribute(textcell, 'text', col)
# else:
# listcols[col].pack_start(textcell50, expand=True)
# listcols[col].add_attribute(textcell50, 'text', col)
# listcols[col].set_expand(True)
# else:
# listcols[col].pack_start(numcell, expand=True)
# listcols[col].add_attribute(numcell, 'text', col)
# listcols[col].set_expand(True)
# #listcols[col].set_alignment(column[colxalign]) # no effect?
rows = len(result)
colnames = [desc[0].lower() for desc in self.cursor.description]
last_game,last_seats,sqlrow = "","",0
while sqlrow < rows:
if(row%2 == 0):
bgcolor = "white"
else:
bgcolor = "lightgrey"
rowprinted=0
treerow = []
avgcol = colnames.index('avgseats')
for col,colname in enumerate(self.posncols):
if colname in colnames:
sqlcol = colnames.index(colname)
else:
continue
eb = gtk.EventBox()
eb.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(bgcolor))
# print blank row between levels:
if result[sqlrow][sqlcol]:
if sqlrow == 0:
l = gtk.Label(result[sqlrow][sqlcol])
value = result[sqlrow][sqlcol]
rowprinted=1
elif result[sqlrow][0] != last_game:
l = gtk.Label(' ')
value = ' '
elif 'show' in seats and seats['show'] and result[sqlrow][avgcol] != last_seats:
l = gtk.Label(' ')
value = ' '
else:
l = gtk.Label(result[sqlrow][sqlcol])
value = result[sqlrow][sqlcol]
rowprinted=1
else:
l = gtk.Label(' ')
if col == 0:
l.set_alignment(xalign=0.0, yalign=0.5)
elif col == 1:
l.set_alignment(xalign=0.5, yalign=0.5)
value = ' '
if value and value != -999:
treerow.append(value)
else:
l.set_alignment(xalign=1.0, yalign=0.5)
eb.add(l)
self.stats_table.attach(eb, col, col+1, row+1, row+2, yoptions=gtk.SHRINK)
l.show()
eb.show()
treerow.append(' ')
iter = liststore.append(treerow)
last_game = result[sqlrow][0]
last_seats = result[sqlrow][avgcol]
if rowprinted:
@ -220,50 +291,36 @@ class GuiPositionalStats (threading.Thread):
# blank row between main stats and totals:
col = 0
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(' ')
eb.add(l)
self.stats_table.attach(eb, col, col+1, row+1, row+2, yoptions=gtk.SHRINK)
l.show()
eb.show()
treerow = [' ' for x in self.posncols]
iter = liststore.append(treerow)
row = row + 1
for sqlrow in range(rows):
if(row%2 == 0):
bgcolor = "white"
else:
bgcolor = "lightgrey"
treerow = []
for col,colname in enumerate(self.posncols):
if colname in colnames:
sqlcol = colnames.index(colname)
elif colname != "plposition":
continue
eb = gtk.EventBox()
eb.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(bgcolor))
if colname == 'plposition':
l = gtk.Label('Totals')
value = 'Totals'
elif result[sqlrow][sqlcol]:
l = gtk.Label(result[sqlrow][sqlcol])
value = result[sqlrow][sqlcol]
else:
l = gtk.Label(' ')
if col == 0:
l.set_alignment(xalign=0.0, yalign=0.5)
elif col == 1:
l.set_alignment(xalign=0.5, yalign=0.5)
value = ' '
if value and value != -999:
treerow.append(value)
else:
l.set_alignment(xalign=1.0, yalign=0.5)
eb.add(l)
self.stats_table.attach(eb, col, col+1, row+1, row+2, yoptions=gtk.SHRINK)
l.show()
eb.show()
treerow.append(' ')
iter = liststore.append(treerow)
row = row + 1
vbox.show_all()
self.db.db.rollback()
print "Positional Stats page displayed in %4.2f seconds" % (time() - starttime)
#end def fillStatsFrame(self, vbox):
def refineQuery(self, query, playerids, sitenos, limits, seats, dates):

View File

@ -15,6 +15,8 @@
#In the "official" distribution you can find the license in
#agpl-3.0.txt in the docs folder of the package.
# TODO: get writehand() encoding correct
import re
import sys
import traceback
@ -26,12 +28,20 @@ import operator
import time,datetime
from copy import deepcopy
from Exceptions import *
import pprint
import DerivedStats
import Card
class Hand:
###############################################################3
# Class Variables
UPS = {'a':'A', 't':'T', 'j':'J', 'q':'Q', 'k':'K', 'S':'s', 'C':'c', 'H':'h', 'D':'d'}
LCS = {'H':'h', 'D':'d', 'C':'c', 'S':'s'}
SYMBOL = {'USD': '$', 'EUR': u'E', 'T$': '', 'play': ''}
MS = {'horse' : 'HORSE', '8game' : '8-Game', 'hose' : 'HOSE'}
def __init__(self, sitename, gametype, handText, builtFrom = "HHC"):
self.sitename = sitename
self.stats = DerivedStats.DerivedStats(self)
@ -44,6 +54,10 @@ class Hand:
self.maxseats = 10
self.counted_seats = 0
self.buttonpos = 0
self.tourNo = None
self.buyin = None
self.level = None
self.mixed = None
self.seating = []
self.players = []
self.posted = []
@ -55,17 +69,19 @@ class Hand:
self.actions = {} # [['mct','bets','$10'],['mika','folds'],['carlg','raises','$20']]
self.board = {} # dict from street names to community cards
self.holecards = {}
self.discards = {}
for street in self.allStreets:
self.streets[street] = "" # portions of the handText, filled by markStreets()
self.actions[street] = []
for street in self.actionStreets:
self.bets[street] = {}
self.lastBet[street] = 0
self.actions[street] = []
self.board[street] = []
for street in self.holeStreets:
self.holecards[street] = {} # dict from player names to holecards
self.discards[street] = {} # dict from player names to dicts by street ... of tuples ... of discarded holecards
# Collections indexed by player names
# self.holecards = {} # dict from player names to dicts by street ... of tuples ... of holecards
self.discards = {} # dict from player names to dicts by street ... of tuples ... of discarded holecards
self.stacks = {}
self.collected = [] #list of ?
self.collectees = {} # dict from player names to amounts collected (?)
@ -74,6 +90,7 @@ class Hand:
self.folded = set()
self.dealt = set() # 'dealt to' line to be printed
self.shown = set() # cards were shown
self.mucked = set() # cards were mucked at showdown
# self.action = []
# Things to do with money
@ -83,10 +100,77 @@ class Hand:
self.rake = None
def __str__(self):
vars = ( ("BB", self.bb),
("SB", self.sb),
("BUTTONPOS", self.buttonpos),
("HAND NO.", self.handid),
("SITE", self.sitename),
("TABLE NAME", self.tablename),
("HERO", self.hero),
("MAXSEATS", self.maxseats),
("TOURNAMENT NO", self.tourNo),
("BUYIN", self.buyin),
("LEVEL", self.level),
("MIXED", self.mixed),
("LASTBET", self.lastBet),
("ACTION STREETS", self.actionStreets),
("STREETS", self.streets),
("ALL STREETS", self.allStreets),
("COMMUNITY STREETS", self.communityStreets),
("HOLE STREETS", self.holeStreets),
("COUNTED SEATS", self.counted_seats),
("DEALT", self.dealt),
("SHOWN", self.shown),
("MUCKED", self.mucked),
("TOTAL POT", self.totalpot),
("TOTAL COLLECTED", self.totalcollected),
("RAKE", self.rake),
("START TIME", self.starttime),
)
structs = ( ("PLAYERS", self.players),
("STACKS", self.stacks),
("POSTED", self.posted),
("POT", self.pot),
("SEATING", self.seating),
("GAMETYPE", self.gametype),
("ACTION", self.actions),
("COLLECTEES", self.collectees),
("BETS", self.bets),
("BOARD", self.board),
("DISCARDS", self.discards),
("HOLECARDS", self.holecards),
)
str = ''
str = str + "Hand Object for %s at %s" % (self.handid, self.sitename)
for (name, var) in vars:
str = str + "\n%s = " % name + pprint.pformat(var)
for (name, struct) in structs:
str = str + "\n%s =\n" % name + pprint.pformat(struct, 4)
return str
def addHoleCards(self, street, player, open=[], closed=[], shown=False, mucked=False, dealt=False):
"""\
Assigns observed holecards to a player.
cards list of card bigrams e.g. ['2h','Jc']
player (string) name of player
shown whether they were revealed at showdown
mucked whether they were mucked at showdown
dealt whether they were seen in a 'dealt to' line
"""
# logging.debug("addHoleCards %s %s" % (open + closed, player))
try:
self.checkPlayerExists(player)
except FpdbParseError, e:
print "[ERROR] Tried to add holecards for unknown player: %s" % (player,)
return
if dealt: self.dealt.add(player)
if shown: self.shown.add(player)
if mucked: self.mucked.add(player)
self.holecards[street][player] = [open, closed]
def insert(self, db):
""" Function to insert Hand into database
Should not commit, and do minimal selects. Callers may want to cache commits
@ -178,10 +262,10 @@ If a player has None chips he won't be added."""
self.players.append([seat, name, chips])
self.stacks[name] = Decimal(chips)
self.pot.addPlayer(name)
for street in self.allStreets:
for street in self.actionStreets:
self.bets[street][name] = []
#self.holecards[name] = {} # dict from street names.
self.discards[name] = {} # dict from street names.
#self.discards[name] = {} # dict from street names.
def addStreets(self, match):
@ -202,7 +286,7 @@ If a player has None chips he won't be added."""
def setCommunityCards(self, street, cards):
logging.debug("setCommunityCards %s %s" %(street, cards))
self.board[street] = [self.card(c) for c in cards]
print "DEBUG: self.board: %s" % self.board
# print "DEBUG: self.board: %s" % self.board
def card(self,c):
"""upper case the ranks but not suits, 'atjqk' => 'ATJQK'"""
@ -367,6 +451,20 @@ Add a raise on [street] by [player] to [amountTo]
self.collectees[player] += Decimal(pot)
def addShownCards(self, cards, player, holeandboard=None, shown=True, mucked=False):
"""\
For when a player shows cards for any reason (for showdown or out of choice).
Card ranks will be uppercased
"""
logging.debug("addShownCards %s hole=%s all=%s" % (player, cards, holeandboard))
if cards is not None:
self.addHoleCards(cards,player,shown, mucked)
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])
self.addHoleCards(holeandboard.difference(board),player,shown, mucked)
def totalPot(self):
"""If all bets and blinds have been added, totals up the total pot size"""
@ -410,7 +508,6 @@ Map the tuple self.gametype onto the pokerstars string describing it
logging.debug("gametype: %s" %(self.gametype))
retstring = "%s %s" %(gs[self.gametype['category']], ls[self.gametype['limitType']])
return retstring
@ -447,6 +544,33 @@ Map the tuple self.gametype onto the pokerstars string describing it
elif act[1] == 'stands pat':
return ("%s: stands pat" %(act[0]))
def getStakesAsString(self):
retstring = "%s%s/%s%s" % (self.SYMBOL[self.gametype['currency']], self.sb, self.SYMBOL[self.gametype['currency']], self.bb)
return retstring
def writeGameLine(self):
# print >>fh, ("%s Game #%s: %s ($%s/$%s) - %s" %("PokerStars", self.handid, self.getGameTypeAsString(), self.sb, self.bb, datetime.datetime.strftime(self.starttime,'%Y/%m/%d - %H:%M:%S ET')))
game_string = "PokerStars Game #%s: " % self.handid
if self.tourNo != None:
game_string = game_string + "Tournament #%s, %s %s - Level %s (%s) - " % (self.tourNo,
self.buyin, self.getGameTypeAsString(), self.level, self.getStakesAsString())
elif self.mixed != None:
game_string = game_string + " %s (%s, %s) - " % (self.MS[self.mixed],
self.getGameTypeAsString(), self.getStakesAsString())
else:
game_string = game_string + " %s (%s) - " % (self.getGameTypeAsString(), self.getStakesAsString())
game_string = game_string + datetime.datetime.strftime(self.starttime,'%Y/%m/%d %H:%M:%S ET')
return game_string
def writeTableLine(self):
table_string = "Table \'%s\' %s-max" % (self.tablename, self.maxseats)
if self.gametype['currency'] == 'play':
table_string = table_string + " (Play Money)"
if self.buttonpos != None:
table_string = table_string + " Seat #%s is the button" % self.buttonpos
return table_string
class HoldemOmahaHand(Hand):
def __init__(self, hhc, sitename, gametype, handText, builtFrom = "HHC", handid=None):
@ -494,45 +618,12 @@ class HoldemOmahaHand(Hand):
pass
def addHoleCards(self, cards, player, shown=False, dealt=False):
"""\
Assigns observed holecards to a player.
cards list of card bigrams e.g. ['2h','Jc']
player (string) name of player
shown whether they were revealed at showdown
dealt whether they were seen in a 'dealt to' line
"""
logging.debug("addHoleCards %s %s" % (cards, player))
try:
self.checkPlayerExists(player)
except FpdbParseError, e:
print "[ERROR] Tried to add holecards for unknown player: %s" % (player,)
return
cardset = set((self.card(c) for c in cards))
if len(cardset) == 0:
return
if dealt:
self.dealt.add(player)
if shown:
self.shown.add(player)
if player in self.holecards['PREFLOP']:
self.holecards['PREFLOP'][player].update(cardset)
def addShownCards(self, cards, player, shown=True, mucked=False, dealt=False):
if player == self.hero: # we have hero's cards just update shown/mucked
if shown: self.shown.add(player)
if mucked: self.mucked.add(player)
else:
self.holecards['PREFLOP'][player] = cardset
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
"""
logging.debug("addShownCards %s hole=%s all=%s" % (player, cards, holeandboard))
if cards is not None:
self.addHoleCards(cards,player,shown=True)
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])
self.addHoleCards(holeandboard.difference(board),player,shown=True)
self.addHoleCards('PREFLOP', player, open=[], closed=cards, shown=shown, mucked=mucked, dealt=dealt)
def writeHTMLHand(self, fh=sys.__stdout__):
@ -632,8 +723,11 @@ Card ranks will be uppercased
def writeHand(self, fh=sys.__stdout__):
# PokerStars format.
print >>fh, ("%s Game #%s: %s ($%s/$%s) - %s" %("PokerStars", self.handid, self.getGameTypeAsString(), self.sb, self.bb, datetime.datetime.strftime(self.starttime,'%Y/%m/%d - %H:%M:%S ET')))
print >>fh, ("Table '%s' %d-max Seat #%s is the button" %(self.tablename, self.maxseats, self.buttonpos))
# print >>fh, ("%s Game #%s: %s ($%s/$%s) - %s" %("PokerStars", self.handid, self.getGameTypeAsString(), self.sb, self.bb, datetime.datetime.strftime(self.starttime,'%Y/%m/%d - %H:%M:%S ET')))
print >>fh, self.writeGameLine()
print >>fh, self.writeTableLine()
# 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']]+[x[0] for x in self.actions['BLINDSANTES']]))
logging.debug(self.actions['PREFLOP'])
@ -647,10 +741,10 @@ Card ranks will be uppercased
print >>fh, ("*** HOLE CARDS ***")
for player in self.dealt:
print >>fh, ("Dealt to %s [%s]" %(player, " ".join(self.holecards['PREFLOP'][player])))
print >>fh, ("Dealt to %s [%s]" %(player, " ".join(self.holecards['PREFLOP'][player][1])))
if self.hero == "":
for player in self.shown.difference(self.dealt):
print >>fh, ("Dealt to %s [%s]" %(player, " ".join(self.holecards['PREFLOP'][player])))
print >>fh, ("Dealt to %s [%s]" %(player, " ".join(self.holecards['PREFLOP'][player][1])))
if self.actions['PREFLOP']:
for act in self.actions['PREFLOP']:
@ -689,7 +783,7 @@ Card ranks will be uppercased
elif self.gametype['category'] in ('holdem'):
numOfHoleCardsNeeded = 2
if len(self.holecards['PREFLOP'][name]) == numOfHoleCardsNeeded:
print >>fh, ("%s shows [%s] (a hand...)" % (name, " ".join(self.holecards['PREFLOP'][name])))
print >>fh, ("%s shows [%s] (a hand...)" % (name, " ".join(self.holecards['PREFLOP'][name][1])))
# Current PS format has the lines:
# Uncalled bet ($111.25) returned to s0rrow
@ -716,7 +810,7 @@ Card ranks will be uppercased
seatnum = player[0]
name = player[1]
if name in self.collectees and name in self.shown:
print >>fh, ("Seat %d: %s showed [%s] and won ($%s)" % (seatnum, name, " ".join(self.holecards['PREFLOP'][name]), self.collectees[name]))
print >>fh, ("Seat %d: %s showed [%s] and won ($%s)" % (seatnum, name, " ".join(self.holecards['PREFLOP'][name][1]), self.collectees[name]))
elif name in self.collectees:
print >>fh, ("Seat %d: %s collected ($%s)" % (seatnum, name, self.collectees[name]))
#~ elif name in self.shown:
@ -725,7 +819,9 @@ Card ranks will be uppercased
print >>fh, ("Seat %d: %s folded" % (seatnum, name))
else:
if name in self.shown:
print >>fh, ("Seat %d: %s showed [%s] and lost with..." % (seatnum, name, " ".join(self.holecards['PREFLOP'][name])))
print >>fh, ("Seat %d: %s showed [%s] and lost with..." % (seatnum, name, " ".join(self.holecards['PREFLOP'][name][1])))
elif name in self.mucked:
print >>fh, ("Seat %d: %s mucked [%s] " % (seatnum, name, " ".join(self.holecards['PREFLOP'][name][1])))
else:
print >>fh, ("Seat %d: %s mucked" % (seatnum, name))
@ -736,8 +832,10 @@ class DrawHand(Hand):
if gametype['base'] != 'draw':
pass # or indeed don't pass and complain instead
self.streetList = ['BLINDSANTES', 'DEAL', 'DRAWONE', 'DRAWTWO', 'DRAWTHREE']
self.allStreets = ['BLINDSANTES', 'DEAL', 'DRAWONE', 'DRAWTWO', 'DRAWTHREE']
self.holeStreets = ['DEAL', 'DRAWONE', 'DRAWTWO', 'DRAWTHREE']
self.actionStreets = ['PREDEAL', 'DEAL', 'DRAWONE', 'DRAWTWO', 'DRAWTHREE']
self.communityStreets = []
Hand.__init__(self, sitename, gametype, handText)
self.sb = gametype['sb']
self.bb = gametype['bb']
@ -749,12 +847,13 @@ class DrawHand(Hand):
hhc.markStreets(self)
hhc.readBlinds(self)
hhc.readButton(self)
hhc.readHeroCards(self)
hhc.readShowdownActions(self)
# Read actions in street order
for street in self.streetList:
if self.streets[street]:
# hhc.readCommunityCards(self, street)
hhc.readDrawCards(self, street)
# hhc.readDrawCards(self, street)
hhc.readAction(self, street)
hhc.readCollectPot(self)
hhc.readShownCards(self)
@ -790,25 +889,33 @@ class DrawHand(Hand):
#print "DEBUG: self.posted: %s" %(self.posted)
def addShownCards(self, cards, player, shown=True, mucked=False, dealt=False):
if player == self.hero: # we have hero's cards just update shown/mucked
if shown: self.shown.add(player)
if mucked: self.mucked.add(player)
else:
# TODO: Probably better to find the last street with action and add the hole cards to that street
self.addHoleCards('DRAWTHREE', player, open=[], closed=cards, shown=shown, mucked=mucked, dealt=dealt)
def addDrawHoleCards(self, newcards, oldcards, player, street, shown=False):
"""\
Assigns observed holecards to a player.
cards list of card bigrams e.g. ['2h','Jc']
player (string) name of player
"""
try:
self.checkPlayerExists(player)
# if shown and len(cardset) > 0:
# self.shown.add(player)
self.holecards[player][street] = (newcards,oldcards)
except FpdbParseError, e:
print "[ERROR] Tried to add holecards for unknown player: %s" % (player,)
# def addDrawHoleCards(self, newcards, oldcards, player, street, shown=False):
# """\
#Assigns observed holecards to a player.
#cards list of card bigrams e.g. ['2h','Jc']
#player (string) name of player
#"""
# try:
# self.checkPlayerExists(player)
## if shown and len(cardset) > 0:
## self.shown.add(player)
# self.holecards[street][player] = (newcards,oldcards)
# except FpdbParseError, e:
# print "[ERROR] Tried to add holecards for unknown player: %s" % (player,)
def discardDrawHoleCards(self, cards, player, street):
logging.debug("discardDrawHoleCards '%s' '%s' '%s'" % (cards, player, street))
self.discards[player][street] = set([cards])
self.discards[street][player] = set([cards])
def addDiscard(self, street, player, num, cards):
@ -821,12 +928,12 @@ player (string) name of player
self.actions[street].append(act)
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
"""
logging.debug("addShownCards %s hole=%s all=%s" % (player, cards, holeandboard))
# def addShownCards(self, cards, player, holeandboard=None, shown=False, mucked=False):
# """\
#For when a player shows cards for any reason (for showdown or out of choice).
#Card ranks will be uppercased
#"""
# logging.debug("addShownCards %s hole=%s all=%s" % (player, cards, holeandboard))
# if cards is not None:
# self.shown.add(player)
# self.addHoleCards(cards,player)
@ -836,10 +943,39 @@ Card ranks will be uppercased
# self.addHoleCards(holeandboard.difference(board),player,shown=True)
# def addHoleCards(self, cards, player, shown, mucked, dealt=False):
# """\
#Assigns observed holecards to a player.
#cards list of card bigrams e.g. ['2h','Jc']
#player (string) name of player
#shown whether they were revealed at showdown
#mucked whether they were mucked at showdown
#dealt whether they were seen in a 'dealt to' line
#"""
## I think this only gets called for shown cards.
# logging.debug("addHoleCards %s %s" % (cards, player))
# try:
# self.checkPlayerExists(player)
# except FpdbParseError, e:
# print "[ERROR] Tried to add holecards for unknown player: %s" % (player,)
# return
#
# if dealt:
# self.dealt.add(player)
# if shown:
# self.shown.add(player)
# if mucked:
# self.mucked.add(player)
# if player != self.hero: #skip hero, we know his cards
# print "player, cards =", player, cards
# self.holecards[self.holeStreets[-1]][player] = (cards, set([]))
def writeHand(self, fh=sys.__stdout__):
# PokerStars format.
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))
# 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, self.writeGameLine()
# print >>fh, _("Table '%s' %d-max Seat #%s is the button" %(self.tablename, self.maxseats, self.buttonpos))
print >>fh, self.writeTableLine()
players_who_act_ondeal = set(([x[0] for x in self.actions['DEAL']]+[x[0] for x in self.actions['BLINDSANTES']]))
@ -865,8 +1001,8 @@ Card ranks will be uppercased
for act in self.actions['DRAWONE']:
print >>fh, self.actionString(act)
if act[0] == self.hero and act[1] == 'discards':
(nc,oc) = self.holecards[act[0]]['DRAWONE']
dc = self.discards[act[0]]['DRAWONE']
(nc,oc) = self.holecards['DRAWONE'][act[0]]
dc = self.discards['DRAWONE'][act[0]]
kc = oc - dc
print >>fh, _("Dealt to %s [%s] [%s]" % (act[0], " ".join(kc), " ".join(nc)))
@ -875,8 +1011,8 @@ Card ranks will be uppercased
for act in self.actions['DRAWTWO']:
print >>fh, self.actionString(act)
if act[0] == self.hero and act[1] == 'discards':
(nc,oc) = self.holecards[act[0]]['DRAWTWO']
dc = self.discards[act[0]]['DRAWTWO']
(nc,oc) = self.holecards['DRAWTWO'][act[0]]
dc = self.discards['DRAWTWO'][act[0]]
kc = oc - dc
print >>fh, _("Dealt to %s [%s] [%s]" % (act[0], " ".join(kc), " ".join(nc)))
@ -885,8 +1021,8 @@ Card ranks will be uppercased
for act in self.actions['DRAWTHREE']:
print >>fh, self.actionString(act)
if act[0] == self.hero and act[1] == 'discards':
(nc,oc) = self.holecards[act[0]]['DRAWTHREE']
dc = self.discards[act[0]]['DRAWTHREE']
(nc,oc) = self.holecards['DRAWTHREE'][act[0]]
dc = self.discards['DRAWTHREE'][act[0]]
kc = oc - dc
print >>fh, _("Dealt to %s [%s] [%s]" % (act[0], " ".join(kc), " ".join(nc)))
@ -916,6 +1052,11 @@ class StudHand(Hand):
def __init__(self, hhc, sitename, gametype, handText, builtFrom = "HHC"):
if gametype['base'] != 'stud':
pass # or indeed don't pass and complain instead
self.allStreets = ['ANTES','THIRD','FOURTH','FIFTH','SIXTH','SEVENTH']
self.communityStreets = []
self.actionStreets = ['ANTES','THIRD','FOURTH','FIFTH','SIXTH','SEVENTH']
self.streetList = ['ANTES','THIRD','FOURTH','FIFTH','SIXTH','SEVENTH'] # a list of the observed street names in order
self.holeStreets = ['ANTES','THIRD','FOURTH','FIFTH','SIXTH','SEVENTH']
Hand.__init__(self, sitename, gametype, handText)
@ -931,21 +1072,35 @@ class StudHand(Hand):
hhc.markStreets(self)
hhc.readAntes(self)
hhc.readBringIn(self)
hhc.readHeroCards(self)
#hhc.readShowdownActions(self) # not done yet
# Read actions in street order
for street in self.streetList:
if self.streets[street]:
logging.debug(street)
logging.debug(self.streets[street])
hhc.readStudPlayerCards(self, street)
# hhc.readStudPlayerCards(self, street)
hhc.readAction(self, street)
hhc.readCollectPot(self)
#hhc.readShownCards(self) # not done yet
hhc.readShownCards(self) # not done yet
self.totalPot() # finalise it (total the pot)
hhc.getRake(self)
elif builtFrom == "DB":
self.select("dummy") # Will need a handId
def addShownCards(self, cards, player, shown=True, mucked=False, dealt=False):
if player == self.hero: # we have hero's cards just update shown/mucked
if shown: self.shown.add(player)
if mucked: self.mucked.add(player)
else:
# self.addHoleCards('PREFLOP', player, open=[], closed=cards, shown=shown, mucked=mucked, dealt=dealt)
self.addHoleCards('THIRD', player, open=[cards[2]], closed=cards[0:2], shown=shown, mucked=mucked)
self.addHoleCards('FOURTH', player, open=[cards[3]], closed=[], shown=shown, mucked=mucked)
self.addHoleCards('FIFTH', player, open=[cards[4]], closed=[], shown=shown, mucked=mucked)
self.addHoleCards('SIXTH', player, open=[cards[5]], closed=[], shown=shown, mucked=mucked)
self.addHoleCards('SEVENTH', player, open=[], closed=[cards[6]], shown=shown, mucked=mucked)
def addPlayerCards(self, player, street, open=[], closed=[]):
"""\
Assigns observed cards to a player.
@ -957,12 +1112,47 @@ closed likewise, but known only to player
logging.debug("addPlayerCards %s, o%s x%s" % (player, open, closed))
try:
self.checkPlayerExists(player)
self.holecards[player][street] = (open, closed)
self.holecards[street][player] = (open, closed)
# cards = set([self.card(c) for c in cards])
# self.holecards[player].update(cards)
except FpdbParseError, e:
print "[ERROR] Tried to add holecards for unknown player: %s" % (player,)
# def addHoleCards(self, cards, player, shown, mucked, dealt=False):
# """\
#Assigns observed holecards to a player.
#cards list of card bigrams e.g. ['2h','Jc']
#player (string) name of player
#shown whether they were revealed at showdown
#mucked whether they were mucked at showdown
#dealt whether they were seen in a 'dealt to' line
#"""
##
## For stud games we just need to do the routine setting of shown/mucked/etc
## and then update the cards 'THIRD' and 'SEVENTH'
# logging.debug("addHoleCards %s %s" % (cards, player))
# try:
# self.checkPlayerExists(player)
# except FpdbParseError, e:
# print "[ERROR] Tried to add holecards for unknown player: %s" % (player,)
# return
#
# if dealt:
# self.dealt.add(player)
# if shown:
# self.shown.add(player)
# if mucked:
# self.mucked.add(player)
# if player == self.hero:
# if len(cards) > 2:
# self.holecards['THIRD'][player] = ([cards[0:3]], [])
# if len(cards) > 6:
# self.holecards['SEVENTH'][player] = ([cards[6]], [])
# else:
# if len(cards) > 2:
# self.holecards['THIRD'][player] = ([cards[0]], cards[1:3])
# if len(cards) > 6:
# self.holecards['SEVENTH'][player] = ([], [cards[6]])
# TODO: def addComplete(self, player, amount):
def addComplete(self, street, player, amountTo):
# assert street=='THIRD'
@ -995,11 +1185,19 @@ Add a complete on [street] by [player] to [amountTo]
self.lastBet['THIRD'] = Decimal(bringin)
self.pot.addMoney(player, Decimal(bringin))
def writeHand(self, fh=sys.__stdout__):
# PokerStars format.
# 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, _("%s Game #%s: %s ($%s/$%s) - %s" %("PokerStars", self.handid, self.getGameTypeAsString(), self.sb, self.bb, datetime.datetime.strftime(self.starttime,'%Y/%m/%d - %H:%M:%S ET')))
print >>fh, _("Table '%s' %d-max Seat #%s is the button" %(self.tablename, self.maxseats, self.buttonpos))
# TODO:
# Hole cards are not currently correctly written. Currently the down cards for non-heros
# are shown in the "dealt to" lines. They should be hidden in those lines. I tried to fix
# but mind got boggled, will try again.
# print >>fh, _("%s Game #%s: %s ($%s/$%s) - %s" %("PokerStars", self.handid, self.getGameTypeAsString(), self.sb, self.bb, datetime.datetime.strftime(self.starttime,'%Y/%m/%d - %H:%M:%S ET')))
print >>fh, self.writeGameLine()
print >>fh, self.writeTableLine()
# print >>fh, _("Table '%s' %d-max Seat #%s is the button" %(self.tablename, self.maxseats, self.buttonpos))
players_who_post_antes = set([x[0] for x in self.actions['ANTES']])
@ -1015,8 +1213,8 @@ Add a complete on [street] by [player] to [amountTo]
dealt = 0
#~ print >>fh, _("*** 3RD STREET ***")
for player in [x[1] for x in self.players if x[1] in players_who_post_antes]:
if 'THIRD' in self.holecards[player]:
(open, closed) = self.holecards[player]['THIRD']
if self.holecards['THIRD'].has_key(player):
(open, closed) = self.holecards['THIRD'][player]
dealt+=1
if dealt==1:
print >>fh, _("*** 3RD STREET ***")
@ -1029,12 +1227,12 @@ Add a complete on [street] by [player] to [amountTo]
dealt = 0
#~ print >>fh, _("*** 4TH STREET ***")
for player in [x[1] for x in self.players if x[1] in players_who_post_antes]:
if 'FOURTH' in self.holecards[player]:
if player in self.holecards['FOURTH']:
old = []
(o,c) = self.holecards[player]['THIRD']
(o,c) = self.holecards['THIRD'][player]
if o:old.extend(o)
if c:old.extend(c)
new = self.holecards[player]['FOURTH'][0]
new = self.holecards['FOURTH'][player][0]
dealt+=1
if dealt==1:
print >>fh, _("*** 4TH STREET ***")
@ -1046,13 +1244,13 @@ Add a complete on [street] by [player] to [amountTo]
dealt = 0
#~ print >>fh, _("*** 5TH STREET ***")
for player in [x[1] for x in self.players if x[1] in players_who_post_antes]:
if 'FIFTH' in self.holecards[player]:
if self.holecards['FIFTH'].has_key(player):
old = []
for street in ('THIRD','FOURTH'):
(o,c) = self.holecards[player][street]
(o,c) = self.holecards[street][player]
if o:old.extend(o)
if c:old.extend(c)
new = self.holecards[player]['FIFTH'][0]
new = self.holecards['FIFTH'][player][0]
dealt+=1
if dealt==1:
print >>fh, _("*** 5TH STREET ***")
@ -1064,13 +1262,13 @@ Add a complete on [street] by [player] to [amountTo]
dealt = 0
#~ print >>fh, _("*** 6TH STREET ***")
for player in [x[1] for x in self.players if x[1] in players_who_post_antes]:
if 'SIXTH' in self.holecards[player]:
if self.holecards['SIXTH'].has_key(player):
old = []
for street in ('THIRD','FOURTH','FIFTH'):
(o,c) = self.holecards[player][street]
(o,c) = self.holecards[street][player]
if o:old.extend(o)
if c:old.extend(c)
new = self.holecards[player]['SIXTH'][0]
new = self.holecards['SIXTH'][player][0]
dealt += 1
if dealt == 1:
print >>fh, _("*** 6TH STREET ***")
@ -1085,13 +1283,13 @@ Add a complete on [street] by [player] to [amountTo]
# i.e. are all but one players folded; is there an allin showdown; and all that.
print >>fh, _("*** 7TH STREET ***")
for player in [x[1] for x in self.players if x[1] in players_who_post_antes]:
if 'SEVENTH' in self.holecards[player]:
if self.holecards['SEVENTH'].has_key(player):
old = []
for street in ('THIRD','FOURTH','FIFTH','SIXTH'):
(o,c) = self.holecards[player][street]
(o,c) = self.holecards[street][player]
if o:old.extend(o)
if c:old.extend(c)
new = self.holecards[player]['SEVENTH'][0]
new = self.holecards['SEVENTH'][player][0]
if new:
print >>fh, _("Dealt to %s:%s%s") % (player, " [" + " ".join(old) + "] " if old else " ", "[" + " ".join(new) + "]" if new else "")
for act in self.actions['SEVENTH']:
@ -1130,11 +1328,13 @@ Add a complete on [street] by [player] to [amountTo]
seatnum = player[0]
name = player[1]
if name in self.collectees and name in self.shown:
print >>fh, _("Seat %d: %s showed [%s] and won ($%s)" % (seatnum, name, " ".join(self.holecards[name]), self.collectees[name]))
print >>fh, _("Seat %d: %s showed [%s] and won ($%s)" % (seatnum, name, self.join_holecards(name), self.collectees[name]))
elif name in self.collectees:
print >>fh, _("Seat %d: %s collected ($%s)" % (seatnum, name, self.collectees[name]))
elif name in self.shown:
print >>fh, _("Seat %d: %s showed [%s]" % (seatnum, name, " ".join(self.holecards[name])))
print >>fh, _("Seat %d: %s showed [%s]" % (seatnum, name, self.join_holecards(name)))
elif name in self.mucked:
print >>fh, _("Seat %d: %s mucked [%s]" % (seatnum, name, self.join_holecards(name)))
elif name in self.folded:
print >>fh, _("Seat %d: %s folded" % (seatnum, name))
else:
@ -1143,6 +1343,12 @@ Add a complete on [street] by [player] to [amountTo]
print >>fh, "\n\n"
def join_holecards(self, player):
holecards = []
for street in self.holeStreets:
if self.holecards[street].has_key(player):
holecards = holecards + self.holecards[street][player][0]
return " ".join(holecards)
class Pot(object):

View File

@ -197,6 +197,7 @@ which it expects to find at self.re_TailSplitHands -- see for e.g. Everleaf.py.
logging.info("Unsupported game type: %s" % gametype)
if hand:
# print hand
hand.writeHand(self.out_fh)
else:
logging.info("Unsupported game type: %s" % gametype)

View File

@ -34,10 +34,13 @@ import gobject
# FreePokerTools modules
from Mucked import Aux_Window
from Mucked import Seat_Window
from Mucked import Aux_Seats
class Hello(Aux_Window):
"""A 'Hello World' Aux_Window demo."""
def create(self):
print "creating Hello"
# This demo simply creates a label in a window.
self.container = gtk.Window()
self.container.add(gtk.Label("Hello World"))
@ -99,15 +102,18 @@ class Hello_plus(Aux_Window):
# hands played that was updated in the "update_data()" function.
self.label.set_text("Hello %s\nYou have played %d hands\n on %s." % (self.hero, self.hands_played, self.site))
class Hello_Menu(Aux_Window):
"""A 'Hello World' Aux_Window demo."""
def create(self):
# This demo puts a menu item on the HUD mainwindow.
self.item = gtk.MenuItem('Print cards')
self.hud.menu.append(self.item)
self.item.connect("activate", self.print_cards)
self.item.show()
class Hello_Seats(Aux_Seats):
"""A 'Hello World' Seat_Window demo."""
def print_cards(self, *args):
# callback for the menu item
print "cards =", self.hud.cards
def create_contents(self, container, i):
container.label = gtk.Label("empty")
container.add(container.label)
container.show_all()
def update_contents(self, container, i):
if i == "common": return
id = self.get_id_from_seat(i)
if id == None:
container.label.set_text("empty")
else:
container.label.set_text("player = %s" % self.hud.stat_dict[id]['screen_name'])

View File

@ -339,30 +339,30 @@ class Hud:
Stats.do_tip(window.e_box[r][c], tip)
def topify_window(self, window):
"""Set the specified gtk window to stayontop in MS Windows."""
def windowEnumerationHandler(hwnd, resultList):
'''Callback for win32gui.EnumWindows() to generate list of window handles.'''
resultList.append((hwnd, win32gui.GetWindowText(hwnd)))
unique_name = 'unique name for finding this window'
real_name = window.get_title()
window.set_title(unique_name)
tl_windows = []
win32gui.EnumWindows(windowEnumerationHandler, tl_windows)
for w in tl_windows:
if w[1] == unique_name:
# """Set the specified gtk window to stayontop in MS Windows."""
#
# def windowEnumerationHandler(hwnd, resultList):
# '''Callback for win32gui.EnumWindows() to generate list of window handles.'''
# resultList.append((hwnd, win32gui.GetWindowText(hwnd)))
# unique_name = 'unique name for finding this window'
# real_name = window.get_title()
# window.set_title(unique_name)
# tl_windows = []
# win32gui.EnumWindows(windowEnumerationHandler, tl_windows)
#
# for w in tl_windows:
# if w[1] == unique_name:
self.main_window.parentgdkhandle = gtk.gdk.window_foreign_new(long(self.table.number))
self.main_window.gdkhandle = gtk.gdk.window_foreign_new(w[0])
# self.main_window.gdkhandle = gtk.gdk.window_foreign_new(w[0])
self.main_window.gdkhandle = self.main_window.window
self.main_window.gdkhandle.set_transient_for(self.main_window.parentgdkhandle)
style = win32gui.GetWindowLong(self.table.number, win32con.GWL_EXSTYLE)
style |= win32con.WS_CLIPCHILDREN
win32gui.SetWindowLong(self.table.number, win32con.GWL_EXSTYLE, style)
break
# break
window.set_title(real_name)
# window.set_title(real_name)
class Stat_Window:
@ -445,10 +445,10 @@ class Stat_Window:
Stats.do_tip(e_box[r][c], 'stuff')
if usegtkframes:
grid.attach(self.frame[r][c], c, c+1, r, r+1, xpadding = 0, ypadding = 0)
grid.attach(self.frame[r][c], c, c+1, r, r+1, xpadding = game.xpad, ypadding = game.ypad)
self.frame[r][c].add(e_box[r][c])
else:
grid.attach(e_box[r][c], c, c+1, r, r+1, xpadding = 0, ypadding = 0)
grid.attach(e_box[r][c], c, c+1, r, r+1, xpadding = game.xpad, ypadding = game.ypad)
label[r].append( gtk.Label('xxx') )
if usegtkframes:
@ -473,6 +473,7 @@ class Popup_window:
def __init__(self, parent, stat_window):
self.sb_click = 0
self.stat_window = stat_window
self.parent = parent
# create the popup window
self.window = gtk.Window()
@ -532,11 +533,15 @@ class Popup_window:
# db_connection.close_connection()
stat_dict = stat_window.parent.stat_dict
pu_text = ""
mo_text = ""
for s in stat_list:
number = Stats.do_stat(stat_dict, player = int(stat_window.player_id), stat = s)
mo_text += number[5] + " " + number[4] + "\n"
pu_text += number[3] + "\n"
self.lab.set_text(pu_text)
Stats.do_tip(self.lab, mo_text)
self.window.show_all()
self.window.set_transient_for(stat_window.window)
@ -572,25 +577,25 @@ class Popup_window:
def topify_window(self, window):
"""Set the specified gtk window to stayontop in MS Windows."""
def windowEnumerationHandler(hwnd, resultList):
'''Callback for win32gui.EnumWindows() to generate list of window handles.'''
resultList.append((hwnd, win32gui.GetWindowText(hwnd)))
# def windowEnumerationHandler(hwnd, resultList):
# '''Callback for win32gui.EnumWindows() to generate list of window handles.'''
# resultList.append((hwnd, win32gui.GetWindowText(hwnd)))
unique_name = 'unique name for finding this window'
real_name = window.get_title()
window.set_title(unique_name)
tl_windows = []
win32gui.EnumWindows(windowEnumerationHandler, tl_windows)
# unique_name = 'unique name for finding this window'
# real_name = window.get_title()
# window.set_title(unique_name)
# tl_windows = []
# win32gui.EnumWindows(windowEnumerationHandler, tl_windows)
for w in tl_windows:
if w[1] == unique_name:
# for w in tl_windows:
# if w[1] == unique_name:
window.set_transient_for(self.parent.main_window)
style = win32gui.GetWindowLong(self.table.number, win32con.GWL_EXSTYLE)
style |= win32con.WS_CLIPCHILDREN
win32gui.SetWindowLong(self.table.number, win32con.GWL_EXSTYLE, style)
break
# break
window.set_title(real_name)
# window.set_title(real_name)
if __name__== "__main__":
main_window = gtk.Window()

View File

@ -37,27 +37,18 @@ import Configuration
import Database
import Card
class Aux_Window:
class Aux_Window(object):
def __init__(self, hud, params, config):
self.hud = hud
self.params = params
self.config = config
def update_data(self, *args):
pass
def update_gui(self, *args):
pass
def create(self, *args):
pass
def relocate(self, *args):
pass
def save_layout(self, *args):
pass
# Override these methods as needed
def update_data(self, *args): pass
def update_gui(self, *args): pass
def create(self, *args): pass
def relocate(self, *args): pass
def save_layout(self, *args): pass
def destroy(self):
try:
self.container.destroy()
@ -89,14 +80,19 @@ class Aux_Window:
pb.copy_area(30*j, 42*i, 30, 42, temp_pb, 0, 0)
return temp_pb
def split_cards(self, card):
if card == 'xx': return ('B', 'S')
return (card[0], card[1].upper())
def has_cards(self, cards):
"""Returns the number of cards in the list."""
n = 0
for c in cards:
if c in set('shdc'): return True
return False
if c != None and c > 0: n = n + 1
return n
def get_id_from_seat(self, seat):
"""Determine player id from seat number, given stat_dict."""
for id, dict in self.hud.stat_dict.iteritems():
if seat == dict['seat']:
return id
return None
class Stud_mucked(Aux_Window):
def __init__(self, hud, config, params):
@ -329,87 +325,152 @@ class Stud_cards:
self.seen_cards[(c, r)].set_from_pixbuf(self.card_images[0])
self.eb[(c, r)].set_tooltip_text('')
class Flop_Mucked(Aux_Window):
"""Aux_Window class for displaying mucked cards for flop games."""
class Seat_Window(gtk.Window):
"""Subclass gtk.Window for the seat windows."""
class Aux_Seats(Aux_Window):
"""A super class to display an aux_window at each seat."""
def __init__(self, hud, config, params):
self.hud = hud # hud object that this aux window supports
self.config = config # configuration object for this aux window to use
self.params = params # dict aux params from config
self.positions = {} # dict of window positions
# self.rel_positions = {} # dict of window positions, relative to the table origin
self.displayed_cards = False
self.displayed = False # the seat windows are displayed
self.uses_timer = False # the Aux_seats object uses a timer to control hiding
self.timer_on = False # bool = Ture if the timeout for removing the cards is on
self.card_images = self.get_card_images()
# placeholders that should be overridden--so we don't throw errors
def create_contents(self): pass
def update_contents(self): pass
def create(self):
self.adj = self.hud.adj_seats(0, self.config)
self.adj = self.hud.adj_seats(0, self.config) # move adj_seats to aux and get rid of it in Hud.py
loc = self.config.get_aux_locations(self.params['name'], int(self.hud.max))
self.m_windows = {} # windows to put the card images in
self.eb = {} # event boxes so we can interact with the mucked cards
self.seen_cards = {} # image objects to stash the cards in
for i in (range(1, self.hud.max + 1) + ['common']):
if i == 'common':
(x, y) = self.params['layout'][self.hud.max].common
else:
(x, y) = loc[self.adj[i]]
self.m_windows[i] = gtk.Window()
self.m_windows[i] = Seat_Window()
self.m_windows[i].set_decorated(False)
self.m_windows[i].set_property("skip-taskbar-hint", True)
self.m_windows[i].set_transient_for(self.hud.main_window)
self.m_windows[i].set_focus_on_map(False)
self.eb[i] = gtk.EventBox()
self.eb[i].connect("button_press_event", self.button_press_cb)
self.m_windows[i].connect("configure_event", self.configure_event_cb, i)
self.m_windows[i].add(self.eb[i])
self.seen_cards[i] = gtk.image_new_from_pixbuf(self.card_images[('B', 'H')])
self.eb[i].add(self.seen_cards[i])
self.positions[i] = (int(x) + self.hud.table.x, int(y) + self.hud.table.y)
# self.rel_positions[i] = (int(x), int(y))
self.m_windows[i].move(self.positions[i][0], self.positions[i][1])
if self.params.has_key('opacity'):
self.m_windows[i].set_opacity(float(self.params['opacity']))
# the create_contents method is supplied by the subclass
self.create_contents(self.m_windows[i], i)
self.m_windows[i].show_all()
if self.uses_timer:
self.m_windows[i].hide()
def update_gui(self, new_hand_id):
"""Prepare and show the mucked cards."""
if self.displayed_cards:
self.hide_mucked_cards()
self.displayed_cards = False
for (i, cards) in self.hud.cards.iteritems():
if self.has_cards(cards):
"""Update the gui, LDO."""
for i in self.m_windows.keys():
self.update_contents(self.m_windows[i], i)
# Methods likely to be of use for any Seat_Window implementation
def destroy(self):
"""Destroy all of the seat windows."""
for i in self.m_windows.keys():
self.m_windows[i].destroy()
del(self.m_windows[i])
# Methods likely to be useful for mucked card windows (or similar) only
def hide(self):
"""Hide the seat windows."""
for (i, w) in self.m_windows.iteritems():
w.hide()
self.displayed = False
def save_layout(self, *args):
"""Save new layout back to the aux element in the config file."""
new_locs = {}
# print "adj =", self.adj
for (i, pos) in self.positions.iteritems():
if i != 'common':
new_locs[self.adj[int(i)]] = (pos[0] - self.hud.table.x, pos[1] - self.hud.table.y)
else:
new_locs[i] = (pos[0] - self.hud.table.x, pos[1] - self.hud.table.y)
self.config.edit_aux_layout(self.params['name'], self.hud.max, locations = new_locs)
def configure_event_cb(self, widget, event, i, *args):
self.positions[i] = widget.get_position()
# self.rel_positions[i] = (self.positions[i][0] - self.hud.table.x, self.positions[i][1] - self.hud.table.y)
class Flop_Mucked(Aux_Seats):
"""Aux_Window class for displaying mucked cards for flop games."""
def __init__(self, hud, config, params):
super(Flop_Mucked, self).__init__(hud, config, params)
self.card_images = self.get_card_images()
self.uses_timer = True # this Aux_seats object uses a timer to control hiding
def create_contents(self, container, i):
"""Create the widgets for showing the contents of the Aux_seats window."""
container.eb = gtk.EventBox()
container.eb.connect("button_press_event", self.button_press_cb)
container.add(container.eb)
container.seen_cards = gtk.image_new_from_pixbuf(self.card_images[0])
container.eb.add(container.seen_cards)
def update_contents(self, container, i):
if not self.hud.cards.has_key(i): return
cards = self.hud.cards[i]
n_cards = self.has_cards(cards)
if n_cards > 1:
# scratch is a working pixbuf, used to assemble the image
scratch = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8,
int(self.params['card_wd'])*len(cards)/2,
int(self.params['card_wd'])*n_cards,
int(self.params['card_ht']))
x = 0 # x coord where the next card starts in scratch
for card in [cards[k:k+2] for k in xrange(0, len(cards), 2)]:
for card in cards:
# concatenate each card image to scratch
self.card_images[self.split_cards(card)].copy_area(0, 0,
if card == None or card ==0:
break
self.card_images[card].copy_area(0, 0,
int(self.params['card_wd']), int(self.params['card_ht']),
scratch, x, 0)
x = x + int(self.params['card_wd'])
self.seen_cards[i].set_from_pixbuf(scratch)
# self.m_windows[i].show_all()
self.m_windows[i].resize(1,1)
self.m_windows[i].show()
self.m_windows[i].move(self.positions[i][0], self.positions[i][1]) # here is where I move back
self.displayed_cards = True
container.seen_cards.set_from_pixbuf(scratch)
container.resize(1,1)
container.show()
container.move(self.positions[i][0], self.positions[i][1]) # here is where I move back
self.displayed = True
if i != "common":
id = self.get_id_from_seat(i)
self.m_windows[i].eb.set_tooltip_text(self.hud.stat_dict[id]['screen_name'])
for stats in self.hud.stat_dict.itervalues():
self.eb[stats['seat']].set_tooltip_text(stats['screen_name'])
def update_gui(self, new_hand_id):
"""Prepare and show the mucked cards."""
if self.displayed: self.hide()
if self.displayed_cards and float(self.params['timeout']) > 0:
# See how many players showed a hand. Skip if only 1 shows (= hero)
n_sd = 0
for (i, cards) in self.hud.cards.iteritems():
n_cards = self.has_cards(cards)
if n_cards > 0 and i != 'common':
n_sd = n_sd + 1
if n_sd < 2:
print "skipping, n_sd =", n_sd
return
super(Flop_Mucked, self).update_gui(new_hand_id)
if self.displayed and float(self.params['timeout']) > 0:
self.timer_on = True
gobject.timeout_add(int(1000*float(self.params['timeout'])), self.timed_out)
def destroy(self):
"""Destroy all of the mucked windows."""
for w in self.m_windows.values():
w.destroy()
def timed_out(self):
# this is the callback from the timeout
@ -418,15 +479,9 @@ class Flop_Mucked(Aux_Window):
if not self.timer_on:
return False
else:
self.hide_mucked_cards()
self.hide()
return False
def hide_mucked_cards(self):
"""Hide the mucked card windows."""
for (i, w) in self.m_windows.iteritems():
w.hide()
self.displayed_cards = False
def button_press_cb(self, widget, event, *args):
"""Handle button clicks in the event boxes."""
@ -444,58 +499,14 @@ class Flop_Mucked(Aux_Window):
self.timer_on = False
else:
self.timer_on = False
self.hide_mucked_cards()
self.hide()
elif event.button == 1: # left button event
window = widget.get_parent()
window.begin_move_drag(event.button, int(event.x_root), int(event.y_root), event.time)
def configure_event_cb(self, widget, event, i, *args):
self.positions[i] = widget.get_position()
# self.rel_positions[i] = (self.positions[i][0] - self.hud.table.x, self.positions[i][1] - self.hud.table.y)
def expose_all(self):
for (i, cards) in self.hud.cards.iteritems():
self.m_windows[i].show()
self.m_windows[i].move(self.positions[i][0], self.positions[i][1]) # here is where I move back
self.displayed_cards = True
def save_layout(self, *args):
"""Save new layout back to the aux element in the config file."""
new_locs = {}
# print "adj =", self.adj
for (i, pos) in self.positions.iteritems():
if i != 'common':
new_locs[self.adj[int(i)]] = (pos[0] - self.hud.table.x, pos[1] - self.hud.table.y)
else:
new_locs[i] = (pos[0] - self.hud.table.x, pos[1] - self.hud.table.y)
self.config.edit_aux_layout(self.params['name'], self.hud.max, locations = new_locs)
# This test program doesn't work
#if __name__== "__main__":
#
# def destroy(*args): # call back for terminating the main eventloop
# gtk.main_quit() # used only for testing
#
# def process_new_hand(source, condition, db_connection): #callback from stdin watch -- testing only
## there is a new hand_id to be processed
## just read it and pass it to update
# new_hand_id = sys.stdin.readline()
# new_hand_id = new_hand_id.rstrip() # remove trailing whitespace
# m.update_data(new_hand_id, db_connection)
# m.update_gui(new_hand_id)
# return(True)
#
# config = Configuration.Config()
# db_connection = Database.Database(config, 'fpdbTEST', '')
# main_window = gtk.Window()
# main_window.set_keep_above(True)
# main_window.connect("destroy", destroy)
#
# aux_to_call = "stud_mucked"
# aux_params = config.get_aux_parameters(aux_to_call)
# m = eval("%s(None, config, aux_params)" % aux_params['class'])
#
# s_id = gobject.io_add_watch(sys.stdin, gobject.IO_IN, process_new_hand, db_connection)
# gtk.main()
self.displayed = True

View File

@ -18,6 +18,7 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
########################################################################
# TODO: straighten out discards for draw games
import sys
from HandHistoryConverter import *
@ -25,16 +26,35 @@ from HandHistoryConverter import *
class PokerStars(HandHistoryConverter):
############################################################
# Class Variables
# Static regexes
re_GameInfo = re.compile("PokerStars Game #(?P<HID>[0-9]+):\s+(HORSE)? \(?(?P<GAME>Hold\'em|Razz|7 Card Stud|7 Card Stud Hi/Lo|Omaha|Omaha Hi/Lo|Badugi) (?P<LIMIT>No Limit|Limit|Pot Limit),? \(?(?P<CURRENCY>\$|)?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+)\) - (?P<DATETIME>.*$)", re.MULTILINE)
re_GameInfo = re.compile("""PokerStars\sGame\s\#(?P<HID>[0-9]+):\s+
(Tournament\s\#(?P<TOURNO>\d+),\s(?P<BUYIN>[\$\+\d\.]+)\s)?
(?P<MIXED>HORSE|8\-Game|HOSE)?\s?\(?
(?P<GAME>Hold\'em|Razz|7\sCard\sStud|7\sCard\sStud\sHi/Lo|Omaha|Omaha\sHi/Lo|Badugi|Triple\sDraw\s2\-7\sLowball)\s
(?P<LIMIT>No\sLimit|Limit|Pot\sLimit)\)?,?\s
(-\sLevel\s(?P<LEVEL>[IVXLC]+)\s)?\(?
(?P<CURRENCY>\$|)?
(?P<SB>[.0-9]+)/\$?
(?P<BB>[.0-9]+)\)\s-\s
(?P<DATETIME>.*$)""",
re.MULTILINE|re.VERBOSE)
re_SplitHands = re.compile('\n\n+')
re_TailSplitHands = re.compile('(\n\n\n+)')
re_HandInfo = re.compile("^Table \'(?P<TABLE>[- a-zA-Z]+)\'(?P<TABLEATTRIBUTES>.+?$)?", re.MULTILINE)
re_HandInfo = re.compile("""^Table\s\'(?P<TABLE>[-\ a-zA-Z\d]+)\'\s
((?P<MAX>\d+)-max\s)?
(?P<PLAY>\(Play\sMoney\)\s)?
(Seat\s\#(?P<BUTTON>\d+)\sis\sthe\sbutton)?""",
re.MULTILINE|re.VERBOSE)
re_Button = re.compile('Seat #(?P<BUTTON>\d+) is the button', re.MULTILINE)
re_PlayerInfo = re.compile('^Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \(\$?(?P<CASH>[.0-9]+) in chips\)', re.MULTILINE)
re_Board = re.compile(r"\[(?P<CARDS>.+)\]")
# self.re_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]+)')
mixes = { 'HORSE': 'horse', '8-Game': '8game', 'HOSE': 'hose'}
def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True):
"""\
in_path (default '-' = sys.stdin)
@ -53,6 +73,9 @@ follow : whether to tail -f the input"""
players = set([player[1] for player in hand.players])
if not players <= self.compiledPlayers: # x <= y means 'x is subset of y'
# we need to recompile the player regexs.
# TODO: should probably rename re_HeroCards and corresponding method,
# since they are used to find all cards on lines starting with "Dealt to:"
# They still identify the hero.
self.compiledPlayers = players
player_re = "(?P<PNAME>" + "|".join(map(re.escape, players)) + ")"
logging.debug("player_re: " + player_re)
@ -62,32 +85,43 @@ follow : whether to tail -f the input"""
self.re_BringIn = re.compile(r"^%s: brings[- ]in( low|) for \$?(?P<BRINGIN>[.0-9]+)" % player_re, re.MULTILINE)
self.re_PostBoth = re.compile(r"^%s: posts small \& big blinds \[\$? (?P<SBBB>[.0-9]+)" % player_re, re.MULTILINE)
self.re_HeroCards = re.compile(r"^Dealt to %s(?: \[(?P<OLDCARDS>.+?)\])?( \[(?P<NEWCARDS>.+?)\])" % player_re, re.MULTILINE)
self.re_Action = re.compile(r"^%s:(?P<ATYPE> bets| checks| raises| calls| folds| discards| stands pat)( \$(?P<BET>[.\d]+))?( to \$(?P<BETTO>[.\d]+))?( (?P<NODISCARDED>\d) cards?( \[(?P<DISCARDED>.+?)\])?)?" % player_re, re.MULTILINE)
self.re_Action = re.compile(r"""^%s:(?P<ATYPE>\sbets|\schecks|\sraises|\scalls|\sfolds|\sdiscards|\sstands\spat)
(\s\$?(?P<BET>[.\d]+))?(\sto\s\$?(?P<BETTO>[.\d]+))? # the number discarded goes in <BET>
(\scards?(\s\[(?P<DISCARDED>.+?)\])?)?"""
% player_re, re.MULTILINE|re.VERBOSE)
self.re_ShowdownAction = re.compile(r"^%s: shows \[(?P<CARDS>.*)\]" % player_re, re.MULTILINE)
self.re_CollectPot = re.compile(r"Seat (?P<SEAT>[0-9]+): %s (\(button\) |\(small blind\) |\(big blind\) )?(collected|showed \[.*\] and won) \(\$(?P<POT>[.\d]+)\)(, mucked| with.*|)" % player_re, re.MULTILINE)
self.re_CollectPot = re.compile(r"Seat (?P<SEAT>[0-9]+): %s (\(button\) |\(small blind\) |\(big blind\) )?(collected|showed \[.*\] and won) \(\$?(?P<POT>[.\d]+)\)(, mucked| with.*|)" % player_re, re.MULTILINE)
self.re_sitsOut = re.compile("^%s sits out" % player_re, re.MULTILINE)
self.re_ShownCards = re.compile("^Seat (?P<SEAT>[0-9]+): %s \(.*\) showed \[(?P<CARDS>.*)\].*" % player_re, re.MULTILINE)
self.re_ShownCards = re.compile("^Seat (?P<SEAT>[0-9]+): %s (\(.*\) )?(?P<SHOWED>showed|mucked) \[(?P<CARDS>.*)\].*" % player_re, re.MULTILINE)
def readSupportedGames(self):
return [["ring", "hold", "nl"],
["ring", "hold", "pl"],
["ring", "hold", "fl"],
["ring", "stud", "fl"],
#["ring", "draw", "fl"],
["ring", "omaha", "pl"]
["ring", "draw", "fl"],
["tour", "hold", "nl"],
["tour", "hold", "pl"],
["tour", "hold", "fl"],
["tour", "stud", "fl"],
]
def determineGameType(self, handText):
info = {'type':'ring'}
# inspect the handText and return the gametype dict
# gametype dict is:
# {'limitType': xxx, 'base': xxx, 'category': xxx}
info = {}
m = self.re_GameInfo.search(handText)
if not m:
return None
mg = m.groupdict()
# translations from captured groups to our info strings
# translations from captured groups to fpdb info strings
limits = { 'No Limit':'nl', 'Pot Limit':'pl', 'Limit':'fl' }
games = { # base, category
"Hold'em" : ('hold','holdem'),
@ -96,9 +130,14 @@ follow : whether to tail -f the input"""
'Razz' : ('stud','razz'),
'7 Card Stud' : ('stud','studhi'),
'7 Card Stud Hi/Lo' : ('stud','studhilo'),
'Badugi' : ('draw','badugi')
'Badugi' : ('draw','badugi'),
'Triple Draw 2-7 Lowball' : ('draw','27_3draw'),
}
currencies = { u'':'EUR', '$':'USD', '':'T$' }
# I don't think this is doing what we think. mg will always have all
# the expected keys, but the ones that didn't match in the regex will
# have a value of None. It is OK if it throws an exception when it
# runs across an unknown game or limit or whatever.
if 'LIMIT' in mg:
info['limitType'] = limits[mg['LIMIT']]
if 'GAME' in mg:
@ -109,8 +148,13 @@ follow : whether to tail -f the input"""
info['bb'] = mg['BB']
if 'CURRENCY' in mg:
info['currency'] = currencies[mg['CURRENCY']]
# NB: SB, BB must be interpreted as blinds or bets depending on limit type.
if 'TOURNO' in mg and mg['TOURNO'] == None:
info['type'] = 'ring'
else:
info['type'] = 'tour'
# NB: SB, BB must be interpreted as blinds or bets depending on limit type.
return info
@ -119,14 +163,13 @@ follow : whether to tail -f the input"""
m = self.re_HandInfo.search(hand.handText,re.DOTALL)
if m:
info.update(m.groupdict())
# TODO: Be less lazy and parse maxseats from the HandInfo regex
if m.group('TABLEATTRIBUTES'):
m2 = re.search("\s*(\d+)-max", m.group('TABLEATTRIBUTES'))
hand.maxseats = int(m2.group(1))
# hand.maxseats = int(m2.group(1))
else:
pass # throw an exception here, eh?
m = self.re_GameInfo.search(hand.handText)
if m: info.update(m.groupdict())
m = self.re_Button.search(hand.handText)
if m: info.update(m.groupdict())
# m = self.re_Button.search(hand.handText)
# if m: info.update(m.groupdict())
# TODO : I rather like the idea of just having this dict as hand.info
logging.debug("readHandInfo: %s" % info)
for key in info:
@ -143,6 +186,22 @@ follow : whether to tail -f the input"""
hand.tablename = info[key]
if key == 'BUTTON':
hand.buttonpos = info[key]
if key == 'MAX':
hand.maxseats = int(info[key])
if key == 'MIXED':
if info[key] == None: hand.mixed = None
else: hand.mixed = self.mixes[info[key]]
if key == 'TOURNO':
hand.tourNo = info[key]
if key == 'BUYIN':
hand.buyin = info[key]
if key == 'LEVEL':
hand.level = info[key]
if key == 'PLAY' and info['PLAY'] != None:
# hand.currency = 'play' # overrides previously set value
hand.gametype['currency'] = 'play'
def readButton(self, hand):
m = self.re_Button.search(hand.handText)
@ -211,78 +270,115 @@ follow : whether to tail -f the input"""
for a in self.re_PostBoth.finditer(hand.handText):
hand.addBlind(a.group('PNAME'), 'both', a.group('SBBB'))
# def readHeroCards(self, hand):
# m = self.re_HeroCards.search(hand.handText)
# 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('NEWCARDS')
# cards = set(cards.split(' '))
# hand.addHoleCards(cards, m.group('PNAME'), shown=False, mucked=False, dealt=True)
def readHeroCards(self, hand):
m = self.re_HeroCards.search(hand.handText)
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('NEWCARDS')
cards = set(cards.split(' '))
hand.addHoleCards(cards, m.group('PNAME'))
def readDrawCards(self, hand, street):
logging.debug("readDrawCards")
# streets PREFLOP, PREDRAW, and THIRD are special cases beacause
# we need to grab hero's cards
for street in ('PREFLOP', 'DEAL'):
if street in hand.streets.keys():
m = self.re_HeroCards.finditer(hand.streets[street])
if m == None:
hand.involved = False
else:
for player in m:
hand.hero = player.group('PNAME') # Only really need to do this once
newcards = player.group('NEWCARDS')
oldcards = player.group('OLDCARDS')
if newcards == None:
newcards = set()
else:
newcards = set(newcards.split(' '))
if oldcards == None:
oldcards = set()
else:
oldcards = set(oldcards.split(' '))
hand.addDrawHoleCards(newcards, oldcards, player.group('PNAME'), street)
for found in m:
# if m == None:
# hand.involved = False
# else:
hand.hero = found.group('PNAME')
newcards = found.group('NEWCARDS').split(' ')
hand.addHoleCards(street, hand.hero, closed=newcards, shown=False, mucked=False, dealt=True)
def readStudPlayerCards(self, hand, street):
# See comments of reference implementation in FullTiltToFpdb.py
logging.debug("readStudPlayerCards")
for street, text in hand.streets.iteritems():
if street in ('PREFLOP', 'DEAL'): continue # already done these
m = self.re_HeroCards.finditer(hand.streets[street])
for player in m:
#~ logging.debug(player.groupdict())
(pname, oldcards, newcards) = (player.group('PNAME'), player.group('OLDCARDS'), player.group('NEWCARDS'))
if oldcards:
oldcards = [c.strip() for c in oldcards.split(' ')]
if newcards:
newcards = [c.strip() for c in newcards.split(' ')]
if street=='ANTES':
return
elif street=='THIRD':
# we'll have observed hero holecards in CARDS and thirdstreet open cards in 'NEWCARDS'
# hero: [xx][o]
# others: [o]
hand.addPlayerCards(player = player.group('PNAME'), street = street, closed = oldcards, open = newcards)
elif street in ('FOURTH', 'FIFTH', 'SIXTH'):
# 4th:
# hero: [xxo] [o]
# others: [o] [o]
# 5th:
# hero: [xxoo] [o]
# others: [oo] [o]
# 6th:
# hero: [xxooo] [o]
# others: [ooo] [o]
hand.addPlayerCards(player = player.group('PNAME'), street = street, open = newcards)
# we may additionally want to check the earlier streets tally with what we have but lets trust it for now.
elif street=='SEVENTH' and newcards:
# hero: [xxoooo] [x]
# others: not reported.
hand.addPlayerCards(player = player.group('PNAME'), street = street, closed = newcards)
for found in m:
player = found.group('PNAME')
if found.group('NEWCARDS') == None:
newcards = []
else:
newcards = found.group('NEWCARDS').split(' ')
if found.group('OLDCARDS') == None:
oldcards = []
else:
oldcards = found.group('OLDCARDS').split(' ')
if street == 'THIRD' and len(newcards) == 3: # hero in stud game
hand.hero = player
hand.dealt.add(player) # need this for stud??
hand.addHoleCards(street, player, closed=newcards[0:2], open=[newcards[2]], shown=False, mucked=False, dealt=False)
else:
hand.addHoleCards(street, player, open=newcards, closed=oldcards, shown=False, mucked=False, dealt=False)
# def readDrawCards(self, hand, street):
# logging.debug("readDrawCards")
# m = self.re_HeroCards.finditer(hand.streets[street])
# if m == None:
# hand.involved = False
# else:
# for player in m:
# hand.hero = player.group('PNAME') # Only really need to do this once
# newcards = player.group('NEWCARDS')
# oldcards = player.group('OLDCARDS')
# if newcards == None:
# newcards = set()
# else:
# newcards = set(newcards.split(' '))
# if oldcards == None:
# oldcards = set()
# else:
# oldcards = set(oldcards.split(' '))
# hand.addDrawHoleCards(newcards, oldcards, player.group('PNAME'), street)
# def readStudPlayerCards(self, hand, street):
# # See comments of reference implementation in FullTiltToFpdb.py
# logging.debug("readStudPlayerCards")
# m = self.re_HeroCards.finditer(hand.streets[street])
# for player in m:
# #~ logging.debug(player.groupdict())
# (pname, oldcards, newcards) = (player.group('PNAME'), player.group('OLDCARDS'), player.group('NEWCARDS'))
# if oldcards:
# oldcards = [c.strip() for c in oldcards.split(' ')]
# if newcards:
# newcards = [c.strip() for c in newcards.split(' ')]
# if street=='ANTES':
# return
# elif street=='THIRD':
# # we'll have observed hero holecards in CARDS and thirdstreet open cards in 'NEWCARDS'
# # hero: [xx][o]
# # others: [o]
# hand.addPlayerCards(player = player.group('PNAME'), street = street, closed = oldcards, open = newcards)
# elif street in ('FOURTH', 'FIFTH', 'SIXTH'):
# # 4th:
# # hero: [xxo] [o]
# # others: [o] [o]
# # 5th:
# # hero: [xxoo] [o]
# # others: [oo] [o]
# # 6th:
# # hero: [xxooo] [o]
# # others: [ooo] [o]
# hand.addPlayerCards(player = player.group('PNAME'), street = street, open = newcards)
# # we may additionally want to check the earlier streets tally with what we have but lets trust it for now.
# elif street=='SEVENTH' and newcards:
# # hero: [xxoooo] [x]
# # others: not reported.
# hand.addPlayerCards(player = player.group('PNAME'), street = street, closed = newcards)
def readAction(self, hand, street):
m = self.re_Action.finditer(hand.streets[street])
for action in m:
acts = action.groupdict()
if action.group('ATYPE') == ' raises':
hand.addRaiseBy( street, action.group('PNAME'), action.group('BET') )
elif action.group('ATYPE') == ' calls':
@ -294,7 +390,7 @@ follow : whether to tail -f the input"""
elif action.group('ATYPE') == ' checks':
hand.addCheck( street, action.group('PNAME'))
elif action.group('ATYPE') == ' discards':
hand.addDiscard(street, action.group('PNAME'), action.group('NODISCARDED'), action.group('DISCARDED'))
hand.addDiscard(street, action.group('PNAME'), action.group('BET'), action.group('DISCARDED'))
elif action.group('ATYPE') == ' stands pat':
hand.addStandsPat( street, action.group('PNAME'))
else:
@ -302,9 +398,9 @@ follow : whether to tail -f the input"""
def readShowdownActions(self, hand):
# TODO: pick up mucks also
for shows in self.re_ShowdownAction.finditer(hand.handText):
cards = shows.group('CARDS')
cards = set(cards.split(' '))
cards = shows.group('CARDS').split(' ')
hand.addShownCards(cards, shows.group('PNAME'))
def readCollectPot(self,hand):
@ -315,8 +411,13 @@ follow : whether to tail -f the input"""
for m in self.re_ShownCards.finditer(hand.handText):
if m.group('CARDS') is not None:
cards = m.group('CARDS')
cards = set(cards.split(' '))
hand.addShownCards(cards=cards, player=m.group('PNAME'))
cards = cards.split(' ') # needs to be a list, not a set--stud needs the order
(shown, mucked) = (False, False)
if m.group('SHOWED') == "showed": shown = True
elif m.group('SHOWED') == "mucked": mucked = True
hand.addShownCards(cards=cards, player=m.group('PNAME'), shown=shown, mucked=mucked)
if __name__ == "__main__":
parser = OptionParser()

View File

@ -542,18 +542,13 @@ class Sql:
self.query['get_common_cards'] = """
select
card1Value AS card1value,
card1Suit AS card1suit,
card2Value AS card2value,
card2Suit AS card2suit,
card3Value AS card3value,
card3Suit AS card3suit,
card4Value AS card4value,
card4Suit AS card4suit,
card5Value AS card5value,
card5Suit AS card5suit
from BoardCards
where handId = %s
boardcard1,
boardcard2,
boardcard3,
boardcard4,
boardcard5
from Hands
where Id = %s
"""
self.query['get_action_from_hand'] = """

View File

@ -231,7 +231,7 @@ def discover_nt_by_name(c, tablename):
titles = {}
win32gui.EnumWindows(win_enum_handler, titles)
for hwnd in titles:
print "Tbales.py: tablename =", tablename, "title =", titles[hwnd]
#print "Tables.py: tablename =", tablename, "title =", titles[hwnd]
try:
# this can blow up in XP on some windows, eg firefox displaying http://docs.python.org/tutorial/classes.html
if not tablename in titles[hwnd]: continue
@ -314,6 +314,10 @@ def get_site_from_exe(c, exe):
return params['site_name']
return None
def everleaf_decode_table(tw):
# 2 - Tournament ID: 573256 - NL Hold'em - 150/300 blinds - Good luck <username>! - [Connection is ...]
pass
def pokerstars_decode_table(tw):
# Extract poker information from the window title. This is not needed for
# fpdb, since all that information is available in the db via new_hand_number.
@ -365,7 +369,7 @@ def clean_title(name):
for pattern in [' \(6 max\)', ' \(heads up\)', ' \(deep\)',
' \(deep hu\)', ' \(deep 6\)', ' \(2\)',
' \(edu\)', ' \(edu, 6 max\)', ' \(6\)',
' \(speed\)',
' \(speed\)', 'special', 'newVPP',
' no all-in', ' fast', ',', ' 50BB min', '50bb min', '\s+$']:
name = re.sub(pattern, '', name)
name = name.rstrip()

View File

@ -92,6 +92,8 @@ if __name__=="__main__":
print "game =", table.get_game()
fake = fake_hud(table)
print "fake =", fake
gobject.timeout_add(100, check_on_table, table, fake)
print "calling main"
gtk.main()

View File

@ -65,8 +65,11 @@ class Table(Table_Window):
print "x = %s y = %s width = %s height = %s" % (x, y, width, height)
self.x = int(x) + b_width
self.y = int(y) + tb_height
self.height = int(height) - b_width - tb_height
self.width = int(width) - 2*b_width
self.width = width - x
self.height = height - y
print "x = %s y = %s width = %s height = %s" % (self.x, self.y, self.width, self.height)
#self.height = int(height) - b_width - tb_height
#self.width = int(width) - 2*b_width
self.exe = self.get_nt_exe(hwnd)
self.title = titles[hwnd]
@ -76,12 +79,13 @@ class Table(Table_Window):
self.gdkhandle = gtk.gdk.window_foreign_new(long(self.window))
def get_geometry(self):
if not win32gui.IsWindow(self.window): # window closed
if not win32gui.IsWindow(self.number): # window closed
return None
try:
(x, y, width, height) = win32gui.GetWindowRect(hwnd)
(x, y, width, height) = win32gui.GetWindowRect(self.number)
width = width - x
height = height - y
return {'x' : int(x) + b_width,
'y' : int(y) + tb_height,
'width' : int(height) - b_width - tb_height,
@ -116,27 +120,32 @@ class Table(Table_Window):
def topify(self, hud):
"""Set the specified gtk window to stayontop in MS Windows."""
def windowEnumerationHandler(hwnd, resultList):
'''Callback for win32gui.EnumWindows() to generate list of window handles.'''
resultList.append((hwnd, win32gui.GetWindowText(hwnd)))
unique_name = 'unique name for finding this window'
real_name = hud.main_window.get_title()
hud.main_window.set_title(unique_name)
tl_windows = []
win32gui.EnumWindows(windowEnumerationHandler, tl_windows)
for w in tl_windows:
if w[1] == unique_name:
hud.main_window.gdkhandle = gtk.gdk.window_foreign_new(w[0])
# def windowEnumerationHandler(hwnd, resultList):
# '''Callback for win32gui.EnumWindows() to generate list of window handles.'''
# resultList.append((hwnd, win32gui.GetWindowText(hwnd)))
#
# unique_name = 'unique name for finding this window'
# real_name = hud.main_window.get_title()
# hud.main_window.set_title(unique_name)
# tl_windows = []
# win32gui.EnumWindows(windowEnumerationHandler, tl_windows)
#
# for w in tl_windows:
# if w[1] == unique_name:
# hud.main_window.gdkhandle = gtk.gdk.window_foreign_new(w[0])
hud.main_window.gdkhandle = hud.main_window.window
hud.main_window.gdkhandle.set_transient_for(self.gdkhandle)
rect = self.gdkhandle.get_frame_extents()
(innerx, innery) = self.gdkhandle.get_origin()
b_width = rect.x - innerx
tb_height = rect.y - innery
#
# style = win32gui.GetWindowLong(self.number, win32con.GWL_EXSTYLE)
# style |= win32con.WS_CLIPCHILDREN
# win32gui.SetWindowLong(self.number, win32con.GWL_EXSTYLE, style)
break
# break
hud.main_window.set_title(real_name)
# hud.main_window.set_title(real_name)
def win_enum_handler(hwnd, titles):
titles[hwnd] = win32gui.GetWindowText(hwnd)

View File

@ -33,7 +33,7 @@ import pygtk
pygtk.require('2.0')
import gtk
import fpdb_db
import fpdb_simple
import GuiBulkImport
import GuiPlayerStats
@ -42,6 +42,8 @@ import GuiTableViewer
import GuiAutoImport
import GuiGraphViewer
import GuiSessionViewer
import SQL
import Database
import FpdbSQLQueries
import Configuration
@ -179,20 +181,21 @@ class fpdb:
def dia_load_profile(self, widget, data=None):
"""Dialogue to select a file to load a profile from"""
if self.obtain_global_lock() == 0: # returns 0 if successful
try:
chooser = gtk.FileChooserDialog(title="Please select a profile file to load",
action=gtk.FILE_CHOOSER_ACTION_OPEN,
buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
chooser.set_filename(self.profile)
#try:
# chooser = gtk.FileChooserDialog(title="Please select a profile file to load",
# action=gtk.FILE_CHOOSER_ACTION_OPEN,
# buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
# chooser.set_filename(self.profile)
response = chooser.run()
chooser.destroy()
if response == gtk.RESPONSE_OK:
self.load_profile(chooser.get_filename())
elif response == gtk.RESPONSE_CANCEL:
print 'User cancelled loading profile'
except:
pass
# response = chooser.run()
# chooser.destroy()
# if response == gtk.RESPONSE_OK:
# self.load_profile(chooser.get_filename())
# elif response == gtk.RESPONSE_CANCEL:
# print 'User cancelled loading profile'
#except:
# pass
self.load_profile()
self.release_global_lock()
#end def dia_load_profile
@ -204,22 +207,23 @@ class fpdb:
try:
dia_confirm = gtk.MessageDialog(parent=None, flags=0, type=gtk.MESSAGE_WARNING,
buttons=(gtk.BUTTONS_YES_NO), message_format="Confirm deleting and recreating tables")
diastring = "Please confirm that you want to (re-)create the tables. If there already are tables in the database "+self.db.database+" on "+self.db.host+" they will be deleted."
diastring = "Please confirm that you want to (re-)create the tables. If there already are tables in the database " \
+self.db.fdb.database+" on "+self.db.fdb.host+" they will be deleted."
dia_confirm.format_secondary_text(diastring)#todo: make above string with bold for db, host and deleted
response = dia_confirm.run()
dia_confirm.destroy()
if response == gtk.RESPONSE_YES:
if self.db.backend == self.fdb_lock.MYSQL_INNODB:
if self.db.fdb.backend == self.fdb_lock.fdb.MYSQL_INNODB:
# mysql requires locks on all tables or none - easier to release this lock
# than lock all the other tables
# ToDo: lock all other tables so that lock doesn't have to be released
self.release_global_lock()
lock_released = True
self.db.recreate_tables()
self.db.fdb.recreate_tables()
else:
# for other dbs use same connection as holds global lock
self.fdb_lock.recreate_tables()
self.fdb_lock.fdb.recreate_tables()
elif response == gtk.RESPONSE_NO:
print 'User cancelled recreating tables'
except:
@ -347,6 +351,7 @@ class fpdb:
def load_profile(self):
"""Loads profile from the provided path name."""
self.config = Configuration.Config(file=options.config, dbname=options.dbname)
self.settings = {}
if (os.sep=="/"):
self.settings['os']="linuxmac"
@ -359,17 +364,13 @@ class fpdb:
self.settings.update(self.config.get_import_parameters())
self.settings.update(self.config.get_default_paths())
if self.db!=None:
if self.db != None and self.db.fdb != None:
self.db.disconnect()
self.db = fpdb_db.fpdb_db()
#print "end of fpdb.load_profile, databaseName:",self.settings['db-databaseName']
self.db.connect(self.settings['db-backend'],
self.settings['db-host'],
self.settings['db-databaseName'],
self.settings['db-user'],
self.settings['db-password'])
if self.db.wrongDbVersion:
self.sql = SQL.Sql(type = self.settings['db-type'], db_server = self.settings['db-server'])
self.db = Database.Database(self.config, sql = self.sql)
if self.db.fdb.wrongDbVersion:
diaDbVersionWarning = gtk.Dialog(title="Strong Warning - Invalid database version", parent=None, flags=0, buttons=(gtk.STOCK_OK,gtk.RESPONSE_OK))
label = gtk.Label("An invalid DB version or missing tables have been detected.")
@ -387,9 +388,16 @@ class fpdb:
response = diaDbVersionWarning.run()
diaDbVersionWarning.destroy()
if self.status_bar == None:
self.status_bar = gtk.Label("Status: Connected to %s database named %s on host %s"%(self.db.get_backend_name(),self.db.fdb.database, self.db.fdb.host))
self.main_vbox.pack_end(self.status_bar, False, True, 0)
self.status_bar.show()
else:
self.status_bar.set_text("Status: Connected to %s database named %s on host %s" % (self.db.get_backend_name(),self.db.fdb.database, self.db.fdb.host))
# Database connected to successfully, load queries to pass on to other classes
self.querydict = FpdbSQLQueries.FpdbSQLQueries(self.db.get_backend_name())
self.db.db.rollback()
self.db.connection.rollback()
#end def load_profile
def not_implemented(self, widget, data=None):
@ -398,13 +406,9 @@ class fpdb:
def obtain_global_lock(self):
print "\nTaking global lock ..."
self.fdb_lock = fpdb_db.fpdb_db()
self.fdb_lock.connect(self.settings['db-backend'],
self.settings['db-host'],
self.settings['db-databaseName'],
self.settings['db-user'],
self.settings['db-password'])
return self.fdb_lock.get_global_lock()
self.fdb_lock = Database.Database(self.config, sql = self.sql)
self.fdb_lock.do_connect(self.config)
return self.fdb_lock.fdb.get_global_lock()
#end def obtain_global_lock
def quit(self, widget, data=None):
@ -415,9 +419,9 @@ class fpdb:
#end def quit_cliecked
def release_global_lock(self):
self.fdb_lock.db.rollback()
self.fdb_lock.disconnect()
print "Global lock released."
self.fdb_lock.fdb.db.rollback()
self.fdb_lock.fdb.disconnect()
print "Global lock released.\n"
#end def release_global_lock
def tab_abbreviations(self, widget, data=None):
@ -466,7 +470,7 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt")
def tab_table_viewer(self, widget, data=None):
"""opens a table viewer tab"""
#print "start of tab_table_viewer"
new_tv_thread=GuiTableViewer.GuiTableViewer(self.db, self.settings)
new_tv_thread=GuiTableViewer.GuiTableViewer(self.db.fdb, self.settings)
self.threads.append(new_tv_thread)
tv_tab=new_tv_thread.get_vbox()
self.add_and_display_tab(tv_tab, "Table Viewer")
@ -475,22 +479,16 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt")
def tabGraphViewer(self, widget, data=None):
"""opens a graph viewer tab"""
#print "start of tabGraphViewer"
new_gv_thread=GuiGraphViewer.GuiGraphViewer(self.db, self.settings, self.querydict, self.config)
new_gv_thread = GuiGraphViewer.GuiGraphViewer(self.querydict, self.config)
self.threads.append(new_gv_thread)
gv_tab=new_gv_thread.get_vbox()
self.add_and_display_tab(gv_tab, "Graphs")
#end def tabGraphViewer
def __init__(self):
LOG_FILENAME = './logging.out'
LOG_FORMAT = "%(asctime)-15s %(levelname)-8s %(message)s"
logging.basicConfig(filename=LOG_FILENAME,level=10,format=LOG_FORMAT)
logging.info("Fpdb started")
self.threads = []
self.db = None
self.config = Configuration.Config(file=options.config, dbname=options.dbname)
self.load_profile()
self.status_bar = None
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.connect("delete_event", self.delete_event)
@ -525,11 +523,8 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt")
self.tab_main_help(None, None)
self.status_bar = gtk.Label("Status: Connected to %s database named %s on host %s"%(self.db.get_backend_name(),self.db.database, self.db.host))
self.main_vbox.pack_end(self.status_bar, False, True, 0)
self.status_bar.show()
self.window.show()
self.load_profile()
sys.stderr.write("fpdb starting ...")
#end def __init__

View File

@ -255,7 +255,6 @@ class fpdb_db:
self.cursor.execute(self.sql.query['createPlayersTable'])
self.cursor.execute(self.sql.query['createAutoratesTable'])
self.cursor.execute(self.sql.query['createHandsTable'])
self.cursor.execute(self.sql.query['createBoardCardsTable'])
self.cursor.execute(self.sql.query['createTourneyTypesTable'])
self.cursor.execute(self.sql.query['createTourneysTable'])
self.cursor.execute(self.sql.query['createTourneysPlayersTable'])
@ -587,17 +586,18 @@ class fpdb_db:
print "analyze took", atime, "seconds"
#end def analyzeDB
# Currently uses an exclusive lock on the Hands table as a global lock
# Currently uses an exclusive lock on the Players table as a global lock
# ( Changed because Hands is used in Database.init() )
# Return values are Unix style, 0 for success, positive integers for errors
# 1 = generic error
# 2 = hands table does not exist (error message is suppressed)
# 2 = players table does not exist (error message is suppressed)
def get_global_lock(self):
if self.backend == self.MYSQL_INNODB:
try:
self.cursor.execute( "lock tables Hands write" )
self.cursor.execute( "lock tables Players write" )
except:
# Table 'fpdb.hands' doesn't exist
if str(sys.exc_value).find(".Hands' doesn't exist") >= 0:
# Table 'fpdb.players' doesn't exist
if str(sys.exc_value).find(".Players' doesn't exist") >= 0:
return(2)
print "Error! failed to obtain global lock. Close all programs accessing " \
+ "database (including fpdb) and try again (%s)." \
@ -605,11 +605,11 @@ class fpdb_db:
return(1)
elif self.backend == self.PGSQL:
try:
self.cursor.execute( "lock table Hands in exclusive mode nowait" )
self.cursor.execute( "lock table Players in exclusive mode nowait" )
#print "... after lock table, status =", self.cursor.statusmessage
except:
# relation "hands" does not exist
if str(sys.exc_value).find('relation "hands" does not exist') >= 0:
# relation "players" does not exist
if str(sys.exc_value).find('relation "players" does not exist') >= 0:
return(2)
print "Error! failed to obtain global lock. Close all programs accessing " \
+ "database (including fpdb) and try again (%s)." \

View File

@ -32,6 +32,7 @@ import re
import fpdb_simple
import fpdb_db
import Database
import fpdb_parse_logic
import Configuration
@ -58,7 +59,8 @@ class Importer:
self.settings = settings
self.caller = caller
self.config = config
self.fdb = None
self.database = None # database will be the main db interface eventually
self.fdb = None # fdb may disappear or just hold the simple db connection
self.cursor = None
self.filelist = {}
self.dirlist = {}
@ -76,6 +78,7 @@ class Importer:
self.settings.setdefault("minPrint", 30)
self.settings.setdefault("handCount", 0)
self.database = Database.Database(self.config) # includes .connection and .sql variables
self.fdb = fpdb_db.fpdb_db() # sets self.fdb.db self.fdb.cursor and self.fdb.sql
self.fdb.do_connect(self.config)
self.fdb.db.rollback()
@ -394,7 +397,8 @@ class Importer:
try:
handsId = fpdb_parse_logic.mainParser( self.settings, self.fdb
, self.siteIds[site], category, hand, self.config)
, self.siteIds[site], category, hand
, self.config, self.database )
self.fdb.db.commit()
stored += 1

View File

@ -21,10 +21,13 @@ import fpdb_simple
import Database
#parses a holdem hand
def mainParser(settings, fdb, siteID, category, hand, config):
def mainParser(settings, fdb, siteID, category, hand, config, db = None):
backend = settings['db-backend']
#This is redundant - hopefully fdb will be a Database object in an interation soon
db = Database.Database(config, 'fpdb', '')
if db == None:
#This is redundant - hopefully fdb will be a Database object in an iteration soon
db = Database.Database(c = config, sql = None)
else:
db = db
category = fpdb_simple.recogniseCategory(hand[0])
base = "hold" if category == "holdem" or category == "omahahi" or category == "omahahilo" else "stud"

View File

@ -1136,16 +1136,6 @@ def storeActions(cursor, handsPlayersIds, actionTypes, allIns, actionAmounts, ac
cursor.executemany("INSERT INTO HandsActions (handsPlayerId, street, actionNo, action, allIn, amount) VALUES (%s, %s, %s, %s, %s, %s)", inserts)
#end def storeActions
def store_board_cards(cursor, hands_id, board_values, board_suits):
#stores into table board_cards
cursor.execute ("""INSERT INTO BoardCards (handId, card1Value, card1Suit,
card2Value, card2Suit, card3Value, card3Suit, card4Value, card4Suit,
card5Value, card5Suit) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""",
(hands_id, board_values[0], board_suits[0], board_values[1], board_suits[1],
board_values[2], board_suits[2], board_values[3], board_suits[3],
board_values[4], board_suits[4]))
#end def store_board_cards
def storeHands(backend, conn, cursor, site_hand_no, gametype_id
,hand_start_time, names, tableName, maxSeats, hudCache,
board_values, board_suits):