diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 1670801e..00000000 --- a/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ - -*.pyc \ No newline at end of file diff --git a/packaging/windows/py2exe_setup.py b/packaging/windows/py2exe_setup.py index 8bf723a4..84b75886 100644 --- a/packaging/windows/py2exe_setup.py +++ b/packaging/windows/py2exe_setup.py @@ -68,7 +68,6 @@ import matplotlib import shutil #from datetime import date - def isSystemDLL(pathname): #dwmapi appears to be vista-specific file, not XP if os.path.basename(pathname).lower() in ("dwmapi.dll"): @@ -112,7 +111,7 @@ def copy_file(source,destination): shutil.copy( source, destination ) -fpdbver = '0.20.906' +fpdbver = '0.21.rc1' distdir = r'fpdb-' + fpdbver rootdir = r'../../' #cwd is normally /packaging/windows @@ -120,6 +119,7 @@ pydir = rootdir+'pyfpdb/' gfxdir = rootdir+'gfx/' sys.path.append( pydir ) # allows fpdb modules to be found by options/includes below + print "\n" + r"Output will be created in "+distdir print "*** Cleaning working folders ***" @@ -142,15 +142,19 @@ setup( {'script': pydir+'Configuration.py', } ], + console = [ {'script': pydir+'Stove.py', } + ], + options = {'py2exe': { 'packages' : ['encodings', 'matplotlib'], 'includes' : ['gio', 'cairo', 'pango', 'pangocairo', 'atk', 'gobject' ,'matplotlib.numerix.random_array' ,'AbsoluteToFpdb', 'BetfairToFpdb' ,'CarbonToFpdb', 'EverleafToFpdb' - ,'FulltiltToFpdb', 'OnGameToFpdb' - ,'PartyPokerToFpdb', 'PokerStarsToFpdb' - ,'UltimateBetToFpdb', 'Win2dayToFpdb' + ,'FulltiltToFpdb', 'iPokerToFpdb' + ,'OnGameToFpdb', 'PartyPokerToFpdb' + ,'PkrToFpdb', 'PokerStarsToFpdb' + ,'Win2dayToFpdb', 'WinamaxToFpdb' ], 'excludes' : ['_tkagg', '_agg2', 'cocoaagg', 'fltkagg'], 'dll_excludes': ['libglade-2.0-0.dll', 'libgdk-win32-2.0-0.dll', 'libgobject-2.0-0.dll' diff --git a/packaging/windows/pypoker138walkthrough.txt b/packaging/windows/pypoker138walkthrough.txt new file mode 100644 index 00000000..1c7ee0f4 --- /dev/null +++ b/packaging/windows/pypoker138walkthrough.txt @@ -0,0 +1,191 @@ +pypokereval build for windows stepbystep guide +---------------------------------------------- + +Created by Gimick on 3rd December 2010 + +This walkthrough is derived with the assistance of EricBlade and the build notes +supplied by Loic Dachary http://dachary.org/ + +Content is available under the the GNU Affero General Public License version 3 + + +0. Build environ +---------------- + +We are building against the 2008 runtime because Python 2.6 + has the same dependency (msvcr90.dll version 9.0.21022.8) + +Using winXPhome 32 bit + +1 Visual studio +--------------- + +1.1/ Get the ISO CD from here ... http://www.microsoft.com/express/Downloads/#2008-All + +1.2/ Run and install Visual C++ only, don't bother with the additional packages offered + +This package will run 30 days before registration is needed + +2. Python runtime +----------------- + +2.1/ Install python runtime from here ... + +Python 2.6.5 ... http://www.python.org/ftp/python/2.6.5/python-2.6.5.msi + +3. Source install +----------------- + +3.1/ grab sources from here + +pypoker-eval v138 ... http://download.gna.org/pokersource/sources/pypoker-eval-138.0.tar.gz +poker-eval v138 ... http://download.gna.org/pokersource/sources/poker-eval-138.0.tar.gz + +3.2/ unpack and place the pypoker-eval-138 directory in c:\ +3.2.1/ rename to pypoker-eval + +3.3/ unpack and place the poker-eval-138 directory in c:\ +3.3.1/ rename to poker-eval + +Important: the build will fail with bizarre missing header files if the project is placed + in a directory containing a space character - you have been warned! + +4. Update source file +--------------------- + +4.1/ dos> write c:/pypoker-eval/pypokereval.c + +change this: + +#define VERSION_NAME(W) W##2_4 +#define PYTHON_VERSION "2_4" + +to be this: + +#define VERSION_NAME(W) W##2_6 +#define PYTHON_VERSION "2_6" + +4.2/ save and exit + +4.3/ dos> write c:/pypoker-eval/pokereval.py + +Comment-out this line: + +_pokereval = __import__('_pokereval_' + sys.version[0] + '_' + sys.version[2]) + +Insert this one in its' place: + +import _pokereval_2_6 as _pokereval + + +4.4/ save and exit + + +5. Build pre-preparation +------------------------ + +(Here we are converting the two project definition files to 2008) + +5.1 navigate to directory c:/poker-eval +5.1.1 double click poker-eval.vcproj +5.1.2 Visual studio will launch and make a conversion - accept all defaults +5.1.3 exit and save + +5.2 navigate to directory c:/pypoker-eval +5.2.1 double click pypoker-eval.vcproj +5.2.2 Visual studio will launch and make a conversion - accept all defaults +5.2.3 exit + +6. build preparation +-------------------- + +6.2 navigate to directory c:/pypoker-eval +6.2.1 double click pypoker-eval.vcproj - visual studio should launch + +6.2.3 Select Build...configuration manager... + Select "active solution configuration" to "Release" + (The configuration for both projects will change to "Release") + +6.2.3 Close the configuration manager + +6.2.4 In the solution explorer window, hilight pythonpoker-eval / right mouse / properties... + +6.2.5 In the pythonpoker-eval properties dialog, + +change references to "python24" to "python26" in the following: + + = C/C++/Additional Include Directories/ + = linker/general/Additional library directories + = linker/input/Additional Dependencies + +Change the following + + = linker/generate debug info - set to No + = linker/debugging/Generate debug info - set to No + +6.2.6 Apply all changes to the properties dialog and close + +6.3 Exit from visual studio + +7. Build poker eval +------------------- + +7.1 navigate to directory c:/poker-eval +7.1.1 double click poker-eval.vcproj +7.1.2 Visual studio will launch + +7.2 In the solution explorer window, hilight poker-eval / right mouse / build + +7.3 There should be no errors + +7.4 Exit from visual studio + + +8. Build pypoker eval +--------------------- + +8.1 navigate to directory c:/pypoker-eval +8.1.1 double click pypoker-eval.vcproj +8.1.2 Visual studio will launch + +8.2 In the solution explorer window, hilight pythonpoker-eval / right mouse / build + +8.3 There should be no errors (but a few warnings) + +8.4 Exit from visual studio + +9. packaging +------------ + +9.1 Navigate to c:/pypoker-eval/release +9.2 the output file is pypokereval.dll +9.3 rename this file to _pokereval_2_6.pyd + +9.4 create a zip file containing : + +_pokereval_2_6.pyd from releases +test.py from pypoker-eval-138.0 +pokereval.py from pypoker-eval-138.0 +poker-eval.vcproj from c:\poker-eval +pypoker-eval.vcproj from c:\pypoker-eval +pypokereval.c from c:\pypoker-eval + +Remember to include the version (138), python 265 and win32 in the package filename + +10. Installation and Testing +---------------------------- + +Python 2.6.5 must be installed + +10.1 Extract this package to directory +10.2 Change directory to the directory in 10.1 +10.3 execute dos> c:\Python26\python.exe test.py +10.4 hand-output should scroll down the screen +10.5 start the python interpreter +10.6 >>> import pokereval +10.7 No errors should be seen + + + + + diff --git a/pyfpdb/Configuration.py b/pyfpdb/Configuration.py index ed12f7a4..a0ad4bad 100644 --- a/pyfpdb/Configuration.py +++ b/pyfpdb/Configuration.py @@ -480,12 +480,13 @@ class Import: self.hhBulkPath = node.getAttribute("hhBulkPath") self.saveActions = string_to_bool(node.getAttribute("saveActions"), default=False) self.cacheSessions = string_to_bool(node.getAttribute("cacheSessions"), default=False) + self.sessionTimeout = string_to_bool(node.getAttribute("sessionTimeout"), default=30) self.fastStoreHudCache = string_to_bool(node.getAttribute("fastStoreHudCache"), default=False) self.saveStarsHH = string_to_bool(node.getAttribute("saveStarsHH"), default=False) def __str__(self): return " interval = %s\n callFpdbHud = %s\n hhArchiveBase = %s\n saveActions = %s\n fastStoreHudCache = %s\n" \ - % (self.interval, self.callFpdbHud, self.hhArchiveBase, self.saveActions, self.cacheSessions, self.fastStoreHudCache) + % (self.interval, self.callFpdbHud, self.hhArchiveBase, self.saveActions, self.cacheSessions, self.sessionTimeout, self.fastStoreHudCache) class HudUI: def __init__(self, node): @@ -1263,6 +1264,9 @@ class Config: try: imp['cacheSessions'] = self.imp.cacheSessions except: imp['cacheSessions'] = False + + try: imp['sessionTimeout'] = self.imp.sessionTimeout + except: imp['sessionTimeout'] = 30 try: imp['saveStarsHH'] = self.imp.saveStarsHH except: imp['saveStarsHH'] = False diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index b644cbd5..567ab166 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -73,7 +73,7 @@ except ImportError: use_numpy = False -DB_VERSION = 146 +DB_VERSION = 147 # Variance created as sqlite has a bunch of undefined aggregate functions. @@ -260,6 +260,8 @@ class Database: if 'day_start' in gen: self.day_start = float(gen['day_start']) + + self.sessionTimeout = float(self.import_options['sessionTimeout']) # where possible avoid creating new SQL instance by using the global one passed in if sql is None: @@ -312,7 +314,7 @@ class Database: tables=self.cursor.execute(self.sql.query['list_tables']) tables=self.cursor.fetchall() - for table in (u'Actions', u'Autorates', u'Backings', u'Gametypes', u'Hands', u'HandsActions', u'HandsPlayers', u'HudCache', u'Players', u'RawHands', u'RawTourneys', u'Settings', u'Sites', u'TourneyTypes', u'Tourneys', u'TourneysPlayers'): + for table in (u'Actions', u'Autorates', u'Backings', u'Gametypes', u'Hands', u'HandsActions', u'HandsPlayers', u'HudCache', u'SessionsCache', u'Players', u'RawHands', u'RawTourneys', u'Settings', u'Sites', u'TourneyTypes', u'Tourneys', u'TourneysPlayers'): print "table:", table result+="###################\nTable "+table+"\n###################\n" rows=self.cursor.execute(self.sql.query['get'+table]) @@ -913,6 +915,56 @@ class Database: result = c.fetchall() return result + def resetPlayerIDs(self): + self.pcache = None + + def getSqlPlayerIDs(self, pnames, siteid): + result = {} + if(self.pcache == None): + self.pcache = LambdaDict(lambda key:self.insertPlayer(key[0], key[1])) + + for player in pnames: + result[player] = self.pcache[(player,siteid)] + # NOTE: Using the LambdaDict does the same thing as: + #if player in self.pcache: + # #print "DEBUG: cachehit" + # pass + #else: + # self.pcache[player] = self.insertPlayer(player, siteid) + #result[player] = self.pcache[player] + + return result + + def insertPlayer(self, name, site_id): + result = None + _name = Charset.to_db_utf8(name) + c = self.get_cursor() + q = "SELECT name, id FROM Players WHERE siteid=%s and name=%s" + q = q.replace('%s', self.sql.query['placeholder']) + + #NOTE/FIXME?: MySQL has ON DUPLICATE KEY UPDATE + #Usage: + # INSERT INTO `tags` (`tag`, `count`) + # VALUES ($tag, 1) + # ON DUPLICATE KEY UPDATE `count`=`count`+1; + + + #print "DEBUG: name: %s site: %s" %(name, site_id) + + c.execute (q, (site_id, _name)) + + tmp = c.fetchone() + if (tmp == None): #new player + c.execute ("INSERT INTO Players (name, siteId) VALUES (%s, %s)".replace('%s',self.sql.query['placeholder']) + ,(_name, site_id)) + #Get last id might be faster here. + #c.execute ("SELECT id FROM Players WHERE name=%s", (name,)) + result = self.get_last_insert_id(c) + else: + result = tmp[1] + return result + + def get_last_insert_id(self, cursor=None): ret = None try: @@ -1177,6 +1229,7 @@ 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['createSessionsCacheTable']) c.execute(self.sql.query['createBackingsTable']) c.execute(self.sql.query['createRawHands']) c.execute(self.sql.query['createRawTourneys']) @@ -1543,6 +1596,11 @@ class Database: print _("Error rebuilding hudcache:"), str(sys.exc_value) print err #end def rebuild_hudcache + + def rebuild_sessionscache(self, h_start=None, v_start=None): + """clears sessionscache and rebuilds from the individual handsplayers records""" + #Will get to this soon + pass def get_hero_hudcache_start(self): """fetches earliest stylekey from hudcache for one of hero's player ids""" @@ -1967,52 +2025,135 @@ class Database: #print "DEBUG: Successfully updated HudCacho using UPDATE" pass - def storeSessionsCache(self, pids, starttime, pdata): + def storeSessionsCache(self, pids, startTime, game, pdata): """Update cached sessions. If update fails because no record exists, do an insert.""" - #In development - pass + + THRESHOLD = timedelta(seconds=int(self.sessionTimeout * 60)) #convert minutes to seconds + bigBet = int(Decimal(game['bb'])*200) + + check_sessionscache = self.sql.query['check_sessionscache'] + check_sessionscache = check_sessionscache.replace('%s', self.sql.query['placeholder']) + update_sessionscache = self.sql.query['update_sessionscache'] + update_sessionscache = update_sessionscache.replace('%s', self.sql.query['placeholder']) + update_sessionscache_start = self.sql.query['update_sessionscache_start'] + update_sessionscache_start = update_sessionscache_start.replace('%s', self.sql.query['placeholder']) + update_sessionscache_end = self.sql.query['update_sessionscache_end'] + update_sessionscache_end = update_sessionscache_end.replace('%s', self.sql.query['placeholder']) + insert_sessionscache = self.sql.query['insert_sessionscache'] + insert_sessionscache = insert_sessionscache.replace('%s', self.sql.query['placeholder']) + merge_sessionscache = self.sql.query['merge_sessionscache'] + merge_sessionscache = merge_sessionscache.replace('%s', self.sql.query['placeholder']) + delete_sessions = self.sql.query['delete_sessions'] + delete_sessions = delete_sessions.replace('%s', self.sql.query['placeholder']) + + #Grab playerIds using hero names in HUD_Config.xml + try: + # derive list of program owner's player ids + self.hero = {} # name of program owner indexed by site id + self.hero_ids = [] + # 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.append(int(p_id)) - #update_sessionscache = self.sql.query['update_sessionscache'] - #update_sessionscache = update_sessionscache.replace('%s', self.sql.query['placeholder']) - #insert_sessionscache = self.sql.query['insert_sessionscache'] - #insert_sessionscache = insert_sessionscache.replace('%s', self.sql.query['placeholder']) - #merge_sessionscache = self.sql.query['merge_sessionscache'] - #merge_sessionscache = merge_sessionscache.replace('%s', self.sql.query['placeholder']) + except: + err = traceback.extract_tb(sys.exc_info()[2])[-1] + print _("Error aquiring hero ids:"), str(sys.exc_value) + print err - #print "DEBUG: %s %s %s" %(hid, pids, pdata) - #inserts = [] - #for p in pdata: - #line = [0]*5 + inserts = [] + for p in pdata: + if pids[p] in self.hero_ids: + line = [0]*5 + + if (game['type']=='ring'): line[0] = 1 # count ring hands + if (game['type']=='tour'): line[1] = 1 # count tour hands + if (game['type']=='ring'): line[2] = pdata[p]['totalProfit'] #sum of profit + if (game['type']=='ring'): line[3] = float(Decimal(pdata[p]['totalProfit'])/Decimal(bigBet)) #sum of big bets won + line[4] = startTime + inserts.append(line) - #line[0] = 1 # HDs - #line[1] = pdata[p]['totalProfit'] + cursor = self.get_cursor() + + for row in inserts: + check = [] + check.append(row[-1]-THRESHOLD) + check.append(row[-1]+THRESHOLD) + num = cursor.execute(check_sessionscache, check) + #DEBUG log.info(_("check yurself: '%s'") % (num.rowcount)) - #line[2] = pids[p] # playerId - #line[3] = sessionStart - #line[4] = sessionEnd - #inserts.append(line) - - - #cursor = self.get_cursor() - - #for row in inserts: # Try to do the update first: - #num = cursor.execute(update_sessionscache, row) - #print "DEBUG: values: %s" % row[-3:] - # Test statusmessage to see if update worked, do insert if not - # num is a cursor in sqlite - #if ((self.backend == self.PGSQL and cursor.statusmessage != "UPDATE 1") - #or (self.backend == self.MYSQL_INNODB and num == 0) - #or (self.backend == self.SQLITE and num.rowcount == 0)): - #move the last 6 items in WHERE clause of row from the end of the array + if ((self.backend == self.PGSQL and cursor.statusmessage == "UPDATE 1") + or (self.backend == self.MYSQL_INNODB and num == 1) + or (self.backend == self.SQLITE and num.rowcount == 1)): + update = row + row[-1:] + mid = cursor.execute(update_sessionscache, update) + #DEBUG log.info(_("update '%s' rows, no change to session times ") % str(mid.rowcount)) + if ((self.backend == self.PGSQL and cursor.statusmessage != "UPDATE 1") + or (self.backend == self.MYSQL_INNODB and mid == 0) + or (self.backend == self.SQLITE and mid.rowcount == 0)): + update_start = row[-1:] + row + check + start = cursor.execute(update_sessionscache_start, update_start) + #DEBUG log.info(_("update '%s' rows, and updated sessionStart") % str(start.rowcount)) + if ((self.backend == self.PGSQL and cursor.statusmessage != "UPDATE 1") + or (self.backend == self.MYSQL_INNODB and start == 0) + or (self.backend == self.SQLITE and start.rowcount == 0)): + update_end = row[-1:] + row + check + end = cursor.execute(update_sessionscache_end, update_end) + #DEBUG log.info(_("update '%s' rows, and updated sessionEnd") % str(end.rowcount)) + else: + pass + else: + pass + elif ((self.backend == self.PGSQL and cursor.statusmessage != "UPDATE 1" and "UPDATE" in cursor.statusmessage) + or (self.backend == self.MYSQL_INNODB and num > 1) + or (self.backend == self.SQLITE and num.rowcount > 1)): + #DEBUG log.info(_("multiple matches")) + pass + #merge two sessions if there are multiple matches + cursor.execute(merge_sessionscache, check) + merge = cursor.fetchone() + cursor.execute(delete_sessions, check) + cursor.execute(insert_sessionscache, merge) + update = row + row[-1:] + mid = cursor.execute(update_sessionscache, update) + #DEBUG log.info(_("update '%s' rows, no change to session times ") % str(mid.rowcount)) + if ((self.backend == self.PGSQL and cursor.statusmessage != "UPDATE 1") + or (self.backend == self.MYSQL_INNODB and mid == 0) + or (self.backend == self.SQLITE and mid.rowcount == 0)): + update_start = row[-1:] + row + check + start = cursor.execute(update_sessionscache_start, update_start) + #DEBUG log.info(_("update '%s' rows, and updated sessionStart") % str(start.rowcount)) + if ((self.backend == self.PGSQL and cursor.statusmessage != "UPDATE 1") + or (self.backend == self.MYSQL_INNODB and start == 0) + or (self.backend == self.SQLITE and start.rowcount == 0)): + update_end = row[-1:] + row + check + end = cursor.execute(update_sessionscache_end, update_end) + #DEBUG log.info(_("update '%s' rows, and updated sessionEnd") % str(end.rowcount)) + else: + pass + else: + pass + + elif ((self.backend == self.PGSQL and cursor.statusmessage != "UPDATE 1") + or (self.backend == self.MYSQL_INNODB and num == 0) + or (self.backend == self.SQLITE and num.rowcount == 0)): + #move the last 2 items in WHERE clause of row from the end of the array # to the beginning for the INSERT statement #print "DEBUG: using INSERT: %s" % num - #row = row[-3:] + row[:-3] - #num = cursor.execute(insert_sessionscache, row) - #print "DEBUG: Successfully(?: %s) updated HudCacho using INSERT" % num - #else: - #print "DEBUG: Successfully updated HudCacho using UPDATE" - #pass + insert = row + row[-1:] + insert = insert[-2:] + insert[:-2] + #DEBUG log.info(_("insert row: '%s'") % (insert)) + cursor.execute(insert_sessionscache, insert) + else: + pass def isDuplicate(self, gametypeID, siteHandNo): dup = False @@ -2041,54 +2182,6 @@ class Database: #FIXME: recognise currency return tmp[0] - def resetPlayerIDs(self): - self.pcache = None - - def getSqlPlayerIDs(self, pnames, siteid): - result = {} - if(self.pcache == None): - self.pcache = LambdaDict(lambda key:self.insertPlayer(key[0], key[1])) - - for player in pnames: - result[player] = self.pcache[(player,siteid)] - # NOTE: Using the LambdaDict does the same thing as: - #if player in self.pcache: - # #print "DEBUG: cachehit" - # pass - #else: - # self.pcache[player] = self.insertPlayer(player, siteid) - #result[player] = self.pcache[player] - - return result - - def insertPlayer(self, name, site_id): - result = None - _name = Charset.to_db_utf8(name) - c = self.get_cursor() - q = "SELECT name, id FROM Players WHERE siteid=%s and name=%s" - q = q.replace('%s', self.sql.query['placeholder']) - - #NOTE/FIXME?: MySQL has ON DUPLICATE KEY UPDATE - #Usage: - # INSERT INTO `tags` (`tag`, `count`) - # VALUES ($tag, 1) - # ON DUPLICATE KEY UPDATE `count`=`count`+1; - - - #print "DEBUG: name: %s site: %s" %(name, site_id) - - c.execute (q, (site_id, _name)) - - tmp = c.fetchone() - if (tmp == None): #new player - c.execute ("INSERT INTO Players (name, siteId) VALUES (%s, %s)".replace('%s',self.sql.query['placeholder']) - ,(_name, site_id)) - #Get last id might be faster here. - #c.execute ("SELECT id FROM Players WHERE name=%s", (name,)) - result = self.get_last_insert_id(c) - else: - result = tmp[1] - return result def insertGameTypes(self, row): c = self.get_cursor() diff --git a/pyfpdb/HUD_config.test.xml b/pyfpdb/HUD_config.test.xml index f62a13bf..cfad09c5 100644 --- a/pyfpdb/HUD_config.test.xml +++ b/pyfpdb/HUD_config.test.xml @@ -2,7 +2,7 @@ - +