diff --git a/pyfpdb/Configuration.py b/pyfpdb/Configuration.py index d40f37f9..83648577 100755 --- a/pyfpdb/Configuration.py +++ b/pyfpdb/Configuration.py @@ -672,38 +672,50 @@ class Config: except: hui['label'] = default_text + try: hui['hud_style'] = self.ui.hud_style + except: hui['hud_style'] = 'A' # default is show stats for All-time, also S(session) and T(ime) + + try: hui['hud_days'] = int(self.ui.hud_days) + except: hui['hud_days'] = 90 + try: hui['aggregate_ring'] = self.ui.aggregate_ring except: hui['aggregate_ring'] = False try: hui['aggregate_tour'] = self.ui.aggregate_tour except: hui['aggregate_tour'] = True - try: hui['hud_style'] = self.ui.hud_style - except: hui['hud_style'] = 'A' - - try: hui['hud_days'] = int(self.ui.hud_days) - except: hui['hud_days'] = 90 - try: hui['agg_bb_mult'] = self.ui.agg_bb_mult except: hui['agg_bb_mult'] = 1 + try: hui['seats_style'] = self.ui.seats_style + except: hui['seats_style'] = 'C' # A / C / E, use A(ll) / C(ustom) / E(xact) seat numbers + + try: hui['seats_cust_nums'] = self.ui.seats_cust_nums + except: hui['seats_cust_nums'] = ['n/a', 'n/a', (2,2), (3,4), (3,5), (4,6), (5,7), (6,8), (7,9), (8,10), (8,10)] + # Hero specific - try: hui['h_aggregate_ring'] = self.ui.h_aggregate_ring - except: hui['h_aggregate_ring'] = False - - try: hui['h_aggregate_tour'] = self.ui.h_aggregate_tour - except: hui['h_aggregate_tour'] = True - try: hui['h_hud_style'] = self.ui.h_hud_style except: hui['h_hud_style'] = 'S' try: hui['h_hud_days'] = int(self.ui.h_hud_days) except: hui['h_hud_days'] = 30 + try: hui['h_aggregate_ring'] = self.ui.h_aggregate_ring + except: hui['h_aggregate_ring'] = False + + try: hui['h_aggregate_tour'] = self.ui.h_aggregate_tour + except: hui['h_aggregate_tour'] = True + try: hui['h_agg_bb_mult'] = self.ui.h_agg_bb_mult except: hui['h_agg_bb_mult'] = 1 + try: hui['h_seats_style'] = self.ui.h_seats_style + except: hui['h_seats_style'] = 'E' # A / C / E, use A(ll) / C(ustom) / E(xact) seat numbers + + try: hui['h_seats_cust_nums'] = self.ui.h_seats_cust_nums + except: hui['h_seats_cust_nums'] = ['n/a', 'n/a', (2,2), (3,4), (3,5), (4,6), (5,7), (6,8), (7,9), (8,10), (8,10)] + return hui @@ -948,8 +960,14 @@ if __name__== "__main__": for game in c.get_supported_games(): print c.get_game_parameters(game) + for hud_param, value in c.get_hud_ui_parameters().iteritems(): + print "hud param %s = %s" % (hud_param, value) + print "start up path = ", c.execution_path("") - from xml.dom.ext import PrettyPrint - for site_node in c.doc.getElementsByTagName("site"): - PrettyPrint(site_node, stream=sys.stdout, encoding="utf-8") + try: + from xml.dom.ext import PrettyPrint + for site_node in c.doc.getElementsByTagName("site"): + PrettyPrint(site_node, stream=sys.stdout, encoding="utf-8") + except: + print "xml.dom.ext needs PyXML to be installed!" diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index 42cd986c..775e1475 100755 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -433,17 +433,50 @@ class Database: def get_stats_from_hand( self, hand, type # type is "ring" or "tour" , hud_params = {'hud_style':'A', 'agg_bb_mult':1000 - ,'h_hud_style':'S', 'h_agg_bb_mult':1000} + ,'seats_style':'A', 'seats_cust_nums':['n/a', 'n/a', (2,2), (3,4), (3,5), (4,6), (5,7), (6,8), (7,9), (8,10), (8,10)] + ,'h_hud_style':'S', 'h_agg_bb_mult':1000 + ,'h_seats_style':'A', 'h_seats_cust_nums':['n/a', 'n/a', (2,2), (3,4), (3,5), (4,6), (5,7), (6,8), (7,9), (8,10), (8,10)] + } , hero_id = -1 + , num_seats = 6 ): hud_style = hud_params['hud_style'] agg_bb_mult = hud_params['agg_bb_mult'] + seats_style = hud_params['seats_style'] + seats_cust_nums = hud_params['seats_cust_nums'] h_hud_style = hud_params['h_hud_style'] h_agg_bb_mult = hud_params['h_agg_bb_mult'] + h_seats_style = hud_params['h_seats_style'] + h_seats_cust_nums = hud_params['h_seats_cust_nums'] + stat_dict = {} + if seats_style == 'A': + seats_min, seats_max = 0, 10 + elif seats_style == 'C': + seats_min, seats_max = seats_cust_nums[num_seats][0], seats_cust_nums[num_seats][1] + elif seats_style == 'E': + seats_min, seats_max = num_seats, num_seats + else: + seats_min, seats_max = 0, 10 + print "bad seats_style value:", seats_style + + if h_seats_style == 'A': + h_seats_min, h_seats_max = 0, 10 + elif h_seats_style == 'C': + h_seats_min, h_seats_max = h_seats_cust_nums[num_seats][0], h_seats_cust_nums[num_seats][1] + elif h_seats_style == 'E': + h_seats_min, h_seats_max = num_seats, num_seats + else: + h_seats_min, h_seats_max = 0, 10 + print "bad h_seats_style value:", h_seats_style + print "opp seats style", seats_style, "hero seats style", h_seats_style + print "opp seats:", seats_min, seats_max, " hero seats:", h_seats_min, h_seats_max + 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, seats_min, seats_max + ,h_hud_style, h_seats_min, h_seats_max) if hud_style == 'S' and h_hud_style == 'S': return stat_dict @@ -475,7 +508,9 @@ class Database: # h_stylekey = date_nhands_ago needs array by player here ... 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, seats_min, seats_max # hero params + ,hero_id, h_stylekey, h_agg_bb_mult, h_agg_bb_mult, h_seats_min, h_seats_max) # villain params #print "get stats: hud style =", hud_style, "query =", query, "subs =", subs c = self.connection.cursor() @@ -495,12 +530,15 @@ class Database: return stat_dict # uses query on handsplayers instead of hudcache to get stats on just this session - def get_stats_from_hand_session(self, hand, stat_dict, hero_id, hud_style, h_hud_style): + def get_stats_from_hand_session(self, hand, stat_dict, hero_id + ,hud_style, seats_min, seats_max + ,h_hud_style, h_seats_min, h_seats_max): """Get stats for just this session (currently defined as any play in the last 24 hours - to be improved at some point ...) h_hud_style and hud_style params indicate whether to get stats for hero and/or others - only fetch heros stats if h_hud_style == 'S', and only fetch others stats if hud_style == 'S' + seats_min/max params give seats limits, only include stats if between these values """ query = self.sql.query['get_stats_from_hand_session'] @@ -509,7 +547,8 @@ class Database: else: query = query.replace("", '') - subs = (self.hand_1day_ago, hand) + subs = (self.hand_1day_ago, hand, hero_id, seats_min, seats_max + , hero_id, h_seats_min, h_seats_max) c = self.get_cursor() # now get the stats @@ -524,6 +563,7 @@ class Database: # Loop through stats adding them to appropriate stat_dict: while row: playerid = row[0] + seats = row[1] if (playerid == hero_id and h_hud_style == 'S') or (playerid != hero_id and hud_style == 'S'): for name, val in zip(colnames, row): if not playerid in stat_dict: @@ -535,7 +575,7 @@ class Database: stat_dict[playerid][name.lower()] += val n += 1 if n >= 10000: break # todo: don't think this is needed so set nice and high - # for now - comment out or remove? + # prevents infinite loop so leave for now - comment out or remove? row = c.fetchone() else: log.error("ERROR: query %s result does not have player_id as first column" % (query,)) @@ -2746,7 +2786,9 @@ if __name__=="__main__": # db_connection = Database(c, 'PTrackSv2', 'razz') # mysql razz # db_connection = Database(c, 'ptracks', 'razz') # postgres print "database connection object = ", db_connection.connection - db_connection.recreate_tables() + # db_connection.recreate_tables() + db_connection.dropAllIndexes() + db_connection.createAllIndexes() h = db_connection.get_last_hand() print "last hand = ", h diff --git a/pyfpdb/Exceptions.py b/pyfpdb/Exceptions.py index 87015e3e..5b4f4391 100644 --- a/pyfpdb/Exceptions.py +++ b/pyfpdb/Exceptions.py @@ -27,5 +27,12 @@ class FpdbMySQLAccessDenied(FpdbDatabaseError): def __str__(self): return repr(self.value +" " + self.errmsg) +class FpdbMySQLNoDatabase(FpdbDatabaseError): + def __init__(self, value='', errmsg=''): + self.value = value + self.errmsg = errmsg + def __str__(self): + return repr(self.value +" " + self.errmsg) + class DuplicateError(FpdbError): pass diff --git a/pyfpdb/HUD_main.py b/pyfpdb/HUD_main.py index 22184b42..7870573c 100755 --- a/pyfpdb/HUD_main.py +++ b/pyfpdb/HUD_main.py @@ -200,7 +200,7 @@ class HUD_main(object): # get basic info about the new hand from the db # if there is a db error, complain, skip hand, and proceed try: - (table_name, max, poker_game, type, site_id, site_name, tour_number, tab_number) = \ + (table_name, max, poker_game, type, site_id, site_name, num_seats, tour_number, tab_number) = \ self.db_connection.get_table_info(new_hand_id) except Exception, err: # TODO: we need to make this a much less generic Exception lulz print "db error: skipping %s" % new_hand_id @@ -217,7 +217,8 @@ class HUD_main(object): # get stats using hud's specific params and get cards self.db_connection.init_hud_stat_vars( self.hud_dict[temp_key].hud_params['hud_days'] , self.hud_dict[temp_key].hud_params['h_hud_days']) - stat_dict = self.db_connection.get_stats_from_hand(new_hand_id, type, self.hud_dict[temp_key].hud_params, self.hero_ids[site_id]) + stat_dict = self.db_connection.get_stats_from_hand(new_hand_id, type, self.hud_dict[temp_key].hud_params + ,self.hero_ids[site_id], num_seats) try: self.hud_dict[temp_key].stat_dict = stat_dict except KeyError: # HUD instance has been killed off, key is stale @@ -238,7 +239,8 @@ class HUD_main(object): else: # get stats using default params--also get cards self.db_connection.init_hud_stat_vars( self.hud_params['hud_days'], self.hud_params['h_hud_days'] ) - stat_dict = self.db_connection.get_stats_from_hand(new_hand_id, type, self.hud_params, self.hero_ids[site_id]) + stat_dict = self.db_connection.get_stats_from_hand(new_hand_id, type, self.hud_params + ,self.hero_ids[site_id], num_seats) cards = self.db_connection.get_cards(new_hand_id) comm_cards = self.db_connection.get_common_cards(new_hand_id) if comm_cards != {}: # stud! diff --git a/pyfpdb/Hand.py b/pyfpdb/Hand.py index f6fa478e..6242d866 100644 --- a/pyfpdb/Hand.py +++ b/pyfpdb/Hand.py @@ -510,7 +510,7 @@ Map the tuple self.gametype onto the pokerstars string describing it def printHand(self): self.writeHand(sys.stdout) - def actionString(self, act): + def actionString(self, act, street=None): if act[1] == 'folds': return ("%s: folds " %(act[0])) elif act[1] == 'checks': @@ -535,7 +535,7 @@ Map the tuple self.gametype onto the pokerstars string describing it elif act[1] == 'bringin': return ("%s: brings in for %s%s%s" %(act[0], self.sym, act[2], ' and is all-in' if act[3] else '')) elif act[1] == 'discards': - return ("%s: discards %s %s%s" %(act[0], act[2], 'card' if act[2] == 1 else 'cards' , " [" + " ".join(self.discards[act[0]]['DRAWONE']) + "]" if self.hero == act[0] else '')) + return ("%s: discards %s %s%s" %(act[0], act[2], 'card' if act[2] == 1 else 'cards' , " [" + " ".join(self.discards[street][act[0]]) + "]" if self.hero == act[0] else '')) elif act[1] == 'stands pat': return ("%s: stands pat" %(act[0])) @@ -872,7 +872,7 @@ class DrawHand(Hand): self.streetList = ['BLINDSANTES', 'DEAL', 'DRAWONE', 'DRAWTWO', 'DRAWTHREE'] self.allStreets = ['BLINDSANTES', 'DEAL', 'DRAWONE', 'DRAWTWO', 'DRAWTHREE'] self.holeStreets = ['DEAL', 'DRAWONE', 'DRAWTWO', 'DRAWTHREE'] - self.actionStreets = ['PREDEAL', 'DEAL', 'DRAWONE', 'DRAWTWO', 'DRAWTHREE'] + self.actionStreets = ['BLINDSANTES', 'DEAL', 'DRAWONE', 'DRAWTWO', 'DRAWTHREE'] self.communityStreets = [] Hand.__init__(self, sitename, gametype, handText) self.sb = gametype['sb'] @@ -953,6 +953,13 @@ class DrawHand(Hand): act = (player, 'discards', num) self.actions[street].append(act) + def holecardsAsSet(self, street, player): + """Return holdcards: (oc, nc) as set()""" + (nc,oc) = self.holecards[street][player] + nc = set(nc) + oc = set(oc) + return (nc, oc) + def getStreetTotals(self): # street1Pot INT, /* pot size at flop/street4 */ # street2Pot INT, /* pot size at turn/street5 */ @@ -979,18 +986,19 @@ class DrawHand(Hand): if 'DEAL' in self.actions: print >>fh, _("*** DEALING HANDS ***") for player in [x[1] for x in self.players if x[1] in players_who_act_ondeal]: - if 'DEAL' in self.holecards[player]: - (nc,oc) = self.holecards[player]['DEAL'] - print >>fh, _("Dealt to %s: [%s]") % (player, " ".join(nc)) + if 'DEAL' in self.holecards: + if self.holecards['DEAL'].has_key(player): + (nc,oc) = self.holecards['DEAL'][player] + print >>fh, _("Dealt to %s: [%s]") % (player, " ".join(nc)) for act in self.actions['DEAL']: - print >>fh, self.actionString(act) + print >>fh, self.actionString(act, 'DEAL') if 'DRAWONE' in self.actions: print >>fh, _("*** FIRST DRAW ***") for act in self.actions['DRAWONE']: - print >>fh, self.actionString(act) + print >>fh, self.actionString(act, 'DRAWONE') if act[0] == self.hero and act[1] == 'discards': - (nc,oc) = self.holecards['DRAWONE'][act[0]] + (nc,oc) = self.holecardsAsSet('DRAWONE', act[0]) dc = self.discards['DRAWONE'][act[0]] kc = oc - dc print >>fh, _("Dealt to %s [%s] [%s]" % (act[0], " ".join(kc), " ".join(nc))) @@ -998,9 +1006,9 @@ class DrawHand(Hand): if 'DRAWTWO' in self.actions: print >>fh, _("*** SECOND DRAW ***") for act in self.actions['DRAWTWO']: - print >>fh, self.actionString(act) + print >>fh, self.actionString(act, 'DRAWTWO') if act[0] == self.hero and act[1] == 'discards': - (nc,oc) = self.holecards['DRAWTWO'][act[0]] + (nc,oc) = self.holecardsAsSet('DRAWONE', act[0]) dc = self.discards['DRAWTWO'][act[0]] kc = oc - dc print >>fh, _("Dealt to %s [%s] [%s]" % (act[0], " ".join(kc), " ".join(nc))) @@ -1008,9 +1016,9 @@ class DrawHand(Hand): if 'DRAWTHREE' in self.actions: print >>fh, _("*** THIRD DRAW ***") for act in self.actions['DRAWTHREE']: - print >>fh, self.actionString(act) + print >>fh, self.actionString(act, 'DRAWTHREE') if act[0] == self.hero and act[1] == 'discards': - (nc,oc) = self.holecards['DRAWTHREE'][act[0]] + (nc,oc) = self.holecardsAsSet('DRAWONE', act[0]) dc = self.discards['DRAWTHREE'][act[0]] kc = oc - dc print >>fh, _("Dealt to %s [%s] [%s]" % (act[0], " ".join(kc), " ".join(nc))) diff --git a/pyfpdb/HandHistoryConverter.py b/pyfpdb/HandHistoryConverter.py index 804b3534..b618df8a 100644 --- a/pyfpdb/HandHistoryConverter.py +++ b/pyfpdb/HandHistoryConverter.py @@ -261,8 +261,8 @@ which it expects to find at self.re_TailSplitHands -- see for e.g. Everleaf.py. gametype = self.determineGameType(handText) log.debug("gametype %s" % gametype) hand = None + l = None if gametype is None: - l = None gametype = "unmatched" # TODO: not ideal, just trying to not error. # TODO: Need to count failed hands. diff --git a/pyfpdb/Hud.py b/pyfpdb/Hud.py index c9fe2f0a..c9420a8a 100644 --- a/pyfpdb/Hud.py +++ b/pyfpdb/Hud.py @@ -173,8 +173,26 @@ class Hud: item = gtk.CheckMenuItem(' All Levels') self.aggMenu.append(item) item.connect("activate", self.set_aggregation, ('P',10000)) - setattr(self, 'h_aggBBmultItem10000', item) - # + setattr(self, 'h_aggBBmultItem10000', item) + # + item = gtk.MenuItem('For #Seats:') + self.aggMenu.append(item) + # + item = gtk.CheckMenuItem(' Any Number') + self.aggMenu.append(item) + item.connect("activate", self.set_seats_style, ('P','A')) + setattr(self, 'h_seatsStyleOptionA', item) + # + item = gtk.CheckMenuItem(' Custom') + self.aggMenu.append(item) + item.connect("activate", self.set_seats_style, ('P','C')) + setattr(self, 'h_seatsStyleOptionC', item) + # + item = gtk.CheckMenuItem(' Exact') + self.aggMenu.append(item) + item.connect("activate", self.set_seats_style, ('P','E')) + setattr(self, 'h_seatsStyleOptionE', item) + # item = gtk.MenuItem('Since:') self.aggMenu.append(item) # @@ -224,8 +242,26 @@ class Hud: item = gtk.CheckMenuItem(' All Levels') self.aggMenu.append(item) item.connect("activate", self.set_aggregation, ('O',10000)) - setattr(self, 'aggBBmultItem10000', item) - # + setattr(self, 'aggBBmultItem10000', item) + # + item = gtk.MenuItem('For #Seats:') + self.aggMenu.append(item) + # + item = gtk.CheckMenuItem(' Any Number') + self.aggMenu.append(item) + item.connect("activate", self.set_seats_style, ('O','A')) + setattr(self, 'seatsStyleOptionA', item) + # + item = gtk.CheckMenuItem(' Custom') + self.aggMenu.append(item) + item.connect("activate", self.set_seats_style, ('O','C')) + setattr(self, 'seatsStyleOptionC', item) + # + item = gtk.CheckMenuItem(' Exact') + self.aggMenu.append(item) + item.connect("activate", self.set_seats_style, ('O','E')) + setattr(self, 'seatsStyleOptionE', item) + # item = gtk.MenuItem('Since:') self.aggMenu.append(item) # @@ -267,6 +303,20 @@ class Hud: elif self.hud_params['agg_bb_mult'] > 9000: getattr(self, 'aggBBmultItem10000').set_active(True) # + if self.hud_params['h_seats_style'] == 'A': + getattr(self, 'h_seatsStyleOptionA').set_active(True) + elif self.hud_params['h_seats_style'] == 'C': + getattr(self, 'h_seatsStyleOptionC').set_active(True) + elif self.hud_params['h_seats_style'] == 'E': + getattr(self, 'h_seatsStyleOptionE').set_active(True) + # + if self.hud_params['seats_style'] == 'A': + getattr(self, 'seatsStyleOptionA').set_active(True) + elif self.hud_params['seats_style'] == 'C': + getattr(self, 'seatsStyleOptionC').set_active(True) + elif self.hud_params['seats_style'] == 'E': + getattr(self, 'seatsStyleOptionE').set_active(True) + # if self.hud_params['h_hud_style'] == 'A': getattr(self, 'h_hudStyleOptionA').set_active(True) elif self.hud_params['h_hud_style'] == 'S': @@ -344,6 +394,29 @@ class Hud: if mult != str(num): getattr(self, 'aggBBmultItem'+mult).set_active(False) + def set_seats_style(self, widget, val): + (player_opp, style) = val + if player_opp == 'P': + param = 'h_seats_style' + prefix = 'h_' + else: + param = 'seats_style' + prefix = '' + + if style == 'A' and getattr(self, prefix+'seatsStyleOptionA').get_active(): + self.hud_params[param] = 'A' + getattr(self, prefix+'seatsStyleOptionC').set_active(False) + getattr(self, prefix+'seatsStyleOptionE').set_active(False) + elif style == 'C' and getattr(self, prefix+'seatsStyleOptionC').get_active(): + self.hud_params[param] = 'C' + getattr(self, prefix+'seatsStyleOptionA').set_active(False) + getattr(self, prefix+'seatsStyleOptionE').set_active(False) + elif style == 'E' and getattr(self, prefix+'seatsStyleOptionE').get_active(): + self.hud_params[param] = 'E' + getattr(self, prefix+'seatsStyleOptionA').set_active(False) + getattr(self, prefix+'seatsStyleOptionC').set_active(False) + print "setting self.hud_params[%s] = %s" % (param, style) + def set_hud_style(self, widget, val): (player_opp, style) = val if player_opp == 'P': diff --git a/pyfpdb/PokerStarsToFpdb.py b/pyfpdb/PokerStarsToFpdb.py index 7e63cee6..b3463da9 100755 --- a/pyfpdb/PokerStarsToFpdb.py +++ b/pyfpdb/PokerStarsToFpdb.py @@ -50,7 +50,7 @@ class PokerStars(HandHistoryConverter): \s?(?P%(LEGAL_ISO)s)? )\s)? # close paren of tournament info (?PHORSE|8\-Game|HOSE)?\s?\(? - (?PHold\'em|Razz|7\sCard\sStud|7\sCard\sStud\sHi/Lo|Omaha|Omaha\sHi/Lo|Badugi|Triple\sDraw\s2\-7\sLowball)\s + (?PHold\'em|Razz|7\sCard\sStud|7\sCard\sStud\sHi/Lo|Omaha|Omaha\sHi/Lo|Badugi|Triple\sDraw\s2\-7\sLowball|5\sCard\sDraw)\s (?PNo\sLimit|Limit|Pot\sLimit)\)?,?\s (-\sLevel\s(?P[IVXLC]+)\s)? \(? # open paren of the stakes @@ -101,7 +101,7 @@ class PokerStars(HandHistoryConverter): self.re_HeroCards = re.compile(r"^Dealt to %(PLYR)s(?: \[(?P.+?)\])?( \[(?P.+?)\])" % subst, re.MULTILINE) self.re_Action = re.compile(r""" ^%(PLYR)s:(?P\sbets|\schecks|\sraises|\scalls|\sfolds|\sdiscards|\sstands\spat) - (\s%(CUR)s(?P[.\d]+))?(\sto\s%(CUR)s(?P[.\d]+))? # the number discarded goes in + (\s(%(CUR)s)?(?P[.\d]+))?(\sto\s%(CUR)s(?P[.\d]+))? # the number discarded goes in (\scards?(\s\[(?P.+?)\])?)?""" % subst, re.MULTILINE|re.VERBOSE) self.re_ShowdownAction = re.compile(r"^%s: shows \[(?P.*)\]" % player_re, re.MULTILINE) @@ -133,6 +133,7 @@ class PokerStars(HandHistoryConverter): info = {} m = self.re_GameInfo.search(handText) if not m: + print "DEBUG: determineGameType(): did not match" return None mg = m.groupdict() @@ -147,6 +148,7 @@ class PokerStars(HandHistoryConverter): '7 Card Stud Hi/Lo' : ('stud','studhilo'), 'Badugi' : ('draw','badugi'), 'Triple Draw 2-7 Lowball' : ('draw','27_3draw'), + '5 Card Draw' : ('draw','fivedraw') } currencies = { u'€':'EUR', '$':'USD', '':'T$' } # I don't think this is doing what we think. mg will always have all diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index 9c74e1f1..d98d9fbe 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -1402,6 +1402,7 @@ class Sql: AND gt1.bigblind <= gt2.bigblind * %s /* bigblind similar size */ AND gt1.bigblind >= gt2.bigblind / %s AND gt2.id = h.gametypeId) + AND hc.activeSeats between %s and %s ) OR ( hp.playerId = %s @@ -1415,6 +1416,7 @@ class Sql: AND gt1.bigblind <= gt2.bigblind * %s /* bigblind similar size */ AND gt1.bigblind >= gt2.bigblind / %s AND gt2.id = h.gametypeId) + AND hc.activeSeats between %s and %s ) ) GROUP BY hc.PlayerId, p.name @@ -1432,11 +1434,11 @@ class Sql: if db_server == 'mysql': self.query['get_stats_from_hand_session'] = """ - SELECT hp.playerId AS player_id, + SELECT hp.playerId AS player_id, /* playerId and seats must */ + h.seats AS seats, /* be first and second field */ hp.handId AS hand_id, hp.seatNo AS seat, p.name AS screen_name, - h.seats AS seats, 1 AS n, cast(hp2.street0VPI as integer) AS vpip, cast(hp2.street0Aggr as integer) AS pfr, @@ -1494,21 +1496,30 @@ class Sql: cast(hp2.street4CheckCallRaiseChance as integer) AS ccr_opp_4, cast(hp2.street4CheckCallRaiseDone as integer) AS ccr_4 FROM - Hands h /* players in this hand */ + Hands h INNER JOIN Hands h2 ON (h2.id > %s AND h2.tableName = h.tableName) - INNER JOIN HandsPlayers hp ON (h.id = hp.handId) + INNER JOIN HandsPlayers hp ON (h.id = hp.handId) /* players in this hand */ INNER JOIN HandsPlayers hp2 ON (hp2.playerId+0 = hp.playerId+0 AND (hp2.handId = h2.id+0)) /* other hands by these players */ INNER JOIN Players p ON (p.id = hp2.PlayerId+0) WHERE hp.handId = %s /* check activeseats once this data returned (don't want to do that here as it might assume a session ended just because the number of seats dipped for a few hands) */ + AND ( /* 2 separate parts for hero and opponents */ + ( hp2.playerId != %s + AND h2.seats between %s and %s + ) + OR + ( hp2.playerId = %s + AND h2.seats between %s and %s + ) + ) ORDER BY h.handStart desc, hp2.PlayerId /* order rows by handstart descending so that we can stop reading rows when there's a gap over X minutes between hands (ie. when we get back to start of the session */ """ - else: # assume postgresql + elif db_server == 'postgresql': self.query['get_stats_from_hand_session'] = """ SELECT hp.playerId AS player_id, hp.handId AS hand_id, @@ -1582,6 +1593,103 @@ class Sql: /* check activeseats once this data returned (don't want to do that here as it might assume a session ended just because the number of seats dipped for a few hands) */ + AND ( /* 2 separate parts for hero and opponents */ + ( hp2.playerId != %s + AND h2.seats between %s and %s + ) + OR + ( hp2.playerId = %s + AND h2.seats between %s and %s + ) + ) + ORDER BY h.handStart desc, hp2.PlayerId + /* order rows by handstart descending so that we can stop reading rows when + there's a gap over X minutes between hands (ie. when we get back to start of + the session */ + """ + elif db_server == 'sqlite': + self.query['get_stats_from_hand_session'] = """ + SELECT hp.playerId AS player_id, + hp.handId AS hand_id, + hp.seatNo AS seat, + p.name AS screen_name, + h.seats AS seats, + 1 AS n, + cast(hp2.street0VPI as integer) AS vpip, + cast(hp2.street0Aggr as integer) AS pfr, + cast(hp2.street0_3BChance as integer) AS TB_opp_0, + cast(hp2.street0_3BDone as integer) AS TB_0, + cast(hp2.street1Seen as integer) AS saw_f, + cast(hp2.street1Seen as integer) AS saw_1, + cast(hp2.street2Seen as integer) AS saw_2, + cast(hp2.street3Seen as integer) AS saw_3, + cast(hp2.street4Seen as integer) AS saw_4, + cast(hp2.sawShowdown as integer) AS sd, + cast(hp2.street1Aggr as integer) AS aggr_1, + cast(hp2.street2Aggr as integer) AS aggr_2, + cast(hp2.street3Aggr as integer) AS aggr_3, + cast(hp2.street4Aggr as integer) AS aggr_4, + cast(hp2.otherRaisedStreet1 as integer) AS was_raised_1, + cast(hp2.otherRaisedStreet2 as integer) AS was_raised_2, + cast(hp2.otherRaisedStreet3 as integer) AS was_raised_3, + cast(hp2.otherRaisedStreet4 as integer) AS was_raised_4, + cast(hp2.foldToOtherRaisedStreet1 as integer) AS f_freq_1, + cast(hp2.foldToOtherRaisedStreet2 as integer) AS f_freq_2, + cast(hp2.foldToOtherRaisedStreet3 as integer) AS f_freq_3, + cast(hp2.foldToOtherRaisedStreet4 as integer) AS f_freq_4, + cast(hp2.wonWhenSeenStreet1 as integer) AS w_w_s_1, + cast(hp2.wonAtSD as integer) AS wmsd, + cast(hp2.stealAttemptChance as integer) AS steal_opp, + cast(hp2.stealAttempted as integer) AS steal, + cast(hp2.foldSbToStealChance as integer) AS SBstolen, + cast(hp2.foldedSbToSteal as integer) AS SBnotDef, + cast(hp2.foldBbToStealChance as integer) AS BBstolen, + cast(hp2.foldedBbToSteal as integer) AS BBnotDef, + cast(hp2.street1CBChance as integer) AS CB_opp_1, + cast(hp2.street1CBDone as integer) AS CB_1, + cast(hp2.street2CBChance as integer) AS CB_opp_2, + cast(hp2.street2CBDone as integer) AS CB_2, + cast(hp2.street3CBChance as integer) AS CB_opp_3, + cast(hp2.street3CBDone as integer) AS CB_3, + cast(hp2.street4CBChance as integer) AS CB_opp_4, + cast(hp2.street4CBDone as integer) AS CB_4, + cast(hp2.foldToStreet1CBChance as integer) AS f_cb_opp_1, + cast(hp2.foldToStreet1CBDone as integer) AS f_cb_1, + cast(hp2.foldToStreet2CBChance as integer) AS f_cb_opp_2, + cast(hp2.foldToStreet2CBDone as integer) AS f_cb_2, + cast(hp2.foldToStreet3CBChance as integer) AS f_cb_opp_3, + cast(hp2.foldToStreet3CBDone as integer) AS f_cb_3, + cast(hp2.foldToStreet4CBChance as integer) AS f_cb_opp_4, + cast(hp2.foldToStreet4CBDone as integer) AS f_cb_4, + cast(hp2.totalProfit as integer) AS net, + cast(hp2.street1CheckCallRaiseChance as integer) AS ccr_opp_1, + cast(hp2.street1CheckCallRaiseDone as integer) AS ccr_1, + cast(hp2.street2CheckCallRaiseChance as integer) AS ccr_opp_2, + cast(hp2.street2CheckCallRaiseDone as integer) AS ccr_2, + cast(hp2.street3CheckCallRaiseChance as integer) AS ccr_opp_3, + cast(hp2.street3CheckCallRaiseDone as integer) AS ccr_3, + cast(hp2.street4CheckCallRaiseChance as integer) AS ccr_opp_4, + cast(hp2.street4CheckCallRaiseDone as integer) AS ccr_4 + FROM Hands h /* this hand */ + INNER JOIN Hands h2 ON ( h2.id > %s /* other hands */ + AND h2.tableName = h.tableName) + INNER JOIN HandsPlayers hp ON (h.id = hp.handId) /* players in this hand */ + INNER JOIN HandsPlayers hp2 ON ( hp2.playerId+0 = hp.playerId+0 + AND hp2.handId = h2.id) /* other hands by these players */ + INNER JOIN Players p ON (p.id = hp2.PlayerId+0) + WHERE h.id = %s + /* check activeseats once this data returned (don't want to do that here as it might + assume a session ended just because the number of seats dipped for a few hands) + */ + AND ( /* 2 separate parts for hero and opponents */ + ( hp2.playerId != %s + AND h2.seats between %s and %s + ) + OR + ( hp2.playerId = %s + AND h2.seats between %s and %s + ) + ) ORDER BY h.handStart desc, hp2.PlayerId /* order rows by handstart descending so that we can stop reading rows when there's a gap over X minutes between hands (ie. when we get back to start of @@ -1605,10 +1713,13 @@ class Sql: self.query['get_table_name'] = """ SELECT h.tableName, h.maxSeats, gt.category, gt.type, s.id, s.name - FROM Hands h, Gametypes gt, Sites s + , count(1) as numseats + FROM Hands h, Gametypes gt, Sites s, HandsPlayers hp WHERE h.id = %s AND gt.id = h.gametypeId AND s.id = gt.siteID + AND hp.handId = h.id + GROUP BY h.tableName, h.maxSeats, gt.category, gt.type, s.id, s.name """ self.query['get_actual_seat'] = """ diff --git a/pyfpdb/TournamentTracker.py b/pyfpdb/TournamentTracker.py index 72be8494..e2dc724c 100644 --- a/pyfpdb/TournamentTracker.py +++ b/pyfpdb/TournamentTracker.py @@ -248,7 +248,7 @@ class ttracker_main(object): # get basic info about the new hand from the db # if there is a db error, complain, skip hand, and proceed try: - (table_name, max, poker_game, type, site_id) = self.db_connection.get_table_name(new_hand_id) + (table_name, max, poker_game, type, site_id, numseats) = self.db_connection.get_table_name(new_hand_id) stat_dict = self.db_connection.get_stats_from_hand(new_hand_id, aggregate_stats[type] ,hud_style, agg_bb_mult) diff --git a/pyfpdb/fpdb.py b/pyfpdb/fpdb.py index 80d3fb93..872c6672 100755 --- a/pyfpdb/fpdb.py +++ b/pyfpdb/fpdb.py @@ -95,37 +95,97 @@ class fpdb: self.add_tab(new_tab, new_tab_name) self.display_tab(new_tab_name) - def add_tab(self, new_tab, new_tab_name): + def add_tab(self, new_page, new_tab_name): """adds a tab, namely creates the button and displays it and appends all the relevant arrays""" - for i in self.tab_names: #todo: check this is valid - if i == new_tab_name: + for name in self.nb_tabs: #todo: check this is valid + if name == new_tab_name: return # if tab already exists, just go to it - self.tabs.append(new_tab) - self.tab_names.append(new_tab_name) + used_before = False + for i, name in enumerate(self.tab_names): #todo: check this is valid + if name == new_tab_name: + used_before = True + event_box = self.tabs[i] + page = self.pages[i] + break - new_tab_sel_button = gtk.ToggleButton(new_tab_name) - new_tab_sel_button.connect("clicked", self.tab_clicked, new_tab_name) - self.tab_box.add(new_tab_sel_button) - new_tab_sel_button.show() - self.tab_buttons.append(new_tab_sel_button) + if not used_before: + event_box = self.create_custom_tab(new_tab_name, self.nb) + page = new_page + self.pages.append(new_page) + self.tabs.append(event_box) + self.tab_names.append(new_tab_name) + + #self.nb.append_page(new_page, gtk.Label(new_tab_name)) + self.nb.append_page(page, event_box) + self.nb_tabs.append(new_tab_name) + page.show() def display_tab(self, new_tab_name): """displays the indicated tab""" tab_no = -1 - for i, name in enumerate(self.tab_names): - if name == new_tab_name: + for i, name in enumerate(self.nb_tabs): + if new_tab_name == name: tab_no = i break - if tab_no == -1: - raise FpdbError("invalid tab_no") + if tab_no < 0 or tab_no >= self.nb.get_n_pages(): + raise FpdbError("invalid tab_no " + str(tab_no)) else: - self.main_vbox.remove(self.current_tab) - self.current_tab=self.tabs[tab_no] - self.main_vbox.add(self.current_tab) - self.tab_buttons[tab_no].set_active(True) - self.current_tab.show() + self.nb.set_current_page(tab_no) + + def create_custom_tab(self, text, nb): + #create a custom tab for notebook containing a + #label and a button with STOCK_ICON + eventBox = gtk.EventBox() + tabBox = gtk.HBox(False, 2) + tabLabel = gtk.Label(text) + tabBox.pack_start(tabLabel, False) + eventBox.add(tabBox) + + if nb.get_n_pages() > 0: + tabButton = gtk.Button() + + tabButton.connect('clicked', self.remove_tab, (nb, text)) + #Add a picture on a button + self.add_icon_to_button(tabButton) + tabBox.pack_start(tabButton, False) + + # needed, otherwise even calling show_all on the notebook won't + # make the hbox contents appear. + tabBox.show_all() + return eventBox + + def add_icon_to_button(self, button): + iconBox = gtk.HBox(False, 0) + image = gtk.Image() + image.set_from_stock(gtk.STOCK_CLOSE, gtk.ICON_SIZE_MENU) + gtk.Button.set_relief(button, gtk.RELIEF_NONE) + settings = gtk.Widget.get_settings(button); + (w,h) = gtk.icon_size_lookup_for_settings(settings, gtk.ICON_SIZE_MENU); + gtk.Widget.set_size_request (button, w + 4, h + 4); + image.show() + iconBox.pack_start(image, True, False, 0) + button.add(iconBox) + iconBox.show() + return + + # Remove a page from the notebook + def remove_tab(self, button, data): + (nb, text) = data + page = -1 + #print "\n remove_tab: start", text + for i, tab in enumerate(self.nb_tabs): + if text == tab: + page = i + #print " page =", page + if page >= 0 and page < self.nb.get_n_pages(): + #print " removing page", page + del self.nb_tabs[page] + nb.remove_page(page) + # Need to refresh the widget -- + # This forces the widget to redraw itself. + #nb.queue_draw_area(0,0,-1,-1) needed or not?? def delete_event(self, widget, event, data=None): return False @@ -473,6 +533,10 @@ class fpdb: except Exceptions.FpdbMySQLAccessDenied: self.warning_box("MySQL Server reports: Access denied. Are your permissions set correctly?") exit() + except Exceptions.FpdbMySQLNoDatabase: + msg = "MySQL client reports: 2002 error. Unable to connect - Please check that the MySQL service has been started" + self.warning_box(msg) + exit # except FpdbMySQLFailedError: # self.warning_box("Unable to connect to MySQL! Is the MySQL server running?!", "FPDB ERROR") @@ -628,18 +692,14 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt") menubar.show() #done menubar + self.nb = gtk.Notebook() + self.nb.set_show_tabs(True) + self.nb.show() + self.main_vbox.pack_start(self.nb, True, True, 0) + self.pages=[] self.tabs=[] self.tab_names=[] - self.tab_buttons=[] - self.tab_box = gtk.HBox(True,1) - self.main_vbox.pack_start(self.tab_box, False, True, 0) - self.tab_box.show() - #done tab bar - - self.current_tab = gtk.VBox(False,1) - self.current_tab.set_border_width(1) - self.main_vbox.add(self.current_tab) - self.current_tab.show() + self.nb_tabs=[] self.tab_main_help(None, None) diff --git a/pyfpdb/fpdb_db.py b/pyfpdb/fpdb_db.py index a58606be..8a0ec54e 100644 --- a/pyfpdb/fpdb_db.py +++ b/pyfpdb/fpdb_db.py @@ -106,6 +106,8 @@ class fpdb_db: except MySQLdb.Error, ex: if ex.args[0] == 1045: raise FpdbMySQLAccessDenied(ex.args[0], ex.args[1]) + elif ex.args[0] == 2002: + raise FpdbMySQLNoDatabase(ex.args[0], ex.args[1]) else: print "*** WARNING UNKNOWN MYSQL ERROR", ex elif backend == fpdb_db.PGSQL: diff --git a/pyfpdb/test_PokerStars.py b/pyfpdb/test_PokerStars.py index 8730d98a..b58f8a75 100644 --- a/pyfpdb/test_PokerStars.py +++ b/pyfpdb/test_PokerStars.py @@ -95,16 +95,16 @@ def testStudImport(): (stored, dups, partial, errs, ttime) = importer.runImport() importer.clearFileList() -#def testDrawImport(): -# db.recreate_tables() -# importer = fpdb_import.Importer(False, settings, config) -# importer.setDropIndexes("don't drop") -# importer.setFailOnError(True) -# importer.setThreads(-1) -# importer.addBulkImportImportFileOrDir( -# """regression-test-files/cash/Stars/Draw/3-Draw-Limit-USD-0.10-0.20-200911.txt""", site="PokerStars") -# importer.addBulkImportImportFileOrDir( -# """regression-test-files/cash/Stars/Draw/5-Carddraw-USD-0.10-0.20-200911.txt""", site="PokerStars") -# importer.setCallHud(False) -# (stored, dups, partial, errs, ttime) = importer.runImport() -# importer.clearFileList() +def testDrawImport(): + db.recreate_tables() + importer = fpdb_import.Importer(False, settings, config) + importer.setDropIndexes("don't drop") + importer.setFailOnError(True) + importer.setThreads(-1) + importer.addBulkImportImportFileOrDir( + """regression-test-files/cash/Stars/Draw/3-Draw-Limit-USD-0.10-0.20-200911.txt""", site="PokerStars") + importer.addBulkImportImportFileOrDir( + """regression-test-files/cash/Stars/Draw/5-Carddraw-USD-0.10-0.20-200911.txt""", site="PokerStars") + importer.setCallHud(False) + (stored, dups, partial, errs, ttime) = importer.runImport() + importer.clearFileList()