diff --git a/pyfpdb/BetfairToFpdb.py b/pyfpdb/BetfairToFpdb.py index 59344991..f4d8bf9b 100755 --- a/pyfpdb/BetfairToFpdb.py +++ b/pyfpdb/BetfairToFpdb.py @@ -105,7 +105,7 @@ class Betfair(HandHistoryConverter): logging.debug("HID %s, Table %s" % (m.group('HID'), m.group('TABLE'))) hand.handid = m.group('HID') 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')) def readPlayerStacks(self, hand): @@ -144,6 +144,7 @@ class Betfair(HandHistoryConverter): def readAntes(self, hand): logging.debug("reading antes") + m = self.re_Antes.finditer(hand.handText) for player in m: logging.debug("hand.addAnte(%s,%s)" %(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')) def readHeroCards(self, hand): - m = self.re_HeroCards.search(hand.handText) - if(m == None): - #Not involved in hand - hand.involved = False - else: - hand.hero = m.group('PNAME') - # "2c, qh" -> set(["2c","qc"]) - # Also works with Omaha hands. - cards = m.group('CARDS') - cards = [c.strip() for c in cards.split(',')] - hand.addHoleCards(cards, m.group('PNAME')) + # streets PREFLOP, PREDRAW, and THIRD are special cases beacause + # we need to grab hero's cards + for street in ('PREFLOP', 'DEAL'): + if street in hand.streets.keys(): + m = self.re_HeroCards.finditer(hand.streets[street]) + for found in m: + hand.hero = found.group('PNAME') + newcards = [c.strip() for c in found.group('CARDS').split(',')] + hand.addHoleCards(street, hand.hero, closed=newcards, shown=False, mucked=False, dealt=True) def readStudPlayerCards(self, hand, street): # balh blah blah diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index 87b1cd52..bb9739ec 100755 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -398,7 +398,7 @@ class Database: print "*** Error: " + err[2] + "(" + str(err[1]) + "): " + str(sys.exc_info()[1]) else: if row and row[0]: - self.hand_1_day_ago = row[0] + self.hand_1day_ago = int(row[0]) d = timedelta(days=hud_days) now = datetime.utcnow() - d @@ -440,10 +440,6 @@ class Database: 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) - try: - print "Session: hero_id =", hero_id, "hds =", stat_dict[hero_id]['n'] - except: - pass if hud_style == 'S' and h_hud_style == 'S': 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: 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) - print "agg query subs:", subs + #print "agg query subs:", subs #else: # query = 'get_stats_from_hand' # subs = (hand, stylekey) @@ -488,10 +484,6 @@ class Database: t_dict[name.lower()] = val # print 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 @@ -1392,41 +1384,7 @@ class Database: def storeHand(self, p): #stores into table hands: - q = """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)""" + q = self.sql.query['store_hand'] q = q.replace('%s', self.sql.query['placeholder']) @@ -1461,19 +1419,40 @@ class Database: p['street4Pot'], p['showdownPot'] )) - #return getLastInsertId(backend, conn, cursor) + return self.get_last_insert_id(self.cursor) # 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 ( handId, - playerId + playerId, + startCash, + seatNo, + street0Aggr, + street1Aggr, + street2Aggr, + street3Aggr, + street4Aggr ) VALUES ( - %s, %s + %s, %s, %s, %s, %s, + %s, %s, %s, %s )""" -# startCash, # position, # tourneyTypeId, # card1, @@ -1483,10 +1462,8 @@ class Database: # startCards, # winnings, # rake, -# seatNo, # totalProfit, # street0VPI, -# street0Aggr, # street0_3BChance, # street0_3BDone, # street1Seen, @@ -1494,10 +1471,6 @@ class Database: # street3Seen, # street4Seen, # sawShowdown, -# street1Aggr, -# street2Aggr, -# street3Aggr, -# street4Aggr, # otherRaisedStreet1, # otherRaisedStreet2, # otherRaisedStreet3, @@ -1551,85 +1524,8 @@ class Database: q = q.replace('%s', self.sql.query['placeholder']) - self.cursor.execute(q, ( - hid, - 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 + #print "DEBUG: inserts: %s" %inserts + self.cursor.executemany(q, inserts) def storeHudCacheNew(self, gid, pid, hc): q = """INSERT INTO HudCache ( diff --git a/pyfpdb/DerivedStats.py b/pyfpdb/DerivedStats.py index 315ae707..106bd038 100644 --- a/pyfpdb/DerivedStats.py +++ b/pyfpdb/DerivedStats.py @@ -29,6 +29,8 @@ class DerivedStats(): for player in hand.players: 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.assembleHandsPlayers(self.hand) @@ -39,6 +41,9 @@ class DerivedStats(): def getHands(self): return self.hands + def getHandsPlayers(self): + return self.handsplayers + def assembleHands(self, hand): self.hands['tableName'] = hand.tablename self.hands['siteHandNo'] = hand.handid @@ -77,10 +82,15 @@ class DerivedStats(): # commentTs DATETIME 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:]): self.aggr(self.hand, i) + def assembleHudCache(self, hand): # # def generateHudCacheData(player_ids, base, category, action_types, allIns, actionTypeByNo # # ,winnings, totalWinnings, positions, actionTypes, actionAmounts, antes): @@ -777,20 +787,22 @@ class DerivedStats(): if act[1] in ('calls','bets', 'raises'): 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) + 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): """ 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 # [ (player, action, ....), (player2, action, ...) ] # 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['playersAtStreet2'] = 0 self.hands['playersAtStreet3'] = 0 diff --git a/pyfpdb/Filters.py b/pyfpdb/Filters.py index 6ad242bb..08581c83 100644 --- a/pyfpdb/Filters.py +++ b/pyfpdb/Filters.py @@ -55,6 +55,7 @@ class Filters(threading.Thread): ,'seatsbetween':'Between:', 'seatsand':'And:', 'seatsshow':'Show Number of _Players' ,'limitstitle':'Limits:', 'seatstitle':'Number of Players:' ,'groupstitle':'Grouping:', 'posnshow':'Show Position Stats:' + ,'groupsall':'All Players' ,'limitsFL':'FL', 'limitsNL':'NL', 'ring':'Ring', 'tour':'Tourney' } @@ -64,6 +65,10 @@ class Filters(threading.Thread): self.start_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 self.mainVBox = gtk.VBox(False, 0) @@ -71,7 +76,7 @@ class Filters(threading.Thread): playerFrame.set_label_align(0.0, 0.0) vbox = gtk.VBox(False, 0) - self.fillPlayerFrame(vbox) + self.fillPlayerFrame(vbox, self.display) playerFrame.add(vbox) self.boxes['player'] = vbox @@ -122,7 +127,6 @@ class Filters(threading.Thread): groupsFrame = gtk.Frame() groupsFrame.show() vbox = gtk.VBox(False, 0) - self.sbGroups = {} self.fillGroupsFrame(vbox, self.display) groupsFrame.add(vbox) @@ -181,6 +185,9 @@ class Filters(threading.Thread): return self.mainVBox #end def get_vbox + def getNumHands(self): + return self.numHands + def getSites(self): return self.sites @@ -256,6 +263,13 @@ class Filters(threading.Thread): self.heroes[site] = w.get_text() # 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): cb = gtk.CheckButton(site) cb.connect('clicked', self.__set_site_select, site) @@ -393,13 +407,32 @@ class Filters(threading.Thread): self.groups[group] = w.get_active() 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(): - pathHBox = gtk.HBox(False, 0) - vbox.pack_start(pathHBox, False, True, 0) + hBox = gtk.HBox(False, 0) + vbox.pack_start(hBox, False, True, 0) 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): for site in self.conf.get_supported_sites(): diff --git a/pyfpdb/GuiBulkImport.py b/pyfpdb/GuiBulkImport.py index 21f9e050..9cb54d50 100755 --- a/pyfpdb/GuiBulkImport.py +++ b/pyfpdb/GuiBulkImport.py @@ -40,21 +40,6 @@ class GuiBulkImport(): # CONFIGURATION - update these as preferred: 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): self.progressbar.pulse() return True @@ -77,7 +62,7 @@ class GuiBulkImport(): self.timer = gobject.timeout_add(100, self.dopulse) # 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 self.importer.setHandCount(int(self.spin_hands.get_text())) @@ -103,7 +88,8 @@ class GuiBulkImport(): self.importer.setDropHudCache("auto") 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) starttime = time() # try: @@ -151,6 +137,7 @@ class GuiBulkImport(): self.chooser = gtk.FileChooserWidget() self.chooser.set_filename(self.settings['bulkImport-defaultPath']) + self.chooser.set_select_multiple(True) self.vbox.add(self.chooser) self.chooser.show() @@ -317,8 +304,20 @@ def main(argv=None): 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", 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) + 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() settings = {} @@ -350,8 +349,10 @@ def main(argv=None): importer.setThreads(-1) importer.addBulkImportImportFileOrDir(os.path.expanduser(options.filename), site=options.filtername) importer.setCallHud(False) - importer.runImport() + (stored, dups, partial, errs, ttime) = importer.runImport() 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__': diff --git a/pyfpdb/GuiPlayerStats.py b/pyfpdb/GuiPlayerStats.py index 4b82c861..dd3db0be 100644 --- a/pyfpdb/GuiPlayerStats.py +++ b/pyfpdb/GuiPlayerStats.py @@ -36,6 +36,8 @@ class GuiPlayerStats (threading.Thread): self.main_window = mainwin self.sql = querylist + self.liststore = None + self.MYSQL_INNODB = 2 self.PGSQL = 3 self.SQLITE = 4 @@ -65,6 +67,7 @@ class GuiPlayerStats (threading.Thread): "SeatSep" : True, "Dates" : True, "Groups" : True, + "GroupsAll" : True, "Button1" : True, "Button2" : True } @@ -78,29 +81,30 @@ class GuiPlayerStats (threading.Thread): # 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", False, "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"] + # is column displayed, column heading, xalignment, formatting, celltype + self.columns = [ ["game", True, "Game", 0.0, "%s", "str"] + , ["hand", False, "Hand", 0.0, "%s", "str"] # true not allowed for this line + , ["plposition", False, "Posn", 1.0, "%s", "str"] # true not allowed for this line (set in code) + , ["pname", False, "Name", 0.0, "%s", "str"] # true not allowed for this line (set in code) + , ["n", True, "Hds", 1.0, "%d", "str"] + , ["avgseats", False, "Seats", 1.0, "%3.1f", "str"] + , ["vpip", True, "VPIP", 1.0, "%3.1f", "str"] + , ["pfr", True, "PFR", 1.0, "%3.1f", "str"] + , ["pf3", True, "PF3", 1.0, "%3.1f", "str"] + , ["steals", True, "Steals", 1.0, "%3.1f", "str"] + , ["saw_f", True, "Saw_F", 1.0, "%3.1f", "str"] + , ["sawsd", True, "SawSD", 1.0, "%3.1f", "str"] + , ["wtsdwsf", True, "WtSDwsF", 1.0, "%3.1f", "str"] + , ["wmsd", True, "W$SD", 1.0, "%3.1f", "str"] + , ["flafq", True, "FlAFq", 1.0, "%3.1f", "str"] + , ["tuafq", True, "TuAFq", 1.0, "%3.1f", "str"] + , ["rvafq", True, "RvAFq", 1.0, "%3.1f", "str"] + , ["pofafq", False, "PoFAFq", 1.0, "%3.1f", "str"] + , ["net", True, "Net($)", 1.0, "%6.2f", "cash"] + , ["bbper100", True, "bb/100", 1.0, "%4.2f", "str"] + , ["rake", True, "Rake($)", 1.0, "%6.2f", "cash"] + , ["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 @@ -125,8 +129,9 @@ class GuiPlayerStats (threading.Thread): self.stats_vbox = None self.detailFilters = [] # the data used to enhance the sql select - self.main_hbox = gtk.HBox(False, 0) - self.main_hbox.show() + #self.main_hbox = gtk.HBox(False, 0) + #self.main_hbox.show() + self.main_hbox = gtk.HPaned() self.stats_frame = gtk.Frame() self.stats_frame.show() @@ -136,8 +141,11 @@ class GuiPlayerStats (threading.Thread): self.stats_frame.add(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 [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): try: self.stats_vbox.destroy() 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_frame.add(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): 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 # 3rd parameter passes extra flags, currently includes: # holecards - whether to display card breakdown (True/False) - flags = [False] - self.addTable(vbox, 'playerDetailedStats', flags, playerids, sitenos, limits, type, seats, groups, dates) + # numhands - min number hands required when displaying all players + flags = [False, self.filters.getNumHands()] + self.addTable(swin, 'playerDetailedStats', flags, playerids, sitenos, limits, type, seats, groups, dates) # Separator - sep = gtk.HSeparator() - vbox.pack_start(sep, expand=False, padding=3) - sep.show_now() - vbox.show_now() + vbox2 = gtk.VBox(False, 0) heading = gtk.Label(self.filterText['handhead']) 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) swin = gtk.ScrolledWindow(hadjustment=None, vadjustment=None) swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) swin.show() - vbox.pack_start(swin, expand=True, padding=3) - - vbox1 = gtk.VBox(False, 0) - vbox1.show() - swin.add_with_viewport(vbox1) + vbox2.pack_start(swin, expand=True, padding=3) + vbox.pack2(vbox2) + vbox2.show() # Detailed table - flags = [True] - self.addTable(vbox1, 'playerDetailedStats', flags, playerids, sitenos, limits, type, seats, groups, dates) + flags[0] = True + self.addTable(swin, 'playerDetailedStats', flags, playerids, sitenos, limits, type, seats, groups, dates) self.db.rollback() print "Stats page displayed in %4.2f seconds" % (time() - starttime) #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): + counter = 0 row = 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 else: holecards = flags[0] @@ -243,16 +281,18 @@ class GuiPlayerStats (threading.Thread): 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) + self.liststore = gtk.ListStore(*([str] * len(cols_to_show))) + view = gtk.TreeView(model=self.liststore) 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() textcell50 = gtk.CellRendererText() textcell50.set_property('xalign', 0.5) numcell = gtk.CellRendererText() numcell.set_property('xalign', 1.0) listcols = [] + idx = 0 # Create header row eg column: ("game", True, "Game", 0.0, "%s") for col, column in enumerate(cols_to_show): @@ -262,6 +302,9 @@ class GuiPlayerStats (threading.Thread): s = column[colheading] listcols.append(gtk.TreeViewColumn(s)) 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[colxalign] == 0.0: listcols[col].pack_start(textcell, expand=True) @@ -275,6 +318,11 @@ class GuiPlayerStats (threading.Thread): listcols[col].add_attribute(numcell, 'text', col) listcols[col].set_expand(True) #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 @@ -315,7 +363,7 @@ class GuiPlayerStats (threading.Thread): treerow.append(column[colformat] % value) else: treerow.append(' ') - iter = liststore.append(treerow) + iter = self.liststore.append(treerow) sqlrow += 1 row += 1 vbox.show_all() @@ -323,16 +371,42 @@ class GuiPlayerStats (threading.Thread): #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): - if not flags: holecards = False - else: holecards = flags[0] - - if playerids: - nametest = str(tuple(playerids)) - nametest = nametest.replace("L", "") - nametest = nametest.replace(",)",")") - query = query.replace("", nametest) + having = '' + if not flags: + holecards = False + numhands = 0 else: - query = query.replace("", "1 = 2") + 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: + nametest = str(tuple(playerids)) + nametest = nametest.replace("L", "") + nametest = nametest.replace(",)",")") + else: + 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("", nametest) + query = query.replace("", pname) + query = query.replace("", having) if seats: query = query.replace('', 'between ' + str(seats['from']) + ' and ' + str(seats['to'])) @@ -372,9 +446,12 @@ class GuiPlayerStats (threading.Thread): bbtest = bbtest + " and gt.type = 'tour' " query = query.replace("", bbtest) - if holecards: # pinch level variables for hole card query + if holecards: # re-use level variables for hole card query query = query.replace("", "hp.startcards") - query = query.replace("", ",hgameTypeId desc") + query = query.replace("" + , ",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: query = query.replace("", "") groupLevels = "show" not in str(limits) diff --git a/pyfpdb/GuiSessionViewer.py b/pyfpdb/GuiSessionViewer.py old mode 100644 new mode 100755 index 0587e5e0..96586263 --- a/pyfpdb/GuiSessionViewer.py +++ b/pyfpdb/GuiSessionViewer.py @@ -15,6 +15,7 @@ #In the "official" distribution you can find the license in #agpl-3.0.txt in the docs folder of the package. +import sys import threading import pygtk pygtk.require('2.0') @@ -22,7 +23,10 @@ import gtk import os from time import time, strftime, localtime try: - from numpy import diff, nonzero + from numpy import diff, nonzero, sum +# from matplotlib.dates import DateFormatter, WeekdayLocator, HourLocator, \ +# DayLocator, MONDAY, timezone + except: print """Failed to load numpy in Session Viewer""" print """This is of no consequence as the module currently doesn't do anything.""" @@ -34,10 +38,13 @@ import Filters import FpdbSQLQueries class GuiSessionViewer (threading.Thread): - def __init__(self, config, querylist, debug=True): + def __init__(self, config, querylist, mainwin, debug=True): self.debug = debug self.conf = config self.sql = querylist + + self.liststore = None + self.MYSQL_INNODB = 2 self.PGSQL = 3 self.SQLITE = 4 @@ -56,55 +63,63 @@ class GuiSessionViewer (threading.Thread): self.filterText = {'handhead':'Hand Breakdown for all levels listed above' } - filters_display = { "Heroes" : True, - "Sites" : True, - "Games" : False, - "Limits" : True, - "LimitSep" : True, - "Seats" : True, - "SeatSep" : True, - "Dates" : False, - "Groups" : True, - "Button1" : True, - "Button2" : True + filters_display = { "Heroes" : True, + "Sites" : True, + "Games" : False, + "Limits" : True, + "LimitSep" : True, + "LimitType" : True, + "Type" : True, + "Seats" : True, + "SeatSep" : True, + "Dates" : True, + "Groups" : True, + "GroupsAll" : True, + "Button1" : True, + "Button2" : True } self.filters = Filters.Filters(self.db, self.conf, self.sql, display = filters_display) - self.filters.registerButton2Name("_Refresh") - self.filters.registerButton2Callback(self.refreshStats) + 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") + self.columns = [ ("sid", True, "SID", 0.0, "%s") , ("hand", False, "Hand", 0.0, "%s") # true not allowed for this line , ("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") - , ("variance", True, "Variance", 1.0, "%5.2f") + , ("start", True, "Start", 1.0, "%d") + , ("end", True, "End", 1.0, "%d") + , ("hph", True, "Hands/h", 1.0, "%d") + , ("profit", True, "Profit", 1.0, "%s") + #, ("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") + #, ("variance", True, "Variance", 1.0, "%5.2f") ] self.stats_frame = None self.stats_vbox = None self.detailFilters = [] # the data used to enhance the sql select - - self.main_hbox = gtk.HBox(False, 0) - self.main_hbox.show() + + #self.main_hbox = gtk.HBox(False, 0) + #self.main_hbox.show() + self.main_hbox = gtk.HPaned() self.stats_frame = gtk.Frame() self.stats_frame.show() @@ -112,21 +127,46 @@ class GuiSessionViewer (threading.Thread): self.stats_vbox = gtk.VBox(False, 0) self.stats_vbox.show() self.stats_frame.add(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.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.pack1(self.filters.get_vbox()) + self.main_hbox.pack2(self.stats_frame) + self.main_hbox.show() # 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): """returns the vbox of this thread""" 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): try: self.stats_vbox.destroy() except AttributeError: pass @@ -209,105 +249,97 @@ class GuiSessionViewer (threading.Thread): if not flags: holecards = False 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.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""") + self.liststore = gtk.ListStore(*([str] * len(cols_to_show))) + + 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 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) handids = map(lambda x:int(x[1]), hands) + winnings = map(lambda x:int(x[4]), hands) print "DEBUG: len(times) %s" %(len(times)) - diffs = diff(times) - print "DEBUG: len(diffs) %s" %(len(diffs)) - index = nonzero(diff(times) > THRESHOLD) - print "DEBUG: len(index[0]) %s" %(len(index[0])) - print "DEBUG: index %s" %(index) - print "DEBUG: index[0][0] %s" %(index[0][0]) + diffs = diff(times) # This array is the difference in starttime between consecutive hands + index = nonzero(diff(times) > THRESHOLD) # This array represents the indexes into 'times' for start/end times of sessions + # ie. times[index[0][0]] is the end of the first session + #print "DEBUG: len(index[0]) %s" %(len(index[0])) + #print "DEBUG: index %s" %(index) + #print "DEBUG: index[0][0] %s" %(index[0][0]) total = 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])): - 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) last_idx = index[0][i] + 1 - print "Total: ", total -# -# 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 + for row in results: + iter = self.liststore.append(row) + vbox.show_all() + +def main(argv=None): + config = Configuration.Config() + i = GuiBulkImport(settings, config) + +if __name__ == '__main__': + sys.exit(main()) + diff --git a/pyfpdb/HUD_config.xml.example b/pyfpdb/HUD_config.xml.example index bfeafd8d..34a8f16d 100644 --- a/pyfpdb/HUD_config.xml.example +++ b/pyfpdb/HUD_config.xml.example @@ -96,359 +96,404 @@ Left-Drag to Move" /> - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + bgcolor="#000000" + fgcolor="#FFFFFF" + hudopacity="1.0" + font="Sans" + font_size="8" + supported_games="holdem,razz,omahahi,omahahilo,studhi,studhilo"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + bgcolor="#000000" + fgcolor="#FFFFFF" + hudopacity="1.0" + font="Sans" + font_size="8" + supported_games="holdem,razz,omahahi,omahahilo,studhi,studhilo"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + supported_games="holdem"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + supported_games="holdem"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + supported_games="holdem"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + supported_games="holdem"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - + - + @@ -468,50 +513,50 @@ Left-Drag to Move" - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -521,6 +566,7 @@ Left-Drag to Move" + diff --git a/pyfpdb/HUD_main.py b/pyfpdb/HUD_main.py index 95a25a29..77d8312d 100755 --- a/pyfpdb/HUD_main.py +++ b/pyfpdb/HUD_main.py @@ -59,9 +59,11 @@ import Hud # 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 -# - 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 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 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) @@ -71,14 +73,14 @@ def_hud_params = { # Settings for all players apart from program owner ('hero') , 'aggregate_tour' : True , 'hud_style' : 'A' , '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 # Second set of variables for hero - these settings only apply to the program owner , 'h_aggregate_ring' : False , 'h_aggregate_tour' : True , 'h_hud_style' : 'S' # A(ll) / S(ession) / T(ime in days) - , 'h_hud_days' : 30 - , 'h_agg_bb_mult' : 1 # 1 means no aggregation + , 'h_hud_days' : 60 + , 'h_agg_bb_mult' : 10000 # 1 means no aggregation # , 'h_hud_session_gap' : 30 not currently used } diff --git a/pyfpdb/Hand.py b/pyfpdb/Hand.py index 2527f636..602a6a1a 100644 --- a/pyfpdb/Hand.py +++ b/pyfpdb/Hand.py @@ -205,14 +205,14 @@ db: a connected fpdb_db object""" #Gametypes gtid = db.getGameTypeId(self.siteId, self.gametype) - self.stats.assembleHands(self) + self.stats.getStats(self) ##### # End prep functions ##### - # HudCache data to come from DerivedStats class # 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 hh = self.stats.getHands() @@ -223,6 +223,7 @@ db: a connected fpdb_db object""" #print hh handid = db.storeHand(hh) # HandsPlayers - ? ... Do we fix winnings? + db.storeHandsPlayers(handid, sqlids, self.stats.getHandsPlayers()) # Tourneys ? # TourneysPlayers diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index bb918d97..251a56fa 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -1727,6 +1727,7 @@ class Sql: if db_server == 'mysql': self.query['playerDetailedStats'] = """ select AS hgametypeid + , AS pname ,gt.base ,gt.category ,upper(gt.limitType) AS limittype @@ -1777,6 +1778,7 @@ class Sql: 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 /*and hp.tourneysPlayersId IS NULL*/ and h.seats @@ -1784,14 +1786,15 @@ class Sql: and date_format(h.handStart, '%Y-%m-%d') group by hgameTypeId - ,hp.playerId + ,pname ,gt.base ,gt.category ,plposition ,upper(gt.limitType) ,s.name - order by hp.playerId + having 1 = 1 + order by pname ,gt.base ,gt.category @@ -1807,6 +1810,7 @@ class Sql: elif db_server == 'postgresql': self.query['playerDetailedStats'] = """ select AS hgametypeid + , AS pname ,gt.base ,gt.category ,upper(gt.limitType) AS limittype @@ -1857,6 +1861,7 @@ class Sql: 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 /*and hp.tourneysPlayersId IS NULL*/ and h.seats @@ -1864,14 +1869,15 @@ class Sql: and to_char(h.handStart, 'YYYY-MM-DD') group by hgameTypeId - ,hp.playerId + ,pname ,gt.base ,gt.category ,plposition ,upper(gt.limitType) ,s.name - order by hp.playerId + having 1 = 1 + order by pname ,gt.base ,gt.category @@ -3110,6 +3116,44 @@ class Sql: self.query['handsPlayersTTypeId_joiner'] = " OR TourneysPlayersId+0=" 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': diff --git a/pyfpdb/fpdb.py b/pyfpdb/fpdb.py index 18bc65d9..4f3210f4 100755 --- a/pyfpdb/fpdb.py +++ b/pyfpdb/fpdb.py @@ -155,12 +155,6 @@ class fpdb: def dia_database_stats(self, widget, data=None): 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): self.warning_box("Unimplemented: Delete Database Parts") self.obtain_global_lock() @@ -368,6 +362,7 @@ class fpdb: + @@ -377,7 +372,6 @@ class fpdb: - @@ -409,6 +403,7 @@ class fpdb: ('playerdetails', None, 'Player _Details (todo)', None, 'Player Details (todo)', self.not_implemented), ('playerstats', None, '_Player Stats (tabulated view)', 'P', 'Player Stats (tabulated view)', self.tab_player_stats), ('posnstats', None, 'P_ositional Stats (tabulated view)', '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), ('tableviewer', None, 'Poker_table Viewer (mostly obselete)', None, 'Poker_table Viewer (mostly obselete)', self.tab_table_viewer), ('database', None, '_Database'), @@ -417,7 +412,6 @@ class fpdb: ('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), ('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'), ('Abbrev', None, '_Abbrevations (todo)', None, 'List of Abbrevations', self.tab_abbreviations), ('About', None, 'A_bout', None, 'About the program', self.dia_about), @@ -554,6 +548,12 @@ class fpdb: ps_tab=new_ps_thread.get_vbox() 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): """Displays a tab with the main fpdb help screen""" mh_tab=gtk.Label("""Welcome to Fpdb!