diff --git a/packaging/gentoo/fpdb-0.20-r1.ebuild b/packaging/gentoo/fpdb-0.20-r1.ebuild new file mode 100644 index 00000000..988a90b1 --- /dev/null +++ b/packaging/gentoo/fpdb-0.20-r1.ebuild @@ -0,0 +1,57 @@ +# Copyright 1999-2010 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# created by Steffen Schaumburg, steffen@schaumburger.info + +EAPI="2" +NEED_PYTHON=2.5 + +#inherit distutils + +DESCRIPTION="A database program to track your online poker games" +HOMEPAGE="http://fpdb.sourceforge.net/" +#SRC_URI="mirror://sourceforge/fpdb/${MY_P}.tar.bz2" + +LICENSE="AGPL-3" +SLOT="0" +KEYWORDS="~amd64 ~x86" +#note: this should work on other architectures too, please send me your experiences + +IUSE="graphing mysql postgres sqlite" +RDEPEND=" + mysql? ( virtual/mysql + dev-python/mysql-python ) + postgres? ( dev-db/postgresql-server + dev-python/psycopg ) + sqlite? ( dev-lang/python[sqlite] + dev-python/numpy ) + >=x11-libs/gtk+-2.10 + dev-python/pygtk + graphing? ( dev-python/numpy + dev-python/matplotlib[gtk] ) + dev-python/python-xlib" +DEPEND="${RDEPEND}" + +#src_install() { +# DIRINST="${D}usr/share/games/fpdb/" +# mkdir -p "${DIRINST}" +# cp -R * "${DIRINST}" || die +# +# DIRBIN="${D}usr/games/bin/" +# mkdir -p "${DIRBIN}" +# #echo "pathes" +# #echo "${DIRINST}pyfpdb/fpdb.py" +# #echo "${DIRBIN}fpdb.py" +# #echo +# echo "cd /usr/share/games/fpdb/pyfpdb/ && python fpdb.py" > "${DIRBIN}fpdb" || die +# chmod 755 "${DIRBIN}fpdb" || die +#} + +#src_test() { +#} + +pkg_postinst() { + elog "Fpdb's dependencies have been installed. Please visit fpdb.sourceforge.net" + elog "and download and unpack the archive.You can then start fpdb by running run_fpdb.py." + elog "Note that if you really want to use mysql or postgresql you will have to create" + elog "the database and user yourself and enter it into the fpdb config." +} diff --git a/packaging/gentoo/fpdb-0.20_rc1.ebuild b/packaging/gentoo/fpdb-0.20_rc1.ebuild deleted file mode 100644 index cfff9fac..00000000 --- a/packaging/gentoo/fpdb-0.20_rc1.ebuild +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright 1999-2010 Gentoo Foundation -# Distributed under the terms of the GNU General Public License v2 -# created by Steffen Schaumburg, steffen@schaumburger.info - -EAPI="2" -NEED_PYTHON=2.5 - -#inherit distutils - -DESCRIPTION="A database program to track your online poker games" -HOMEPAGE="http://fpdb.sourceforge.net/" -#SRC_URI="mirror://sourceforge/fpdb/${MY_P}.tar.bz2" - -LICENSE="AGPL-3" -SLOT="0" -KEYWORDS="~amd64 ~x86" -#note: this should work on other architectures too, please send me your experiences - -IUSE="mysql postgres graphing" -RDEPEND=" - mysql? ( virtual/mysql - dev-python/mysql-python ) - postgres? ( dev-db/postgresql-server - dev-python/psycopg ) - >=x11-libs/gtk+-2.10 - dev-python/pygtk - graphing? ( dev-python/numpy - dev-python/matplotlib[gtk] ) - dev-python/python-xlib" -DEPEND="${RDEPEND}" - -#src_install() { -# DIRINST="${D}usr/share/games/fpdb/" -# mkdir -p "${DIRINST}" -# cp -R * "${DIRINST}" || die -# -# DIRBIN="${D}usr/games/bin/" -# mkdir -p "${DIRBIN}" -# #echo "pathes" -# #echo "${DIRINST}pyfpdb/fpdb.py" -# #echo "${DIRBIN}fpdb.py" -# #echo -# echo "cd /usr/share/games/fpdb/pyfpdb/ && python fpdb.py" > "${DIRBIN}fpdb" || die -# chmod 755 "${DIRBIN}fpdb" || die -#} - -#src_test() { -#} - -pkg_postinst() { - elog "Fpdb's dependencies have been installed. Please visit fpdb.sourceforge.net and download and unpack the archive." - elog "You can then start fpdb by running run_fpdb.py. Good luck!" -} diff --git a/pyfpdb/AbsoluteToFpdb.py b/pyfpdb/AbsoluteToFpdb.py index cbeaae29..26aea7e8 100755 --- a/pyfpdb/AbsoluteToFpdb.py +++ b/pyfpdb/AbsoluteToFpdb.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- # # Copyright 2008-2010, Carl Gherardi diff --git a/pyfpdb/AlchemyFacilities.py b/pyfpdb/AlchemyFacilities.py index dc5b57af..157d68f2 100644 --- a/pyfpdb/AlchemyFacilities.py +++ b/pyfpdb/AlchemyFacilities.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2009-2010 Grigorij Indigirkin diff --git a/pyfpdb/AlchemyMappings.py b/pyfpdb/AlchemyMappings.py index 055cbf25..15cdf70d 100644 --- a/pyfpdb/AlchemyMappings.py +++ b/pyfpdb/AlchemyMappings.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2009-2010 Grigorij Indigirkin @@ -193,8 +193,8 @@ class HandInternal(DerivedStats): setattr(tour, col, hand_val) elif col == 'koBounty': setattr(tour, col, max(db_val, hand_val)) - elif col == 'tourStartTime' and hand.handStart: - setattr(tour, col, min(db_val, hand.handStart)) + elif col == 'tourStartTime' and hand.startTime: + setattr(tour, col, min(db_val, hand.startTime)) if tour.entries is None and tour_type.sng: tour.entries = tour_type.maxSeats diff --git a/pyfpdb/AlchemyTables.py b/pyfpdb/AlchemyTables.py index f919fd09..84c4b9f0 100644 --- a/pyfpdb/AlchemyTables.py +++ b/pyfpdb/AlchemyTables.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2009-2010 Grigorij Indigirkin @@ -65,7 +65,7 @@ hands_table = Table('Hands', metadata, Column('tableName', String(30), nullable=False), Column('siteHandNo', BigIntColumn, nullable=False), Column('gametypeId', SmallInteger, ForeignKey('Gametypes.id'), nullable=False), - Column('handStart', DateTime, nullable=False), + Column('startTime', DateTime, nullable=False), Column('importTime', DateTime, nullable=False), Column('seats', SmallInteger, nullable=False), Column('maxSeats', SmallInteger, nullable=False), diff --git a/pyfpdb/Anonymise.py b/pyfpdb/Anonymise.py index 4922ef86..6b7e7352 100755 --- a/pyfpdb/Anonymise.py +++ b/pyfpdb/Anonymise.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2009-2010 Carl Gherardi diff --git a/pyfpdb/BetfairToFpdb.py b/pyfpdb/BetfairToFpdb.py index 2acb6ee9..5305c1c1 100755 --- a/pyfpdb/BetfairToFpdb.py +++ b/pyfpdb/BetfairToFpdb.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- # # Copyright 2008-2010, Carl Gherardi diff --git a/pyfpdb/CarbonToFpdb.py b/pyfpdb/CarbonToFpdb.py index 317fec80..9239ab12 100644 --- a/pyfpdb/CarbonToFpdb.py +++ b/pyfpdb/CarbonToFpdb.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- # # Copyright 2010, Matthew Boss diff --git a/pyfpdb/Card.py b/pyfpdb/Card.py index 2262a720..eae7447f 100755 --- a/pyfpdb/Card.py +++ b/pyfpdb/Card.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2008-2010 Carl Gherardi diff --git a/pyfpdb/Charset.py b/pyfpdb/Charset.py index 4341aa4f..fcba6f98 100644 --- a/pyfpdb/Charset.py +++ b/pyfpdb/Charset.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2010 Mika Bostrom diff --git a/pyfpdb/Configuration.py b/pyfpdb/Configuration.py index 6a71a750..ea2a8a78 100755 --- a/pyfpdb/Configuration.py +++ b/pyfpdb/Configuration.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- """Configuration.py @@ -434,6 +434,19 @@ class Import: 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.fastStoreHudCache) +class Email: + def __init__(self, node): + self.node = node + self.host= node.getAttribute("host") + self.username = node.getAttribute("username") + self.password = node.getAttribute("password") + self.useSsl = node.getAttribute("useSsl") + self.folder = node.getAttribute("folder") + + def __str__(self): + return " host = %s\n username = %s\n password = %s\n useSsl = %s\n folder = %s\n" \ + % (self.host, self.username, self.password, self.useSsl, self.folder) + class HudUI: def __init__(self, node): self.node = node @@ -593,6 +606,10 @@ class Config: imp = Import(node = imp_node) self.imp = imp + for email_node in doc.getElementsByTagName("email"): + email = Email(node = email_node) + self.email = email + for hui_node in doc.getElementsByTagName('hud_ui'): hui = HudUI(node = hui_node) self.ui = hui diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index 7b9262f1..bf0ed73b 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- """Database.py @@ -74,7 +74,7 @@ except ImportError: use_numpy = False -DB_VERSION = 127 +DB_VERSION = 132 # Variance created as sqlite has a bunch of undefined aggregate functions. @@ -140,6 +140,8 @@ class Database: , {'tab':'TourneysPlayers', 'col':'playerId', 'drop':0} #, {'tab':'TourneysPlayers', 'col':'tourneyId', 'drop':0} unique indexes not dropped , {'tab':'TourneyTypes', 'col':'siteId', 'drop':0} + , {'tab':'Backings', 'col':'tourneysPlayerId', 'drop':0} + , {'tab':'Backings', 'col':'playerId', 'drop':0} ] , [ # indexes for sqlite (list index 4) {'tab':'Hands', 'col':'gametypeId', 'drop':0} @@ -154,6 +156,8 @@ class Database: , {'tab':'Tourneys', 'col':'tourneyTypeId', 'drop':1} , {'tab':'TourneysPlayers', 'col':'playerId', 'drop':0} , {'tab':'TourneyTypes', 'col':'siteId', 'drop':0} + , {'tab':'Backings', 'col':'tourneysPlayerId', 'drop':0} + , {'tab':'Backings', 'col':'playerId', 'drop':0} ] ] @@ -290,6 +294,34 @@ class Database: self.connection.rollback() # make sure any locks taken so far are released #end def __init__ + def dumpDatabase(self, filename): + dumpFile = open(filename, 'w') + + result="Database dump version " + str(DB_VERSION)+"\n\n" + + tables=self.cursor.execute(self.sql.query['list_tables']) + tables=self.cursor.fetchall() + dumpFile.write(result) + + for table in tables: + table=table[0] + print "table:", table + result="###################\nTable "+table+"\n###################\n" + rows=self.cursor.execute(self.sql.query['get'+table]) + rows=self.cursor.fetchall() + columnNames=self.cursor.description + if not rows: + result+="empty table\n" + else: + for row in rows: + for columnNumber in range(len(columnNames)): + result+=(" "+columnNames[columnNumber][0]+"="+str(row[columnNumber])+"\n") + result+="\n" + result+="\n" + dumpFile.write(result) + dumpFile.close() + #end def dumpDatabase + # could be used by hud to change hud style def set_hud_style(self, style): self.hud_style = style @@ -553,6 +585,24 @@ class Database: c.execute(self.sql.query['get_hand_info'], new_hand_id) return c.fetchall() + def getHandCount(self): + c = self.connection.cursor() + c.execute(self.sql.query['getHandCount']) + return c.fetchone()[0] + #end def getHandCount + + def getTourneyCount(self): + c = self.connection.cursor() + c.execute(self.sql.query['getTourneyCount']) + return c.fetchone()[0] + #end def getTourneyCount + + def getTourneyTypeCount(self): + c = self.connection.cursor() + c.execute(self.sql.query['getTourneyTypeCount']) + return c.fetchone()[0] + #end def getTourneyCount + def get_actual_seat(self, hand_id, name): c = self.connection.cursor() c.execute(self.sql.query['get_actual_seat'], (hand_id, name)) @@ -802,17 +852,18 @@ class Database: #print "session stat_dict =", stat_dict #return stat_dict - def get_player_id(self, config, site, player_name): + def get_player_id(self, config, siteName, playerName): c = self.connection.cursor() - #print "get_player_id: player_name =", player_name, type(player_name) - p_name = Charset.to_utf8(player_name) - c.execute(self.sql.query['get_player_id'], (p_name, site)) + siteNameUtf = Charset.to_utf8(siteName) + playerNameUtf = Charset.to_utf8(playerName) + #print "db.get_player_id siteName",siteName,"playerName",playerName + c.execute(self.sql.query['get_player_id'], (playerNameUtf, siteNameUtf)) row = c.fetchone() if row: return row[0] else: return None - + def get_player_names(self, config, site_id=None, like_player_name="%"): """Fetch player names from players. Use site_id and like_player_name if provided""" @@ -1088,6 +1139,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['createBackingsTable']) # Create unique indexes: log.debug("Creating unique indexes") @@ -1165,7 +1217,7 @@ class Database: for idx in self.indexes[self.backend]: if self.backend == self.MYSQL_INNODB: print "Creating mysql index %s %s" %(idx['tab'], idx['col']) - log.debug("Creating sqlite index %s %s" %(idx['tab'], idx['col'])) + log.debug("Creating mysql index %s %s" %(idx['tab'], idx['col'])) try: s = "create index %s on %s(%s)" % (idx['col'],idx['tab'],idx['col']) self.get_cursor().execute(s) @@ -1173,8 +1225,8 @@ class Database: print " create idx failed: " + str(sys.exc_info()) elif self.backend == self.PGSQL: # mod to use tab_col for index name? - print "Creating pg index %s %s" %(idx['tab'], idx['col']) - log.debug("Creating sqlite index %s %s" %(idx['tab'], idx['col'])) + print "Creating pgsql index %s %s" %(idx['tab'], idx['col']) + log.debug("Creating pgsql index %s %s" %(idx['tab'], idx['col'])) try: s = "create index %s_%s_idx on %s(%s)" % (idx['tab'], idx['col'], idx['tab'], idx['col']) self.get_cursor().execute(s) @@ -1356,17 +1408,17 @@ class Database: c.execute("INSERT INTO Sites (name,code) VALUES ('Carbon', 'CA')") c.execute("INSERT INTO Sites (name,code) VALUES ('PKR', 'PK')") if self.backend == self.SQLITE: - c.execute("""INSERT INTO TourneyTypes (id, siteId, currency, buyin, fee, buyInChips, maxSeats, knockout, - rebuy, addOn, speed, shootout, matrix) - VALUES (NULL, 1, 'USD', 0, 0, 0, 0, 0, 0, 0, NULL, 0, 0);""") + c.execute("""INSERT INTO TourneyTypes (id, siteId, currency, buyin, fee, category, limitType, + buyInChips, maxSeats, knockout, rebuy, addOn, speed, shootout, matrix) + VALUES (NULL, 1, 'USD', 0, 0, "NA", "NA", 0, 0, 0, 0, 0, NULL, 0, 0);""") elif self.backend == self.PGSQL: - c.execute("""insert into TourneyTypes(siteId, currency, buyin, fee, buyInChips, maxSeats, knockout - ,rebuy, addOn, speed, shootout, matrix) - values (1, 'USD', 0, 0, 0, 0, False, False, False, null, False, False);""") + c.execute("""insert into TourneyTypes(siteId, currency, buyin, fee, category, limitType, + buyInChips, maxSeats, knockout, rebuy, addOn, speed, shootout, matrix) + values (1, 'USD', 0, 0, "NA", "NA", 0, 0, False, False, False, null, False, False);""") elif self.backend == self.MYSQL_INNODB: - c.execute("""insert into TourneyTypes(id, siteId, currency, buyin, fee, buyInChips, maxSeats, knockout - ,rebuy, addOn, speed, shootout, matrix) - values (DEFAULT, 1, 'USD', 0, 0, 0, 0, False, False, False, null, False, False);""") + c.execute("""insert into TourneyTypes(id, siteId, currency, buyin, fee, category, limitType, + buyInChips, maxSeats, knockout, rebuy, addOn, speed, shootout, matrix) + values (DEFAULT, 1, 'USD', 0, 0, "NA", "NA", 0, 0, False, False, False, null, False, False);""") #end def fillDefaultData def rebuild_indexes(self, start=None): @@ -1404,9 +1456,9 @@ class Database: where = "" else: where = "where ( hp.playerId not in " + str(tuple(self.hero_ids.values())) \ - + " and h.handStart > '" + v_start + "')" \ + + " and h.startTime > '" + v_start + "')" \ + " or ( hp.playerId in " + str(tuple(self.hero_ids.values())) \ - + " and h.handStart > '" + h_start + "')" + + " and h.startTime > '" + h_start + "')" rebuild_sql = self.sql.query['rebuildHudCache'].replace('', where) self.get_cursor().execute(self.sql.query['clearHudCache']) @@ -1522,7 +1574,7 @@ class Database: p['gameTypeId'], p['siteHandNo'], 0, # tourneyId: 0 means not a tourney hand - p['handStart'], + p['startTime'], datetime.today(), #importtime p['seats'], p['maxSeats'], @@ -1932,7 +1984,7 @@ class Database: print "***Error sending finish: "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1]) # end def send_finish_msg(): - def createOrUpdateTourneyType(self, hand): + def createOrUpdateTourneyType(self, hand):#note: this method is used on Hand and TourneySummary objects tourneyTypeId = 1 # Check if Tourney exists, and if so retrieve TTypeId : in that case, check values of the ttype @@ -1957,11 +2009,11 @@ class Database: except: # Tourney not found : a TourneyTypeId has to be found or created for that specific tourney tourneyTypeIdMatch = False - + if tourneyTypeIdMatch == False : # Check for an existing TTypeId that matches tourney info, if not found create it cursor.execute (self.sql.query['getTourneyTypeId'].replace('%s', self.sql.query['placeholder']), - (hand.siteId, hand.buyinCurrency, hand.buyin, hand.fee, hand.isKO, + (hand.siteId, hand.buyinCurrency, hand.buyin, hand.fee, hand.gametype['category'], hand.gametype['limitType'], hand.isKO, hand.isRebuy, hand.isRebuy, hand.speed, hand.isShootout, hand.isMatrix) ) result=cursor.fetchone() @@ -1970,7 +2022,7 @@ class Database: tourneyTypeId = result[0] except TypeError: #this means we need to create a new entry cursor.execute (self.sql.query['insertTourneyType'].replace('%s', self.sql.query['placeholder']), - (hand.siteId, hand.buyinCurrency, hand.buyin, hand.fee, hand.buyInChips, + (hand.siteId, hand.buyinCurrency, hand.buyin, hand.fee, hand.gametype['category'], hand.gametype['limitType'], hand.buyInChips, hand.isKO, hand.isRebuy, hand.isAddOn, hand.speed, hand.isShootout, hand.isMatrix) ) @@ -1978,7 +2030,7 @@ class Database: return tourneyTypeId #end def createOrUpdateTourneyType - def createOrUpdateTourney(self, hand): + def createOrUpdateTourney(self, hand, source):#note: this method is used on Hand and TourneySummary objects cursor = self.get_cursor() cursor.execute (self.sql.query['getTourneyIdByTourneyNo'].replace('%s', self.sql.query['placeholder']), (hand.siteId, hand.tourNo)) @@ -1987,18 +2039,29 @@ class Database: if result != None and len(result)==1: tourneyId = result[0] else: - cursor.execute (self.sql.query['insertTourney'].replace('%s', self.sql.query['placeholder']), + if source=="HHC": + cursor.execute (self.sql.query['insertTourney'].replace('%s', self.sql.query['placeholder']), (hand.tourneyTypeId, hand.tourNo, None, None, - hand.startTime, None, None, None, - None, None)) + hand.startTime, None, None, None, None, None)) + elif source=="TS": + cursor.execute (self.sql.query['insertTourney'].replace('%s', self.sql.query['placeholder']), + (hand.tourneyTypeId, hand.tourNo, hand.entries, hand.prizepool, + hand.startTime, hand.endTime, hand.tourneyName, hand.matrixIdProcessed, hand.totalRebuyCount, hand.totalAddOnCount)) + else: + raise FpdbParseError("invalid source in Database.createOrUpdateTourney") tourneyId = self.get_last_insert_id(cursor) return tourneyId #end def createOrUpdateTourney - def createOrUpdateTourneysPlayers(self, hand): + def createOrUpdateTourneysPlayers(self, hand, source):#note: this method is used on Hand and TourneySummary objects tourneysPlayersIds=[] for player in hand.players: - playerId = hand.dbid_pids[player[1]] + if source=="TS": #TODO remove this horrible hack + playerId = hand.dbid_pids[player] + elif source=="HHC": + playerId = hand.dbid_pids[player[1]] + else: + raise FpdbParseError("invalid source in Database.createOrUpdateTourneysPlayers") cursor = self.get_cursor() cursor.execute (self.sql.query['getTourneysPlayersId'].replace('%s', self.sql.query['placeholder']), @@ -2008,11 +2071,29 @@ class Database: if result != None and len(result)==1: tourneysPlayersIds.append(result[0]) else: - cursor.execute (self.sql.query['insertTourneysPlayer'].replace('%s', self.sql.query['placeholder']), - (hand.tourneyId, playerId, None, None, None, None, None, None, None, None)) + if source=="HHC": + cursor.execute (self.sql.query['insertTourneysPlayer'].replace('%s', self.sql.query['placeholder']), + (hand.tourneyId, playerId, None, None, None, None, None, None)) + elif source=="TS": + #print "all values: tourneyId",hand.tourneyId, "playerId",playerId, "rank",hand.ranks[player], "winnings",hand.winnings[player], "winCurr",hand.winningsCurrency[player], hand.rebuyCounts[player], hand.addOnCounts[player], hand.koCounts[player] + if hand.ranks[player]: + cursor.execute (self.sql.query['insertTourneysPlayer'].replace('%s', self.sql.query['placeholder']), + (hand.tourneyId, playerId, int(hand.ranks[player]), int(hand.winnings[player]), hand.winningsCurrency[player], + hand.rebuyCounts[player], hand.addOnCounts[player], hand.koCounts[player])) + else: + cursor.execute (self.sql.query['insertTourneysPlayer'].replace('%s', self.sql.query['placeholder']), + (hand.tourneyId, playerId, None, None, None, + hand.rebuyCounts[player], hand.addOnCounts[player], hand.koCounts[player])) tourneysPlayersIds.append(self.get_last_insert_id(cursor)) return tourneysPlayersIds #end def createOrUpdateTourneysPlayers + + def getTourneyTypesIds(self): + c = self.connection.cursor() + c.execute(self.sql.query['getTourneyTypesIds']) + result = c.fetchall() + return result + #end def getTourneyTypesIds #end class Database # Class used to hold all the data needed to write a hand to the db @@ -2064,7 +2145,7 @@ class HandToWrite: print "htw.init error: " + str(sys.exc_info()) raise # end def __init__ - + def set_all( self, config, settings, base, category, siteTourneyNo, buyin , fee, knockout, entries, prizepool, tourneyStartTime , isTourney, tourneyTypeId, siteID, siteHandNo diff --git a/pyfpdb/DerivedStats.py b/pyfpdb/DerivedStats.py index b7b1ab9e..96928486 100644 --- a/pyfpdb/DerivedStats.py +++ b/pyfpdb/DerivedStats.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2008-2010 Carl Gherardi @@ -97,7 +97,7 @@ class DerivedStats(): self.hands['tableName'] = hand.tablename self.hands['siteHandNo'] = hand.handid self.hands['gametypeId'] = None # Leave None, handled later after checking db - self.hands['handStart'] = hand.startTime # format this! + self.hands['startTime'] = hand.startTime # format this! self.hands['importTime'] = None self.hands['seats'] = self.countPlayers(hand) self.hands['maxSeats'] = hand.maxseats diff --git a/pyfpdb/EverleafToFpdb.py b/pyfpdb/EverleafToFpdb.py index 7919a718..b16980ed 100755 --- a/pyfpdb/EverleafToFpdb.py +++ b/pyfpdb/EverleafToFpdb.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- # # Copyright 2008-2010, Carl Gherardi diff --git a/pyfpdb/Exceptions.py b/pyfpdb/Exceptions.py index af454586..34b3bade 100644 --- a/pyfpdb/Exceptions.py +++ b/pyfpdb/Exceptions.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2009-2010 Matt Turnbull diff --git a/pyfpdb/Filters.py b/pyfpdb/Filters.py index 0d2f560b..b211b698 100644 --- a/pyfpdb/Filters.py +++ b/pyfpdb/Filters.py @@ -1,7 +1,7 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- -#Copyright 2008-2010 Steffen Schaumburg +#Copyright 2010 Steffen Schaumburg #This program is free software: you can redistribute it and/or modify #it under the terms of the GNU Affero General Public License as published by #the Free Software Foundation, version 3 of the License. @@ -16,915 +16,7 @@ #In the "official" distribution you can find the license in agpl-3.0.txt. import threading -import pygtk -pygtk.require('2.0') -import gtk -import os -import sys -from optparse import OptionParser -from time import * -import gobject -#import pokereval - -import logging -# logging has been set up in fpdb.py or HUD_main.py, use their settings: -log = logging.getLogger("filter") - - -import Configuration -import Database -import SQL -import Charset - class Filters(threading.Thread): - def __init__(self, db, config, qdict, display = {}, debug=True): - # config and qdict are now redundant - self.debug = debug - self.db = db - self.cursor = db.cursor - self.sql = db.sql - self.conf = db.config - self.display = display - - # text used on screen stored here so that it can be configured - self.filterText = {'limitsall':'All', 'limitsnone':'None', 'limitsshow':'Show _Limits' - ,'seatsbetween':'Between:', 'seatsand':'And:', 'seatsshow':'Show Number of _Players' - ,'playerstitle':'Hero:', 'sitestitle':'Sites:', 'gamestitle':'Games:' - ,'limitstitle':'Limits:', 'seatstitle':'Number of Players:' - ,'groupstitle':'Grouping:', 'posnshow':'Show Position Stats:' - ,'datestitle':'Date:' - ,'groupsall':'All Players' - ,'limitsFL':'FL', 'limitsNL':'NL', 'limitsPL':'PL', 'ring':'Ring', 'tour':'Tourney' - } - - gen = self.conf.get_general_params() - self.day_start = 0 - if 'day_start' in gen: - self.day_start = float(gen['day_start']) - - # Outer Packing box - self.mainVBox = gtk.VBox(False, 0) - - self.label = {} - self.callback = {} - - self.make_filter() - - def make_filter(self): - self.sites = {} - self.games = {} - self.limits = {} - self.seats = {} - self.groups = {} - self.siteid = {} - self.heroes = {} - self.boxes = {} - - for site in self.conf.get_supported_sites(): - #Get db site id for filtering later - self.cursor.execute(self.sql.query['getSiteId'], (site,)) - result = self.db.cursor.fetchall() - if len(result) == 1: - self.siteid[site] = result[0][0] - else: - print "Either 0 or more than one site matched (%s) - EEK" % site - - # For use in date ranges. - self.start_date = gtk.Entry(max=12) - self.end_date = gtk.Entry(max=12) - self.start_date.set_property('editable', False) - self.end_date.set_property('editable', False) - - # For use in groups etc - self.sbGroups = {} - self.numHands = 0 - - playerFrame = gtk.Frame() - playerFrame.set_label_align(0.0, 0.0) - vbox = gtk.VBox(False, 0) - - self.fillPlayerFrame(vbox, self.display) - playerFrame.add(vbox) - - sitesFrame = gtk.Frame() - sitesFrame.set_label_align(0.0, 0.0) - vbox = gtk.VBox(False, 0) - - self.fillSitesFrame(vbox) - sitesFrame.add(vbox) - - # Game types - gamesFrame = gtk.Frame() - gamesFrame.set_label_align(0.0, 0.0) - gamesFrame.show() - vbox = gtk.VBox(False, 0) - - self.fillGamesFrame(vbox) - gamesFrame.add(vbox) - - # Limits - limitsFrame = gtk.Frame() - limitsFrame.show() - vbox = gtk.VBox(False, 0) - self.cbLimits = {} - self.cbNoLimits = None - self.cbAllLimits = None - self.cbFL = None - self.cbNL = None - self.cbPL = None - self.rb = {} # radio buttons for ring/tour - self.type = None # ring/tour - self.types = {} # list of all ring/tour values - - self.fillLimitsFrame(vbox, self.display) - limitsFrame.add(vbox) - - # Seats - seatsFrame = gtk.Frame() - seatsFrame.show() - vbox = gtk.VBox(False, 0) - self.sbSeats = {} - - self.fillSeatsFrame(vbox, self.display) - seatsFrame.add(vbox) - - # Groups - groupsFrame = gtk.Frame() - groupsFrame.show() - vbox = gtk.VBox(False, 0) - - self.fillGroupsFrame(vbox, self.display) - groupsFrame.add(vbox) - - # Date - dateFrame = gtk.Frame() - dateFrame.set_label_align(0.0, 0.0) - dateFrame.show() - vbox = gtk.VBox(False, 0) - - self.fillDateFrame(vbox) - dateFrame.add(vbox) - - # Buttons - self.Button1=gtk.Button("Unnamed 1") - self.Button1.set_sensitive(False) - - self.Button2=gtk.Button("Unnamed 2") - self.Button2.set_sensitive(False) - - self.mainVBox.add(playerFrame) - self.mainVBox.add(sitesFrame) - self.mainVBox.add(gamesFrame) - self.mainVBox.add(limitsFrame) - self.mainVBox.add(seatsFrame) - self.mainVBox.add(groupsFrame) - self.mainVBox.add(dateFrame) - self.mainVBox.add(self.Button1) - self.mainVBox.add(self.Button2) - - self.mainVBox.show_all() - - # Should do this cleaner - if "Heroes" not in self.display or self.display["Heroes"] == False: - playerFrame.hide() - if "Sites" not in self.display or self.display["Sites"] == False: - sitesFrame.hide() - if "Games" not in self.display or self.display["Games"] == False: - gamesFrame.hide() - if "Limits" not in self.display or self.display["Limits"] == False: - limitsFrame.hide() - if "Seats" not in self.display or self.display["Seats"] == False: - seatsFrame.hide() - if "Groups" not in self.display or self.display["Groups"] == False: - groupsFrame.hide() - if "Dates" not in self.display or self.display["Dates"] == False: - dateFrame.hide() - if "Button1" not in self.display or self.display["Button1"] == False: - self.Button1.hide() - if "Button2" not in self.display or self.display["Button2"] == False: - self.Button2.hide() - - if 'button1' in self.label and self.label['button1']: - self.Button1.set_label( self.label['button1'] ) - if 'button2' in self.label and self.label['button2']: - self.Button2.set_label( self.label['button2'] ) - if 'button1' in self.callback and self.callback['button1']: - self.Button1.connect("clicked", self.callback['button1'], "clicked") - self.Button1.set_sensitive(True) - if 'button2' in self.callback and self.callback['button2']: - self.Button2.connect("clicked", self.callback['button2'], "clicked") - self.Button2.set_sensitive(True) - - # make sure any locks on db are released: - self.db.rollback() - - def get_vbox(self): - """returns the vbox of this thread""" - return self.mainVBox - #end def get_vbox - - def getNumHands(self): - return self.numHands - - def getSites(self): - return self.sites - - def getGames(self): - return self.games - - def getSiteIds(self): - return self.siteid - - def getHeroes(self): - return self.heroes - - def getLimits(self): - ltuple = [] - for l in self.limits: - if self.limits[l] == True: - ltuple.append(l) - return ltuple - - def getType(self): - return(self.type) - - def getSeats(self): - if 'from' in self.sbSeats: - self.seats['from'] = self.sbSeats['from'].get_value_as_int() - if 'to' in self.sbSeats: - self.seats['to'] = self.sbSeats['to'].get_value_as_int() - return self.seats - - def getGroups(self): - return self.groups - - def getDates(self): - return self.__get_dates() - - def registerButton1Name(self, title): - self.Button1.set_label(title) - self.label['button1'] = title - - def registerButton1Callback(self, callback): - self.Button1.connect("clicked", callback, "clicked") - self.Button1.set_sensitive(True) - self.callback['button1'] = callback - - def registerButton2Name(self, title): - self.Button2.set_label(title) - self.label['button2'] = title - - def registerButton2Callback(self, callback): - self.Button2.connect("clicked", callback, "clicked") - self.Button2.set_sensitive(True) - self.callback['button2'] = callback - - def cardCallback(self, widget, data=None): - log.debug( "%s was toggled %s" % (data, ("OFF", "ON")[widget.get_active()]) ) - - def createPlayerLine(self, hbox, site, player): - log.debug('add:"%s"' % player) - label = gtk.Label(site +" id:") - hbox.pack_start(label, False, False, 3) - - pname = gtk.Entry() - pname.set_text(player) - pname.set_width_chars(20) - hbox.pack_start(pname, False, True, 0) - pname.connect("changed", self.__set_hero_name, site) - - # Added EntryCompletion but maybe comboBoxEntry is more flexible? (e.g. multiple choices) - completion = gtk.EntryCompletion() - pname.set_completion(completion) - liststore = gtk.ListStore(gobject.TYPE_STRING) - completion.set_model(liststore) - completion.set_text_column(0) - names = self.db.get_player_names(self.conf, self.siteid[site]) # (config=self.conf, site_id=None, like_player_name="%") - for n in names: # list of single-element "tuples" - _n = Charset.to_gui(n[0]) - _nt = (_n, ) - liststore.append(_nt) - - self.__set_hero_name(pname, site) - - def __set_hero_name(self, w, site): - _name = w.get_text() - # get_text() returns a str but we want internal variables to be unicode: - _guiname = unicode(_name) - self.heroes[site] = _guiname -# log.debug("setting heroes[%s]: %s"%(site, self.heroes[site])) - - def __set_num_hands(self, w, val): - try: - self.numHands = int(w.get_text()) - except: - self.numHands = 0 -# log.debug("setting numHands:", self.numHands) - - def createSiteLine(self, hbox, site): - cb = gtk.CheckButton(site) - cb.connect('clicked', self.__set_site_select, site) - cb.set_active(True) - hbox.pack_start(cb, False, False, 0) - - def createGameLine(self, hbox, game): - cb = gtk.CheckButton(game) - cb.connect('clicked', self.__set_game_select, game) - hbox.pack_start(cb, False, False, 0) - cb.set_active(True) - - def createLimitLine(self, hbox, limit, ltext): - cb = gtk.CheckButton(str(ltext)) - cb.connect('clicked', self.__set_limit_select, limit) - hbox.pack_start(cb, False, False, 0) - if limit != "none": - cb.set_active(True) - return(cb) - - def __set_site_select(self, w, site): - #print w.get_active() - self.sites[site] = w.get_active() - log.debug("self.sites[%s] set to %s" %(site, self.sites[site])) - - def __set_game_select(self, w, game): - #print w.get_active() - self.games[game] = w.get_active() - log.debug("self.games[%s] set to %s" %(game, self.games[game])) - - def __set_limit_select(self, w, limit): - #print w.get_active() - self.limits[limit] = w.get_active() - log.debug("self.limit[%s] set to %s" %(limit, self.limits[limit])) - if limit.isdigit() or (len(limit) > 2 and (limit[-2:] == 'nl' or limit[-2:] == 'fl' or limit[-2:] == 'pl')): - if self.limits[limit]: - if self.cbNoLimits is not None: - self.cbNoLimits.set_active(False) - else: - if self.cbAllLimits is not None: - self.cbAllLimits.set_active(False) - if not self.limits[limit]: - if limit.isdigit(): - if self.cbFL is not None: - self.cbFL.set_active(False) - elif (len(limit) > 2 and (limit[-2:] == 'nl')): - if self.cbNL is not None: - self.cbNL.set_active(False) - else: - if self.cbPL is not None: - self.cbPL.set_active(False) - elif limit == "all": - if self.limits[limit]: - #for cb in self.cbLimits.values(): - # cb.set_active(True) - if self.cbFL is not None: - self.cbFL.set_active(True) - if self.cbNL is not None: - self.cbNL.set_active(True) - if self.cbPL is not None: - self.cbPL.set_active(True) - elif limit == "none": - if self.limits[limit]: - for cb in self.cbLimits.values(): - cb.set_active(False) - if self.cbNL is not None: - self.cbNL.set_active(False) - if self.cbFL is not None: - self.cbFL.set_active(False) - if self.cbPL is not None: - self.cbPL.set_active(False) - elif limit == "fl": - if not self.limits[limit]: - # only toggle all fl limits off if they are all currently on - # this stops turning one off from cascading into 'fl' box off - # and then all fl limits being turned off - all_fl_on = True - for cb in self.cbLimits.values(): - t = cb.get_children()[0].get_text() - if t.isdigit(): - if not cb.get_active(): - all_fl_on = False - found = {'ring':False, 'tour':False} - for cb in self.cbLimits.values(): - #print "cb label: ", cb.children()[0].get_text() - t = cb.get_children()[0].get_text() - if t.isdigit(): - if self.limits[limit] or all_fl_on: - cb.set_active(self.limits[limit]) - found[self.types[t]] = True - if self.limits[limit]: - if not found[self.type]: - if self.type == 'ring': - if 'tour' in self.rb: - self.rb['tour'].set_active(True) - elif self.type == 'tour': - if 'ring' in self.rb: - self.rb['ring'].set_active(True) - elif limit == "nl": - if not self.limits[limit]: - # only toggle all nl limits off if they are all currently on - # this stops turning one off from cascading into 'nl' box off - # and then all nl limits being turned off - all_nl_on = True - for cb in self.cbLimits.values(): - t = cb.get_children()[0].get_text() - if "nl" in t and len(t) > 2: - if not cb.get_active(): - all_nl_on = False - found = {'ring':False, 'tour':False} - for cb in self.cbLimits.values(): - t = cb.get_children()[0].get_text() - if "nl" in t and len(t) > 2: - if self.limits[limit] or all_nl_on: - cb.set_active(self.limits[limit]) - found[self.types[t]] = True - if self.limits[limit]: - if not found[self.type]: - if self.type == 'ring': - if 'tour' in self.rb: - self.rb['tour'].set_active(True) - elif self.type == 'tour': - if 'ring' in self.rb: - self.rb['ring'].set_active(True) - elif limit == "pl": - if not self.limits[limit]: - # only toggle all nl limits off if they are all currently on - # this stops turning one off from cascading into 'nl' box off - # and then all nl limits being turned off - all_nl_on = True - for cb in self.cbLimits.values(): - t = cb.get_children()[0].get_text() - if "pl" in t and len(t) > 2: - if not cb.get_active(): - all_nl_on = False - found = {'ring':False, 'tour':False} - for cb in self.cbLimits.values(): - t = cb.get_children()[0].get_text() - if "pl" in t and len(t) > 2: - if self.limits[limit] or all_nl_on: - cb.set_active(self.limits[limit]) - found[self.types[t]] = True - if self.limits[limit]: - if not found[self.type]: - if self.type == 'ring': - if 'tour' in self.rb: - self.rb['tour'].set_active(True) - elif self.type == 'tour': - if 'ring' in self.rb: - self.rb['ring'].set_active(True) - elif limit == "ring": - log.debug("set", limit, "to", self.limits[limit]) - if self.limits[limit]: - self.type = "ring" - for cb in self.cbLimits.values(): - #print "cb label: ", cb.children()[0].get_text() - if self.types[cb.get_children()[0].get_text()] == 'tour': - cb.set_active(False) - elif limit == "tour": - log.debug( "set", limit, "to", self.limits[limit] ) - if self.limits[limit]: - self.type = "tour" - for cb in self.cbLimits.values(): - #print "cb label: ", cb.children()[0].get_text() - if self.types[cb.get_children()[0].get_text()] == 'ring': - cb.set_active(False) - - def __set_seat_select(self, w, seat): - #print "__set_seat_select: seat =", seat, "active =", w.get_active() - self.seats[seat] = w.get_active() - log.debug( "self.seats[%s] set to %s" %(seat, self.seats[seat]) ) - - def __set_group_select(self, w, group): - #print "__set_seat_select: seat =", seat, "active =", w.get_active() - self.groups[group] = w.get_active() - log.debug( "self.groups[%s] set to %s" %(group, self.groups[group]) ) - - def fillPlayerFrame(self, vbox, display): - top_hbox = gtk.HBox(False, 0) - vbox.pack_start(top_hbox, False, False, 0) - lbl_title = gtk.Label(self.filterText['playerstitle']) - lbl_title.set_alignment(xalign=0.0, yalign=0.5) - top_hbox.pack_start(lbl_title, expand=True, padding=3) - showb = gtk.Button(label="refresh", stock=None, use_underline=True) - showb.set_alignment(xalign=1.0, yalign=0.5) - showb.connect('clicked', self.__refresh, 'players') - - vbox1 = gtk.VBox(False, 0) - vbox.pack_start(vbox1, False, False, 0) - self.boxes['players'] = vbox1 - - for site in self.conf.get_supported_sites(): - hBox = gtk.HBox(False, 0) - vbox1.pack_start(hBox, False, True, 0) - - player = self.conf.supported_sites[site].screen_name - _pname = Charset.to_gui(player) - self.createPlayerLine(hBox, site, _pname) - - if "GroupsAll" in display and display["GroupsAll"] == True: - hbox = gtk.HBox(False, 0) - vbox1.pack_start(hbox, False, False, 0) - cb = gtk.CheckButton(self.filterText['groupsall']) - cb.connect('clicked', self.__set_group_select, 'allplayers') - hbox.pack_start(cb, False, False, 0) - self.sbGroups['allplayers'] = cb - self.groups['allplayers'] = False - - lbl = gtk.Label('Min # Hands:') - lbl.set_alignment(xalign=1.0, yalign=0.5) - hbox.pack_start(lbl, expand=True, padding=3) - - phands = gtk.Entry() - phands.set_text('0') - phands.set_width_chars(8) - hbox.pack_start(phands, False, False, 0) - phands.connect("changed", self.__set_num_hands, site) - top_hbox.pack_start(showb, expand=False, padding=1) - - def fillSitesFrame(self, vbox): - top_hbox = gtk.HBox(False, 0) - top_hbox.show() - vbox.pack_start(top_hbox, False, False, 0) - - lbl_title = gtk.Label(self.filterText['sitestitle']) - lbl_title.set_alignment(xalign=0.0, yalign=0.5) - top_hbox.pack_start(lbl_title, expand=True, padding=3) - - showb = gtk.Button(label="hide", stock=None, use_underline=True) - showb.set_alignment(xalign=1.0, yalign=0.5) - showb.connect('clicked', self.__toggle_box, 'sites') - showb.show() - top_hbox.pack_start(showb, expand=False, padding=1) - - vbox1 = gtk.VBox(False, 0) - self.boxes['sites'] = vbox1 - vbox.pack_start(vbox1, False, False, 0) - - for site in self.conf.get_supported_sites(): - hbox = gtk.HBox(False, 0) - vbox1.pack_start(hbox, False, True, 0) - self.createSiteLine(hbox, site) - #Get db site id for filtering later - #self.cursor.execute(self.sql.query['getSiteId'], (site,)) - #result = self.db.cursor.fetchall() - #if len(result) == 1: - # self.siteid[site] = result[0][0] - #else: - # print "Either 0 or more than one site matched - EEK" - - def fillGamesFrame(self, vbox): - top_hbox = gtk.HBox(False, 0) - vbox.pack_start(top_hbox, False, False, 0) - lbl_title = gtk.Label(self.filterText['gamestitle']) - lbl_title.set_alignment(xalign=0.0, yalign=0.5) - top_hbox.pack_start(lbl_title, expand=True, padding=3) - showb = gtk.Button(label="hide", stock=None, use_underline=True) - showb.set_alignment(xalign=1.0, yalign=0.5) - showb.connect('clicked', self.__toggle_box, 'games') - top_hbox.pack_start(showb, expand=False, padding=1) - - vbox1 = gtk.VBox(False, 0) - vbox.pack_start(vbox1, False, False, 0) - self.boxes['games'] = vbox1 - - self.cursor.execute(self.sql.query['getGames']) - result = self.db.cursor.fetchall() - if len(result) >= 1: - for line in result: - hbox = gtk.HBox(False, 0) - vbox1.pack_start(hbox, False, True, 0) - self.createGameLine(hbox, line[0]) - else: - print "INFO: No games returned from database" - log.info("No games returned from database") - - def fillLimitsFrame(self, vbox, display): - top_hbox = gtk.HBox(False, 0) - vbox.pack_start(top_hbox, False, False, 0) - lbl_title = gtk.Label(self.filterText['limitstitle']) - lbl_title.set_alignment(xalign=0.0, yalign=0.5) - top_hbox.pack_start(lbl_title, expand=True, padding=3) - showb = gtk.Button(label="hide", stock=None, use_underline=True) - showb.set_alignment(xalign=1.0, yalign=0.5) - showb.connect('clicked', self.__toggle_box, 'limits') - - vbox1 = gtk.VBox(False, 0) - vbox.pack_start(vbox1, False, False, 0) - self.boxes['limits'] = vbox1 - - self.cursor.execute(self.sql.query['getLimits3']) - # selects limitType, bigBlind - result = self.db.cursor.fetchall() - found = {'nl':False, 'fl':False, 'pl':False, 'ring':False, 'tour':False} - - if len(result) >= 1: - hbox = gtk.HBox(True, 0) - vbox1.pack_start(hbox, False, False, 0) - vbox2 = gtk.VBox(False, 0) - hbox.pack_start(vbox2, False, False, 0) - vbox3 = gtk.VBox(False, 0) - hbox.pack_start(vbox3, False, False, 0) - for i, line in enumerate(result): - if "UseType" in self.display: - if line[0] != self.display["UseType"]: - continue - hbox = gtk.HBox(False, 0) - if i <= len(result)/2: - vbox2.pack_start(hbox, False, False, 0) - else: - vbox3.pack_start(hbox, False, False, 0) - if True: #line[0] == 'ring': - if line[1] == 'fl': - name = str(line[2]) - found['fl'] = True - elif line[1] == 'pl': - name = str(line[2])+line[1] - found['pl'] = True - else: - name = str(line[2])+line[1] - found['nl'] = True - self.cbLimits[name] = self.createLimitLine(hbox, name, name) - self.types[name] = line[0] - found[line[0]] = True # type is ring/tour - self.type = line[0] # if only one type, set it now - if "LimitSep" in display and display["LimitSep"] == True and len(result) >= 2: - hbox = gtk.HBox(True, 0) - vbox1.pack_start(hbox, False, False, 0) - vbox2 = gtk.VBox(False, 0) - hbox.pack_start(vbox2, False, False, 0) - vbox3 = gtk.VBox(False, 0) - hbox.pack_start(vbox3, False, False, 0) - - hbox = gtk.HBox(False, 0) - vbox2.pack_start(hbox, False, False, 0) - self.cbAllLimits = self.createLimitLine(hbox, 'all', self.filterText['limitsall']) - hbox = gtk.HBox(False, 0) - vbox2.pack_start(hbox, False, False, 0) - self.cbNoLimits = self.createLimitLine(hbox, 'none', self.filterText['limitsnone']) - - dest = vbox3 # for ring/tour buttons - if "LimitType" in display and display["LimitType"] == True and found['nl'] and found['fl']: - #if found['fl']: - hbox = gtk.HBox(False, 0) - vbox3.pack_start(hbox, False, False, 0) - self.cbFL = self.createLimitLine(hbox, 'fl', self.filterText['limitsFL']) - #if found['nl']: - hbox = gtk.HBox(False, 0) - vbox3.pack_start(hbox, False, False, 0) - self.cbNL = self.createLimitLine(hbox, 'nl', self.filterText['limitsNL']) - hbox = gtk.HBox(False, 0) - vbox3.pack_start(hbox, False, False, 0) - self.cbPL = self.createLimitLine(hbox, 'pl', self.filterText['limitsPL']) - dest = vbox2 # for ring/tour buttons - else: - print "INFO: No games returned from database" - log.info("No games returned from database") - - if "Type" in display and display["Type"] == True and found['ring'] and found['tour']: - rb1 = gtk.RadioButton(None, self.filterText['ring']) - rb1.connect('clicked', self.__set_limit_select, 'ring') - rb2 = gtk.RadioButton(rb1, self.filterText['tour']) - rb2.connect('clicked', self.__set_limit_select, 'tour') - top_hbox.pack_start(rb1, False, False, 0) # (child, expand, fill, padding) - top_hbox.pack_start(rb2, True, True, 0) # child uses expand space if fill is true - - self.rb['ring'] = rb1 - self.rb['tour'] = rb2 - #print "about to set ring to true" - rb1.set_active(True) - # set_active doesn't seem to call this for some reason so call manually: - self.__set_limit_select(rb1, 'ring') - self.type = 'ring' - top_hbox.pack_start(showb, expand=False, padding=1) - - def fillSeatsFrame(self, vbox, display): - hbox = gtk.HBox(False, 0) - vbox.pack_start(hbox, False, False, 0) - lbl_title = gtk.Label(self.filterText['seatstitle']) - lbl_title.set_alignment(xalign=0.0, yalign=0.5) - hbox.pack_start(lbl_title, expand=True, padding=3) - showb = gtk.Button(label="hide", stock=None, use_underline=True) - showb.set_alignment(xalign=1.0, yalign=0.5) - showb.connect('clicked', self.__toggle_box, 'seats') - hbox.pack_start(showb, expand=False, padding=1) - - vbox1 = gtk.VBox(False, 0) - vbox.pack_start(vbox1, False, False, 0) - self.boxes['seats'] = vbox1 - - hbox = gtk.HBox(False, 0) - vbox1.pack_start(hbox, False, True, 0) - - lbl_from = gtk.Label(self.filterText['seatsbetween']) - lbl_to = gtk.Label(self.filterText['seatsand']) - adj1 = gtk.Adjustment(value=2, lower=2, upper=10, step_incr=1, page_incr=1, page_size=0) - sb1 = gtk.SpinButton(adjustment=adj1, climb_rate=0.0, digits=0) - adj2 = gtk.Adjustment(value=10, lower=2, upper=10, step_incr=1, page_incr=1, page_size=0) - sb2 = gtk.SpinButton(adjustment=adj2, climb_rate=0.0, digits=0) - - hbox.pack_start(lbl_from, expand=False, padding=3) - hbox.pack_start(sb1, False, False, 0) - hbox.pack_start(lbl_to, expand=False, padding=3) - hbox.pack_start(sb2, False, False, 0) - - self.sbSeats['from'] = sb1 - self.sbSeats['to'] = sb2 - - def fillGroupsFrame(self, vbox, display): - hbox = gtk.HBox(False, 0) - vbox.pack_start(hbox, False, False, 0) - lbl_title = gtk.Label(self.filterText['groupstitle']) - lbl_title.set_alignment(xalign=0.0, yalign=0.5) - hbox.pack_start(lbl_title, expand=True, padding=3) - showb = gtk.Button(label="hide", stock=None, use_underline=True) - showb.set_alignment(xalign=1.0, yalign=0.5) - showb.connect('clicked', self.__toggle_box, 'groups') - hbox.pack_start(showb, expand=False, padding=1) - - vbox1 = gtk.VBox(False, 0) - vbox.pack_start(vbox1, False, False, 0) - self.boxes['groups'] = vbox1 - - hbox = gtk.HBox(False, 0) - vbox1.pack_start(hbox, False, False, 0) - cb = self.createLimitLine(hbox, 'show', self.filterText['limitsshow']) - - hbox = gtk.HBox(False, 0) - vbox1.pack_start(hbox, False, True, 0) - cb = gtk.CheckButton(self.filterText['posnshow']) - cb.connect('clicked', self.__set_group_select, 'posn') - hbox.pack_start(cb, False, False, 0) - self.sbGroups['posn'] = cb - self.groups['posn'] = False - - if "SeatSep" in display and display["SeatSep"] == True: - hbox = gtk.HBox(False, 0) - vbox1.pack_start(hbox, False, True, 0) - cb = gtk.CheckButton(self.filterText['seatsshow']) - cb.connect('clicked', self.__set_seat_select, 'show') - hbox.pack_start(cb, False, False, 0) - self.sbSeats['show'] = cb - self.seats['show'] = False - - def fillCardsFrame(self, vbox): - hbox1 = gtk.HBox(True,0) - hbox1.show() - vbox.pack_start(hbox1, True, True, 0) - - cards = [ "A", "K","Q","J","T","9","8","7","6","5","4","3","2" ] - - for j in range(0, len(cards)): - hbox1 = gtk.HBox(True,0) - hbox1.show() - vbox.pack_start(hbox1, True, True, 0) - for i in range(0, len(cards)): - if i < (j + 1): - suit = "o" - else: - suit = "s" - button = gtk.ToggleButton("%s%s%s" %(cards[i], cards[j], suit)) - button.connect("toggled", self.cardCallback, "%s%s%s" %(cards[i], cards[j], suit)) - hbox1.pack_start(button, True, True, 0) - button.show() - - def fillDateFrame(self, vbox): - # Hat tip to Mika Bostrom - calendar code comes from PokerStats - top_hbox = gtk.HBox(False, 0) - vbox.pack_start(top_hbox, False, False, 0) - lbl_title = gtk.Label(self.filterText['datestitle']) - lbl_title.set_alignment(xalign=0.0, yalign=0.5) - top_hbox.pack_start(lbl_title, expand=True, padding=3) - showb = gtk.Button(label="hide", stock=None, use_underline=True) - showb.set_alignment(xalign=1.0, yalign=0.5) - showb.connect('clicked', self.__toggle_box, 'dates') - top_hbox.pack_start(showb, expand=False, padding=1) - - vbox1 = gtk.VBox(False, 0) - vbox.pack_start(vbox1, False, False, 0) - self.boxes['dates'] = vbox1 - - hbox = gtk.HBox() - vbox1.pack_start(hbox, False, True, 0) - - lbl_start = gtk.Label('From:') - - btn_start = gtk.Button() - btn_start.set_image(gtk.image_new_from_stock(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)) - btn_start.connect('clicked', self.__calendar_dialog, self.start_date) - - hbox.pack_start(lbl_start, expand=False, padding=3) - hbox.pack_start(btn_start, expand=False, padding=3) - hbox.pack_start(self.start_date, expand=False, padding=2) - - #New row for end date - hbox = gtk.HBox() - vbox1.pack_start(hbox, False, True, 0) - - lbl_end = gtk.Label(' To:') - btn_end = gtk.Button() - btn_end.set_image(gtk.image_new_from_stock(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)) - btn_end.connect('clicked', self.__calendar_dialog, self.end_date) - - btn_clear = gtk.Button(label=' Clear Dates ') - btn_clear.connect('clicked', self.__clear_dates) - - hbox.pack_start(lbl_end, expand=False, padding=3) - hbox.pack_start(btn_end, expand=False, padding=3) - hbox.pack_start(self.end_date, expand=False, padding=2) - - hbox.pack_start(btn_clear, expand=False, padding=15) - - def __refresh(self, widget, entry): - for w in self.mainVBox.get_children(): - w.destroy() - self.make_filter() - - def __toggle_box(self, widget, entry): - if self.boxes[entry].props.visible: - self.boxes[entry].hide() - widget.set_label("show") - else: - self.boxes[entry].show() - widget.set_label("hide") - - def __calendar_dialog(self, widget, entry): - 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 __clear_dates(self, w): - self.start_date.set_text('') - self.end_date.set_text('') - - def __get_dates(self): - # self.day_start gives user's start of day in hours - offset = int(self.day_start * 3600) # calc day_start in seconds - - t1 = self.start_date.get_text() - t2 = self.end_date.get_text() - - if t1 == '': - t1 = '1970-01-02' - if t2 == '': - t2 = '2020-12-12' - - s1 = strptime(t1, "%Y-%m-%d") # make time_struct - s2 = strptime(t2, "%Y-%m-%d") - e1 = mktime(s1) + offset # s1 is localtime, but returned time since epoch is UTC, then add the - e2 = mktime(s2) + offset # s2 is localtime, but returned time since epoch is UTC - e2 = e2 + 24 * 3600 - 1 # date test is inclusive, so add 23h 59m 59s to e2 - - adj_t1 = strftime("%Y-%m-%d %H:%M:%S", gmtime(e1)) # make adjusted string including time - adj_t2 = strftime("%Y-%m-%d %H:%M:%S", gmtime(e2)) - log.info("t1="+t1+" adj_t1="+adj_t1+'.') - - return (adj_t1, adj_t2) - - 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() - -def main(argv=None): - """main can also be called in the python interpreter, by supplying the command line as the argument.""" - if argv is None: - argv = sys.argv[1:] - - def destroy(*args): # call back for terminating the main eventloop - gtk.main_quit() - - parser = OptionParser() - (options, argv) = parser.parse_args(args = argv) - - config = Configuration.Config() - db = None - - db = Database.Database() - db.do_connect(config) - - qdict = SQL.SQL(db.get_backend_name()) - - i = Filters(db, config, qdict) - main_window = gtk.Window() - main_window.connect('destroy', destroy) - main_window.add(i.get_vbox()) - main_window.show() - gtk.main() - -if __name__ == '__main__': - sys.exit(main()) + pass +#end class Filters diff --git a/pyfpdb/FpdbSQLQueries.py b/pyfpdb/FpdbSQLQueries.py deleted file mode 100644 index fb67f560..00000000 --- a/pyfpdb/FpdbSQLQueries.py +++ /dev/null @@ -1,77 +0,0 @@ -#!/usr/bin/python2 -# -*- coding: utf-8 -*- - -#Copyright 2008-2010 Steffen Schaumburg -#This program is free software: you can redistribute it and/or modify -#it under the terms of the GNU Affero General Public License as published by -#the Free Software Foundation, version 3 of the License. -# -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -#GNU General Public License for more details. -# -#You should have received a copy of the GNU Affero General Public License -#along with this program. If not, see . -#In the "official" distribution you can find the license in agpl-3.0.txt. - -############################################################################ -# -# File for DB queries used in fpdb -# - -import sys -import os - -class FpdbSQLQueries: - - def __init__(self, db): - self.query = {} - self.dbname = db - - - if(self.dbname == 'MySQL InnoDB' or self.dbname == 'PostgreSQL'): - self.query['set tx level'] = """SET SESSION TRANSACTION - ISOLATION LEVEL READ COMMITTED""" - elif(self.dbname == 'SQLite'): - self.query['set tx level'] = """ """ - - - if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL'): - self.query['getSiteId'] = """SELECT id from Sites where name = %s""" - elif(self.dbname == 'SQLite'): - self.query['getSiteId'] = """SELECT id from Sites where name = %s""" - - if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL') or (self.dbname == 'SQLite'): - self.query['getGames'] = """SELECT DISTINCT category from Gametypes""" - - if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL') or (self.dbname == 'SQLite'): - self.query['getLimits'] = """SELECT DISTINCT bigBlind from Gametypes ORDER by bigBlind DESC""" - - -if __name__== "__main__": - from optparse import OptionParser - - print "FpdbSQLQueries starting from CLI" - - #process CLI parameters - usage = "usage: %prog [options]" - parser = OptionParser() - parser.add_option("-t", "--type", dest="dbtype", help="Available 'MySQL InnoDB', 'PostgreSQL', 'SQLite'(default: MySQL InnoDB)", default="MySQL InnoDB") - parser.add_option("-s", "--show", action="store_true", dest="showsql", help="Show full SQL output") - parser.add_option("-v", "--verbose", action="store_true", dest="verbose") - - - (options, args) = parser.parse_args() - - if options.verbose: - print """No additional output available in this file""" - - obj = FpdbSQLQueries(options.dbtype) - - print "Available Queries for '" + options.dbtype + "':" - - for key in obj.query: - print " " + key - if options.showsql: - print obj.query[key] diff --git a/pyfpdb/FulltiltToFpdb.py b/pyfpdb/FulltiltToFpdb.py index 0c6faf43..d9e924f0 100755 --- a/pyfpdb/FulltiltToFpdb.py +++ b/pyfpdb/FulltiltToFpdb.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- # # Copyright 2008-2010, Carl Gherardi @@ -20,7 +20,7 @@ import logging from HandHistoryConverter import * -import TourneySummary +#import TourneySummary # Fulltilt HH Format converter @@ -434,23 +434,23 @@ class Fulltilt(HandHistoryConverter): def readSummaryInfo(self, summaryInfoList): self.status = True - m = re.search("Tournament Summary", summaryInfoList[0]) - if m: - # info list should be 2 lines : Tourney infos & Finsihing postions with winnings - if (len(summaryInfoList) != 2 ): - log.info("Too many or too few lines (%d) in file '%s' : '%s'" % (len(summaryInfoList), self.in_path, summaryInfoList) ) - self.status = False - else: - self.tourney = TourneySummary.TourneySummary(sitename = self.sitename, gametype = None, summaryText = summaryInfoList, builtFrom = "HHC") - self.status = self.getPlayersPositionsAndWinnings(self.tourney) - if self.status == True : - self.status = self.determineTourneyType(self.tourney) - #print self.tourney - else: - log.info("Parsing NOK : rejected") - else: - log.info( "This is not a summary file : '%s'" % (self.in_path) ) - self.status = False + #m = re.search("Tournament Summary", summaryInfoList[0]) + #if m: + # # info list should be 2 lines : Tourney infos & Finsihing postions with winnings + # if (len(summaryInfoList) != 2 ): + # log.info("Too many or too few lines (%d) in file '%s' : '%s'" % (len(summaryInfoList), self.in_path, summaryInfoList) ) + # self.status = False + # else: + # self.tourney = TourneySummary.TourneySummary(sitename = self.sitename, gametype = None, summaryText = summaryInfoList, builtFrom = "HHC") + # self.status = self.getPlayersPositionsAndWinnings(self.tourney) + # if self.status == True : + # self.status = self.determineTourneyType(self.tourney) + # #print self.tourney + # else: + # log.info("Parsing NOK : rejected") + #else: + # log.info( "This is not a summary file : '%s'" % (self.in_path) ) + # self.status = False return self.status diff --git a/pyfpdb/GuiAutoImport.py b/pyfpdb/GuiAutoImport.py index 5ddebaf3..518ab647 100755 --- a/pyfpdb/GuiAutoImport.py +++ b/pyfpdb/GuiAutoImport.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2008-2010 Steffen Schaumburg diff --git a/pyfpdb/GuiBulkImport.py b/pyfpdb/GuiBulkImport.py index 22d96ab9..83f841e2 100755 --- a/pyfpdb/GuiBulkImport.py +++ b/pyfpdb/GuiBulkImport.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2008-2010 Steffen Schaumburg @@ -50,7 +50,7 @@ class GuiBulkImport(): ttime = None # Does the lock acquisition need to be more sophisticated for multiple dirs? # (see comment above about what to do if pipe already open) - if self.settings['global_lock'].acquire(False): # returns false immediately if lock not acquired + if self.settings['global_lock'].acquire(wait=False, source="GuiBulkImport"): # returns false immediately if lock not acquired #try: print "\nGlobal lock taken ..." self.progressbar.set_text("Importing...") diff --git a/pyfpdb/GuiDatabase.py b/pyfpdb/GuiDatabase.py index d8635320..418910cf 100755 --- a/pyfpdb/GuiDatabase.py +++ b/pyfpdb/GuiDatabase.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2008-2010 Carl Gherardi diff --git a/pyfpdb/GuiGraphViewer.py b/pyfpdb/GuiGraphViewer.py index 0f617f00..68cbacc7 100644 --- a/pyfpdb/GuiGraphViewer.py +++ b/pyfpdb/GuiGraphViewer.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2008-2010 Steffen Schaumburg @@ -44,7 +44,7 @@ except ImportError, inst: import fpdb_import import Database -import Filters +import RingFilters import Charset class GuiGraphViewer (threading.Thread): @@ -75,7 +75,7 @@ class GuiGraphViewer (threading.Thread): "Button2" : True } - self.filters = Filters.Filters(self.db, self.conf, self.sql, display = filters_display) + self.filters = RingFilters.RingFilters(self.db, self.conf, self.sql, display = filters_display) self.filters.registerButton1Name("Refresh _Graph") self.filters.registerButton1Callback(self.generateGraph) self.filters.registerButton2Name("_Export to File") diff --git a/pyfpdb/GuiLogView.py b/pyfpdb/GuiLogView.py index 80e0cb22..127dc669 100755 --- a/pyfpdb/GuiLogView.py +++ b/pyfpdb/GuiLogView.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2008-2010 Carl Gherardi diff --git a/pyfpdb/GuiPlayerStats.py b/pyfpdb/GuiPlayerStats.py index b8aaf55c..34e8e122 100644 --- a/pyfpdb/GuiPlayerStats.py +++ b/pyfpdb/GuiPlayerStats.py @@ -1,7 +1,7 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- -#Copyright 2008-2010 Steffen Schaumburg +#Copyright 2010 Steffen Schaumburg #This program is free software: you can redistribute it and/or modify #it under the terms of the GNU Affero General Public License as published by #the Free Software Foundation, version 3 of the License. @@ -15,662 +15,8 @@ #along with this program. If not, see . #In the "official" distribution you can find the license in agpl-3.0.txt. -import traceback import threading -import pygtk -pygtk.require('2.0') -import gtk -import os -import sys -from time import time, strftime - -import Card -import fpdb_import -import Database -import Filters -import Charset - -colalias,colshow,colheading,colxalign,colformat,coltype = 0,1,2,3,4,5 -ranks = {'x':0, '2':2, '3':3, '4':4, '5':5, '6':6, '7':7, '8':8, '9':9, 'T':10, 'J':11, 'Q':12, 'K':13, 'A':14} - -class GuiPlayerStats (threading.Thread): - - def __init__(self, config, querylist, mainwin, debug=True): - self.debug = debug - self.conf = config - self.main_window = mainwin - self.sql = querylist - - self.liststore = [] # gtk.ListStore[] stores the contents of the grids - self.listcols = [] # gtk.TreeViewColumn[][] stores the columns in the grids - - self.MYSQL_INNODB = 2 - self.PGSQL = 3 - self.SQLITE = 4 - - # create new db connection to avoid conflicts with other threads - self.db = Database.Database(self.conf, sql=self.sql) - self.cursor = self.db.cursor - - settings = {} - settings.update(self.conf.get_db_parameters()) - settings.update(self.conf.get_tv_parameters()) - settings.update(self.conf.get_import_parameters()) - settings.update(self.conf.get_default_paths()) - - # text used on screen stored here so that it can be configured - self.filterText = {'handhead':'Hand Breakdown for all levels listed above' - } - - filters_display = { "Heroes" : True, - "Sites" : True, - "Games" : True, - "Limits" : True, - "LimitSep" : True, - "LimitType" : True, - "Type" : True, - "Seats" : True, - "SeatSep" : True, - "Dates" : True, - "Groups" : True, - "GroupsAll" : True, - "Button1" : True, - "Button2" : True - } - - self.filters = Filters.Filters(self.db, self.conf, self.sql, display = filters_display) - self.filters.registerButton1Name("_Filters") - self.filters.registerButton1Callback(self.showDetailFilter) - self.filters.registerButton2Name("_Refresh Stats") - self.filters.registerButton2Callback(self.refreshStats) - - # ToDo: store in config - # ToDo: create popup to adjust column config - # columns to display, keys match column name returned by sql, values in tuple are: - # is column displayed, column heading, xalignment, formatting, celltype - self.columns = [ ["game", True, "Game", 0.0, "%s", "str"] - , ["hand", False, "Hand", 0.0, "%s", "str"] # true not allowed for this line - , ["plposition", False, "Posn", 1.0, "%s", "str"] # true not allowed for this line (set in code) - , ["pname", False, "Name", 0.0, "%s", "str"] # true not allowed for this line (set in code) - , ["n", True, "Hds", 1.0, "%1.0f", "str"] - , ["avgseats", False, "Seats", 1.0, "%3.1f", "str"] - , ["vpip", True, "VPIP", 1.0, "%3.1f", "str"] - , ["pfr", True, "PFR", 1.0, "%3.1f", "str"] - , ["pf3", True, "PF3", 1.0, "%3.1f", "str"] - , ["aggfac", True, "AggFac", 1.0, "%2.2f", "str"] - , ["aggfrq", True, "AggFreq", 1.0, "%3.1f", "str"] - , ["conbet", True, "ContBet", 1.0, "%3.1f", "str"] - , ["steals", True, "Steals", 1.0, "%3.1f", "str"] - , ["saw_f", True, "Saw_F", 1.0, "%3.1f", "str"] - , ["sawsd", True, "SawSD", 1.0, "%3.1f", "str"] - , ["wtsdwsf", True, "WtSDwsF", 1.0, "%3.1f", "str"] - , ["wmsd", True, "W$SD", 1.0, "%3.1f", "str"] - , ["flafq", True, "FlAFq", 1.0, "%3.1f", "str"] - , ["tuafq", True, "TuAFq", 1.0, "%3.1f", "str"] - , ["rvafq", True, "RvAFq", 1.0, "%3.1f", "str"] - , ["pofafq", False, "PoFAFq", 1.0, "%3.1f", "str"] - , ["net", True, "Net($)", 1.0, "%6.2f", "cash"] - , ["bbper100", True, "bb/100", 1.0, "%4.2f", "str"] - , ["rake", True, "Rake($)", 1.0, "%6.2f", "cash"] - , ["bb100xr", True, "bbxr/100", 1.0, "%4.2f", "str"] - , ["variance", True, "Variance", 1.0, "%5.2f", "str"] - ] - - # Detail filters: This holds the data used in the popup window, extra values are - # added at the end of these lists during processing - # sql test, screen description, min, max - self.handtests = [ # already in filter class : ['h.seats', 'Number of Players', 2, 10] - ['h.maxSeats', 'Size of Table', 2, 10] - ,['h.playersVpi', 'Players who VPI', 0, 10] - ,['h.playersAtStreet1', 'Players at Flop', 0, 10] - ,['h.playersAtStreet2', 'Players at Turn', 0, 10] - ,['h.playersAtStreet3', 'Players at River', 0, 10] - ,['h.playersAtStreet4', 'Players at Street7', 0, 10] - ,['h.playersAtShowdown', 'Players at Showdown', 0, 10] - ,['h.street0Raises', 'Bets to See Flop', 0, 5] - ,['h.street1Raises', 'Bets to See Turn', 0, 5] - ,['h.street2Raises', 'Bets to See River', 0, 5] - ,['h.street3Raises', 'Bets to See Street7', 0, 5] - ,['h.street4Raises', 'Bets to See Showdown', 0, 5] - ] - - self.stats_frame = None - self.stats_vbox = None - self.detailFilters = [] # the data used to enhance the sql select - - #self.main_hbox = gtk.HBox(False, 0) - #self.main_hbox.show() - self.main_hbox = gtk.HPaned() - - self.stats_frame = gtk.Frame() - self.stats_frame.show() - - self.stats_vbox = gtk.VPaned() - self.stats_vbox.show() - self.stats_frame.add(self.stats_vbox) - # self.fillStatsFrame(self.stats_vbox) - - #self.main_hbox.pack_start(self.filters.get_vbox()) - #self.main_hbox.pack_start(self.stats_frame, expand=True, fill=True) - self.main_hbox.pack1(self.filters.get_vbox()) - self.main_hbox.pack2(self.stats_frame) - self.main_hbox.show() - - # make sure Hand column is not displayed - [x for x in self.columns if x[0] == 'hand'][0][1] = False - self.last_pos = -1 - - - def get_vbox(self): - """returns the vbox of this thread""" - return self.main_hbox - - def refreshStats(self, widget, data): - self.last_pos = self.stats_vbox.get_position() - try: self.stats_vbox.destroy() - except AttributeError: pass - self.liststore = [] - self.listcols = [] - #self.stats_vbox = gtk.VBox(False, 0) - self.stats_vbox = gtk.VPaned() - self.stats_vbox.show() - self.stats_frame.add(self.stats_vbox) - self.fillStatsFrame(self.stats_vbox) - if self.last_pos > 0: - self.stats_vbox.set_position(self.last_pos) - - def fillStatsFrame(self, vbox): - sites = self.filters.getSites() - heroes = self.filters.getHeroes() - siteids = self.filters.getSiteIds() - limits = self.filters.getLimits() - type = self.filters.getType() - seats = self.filters.getSeats() - groups = self.filters.getGroups() - dates = self.filters.getDates() - games = self.filters.getGames() - sitenos = [] - playerids = [] - - # Which sites are selected? - for site in sites: - if sites[site] == True: - sitenos.append(siteids[site]) - _hname = Charset.to_utf8(heroes[site]) - result = self.db.get_player_id(self.conf, site, _hname) - if result is not None: - playerids.append(int(result)) - - if not sitenos: - #Should probably pop up here. - print "No sites selected - defaulting to PokerStars" - sitenos = [2] - if not playerids: - print "No player ids found" - return - if not limits: - print "No limits found" - return - - self.createStatsTable(vbox, playerids, sitenos, limits, type, seats, groups, dates, games) - - def createStatsTable(self, vbox, playerids, sitenos, limits, type, seats, groups, dates, games): - starttime = time() - show_detail = True - - # Scrolled window for summary table - swin = gtk.ScrolledWindow(hadjustment=None, vadjustment=None) - swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - swin.show() - vbox.pack1(swin) - - # Display summary table at top of page - # 3rd parameter passes extra flags, currently includes: - # holecards - whether to display card breakdown (True/False) - # numhands - min number hands required when displaying all players - # gridnum - index for grid data structures - flags = [False, self.filters.getNumHands(), 0] - self.addGrid(swin, 'playerDetailedStats', flags, playerids - ,sitenos, limits, type, seats, groups, dates, games) - - if 'allplayers' in groups and groups['allplayers']: - # can't currently do this combination so skip detailed table - show_detail = False - - if show_detail: - # Separator - vbox2 = gtk.VBox(False, 0) - heading = gtk.Label(self.filterText['handhead']) - heading.show() - vbox2.pack_start(heading, expand=False, padding=3) - - # Scrolled window for detailed table (display by hand) - swin = gtk.ScrolledWindow(hadjustment=None, vadjustment=None) - swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - swin.show() - vbox2.pack_start(swin, expand=True, padding=3) - vbox.pack2(vbox2) - vbox2.show() - - # Detailed table - flags[0] = True - flags[2] = 1 - self.addGrid(swin, 'playerDetailedStats', flags, playerids - ,sitenos, limits, type, seats, groups, dates, games) - - self.db.rollback() - print "Stats page displayed in %4.2f seconds" % (time() - starttime) - #end def fillStatsFrame(self, vbox): - - def reset_style_render_func(self, treeviewcolumn, cell, model, iter): - cell.set_property('foreground', 'black') - - def ledger_style_render_func(self, tvcol, cell, model, iter): - str = cell.get_property('text') - if '-' in str: - str = str.replace("-", "") - str = "(%s)" %(str) - cell.set_property('text', str) - cell.set_property('foreground', 'red') - else: - cell.set_property('foreground', 'darkgreen') - - return - - def sortnums(self, model, iter1, iter2, nums): - try: - ret = 0 - (n, grid) = nums - a = self.liststore[grid].get_value(iter1, n) - b = self.liststore[grid].get_value(iter2, n) - if 'f' in self.cols_to_show[n][4]: - try: a = float(a) - except: a = 0.0 - try: b = float(b) - except: b = 0.0 - if n == 0 and grid == 1: #make sure it only works on the starting hands - a1,a2,a3 = ranks[a[0]], ranks[a[1]], (a+'o')[2] - b1,b2,b3 = ranks[b[0]], ranks[b[1]], (b+'o')[2] - if a1 > b1 or ( a1 == b1 and (a2 > b2 or (a2 == b2 and a3 > b3) ) ): - ret = 1 - else: - ret = -1 - else: - if a < b: - ret = -1 - elif a == b: - ret = 0 - else: - ret = 1 - #print "n =", n, "iter1[n] =", self.liststore[grid].get_value(iter1,n), "iter2[n] =", self.liststore[grid].get_value(iter2,n), "ret =", ret - except: - err = traceback.extract_tb(sys.exc_info()[2]) - print "***sortnums error: " + str(sys.exc_info()[1]) - print "\n".join( [e[0]+':'+str(e[1])+" "+e[2] for e in err] ) - - return(ret) - - def sortcols(self, col, nums): - try: - #This doesn't actually work yet - clicking heading in top section sorts bottom section :-( - (n, grid) = nums - if not col.get_sort_indicator() or col.get_sort_order() == gtk.SORT_ASCENDING: - col.set_sort_order(gtk.SORT_DESCENDING) - else: - col.set_sort_order(gtk.SORT_ASCENDING) - self.liststore[grid].set_sort_column_id(n, col.get_sort_order()) - self.liststore[grid].set_sort_func(n, self.sortnums, (n,grid)) - for i in xrange(len(self.listcols[grid])): - self.listcols[grid][i].set_sort_indicator(False) - self.listcols[grid][n].set_sort_indicator(True) - # use this listcols[col].set_sort_indicator(True) - # to turn indicator off for other cols - except: - err = traceback.extract_tb(sys.exc_info()[2]) - print "***sortcols error: " + str(sys.exc_info()[1]) - print "\n".join( [e[0]+':'+str(e[1])+" "+e[2] for e in err] ) - - def addGrid(self, vbox, query, flags, playerids, sitenos, limits, type, seats, groups, dates, games): - counter = 0 - row = 0 - sqlrow = 0 - if not flags: holecards,grid = False,0 - else: holecards,grid = flags[0],flags[2] - - tmp = self.sql.query[query] - tmp = self.refineQuery(tmp, flags, playerids, sitenos, limits, type, seats, groups, dates, games) - self.cursor.execute(tmp) - result = self.cursor.fetchall() - colnames = [desc[0].lower() for desc in self.cursor.description] - - # pre-fetch some constant values: - self.cols_to_show = [x for x in self.columns if x[colshow]] - hgametypeid_idx = colnames.index('hgametypeid') - - assert len(self.liststore) == grid, "len(self.liststore)="+str(len(self.liststore))+" grid-1="+str(grid) - self.liststore.append( gtk.ListStore(*([str] * len(self.cols_to_show))) ) - view = gtk.TreeView(model=self.liststore[grid]) - view.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_BOTH) - #vbox.pack_start(view, expand=False, padding=3) - vbox.add(view) - textcell = gtk.CellRendererText() - textcell50 = gtk.CellRendererText() - textcell50.set_property('xalign', 0.5) - numcell = gtk.CellRendererText() - numcell.set_property('xalign', 1.0) - assert len(self.listcols) == grid - self.listcols.append( [] ) - - # Create header row eg column: ("game", True, "Game", 0.0, "%s") - for col, column in enumerate(self.cols_to_show): - if column[colalias] == 'game' and holecards: - s = [x for x in self.columns if x[colalias] == 'hand'][0][colheading] - else: - s = column[colheading] - self.listcols[grid].append(gtk.TreeViewColumn(s)) - view.append_column(self.listcols[grid][col]) - if column[colformat] == '%s': - if column[colxalign] == 0.0: - self.listcols[grid][col].pack_start(textcell, expand=True) - self.listcols[grid][col].add_attribute(textcell, 'text', col) - cellrend = textcell - else: - self.listcols[grid][col].pack_start(textcell50, expand=True) - self.listcols[grid][col].add_attribute(textcell50, 'text', col) - cellrend = textcell50 - self.listcols[grid][col].set_expand(True) - else: - self.listcols[grid][col].pack_start(numcell, expand=True) - self.listcols[grid][col].add_attribute(numcell, 'text', col) - self.listcols[grid][col].set_expand(True) - cellrend = numcell - #self.listcols[grid][col].set_alignment(column[colxalign]) # no effect? - self.listcols[grid][col].set_clickable(True) - self.listcols[grid][col].connect("clicked", self.sortcols, (col,grid)) - if col == 0: - self.listcols[grid][col].set_sort_order(gtk.SORT_DESCENDING) - self.listcols[grid][col].set_sort_indicator(True) - if column[coltype] == 'cash': - self.listcols[grid][col].set_cell_data_func(numcell, self.ledger_style_render_func) - else: - self.listcols[grid][col].set_cell_data_func(cellrend, self.reset_style_render_func) - - rows = len(result) # +1 for title row - - while sqlrow < rows: - treerow = [] - for col,column in enumerate(self.cols_to_show): - if column[colalias] in colnames: - value = result[sqlrow][colnames.index(column[colalias])] - if column[colalias] == 'plposition': - if value == 'B': - value = 'BB' - elif value == 'S': - value = 'SB' - elif value == '0': - value = 'Btn' - else: - if column[colalias] == 'game': - if holecards: - value = Card.twoStartCardString( result[sqlrow][hgametypeid_idx] ) - else: - minbb = result[sqlrow][colnames.index('minbigblind')] - maxbb = result[sqlrow][colnames.index('maxbigblind')] - value = result[sqlrow][colnames.index('limittype')] + ' ' \ - + result[sqlrow][colnames.index('category')].title() + ' ' \ - + result[sqlrow][colnames.index('name')] + ' $' - if 100 * int(minbb/100.0) != minbb: - value += '%.2f' % (minbb/100.0) - else: - value += '%.0f' % (minbb/100.0) - if minbb != maxbb: - if 100 * int(maxbb/100.0) != maxbb: - value += ' - $' + '%.2f' % (maxbb/100.0) - else: - value += ' - $' + '%.0f' % (maxbb/100.0) - else: - continue - if value and value != -999: - treerow.append(column[colformat] % value) - else: - treerow.append(' ') - iter = self.liststore[grid].append(treerow) - #print treerow - sqlrow += 1 - row += 1 - vbox.show_all() - - #end def addGrid(self, query, vars, playerids, sitenos, limits, type, seats, groups, dates): - - def refineQuery(self, query, flags, playerids, sitenos, limits, type, seats, groups, dates, games): - having = '' - if not flags: - holecards = False - numhands = 0 - else: - holecards = flags[0] - numhands = flags[1] - - if 'allplayers' in groups and groups['allplayers']: - nametest = "(hp.playerId)" - if holecards or groups['posn']: - pname = "'all players'" - # set flag in self.columns to not show player name column - [x for x in self.columns if x[0] == 'pname'][0][1] = False - # can't do this yet (re-write doing more maths in python instead of sql?) - if numhands: - nametest = "(-1)" - else: - pname = "p.name" - # set flag in self.columns to show player name column - [x for x in self.columns if x[0] == 'pname'][0][1] = True - if numhands: - having = ' and count(1) > %d ' % (numhands,) - else: - if playerids: - nametest = str(tuple(playerids)) - nametest = nametest.replace("L", "") - nametest = nametest.replace(",)",")") - else: - nametest = "1 = 2" - pname = "p.name" - # set flag in self.columns to not show player name column - [x for x in self.columns if x[0] == 'pname'][0][1] = False - query = query.replace("", nametest) - query = query.replace("", pname) - query = query.replace("", having) - - gametest = "" - q = [] - for m in self.filters.display.items(): - if m[0] == 'Games' and m[1]: - for n in games: - if games[n]: - q.append(n) - if len(q) > 0: - gametest = str(tuple(q)) - gametest = gametest.replace("L", "") - gametest = gametest.replace(",)",")") - gametest = gametest.replace("u'","'") - gametest = "and gt.category in %s" % gametest - else: - gametest = "and gt.category IS NULL" - query = query.replace("", gametest) - - sitetest = "" - q = [] - for m in self.filters.display.items(): - if m[0] == 'Sites' and m[1]: - for n in sitenos: - q.append(n) - if len(q) > 0: - sitetest = str(tuple(q)) - sitetest = sitetest.replace("L", "") - sitetest = sitetest.replace(",)",")") - sitetest = sitetest.replace("u'","'") - sitetest = "and gt.siteId in %s" % sitetest - else: - sitetest = "and gt.siteId IS NULL" - query = query.replace("", sitetest) - - if seats: - query = query.replace('', 'between ' + str(seats['from']) + ' and ' + str(seats['to'])) - if 'show' in seats and seats['show']: - query = query.replace('', ',h.seats') - query = query.replace('', ',h.seats') - else: - query = query.replace('', '') - query = query.replace('', '') - else: - query = query.replace('', 'between 0 and 100') - query = query.replace('', '') - query = query.replace('', '') - - lims = [int(x) for x in limits if x.isdigit()] - potlims = [int(x[0:-2]) for x in limits if len(x) > 2 and x[-2:] == 'pl'] - nolims = [int(x[0:-2]) for x in limits if len(x) > 2 and x[-2:] == 'nl'] - bbtest = "and ( (gt.limitType = 'fl' and gt.bigBlind in " - # and ( (limit and bb in()) or (nolimit and bb in ()) ) - if lims: - blindtest = str(tuple(lims)) - blindtest = blindtest.replace("L", "") - blindtest = blindtest.replace(",)",")") - bbtest = bbtest + blindtest + ' ) ' - else: - bbtest = bbtest + '(-1) ) ' - bbtest = bbtest + " or (gt.limitType = 'pl' and gt.bigBlind in " - if potlims: - blindtest = str(tuple(potlims)) - blindtest = blindtest.replace("L", "") - blindtest = blindtest.replace(",)",")") - bbtest = bbtest + blindtest + ' ) ' - else: - bbtest = bbtest + '(-1) ) ' - bbtest = bbtest + " or (gt.limitType = 'nl' and gt.bigBlind in " - if nolims: - blindtest = str(tuple(nolims)) - blindtest = blindtest.replace("L", "") - blindtest = blindtest.replace(",)",")") - bbtest = bbtest + blindtest + ' ) )' - else: - bbtest = bbtest + '(-1) ) )' - if type == 'ring': - bbtest = bbtest + " and gt.type = 'ring' " - elif type == 'tour': - bbtest = " and gt.type = 'tour' " - query = query.replace("", bbtest) - - if holecards: # re-use level variables for hole card query - query = query.replace("", "hp.startcards") - query = query.replace("" - , ",case when floor((hp.startcards-1)/13) >= mod((hp.startcards-1),13) then hp.startcards + 0.1 " - + " else 13*mod((hp.startcards-1),13) + floor((hp.startcards-1)/13) + 1 " - + " end desc ") - else: - query = query.replace("", "") - groupLevels = "show" not in str(limits) - if groupLevels: - query = query.replace("", "p.name") # need to use p.name for sqlite posn stats to work - else: - query = query.replace("", "h.gameTypeId") - - # process self.detailFilters (a list of tuples) - flagtest = '' - #self.detailFilters = [('h.seats', 5, 6)] # for debug - if self.detailFilters: - for f in self.detailFilters: - if len(f) == 3: - # X between Y and Z - flagtest += ' and %s between %s and %s ' % (f[0], str(f[1]), str(f[2])) - query = query.replace("", flagtest) - - # allow for differences in sql cast() function: - if self.db.backend == self.MYSQL_INNODB: - query = query.replace("", 'signed ') - else: - query = query.replace("", '') - - # Filter on dates - query = query.replace("", " between '" + dates[0] + "' and '" + dates[1] + "'") - - # Group by position? - if groups['posn']: - #query = query.replace("", "case hp.position when '0' then 'Btn' else hp.position end") - query = query.replace("", "hp.position") - # set flag in self.columns to show posn column - [x for x in self.columns if x[0] == 'plposition'][0][1] = True - else: - query = query.replace("", "gt.base") - # unset flag in self.columns to hide posn column - [x for x in self.columns if x[0] == 'plposition'][0][1] = False - - #print "query =\n", query - return(query) - #end def refineQuery(self, query, playerids, sitenos, limits): - - def showDetailFilter(self, widget, data): - detailDialog = gtk.Dialog(title="Detailed Filters", parent=self.main_window - ,flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT - ,buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, - gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)) - - handbox = gtk.VBox(True, 0) - detailDialog.vbox.pack_start(handbox, False, False, 0) - handbox.show() - - label = gtk.Label("Hand Filters:") - handbox.add(label) - label.show() - - betweenFilters = [] - for htest in self.handtests: - hbox = gtk.HBox(False, 0) - handbox.pack_start(hbox, False, False, 0) - hbox.show() - - cb = gtk.CheckButton() - lbl_from = gtk.Label(htest[1]) - lbl_from.set_alignment(xalign=0.0, yalign=0.5) - lbl_tween = gtk.Label('between') - lbl_to = gtk.Label('and') - adj1 = gtk.Adjustment(value=htest[2], lower=0, upper=10, step_incr=1, page_incr=1, page_size=0) - sb1 = gtk.SpinButton(adjustment=adj1, climb_rate=0.0, digits=0) - adj2 = gtk.Adjustment(value=htest[3], lower=2, upper=10, step_incr=1, page_incr=1, page_size=0) - sb2 = gtk.SpinButton(adjustment=adj2, climb_rate=0.0, digits=0) - - for df in [x for x in self.detailFilters if x[0] == htest[0]]: - cb.set_active(True) - - hbox.pack_start(cb, expand=False, padding=3) - hbox.pack_start(lbl_from, expand=True, padding=3) - hbox.pack_start(lbl_tween, expand=False, padding=3) - hbox.pack_start(sb1, False, False, 0) - hbox.pack_start(lbl_to, expand=False, padding=3) - hbox.pack_start(sb2, False, False, 0) - - cb.show() - lbl_from.show() - lbl_tween.show() - sb1.show() - lbl_to.show() - sb2.show() - - htest[4:7] = [cb,sb1,sb2] - - response = detailDialog.run() - - if response == gtk.RESPONSE_ACCEPT: - self.detailFilters = [] - for ht in self.handtests: - if ht[4].get_active(): - self.detailFilters.append( (ht[0], ht[5].get_value_as_int(), ht[6].get_value_as_int()) ) - ht[2],ht[3] = ht[5].get_value_as_int(), ht[6].get_value_as_int() - print "detailFilters =", self.detailFilters - self.refreshStats(None, None) - - detailDialog.destroy() - - - - +class GuiPlayerStats(threading.Thread): + pass +#end class GuiPlayerStats diff --git a/pyfpdb/GuiPositionalStats.py b/pyfpdb/GuiPositionalStats.py index 3dd32c98..b114e892 100644 --- a/pyfpdb/GuiPositionalStats.py +++ b/pyfpdb/GuiPositionalStats.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2008-2010 Steffen Schaumburg @@ -24,8 +24,7 @@ from time import time, strftime import fpdb_import import Database -import Filters -import FpdbSQLQueries +import RingFilters class GuiPositionalStats (threading.Thread): def __init__(self, config, querylist, debug=True): @@ -58,7 +57,7 @@ class GuiPositionalStats (threading.Thread): "Button2" : False } - self.filters = Filters.Filters(self.db, self.conf, self.sql, display = filters_display) + self.filters = RingFilters.RingFilters(self.db, self.conf, self.sql, display = filters_display) self.filters.registerButton1Name("Refresh") self.filters.registerButton1Callback(self.refreshStats) diff --git a/pyfpdb/GuiPrefs.py b/pyfpdb/GuiPrefs.py index 1c68eb45..15ae7338 100755 --- a/pyfpdb/GuiPrefs.py +++ b/pyfpdb/GuiPrefs.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2008-2010 Carl Gherardi diff --git a/pyfpdb/GuiRingPlayerStats.py b/pyfpdb/GuiRingPlayerStats.py new file mode 100644 index 00000000..29aaf44c --- /dev/null +++ b/pyfpdb/GuiRingPlayerStats.py @@ -0,0 +1,681 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +#Copyright 2008-2010 Steffen Schaumburg +#This program is free software: you can redistribute it and/or modify +#it under the terms of the GNU Affero General Public License as published by +#the Free Software Foundation, version 3 of the License. +# +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +# +#You should have received a copy of the GNU Affero General Public License +#along with this program. If not, see . +#In the "official" distribution you can find the license in agpl-3.0.txt. + +import traceback +import threading +import pygtk +pygtk.require('2.0') +import gtk +import os +import sys +from time import time, strftime + +import Card +import fpdb_import +import Database +import RingFilters +import Charset +import GuiPlayerStats + +colalias,colshow,colheading,colxalign,colformat,coltype = 0,1,2,3,4,5 +ranks = {'x':0, '2':2, '3':3, '4':4, '5':5, '6':6, '7':7, '8':8, '9':9, 'T':10, 'J':11, 'Q':12, 'K':13, 'A':14} + +class GuiRingPlayerStats (GuiPlayerStats.GuiPlayerStats): + + def __init__(self, config, querylist, mainwin, debug=True): + self.debug = debug + self.conf = config + self.main_window = mainwin + self.sql = querylist + + self.liststore = [] # gtk.ListStore[] stores the contents of the grids + self.listcols = [] # gtk.TreeViewColumn[][] stores the columns in the grids + + self.MYSQL_INNODB = 2 + self.PGSQL = 3 + self.SQLITE = 4 + + # create new db connection to avoid conflicts with other threads + self.db = Database.Database(self.conf, sql=self.sql) + self.cursor = self.db.cursor + + settings = {} + settings.update(self.conf.get_db_parameters()) + settings.update(self.conf.get_tv_parameters()) + settings.update(self.conf.get_import_parameters()) + settings.update(self.conf.get_default_paths()) + + # text used on screen stored here so that it can be configured + self.filterText = {'handhead':'Hand Breakdown for all levels listed above' + } + + filters_display = { "Heroes" : True, + "Sites" : True, + "Games" : True, + "Limits" : True, + "LimitSep" : True, + "LimitType" : True, + "Type" : True, + "Seats" : True, + "SeatSep" : True, + "Dates" : True, + "Groups" : True, + "GroupsAll" : True, + "Button1" : True, + "Button2" : True + } + + self.filters = RingFilters.RingFilters(self.db, self.conf, self.sql, display = filters_display) + self.filters.registerButton1Name("_Filters") + self.filters.registerButton1Callback(self.showDetailFilter) + self.filters.registerButton2Name("_Refresh Stats") + self.filters.registerButton2Callback(self.refreshStats) + + # ToDo: store in config + # ToDo: create popup to adjust column config + # columns to display, keys match column name returned by sql, values in tuple are: + # is column displayed, column heading, xalignment, formatting, celltype + self.columns = [ ["game", True, "Game", 0.0, "%s", "str"] + , ["hand", False, "Hand", 0.0, "%s", "str"] # true not allowed for this line + , ["plposition", False, "Posn", 1.0, "%s", "str"] # true not allowed for this line (set in code) + , ["pname", False, "Name", 0.0, "%s", "str"] # true not allowed for this line (set in code) + , ["n", True, "Hds", 1.0, "%1.0f", "str"] + , ["avgseats", False, "Seats", 1.0, "%3.1f", "str"] + , ["vpip", True, "VPIP", 1.0, "%3.1f", "str"] + , ["pfr", True, "PFR", 1.0, "%3.1f", "str"] + , ["pf3", True, "PF3", 1.0, "%3.1f", "str"] + , ["aggfac", True, "AggFac", 1.0, "%2.2f", "str"] + , ["aggfrq", True, "AggFreq", 1.0, "%3.1f", "str"] + , ["conbet", True, "ContBet", 1.0, "%3.1f", "str"] + , ["steals", True, "Steals", 1.0, "%3.1f", "str"] + , ["saw_f", True, "Saw_F", 1.0, "%3.1f", "str"] + , ["sawsd", True, "SawSD", 1.0, "%3.1f", "str"] + , ["wtsdwsf", True, "WtSDwsF", 1.0, "%3.1f", "str"] + , ["wmsd", True, "W$SD", 1.0, "%3.1f", "str"] + , ["flafq", True, "FlAFq", 1.0, "%3.1f", "str"] + , ["tuafq", True, "TuAFq", 1.0, "%3.1f", "str"] + , ["rvafq", True, "RvAFq", 1.0, "%3.1f", "str"] + , ["pofafq", False, "PoFAFq", 1.0, "%3.1f", "str"] + , ["net", True, "Net($)", 1.0, "%6.2f", "cash"] + , ["bbper100", True, "bb/100", 1.0, "%4.2f", "str"] + , ["rake", True, "Rake($)", 1.0, "%6.2f", "cash"] + , ["bb100xr", True, "bbxr/100", 1.0, "%4.2f", "str"] + , ["variance", True, "Variance", 1.0, "%5.2f", "str"] + ] + + # Detail filters: This holds the data used in the popup window, extra values are + # added at the end of these lists during processing + # sql test, screen description, min, max + self.handtests = [ # already in filter class : ['h.seats', 'Number of Players', 2, 10] + ['h.maxSeats', 'Size of Table', 2, 10] + ,['h.playersVpi', 'Players who VPI', 0, 10] + ,['h.playersAtStreet1', 'Players at Flop', 0, 10] + ,['h.playersAtStreet2', 'Players at Turn', 0, 10] + ,['h.playersAtStreet3', 'Players at River', 0, 10] + ,['h.playersAtStreet4', 'Players at Street7', 0, 10] + ,['h.playersAtShowdown', 'Players at Showdown', 0, 10] + ,['h.street0Raises', 'Bets to See Flop', 0, 5] + ,['h.street1Raises', 'Bets to See Turn', 0, 5] + ,['h.street2Raises', 'Bets to See River', 0, 5] + ,['h.street3Raises', 'Bets to See Street7', 0, 5] + ,['h.street4Raises', 'Bets to See Showdown', 0, 5] + ] + + self.stats_frame = None + self.stats_vbox = None + self.detailFilters = [] # the data used to enhance the sql select + + #self.main_hbox = gtk.HBox(False, 0) + #self.main_hbox.show() + self.main_hbox = gtk.HPaned() + + self.stats_frame = gtk.Frame() + self.stats_frame.show() + + self.stats_vbox = gtk.VPaned() + self.stats_vbox.show() + self.stats_frame.add(self.stats_vbox) + # self.fillStatsFrame(self.stats_vbox) + + #self.main_hbox.pack_start(self.filters.get_vbox()) + #self.main_hbox.pack_start(self.stats_frame, expand=True, fill=True) + self.main_hbox.pack1(self.filters.get_vbox()) + self.main_hbox.pack2(self.stats_frame) + self.main_hbox.show() + + # make sure Hand column is not displayed + [x for x in self.columns if x[0] == 'hand'][0][1] = False + self.last_pos = -1 + + + def get_vbox(self): + """returns the vbox of this thread""" + return self.main_hbox + #end def get_vbox + + def refreshStats(self, widget, data): + self.last_pos = self.stats_vbox.get_position() + try: self.stats_vbox.destroy() + except AttributeError: pass + self.liststore = [] + self.listcols = [] + #self.stats_vbox = gtk.VBox(False, 0) + self.stats_vbox = gtk.VPaned() + self.stats_vbox.show() + self.stats_frame.add(self.stats_vbox) + self.fillStatsFrame(self.stats_vbox) + if self.last_pos > 0: + self.stats_vbox.set_position(self.last_pos) + #end def refreshStats + + def fillStatsFrame(self, vbox): + sites = self.filters.getSites() + heroes = self.filters.getHeroes() + siteids = self.filters.getSiteIds() + limits = self.filters.getLimits() + type = self.filters.getType() + seats = self.filters.getSeats() + groups = self.filters.getGroups() + dates = self.filters.getDates() + games = self.filters.getGames() + sitenos = [] + playerids = [] + + # Which sites are selected? + for site in sites: + if sites[site] == True: + sitenos.append(siteids[site]) + _hname = Charset.to_utf8(heroes[site]) + result = self.db.get_player_id(self.conf, site, _hname) + if result is not None: + playerids.append(int(result)) + + if not sitenos: + #Should probably pop up here. + print "No sites selected - defaulting to PokerStars" + sitenos = [2] + if not playerids: + print "No player ids found" + return + if not limits: + print "No limits found" + return + + self.createStatsTable(vbox, playerids, sitenos, limits, type, seats, groups, dates, games) + #end def fillStatsFrame + + def createStatsTable(self, vbox, playerids, sitenos, limits, type, seats, groups, dates, games): + startTime = time() + show_detail = True + + # Scrolled window for summary table + swin = gtk.ScrolledWindow(hadjustment=None, vadjustment=None) + swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + swin.show() + vbox.pack1(swin) + + # Display summary table at top of page + # 3rd parameter passes extra flags, currently includes: + # holecards - whether to display card breakdown (True/False) + # numhands - min number hands required when displaying all players + # gridnum - index for grid data structures + flags = [False, self.filters.getNumHands(), 0] + self.addGrid(swin, 'playerDetailedStats', flags, playerids + ,sitenos, limits, type, seats, groups, dates, games) + + if 'allplayers' in groups and groups['allplayers']: + # can't currently do this combination so skip detailed table + show_detail = False + + if show_detail: + # Separator + vbox2 = gtk.VBox(False, 0) + heading = gtk.Label(self.filterText['handhead']) + heading.show() + vbox2.pack_start(heading, expand=False, padding=3) + + # Scrolled window for detailed table (display by hand) + swin = gtk.ScrolledWindow(hadjustment=None, vadjustment=None) + swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + swin.show() + vbox2.pack_start(swin, expand=True, padding=3) + vbox.pack2(vbox2) + vbox2.show() + + # Detailed table + flags[0] = True + flags[2] = 1 + self.addGrid(swin, 'playerDetailedStats', flags, playerids + ,sitenos, limits, type, seats, groups, dates, games) + + self.db.rollback() + print "Stats page displayed in %4.2f seconds" % (time() - startTime) + #end def createStatsTable + + def reset_style_render_func(self, treeviewcolumn, cell, model, iter): + cell.set_property('foreground', 'black') + #end def reset_style_render_func + + def ledger_style_render_func(self, tvcol, cell, model, iter): + str = cell.get_property('text') + if '-' in str: + str = str.replace("-", "") + str = "(%s)" %(str) + cell.set_property('text', str) + cell.set_property('foreground', 'red') + else: + cell.set_property('foreground', 'darkgreen') + + return + + def sortnums(self, model, iter1, iter2, nums): + try: + ret = 0 + (n, grid) = nums + a = self.liststore[grid].get_value(iter1, n) + b = self.liststore[grid].get_value(iter2, n) + if 'f' in self.cols_to_show[n][4]: + try: a = float(a) + except: a = 0.0 + try: b = float(b) + except: b = 0.0 + if n == 0 and grid == 1: #make sure it only works on the starting hands + a1,a2,a3 = ranks[a[0]], ranks[a[1]], (a+'o')[2] + b1,b2,b3 = ranks[b[0]], ranks[b[1]], (b+'o')[2] + if a1 > b1 or ( a1 == b1 and (a2 > b2 or (a2 == b2 and a3 > b3) ) ): + ret = 1 + else: + ret = -1 + else: + if a < b: + ret = -1 + elif a == b: + ret = 0 + else: + ret = 1 + #print "n =", n, "iter1[n] =", self.liststore[grid].get_value(iter1,n), "iter2[n] =", self.liststore[grid].get_value(iter2,n), "ret =", ret + except: + err = traceback.extract_tb(sys.exc_info()[2]) + print "***sortnums error: " + str(sys.exc_info()[1]) + print "\n".join( [e[0]+':'+str(e[1])+" "+e[2] for e in err] ) + + return(ret) + + def sortcols(self, col, nums): + try: + #This doesn't actually work yet - clicking heading in top section sorts bottom section :-( + (n, grid) = nums + if not col.get_sort_indicator() or col.get_sort_order() == gtk.SORT_ASCENDING: + col.set_sort_order(gtk.SORT_DESCENDING) + else: + col.set_sort_order(gtk.SORT_ASCENDING) + self.liststore[grid].set_sort_column_id(n, col.get_sort_order()) + self.liststore[grid].set_sort_func(n, self.sortnums, (n,grid)) + for i in xrange(len(self.listcols[grid])): + self.listcols[grid][i].set_sort_indicator(False) + self.listcols[grid][n].set_sort_indicator(True) + # use this listcols[col].set_sort_indicator(True) + # to turn indicator off for other cols + except: + err = traceback.extract_tb(sys.exc_info()[2]) + print "***sortcols error: " + str(sys.exc_info()[1]) + print "\n".join( [e[0]+':'+str(e[1])+" "+e[2] for e in err] ) + #end def sortcols + + def addGrid(self, vbox, query, flags, playerids, sitenos, limits, type, seats, groups, dates, games): + counter = 0 + row = 0 + sqlrow = 0 + if not flags: holecards,grid = False,0 + else: holecards,grid = flags[0],flags[2] + + tmp = self.sql.query[query] + tmp = self.refineQuery(tmp, flags, playerids, sitenos, limits, type, seats, groups, dates, games) + self.cursor.execute(tmp) + result = self.cursor.fetchall() + colnames = [desc[0].lower() for desc in self.cursor.description] + + # pre-fetch some constant values: + self.cols_to_show = [x for x in self.columns if x[colshow]] + hgametypeid_idx = colnames.index('hgametypeid') + + assert len(self.liststore) == grid, "len(self.liststore)="+str(len(self.liststore))+" grid-1="+str(grid) + self.liststore.append( gtk.ListStore(*([str] * len(self.cols_to_show))) ) + view = gtk.TreeView(model=self.liststore[grid]) + view.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_BOTH) + #vbox.pack_start(view, expand=False, padding=3) + vbox.add(view) + textcell = gtk.CellRendererText() + textcell50 = gtk.CellRendererText() + textcell50.set_property('xalign', 0.5) + numcell = gtk.CellRendererText() + numcell.set_property('xalign', 1.0) + assert len(self.listcols) == grid + self.listcols.append( [] ) + + # Create header row eg column: ("game", True, "Game", 0.0, "%s") + for col, column in enumerate(self.cols_to_show): + if column[colalias] == 'game' and holecards: + s = [x for x in self.columns if x[colalias] == 'hand'][0][colheading] + else: + s = column[colheading] + self.listcols[grid].append(gtk.TreeViewColumn(s)) + view.append_column(self.listcols[grid][col]) + if column[colformat] == '%s': + if column[colxalign] == 0.0: + self.listcols[grid][col].pack_start(textcell, expand=True) + self.listcols[grid][col].add_attribute(textcell, 'text', col) + cellrend = textcell + else: + self.listcols[grid][col].pack_start(textcell50, expand=True) + self.listcols[grid][col].add_attribute(textcell50, 'text', col) + cellrend = textcell50 + self.listcols[grid][col].set_expand(True) + else: + self.listcols[grid][col].pack_start(numcell, expand=True) + self.listcols[grid][col].add_attribute(numcell, 'text', col) + self.listcols[grid][col].set_expand(True) + cellrend = numcell + #self.listcols[grid][col].set_alignment(column[colxalign]) # no effect? + self.listcols[grid][col].set_clickable(True) + self.listcols[grid][col].connect("clicked", self.sortcols, (col,grid)) + if col == 0: + self.listcols[grid][col].set_sort_order(gtk.SORT_DESCENDING) + self.listcols[grid][col].set_sort_indicator(True) + if column[coltype] == 'cash': + self.listcols[grid][col].set_cell_data_func(numcell, self.ledger_style_render_func) + else: + self.listcols[grid][col].set_cell_data_func(cellrend, self.reset_style_render_func) + + rows = len(result) # +1 for title row + + while sqlrow < rows: + treerow = [] + for col,column in enumerate(self.cols_to_show): + if column[colalias] in colnames: + value = result[sqlrow][colnames.index(column[colalias])] + if column[colalias] == 'plposition': + if value == 'B': + value = 'BB' + elif value == 'S': + value = 'SB' + elif value == '0': + value = 'Btn' + else: + if column[colalias] == 'game': + if holecards: + value = Card.twoStartCardString( result[sqlrow][hgametypeid_idx] ) + else: + minbb = result[sqlrow][colnames.index('minbigblind')] + maxbb = result[sqlrow][colnames.index('maxbigblind')] + value = result[sqlrow][colnames.index('limittype')] + ' ' \ + + result[sqlrow][colnames.index('category')].title() + ' ' \ + + result[sqlrow][colnames.index('name')] + ' $' + if 100 * int(minbb/100.0) != minbb: + value += '%.2f' % (minbb/100.0) + else: + value += '%.0f' % (minbb/100.0) + if minbb != maxbb: + if 100 * int(maxbb/100.0) != maxbb: + value += ' - $' + '%.2f' % (maxbb/100.0) + else: + value += ' - $' + '%.0f' % (maxbb/100.0) + else: + continue + if value and value != -999: + treerow.append(column[colformat] % value) + else: + treerow.append(' ') + iter = self.liststore[grid].append(treerow) + #print treerow + sqlrow += 1 + row += 1 + vbox.show_all() + #end def addGrid + + def refineQuery(self, query, flags, playerids, sitenos, limits, type, seats, groups, dates, games): + having = '' + if not flags: + holecards = False + numhands = 0 + else: + holecards = flags[0] + numhands = flags[1] + + if 'allplayers' in groups and groups['allplayers']: + nametest = "(hp.playerId)" + if holecards or groups['posn']: + pname = "'all players'" + # set flag in self.columns to not show player name column + [x for x in self.columns if x[0] == 'pname'][0][1] = False + # can't do this yet (re-write doing more maths in python instead of sql?) + if numhands: + nametest = "(-1)" + else: + pname = "p.name" + # set flag in self.columns to show player name column + [x for x in self.columns if x[0] == 'pname'][0][1] = True + if numhands: + having = ' and count(1) > %d ' % (numhands,) + else: + if playerids: + nametest = str(tuple(playerids)) + nametest = nametest.replace("L", "") + nametest = nametest.replace(",)",")") + else: + nametest = "1 = 2" + pname = "p.name" + # set flag in self.columns to not show player name column + [x for x in self.columns if x[0] == 'pname'][0][1] = False + query = query.replace("", nametest) + query = query.replace("", pname) + query = query.replace("", having) + + gametest = "" + q = [] + for m in self.filters.display.items(): + if m[0] == 'Games' and m[1]: + for n in games: + if games[n]: + q.append(n) + if len(q) > 0: + gametest = str(tuple(q)) + gametest = gametest.replace("L", "") + gametest = gametest.replace(",)",")") + gametest = gametest.replace("u'","'") + gametest = "and gt.category in %s" % gametest + else: + gametest = "and gt.category IS NULL" + query = query.replace("", gametest) + + sitetest = "" + q = [] + for m in self.filters.display.items(): + if m[0] == 'Sites' and m[1]: + for n in sitenos: + q.append(n) + if len(q) > 0: + sitetest = str(tuple(q)) + sitetest = sitetest.replace("L", "") + sitetest = sitetest.replace(",)",")") + sitetest = sitetest.replace("u'","'") + sitetest = "and gt.siteId in %s" % sitetest + else: + sitetest = "and gt.siteId IS NULL" + query = query.replace("", sitetest) + + if seats: + query = query.replace('', 'between ' + str(seats['from']) + ' and ' + str(seats['to'])) + if 'show' in seats and seats['show']: + query = query.replace('', ',h.seats') + query = query.replace('', ',h.seats') + else: + query = query.replace('', '') + query = query.replace('', '') + else: + query = query.replace('', 'between 0 and 100') + query = query.replace('', '') + query = query.replace('', '') + + lims = [int(x) for x in limits if x.isdigit()] + potlims = [int(x[0:-2]) for x in limits if len(x) > 2 and x[-2:] == 'pl'] + nolims = [int(x[0:-2]) for x in limits if len(x) > 2 and x[-2:] == 'nl'] + bbtest = "and ( (gt.limitType = 'fl' and gt.bigBlind in " + # and ( (limit and bb in()) or (nolimit and bb in ()) ) + if lims: + blindtest = str(tuple(lims)) + blindtest = blindtest.replace("L", "") + blindtest = blindtest.replace(",)",")") + bbtest = bbtest + blindtest + ' ) ' + else: + bbtest = bbtest + '(-1) ) ' + bbtest = bbtest + " or (gt.limitType = 'pl' and gt.bigBlind in " + if potlims: + blindtest = str(tuple(potlims)) + blindtest = blindtest.replace("L", "") + blindtest = blindtest.replace(",)",")") + bbtest = bbtest + blindtest + ' ) ' + else: + bbtest = bbtest + '(-1) ) ' + bbtest = bbtest + " or (gt.limitType = 'nl' and gt.bigBlind in " + if nolims: + blindtest = str(tuple(nolims)) + blindtest = blindtest.replace("L", "") + blindtest = blindtest.replace(",)",")") + bbtest = bbtest + blindtest + ' ) )' + else: + bbtest = bbtest + '(-1) ) )' + if type == 'ring': + bbtest = bbtest + " and gt.type = 'ring' " + elif type == 'tour': + bbtest = " and gt.type = 'tour' " + query = query.replace("", bbtest) + + if holecards: # re-use level variables for hole card query + query = query.replace("", "hp.startcards") + query = query.replace("" + , ",case when floor((hp.startcards-1)/13) >= mod((hp.startcards-1),13) then hp.startcards + 0.1 " + + " else 13*mod((hp.startcards-1),13) + floor((hp.startcards-1)/13) + 1 " + + " end desc ") + else: + query = query.replace("", "") + groupLevels = "show" not in str(limits) + if groupLevels: + query = query.replace("", "p.name") # need to use p.name for sqlite posn stats to work + else: + query = query.replace("", "h.gameTypeId") + + # process self.detailFilters (a list of tuples) + flagtest = '' + #self.detailFilters = [('h.seats', 5, 6)] # for debug + if self.detailFilters: + for f in self.detailFilters: + if len(f) == 3: + # X between Y and Z + flagtest += ' and %s between %s and %s ' % (f[0], str(f[1]), str(f[2])) + query = query.replace("", flagtest) + + # allow for differences in sql cast() function: + if self.db.backend == self.MYSQL_INNODB: + query = query.replace("", 'signed ') + else: + query = query.replace("", '') + + # Filter on dates + query = query.replace("", " between '" + dates[0] + "' and '" + dates[1] + "'") + + # Group by position? + if groups['posn']: + #query = query.replace("", "case hp.position when '0' then 'Btn' else hp.position end") + query = query.replace("", "hp.position") + # set flag in self.columns to show posn column + [x for x in self.columns if x[0] == 'plposition'][0][1] = True + else: + query = query.replace("", "gt.base") + # unset flag in self.columns to hide posn column + [x for x in self.columns if x[0] == 'plposition'][0][1] = False + + #print "query =\n", query + return(query) + #end def refineQuery + + def showDetailFilter(self, widget, data): + detailDialog = gtk.Dialog(title="Detailed Filters", parent=self.main_window + ,flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT + ,buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, + gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)) + + handbox = gtk.VBox(True, 0) + detailDialog.vbox.pack_start(handbox, False, False, 0) + handbox.show() + + label = gtk.Label("Hand Filters:") + handbox.add(label) + label.show() + + betweenFilters = [] + for htest in self.handtests: + hbox = gtk.HBox(False, 0) + handbox.pack_start(hbox, False, False, 0) + hbox.show() + + cb = gtk.CheckButton() + lbl_from = gtk.Label(htest[1]) + lbl_from.set_alignment(xalign=0.0, yalign=0.5) + lbl_tween = gtk.Label('between') + lbl_to = gtk.Label('and') + adj1 = gtk.Adjustment(value=htest[2], lower=0, upper=10, step_incr=1, page_incr=1, page_size=0) + sb1 = gtk.SpinButton(adjustment=adj1, climb_rate=0.0, digits=0) + adj2 = gtk.Adjustment(value=htest[3], lower=2, upper=10, step_incr=1, page_incr=1, page_size=0) + sb2 = gtk.SpinButton(adjustment=adj2, climb_rate=0.0, digits=0) + + for df in [x for x in self.detailFilters if x[0] == htest[0]]: + cb.set_active(True) + + hbox.pack_start(cb, expand=False, padding=3) + hbox.pack_start(lbl_from, expand=True, padding=3) + hbox.pack_start(lbl_tween, expand=False, padding=3) + hbox.pack_start(sb1, False, False, 0) + hbox.pack_start(lbl_to, expand=False, padding=3) + hbox.pack_start(sb2, False, False, 0) + + cb.show() + lbl_from.show() + lbl_tween.show() + sb1.show() + lbl_to.show() + sb2.show() + + htest[4:7] = [cb,sb1,sb2] + + response = detailDialog.run() + + if response == gtk.RESPONSE_ACCEPT: + self.detailFilters = [] + for ht in self.handtests: + if ht[4].get_active(): + self.detailFilters.append( (ht[0], ht[5].get_value_as_int(), ht[6].get_value_as_int()) ) + ht[2],ht[3] = ht[5].get_value_as_int(), ht[6].get_value_as_int() + print "detailFilters =", self.detailFilters + self.refreshStats(None, None) + + detailDialog.destroy() + + + + + diff --git a/pyfpdb/GuiSessionViewer.py b/pyfpdb/GuiSessionViewer.py index ed4e9eff..f8c0d4cc 100755 --- a/pyfpdb/GuiSessionViewer.py +++ b/pyfpdb/GuiSessionViewer.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2008-2010 Steffen Schaumburg @@ -45,8 +45,7 @@ except ImportError, inst: import Card import fpdb_import import Database -import Filters -import FpdbSQLQueries +import RingFilters import Charset class GuiSessionViewer (threading.Thread): @@ -96,7 +95,7 @@ class GuiSessionViewer (threading.Thread): "Button2" : False } - self.filters = Filters.Filters(self.db, self.conf, self.sql, display = filters_display) + self.filters = RingFilters.RingFilters(self.db, self.conf, self.sql, display = filters_display) self.filters.registerButton1Name("_Refresh") self.filters.registerButton1Callback(self.refreshStats) diff --git a/pyfpdb/GuiTableViewer.py b/pyfpdb/GuiTableViewer.py index 04b2025b..8086c0a0 100644 --- a/pyfpdb/GuiTableViewer.py +++ b/pyfpdb/GuiTableViewer.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2008-2010 Steffen Schaumburg diff --git a/pyfpdb/GuiTourneyPlayerStats.py b/pyfpdb/GuiTourneyPlayerStats.py new file mode 100644 index 00000000..7523f594 --- /dev/null +++ b/pyfpdb/GuiTourneyPlayerStats.py @@ -0,0 +1,457 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +#Copyright 2010 Steffen Schaumburg +#This program is free software: you can redistribute it and/or modify +#it under the terms of the GNU Affero General Public License as published by +#the Free Software Foundation, version 3 of the License. +# +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +# +#You should have received a copy of the GNU Affero General Public License +#along with this program. If not, see . +#In the "official" distribution you can find the license in agpl-3.0.txt. + +#import traceback +import threading +import pygtk +pygtk.require('2.0') +import gtk +#import os +#import sys +from time import time, strftime + +#import Card +#import fpdb_import +#import Database +import Charset +import TourneyFilters +import GuiPlayerStats + +colalias,colshow,colheading,colxalign,colformat,coltype = 0,1,2,3,4,5 + +class GuiTourneyPlayerStats (GuiPlayerStats.GuiPlayerStats): + def __init__(self, config, db, sql, mainwin, debug=True): + self.conf = config + self.db = db + self.cursor = self.db.cursor + self.sql = sql + self.main_window = mainwin + self.debug = debug + + self.liststore = [] # gtk.ListStore[] stores the contents of the grids + self.listcols = [] # gtk.TreeViewColumn[][] stores the columns in the grids + + filters_display = { "Heroes" : True, + "Sites" : True, + #"Games" : True, + #"Limits" : True, + #"LimitSep" : True, + #"LimitType" : True, + #"Type" : True, + "Seats" : True, + #"SeatSep" : True, + "Dates" : True, + #"Groups" : True, + #"GroupsAll" : True, + #"Button1" : True, + "Button2" : True} + + self.stats_frame = None + self.stats_vbox = None + self.detailFilters = [] # the data used to enhance the sql select + + self.main_hbox = gtk.HPaned() + + self.filters = TourneyFilters.TourneyFilters(self.db, self.conf, self.sql, display = filters_display) + #self.filters.registerButton1Name("_Filters") + #self.filters.registerButton1Callback(self.showDetailFilter) + self.filters.registerButton2Name("_Refresh Stats") + self.filters.registerButton2Callback(self.refreshStats) + + # ToDo: store in config + # ToDo: create popup to adjust column config + # columns to display, keys match column name returned by sql, values in tuple are: + # is column displayed, column heading, xalignment, formatting, celltype + self.columns = [ ["siteName", True, "Site", 0.0, "%s", "str"] + #,["tourney", False, "Tourney", 0.0, "%s", "str"] # true not allowed for this line + , ["category", True, "Cat.", 0.0, "%s", "str"] + , ["limitType", True, "Limit", 0.0, "%s", "str"] + , ["currency", True, "Curr.", 0.0, "%s", "str"] + , ["buyIn", True, "BuyIn", 1.0, "%3.2f", "str"] + , ["fee", True, "Fee", 1.0, "%3.2f", "str"] + , ["playerName", False, "Name", 0.0, "%s", "str"] # true not allowed for this line (set in code) + , ["tourneyCount", True, "#", 1.0, "%1.0f", "str"] + , ["itm", True, "ITM%", 1.0, "%3.2f", "str"] + , ["1st", False, "1st", 1.0, "%1.0f", "str"] + , ["2nd", True, "2nd", 1.0, "%1.0f", "str"] + , ["3rd", True, "3rd", 1.0, "%1.0f", "str"] + , ["unknownRank", True, "Rank?", 1.0, "%1.0f", "str"] + , ["spent", True, "Spent", 1.0, "%3.2f", "str"] + , ["won", True, "Won", 1.0, "%3.2f", "str"] + , ["roi", True, "ROI%", 1.0, "%3.0f", "str"] + , ["profitPerTourney", True,"$/Tour", 1.0, "%3.2f", "str"]] + + self.stats_frame = gtk.Frame() + self.stats_frame.show() + + self.stats_vbox = gtk.VPaned() + self.stats_vbox.show() + self.stats_frame.add(self.stats_vbox) + # self.fillStatsFrame(self.stats_vbox) + + #self.main_hbox.pack_start(self.filters.get_vbox()) + #self.main_hbox.pack_start(self.stats_frame, expand=True, fill=True) + self.main_hbox.pack1(self.filters.get_vbox()) + self.main_hbox.pack2(self.stats_frame) + self.main_hbox.show() + #end def __init__ + + def addGrid(self, vbox, query, numTourneys, tourneyTypes, playerids, sitenos, seats, dates): + #print "start of addGrid query", query + #print "start of addGrid. numTourneys:",numTourneys,"tourneyTypes:", tourneyTypes, "playerids:",playerids + counter = 0 + row = 0 + sqlrow = 0 + grid=numTourneys #TODO: should this be numTourneyTypes? + + query = self.sql.query[query] + query = self.refineQuery(query, numTourneys, tourneyTypes, playerids, sitenos, seats, dates) + self.cursor.execute(query) + result = self.cursor.fetchall() + #print "result of the big query in addGrid:",result + colnames = [desc[0] for desc in self.cursor.description] + + # pre-fetch some constant values: + #self.cols_to_show = [x for x in self.columns if x[colshow]] + #htourneytypeid_idx = colnames.index('tourneyTypeId') + self.cols_to_show = self.columns #TODO do i need above 2 lines? + + assert len(self.liststore) == grid, "len(self.liststore)="+str(len(self.liststore))+" grid-1="+str(grid) + self.liststore.append( gtk.ListStore(*([str] * len(self.cols_to_show))) ) + view = gtk.TreeView(model=self.liststore[grid]) + view.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_BOTH) + #vbox.pack_start(view, expand=False, padding=3) + vbox.add(view) + textcell = gtk.CellRendererText() + textcell50 = gtk.CellRendererText() + textcell50.set_property('xalign', 0.5) + numcell = gtk.CellRendererText() + numcell.set_property('xalign', 1.0) + assert len(self.listcols) == grid + self.listcols.append( [] ) + + # Create header row eg column: ("game", True, "Game", 0.0, "%s") + for col, column in enumerate(self.cols_to_show): + if column[colalias] == 'game' and holecards: + s = [x for x in self.columns if x[colalias] == 'hand'][0][colheading] + else: + s = column[colheading] + self.listcols[grid].append(gtk.TreeViewColumn(s)) + view.append_column(self.listcols[grid][col]) + if column[colformat] == '%s': + if column[colxalign] == 0.0: + self.listcols[grid][col].pack_start(textcell, expand=True) + self.listcols[grid][col].add_attribute(textcell, 'text', col) + cellrend = textcell + else: + self.listcols[grid][col].pack_start(textcell50, expand=True) + self.listcols[grid][col].add_attribute(textcell50, 'text', col) + cellrend = textcell50 + self.listcols[grid][col].set_expand(True) + else: + self.listcols[grid][col].pack_start(numcell, expand=True) + self.listcols[grid][col].add_attribute(numcell, 'text', col) + self.listcols[grid][col].set_expand(True) + cellrend = numcell + #self.listcols[grid][col].set_alignment(column[colxalign]) # no effect? + self.listcols[grid][col].set_clickable(True) + self.listcols[grid][col].connect("clicked", self.sortCols, (col,grid)) + if col == 0: + self.listcols[grid][col].set_sort_order(gtk.SORT_DESCENDING) + self.listcols[grid][col].set_sort_indicator(True) + if column[coltype] == 'cash': + self.listcols[grid][col].set_cell_data_func(numcell, self.ledger_style_render_func) + else: + self.listcols[grid][col].set_cell_data_func(cellrend, self.reset_style_render_func) + + rows = len(result) # +1 for title row + + while sqlrow < rows: + treerow = [] + for col,column in enumerate(self.cols_to_show): + if column[colalias] in colnames: + value = result[sqlrow][colnames.index(column[colalias])] + else: + value = 111 + if value and value != -999: + treerow.append(column[colformat] % value) + else: + treerow.append(' ') + #print "addGrid, just before end of big for. grid:",grid,"treerow:",treerow + iter = self.liststore[grid].append(treerow) + sqlrow += 1 + row += 1 + vbox.show_all() + #end def addGrid + + def createStatsTable(self, vbox, tourneyTypes, playerids, sitenos, seats, dates): + startTime = time() + show_detail = True + + # Scrolled window for summary table + swin = gtk.ScrolledWindow(hadjustment=None, vadjustment=None) + swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + swin.show() + vbox.pack1(swin) + + numTourneys = self.filters.getNumTourneys() + self.addGrid(swin, 'tourneyPlayerDetailedStats', numTourneys, tourneyTypes, playerids + ,sitenos, seats, dates) + + #if 'allplayers' in groups and groups['allplayers']: + # can't currently do this combination so skip detailed table + show_detail = False + + if show_detail: + # Separator + vbox2 = gtk.VBox(False, 0) + heading = gtk.Label(self.filterText['handhead']) + heading.show() + vbox2.pack_start(heading, expand=False, padding=3) + + # Scrolled window for detailed table (display by hand) + swin = gtk.ScrolledWindow(hadjustment=None, vadjustment=None) + swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + swin.show() + vbox2.pack_start(swin, expand=True, padding=3) + vbox.pack2(vbox2) + vbox2.show() + + # Detailed table + flags[0] = True + flags[2] = 1 + self.addGrid(swin, 'playerDetailedStats', flags, playerids, sitenos, seats, dates) + + self.db.rollback() + print "Stats page displayed in %4.2f seconds" % (time() - startTime) + #end def createStatsTable + + def fillStatsFrame(self, vbox): + tourneyTypes = self.filters.getTourneyTypes() + #tourneys = self.tourneys.getTourneys() + sites = self.filters.getSites() + heroes = self.filters.getHeroes() + siteids = self.filters.getSiteIds() + seats = self.filters.getSeats() + dates = self.filters.getDates() + sitenos = [] + playerids = [] + + # Which sites are selected? + for site in sites: + if sites[site] == True: + sitenos.append(siteids[site]) + _hname = Charset.to_utf8(heroes[site]) + result = self.db.get_player_id(self.conf, site, _hname) + if result is not None: + playerids.append(int(result)) + + if not sitenos: + #Should probably pop up here. + print "No sites selected - defaulting to PokerStars" + sitenos = [2] + if not playerids: + print "No player ids found" + return + + self.createStatsTable(vbox, tourneyTypes, playerids, sitenos, seats, dates) + #end def fillStatsFrame + + def get_vbox(self): + """returns the vbox of this thread""" + return self.main_hbox + #end def get_vbox + + def refineQuery(self, query, numTourneys, tourneyTypes, playerids, sitenos, seats, dates): + having = '' + + #print "start of refinequery, playerids:",playerids + if playerids: + nametest = str(tuple(playerids)) + nametest = nametest.replace("L", "") + nametest = nametest.replace(",)",")") + else: + nametest = "1 = 2" + #print "refinequery, nametest after initial creation:",nametest + pname = "p.name" + # set flag in self.columns to not show player name column + #[x for x in self.columns if x[0] == 'pname'][0][1] = False #TODO: fix and reactivate + + query = query.replace("", nametest) + query = query.replace("", pname) + query = query.replace("", having) + + #TODO: remove, or replace with tourneytest + #gametest = "" + #q = [] + #for m in self.filters.display.items(): + # if m[0] == 'Games' and m[1]: + # for n in games: + # if games[n]: + # q.append(n) + # if len(q) > 0: + # gametest = str(tuple(q)) + # gametest = gametest.replace("L", "") + # gametest = gametest.replace(",)",")") + # gametest = gametest.replace("u'","'") + # gametest = "and gt.category in %s" % gametest + # else: + # gametest = "and gt.category IS NULL" + #query = query.replace("", gametest) + + sitetest = "" + q = [] + for m in self.filters.display.items(): + if m[0] == 'Sites' and m[1]: + for n in sitenos: + q.append(n) + if len(q) > 0: + sitetest = str(tuple(q)) + sitetest = sitetest.replace("L", "") + sitetest = sitetest.replace(",)",")") + sitetest = sitetest.replace("u'","'") + sitetest = "and tt.siteId in %s" % sitetest#[1:-1] + else: + sitetest = "and tt.siteId IS NULL" + #print "refinequery, sitetest before its use for replacement:",sitetest + query = query.replace("", sitetest) + + if seats: + query = query.replace('', 'between ' + str(seats['from']) + ' and ' + str(seats['to'])) + if 'show' in seats and seats['show']: + query = query.replace('', ',h.seats') + query = query.replace('', ',h.seats') + else: + query = query.replace('', '') + query = query.replace('', '') + else: + query = query.replace('', 'between 0 and 100') + query = query.replace('', '') + query = query.replace('', '') + + #lims = [int(x) for x in limits if x.isdigit()] + #potlims = [int(x[0:-2]) for x in limits if len(x) > 2 and x[-2:] == 'pl'] + #nolims = [int(x[0:-2]) for x in limits if len(x) > 2 and x[-2:] == 'nl'] + #bbtest = "and ( (gt.limitType = 'fl' and gt.bigBlind in " + # and ( (limit and bb in()) or (nolimit and bb in ()) ) + #if lims: + # blindtest = str(tuple(lims)) + # blindtest = blindtest.replace("L", "") + # blindtest = blindtest.replace(",)",")") + # bbtest = bbtest + blindtest + ' ) ' + #else: + # bbtest = bbtest + '(-1) ) ' + #bbtest = bbtest + " or (gt.limitType = 'pl' and gt.bigBlind in " + #if potlims: + # blindtest = str(tuple(potlims)) + # blindtest = blindtest.replace("L", "") + # blindtest = blindtest.replace(",)",")") + # bbtest = bbtest + blindtest + ' ) ' + #else: + # bbtest = bbtest + '(-1) ) ' + #bbtest = bbtest + " or (gt.limitType = 'nl' and gt.bigBlind in " + #if nolims: + # blindtest = str(tuple(nolims)) + # blindtest = blindtest.replace("L", "") + # blindtest = blindtest.replace(",)",")") + # bbtest = bbtest + blindtest + ' ) )' + #else: + # bbtest = bbtest + '(-1) ) )' + + #if type == 'ring': + # bbtest = bbtest + " and gt.type = 'ring' " + #elif type == 'tour': + #bbtest = " and gt.type = 'tour' " + + #query = query.replace("", bbtest) + + #query = query.replace("", "") + + # process self.detailFilters (a list of tuples) + flagtest = '' + #self.detailFilters = [('h.seats', 5, 6)] # for debug + if self.detailFilters: + for f in self.detailFilters: + if len(f) == 3: + # X between Y and Z + flagtest += ' and %s between %s and %s ' % (f[0], str(f[1]), str(f[2])) + query = query.replace("", flagtest) + + # allow for differences in sql cast() function: + if self.db.backend == self.db.MYSQL_INNODB: + query = query.replace("", 'signed ') + else: + query = query.replace("", '') + + # Filter on dates + query = query.replace("", " between '" + dates[0] + "' and '" + dates[1] + "'") + + # Group by position? + #if groups['posn']: + # #query = query.replace("", "case hp.position when '0' then 'Btn' else hp.position end") + # query = query.replace("", "hp.position") + # # set flag in self.columns to show posn column + # [x for x in self.columns if x[0] == 'plposition'][0][1] = True + #else: + # query = query.replace("", "gt.base") + # # unset flag in self.columns to hide posn column + # [x for x in self.columns if x[0] == 'plposition'][0][1] = False + + #print "query at end of refine query:", query + return(query) + #end def refineQuery + + def refreshStats(self, widget, data): + self.last_pos = self.stats_vbox.get_position() + try: self.stats_vbox.destroy() + except AttributeError: pass + self.liststore = [] + self.listcols = [] + #self.stats_vbox = gtk.VBox(False, 0) + self.stats_vbox = gtk.VPaned() + self.stats_vbox.show() + self.stats_frame.add(self.stats_vbox) + self.fillStatsFrame(self.stats_vbox) + if self.last_pos > 0: + self.stats_vbox.set_position(self.last_pos) + #end def refreshStats + + def reset_style_render_func(self, treeviewcolumn, cell, model, iter): + cell.set_property('foreground', 'black') + #end def reset_style_render_func + + def sortCols(self, col, nums): + try: + #This doesn't actually work yet - clicking heading in top section sorts bottom section :-( + (n, grid) = nums + if not col.get_sort_indicator() or col.get_sort_order() == gtk.SORT_ASCENDING: + col.set_sort_order(gtk.SORT_DESCENDING) + else: + col.set_sort_order(gtk.SORT_ASCENDING) + self.liststore[grid].set_sort_column_id(n, col.get_sort_order()) + self.liststore[grid].set_sort_func(n, self.sortnums, (n,grid)) + for i in xrange(len(self.listcols[grid])): + self.listcols[grid][i].set_sort_indicator(False) + self.listcols[grid][n].set_sort_indicator(True) + # use this listcols[col].set_sort_indicator(True) + # to turn indicator off for other cols + except: + err = traceback.extract_tb(sys.exc_info()[2]) + print "***sortCols error: " + str(sys.exc_info()[1]) + print "\n".join( [e[0]+':'+str(e[1])+" "+e[2] for e in err] ) + #end def sortCols +#end class GuiTourneyPlayerStats diff --git a/pyfpdb/HUD_config.xml.example b/pyfpdb/HUD_config.xml.example index 8966eb78..c1d1f644 100644 --- a/pyfpdb/HUD_config.xml.example +++ b/pyfpdb/HUD_config.xml.example @@ -644,5 +644,9 @@ Left-Drag to Move" + + + - + diff --git a/pyfpdb/HUD_main.pyw b/pyfpdb/HUD_main.pyw index e1704d29..e42f0eb2 100755 --- a/pyfpdb/HUD_main.pyw +++ b/pyfpdb/HUD_main.pyw @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright 2008-2010, Ray E. Barker diff --git a/pyfpdb/HUD_run_me.py b/pyfpdb/HUD_run_me.py index f3d95c01..c9dfabaf 100755 --- a/pyfpdb/HUD_run_me.py +++ b/pyfpdb/HUD_run_me.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- #Copyright 2009-2010 Eric Blade diff --git a/pyfpdb/Hand.py b/pyfpdb/Hand.py index a2cd8b0e..49553f85 100644 --- a/pyfpdb/Hand.py +++ b/pyfpdb/Hand.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2008-2010 Carl Gherardi @@ -37,7 +37,6 @@ import Configuration from Exceptions import * import DerivedStats import Card -import Tourney class Hand(object): @@ -230,9 +229,9 @@ dealt whether they were seen in a 'dealt to' line if self.tourNo!=None: self.tourneyTypeId = db.createOrUpdateTourneyType(self) db.commit() - self.tourneyId = db.createOrUpdateTourney(self) + self.tourneyId = db.createOrUpdateTourney(self, "HHC") db.commit() - self.tourneysPlayersIds = db.createOrUpdateTourneysPlayers(self) + self.tourneysPlayersIds = db.createOrUpdateTourneysPlayers(self, "HHC") db.commit() #end def prepInsert @@ -1581,7 +1580,7 @@ limit 1""", {'handid':handid}) SELECT h.sitehandno as hid, h.tablename as table, - h.handstart as startTime + h.startTime as startTime FROM hands as h WHERE h.id = %(handid)s diff --git a/pyfpdb/HandHistory.py b/pyfpdb/HandHistory.py index 64d22706..bc6c118a 100644 --- a/pyfpdb/HandHistory.py +++ b/pyfpdb/HandHistory.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- """HandHistory.py diff --git a/pyfpdb/HandHistoryConverter.py b/pyfpdb/HandHistoryConverter.py index 7d04e824..4554c2e8 100644 --- a/pyfpdb/HandHistoryConverter.py +++ b/pyfpdb/HandHistoryConverter.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2008-2010 Carl Gherardi @@ -35,7 +35,6 @@ log = logging.getLogger("parser") import Hand -import Tourney from Exceptions import FpdbParseError import Configuration @@ -495,6 +494,23 @@ or None if we fail to get the info """ def getTourney(self): return self.tourney + + @staticmethod + def changeTimezone(time, givenTimezone, wantedTimezone): + if givenTimezone=="ET" and wantedTimezone=="UTC": + # approximate rules for ET daylight savings time: + if ( time.month == 12 # all of Dec + or (time.month == 11 and time.day > 4) # and most of November + or time.month < 3 # and all of Jan/Feb + or (time.month == 3 and time.day < 11) ): # and 1st 10 days of March + offset = datetime.timedelta(hours=5) # are EST: assume 5 hour offset (ET without daylight saving) + else: + offset = datetime.timedelta(hours=4) # rest is EDT: assume 4 hour offset (ET with daylight saving) + # adjust time into UTC: + time = time + offset + #print " tz = %s start = %s" % (tz, str(hand.starttime)) + return time + #end @staticmethod def changeTimezone @staticmethod def getTableTitleRe(type, table_name=None, tournament = None, table_number=None): diff --git a/pyfpdb/Hello.py b/pyfpdb/Hello.py index 24d45a0b..5312bd3f 100644 --- a/pyfpdb/Hello.py +++ b/pyfpdb/Hello.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- """Hello.py diff --git a/pyfpdb/Hud.py b/pyfpdb/Hud.py index 5b55b283..8d3d7d0c 100644 --- a/pyfpdb/Hud.py +++ b/pyfpdb/Hud.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- """Hud.py diff --git a/pyfpdb/ImapSummaries.py b/pyfpdb/ImapSummaries.py index 50079488..f2874725 100755 --- a/pyfpdb/ImapSummaries.py +++ b/pyfpdb/ImapSummaries.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2008-2010 Steffen Schaumburg @@ -19,8 +19,7 @@ #see http://docs.python.org/library/imaplib.html for the python interface #see http://tools.ietf.org/html/rfc2060#section-6.4.4 for IMAP4 search criteria -import sys -from imaplib import IMAP4_SSL +from imaplib import IMAP4, IMAP4_SSL import PokerStarsSummary def splitPokerStarsSummaries(emailText): @@ -30,20 +29,20 @@ def splitPokerStarsSummaries(emailText): return splitSummaries #end def emailText -if __name__ == '__main__': - #TODO: move all these into the config file. until then usage is: ./ImapSummaries.py YourImapHost YourImapUser YourImapPw - configHost=sys.argv[1] - configUser=sys.argv[2] - configPw=sys.argv[3] - #TODO: specify folder, whether to use SSL - - try: - server = IMAP4_SSL(configHost) #TODO: optionally non-SSL - response = server.login(configUser, configPw) #TODO catch authentication error - #print "response to logging in:",response +def run(config, db): + #print "start of IS.run" + server=None + #try: + #print "useSSL",config.email.useSsl,"host",config.email.host + if config.email.useSsl: + server = IMAP4_SSL(config.email.host) + else: + server = IMAP4(config.email.host) + response = server.login(config.email.username, config.email.password) #TODO catch authentication error + print "response to logging in:",response #print "server.list():",server.list() #prints list of folders - response = server.select("INBOX") + response = server.select(config.email.folder) #print "response to selecting INBOX:",response if response[0]!="OK": raise error #TODO: show error message @@ -68,15 +67,15 @@ if __name__ == '__main__': if messageData[0]=="PS": summaryTexts=(splitPokerStarsSummaries(bodyData)) for summaryText in summaryTexts: - result=PokerStarsSummary.PokerStarsSummary(sitename="PokerStars", gametype=None, summaryText=summaryText, builtFrom = "IMAP") - #print "result:",result + result=PokerStarsSummary.PokerStarsSummary(db=db, config=config, siteName=u"PokerStars", summaryText=summaryText, builtFrom = "IMAP") + #print "finished importing a PS summary with result:",result #TODO: count results and output to shell like hand importer does print "completed running Imap import, closing server connection" - finally: - try: - server.close() - finally: - pass + #finally: + # try: + server.close() + # finally: + # pass server.logout() \ No newline at end of file diff --git a/pyfpdb/Mucked.py b/pyfpdb/Mucked.py index 007573b7..525ce206 100755 --- a/pyfpdb/Mucked.py +++ b/pyfpdb/Mucked.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- """Mucked.py diff --git a/pyfpdb/OnGameToFpdb.py b/pyfpdb/OnGameToFpdb.py index 1d6fef1a..80d8d646 100755 --- a/pyfpdb/OnGameToFpdb.py +++ b/pyfpdb/OnGameToFpdb.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright 2008-2010, Carl Gherardi diff --git a/pyfpdb/Options.py b/pyfpdb/Options.py index 85c0d22f..6b5e3285 100644 --- a/pyfpdb/Options.py +++ b/pyfpdb/Options.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2008-2010 Ray E. Barker diff --git a/pyfpdb/PartyPokerToFpdb.py b/pyfpdb/PartyPokerToFpdb.py index 07a43f22..9c665bd3 100755 --- a/pyfpdb/PartyPokerToFpdb.py +++ b/pyfpdb/PartyPokerToFpdb.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- # # Copyright 2009-2010, Grigorij Indigirkin diff --git a/pyfpdb/PokerStarsSummary.py b/pyfpdb/PokerStarsSummary.py index 1f8042af..73844e57 100644 --- a/pyfpdb/PokerStarsSummary.py +++ b/pyfpdb/PokerStarsSummary.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2008-2010 Steffen Schaumburg @@ -18,38 +18,109 @@ """pokerstars-specific summary parsing code""" from decimal import Decimal +import datetime -from PokerStarsToFpdb import PokerStars +from Exceptions import FpdbParseError +from HandHistoryConverter import * +import PokerStarsToFpdb from TourneySummary import * class PokerStarsSummary(TourneySummary): - sitename = "PokerStars" - siteId = 2 - #limits = PokerStars.limits - #games = PokerStars.games - # = PokerStars. + limits = { 'No Limit':'nl', 'Pot Limit':'pl', 'Limit':'fl', 'LIMIT':'fl' } + games = { # base, category + "Hold'em" : ('hold','holdem'), + 'Omaha' : ('hold','omahahi'), + 'Omaha Hi/Lo' : ('hold','omahahilo'), + 'Razz' : ('stud','razz'), + 'RAZZ' : ('stud','razz'), + '7 Card Stud' : ('stud','studhi'), + '7 Card Stud Hi/Lo' : ('stud','studhilo'), + 'Badugi' : ('draw','badugi'), + 'Triple Draw 2-7 Lowball' : ('draw','27_3draw'), + '5 Card Draw' : ('draw','fivedraw') + } re_TourNo = re.compile("\#[0-9]+,") re_Entries = re.compile("[0-9]+") re_Prizepool = re.compile("\$[0-9]+\.[0-9]+") - re_Player = re.compile("""(?P[0-9]+):\s(?P.*)\s\(.*\),(\s\$(?P[0-9]+\.[0-9]+)\s\()?""") - # = re.compile("") + re_Player = re.compile(u"""(?P[0-9]+):\s(?P.*)\s\(.*\),(\s)?(\$(?P[0-9]+\.[0-9]+))?(?Pstill\splaying)?""") + re_BuyInFee = re.compile("(?P[0-9]+\.[0-9]+).*(?P[0-9]+\.[0-9]+)") + re_FPP = re.compile("(?P[0-9]+)\sFPP") + #note: the dollar and cent in the below line are currency-agnostic + re_Added = re.compile("(?P[0-9]+)\.(?P[0-9]+)\s(?P[A-Z]+)(\sadded\sto\sthe\sprize\spool\sby\sPokerStars)") + re_DateTime = re.compile("(?P[0-9]{4})\/(?P[0-9]{2})\/(?P[0-9]{2})[\- ]+(?P[0-9]+):(?P[0-9]+):(?P[0-9]+)") + re_GameInfo = re.compile(u""".+(?PNo\sLimit|Limit|LIMIT|Pot\sLimit)\s(?PHold\'em|Razz|RAZZ|7\sCard\sStud|7\sCard\sStud\sHi/Lo|Omaha|Omaha\sHi/Lo|Badugi|Triple\sDraw\s2\-7\sLowball|5\sCard\sDraw)""") def parseSummary(self): lines=self.summaryText.splitlines() self.tourNo = self.re_TourNo.findall(lines[0])[0][1:-1] #ignore game and limit type as thats not recorded - #ignore lines[1] as buyin/fee are already recorded by HHC + result=self.re_GameInfo.search(lines[0]) + result=result.groupdict() + self.gametype['limitType']=self.limits[result['LIMIT']] + self.gametype['category']=self.games[result['GAME']][0] - self.entries = self.re_Entries.findall(lines[2])[0] + if lines[1].find("$")!=-1: #TODO: move this into a method and call that from PokerStarsToFpdb.py:269 if hand.buyinCurrency=="USD" etc. + self.currency="USD" + elif lines[1].find(u"€")!=-1: + self.currency="EUR" + elif lines[1].find("FPP")!=-1: + self.currency="PSFP" + else: + raise FpdbParseError("didn't recognise buyin currency in:"+lines[1]) - self.prizepool = self.re_Prizepool.findall(lines[3])[0] - self.prizepool = self.prizepool[1:-3]+self.prizepool[-2:] + if self.currency=="USD" or self.currency=="EUR": + result=self.re_BuyInFee.search(lines[1]) + result=result.groupdict() + self.buyin=int(100*Decimal(result['BUYIN'])) + self.fee=int(100*Decimal(result['FEE'])) + elif self.currency=="PSFP": + result=self.re_FPP.search(lines[1]) + result=result.groupdict() + self.buyin=int(Decimal(result['FPP'])) + self.fee=0 - #TODO: lines 4 and 5 are dates, read them + currentLine=2 + self.entries = self.re_Entries.findall(lines[currentLine])[0] + currentLine+=1 #note that I chose to make the code keep state (the current line number) + #as that means it'll fail rather than silently skip potentially valuable information + #print "after entries lines[currentLine]", lines[currentLine] - for i in range(6,len(lines)-2): #lines with rank and winnings info + result=self.re_Added.search(lines[currentLine]) + if result: + result=result.groupdict() + self.added=100*int(Decimal(result['DOLLAR']))+int(Decimal(result['CENT'])) + self.addedCurrency=result['CURRENCY'] + #print "TODO: implement added:",self.added,self.addedCurrency + currentLine+=1 + #print "after added/entries lines[currentLine]", lines[currentLine] + + result=self.re_Prizepool.findall(lines[currentLine]) + if result: + self.prizepool = result[0] + self.prizepool = self.prizepool[1:-3]+self.prizepool[-2:] + currentLine+=1 + #print "after prizepool lines[currentLine]", lines[currentLine] + + result=self.re_DateTime.search(lines[currentLine]) + result=result.groupdict() + datetimestr = "%s/%s/%s %s:%s:%s" % (result['Y'], result['M'],result['D'],result['H'],result['MIN'],result['S']) + self.startTime= datetime.datetime.strptime(datetimestr, "%Y/%m/%d %H:%M:%S") # also timezone at end, e.g. " ET" + self.startTime = HandHistoryConverter.changeTimezone(self.startTime, "ET", "UTC") + currentLine+=1 + + result=self.re_DateTime.search(lines[currentLine]) + if result: + result=result.groupdict() + datetimestr = "%s/%s/%s %s:%s:%s" % (result['Y'], result['M'],result['D'],result['H'],result['MIN'],result['S']) + self.endTime= datetime.datetime.strptime(datetimestr, "%Y/%m/%d %H:%M:%S") # also timezone at end, e.g. " ET" + currentLine+=1 + + if lines[currentLine].find("Tournament is still in progress")!=-1: + currentLine+=1 + + for i in range(currentLine,len(lines)-2): #lines with rank and winnings info if lines[i].find(":")==-1: break result=self.re_Player.search(lines[i]) @@ -57,11 +128,17 @@ class PokerStarsSummary(TourneySummary): rank=result['RANK'] name=result['NAME'] winnings=result['WINNINGS'] + if winnings: winnings=int(100*Decimal(winnings)) else: winnings=0 - self.addPlayer(rank, name, winnings, "USD", None, None, None)#TODO: currency, ko/addon/rebuy count -> need examples! + if result['STILLPLAYING']: + #print "stillplaying" + rank=None + winnings=None + + self.addPlayer(rank, name, winnings, self.currency, None, None, None)#TODO: currency, ko/addon/rebuy count -> need examples! #end def parseSummary #end class PokerStarsSummary diff --git a/pyfpdb/PokerStarsToFpdb.py b/pyfpdb/PokerStarsToFpdb.py index 762eeb28..0a9e47fc 100644 --- a/pyfpdb/PokerStarsToFpdb.py +++ b/pyfpdb/PokerStarsToFpdb.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- # # Copyright 2008-2010, Carl Gherardi @@ -77,7 +77,9 @@ class PokerStars(HandHistoryConverter): (?PHORSE|8\-Game|HOSE)?\s?\(? (?PHold\'em|Razz|RAZZ|7\sCard\sStud|7\sCard\sStud\sHi/Lo|Omaha|Omaha\sHi/Lo|Badugi|Triple\sDraw\s2\-7\sLowball|5\sCard\sDraw)\s (?PNo\sLimit|Limit|LIMIT|Pot\sLimit)\)?,?\s - (-\sLevel\s(?P[IVXLC]+)\s)? + (-\s)? + (Match.*)? #TODO: waiting for reply from user as to what this means + (Level\s(?P[IVXLC]+)\s)? \(? # open paren of the stakes (?P%(LS)s|)? (?P[.0-9]+)/(%(LS)s)? @@ -221,23 +223,13 @@ class PokerStars(HandHistoryConverter): #2008/09/07 06:23:14 ET m1 = self.re_DateTime.finditer(info[key]) # m2 = re.search("(?P[0-9]{4})\/(?P[0-9]{2})\/(?P[0-9]{2})[\- ]+(?P[0-9]+):(?P[0-9]+):(?P[0-9]+)", info[key]) - datetimestr = "2000/01/01 00:00:00" # default used if time not found (stops import crashing, but handstart will be wrong) + datetimestr = "2000/01/01 00:00:00" # default used if time not found (stops import crashing, but startTime will be wrong) for a in m1: datetimestr = "%s/%s/%s %s:%s:%s" % (a.group('Y'), a.group('M'),a.group('D'),a.group('H'),a.group('MIN'),a.group('S')) #tz = a.group('TZ') # just assume ET?? #print " tz = ", tz, " datetime =", datetimestr hand.startTime = datetime.datetime.strptime(datetimestr, "%Y/%m/%d %H:%M:%S") # also timezone at end, e.g. " ET" - # approximate rules for ET daylight savings time: - if ( hand.startTime.month == 12 # all of Dec - or (hand.startTime.month == 11 and hand.startTime.day > 4) # and most of November - or hand.startTime.month < 3 # and all of Jan/Feb - or (hand.startTime.month == 3 and hand.startTime.day < 11) ): # and 1st 10 days of March - offset = datetime.timedelta(hours=5) # are EST: assume 5 hour offset (ET without daylight saving) - else: - offset = datetime.timedelta(hours=4) # rest is EDT: assume 4 hour offset (ET with daylight saving) - # adjust time into UTC: - hand.startTime = hand.startTime + offset - #print " tz = %s start = %s" % (tz, str(hand.startTime)) + hand.startTime = HandHistoryConverter.changeTimezone(hand.startTime, "ET", "UTC") if key == 'HID': hand.handid = info[key] if key == 'TOURNO': @@ -249,16 +241,24 @@ class PokerStars(HandHistoryConverter): hand.fee = 0 hand.buyinCurrency = "FREE" else: + #print "info[key]:",info[key] if info[key].find("$")!=-1: hand.buyinCurrency="USD" elif info[key].find(u"€")!=-1: hand.buyinCurrency="EUR" + elif info[key].find("FPP")!=-1: + hand.buyinCurrency="PSFP" else: - hand.buyinCurrency="NA" #FIXME: handle other currencies, FPP, play money - info[key]=info[key][:-4] - middle=info[key].find("+") - hand.buyin = int(100*Decimal(info[key][1:middle])) - hand.fee = int(100*Decimal(info[key][middle+2:])) + raise FpdbParseError("failed to detect currency") #FIXME: handle other currencies, FPP, play money + + if hand.buyinCurrency=="USD" or hand.buyinCurrency=="EUR": + info[key]=info[key][:-4] + middle=info[key].find("+") + hand.buyin = int(100*Decimal(info[key][1:middle])) + hand.fee = int(100*Decimal(info[key][middle+2:])) + elif hand.buyinCurrency=="PSFP": + hand.buyin = int(Decimal(info[key][0:-3])) + hand.fee = 0 if key == 'LEVEL': hand.level = info[key] @@ -277,7 +277,7 @@ class PokerStars(HandHistoryConverter): if key == 'PLAY' and info['PLAY'] is not None: # hand.currency = 'play' # overrides previously set value hand.gametype['currency'] = 'play' - + def readButton(self, hand): m = self.re_Button.search(hand.handText) if m: diff --git a/pyfpdb/RingFilters.py b/pyfpdb/RingFilters.py new file mode 100644 index 00000000..db7ed4de --- /dev/null +++ b/pyfpdb/RingFilters.py @@ -0,0 +1,948 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +#Copyright 2008-2010 Steffen Schaumburg +#This program is free software: you can redistribute it and/or modify +#it under the terms of the GNU Affero General Public License as published by +#the Free Software Foundation, version 3 of the License. +# +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +# +#You should have received a copy of the GNU Affero General Public License +#along with this program. If not, see . +#In the "official" distribution you can find the license in agpl-3.0.txt. + +import threading +import pygtk +pygtk.require('2.0') +import gtk +import os +import sys +from optparse import OptionParser +from time import gmtime, mktime, strftime, strptime +import gobject +#import pokereval + +import logging +# logging has been set up in fpdb.py or HUD_main.py, use their settings: +log = logging.getLogger("filter") + + +import Configuration +import Database +import SQL +import Charset +import Filters + +class RingFilters(Filters.Filters): + def __init__(self, db, config, qdict, display = {}, debug=True): + # config and qdict are now redundant + self.debug = debug + self.db = db + self.cursor = db.cursor + self.sql = db.sql + self.conf = db.config + self.display = display + + # text used on screen stored here so that it can be configured + self.filterText = {'limitsall':'All', 'limitsnone':'None', 'limitsshow':'Show _Limits' + ,'seatsbetween':'Between:', 'seatsand':'And:', 'seatsshow':'Show Number of _Players' + ,'playerstitle':'Hero:', 'sitestitle':'Sites:', 'gamestitle':'Games:' + ,'limitstitle':'Limits:', 'seatstitle':'Number of Players:' + ,'groupstitle':'Grouping:', 'posnshow':'Show Position Stats:' + ,'datestitle':'Date:' + ,'groupsall':'All Players' + ,'limitsFL':'FL', 'limitsNL':'NL', 'limitsPL':'PL', 'ring':'Ring', 'tour':'Tourney' + } + + gen = self.conf.get_general_params() + self.day_start = 0 + if 'day_start' in gen: + self.day_start = float(gen['day_start']) + + # Outer Packing box + self.mainVBox = gtk.VBox(False, 0) + + self.label = {} + self.callback = {} + + self.make_filter() + + def make_filter(self): + self.sites = {} + self.games = {} + self.limits = {} + self.seats = {} + self.groups = {} + self.siteid = {} + self.heroes = {} + self.boxes = {} + + for site in self.conf.get_supported_sites(): + #Get db site id for filtering later + self.cursor.execute(self.sql.query['getSiteId'], (site,)) + result = self.db.cursor.fetchall() + if len(result) == 1: + self.siteid[site] = result[0][0] + else: + print "Either 0 or more than one site matched (%s) - EEK" % site + + # For use in date ranges. + self.start_date = gtk.Entry(max=12) + self.end_date = gtk.Entry(max=12) + self.start_date.set_property('editable', False) + self.end_date.set_property('editable', False) + + # For use in groups etc + self.sbGroups = {} + self.numHands = 0 + + playerFrame = gtk.Frame() + playerFrame.set_label_align(0.0, 0.0) + vbox = gtk.VBox(False, 0) + + self.fillPlayerFrame(vbox, self.display) + playerFrame.add(vbox) + + sitesFrame = gtk.Frame() + sitesFrame.set_label_align(0.0, 0.0) + vbox = gtk.VBox(False, 0) + + self.fillSitesFrame(vbox) + sitesFrame.add(vbox) + + # Game types + gamesFrame = gtk.Frame() + gamesFrame.set_label_align(0.0, 0.0) + gamesFrame.show() + vbox = gtk.VBox(False, 0) + + self.fillGamesFrame(vbox) + gamesFrame.add(vbox) + + # Limits + limitsFrame = gtk.Frame() + limitsFrame.show() + vbox = gtk.VBox(False, 0) + self.cbLimits = {} + self.cbNoLimits = None + self.cbAllLimits = None + self.cbFL = None + self.cbNL = None + self.cbPL = None + self.rb = {} # radio buttons for ring/tour + self.type = None # ring/tour + self.types = {} # list of all ring/tour values + + self.fillLimitsFrame(vbox, self.display) + limitsFrame.add(vbox) + + # Seats + seatsFrame = gtk.Frame() + seatsFrame.show() + vbox = gtk.VBox(False, 0) + self.sbSeats = {} + + self.fillSeatsFrame(vbox, self.display) + seatsFrame.add(vbox) + + # Groups + groupsFrame = gtk.Frame() + groupsFrame.show() + vbox = gtk.VBox(False, 0) + + self.fillGroupsFrame(vbox, self.display) + groupsFrame.add(vbox) + + # Date + dateFrame = gtk.Frame() + dateFrame.set_label_align(0.0, 0.0) + dateFrame.show() + vbox = gtk.VBox(False, 0) + + self.fillDateFrame(vbox) + dateFrame.add(vbox) + + # Buttons + self.Button1=gtk.Button("Unnamed 1") + self.Button1.set_sensitive(False) + + self.Button2=gtk.Button("Unnamed 2") + self.Button2.set_sensitive(False) + + self.mainVBox.add(playerFrame) + self.mainVBox.add(sitesFrame) + self.mainVBox.add(gamesFrame) + self.mainVBox.add(limitsFrame) + self.mainVBox.add(seatsFrame) + self.mainVBox.add(groupsFrame) + self.mainVBox.add(dateFrame) + self.mainVBox.add(self.Button1) + self.mainVBox.add(self.Button2) + + self.mainVBox.show_all() + + # Should do this cleaner + if "Heroes" not in self.display or self.display["Heroes"] == False: + playerFrame.hide() + if "Sites" not in self.display or self.display["Sites"] == False: + sitesFrame.hide() + if "Games" not in self.display or self.display["Games"] == False: + gamesFrame.hide() + if "Limits" not in self.display or self.display["Limits"] == False: + limitsFrame.hide() + if "Seats" not in self.display or self.display["Seats"] == False: + seatsFrame.hide() + if "Groups" not in self.display or self.display["Groups"] == False: + groupsFrame.hide() + if "Dates" not in self.display or self.display["Dates"] == False: + dateFrame.hide() + if "Button1" not in self.display or self.display["Button1"] == False: + self.Button1.hide() + if "Button2" not in self.display or self.display["Button2"] == False: + self.Button2.hide() + + if 'button1' in self.label and self.label['button1']: + self.Button1.set_label( self.label['button1'] ) + if 'button2' in self.label and self.label['button2']: + self.Button2.set_label( self.label['button2'] ) + if 'button1' in self.callback and self.callback['button1']: + self.Button1.connect("clicked", self.callback['button1'], "clicked") + self.Button1.set_sensitive(True) + if 'button2' in self.callback and self.callback['button2']: + self.Button2.connect("clicked", self.callback['button2'], "clicked") + self.Button2.set_sensitive(True) + + # make sure any locks on db are released: + self.db.rollback() + + def get_vbox(self): + """returns the vbox of this thread""" + return self.mainVBox + #end def get_vbox + + def getNumHands(self): + return self.numHands + #end def getNumHands + + def getSites(self): + return self.sites + #end def getSites + + def getGames(self): + return self.games + + def getSiteIds(self): + return self.siteid + #end def getSiteIds + + def getHeroes(self): + return self.heroes + #end def getHeroes + + def getLimits(self): + ltuple = [] + for l in self.limits: + if self.limits[l] == True: + ltuple.append(l) + return ltuple + + def getType(self): + return(self.type) + + def getSeats(self): + if 'from' in self.sbSeats: + self.seats['from'] = self.sbSeats['from'].get_value_as_int() + if 'to' in self.sbSeats: + self.seats['to'] = self.sbSeats['to'].get_value_as_int() + return self.seats + #end def getSeats + + def getGroups(self): + return self.groups + + def getDates(self): + return self.__get_dates() + #end def getDates + + def registerButton1Name(self, title): + self.Button1.set_label(title) + self.label['button1'] = title + + def registerButton1Callback(self, callback): + self.Button1.connect("clicked", callback, "clicked") + self.Button1.set_sensitive(True) + self.callback['button1'] = callback + + def registerButton2Name(self, title): + self.Button2.set_label(title) + self.label['button2'] = title + #end def registerButton2Name + + def registerButton2Callback(self, callback): + self.Button2.connect("clicked", callback, "clicked") + self.Button2.set_sensitive(True) + self.callback['button2'] = callback + #end def registerButton2Callback + + def cardCallback(self, widget, data=None): + log.debug( "%s was toggled %s" % (data, ("OFF", "ON")[widget.get_active()]) ) + + def createPlayerLine(self, hbox, site, player): + log.debug('add:"%s"' % player) + label = gtk.Label(site +" id:") + hbox.pack_start(label, False, False, 3) + + pname = gtk.Entry() + pname.set_text(player) + pname.set_width_chars(20) + hbox.pack_start(pname, False, True, 0) + pname.connect("changed", self.__set_hero_name, site) + + # Added EntryCompletion but maybe comboBoxEntry is more flexible? (e.g. multiple choices) + completion = gtk.EntryCompletion() + pname.set_completion(completion) + liststore = gtk.ListStore(gobject.TYPE_STRING) + completion.set_model(liststore) + completion.set_text_column(0) + names = self.db.get_player_names(self.conf, self.siteid[site]) # (config=self.conf, site_id=None, like_player_name="%") + for n in names: # list of single-element "tuples" + _n = Charset.to_gui(n[0]) + _nt = (_n, ) + liststore.append(_nt) + + self.__set_hero_name(pname, site) + + def __set_hero_name(self, w, site): + _name = w.get_text() + # get_text() returns a str but we want internal variables to be unicode: + _guiname = unicode(_name) + self.heroes[site] = _guiname + #log.debug("setting heroes[%s]: %s"%(site, self.heroes[site])) + #end def __set_hero_name + + def __set_num_hands(self, w, val): + try: + self.numHands = int(w.get_text()) + except: + self.numHands = 0 + #log.debug("setting numHands:", self.numHands) + #end def __set_num_hands + + def createSiteLine(self, hbox, site): + cb = gtk.CheckButton(site) + cb.connect('clicked', self.__set_site_select, site) + cb.set_active(True) + hbox.pack_start(cb, False, False, 0) + + def createGameLine(self, hbox, game): + cb = gtk.CheckButton(game) + cb.connect('clicked', self.__set_game_select, game) + hbox.pack_start(cb, False, False, 0) + cb.set_active(True) + + def createLimitLine(self, hbox, limit, ltext): + cb = gtk.CheckButton(str(ltext)) + cb.connect('clicked', self.__set_limit_select, limit) + hbox.pack_start(cb, False, False, 0) + if limit != "none": + cb.set_active(True) + return(cb) + + def __set_site_select(self, w, site): + #print w.get_active() + self.sites[site] = w.get_active() + log.debug("self.sites[%s] set to %s" %(site, self.sites[site])) + + def __set_game_select(self, w, game): + #print w.get_active() + self.games[game] = w.get_active() + log.debug("self.games[%s] set to %s" %(game, self.games[game])) + #end def __set_game_select + + def __set_limit_select(self, w, limit): + #print w.get_active() + self.limits[limit] = w.get_active() + log.debug("self.limit[%s] set to %s" %(limit, self.limits[limit])) + if limit.isdigit() or (len(limit) > 2 and (limit[-2:] == 'nl' or limit[-2:] == 'fl' or limit[-2:] == 'pl')): + if self.limits[limit]: + if self.cbNoLimits is not None: + self.cbNoLimits.set_active(False) + else: + if self.cbAllLimits is not None: + self.cbAllLimits.set_active(False) + if not self.limits[limit]: + if limit.isdigit(): + if self.cbFL is not None: + self.cbFL.set_active(False) + elif (len(limit) > 2 and (limit[-2:] == 'nl')): + if self.cbNL is not None: + self.cbNL.set_active(False) + else: + if self.cbPL is not None: + self.cbPL.set_active(False) + elif limit == "all": + if self.limits[limit]: + #for cb in self.cbLimits.values(): + # cb.set_active(True) + if self.cbFL is not None: + self.cbFL.set_active(True) + if self.cbNL is not None: + self.cbNL.set_active(True) + if self.cbPL is not None: + self.cbPL.set_active(True) + elif limit == "none": + if self.limits[limit]: + for cb in self.cbLimits.values(): + cb.set_active(False) + if self.cbNL is not None: + self.cbNL.set_active(False) + if self.cbFL is not None: + self.cbFL.set_active(False) + if self.cbPL is not None: + self.cbPL.set_active(False) + elif limit == "fl": + if not self.limits[limit]: + # only toggle all fl limits off if they are all currently on + # this stops turning one off from cascading into 'fl' box off + # and then all fl limits being turned off + all_fl_on = True + for cb in self.cbLimits.values(): + t = cb.get_children()[0].get_text() + if t.isdigit(): + if not cb.get_active(): + all_fl_on = False + found = {'ring':False, 'tour':False} + for cb in self.cbLimits.values(): + #print "cb label: ", cb.children()[0].get_text() + t = cb.get_children()[0].get_text() + if t.isdigit(): + if self.limits[limit] or all_fl_on: + cb.set_active(self.limits[limit]) + found[self.types[t]] = True + if self.limits[limit]: + if not found[self.type]: + if self.type == 'ring': + if 'tour' in self.rb: + self.rb['tour'].set_active(True) + elif self.type == 'tour': + if 'ring' in self.rb: + self.rb['ring'].set_active(True) + elif limit == "nl": + if not self.limits[limit]: + # only toggle all nl limits off if they are all currently on + # this stops turning one off from cascading into 'nl' box off + # and then all nl limits being turned off + all_nl_on = True + for cb in self.cbLimits.values(): + t = cb.get_children()[0].get_text() + if "nl" in t and len(t) > 2: + if not cb.get_active(): + all_nl_on = False + found = {'ring':False, 'tour':False} + for cb in self.cbLimits.values(): + t = cb.get_children()[0].get_text() + if "nl" in t and len(t) > 2: + if self.limits[limit] or all_nl_on: + cb.set_active(self.limits[limit]) + found[self.types[t]] = True + if self.limits[limit]: + if not found[self.type]: + if self.type == 'ring': + if 'tour' in self.rb: + self.rb['tour'].set_active(True) + elif self.type == 'tour': + if 'ring' in self.rb: + self.rb['ring'].set_active(True) + elif limit == "pl": + if not self.limits[limit]: + # only toggle all nl limits off if they are all currently on + # this stops turning one off from cascading into 'nl' box off + # and then all nl limits being turned off + all_nl_on = True + for cb in self.cbLimits.values(): + t = cb.get_children()[0].get_text() + if "pl" in t and len(t) > 2: + if not cb.get_active(): + all_nl_on = False + found = {'ring':False, 'tour':False} + for cb in self.cbLimits.values(): + t = cb.get_children()[0].get_text() + if "pl" in t and len(t) > 2: + if self.limits[limit] or all_nl_on: + cb.set_active(self.limits[limit]) + found[self.types[t]] = True + if self.limits[limit]: + if not found[self.type]: + if self.type == 'ring': + if 'tour' in self.rb: + self.rb['tour'].set_active(True) + elif self.type == 'tour': + if 'ring' in self.rb: + self.rb['ring'].set_active(True) + elif limit == "ring": + log.debug("set", limit, "to", self.limits[limit]) + if self.limits[limit]: + self.type = "ring" + for cb in self.cbLimits.values(): + #print "cb label: ", cb.children()[0].get_text() + if self.types[cb.get_children()[0].get_text()] == 'tour': + cb.set_active(False) + elif limit == "tour": + log.debug( "set", limit, "to", self.limits[limit] ) + if self.limits[limit]: + self.type = "tour" + for cb in self.cbLimits.values(): + #print "cb label: ", cb.children()[0].get_text() + if self.types[cb.get_children()[0].get_text()] == 'ring': + cb.set_active(False) + + def __set_seat_select(self, w, seat): + #print "__set_seat_select: seat =", seat, "active =", w.get_active() + self.seats[seat] = w.get_active() + log.debug( "self.seats[%s] set to %s" %(seat, self.seats[seat]) ) + + def __set_group_select(self, w, group): + #print "__set_seat_select: seat =", seat, "active =", w.get_active() + self.groups[group] = w.get_active() + log.debug( "self.groups[%s] set to %s" %(group, self.groups[group]) ) + + def fillPlayerFrame(self, vbox, display): + top_hbox = gtk.HBox(False, 0) + vbox.pack_start(top_hbox, False, False, 0) + lbl_title = gtk.Label(self.filterText['playerstitle']) + lbl_title.set_alignment(xalign=0.0, yalign=0.5) + top_hbox.pack_start(lbl_title, expand=True, padding=3) + showb = gtk.Button(label="refresh", stock=None, use_underline=True) + showb.set_alignment(xalign=1.0, yalign=0.5) + showb.connect('clicked', self.__refresh, 'players') + + vbox1 = gtk.VBox(False, 0) + vbox.pack_start(vbox1, False, False, 0) + self.boxes['players'] = vbox1 + + for site in self.conf.get_supported_sites(): + hBox = gtk.HBox(False, 0) + vbox1.pack_start(hBox, False, True, 0) + + player = self.conf.supported_sites[site].screen_name + _pname = Charset.to_gui(player) + self.createPlayerLine(hBox, site, _pname) + + if "GroupsAll" in display and display["GroupsAll"] == True: + hbox = gtk.HBox(False, 0) + vbox1.pack_start(hbox, False, False, 0) + cb = gtk.CheckButton(self.filterText['groupsall']) + cb.connect('clicked', self.__set_group_select, 'allplayers') + hbox.pack_start(cb, False, False, 0) + self.sbGroups['allplayers'] = cb + self.groups['allplayers'] = False + + lbl = gtk.Label('Min # Hands:') + lbl.set_alignment(xalign=1.0, yalign=0.5) + hbox.pack_start(lbl, expand=True, padding=3) + + phands = gtk.Entry() + phands.set_text('0') + phands.set_width_chars(8) + hbox.pack_start(phands, False, False, 0) + phands.connect("changed", self.__set_num_hands, site) + top_hbox.pack_start(showb, expand=False, padding=1) + + def fillSitesFrame(self, vbox): + top_hbox = gtk.HBox(False, 0) + top_hbox.show() + vbox.pack_start(top_hbox, False, False, 0) + + lbl_title = gtk.Label(self.filterText['sitestitle']) + lbl_title.set_alignment(xalign=0.0, yalign=0.5) + top_hbox.pack_start(lbl_title, expand=True, padding=3) + + showb = gtk.Button(label="hide", stock=None, use_underline=True) + showb.set_alignment(xalign=1.0, yalign=0.5) + showb.connect('clicked', self.__toggle_box, 'sites') + showb.show() + top_hbox.pack_start(showb, expand=False, padding=1) + + vbox1 = gtk.VBox(False, 0) + self.boxes['sites'] = vbox1 + vbox.pack_start(vbox1, False, False, 0) + + for site in self.conf.get_supported_sites(): + hbox = gtk.HBox(False, 0) + vbox1.pack_start(hbox, False, True, 0) + self.createSiteLine(hbox, site) + #Get db site id for filtering later + #self.cursor.execute(self.sql.query['getSiteId'], (site,)) + #result = self.db.cursor.fetchall() + #if len(result) == 1: + # self.siteid[site] = result[0][0] + #else: + # print "Either 0 or more than one site matched - EEK" + + def fillGamesFrame(self, vbox): + top_hbox = gtk.HBox(False, 0) + vbox.pack_start(top_hbox, False, False, 0) + lbl_title = gtk.Label(self.filterText['gamestitle']) + lbl_title.set_alignment(xalign=0.0, yalign=0.5) + top_hbox.pack_start(lbl_title, expand=True, padding=3) + showb = gtk.Button(label="hide", stock=None, use_underline=True) + showb.set_alignment(xalign=1.0, yalign=0.5) + showb.connect('clicked', self.__toggle_box, 'games') + top_hbox.pack_start(showb, expand=False, padding=1) + + vbox1 = gtk.VBox(False, 0) + vbox.pack_start(vbox1, False, False, 0) + self.boxes['games'] = vbox1 + + self.cursor.execute(self.sql.query['getGames']) + result = self.db.cursor.fetchall() + if len(result) >= 1: + for line in result: + hbox = gtk.HBox(False, 0) + vbox1.pack_start(hbox, False, True, 0) + self.createGameLine(hbox, line[0]) + else: + print "INFO: No games returned from database" + log.info("No games returned from database") + #end def fillGamesFrame + + def fillLimitsFrame(self, vbox, display): + top_hbox = gtk.HBox(False, 0) + vbox.pack_start(top_hbox, False, False, 0) + lbl_title = gtk.Label(self.filterText['limitstitle']) + lbl_title.set_alignment(xalign=0.0, yalign=0.5) + top_hbox.pack_start(lbl_title, expand=True, padding=3) + showb = gtk.Button(label="hide", stock=None, use_underline=True) + showb.set_alignment(xalign=1.0, yalign=0.5) + showb.connect('clicked', self.__toggle_box, 'limits') + + vbox1 = gtk.VBox(False, 0) + vbox.pack_start(vbox1, False, False, 0) + self.boxes['limits'] = vbox1 + + self.cursor.execute(self.sql.query['getCashLimits']) + # selects limitType, bigBlind + result = self.db.cursor.fetchall() + found = {'nl':False, 'fl':False, 'pl':False, 'ring':False, 'tour':False} + + if len(result) >= 1: + hbox = gtk.HBox(True, 0) + vbox1.pack_start(hbox, False, False, 0) + vbox2 = gtk.VBox(False, 0) + hbox.pack_start(vbox2, False, False, 0) + vbox3 = gtk.VBox(False, 0) + hbox.pack_start(vbox3, False, False, 0) + for i, line in enumerate(result): + if "UseType" in self.display: + if line[0] != self.display["UseType"]: + continue + hbox = gtk.HBox(False, 0) + if i <= len(result)/2: + vbox2.pack_start(hbox, False, False, 0) + else: + vbox3.pack_start(hbox, False, False, 0) + if True: #line[0] == 'ring': + if line[1] == 'fl': + name = str(line[2]) + found['fl'] = True + elif line[1] == 'pl': + name = str(line[2])+line[1] + found['pl'] = True + else: + name = str(line[2])+line[1] + found['nl'] = True + self.cbLimits[name] = self.createLimitLine(hbox, name, name) + self.types[name] = line[0] + found[line[0]] = True # type is ring/tour + self.type = line[0] # if only one type, set it now + if "LimitSep" in display and display["LimitSep"] == True and len(result) >= 2: + hbox = gtk.HBox(True, 0) + vbox1.pack_start(hbox, False, False, 0) + vbox2 = gtk.VBox(False, 0) + hbox.pack_start(vbox2, False, False, 0) + vbox3 = gtk.VBox(False, 0) + hbox.pack_start(vbox3, False, False, 0) + + hbox = gtk.HBox(False, 0) + vbox2.pack_start(hbox, False, False, 0) + self.cbAllLimits = self.createLimitLine(hbox, 'all', self.filterText['limitsall']) + hbox = gtk.HBox(False, 0) + vbox2.pack_start(hbox, False, False, 0) + self.cbNoLimits = self.createLimitLine(hbox, 'none', self.filterText['limitsnone']) + + dest = vbox3 # for ring/tour buttons + if "LimitType" in display and display["LimitType"] == True and found['nl'] and found['fl']: + #if found['fl']: + hbox = gtk.HBox(False, 0) + vbox3.pack_start(hbox, False, False, 0) + self.cbFL = self.createLimitLine(hbox, 'fl', self.filterText['limitsFL']) + #if found['nl']: + hbox = gtk.HBox(False, 0) + vbox3.pack_start(hbox, False, False, 0) + self.cbNL = self.createLimitLine(hbox, 'nl', self.filterText['limitsNL']) + hbox = gtk.HBox(False, 0) + vbox3.pack_start(hbox, False, False, 0) + self.cbPL = self.createLimitLine(hbox, 'pl', self.filterText['limitsPL']) + dest = vbox2 # for ring/tour buttons + else: + print "INFO: No games returned from database" + log.info("No games returned from database") + + if "Type" in display and display["Type"] == True and found['ring'] and found['tour']: + rb1 = gtk.RadioButton(None, self.filterText['ring']) + rb1.connect('clicked', self.__set_limit_select, 'ring') + rb2 = gtk.RadioButton(rb1, self.filterText['tour']) + rb2.connect('clicked', self.__set_limit_select, 'tour') + top_hbox.pack_start(rb1, False, False, 0) # (child, expand, fill, padding) + top_hbox.pack_start(rb2, True, True, 0) # child uses expand space if fill is true + + self.rb['ring'] = rb1 + self.rb['tour'] = rb2 + #print "about to set ring to true" + rb1.set_active(True) + # set_active doesn't seem to call this for some reason so call manually: + self.__set_limit_select(rb1, 'ring') + self.type = 'ring' + top_hbox.pack_start(showb, expand=False, padding=1) + + def fillSeatsFrame(self, vbox, display): + hbox = gtk.HBox(False, 0) + vbox.pack_start(hbox, False, False, 0) + lbl_title = gtk.Label(self.filterText['seatstitle']) + lbl_title.set_alignment(xalign=0.0, yalign=0.5) + hbox.pack_start(lbl_title, expand=True, padding=3) + showb = gtk.Button(label="hide", stock=None, use_underline=True) + showb.set_alignment(xalign=1.0, yalign=0.5) + showb.connect('clicked', self.__toggle_box, 'seats') + hbox.pack_start(showb, expand=False, padding=1) + + vbox1 = gtk.VBox(False, 0) + vbox.pack_start(vbox1, False, False, 0) + self.boxes['seats'] = vbox1 + + hbox = gtk.HBox(False, 0) + vbox1.pack_start(hbox, False, True, 0) + + lbl_from = gtk.Label(self.filterText['seatsbetween']) + lbl_to = gtk.Label(self.filterText['seatsand']) + adj1 = gtk.Adjustment(value=2, lower=2, upper=10, step_incr=1, page_incr=1, page_size=0) + sb1 = gtk.SpinButton(adjustment=adj1, climb_rate=0.0, digits=0) + adj2 = gtk.Adjustment(value=10, lower=2, upper=10, step_incr=1, page_incr=1, page_size=0) + sb2 = gtk.SpinButton(adjustment=adj2, climb_rate=0.0, digits=0) + + hbox.pack_start(lbl_from, expand=False, padding=3) + hbox.pack_start(sb1, False, False, 0) + hbox.pack_start(lbl_to, expand=False, padding=3) + hbox.pack_start(sb2, False, False, 0) + + self.sbSeats['from'] = sb1 + self.sbSeats['to'] = sb2 + #end def fillSeatsFrame + + def fillGroupsFrame(self, vbox, display): + hbox = gtk.HBox(False, 0) + vbox.pack_start(hbox, False, False, 0) + lbl_title = gtk.Label(self.filterText['groupstitle']) + lbl_title.set_alignment(xalign=0.0, yalign=0.5) + hbox.pack_start(lbl_title, expand=True, padding=3) + showb = gtk.Button(label="hide", stock=None, use_underline=True) + showb.set_alignment(xalign=1.0, yalign=0.5) + showb.connect('clicked', self.__toggle_box, 'groups') + hbox.pack_start(showb, expand=False, padding=1) + + vbox1 = gtk.VBox(False, 0) + vbox.pack_start(vbox1, False, False, 0) + self.boxes['groups'] = vbox1 + + hbox = gtk.HBox(False, 0) + vbox1.pack_start(hbox, False, False, 0) + cb = self.createLimitLine(hbox, 'show', self.filterText['limitsshow']) + + hbox = gtk.HBox(False, 0) + vbox1.pack_start(hbox, False, True, 0) + cb = gtk.CheckButton(self.filterText['posnshow']) + cb.connect('clicked', self.__set_group_select, 'posn') + hbox.pack_start(cb, False, False, 0) + self.sbGroups['posn'] = cb + self.groups['posn'] = False + + if "SeatSep" in display and display["SeatSep"] == True: + hbox = gtk.HBox(False, 0) + vbox1.pack_start(hbox, False, True, 0) + cb = gtk.CheckButton(self.filterText['seatsshow']) + cb.connect('clicked', self.__set_seat_select, 'show') + hbox.pack_start(cb, False, False, 0) + self.sbSeats['show'] = cb + self.seats['show'] = False + + def fillCardsFrame(self, vbox): + hbox1 = gtk.HBox(True,0) + hbox1.show() + vbox.pack_start(hbox1, True, True, 0) + + cards = [ "A", "K","Q","J","T","9","8","7","6","5","4","3","2" ] + + for j in range(0, len(cards)): + hbox1 = gtk.HBox(True,0) + hbox1.show() + vbox.pack_start(hbox1, True, True, 0) + for i in range(0, len(cards)): + if i < (j + 1): + suit = "o" + else: + suit = "s" + button = gtk.ToggleButton("%s%s%s" %(cards[i], cards[j], suit)) + button.connect("toggled", self.cardCallback, "%s%s%s" %(cards[i], cards[j], suit)) + hbox1.pack_start(button, True, True, 0) + button.show() + + def fillDateFrame(self, vbox): + # Hat tip to Mika Bostrom - calendar code comes from PokerStats + top_hbox = gtk.HBox(False, 0) + vbox.pack_start(top_hbox, False, False, 0) + lbl_title = gtk.Label(self.filterText['datestitle']) + lbl_title.set_alignment(xalign=0.0, yalign=0.5) + top_hbox.pack_start(lbl_title, expand=True, padding=3) + showb = gtk.Button(label="hide", stock=None, use_underline=True) + showb.set_alignment(xalign=1.0, yalign=0.5) + showb.connect('clicked', self.__toggle_box, 'dates') + top_hbox.pack_start(showb, expand=False, padding=1) + + vbox1 = gtk.VBox(False, 0) + vbox.pack_start(vbox1, False, False, 0) + self.boxes['dates'] = vbox1 + + hbox = gtk.HBox() + vbox1.pack_start(hbox, False, True, 0) + + lbl_start = gtk.Label('From:') + + btn_start = gtk.Button() + btn_start.set_image(gtk.image_new_from_stock(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)) + btn_start.connect('clicked', self.__calendar_dialog, self.start_date) + + hbox.pack_start(lbl_start, expand=False, padding=3) + hbox.pack_start(btn_start, expand=False, padding=3) + hbox.pack_start(self.start_date, expand=False, padding=2) + + #New row for end date + hbox = gtk.HBox() + vbox1.pack_start(hbox, False, True, 0) + + lbl_end = gtk.Label(' To:') + btn_end = gtk.Button() + btn_end.set_image(gtk.image_new_from_stock(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)) + btn_end.connect('clicked', self.__calendar_dialog, self.end_date) + + btn_clear = gtk.Button(label=' Clear Dates ') + btn_clear.connect('clicked', self.__clear_dates) + + hbox.pack_start(lbl_end, expand=False, padding=3) + hbox.pack_start(btn_end, expand=False, padding=3) + hbox.pack_start(self.end_date, expand=False, padding=2) + + hbox.pack_start(btn_clear, expand=False, padding=15) + #end def fillDateFrame + + def __refresh(self, widget, entry): + for w in self.mainVBox.get_children(): + w.destroy() + self.make_filter() + + def __toggle_box(self, widget, entry): + if self.boxes[entry].props.visible: + self.boxes[entry].hide() + widget.set_label("show") + else: + self.boxes[entry].show() + widget.set_label("hide") + #end def __toggle_box + + def __calendar_dialog(self, widget, entry): + 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() + #end def __calendar_dialog + + def __clear_dates(self, w): + self.start_date.set_text('') + self.end_date.set_text('') + #end def __clear_dates + + def __get_dates(self): + # self.day_start gives user's start of day in hours + offset = int(self.day_start * 3600) # calc day_start in seconds + + t1 = self.start_date.get_text() + t2 = self.end_date.get_text() + + if t1 == '': + t1 = '1970-01-02' + if t2 == '': + t2 = '2020-12-12' + + s1 = strptime(t1, "%Y-%m-%d") # make time_struct + s2 = strptime(t2, "%Y-%m-%d") + e1 = mktime(s1) + offset # s1 is localtime, but returned time since epoch is UTC, then add the + e2 = mktime(s2) + offset # s2 is localtime, but returned time since epoch is UTC + e2 = e2 + 24 * 3600 - 1 # date test is inclusive, so add 23h 59m 59s to e2 + + adj_t1 = strftime("%Y-%m-%d %H:%M:%S", gmtime(e1)) # make adjusted string including time + adj_t2 = strftime("%Y-%m-%d %H:%M:%S", gmtime(e2)) + log.info("t1="+t1+" adj_t1="+adj_t1+'.') + + return (adj_t1, adj_t2) + #end def __get_dates + + 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() + +def main(argv=None): + """main can also be called in the python interpreter, by supplying the command line as the argument.""" + if argv is None: + argv = sys.argv[1:] + + def destroy(*args): # call back for terminating the main eventloop + gtk.main_quit() + + parser = OptionParser() + (options, argv) = parser.parse_args(args = argv) + + config = Configuration.Config() + db = None + + db = Database.Database() + db.do_connect(config) + + qdict = SQL.SQL(db.get_backend_name()) + + i = Filters(db, config, qdict) + main_window = gtk.Window() + main_window.connect('destroy', destroy) + main_window.add(i.get_vbox()) + main_window.show() + gtk.main() + +if __name__ == '__main__': + sys.exit(main()) diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index 97cc89cc..96a45d2c 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- """Returns a dict of SQL statements used in fpdb. """ @@ -92,6 +92,8 @@ class Sql: self.query['getLimits'] = """SELECT DISTINCT bigBlind from Gametypes ORDER by bigBlind DESC""" + self.query['getTourneyTypesIds'] = "SELECT id FROM TourneyTypes" + ################################ # Create Settings ################################ @@ -128,7 +130,32 @@ class Sql: name TEXT NOT NULL, code TEXT NOT NULL)""" - + ################################ + # Create Backings + ################################ + + if db_server == 'mysql': + self.query['createBackingsTable'] = """CREATE TABLE Backings ( + id SMALLINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id), + tourneysPlayerId BIGINT UNSIGNED NOT NULL, FOREIGN KEY (tourneysPlayerId) REFERENCES TourneysPlayers(id), + playerId INT UNSIGNED NOT NULL, FOREIGN KEY (playerId) REFERENCES Players(id), + buyInPercentage FLOAT UNSIGNED NOT NULL, + payOffPercentage FLOAT UNSIGNED NOT NULL) ENGINE=INNODB""" + elif db_server == 'postgresql': + self.query['createBackingsTable'] = """CREATE TABLE Backings ( + id BIGSERIAL, PRIMARY KEY (id), + tourneysPlayerId INT NOT NULL, FOREIGN KEY (tourneysPlayerId) REFERENCES TourneysPlayers(id), + playerId INT NOT NULL, FOREIGN KEY (playerId) REFERENCES Players(id), + buyInPercentage FLOAT UNSIGNED NOT NULL, + payOffPercentage FLOAT UNSIGNED NOT NULL)""" + elif db_server == 'sqlite': + self.query['createBackingsTable'] = """CREATE TABLE Backings ( + id INTEGER PRIMARY KEY, + tourneysPlayerId INT NOT NULL, + playerId INT NOT NULL, + buyInPercentage REAL UNSIGNED NOT NULL, + payOffPercentage REAL UNSIGNED NOT NULL)""" + ################################ # Create Gametypes ################################ @@ -163,7 +190,7 @@ class Sql: smallBet int, bigBet int)""" elif db_server == 'sqlite': - self.query['createGametypesTable'] = """CREATE TABLE GameTypes ( + self.query['createGametypesTable'] = """CREATE TABLE Gametypes ( id INTEGER PRIMARY KEY, siteId INTEGER, currency TEXT, @@ -253,11 +280,11 @@ class Sql: siteHandNo BIGINT NOT NULL, tourneyId INT UNSIGNED NOT NULL, gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id), - handStart DATETIME NOT NULL, + startTime DATETIME NOT NULL, importTime DATETIME NOT NULL, seats TINYINT NOT NULL, maxSeats TINYINT NOT NULL, - rush BOOLEAN NOT NULL DEFAULT True, + rush BOOLEAN, boardcard1 smallint, /* 0=none, 1-13=2-Ah 14-26=2-Ad 27-39=2-Ac 40-52=2-As */ boardcard2 smallint, boardcard3 smallint, @@ -290,11 +317,11 @@ class Sql: siteHandNo BIGINT NOT NULL, tourneyId INT NOT NULL, gametypeId INT NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id), - handStart timestamp without time zone NOT NULL, + startTime timestamp without time zone NOT NULL, importTime timestamp without time zone NOT NULL, seats SMALLINT NOT NULL, maxSeats SMALLINT NOT NULL, - rush BOOLEAN NOT NULL DEFAULT True, + rush BOOLEAN, boardcard1 smallint, /* 0=none, 1-13=2-Ah 14-26=2-Ad 27-39=2-Ac 40-52=2-As */ boardcard2 smallint, boardcard3 smallint, @@ -326,11 +353,11 @@ class Sql: siteHandNo INT NOT NULL, tourneyId INT NOT NULL, gametypeId INT NOT NULL, - handStart REAL NOT NULL, + startTime REAL NOT NULL, importTime REAL NOT NULL, seats INT NOT NULL, maxSeats INT NOT NULL, - rush BOOLEAN NOT NULL DEFAULT 1, + rush BOOLEAN, boardcard1 INT, /* 0=none, 1-13=2-Ah 14-26=2-Ad 27-39=2-Ac 40-52=2-As */ boardcard2 INT, boardcard3 INT, @@ -368,6 +395,8 @@ class Sql: currency varchar(4) NOT NULL, buyIn INT NOT NULL, fee INT NOT NULL, + category varchar(9) NOT NULL, + limitType char(2) NOT NULL, buyInChips INT, maxSeats INT, rebuy BOOLEAN, @@ -393,6 +422,8 @@ class Sql: currency varchar(4) NOT NULL, buyin INT NOT NULL, fee INT NOT NULL, + category varchar(9), + limitType char(2), buyInChips INT, maxSeats INT, rebuy BOOLEAN, @@ -417,6 +448,8 @@ class Sql: currency VARCHAR(4) NOT NULL, buyin INT NOT NULL, fee INT NOT NULL, + category TEXT, + limitType TEXT, buyInChips INT, maxSeats INT, rebuy BOOLEAN, @@ -1612,7 +1645,7 @@ class Sql: AND h2.seats between %s and %s ) ) - ORDER BY h.handStart desc, hp2.PlayerId + ORDER BY h.startTime desc, hp2.PlayerId /* order rows by handstart descending so that we can stop reading rows when there's a gap over X minutes between hands (ie. when we get back to start of the session */ @@ -1715,7 +1748,7 @@ class Sql: AND h2.seats between %s and %s ) ) - ORDER BY h.handStart desc, hp2.PlayerId + ORDER BY h.startTime desc, hp2.PlayerId /* order rows by handstart descending so that we can stop reading rows when there's a gap over X minutes between hands (ie. when we get back to start of the session */ @@ -1818,7 +1851,7 @@ class Sql: AND h2.seats between %s and %s ) ) - ORDER BY h.handStart desc, hp2.PlayerId + ORDER BY h.startTime desc, hp2.PlayerId /* order rows by handstart descending so that we can stop reading rows when there's a gap over X minutes between hands (ie. when we get back to start of the session */ @@ -1888,23 +1921,23 @@ class Sql: self.query['get_hand_1day_ago'] = """ select coalesce(max(id),0) from Hands - where handStart < date_sub(utc_timestamp(), interval '1' day)""" + where startTime < date_sub(utc_timestamp(), interval '1' day)""" elif db_server == 'postgresql': self.query['get_hand_1day_ago'] = """ select coalesce(max(id),0) from Hands - where handStart < now() at time zone 'UTC' - interval '1 day'""" + where startTime < now() at time zone 'UTC' - interval '1 day'""" elif db_server == 'sqlite': self.query['get_hand_1day_ago'] = """ select coalesce(max(id),0) from Hands - where handStart < strftime('%J', 'now') - 1""" + where startTime < strftime('%J', 'now') - 1""" # not used yet ... # gets a date, would need to use handsplayers (not hudcache) to get exact hand Id if db_server == 'mysql': self.query['get_date_nhands_ago'] = """ - select concat( 'd', date_format(max(h.handStart), '%Y%m%d') ) + select concat( 'd', date_format(max(h.startTime), '%Y%m%d') ) from (select hp.playerId ,coalesce(greatest(max(hp.handId)-%s,1),1) as maxminusx from HandsPlayers hp @@ -1916,7 +1949,7 @@ class Sql: """ elif db_server == 'postgresql': self.query['get_date_nhands_ago'] = """ - select 'd' || to_char(max(h3.handStart), 'YYMMDD') + select 'd' || to_char(max(h3.startTime), 'YYMMDD') from (select hp.playerId ,coalesce(greatest(max(hp.handId)-%s,1),1) as maxminusx from HandsPlayers hp @@ -1928,7 +1961,7 @@ class Sql: """ elif db_server == 'sqlite': # untested guess at query: self.query['get_date_nhands_ago'] = """ - select 'd' || strftime(max(h3.handStart), 'YYMMDD') + select 'd' || strftime(max(h3.startTime), 'YYMMDD') from (select hp.playerId ,coalesce(greatest(max(hp.handId)-%s,1),1) as maxminusx from HandsPlayers hp @@ -1939,27 +1972,31 @@ class Sql: inner join Hands h on (h.id = hp3.handId) """ - # used in GuiPlayerStats: + # used in Gui*PlayerStats: self.query['getPlayerId'] = """SELECT id from Players where name = %s""" self.query['getPlayerIdBySite'] = """SELECT id from Players where name = %s AND siteId = %s""" - - # used in Filters: + # used in *Filters: self.query['getSiteId'] = """SELECT id from Sites where name = %s""" self.query['getGames'] = """SELECT DISTINCT category from Gametypes""" - self.query['getLimits'] = """SELECT DISTINCT bigBlind from Gametypes ORDER by bigBlind DESC""" + #self.query['getLimits'] = already defined further up self.query['getLimits2'] = """SELECT DISTINCT type, limitType, bigBlind from Gametypes ORDER by type, limitType DESC, bigBlind DESC""" self.query['getLimits3'] = """select DISTINCT type - , limitType - , case type + , gt.limitType + , case type when 'ring' then bigBlind - else buyin - end as bb_or_buyin +- else buyin +- end as bb_or_buyin from Gametypes gt cross join TourneyTypes tt + order by type, gt.limitType DESC, bb_or_buyin DESC""" + self.query['getCashLimits'] = """select DISTINCT type + , limitType + , bigBlind as bb_or_buyin + from Gametypes gt order by type, limitType DESC, bb_or_buyin DESC""" if db_server == 'mysql': @@ -2036,7 +2073,7 @@ class Sql: and h.seats - and date_format(h.handStart, '%Y-%m-%d %T') + and date_format(h.startTime, '%Y-%m-%d %T') group by hgameTypeId ,pname ,gt.base @@ -2133,7 +2170,7 @@ class Sql: and h.seats - and to_char(h.handStart, 'YYYY-MM-DD HH24:MI:SS') + and to_char(h.startTime, 'YYYY-MM-DD HH24:MI:SS') group by hgameTypeId ,pname ,gt.base @@ -2231,7 +2268,7 @@ class Sql: and h.seats - and datetime(h.handStart) + and datetime(h.startTime) group by hgameTypeId ,hp.playerId ,gt.base @@ -2256,6 +2293,42 @@ class Sql: ,s.name """ + if db_server == 'mysql': + self.query['tourneyPlayerDetailedStats'] = """ + select s.name AS siteName + ,t.tourneyTypeId AS tourneyTypeId + ,tt.currency AS currency + ,(CASE WHEN tt.currency = "USD" THEN tt.buyIn/100.0 ELSE tt.buyIn END) AS buyIn + ,tt.fee/100.0 AS fee + ,tt.category AS category + ,tt.limitType AS limitType + ,p.name AS playerName + ,COUNT(1) AS tourneyCount + ,SUM(CASE WHEN tp.rank > 0 THEN 0 ELSE 1 END) AS unknownRank + ,SUM(CASE WHEN winnings > 0 THEN 1 ELSE 0 END)/(COUNT(1) - SUM(CASE WHEN tp.rank > 0 THEN 0 ELSE 1 END)) AS itm + ,SUM(CASE WHEN rank = 1 THEN 1 ELSE 0 END) AS 1st + ,SUM(CASE WHEN rank = 2 THEN 1 ELSE 0 END) AS 2nd + ,SUM(CASE WHEN rank = 3 THEN 1 ELSE 0 END) AS 3rd + ,SUM(tp.winnings)/100.0 AS won + ,SUM(CASE WHEN tt.currency = "USD" THEN (tt.buyIn+tt.fee)/100.0 ELSE tt.buyIn END) AS spent + ,SUM(tp.winnings)/SUM(tt.buyin+tt.fee)*100.0-100 AS roi + ,SUM(tp.winnings-(tt.buyin+tt.fee))/100.0/(COUNT(1)-SUM(CASE WHEN tp.rank > 0 THEN 0 ELSE 1 END)) AS profitPerTourney + from TourneysPlayers tp + inner join Tourneys t on (t.id = tp.tourneyId) + inner join TourneyTypes tt on (tt.Id = t.tourneyTypeId) + inner join Sites s on (s.Id = tt.siteId) + inner join Players p on (p.Id = tp.playerId) + where tp.playerId in + and date_format(t.startTime, '%Y-%m-%d %T') + group by tourneyTypeId, playerName + order by tourneyTypeId + ,playerName + ,siteName""" + elif db_server == 'postgresql': + self.query['tourneyPlayerDetailedStats'] = """TODO""" + elif db_server == 'sqlite': + self.query['tourneyPlayerDetailedStats'] = """TODO""" + if db_server == 'mysql': self.query['playerStats'] = """ SELECT @@ -2355,7 +2428,7 @@ class Sql: inner join Hands h ON h.id = hp.handId where hp.playerId in and hp.tourneysPlayersId IS NULL - and date_format(h.handStart, '%Y-%m-%d') + and date_format(h.startTime, '%Y-%m-%d') group by hp.handId, gtId, hp.totalProfit ) hprof group by hprof.gtId @@ -2458,7 +2531,7 @@ class Sql: inner join Hands h ON (h.id = hp.handId) where hp.playerId in and hp.tourneysPlayersId IS NULL - and to_char(h.handStart, 'YYYY-MM-DD') + and to_char(h.startTime, 'YYYY-MM-DD') group by hp.handId, gtId, hp.totalProfit ) hprof group by hprof.gtId @@ -2593,7 +2666,7 @@ class Sql: inner join Hands h ON (h.id = hp.handId) where hp.playerId in and hp.tourneysPlayersId IS NULL - and date_format(h.handStart, '%Y-%m-%d') + and date_format(h.startTime, '%Y-%m-%d') group by hp.handId, gtId, hp.position, hp.totalProfit ) hprof group by hprof.gtId, PlPosition @@ -2730,7 +2803,7 @@ class Sql: inner join Hands h ON (h.id = hp.handId) where hp.playerId in and hp.tourneysPlayersId IS NULL - and to_char(h.handStart, 'YYYY-MM-DD') + and to_char(h.startTime, 'YYYY-MM-DD') group by hp.handId, gameTypeId, hp.position, hp.totalProfit ) hprof group by hprof.gtId, PlPosition @@ -2751,13 +2824,13 @@ class Sql: INNER JOIN Gametypes gt ON (gt.id = h.gametypeId) WHERE pl.id in AND pl.siteId in - AND h.handStart > '' - AND h.handStart < '' + AND h.startTime > '' + AND h.startTime < '' - AND hp.tourneysPlayersId IS NULL - GROUP BY h.handStart, hp.handId, hp.sawShowdown, hp.totalProfit - ORDER BY h.handStart""" + AND gt.type is 'ring' + GROUP BY h.startTime, hp.handId, hp.sawShowdown, hp.totalProfit + ORDER BY h.startTime""" #################################### @@ -2765,39 +2838,39 @@ class Sql: #################################### if db_server == 'mysql': self.query['sessionStats'] = """ - SELECT UNIX_TIMESTAMP(h.handStart) as time, hp.handId, hp.startCash, hp.winnings, hp.totalProfit + SELECT UNIX_TIMESTAMP(h.startTime) as time, hp.handId, hp.startCash, hp.winnings, hp.totalProfit FROM HandsPlayers hp INNER JOIN Hands h on (h.id = hp.handId) INNER JOIN Gametypes gt on (gt.Id = h.gameTypeId) INNER JOIN Sites s on (s.Id = gt.siteId) INNER JOIN Players p on (p.Id = hp.playerId) WHERE hp.playerId in - AND date_format(h.handStart, '%Y-%m-%d') - AND hp.tourneysPlayersId IS NULL + AND date_format(h.startTime, '%Y-%m-%d') + AND gt.type is 'ring' ORDER by time""" elif db_server == 'postgresql': self.query['sessionStats'] = """ - SELECT EXTRACT(epoch from h.handStart) as time, hp.handId, hp.startCash, hp.winnings, hp.totalProfit + SELECT EXTRACT(epoch from h.startTime) as time, hp.handId, hp.startCash, hp.winnings, hp.totalProfit FROM HandsPlayers hp INNER JOIN Hands h on (h.id = hp.handId) INNER JOIN Gametypes gt on (gt.Id = h.gameTypeId) INNER JOIN Sites s on (s.Id = gt.siteId) INNER JOIN Players p on (p.Id = hp.playerId) WHERE hp.playerId in - AND h.handStart - AND hp.tourneysPlayersId IS NULL + AND h.startTime + AND gt.type is 'ring' ORDER by time""" elif db_server == 'sqlite': self.query['sessionStats'] = """ - SELECT STRFTIME('', h.handStart) as time, hp.handId, hp.startCash, hp.winnings, hp.totalProfit + SELECT STRFTIME('', h.startTime) as time, hp.handId, hp.startCash, hp.winnings, hp.totalProfit FROM HandsPlayers hp INNER JOIN Hands h on (h.id = hp.handId) INNER JOIN Gametypes gt on (gt.Id = h.gameTypeId) INNER JOIN Sites s on (s.Id = gt.siteId) INNER JOIN Players p on (p.Id = hp.playerId) WHERE hp.playerId in - AND h.handStart - AND hp.tourneysPlayersId IS NULL + AND h.startTime + AND gt.type is 'ring' ORDER by time""" @@ -2905,7 +2978,7 @@ class Sql: else 'E' end AS hc_position ,hp.tourneyTypeId - ,date_format(h.handStart, 'd%y%m%d') + ,date_format(h.startTime, 'd%y%m%d') ,count(1) ,sum(wonWhenSeenStreet1) ,sum(wonAtSD) @@ -2984,7 +3057,7 @@ class Sql: ,h.seats ,hc_position ,hp.tourneyTypeId - ,date_format(h.handStart, 'd%y%m%d') + ,date_format(h.startTime, 'd%y%m%d') """ elif db_server == 'postgresql': self.query['rebuildHudCache'] = """ @@ -3084,7 +3157,7 @@ class Sql: else 'E' end AS hc_position ,hp.tourneyTypeId - ,'d' || to_char(h.handStart, 'YYMMDD') + ,'d' || to_char(h.startTime, 'YYMMDD') ,count(1) ,sum(wonWhenSeenStreet1) ,sum(wonAtSD) @@ -3163,7 +3236,7 @@ class Sql: ,h.seats ,hc_position ,hp.tourneyTypeId - ,to_char(h.handStart, 'YYMMDD') + ,to_char(h.startTime, 'YYMMDD') """ else: # assume sqlite self.query['rebuildHudCache'] = """ @@ -3263,7 +3336,7 @@ class Sql: else 'E' end AS hc_position ,hp.tourneyTypeId - ,'d' || substr(strftime('%Y%m%d', h.handStart),3,7) + ,'d' || substr(strftime('%Y%m%d', h.startTime),3,7) ,count(1) ,sum(wonWhenSeenStreet1) ,sum(wonAtSD) @@ -3342,7 +3415,7 @@ class Sql: ,h.seats ,hc_position ,hp.tourneyTypeId - ,'d' || substr(strftime('%Y%m%d', h.handStart),3,7) + ,'d' || substr(strftime('%Y%m%d', h.startTime),3,7) """ self.query['insert_hudcache'] = """ @@ -3605,6 +3678,8 @@ class Sql: AND currency=%s AND buyin=%s AND fee=%s + AND category=%s + AND limitType=%s AND knockout=%s AND rebuy=%s AND addOn=%s @@ -3614,9 +3689,9 @@ class Sql: """ self.query['insertTourneyType'] = """INSERT INTO TourneyTypes - (siteId, currency, buyin, fee, buyInChips, knockout, rebuy, + (siteId, currency, buyin, fee, category, limitType, buyInChips, knockout, rebuy, addOn ,speed, shootout, matrix) - VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) + VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) """ self.query['getTourneyIdByTourneyNo'] = """SELECT t.id @@ -3665,8 +3740,8 @@ class Sql: """ self.query['insertTourneysPlayer'] = """INSERT INTO TourneysPlayers - (tourneyId, playerId, rank, winnings, winningsCurrency, rebuyCount, addOnCount, koCount, comment, commentTs) - VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s) + (tourneyId, playerId, rank, winnings, winningsCurrency, rebuyCount, addOnCount, koCount) + VALUES (%s, %s, %s, %s, %s, %s, %s, %s) """ self.query['selectHandsPlayersWithWrongTTypeId'] = """SELECT id @@ -3693,7 +3768,7 @@ class Sql: gametypeid, sitehandno, tourneyId, - handstart, + startTime, importtime, seats, maxseats, @@ -3835,6 +3910,22 @@ class Sql: %s )""" + ################################ + # Counts for DB stats window + ################################ + self.query['getHandCount'] = "SELECT COUNT(id) FROM Hands" + self.query['getTourneyCount'] = "SELECT COUNT(id) FROM Tourneys" + self.query['getTourneyTypeCount'] = "SELECT COUNT(id) FROM TourneyTypes" + + ################################ + # queries for dumpDatabase + ################################ + for table in (u'Autorates', u'Backings', u'Gametypes', u'Hands', u'HandsActions', u'HandsPlayers', u'HudCache', u'Players', u'Settings', u'Sites', u'TourneyTypes', u'Tourneys', u'TourneysPlayers'): + self.query['get'+table] = u"SELECT * FROM "+table + + ################################ + # placeholders and substitution stuff + ################################ if db_server == 'mysql': self.query['placeholder'] = u'%s' elif db_server == 'postgresql': diff --git a/pyfpdb/Stats.py b/pyfpdb/Stats.py index 646725d4..ea3d33bb 100755 --- a/pyfpdb/Stats.py +++ b/pyfpdb/Stats.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- """Manage collecting and formatting of stats and tooltips. diff --git a/pyfpdb/Summaries.py b/pyfpdb/Summaries.py index 37b3367f..ca19ec7c 100644 --- a/pyfpdb/Summaries.py +++ b/pyfpdb/Summaries.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2008-2010 Steffen Schaumburg diff --git a/pyfpdb/SummaryEverleaf.py b/pyfpdb/SummaryEverleaf.py index 234da0da..3d079bef 100644 --- a/pyfpdb/SummaryEverleaf.py +++ b/pyfpdb/SummaryEverleaf.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (c) 2009-2010 Eric Blade, and the FPDB team. diff --git a/pyfpdb/TableWindow.py b/pyfpdb/TableWindow.py index 84d48b0b..5aed1f04 100644 --- a/pyfpdb/TableWindow.py +++ b/pyfpdb/TableWindow.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- """Discover_TableWindow.py diff --git a/pyfpdb/Tables.py b/pyfpdb/Tables.py index 9f0fbb0d..c33b1828 100755 --- a/pyfpdb/Tables.py +++ b/pyfpdb/Tables.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- """Discover_Tables.py diff --git a/pyfpdb/Tables_Demo.py b/pyfpdb/Tables_Demo.py index 52ec77b1..6659c630 100755 --- a/pyfpdb/Tables_Demo.py +++ b/pyfpdb/Tables_Demo.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- """Tables_Demo.py diff --git a/pyfpdb/TournamentTracker.py b/pyfpdb/TournamentTracker.py index 16491252..1c8ee207 100644 --- a/pyfpdb/TournamentTracker.py +++ b/pyfpdb/TournamentTracker.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- """TourneyTracker.py Based on HUD_main .. who knows if we want to actually use this or not diff --git a/pyfpdb/TourneyFilters.py b/pyfpdb/TourneyFilters.py new file mode 100644 index 00000000..977c94ec --- /dev/null +++ b/pyfpdb/TourneyFilters.py @@ -0,0 +1,540 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +#Copyright 2010 Steffen Schaumburg +#This program is free software: you can redistribute it and/or modify +#it under the terms of the GNU Affero General Public License as published by +#the Free Software Foundation, version 3 of the License. +# +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +# +#You should have received a copy of the GNU Affero General Public License +#along with this program. If not, see . +#In the "official" distribution you can find the license in agpl-3.0.txt. + +import threading +import pygtk +pygtk.require('2.0') +import gtk +import gobject +#import os +#import sys +#from optparse import OptionParser +from time import gmtime, mktime, strftime, strptime +#import pokereval + +import logging #logging has been set up in fpdb.py or HUD_main.py, use their settings: +log = logging.getLogger("filter") + + +#import Configuration +#import Database +#import SQL +import Charset +import Filters + +class TourneyFilters(Filters.Filters): + def __init__(self, db, config, qdict, display = {}, debug=True): + self.debug = debug + self.db = db + self.cursor = db.cursor + self.sql = db.sql + self.conf = db.config + self.display = display + + self.filterText = {'playerstitle':'Hero:', 'sitestitle':'Sites:', 'seatstitle':'Number of Players:', + 'seatsbetween':'Between:', 'seatsand':'And:', 'datestitle':'Date:', + 'tourneyTypesTitle':'Tourney Type'} + + gen = self.conf.get_general_params() + self.day_start = 0 + if 'day_start' in gen: + self.day_start = float(gen['day_start']) + + # Outer Packing box + self.mainVBox = gtk.VBox(False, 0) + + self.label = {} + self.callback = {} + + self.make_filter() + #end def __init__ + + def __calendar_dialog(self, widget, entry): + 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() + #end def __calendar_dialog + + def __clear_dates(self, w): + self.start_date.set_text('') + self.end_date.set_text('') + #end def __clear_dates + + def __get_dates(self): + # self.day_start gives user's start of day in hours + offset = int(self.day_start * 3600) # calc day_start in seconds + + t1 = self.start_date.get_text() + t2 = self.end_date.get_text() + + if t1 == '': + t1 = '1970-01-02' + if t2 == '': + t2 = '2020-12-12' + + s1 = strptime(t1, "%Y-%m-%d") # make time_struct + s2 = strptime(t2, "%Y-%m-%d") + e1 = mktime(s1) + offset # s1 is localtime, but returned time since epoch is UTC, then add the + e2 = mktime(s2) + offset # s2 is localtime, but returned time since epoch is UTC + e2 = e2 + 24 * 3600 - 1 # date test is inclusive, so add 23h 59m 59s to e2 + + adj_t1 = strftime("%Y-%m-%d %H:%M:%S", gmtime(e1)) # make adjusted string including time + adj_t2 = strftime("%Y-%m-%d %H:%M:%S", gmtime(e2)) + log.info("t1="+t1+" adj_t1="+adj_t1+'.') + + return (adj_t1, adj_t2) + #end def __get_dates + + def __refresh(self, widget, entry): + for w in self.mainVBox.get_children(): + w.destroy() + self.make_filter() + #end def __refresh + + def __set_hero_name(self, w, site): + _name = w.get_text() + # get_text() returns a str but we want internal variables to be unicode: + _guiname = unicode(_name) + self.heroes[site] = _guiname + #log.debug("setting heroes[%s]: %s"%(site, self.heroes[site])) + #end def __set_hero_name + + def __set_num_tourneys(self, w, val): + try: + self.numTourneys = int(w.get_text()) + except: + self.numTourneys = 0 + print "setting numTourneys:", self.numTourneys + #end def __set_num_tourneys + + def __set_seat_select(self, w, seat): + #print "__set_seat_select: seat =", seat, "active =", w.get_active() + self.seats[seat] = w.get_active() + log.debug( "self.seats[%s] set to %s" %(seat, self.seats[seat]) ) + #end def __set_seat_select + + def __set_site_select(self, w, site): + #print w.get_active() + self.sites[site] = w.get_active() + log.debug("self.sites[%s] set to %s" %(site, self.sites[site])) + #end def __set_site_select + + def __set_tourney_type_select(self, w, tourneyType): + #print w.get_active() + self.tourneyTypes[tourneyType] = w.get_active() + log.debug("self.tourney_types[%s] set to %s" %(tourneyType, self.tourneyTypes[tourneyType])) + #end def __set_tourney_type_select + + def __toggle_box(self, widget, entry): + if self.boxes[entry].props.visible: + self.boxes[entry].hide() + widget.set_label("show") + else: + self.boxes[entry].show() + widget.set_label("hide") + #end def __toggle_box + + def createPlayerLine(self, hbox, site, player): + log.debug('add:"%s"' % player) + label = gtk.Label(site +" id:") + hbox.pack_start(label, False, False, 3) + + pname = gtk.Entry() + pname.set_text(player) + pname.set_width_chars(20) + hbox.pack_start(pname, False, True, 0) + #pname.connect("changed", self.__set_hero_name, site) + + # Added EntryCompletion but maybe comboBoxEntry is more flexible? (e.g. multiple choices) + completion = gtk.EntryCompletion() + pname.set_completion(completion) + liststore = gtk.ListStore(gobject.TYPE_STRING) + completion.set_model(liststore) + completion.set_text_column(0) + names = self.db.get_player_names(self.conf, self.siteid[site]) # (config=self.conf, site_id=None, like_player_name="%") + for n in names: # list of single-element "tuples" + _n = Charset.to_gui(n[0]) + _nt = (_n, ) + liststore.append(_nt) + + self.__set_hero_name(pname, site) + #end def createPlayerLine + + def createSiteLine(self, hbox, site): + cb = gtk.CheckButton(site) + cb.connect('clicked', self.__set_site_select, site) + cb.set_active(True) + hbox.pack_start(cb, False, False, 0) + #end def createSiteLine + + def createTourneyTypeLine(self, hbox, tourneyType): + cb = gtk.CheckButton(str(tourneyType)) + cb.connect('clicked', self.__set_tourney_type_select, tourneyType) + hbox.pack_start(cb, False, False, 0) + cb.set_active(True) + #end def createTourneyTypeLine + + def fillDateFrame(self, vbox): + # Hat tip to Mika Bostrom - calendar code comes from PokerStats + top_hbox = gtk.HBox(False, 0) + vbox.pack_start(top_hbox, False, False, 0) + lbl_title = gtk.Label(self.filterText['datestitle']) + lbl_title.set_alignment(xalign=0.0, yalign=0.5) + top_hbox.pack_start(lbl_title, expand=True, padding=3) + showb = gtk.Button(label="hide", stock=None, use_underline=True) + showb.set_alignment(xalign=1.0, yalign=0.5) + showb.connect('clicked', self.__toggle_box, 'dates') + top_hbox.pack_start(showb, expand=False, padding=1) + + vbox1 = gtk.VBox(False, 0) + vbox.pack_start(vbox1, False, False, 0) + self.boxes['dates'] = vbox1 + + hbox = gtk.HBox() + vbox1.pack_start(hbox, False, True, 0) + + lbl_start = gtk.Label('From:') + + btn_start = gtk.Button() + btn_start.set_image(gtk.image_new_from_stock(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)) + btn_start.connect('clicked', self.__calendar_dialog, self.start_date) + + hbox.pack_start(lbl_start, expand=False, padding=3) + hbox.pack_start(btn_start, expand=False, padding=3) + hbox.pack_start(self.start_date, expand=False, padding=2) + + #New row for end date + hbox = gtk.HBox() + vbox1.pack_start(hbox, False, True, 0) + + lbl_end = gtk.Label(' To:') + btn_end = gtk.Button() + btn_end.set_image(gtk.image_new_from_stock(gtk.STOCK_INDEX, gtk.ICON_SIZE_BUTTON)) + btn_end.connect('clicked', self.__calendar_dialog, self.end_date) + + btn_clear = gtk.Button(label=' Clear Dates ') + btn_clear.connect('clicked', self.__clear_dates) + + hbox.pack_start(lbl_end, expand=False, padding=3) + hbox.pack_start(btn_end, expand=False, padding=3) + hbox.pack_start(self.end_date, expand=False, padding=2) + + hbox.pack_start(btn_clear, expand=False, padding=15) + #end def fillDateFrame + + def fillPlayerFrame(self, vbox, display): + top_hbox = gtk.HBox(False, 0) + vbox.pack_start(top_hbox, False, False, 0) + lbl_title = gtk.Label(self.filterText['playerstitle']) + lbl_title.set_alignment(xalign=0.0, yalign=0.5) + top_hbox.pack_start(lbl_title, expand=True, padding=3) + showb = gtk.Button(label="refresh", stock=None, use_underline=True) + showb.set_alignment(xalign=1.0, yalign=0.5) + showb.connect('clicked', self.__refresh, 'players') + + vbox1 = gtk.VBox(False, 0) + vbox.pack_start(vbox1, False, False, 0) + self.boxes['players'] = vbox1 + + for site in self.conf.get_supported_sites(): + hBox = gtk.HBox(False, 0) + vbox1.pack_start(hBox, False, True, 0) + + player = self.conf.supported_sites[site].screen_name + _pname = Charset.to_gui(player) + self.createPlayerLine(hBox, site, _pname) + + hbox = gtk.HBox(False, 0) + vbox1.pack_start(hbox, False, False, 0) + #cb = gtk.CheckButton(self.filterText['groupsall']) + #cb.connect('clicked', self.__set_group_select, 'allplayers') + #hbox.pack_start(cb, False, False, 0) + #self.sbGroups['allplayers'] = cb + #self.groups['allplayers'] = False + + #lbl = gtk.Label('Min # Hands:') + #lbl.set_alignment(xalign=1.0, yalign=0.5) + #hbox.pack_start(lbl, expand=True, padding=3) + + #phands = gtk.Entry() + #phands.set_text('0') + #phands.set_width_chars(8) + #hbox.pack_start(phands, False, False, 0) + #phands.connect("changed", self.__set_num_hands, site) + + top_hbox.pack_start(showb, expand=False, padding=1) + #end def fillPlayerFrame + + def fillSeatsFrame(self, vbox, display): + hbox = gtk.HBox(False, 0) + vbox.pack_start(hbox, False, False, 0) + lbl_title = gtk.Label(self.filterText['seatstitle']) + lbl_title.set_alignment(xalign=0.0, yalign=0.5) + hbox.pack_start(lbl_title, expand=True, padding=3) + showb = gtk.Button(label="hide", stock=None, use_underline=True) + showb.set_alignment(xalign=1.0, yalign=0.5) + showb.connect('clicked', self.__toggle_box, 'seats') + hbox.pack_start(showb, expand=False, padding=1) + + vbox1 = gtk.VBox(False, 0) + vbox.pack_start(vbox1, False, False, 0) + self.boxes['seats'] = vbox1 + + hbox = gtk.HBox(False, 0) + vbox1.pack_start(hbox, False, True, 0) + + lbl_from = gtk.Label(self.filterText['seatsbetween']) + lbl_to = gtk.Label(self.filterText['seatsand']) + adj1 = gtk.Adjustment(value=2, lower=2, upper=10, step_incr=1, page_incr=1, page_size=0) + sb1 = gtk.SpinButton(adjustment=adj1, climb_rate=0.0, digits=0) + adj2 = gtk.Adjustment(value=10, lower=2, upper=10, step_incr=1, page_incr=1, page_size=0) + sb2 = gtk.SpinButton(adjustment=adj2, climb_rate=0.0, digits=0) + + hbox.pack_start(lbl_from, expand=False, padding=3) + hbox.pack_start(sb1, False, False, 0) + hbox.pack_start(lbl_to, expand=False, padding=3) + hbox.pack_start(sb2, False, False, 0) + + self.sbSeats['from'] = sb1 + self.sbSeats['to'] = sb2 + #end def fillSeatsFrame + + def fillSitesFrame(self, vbox): + top_hbox = gtk.HBox(False, 0) + top_hbox.show() + vbox.pack_start(top_hbox, False, False, 0) + + lbl_title = gtk.Label(self.filterText['sitestitle']) + lbl_title.set_alignment(xalign=0.0, yalign=0.5) + top_hbox.pack_start(lbl_title, expand=True, padding=3) + + showb = gtk.Button(label="hide", stock=None, use_underline=True) + showb.set_alignment(xalign=1.0, yalign=0.5) + showb.connect('clicked', self.__toggle_box, 'sites') + showb.show() + top_hbox.pack_start(showb, expand=False, padding=1) + + vbox1 = gtk.VBox(False, 0) + self.boxes['sites'] = vbox1 + vbox.pack_start(vbox1, False, False, 0) + + for site in self.conf.get_supported_sites(): + hbox = gtk.HBox(False, 0) + vbox1.pack_start(hbox, False, True, 0) + self.createSiteLine(hbox, site) + #end def fillSitesFrame + + def fillTourneyTypesFrame(self, vbox): + top_hbox = gtk.HBox(False, 0) + vbox.pack_start(top_hbox, False, False, 0) + lbl_title = gtk.Label(self.filterText['tourneyTypesTitle']) + lbl_title.set_alignment(xalign=0.0, yalign=0.5) + top_hbox.pack_start(lbl_title, expand=True, padding=3) + showb = gtk.Button(label="hide", stock=None, use_underline=True) + showb.set_alignment(xalign=1.0, yalign=0.5) + showb.connect('clicked', self.__toggle_box, 'tourneyTypes') + top_hbox.pack_start(showb, expand=False, padding=1) + + vbox1 = gtk.VBox(False, 0) + vbox.pack_start(vbox1, False, False, 0) + self.boxes['tourneyTypes'] = vbox1 + + result = self.db.getTourneyTypesIds() + if len(result) >= 1: + for line in result: + hbox = gtk.HBox(False, 0) + vbox1.pack_start(hbox, False, True, 0) + self.createTourneyTypeLine(hbox, line[0]) + else: + print "INFO: No tourney types returned from database" + log.info("No tourney types returned from database") + #end def fillTourneyTypesFrame + + def getDates(self): + return self.__get_dates() + #end def getDates + + def getHeroes(self): + return self.heroes + #end def getHeroes + + def getNumTourneys(self): + return self.numTourneys + #end def getNumTourneys + + def getSeats(self): + if 'from' in self.sbSeats: + self.seats['from'] = self.sbSeats['from'].get_value_as_int() + if 'to' in self.sbSeats: + self.seats['to'] = self.sbSeats['to'].get_value_as_int() + return self.seats + #end def getSeats + + def getSiteIds(self): + return self.siteid + #end def getSiteIds + + def getSites(self): + return self.sites + #end def getSites + + def getTourneyTypes(self): + return self.tourneyTypes + #end def getTourneyTypes + + def get_vbox(self): + """returns the vbox of this thread""" + return self.mainVBox + #end def get_vbox + + def make_filter(self): + self.tourneyTypes = {} + #self.tourneys = {} + self.sites = {} + self.seats = {} + self.siteid = {} + self.heroes = {} + self.boxes = {} + + for site in self.conf.get_supported_sites(): + #Get db site id for filtering later + self.cursor.execute(self.sql.query['getSiteId'], (site,)) + result = self.db.cursor.fetchall() + if len(result) == 1: + self.siteid[site] = result[0][0] + else: + print "Either 0 or more than one site matched (%s) - EEK" % site + + # For use in date ranges. + self.start_date = gtk.Entry(max=12) + self.end_date = gtk.Entry(max=12) + self.start_date.set_property('editable', False) + self.end_date.set_property('editable', False) + + # For use in groups etc + #self.sbGroups = {} + self.numTourneys = 0 + + playerFrame = gtk.Frame() + playerFrame.set_label_align(0.0, 0.0) + vbox = gtk.VBox(False, 0) + + self.fillPlayerFrame(vbox, self.display) + playerFrame.add(vbox) + + sitesFrame = gtk.Frame() + sitesFrame.set_label_align(0.0, 0.0) + vbox = gtk.VBox(False, 0) + + self.fillSitesFrame(vbox) + sitesFrame.add(vbox) + + # Tourney types + tourneyTypesFrame = gtk.Frame() + tourneyTypesFrame.set_label_align(0.0, 0.0) + tourneyTypesFrame.show() + vbox = gtk.VBox(False, 0) + + self.fillTourneyTypesFrame(vbox) + tourneyTypesFrame.add(vbox) + + # Seats + seatsFrame = gtk.Frame() + seatsFrame.show() + vbox = gtk.VBox(False, 0) + self.sbSeats = {} + + self.fillSeatsFrame(vbox, self.display) + seatsFrame.add(vbox) + + # Date + dateFrame = gtk.Frame() + dateFrame.set_label_align(0.0, 0.0) + dateFrame.show() + vbox = gtk.VBox(False, 0) + + self.fillDateFrame(vbox) + dateFrame.add(vbox) + + # Buttons + #self.Button1=gtk.Button("Unnamed 1") + #self.Button1.set_sensitive(False) + + self.Button2=gtk.Button("Unnamed 2") + self.Button2.set_sensitive(False) + + self.mainVBox.add(playerFrame) + self.mainVBox.add(sitesFrame) + self.mainVBox.add(seatsFrame) + self.mainVBox.add(dateFrame) + #self.mainVBox.add(self.Button1) + self.mainVBox.add(self.Button2) + + self.mainVBox.show_all() + + # Should do this cleaner + if "Heroes" not in self.display or self.display["Heroes"] == False: + playerFrame.hide() + if "Sites" not in self.display or self.display["Sites"] == False: + sitesFrame.hide() + if "Seats" not in self.display or self.display["Seats"] == False: + seatsFrame.hide() + if "Dates" not in self.display or self.display["Dates"] == False: + dateFrame.hide() + #if "Button1" not in self.display or self.display["Button1"] == False: + # self.Button1.hide() + if "Button2" not in self.display or self.display["Button2"] == False: + self.Button2.hide() + + #if 'button1' in self.label and self.label['button1']: + # self.Button1.set_label( self.label['button1'] ) + if 'button2' in self.label and self.label['button2']: + self.Button2.set_label( self.label['button2'] ) + #if 'button1' in self.callback and self.callback['button1']: + # self.Button1.connect("clicked", self.callback['button1'], "clicked") + # self.Button1.set_sensitive(True) + if 'button2' in self.callback and self.callback['button2']: + self.Button2.connect("clicked", self.callback['button2'], "clicked") + self.Button2.set_sensitive(True) + + # make sure any locks on db are released: + self.db.rollback() + #end def make_filter + + def registerButton2Name(self, title): + self.Button2.set_label(title) + self.label['button2'] = title + #end def registerButton2Name + + def registerButton2Callback(self, callback): + self.Button2.connect("clicked", callback, "clicked") + self.Button2.set_sensitive(True) + self.callback['button2'] = callback + #end def registerButton2Callback +#end class TourneyFilters diff --git a/pyfpdb/TourneySummary.py b/pyfpdb/TourneySummary.py index 5f12b412..89d7388f 100644 --- a/pyfpdb/TourneySummary.py +++ b/pyfpdb/TourneySummary.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2009-2010 Stephane Alessio @@ -47,10 +47,11 @@ class TourneySummary(object): SITEIDS = {'Fulltilt':1, 'PokerStars':2, 'Everleaf':3, 'Win2day':4, 'OnGame':5, 'UltimateBet':6, 'Betfair':7, 'Absolute':8, 'PartyPoker':9 } - def __init__(self, sitename, gametype, summaryText, builtFrom = "HHC"): - self.sitename = sitename - self.siteId = self.SITEIDS[sitename] - self.gametype = gametype + def __init__(self, db, config, siteName, summaryText, builtFrom = "HHC"): + self.db = db + self.config = config + self.siteName = siteName + self.siteId = self.SITEIDS[siteName] self.summaryText = summaryText self.tourneyName = None @@ -75,6 +76,7 @@ class TourneySummary(object): self.isMatrix = False self.isShootout = False self.matrixMatchId = None # For Matrix tourneys : 1-4 => match tables (traditionnal), 0 => Positional winnings info + self.matrixIdProcessed = None self.subTourneyBuyin = None self.subTourneyFee = None self.rebuyChips = 0 @@ -90,8 +92,11 @@ class TourneySummary(object): self.isSatellite = False self.isDoubleOrNothing = False self.guarantee = 0 + self.gametype = {'category':None, 'limitType':None} # Collections indexed by player names + self.playerIds = {} + self.tourneysPlayersIds = {} self.ranks = {} self.winnings = {} self.winningsCurrency = {} @@ -101,16 +106,15 @@ class TourneySummary(object): # currency symbol for this summary self.sym = None - #self.sym = self.SYMBOL[self.gametype['currency']] # save typing! delete this attr when done if builtFrom=="IMAP": self.parseSummary() - #TODO: self.insert() + self.insertOrUpdate() #end def __init__ def __str__(self): #TODO : Update - vars = ( ("SITE", self.sitename), + vars = ( ("SITE", self.siteName), ("START TIME", self.startTime), ("END TIME", self.endTime), ("TOURNEY NAME", self.tourneyName), @@ -119,6 +123,7 @@ class TourneySummary(object): ("TOURNEY ID", self.tourneyId), ("BUYIN", self.buyin), ("FEE", self.fee), + ("CURRENCY", self.currency), ("HERO", self.hero), ("MAXSEATS", self.maxseats), ("ENTRIES", self.entries), @@ -130,6 +135,7 @@ class TourneySummary(object): ("ADDON", self.isAddOn), ("KO", self.isKO), ("MATRIX", self.isMatrix), + ("MATRIX ID PROCESSED", self.matrixIdProcessed), ("SHOOTOUT", self.isShootout), ("MATRIX MATCH ID", self.matrixMatchId), ("SUB TOURNEY BUY IN", self.subTourneyBuyin), @@ -148,10 +154,12 @@ class TourneySummary(object): ("GUARANTEE", self.guarantee) ) - structs = ( ("GAMETYPE", self.gametype), + structs = ( ("PLAYER IDS", self.playerIds), ("PLAYERS", self.players), + ("TOURNEYS PLAYERS IDS", self.tourneysPlayersIds), ("RANKS", self.ranks), ("WINNINGS", self.winnings), + ("WINNINGS CURRENCY", self.winningsCurrency), ("COUNT REBUYS", self.rebuyCounts), ("COUNT ADDONS", self.addOnCounts), ("NB OF KO", self.koCounts) @@ -170,10 +178,8 @@ class TourneySummary(object): def getSummaryText(self): return self.summaryText - - def insert(self, db): - # Note that this method is not used by the PS tourney storage stuff - this is for summary files only - + + def insertOrUpdate(self): # First : check all needed info is filled in the object, especially for the initial select # Notes on DB Insert @@ -184,15 +190,26 @@ class TourneySummary(object): # Starttime may not match the one in the Summary file : HH = time of the first Hand / could be slighltly different from the one in the summary file # Note: If the TourneyNo could be a unique id .... this would really be a relief to deal with matrix matches ==> Ask on the IRC / Ask Fulltilt ?? - dbTourneyTypeId = db.getTourneyTypeId(self) - logging.debug("Tourney Type ID = %d" % dbTourneyTypeId) - dbTourneyId = db.tRecognizeTourney(self, dbTourneyTypeId) - logging.debug("Tourney ID = %d" % dbTourneyId) - dbTourneysPlayersIds = db.tStoreTourneysPlayers(self, dbTourneyId) - logging.debug("TourneysPlayersId = %s" % dbTourneysPlayersIds) - db.tUpdateTourneysHandsPlayers(self, dbTourneysPlayersIds, dbTourneyTypeId) - logging.debug("tUpdateTourneysHandsPlayers done") - logging.debug("Tourney Insert done") + for player in self.players: + id=self.db.get_player_id(self.config, self.siteName, player) + if not id: + id=self.db.insertPlayer(player, self.siteId) + self.playerIds.update({player:id}) + + #print "TS.insert players",self.players,"playerIds",self.playerIds + + self.buyinCurrency=self.currency + self.dbid_pids=self.playerIds #TODO:rename this field in Hand so this silly renaming can be removed + + #print "TS.self before starting insert",self + self.tourneyTypeId = self.db.createOrUpdateTourneyType(self) + self.db.commit() + self.tourneyId = self.db.createOrUpdateTourney(self, "TS") + self.db.commit() + self.tourneysPlayersIds = self.db.createOrUpdateTourneysPlayers(self, "TS") + self.db.commit() + + logging.debug("Tourney Insert/Update done") # TO DO : Return what has been done (tourney created, updated, nothing) # ?? stored = 1 if tourney is fully created / duplicates = 1, if everything was already here and correct / partial=1 if some things were already here (between tourney, tourneysPlayers and handsPlayers) @@ -214,15 +231,28 @@ winnings (decimal) the money the player ended the tourney with (can be 0, or """ log.debug("addPlayer: rank:%s - name : '%s' - Winnings (%s)" % (rank, name, winnings)) self.players.append(name) - self.ranks.update( { name : Decimal(rank) } ) - self.winnings.update( { name : Decimal(winnings) } ) - self.winningsCurrency.update( { name : winningsCurrency } ) + if rank: + self.ranks.update( { name : Decimal(rank) } ) + self.winnings.update( { name : Decimal(winnings) } ) + self.winningsCurrency.update( { name : winningsCurrency } ) + else: + self.ranks.update( { name : None } ) + self.winnings.update( { name : None } ) + self.winningsCurrency.update( { name : None } ) if rebuyCount: self.rebuyCounts.update( {name: Decimal(rebuyCount) } ) + else: + self.rebuyCounts.update( {name: None } ) + if addOnCount: self.addOnCounts.update( {name: Decimal(addOnCount) } ) + else: + self.addOnCounts.update( {name: None } ) + if koCount: self.koCounts.update( {name : Decimal(koCount) } ) + else: + self.koCounts.update( {name: None } ) #end def addPlayer def incrementPlayerWinnings(self, name, additionnalWinnings): @@ -240,35 +270,6 @@ winnings (decimal) the money the player ended the tourney with (can be 0, or print "checkPlayerExists", player, "fail" raise FpdbParseError - - def getGameTypeAsString(self): - """\ -Map the tuple self.gametype onto the pokerstars string describing it -""" - # currently it appears to be something like ["ring", "hold", "nl", sb, bb]: - gs = {"holdem" : "Hold'em", - "omahahi" : "Omaha", - "omahahilo" : "Omaha Hi/Lo", - "razz" : "Razz", - "studhi" : "7 Card Stud", - "studhilo" : "7 Card Stud Hi/Lo", - "fivedraw" : "5 Card Draw", - "27_1draw" : "FIXME", - "27_3draw" : "Triple Draw 2-7 Lowball", - "badugi" : "Badugi" - } - ls = {"nl" : "No Limit", - "pl" : "Pot Limit", - "fl" : "Limit", - "cn" : "Cap No Limit", - "cp" : "Cap Pot Limit" - } - - log.debug("gametype: %s" %(self.gametype)) - retstring = "%s %s" %(gs[self.gametype['category']], ls[self.gametype['limitType']]) - return retstring - - def writeSummary(self, fh=sys.__stdout__): print >>fh, "Override me" @@ -280,7 +281,7 @@ def assemble(cnxn, tourneyId): #TODO: move this method to Hand or Database # TODO !! c = cnxn.cursor() - # We need at least sitename, gametype, handid + # We need at least siteName, gametype, handid # for the Hand.__init__ c.execute(""" select @@ -316,7 +317,7 @@ limit 1""", {'handid':handid}) #TODO: siteid should be in hands table - we took the scenic route through players here. res = c.fetchone() gametype = {'category':res[1],'base':res[2],'type':res[3],'limitType':res[4],'hilo':res[5],'sb':res[6],'bb':res[7], 'currency':res[10]} - h = HoldemOmahaHand(hhc = None, sitename=res[0], gametype = gametype, handText=None, builtFrom = "DB", handid=handid) + h = HoldemOmahaHand(hhc = None, siteName=res[0], gametype = gametype, handText=None, builtFrom = "DB", handid=handid) cards = map(Card.valueSuitFromCard, res[11:16] ) if cards[0]: h.setCommunityCards('FLOP', cards[0:3]) @@ -334,9 +335,9 @@ limit 1""", {'handid':handid}) SELECT h.sitehandno as hid, h.tablename as table, - h.handstart as startTime + h.startTime as startTime FROM - hands as h + Hands as h WHERE h.id = %(handid)s """, {'handid':handid}) res = c.fetchone() diff --git a/pyfpdb/UltimateBetToFpdb.py b/pyfpdb/UltimateBetToFpdb.py index 15655ecc..acf29b06 100755 --- a/pyfpdb/UltimateBetToFpdb.py +++ b/pyfpdb/UltimateBetToFpdb.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- # # Copyright 2008-2010 Carl Gherardi diff --git a/pyfpdb/Win2dayToFpdb.py b/pyfpdb/Win2dayToFpdb.py index 7e76adef..dd3d922e 100755 --- a/pyfpdb/Win2dayToFpdb.py +++ b/pyfpdb/Win2dayToFpdb.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- # # Copyright 2008-2010, Carl Gherardi diff --git a/pyfpdb/WinTables.py b/pyfpdb/WinTables.py index c75ad248..8d90eb5b 100644 --- a/pyfpdb/WinTables.py +++ b/pyfpdb/WinTables.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- """WinTables.py diff --git a/pyfpdb/XTables.py b/pyfpdb/XTables.py index db9a89c5..d0349563 100644 --- a/pyfpdb/XTables.py +++ b/pyfpdb/XTables.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- """Discover_Tables.py diff --git a/pyfpdb/fpdb.pyw b/pyfpdb/fpdb.pyw index 3c42ae1c..1203246f 100755 --- a/pyfpdb/fpdb.pyw +++ b/pyfpdb/fpdb.pyw @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2008-2010 Steffen Schaumburg @@ -103,7 +103,9 @@ import GuiPrefs import GuiLogView import GuiDatabase import GuiBulkImport -import GuiPlayerStats +import ImapSummaries +import GuiRingPlayerStats +import GuiTourneyPlayerStats import GuiPositionalStats import GuiTableViewer import GuiAutoImport @@ -114,7 +116,7 @@ import Database import Configuration import Exceptions -VERSION = "0.20" +VERSION = "0.20 plus git" class fpdb: @@ -234,6 +236,7 @@ class fpdb: dia.set_comments("") dia.set_license("This program is licensed under the AGPL3, see agpl-3.0.txt in the fpdb installation directory") dia.set_website("http://fpdb.sourceforge.net/") + dia.set_authors(['Steffen', 'Eratosthenes', 'Carl Gherardi', 'Eric Blade', '_mt', 'sqlcoder', 'Bostik', 'and others']) dia.set_program_name("Free Poker Database (FPDB)") @@ -260,11 +263,17 @@ class fpdb: view.modify_font(pango.FontDescription('monospace 10')) view.show() dia.vbox.pack_end(view, True, True, 2) - l = gtk.Label('Version Information:') + + l = gtk.Label("Your config file is: "+self.config.file) l.set_alignment(0.5, 0.5) l.show() dia.vbox.pack_end(l, True, True, 2) + l = gtk.Label('Version Information:') + l.set_alignment(0.5, 0.5) + l.show() + dia.vbox.pack_end(l, True, True, 2) + dia.run() dia.destroy() log.debug("Threads: ") @@ -299,7 +308,7 @@ class fpdb: self.warning_box("Unimplemented: Maintain Databases") return if len(self.tab_names) == 1: - if self.obtain_global_lock(): # returns true if successful + if self.obtain_global_lock("dia_maintain_dbs"): # returns true if successful # only main tab has been opened, open dialog dia = gtk.Dialog("Maintain Databases", self.window, @@ -321,32 +330,20 @@ class fpdb: self.warning_box("Cannot open Database Maintenance window because " + "other windows have been opened. Re-start fpdb to use this option.") - def dia_create_del_user(self, widget, data=None): - self.warning_box("Unimplemented: Create/Delete user") - self.obtain_global_lock() - self.release_global_lock() - def dia_database_stats(self, widget, data=None): - self.warning_box("Unimplemented: Database Stats") + self.warning_box(str="Number of Hands: "+str(self.db.getHandCount())+ + "\nNumber of Tourneys: "+str(self.db.getTourneyCount())+ + "\nNumber of TourneyTypes: "+str(self.db.getTourneyTypeCount()), + diatitle="Database Statistics") + #end def dia_database_stats - def dia_delete_db_parts(self, widget, data=None): - self.warning_box("Unimplemented: Delete Database Parts") - self.obtain_global_lock() - self.release_global_lock() + def dia_dump_db(self, widget, data=None): + self.db.dumpDatabase("database-dump.sql") + #end def dia_database_stats - def dia_edit_profile(self, widget=None, data=None, create_default=False, path=None): - self.warning_box("Unimplemented: Edit Profile") - self.obtain_global_lock() - self.release_global_lock() - def dia_export_db(self, widget, data=None): - self.warning_box("Unimplemented: Export Database") - self.obtain_global_lock() - self.release_global_lock() - - def dia_get_db_root_credentials(self): - """obtains db root credentials from user""" - self.warning_box("Unimplemented: Get Root Database Credentials") +# def dia_get_db_root_credentials(self): +# """obtains db root credentials from user""" # user, pw=None, None # # dialog=gtk.Dialog(title="DB Credentials needed", parent=None, flags=0, @@ -363,17 +360,12 @@ class fpdb: # dialog.destroy() # return (user, pw, response) - def dia_import_db(self, widget, data=None): - self.warning_box("Unimplemented: Import Database") - self.obtain_global_lock() - self.release_global_lock() - def dia_licensing(self, widget, data=None): self.warning_box("Unimplemented: Licensing") def dia_load_profile(self, widget, data=None): """Dialogue to select a file to load a profile from""" - if self.obtain_global_lock(): # returns true if successful + if self.obtain_global_lock("fpdb.dia_load_profile"): # returns true if successful #try: # chooser = gtk.FileChooserDialog(title="Please select a profile file to load", # action=gtk.FILE_CHOOSER_ACTION_OPEN, @@ -396,7 +388,7 @@ class fpdb: def dia_recreate_tables(self, widget, data=None): """Dialogue that asks user to confirm that he wants to delete and recreate the tables""" - if self.obtain_global_lock(): # returns true if successful + if self.obtain_global_lock("fpdb.dia_recreate_tables"): # returns true if successful #lock_released = False dia_confirm = gtk.MessageDialog(parent=self.window, flags=gtk.DIALOG_DESTROY_WITH_PARENT, type=gtk.MESSAGE_WARNING, @@ -443,7 +435,7 @@ class fpdb: #end def dia_recreate_tables def dia_recreate_hudcache(self, widget, data=None): - if self.obtain_global_lock(): + if self.obtain_global_lock("dia_recreate_hudcache"): self.dia_confirm = gtk.MessageDialog(parent=self.window, flags=gtk.DIALOG_DESTROY_WITH_PARENT, 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." self.dia_confirm.format_secondary_text(diastring) @@ -495,7 +487,7 @@ class fpdb: self.release_global_lock() def dia_rebuild_indexes(self, widget, data=None): - if self.obtain_global_lock(): + if self.obtain_global_lock("dia_rebuild_indexes"): self.dia_confirm = gtk.MessageDialog(parent=self.window ,flags=gtk.DIALOG_DESTROY_WITH_PARENT ,type=gtk.MESSAGE_WARNING @@ -536,7 +528,7 @@ class fpdb: """opens the log viewer window""" #lock_set = False - #if self.obtain_global_lock(): + #if self.obtain_global_lock("dia_logs"): # lock_set = True # remove members from self.threads if close messages received @@ -622,8 +614,8 @@ class fpdb: def dia_regression_test(self, widget, data=None): self.warning_box("Unimplemented: Regression Test") - self.obtain_global_lock() - self.release_global_lock() + #self.obtain_global_lock("dia_regression_test") + #self.release_global_lock() def dia_save_profile(self, widget, data=None): self.warning_box("Unimplemented: Save Profile (try saving a HUD layout, that should do it)") @@ -653,7 +645,6 @@ class fpdb: - @@ -662,30 +653,27 @@ class fpdb: + - - - - + + - - - + + - @@ -702,32 +690,28 @@ class fpdb: actiongroup.add_actions([('main', None, '_Main'), ('Quit', gtk.STOCK_QUIT, '_Quit', None, 'Quit the Program', self.quit), ('LoadProf', None, '_Load Profile (broken)', 'L', 'Load your profile', self.dia_load_profile), - ('EditProf', None, '_Edit Profile (todo)', 'E', 'Edit your profile', self.dia_edit_profile), ('SaveProf', None, '_Save Profile (todo)', 'S', 'Save your profile', self.dia_save_profile), ('Preferences', None, 'Pre_ferences', 'F', 'Edit your preferences', self.dia_preferences), ('import', None, '_Import'), ('sethharchive', None, '_Set HandHistory Archive Directory', None, 'Set HandHistory Archive Directory', self.select_hhArchiveBase), ('bulkimp', None, '_Bulk Import', 'B', 'Bulk Import', self.tab_bulk_import), - ('autorate', None, 'Auto _Rating (todo)', 'R', 'Auto Rating (todo)', self.not_implemented), + ('imapsummaries', None, '_Import Tourney Summaries through eMail/IMAP', 'I', 'Auto Import and HUD', self.import_imap_summaries), ('viewers', None, '_Viewers'), ('autoimp', None, '_Auto Import and HUD', 'A', 'Auto Import and HUD', self.tab_auto_import), ('graphs', None, '_Graphs', 'G', 'Graphs', self.tabGraphViewer), - ('handreplay', None, 'Hand _Replayer (todo)', None, 'Hand Replayer (todo)', self.not_implemented), - ('playerdetails', None, 'Player _Details (todo)', None, 'Player Details (todo)', self.not_implemented), - ('playerstats', None, '_Player Stats (tabulated view)', 'P', 'Player Stats (tabulated view)', self.tab_player_stats), + ('ringplayerstats', None, 'Ring _Player Stats (tabulated view)', 'P', 'Ring Player Stats (tabulated view)', self.tab_ring_player_stats), + ('tourneyplayerstats', None, '_Tourney Player Stats (tabulated view)', 'T', 'Tourney Player Stats (tabulated view)', self.tab_tourney_player_stats), ('posnstats', None, 'P_ositional Stats (tabulated view)', 'O', 'Positional Stats (tabulated view)', self.tab_positional_stats), ('sessionstats', None, 'Session Stats', None, 'Session Stats', self.tab_session_stats), - ('sessionreplay', None, '_Session Replayer (todo)', None, 'Session Replayer (todo)', self.not_implemented), ('tableviewer', None, 'Poker_table Viewer (mostly obselete)', None, 'Poker_table Viewer (mostly obselete)', self.tab_table_viewer), ('database', None, '_Database'), ('maintaindbs', None, '_Maintain Databases (todo)', None, 'Maintain Databases', self.dia_maintain_dbs), - ('createuser', None, 'Create or Delete _User (todo)', None, 'Create or Delete User', self.dia_create_del_user), ('createtabs', None, 'Create or Recreate _Tables', None, 'Create or Recreate Tables ', self.dia_recreate_tables), ('rebuildhudcache', None, 'Rebuild HUD Cache', None, 'Rebuild HUD Cache', self.dia_recreate_hudcache), ('rebuildindexes', None, 'Rebuild DB Indexes', None, 'Rebuild DB Indexes', self.dia_rebuild_indexes), - ('stats', None, '_Statistics (todo)', None, 'View Database Statistics', self.dia_database_stats), + ('databasestats', None, '_Statistics', None, 'View Database Statistics', self.dia_database_stats), + ('dumptofile', None, 'Dump Database to Textfile', None, 'Dump Database to Textfile (takes much time, RAM, HD)', self.dia_dump_db), ('help', None, '_Help'), - ('Abbrev', None, '_Abbrevations (todo)', None, 'List of Abbrevations', self.tab_abbreviations), ('Logs', None, '_Log Messages', None, 'Log and Debug Messages', self.dia_logs), ('About', None, 'A_bout', None, 'About the program', self.dia_about), ('License', None, '_License and Copying (todo)', None, 'License and Copying', self.dia_licensing), @@ -741,6 +725,12 @@ class fpdb: menubar = uimanager.get_widget('/MenuBar') window.add_accel_group(accel_group) return menubar + #end def get_menu + + def import_imap_summaries(self, widget, data=None): + result=ImapSummaries.run(self.config, self.db) + #print "import imap summaries result:", result + #end def import_imap_summaries def load_profile(self, create_db = False): """Loads profile from the provided path name.""" @@ -845,12 +835,13 @@ class fpdb: def not_implemented(self, widget, data=None): self.warning_box("Unimplemented menu entry") - def obtain_global_lock(self): - ret = self.lock.acquire(False) # will return false if lock is already held + def obtain_global_lock(self, source): + ret = self.lock.acquire(source=source) # will return false if lock is already held if ret: - print "\nGlobal lock taken ..." + print "\nGlobal lock taken by", source + self.lockTakenBy=source else: - print "\nFailed to get global lock." + print "\nFailed to get global lock, it is currently held by", source return ret # need to release it later: # self.lock.release() @@ -860,11 +851,18 @@ class fpdb: #FIXME get two "quitting normally" messages, following the addition of the self.window.destroy() call print "Quitting normally" # TODO: check if current settings differ from profile, if so offer to save or abort - try: - if self.db is not None and self.db.connected(): - self.db.disconnect() - except _mysql_exceptions.OperationalError: # oh, damn, we're already disconnected - log.info("fpdb.quit disconnect error being ignored: "+str(sys.exc_info())) + + if self.db!=None: + if self.db.backend==self.db.MYSQL_INNODB: + try: + if self.db is not None and self.db.connected: + self.db.disconnect() + except _mysql_exceptions.OperationalError: # oh, damn, we're already disconnected + pass + else: + if self.db is not None and self.db.connected: + self.db.disconnect() + else: pass self.statusIcon.set_visible(False) @@ -873,11 +871,9 @@ class fpdb: def release_global_lock(self): self.lock.release() + self.lockTakenBy=None print "Global lock released.\n" - def tab_abbreviations(self, widget, data=None): - print "todo: implement tab_abbreviations" - def tab_auto_import(self, widget, data=None): """opens the auto import tab""" new_aimp_thread = GuiAutoImport.GuiAutoImport(self.settings, self.config, self.sql, self.window) @@ -887,17 +883,22 @@ class fpdb: def tab_bulk_import(self, widget, data=None): """opens a tab for bulk importing""" - #print "start of tab_bulk_import" new_import_thread = GuiBulkImport.GuiBulkImport(self.settings, self.config, self.sql) self.threads.append(new_import_thread) bulk_tab=new_import_thread.get_vbox() self.add_and_display_tab(bulk_tab, "Bulk Import") - def tab_player_stats(self, widget, data=None): - new_ps_thread = GuiPlayerStats.GuiPlayerStats(self.config, self.sql, self.window) + def tab_ring_player_stats(self, widget, data=None): + new_ps_thread = GuiRingPlayerStats.GuiRingPlayerStats(self.config, self.sql, self.window) self.threads.append(new_ps_thread) ps_tab=new_ps_thread.get_vbox() - self.add_and_display_tab(ps_tab, "Player Stats") + self.add_and_display_tab(ps_tab, "Ring Player Stats") + + def tab_tourney_player_stats(self, widget, data=None): + new_ps_thread = GuiTourneyPlayerStats.GuiTourneyPlayerStats(self.config, self.db, self.sql, self.window) + self.threads.append(new_ps_thread) + ps_tab=new_ps_thread.get_vbox() + self.add_and_display_tab(ps_tab, "Tourney Player Stats") def tab_positional_stats(self, widget, data=None): new_ps_thread = GuiPositionalStats.GuiPositionalStats(self.config, self.sql) @@ -916,6 +917,7 @@ class fpdb: mh_tab=gtk.Label("""Welcome to Fpdb! This program is currently in an alpha-state, so our database format is still sometimes changed. You should therefore always keep your hand history files so that you can re-import after an update, if necessary. + For documentation please visit our website at http://fpdb.sourceforge.net/. If you need help click on Contact - Get Help on our website. Please note that default.conf is no longer needed nor used, all configuration now happens in HUD_config.xml. @@ -947,7 +949,7 @@ You can find the full license texts in agpl-3.0.txt, gpl-2.0.txt and gpl-3.0.txt self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) self.window.connect("delete_event", self.delete_event) self.window.connect("destroy", self.destroy) - self.window.set_title("Free Poker DB - v%s or higher" % (VERSION, )) + self.window.set_title("Free Poker DB - v%s" % (VERSION, )) self.window.set_border_width(1) defx, defy = 900, 720 sx, sy = gtk.gdk.screen_width(), gtk.gdk.screen_height() diff --git a/pyfpdb/fpdb_import.py b/pyfpdb/fpdb_import.py index d777dec8..ac569c95 100755 --- a/pyfpdb/fpdb_import.py +++ b/pyfpdb/fpdb_import.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2008-2010 Steffen Schaumburg @@ -152,7 +152,7 @@ class Importer: #Add an individual file to filelist def addImportFile(self, filename, site = "default", filter = "passthrough"): #TODO: test it is a valid file -> put that in config!! - if filename in self.filelist or not os.path.exists(filename): + if filename in self.filelist or not os.path.exists(unicode(filename,'utf-8')): return self.filelist[filename] = [site] + [filter] if site not in self.siteIds: @@ -406,7 +406,7 @@ class Importer: conv = None (stored, duplicates, partial, errors, ttime) = (0, 0, 0, 0, time()) - file = file.decode(Configuration.LOCALE_ENCODING) + file = file.decode("utf-8") #(Configuration.LOCALE_ENCODING) # Load filter, process file, pass returned filename to import_fpdb_file if self.settings['threads'] > 0 and self.writeq is not None: diff --git a/pyfpdb/interlocks.py b/pyfpdb/interlocks.py index 4e427e7d..f6ef7857 100755 --- a/pyfpdb/interlocks.py +++ b/pyfpdb/interlocks.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- # Code from http://ender.snowburst.org:4747/~jjohns/interlocks.py # Thanks JJ! @@ -34,19 +35,24 @@ class InterProcessLockBase: if not name: name = sys.argv[0] self.name = name + self.heldBy = None def getHashedName(self): return base64.b64encode(self.name).replace('=','') def acquire_impl(self, wait): abstract - def acquire(self, wait=False, retry_time=1): + def acquire(self, wait=False, retry_time=1, source=None): + if source == None: + source="Unknown" if self._has_lock: # make sure 2nd acquire in same process fails + print "lock already held by:",self.heldBy return False while not self._has_lock: try: self.acquire_impl(wait) self._has_lock = True + self.heldBy=source #print 'i have the lock' except SingleInstanceError: if not wait: @@ -58,6 +64,7 @@ class InterProcessLockBase: def release(self): self.release_impl() self._has_lock = False + self.heldBy=None def locked(self): if self.acquire(): diff --git a/pyfpdb/makeexe.py b/pyfpdb/makeexe.py index f70a5400..568b1939 100644 --- a/pyfpdb/makeexe.py +++ b/pyfpdb/makeexe.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- #Copyright 2009-2010 Eric Blade diff --git a/pyfpdb/py2exe_setup.py b/pyfpdb/py2exe_setup.py index 05a070ef..ac136cff 100644 --- a/pyfpdb/py2exe_setup.py +++ b/pyfpdb/py2exe_setup.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- """setup.py diff --git a/pyfpdb/test_Betfair.py b/pyfpdb/test_Betfair.py index 3638eb9f..231b1bf3 100644 --- a/pyfpdb/test_Betfair.py +++ b/pyfpdb/test_Betfair.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2009-2010 Carl Gherardi diff --git a/pyfpdb/test_Database.py b/pyfpdb/test_Database.py index 22e42af0..54b44e36 100644 --- a/pyfpdb/test_Database.py +++ b/pyfpdb/test_Database.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2008-2010 Carl Gherardi diff --git a/pyfpdb/test_Everleaf.py b/pyfpdb/test_Everleaf.py index fef124cc..12c00590 100644 --- a/pyfpdb/test_Everleaf.py +++ b/pyfpdb/test_Everleaf.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2009-2010 Matt Turnbull diff --git a/pyfpdb/test_FullTilt.py b/pyfpdb/test_FullTilt.py index c412540d..432932a0 100644 --- a/pyfpdb/test_FullTilt.py +++ b/pyfpdb/test_FullTilt.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2009-2010 Matt Turnbull diff --git a/pyfpdb/test_PokerStars.py b/pyfpdb/test_PokerStars.py index dd2c827e..5178adef 100755 --- a/pyfpdb/test_PokerStars.py +++ b/pyfpdb/test_PokerStars.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2009-2010 Matt Turnbull diff --git a/pyfpdb/windows_make_bats.py b/pyfpdb/windows_make_bats.py index 1bc9703d..2ea3e06c 100755 --- a/pyfpdb/windows_make_bats.py +++ b/pyfpdb/windows_make_bats.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2009-2010 Carl Gherardi diff --git a/regression-test/PrintHand.py b/regression-test/PrintHand.py index f35c49b0..98966f8b 100755 --- a/regression-test/PrintHand.py +++ b/regression-test/PrintHand.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2008-2010 Steffen Schaumburg diff --git a/regression-test/PrintPlayerHudData.py b/regression-test/PrintPlayerHudData.py index 80b8c436..f290a4dc 100755 --- a/regression-test/PrintPlayerHudData.py +++ b/regression-test/PrintPlayerHudData.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2008-2010 Steffen Schaumburg diff --git a/regression-test/fpdb_util_lib.py b/regression-test/fpdb_util_lib.py index ef19bdec..e5e0c86c 100644 --- a/regression-test/fpdb_util_lib.py +++ b/regression-test/fpdb_util_lib.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2008-2010 Steffen Schaumburg diff --git a/run_fpdb.py b/run_fpdb.py index 231fedf9..d5295386 100755 --- a/run_fpdb.py +++ b/run_fpdb.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2008-2010 Carl Gherardi diff --git a/setup.py b/setup.py index 9081ab01..bdf5560d 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Created by Mika Bostrom, released into the public domain as far as legally possible. diff --git a/test_Python.py b/test_Python.py index d55da7a8..a12b6440 100755 --- a/test_Python.py +++ b/test_Python.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- """test1.py diff --git a/test_Python_Libs.py b/test_Python_Libs.py index 016f7e42..a9db8252 100755 --- a/test_Python_Libs.py +++ b/test_Python_Libs.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python # -*- coding: utf-8 -*- """test2.py diff --git a/utils/fix_table_desc.py b/utils/fix_table_desc.py index 37ed298e..46044f1c 100644 --- a/utils/fix_table_desc.py +++ b/utils/fix_table_desc.py @@ -1,4 +1,4 @@ -#!/usr/bin/python2 +#!/usr/bin/python # -*- coding: utf-8 -*- #Copyright 2009-2010 Ray E. Barker