diff --git a/pyfpdb/Configuration.py b/pyfpdb/Configuration.py index 27e06b3d..e01a6b43 100755 --- a/pyfpdb/Configuration.py +++ b/pyfpdb/Configuration.py @@ -86,14 +86,34 @@ class Site: self.aux_window = node.getAttribute("aux_window") self.font = node.getAttribute("font") self.font_size = node.getAttribute("font_size") - self.use_frames = node.getAttribute("use_frames") + 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 diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index 932a8578..0f354054 100755 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -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,13 +87,28 @@ 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() 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 diff --git a/pyfpdb/Filters.py b/pyfpdb/Filters.py index 4846b998..9ded56b4 100644 --- a/pyfpdb/Filters.py +++ b/pyfpdb/Filters.py @@ -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()) diff --git a/pyfpdb/FpdbSQLQueries.py b/pyfpdb/FpdbSQLQueries.py index b7e5679e..127644b3 100644 --- a/pyfpdb/FpdbSQLQueries.py +++ b/pyfpdb/FpdbSQLQueries.py @@ -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 - # use 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 - # use here ? - and hp.tourneysPlayersId IS NULL - and ((hp.card1Value = and hp.card2Value = ) or (hp.card1Value = and hp.card2Value = )) - 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""" diff --git a/pyfpdb/GuiGraphViewer.py b/pyfpdb/GuiGraphViewer.py index cec91e61..811b1e82 100644 --- a/pyfpdb/GuiGraphViewer.py +++ b/pyfpdb/GuiGraphViewer.py @@ -43,14 +43,14 @@ 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.sql=querylist + 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 filters_display = { "Heroes" : True, @@ -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() diff --git a/pyfpdb/GuiPlayerStats.py b/pyfpdb/GuiPlayerStats.py index 268aeab3..4ad21764 100644 --- a/pyfpdb/GuiPlayerStats.py +++ b/pyfpdb/GuiPlayerStats.py @@ -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])] diff --git a/pyfpdb/GuiPositionalStats.py b/pyfpdb/GuiPositionalStats.py index 0e20e632..6fde0617 100644 --- a/pyfpdb/GuiPositionalStats.py +++ b/pyfpdb/GuiPositionalStats.py @@ -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): diff --git a/pyfpdb/Hand.py b/pyfpdb/Hand.py index 38c3609b..1846b240 100644 --- a/pyfpdb/Hand.py +++ b/pyfpdb/Hand.py @@ -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""" @@ -390,7 +488,7 @@ Add a raise on [street] by [player] to [amountTo] Map the tuple self.gametype onto the pokerstars string describing it """ # currently it appears to be something like ["ring", "hold", "nl", sb, bb]: - gs = {"holdem" : "Hold'em", + gs = {"holdem" : "Hold'em", "omahahi" : "Omaha", "omahahilo" : "Omaha Hi/Lo", "razz" : "Razz", @@ -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' @@ -994,12 +1184,20 @@ Add a complete on [street] by [player] to [amountTo] self.actions['THIRD'].append(act) 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): diff --git a/pyfpdb/HandHistoryConverter.py b/pyfpdb/HandHistoryConverter.py index cd3a1db0..590ee065 100644 --- a/pyfpdb/HandHistoryConverter.py +++ b/pyfpdb/HandHistoryConverter.py @@ -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) diff --git a/pyfpdb/Hello.py b/pyfpdb/Hello.py index 41262d6c..7f3f6442 100644 --- a/pyfpdb/Hello.py +++ b/pyfpdb/Hello.py @@ -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']) diff --git a/pyfpdb/Hud.py b/pyfpdb/Hud.py index 56ad08f3..888655bc 100644 --- a/pyfpdb/Hud.py +++ b/pyfpdb/Hud.py @@ -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) + 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: - 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 +# 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 - window.set_title(real_name) +# window.set_title(real_name) if __name__== "__main__": main_window = gtk.Window() diff --git a/pyfpdb/Mucked.py b/pyfpdb/Mucked.py index 8ae6bc60..b3b134c5 100755 --- a/pyfpdb/Mucked.py +++ b/pyfpdb/Mucked.py @@ -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,15 +80,20 @@ 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]) - self.m_windows[i].set_opacity(float(self.params['opacity'])) + 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() - self.m_windows[i].hide() + if self.uses_timer: + self.m_windows[i].hide() + + def update_gui(self, new_hand_id): + """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'])*n_cards, + int(self.params['card_ht'])) + x = 0 # x coord where the next card starts in scratch + for card in cards: +# concatenate each card image to scratch + 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']) + 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']) 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 + if self.displayed: self.hide() + +# See how many players showed a hand. Skip if only 1 shows (= hero) + n_sd = 0 for (i, cards) in self.hud.cards.iteritems(): - if self.has_cards(cards): -# 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_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)]: -# concatenate each card image to scratch - self.card_images[self.split_cards(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 + 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 - for stats in self.hud.stat_dict.itervalues(): - self.eb[stats['seat']].set_tooltip_text(stats['screen_name']) + super(Flop_Mucked, self).update_gui(new_hand_id) - if self.displayed_cards and float(self.params['timeout']) > 0: + 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 diff --git a/pyfpdb/PokerStarsToFpdb.py b/pyfpdb/PokerStarsToFpdb.py index 4b66e899..41b3c67f 100755 --- a/pyfpdb/PokerStarsToFpdb.py +++ b/pyfpdb/PokerStarsToFpdb.py @@ -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[0-9]+):\s+(HORSE)? \(?(?PHold\'em|Razz|7 Card Stud|7 Card Stud Hi/Lo|Omaha|Omaha Hi/Lo|Badugi) (?PNo Limit|Limit|Pot Limit),? \(?(?P\$|)?(?P[.0-9]+)/\$?(?P[.0-9]+)\) - (?P.*$)", re.MULTILINE) + re_GameInfo = re.compile("""PokerStars\sGame\s\#(?P[0-9]+):\s+ + (Tournament\s\#(?P\d+),\s(?P[\$\+\d\.]+)\s)? + (?PHORSE|8\-Game|HOSE)?\s?\(? + (?PHold\'em|Razz|7\sCard\sStud|7\sCard\sStud\sHi/Lo|Omaha|Omaha\sHi/Lo|Badugi|Triple\sDraw\s2\-7\sLowball)\s + (?PNo\sLimit|Limit|Pot\sLimit)\)?,?\s + (-\sLevel\s(?P[IVXLC]+)\s)?\(? + (?P\$|)? + (?P[.0-9]+)/\$? + (?P[.0-9]+)\)\s-\s + (?P.*$)""", + re.MULTILINE|re.VERBOSE) re_SplitHands = re.compile('\n\n+') re_TailSplitHands = re.compile('(\n\n\n+)') - re_HandInfo = re.compile("^Table \'(?P[- a-zA-Z]+)\'(?P.+?$)?", re.MULTILINE) + re_HandInfo = re.compile("""^Table\s\'(?P
[-\ a-zA-Z\d]+)\'\s + ((?P\d+)-max\s)? + (?P\(Play\sMoney\)\s)? + (Seat\s\#(?P
[ a-zA-Z]+) - \$?(?P[.0-9]+)/\$?(?P[.0-9]+) - (?P.*) - (?P
[0-9]+):(?P[0-9]+) ET - (?P[0-9]+)/(?P[0-9]+)/(?P[0-9]+)Table (?P
[ a-zA-Z]+)\nSeat (?P