diff --git a/packaging/debian/control b/packaging/debian/control index 93660791..784c4b40 100644 --- a/packaging/debian/control +++ b/packaging/debian/control @@ -6,7 +6,7 @@ Build-Depends: debhelper, python-support Standards-Version: 3.8.0 Package: python-fpdb -Architecture: any +Architecture: all Section: games Priority: extra Depends: ${python:Depends}, python-gtk2, python-matplotlib, diff --git a/pyfpdb/Card.py b/pyfpdb/Card.py index 8e99599b..287a0f6a 100755 --- a/pyfpdb/Card.py +++ b/pyfpdb/Card.py @@ -26,17 +26,17 @@ def twoStartCards(value1, suit1, value2, suit2): if value1 < 2 or value2 < 2: ret = 0 if value1 == value2: # pairs - ret = (13 * (value2-2) + (value2-1) ) + ret = (13 * (value2-2) + (value2-2) ) elif suit1 == suit2: if value1 > value2: - ret = 13 * (value1-2) + (value2-1) + ret = 13 * (value1-2) + (value2-2) else: - ret = 13 * (value2-2) + (value1-1) + ret = 13 * (value2-2) + (value1-2) else: if value1 > value2: - ret = 13 * (value2-2) + (value2-1) + ret = 13 * (value2-2) + (value1-2) else: - ret = 13 * (value1-2) + (value2-1) + ret = 13 * (value1-2) + (value2-2) # print "twoStartCards(", value1, suit1, value2, suit2, ")=", ret return ret @@ -46,7 +46,6 @@ def twoStartCardString(card): into a string like AQo """ ret = 'xx' if card > 0: - card -= 1 s = ('2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K', 'A') x = card / 13 y = card - 13 * x diff --git a/pyfpdb/Configuration.py b/pyfpdb/Configuration.py index dbe973c1..38a720fc 100755 --- a/pyfpdb/Configuration.py +++ b/pyfpdb/Configuration.py @@ -282,6 +282,19 @@ class HudUI: def __init__(self, node): self.node = node self.label = node.getAttribute('label') + # + self.aggregate_ring = fix_tf(node.getAttribute('aggregate_ring_game_stats')) + self.aggregate_tour = fix_tf(node.getAttribute('aggregate_tourney_stats')) + self.hud_style = node.getAttribute('stat_aggregation_range') + self.hud_days = node.getAttribute('aggregation_days') + self.agg_bb_mult = node.getAttribute('aggregation_level_multiplier') + # + self.h_aggregate_ring = fix_tf(node.getAttribute('aggregate_hero_ring_game_stats')) + self.h_aggregate_tour = fix_tf(node.getAttribute('aggregate_hero_tourney_stats')) + self.h_hud_style = node.getAttribute('hero_stat_aggregation_range') + self.h_hud_days = node.getAttribute('hero_aggregation_days') + self.h_agg_bb_mult = node.getAttribute('hero_aggregation_level_multiplier') + def __str__(self): return " label = %s\n" % self.label @@ -320,13 +333,21 @@ class Config: pass if file == None: # that didn't work either, just die - print "No HUD_config_xml found. Exiting" - sys.stderr.write("No HUD_config_xml found. Exiting") + print "No HUD_config_xml found after looking in current directory and "+self.default_config_path+"\nExiting" + sys.stderr.write("No HUD_config_xml found after looking in current directory and "+self.default_config_path+"\nExiting") + print "press enter to continue" + sys.stdin.readline() sys.exit() # Parse even if there was no real config file found and we are using the example # If using the example, we'll edit it later +# sc 2009/10/04 Example already copied to main filename, is this ok? log.info("Reading configuration file %s" % file) + if os.sep in file: + print "\nReading configuration file %s\n" % file + else: + print "\nReading configuration file %s" % file + print "in %s\n" % os.getcwd() try: doc = xml.dom.minidom.parse(file) except: @@ -418,6 +439,8 @@ class Config: db_pass = df_parms['db-password']) self.save(file=os.path.join(self.default_config_path, "HUD_config.xml")) + print "" + def set_hhArchiveBase(self, path): self.imp.node.setAttribute("hhArchiveBase", path) @@ -467,11 +490,15 @@ class Config: def find_example_config(self): if os.path.exists('HUD_config.xml.example'): # there is a HUD_config in the cwd - file = 'HUD_config.xml.example' # so we use it + file = 'HUD_config.xml' # so we use it + try: + shutil.copyfile(file+'.example', file) + except: + file = '' print "No HUD_config.xml found, using HUD_config.xml.example.\n", \ - "A HUD_config.xml will be written. You will probably have to edit it." + "A HUD_config.xml has been created. You will probably have to edit it." sys.stderr.write("No HUD_config.xml found, using HUD_config.xml.example.\n" + \ - "A HUD_config.xml will be written. You will probably have to edit it.") + "A HUD_config.xml has been created. You will probably have to edit it.") else: file = None return file @@ -615,6 +642,7 @@ class Config: # Allow to change the menu appearance def get_hud_ui_parameters(self): hui = {} + default_text = 'FPDB Menu - Right click\nLeft-Drag to Move' try: hui['label'] = self.ui.label @@ -622,6 +650,39 @@ class Config: hui['label'] = default_text except: hui['label'] = default_text + + 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 + + # 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_agg_bb_mult'] = self.ui.h_agg_bb_mult + except: hui['h_agg_bb_mult'] = 1 + return hui diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index 22630fb6..4a0f2e17 100755 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -49,12 +49,15 @@ import logging, logging.config logging.config.fileConfig(os.path.join(sys.path[0],"logging.conf")) log = logging.getLogger('db') + class Database: MYSQL_INNODB = 2 PGSQL = 3 SQLITE = 4 + hero_hudstart_def = '1999-12-31' # default for length of Hero's stats in HUD + # Data Structures for index and foreign key creation # drop_code is an int with possible values: 0 - don't drop for bulk import # 1 - drop during bulk import @@ -70,19 +73,19 @@ class Database: [ ] # no db with index 0 , [ ] # no db with index 1 , [ # indexes for mysql (list index 2) - {'tab':'Players', 'col':'name', 'drop':0} - , {'tab':'Hands', 'col':'siteHandNo', 'drop':0} - , {'tab':'Hands', 'col':'gametypeId', 'drop':0} # mct 22/3/09 + # {'tab':'Players', 'col':'name', 'drop':0} unique indexes not dropped + # {'tab':'Hands', 'col':'siteHandNo', 'drop':0} unique indexes not dropped + {'tab':'Hands', 'col':'gametypeId', 'drop':0} # mct 22/3/09 , {'tab':'HandsPlayers', 'col':'handId', 'drop':0} # not needed, handled by fk , {'tab':'HandsPlayers', 'col':'playerId', 'drop':0} # not needed, handled by fk , {'tab':'HandsPlayers', 'col':'tourneyTypeId', 'drop':0} , {'tab':'HandsPlayers', 'col':'tourneysPlayersId', 'drop':0} - , {'tab':'Tourneys', 'col':'siteTourneyNo', 'drop':0} + #, {'tab':'Tourneys', 'col':'siteTourneyNo', 'drop':0} unique indexes not dropped ] , [ # indexes for postgres (list index 3) {'tab':'Gametypes', 'col':'siteId', 'drop':0} , {'tab':'Hands', 'col':'gametypeId', 'drop':0} # mct 22/3/09 - , {'tab':'Hands', 'col':'siteHandNo', 'drop':0} + #, {'tab':'Hands', 'col':'siteHandNo', 'drop':0} unique indexes not dropped , {'tab':'HandsActions', 'col':'handsPlayerId', 'drop':0} , {'tab':'HandsPlayers', 'col':'handId', 'drop':1} , {'tab':'HandsPlayers', 'col':'playerId', 'drop':1} @@ -91,22 +94,22 @@ class Database: , {'tab':'HudCache', 'col':'playerId', 'drop':0} , {'tab':'HudCache', 'col':'tourneyTypeId', 'drop':0} , {'tab':'Players', 'col':'siteId', 'drop':1} - , {'tab':'Players', 'col':'name', 'drop':0} + #, {'tab':'Players', 'col':'name', 'drop':0} unique indexes not dropped , {'tab':'Tourneys', 'col':'tourneyTypeId', 'drop':1} - , {'tab':'Tourneys', 'col':'siteTourneyNo', 'drop':0} + #, {'tab':'Tourneys', 'col':'siteTourneyNo', 'drop':0} unique indexes not dropped , {'tab':'TourneysPlayers', 'col':'playerId', 'drop':0} - , {'tab':'TourneysPlayers', 'col':'tourneyId', 'drop':0} + #, {'tab':'TourneysPlayers', 'col':'tourneyId', 'drop':0} unique indexes not dropped , {'tab':'TourneyTypes', 'col':'siteId', 'drop':0} ] , [ # indexes for sqlite (list index 4) - {'tab':'Players', 'col':'name', 'drop':0} - , {'tab':'Hands', 'col':'siteHandNo', 'drop':0} - , {'tab':'Hands', 'col':'gametypeId', 'drop':0} + # {'tab':'Players', 'col':'name', 'drop':0} unique indexes not dropped + # {'tab':'Hands', 'col':'siteHandNo', 'drop':0} unique indexes not dropped + {'tab':'Hands', 'col':'gametypeId', 'drop':0} , {'tab':'HandsPlayers', 'col':'handId', 'drop':0} , {'tab':'HandsPlayers', 'col':'playerId', 'drop':0} , {'tab':'HandsPlayers', 'col':'tourneyTypeId', 'drop':0} , {'tab':'HandsPlayers', 'col':'tourneysPlayersId', 'drop':0} - , {'tab':'Tourneys', 'col':'siteTourneyNo', 'drop':0} + #, {'tab':'Tourneys', 'col':'siteTourneyNo', 'drop':0} unique indexes not dropped ] ] @@ -183,15 +186,9 @@ class Database: def __init__(self, c, sql = None): log.info("Creating Database instance, sql = %s" % sql) + self.config = c self.fdb = fpdb_db.fpdb_db() # sets self.fdb.db self.fdb.cursor and self.fdb.sql - self.fdb.do_connect(c) - self.connection = self.fdb.db - - db_params = c.get_db_parameters() - self.import_options = c.get_import_parameters() - self.type = db_params['db-type'] - self.backend = db_params['db-backend'] - self.db_server = db_params['db-server'] + self.do_connect(c) if self.backend == self.PGSQL: from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT, ISOLATION_LEVEL_READ_COMMITTED, ISOLATION_LEVEL_SERIALIZABLE @@ -202,14 +199,14 @@ class Database: # where possible avoid creating new SQL instance by using the global one passed in if sql is None: - self.sql = SQL.Sql(type = self.type, db_server = db_params['db-server']) + self.sql = SQL.Sql(type = self.type, db_server = self.db_server) else: self.sql = sql - if self.backend == self.SQLITE and db_params['db-databaseName'] == ':memory:' and self.fdb.wrongDbVersion: + if self.backend == self.SQLITE and self.database == ':memory:' and self.wrongDbVersion: log.info("sqlite/:memory: - creating") self.recreate_tables() - self.fdb.wrongDbVersion = False + self.wrongDbVersion = False self.pcache = None # PlayerId cache self.cachemiss = 0 # Delete me later - using to count player cache misses @@ -223,9 +220,10 @@ class Database: #self.hud_hero_days = 30 # but last T days or last H hands for yourself # vars for hand ids or dates fetched according to above config: - self.hand_1day_ago = 0 # max hand id more than 24 hrs earlier than now - self.date_ndays_ago = 'd000000' # date N days ago ('d' + YYMMDD) - self.date_nhands_ago = {} # dates N hands ago per player - not used yet + self.hand_1day_ago = 0 # max hand id more than 24 hrs earlier than now + self.date_ndays_ago = 'd000000' # date N days ago ('d' + YYMMDD) + self.h_date_ndays_ago = 'd000000' # date N days ago ('d' + YYMMDD) for hero + self.date_nhands_ago = {} # dates N hands ago per player - not used yet self.cursor = self.fdb.cursor @@ -240,6 +238,16 @@ class Database: def do_connect(self, c): self.fdb.do_connect(c) + self.connection = self.fdb.db + self.wrongDbVersion = self.fdb.wrongDbVersion + + db_params = c.get_db_parameters() + self.import_options = c.get_import_parameters() + self.type = db_params['db-type'] + self.backend = db_params['db-backend'] + self.db_server = db_params['db-server'] + self.database = db_params['db-databaseName'] + self.host = db_params['db-host'] def commit(self): self.fdb.db.commit() @@ -367,8 +375,12 @@ class Database: winners[row[0]] = row[1] return winners - def init_hud_stat_vars(self, hud_days): - """Initialise variables used by Hud to fetch stats.""" + def init_hud_stat_vars(self, hud_days, h_hud_days): + """Initialise variables used by Hud to fetch stats: + self.hand_1day_ago handId of latest hand played more than a day ago + self.date_ndays_ago date n days ago + self.h_date_ndays_ago date n days ago for hero (different n) + """ self.hand_1day_ago = 1 try: @@ -381,9 +393,14 @@ class Database: else: if row and row[0]: self.hand_1_day_ago = row[0] - d = timedelta(days=hud_days) - now = datetime.utcnow() - d - self.date_ndays_ago = "d%02d%02d%02d" % (now.year - 2000, now.month, now.day) + + d = timedelta(days=hud_days) + now = datetime.utcnow() - d + self.date_ndays_ago = "d%02d%02d%02d" % (now.year - 2000, now.month, now.day) + + d = timedelta(days=h_hud_days) + now = datetime.utcnow() - d + self.h_date_ndays_ago = "d%02d%02d%02d" % (now.year - 2000, now.month, now.day) def init_player_hud_stat_vars(self, playerid): # not sure if this is workable, to be continued ... @@ -403,26 +420,53 @@ class Database: err = traceback.extract_tb(sys.exc_info()[2])[-1] print "***Error: "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1]) - def get_stats_from_hand(self, hand, aggregate = False, hud_style = 'A', agg_bb_mult = 100): - if hud_style == 'S': + def get_stats_from_hand( self, hand, type # type is "ring" or "tour" + , hud_params = {'aggregate_tour':False, 'aggregate_ring':False, 'hud_style':'A', 'agg_bb_mult':100} + , hero_id = -1 + ): + aggregate = hud_params['aggregate_tour'] if type == "tour" else hud_params['aggregate_ring'] + hud_style = hud_params['hud_style'] + agg_bb_mult = hud_params['agg_bb_mult'] if aggregate else 1 + h_aggregate = hud_params['h_aggregate_tour'] if type == "tour" else hud_params['h_aggregate_ring'] + h_hud_style = hud_params['h_hud_style'] + h_agg_bb_mult = hud_params['h_agg_bb_mult'] if h_aggregate else 1 + stat_dict = {} - return( self.get_stats_from_hand_session(hand) ) + 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 - else: # hud_style == A + if hud_style == 'S' and h_hud_style == 'S': + return stat_dict - if hud_style == 'T': - stylekey = self.date_ndays_ago - #elif hud_style == 'H': - # stylekey = date_nhands_ago needs array by player here ... - else: # assume A (all-time) - stylekey = '0000000' # all stylekey values should be higher than this + if hud_style == 'T': + stylekey = self.date_ndays_ago + elif hud_style == 'A': + stylekey = '0000000' # all stylekey values should be higher than this + elif hud_style == 'S': + stylekey = 'zzzzzzz' # all stylekey values should be lower than this + #elif hud_style == 'H': + # stylekey = date_nhands_ago needs array by player here ... - if aggregate: - query = 'get_stats_from_hand_aggregated' - subs = (hand, stylekey, agg_bb_mult, agg_bb_mult) - else: - query = 'get_stats_from_hand' - subs = (hand, stylekey) + if h_hud_style == 'T': + h_stylekey = self.h_date_ndays_ago + elif h_hud_style == 'A': + h_stylekey = '0000000' # all stylekey values should be higher than this + elif h_hud_style == 'S': + h_stylekey = 'zzzzzzz' # all stylekey values should be lower than this + #elif h_hud_style == 'H': + # h_stylekey = date_nhands_ago needs array by player here ... + + #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 + #else: + # query = 'get_stats_from_hand' + # subs = (hand, stylekey) #print "get stats: hud style =", hud_style, "query =", query, "subs =", subs c = self.connection.cursor() @@ -430,18 +474,29 @@ class Database: # now get the stats c.execute(self.sql.query[query], subs) colnames = [desc[0] for desc in c.description] - stat_dict = {} for row in c.fetchall(): - t_dict = {} - for name, val in zip(colnames, row): - t_dict[name.lower()] = val -# print t_dict - stat_dict[t_dict['player_id']] = t_dict + playerid = row[0] + if (playerid == hero_id and h_hud_style != 'S') or (playerid != hero_id and hud_style != 'S'): + t_dict = {} + for name, val in zip(colnames, row): + 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 # uses query on handsplayers instead of hudcache to get stats on just this session - def get_stats_from_hand_session(self, hand): + def get_stats_from_hand_session(self, hand, stat_dict, hero_id, hud_style, h_hud_style): + """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' + """ query = self.sql.query['get_stats_from_hand_session'] if self.db_server == 'mysql': @@ -456,31 +511,34 @@ class Database: #print "sess_stats: subs =", subs, "subs[0] =", subs[0] c.execute(query, subs) colnames = [desc[0] for desc in c.description] - n,stat_dict = 0,{} - row = c.fetchone() - while row: - if colnames[0].lower() == 'player_id': - playerid = row[0] - else: - log.error("ERROR: query %s result does not have player_id as first column" % (query,)) - break + n = 0 + + row = c.fetchone() + if colnames[0].lower() == 'player_id': + + # Loop through stats adding them to appropriate stat_dict: + while row: + playerid = row[0] + 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: + stat_dict[playerid] = {} + stat_dict[playerid][name.lower()] = val + elif not name.lower() in stat_dict[playerid]: + stat_dict[playerid][name.lower()] = val + elif name.lower() not in ('hand_id', 'player_id', 'seat', 'screen_name', 'seats'): + 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? + row = c.fetchone() + else: + log.error("ERROR: query %s result does not have player_id as first column" % (query,)) - for name, val in zip(colnames, row): - if not playerid in stat_dict: - stat_dict[playerid] = {} - stat_dict[playerid][name.lower()] = val - elif not name.lower() in stat_dict[playerid]: - stat_dict[playerid][name.lower()] = val - elif name.lower() not in ('hand_id', 'player_id', 'seat', 'screen_name', 'seats'): - stat_dict[playerid][name.lower()] += val - n += 1 - if n >= 4000: break # todo: don't think this is needed so set nice and high - # for now - comment out or remove? - row = c.fetchone() #print " %d rows fetched, len(stat_dict) = %d" % (n, len(stat_dict)) #print "session stat_dict =", stat_dict - return stat_dict + #return stat_dict def get_player_id(self, config, site, player_name): c = self.connection.cursor() @@ -491,6 +549,79 @@ class Database: else: return None + def get_player_names(self, config, site_id=None, like_player_name="%"): + """Fetch player names from players. Use site_id and like_player_name if provided""" + + if site_id == None: + site_id = -1 + c = self.get_cursor() + c.execute(self.sql.query['get_player_names'], (like_player_name, site_id, site_id)) + rows = c.fetchall() + return rows + + #returns the SQL ids of the names given in an array + # TODO: if someone gets industrious, they should make the parts that use the output of this function deal with a dict + # { playername: id } instead of depending on it's relation to the positions list + # then this can be reduced in complexity a bit + + #def recognisePlayerIDs(cursor, names, site_id): + # result = [] + # for i in xrange(len(names)): + # cursor.execute ("SELECT id FROM Players WHERE name=%s", (names[i],)) + # tmp=cursor.fetchall() + # if (len(tmp)==0): #new player + # cursor.execute ("INSERT INTO Players (name, siteId) VALUES (%s, %s)", (names[i], site_id)) + # #print "Number of players rows inserted: %d" % cursor.rowcount + # cursor.execute ("SELECT id FROM Players WHERE name=%s", (names[i],)) + # tmp=cursor.fetchall() + # #print "recognisePlayerIDs, names[i]:",names[i],"tmp:",tmp + # result.append(tmp[0][0]) + # return result + + def recognisePlayerIDs(self, names, site_id): + c = self.get_cursor() + q = "SELECT name,id FROM Players WHERE siteid=%d and (name=%s)" %(site_id, " OR name=".join([self.sql.query['placeholder'] for n in names])) + c.execute(q, names) # get all playerids by the names passed in + ids = dict(c.fetchall()) # convert to dict + if len(ids) != len(names): + notfound = [n for n in names if n not in ids] # make list of names not in database + if notfound: # insert them into database + q_ins = "INSERT INTO Players (name, siteId) VALUES (%s, "+str(site_id)+")" + q_ins = q_ins.replace('%s', self.sql.query['placeholder']) + c.executemany(q_ins, [(n,) for n in notfound]) + q2 = "SELECT name,id FROM Players WHERE siteid=%d and (name=%s)" % (site_id, " OR name=".join(["%s" for n in notfound])) + q2 = q2.replace('%s', self.sql.query['placeholder']) + c.execute(q2, notfound) # get their new ids + tmp = c.fetchall() + for n,id in tmp: # put them all into the same dict + ids[n] = id + # return them in the SAME ORDER that they came in in the names argument, rather than the order they came out of the DB + return [ids[n] for n in names] + #end def recognisePlayerIDs + + # Here's a version that would work if it wasn't for the fact that it needs to have the output in the same order as input + # this version could also be improved upon using list comprehensions, etc + + #def recognisePlayerIDs(cursor, names, site_id): + # result = [] + # notfound = [] + # cursor.execute("SELECT name,id FROM Players WHERE name='%s'" % "' OR name='".join(names)) + # tmp = dict(cursor.fetchall()) + # for n in names: + # if n not in tmp: + # notfound.append(n) + # else: + # result.append(tmp[n]) + # if notfound: + # cursor.executemany("INSERT INTO Players (name, siteId) VALUES (%s, "+str(site_id)+")", (notfound)) + # cursor.execute("SELECT id FROM Players WHERE name='%s'" % "' OR name='".join(notfound)) + # tmp = cursor.fetchall() + # for n in tmp: + # result.append(n[0]) + # + # return result + + def get_site_id(self, site): c = self.get_cursor() c.execute(self.sql.query['getSiteId'], (site,)) @@ -855,6 +986,7 @@ class Database: log.debug(self.sql.query['createSettingsTable']) c = self.get_cursor() c.execute(self.sql.query['createSettingsTable']) + log.debug(self.sql.query['createSitesTable']) c.execute(self.sql.query['createSitesTable']) c.execute(self.sql.query['createGametypesTable']) @@ -867,9 +999,14 @@ class Database: c.execute(self.sql.query['createHandsPlayersTable']) c.execute(self.sql.query['createHandsActionsTable']) c.execute(self.sql.query['createHudCacheTable']) - #c.execute(self.sql.query['addTourneyIndex']) - #c.execute(self.sql.query['addHandsIndex']) - #c.execute(self.sql.query['addPlayersIndex']) + + # create unique indexes: + c.execute(self.sql.query['addTourneyIndex']) + c.execute(self.sql.query['addHandsIndex']) + c.execute(self.sql.query['addPlayersIndex']) + c.execute(self.sql.query['addTPlayersIndex']) + c.execute(self.sql.query['addTTypesIndex']) + self.fillDefaultData() self.commit() except: @@ -1011,20 +1148,48 @@ class Database: c.execute("INSERT INTO Sites (name,currency) VALUES ('PartyPoker', 'USD')") if self.backend == self.SQLITE: c.execute("INSERT INTO TourneyTypes (id, siteId, buyin, fee) VALUES (NULL, 1, 0, 0);") - else: + elif self.backend == self.PGSQL: c.execute("""insert into TourneyTypes(siteId, buyin, fee, maxSeats, knockout ,rebuyOrAddon, speed, headsUp, shootout, matrix) values (1, 0, 0, 0, False, False, null, False, False, False);""") + elif self.backend == self.MYSQL_INNODB: + c.execute("""insert into TourneyTypes(id, siteId, buyin, fee, maxSeats, knockout + ,rebuyOrAddon, speed, headsUp, shootout, matrix) + values (DEFAULT, 1, 0, 0, 0, False, False, null, False, False, False);""") #end def fillDefaultData - def rebuild_hudcache(self): + def rebuild_hudcache(self, start=None): """clears hudcache and rebuilds from the individual handsplayers records""" try: stime = time() + # derive list of program owner's player ids + self.hero = {} # name of program owner indexed by site id + self.hero_ids = {'dummy':-53, 'dummy2':-52} # playerid of owner indexed by site id + # make sure at least two values in list + # so that tuple generation creates doesn't use + # () or (1,) style + for site in self.config.get_supported_sites(): + result = self.get_site_id(site) + if result: + site_id = result[0][0] + self.hero[site_id] = self.config.supported_sites[site].screen_name + p_id = self.get_player_id(self.config, site, self.hero[site_id]) + if p_id: + self.hero_ids[site_id] = int(p_id) + + if start == None: + start = self.hero_hudstart_def + if self.hero_ids == {}: + where = "" + else: + where = "where hp.playerId not in " + str(tuple(self.hero_ids.values())) \ + + " or h.handStart > '" + start + "'" + rebuild_sql = self.sql.query['rebuildHudCache'].replace('', where) + self.get_cursor().execute(self.sql.query['clearHudCache']) - self.get_cursor().execute(self.sql.query['rebuildHudCache']) + self.get_cursor().execute(rebuild_sql) self.commit() print "Rebuild hudcache took %.1f seconds" % (time() - stime,) except: @@ -1033,6 +1198,39 @@ class Database: print err #end def rebuild_hudcache + def get_hero_hudcache_start(self): + """fetches earliest stylekey from hudcache for one of hero's player ids""" + + try: + # derive list of program owner's player ids + self.hero = {} # name of program owner indexed by site id + self.hero_ids = {'dummy':-53, 'dummy2':-52} # playerid of owner indexed by site id + # make sure at least two values in list + # so that tuple generation creates doesn't use + # () or (1,) style + for site in self.config.get_supported_sites(): + result = self.get_site_id(site) + if result: + site_id = result[0][0] + self.hero[site_id] = self.config.supported_sites[site].screen_name + p_id = self.get_player_id(self.config, site, self.hero[site_id]) + if p_id: + self.hero_ids[site_id] = int(p_id) + + q = self.sql.query['get_hero_hudcache_start'].replace("", str(tuple(self.hero_ids.values()))) + c = self.get_cursor() + c.execute(q) + tmp = c.fetchone() + if tmp == (None,): + return self.hero_hudstart_def + else: + return "20"+tmp[0][1:3] + "-" + tmp[0][3:5] + "-" + tmp[0][5:7] + except: + err = traceback.extract_tb(sys.exc_info()[2])[-1] + print "Error rebuilding hudcache:", str(sys.exc_value) + print err + #end def get_hero_hudcache_start + def analyzeDB(self): """Do whatever the DB can offer to update index/table statistics""" @@ -1196,11 +1394,23 @@ class Database: importtime, seats, maxseats, + texture, + playersVpi, boardcard1, boardcard2, boardcard3, boardcard4, boardcard5, + playersAtStreet1, + playersAtStreet2, + playersAtStreet3, + playersAtStreet4, + playersAtShowdown, + street0Raises, + street1Raises, + street2Raises, + street3Raises, + street4Raises, street1Pot, street2Pot, street3Pot, @@ -1209,49 +1419,36 @@ class Database: ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, - %s, %s, %s, %s, %s, %s, %s)""" -#--- texture, -#-- playersVpi, -#-- playersAtStreet1, -#-- playersAtStreet2, -#-- playersAtStreet3, -#-- playersAtStreet4, -#-- playersAtShowdown, -#-- street0Raises, -#-- street1Raises, -#-- street2Raises, -#-- street3Raises, -#-- street4Raises, -#-- seats, + %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']) - print "DEBUG: p: %s" %p - print "DEBUG: gtid: %s" % p['gameTypeId'] + self.cursor.execute(q, ( p['tableName'], p['gameTypeId'], p['siteHandNo'], p['handStart'], datetime.today(), #importtime -# len(p['names']), #seats - p['maxSeats'], p['seats'], + p['maxSeats'], + p['texture'], + p['playersVpi'], p['boardcard1'], p['boardcard2'], p['boardcard3'], p['boardcard4'], p['boardcard5'], -# hudCache['playersVpi'], -# hudCache['playersAtStreet1'], -# hudCache['playersAtStreet2'], -# hudCache['playersAtStreet3'], -# hudCache['playersAtStreet4'], -# hudCache['playersAtShowdown'], -# hudCache['street0Raises'], -# hudCache['street1Raises'], -# hudCache['street2Raises'], -# hudCache['street3Raises'], -# hudCache['street4Raises'], + p['playersAtStreet1'], + p['playersAtStreet2'], + p['playersAtStreet3'], + p['playersAtStreet4'], + p['playersAtShowdown'], + p['street0Raises'], + p['street1Raises'], + p['street2Raises'], + p['street3Raises'], + p['street4Raises'], p['street1Pot'], p['street2Pot'], p['street3Pot'], @@ -1261,101 +1458,312 @@ class Database: #return getLastInsertId(backend, conn, cursor) # def storeHand - def storeHandsPlayers(self, p): - #def store_hands_players_holdem_omaha(self, backend, category, hands_id, player_ids, start_cashes - # ,positions, card_values, card_suits, winnings, rakes, seatNos, hudCache): - # result=[] - # - # # postgres (and others?) needs the booleans converted to ints before saving: - # # (or we could just save them as boolean ... but then we can't sum them so easily in sql ???) - # # NO - storing booleans for now so don't need this - # #hudCacheInt = {} - # #for k,v in hudCache.iteritems(): - # # if k in ('wonWhenSeenStreet1', 'wonAtSD', 'totalProfit'): - # # hudCacheInt[k] = v - # # else: - # # hudCacheInt[k] = map(lambda x: 1 if x else 0, v) - # - # try: - # inserts = [] - # for i in xrange(len(player_ids)): - # card1 = Card.cardFromValueSuit(card_values[i][0], card_suits[i][0]) - # card2 = Card.cardFromValueSuit(card_values[i][1], card_suits[i][1]) - # - # if (category=="holdem"): - # startCards = Card.twoStartCards(card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1]) - # card3 = None - # card4 = None - # elif (category=="omahahi" or category=="omahahilo"): - # startCards = Card.fourStartCards(card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1] - # ,card_values[i][2], card_suits[i][2], card_values[i][3], card_suits[i][3]) - # card3 = Card.cardFromValueSuit(card_values[i][2], card_suits[i][2]) - # card4 = Card.cardFromValueSuit(card_values[i][3], card_suits[i][3]) - # else: - # raise FpdbError("invalid category") - # - # inserts.append( ( - # hands_id, player_ids[i], start_cashes[i], positions[i], 1, # tourneytypeid - needed for hudcache - # card1, card2, card3, card4, startCards, - # winnings[i], rakes[i], seatNos[i], hudCache['totalProfit'][i], - # hudCache['street0VPI'][i], hudCache['street0Aggr'][i], - # hudCache['street0_3BChance'][i], hudCache['street0_3BDone'][i], - # hudCache['street1Seen'][i], hudCache['street2Seen'][i], hudCache['street3Seen'][i], - # hudCache['street4Seen'][i], hudCache['sawShowdown'][i], - # hudCache['street1Aggr'][i], hudCache['street2Aggr'][i], hudCache['street3Aggr'][i], hudCache['street4Aggr'][i], - # hudCache['otherRaisedStreet1'][i], hudCache['otherRaisedStreet2'][i], - # hudCache['otherRaisedStreet3'][i], hudCache['otherRaisedStreet4'][i], - # hudCache['foldToOtherRaisedStreet1'][i], hudCache['foldToOtherRaisedStreet2'][i], - # hudCache['foldToOtherRaisedStreet3'][i], hudCache['foldToOtherRaisedStreet4'][i], - # hudCache['wonWhenSeenStreet1'][i], hudCache['wonAtSD'][i], - # hudCache['stealAttemptChance'][i], hudCache['stealAttempted'][i], hudCache['foldBbToStealChance'][i], - # hudCache['foldedBbToSteal'][i], hudCache['foldSbToStealChance'][i], hudCache['foldedSbToSteal'][i], - # hudCache['street1CBChance'][i], hudCache['street1CBDone'][i], hudCache['street2CBChance'][i], hudCache['street2CBDone'][i], - # hudCache['street3CBChance'][i], hudCache['street3CBDone'][i], hudCache['street4CBChance'][i], hudCache['street4CBDone'][i], - # hudCache['foldToStreet1CBChance'][i], hudCache['foldToStreet1CBDone'][i], - # hudCache['foldToStreet2CBChance'][i], hudCache['foldToStreet2CBDone'][i], - # hudCache['foldToStreet3CBChance'][i], hudCache['foldToStreet3CBDone'][i], - # hudCache['foldToStreet4CBChance'][i], hudCache['foldToStreet4CBDone'][i], - # hudCache['street1CheckCallRaiseChance'][i], hudCache['street1CheckCallRaiseDone'][i], - # hudCache['street2CheckCallRaiseChance'][i], hudCache['street2CheckCallRaiseDone'][i], - # hudCache['street3CheckCallRaiseChance'][i], hudCache['street3CheckCallRaiseDone'][i], - # hudCache['street4CheckCallRaiseChance'][i], hudCache['street4CheckCallRaiseDone'][i], - # hudCache['street0Calls'][i], hudCache['street1Calls'][i], hudCache['street2Calls'][i], hudCache['street3Calls'][i], hudCache['street4Calls'][i], - # hudCache['street0Bets'][i], hudCache['street1Bets'][i], hudCache['street2Bets'][i], hudCache['street3Bets'][i], hudCache['street4Bets'][i] - # ) ) - # c = self.get_cursor() - # c.executemany (""" - # INSERT INTO HandsPlayers - # (handId, playerId, 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 - # ) - # 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, %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, %s, %s, %s, %s, %s, %s, %s, %s, %s, - # %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""".replace('%s', self.sql.query['placeholder']) - # ,inserts ) - # result.append( self.get_last_insert_id(c) ) # wrong? not used currently - # except: - # raise FpdbError( "store_hands_players_holdem_omaha error: " + str(sys.exc_value) ) - # - # return result + def storeHandsPlayers(self, hid, pid, p): + q = """INSERT INTO HandsPlayers ( + handId, + playerId + ) + VALUES ( + %s, %s + )""" - pass +# 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 + + 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 + + def storeHudCacheNew(self, gid, pid, hc): + q = """INSERT INTO HudCache ( + gametypeId, + playerId + ) + VALUES ( + %s, %s + )""" + +# gametypeId, +# playerId, +# activeSeats, +# position, +# tourneyTypeId, +# styleKey, +# HDs, +# 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, +# totalProfit, +# street1CheckCallRaiseChance, +# street1CheckCallRaiseDone, +# street2CheckCallRaiseChance, +# street2CheckCallRaiseDone, +# street3CheckCallRaiseChance, +# street3CheckCallRaiseDone, +# street4CheckCallRaiseChance, +# street4CheckCallRaiseDone) + + q = q.replace('%s', self.sql.query['placeholder']) + + self.cursor.execute(q, ( + gid, + pid + )) + +# gametypeId, +# playerId, +# activeSeats, +# position, +# tourneyTypeId, +# styleKey, +# HDs, +# 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, +# totalProfit, +# street1CheckCallRaiseChance, +# street1CheckCallRaiseDone, +# street2CheckCallRaiseChance, +# street2CheckCallRaiseDone, +# street3CheckCallRaiseChance, +# street3CheckCallRaiseDone, +# street4CheckCallRaiseChance, +# street4CheckCallRaiseDone) ################################# @@ -1623,6 +2031,8 @@ class Database: #cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i])) #result.append(cursor.fetchall()[0][0]) except: + err = traceback.extract_tb(sys.exc_info()[2])[-1] + print "***Error storing hand: "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1]) raise FpdbError( "store_hands_players_holdem_omaha_tourney error: " + str(sys.exc_value) ) return result @@ -1855,27 +2265,40 @@ class Database: #end def storeHudCache def store_tourneys(self, tourneyTypeId, siteTourneyNo, entries, prizepool, startTime): + ret = -1 try: + # try and create tourney record, fetch id if it already exists + # avoids race condition when doing the select first cursor = self.get_cursor() - cursor.execute("SELECT id FROM Tourneys WHERE siteTourneyNo=%s AND tourneyTypeId+0=%s".replace('%s', self.sql.query['placeholder']) - , (siteTourneyNo, tourneyTypeId)) - tmp=cursor.fetchone() - #print "tried SELECTing tourneys.id, result:",tmp - - try: - len(tmp) - except TypeError:#means we have to create new one - cursor.execute("""INSERT INTO Tourneys - (tourneyTypeId, siteTourneyNo, entries, prizepool, startTime) - VALUES (%s, %s, %s, %s, %s)""".replace('%s', self.sql.query['placeholder']) - ,(tourneyTypeId, siteTourneyNo, entries, prizepool, startTime)) - cursor.execute("SELECT id FROM Tourneys WHERE siteTourneyNo=%s AND tourneyTypeId+0=%s", (siteTourneyNo, tourneyTypeId)) - tmp=cursor.fetchone() - #print "created new tourneys.id:",tmp + cursor.execute("savepoint ins_tourney") + cursor.execute("""INSERT INTO Tourneys + (tourneyTypeId, siteTourneyNo, entries, prizepool, startTime) + VALUES (%s, %s, %s, %s, %s)""".replace('%s', self.sql.query['placeholder']) + ,(tourneyTypeId, siteTourneyNo, entries, prizepool, startTime)) + ret = self.get_last_insert_id(cursor) + #print "created new tourneys.id:",ret except: - raise FpdbError( "store_tourneys error: " + str(sys.exc_value) ) - - return tmp[0] + #if str(sys.exc_value) contains 'sitetourneyno': + # raise FpdbError( "store_tourneys error: " + str(sys.exc_value) ) + #else: + #print "error insert tourney (%s) trying select ..." % (str(sys.exc_value),) + cursor.execute("rollback to savepoint ins_tourney") + try: + cursor.execute( "SELECT id FROM Tourneys WHERE siteTourneyNo=%s AND tourneyTypeId+0=%s".replace('%s', self.sql.query['placeholder']) + , (siteTourneyNo, tourneyTypeId) ) + rec = cursor.fetchone() + #print "select tourney result: ", rec + try: + len(rec) + ret = rec[0] + except: + print "Tourney id not found" + except: + print "Error selecting tourney id:", str(sys.exc_info()[1]) + + cursor.execute("release savepoint ins_tourney") + #print "store_tourneys returning", ret + return ret #end def store_tourneys def store_tourneys_players(self, tourney_id, player_ids, payin_amounts, ranks, winnings): @@ -1888,26 +2311,32 @@ class Database: #print "ranks:",ranks #print "winnings:",winnings for i in xrange(len(player_ids)): - cursor.execute("SELECT id FROM TourneysPlayers WHERE tourneyId=%s AND playerId+0=%s".replace('%s', self.sql.query['placeholder']) - ,(tourney_id, player_ids[i])) - tmp=cursor.fetchone() - #print "tried SELECTing tourneys_players.id:",tmp - try: - len(tmp) - except TypeError: + cursor.execute("savepoint ins_tplayer") cursor.execute("""INSERT INTO TourneysPlayers - (tourneyId, playerId, payinAmount, rank, winnings) VALUES (%s, %s, %s, %s, %s)""".replace('%s', self.sql.query['placeholder']), + (tourneyId, playerId, payinAmount, rank, winnings) VALUES (%s, %s, %s, %s, %s)""".replace('%s', self.sql.query['placeholder']), (tourney_id, player_ids[i], payin_amounts[i], ranks[i], winnings[i])) - cursor.execute("SELECT id FROM TourneysPlayers WHERE tourneyId=%s AND playerId+0=%s".replace('%s', self.sql.query['placeholder']), - (tourney_id, player_ids[i])) - tmp=cursor.fetchone() - #print "created new tourneys_players.id:",tmp - result.append(tmp[0]) + tmp = self.get_last_insert_id(cursor) + result.append(tmp) + #print "created new tourneys_players.id:", tmp + except: + cursor.execute("rollback to savepoint ins_tplayer") + cursor.execute("SELECT id FROM TourneysPlayers WHERE tourneyId=%s AND playerId+0=%s".replace('%s', self.sql.query['placeholder']) + ,(tourney_id, player_ids[i])) + tmp = cursor.fetchone() + #print "tried SELECTing tourneys_players.id:", tmp + try: + len(tmp) + result.append(tmp[0]) + except: + print "tplayer id not found for tourney,player %s,%s" % (tourney_id, player_ids[i]) + pass except: raise FpdbError( "store_tourneys_players error: " + str(sys.exc_value) ) - + + cursor.execute("release savepoint ins_tplayer") + #print "store_tourneys_players returning", result return result #end def store_tourneys_players @@ -2130,10 +2559,10 @@ class Database: def tStoreTourneyPlayers(self, tourney, dbTourneyId): logging.debug("Database.tStoreTourneyPlayers") # First, get playerids for the players and specifically the one for hero : - playersIds = fpdb_simple.recognisePlayerIDs(self, tourney.players, tourney.siteId) + playersIds = self.recognisePlayerIDs(tourney.players, tourney.siteId) # hero may be None for matrix tourneys summaries # hero = [ tourney.hero ] -# heroId = fpdb_simple.recognisePlayerIDs(self, hero , tourney.siteId) +# heroId = self.recognisePlayerIDs(hero , tourney.siteId) # logging.debug("hero Id = %s - playersId = %s" % (heroId , playersIds)) tourneyPlayersIds=[] @@ -2347,12 +2776,12 @@ if __name__=="__main__": if hero: print "nutOmatic is id_player = %d" % hero - stat_dict = db_connection.get_stats_from_hand(h) + stat_dict = db_connection.get_stats_from_hand(h, "ring") for p in stat_dict.keys(): print p, " ", stat_dict[p] #print "nutOmatics stats:" - #stat_dict = db_connection.get_stats_from_hand(h, hero) + #stat_dict = db_connection.get_stats_from_hand(h, "ring") #for p in stat_dict.keys(): # print p, " ", stat_dict[p] diff --git a/pyfpdb/DerivedStats.py b/pyfpdb/DerivedStats.py index 3de15ee5..315ae707 100644 --- a/pyfpdb/DerivedStats.py +++ b/pyfpdb/DerivedStats.py @@ -22,76 +22,6 @@ class DerivedStats(): def __init__(self, hand): self.hand = hand - self.activeSeats = 0 - self.position = 0 - self.tourneyTypeId = 0 - - self.HDs = 0 - self.street0VPI = 0 - self.street0Aggr = 0 - self.street0_3BChance = 0 - self.street0_3BDone = 0 - self.street0_4BChance = 0 - self.street0_4BDone = 0 - - self.street1Seen = 0 - self.street2Seen = 0 - self.street3Seen = 0 - self.street4Seen = 0 - self.sawShowdown = 0 - - self.street1Aggr = 0 - self.street2Aggr = 0 - self.street3Aggr = 0 - self.street4Aggr = 0 - - self.otherRaisedStreet1 = 0 - self.otherRaisedStreet2 = 0 - self.otherRaisedStreet3 = 0 - self.otherRaisedStreet4 = 0 - self.foldToOtherRaisedStreet1 = 0 - self.foldToOtherRaisedStreet2 = 0 - self.foldToOtherRaisedStreet3 = 0 - self.foldToOtherRaisedStreet4 = 0 - self.wonWhenSeenStreet1 = 0 - self.wonAtSD = 0 - - self.stealAttemptChance = 0 - self.stealAttempted = 0 - self.foldBbToStealChance = 0 - self.foldedBbToSteal = 0 - self.foldSbToStealChance = 0 - self.foldedSbToSteal = 0 - - self.street1CBChance = 0 - self.street1CBDone = 0 - self.street2CBChance = 0 - self.street2CBDone = 0 - self.street3CBChance = 0 - self.street3CBDone = 0 - self.street4CBChance = 0 - self.street4CBDone = 0 - - self.foldToStreet1CBChance = 0 - self.foldToStreet1CBDone = 0 - self.foldToStreet2CBChance = 0 - self.foldToStreet2CBDone = 0 - self.foldToStreet3CBChance = 0 - self.foldToStreet3CBDone = 0 - self.foldToStreet4CBChance = 0 - self.foldToStreet4CBDone = 0 - - self.totalProfit = 0 - - self.street1CheckCallRaiseChance = 0 - self.street1CheckCallRaiseDone = 0 - self.street2CheckCallRaiseChance = 0 - self.street2CheckCallRaiseDone = 0 - self.street3CheckCallRaiseChance = 0 - self.street3CheckCallRaiseDone = 0 - self.street4CheckCallRaiseChance = 0 - self.street4CheckCallRaiseDone = 0 - self.hands = {} self.handsplayers = {} @@ -106,6 +36,9 @@ class DerivedStats(): print "hands =", self.hands print "handsplayers =", self.handsplayers + def getHands(self): + return self.hands + def assembleHands(self, hand): self.hands['tableName'] = hand.tablename self.hands['siteHandNo'] = hand.handid @@ -114,36 +47,790 @@ class DerivedStats(): self.hands['importTime'] = None self.hands['seats'] = self.countPlayers(hand) self.hands['maxSeats'] = hand.maxseats - self.hands['boardcard1'] = None - self.hands['boardcard2'] = None - self.hands['boardcard3'] = None - self.hands['boardcard4'] = None - self.hands['boardcard5'] = None + self.hands['texture'] = None # No calculation done for this yet. - boardCard = 1 - for street in hand.communityStreets: - for card in hand.board[street]: - self.hands['boardcard%s' % str(boardCard)] = Card.encodeCard(card) - boardCard += 1 + # This (i think...) is correct for both stud and flop games, as hand.board['street'] disappears, and + # those values remain default in stud. + boardcards = hand.board['FLOP'] + hand.board['TURN'] + hand.board['RIVER'] + [u'0x', u'0x', u'0x', u'0x', u'0x'] + cards = [Card.encodeCard(c) for c in boardcards[0:5]] + self.hands['boardcard1'] = cards[0] + self.hands['boardcard2'] = cards[1] + self.hands['boardcard3'] = cards[2] + self.hands['boardcard4'] = cards[3] + self.hands['boardcard5'] = cards[4] + + #print "DEBUG: self.getStreetTotals = (%s, %s, %s, %s, %s)" % hand.getStreetTotals() + #FIXME: Pot size still in decimal, needs to be converted to cents + (self.hands['street1Pot'], + self.hands['street2Pot'], + self.hands['street3Pot'], + self.hands['street4Pot'], + self.hands['showdownPot']) = hand.getStreetTotals() + + self.vpip(hand) # Gives playersVpi (num of players vpip) + #print "DEBUG: vpip: %s" %(self.hands['playersVpi']) + self.playersAtStreetX(hand) # Gives playersAtStreet1..4 and Showdown + #print "DEBUG: playersAtStreet 1:'%s' 2:'%s' 3:'%s' 4:'%s'" %(self.hands['playersAtStreet1'],self.hands['playersAtStreet2'],self.hands['playersAtStreet3'],self.hands['playersAtStreet4']) + self.streetXRaises(hand) # Empty function currently + + # comment TEXT, + # commentTs DATETIME def assembleHandsPlayers(self, hand): self.vpip(self.hand) 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): +# #"""calculates data for the HUD during import. IMPORTANT: if you change this method make +# # sure to also change the following storage method and table_viewer.prepare_data if necessary +# #""" +# #print "generateHudCacheData, len(player_ids)=", len(player_ids) +# #setup subarrays of the result dictionary. +# 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=[] +# hudDataPositions=[] +# +# street0Calls=[] +# street1Calls=[] +# street2Calls=[] +# street3Calls=[] +# street4Calls=[] +# street0Bets=[] +# street1Bets=[] +# street2Bets=[] +# street3Bets=[] +# street4Bets=[] +# #street0Raises=[] +# #street1Raises=[] +# #street2Raises=[] +# #street3Raises=[] +# #street4Raises=[] +# +# # Summary figures for hand table: +# result={} +# result['playersVpi']=0 +# result['playersAtStreet1']=0 +# result['playersAtStreet2']=0 +# result['playersAtStreet3']=0 +# result['playersAtStreet4']=0 +# result['playersAtShowdown']=0 +# result['street0Raises']=0 +# result['street1Raises']=0 +# result['street2Raises']=0 +# result['street3Raises']=0 +# result['street4Raises']=0 +# result['street1Pot']=0 +# result['street2Pot']=0 +# result['street3Pot']=0 +# result['street4Pot']=0 +# result['showdownPot']=0 +# +# firstPfRaiseByNo=-1 +# firstPfRaiserId=-1 +# firstPfRaiserNo=-1 +# firstPfCallByNo=-1 +# firstPfCallerId=-1 +# +# for i, action in enumerate(actionTypeByNo[0]): +# if action[1] == "bet": +# firstPfRaiseByNo = i +# firstPfRaiserId = action[0] +# for j, pid in enumerate(player_ids): +# if pid == firstPfRaiserId: +# firstPfRaiserNo = j +# break +# break +# for i, action in enumerate(actionTypeByNo[0]): +# if action[1] == "call": +# firstPfCallByNo = i +# firstPfCallerId = action[0] +# break +# firstPlayId = firstPfCallerId +# if firstPfRaiseByNo <> -1: +# if firstPfRaiseByNo < firstPfCallByNo or firstPfCallByNo == -1: +# firstPlayId = firstPfRaiserId +# +# +# cutoffId=-1 +# buttonId=-1 +# sbId=-1 +# bbId=-1 +# if base=="hold": +# for player, pos in enumerate(positions): +# if pos == 1: +# cutoffId = player_ids[player] +# if pos == 0: +# buttonId = player_ids[player] +# if pos == 'S': +# sbId = player_ids[player] +# if pos == 'B': +# bbId = player_ids[player] +# +# someoneStole=False +# +# #run a loop for each player preparing the actual values that will be commited to SQL +# for player in xrange(len(player_ids)): +# #set default values +# myStreet0VPI=False +# myStreet0Aggr=False +# myStreet0_3BChance=False +# myStreet0_3BDone=False +# myStreet1Seen=False +# myStreet2Seen=False +# myStreet3Seen=False +# myStreet4Seen=False +# mySawShowdown=False +# myStreet1Aggr=False +# myStreet2Aggr=False +# myStreet3Aggr=False +# myStreet4Aggr=False +# myOtherRaisedStreet1=False +# myOtherRaisedStreet2=False +# myOtherRaisedStreet3=False +# myOtherRaisedStreet4=False +# myFoldToOtherRaisedStreet1=False +# myFoldToOtherRaisedStreet2=False +# myFoldToOtherRaisedStreet3=False +# myFoldToOtherRaisedStreet4=False +# myWonWhenSeenStreet1=0.0 +# myWonAtSD=0.0 +# myStealAttemptChance=False +# myStealAttempted=False +# myStreet0Calls=0 +# myStreet1Calls=0 +# myStreet2Calls=0 +# myStreet3Calls=0 +# myStreet4Calls=0 +# myStreet0Bets=0 +# myStreet1Bets=0 +# myStreet2Bets=0 +# myStreet3Bets=0 +# myStreet4Bets=0 +# #myStreet0Raises=0 +# #myStreet1Raises=0 +# #myStreet2Raises=0 +# #myStreet3Raises=0 +# #myStreet4Raises=0 +# +# #calculate VPIP and PFR +# street=0 +# heroPfRaiseCount=0 +# for currentAction in action_types[street][player]: # finally individual actions +# if currentAction == "bet": +# myStreet0Aggr = True +# if currentAction == "bet" or currentAction == "call": +# myStreet0VPI = True +# +# if myStreet0VPI: +# result['playersVpi'] += 1 +# myStreet0Calls = action_types[street][player].count('call') +# myStreet0Bets = action_types[street][player].count('bet') +# # street0Raises = action_types[street][player].count('raise') bet count includes raises for now +# result['street0Raises'] += myStreet0Bets +# +# #PF3BChance and PF3B +# pfFold=-1 +# pfRaise=-1 +# if firstPfRaiseByNo != -1: +# for i, actionType in enumerate(actionTypeByNo[0]): +# if actionType[0] == player_ids[player]: +# if actionType[1] == "bet" and pfRaise == -1 and i > firstPfRaiseByNo: +# pfRaise = i +# if actionType[1] == "fold" and pfFold == -1: +# pfFold = i +# if pfFold == -1 or pfFold > firstPfRaiseByNo: +# myStreet0_3BChance = True +# if pfRaise > firstPfRaiseByNo: +# myStreet0_3BDone = True +# +# #steal calculations +# if base=="hold": +# if len(player_ids)>=3: # no point otherwise # was 5, use 3 to match pokertracker definition +# if positions[player]==1: +# if firstPfRaiserId==player_ids[player] \ +# and (firstPfCallByNo==-1 or firstPfCallByNo>firstPfRaiseByNo): +# myStealAttempted=True +# myStealAttemptChance=True +# if firstPlayId==cutoffId or firstPlayId==buttonId or firstPlayId==sbId or firstPlayId==bbId or firstPlayId==-1: +# myStealAttemptChance=True +# if positions[player]==0: +# if firstPfRaiserId==player_ids[player] \ +# and (firstPfCallByNo==-1 or firstPfCallByNo>firstPfRaiseByNo): +# myStealAttempted=True +# myStealAttemptChance=True +# if firstPlayId==buttonId or firstPlayId==sbId or firstPlayId==bbId or firstPlayId==-1: +# myStealAttemptChance=True +# if positions[player]=='S': +# if firstPfRaiserId==player_ids[player] \ +# and (firstPfCallByNo==-1 or firstPfCallByNo>firstPfRaiseByNo): +# myStealAttempted=True +# myStealAttemptChance=True +# if firstPlayId==sbId or firstPlayId==bbId or firstPlayId==-1: +# myStealAttemptChance=True +# if positions[player]=='B': +# pass +# +# if myStealAttempted: +# someoneStole=True +# +# +# #calculate saw* values +# isAllIn = False +# if any(i for i in allIns[0][player]): +# isAllIn = True +# if (len(action_types[1][player])>0 or isAllIn): +# myStreet1Seen = True +# +# if any(i for i in allIns[1][player]): +# isAllIn = True +# if (len(action_types[2][player])>0 or isAllIn): +# myStreet2Seen = True +# +# if any(i for i in allIns[2][player]): +# isAllIn = True +# if (len(action_types[3][player])>0 or isAllIn): +# myStreet3Seen = True +# +# #print "base:", base +# if base=="hold": +# mySawShowdown = True +# if any(actiontype == "fold" for actiontype in action_types[3][player]): +# mySawShowdown = False +# else: +# #print "in else" +# if any(i for i in allIns[3][player]): +# isAllIn = True +# if (len(action_types[4][player])>0 or isAllIn): +# #print "in if" +# myStreet4Seen = True +# +# mySawShowdown = True +# if any(actiontype == "fold" for actiontype in action_types[4][player]): +# mySawShowdown = False +# +# if myStreet1Seen: +# result['playersAtStreet1'] += 1 +# if myStreet2Seen: +# result['playersAtStreet2'] += 1 +# if myStreet3Seen: +# result['playersAtStreet3'] += 1 +# if myStreet4Seen: +# result['playersAtStreet4'] += 1 +# if mySawShowdown: +# result['playersAtShowdown'] += 1 +# +# #flop stuff +# street=1 +# if myStreet1Seen: +# if any(actiontype == "bet" for actiontype in action_types[street][player]): +# myStreet1Aggr = True +# +# myStreet1Calls = action_types[street][player].count('call') +# myStreet1Bets = action_types[street][player].count('bet') +# # street1Raises = action_types[street][player].count('raise') bet count includes raises for now +# result['street1Raises'] += myStreet1Bets +# +# for otherPlayer in xrange(len(player_ids)): +# if player==otherPlayer: +# pass +# else: +# for countOther in xrange(len(action_types[street][otherPlayer])): +# if action_types[street][otherPlayer][countOther]=="bet": +# myOtherRaisedStreet1=True +# for countOtherFold in xrange(len(action_types[street][player])): +# if action_types[street][player][countOtherFold]=="fold": +# myFoldToOtherRaisedStreet1=True +# +# #turn stuff - copy of flop with different vars +# street=2 +# if myStreet2Seen: +# if any(actiontype == "bet" for actiontype in action_types[street][player]): +# myStreet2Aggr = True +# +# myStreet2Calls = action_types[street][player].count('call') +# myStreet2Bets = action_types[street][player].count('bet') +# # street2Raises = action_types[street][player].count('raise') bet count includes raises for now +# result['street2Raises'] += myStreet2Bets +# +# for otherPlayer in xrange(len(player_ids)): +# if player==otherPlayer: +# pass +# else: +# for countOther in xrange(len(action_types[street][otherPlayer])): +# if action_types[street][otherPlayer][countOther]=="bet": +# myOtherRaisedStreet2=True +# for countOtherFold in xrange(len(action_types[street][player])): +# if action_types[street][player][countOtherFold]=="fold": +# myFoldToOtherRaisedStreet2=True +# +# #river stuff - copy of flop with different vars +# street=3 +# if myStreet3Seen: +# if any(actiontype == "bet" for actiontype in action_types[street][player]): +# myStreet3Aggr = True +# +# myStreet3Calls = action_types[street][player].count('call') +# myStreet3Bets = action_types[street][player].count('bet') +# # street3Raises = action_types[street][player].count('raise') bet count includes raises for now +# result['street3Raises'] += myStreet3Bets +# +# for otherPlayer in xrange(len(player_ids)): +# if player==otherPlayer: +# pass +# else: +# for countOther in xrange(len(action_types[street][otherPlayer])): +# if action_types[street][otherPlayer][countOther]=="bet": +# myOtherRaisedStreet3=True +# for countOtherFold in xrange(len(action_types[street][player])): +# if action_types[street][player][countOtherFold]=="fold": +# myFoldToOtherRaisedStreet3=True +# +# #stud river stuff - copy of flop with different vars +# street=4 +# if myStreet4Seen: +# if any(actiontype == "bet" for actiontype in action_types[street][player]): +# myStreet4Aggr=True +# +# myStreet4Calls = action_types[street][player].count('call') +# myStreet4Bets = action_types[street][player].count('bet') +# # street4Raises = action_types[street][player].count('raise') bet count includes raises for now +# result['street4Raises'] += myStreet4Bets +# +# for otherPlayer in xrange(len(player_ids)): +# if player==otherPlayer: +# pass +# else: +# for countOther in xrange(len(action_types[street][otherPlayer])): +# if action_types[street][otherPlayer][countOther]=="bet": +# myOtherRaisedStreet4=True +# for countOtherFold in xrange(len(action_types[street][player])): +# if action_types[street][player][countOtherFold]=="fold": +# myFoldToOtherRaisedStreet4=True +# +# if winnings[player] != 0: +# if myStreet1Seen: +# myWonWhenSeenStreet1 = winnings[player] / float(totalWinnings) +# if mySawShowdown: +# myWonAtSD=myWonWhenSeenStreet1 +# +# #add each value to the appropriate array +# street0VPI.append(myStreet0VPI) +# street0Aggr.append(myStreet0Aggr) +# street0_3BChance.append(myStreet0_3BChance) +# street0_3BDone.append(myStreet0_3BDone) +# street1Seen.append(myStreet1Seen) +# street2Seen.append(myStreet2Seen) +# street3Seen.append(myStreet3Seen) +# street4Seen.append(myStreet4Seen) +# sawShowdown.append(mySawShowdown) +# street1Aggr.append(myStreet1Aggr) +# street2Aggr.append(myStreet2Aggr) +# street3Aggr.append(myStreet3Aggr) +# street4Aggr.append(myStreet4Aggr) +# otherRaisedStreet1.append(myOtherRaisedStreet1) +# otherRaisedStreet2.append(myOtherRaisedStreet2) +# otherRaisedStreet3.append(myOtherRaisedStreet3) +# otherRaisedStreet4.append(myOtherRaisedStreet4) +# foldToOtherRaisedStreet1.append(myFoldToOtherRaisedStreet1) +# foldToOtherRaisedStreet2.append(myFoldToOtherRaisedStreet2) +# foldToOtherRaisedStreet3.append(myFoldToOtherRaisedStreet3) +# foldToOtherRaisedStreet4.append(myFoldToOtherRaisedStreet4) +# wonWhenSeenStreet1.append(myWonWhenSeenStreet1) +# wonAtSD.append(myWonAtSD) +# stealAttemptChance.append(myStealAttemptChance) +# stealAttempted.append(myStealAttempted) +# if base=="hold": +# pos=positions[player] +# if pos=='B': +# hudDataPositions.append('B') +# elif pos=='S': +# hudDataPositions.append('S') +# elif pos==0: +# hudDataPositions.append('D') +# elif pos==1: +# hudDataPositions.append('C') +# elif pos>=2 and pos<=4: +# hudDataPositions.append('M') +# elif pos>=5 and pos<=8: +# hudDataPositions.append('E') +# ### RHH Added this elif to handle being a dead hand before the BB (pos==9) +# elif pos==9: +# hudDataPositions.append('X') +# else: +# raise FpdbError("invalid position") +# elif base=="stud": +# #todo: stud positions and steals +# pass +# +# street0Calls.append(myStreet0Calls) +# street1Calls.append(myStreet1Calls) +# street2Calls.append(myStreet2Calls) +# street3Calls.append(myStreet3Calls) +# street4Calls.append(myStreet4Calls) +# street0Bets.append(myStreet0Bets) +# street1Bets.append(myStreet1Bets) +# street2Bets.append(myStreet2Bets) +# street3Bets.append(myStreet3Bets) +# street4Bets.append(myStreet4Bets) +# #street0Raises.append(myStreet0Raises) +# #street1Raises.append(myStreet1Raises) +# #street2Raises.append(myStreet2Raises) +# #street3Raises.append(myStreet3Raises) +# #street4Raises.append(myStreet4Raises) +# +# #add each array to the to-be-returned dictionary +# result['street0VPI']=street0VPI +# result['street0Aggr']=street0Aggr +# result['street0_3BChance']=street0_3BChance +# result['street0_3BDone']=street0_3BDone +# result['street1Seen']=street1Seen +# result['street2Seen']=street2Seen +# result['street3Seen']=street3Seen +# result['street4Seen']=street4Seen +# result['sawShowdown']=sawShowdown +# +# result['street1Aggr']=street1Aggr +# result['otherRaisedStreet1']=otherRaisedStreet1 +# result['foldToOtherRaisedStreet1']=foldToOtherRaisedStreet1 +# result['street2Aggr']=street2Aggr +# result['otherRaisedStreet2']=otherRaisedStreet2 +# result['foldToOtherRaisedStreet2']=foldToOtherRaisedStreet2 +# result['street3Aggr']=street3Aggr +# result['otherRaisedStreet3']=otherRaisedStreet3 +# result['foldToOtherRaisedStreet3']=foldToOtherRaisedStreet3 +# result['street4Aggr']=street4Aggr +# result['otherRaisedStreet4']=otherRaisedStreet4 +# result['foldToOtherRaisedStreet4']=foldToOtherRaisedStreet4 +# result['wonWhenSeenStreet1']=wonWhenSeenStreet1 +# result['wonAtSD']=wonAtSD +# result['stealAttemptChance']=stealAttemptChance +# result['stealAttempted']=stealAttempted +# result['street0Calls']=street0Calls +# result['street1Calls']=street1Calls +# result['street2Calls']=street2Calls +# result['street3Calls']=street3Calls +# result['street4Calls']=street4Calls +# result['street0Bets']=street0Bets +# result['street1Bets']=street1Bets +# result['street2Bets']=street2Bets +# result['street3Bets']=street3Bets +# result['street4Bets']=street4Bets +# #result['street0Raises']=street0Raises +# #result['street1Raises']=street1Raises +# #result['street2Raises']=street2Raises +# #result['street3Raises']=street3Raises +# #result['street4Raises']=street4Raises +# +# #now the various steal values +# foldBbToStealChance=[] +# foldedBbToSteal=[] +# foldSbToStealChance=[] +# foldedSbToSteal=[] +# for player in xrange(len(player_ids)): +# myFoldBbToStealChance=False +# myFoldedBbToSteal=False +# myFoldSbToStealChance=False +# myFoldedSbToSteal=False +# +# if base=="hold": +# if someoneStole and (positions[player]=='B' or positions[player]=='S') and firstPfRaiserId!=player_ids[player]: +# street=0 +# for count in xrange(len(action_types[street][player])):#individual actions +# if positions[player]=='B': +# myFoldBbToStealChance=True +# if action_types[street][player][count]=="fold": +# myFoldedBbToSteal=True +# if positions[player]=='S': +# myFoldSbToStealChance=True +# if action_types[street][player][count]=="fold": +# myFoldedSbToSteal=True +# +# +# foldBbToStealChance.append(myFoldBbToStealChance) +# foldedBbToSteal.append(myFoldedBbToSteal) +# foldSbToStealChance.append(myFoldSbToStealChance) +# foldedSbToSteal.append(myFoldedSbToSteal) +# result['foldBbToStealChance']=foldBbToStealChance +# result['foldedBbToSteal']=foldedBbToSteal +# result['foldSbToStealChance']=foldSbToStealChance +# result['foldedSbToSteal']=foldedSbToSteal +# +# #now CB +# street1CBChance=[] +# street1CBDone=[] +# didStreet1CB=[] +# for player in xrange(len(player_ids)): +# myStreet1CBChance=False +# myStreet1CBDone=False +# +# if street0VPI[player]: +# myStreet1CBChance=True +# if street1Aggr[player]: +# myStreet1CBDone=True +# didStreet1CB.append(player_ids[player]) +# +# street1CBChance.append(myStreet1CBChance) +# street1CBDone.append(myStreet1CBDone) +# result['street1CBChance']=street1CBChance +# result['street1CBDone']=street1CBDone +# +# #now 2B +# street2CBChance=[] +# street2CBDone=[] +# didStreet2CB=[] +# for player in xrange(len(player_ids)): +# myStreet2CBChance=False +# myStreet2CBDone=False +# +# if street1CBDone[player]: +# myStreet2CBChance=True +# if street2Aggr[player]: +# myStreet2CBDone=True +# didStreet2CB.append(player_ids[player]) +# +# street2CBChance.append(myStreet2CBChance) +# street2CBDone.append(myStreet2CBDone) +# result['street2CBChance']=street2CBChance +# result['street2CBDone']=street2CBDone +# +# #now 3B +# street3CBChance=[] +# street3CBDone=[] +# didStreet3CB=[] +# for player in xrange(len(player_ids)): +# myStreet3CBChance=False +# myStreet3CBDone=False +# +# if street2CBDone[player]: +# myStreet3CBChance=True +# if street3Aggr[player]: +# myStreet3CBDone=True +# didStreet3CB.append(player_ids[player]) +# +# street3CBChance.append(myStreet3CBChance) +# street3CBDone.append(myStreet3CBDone) +# result['street3CBChance']=street3CBChance +# result['street3CBDone']=street3CBDone +# +# #and 4B +# street4CBChance=[] +# street4CBDone=[] +# didStreet4CB=[] +# for player in xrange(len(player_ids)): +# myStreet4CBChance=False +# myStreet4CBDone=False +# +# if street3CBDone[player]: +# myStreet4CBChance=True +# if street4Aggr[player]: +# myStreet4CBDone=True +# didStreet4CB.append(player_ids[player]) +# +# street4CBChance.append(myStreet4CBChance) +# street4CBDone.append(myStreet4CBDone) +# result['street4CBChance']=street4CBChance +# result['street4CBDone']=street4CBDone +# +# +# result['position']=hudDataPositions +# +# foldToStreet1CBChance=[] +# foldToStreet1CBDone=[] +# foldToStreet2CBChance=[] +# foldToStreet2CBDone=[] +# foldToStreet3CBChance=[] +# foldToStreet3CBDone=[] +# foldToStreet4CBChance=[] +# foldToStreet4CBDone=[] +# +# for player in xrange(len(player_ids)): +# myFoldToStreet1CBChance=False +# myFoldToStreet1CBDone=False +# foldToStreet1CBChance.append(myFoldToStreet1CBChance) +# foldToStreet1CBDone.append(myFoldToStreet1CBDone) +# +# myFoldToStreet2CBChance=False +# myFoldToStreet2CBDone=False +# foldToStreet2CBChance.append(myFoldToStreet2CBChance) +# foldToStreet2CBDone.append(myFoldToStreet2CBDone) +# +# myFoldToStreet3CBChance=False +# myFoldToStreet3CBDone=False +# foldToStreet3CBChance.append(myFoldToStreet3CBChance) +# foldToStreet3CBDone.append(myFoldToStreet3CBDone) +# +# myFoldToStreet4CBChance=False +# myFoldToStreet4CBDone=False +# foldToStreet4CBChance.append(myFoldToStreet4CBChance) +# foldToStreet4CBDone.append(myFoldToStreet4CBDone) +# +# if len(didStreet1CB)>=1: +# generateFoldToCB(1, player_ids, didStreet1CB, street1CBDone, foldToStreet1CBChance, foldToStreet1CBDone, actionTypeByNo) +# +# if len(didStreet2CB)>=1: +# generateFoldToCB(2, player_ids, didStreet2CB, street2CBDone, foldToStreet2CBChance, foldToStreet2CBDone, actionTypeByNo) +# +# if len(didStreet3CB)>=1: +# generateFoldToCB(3, player_ids, didStreet3CB, street3CBDone, foldToStreet3CBChance, foldToStreet3CBDone, actionTypeByNo) +# +# if len(didStreet4CB)>=1: +# generateFoldToCB(4, player_ids, didStreet4CB, street4CBDone, foldToStreet4CBChance, foldToStreet4CBDone, actionTypeByNo) +# +# result['foldToStreet1CBChance']=foldToStreet1CBChance +# result['foldToStreet1CBDone']=foldToStreet1CBDone +# result['foldToStreet2CBChance']=foldToStreet2CBChance +# result['foldToStreet2CBDone']=foldToStreet2CBDone +# result['foldToStreet3CBChance']=foldToStreet3CBChance +# result['foldToStreet3CBDone']=foldToStreet3CBDone +# result['foldToStreet4CBChance']=foldToStreet4CBChance +# result['foldToStreet4CBDone']=foldToStreet4CBDone +# +# +# totalProfit=[] +# +# street1CheckCallRaiseChance=[] +# street1CheckCallRaiseDone=[] +# street2CheckCallRaiseChance=[] +# street2CheckCallRaiseDone=[] +# street3CheckCallRaiseChance=[] +# street3CheckCallRaiseDone=[] +# street4CheckCallRaiseChance=[] +# street4CheckCallRaiseDone=[] +# #print "b4 totprof calc, len(playerIds)=", len(player_ids) +# for pl in xrange(len(player_ids)): +# #print "pl=", pl +# myTotalProfit=winnings[pl] # still need to deduct other costs +# if antes: +# myTotalProfit=winnings[pl] - antes[pl] +# for i in xrange(len(actionTypes)): #iterate through streets +# #for j in xrange(len(actionTypes[i])): #iterate through names (using pl loop above) +# for k in xrange(len(actionTypes[i][pl])): #iterate through individual actions of that player on that street +# myTotalProfit -= actionAmounts[i][pl][k] +# +# myStreet1CheckCallRaiseChance=False +# myStreet1CheckCallRaiseDone=False +# myStreet2CheckCallRaiseChance=False +# myStreet2CheckCallRaiseDone=False +# myStreet3CheckCallRaiseChance=False +# myStreet3CheckCallRaiseDone=False +# myStreet4CheckCallRaiseChance=False +# myStreet4CheckCallRaiseDone=False +# +# #print "myTotalProfit=", myTotalProfit +# totalProfit.append(myTotalProfit) +# #print "totalProfit[]=", totalProfit +# +# street1CheckCallRaiseChance.append(myStreet1CheckCallRaiseChance) +# street1CheckCallRaiseDone.append(myStreet1CheckCallRaiseDone) +# street2CheckCallRaiseChance.append(myStreet2CheckCallRaiseChance) +# street2CheckCallRaiseDone.append(myStreet2CheckCallRaiseDone) +# street3CheckCallRaiseChance.append(myStreet3CheckCallRaiseChance) +# street3CheckCallRaiseDone.append(myStreet3CheckCallRaiseDone) +# street4CheckCallRaiseChance.append(myStreet4CheckCallRaiseChance) +# street4CheckCallRaiseDone.append(myStreet4CheckCallRaiseDone) +# +# result['totalProfit']=totalProfit +# #print "res[totalProfit]=", result['totalProfit'] +# +# result['street1CheckCallRaiseChance']=street1CheckCallRaiseChance +# result['street1CheckCallRaiseDone']=street1CheckCallRaiseDone +# result['street2CheckCallRaiseChance']=street2CheckCallRaiseChance +# result['street2CheckCallRaiseDone']=street2CheckCallRaiseDone +# result['street3CheckCallRaiseChance']=street3CheckCallRaiseChance +# result['street3CheckCallRaiseDone']=street3CheckCallRaiseDone +# result['street4CheckCallRaiseChance']=street4CheckCallRaiseChance +# result['street4CheckCallRaiseDone']=street4CheckCallRaiseDone +# return result +# #end def generateHudCacheData + pass + def vpip(self, hand): vpipers = set() for act in hand.actions[hand.actionStreets[1]]: if act[1] in ('calls','bets', 'raises'): vpipers.add(act[0]) - for player in hand.players: - if player[1] in vpipers: - self.handsplayers[player[1]]['vpip'] = True - else: - self.handsplayers[player[1]]['vpip'] = False + #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) + 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 + + self.hands['playersAtStreet1'] = 0 + self.hands['playersAtStreet2'] = 0 + self.hands['playersAtStreet3'] = 0 + self.hands['playersAtStreet4'] = 0 + self.hands['playersAtShowdown'] = 0 + + for street in hand.actionStreets: + actors = {} + for act in hand.actions[street]: + actors[act[0]] = 1 + #print "len(actors.keys(%s)): %s" % ( street, len(actors.keys())) + if hand.gametype['base'] in ("hold"): + if street in "FLOP": self.hands['playersAtStreet1'] = len(actors.keys()) + elif street in "TURN": self.hands['playersAtStreet2'] = len(actors.keys()) + elif street in "RIVER": self.hands['playersAtStreet3'] = len(actors.keys()) + elif hand.gametype['base'] in ("stud"): + if street in "FOURTH": self.hands['playersAtStreet1'] = len(actors.keys()) + elif street in "FIFTH": self.hands['playersAtStreet2'] = len(actors.keys()) + elif street in "SIXTH": self.hands['playersAtStreet3'] = len(actors.keys()) + elif street in "SEVENTH": self.hands['playersAtStreet4'] = len(actors.keys()) + elif hand.gametype['base'] in ("draw"): + if street in "DRAWONE": self.hands['playersAtStreet1'] = len(actors.keys()) + elif street in "DRAWTWO": self.hands['playersAtStreet2'] = len(actors.keys()) + elif street in "DRAWTHREE": self.hands['playersAtStreet3'] = len(actors.keys()) + + #Need playersAtShowdown + + + def streetXRaises(self, hand): + # self.actions[street] is a list of all actions in a tuple, contining the action as the second element + # [ (player, action, ....), (player2, action, ...) ] + # No idea what this value is actually supposed to be + # In theory its "num small bets paid to see flop/street4, including blind" which makes sense for limit. Not so useful for nl + # Leaving empty for the moment, + self.hands['street0Raises'] = 0 # /* num small bets paid to see flop/street4, including blind */ + self.hands['street1Raises'] = 0 # /* num small bets paid to see turn/street5 */ + self.hands['street2Raises'] = 0 # /* num big bets paid to see river/street6 */ + self.hands['street3Raises'] = 0 # /* num big bets paid to see sd/street7 */ + self.hands['street4Raises'] = 0 # /* num big bets paid to see showdown */ + def aggr(self, hand, i): aggrers = set() for act in hand.actions[hand.actionStreets[i]]: @@ -157,4 +844,4 @@ class DerivedStats(): self.handsplayers[player[1]]['street%sAggr' % i] = False def countPlayers(self, hand): - pass \ No newline at end of file + pass diff --git a/pyfpdb/Exceptions.py b/pyfpdb/Exceptions.py index f3d75e89..eaf5d798 100644 --- a/pyfpdb/Exceptions.py +++ b/pyfpdb/Exceptions.py @@ -17,5 +17,8 @@ class FpdbParseError(FpdbError): class FpdbDatabaseError(FpdbError): pass +class FpdbMySQLFailedError(FpdbDatabaseError): + pass + class DuplicateError(FpdbError): pass diff --git a/pyfpdb/Filters.py b/pyfpdb/Filters.py index 9ded56b4..6ad242bb 100644 --- a/pyfpdb/Filters.py +++ b/pyfpdb/Filters.py @@ -23,6 +23,7 @@ import os import sys from optparse import OptionParser from time import * +import gobject #import pokereval import Configuration @@ -31,19 +32,20 @@ import FpdbSQLQueries class Filters(threading.Thread): def __init__(self, db, config, qdict, display = {}, debug=True): - self.debug=debug + # config and qdict are now redundant + self.debug = debug #print "start of GraphViewer constructor" - self.db=db - self.cursor=db.cursor - self.sql=qdict - self.conf = config + self.db = db + self.cursor = db.cursor + self.sql = db.sql + self.conf = db.config self.display = display self.sites = {} self.games = {} self.limits = {} self.seats = {} - self.groups = {} + self.groups = {} self.siteid = {} self.heroes = {} self.boxes = {} @@ -53,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:' + ,'limitsFL':'FL', 'limitsNL':'NL', 'ring':'Ring', 'tour':'Tourney' } # For use in date ranges. @@ -97,6 +100,11 @@ class Filters(threading.Thread): self.cbLimits = {} self.cbNoLimits = None self.cbAllLimits = None + self.cbFL = None + self.cbNL = None + self.rb = {} # radio buttons for ring/tour + self.type = None # ring/tour + self.types = {} # list of all ring/tour values self.fillLimitsFrame(vbox, self.display) limitsFrame.add(vbox) @@ -189,6 +197,9 @@ class Filters(threading.Thread): ltuple.append(l) return ltuple + def getType(self): + return(self.type) + def getSeats(self): if 'from' in self.sbSeats: self.seats['from'] = self.sbSeats['from'].get_value_as_int() @@ -228,7 +239,16 @@ class Filters(threading.Thread): pname.set_width_chars(20) hbox.pack_start(pname, False, True, 0) pname.connect("changed", self.__set_hero_name, site) - #TODO: Look at GtkCompletion - to fill out usernames + + # Added EntryCompletion but maybe comboBoxEntry is more flexible? (e.g. multiple choices) + completion = gtk.EntryCompletion() + pname.set_completion(completion) + liststore = gtk.ListStore(gobject.TYPE_STRING) + completion.set_model(liststore) + completion.set_text_column(0) + names = self.db.get_player_names(self.conf) # (config=self.conf, site_id=None, like_player_name="%") + for n in names: + liststore.append(n) self.__set_hero_name(pname, site) @@ -269,21 +289,99 @@ class Filters(threading.Thread): #print w.get_active() self.limits[limit] = w.get_active() print "self.limit[%s] set to %s" %(limit, self.limits[limit]) - if str(limit).isdigit(): + if limit.isdigit() or (len(limit) > 2 and limit[-2:] == 'nl'): if self.limits[limit]: if self.cbNoLimits != None: self.cbNoLimits.set_active(False) else: if self.cbAllLimits != None: self.cbAllLimits.set_active(False) + if not self.limits[limit]: + if limit.isdigit(): + self.cbFL.set_active(False) + else: + self.cbNL.set_active(False) elif limit == "all": if self.limits[limit]: - for cb in self.cbLimits.values(): - cb.set_active(True) + #for cb in self.cbLimits.values(): + # cb.set_active(True) + if self.cbFL != None: + self.cbFL.set_active(True) + if self.cbNL != None: + self.cbNL.set_active(True) elif limit == "none": if self.limits[limit]: for cb in self.cbLimits.values(): cb.set_active(False) + self.cbNL.set_active(False) + self.cbFL.set_active(False) + elif limit == "fl": + if not self.limits[limit]: + # only toggle all fl limits off if they are all currently on + # this stops turning one off from cascading into 'fl' box off + # and then all fl limits being turned off + all_fl_on = True + for cb in self.cbLimits.values(): + t = cb.get_children()[0].get_text() + if t.isdigit(): + if not cb.get_active(): + all_fl_on = False + found = {'ring':False, 'tour':False} + for cb in self.cbLimits.values(): + #print "cb label: ", cb.children()[0].get_text() + t = cb.get_children()[0].get_text() + if t.isdigit(): + if self.limits[limit] or all_fl_on: + cb.set_active(self.limits[limit]) + found[self.types[t]] = True + if self.limits[limit]: + if not found[self.type]: + if self.type == 'ring': + if 'tour' in self.rb: + self.rb['tour'].set_active(True) + elif self.type == 'tour': + if 'ring' in self.rb: + self.rb['ring'].set_active(True) + elif limit == "nl": + if not self.limits[limit]: + # only toggle all nl limits off if they are all currently on + # this stops turning one off from cascading into 'nl' box off + # and then all nl limits being turned off + all_nl_on = True + for cb in self.cbLimits.values(): + t = cb.get_children()[0].get_text() + if "nl" in t and len(t) > 2: + if not cb.get_active(): + all_nl_on = False + found = {'ring':False, 'tour':False} + for cb in self.cbLimits.values(): + t = cb.get_children()[0].get_text() + if "nl" in t and len(t) > 2: + if self.limits[limit] or all_nl_on: + cb.set_active(self.limits[limit]) + found[self.types[t]] = True + if self.limits[limit]: + if not found[self.type]: + if self.type == 'ring': + self.rb['tour'].set_active(True) + elif self.type == 'tour': + self.rb['ring'].set_active(True) + elif limit == "ring": + print "set", limit, "to", self.limits[limit] + if self.limits[limit]: + self.type = "ring" + for cb in self.cbLimits.values(): + #print "cb label: ", cb.children()[0].get_text() + if self.types[cb.get_children()[0].get_text()] == 'tour': + cb.set_active(False) + elif limit == "tour": + print "set", limit, "to", self.limits[limit] + if self.limits[limit]: + self.type = "tour" + for cb in self.cbLimits.values(): + #print "cb label: ", cb.children()[0].get_text() + if self.types[cb.get_children()[0].get_text()] == 'ring': + cb.set_active(False) def __set_seat_select(self, w, seat): #print "__set_seat_select: seat =", seat, "active =", w.get_active() @@ -328,22 +426,23 @@ class Filters(threading.Thread): print "INFO: No games returned from database" def fillLimitsFrame(self, vbox, display): - hbox = gtk.HBox(False, 0) - vbox.pack_start(hbox, False, False, 0) + top_hbox = gtk.HBox(False, 0) + vbox.pack_start(top_hbox, False, False, 0) lbl_title = gtk.Label(self.filterText['limitstitle']) lbl_title.set_alignment(xalign=0.0, yalign=0.5) - hbox.pack_start(lbl_title, expand=True, padding=3) + top_hbox.pack_start(lbl_title, expand=True, padding=3) showb = gtk.Button(label="hide", stock=None, use_underline=True) showb.set_alignment(xalign=1.0, yalign=0.5) showb.connect('clicked', self.__toggle_box, 'limits') - hbox.pack_start(showb, expand=False, padding=1) vbox1 = gtk.VBox(False, 0) vbox.pack_start(vbox1, False, False, 0) self.boxes['limits'] = vbox1 - self.cursor.execute(self.sql.query['getLimits']) + self.cursor.execute(self.sql.query['getLimits2']) + # selects limitType, bigBlind result = self.db.cursor.fetchall() + fl, nl = False, False if len(result) >= 1: hbox = gtk.HBox(True, 0) vbox1.pack_start(hbox, False, False, 0) @@ -351,26 +450,72 @@ class Filters(threading.Thread): hbox.pack_start(vbox2, False, False, 0) vbox3 = gtk.VBox(False, 0) hbox.pack_start(vbox3, False, False, 0) + found = {'nl':False, 'fl':False, 'ring':False, 'tour':False} for i, line in enumerate(result): + if "UseType" in self.display: + if line[0] != self.display["UseType"]: + continue hbox = gtk.HBox(False, 0) if i <= len(result)/2: vbox2.pack_start(hbox, False, False, 0) else: vbox3.pack_start(hbox, False, False, 0) - self.cbLimits[line[0]] = self.createLimitLine(hbox, line[0], line[0]) + if line[1] == 'fl': + name = str(line[2]) + found['fl'] = True + else: + name = str(line[2])+line[1] + found['nl'] = True + self.cbLimits[name] = self.createLimitLine(hbox, name, name) + self.types[name] = line[0] + found[line[0]] = True # type is ring/tour + self.type = line[0] # if only one type, set it now if "LimitSep" in display and display["LimitSep"] == True and len(result) >= 2: + hbox = gtk.HBox(True, 0) + vbox1.pack_start(hbox, False, False, 0) + vbox2 = gtk.VBox(False, 0) + hbox.pack_start(vbox2, False, False, 0) + vbox3 = gtk.VBox(False, 0) + hbox.pack_start(vbox3, False, False, 0) + hbox = gtk.HBox(False, 0) - vbox1.pack_start(hbox, False, True, 0) + vbox2.pack_start(hbox, False, False, 0) self.cbAllLimits = self.createLimitLine(hbox, 'all', self.filterText['limitsall']) hbox = gtk.HBox(False, 0) - vbox1.pack_start(hbox, False, True, 0) + vbox2.pack_start(hbox, False, False, 0) self.cbNoLimits = self.createLimitLine(hbox, 'none', self.filterText['limitsnone']) - hbox = gtk.HBox(False, 0) - vbox1.pack_start(hbox, False, True, 0) - cb = self.createLimitLine(hbox, 'show', self.filterText['limitsshow']) + + dest = vbox3 # for ring/tour buttons + if "LimitType" in display and display["LimitType"] == True and found['nl'] and found['fl']: + #if found['fl']: + hbox = gtk.HBox(False, 0) + vbox3.pack_start(hbox, False, False, 0) + self.cbFL = self.createLimitLine(hbox, 'fl', self.filterText['limitsFL']) + #if found['nl']: + hbox = gtk.HBox(False, 0) + vbox3.pack_start(hbox, False, False, 0) + self.cbNL = self.createLimitLine(hbox, 'nl', self.filterText['limitsNL']) + dest = vbox2 # for ring/tour buttons else: print "INFO: No games returned from database" + if "Type" in display and display["Type"] == True and found['ring'] and found['tour']: + rb1 = gtk.RadioButton(None, self.filterText['ring']) + rb1.connect('clicked', self.__set_limit_select, 'ring') + rb2 = gtk.RadioButton(rb1, self.filterText['tour']) + rb2.connect('clicked', self.__set_limit_select, 'tour') + top_hbox.pack_start(rb1, False, False, 0) # (child, expand, fill, padding) + top_hbox.pack_start(rb2, True, True, 0) # child uses expand space if fill is true + + self.rb['ring'] = rb1 + self.rb['tour'] = rb2 + #print "about to set ring to true" + rb1.set_active(True) + # set_active doesn't seem to call this for some reason so call manually: + self.__set_limit_select(rb1, 'ring') + self.type = 'ring' + top_hbox.pack_start(showb, expand=False, padding=1) + def fillSeatsFrame(self, vbox, display): hbox = gtk.HBox(False, 0) vbox.pack_start(hbox, False, False, 0) @@ -401,15 +546,6 @@ class Filters(threading.Thread): hbox.pack_start(lbl_to, expand=False, padding=3) hbox.pack_start(sb2, False, False, 0) - if "SeatSep" in display and display["SeatSep"] == True: - hbox = gtk.HBox(False, 0) - vbox1.pack_start(hbox, False, True, 0) - cb = gtk.CheckButton(self.filterText['seatsshow']) - cb.connect('clicked', self.__set_seat_select, 'show') - hbox.pack_start(cb, False, False, 0) - self.sbSeats['show'] = cb - self.seats['show'] = False - self.sbSeats['from'] = sb1 self.sbSeats['to'] = sb2 @@ -429,14 +565,26 @@ class Filters(threading.Thread): self.boxes['groups'] = vbox1 hbox = gtk.HBox(False, 0) - vbox1.pack_start(hbox, False, True, 0) + vbox1.pack_start(hbox, False, False, 0) + cb = self.createLimitLine(hbox, 'show', self.filterText['limitsshow']) + hbox = gtk.HBox(False, 0) + vbox1.pack_start(hbox, False, True, 0) cb = gtk.CheckButton(self.filterText['posnshow']) cb.connect('clicked', self.__set_group_select, 'posn') hbox.pack_start(cb, False, False, 0) self.sbGroups['posn'] = cb self.groups['posn'] = False + if "SeatSep" in display and display["SeatSep"] == True: + hbox = gtk.HBox(False, 0) + vbox1.pack_start(hbox, False, True, 0) + cb = gtk.CheckButton(self.filterText['seatsshow']) + cb.connect('clicked', self.__set_seat_select, 'show') + hbox.pack_start(cb, False, False, 0) + self.sbSeats['show'] = cb + self.seats['show'] = False + def fillCardsFrame(self, vbox): hbox1 = gtk.HBox(True,0) hbox1.show() diff --git a/pyfpdb/FulltiltToFpdb.py b/pyfpdb/FulltiltToFpdb.py old mode 100644 new mode 100755 index a74d71d3..5ce0a5c8 --- a/pyfpdb/FulltiltToFpdb.py +++ b/pyfpdb/FulltiltToFpdb.py @@ -40,6 +40,7 @@ class Fulltilt(HandHistoryConverter): (?P[.0-9]+)/ \$?(?P[.0-9]+)\s (Ante\s\$?(?P[.0-9]+)\s)?-\s + \$?(?P[.0-9]+\sCap\s)? (?P(No\sLimit|Pot\sLimit|Limit))?\s (?P(Hold\'em|Omaha\sHi|Omaha\sH/L|7\sCard\sStud|Stud\sH/L|Razz|Stud\sHi)) ''', re.VERBOSE) @@ -52,6 +53,7 @@ class Fulltilt(HandHistoryConverter): (?P[-\s\da-zA-Z]+)\s (\((?P.+)\)\s)?-\s \$?(?P[.0-9]+)/\$?(?P[.0-9]+)\s(Ante\s\$?(?P[.0-9]+)\s)?-\s + \$?(?P[.0-9]+\sCap\s)? (?P[a-zA-Z\/\'\s]+)\s-\s (?P\d+:\d+:\d+\s\w+\s-\s\d+/\d+/\d+)\s? (?P\(partial\))?\n @@ -143,6 +145,7 @@ class Fulltilt(HandHistoryConverter): return [["ring", "hold", "nl"], ["ring", "hold", "pl"], ["ring", "hold", "fl"], + ["ring", "hold", "cn"], ["ring", "stud", "fl"], @@ -175,7 +178,10 @@ class Fulltilt(HandHistoryConverter): 'Stud H/L' : ('stud','studhilo') } currencies = { u' €':'EUR', '$':'USD', '':'T$' } - info['limitType'] = limits[mg['LIMIT']] + if mg['CAP']: + info['limitType'] = 'cn' + else: + info['limitType'] = limits[mg['LIMIT']] info['sb'] = mg['SB'] info['bb'] = mg['BB'] if mg['GAME'] != None: diff --git a/pyfpdb/GuiGraphViewer.py b/pyfpdb/GuiGraphViewer.py index f5beadcf..f91c9870 100644 --- a/pyfpdb/GuiGraphViewer.py +++ b/pyfpdb/GuiGraphViewer.py @@ -53,20 +53,26 @@ class GuiGraphViewer (threading.Thread): self.db = Database.Database(self.conf, sql=self.sql) - filters_display = { "Heroes" : True, - "Sites" : True, - "Games" : True, - "Limits" : True, - "Seats" : False, - "Dates" : True, - "Button1" : True, - "Button2" : True + filters_display = { "Heroes" : True, + "Sites" : True, + "Games" : True, + "Limits" : True, + "LimitSep" : True, + "LimitType" : True, + "Type" : False, + "UseType" : 'ring', + "Seats" : False, + "SeatSep" : False, + "Dates" : True, + "Groups" : False, + "Button1" : True, + "Button2" : True } self.filters = Filters.Filters(self.db, self.conf, self.sql, display = filters_display) - self.filters.registerButton1Name("Refresh Graph") + self.filters.registerButton1Name("Refresh _Graph") self.filters.registerButton1Callback(self.generateGraph) - self.filters.registerButton2Name("Export to File") + self.filters.registerButton2Name("_Export to File") self.filters.registerButton2Callback(self.exportGraph) self.mainHBox = gtk.HBox(False, 0) @@ -146,10 +152,8 @@ class GuiGraphViewer (threading.Thread): raise def generateGraph(self, widget, data): - print "generateGraph: start" try: self.clearGraphData() - print "after cleardata" sitenos = [] playerids = [] @@ -158,15 +162,18 @@ class GuiGraphViewer (threading.Thread): heroes = self.filters.getHeroes() siteids = self.filters.getSiteIds() limits = self.filters.getLimits() - print "got filter data" + for i in ('show', 'none'): + if i in limits: + limits.remove(i) # Which sites are selected? for site in sites: if sites[site] == True: sitenos.append(siteids[site]) - self.db.cursor.execute(self.sql.query['getPlayerId'], (heroes[site],)) - result = self.db.cursor.fetchall() + c = self.db.get_cursor() + c.execute(self.sql.query['getPlayerId'], (heroes[site],)) + result = c.fetchall() if len(result) == 1: - playerids.append(result[0][0]) + playerids.append( int(result[0][0]) ) if not sitenos: #Should probably pop up here. @@ -182,12 +189,10 @@ class GuiGraphViewer (threading.Thread): return #Set graph properties - print "add_subplot" self.ax = self.fig.add_subplot(111) #Get graph data from DB starttime = time() - print "get line: playerids =", playerids, "sitenos =", sitenos, "limits =", limits line = self.getRingProfitGraph(playerids, sitenos, limits) print "Graph generated in: %s" %(time() - starttime) @@ -234,12 +239,31 @@ class GuiGraphViewer (threading.Thread): # [5L] into (5) not (5,) and [5L, 2829L] into (5, 2829) nametest = str(tuple(names)) sitetest = str(tuple(sites)) - limittest = str(tuple(limits)) - nametest = nametest.replace("L", "") - nametest = nametest.replace(",)",")") - sitetest = sitetest.replace(",)",")") - limittest = limittest.replace("L", "") - limittest = limittest.replace(",)",")") + #nametest = nametest.replace("L", "") + + lims = [int(x) for x in limits if x.isdigit()] + nolims = [int(x[0:-2]) for x in limits if len(x) > 2 and x[-2:] == 'nl'] + limittest = "and ( (gt.limitType = 'fl' and gt.bigBlind in " + # and ( (limit and bb in()) or (nolimit and bb in ()) ) + if lims: + blindtest = str(tuple(lims)) + blindtest = blindtest.replace("L", "") + blindtest = blindtest.replace(",)",")") + limittest = limittest + blindtest + ' ) ' + else: + limittest = limittest + '(-1) ) ' + limittest = limittest + " or (gt.limitType = 'nl' and gt.bigBlind in " + if nolims: + blindtest = str(tuple(nolims)) + blindtest = blindtest.replace("L", "") + blindtest = blindtest.replace(",)",")") + limittest = limittest + blindtest + ' ) )' + else: + limittest = limittest + '(-1) ) )' + if type == 'ring': + limittest = limittest + " and gt.type = 'ring' " + elif type == 'tour': + limittest = limittest + " and gt.type = 'tour' " #Must be a nicer way to deal with tuples of size 1 ie. (2,) - which makes sql barf tmp = tmp.replace("", nametest) @@ -247,6 +271,7 @@ class GuiGraphViewer (threading.Thread): tmp = tmp.replace("", start_date) tmp = tmp.replace("", end_date) tmp = tmp.replace("", limittest) + tmp = tmp.replace(",)", ")") #print "DEBUG: sql query:" #print tmp @@ -255,10 +280,10 @@ class GuiGraphViewer (threading.Thread): winnings = self.db.cursor.fetchall() self.db.rollback() - if(winnings == ()): + if winnings == (): return None - y=map(lambda x:float(x[3]), winnings) + y = map(lambda x:float(x[1]), winnings) line = cumsum(y) return line/100 #end of def getRingProfitGraph diff --git a/pyfpdb/GuiPlayerStats.py b/pyfpdb/GuiPlayerStats.py index add46bef..4b82c861 100644 --- a/pyfpdb/GuiPlayerStats.py +++ b/pyfpdb/GuiPlayerStats.py @@ -54,17 +54,19 @@ class GuiPlayerStats (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" : True, - "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, + "Button1" : True, + "Button2" : True } self.filters = Filters.Filters(self.db, self.conf, self.sql, display = filters_display) @@ -157,6 +159,7 @@ class GuiPlayerStats (threading.Thread): heroes = self.filters.getHeroes() siteids = self.filters.getSiteIds() limits = self.filters.getLimits() + type = self.filters.getType() seats = self.filters.getSeats() groups = self.filters.getGroups() dates = self.filters.getDates() @@ -185,16 +188,16 @@ class GuiPlayerStats (threading.Thread): print "No limits found" return - self.createStatsTable(vbox, playerids, sitenos, limits, seats, groups, dates) + self.createStatsTable(vbox, playerids, sitenos, limits, type, seats, groups, dates) - def createStatsTable(self, vbox, playerids, sitenos, limits, seats, groups, dates): + def createStatsTable(self, vbox, playerids, sitenos, limits, type, seats, groups, dates): starttime = time() # 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, seats, groups, dates) + self.addTable(vbox, 'playerDetailedStats', flags, playerids, sitenos, limits, type, seats, groups, dates) # Separator sep = gtk.HSeparator() @@ -217,13 +220,13 @@ class GuiPlayerStats (threading.Thread): # Detailed table flags = [True] - self.addTable(vbox1, 'playerDetailedStats', flags, playerids, sitenos, limits, seats, groups, dates) + self.addTable(vbox1, '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 addTable(self, vbox, query, flags, playerids, sitenos, limits, seats, groups, dates): + def addTable(self, vbox, query, flags, playerids, sitenos, limits, type, seats, groups, dates): row = 0 sqlrow = 0 colalias,colshow,colheading,colxalign,colformat = 0,1,2,3,4 @@ -231,7 +234,7 @@ class GuiPlayerStats (threading.Thread): else: holecards = flags[0] tmp = self.sql.query[query] - tmp = self.refineQuery(tmp, flags, playerids, sitenos, limits, seats, groups, dates) + tmp = self.refineQuery(tmp, flags, playerids, sitenos, limits, type, seats, groups, dates) self.cursor.execute(tmp) result = self.cursor.fetchall() colnames = [desc[0].lower() for desc in self.cursor.description] @@ -317,9 +320,9 @@ class GuiPlayerStats (threading.Thread): row += 1 vbox.show_all() - #end def addTable(self, query, vars, playerids, sitenos, limits, seats): + #end def addTable(self, query, vars, playerids, sitenos, limits, type, seats, groups, dates): - def refineQuery(self, query, flags, playerids, sitenos, limits, seats, groups, dates): + def refineQuery(self, query, flags, playerids, sitenos, limits, type, seats, groups, dates): if not flags: holecards = False else: holecards = flags[0] @@ -344,13 +347,30 @@ class GuiPlayerStats (threading.Thread): query = query.replace('', '') query = query.replace('', '') - if [x for x in limits if str(x).isdigit()]: - blindtest = str(tuple([x for x in limits if str(x).isdigit()])) + lims = [int(x) for x in limits if x.isdigit()] + nolims = [int(x[0:-2]) for x in limits if len(x) > 2 and x[-2:] == 'nl'] + bbtest = "and ( (gt.limitType = 'fl' and gt.bigBlind in " + # and ( (limit and bb in()) or (nolimit and bb in ()) ) + if lims: + blindtest = str(tuple(lims)) blindtest = blindtest.replace("L", "") blindtest = blindtest.replace(",)",")") - query = query.replace("", " and gt.bigBlind in " + blindtest + " ") + bbtest = bbtest + blindtest + ' ) ' else: - query = query.replace("", "") + bbtest = bbtest + '(-1) ) ' + bbtest = bbtest + " or (gt.limitType = 'nl' and gt.bigBlind in " + if nolims: + blindtest = str(tuple(nolims)) + blindtest = blindtest.replace("L", "") + blindtest = blindtest.replace(",)",")") + bbtest = bbtest + blindtest + ' ) )' + else: + bbtest = bbtest + '(-1) ) )' + if type == 'ring': + bbtest = bbtest + " and gt.type = 'ring' " + elif type == 'tour': + bbtest = bbtest + " and gt.type = 'tour' " + query = query.replace("", bbtest) if holecards: # pinch level variables for hole card query query = query.replace("", "hp.startcards") diff --git a/pyfpdb/HUD_config.xml.example b/pyfpdb/HUD_config.xml.example index de2f1bba..e4344beb 100644 --- a/pyfpdb/HUD_config.xml.example +++ b/pyfpdb/HUD_config.xml.example @@ -4,8 +4,96 @@ - + + diff --git a/pyfpdb/HUD_main.py b/pyfpdb/HUD_main.py index b1164b26..95a25a29 100755 --- a/pyfpdb/HUD_main.py +++ b/pyfpdb/HUD_main.py @@ -56,23 +56,32 @@ import Database import Tables import Hud -# To add to config: -aggregate_stats = {"ring": False, "tour": False} # uses agg_bb_mult -hud_style = 'A' # A=All-time - # S=Session - # T=timed (last n days - set hud_days to required value) - # Future values may also include: - # H=Hands (last n hands) -hud_days = 90 # Max number of days from each player to use for hud stats -agg_bb_mult = 100 # 1 = no aggregation. When aggregating stats across levels larger blinds - # must be < (agg_bb_mult * smaller blinds) to be aggregated - # ie. 100 will aggregate almost everything, 2 will probably agg just the - # next higher and lower levels into the current one, try 3/10/30/100 -hud_session_gap = 30 # Gap (minutes) between hands that indicates a change of session - # (hands every 2 mins for 1 hour = one session, if followed - # by a 40 minute gap and then more hands on same table that is - # a new session) -#hud_hands = 0 # Max number of hands from each player to use for hud stats (not used) + +# 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 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 +# - 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) +# - Set hud_days to N to see stats for the last N days in the HUD (only applies if hud_style is T) +def_hud_params = { # Settings for all players apart from program owner ('hero') + 'aggregate_ring' : False + , 'aggregate_tour' : True + , 'hud_style' : 'A' + , 'hud_days' : 90 + , 'agg_bb_mult' : 1 # 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_session_gap' : 30 not currently used + } + class HUD_main(object): """A main() object to own both the read_stdin thread and the gui.""" @@ -82,6 +91,7 @@ class HUD_main(object): self.db_name = db_name self.config = Configuration.Config(file=options.config, dbname=options.dbname) self.hud_dict = {} + self.hud_params = self.config.get_hud_ui_parameters() # a thread to read stdin gobject.threads_init() # this is required @@ -104,12 +114,17 @@ class HUD_main(object): # called by an event in the HUD, to kill this specific HUD if table in self.hud_dict: self.hud_dict[table].kill() - self.hud_dict[table].main_window.destroy() + try: + # throws exception in windows sometimes (when closing using main_window menu?) + self.hud_dict[table].main_window.destroy() + except: + pass self.vb.remove(self.hud_dict[table].tablehudlabel) del(self.hud_dict[table]) self.main_window.resize(1,1) - def create_HUD(self, new_hand_id, table, table_name, max, poker_game, stat_dict, cards): + def create_HUD(self, new_hand_id, table, table_name, max, poker_game, type, stat_dict, cards): + """type is "ring" or "tour" used to set hud_params""" def idle_func(): @@ -135,6 +150,18 @@ class HUD_main(object): self.hud_dict[table_name].table_name = table_name self.hud_dict[table_name].stat_dict = stat_dict self.hud_dict[table_name].cards = cards + + if type == "tour" and self.hud_params['aggregate_tour'] == False: + self.hud_dict[table_name].hud_params['agg_bb_mult'] = 1 + elif type == "ring" and self.hud_params['aggregate_ring'] == False: + self.hud_dict[table_name].hud_params['agg_bb_mult'] = 1 + if type == "tour" and self.hud_params['h_aggregate_tour'] == False: + self.hud_dict[table_name].hud_params['h_agg_bb_mult'] = 1 + elif type == "ring" and self.hud_params['h_aggregate_ring'] == False: + self.hud_dict[table_name].hud_params['h_agg_bb_mult'] = 1 + self.hud_params['aggregate_ring'] == True + self.hud_params['h_aggregate_ring'] == True + [aw.update_data(new_hand_id, self.db_connection) for aw in self.hud_dict[table_name].aux_windows] gobject.idle_add(idle_func) @@ -160,68 +187,99 @@ class HUD_main(object): # be passed to HUDs for use in the gui thread. HUD objects should not # need their own access to the database, but should open their own # if it is required. - self.db_connection = Database.Database(self.config) - self.db_connection.init_hud_stat_vars(hud_days) - tourny_finder = re.compile('(\d+) (\d+)') - - while 1: # wait for a new hand number on stdin - new_hand_id = sys.stdin.readline() - new_hand_id = string.rstrip(new_hand_id) - if new_hand_id == "": # blank line means quit - self.destroy() - break # this thread is not always killed immediately with gtk.main_quit() -# 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) = 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) + try: + self.db_connection = Database.Database(self.config) + tourny_finder = re.compile('(\d+) (\d+)') + +# get hero's screen names and player ids + self.hero, self.hero_ids = {}, {} + for site in self.config.get_supported_sites(): + result = self.db_connection.get_site_id(site) + if result: + site_id = result[0][0] + self.hero[site_id] = self.config.supported_sites[site].screen_name + self.hero_ids[site_id] = self.db_connection.get_player_id(self.config, site, self.hero[site_id]) + + while 1: # wait for a new hand number on stdin + new_hand_id = sys.stdin.readline() + new_hand_id = string.rstrip(new_hand_id) + if new_hand_id == "": # blank line means quit + self.destroy() + break # this thread is not always killed immediately with gtk.main_quit() +# 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) - cards = self.db_connection.get_cards(new_hand_id) - comm_cards = self.db_connection.get_common_cards(new_hand_id) - if comm_cards != {}: # stud! - cards['common'] = comm_cards['common'] - except Exception, err: - err = traceback.extract_tb(sys.exc_info()[2])[-1] - print "db error: skipping "+str(new_hand_id)+" "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1]) - if new_hand_id: # new_hand_id is none if we had an error prior to the store - sys.stderr.write("Database error %s in hand %d. Skipping.\n" % (err, int(new_hand_id))) - continue - - if type == "tour": # hand is from a tournament - mat_obj = tourny_finder.search(table_name) - if mat_obj: - (tour_number, tab_number) = mat_obj.group(1, 2) - temp_key = tour_number - else: # tourney, but can't get number and table - print "could not find tournament: skipping " - #sys.stderr.write("Could not find tournament %d in hand %d. Skipping.\n" % (int(tour_number), int(new_hand_id))) + cards = self.db_connection.get_cards(new_hand_id) + comm_cards = self.db_connection.get_common_cards(new_hand_id) + if comm_cards != {}: # stud! + cards['common'] = comm_cards['common'] + except Exception, err: + err = traceback.extract_tb(sys.exc_info()[2])[-1] + print "db error: skipping "+str(new_hand_id)+" "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1]) + if new_hand_id: # new_hand_id is none if we had an error prior to the store + sys.stderr.write("Database error %s in hand %d. Skipping.\n" % (err, int(new_hand_id))) continue - - else: - temp_key = table_name -# Update an existing HUD - if temp_key in self.hud_dict: - self.hud_dict[temp_key].stat_dict = stat_dict - self.hud_dict[temp_key].cards = cards - [aw.update_data(new_hand_id, self.db_connection) for aw in self.hud_dict[temp_key].aux_windows] - self.update_HUD(new_hand_id, temp_key, self.config) - -# Or create a new HUD - else: - if type == "tour": - tablewindow = Tables.discover_tournament_table(self.config, tour_number, tab_number) + if type == "tour": # hand is from a tournament + mat_obj = tourny_finder.search(table_name) + if mat_obj: + (tour_number, tab_number) = mat_obj.group(1, 2) + temp_key = tour_number + else: # tourney, but can't get number and table + print "could not find tournament: skipping " + #sys.stderr.write("Could not find tournament %d in hand %d. Skipping.\n" % (int(tour_number), int(new_hand_id))) + continue + else: - tablewindow = Tables.discover_table_by_name(self.config, table_name) - if tablewindow == None: -# If no client window is found on the screen, complain and continue + temp_key = table_name + +# Update an existing HUD + if temp_key in self.hud_dict: + try: + # get stats using hud's specific params + 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]) + except: + err = traceback.extract_tb(sys.exc_info()[2])[-1] + print "db get_stats error: skipping "+str(new_hand_id)+" "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1]) + if new_hand_id: # new_hand_id is none if we had an error prior to the store + sys.stderr.write("Database get_stats error %s in hand %d. Skipping.\n" % (err, int(new_hand_id))) + continue + self.hud_dict[temp_key].stat_dict = stat_dict + self.hud_dict[temp_key].cards = cards + [aw.update_data(new_hand_id, self.db_connection) for aw in self.hud_dict[temp_key].aux_windows] + self.update_HUD(new_hand_id, temp_key, self.config) + +# Or create a new HUD + else: + try: + # get stats using default params + 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]) + except: + err = traceback.extract_tb(sys.exc_info()[2])[-1] + print "db get_stats error: skipping "+str(new_hand_id)+" "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1]) + if new_hand_id: # new_hand_id is none if we had an error prior to the store + sys.stderr.write("Database get_stats error %s in hand %d. Skipping.\n" % (err, int(new_hand_id))) + continue if type == "tour": - table_name = "%s %s" % (tour_number, tab_number) - sys.stderr.write("table name "+table_name+" not found, skipping.\n") - else: - self.create_HUD(new_hand_id, tablewindow, temp_key, max, poker_game, stat_dict, cards) - self.db_connection.connection.rollback() + tablewindow = Tables.discover_tournament_table(self.config, tour_number, tab_number) + else: + tablewindow = Tables.discover_table_by_name(self.config, table_name) + if tablewindow == None: +# If no client window is found on the screen, complain and continue + if type == "tour": + table_name = "%s %s" % (tour_number, tab_number) + sys.stderr.write("table name "+table_name+" not found, skipping.\n") + else: + self.create_HUD(new_hand_id, tablewindow, temp_key, max, poker_game, type, stat_dict, cards) + self.db_connection.connection.rollback() + except: + err = traceback.extract_tb(sys.exc_info()[2])[-1] + print "***Error: "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1]) if __name__== "__main__": diff --git a/pyfpdb/Hand.py b/pyfpdb/Hand.py index 61533e63..2527f636 100644 --- a/pyfpdb/Hand.py +++ b/pyfpdb/Hand.py @@ -194,64 +194,32 @@ dealt whether they were seen in a 'dealt to' line """ Function to insert Hand into database Should not commit, and do minimal selects. Callers may want to cache commits db: a connected fpdb_db object""" - # TODO: + + ##### + # Players, Gametypes, TourneyTypes are all shared functions that are needed for additional tables + # These functions are intended for prep insert eventually + ##### # Players - base playerid and siteid tuple sqlids = db.getSqlPlayerIDs([p[1] for p in self.players], self.siteId) #Gametypes gtid = db.getGameTypeId(self.siteId, self.gametype) + self.stats.assembleHands(self) + + ##### + # End prep functions + ##### + # HudCache data to come from DerivedStats class # HandsActions - all actions for all players for all streets - self.actions + # Hands - Summary information of hand indexed by handId - gameinfo - #This should be moved to prepInsert - hh = {} - hh['siteHandNo'] = self.handid - hh['handStart'] = self.starttime + hh = self.stats.getHands() hh['gameTypeId'] = gtid # seats TINYINT NOT NULL, - hh['tableName'] = self.tablename - hh['maxSeats'] = self.maxseats hh['seats'] = len(sqlids) - # Flop turn and river may all be empty - add (likely) too many elements and trim with range - boardcards = self.board['FLOP'] + self.board['TURN'] + self.board['RIVER'] + [u'0x', u'0x', u'0x', u'0x', u'0x'] - cards = [Card.encodeCard(c) for c in boardcards[0:5]] - hh['boardcard1'] = cards[0] - hh['boardcard2'] = cards[1] - hh['boardcard3'] = cards[2] - hh['boardcard4'] = cards[3] - hh['boardcard5'] = cards[4] - # texture smallint, - # playersVpi SMALLINT NOT NULL, /* num of players vpi */ - # Needs to be recorded - # playersAtStreet1 SMALLINT NOT NULL, /* num of players seeing flop/street4 */ - # Needs to be recorded - # playersAtStreet2 SMALLINT NOT NULL, - # Needs to be recorded - # playersAtStreet3 SMALLINT NOT NULL, - # Needs to be recorded - # playersAtStreet4 SMALLINT NOT NULL, - # Needs to be recorded - # playersAtShowdown SMALLINT NOT NULL, - # Needs to be recorded - # street0Raises TINYINT NOT NULL, /* num small bets paid to see flop/street4, including blind */ - # Needs to be recorded - # street1Raises TINYINT NOT NULL, /* num small bets paid to see turn/street5 */ - # Needs to be recorded - # street2Raises TINYINT NOT NULL, /* num big bets paid to see river/street6 */ - # Needs to be recorded - # street3Raises TINYINT NOT NULL, /* num big bets paid to see sd/street7 */ - # Needs to be recorded - # street4Raises TINYINT NOT NULL, /* num big bets paid to see showdown */ - # Needs to be recorded - - #print "DEBUG: self.getStreetTotals = (%s, %s, %s, %s, %s)" % self.getStreetTotals() - #FIXME: Pot size still in decimal, needs to be converted to cents - (hh['street1Pot'], hh['street2Pot'], hh['street3Pot'], hh['street4Pot'], hh['showdownPot']) = self.getStreetTotals() - - # comment TEXT, - # commentTs DATETIME #print hh handid = db.storeHand(hh) # HandsPlayers - ? ... Do we fix winnings? @@ -603,7 +571,11 @@ Map the tuple self.gametype onto the pokerstars string describing it return gs + timestr def writeTableLine(self): - table_string = "Table \'%s\' %s-max" % (self.tablename, self.maxseats) + table_string = "Table " + if self.gametype['type'] == 'tour': + table_string = table_string + "\'%s %s\' %s-max" % (self.tourNo, self.tablename, self.maxseats) + else: + table_string = table_string + "\'%s\' %s-max" % (self.tablename, self.maxseats) if self.gametype['currency'] == 'play': table_string = table_string + " (Play Money)" if self.buttonpos != None and self.buttonpos != 0: @@ -673,7 +645,14 @@ class HoldemOmahaHand(Hand): if shown: self.shown.add(player) if mucked: self.mucked.add(player) else: - self.addHoleCards('PREFLOP', player, open=[], closed=cards, shown=shown, mucked=mucked, dealt=dealt) + if len(cards) in (2, 4): # avoid adding board by mistake (Everleaf problem) + self.addHoleCards('PREFLOP', player, open=[], closed=cards, shown=shown, mucked=mucked, dealt=dealt) + elif len(cards) == 5: # cards holds a winning hand, not hole cards + # filter( lambda x: x not in b, a ) # calcs a - b where a and b are lists + # so diff is set to the winning hand minus the board cards, if we're lucky that leaves the hole cards + diff = filter( lambda x: x not in self.board['FLOP']+self.board['TURN']+self.board['RIVER'], cards ) + if len(diff) == 2 and self.gametype['category'] in ('holdem'): + self.addHoleCards('PREFLOP', player, open=[], closed=diff, shown=shown, mucked=mucked, dealt=dealt) def getStreetTotals(self): # street1Pot INT, /* pot size at flop/street4 */ diff --git a/pyfpdb/Hud.py b/pyfpdb/Hud.py index 6d88e6d4..38402f8f 100644 --- a/pyfpdb/Hud.py +++ b/pyfpdb/Hud.py @@ -73,6 +73,8 @@ class Hud: self.stacked = True self.site = table.site self.mw_created = False + self.hud_params = parent.hud_params + self.stat_windows = {} self.popup_windows = {} @@ -144,6 +146,147 @@ class Hud: repositem = gtk.MenuItem('Reposition StatWindows') menu.append(repositem) repositem.connect("activate", self.reposition_windows) + + aggitem = gtk.MenuItem('Show Player Stats') + menu.append(aggitem) + self.aggMenu = gtk.Menu() + aggitem.set_submenu(self.aggMenu) + # set agg_bb_mult to 1 to stop aggregation + item = gtk.CheckMenuItem('For This Blind Level Only') + self.aggMenu.append(item) + item.connect("activate", self.set_aggregation, ('P',1)) + setattr(self, 'h_aggBBmultItem1', item) + # + item = gtk.MenuItem('For Multiple Blind Levels:') + self.aggMenu.append(item) + # + item = gtk.CheckMenuItem(' 0.5 to 2.0 x Current Blinds') + self.aggMenu.append(item) + item.connect("activate", self.set_aggregation, ('P',2)) + setattr(self, 'h_aggBBmultItem2', item) + # + item = gtk.CheckMenuItem(' 0.33 to 3.0 x Current Blinds') + self.aggMenu.append(item) + item.connect("activate", self.set_aggregation, ('P',3)) + setattr(self, 'h_aggBBmultItem3', item) + # + item = gtk.CheckMenuItem(' 0.1 to 10 x Current Blinds') + self.aggMenu.append(item) + item.connect("activate", self.set_aggregation, ('P',10)) + setattr(self, 'h_aggBBmultItem10', item) + # + item = gtk.CheckMenuItem(' All Levels') + self.aggMenu.append(item) + item.connect("activate", self.set_aggregation, ('P',10000)) + setattr(self, 'h_aggBBmultItem10000', item) + # + item = gtk.MenuItem('Since:') + self.aggMenu.append(item) + # + item = gtk.CheckMenuItem(' All Time') + self.aggMenu.append(item) + item.connect("activate", self.set_hud_style, ('P','A')) + setattr(self, 'h_hudStyleOptionA', item) + # + item = gtk.CheckMenuItem(' Session') + self.aggMenu.append(item) + item.connect("activate", self.set_hud_style, ('P','S')) + setattr(self, 'h_hudStyleOptionS', item) + # + item = gtk.CheckMenuItem(' %s Days' % (self.hud_params['h_hud_days'])) + self.aggMenu.append(item) + item.connect("activate", self.set_hud_style, ('P','T')) + setattr(self, 'h_hudStyleOptionT', item) + + aggitem = gtk.MenuItem('Show Opponent Stats') + menu.append(aggitem) + self.aggMenu = gtk.Menu() + aggitem.set_submenu(self.aggMenu) + # set agg_bb_mult to 1 to stop aggregation + item = gtk.CheckMenuItem('For This Blind Level Only') + self.aggMenu.append(item) + item.connect("activate", self.set_aggregation, ('O',1)) + setattr(self, 'aggBBmultItem1', item) + # + item = gtk.MenuItem('For Multiple Blind Levels:') + self.aggMenu.append(item) + # + item = gtk.CheckMenuItem(' 0.5 to 2.0 x Current Blinds') + self.aggMenu.append(item) + item.connect("activate", self.set_aggregation, ('O',2)) + setattr(self, 'aggBBmultItem2', item) + # + item = gtk.CheckMenuItem(' 0.33 to 3.0 x Current Blinds') + self.aggMenu.append(item) + item.connect("activate", self.set_aggregation, ('O',3)) + setattr(self, 'aggBBmultItem3', item) + # + item = gtk.CheckMenuItem(' 0.1 to 10 x Current Blinds') + self.aggMenu.append(item) + item.connect("activate", self.set_aggregation, ('O',10)) + setattr(self, 'aggBBmultItem10', item) + # + item = gtk.CheckMenuItem(' All Levels') + self.aggMenu.append(item) + item.connect("activate", self.set_aggregation, ('O',10000)) + setattr(self, 'aggBBmultItem10000', item) + # + item = gtk.MenuItem('Since:') + self.aggMenu.append(item) + # + item = gtk.CheckMenuItem(' All Time') + self.aggMenu.append(item) + item.connect("activate", self.set_hud_style, ('O','A')) + setattr(self, 'hudStyleOptionA', item) + # + item = gtk.CheckMenuItem(' Session') + self.aggMenu.append(item) + item.connect("activate", self.set_hud_style, ('O','S')) + setattr(self, 'hudStyleOptionS', item) + # + item = gtk.CheckMenuItem(' %s Days' % (self.hud_params['h_hud_days'])) + self.aggMenu.append(item) + item.connect("activate", self.set_hud_style, ('O','T')) + setattr(self, 'hudStyleOptionT', item) + + # set active on current options: + if self.hud_params['h_agg_bb_mult'] == 1: + getattr(self, 'h_aggBBmultItem1').set_active(True) + elif self.hud_params['h_agg_bb_mult'] == 2: + getattr(self, 'h_aggBBmultItem2').set_active(True) + elif self.hud_params['h_agg_bb_mult'] == 3: + getattr(self, 'h_aggBBmultItem3').set_active(True) + elif self.hud_params['h_agg_bb_mult'] == 10: + getattr(self, 'h_aggBBmultItem10').set_active(True) + elif self.hud_params['h_agg_bb_mult'] > 9000: + getattr(self, 'h_aggBBmultItem10000').set_active(True) + # + if self.hud_params['agg_bb_mult'] == 1: + getattr(self, 'aggBBmultItem1').set_active(True) + elif self.hud_params['agg_bb_mult'] == 2: + getattr(self, 'aggBBmultItem2').set_active(True) + elif self.hud_params['agg_bb_mult'] == 3: + getattr(self, 'aggBBmultItem3').set_active(True) + elif self.hud_params['agg_bb_mult'] == 10: + getattr(self, 'aggBBmultItem10').set_active(True) + elif self.hud_params['agg_bb_mult'] > 9000: + getattr(self, 'aggBBmultItem10000').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': + getattr(self, 'h_hudStyleOptionS').set_active(True) + elif self.hud_params['h_hud_style'] == 'T': + getattr(self, 'h_hudStyleOptionT').set_active(True) + # + if self.hud_params['hud_style'] == 'A': + getattr(self, 'hudStyleOptionA').set_active(True) + elif self.hud_params['hud_style'] == 'S': + getattr(self, 'hudStyleOptionS').set_active(True) + elif self.hud_params['hud_style'] == 'T': + getattr(self, 'hudStyleOptionT').set_active(True) + + eventbox.connect_object("button-press-event", self.on_button_press, menu) debugitem = gtk.MenuItem('Debug StatWindows') menu.append(debugitem) @@ -177,9 +320,58 @@ class Hud: self.create(*self.creation_attrs) self.update(self.hand, self.config) except Exception, e: - print "Expcetion:",str(e) + print "Exception:",str(e) pass + + def set_aggregation(self, widget, val): + (player_opp, num) = val + if player_opp == 'P': + # set these true all the time, set the multiplier to 1 to turn agg off: + self.hud_params['h_aggregate_ring'] = True + self.hud_params['h_aggregate_tour'] = True + + if self.hud_params['h_agg_bb_mult'] != num \ + and getattr(self, 'h_aggBBmultItem'+str(num)).get_active(): + print 'set_player_aggregation', num + self.hud_params['h_agg_bb_mult'] = num + for mult in ('1', '2', '3', '10', '10000'): + if mult != str(num): + getattr(self, 'h_aggBBmultItem'+mult).set_active(False) + else: + self.hud_params['aggregate_ring'] = True + self.hud_params['aggregate_tour'] = True + + if self.hud_params['agg_bb_mult'] != num \ + and getattr(self, 'aggBBmultItem'+str(num)).get_active(): + print 'set_opponent_aggregation', num + self.hud_params['agg_bb_mult'] = num + for mult in ('1', '2', '3', '10', '10000'): + if mult != str(num): + getattr(self, 'aggBBmultItem'+mult).set_active(False) + + def set_hud_style(self, widget, val): + (player_opp, style) = val + if player_opp == 'P': + param = 'h_hud_style' + prefix = 'h_' + else: + param = 'hud_style' + prefix = '' + if style == 'A' and getattr(self, prefix+'hudStyleOptionA').get_active(): + self.hud_params[param] = 'A' + getattr(self, prefix+'hudStyleOptionS').set_active(False) + getattr(self, prefix+'hudStyleOptionT').set_active(False) + elif style == 'S' and getattr(self, prefix+'hudStyleOptionS').get_active(): + self.hud_params[param] = 'S' + getattr(self, prefix+'hudStyleOptionA').set_active(False) + getattr(self, prefix+'hudStyleOptionT').set_active(False) + elif style == 'T' and getattr(self, prefix+'hudStyleOptionT').get_active(): + self.hud_params[param] = 'T' + getattr(self, prefix+'hudStyleOptionA').set_active(False) + getattr(self, prefix+'hudStyleOptionS').set_active(False) + print "setting self.hud_params[%s] = %s" % (param, style) + def update_table_position(self): if os.name == 'nt': if not win32gui.IsWindow(self.table.number): @@ -219,7 +411,11 @@ class Hud: # heap dead, burnt bodies, blood 'n guts, veins between my teeth for s in self.stat_windows.itervalues(): s.kill_popups() - s.window.destroy() + try: + # throws "invalid window handle" in WinXP (sometimes?) + s.window.destroy() + except: + pass self.stat_windows = {} # also kill any aux windows for aux in self.aux_windows: @@ -627,7 +823,7 @@ class Popup_window: # window.window.reparent(self.table.gdkhandle, 0, 0) window.window.set_transient_for(self.table.gdkhandle) # window.present() - + if __name__== "__main__": main_window = gtk.Window() diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index 986f9762..bb918d97 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -331,7 +331,8 @@ class Sql: speed varchar(10), headsUp BOOLEAN NOT NULL DEFAULT False, shootout BOOLEAN NOT NULL DEFAULT False, - matrix BOOLEAN NOT NULL DEFAULT False + matrix BOOLEAN NOT NULL DEFAULT False, + sng BOOLEAN NOT NULL DEFAULT False ) ENGINE=INNODB""" elif db_server == 'postgresql': @@ -346,7 +347,8 @@ class Sql: speed varchar(10), headsUp BOOLEAN NOT NULL DEFAULT False, shootout BOOLEAN NOT NULL DEFAULT False, - matrix BOOLEAN NOT NULL DEFAULT False + matrix BOOLEAN NOT NULL DEFAULT False, + sng BOOLEAN NOT NULL DEFAULT False )""" elif db_server == 'sqlite': self.query['createTourneyTypesTable'] = """CREATE TABLE TourneyTypes ( @@ -360,7 +362,8 @@ class Sql: speed TEXT, headsUp BOOLEAN NOT NULL DEFAULT 0, shootout BOOLEAN NOT NULL DEFAULT 0, - matrix BOOLEAN NOT NULL DEFAULT 0 + matrix BOOLEAN NOT NULL DEFAULT 0, + sng BOOLEAN NOT NULL DEFAULT 0 )""" ################################ @@ -820,7 +823,21 @@ class Sql: comment TEXT, commentTs timestamp without time zone)""" elif db_server == 'sqlite': - self.query['createTourneysPlayersTable'] = """ """ + self.query['createTourneysPlayersTable'] = """CREATE TABLE TourneysPlayers ( + id INT PRIMARY KEY, + tourneyId INT, + playerId INT, + payinAmount INT, + rank INT, + winnings INT, + nbRebuys INT DEFAULT 0, + nbAddons INT DEFAULT 0, + nbKO INT DEFAULT 0, + comment TEXT, + commentTs timestamp without time zone, + FOREIGN KEY (tourneyId) REFERENCES Tourneys(id), + FOREIGN KEY (playerId) REFERENCES Players(id) + )""" ################################ @@ -851,7 +868,18 @@ class Sql: comment TEXT, commentTs timestamp without time zone)""" elif db_server == 'sqlite': - self.query['createHandsActionsTable'] = """ """ + self.query['createHandsActionsTable'] = """CREATE TABLE HandsActions ( + id INT PRIMARY KEY, + handsPlayerId BIGINT, + street SMALLINT, + actionNo SMALLINT, + action CHAR(5), + allIn INT, + amount INT, + comment TEXT, + commentTs timestamp without time zone, + FOREIGN KEY (handsPlayerId) REFERENCES HandsPlayers(id) + )""" ################################ @@ -1160,26 +1188,42 @@ class Sql: if db_server == 'mysql': - self.query['addTourneyIndex'] = """ALTER TABLE Tourneys ADD INDEX siteTourneyNo(siteTourneyNo)""" + self.query['addTourneyIndex'] = """ALTER TABLE Tourneys ADD UNIQUE INDEX siteTourneyNo(siteTourneyNo, tourneyTypeId)""" elif db_server == 'postgresql': - self.query['addTourneyIndex'] = """CREATE INDEX siteTourneyNo ON Tourneys (siteTourneyNo)""" + self.query['addTourneyIndex'] = """CREATE UNIQUE INDEX siteTourneyNo ON Tourneys (siteTourneyNo, tourneyTypeId)""" elif db_server == 'sqlite': - self.query['addHandsIndex'] = """ """ + self.query['addTourneyIndex'] = """CREATE UNIQUE INDEX siteTourneyNo ON Tourneys (siteTourneyNo, tourneyTypeId)""" if db_server == 'mysql': - self.query['addHandsIndex'] = """ALTER TABLE Hands ADD INDEX siteHandNo(siteHandNo)""" + self.query['addHandsIndex'] = """ALTER TABLE Hands ADD UNIQUE INDEX siteHandNo(siteHandNo, gameTypeId)""" elif db_server == 'postgresql': - self.query['addHandsIndex'] = """CREATE INDEX siteHandNo ON Hands (siteHandNo)""" + self.query['addHandsIndex'] = """CREATE UNIQUE INDEX siteHandNo ON Hands (siteHandNo, gameTypeId)""" elif db_server == 'sqlite': - self.query['addHandsIndex'] = """ """ + self.query['addHandsIndex'] = """CREATE UNIQUE INDEX siteHandNo ON Hands (siteHandNo, gameTypeId)""" if db_server == 'mysql': - self.query['addPlayersIndex'] = """ALTER TABLE Players ADD INDEX name(name)""" + self.query['addPlayersIndex'] = """ALTER TABLE Players ADD UNIQUE INDEX name(name, siteId)""" elif db_server == 'postgresql': - self.query['addPlayersIndex'] = """CREATE INDEX name ON Players (name)""" + self.query['addPlayersIndex'] = """CREATE UNIQUE INDEX name ON Players (name, siteId)""" elif db_server == 'sqlite': - self.query['addPlayersIndex'] = """ """ + self.query['addPlayersIndex'] = """CREATE UNIQUE INDEX name ON Players (name, siteId)""" + if db_server == 'mysql': + self.query['addTPlayersIndex'] = """ALTER TABLE TourneysPlayers ADD UNIQUE INDEX tourneyId(tourneyId, playerId)""" + elif db_server == 'postgresql': + self.query['addTPlayersIndex'] = """CREATE UNIQUE INDEX tourneyId ON TourneysPlayers (tourneyId, playerId)""" + elif db_server == 'sqlite': + self.query['addTPlayersIndex'] = """CREATE UNIQUE INDEX tourneyId ON TourneysPlayers (tourneyId, playerId)""" + + if db_server == 'mysql': + self.query['addTTypesIndex'] = """ALTER TABLE TourneyTypes ADD UNIQUE INDEX tourneytypes_all(buyin, fee + , maxSeats, knockout, rebuyOrAddon, speed, headsUp, shootout, matrix, sng)""" + elif db_server == 'postgresql': + self.query['addTTypesIndex'] = """CREATE UNIQUE INDEX tourneyTypes_all ON TourneyTypes (buyin, fee + , maxSeats, knockout, rebuyOrAddon, speed, headsUp, shootout, matrix, sng)""" + elif db_server == 'sqlite': + self.query['addTTypesIndex'] = """CREATE UNIQUE INDEX tourneyTypes_all ON TourneyTypes (buyin, fee + , maxSeats, knockout, rebuyOrAddon, speed, headsUp, shootout, matrix, sng)""" self.query['get_last_hand'] = "select max(id) from Hands" @@ -1188,7 +1232,14 @@ class Sql: from Players, Sites where Players.name = %s and Sites.name = %s - and Players.SiteId = Sites.id + and Players.siteId = Sites.id + """ + + self.query['get_player_names'] = """ + select p.name + from Players p + where lower(p.name) like lower(%s) + and (p.siteId = %s or %s = -1) """ self.query['getSiteId'] = """SELECT id from Sites where name = %s""" @@ -1340,28 +1391,45 @@ class Sql: INNER JOIN HudCache hc ON (hc.playerId = hp.playerId) INNER JOIN Players p ON (p.id = hc.playerId) WHERE h.id = %s - AND hc.styleKey > %s - /* styleKey is currently 'd' (for date) followed by a yyyymmdd - date key. Set it to 0000000 or similar to get all records */ - /* Note: s means the placeholder 'percent's but we can't include that - in comments. (db api thinks they are actual arguments) - Could also check activeseats here even if only 3 groups eg 2-3/4-6/7+ - e.g. could use a multiplier: - AND h.seats > s / 1.25 and hp.seats < s * 1.25 - where s is the number of active players at the current table (and - 1.25 would be a config value so user could change it) - */ - AND hc.gametypeId+0 in - (SELECT gt1.id from Gametypes gt1, Gametypes gt2 - WHERE gt1.siteid = gt2.siteid /* find gametypes where these match: */ - AND gt1.type = gt2.type /* ring/tourney */ - AND gt1.category = gt2.category /* holdem/stud*/ - AND gt1.limittype = gt2.limittype /* fl/nl */ - AND gt1.bigblind < gt2.bigblind * %s /* bigblind similar size */ - AND gt1.bigblind > gt2.bigblind / %s - AND gt2.id = h.gametypeId) + AND ( /* 2 separate parts for hero and opponents */ + ( hp.playerId != %s + AND hc.styleKey > %s + AND hc.gametypeId+0 in + (SELECT gt1.id from Gametypes gt1, Gametypes gt2 + WHERE gt1.siteid = gt2.siteid /* find gametypes where these match: */ + AND gt1.type = gt2.type /* ring/tourney */ + AND gt1.category = gt2.category /* holdem/stud*/ + AND gt1.limittype = gt2.limittype /* fl/nl */ + AND gt1.bigblind <= gt2.bigblind * %s /* bigblind similar size */ + AND gt1.bigblind >= gt2.bigblind / %s + AND gt2.id = h.gametypeId) + ) + OR + ( hp.playerId = %s + AND hc.styleKey > %s + AND hc.gametypeId+0 in + (SELECT gt1.id from Gametypes gt1, Gametypes gt2 + WHERE gt1.siteid = gt2.siteid /* find gametypes where these match: */ + AND gt1.type = gt2.type /* ring/tourney */ + AND gt1.category = gt2.category /* holdem/stud*/ + AND gt1.limittype = gt2.limittype /* fl/nl */ + AND gt1.bigblind <= gt2.bigblind * %s /* bigblind similar size */ + AND gt1.bigblind >= gt2.bigblind / %s + AND gt2.id = h.gametypeId) + ) + ) GROUP BY hc.PlayerId, p.name """ + # NOTES on above cursor: + # - Do NOT include %s inside query in a comment - the db api thinks + # they are actual arguments. + # - styleKey is currently 'd' (for date) followed by a yyyymmdd + # date key. Set it to 0000000 or similar to get all records + # Could also check activeseats here even if only 3 groups eg 2-3/4-6/7+ + # e.g. could use a multiplier: + # AND h.seats > %s / 1.25 and hp.seats < %s * 1.25 + # where %s is the number of active players at the current table (and + # 1.25 would be a config value so user could change it) if db_server == 'mysql': self.query['get_stats_from_hand_session'] = """ @@ -1537,10 +1605,11 @@ class Sql: """ self.query['get_table_name'] = """ - select tableName, maxSeats, category, type - from Hands,Gametypes - where Hands.id = %s - and Gametypes.id = Hands.gametypeId + select h.tableName, h.maxSeats, gt.category, gt.type, gt.siteId + from Hands h + ,Gametypes gt + where h.id = %s + and gt.id = h.gametypeId """ self.query['get_actual_seat'] = """ @@ -1651,6 +1720,9 @@ class Sql: self.query['getSiteId'] = """SELECT id from Sites where name = %s""" self.query['getGames'] = """SELECT DISTINCT category from Gametypes""" self.query['getLimits'] = """SELECT DISTINCT bigBlind from Gametypes ORDER by bigBlind DESC""" + self.query['getLimits2'] = """SELECT DISTINCT type, limitType, bigBlind + from Gametypes + ORDER by type, limitType DESC, bigBlind DESC""" if db_server == 'mysql': self.query['playerDetailedStats'] = """ @@ -1706,7 +1778,7 @@ class Sql: inner join Gametypes gt on (gt.Id = h.gameTypeId) inner join Sites s on (s.Id = gt.siteId) where hp.playerId in - and hp.tourneysPlayersId IS NULL + /*and hp.tourneysPlayersId IS NULL*/ and h.seats @@ -1728,11 +1800,11 @@ class Sql: else concat('Z', ) end + ,upper(gt.limitType) desc ,maxbigblind desc - ,upper(gt.limitType) ,s.name """ - else: # assume postgresql + elif db_server == 'postgresql': self.query['playerDetailedStats'] = """ select AS hgametypeid ,gt.base @@ -1786,7 +1858,7 @@ class Sql: inner join Gametypes gt on (gt.Id = h.gameTypeId) inner join Sites s on (s.Id = gt.siteId) where hp.playerId in - and hp.tourneysPlayersId IS NULL + /*and hp.tourneysPlayersId IS NULL*/ and h.seats @@ -1809,12 +1881,91 @@ class Sql: else 'Z'|| end + ,upper(gt.limitType) desc + ,maxbigblind desc + ,s.name + """ + elif db_server == 'sqlite': + self.query['playerDetailedStats'] = """ + select AS hgametypeid + ,gt.base + ,gt.category + ,upper(gt.limitType) AS limittype + ,s.name + ,min(gt.bigBlind) AS minbigblind + ,max(gt.bigBlind) AS maxbigblind + /*, AS gtid*/ + , AS plposition + ,count(1) AS n + ,100.0*sum(cast(hp.street0VPI as integer))/count(1) AS vpip + ,100.0*sum(cast(hp.street0Aggr as integer))/count(1) AS pfr + ,case when sum(cast(hp.street0_3Bchance as integer)) = 0 then -999 + else 100.0*sum(cast(hp.street0_3Bdone as integer))/sum(cast(hp.street0_3Bchance as integer)) + end AS pf3 + ,case when sum(cast(hp.stealattemptchance as integer)) = 0 then -999 + else 100.0*sum(cast(hp.stealattempted as integer))/sum(cast(hp.stealattemptchance as integer)) + end AS steals + ,100.0*sum(cast(hp.street1Seen as integer))/count(1) AS saw_f + ,100.0*sum(cast(hp.sawShowdown as integer))/count(1) AS sawsd + ,case when sum(cast(hp.street1Seen as integer)) = 0 then -999 + else 100.0*sum(cast(hp.sawShowdown as integer))/sum(cast(hp.street1Seen as integer)) + end AS wtsdwsf + ,case when sum(cast(hp.sawShowdown as integer)) = 0 then -999 + else 100.0*sum(cast(hp.wonAtSD as integer))/sum(cast(hp.sawShowdown as integer)) + end AS wmsd + ,case when sum(cast(hp.street1Seen as integer)) = 0 then -999 + else 100.0*sum(cast(hp.street1Aggr as integer))/sum(cast(hp.street1Seen as integer)) + end AS flafq + ,case when sum(cast(hp.street2Seen as integer)) = 0 then -999 + else 100.0*sum(cast(hp.street2Aggr as integer))/sum(cast(hp.street2Seen as integer)) + end AS tuafq + ,case when sum(cast(hp.street3Seen as integer)) = 0 then -999 + else 100.0*sum(cast(hp.street3Aggr as integer))/sum(cast(hp.street3Seen as integer)) + end AS rvafq + ,case when sum(cast(hp.street1Seen as integer))+sum(cast(hp.street2Seen as integer))+sum(cast(hp.street3Seen as integer)) = 0 then -999 + else 100.0*(sum(cast(hp.street1Aggr as integer))+sum(cast(hp.street2Aggr as integer))+sum(cast(hp.street3Aggr as integer))) + /(sum(cast(hp.street1Seen as integer))+sum(cast(hp.street2Seen as integer))+sum(cast(hp.street3Seen as integer))) + end AS pofafq + ,sum(hp.totalProfit)/100.0 AS net + ,sum(hp.rake)/100.0 AS rake + ,100.0*avg(hp.totalProfit/(gt.bigBlind+0.0)) AS bbper100 + ,avg(hp.totalProfit)/100.0 AS profitperhand + ,100.0*avg((hp.totalProfit+hp.rake)/(gt.bigBlind+0.0)) AS bb100xr + ,avg((hp.totalProfit+hp.rake)/100.0) AS profhndxr + ,avg(h.seats+0.0) AS avgseats + ,variance(hp.totalProfit/100.0) AS variance + 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) + where hp.playerId in + /*and hp.tourneysPlayersId IS NULL*/ + and h.seats + + + and to_char(h.handStart, 'YYYY-MM-DD') + group by hgameTypeId + ,hp.playerId + ,gt.base + ,gt.category + + ,plposition + ,upper(gt.limitType) + ,s.name + order by hp.playerId + ,gt.base + ,gt.category + + ,case when 'B' then 'B' + when 'S' then 'S' + when '0' then 'Y' + else 'Z'|| + end + + ,upper(gt.limitType) desc ,maxbigblind desc - ,upper(gt.limitType) ,s.name """ - #elif db_server == 'sqlite': - # self.query['playerDetailedStats'] = """ """ if db_server == 'mysql': self.query['playerStats'] = """ @@ -2304,16 +2455,16 @@ class Sql: # self.query['playerStatsByPosition'] = """ """ self.query['getRingProfitAllHandsPlayerIdSite'] = """ - SELECT hp.handId, hp.totalProfit, hp.totalProfit, hp.totalProfit + SELECT hp.handId, hp.totalProfit FROM HandsPlayers hp - INNER JOIN Players pl ON (hp.playerId = pl.id) - INNER JOIN Hands h ON (h.id = hp.handId) - INNER JOIN Gametypes g ON (h.gametypeId = g.id) - where pl.id in + INNER JOIN Players pl ON (pl.id = hp.playerId) + INNER JOIN Hands h ON (h.id = hp.handId) + INNER JOIN Gametypes gt ON (gt.id = h.gametypeId) + WHERE pl.id in AND pl.siteId in AND h.handStart > '' AND h.handStart < '' - AND g.bigBlind in + AND hp.tourneysPlayersId IS NULL GROUP BY h.handStart, hp.handId, hp.totalProfit ORDER BY h.handStart""" @@ -2466,6 +2617,7 @@ class Sql: ,sum(street4CheckCallRaiseDone) FROM HandsPlayers hp INNER JOIN Hands h ON (h.id = hp.handId) + GROUP BY h.gametypeId ,hp.playerId ,h.seats @@ -2614,6 +2766,7 @@ class Sql: ,sum(CAST(street4CheckCallRaiseDone as integer)) FROM HandsPlayers hp INNER JOIN Hands h ON (h.id = hp.handId) + GROUP BY h.gametypeId ,hp.playerId ,h.seats @@ -2762,6 +2915,7 @@ class Sql: ,sum(CAST(street4CheckCallRaiseDone as integer)) FROM HandsPlayers hp INNER JOIN Hands h ON (h.id = hp.handId) + GROUP BY h.gametypeId ,hp.playerId ,h.seats @@ -2770,9 +2924,14 @@ class Sql: ,'d' || substr(strftime('%Y%m%d', h.handStart),3,7) """ + self.query['get_hero_hudcache_start'] = """select min(hc.styleKey) + from HudCache hc + where hc.playerId in + and hc.styleKey like 'd%'""" + if db_server == 'mysql': self.query['analyze'] = """ - analyze table Autorates, GameTypes, Hands, HandsPlayers, Hudcache, Players + analyze table Autorates, GameTypes, Hands, HandsPlayers, HudCache, Players , Settings, Sites, Tourneys, TourneysPlayers, TourneyTypes """ else: # assume postgres diff --git a/pyfpdb/Tables.py b/pyfpdb/Tables.py index 6bc9e57d..e725d5b8 100755 --- a/pyfpdb/Tables.py +++ b/pyfpdb/Tables.py @@ -257,6 +257,10 @@ def discover_nt_tournament(c, tour_number, tab_number): titles ={} win32gui.EnumWindows(win_enum_handler, titles) for hwnd in titles: + if 'Chat:' in titles[hwnd]: continue # Some sites (FTP? PS? Others?) have seperable or seperately constructed chat windows + if 'History for table:' in titles[hwnd]: continue # Everleaf Network HH viewer window + if 'HUD:' in titles[hwnd]: continue # FPDB HUD window + if re.search(search_string, titles[hwnd]): return decode_windows(c, titles[hwnd], hwnd) return None @@ -376,7 +380,7 @@ def clean_title(name): """Clean the little info strings from the table name.""" # these strings could go in a config file for pattern in [' \(6 max\)', ' \(heads up\)', ' \(deep\)', - ' \(deep hu\)', ' \(deep 6\)', ' \(2\)', + ' \(deep hu\)', ' \(deep 6\)', '\(6 max, deep\)', ' \(2\)', ' \(edu\)', ' \(edu, 6 max\)', ' \(6\)', ' \(speed\)', 'special', 'newVPP', ' no all-in', ' fast', ',', ' 50BB min', '50bb min', '\s+$']: diff --git a/pyfpdb/TournamentTracker.py b/pyfpdb/TournamentTracker.py index 7a68a644..2ac95b24 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) = self.db_connection.get_table_name(new_hand_id) + (table_name, max, poker_game, type, site_id) = 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 fd2feca2..18bc65d9 100755 --- a/pyfpdb/fpdb.py +++ b/pyfpdb/fpdb.py @@ -44,6 +44,7 @@ else: print "Python " + sys.version[0:3] + '...\n' +import traceback import threading import Options import string @@ -64,7 +65,6 @@ import gtk import interlocks -import fpdb_simple import GuiBulkImport import GuiPlayerStats import GuiPositionalStats @@ -234,13 +234,13 @@ class fpdb: dia_confirm = gtk.MessageDialog(parent=None, flags=0, type=gtk.MESSAGE_WARNING, buttons=(gtk.BUTTONS_YES_NO), message_format="Confirm deleting and recreating tables") diastring = "Please confirm that you want to (re-)create the tables. If there already are tables in the database " \ - +self.db.fdb.database+" on "+self.db.fdb.host+" they will be deleted." + +self.db.database+" on "+self.db.host+" they will be deleted." dia_confirm.format_secondary_text(diastring)#todo: make above string with bold for db, host and deleted response = dia_confirm.run() dia_confirm.destroy() if response == gtk.RESPONSE_YES: - #if self.db.fdb.backend == self.fdb_lock.fdb.MYSQL_INNODB: + #if self.db.backend == self.fdb_lock.fdb.MYSQL_INNODB: # mysql requires locks on all tables or none - easier to release this lock # than lock all the other tables # ToDo: lock all other tables so that lock doesn't have to be released @@ -257,17 +257,65 @@ class fpdb: def dia_recreate_hudcache(self, widget, data=None): if self.obtain_global_lock(): - dia_confirm = gtk.MessageDialog(parent=None, flags=0, type=gtk.MESSAGE_WARNING, buttons=(gtk.BUTTONS_YES_NO), message_format="Confirm recreating HUD cache") + self.dia_confirm = gtk.MessageDialog(parent=None, flags=0, type=gtk.MESSAGE_WARNING, buttons=(gtk.BUTTONS_YES_NO), message_format="Confirm recreating HUD cache") diastring = "Please confirm that you want to re-create the HUD cache." - dia_confirm.format_secondary_text(diastring) - - response = dia_confirm.run() - dia_confirm.destroy() + self.dia_confirm.format_secondary_text(diastring) + + hb = gtk.HBox(True, 1) + self.start_date = gtk.Entry(max=12) + self.start_date.set_text( self.db.get_hero_hudcache_start() ) + lbl = gtk.Label(" Hero's cache starts: ") + btn = gtk.Button() + btn.set_image(gtk.image_new_from_stock(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)) + btn.connect('clicked', self.__calendar_dialog, self.start_date) + + hb.pack_start(lbl, expand=True, padding=3) + hb.pack_start(self.start_date, expand=True, padding=2) + hb.pack_start(btn, expand=False, padding=3) + self.dia_confirm.vbox.add(hb) + hb.show_all() + + response = self.dia_confirm.run() + self.dia_confirm.destroy() if response == gtk.RESPONSE_YES: - self.db.rebuild_hudcache() - elif response == gtk.REPSONSE_NO: + self.db.rebuild_hudcache( self.start_date.get_text() ) + elif response == gtk.RESPONSE_NO: print 'User cancelled rebuilding hud cache' + self.release_global_lock() + + def __calendar_dialog(self, widget, entry): + self.dia_confirm.set_modal(False) + d = gtk.Window(gtk.WINDOW_TOPLEVEL) + d.set_title('Pick a date') + + vb = gtk.VBox() + cal = gtk.Calendar() + vb.pack_start(cal, expand=False, padding=0) + + btn = gtk.Button('Done') + btn.connect('clicked', self.__get_date, cal, entry, d) + + vb.pack_start(btn, expand=False, padding=4) + + d.add(vb) + d.set_position(gtk.WIN_POS_MOUSE) + d.show_all() + + def __get_dates(self): + t1 = self.start_date.get_text() + if t1 == '': + t1 = '1970-01-01' + return (t1) + + def __get_date(self, widget, calendar, entry, win): + # year and day are correct, month is 0..11 + (year, month, day) = calendar.get_date() + month += 1 + ds = '%04d-%02d-%02d' % (year, month, day) + entry.set_text(ds) + win.destroy() + self.dia_confirm.set_modal(True) def dia_regression_test(self, widget, data=None): self.warning_box("Unimplemented: Regression Test") @@ -405,9 +453,25 @@ class fpdb: self.db.disconnect() self.sql = SQL.Sql(type = self.settings['db-type'], db_server = self.settings['db-server']) - self.db = Database.Database(self.config, sql = self.sql) + try: + self.db = Database.Database(self.config, sql = self.sql) + except FpdbMySQLFailedError: + self.warning_box("Unable to connect to MySQL! Is the MySQL server running?!", "FPDB ERROR") + exit() + except FpdbError: + #print "Failed to connect to %s database with username %s." % (self.settings['db-server'], self.settings['db-user']) + self.warning_box("Failed to connect to %s database with username %s." % (self.settings['db-server'], self.settings['db-user']), "FPDB ERROR") + err = traceback.extract_tb(sys.exc_info()[2])[-1] + print "*** Error: " + err[2] + "(" + str(err[1]) + "): " + str(sys.exc_info()[1]) + sys.stderr.write("Failed to connect to %s database with username %s." % (self.settings['db-server'], self.settings['db-user'])) + except: + #print "Failed to connect to %s database with username %s." % (self.settings['db-server'], self.settings['db-user']) + self.warning_box("Failed to connect to %s database with username %s." % (self.settings['db-server'], self.settings['db-user']), "FPDB ERROR") + err = traceback.extract_tb(sys.exc_info()[2])[-1] + print "*** Error: " + err[2] + "(" + str(err[1]) + "): " + str(sys.exc_info()[1]) + sys.stderr.write("Failed to connect to %s database with username %s." % (self.settings['db-server'], self.settings['db-user'])) - if self.db.fdb.wrongDbVersion: + if self.db.wrongDbVersion: diaDbVersionWarning = gtk.Dialog(title="Strong Warning - Invalid database version", parent=None, flags=0, buttons=(gtk.STOCK_OK,gtk.RESPONSE_OK)) label = gtk.Label("An invalid DB version or missing tables have been detected.") @@ -426,14 +490,14 @@ class fpdb: diaDbVersionWarning.destroy() if self.status_bar == None: - self.status_bar = gtk.Label("Status: Connected to %s database named %s on host %s"%(self.db.get_backend_name(),self.db.fdb.database, self.db.fdb.host)) + self.status_bar = gtk.Label("Status: Connected to %s database named %s on host %s"%(self.db.get_backend_name(),self.db.database, self.db.host)) self.main_vbox.pack_end(self.status_bar, False, True, 0) self.status_bar.show() else: - self.status_bar.set_text("Status: Connected to %s database named %s on host %s" % (self.db.get_backend_name(),self.db.fdb.database, self.db.fdb.host)) + self.status_bar.set_text("Status: Connected to %s database named %s on host %s" % (self.db.get_backend_name(),self.db.database, self.db.host)) # Database connected to successfully, load queries to pass on to other classes - self.db.connection.rollback() + self.db.rollback() self.validate_config() diff --git a/pyfpdb/fpdb_db.py b/pyfpdb/fpdb_db.py index 2b73a40e..986c747f 100644 --- a/pyfpdb/fpdb_db.py +++ b/pyfpdb/fpdb_db.py @@ -38,6 +38,8 @@ class fpdb_db: MYSQL_INNODB = 2 PGSQL = 3 SQLITE = 4 + sqlite_db_dir = ".." + os.sep + "database" + def __init__(self): """Simple constructor, doesnt really do anything""" self.db = None @@ -78,7 +80,7 @@ class fpdb_db: try: self.db = MySQLdb.connect(host = host, user = user, passwd = password, db = database, use_unicode=True) except: - raise FpdbError("MySQL connection failed") + raise FpdbMySQLFailedError("MySQL connection failed") elif backend==fpdb_db.PGSQL: import psycopg2 import psycopg2.extensions @@ -109,7 +111,7 @@ class fpdb_db: password = password, database = database) except: - msg = "PostgreSQL connection to database (%s) user (%s) failed." % (database, user) + msg = "PostgreSQL connection to database (%s) user (%s) failed. Are you sure the DB is running?" % (database, user) print msg raise FpdbError(msg) elif backend == fpdb_db.SQLITE: @@ -119,7 +121,12 @@ class fpdb_db: sqlite3 = pool.manage(sqlite3, pool_size=1) else: logging.warning("SQLite won't work well without 'sqlalchemy' installed.") - self.db = sqlite3.connect(database,detect_types=sqlite3.PARSE_DECLTYPES) + + if not os.path.isdir(self.sqlite_db_dir): + print "Creating directory: '%s'" % (self.sqlite_db_dir) + os.mkdir(self.sqlite_db_dir) + self.db = sqlite3.connect( self.sqlite_db_dir + os.sep + database + , detect_types=sqlite3.PARSE_DECLTYPES ) sqlite3.register_converter("bool", lambda x: bool(int(x))) sqlite3.register_adapter(bool, lambda x: "1" if x else "0") else: diff --git a/pyfpdb/fpdb_import.py b/pyfpdb/fpdb_import.py index 4fb4789d..878b64b2 100644 --- a/pyfpdb/fpdb_import.py +++ b/pyfpdb/fpdb_import.py @@ -547,7 +547,7 @@ class Importer: if self.callHud: #print "call to HUD here. handsId:",handsId #pipe the Hands.id out to the HUD - #print "sending hand to hud", handsId, "pipe =", self.caller.pipe_to_hud + print "sending hand to hud", handsId, "pipe =", self.caller.pipe_to_hud self.caller.pipe_to_hud.stdin.write("%s" % (handsId) + os.linesep) except Exceptions.DuplicateError: duplicates += 1 diff --git a/pyfpdb/fpdb_parse_logic.py b/pyfpdb/fpdb_parse_logic.py index 1972fc30..02300315 100644 --- a/pyfpdb/fpdb_parse_logic.py +++ b/pyfpdb/fpdb_parse_logic.py @@ -118,7 +118,7 @@ def mainParser(settings, siteID, category, hand, config, db = None, writeq = Non seatLines.append(line) names = fpdb_simple.parseNames(seatLines) - playerIDs = fpdb_simple.recognisePlayerIDs(db, names, siteID) # inserts players as needed + playerIDs = db.recognisePlayerIDs(names, siteID) # inserts players as needed tmp = fpdb_simple.parseCashesAndSeatNos(seatLines) startCashes = tmp['startCashes'] seatNos = tmp['seatNos'] diff --git a/pyfpdb/fpdb_simple.py b/pyfpdb/fpdb_simple.py index 084949d6..3e2d6f5f 100644 --- a/pyfpdb/fpdb_simple.py +++ b/pyfpdb/fpdb_simple.py @@ -913,92 +913,54 @@ def recogniseGametypeID(backend, db, cursor, topline, smallBlindLine, site_id, c #end def recogniseGametypeID def recogniseTourneyTypeId(db, siteId, tourneySiteId, buyin, fee, knockout, rebuyOrAddon): + ret = -1 cursor = db.get_cursor() # First we try to find the tourney itself (by its tourneySiteId) in case it has already been inserted before (by a summary file for instance) # The reason is that some tourneys may not be identified correctly in the HH toplines (especially Buy-In and Fee which are used to search/create the TourneyTypeId) #TODO: When the summary file will be dumped to BD, if the tourney is already in, Buy-In/Fee may need an update (e.g. creation of a new type and link to the Tourney) cursor.execute (db.sql.query['getTourneyTypeIdByTourneyNo'].replace('%s', db.sql.query['placeholder']), (tourneySiteId, siteId)) - result=cursor.fetchone() + result = cursor.fetchone() try: len(result) + ret = result[0] except: - cursor.execute ("SELECT id FROM TourneyTypes WHERE siteId=%s AND buyin=%s AND fee=%s AND knockout=%s AND rebuyOrAddon=%s", (siteId, buyin, fee, knockout, rebuyOrAddon)) - result=cursor.fetchone() - #print "tried SELECTing gametypes.id, result:",result + cursor.execute( """SELECT id FROM TourneyTypes + WHERE siteId=%s AND buyin=%s AND fee=%s + AND knockout=%s AND rebuyOrAddon=%s""" + , (siteId, buyin, fee, knockout, rebuyOrAddon) ) + result = cursor.fetchone() + #print "tried selecting tourneytypes.id, result:", result try: len(result) + ret = result[0] except TypeError:#this means we need to create a new entry - cursor.execute("""INSERT INTO TourneyTypes (siteId, buyin, fee, knockout, rebuyOrAddon) VALUES (%s, %s, %s, %s, %s)""", (siteId, buyin, fee, knockout, rebuyOrAddon)) - cursor.execute("SELECT id FROM TourneyTypes WHERE siteId=%s AND buyin=%s AND fee=%s AND knockout=%s AND rebuyOrAddon=%s", (siteId, buyin, fee, knockout, rebuyOrAddon)) - result=cursor.fetchone() + #print "insert new tourneytype record ..." + try: + cursor.execute( """INSERT INTO TourneyTypes (siteId, buyin, fee, knockout, rebuyOrAddon) + VALUES (%s, %s, %s, %s, %s)""" + , (siteId, buyin, fee, knockout, rebuyOrAddon) ) + ret = db.get_last_insert_id(cursor) + except: + #print "maybe tourneytype was created since select, try selecting again ..." + cursor.execute( """SELECT id FROM TourneyTypes + WHERE siteId=%s AND buyin=%s AND fee=%s + AND knockout=%s AND rebuyOrAddon=%s""" + , (siteId, buyin, fee, knockout, rebuyOrAddon) ) + result = cursor.fetchone() + try: + len(result) + ret = result[0] + except: + print "Failed to find or insert TourneyTypes record" + ret = -1 # failed to find or insert record + #print "tried selecting tourneytypes.id again, result:", result - return result[0] + #print "recogniseTourneyTypeId: returning", ret + return ret #end def recogniseTourneyTypeId -#returns the SQL ids of the names given in an array -# TODO: if someone gets industrious, they should make the parts that use the output of this function deal with a dict -# { playername: id } instead of depending on it's relation to the positions list -# then this can be reduced in complexity a bit - -#def recognisePlayerIDs(cursor, names, site_id): -# result = [] -# for i in xrange(len(names)): -# cursor.execute ("SELECT id FROM Players WHERE name=%s", (names[i],)) -# tmp=cursor.fetchall() -# if (len(tmp)==0): #new player -# cursor.execute ("INSERT INTO Players (name, siteId) VALUES (%s, %s)", (names[i], site_id)) -# #print "Number of players rows inserted: %d" % cursor.rowcount -# cursor.execute ("SELECT id FROM Players WHERE name=%s", (names[i],)) -# tmp=cursor.fetchall() -# #print "recognisePlayerIDs, names[i]:",names[i],"tmp:",tmp -# result.append(tmp[0][0]) -# return result - -def recognisePlayerIDs(db, names, site_id): - c = db.get_cursor() - q = "SELECT name,id FROM Players WHERE siteid=%d and (name=%s)" %(site_id, " OR name=".join([db.sql.query['placeholder'] for n in names])) - c.execute(q, names) # get all playerids by the names passed in - ids = dict(c.fetchall()) # convert to dict - if len(ids) != len(names): - notfound = [n for n in names if n not in ids] # make list of names not in database - if notfound: # insert them into database - q_ins = "INSERT INTO Players (name, siteId) VALUES (%s, "+str(site_id)+")" - q_ins = q_ins.replace('%s', db.sql.query['placeholder']) - c.executemany(q_ins, [(n,) for n in notfound]) - q2 = "SELECT name,id FROM Players WHERE siteid=%d and (name=%s)" % (site_id, " OR name=".join(["%s" for n in notfound])) - q2 = q2.replace('%s', db.sql.query['placeholder']) - c.execute(q2, notfound) # get their new ids - tmp = c.fetchall() - for n,id in tmp: # put them all into the same dict - ids[n] = id - # return them in the SAME ORDER that they came in in the names argument, rather than the order they came out of the DB - return [ids[n] for n in names] -#end def recognisePlayerIDs - - -# Here's a version that would work if it wasn't for the fact that it needs to have the output in the same order as input -# this version could also be improved upon using list comprehensions, etc - -#def recognisePlayerIDs(cursor, names, site_id): -# result = [] -# notfound = [] -# cursor.execute("SELECT name,id FROM Players WHERE name='%s'" % "' OR name='".join(names)) -# tmp = dict(cursor.fetchall()) -# for n in names: -# if n not in tmp: -# notfound.append(n) -# else: -# result.append(tmp[n]) -# if notfound: -# cursor.executemany("INSERT INTO Players (name, siteId) VALUES (%s, "+str(site_id)+")", (notfound)) -# cursor.execute("SELECT id FROM Players WHERE name='%s'" % "' OR name='".join(notfound)) -# tmp = cursor.fetchall() -# for n in tmp: -# result.append(n[0]) -# -# return result #recognises the name in the given line and returns its array position in the given array def recognisePlayerNo(line, names, atype):