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

This commit is contained in:
Mika Bostrom 2009-10-21 20:29:44 +03:00
commit 616c07a4eb
12 changed files with 894 additions and 751 deletions

View File

@ -105,7 +105,7 @@ class Betfair(HandHistoryConverter):
logging.debug("HID %s, Table %s" % (m.group('HID'), m.group('TABLE'))) logging.debug("HID %s, Table %s" % (m.group('HID'), m.group('TABLE')))
hand.handid = m.group('HID') hand.handid = m.group('HID')
hand.tablename = m.group('TABLE') hand.tablename = m.group('TABLE')
hand.starttime = time.strptime(m.group('DATETIME'), "%A, %B %d, %H:%M:%S GMT %Y") hand.starttime = datetime.datetime.strptime(m.group('DATETIME'), "%A, %B %d, %H:%M:%S GMT %Y")
#hand.buttonpos = int(m.group('BUTTON')) #hand.buttonpos = int(m.group('BUTTON'))
def readPlayerStacks(self, hand): def readPlayerStacks(self, hand):
@ -144,6 +144,7 @@ class Betfair(HandHistoryConverter):
def readAntes(self, hand): def readAntes(self, hand):
logging.debug("reading antes") logging.debug("reading antes")
m = self.re_Antes.finditer(hand.handText)
for player in m: for player in m:
logging.debug("hand.addAnte(%s,%s)" %(player.group('PNAME'), player.group('ANTE'))) logging.debug("hand.addAnte(%s,%s)" %(player.group('PNAME'), player.group('ANTE')))
hand.addAnte(player.group('PNAME'), player.group('ANTE')) hand.addAnte(player.group('PNAME'), player.group('ANTE'))
@ -160,17 +161,15 @@ class Betfair(HandHistoryConverter):
hand.buttonpos = int(self.re_Button.search(hand.handText).group('BUTTON')) hand.buttonpos = int(self.re_Button.search(hand.handText).group('BUTTON'))
def readHeroCards(self, hand): def readHeroCards(self, hand):
m = self.re_HeroCards.search(hand.handText) # streets PREFLOP, PREDRAW, and THIRD are special cases beacause
if(m == None): # we need to grab hero's cards
#Not involved in hand for street in ('PREFLOP', 'DEAL'):
hand.involved = False if street in hand.streets.keys():
else: m = self.re_HeroCards.finditer(hand.streets[street])
hand.hero = m.group('PNAME') for found in m:
# "2c, qh" -> set(["2c","qc"]) hand.hero = found.group('PNAME')
# Also works with Omaha hands. newcards = [c.strip() for c in found.group('CARDS').split(',')]
cards = m.group('CARDS') hand.addHoleCards(street, hand.hero, closed=newcards, shown=False, mucked=False, dealt=True)
cards = [c.strip() for c in cards.split(',')]
hand.addHoleCards(cards, m.group('PNAME'))
def readStudPlayerCards(self, hand, street): def readStudPlayerCards(self, hand, street):
# balh blah blah # balh blah blah

View File

@ -398,7 +398,7 @@ class Database:
print "*** Error: " + err[2] + "(" + str(err[1]) + "): " + str(sys.exc_info()[1]) print "*** Error: " + err[2] + "(" + str(err[1]) + "): " + str(sys.exc_info()[1])
else: else:
if row and row[0]: if row and row[0]:
self.hand_1_day_ago = row[0] self.hand_1day_ago = int(row[0])
d = timedelta(days=hud_days) d = timedelta(days=hud_days)
now = datetime.utcnow() - d now = datetime.utcnow() - d
@ -440,10 +440,6 @@ class Database:
if hud_style == 'S' or h_hud_style == 'S': if hud_style == 'S' or h_hud_style == 'S':
self.get_stats_from_hand_session(hand, stat_dict, hero_id, hud_style, h_hud_style) self.get_stats_from_hand_session(hand, stat_dict, hero_id, hud_style, h_hud_style)
try:
print "Session: hero_id =", hero_id, "hds =", stat_dict[hero_id]['n']
except:
pass
if hud_style == 'S' and h_hud_style == 'S': if hud_style == 'S' and h_hud_style == 'S':
return stat_dict return stat_dict
@ -469,7 +465,7 @@ class Database:
#if aggregate: always use aggregate query now: use agg_bb_mult of 1 for no aggregation: #if aggregate: always use aggregate query now: use agg_bb_mult of 1 for no aggregation:
query = 'get_stats_from_hand_aggregated' query = 'get_stats_from_hand_aggregated'
subs = (hand, hero_id, stylekey, agg_bb_mult, agg_bb_mult, hero_id, h_stylekey, h_agg_bb_mult, h_agg_bb_mult) subs = (hand, hero_id, stylekey, agg_bb_mult, agg_bb_mult, hero_id, h_stylekey, h_agg_bb_mult, h_agg_bb_mult)
print "agg query subs:", subs #print "agg query subs:", subs
#else: #else:
# query = 'get_stats_from_hand' # query = 'get_stats_from_hand'
# subs = (hand, stylekey) # subs = (hand, stylekey)
@ -488,10 +484,6 @@ class Database:
t_dict[name.lower()] = val t_dict[name.lower()] = val
# print t_dict # print t_dict
stat_dict[t_dict['player_id']] = t_dict stat_dict[t_dict['player_id']] = t_dict
try:
print "get_stats end: hero_id =", hero_id, "hds =", stat_dict[hero_id]['n']
except:
pass
return stat_dict return stat_dict
@ -1392,41 +1384,7 @@ class Database:
def storeHand(self, p): def storeHand(self, p):
#stores into table hands: #stores into table hands:
q = """INSERT INTO Hands ( q = self.sql.query['store_hand']
tablename,
gametypeid,
sitehandno,
handstart,
importtime,
seats,
maxseats,
texture,
playersVpi,
boardcard1,
boardcard2,
boardcard3,
boardcard4,
boardcard5,
playersAtStreet1,
playersAtStreet2,
playersAtStreet3,
playersAtStreet4,
playersAtShowdown,
street0Raises,
street1Raises,
street2Raises,
street3Raises,
street4Raises,
street1Pot,
street2Pot,
street3Pot,
street4Pot,
showdownPot
)
VALUES
(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
%s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
%s, %s, %s, %s, %s, %s, %s, %s, %s)"""
q = q.replace('%s', self.sql.query['placeholder']) q = q.replace('%s', self.sql.query['placeholder'])
@ -1461,19 +1419,40 @@ class Database:
p['street4Pot'], p['street4Pot'],
p['showdownPot'] p['showdownPot']
)) ))
#return getLastInsertId(backend, conn, cursor) return self.get_last_insert_id(self.cursor)
# def storeHand # def storeHand
def storeHandsPlayers(self, hid, pid, p): def storeHandsPlayers(self, hid, pids, pdata):
#print "DEBUG: %s %s %s" %(hid, pids, pdata)
inserts = []
for p in pdata:
inserts.append( (hid,
pids[p],
pdata[p]['startCash'],
pdata[p]['seatNo'],
pdata[p]['street0Aggr'],
pdata[p]['street1Aggr'],
pdata[p]['street2Aggr'],
pdata[p]['street3Aggr'],
pdata[p]['street4Aggr']
) )
q = """INSERT INTO HandsPlayers ( q = """INSERT INTO HandsPlayers (
handId, handId,
playerId playerId,
startCash,
seatNo,
street0Aggr,
street1Aggr,
street2Aggr,
street3Aggr,
street4Aggr
) )
VALUES ( VALUES (
%s, %s %s, %s, %s, %s, %s,
%s, %s, %s, %s
)""" )"""
# startCash,
# position, # position,
# tourneyTypeId, # tourneyTypeId,
# card1, # card1,
@ -1483,10 +1462,8 @@ class Database:
# startCards, # startCards,
# winnings, # winnings,
# rake, # rake,
# seatNo,
# totalProfit, # totalProfit,
# street0VPI, # street0VPI,
# street0Aggr,
# street0_3BChance, # street0_3BChance,
# street0_3BDone, # street0_3BDone,
# street1Seen, # street1Seen,
@ -1494,10 +1471,6 @@ class Database:
# street3Seen, # street3Seen,
# street4Seen, # street4Seen,
# sawShowdown, # sawShowdown,
# street1Aggr,
# street2Aggr,
# street3Aggr,
# street4Aggr,
# otherRaisedStreet1, # otherRaisedStreet1,
# otherRaisedStreet2, # otherRaisedStreet2,
# otherRaisedStreet3, # otherRaisedStreet3,
@ -1551,85 +1524,8 @@ class Database:
q = q.replace('%s', self.sql.query['placeholder']) q = q.replace('%s', self.sql.query['placeholder'])
self.cursor.execute(q, ( #print "DEBUG: inserts: %s" %inserts
hid, self.cursor.executemany(q, inserts)
pid
))
# startCash,
# position,
# tourneyTypeId,
# card1,
# card2,
# card3,
# card4,
# startCards,
# winnings,
# rake,
# seatNo,
# totalProfit,
# street0VPI,
# street0Aggr,
# street0_3BChance,
# street0_3BDone,
# street1Seen,
# street2Seen,
# street3Seen,
# street4Seen,
# sawShowdown,
# street1Aggr,
# street2Aggr,
# street3Aggr,
# street4Aggr,
# otherRaisedStreet1,
# otherRaisedStreet2,
# otherRaisedStreet3,
# otherRaisedStreet4,
# foldToOtherRaisedStreet1,
# foldToOtherRaisedStreet2,
# foldToOtherRaisedStreet3,
# foldToOtherRaisedStreet4,
# wonWhenSeenStreet1,
# wonAtSD,
# stealAttemptChance,
# stealAttempted,
# foldBbToStealChance,
# foldedBbToSteal,
# foldSbToStealChance,
# foldedSbToSteal,
# street1CBChance,
# street1CBDone,
# street2CBChance,
# street2CBDone,
# street3CBChance,
# street3CBDone,
# street4CBChance,
# street4CBDone,
# foldToStreet1CBChance,
# foldToStreet1CBDone,
# foldToStreet2CBChance,
# foldToStreet2CBDone,
# foldToStreet3CBChance,
# foldToStreet3CBDone,
# foldToStreet4CBChance,
# foldToStreet4CBDone,
# street1CheckCallRaiseChance,
# street1CheckCallRaiseDone,
# street2CheckCallRaiseChance,
# street2CheckCallRaiseDone,
# street3CheckCallRaiseChance,
# street3CheckCallRaiseDone,
# street4CheckCallRaiseChance,
# street4CheckCallRaiseDone,
# street0Calls,
# street1Calls,
# street2Calls,
# street3Calls,
# street4Calls,
# street0Bets,
# street1Bets,
# street2Bets,
# street3Bets,
# street4Bets
def storeHudCacheNew(self, gid, pid, hc): def storeHudCacheNew(self, gid, pid, hc):
q = """INSERT INTO HudCache ( q = """INSERT INTO HudCache (

View File

@ -29,6 +29,8 @@ class DerivedStats():
for player in hand.players: for player in hand.players:
self.handsplayers[player[1]] = {} self.handsplayers[player[1]] = {}
#Init vars that may not be used, but still need to be inserted.
self.handsplayers[player[1]]['street4Aggr'] = False
self.assembleHands(self.hand) self.assembleHands(self.hand)
self.assembleHandsPlayers(self.hand) self.assembleHandsPlayers(self.hand)
@ -39,6 +41,9 @@ class DerivedStats():
def getHands(self): def getHands(self):
return self.hands return self.hands
def getHandsPlayers(self):
return self.handsplayers
def assembleHands(self, hand): def assembleHands(self, hand):
self.hands['tableName'] = hand.tablename self.hands['tableName'] = hand.tablename
self.hands['siteHandNo'] = hand.handid self.hands['siteHandNo'] = hand.handid
@ -77,10 +82,15 @@ class DerivedStats():
# commentTs DATETIME # commentTs DATETIME
def assembleHandsPlayers(self, hand): def assembleHandsPlayers(self, hand):
self.vpip(self.hand) #hand.players = [[seat, name, chips],[seat, name, chips]]
for player in hand.players:
self.handsplayers[player[1]]['seatNo'] = player[0]
self.handsplayers[player[1]]['startCash'] = player[2]
for i, street in enumerate(hand.actionStreets[1:]): for i, street in enumerate(hand.actionStreets[1:]):
self.aggr(self.hand, i) self.aggr(self.hand, i)
def assembleHudCache(self, hand): def assembleHudCache(self, hand):
# # def generateHudCacheData(player_ids, base, category, action_types, allIns, actionTypeByNo # # def generateHudCacheData(player_ids, base, category, action_types, allIns, actionTypeByNo
# # ,winnings, totalWinnings, positions, actionTypes, actionAmounts, antes): # # ,winnings, totalWinnings, positions, actionTypes, actionAmounts, antes):
@ -777,20 +787,22 @@ class DerivedStats():
if act[1] in ('calls','bets', 'raises'): if act[1] in ('calls','bets', 'raises'):
vpipers.add(act[0]) vpipers.add(act[0])
#for player in hand.players:
# print "DEBUG: '%s' '%s' '%s'" %(player, player[1], vpipers)
# if player[1] in vpipers:
# self.handsplayers[player[1]]['vpip'] = True
# else:
# self.handsplayers[player[1]]['vpip'] = False
self.hands['playersVpi'] = len(vpipers) self.hands['playersVpi'] = len(vpipers)
for player in hand.players:
if player[1] in vpipers:
self.handsplayers[player[1]]['vpip'] = True
else:
self.handsplayers[player[1]]['vpip'] = False
def playersAtStreetX(self, hand): def playersAtStreetX(self, hand):
""" playersAtStreet1 SMALLINT NOT NULL, /* num of players seeing flop/street4/draw1 */""" """ playersAtStreet1 SMALLINT NOT NULL, /* num of players seeing flop/street4/draw1 */"""
# self.actions[street] is a list of all actions in a tuple, contining the player name first # self.actions[street] is a list of all actions in a tuple, contining the player name first
# [ (player, action, ....), (player2, action, ...) ] # [ (player, action, ....), (player2, action, ...) ]
# The number of unique players in the list per street gives the value for playersAtStreetXXX # The number of unique players in the list per street gives the value for playersAtStreetXXX
# FIXME?? - This isn't couting people that are all in - at least showdown needs to reflect this
self.hands['playersAtStreet1'] = 0 self.hands['playersAtStreet1'] = 0
self.hands['playersAtStreet2'] = 0 self.hands['playersAtStreet2'] = 0
self.hands['playersAtStreet3'] = 0 self.hands['playersAtStreet3'] = 0

View File

@ -55,6 +55,7 @@ class Filters(threading.Thread):
,'seatsbetween':'Between:', 'seatsand':'And:', 'seatsshow':'Show Number of _Players' ,'seatsbetween':'Between:', 'seatsand':'And:', 'seatsshow':'Show Number of _Players'
,'limitstitle':'Limits:', 'seatstitle':'Number of Players:' ,'limitstitle':'Limits:', 'seatstitle':'Number of Players:'
,'groupstitle':'Grouping:', 'posnshow':'Show Position Stats:' ,'groupstitle':'Grouping:', 'posnshow':'Show Position Stats:'
,'groupsall':'All Players'
,'limitsFL':'FL', 'limitsNL':'NL', 'ring':'Ring', 'tour':'Tourney' ,'limitsFL':'FL', 'limitsNL':'NL', 'ring':'Ring', 'tour':'Tourney'
} }
@ -64,6 +65,10 @@ class Filters(threading.Thread):
self.start_date.set_property('editable', False) self.start_date.set_property('editable', False)
self.end_date.set_property('editable', False) self.end_date.set_property('editable', False)
# For use in groups etc
self.sbGroups = {}
self.numHands = 0
# Outer Packing box # Outer Packing box
self.mainVBox = gtk.VBox(False, 0) self.mainVBox = gtk.VBox(False, 0)
@ -71,7 +76,7 @@ class Filters(threading.Thread):
playerFrame.set_label_align(0.0, 0.0) playerFrame.set_label_align(0.0, 0.0)
vbox = gtk.VBox(False, 0) vbox = gtk.VBox(False, 0)
self.fillPlayerFrame(vbox) self.fillPlayerFrame(vbox, self.display)
playerFrame.add(vbox) playerFrame.add(vbox)
self.boxes['player'] = vbox self.boxes['player'] = vbox
@ -122,7 +127,6 @@ class Filters(threading.Thread):
groupsFrame = gtk.Frame() groupsFrame = gtk.Frame()
groupsFrame.show() groupsFrame.show()
vbox = gtk.VBox(False, 0) vbox = gtk.VBox(False, 0)
self.sbGroups = {}
self.fillGroupsFrame(vbox, self.display) self.fillGroupsFrame(vbox, self.display)
groupsFrame.add(vbox) groupsFrame.add(vbox)
@ -181,6 +185,9 @@ class Filters(threading.Thread):
return self.mainVBox return self.mainVBox
#end def get_vbox #end def get_vbox
def getNumHands(self):
return self.numHands
def getSites(self): def getSites(self):
return self.sites return self.sites
@ -256,6 +263,13 @@ class Filters(threading.Thread):
self.heroes[site] = w.get_text() self.heroes[site] = w.get_text()
# print "DEBUG: setting heroes[%s]: %s"%(site, self.heroes[site]) # print "DEBUG: setting heroes[%s]: %s"%(site, self.heroes[site])
def __set_num_hands(self, w, val):
try:
self.numHands = int(w.get_text())
except:
self.numHands = 0
# print "DEBUG: setting numHands:", self.numHands
def createSiteLine(self, hbox, site): def createSiteLine(self, hbox, site):
cb = gtk.CheckButton(site) cb = gtk.CheckButton(site)
cb.connect('clicked', self.__set_site_select, site) cb.connect('clicked', self.__set_site_select, site)
@ -393,13 +407,32 @@ class Filters(threading.Thread):
self.groups[group] = w.get_active() self.groups[group] = w.get_active()
print "self.groups[%s] set to %s" %(group, self.groups[group]) print "self.groups[%s] set to %s" %(group, self.groups[group])
def fillPlayerFrame(self, vbox): def fillPlayerFrame(self, vbox, display):
for site in self.conf.get_supported_sites(): for site in self.conf.get_supported_sites():
pathHBox = gtk.HBox(False, 0) hBox = gtk.HBox(False, 0)
vbox.pack_start(pathHBox, False, True, 0) vbox.pack_start(hBox, False, True, 0)
player = self.conf.supported_sites[site].screen_name player = self.conf.supported_sites[site].screen_name
self.createPlayerLine(pathHBox, site, player) self.createPlayerLine(hBox, site, player)
if "GroupsAll" in display and display["GroupsAll"] == True:
hbox = gtk.HBox(False, 0)
vbox.pack_start(hbox, False, False, 0)
cb = gtk.CheckButton(self.filterText['groupsall'])
cb.connect('clicked', self.__set_group_select, 'allplayers')
hbox.pack_start(cb, False, False, 0)
self.sbGroups['allplayers'] = cb
self.groups['allplayers'] = False
lbl = gtk.Label('Min # Hands:')
lbl.set_alignment(xalign=1.0, yalign=0.5)
hbox.pack_start(lbl, expand=True, padding=3)
phands = gtk.Entry()
phands.set_text('0')
phands.set_width_chars(8)
hbox.pack_start(phands, False, False, 0)
phands.connect("changed", self.__set_num_hands, site)
def fillSitesFrame(self, vbox): def fillSitesFrame(self, vbox):
for site in self.conf.get_supported_sites(): for site in self.conf.get_supported_sites():

View File

@ -40,21 +40,6 @@ class GuiBulkImport():
# CONFIGURATION - update these as preferred: # CONFIGURATION - update these as preferred:
allowThreads = True # set to True to try out the threads field allowThreads = True # set to True to try out the threads field
# not used
def import_dir(self):
"""imports a directory, non-recursive. todo: move this to fpdb_import so CLI can use it"""
self.path = self.inputFile
self.importer.addImportDirectory(self.path)
self.importer.setCallHud(False)
starttime = time()
if not self.importer.settings['threads'] > 1:
(stored, dups, partial, errs, ttime) = self.importer.runImport()
print 'GuiBulkImport.import_dir done: Stored: %d Duplicates: %d Partial: %d Errors: %d in %s seconds - %d/sec'\
% (stored, dups, partial, errs, ttime, stored / ttime)
else:
self.importer.RunImportThreaded()
def dopulse(self): def dopulse(self):
self.progressbar.pulse() self.progressbar.pulse()
return True return True
@ -77,7 +62,7 @@ class GuiBulkImport():
self.timer = gobject.timeout_add(100, self.dopulse) self.timer = gobject.timeout_add(100, self.dopulse)
# get the dir to import from the chooser # get the dir to import from the chooser
self.inputFile = self.chooser.get_filename() selected = self.chooser.get_filenames()
# get the import settings from the gui and save in the importer # get the import settings from the gui and save in the importer
self.importer.setHandCount(int(self.spin_hands.get_text())) self.importer.setHandCount(int(self.spin_hands.get_text()))
@ -103,7 +88,8 @@ class GuiBulkImport():
self.importer.setDropHudCache("auto") self.importer.setDropHudCache("auto")
sitename = self.cbfilter.get_model()[self.cbfilter.get_active()][0] sitename = self.cbfilter.get_model()[self.cbfilter.get_active()][0]
self.importer.addBulkImportImportFileOrDir(self.inputFile, site = sitename) for selection in selected:
self.importer.addBulkImportImportFileOrDir(selection, site = sitename)
self.importer.setCallHud(False) self.importer.setCallHud(False)
starttime = time() starttime = time()
# try: # try:
@ -151,6 +137,7 @@ class GuiBulkImport():
self.chooser = gtk.FileChooserWidget() self.chooser = gtk.FileChooserWidget()
self.chooser.set_filename(self.settings['bulkImport-defaultPath']) self.chooser.set_filename(self.settings['bulkImport-defaultPath'])
self.chooser.set_select_multiple(True)
self.vbox.add(self.chooser) self.vbox.add(self.chooser)
self.chooser.show() self.chooser.show()
@ -317,8 +304,20 @@ def main(argv=None):
help="If this option is passed it quits when it encounters any error") help="If this option is passed it quits when it encounters any error")
parser.add_option("-m", "--minPrint", "--status", dest="minPrint", default="0", type="int", parser.add_option("-m", "--minPrint", "--status", dest="minPrint", default="0", type="int",
help="How often to print a one-line status report (0 (default) means never)") help="How often to print a one-line status report (0 (default) means never)")
parser.add_option("-u", "--usage", action="store_true", dest="usage", default=False,
help="Print some useful one liners")
(options, sys.argv) = parser.parse_args(args = argv) (options, sys.argv) = parser.parse_args(args = argv)
if options.usage == True:
#Print usage examples and exit
print "USAGE:"
print 'PokerStars converter: ./GuiBulkImport -c PokerStars -f filename'
print 'Full Tilt converter: ./GuiBulkImport -c "Full Tilt Poker" -f filename'
print "Everleaf converter: ./GuiBulkImport -c Everleaf -f filename"
print "Absolute converter: ./GuiBulkImport -c Absolute -f filename"
print "PartyPoker converter: ./GuiBulkImport -c PartyPoker -f filename"
sys.exit(0)
config = Configuration.Config() config = Configuration.Config()
settings = {} settings = {}
@ -350,8 +349,10 @@ def main(argv=None):
importer.setThreads(-1) importer.setThreads(-1)
importer.addBulkImportImportFileOrDir(os.path.expanduser(options.filename), site=options.filtername) importer.addBulkImportImportFileOrDir(os.path.expanduser(options.filename), site=options.filtername)
importer.setCallHud(False) importer.setCallHud(False)
importer.runImport() (stored, dups, partial, errs, ttime) = importer.runImport()
importer.clearFileList() importer.clearFileList()
print 'GuiBulkImport done: Stored: %d \tDuplicates: %d \tPartial: %d \tErrors: %d in %s seconds - %.0f/sec'\
% (stored, dups, partial, errs, ttime, (stored+0.0) / ttime)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -36,6 +36,8 @@ class GuiPlayerStats (threading.Thread):
self.main_window = mainwin self.main_window = mainwin
self.sql = querylist self.sql = querylist
self.liststore = None
self.MYSQL_INNODB = 2 self.MYSQL_INNODB = 2
self.PGSQL = 3 self.PGSQL = 3
self.SQLITE = 4 self.SQLITE = 4
@ -65,6 +67,7 @@ class GuiPlayerStats (threading.Thread):
"SeatSep" : True, "SeatSep" : True,
"Dates" : True, "Dates" : True,
"Groups" : True, "Groups" : True,
"GroupsAll" : True,
"Button1" : True, "Button1" : True,
"Button2" : True "Button2" : True
} }
@ -78,29 +81,30 @@ class GuiPlayerStats (threading.Thread):
# ToDo: store in config # ToDo: store in config
# ToDo: create popup to adjust column config # ToDo: create popup to adjust column config
# columns to display, keys match column name returned by sql, values in tuple are: # columns to display, keys match column name returned by sql, values in tuple are:
# is column displayed, column heading, xalignment, formatting # is column displayed, column heading, xalignment, formatting, celltype
self.columns = [ ["game", True, "Game", 0.0, "%s"] self.columns = [ ["game", True, "Game", 0.0, "%s", "str"]
, ["hand", False, "Hand", 0.0, "%s"] # true not allowed for this line , ["hand", False, "Hand", 0.0, "%s", "str"] # true not allowed for this line
, ["plposition", False, "Posn", 1.0, "%s"] # true not allowed for this line (set in code) , ["plposition", False, "Posn", 1.0, "%s", "str"] # true not allowed for this line (set in code)
, ["n", True, "Hds", 1.0, "%d"] , ["pname", False, "Name", 0.0, "%s", "str"] # true not allowed for this line (set in code)
, ["avgseats", False, "Seats", 1.0, "%3.1f"] , ["n", True, "Hds", 1.0, "%d", "str"]
, ["vpip", True, "VPIP", 1.0, "%3.1f"] , ["avgseats", False, "Seats", 1.0, "%3.1f", "str"]
, ["pfr", True, "PFR", 1.0, "%3.1f"] , ["vpip", True, "VPIP", 1.0, "%3.1f", "str"]
, ["pf3", True, "PF3", 1.0, "%3.1f"] , ["pfr", True, "PFR", 1.0, "%3.1f", "str"]
, ["steals", True, "Steals", 1.0, "%3.1f"] , ["pf3", True, "PF3", 1.0, "%3.1f", "str"]
, ["saw_f", True, "Saw_F", 1.0, "%3.1f"] , ["steals", True, "Steals", 1.0, "%3.1f", "str"]
, ["sawsd", True, "SawSD", 1.0, "%3.1f"] , ["saw_f", True, "Saw_F", 1.0, "%3.1f", "str"]
, ["wtsdwsf", True, "WtSDwsF", 1.0, "%3.1f"] , ["sawsd", True, "SawSD", 1.0, "%3.1f", "str"]
, ["wmsd", True, "W$SD", 1.0, "%3.1f"] , ["wtsdwsf", True, "WtSDwsF", 1.0, "%3.1f", "str"]
, ["flafq", True, "FlAFq", 1.0, "%3.1f"] , ["wmsd", True, "W$SD", 1.0, "%3.1f", "str"]
, ["tuafq", True, "TuAFq", 1.0, "%3.1f"] , ["flafq", True, "FlAFq", 1.0, "%3.1f", "str"]
, ["rvafq", True, "RvAFq", 1.0, "%3.1f"] , ["tuafq", True, "TuAFq", 1.0, "%3.1f", "str"]
, ["pofafq", False, "PoFAFq", 1.0, "%3.1f"] , ["rvafq", True, "RvAFq", 1.0, "%3.1f", "str"]
, ["net", True, "Net($)", 1.0, "%6.2f"] , ["pofafq", False, "PoFAFq", 1.0, "%3.1f", "str"]
, ["bbper100", True, "bb/100", 1.0, "%4.2f"] , ["net", True, "Net($)", 1.0, "%6.2f", "cash"]
, ["rake", True, "Rake($)", 1.0, "%6.2f"] , ["bbper100", True, "bb/100", 1.0, "%4.2f", "str"]
, ["bb100xr", True, "bbxr/100", 1.0, "%4.2f"] , ["rake", True, "Rake($)", 1.0, "%6.2f", "cash"]
, ["variance", True, "Variance", 1.0, "%5.2f"] , ["bb100xr", True, "bbxr/100", 1.0, "%4.2f", "str"]
, ["variance", True, "Variance", 1.0, "%5.2f", "str"]
] ]
# Detail filters: This holds the data used in the popup window, extra values are # Detail filters: This holds the data used in the popup window, extra values are
@ -125,8 +129,9 @@ class GuiPlayerStats (threading.Thread):
self.stats_vbox = None self.stats_vbox = None
self.detailFilters = [] # the data used to enhance the sql select self.detailFilters = [] # the data used to enhance the sql select
self.main_hbox = gtk.HBox(False, 0) #self.main_hbox = gtk.HBox(False, 0)
self.main_hbox.show() #self.main_hbox.show()
self.main_hbox = gtk.HPaned()
self.stats_frame = gtk.Frame() self.stats_frame = gtk.Frame()
self.stats_frame.show() self.stats_frame.show()
@ -136,8 +141,11 @@ class GuiPlayerStats (threading.Thread):
self.stats_frame.add(self.stats_vbox) self.stats_frame.add(self.stats_vbox)
# self.fillStatsFrame(self.stats_vbox) # self.fillStatsFrame(self.stats_vbox)
self.main_hbox.pack_start(self.filters.get_vbox()) #self.main_hbox.pack_start(self.filters.get_vbox())
self.main_hbox.pack_start(self.stats_frame, expand=True, fill=True) #self.main_hbox.pack_start(self.stats_frame, expand=True, fill=True)
self.main_hbox.pack1(self.filters.get_vbox())
self.main_hbox.pack2(self.stats_frame)
self.main_hbox.show()
# make sure Hand column is not displayed # make sure Hand column is not displayed
[x for x in self.columns if x[0] == 'hand'][0][1] = False [x for x in self.columns if x[0] == 'hand'][0][1] = False
@ -149,7 +157,8 @@ class GuiPlayerStats (threading.Thread):
def refreshStats(self, widget, data): def refreshStats(self, widget, data):
try: self.stats_vbox.destroy() try: self.stats_vbox.destroy()
except AttributeError: pass except AttributeError: pass
self.stats_vbox = gtk.VBox(False, 0) #self.stats_vbox = gtk.VBox(False, 0)
self.stats_vbox = gtk.VPaned()
self.stats_vbox.show() self.stats_vbox.show()
self.stats_frame.add(self.stats_vbox) self.stats_frame.add(self.stats_vbox)
self.fillStatsFrame(self.stats_vbox) self.fillStatsFrame(self.stats_vbox)
@ -193,43 +202,72 @@ class GuiPlayerStats (threading.Thread):
def createStatsTable(self, vbox, playerids, sitenos, limits, type, seats, groups, dates): def createStatsTable(self, vbox, playerids, sitenos, limits, type, seats, groups, dates):
starttime = time() starttime = time()
# Scrolled window for summary table
swin = gtk.ScrolledWindow(hadjustment=None, vadjustment=None)
swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
swin.show()
vbox.pack1(swin)
# Display summary table at top of page # Display summary table at top of page
# 3rd parameter passes extra flags, currently includes: # 3rd parameter passes extra flags, currently includes:
# holecards - whether to display card breakdown (True/False) # holecards - whether to display card breakdown (True/False)
flags = [False] # numhands - min number hands required when displaying all players
self.addTable(vbox, 'playerDetailedStats', flags, playerids, sitenos, limits, type, seats, groups, dates) flags = [False, self.filters.getNumHands()]
self.addTable(swin, 'playerDetailedStats', flags, playerids, sitenos, limits, type, seats, groups, dates)
# Separator # Separator
sep = gtk.HSeparator() vbox2 = gtk.VBox(False, 0)
vbox.pack_start(sep, expand=False, padding=3)
sep.show_now()
vbox.show_now()
heading = gtk.Label(self.filterText['handhead']) heading = gtk.Label(self.filterText['handhead'])
heading.show() heading.show()
vbox.pack_start(heading, expand=False, padding=3) vbox2.pack_start(heading, expand=False, padding=3)
# Scrolled window for detailed table (display by hand) # Scrolled window for detailed table (display by hand)
swin = gtk.ScrolledWindow(hadjustment=None, vadjustment=None) swin = gtk.ScrolledWindow(hadjustment=None, vadjustment=None)
swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
swin.show() swin.show()
vbox.pack_start(swin, expand=True, padding=3) vbox2.pack_start(swin, expand=True, padding=3)
vbox.pack2(vbox2)
vbox1 = gtk.VBox(False, 0) vbox2.show()
vbox1.show()
swin.add_with_viewport(vbox1)
# Detailed table # Detailed table
flags = [True] flags[0] = True
self.addTable(vbox1, 'playerDetailedStats', flags, playerids, sitenos, limits, type, seats, groups, dates) self.addTable(swin, 'playerDetailedStats', flags, playerids, sitenos, limits, type, seats, groups, dates)
self.db.rollback() self.db.rollback()
print "Stats page displayed in %4.2f seconds" % (time() - starttime) print "Stats page displayed in %4.2f seconds" % (time() - starttime)
#end def fillStatsFrame(self, vbox): #end def fillStatsFrame(self, vbox):
def reset_style_render_func(self, treeviewcolumn, cell, model, iter):
cell.set_property('foreground', 'black')
def ledger_style_render_func(self, tvcol, cell, model, iter):
str = cell.get_property('text')
if '-' in str:
str = str.replace("-", "")
str = "(%s)" %(str)
cell.set_property('text', str)
cell.set_property('foreground', 'red')
else:
cell.set_property('foreground', 'green')
return
def sortcols(self, col, n):
#This doesn't actually work yet
if n == 0:
# Card values can stay the same for the moment.
return
if col.get_sort_order() == gtk.SORT_ASCENDING:
col.set_sort_order(gtk.SORT_DESCENDING)
else:
col.set_sort_order(gtk.SORT_ASCENDING)
self.liststore.set_sort_column_id(n, col.get_sort_order())
def addTable(self, vbox, query, flags, playerids, sitenos, limits, type, seats, groups, dates): def addTable(self, vbox, query, flags, playerids, sitenos, limits, type, seats, groups, dates):
counter = 0
row = 0 row = 0
sqlrow = 0 sqlrow = 0
colalias,colshow,colheading,colxalign,colformat = 0,1,2,3,4 colalias,colshow,colheading,colxalign,colformat,coltype = 0,1,2,3,4,5
if not flags: holecards = False if not flags: holecards = False
else: holecards = flags[0] else: holecards = flags[0]
@ -243,16 +281,18 @@ class GuiPlayerStats (threading.Thread):
cols_to_show = [x for x in self.columns if x[colshow]] cols_to_show = [x for x in self.columns if x[colshow]]
hgametypeid_idx = colnames.index('hgametypeid') hgametypeid_idx = colnames.index('hgametypeid')
liststore = gtk.ListStore(*([str] * len(cols_to_show))) self.liststore = gtk.ListStore(*([str] * len(cols_to_show)))
view = gtk.TreeView(model=liststore) view = gtk.TreeView(model=self.liststore)
view.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_BOTH) view.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_BOTH)
vbox.pack_start(view, expand=False, padding=3) #vbox.pack_start(view, expand=False, padding=3)
vbox.add(view)
textcell = gtk.CellRendererText() textcell = gtk.CellRendererText()
textcell50 = gtk.CellRendererText() textcell50 = gtk.CellRendererText()
textcell50.set_property('xalign', 0.5) textcell50.set_property('xalign', 0.5)
numcell = gtk.CellRendererText() numcell = gtk.CellRendererText()
numcell.set_property('xalign', 1.0) numcell.set_property('xalign', 1.0)
listcols = [] listcols = []
idx = 0
# Create header row eg column: ("game", True, "Game", 0.0, "%s") # Create header row eg column: ("game", True, "Game", 0.0, "%s")
for col, column in enumerate(cols_to_show): for col, column in enumerate(cols_to_show):
@ -262,6 +302,9 @@ class GuiPlayerStats (threading.Thread):
s = column[colheading] s = column[colheading]
listcols.append(gtk.TreeViewColumn(s)) listcols.append(gtk.TreeViewColumn(s))
view.append_column(listcols[col]) view.append_column(listcols[col])
#listcols[col].set_clickable(True)
#listcols[col].set_sort_indicator(True)
#listcols[col].connect("clicked", self.sortcols, idx)
if column[colformat] == '%s': if column[colformat] == '%s':
if column[colxalign] == 0.0: if column[colxalign] == 0.0:
listcols[col].pack_start(textcell, expand=True) listcols[col].pack_start(textcell, expand=True)
@ -275,6 +318,11 @@ class GuiPlayerStats (threading.Thread):
listcols[col].add_attribute(numcell, 'text', col) listcols[col].add_attribute(numcell, 'text', col)
listcols[col].set_expand(True) listcols[col].set_expand(True)
#listcols[col].set_alignment(column[colxalign]) # no effect? #listcols[col].set_alignment(column[colxalign]) # no effect?
if column[coltype] == 'cash':
listcols[col].set_cell_data_func(numcell, self.ledger_style_render_func)
else:
listcols[col].set_cell_data_func(numcell, self.reset_style_render_func)
idx = idx+1
rows = len(result) # +1 for title row rows = len(result) # +1 for title row
@ -315,7 +363,7 @@ class GuiPlayerStats (threading.Thread):
treerow.append(column[colformat] % value) treerow.append(column[colformat] % value)
else: else:
treerow.append(' ') treerow.append(' ')
iter = liststore.append(treerow) iter = self.liststore.append(treerow)
sqlrow += 1 sqlrow += 1
row += 1 row += 1
vbox.show_all() vbox.show_all()
@ -323,16 +371,42 @@ class GuiPlayerStats (threading.Thread):
#end def addTable(self, query, vars, playerids, sitenos, limits, type, seats, groups, dates): #end def addTable(self, query, vars, playerids, sitenos, limits, type, seats, groups, dates):
def refineQuery(self, query, flags, playerids, sitenos, limits, type, seats, groups, dates): def refineQuery(self, query, flags, playerids, sitenos, limits, type, seats, groups, dates):
if not flags: holecards = False having = ''
else: holecards = flags[0] if not flags:
holecards = False
numhands = 0
else:
holecards = flags[0]
numhands = flags[1]
if 'allplayers' in groups and groups['allplayers']:
nametest = "(hp.playerId)"
if holecards or groups['posn']:
pname = "'all players'"
# set flag in self.columns to not show player name column
[x for x in self.columns if x[0] == 'pname'][0][1] = False
# can't do this yet (re-write doing more maths in python instead of sql?)
if numhands:
nametest = "(-1)"
else:
pname = "p.name"
# set flag in self.columns to show player name column
[x for x in self.columns if x[0] == 'pname'][0][1] = True
if numhands:
having = ' and count(1) > %d ' % (numhands,)
else:
if playerids: if playerids:
nametest = str(tuple(playerids)) nametest = str(tuple(playerids))
nametest = nametest.replace("L", "") nametest = nametest.replace("L", "")
nametest = nametest.replace(",)",")") nametest = nametest.replace(",)",")")
query = query.replace("<player_test>", nametest)
else: else:
query = query.replace("<player_test>", "1 = 2") nametest = "1 = 2"
pname = "p.name"
# set flag in self.columns to not show player name column
[x for x in self.columns if x[0] == 'pname'][0][1] = False
query = query.replace("<player_test>", nametest)
query = query.replace("<playerName>", pname)
query = query.replace("<havingclause>", having)
if seats: if seats:
query = query.replace('<seats_test>', 'between ' + str(seats['from']) + ' and ' + str(seats['to'])) query = query.replace('<seats_test>', 'between ' + str(seats['from']) + ' and ' + str(seats['to']))
@ -372,9 +446,12 @@ class GuiPlayerStats (threading.Thread):
bbtest = bbtest + " and gt.type = 'tour' " bbtest = bbtest + " and gt.type = 'tour' "
query = query.replace("<gtbigBlind_test>", bbtest) query = query.replace("<gtbigBlind_test>", bbtest)
if holecards: # pinch level variables for hole card query if holecards: # re-use level variables for hole card query
query = query.replace("<hgameTypeId>", "hp.startcards") query = query.replace("<hgameTypeId>", "hp.startcards")
query = query.replace("<orderbyhgameTypeId>", ",hgameTypeId desc") query = query.replace("<orderbyhgameTypeId>"
, ",case when floor(hp.startcards/13) >= mod(hp.startcards,13) then hp.startcards + 0.1 "
+ " else 13*mod(hp.startcards,13) + floor(hp.startcards/13) "
+ " end desc ")
else: else:
query = query.replace("<orderbyhgameTypeId>", "") query = query.replace("<orderbyhgameTypeId>", "")
groupLevels = "show" not in str(limits) groupLevels = "show" not in str(limits)

268
pyfpdb/GuiSessionViewer.py Normal file → Executable file
View File

@ -15,6 +15,7 @@
#In the "official" distribution you can find the license in #In the "official" distribution you can find the license in
#agpl-3.0.txt in the docs folder of the package. #agpl-3.0.txt in the docs folder of the package.
import sys
import threading import threading
import pygtk import pygtk
pygtk.require('2.0') pygtk.require('2.0')
@ -22,7 +23,10 @@ import gtk
import os import os
from time import time, strftime, localtime from time import time, strftime, localtime
try: try:
from numpy import diff, nonzero from numpy import diff, nonzero, sum
# from matplotlib.dates import DateFormatter, WeekdayLocator, HourLocator, \
# DayLocator, MONDAY, timezone
except: except:
print """Failed to load numpy in Session Viewer""" print """Failed to load numpy in Session Viewer"""
print """This is of no consequence as the module currently doesn't do anything.""" print """This is of no consequence as the module currently doesn't do anything."""
@ -34,10 +38,13 @@ import Filters
import FpdbSQLQueries import FpdbSQLQueries
class GuiSessionViewer (threading.Thread): class GuiSessionViewer (threading.Thread):
def __init__(self, config, querylist, debug=True): def __init__(self, config, querylist, mainwin, debug=True):
self.debug = debug self.debug = debug
self.conf = config self.conf = config
self.sql = querylist self.sql = querylist
self.liststore = None
self.MYSQL_INNODB = 2 self.MYSQL_INNODB = 2
self.PGSQL = 3 self.PGSQL = 3
self.SQLITE = 4 self.SQLITE = 4
@ -61,50 +68,58 @@ class GuiSessionViewer (threading.Thread):
"Games" : False, "Games" : False,
"Limits" : True, "Limits" : True,
"LimitSep" : True, "LimitSep" : True,
"LimitType" : True,
"Type" : True,
"Seats" : True, "Seats" : True,
"SeatSep" : True, "SeatSep" : True,
"Dates" : False, "Dates" : True,
"Groups" : True, "Groups" : True,
"GroupsAll" : True,
"Button1" : True, "Button1" : True,
"Button2" : True "Button2" : True
} }
self.filters = Filters.Filters(self.db, self.conf, self.sql, display = filters_display) self.filters = Filters.Filters(self.db, self.conf, self.sql, display = filters_display)
self.filters.registerButton2Name("_Refresh") self.filters.registerButton1Name("_Refresh")
self.filters.registerButton2Callback(self.refreshStats) self.filters.registerButton1Callback(self.refreshStats)
# ToDo: store in config # ToDo: store in config
# ToDo: create popup to adjust column config # ToDo: create popup to adjust column config
# columns to display, keys match column name returned by sql, values in tuple are: # columns to display, keys match column name returned by sql, values in tuple are:
# is column displayed, column heading, xalignment, formatting # is column displayed, column heading, xalignment, formatting
self.columns = [ ("game", True, "Game", 0.0, "%s") self.columns = [ ("sid", True, "SID", 0.0, "%s")
, ("hand", False, "Hand", 0.0, "%s") # true not allowed for this line , ("hand", False, "Hand", 0.0, "%s") # true not allowed for this line
, ("n", True, "Hds", 1.0, "%d") , ("n", True, "Hds", 1.0, "%d")
, ("avgseats", True, "Seats", 1.0, "%3.1f") , ("start", True, "Start", 1.0, "%d")
, ("vpip", True, "VPIP", 1.0, "%3.1f") , ("end", True, "End", 1.0, "%d")
, ("pfr", True, "PFR", 1.0, "%3.1f") , ("hph", True, "Hands/h", 1.0, "%d")
, ("pf3", True, "PF3", 1.0, "%3.1f") , ("profit", True, "Profit", 1.0, "%s")
, ("steals", True, "Steals", 1.0, "%3.1f") #, ("avgseats", True, "Seats", 1.0, "%3.1f")
, ("saw_f", True, "Saw_F", 1.0, "%3.1f") #, ("vpip", True, "VPIP", 1.0, "%3.1f")
, ("sawsd", True, "SawSD", 1.0, "%3.1f") #, ("pfr", True, "PFR", 1.0, "%3.1f")
, ("wtsdwsf", True, "WtSDwsF", 1.0, "%3.1f") #, ("pf3", True, "PF3", 1.0, "%3.1f")
, ("wmsd", True, "W$SD", 1.0, "%3.1f") #, ("steals", True, "Steals", 1.0, "%3.1f")
, ("flafq", True, "FlAFq", 1.0, "%3.1f") #, ("saw_f", True, "Saw_F", 1.0, "%3.1f")
, ("tuafq", True, "TuAFq", 1.0, "%3.1f") #, ("sawsd", True, "SawSD", 1.0, "%3.1f")
, ("rvafq", True, "RvAFq", 1.0, "%3.1f") #, ("wtsdwsf", True, "WtSDwsF", 1.0, "%3.1f")
, ("pofafq", False, "PoFAFq", 1.0, "%3.1f") #, ("wmsd", True, "W$SD", 1.0, "%3.1f")
, ("net", True, "Net($)", 1.0, "%6.2f") #, ("flafq", True, "FlAFq", 1.0, "%3.1f")
, ("bbper100", True, "BB/100", 1.0, "%4.2f") #, ("tuafq", True, "TuAFq", 1.0, "%3.1f")
, ("rake", True, "Rake($)", 1.0, "%6.2f") #, ("rvafq", True, "RvAFq", 1.0, "%3.1f")
, ("variance", True, "Variance", 1.0, "%5.2f") #, ("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")
#, ("variance", True, "Variance", 1.0, "%5.2f")
] ]
self.stats_frame = None self.stats_frame = None
self.stats_vbox = None self.stats_vbox = None
self.detailFilters = [] # the data used to enhance the sql select self.detailFilters = [] # the data used to enhance the sql select
self.main_hbox = gtk.HBox(False, 0) #self.main_hbox = gtk.HBox(False, 0)
self.main_hbox.show() #self.main_hbox.show()
self.main_hbox = gtk.HPaned()
self.stats_frame = gtk.Frame() self.stats_frame = gtk.Frame()
self.stats_frame.show() self.stats_frame.show()
@ -112,21 +127,46 @@ class GuiSessionViewer (threading.Thread):
self.stats_vbox = gtk.VBox(False, 0) self.stats_vbox = gtk.VBox(False, 0)
self.stats_vbox.show() self.stats_vbox.show()
self.stats_frame.add(self.stats_vbox) self.stats_frame.add(self.stats_vbox)
self.fillStatsFrame(self.stats_vbox) # self.fillStatsFrame(self.stats_vbox)
self.main_hbox.pack_start(self.filters.get_vbox())
self.main_hbox.pack_start(self.stats_frame, expand=True, fill=True)
################################
#self.main_hbox.pack_start(self.filters.get_vbox())
#self.main_hbox.pack_start(self.stats_frame, expand=True, fill=True)
self.main_hbox.pack1(self.filters.get_vbox())
self.main_hbox.pack2(self.stats_frame)
self.main_hbox.show()
# make sure Hand column is not displayed # make sure Hand column is not displayed
[x for x in self.columns if x[0] == 'hand'][0][1] == False #[x for x in self.columns if x[0] == 'hand'][0][1] = False
def get_vbox(self): def get_vbox(self):
"""returns the vbox of this thread""" """returns the vbox of this thread"""
return self.main_hbox return self.main_hbox
def generateGraph(self):
fig = figure()
fig.subplots_adjust(bottom=0.2)
ax = fig.add_subplot(111)
ax.xaxis.set_major_locator(mondays)
ax.xaxis.set_minor_locator(alldays)
ax.xaxis.set_major_formatter(weekFormatter)
#ax.xaxis.set_minor_formatter(dayFormatter)
#plot_day_summary(ax, quotes, ticksize=3)
# candlestick(ax, quotes, width=0.6)
# candlestick2(ax, opens, closes, highs, lows, width=4, colorup='k', colordown='r', alpha=0.75)
# Represent the open, close as a bar line and high low range as a vertical line.
# ax : an Axes instance to plot to
# width : the bar width in points
# colorup : the color of the lines where close >= open
# colordown : the color of the lines where close < open
# alpha : bar transparency
# return value is lineCollection, barCollection
ax.xaxis_date()
ax.autoscale_view()
setp( gca().get_xticklabels(), rotation=45, horizontalalignment='right')
show()
def refreshStats(self, widget, data): def refreshStats(self, widget, data):
try: self.stats_vbox.destroy() try: self.stats_vbox.destroy()
except AttributeError: pass except AttributeError: pass
@ -209,105 +249,97 @@ class GuiSessionViewer (threading.Thread):
if not flags: holecards = False if not flags: holecards = False
else: holecards = flags[0] else: holecards = flags[0]
# pre-fetch some constant values:
cols_to_show = [x for x in self.columns if x[colshow]]
self.stats_table = gtk.Table(1, 1, False) self.liststore = gtk.ListStore(*([str] * len(cols_to_show)))
self.stats_table.set_col_spacings(4)
self.stats_table.show()
self.db.cursor.execute("""select UNIX_TIMESTAMP(handStart) as time, id from Hands ORDER BY time""") view = gtk.TreeView(model=self.liststore)
view.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_BOTH)
vbox.add(view)
textcell = gtk.CellRendererText()
textcell50 = gtk.CellRendererText()
textcell50.set_property('xalign', 0.5)
numcell = gtk.CellRendererText()
numcell.set_property('xalign', 1.0)
listcols = []
# Create header row eg column: ("game", True, "Game", 0.0, "%s")
for col, column in enumerate(cols_to_show):
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)
# Get a list of all handids and their timestampts
# FIXME: Will probably want to be able to filter this list eventually
# FIXME: Join on handsplayers for Hero to get other useful stuff like total profit?
q = """
select UNIX_TIMESTAMP(h.handStart) as time, hp.handId, hp.startCash, hp.winnings, hp.totalProfit
from HandsPlayers hp
inner join Hands h on (h.id = hp.handId)
inner join Gametypes gt on (gt.Id = h.gameTypeId)
inner join Sites s on (s.Id = gt.siteId)
inner join Players p on (p.Id = hp.playerId)
where hp.playerId in (2)
order by time
"""
self.db.cursor.execute(q)
THRESHOLD = 1800 THRESHOLD = 1800
hands = self.db.cursor.fetchall() hands = self.db.cursor.fetchall()
# Take that list and create an array of the time between hands
times = map(lambda x:long(x[0]), hands) times = map(lambda x:long(x[0]), hands)
handids = map(lambda x:int(x[1]), hands) handids = map(lambda x:int(x[1]), hands)
winnings = map(lambda x:int(x[4]), hands)
print "DEBUG: len(times) %s" %(len(times)) print "DEBUG: len(times) %s" %(len(times))
diffs = diff(times) diffs = diff(times) # This array is the difference in starttime between consecutive hands
print "DEBUG: len(diffs) %s" %(len(diffs)) index = nonzero(diff(times) > THRESHOLD) # This array represents the indexes into 'times' for start/end times of sessions
index = nonzero(diff(times) > THRESHOLD) # ie. times[index[0][0]] is the end of the first session
print "DEBUG: len(index[0]) %s" %(len(index[0])) #print "DEBUG: len(index[0]) %s" %(len(index[0]))
print "DEBUG: index %s" %(index) #print "DEBUG: index %s" %(index)
print "DEBUG: index[0][0] %s" %(index[0][0]) #print "DEBUG: index[0][0] %s" %(index[0][0])
total = 0 total = 0
last_idx = 0 last_idx = 0
lowidx = 0
uppidx = 0
results = []
# Take all results and format them into a list for feeding into gui model.
for i in range(len(index[0])): for i in range(len(index[0])):
print "Hands in session %4s: %4s Start: %s End: %s Total: %s" %(i, index[0][i] - last_idx, strftime("%d/%m/%Y %H:%M", localtime(times[last_idx])), strftime("%d/%m/%Y %H:%M", localtime(times[index[0][i]])), times[index[0][i]] - times[last_idx]) sid = i # Session id
hds = index[0][i] - last_idx # Number of hands in session
stime = strftime("%d/%m/%Y %H:%M", localtime(times[last_idx])) # Formatted start time
etime = strftime("%d/%m/%Y %H:%M", localtime(times[index[0][i]])) # Formatted end time
hph = (times[index[0][i]] - times[last_idx])/60 # Hands per hour
won = sum(winnings[last_idx:index[0][i]])
print "DEBUG: range: %s - %s" %(last_idx, index[0][i])
results.append([sid, hds, stime, etime, hph, won])
print "Hands in session %4s: %4s Start: %s End: %s HPH: %s Profit: %s" %(sid, hds, stime, etime, hph, won)
total = total + (index[0][i] - last_idx) total = total + (index[0][i] - last_idx)
last_idx = index[0][i] + 1 last_idx = index[0][i] + 1
print "Total: ", total for row in results:
# iter = self.liststore.append(row)
# colnames = [desc[0].lower() for desc in self.cursor.description]
#
# # pre-fetch some constant values:
# cols_to_show = [x for x in self.columns if x[colshow]]
# hgametypeid_idx = colnames.index('hgametypeid')
#
# liststore = gtk.ListStore(*([str] * len(cols_to_show)))
# view = gtk.TreeView(model=liststore)
# view.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_BOTH)
# vbox.pack_start(view, expand=False, padding=3)
# textcell = gtk.CellRendererText()
# numcell = gtk.CellRendererText()
# numcell.set_property('xalign', 1.0)
# listcols = []
#
# # 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 col == 1 and holecards:
# listcols[col].pack_start(textcell, expand=True)
# else:
# listcols[col].pack_start(textcell, expand=False)
# listcols[col].add_attribute(textcell, 'text', col)
# else:
# listcols[col].pack_start(numcell, expand=False)
# listcols[col].add_attribute(numcell, 'text', col)
#
# rows = len(result) # +1 for title row
#
# 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])]
# else:
# if column[colalias] == 'game':
# if holecards:
# value = Card.twoStartCardString( result[sqlrow][hgametypeid_idx] )
# else:
# minbb = result[sqlrow][colnames.index('minbigblind')]
# maxbb = result[sqlrow][colnames.index('maxbigblind')]
# value = result[sqlrow][colnames.index('limittype')] + ' ' \
# + result[sqlrow][colnames.index('category')].title() + ' ' \
# + result[sqlrow][colnames.index('name')] + ' $'
# if 100 * int(minbb/100.0) != minbb:
# value += '%.2f' % (minbb/100.0)
# else:
# value += '%.0f' % (minbb/100.0)
# if minbb != maxbb:
# if 100 * int(maxbb/100.0) != maxbb:
# value += ' - $' + '%.2f' % (maxbb/100.0)
# else:
# value += ' - $' + '%.0f' % (maxbb/100.0)
# else:
# continue
# if value and value != -999:
# treerow.append(column[colformat] % value)
# else:
# treerow.append(' ')
# iter = liststore.append(treerow)
# sqlrow += 1
# row += 1
vbox.show_all() vbox.show_all()
def main(argv=None):
config = Configuration.Config()
i = GuiBulkImport(settings, config)
if __name__ == '__main__':
sys.exit(main())

View File

@ -385,6 +385,51 @@ Left-Drag to Move"
<location seat="9" x="70" y="53"> </location> <location seat="9" x="70" y="53"> </location>
</layout> </layout>
</site> </site>
<site enabled="False"
site_name="Betfair"
table_finder="Betfair Poker.exe"
screen_name="YOUR SCREEN NAME HERE"
site_path="C:/Program Files/Betfair/Betfair Poker/"
HH_path="C:/Program Files/Betfair/Betfair Poker/HandHistory/YOUR SCREEN NAME HERE/"
decoder="everleaf_decode_table"
converter="BetfairToFpdb"
supported_games="holdem">
<layout fav_seat="0" height="547" max="8" width="794">
<location seat="1" x="640" y="64"> </location>
<location seat="2" x="650" y="230"> </location>
<location seat="3" x="650" y="385"> </location>
<location seat="4" x="588" y="425"> </location>
<location seat="5" x="92" y="425"> </location>
<location seat="6" x="0" y="373"> </location>
<location seat="7" x="0" y="223"> </location>
<location seat="8" x="25" y="50"> </location>
</layout>
<layout fav_seat="0" height="547" max="6" width="794">
<location seat="1" x="640" y="58"> </location>
<location seat="2" x="654" y="288"> </location>
<location seat="3" x="615" y="424"> </location>
<location seat="4" x="70" y="421"> </location>
<location seat="5" x="0" y="280"> </location>
<location seat="6" x="70" y="58"> </location>
</layout>
<layout fav_seat="0" height="547" max="2" width="794">
<location seat="1" x="651" y="288"> </location>
<location seat="2" x="10" y="288"> </location>
</layout>
<layout fav_seat="0" height="547" max="9" width="794">
<location seat="1" x="634" y="38"> </location>
<location seat="2" x="667" y="184"> </location>
<location seat="3" x="667" y="321"> </location>
<location seat="4" x="667" y="445"> </location>
<location seat="5" x="337" y="459"> </location>
<location seat="6" x="0" y="400"> </location>
<location seat="7" x="0" y="322"> </location>
<location seat="8" x="0" y="181"> </location>
<location seat="9" x="70" y="53"> </location>
</layout>
</site>
</supported_sites> </supported_sites>
<supported_games> <supported_games>
@ -521,6 +566,7 @@ Left-Drag to Move"
<hhc site="Win2day" converter="Win2dayToFpdb"/> <hhc site="Win2day" converter="Win2dayToFpdb"/>
<hhc site="Absolute" converter="AbsoluteToFpdb"/> <hhc site="Absolute" converter="AbsoluteToFpdb"/>
<hhc site="PartyPoker" converter="PartyPokerToFpdb"/> <hhc site="PartyPoker" converter="PartyPokerToFpdb"/>
<hhc site="Betfair" converter="BetfairToFpdb"/>
</hhcs> </hhcs>
<supported_databases> <supported_databases>

View File

@ -59,9 +59,11 @@ import Hud
# HUD params: # HUD params:
# - Set aggregate_ring and/or aggregate_tour to True is you want to include stats from other blind levels in the HUD display # - Set aggregate_ring and/or aggregate_tour to True is you want to include stats from other blind levels in the HUD display
# - If aggregation is used, the value of agg_bb_mult determines how what levels are included, e.g. # - If aggregation is used, the value of agg_bb_mult determines what levels are included. If
# agg_bb_mult is M and current blind level is L, blinds between L/M and L*M are included. e.g.
# if agg_bb_mult is 100, almost all levels are included in all HUD displays # if agg_bb_mult is 100, almost all levels are included in all HUD displays
# if agg_bb_mult is 2.1, levels from half to double the current blind level are included in the HUD # if agg_bb_mult is 2, levels from half to double the current blind level are included in the HUD
# if agg_bb_mult is 1 only the current level is included
# - Set hud_style to A to see stats for all-time # - Set hud_style to A to see stats for all-time
# Set hud_style to S to only see stats for current session (currently this shows stats for the last 24 hours) # Set hud_style to S to only see stats for current session (currently this shows stats for the last 24 hours)
# Set hud_style to T to only see stats for the last N days (uses value in hud_days) # Set hud_style to T to only see stats for the last N days (uses value in hud_days)
@ -71,14 +73,14 @@ def_hud_params = { # Settings for all players apart from program owner ('hero')
, 'aggregate_tour' : True , 'aggregate_tour' : True
, 'hud_style' : 'A' , 'hud_style' : 'A'
, 'hud_days' : 90 , 'hud_days' : 90
, 'agg_bb_mult' : 1 # 1 means no aggregation , 'agg_bb_mult' : 10000 # 1 means no aggregation
# , 'hud_session_gap' : 30 not currently used # , 'hud_session_gap' : 30 not currently used
# Second set of variables for hero - these settings only apply to the program owner # Second set of variables for hero - these settings only apply to the program owner
, 'h_aggregate_ring' : False , 'h_aggregate_ring' : False
, 'h_aggregate_tour' : True , 'h_aggregate_tour' : True
, 'h_hud_style' : 'S' # A(ll) / S(ession) / T(ime in days) , 'h_hud_style' : 'S' # A(ll) / S(ession) / T(ime in days)
, 'h_hud_days' : 30 , 'h_hud_days' : 60
, 'h_agg_bb_mult' : 1 # 1 means no aggregation , 'h_agg_bb_mult' : 10000 # 1 means no aggregation
# , 'h_hud_session_gap' : 30 not currently used # , 'h_hud_session_gap' : 30 not currently used
} }

View File

@ -205,14 +205,14 @@ db: a connected fpdb_db object"""
#Gametypes #Gametypes
gtid = db.getGameTypeId(self.siteId, self.gametype) gtid = db.getGameTypeId(self.siteId, self.gametype)
self.stats.assembleHands(self) self.stats.getStats(self)
##### #####
# End prep functions # End prep functions
##### #####
# HudCache data to come from DerivedStats class
# HandsActions - all actions for all players for all streets - self.actions # HandsActions - all actions for all players for all streets - self.actions
# HudCache data can be generated from HandsActions (HandsPlayers?)
# Hands - Summary information of hand indexed by handId - gameinfo # Hands - Summary information of hand indexed by handId - gameinfo
hh = self.stats.getHands() hh = self.stats.getHands()
@ -223,6 +223,7 @@ db: a connected fpdb_db object"""
#print hh #print hh
handid = db.storeHand(hh) handid = db.storeHand(hh)
# HandsPlayers - ? ... Do we fix winnings? # HandsPlayers - ? ... Do we fix winnings?
db.storeHandsPlayers(handid, sqlids, self.stats.getHandsPlayers())
# Tourneys ? # Tourneys ?
# TourneysPlayers # TourneysPlayers

View File

@ -1727,6 +1727,7 @@ class Sql:
if db_server == 'mysql': if db_server == 'mysql':
self.query['playerDetailedStats'] = """ self.query['playerDetailedStats'] = """
select <hgameTypeId> AS hgametypeid select <hgameTypeId> AS hgametypeid
,<playerName> AS pname
,gt.base ,gt.base
,gt.category ,gt.category
,upper(gt.limitType) AS limittype ,upper(gt.limitType) AS limittype
@ -1777,6 +1778,7 @@ class Sql:
inner join Hands h on (h.id = hp.handId) inner join Hands h on (h.id = hp.handId)
inner join Gametypes gt on (gt.Id = h.gameTypeId) inner join Gametypes gt on (gt.Id = h.gameTypeId)
inner join Sites s on (s.Id = gt.siteId) inner join Sites s on (s.Id = gt.siteId)
inner join Players p on (p.Id = hp.playerId)
where hp.playerId in <player_test> where hp.playerId in <player_test>
/*and hp.tourneysPlayersId IS NULL*/ /*and hp.tourneysPlayersId IS NULL*/
and h.seats <seats_test> and h.seats <seats_test>
@ -1784,14 +1786,15 @@ class Sql:
<gtbigBlind_test> <gtbigBlind_test>
and date_format(h.handStart, '%Y-%m-%d') <datestest> and date_format(h.handStart, '%Y-%m-%d') <datestest>
group by hgameTypeId group by hgameTypeId
,hp.playerId ,pname
,gt.base ,gt.base
,gt.category ,gt.category
<groupbyseats> <groupbyseats>
,plposition ,plposition
,upper(gt.limitType) ,upper(gt.limitType)
,s.name ,s.name
order by hp.playerId having 1 = 1 <havingclause>
order by pname
,gt.base ,gt.base
,gt.category ,gt.category
<orderbyseats> <orderbyseats>
@ -1807,6 +1810,7 @@ class Sql:
elif db_server == 'postgresql': elif db_server == 'postgresql':
self.query['playerDetailedStats'] = """ self.query['playerDetailedStats'] = """
select <hgameTypeId> AS hgametypeid select <hgameTypeId> AS hgametypeid
,<playerName> AS pname
,gt.base ,gt.base
,gt.category ,gt.category
,upper(gt.limitType) AS limittype ,upper(gt.limitType) AS limittype
@ -1857,6 +1861,7 @@ class Sql:
inner join Hands h on (h.id = hp.handId) inner join Hands h on (h.id = hp.handId)
inner join Gametypes gt on (gt.Id = h.gameTypeId) inner join Gametypes gt on (gt.Id = h.gameTypeId)
inner join Sites s on (s.Id = gt.siteId) inner join Sites s on (s.Id = gt.siteId)
inner join Players p on (p.Id = hp.playerId)
where hp.playerId in <player_test> where hp.playerId in <player_test>
/*and hp.tourneysPlayersId IS NULL*/ /*and hp.tourneysPlayersId IS NULL*/
and h.seats <seats_test> and h.seats <seats_test>
@ -1864,14 +1869,15 @@ class Sql:
<gtbigBlind_test> <gtbigBlind_test>
and to_char(h.handStart, 'YYYY-MM-DD') <datestest> and to_char(h.handStart, 'YYYY-MM-DD') <datestest>
group by hgameTypeId group by hgameTypeId
,hp.playerId ,pname
,gt.base ,gt.base
,gt.category ,gt.category
<groupbyseats> <groupbyseats>
,plposition ,plposition
,upper(gt.limitType) ,upper(gt.limitType)
,s.name ,s.name
order by hp.playerId having 1 = 1 <havingclause>
order by pname
,gt.base ,gt.base
,gt.category ,gt.category
<orderbyseats> <orderbyseats>
@ -3110,6 +3116,44 @@ class Sql:
self.query['handsPlayersTTypeId_joiner'] = " OR TourneysPlayersId+0=" self.query['handsPlayersTTypeId_joiner'] = " OR TourneysPlayersId+0="
self.query['handsPlayersTTypeId_joiner_id'] = " OR id=" self.query['handsPlayersTTypeId_joiner_id'] = " OR id="
self.query['store_hand'] = """INSERT INTO Hands (
tablename,
gametypeid,
sitehandno,
handstart,
importtime,
seats,
maxseats,
texture,
playersVpi,
boardcard1,
boardcard2,
boardcard3,
boardcard4,
boardcard5,
playersAtStreet1,
playersAtStreet2,
playersAtStreet3,
playersAtStreet4,
playersAtShowdown,
street0Raises,
street1Raises,
street2Raises,
street3Raises,
street4Raises,
street1Pot,
street2Pot,
street3Pot,
street4Pot,
showdownPot
)
VALUES
(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
%s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
%s, %s, %s, %s, %s, %s, %s, %s, %s)"""
if db_server == 'mysql': if db_server == 'mysql':

View File

@ -155,12 +155,6 @@ class fpdb:
def dia_database_stats(self, widget, data=None): def dia_database_stats(self, widget, data=None):
self.warning_box("Unimplemented: Database Stats") self.warning_box("Unimplemented: Database Stats")
def dia_database_sessions(self, widget, data=None):
new_sessions_thread = GuiSessionViewer.GuiSessionViewer(self.config, self.sql)
self.threads.append(new_sessions_thread)
sessions_tab=new_sessions_thread.get_vbox()
self.add_and_display_tab(sessions_tab, "Sessions")
def dia_delete_db_parts(self, widget, data=None): def dia_delete_db_parts(self, widget, data=None):
self.warning_box("Unimplemented: Delete Database Parts") self.warning_box("Unimplemented: Delete Database Parts")
self.obtain_global_lock() self.obtain_global_lock()
@ -368,6 +362,7 @@ class fpdb:
<menuitem action="playerdetails"/> <menuitem action="playerdetails"/>
<menuitem action="playerstats"/> <menuitem action="playerstats"/>
<menuitem action="posnstats"/> <menuitem action="posnstats"/>
<menuitem action="sessionstats"/>
<menuitem action="sessionreplay"/> <menuitem action="sessionreplay"/>
<menuitem action="tableviewer"/> <menuitem action="tableviewer"/>
</menu> </menu>
@ -377,7 +372,6 @@ class fpdb:
<menuitem action="createtabs"/> <menuitem action="createtabs"/>
<menuitem action="rebuildhudcache"/> <menuitem action="rebuildhudcache"/>
<menuitem action="stats"/> <menuitem action="stats"/>
<menuitem action="sessions"/>
</menu> </menu>
<menu action="help"> <menu action="help">
<menuitem action="Abbrev"/> <menuitem action="Abbrev"/>
@ -409,6 +403,7 @@ class fpdb:
('playerdetails', None, 'Player _Details (todo)', None, 'Player Details (todo)', self.not_implemented), ('playerdetails', None, 'Player _Details (todo)', None, 'Player Details (todo)', self.not_implemented),
('playerstats', None, '_Player Stats (tabulated view)', '<control>P', 'Player Stats (tabulated view)', self.tab_player_stats), ('playerstats', None, '_Player Stats (tabulated view)', '<control>P', 'Player Stats (tabulated view)', self.tab_player_stats),
('posnstats', None, 'P_ositional Stats (tabulated view)', '<control>O', 'Positional Stats (tabulated view)', self.tab_positional_stats), ('posnstats', None, 'P_ositional Stats (tabulated view)', '<control>O', 'Positional Stats (tabulated view)', self.tab_positional_stats),
('sessionstats', None, 'Session Stats', None, 'Session Stats', self.tab_session_stats),
('sessionreplay', None, '_Session Replayer (todo)', None, 'Session Replayer (todo)', self.not_implemented), ('sessionreplay', None, '_Session Replayer (todo)', None, 'Session Replayer (todo)', self.not_implemented),
('tableviewer', None, 'Poker_table Viewer (mostly obselete)', None, 'Poker_table Viewer (mostly obselete)', self.tab_table_viewer), ('tableviewer', None, 'Poker_table Viewer (mostly obselete)', None, 'Poker_table Viewer (mostly obselete)', self.tab_table_viewer),
('database', None, '_Database'), ('database', None, '_Database'),
@ -417,7 +412,6 @@ class fpdb:
('createtabs', None, 'Create or Recreate _Tables', None, 'Create or Recreate Tables ', self.dia_recreate_tables), ('createtabs', None, 'Create or Recreate _Tables', None, 'Create or Recreate Tables ', self.dia_recreate_tables),
('rebuildhudcache', None, 'Rebuild HUD Cache', None, 'Rebuild HUD Cache', self.dia_recreate_hudcache), ('rebuildhudcache', None, 'Rebuild HUD Cache', None, 'Rebuild HUD Cache', self.dia_recreate_hudcache),
('stats', None, '_Statistics (todo)', None, 'View Database Statistics', self.dia_database_stats), ('stats', None, '_Statistics (todo)', None, 'View Database Statistics', self.dia_database_stats),
('sessions', None, 'Sessions', None, 'View Sessions', self.dia_database_sessions),
('help', None, '_Help'), ('help', None, '_Help'),
('Abbrev', None, '_Abbrevations (todo)', None, 'List of Abbrevations', self.tab_abbreviations), ('Abbrev', None, '_Abbrevations (todo)', None, 'List of Abbrevations', self.tab_abbreviations),
('About', None, 'A_bout', None, 'About the program', self.dia_about), ('About', None, 'A_bout', None, 'About the program', self.dia_about),
@ -554,6 +548,12 @@ class fpdb:
ps_tab=new_ps_thread.get_vbox() ps_tab=new_ps_thread.get_vbox()
self.add_and_display_tab(ps_tab, "Positional Stats") self.add_and_display_tab(ps_tab, "Positional Stats")
def tab_session_stats(self, widget, data=None):
new_ps_thread = GuiSessionViewer.GuiSessionViewer(self.config, self.sql, self.window)
self.threads.append(new_ps_thread)
ps_tab=new_ps_thread.get_vbox()
self.add_and_display_tab(ps_tab, "Session Stats")
def tab_main_help(self, widget, data=None): def tab_main_help(self, widget, data=None):
"""Displays a tab with the main fpdb help screen""" """Displays a tab with the main fpdb help screen"""
mh_tab=gtk.Label("""Welcome to Fpdb! mh_tab=gtk.Label("""Welcome to Fpdb!