Merge branch 'master' of git://trac-git.assembla.com/fpdb-sql

Conflicts:
	pyfpdb/Database.py

Resolve by reverting to sqlcoder's version; it uses the nice insert in
fillDefaultData for each case now and having the DB backends separate
might be good for the foreseeable future.
This commit is contained in:
Mika Bostrom 2009-09-28 18:07:08 +03:00
commit 5260f5c384
12 changed files with 617 additions and 257 deletions

View File

@ -26,17 +26,17 @@ def twoStartCards(value1, suit1, value2, suit2):
if value1 < 2 or value2 < 2: if value1 < 2 or value2 < 2:
ret = 0 ret = 0
if value1 == value2: # pairs if value1 == value2: # pairs
ret = (13 * (value2-2) + (value2-1) ) ret = (13 * (value2-2) + (value2-2) )
elif suit1 == suit2: elif suit1 == suit2:
if value1 > value2: if value1 > value2:
ret = 13 * (value1-2) + (value2-1) ret = 13 * (value1-2) + (value2-2)
else: else:
ret = 13 * (value2-2) + (value1-1) ret = 13 * (value2-2) + (value1-2)
else: else:
if value1 > value2: if value1 > value2:
ret = 13 * (value2-2) + (value2-1) ret = 13 * (value2-2) + (value1-2)
else: else:
ret = 13 * (value1-2) + (value2-1) ret = 13 * (value1-2) + (value2-2)
# print "twoStartCards(", value1, suit1, value2, suit2, ")=", ret # print "twoStartCards(", value1, suit1, value2, suit2, ")=", ret
return ret return ret
@ -46,7 +46,6 @@ def twoStartCardString(card):
into a string like AQo """ into a string like AQo """
ret = 'xx' ret = 'xx'
if card > 0: if card > 0:
card -= 1
s = ('2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K', 'A') s = ('2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K', 'A')
x = card / 13 x = card / 13
y = card - 13 * x y = card - 13 * x

View File

@ -49,12 +49,15 @@ import logging, logging.config
logging.config.fileConfig(os.path.join(sys.path[0],"logging.conf")) logging.config.fileConfig(os.path.join(sys.path[0],"logging.conf"))
log = logging.getLogger('db') log = logging.getLogger('db')
class Database: class Database:
MYSQL_INNODB = 2 MYSQL_INNODB = 2
PGSQL = 3 PGSQL = 3
SQLITE = 4 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 # Data Structures for index and foreign key creation
# drop_code is an int with possible values: 0 - don't drop for bulk import # drop_code is an int with possible values: 0 - don't drop for bulk import
# 1 - drop during bulk import # 1 - drop during bulk import
@ -70,19 +73,19 @@ class Database:
[ ] # no db with index 0 [ ] # no db with index 0
, [ ] # no db with index 1 , [ ] # no db with index 1
, [ # indexes for mysql (list index 2) , [ # indexes for mysql (list index 2)
{'tab':'Players', 'col':'name', 'drop':0} # {'tab':'Players', 'col':'name', 'drop':0} unique indexes not dropped
, {'tab':'Hands', 'col':'siteHandNo', 'drop':0} # {'tab':'Hands', 'col':'siteHandNo', 'drop':0} unique indexes not dropped
, {'tab':'Hands', 'col':'gametypeId', 'drop':0} # mct 22/3/09 {'tab':'Hands', 'col':'gametypeId', 'drop':0} # mct 22/3/09
, {'tab':'HandsPlayers', 'col':'handId', 'drop':0} # not needed, handled by fk , {'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':'playerId', 'drop':0} # not needed, handled by fk
, {'tab':'HandsPlayers', 'col':'tourneyTypeId', 'drop':0} , {'tab':'HandsPlayers', 'col':'tourneyTypeId', 'drop':0}
, {'tab':'HandsPlayers', 'col':'tourneysPlayersId', '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) , [ # indexes for postgres (list index 3)
{'tab':'Gametypes', 'col':'siteId', 'drop':0} {'tab':'Gametypes', 'col':'siteId', 'drop':0}
, {'tab':'Hands', 'col':'gametypeId', 'drop':0} # mct 22/3/09 , {'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':'HandsActions', 'col':'handsPlayerId', 'drop':0}
, {'tab':'HandsPlayers', 'col':'handId', 'drop':1} , {'tab':'HandsPlayers', 'col':'handId', 'drop':1}
, {'tab':'HandsPlayers', 'col':'playerId', 'drop':1} , {'tab':'HandsPlayers', 'col':'playerId', 'drop':1}
@ -91,22 +94,22 @@ class Database:
, {'tab':'HudCache', 'col':'playerId', 'drop':0} , {'tab':'HudCache', 'col':'playerId', 'drop':0}
, {'tab':'HudCache', 'col':'tourneyTypeId', 'drop':0} , {'tab':'HudCache', 'col':'tourneyTypeId', 'drop':0}
, {'tab':'Players', 'col':'siteId', 'drop':1} , {'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':'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':'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} , {'tab':'TourneyTypes', 'col':'siteId', 'drop':0}
] ]
, [ # indexes for sqlite (list index 4) , [ # indexes for sqlite (list index 4)
{'tab':'Players', 'col':'name', 'drop':0} # {'tab':'Players', 'col':'name', 'drop':0} unique indexes not dropped
, {'tab':'Hands', 'col':'siteHandNo', 'drop':0} # {'tab':'Hands', 'col':'siteHandNo', 'drop':0} unique indexes not dropped
, {'tab':'Hands', 'col':'gametypeId', 'drop':0} {'tab':'Hands', 'col':'gametypeId', 'drop':0}
, {'tab':'HandsPlayers', 'col':'handId', 'drop':0} , {'tab':'HandsPlayers', 'col':'handId', 'drop':0}
, {'tab':'HandsPlayers', 'col':'playerId', 'drop':0} , {'tab':'HandsPlayers', 'col':'playerId', 'drop':0}
, {'tab':'HandsPlayers', 'col':'tourneyTypeId', 'drop':0} , {'tab':'HandsPlayers', 'col':'tourneyTypeId', 'drop':0}
, {'tab':'HandsPlayers', 'col':'tourneysPlayersId', '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,6 +186,7 @@ class Database:
def __init__(self, c, sql = None): def __init__(self, c, sql = None):
log.info("Creating Database instance, sql = %s" % sql) 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 = fpdb_db.fpdb_db() # sets self.fdb.db self.fdb.cursor and self.fdb.sql
self.fdb.do_connect(c) self.fdb.do_connect(c)
self.connection = self.fdb.db self.connection = self.fdb.db
@ -368,7 +372,10 @@ class Database:
return winners return winners
def init_hud_stat_vars(self, hud_days): def init_hud_stat_vars(self, hud_days):
"""Initialise variables used by Hud to fetch stats.""" """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.hand_1day_ago = 1 self.hand_1day_ago = 1
try: try:
@ -381,9 +388,10 @@ class Database:
else: else:
if row and row[0]: if row and row[0]:
self.hand_1_day_ago = row[0] self.hand_1_day_ago = row[0]
d = timedelta(days=hud_days)
now = datetime.utcnow() - d d = timedelta(days=hud_days)
self.date_ndays_ago = "d%02d%02d%02d" % (now.year - 2000, now.month, now.day) now = datetime.utcnow() - d
self.date_ndays_ago = "d%02d%02d%02d" % (now.year - 2000, now.month, now.day)
def init_player_hud_stat_vars(self, playerid): def init_player_hud_stat_vars(self, playerid):
# not sure if this is workable, to be continued ... # not sure if this is workable, to be continued ...
@ -403,10 +411,19 @@ class Database:
err = traceback.extract_tb(sys.exc_info()[2])[-1] err = traceback.extract_tb(sys.exc_info()[2])[-1]
print "***Error: "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[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): def get_stats_from_hand( self, hand
, hud_params = {'aggregate_tour':False, 'aggregate_ring':False, 'hud_style':'A', 'agg_bb_mult':100}
, hero_ids = {}
):
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']
stat_dict = {}
if hud_style == 'S': if hud_style == 'S':
return( self.get_stats_from_hand_session(hand) ) self.get_stats_from_hand_session(hand, stat_dict)
return stat_dict
else: # hud_style == A else: # hud_style == A
@ -430,7 +447,6 @@ class Database:
# now get the stats # now get the stats
c.execute(self.sql.query[query], subs) c.execute(self.sql.query[query], subs)
colnames = [desc[0] for desc in c.description] colnames = [desc[0] for desc in c.description]
stat_dict = {}
for row in c.fetchall(): for row in c.fetchall():
t_dict = {} t_dict = {}
for name, val in zip(colnames, row): for name, val in zip(colnames, row):
@ -441,7 +457,7 @@ class Database:
return stat_dict return stat_dict
# uses query on handsplayers instead of hudcache to get stats on just this session # 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):
query = self.sql.query['get_stats_from_hand_session'] query = self.sql.query['get_stats_from_hand_session']
if self.db_server == 'mysql': if self.db_server == 'mysql':
@ -456,31 +472,32 @@ class Database:
#print "sess_stats: subs =", subs, "subs[0] =", subs[0] #print "sess_stats: subs =", subs, "subs[0] =", subs[0]
c.execute(query, subs) c.execute(query, subs)
colnames = [desc[0] for desc in c.description] colnames = [desc[0] for desc in c.description]
n,stat_dict = 0,{} n = 0
row = c.fetchone()
while row: row = c.fetchone()
if colnames[0].lower() == 'player_id': if colnames[0].lower() == 'player_id':
playerid = row[0] playerid = row[0]
else:
log.error("ERROR: query %s result does not have player_id as first column" % (query,)) while row:
break 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 " %d rows fetched, len(stat_dict) = %d" % (n, len(stat_dict))
#print "session stat_dict =", stat_dict #print "session stat_dict =", stat_dict
return stat_dict #return stat_dict
def get_player_id(self, config, site, player_name): def get_player_id(self, config, site, player_name):
c = self.connection.cursor() c = self.connection.cursor()
@ -491,6 +508,69 @@ class Database:
else: else:
return None return None
#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): def get_site_id(self, site):
c = self.get_cursor() c = self.get_cursor()
c.execute(self.sql.query['getSiteId'], (site,)) c.execute(self.sql.query['getSiteId'], (site,))
@ -855,6 +935,7 @@ class Database:
log.debug(self.sql.query['createSettingsTable']) log.debug(self.sql.query['createSettingsTable'])
c = self.get_cursor() c = self.get_cursor()
c.execute(self.sql.query['createSettingsTable']) c.execute(self.sql.query['createSettingsTable'])
log.debug(self.sql.query['createSitesTable']) log.debug(self.sql.query['createSitesTable'])
c.execute(self.sql.query['createSitesTable']) c.execute(self.sql.query['createSitesTable'])
c.execute(self.sql.query['createGametypesTable']) c.execute(self.sql.query['createGametypesTable'])
@ -867,9 +948,14 @@ class Database:
c.execute(self.sql.query['createHandsPlayersTable']) c.execute(self.sql.query['createHandsPlayersTable'])
c.execute(self.sql.query['createHandsActionsTable']) c.execute(self.sql.query['createHandsActionsTable'])
c.execute(self.sql.query['createHudCacheTable']) c.execute(self.sql.query['createHudCacheTable'])
#c.execute(self.sql.query['addTourneyIndex'])
#c.execute(self.sql.query['addHandsIndex']) # create unique indexes:
#c.execute(self.sql.query['addPlayersIndex']) 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.fillDefaultData()
self.commit() self.commit()
except: except:
@ -1011,20 +1097,48 @@ class Database:
c.execute("INSERT INTO Sites (name,currency) VALUES ('PartyPoker', 'USD')") c.execute("INSERT INTO Sites (name,currency) VALUES ('PartyPoker', 'USD')")
if self.backend == self.SQLITE: if self.backend == self.SQLITE:
c.execute("INSERT INTO TourneyTypes (id, siteId, buyin, fee) VALUES (NULL, 1, 0, 0);") 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 c.execute("""insert into TourneyTypes(siteId, buyin, fee, maxSeats, knockout
,rebuyOrAddon, speed, headsUp, shootout, matrix) ,rebuyOrAddon, speed, headsUp, shootout, matrix)
values (1, 0, 0, 0, False, False, null, False, False, False);""") 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 (1, 0, 0, 0, False, False, null, False, False, False);""")
#end def fillDefaultData #end def fillDefaultData
def rebuild_hudcache(self): def rebuild_hudcache(self, start=None):
"""clears hudcache and rebuilds from the individual handsplayers records""" """clears hudcache and rebuilds from the individual handsplayers records"""
try: try:
stime = time() 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_clause>', where)
self.get_cursor().execute(self.sql.query['clearHudCache']) self.get_cursor().execute(self.sql.query['clearHudCache'])
self.get_cursor().execute(self.sql.query['rebuildHudCache']) self.get_cursor().execute(rebuild_sql)
self.commit() self.commit()
print "Rebuild hudcache took %.1f seconds" % (time() - stime,) print "Rebuild hudcache took %.1f seconds" % (time() - stime,)
except: except:
@ -1033,6 +1147,39 @@ class Database:
print err print err
#end def rebuild_hudcache #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("<playerid_list>", 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): def analyzeDB(self):
"""Do whatever the DB can offer to update index/table statistics""" """Do whatever the DB can offer to update index/table statistics"""
@ -1623,6 +1770,8 @@ class Database:
#cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i])) #cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i]))
#result.append(cursor.fetchall()[0][0]) #result.append(cursor.fetchall()[0][0])
except: 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) ) raise FpdbError( "store_hands_players_holdem_omaha_tourney error: " + str(sys.exc_value) )
return result return result
@ -1855,27 +2004,40 @@ class Database:
#end def storeHudCache #end def storeHudCache
def store_tourneys(self, tourneyTypeId, siteTourneyNo, entries, prizepool, startTime): def store_tourneys(self, tourneyTypeId, siteTourneyNo, entries, prizepool, startTime):
ret = -1
try: 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 = self.get_cursor()
cursor.execute("SELECT id FROM Tourneys WHERE siteTourneyNo=%s AND tourneyTypeId+0=%s".replace('%s', self.sql.query['placeholder']) cursor.execute("savepoint ins_tourney")
, (siteTourneyNo, tourneyTypeId)) cursor.execute("""INSERT INTO Tourneys
tmp=cursor.fetchone() (tourneyTypeId, siteTourneyNo, entries, prizepool, startTime)
#print "tried SELECTing tourneys.id, result:",tmp VALUES (%s, %s, %s, %s, %s)""".replace('%s', self.sql.query['placeholder'])
,(tourneyTypeId, siteTourneyNo, entries, prizepool, startTime))
try: ret = self.get_last_insert_id(cursor)
len(tmp) #print "created new tourneys.id:",ret
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
except: except:
raise FpdbError( "store_tourneys error: " + str(sys.exc_value) ) #if str(sys.exc_value) contains 'sitetourneyno':
# raise FpdbError( "store_tourneys error: " + str(sys.exc_value) )
return tmp[0] #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 #end def store_tourneys
def store_tourneys_players(self, tourney_id, player_ids, payin_amounts, ranks, winnings): def store_tourneys_players(self, tourney_id, player_ids, payin_amounts, ranks, winnings):
@ -1888,26 +2050,32 @@ class Database:
#print "ranks:",ranks #print "ranks:",ranks
#print "winnings:",winnings #print "winnings:",winnings
for i in xrange(len(player_ids)): 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: try:
len(tmp) cursor.execute("savepoint ins_tplayer")
except TypeError:
cursor.execute("""INSERT INTO TourneysPlayers 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])) (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']), tmp = self.get_last_insert_id(cursor)
(tourney_id, player_ids[i])) result.append(tmp)
tmp=cursor.fetchone() #print "created new tourneys_players.id:", tmp
#print "created new tourneys_players.id:",tmp except:
result.append(tmp[0]) 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: except:
raise FpdbError( "store_tourneys_players error: " + str(sys.exc_value) ) raise FpdbError( "store_tourneys_players error: " + str(sys.exc_value) )
cursor.execute("release savepoint ins_tplayer")
#print "store_tourneys_players returning", result
return result return result
#end def store_tourneys_players #end def store_tourneys_players
@ -2130,10 +2298,10 @@ class Database:
def tStoreTourneyPlayers(self, tourney, dbTourneyId): def tStoreTourneyPlayers(self, tourney, dbTourneyId):
logging.debug("Database.tStoreTourneyPlayers") logging.debug("Database.tStoreTourneyPlayers")
# First, get playerids for the players and specifically the one for hero : # 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 may be None for matrix tourneys summaries
# hero = [ tourney.hero ] # 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)) # logging.debug("hero Id = %s - playersId = %s" % (heroId , playersIds))
tourneyPlayersIds=[] tourneyPlayersIds=[]

View File

@ -56,24 +56,50 @@ import Database
import Tables import Tables
import Hud import Hud
# To add to config: # To add to config: *** these vars now replaced by def_hud_params list
aggregate_stats = {"ring": False, "tour": False} # uses agg_bb_mult #aggregate_stats = {"ring": False, "tour": False} # uses agg_bb_mult
hud_style = 'A' # A=All-time #hud_style = 'A' # A=All-time
# S=Session # S=Session
# T=timed (last n days - set hud_days to required value) # T=timed (last n days - set hud_days to required value)
# Future values may also include: # Future values may also include:
# H=Hands (last n hands) # H=Hands (last n hands)
hud_days = 90 # Max number of days from each player to use for hud stats #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 #agg_bb_mult = 100 # 1 = no aggregation. When aggregating stats across levels larger blinds
# must be < (agg_bb_mult * smaller blinds) to be aggregated # must be < (agg_bb_mult * smaller blinds) to be aggregated
# ie. 100 will aggregate almost everything, 2 will probably agg just the # 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 # 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 #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 # (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 # by a 40 minute gap and then more hands on same table that is
# a new session) # a new session)
#hud_hands = 0 # Max number of hands from each player to use for hud stats (not used) #hud_hands = 0 # Max number of hands from each player to use for hud stats (not used)
# New list to hold all 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' : 'A'
, 'h_hud_days' : 90
, 'h_agg_bb_mult' : 1 # 1 means no aggregation
# , 'h_hud_session_gap' : 30 not currently used
}
class HUD_main(object): class HUD_main(object):
"""A main() object to own both the read_stdin thread and the gui.""" """A main() object to own both the read_stdin thread and the gui."""
# This class mainly provides state for controlling the multiple HUDs. # This class mainly provides state for controlling the multiple HUDs.
@ -82,6 +108,7 @@ class HUD_main(object):
self.db_name = db_name self.db_name = db_name
self.config = Configuration.Config(file=options.config, dbname=options.dbname) self.config = Configuration.Config(file=options.config, dbname=options.dbname)
self.hud_dict = {} self.hud_dict = {}
self.hud_params = def_hud_params
# a thread to read stdin # a thread to read stdin
gobject.threads_init() # this is required gobject.threads_init() # this is required
@ -104,7 +131,11 @@ class HUD_main(object):
# called by an event in the HUD, to kill this specific HUD # called by an event in the HUD, to kill this specific HUD
if table in self.hud_dict: if table in self.hud_dict:
self.hud_dict[table].kill() 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) self.vb.remove(self.hud_dict[table].tablehudlabel)
del(self.hud_dict[table]) del(self.hud_dict[table])
self.main_window.resize(1,1) self.main_window.resize(1,1)
@ -160,68 +191,98 @@ class HUD_main(object):
# be passed to HUDs for use in the gui thread. HUD objects should not # 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 # need their own access to the database, but should open their own
# if it is required. # if it is required.
self.db_connection = Database.Database(self.config) try:
self.db_connection.init_hud_stat_vars(hud_days) self.db_connection = Database.Database(self.config)
tourny_finder = re.compile('(\d+) (\d+)') tourny_finder = re.compile('(\d+) (\d+)')
while 1: # wait for a new hand number on stdin # get hero's screen names and player ids
new_hand_id = sys.stdin.readline() self.hero, self.hero_ids = {}, {}
new_hand_id = string.rstrip(new_hand_id) for site in self.config.get_supported_sites():
if new_hand_id == "": # blank line means quit result = self.db_connection.get_site_id(site)
self.destroy() if result:
break # this thread is not always killed immediately with gtk.main_quit() site_id = result[0][0]
# get basic info about the new hand from the db self.hero[site_id] = self.config.supported_sites[site].screen_name
# if there is a db error, complain, skip hand, and proceed self.hero_ids[site_id] = self.db_connection.get_player_id(self.config, site, self.hero[site_id])
try:
(table_name, max, poker_game, type) = self.db_connection.get_table_name(new_hand_id) while 1: # wait for a new hand number on stdin
stat_dict = self.db_connection.get_stats_from_hand(new_hand_id, aggregate_stats[type] new_hand_id = sys.stdin.readline()
,hud_style, agg_bb_mult) 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) cards = self.db_connection.get_cards(new_hand_id)
comm_cards = self.db_connection.get_common_cards(new_hand_id) comm_cards = self.db_connection.get_common_cards(new_hand_id)
if comm_cards != {}: # stud! if comm_cards != {}: # stud!
cards['common'] = comm_cards['common'] cards['common'] = comm_cards['common']
except Exception, err: except Exception, err:
err = traceback.extract_tb(sys.exc_info()[2])[-1] 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]) 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 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))) 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)))
continue continue
else:
temp_key = table_name
# Update an existing HUD if type == "tour": # hand is from a tournament
if temp_key in self.hud_dict: mat_obj = tourny_finder.search(table_name)
self.hud_dict[temp_key].stat_dict = stat_dict if mat_obj:
self.hud_dict[temp_key].cards = cards (tour_number, tab_number) = mat_obj.group(1, 2)
[aw.update_data(new_hand_id, self.db_connection) for aw in self.hud_dict[temp_key].aux_windows] temp_key = tour_number
self.update_HUD(new_hand_id, temp_key, self.config) else: # tourney, but can't get number and table
print "could not find tournament: skipping "
# Or create a new HUD #sys.stderr.write("Could not find tournament %d in hand %d. Skipping.\n" % (int(tour_number), int(new_hand_id)))
else: continue
if type == "tour":
tablewindow = Tables.discover_tournament_table(self.config, tour_number, tab_number)
else: else:
tablewindow = Tables.discover_table_by_name(self.config, table_name) temp_key = table_name
if tablewindow == None:
# If no client window is found on the screen, complain and continue # 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'] )
stat_dict = self.db_connection.get_stats_from_hand(new_hand_id, self.hud_dict[temp_key].hud_params, self.hero_ids)
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'] )
stat_dict = self.db_connection.get_stats_from_hand(new_hand_id, self.hud_params, self.hero_ids)
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": if type == "tour":
table_name = "%s %s" % (tour_number, tab_number) tablewindow = Tables.discover_tournament_table(self.config, tour_number, tab_number)
sys.stderr.write("table name "+table_name+" not found, skipping.\n") else:
else: tablewindow = Tables.discover_table_by_name(self.config, table_name)
self.create_HUD(new_hand_id, tablewindow, temp_key, max, poker_game, stat_dict, cards) if tablewindow == None:
self.db_connection.connection.rollback() # 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, 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__": if __name__== "__main__":

View File

@ -673,7 +673,14 @@ class HoldemOmahaHand(Hand):
if shown: self.shown.add(player) if shown: self.shown.add(player)
if mucked: self.mucked.add(player) if mucked: self.mucked.add(player)
else: 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): def getStreetTotals(self):
# street1Pot INT, /* pot size at flop/street4 */ # street1Pot INT, /* pot size at flop/street4 */

View File

@ -73,6 +73,7 @@ class Hud:
self.stacked = True self.stacked = True
self.site = table.site self.site = table.site
self.mw_created = False self.mw_created = False
self.hud_params = parent.hud_params
self.stat_windows = {} self.stat_windows = {}
self.popup_windows = {} self.popup_windows = {}
@ -143,6 +144,47 @@ class Hud:
repositem = gtk.MenuItem('Reposition StatWindows') repositem = gtk.MenuItem('Reposition StatWindows')
menu.append(repositem) menu.append(repositem)
repositem.connect("activate", self.reposition_windows) repositem.connect("activate", self.reposition_windows)
aggitem = gtk.MenuItem('Show Stats')
menu.append(aggitem)
aggMenu = gtk.Menu()
aggitem.set_submenu(aggMenu)
# set agg_bb_mult to 1 to stop aggregation
item = gtk.MenuItem('For This Blind Level')
item.ms = 1
aggMenu.append(item)
item.connect("activate", self.set_aggregation)
setattr(self, 'showStatsMenuItem1', item)
#
item = gtk.MenuItem('For Multiple Blind Levels:')
aggMenu.append(item)
setattr(self, 'showStatsMenuItem2', item)
#
item = gtk.MenuItem(' 0.5 to 2.0 x Current Blinds')
item.ms = 2.01
aggMenu.append(item)
item.connect("activate", self.set_aggregation)
setattr(self, 'showStatsMenuItem3', item)
#
item = gtk.MenuItem(' 0.33 to 3.0 x Current Blinds')
item.ms = 3.01
aggMenu.append(item)
item.connect("activate", self.set_aggregation)
setattr(self, 'showStatsMenuItem4', item)
#
item = gtk.MenuItem(' 0.1 to 10 x Current Blinds')
item.ms = 10.01
aggMenu.append(item)
item.connect("activate", self.set_aggregation)
setattr(self, 'showStatsMenuItem5', item)
#
item = gtk.MenuItem(' All Levels')
item.ms = 10000
aggMenu.append(item)
item.connect("activate", self.set_aggregation)
setattr(self, 'showStatsMenuItem6', item)
eventbox.connect_object("button-press-event", self.on_button_press, menu)
debugitem = gtk.MenuItem('Debug StatWindows') debugitem = gtk.MenuItem('Debug StatWindows')
menu.append(debugitem) menu.append(debugitem)
@ -176,9 +218,18 @@ class Hud:
self.create(*self.creation_attrs) self.create(*self.creation_attrs)
self.update(self.hand, self.config) self.update(self.hand, self.config)
except Exception, e: except Exception, e:
print "Expcetion:",str(e) print "Exception:",str(e)
pass pass
def set_aggregation(self, widget):
# try setting these to true all the time, and set the multiplier to 1 to turn agg off:
self.hud_params['aggregate_ring'] = True
self.hud_params['aggregate_tour'] = True
if self.hud_params['agg_bb_mult'] != widget.ms:
print 'set_aggregation', widget.ms
self.hud_params['agg_bb_mult'] = widget.ms
def update_table_position(self): def update_table_position(self):
if os.name == 'nt': if os.name == 'nt':
if not win32gui.IsWindow(self.table.number): if not win32gui.IsWindow(self.table.number):
@ -218,7 +269,11 @@ class Hud:
# heap dead, burnt bodies, blood 'n guts, veins between my teeth # heap dead, burnt bodies, blood 'n guts, veins between my teeth
for s in self.stat_windows.itervalues(): for s in self.stat_windows.itervalues():
s.kill_popups() s.kill_popups()
s.window.destroy() try:
# throws "invalid window handle" in WinXP (sometimes?)
s.window.destroy()
except:
pass
self.stat_windows = {} self.stat_windows = {}
# also kill any aux windows # also kill any aux windows
for aux in self.aux_windows: for aux in self.aux_windows:
@ -626,7 +681,7 @@ class Popup_window:
# window.window.reparent(self.table.gdkhandle, 0, 0) # window.window.reparent(self.table.gdkhandle, 0, 0)
window.window.set_transient_for(self.table.gdkhandle) window.window.set_transient_for(self.table.gdkhandle)
# window.present() # window.present()
if __name__== "__main__": if __name__== "__main__":
main_window = gtk.Window() main_window = gtk.Window()

View File

@ -331,7 +331,8 @@ class Sql:
speed varchar(10), speed varchar(10),
headsUp BOOLEAN NOT NULL DEFAULT False, headsUp BOOLEAN NOT NULL DEFAULT False,
shootout 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""" ENGINE=INNODB"""
elif db_server == 'postgresql': elif db_server == 'postgresql':
@ -346,7 +347,8 @@ class Sql:
speed varchar(10), speed varchar(10),
headsUp BOOLEAN NOT NULL DEFAULT False, headsUp BOOLEAN NOT NULL DEFAULT False,
shootout 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': elif db_server == 'sqlite':
self.query['createTourneyTypesTable'] = """CREATE TABLE TourneyTypes ( self.query['createTourneyTypesTable'] = """CREATE TABLE TourneyTypes (
@ -360,7 +362,8 @@ class Sql:
speed TEXT, speed TEXT,
headsUp BOOLEAN NOT NULL DEFAULT 0, headsUp BOOLEAN NOT NULL DEFAULT 0,
shootout 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, comment TEXT,
commentTs timestamp without time zone)""" commentTs timestamp without time zone)"""
elif db_server == 'sqlite': 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, comment TEXT,
commentTs timestamp without time zone)""" commentTs timestamp without time zone)"""
elif db_server == 'sqlite': 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': 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': 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': elif db_server == 'sqlite':
self.query['addHandsIndex'] = """ """ self.query['addTourneyIndex'] = """CREATE UNIQUE INDEX siteTourneyNo ON Tourneys (siteTourneyNo, tourneyTypeId)"""
if db_server == 'mysql': 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': 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': elif db_server == 'sqlite':
self.query['addHandsIndex'] = """ """ self.query['addHandsIndex'] = """CREATE UNIQUE INDEX siteHandNo ON Hands (siteHandNo, gameTypeId)"""
if db_server == 'mysql': 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': 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': 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" self.query['get_last_hand'] = "select max(id) from Hands"
@ -1188,7 +1232,7 @@ class Sql:
from Players, Sites from Players, Sites
where Players.name = %s where Players.name = %s
and Sites.name = %s and Sites.name = %s
and Players.SiteId = Sites.id and Players.siteId = Sites.id
""" """
self.query['getSiteId'] = """SELECT id from Sites where name = %s""" self.query['getSiteId'] = """SELECT id from Sites where name = %s"""
@ -1537,10 +1581,11 @@ class Sql:
""" """
self.query['get_table_name'] = """ self.query['get_table_name'] = """
select tableName, maxSeats, category, type select h.tableName, h.maxSeats, gt.category, gt.type, gt.siteId
from Hands,Gametypes from Hands h
where Hands.id = %s ,Gametypes gt
and Gametypes.id = Hands.gametypeId where h.id = %s
and gt.id = h.gametypeId
""" """
self.query['get_actual_seat'] = """ self.query['get_actual_seat'] = """
@ -2466,6 +2511,7 @@ class Sql:
,sum(street4CheckCallRaiseDone) ,sum(street4CheckCallRaiseDone)
FROM HandsPlayers hp FROM HandsPlayers hp
INNER JOIN Hands h ON (h.id = hp.handId) INNER JOIN Hands h ON (h.id = hp.handId)
<where_clause>
GROUP BY h.gametypeId GROUP BY h.gametypeId
,hp.playerId ,hp.playerId
,h.seats ,h.seats
@ -2614,6 +2660,7 @@ class Sql:
,sum(CAST(street4CheckCallRaiseDone as integer)) ,sum(CAST(street4CheckCallRaiseDone as integer))
FROM HandsPlayers hp FROM HandsPlayers hp
INNER JOIN Hands h ON (h.id = hp.handId) INNER JOIN Hands h ON (h.id = hp.handId)
<where_clause>
GROUP BY h.gametypeId GROUP BY h.gametypeId
,hp.playerId ,hp.playerId
,h.seats ,h.seats
@ -2762,6 +2809,7 @@ class Sql:
,sum(CAST(street4CheckCallRaiseDone as integer)) ,sum(CAST(street4CheckCallRaiseDone as integer))
FROM HandsPlayers hp FROM HandsPlayers hp
INNER JOIN Hands h ON (h.id = hp.handId) INNER JOIN Hands h ON (h.id = hp.handId)
<where_clause>
GROUP BY h.gametypeId GROUP BY h.gametypeId
,hp.playerId ,hp.playerId
,h.seats ,h.seats
@ -2770,9 +2818,14 @@ class Sql:
,'d' || substr(strftime('%Y%m%d', h.handStart),3,7) ,'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 <playerid_list>
and hc.styleKey like 'd%'"""
if db_server == 'mysql': if db_server == 'mysql':
self.query['analyze'] = """ self.query['analyze'] = """
analyze table Autorates, GameTypes, Hands, HandsPlayers, Hudcache, Players analyze table Autorates, GameTypes, Hands, HandsPlayers, HudCache, Players
, Settings, Sites, Tourneys, TourneysPlayers, TourneyTypes , Settings, Sites, Tourneys, TourneysPlayers, TourneyTypes
""" """
else: # assume postgres else: # assume postgres

View File

@ -248,7 +248,7 @@ class ttracker_main(object):
# get basic info about the new hand from the db # get basic info about the new hand from the db
# if there is a db error, complain, skip hand, and proceed # if there is a db error, complain, skip hand, and proceed
try: 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] stat_dict = self.db_connection.get_stats_from_hand(new_hand_id, aggregate_stats[type]
,hud_style, agg_bb_mult) ,hud_style, agg_bb_mult)

View File

@ -257,17 +257,65 @@ class fpdb:
def dia_recreate_hudcache(self, widget, data=None): def dia_recreate_hudcache(self, widget, data=None):
if self.obtain_global_lock(): 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." diastring = "Please confirm that you want to re-create the HUD cache."
dia_confirm.format_secondary_text(diastring) self.dia_confirm.format_secondary_text(diastring)
response = dia_confirm.run() hb = gtk.HBox(True, 1)
dia_confirm.destroy() 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: if response == gtk.RESPONSE_YES:
self.db.rebuild_hudcache() self.db.rebuild_hudcache( self.start_date.get_text() )
elif response == gtk.REPSONSE_NO: elif response == gtk.RESPONSE_NO:
print 'User cancelled rebuilding hud cache' print 'User cancelled rebuilding hud cache'
self.release_global_lock() 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): def dia_regression_test(self, widget, data=None):
self.warning_box("Unimplemented: Regression Test") self.warning_box("Unimplemented: Regression Test")

View File

@ -38,6 +38,8 @@ class fpdb_db:
MYSQL_INNODB = 2 MYSQL_INNODB = 2
PGSQL = 3 PGSQL = 3
SQLITE = 4 SQLITE = 4
sqlite_db_dir = ".." + os.sep + "database"
def __init__(self): def __init__(self):
"""Simple constructor, doesnt really do anything""" """Simple constructor, doesnt really do anything"""
self.db = None self.db = None
@ -119,7 +121,12 @@ class fpdb_db:
sqlite3 = pool.manage(sqlite3, pool_size=1) sqlite3 = pool.manage(sqlite3, pool_size=1)
else: else:
logging.warning("SQLite won't work well without 'sqlalchemy' installed.") 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_converter("bool", lambda x: bool(int(x)))
sqlite3.register_adapter(bool, lambda x: "1" if x else "0") sqlite3.register_adapter(bool, lambda x: "1" if x else "0")
else: else:

View File

@ -547,7 +547,7 @@ class Importer:
if self.callHud: if self.callHud:
#print "call to HUD here. handsId:",handsId #print "call to HUD here. handsId:",handsId
#pipe the Hands.id out to the HUD #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) self.caller.pipe_to_hud.stdin.write("%s" % (handsId) + os.linesep)
except Exceptions.DuplicateError: except Exceptions.DuplicateError:
duplicates += 1 duplicates += 1

View File

@ -118,7 +118,7 @@ def mainParser(settings, siteID, category, hand, config, db = None, writeq = Non
seatLines.append(line) seatLines.append(line)
names = fpdb_simple.parseNames(seatLines) 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) tmp = fpdb_simple.parseCashesAndSeatNos(seatLines)
startCashes = tmp['startCashes'] startCashes = tmp['startCashes']
seatNos = tmp['seatNos'] seatNos = tmp['seatNos']

View File

@ -913,92 +913,54 @@ def recogniseGametypeID(backend, db, cursor, topline, smallBlindLine, site_id, c
#end def recogniseGametypeID #end def recogniseGametypeID
def recogniseTourneyTypeId(db, siteId, tourneySiteId, buyin, fee, knockout, rebuyOrAddon): def recogniseTourneyTypeId(db, siteId, tourneySiteId, buyin, fee, knockout, rebuyOrAddon):
ret = -1
cursor = db.get_cursor() 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) # 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) # 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) #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)) cursor.execute (db.sql.query['getTourneyTypeIdByTourneyNo'].replace('%s', db.sql.query['placeholder']), (tourneySiteId, siteId))
result=cursor.fetchone() result = cursor.fetchone()
try: try:
len(result) len(result)
ret = result[0]
except: 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)) cursor.execute( """SELECT id FROM TourneyTypes
result=cursor.fetchone() WHERE siteId=%s AND buyin=%s AND fee=%s
#print "tried SELECTing gametypes.id, result:",result AND knockout=%s AND rebuyOrAddon=%s"""
, (siteId, buyin, fee, knockout, rebuyOrAddon) )
result = cursor.fetchone()
#print "tried selecting tourneytypes.id, result:", result
try: try:
len(result) len(result)
ret = result[0]
except TypeError:#this means we need to create a new entry 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)) #print "insert new tourneytype record ..."
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)) try:
result=cursor.fetchone() 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 #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 #recognises the name in the given line and returns its array position in the given array
def recognisePlayerNo(line, names, atype): def recognisePlayerNo(line, names, atype):