diff --git a/packaging/debian/changelog b/packaging/debian/changelog new file mode 100644 index 00000000..84ecbce5 --- /dev/null +++ b/packaging/debian/changelog @@ -0,0 +1,5 @@ +free-poker-tools (0.10.99) unstable; urgency=low + + * Initial packaging release. + + -- Mika Bostrom Thu, 20 Aug 2009 06:30:53 +0300 diff --git a/packaging/debian/compat b/packaging/debian/compat new file mode 100644 index 00000000..7f8f011e --- /dev/null +++ b/packaging/debian/compat @@ -0,0 +1 @@ +7 diff --git a/packaging/debian/control b/packaging/debian/control new file mode 100644 index 00000000..93660791 --- /dev/null +++ b/packaging/debian/control @@ -0,0 +1,24 @@ +Source: free-poker-tools +Maintainer: Mika Bostrom +Section: games +Priority: extra +Build-Depends: debhelper, python-support +Standards-Version: 3.8.0 + +Package: python-fpdb +Architecture: any +Section: games +Priority: extra +Depends: ${python:Depends}, python-gtk2, python-matplotlib, + python-support, mysql-server | postgresql | python-pysqlite2, + python-psycopg2 | python-mysqldb +Suggests: wine +Description: free poker database with HUD + FPDB is a statistics tool for online poker. It supports most sites + and several games. Most prominent feature is its heads-up display + (HUD) which shows statistical details for players in real time. + . + Due to the fact that most online poker clients are Windows-only, + you may need to install wine. + . + FPDB is under heavy development. diff --git a/packaging/debian/copyright b/packaging/debian/copyright new file mode 100644 index 00000000..8796c11a --- /dev/null +++ b/packaging/debian/copyright @@ -0,0 +1,7 @@ +This package was debianised by Mika Bostrom + +Upstream authors: ... + +License: AGPL + +Copyright (C) 2008- The FPDB developers diff --git a/packaging/debian/links b/packaging/debian/links new file mode 100644 index 00000000..01d1c490 --- /dev/null +++ b/packaging/debian/links @@ -0,0 +1 @@ +/usr/share/python-support/python-fpdb/fpdb/fpdb.py /usr/bin/fpdb diff --git a/packaging/debian/python-fpdb.postinst b/packaging/debian/python-fpdb.postinst new file mode 100644 index 00000000..1f618958 --- /dev/null +++ b/packaging/debian/python-fpdb.postinst @@ -0,0 +1,5 @@ +#!/bin/sh + +# When installed into .../fpdb/ the script gets mode 644 +# Note: "dh_fixperms -Xfpdb.py" did not work, hence this hack +chmod 755 /usr/bin/fpdb diff --git a/packaging/debian/pyversions b/packaging/debian/pyversions new file mode 100644 index 00000000..8b253bc3 --- /dev/null +++ b/packaging/debian/pyversions @@ -0,0 +1 @@ +2.4- diff --git a/packaging/debian/rules b/packaging/debian/rules new file mode 100755 index 00000000..2ae2c776 --- /dev/null +++ b/packaging/debian/rules @@ -0,0 +1,45 @@ +#!/usr/bin/make -f +# -*- makefile -*- + +PACKAGE := python-fpdb + +build: build-stamp +build-stamp: + dh_testdir + python setup.py build + touch $@ + +clean: + dh_testdir + dh_testroot + python setup.py clean + rm -rf build + dh_clean build-stamp + +install: build + dh_testdir + dh_testroot + dh_prep || dh_clean -k + dh_installdirs + # + python setup.py install --root=debian/$(PACKAGE) --prefix=/usr --no-compile + +binary-indep: build install + dh_testdir + dh_testroot + dh_installchangelogs + dh_installdocs + dh_link + dh_compress + dh_fixperms + dh_pysupport + dh_installdeb + dh_gencontrol + dh_md5sums + dh_builddeb + +binary-arch: build install + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install + 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/Database.py b/pyfpdb/Database.py index 86010d4e..b9ec62f4 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,6 +186,7 @@ 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 @@ -223,9 +227,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 @@ -367,8 +372,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 +390,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 +417,48 @@ 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) + if hud_style == 'S' and h_hud_style == 'S': + return stat_dict - else: # hud_style == A + 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 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 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: - 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 aggregate: always use aggreagte 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,7 +466,6 @@ 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): @@ -441,7 +476,13 @@ class Database: return stat_dict # uses query on handsplayers instead of hudcache to get stats on just this session - def get_stats_from_hand_session(self, hand): + 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 +497,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': + playerid = row[0] + + # Loop through stats adding them to appropriate stat_dict: + while row: + 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 +535,69 @@ class Database: else: 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): c = self.get_cursor() c.execute(self.sql.query['getSiteId'], (site,)) @@ -855,6 +962,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 +975,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 +1124,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 (1, 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 +1174,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""" @@ -1182,6 +1356,10 @@ class Database: return result #end def store_the_hand +########################### +# NEWIMPORT CODE +########################### + def storeHand(self, p): #stores into table hands: q = """INSERT INTO Hands ( @@ -1257,6 +1435,109 @@ 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 + + pass + + +################################# +# Finish of NEWIMPORT CODE +################################# + + + def storeHands(self, backend, site_hand_no, gametype_id ,hand_start_time, names, tableName, maxSeats, hudCache ,board_values, board_suits): @@ -1516,6 +1797,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 @@ -1748,27 +2031,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): @@ -1781,26 +2077,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 @@ -2023,10 +2325,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=[] @@ -2240,12 +2542,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/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/HUD_main.py b/pyfpdb/HUD_main.py index b1164b26..23c1ef03 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 = def_hud_params # 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..bd905036 100644 --- a/pyfpdb/Hand.py +++ b/pyfpdb/Hand.py @@ -673,7 +673,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 77143d79..2c83ccd0 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 = {} @@ -143,6 +145,78 @@ class Hud: repositem = gtk.MenuItem('Reposition StatWindows') menu.append(repositem) repositem.connect("activate", self.reposition_windows) + + aggitem = gtk.MenuItem('Show 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, 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, 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, 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, 10) + setattr(self, 'aggBBmultItem10', item) + # + item = gtk.CheckMenuItem(' All Levels') + self.aggMenu.append(item) + item.connect("activate", self.set_aggregation, 10000) + setattr(self, 'aggBBmultItem10000', item) + # + item = gtk.MenuItem('For Hero:') + self.aggMenu.append(item) + setattr(self, 'showStatsMenuItem7', item) + # + item = gtk.CheckMenuItem(' All Time') + self.aggMenu.append(item) + item.connect("activate", self.set_hud_style, 'HA') + setattr(self, 'HAStyleOption', item) + # + item = gtk.CheckMenuItem(' Session') + self.aggMenu.append(item) + item.connect("activate", self.set_hud_style, 'HS') + setattr(self, 'HSStyleOption', item) + # + item = gtk.CheckMenuItem(' %s Days' % (self.hud_params['h_hud_days'])) + self.aggMenu.append(item) + item.connect("activate", self.set_hud_style, 'HT') + setattr(self, 'HTStyleOption', item) + + # set active on current options: + 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, 'aggBBmultItemAll').set_active(True) + if self.hud_params['h_hud_style'] == 'A': + getattr(self, 'HAStyleOption').set_active(True) + elif self.hud_params['h_hud_style'] == 'S': + getattr(self, 'HSStyleOption').set_active(True) + elif self.hud_params['h_hud_style'] == 'T': + getattr(self, 'HTStyleOption').set_active(True) + + eventbox.connect_object("button-press-event", self.on_button_press, menu) debugitem = gtk.MenuItem('Debug StatWindows') menu.append(debugitem) @@ -176,9 +250,46 @@ 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): + # 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 + self.hud_params['h_aggregate_ring'] = True + self.hud_params['h_aggregate_tour'] = True + + if self.hud_params['agg_bb_mult'] != val \ + and getattr(self, 'aggBBmultItem'+str(val)).get_active(): + print 'set_aggregation', val + self.hud_params['agg_bb_mult'] = val + self.hud_params['h_agg_bb_mult'] = val + for mult in ('1', '2', '3', '10', '10000'): + if mult != str(val): + getattr(self, 'aggBBmultItem'+mult).set_active(False) + + def set_hud_style(self, widget, val): + # try setting these to true all the time, and set the multiplier to 1 to turn agg off: + if val[0] == 'H': + param = 'h_hud_style' + else: + param = 'hud_style' + if val[1] == 'A' and getattr(self, 'HAStyleOption').get_active(): + self.hud_params[param] = 'A' + getattr(self, 'HSStyleOption').set_active(False) + getattr(self, 'HTStyleOption').set_active(False) + elif val[1] == 'S' and getattr(self, 'HSStyleOption').get_active(): + self.hud_params[param] = 'S' + getattr(self, 'HAStyleOption').set_active(False) + getattr(self, 'HTStyleOption').set_active(False) + elif val[1] == 'T' and self.HTStyleOption.get_active(): + self.hud_params[param] = 'T' + getattr(self, 'HAStyleOption').set_active(False) + getattr(self, 'HSStyleOption').set_active(False) + print "setting self.hud_params[%s] = %s" % (param, val[1]) + def update_table_position(self): if os.name == 'nt': if not win32gui.IsWindow(self.table.number): @@ -218,7 +329,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: @@ -626,7 +741,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..0c551634 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,7 @@ 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['getSiteId'] = """SELECT id from Sites where name = %s""" @@ -1340,28 +1384,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 +1598,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'] = """ @@ -2466,6 +2528,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 +2677,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 +2826,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 +2835,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..d9efff6a 100755 --- a/pyfpdb/Tables.py +++ b/pyfpdb/Tables.py @@ -376,7 +376,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..4703d96d 100755 --- a/pyfpdb/fpdb.py +++ b/pyfpdb/fpdb.py @@ -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") diff --git a/pyfpdb/fpdb_db.py b/pyfpdb/fpdb_db.py index 2b73a40e..428aa173 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 @@ -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 4ce1f678..02300315 100644 --- a/pyfpdb/fpdb_parse_logic.py +++ b/pyfpdb/fpdb_parse_logic.py @@ -81,7 +81,7 @@ def mainParser(settings, siteID, category, hand, config, db = None, writeq = Non fee = fpdb_simple.parseFee(hand[0]) entries = -1 #todo: parse this prizepool = -1 #todo: parse this - knockout = 0 + knockout = False tourneyStartTime= handStartTime #todo: read tourney start time rebuyOrAddon = fpdb_simple.isRebuyOrAddon(hand[0]) @@ -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): diff --git a/setup.py b/setup.py new file mode 100644 index 00000000..668693cf --- /dev/null +++ b/setup.py @@ -0,0 +1,17 @@ +# setup.py +# Python packaging for fpdb + +from distutils.core import setup + +setup(name = 'fpdb', + description = 'Free Poker Database', + version = '0.10.999', + author = 'FPDB team', + author_email = 'fpdb-main@lists.sourceforge.net', + packages = ['fpdb'], + package_dir = { 'fpdb' : 'pyfpdb' }, + data_files = [ + ('/usr/share/doc/python-fpdb', + ['docs/readme.txt', 'docs/release-notes.txt', + 'docs/tabledesign.html', 'THANKS.txt'])] +)